3d211ec7094e675d7ee2ca8db898e876ba80810f
[portal/sdk.git] /
1 /*! b2b-angular-library - v1.0.4 - Last updated: 2017-05-03. 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.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.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                     if(scope.coachmarkIndex === -1 || scope.coachmarkIndex >= coachmarkItems.length ){
2403                         scope.coachmarkIndex = 0;
2404                     }
2405                     scope.$watch('coachmarkIndex', function () {
2406                         if(initaitedCoachmark === true){
2407                             if(scope.coachmarkIndex === -1 || scope.coachmarkIndex >= coachmarkItems.length ){
2408                                 scope.closeCoachmark();
2409                             }else{
2410                                 showCoachmark(coachmarkItems[scope.coachmarkIndex]);
2411                             }
2412                         }
2413                     });
2414                     coachmarkBackdrop();
2415                     showCoachmark(coachmarkItems[scope.coachmarkIndex]);
2416                     if (angular.isFunction(scope.startCoachmarkCallback)){
2417                         scope.startCoachmarkCallback(); 
2418                     }
2419                     $document.bind('keydown', function (evt) {
2420                         if (evt.which === 27 && initaitedCoachmark) {
2421                             scope.closeCoachmark();
2422                             scope.$apply(); 
2423                         }
2424                     });
2425                 });
2426
2427
2428                 //performance technique to ensure scroll event doesn't cause lag
2429                 var throttle = function(type, name, obj) {
2430                     obj = obj || window; 
2431                     var running = false; 
2432                     var func = function() { 
2433                         if (running) { return; } 
2434                         running = true; 
2435                          requestAnimationFrame(function() { 
2436                             obj.dispatchEvent(new CustomEvent(name)); 
2437                             running = false; 
2438                         }); 
2439                     }; 
2440                     obj.addEventListener(type, func); 
2441                 };
2442              
2443                 scope.viewportWidth = b2bViewport.viewportWidth(); 
2444                 /* init - you can init any event */ 
2445                 throttle("resize", "optimizedResize"); 
2446                 window.addEventListener("optimizedResize", function() {
2447                     if(initaitedCoachmark){
2448                         showCoachmark(coachmarkItems[scope.coachmarkIndex]);
2449                         scope.viewportWidth = b2bViewport.viewportWidth(); 
2450                         scope.$digest();
2451                     }
2452                 });
2453             }
2454         };
2455     }])
2456     .directive('b2bCoachmarkContainer', ['$document', '$position', function($document, $position) {
2457         return {
2458             restrict: 'A',
2459             transclude: true,
2460             replace: true,
2461             templateUrl: 'b2bTemplate/coachmark/coachmark.html',
2462             link: function (scope, element, attrs, ctrl) {
2463                             
2464             }
2465         };  
2466     }]);
2467     
2468 /** 
2469  * @ngdoc directive 
2470  * @name Template.att:Configuration Section 
2471  * 
2472  * @description 
2473  *  <file src="src/configurationSection/docs/readme.md" /> 
2474  * 
2475  * @example 
2476  *  <section id="code"> 
2477         <b>HTML + AngularJS</b> 
2478         <example module="b2b.att"> 
2479             <file src="src/configurationSection/docs/demo.html" /> 
2480             <file src="src/configurationSection/docs/demo.js" /> 
2481        </example> 
2482     </section>    
2483  * 
2484  */
2485 angular.module('b2b.att.configurationSection', [])
2486   
2487 /** 
2488  * @ngdoc directive 
2489  * @name Template.att:Directory Listing 
2490  * 
2491  * @description 
2492  *  <file src="src/directoryListingTemplate/docs/readme.md" /> 
2493  * 
2494  * @example 
2495  *  <section id="code"> 
2496         <b>HTML + AngularJS</b> 
2497         <example module="b2b.att"> 
2498             <file src="src/directoryListingTemplate/docs/demo.html" /> 
2499             <file src="src/directoryListingTemplate/docs/demo.js" /> 
2500        </example> 
2501     </section>    
2502  * 
2503  */
2504 angular.module('b2b.att.directoryListingTemplate', [])
2505   
2506 /**
2507  * @ngdoc directive
2508  * @name Forms.att:dropdowns
2509  *
2510  * @description
2511  *  <file src="src/dropdowns/docs/readme.md" />
2512  * @usage
2513  *
2514  * @example
2515    <section id="code">
2516     <example module="b2b.att">
2517      <file src="src/dropdowns/docs/demo.html" />
2518      <file src="src/dropdowns/docs/demo.js" />
2519     </example>
2520    </section>
2521  */
2522 angular.module('b2b.att.dropdowns', ['b2b.att.utilities', 'b2b.att.position', 'ngSanitize'])
2523
2524 .constant('b2bDropdownConfig', {
2525     prev: '37,38',
2526     next: '39,40',
2527     menuKeyword: 'menu',
2528     linkMenuKeyword: 'link-menu',
2529     largeKeyword: 'large',
2530     smallKeyword: 'small'
2531 })  
2532
2533 .directive("b2bDropdown", ['$timeout', '$compile', '$templateCache', 'b2bUserAgent', 'b2bDropdownConfig', '$position', function ($timeout, $compile, $templateCache, b2bUserAgent, b2bDropdownConfig, $position) {
2534     return {
2535         restrict: 'A',
2536         scope: true,
2537         require: 'ngModel',
2538         controller: ['$scope', '$element', '$attrs', function (scope, elem, attr) {
2539             scope.isInputDropdown = true;
2540             scope.placeHoldertext = attr.placeholderText;
2541             if (attr.type) {
2542                 if (attr.type.indexOf(b2bDropdownConfig.menuKeyword) > -1 || attr.type.indexOf(b2bDropdownConfig.linkMenuKeyword) > -1) {
2543                     scope.isInputDropdown = false;
2544                     if (attr.type.indexOf(b2bDropdownConfig.linkMenuKeyword) > -1) {
2545                         scope.dropdownType = b2bDropdownConfig.linkMenuKeyword;
2546                     } else if (attr.type.indexOf(b2bDropdownConfig.menuKeyword) > -1) {
2547                         scope.dropdownType = b2bDropdownConfig.menuKeyword;
2548                     }
2549                 }
2550                 if (attr.type.indexOf(b2bDropdownConfig.largeKeyword) > -1) {
2551                     scope.dropdownSize = b2bDropdownConfig.largeKeyword;
2552                 } else if (attr.type.indexOf(b2bDropdownConfig.smallKeyword) > -1) {
2553                     scope.dropdownSize = b2bDropdownConfig.smallKeyword;
2554                 }
2555             }
2556
2557             scope.labelText = attr.labelText;
2558
2559             scope.setBlur = function () {
2560                 scope.setTouched();
2561             };
2562
2563             if ((scope.isInputDropdown && b2bUserAgent.notMobile()) || (!scope.isInputDropdown)) {
2564                 var formCtrl = elem.controller('form');
2565                 scope.setNgModelController = function (name, ngModelCtrl) {
2566                     if (name && formCtrl && ngModelCtrl) {
2567                         formCtrl[name] = ngModelCtrl;
2568                     }
2569                 };
2570                 scope.setOptionalCta = function (optionalCta) {
2571                     scope.optionalCta = optionalCta;
2572                 };
2573                 var innerHtml = angular.element('<div></div>').append(elem.html());
2574                 innerHtml = ($compile(innerHtml)(scope)).html();
2575                 var template = angular.element($templateCache.get('b2bTemplate/dropdowns/b2bDropdownDesktop.html'));
2576                 template.find('ul').eq(0).append(innerHtml);
2577                 template = $compile(template)(scope);
2578                 elem.replaceWith(template);
2579             } else if (scope.isInputDropdown && b2bUserAgent.isMobile()) {
2580                 elem.css({
2581                     'opacity': '0',
2582                     'filter': 'alpha(opacity=0)'
2583                 });
2584                 elem.addClass('awd-select isWrapped');
2585                 elem.wrap('<span class="selectWrap"></span>');
2586                 var cover = angular.element('<span aria-hidden="true"><i class="icon-primary-down" aria-hidden="true"></i></span>');
2587                 elem.parent().append(cover);
2588                 elem.parent().append('<i class="icon-primary-down" aria-hidden="true"></i>');
2589                 var set = function () {
2590                     var sel = elem[0] ? elem[0] : elem;
2591                     var selectedText = "";
2592                     var selIndex = sel.selectedIndex;
2593                     if (typeof selIndex !== 'undefined') {
2594                         selectedText = sel.options[selIndex].text;
2595                     }
2596                     cover.text(selectedText).append('<i class="icon-primary-down" aria-hidden="true"></i>');
2597                 };
2598                 var update = function (value) {
2599                     $timeout(set, 100);
2600                 };
2601
2602                 if (attr.ngModel) {
2603                     scope.$watch(attr.ngModel, function (newVal, oldVal) {
2604                         update();
2605                     });
2606                 }
2607                 elem.bind('keyup', function (ev) {
2608                     if (ev.keyCode === keymap.KEY.TAB || ev.keyCode === keymap.KEY.ESC) {
2609                         return;
2610                     }
2611                     set();
2612                 });
2613             }
2614
2615         }],  
2616         link: function (scope, elem, attr, ctrl) {
2617             if ((scope.isInputDropdown && b2bUserAgent.notMobile()) || (!scope.isInputDropdown)) {
2618                 scope.updateModel = function () {
2619                     ctrl.$setViewValue(scope.currentSelected.value);
2620                     if (scope.dropdownRequired && scope.currentSelected.value === '') {
2621                         scope.setRequired(false);
2622                     } else {
2623                         scope.setRequired(true);
2624                     }
2625
2626                     if (scope.dropdownType === b2bDropdownConfig.linkMenuKeyword) {
2627                         $timeout(function () {
2628                             scope.appendCaretPositionStyle();
2629                         }, 100);
2630                     }
2631                 };
2632                 ctrl.$render = function () {
2633
2634                 $timeout(function () {
2635
2636                         if ((angular.isUndefined(ctrl.$viewValue) || ctrl.$viewValue == '') && (angular.isUndefined(scope.placeHoldertext) || scope.placeHoldertext == '')) {
2637                             scope.dropdownLists[ctrl.$viewValue] && scope.dropdownLists[ctrl.$viewValue][0].updateDropdownValue();
2638                         } else if ((angular.isUndefined(scope.placeHoldertext) || scope.placeHoldertext == '') && ctrl.$viewValue !== '' ) {
2639                             scope.dropdownLists[ctrl.$viewValue] && scope.dropdownLists[ctrl.$viewValue][0].updateDropdownValue();
2640                         } else if ((angular.isUndefined(ctrl.$viewValue) || ctrl.$viewValue == '') && scope.placeHoldertext !== '' )  {
2641                             scope.currentSelected.text = scope.placeHoldertext;
2642                             ctrl.$setViewValue(scope.placeHoldertext);
2643                         } else {
2644                             scope.dropdownLists[ctrl.$viewValue] && scope.dropdownLists[ctrl.$viewValue][0].updateDropdownValue();
2645                         }
2646
2647                     }, 100);
2648                 };
2649
2650                 scope.disabled = false;
2651                 scope.dropdownName = attr.name;
2652                 scope.dropdownId = attr.id;
2653                 scope.labelId = attr.ariaLabelledby;
2654                 scope.dropdownDescribedBy = attr.ariaDescribedby;
2655                 if (attr.required) {
2656                     scope.dropdownRequired = true;
2657                 } else {
2658                     scope.dropdownRequired = false;
2659                 }
2660                 elem.removeAttr('name');
2661                 elem.removeAttr('id');
2662                 scope.$parent.$watch(attr.ngDisabled, function (val) {
2663                     scope.disabled = val;
2664                 });
2665             }
2666         }
2667     };
2668 }])
2669
2670 .directive("b2bDropdownToggle", ['$document', '$documentBind', '$isElement', 'b2bDropdownConfig', 'keymap', 'b2bUtilitiesConfig', '$timeout', '$position', function ($document, $documentBind, $isElement, b2bDropdownConfig, keymap, b2bUtilitiesConfig, $timeout, $position) {
2671     return {
2672         restrict: 'A',
2673         require: '?^b2bKey',
2674         link: function (scope, elem, attr, ctrl) {
2675             scope.appendCaretPositionStyle = function () {
2676                 while (document.querySelector('style.b2bDropdownCaret')) {
2677                     document.querySelector('style.b2bDropdownCaret').remove();
2678                 };
2679                 var caretPosition = $position.position(elem).width - 26;
2680                 if (scope.dropdownType === b2bDropdownConfig.linkMenuKeyword) {
2681                     var template = angular.element('<style class="b2bDropdownCaret" type="text/css">.linkSelectorModule .active+.moduleWrapper:before {left: ' + caretPosition + 'px;}</style>');
2682                     $document.find('head').append(template);
2683                 }
2684             };
2685
2686             if (scope.isInputDropdown && (scope.labelText !== undefined)) {
2687                 elem.attr('aria-label', scope.labelText);
2688             }
2689
2690             scope.toggleFlag = false;
2691             scope.dropdownLists = {};
2692             scope.dropdownListValues = [];
2693             scope.dropdown = {
2694                 totalIndex: -1
2695             };
2696             scope.currentSelected = {
2697                 value: '',
2698                 text: '',
2699                 label: '',
2700                 index: -1
2701             };
2702             scope.dropdownTextList = [];
2703             var searchString = '';
2704
2705             var getDropdownText = function(){
2706                 var dropdownItems = elem.parent().find('ul').children();
2707                 var count = dropdownItems.length;
2708                 for(var i=0;i<count;i++){
2709                     scope.dropdownTextList.push(dropdownItems.eq(i).text());
2710                 }
2711             };
2712             var searchElement = function (searchExp) {
2713                  if(scope.dropdownTextList.length ==0){
2714                     getDropdownText ();
2715                 }
2716                 var regex = new RegExp("^" + searchExp, "i");
2717                 var position = scope.dropdownTextList.regexIndexOf(regex, scope.currentSelected.index + 1, true);
2718                 if (position > -1) {
2719                     return position;
2720                 }
2721                 return undefined;
2722             };
2723             var startTimer = function (time) {
2724                 if (searchString === '') {
2725                     $timeout(function () {
2726                         searchString = '';
2727                     }, time);
2728                 }
2729             };
2730             scope.toggleDropdown = function (toggleFlag) {
2731                 if (!scope.disabled) {
2732                     if (angular.isDefined(toggleFlag)) {
2733                         scope.toggleFlag = toggleFlag;
2734                     } else {
2735                         scope.toggleFlag = !scope.toggleFlag;
2736                     }
2737                     if (!scope.toggleFlag) {
2738                         if (scope.isInputDropdown) {
2739                             elem.parent().find('input')[0].focus();
2740                         } else {
2741                             elem.parent().find('button')[0].focus();
2742                         }
2743                     } else {
2744                         scope.dropdown.highlightedValue = scope.currentSelected.value;
2745                         if (ctrl && ctrl.enableSearch) {
2746                             if (angular.isDefined(scope.dropdownLists[scope.currentSelected.value])) {
2747                                 ctrl.resetCounter(scope.dropdownLists[scope.currentSelected.value][2]);
2748                             }
2749                         }
2750                         $timeout(function () {
2751                             if(scope.dropdownLists[scope.currentSelected.value] !== undefined){
2752                                 (scope.dropdownLists[scope.currentSelected.value][1])[0].focus();
2753                             } else {
2754                                 if (scope.isInputDropdown) {
2755                                     elem.parent().find('input')[0].focus();
2756                                 } else {
2757                                     elem.parent().find('button')[0].focus();
2758                                 }
2759                             }
2760                         }, 100);
2761                         if (scope.dropdownType === b2bDropdownConfig.linkMenuKeyword) {
2762                             scope.appendCaretPositionStyle();
2763                         }
2764                     }
2765                 }
2766             };
2767
2768             elem.bind('keydown', function (ev) {
2769                 if (!ev.keyCode) {
2770                     if (ev.which) {
2771                         ev.keyCode = ev.which;
2772                     } else if (ev.charCode) {
2773                         ev.keyCode = ev.charCode;
2774                     }
2775                 }
2776                 if (!scope.toggleFlag) {
2777                     if (ev.keyCode) {
2778                         var currentIndex = scope.currentSelected.index;
2779                         if (ev.keyCode === keymap.KEY.DOWN) {
2780                             scope.toggleDropdown(true);
2781                             ev.preventDefault();
2782                             ev.stopPropagation();
2783                         } else if (b2bDropdownConfig.prev.split(',').indexOf(ev.keyCode.toString()) > -1) {
2784                             angular.isDefined(scope.dropdownListValues[currentIndex - 1]) ? scope.dropdownLists[scope.dropdownListValues[currentIndex - 1]][0].updateDropdownValue() : angular.noop();
2785                             ev.preventDefault();
2786                             ev.stopPropagation();
2787                         } else if (b2bDropdownConfig.next.split(',').indexOf(ev.keyCode.toString()) > -1) {
2788                             angular.isDefined(scope.dropdownListValues[currentIndex + 1]) ? scope.dropdownLists[scope.dropdownListValues[currentIndex + 1]][0].updateDropdownValue() : angular.noop();
2789                             ev.preventDefault();
2790                             ev.stopPropagation();
2791                         } else if (ev.keyCode >= 48 && ev.keyCode <= 105) {
2792                             startTimer(b2bUtilitiesConfig.searchTimer);
2793                             searchString = searchString + (keymap.MAP[ev.keyCode] || '');
2794                             var position = searchElement(searchString);
2795                             angular.isDefined(scope.dropdownListValues[position]) ? scope.dropdownLists[scope.dropdownListValues[position]][0].updateDropdownValue() : angular.noop();
2796                             ev.preventDefault();
2797                             ev.stopPropagation();
2798                         }
2799                     }
2800                 } else {
2801                     if (ev.altKey === true && ev.keyCode === keymap.KEY.UP) {
2802                         scope.toggleDropdown(false);
2803                         ev.preventDefault();
2804                         ev.stopPropagation();
2805                     } else if (ev.keyCode === keymap.KEY.TAB || ev.keyCode === keymap.KEY.ESC) {
2806                         scope.toggleDropdown(false);
2807                         ev.preventDefault();
2808                         ev.stopPropagation();
2809                     }
2810                 }
2811                 scope.$apply(); // TODO: Move this into each block to avoid expensive digest cycles
2812             });
2813             var outsideClick = function (e) {
2814                 var isElement = $isElement(angular.element(e.target), elem.parent(), $document);
2815                 if (!isElement) {
2816                     scope.toggleDropdown(false);
2817                     scope.$apply();
2818                 }
2819             };
2820             $documentBind.click('toggleFlag', outsideClick, scope);
2821             $documentBind.touch('toggleFlag', outsideClick, scope);
2822         }
2823     };
2824 }])
2825
2826 .directive("b2bDropdownGroup", ['$compile', '$templateCache', 'b2bUserAgent', function ($compile, $templateCache, b2bUserAgent) {
2827     return {
2828         restrict: 'A',
2829         controller: ['$scope', '$element', '$attrs', function (scope, elem, attr) {
2830             if ((scope.isInputDropdown && b2bUserAgent.notMobile()) || (!scope.isInputDropdown)) {
2831                 var innerHtml = angular.element('<div></div>').append(elem.html());
2832                 innerHtml = ($compile(innerHtml)(scope)).html();
2833                 var template = angular.element($templateCache.get('b2bTemplate/dropdowns/b2bDropdownGroupDesktop.html'));
2834                 template.attr('ng-repeat', attr.optGroupRepeat);
2835                 template.attr('label', elem.attr('label'));
2836                 template.find('ul').append(innerHtml);
2837                 elem.replaceWith(template);
2838             } else if (scope.isInputDropdown && b2bUserAgent.isMobile()) {
2839                 var template = angular.element(elem.prop('outerHTML'));
2840                 template.attr('ng-repeat', attr.optGroupRepeat);
2841                 template.removeAttr('b2b-dropdown-group');
2842                 template.removeAttr('opt-group-repeat');
2843                 template = $compile(template)(scope);
2844                 elem.replaceWith(template);
2845             }
2846         }]
2847     };
2848 }])
2849
2850 .directive("b2bDropdownGroupDesktop", [function () {
2851     return {
2852         restrict: 'A',
2853         scope: true,
2854         link: function (scope, elem, attr, ctrl) {
2855             scope.groupHeader = attr.label;
2856         }
2857     };
2858 }])
2859
2860 .directive("b2bDropdownList", ['$compile', '$templateCache', 'b2bUserAgent', function ($compile, $templateCache, b2bUserAgent) {
2861     return {
2862         restrict: 'A',
2863         controller: ['$scope', '$element', '$attrs', function (scope, elem, attr) {
2864             if(attr.optionRepeat && !scope.watchRegistered){
2865                  var arrayName = attr.optionRepeat.split(" ").pop();
2866                 if(arrayName.indexOf(']') == -1){
2867                     scope.$watch(arrayName,function(){
2868                         /*scope.dropdownLists ={};*/
2869                         scope.dropdownListValues =[];
2870                         scope.dropdown = {
2871                             totalIndex: -1
2872                         };
2873                         scope.dropdownTextList = [];
2874                     },true);
2875                 scope.watchRegistered = true;
2876                 }                  
2877             }
2878            
2879             if ((scope.isInputDropdown && b2bUserAgent.notMobile()) || (!scope.isInputDropdown)) {
2880                 var innerHtml = angular.element('<div></div>').append(elem.html());
2881                 innerHtml = ($compile(innerHtml)(scope)).html();
2882                 var template = angular.element($templateCache.get('b2bTemplate/dropdowns/b2bDropdownListDesktop.html'));
2883                 template.attr('ng-repeat', attr.optionRepeat);
2884                 template.attr('value', elem.attr('value'));
2885                 template.attr('search-key', elem.text());
2886                 if (elem.attr('aria-describedby')){
2887                     template.attr('aria-describedby', attr.ariaDescribedby);
2888                 }
2889                 if (elem.attr('imgsrc')) {
2890                     if (elem.attr('imgalt')) {
2891                         template.append('<img role="presentation" ng-src="' + elem.attr('imgsrc') + '" alt="' + elem.attr('imgalt') + '"/>');
2892                     } else {
2893                         template.append('<img role="presentation" ng-src="' + elem.attr('imgsrc') + '" alt=""/>');
2894                     }
2895                 }
2896                 template.append(innerHtml);
2897                 elem.replaceWith(template);
2898             } else if (scope.isInputDropdown && b2bUserAgent.isMobile()) {
2899                 var template = angular.element(elem.prop('outerHTML'));
2900                 template.attr('ng-repeat', attr.optionRepeat);
2901                 if (elem.attr('aria-describedby')){
2902                     template.attr('aria-describedby', attr.ariaDescribedby);
2903                 }
2904                 template.removeAttr('b2b-dropdown-list');
2905                 template.removeAttr('option-repeat');
2906                 template = $compile(template)(scope);
2907                 elem.replaceWith(template);
2908             }
2909         }]
2910     };
2911 }])
2912
2913 .directive("b2bDropdownListDesktop", ['$sce', 'keymap', 'b2bDropdownConfig', function ($sce, keymap, b2bDropdownConfig) {
2914     return {
2915         restrict: 'A',
2916         scope: true,
2917
2918         link: function (scope, elem, attr, ctrl) {
2919             var dropdownListValue = scope.dropdownListValue = attr.value;
2920             scope.dropdown.totalIndex++;
2921             var dropdownListIndex = scope.dropdown.totalIndex;
2922             scope.dropdownListValues.push(dropdownListValue);
2923             scope.dropdownLists[dropdownListValue] = [];
2924             scope.dropdownLists[dropdownListValue][0] = scope;
2925             scope.dropdownLists[dropdownListValue][1] = elem;
2926             scope.dropdownLists[dropdownListValue][2] = dropdownListIndex;
2927                                    
2928             scope.updateDropdownValue = function () {
2929                 scope.currentSelected.value = dropdownListValue;
2930                 if (scope.isInputDropdown) {
2931                     scope.currentSelected.text = elem.text();
2932                     scope.currentSelected.label = elem.text();
2933                 } else if ((scope.dropdownType === b2bDropdownConfig.linkMenuKeyword) || (scope.dropdownType === b2bDropdownConfig.menuKeyword && scope.dropdownSize === b2bDropdownConfig.smallKeyword)) {
2934                     scope.currentSelected.text = dropdownListValue;
2935                     scope.currentSelected.label = dropdownListValue;
2936                 } else if (scope.dropdownType === b2bDropdownConfig.menuKeyword) {
2937                     scope.currentSelected.text = $sce.trustAsHtml(elem.html());
2938                     scope.currentSelected.label = elem.text();
2939                 }
2940                 scope.currentSelected.index = dropdownListIndex;
2941                 scope.updateModel();
2942             };
2943             scope.selectDropdownItem = function () {
2944                 scope.setDirty();
2945                 scope.updateDropdownValue();
2946                 scope.toggleDropdown(false);
2947             };
2948             scope.highlightDropdown = function () {
2949                 scope.dropdown.highlightedValue = dropdownListValue;
2950             };
2951             elem.bind('mouseover', function (ev) {
2952                 elem[0].focus();
2953             });
2954
2955             elem.bind('keydown', function (ev) {
2956                 if (!ev.keyCode) {
2957                     if (ev.which) {
2958                         ev.keyCode = ev.which;
2959                     } else if (ev.charCode) {
2960                         ev.keyCode = ev.charCode;
2961                     }  
2962                 }
2963                 if (ev.altKey === true && ev.keyCode === keymap.KEY.UP) {
2964                     scope.toggleDropdown(false);
2965                     ev.preventDefault();
2966                     ev.stopPropagation();
2967                 } else if (ev.keyCode === keymap.KEY.TAB || ev.keyCode === keymap.KEY.ESC) {
2968                     scope.toggleDropdown(false);
2969                     ev.preventDefault();
2970                     ev.stopPropagation();
2971                 }
2972                 scope.$apply();
2973             });
2974         }
2975     };
2976 }])
2977
2978 .directive("b2bDropdownRepeat", ['$compile', 'b2bUserAgent', function ($compile, b2bUserAgent) {
2979     return {
2980         restrict: 'A',
2981         controller: ['$scope', '$element', '$attrs', function (scope, elem, attr) {
2982             if ((scope.isInputDropdown && b2bUserAgent.notMobile()) || (!scope.isInputDropdown)) {
2983                 var innerHtml = angular.element('<div></div>').append(elem.html());
2984                 innerHtml = ($compile(innerHtml)(scope)).html();
2985                 var template = angular.element('<div></div>');
2986                 template.attr('ng-repeat', attr.b2bDropdownRepeat);
2987                 template.append(innerHtml);
2988                 elem.replaceWith(template);
2989             } else if (scope.isInputDropdown && b2bUserAgent.isMobile()) {
2990                 angular.noop();
2991             }
2992         }]
2993     };
2994 }])
2995
2996 .directive("b2bDropdownValidation", ['$timeout', function ($timeout) {
2997     return {
2998         restrict: 'A',
2999         require: 'ngModel',
3000         link: function (scope, elem, attr, ctrl) {
3001             $timeout(function () {
3002                 scope.setNgModelController(attr.name, ctrl);
3003             }, 100);
3004             scope.setDirty = function () {
3005                 if (ctrl.$dirty === false) {
3006                     ctrl.$dirty = true;
3007                     ctrl.$pristine = false;
3008                 }
3009             };
3010             scope.setTouched = function () {
3011                 if (ctrl.$touched === false) {
3012                     ctrl.$touched = true;
3013                     ctrl.$pristine = false;
3014                 }
3015             };
3016             scope.setRequired = function (flag) {
3017                 ctrl.$setValidity('required', flag);
3018             };
3019         }
3020     };
3021 }])
3022
3023 .directive('b2bDropdownOptionalCta', [function () {
3024     return {
3025         restrict: 'EA',
3026         transclude: true,
3027         replace: true,
3028         template: '',
3029         compile: function (elem, attr, transclude) {
3030             return function link(scope, elem, attr, ctrl) {
3031                 if (scope.setOptionalCta) {
3032                     scope.setOptionalCta(transclude(scope, function () {}));
3033                 }
3034                 elem.remove();
3035             };
3036         }
3037     };
3038 }]);
3039 /**
3040  * @ngdoc directive
3041  * @name Forms.att:File Upload
3042  *
3043  * @description
3044  *  <file src="src/fileUpload/docs/readme.md" />
3045  *
3046  * @usage
3047  * 
3048 <form id="dragDropFile">        
3049     <div b2b-file-drop file-model="fileModel" on-drop="triggerFileUpload()"  align="center">
3050         <p>
3051             <br>To upload a file, drag & drop it here or 
3052             <span b2b-file-link file-model="fileModel" on-file-select="triggerFileUpload()" >
3053                 click here to select from your computer.
3054             </span><br>
3055         </p>
3056     </div>
3057 </form>
3058  *
3059  * @example
3060  *  <section id="code">
3061         <example module="b2b.att">
3062             <file src="src/fileUpload/docs/demo.html" />
3063             <file src="src/fileUpload/docs/demo.js" />
3064        </example>
3065     </section>
3066  *
3067  */
3068 angular.module('b2b.att.fileUpload', ['b2b.att.utilities'])
3069     .directive('b2bFileDrop', [function() {
3070             return {
3071                 restrict: 'EA',
3072                 scope: {
3073                     fileModel: '=',
3074                     onDrop: '&'
3075                 },
3076                 controller: ['$scope', '$attrs', function($scope, $attrs) {
3077                     this.onDrop = $scope.onDrop;
3078                 }],
3079                 link: function(scope, element) {
3080                     element.addClass('b2b-dragdrop');
3081                     element.bind(
3082                         'dragover',
3083                         function(e) {
3084                             if (e.originalEvent) {
3085                                 e.dataTransfer = e.originalEvent.dataTransfer;
3086                             }
3087                             e.dataTransfer.dropEffect = 'move';
3088                             // allows us to drop
3089                             if (e.preventDefault) {
3090                                 e.preventDefault();
3091                             }
3092                             element.addClass('b2b-dragdrop-over');
3093                             return false;
3094                         }
3095                     );
3096                     element.bind(
3097                         'dragenter',
3098                         function(e) {
3099                             // allows us to drop
3100                             if (e.preventDefault) {
3101                                 e.preventDefault();
3102                             }
3103                             element.addClass('b2b-dragdrop-over');
3104                             return false;
3105                         }
3106                     );
3107                     element.bind(
3108                         'dragleave',
3109                         function() {
3110                             element.removeClass('b2b-dragdrop-over');
3111                             return false;
3112                         }
3113                     );
3114                     element.bind(
3115                         'drop',
3116                         function(e) {
3117                             // Stops some browsers from redirecting.
3118                             if (e.preventDefault) {
3119                                 e.preventDefault();
3120                             }
3121                             if (e.stopPropagation) {
3122                                 e.stopPropagation();
3123                             }
3124                             if (e.originalEvent) {
3125                                 e.dataTransfer = e.originalEvent.dataTransfer;
3126                             }
3127                             element.removeClass('b2b-dragdrop-over');
3128                             if (e.dataTransfer.files && e.dataTransfer.files.length > 0) {
3129                                 scope.fileModel = e.dataTransfer.files[0];
3130                                 scope.$apply();
3131                                 if (angular.isFunction(scope.onDrop)) {
3132                                     scope.onDrop();
3133                                 }
3134                             }
3135                             return false;
3136                         }
3137                     );
3138                 }
3139             };
3140         }])
3141         .directive('b2bFileLink', [function() {
3142             return {
3143                 restrict: 'EA',
3144                 require: '^?b2bFileDrop',
3145                 replace: true,
3146                 transclude: true,
3147                 templateUrl: 'b2bTemplate/fileUpload/fileUpload.html',
3148                 scope: {
3149                     fileModel: '=?',
3150                     onFileSelect: '&'
3151                 },
3152                 controller: ['$scope', function($scope) {
3153                     this.setFileModel = function(fileModel) {
3154                         if ($scope.takeFileModelFromParent) {
3155                             $scope.$parent.fileModel = fileModel;
3156                             $scope.$parent.$apply();
3157                         } else {
3158                             $scope.fileModel = fileModel;
3159                             $scope.$apply();
3160                         }
3161                     };
3162                     this.callbackFunction = function() {
3163                         if (angular.isFunction($scope.onFileSelect)) {
3164                             $scope.onFileSelect();
3165                         }
3166                     };
3167         
3168                 }],
3169                 link: function(scope, element, attr, b2bFileDropCtrl) {
3170                     scope.takeFileModelFromParent = false;
3171                     if (!(attr.fileModel) && b2bFileDropCtrl) {
3172                         scope.takeFileModelFromParent = true;
3173                     }
3174                     if (!(attr.onFileSelect) && b2bFileDropCtrl) {
3175                         scope.onFileSelect = b2bFileDropCtrl.onDrop;
3176                     }
3177                 }
3178             };
3179         }])
3180         .directive('b2bFileChange', ['$log', '$rootScope', function($log, $rootScope) {
3181             return {
3182                 restrict: 'A',
3183                 require: '^b2bFileLink',
3184                 link: function(scope, element, attr, b2bFileLinkCtrl) {
3185                     element.bind('change', changeFileModel);
3186
3187                     function changeFileModel(e) {
3188                         if (e.target.files && e.target.files.length > 0) {
3189                             b2bFileLinkCtrl.setFileModel(e.target.files[0]);
3190                             b2bFileLinkCtrl.callbackFunction();
3191                         } else {
3192                             var strFileName = e.target.value;
3193                             try {
3194                                 var objFSO = new ActiveXObject("Scripting.FileSystemObject");
3195                                 b2bFileLinkCtrl.setFileModel(objFSO.getFile(strFileName));
3196                                 b2bFileLinkCtrl.callbackFunction();
3197                             } catch (e) {
3198                                 var errMsg = "There was an issue uploading " + strFileName + ". Please try again.";
3199                                 $log.error(errMsg);
3200                                 $rootScope.$broadcast('b2b-file-link-failure', errMsg);
3201                             }
3202                         }
3203                     }
3204                 }
3205             };
3206         }]);
3207 /**
3208  * @ngdoc directive
3209  * @name Navigation.att:filters
3210  *
3211  * @description
3212  *  <file src="src/filters/docs/readme.md" />
3213  *
3214  * @usage
3215  *  <div b2b-filters></div>
3216  *
3217  * @example
3218  *  <section id="code">
3219        <b>HTML + AngularJS</b>
3220         <example module="b2b.att">
3221             <file src="src/filters/docs/demo.html" />
3222             <file src="src/filters/docs/demo.js" />
3223        </example>
3224     </section>
3225  * 
3226  */
3227 angular.module('b2b.att.filters', ['b2b.att.utilities', 'b2b.att.multipurposeExpander'])
3228     .filter('filtersSelectedItemsFilter', [function () {
3229         return function (listOfItemsArray) {
3230
3231             if (!listOfItemsArray) {
3232                 listOfItemsArray = [];
3233             }
3234
3235             var returnArray = [];
3236
3237             for (var i = 0; i < listOfItemsArray.length; i++) {
3238                 for (var j = 0; j < listOfItemsArray[i].filterTypeItems.length; j++) {
3239                     if (listOfItemsArray[i].filterTypeItems[j].selected && !listOfItemsArray[i].filterTypeItems[j].inProgress) {
3240                         returnArray.push(listOfItemsArray[i].filterTypeItems[j]);
3241                     }
3242                 }
3243             }
3244
3245             return returnArray;
3246         };
3247     }]);
3248 /**
3249  * @ngdoc directive
3250  * @name Messages, modals & alerts.att:flyout
3251  *
3252  * @description
3253  *  <file src="src/flyout/docs/readme.md" />
3254  * @example
3255  *  <section id="code">
3256         <example module="b2b.att">
3257             <file src="src/flyout/docs/demo.html" />
3258             <file src="src/flyout/docs/demo.js" />
3259        </example>
3260     </section>
3261  *
3262  */
3263 angular.module('b2b.att.flyout', ['b2b.att.utilities', 'b2b.att.position'])
3264     .directive('b2bFlyout', ['$timeout', 'b2bDOMHelper', 'keymap', 'events', function ($timeout, b2bDOMHelper, keymap, events) {
3265         return {
3266             restrict: 'EA',
3267             transclude: true,
3268             templateUrl: 'b2bTemplate/flyout/flyout.html',
3269             controller: ['$scope', '$element', '$attrs', function (scope, elem, attr) {
3270                 scope.flyoutOpened = false;
3271                 var contentScope = '';
3272                 var togglerScope = '';
3273                 this.registerContentScope = function (scp) {
3274                     contentScope = scp;
3275                 };
3276                 this.registerTogglerScope = function (scp) {
3277                     togglerScope = scp;
3278                 };
3279
3280                 this.toggleFlyoutState = function () {
3281                     if (contentScope) {
3282                         contentScope.toggleFlyout();
3283                     }
3284                 };
3285                 this.getTogglerDimensions = function () {
3286                     return togglerScope.getTogglerDimensions();
3287                 }
3288                 this.setTogglerFocus = function () {
3289                     return togglerScope.setTogglerFocus();
3290                 }
3291
3292                 this.closeFlyout = function (e) {
3293                     contentScope.closeFromChild(e);
3294                 };
3295                 this.gotFocus = function () {
3296                     contentScope.gotFocus();
3297                 };
3298
3299                 this.updateAriaModel = function (val) {
3300                     scope.flyoutOpened = val;
3301                 };
3302
3303                 var firstTabableElement = undefined,
3304                     lastTabableElement = undefined;
3305
3306                 var firstTabableElementKeyhandler = function (e) {
3307                     if (!e.keyCode) {
3308                         e.keyCode = e.which;
3309                     }
3310                     if (e.keyCode === keymap.KEY.TAB && e.shiftKey && scope.flyoutOpened) { 
3311                         contentScope.gotFocus();
3312                         events.preventDefault(e);
3313                         events.stopPropagation(e);
3314                     }
3315                 };
3316
3317                 var lastTabableElementKeyhandler = function (e) {
3318                     if (!e.keyCode) {
3319                         e.keyCode = e.which;
3320                     }
3321                     if (e.keyCode === keymap.KEY.TAB && !e.shiftKey) {
3322                         contentScope.gotFocus();    
3323                         events.preventDefault(e);
3324                         events.stopPropagation(e);
3325                     }
3326                 };
3327                 this.associateTabEvent = function(){
3328                     $timeout(function () {
3329                         var element = elem[0].getElementsByClassName('b2b-flyout-container')[0];
3330                         firstTabableElement = b2bDOMHelper.firstTabableElement(element);
3331                         lastTabableElement = b2bDOMHelper.lastTabableElement(element);
3332                         if(angular.isUndefined(firstTabableElement)){
3333                             angular.element(element).css('display','block');
3334                             firstTabableElement = b2bDOMHelper.firstTabableElement(element);
3335                             lastTabableElement = b2bDOMHelper.lastTabableElement(element);
3336                             angular.element(element).css('display','none');
3337                         }
3338                         angular.element(firstTabableElement).bind('keydown', firstTabableElementKeyhandler);
3339                         angular.element(lastTabableElement).bind('keydown', lastTabableElementKeyhandler);
3340                     });
3341                 }
3342                 this.updateTabbableElements = function(){
3343                     $timeout(function () {
3344                         var element = elem[0].getElementsByClassName('b2b-flyout-container')[0];
3345                         angular.element(element).css('display','block');
3346                         firstTabableElement = b2bDOMHelper.firstTabableElement(element);
3347                         lastTabableElement = b2bDOMHelper.lastTabableElement(element);
3348                         angular.element(firstTabableElement).bind('keydown', firstTabableElementKeyhandler);
3349                         angular.element(lastTabableElement).bind('keydown', lastTabableElementKeyhandler);
3350                         angular.element(element).css('display','none');
3351                     });
3352                 }
3353                 this.unbindTabbaleEvents = function(){
3354                     if(angular.isDefined(firstTabableElement)){
3355                         angular.element(firstTabableElement).unbind('keydown', firstTabableElementKeyhandler);
3356                     }
3357
3358                     if(angular.isDefined(lastTabableElement)){
3359                         angular.element(lastTabableElement).unbind('keydown', lastTabableElementKeyhandler);
3360                     }
3361                 }
3362             }],
3363             link: function (scope, element, attrs, ctrl) {
3364
3365             }
3366         };
3367     }])
3368     .directive('b2bFlyoutToggler', [function () {
3369         return {
3370             restrict: 'A',
3371             require: '^b2bFlyout',
3372             link: function (scope, element, attrs, ctrl) {
3373                 element.bind('click', function (e) {
3374                     ctrl.toggleFlyoutState();
3375                 });
3376
3377                 scope.getTogglerDimensions = function () {
3378                     return element[0].getBoundingClientRect();
3379                 }
3380
3381                 scope.setTogglerFocus = function () {
3382                     element[0].focus();
3383                 }
3384
3385                 ctrl.registerTogglerScope(scope);
3386             }
3387         };
3388     }])
3389     .directive('b2bFlyoutContent', ['$position', '$timeout', '$documentBind', '$isElement', '$document', function ($position, $timeout, $documentBind, $isElement, $document) {
3390         return {
3391             restrict: 'EA',
3392             transclude: true,
3393             replace: true,
3394             require: '^b2bFlyout',
3395             scope: {
3396                 horizontalPlacement: '@',
3397                 verticalPlacement: '@',
3398                 flyoutStyle: '@',
3399                 flyoutTitle: '@',
3400                 contentUpdated: "=?"
3401             },
3402             templateUrl: 'b2bTemplate/flyout/flyoutContent.html',
3403             link: function (scope, element, attrs, ctrl) {
3404                 var flyoutStyleArray, eachCssProperty, cssPropertyKey, cssPropertyVal, temp;
3405                 scope.openFlyout = false;
3406                 if (!scope.horizontalPlacement) {
3407                     scope.horizontalPlacement = 'center';
3408                 }
3409                 if (!scope.verticalPlacement) {
3410                     scope.verticalPlacement = 'below';
3411                 }
3412
3413                 scope.toggleFlyout = function () {
3414
3415                     scope.openFlyout = !scope.openFlyout;
3416
3417                     if (scope.openFlyout) {
3418
3419                         if (angular.isDefined(scope.flyoutStyle) && scope.flyoutStyle != "") {
3420                             flyoutStyleArray = scope.flyoutStyle.split(";");
3421                             for (i = 0; i < flyoutStyleArray.length; i++) {
3422                                 eachCssProperty = flyoutStyleArray[i].split(":");
3423                                 if (eachCssProperty.length == 2) {
3424                                     cssPropertyKey = eachCssProperty[0].trim();
3425                                     cssPropertyVal = eachCssProperty[1].trim();
3426                                     angular.element(element[0])[0].style[cssPropertyKey] = cssPropertyVal;
3427                                 }
3428                             }
3429                         }
3430
3431                         angular.element(element[0]).css({
3432                             'opacity': 0,
3433                             'display': 'block'
3434                         });
3435
3436                         var flyoutIcons = angular.element(document.querySelectorAll(".b2b-flyout-icon"));
3437                         angular.forEach(flyoutIcons, function (elm) {
3438                             angular.element(elm)[0].blur();
3439                         });
3440
3441                         $timeout(function () {
3442                             ctrl.setTogglerFocus();
3443
3444                             var togglerDimensions = ctrl.getTogglerDimensions();
3445                             var flyoutDimensions = element[0].getBoundingClientRect();
3446
3447                             switch (scope.horizontalPlacement) {
3448                             case "left":
3449                                 angular.element(element[0]).css({
3450                                     'left': ((togglerDimensions.width / 2) - 26) + 'px'
3451                                 });
3452                                 break;
3453                             case "right":
3454                                 angular.element(element[0]).css({
3455                                     'right': ((togglerDimensions.width / 2) - 23) + 'px'
3456                                 });
3457                                 break;  
3458
3459                             case "centerLeft":
3460                                 var marginLeft =  10-(flyoutDimensions.width)-20;
3461                                 angular.element(element[0]).css({
3462                                     'margin-left': marginLeft + 'px'
3463                                 });
3464                                 break;
3465                             case "centerRight":
3466                                 angular.element(element[0]).css({
3467                                     'left': ((togglerDimensions.width + 9 )) + 'px'
3468                                 });
3469                                 break;    
3470
3471                             default:
3472                                 var marginLeft = (togglerDimensions.width / 2) - (flyoutDimensions.width / 2) - 8;
3473                                 angular.element(element[0]).css({
3474                                     'margin-left': marginLeft + 'px'
3475                                 });
3476                             }
3477
3478                             switch (scope.verticalPlacement) {
3479                             case "above":
3480                                 angular.element(element[0]).css({
3481                                     'top': -(flyoutDimensions.height + 13) + 'px'
3482                                 });
3483                                 break;
3484                             case "centerLeft":
3485                                 angular.element(element[0]).css({
3486                                     'top': -((togglerDimensions.height-13))+ 'px'
3487                                 });
3488                                 break;
3489                             case "centerRight":
3490                                 angular.element(element[0]).css({
3491                                     'top': -(flyoutDimensions.height - 23)+ 'px'
3492                                 });
3493                                 break;                                    
3494                             default:
3495                                 angular.element(element[0]).css({
3496                                     'top': (togglerDimensions.height + 13) + 'px'
3497                                 });
3498                             }
3499
3500                             angular.element(element[0]).css({
3501                                 'opacity': 1
3502                             });
3503                         }, 100);
3504                     } else {
3505                         scope.hideFlyout();
3506                     }
3507                 };
3508
3509                 scope.gotFocus = function () {
3510                     scope.openFlyout = false;
3511                     scope.hideFlyout();
3512                     ctrl.setTogglerFocus();
3513                     scope.$apply();
3514                 };
3515
3516                 scope.closeFromChild = function (e) {
3517                     scope.openFlyout = false;
3518                     scope.hideFlyout();
3519                     ctrl.setTogglerFocus();
3520                     scope.$apply();
3521                 };
3522
3523                 scope.hideFlyout = function () {
3524                     angular.element(element[0]).css({
3525                         'opacity': 0,
3526                         'display': 'none'
3527                     });
3528                 };
3529
3530                 scope.closeFlyout = function (e) {
3531                     var isElement = $isElement(angular.element(e.target), element, $document);
3532                     if ((e.type === "keydown" && e.which === 27) || ((e.type === "click" || e.type==="touchend") && !isElement)) {
3533                         scope.openFlyout = false;
3534                         scope.hideFlyout();
3535                         ctrl.setTogglerFocus();
3536                         scope.$apply();
3537                     }
3538                 };
3539
3540                 scope.$watch('openFlyout', function () {
3541                     ctrl.updateAriaModel(scope.openFlyout);
3542                 });
3543
3544                 $documentBind.click('openFlyout', scope.closeFlyout, scope);
3545                 $documentBind.event('keydown', 'openFlyout', scope.closeFlyout, scope);
3546                 $documentBind.event('touchend', 'openFlyout', scope.closeFlyout, scope);
3547                 ctrl.registerContentScope(scope);
3548
3549                 if (angular.isDefined(scope.contentUpdated) && scope.contentUpdated !== null) {
3550                     scope.$watch('contentUpdated', function (newVal, oldVal) {
3551                         if(newVal){
3552                             if (newVal !== oldVal) {
3553                                 ctrl.unbindTabbaleEvents();
3554                                 ctrl.associateTabEvent();
3555                             }
3556                             scope.contentUpdated = false;
3557                         } 
3558                     });
3559                 }  
3560
3561             }
3562         };
3563     }])
3564     .directive('b2bCloseFlyout', [function () {
3565         return {
3566             restrict: 'A',
3567             require: '^b2bFlyout',
3568             scope: {
3569                 closeFlyout: '&'
3570             },
3571             link: function (scope, element, attrs, ctrl) {
3572                 element.bind('click touchstart', function (e) {
3573                     scope.closeFlyout(e);
3574                     ctrl.closeFlyout(e);
3575                 });
3576             }
3577         };
3578     }])
3579     .directive('b2bFlyoutTrapFocusInside', [function () {
3580         return {
3581             restrict: 'A',
3582             transclude: false,
3583             require: '^b2bFlyout',
3584             link: function (scope, elem, attr, ctrl) {
3585                 /* Before opening modal, find the focused element */
3586                 ctrl.updateTabbableElements();
3587             }
3588         };
3589     }]);
3590 /**
3591  * @ngdoc directive
3592  * @name Layouts.att:footer
3593  *
3594  * @description
3595  *  <file src="src/footer/docs/readme.md" />
3596  *
3597  * @usage
3598  * 
3599  <footer class="b2b-footer-wrapper" role="contentinfo" aria-label="footer">
3600         <div class="b2b-footer-container" b2b-column-switch-footer footer-link-items='footerItems'>
3601             <hr>
3602             <div class="divider-bottom-footer">
3603                 <div class="span2 dispalyInline">&nbsp;</div>
3604                 <div class="span6 dispalyInline">
3605                     <ul class="footer-nav-content">
3606                         <li><a href="Terms_of_use.html" title="Terms of use" id="foot0">Terms of use</a>|</li>
3607                         <li><a href="Privacy_policy.html" title="Privacy policy" id="foot1" class="active">Privacy policy</a>|</li>
3608                         <li><a href="Tollfree_directory_assistance.html" title="Tollfree directory assistance" id="foot2">Tollfree directory assistance</a>|</li>
3609                         <li><a href="compliance.html" title="Accessibility" id="foot3">Accessibility</a></li>
3610
3611                     </ul>
3612                     <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.
3613
3614                     </p>
3615                 </div>
3616                 <div class="span3 footerLogo dispalyInline">
3617                     <a href="index.html" class="footer-logo">
3618                         <i class="icon-primary-att-globe"><span class="hidden-spoken">A T &amp; T</span></i>
3619                         <h2 class="logo-title">AT&amp;T</h2>
3620                     </a>
3621                 </div>
3622             </div>
3623
3624         </div>  
3625     </footer>
3626
3627  * @example
3628  *  <section id="code">   
3629  <example module="b2b.att">
3630  <file src="src/footer/docs/demo.html" />
3631  <file src="src/footer/docs/demo.js" />
3632  </example>
3633  </section>
3634  *
3635  */
3636 angular.module('b2b.att.footer', ['b2b.att.utilities']).
3637         directive('b2bColumnSwitchFooter', [function() {
3638                 return {
3639                     restrict: 'A',
3640                     transclude: true,
3641                     scope: {
3642                         footerLinkItems: "="
3643                     },
3644                     templateUrl: 'b2bTemplate/footer/footer_column_switch_tpl.html',
3645                     link: function(scope) {
3646                         var tempFooterColumns = scope.footerLinkItems.length;
3647                         scope.footerColumns = 3;
3648                         if ( (tempFooterColumns === 5) || (tempFooterColumns === 4) ) {
3649                             scope.footerColumns = tempFooterColumns;
3650                         }
3651                     }
3652
3653                 };
3654
3655             }]);
3656      
3657
3658 /**
3659  * @ngdoc directive
3660  * @name Layouts.att:header
3661  *
3662  * @description
3663  *  <file src="src/header/docs/readme.md" />
3664  *
3665  * @usage
3666  *  <li b2b-header-menu class="header__item b2b-headermenu" ng-repeat="item in tabItems" role="presentation">
3667         <a href="#" class="menu__item" role="menuitem">{{item.title}}</a>
3668         <div class="header-secondary-wrapper">
3669             <ul class="header-secondary" role="menu">
3670                 <li class="header-subitem" b2b-header-submenu ng-repeat="i in item.subitems" role="presentation">
3671                     <a href="#" class="menu__item" aria-haspopup="true" role="menuitem">{{i.value}}</a>
3672                     <div class="header-tertiary-wrapper" ng-if="i.links">
3673                         <ul class="header-tertiary" role="menu">
3674                             <li b2b-header-tertiarymenu ng-repeat="link in i.links" role="presentation">
3675                                 <label>{{link.title}}</label>
3676                                 <div b2b-tertiary-link ng-repeat="title in link.value">
3677                                     <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>
3678                                     <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>
3679                                     <ul class="header-quarternary" role="menu"  ng-if="title.subitems">
3680                                         <li b2b-header-quarternarymenu role="presentation">
3681                                             <a href="{{nav.href}}" ng-repeat="nav in title.subitems" role="menuitem" aria-haspopup="true">{{nav.title}}</a>
3682                                         </li>
3683                                     </ul>
3684                                 </div>
3685                             </li>
3686                         </ul>
3687                     </div>
3688                 </li>
3689             </ul>
3690         </div>
3691     </li> 
3692  *
3693  * @example
3694  *  <section id="code">
3695  <example module="b2b.att.header">
3696  <file src="src/header/docs/demo.html" />
3697  <file src="src/header/docs/demo.js" />
3698  </example>
3699  </section>
3700  *
3701  */
3702 angular.module('b2b.att.header', ['b2b.att.dropdowns','b2b.att.utilities'])
3703     .directive('b2bHeaderMenu', ['keymap', '$documentBind', '$timeout', '$isElement', '$document', function (keymap, $documentBind, $timeout, $isElement, $document) {
3704         return {
3705             restrict: 'A',
3706             controller:['$scope',function($scope){
3707                 this.nextSiblingFocus = function (elObj,flag) {
3708                         if (elObj.nextElementSibling) {
3709                             if(flag){
3710                                 var nextmenuItem = this.getFirstElement(elObj.nextElementSibling,'a');
3711                                 nextmenuItem.focus();
3712                             }else{
3713                                 elObj.nextElementSibling.focus();
3714                             }
3715                         }
3716                 };
3717                 
3718                 this.previousSiblingFocus = function (elObj,flag) {
3719                         if (elObj.previousElementSibling) {
3720                             if(flag){
3721                                 var prevmenuItem = this.getFirstElement(elObj.previousElementSibling,'a');
3722                                 prevmenuItem.focus();
3723                             }else{
3724                                 elObj.previousElementSibling.focus();
3725                             }
3726                         }
3727                 };
3728                     
3729                 this.getFirstElement = function(elmObj,selector){
3730                         return elmObj.querySelector(selector);                        
3731                     };
3732             }],
3733             link: function (scope, elem,attr,ctrl) {
3734                 scope.showMenu = false;
3735                 var activeElm, subMenu, tertiaryMenu, el= angular.element(elem)[0], 
3736                         menuItem = angular.element(elem[0].children[0]);
3737                 menuItem.bind('click', function () {
3738                     elem.parent().children().removeClass('active');
3739                     elem.addClass('active');
3740                     var elems= this.parentElement.parentElement.querySelectorAll('li[b2b-header-menu]>a');
3741                     for (var i=0; i<elems.length; i++) {
3742                         elems[i].setAttribute("aria-expanded",false);
3743                     }
3744                     scope.showMenu = true;
3745                     var elmTofocus = ctrl.getFirstElement(this.parentElement,'li[b2b-header-submenu]');
3746                     elmTofocus.firstElementChild.focus();
3747                     this.setAttribute('aria-expanded',true);
3748                     scope.$apply();
3749                 });
3750                
3751                 elem.bind('keydown', function (evt) {
3752                     activeElm = document.activeElement;
3753                     subMenu = ctrl.getFirstElement(activeElm.parentElement,'li[b2b-header-submenu]');
3754                     tertiaryMenu = ctrl.getFirstElement(activeElm.parentElement,'li[b2b-header-tertiarymenu]');
3755                     switch (evt.keyCode) {
3756                         case keymap.KEY.ENTER:
3757                         case keymap.KEY.SPACE:
3758                             elem[0].click();
3759                             break;
3760                         case keymap.KEY.UP:
3761                             evt.stopPropagation();
3762                             evt.preventDefault();
3763                             if (activeElm.parentElement.hasAttribute('b2b-header-submenu')) {
3764                                 menuItem[0].focus();
3765                             }
3766                             break;
3767                         case keymap.KEY.DOWN:
3768                             evt.stopPropagation();
3769                             evt.preventDefault();
3770                             if (subMenu) {
3771                                 subMenu.firstElementChild.focus();
3772                             } else if (tertiaryMenu) {
3773                                 var firstSubitem = ctrl.getFirstElement(tertiaryMenu,'a.header-tertiaryitem');
3774                                 firstSubitem.focus();
3775                             }
3776                             break;
3777                         case keymap.KEY.RIGHT:
3778                             evt.stopPropagation();
3779                             evt.preventDefault();
3780                             if (activeElm.parentElement.hasAttribute('b2b-header-submenu')) {
3781                                 var elm = angular.element(activeElm.parentElement)[0];
3782                                 ctrl.nextSiblingFocus(elm,true);
3783                             } else if (activeElm.parentElement.parentElement.hasAttribute('b2b-header-tertiarymenu')) {
3784                                 var tertiaryLI = angular.element(activeElm.parentElement.parentElement)[0];
3785                                 if (tertiaryLI.nextElementSibling) {
3786                                     var nextElm = ctrl.getFirstElement(tertiaryLI.nextElementSibling,"a.header-tertiaryitem");
3787                                     nextElm.focus();
3788                                 }
3789                             }
3790                             else if(activeElm.parentElement.hasAttribute('b2b-header-menu')){
3791                                 ctrl.nextSiblingFocus(el,true);
3792                             }
3793                             break;
3794                         case keymap.KEY.LEFT:
3795                             evt.stopPropagation();
3796                             evt.preventDefault();
3797                             if (activeElm.parentElement.hasAttribute('b2b-header-submenu')) {
3798                                 var previousElm = angular.element(activeElm.parentElement)[0];
3799                                 ctrl.previousSiblingFocus(previousElm,true);
3800                             } else if (activeElm.parentElement.parentElement.hasAttribute('b2b-header-tertiarymenu')) {
3801                                 var tertiaryLI = angular.element(activeElm.parentElement.parentElement)[0];
3802                                 if (tertiaryLI.previousElementSibling) {
3803                                     var prevElm = ctrl.getFirstElement(tertiaryLI.previousElementSibling,"a.header-tertiaryitem");
3804                                     prevElm.focus();
3805                                 }
3806                             }
3807                             else if(activeElm.parentElement.hasAttribute('b2b-header-menu')) {
3808                                 ctrl.previousSiblingFocus(el,true);
3809                             }
3810                             break;
3811                         case keymap.KEY.ESC:
3812                             evt.stopPropagation();
3813                             evt.preventDefault();
3814                             scope.showMenu = false;
3815                             elem.removeClass('active');
3816                             menuItem.attr('aria-expanded',false);
3817                             $timeout(function(){
3818                                 menuItem[0].focus();
3819                             },100);
3820                             scope.$apply();
3821                             break;
3822                         default:
3823                             break;
3824                     }
3825                 });
3826                 var outsideClick = function (e) {
3827                     var isElement = $isElement(angular.element(e.target), elem, $document);
3828                     if (!isElement) {
3829                         scope.showMenu = false;
3830                         elem.removeClass('active');
3831                         scope.$apply();
3832                     }
3833                 };
3834                 $documentBind.click('showMenu', outsideClick, scope);
3835             }
3836         };
3837     }]).directive('b2bHeaderSubmenu', ['$timeout',function ($timeout) {
3838         return{
3839             restrict: 'A',
3840             link: function (scope, elem) {
3841                 var caretSign = angular.element("<i class='menuCaret'></i>");
3842                 $timeout(function(){
3843                     var menuItem = angular.element(elem[0].children[0]);
3844                     menuItem.bind('focus mouseenter', function () {
3845                         elem.parent().children().removeClass('active');
3846                         elem.addClass('active');
3847                         if(elem[0].childElementCount > 1){ // > 1 has third level menu
3848                             menuItem.attr('aria-expanded',true);
3849                             menuItem.attr('aria-haspopup',true);
3850                         }
3851                         var caretLeft = (elem[0].offsetLeft +  elem[0].offsetWidth/2) - 10;
3852                         caretSign.css({left: caretLeft + 'px'});
3853                         angular.element(caretSign);
3854                         var tertiaryItems = elem[0].querySelectorAll('[b2b-header-tertiarymenu]');
3855                         if(tertiaryItems.length >=1){
3856                             elem.append(caretSign);
3857                         }
3858                     });
3859                     menuItem.bind('blur', function () {
3860                         $timeout(function () {
3861                             var parentElm = document.activeElement.parentElement.parentElement;
3862                             if(parentElm){
3863                                 if (!(parentElm.hasAttribute('b2b-header-tertiarymenu'))) {
3864                                     elem.removeClass('active');
3865                                     if(elem[0].childElementCount > 1){ // > 1 has third level menu
3866                                         menuItem.attr('aria-expanded',false);
3867                                     }
3868                                     var caret = elem[0].querySelector('.menuCaret');
3869                                     if(caret){
3870                                         caret.remove();
3871                                     }   
3872                                 }
3873                             }
3874                         });
3875                     });
3876                 });
3877             }
3878         };
3879     }]).directive('b2bHeaderTertiarymenu', ['$timeout','keymap', function ($timeout,keymap){
3880         return{
3881             restrict: 'A',
3882             require:'^b2bHeaderMenu',
3883             link: function (scope, elem,attr,ctrl) {
3884                 
3885                 elem.bind('keydown', function (evt) {
3886                     var activeElm = document.activeElement;
3887                     var activeParentElm = activeElm.parentElement;
3888                     var activeParentObj  = angular.element(activeParentElm)[0];
3889                     
3890                     if(activeParentElm.hasAttribute('b2b-tertiary-link')){
3891                         var quarterNav = angular.element(activeParentElm)[0].querySelector('li[b2b-header-quarternarymenu]');
3892                         if(quarterNav){
3893                             var links = ctrl.getFirstElement(angular.element(quarterNav)[0],'a');
3894                         }
3895                     }
3896                     var tertiaryMenu = activeElm.parentElement.parentElement.parentElement;
3897                     var tertiaryMenuFlag = tertiaryMenu.hasAttribute('b2b-tertiary-link');
3898                     
3899                     switch (evt.keyCode) {
3900                         case keymap.KEY.DOWN:
3901                             evt.stopPropagation();
3902                             evt.preventDefault();
3903                             if (activeParentElm.hasAttribute('b2b-tertiary-link')) {
3904                                 if(angular.element(quarterNav).hasClass('active')){
3905                                     links.focus();
3906                                 }else if(activeParentObj.nextElementSibling){
3907                                     ctrl.nextSiblingFocus(activeParentObj,true);
3908                                 }
3909                             }
3910                             else if(angular.element(activeParentElm).hasClass('active')){
3911                                 ctrl.nextSiblingFocus(activeElm);
3912                             }
3913                             break;                        
3914                         case keymap.KEY.UP:
3915                             evt.stopPropagation();
3916                             evt.preventDefault();
3917                             if(activeParentElm.hasAttribute('b2b-tertiary-link')){
3918                                 if(activeParentObj.previousElementSibling.hasAttribute('b2b-tertiary-link')){
3919                                     ctrl.previousSiblingFocus(activeParentObj,true);
3920                                 }else{
3921                                     var elm = angular.element(activeElm.parentElement.parentElement.parentElement.parentElement.parentElement)[0];
3922                                     ctrl.getFirstElement(elm,"a").focus();
3923                                 }
3924                             }else if(angular.element(activeParentElm).hasClass('active')){
3925                                     if (activeElm.previousElementSibling) {
3926                                         ctrl.previousSiblingFocus(activeElm);
3927                                     }else if (tertiaryMenuFlag) {
3928                                         var elm = angular.element(tertiaryMenu)[0];
3929                                         ctrl.getFirstElement(elm,"a.header-tertiaryitem").focus();
3930                                     }
3931                                 }
3932                             break;
3933                         default:
3934                             break;
3935                     }
3936                 });
3937             }            
3938         };          
3939     }]).directive('b2bHeaderTogglemenu', ['$timeout', 'keymap', function ($timeout, keymap) {
3940         return{
3941             restrict: 'A',
3942             require: '^b2bHeaderMenu',
3943             link: function (scope, elem, attrs, ctrl) {
3944                 var quarterNav;
3945                 $timeout(function () {
3946                     quarterNav = angular.element(elem.parent())[0].querySelector('li[b2b-header-quarternarymenu]');
3947                     elem.bind('click', function () {
3948                         angular.element(quarterNav).toggleClass('active');
3949                     });
3950                 });
3951             }
3952         };
3953     }]).directive('b2bHeaderResponsive', ['$timeout',function ($timeout) {
3954         return{
3955             restrict: 'A',
3956             controller: function($scope){
3957                 this.applyMediaQueries = function(value){
3958                     document.querySelector('style').textContent += 
3959                         "@media screen and (max-width:950px) { \
3960                             .header__item.profile { right: " + value + "px; } \
3961                         }";
3962                 };
3963                 this.arrangeResponsiveHeader = function(children){
3964                     /* 
3965                      * clientWidth of 1090 === max-width of 1100px
3966                      * clientWidth of 920 === max-width of 950px
3967                      * see b2b-angular.css for rest of responsive header CSS
3968                      */
3969                   if (document.documentElement.clientWidth <= 920) { 
3970                         switch(children){
3971                             case 1:
3972                                 this.applyMediaQueries(200);                    
3973                                 break;
3974                             case 2:
3975                                 this.applyMediaQueries(200);                            
3976                                 break;
3977                             default: // anthing above 3, however, should not have more than 3 to date
3978                                 this.applyMediaQueries(200);                                                                            
3979                         }
3980                     }
3981                 }
3982             },
3983             link: function (scope, elem, attrs, ctrl) {
3984                 var children;
3985                 var profile;
3986                 
3987                 // onload of page
3988                 $timeout(function(){ 
3989                     profile = document.querySelector('li.header__item.profile');
3990                     children = angular.element(profile).children().length;
3991                     
3992                     ctrl.arrangeResponsiveHeader(children); // shift right-side icon flyovers
3993                 });
3994
3995                 // on screen resize
3996                 window.addEventListener('resize', function(event){ // caret adjustmet
3997                     var activeSubmenu = elem[0].querySelector('[b2b-header-menu] [b2b-header-submenu].active');
3998                     var activeSubmenuEl = angular.element(activeSubmenu);
3999                     if(activeSubmenu){
4000                         var caretSign = activeSubmenu.querySelector('i.menuCaret');
4001                         if(caretSign){
4002                             var caretSignEl = angular.element(caretSign);
4003                             var caretLeft = (activeSubmenu.offsetLeft +  activeSubmenu.offsetWidth/2) - 10;
4004                             caretSignEl.css({left: caretLeft + 'px'});
4005                         }
4006                     }
4007
4008                     ctrl.arrangeResponsiveHeader(children); // shift right-side icon flyovers
4009                 });
4010             }
4011         };
4012     }]);
4013
4014 /**
4015  * @ngdoc directive
4016  * @name Layouts.att:headings & copy
4017  *
4018  * @description
4019  *  <file src="src/headingsAndCopy/docs/readme.md" />
4020  *
4021  * @example
4022  <section id="code">
4023     <b>HTML + AngularJS</b>
4024     <example module="b2b.att">
4025     <file src="src/headingsAndCopy/docs/demo.html" />
4026 </example>
4027 </section>
4028  */
4029
4030 var b2bLegalCopy = angular.module('b2b.att.headingsAndCopy', []);
4031 /**
4032  * @ngdoc directive
4033  * @name Tabs, tables & accordions.att:horizontalTable
4034  *
4035  * @description
4036  *  <file src="src/horizontalTable/docs/readme.md" />
4037  *
4038  * @usage
4039  * @param {int} sticky - Number of sticky columns to have. Maximum of 3.
4040  * @param {boolean} refresh - A boolean that when set to true will force a re-render of table. Only use when using 'bulk mode'
4041  * @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.
4042  * @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. 
4043  * @example
4044  *  <section id="code">
4045         <example module="b2b.att">
4046             <file src="src/horizontalTable/docs/demo.html" />
4047             <file src="src/horizontalTable/docs/demo.js" />
4048        </example>
4049     </section>
4050  *
4051  */
4052 angular.module('b2b.att.horizontalTable', [])
4053     .constant('b2bHorizontalTableConfig', {
4054         'maxStickyColumns': 3
4055     })
4056     .directive('b2bHorizontalTable', ['$timeout', 'b2bHorizontalTableConfig', 'b2bDOMHelper', function ($timeout, b2bHorizontalTableConfig, b2bDOMHelper) {
4057         return {
4058             restrict: 'EA',
4059             scope: true,
4060             transclude: true,
4061             scope: {
4062                 numOfStickyCols: '=?sticky',
4063                 refresh: '=?',
4064                 legendContent: '=?',
4065                 retainColumnSet: '=?'
4066
4067             },
4068             templateUrl: 'b2bTemplate/horizontalTable/horizontalTable.html',
4069             link: function (scope, element, attrs, ctrl) {
4070                 scope.numOfStickyCols = scope.numOfStickyCols || 1;
4071                 scope.viewportIndex = scope.numOfStickyCols;
4072
4073                 var tableElement = element.find('table');
4074                 var thElements = element.find('th');
4075                 var innerContainer = angular.element(element[0].querySelector('.b2b-horizontal-table-inner-container'));
4076                 var outerContainer = angular.element(element[0].querySelector('.b2b-horizontal-table'));
4077
4078                 var tableColumns = [];
4079                 var tableRows = element.find('tr');
4080
4081                 var maxWidth = 0,
4082                     maxHeight = 0;
4083                 var totalWidth = element.children()[0].offsetWidth;
4084                 var lastVisibleColumn = 0;
4085                 var collectiveColumnWidth = [];
4086                 var collectiveRowHeight = [];
4087                 var columnSets = [];
4088                 var setIndex = 0; 
4089                 var stickyPixels = 0;
4090
4091                 var displayNoneCSS = {'display': 'none'};
4092                 var displayBlockCSS = {'display': 'table-cell'};
4093
4094                 // JM520E: This is a temporary hack until I solve the ngRepeat issue
4095                 function hack() {
4096                     if (element.find('th').length < scope.numOfStickyCols) {
4097                         // DOM ngRepeat is not ready, let's check back in 10 ms
4098                         $timeout(hack, 10, false);
4099                     } else {
4100                         if (scope.refresh !== undefined) {
4101                             scope.$watch('refresh', function(oldVal, newVal) { // this watch calls too many times
4102                                 if (!angular.equals(oldVal, newVal)) { //hackFinished && oldVal < newVal
4103                                     // From testing it takes about 30 ms before ngRepeat executes, so let's set initial timeout
4104                                     // NOTE: May need to expose timeout to developers. Application is known to have digest cycle of 3-5k watches.
4105                                     $timeout(init, 100, false);
4106                                     scope.refresh = false;
4107                                 }
4108                             });
4109                         }
4110
4111                         init();
4112                     }
4113                 }
4114
4115                 var init = function() {
4116                     // Reset this from a previous execution
4117                     tableColumns = [];
4118                     collectiveColumnWidth = [];
4119                     collectiveRowHeight = [];
4120                     maxWidth = 0;
4121                     maxHeight = 0;
4122                     lastVisibleColumn = 0;
4123                     columnSets = [];
4124                     if ((!!scope.retainColumnSet)) {
4125                         setIndex = 0;
4126                     }
4127                     visibleColumns = [];
4128                     stickyPixels = 0;
4129
4130                     tableElement = element.find('table');
4131                     thElements = element.find('th');
4132                     innerContainer = angular.element(element[0].querySelector('.b2b-horizontal-table-inner-container'));
4133                     outerContainer = angular.element(element[0].querySelector('.b2b-horizontal-table'));
4134                     totalWidth = element.children()[0].offsetWidth;
4135                     tableRows = element.find('tr');
4136
4137                     scope.disableLeft = true;
4138                     scope.disableRight = false;
4139
4140                     if (scope.numOfStickyCols > b2bHorizontalTableConfig.maxStickyColumns) {
4141                         throw new Error('Table can only support ' + b2bHorizontalTableConfig.maxStickyColumns + ' sticky columns.');
4142                     }             
4143
4144                     angular.forEach(tableRows, function(row, rowIndex) {
4145                         for(var j = 0; j < row.children.length; j++) {
4146                             if (tableColumns[j] === undefined) {
4147                                 tableColumns[j] = [];
4148                             }
4149                             tableColumns[j].push(row.children[j]);
4150                         }
4151                     });
4152
4153                     // We need to reset all the displayNones from previous runs, if applicable
4154                     if (attrs.refresh !== undefined && attrs.refresh !== '')  {
4155                         for (var i = scope.numOfStickyCols+1; i < tableColumns.length; i++) {
4156                             angular.element(tableColumns[i]).css(displayBlockCSS);
4157                         }
4158                     }   
4159
4160                     // We must calculate here as we need cells to be reset after re-render. 
4161                     angular.forEach(tableRows, function(row, rowIndex) {
4162                         collectiveRowHeight.push(findMax(row.children, 'height')); // BUG: Keeping this here now causes row height bugs
4163                     });
4164                     
4165                     console.log('tableColumns:', tableColumns);
4166                     for (var i = 0; i < tableColumns.length; i++) {
4167                         collectiveColumnWidth.push(findMax(tableColumns[i], 'width')); //offsetWidth doesn't take into account custom css inside
4168                     }
4169                     for(var i = 0; i < scope.numOfStickyCols; i++) {
4170                         maxWidth += collectiveColumnWidth[i];
4171                     }
4172
4173                     // BUG: The code I put in to fix the table not spanning 100%  width is now preventing 
4174                     // table cells from laying out more than stickyPixels and thus we have weird wrapping
4175                     stickyPixels = totalWidth-maxWidth;
4176
4177                     // At this point, for each tr, I need to set the properties (height) and each numOfStickyCols children
4178                     // should be set with sticky properties (margin-left and width)
4179                     var width = maxWidth;
4180                     var additive = 0; 
4181
4182                     if (angular.element(document).find('html').hasClass('isIE')) {
4183                         additive = 25;
4184                     }
4185                     var thObject = undefined;
4186                     for(var i = 0; i < scope.numOfStickyCols; i++) {
4187                         for (var j = 0; j < tableRows.length; j++) {
4188                             thObject = angular.element(tableRows[j].children[i]);
4189                             angular.element(thObject).css({
4190                                 'margin-left': -(width + 2) + 'px', 
4191                                 'width': (collectiveColumnWidth[i] + 3) + 'px', // instead of taking the max width, grab max width for that column
4192                                 'height': (collectiveRowHeight[j] + additive) + 'px',
4193                                 'position': 'absolute',
4194                                 'background-color': '#F2F2F2'
4195                             });
4196                         }
4197                         width -= collectiveColumnWidth[i];
4198                     }
4199                     angular.element(tableRows[0]).css('height', collectiveRowHeight[0] + 'px');
4200                     for(var i = 0; i < tableRows.length; i++) {
4201                         angular.element(tableRows[i]).css('height', (collectiveRowHeight[i] + additive) + 'px'); 
4202                     }
4203                     
4204                     innerContainer.css({
4205                         'padding-left': (maxWidth + 2) + 'px'
4206                     });
4207
4208
4209                     // Let's precompute all the (set) combinations beforehand
4210                     columnSets = []; 
4211                     for (var i = scope.numOfStickyCols; i < tableColumns.length;) {
4212                         visibleColumns = calculateVisibleColumns(i);
4213                         columnSets.push([i, visibleColumns]);
4214                         i = visibleColumns + 1;
4215                     }
4216                     
4217                     //columnSets = [[1, 1], [2,7]]; 
4218                     
4219                     updateCellDisplay(columnSets[setIndex]);
4220                     checkScrollArrows();
4221
4222                     scope.numOfCols = tableColumns.length;
4223
4224                     console.log('collectiveColumnWidth', collectiveColumnWidth);
4225                     console.log('collectiveRowHeight: ', collectiveRowHeight);
4226                 }
4227                 
4228
4229                 // Let's get started with some math!
4230                 hack();
4231
4232                 function calculateVisibleColumns(startingPoint) {
4233                     var usedWidth = 0, 
4234                         visibleColumns = startingPoint || scope.numOfStickyCols;
4235
4236                     while(usedWidth < stickyPixels && visibleColumns < collectiveColumnWidth.length) {
4237                         if (usedWidth+collectiveColumnWidth[visibleColumns] > stickyPixels) {
4238                             if (startingPoint === visibleColumns) {
4239                                 return visibleColumns; // The next cell is too large to fit, it should be only thing to fit
4240                             }
4241                             visibleColumns--;
4242                             return visibleColumns;
4243                         }
4244                         usedWidth += collectiveColumnWidth[visibleColumns];
4245                         visibleColumns++;
4246                     }
4247
4248                     if (usedWidth > stickyPixels) {
4249                         return --visibleColumns;
4250                     }
4251                     return visibleColumns;
4252                 }
4253
4254                 function updateCellDisplay(set) {
4255                     for (var i = scope.numOfStickyCols; i < tableColumns.length; i++) {
4256                         angular.element(tableColumns[i]).css(displayNoneCSS);
4257                     }
4258
4259                     for (var i = set[0]; i <= set[1]; i++) {
4260                         angular.element(tableColumns[i]).css(displayBlockCSS);
4261                     }
4262                 }
4263
4264                 function findMax(arr, prop) {
4265                     var max = 0;
4266                     var localVal = 0;
4267                     var prevDisplay;
4268                     var item;
4269                     for (var i = 0; i < arr.length; i++) {
4270                         item = arr[i];
4271                         prevDisplay = angular.element(item).css('display');
4272
4273                         if (scope.$$phase) {
4274                             scope.$digest();
4275                         }
4276                         // Remove inline styles, they will mess up calculations from original run
4277                         angular.element(item).css('height', '');
4278                         angular.element(item).css('width', ''); 
4279                         if (prop === 'width') {
4280                             // If we do not undo previous run's inline styles, this will grow widths on each re-render.
4281                             localVal = Math.ceil(parseInt(window.getComputedStyle(item).width.split('px')[0], 10)) + 30; // 30 px is padding
4282                         } else if (prop === 'offsetWidth') {
4283                             localVal = item.offsetWidth;
4284                         } else if (prop === 'height') {
4285                             //localVal = item.offsetHeight;
4286                             localVal = Math.ceil(parseInt(window.getComputedStyle(item).height.split('px')[0], 10))
4287                         }
4288
4289                         if (localVal >= max) {
4290                             max = localVal;
4291                         }
4292                     }
4293                     
4294                     return max;
4295                 }
4296
4297                 
4298
4299                 function checkScrollArrows() {
4300                     scope.disableLeft = (setIndex === 0);
4301                     scope.disableRight = !(setIndex < columnSets.length-1);
4302                 }
4303
4304                 scope.moveViewportLeft = function () {
4305                     setIndex--;
4306                     updateCellDisplay(columnSets[setIndex]);
4307                     checkScrollArrows();
4308
4309                     if (scope.disableLeft) {
4310                         element.find('span')[0].focus();
4311                     }
4312                 };
4313                 
4314                 scope.moveViewportRight = function () {
4315                     setIndex++;
4316                     updateCellDisplay(columnSets[setIndex]);
4317                     checkScrollArrows();
4318                     
4319                     if (scope.disableRight) {
4320                         element.find('span')[0].focus();
4321                     }
4322                 };
4323
4324                 scope.getColumnSet = function () {
4325                     return columnSets[setIndex];
4326                 };
4327
4328                 innerContainer.bind('scroll', function () {
4329                     $timeout(function () {
4330                         checkScrollArrows();
4331                     }, 1);
4332                 });
4333
4334             }
4335         };
4336     }]);
4337 /**
4338  * @ngdoc directive
4339  * @name Forms.att:hourPicker
4340  *
4341  * @description
4342  *  <file src="src/hourPicker/docs/readme.md" />
4343  *
4344  * @usage
4345  * <div b2b-hourpicker ng-model="hourpickerValue.value"></div>
4346     
4347  * @example
4348  *  <section id="code">
4349         <example module="b2b.att">
4350             <file src="src/hourPicker/docs/demo.html" />
4351             <file src="src/hourPicker/docs/demo.js" />
4352         </example>
4353     </section>
4354  *
4355  */
4356 angular.module('b2b.att.hourPicker', ['b2b.att.utilities'])
4357
4358 .constant('b2bHourpickerConfig', {
4359     dayOptions: [{
4360         title: 'sunday',
4361         caption: 'Sun',
4362         label: 'S',
4363         disabled: false
4364     }, {
4365         title: 'monday',
4366         caption: 'Mon',
4367         label: 'M',
4368         disabled: false
4369     }, {
4370         title: 'tuesday',
4371         caption: 'Tues',
4372         label: 'T',
4373         disabled: false
4374     }, {
4375         title: 'wednesday',
4376         caption: 'Wed',
4377         label: 'W',
4378         disabled: false
4379     }, {
4380         title: 'thursday',
4381         caption: 'Thu',
4382         label: 'T',
4383         disabled: false
4384     }, {
4385         title: 'friday',
4386         caption: 'Fri',
4387         label: 'F',
4388         disabled: false
4389     }, {
4390         title: 'saturday',
4391         caption: 'Sat',
4392         label: 'S',
4393         disabled: false
4394     }],
4395     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'],
4396     startTimeDefaultOptionIndex: -1,
4397     startTimeDefaultMeridiem: "am",
4398     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'],
4399     endTimeDefaultOptionIndex: -1,
4400     endTimeDefaultMeridiem: "pm",
4401     sameDayOption: true
4402 })
4403
4404 .factory('b2bNormalizeHourpickerValues', [function () {
4405     var _normalize = function (hourpickerValues) {
4406         if (angular.isDefined(hourpickerValues) && hourpickerValues != null) {
4407             var finalHourpickerValues = [];
4408             var hourpickerValue = {};
4409             var days = {};
4410             for (var i = 0; i < hourpickerValues.length; i++) {
4411                 days = hourpickerValues[i].days ? hourpickerValues[i].days : {};
4412                 hourpickerValue.startTime = hourpickerValues[i].startTime ? hourpickerValues[i].startTime : '';
4413                 hourpickerValue.startMeridiem = hourpickerValues[i].startMeridiem ? hourpickerValues[i].startMeridiem : '';
4414                 hourpickerValue.endTime = hourpickerValues[i].endTime ? hourpickerValues[i].endTime : '';
4415                 hourpickerValue.endMeridiem = hourpickerValues[i].endMeridiem ? hourpickerValues[i].endMeridiem : '';
4416                 hourpickerValue.days = [];
4417
4418                 var retrieveDaysText = function (daysDetails) {
4419                     var daysTexts = [];
4420                     var first = -1;
4421                     var last = -1;
4422                     var index = -1;
4423                     for (var i in days) {
4424                         if (days[i].value) {
4425                             daysTexts.push(i);
4426                         }
4427                     }
4428
4429                     first = daysTexts[0];
4430                     last = daysTexts[0];
4431                     index = 0;
4432                     hourpickerValue.days[index] = days[first].caption;
4433                     if (daysTexts.length > 1) {
4434                         for (var i = 1; i < daysTexts.length; i++) {
4435                             if (daysTexts[i] - last === 1) {
4436                                 last = daysTexts[i];
4437                                 hourpickerValue.days[index] = days[first].caption + ' - ' + days[last].caption;
4438                             } else {
4439                                 index++;
4440                                 first = last = daysTexts[i];
4441                                 hourpickerValue.days[index] = days[first].caption;
4442                             }
4443                         }
4444                     }
4445                 };
4446                 retrieveDaysText();
4447
4448                 finalHourpickerValues.push(angular.copy(hourpickerValue));
4449             }
4450
4451             return angular.copy(finalHourpickerValues);
4452         }
4453     };
4454
4455     return {
4456         normalize: _normalize
4457     };
4458 }])
4459
4460 .directive('b2bHourpicker', ['b2bHourpickerConfig', 'b2bNormalizeHourpickerValues', function (b2bHourpickerConfig, b2bNormalizeHourpickerValues) {
4461     return {
4462         restrict: 'EA',
4463         replace: false,
4464         scope: true,
4465         require: 'ngModel',
4466         templateUrl: 'b2bTemplate/hourPicker/b2bHourpicker.html',
4467         controller: ['$scope', function (scope) {
4468
4469         }],
4470         link: function (scope, elem, attr, ctrl) {
4471             scope.hourpicker = {};
4472             scope.hourpicker.dayOptions = attr.dayOptions ? scope.$parent.$eval(attr.dayOptions) : b2bHourpickerConfig.dayOptions;
4473             scope.hourpicker.startTimeOptions = attr.startTimeOptions ? scope.$parent.$eval(attr.startTimeOptions) : b2bHourpickerConfig.startTimeOptions;
4474             scope.hourpicker.endTimeOptions = attr.endTimeOptions ? scope.$parent.$eval(attr.endTimeOptions) : b2bHourpickerConfig.endTimeOptions;
4475             scope.hourpicker.startTimeDefaultOptionIndex = attr.startTimeDefaultOptionIndex ? scope.$parent.$eval(attr.startTimeDefaultOptionIndex) : b2bHourpickerConfig.startTimeDefaultOptionIndex;
4476             scope.hourpicker.endTimeDefaultOptionIndex = attr.endTimeDefaultOptionIndex ? scope.$parent.$eval(attr.endTimeDefaultOptionIndex) : b2bHourpickerConfig.endTimeDefaultOptionIndex;
4477             scope.hourpicker.startTimeDefaultMeridiem = attr.startTimeDefaultMeridiem ? scope.$parent.$eval(attr.startTimeDefaultMeridiem) : b2bHourpickerConfig.startTimeDefaultMeridiem;
4478             scope.hourpicker.endTimeDefaultMeridiem = attr.endTimeDefaultMeridiem ? scope.$parent.$eval(attr.endTimeDefaultMeridiem) : b2bHourpickerConfig.endTimeDefaultMeridiem;
4479             scope.hourpicker.sameDayOption = attr.sameDayOption ? scope.$parent.$eval(attr.sameDayOption) : b2bHourpickerConfig.sameDayOption;
4480             scope.hourpicker.editMode = -1;
4481
4482             scope.hourpickerValues = [];
4483             scope.finalHourpickerValues = [];
4484             scope.addHourpickerValue = function (hourpickerPanelValue) {
4485                 if (hourpickerPanelValue) {
4486                     if (scope.hourpicker.editMode > -1) {
4487                         scope.hourpickerValues[scope.hourpicker.editMode] = hourpickerPanelValue;
4488                         scope.hourpicker.editMode = -1;
4489                     } else {
4490                         scope.hourpickerValues.push(hourpickerPanelValue);
4491                     }
4492                 }
4493                 scope.finalHourpickerValues = b2bNormalizeHourpickerValues.normalize(angular.copy(scope.hourpickerValues));
4494                 ctrl.$setViewValue(angular.copy(scope.hourpickerValues));
4495             };
4496             ctrl.$render = function () {
4497                 if (angular.isDefined(ctrl.$modelValue)) {
4498                     scope.hourpickerValues = angular.copy(ctrl.$modelValue);
4499                     scope.finalHourpickerValues = b2bNormalizeHourpickerValues.normalize(angular.copy(scope.hourpickerValues));
4500                 }
4501             };
4502             scope.editHourpickerValue = function (index) {
4503                 scope.hourpickerPanelValue = angular.copy(scope.hourpickerValues[index]);
4504                 scope.hourpicker.editMode = index;
4505             };
4506             scope.deleteHourpickerValue = function (index) {
4507                 scope.hourpickerValues.splice(index, 1);
4508                 scope.resetHourpickerPanelValue();
4509                 scope.addHourpickerValue();
4510             };
4511
4512             scope.setValidity = function (errorType, errorValue) {
4513                 ctrl.$setValidity(errorType, errorValue);
4514             }
4515         }
4516     }
4517 }])
4518
4519 .directive('b2bHourpickerPanel', [function () {
4520     return {
4521         restrict: 'EA',
4522         replace: false,
4523         templateUrl: 'b2bTemplate/hourPicker/b2bHourpickerPanel.html',
4524         controller: ['$scope', function (scope) {
4525
4526         }],
4527         link: function (scope, elem, attr, ctrl) {
4528             var hourpickerPanelValueTemplate = {
4529                 days: {},
4530                 startTime: '',
4531                 startMeridiem: 'am',
4532                 endTime: '',
4533                 endMeridiem: 'pm'
4534             };
4535             for (var i = 0; i < scope.hourpicker.dayOptions.length; i++) {
4536                 hourpickerPanelValueTemplate.days[i] = {
4537                     value: false,
4538                     title: scope.hourpicker.dayOptions[i].title,
4539                     caption: scope.hourpicker.dayOptions[i].caption
4540                 };
4541             }
4542             scope.hourpickerPanelValue = {};
4543             scope.disableAddBtn = true;
4544
4545             scope.$watch('hourpickerPanelValue.days', function(){
4546                 for(var i in scope.hourpickerPanelValue.days)
4547                 {
4548                     if(scope.hourpickerPanelValue.days[i].value)
4549                     {
4550                         scope.disableAddBtn = false;
4551                         break;
4552                     }
4553                     scope.disableAddBtn = true;
4554                 }
4555             }, true);
4556
4557             scope.resetHourpickerPanelValue = function () {
4558                 scope.hourpickerPanelValue = angular.copy(hourpickerPanelValueTemplate);
4559                 if (scope.hourpicker.startTimeDefaultOptionIndex > -1) {
4560                     scope.hourpickerPanelValue.startTime = scope.hourpicker.startTimeOptions[scope.hourpicker.startTimeDefaultOptionIndex];
4561                 }
4562                 if (scope.hourpicker.endTimeDefaultOptionIndex > -1) {
4563                     scope.hourpickerPanelValue.endTime = scope.hourpicker.endTimeOptions[scope.hourpicker.endTimeDefaultOptionIndex];
4564                 }
4565                 scope.hourpickerPanelValue.startMeridiem = scope.hourpicker.startTimeDefaultMeridiem;
4566                 scope.hourpickerPanelValue.endMeridiem = scope.hourpicker.endTimeDefaultMeridiem;
4567                 scope.hourpicker.editMode = -1;
4568                 scope.setValidity('invalidHourpickerData', true);
4569                 scope.setValidity('invalidHourpickerTimeRange', true);
4570             };
4571             scope.resetHourpickerPanelValue();
4572             scope.updateHourpickerValue = function () {
4573                 if (scope.isFormValid() && !scope.isTimeOverlap()) {
4574                     scope.addHourpickerValue(angular.copy(scope.hourpickerPanelValue));
4575                     scope.resetHourpickerPanelValue();
4576                 }
4577             };
4578
4579             scope.isFormValid = function () {
4580                 var isStartTimeAvailable = scope.hourpickerPanelValue.startTime ? true : false;
4581                 var isStartMeridiemAvailable = scope.hourpickerPanelValue.startMeridiem ? true : false;
4582                 var isEndTimeAvailable = scope.hourpickerPanelValue.endTime ? true : false;
4583                 var isEndMeridiemAvailable = scope.hourpickerPanelValue.endMeridiem ? true : false;
4584                 var currentStartTime = getTime(scope.hourpickerPanelValue.startTime, scope.hourpickerPanelValue.startMeridiem);
4585                 var currentEndTime = getTime(scope.hourpickerPanelValue.endTime, scope.hourpickerPanelValue.endMeridiem);
4586                 var isTimeInProperSequence = currentEndTime > currentStartTime;
4587                 var isDayChecked = false;
4588                 for (var i in scope.hourpickerPanelValue.days) {
4589                     if (scope.hourpickerPanelValue.days[i].value) {
4590                         isDayChecked = true;
4591                         break;
4592                     }
4593                 }
4594
4595                 if (isStartTimeAvailable && isStartMeridiemAvailable && isEndTimeAvailable && isEndMeridiemAvailable && isTimeInProperSequence && isDayChecked) {
4596                     scope.setValidity('invalidHourpickerData', true);
4597                     return true;
4598                 } else {
4599                     scope.setValidity('invalidHourpickerData', false);
4600                     return false;
4601                 }
4602             };
4603             scope.isTimeOverlap = function () {
4604                 var selectedDays = [];
4605                 for (var i in scope.hourpickerPanelValue.days) {
4606                     if (scope.hourpickerPanelValue.days[i].value) {
4607                         selectedDays.push(i);
4608                     }
4609                 }
4610
4611                 var currentStartTime, currentEndTime, existingStartTime, existingEndTime;
4612                 currentStartTime = getTime(scope.hourpickerPanelValue.startTime, scope.hourpickerPanelValue.startMeridiem);
4613                 currentEndTime = getTime(scope.hourpickerPanelValue.endTime, scope.hourpickerPanelValue.endMeridiem);
4614                 for (var i = 0; i < scope.hourpickerValues.length; i++) {
4615                     
4616                     if (i === scope.hourpicker.editMode) {
4617                         continue;
4618                     }
4619
4620                     for (var j = 0; j < selectedDays.length; j++) {
4621                         existingStartTime = getTime(scope.hourpickerValues[i].startTime, scope.hourpickerValues[i].startMeridiem);
4622                         existingEndTime = getTime(scope.hourpickerValues[i].endTime, scope.hourpickerValues[i].endMeridiem);
4623                         if (scope.hourpickerValues[i].days[selectedDays[j]].value) {
4624                             if(!scope.hourpicker.sameDayOption){
4625                                 scope.setValidity('dayAlreadySelected', false);
4626                                 return true;
4627                             } else if ((currentStartTime > existingStartTime && currentStartTime < existingEndTime) || (currentEndTime > existingStartTime && currentEndTime < existingEndTime)) {
4628                                 scope.setValidity('invalidHourpickerTimeRange', false);
4629                                 return true;
4630                             } else if ((existingStartTime > currentStartTime && existingStartTime < currentEndTime) || (existingEndTime > currentStartTime && existingEndTime < currentEndTime)) {
4631                                 scope.setValidity('invalidHourpickerTimeRange', false);
4632                                 return true;
4633                             } else if ((currentStartTime === existingStartTime) && (currentEndTime === existingEndTime)) {
4634                                 scope.setValidity('invalidHourpickerTimeRange', false);
4635                                 return true;
4636                             }
4637                         }
4638                     }
4639                 }
4640
4641                 scope.setValidity('dayAlreadySelected', true);
4642                 scope.setValidity('invalidHourpickerTimeRange', true);
4643                 return false;
4644             };
4645             var getTime = function (timeString, meridiem) {
4646                 var tempDate = new Date();
4647                 if (timeString && meridiem) {
4648                     var timeSplit = timeString.split(':');
4649                     var hour = ((meridiem === 'PM' || meridiem === 'pm') && timeSplit[0] !== '12') ? parseInt(timeSplit[0], 10) + 12 : parseInt(timeSplit[0], 10);
4650                     tempDate.setHours(hour, parseInt(timeSplit[1], 10), 0, 0);
4651                 }
4652
4653                 return tempDate.getTime();
4654             };
4655         }
4656     }
4657 }])
4658
4659 .directive('b2bHourpickerValue', [function () {
4660     return {
4661         restrict: 'EA',
4662         replace: false,
4663         templateUrl: 'b2bTemplate/hourPicker/b2bHourpickerValue.html',
4664         controller: ['$scope', function (scope) {
4665
4666         }],
4667         link: function (scope, elem, attr, ctrl) {
4668             scope.hourpickerValue = {};
4669             scope.hourpickerValue.startTime = attr.startTime ? scope.$eval(attr.startTime) : '';
4670             scope.hourpickerValue.startMeridiem = attr.startMeridiem ? scope.$eval(attr.startMeridiem) : '';
4671             scope.hourpickerValue.endTime = attr.endTime ? scope.$eval(attr.endTime) : '';
4672             scope.hourpickerValue.endMeridiem = attr.endMeridiem ? scope.$eval(attr.endMeridiem) : '';
4673             scope.hourpickerValue.days = attr.days ? scope.$eval(attr.days).join(', ') : '';
4674             scope.hourpickerValue.index = attr.b2bHourpickerValue ? scope.$eval(attr.b2bHourpickerValue) : -1;
4675         }
4676     }
4677 }]);
4678 /**
4679  * @ngdoc directive
4680  * @name Template.att:inputTemplate
4681  *
4682  * @description
4683  *  <file src="src/inputTemplate/docs/readme.md" />
4684  *
4685  * @usage
4686  *  <input type="text" id="fieldId" placeholder="placholder text here" class="span12 input-enhanced" name="fieldName">
4687  *
4688  * @example
4689  <section id="code">
4690     <b>HTML + AngularJS</b>
4691     <example module="b2b.att">
4692         <file src="src/inputTemplate/docs/demo.html" />
4693         <file src="src/inputTemplate/docs/demo.js" />
4694     </example>
4695  </section>
4696  */
4697 angular.module('b2b.att.inputTemplate', []);
4698
4699 /**
4700  * @ngdoc directive
4701  * @name Navigation.att:leftNavigation
4702  *
4703  * @description
4704  *  <file src="src/leftNavigation/docs/readme.md" />
4705  *
4706  * @usage
4707  *   <b2b-left-navigation data-menu="menuData"></b2b-left-navigation> 
4708  *
4709  * @example
4710  *  <section id="code">
4711         <example module="b2b.att">
4712             <file src="src/leftNavigation/docs/demo.html" />
4713             <file src="src/leftNavigation/docs/demo.js" />
4714        </example>
4715     </section>
4716  *
4717  */
4718 angular.module('b2b.att.leftNavigation', [])
4719     .directive('b2bLeftNavigation', [function () {
4720         return {
4721             restrict: 'EA',
4722             templateUrl: 'b2bTemplate/leftNavigation/leftNavigation.html',
4723             scope: {
4724                 menuData: '='
4725             },
4726             link: function (scope, element, attrs, ctrl) {
4727                 scope.idx = -1;
4728                 scope.itemIdx = -1;
4729                 scope.navIdx = -1;
4730                 scope.toggleNav = function (val) {
4731                     if (val === scope.idx) {
4732                         scope.idx = -1;
4733                         return;
4734                     }
4735                     scope.idx = val;
4736                 };
4737                 scope.liveLink = function (evt, val1, val2) {
4738                     scope.itemIdx = val1;
4739                     scope.navIdx = val2;
4740                     evt.stopPropagation();
4741                 };
4742             }
4743         };
4744     }]);
4745 /**
4746  * @ngdoc directive
4747  * @name Buttons, links & UI controls.att:links
4748  *
4749  * @description
4750  *  <file src="src/links/docs/readme.md" />
4751  * @usage
4752  *      <!-- See below examples for link implementation -->
4753  *      
4754  * @example
4755        <section id="code">              
4756            <b>HTML + AngularJS</b>
4757            <example module="b2b.att">
4758            <file src="src/links/docs/demo.html" />
4759             <file src="src/links/docs/demo.js" />            
4760           </example>          
4761         </section>
4762  */
4763 angular.module('b2b.att.links', []);
4764 /**
4765  * @ngdoc directive
4766  * @name Misc.att:listbox
4767  *
4768  * @description
4769  *  <file src="src/listbox/docs/readme.md" />
4770  *
4771  * @param {int} currentIndex - Current index of selected listbox item. Is not supported on multiselect listbox
4772  * @param {Array} listboxData - Data of listbox items. Should include full data regardless if HTML will be filtered.
4773
4774  * @example
4775  *  <section id="code">   
4776      <example module="b2b.att">
4777      <file src="src/listbox/docs/demo.html" />
4778      <file src="src/listbox/docs/demo.js" />
4779      </example>
4780     </section>
4781  *
4782  */
4783 angular.module('b2b.att.listbox', ['b2b.att.utilities'])
4784 .directive('b2bListBox', ['keymap', 'b2bDOMHelper', '$rootScope', function(keymap, b2bDOMHelper, $rootScope) {
4785                 return {
4786                     restrict: 'AE',
4787                     transclude: true,
4788                     replace: true,
4789                     scope: {
4790                         currentIndex: '=', 
4791                         listboxData: '='
4792                     },
4793                     templateUrl: 'b2bTemplate/listbox/listbox.html',
4794                     link: function(scope, elem, attr) {
4795
4796                         if (attr.ariaMultiselectable !== undefined || attr.ariaMultiselectable === 'true') {
4797                             scope.multiselectable = true;
4798                         } else {
4799                             scope.multiselectable = false;
4800                         }
4801
4802                         var shiftKey = false;
4803                         var elements = [];
4804                         var prevDirection = undefined; // previous direction is used for an edge case when shifting
4805                         var shiftKeyPressed = false; // Used to handle shift clicking
4806                         var ctrlKeyPressed = false;
4807
4808                         var currentIndexSet = {
4809                             'elementIndex': 0,
4810                             'listboxDataIndex': 0
4811                         };
4812
4813                         function isTrue(item) {
4814                             if (item.selected === true) {
4815                                 return true;
4816                             }
4817                         }
4818
4819                         function incrementIndex(elem) {
4820                             $rootScope.$apply();
4821
4822                             var nextElem = elem.next();
4823                             if (!angular.isDefined(nextElem) || nextElem.length === 0) {
4824                                 return;
4825                             }
4826
4827                             currentIndexSet.elementIndex += 1;
4828                             currentIndexSet.listboxDataIndex = parseInt(nextElem.attr('data-index'), 10);
4829                             scope.currentIndex = currentIndexSet.listboxDataIndex;
4830
4831                             if (currentIndexSet.elementIndex >= elements.length - 1) {
4832                                 currentIndexSet.elementIndex = elements.length-1;
4833                             }
4834                         }
4835
4836                         function decrementIndex(elem) {
4837                             $rootScope.$apply();
4838                             var prevElem = angular.element(b2bDOMHelper.previousElement(elem));
4839                             if (!angular.isDefined(prevElem) || prevElem.length === 0) {
4840                                 return;
4841                             }
4842
4843                             currentIndexSet.elementIndex -= 1;
4844                             currentIndexSet.listboxDataIndex = parseInt(prevElem.attr('data-index'), 10);
4845                             scope.currentIndex = currentIndexSet.listboxDataIndex;
4846
4847                             if (currentIndexSet.elementIndex <= 0) {
4848                                 currentIndexSet.elementIndex = 0;
4849                             }
4850                         }
4851
4852                         var focusOnElement = function(index) {
4853                             try {
4854                                 elements[index].focus();
4855                             } catch (e) {};
4856                         }
4857
4858                         function selectItems(startIndex, endIndex, forceValue) {
4859                             for (var i = startIndex; i < endIndex; i++) {
4860                                 if (forceValue === undefined) {
4861                                     // We will flip the value
4862                                     scope.listboxData[i].selected = !scope.listboxData[i].selected;
4863                                 } else {
4864                                     scope.listboxData[i].selected = forceValue;
4865                                 }
4866                             }
4867
4868                             if (!scope.$$phase) {
4869                                 scope.$apply();
4870                             }
4871                         }
4872
4873                         elem.bind('focus', function(evt) { 
4874                             // If multiselectable or not and nothing is selected, put focus on first element 
4875                             // If multiselectable and a range is set, put focus on first element of range 
4876                             // If not multiselectable and something selected, put focus on element 
4877                             elements = elem.children(); 
4878                              var selectedItems = scope.listboxData.filter(isTrue); 
4879                              var elementsIndies = Array.prototype.map.call(elements, function(item) {
4880                                 return parseInt(angular.element(item).attr('data-index'), 10);
4881                             });
4882  
4883                             if (selectedItems.length == 0) { 
4884                                 focusOnElement(0); 
4885                                 currentIndexSet.listboxDataIndex = 0;
4886                             } else if (attr.ariaMultiselectable) { 
4887                                 var index = scope.listboxData.indexOf(selectedItems[0]); 
4888                                 var indies = elementsIndies.filter(function(item) {
4889                                     return (item === index);
4890                                 });
4891
4892                                 if (indies.length === 0 || indies[0] != index) {
4893                                     // Set focused on 0
4894                                     currentIndexSet.elementIndex = elementsIndies[0]; 
4895                                     currentIndexSet.listboxDataIndex = 0;
4896                                     focusOnElement(currentIndexSet.elementIndex);
4897                                 } else {
4898                                     focusOnElement(indies[0]); 
4899                                     currentIndexSet.elementIndex = indies[0];
4900                                     currentIndexSet.listboxDataIndex = index;
4901                                 }
4902                             } else { 
4903                                 focusOnElement(currentIndexSet.elementIndex);  
4904                             }
4905                             scope.currentIndex = currentIndexSet.listboxDataIndex;
4906
4907                             if (!scope.$$phase) {
4908                                 scope.$apply();
4909                             }
4910                         });
4911                         elem.bind('keyup', function(evt) {
4912                             if (evt.keyCode === keymap.KEY.SHIFT) {
4913                                 shiftKeyPressed = false;
4914                             } else if (evt.keyCode === keymap.KEY.CTRL) {
4915                                 ctrlKeyPressed = false;
4916                             }
4917                         });
4918         
4919                         elem.bind('keydown', function(evt) {
4920                             var keyCode = evt.keyCode;
4921                             elements = elem.children();
4922                             if (keyCode === keymap.KEY.SHIFT) {
4923                                 shiftKeyPressed = true;
4924                             } else if (evt.keyCode === keymap.KEY.CTRL) {
4925                                 ctrlKeyPressed = true;
4926                             }
4927
4928                             switch(keyCode) {
4929                                 case 65: // A key
4930                                 {
4931                                     if (scope.multiselectable && evt.ctrlKey) {
4932                                         var arr = scope.listboxData.filter(isTrue);
4933                                         var elementsIndies = Array.prototype.map.call(elements, function(item) {
4934                                             return parseInt(angular.element(item).attr('data-index'), 10);
4935                                         });
4936                                         var val = !(arr.length === scope.listboxData.length);
4937                                         for (var i = 0; i < elementsIndies.length; i++) {
4938                                             scope.listboxData[elementsIndies[i]].selected = val;
4939                                         }
4940
4941                                         if (!scope.$$phase) {
4942                                             scope.$apply();
4943                                         }
4944                                         
4945                                         evt.preventDefault();
4946                                         evt.stopPropagation();
4947                                     }
4948                                     break;
4949                                 }
4950                                 case keymap.KEY.END:
4951                                 {
4952                                     if (scope.multiselectable && evt.ctrlKey && evt.shiftKey) {
4953                                         var elementsIndies = Array.prototype.map.call(elements, function(item) {
4954                                             return parseInt(angular.element(item).attr('data-index'), 10);
4955                                         }).filter(function(item) {
4956                                             return (item >= currentIndexSet.listboxDataIndex);
4957                                         });
4958                                         for (var i = 0; i < elementsIndies.length; i++) {
4959                                             scope.listboxData[elementsIndies[i]].selected = true;
4960                                         }
4961                                         evt.preventDefault();
4962                                         evt.stopPropagation();
4963
4964                                         if (!scope.$$phase) {
4965                                             scope.$apply();
4966                                         }
4967                                     }
4968                                     break;
4969                                 }
4970                                 case keymap.KEY.HOME: 
4971                                 {
4972                                     if (scope.multiselectable && evt.ctrlKey && evt.shiftKey) {
4973                                         selectItems(0, currentIndexSet.listboxDataIndex+1, true); // currentIndex+1 is what is being focused on
4974                                         evt.preventDefault();
4975                                         evt.stopPropagation();
4976                                     }
4977                                     break;
4978                                 }
4979                                 case keymap.KEY.LEFT:
4980                                 case keymap.KEY.UP:
4981                                 {
4982                                     if (currentIndexSet.listboxDataIndex === 0) {
4983                                         evt.preventDefault();
4984                                         evt.stopPropagation();
4985                                         return;
4986                                     }
4987
4988                                     decrementIndex(elements.eq(currentIndexSet.elementIndex));
4989                                     if (scope.multiselectable && (evt.shiftKey || evt.ctrlKey)) {
4990                                         if (evt.shiftKey) {
4991                                             if (prevDirection === 'DOWN') {
4992                                                 scope.listboxData[currentIndexSet.listboxDataIndex+1].selected = !scope.listboxData[currentIndexSet.listboxDataIndex+1].selected;
4993                                             }
4994                                             scope.listboxData[currentIndexSet.listboxDataIndex].selected = !scope.listboxData[currentIndexSet.listboxDataIndex].selected;
4995                                         }
4996                                         prevDirection = 'UP';
4997                                     } else {
4998                                         // If no modifier keys are selected, all other items need to be unselected.
4999                                         prevDirection = undefined;
5000                                         selectItems(0, scope.listboxData.length, false);
5001                                         if(currentIndexSet.listboxDataIndex !== undefined && !isNaN(currentIndexSet.listboxDataIndex)){
5002                                             scope.listboxData[currentIndexSet.listboxDataIndex].selected = true;
5003                                         }
5004                                     }
5005                                     focusOnElement(currentIndexSet.elementIndex);
5006                                     if(!scope.$$phase) {
5007                                         scope.$apply();
5008                                     }
5009                                     evt.preventDefault();
5010                                     evt.stopPropagation();
5011                                     break;
5012                                 }
5013                                 case keymap.KEY.RIGHT:
5014                                 case keymap.KEY.DOWN:
5015                                 {
5016                                     if (currentIndexSet.listboxDataIndex === scope.listboxData.length-1) {
5017                                         evt.preventDefault();
5018                                         evt.stopPropagation();
5019                                         return;
5020                                     }
5021
5022                                     incrementIndex(elements.eq(currentIndexSet.elementIndex));
5023                                     
5024                                     if (scope.multiselectable && (evt.shiftKey || evt.ctrlKey)) {
5025                                         if (evt.shiftKey) {
5026                                             if (prevDirection === 'UP') {
5027                                                 scope.listboxData[currentIndexSet.listboxDataIndex-1].selected = !scope.listboxData[currentIndexSet.listboxDataIndex-1].selected;
5028                                             }
5029                                             
5030                                             scope.listboxData[currentIndexSet.listboxDataIndex].selected = !scope.listboxData[currentIndexSet.listboxDataIndex].selected;    
5031                                         }
5032                                         prevDirection = 'DOWN';
5033                                     } else {
5034                                         // If no modifier keys are selected, all other items need to be unselected.
5035                                         prevDirection = undefined;
5036                                         selectItems(0, scope.listboxData.length, false);
5037                                         if(currentIndexSet.listboxDataIndex !== undefined && !isNaN(currentIndexSet.listboxDataIndex)){
5038                                             scope.listboxData[currentIndexSet.listboxDataIndex].selected = true;
5039                                         }
5040                                     }
5041
5042                                     focusOnElement(currentIndexSet.elementIndex);
5043                                     if(!scope.$$phase) {
5044                                         scope.$apply();
5045                                     }
5046                                     evt.preventDefault();
5047                                     evt.stopPropagation();
5048                                     break;
5049                                 }
5050                                 case keymap.KEY.TAB:
5051                                     if(evt.shiftKey) {
5052                                         var previousElement = b2bDOMHelper.previousElement(elem.parent().parent(), true);
5053                                         evt.preventDefault();
5054                                         previousElement.focus();
5055                                     }
5056                                     break;
5057                                 default:
5058                                     break;
5059                             }
5060                         });
5061
5062                         elem.bind('click', function(evt) {
5063                             var index = parseInt(evt.target.dataset.index, 10);
5064                             if (index === undefined || isNaN(index)) {
5065                                 return;
5066                             }
5067                             if (scope.multiselectable && currentIndexSet.listboxDataIndex !== undefined) {
5068                                 if (shiftKeyPressed) {
5069                                     var min = Math.min(index, currentIndexSet.listboxDataIndex);
5070                                     var max = Math.max(index, currentIndexSet.listboxDataIndex);
5071
5072                                     if (index === min) { // clicking up
5073                                         var firstIndex = scope.listboxData.findIndex(function(item) { return item.selected === true;});
5074                                         // Given the firstIndex, let's find the matching element to get proper element match
5075                                         elements = elem.children();
5076                                         elements.eq(firstIndex)
5077                                         var elementsThatMatch = Array.prototype.filter.call(elements, function(item) {
5078                                             if (parseInt(angular.element(item).attr('data-index'), 10) === firstIndex) {
5079                                                 return true;
5080                                             }
5081                                         });
5082                                         firstIndex = parseInt(angular.element(elementsThatMatch).attr('data-index'), 10);
5083                                         
5084                                         if (index <= firstIndex && scope.listboxData.filter(isTrue).length > 1) {
5085                                             // Break the selection into 2
5086                                             selectItems(firstIndex + 1, max + 1, undefined); // + 1 needed because selectItems only selects up to MAX
5087                                             selectItems(min, firstIndex, undefined); 
5088                                         } else if (scope.listboxData.filter(isTrue).length == 1){
5089                                             selectItems(min, max, undefined); 
5090                                         } else {
5091                                             selectItems(min + 1, max + 1, undefined);
5092                                         }
5093                                     } else { // clicking down
5094                                         selectItems(min + 1, max + 1, scope.listboxData[min].selected);
5095                                     }
5096                                 } else if (ctrlKeyPressed) {
5097                                     scope.listboxData[index].selected = !scope.listboxData[index].selected;
5098                                 } else {
5099                                     selectItems(0, scope.listboxData.length, false);
5100                                     scope.listboxData[index].selected = !scope.listboxData[index].selected;
5101                                 }
5102                             } else {
5103                                 selectItems(0, scope.listboxData.length, false);
5104                                 scope.listboxData[index].selected = !scope.listboxData[index].selected;
5105                             }
5106                             currentIndexSet.elementIndex = index;
5107                             currentIndexSet.listboxDataIndex = index;
5108                             scope.currentIndex = currentIndexSet.listboxDataIndex;
5109                             if (!scope.$$phase) {
5110                                 scope.$apply();
5111                             }
5112                             focusOnElement(index);
5113                         });
5114                     }
5115                 };
5116             }]);
5117 /**
5118  * @ngdoc directive
5119  * @name Videos, audio & animation.att:loaderAnimation
5120  *
5121  * @description
5122  *  <file src="src/loaderAnimation/docs/readme.md" />
5123  *
5124  * @usage
5125  *   <!-- Below demo js shows-->
5126  *   Angular library uses Global.css's icon-primary-spinner.
5127  *
5128  * @example
5129  *  <section id="code">
5130         <example module="b2b.att">
5131             <file src="src/loaderAnimation/docs/demo.html" />
5132             <file src="src/loaderAnimation/docs/demo.js" />
5133        </example>
5134     </section>
5135  *
5136  */
5137 angular.module('b2b.att.loaderAnimation', [])
5138     .constant('b2bSpinnerConfig', {
5139         loadingText: 'Loading...',
5140         startEvent: 'startButtonSpinner',
5141         stopEvent: 'stopButtonSpinner'
5142     })
5143     .constant("progressTrackerConfig", {
5144         loadingText: 'Loading...',
5145         minDuration: "",
5146         activationDelay: "",
5147         minDurationPromise: "",
5148         activationDelayPromise: ""
5149     })
5150
5151 .provider('progressTracker', function () {
5152     this.$get = ['$q', '$timeout', function ($q, $timeout) {
5153         function cancelTimeout(promise) {
5154             if (promise) {
5155                 $timeout.cancel(promise);
5156             }
5157         }
5158         return function ProgressTracker(options) {
5159             //do new if user doesn't
5160             if (!(this instanceof ProgressTracker)) {
5161                 return new ProgressTracker(options);
5162             }
5163
5164             options = options || {};
5165             //Array of promises being tracked
5166             var tracked = [];
5167             var self = this;
5168             //Allow an optional "minimum duration" that the tracker has to stay active for.
5169             var minDuration = options.minDuration;
5170             //Allow a delay that will stop the tracker from activating until that time is reached
5171             var activationDelay = options.activationDelay;
5172             var minDurationPromise;
5173             var activationDelayPromise;
5174             self.active = function () {
5175                 //Even if we have a promise in our tracker, we aren't active until delay is elapsed
5176                 if (activationDelayPromise) {
5177                     return false;
5178                 }
5179                 return tracked.length > 0;
5180             };
5181             self.tracking = function () {
5182                 //Even if we aren't active, we could still have a promise in our tracker
5183                 return tracked.length > 0;
5184             };
5185             self.destroy = self.cancel = function () {
5186                 minDurationPromise = cancelTimeout(minDurationPromise);
5187                 activationDelayPromise = cancelTimeout(activationDelayPromise);
5188                 for (var i = tracked.length - 1; i >= 0; i--) {
5189                     tracked[i].resolve();
5190                 }
5191                 tracked.length = 0;
5192             };
5193             //Create a promise that will make our tracker active until it is resolved.
5194             // @return deferred - our deferred object that is being tracked
5195             self.createPromise = function () {
5196                 var deferred = $q.defer();
5197                 tracked.push(deferred);
5198                 //If the tracker was just inactive and this the first in the list of promises, we reset our delay and minDuration again.
5199                 if (tracked.length === 1) {
5200                     if (activationDelay) {
5201                         activationDelayPromise = $timeout(function () {
5202                             activationDelayPromise = cancelTimeout(activationDelayPromise);
5203                             startMinDuration();
5204                         }, activationDelay);
5205                     } else {
5206                         startMinDuration();
5207                     }
5208                 }
5209                 deferred.promise.then(onDone(false), onDone(true));
5210                 return deferred;
5211
5212                 function startMinDuration() {
5213                     if (minDuration) {
5214                         minDurationPromise = $timeout(angular.noop, minDuration);
5215                     }
5216                 }
5217                 //Create a callback for when this promise is done. It will remove our tracked promise from the array if once minDuration is complete
5218                 function onDone() {
5219                     return function () {
5220                         (minDurationPromise || $q.when()).then(function () {
5221                             var index = tracked.indexOf(deferred);
5222                             tracked.splice(index, 1);
5223                             //If this is the last promise, cleanup the timeouts for activationDelay
5224                             if (tracked.length === 0) {
5225                                 activationDelayPromise = cancelTimeout(activationDelayPromise);
5226                             }
5227                         });
5228                     };
5229                 }
5230             };
5231             self.addPromise = function (promise) {
5232                 
5233 //                we cannot assign then function in other var and then add the resolve and reject 
5234                 var thenFxn = promise && (promise.then || promise.$then || (promise.$promise && promise.$promise.then));                
5235                 if (!thenFxn) {
5236                     throw new Error("progressTracker expects a promise object :: Not found");
5237                 }
5238                 var deferred = self.createPromise();
5239                 //When given promise is done, resolve our created promise
5240                 //Allow $then for angular-resource objects
5241
5242                 promise.then(function (value) {
5243                         deferred.resolve(value);
5244                         return value;
5245                     }, function (value) {
5246                         deferred.reject(value);
5247                         return $q.reject(value);
5248                     }
5249                 );
5250                 return deferred;
5251             };
5252         };
5253     }];
5254 })
5255
5256 .config(['$httpProvider', function ($httpProvider) {
5257     $httpProvider.interceptors.push(['$q', 'progressTracker', function ($q) {
5258         return {
5259             request: function (config) {
5260                 if (config.tracker) {
5261                     if (!angular.isArray(config.tracker)) {
5262                         config.tracker = [config.tracker];
5263                     }
5264                     config.$promiseTrackerDeferred = config.$promiseTrackerDeferred || [];
5265
5266                     angular.forEach(config.tracker, function (tracker) {
5267                         var deferred = tracker.createPromise();
5268                         config.$promiseTrackerDeferred.push(deferred);
5269                     });
5270                 }
5271                 return $q.when(config);
5272             },
5273             response: function (response) {
5274                 if (response.config && response.config.$promiseTrackerDeferred) {
5275                     angular.forEach(response.config.$promiseTrackerDeferred, function (deferred) {
5276                         deferred.resolve(response);
5277                     });
5278                 }
5279                 return $q.when(response);
5280             },
5281             responseError: function (response) {
5282                 if (response.config && response.config.$promiseTrackerDeferred) {
5283                     angular.forEach(response.config.$promiseTrackerDeferred, function (deferred) {
5284                         deferred.reject(response);
5285                     });
5286                 }
5287                 return $q.reject(response);
5288             }
5289         };
5290     }]);
5291 }])
5292
5293 .directive('b2bClickSpin', ['$timeout', '$parse', '$rootScope', 'progressTracker', function ($timeout, $parse, $rootScope, progressTracker) {
5294     return {
5295         restrict: 'A',
5296         link: function (scope, elm, attrs) {
5297             var fn = $parse(attrs.b2bClickSpin);
5298             elm.on('click', function (event) {
5299                 var promise = $timeout(function () {console.log("inside Promise")}, 5000);
5300                 scope.$apply(function () {
5301                     fn(scope, {
5302                         $event: event
5303                     });
5304                 });
5305                 //comment this line if not running unit test
5306                 $rootScope.loadingTracker = progressTracker({
5307                     minDuration: 750
5308                 });
5309                 $rootScope.loadingTracker.addPromise(promise);
5310                 angular.forEach("$routeChangeSuccess $viewContentLoaded $locationChangeSuccess".split(" "), function (event) {
5311                     $rootScope.$on(event, function () {
5312
5313                         $timeout.cancel(promise);
5314                     });
5315                 });
5316             });
5317         }
5318     };
5319 }])
5320
5321 .directive('b2bProgressTracker', ['progressTrackerConfig', function (ptc) {
5322     return {
5323         restrict: 'EA',
5324         replace: true,
5325         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>'
5326     };
5327 }])
5328
5329 .directive('b2bLoadButton', ['b2bSpinnerConfig', '$timeout', function (spinnerConfig, $timeout) {
5330     var spinButton = function (state, element, data) {
5331         
5332         var attr = element.html() ? 'html' : 'val';
5333         state = state + 'Text';
5334         if (state === 'loadingText') {
5335             element[attr](data[state]);
5336             element.attr("disabled",'disabled');
5337             element.addClass('disabled');
5338         } else if (state === 'resetText') {
5339             element[attr](data[state]);
5340             element.removeAttr("disabled");
5341             element.removeClass('disabled');
5342         }
5343     };
5344
5345     return {
5346         restrict: 'A',
5347         replace: false,
5348         scope: {
5349             promise: '=promise',
5350             startEvent: '@startEvent',
5351             stopEvent: '@stopEvent'
5352         },
5353         link: function (scope, element, attr) {
5354             var validAttr = element.html() ? 'html' : 'val';
5355             var data = {
5356                 loadingText: '',
5357                 resetText: ''
5358             };
5359
5360             var updateLoadingText = function (val) {
5361                 var loadingText = val;
5362                 if (!angular.isDefined(loadingText) || loadingText === "") {
5363                     loadingText = spinnerConfig.loadingText;
5364                 }
5365                 data.loadingText = validAttr === 'html' ? "<i class=\"icon-primary-spinner small\"></i>" + loadingText : loadingText;
5366             };
5367             var updateResetText = function (val) {
5368                 data.resetText = val;
5369             };
5370
5371             attr.$observe('b2bLoadButton', function (val) {
5372                 updateLoadingText(val);
5373             });
5374             $timeout(function () {
5375                 updateResetText(element[validAttr]());
5376             }, 500);
5377
5378             if (!angular.isDefined(scope.startEvent) || scope.startEvent === "") {
5379                 scope.startEvent = spinnerConfig.startEvent;
5380             }
5381
5382             if (!angular.isDefined(scope.stopEvent) || scope.stopEvent === "") {
5383                 scope.stopEvent = spinnerConfig.stopEvent;
5384             }
5385
5386             scope.$watch('promise', function () {
5387                 if (angular.isDefined(scope.promise) && angular.isFunction(scope.promise.then)) {
5388                     spinButton('loading', element, data);
5389                     scope.promise.then(function () {
5390                         spinButton('reset', element, data);
5391                     }, function () {
5392                         spinButton('reset', element, data);
5393                     });
5394                 }
5395             });
5396
5397             scope.$on(scope.startEvent, function () {
5398                 spinButton('loading', element, data);
5399                 scope.$on(scope.stopEvent, function () {
5400                     spinButton('reset', element, data);
5401                 });
5402             });
5403         }
5404     };
5405 }]);
5406  /**
5407  * @ngdoc directive
5408  * @name Misc.att:messageWrapper
5409  * @scope
5410  * @param {boolean} trigger - A boolean that triggers directive to switch focus
5411  * @param {integer} delay  - Extra delay added to trigger code to allow for DOM to be ready. Default is 10ms.
5412  * @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)
5413  * @param {string} trapFocus - Attribute-based API to trap focus within the message. This should be enabled by default on all toast messages.
5414  * @description
5415  *  <file src="src/messageWrapper/docs/readme.md" />
5416  * @usage
5417  * <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>
5418  *
5419  * @example
5420  *  <section id="code">   
5421  <b>HTML + AngularJS</b>
5422  <example module="b2b.att">
5423  <file src="src/messageWrapper/docs/demo.html" />
5424  <file src="src/messageWrapper/docs/demo.js" />
5425  </example>
5426  </section>
5427  *
5428  */
5429 angular.module('b2b.att.messageWrapper', ['b2b.att.utilities'])
5430 .directive('b2bMessageWrapper', ['b2bDOMHelper', '$compile', '$timeout', '$log', function(b2bDOMHelper, $compile, $timeout, $log) {
5431   return {
5432     restrict: 'AE',
5433     scope: {
5434       trigger: '=',
5435       delay: '=?'
5436     },
5437     transclude: true,
5438     replace: true,
5439     template: '<div ng-transclude></div>',
5440     link: function(scope, elem, attrs) {
5441       scope.delay = scope.delay || 10;
5442
5443       if (attrs.trapFocus != undefined && !elem.children().eq(0).attr('b2b-trap-focus-inside-element')) {
5444         // Append b2bTrapFocusInsideElement onto first child and recompile
5445         elem.children().eq(0).attr('b2b-trap-focus-inside-element', 'false');
5446         elem.children().eq(0).attr('trigger', scope.trigger);
5447         $compile(elem.contents())(scope);
5448       }
5449
5450       var firstElement = undefined,
5451           launchingElement = undefined;
5452       
5453       scope.$watch('trigger', function(oldVal, newVal) {
5454         if (oldVal === newVal) return;
5455         if (!angular.isDefined(launchingElement)) {
5456           launchingElement = document.activeElement;
5457         }
5458         $timeout(function() {
5459           if (scope.trigger) {
5460
5461             if (attrs.noFocus === true || attrs.noFocus === "") {
5462               elem.children()[0].focus();
5463             } else {
5464               firstElement = b2bDOMHelper.firstTabableElement(elem);
5465
5466               if (angular.isDefined(firstElement)) {
5467                 firstElement.focus();
5468               }
5469             }
5470             
5471           } else {
5472             if (angular.isDefined(launchingElement) && launchingElement.nodeName !== 'BODY') {
5473               if (launchingElement === document.activeElement) {
5474                 return;
5475               }
5476
5477               if (b2bDOMHelper.isInDOM(launchingElement) && b2bDOMHelper.isTabable(launchingElement)) {
5478                   // At this point, launchingElement is still a valid element, but focus will fail and 
5479                   // activeElement will become body, hence we want to apply custom logic and find previousElement
5480                   var prevLaunchingElement = launchingElement;
5481                   launchingElement.focus();
5482
5483                   if (document.activeElement !== launchingElement || document.activeElement.nodeName === 'BODY') {
5484                     launchingElement = b2bDOMHelper.previousElement(angular.element(prevLaunchingElement), true);
5485                     launchingElement.focus();
5486                   }
5487               } else {
5488                 launchingElement = b2bDOMHelper.previousElement(launchingElement, true);
5489                 launchingElement.focus();
5490               }
5491             }
5492           }
5493         }, scope.delay); 
5494       });
5495     }
5496   };
5497 }]);
5498 /**
5499  * @ngdoc directive
5500  * @name Messages, modals & alerts.att:modalsAndAlerts
5501  *
5502  * @description
5503  *  <file src="src/modalsAndAlerts/docs/readme.md" />
5504  *
5505  * @usage
5506  *  <button class="btn" b2b-modal="b2bTemplate/modalsAndAlerts/demo_modal.html" modal-ok="ok()" modal-cancel="cancel()">Launch demo modal</button>
5507  *
5508  * @example
5509  *  <section id="code">
5510      <example module="b2b.att">
5511       <file src="src/modalsAndAlerts/docs/demo.html" />
5512       <file src="src/modalsAndAlerts/docs/demo.js" />
5513      </example>
5514     </section>
5515  *
5516  */
5517 angular.module('b2b.att.modalsAndAlerts', ['b2b.att.position', 'b2b.att.transition', 'b2b.att.utilities'])
5518
5519 /**
5520  * A helper, internal data structure that acts as a map but also allows getting / removing
5521  * elements in the LIFO order
5522  */
5523 .factory('$$stackedMap', function () {
5524     return {
5525         createNew: function () {
5526             var stack = [];
5527
5528             return {
5529                 add: function (key, value) {
5530                     stack.push({
5531                         key: key,
5532                         value: value
5533                     });
5534                 },
5535                 get: function (key) {
5536                     for (var i = 0; i < stack.length; i++) {
5537                         if (key === stack[i].key) {
5538                             return stack[i];
5539                         }
5540                     }
5541                 },
5542                 keys: function () {
5543                     var keys = [];
5544                     for (var i = 0; i < stack.length; i++) {
5545                         keys.push(stack[i].key);
5546                     }
5547                     return keys;
5548                 },
5549                 top: function () {
5550                     return stack[stack.length - 1];
5551                 },
5552                 remove: function (key) {
5553                     var idx = -1;
5554                     for (var i = 0; i < stack.length; i++) {
5555                         if (key === stack[i].key) {
5556                             idx = i;
5557                             break;
5558                         }
5559                     }
5560                     return stack.splice(idx, 1)[0];
5561                 },
5562                 removeTop: function () {
5563                     return stack.splice(stack.length - 1, 1)[0];
5564                 },
5565                 length: function () {
5566                     return stack.length;
5567                 }
5568             };
5569         }
5570     };
5571 }).factory('trapFocusInElement', ['$document', '$isElement', 'DOMHelper', 'keymap', function ($document, $isElement, DOMHelper, keymap) {
5572     var elementStack = [];
5573     var stackHead = undefined;
5574     var firstTabableElement, lastTabableElement;
5575
5576     var trapKeyboardFocusInFirstElement = function (e) {
5577         if (!e.keyCode) {
5578             e.keyCode = e.which;
5579         }
5580
5581         if (e.shiftKey === true && e.keyCode === keymap.KEY.TAB) {
5582             lastTabableElement[0].focus();
5583             e.preventDefault(e);
5584             e.stopPropagation(e);
5585         }
5586
5587     };
5588
5589     var trapKeyboardFocusInLastElement = function (e) {
5590         if (!e.keyCode) {
5591             e.keyCode = e.which;
5592         }
5593
5594         if (e.shiftKey === false && e.keyCode === keymap.KEY.TAB) {
5595             firstTabableElement[0].focus();
5596             e.preventDefault(e);
5597             e.stopPropagation(e);
5598         }
5599     };
5600     
5601     var trapFocusInElement = function (flag, firstTabableElementParam, lastTabableElementParam) {
5602         var bodyElements = $document.find('body').children();
5603
5604         firstTabableElement = firstTabableElementParam ? firstTabableElementParam : angular.element(DOMHelper.firstTabableElement(stackHead));
5605         lastTabableElement = lastTabableElementParam ? lastTabableElementParam : angular.element(DOMHelper.lastTabableElement(stackHead));
5606
5607         if (flag) {
5608             for (var i = 0; i < bodyElements.length; i++) {
5609                 if (bodyElements[i] !== stackHead[0]) {
5610                     bodyElements.eq(i).attr('aria-hidden', true);
5611                 }
5612             }
5613             firstTabableElement.bind('keydown', trapKeyboardFocusInFirstElement);
5614             lastTabableElement.bind('keydown', trapKeyboardFocusInLastElement);
5615         } else {
5616             for (var j = 0; j < bodyElements.length; j++) {
5617                 if (bodyElements[j] !== stackHead[0]) {
5618                     bodyElements.eq(j).removeAttr('aria-hidden');
5619                 }
5620             }
5621             firstTabableElement.unbind('keydown', trapKeyboardFocusInFirstElement);
5622             lastTabableElement.unbind('keydown', trapKeyboardFocusInLastElement);
5623         }
5624     };
5625     var toggleTrapFocusInElement = function (flag, element) {
5626         if (angular.isDefined(flag) && angular.isDefined(element)) {
5627             if (angular.isUndefined(stackHead)) {
5628                 stackHead = element;
5629                 trapFocusInElement(flag);
5630             } else {
5631                 if (flag) {
5632                     trapFocusInElement(false);
5633                     elementStack.push(stackHead);
5634                     stackHead = element;
5635                     trapFocusInElement(true);
5636                 } else {
5637                     if (stackHead.prop('$$hashKey') === element.prop('$$hashKey')) {
5638                         trapFocusInElement(false);
5639                         stackHead = elementStack.pop();
5640                         if (angular.isDefined(stackHead)) {
5641                             trapFocusInElement(true);
5642                         }
5643                     }
5644                 }
5645             }
5646         }else {
5647             if (angular.isDefined(stackHead)) {
5648                 trapFocusInElement(false, firstTabableElement, lastTabableElement);
5649                 trapFocusInElement(true);
5650             }
5651         }
5652     };
5653
5654     return toggleTrapFocusInElement;
5655 }])
5656
5657 /**
5658  * A helper directive for the $modal service. It creates a backdrop element.
5659  */
5660 .directive('b2bModalBackdrop', ['$modalStack', '$timeout', function ($modalStack, $timeout) {
5661     return {
5662         restrict: 'EA',
5663         replace: true,
5664         templateUrl: 'b2bTemplate/modalsAndAlerts/b2b-backdrop.html',
5665         link: function (scope, element, attrs) {
5666             scope.close = function (evt) {
5667                 var modal = $modalStack.getTop();
5668                 if (modal && modal.value.backdrop && modal.value.backdrop !== 'static') {
5669                     evt.preventDefault();
5670                     evt.stopPropagation();
5671                     $modalStack.dismiss(modal.key, 'backdrop click');
5672                 }
5673             };
5674         }
5675     };
5676 }])
5677
5678 .directive('b2bModalWindow', ['$timeout', 'windowOrientation', '$window', 'keymap', function ($timeout, windowOrientation, $window, keymap) {
5679     return {
5680         restrict: 'EA',
5681         scope: {
5682             index: '@'
5683         },
5684         replace: true,
5685         transclude: true,
5686         templateUrl: 'b2bTemplate/modalsAndAlerts/b2b-window.html',
5687         controller: ['$scope', '$element', '$attrs', function (scope, element, attrs) {
5688             scope.windowClass = attrs.windowClass || '';
5689             scope.sizeClass = attrs.sizeClass || '';
5690             scope.isNotifDialog = false;
5691             scope.modalClose = attrs.modalClose || false;
5692
5693             this.setTitle = function (title) {
5694                 scope.title = title;
5695             };
5696             this.setContent = function (content) {
5697                 scope.content = content;
5698                 scope.isNotifDialog = true;
5699             };
5700             this.isDockedModal = scope.windowClass.indexOf('modal-docked') > -1;
5701         }],
5702         link: function (scope, element, attrs, ctrl) {
5703             if (ctrl.isDockedModal) {
5704                 scope.isModalLandscape = false;
5705
5706                 var window = angular.element($window);
5707                 scope.updateCss = function () {
5708                     if (windowOrientation.isPotrait()) { // Potrait Mode
5709                         scope.isModalLandscape = false;
5710                     } else if (windowOrientation.isLandscape()) { // Landscape Mode
5711                         scope.isModalLandscape = true;
5712                     }
5713                 };
5714
5715                 $timeout(function () {
5716                     scope.updateCss();
5717                     scope.$apply();
5718                 }, 100);
5719                 window.bind('orientationchange', function () {
5720                     scope.updateCss();
5721                     scope.$apply();
5722                 });
5723                 window.bind('resize', function () {
5724                     scope.updateCss();
5725                     scope.$apply();
5726                 });
5727             }else {
5728                 angular.element(element[0].querySelectorAll(".awd-select-list")).css({
5729                     "max-height": "200px"
5730                 });
5731             }
5732
5733             var isIE = /msie|trident/i.test(navigator.userAgent);
5734             if (isIE) {
5735                 if(angular.element(element[0].querySelector('.corner-button button.close')).length > 0){
5736                     angular.element(element[0].querySelector('.corner-button button.close')).bind('focus', function () {
5737                        angular.element(element[0].querySelector('.b2b-modal-header'))[0].scrollLeft = 0;
5738                        angular.element(element[0].querySelector('.b2b-modal-header'))[0].scrollTop = 0;
5739                     });
5740                 }
5741             }
5742
5743             if(scope.modalClose){
5744                 element.bind('keydown', function (e) {
5745                     if(e.keyCode == keymap.KEY.ESC){
5746                         e.preventDefault();
5747                         e.stopPropagation();
5748                     }
5749                 });
5750             }
5751         }
5752     };
5753 }])
5754
5755 .directive('b2bModalTitle', [function () {
5756     return {
5757         restrict: 'A',
5758         require: '^b2bModalWindow',
5759         link: function (scope, elem, attr, ctrl) {
5760             ctrl.setTitle(attr.id);
5761         }
5762     };
5763 }])
5764
5765 .directive('b2bModalContent', [function () {
5766     return {
5767         restrict: 'A',
5768         require: '^b2bModalWindow',
5769         link: function (scope, elem, attr, ctrl) {
5770             ctrl.setContent(attr.id);
5771         }
5772     };
5773 }])
5774
5775
5776 .directive('b2bModalBody', ['$timeout', '$position', '$document', '$window', 'windowOrientation', 'b2bAwdBreakpoints', function ($timeout, $position, $document, $window, windowOrientation, b2bAwdBreakpoints) {
5777     return {
5778         restrict: 'AC',
5779         scope: {
5780             index: '@'
5781         },
5782         require: '^b2bModalWindow',
5783         link: function (scope, element, attrs, ctrl) {
5784             var window = angular.element($window);
5785             var body = $document.find('body').eq(0);
5786             scope.setModalHeight = function () {
5787                 var modalHeaderHeight, modalFooterHeight, modalBodyHeight, windowHeight, windowWidth, modalHeight;
5788                 modalHeaderHeight = 0;
5789                 modalFooterHeight = 0;
5790                 windowHeight = $window.innerHeight;
5791                 windowWidth = $window.innerWidth;
5792                 body.css({
5793                     'height': windowHeight + 'px'
5794                 });
5795
5796                 if (ctrl.isDockedModal) {
5797                     var modalElements = element.parent().children();
5798                     for (var i = 0; i < modalElements.length; i++) {
5799                         if (modalElements.eq(i).hasClass('b2b-modal-header')) {
5800                             modalHeaderHeight = $position.position(modalElements.eq(i)).height;
5801                         } else if (modalElements.eq(i).hasClass('b2b-modal-footer')) {
5802                             modalFooterHeight = $position.position(modalElements.eq(i)).height;
5803                         }
5804                     }
5805
5806                     modalHeight = $position.position(element.parent()).height;
5807
5808                     modalBodyHeight = modalHeight - (modalHeaderHeight + modalFooterHeight) + 'px';
5809
5810                     if (windowOrientation.isPotrait()) { // Potrait Mode
5811                         element.removeAttr('style').css({
5812                             height: modalBodyHeight
5813                         });
5814                     } else if (windowOrientation.isLandscape() && windowWidth < b2bAwdBreakpoints.breakpoints.mobile.max) { // Landscape Mode Mobile
5815                         element.removeAttr('style');
5816                     } else if (windowOrientation.isLandscape() && windowWidth >= b2bAwdBreakpoints.breakpoints.mobile.max) { // Landscape Mode Non-Mobile
5817                         element.removeAttr('style').css({
5818                             height: modalBodyHeight
5819                         });
5820                     }
5821                 }
5822             };
5823
5824             $timeout(function () {
5825                 scope.setModalHeight();
5826                 scope.$apply();
5827             }, 100);
5828             window.bind('orientationchange', function () {
5829                 scope.setModalHeight();
5830                 scope.$apply();
5831             });
5832             window.bind('resize', function () {
5833                 scope.setModalHeight();
5834                 scope.$apply();
5835             });
5836         }
5837     };
5838 }])
5839
5840 .directive('b2bModalFooter', ['windowOrientation', '$window', function (windowOrientation, $window) {
5841     return {
5842         restrict: 'AC',
5843         scope: {
5844             index: '@'
5845         },
5846         link: function (scope, element, attrs) {
5847
5848         }
5849     };
5850 }])
5851
5852 .factory('$modalStack', ['$document', '$compile', '$rootScope', '$$stackedMap', '$log', '$timeout', 'trapFocusInElement', function ($document, $compile, $rootScope, $$stackedMap, $log, $timeout, trapFocusInElement) {
5853     var backdropjqLiteEl, backdropDomEl;
5854     var backdropScope = $rootScope.$new(true);
5855     var body = $document.find('body').eq(0);
5856     var html = $document.find('html').eq(0);
5857     var openedWindows = $$stackedMap.createNew();
5858     var $modalStack = {};
5859
5860     function backdropIndex() {
5861         var topBackdropIndex = -1;
5862         var opened = openedWindows.keys();
5863         for (var i = 0; i < opened.length; i++) {
5864             if (openedWindows.get(opened[i]).value.backdrop) {
5865                 topBackdropIndex = i;
5866             }
5867         }
5868         return topBackdropIndex;
5869     }
5870
5871     $rootScope.$watch(backdropIndex, function (newBackdropIndex) {
5872         backdropScope.index = newBackdropIndex;
5873     });
5874
5875     function removeModalWindow(modalInstance) {
5876         //background scroll fix
5877         html.removeAttr('style');
5878         body.removeAttr('style');
5879         body.removeClass('styled-by-modal');
5880
5881         var modalWindow = openedWindows.get(modalInstance).value;
5882         trapFocusInElement(false, modalWindow.modalDomEl);
5883
5884         //clean up the stack
5885         openedWindows.remove(modalInstance);
5886
5887         //remove window DOM element
5888         modalWindow.modalDomEl.remove();
5889
5890         //remove backdrop if no longer needed
5891         if (backdropDomEl && backdropIndex() === -1) {
5892             backdropDomEl.remove();
5893             backdropDomEl = undefined;
5894         }
5895
5896         //destroy scope
5897         modalWindow.modalScope.$destroy();
5898     }
5899
5900     $document.bind('keydown', function (evt) {
5901         var modal;
5902
5903         if (evt.which === 27) {
5904             modal = openedWindows.top();
5905             if (modal && modal.value.keyboard) {
5906                 $rootScope.$apply(function () {
5907                     $modalStack.dismiss(modal.key);
5908                 });
5909             }
5910         }
5911     });
5912
5913     $modalStack.open = function (modalInstance, modal) {
5914
5915         openedWindows.add(modalInstance, {
5916             deferred: modal.deferred,
5917             modalScope: modal.scope,
5918             backdrop: modal.backdrop,
5919             keyboard: modal.keyboard
5920         });
5921
5922         var angularDomEl = angular.element('<div b2b-modal-window></div>');
5923         angularDomEl.attr('window-class', modal.windowClass);
5924         angularDomEl.attr('size-class', modal.sizeClass);
5925         angularDomEl.attr('index', openedWindows.length() - 1);
5926         angularDomEl.attr('modal-close', modal.modalClose);
5927         angularDomEl.html(modal.content);
5928
5929         var modalDomEl = $compile(angularDomEl)(modal.scope);
5930         openedWindows.top().value.modalDomEl = modalDomEl;
5931         //background page scroll fix
5932         html.css({
5933             'overflow-y': 'hidden'
5934         });
5935         body.css({
5936             'overflow-y': 'hidden',
5937             'width': '100%',
5938             'height': window.innerHeight + 'px'
5939         });
5940         body.addClass('styled-by-modal');
5941         body.append(modalDomEl);
5942
5943         if (backdropIndex() >= 0 && !backdropDomEl) {
5944             backdropjqLiteEl = angular.element('<div b2b-modal-backdrop></div>');
5945             backdropDomEl = $compile(backdropjqLiteEl)(backdropScope);
5946             body.append(backdropDomEl);
5947         }
5948
5949         $timeout(function () {
5950
5951             if (modal.scope.$$childHead.isNotifDialog) {
5952                 angular.element(modalDomEl).find('button')[0].focus();
5953             } else {
5954                 angular.element(modalDomEl)[0].focus();
5955             }
5956             trapFocusInElement(true, angular.element(modalDomEl).eq(0));
5957         }, 200);
5958     };
5959
5960     $modalStack.close = function (modalInstance, result) {
5961         var modal = openedWindows.get(modalInstance);
5962         if (modal) {
5963             modal.value.deferred.resolve(result);
5964             removeModalWindow(modalInstance);
5965         }
5966     };
5967
5968     $modalStack.dismiss = function (modalInstance, reason) {
5969         var modalWindow = openedWindows.get(modalInstance).value;
5970         if (modalWindow) {
5971             modalWindow.deferred.reject(reason);
5972             removeModalWindow(modalInstance);
5973         }
5974     };
5975
5976     $modalStack.getTop = function () {
5977         return openedWindows.top();
5978     };
5979
5980     return $modalStack;
5981 }])
5982
5983 .provider('$modal', function () {
5984     var $modalProvider = {
5985         options: {
5986             backdrop: true, //can be also false or 'static'
5987             keyboard: true
5988         },
5989         $get: ['$injector', '$rootScope', '$q', '$http', '$templateCache', '$controller', '$modalStack', function ($injector, $rootScope, $q, $http, $templateCache, $controller, $modalStack) {
5990             var $modal = {};
5991
5992             function getTemplatePromise(options) {
5993                 return options.template ? $q.when(options.template) :
5994                     $http.get(options.templateUrl, {
5995                         cache: $templateCache
5996                     }).then(function (result) {
5997                         return result.data;
5998                     });
5999             }
6000
6001             function getResolvePromises(resolves) {
6002                 var promisesArr = [];
6003                 angular.forEach(resolves, function (value, key) {
6004                     if (angular.isFunction(value) || angular.isArray(value)) {
6005                         promisesArr.push($q.when($injector.invoke(value)));
6006                     }
6007                 });
6008                 return promisesArr;
6009             }
6010
6011             $modal.open = function (modalOptions) {
6012
6013                 var modalResultDeferred = $q.defer();
6014                 var modalOpenedDeferred = $q.defer();
6015                 //prepare an instance of a modal to be injected into controllers and returned to a caller
6016                 var modalInstance = {
6017                     result: modalResultDeferred.promise,
6018                     opened: modalOpenedDeferred.promise,
6019                     close: function (result) {
6020                         $modalStack.close(modalInstance, result);
6021                     },
6022                     dismiss: function (reason) {
6023                         $modalStack.dismiss(modalInstance, reason);
6024                     }
6025                 };
6026
6027                 //merge and clean up options
6028                 modalOptions = angular.extend({}, $modalProvider.options, modalOptions);
6029                 modalOptions.resolve = modalOptions.resolve || {};
6030
6031                 //verify options
6032                 if (!modalOptions.template && !modalOptions.templateUrl) {
6033                     throw new Error('One of template or templateUrl options is required.');
6034                 }
6035
6036                 var templateAndResolvePromise =
6037                     $q.all([getTemplatePromise(modalOptions)].concat(getResolvePromises(modalOptions.resolve)));
6038
6039
6040                 templateAndResolvePromise.then(function resolveSuccess(tplAndVars) {
6041
6042                     var modalScope = (modalOptions.scope || $rootScope).$new();
6043                     modalScope.$close = modalInstance.close;
6044                     modalScope.$dismiss = modalInstance.dismiss;
6045
6046                     var ctrlInstance, ctrlLocals = {};
6047                     var resolveIter = 1;
6048
6049                     //controllers
6050                     if (modalOptions.controller) {
6051                         ctrlLocals.$scope = modalScope;
6052                         ctrlLocals.$modalInstance = modalInstance;
6053                         angular.forEach(modalOptions.resolve, function (value, key) {
6054                             ctrlLocals[key] = tplAndVars[resolveIter++];
6055                         });
6056
6057                         ctrlInstance = $controller(modalOptions.controller, ctrlLocals);
6058                     }
6059
6060                     $modalStack.open(modalInstance, {
6061                         scope: modalScope,
6062                         deferred: modalResultDeferred,
6063                         content: tplAndVars[0],
6064                         backdrop: modalOptions.backdrop,
6065                         keyboard: modalOptions.keyboard,
6066                         windowClass: modalOptions.windowClass,
6067                         sizeClass: modalOptions.sizeClass,
6068                         modalClose: modalOptions.modalClose
6069                     });
6070
6071                 }, function resolveError(reason) {
6072                     modalResultDeferred.reject(reason);
6073                 });
6074
6075                 templateAndResolvePromise.then(function () {
6076                     modalOpenedDeferred.resolve(true);
6077                 }, function () {
6078                     modalOpenedDeferred.reject(false);
6079                 });
6080
6081                 return modalInstance;
6082             };
6083
6084             return $modal;
6085         }]
6086     };
6087
6088     return $modalProvider;
6089 })
6090
6091 .directive("b2bModal", ["$modal", "$log", '$scrollTo', function ($modal, $log, $scrollTo) {
6092     return {
6093         restrict: 'A',
6094         scope: {
6095             b2bModal: '@',
6096             modalController: '@',
6097             modalOk: '&',
6098             modalCancel: '&',
6099             windowClass: '@',
6100             sizeClass: '@', 
6101             modalClose: '@'
6102         },
6103         link: function (scope, elm, attr) {
6104             elm.bind('click', function (ev) {
6105                 var currentPosition = ev.pageY - ev.clientY;
6106                 ev.preventDefault();
6107                 if (angular.isDefined(elm.attr("href")) && elm.attr("href") !== "") {
6108                     scope.b2bModal = elm.attr("href");
6109                 }
6110                 $modal.open({
6111                     templateUrl: scope.b2bModal,
6112                     controller: scope.modalController,
6113                     windowClass: scope.windowClass,
6114                     sizeClass: scope.sizeClass, 
6115                     modalClose: scope.modalClose
6116                 }).result.then(function (value) {
6117                     scope.modalOk({
6118                         value: value
6119                     });
6120                     elm[0].focus();
6121                 }, function (value) {
6122                     scope.modalCancel({
6123                         value: value
6124                     });
6125                     elm[0].focus();
6126                 });
6127             });
6128         }
6129     };
6130 }])
6131
6132 .directive("utilityFilter", ["$modal", "$log", '$scrollTo', function ($modal, $log, $scrollTo) {
6133     return {
6134         restrict: 'EA',
6135         scope: {
6136             utilityFilter: '@'
6137         },
6138         require: 'ngModel',
6139         templateUrl: 'b2bTemplate/modal/u-filter.html',
6140         link: function (scope, element, attribute, ctrl) {
6141             //controller to be passed to $modal service
6142             scope.options = angular.copy(scope.$parent.$eval(attribute.ngModel));
6143             scope.$parent.$watch(attribute.ngModel, function (newVal, oldVal) {
6144                 if (newVal !== oldVal) {
6145                     scope.options = newVal;
6146                 }
6147             });
6148             var modalCtrl = function ($scope, options) {
6149                 $scope.options = angular.copy(options);
6150             };
6151
6152             if (angular.isDefined(scope.utilityFilter)) {
6153                 scope.templateUrl = scope.utilityFilter;
6154             } else {
6155                 scope.templateUrl = 'b2bTemplate/modal/u-filter-window.html';
6156             }
6157             element.bind('click', function (ev) {
6158                 var currentPosition = ev.pageY - ev.clientY;
6159                 $modal.open({
6160                     templateUrl: scope.templateUrl,
6161                     controller: modalCtrl,
6162                     resolve: {
6163                         options: function () {
6164                             return scope.options;
6165                         }
6166                     }
6167                 }).result.then(function (value) {
6168                     ctrl.$setViewValue(value);
6169                     element[0].focus();
6170                     $scrollTo(0, currentPosition, 0);
6171                 }, function () {
6172                     element[0].focus();
6173                     $scrollTo(0, currentPosition, 0);
6174                 });
6175             });
6176         }
6177     };
6178 }]);
6179 /**
6180  * @ngdoc directive
6181  * @name Forms.att:monthSelector
6182  *
6183  * @description
6184  *  <file src="src/monthSelector/docs/readme.md" />
6185  *
6186  * @usage
6187  * <div b2b-monthpicker ng-model="dt" min="minDate" max="maxDate" mode="monthpicker"></div>
6188     
6189  * @example
6190  *  <section id="code">
6191         <example module="b2b.att">
6192             <file src="src/monthSelector/docs/demo.html" />
6193             <file src="src/monthSelector/docs/demo.js" />
6194         </example>
6195     </section>
6196  *
6197  */
6198 angular.module('b2b.att.monthSelector', ['b2b.att.position', 'b2b.att.utilities'])
6199
6200 .constant('b2bMonthpickerConfig', {
6201     dateFormat: 'MM/dd/yyyy',
6202     dayFormat: 'd',
6203     monthFormat: 'MMMM',
6204     yearFormat: 'yyyy',
6205     dayHeaderFormat: 'EEEE',
6206     dayTitleFormat: 'MMMM yyyy',
6207     disableWeekend: false,
6208     disableSunday: false,
6209     disableDates: null,
6210     onSelectClose: null,
6211     startingDay: 0,
6212     minDate: null,
6213     maxDate: null,
6214     dueDate: null,
6215     fromDate: null,
6216     legendIcon: null,
6217     legendMessage: null,
6218     calendarDisabled: false,
6219     collapseWait: 0,
6220     orientation: 'left',
6221     inline: false,
6222     mode:0,
6223     helperText: 'The date you selected is $date. Double tap to open calendar. Select a date to close the calendar.',
6224     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.',
6225     MonthpickerEvalAttributes: ['dateFormat', 'dayFormat', 'monthFormat', 'yearFormat', 'dayHeaderFormat', 'dayTitleFormat', 'disableWeekend', 'disableSunday', 'startingDay', 'collapseWait', 'orientation','mode','id'],
6226     MonthpickerWatchAttributes: ['min', 'max', 'due', 'from', 'legendIcon', 'legendMessage', 'ngDisabled'],
6227     MonthpickerFunctionAttributes: ['disableDates', 'onSelectClose']
6228 })
6229
6230 .factory('b2bMonthpickerService', ['b2bMonthpickerConfig', 'dateFilter', function (b2bMonthpickerConfig, dateFilter) {
6231     var setAttributes = function (attr, elem) {
6232         if (angular.isDefined(attr) && attr !== null && angular.isDefined(elem) && elem !== null) {
6233             var attributes = b2bMonthpickerConfig.MonthpickerEvalAttributes.concat(b2bMonthpickerConfig.MonthpickerWatchAttributes, b2bMonthpickerConfig.MonthpickerFunctionAttributes);
6234             for (var key in attr) {
6235                 var val = attr[key];
6236                 if (attributes.indexOf(key) !== -1 && angular.isDefined(val)) {
6237                     elem.attr(key.toSnakeCase(), key);
6238                 }
6239             }
6240         }
6241     };
6242
6243     var bindScope = function (attr, scope) {
6244         if (angular.isDefined(attr) && attr !== null && angular.isDefined(scope) && scope !== null) {
6245             var evalFunction = function (key, val) {
6246                 scope[key] = scope.$parent.$eval(val);
6247             };
6248
6249             var watchFunction = function (key, val) {
6250                 scope.$parent.$watch(val, function (value) {
6251                     scope[key] = value;
6252                 });
6253                 scope.$watch(key, function (value) {
6254                     scope.$parent[val] = value;
6255                 });
6256             };
6257
6258             var evalAttributes = b2bMonthpickerConfig.MonthpickerEvalAttributes;
6259             var watchAttributes = b2bMonthpickerConfig.MonthpickerWatchAttributes;
6260             for (var key in attr) {
6261                 var val = attr[key];
6262                 if (evalAttributes.indexOf(key) !== -1 && angular.isDefined(val)) {
6263                     evalFunction(key, val);
6264                 } else if (watchAttributes.indexOf(key) !== -1 && angular.isDefined(val)) {
6265                     watchFunction(key, val);
6266                 }
6267             }
6268         }
6269     };
6270
6271     return {
6272         setAttributes: setAttributes,
6273         bindScope: bindScope
6274     };
6275 }])
6276
6277 .controller('b2bMonthpickerController', ['$scope', '$attrs', 'dateFilter', '$element', '$position', 'b2bMonthpickerConfig', function ($scope, $attrs, dateFilter, $element, $position, dtConfig) {
6278     var format = {
6279             date: getValue($attrs.dateFormat, dtConfig.dateFormat),
6280             day: getValue($attrs.dayFormat, dtConfig.dayFormat),
6281             month: getValue($attrs.monthFormat, dtConfig.monthFormat),
6282             year: getValue($attrs.yearFormat, dtConfig.yearFormat),
6283             dayHeader: getValue($attrs.dayHeaderFormat, dtConfig.dayHeaderFormat),
6284             dayTitle: getValue($attrs.dayTitleFormat, dtConfig.dayTitleFormat),
6285             disableWeekend: getValue($attrs.disableWeekend, dtConfig.disableWeekend),
6286             disableSunday: getValue($attrs.disableSunday, dtConfig.disableSunday),
6287             disableDates: getValue($attrs.disableDates, dtConfig.disableDates)
6288         },
6289         startingDay = getValue($attrs.startingDay, dtConfig.startingDay);
6290
6291     $scope.minDate = dtConfig.minDate ? $scope.resetTime(dtConfig.minDate) : null;
6292     $scope.maxDate = dtConfig.maxDate ? $scope.resetTime(dtConfig.maxDate) : null;
6293     $scope.dueDate = dtConfig.dueDate ? $scope.resetTime(dtConfig.dueDate) : null;
6294     $scope.fromDate = dtConfig.fromDate ? $scope.resetTime(dtConfig.fromDate) : null;
6295     $scope.legendIcon = dtConfig.legendIcon ? dtConfig.legendIcon : null;
6296     $scope.legendMessage = dtConfig.legendMessage ? dtConfig.legendMessage : null;
6297     $scope.ngDisabled = dtConfig.calendarDisabled ? dtConfig.calendarDisabled : null;
6298     $scope.collapseWait = getValue($attrs.collapseWait, dtConfig.collapseWait);
6299     $scope.orientation = getValue($attrs.orientation, dtConfig.orientation);
6300     $scope.onSelectClose = getValue($attrs.onSelectClose, dtConfig.onSelectClose);
6301     $scope.mode = getValue($attrs.mode, dtConfig.mode);
6302     
6303     $scope.inline = $attrs.inline === 'true' ? true : dtConfig.inline;
6304
6305     function getValue(value, defaultValue) {
6306         return angular.isDefined(value) ? $scope.$parent.$eval(value) : defaultValue;
6307     }
6308
6309     function getDaysInMonth(year, month) {
6310         return new Date(year, month, 0).getDate();
6311     }
6312
6313     function getDates(startDate, n) {
6314         var dates = new Array(n);
6315         var current = startDate,
6316             i = 0;
6317         while (i < n) {
6318             dates[i++] = new Date(current);
6319             current.setDate(current.getDate() + 1);
6320         }
6321         return dates;
6322     }
6323
6324     this.updatePosition = function (b2bMonthpickerPopupTemplate) {
6325         $scope.position = $position.offset($element);
6326         if($element.find('input').length > 0 ){
6327             $scope.position.top += $element.find('input').prop('offsetHeight');
6328         }else{
6329             $scope.position.top += $element.find('a').prop('offsetHeight');
6330         }
6331         
6332         if ($scope.orientation === 'right') {
6333             $scope.position.left -= (((b2bMonthpickerPopupTemplate && b2bMonthpickerPopupTemplate.prop('offsetWidth')) || 290) - $element.find('input').prop('offsetWidth'));
6334         }
6335     };
6336
6337     function isSelected(dt) { 
6338         if (dt && angular.isDate($scope.currentDate) && compare(dt, $scope.currentDate) === 0) {
6339             return true;
6340         }
6341         return false;
6342     }
6343
6344     function isFromDate(dt) {
6345         if (dt && angular.isDate($scope.fromDate) && compare(dt, $scope.fromDate) === 0) {
6346             return true;
6347         }
6348         return false;
6349     }
6350
6351     function isDateRange(dt) {
6352         if (dt && $scope.fromDate && angular.isDate($scope.currentDate) && (compare(dt, $scope.fromDate) >= 0) && (compare(dt, $scope.currentDate) <= 0)) {
6353             return true;
6354         } else if (dt && $scope.fromDate && compare(dt, $scope.fromDate) === 0) {
6355             return true;
6356         }
6357         return false;
6358     }
6359
6360     function isOld(date, currentMonthDate) {
6361         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())) {
6362             return true;
6363         } else {
6364             return false;
6365         }
6366     }
6367
6368     function isNew(date, currentMonthDate) {
6369         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())) {
6370             return true;
6371         } else {
6372             return false;
6373         }
6374     }
6375
6376     function isPastDue(dt) {
6377         if ($scope.dueDate) {
6378             return (dt > $scope.dueDate);
6379         }
6380         return false;
6381     }
6382
6383     function isDueDate(dt) {
6384         if ($scope.dueDate) {
6385             return (dt.getTime() === $scope.dueDate.getTime());
6386         }
6387         return false;
6388     }
6389
6390     var isDisabled = function (date, currentMonthDate) {
6391         if ($attrs.from && !angular.isDate($scope.fromDate)) {
6392             return true;
6393         }
6394         if (format.disableWeekend === true && (dateFilter(date, format.dayHeader) === "Saturday" || dateFilter(date, format.dayHeader) === "Sunday")) {
6395             return true;
6396         }
6397         if (format.disableSunday === true && (dateFilter(date, format.dayHeader) === "Sunday")) {
6398             return true;
6399         }
6400         if (isOld(date, currentMonthDate) || isNew(date, currentMonthDate)) {
6401             return true;
6402         }
6403         return (($scope.minDate && compare(date, $scope.minDate) < 0) || ($scope.maxDate && compare(date, $scope.maxDate) > 0) || (format.disableDates && format.disableDates({
6404             date: date
6405         })));
6406     };
6407     
6408     var isDisabledMonth = function (date, currentMonthDate) {
6409         if ($attrs.from && !angular.isDate($scope.fromDate)) {
6410             return true;
6411         }
6412         if (format.disableWeekend === true && (dateFilter(date, format.dayHeader) === "Saturday" || dateFilter(date, format.dayHeader) === "Sunday")) {
6413             return true;
6414         }
6415         if (format.disableSunday === true && (dateFilter(date, format.dayHeader) === "Sunday")) {
6416             return true;
6417         }
6418         return (($scope.minDate && compare(date, $scope.minDate) < 0) || ($scope.maxDate && compare(date, $scope.maxDate) > 0) || (format.disableDates && format.disableDates({
6419             date: date
6420         })));
6421     };    
6422          
6423     var compare = function (date1, date2) {
6424         return (new Date(date1.getFullYear(), date1.getMonth(), date1.getDate()) - new Date(date2.getFullYear(), date2.getMonth(), date2.getDate()));
6425     };
6426
6427     function isMinDateAvailable(startDate, endDate) {
6428         if (($scope.minDate && $scope.minDate.getTime() >= startDate.getTime()) && ($scope.minDate.getTime() <= endDate.getTime())) {
6429             $scope.disablePrev = true;
6430             $scope.visibilityPrev = "hidden";
6431         } else {
6432             $scope.disablePrev = false;
6433             $scope.visibilityPrev = "visible";
6434         }
6435     }
6436     
6437     function isMaxDateAvailable(startDate, endDate) {
6438         if (($scope.maxDate && $scope.maxDate.getTime() >= startDate.getTime()) && ($scope.maxDate.getTime() <= endDate.getTime())) {
6439             $scope.disableNext = true;
6440             $scope.visibilityNext = "hidden";
6441         } else {
6442             $scope.disableNext = false;
6443             $scope.visibilityNext = "visible";
6444         }
6445     }    
6446     
6447     function isYearInRange(currentYear) {
6448             
6449         if ($scope.minDate && currentYear === $scope.minDate.getFullYear()) {
6450             $scope.disablePrev = true;
6451             $scope.visibilityPrev = "hidden";
6452         } else {
6453             $scope.disablePrev = false;
6454             $scope.visibilityPrev = "visible";
6455         }
6456         
6457         if ($scope.maxDate && currentYear === $scope.maxDate.getFullYear()) {
6458             $scope.disableNext = true;
6459             $scope.visibilityNext = "hidden";
6460         } else {
6461             $scope.disableNext = false;
6462             $scope.visibilityNext = "visible";
6463         }
6464         
6465     }    
6466
6467     this.focusNextPrev = function(b2bMonthpickerPopupTemplate,init){
6468         if(init){
6469             if (!$scope.disablePrev){
6470                 b2bMonthpickerPopupTemplate[0].querySelector('th.prev').focus();
6471             }else if (!$scope.disableNext){
6472                 b2bMonthpickerPopupTemplate[0].querySelector('th.next').focus();
6473             }else{
6474                 b2bMonthpickerPopupTemplate[0].querySelector('th.b2b-monthSelector-label').focus();
6475             }
6476         }else{
6477             if ($scope.disableNext || $scope.disablePrev){
6478                 b2bMonthpickerPopupTemplate[0].querySelector('th.b2b-monthSelector-label').focus();
6479             }       
6480         }    
6481     };
6482
6483     function getLabel(label) {
6484         if (label) {
6485             var labelObj = {
6486                 pre: label.substr(0, 1).toUpperCase(),
6487                 post: label
6488             };
6489             return labelObj;
6490         }
6491         return;
6492     }
6493
6494     function makeDate(date, dayFormat, dayHeaderFormat, isSelected, isFromDate, isDateRange, isOld, isNew, isDisabled, dueDate, pastDue) {
6495         return {
6496             date: date,
6497             label: dateFilter(date, dayFormat),
6498             header: dateFilter(date, dayHeaderFormat),
6499             selected: !!isSelected,
6500             fromDate: !!isFromDate,
6501             dateRange: !!isDateRange,
6502             oldMonth: !!isOld,
6503             nextMonth: !!isNew,
6504             disabled: !!isDisabled,
6505             dueDate: !!dueDate,
6506             pastDue: !!pastDue,
6507             focusable: !((isDisabled && !(isSelected || isDateRange)) || (isOld || isNew))
6508         };
6509     }
6510     
6511     this.modes = [
6512         {
6513             name: 'day',
6514             getVisibleDates: function (date) {
6515                 var year = date.getFullYear(),
6516                     month = date.getMonth(),
6517                     firstDayOfMonth = new Date(year, month, 1),
6518                     lastDayOfMonth = new Date(year, month + 1, 0);
6519                 var difference = startingDay - firstDayOfMonth.getDay(),
6520                     numDisplayedFromPreviousMonth = (difference > 0) ? 7 - difference : -difference,
6521                     firstDate = new Date(firstDayOfMonth),
6522                     numDates = 0;
6523
6524                 if (numDisplayedFromPreviousMonth > 0) {
6525                     firstDate.setDate(-numDisplayedFromPreviousMonth + 1);
6526                     numDates += numDisplayedFromPreviousMonth; // Previous
6527                 }
6528                 numDates += getDaysInMonth(year, month + 1); // Current
6529                 numDates += (7 - numDates % 7) % 7; // Next
6530
6531                 var days = getDates(firstDate, numDates),
6532                     labels = new Array(7);
6533                 for (var i = 0; i < numDates; i++) {
6534                     var dt = new Date(days[i]);
6535                     days[i] = makeDate(dt,
6536                         format.day,
6537                         format.dayHeader,
6538                         isSelected(dt),
6539                         isFromDate(dt),
6540                         isDateRange(dt),
6541                         isOld(dt, date),
6542                         isNew(dt, date),
6543                         isDisabled(dt, date),
6544                         isDueDate(dt),
6545                         isPastDue(dt));
6546                 }
6547                 for (var j = 0; j < 7; j++) {
6548                     labels[j] = getLabel(dateFilter(days[j].date, format.dayHeader));
6549                 }
6550                 isMinDateAvailable(firstDayOfMonth, lastDayOfMonth);
6551                 isMaxDateAvailable(firstDayOfMonth, lastDayOfMonth);
6552                 return {
6553                     objects: days,
6554                     title: dateFilter(date, format.dayTitle),
6555                     labels: labels
6556                 };
6557             },
6558             split: 7,
6559             step: {
6560                 months: 1
6561             }
6562         },
6563         {
6564             name: 'month',
6565             getVisibleDates: function(date) {
6566                 var months = [], 
6567                     labels = [], 
6568                     year = date.getFullYear();
6569                     for (var i = 0; i < 12; i++) {
6570                         var dt = new Date(year,i,1);                
6571                         months[i] = makeDate(dt,
6572                                     format.month,
6573                                     format.dayHeader,
6574                                     isSelected(dt), 
6575                                     isFromDate(dt),
6576                                     isDateRange(dt),
6577                                     false,
6578                                     false,
6579                                     isDisabledMonth(dt, date),
6580                                     isDueDate(dt),                                       
6581                                     isPastDue(dt));                                                                                                                                                         
6582                     }
6583                 isYearInRange(year);  
6584                 return {objects: months, title: dateFilter(date, format.year), labels: labels};
6585             },
6586             split:4,
6587             step: {years: 1}
6588         }
6589     ];
6590 }])
6591
6592 .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) {
6593     return {
6594         restrict: 'EA',
6595         scope: {
6596           trigger: '='
6597         },
6598         replace: true,
6599         transclude: true,
6600         templateUrl: function (elem, attr) {
6601             if (attr.inline === 'true') {
6602                 return 'b2bTemplate/monthSelector/monthSelector-popup.html';
6603             }else if (attr.link === 'true') {
6604                 return 'b2bTemplate/monthSelector/monthSelectorLink.html';
6605             }else {
6606                 return 'b2bTemplate/monthSelector/monthSelector.html';
6607             }
6608         },
6609         scope: {},
6610         require: ['b2bMonthpickerPopup', 'ngModel', '?^b2bMonthpickerGroup'],
6611         controller: 'b2bMonthpickerController',
6612         link: function (scope, element, attrs, ctrls) {
6613             var MonthpickerCtrl = ctrls[0],
6614                 ngModel = ctrls[1],
6615                 b2bMonthpickerGroupCtrl = ctrls[2];
6616             var b2bMonthpickerPopupTemplate;
6617
6618             if (!ngModel) {
6619                 $log.error("ng-model is required.");
6620                 return; // do nothing if no ng-model
6621             }
6622
6623             // Configuration parameters
6624             var mode = scope.mode,
6625                 selected;
6626             scope.isOpen = false;
6627
6628             scope.headers = [];
6629             scope.footers = [];
6630             scope.triggerInterval=undefined;
6631
6632
6633             if (b2bMonthpickerGroupCtrl) {
6634                 b2bMonthpickerGroupCtrl.registerMonthpickerScope(scope);
6635             }
6636
6637             element.bind('keydown', function (ev) {                   
6638                 if (!ev.keyCode) {
6639                     if (ev.which) {
6640                         ev.keyCode = ev.which;
6641                     } else if (ev.charCode) {
6642                         ev.keyCode = ev.charCode;
6643                     }
6644                 }                                
6645                 if(ev.keyCode === keymap.KEY.ESC)
6646                 {
6647                     scope.isOpen = false;
6648                     toggleCalendar(scope.isOpen);
6649                     scope.$apply();
6650                 }
6651             });
6652             
6653             element.find('button').bind('click', function () {
6654                 onClicked();                
6655             });
6656
6657             element.find('a').bind('click', function () {
6658                 onClicked();                
6659             });
6660
6661             
6662             element.find('input').bind('click', function () {
6663                 onClicked();
6664             });
6665
6666             var onClicked = function() {        
6667                 if (!scope.ngDisabled) {
6668                     scope.isOpen = !scope.isOpen;
6669                     toggleCalendar(scope.isOpen);                    
6670                     MonthpickerCtrl.updatePosition(b2bMonthpickerPopupTemplate);
6671                     scope.$apply();
6672                 }
6673             };
6674         
6675             var toggleCalendar = function (flag) {
6676                 if (!scope.inline) {
6677                     if (flag) {
6678                         b2bMonthpickerPopupTemplate = angular.element($templateCache.get('b2bTemplate/monthSelector/monthSelector-popup.html'));
6679                         b2bMonthpickerPopupTemplate.attr('b2b-trap-focus-inside-element', 'false');
6680                         b2bMonthpickerPopupTemplate.attr('trigger', 'true');
6681                         b2bMonthpickerPopupTemplate = $compile(b2bMonthpickerPopupTemplate)(scope);
6682                         $document.find('body').append(b2bMonthpickerPopupTemplate);
6683                         b2bMonthpickerPopupTemplate.bind('keydown', escPress);
6684                         $timeout(function () {
6685                             scope.getFocus = true;
6686                             scope.trigger=0;
6687                             scope.$apply();
6688                             $timeout(function () {
6689                                 scope.getFocus = false;
6690                                 scope.$apply();
6691                                 MonthpickerCtrl.focusNextPrev(b2bMonthpickerPopupTemplate,true);
6692                             }, 100);
6693                         });
6694                         scope.triggerInterval = $interval(function () {
6695                             //This value is updated to trigger init() function of directive on year change.
6696                             scope.trigger=(scope.trigger === 0 ? 1 : 0);
6697                         }, 200);
6698
6699                     } else {
6700                         b2bMonthpickerPopupTemplate.unbind('keydown', escPress);
6701                         if(scope.triggerInterval)
6702                         {
6703                             $interval.cancel(scope.triggerInterval);
6704                             scope.triggerInterval=undefined;
6705                         }
6706                         b2bMonthpickerPopupTemplate.remove();
6707                         if(element.find('button').length > 0){
6708                             element.find('button')[0].focus();
6709                         }else{
6710                             element.find('a')[0].focus();
6711                         }
6712                         
6713                         scope.getFocus = false;
6714                     }
6715                 }
6716             };
6717
6718             var outsideClick = function (e) {
6719                 var isElement = $isElement(angular.element(e.target), element, $document);
6720                 var isb2bMonthpickerPopupTemplate = $isElement(angular.element(e.target), b2bMonthpickerPopupTemplate, $document);
6721                 if (!(isElement || isb2bMonthpickerPopupTemplate)) {
6722                     scope.isOpen = false;
6723                     toggleCalendar(scope.isOpen);
6724                     scope.$apply();
6725                 }
6726             };
6727
6728             var escPress = function (ev) {
6729                 if (!ev.keyCode) {
6730                     if (ev.which) {
6731                         ev.keyCode = ev.which;
6732                     } else if (ev.charCode) {
6733                         ev.keyCode = ev.charCode;
6734                     }
6735                 }
6736                 if (ev.keyCode) {
6737                     if (ev.keyCode === keymap.KEY.ESC) {
6738                         scope.isOpen = false;
6739                         toggleCalendar(scope.isOpen);
6740                         ev.preventDefault();
6741                         ev.stopPropagation();
6742                     } else if (ev.keyCode === 33) {
6743                         !scope.disablePrev && scope.move(-1);
6744                         $timeout(function () {
6745                             scope.getFocus = true;
6746                             scope.$apply();
6747                             $timeout(function () {
6748                                 scope.getFocus = false;
6749                                 scope.$apply();
6750                             }, 100);
6751                         });
6752                         ev.preventDefault();
6753                         ev.stopPropagation();
6754                     } else if (ev.keyCode === 34) {
6755                         !scope.disableNext && scope.move(1);
6756                         $timeout(function () {
6757                             scope.getFocus = true;
6758                             scope.$apply();
6759                             $timeout(function () {
6760                                 scope.getFocus = false;
6761                                 scope.$apply();
6762                             }, 100);
6763                         });
6764                         ev.preventDefault();
6765                         ev.stopPropagation();
6766                     }
6767                     scope.$apply();
6768                 }
6769             };              
6770                     
6771             $documentBind.click('isOpen', outsideClick, scope);
6772
6773             scope.$on('$destroy', function () {
6774                 if (scope.isOpen) {
6775                     scope.isOpen = false;
6776                     toggleCalendar(scope.isOpen);
6777                 }
6778             });
6779
6780             scope.resetTime = function (date) {
6781                 if (typeof date === 'string') {
6782                     date = date + 'T12:00:00';
6783                 }
6784                 var dt;
6785                 if (!isNaN(new Date(date))) {
6786                     dt = new Date(date);
6787                     if(scope.mode === 1){
6788                         dt = new Date(dt.getFullYear(), dt.getMonth());
6789                     }else{
6790                         dt = new Date(dt.getFullYear(), dt.getMonth(), dt.getDate());
6791                     }                                                            
6792                 } else {
6793                     return null;
6794                 }
6795                 return new Date(dt.getFullYear(), dt.getMonth(), dt.getDate());
6796             };
6797             
6798             if (attrs.min) {
6799                 scope.$parent.$watch($parse(attrs.min), function (value) {
6800                     scope.minDate = value ? scope.resetTime(value) : null;
6801                     refill();
6802                 });
6803             }
6804             if (attrs.max) {
6805                 scope.$parent.$watch($parse(attrs.max), function (value) {
6806                     scope.maxDate = value ? scope.resetTime(value) : null;
6807                     refill();
6808                 });
6809             }
6810             if (attrs.due) {
6811                 scope.$parent.$watch($parse(attrs.due), function (value) {
6812                     scope.dueDate = value ? scope.resetTime(value) : null;
6813                     refill();
6814                 });
6815             }
6816             if (attrs.from) {
6817                 scope.$parent.$watch($parse(attrs.from), function (value) {
6818                     scope.fromDate = value ? scope.resetTime(value) : null;
6819                     refill();
6820                 });
6821             }
6822
6823             if (attrs.legendIcon) {
6824                 scope.$parent.$watch(attrs.legendIcon, function (value) {
6825                     scope.legendIcon = value ? value : null;
6826                     refill();
6827                 });
6828             }
6829             if (attrs.legendMessage) {
6830                 scope.$parent.$watch(attrs.legendMessage, function (value) {
6831                     scope.legendMessage = value ? value : null;
6832                     refill();
6833                 });
6834             }
6835             if (attrs.ngDisabled) {
6836                 scope.$parent.$watch(attrs.ngDisabled, function (value) {
6837                     scope.ngDisabled = value ? value : null;
6838                 });
6839             }      
6840             
6841
6842             // Split array into smaller arrays
6843             function split(arr, size) {
6844                 var arrays = [];
6845                 while (arr.length > 0) {
6846                     arrays.push(arr.splice(0, size));
6847                 }
6848                 return arrays;
6849             }
6850             
6851             var moveMonth = function(selectedDate, direction) {
6852                 var step = MonthpickerCtrl.modes[scope.mode].step;
6853                 selectedDate.setDate(1);
6854                 selectedDate.setMonth(selectedDate.getMonth() + direction * (step.months || 0));
6855                 selectedDate.setFullYear(selectedDate.getFullYear() + direction * (step.years || 0));
6856
6857                 return selectedDate;
6858             };            
6859
6860             function refill(date) {
6861                 if (angular.isDate(date) && !isNaN(date)) {
6862                     selected = new Date(date);
6863                 } else {
6864                     if (!selected) {
6865                         selected = new Date();
6866                     }
6867                 }
6868
6869                 if (selected) {                    
6870                     var selectedCalendar;
6871                     if(scope.mode === 1){
6872                         if(!angular.isDate(selected))
6873                            {                           
6874                                 selected = new Date();
6875                            }
6876                         selectedCalendar = moveMonth(angular.copy(selected), -1);
6877                     } else {
6878                         selectedCalendar = angular.copy(selected);
6879                     }
6880                     
6881                     var currentMode = MonthpickerCtrl.modes[mode],
6882                         data = currentMode.getVisibleDates(selected);
6883
6884                     scope.rows = split(data.objects, currentMode.split);
6885             
6886                     var flag=false;
6887                     var startFlag=false;
6888                     var firstSelected = false;
6889                     for(var i=0; i<scope.rows.length; i++)
6890                     {
6891                         for(var j=0; j<scope.rows[i].length; j++)
6892                         {
6893                             if(!scope.rows[i][j].disabled && !firstSelected)
6894                             {
6895                                 firstSelected=true;
6896                                 var firstDay = scope.rows[i][j];
6897                             }
6898
6899                             if(scope.rows[i][j].selected)
6900                             {
6901                                 flag=true;
6902                                 break;
6903                             }                  
6904                         }
6905                         if(flag)
6906                         {
6907                             break;
6908                         }
6909                     }
6910                     if(!flag && firstSelected)
6911                     {
6912                        firstDay.firstFocus=true;
6913                     }
6914
6915                     scope.labels = data.labels || [];
6916                     scope.title = data.title;                    
6917                 }
6918             }
6919
6920             scope.select = function (date,$event) {
6921                 var dt = new Date(date.getFullYear(), date.getMonth(), date.getDate());
6922                 scope.currentDate = dt;
6923                 if (!scope.onSelectClose || (scope.onSelectClose && scope.onSelectClose({
6924                         date: dt
6925                     }) !== false)) {
6926                     if (angular.isNumber(scope.collapseWait)) {
6927                         $timeout(function () {
6928                             scope.isOpen = false;
6929                             toggleCalendar(scope.isOpen);
6930                         }, scope.collapseWait);
6931                     } else {
6932                         scope.isOpen = false;
6933                         toggleCalendar(scope.isOpen);
6934                     }
6935                 }
6936             };
6937
6938             scope.move = function (direction,$event) {
6939                 var step = MonthpickerCtrl.modes[mode].step;
6940                 selected.setDate(1);
6941                 selected.setMonth(selected.getMonth() + direction * (step.months || 0));
6942                 selected.setFullYear(selected.getFullYear() + direction * (step.years || 0));
6943                 refill();
6944                 scope.getFocus = true;
6945                 $timeout(function () {
6946                     if (attrs.inline === 'true') {
6947                         MonthpickerCtrl.focusNextPrev(element,false); 
6948                     }else{
6949                         MonthpickerCtrl.focusNextPrev(b2bMonthpickerPopupTemplate,false);
6950                     }
6951                 },100);
6952                 $event.preventDefault();
6953                 $event.stopPropagation();
6954             };
6955
6956             scope.$watch('currentDate', function (value) {
6957                 if (angular.isDefined(value) && value !== null) {
6958                     refill(value);
6959                 } else {
6960                     refill();
6961                 }
6962                 ngModel.$setViewValue(value);
6963             });
6964
6965             ngModel.$render = function () {
6966                 scope.currentDate = ngModel.$viewValue;
6967             };
6968
6969             var stringToDate = function (value) {
6970                 if (!isNaN(new Date(value))) {
6971                     value = new Date(value);
6972                 }
6973                 return value;
6974             };
6975             ngModel.$formatters.unshift(stringToDate);
6976         }
6977     };
6978 }])
6979
6980 .directive('b2bMonthpicker', ['$compile', '$log', 'b2bMonthpickerConfig', 'b2bMonthpickerService', function ($compile, $log, b2bMonthpickerConfig, b2bMonthpickerService) {
6981     return {
6982         restrict: 'A',
6983         scope: {
6984             disableDates: '&',
6985             onSelectClose: '&'
6986         },
6987         require: 'ngModel',
6988         controller: ['$scope', '$element', '$attrs', function (scope, elem, attr) {
6989             var dateFormatString = angular.isDefined(attr.dateFormat) ? scope.$parent.$eval(attr.dateFormat) : b2bMonthpickerConfig.dateFormat;
6990             var helperText = angular.isDefined(attr.helperText) ? scope.$parent.$eval(attr.helperText) : b2bMonthpickerConfig.helperText; 
6991             helperText = helperText.replace('$date', '{{dt | date : \'' + dateFormatString + '\'}}');
6992
6993             var descriptionText = angular.isDefined(attr.descriptionText) ? scope.$parent.$eval(attr.descriptionText) : b2bMonthpickerConfig.descriptionText;  
6994
6995
6996             var inline = false;
6997             if (elem.prop('nodeName') !== 'INPUT' && elem.prop('nodeName') !== 'A') {
6998                 inline = true;
6999             }
7000
7001             var selectedDateMessage = "";
7002             
7003             if (elem.prop('nodeName') !== 'A'){
7004                 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>';    
7005                 elem.attr('tabindex', '-1'); 
7006                 elem.attr('aria-hidden', 'true');  
7007                 elem.attr('readonly', 'true'); 
7008             }else{
7009                 selectedDateMessage = ''
7010                 elem.attr('aria-label', helperText);
7011             }
7012             
7013             var descriptionTextSpan = '<span class="offscreen-text" id="monthpicker-description'+scope.$id+'">'+descriptionText+'</span>';
7014             elem.removeAttr('b2b-Monthpicker');
7015             elem.removeAttr('ng-model');
7016             elem.removeAttr('ng-disabled');
7017             elem.addClass('Monthpicker-input');
7018             elem.attr('ng-model', 'dt');
7019             elem.attr('aria-describedby', 'monthpicker-description'+scope.$id);
7020             
7021             
7022             
7023             elem.attr('ng-disabled', 'ngDisabled');
7024             elem.attr('b2b-format-date', dateFormatString);
7025
7026             var wrapperElement = angular.element('<div></div>');
7027             wrapperElement.attr('b2b-Monthpicker-popup', '');
7028             wrapperElement.attr('ng-model', 'dt');
7029             if (inline) {
7030                 wrapperElement.attr('inline', inline);
7031             }
7032             if (elem.prop('nodeName') === 'A'){
7033                 wrapperElement.attr('link', true);
7034             }
7035             b2bMonthpickerService.setAttributes(attr, wrapperElement);
7036             b2bMonthpickerService.bindScope(attr, scope);
7037
7038             wrapperElement.html('');
7039             wrapperElement.append(selectedDateMessage);
7040             wrapperElement.append('');
7041             wrapperElement.append(descriptionTextSpan);
7042             wrapperElement.append('');
7043             wrapperElement.append(elem.prop('outerHTML'));
7044
7045             var elm = wrapperElement.prop('outerHTML');
7046             elm = $compile(elm)(scope);
7047             elem.replaceWith(elm);
7048         }],
7049         link: function (scope, elem, attr, ctrl) {
7050             if (!ctrl) {
7051                 $log.error("ng-model is required.");
7052                 return; // do nothing if no ng-model
7053             }
7054             
7055             scope.$watch('dt', function (value) {
7056                 ctrl.$setViewValue(value);
7057             });
7058             ctrl.$render = function () {
7059                 scope.dt = ctrl.$viewValue;
7060             };
7061         }
7062     };
7063 }])
7064
7065 .directive('b2bMonthpickerGroup', [function () {
7066     return {
7067         restrict: 'EA',
7068         controller: ['$scope', '$element', '$attrs', function (scope, elem, attr) {
7069             this.$$headers = [];
7070             this.$$footers = [];
7071             this.registerMonthpickerScope = function (MonthpickerScope) {
7072                 MonthpickerScope.headers = this.$$headers;
7073                 MonthpickerScope.footers = this.$$footers;
7074             };
7075         }],
7076         link: function (scope, elem, attr, ctrl) {}
7077     };
7078 }])
7079
7080 .directive('b2bFormatDate', ['dateFilter', function (dateFilter) {
7081     return {
7082         restrict: 'A',
7083         require: 'ngModel',
7084         link: function (scope, elem, attr, ctrl) {
7085             var b2bFormatDate = "";
7086             attr.$observe('b2bFormatDate', function (value) {
7087                 b2bFormatDate = value;
7088             });
7089             var dateToString = function (value) {
7090                 if (!isNaN(new Date(value))) {
7091                     return dateFilter(new Date(value), b2bFormatDate);
7092                 }
7093                 return value;
7094             };
7095             ctrl.$formatters.unshift(dateToString);
7096         }
7097     };
7098 }])
7099
7100 .directive('b2bMonthpickerHeader', [function () {
7101     return {
7102         restrict: 'EA',
7103         require: '^b2bMonthpickerGroup',
7104         transclude: true,
7105         replace: true,
7106         template: '',
7107         compile: function (elem, attr, transclude) {
7108             return function link(scope, elem, attr, ctrl) {
7109                 if (ctrl) {
7110                     ctrl.$$headers.push(transclude(scope, function () {}));
7111                 }
7112                 elem.remove();
7113             };
7114         }
7115     };
7116 }])
7117
7118 .directive('b2bMonthpickerFooter', [function () {
7119     return {
7120         restrict: 'EA',
7121         require: '^b2bMonthpickerGroup',
7122         transclude: true,
7123         replace: true,
7124         template: '',
7125         compile: function (elem, attr, transclude) {
7126             return function link(scope, elem, attr, ctrl) {
7127                 if (ctrl) {
7128                     ctrl.$$footers.push(transclude(scope, function () {}));
7129                 }
7130                 elem.remove();
7131             };
7132         }
7133     };
7134 }]);
7135 /**
7136  * @ngdoc directive
7137  * @name Navigation.att:multiLevelNavigation
7138  *
7139  * @description
7140  *  <file src="src/multiLevelNavigation/docs/readme.md" />
7141  *
7142  * @usage
7143  *       <div class="b2b-ml-nav">
7144  *          <ul role="tree">
7145  *             <li aria-label="{{child.name}}" tabindex="-1" b2b-ml-nav="{{child.type}}" role="treeitem" ng-repeat="child in treeStructure">
7146  *                  <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>
7147  *                      <!-- Below UL tag is RECURSIVE to generate n-childs -->
7148  *                      <ul role="group" ng-if="child.child">
7149  *                          <li aria-label="{{child.name}}" b2b-ml-nav="{{child.type}}" tabindex="-1" role="treeitem" ng-repeat="child in child.child">
7150  *                          <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>
7151  *                               <!-- RECURSIVE UL tag goes here -->
7152  *                          </li>
7153  *                      </ul>
7154  *             </li>
7155  *           </ul>
7156  *        </div>
7157  *
7158  * @example
7159  *  <section id="code">
7160         <example module="b2b.att">
7161             <file src="src/multiLevelNavigation/docs/demo.html" />
7162             <file src="src/multiLevelNavigation/docs/demo.js" />
7163        </example>
7164     </section>
7165  *
7166  */
7167 angular.module('b2b.att.multiLevelNavigation', ['b2b.att.utilities'])
7168     //directive b2bMlNav Test coverage 100% on 5/13
7169     .directive('b2bMlNav', ['keymap', function (keymap) {
7170         return {
7171             restrict: 'EA',
7172             link: function (scope, element) {
7173                 var rootE, parentE, upE, downE, lastE, homeE, endE;
7174                 //default root tree element tabindex set zero
7175                 if (element.parent().parent().hasClass('b2b-ml-nav') && (element[0].previousElementSibling === null)) {
7176                     element.attr('tabindex', 0);
7177                 }
7178                 //check root via class
7179                 var isRoot = function (elem) {
7180                         if (elem.parent().parent().eq(0).hasClass('b2b-ml-nav')) {
7181                             return true;
7182                         } else {
7183                             return false;
7184                         }
7185
7186                     }
7187                     //for any expandable tree item on click
7188                 var toggleState = function (e) {
7189                                     
7190                     if (angular.element(e.target).attr("b2b-ml-nav") !== "endNode") {
7191                         var eLink = element.find('a').eq(0);
7192                         if (eLink.hasClass('active')) {
7193                             eLink.removeClass('active');
7194                             eLink.parent().attr("aria-expanded", "false");
7195                             eLink.find('i').eq(0).removeClass('icon-primary-expanded');
7196                             eLink.find('i').eq(0).addClass('icon-primary-collapsed');
7197                         } else {
7198                             eLink.addClass('active');
7199                             eLink.parent().attr("aria-expanded", "true");
7200                             eLink.find('i').eq(0).removeClass('icon-primary-collapsed');
7201                             eLink.find('i').eq(0).addClass('icon-primary-expanded');
7202                         }
7203                     }
7204                 };
7205                 //function finds the main root-item from particular tree-group
7206                 var findRoot = function (elem) {
7207                     if (isRoot(elem)) {
7208                         rootE = elem;
7209                         return;
7210                     }
7211                     if (elem.attr("b2b-ml-nav") === "middleNode" || elem.attr("b2b-ml-nav") === "endNode") {
7212                         parentE = elem.parent().parent();
7213                     } else {
7214                         parentE = elem;
7215                     }
7216                     if (parentE.attr("b2b-ml-nav") === "rootNode") {
7217                         rootE = parentE;
7218                     } else {
7219                         findRoot(parentE);
7220                     }
7221                 };
7222                 //finds the last visible node of the previous tree-group
7223                 var findPreActive = function (elem) {
7224                     if (!(elem.hasClass("active"))) {
7225                         return;
7226                     } else {
7227                         var childElems = angular.element(elem[0].nextElementSibling.children);
7228                         lastE = angular.element(childElems[childElems.length - 1]);
7229                         if (lastE.attr("b2b-ml-nav") === "middleNode" && lastE.find('a').eq(0).hasClass('active')) {
7230                             findPreActive(lastE.find('a').eq(0));
7231                         }
7232                         upE = lastE;
7233                     }
7234                 };
7235                 //find above visible link
7236                 var findUp = function (elem) {
7237                     if (elem[0].previousElementSibling !== null) {
7238                         upE = angular.element(elem[0].previousElementSibling);
7239                     } else {
7240                         upE = elem.parent().parent();
7241                     }
7242                     if (isRoot(elem) || (upE.attr('b2b-ml-nav') === "middleNode" && upE[0] !== elem.parent().parent()[0])) {
7243                         findPreActive(upE.find('a').eq(0)); 
7244                     }
7245                 };
7246                 //find below visible link
7247                 var findDown = function (elem) {
7248                     if (elem.hasClass('active')) {
7249                         downE = elem.next().find('li').eq(0);
7250                     } else {
7251                         if (elem.parent().next().length !== 0) {
7252                             downE = elem.parent().next().eq(0);
7253                         } else {
7254                             if (elem.parent().parent().parent().next().length !== 0) {
7255                                 downE = elem.parent().parent().parent().next().eq(0);
7256                                 return;
7257                             }
7258                             downE = elem.parent().eq(0);
7259                         }
7260                     }
7261                 };
7262                 //finds last root-group element of the tree
7263                 var findEnd = function (elem) {
7264                     findRoot(elem);
7265                     endE = angular.element(rootE.parent()[0].children[rootE.parent()[0].children.length - 1]);
7266                 };
7267                 //finds first root element of tree
7268                 var findHome = function (elem) {
7269                     findRoot(elem);
7270                     homeE = angular.element(rootE.parent()[0].children[0]);
7271                 };
7272                 element.bind('click', function (e) {
7273                     if(element.attr("b2b-ml-nav") !== "endNode") { 
7274                         toggleState(e); 
7275                     }
7276                     if (rootE==undefined){
7277                         findRoot(element);
7278                     }
7279                     var currSelected = rootE.parent()[0].querySelector('.selected');
7280                     if(currSelected){
7281                         angular.element(currSelected).removeClass('selected');
7282                     }                    
7283                     element.find('a').eq(0).addClass('selected');
7284                     e.stopPropagation();
7285                 });
7286                 element.bind('focus', function (e) {
7287                     if(element.attr("b2b-ml-nav") !== "endNode") {
7288                         if(element.find('a').eq(0).hasClass('active')) {
7289                             element.attr("aria-expanded", true);
7290                         }
7291                         else {
7292                             element.attr("aria-expanded", false);
7293                         }
7294                         
7295                     }
7296                 })
7297                 //Keyboard functionality approach:
7298                 //find keycode
7299                 //set set tabindex -1 on the current focus element
7300                 //find the next element to be focussed, set tabindex 0 and throw focus
7301                 element.bind('keydown', function (evt) {
7302                     switch (evt.keyCode) {
7303                     case keymap.KEY.ENTER:
7304                     case keymap.KEY.SPACE:
7305                         element.triggerHandler('click');
7306                         evt.stopPropagation();
7307                         evt.preventDefault();
7308                         break;
7309                     case keymap.KEY.END:
7310                         evt.preventDefault();
7311                         element.attr('tabindex', -1);
7312                         findEnd(element);
7313                         endE.eq(0).attr('tabindex', 0);
7314                         endE[0].focus();
7315                         evt.stopPropagation();
7316                         break;
7317                     case keymap.KEY.HOME:
7318                         evt.preventDefault();
7319                         element.attr('tabindex', -1);
7320                         findHome(element);
7321                         homeE.eq(0).attr('tabindex', 0);
7322                         homeE[0].focus();
7323                         evt.stopPropagation();
7324                         break;
7325                     case keymap.KEY.LEFT:
7326                         evt.preventDefault();
7327                         if (!isRoot(element)) {
7328                             element.attr('tabindex', -1);
7329                             parentE = element.parent().parent();
7330                             parentE.eq(0).attr('tabindex', 0);
7331                             parentE[0].focus();
7332                             parentE.eq(0).triggerHandler('click');
7333                         } else {
7334                             if (element.find('a').eq(0).hasClass('active')) {
7335                                 element.triggerHandler('click');
7336                             }
7337                         }
7338                         evt.stopPropagation();
7339                         break;
7340                     case keymap.KEY.UP:
7341                         evt.preventDefault();
7342                         if (!(isRoot(element) && element[0].previousElementSibling === null)) {
7343                             element.attr('tabindex', -1);
7344                             findUp(element);
7345                             upE.eq(0).attr('tabindex', 0);
7346                             upE[0].focus();
7347                         }
7348                         evt.stopPropagation();
7349                         break;
7350                     case keymap.KEY.RIGHT:
7351                         evt.preventDefault();
7352                         if (element.attr("b2b-ml-nav") !== "endNode") {
7353                             if (!element.find('a').eq(0).hasClass('active')) {
7354                                 element.triggerHandler('click');
7355                             }
7356                             element.attr('tabindex', -1);
7357                             findDown(element.find('a').eq(0));
7358                             downE.eq(0).attr('tabindex', 0);
7359                             downE[0].focus();
7360                         }
7361                         evt.stopPropagation();
7362                         break;
7363                     case keymap.KEY.DOWN:
7364                         evt.preventDefault();
7365                         element.attr('tabindex', -1);
7366                         if (!(element.attr("b2b-ml-nav") === "middleNode" && element.find('a').eq(0).hasClass('active')) && (element.next().length === 0)) {
7367                             if(element.parent().parent().attr("b2b-ml-nav") !== "rootNode" && element.parent().parent()[0].nextElementSibling !== null)
7368                             {
7369                                 findDown(element.find('a').eq(0));
7370                                 downE.eq(0).attr('tabindex', 0);
7371                                 downE[0].focus();
7372                                 evt.stopPropagation();
7373                                 break;
7374                             }
7375                             findRoot(element);
7376                             if (!(rootE.next().length === 0)) {
7377                                 rootE.next().eq(0).attr('tabindex', 0);
7378                                 rootE.next()[0].focus();
7379                             } else {
7380                                 rootE.eq(0).attr('tabindex', 0);
7381                                 rootE[0].focus();
7382                             }
7383                             evt.stopPropagation();
7384                             break;
7385                         }
7386                         findDown(element.find('a').eq(0));
7387                         downE.eq(0).attr('tabindex', 0);
7388                         downE[0].focus();
7389                         evt.stopPropagation();
7390                         break;
7391                     default:
7392                         break;
7393                     }
7394                 });
7395             }
7396         };
7397     }]);
7398 /**
7399  * @ngdoc directive
7400  * @name Tabs, tables & accordions.att:multipurposeExpander
7401  *
7402  * @description
7403  *  <file src="src/multipurposeExpander/docs/readme.md" />
7404  *
7405  * @usage
7406  * <!--With Close Other -->
7407  * <b2b-expander-group close-others="true">
7408  *  <b2b-expanders class="mpc-expanders" is-open="testmpc">            
7409  *      <b2b-expander-heading ng-class=" { 'b2b-toggle-header-active': !testmpc, 'b2b-toggle-header-inactive': testmpc } ">Heading content goes here</b2b-expander-heading>               
7410  *      <b2b-expander-body>
7411             <p>body content goes here</p>
7412         </b2b-expander-body>
7413  *  </b2b-expanders>
7414  *  </b2b-expander-group>
7415  *  
7416  * <!-- Without Close Other -->
7417  *  <b2b-expanders class="mpc-expanders" is-open="testmpc2">            
7418  *      <b2b-expander-heading ng-class=" { 'b2b-toggle-header-active': !testmpc2, 'b2b-toggle-header-inactive': testmpc2 } ">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  *  
7424  * @example
7425  *  <section id="code">
7426         <example module="b2b.att.multipurposeExpander">
7427             <file src="src/multipurposeExpander/docs/demo.html" />
7428             <file src="src/multipurposeExpander/docs/demo.js" />
7429         </example>
7430     </section>
7431  *
7432  */
7433
7434 angular.module('b2b.att.multipurposeExpander', ['b2b.att', 'b2b.att.collapse'])
7435 .directive('b2bExpanderGroup', function () {
7436     return {
7437         restrict: 'EA',
7438         transclude: true,
7439         template: "<ng-transclude></ng-transclude>",
7440         controller:['$scope','$attrs', function($scope,$attrs){
7441             this.groups = [];
7442             this.index = -1;            
7443             this.scope = $scope;
7444             
7445             this.addGroup = function (groupScope) {
7446                 var that = this;
7447                 groupScope.index = this.groups.length;
7448                 this.groups.push(groupScope);
7449                 if(this.groups.length > 0){
7450                     this.index = 0;
7451                 }
7452                 groupScope.$on('$destroy', function () {
7453                 that.removeGroup(groupScope);
7454             });
7455             };
7456
7457             this.closeOthers = function (openGroup) {
7458                 var closeOthers = angular.isDefined($attrs.closeOthers);
7459                 if (closeOthers && !$scope.forceExpand) {
7460                     angular.forEach(this.groups, function (group) {
7461                         if (group !== openGroup) {
7462                             group.isOpen = false;
7463                         }
7464                     });
7465                 }
7466                 if (this.groups.indexOf(openGroup) === (this.groups.length - 1) && $scope.forceExpand) {
7467                     $scope.forceExpand = false;
7468                 }
7469             };
7470             this.removeGroup = function (group) {
7471             var index = this.groups.indexOf(group);
7472             if (index !== -1) {
7473                 this.groups.splice(this.groups.indexOf(group), 1);
7474             }
7475         };
7476         }]
7477        
7478     };
7479     
7480 })
7481 .directive('b2bExpanders', function () {
7482     return{
7483         restrict: 'EA',
7484         replace: true,
7485         require:['b2bExpanders','?^b2bExpanderGroup'],
7486         transclude: true,
7487         scope:{isOpen:'=?'},
7488         template: "<div ng-transclude></div>",
7489         controller: ['$scope', function ($scope){
7490                 var bodyScope = null;
7491                 var expanderScope = null;
7492                 this.isOpened = function(){                
7493                     if($scope.isOpen)
7494                     {
7495                         return  true;
7496                     }else
7497                     {
7498                         return false;
7499                     }
7500                 };                
7501                 this.setScope = function (scope) {
7502                     bodyScope = scope; 
7503                     bodyScope.isOpen = $scope.isOpen;                   
7504                 };                
7505                 this.setExpanderScope = function (scope) {
7506                     expanderScope = scope;                                   
7507                 };
7508                 this.toggle = function () {
7509                     $scope.isOpen = bodyScope.isOpen = !bodyScope.isOpen;                    
7510                     return bodyScope.isOpen;
7511                     
7512                 };
7513                 this.watchToggle = function(io){ 
7514                     bodyScope.isOpen = io;
7515                     expanderScope.updateIcons(io);
7516                 };  
7517             }],
7518         link: function (scope, elem, attr, myCtrl)
7519         {
7520             //scope.isOpen = false; 
7521             if(myCtrl[1]){
7522                 myCtrl[1].addGroup(scope);
7523             }
7524             scope.$watch('isOpen', function(val){                               
7525                 myCtrl[0].watchToggle(scope.isOpen);
7526                 if(val && myCtrl[1]){
7527                     myCtrl[1].closeOthers(scope);
7528                 }
7529             });            
7530         }
7531     };
7532 })
7533
7534 .directive('b2bExpanderHeading', function () {
7535     return{
7536         require: "^b2bExpanders",
7537         restrict: 'EA',
7538         replace: true,
7539         transclude: true,
7540         scope: true,
7541         template: "<div style='padding:10px 10px 10px 0 !important' ng-transclude></div>"
7542     };
7543 })
7544
7545 .directive('b2bExpanderBody', function () {
7546     return{
7547         restrict: 'EA',
7548         require: "^b2bExpanders",
7549         replace: true,
7550         transclude: true,
7551         scope: {},
7552         template: "<div b2b-collapse='!isOpen' ><div ng-transclude></div></div>",
7553         link: function (scope, elem, attr, myCtrl) {
7554             scope.isOpen = false;
7555             myCtrl.setScope(scope);
7556         }
7557     };
7558 })
7559
7560 .directive('b2bExpanderToggle', function () {
7561     return{
7562         restrict: 'EA',
7563         require: "^b2bExpanders",
7564         scope: {
7565             expandIcon: '@',
7566             collapseIcon: '@'
7567         },
7568         
7569         link: function (scope, element, attr, myCtrl)
7570         {
7571             myCtrl.setExpanderScope(scope);
7572             var isOpen = myCtrl.isOpened();   
7573
7574             scope.setIcon = function () {
7575                 element.attr("role", "tab");
7576
7577                 if (scope.expandIcon && scope.collapseIcon)
7578                 {
7579                     if (isOpen) {
7580                         element.removeClass(scope.expandIcon);
7581                         element.addClass(scope.collapseIcon);
7582
7583                         element.attr("aria-expanded", "true");
7584                     }
7585                     else {
7586                         element.removeClass(scope.collapseIcon);
7587                         element.addClass(scope.expandIcon);
7588
7589                         element.attr("aria-expanded", "false");
7590                     }
7591                 }                               
7592             };
7593             
7594             element.bind('click', function (){
7595                 scope.toggleit();
7596             });
7597             scope.updateIcons = function(nStat){
7598                 isOpen = nStat;
7599                 scope.setIcon();                
7600             };
7601             scope.toggleit = function (){
7602                 isOpen = myCtrl.toggle();
7603                 scope.setIcon();
7604                 scope.$apply();
7605             };                    
7606             scope.setIcon();
7607         }
7608     };
7609 });
7610 /**
7611  * @ngdoc directive
7612  * @name Messages, modals & alerts.att:notesMessagesAndErrors
7613  *
7614  * @description
7615  *  <file src="src/notesMessagesAndErrors/docs/readme.md" />
7616  *
7617  * @usage
7618  *  See Demo
7619  *
7620  * @example
7621  *  <section id="code">
7622         <example module="b2b.att">
7623             <file src="src/notesMessagesAndErrors/docs/demo.html" />
7624             <file src="src/notesMessagesAndErrors/docs/demo.js" />
7625        </example>
7626         </section>
7627  *
7628  */
7629 angular.module('b2b.att.notesMessagesAndErrors', []);
7630 /** 
7631  * @ngdoc directive 
7632  * @name Template.att:Notification Card
7633  * 
7634  * @description 
7635  *  <file src="src/notificationCardTemplate/docs/readme.md" /> 
7636  * 
7637  * @example 
7638  *  <section id="code"> 
7639         <b>HTML + AngularJS</b> 
7640         <example module="b2b.att"> 
7641             <file src="src/notificationCardTemplate/docs/demo.html" /> 
7642             <file src="src/notificationCardTemplate/docs/demo.js" /> 
7643        </example> 
7644     </section>    
7645  * 
7646  */
7647 angular.module('b2b.att.notificationCardTemplate', [])
7648   
7649 /** 
7650  * @ngdoc directive 
7651  * @name Template.att:Order Confirmation Template
7652  * 
7653  * @description 
7654  *  <file src="src/orderConfirmationTemplate/docs/readme.md" /> 
7655  * 
7656  * @example 
7657  *  <section id="code"> 
7658         <b>HTML + AngularJS</b> 
7659         <example module="b2b.att"> 
7660             <file src="src/orderConfirmationTemplate/docs/demo.html" /> 
7661             <file src="src/orderConfirmationTemplate/docs/demo.js" /> 
7662        </example> 
7663     </section>    
7664  * 
7665  */
7666 angular.module('b2b.att.orderConfirmationTemplate', []);
7667   
7668 /**
7669  * @ngdoc directive
7670  * @name Navigation.att:pagination
7671  *
7672  * @description
7673  *  <file src="src/pagination/docs/readme.md" />
7674  *
7675  * @usage
7676  *   <div b2b-pagination="" input-id="goto-page-2" total-pages="totalPages1" current-page="currentPage1" click-handler="customHandler" show-input="showInput"></div> 
7677  *
7678  * @example
7679  *  <section id="code">
7680         <example module="b2b.att">
7681             <file src="src/pagination/docs/demo.html" />
7682             <file src="src/pagination/docs/demo.js" />
7683        </example>
7684         </section>
7685  *
7686  */
7687 angular.module('b2b.att.pagination', ['b2b.att.utilities', 'ngTouch'])
7688     .directive('b2bPagination', ['b2bUserAgent', 'keymap', '$window', '$timeout', function (b2bUserAgent, keymap, $window, $timeout) {
7689         return {
7690             restrict: 'A',
7691             scope: {
7692                 totalPages: '=',
7693                 currentPage: '=',
7694                 showInput: '=',
7695                 clickHandler: '=?',
7696                 inputId: '='
7697             },
7698             replace: true,
7699             templateUrl: 'b2bTemplate/pagination/b2b-pagination.html',
7700             link: function (scope, elem) {
7701                 scope.isMobile = b2bUserAgent.isMobile();
7702                 scope.notMobile = b2bUserAgent.notMobile();
7703                 scope.focusedPage;
7704                 scope.meanVal = 3;
7705                 scope.$watch('totalPages', function (value) {
7706                     if (angular.isDefined(value) && value !== null) {
7707                         scope.pages = [];
7708                         if (value < 1) {
7709                             scope.totalPages = 1;
7710                             return;
7711                         }
7712                         if (value <= 10) {
7713                             for (var i = 1; i <= value; i++) {
7714                                 scope.pages.push(i);
7715                             }
7716                         } else if (value > 10) {
7717                             var midVal = Math.ceil(value / 2);
7718                             scope.pages = [midVal - 2, midVal - 1, midVal, midVal + 1, midVal + 2];
7719                         }
7720                         if(scope.currentPage === undefined || scope.currentPage === 1)
7721                         {
7722                             currentPageChanged(1);
7723                         }
7724                     }
7725                 });
7726                 scope.$watch('currentPage', function (value) {
7727                     currentPageChanged(value);
7728                     callbackHandler(value);
7729                 });
7730                 var callbackHandler = function (num) {
7731                     if (angular.isFunction(scope.clickHandler)) {
7732                         scope.clickHandler(num);
7733                     }
7734                 };
7735
7736                 function currentPageChanged(value) {
7737                     if (angular.isDefined(value) && value !== null) {
7738                         if (!value || value < 1) {
7739                             value = 1;
7740                         }
7741                         if (value > scope.totalPages) {
7742                             value = scope.totalPages;
7743                         }
7744                         if (scope.currentPage !== value) {
7745                             scope.currentPage = value;
7746                             callbackHandler(scope.currentPage);
7747                         }
7748                         if (scope.totalPages > 10) {
7749                             var val = parseInt(value);
7750                             if (val <= 6) {
7751                                 scope.pages = [1, 2, 3, 4, 5, 6, 7, 8];
7752                             } else if (val > 6 && val <= scope.totalPages - 5) {
7753                                 scope.pages = [val - 1, val, val + 1];
7754                             } else if (val >= scope.totalPages - 5) {
7755                                 scope.pages = [scope.totalPages - 7, scope.totalPages - 6, scope.totalPages - 5, scope.totalPages - 4, scope.totalPages - 3, scope.totalPages - 2, scope.totalPages - 1, scope.totalPages];
7756                             }
7757                         }
7758                         if (scope.isMobile) {
7759                             var inWidth = $window.innerWidth;
7760                             var viewLimit = 7;
7761                             if (inWidth <= 400) {
7762                                 viewLimit = 7;
7763                             } else if (inWidth > 400 && inWidth < 500) {
7764                                 viewLimit = 9;
7765                             } else if (inWidth >= 500 && inWidth < 600) {
7766                                 viewLimit = 11;
7767                             } else if (inWidth >= 600 && inWidth < 700) {
7768                                 viewLimit = 13;
7769                             } else if (inWidth >= 700 && inWidth < 800) {
7770                                 viewLimit = 15;
7771                             }
7772
7773                             var val = parseInt(value);
7774
7775                             scope.meanVal = Math.floor(viewLimit / 2);
7776                             var lowerLimit = (val - scope.meanVal) < 1 ? 1 : val - scope.meanVal;
7777                             var upperLimit = (lowerLimit + viewLimit - 1) > scope.totalPages ? scope.totalPages : lowerLimit + viewLimit - 1;
7778                             scope.pages = [];
7779                             for (var i = lowerLimit; i <= upperLimit; i++) {
7780                                 scope.pages.push(i);
7781                             }
7782                         }
7783                     }
7784                 }
7785                 scope.gotoKeyClick = function (keyEvent) {
7786                   if (keyEvent.which === keymap.KEY.ENTER) {
7787                     scope.gotoBtnClick()
7788                   }
7789                 }
7790                 scope.gotoBtnClick = function () {
7791                     currentPageChanged(parseInt(scope.gotoPage)); 
7792                     callbackHandler(scope.currentPage);
7793                     var qResult = elem[0].querySelector('button');
7794                     angular.element(qResult).attr('disabled','true');
7795                     $timeout(function(){
7796                         elem[0].querySelector('.b2b-pager__item--active').focus();
7797                     }, 50); 
7798                     scope.gotoPage = null;               
7799                 }
7800                 scope.onfocusIn = function(evt)
7801                 {
7802                     var qResult = elem[0].querySelector('button');
7803                     angular.element(qResult).removeAttr('disabled');
7804                 }
7805                 scope.onfocusOut = function(evt)
7806                 {
7807                     if(evt.target.value === "")
7808                     {
7809                         var qResult = elem[0].querySelector('button');
7810                         angular.element(qResult).attr('disabled','true');
7811                     }                    
7812                 }
7813                 scope.next = function (event) {
7814                     if (event != undefined) {
7815                         event.preventDefault();
7816                     }
7817                     if (scope.currentPage < scope.totalPages) {
7818                         scope.currentPage += 1;
7819                         callbackHandler(scope.currentPage);
7820                     }
7821                 };
7822                 scope.prev = function (event) {
7823                     if (event != undefined) {
7824                         event.preventDefault();
7825                     }
7826                     if (scope.currentPage > 1) {
7827                         scope.currentPage -= 1;
7828                         callbackHandler(scope.currentPage);
7829                     }
7830                 };
7831                 scope.selectPage = function (value, event) {
7832                     event.preventDefault();
7833                     scope.currentPage = value;
7834                     scope.focusedPage = value;
7835                     callbackHandler(scope.currentPage);
7836                 };
7837                 scope.checkSelectedPage = function (value) {
7838                     if (scope.currentPage === value) {
7839                         return true;
7840                     }
7841                     return false;
7842                 };
7843                 scope.isFocused = function (page) {
7844                     return scope.focusedPage === page;
7845                 };
7846             }
7847         };
7848     }]);
7849 /**
7850  * @ngdoc directive
7851  * @name Navigation.att:paneSelector
7852  *
7853  * @description
7854  *  <file src="src/paneSelector/docs/readme.md" />
7855  *
7856  * @usage
7857  *  Please refer demo.html tab in Example section below.
7858  *
7859  * @example
7860     <section id="code">
7861         <b>HTML + AngularJS</b>
7862         <example module="b2b.att">
7863             <file src="src/paneSelector/docs/demo.html" />
7864             <file src="src/paneSelector/docs/demo.js" />
7865         </example>
7866     </section>
7867  */
7868
7869 angular.module('b2b.att.paneSelector', ['b2b.att.tabs', 'b2b.att.utilities'])
7870
7871 .filter('paneSelectorSelectedItemsFilter', [function () {
7872     return function (listOfItemsArray) {
7873
7874         if (!listOfItemsArray) {
7875             listOfItemsArray = [];
7876         }
7877
7878         var returnArray = [];
7879
7880         for (var i = 0; i < listOfItemsArray.length; i++) {
7881             if (listOfItemsArray[i].isSelected) {
7882                 returnArray.push(listOfItemsArray[i]);
7883             }
7884         }
7885
7886         return returnArray;
7887     };
7888 }])
7889
7890 .filter('paneSelectorFetchChildItemsFilter', [function () {
7891     return function (listOfItemsArray) {
7892
7893         if (!listOfItemsArray) {
7894             listOfItemsArray = [];
7895         }
7896
7897         var returnArray = [];
7898
7899         for (var i = 0; i < listOfItemsArray.length; i++) {
7900             for (var j = 0; j < listOfItemsArray[i].childItems.length; j++) {
7901                 returnArray.push(listOfItemsArray[i].childItems[j]);
7902             }
7903         }
7904
7905         return returnArray;
7906     };
7907 }])
7908
7909 .directive('b2bPaneSelector', [function () {
7910     return {
7911         restrict: 'AE',
7912         replace: true,
7913         templateUrl: 'b2bTemplate/paneSelector/paneSelector.html',
7914         transclude: true,
7915         scope: {}
7916     };
7917 }])
7918
7919 .directive('b2bPaneSelectorPane', [ function () {
7920     return {
7921         restrict: 'AE',
7922         replace: true,
7923         templateUrl: 'b2bTemplate/paneSelector/paneSelectorPane.html',
7924         transclude: true,
7925         scope: {}
7926     };
7927 }])
7928
7929 .directive('b2bTabVertical', ['$timeout', 'keymap', function ($timeout, keymap) {
7930     return {
7931         restrict: 'A',
7932         require: '^b2bTab',
7933         link: function (scope, element, attr, b2bTabCtrl) {
7934
7935             if (!b2bTabCtrl) {
7936                 return;
7937             }
7938
7939             // retreive the isolateScope
7940             var iScope = angular.element(element).isolateScope();
7941
7942             $timeout(function () {
7943                 angular.element(element[0].querySelector('a')).unbind('keydown');
7944                 angular.element(element[0].querySelector('a')).bind('keydown', function (evt) {
7945
7946                     if (!(evt.keyCode)) {
7947                         evt.keyCode = evt.which;
7948                     }
7949
7950                     switch (evt.keyCode) {
7951                         case keymap.KEY.DOWN:
7952                             evt.preventDefault();
7953                             iScope.nextKey();
7954                             break;
7955
7956                         case keymap.KEY.UP:
7957                             evt.preventDefault();
7958                             iScope.previousKey();
7959                             break;
7960
7961                         default:;
7962                     }
7963                 });
7964             });
7965         }
7966     };
7967 }]);
7968 /**
7969  * @ngdoc directive
7970  * @name Forms.att:phoneNumberInput
7971  *
7972  * @description
7973  *  <file src="src/phoneNumberInput/docs/readme.md" />
7974  *
7975  * @usage
7976 <form name="userForm1">
7977     <div class="form-row" ng-class="{'error':!userForm1.text.$valid && userForm1.text.$dirty}">
7978         <label>Phone Mask<span style="color:red">*</span>: (npa) nxx-line &nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Model Value: {{mask.text}}</label>
7979         <div>
7980             <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 />
7981             <div ng-messages="userForm1.text.$error" class="error-msg" aria-live="polite" aria-atomic="true">
7982                 <span ng-message="required" role="alert">This field is mandatory!</span>
7983                 <span ng-message="invalidPhoneNumber" role="alert">Please enter valid phone number!</span>
7984                 <span ng-message="mask" role="alert">Please enter valid phone number!</span>
7985             </div>
7986         </div>
7987     </div>
7988 </form>
7989  *
7990  * @example
7991  *  <section id="code">
7992         <example module="b2b.att">
7993             <file src="src/phoneNumberInput/docs/demo.html" />
7994             <file src="src/phoneNumberInput/docs/demo.js" />
7995        </example>
7996     </section>
7997  *
7998  */
7999 angular.module('b2b.att.phoneNumberInput', ['ngMessages', 'b2b.att.utilities'])
8000     .constant("CoreFormsUiConfig", {
8001         phoneMask: '(___) ___-____',
8002         phoneMaskDot: '___.___.____',
8003         phoneMaskHyphen: '___-___-____'
8004     })
8005     .directive('b2bPhoneMask', ['$parse', 'CoreFormsUiConfig', 'keymap', 'b2bUserAgent', function ($parse, CoreFormsUiConfig, keymap, b2bUserAgent) {
8006         return {
8007             require: 'ngModel',
8008             scope: {
8009                 ngModel: '='
8010             },
8011             link: function (scope, iElement, iAttrs, ctrl) {
8012                 
8013                 var mask = '';
8014                 var validPhoneNumber = false;
8015                 var currentKey = '';
8016                 if (b2bUserAgent.isMobile()) {
8017                     mask = "__________";
8018                 } else {
8019                     switch (iAttrs.b2bPhoneMask) {
8020                     case "phoneMask":
8021                         mask = CoreFormsUiConfig.phoneMask;
8022                         break;
8023                     case "phoneMaskDot":
8024                         mask = CoreFormsUiConfig.phoneMaskDot;
8025                         break;
8026                     case "phoneMaskHyphen":
8027                         mask = CoreFormsUiConfig.phoneMaskHyphen;
8028                         break;
8029                     default:
8030                         mask = CoreFormsUiConfig.phoneMask;
8031                     }
8032                 }
8033                 iElement.attr("maxlength", mask.length);
8034                 var checkValidity = function (unmaskedValue, rawValue) {
8035                     var valid = false;
8036                     if (angular.isUndefined(rawValue) || rawValue === '') {
8037                         valid = true;
8038                     } else if (unmaskedValue) {
8039                         valid = (unmaskedValue.length === 10);
8040                     }
8041                     ctrl.$setValidity('invalidPhoneNumber', validPhoneNumber);
8042                     ctrl.$setValidity('mask', valid);
8043                     return valid;
8044                 };
8045                 var handleKeyup = function (evt) {
8046
8047                     if (evt && evt.keyCode === keymap.KEY.SHIFT) {
8048                         return;
8049                     }
8050
8051                     var index, formattedNumber;
8052                     if (ctrl.$modelValue) {
8053                         formattedNumber = ctrl.$modelValue;
8054                     } else {
8055                         formattedNumber = iElement.val();
8056                     }
8057                     if (!formattedNumber.length && currentKey === '') {
8058                         return;
8059                     }
8060                     var maskLength, inputNumbers, maskArray, tempArray, maskArrayLength;
8061                     tempArray = [];
8062                     maskArray = mask.split("");
8063                     maskArrayLength = maskArray.length;
8064                     maskLength = formattedNumber.substring(0, mask.length);
8065                     inputNumbers = formattedNumber.replace(/[^0-9]/g, "").split("");
8066                     for (index = 0; index < maskArrayLength; index++) {
8067                         tempArray.push(maskArray[index] === "_" ? inputNumbers.shift() : maskArray[index]);
8068                         if (inputNumbers.length === 0) {
8069                             break;
8070                         }
8071                     }
8072                     formattedNumber = tempArray.join("");
8073                     if (formattedNumber === '(') {
8074                         formattedNumber = '';
8075                     }
8076
8077                     if ( (angular.isDefined(evt) && evt.which) && currentKey !== '') {
8078                         if (maskArray[0] === currentKey && formattedNumber === '') {
8079                             formattedNumber = '(';
8080                         } else if (evt.which === keymap.KEY.SPACE && currentKey === ' ') {
8081                             formattedNumber = formattedNumber + ') ';
8082                         } else if (maskArray[0] === currentKey && formattedNumber === '') {
8083                             formattedNumber = formattedNumber + currentKey;
8084                         } else if (maskArray[formattedNumber.length] === currentKey) {
8085                             formattedNumber = formattedNumber + currentKey;
8086                         }
8087                         currentKey = '';
8088                     }
8089
8090                     ctrl.$setViewValue(formattedNumber);
8091                     ctrl.$render();
8092                     return formattedNumber;
8093                 };
8094
8095
8096                 // since we are only allowing 0-9, why even let the keypress go forward?
8097                 // also added in delete... in case they want to delete :)
8098                 var handlePress = function (e) {
8099                     if (e.which) {
8100                         if ((e.which < 48 || e.which > 57) && (e.which < 96 || e.which > 105)) {
8101                             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 &&
8102                                 // Allow: Ctrl+V/v
8103                                 (!(e.ctrlKey) && (e.which !== '118' || e.which !== '86')) &&
8104                                 // Allow: Ctrl+C/c
8105                                 (!(e.ctrlKey) && (e.which !== '99' || e.which !== '67')) &&
8106                                 // Allow: Ctrl+X/x
8107                                 (!(e.ctrlKey) && (e.which !== '120' || e.which !== '88')) &&
8108                                 /* 229 key code will sent as placeholder key for andriod devices */
8109                                 (e.which != 229 )) {
8110                                 e.preventDefault ? e.preventDefault() : e.returnValue = false;
8111                                 validPhoneNumber = false;
8112                             }
8113                         } else {
8114                             validPhoneNumber = true;
8115                         }
8116
8117                         setCurrentKey(e);
8118                     }
8119                     scope.$apply();
8120                 };
8121                 // i moved this out because i thought i might need focus as well..
8122                 // to handle setting the model as the view changes
8123                 var parser = function (fromViewValue) {
8124                     var letters = /^[A-Za-z]+$/;
8125                     var numbers = /^[0-9]+$/;
8126                     if (angular.isUndefined(fromViewValue) || fromViewValue === '') {
8127                         validPhoneNumber = true;
8128                     } else {
8129                         if (fromViewValue.match(letters)) {
8130                             validPhoneNumber = false;
8131                         }
8132                         if (fromViewValue.match(numbers)) {
8133                             validPhoneNumber = true;
8134                         }
8135                     }
8136                     var clean = "";
8137                     if (fromViewValue && fromViewValue.length > 0) {
8138                         clean = fromViewValue.replace(/[^0-9]/g, '');
8139                     }
8140                     checkValidity(clean, fromViewValue);
8141                     return clean;
8142                 };
8143
8144                 //to handle reading the model and formatting it
8145                 var formatter = function (fromModelView) {
8146                     var input = '';
8147                     checkValidity(fromModelView);
8148                     if (fromModelView) {
8149                         input = handleKeyup();
8150                     }
8151                     return input;
8152                 };
8153
8154                 var setCurrentKey = function (e) {
8155                     switch (e.which) {
8156                     case 189:
8157                     case 109:
8158                         currentKey = '-';
8159                         break;
8160                     case 190:
8161                     case 110:
8162                         currentKey = '.';
8163                         break;
8164                     case 57:
8165                         if (e.shiftKey === true) {
8166                             currentKey = '(';
8167                         }
8168                         break;
8169                     case 48:
8170                         if (e.shiftKey === true) {
8171                             currentKey = ')';
8172                         }
8173                         break;
8174                     case 32:
8175                         currentKey = ' ';
8176                         break;
8177                     }
8178                 };
8179
8180                 if (angular.isDefined(scope.ngModel)) {
8181                     parser(scope.ngModel);
8182                 }
8183
8184                 ctrl.$parsers.push(parser);
8185                 ctrl.$formatters.push(formatter);
8186                 iElement.bind('keyup', handleKeyup);
8187                 iElement.bind('keydown', handlePress);
8188             }
8189         };
8190 }]);
8191 /** 
8192  * @ngdoc directive 
8193  * @name Template.att:Profile Blocks 
8194  * 
8195  * @description 
8196  *  <file src="src/profileBlockTemplate/docs/readme.md" /> 
8197  * @example 
8198  *  <section id="code">  
8199         <example module="b2b.att"> 
8200             <file src="src/profileBlockTemplate/docs/demo.html" /> 
8201             <file src="src/profileBlockTemplate/docs/demo.js" /> 
8202        </example>  
8203     </section>  
8204  *  
8205  */   
8206
8207 angular.module('b2b.att.profileBlockTemplate', [])
8208     
8209      
8210   
8211 /**
8212  * @ngdoc directive
8213  * @name Layouts.att:profileCard
8214  *
8215  * @description
8216  *  <file src="src/profileCard/docs/readme.md" />
8217  *
8218  * @usage
8219  *  <b2b-profile-card></b2b-profile-card>
8220  *
8221  * @example
8222     <section id="code">   
8223         <example module="b2b.att">
8224             <file src="src/profileCard/docs/demo.html" />
8225             <file src="src/profileCard/docs/demo.js" />
8226         </example>
8227     </section>
8228  */
8229
8230 angular.module('b2b.att.profileCard', ['b2b.att'])
8231 .constant('profileStatus',{
8232     status: {
8233         ACTIVE: {
8234             status: "Active",
8235             color: "green"
8236         },
8237         DEACTIVATED: {
8238             status: "Deactivated",
8239             color: "red"
8240         },
8241         LOCKED: {
8242             status: "Locked",
8243             color: "red"
8244         },
8245         IDLE: {
8246             status: "Idle",
8247             color: "yellow"
8248         },
8249         PENDING: {
8250             status: "Pending",
8251             color: "blue"
8252         }
8253     },
8254     role: "COMPANY ADMINISTRATOR"
8255
8256 })
8257 .directive('b2bProfileCard',['$http','$q','profileStatus', function($http,$q,profileStatus) {
8258    return {
8259         restrict: 'EA',
8260         replace: 'true',
8261         templateUrl: function(element, attrs){
8262             if(!attrs.addUser){
8263                 return 'b2bTemplate/profileCard/profileCard.html';
8264             }
8265             else{
8266                 return 'b2bTemplate/profileCard/profileCard-addUser.html';
8267             }
8268         },
8269         scope: {
8270             profile:'=',
8271             characterLimit: '@'
8272         },
8273         link: function(scope, elem, attr){
8274             scope.characterLimit = parseInt(attr.characterLimit, 10) || 25;
8275             scope.shouldClip = function(str) {
8276                 return str.length > scope.characterLimit;
8277             };
8278
8279             scope.showEmailTooltip = false;
8280
8281             scope.image=true;
8282             function isImage(src) {
8283                 var deferred = $q.defer();
8284                 var image = new Image();
8285                 image.onerror = function() {
8286                     deferred.reject(false);
8287                 };
8288                 image.onload = function() {
8289                     deferred.resolve(true);
8290                 };
8291                 if(src !== undefined && src.length>0 ){
8292                     image.src = src;
8293                 } else {
8294                      deferred.reject(false);
8295                 }
8296                 return deferred.promise;
8297             }
8298             if(!attr.addUser){
8299             scope.image=false;
8300             isImage(scope.profile.img).then(function(img) {
8301                 scope.image=img;
8302             });
8303             var splitName=(scope.profile.name).split(' ');
8304             scope.initials='';
8305             for(var i=0;i<splitName.length;i++){
8306                 scope.initials += splitName[i][0];
8307             }
8308             if(scope.profile.role.toUpperCase() === profileStatus.role){
8309                 scope.badge=true;
8310             }
8311             var profileState=profileStatus.status[scope.profile.state.toUpperCase()];
8312             if(profileState) {
8313                 scope.profile.state=profileStatus.status[scope.profile.state.toUpperCase()].status;
8314                 scope.colorIcon=profileStatus.status[scope.profile.state.toUpperCase()].color;
8315                 if(scope.profile.state.toUpperCase()===profileStatus.status.PENDING.status.toUpperCase()||scope.profile.state.toUpperCase()===profileStatus.status.LOCKED.status.toUpperCase()){
8316                         scope.profile.lastLogin=scope.profile.state;
8317                 }
8318             }
8319             var today=new Date().getTime();
8320             var lastlogin=new Date(scope.profile.lastLogin).getTime();
8321             var diff=(today-lastlogin)/(1000*60*60*24);
8322             if(diff<=1){
8323                 scope.profile.lastLogin="Today";
8324             }
8325             else if(diff<=2){
8326                 scope.profile.lastLogin="Yesterday";
8327             }
8328         }
8329     }
8330 };
8331 }]);
8332 /**
8333  * @ngdoc directive
8334  * @name Forms.att:radios
8335  *
8336  * @description
8337  *  <file src="src/radios/docs/readme.md" />
8338  *
8339  * @usage
8340  *  See demo section
8341  *
8342  * @param {boolean} refreshRadioGroup - A trigger that recalculates and updates the accessibility roles on radios in a group.
8343  * 
8344  * @example
8345     <section id="code">
8346                <b>HTML + AngularJS</b>
8347                <example module="b2b.att">
8348                 <file src="src/radios/docs/demo.html" />
8349                 <file src="src/radios/docs/demo.js" />
8350                </example>
8351             </section>
8352  */
8353 angular.module('b2b.att.radios', ['b2b.att.utilities'])
8354 .directive('b2bRadioGroupAccessibility', ['$timeout', 'b2bUserAgent', function($timeout, b2bUserAgent) {
8355     return {
8356         restrict: "A",
8357         scope: {
8358             refreshRadioGroup: "=",
8359         },
8360         link: function(scope, ele, attr) {
8361
8362             var roleRadioElement, radioProductSelectElement, radioInputTypeElement;
8363
8364             $timeout(calculateNumberOfRadio);
8365
8366             scope.$watch('refreshRadioGroup', function(value) {
8367                 if (value === true) {
8368                     addingRoleAttribute();
8369                     $timeout(calculateNumberOfRadio);
8370                     scope.refreshRadioGroup = false;
8371                 } else {
8372                     return;
8373                 }
8374             })
8375
8376
8377             function calculateNumberOfRadio() {
8378                 roleRadioElement = ele[0].querySelectorAll('[role="radio"]');
8379
8380                 radioProductSelectElement = ele[0].querySelectorAll('[role="radiogroup"] li.radio-box');
8381
8382                 radioInputTypeElement = ele[0].querySelectorAll('input[type="radio"]');
8383
8384                 for (var i = 0; i < radioInputTypeElement.length; i++) {
8385                     var isChecked = radioInputTypeElement[i].checked ? 'true' : 'false';
8386                     var isDisabled = radioInputTypeElement[i].disabled ? 'true' : 'false';
8387                     var numOfx = i + 1 + ' of ' + radioInputTypeElement.length;
8388                     angular.element(roleRadioElement[i]).attr({
8389                         'aria-checked': isChecked,
8390                         'aria-disabled': isDisabled,
8391                         'data-opNum': numOfx
8392                     });
8393                     if (b2bUserAgent.notMobile()) {
8394                         angular.element(roleRadioElement[i]).removeAttr("role");
8395                     }
8396
8397                     if (radioProductSelectElement.length) {
8398                         isChecked === 'true' ? angular.element(radioProductSelectElement[i]).addClass('active') : angular.element(radioProductSelectElement[i]).removeClass('active');
8399                     }
8400
8401                     if (/Android/i.test(navigator.userAgent)) {
8402                         angular.element(roleRadioElement[i]).append('<span class="hidden-spoken">. ' + numOfx + '.</span>');
8403                     }
8404
8405
8406                     angular.element(radioInputTypeElement[i]).bind('click', radioStateChangeonClick);
8407
8408                 }
8409             }
8410
8411             function addingRoleAttribute() {
8412                 for (var i = 0; i < radioInputTypeElement.length; i++) {
8413                     if (b2bUserAgent.notMobile()) {
8414                         angular.element(roleRadioElement[i]).attr("role", "radio");
8415                     }
8416                 }
8417             }
8418
8419             function radioStateChangeonClick() {
8420                 for (var i = 0; i < radioInputTypeElement.length; i++) {
8421                     var isChecked = radioInputTypeElement[i].checked ? 'true' : 'false';
8422                     var isDisabled = radioInputTypeElement[i].disabled ? 'true' : 'false';
8423                     if (radioProductSelectElement.length) {
8424                         isChecked === 'true' ? angular.element(radioProductSelectElement[i]).addClass('active') : angular.element(radioProductSelectElement[i]).removeClass('active');
8425                     }
8426                     angular.element(roleRadioElement[i]).attr({
8427                         'aria-checked': isChecked,
8428                         'aria-disabled': isDisabled
8429                     });
8430                 }
8431
8432             }
8433         }
8434     }
8435
8436 }]);
8437
8438 /**
8439  * @ngdoc directive
8440  * @name Forms.att:searchField
8441  *
8442  * @description
8443  *  <file src="src/searchField/docs/readme.md" />
8444  *
8445  * @usage
8446  *  <div b2b-search-field dropdown-list="listdata" on-click-callback="clickFn(value)" class="span12" input-model='typedString' config-obj='keyConfigObj'></div>
8447  *
8448  * @example
8449  <section id="code">
8450     <example module="b2b.att">
8451     <file src="src/searchField/docs/demo.html" />
8452     <file src="src/searchField/docs/demo.js" />
8453     </example>
8454 </section>
8455  */
8456
8457 angular.module('b2b.att.searchField', ['b2b.att.utilities', 'b2b.att.position'])
8458     .filter('b2bFilterInput', [function() {
8459         return function(list, str, keyArray, displayListKey, isContainsSearch, searchSeperator) {
8460             var res = [];
8461             var searchLabel;
8462             var searchCondition;
8463             var conditionCheck = function(searchSeperator, listItem, displayListKey, splitString) {
8464                 var displayTitle = null;
8465                 if (splitString) {
8466                     for (var i = 0; i < displayListKey.length; i++) {
8467                         if (i <= 0) {
8468                             displayTitle = listItem[displayListKey[i]].toLowerCase().indexOf(splitString[i].toLowerCase()) > -1;
8469                         } else {
8470                             displayTitle = (splitString[i]) ? displayTitle && listItem[displayListKey[i]].toLowerCase().indexOf(splitString[i].toLowerCase().trim()) > -1 : displayTitle;
8471                         }
8472                     }
8473                 } else {
8474                     angular.forEach(displayListKey, function(value) {
8475                         if (!displayTitle) {
8476                             displayTitle = listItem[value];
8477                         } else {
8478                             displayTitle = displayTitle + (listItem[value] ? searchSeperator + ' ' + listItem[value] : '');
8479                         }
8480                     });
8481                 }
8482                 return displayTitle;
8483             }
8484             angular.forEach(list, function(listItem) {
8485                 var splitString = str.indexOf(searchSeperator) > -1 ? str.split(searchSeperator) : false;
8486                 var displayList = conditionCheck(searchSeperator, listItem, displayListKey, splitString)
8487                 for (var i = 0; i < keyArray.length; i++) {
8488                     searchLabel = keyArray[i];
8489                     if (listItem[searchLabel]) {
8490                         if (isContainsSearch) {
8491                             var displaySearchList = listItem[searchLabel].toLowerCase().indexOf(str.toLowerCase()) > -1;
8492                             if (splitString.length > 1) {
8493                                 displaySearchList = (splitString.length <= keyArray.length) ? displayList : false;
8494                             }
8495                             searchCondition = displaySearchList;
8496                         } else {
8497                             searchCondition = listItem[searchLabel].match(new RegExp('^' + str, 'gi'));
8498                         }
8499                         if (searchCondition) {
8500                             res.push({
8501                                 'title': conditionCheck(searchSeperator, listItem, displayListKey),
8502                                 'valueObj': listItem
8503                             });
8504                             break;
8505                         }
8506                     }
8507                 }
8508             });
8509             return res;
8510         };
8511     }]).directive('b2bSearchField', ['$filter', 'b2bFilterInputFilter', 'keymap', '$documentBind', '$isElement', '$document', 'events', '$timeout', function($filter, b2bFilterInput, keymap, $documentBind, $isElement, $document, events, $timeout) {
8512         return {
8513             restrict: 'A',
8514             scope: {
8515                 dataList: '=dropdownList',
8516                 onClickCallback: '&',
8517                 inputModel: '=',
8518                 configObj: '=',
8519                 objModel: '=',
8520                 inputDeny: '=?',
8521                 disabled: '=?'
8522             },
8523             replace: true,
8524             templateUrl: 'b2bTemplate/searchField/searchField.html',
8525             controller: ['$scope', function($scope) {
8526                 this.searchKeyArray = [];
8527                 if ($scope.configObj.searchKeys) {
8528                     this.searchKeyArray = $scope.configObj.searchKeys;
8529                 }
8530                 if (angular.isUndefined($scope.disabled)) {
8531                     $scope.disabled = false;
8532                 }
8533                 this.triggerInput = function(searchString) {
8534                     $scope.originalInputModel = searchString;
8535                     if (searchString === '') {
8536                         $scope.currentIndex = -1;
8537                         $scope.filterList = [];
8538                         $scope.showListFlag = false;
8539                     } else if (searchString !== '' && !$scope.isFilterEnabled) {
8540                         $scope.filterList = $filter('b2bFilterInput')($scope.dataList, searchString, this.searchKeyArray, $scope.configObj.displayListDataKey, $scope.configObj.isContainsSearch, $scope.configObj.searchSeperator);
8541                         $scope.showListFlag = true;
8542                     }
8543                 };
8544                 this.denyRegex = function() {
8545                     return $scope.inputDeny;
8546                 };
8547             }],
8548             link: function(scope, elem) {
8549                 scope.isFilterEnabled = false;
8550                 scope.showListFlag = false;
8551                 scope.currentIndex = -1;
8552                 scope.setCurrentIdx = function(idx) {
8553                     scope.currentIndex = idx;
8554                     if (idx > -1) {
8555                         scope.inputModel = scope.filterList[idx].title;
8556                         scope.objModel = scope.filterList[idx];
8557                     }
8558                 };
8559                 scope.isActive = function(index, dropdownLength) {
8560                     scope.dropdownLength = dropdownLength;
8561                     return scope.currentIndex === index;
8562                 };
8563                 scope.selectItem = function(idx) {
8564                     scope.setCurrentIdx(idx);
8565                     scope.onClickCallback({
8566                         value: scope.inputModel,
8567                         objValue: scope.objModel
8568                     });
8569                     scope.showListFlag = false;
8570                     $timeout(function() {
8571                         elem.find('div').find('input')[0].focus();
8572                     }, 150);
8573                 };
8574                 scope.startSearch = function() {
8575                     scope.onClickCallback({
8576                         value: scope.inputModel,
8577                         objValue: scope.objModel
8578                     });
8579                 };
8580                 scope.selectPrev = function() {
8581                     if (scope.currentIndex > 0 && scope.filterList.length > 0) {
8582                         scope.currentIndex = scope.currentIndex - 1;
8583                         scope.setCurrentIdx(scope.currentIndex);
8584                     } else if (scope.currentIndex === 0) {
8585                         scope.currentIndex = scope.currentIndex - 1;
8586                         scope.inputModel = scope.originalInputModel;
8587                         scope.isFilterEnabled = true;
8588                     }
8589                 };
8590                 scope.selectNext = function() {
8591                     if (scope.currentIndex < scope.configObj.noOfItemsDisplay - 1) {
8592                         if (scope.currentIndex < scope.filterList.length - 1) {
8593                             scope.currentIndex = scope.currentIndex + 1;
8594                             scope.setCurrentIdx(scope.currentIndex);
8595                         }
8596                     }
8597                 };
8598                 scope.selectCurrent = function() {
8599                     scope.selectItem(scope.currentIndex);
8600                 };
8601                 scope.selectionIndex = function(e) {
8602                     switch (e.keyCode) {
8603                         case keymap.KEY.DOWN:
8604                             events.preventDefault(e);
8605                             scope.isFilterEnabled = true;
8606                             scope.selectNext();
8607                             break;
8608                         case keymap.KEY.UP:
8609                             events.preventDefault(e);
8610                             scope.isFilterEnabled = true;
8611                             scope.selectPrev();
8612                             break;
8613                         case keymap.KEY.ENTER:
8614                             events.preventDefault(e);
8615                             scope.isFilterEnabled = true;
8616                             scope.selectCurrent();
8617                             break;
8618                         case keymap.KEY.ESC:
8619                             events.preventDefault(e);
8620                             scope.isFilterEnabled = false;
8621                             scope.showListFlag = false;
8622                             scope.inputModel = '';
8623                             break;
8624                         default:
8625                             scope.isFilterEnabled = false;
8626                             break;
8627                     }
8628                     if (elem[0].querySelector('.filtercontainer')) {
8629                         elem[0].querySelector('.filtercontainer').scrollTop = (scope.currentIndex - 1) * 35;
8630                     }
8631                 };
8632                 scope.$watch('filterList', function(newVal, oldVal) {
8633                     if (newVal !== oldVal) {
8634                         scope.currentIndex = -1;
8635                     }
8636                 });
8637                 scope.blurInput = function() {
8638                     $timeout(function() {
8639                         scope.showListFlag = false;
8640                     }, 150);
8641                 };
8642                 var outsideClick = function(e) {
8643                     var isElement = $isElement(angular.element(e.target), elem.find('ul').eq(0), $document);
8644                     if (!isElement && document.activeElement.tagName.toLowerCase() !== 'input') {
8645                         scope.showListFlag = false;
8646                         scope.$apply();
8647                     }
8648                 };
8649                 $documentBind.click('showListFlag', outsideClick, scope);
8650             }
8651         };
8652     }])
8653     .directive('b2bSearchInput', [function() {
8654         return {
8655             restrict: 'A',
8656             require: ['ngModel', '^b2bSearchField'],
8657             link: function(scope, elem, attr, ctrl) {
8658                 var ngModelCtrl = ctrl[0];
8659                 var attSearchBarCtrl = ctrl[1];
8660                 var REGEX = ctrl[1].denyRegex();
8661                 var parser = function(viewValue) {
8662                     attSearchBarCtrl.triggerInput(viewValue);
8663                     return viewValue;
8664                 };
8665                 ngModelCtrl.$parsers.push(parser);
8666
8667                 if (REGEX !== undefined || REGEX !== '') {
8668                     elem.bind('input', function() {
8669                         var inputString = ngModelCtrl.$viewValue && ngModelCtrl.$viewValue.replace(REGEX, '');
8670                         if (inputString !== ngModelCtrl.$viewValue) {
8671                             ngModelCtrl.$setViewValue(inputString);
8672                             ngModelCtrl.$render();
8673                             scope.$apply();
8674                         }
8675                     });
8676                 }
8677             }
8678         };
8679     }]);
8680
8681 /**
8682  * @ngdoc directive
8683  * @name Buttons, links & UI controls.att:Seek bar
8684  *
8685  * @description
8686  *  <file src="src/seekBar/docs/readme.md" />
8687  *
8688  * @usage
8689  *  Horizontal Seek Bar
8690  *  <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>
8691
8692  *  Vertical Seek Bar
8693  *  <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>
8694  *
8695  * @example
8696     <section id="code">   
8697         <b>HTML + AngularJS</b>
8698         <example module="b2b.att">
8699             <file src="src/seekBar/docs/demo.html" />
8700             <file src="src/seekBar/docs/demo.js" />
8701         </example>
8702     </section>
8703  */
8704
8705 angular.module('b2b.att.seekBar', ['b2b.att.utilities','b2b.att.position'])
8706         .constant('b2bSeekBarConfig', {
8707             'min': 0,
8708             'max': 100,
8709             'step': 1,
8710             'skipInterval': 1
8711         })
8712         .directive('b2bSeekBar', ['$documentBind', 'events', 'b2bSeekBarConfig', 'keymap', '$compile', function($documentBind, events, b2bSeekBarConfig, keymap, $compile) {
8713                 return {
8714                     restrict: 'AE',
8715                     replace: true,
8716                     require: 'ngModel',
8717                     templateUrl: 'b2bTemplate/seekBar/seekBar.html',
8718                     scope: {
8719                         onDragEnd: '&?',
8720                         onDragInit: '&?'
8721                     },
8722                     link: function(scope, elm, attr, ngModelCtrl) {
8723                         scope.isDragging = false;
8724                         scope.verticalSeekBar = false;
8725                         var min;
8726                         var max;
8727                         var step = b2bSeekBarConfig.step;
8728                         var skipInterval = b2bSeekBarConfig.skipInterval;
8729                         var knob = angular.element(elm[0].querySelector('.b2b-seek-bar-knob-container'));
8730                         var seekBarKnob = angular.element(knob[0].querySelector('.b2b-seek-bar-knob'));
8731                         var trackContainer = angular.element(elm[0].querySelector('.b2b-seek-bar-track-container'));
8732                         var trackFill = angular.element(elm[0].querySelector('.b2b-seek-bar-track-fill'));
8733                         var trackContainerRect = {};
8734                         var axisPosition;
8735                         var trackFillOrderPositioning;
8736
8737                         if (angular.isDefined(attr.vertical)) {
8738                             scope.verticalSeekBar = true;
8739                             axisPosition = "clientY";
8740                         }
8741                         else {
8742                             scope.verticalSeekBar = false;
8743                             axisPosition = "clientX";
8744                         }
8745                         var getValidStep = function(val) {
8746                             val = parseFloat(val);
8747                             // in case $modelValue came in string number
8748                             if (angular.isNumber(val)) {
8749                                 val = Math.round((val - min) / step) * step + min;
8750                                 return Math.round(val * 1000) / 1000;
8751                             }
8752                         };
8753
8754                         var getPositionToPercent = function(x) {
8755                             if (scope.verticalSeekBar) {
8756                                 return Math.max(0, Math.min(1, (trackContainerRect.bottom - x) / (trackFillOrderPositioning)));
8757                             }
8758                             else {
8759                                 return Math.max(0, Math.min(1, (x - trackContainerRect.left) / (trackFillOrderPositioning)));
8760                             }
8761                         };
8762
8763                         var getPercentToValue = function(percent) {
8764                             return (min + percent * (max - min));
8765                         };
8766
8767                         var getValueToPercent = function(val) {
8768                             return (val - min) / (max - min);
8769                         };
8770
8771                         var getValidMinMax = function(val) {
8772                             return Math.max(min, Math.min(max, val));
8773                         };
8774
8775                         var updateTrackContainerRect = function() {
8776                             trackContainerRect = trackContainer[0].getBoundingClientRect();
8777                             if (scope.verticalSeekBar) {
8778                                 if (!trackContainerRect.height) {
8779                                     trackFillOrderPositioning = trackContainer[0].scrollHeight;
8780                                 } else {
8781                                     trackFillOrderPositioning = trackContainerRect.height;
8782                                 }
8783                             }
8784                             else {
8785                                 if (!trackContainerRect.width) {
8786                                     trackFillOrderPositioning = trackContainer[0].scrollWidth;
8787                                 } else {
8788                                     trackFillOrderPositioning = trackContainerRect.width;
8789                                 }
8790
8791                             }
8792
8793                         };
8794
8795                         var updateKnobPosition = function(percent) {
8796                             var percentStr = (percent * 100) + '%';
8797                             if (scope.verticalSeekBar) {
8798                                 knob.css('bottom', percentStr);
8799                                 trackFill.css('height', percentStr);
8800                             }
8801                             else {
8802                                 knob.css('left', percentStr);
8803                                 trackFill.css('width', percentStr);
8804                             }
8805                         };
8806
8807                         var modelRenderer = function() {
8808                             if (isNaN(ngModelCtrl.$viewValue)) {
8809                                 ngModelCtrl.$viewValue = ngModelCtrl.$modelValue || min;
8810                             }
8811
8812                             var viewVal = ngModelCtrl.$viewValue;
8813                             scope.currentModelValue = viewVal;
8814
8815                             //wait for min, max and step to be set then only update UI to avoid NaN on percent calculation
8816                             if ((min || min === 0) && max && step) {
8817                                 updateKnobPosition(getValueToPercent(viewVal));
8818                             }
8819                         };
8820
8821                         var setModelValue = function(val) {
8822                             scope.currentModelValue = getValidMinMax(getValidStep(val));
8823                             ngModelCtrl.$setViewValue(scope.currentModelValue);
8824                         };
8825
8826                         var updateMin = function(val) {
8827                             min = parseFloat(val);
8828                             if(isNaN(min)){
8829                                min = b2bSeekBarConfig.min; 
8830                             }
8831                             modelRenderer();
8832                         };
8833
8834                         var updateMax = function(val) {
8835                             max = parseFloat(val);
8836                             if(isNaN(max)){
8837                                max = b2bSeekBarConfig.max; 
8838                             }
8839                             modelRenderer();
8840                         };
8841
8842                         var updateStep = function(val) {
8843                             step = parseFloat(val);
8844                             if (!attr['skipInterval']) {
8845                                 skipInterval = step;
8846                             }
8847                         };
8848
8849                         var updateSkipInterval = function(val) {
8850                             skipInterval = step * Math.ceil(val / (step!==0?step:1));
8851                         };
8852
8853                         angular.isDefined(attr.min) ? attr.$observe('min', updateMin) : updateMin(b2bSeekBarConfig.min);
8854                         angular.isDefined(attr.max) ? attr.$observe('max', updateMax) : updateMax(b2bSeekBarConfig.max);
8855                         if (angular.isDefined(attr.step)) {
8856                             attr.$observe('step', updateStep);
8857                         }
8858                         if (angular.isDefined(attr.skipInterval)) {
8859                             attr.$observe('skipInterval', updateSkipInterval);
8860                         }
8861                         scope.currentModelValue = getValidMinMax(getValidStep(min));
8862                         var onMouseDown = function(e) {
8863                             switch (e.which) {
8864                                 case 1:
8865                                     // left mouse button
8866                                     break;
8867                                 case 2:
8868                                 case 3:
8869                                     // right or middle mouse button
8870                                     return;
8871                             }
8872                             ;
8873
8874                             scope.isDragging = true;
8875                             seekBarKnob[0].focus();
8876                             updateTrackContainerRect();
8877                             if (attr['onDragInit']) {
8878                                 scope.onDragInit();
8879                             }
8880                             events.stopPropagation(e);
8881                             events.preventDefault(e);
8882                              scope.$apply(function() {
8883                                 setModelValue(getPercentToValue(getPositionToPercent(e[axisPosition])));
8884                             });
8885                         };
8886
8887                         var onMouseUp = function() {
8888
8889                             if (attr['onDragEnd']) {
8890                                 scope.onDragEnd();
8891                             }
8892                             scope.isDragging = false;
8893                             scope.$digest();
8894                         };
8895
8896                         var onMouseMove = function(e) {
8897                             if (scope.isDragging) {
8898                                 events.stopPropagation(e);
8899                                 events.preventDefault(e);
8900
8901                                 scope.$apply(function() {
8902                                     setModelValue(getPercentToValue(getPositionToPercent(e[axisPosition])));
8903                                 });
8904                             }
8905                         };
8906
8907                         function onKeyDown(e) {
8908                             if (!(e.keyCode)) {
8909                                 e.keyCode = e.which;
8910                             }
8911                             var updateStep;
8912                             switch (e.keyCode) {
8913                                 case keymap.KEY.LEFT:
8914                                     if (!scope.verticalSeekBar) {
8915                                         updateStep = -skipInterval;
8916                                     }
8917                                     break;
8918                                 case keymap.KEY.RIGHT:
8919                                     if (!scope.verticalSeekBar) {
8920                                         updateStep = skipInterval;
8921                                     }
8922                                     break;
8923                                 case keymap.KEY.UP:
8924                                     if (scope.verticalSeekBar) {
8925                                         updateStep = skipInterval;
8926                                     }
8927                                     break;
8928                                 case keymap.KEY.DOWN:
8929                                     if (scope.verticalSeekBar) {
8930                                         updateStep = -skipInterval;
8931                                     }
8932                                     break;
8933                                 default:
8934                                     return;
8935                             }
8936
8937                             if (updateStep) {
8938                                 events.stopPropagation(e);
8939                                 events.preventDefault(e);
8940                                 scope.$apply(function() {
8941                                     setModelValue(ngModelCtrl.$viewValue + updateStep);
8942                                 });
8943                                 if (attr['onDragEnd']) {
8944                                 scope.onDragEnd();
8945                             }
8946                             }
8947                         }
8948
8949                         elm.on('keydown', onKeyDown);
8950                         elm.on('mousedown', onMouseDown);
8951
8952                         $documentBind.event('mousemove', 'isDragging', onMouseMove, scope, true, 0);
8953                         $documentBind.event('mouseup', 'isDragging', onMouseUp, scope, true, 0);
8954
8955                         ngModelCtrl.$render = function() {
8956                             if (!scope.isDragging) {
8957                                 modelRenderer();
8958                             }
8959                         };
8960                         ngModelCtrl.$viewChangeListeners.push(modelRenderer);
8961                         ngModelCtrl.$formatters.push(getValidMinMax);
8962                         ngModelCtrl.$formatters.push(getValidStep);
8963                     }
8964                 };
8965             }]);
8966 /**
8967  * @ngdoc directive
8968  * @name Layouts.att:separators
8969  *
8970  * @description
8971  *  <file src="src/separators/docs/readme.md" />
8972  *
8973  * @usage
8974
8975  *
8976  * @example
8977  *  <section id="code">   
8978  <b>HTML + AngularJS</b>
8979  <example module="b2b.att">
8980  <file src="src/separators/docs/demo.html" />
8981  <file src="src/separators/docs/demo.js" />
8982  </example>
8983  </section>
8984  *
8985  */
8986
8987 angular.module('b2b.att.separators', []);
8988 /**
8989  * @ngdoc directive
8990  * @name Buttons, links & UI controls.att:slider
8991  *
8992  * @description
8993  *  <file src="src/slider/docs/readme.md" />
8994  *
8995  * @usage
8996  *  <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>
8997  *
8998  * @example
8999     <section id="code">   
9000         <b>HTML + AngularJS</b>
9001         <example module="b2b.att">
9002             <file src="src/slider/docs/demo.html" />
9003             <file src="src/slider/docs/demo.js" />
9004         </example>
9005     </section>
9006  */
9007
9008 angular.module('b2b.att.slider', ['b2b.att.utilities'])
9009         .constant('SliderConfig', {
9010             'min': 0,
9011             'max': 100,
9012             'step': 1,
9013             'skipInterval': 1
9014         })
9015         .directive('b2bSlider', ['$documentBind', 'SliderConfig', 'keymap', '$compile', '$log', function($documentBind, SliderConfig, keymap, $compile, $log) {
9016                 return {
9017                     restrict: 'AE',
9018                     replace: true,
9019                     require: 'ngModel',
9020                     templateUrl: 'b2bTemplate/slider/slider.html',
9021                     scope: {
9022                         onDragEnd: '&?',
9023                         onDragInit: '&?',
9024                         trackFillColor: '=?',
9025                         preAriaLabel: '=?',
9026                         postAriaLabel: '=?',
9027                         onRenderEnd: '&?',
9028                         sliderSnapPoints: '=?',
9029                         customAriaLabel: '=?',
9030                         labelId: '@?'
9031                     },
9032                     link: function(scope, elm, attr, ngModelCtrl) {
9033                         scope.isDragging = false;
9034                         scope.verticalSlider = false;
9035                         var min;
9036                         var max;
9037                         var step = SliderConfig.step;
9038                         var skipInterval = SliderConfig.skipInterval;
9039                         var knob = angular.element(elm[0].querySelector('.slider-knob-container'));
9040                         var sliderKnob = angular.element(knob[0].querySelector('.slider-knob'));
9041                         var trackContainer = angular.element(elm[0].querySelector('.slider-track-container'));
9042                         var trackFill = angular.element(elm[0].querySelector('.slider-track-fill'));
9043                         var trackContainerRect = {};
9044                         var axisPosition = "clientX";
9045                         var trackFillOrderPositioning;
9046
9047                         //Forcefully disabling the vertical Slider code.
9048                         if (angular.isDefined(attr.vertical)) {
9049                             scope.verticalSlider = true;
9050                             axisPosition = "clientY";
9051                         }
9052
9053                         if (angular.isDefined(scope.noAriaLabel) && scope.noAriaLabel !== '') {
9054                             $log.warn('no-aria-label has been deprecated. This will be removed in v0.6.0.');
9055                         }
9056                         if (angular.isDefined(scope.preAriaLabel) && scope.preAriaLabel !== '') {
9057                             $log.warn('pre-aria-label has been deprecated. Please use label-id instead. This will be removed in v0.6.0.');
9058                         }
9059                         if (angular.isDefined(scope.customAriaLabel) && scope.customAriaLabel !== '') {
9060                             $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.');
9061                         }
9062
9063                         var binarySearchNearest = function (num, arr) {
9064                             var mid;
9065                             var lo = 0;
9066                             var hi = arr.length - 1;
9067                             
9068                             while (hi - lo > 1) {
9069                                 mid = Math.floor((lo + hi) / 2);
9070                                 if (arr[mid] < num) {
9071                                     lo = mid;
9072                                 } else {
9073                                     hi = mid;
9074                                 }
9075                             }
9076                             if (num - arr[lo] < arr[hi] - num) {
9077                                 return arr[lo];
9078                             }
9079                             return arr[hi];
9080                         };
9081                         
9082                         var getValidStep = function(val) {
9083                             val = parseFloat(val);
9084                             // in case $modelValue came in string number
9085                             if (!isNaN(val)) {
9086                                 
9087                                 if(attr['sliderSnapPoints'] && scope.sliderSnapPoints.length > 0) {
9088                                     val = binarySearchNearest(val, scope.sliderSnapPoints);
9089                                 }
9090                                 else {
9091                                     val = Math.round((val - min) / step) * step + min;
9092                                 }
9093                                 
9094                                 return Math.round(val * 1000) / 1000;
9095                             }
9096                         };
9097
9098                         var getPositionToPercent = function(x) {
9099                             if (scope.verticalSlider) {
9100                                 return Math.max(0, Math.min(1, (trackContainerRect.bottom - x) / (trackFillOrderPositioning)));
9101                             }
9102                             else {
9103                                 return Math.max(0, Math.min(1, (x - trackContainerRect.left) / (trackFillOrderPositioning)));
9104                             }
9105                         };
9106
9107                         var getPercentToValue = function(percent) {
9108                             return (min + percent * (max - min));
9109                         };
9110
9111                         var getValueToPercent = function(val) {
9112                             return (val - min) / (max - min);
9113                         };
9114
9115                         var getValidMinMax = function(val) {
9116                             return Math.max(min, Math.min(max, val));
9117                         };
9118
9119                         var updateTrackContainerRect = function() {
9120                             trackContainerRect = trackContainer[0].getBoundingClientRect();
9121                             if (scope.verticalSlider) {
9122                                 if (!trackContainerRect.height) {
9123                                     trackFillOrderPositioning = trackContainer[0].scrollHeight;
9124                                 } else {
9125                                     trackFillOrderPositioning = trackContainerRect.height;
9126                                 }
9127                             }
9128                             else {
9129                                 if (!trackContainerRect.width) {
9130                                     trackFillOrderPositioning = trackContainer[0].scrollWidth;
9131                                 } else {
9132                                     trackFillOrderPositioning = trackContainerRect.width;
9133                                 }
9134
9135                             }
9136
9137                         };
9138
9139                         var updateKnobPosition = function(percent) {
9140                             var percentStr = (percent * 100) + '%';
9141                             if (scope.verticalSlider) {
9142                                 knob.css('bottom', percentStr);
9143                                 trackFill.css('height', percentStr);
9144                             }
9145                             else {
9146                                 knob.css('left', percentStr);
9147                                 trackFill.css('width', percentStr);
9148                             }
9149                         };
9150
9151                         var modelRenderer = function() {
9152
9153                             if(attr['disabled']){
9154                                 return;
9155                             }
9156
9157                             if (isNaN(ngModelCtrl.$viewValue)) {
9158                                 ngModelCtrl.$viewValue = ngModelCtrl.$modelValue || min;
9159                             }
9160
9161                             var viewVal = ngModelCtrl.$viewValue;
9162                             scope.currentModelValue = viewVal;
9163
9164                             //wait for min, max and step to be set then only update UI to avoid NaN on percent calculation
9165                             if ((min || min === 0) && max && step) {
9166                                 updateKnobPosition(getValueToPercent(viewVal));
9167                             }
9168                         };
9169
9170                         var setModelValue = function(val) {
9171                             scope.currentModelValue = getValidMinMax(getValidStep(val));
9172                             ngModelCtrl.$setViewValue(scope.currentModelValue);
9173                         };
9174
9175                         var updateMin = function(val) {
9176                             min = parseFloat(val);
9177                             if(isNaN(min)){
9178                                min = SliderConfig.min; 
9179                             }
9180                             scope.min = min;
9181                             modelRenderer();
9182                         };
9183
9184                         var updateMax = function(val) {
9185                             max = parseFloat(val);
9186                             if(isNaN(max)){
9187                                max = SliderConfig.max; 
9188                             }
9189                             scope.max = max;
9190                             modelRenderer();
9191                         };
9192
9193                         var updateStep = function(val) {
9194                             step = parseFloat(val);
9195                             if (!attr['skipInterval']) {
9196                                 skipInterval = step;
9197                             }
9198                         };
9199
9200                         var updateSkipInterval = function(val) {
9201                             skipInterval = step * Math.ceil(val / (step!==0?step:1));
9202                         };
9203
9204                         angular.isDefined(attr.min) ? attr.$observe('min', updateMin) : updateMin(SliderConfig.min);
9205                         angular.isDefined(attr.max) ? attr.$observe('max', updateMax) : updateMax(SliderConfig.max);
9206                         if (angular.isDefined(attr.step)) {
9207                             attr.$observe('step', updateStep);
9208                         }
9209                         if (angular.isDefined(attr.skipInterval)) {
9210                             attr.$observe('skipInterval', updateSkipInterval);
9211                         }
9212                         scope.currentModelValue = getValidMinMax(getValidStep(min));
9213                         var onMouseDown = function(e) {
9214
9215                             if(attr['disabled']){
9216                                 return;
9217                             }
9218
9219                             switch (e.which) {
9220                                 case 1:
9221                                     // left mouse button
9222                                     break;
9223                                 case 2:
9224                                 case 3:
9225                                     // right or middle mouse button
9226                                     return;
9227                             }
9228
9229                             scope.isDragging = true;
9230                             sliderKnob[0].focus();
9231                             updateTrackContainerRect();
9232                             if (attr['onDragInit']) {
9233                                 scope.onDragInit();
9234                             }
9235                             e.stopPropagation();
9236                             e.preventDefault();
9237                              scope.$apply(function() {
9238                                 setModelValue(getPercentToValue(getPositionToPercent(e[axisPosition])));
9239                             });
9240                         };
9241
9242                         var onMouseUp = function() {
9243
9244                             if (attr['onDragEnd']) {
9245                                 scope.onDragEnd();
9246                             }
9247                             scope.isDragging = false;
9248                             scope.$digest();
9249                         };
9250
9251                         var onMouseMove = function(e) {
9252                             if (scope.isDragging) {
9253                                 e.stopPropagation();
9254                                 e.preventDefault();
9255
9256                                 scope.$apply(function() {
9257                                     setModelValue(getPercentToValue(getPositionToPercent(e[axisPosition])));
9258                                 });
9259                             }
9260                         };
9261
9262                         function onKeyDown(e) {
9263                             if (!(e.keyCode)) {
9264                                 e.keyCode = e.which;
9265                             }
9266                             var updateStep;
9267                             switch (e.keyCode) {
9268                                 case keymap.KEY.DOWN:
9269                                 case keymap.KEY.LEFT:
9270                                     if(attr['sliderSnapPoints'] && scope.sliderSnapPoints.length > 0) {
9271                                         var currentIndex = scope.sliderSnapPoints.indexOf(ngModelCtrl.$viewValue);
9272                                         if (currentIndex > 0) {
9273                                             currentIndex--;
9274                                         }
9275                                         updateStep = scope.sliderSnapPoints[currentIndex];
9276                                     }
9277                                     else {
9278                                         updateStep = ngModelCtrl.$viewValue - skipInterval;
9279                                     }
9280                                     break;
9281                                 case keymap.KEY.UP:
9282                                 case keymap.KEY.RIGHT:
9283                                     if(attr['sliderSnapPoints'] && scope.sliderSnapPoints.length > 0) {
9284                                         var currentIndex = scope.sliderSnapPoints.indexOf(ngModelCtrl.$viewValue);
9285                                         if (currentIndex < scope.sliderSnapPoints.length-1) {
9286                                             currentIndex++;
9287                                         }
9288                                         updateStep = scope.sliderSnapPoints[currentIndex];
9289                                     }
9290                                     else {
9291                                         updateStep = ngModelCtrl.$viewValue + skipInterval;
9292                                     }
9293                                     break;
9294                                 case keymap.KEY.END:
9295                                     if(attr['sliderSnapPoints'] && scope.sliderSnapPoints.length > 0) {
9296                                         currentIndex = scope.sliderSnapPoints.length-1;
9297                                         updateStep = scope.sliderSnapPoints[currentIndex];
9298                                     } else {
9299                                         setModelValue(scope.max);
9300                                     }
9301                                     e.preventDefault();
9302                                     e.stopPropagation();
9303                                     break;
9304                                 case keymap.KEY.HOME:
9305                                     if(attr['sliderSnapPoints'] && scope.sliderSnapPoints.length > 0) {
9306                                         currentIndex = 0;
9307                                         updateStep = scope.sliderSnapPoints[currentIndex];
9308                                     } else {
9309                                         setModelValue(scope.min);
9310                                     }
9311                                     e.preventDefault();
9312                                     e.stopPropagation();
9313                                     break;
9314                                 default:
9315                                     return;
9316                             }
9317
9318                             if (angular.isNumber(updateStep) && !attr['disabled']) {
9319                                 e.stopPropagation();
9320                                 e.preventDefault();
9321                                 scope.$apply(function() {
9322                                     setModelValue(updateStep);
9323                                 });
9324                                 if (attr['onDragEnd']) {
9325                                     scope.onDragEnd();
9326                                 }
9327                             }
9328                         }
9329
9330                         elm.on('keydown', onKeyDown);
9331                         elm.on('mousedown', onMouseDown);
9332
9333                         $documentBind.event('mousemove', 'isDragging', onMouseMove, scope, true, 0);
9334                         $documentBind.event('mouseup', 'isDragging', onMouseUp, scope, true, 0);
9335
9336                         attr.$observe('disabled', function (disabled) {
9337                             if (disabled) {
9338                                 sliderKnob.removeAttr('tabindex');
9339                             } else {
9340                                 sliderKnob.attr('tabindex', '0');
9341                                 disabled = false;
9342                             }
9343
9344                             elm.toggleClass("slider-disabled", disabled);
9345
9346                             if (angular.isDefined(attr.hideDisabledKnob)) {
9347                                 scope.hideKnob = disabled;
9348                             }
9349                         });
9350
9351                         ngModelCtrl.$render = function() {
9352                             if (!scope.isDragging) {
9353                                 modelRenderer();
9354                                 if (attr['onRenderEnd'] && !attr['disabled']) {
9355                                     scope.onRenderEnd({currentModelValue: scope.currentModelValue});
9356                                 }
9357                             }
9358                         };
9359                         ngModelCtrl.$viewChangeListeners.push(modelRenderer);
9360                         ngModelCtrl.$formatters.push(getValidMinMax);
9361                         ngModelCtrl.$formatters.push(getValidStep);
9362                     }
9363                 };
9364             }]);
9365 /**
9366  * @ngdoc directive
9367  * @name Forms.att:spinButton
9368  *
9369  * @param {String} spin-button-id - An ID for the input field
9370  * @param {Integer} min - Minimum value for the input
9371  * @param {Integer} max - Maximum value for the input
9372  * @param {Integer} step - Value by which input field increments or decrements on up/down arrow keys
9373  * @param {Integer} page-step - Value by which input field increments or decrements on page up/down keys
9374  * @param {boolean} input-model-key - Default value for input field
9375  * @param {boolean} disabled-flag - A boolean that triggers directive once the min or max value has reached
9376  *
9377  * @description
9378  *  <file src="src/spinButton/docs/readme.md" />
9379  *
9380  * @example
9381  *  <section id="code">
9382         <example module="b2b.att">
9383             <file src="src/spinButton/docs/demo.html" />
9384             <file src="src/spinButton/docs/demo.js" />
9385        </example>
9386     </section>
9387  * 
9388  */
9389 angular.module('b2b.att.spinButton', ['b2b.att.utilities'])
9390     .constant('b2bSpinButtonConfig', {
9391         min: 1,
9392         max: 10,
9393         step: 1,
9394         pageStep: 10,
9395         inputModelKey: 'value',
9396         disabledFlag: false
9397     })
9398     .directive('b2bSpinButton', ['keymap', 'b2bSpinButtonConfig', 'b2bUserAgent', function (keymap, b2bSpinButtonConfig, userAgent) {
9399         return {
9400             restrict: 'EA',
9401             require: '?ngModel',
9402             transclude: false,
9403             replace: true,
9404             scope: {
9405                 min: '=min',
9406                 max: '=max',
9407                 step: '=step',
9408                 pageStep: '=pageStep',
9409                 spinButtonId: '@',
9410                 inputValue: '=ngModel',
9411                 inputModelKey: '@',
9412                 disabledFlag: "=?"
9413             },
9414             templateUrl: 'b2bTemplate/spinButton/spinButton.html',
9415             controller: ['$scope', '$element', '$attrs', function (scope, element, attrs) {
9416
9417                 scope.isMobile = userAgent.isMobile();
9418                 scope.notMobile = userAgent.notMobile();
9419
9420                 scope.min = attrs.min ? scope.min : b2bSpinButtonConfig.min;
9421                 scope.max = attrs.max ? scope.max : b2bSpinButtonConfig.max;
9422                 scope.step = attrs.step ? scope.step : b2bSpinButtonConfig.step;
9423                 scope.pageStep = attrs.pageStep ? scope.pageStep : b2bSpinButtonConfig.pageStep;
9424                 scope.inputModelKey = attrs.inputModelKey ? scope.inputModelKey : b2bSpinButtonConfig.inputModelKey;
9425                 scope.disabledFlag = attrs.disabledFlag ? scope.disabledFlag : b2bSpinButtonConfig.disabledFlag;
9426                 
9427                 if (scope.min < 0) {
9428                     scope.min = 0;
9429                 }
9430                 if (scope.max > 999) {
9431                     scope.max = 999;
9432                 }
9433
9434                 scope.isPlusDisabled = function () {
9435                     return (scope.disabledFlag || scope.inputValue[scope.inputModelKey] >= scope.max);
9436                 };
9437                 scope.isMinusDisabled = function () {
9438                     return (scope.disabledFlag || scope.inputValue[scope.inputModelKey] <= scope.min);
9439                 };
9440
9441                 scope.getValidateInputValue = function (value) {
9442                     if (value <= scope.min) {
9443                         return scope.min;
9444                     } else if (value >= scope.max) {
9445                         return scope.max;
9446                     } else {
9447                         return value;
9448                     }
9449                 };
9450
9451                 scope.plus = function () {
9452                     scope.inputValue[scope.inputModelKey] = scope.getValidateInputValue(parseInt(scope.inputValue[scope.inputModelKey], 10) + scope.step);
9453                 };
9454                 scope.minus = function () {
9455                     scope.inputValue[scope.inputModelKey] = scope.getValidateInputValue(parseInt(scope.inputValue[scope.inputModelKey], 10) - scope.step);
9456                 };
9457                 scope.pagePlus = function () {
9458                     scope.inputValue[scope.inputModelKey] = scope.getValidateInputValue(parseInt(scope.inputValue[scope.inputModelKey], 10) + scope.pageStep);
9459                 };
9460                 scope.pageMinus = function () {
9461                     scope.inputValue[scope.inputModelKey] = scope.getValidateInputValue(parseInt(scope.inputValue[scope.inputModelKey], 10) - scope.pageStep);
9462                 };
9463
9464             }],
9465             link: function (scope, elem) {
9466
9467                 if (scope.notMobile) {
9468                     angular.element(elem).find('input').attr('aria-live', 'off');
9469                     angular.element(elem).find('input').attr('role', 'spinbutton');
9470                 }
9471
9472                 elem.find('input').bind('keydown', function (e) {
9473                     if (e.keyCode === keymap.KEY.UP) {
9474                         scope.plus();
9475                     } else if (e.keyCode === keymap.KEY.DOWN){
9476                         scope.minus();
9477                     } else if (e.keyCode === keymap.KEY.HOME) {
9478                         scope.inputValue[scope.inputModelKey] = parseInt(scope.min);
9479                     } else if (e.keyCode === keymap.KEY.END) {
9480                         scope.inputValue[scope.inputModelKey] = parseInt(scope.max);
9481                     } else if (e.keyCode === keymap.KEY.PAGE_UP) {
9482                         scope.pagePlus();
9483                     } else if (e.keyCode === keymap.KEY.PAGE_DOWN) {
9484                         scope.pageMinus();
9485                     }
9486                     scope.$apply();
9487                 });
9488
9489                 elem.find('input').bind('keyup', function () {
9490                     if (scope.inputValue[scope.inputModelKey] === null ||
9491                         scope.inputValue[scope.inputModelKey] === '' ||
9492                         scope.inputValue[scope.inputModelKey] < scope.min) {
9493                         scope.inputValue[scope.inputModelKey] = scope.min;
9494                         scope.$apply();
9495                     } else if (angular.isUndefined(scope.inputValue[scope.inputModelKey]) || 
9496                                scope.inputValue[scope.inputModelKey] > scope.max) {
9497                         scope.inputValue[scope.inputModelKey] = scope.max;
9498                         scope.$apply();
9499                     }
9500                 });
9501
9502                 scope.focusInputSpinButton = function (evt) {
9503                     evt.preventDefault();
9504                     if (scope.notMobile) {
9505                         elem[0].querySelector('input').focus();
9506                     }
9507                 };
9508
9509             }
9510         };  
9511     }]);
9512 /** 
9513  * @ngdoc directive 
9514  * @name Template.att:Static Route
9515  * 
9516  * @description 
9517  *  <file src="src/staticRouteTemplate/docs/readme.md" /> 
9518  * 
9519  * @example 
9520  *  <section id="code"> 
9521         <example module="b2b.att"> 
9522             <file src="src/staticRouteTemplate/docs/demo.html" /> 
9523             <file src="src/staticRouteTemplate/docs/demo.js" /> 
9524        </example> 
9525     </section>    
9526  * 
9527  */
9528 angular.module('b2b.att.staticRouteTemplate', ['b2b.att.utilities'])
9529   
9530 /**
9531  * @ngdoc directive
9532  * @name Progress & usage indicators.att:statusTracker
9533  *
9534  * @scope
9535  * @param {array} statusObject - An array of status objects that accept heading, estimate, description and state
9536  * @description
9537  * <file src="src/statusTracker/docs/readme.md" />
9538  *
9539  * @usage
9540  *
9541 <div ng-controller="statusTrackerController">
9542     <b2b-status-tracker statuses="statusObject"></b2b-status-tracker>
9543 </div>
9544
9545  * @example
9546     <section id="code">   
9547         <example module="b2b.att">
9548             <file src="src/statusTracker/docs/demo.html" />
9549             <file src="src/statusTracker/docs/demo.js" />
9550         </example>
9551     </section>
9552  */
9553
9554 angular.module('b2b.att.statusTracker', ['b2b.att.utilities'])
9555 .constant('b2bStatusTrackerConfig', {
9556     'maxViewItems': 3,
9557     'icons': {
9558         'complete': 'icoControls-approval',
9559         'current': 'icon-misc-time',
9560         'pending': 'icoControls-statusokay',
9561         'actionRequired': 'icon-primary-securityalerts-alert',
9562         'notAvailable': 'icoControls-restricted'
9563     }
9564 })
9565 .directive('b2bStatusTracker', ['b2bStatusTrackerConfig', function(b2bStatusTrackerConfig) {
9566         return {
9567             restrict: 'EA',
9568             transclude: false,
9569             replace: true,
9570             scope:{
9571                 statuses: '='
9572             },
9573             templateUrl: function(scope) {
9574                 return 'b2bTemplate/statusTracker/statusTracker.html';
9575             },
9576             link: function(scope, element, attr) {
9577                 scope.currentViewIndex = 0;
9578                 scope.b2bStatusTrackerConfig = b2bStatusTrackerConfig;
9579
9580                 scope.nextStatus = function() {
9581                     if (scope.currentViewIndex+1 <= scope.statuses.length) {
9582                         scope.currentViewIndex++;
9583                     }
9584                 };
9585                 scope.previousStatus = function() {
9586                     if (scope.currentViewIndex-1 >= 0) {
9587                         scope.currentViewIndex--;
9588                     }
9589                 };
9590                 scope.isInViewport = function(index) {
9591                     return (index < scope.currentViewIndex+3 && index >= scope.currentViewIndex);  // && index > scope.currentViewIndex-2
9592                 };
9593
9594                 scope.removeCamelCase = function(str) {
9595                     return str.replace(/([a-z])([A-Z])/g, '$1 $2').toLowerCase();
9596                 }
9597             }
9598         };
9599     }]);
9600 /**
9601  * @ngdoc directive
9602  * @name Progress & usage indicators.att:stepTracker
9603  *
9604  * @scope
9605  * @param {array} stepsItemsObject - An array of step objects
9606  * @param {Integer} currenIindex - This indicates the current running step
9607  * @param {Integer} viewportIndex - This is optional. This can used to start the view port rather than 1 item.
9608  * @description
9609  * <file src="src/stepTracker/docs/readme.md" />
9610  *
9611  * @usage
9612  *
9613  *  <b2b-step-tracker steps-items-object="stepsObject" current-index="currentStepIndex" step-indicator-heading="stepHeading"></b2b-step-tracker>
9614  *
9615
9616  * @example
9617     <section id="code">   
9618         <b>HTML + AngularJS</b>
9619         <example module="b2b.att">
9620             <file src="src/stepTracker/docs/demo.html" />
9621             <file src="src/stepTracker/docs/demo.js" />
9622         </example>
9623     </section>
9624  */
9625 angular.module('b2b.att.stepTracker', ['b2b.att.utilities'])
9626     .constant('b2bStepTrackerConfig', {
9627         'maxViewItems': 5
9628     })
9629     .directive('b2bStepTracker', ['b2bStepTrackerConfig', function(b2bStepTrackerConfig) {
9630         return {
9631             restrict: 'EA',
9632             transclude: true,
9633             scope:{
9634                 stepsItemsObject:"=",
9635                 currentIndex:"=",
9636                 viewportIndex:"=?"
9637             },
9638             templateUrl: 'b2bTemplate/stepTracker/stepTracker.html',
9639             link: function(scope, ele, attr) {
9640                 if (angular.isDefined(scope.viewportIndex)) {
9641                     scope.currentViewIndex = scope.viewportIndex - 1;   
9642                 }else{
9643                     scope.currentViewIndex = 0;
9644                 }
9645                
9646                scope.b2bStepTrackerConfig = b2bStepTrackerConfig;
9647                scope.nextStatus = function() {
9648                     if (scope.currentViewIndex+1 <= scope.stepsItemsObject.length) {
9649                         scope.currentViewIndex++;
9650                     }
9651                 };
9652                 scope.previousStatus = function() {
9653                     if (scope.currentViewIndex-1 >= 0) {
9654                         scope.currentViewIndex--;
9655                     }
9656                 };
9657                 scope.isInViewport = function(index) {
9658                     return (index < scope.currentViewIndex+b2bStepTrackerConfig.maxViewItems && index >= scope.currentViewIndex);
9659                 };
9660             }
9661         };
9662     }]);
9663      
9664 /**
9665  * @ngdoc directive
9666  * @name Buttons, links & UI controls.att:switches
9667  *
9668  * @description
9669  *  <file src="src/switches/docs/readme.md" />
9670  *
9671  * @usage
9672  *  
9673  *  <!-- On / Off Toggle switch -->
9674  *  <label for="switch1" class="controlled-text-wrap"> This is ON
9675  *      <input type="checkbox" role="checkbox" b2b-switches id="switch1" ng-model="switch1.value">
9676  *  </label>
9677  *
9678  *  <!-- On / Off Toggle switch and DISABLED -->
9679  *  <label for="switch2" class="controlled-text-wrap"> This is ON (disabled)
9680  *      <input type="checkbox" role="checkbox" b2b-switches id="switch2" ng-model="switch1.value" ng-disabled="true" >
9681  *  </label> 
9682  *
9683  *
9684  * @example
9685  *  <section id="code">
9686         <b>HTML + AngularJS</b>
9687         <example module="b2b.att">
9688             <file src="src/switches/docs/demo.js" />
9689             <file src="src/switches/docs/demo.html" />
9690         </example>
9691     </section>
9692  */
9693 angular.module('b2b.att.switches', ['b2b.att.utilities'])
9694     .directive('b2bSwitches', ['$compile', '$templateCache', 'keymap', 'events', function ($compile, $templateCache, keymap, events) {
9695         return {
9696             restrict: 'EA',
9697             require: ['ngModel'],
9698             link: function (scope, element, attrs, ctrl) {
9699                 var ngModelController = ctrl[0];
9700         
9701                 element.parent().bind("keydown", function (e) {
9702                     if (!attrs.disabled && (e.keyCode === keymap.KEY.ENTER || e.keyCode === keymap.KEY.SPACE)) {
9703                         events.preventDefault(e);
9704                         ngModelController.$setViewValue(!ngModelController.$viewValue);
9705                         element.prop("checked", ngModelController.$viewValue);
9706                     }
9707                 });
9708
9709                 element.wrap('<div class="btn-switch">');
9710                 //element.attr("tabindex", -1);
9711                 if (navigator.userAgent.match(/iphone/i)){
9712                     element.attr("aria-live", "polite");
9713                 }
9714                 else {
9715                     element.removeAttr('aria-live');
9716                 }
9717
9718                 var templateSwitch = angular.element($templateCache.get("b2bTemplate/switches/switches.html"));
9719                 if (angular.isDefined(attrs.typeSpanish)) {
9720                     templateSwitch = angular.element($templateCache.get("b2bTemplate/switches/switches-spanish.html"));
9721                 }
9722
9723                 templateSwitch = $compile(templateSwitch)(scope);
9724                 element.parent().append(templateSwitch);
9725
9726                 element.bind("focus", function (e) {
9727                     element.parent().addClass('focused');
9728                 });
9729
9730                 element.bind("blur", function (e) {
9731                     element.parent().removeClass('focused');
9732                 });
9733             }
9734         };
9735     }]);
9736 /**
9737  * @ngdoc directive
9738  * @name Messages, modals & alerts.att:tableMessages
9739  *
9740  * @description
9741  *  <file src="src/tableMessages/docs/readme.md" />
9742  *
9743  * @usage
9744     <!-- no matching results -->
9745     <b2b-table-message msg-type="'noMatchingResults'">
9746        <p>No Matching Results</p>
9747     </b2b-table-message>
9748   
9749     <!-- info could not load -->
9750     <b2b-table-message msg-type="'infoCouldNotLoad'" on-refresh-click="refreshClicked()">
9751     </b2b-table-message>
9752    
9753     <!-- magnify search -->
9754     <b2b-table-message msg-type="'magnifySearch'">
9755     </b2b-table-message>
9756    
9757     <!-- loading data -->
9758     <b2b-table-message msg-type="'loadingTable'">
9759           <!-- custom html -->
9760           <p>The data is currently loading...</p>
9761     </b2b-table-message>
9762
9763  * @example
9764     <section id="code">   
9765         <b>HTML + AngularJS</b>
9766         <example module="b2b.att">
9767             <file src="src/tableMessages/docs/demo.html" />
9768             <file src="src/tableMessages/docs/demo.js" />
9769         </example>
9770     </section>
9771  */
9772 angular.module('b2b.att.tableMessages', [])
9773     .directive('b2bTableMessage', [function() {
9774         return {
9775             restrict: 'AE',
9776             replace: true,
9777             transclude: true,
9778             scope: {
9779                 msgType: '=',
9780                 onRefreshClick: '&'
9781             },
9782             templateUrl: 'b2bTemplate/tableMessages/tableMessage.html',
9783             link: function(scope) {
9784                 scope.refreshAction = function(evt) {
9785                     scope.onRefreshClick(evt);
9786                 };
9787             }
9788         };
9789     }]);
9790
9791 /**
9792  * @ngdoc directive
9793  * @name Tabs, tables & accordions.att:tableScrollbar
9794  *
9795  * @description
9796  *  <file src="src/tableScrollbar/docs/readme.md" />
9797  *
9798  * @usage
9799  * 
9800 <b2b-table-scrollbar>
9801     <table>
9802         <thead type="header">
9803             <tr>
9804                 <th role="columnheader" scope="col" key="Id" id="col1">Id</th>
9805                 .....
9806             </tr>
9807         </thead>
9808         <tbody type="body">
9809             <tr>
9810                 <td id="rowheader0" headers="col1">1002</td>
9811                 .....
9812             </tr>
9813         </tbody>
9814     </table>
9815 </b2b-table-scrollbar>
9816  *
9817  * @example
9818  *  <section id="code">
9819         <example module="b2b.att">
9820             <file src="src/tableScrollbar/docs/demo.html" />
9821             <file src="src/tableScrollbar/docs/demo.js" />
9822        </example>
9823     </section>
9824  *
9825  */
9826 angular.module('b2b.att.tableScrollbar', [])
9827     .directive('b2bTableScrollbar', ['$timeout', function ($timeout) {
9828         return {
9829             restrict: 'E',
9830             scope: true,
9831             transclude: true,
9832             templateUrl: 'b2bTemplate/tableScrollbar/tableScrollbar.html',
9833             link: function (scope, element, attrs, ctrl) {
9834                 var firstThWidth, firstTdWidth, firstColumnWidth, firstColumnHeight, trHeight = 0;
9835                 var pxToScroll = '';
9836                 var tableElement = element.find('table');
9837                 var thElements = element.find('th');
9838                 var tdElements = element.find('td');
9839                 var innerContainer = angular.element(element[0].querySelector('.b2b-table-inner-container'));
9840                 var outerContainer = angular.element(element[0].querySelector('.b2b-table-scrollbar'));
9841
9842                 scope.disableLeft = true;
9843                 scope.disableRight = false;
9844
9845                 if (angular.isDefined(thElements[0])) {
9846                     firstThWidth = thElements[0].offsetWidth;
9847                 }
9848                 if (angular.isDefined(tdElements[0])) {
9849                     firstTdWidth = tdElements[0].offsetWidth;
9850                 }
9851                 firstColumnWidth = (firstThWidth > firstTdWidth) ? firstThWidth : firstTdWidth;
9852
9853                 innerContainer.css({
9854                     'padding-left': (firstColumnWidth + 2) + 'px'
9855                 });
9856
9857                 angular.forEach(element.find('tr'), function (eachTr, index) {
9858                     trObject = angular.element(eachTr);
9859                     firstColumn = angular.element(trObject.children()[0]);
9860
9861                     angular.element(firstColumn).css({
9862                         'left': '0px',
9863                         'width': (firstColumnWidth + 2) + 'px',
9864                         'position': 'absolute'
9865                     });
9866
9867                     trHeight = trObject[0].offsetHeight;
9868                     firstColumnHeight = firstColumn[0].offsetHeight;
9869                     if (navigator.userAgent.toLowerCase().indexOf('firefox') > -1) {
9870                         firstColumnHeight += 1;
9871                     }
9872
9873                     if (trHeight !== firstColumnHeight - 1) {
9874                         if (trHeight > firstColumnHeight) {
9875                             if (navigator.userAgent.toLowerCase().indexOf('firefox') > -1) {
9876                                 trHeight -= 1;
9877                             }
9878                             angular.element(firstColumn).css({
9879                                 'height': (trHeight + 1) + 'px'
9880                             });
9881                         } else {
9882                             angular.element(trObject).css({
9883                                 'height': (firstColumnHeight - 1) + 'px'
9884                             });
9885                         }
9886                     }
9887
9888                 });
9889
9890                 pxToScroll = outerContainer[0].offsetWidth - firstColumnWidth;
9891
9892                 scope.scrollLeft = function () {
9893                     innerContainer[0].scrollLeft = innerContainer[0].scrollLeft + 20 - pxToScroll;
9894                 };
9895
9896                 scope.scrollRight = function () {
9897                     innerContainer[0].scrollLeft = innerContainer[0].scrollLeft + pxToScroll - 20;
9898                 };
9899
9900                 scope.checkScrollArrows = function () {
9901                     if (innerContainer[0].scrollLeft == 0) {
9902                         scope.disableLeft = true;
9903                     } else {
9904                         scope.disableLeft = false;
9905                     }
9906
9907                     if (((innerContainer[0].offsetWidth - firstColumnWidth) + innerContainer[0].scrollLeft) >= tableElement[0].offsetWidth) {
9908                         scope.disableRight = true;
9909                     } else {
9910                         scope.disableRight = false;
9911                     }
9912                 };
9913
9914
9915                 innerContainer.bind('scroll', function () {
9916                     $timeout(function () {
9917                         scope.checkScrollArrows();
9918                     }, 1);
9919                 });
9920
9921             }
9922         };
9923     }]);
9924 /**
9925  * @ngdoc directive
9926  * @name Tabs, tables & accordions.att:tables
9927  *
9928  * @description
9929  *  <file src="src/tables/docs/readme.md" />
9930  *
9931  * @usage
9932  *   
9933  Table
9934  <table b2b-table table-data="tableData" search-string="searchString">
9935     <thead b2b-table-row type="header">
9936         <tr>
9937             <th b2b-table-header key="requestId" default-sort="a" id="col1">Header 1</th>
9938             <th b2b-table-header key="requestType" sortable="false" id="col2">Header 2</th>
9939         </tr>
9940     </thead>
9941     <tbody b2b-table-row type="body" row-repeat="rowData in tableData">
9942         <tr>
9943             <td b2b-table-body id="rowheader{{$index}}" headers="col1" ng-bind="rowData['requestId']"> </td>
9944             <td b2b-table-body ng-bind="rowData['requestType']"></td>
9945         </tr>
9946     </tbody>
9947  </table>
9948  *
9949  * @example
9950  *  <section id="code">
9951         <example module="b2b.att">
9952             <file src="src/tables/docs/demo.html" />
9953             <file src="src/tables/docs/demo.js" />
9954        </example>
9955     </section>
9956  *
9957  */
9958 angular.module('b2b.att.tables', ['b2b.att.utilities'])
9959     .constant('b2bTableConfig', {
9960         defaultSortPattern: false, // true for descending & false for ascending
9961         highlightSearchStringClass: 'tablesorter-search-highlight',
9962         zebraStripCutOff: 6, // > zebraStripCutOff
9963         tableBreakpoints: [ // breakpoints are >= min and < max
9964             {
9965                 min: 0,
9966                 max: 480,
9967                 columns: 2
9968             },
9969             {
9970                 min: 480,
9971                 max: 768,
9972                 columns: 3
9973             },
9974             {
9975                 min: 768,
9976                 max: 1025,
9977                 columns: 5
9978             },
9979             {
9980                 min: 1025,
9981                 max: 1920,
9982                 columns: 7
9983             }
9984         ]
9985     })
9986     .directive('b2bTable', ['$filter', '$window', 'b2bTableConfig', '$timeout', function ($filter, $window, b2bTableConfig, $timeout) {
9987         return {
9988             restrict: 'EA',
9989             replace: true,
9990             transclude: true,
9991             scope: {
9992                 tableData: "=",
9993                 viewPerPage: "=",
9994                 currentPage: "=",
9995                 totalPage: "=",
9996                 searchCategory: "=",
9997                 searchString: "=",
9998                 nextSort: '='
9999             },
10000             require: 'b2bTable',
10001             templateUrl: 'b2bTemplate/tables/b2bTable.html',
10002             controller: ['$scope', '$attrs', function ($scope, $attrs) {
10003                 this.headers = [];
10004                 this.currentSortIndex = null;
10005                 this.responsive = $scope.responsive = $attrs.responsive;
10006                 this.maxTableColumns = -1;
10007                 this.totalTableColums = 0;
10008                 this.active = $scope.active = false;
10009                 this.responsiveRowScopes = [];
10010                 this.hideColumnPriority = [];
10011                 this.hiddenColumn = [];
10012                 this.setIndex = function (headerScope, priority) {
10013                     this.headers.push(headerScope);
10014                     if (this.responsive) {
10015                         this.totalTableColums++;
10016                         if (!isNaN(priority)) {
10017                             this.hideColumnPriority[priority] = this.totalTableColums - 1;
10018                         } else {
10019                             this.hideColumnPriority[this.totalTableColums - 1] = this.totalTableColums - 1;
10020                         }
10021                     }
10022                     return this.totalTableColums - 1;
10023                 };
10024                 this.getIndex = function (headerName) {
10025                     for (var i = 0; i < this.headers.length; i++) {
10026                         if (this.headers[i].headerName === headerName) {
10027                             return this.headers[i].index;
10028                         }
10029                     }
10030                     return null;
10031                 };
10032                 this.setResponsiveRow = function (responsiveRowScope) {
10033                     this.responsiveRowScopes.push(responsiveRowScope);
10034                 }
10035                 $scope.nextSort = '';
10036                 this.sortData = function (columnIndex, reverse, externalSort) {
10037                     if ($scope.$parent && $scope.$parent !== undefined) {
10038                         $scope.$parent.columnIndex = columnIndex;
10039                         $scope.$parent.reverse = reverse;
10040                     }
10041                     this.currentSortIndex = columnIndex;
10042                     if (externalSort === true) {
10043                         if (!reverse) {
10044                             $scope.nextSort = 'd'
10045                         } else {
10046                             $scope.nextSort = 'a'
10047                         }
10048                     }
10049                     $scope.currentPage = 1;
10050                     this.resetSortPattern();
10051                 };
10052                 this.getSearchString = function () {
10053                     return $scope.searchString;
10054                 };
10055                 this.resetSortPattern = function () {
10056                     for (var i = 0; i < this.headers.length; i++) {
10057                         var currentScope = this.headers[i];
10058                         if (currentScope.index !== this.currentSortIndex) {
10059                             currentScope.resetSortPattern();
10060                         }
10061                     }
10062                 };
10063
10064                 $scope.$watch('nextSort', function (val) {
10065                     if ($scope.$parent && $scope.$parent !== undefined) {
10066                         $scope.$parent.nextSort = val;
10067                     }
10068
10069                 });
10070             }],
10071             link: function (scope, elem, attr, ctrl) {
10072                 scope.searchCriteria = {};
10073                 scope.tableBreakpoints = attr.tableConfig ? scope.$parent.$eval(attr.tableConfig) : angular.copy(b2bTableConfig.tableBreakpoints);
10074                 scope.$watchCollection('tableData', function (value) {
10075                     if (value && !isNaN(value.length)) {
10076                         scope.totalRows = value.length;
10077                     }
10078                 });
10079                 scope.$watch('currentPage', function (val) {
10080                     if (scope.$parent && scope.$parent !== undefined) {
10081                         scope.$parent.currentPage = val;
10082                     }
10083
10084                 });
10085                 scope.$watch('viewPerPage', function (val) {
10086                     if (scope.$parent && scope.$parent !== undefined) {
10087                         scope.$parent.viewPerPage = val;
10088                     }
10089                 });
10090                 scope.$watch('totalRows', function (val) {
10091                     if (scope.$parent && scope.$parent !== undefined) {
10092                         if (val > b2bTableConfig.zebraStripCutOff) {
10093                             scope.$parent.zebraStripFlag = true;
10094                         } else {
10095                             scope.$parent.zebraStripFlag = false;
10096                         }
10097                     }
10098                 });
10099                 scope.$watch(function () {
10100                     return scope.totalRows / scope.viewPerPage;
10101                 }, function (value) {
10102                     if (!isNaN(value)) {
10103                         scope.totalPage = Math.ceil(value);
10104                         scope.currentPage = 1;
10105                     }
10106                 });
10107                 var searchValCheck = function (val) {
10108                     if (angular.isDefined(val) && val !== null && val !== "") {
10109                         return true;
10110                     }
10111                 };
10112                 var setSearchCriteria = function (v1, v2) {
10113                     if (searchValCheck(v1) && searchValCheck(v2)) {
10114                         var index = ctrl.getIndex(v2);
10115                         scope.searchCriteria = {};
10116                         if (index !== null) {
10117                             scope.searchCriteria[index] = v1;
10118                         }
10119                     } else if (searchValCheck(v1) && (!angular.isDefined(v2) || v2 === null || v2 === "")) {
10120                         scope.searchCriteria = {
10121                             $: v1
10122                         };
10123                     } else {
10124                         scope.searchCriteria = {};
10125                     }
10126                 };
10127                 scope.$watch('searchCategory', function (newVal, oldVal) {
10128                     if (newVal !== oldVal) {
10129                         setSearchCriteria(scope.searchString, newVal);
10130                     }
10131                 });
10132                 scope.$watch('searchString', function (newVal, oldVal) {
10133                     if (newVal !== oldVal) {
10134                         setSearchCriteria(newVal, scope.searchCategory);
10135                     }
10136                 });
10137                 scope.$watchCollection('searchCriteria', function (val) {
10138                     if (scope.$parent && scope.$parent !== undefined) {
10139                         scope.$parent.searchCriteria = val;
10140                     }
10141                     scope.totalRows = scope.tableData && ($filter('filter')(scope.tableData, val, false)).length || 0;
10142                     scope.currentPage = 1;
10143                 });
10144                 var window = angular.element($window);
10145                 var findMaxTableColumns = function () {
10146                     var windowWidth;
10147                     windowWidth = $window.innerWidth;
10148                     ctrl.maxTableColumns = -1;
10149                     for (var i in scope.tableBreakpoints) {
10150                         if (windowWidth >= scope.tableBreakpoints[i].min && windowWidth < scope.tableBreakpoints[i].max) {
10151                             ctrl.maxTableColumns = scope.tableBreakpoints[i].columns;
10152                             break;
10153                         }
10154                     }
10155                     if (ctrl.maxTableColumns > -1 && ctrl.totalTableColums > ctrl.maxTableColumns) {
10156                         ctrl.active = true;
10157                     } else {
10158                         ctrl.active = false;
10159                     }
10160                     for (var i in ctrl.responsiveRowScopes) {
10161                         ctrl.responsiveRowScopes[i].setActive(ctrl.active);
10162                     }
10163                 };
10164                 var findHiddenColumn = function () {
10165                     var columnDiffenence = ctrl.maxTableColumns > -1 ? ctrl.totalTableColums - ctrl.maxTableColumns : 0;
10166                     ctrl.hiddenColumn = [];
10167                     if (columnDiffenence > 0) {
10168                         var tempHideColumnPriority = angular.copy(ctrl.hideColumnPriority);
10169                         for (var i = 0; i < columnDiffenence; i++) {
10170                             ctrl.hiddenColumn.push(tempHideColumnPriority.pop());
10171                         }
10172                     }
10173                 };
10174                 var resizeListener = function () {
10175                     findMaxTableColumns();
10176                     findHiddenColumn();
10177                 };
10178                 if (ctrl.responsive) {
10179                     window.bind('resize', function () {
10180                         resizeListener();
10181                         scope.$apply();
10182                     });
10183                     $timeout(function () {
10184                         resizeListener();
10185                     }, 100);
10186                 }
10187             }
10188         };
10189     }])
10190     .directive('b2bTableRow', [function () {
10191         return {
10192             restrict: 'EA',
10193             compile: function (elem, attr) {
10194                 if (attr.type === 'header') {
10195                     angular.noop();
10196                 } else if (attr.type === 'body') {
10197                     var html = elem.children();
10198                     if (attr.rowRepeat) {
10199                         html.attr('ng-repeat', attr.rowRepeat.concat(" | orderBy : (reverse?'-':'')+ columnIndex  | filter : searchCriteria : false "));
10200                     }
10201                     html.attr('ng-class', "{'odd': $odd && zebraStripFlag}");
10202                     html.attr('class', 'data-row');
10203                     html.attr('b2b-responsive-row', '{{$index}}');
10204                     elem.append(html);
10205                 }
10206             }
10207         };
10208     }])
10209     .directive('b2bTableHeader', ['b2bTableConfig', function (b2bTableConfig) {
10210         return {
10211             restrict: 'EA',
10212             replace: true,
10213             transclude: true,
10214             scope: {
10215                 sortable: '@',
10216                 defaultSort: '@',
10217                 index: '@key'
10218             },
10219             require: '^b2bTable',
10220             templateUrl: function (elem, attr) {
10221                 if (attr.sortable === 'false') {
10222                     return 'b2bTemplate/tables/b2bTableHeaderUnsortable.html';
10223                 } else {
10224                     return 'b2bTemplate/tables/b2bTableHeaderSortable.html';
10225                 }
10226             },
10227             link: function (scope, elem, attr, ctrl) {
10228                 var reverse = b2bTableConfig.defaultSortPattern;
10229                 scope.headerName = elem.text();
10230                 scope.headerId = elem.attr('id');
10231                 scope.sortPattern = null;
10232                 var priority = parseInt(attr.priority, 10);
10233                 scope.columnIndex = ctrl.setIndex(scope, priority);
10234
10235                 scope.isHidden = function () {
10236                     return (ctrl.hiddenColumn.indexOf(scope.columnIndex) > -1);
10237                 };
10238
10239                 scope.$watch(function () {
10240                     return elem.text();
10241                 }, function (value) {
10242                     scope.headerName = value;
10243                 });
10244                 scope.sort = function (sortType) {
10245                     if (typeof sortType === 'boolean') {
10246                         reverse = sortType;
10247                     }
10248                     ctrl.sortData(scope.index, reverse, false);
10249                     scope.sortPattern = reverse ? 'descending' : 'ascending';
10250                     reverse = !reverse;
10251                 };
10252                 scope.$watch(function () {
10253                     return ctrl.currentSortIndex;
10254                 }, function (value) {
10255                     if (value !== scope.index) {
10256                         scope.sortPattern = null;
10257                     }
10258                 });
10259
10260                 if (scope.sortable === undefined || scope.sortable === 'true' || scope.sortable === true) {
10261                     scope.sortable = 'true';
10262                 } else if (scope.sortable === false || scope.sortable === 'false') {
10263                     scope.sortable = 'false';
10264                 }
10265
10266                 if (scope.sortable !== 'false') {
10267                     if (scope.defaultSort === 'A' || scope.defaultSort === 'a') {
10268                         scope.sort(false);
10269                     } else if (scope.defaultSort === 'D' || scope.defaultSort === 'd') {
10270                         scope.sort(true);
10271                     }
10272                 }
10273                 scope.resetSortPattern = function () {
10274                     reverse = b2bTableConfig.defaultSortPattern;
10275                 };
10276             }
10277         };
10278     }])
10279     .directive('b2bResponsiveRow', ['$templateCache', '$timeout', '$compile', function ($templateCache, $timeout, $compile) {
10280         return {
10281             restrict: 'EA',
10282             require: '^b2bTable',
10283             controller: ['$scope', function ($scope) {
10284                 this.rowValues = $scope.rowValues = [];
10285                 this.setRowValues = function (rowValue) {
10286                     this.rowValues.push(rowValue);
10287                 };
10288                 var columnIndexCounter = -1;
10289                 this.getIndex = function () {
10290                     columnIndexCounter++;
10291                     return columnIndexCounter;
10292                 };
10293             }],
10294             link: function (scope, elem, attr, ctrl) {
10295                 if (ctrl.responsive) {
10296                     scope.rowIndex = attr.b2bResponsiveRow;
10297                     scope.active = false;
10298                     scope.expandFlag = false;
10299                     scope.headerValues = ctrl.headers;
10300                     ctrl.setResponsiveRow(scope);
10301                     var firstTd = elem.find('td').eq(0);
10302                     scope.setActive = function (activeFlag) {
10303                         scope.active = activeFlag;
10304                         if (scope.active) {
10305                             elem.addClass('has-button');
10306                             firstTd.attr('role', 'rowheader');
10307                             firstTd.parent().attr('role', 'row');
10308                         } else {
10309                             elem.removeClass('has-button');
10310                             firstTd.removeAttr('role');
10311                             firstTd.parent().removeAttr('role');
10312                         }
10313                     };
10314                     scope.toggleExpandFlag = function (expandFlag) {
10315                         if (angular.isDefined(expandFlag)) {
10316                             scope.expandFlag = expandFlag;
10317                         } else {
10318                             scope.expandFlag = !scope.expandFlag;
10319                         }
10320                         if (scope.expandFlag) {
10321                             elem.addClass('opened');
10322                         } else {
10323                             elem.removeClass('opened');
10324                         }
10325                     };
10326
10327                     firstTd.attr('scope', 'row');
10328                     firstTd.addClass('col-1');
10329                     scope.$on('$destroy', function () {
10330                         elem.next().remove();
10331                     });
10332                     $timeout(function () {
10333                         scope.firstTdId = firstTd.attr('id');
10334                         var firstTdContent = firstTd.html();
10335                         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>';
10336                         toggleButtonTemplate = $compile(toggleButtonTemplate)(scope);
10337                         firstTd.html('');
10338                         firstTd.prepend(toggleButtonTemplate);
10339
10340                         var template = $templateCache.get('b2bTemplate/tables/b2bResponsiveRow.html');
10341                         template = $compile(template)(scope);
10342                         elem.after(template);
10343                     }, 100);
10344                 }
10345             }
10346         };
10347     }])
10348     .directive('b2bResponsiveList', ['$templateCache', '$timeout', '$compile', function ($templateCache, $timeout, $compile) {
10349         return {
10350             restrict: 'EA',
10351             require: '^b2bTable',
10352             link: function (scope, elem, attr, ctrl) {
10353                 scope.columnIndex = parseInt(attr.b2bResponsiveList, 10);
10354                 scope.isVisible = function () {
10355                     return (ctrl.hiddenColumn.indexOf(scope.columnIndex) > -1);
10356                 };
10357             }
10358         };
10359     }])
10360     .directive('b2bTableBody', ['$filter', '$timeout', 'b2bTableConfig', function ($filter, $timeout, b2bTableConfig) {
10361         return {
10362             restrict: 'EA',
10363             require: ['^b2bTable', '?^b2bResponsiveRow'],
10364             scope: true,
10365             replace: true,
10366             transclude: true,
10367             templateUrl: 'b2bTemplate/tables/b2bTableBody.html',
10368             link: function (scope, elem, attr, ctrl) {
10369                 var b2bTableCtrl = ctrl[0];
10370                 var b2bResponsiveRowCtrl = ctrl[1];
10371                 var highlightSearchStringClass = b2bTableConfig.highlightSearchStringClass;
10372                 var searchString = "";
10373                 var wrapElement = function (elem) {
10374                     var text = elem.text();
10375                     elem.html($filter('b2bHighlight')(text, searchString, highlightSearchStringClass));
10376                 };
10377                 var traverse = function (elem) {
10378                     var innerHtml = elem.children();
10379                     if (innerHtml.length > 0) {
10380                         for (var i = 0; i < innerHtml.length; i++) {
10381                             traverse(innerHtml.eq(i));
10382                         }
10383                     } else {
10384                         wrapElement(elem);
10385                         return;
10386                     }
10387                 };
10388                 var clearWrap = function (elem) {
10389                     var elems = elem.find('*');
10390                     for (var i = 0; i < elems.length; i++) {
10391                         if (elems.eq(i).attr('class') && elems.eq(i).attr('class').indexOf(highlightSearchStringClass) !== -1) {
10392                             var text = elems.eq(i).text();
10393                             elems.eq(i).replaceWith(text);
10394                         }
10395                     }
10396                 };
10397                 if (b2bResponsiveRowCtrl) {
10398                     scope.columnIndex = b2bResponsiveRowCtrl.getIndex();
10399                     scope.isHidden = function () {
10400                         return (b2bTableCtrl.hiddenColumn.indexOf(scope.columnIndex) > -1);
10401                     };
10402                 }
10403                 $timeout(function () {
10404                     var actualHtml = elem.children();
10405                     scope.$watch(function () {
10406                         return b2bTableCtrl.getSearchString();
10407                     }, function (val) {
10408                         searchString = val;
10409                         clearWrap(elem);
10410                         if (actualHtml.length > 0) {
10411                             traverse(elem);
10412                         } else {
10413                             wrapElement(elem);
10414                         }
10415                     });
10416                     if (b2bResponsiveRowCtrl) {
10417                         b2bResponsiveRowCtrl.setRowValues(elem.html());
10418                     }
10419                 }, 50);
10420             }
10421         };
10422     }])
10423     .directive('b2bTableSort', ['b2bTableConfig','$timeout', function (b2bTableConfig,$timeout) {
10424         return {
10425             restrict: 'EA',
10426             replace: true,
10427             require: '^b2bTable',
10428             link: function (scope, elem, attr, ctrl) {
10429                 var initialSort = '',
10430                     nextSort = '',
10431                     tempsort = '';
10432                 initialSort = attr.initialSort;
10433
10434                 scope.sortTable = function (msg) {
10435                     $timeout(function(){
10436                         if (nextSort.length > 0) {
10437
10438                         if (nextSort === 'd' || nextSort === 'D') {
10439                             tempsort = nextSort
10440                             ctrl.sortData(msg, true, true);
10441                             nextSort = 'a';
10442                              $timeout(function(){
10443                                 if(!angular.isUndefined(elem[0].querySelector('.sortButton')) || elem[0].querySelector('.sortButton') !== null ){
10444                                     angular.element(elem[0].querySelector('.sortButton'))[0].focus();
10445                                 }   
10446                             },100);
10447                             
10448                         } else {
10449                             tempsort = nextSort
10450                             ctrl.sortData(msg, false, true);
10451                             nextSort = 'd';
10452                              $timeout(function(){
10453                                 if(!angular.isUndefined(elem[0].querySelector('.sortButton')) || elem[0].querySelector('.sortButton') !== null ){
10454                                     angular.element(elem[0].querySelector('.sortButton'))[0].focus();
10455                                 }   
10456                             },100);
10457                         }
10458                     } else if (initialSort.length > 0) {
10459
10460                         if (initialSort === 'd' || initialSort === 'D') {
10461                             tempsort = nextSort
10462                             ctrl.sortData(msg, true, true);
10463                             nextSort = 'a';
10464                             $timeout(function(){
10465                                 if(!angular.isUndefined(elem[0].querySelector('.sortButton')) || elem[0].querySelector('.sortButton') !== null ){
10466                                     angular.element(elem[0].querySelector('.sortButton'))[0].focus();
10467                                 }   
10468                             },100);
10469                              
10470                         } else {
10471                             tempsort = nextSort
10472                             ctrl.sortData(msg, false, true);
10473                             nextSort = 'd';
10474                              $timeout(function(){
10475                                 if(!angular.isUndefined(elem[0].querySelector('.sortButton')) || elem[0].querySelector('.sortButton') !== null ){
10476                                     angular.element(elem[0].querySelector('.sortButton'))[0].focus();
10477                                 }   
10478                             },100);
10479
10480                              
10481                         }
10482                     }
10483                     },10)
10484
10485                 };
10486
10487                 scope.sortDropdown = function(msg) {
10488
10489                     if(tempsort==='') {
10490
10491                         tempsort='a'
10492                     }
10493                     if(tempsort === 'd' || tempsort === 'D' ) {
10494                         ctrl.sortData(msg, true, false);       
10495                     } else {
10496                        ctrl.sortData(msg, false, false);
10497                     }
10498
10499                 };
10500             }
10501         };
10502     }]);
10503 /**
10504  * @ngdoc directive
10505  * @name Tabs, tables & accordions.att:tabs
10506  *
10507  * @description
10508  *  <file src="src/tabs/docs/readme.md" />
10509  *
10510  * @usage
10511  *  <b2b-tabset tab-id-selected="activeTabsId">
10512         <b2b-tab ng-repeat="tab in gTabs" tab-item="tab" 
10513                  id="{{tab.uniqueId}}" aria-controls="{{tab.tabPanelId}}"
10514                  ng-disabled="tab.disabled">
10515             {{tab.title}}
10516         </b2b-tab>
10517     </b2b-tabset>
10518  *
10519  * @example
10520  *  <section id="code">
10521         <example module="b2b.att">
10522             <file src="src/tabs/docs/demo.html" />
10523             <file src="src/tabs/docs/demo.js" />
10524         </example>
10525     </section>
10526  *
10527  */
10528
10529 angular.module('b2b.att.tabs', ['b2b.att.utilities'])
10530     .directive('b2bTabset', function () {
10531         return {
10532             restrict: 'EA',
10533             transclude: true,
10534             replace: true,
10535             scope: {
10536                 tabIdSelected: '='
10537             },
10538             templateUrl: 'b2bTemplate/tabs/b2bTabset.html',
10539             controller: ['$scope', function ($scope) {
10540
10541                 this.setTabIdSelected = function (tab) {
10542                     $scope.tabIdSelected = tab.id;
10543                 };
10544
10545                 this.getTabIdSelected = function () {
10546                     return $scope.tabIdSelected;
10547                 };
10548             }]
10549         };
10550     })
10551     .directive('b2bTab', ['keymap', function (keymap) {
10552         return {
10553             restrict: 'EA',
10554             transclude: true,
10555             replace: true,
10556             require: '^b2bTabset',
10557             scope: {
10558                 tabItem: "="
10559             },
10560             templateUrl: 'b2bTemplate/tabs/b2bTab.html',
10561             controller: [function(){}],
10562             link: function (scope, element, attr, b2bTabsetCtrl) {
10563
10564                 if (scope.tabItem && !scope.tabItem.disabled) {
10565                     scope.tabItem.disabled = false;
10566                 }
10567
10568                 scope.isTabActive = function () {
10569                     return (scope.tabItem.id === b2bTabsetCtrl.getTabIdSelected());
10570                 };
10571
10572                 scope.clickTab = function () {
10573                     if (attr.disabled) {
10574                         return;
10575                     }
10576                     b2bTabsetCtrl.setTabIdSelected(scope.tabItem);
10577                 };
10578
10579                 scope.nextKey = function () {
10580                     var el = angular.element(element[0])[0];
10581                     var elementToFocus = null;
10582                     while (el && el.nextElementSibling) {
10583                         el = el.nextElementSibling;
10584                         if (!el.querySelector('a').disabled) {
10585                             elementToFocus = el.querySelector('a');
10586                             break;
10587                         }
10588                     }
10589
10590                     if (!elementToFocus) {
10591                         var childTabs = element.parent().children();
10592                         for (var i = 0; i < childTabs.length; i++) {
10593                             if (!childTabs[i].querySelector('a').disabled) {
10594                                 elementToFocus = childTabs[i].querySelector('a');
10595                                 break;
10596                             }
10597                         }
10598                     }
10599
10600                     if (elementToFocus) {
10601                         elementToFocus.focus();
10602                     }
10603                 };
10604
10605                 scope.previousKey = function () {
10606                     var el = angular.element(element[0])[0];
10607                     var elementToFocus = null;
10608
10609                     while (el && el.previousElementSibling) {
10610                         el = el.previousElementSibling;
10611                         if (!el.querySelector('a').disabled) {
10612                             elementToFocus = el.querySelector('a');
10613                             break;
10614                         }
10615                     }
10616
10617                     if (!elementToFocus) {
10618                         var childTabs = element.parent().children();
10619                         for (var i = childTabs.length - 1; i > 0; i--) {
10620                             if (!childTabs[i].querySelector('a').disabled) {
10621                                 elementToFocus = childTabs[i].querySelector('a');
10622                                 break;
10623                             }
10624                         }
10625                     }
10626
10627                     if (elementToFocus) {
10628                         elementToFocus.focus();
10629                     }
10630                 };
10631
10632                 angular.element(element[0].querySelector('a')).bind('keydown', function (evt) {
10633
10634                     if (!(evt.keyCode)) {
10635                         evt.keyCode = evt.which;
10636                     }
10637
10638                     switch (evt.keyCode) {
10639                         case keymap.KEY.RIGHT:
10640                             evt.preventDefault();
10641                             scope.nextKey();
10642                             break;
10643
10644                         case keymap.KEY.LEFT:
10645                             evt.preventDefault();
10646                             scope.previousKey();
10647                             break;
10648
10649                         default:;
10650                     }
10651                 });
10652             }
10653         };
10654     }]);
10655 /**
10656  * @ngdoc directive
10657  * @name Messages, modals & alerts.att:tagBadges
10658  *
10659  * @description
10660  *  <file src="src/tagBadges/docs/readme.md" />
10661  *
10662  * @example
10663  *  <section id="code">
10664         <example module="b2b.att">
10665             <file src="src/tagBadges/docs/demo.html" />
10666             <file src="src/tagBadges/docs/demo.js" />
10667         </example>
10668     </section>
10669  *
10670  */
10671 angular.module('b2b.att.tagBadges', ['b2b.att.utilities'])
10672         .directive('b2bTagBadge',['$timeout',function($timeout){
10673             return{
10674                 restrict: 'EA',
10675                 link: function(scope,elem,attr,ctrl){
10676                     elem.addClass('b2b-tags');
10677                     if(angular.element(elem[0].querySelector('.icon-primary-close')).length>0) {
10678                         var item = angular.element(elem[0].querySelector('.icon-primary-close'));
10679                         item.bind('click',function(){
10680                         elem.css({'height':'0','width':'0','padding':'0','border':'0'});
10681                         elem.attr('tabindex','0');
10682                         elem[0].focus();
10683                         item.parent().remove();
10684                         elem[0].bind('blur',function(){
10685                             elem[0].remove();
10686                         });
10687                     });  
10688                     }
10689                   
10690
10691
10692
10693                 }
10694             };   
10695 }]);
10696 /**
10697  * @ngdoc directive
10698  * @name Forms.att:textArea
10699  *
10700  * @description
10701  *  <file src="src/textArea/docs/readme.md" />
10702  *
10703  * @usage
10704  *  <textarea b2b-reset b2b-reset-textarea ng-model="textAreaModel" ng-disabled="disabled" ng-trim="false" placeholder="{{placeholderText}}" rows="{{textAreaRows}}" maxlength="{{textAreaMaxlength}}" role="textarea"></textarea>
10705  *
10706  * @example
10707     <section id="code">
10708         <b>HTML + AngularJS</b>
10709         <example module="b2b.att">
10710             <file src="src/textArea/docs/demo.html" />
10711             <file src="src/textArea/docs/demo.js" />
10712         </example>
10713     </section>
10714  */
10715 angular.module('b2b.att.textArea', ['b2b.att.utilities'])
10716
10717 .directive('b2bResetTextarea', [ function () {
10718     return {
10719         restrict: 'A',
10720         require: 'b2bReset',
10721         link: function (scope, element, attrs, ctrl) {
10722
10723             var resetButton = ctrl.getResetButton();
10724             
10725             var computeScrollbarAndAddClass = function () {
10726                 if (element.prop('scrollHeight') > element[0].clientHeight) {
10727                     element.addClass('hasScrollbar');
10728                 } else {
10729                     element.removeClass('hasScrollbar');
10730                 }
10731             };
10732             
10733             computeScrollbarAndAddClass();
10734
10735             element.on('focus keyup', function(){
10736                 computeScrollbarAndAddClass();
10737             });
10738         }
10739     };
10740 }]);
10741
10742 /**
10743  * @ngdoc directive
10744  * @name Forms.att:timeInputField
10745  *
10746  * @description
10747  *  <file src="src/timeInputField/docs/readme.md" />
10748  *
10749  
10750  * @example
10751  *  <section id="code">
10752         <example module="b2b.att">
10753             <file src="src/timeInputField/docs/demo.html" />
10754             <file src="src/timeInputField/docs/demo.js" />
10755        </example>
10756     </section>
10757  *
10758  */
10759 angular.module('b2b.att.timeInputField',['ngMessages', 'b2b.att.utilities']).directive('b2bTimeFormat',function(){
10760     return{
10761         restrict : 'A',
10762         require : '^ngModel',
10763         link : function(scope,elem,attr,ctrl){
10764             elem.on('keyup',function(evt){
10765                 var modelValue = ctrl.$modelValue;
10766                 var format = attr.b2bTimeFormat;
10767                  modelValue = modelValue.split(':');
10768                 if(format == "12"){
10769                     if(!(modelValue[0] <= 12 && modelValue[0] > 0 ) || !(modelValue[1] <= 59)){
10770                         ctrl.$setValidity('inValidTime',false);   
10771                     }else{
10772                         ctrl.$setValidity('inValidTime',true);
10773                     }
10774                 }else if(format =="24"){
10775                     if(!(modelValue[0] <= 23) || !(modelValue[1] <= 59)){
10776                         ctrl.$setValidity('inValidTime',false);
10777                     }else{
10778                         ctrl.$setValidity('inValidTime',true);
10779                     }
10780                 }                
10781                scope.$apply();
10782             });
10783         }
10784     }
10785 });
10786
10787 /**
10788  * @ngdoc directive
10789  * @name Forms.att:tooltipsForForms
10790  *
10791  * @description
10792  *  <file src="src/tooltipsForForms/docs/readme.md" />
10793  *
10794  * @example
10795  <example module="b2b.att">
10796  <file src="src/tooltipsForForms/docs/demo.html" />
10797  <file src="src/tooltipsForForms/docs/demo.js" />
10798  </example>
10799  */
10800 angular.module('b2b.att.tooltipsForForms', ['b2b.att.utilities'])
10801         .directive('b2bTooltip', ['$document', '$window', '$isElement', function ($document, $window, $isElement) {
10802                 return  {
10803                     restrict: 'A',
10804                     link: function (scope, elem, attr, ctrl) {
10805                         var icon = elem[0].querySelector('a.tooltip-element');
10806                         var btnIcon = elem[0].querySelector('.btn.tooltip-element');
10807                         var tooltipText = elem[0].querySelector('.helpertext');
10808                         var tooltipWrapper = elem[0].querySelector('.tooltip-size-control');
10809                         if (elem.hasClass('tooltip-onfocus')) {
10810                             var inputElm = angular.element(elem[0].querySelector("input"));
10811                             var textAreaElm = angular.element(elem[0].querySelector("textarea"));
10812                         }
10813                         angular.element(icon).attr({'aria-expanded': false});
10814                         angular.element(btnIcon).attr({'aria-expanded': false});
10815                         var calcTooltip = function () {
10816                             if (!elem.hasClass('tooltip active')) {
10817                                 if (elem.hasClass('tooltip-onfocus')) {
10818                                     angular.element(elem[0].querySelector("input")).triggerHandler('focusout');
10819                                 }
10820                                 if (elem.hasClass('tooltip-onclick')) {
10821                                     return false;
10822                                 }
10823                                 angular.element(icon).removeClass('active');
10824                                 angular.element(icon).attr({'aria-expanded': true});
10825                                 angular.element(icon).attr({'aria-describedby': angular.element(tooltipText).attr('id')});
10826                                 angular.element(tooltipText).attr({'aria-hidden': false});
10827                                 elem.addClass('active');
10828
10829                                 var tooltipIconPos = angular.element(icon).prop('offsetLeft'),
10830                                         tooltipPosition = angular.element(tooltipText).prop('offsetWidth') / 2,
10831                                         tipOffset = (tooltipIconPos - 30) - tooltipPosition,
10832                                         maxRightPos = (($window.innerWidth - 72) - (tooltipPosition * 2)) - 14.5;
10833
10834                                 if ($window.innerWidth >= '768') {
10835                                     if (tipOffset < 0) {// if icon on far left side of page
10836                                         tipOffset = 15;
10837                                     }
10838                                     else if (tooltipIconPos > maxRightPos) {// if icon is far right side of page
10839                                         tipOffset = maxRightPos;
10840                                     }
10841                                     else {// if tooltip in the middle somewhere
10842                                         tipOffset = tipOffset;
10843                                     }
10844                                     angular.element(tooltipWrapper).css({left: tipOffset + 'px'});
10845                                 }
10846                             }
10847                         };
10848                         
10849                         // TOOLTIP LINK ONCLICK AND FOCUS
10850                         angular.element(icon).on('click mouseover mouseout focus blur', function (e) {
10851                             if (e.type == 'mouseover') {
10852                                 calcTooltip();
10853                             }
10854                             else if (e.type == 'mouseout' && elem.hasClass('active')) {
10855                                 if (!elem.hasClass('activeClick')) {
10856                                     angular.element(tooltipText).attr({
10857                                         'aria-hidden': true,
10858                                         'tabindex': '-1'
10859                                     });
10860                                     elem.removeClass('active');
10861                                 } else if (elem.hasClass('activeClick') && navigator.userAgent.match(/iphone/i)) {
10862                                     elem.removeClass('active activeClick');
10863                                 }
10864                             }
10865
10866                             else {
10867                                 if (elem.hasClass('activeClick')) {
10868                                     angular.element(icon).attr({'aria-expanded': false});
10869                                     angular.element(tooltipText).attr({'aria-hidden': true});
10870                                     angular.element(icon).removeAttr('aria-describedby');
10871                                     elem.removeClass('activeClick active');
10872                                     e.preventDefault();
10873                                 }
10874                                 else if (e.type == 'click') {
10875                                     elem.addClass('activeClick');
10876                                     calcTooltip();
10877                                     e.preventDefault();
10878                                 }
10879                                 else {
10880                                     angular.element(icon).on('keydown', function (e) {
10881                                         if (e.keyCode == '32') {
10882                                             (elem.hasClass('active')) ? elem.removeClass('active') : elem.addClass('value');
10883                                             angular.element(icon).triggerHandler('click');
10884                                             e.preventDefault();
10885                                         } else if (e.keyCode == '27') {
10886                                             (elem.hasClass('active')) ? elem.removeClass('active activeClick') : elem.addClass('value');
10887                                         }
10888                                     });
10889                                     e.preventDefault();
10890                                 }
10891                             }
10892                             e.preventDefault();
10893                         });
10894
10895                         // TOOLTIP BUTTON INSIDE A TEXT FIELD
10896                         angular.element(btnIcon).on('click', function (e) {
10897                             var $this = angular.element(this);
10898                             if ($this.hasClass('active') && elem.hasClass('tooltip-onclick')) {
10899                                 elem.removeClass('active');
10900                                 $this.removeClass('active');
10901                                 angular.element(tooltipText).removeAttr('aria-live');
10902                                 $this.attr({'aria-expanded': 'false'});
10903                                 $this.removeAttr('aria-describedby');
10904                             } else {
10905                                 elem.addClass('active');
10906                                 $this.addClass('active');
10907                                 $this.attr({'aria-expanded': 'true', 'aria-describedby': angular.element(tooltipText).attr('id')});
10908                                 angular.element(tooltipText).attr({'aria-live': 'polite'});
10909                             }
10910                         });
10911
10912                         angular.element(btnIcon).on('blur', function (e) {
10913                             var $this = angular.element(this);
10914                             if ($this.hasClass('active') && elem.hasClass('tooltip-onclick')) {
10915                                 elem.removeClass('active');
10916                                 $this.removeClass('active');
10917                                 angular.element(tooltipText).removeAttr('aria-live');
10918                                 $this.attr({'aria-expanded': 'false'});
10919                                 $this.removeAttr('aria-describedby');
10920                             }
10921                         });  
10922
10923                         angular.element(btnIcon).on('keydown', function (e) {
10924                             var $this = angular.element(this);
10925                             if (e.keyCode == '27') {
10926                                 var $this = angular.element(this);
10927                                 if ($this.hasClass('active') && elem.hasClass('tooltip-onclick')) {
10928                                     elem.removeClass('active');
10929                                     $this.removeClass('active');
10930                                     angular.element(tooltipText).removeAttr('aria-live');
10931                                     $this.attr({'aria-expanded': 'false'});
10932                                     $this.removeAttr('aria-describedby');
10933                                 }
10934                             }
10935                         });
10936
10937                         // close all tooltips if clicking something else
10938                         $document.bind('click', function (e) {
10939                             var isElement = $isElement(angular.element(e.target), elem, $document);
10940                             if (!isElement) {
10941                                 elem.removeClass('active');
10942                                 angular.element(elem[0].querySelector('.tooltip-element')).removeClass('active');
10943                                 angular.element(tooltipText).removeAttr('aria-live');
10944                                 angular.element(elem[0].querySelector('.tooltip-element')).attr({'aria-expanded': 'false'});
10945                                 angular.element(elem[0].querySelector('.tooltip-element')).removeAttr('aria-describedby');
10946                             };
10947                         });
10948
10949                         angular.element(inputElm).on('keydown', function (e) {
10950                             if (e.keyCode == '27'){
10951                                 elem.removeClass('active');
10952                                 angular.element(tooltipText).css('display', 'none');
10953                                 angular.element(tooltipText).removeAttr('aria-live');
10954
10955                                 if (angular.element(this).attr('aria-describedby') === undefined){
10956
10957                                 }
10958
10959                                 else if ((spaceIndex = angular.element(this).attr('aria-describedby').lastIndexOf(' ')) >= 0){
10960
10961                                     var describedByValue = angular.element(this).attr('aria-describedby').slice(0, spaceIndex);
10962
10963                                     angular.element(this).attr('aria-describedby', describedByValue);
10964
10965                                 }
10966                                 else {
10967                                     angular.element(this).removeAttr('aria-describedby');
10968                                 }
10969                             }
10970                         });
10971
10972                         angular.element(textAreaElm).on('keydown', function (e) {
10973                             if (e.keyCode == '27'){
10974                                 elem.removeClass('active');
10975                                 angular.element(tooltipText).css('display', 'none');
10976                                 angular.element(tooltipText).removeAttr('aria-live');
10977                                 if (angular.element(this).attr('aria-describedby') === undefined){
10978
10979                                 }
10980
10981                                 else if ((spaceIndex = angular.element(this).attr('aria-describedby').lastIndexOf(' ')) >= 0){
10982
10983                                     var describedByValue = angular.element(this).attr('aria-describedby').slice(0, spaceIndex);
10984
10985                                     angular.element(this).attr('aria-describedby', describedByValue);
10986
10987                                 }
10988                                 else {
10989                                     angular.element(this).removeAttr('aria-describedby');
10990                                 }
10991                             }
10992                         });
10993
10994                         // TOOLTIP TRIGGERED AUTOMATICALLY INSIDE A TEXT FIELD
10995                         angular.element(inputElm).on('focus', function (e) {
10996                             var allTooltip = $document[0].querySelectorAll('[class*="tooltip"]');
10997                             for (var i = 0; i < allTooltip.length; i++) {
10998                                 if (angular.element(allTooltip[i]).hasClass('active')) {
10999                                     angular.element(allTooltip[i]).triggerHandler('click');
11000                                 }
11001                             };
11002                             angular.element(this).attr({'aria-describedby': angular.element(tooltipText).attr('id')});
11003                             angular.element(tooltipText).css('display', 'block');
11004                             angular.element(tooltipText).attr({'aria-live': 'polite'});
11005                             elem.addClass('active');
11006                         });
11007                         angular.element(inputElm).on('blur', function (e) {
11008                             elem.removeClass('active');
11009                             angular.element(tooltipText).css('display', 'none');
11010                             angular.element(tooltipText).removeAttr('aria-live');
11011                             angular.element(this).removeAttr('aria-describedby');
11012                         });
11013
11014                         // TOOLTIP TRIGGERED AUTOMATICALLY INSIDE A TEXTAREA
11015                         angular.element(textAreaElm).on('focus', function (e) {
11016                             var allTooltip = $document[0].querySelectorAll('[class*="tooltip"]');
11017                             for (var i = 0; i < allTooltip.length; i++) {
11018                                 if (angular.element(allTooltip[i]).hasClass('active')) {
11019                                     angular.element(allTooltip[i]).triggerHandler('click');
11020                                 }
11021                             };
11022                             elem.addClass('active');
11023                             angular.element(tooltipText).css('display', 'block');
11024                             angular.element(tooltipText).attr({'aria-live': 'polite'});
11025                             angular.element(this).attr({'aria-describedby': angular.element(tooltipText).attr('id')});
11026                         });
11027                         angular.element(textAreaElm).on('blur', function (e) {
11028                             elem.removeClass('active');
11029                             angular.element(tooltipText).css('display', 'none');
11030                             angular.element(tooltipText).removeAttr('aria-live');
11031                             angular.element(this).removeAttr('aria-describedby');
11032                         });
11033                     }
11034                 };
11035             }]); 
11036 /**
11037  * @ngdoc directive
11038  * @name Navigation.att:TreeNavigation
11039  *
11040  *
11041  * @scope
11042  * @param {String} setRole - This value needs to be "tree". This is required to incorporate CATO requirements.
11043  * @param {Boolean} groupIt - This value needs to be "false" for top-level tree rendered.
11044  *
11045  * @description
11046  *  <file src="src/treeNav/docs/readme.md" />
11047  *
11048  * @usage
11049  *      <div class="b2b-tree">
11050  *                <b2b-tree-nav collection="treeStructure" set-role="tree" group-it="false"></b2b-tree-nav>
11051  *            </div>
11052  * @example
11053  *  <section id="code">
11054         <example module="b2b.att">
11055             <file src="src/treeNav/docs/demo.html" />
11056             <file src="src/treeNav/docs/demo.js" />
11057        </example>
11058     </section>
11059  *
11060  */
11061 angular.module('b2b.att.treeNav', ['b2b.att.utilities'])
11062     .directive('b2bTreeNav', function () {
11063         return {
11064             restrict: "E",
11065             replace: true,
11066             scope: {
11067                 collection: '=',
11068                 groupIt: '=',
11069                 setRole: '@'
11070             },
11071             templateUrl: function (element, attrs) {
11072                 if (attrs.groupIt === 'true') {
11073                     return "b2bTemplate/treeNav/groupedTree.html";
11074                 } else {
11075                     return "b2bTemplate/treeNav/ungroupedTree.html";
11076                 }
11077             },
11078             link: function (scope) {               
11079                 if (!(scope.setRole === 'tree')) {
11080                     scope.setRole = 'group';
11081                 }             
11082             }
11083         }
11084     })
11085     .directive('b2bMember', ['$compile', '$timeout', 'keymap', function ($compile, $timeout, keymap) {
11086         return {
11087             restrict: "E",
11088             replace: true,
11089             scope: {
11090                 member: '=',
11091                 groupIt: '='
11092             },
11093             templateUrl: 'b2bTemplate/treeNav/treeMember.html',
11094             link: function (scope, element, attrs) {
11095                 scope.elemArr = [];
11096                 var removeRootTabIndex = function (elem) {
11097                     if (elem.parent().parent().eq(0).hasClass('b2b-tree')) {
11098                         elem.attr('tabindex', -1);                        
11099                         return;
11100                     }
11101                     removeRootTabIndex(elem.parent());
11102                 };
11103                 scope.$watch('member.child', function(newVal, oldVal){                  
11104                     if(newVal !== oldVal){
11105                         scope.showChild();
11106                     };
11107                 });
11108                 scope.showChild = function () {
11109                         if (!element.hasClass('grouped')) {
11110                             if (angular.isArray(scope.member.child) && scope.member.child.length > 0 && (scope.member.divide === undefined || scope.member.child.length < scope.member.divide)) {
11111                                 scope.groupIt = false;
11112                                 element.addClass('grouped');
11113                                 element.append("<b2b-tree-nav collection='member.child' group-it='" + scope.groupIt + "'></b2b-tree-nav>");
11114                                 $compile(element.contents())(scope);
11115                                 if(scope.member.active && scope.member.active === true){
11116                                     element.find('i').eq(0).removeClass('icon-primary-collapsed');
11117                                 };
11118                                 if(scope.member.selected && scope.member.selected === true){
11119                                     element.attr('aria-selected', true);
11120                                     element.attr('tabindex', 0);
11121                                     removeRootTabIndex(element);
11122                                 };
11123                                 if(scope.member.active && scope.member.active == undefined){
11124                                     element.find('i').eq(0).addClass('icon-primary-collapsed');
11125                                 };
11126                             } else if (scope.member.child && scope.member.divide && scope.member.child.length > scope.member.divide) {
11127                                 element.addClass('grouped');
11128                                 scope.groupIt = true;
11129                                 // FILTER - GROUPBY - APPROACH 
11130                                 var j = 0;
11131                                 var grpName = '';
11132                                 if(scope.member.child[0].groupName !== undefined){
11133                                     grpName = scope.member.child[0].groupName;
11134                                 }
11135                                 else{
11136                                     var toSlice = scope.member.child[0].name.search(' ');
11137                                     grpName = scope.member.child[0].name.slice(0, toSlice);
11138                                 }
11139
11140                                 for (i = 0; i < scope.member.child.length; i += scope.member.divide) {
11141                                     j = 0;
11142                                     for (j = j + i; j < (i + scope.member.divide); j++) {                                        
11143                                         if (j === scope.member.child.length) {
11144                                             scope.member.child[j - 1].grpChild = grpName + ' ' + (i + 1) + ' - ' + (scope.member.child.length);
11145                                             break;
11146                                             
11147                                             if(scope.member.child[j-1].active && scope.member.child[j-1].active===true){
11148                                                 scope.member.child[j-1].activeGrp = true;
11149                                             };
11150                                             
11151                                         }
11152                                         if (i + scope.member.divide > scope.member.child.length) {
11153                                             scope.member.child[j].grpChild = grpName + ' ' + (i + 1) + ' - ' + (scope.member.child.length);
11154                                             if(scope.member.child[j].active && scope.member.child[j].active===true){
11155                                                 scope.member.child[j].activeGrp = true;
11156                                             };
11157
11158                                         } else {
11159                                             scope.member.child[j].grpChild = grpName + ' ' + (i + 1) + ' - ' + (i + scope.member.divide);
11160                                             if(scope.member.child[j].active && scope.member.child[j].active===true){
11161                                                 scope.member.child[j].activeGrp = true;
11162                                             };
11163                                         }
11164                                     }
11165                                 }
11166                                 if(scope.member.divide){
11167                                     element.append("<b2b-tree-nav collection='member.child' group-it='" + scope.groupIt + "'></b2b-tree-nav>");
11168                                 } else {
11169                                     element.append("<b2b-tree-nav collection='member.child' group-it='" + scope.groupIt + "'></b2b-tree-nav>");
11170                                 }
11171                                 $compile(element.contents())(scope);
11172                                 if(scope.member.active && scope.member.active === true){
11173                                     element.find('i').eq(0).removeClass('icon-primary-collapsed');
11174                                 };
11175                                 if(scope.member.selected && scope.member.selected === true){
11176                                     element.attr('aria-selected', true);
11177                                 };
11178                                 if( scope.member.active && scope.member.active == undefined){
11179                                     element.find('i').eq(0).addClass('icon-primary-collapsed');
11180                                 };
11181                             }
11182                         }
11183                 };
11184                 //Below condition opens node for opening on json load.
11185                 if(scope.member.active && scope.member.active == true){
11186                     scope.showChild();
11187                 };
11188                 if(scope.member.active == undefined && !element.find('a').eq(0).hasClass('active') && scope.member.child !== undefined){
11189                     element.find('i').eq(0).addClass('icon-primary-collapsed');
11190                 }
11191                 else if(scope.member.child == undefined){
11192                     element.find('i').eq(0).addClass('icon-primary-circle');
11193                 };
11194                 element.bind('keydown', function (evt) {
11195                     switch (evt.keyCode) {
11196                         case keymap.KEY.ENTER:
11197                             if (element.hasClass('bg') && scope.member.onSelect !== undefined) {
11198                                 scope.member.onSelect(scope.member);
11199                             }
11200                             evt.stopPropagation();
11201                             break;
11202                         default: 
11203                             break;                            
11204                     }
11205                     
11206                 });
11207                 //else getting true in every case .. so better use switch case .. that makes more sense you dumb.
11208                 element.bind('click', function (evt) {
11209                     scope.showChild();
11210                     var expandFunc = scope.member.onExpand;
11211                     
11212                     //onSelect
11213                         if (element.hasClass('bg') && scope.member.onSelect !== undefined) {
11214                                     scope.member.onSelect(scope.member);
11215                                 }
11216                         if (element.find('a').eq(0).hasClass('active') && scope.member.onExpand !== undefined) {
11217                            var eValue = scope.member.onExpand(scope.member);
11218                         }
11219                         if (!element.find('a').eq(0).hasClass('active') && scope.member.onCollapse !== undefined) {
11220                             scope.member.onCollapse(scope.member);
11221                         }
11222                 });
11223             }
11224         }
11225 }])
11226     .directive('b2bTreeLink', ['keymap', '$timeout', function (keymap, $timeout) {
11227         return {
11228             restrict: 'A',
11229             link: function (scope, element, attr, ctrl) {
11230                 var rootE, parentE, upE, downE;
11231                 var closeOthersUp = function (elem,isKeyPress,passiveClose) {
11232                     //For accordion functionality on sibling nodes
11233                     if (elem.find('a').eq(0).hasClass('active')) {
11234                         activeToggle(elem,isKeyPress,passiveClose);
11235                         return;
11236                     }
11237                     if (elem.hasClass('bg') && !isKeyPress) {
11238                         elem.removeClass('bg');
11239                         if (elem.attr('aria-selected')) {
11240                             elem.attr('aria-selected', 'false');
11241                         }                        
11242                     }
11243                     if (elem[0].previousElementSibling !== null) {
11244                         closeOthersUp(angular.element(elem[0].previousElementSibling),isKeyPress);
11245                     }
11246                 };
11247                 var closeOthersDown = function (elem,isKeyPress,passiveClose) {
11248                     //For accordion functionality on sibling nodes
11249                     if (elem.find('a').eq(0).hasClass('active')) {
11250                         activeToggle(elem,isKeyPress,passiveClose);
11251                         return;
11252                     }
11253                     if (elem.hasClass('bg') && !isKeyPress) {
11254                         elem.removeClass('bg');
11255                         if (elem.attr('aria-selected')) {
11256                             elem.attr('aria-selected', 'false');
11257                         }                        
11258                     }
11259                     if (elem[0].nextElementSibling !== null) {
11260                         closeOthersDown(angular.element(elem[0].nextElementSibling),isKeyPress);
11261                     }
11262                 };
11263
11264                
11265                 var removeBackground = function(elem){
11266
11267                     if(elem.hasClass('b2b-tree')){
11268                         angular.element(elem[0].getElementsByClassName('bg')).removeClass('bg');
11269                         return;
11270                     }else{
11271                         removeBackground(elem.parent().parent());
11272                     }
11273
11274                 };
11275
11276 /**
11277 * These two functions used for setting heights on parent nodes as the child node closes
11278 * Retaining this code for future reference
11279
11280                 var addParentHeight = function(e, h) {
11281                     var parentLi = e.parent().parent();
11282                     var parentUl = e.parent();
11283                     if(!parentLi.hasClass('b2b-tree')) {
11284                         var addHeight = parentUl[0].offsetHeight + h;
11285                         parentLi.find('ul').eq(0).css({
11286                             height: addHeight+'px'
11287                         })
11288                         addParentHeight(parentLi, h);
11289                     }                    
11290                 };
11291
11292                 var removeParentHeight = function(e, h) {
11293                     var parentLi = e.parent().parent();
11294                     var parentUl = e.parent();
11295                     if(!parentLi.hasClass('b2b-tree')) {
11296                         var addHeight = parentUl[0].offsetHeight - h;
11297                         parentLi.find('ul').eq(0).css({
11298                             height: addHeight+'px'
11299                         })
11300                         removeParentHeight(parentLi, h);
11301                     }
11302                 };
11303 */          
11304
11305             // isKeyPress - to notify that the function is called by Right Key press
11306             // passiveClose -  prevents firing of oncollapse events during the action
11307             // of expand function(check the function definition)
11308
11309                 var activeToggle = function (elem,isKeyPress,passiveClose) {
11310                     var element = elem.find('a').eq(0);
11311                     if (element.hasClass('active')) {
11312                         if(!isKeyPress){
11313                             elem.removeClass('bg');
11314                         }
11315                         
11316                         if (elem.attr('aria-selected') && !isKeyPress) {
11317                             elem.attr('aria-selected', 'false');
11318                         }
11319                         if (!element.find('i').eq(0).hasClass('icon-primary-circle')) {
11320                             if(isKeyPress && scope.member){
11321                                 if (scope.member.onCollapse !== undefined && !passiveClose) {
11322                                     scope.member.onCollapse(scope.member);
11323                                 }
11324                             }
11325                             element.removeClass('active');
11326                             elem.attr('aria-expanded', 'false');
11327                             element.find('i').eq(0).removeClass('icon-primary-expanded');
11328                             element.find('i').eq(0).addClass('icon-primary-collapsed');
11329                             //For Animation: below commented code is used to manually set height of UL to zero 
11330                             //retaining code for future reference
11331                             /*
11332                             var totalHeight = elem.find('ul')[0].scrollHeight;
11333                             removeParentHeight(elem, totalHeight);
11334                             elem.find('ul').eq(0).css({
11335                                 height: null
11336                             });*/
11337                         }
11338                     } else {
11339                         if(!isKeyPress){
11340                             elem.addClass('bg');
11341                             elem.attr('aria-selected', 'true');
11342                         }
11343                         
11344                         if (!element.find('i').eq(0).hasClass('icon-primary-circle')) {
11345                             if(isKeyPress){
11346                                 if(typeof scope.showChild === 'function' ){
11347                                 scope.showChild();
11348                                 }
11349                                 if(scope.member){
11350                                     if (scope.member.onExpand !== undefined) {
11351                                         scope.member.onExpand(scope.member);
11352                                     }
11353                                 }
11354                             }
11355                             element.addClass('active');
11356                             elem.attr('aria-expanded', 'true');
11357                             element.find('i').eq(0).removeClass('icon-primary-collapsed');
11358                             element.find('i').eq(0).addClass('icon-primary-expanded');
11359                             //For Animation: below commented code is used to manually set height of the ul generatedon the click of parent LI.
11360                             //retaining code for future reference
11361                             /*                            
11362                             var totalHeight = elem.find('ul')[0].scrollHeight;
11363                             addParentHeight(elem, totalHeight);
11364                             elem.find('ul').eq(0).css({
11365                                 height: totalHeight+'px'
11366                             });*/
11367                             
11368                         }
11369                     }
11370                 };
11371                 element.bind('click', function (evt) {
11372                     //first we close others and then we open the clicked element
11373                     if (element[0].previousElementSibling) {
11374                         closeOthersUp(angular.element(element[0].previousElementSibling));
11375                     }
11376                     if (element[0].nextElementSibling) {
11377                         closeOthersDown(angular.element(element[0].nextElementSibling));
11378                     }
11379                     removeBackground(element);
11380                     activeToggle(element);                    
11381                     
11382                     evt.stopPropagation();                    
11383                 });
11384                 //default root tree element tabindex set zero
11385                 if (element.parent().parent().hasClass('b2b-tree') && (element.parent()[0].previousElementSibling === null)) {
11386                     element.attr('tabindex', 0);
11387                 }
11388                 //check root via class
11389                 var isRoot = function (elem) {
11390                     if (elem.parent().parent().eq(0).hasClass('b2b-tree')) {
11391                         return true;
11392                     } else {
11393                         return false;
11394                     }
11395                 };
11396                 var findRoot = function (elem) {
11397                     if (isRoot(elem)) {
11398                         rootE = elem;
11399                         return;
11400                     }
11401                     findRoot(elem.parent());
11402                 };
11403
11404                 var findPreActive = function (elem) {
11405
11406                     if (!(elem.hasClass("active"))) {
11407                         return;
11408                     } else {
11409                         var childElems = angular.element(elem[0].nextElementSibling.children);
11410                         lastE = angular.element(childElems[childElems.length - 1]);
11411                         if (lastE.find('a').eq(0).hasClass('active')) {
11412                             findPreActive(lastE.find('a').eq(0));
11413                         }
11414                         upE = lastE;
11415                     }
11416                 };
11417
11418                 var findUp = function (elem) {
11419                     if (isRoot(elem)) {
11420                         upE = elem;
11421                         return;
11422                     }
11423                     if (elem[0].previousElementSibling !== null && !angular.element(elem[0].previousElementSibling).hasClass('tree-hide')) {
11424                         upE = angular.element(elem[0].previousElementSibling);
11425                         if (upE.find('a').eq(0).hasClass('active')) {
11426                             findPreActive(upE.find('a').eq(0));
11427                         }
11428                     } else {
11429                         upE = elem.parent().parent();
11430                     }
11431                 };
11432
11433                 var downElement = function (elem) {
11434                     if (elem.next().hasClass('tree-hide')) {
11435                         downElement(elem.next());
11436                     } else {
11437                         downE = elem.next();
11438                     }
11439                 }
11440                 var isBottomElem = false;
11441                 var downParent = function(liElem){
11442                     if(liElem.eq(0).parent().parent().eq(0).hasClass('b2b-tree')){
11443                         isBottomElem = true;
11444                         return;
11445                     }
11446                     if(liElem.next().length !== 0){
11447                         downE = liElem.next().eq(0);
11448                         return;
11449                     }
11450                     else {
11451                         downParent(liElem.parent().parent());
11452                     }
11453                 }
11454                 
11455                 var findDown = function (elem) {
11456                     if (isRoot(elem.parent()) && !elem.hasClass('active')) {
11457                         downE = elem.parent();
11458                         return;
11459                     }
11460                     if (elem.hasClass('active')) {
11461                         downE = elem.next().find('li').eq(0);
11462                         if (downE.hasClass('tree-hide')) {
11463                             downElement(downE);
11464                         }
11465
11466                     } else {
11467                         downParent(elem.parent());
11468                         if(isBottomElem === true){
11469                             downE = elem.parent();
11470                             isBottomElem = false;
11471                         }
11472                     }
11473                 };
11474
11475
11476                 var resetTabPosition = function(element){
11477                     findRoot(element);
11478                     angular.element(rootE.parent().parent()[0].querySelector("li[tabindex='0']")).attr('tabindex','-1');
11479                     var elemToFocus =  rootE.parent().parent()[0].querySelector(".bg")|| rootE;
11480
11481                     angular.element(elemToFocus).attr('tabindex','0');
11482                 };
11483                 // Function to control the expansion of nodes when the user tabs into the tree and
11484                 // the slected node is not visible
11485                 var expand = function(elemArr){
11486                     var elem= elemArr.pop();
11487                     var element = elem.find('a').eq(0);                    
11488                     var selectedNode = elem.parent().parent()[0].querySelector(".bg");
11489                     if(selectedNode != null){
11490                         while(elem){
11491                              element = elem.find('a').eq(0);
11492                     if(!element.hasClass('active') ){
11493
11494
11495                     if (elem[0].previousElementSibling) {
11496                         closeOthersUp(angular.element(elem[0].previousElementSibling),true,true);
11497                         }
11498                         if (elem[0].nextElementSibling) {
11499                             closeOthersDown(angular.element(elem[0].nextElementSibling),true,true);
11500                         }
11501
11502                          if (!element.find('i').eq(0).hasClass('icon-primary-circle')) {
11503                             if(typeof scope.showChild === 'function' ){
11504                                 scope.showChild();
11505                             }
11506                             element.addClass('active');
11507                             elem.attr('aria-expanded', 'true');
11508                             element.find('i').eq(0).removeClass('icon-primary-collapsed');
11509                             element.find('i').eq(0).addClass('icon-primary-expanded');
11510                             }
11511                           
11512                           }   
11513                           elem = elemArr.pop();
11514                         }                      
11515                         
11516                     }else{
11517                         return;
11518                     }                   
11519                 };
11520
11521                 element.find('a').eq(0).bind('mouseenter', function (evt) {
11522                     angular.forEach(document.querySelectorAll('.activeTooltip'), function(value, key) {
11523                         angular.element(value).removeClass('activeTooltip') 
11524                     });
11525                     element.addClass('activeTooltip');
11526                 });
11527                 element.find('a').eq(0).bind('mouseleave', function (evt) {
11528                     element.removeClass('activeTooltip');
11529                 });
11530                 element.bind('focus', function (evt) {
11531                     angular.forEach(document.querySelectorAll('.activeTooltip'), function(value, key) {
11532                         angular.element(value).removeClass('activeTooltip') 
11533                     });
11534                     element.addClass('activeTooltip');
11535                 });
11536                 element.bind('blur', function (evt) {
11537                     element.removeClass('activeTooltip');
11538                 });
11539                 element.bind('keydown', function (evt) {
11540                     switch (evt.keyCode) {
11541                     case keymap.KEY.HOME:
11542                         evt.preventDefault();
11543                         evt.stopPropagation();
11544                         element.attr('tabindex', -1);
11545                         findRoot(element);
11546                         rootE.eq(0).attr('tabindex', 0);
11547                         rootE[0].focus();
11548                         break;
11549                     case keymap.KEY.LEFT:
11550                         evt.preventDefault();
11551                         evt.stopPropagation(); 
11552                       
11553                         if(element.find('a').eq(0).hasClass('active')){
11554                             if (element[0].previousElementSibling) {
11555                                 closeOthersUp(angular.element(element[0].previousElementSibling),true);
11556                             }
11557                             if (element[0].nextElementSibling) {
11558                                 closeOthersDown(angular.element(element[0].nextElementSibling),true);
11559                              }
11560                              activeToggle(element,true);
11561                                 return;
11562                         }
11563                             element.attr('tabindex', -1);
11564                             parentE = element.parent().parent();
11565                             parentE.attr('tabindex', 0);
11566                             parentE[0].focus();
11567                         break;
11568                     case keymap.KEY.UP:
11569                         evt.preventDefault();
11570                         evt.stopPropagation();
11571                         element.attr('tabindex', -1);
11572                         findUp(element);
11573                         upE.eq(0).attr('tabindex', 0);
11574                         upE[0].focus();
11575                         break;
11576                     case keymap.KEY.RIGHT:
11577                         evt.preventDefault();
11578                         evt.stopPropagation();
11579                         if(element.find('i').eq(0).hasClass('icon-primary-circle')){
11580                             break;
11581                         }    
11582                         if (!element.find('a').eq(0).hasClass('active')) {
11583                             if (element[0].previousElementSibling) {
11584                         closeOthersUp(angular.element(element[0].previousElementSibling),true);
11585                         }
11586                         if (element[0].nextElementSibling) {
11587                             closeOthersDown(angular.element(element[0].nextElementSibling),true);
11588                         }
11589                         activeToggle(element,true);
11590                     
11591                         }
11592                         else {
11593                             element.attr('tabindex', -1);
11594                             findDown(element.find('a').eq(0));
11595                             downE.eq(0).attr('tabindex', 0);
11596                             downE[0].focus();                            
11597                         }                        
11598                         break;
11599                     case keymap.KEY.DOWN:
11600                         evt.preventDefault();
11601                         element.attr('tabindex', -1);
11602                         findDown(element.find('a').eq(0));
11603                         downE.eq(0).attr('tabindex', 0);
11604                         downE[0].focus();
11605                         evt.stopPropagation();
11606                         break;
11607                     case keymap.KEY.ENTER:
11608                         var isSelectedElem = element.hasClass('bg');
11609                         var enterFunc = function(element){
11610                             if (isSelectedElem) {
11611                                 element.removeClass('bg');
11612                                 if (element.attr('aria-selected')) {
11613                                     element.attr('aria-selected', 'false');
11614                                 }                        
11615                             }
11616                             else {
11617                                 element.addClass('bg');
11618                                 element.attr('aria-selected', 'true');                                   
11619                             }  
11620                         };                            
11621                         if (element[0].previousElementSibling) {
11622                             closeOthersUp(angular.element(element[0].previousElementSibling));
11623                         }
11624                         if (element[0].nextElementSibling) {
11625                             closeOthersDown(angular.element(element[0].nextElementSibling));
11626                         }                   
11627                         
11628                         removeBackground(element);
11629                         enterFunc(element);
11630                         evt.stopPropagation();                                                      
11631                         break;
11632                     case keymap.KEY.TAB:
11633                         $timeout(function(){
11634                             resetTabPosition(element);
11635                         },0);
11636                          evt.stopPropagation(); 
11637                         
11638                         break;
11639                     default:
11640                         break;
11641                     }
11642                 });
11643             element.bind('keyup',function(evt){
11644                 if(evt.keyCode === keymap.KEY.TAB){
11645                   
11646                         var tempElem = element;
11647                         var elemArr = [];
11648                         while(!tempElem.hasClass('b2b-tree')){
11649                             elemArr.push(tempElem);
11650                             tempElem = tempElem.parent().parent();
11651                         }
11652                         elemArr.push(tempElem);
11653                       
11654                         expand(elemArr);                    
11655                 }
11656                  evt.stopPropagation(); 
11657             });
11658             }
11659         };
11660     }]);
11661 /**
11662  * @ngdoc directive
11663  * @name Navigation.att:Tree nodes with checkboxes
11664  *
11665  * @param {String} setRole - The value needs to be "tree". This is required to incorporate CATO requirements.
11666  * @param {boolean} groupIt - The value needs to be "false" for top-level tree rendered. 
11667  * @param {Object} collection -  The JSON object of tree to be rendered.
11668  * @description
11669  *  <file src="src/treeNodeCheckbox/docs/readme.md" />
11670  *
11671  * @usage
11672  *      <div class="b2b-tree-checkbox">
11673  *                <b2b-tree-node-checkbox collection="treeStructure" set-role="tree" group-it="false"></b2b-tree-node-checkbox>
11674  *            </div>
11675  * @example
11676  *  <section id="code">
11677         <example module="b2b.att">
11678             <file src="src/treeNodeCheckbox/docs/demo.html" />
11679             <file src="src/treeNodeCheckbox/docs/demo.js" />
11680        </example>
11681     </section>
11682  *
11683  */
11684 angular.module('b2b.att.treeNodeCheckbox', ['b2b.att.utilities'])
11685     .directive('b2bTreeNodeCheckbox', function () {
11686         return {
11687             restrict: "E",
11688             replace: true,
11689             scope: {
11690                 collection: '=',
11691                 groupIt: '=',
11692                 setRole: '@'
11693             },
11694             templateUrl: function (element, attrs) {
11695                 if (attrs.groupIt === 'true') {
11696                     return "b2bTemplate/treeNodeCheckbox/groupedTree.html";
11697                 } else {
11698                     return "b2bTemplate/treeNodeCheckbox/ungroupedTree.html";
11699                 }
11700             },
11701             link: function (scope) {
11702                 if (!(scope.setRole === 'tree')) {
11703                     scope.setRole = 'group';
11704                 }
11705             }
11706         }
11707     })
11708     .directive('b2bTreeMember', ['$compile', '$timeout', 'keymap', function ($compile, $timeout, keymap) {
11709         return {
11710             restrict: "E",
11711             replace: true,
11712             scope: {
11713                 member: '=',
11714                 groupIt: '='
11715             },
11716             templateUrl: 'b2bTemplate/treeNodeCheckbox/treeMember.html',
11717             link: function (scope, element, attrs) {
11718                 scope.elemArr = [];
11719                 var removeRootTabIndex = function (elem) {
11720                     if (elem.parent().parent().eq(0).hasClass('b2b-tree-checkbox')) {
11721                         elem.attr('tabindex', -1);                        
11722                         return;
11723                     }
11724                     removeRootTabIndex(elem.parent());
11725                 };
11726                 scope.$watch('member.child', function(newVal, oldVal){                  
11727                     if(newVal !== oldVal){
11728                         scope.showChild();
11729                     };
11730                 });
11731
11732                 var checkedCount = 0;
11733                 var nonCheckedCount = 0;
11734                 var checkBoxesCount = 0;
11735
11736                 if(element.find('a').eq(0).find('input')){
11737                     if(scope.member.indeterminate){
11738                         element.find('a').eq(0).find('input').prop('indeterminate', true);
11739                         element.attr('aria-checked',"mixed");
11740                     }
11741                     element.attr('aria-checked',scope.member.isSelected);
11742                 }
11743
11744                 element.find('a').eq(0).find('input').bind('change',function(){
11745                     scope.member.indeterminate = false;
11746                     downwardModalUpdate(scope.member);
11747                     downwardSelection(element);
11748                     upwardSelection(element);
11749                     element.attr('aria-checked',scope.member.isSelected);
11750                      if (scope.member.onSelect !== undefined) {
11751                         scope.member.onSelect(scope.member);
11752                     }
11753                 });
11754
11755                 element.find('a').eq(0).find('input').bind('click',function(){
11756                     var elem = angular.element(this);
11757                     if(scope.member.indeterminate){
11758                         scope.member.indeterminate = false;
11759                         scope.member.isSelected = true;
11760                         elem.prop('indeterminate', false);
11761                         elem.prop('checked', true);
11762                         elem.triggerHandler('change');
11763                     }
11764                 });
11765
11766                 var groupNode = false;
11767                 var checkedTreeNode = false;
11768
11769                 var isCheckboxSelected = function(elem){
11770                     checkedTreeNode = false;
11771                     checkedTreeNode = angular.element(angular.element(elem).find('a').eq(0))[0].querySelector('input.treeCheckBox').checked;
11772                 }
11773
11774                 var findCheckbox = function(elem){
11775                     return angular.element(angular.element(elem).find('a').eq(0))[0].querySelector('input.treeCheckBox');
11776                 }
11777
11778                 var updateGrpNodeCheckboxes = function(elem, checked){
11779                     angular.element(angular.element(elem).find('a').eq(0))[0].querySelector('input.treeCheckBox').checked = checked;
11780                 }
11781
11782                 
11783                 var isGroupNode = function(elem){
11784                     groupNode = false;
11785                     if(angular.element(angular.element(elem).find('a').eq(0))[0].querySelector('input.grpTreeCheckbox')){
11786                         groupNode = true;
11787                     }
11788                 }
11789
11790                 var downwardModalUpdate = function(curMember){
11791                     angular.forEach(curMember.child, function(childMember, key) {
11792                         childMember.isSelected = curMember.isSelected;
11793                         childMember.indeterminate = false;
11794                         if(angular.isArray(childMember.child) && scope.member.child.length > 0){
11795                             downwardModalUpdate(childMember);
11796                         }
11797                     });
11798                 }
11799
11800                 var downwardSelection = function(elem){
11801                     if(findCheckbox(elem)){
11802                         isCheckboxSelected(elem)
11803                     } 
11804                     if(angular.element(elem).find('ul').length > 0){
11805                         var childNodes = angular.element(elem).find('ul').eq(0).children('li');
11806                         for(var i=0; i<childNodes.length; i++){
11807                             if(findCheckbox(childNodes[i])){
11808                                 isGroupNode(childNodes[i]);
11809                                 angular.element(findCheckbox(childNodes[i])).prop('indeterminate', false);
11810                                 angular.element(childNodes[i]).attr('aria-checked',checkedTreeNode);
11811                                 if(groupNode){
11812                                     updateGrpNodeCheckboxes(childNodes[i],checkedTreeNode);
11813                                 }else{
11814                                     angular.element(childNodes[i]).scope().member.isSelected = checkedTreeNode;
11815                                     angular.element(childNodes[i]).scope().member.indeterminate = false
11816                                     angular.element(childNodes[i]).scope().$apply();
11817                                 }
11818                                 downwardSelection(childNodes[i]);
11819                             }
11820                         }
11821
11822                     }
11823                 }
11824                 var upwardSelection = function(elem){
11825                     var childNodes = elem.parent().parent().find('ul').eq(0).children('li');
11826                     checkedCount = 0;
11827                     nonCheckedCount = 0;
11828                     checkBoxesCount = 0;    
11829                     for(i=0; i<childNodes.length; i++){
11830                         if(findCheckbox(childNodes[i])){
11831                             isGroupNode(childNodes[i]);
11832                             isCheckboxSelected(childNodes[i]);
11833                             checkBoxesCount++;
11834                             if(checkedTreeNode){
11835                                 checkedCount++;
11836                             }else if(!angular.element(angular.element(angular.element(childNodes[i]).find('a').eq(0))[0].querySelector('input.treeCheckBox')).prop('indeterminate')){
11837                                 nonCheckedCount++;
11838                             }
11839                         }
11840                     }
11841                     var parentNodeScope;
11842                     parentNodeScope = angular.element(elem.parent().parent()).scope();
11843                     if(findCheckbox(elem.parent().parent())){
11844                         if(nonCheckedCount == checkBoxesCount){
11845                             angular.element(findCheckbox(elem.parent().parent())).prop('indeterminate', false);
11846                             if(parentNodeScope &&  parentNodeScope.member){
11847                                 parentNodeScope.member.isSelected = false;
11848                                 parentNodeScope.member.indeterminate = false;
11849                             }else{
11850                                 updateGrpNodeCheckboxes(elem.parent().parent(),false);
11851                             }
11852                             angular.element(elem.parent().parent()).attr('aria-checked',false);
11853                         }else if(checkedCount == checkBoxesCount){
11854                             angular.element(findCheckbox(elem.parent().parent())).prop('indeterminate', false);
11855                             if(parentNodeScope &&  parentNodeScope.member){
11856                                 parentNodeScope.member.isSelected = true;
11857                                 parentNodeScope.member.indeterminate = false;
11858                             }else{
11859                                 updateGrpNodeCheckboxes(elem.parent().parent(),true);
11860                             }
11861                             angular.element(elem.parent().parent()).attr('aria-checked',true);
11862                         }else{
11863                             angular.element(findCheckbox(elem.parent().parent())).prop('indeterminate', true);
11864                             if(parentNodeScope &&  parentNodeScope.member){
11865                                 parentNodeScope.member.isSelected = false;
11866                                 parentNodeScope.member.indeterminate = true;
11867                             }else{
11868                                 updateGrpNodeCheckboxes(elem.parent().parent(),false);
11869                             }
11870                             angular.element(elem.parent().parent()).attr('aria-checked',"mixed");
11871                         }
11872                         if(parentNodeScope &&  parentNodeScope.member){
11873                             parentNodeScope.$apply();
11874                         }        
11875                     }
11876                     
11877                     
11878                     
11879                     if(elem.parent().parent().attr('role') == "treeitem"){
11880                         upwardSelection(elem.parent().parent());
11881                     }
11882                 }
11883
11884                 scope.showChild = function () {
11885                         if (!element.hasClass('grouped')) {
11886                             if (angular.isArray(scope.member.child) && scope.member.child.length > 0 && (scope.member.divide === undefined || scope.member.child.length < scope.member.divide)) {
11887                                 scope.groupIt = false;
11888                                 element.addClass('grouped');
11889                                 element.append("<b2b-tree-node-checkbox collection='member.child' group-it='" + scope.groupIt + "'></b2b-tree-node-checkbox>");
11890                                 $compile(element.contents())(scope);
11891                                 if(scope.member.active && scope.member.active === true){
11892                                     angular.element(element[0].querySelectorAll('i.expandCollapseIcon')).eq(0).removeClass('icon-primary-collapsed');
11893                                 };
11894                                 if(scope.member.selected && scope.member.selected === true){
11895                                     element.attr('tabindex', 0);
11896                                     removeRootTabIndex(element);
11897                                 };
11898                                 if(scope.member.active && scope.member.active == undefined){
11899                                     angular.element(element[0].querySelectorAll('i.expandCollapseIcon')).eq(0).addClass('icon-primary-collapsed');
11900                                 };
11901                             } else if (scope.member.child && scope.member.divide && scope.member.child.length > scope.member.divide) {
11902                                 element.addClass('grouped');
11903                                 scope.groupIt = true;
11904                                 var j = 0;
11905                                 var grpName = '';
11906                                 if(scope.member.child[0].groupName !== undefined){
11907                                     grpName = scope.member.child[0].groupName;
11908                                 }
11909                                 else{
11910                                     var toSlice = scope.member.child[0].name.search(' ');
11911                                     grpName = scope.member.child[0].name.slice(0, toSlice);
11912                                 }
11913
11914                                 for (i = 0; i < scope.member.child.length; i += scope.member.divide) {
11915                                     j = 0;
11916                                     for (j = j + i; j < (i + scope.member.divide); j++) {                                        
11917                                         if (j === scope.member.child.length) {
11918                                             scope.member.child[j - 1].grpChild = grpName + ' ' + (i + 1) + ' - ' + (scope.member.child.length);
11919                                             break;
11920                                             
11921                                             if(scope.member.child[j-1].active && scope.member.child[j-1].active===true){
11922                                                 scope.member.child[j-1].activeGrp = true;
11923                                             };
11924                                             
11925                                         }
11926                                         if (i + scope.member.divide > scope.member.child.length) {
11927                                             scope.member.child[j].grpChild = grpName + ' ' + (i + 1) + ' - ' + (scope.member.child.length);
11928                                             if(scope.member.child[j].active && scope.member.child[j].active===true){
11929                                                 scope.member.child[j].activeGrp = true;
11930                                             };
11931
11932                                         } else {
11933                                             scope.member.child[j].grpChild = grpName + ' ' + (i + 1) + ' - ' + (i + scope.member.divide);
11934                                             if(scope.member.child[j].active && scope.member.child[j].active===true){
11935                                                 scope.member.child[j].activeGrp = true;
11936                                             };
11937                                         }
11938                                     }
11939                                 }
11940                                 if(scope.member.divide){
11941                                     element.append("<b2b-tree-node-checkbox collection='member.child' group-it='" + scope.groupIt + "'></b2b-tree-node-checkbox>");
11942                                 } else {
11943                                     element.append("<b2b-tree-node-checkbox collection='member.child' group-it='" + scope.groupIt + "'></b2b-tree-node-checkbox>");
11944                                 }
11945                                 $compile(element.contents())(scope);
11946                                 if(scope.member.active && scope.member.active === true){
11947                                     angular.element(element[0].querySelectorAll('i.expandCollapseIcon')).eq(0).removeClass('icon-primary-collapsed');
11948                                 };
11949                                 
11950                                 if( scope.member.active && scope.member.active == undefined){
11951                                     angular.element(element[0].querySelectorAll('i.expandCollapseIcon')).eq(0).addClass('icon-primary-collapsed');
11952                                 };
11953                             }
11954                         }
11955                         $timeout(function () {
11956                             if(!scope.member.indeterminate){
11957                                 downwardSelection(element);
11958                             }    
11959                         });  
11960
11961                 };
11962                 
11963                 if(scope.member.active && scope.member.active == true){
11964                     scope.showChild();
11965                 };
11966                 if(scope.member.active == undefined && !element.find('a').eq(0).hasClass('active') && scope.member.child !== undefined){
11967                     angular.element(element[0].querySelectorAll('i.expandCollapseIcon')).eq(0).addClass('icon-primary-collapsed');
11968                 }
11969                 else if(scope.member.child == undefined){
11970                     angular.element(element[0].querySelectorAll('i.expandCollapseIcon')).eq(0).addClass('icon-primary-circle');
11971                     if(scope.$parent.$index === 0) {
11972                         element.find('a').eq(0).append('<span class="first-link"></span>');
11973                     };
11974                 };
11975                 
11976                 angular.element(element[0].querySelectorAll('i.expandCollapseIcon')).eq(0).bind('click', function (evt) {
11977                     scope.showChild();
11978                     var expandFunc = scope.member.onExpand;
11979                     if (element.find('a').eq(0).hasClass('active') && scope.member.onExpand !== undefined) {
11980                        var eValue = scope.member.onExpand(scope.member);
11981                     }
11982                     if (!element.find('a').eq(0).hasClass('active') && scope.member.onCollapse !== undefined) {
11983                         scope.member.onCollapse(scope.member);
11984                     }
11985                 });
11986
11987                 angular.element(element[0].querySelectorAll('.treeNodeName')).eq(0).bind('click', function (evt) {
11988
11989                 });
11990                 
11991             }
11992         }
11993 }])
11994     .directive('b2bTreeNodeLink', ['keymap', '$timeout', function (keymap, $timeout) {
11995         return {
11996             restrict: 'A',
11997             link: function (scope, element, attr, ctrl) {
11998                 var rootE, parentE, upE, downE;
11999                 var closeOthersUp = function (elem) {
12000                     
12001                     if (elem.find('a').eq(0).hasClass('active')) {
12002                         activeToggle(elem);
12003                         return;
12004                     }
12005                     if (elem.hasClass('bg')) {
12006                         elem.removeClass('bg');
12007                     }
12008                     if (elem[0].previousElementSibling !== null) {
12009                         closeOthersUp(angular.element(elem[0].previousElementSibling));
12010                     }
12011                 };
12012                 var closeOthersDown = function (elem) {
12013                     
12014                     if (elem.find('a').eq(0).hasClass('active')) {
12015                         activeToggle(elem);
12016                         return;
12017                     }
12018                     if (elem.hasClass('bg')) {
12019                         elem.removeClass('bg');
12020                     }
12021                     if (elem[0].nextElementSibling !== null) {
12022                         closeOthersDown(angular.element(elem[0].nextElementSibling));
12023                     }
12024                 };
12025
12026                 var removeBackgroundUp = function (elem) {
12027                     
12028                     if (elem.hasClass('b2b-tree-checkbox')) {
12029                         return;
12030                     } else {
12031                         elem.parent().parent().removeClass('bg');
12032                         removeBackgroundUp(elem.parent().parent());
12033                     }
12034                 };
12035
12036                 var removeBackgroundDown = function (elem) {
12037                     
12038                     angular.element(elem[0].querySelector('.bg')).removeClass('bg');
12039                 };
12040
12041
12042
12043                 var activeToggle = function (elem) {
12044                     var element = elem.find('a').eq(0);
12045                     if (element.hasClass('active')) {
12046                         elem.removeClass('bg');
12047                         if (!angular.element(element[0].querySelectorAll('i.expandCollapseIcon')).eq(0).hasClass('icon-primary-circle')) {
12048                             element.removeClass('active');
12049                             elem.attr('aria-expanded', 'false');
12050                             angular.element(element[0].querySelectorAll('i.expandCollapseIcon')).eq(0).removeClass('icon-primary-expanded');
12051                             angular.element(element[0].querySelectorAll('i.expandCollapseIcon')).eq(0).addClass('icon-primary-collapsed');
12052                         }
12053                     } else {
12054                         elem.addClass('bg');
12055                         if (!angular.element(element[0].querySelectorAll('i.expandCollapseIcon')).eq(0).hasClass('icon-primary-circle')) {
12056                             element.addClass('active');
12057                             elem.attr('aria-expanded', 'true');
12058                             angular.element(element[0].querySelectorAll('i.expandCollapseIcon')).eq(0).removeClass('icon-primary-collapsed');
12059                             angular.element(element[0].querySelectorAll('i.expandCollapseIcon')).eq(0).addClass('icon-primary-expanded');
12060                         }
12061                     }
12062                 };
12063                 angular.element(element[0].querySelectorAll('i.expandCollapseIcon')).eq(0).bind('click', function (evt) {
12064                     
12065                         if (element[0].previousElementSibling) {
12066                             closeOthersUp(angular.element(element[0].previousElementSibling));
12067                         }
12068                         if (element[0].nextElementSibling) {
12069                             closeOthersDown(angular.element(element[0].nextElementSibling));
12070                         }
12071
12072                         activeToggle(element);
12073
12074                     removeBackgroundDown(element);
12075                     removeBackgroundUp(element);
12076                     evt.stopPropagation();                    
12077                 });
12078                 
12079                 if (element.parent().parent().hasClass('b2b-tree-checkbox') && (element.parent()[0].previousElementSibling === null)) {
12080                     element.attr('tabindex', 0);
12081                 }
12082                 
12083                 var isRoot = function (elem) {
12084                     if (elem.parent().parent().eq(0).hasClass('b2b-tree-checkbox')) {
12085                         return true;
12086                     } else {
12087                         return false;
12088                     }
12089                 };
12090                 var findRoot = function (elem) {
12091                     if (isRoot(elem)) {
12092                         rootE = elem;
12093                         return;
12094                     }
12095                     findRoot(elem.parent());
12096                 };
12097
12098                 var findPreActive = function (elem) {
12099
12100                     if (!(elem.hasClass("active"))) {
12101                         return;
12102                     } else {
12103                         var childElems = angular.element(elem[0].nextElementSibling.children);
12104                         lastE = angular.element(childElems[childElems.length - 1]);
12105                         if (lastE.find('a').eq(0).hasClass('active')) {
12106                             findPreActive(lastE.find('a').eq(0));
12107                         }
12108                         upE = lastE;
12109                     }
12110                 };
12111
12112                 var findUp = function (elem) {
12113                     if (isRoot(elem)) {
12114                         upE = elem;
12115                         return;
12116                     }
12117                     if (elem[0].previousElementSibling !== null && !angular.element(elem[0].previousElementSibling).hasClass('tree-hide')) {
12118                         upE = angular.element(elem[0].previousElementSibling);
12119                         if (upE.find('a').eq(0).hasClass('active')) {
12120                             findPreActive(upE.find('a').eq(0));
12121                         }
12122                     } else {
12123                         upE = elem.parent().parent();
12124                     }
12125                 };
12126
12127                 var downElement = function (elem) {
12128                     if (elem.next().hasClass('tree-hide')) {
12129                         downElement(elem.next());
12130                     } else {
12131                         downE = elem.next();
12132                     }
12133                 }
12134                 var isBottomElem = false;
12135                 var downParent = function(liElem){
12136                     if(liElem.eq(0).parent().parent().eq(0).hasClass('b2b-tree-checkbox')){
12137                         isBottomElem = true;
12138                         return;
12139                     }
12140                     if(liElem.next().length !== 0){
12141                         downE = liElem.next().eq(0);
12142                         return;
12143                     }
12144                     else {
12145                         downParent(liElem.parent().parent());
12146                     }
12147                 }
12148                 
12149                 var findDown = function (elem) {
12150                     if (isRoot(elem.parent()) && !elem.hasClass('active')) {
12151                         downE = elem.parent();
12152                         return;
12153                     }
12154                     if (elem.hasClass('active')) {
12155                         downE = elem.next().find('li').eq(0);
12156                         if (downE.hasClass('tree-hide')) {
12157                             downElement(downE);
12158                         }
12159
12160                     } else {
12161                         downParent(elem.parent());
12162                         if(isBottomElem === true){
12163                             downE = elem.parent();
12164                             isBottomElem = false;
12165                         }
12166                     }
12167                 };
12168                 element.bind('keydown', function (evt) {
12169                     switch (evt.keyCode) {
12170                     case keymap.KEY.HOME:
12171                         evt.preventDefault();
12172                         evt.stopPropagation();
12173                         element.attr('tabindex', -1);
12174                         findRoot(element);
12175                         rootE.eq(0).attr('tabindex', 0);
12176                         rootE[0].focus();
12177                         break;
12178                     case keymap.KEY.LEFT:
12179                         evt.preventDefault();
12180                         evt.stopPropagation();
12181                         if (!isRoot(element)) {
12182                             if(element.find('a').eq(0).hasClass('active')){
12183                                 angular.element(element[0].querySelectorAll('i.expandCollapseIcon')).eq(0).triggerHandler('click');
12184                                 return;
12185                             }
12186                             element.attr('tabindex', -1);
12187                             parentE = element.parent().parent();
12188                             parentE.attr('tabindex', 0);
12189                             parentE[0].focus();
12190                         } else {
12191                             if (element.find('a').eq(0).hasClass('active')) {
12192                                 angular.element(element[0].querySelectorAll('i.expandCollapseIcon')).eq(0).triggerHandler('click');
12193                             }
12194                         };
12195                         break;
12196                     case keymap.KEY.UP:
12197                         evt.preventDefault();
12198                         evt.stopPropagation();
12199                         element.attr('tabindex', -1);
12200                         findUp(element);
12201                         upE.eq(0).attr('tabindex', 0);
12202                         upE[0].focus();
12203                         break;
12204                     case keymap.KEY.RIGHT:
12205                         evt.preventDefault();
12206                         evt.stopPropagation();
12207                         if(angular.element(element[0].querySelectorAll('i.expandCollapseIcon')).eq(0).hasClass('icon-primary-circle')){
12208                             break;
12209                         }    
12210                         if (!element.find('a').eq(0).hasClass('active')) {
12211                             angular.element(element[0].querySelectorAll('i.expandCollapseIcon')).eq(0).triggerHandler('click');
12212                         }
12213                         else {
12214                             element.attr('tabindex', -1);
12215                             findDown(element.find('a').eq(0));
12216                             downE.eq(0).attr('tabindex', 0);
12217                             downE[0].focus();                            
12218                         }                        
12219                         break;
12220                     case keymap.KEY.DOWN:
12221                         evt.preventDefault();
12222                         element.attr('tabindex', -1);
12223                         findDown(element.find('a').eq(0));
12224                         downE.eq(0).attr('tabindex', 0);
12225                         downE[0].focus();
12226                         evt.stopPropagation();
12227                         break;
12228                     case keymap.KEY.SPACE:
12229                     case keymap.KEY.ENTER:
12230                         evt.preventDefault();
12231                         evt.stopPropagation();
12232                         if(angular.isDefined(element.scope().member.isSelected)){
12233                             element.scope().member.isSelected = !element.scope().member.isSelected;
12234                             element.scope().member.indeterminate = false;
12235                             element.scope().$apply();
12236                             element.find('a').eq(0).find('input').prop('indeterminate', false);
12237                             element.find('a').eq(0).find('input').triggerHandler('change');
12238                         }
12239                         break;    
12240                     default:
12241                         break;
12242                     }
12243                 });
12244             }
12245         };
12246     }]);
12247 /*!
12248  * VERSION: 1.7.3
12249  * DATE: 2014-01-14
12250  * UPDATES AND DOCS AT: http://www.greensock.com
12251  *
12252  * @license Copyright (c) 2008-2014, GreenSock. All rights reserved.
12253  * This work is subject to the terms at http://www.greensock.com/terms_of_use.html or for
12254  * Club GreenSock members, the software agreement that was issued with your membership.
12255  * 
12256  * @author: Jack Doyle, jack@greensock.com
12257  **/
12258 (window._gsQueue || (window._gsQueue = [])).push( function() {
12259
12260     "use strict";
12261
12262     var _doc = document.documentElement,
12263         _window = window,
12264         _max = function(element, axis) {
12265             var dim = (axis === "x") ? "Width" : "Height",
12266                 scroll = "scroll" + dim,
12267                 client = "client" + dim,
12268                 body = document.body;
12269             return (element === _window || element === _doc || element === body) ? Math.max(_doc[scroll], body[scroll]) - (_window["inner" + dim] || Math.max(_doc[client], body[client])) : element[scroll] - element["offset" + dim];
12270         },
12271
12272         ScrollToPlugin = window._gsDefine.plugin({
12273             propName: "scrollTo",
12274             API: 2,
12275             version:"1.7.3",
12276
12277             //called when the tween renders for the first time. This is where initial values should be recorded and any setup routines should run.
12278             init: function(target, value, tween) {
12279                 this._wdw = (target === _window);
12280                 this._target = target;
12281                 this._tween = tween;
12282                 if (typeof(value) !== "object") {
12283                     value = {y:value}; //if we don't receive an object as the parameter, assume the user intends "y".
12284                 }
12285                 this._autoKill = (value.autoKill !== false);
12286                 this.x = this.xPrev = this.getX();
12287                 this.y = this.yPrev = this.getY();
12288                 if (value.x != null) {
12289                     this._addTween(this, "x", this.x, (value.x === "max") ? _max(target, "x") : value.x, "scrollTo_x", true);
12290                     this._overwriteProps.push("scrollTo_x");
12291                 } else {
12292                     this.skipX = true;
12293                 }
12294                 if (value.y != null) {
12295                     this._addTween(this, "y", this.y, (value.y === "max") ? _max(target, "y") : value.y, "scrollTo_y", true);
12296                     this._overwriteProps.push("scrollTo_y");
12297                 } else {
12298                     this.skipY = true;
12299                 }
12300                 return true;
12301             },
12302
12303             //called each time the values should be updated, and the ratio gets passed as the only parameter (typically it's a value between 0 and 1, but it can exceed those when using an ease like Elastic.easeOut or Back.easeOut, etc.)
12304             set: function(v) {
12305                 this._super.setRatio.call(this, v);
12306
12307                 var x = (this._wdw || !this.skipX) ? this.getX() : this.xPrev,
12308                     y = (this._wdw || !this.skipY) ? this.getY() : this.yPrev,
12309                     yDif = y - this.yPrev,
12310                     xDif = x - this.xPrev;
12311
12312                 if (this._autoKill) {
12313                     //note: iOS has a bug that throws off the scroll by several pixels, so we need to check if it's within 7 pixels of the previous one that we set instead of just looking for an exact match.
12314                     if (!this.skipX && (xDif > 7 || xDif < -7) && x < _max(this._target, "x")) {
12315                         this.skipX = true; //if the user scrolls separately, we should stop tweening!
12316                     }
12317                     if (!this.skipY && (yDif > 7 || yDif < -7) && y < _max(this._target, "y")) {
12318                         this.skipY = true; //if the user scrolls separately, we should stop tweening!
12319                     }
12320                     if (this.skipX && this.skipY) {
12321                         this._tween.kill();
12322                     }
12323                 }
12324                 if (this._wdw) {
12325                     _window.scrollTo((!this.skipX) ? this.x : x, (!this.skipY) ? this.y : y);
12326                 } else {
12327                     if (!this.skipY) {
12328                         this._target.scrollTop = this.y;
12329                     }
12330                     if (!this.skipX) {
12331                         this._target.scrollLeft = this.x;
12332                     }
12333                 }
12334                 this.xPrev = this.x;
12335                 this.yPrev = this.y;
12336             }
12337
12338         }),
12339         p = ScrollToPlugin.prototype;
12340
12341     ScrollToPlugin.max = _max;
12342
12343     p.getX = function() {
12344         return (!this._wdw) ? this._target.scrollLeft : (_window.pageXOffset != null) ? _window.pageXOffset : (_doc.scrollLeft != null) ? _doc.scrollLeft : document.body.scrollLeft;
12345     };
12346
12347     p.getY = function() {
12348         return (!this._wdw) ? this._target.scrollTop : (_window.pageYOffset != null) ? _window.pageYOffset : (_doc.scrollTop != null) ? _doc.scrollTop : document.body.scrollTop;
12349     };
12350
12351     p._kill = function(lookup) {
12352         if (lookup.scrollTo_x) {
12353             this.skipX = true;
12354         }
12355         if (lookup.scrollTo_y) {
12356             this.skipY = true;
12357         }
12358         return this._super._kill.call(this, lookup);
12359     };
12360
12361 }); if (window._gsDefine) { window._gsQueue.pop()(); }
12362 /*!
12363  * VERSION: 1.12.1
12364  * DATE: 2014-06-26
12365  * UPDATES AND DOCS AT: http://www.greensock.com
12366  * 
12367  * Includes all of the following: TweenLite, TweenMax, TimelineLite, TimelineMax, EasePack, CSSPlugin, RoundPropsPlugin, BezierPlugin, AttrPlugin, DirectionalRotationPlugin
12368  *
12369  * @license Copyright (c) 2008-2014, GreenSock. All rights reserved.
12370  * This work is subject to the terms at http://www.greensock.com/terms_of_use.html or for
12371  * Club GreenSock members, the software agreement that was issued with your membership.
12372  * 
12373  * @author: Jack Doyle, jack@greensock.com
12374  **/
12375
12376 (window._gsQueue || (window._gsQueue = [])).push( function() {
12377
12378     "use strict";
12379
12380     window._gsDefine("TweenMax", ["core.Animation","core.SimpleTimeline","TweenLite"], function(Animation, SimpleTimeline, TweenLite) {
12381
12382         var _slice = [].slice,
12383             TweenMax = function(target, duration, vars) {
12384                 TweenLite.call(this, target, duration, vars);
12385                 this._cycle = 0;
12386                 this._yoyo = (this.vars.yoyo === true);
12387                 this._repeat = this.vars.repeat || 0;
12388                 this._repeatDelay = this.vars.repeatDelay || 0;
12389                 this._dirty = true; //ensures that if there is any repeat, the totalDuration will get recalculated to accurately report it.
12390                 this.render = TweenMax.prototype.render; //speed optimization (avoid prototype lookup on this "hot" method)
12391             },
12392             _tinyNum = 0.0000000001,
12393             TweenLiteInternals = TweenLite._internals,
12394             _isSelector = TweenLiteInternals.isSelector,
12395             _isArray = TweenLiteInternals.isArray,
12396             p = TweenMax.prototype = TweenLite.to({}, 0.1, {}),
12397             _blankArray = [];
12398
12399         TweenMax.version = "1.12.1";
12400         p.constructor = TweenMax;
12401         p.kill()._gc = false;
12402         TweenMax.killTweensOf = TweenMax.killDelayedCallsTo = TweenLite.killTweensOf;
12403         TweenMax.getTweensOf = TweenLite.getTweensOf;
12404         TweenMax.lagSmoothing = TweenLite.lagSmoothing;
12405         TweenMax.ticker = TweenLite.ticker;
12406         TweenMax.render = TweenLite.render;
12407
12408         p.invalidate = function() {
12409             this._yoyo = (this.vars.yoyo === true);
12410             this._repeat = this.vars.repeat || 0;
12411             this._repeatDelay = this.vars.repeatDelay || 0;
12412             this._uncache(true);
12413             return TweenLite.prototype.invalidate.call(this);
12414         };
12415         
12416         p.updateTo = function(vars, resetDuration) {
12417             var curRatio = this.ratio, p;
12418             if (resetDuration && this._startTime < this._timeline._time) {
12419                 this._startTime = this._timeline._time;
12420                 this._uncache(false);
12421                 if (this._gc) {
12422                     this._enabled(true, false);
12423                 } else {
12424                     this._timeline.insert(this, this._startTime - this._delay); //ensures that any necessary re-sequencing of Animations in the timeline occurs to make sure the rendering order is correct.
12425                 }
12426             }
12427             for (p in vars) {
12428                 this.vars[p] = vars[p];
12429             }
12430             if (this._initted) {
12431                 if (resetDuration) {
12432                     this._initted = false;
12433                 } else {
12434                     if (this._gc) {
12435                         this._enabled(true, false);
12436                     }
12437                     if (this._notifyPluginsOfEnabled && this._firstPT) {
12438                         TweenLite._onPluginEvent("_onDisable", this); //in case a plugin like MotionBlur must perform some cleanup tasks
12439                     }
12440                     if (this._time / this._duration > 0.998) { //if the tween has finished (or come extremely close to finishing), we just need to rewind it to 0 and then render it again at the end which forces it to re-initialize (parsing the new vars). We allow tweens that are close to finishing (but haven't quite finished) to work this way too because otherwise, the values are so small when determining where to project the starting values that binary math issues creep in and can make the tween appear to render incorrectly when run backwards. 
12441                         var prevTime = this._time;
12442                         this.render(0, true, false);
12443                         this._initted = false;
12444                         this.render(prevTime, true, false);
12445                     } else if (this._time > 0) {
12446                         this._initted = false;
12447                         this._init();
12448                         var inv = 1 / (1 - curRatio),
12449                             pt = this._firstPT, endValue;
12450                         while (pt) {
12451                             endValue = pt.s + pt.c; 
12452                             pt.c *= inv;
12453                             pt.s = endValue - pt.c;
12454                             pt = pt._next;
12455                         }
12456                     }
12457                 }
12458             }
12459             return this;
12460         };
12461                 
12462         p.render = function(time, suppressEvents, force) {
12463             if (!this._initted) if (this._duration === 0 && this.vars.repeat) { //zero duration tweens that render immediately have render() called from TweenLite's constructor, before TweenMax's constructor has finished setting _repeat, _repeatDelay, and _yoyo which are critical in determining totalDuration() so we need to call invalidate() which is a low-kb way to get those set properly.
12464                 this.invalidate();
12465             }
12466             var totalDur = (!this._dirty) ? this._totalDuration : this.totalDuration(),
12467                 prevTime = this._time,
12468                 prevTotalTime = this._totalTime, 
12469                 prevCycle = this._cycle,
12470                 duration = this._duration,
12471                 prevRawPrevTime = this._rawPrevTime,
12472                 isComplete, callback, pt, cycleDuration, r, type, pow, rawPrevTime, i;
12473             if (time >= totalDur) {
12474                 this._totalTime = totalDur;
12475                 this._cycle = this._repeat;
12476                 if (this._yoyo && (this._cycle & 1) !== 0) {
12477                     this._time = 0;
12478                     this.ratio = this._ease._calcEnd ? this._ease.getRatio(0) : 0;
12479                 } else {
12480                     this._time = duration;
12481                     this.ratio = this._ease._calcEnd ? this._ease.getRatio(1) : 1;
12482                 }
12483                 if (!this._reversed) {
12484                     isComplete = true;
12485                     callback = "onComplete";
12486                 }
12487                 if (duration === 0) if (this._initted || !this.vars.lazy || force) { //zero-duration tweens are tricky because we must discern the momentum/direction of time in order to determine whether the starting values should be rendered or the ending values. If the "playhead" of its timeline goes past the zero-duration tween in the forward direction or lands directly on it, the end values should be rendered, but if the timeline's "playhead" moves past it in the backward direction (from a postitive time to a negative time), the starting values must be rendered.
12488                     if (this._startTime === this._timeline._duration) { //if a zero-duration tween is at the VERY end of a timeline and that timeline renders at its end, it will typically add a tiny bit of cushion to the render time to prevent rounding errors from getting in the way of tweens rendering their VERY end. If we then reverse() that timeline, the zero-duration tween will trigger its onReverseComplete even though technically the playhead didn't pass over it again. It's a very specific edge case we must accommodate.
12489                         time = 0;
12490                     }
12491                     if (time === 0 || prevRawPrevTime < 0 || prevRawPrevTime === _tinyNum) if (prevRawPrevTime !== time) {
12492                         force = true;
12493                         if (prevRawPrevTime > _tinyNum) {
12494                             callback = "onReverseComplete";
12495                         }
12496                     }
12497                     this._rawPrevTime = rawPrevTime = (!suppressEvents || time || prevRawPrevTime === time) ? time : _tinyNum; //when the playhead arrives at EXACTLY time 0 (right on top) of a zero-duration tween, we need to discern if events are suppressed so that when the playhead moves again (next time), it'll trigger the callback. If events are NOT suppressed, obviously the callback would be triggered in this render. Basically, the callback should fire either when the playhead ARRIVES or LEAVES this exact spot, not both. Imagine doing a timeline.seek(0) and there's a callback that sits at 0. Since events are suppressed on that seek() by default, nothing will fire, but when the playhead moves off of that position, the callback should fire. This behavior is what people intuitively expect. We set the _rawPrevTime to be a precise tiny number to indicate this scenario rather than using another property/variable which would increase memory usage. This technique is less readable, but more efficient.
12498                 }
12499                 
12500             } else if (time < 0.0000001) { //to work around occasional floating point math artifacts, round super small values to 0.
12501                 this._totalTime = this._time = this._cycle = 0;
12502                 this.ratio = this._ease._calcEnd ? this._ease.getRatio(0) : 0;
12503                 if (prevTotalTime !== 0 || (duration === 0 && prevRawPrevTime > 0 && prevRawPrevTime !== _tinyNum)) {
12504                     callback = "onReverseComplete";
12505                     isComplete = this._reversed;
12506                 }
12507                 if (time < 0) {
12508                     this._active = false;
12509                     if (duration === 0) if (this._initted || !this.vars.lazy || force) { //zero-duration tweens are tricky because we must discern the momentum/direction of time in order to determine whether the starting values should be rendered or the ending values. If the "playhead" of its timeline goes past the zero-duration tween in the forward direction or lands directly on it, the end values should be rendered, but if the timeline's "playhead" moves past it in the backward direction (from a postitive time to a negative time), the starting values must be rendered.
12510                         if (prevRawPrevTime >= 0) {
12511                             force = true;
12512                         }
12513                         this._rawPrevTime = rawPrevTime = (!suppressEvents || time || prevRawPrevTime === time) ? time : _tinyNum; //when the playhead arrives at EXACTLY time 0 (right on top) of a zero-duration tween, we need to discern if events are suppressed so that when the playhead moves again (next time), it'll trigger the callback. If events are NOT suppressed, obviously the callback would be triggered in this render. Basically, the callback should fire either when the playhead ARRIVES or LEAVES this exact spot, not both. Imagine doing a timeline.seek(0) and there's a callback that sits at 0. Since events are suppressed on that seek() by default, nothing will fire, but when the playhead moves off of that position, the callback should fire. This behavior is what people intuitively expect. We set the _rawPrevTime to be a precise tiny number to indicate this scenario rather than using another property/variable which would increase memory usage. This technique is less readable, but more efficient.
12514                     }
12515                 } else if (!this._initted) { //if we render the very beginning (time == 0) of a fromTo(), we must force the render (normal tweens wouldn't need to render at a time of 0 when the prevTime was also 0). This is also mandatory to make sure overwriting kicks in immediately.
12516                     force = true;
12517                 }
12518             } else {
12519                 this._totalTime = this._time = time;
12520                 
12521                 if (this._repeat !== 0) {
12522                     cycleDuration = duration + this._repeatDelay;
12523                     this._cycle = (this._totalTime / cycleDuration) >> 0; //originally _totalTime % cycleDuration but floating point errors caused problems, so I normalized it. (4 % 0.8 should be 0 but Flash reports it as 0.79999999!)
12524                     if (this._cycle !== 0) if (this._cycle === this._totalTime / cycleDuration) {
12525                         this._cycle--; //otherwise when rendered exactly at the end time, it will act as though it is repeating (at the beginning)
12526                     }
12527                     this._time = this._totalTime - (this._cycle * cycleDuration);
12528                     if (this._yoyo) if ((this._cycle & 1) !== 0) {
12529                         this._time = duration - this._time;
12530                     }
12531                     if (this._time > duration) {
12532                         this._time = duration;
12533                     } else if (this._time < 0) {
12534                         this._time = 0;
12535                     }
12536                 }
12537
12538                 if (this._easeType) {
12539                     r = this._time / duration;
12540                     type = this._easeType;
12541                     pow = this._easePower;
12542                     if (type === 1 || (type === 3 && r >= 0.5)) {
12543                         r = 1 - r;
12544                     }
12545                     if (type === 3) {
12546                         r *= 2;
12547                     }
12548                     if (pow === 1) {
12549                         r *= r;
12550                     } else if (pow === 2) {
12551                         r *= r * r;
12552                     } else if (pow === 3) {
12553                         r *= r * r * r;
12554                     } else if (pow === 4) {
12555                         r *= r * r * r * r;
12556                     }
12557
12558                     if (type === 1) {
12559                         this.ratio = 1 - r;
12560                     } else if (type === 2) {
12561                         this.ratio = r;
12562                     } else if (this._time / duration < 0.5) {
12563                         this.ratio = r / 2;
12564                     } else {
12565                         this.ratio = 1 - (r / 2);
12566                     }
12567
12568                 } else {
12569                     this.ratio = this._ease.getRatio(this._time / duration);
12570                 }
12571                 
12572             }
12573                 
12574             if (prevTime === this._time && !force && prevCycle === this._cycle) {
12575                 if (prevTotalTime !== this._totalTime) if (this._onUpdate) if (!suppressEvents) { //so that onUpdate fires even during the repeatDelay - as long as the totalTime changed, we should trigger onUpdate.
12576                     this._onUpdate.apply(this.vars.onUpdateScope || this, this.vars.onUpdateParams || _blankArray);
12577                 }
12578                 return;
12579             } else if (!this._initted) {
12580                 this._init();
12581                 if (!this._initted || this._gc) { //immediateRender tweens typically won't initialize until the playhead advances (_time is greater than 0) in order to ensure that overwriting occurs properly. Also, if all of the tweening properties have been overwritten (which would cause _gc to be true, as set in _init()), we shouldn't continue otherwise an onStart callback could be called for example.
12582                     return;
12583                 } else if (!force && this._firstPT && ((this.vars.lazy !== false && this._duration) || (this.vars.lazy && !this._duration))) { //we stick it in the queue for rendering at the very end of the tick - this is a performance optimization because browsers invalidate styles and force a recalculation if you read, write, and then read style data (so it's better to read/read/read/write/write/write than read/write/read/write/read/write). The down side, of course, is that usually you WANT things to render immediately because you may have code running right after that which depends on the change. Like imagine running TweenLite.set(...) and then immediately after that, creating a nother tween that animates the same property to another value; the starting values of that 2nd tween wouldn't be accurate if lazy is true.
12584                     this._time = prevTime;
12585                     this._totalTime = prevTotalTime;
12586                     this._rawPrevTime = prevRawPrevTime;
12587                     this._cycle = prevCycle;
12588                     TweenLiteInternals.lazyTweens.push(this);
12589                     this._lazy = time;
12590                     return;
12591                 }
12592                 //_ease is initially set to defaultEase, so now that init() has run, _ease is set properly and we need to recalculate the ratio. Overall this is faster than using conditional logic earlier in the method to avoid having to set ratio twice because we only init() once but renderTime() gets called VERY frequently.
12593                 if (this._time && !isComplete) {
12594                     this.ratio = this._ease.getRatio(this._time / duration);
12595                 } else if (isComplete && this._ease._calcEnd) {
12596                     this.ratio = this._ease.getRatio((this._time === 0) ? 0 : 1);
12597                 }
12598             }
12599             if (this._lazy !== false) {
12600                 this._lazy = false;
12601             }
12602
12603             if (!this._active) if (!this._paused && this._time !== prevTime && time >= 0) {
12604                 this._active = true; //so that if the user renders a tween (as opposed to the timeline rendering it), the timeline is forced to re-render and align it with the proper time/frame on the next rendering cycle. Maybe the tween already finished but the user manually re-renders it as halfway done.
12605             }
12606             if (prevTotalTime === 0) {
12607                 if (this._initted === 2 && time > 0) {
12608                     //this.invalidate();
12609                     this._init(); //will just apply overwriting since _initted of (2) means it was a from() tween that had immediateRender:true
12610                 }
12611                 if (this._startAt) {
12612                     if (time >= 0) {
12613                         this._startAt.render(time, suppressEvents, force);
12614                     } else if (!callback) {
12615                         callback = "_dummyGS"; //if no callback is defined, use a dummy value just so that the condition at the end evaluates as true because _startAt should render AFTER the normal render loop when the time is negative. We could handle this in a more intuitive way, of course, but the render loop is the MOST important thing to optimize, so this technique allows us to avoid adding extra conditional logic in a high-frequency area.
12616                     }
12617                 }
12618                 if (this.vars.onStart) if (this._totalTime !== 0 || duration === 0) if (!suppressEvents) {
12619                     this.vars.onStart.apply(this.vars.onStartScope || this, this.vars.onStartParams || _blankArray);
12620                 }
12621             }
12622             
12623             pt = this._firstPT;
12624             while (pt) {
12625                 if (pt.f) {
12626                     pt.t[pt.p](pt.c * this.ratio + pt.s);
12627                 } else {
12628                     pt.t[pt.p] = pt.c * this.ratio + pt.s;
12629                 }
12630                 pt = pt._next;
12631             }
12632             
12633             if (this._onUpdate) {
12634                 if (time < 0) if (this._startAt && this._startTime) { //if the tween is positioned at the VERY beginning (_startTime 0) of its parent timeline, it's illegal for the playhead to go back further, so we should not render the recorded startAt values.
12635                     this._startAt.render(time, suppressEvents, force); //note: for performance reasons, we tuck this conditional logic inside less traveled areas (most tweens don't have an onUpdate). We'd just have it at the end before the onComplete, but the values should be updated before any onUpdate is called, so we ALSO put it here and then if it's not called, we do so later near the onComplete.
12636                 }
12637                 if (!suppressEvents) if (this._totalTime !== prevTotalTime || isComplete) {
12638                     this._onUpdate.apply(this.vars.onUpdateScope || this, this.vars.onUpdateParams || _blankArray);
12639                 }
12640             }
12641             if (this._cycle !== prevCycle) if (!suppressEvents) if (!this._gc) if (this.vars.onRepeat) {
12642                 this.vars.onRepeat.apply(this.vars.onRepeatScope || this, this.vars.onRepeatParams || _blankArray);
12643             }
12644             if (callback) if (!this._gc) { //check gc because there's a chance that kill() could be called in an onUpdate
12645                 if (time < 0 && this._startAt && !this._onUpdate && this._startTime) { //if the tween is positioned at the VERY beginning (_startTime 0) of its parent timeline, it's illegal for the playhead to go back further, so we should not render the recorded startAt values.
12646                     this._startAt.render(time, suppressEvents, force);
12647                 }
12648                 if (isComplete) {
12649                     if (this._timeline.autoRemoveChildren) {
12650                         this._enabled(false, false);
12651                     }
12652                     this._active = false;
12653                 }
12654                 if (!suppressEvents && this.vars[callback]) {
12655                     this.vars[callback].apply(this.vars[callback + "Scope"] || this, this.vars[callback + "Params"] || _blankArray);
12656                 }
12657                 if (duration === 0 && this._rawPrevTime === _tinyNum && rawPrevTime !== _tinyNum) { //the onComplete or onReverseComplete could trigger movement of the playhead and for zero-duration tweens (which must discern direction) that land directly back on their start time, we don't want to fire again on the next render. Think of several addPause()'s in a timeline that forces the playhead to a certain spot, but what if it's already paused and another tween is tweening the "time" of the timeline? Each time it moves [forward] past that spot, it would move back, and since suppressEvents is true, it'd reset _rawPrevTime to _tinyNum so that when it begins again, the callback would fire (so ultimately it could bounce back and forth during that tween). Again, this is a very uncommon scenario, but possible nonetheless.
12658                     this._rawPrevTime = 0;
12659                 }
12660             }
12661         };
12662         
12663 //---- STATIC FUNCTIONS -----------------------------------------------------------------------------------------------------------
12664         
12665         TweenMax.to = function(target, duration, vars) {
12666             return new TweenMax(target, duration, vars);
12667         };
12668         
12669         TweenMax.from = function(target, duration, vars) {
12670             vars.runBackwards = true;
12671             vars.immediateRender = (vars.immediateRender != false);
12672             return new TweenMax(target, duration, vars);
12673         };
12674         
12675         TweenMax.fromTo = function(target, duration, fromVars, toVars) {
12676             toVars.startAt = fromVars;
12677             toVars.immediateRender = (toVars.immediateRender != false && fromVars.immediateRender != false);
12678             return new TweenMax(target, duration, toVars);
12679         };
12680         
12681         TweenMax.staggerTo = TweenMax.allTo = function(targets, duration, vars, stagger, onCompleteAll, onCompleteAllParams, onCompleteAllScope) {
12682             stagger = stagger || 0;
12683             var delay = vars.delay || 0,
12684                 a = [],
12685                 finalComplete = function() {
12686                     if (vars.onComplete) {
12687                         vars.onComplete.apply(vars.onCompleteScope || this, arguments);
12688                     }
12689                     onCompleteAll.apply(onCompleteAllScope || this, onCompleteAllParams || _blankArray);
12690                 },
12691                 l, copy, i, p;
12692             if (!_isArray(targets)) {
12693                 if (typeof(targets) === "string") {
12694                     targets = TweenLite.selector(targets) || targets;
12695                 }
12696                 if (_isSelector(targets)) {
12697                     targets = _slice.call(targets, 0);
12698                 }
12699             }
12700             l = targets.length;
12701             for (i = 0; i < l; i++) {
12702                 copy = {};
12703                 for (p in vars) {
12704                     copy[p] = vars[p];
12705                 }
12706                 copy.delay = delay;
12707                 if (i === l - 1 && onCompleteAll) {
12708                     copy.onComplete = finalComplete;
12709                 }
12710                 a[i] = new TweenMax(targets[i], duration, copy);
12711                 delay += stagger;
12712             }
12713             return a;
12714         };
12715         
12716         TweenMax.staggerFrom = TweenMax.allFrom = function(targets, duration, vars, stagger, onCompleteAll, onCompleteAllParams, onCompleteAllScope) {
12717             vars.runBackwards = true;
12718             vars.immediateRender = (vars.immediateRender != false);
12719             return TweenMax.staggerTo(targets, duration, vars, stagger, onCompleteAll, onCompleteAllParams, onCompleteAllScope);
12720         };
12721         
12722         TweenMax.staggerFromTo = TweenMax.allFromTo = function(targets, duration, fromVars, toVars, stagger, onCompleteAll, onCompleteAllParams, onCompleteAllScope) {
12723             toVars.startAt = fromVars;
12724             toVars.immediateRender = (toVars.immediateRender != false && fromVars.immediateRender != false);
12725             return TweenMax.staggerTo(targets, duration, toVars, stagger, onCompleteAll, onCompleteAllParams, onCompleteAllScope);
12726         };
12727                 
12728         TweenMax.delayedCall = function(delay, callback, params, scope, useFrames) {
12729             return new TweenMax(callback, 0, {delay:delay, onComplete:callback, onCompleteParams:params, onCompleteScope:scope, onReverseComplete:callback, onReverseCompleteParams:params, onReverseCompleteScope:scope, immediateRender:false, useFrames:useFrames, overwrite:0});
12730         };
12731         
12732         TweenMax.set = function(target, vars) {
12733             return new TweenMax(target, 0, vars);
12734         };
12735         
12736         TweenMax.isTweening = function(target) {
12737             return (TweenLite.getTweensOf(target, true).length > 0);
12738         };
12739         
12740         var _getChildrenOf = function(timeline, includeTimelines) {
12741                 var a = [],
12742                     cnt = 0,
12743                     tween = timeline._first;
12744                 while (tween) {
12745                     if (tween instanceof TweenLite) {
12746                         a[cnt++] = tween;
12747                     } else {
12748                         if (includeTimelines) {
12749                             a[cnt++] = tween;
12750                         }
12751                         a = a.concat(_getChildrenOf(tween, includeTimelines));
12752                         cnt = a.length;
12753                     }
12754                     tween = tween._next;
12755                 }
12756                 return a;
12757             }, 
12758             getAllTweens = TweenMax.getAllTweens = function(includeTimelines) {
12759                 return _getChildrenOf(Animation._rootTimeline, includeTimelines).concat( _getChildrenOf(Animation._rootFramesTimeline, includeTimelines) );
12760             };
12761         
12762         TweenMax.killAll = function(complete, tweens, delayedCalls, timelines) {
12763             if (tweens == null) {
12764                 tweens = true;
12765             }
12766             if (delayedCalls == null) {
12767                 delayedCalls = true;
12768             }
12769             var a = getAllTweens((timelines != false)),
12770                 l = a.length,
12771                 allTrue = (tweens && delayedCalls && timelines),
12772                 isDC, tween, i;
12773             for (i = 0; i < l; i++) {
12774                 tween = a[i];
12775                 if (allTrue || (tween instanceof SimpleTimeline) || ((isDC = (tween.target === tween.vars.onComplete)) && delayedCalls) || (tweens && !isDC)) {
12776                     if (complete) {
12777                         tween.totalTime(tween._reversed ? 0 : tween.totalDuration());
12778                     } else {
12779                         tween._enabled(false, false);
12780                     }
12781                 }
12782             }
12783         };
12784         
12785         TweenMax.killChildTweensOf = function(parent, complete) {
12786             if (parent == null) {
12787                 return;
12788             }
12789             var tl = TweenLiteInternals.tweenLookup,
12790                 a, curParent, p, i, l;
12791             if (typeof(parent) === "string") {
12792                 parent = TweenLite.selector(parent) || parent;
12793             }
12794             if (_isSelector(parent)) {
12795                 parent = _slice.call(parent, 0);
12796             }
12797             if (_isArray(parent)) {
12798                 i = parent.length;
12799                 while (--i > -1) {
12800                     TweenMax.killChildTweensOf(parent[i], complete);
12801                 }
12802                 return;
12803             }
12804             a = [];
12805             for (p in tl) {
12806                 curParent = tl[p].target.parentNode;
12807                 while (curParent) {
12808                     if (curParent === parent) {
12809                         a = a.concat(tl[p].tweens);
12810                     }
12811                     curParent = curParent.parentNode;
12812                 }
12813             }
12814             l = a.length;
12815             for (i = 0; i < l; i++) {
12816                 if (complete) {
12817                     a[i].totalTime(a[i].totalDuration());
12818                 }
12819                 a[i]._enabled(false, false);
12820             }
12821         };
12822
12823         var _changePause = function(pause, tweens, delayedCalls, timelines) {
12824             tweens = (tweens !== false);
12825             delayedCalls = (delayedCalls !== false);
12826             timelines = (timelines !== false);
12827             var a = getAllTweens(timelines),
12828                 allTrue = (tweens && delayedCalls && timelines),
12829                 i = a.length,
12830                 isDC, tween;
12831             while (--i > -1) {
12832                 tween = a[i];
12833                 if (allTrue || (tween instanceof SimpleTimeline) || ((isDC = (tween.target === tween.vars.onComplete)) && delayedCalls) || (tweens && !isDC)) {
12834                     tween.paused(pause);
12835                 }
12836             }
12837         };
12838         
12839         TweenMax.pauseAll = function(tweens, delayedCalls, timelines) {
12840             _changePause(true, tweens, delayedCalls, timelines);
12841         };
12842         
12843         TweenMax.resumeAll = function(tweens, delayedCalls, timelines) {
12844             _changePause(false, tweens, delayedCalls, timelines);
12845         };
12846
12847         TweenMax.globalTimeScale = function(value) {
12848             var tl = Animation._rootTimeline,
12849                 t = TweenLite.ticker.time;
12850             if (!arguments.length) {
12851                 return tl._timeScale;
12852             }
12853             value = value || _tinyNum; //can't allow zero because it'll throw the math off
12854             tl._startTime = t - ((t - tl._startTime) * tl._timeScale / value);
12855             tl = Animation._rootFramesTimeline;
12856             t = TweenLite.ticker.frame;
12857             tl._startTime = t - ((t - tl._startTime) * tl._timeScale / value);
12858             tl._timeScale = Animation._rootTimeline._timeScale = value;
12859             return value;
12860         };
12861         
12862     
12863 //---- GETTERS / SETTERS ----------------------------------------------------------------------------------------------------------
12864         
12865         p.progress = function(value) {
12866             return (!arguments.length) ? this._time / this.duration() : this.totalTime( this.duration() * ((this._yoyo && (this._cycle & 1) !== 0) ? 1 - value : value) + (this._cycle * (this._duration + this._repeatDelay)), false);
12867         };
12868         
12869         p.totalProgress = function(value) {
12870             return (!arguments.length) ? this._totalTime / this.totalDuration() : this.totalTime( this.totalDuration() * value, false);
12871         };
12872         
12873         p.time = function(value, suppressEvents) {
12874             if (!arguments.length) {
12875                 return this._time;
12876             }
12877             if (this._dirty) {
12878                 this.totalDuration();
12879             }
12880             if (value > this._duration) {
12881                 value = this._duration;
12882             }
12883             if (this._yoyo && (this._cycle & 1) !== 0) {
12884                 value = (this._duration - value) + (this._cycle * (this._duration + this._repeatDelay));
12885             } else if (this._repeat !== 0) {
12886                 value += this._cycle * (this._duration + this._repeatDelay);
12887             }
12888             return this.totalTime(value, suppressEvents);
12889         };
12890
12891         p.duration = function(value) {
12892             if (!arguments.length) {
12893                 return this._duration; //don't set _dirty = false because there could be repeats that haven't been factored into the _totalDuration yet. Otherwise, if you create a repeated TweenMax and then immediately check its duration(), it would cache the value and the totalDuration would not be correct, thus repeats wouldn't take effect.
12894             }
12895             return Animation.prototype.duration.call(this, value);
12896         };
12897
12898         p.totalDuration = function(value) {
12899             if (!arguments.length) {
12900                 if (this._dirty) {
12901                     //instead of Infinity, we use 999999999999 so that we can accommodate reverses
12902                     this._totalDuration = (this._repeat === -1) ? 999999999999 : this._duration * (this._repeat + 1) + (this._repeatDelay * this._repeat);
12903                     this._dirty = false;
12904                 }
12905                 return this._totalDuration;
12906             }
12907             return (this._repeat === -1) ? this : this.duration( (value - (this._repeat * this._repeatDelay)) / (this._repeat + 1) );
12908         };
12909         
12910         p.repeat = function(value) {
12911             if (!arguments.length) {
12912                 return this._repeat;
12913             }
12914             this._repeat = value;
12915             return this._uncache(true);
12916         };
12917         
12918         p.repeatDelay = function(value) {
12919             if (!arguments.length) {
12920                 return this._repeatDelay;
12921             }
12922             this._repeatDelay = value;
12923             return this._uncache(true);
12924         };
12925         
12926         p.yoyo = function(value) {
12927             if (!arguments.length) {
12928                 return this._yoyo;
12929             }
12930             this._yoyo = value;
12931             return this;
12932         };
12933         
12934         
12935         return TweenMax;
12936         
12937     }, true);
12938
12939
12940
12941
12942
12943
12944
12945
12946 /*
12947  * ----------------------------------------------------------------
12948  * TimelineLite
12949  * ----------------------------------------------------------------
12950  */
12951     window._gsDefine("TimelineLite", ["core.Animation","core.SimpleTimeline","TweenLite"], function(Animation, SimpleTimeline, TweenLite) {
12952
12953         var TimelineLite = function(vars) {
12954                 SimpleTimeline.call(this, vars);
12955                 this._labels = {};
12956                 this.autoRemoveChildren = (this.vars.autoRemoveChildren === true);
12957                 this.smoothChildTiming = (this.vars.smoothChildTiming === true);
12958                 this._sortChildren = true;
12959                 this._onUpdate = this.vars.onUpdate;
12960                 var v = this.vars,
12961                     val, p;
12962                 for (p in v) {
12963                     val = v[p];
12964                     if (_isArray(val)) if (val.join("").indexOf("{self}") !== -1) {
12965                         v[p] = this._swapSelfInParams(val);
12966                     }
12967                 }
12968                 if (_isArray(v.tweens)) {
12969                     this.add(v.tweens, 0, v.align, v.stagger);
12970                 }
12971             },
12972             _tinyNum = 0.0000000001,
12973             _isSelector = TweenLite._internals.isSelector,
12974             _isArray = TweenLite._internals.isArray,
12975             _blankArray = [],
12976             _globals = window._gsDefine.globals,
12977             _copy = function(vars) {
12978                 var copy = {}, p;
12979                 for (p in vars) {
12980                     copy[p] = vars[p];
12981                 }
12982                 return copy;
12983             },
12984             _pauseCallback = function(tween, callback, params, scope) {
12985                 tween._timeline.pause(tween._startTime);
12986                 if (callback) {
12987                     callback.apply(scope || tween._timeline, params || _blankArray);
12988                 }
12989             },
12990             _slice = _blankArray.slice,
12991             p = TimelineLite.prototype = new SimpleTimeline();
12992
12993         TimelineLite.version = "1.12.1";
12994         p.constructor = TimelineLite;
12995         p.kill()._gc = false;
12996
12997         p.to = function(target, duration, vars, position) {
12998             var Engine = (vars.repeat && _globals.TweenMax) || TweenLite;
12999             return duration ? this.add( new Engine(target, duration, vars), position) : this.set(target, vars, position);
13000         };
13001
13002         p.from = function(target, duration, vars, position) {
13003             return this.add( ((vars.repeat && _globals.TweenMax) || TweenLite).from(target, duration, vars), position);
13004         };
13005
13006         p.fromTo = function(target, duration, fromVars, toVars, position) {
13007             var Engine = (toVars.repeat && _globals.TweenMax) || TweenLite;
13008             return duration ? this.add( Engine.fromTo(target, duration, fromVars, toVars), position) : this.set(target, toVars, position);
13009         };
13010
13011         p.staggerTo = function(targets, duration, vars, stagger, position, onCompleteAll, onCompleteAllParams, onCompleteAllScope) {
13012             var tl = new TimelineLite({onComplete:onCompleteAll, onCompleteParams:onCompleteAllParams, onCompleteScope:onCompleteAllScope, smoothChildTiming:this.smoothChildTiming}),
13013                 i;
13014             if (typeof(targets) === "string") {
13015                 targets = TweenLite.selector(targets) || targets;
13016             }
13017             if (_isSelector(targets)) { //senses if the targets object is a selector. If it is, we should translate it into an array.
13018                 targets = _slice.call(targets, 0);
13019             }
13020             stagger = stagger || 0;
13021             for (i = 0; i < targets.length; i++) {
13022                 if (vars.startAt) {
13023                     vars.startAt = _copy(vars.startAt);
13024                 }
13025                 tl.to(targets[i], duration, _copy(vars), i * stagger);
13026             }
13027             return this.add(tl, position);
13028         };
13029
13030         p.staggerFrom = function(targets, duration, vars, stagger, position, onCompleteAll, onCompleteAllParams, onCompleteAllScope) {
13031             vars.immediateRender = (vars.immediateRender != false);
13032             vars.runBackwards = true;
13033             return this.staggerTo(targets, duration, vars, stagger, position, onCompleteAll, onCompleteAllParams, onCompleteAllScope);
13034         };
13035
13036         p.staggerFromTo = function(targets, duration, fromVars, toVars, stagger, position, onCompleteAll, onCompleteAllParams, onCompleteAllScope) {
13037             toVars.startAt = fromVars;
13038             toVars.immediateRender = (toVars.immediateRender != false && fromVars.immediateRender != false);
13039             return this.staggerTo(targets, duration, toVars, stagger, position, onCompleteAll, onCompleteAllParams, onCompleteAllScope);
13040         };
13041
13042         p.call = function(callback, params, scope, position) {
13043             return this.add( TweenLite.delayedCall(0, callback, params, scope), position);
13044         };
13045
13046         p.set = function(target, vars, position) {
13047             position = this._parseTimeOrLabel(position, 0, true);
13048             if (vars.immediateRender == null) {
13049                 vars.immediateRender = (position === this._time && !this._paused);
13050             }
13051             return this.add( new TweenLite(target, 0, vars), position);
13052         };
13053
13054         TimelineLite.exportRoot = function(vars, ignoreDelayedCalls) {
13055             vars = vars || {};
13056             if (vars.smoothChildTiming == null) {
13057                 vars.smoothChildTiming = true;
13058             }
13059             var tl = new TimelineLite(vars),
13060                 root = tl._timeline,
13061                 tween, next;
13062             if (ignoreDelayedCalls == null) {
13063                 ignoreDelayedCalls = true;
13064             }
13065             root._remove(tl, true);
13066             tl._startTime = 0;
13067             tl._rawPrevTime = tl._time = tl._totalTime = root._time;
13068             tween = root._first;
13069             while (tween) {
13070                 next = tween._next;
13071                 if (!ignoreDelayedCalls || !(tween instanceof TweenLite && tween.target === tween.vars.onComplete)) {
13072                     tl.add(tween, tween._startTime - tween._delay);
13073                 }
13074                 tween = next;
13075             }
13076             root.add(tl, 0);
13077             return tl;
13078         };
13079
13080         p.add = function(value, position, align, stagger) {
13081             var curTime, l, i, child, tl, beforeRawTime;
13082             if (typeof(position) !== "number") {
13083                 position = this._parseTimeOrLabel(position, 0, true, value);
13084             }
13085             if (!(value instanceof Animation)) {
13086                 if ((value instanceof Array) || (value && value.push && _isArray(value))) {
13087                     align = align || "normal";
13088                     stagger = stagger || 0;
13089                     curTime = position;
13090                     l = value.length;
13091                     for (i = 0; i < l; i++) {
13092                         if (_isArray(child = value[i])) {
13093                             child = new TimelineLite({tweens:child});
13094                         }
13095                         this.add(child, curTime);
13096                         if (typeof(child) !== "string" && typeof(child) !== "function") {
13097                             if (align === "sequence") {
13098                                 curTime = child._startTime + (child.totalDuration() / child._timeScale);
13099                             } else if (align === "start") {
13100                                 child._startTime -= child.delay();
13101                             }
13102                         }
13103                         curTime += stagger;
13104                     }
13105                     return this._uncache(true);
13106                 } else if (typeof(value) === "string") {
13107                     return this.addLabel(value, position);
13108                 } else if (typeof(value) === "function") {
13109                     value = TweenLite.delayedCall(0, value);
13110                 } else {
13111                     throw("Cannot add " + value + " into the timeline; it is not a tween, timeline, function, or string.");
13112                 }
13113             }
13114
13115             SimpleTimeline.prototype.add.call(this, value, position);
13116
13117             //if the timeline has already ended but the inserted tween/timeline extends the duration, we should enable this timeline again so that it renders properly. We should also align the playhead with the parent timeline's when appropriate.
13118             if (this._gc || this._time === this._duration) if (!this._paused) if (this._duration < this.duration()) {
13119                 //in case any of the ancestors had completed but should now be enabled...
13120                 tl = this;
13121                 beforeRawTime = (tl.rawTime() > value._startTime); //if the tween is placed on the timeline so that it starts BEFORE the current rawTime, we should align the playhead (move the timeline). This is because sometimes users will create a timeline, let it finish, and much later append a tween and expect it to run instead of jumping to its end state. While technically one could argue that it should jump to its end state, that's not what users intuitively expect.
13122                 while (tl._timeline) {
13123                     if (beforeRawTime && tl._timeline.smoothChildTiming) {
13124                         tl.totalTime(tl._totalTime, true); //moves the timeline (shifts its startTime) if necessary, and also enables it.
13125                     } else if (tl._gc) {
13126                         tl._enabled(true, false);
13127                     }
13128                     tl = tl._timeline;
13129                 }
13130             }
13131
13132             return this;
13133         };
13134
13135         p.remove = function(value) {
13136             if (value instanceof Animation) {
13137                 return this._remove(value, false);
13138             } else if (value instanceof Array || (value && value.push && _isArray(value))) {
13139                 var i = value.length;
13140                 while (--i > -1) {
13141                     this.remove(value[i]);
13142                 }
13143                 return this;
13144             } else if (typeof(value) === "string") {
13145                 return this.removeLabel(value);
13146             }
13147             return this.kill(null, value);
13148         };
13149
13150         p._remove = function(tween, skipDisable) {
13151             SimpleTimeline.prototype._remove.call(this, tween, skipDisable);
13152             var last = this._last;
13153             if (!last) {
13154                 this._time = this._totalTime = this._duration = this._totalDuration = 0;
13155             } else if (this._time > last._startTime + last._totalDuration / last._timeScale) {
13156                 this._time = this.duration();
13157                 this._totalTime = this._totalDuration;
13158             }
13159             return this;
13160         };
13161
13162         p.append = function(value, offsetOrLabel) {
13163             return this.add(value, this._parseTimeOrLabel(null, offsetOrLabel, true, value));
13164         };
13165
13166         p.insert = p.insertMultiple = function(value, position, align, stagger) {
13167             return this.add(value, position || 0, align, stagger);
13168         };
13169
13170         p.appendMultiple = function(tweens, offsetOrLabel, align, stagger) {
13171             return this.add(tweens, this._parseTimeOrLabel(null, offsetOrLabel, true, tweens), align, stagger);
13172         };
13173
13174         p.addLabel = function(label, position) {
13175             this._labels[label] = this._parseTimeOrLabel(position);
13176             return this;
13177         };
13178
13179         p.addPause = function(position, callback, params, scope) {
13180             return this.call(_pauseCallback, ["{self}", callback, params, scope], this, position);
13181         };
13182
13183         p.removeLabel = function(label) {
13184             delete this._labels[label];
13185             return this;
13186         };
13187
13188         p.getLabelTime = function(label) {
13189             return (this._labels[label] != null) ? this._labels[label] : -1;
13190         };
13191
13192         p._parseTimeOrLabel = function(timeOrLabel, offsetOrLabel, appendIfAbsent, ignore) {
13193             var i;
13194             //if we're about to add a tween/timeline (or an array of them) that's already a child of this timeline, we should remove it first so that it doesn't contaminate the duration().
13195             if (ignore instanceof Animation && ignore.timeline === this) {
13196                 this.remove(ignore);
13197             } else if (ignore && ((ignore instanceof Array) || (ignore.push && _isArray(ignore)))) {
13198                 i = ignore.length;
13199                 while (--i > -1) {
13200                     if (ignore[i] instanceof Animation && ignore[i].timeline === this) {
13201                         this.remove(ignore[i]);
13202                     }
13203                 }
13204             }
13205             if (typeof(offsetOrLabel) === "string") {
13206                 return this._parseTimeOrLabel(offsetOrLabel, (appendIfAbsent && typeof(timeOrLabel) === "number" && this._labels[offsetOrLabel] == null) ? timeOrLabel - this.duration() : 0, appendIfAbsent);
13207             }
13208             offsetOrLabel = offsetOrLabel || 0;
13209             if (typeof(timeOrLabel) === "string" && (isNaN(timeOrLabel) || this._labels[timeOrLabel] != null)) { //if the string is a number like "1", check to see if there's a label with that name, otherwise interpret it as a number (absolute value).
13210                 i = timeOrLabel.indexOf("=");
13211                 if (i === -1) {
13212                     if (this._labels[timeOrLabel] == null) {
13213                         return appendIfAbsent ? (this._labels[timeOrLabel] = this.duration() + offsetOrLabel) : offsetOrLabel;
13214                     }
13215                     return this._labels[timeOrLabel] + offsetOrLabel;
13216                 }
13217                 offsetOrLabel = parseInt(timeOrLabel.charAt(i-1) + "1", 10) * Number(timeOrLabel.substr(i+1));
13218                 timeOrLabel = (i > 1) ? this._parseTimeOrLabel(timeOrLabel.substr(0, i-1), 0, appendIfAbsent) : this.duration();
13219             } else if (timeOrLabel == null) {
13220                 timeOrLabel = this.duration();
13221             }
13222             return Number(timeOrLabel) + offsetOrLabel;
13223         };
13224
13225         p.seek = function(position, suppressEvents) {
13226             return this.totalTime((typeof(position) === "number") ? position : this._parseTimeOrLabel(position), (suppressEvents !== false));
13227         };
13228
13229         p.stop = function() {
13230             return this.paused(true);
13231         };
13232
13233         p.gotoAndPlay = function(position, suppressEvents) {
13234             return this.play(position, suppressEvents);
13235         };
13236
13237         p.gotoAndStop = function(position, suppressEvents) {
13238             return this.pause(position, suppressEvents);
13239         };
13240
13241         p.render = function(time, suppressEvents, force) {
13242             if (this._gc) {
13243                 this._enabled(true, false);
13244             }
13245             var totalDur = (!this._dirty) ? this._totalDuration : this.totalDuration(),
13246                 prevTime = this._time,
13247                 prevStart = this._startTime,
13248                 prevTimeScale = this._timeScale,
13249                 prevPaused = this._paused,
13250                 tween, isComplete, next, callback, internalForce;
13251             if (time >= totalDur) {
13252                 this._totalTime = this._time = totalDur;
13253                 if (!this._reversed) if (!this._hasPausedChild()) {
13254                     isComplete = true;
13255                     callback = "onComplete";
13256                     if (this._duration === 0) if (time === 0 || this._rawPrevTime < 0 || this._rawPrevTime === _tinyNum) if (this._rawPrevTime !== time && this._first) {
13257                         internalForce = true;
13258                         if (this._rawPrevTime > _tinyNum) {
13259                             callback = "onReverseComplete";
13260                         }
13261                     }
13262                 }
13263                 this._rawPrevTime = (this._duration || !suppressEvents || time || this._rawPrevTime === time) ? time : _tinyNum; //when the playhead arrives at EXACTLY time 0 (right on top) of a zero-duration timeline or tween, we need to discern if events are suppressed so that when the playhead moves again (next time), it'll trigger the callback. If events are NOT suppressed, obviously the callback would be triggered in this render. Basically, the callback should fire either when the playhead ARRIVES or LEAVES this exact spot, not both. Imagine doing a timeline.seek(0) and there's a callback that sits at 0. Since events are suppressed on that seek() by default, nothing will fire, but when the playhead moves off of that position, the callback should fire. This behavior is what people intuitively expect. We set the _rawPrevTime to be a precise tiny number to indicate this scenario rather than using another property/variable which would increase memory usage. This technique is less readable, but more efficient.
13264                 time = totalDur + 0.0001; //to avoid occasional floating point rounding errors - sometimes child tweens/timelines were not being fully completed (their progress might be 0.999999999999998 instead of 1 because when _time - tween._startTime is performed, floating point errors would return a value that was SLIGHTLY off). Try (999999999999.7 - 999999999999) * 1 = 0.699951171875 instead of 0.7.
13265
13266             } else if (time < 0.0000001) { //to work around occasional floating point math artifacts, round super small values to 0.
13267                 this._totalTime = this._time = 0;
13268                 if (prevTime !== 0 || (this._duration === 0 && this._rawPrevTime !== _tinyNum && (this._rawPrevTime > 0 || (time < 0 && this._rawPrevTime >= 0)))) {
13269                     callback = "onReverseComplete";
13270                     isComplete = this._reversed;
13271                 }
13272                 if (time < 0) {
13273                     this._active = false;
13274                     if (this._duration === 0) if (this._rawPrevTime >= 0 && this._first) { //zero-duration timelines are tricky because we must discern the momentum/direction of time in order to determine whether the starting values should be rendered or the ending values. If the "playhead" of its timeline goes past the zero-duration tween in the forward direction or lands directly on it, the end values should be rendered, but if the timeline's "playhead" moves past it in the backward direction (from a postitive time to a negative time), the starting values must be rendered.
13275                         internalForce = true;
13276                     }
13277                     this._rawPrevTime = time;
13278                 } else {
13279                     this._rawPrevTime = (this._duration || !suppressEvents || time || this._rawPrevTime === time) ? time : _tinyNum; //when the playhead arrives at EXACTLY time 0 (right on top) of a zero-duration timeline or tween, we need to discern if events are suppressed so that when the playhead moves again (next time), it'll trigger the callback. If events are NOT suppressed, obviously the callback would be triggered in this render. Basically, the callback should fire either when the playhead ARRIVES or LEAVES this exact spot, not both. Imagine doing a timeline.seek(0) and there's a callback that sits at 0. Since events are suppressed on that seek() by default, nothing will fire, but when the playhead moves off of that position, the callback should fire. This behavior is what people intuitively expect. We set the _rawPrevTime to be a precise tiny number to indicate this scenario rather than using another property/variable which would increase memory usage. This technique is less readable, but more efficient.
13280
13281                     time = 0; //to avoid occasional floating point rounding errors (could cause problems especially with zero-duration tweens at the very beginning of the timeline)
13282                     if (!this._initted) {
13283                         internalForce = true;
13284                     }
13285                 }
13286
13287             } else {
13288                 this._totalTime = this._time = this._rawPrevTime = time;
13289             }
13290             if ((this._time === prevTime || !this._first) && !force && !internalForce) {
13291                 return;
13292             } else if (!this._initted) {
13293                 this._initted = true;
13294             }
13295
13296             if (!this._active) if (!this._paused && this._time !== prevTime && time > 0) {
13297                 this._active = true;  //so that if the user renders the timeline (as opposed to the parent timeline rendering it), it is forced to re-render and align it with the proper time/frame on the next rendering cycle. Maybe the timeline already finished but the user manually re-renders it as halfway done, for example.
13298             }
13299
13300             if (prevTime === 0) if (this.vars.onStart) if (this._time !== 0) if (!suppressEvents) {
13301                 this.vars.onStart.apply(this.vars.onStartScope || this, this.vars.onStartParams || _blankArray);
13302             }
13303
13304             if (this._time >= prevTime) {
13305                 tween = this._first;
13306                 while (tween) {
13307                     next = tween._next; //record it here because the value could change after rendering...
13308                     if (this._paused && !prevPaused) { //in case a tween pauses the timeline when rendering
13309                         break;
13310                     } else if (tween._active || (tween._startTime <= this._time && !tween._paused && !tween._gc)) {
13311                         if (!tween._reversed) {
13312                             tween.render((time - tween._startTime) * tween._timeScale, suppressEvents, force);
13313                         } else {
13314                             tween.render(((!tween._dirty) ? tween._totalDuration : tween.totalDuration()) - ((time - tween._startTime) * tween._timeScale), suppressEvents, force);
13315                         }
13316                     }
13317                     tween = next;
13318                 }
13319             } else {
13320                 tween = this._last;
13321                 while (tween) {
13322                     next = tween._prev; //record it here because the value could change after rendering...
13323                     if (this._paused && !prevPaused) { //in case a tween pauses the timeline when rendering
13324                         break;
13325                     } else if (tween._active || (tween._startTime <= prevTime && !tween._paused && !tween._gc)) {
13326                         if (!tween._reversed) {
13327                             tween.render((time - tween._startTime) * tween._timeScale, suppressEvents, force);
13328                         } else {
13329                             tween.render(((!tween._dirty) ? tween._totalDuration : tween.totalDuration()) - ((time - tween._startTime) * tween._timeScale), suppressEvents, force);
13330                         }
13331                     }
13332                     tween = next;
13333                 }
13334             }
13335
13336             if (this._onUpdate) if (!suppressEvents) {
13337                 this._onUpdate.apply(this.vars.onUpdateScope || this, this.vars.onUpdateParams || _blankArray);
13338             }
13339
13340             if (callback) if (!this._gc) if (prevStart === this._startTime || prevTimeScale !== this._timeScale) if (this._time === 0 || totalDur >= this.totalDuration()) { //if one of the tweens that was rendered altered this timeline's startTime (like if an onComplete reversed the timeline), it probably isn't complete. If it is, don't worry, because whatever call altered the startTime would complete if it was necessary at the new time. The only exception is the timeScale property. Also check _gc because there's a chance that kill() could be called in an onUpdate
13341                 if (isComplete) {
13342                     if (this._timeline.autoRemoveChildren) {
13343                         this._enabled(false, false);
13344                     }
13345                     this._active = false;
13346                 }
13347                 if (!suppressEvents && this.vars[callback]) {
13348                     this.vars[callback].apply(this.vars[callback + "Scope"] || this, this.vars[callback + "Params"] || _blankArray);
13349                 }
13350             }
13351         };
13352
13353         p._hasPausedChild = function() {
13354             var tween = this._first;
13355             while (tween) {
13356                 if (tween._paused || ((tween instanceof TimelineLite) && tween._hasPausedChild())) {
13357                     return true;
13358                 }
13359                 tween = tween._next;
13360             }
13361             return false;
13362         };
13363
13364         p.getChildren = function(nested, tweens, timelines, ignoreBeforeTime) {
13365             ignoreBeforeTime = ignoreBeforeTime || -9999999999;
13366             var a = [],
13367                 tween = this._first,
13368                 cnt = 0;
13369             while (tween) {
13370                 if (tween._startTime < ignoreBeforeTime) {
13371                     //do nothing
13372                 } else if (tween instanceof TweenLite) {
13373                     if (tweens !== false) {
13374                         a[cnt++] = tween;
13375                     }
13376                 } else {
13377                     if (timelines !== false) {
13378                         a[cnt++] = tween;
13379                     }
13380                     if (nested !== false) {
13381                         a = a.concat(tween.getChildren(true, tweens, timelines));
13382                         cnt = a.length;
13383                     }
13384                 }
13385                 tween = tween._next;
13386             }
13387             return a;
13388         };
13389
13390         p.getTweensOf = function(target, nested) {
13391             var disabled = this._gc,
13392                 a = [],
13393                 cnt = 0,
13394                 tweens, i;
13395             if (disabled) {
13396                 this._enabled(true, true); //getTweensOf() filters out disabled tweens, and we have to mark them as _gc = true when the timeline completes in order to allow clean garbage collection, so temporarily re-enable the timeline here.
13397             }
13398             tweens = TweenLite.getTweensOf(target);
13399             i = tweens.length;
13400             while (--i > -1) {
13401                 if (tweens[i].timeline === this || (nested && this._contains(tweens[i]))) {
13402                     a[cnt++] = tweens[i];
13403                 }
13404             }
13405             if (disabled) {
13406                 this._enabled(false, true);
13407             }
13408             return a;
13409         };
13410
13411         p._contains = function(tween) {
13412             var tl = tween.timeline;
13413             while (tl) {
13414                 if (tl === this) {
13415                     return true;
13416                 }
13417                 tl = tl.timeline;
13418             }
13419             return false;
13420         };
13421
13422         p.shiftChildren = function(amount, adjustLabels, ignoreBeforeTime) {
13423             ignoreBeforeTime = ignoreBeforeTime || 0;
13424             var tween = this._first,
13425                 labels = this._labels,
13426                 p;
13427             while (tween) {
13428                 if (tween._startTime >= ignoreBeforeTime) {
13429                     tween._startTime += amount;
13430                 }
13431                 tween = tween._next;
13432             }
13433             if (adjustLabels) {
13434                 for (p in labels) {
13435                     if (labels[p] >= ignoreBeforeTime) {
13436                         labels[p] += amount;
13437                     }
13438                 }
13439             }
13440             return this._uncache(true);
13441         };
13442
13443         p._kill = function(vars, target) {
13444             if (!vars && !target) {
13445                 return this._enabled(false, false);
13446             }
13447             var tweens = (!target) ? this.getChildren(true, true, false) : this.getTweensOf(target),
13448                 i = tweens.length,
13449                 changed = false;
13450             while (--i > -1) {
13451                 if (tweens[i]._kill(vars, target)) {
13452                     changed = true;
13453                 }
13454             }
13455             return changed;
13456         };
13457
13458         p.clear = function(labels) {
13459             var tweens = this.getChildren(false, true, true),
13460                 i = tweens.length;
13461             this._time = this._totalTime = 0;
13462             while (--i > -1) {
13463                 tweens[i]._enabled(false, false);
13464             }
13465             if (labels !== false) {
13466                 this._labels = {};
13467             }
13468             return this._uncache(true);
13469         };
13470
13471         p.invalidate = function() {
13472             var tween = this._first;
13473             while (tween) {
13474                 tween.invalidate();
13475                 tween = tween._next;
13476             }
13477             return this;
13478         };
13479
13480         p._enabled = function(enabled, ignoreTimeline) {
13481             if (enabled === this._gc) {
13482                 var tween = this._first;
13483                 while (tween) {
13484                     tween._enabled(enabled, true);
13485                     tween = tween._next;
13486                 }
13487             }
13488             return SimpleTimeline.prototype._enabled.call(this, enabled, ignoreTimeline);
13489         };
13490
13491         p.duration = function(value) {
13492             if (!arguments.length) {
13493                 if (this._dirty) {
13494                     this.totalDuration(); //just triggers recalculation
13495                 }
13496                 return this._duration;
13497             }
13498             if (this.duration() !== 0 && value !== 0) {
13499                 this.timeScale(this._duration / value);
13500             }
13501             return this;
13502         };
13503
13504         p.totalDuration = function(value) {
13505             if (!arguments.length) {
13506                 if (this._dirty) {
13507                     var max = 0,
13508                         tween = this._last,
13509                         prevStart = 999999999999,
13510                         prev, end;
13511                     while (tween) {
13512                         prev = tween._prev; //record it here in case the tween changes position in the sequence...
13513                         if (tween._dirty) {
13514                             tween.totalDuration(); //could change the tween._startTime, so make sure the tween's cache is clean before analyzing it.
13515                         }
13516                         if (tween._startTime > prevStart && this._sortChildren && !tween._paused) { //in case one of the tweens shifted out of order, it needs to be re-inserted into the correct position in the sequence
13517                             this.add(tween, tween._startTime - tween._delay);
13518                         } else {
13519                             prevStart = tween._startTime;
13520                         }
13521                         if (tween._startTime < 0 && !tween._paused) { //children aren't allowed to have negative startTimes unless smoothChildTiming is true, so adjust here if one is found.
13522                             max -= tween._startTime;
13523                             if (this._timeline.smoothChildTiming) {
13524                                 this._startTime += tween._startTime / this._timeScale;
13525                             }
13526                             this.shiftChildren(-tween._startTime, false, -9999999999);
13527                             prevStart = 0;
13528                         }
13529                         end = tween._startTime + (tween._totalDuration / tween._timeScale);
13530                         if (end > max) {
13531                             max = end;
13532                         }
13533                         tween = prev;
13534                     }
13535                     this._duration = this._totalDuration = max;
13536                     this._dirty = false;
13537                 }
13538                 return this._totalDuration;
13539             }
13540             if (this.totalDuration() !== 0) if (value !== 0) {
13541                 this.timeScale(this._totalDuration / value);
13542             }
13543             return this;
13544         };
13545
13546         p.usesFrames = function() {
13547             var tl = this._timeline;
13548             while (tl._timeline) {
13549                 tl = tl._timeline;
13550             }
13551             return (tl === Animation._rootFramesTimeline);
13552         };
13553
13554         p.rawTime = function() {
13555             return this._paused ? this._totalTime : (this._timeline.rawTime() - this._startTime) * this._timeScale;
13556         };
13557
13558         return TimelineLite;
13559
13560     }, true);
13561     
13562
13563
13564
13565
13566
13567
13568
13569     
13570     
13571     
13572     
13573     
13574 /*
13575  * ----------------------------------------------------------------
13576  * TimelineMax
13577  * ----------------------------------------------------------------
13578  */
13579     window._gsDefine("TimelineMax", ["TimelineLite","TweenLite","easing.Ease"], function(TimelineLite, TweenLite, Ease) {
13580
13581         var TimelineMax = function(vars) {
13582                 TimelineLite.call(this, vars);
13583                 this._repeat = this.vars.repeat || 0;
13584                 this._repeatDelay = this.vars.repeatDelay || 0;
13585                 this._cycle = 0;
13586                 this._yoyo = (this.vars.yoyo === true);
13587                 this._dirty = true;
13588             },
13589             _tinyNum = 0.0000000001,
13590             _blankArray = [],
13591             _easeNone = new Ease(null, null, 1, 0),
13592             p = TimelineMax.prototype = new TimelineLite();
13593
13594         p.constructor = TimelineMax;
13595         p.kill()._gc = false;
13596         TimelineMax.version = "1.12.1";
13597
13598         p.invalidate = function() {
13599             this._yoyo = (this.vars.yoyo === true);
13600             this._repeat = this.vars.repeat || 0;
13601             this._repeatDelay = this.vars.repeatDelay || 0;
13602             this._uncache(true);
13603             return TimelineLite.prototype.invalidate.call(this);
13604         };
13605
13606         p.addCallback = function(callback, position, params, scope) {
13607             return this.add( TweenLite.delayedCall(0, callback, params, scope), position);
13608         };
13609
13610         p.removeCallback = function(callback, position) {
13611             if (callback) {
13612                 if (position == null) {
13613                     this._kill(null, callback);
13614                 } else {
13615                     var a = this.getTweensOf(callback, false),
13616                         i = a.length,
13617                         time = this._parseTimeOrLabel(position);
13618                     while (--i > -1) {
13619                         if (a[i]._startTime === time) {
13620                             a[i]._enabled(false, false);
13621                         }
13622                     }
13623                 }
13624             }
13625             return this;
13626         };
13627
13628         p.tweenTo = function(position, vars) {
13629             vars = vars || {};
13630             var copy = {ease:_easeNone, overwrite:(vars.delay ? 2 : 1), useFrames:this.usesFrames(), immediateRender:false},//note: set overwrite to 1 (true/all) by default unless there's a delay so that we avoid a racing situation that could happen if, for example, an onmousemove creates the same tweenTo() over and over again.
13631                 duration, p, t;
13632             for (p in vars) {
13633                 copy[p] = vars[p];
13634             }
13635             copy.time = this._parseTimeOrLabel(position);
13636             duration = (Math.abs(Number(copy.time) - this._time) / this._timeScale) || 0.001;
13637             t = new TweenLite(this, duration, copy);
13638             copy.onStart = function() {
13639                 t.target.paused(true);
13640                 if (t.vars.time !== t.target.time() && duration === t.duration()) { //don't make the duration zero - if it's supposed to be zero, don't worry because it's already initting the tween and will complete immediately, effectively making the duration zero anyway. If we make duration zero, the tween won't run at all.
13641                     t.duration( Math.abs( t.vars.time - t.target.time()) / t.target._timeScale );
13642                 }
13643                 if (vars.onStart) { //in case the user had an onStart in the vars - we don't want to overwrite it.
13644                     vars.onStart.apply(vars.onStartScope || t, vars.onStartParams || _blankArray);
13645                 }
13646             };
13647             return t;
13648         };
13649
13650         p.tweenFromTo = function(fromPosition, toPosition, vars) {
13651             vars = vars || {};
13652             fromPosition = this._parseTimeOrLabel(fromPosition);
13653             vars.startAt = {onComplete:this.seek, onCompleteParams:[fromPosition], onCompleteScope:this};
13654             vars.immediateRender = (vars.immediateRender !== false);
13655             var t = this.tweenTo(toPosition, vars);
13656             return t.duration((Math.abs( t.vars.time - fromPosition) / this._timeScale) || 0.001);
13657         };
13658
13659         p.render = function(time, suppressEvents, force) {
13660             if (this._gc) {
13661                 this._enabled(true, false);
13662             }
13663             var totalDur = (!this._dirty) ? this._totalDuration : this.totalDuration(),
13664                 dur = this._duration,
13665                 prevTime = this._time,
13666                 prevTotalTime = this._totalTime,
13667                 prevStart = this._startTime,
13668                 prevTimeScale = this._timeScale,
13669                 prevRawPrevTime = this._rawPrevTime,
13670                 prevPaused = this._paused,
13671                 prevCycle = this._cycle,
13672                 tween, isComplete, next, callback, internalForce, cycleDuration;
13673             if (time >= totalDur) {
13674                 if (!this._locked) {
13675                     this._totalTime = totalDur;
13676                     this._cycle = this._repeat;
13677                 }
13678                 if (!this._reversed) if (!this._hasPausedChild()) {
13679                     isComplete = true;
13680                     callback = "onComplete";
13681                     if (this._duration === 0) if (time === 0 || prevRawPrevTime < 0 || prevRawPrevTime === _tinyNum) if (prevRawPrevTime !== time && this._first) {
13682                         internalForce = true;
13683                         if (prevRawPrevTime > _tinyNum) {
13684                             callback = "onReverseComplete";
13685                         }
13686                     }
13687                 }
13688                 this._rawPrevTime = (this._duration || !suppressEvents || time || this._rawPrevTime === time) ? time : _tinyNum; //when the playhead arrives at EXACTLY time 0 (right on top) of a zero-duration timeline or tween, we need to discern if events are suppressed so that when the playhead moves again (next time), it'll trigger the callback. If events are NOT suppressed, obviously the callback would be triggered in this render. Basically, the callback should fire either when the playhead ARRIVES or LEAVES this exact spot, not both. Imagine doing a timeline.seek(0) and there's a callback that sits at 0. Since events are suppressed on that seek() by default, nothing will fire, but when the playhead moves off of that position, the callback should fire. This behavior is what people intuitively expect. We set the _rawPrevTime to be a precise tiny number to indicate this scenario rather than using another property/variable which would increase memory usage. This technique is less readable, but more efficient.
13689                 if (this._yoyo && (this._cycle & 1) !== 0) {
13690                     this._time = time = 0;
13691                 } else {
13692                     this._time = dur;
13693                     time = dur + 0.0001; //to avoid occasional floating point rounding errors - sometimes child tweens/timelines were not being fully completed (their progress might be 0.999999999999998 instead of 1 because when _time - tween._startTime is performed, floating point errors would return a value that was SLIGHTLY off). Try (999999999999.7 - 999999999999) * 1 = 0.699951171875 instead of 0.7. We cannot do less then 0.0001 because the same issue can occur when the duration is extremely large like 999999999999 in which case adding 0.00000001, for example, causes it to act like nothing was added.
13694                 }
13695
13696             } else if (time < 0.0000001) { //to work around occasional floating point math artifacts, round super small values to 0.
13697                 if (!this._locked) {
13698                     this._totalTime = this._cycle = 0;
13699                 }
13700                 this._time = 0;
13701                 if (prevTime !== 0 || (dur === 0 && prevRawPrevTime !== _tinyNum && (prevRawPrevTime > 0 || (time < 0 && prevRawPrevTime >= 0)) && !this._locked)) { //edge case for checking time < 0 && prevRawPrevTime >= 0: a zero-duration fromTo() tween inside a zero-duration timeline (yeah, very rare)
13702                     callback = "onReverseComplete";
13703                     isComplete = this._reversed;
13704                 }
13705                 if (time < 0) {
13706                     this._active = false;
13707                     if (dur === 0) if (prevRawPrevTime >= 0 && this._first) { //zero-duration timelines are tricky because we must discern the momentum/direction of time in order to determine whether the starting values should be rendered or the ending values. If the "playhead" of its timeline goes past the zero-duration tween in the forward direction or lands directly on it, the end values should be rendered, but if the timeline's "playhead" moves past it in the backward direction (from a postitive time to a negative time), the starting values must be rendered.
13708                         internalForce = true;
13709                     }
13710                     this._rawPrevTime = time;
13711                 } else {
13712                     this._rawPrevTime = (dur || !suppressEvents || time || this._rawPrevTime === time) ? time : _tinyNum; //when the playhead arrives at EXACTLY time 0 (right on top) of a zero-duration timeline or tween, we need to discern if events are suppressed so that when the playhead moves again (next time), it'll trigger the callback. If events are NOT suppressed, obviously the callback would be triggered in this render. Basically, the callback should fire either when the playhead ARRIVES or LEAVES this exact spot, not both. Imagine doing a timeline.seek(0) and there's a callback that sits at 0. Since events are suppressed on that seek() by default, nothing will fire, but when the playhead moves off of that position, the callback should fire. This behavior is what people intuitively expect. We set the _rawPrevTime to be a precise tiny number to indicate this scenario rather than using another property/variable which would increase memory usage. This technique is less readable, but more efficient.
13713                     time = 0; //to avoid occasional floating point rounding errors (could cause problems especially with zero-duration tweens at the very beginning of the timeline)
13714                     if (!this._initted) {
13715                         internalForce = true;
13716                     }
13717                 }
13718
13719             } else {
13720                 if (dur === 0 && prevRawPrevTime < 0) { //without this, zero-duration repeating timelines (like with a simple callback nested at the very beginning and a repeatDelay) wouldn't render the first time through.
13721                     internalForce = true;
13722                 }
13723                 this._time = this._rawPrevTime = time;
13724                 if (!this._locked) {
13725                     this._totalTime = time;
13726                     if (this._repeat !== 0) {
13727                         cycleDuration = dur + this._repeatDelay;
13728                         this._cycle = (this._totalTime / cycleDuration) >> 0; //originally _totalTime % cycleDuration but floating point errors caused problems, so I normalized it. (4 % 0.8 should be 0 but it gets reported as 0.79999999!)
13729                         if (this._cycle !== 0) if (this._cycle === this._totalTime / cycleDuration) {
13730                             this._cycle--; //otherwise when rendered exactly at the end time, it will act as though it is repeating (at the beginning)
13731                         }
13732                         this._time = this._totalTime - (this._cycle * cycleDuration);
13733                         if (this._yoyo) if ((this._cycle & 1) !== 0) {
13734                             this._time = dur - this._time;
13735                         }
13736                         if (this._time > dur) {
13737                             this._time = dur;
13738                             time = dur + 0.0001; //to avoid occasional floating point rounding error
13739                         } else if (this._time < 0) {
13740                             this._time = time = 0;
13741                         } else {
13742                             time = this._time;
13743                         }
13744                     }
13745                 }
13746             }
13747
13748             if (this._cycle !== prevCycle) if (!this._locked) {
13749                 /*
13750                 make sure children at the end/beginning of the timeline are rendered properly. If, for example,
13751                 a 3-second long timeline rendered at 2.9 seconds previously, and now renders at 3.2 seconds (which
13752                 would get transated to 2.8 seconds if the timeline yoyos or 0.2 seconds if it just repeats), there
13753                 could be a callback or a short tween that's at 2.95 or 3 seconds in which wouldn't render. So
13754                 we need to push the timeline to the end (and/or beginning depending on its yoyo value). Also we must
13755                 ensure that zero-duration tweens at the very beginning or end of the TimelineMax work.
13756                 */
13757                 var backwards = (this._yoyo && (prevCycle & 1) !== 0),
13758                     wrap = (backwards === (this._yoyo && (this._cycle & 1) !== 0)),
13759                     recTotalTime = this._totalTime,
13760                     recCycle = this._cycle,
13761                     recRawPrevTime = this._rawPrevTime,
13762                     recTime = this._time;
13763
13764                 this._totalTime = prevCycle * dur;
13765                 if (this._cycle < prevCycle) {
13766                     backwards = !backwards;
13767                 } else {
13768                     this._totalTime += dur;
13769                 }
13770                 this._time = prevTime; //temporarily revert _time so that render() renders the children in the correct order. Without this, tweens won't rewind correctly. We could arhictect things in a "cleaner" way by splitting out the rendering queue into a separate method but for performance reasons, we kept it all inside this method.
13771
13772                 this._rawPrevTime = (dur === 0) ? prevRawPrevTime - 0.0001 : prevRawPrevTime;
13773                 this._cycle = prevCycle;
13774                 this._locked = true; //prevents changes to totalTime and skips repeat/yoyo behavior when we recursively call render()
13775                 prevTime = (backwards) ? 0 : dur;
13776                 this.render(prevTime, suppressEvents, (dur === 0));
13777                 if (!suppressEvents) if (!this._gc) {
13778                     if (this.vars.onRepeat) {
13779                         this.vars.onRepeat.apply(this.vars.onRepeatScope || this, this.vars.onRepeatParams || _blankArray);
13780                     }
13781                 }
13782                 if (wrap) {
13783                     prevTime = (backwards) ? dur + 0.0001 : -0.0001;
13784                     this.render(prevTime, true, false);
13785                 }
13786                 this._locked = false;
13787                 if (this._paused && !prevPaused) { //if the render() triggered callback that paused this timeline, we should abort (very rare, but possible)
13788                     return;
13789                 }
13790                 this._time = recTime;
13791                 this._totalTime = recTotalTime;
13792                 this._cycle = recCycle;
13793                 this._rawPrevTime = recRawPrevTime;
13794             }
13795
13796             if ((this._time === prevTime || !this._first) && !force && !internalForce) {
13797                 if (prevTotalTime !== this._totalTime) if (this._onUpdate) if (!suppressEvents) { //so that onUpdate fires even during the repeatDelay - as long as the totalTime changed, we should trigger onUpdate.
13798                     this._onUpdate.apply(this.vars.onUpdateScope || this, this.vars.onUpdateParams || _blankArray);
13799                 }
13800                 return;
13801             } else if (!this._initted) {
13802                 this._initted = true;
13803             }
13804
13805             if (!this._active) if (!this._paused && this._totalTime !== prevTotalTime && time > 0) {
13806                 this._active = true;  //so that if the user renders the timeline (as opposed to the parent timeline rendering it), it is forced to re-render and align it with the proper time/frame on the next rendering cycle. Maybe the timeline already finished but the user manually re-renders it as halfway done, for example.
13807             }
13808
13809             if (prevTotalTime === 0) if (this.vars.onStart) if (this._totalTime !== 0) if (!suppressEvents) {
13810                 this.vars.onStart.apply(this.vars.onStartScope || this, this.vars.onStartParams || _blankArray);
13811             }
13812
13813             if (this._time >= prevTime) {
13814                 tween = this._first;
13815                 while (tween) {
13816                     next = tween._next; //record it here because the value could change after rendering...
13817                     if (this._paused && !prevPaused) { //in case a tween pauses the timeline when rendering
13818                         break;
13819                     } else if (tween._active || (tween._startTime <= this._time && !tween._paused && !tween._gc)) {
13820                         if (!tween._reversed) {
13821                             tween.render((time - tween._startTime) * tween._timeScale, suppressEvents, force);
13822                         } else {
13823                             tween.render(((!tween._dirty) ? tween._totalDuration : tween.totalDuration()) - ((time - tween._startTime) * tween._timeScale), suppressEvents, force);
13824                         }
13825
13826                     }
13827                     tween = next;
13828                 }
13829             } else {
13830                 tween = this._last;
13831                 while (tween) {
13832                     next = tween._prev; //record it here because the value could change after rendering...
13833                     if (this._paused && !prevPaused) { //in case a tween pauses the timeline when rendering
13834                         break;
13835                     } else if (tween._active || (tween._startTime <= prevTime && !tween._paused && !tween._gc)) {
13836                         if (!tween._reversed) {
13837                             tween.render((time - tween._startTime) * tween._timeScale, suppressEvents, force);
13838                         } else {
13839                             tween.render(((!tween._dirty) ? tween._totalDuration : tween.totalDuration()) - ((time - tween._startTime) * tween._timeScale), suppressEvents, force);
13840                         }
13841                     }
13842                     tween = next;
13843                 }
13844             }
13845
13846             if (this._onUpdate) if (!suppressEvents) {
13847                 this._onUpdate.apply(this.vars.onUpdateScope || this, this.vars.onUpdateParams || _blankArray);
13848             }
13849             if (callback) if (!this._locked) if (!this._gc) if (prevStart === this._startTime || prevTimeScale !== this._timeScale) if (this._time === 0 || totalDur >= this.totalDuration()) { //if one of the tweens that was rendered altered this timeline's startTime (like if an onComplete reversed the timeline), it probably isn't complete. If it is, don't worry, because whatever call altered the startTime would complete if it was necessary at the new time. The only exception is the timeScale property. Also check _gc because there's a chance that kill() could be called in an onUpdate
13850                 if (isComplete) {
13851                     if (this._timeline.autoRemoveChildren) {
13852                         this._enabled(false, false);
13853                     }
13854                     this._active = false;
13855                 }
13856                 if (!suppressEvents && this.vars[callback]) {
13857                     this.vars[callback].apply(this.vars[callback + "Scope"] || this, this.vars[callback + "Params"] || _blankArray);
13858                 }
13859             }
13860         };
13861
13862         p.getActive = function(nested, tweens, timelines) {
13863             if (nested == null) {
13864                 nested = true;
13865             }
13866             if (tweens == null) {
13867                 tweens = true;
13868             }
13869             if (timelines == null) {
13870                 timelines = false;
13871             }
13872             var a = [],
13873                 all = this.getChildren(nested, tweens, timelines),
13874                 cnt = 0,
13875                 l = all.length,
13876                 i, tween;
13877             for (i = 0; i < l; i++) {
13878                 tween = all[i];
13879                 if (tween.isActive()) {
13880                     a[cnt++] = tween;
13881                 }
13882             }
13883             return a;
13884         };
13885
13886
13887         p.getLabelAfter = function(time) {
13888             if (!time) if (time !== 0) { //faster than isNan()
13889                 time = this._time;
13890             }
13891             var labels = this.getLabelsArray(),
13892                 l = labels.length,
13893                 i;
13894             for (i = 0; i < l; i++) {
13895                 if (labels[i].time > time) {
13896                     return labels[i].name;
13897                 }
13898             }
13899             return null;
13900         };
13901
13902         p.getLabelBefore = function(time) {
13903             if (time == null) {
13904                 time = this._time;
13905             }
13906             var labels = this.getLabelsArray(),
13907                 i = labels.length;
13908             while (--i > -1) {
13909                 if (labels[i].time < time) {
13910                     return labels[i].name;
13911                 }
13912             }
13913             return null;
13914         };
13915
13916         p.getLabelsArray = function() {
13917             var a = [],
13918                 cnt = 0,
13919                 p;
13920             for (p in this._labels) {
13921                 a[cnt++] = {time:this._labels[p], name:p};
13922             }
13923             a.sort(function(a,b) {
13924                 return a.time - b.time;
13925             });
13926             return a;
13927         };
13928
13929
13930 //---- GETTERS / SETTERS -------------------------------------------------------------------------------------------------------
13931
13932         p.progress = function(value) {
13933             return (!arguments.length) ? this._time / this.duration() : this.totalTime( this.duration() * ((this._yoyo && (this._cycle & 1) !== 0) ? 1 - value : value) + (this._cycle * (this._duration + this._repeatDelay)), false);
13934         };
13935
13936         p.totalProgress = function(value) {
13937             return (!arguments.length) ? this._totalTime / this.totalDuration() : this.totalTime( this.totalDuration() * value, false);
13938         };
13939
13940         p.totalDuration = function(value) {
13941             if (!arguments.length) {
13942                 if (this._dirty) {
13943                     TimelineLite.prototype.totalDuration.call(this); //just forces refresh
13944                     //Instead of Infinity, we use 999999999999 so that we can accommodate reverses.
13945                     this._totalDuration = (this._repeat === -1) ? 999999999999 : this._duration * (this._repeat + 1) + (this._repeatDelay * this._repeat);
13946                 }
13947                 return this._totalDuration;
13948             }
13949             return (this._repeat === -1) ? this : this.duration( (value - (this._repeat * this._repeatDelay)) / (this._repeat + 1) );
13950         };
13951
13952         p.time = function(value, suppressEvents) {
13953             if (!arguments.length) {
13954                 return this._time;
13955             }
13956             if (this._dirty) {
13957                 this.totalDuration();
13958             }
13959             if (value > this._duration) {
13960                 value = this._duration;
13961             }
13962             if (this._yoyo && (this._cycle & 1) !== 0) {
13963                 value = (this._duration - value) + (this._cycle * (this._duration + this._repeatDelay));
13964             } else if (this._repeat !== 0) {
13965                 value += this._cycle * (this._duration + this._repeatDelay);
13966             }
13967             return this.totalTime(value, suppressEvents);
13968         };
13969
13970         p.repeat = function(value) {
13971             if (!arguments.length) {
13972                 return this._repeat;
13973             }
13974             this._repeat = value;
13975             return this._uncache(true);
13976         };
13977
13978         p.repeatDelay = function(value) {
13979             if (!arguments.length) {
13980                 return this._repeatDelay;
13981             }
13982             this._repeatDelay = value;
13983             return this._uncache(true);
13984         };
13985
13986         p.yoyo = function(value) {
13987             if (!arguments.length) {
13988                 return this._yoyo;
13989             }
13990             this._yoyo = value;
13991             return this;
13992         };
13993
13994         p.currentLabel = function(value) {
13995             if (!arguments.length) {
13996                 return this.getLabelBefore(this._time + 0.00000001);
13997             }
13998             return this.seek(value, true);
13999         };
14000
14001         return TimelineMax;
14002
14003     }, true);
14004     
14005
14006
14007
14008
14009     
14010     
14011     
14012     
14013     
14014     
14015     
14016 /*
14017  * ----------------------------------------------------------------
14018  * BezierPlugin
14019  * ----------------------------------------------------------------
14020  */
14021     (function() {
14022
14023         var _RAD2DEG = 180 / Math.PI,
14024             _r1 = [],
14025             _r2 = [],
14026             _r3 = [],
14027             _corProps = {},
14028             Segment = function(a, b, c, d) {
14029                 this.a = a;
14030                 this.b = b;
14031                 this.c = c;
14032                 this.d = d;
14033                 this.da = d - a;
14034                 this.ca = c - a;
14035                 this.ba = b - a;
14036             },
14037             _correlate = ",x,y,z,left,top,right,bottom,marginTop,marginLeft,marginRight,marginBottom,paddingLeft,paddingTop,paddingRight,paddingBottom,backgroundPosition,backgroundPosition_y,",
14038             cubicToQuadratic = function(a, b, c, d) {
14039                 var q1 = {a:a},
14040                     q2 = {},
14041                     q3 = {},
14042                     q4 = {c:d},
14043                     mab = (a + b) / 2,
14044                     mbc = (b + c) / 2,
14045                     mcd = (c + d) / 2,
14046                     mabc = (mab + mbc) / 2,
14047                     mbcd = (mbc + mcd) / 2,
14048                     m8 = (mbcd - mabc) / 8;
14049                 q1.b = mab + (a - mab) / 4;
14050                 q2.b = mabc + m8;
14051                 q1.c = q2.a = (q1.b + q2.b) / 2;
14052                 q2.c = q3.a = (mabc + mbcd) / 2;
14053                 q3.b = mbcd - m8;
14054                 q4.b = mcd + (d - mcd) / 4;
14055                 q3.c = q4.a = (q3.b + q4.b) / 2;
14056                 return [q1, q2, q3, q4];
14057             },
14058             _calculateControlPoints = function(a, curviness, quad, basic, correlate) {
14059                 var l = a.length - 1,
14060                     ii = 0,
14061                     cp1 = a[0].a,
14062                     i, p1, p2, p3, seg, m1, m2, mm, cp2, qb, r1, r2, tl;
14063                 for (i = 0; i < l; i++) {
14064                     seg = a[ii];
14065                     p1 = seg.a;
14066                     p2 = seg.d;
14067                     p3 = a[ii+1].d;
14068
14069                     if (correlate) {
14070                         r1 = _r1[i];
14071                         r2 = _r2[i];
14072                         tl = ((r2 + r1) * curviness * 0.25) / (basic ? 0.5 : _r3[i] || 0.5);
14073                         m1 = p2 - (p2 - p1) * (basic ? curviness * 0.5 : (r1 !== 0 ? tl / r1 : 0));
14074                         m2 = p2 + (p3 - p2) * (basic ? curviness * 0.5 : (r2 !== 0 ? tl / r2 : 0));
14075                         mm = p2 - (m1 + (((m2 - m1) * ((r1 * 3 / (r1 + r2)) + 0.5) / 4) || 0));
14076                     } else {
14077                         m1 = p2 - (p2 - p1) * curviness * 0.5;
14078                         m2 = p2 + (p3 - p2) * curviness * 0.5;
14079                         mm = p2 - (m1 + m2) / 2;
14080                     }
14081                     m1 += mm;
14082                     m2 += mm;
14083
14084                     seg.c = cp2 = m1;
14085                     if (i !== 0) {
14086                         seg.b = cp1;
14087                     } else {
14088                         seg.b = cp1 = seg.a + (seg.c - seg.a) * 0.6; //instead of placing b on a exactly, we move it inline with c so that if the user specifies an ease like Back.easeIn or Elastic.easeIn which goes BEYOND the beginning, it will do so smoothly.
14089                     }
14090
14091                     seg.da = p2 - p1;
14092                     seg.ca = cp2 - p1;
14093                     seg.ba = cp1 - p1;
14094
14095                     if (quad) {
14096                         qb = cubicToQuadratic(p1, cp1, cp2, p2);
14097                         a.splice(ii, 1, qb[0], qb[1], qb[2], qb[3]);
14098                         ii += 4;
14099                     } else {
14100                         ii++;
14101                     }
14102
14103                     cp1 = m2;
14104                 }
14105                 seg = a[ii];
14106                 seg.b = cp1;
14107                 seg.c = cp1 + (seg.d - cp1) * 0.4; //instead of placing c on d exactly, we move it inline with b so that if the user specifies an ease like Back.easeOut or Elastic.easeOut which goes BEYOND the end, it will do so smoothly.
14108                 seg.da = seg.d - seg.a;
14109                 seg.ca = seg.c - seg.a;
14110                 seg.ba = cp1 - seg.a;
14111                 if (quad) {
14112                     qb = cubicToQuadratic(seg.a, cp1, seg.c, seg.d);
14113                     a.splice(ii, 1, qb[0], qb[1], qb[2], qb[3]);
14114                 }
14115             },
14116             _parseAnchors = function(values, p, correlate, prepend) {
14117                 var a = [],
14118                     l, i, p1, p2, p3, tmp;
14119                 if (prepend) {
14120                     values = [prepend].concat(values);
14121                     i = values.length;
14122                     while (--i > -1) {
14123                         if (typeof( (tmp = values[i][p]) ) === "string") if (tmp.charAt(1) === "=") {
14124                             values[i][p] = prepend[p] + Number(tmp.charAt(0) + tmp.substr(2)); //accommodate relative values. Do it inline instead of breaking it out into a function for speed reasons
14125                         }
14126                     }
14127                 }
14128                 l = values.length - 2;
14129                 if (l < 0) {
14130                     a[0] = new Segment(values[0][p], 0, 0, values[(l < -1) ? 0 : 1][p]);
14131                     return a;
14132                 }
14133                 for (i = 0; i < l; i++) {
14134                     p1 = values[i][p];
14135                     p2 = values[i+1][p];
14136                     a[i] = new Segment(p1, 0, 0, p2);
14137                     if (correlate) {
14138                         p3 = values[i+2][p];
14139                         _r1[i] = (_r1[i] || 0) + (p2 - p1) * (p2 - p1);
14140                         _r2[i] = (_r2[i] || 0) + (p3 - p2) * (p3 - p2);
14141                     }
14142                 }
14143                 a[i] = new Segment(values[i][p], 0, 0, values[i+1][p]);
14144                 return a;
14145             },
14146             bezierThrough = function(values, curviness, quadratic, basic, correlate, prepend) {
14147                 var obj = {},
14148                     props = [],
14149                     first = prepend || values[0],
14150                     i, p, a, j, r, l, seamless, last;
14151                 correlate = (typeof(correlate) === "string") ? ","+correlate+"," : _correlate;
14152                 if (curviness == null) {
14153                     curviness = 1;
14154                 }
14155                 for (p in values[0]) {
14156                     props.push(p);
14157                 }
14158                 //check to see if the last and first values are identical (well, within 0.05). If so, make seamless by appending the second element to the very end of the values array and the 2nd-to-last element to the very beginning (we'll remove those segments later)
14159                 if (values.length > 1) {
14160                     last = values[values.length - 1];
14161                     seamless = true;
14162                     i = props.length;
14163                     while (--i > -1) {
14164                         p = props[i];
14165                         if (Math.abs(first[p] - last[p]) > 0.05) { //build in a tolerance of +/-0.05 to accommodate rounding errors. For example, if you set an object's position to 4.945, Flash will make it 4.9
14166                             seamless = false;
14167                             break;
14168                         }
14169                     }
14170                     if (seamless) {
14171                         values = values.concat(); //duplicate the array to avoid contaminating the original which the user may be reusing for other tweens
14172                         if (prepend) {
14173                             values.unshift(prepend);
14174                         }
14175                         values.push(values[1]);
14176                         prepend = values[values.length - 3];
14177                     }
14178                 }
14179                 _r1.length = _r2.length = _r3.length = 0;
14180                 i = props.length;
14181                 while (--i > -1) {
14182                     p = props[i];
14183                     _corProps[p] = (correlate.indexOf(","+p+",") !== -1);
14184                     obj[p] = _parseAnchors(values, p, _corProps[p], prepend);
14185                 }
14186                 i = _r1.length;
14187                 while (--i > -1) {
14188                     _r1[i] = Math.sqrt(_r1[i]);
14189                     _r2[i] = Math.sqrt(_r2[i]);
14190                 }
14191                 if (!basic) {
14192                     i = props.length;
14193                     while (--i > -1) {
14194                         if (_corProps[p]) {
14195                             a = obj[props[i]];
14196                             l = a.length - 1;
14197                             for (j = 0; j < l; j++) {
14198                                 r = a[j+1].da / _r2[j] + a[j].da / _r1[j];
14199                                 _r3[j] = (_r3[j] || 0) + r * r;
14200                             }
14201                         }
14202                     }
14203                     i = _r3.length;
14204                     while (--i > -1) {
14205                         _r3[i] = Math.sqrt(_r3[i]);
14206                     }
14207                 }
14208                 i = props.length;
14209                 j = quadratic ? 4 : 1;
14210                 while (--i > -1) {
14211                     p = props[i];
14212                     a = obj[p];
14213                     _calculateControlPoints(a, curviness, quadratic, basic, _corProps[p]); //this method requires that _parseAnchors() and _setSegmentRatios() ran first so that _r1, _r2, and _r3 values are populated for all properties
14214                     if (seamless) {
14215                         a.splice(0, j);
14216                         a.splice(a.length - j, j);
14217                     }
14218                 }
14219                 return obj;
14220             },
14221             _parseBezierData = function(values, type, prepend) {
14222                 type = type || "soft";
14223                 var obj = {},
14224                     inc = (type === "cubic") ? 3 : 2,
14225                     soft = (type === "soft"),
14226                     props = [],
14227                     a, b, c, d, cur, i, j, l, p, cnt, tmp;
14228                 if (soft && prepend) {
14229                     values = [prepend].concat(values);
14230                 }
14231                 if (values == null || values.length < inc + 1) { throw "invalid Bezier data"; }
14232                 for (p in values[0]) {
14233                     props.push(p);
14234                 }
14235                 i = props.length;
14236                 while (--i > -1) {
14237                     p = props[i];
14238                     obj[p] = cur = [];
14239                     cnt = 0;
14240                     l = values.length;
14241                     for (j = 0; j < l; j++) {
14242                         a = (prepend == null) ? values[j][p] : (typeof( (tmp = values[j][p]) ) === "string" && tmp.charAt(1) === "=") ? prepend[p] + Number(tmp.charAt(0) + tmp.substr(2)) : Number(tmp);
14243                         if (soft) if (j > 1) if (j < l - 1) {
14244                             cur[cnt++] = (a + cur[cnt-2]) / 2;
14245                         }
14246                         cur[cnt++] = a;
14247                     }
14248                     l = cnt - inc + 1;
14249                     cnt = 0;
14250                     for (j = 0; j < l; j += inc) {
14251                         a = cur[j];
14252                         b = cur[j+1];
14253                         c = cur[j+2];
14254                         d = (inc === 2) ? 0 : cur[j+3];
14255                         cur[cnt++] = tmp = (inc === 3) ? new Segment(a, b, c, d) : new Segment(a, (2 * b + a) / 3, (2 * b + c) / 3, c);
14256                     }
14257                     cur.length = cnt;
14258                 }
14259                 return obj;
14260             },
14261             _addCubicLengths = function(a, steps, resolution) {
14262                 var inc = 1 / resolution,
14263                     j = a.length,
14264                     d, d1, s, da, ca, ba, p, i, inv, bez, index;
14265                 while (--j > -1) {
14266                     bez = a[j];
14267                     s = bez.a;
14268                     da = bez.d - s;
14269                     ca = bez.c - s;
14270                     ba = bez.b - s;
14271                     d = d1 = 0;
14272                     for (i = 1; i <= resolution; i++) {
14273                         p = inc * i;
14274                         inv = 1 - p;
14275                         d = d1 - (d1 = (p * p * da + 3 * inv * (p * ca + inv * ba)) * p);
14276                         index = j * resolution + i - 1;
14277                         steps[index] = (steps[index] || 0) + d * d;
14278                     }
14279                 }
14280             },
14281             _parseLengthData = function(obj, resolution) {
14282                 resolution = resolution >> 0 || 6;
14283                 var a = [],
14284                     lengths = [],
14285                     d = 0,
14286                     total = 0,
14287                     threshold = resolution - 1,
14288                     segments = [],
14289                     curLS = [], //current length segments array
14290                     p, i, l, index;
14291                 for (p in obj) {
14292                     _addCubicLengths(obj[p], a, resolution);
14293                 }
14294                 l = a.length;
14295                 for (i = 0; i < l; i++) {
14296                     d += Math.sqrt(a[i]);
14297                     index = i % resolution;
14298                     curLS[index] = d;
14299                     if (index === threshold) {
14300                         total += d;
14301                         index = (i / resolution) >> 0;
14302                         segments[index] = curLS;
14303                         lengths[index] = total;
14304                         d = 0;
14305                         curLS = [];
14306                     }
14307                 }
14308                 return {length:total, lengths:lengths, segments:segments};
14309             },
14310
14311
14312
14313             BezierPlugin = window._gsDefine.plugin({
14314                     propName: "bezier",
14315                     priority: -1,
14316                     version: "1.3.2",
14317                     API: 2,
14318                     global:true,
14319
14320                     //gets called when the tween renders for the first time. This is where initial values should be recorded and any setup routines should run.
14321                     init: function(target, vars, tween) {
14322                         this._target = target;
14323                         if (vars instanceof Array) {
14324                             vars = {values:vars};
14325                         }
14326                         this._func = {};
14327                         this._round = {};
14328                         this._props = [];
14329                         this._timeRes = (vars.timeResolution == null) ? 6 : parseInt(vars.timeResolution, 10);
14330                         var values = vars.values || [],
14331                             first = {},
14332                             second = values[0],
14333                             autoRotate = vars.autoRotate || tween.vars.orientToBezier,
14334                             p, isFunc, i, j, prepend;
14335
14336                         this._autoRotate = autoRotate ? (autoRotate instanceof Array) ? autoRotate : [["x","y","rotation",((autoRotate === true) ? 0 : Number(autoRotate) || 0)]] : null;
14337                         for (p in second) {
14338                             this._props.push(p);
14339                         }
14340
14341                         i = this._props.length;
14342                         while (--i > -1) {
14343                             p = this._props[i];
14344
14345                             this._overwriteProps.push(p);
14346                             isFunc = this._func[p] = (typeof(target[p]) === "function");
14347                             first[p] = (!isFunc) ? parseFloat(target[p]) : target[ ((p.indexOf("set") || typeof(target["get" + p.substr(3)]) !== "function") ? p : "get" + p.substr(3)) ]();
14348                             if (!prepend) if (first[p] !== values[0][p]) {
14349                                 prepend = first;
14350                             }
14351                         }
14352                         this._beziers = (vars.type !== "cubic" && vars.type !== "quadratic" && vars.type !== "soft") ? bezierThrough(values, isNaN(vars.curviness) ? 1 : vars.curviness, false, (vars.type === "thruBasic"), vars.correlate, prepend) : _parseBezierData(values, vars.type, first);
14353                         this._segCount = this._beziers[p].length;
14354
14355                         if (this._timeRes) {
14356                             var ld = _parseLengthData(this._beziers, this._timeRes);
14357                             this._length = ld.length;
14358                             this._lengths = ld.lengths;
14359                             this._segments = ld.segments;
14360                             this._l1 = this._li = this._s1 = this._si = 0;
14361                             this._l2 = this._lengths[0];
14362                             this._curSeg = this._segments[0];
14363                             this._s2 = this._curSeg[0];
14364                             this._prec = 1 / this._curSeg.length;
14365                         }
14366
14367                         if ((autoRotate = this._autoRotate)) {
14368                             this._initialRotations = [];
14369                             if (!(autoRotate[0] instanceof Array)) {
14370                                 this._autoRotate = autoRotate = [autoRotate];
14371                             }
14372                             i = autoRotate.length;
14373                             while (--i > -1) {
14374                                 for (j = 0; j < 3; j++) {
14375                                     p = autoRotate[i][j];
14376                                     this._func[p] = (typeof(target[p]) === "function") ? target[ ((p.indexOf("set") || typeof(target["get" + p.substr(3)]) !== "function") ? p : "get" + p.substr(3)) ] : false;
14377                                 }
14378                                 p = autoRotate[i][2];
14379                                 this._initialRotations[i] = this._func[p] ? this._func[p].call(this._target) : this._target[p];
14380                             }
14381                         }
14382                         this._startRatio = tween.vars.runBackwards ? 1 : 0; //we determine the starting ratio when the tween inits which is always 0 unless the tween has runBackwards:true (indicating it's a from() tween) in which case it's 1.
14383                         return true;
14384                     },
14385
14386                     //called each time the values should be updated, and the ratio gets passed as the only parameter (typically it's a value between 0 and 1, but it can exceed those when using an ease like Elastic.easeOut or Back.easeOut, etc.)
14387                     set: function(v) {
14388                         var segments = this._segCount,
14389                             func = this._func,
14390                             target = this._target,
14391                             notStart = (v !== this._startRatio),
14392                             curIndex, inv, i, p, b, t, val, l, lengths, curSeg;
14393                         if (!this._timeRes) {
14394                             curIndex = (v < 0) ? 0 : (v >= 1) ? segments - 1 : (segments * v) >> 0;
14395                             t = (v - (curIndex * (1 / segments))) * segments;
14396                         } else {
14397                             lengths = this._lengths;
14398                             curSeg = this._curSeg;
14399                             v *= this._length;
14400                             i = this._li;
14401                             //find the appropriate segment (if the currently cached one isn't correct)
14402                             if (v > this._l2 && i < segments - 1) {
14403                                 l = segments - 1;
14404                                 while (i < l && (this._l2 = lengths[++i]) <= v) {   }
14405                                 this._l1 = lengths[i-1];
14406                                 this._li = i;
14407                                 this._curSeg = curSeg = this._segments[i];
14408                                 this._s2 = curSeg[(this._s1 = this._si = 0)];
14409                             } else if (v < this._l1 && i > 0) {
14410                                 while (i > 0 && (this._l1 = lengths[--i]) >= v) { }
14411                                 if (i === 0 && v < this._l1) {
14412                                     this._l1 = 0;
14413                                 } else {
14414                                     i++;
14415                                 }
14416                                 this._l2 = lengths[i];
14417                                 this._li = i;
14418                                 this._curSeg = curSeg = this._segments[i];
14419                                 this._s1 = curSeg[(this._si = curSeg.length - 1) - 1] || 0;
14420                                 this._s2 = curSeg[this._si];
14421                             }
14422                             curIndex = i;
14423                             //now find the appropriate sub-segment (we split it into the number of pieces that was defined by "precision" and measured each one)
14424                             v -= this._l1;
14425                             i = this._si;
14426                             if (v > this._s2 && i < curSeg.length - 1) {
14427                                 l = curSeg.length - 1;
14428                                 while (i < l && (this._s2 = curSeg[++i]) <= v) {    }
14429                                 this._s1 = curSeg[i-1];
14430                                 this._si = i;
14431                             } else if (v < this._s1 && i > 0) {
14432                                 while (i > 0 && (this._s1 = curSeg[--i]) >= v) {    }
14433                                 if (i === 0 && v < this._s1) {
14434                                     this._s1 = 0;
14435                                 } else {
14436                                     i++;
14437                                 }
14438                                 this._s2 = curSeg[i];
14439                                 this._si = i;
14440                             }
14441                             t = (i + (v - this._s1) / (this._s2 - this._s1)) * this._prec;
14442                         }
14443                         inv = 1 - t;
14444
14445                         i = this._props.length;
14446                         while (--i > -1) {
14447                             p = this._props[i];
14448                             b = this._beziers[p][curIndex];
14449                             val = (t * t * b.da + 3 * inv * (t * b.ca + inv * b.ba)) * t + b.a;
14450                             if (this._round[p]) {
14451                                 val = Math.round(val);
14452                             }
14453                             if (func[p]) {
14454                                 target[p](val);
14455                             } else {
14456                                 target[p] = val;
14457                             }
14458                         }
14459
14460                         if (this._autoRotate) {
14461                             var ar = this._autoRotate,
14462                                 b2, x1, y1, x2, y2, add, conv;
14463                             i = ar.length;
14464                             while (--i > -1) {
14465                                 p = ar[i][2];
14466                                 add = ar[i][3] || 0;
14467                                 conv = (ar[i][4] === true) ? 1 : _RAD2DEG;
14468                                 b = this._beziers[ar[i][0]];
14469                                 b2 = this._beziers[ar[i][1]];
14470
14471                                 if (b && b2) { //in case one of the properties got overwritten.
14472                                     b = b[curIndex];
14473                                     b2 = b2[curIndex];
14474
14475                                     x1 = b.a + (b.b - b.a) * t;
14476                                     x2 = b.b + (b.c - b.b) * t;
14477                                     x1 += (x2 - x1) * t;
14478                                     x2 += ((b.c + (b.d - b.c) * t) - x2) * t;
14479
14480                                     y1 = b2.a + (b2.b - b2.a) * t;
14481                                     y2 = b2.b + (b2.c - b2.b) * t;
14482                                     y1 += (y2 - y1) * t;
14483                                     y2 += ((b2.c + (b2.d - b2.c) * t) - y2) * t;
14484
14485                                     val = notStart ? Math.atan2(y2 - y1, x2 - x1) * conv + add : this._initialRotations[i];
14486
14487                                     if (func[p]) {
14488                                         target[p](val);
14489                                     } else {
14490                                         target[p] = val;
14491                                     }
14492                                 }
14493                             }
14494                         }
14495                     }
14496             }),
14497             p = BezierPlugin.prototype;
14498
14499
14500         BezierPlugin.bezierThrough = bezierThrough;
14501         BezierPlugin.cubicToQuadratic = cubicToQuadratic;
14502         BezierPlugin._autoCSS = true; //indicates that this plugin can be inserted into the "css" object using the autoCSS feature of TweenLite
14503         BezierPlugin.quadraticToCubic = function(a, b, c) {
14504             return new Segment(a, (2 * b + a) / 3, (2 * b + c) / 3, c);
14505         };
14506
14507         BezierPlugin._cssRegister = function() {
14508             var CSSPlugin = window._gsDefine.globals.CSSPlugin;
14509             if (!CSSPlugin) {
14510                 return;
14511             }
14512             var _internals = CSSPlugin._internals,
14513                 _parseToProxy = _internals._parseToProxy,
14514                 _setPluginRatio = _internals._setPluginRatio,
14515                 CSSPropTween = _internals.CSSPropTween;
14516             _internals._registerComplexSpecialProp("bezier", {parser:function(t, e, prop, cssp, pt, plugin) {
14517                 if (e instanceof Array) {
14518                     e = {values:e};
14519                 }
14520                 plugin = new BezierPlugin();
14521                 var values = e.values,
14522                     l = values.length - 1,
14523                     pluginValues = [],
14524                     v = {},
14525                     i, p, data;
14526                 if (l < 0) {
14527                     return pt;
14528                 }
14529                 for (i = 0; i <= l; i++) {
14530                     data = _parseToProxy(t, values[i], cssp, pt, plugin, (l !== i));
14531                     pluginValues[i] = data.end;
14532                 }
14533                 for (p in e) {
14534                     v[p] = e[p]; //duplicate the vars object because we need to alter some things which would cause problems if the user plans to reuse the same vars object for another tween.
14535                 }
14536                 v.values = pluginValues;
14537                 pt = new CSSPropTween(t, "bezier", 0, 0, data.pt, 2);
14538                 pt.data = data;
14539                 pt.plugin = plugin;
14540                 pt.setRatio = _setPluginRatio;
14541                 if (v.autoRotate === 0) {
14542                     v.autoRotate = true;
14543                 }
14544                 if (v.autoRotate && !(v.autoRotate instanceof Array)) {
14545                     i = (v.autoRotate === true) ? 0 : Number(v.autoRotate);
14546                     v.autoRotate = (data.end.left != null) ? [["left","top","rotation",i,false]] : (data.end.x != null) ? [["x","y","rotation",i,false]] : false;
14547                 }
14548                 if (v.autoRotate) {
14549                     if (!cssp._transform) {
14550                         cssp._enableTransforms(false);
14551                     }
14552                     data.autoRotate = cssp._target._gsTransform;
14553                 }
14554                 plugin._onInitTween(data.proxy, v, cssp._tween);
14555                 return pt;
14556             }});
14557         };
14558
14559         p._roundProps = function(lookup, value) {
14560             var op = this._overwriteProps,
14561                 i = op.length;
14562             while (--i > -1) {
14563                 if (lookup[op[i]] || lookup.bezier || lookup.bezierThrough) {
14564                     this._round[op[i]] = value;
14565                 }
14566             }
14567         };
14568
14569         p._kill = function(lookup) {
14570             var a = this._props,
14571                 p, i;
14572             for (p in this._beziers) {
14573                 if (p in lookup) {
14574                     delete this._beziers[p];
14575                     delete this._func[p];
14576                     i = a.length;
14577                     while (--i > -1) {
14578                         if (a[i] === p) {
14579                             a.splice(i, 1);
14580                         }
14581                     }
14582                 }
14583             }
14584             return this._super._kill.call(this, lookup);
14585         };
14586
14587     }());
14588
14589
14590
14591
14592
14593
14594     
14595     
14596     
14597     
14598     
14599     
14600     
14601     
14602 /*
14603  * ----------------------------------------------------------------
14604  * CSSPlugin
14605  * ----------------------------------------------------------------
14606  */
14607     window._gsDefine("plugins.CSSPlugin", ["plugins.TweenPlugin","TweenLite"], function(TweenPlugin, TweenLite) {
14608
14609         /** @constructor **/
14610         var CSSPlugin = function() {
14611                 TweenPlugin.call(this, "css");
14612                 this._overwriteProps.length = 0;
14613                 this.setRatio = CSSPlugin.prototype.setRatio; //speed optimization (avoid prototype lookup on this "hot" method)
14614             },
14615             _hasPriority, //turns true whenever a CSSPropTween instance is created that has a priority other than 0. This helps us discern whether or not we should spend the time organizing the linked list or not after a CSSPlugin's _onInitTween() method is called.
14616             _suffixMap, //we set this in _onInitTween() each time as a way to have a persistent variable we can use in other methods like _parse() without having to pass it around as a parameter and we keep _parse() decoupled from a particular CSSPlugin instance
14617             _cs, //computed style (we store this in a shared variable to conserve memory and make minification tighter
14618             _overwriteProps, //alias to the currently instantiating CSSPlugin's _overwriteProps array. We use this closure in order to avoid having to pass a reference around from method to method and aid in minification.
14619             _specialProps = {},
14620             p = CSSPlugin.prototype = new TweenPlugin("css");
14621
14622         p.constructor = CSSPlugin;
14623         CSSPlugin.version = "1.12.1";
14624         CSSPlugin.API = 2;
14625         CSSPlugin.defaultTransformPerspective = 0;
14626         CSSPlugin.defaultSkewType = "compensated";
14627         p = "px"; //we'll reuse the "p" variable to keep file size down
14628         CSSPlugin.suffixMap = {top:p, right:p, bottom:p, left:p, width:p, height:p, fontSize:p, padding:p, margin:p, perspective:p, lineHeight:""};
14629
14630
14631         var _numExp = /(?:\d|\-\d|\.\d|\-\.\d)+/g,
14632             _relNumExp = /(?:\d|\-\d|\.\d|\-\.\d|\+=\d|\-=\d|\+=.\d|\-=\.\d)+/g,
14633             _valuesExp = /(?:\+=|\-=|\-|\b)[\d\-\.]+[a-zA-Z0-9]*(?:%|\b)/gi, //finds all the values that begin with numbers or += or -= and then a number. Includes suffixes. We use this to split complex values apart like "1px 5px 20px rgb(255,102,51)"
14634             _NaNExp = /[^\d\-\.]/g,
14635             _suffixExp = /(?:\d|\-|\+|=|#|\.)*/g,
14636             _opacityExp = /opacity *= *([^)]*)/i,
14637             _opacityValExp = /opacity:([^;]*)/i,
14638             _alphaFilterExp = /alpha\(opacity *=.+?\)/i,
14639             _rgbhslExp = /^(rgb|hsl)/,
14640             _capsExp = /([A-Z])/g,
14641             _camelExp = /-([a-z])/gi,
14642             _urlExp = /(^(?:url\(\"|url\())|(?:(\"\))$|\)$)/gi, //for pulling out urls from url(...) or url("...") strings (some browsers wrap urls in quotes, some don't when reporting things like backgroundImage)
14643             _camelFunc = function(s, g) { return g.toUpperCase(); },
14644             _horizExp = /(?:Left|Right|Width)/i,
14645             _ieGetMatrixExp = /(M11|M12|M21|M22)=[\d\-\.e]+/gi,
14646             _ieSetMatrixExp = /progid\:DXImageTransform\.Microsoft\.Matrix\(.+?\)/i,
14647             _commasOutsideParenExp = /,(?=[^\)]*(?:\(|$))/gi, //finds any commas that are not within parenthesis
14648             _DEG2RAD = Math.PI / 180,
14649             _RAD2DEG = 180 / Math.PI,
14650             _forcePT = {},
14651             _doc = document,
14652             _tempDiv = _doc.createElement("div"),
14653             _tempImg = _doc.createElement("img"),
14654             _internals = CSSPlugin._internals = {_specialProps:_specialProps}, //provides a hook to a few internal methods that we need to access from inside other plugins
14655             _agent = navigator.userAgent,
14656             _autoRound,
14657             _reqSafariFix, //we won't apply the Safari transform fix until we actually come across a tween that affects a transform property (to maintain best performance).
14658
14659             _isSafari,
14660             _isFirefox, //Firefox has a bug that causes 3D transformed elements to randomly disappear unless a repaint is forced after each update on each element.
14661             _isSafariLT6, //Safari (and Android 4 which uses a flavor of Safari) has a bug that prevents changes to "top" and "left" properties from rendering properly if changed on the same frame as a transform UNLESS we set the element's WebkitBackfaceVisibility to hidden (weird, I know). Doing this for Android 3 and earlier seems to actually cause other problems, though (fun!)
14662             _ieVers,
14663             _supportsOpacity = (function() { //we set _isSafari, _ieVers, _isFirefox, and _supportsOpacity all in one function here to reduce file size slightly, especially in the minified version.
14664                 var i = _agent.indexOf("Android"),
14665                     d = _doc.createElement("div"), a;
14666
14667                 _isSafari = (_agent.indexOf("Safari") !== -1 && _agent.indexOf("Chrome") === -1 && (i === -1 || Number(_agent.substr(i+8, 1)) > 3));
14668                 _isSafariLT6 = (_isSafari && (Number(_agent.substr(_agent.indexOf("Version/")+8, 1)) < 6));
14669                 _isFirefox = (_agent.indexOf("Firefox") !== -1);
14670
14671                 if ((/MSIE ([0-9]{1,}[\.0-9]{0,})/).exec(_agent)) {
14672                     _ieVers = parseFloat( RegExp.$1 );
14673                 }
14674
14675                 d.innerHTML = "<a title='' style='top:1px;opacity:.55;'>a</a>";
14676                 a = d.getElementsByTagName("a")[0];
14677                 return a ? /^0.55/.test(a.style.opacity) : false;
14678             }()),
14679             _getIEOpacity = function(v) {
14680                 return (_opacityExp.test( ((typeof(v) === "string") ? v : (v.currentStyle ? v.currentStyle.filter : v.style.filter) || "") ) ? ( parseFloat( RegExp.$1 ) / 100 ) : 1);
14681             },
14682             _log = function(s) {//for logging messages, but in a way that won't throw errors in old versions of IE.
14683                 if (window.console) {
14684                     //console.log(s);
14685                 }
14686             },
14687             _prefixCSS = "", //the non-camelCase vendor prefix like "-o-", "-moz-", "-ms-", or "-webkit-"
14688             _prefix = "", //camelCase vendor prefix like "O", "ms", "Webkit", or "Moz".
14689
14690             // @private feed in a camelCase property name like "transform" and it will check to see if it is valid as-is or if it needs a vendor prefix. It returns the corrected camelCase property name (i.e. "WebkitTransform" or "MozTransform" or "transform" or null if no such property is found, like if the browser is IE8 or before, "transform" won't be found at all)
14691             _checkPropPrefix = function(p, e) {
14692                 e = e || _tempDiv;
14693                 var s = e.style,
14694                     a, i;
14695                 if (s[p] !== undefined) {
14696                     return p;
14697                 }
14698                 p = p.charAt(0).toUpperCase() + p.substr(1);
14699                 a = ["O","Moz","ms","Ms","Webkit"];
14700                 i = 5;
14701                 while (--i > -1 && s[a[i]+p] === undefined) { }
14702                 if (i >= 0) {
14703                     _prefix = (i === 3) ? "ms" : a[i];
14704                     _prefixCSS = "-" + _prefix.toLowerCase() + "-";
14705                     return _prefix + p;
14706                 }
14707                 return null;
14708             },
14709
14710             _getComputedStyle = _doc.defaultView ? _doc.defaultView.getComputedStyle : function() {},
14711
14712             /**
14713              * @private Returns the css style for a particular property of an element. For example, to get whatever the current "left" css value for an element with an ID of "myElement", you could do:
14714              * var currentLeft = CSSPlugin.getStyle( document.getElementById("myElement"), "left");
14715              *
14716              * @param {!Object} t Target element whose style property you want to query
14717              * @param {!string} p Property name (like "left" or "top" or "marginTop", etc.)
14718              * @param {Object=} cs Computed style object. This just provides a way to speed processing if you're going to get several properties on the same element in quick succession - you can reuse the result of the getComputedStyle() call.
14719              * @param {boolean=} calc If true, the value will not be read directly from the element's "style" property (if it exists there), but instead the getComputedStyle() result will be used. This can be useful when you want to ensure that the browser itself is interpreting the value.
14720              * @param {string=} dflt Default value that should be returned in the place of null, "none", "auto" or "auto auto".
14721              * @return {?string} The current property value
14722              */
14723             _getStyle = CSSPlugin.getStyle = function(t, p, cs, calc, dflt) {
14724                 var rv;
14725                 if (!_supportsOpacity) if (p === "opacity") { //several versions of IE don't use the standard "opacity" property - they use things like filter:alpha(opacity=50), so we parse that here.
14726                     return _getIEOpacity(t);
14727                 }
14728                 if (!calc && t.style[p]) {
14729                     rv = t.style[p];
14730                 } else if ((cs = cs || _getComputedStyle(t))) {
14731                     rv = cs[p] || cs.getPropertyValue(p) || cs.getPropertyValue(p.replace(_capsExp, "-$1").toLowerCase());
14732                 } else if (t.currentStyle) {
14733                     rv = t.currentStyle[p];
14734                 }
14735                 return (dflt != null && (!rv || rv === "none" || rv === "auto" || rv === "auto auto")) ? dflt : rv;
14736             },
14737
14738             /**
14739              * @private Pass the target element, the property name, the numeric value, and the suffix (like "%", "em", "px", etc.) and it will spit back the equivalent pixel number.
14740              * @param {!Object} t Target element
14741              * @param {!string} p Property name (like "left", "top", "marginLeft", etc.)
14742              * @param {!number} v Value
14743              * @param {string=} sfx Suffix (like "px" or "%" or "em")
14744              * @param {boolean=} recurse If true, the call is a recursive one. In some browsers (like IE7/8), occasionally the value isn't accurately reported initially, but if we run the function again it will take effect.
14745              * @return {number} value in pixels
14746              */
14747             _convertToPixels = _internals.convertToPixels = function(t, p, v, sfx, recurse) {
14748                 if (sfx === "px" || !sfx) { return v; }
14749                 if (sfx === "auto" || !v) { return 0; }
14750                 var horiz = _horizExp.test(p),
14751                     node = t,
14752                     style = _tempDiv.style,
14753                     neg = (v < 0),
14754                     pix, cache, time;
14755                 if (neg) {
14756                     v = -v;
14757                 }
14758                 if (sfx === "%" && p.indexOf("border") !== -1) {
14759                     pix = (v / 100) * (horiz ? t.clientWidth : t.clientHeight);
14760                 } else {
14761                     style.cssText = "border:0 solid red;position:" + _getStyle(t, "position") + ";line-height:0;";
14762                     if (sfx === "%" || !node.appendChild) {
14763                         node = t.parentNode || _doc.body;
14764                         cache = node._gsCache;
14765                         time = TweenLite.ticker.frame;
14766                         if (cache && horiz && cache.time === time) { //performance optimization: we record the width of elements along with the ticker frame so that we can quickly get it again on the same tick (seems relatively safe to assume it wouldn't change on the same tick)
14767                             return cache.width * v / 100;
14768                         }
14769                         style[(horiz ? "width" : "height")] = v + sfx;
14770                     } else {
14771                         style[(horiz ? "borderLeftWidth" : "borderTopWidth")] = v + sfx;
14772                     }
14773                     node.appendChild(_tempDiv);
14774                     pix = parseFloat(_tempDiv[(horiz ? "offsetWidth" : "offsetHeight")]);
14775                     node.removeChild(_tempDiv);
14776                     if (horiz && sfx === "%" && CSSPlugin.cacheWidths !== false) {
14777                         cache = node._gsCache = node._gsCache || {};
14778                         cache.time = time;
14779                         cache.width = pix / v * 100;
14780                     }
14781                     if (pix === 0 && !recurse) {
14782                         pix = _convertToPixels(t, p, v, sfx, true);
14783                     }
14784                 }
14785                 return neg ? -pix : pix;
14786             },
14787             _calculateOffset = _internals.calculateOffset = function(t, p, cs) { //for figuring out "top" or "left" in px when it's "auto". We need to factor in margin with the offsetLeft/offsetTop
14788                 if (_getStyle(t, "position", cs) !== "absolute") { return 0; }
14789                 var dim = ((p === "left") ? "Left" : "Top"),
14790                     v = _getStyle(t, "margin" + dim, cs);
14791                 return t["offset" + dim] - (_convertToPixels(t, p, parseFloat(v), v.replace(_suffixExp, "")) || 0);
14792             },
14793
14794             // @private returns at object containing ALL of the style properties in camelCase and their associated values.
14795             _getAllStyles = function(t, cs) {
14796                 var s = {},
14797                     i, tr;
14798                 if ((cs = cs || _getComputedStyle(t, null))) {
14799                     if ((i = cs.length)) {
14800                         while (--i > -1) {
14801                             s[cs[i].replace(_camelExp, _camelFunc)] = cs.getPropertyValue(cs[i]);
14802                         }
14803                     } else { //Opera behaves differently - cs.length is always 0, so we must do a for...in loop.
14804                         for (i in cs) {
14805                             s[i] = cs[i];
14806                         }
14807                     }
14808                 } else if ((cs = t.currentStyle || t.style)) {
14809                     for (i in cs) {
14810                         if (typeof(i) === "string" && s[i] === undefined) {
14811                             s[i.replace(_camelExp, _camelFunc)] = cs[i];
14812                         }
14813                     }
14814                 }
14815                 if (!_supportsOpacity) {
14816                     s.opacity = _getIEOpacity(t);
14817                 }
14818                 tr = _getTransform(t, cs, false);
14819                 s.rotation = tr.rotation;
14820                 s.skewX = tr.skewX;
14821                 s.scaleX = tr.scaleX;
14822                 s.scaleY = tr.scaleY;
14823                 s.x = tr.x;
14824                 s.y = tr.y;
14825                 if (_supports3D) {
14826                     s.z = tr.z;
14827                     s.rotationX = tr.rotationX;
14828                     s.rotationY = tr.rotationY;
14829                     s.scaleZ = tr.scaleZ;
14830                 }
14831                 if (s.filters) {
14832                     delete s.filters;
14833                 }
14834                 return s;
14835             },
14836
14837             // @private analyzes two style objects (as returned by _getAllStyles()) and only looks for differences between them that contain tweenable values (like a number or color). It returns an object with a "difs" property which refers to an object containing only those isolated properties and values for tweening, and a "firstMPT" property which refers to the first MiniPropTween instance in a linked list that recorded all the starting values of the different properties so that we can revert to them at the end or beginning of the tween - we don't want the cascading to get messed up. The forceLookup parameter is an optional generic object with properties that should be forced into the results - this is necessary for className tweens that are overwriting others because imagine a scenario where a rollover/rollout adds/removes a class and the user swipes the mouse over the target SUPER fast, thus nothing actually changed yet and the subsequent comparison of the properties would indicate they match (especially when px rounding is taken into consideration), thus no tweening is necessary even though it SHOULD tween and remove those properties after the tween (otherwise the inline styles will contaminate things). See the className SpecialProp code for details.
14838             _cssDif = function(t, s1, s2, vars, forceLookup) {
14839                 var difs = {},
14840                     style = t.style,
14841                     val, p, mpt;
14842                 for (p in s2) {
14843                     if (p !== "cssText") if (p !== "length") if (isNaN(p)) if (s1[p] !== (val = s2[p]) || (forceLookup && forceLookup[p])) if (p.indexOf("Origin") === -1) if (typeof(val) === "number" || typeof(val) === "string") {
14844                         difs[p] = (val === "auto" && (p === "left" || p === "top")) ? _calculateOffset(t, p) : ((val === "" || val === "auto" || val === "none") && typeof(s1[p]) === "string" && s1[p].replace(_NaNExp, "") !== "") ? 0 : val; //if the ending value is defaulting ("" or "auto"), we check the starting value and if it can be parsed into a number (a string which could have a suffix too, like 700px), then we swap in 0 for "" or "auto" so that things actually tween.
14845                         if (style[p] !== undefined) { //for className tweens, we must remember which properties already existed inline - the ones that didn't should be removed when the tween isn't in progress because they were only introduced to facilitate the transition between classes.
14846                             mpt = new MiniPropTween(style, p, style[p], mpt);
14847                         }
14848                     }
14849                 }
14850                 if (vars) {
14851                     for (p in vars) { //copy properties (except className)
14852                         if (p !== "className") {
14853                             difs[p] = vars[p];
14854                         }
14855                     }
14856                 }
14857                 return {difs:difs, firstMPT:mpt};
14858             },
14859             _dimensions = {width:["Left","Right"], height:["Top","Bottom"]},
14860             _margins = ["marginLeft","marginRight","marginTop","marginBottom"],
14861
14862             /**
14863              * @private Gets the width or height of an element
14864              * @param {!Object} t Target element
14865              * @param {!string} p Property name ("width" or "height")
14866              * @param {Object=} cs Computed style object (if one exists). Just a speed optimization.
14867              * @return {number} Dimension (in pixels)
14868              */
14869             _getDimension = function(t, p, cs) {
14870                 var v = parseFloat((p === "width") ? t.offsetWidth : t.offsetHeight),
14871                     a = _dimensions[p],
14872                     i = a.length;
14873                 cs = cs || _getComputedStyle(t, null);
14874                 while (--i > -1) {
14875                     v -= parseFloat( _getStyle(t, "padding" + a[i], cs, true) ) || 0;
14876                     v -= parseFloat( _getStyle(t, "border" + a[i] + "Width", cs, true) ) || 0;
14877                 }
14878                 return v;
14879             },
14880
14881             // @private Parses position-related complex strings like "top left" or "50px 10px" or "70% 20%", etc. which are used for things like transformOrigin or backgroundPosition. Optionally decorates a supplied object (recObj) with the following properties: "ox" (offsetX), "oy" (offsetY), "oxp" (if true, "ox" is a percentage not a pixel value), and "oxy" (if true, "oy" is a percentage not a pixel value)
14882             _parsePosition = function(v, recObj) {
14883                 if (v == null || v === "" || v === "auto" || v === "auto auto") { //note: Firefox uses "auto auto" as default whereas Chrome uses "auto".
14884                     v = "0 0";
14885                 }
14886                 var a = v.split(" "),
14887                     x = (v.indexOf("left") !== -1) ? "0%" : (v.indexOf("right") !== -1) ? "100%" : a[0],
14888                     y = (v.indexOf("top") !== -1) ? "0%" : (v.indexOf("bottom") !== -1) ? "100%" : a[1];
14889                 if (y == null) {
14890                     y = "0";
14891                 } else if (y === "center") {
14892                     y = "50%";
14893                 }
14894                 if (x === "center" || (isNaN(parseFloat(x)) && (x + "").indexOf("=") === -1)) { //remember, the user could flip-flop the values and say "bottom center" or "center bottom", etc. "center" is ambiguous because it could be used to describe horizontal or vertical, hence the isNaN(). If there's an "=" sign in the value, it's relative.
14895                     x = "50%";
14896                 }
14897                 if (recObj) {
14898                     recObj.oxp = (x.indexOf("%") !== -1);
14899                     recObj.oyp = (y.indexOf("%") !== -1);
14900                     recObj.oxr = (x.charAt(1) === "=");
14901                     recObj.oyr = (y.charAt(1) === "=");
14902                     recObj.ox = parseFloat(x.replace(_NaNExp, ""));
14903                     recObj.oy = parseFloat(y.replace(_NaNExp, ""));
14904                 }
14905                 return x + " " + y + ((a.length > 2) ? " " + a[2] : "");
14906             },
14907
14908             /**
14909              * @private Takes an ending value (typically a string, but can be a number) and a starting value and returns the change between the two, looking for relative value indicators like += and -= and it also ignores suffixes (but make sure the ending value starts with a number or +=/-= and that the starting value is a NUMBER!)
14910              * @param {(number|string)} e End value which is typically a string, but could be a number
14911              * @param {(number|string)} b Beginning value which is typically a string but could be a number
14912              * @return {number} Amount of change between the beginning and ending values (relative values that have a "+=" or "-=" are recognized)
14913              */
14914             _parseChange = function(e, b) {
14915                 return (typeof(e) === "string" && e.charAt(1) === "=") ? parseInt(e.charAt(0) + "1", 10) * parseFloat(e.substr(2)) : parseFloat(e) - parseFloat(b);
14916             },
14917
14918             /**
14919              * @private Takes a value and a default number, checks if the value is relative, null, or numeric and spits back a normalized number accordingly. Primarily used in the _parseTransform() function.
14920              * @param {Object} v Value to be parsed
14921              * @param {!number} d Default value (which is also used for relative calculations if "+=" or "-=" is found in the first parameter)
14922              * @return {number} Parsed value
14923              */
14924             _parseVal = function(v, d) {
14925                 return (v == null) ? d : (typeof(v) === "string" && v.charAt(1) === "=") ? parseInt(v.charAt(0) + "1", 10) * Number(v.substr(2)) + d : parseFloat(v);
14926             },
14927
14928             /**
14929              * @private Translates strings like "40deg" or "40" or 40rad" or "+=40deg" or "270_short" or "-90_cw" or "+=45_ccw" to a numeric radian angle. Of course a starting/default value must be fed in too so that relative values can be calculated properly.
14930              * @param {Object} v Value to be parsed
14931              * @param {!number} d Default value (which is also used for relative calculations if "+=" or "-=" is found in the first parameter)
14932              * @param {string=} p property name for directionalEnd (optional - only used when the parsed value is directional ("_short", "_cw", or "_ccw" suffix). We need a way to store the uncompensated value so that at the end of the tween, we set it to exactly what was requested with no directional compensation). Property name would be "rotation", "rotationX", or "rotationY"
14933              * @param {Object=} directionalEnd An object that will store the raw end values for directional angles ("_short", "_cw", or "_ccw" suffix). We need a way to store the uncompensated value so that at the end of the tween, we set it to exactly what was requested with no directional compensation.
14934              * @return {number} parsed angle in radians
14935              */
14936             _parseAngle = function(v, d, p, directionalEnd) {
14937                 var min = 0.000001,
14938                     cap, split, dif, result;
14939                 if (v == null) {
14940                     result = d;
14941                 } else if (typeof(v) === "number") {
14942                     result = v;
14943                 } else {
14944                     cap = 360;
14945                     split = v.split("_");
14946                     dif = Number(split[0].replace(_NaNExp, "")) * ((v.indexOf("rad") === -1) ? 1 : _RAD2DEG) - ((v.charAt(1) === "=") ? 0 : d);
14947                     if (split.length) {
14948                         if (directionalEnd) {
14949                             directionalEnd[p] = d + dif;
14950                         }
14951                         if (v.indexOf("short") !== -1) {
14952                             dif = dif % cap;
14953                             if (dif !== dif % (cap / 2)) {
14954                                 dif = (dif < 0) ? dif + cap : dif - cap;
14955                             }
14956                         }
14957                         if (v.indexOf("_cw") !== -1 && dif < 0) {
14958                             dif = ((dif + cap * 9999999999) % cap) - ((dif / cap) | 0) * cap;
14959                         } else if (v.indexOf("ccw") !== -1 && dif > 0) {
14960                             dif = ((dif - cap * 9999999999) % cap) - ((dif / cap) | 0) * cap;
14961                         }
14962                     }
14963                     result = d + dif;
14964                 }
14965                 if (result < min && result > -min) {
14966                     result = 0;
14967                 }
14968                 return result;
14969             },
14970
14971             _colorLookup = {aqua:[0,255,255],
14972                 lime:[0,255,0],
14973                 silver:[192,192,192],
14974                 black:[0,0,0],
14975                 maroon:[128,0,0],
14976                 teal:[0,128,128],
14977                 blue:[0,0,255],
14978                 navy:[0,0,128],
14979                 white:[255,255,255],
14980                 fuchsia:[255,0,255],
14981                 olive:[128,128,0],
14982                 yellow:[255,255,0],
14983                 orange:[255,165,0],
14984                 gray:[128,128,128],
14985                 purple:[128,0,128],
14986                 green:[0,128,0],
14987                 red:[255,0,0],
14988                 pink:[255,192,203],
14989                 cyan:[0,255,255],
14990                 transparent:[255,255,255,0]},
14991
14992             _hue = function(h, m1, m2) {
14993                 h = (h < 0) ? h + 1 : (h > 1) ? h - 1 : h;
14994                 return ((((h * 6 < 1) ? m1 + (m2 - m1) * h * 6 : (h < 0.5) ? m2 : (h * 3 < 2) ? m1 + (m2 - m1) * (2 / 3 - h) * 6 : m1) * 255) + 0.5) | 0;
14995             },
14996
14997             /**
14998              * @private Parses a color (like #9F0, #FF9900, or rgb(255,51,153)) into an array with 3 elements for red, green, and blue. Also handles rgba() values (splits into array of 4 elements of course)
14999              * @param {(string|number)} v The value the should be parsed which could be a string like #9F0 or rgb(255,102,51) or rgba(255,0,0,0.5) or it could be a number like 0xFF00CC or even a named color like red, blue, purple, etc.
15000              * @return {Array.<number>} An array containing red, green, and blue (and optionally alpha) in that order.
15001              */
15002             _parseColor = function(v) {
15003                 var c1, c2, c3, h, s, l;
15004                 if (!v || v === "") {
15005                     return _colorLookup.black;
15006                 }
15007                 if (typeof(v) === "number") {
15008                     return [v >> 16, (v >> 8) & 255, v & 255];
15009                 }
15010                 if (v.charAt(v.length - 1) === ",") { //sometimes a trailing commma is included and we should chop it off (typically from a comma-delimited list of values like a textShadow:"2px 2px 2px blue, 5px 5px 5px rgb(255,0,0)" - in this example "blue," has a trailing comma. We could strip it out inside parseComplex() but we'd need to do it to the beginning and ending values plus it wouldn't provide protection from other potential scenarios like if the user passes in a similar value.
15011                     v = v.substr(0, v.length - 1);
15012                 }
15013                 if (_colorLookup[v]) {
15014                     return _colorLookup[v];
15015                 }
15016                 if (v.charAt(0) === "#") {
15017                     if (v.length === 4) { //for shorthand like #9F0
15018                         c1 = v.charAt(1),
15019                         c2 = v.charAt(2),
15020                         c3 = v.charAt(3);
15021                         v = "#" + c1 + c1 + c2 + c2 + c3 + c3;
15022                     }
15023                     v = parseInt(v.substr(1), 16);
15024                     return [v >> 16, (v >> 8) & 255, v & 255];
15025                 }
15026                 if (v.substr(0, 3) === "hsl") {
15027                     v = v.match(_numExp);
15028                     h = (Number(v[0]) % 360) / 360;
15029                     s = Number(v[1]) / 100;
15030                     l = Number(v[2]) / 100;
15031                     c2 = (l <= 0.5) ? l * (s + 1) : l + s - l * s;
15032                     c1 = l * 2 - c2;
15033                     if (v.length > 3) {
15034                         v[3] = Number(v[3]);
15035                     }
15036                     v[0] = _hue(h + 1 / 3, c1, c2);
15037                     v[1] = _hue(h, c1, c2);
15038                     v[2] = _hue(h - 1 / 3, c1, c2);
15039                     return v;
15040                 }
15041                 v = v.match(_numExp) || _colorLookup.transparent;
15042                 v[0] = Number(v[0]);
15043                 v[1] = Number(v[1]);
15044                 v[2] = Number(v[2]);
15045                 if (v.length > 3) {
15046                     v[3] = Number(v[3]);
15047                 }
15048                 return v;
15049             },
15050             _colorExp = "(?:\\b(?:(?:rgb|rgba|hsl|hsla)\\(.+?\\))|\\B#.+?\\b"; //we'll dynamically build this Regular Expression to conserve file size. After building it, it will be able to find rgb(), rgba(), # (hexadecimal), and named color values like red, blue, purple, etc.
15051
15052         for (p in _colorLookup) {
15053             _colorExp += "|" + p + "\\b";
15054         }
15055         _colorExp = new RegExp(_colorExp+")", "gi");
15056
15057         /**
15058          * @private Returns a formatter function that handles taking a string (or number in some cases) and returning a consistently formatted one in terms of delimiters, quantity of values, etc. For example, we may get boxShadow values defined as "0px red" or "0px 0px 10px rgb(255,0,0)" or "0px 0px 20px 20px #F00" and we need to ensure that what we get back is described with 4 numbers and a color. This allows us to feed it into the _parseComplex() method and split the values up appropriately. The neat thing about this _getFormatter() function is that the dflt defines a pattern as well as a default, so for example, _getFormatter("0px 0px 0px 0px #777", true) not only sets the default as 0px for all distances and #777 for the color, but also sets the pattern such that 4 numbers and a color will always get returned.
15059          * @param {!string} dflt The default value and pattern to follow. So "0px 0px 0px 0px #777" will ensure that 4 numbers and a color will always get returned.
15060          * @param {boolean=} clr If true, the values should be searched for color-related data. For example, boxShadow values typically contain a color whereas borderRadius don't.
15061          * @param {boolean=} collapsible If true, the value is a top/left/right/bottom style one that acts like margin or padding, where if only one value is received, it's used for all 4; if 2 are received, the first is duplicated for 3rd (bottom) and the 2nd is duplicated for the 4th spot (left), etc.
15062          * @return {Function} formatter function
15063          */
15064         var _getFormatter = function(dflt, clr, collapsible, multi) {
15065                 if (dflt == null) {
15066                     return function(v) {return v;};
15067                 }
15068                 var dColor = clr ? (dflt.match(_colorExp) || [""])[0] : "",
15069                     dVals = dflt.split(dColor).join("").match(_valuesExp) || [],
15070                     pfx = dflt.substr(0, dflt.indexOf(dVals[0])),
15071                     sfx = (dflt.charAt(dflt.length - 1) === ")") ? ")" : "",
15072                     delim = (dflt.indexOf(" ") !== -1) ? " " : ",",
15073                     numVals = dVals.length,
15074                     dSfx = (numVals > 0) ? dVals[0].replace(_numExp, "") : "",
15075                     formatter;
15076                 if (!numVals) {
15077                     return function(v) {return v;};
15078                 }
15079                 if (clr) {
15080                     formatter = function(v) {
15081                         var color, vals, i, a;
15082                         if (typeof(v) === "number") {
15083                             v += dSfx;
15084                         } else if (multi && _commasOutsideParenExp.test(v)) {
15085                             a = v.replace(_commasOutsideParenExp, "|").split("|");
15086                             for (i = 0; i < a.length; i++) {
15087                                 a[i] = formatter(a[i]);
15088                             }
15089                             return a.join(",");
15090                         }
15091                         color = (v.match(_colorExp) || [dColor])[0];
15092                         vals = v.split(color).join("").match(_valuesExp) || [];
15093                         i = vals.length;
15094                         if (numVals > i--) {
15095                             while (++i < numVals) {
15096                                 vals[i] = collapsible ? vals[(((i - 1) / 2) | 0)] : dVals[i];
15097                             }
15098                         }
15099                         return pfx + vals.join(delim) + delim + color + sfx + (v.indexOf("inset") !== -1 ? " inset" : "");
15100                     };
15101                     return formatter;
15102
15103                 }
15104                 formatter = function(v) {
15105                     var vals, a, i;
15106                     if (typeof(v) === "number") {
15107                         v += dSfx;
15108                     } else if (multi && _commasOutsideParenExp.test(v)) {
15109                         a = v.replace(_commasOutsideParenExp, "|").split("|");
15110                         for (i = 0; i < a.length; i++) {
15111                             a[i] = formatter(a[i]);
15112                         }
15113                         return a.join(",");
15114                     }
15115                     vals = v.match(_valuesExp) || [];
15116                     i = vals.length;
15117                     if (numVals > i--) {
15118                         while (++i < numVals) {
15119                             vals[i] = collapsible ? vals[(((i - 1) / 2) | 0)] : dVals[i];
15120                         }
15121                     }
15122                     return pfx + vals.join(delim) + sfx;
15123                 };
15124                 return formatter;
15125             },
15126
15127             /**
15128              * @private returns a formatter function that's used for edge-related values like marginTop, marginLeft, paddingBottom, paddingRight, etc. Just pass a comma-delimited list of property names related to the edges.
15129              * @param {!string} props a comma-delimited list of property names in order from top to left, like "marginTop,marginRight,marginBottom,marginLeft"
15130              * @return {Function} a formatter function
15131              */
15132             _getEdgeParser = function(props) {
15133                 props = props.split(",");
15134                 return function(t, e, p, cssp, pt, plugin, vars) {
15135                     var a = (e + "").split(" "),
15136                         i;
15137                     vars = {};
15138                     for (i = 0; i < 4; i++) {
15139                         vars[props[i]] = a[i] = a[i] || a[(((i - 1) / 2) >> 0)];
15140                     }
15141                     return cssp.parse(t, vars, pt, plugin);
15142                 };
15143             },
15144
15145             // @private used when other plugins must tween values first, like BezierPlugin or ThrowPropsPlugin, etc. That plugin's setRatio() gets called first so that the values are updated, and then we loop through the MiniPropTweens  which handle copying the values into their appropriate slots so that they can then be applied correctly in the main CSSPlugin setRatio() method. Remember, we typically create a proxy object that has a bunch of uniquely-named properties that we feed to the sub-plugin and it does its magic normally, and then we must interpret those values and apply them to the css because often numbers must get combined/concatenated, suffixes added, etc. to work with css, like boxShadow could have 4 values plus a color.
15146             _setPluginRatio = _internals._setPluginRatio = function(v) {
15147                 this.plugin.setRatio(v);
15148                 var d = this.data,
15149                     proxy = d.proxy,
15150                     mpt = d.firstMPT,
15151                     min = 0.000001,
15152                     val, pt, i, str;
15153                 while (mpt) {
15154                     val = proxy[mpt.v];
15155                     if (mpt.r) {
15156                         val = Math.round(val);
15157                     } else if (val < min && val > -min) {
15158                         val = 0;
15159                     }
15160                     mpt.t[mpt.p] = val;
15161                     mpt = mpt._next;
15162                 }
15163                 if (d.autoRotate) {
15164                     d.autoRotate.rotation = proxy.rotation;
15165                 }
15166                 //at the end, we must set the CSSPropTween's "e" (end) value dynamically here because that's what is used in the final setRatio() method.
15167                 if (v === 1) {
15168                     mpt = d.firstMPT;
15169                     while (mpt) {
15170                         pt = mpt.t;
15171                         if (!pt.type) {
15172                             pt.e = pt.s + pt.xs0;
15173                         } else if (pt.type === 1) {
15174                             str = pt.xs0 + pt.s + pt.xs1;
15175                             for (i = 1; i < pt.l; i++) {
15176                                 str += pt["xn"+i] + pt["xs"+(i+1)];
15177                             }
15178                             pt.e = str;
15179                         }
15180                         mpt = mpt._next;
15181                     }
15182                 }
15183             },
15184
15185             /**
15186              * @private @constructor Used by a few SpecialProps to hold important values for proxies. For example, _parseToProxy() creates a MiniPropTween instance for each property that must get tweened on the proxy, and we record the original property name as well as the unique one we create for the proxy, plus whether or not the value needs to be rounded plus the original value.
15187              * @param {!Object} t target object whose property we're tweening (often a CSSPropTween)
15188              * @param {!string} p property name
15189              * @param {(number|string|object)} v value
15190              * @param {MiniPropTween=} next next MiniPropTween in the linked list
15191              * @param {boolean=} r if true, the tweened value should be rounded to the nearest integer
15192              */
15193             MiniPropTween = function(t, p, v, next, r) {
15194                 this.t = t;
15195                 this.p = p;
15196                 this.v = v;
15197                 this.r = r;
15198                 if (next) {
15199                     next._prev = this;
15200                     this._next = next;
15201                 }
15202             },
15203
15204             /**
15205              * @private Most other plugins (like BezierPlugin and ThrowPropsPlugin and others) can only tween numeric values, but CSSPlugin must accommodate special values that have a bunch of extra data (like a suffix or strings between numeric values, etc.). For example, boxShadow has values like "10px 10px 20px 30px rgb(255,0,0)" which would utterly confuse other plugins. This method allows us to split that data apart and grab only the numeric data and attach it to uniquely-named properties of a generic proxy object ({}) so that we can feed that to virtually any plugin to have the numbers tweened. However, we must also keep track of which properties from the proxy go with which CSSPropTween values and instances. So we create a linked list of MiniPropTweens. Each one records a target (the original CSSPropTween), property (like "s" or "xn1" or "xn2") that we're tweening and the unique property name that was used for the proxy (like "boxShadow_xn1" and "boxShadow_xn2") and whether or not they need to be rounded. That way, in the _setPluginRatio() method we can simply copy the values over from the proxy to the CSSPropTween instance(s). Then, when the main CSSPlugin setRatio() method runs and applies the CSSPropTween values accordingly, they're updated nicely. So the external plugin tweens the numbers, _setPluginRatio() copies them over, and setRatio() acts normally, applying css-specific values to the element.
15206              * This method returns an object that has the following properties:
15207              *  - proxy: a generic object containing the starting values for all the properties that will be tweened by the external plugin.  This is what we feed to the external _onInitTween() as the target
15208              *  - end: a generic object containing the ending values for all the properties that will be tweened by the external plugin. This is what we feed to the external plugin's _onInitTween() as the destination values
15209              *  - firstMPT: the first MiniPropTween in the linked list
15210              *  - pt: the first CSSPropTween in the linked list that was created when parsing. If shallow is true, this linked list will NOT attach to the one passed into the _parseToProxy() as the "pt" (4th) parameter.
15211              * @param {!Object} t target object to be tweened
15212              * @param {!(Object|string)} vars the object containing the information about the tweening values (typically the end/destination values) that should be parsed
15213              * @param {!CSSPlugin} cssp The CSSPlugin instance
15214              * @param {CSSPropTween=} pt the next CSSPropTween in the linked list
15215              * @param {TweenPlugin=} plugin the external TweenPlugin instance that will be handling tweening the numeric values
15216              * @param {boolean=} shallow if true, the resulting linked list from the parse will NOT be attached to the CSSPropTween that was passed in as the "pt" (4th) parameter.
15217              * @return An object containing the following properties: proxy, end, firstMPT, and pt (see above for descriptions)
15218              */
15219             _parseToProxy = _internals._parseToProxy = function(t, vars, cssp, pt, plugin, shallow) {
15220                 var bpt = pt,
15221                     start = {},
15222                     end = {},
15223                     transform = cssp._transform,
15224                     oldForce = _forcePT,
15225                     i, p, xp, mpt, firstPT;
15226                 cssp._transform = null;
15227                 _forcePT = vars;
15228                 pt = firstPT = cssp.parse(t, vars, pt, plugin);
15229                 _forcePT = oldForce;
15230                 //break off from the linked list so the new ones are isolated.
15231                 if (shallow) {
15232                     cssp._transform = transform;
15233                     if (bpt) {
15234                         bpt._prev = null;
15235                         if (bpt._prev) {
15236                             bpt._prev._next = null;
15237                         }
15238                     }
15239                 }
15240                 while (pt && pt !== bpt) {
15241                     if (pt.type <= 1) {
15242                         p = pt.p;
15243                         end[p] = pt.s + pt.c;
15244                         start[p] = pt.s;
15245                         if (!shallow) {
15246                             mpt = new MiniPropTween(pt, "s", p, mpt, pt.r);
15247                             pt.c = 0;
15248                         }
15249                         if (pt.type === 1) {
15250                             i = pt.l;
15251                             while (--i > 0) {
15252                                 xp = "xn" + i;
15253                                 p = pt.p + "_" + xp;
15254                                 end[p] = pt.data[xp];
15255                                 start[p] = pt[xp];
15256                                 if (!shallow) {
15257                                     mpt = new MiniPropTween(pt, xp, p, mpt, pt.rxp[xp]);
15258                                 }
15259                             }
15260                         }
15261                     }
15262                     pt = pt._next;
15263                 }
15264                 return {proxy:start, end:end, firstMPT:mpt, pt:firstPT};
15265             },
15266
15267
15268
15269             /**
15270              * @constructor Each property that is tweened has at least one CSSPropTween associated with it. These instances store important information like the target, property, starting value, amount of change, etc. They can also optionally have a number of "extra" strings and numeric values named xs1, xn1, xs2, xn2, xs3, xn3, etc. where "s" indicates string and "n" indicates number. These can be pieced together in a complex-value tween (type:1) that has alternating types of data like a string, number, string, number, etc. For example, boxShadow could be "5px 5px 8px rgb(102, 102, 51)". In that value, there are 6 numbers that may need to tween and then pieced back together into a string again with spaces, suffixes, etc. xs0 is special in that it stores the suffix for standard (type:0) tweens, -OR- the first string (prefix) in a complex-value (type:1) CSSPropTween -OR- it can be the non-tweening value in a type:-1 CSSPropTween. We do this to conserve memory.
15271              * CSSPropTweens have the following optional properties as well (not defined through the constructor):
15272              *  - l: Length in terms of the number of extra properties that the CSSPropTween has (default: 0). For example, for a boxShadow we may need to tween 5 numbers in which case l would be 5; Keep in mind that the start/end values for the first number that's tweened are always stored in the s and c properties to conserve memory. All additional values thereafter are stored in xn1, xn2, etc.
15273              *  - xfirst: The first instance of any sub-CSSPropTweens that are tweening properties of this instance. For example, we may split up a boxShadow tween so that there's a main CSSPropTween of type:1 that has various xs* and xn* values associated with the h-shadow, v-shadow, blur, color, etc. Then we spawn a CSSPropTween for each of those that has a higher priority and runs BEFORE the main CSSPropTween so that the values are all set by the time it needs to re-assemble them. The xfirst gives us an easy way to identify the first one in that chain which typically ends at the main one (because they're all prepende to the linked list)
15274              *  - plugin: The TweenPlugin instance that will handle the tweening of any complex values. For example, sometimes we don't want to use normal subtweens (like xfirst refers to) to tween the values - we might want ThrowPropsPlugin or BezierPlugin some other plugin to do the actual tweening, so we create a plugin instance and store a reference here. We need this reference so that if we get a request to round values or disable a tween, we can pass along that request.
15275              *  - data: Arbitrary data that needs to be stored with the CSSPropTween. Typically if we're going to have a plugin handle the tweening of a complex-value tween, we create a generic object that stores the END values that we're tweening to and the CSSPropTween's xs1, xs2, etc. have the starting values. We store that object as data. That way, we can simply pass that object to the plugin and use the CSSPropTween as the target.
15276              *  - setRatio: Only used for type:2 tweens that require custom functionality. In this case, we call the CSSPropTween's setRatio() method and pass the ratio each time the tween updates. This isn't quite as efficient as doing things directly in the CSSPlugin's setRatio() method, but it's very convenient and flexible.
15277              * @param {!Object} t Target object whose property will be tweened. Often a DOM element, but not always. It could be anything.
15278              * @param {string} p Property to tween (name). For example, to tween element.width, p would be "width".
15279              * @param {number} s Starting numeric value
15280              * @param {number} c Change in numeric value over the course of the entire tween. For example, if element.width starts at 5 and should end at 100, c would be 95.
15281              * @param {CSSPropTween=} next The next CSSPropTween in the linked list. If one is defined, we will define its _prev as the new instance, and the new instance's _next will be pointed at it.
15282              * @param {number=} type The type of CSSPropTween where -1 = a non-tweening value, 0 = a standard simple tween, 1 = a complex value (like one that has multiple numbers in a comma- or space-delimited string like border:"1px solid red"), and 2 = one that uses a custom setRatio function that does all of the work of applying the values on each update.
15283              * @param {string=} n Name of the property that should be used for overwriting purposes which is typically the same as p but not always. For example, we may need to create a subtween for the 2nd part of a "clip:rect(...)" tween in which case "p" might be xs1 but "n" is still "clip"
15284              * @param {boolean=} r If true, the value(s) should be rounded
15285              * @param {number=} pr Priority in the linked list order. Higher priority CSSPropTweens will be updated before lower priority ones. The default priority is 0.
15286              * @param {string=} b Beginning value. We store this to ensure that it is EXACTLY what it was when the tween began without any risk of interpretation issues.
15287              * @param {string=} e Ending value. We store this to ensure that it is EXACTLY what the user defined at the end of the tween without any risk of interpretation issues.
15288              */
15289             CSSPropTween = _internals.CSSPropTween = function(t, p, s, c, next, type, n, r, pr, b, e) {
15290                 this.t = t; //target
15291                 this.p = p; //property
15292                 this.s = s; //starting value
15293                 this.c = c; //change value
15294                 this.n = n || p; //name that this CSSPropTween should be associated to (usually the same as p, but not always - n is what overwriting looks at)
15295                 if (!(t instanceof CSSPropTween)) {
15296                     _overwriteProps.push(this.n);
15297                 }
15298                 this.r = r; //round (boolean)
15299                 this.type = type || 0; //0 = normal tween, -1 = non-tweening (in which case xs0 will be applied to the target's property, like tp.t[tp.p] = tp.xs0), 1 = complex-value SpecialProp, 2 = custom setRatio() that does all the work
15300                 if (pr) {
15301                     this.pr = pr;
15302                     _hasPriority = true;
15303                 }
15304                 this.b = (b === undefined) ? s : b;
15305                 this.e = (e === undefined) ? s + c : e;
15306                 if (next) {
15307                     this._next = next;
15308                     next._prev = this;
15309                 }
15310             },
15311
15312             /**
15313              * Takes a target, the beginning value and ending value (as strings) and parses them into a CSSPropTween (possibly with child CSSPropTweens) that accommodates multiple numbers, colors, comma-delimited values, etc. For example:
15314              * sp.parseComplex(element, "boxShadow", "5px 10px 20px rgb(255,102,51)", "0px 0px 0px red", true, "0px 0px 0px rgb(0,0,0,0)", pt);
15315              * It will walk through the beginning and ending values (which should be in the same format with the same number and type of values) and figure out which parts are numbers, what strings separate the numeric/tweenable values, and then create the CSSPropTweens accordingly. If a plugin is defined, no child CSSPropTweens will be created. Instead, the ending values will be stored in the "data" property of the returned CSSPropTween like: {s:-5, xn1:-10, xn2:-20, xn3:255, xn4:0, xn5:0} so that it can be fed to any other plugin and it'll be plain numeric tweens but the recomposition of the complex value will be handled inside CSSPlugin's setRatio().
15316              * If a setRatio is defined, the type of the CSSPropTween will be set to 2 and recomposition of the values will be the responsibility of that method.
15317              *
15318              * @param {!Object} t Target whose property will be tweened
15319              * @param {!string} p Property that will be tweened (its name, like "left" or "backgroundColor" or "boxShadow")
15320              * @param {string} b Beginning value
15321              * @param {string} e Ending value
15322              * @param {boolean} clrs If true, the value could contain a color value like "rgb(255,0,0)" or "#F00" or "red". The default is false, so no colors will be recognized (a performance optimization)
15323              * @param {(string|number|Object)} dflt The default beginning value that should be used if no valid beginning value is defined or if the number of values inside the complex beginning and ending values don't match
15324              * @param {?CSSPropTween} pt CSSPropTween instance that is the current head of the linked list (we'll prepend to this).
15325              * @param {number=} pr Priority in the linked list order. Higher priority properties will be updated before lower priority ones. The default priority is 0.
15326              * @param {TweenPlugin=} plugin If a plugin should handle the tweening of extra properties, pass the plugin instance here. If one is defined, then NO subtweens will be created for any extra properties (the properties will be created - just not additional CSSPropTween instances to tween them) because the plugin is expected to do so. However, the end values WILL be populated in the "data" property, like {s:100, xn1:50, xn2:300}
15327              * @param {function(number)=} setRatio If values should be set in a custom function instead of being pieced together in a type:1 (complex-value) CSSPropTween, define that custom function here.
15328              * @return {CSSPropTween} The first CSSPropTween in the linked list which includes the new one(s) added by the parseComplex() call.
15329              */
15330             _parseComplex = CSSPlugin.parseComplex = function(t, p, b, e, clrs, dflt, pt, pr, plugin, setRatio) {
15331                 //DEBUG: _log("parseComplex: "+p+", b: "+b+", e: "+e);
15332                 b = b || dflt || "";
15333                 pt = new CSSPropTween(t, p, 0, 0, pt, (setRatio ? 2 : 1), null, false, pr, b, e);
15334                 e += ""; //ensures it's a string
15335                 var ba = b.split(", ").join(",").split(" "), //beginning array
15336                     ea = e.split(", ").join(",").split(" "), //ending array
15337                     l = ba.length,
15338                     autoRound = (_autoRound !== false),
15339                     i, xi, ni, bv, ev, bnums, enums, bn, rgba, temp, cv, str;
15340                 if (e.indexOf(",") !== -1 || b.indexOf(",") !== -1) {
15341                     ba = ba.join(" ").replace(_commasOutsideParenExp, ", ").split(" ");
15342                     ea = ea.join(" ").replace(_commasOutsideParenExp, ", ").split(" ");
15343                     l = ba.length;
15344                 }
15345                 if (l !== ea.length) {
15346                     //DEBUG: _log("mismatched formatting detected on " + p + " (" + b + " vs " + e + ")");
15347                     ba = (dflt || "").split(" ");
15348                     l = ba.length;
15349                 }
15350                 pt.plugin = plugin;
15351                 pt.setRatio = setRatio;
15352                 for (i = 0; i < l; i++) {
15353                     bv = ba[i];
15354                     ev = ea[i];
15355                     bn = parseFloat(bv);
15356
15357                     //if the value begins with a number (most common). It's fine if it has a suffix like px
15358                     if (bn || bn === 0) {
15359                         pt.appendXtra("", bn, _parseChange(ev, bn), ev.replace(_relNumExp, ""), (autoRound && ev.indexOf("px") !== -1), true);
15360
15361                     //if the value is a color
15362                     } else if (clrs && (bv.charAt(0) === "#" || _colorLookup[bv] || _rgbhslExp.test(bv))) {
15363                         str = ev.charAt(ev.length - 1) === "," ? ")," : ")"; //if there's a comma at the end, retain it.
15364                         bv = _parseColor(bv);
15365                         ev = _parseColor(ev);
15366                         rgba = (bv.length + ev.length > 6);
15367                         if (rgba && !_supportsOpacity && ev[3] === 0) { //older versions of IE don't support rgba(), so if the destination alpha is 0, just use "transparent" for the end color
15368                             pt["xs" + pt.l] += pt.l ? " transparent" : "transparent";
15369                             pt.e = pt.e.split(ea[i]).join("transparent");
15370                         } else {
15371                             if (!_supportsOpacity) { //old versions of IE don't support rgba().
15372                                 rgba = false;
15373                             }
15374                             pt.appendXtra((rgba ? "rgba(" : "rgb("), bv[0], ev[0] - bv[0], ",", true, true)
15375                                 .appendXtra("", bv[1], ev[1] - bv[1], ",", true)
15376                                 .appendXtra("", bv[2], ev[2] - bv[2], (rgba ? "," : str), true);
15377                             if (rgba) {
15378                                 bv = (bv.length < 4) ? 1 : bv[3];
15379                                 pt.appendXtra("", bv, ((ev.length < 4) ? 1 : ev[3]) - bv, str, false);
15380                             }
15381                         }
15382
15383                     } else {
15384                         bnums = bv.match(_numExp); //gets each group of numbers in the beginning value string and drops them into an array
15385
15386                         //if no number is found, treat it as a non-tweening value and just append the string to the current xs.
15387                         if (!bnums) {
15388                             pt["xs" + pt.l] += pt.l ? " " + bv : bv;
15389
15390                         //loop through all the numbers that are found and construct the extra values on the pt.
15391                         } else {
15392                             enums = ev.match(_relNumExp); //get each group of numbers in the end value string and drop them into an array. We allow relative values too, like +=50 or -=.5
15393                             if (!enums || enums.length !== bnums.length) {
15394                                 //DEBUG: _log("mismatched formatting detected on " + p + " (" + b + " vs " + e + ")");
15395                                 return pt;
15396                             }
15397                             ni = 0;
15398                             for (xi = 0; xi < bnums.length; xi++) {
15399                                 cv = bnums[xi];
15400                                 temp = bv.indexOf(cv, ni);
15401                                 pt.appendXtra(bv.substr(ni, temp - ni), Number(cv), _parseChange(enums[xi], cv), "", (autoRound && bv.substr(temp + cv.length, 2) === "px"), (xi === 0));
15402                                 ni = temp + cv.length;
15403                             }
15404                             pt["xs" + pt.l] += bv.substr(ni);
15405                         }
15406                     }
15407                 }
15408                 //if there are relative values ("+=" or "-=" prefix), we need to adjust the ending value to eliminate the prefixes and combine the values properly.
15409                 if (e.indexOf("=") !== -1) if (pt.data) {
15410                     str = pt.xs0 + pt.data.s;
15411                     for (i = 1; i < pt.l; i++) {
15412                         str += pt["xs" + i] + pt.data["xn" + i];
15413                     }
15414                     pt.e = str + pt["xs" + i];
15415                 }
15416                 if (!pt.l) {
15417                     pt.type = -1;
15418                     pt.xs0 = pt.e;
15419                 }
15420                 return pt.xfirst || pt;
15421             },
15422             i = 9;
15423
15424
15425         p = CSSPropTween.prototype;
15426         p.l = p.pr = 0; //length (number of extra properties like xn1, xn2, xn3, etc.
15427         while (--i > 0) {
15428             p["xn" + i] = 0;
15429             p["xs" + i] = "";
15430         }
15431         p.xs0 = "";
15432         p._next = p._prev = p.xfirst = p.data = p.plugin = p.setRatio = p.rxp = null;
15433
15434
15435         /**
15436          * Appends and extra tweening value to a CSSPropTween and automatically manages any prefix and suffix strings. The first extra value is stored in the s and c of the main CSSPropTween instance, but thereafter any extras are stored in the xn1, xn2, xn3, etc. The prefixes and suffixes are stored in the xs0, xs1, xs2, etc. properties. For example, if I walk through a clip value like "rect(10px, 5px, 0px, 20px)", the values would be stored like this:
15437          * xs0:"rect(", s:10, xs1:"px, ", xn1:5, xs2:"px, ", xn2:0, xs3:"px, ", xn3:20, xn4:"px)"
15438          * And they'd all get joined together when the CSSPlugin renders (in the setRatio() method).
15439          * @param {string=} pfx Prefix (if any)
15440          * @param {!number} s Starting value
15441          * @param {!number} c Change in numeric value over the course of the entire tween. For example, if the start is 5 and the end is 100, the change would be 95.
15442          * @param {string=} sfx Suffix (if any)
15443          * @param {boolean=} r Round (if true).
15444          * @param {boolean=} pad If true, this extra value should be separated by the previous one by a space. If there is no previous extra and pad is true, it will automatically drop the space.
15445          * @return {CSSPropTween} returns itself so that multiple methods can be chained together.
15446          */
15447         p.appendXtra = function(pfx, s, c, sfx, r, pad) {
15448             var pt = this,
15449                 l = pt.l;
15450             pt["xs" + l] += (pad && l) ? " " + pfx : pfx || "";
15451             if (!c) if (l !== 0 && !pt.plugin) { //typically we'll combine non-changing values right into the xs to optimize performance, but we don't combine them when there's a plugin that will be tweening the values because it may depend on the values being split apart, like for a bezier, if a value doesn't change between the first and second iteration but then it does on the 3rd, we'll run into trouble because there's no xn slot for that value!
15452                 pt["xs" + l] += s + (sfx || "");
15453                 return pt;
15454             }
15455             pt.l++;
15456             pt.type = pt.setRatio ? 2 : 1;
15457             pt["xs" + pt.l] = sfx || "";
15458             if (l > 0) {
15459                 pt.data["xn" + l] = s + c;
15460                 pt.rxp["xn" + l] = r; //round extra property (we need to tap into this in the _parseToProxy() method)
15461                 pt["xn" + l] = s;
15462                 if (!pt.plugin) {
15463                     pt.xfirst = new CSSPropTween(pt, "xn" + l, s, c, pt.xfirst || pt, 0, pt.n, r, pt.pr);
15464                     pt.xfirst.xs0 = 0; //just to ensure that the property stays numeric which helps modern browsers speed up processing. Remember, in the setRatio() method, we do pt.t[pt.p] = val + pt.xs0 so if pt.xs0 is "" (the default), it'll cast the end value as a string. When a property is a number sometimes and a string sometimes, it prevents the compiler from locking in the data type, slowing things down slightly.
15465                 }
15466                 return pt;
15467             }
15468             pt.data = {s:s + c};
15469             pt.rxp = {};
15470             pt.s = s;
15471             pt.c = c;
15472             pt.r = r;
15473             return pt;
15474         };
15475
15476         /**
15477          * @constructor A SpecialProp is basically a css property that needs to be treated in a non-standard way, like if it may contain a complex value like boxShadow:"5px 10px 15px rgb(255, 102, 51)" or if it is associated with another plugin like ThrowPropsPlugin or BezierPlugin. Every SpecialProp is associated with a particular property name like "boxShadow" or "throwProps" or "bezier" and it will intercept those values in the vars object that's passed to the CSSPlugin and handle them accordingly.
15478          * @param {!string} p Property name (like "boxShadow" or "throwProps")
15479          * @param {Object=} options An object containing any of the following configuration options:
15480          *                      - defaultValue: the default value
15481          *                      - parser: A function that should be called when the associated property name is found in the vars. This function should return a CSSPropTween instance and it should ensure that it is properly inserted into the linked list. It will receive 4 paramters: 1) The target, 2) The value defined in the vars, 3) The CSSPlugin instance (whose _firstPT should be used for the linked list), and 4) A computed style object if one was calculated (this is a speed optimization that allows retrieval of starting values quicker)
15482          *                      - formatter: a function that formats any value received for this special property (for example, boxShadow could take "5px 5px red" and format it to "5px 5px 0px 0px red" so that both the beginning and ending values have a common order and quantity of values.)
15483          *                      - prefix: if true, we'll determine whether or not this property requires a vendor prefix (like Webkit or Moz or ms or O)
15484          *                      - color: set this to true if the value for this SpecialProp may contain color-related values like rgb(), rgba(), etc.
15485          *                      - priority: priority in the linked list order. Higher priority SpecialProps will be updated before lower priority ones. The default priority is 0.
15486          *                      - multi: if true, the formatter should accommodate a comma-delimited list of values, like boxShadow could have multiple boxShadows listed out.
15487          *                      - collapsible: if true, the formatter should treat the value like it's a top/right/bottom/left value that could be collapsed, like "5px" would apply to all, "5px, 10px" would use 5px for top/bottom and 10px for right/left, etc.
15488          *                      - keyword: a special keyword that can [optionally] be found inside the value (like "inset" for boxShadow). This allows us to validate beginning/ending values to make sure they match (if the keyword is found in one, it'll be added to the other for consistency by default).
15489          */
15490         var SpecialProp = function(p, options) {
15491                 options = options || {};
15492                 this.p = options.prefix ? _checkPropPrefix(p) || p : p;
15493                 _specialProps[p] = _specialProps[this.p] = this;
15494                 this.format = options.formatter || _getFormatter(options.defaultValue, options.color, options.collapsible, options.multi);
15495                 if (options.parser) {
15496                     this.parse = options.parser;
15497                 }
15498                 this.clrs = options.color;
15499                 this.multi = options.multi;
15500                 this.keyword = options.keyword;
15501                 this.dflt = options.defaultValue;
15502                 this.pr = options.priority || 0;
15503             },
15504
15505             //shortcut for creating a new SpecialProp that can accept multiple properties as a comma-delimited list (helps minification). dflt can be an array for multiple values (we don't do a comma-delimited list because the default value may contain commas, like rect(0px,0px,0px,0px)). We attach this method to the SpecialProp class/object instead of using a private _createSpecialProp() method so that we can tap into it externally if necessary, like from another plugin.
15506             _registerComplexSpecialProp = _internals._registerComplexSpecialProp = function(p, options, defaults) {
15507                 if (typeof(options) !== "object") {
15508                     options = {parser:defaults}; //to make backwards compatible with older versions of BezierPlugin and ThrowPropsPlugin
15509                 }
15510                 var a = p.split(","),
15511                     d = options.defaultValue,
15512                     i, temp;
15513                 defaults = defaults || [d];
15514                 for (i = 0; i < a.length; i++) {
15515                     options.prefix = (i === 0 && options.prefix);
15516                     options.defaultValue = defaults[i] || d;
15517                     temp = new SpecialProp(a[i], options);
15518                 }
15519             },
15520
15521             //creates a placeholder special prop for a plugin so that the property gets caught the first time a tween of it is attempted, and at that time it makes the plugin register itself, thus taking over for all future tweens of that property. This allows us to not mandate that things load in a particular order and it also allows us to log() an error that informs the user when they attempt to tween an external plugin-related property without loading its .js file.
15522             _registerPluginProp = function(p) {
15523                 if (!_specialProps[p]) {
15524                     var pluginName = p.charAt(0).toUpperCase() + p.substr(1) + "Plugin";
15525                     _registerComplexSpecialProp(p, {parser:function(t, e, p, cssp, pt, plugin, vars) {
15526                         var pluginClass = (window.GreenSockGlobals || window).com.greensock.plugins[pluginName];
15527                         if (!pluginClass) {
15528                             _log("Error: " + pluginName + " js file not loaded.");
15529                             return pt;
15530                         }
15531                         pluginClass._cssRegister();
15532                         return _specialProps[p].parse(t, e, p, cssp, pt, plugin, vars);
15533                     }});
15534                 }
15535             };
15536
15537
15538         p = SpecialProp.prototype;
15539
15540         /**
15541          * Alias for _parseComplex() that automatically plugs in certain values for this SpecialProp, like its property name, whether or not colors should be sensed, the default value, and priority. It also looks for any keyword that the SpecialProp defines (like "inset" for boxShadow) and ensures that the beginning and ending values have the same number of values for SpecialProps where multi is true (like boxShadow and textShadow can have a comma-delimited list)
15542          * @param {!Object} t target element
15543          * @param {(string|number|object)} b beginning value
15544          * @param {(string|number|object)} e ending (destination) value
15545          * @param {CSSPropTween=} pt next CSSPropTween in the linked list
15546          * @param {TweenPlugin=} plugin If another plugin will be tweening the complex value, that TweenPlugin instance goes here.
15547          * @param {function=} setRatio If a custom setRatio() method should be used to handle this complex value, that goes here.
15548          * @return {CSSPropTween=} First CSSPropTween in the linked list
15549          */
15550         p.parseComplex = function(t, b, e, pt, plugin, setRatio) {
15551             var kwd = this.keyword,
15552                 i, ba, ea, l, bi, ei;
15553             //if this SpecialProp's value can contain a comma-delimited list of values (like boxShadow or textShadow), we must parse them in a special way, and look for a keyword (like "inset" for boxShadow) and ensure that the beginning and ending BOTH have it if the end defines it as such. We also must ensure that there are an equal number of values specified (we can't tween 1 boxShadow to 3 for example)
15554             if (this.multi) if (_commasOutsideParenExp.test(e) || _commasOutsideParenExp.test(b)) {
15555                 ba = b.replace(_commasOutsideParenExp, "|").split("|");
15556                 ea = e.replace(_commasOutsideParenExp, "|").split("|");
15557             } else if (kwd) {
15558                 ba = [b];
15559                 ea = [e];
15560             }
15561             if (ea) {
15562                 l = (ea.length > ba.length) ? ea.length : ba.length;
15563                 for (i = 0; i < l; i++) {
15564                     b = ba[i] = ba[i] || this.dflt;
15565                     e = ea[i] = ea[i] || this.dflt;
15566                     if (kwd) {
15567                         bi = b.indexOf(kwd);
15568                         ei = e.indexOf(kwd);
15569                         if (bi !== ei) {
15570                             e = (ei === -1) ? ea : ba;
15571                             e[i] += " " + kwd;
15572                         }
15573                     }
15574                 }
15575                 b = ba.join(", ");
15576                 e = ea.join(", ");
15577             }
15578             return _parseComplex(t, this.p, b, e, this.clrs, this.dflt, pt, this.pr, plugin, setRatio);
15579         };
15580
15581         /**
15582          * Accepts a target and end value and spits back a CSSPropTween that has been inserted into the CSSPlugin's linked list and conforms with all the conventions we use internally, like type:-1, 0, 1, or 2, setting up any extra property tweens, priority, etc. For example, if we have a boxShadow SpecialProp and call:
15583          * this._firstPT = sp.parse(element, "5px 10px 20px rgb(2550,102,51)", "boxShadow", this);
15584          * It should figure out the starting value of the element's boxShadow, compare it to the provided end value and create all the necessary CSSPropTweens of the appropriate types to tween the boxShadow. The CSSPropTween that gets spit back should already be inserted into the linked list (the 4th parameter is the current head, so prepend to that).
15585          * @param {!Object} t Target object whose property is being tweened
15586          * @param {Object} e End value as provided in the vars object (typically a string, but not always - like a throwProps would be an object).
15587          * @param {!string} p Property name
15588          * @param {!CSSPlugin} cssp The CSSPlugin instance that should be associated with this tween.
15589          * @param {?CSSPropTween} pt The CSSPropTween that is the current head of the linked list (we'll prepend to it)
15590          * @param {TweenPlugin=} plugin If a plugin will be used to tween the parsed value, this is the plugin instance.
15591          * @param {Object=} vars Original vars object that contains the data for parsing.
15592          * @return {CSSPropTween} The first CSSPropTween in the linked list which includes the new one(s) added by the parse() call.
15593          */
15594         p.parse = function(t, e, p, cssp, pt, plugin, vars) {
15595             return this.parseComplex(t.style, this.format(_getStyle(t, this.p, _cs, false, this.dflt)), this.format(e), pt, plugin);
15596         };
15597
15598         /**
15599          * Registers a special property that should be intercepted from any "css" objects defined in tweens. This allows you to handle them however you want without CSSPlugin doing it for you. The 2nd parameter should be a function that accepts 3 parameters:
15600          *  1) Target object whose property should be tweened (typically a DOM element)
15601          *  2) The end/destination value (could be a string, number, object, or whatever you want)
15602          *  3) The tween instance (you probably don't need to worry about this, but it can be useful for looking up information like the duration)
15603          *
15604          * Then, your function should return a function which will be called each time the tween gets rendered, passing a numeric "ratio" parameter to your function that indicates the change factor (usually between 0 and 1). For example:
15605          *
15606          * CSSPlugin.registerSpecialProp("myCustomProp", function(target, value, tween) {
15607          *      var start = target.style.width;
15608          *      return function(ratio) {
15609          *              target.style.width = (start + value * ratio) + "px";
15610          *              console.log("set width to " + target.style.width);
15611          *          }
15612          * }, 0);
15613          *
15614          * Then, when I do this tween, it will trigger my special property:
15615          *
15616          * TweenLite.to(element, 1, {css:{myCustomProp:100}});
15617          *
15618          * In the example, of course, we're just changing the width, but you can do anything you want.
15619          *
15620          * @param {!string} name Property name (or comma-delimited list of property names) that should be intercepted and handled by your function. For example, if I define "myCustomProp", then it would handle that portion of the following tween: TweenLite.to(element, 1, {css:{myCustomProp:100}})
15621          * @param {!function(Object, Object, Object, string):function(number)} onInitTween The function that will be called when a tween of this special property is performed. The function will receive 4 parameters: 1) Target object that should be tweened, 2) Value that was passed to the tween, 3) The tween instance itself (rarely used), and 4) The property name that's being tweened. Your function should return a function that should be called on every update of the tween. That function will receive a single parameter that is a "change factor" value (typically between 0 and 1) indicating the amount of change as a ratio. You can use this to determine how to set the values appropriately in your function.
15622          * @param {number=} priority Priority that helps the engine determine the order in which to set the properties (default: 0). Higher priority properties will be updated before lower priority ones.
15623          */
15624         CSSPlugin.registerSpecialProp = function(name, onInitTween, priority) {
15625             _registerComplexSpecialProp(name, {parser:function(t, e, p, cssp, pt, plugin, vars) {
15626                 var rv = new CSSPropTween(t, p, 0, 0, pt, 2, p, false, priority);
15627                 rv.plugin = plugin;
15628                 rv.setRatio = onInitTween(t, e, cssp._tween, p);
15629                 return rv;
15630             }, priority:priority});
15631         };
15632
15633
15634
15635
15636
15637
15638
15639
15640         //transform-related methods and properties
15641         var _transformProps = ("scaleX,scaleY,scaleZ,x,y,z,skewX,skewY,rotation,rotationX,rotationY,perspective").split(","),
15642             _transformProp = _checkPropPrefix("transform"), //the Javascript (camelCase) transform property, like msTransform, WebkitTransform, MozTransform, or OTransform.
15643             _transformPropCSS = _prefixCSS + "transform",
15644             _transformOriginProp = _checkPropPrefix("transformOrigin"),
15645             _supports3D = (_checkPropPrefix("perspective") !== null),
15646             Transform = _internals.Transform = function() {
15647                 this.skewY = 0;
15648             },
15649
15650             /**
15651              * Parses the transform values for an element, returning an object with x, y, z, scaleX, scaleY, scaleZ, rotation, rotationX, rotationY, skewX, and skewY properties. Note: by default (for performance reasons), all skewing is combined into skewX and rotation but skewY still has a place in the transform object so that we can record how much of the skew is attributed to skewX vs skewY. Remember, a skewY of 10 looks the same as a rotation of 10 and skewX of -10.
15652              * @param {!Object} t target element
15653              * @param {Object=} cs computed style object (optional)
15654              * @param {boolean=} rec if true, the transform values will be recorded to the target element's _gsTransform object, like target._gsTransform = {x:0, y:0, z:0, scaleX:1...}
15655              * @param {boolean=} parse if true, we'll ignore any _gsTransform values that already exist on the element, and force a reparsing of the css (calculated style)
15656              * @return {object} object containing all of the transform properties/values like {x:0, y:0, z:0, scaleX:1...}
15657              */
15658             _getTransform = _internals.getTransform = function(t, cs, rec, parse) {
15659                 if (t._gsTransform && rec && !parse) {
15660                     return t._gsTransform; //if the element already has a _gsTransform, use that. Note: some browsers don't accurately return the calculated style for the transform (particularly for SVG), so it's almost always safest to just use the values we've already applied rather than re-parsing things.
15661                 }
15662                 var tm = rec ? t._gsTransform || new Transform() : new Transform(),
15663                     invX = (tm.scaleX < 0), //in order to interpret things properly, we need to know if the user applied a negative scaleX previously so that we can adjust the rotation and skewX accordingly. Otherwise, if we always interpret a flipped matrix as affecting scaleY and the user only wants to tween the scaleX on multiple sequential tweens, it would keep the negative scaleY without that being the user's intent.
15664                     min = 0.00002,
15665                     rnd = 100000,
15666                     minAngle = 179.99,
15667                     minPI = minAngle * _DEG2RAD,
15668                     zOrigin = _supports3D ? parseFloat(_getStyle(t, _transformOriginProp, cs, false, "0 0 0").split(" ")[2]) || tm.zOrigin  || 0 : 0,
15669                     s, m, i, n, dec, scaleX, scaleY, rotation, skewX, difX, difY, difR, difS;
15670                 if (_transformProp) {
15671                     s = _getStyle(t, _transformPropCSS, cs, true);
15672                 } else if (t.currentStyle) {
15673                     //for older versions of IE, we need to interpret the filter portion that is in the format: progid:DXImageTransform.Microsoft.Matrix(M11=6.123233995736766e-17, M12=-1, M21=1, M22=6.123233995736766e-17, sizingMethod='auto expand') Notice that we need to swap b and c compared to a normal matrix.
15674                     s = t.currentStyle.filter.match(_ieGetMatrixExp);
15675                     s = (s && s.length === 4) ? [s[0].substr(4), Number(s[2].substr(4)), Number(s[1].substr(4)), s[3].substr(4), (tm.x || 0), (tm.y || 0)].join(",") : "";
15676                 }
15677                 //split the matrix values out into an array (m for matrix)
15678                 m = (s || "").match(/(?:\-|\b)[\d\-\.e]+\b/gi) || [];
15679                 i = m.length;
15680                 while (--i > -1) {
15681                     n = Number(m[i]);
15682                     m[i] = (dec = n - (n |= 0)) ? ((dec * rnd + (dec < 0 ? -0.5 : 0.5)) | 0) / rnd + n : n; //convert strings to Numbers and round to 5 decimal places to avoid issues with tiny numbers. Roughly 20x faster than Number.toFixed(). We also must make sure to round before dividing so that values like 0.9999999999 become 1 to avoid glitches in browser rendering and interpretation of flipped/rotated 3D matrices. And don't just multiply the number by rnd, floor it, and then divide by rnd because the bitwise operations max out at a 32-bit signed integer, thus it could get clipped at a relatively low value (like 22,000.00000 for example).
15683                 }
15684                 if (m.length === 16) {
15685
15686                     //we'll only look at these position-related 6 variables first because if x/y/z all match, it's relatively safe to assume we don't need to re-parse everything which risks losing important rotational information (like rotationX:180 plus rotationY:180 would look the same as rotation:180 - there's no way to know for sure which direction was taken based solely on the matrix3d() values)
15687                     var a13 = m[8], a23 = m[9], a33 = m[10],
15688                         a14 = m[12], a24 = m[13], a34 = m[14];
15689
15690                     //we manually compensate for non-zero z component of transformOrigin to work around bugs in Safari
15691                     if (tm.zOrigin) {
15692                         a34 = -tm.zOrigin;
15693                         a14 = a13*a34-m[12];
15694                         a24 = a23*a34-m[13];
15695                         a34 = a33*a34+tm.zOrigin-m[14];
15696                     }
15697
15698                     //only parse from the matrix if we MUST because not only is it usually unnecessary due to the fact that we store the values in the _gsTransform object, but also because it's impossible to accurately interpret rotationX, rotationY, rotationZ, scaleX, and scaleY if all are applied, so it's much better to rely on what we store. However, we must parse the first time that an object is tweened. We also assume that if the position has changed, the user must have done some styling changes outside of CSSPlugin, thus we force a parse in that scenario.
15699                     if (!rec || parse || tm.rotationX == null) {
15700                         var a11 = m[0], a21 = m[1], a31 = m[2], a41 = m[3],
15701                             a12 = m[4], a22 = m[5], a32 = m[6], a42 = m[7],
15702                             a43 = m[11],
15703                             angle = Math.atan2(a32, a33),
15704                             xFlip = (angle < -minPI || angle > minPI),
15705                             t1, t2, t3, cos, sin, yFlip, zFlip;
15706                         tm.rotationX = angle * _RAD2DEG;
15707                         //rotationX
15708                         if (angle) {
15709                             cos = Math.cos(-angle);
15710                             sin = Math.sin(-angle);
15711                             t1 = a12*cos+a13*sin;
15712                             t2 = a22*cos+a23*sin;
15713                             t3 = a32*cos+a33*sin;
15714                             a13 = a12*-sin+a13*cos;
15715                             a23 = a22*-sin+a23*cos;
15716                             a33 = a32*-sin+a33*cos;
15717                             a43 = a42*-sin+a43*cos;
15718                             a12 = t1;
15719                             a22 = t2;
15720                             a32 = t3;
15721                         }
15722                         //rotationY
15723                         angle = Math.atan2(a13, a11);
15724                         tm.rotationY = angle * _RAD2DEG;
15725                         if (angle) {
15726                             yFlip = (angle < -minPI || angle > minPI);
15727                             cos = Math.cos(-angle);
15728                             sin = Math.sin(-angle);
15729                             t1 = a11*cos-a13*sin;
15730                             t2 = a21*cos-a23*sin;
15731                             t3 = a31*cos-a33*sin;
15732                             a23 = a21*sin+a23*cos;
15733                             a33 = a31*sin+a33*cos;
15734                             a43 = a41*sin+a43*cos;
15735                             a11 = t1;
15736                             a21 = t2;
15737                             a31 = t3;
15738                         }
15739                         //rotationZ
15740                         angle = Math.atan2(a21, a22);
15741                         tm.rotation = angle * _RAD2DEG;
15742                         if (angle) {
15743                             zFlip = (angle < -minPI || angle > minPI);
15744                             cos = Math.cos(-angle);
15745                             sin = Math.sin(-angle);
15746                             a11 = a11*cos+a12*sin;
15747                             t2 = a21*cos+a22*sin;
15748                             a22 = a21*-sin+a22*cos;
15749                             a32 = a31*-sin+a32*cos;
15750                             a21 = t2;
15751                         }
15752
15753                         if (zFlip && xFlip) {
15754                             tm.rotation = tm.rotationX = 0;
15755                         } else if (zFlip && yFlip) {
15756                             tm.rotation = tm.rotationY = 0;
15757                         } else if (yFlip && xFlip) {
15758                             tm.rotationY = tm.rotationX = 0;
15759                         }
15760
15761                         tm.scaleX = ((Math.sqrt(a11 * a11 + a21 * a21) * rnd + 0.5) | 0) / rnd;
15762                         tm.scaleY = ((Math.sqrt(a22 * a22 + a23 * a23) * rnd + 0.5) | 0) / rnd;
15763                         tm.scaleZ = ((Math.sqrt(a32 * a32 + a33 * a33) * rnd + 0.5) | 0) / rnd;
15764                         tm.skewX = 0;
15765                         tm.perspective = a43 ? 1 / ((a43 < 0) ? -a43 : a43) : 0;
15766                         tm.x = a14;
15767                         tm.y = a24;
15768                         tm.z = a34;
15769                     }
15770
15771                 } else if ((!_supports3D || parse || !m.length || tm.x !== m[4] || tm.y !== m[5] || (!tm.rotationX && !tm.rotationY)) && !(tm.x !== undefined && _getStyle(t, "display", cs) === "none")) { //sometimes a 6-element matrix is returned even when we performed 3D transforms, like if rotationX and rotationY are 180. In cases like this, we still need to honor the 3D transforms. If we just rely on the 2D info, it could affect how the data is interpreted, like scaleY might get set to -1 or rotation could get offset by 180 degrees. For example, do a TweenLite.to(element, 1, {css:{rotationX:180, rotationY:180}}) and then later, TweenLite.to(element, 1, {css:{rotationX:0}}) and without this conditional logic in place, it'd jump to a state of being unrotated when the 2nd tween starts. Then again, we need to honor the fact that the user COULD alter the transforms outside of CSSPlugin, like by manually applying new css, so we try to sense that by looking at x and y because if those changed, we know the changes were made outside CSSPlugin and we force a reinterpretation of the matrix values. Also, in Webkit browsers, if the element's "display" is "none", its calculated style value will always return empty, so if we've already recorded the values in the _gsTransform object, we'll just rely on those.
15772                     var k = (m.length >= 6),
15773                         a = k ? m[0] : 1,
15774                         b = m[1] || 0,
15775                         c = m[2] || 0,
15776                         d = k ? m[3] : 1;
15777                     tm.x = m[4] || 0;
15778                     tm.y = m[5] || 0;
15779                     scaleX = Math.sqrt(a * a + b * b);
15780                     scaleY = Math.sqrt(d * d + c * c);
15781                     rotation = (a || b) ? Math.atan2(b, a) * _RAD2DEG : tm.rotation || 0; //note: if scaleX is 0, we cannot accurately measure rotation. Same for skewX with a scaleY of 0. Therefore, we default to the previously recorded value (or zero if that doesn't exist).
15782                     skewX = (c || d) ? Math.atan2(c, d) * _RAD2DEG + rotation : tm.skewX || 0;
15783                     difX = scaleX - Math.abs(tm.scaleX || 0);
15784                     difY = scaleY - Math.abs(tm.scaleY || 0);
15785                     if (Math.abs(skewX) > 90 && Math.abs(skewX) < 270) {
15786                         if (invX) {
15787                             scaleX *= -1;
15788                             skewX += (rotation <= 0) ? 180 : -180;
15789                             rotation += (rotation <= 0) ? 180 : -180;
15790                         } else {
15791                             scaleY *= -1;
15792                             skewX += (skewX <= 0) ? 180 : -180;
15793                         }
15794                     }
15795                     difR = (rotation - tm.rotation) % 180; //note: matching ranges would be very small (+/-0.0001) or very close to 180.
15796                     difS = (skewX - tm.skewX) % 180;
15797                     //if there's already a recorded _gsTransform in place for the target, we should leave those values in place unless we know things changed for sure (beyond a super small amount). This gets around ambiguous interpretations, like if scaleX and scaleY are both -1, the matrix would be the same as if the rotation was 180 with normal scaleX/scaleY. If the user tweened to particular values, those must be prioritized to ensure animation is consistent.
15798                     if (tm.skewX === undefined || difX > min || difX < -min || difY > min || difY < -min || (difR > -minAngle && difR < minAngle && (difR * rnd) | 0 !== 0) || (difS > -minAngle && difS < minAngle && (difS * rnd) | 0 !== 0)) {
15799                         tm.scaleX = scaleX;
15800                         tm.scaleY = scaleY;
15801                         tm.rotation = rotation;
15802                         tm.skewX = skewX;
15803                     }
15804                     if (_supports3D) {
15805                         tm.rotationX = tm.rotationY = tm.z = 0;
15806                         tm.perspective = parseFloat(CSSPlugin.defaultTransformPerspective) || 0;
15807                         tm.scaleZ = 1;
15808                     }
15809                 }
15810                 tm.zOrigin = zOrigin;
15811
15812                 //some browsers have a hard time with very small values like 2.4492935982947064e-16 (notice the "e-" towards the end) and would render the object slightly off. So we round to 0 in these cases. The conditional logic here is faster than calling Math.abs(). Also, browsers tend to render a SLIGHTLY rotated object in a fuzzy way, so we need to snap to exactly 0 when appropriate.
15813                 for (i in tm) {
15814                     if (tm[i] < min) if (tm[i] > -min) {
15815                         tm[i] = 0;
15816                     }
15817                 }
15818                 //DEBUG: _log("parsed rotation: "+(tm.rotationX)+", "+(tm.rotationY)+", "+(tm.rotation)+", scale: "+tm.scaleX+", "+tm.scaleY+", "+tm.scaleZ+", position: "+tm.x+", "+tm.y+", "+tm.z+", perspective: "+tm.perspective);
15819                 if (rec) {
15820                     t._gsTransform = tm; //record to the object's _gsTransform which we use so that tweens can control individual properties independently (we need all the properties to accurately recompose the matrix in the setRatio() method)
15821                 }
15822                 return tm;
15823             },
15824
15825             //for setting 2D transforms in IE6, IE7, and IE8 (must use a "filter" to emulate the behavior of modern day browser transforms)
15826             _setIETransformRatio = function(v) {
15827                 var t = this.data, //refers to the element's _gsTransform object
15828                     ang = -t.rotation * _DEG2RAD,
15829                     skew = ang + t.skewX * _DEG2RAD,
15830                     rnd = 100000,
15831                     a = ((Math.cos(ang) * t.scaleX * rnd) | 0) / rnd,
15832                     b = ((Math.sin(ang) * t.scaleX * rnd) | 0) / rnd,
15833                     c = ((Math.sin(skew) * -t.scaleY * rnd) | 0) / rnd,
15834                     d = ((Math.cos(skew) * t.scaleY * rnd) | 0) / rnd,
15835                     style = this.t.style,
15836                     cs = this.t.currentStyle,
15837                     filters, val;
15838                 if (!cs) {
15839                     return;
15840                 }
15841                 val = b; //just for swapping the variables an inverting them (reused "val" to avoid creating another variable in memory). IE's filter matrix uses a non-standard matrix configuration (angle goes the opposite way, and b and c are reversed and inverted)
15842                 b = -c;
15843                 c = -val;
15844                 filters = cs.filter;
15845                 style.filter = ""; //remove filters so that we can accurately measure offsetWidth/offsetHeight
15846                 var w = this.t.offsetWidth,
15847                     h = this.t.offsetHeight,
15848                     clip = (cs.position !== "absolute"),
15849                     m = "progid:DXImageTransform.Microsoft.Matrix(M11=" + a + ", M12=" + b + ", M21=" + c + ", M22=" + d,
15850                     ox = t.x,
15851                     oy = t.y,
15852                     dx, dy;
15853
15854                 //if transformOrigin is being used, adjust the offset x and y
15855                 if (t.ox != null) {
15856                     dx = ((t.oxp) ? w * t.ox * 0.01 : t.ox) - w / 2;
15857                     dy = ((t.oyp) ? h * t.oy * 0.01 : t.oy) - h / 2;
15858                     ox += dx - (dx * a + dy * b);
15859                     oy += dy - (dx * c + dy * d);
15860                 }
15861
15862                 if (!clip) {
15863                     m += ", sizingMethod='auto expand')";
15864                 } else {
15865                     dx = (w / 2);
15866                     dy = (h / 2);
15867                     //translate to ensure that transformations occur around the correct origin (default is center).
15868                     m += ", Dx=" + (dx - (dx * a + dy * b) + ox) + ", Dy=" + (dy - (dx * c + dy * d) + oy) + ")";
15869                 }
15870                 if (filters.indexOf("DXImageTransform.Microsoft.Matrix(") !== -1) {
15871                     style.filter = filters.replace(_ieSetMatrixExp, m);
15872                 } else {
15873                     style.filter = m + " " + filters; //we must always put the transform/matrix FIRST (before alpha(opacity=xx)) to avoid an IE bug that slices part of the object when rotation is applied with alpha.
15874                 }
15875
15876                 //at the end or beginning of the tween, if the matrix is normal (1, 0, 0, 1) and opacity is 100 (or doesn't exist), remove the filter to improve browser performance.
15877                 if (v === 0 || v === 1) if (a === 1) if (b === 0) if (c === 0) if (d === 1) if (!clip || m.indexOf("Dx=0, Dy=0") !== -1) if (!_opacityExp.test(filters) || parseFloat(RegExp.$1) === 100) if (filters.indexOf("gradient(" && filters.indexOf("Alpha")) === -1) {
15878                     style.removeAttribute("filter");
15879                 }
15880
15881                 //we must set the margins AFTER applying the filter in order to avoid some bugs in IE8 that could (in rare scenarios) cause them to be ignored intermittently (vibration).
15882                 if (!clip) {
15883                     var mult = (_ieVers < 8) ? 1 : -1, //in Internet Explorer 7 and before, the box model is broken, causing the browser to treat the width/height of the actual rotated filtered image as the width/height of the box itself, but Microsoft corrected that in IE8. We must use a negative offset in IE8 on the right/bottom
15884                         marg, prop, dif;
15885                     dx = t.ieOffsetX || 0;
15886                     dy = t.ieOffsetY || 0;
15887                     t.ieOffsetX = Math.round((w - ((a < 0 ? -a : a) * w + (b < 0 ? -b : b) * h)) / 2 + ox);
15888                     t.ieOffsetY = Math.round((h - ((d < 0 ? -d : d) * h + (c < 0 ? -c : c) * w)) / 2 + oy);
15889                     for (i = 0; i < 4; i++) {
15890                         prop = _margins[i];
15891                         marg = cs[prop];
15892                         //we need to get the current margin in case it is being tweened separately (we want to respect that tween's changes)
15893                         val = (marg.indexOf("px") !== -1) ? parseFloat(marg) : _convertToPixels(this.t, prop, parseFloat(marg), marg.replace(_suffixExp, "")) || 0;
15894                         if (val !== t[prop]) {
15895                             dif = (i < 2) ? -t.ieOffsetX : -t.ieOffsetY; //if another tween is controlling a margin, we cannot only apply the difference in the ieOffsets, so we essentially zero-out the dx and dy here in that case. We record the margin(s) later so that we can keep comparing them, making this code very flexible.
15896                         } else {
15897                             dif = (i < 2) ? dx - t.ieOffsetX : dy - t.ieOffsetY;
15898                         }
15899                         style[prop] = (t[prop] = Math.round( val - dif * ((i === 0 || i === 2) ? 1 : mult) )) + "px";
15900                     }
15901                 }
15902             },
15903
15904             _set3DTransformRatio = _internals.set3DTransformRatio = function(v) {
15905                 var t = this.data, //refers to the element's _gsTransform object
15906                     style = this.t.style,
15907                     angle = t.rotation * _DEG2RAD,
15908                     sx = t.scaleX,
15909                     sy = t.scaleY,
15910                     sz = t.scaleZ,
15911                     perspective = t.perspective,
15912                     a11, a12, a13, a14, a21, a22, a23, a24, a31, a32, a33, a34, a41, a42, a43,
15913                     zOrigin, rnd, cos, sin, t1, t2, t3, t4;
15914                 if (v === 1 || v === 0) if (t.force3D === "auto") if (!t.rotationY && !t.rotationX && sz === 1 && !perspective && !t.z) { //on the final render (which could be 0 for a from tween), if there are no 3D aspects, render in 2D to free up memory and improve performance especially on mobile devices
15915                     _set2DTransformRatio.call(this, v);
15916                     return;
15917                 }
15918                 if (_isFirefox) {
15919                     var n = 0.0001;
15920                     if (sx < n && sx > -n) { //Firefox has a bug (at least in v25) that causes it to render the transparent part of 32-bit PNG images as black when displayed inside an iframe and the 3D scale is very small and doesn't change sufficiently enough between renders (like if you use a Power4.easeInOut to scale from 0 to 1 where the beginning values only change a tiny amount to begin the tween before accelerating). In this case, we force the scale to be 0.00002 instead which is visually the same but works around the Firefox issue.
15921                         sx = sz = 0.00002;
15922                     }
15923                     if (sy < n && sy > -n) {
15924                         sy = sz = 0.00002;
15925                     }
15926                     if (perspective && !t.z && !t.rotationX && !t.rotationY) { //Firefox has a bug that causes elements to have an odd super-thin, broken/dotted black border on elements that have a perspective set but aren't utilizing 3D space (no rotationX, rotationY, or z).
15927                         perspective = 0;
15928                     }
15929                 }
15930                 if (angle || t.skewX) {
15931                     cos = Math.cos(angle);
15932                     sin = Math.sin(angle);
15933                     a11 = cos;
15934                     a21 = sin;
15935                     if (t.skewX) {
15936                         angle -= t.skewX * _DEG2RAD;
15937                         cos = Math.cos(angle);
15938                         sin = Math.sin(angle);
15939                         if (t.skewType === "simple") { //by default, we compensate skewing on the other axis to make it look more natural, but you can set the skewType to "simple" to use the uncompensated skewing that CSS does
15940                             t1 = Math.tan(t.skewX * _DEG2RAD);
15941                             t1 = Math.sqrt(1 + t1 * t1);
15942                             cos *= t1;
15943                             sin *= t1;
15944                         }
15945                     }
15946                     a12 = -sin;
15947                     a22 = cos;
15948
15949                 } else if (!t.rotationY && !t.rotationX && sz === 1 && !perspective) { //if we're only translating and/or 2D scaling, this is faster...
15950                     style[_transformProp] = "translate3d(" + t.x + "px," + t.y + "px," + t.z +"px)" + ((sx !== 1 || sy !== 1) ? " scale(" + sx + "," + sy + ")" : "");
15951                     return;
15952                 } else {
15953                     a11 = a22 = 1;
15954                     a12 = a21 = 0;
15955                 }
15956                 a33 = 1;
15957                 a13 = a14 = a23 = a24 = a31 = a32 = a34 = a41 = a42 = 0;
15958                 a43 = (perspective) ? -1 / perspective : 0;
15959                 zOrigin = t.zOrigin;
15960                 rnd = 100000;
15961                 angle = t.rotationY * _DEG2RAD;
15962                 if (angle) {
15963                     cos = Math.cos(angle);
15964                     sin = Math.sin(angle);
15965                     a31 = a33*-sin;
15966                     a41 = a43*-sin;
15967                     a13 = a11*sin;
15968                     a23 = a21*sin;
15969                     a33 *= cos;
15970                     a43 *= cos;
15971                     a11 *= cos;
15972                     a21 *= cos;
15973                 }
15974                 angle = t.rotationX * _DEG2RAD;
15975                 if (angle) {
15976                     cos = Math.cos(angle);
15977                     sin = Math.sin(angle);
15978                     t1 = a12*cos+a13*sin;
15979                     t2 = a22*cos+a23*sin;
15980                     t3 = a32*cos+a33*sin;
15981                     t4 = a42*cos+a43*sin;
15982                     a13 = a12*-sin+a13*cos;
15983                     a23 = a22*-sin+a23*cos;
15984                     a33 = a32*-sin+a33*cos;
15985                     a43 = a42*-sin+a43*cos;
15986                     a12 = t1;
15987                     a22 = t2;
15988                     a32 = t3;
15989                     a42 = t4;
15990                 }
15991                 if (sz !== 1) {
15992                     a13*=sz;
15993                     a23*=sz;
15994                     a33*=sz;
15995                     a43*=sz;
15996                 }
15997                 if (sy !== 1) {
15998                     a12*=sy;
15999                     a22*=sy;
16000                     a32*=sy;
16001                     a42*=sy;
16002                 }
16003                 if (sx !== 1) {
16004                     a11*=sx;
16005                     a21*=sx;
16006                     a31*=sx;
16007                     a41*=sx;
16008                 }
16009                 if (zOrigin) {
16010                     a34 -= zOrigin;
16011                     a14 = a13*a34;
16012                     a24 = a23*a34;
16013                     a34 = a33*a34+zOrigin;
16014                 }
16015                 //we round the x, y, and z slightly differently to allow even larger values.
16016                 a14 = (t1 = (a14 += t.x) - (a14 |= 0)) ? ((t1 * rnd + (t1 < 0 ? -0.5 : 0.5)) | 0) / rnd + a14 : a14;
16017                 a24 = (t1 = (a24 += t.y) - (a24 |= 0)) ? ((t1 * rnd + (t1 < 0 ? -0.5 : 0.5)) | 0) / rnd + a24 : a24;
16018                 a34 = (t1 = (a34 += t.z) - (a34 |= 0)) ? ((t1 * rnd + (t1 < 0 ? -0.5 : 0.5)) | 0) / rnd + a34 : a34;
16019                 style[_transformProp] = "matrix3d(" + [ (((a11 * rnd) | 0) / rnd), (((a21 * rnd) | 0) / rnd), (((a31 * rnd) | 0) / rnd), (((a41 * rnd) | 0) / rnd), (((a12 * rnd) | 0) / rnd), (((a22 * rnd) | 0) / rnd), (((a32 * rnd) | 0) / rnd), (((a42 * rnd) | 0) / rnd), (((a13 * rnd) | 0) / rnd), (((a23 * rnd) | 0) / rnd), (((a33 * rnd) | 0) / rnd), (((a43 * rnd) | 0) / rnd), a14, a24, a34, (perspective ? (1 + (-a34 / perspective)) : 1) ].join(",") + ")";
16020             },
16021
16022             _set2DTransformRatio = _internals.set2DTransformRatio = function(v) {
16023                 var t = this.data, //refers to the element's _gsTransform object
16024                     targ = this.t,
16025                     style = targ.style,
16026                     ang, skew, rnd, sx, sy;
16027                 if (t.rotationX || t.rotationY || t.z || t.force3D === true || (t.force3D === "auto" && v !== 1 && v !== 0)) { //if a 3D tween begins while a 2D one is running, we need to kick the rendering over to the 3D method. For example, imagine a yoyo-ing, infinitely repeating scale tween running, and then the object gets rotated in 3D space with a different tween.
16028                     this.setRatio = _set3DTransformRatio;
16029                     _set3DTransformRatio.call(this, v);
16030                     return;
16031                 }
16032                 if (!t.rotation && !t.skewX) {
16033                     style[_transformProp] = "matrix(" + t.scaleX + ",0,0," + t.scaleY + "," + t.x + "," + t.y + ")";
16034                 } else {
16035                     ang = t.rotation * _DEG2RAD;
16036                     skew = ang - t.skewX * _DEG2RAD;
16037                     rnd = 100000;
16038                     sx = t.scaleX * rnd;
16039                     sy = t.scaleY * rnd;
16040                     //some browsers have a hard time with very small values like 2.4492935982947064e-16 (notice the "e-" towards the end) and would render the object slightly off. So we round to 5 decimal places.
16041                     style[_transformProp] = "matrix(" + (((Math.cos(ang) * sx) | 0) / rnd) + "," + (((Math.sin(ang) * sx) | 0) / rnd) + "," + (((Math.sin(skew) * -sy) | 0) / rnd) + "," + (((Math.cos(skew) * sy) | 0) / rnd) + "," + t.x + "," + t.y + ")";
16042                 }
16043             };
16044
16045         _registerComplexSpecialProp("transform,scale,scaleX,scaleY,scaleZ,x,y,z,rotation,rotationX,rotationY,rotationZ,skewX,skewY,shortRotation,shortRotationX,shortRotationY,shortRotationZ,transformOrigin,transformPerspective,directionalRotation,parseTransform,force3D,skewType", {parser:function(t, e, p, cssp, pt, plugin, vars) {
16046             if (cssp._transform) { return pt; } //only need to parse the transform once, and only if the browser supports it.
16047             var m1 = cssp._transform = _getTransform(t, _cs, true, vars.parseTransform),
16048                 style = t.style,
16049                 min = 0.000001,
16050                 i = _transformProps.length,
16051                 v = vars,
16052                 endRotations = {},
16053                 m2, skewY, copy, orig, has3D, hasChange, dr;
16054             if (typeof(v.transform) === "string" && _transformProp) { //for values like transform:"rotate(60deg) scale(0.5, 0.8)"
16055                 copy = _tempDiv.style; //don't use the original target because it might be SVG in which case some browsers don't report computed style correctly.
16056                 copy[_transformProp] = v.transform;
16057                 copy.display = "block"; //if display is "none", the browser often refuses to report the transform properties correctly.
16058                 copy.position = "absolute";
16059                 _doc.body.appendChild(_tempDiv);
16060                 m2 = _getTransform(_tempDiv, null, false);
16061                 _doc.body.removeChild(_tempDiv);
16062             } else if (typeof(v) === "object") { //for values like scaleX, scaleY, rotation, x, y, skewX, and skewY or transform:{...} (object)
16063                 m2 = {scaleX:_parseVal((v.scaleX != null) ? v.scaleX : v.scale, m1.scaleX),
16064                     scaleY:_parseVal((v.scaleY != null) ? v.scaleY : v.scale, m1.scaleY),
16065                     scaleZ:_parseVal(v.scaleZ, m1.scaleZ),
16066                     x:_parseVal(v.x, m1.x),
16067                     y:_parseVal(v.y, m1.y),
16068                     z:_parseVal(v.z, m1.z),
16069                     perspective:_parseVal(v.transformPerspective, m1.perspective)};
16070                 dr = v.directionalRotation;
16071                 if (dr != null) {
16072                     if (typeof(dr) === "object") {
16073                         for (copy in dr) {
16074                             v[copy] = dr[copy];
16075                         }
16076                     } else {
16077                         v.rotation = dr;
16078                     }
16079                 }
16080                 m2.rotation = _parseAngle(("rotation" in v) ? v.rotation : ("shortRotation" in v) ? v.shortRotation + "_short" : ("rotationZ" in v) ? v.rotationZ : m1.rotation, m1.rotation, "rotation", endRotations);
16081                 if (_supports3D) {
16082                     m2.rotationX = _parseAngle(("rotationX" in v) ? v.rotationX : ("shortRotationX" in v) ? v.shortRotationX + "_short" : m1.rotationX || 0, m1.rotationX, "rotationX", endRotations);
16083                     m2.rotationY = _parseAngle(("rotationY" in v) ? v.rotationY : ("shortRotationY" in v) ? v.shortRotationY + "_short" : m1.rotationY || 0, m1.rotationY, "rotationY", endRotations);
16084                 }
16085                 m2.skewX = (v.skewX == null) ? m1.skewX : _parseAngle(v.skewX, m1.skewX);
16086
16087                 //note: for performance reasons, we combine all skewing into the skewX and rotation values, ignoring skewY but we must still record it so that we can discern how much of the overall skew is attributed to skewX vs. skewY. Otherwise, if the skewY would always act relative (tween skewY to 10deg, for example, multiple times and if we always combine things into skewX, we can't remember that skewY was 10 from last time). Remember, a skewY of 10 degrees looks the same as a rotation of 10 degrees plus a skewX of -10 degrees.
16088                 m2.skewY = (v.skewY == null) ? m1.skewY : _parseAngle(v.skewY, m1.skewY);
16089                 if ((skewY = m2.skewY - m1.skewY)) {
16090                     m2.skewX += skewY;
16091                     m2.rotation += skewY;
16092                 }
16093             }
16094
16095             if (_supports3D && v.force3D != null) {
16096                 m1.force3D = v.force3D;
16097                 hasChange = true;
16098             }
16099
16100             m1.skewType = v.skewType || m1.skewType || CSSPlugin.defaultSkewType;
16101
16102             has3D = (m1.force3D || m1.z || m1.rotationX || m1.rotationY || m2.z || m2.rotationX || m2.rotationY || m2.perspective);
16103             if (!has3D && v.scale != null) {
16104                 m2.scaleZ = 1; //no need to tween scaleZ.
16105             }
16106
16107             while (--i > -1) {
16108                 p = _transformProps[i];
16109                 orig = m2[p] - m1[p];
16110                 if (orig > min || orig < -min || _forcePT[p] != null) {
16111                     hasChange = true;
16112                     pt = new CSSPropTween(m1, p, m1[p], orig, pt);
16113                     if (p in endRotations) {
16114                         pt.e = endRotations[p]; //directional rotations typically have compensated values during the tween, but we need to make sure they end at exactly what the user requested
16115                     }
16116                     pt.xs0 = 0; //ensures the value stays numeric in setRatio()
16117                     pt.plugin = plugin;
16118                     cssp._overwriteProps.push(pt.n);
16119                 }
16120             }
16121
16122             orig = v.transformOrigin;
16123             if (orig || (_supports3D && has3D && m1.zOrigin)) { //if anything 3D is happening and there's a transformOrigin with a z component that's non-zero, we must ensure that the transformOrigin's z-component is set to 0 so that we can manually do those calculations to get around Safari bugs. Even if the user didn't specifically define a "transformOrigin" in this particular tween (maybe they did it via css directly).
16124                 if (_transformProp) {
16125                     hasChange = true;
16126                     p = _transformOriginProp;
16127                     orig = (orig || _getStyle(t, p, _cs, false, "50% 50%")) + ""; //cast as string to avoid errors
16128                     pt = new CSSPropTween(style, p, 0, 0, pt, -1, "transformOrigin");
16129                     pt.b = style[p];
16130                     pt.plugin = plugin;
16131                     if (_supports3D) {
16132                         copy = m1.zOrigin;
16133                         orig = orig.split(" ");
16134                         m1.zOrigin = ((orig.length > 2 && !(copy !== 0 && orig[2] === "0px")) ? parseFloat(orig[2]) : copy) || 0; //Safari doesn't handle the z part of transformOrigin correctly, so we'll manually handle it in the _set3DTransformRatio() method.
16135                         pt.xs0 = pt.e = orig[0] + " " + (orig[1] || "50%") + " 0px"; //we must define a z value of 0px specifically otherwise iOS 5 Safari will stick with the old one (if one was defined)!
16136                         pt = new CSSPropTween(m1, "zOrigin", 0, 0, pt, -1, pt.n); //we must create a CSSPropTween for the _gsTransform.zOrigin so that it gets reset properly at the beginning if the tween runs backward (as opposed to just setting m1.zOrigin here)
16137                         pt.b = copy;
16138                         pt.xs0 = pt.e = m1.zOrigin;
16139                     } else {
16140                         pt.xs0 = pt.e = orig;
16141                     }
16142
16143                 //for older versions of IE (6-8), we need to manually calculate things inside the setRatio() function. We record origin x and y (ox and oy) and whether or not the values are percentages (oxp and oyp).
16144                 } else {
16145                     _parsePosition(orig + "", m1);
16146                 }
16147             }
16148
16149             if (hasChange) {
16150                 cssp._transformType = (has3D || this._transformType === 3) ? 3 : 2; //quicker than calling cssp._enableTransforms();
16151             }
16152             return pt;
16153         }, prefix:true});
16154
16155         _registerComplexSpecialProp("boxShadow", {defaultValue:"0px 0px 0px 0px #999", prefix:true, color:true, multi:true, keyword:"inset"});
16156
16157         _registerComplexSpecialProp("borderRadius", {defaultValue:"0px", parser:function(t, e, p, cssp, pt, plugin) {
16158             e = this.format(e);
16159             var props = ["borderTopLeftRadius","borderTopRightRadius","borderBottomRightRadius","borderBottomLeftRadius"],
16160                 style = t.style,
16161                 ea1, i, es2, bs2, bs, es, bn, en, w, h, esfx, bsfx, rel, hn, vn, em;
16162             w = parseFloat(t.offsetWidth);
16163             h = parseFloat(t.offsetHeight);
16164             ea1 = e.split(" ");
16165             for (i = 0; i < props.length; i++) { //if we're dealing with percentages, we must convert things separately for the horizontal and vertical axis!
16166                 if (this.p.indexOf("border")) { //older browsers used a prefix
16167                     props[i] = _checkPropPrefix(props[i]);
16168                 }
16169                 bs = bs2 = _getStyle(t, props[i], _cs, false, "0px");
16170                 if (bs.indexOf(" ") !== -1) {
16171                     bs2 = bs.split(" ");
16172                     bs = bs2[0];
16173                     bs2 = bs2[1];
16174                 }
16175                 es = es2 = ea1[i];
16176                 bn = parseFloat(bs);
16177                 bsfx = bs.substr((bn + "").length);
16178                 rel = (es.charAt(1) === "=");
16179                 if (rel) {
16180                     en = parseInt(es.charAt(0)+"1", 10);
16181                     es = es.substr(2);
16182                     en *= parseFloat(es);
16183                     esfx = es.substr((en + "").length - (en < 0 ? 1 : 0)) || "";
16184                 } else {
16185                     en = parseFloat(es);
16186                     esfx = es.substr((en + "").length);
16187                 }
16188                 if (esfx === "") {
16189                     esfx = _suffixMap[p] || bsfx;
16190                 }
16191                 if (esfx !== bsfx) {
16192                     hn = _convertToPixels(t, "borderLeft", bn, bsfx); //horizontal number (we use a bogus "borderLeft" property just because the _convertToPixels() method searches for the keywords "Left", "Right", "Top", and "Bottom" to determine of it's a horizontal or vertical property, and we need "border" in the name so that it knows it should measure relative to the element itself, not its parent.
16193                     vn = _convertToPixels(t, "borderTop", bn, bsfx); //vertical number
16194                     if (esfx === "%") {
16195                         bs = (hn / w * 100) + "%";
16196                         bs2 = (vn / h * 100) + "%";
16197                     } else if (esfx === "em") {
16198                         em = _convertToPixels(t, "borderLeft", 1, "em");
16199                         bs = (hn / em) + "em";
16200                         bs2 = (vn / em) + "em";
16201                     } else {
16202                         bs = hn + "px";
16203                         bs2 = vn + "px";
16204                     }
16205                     if (rel) {
16206                         es = (parseFloat(bs) + en) + esfx;
16207                         es2 = (parseFloat(bs2) + en) + esfx;
16208                     }
16209                 }
16210                 pt = _parseComplex(style, props[i], bs + " " + bs2, es + " " + es2, false, "0px", pt);
16211             }
16212             return pt;
16213         }, prefix:true, formatter:_getFormatter("0px 0px 0px 0px", false, true)});
16214         _registerComplexSpecialProp("backgroundPosition", {defaultValue:"0 0", parser:function(t, e, p, cssp, pt, plugin) {
16215             var bp = "background-position",
16216                 cs = (_cs || _getComputedStyle(t, null)),
16217                 bs = this.format( ((cs) ? _ieVers ? cs.getPropertyValue(bp + "-x") + " " + cs.getPropertyValue(bp + "-y") : cs.getPropertyValue(bp) : t.currentStyle.backgroundPositionX + " " + t.currentStyle.backgroundPositionY) || "0 0"), //Internet Explorer doesn't report background-position correctly - we must query background-position-x and background-position-y and combine them (even in IE10). Before IE9, we must do the same with the currentStyle object and use camelCase
16218                 es = this.format(e),
16219                 ba, ea, i, pct, overlap, src;
16220             if ((bs.indexOf("%") !== -1) !== (es.indexOf("%") !== -1)) {
16221                 src = _getStyle(t, "backgroundImage").replace(_urlExp, "");
16222                 if (src && src !== "none") {
16223                     ba = bs.split(" ");
16224                     ea = es.split(" ");
16225                     _tempImg.setAttribute("src", src); //set the temp <img>'s src to the background-image so that we can measure its width/height
16226                     i = 2;
16227                     while (--i > -1) {
16228                         bs = ba[i];
16229                         pct = (bs.indexOf("%") !== -1);
16230                         if (pct !== (ea[i].indexOf("%") !== -1)) {
16231                             overlap = (i === 0) ? t.offsetWidth - _tempImg.width : t.offsetHeight - _tempImg.height;
16232                             ba[i] = pct ? (parseFloat(bs) / 100 * overlap) + "px" : (parseFloat(bs) / overlap * 100) + "%";
16233                         }
16234                     }
16235                     bs = ba.join(" ");
16236                 }
16237             }
16238             return this.parseComplex(t.style, bs, es, pt, plugin);
16239         }, formatter:_parsePosition});
16240         _registerComplexSpecialProp("backgroundSize", {defaultValue:"0 0", formatter:_parsePosition});
16241         _registerComplexSpecialProp("perspective", {defaultValue:"0px", prefix:true});
16242         _registerComplexSpecialProp("perspectiveOrigin", {defaultValue:"50% 50%", prefix:true});
16243         _registerComplexSpecialProp("transformStyle", {prefix:true});
16244         _registerComplexSpecialProp("backfaceVisibility", {prefix:true});
16245         _registerComplexSpecialProp("userSelect", {prefix:true});
16246         _registerComplexSpecialProp("margin", {parser:_getEdgeParser("marginTop,marginRight,marginBottom,marginLeft")});
16247         _registerComplexSpecialProp("padding", {parser:_getEdgeParser("paddingTop,paddingRight,paddingBottom,paddingLeft")});
16248         _registerComplexSpecialProp("clip", {defaultValue:"rect(0px,0px,0px,0px)", parser:function(t, e, p, cssp, pt, plugin){
16249             var b, cs, delim;
16250             if (_ieVers < 9) { //IE8 and earlier don't report a "clip" value in the currentStyle - instead, the values are split apart into clipTop, clipRight, clipBottom, and clipLeft. Also, in IE7 and earlier, the values inside rect() are space-delimited, not comma-delimited.
16251                 cs = t.currentStyle;
16252                 delim = _ieVers < 8 ? " " : ",";
16253                 b = "rect(" + cs.clipTop + delim + cs.clipRight + delim + cs.clipBottom + delim + cs.clipLeft + ")";
16254                 e = this.format(e).split(",").join(delim);
16255             } else {
16256                 b = this.format(_getStyle(t, this.p, _cs, false, this.dflt));
16257                 e = this.format(e);
16258             }
16259             return this.parseComplex(t.style, b, e, pt, plugin);
16260         }});
16261         _registerComplexSpecialProp("textShadow", {defaultValue:"0px 0px 0px #999", color:true, multi:true});
16262         _registerComplexSpecialProp("autoRound,strictUnits", {parser:function(t, e, p, cssp, pt) {return pt;}}); //just so that we can ignore these properties (not tween them)
16263         _registerComplexSpecialProp("border", {defaultValue:"0px solid #000", parser:function(t, e, p, cssp, pt, plugin) {
16264                 return this.parseComplex(t.style, this.format(_getStyle(t, "borderTopWidth", _cs, false, "0px") + " " + _getStyle(t, "borderTopStyle", _cs, false, "solid") + " " + _getStyle(t, "borderTopColor", _cs, false, "#000")), this.format(e), pt, plugin);
16265             }, color:true, formatter:function(v) {
16266                 var a = v.split(" ");
16267                 return a[0] + " " + (a[1] || "solid") + " " + (v.match(_colorExp) || ["#000"])[0];
16268             }});
16269         _registerComplexSpecialProp("borderWidth", {parser:_getEdgeParser("borderTopWidth,borderRightWidth,borderBottomWidth,borderLeftWidth")}); //Firefox doesn't pick up on borderWidth set in style sheets (only inline).
16270         _registerComplexSpecialProp("float,cssFloat,styleFloat", {parser:function(t, e, p, cssp, pt, plugin) {
16271             var s = t.style,
16272                 prop = ("cssFloat" in s) ? "cssFloat" : "styleFloat";
16273             return new CSSPropTween(s, prop, 0, 0, pt, -1, p, false, 0, s[prop], e);
16274         }});
16275
16276         //opacity-related
16277         var _setIEOpacityRatio = function(v) {
16278                 var t = this.t, //refers to the element's style property
16279                     filters = t.filter || _getStyle(this.data, "filter"),
16280                     val = (this.s + this.c * v) | 0,
16281                     skip;
16282                 if (val === 100) { //for older versions of IE that need to use a filter to apply opacity, we should remove the filter if opacity hits 1 in order to improve performance, but make sure there isn't a transform (matrix) or gradient in the filters.
16283                     if (filters.indexOf("atrix(") === -1 && filters.indexOf("radient(") === -1 && filters.indexOf("oader(") === -1) {
16284                         t.removeAttribute("filter");
16285                         skip = (!_getStyle(this.data, "filter")); //if a class is applied that has an alpha filter, it will take effect (we don't want that), so re-apply our alpha filter in that case. We must first remove it and then check.
16286                     } else {
16287                         t.filter = filters.replace(_alphaFilterExp, "");
16288                         skip = true;
16289                     }
16290                 }
16291                 if (!skip) {
16292                     if (this.xn1) {
16293                         t.filter = filters = filters || ("alpha(opacity=" + val + ")"); //works around bug in IE7/8 that prevents changes to "visibility" from being applied properly if the filter is changed to a different alpha on the same frame.
16294                     }
16295                     if (filters.indexOf("pacity") === -1) { //only used if browser doesn't support the standard opacity style property (IE 7 and 8). We omit the "O" to avoid case-sensitivity issues
16296                         if (val !== 0 || !this.xn1) { //bugs in IE7/8 won't render the filter properly if opacity is ADDED on the same frame/render as "visibility" changes (this.xn1 is 1 if this tween is an "autoAlpha" tween)
16297                             t.filter = filters + " alpha(opacity=" + val + ")"; //we round the value because otherwise, bugs in IE7/8 can prevent "visibility" changes from being applied properly.
16298                         }
16299                     } else {
16300                         t.filter = filters.replace(_opacityExp, "opacity=" + val);
16301                     }
16302                 }
16303             };
16304         _registerComplexSpecialProp("opacity,alpha,autoAlpha", {defaultValue:"1", parser:function(t, e, p, cssp, pt, plugin) {
16305             var b = parseFloat(_getStyle(t, "opacity", _cs, false, "1")),
16306                 style = t.style,
16307                 isAutoAlpha = (p === "autoAlpha");
16308             if (typeof(e) === "string" && e.charAt(1) === "=") {
16309                 e = ((e.charAt(0) === "-") ? -1 : 1) * parseFloat(e.substr(2)) + b;
16310             }
16311             if (isAutoAlpha && b === 1 && _getStyle(t, "visibility", _cs) === "hidden" && e !== 0) { //if visibility is initially set to "hidden", we should interpret that as intent to make opacity 0 (a convenience)
16312                 b = 0;
16313             }
16314             if (_supportsOpacity) {
16315                 pt = new CSSPropTween(style, "opacity", b, e - b, pt);
16316             } else {
16317                 pt = new CSSPropTween(style, "opacity", b * 100, (e - b) * 100, pt);
16318                 pt.xn1 = isAutoAlpha ? 1 : 0; //we need to record whether or not this is an autoAlpha so that in the setRatio(), we know to duplicate the setting of the alpha in order to work around a bug in IE7 and IE8 that prevents changes to "visibility" from taking effect if the filter is changed to a different alpha(opacity) at the same time. Setting it to the SAME value first, then the new value works around the IE7/8 bug.
16319                 style.zoom = 1; //helps correct an IE issue.
16320                 pt.type = 2;
16321                 pt.b = "alpha(opacity=" + pt.s + ")";
16322                 pt.e = "alpha(opacity=" + (pt.s + pt.c) + ")";
16323                 pt.data = t;
16324                 pt.plugin = plugin;
16325                 pt.setRatio = _setIEOpacityRatio;
16326             }
16327             if (isAutoAlpha) { //we have to create the "visibility" PropTween after the opacity one in the linked list so that they run in the order that works properly in IE8 and earlier
16328                 pt = new CSSPropTween(style, "visibility", 0, 0, pt, -1, null, false, 0, ((b !== 0) ? "inherit" : "hidden"), ((e === 0) ? "hidden" : "inherit"));
16329                 pt.xs0 = "inherit";
16330                 cssp._overwriteProps.push(pt.n);
16331                 cssp._overwriteProps.push(p);
16332             }
16333             return pt;
16334         }});
16335
16336
16337         var _removeProp = function(s, p) {
16338                 if (p) {
16339                     if (s.removeProperty) {
16340                         if (p.substr(0,2) === "ms") { //Microsoft browsers don't conform to the standard of capping the first prefix character, so we adjust so that when we prefix the caps with a dash, it's correct (otherwise it'd be "ms-transform" instead of "-ms-transform" for IE9, for example)
16341                             p = "M" + p.substr(1);
16342                         }
16343                         s.removeProperty(p.replace(_capsExp, "-$1").toLowerCase());
16344                     } else { //note: old versions of IE use "removeAttribute()" instead of "removeProperty()"
16345                         s.removeAttribute(p);
16346                     }
16347                 }
16348             },
16349             _setClassNameRatio = function(v) {
16350                 this.t._gsClassPT = this;
16351                 if (v === 1 || v === 0) {
16352                     this.t.setAttribute("class", (v === 0) ? this.b : this.e);
16353                     var mpt = this.data, //first MiniPropTween
16354                         s = this.t.style;
16355                     while (mpt) {
16356                         if (!mpt.v) {
16357                             _removeProp(s, mpt.p);
16358                         } else {
16359                             s[mpt.p] = mpt.v;
16360                         }
16361                         mpt = mpt._next;
16362                     }
16363                     if (v === 1 && this.t._gsClassPT === this) {
16364                         this.t._gsClassPT = null;
16365                     }
16366                 } else if (this.t.getAttribute("class") !== this.e) {
16367                     this.t.setAttribute("class", this.e);
16368                 }
16369             };
16370         _registerComplexSpecialProp("className", {parser:function(t, e, p, cssp, pt, plugin, vars) {
16371             var b = t.getAttribute("class") || "", //don't use t.className because it doesn't work consistently on SVG elements; getAttribute("class") and setAttribute("class", value") is more reliable.
16372                 cssText = t.style.cssText,
16373                 difData, bs, cnpt, cnptLookup, mpt;
16374             pt = cssp._classNamePT = new CSSPropTween(t, p, 0, 0, pt, 2);
16375             pt.setRatio = _setClassNameRatio;
16376             pt.pr = -11;
16377             _hasPriority = true;
16378             pt.b = b;
16379             bs = _getAllStyles(t, _cs);
16380             //if there's a className tween already operating on the target, force it to its end so that the necessary inline styles are removed and the class name is applied before we determine the end state (we don't want inline styles interfering that were there just for class-specific values)
16381             cnpt = t._gsClassPT;
16382             if (cnpt) {
16383                 cnptLookup = {};
16384                 mpt = cnpt.data; //first MiniPropTween which stores the inline styles - we need to force these so that the inline styles don't contaminate things. Otherwise, there's a small chance that a tween could start and the inline values match the destination values and they never get cleaned.
16385                 while (mpt) {
16386                     cnptLookup[mpt.p] = 1;
16387                     mpt = mpt._next;
16388                 }
16389                 cnpt.setRatio(1);
16390             }
16391             t._gsClassPT = pt;
16392             pt.e = (e.charAt(1) !== "=") ? e : b.replace(new RegExp("\\s*\\b" + e.substr(2) + "\\b"), "") + ((e.charAt(0) === "+") ? " " + e.substr(2) : "");
16393             if (cssp._tween._duration) { //if it's a zero-duration tween, there's no need to tween anything or parse the data. In fact, if we switch classes temporarily (which we must do for proper parsing) and the class has a transition applied, it could cause a quick flash to the end state and back again initially in some browsers.
16394                 t.setAttribute("class", pt.e);
16395                 difData = _cssDif(t, bs, _getAllStyles(t), vars, cnptLookup);
16396                 t.setAttribute("class", b);
16397                 pt.data = difData.firstMPT;
16398                 t.style.cssText = cssText; //we recorded cssText before we swapped classes and ran _getAllStyles() because in cases when a className tween is overwritten, we remove all the related tweening properties from that class change (otherwise class-specific stuff can't override properties we've directly set on the target's style object due to specificity).
16399                 pt = pt.xfirst = cssp.parse(t, difData.difs, pt, plugin); //we record the CSSPropTween as the xfirst so that we can handle overwriting propertly (if "className" gets overwritten, we must kill all the properties associated with the className part of the tween, so we can loop through from xfirst to the pt itself)
16400             }
16401             return pt;
16402         }});
16403
16404
16405         var _setClearPropsRatio = function(v) {
16406             if (v === 1 || v === 0) if (this.data._totalTime === this.data._totalDuration && this.data.data !== "isFromStart") { //this.data refers to the tween. Only clear at the END of the tween (remember, from() tweens make the ratio go from 1 to 0, so we can't just check that and if the tween is the zero-duration one that's created internally to render the starting values in a from() tween, ignore that because otherwise, for example, from(...{height:100, clearProps:"height", delay:1}) would wipe the height at the beginning of the tween and after 1 second, it'd kick back in).
16407                 var s = this.t.style,
16408                     transformParse = _specialProps.transform.parse,
16409                     a, p, i, clearTransform;
16410                 if (this.e === "all") {
16411                     s.cssText = "";
16412                     clearTransform = true;
16413                 } else {
16414                     a = this.e.split(",");
16415                     i = a.length;
16416                     while (--i > -1) {
16417                         p = a[i];
16418                         if (_specialProps[p]) {
16419                             if (_specialProps[p].parse === transformParse) {
16420                                 clearTransform = true;
16421                             } else {
16422                                 p = (p === "transformOrigin") ? _transformOriginProp : _specialProps[p].p; //ensures that special properties use the proper browser-specific property name, like "scaleX" might be "-webkit-transform" or "boxShadow" might be "-moz-box-shadow"
16423                             }
16424                         }
16425                         _removeProp(s, p);
16426                     }
16427                 }
16428                 if (clearTransform) {
16429                     _removeProp(s, _transformProp);
16430                     if (this.t._gsTransform) {
16431                         delete this.t._gsTransform;
16432                     }
16433                 }
16434
16435             }
16436         };
16437         _registerComplexSpecialProp("clearProps", {parser:function(t, e, p, cssp, pt) {
16438             pt = new CSSPropTween(t, p, 0, 0, pt, 2);
16439             pt.setRatio = _setClearPropsRatio;
16440             pt.e = e;
16441             pt.pr = -10;
16442             pt.data = cssp._tween;
16443             _hasPriority = true;
16444             return pt;
16445         }});
16446
16447         p = "bezier,throwProps,physicsProps,physics2D".split(",");
16448         i = p.length;
16449         while (i--) {
16450             _registerPluginProp(p[i]);
16451         }
16452
16453
16454
16455
16456
16457
16458
16459
16460         p = CSSPlugin.prototype;
16461         p._firstPT = null;
16462
16463         //gets called when the tween renders for the first time. This kicks everything off, recording start/end values, etc.
16464         p._onInitTween = function(target, vars, tween) {
16465             if (!target.nodeType) { //css is only for dom elements
16466                 return false;
16467             }
16468             this._target = target;
16469             this._tween = tween;
16470             this._vars = vars;
16471             _autoRound = vars.autoRound;
16472             _hasPriority = false;
16473             _suffixMap = vars.suffixMap || CSSPlugin.suffixMap;
16474             _cs = _getComputedStyle(target, "");
16475             _overwriteProps = this._overwriteProps;
16476             var style = target.style,
16477                 v, pt, pt2, first, last, next, zIndex, tpt, threeD;
16478             if (_reqSafariFix) if (style.zIndex === "") {
16479                 v = _getStyle(target, "zIndex", _cs);
16480                 if (v === "auto" || v === "") {
16481                     //corrects a bug in [non-Android] Safari that prevents it from repainting elements in their new positions if they don't have a zIndex set. We also can't just apply this inside _parseTransform() because anything that's moved in any way (like using "left" or "top" instead of transforms like "x" and "y") can be affected, so it is best to ensure that anything that's tweening has a z-index. Setting "WebkitPerspective" to a non-zero value worked too except that on iOS Safari things would flicker randomly. Plus zIndex is less memory-intensive.
16482                     this._addLazySet(style, "zIndex", 0);
16483                 }
16484             }
16485
16486             if (typeof(vars) === "string") {
16487                 first = style.cssText;
16488                 v = _getAllStyles(target, _cs);
16489                 style.cssText = first + ";" + vars;
16490                 v = _cssDif(target, v, _getAllStyles(target)).difs;
16491                 if (!_supportsOpacity && _opacityValExp.test(vars)) {
16492                     v.opacity = parseFloat( RegExp.$1 );
16493                 }
16494                 vars = v;
16495                 style.cssText = first;
16496             }
16497             this._firstPT = pt = this.parse(target, vars, null);
16498
16499             if (this._transformType) {
16500                 threeD = (this._transformType === 3);
16501                 if (!_transformProp) {
16502                     style.zoom = 1; //helps correct an IE issue.
16503                 } else if (_isSafari) {
16504                     _reqSafariFix = true;
16505                     //if zIndex isn't set, iOS Safari doesn't repaint things correctly sometimes (seemingly at random).
16506                     if (style.zIndex === "") {
16507                         zIndex = _getStyle(target, "zIndex", _cs);
16508                         if (zIndex === "auto" || zIndex === "") {
16509                             this._addLazySet(style, "zIndex", 0);
16510                         }
16511                     }
16512                     //Setting WebkitBackfaceVisibility corrects 3 bugs:
16513                     // 1) [non-Android] Safari skips rendering changes to "top" and "left" that are made on the same frame/render as a transform update.
16514                     // 2) iOS Safari sometimes neglects to repaint elements in their new positions. Setting "WebkitPerspective" to a non-zero value worked too except that on iOS Safari things would flicker randomly.
16515                     // 3) Safari sometimes displayed odd artifacts when tweening the transform (or WebkitTransform) property, like ghosts of the edges of the element remained. Definitely a browser bug.
16516                     //Note: we allow the user to override the auto-setting by defining WebkitBackfaceVisibility in the vars of the tween.
16517                     if (_isSafariLT6) {
16518                         this._addLazySet(style, "WebkitBackfaceVisibility", this._vars.WebkitBackfaceVisibility || (threeD ? "visible" : "hidden"));
16519                     }
16520                 }
16521                 pt2 = pt;
16522                 while (pt2 && pt2._next) {
16523                     pt2 = pt2._next;
16524                 }
16525                 tpt = new CSSPropTween(target, "transform", 0, 0, null, 2);
16526                 this._linkCSSP(tpt, null, pt2);
16527                 tpt.setRatio = (threeD && _supports3D) ? _set3DTransformRatio : _transformProp ? _set2DTransformRatio : _setIETransformRatio;
16528                 tpt.data = this._transform || _getTransform(target, _cs, true);
16529                 _overwriteProps.pop(); //we don't want to force the overwrite of all "transform" tweens of the target - we only care about individual transform properties like scaleX, rotation, etc. The CSSPropTween constructor automatically adds the property to _overwriteProps which is why we need to pop() here.
16530             }
16531
16532             if (_hasPriority) {
16533                 //reorders the linked list in order of pr (priority)
16534                 while (pt) {
16535                     next = pt._next;
16536                     pt2 = first;
16537                     while (pt2 && pt2.pr > pt.pr) {
16538                         pt2 = pt2._next;
16539                     }
16540                     if ((pt._prev = pt2 ? pt2._prev : last)) {
16541                         pt._prev._next = pt;
16542                     } else {
16543                         first = pt;
16544                     }
16545                     if ((pt._next = pt2)) {
16546                         pt2._prev = pt;
16547                     } else {
16548                         last = pt;
16549                     }
16550                     pt = next;
16551                 }
16552                 this._firstPT = first;
16553             }
16554             return true;
16555         };
16556
16557
16558         p.parse = function(target, vars, pt, plugin) {
16559             var style = target.style,
16560                 p, sp, bn, en, bs, es, bsfx, esfx, isStr, rel;
16561             for (p in vars) {
16562                 es = vars[p]; //ending value string
16563                 sp = _specialProps[p]; //SpecialProp lookup.
16564                 if (sp) {
16565                     pt = sp.parse(target, es, p, this, pt, plugin, vars);
16566
16567                 } else {
16568                     bs = _getStyle(target, p, _cs) + "";
16569                     isStr = (typeof(es) === "string");
16570                     if (p === "color" || p === "fill" || p === "stroke" || p.indexOf("Color") !== -1 || (isStr && _rgbhslExp.test(es))) { //Opera uses background: to define color sometimes in addition to backgroundColor:
16571                         if (!isStr) {
16572                             es = _parseColor(es);
16573                             es = ((es.length > 3) ? "rgba(" : "rgb(") + es.join(",") + ")";
16574                         }
16575                         pt = _parseComplex(style, p, bs, es, true, "transparent", pt, 0, plugin);
16576
16577                     } else if (isStr && (es.indexOf(" ") !== -1 || es.indexOf(",") !== -1)) {
16578                         pt = _parseComplex(style, p, bs, es, true, null, pt, 0, plugin);
16579
16580                     } else {
16581                         bn = parseFloat(bs);
16582                         bsfx = (bn || bn === 0) ? bs.substr((bn + "").length) : ""; //remember, bs could be non-numeric like "normal" for fontWeight, so we should default to a blank suffix in that case.
16583
16584                         if (bs === "" || bs === "auto") {
16585                             if (p === "width" || p === "height") {
16586                                 bn = _getDimension(target, p, _cs);
16587                                 bsfx = "px";
16588                             } else if (p === "left" || p === "top") {
16589                                 bn = _calculateOffset(target, p, _cs);
16590                                 bsfx = "px";
16591                             } else {
16592                                 bn = (p !== "opacity") ? 0 : 1;
16593                                 bsfx = "";
16594                             }
16595                         }
16596
16597                         rel = (isStr && es.charAt(1) === "=");
16598                         if (rel) {
16599                             en = parseInt(es.charAt(0) + "1", 10);
16600                             es = es.substr(2);
16601                             en *= parseFloat(es);
16602                             esfx = es.replace(_suffixExp, "");
16603                         } else {
16604                             en = parseFloat(es);
16605                             esfx = isStr ? es.substr((en + "").length) || "" : "";
16606                         }
16607
16608                         if (esfx === "") {
16609                             esfx = (p in _suffixMap) ? _suffixMap[p] : bsfx; //populate the end suffix, prioritizing the map, then if none is found, use the beginning suffix.
16610                         }
16611
16612                         es = (en || en === 0) ? (rel ? en + bn : en) + esfx : vars[p]; //ensures that any += or -= prefixes are taken care of. Record the end value before normalizing the suffix because we always want to end the tween on exactly what they intended even if it doesn't match the beginning value's suffix.
16613
16614                         //if the beginning/ending suffixes don't match, normalize them...
16615                         if (bsfx !== esfx) if (esfx !== "") if (en || en === 0) if (bn) { //note: if the beginning value (bn) is 0, we don't need to convert units!
16616                             bn = _convertToPixels(target, p, bn, bsfx);
16617                             if (esfx === "%") {
16618                                 bn /= _convertToPixels(target, p, 100, "%") / 100;
16619                                 if (vars.strictUnits !== true) { //some browsers report only "px" values instead of allowing "%" with getComputedStyle(), so we assume that if we're tweening to a %, we should start there too unless strictUnits:true is defined. This approach is particularly useful for responsive designs that use from() tweens.
16620                                     bs = bn + "%";
16621                                 }
16622
16623                             } else if (esfx === "em") {
16624                                 bn /= _convertToPixels(target, p, 1, "em");
16625
16626                             //otherwise convert to pixels.
16627                             } else if (esfx !== "px") {
16628                                 en = _convertToPixels(target, p, en, esfx);
16629                                 esfx = "px"; //we don't use bsfx after this, so we don't need to set it to px too.
16630                             }
16631                             if (rel) if (en || en === 0) {
16632                                 es = (en + bn) + esfx; //the changes we made affect relative calculations, so adjust the end value here.
16633                             }
16634                         }
16635
16636                         if (rel) {
16637                             en += bn;
16638                         }
16639
16640                         if ((bn || bn === 0) && (en || en === 0)) { //faster than isNaN(). Also, previously we required en !== bn but that doesn't really gain much performance and it prevents _parseToProxy() from working properly if beginning and ending values match but need to get tweened by an external plugin anyway. For example, a bezier tween where the target starts at left:0 and has these points: [{left:50},{left:0}] wouldn't work properly because when parsing the last point, it'd match the first (current) one and a non-tweening CSSPropTween would be recorded when we actually need a normal tween (type:0) so that things get updated during the tween properly.
16641                             pt = new CSSPropTween(style, p, bn, en - bn, pt, 0, p, (_autoRound !== false && (esfx === "px" || p === "zIndex")), 0, bs, es);
16642                             pt.xs0 = esfx;
16643                             //DEBUG: _log("tween "+p+" from "+pt.b+" ("+bn+esfx+") to "+pt.e+" with suffix: "+pt.xs0);
16644                         } else if (style[p] === undefined || !es && (es + "" === "NaN" || es == null)) {
16645                             _log("invalid " + p + " tween value: " + vars[p]);
16646                         } else {
16647                             pt = new CSSPropTween(style, p, en || bn || 0, 0, pt, -1, p, false, 0, bs, es);
16648                             pt.xs0 = (es === "none" && (p === "display" || p.indexOf("Style") !== -1)) ? bs : es; //intermediate value should typically be set immediately (end value) except for "display" or things like borderTopStyle, borderBottomStyle, etc. which should use the beginning value during the tween.
16649                             //DEBUG: _log("non-tweening value "+p+": "+pt.xs0);
16650                         }
16651                     }
16652                 }
16653                 if (plugin) if (pt && !pt.plugin) {
16654                     pt.plugin = plugin;
16655                 }
16656             }
16657             return pt;
16658         };
16659
16660
16661         //gets called every time the tween updates, passing the new ratio (typically a value between 0 and 1, but not always (for example, if an Elastic.easeOut is used, the value can jump above 1 mid-tween). It will always start and 0 and end at 1.
16662         p.setRatio = function(v) {
16663             var pt = this._firstPT,
16664                 min = 0.000001,
16665                 val, str, i;
16666
16667             //at the end of the tween, we set the values to exactly what we received in order to make sure non-tweening values (like "position" or "float" or whatever) are set and so that if the beginning/ending suffixes (units) didn't match and we normalized to px, the value that the user passed in is used here. We check to see if the tween is at its beginning in case it's a from() tween in which case the ratio will actually go from 1 to 0 over the course of the tween (backwards).
16668             if (v === 1 && (this._tween._time === this._tween._duration || this._tween._time === 0)) {
16669                 while (pt) {
16670                     if (pt.type !== 2) {
16671                         pt.t[pt.p] = pt.e;
16672                     } else {
16673                         pt.setRatio(v);
16674                     }
16675                     pt = pt._next;
16676                 }
16677
16678             } else if (v || !(this._tween._time === this._tween._duration || this._tween._time === 0) || this._tween._rawPrevTime === -0.000001) {
16679                 while (pt) {
16680                     val = pt.c * v + pt.s;
16681                     if (pt.r) {
16682                         val = Math.round(val);
16683                     } else if (val < min) if (val > -min) {
16684                         val = 0;
16685                     }
16686                     if (!pt.type) {
16687                         pt.t[pt.p] = val + pt.xs0;
16688                     } else if (pt.type === 1) { //complex value (one that typically has multiple numbers inside a string, like "rect(5px,10px,20px,25px)"
16689                         i = pt.l;
16690                         if (i === 2) {
16691                             pt.t[pt.p] = pt.xs0 + val + pt.xs1 + pt.xn1 + pt.xs2;
16692                         } else if (i === 3) {
16693                             pt.t[pt.p] = pt.xs0 + val + pt.xs1 + pt.xn1 + pt.xs2 + pt.xn2 + pt.xs3;
16694                         } else if (i === 4) {
16695                             pt.t[pt.p] = pt.xs0 + val + pt.xs1 + pt.xn1 + pt.xs2 + pt.xn2 + pt.xs3 + pt.xn3 + pt.xs4;
16696                         } else if (i === 5) {
16697                             pt.t[pt.p] = pt.xs0 + val + pt.xs1 + pt.xn1 + pt.xs2 + pt.xn2 + pt.xs3 + pt.xn3 + pt.xs4 + pt.xn4 + pt.xs5;
16698                         } else {
16699                             str = pt.xs0 + val + pt.xs1;
16700                             for (i = 1; i < pt.l; i++) {
16701                                 str += pt["xn"+i] + pt["xs"+(i+1)];
16702                             }
16703                             pt.t[pt.p] = str;
16704                         }
16705
16706                     } else if (pt.type === -1) { //non-tweening value
16707                         pt.t[pt.p] = pt.xs0;
16708
16709                     } else if (pt.setRatio) { //custom setRatio() for things like SpecialProps, external plugins, etc.
16710                         pt.setRatio(v);
16711                     }
16712                     pt = pt._next;
16713                 }
16714
16715             //if the tween is reversed all the way back to the beginning, we need to restore the original values which may have different units (like % instead of px or em or whatever).
16716             } else {
16717                 while (pt) {
16718                     if (pt.type !== 2) {
16719                         pt.t[pt.p] = pt.b;
16720                     } else {
16721                         pt.setRatio(v);
16722                     }
16723                     pt = pt._next;
16724                 }
16725             }
16726         };
16727
16728         /**
16729          * @private
16730          * Forces rendering of the target's transforms (rotation, scale, etc.) whenever the CSSPlugin's setRatio() is called.
16731          * Basically, this tells the CSSPlugin to create a CSSPropTween (type 2) after instantiation that runs last in the linked
16732          * list and calls the appropriate (3D or 2D) rendering function. We separate this into its own method so that we can call
16733          * it from other plugins like BezierPlugin if, for example, it needs to apply an autoRotation and this CSSPlugin
16734          * doesn't have any transform-related properties of its own. You can call this method as many times as you
16735          * want and it won't create duplicate CSSPropTweens.
16736          *
16737          * @param {boolean} threeD if true, it should apply 3D tweens (otherwise, just 2D ones are fine and typically faster)
16738          */
16739         p._enableTransforms = function(threeD) {
16740             this._transformType = (threeD || this._transformType === 3) ? 3 : 2;
16741             this._transform = this._transform || _getTransform(this._target, _cs, true); //ensures that the element has a _gsTransform property with the appropriate values.
16742         };
16743
16744         var lazySet = function(v) {
16745             this.t[this.p] = this.e;
16746             this.data._linkCSSP(this, this._next, null, true); //we purposefully keep this._next even though it'd make sense to null it, but this is a performance optimization, as this happens during the while (pt) {} loop in setRatio() at the bottom of which it sets pt = pt._next, so if we null it, the linked list will be broken in that loop.
16747         };
16748         /** @private Gives us a way to set a value on the first render (and only the first render). **/
16749         p._addLazySet = function(t, p, v) {
16750             var pt = this._firstPT = new CSSPropTween(t, p, 0, 0, this._firstPT, 2);
16751             pt.e = v;
16752             pt.setRatio = lazySet;
16753             pt.data = this;
16754         };
16755
16756         /** @private **/
16757         p._linkCSSP = function(pt, next, prev, remove) {
16758             if (pt) {
16759                 if (next) {
16760                     next._prev = pt;
16761                 }
16762                 if (pt._next) {
16763                     pt._next._prev = pt._prev;
16764                 }
16765                 if (pt._prev) {
16766                     pt._prev._next = pt._next;
16767                 } else if (this._firstPT === pt) {
16768                     this._firstPT = pt._next;
16769                     remove = true; //just to prevent resetting this._firstPT 5 lines down in case pt._next is null. (optimized for speed)
16770                 }
16771                 if (prev) {
16772                     prev._next = pt;
16773                 } else if (!remove && this._firstPT === null) {
16774                     this._firstPT = pt;
16775                 }
16776                 pt._next = next;
16777                 pt._prev = prev;
16778             }
16779             return pt;
16780         };
16781
16782         //we need to make sure that if alpha or autoAlpha is killed, opacity is too. And autoAlpha affects the "visibility" property.
16783         p._kill = function(lookup) {
16784             var copy = lookup,
16785                 pt, p, xfirst;
16786             if (lookup.autoAlpha || lookup.alpha) {
16787                 copy = {};
16788                 for (p in lookup) { //copy the lookup so that we're not changing the original which may be passed elsewhere.
16789                     copy[p] = lookup[p];
16790                 }
16791                 copy.opacity = 1;
16792                 if (copy.autoAlpha) {
16793                     copy.visibility = 1;
16794                 }
16795             }
16796             if (lookup.className && (pt = this._classNamePT)) { //for className tweens, we need to kill any associated CSSPropTweens too; a linked list starts at the className's "xfirst".
16797                 xfirst = pt.xfirst;
16798                 if (xfirst && xfirst._prev) {
16799                     this._linkCSSP(xfirst._prev, pt._next, xfirst._prev._prev); //break off the prev
16800                 } else if (xfirst === this._firstPT) {
16801                     this._firstPT = pt._next;
16802                 }
16803                 if (pt._next) {
16804                     this._linkCSSP(pt._next, pt._next._next, xfirst._prev);
16805                 }
16806                 this._classNamePT = null;
16807             }
16808             return TweenPlugin.prototype._kill.call(this, copy);
16809         };
16810
16811
16812
16813         //used by cascadeTo() for gathering all the style properties of each child element into an array for comparison.
16814         var _getChildStyles = function(e, props, targets) {
16815                 var children, i, child, type;
16816                 if (e.slice) {
16817                     i = e.length;
16818                     while (--i > -1) {
16819                         _getChildStyles(e[i], props, targets);
16820                     }
16821                     return;
16822                 }
16823                 children = e.childNodes;
16824                 i = children.length;
16825                 while (--i > -1) {
16826                     child = children[i];
16827                     type = child.type;
16828                     if (child.style) {
16829                         props.push(_getAllStyles(child));
16830                         if (targets) {
16831                             targets.push(child);
16832                         }
16833                     }
16834                     if ((type === 1 || type === 9 || type === 11) && child.childNodes.length) {
16835                         _getChildStyles(child, props, targets);
16836                     }
16837                 }
16838             };
16839
16840         /**
16841          * Typically only useful for className tweens that may affect child elements, this method creates a TweenLite
16842          * and then compares the style properties of all the target's child elements at the tween's start and end, and
16843          * if any are different, it also creates tweens for those and returns an array containing ALL of the resulting
16844          * tweens (so that you can easily add() them to a TimelineLite, for example). The reason this functionality is
16845          * wrapped into a separate static method of CSSPlugin instead of being integrated into all regular className tweens
16846          * is because it creates entirely new tweens that may have completely different targets than the original tween,
16847          * so if they were all lumped into the original tween instance, it would be inconsistent with the rest of the API
16848          * and it would create other problems. For example:
16849          *  - If I create a tween of elementA, that tween instance may suddenly change its target to include 50 other elements (unintuitive if I specifically defined the target I wanted)
16850          *  - We can't just create new independent tweens because otherwise, what happens if the original/parent tween is reversed or pause or dropped into a TimelineLite for tight control? You'd expect that tween's behavior to affect all the others.
16851          *  - Analyzing every style property of every child before and after the tween is an expensive operation when there are many children, so this behavior shouldn't be imposed on all className tweens by default, especially since it's probably rare that this extra functionality is needed.
16852          *
16853          * @param {Object} target object to be tweened
16854          * @param {number} Duration in seconds (or frames for frames-based tweens)
16855          * @param {Object} Object containing the end values, like {className:"newClass", ease:Linear.easeNone}
16856          * @return {Array} An array of TweenLite instances
16857          */
16858         CSSPlugin.cascadeTo = function(target, duration, vars) {
16859             var tween = TweenLite.to(target, duration, vars),
16860                 results = [tween],
16861                 b = [],
16862                 e = [],
16863                 targets = [],
16864                 _reservedProps = TweenLite._internals.reservedProps,
16865                 i, difs, p;
16866             target = tween._targets || tween.target;
16867             _getChildStyles(target, b, targets);
16868             tween.render(duration, true);
16869             _getChildStyles(target, e);
16870             tween.render(0, true);
16871             tween._enabled(true);
16872             i = targets.length;
16873             while (--i > -1) {
16874                 difs = _cssDif(targets[i], b[i], e[i]);
16875                 if (difs.firstMPT) {
16876                     difs = difs.difs;
16877                     for (p in vars) {
16878                         if (_reservedProps[p]) {
16879                             difs[p] = vars[p];
16880                         }
16881                     }
16882                     results.push( TweenLite.to(targets[i], duration, difs) );
16883                 }
16884             }
16885             return results;
16886         };
16887
16888         TweenPlugin.activate([CSSPlugin]);
16889         return CSSPlugin;
16890
16891     }, true);
16892
16893     
16894     
16895     
16896     
16897     
16898     
16899     
16900     
16901     
16902     
16903 /*
16904  * ----------------------------------------------------------------
16905  * RoundPropsPlugin
16906  * ----------------------------------------------------------------
16907  */
16908     (function() {
16909
16910         var RoundPropsPlugin = window._gsDefine.plugin({
16911                 propName: "roundProps",
16912                 priority: -1,
16913                 API: 2,
16914
16915                 //called when the tween renders for the first time. This is where initial values should be recorded and any setup routines should run.
16916                 init: function(target, value, tween) {
16917                     this._tween = tween;
16918                     return true;
16919                 }
16920
16921             }),
16922             p = RoundPropsPlugin.prototype;
16923
16924         p._onInitAllProps = function() {
16925             var tween = this._tween,
16926                 rp = (tween.vars.roundProps instanceof Array) ? tween.vars.roundProps : tween.vars.roundProps.split(","),
16927                 i = rp.length,
16928                 lookup = {},
16929                 rpt = tween._propLookup.roundProps,
16930                 prop, pt, next;
16931             while (--i > -1) {
16932                 lookup[rp[i]] = 1;
16933             }
16934             i = rp.length;
16935             while (--i > -1) {
16936                 prop = rp[i];
16937                 pt = tween._firstPT;
16938                 while (pt) {
16939                     next = pt._next; //record here, because it may get removed
16940                     if (pt.pg) {
16941                         pt.t._roundProps(lookup, true);
16942                     } else if (pt.n === prop) {
16943                         this._add(pt.t, prop, pt.s, pt.c);
16944                         //remove from linked list
16945                         if (next) {
16946                             next._prev = pt._prev;
16947                         }
16948                         if (pt._prev) {
16949                             pt._prev._next = next;
16950                         } else if (tween._firstPT === pt) {
16951                             tween._firstPT = next;
16952                         }
16953                         pt._next = pt._prev = null;
16954                         tween._propLookup[prop] = rpt;
16955                     }
16956                     pt = next;
16957                 }
16958             }
16959             return false;
16960         };
16961
16962         p._add = function(target, p, s, c) {
16963             this._addTween(target, p, s, s + c, p, true);
16964             this._overwriteProps.push(p);
16965         };
16966
16967     }());
16968
16969
16970
16971
16972
16973
16974
16975
16976
16977
16978 /*
16979  * ----------------------------------------------------------------
16980  * AttrPlugin
16981  * ----------------------------------------------------------------
16982  */
16983     window._gsDefine.plugin({
16984         propName: "attr",
16985         API: 2,
16986         version: "0.3.2",
16987
16988         //called when the tween renders for the first time. This is where initial values should be recorded and any setup routines should run.
16989         init: function(target, value, tween) {
16990             var p, start, end;
16991             if (typeof(target.setAttribute) !== "function") {
16992                 return false;
16993             }
16994             this._target = target;
16995             this._proxy = {};
16996             this._start = {}; // we record start and end values exactly as they are in case they're strings (not numbers) - we need to be able to revert to them cleanly.
16997             this._end = {};
16998             for (p in value) {
16999                 this._start[p] = this._proxy[p] = start = target.getAttribute(p);
17000                 end = this._addTween(this._proxy, p, parseFloat(start), value[p], p);
17001                 this._end[p] = end ? end.s + end.c : value[p];
17002                 this._overwriteProps.push(p);
17003             }
17004             return true;
17005         },
17006
17007         //called each time the values should be updated, and the ratio gets passed as the only parameter (typically it's a value between 0 and 1, but it can exceed those when using an ease like Elastic.easeOut or Back.easeOut, etc.)
17008         set: function(ratio) {
17009             this._super.setRatio.call(this, ratio);
17010             var props = this._overwriteProps,
17011                 i = props.length,
17012                 lookup = (ratio === 1) ? this._end : ratio ? this._proxy : this._start,
17013                 p;
17014             while (--i > -1) {
17015                 p = props[i];
17016                 this._target.setAttribute(p, lookup[p] + "");
17017             }
17018         }
17019
17020     });
17021
17022
17023
17024
17025
17026
17027
17028
17029
17030
17031 /*
17032  * ----------------------------------------------------------------
17033  * DirectionalRotationPlugin
17034  * ----------------------------------------------------------------
17035  */
17036     window._gsDefine.plugin({
17037         propName: "directionalRotation",
17038         API: 2,
17039         version: "0.2.0",
17040
17041         //called when the tween renders for the first time. This is where initial values should be recorded and any setup routines should run.
17042         init: function(target, value, tween) {
17043             if (typeof(value) !== "object") {
17044                 value = {rotation:value};
17045             }
17046             this.finals = {};
17047             var cap = (value.useRadians === true) ? Math.PI * 2 : 360,
17048                 min = 0.000001,
17049                 p, v, start, end, dif, split;
17050             for (p in value) {
17051                 if (p !== "useRadians") {
17052                     split = (value[p] + "").split("_");
17053                     v = split[0];
17054                     start = parseFloat( (typeof(target[p]) !== "function") ? target[p] : target[ ((p.indexOf("set") || typeof(target["get" + p.substr(3)]) !== "function") ? p : "get" + p.substr(3)) ]() );
17055                     end = this.finals[p] = (typeof(v) === "string" && v.charAt(1) === "=") ? start + parseInt(v.charAt(0) + "1", 10) * Number(v.substr(2)) : Number(v) || 0;
17056                     dif = end - start;
17057                     if (split.length) {
17058                         v = split.join("_");
17059                         if (v.indexOf("short") !== -1) {
17060                             dif = dif % cap;
17061                             if (dif !== dif % (cap / 2)) {
17062                                 dif = (dif < 0) ? dif + cap : dif - cap;
17063                             }
17064                         }
17065                         if (v.indexOf("_cw") !== -1 && dif < 0) {
17066                             dif = ((dif + cap * 9999999999) % cap) - ((dif / cap) | 0) * cap;
17067                         } else if (v.indexOf("ccw") !== -1 && dif > 0) {
17068                             dif = ((dif - cap * 9999999999) % cap) - ((dif / cap) | 0) * cap;
17069                         }
17070                     }
17071                     if (dif > min || dif < -min) {
17072                         this._addTween(target, p, start, start + dif, p);
17073                         this._overwriteProps.push(p);
17074                     }
17075                 }
17076             }
17077             return true;
17078         },
17079
17080         //called each time the values should be updated, and the ratio gets passed as the only parameter (typically it's a value between 0 and 1, but it can exceed those when using an ease like Elastic.easeOut or Back.easeOut, etc.)
17081         set: function(ratio) {
17082             var pt;
17083             if (ratio !== 1) {
17084                 this._super.setRatio.call(this, ratio);
17085             } else {
17086                 pt = this._firstPT;
17087                 while (pt) {
17088                     if (pt.f) {
17089                         pt.t[pt.p](this.finals[pt.p]);
17090                     } else {
17091                         pt.t[pt.p] = this.finals[pt.p];
17092                     }
17093                     pt = pt._next;
17094                 }
17095             }
17096         }
17097
17098     })._autoCSS = true;
17099
17100
17101
17102
17103
17104
17105
17106     
17107     
17108     
17109     
17110 /*
17111  * ----------------------------------------------------------------
17112  * EasePack
17113  * ----------------------------------------------------------------
17114  */
17115     window._gsDefine("easing.Back", ["easing.Ease"], function(Ease) {
17116         
17117         var w = (window.GreenSockGlobals || window),
17118             gs = w.com.greensock,
17119             _2PI = Math.PI * 2,
17120             _HALF_PI = Math.PI / 2,
17121             _class = gs._class,
17122             _create = function(n, f) {
17123                 var C = _class("easing." + n, function(){}, true),
17124                     p = C.prototype = new Ease();
17125                 p.constructor = C;
17126                 p.getRatio = f;
17127                 return C;
17128             },
17129             _easeReg = Ease.register || function(){}, //put an empty function in place just as a safety measure in case someone loads an OLD version of TweenLite.js where Ease.register doesn't exist.
17130             _wrap = function(name, EaseOut, EaseIn, EaseInOut, aliases) {
17131                 var C = _class("easing."+name, {
17132                     easeOut:new EaseOut(),
17133                     easeIn:new EaseIn(),
17134                     easeInOut:new EaseInOut()
17135                 }, true);
17136                 _easeReg(C, name);
17137                 return C;
17138             },
17139             EasePoint = function(time, value, next) {
17140                 this.t = time;
17141                 this.v = value;
17142                 if (next) {
17143                     this.next = next;
17144                     next.prev = this;
17145                     this.c = next.v - value;
17146                     this.gap = next.t - time;
17147                 }
17148             },
17149
17150             //Back
17151             _createBack = function(n, f) {
17152                 var C = _class("easing." + n, function(overshoot) {
17153                         this._p1 = (overshoot || overshoot === 0) ? overshoot : 1.70158;
17154                         this._p2 = this._p1 * 1.525;
17155                     }, true),
17156                     p = C.prototype = new Ease();
17157                 p.constructor = C;
17158                 p.getRatio = f;
17159                 p.config = function(overshoot) {
17160                     return new C(overshoot);
17161                 };
17162                 return C;
17163             },
17164
17165             Back = _wrap("Back",
17166                 _createBack("BackOut", function(p) {
17167                     return ((p = p - 1) * p * ((this._p1 + 1) * p + this._p1) + 1);
17168                 }),
17169                 _createBack("BackIn", function(p) {
17170                     return p * p * ((this._p1 + 1) * p - this._p1);
17171                 }),
17172                 _createBack("BackInOut", function(p) {
17173                     return ((p *= 2) < 1) ? 0.5 * p * p * ((this._p2 + 1) * p - this._p2) : 0.5 * ((p -= 2) * p * ((this._p2 + 1) * p + this._p2) + 2);
17174                 })
17175             ),
17176
17177
17178             //SlowMo
17179             SlowMo = _class("easing.SlowMo", function(linearRatio, power, yoyoMode) {
17180                 power = (power || power === 0) ? power : 0.7;
17181                 if (linearRatio == null) {
17182                     linearRatio = 0.7;
17183                 } else if (linearRatio > 1) {
17184                     linearRatio = 1;
17185                 }
17186                 this._p = (linearRatio !== 1) ? power : 0;
17187                 this._p1 = (1 - linearRatio) / 2;
17188                 this._p2 = linearRatio;
17189                 this._p3 = this._p1 + this._p2;
17190                 this._calcEnd = (yoyoMode === true);
17191             }, true),
17192             p = SlowMo.prototype = new Ease(),
17193             SteppedEase, RoughEase, _createElastic;
17194
17195         p.constructor = SlowMo;
17196         p.getRatio = function(p) {
17197             var r = p + (0.5 - p) * this._p;
17198             if (p < this._p1) {
17199                 return this._calcEnd ? 1 - ((p = 1 - (p / this._p1)) * p) : r - ((p = 1 - (p / this._p1)) * p * p * p * r);
17200             } else if (p > this._p3) {
17201                 return this._calcEnd ? 1 - (p = (p - this._p3) / this._p1) * p : r + ((p - r) * (p = (p - this._p3) / this._p1) * p * p * p);
17202             }
17203             return this._calcEnd ? 1 : r;
17204         };
17205         SlowMo.ease = new SlowMo(0.7, 0.7);
17206
17207         p.config = SlowMo.config = function(linearRatio, power, yoyoMode) {
17208             return new SlowMo(linearRatio, power, yoyoMode);
17209         };
17210
17211
17212         //SteppedEase
17213         SteppedEase = _class("easing.SteppedEase", function(steps) {
17214                 steps = steps || 1;
17215                 this._p1 = 1 / steps;
17216                 this._p2 = steps + 1;
17217             }, true);
17218         p = SteppedEase.prototype = new Ease();
17219         p.constructor = SteppedEase;
17220         p.getRatio = function(p) {
17221             if (p < 0) {
17222                 p = 0;
17223             } else if (p >= 1) {
17224                 p = 0.999999999;
17225             }
17226             return ((this._p2 * p) >> 0) * this._p1;
17227         };
17228         p.config = SteppedEase.config = function(steps) {
17229             return new SteppedEase(steps);
17230         };
17231
17232
17233         //RoughEase
17234         RoughEase = _class("easing.RoughEase", function(vars) {
17235             vars = vars || {};
17236             var taper = vars.taper || "none",
17237                 a = [],
17238                 cnt = 0,
17239                 points = (vars.points || 20) | 0,
17240                 i = points,
17241                 randomize = (vars.randomize !== false),
17242                 clamp = (vars.clamp === true),
17243                 template = (vars.template instanceof Ease) ? vars.template : null,
17244                 strength = (typeof(vars.strength) === "number") ? vars.strength * 0.4 : 0.4,
17245                 x, y, bump, invX, obj, pnt;
17246             while (--i > -1) {
17247                 x = randomize ? Math.random() : (1 / points) * i;
17248                 y = template ? template.getRatio(x) : x;
17249                 if (taper === "none") {
17250                     bump = strength;
17251                 } else if (taper === "out") {
17252                     invX = 1 - x;
17253                     bump = invX * invX * strength;
17254                 } else if (taper === "in") {
17255                     bump = x * x * strength;
17256                 } else if (x < 0.5) {  //"both" (start)
17257                     invX = x * 2;
17258                     bump = invX * invX * 0.5 * strength;
17259                 } else {                //"both" (end)
17260                     invX = (1 - x) * 2;
17261                     bump = invX * invX * 0.5 * strength;
17262                 }
17263                 if (randomize) {
17264                     y += (Math.random() * bump) - (bump * 0.5);
17265                 } else if (i % 2) {
17266                     y += bump * 0.5;
17267                 } else {
17268                     y -= bump * 0.5;
17269                 }
17270                 if (clamp) {
17271                     if (y > 1) {
17272                         y = 1;
17273                     } else if (y < 0) {
17274                         y = 0;
17275                     }
17276                 }
17277                 a[cnt++] = {x:x, y:y};
17278             }
17279             a.sort(function(a, b) {
17280                 return a.x - b.x;
17281             });
17282
17283             pnt = new EasePoint(1, 1, null);
17284             i = points;
17285             while (--i > -1) {
17286                 obj = a[i];
17287                 pnt = new EasePoint(obj.x, obj.y, pnt);
17288             }
17289
17290             this._prev = new EasePoint(0, 0, (pnt.t !== 0) ? pnt : pnt.next);
17291         }, true);
17292         p = RoughEase.prototype = new Ease();
17293         p.constructor = RoughEase;
17294         p.getRatio = function(p) {
17295             var pnt = this._prev;
17296             if (p > pnt.t) {
17297                 while (pnt.next && p >= pnt.t) {
17298                     pnt = pnt.next;
17299                 }
17300                 pnt = pnt.prev;
17301             } else {
17302                 while (pnt.prev && p <= pnt.t) {
17303                     pnt = pnt.prev;
17304                 }
17305             }
17306             this._prev = pnt;
17307             return (pnt.v + ((p - pnt.t) / pnt.gap) * pnt.c);
17308         };
17309         p.config = function(vars) {
17310             return new RoughEase(vars);
17311         };
17312         RoughEase.ease = new RoughEase();
17313
17314
17315         //Bounce
17316         _wrap("Bounce",
17317             _create("BounceOut", function(p) {
17318                 if (p < 1 / 2.75) {
17319                     return 7.5625 * p * p;
17320                 } else if (p < 2 / 2.75) {
17321                     return 7.5625 * (p -= 1.5 / 2.75) * p + 0.75;
17322                 } else if (p < 2.5 / 2.75) {
17323                     return 7.5625 * (p -= 2.25 / 2.75) * p + 0.9375;
17324                 }
17325                 return 7.5625 * (p -= 2.625 / 2.75) * p + 0.984375;
17326             }),
17327             _create("BounceIn", function(p) {
17328                 if ((p = 1 - p) < 1 / 2.75) {
17329                     return 1 - (7.5625 * p * p);
17330                 } else if (p < 2 / 2.75) {
17331                     return 1 - (7.5625 * (p -= 1.5 / 2.75) * p + 0.75);
17332                 } else if (p < 2.5 / 2.75) {
17333                     return 1 - (7.5625 * (p -= 2.25 / 2.75) * p + 0.9375);
17334                 }
17335                 return 1 - (7.5625 * (p -= 2.625 / 2.75) * p + 0.984375);
17336             }),
17337             _create("BounceInOut", function(p) {
17338                 var invert = (p < 0.5);
17339                 if (invert) {
17340                     p = 1 - (p * 2);
17341                 } else {
17342                     p = (p * 2) - 1;
17343                 }
17344                 if (p < 1 / 2.75) {
17345                     p = 7.5625 * p * p;
17346                 } else if (p < 2 / 2.75) {
17347                     p = 7.5625 * (p -= 1.5 / 2.75) * p + 0.75;
17348                 } else if (p < 2.5 / 2.75) {
17349                     p = 7.5625 * (p -= 2.25 / 2.75) * p + 0.9375;
17350                 } else {
17351                     p = 7.5625 * (p -= 2.625 / 2.75) * p + 0.984375;
17352                 }
17353                 return invert ? (1 - p) * 0.5 : p * 0.5 + 0.5;
17354             })
17355         );
17356
17357
17358         //CIRC
17359         _wrap("Circ",
17360             _create("CircOut", function(p) {
17361                 return Math.sqrt(1 - (p = p - 1) * p);
17362             }),
17363             _create("CircIn", function(p) {
17364                 return -(Math.sqrt(1 - (p * p)) - 1);
17365             }),
17366             _create("CircInOut", function(p) {
17367                 return ((p*=2) < 1) ? -0.5 * (Math.sqrt(1 - p * p) - 1) : 0.5 * (Math.sqrt(1 - (p -= 2) * p) + 1);
17368             })
17369         );
17370
17371
17372         //Elastic
17373         _createElastic = function(n, f, def) {
17374             var C = _class("easing." + n, function(amplitude, period) {
17375                     this._p1 = amplitude || 1;
17376                     this._p2 = period || def;
17377                     this._p3 = this._p2 / _2PI * (Math.asin(1 / this._p1) || 0);
17378                 }, true),
17379                 p = C.prototype = new Ease();
17380             p.constructor = C;
17381             p.getRatio = f;
17382             p.config = function(amplitude, period) {
17383                 return new C(amplitude, period);
17384             };
17385             return C;
17386         };
17387         _wrap("Elastic",
17388             _createElastic("ElasticOut", function(p) {
17389                 return this._p1 * Math.pow(2, -10 * p) * Math.sin( (p - this._p3) * _2PI / this._p2 ) + 1;
17390             }, 0.3),
17391             _createElastic("ElasticIn", function(p) {
17392                 return -(this._p1 * Math.pow(2, 10 * (p -= 1)) * Math.sin( (p - this._p3) * _2PI / this._p2 ));
17393             }, 0.3),
17394             _createElastic("ElasticInOut", function(p) {
17395                 return ((p *= 2) < 1) ? -0.5 * (this._p1 * Math.pow(2, 10 * (p -= 1)) * Math.sin( (p - this._p3) * _2PI / this._p2)) : this._p1 * Math.pow(2, -10 *(p -= 1)) * Math.sin( (p - this._p3) * _2PI / this._p2 ) *0.5 + 1;
17396             }, 0.45)
17397         );
17398
17399
17400         //Expo
17401         _wrap("Expo",
17402             _create("ExpoOut", function(p) {
17403                 return 1 - Math.pow(2, -10 * p);
17404             }),
17405             _create("ExpoIn", function(p) {
17406                 return Math.pow(2, 10 * (p - 1)) - 0.001;
17407             }),
17408             _create("ExpoInOut", function(p) {
17409                 return ((p *= 2) < 1) ? 0.5 * Math.pow(2, 10 * (p - 1)) : 0.5 * (2 - Math.pow(2, -10 * (p - 1)));
17410             })
17411         );
17412
17413
17414         //Sine
17415         _wrap("Sine",
17416             _create("SineOut", function(p) {
17417                 return Math.sin(p * _HALF_PI);
17418             }),
17419             _create("SineIn", function(p) {
17420                 return -Math.cos(p * _HALF_PI) + 1;
17421             }),
17422             _create("SineInOut", function(p) {
17423                 return -0.5 * (Math.cos(Math.PI * p) - 1);
17424             })
17425         );
17426
17427         _class("easing.EaseLookup", {
17428                 find:function(s) {
17429                     return Ease.map[s];
17430                 }
17431             }, true);
17432
17433         //register the non-standard eases
17434         _easeReg(w.SlowMo, "SlowMo", "ease,");
17435         _easeReg(RoughEase, "RoughEase", "ease,");
17436         _easeReg(SteppedEase, "SteppedEase", "ease,");
17437
17438         return Back;
17439         
17440     }, true);
17441
17442
17443 }); 
17444
17445
17446
17447
17448
17449
17450
17451
17452
17453
17454
17455 /*
17456  * ----------------------------------------------------------------
17457  * Base classes like TweenLite, SimpleTimeline, Ease, Ticker, etc.
17458  * ----------------------------------------------------------------
17459  */
17460 (function(window) {
17461
17462         "use strict";
17463         var _globals = window.GreenSockGlobals || window;
17464         if (_globals.TweenLite) {
17465             return; //in case the core set of classes is already loaded, don't instantiate twice.
17466         }
17467         var _namespace = function(ns) {
17468                 var a = ns.split("."),
17469                     p = _globals, i;
17470                 for (i = 0; i < a.length; i++) {
17471                     p[a[i]] = p = p[a[i]] || {};
17472                 }
17473                 return p;
17474             },
17475             gs = _namespace("com.greensock"),
17476             _tinyNum = 0.0000000001,
17477             _slice = [].slice,
17478             _emptyFunc = function() {},
17479             _isArray = (function() { //works around issues in iframe environments where the Array global isn't shared, thus if the object originates in a different window/iframe, "(obj instanceof Array)" will evaluate false. We added some speed optimizations to avoid Object.prototype.toString.call() unless it's absolutely necessary because it's VERY slow (like 20x slower)
17480                 var toString = Object.prototype.toString,
17481                     array = toString.call([]);
17482                 return function(obj) {
17483                     return obj != null && (obj instanceof Array || (typeof(obj) === "object" && !!obj.push && toString.call(obj) === array));
17484                 };
17485             }()),
17486             a, i, p, _ticker, _tickerActive,
17487             _defLookup = {},
17488
17489             /**
17490              * @constructor
17491              * Defines a GreenSock class, optionally with an array of dependencies that must be instantiated first and passed into the definition.
17492              * This allows users to load GreenSock JS files in any order even if they have interdependencies (like CSSPlugin extends TweenPlugin which is
17493              * inside TweenLite.js, but if CSSPlugin is loaded first, it should wait to run its code until TweenLite.js loads and instantiates TweenPlugin
17494              * and then pass TweenPlugin to CSSPlugin's definition). This is all done automatically and internally.
17495              *
17496              * Every definition will be added to a "com.greensock" global object (typically window, but if a window.GreenSockGlobals object is found,
17497              * it will go there as of v1.7). For example, TweenLite will be found at window.com.greensock.TweenLite and since it's a global class that should be available anywhere,
17498              * it is ALSO referenced at window.TweenLite. However some classes aren't considered global, like the base com.greensock.core.Animation class, so
17499              * those will only be at the package like window.com.greensock.core.Animation. Again, if you define a GreenSockGlobals object on the window, everything
17500              * gets tucked neatly inside there instead of on the window directly. This allows you to do advanced things like load multiple versions of GreenSock
17501              * files and put them into distinct objects (imagine a banner ad uses a newer version but the main site uses an older one). In that case, you could
17502              * sandbox the banner one like:
17503              *
17504              * <script>
17505              *     var gs = window.GreenSockGlobals = {}; //the newer version we're about to load could now be referenced in a "gs" object, like gs.TweenLite.to(...). Use whatever alias you want as long as it's unique, "gs" or "banner" or whatever.
17506              * </script>
17507              * <script src="js/greensock/v1.7/TweenMax.js"></script>
17508              * <script>
17509              *     window.GreenSockGlobals = null; //reset it back to null so that the next load of TweenMax affects the window and we can reference things directly like TweenLite.to(...)
17510              * </script>
17511              * <script src="js/greensock/v1.6/TweenMax.js"></script>
17512              * <script>
17513              *     gs.TweenLite.to(...); //would use v1.7
17514              *     TweenLite.to(...); //would use v1.6
17515              * </script>
17516              *
17517              * @param {!string} ns The namespace of the class definition, leaving off "com.greensock." as that's assumed. For example, "TweenLite" or "plugins.CSSPlugin" or "easing.Back".
17518              * @param {!Array.<string>} dependencies An array of dependencies (described as their namespaces minus "com.greensock." prefix). For example ["TweenLite","plugins.TweenPlugin","core.Animation"]
17519              * @param {!function():Object} func The function that should be called and passed the resolved dependencies which will return the actual class for this definition.
17520              * @param {boolean=} global If true, the class will be added to the global scope (typically window unless you define a window.GreenSockGlobals object)
17521              */
17522             Definition = function(ns, dependencies, func, global) {
17523                 this.sc = (_defLookup[ns]) ? _defLookup[ns].sc : []; //subclasses
17524                 _defLookup[ns] = this;
17525                 this.gsClass = null;
17526                 this.func = func;
17527                 var _classes = [];
17528                 this.check = function(init) {
17529                     var i = dependencies.length,
17530                         missing = i,
17531                         cur, a, n, cl;
17532                     while (--i > -1) {
17533                         if ((cur = _defLookup[dependencies[i]] || new Definition(dependencies[i], [])).gsClass) {
17534                             _classes[i] = cur.gsClass;
17535                             missing--;
17536                         } else if (init) {
17537                             cur.sc.push(this);
17538                         }
17539                     }
17540                     if (missing === 0 && func) {
17541                         a = ("com.greensock." + ns).split(".");
17542                         n = a.pop();
17543                         cl = _namespace(a.join("."))[n] = this.gsClass = func.apply(func, _classes);
17544
17545                         //exports to multiple environments
17546                         if (global) {
17547                             _globals[n] = cl; //provides a way to avoid global namespace pollution. By default, the main classes like TweenLite, Power1, Strong, etc. are added to window unless a GreenSockGlobals is defined. So if you want to have things added to a custom object instead, just do something like window.GreenSockGlobals = {} before loading any GreenSock files. You can even set up an alias like window.GreenSockGlobals = windows.gs = {} so that you can access everything like gs.TweenLite. Also remember that ALL classes are added to the window.com.greensock object (in their respective packages, like com.greensock.easing.Power1, com.greensock.TweenLite, etc.)
17548                             if (typeof(define) === "function" && define.amd){ //AMD
17549                                 define((window.GreenSockAMDPath ? window.GreenSockAMDPath + "/" : "") + ns.split(".").join("/"), [], function() { return cl; });
17550                             } else if (typeof(module) !== "undefined" && module.exports){ //node
17551                                 module.exports = cl;
17552                             }
17553                         }
17554                         for (i = 0; i < this.sc.length; i++) {
17555                             this.sc[i].check();
17556                         }
17557                     }
17558                 };
17559                 this.check(true);
17560             },
17561
17562             //used to create Definition instances (which basically registers a class that has dependencies).
17563             _gsDefine = window._gsDefine = function(ns, dependencies, func, global) {
17564                 return new Definition(ns, dependencies, func, global);
17565             },
17566
17567             //a quick way to create a class that doesn't have any dependencies. Returns the class, but first registers it in the GreenSock namespace so that other classes can grab it (other classes might be dependent on the class).
17568             _class = gs._class = function(ns, func, global) {
17569                 func = func || function() {};
17570                 _gsDefine(ns, [], function(){ return func; }, global);
17571                 return func;
17572             };
17573
17574         _gsDefine.globals = _globals;
17575
17576
17577
17578 /*
17579  * ----------------------------------------------------------------
17580  * Ease
17581  * ----------------------------------------------------------------
17582  */
17583         var _baseParams = [0, 0, 1, 1],
17584             _blankArray = [],
17585             Ease = _class("easing.Ease", function(func, extraParams, type, power) {
17586                 this._func = func;
17587                 this._type = type || 0;
17588                 this._power = power || 0;
17589                 this._params = extraParams ? _baseParams.concat(extraParams) : _baseParams;
17590             }, true),
17591             _easeMap = Ease.map = {},
17592             _easeReg = Ease.register = function(ease, names, types, create) {
17593                 var na = names.split(","),
17594                     i = na.length,
17595                     ta = (types || "easeIn,easeOut,easeInOut").split(","),
17596                     e, name, j, type;
17597                 while (--i > -1) {
17598                     name = na[i];
17599                     e = create ? _class("easing."+name, null, true) : gs.easing[name] || {};
17600                     j = ta.length;
17601                     while (--j > -1) {
17602                         type = ta[j];
17603                         _easeMap[name + "." + type] = _easeMap[type + name] = e[type] = ease.getRatio ? ease : ease[type] || new ease();
17604                     }
17605                 }
17606             };
17607
17608         p = Ease.prototype;
17609         p._calcEnd = false;
17610         p.getRatio = function(p) {
17611             if (this._func) {
17612                 this._params[0] = p;
17613                 return this._func.apply(null, this._params);
17614             }
17615             var t = this._type,
17616                 pw = this._power,
17617                 r = (t === 1) ? 1 - p : (t === 2) ? p : (p < 0.5) ? p * 2 : (1 - p) * 2;
17618             if (pw === 1) {
17619                 r *= r;
17620             } else if (pw === 2) {
17621                 r *= r * r;
17622             } else if (pw === 3) {
17623                 r *= r * r * r;
17624             } else if (pw === 4) {
17625                 r *= r * r * r * r;
17626             }
17627             return (t === 1) ? 1 - r : (t === 2) ? r : (p < 0.5) ? r / 2 : 1 - (r / 2);
17628         };
17629
17630         //create all the standard eases like Linear, Quad, Cubic, Quart, Quint, Strong, Power0, Power1, Power2, Power3, and Power4 (each with easeIn, easeOut, and easeInOut)
17631         a = ["Linear","Quad","Cubic","Quart","Quint,Strong"];
17632         i = a.length;
17633         while (--i > -1) {
17634             p = a[i]+",Power"+i;
17635             _easeReg(new Ease(null,null,1,i), p, "easeOut", true);
17636             _easeReg(new Ease(null,null,2,i), p, "easeIn" + ((i === 0) ? ",easeNone" : ""));
17637             _easeReg(new Ease(null,null,3,i), p, "easeInOut");
17638         }
17639         _easeMap.linear = gs.easing.Linear.easeIn;
17640         _easeMap.swing = gs.easing.Quad.easeInOut; //for jQuery folks
17641
17642
17643 /*
17644  * ----------------------------------------------------------------
17645  * EventDispatcher
17646  * ----------------------------------------------------------------
17647  */
17648         var EventDispatcher = _class("events.EventDispatcher", function(target) {
17649             this._listeners = {};
17650             this._eventTarget = target || this;
17651         });
17652         p = EventDispatcher.prototype;
17653
17654         p.addEventListener = function(type, callback, scope, useParam, priority) {
17655             priority = priority || 0;
17656             var list = this._listeners[type],
17657                 index = 0,
17658                 listener, i;
17659             if (list == null) {
17660                 this._listeners[type] = list = [];
17661             }
17662             i = list.length;
17663             while (--i > -1) {
17664                 listener = list[i];
17665                 if (listener.c === callback && listener.s === scope) {
17666                     list.splice(i, 1);
17667                 } else if (index === 0 && listener.pr < priority) {
17668                     index = i + 1;
17669                 }
17670             }
17671             list.splice(index, 0, {c:callback, s:scope, up:useParam, pr:priority});
17672             if (this === _ticker && !_tickerActive) {
17673                 _ticker.wake();
17674             }
17675         };
17676
17677         p.removeEventListener = function(type, callback) {
17678             var list = this._listeners[type], i;
17679             if (list) {
17680                 i = list.length;
17681                 while (--i > -1) {
17682                     if (list[i].c === callback) {
17683                         list.splice(i, 1);
17684                         return;
17685                     }
17686                 }
17687             }
17688         };
17689
17690         p.dispatchEvent = function(type) {
17691             var list = this._listeners[type],
17692                 i, t, listener;
17693             if (list) {
17694                 i = list.length;
17695                 t = this._eventTarget;
17696                 while (--i > -1) {
17697                     listener = list[i];
17698                     if (listener.up) {
17699                         listener.c.call(listener.s || t, {type:type, target:t});
17700                     } else {
17701                         listener.c.call(listener.s || t);
17702                     }
17703                 }
17704             }
17705         };
17706
17707
17708 /*
17709  * ----------------------------------------------------------------
17710  * Ticker
17711  * ----------------------------------------------------------------
17712  */
17713         var _reqAnimFrame = window.requestAnimationFrame,
17714             _cancelAnimFrame = window.cancelAnimationFrame,
17715             _getTime = Date.now || function() {return new Date().getTime();},
17716             _lastUpdate = _getTime();
17717
17718         //now try to determine the requestAnimationFrame and cancelAnimationFrame functions and if none are found, we'll use a setTimeout()/clearTimeout() polyfill.
17719         a = ["ms","moz","webkit","o"];
17720         i = a.length;
17721         while (--i > -1 && !_reqAnimFrame) {
17722             _reqAnimFrame = window[a[i] + "RequestAnimationFrame"];
17723             _cancelAnimFrame = window[a[i] + "CancelAnimationFrame"] || window[a[i] + "CancelRequestAnimationFrame"];
17724         }
17725
17726         _class("Ticker", function(fps, useRAF) {
17727             var _self = this,
17728                 _startTime = _getTime(),
17729                 _useRAF = (useRAF !== false && _reqAnimFrame),
17730                 _lagThreshold = 500,
17731                 _adjustedLag = 33,
17732                 _fps, _req, _id, _gap, _nextTime,
17733                 _tick = function(manual) {
17734                     var elapsed = _getTime() - _lastUpdate,
17735                         overlap, dispatch;
17736                     if (elapsed > _lagThreshold) {
17737                         _startTime += elapsed - _adjustedLag;
17738                     }
17739                     _lastUpdate += elapsed;
17740                     _self.time = (_lastUpdate - _startTime) / 1000;
17741                     overlap = _self.time - _nextTime;
17742                     if (!_fps || overlap > 0 || manual === true) {
17743                         _self.frame++;
17744                         _nextTime += overlap + (overlap >= _gap ? 0.004 : _gap - overlap);
17745                         dispatch = true;
17746                     }
17747                     if (manual !== true) { //make sure the request is made before we dispatch the "tick" event so that timing is maintained. Otherwise, if processing the "tick" requires a bunch of time (like 15ms) and we're using a setTimeout() that's based on 16.7ms, it'd technically take 31.7ms between frames otherwise.
17748                         _id = _req(_tick);
17749                     }
17750                     if (dispatch) {
17751                         _self.dispatchEvent("tick");
17752                     }
17753                 };
17754
17755             EventDispatcher.call(_self);
17756             _self.time = _self.frame = 0;
17757             _self.tick = function() {
17758                 _tick(true);
17759             };
17760
17761             _self.lagSmoothing = function(threshold, adjustedLag) {
17762                 _lagThreshold = threshold || (1 / _tinyNum); //zero should be interpreted as basically unlimited
17763                 _adjustedLag = Math.min(adjustedLag, _lagThreshold, 0);
17764             };
17765
17766             _self.sleep = function() {
17767                 if (_id == null) {
17768                     return;
17769                 }
17770                 if (!_useRAF || !_cancelAnimFrame) {
17771                     clearTimeout(_id);
17772                 } else {
17773                     _cancelAnimFrame(_id);
17774                 }
17775                 _req = _emptyFunc;
17776                 _id = null;
17777                 if (_self === _ticker) {
17778                     _tickerActive = false;
17779                 }
17780             };
17781
17782             _self.wake = function() {
17783                 if (_id !== null) {
17784                     _self.sleep();
17785                 } else if (_self.frame > 10) { //don't trigger lagSmoothing if we're just waking up, and make sure that at least 10 frames have elapsed because of the iOS bug that we work around below with the 1.5-second setTimout().
17786                     _lastUpdate = _getTime() - _lagThreshold + 5;
17787                 }
17788                 _req = (_fps === 0) ? _emptyFunc : (!_useRAF || !_reqAnimFrame) ? function(f) { return setTimeout(f, ((_nextTime - _self.time) * 1000 + 1) | 0); } : _reqAnimFrame;
17789                 if (_self === _ticker) {
17790                     _tickerActive = true;
17791                 }
17792                 _tick(2);
17793             };
17794
17795             _self.fps = function(value) {
17796                 if (!arguments.length) {
17797                     return _fps;
17798                 }
17799                 _fps = value;
17800                 _gap = 1 / (_fps || 60);
17801                 _nextTime = this.time + _gap;
17802                 _self.wake();
17803             };
17804
17805             _self.useRAF = function(value) {
17806                 if (!arguments.length) {
17807                     return _useRAF;
17808                 }
17809                 _self.sleep();
17810                 _useRAF = value;
17811                 _self.fps(_fps);
17812             };
17813             _self.fps(fps);
17814
17815             //a bug in iOS 6 Safari occasionally prevents the requestAnimationFrame from working initially, so we use a 1.5-second timeout that automatically falls back to setTimeout() if it senses this condition.
17816             setTimeout(function() {
17817                 if (_useRAF && (!_id || _self.frame < 5)) {
17818                     _self.useRAF(false);
17819                 }
17820             }, 1500);
17821         });
17822
17823         p = gs.Ticker.prototype = new gs.events.EventDispatcher();
17824         p.constructor = gs.Ticker;
17825
17826
17827 /*
17828  * ----------------------------------------------------------------
17829  * Animation
17830  * ----------------------------------------------------------------
17831  */
17832         var Animation = _class("core.Animation", function(duration, vars) {
17833                 this.vars = vars = vars || {};
17834                 this._duration = this._totalDuration = duration || 0;
17835                 this._delay = Number(vars.delay) || 0;
17836                 this._timeScale = 1;
17837                 this._active = (vars.immediateRender === true);
17838                 this.data = vars.data;
17839                 this._reversed = (vars.reversed === true);
17840
17841                 if (!_rootTimeline) {
17842                     return;
17843                 }
17844                 if (!_tickerActive) { //some browsers (like iOS 6 Safari) shut down JavaScript execution when the tab is disabled and they [occasionally] neglect to start up requestAnimationFrame again when returning - this code ensures that the engine starts up again properly.
17845                     _ticker.wake();
17846                 }
17847
17848                 var tl = this.vars.useFrames ? _rootFramesTimeline : _rootTimeline;
17849                 tl.add(this, tl._time);
17850
17851                 if (this.vars.paused) {
17852                     this.paused(true);
17853                 }
17854             });
17855
17856         _ticker = Animation.ticker = new gs.Ticker();
17857         p = Animation.prototype;
17858         p._dirty = p._gc = p._initted = p._paused = false;
17859         p._totalTime = p._time = 0;
17860         p._rawPrevTime = -1;
17861         p._next = p._last = p._onUpdate = p._timeline = p.timeline = null;
17862         p._paused = false;
17863
17864
17865         //some browsers (like iOS) occasionally drop the requestAnimationFrame event when the user switches to a different tab and then comes back again, so we use a 2-second setTimeout() to sense if/when that condition occurs and then wake() the ticker.
17866         var _checkTimeout = function() {
17867                 if (_tickerActive && _getTime() - _lastUpdate > 2000) {
17868                     _ticker.wake();
17869                 }
17870                 setTimeout(_checkTimeout, 2000);
17871             };
17872         _checkTimeout();
17873
17874
17875         p.play = function(from, suppressEvents) {
17876             if (from != null) {
17877                 this.seek(from, suppressEvents);
17878             }
17879             return this.reversed(false).paused(false);
17880         };
17881
17882         p.pause = function(atTime, suppressEvents) {
17883             if (atTime != null) {
17884                 this.seek(atTime, suppressEvents);
17885             }
17886             return this.paused(true);
17887         };
17888
17889         p.resume = function(from, suppressEvents) {
17890             if (from != null) {
17891                 this.seek(from, suppressEvents);
17892             }
17893             return this.paused(false);
17894         };
17895
17896         p.seek = function(time, suppressEvents) {
17897             return this.totalTime(Number(time), suppressEvents !== false);
17898         };
17899
17900         p.restart = function(includeDelay, suppressEvents) {
17901             return this.reversed(false).paused(false).totalTime(includeDelay ? -this._delay : 0, (suppressEvents !== false), true);
17902         };
17903
17904         p.reverse = function(from, suppressEvents) {
17905             if (from != null) {
17906                 this.seek((from || this.totalDuration()), suppressEvents);
17907             }
17908             return this.reversed(true).paused(false);
17909         };
17910
17911         p.render = function(time, suppressEvents, force) {
17912             //stub - we override this method in subclasses.
17913         };
17914
17915         p.invalidate = function() {
17916             return this;
17917         };
17918
17919         p.isActive = function() {
17920             var tl = this._timeline, //the 2 root timelines won't have a _timeline; they're always active.
17921                 startTime = this._startTime,
17922                 rawTime;
17923             return (!tl || (!this._gc && !this._paused && tl.isActive() && (rawTime = tl.rawTime()) >= startTime && rawTime < startTime + this.totalDuration() / this._timeScale));
17924         };
17925
17926         p._enabled = function (enabled, ignoreTimeline) {
17927             if (!_tickerActive) {
17928                 _ticker.wake();
17929             }
17930             this._gc = !enabled;
17931             this._active = this.isActive();
17932             if (ignoreTimeline !== true) {
17933                 if (enabled && !this.timeline) {
17934                     this._timeline.add(this, this._startTime - this._delay);
17935                 } else if (!enabled && this.timeline) {
17936                     this._timeline._remove(this, true);
17937                 }
17938             }
17939             return false;
17940         };
17941
17942
17943         p._kill = function(vars, target) {
17944             return this._enabled(false, false);
17945         };
17946
17947         p.kill = function(vars, target) {
17948             this._kill(vars, target);
17949             return this;
17950         };
17951
17952         p._uncache = function(includeSelf) {
17953             var tween = includeSelf ? this : this.timeline;
17954             while (tween) {
17955                 tween._dirty = true;
17956                 tween = tween.timeline;
17957             }
17958             return this;
17959         };
17960
17961         p._swapSelfInParams = function(params) {
17962             var i = params.length,
17963                 copy = params.concat();
17964             while (--i > -1) {
17965                 if (params[i] === "{self}") {
17966                     copy[i] = this;
17967                 }
17968             }
17969             return copy;
17970         };
17971
17972 //----Animation getters/setters --------------------------------------------------------
17973
17974         p.eventCallback = function(type, callback, params, scope) {
17975             if ((type || "").substr(0,2) === "on") {
17976                 var v = this.vars;
17977                 if (arguments.length === 1) {
17978                     return v[type];
17979                 }
17980                 if (callback == null) {
17981                     delete v[type];
17982                 } else {
17983                     v[type] = callback;
17984                     v[type + "Params"] = (_isArray(params) && params.join("").indexOf("{self}") !== -1) ? this._swapSelfInParams(params) : params;
17985                     v[type + "Scope"] = scope;
17986                 }
17987                 if (type === "onUpdate") {
17988                     this._onUpdate = callback;
17989                 }
17990             }
17991             return this;
17992         };
17993
17994         p.delay = function(value) {
17995             if (!arguments.length) {
17996                 return this._delay;
17997             }
17998             if (this._timeline.smoothChildTiming) {
17999                 this.startTime( this._startTime + value - this._delay );
18000             }
18001             this._delay = value;
18002             return this;
18003         };
18004
18005         p.duration = function(value) {
18006             if (!arguments.length) {
18007                 this._dirty = false;
18008                 return this._duration;
18009             }
18010             this._duration = this._totalDuration = value;
18011             this._uncache(true); //true in case it's a TweenMax or TimelineMax that has a repeat - we'll need to refresh the totalDuration.
18012             if (this._timeline.smoothChildTiming) if (this._time > 0) if (this._time < this._duration) if (value !== 0) {
18013                 this.totalTime(this._totalTime * (value / this._duration), true);
18014             }
18015             return this;
18016         };
18017
18018         p.totalDuration = function(value) {
18019             this._dirty = false;
18020             return (!arguments.length) ? this._totalDuration : this.duration(value);
18021         };
18022
18023         p.time = function(value, suppressEvents) {
18024             if (!arguments.length) {
18025                 return this._time;
18026             }
18027             if (this._dirty) {
18028                 this.totalDuration();
18029             }
18030             return this.totalTime((value > this._duration) ? this._duration : value, suppressEvents);
18031         };
18032
18033         p.totalTime = function(time, suppressEvents, uncapped) {
18034             if (!_tickerActive) {
18035                 _ticker.wake();
18036             }
18037             if (!arguments.length) {
18038                 return this._totalTime;
18039             }
18040             if (this._timeline) {
18041                 if (time < 0 && !uncapped) {
18042                     time += this.totalDuration();
18043                 }
18044                 if (this._timeline.smoothChildTiming) {
18045                     if (this._dirty) {
18046                         this.totalDuration();
18047                     }
18048                     var totalDuration = this._totalDuration,
18049                         tl = this._timeline;
18050                     if (time > totalDuration && !uncapped) {
18051                         time = totalDuration;
18052                     }
18053                     this._startTime = (this._paused ? this._pauseTime : tl._time) - ((!this._reversed ? time : totalDuration - time) / this._timeScale);
18054                     if (!tl._dirty) { //for performance improvement. If the parent's cache is already dirty, it already took care of marking the ancestors as dirty too, so skip the function call here.
18055                         this._uncache(false);
18056                     }
18057                     //in case any of the ancestor timelines had completed but should now be enabled, we should reset their totalTime() which will also ensure that they're lined up properly and enabled. Skip for animations that are on the root (wasteful). Example: a TimelineLite.exportRoot() is performed when there's a paused tween on the root, the export will not complete until that tween is unpaused, but imagine a child gets restarted later, after all [unpaused] tweens have completed. The startTime of that child would get pushed out, but one of the ancestors may have completed.
18058                     if (tl._timeline) {
18059                         while (tl._timeline) {
18060                             if (tl._timeline._time !== (tl._startTime + tl._totalTime) / tl._timeScale) {
18061                                 tl.totalTime(tl._totalTime, true);
18062                             }
18063                             tl = tl._timeline;
18064                         }
18065                     }
18066                 }
18067                 if (this._gc) {
18068                     this._enabled(true, false);
18069                 }
18070                 if (this._totalTime !== time || this._duration === 0) {
18071                     this.render(time, suppressEvents, false);
18072                     if (_lazyTweens.length) { //in case rendering caused any tweens to lazy-init, we should render them because typically when someone calls seek() or time() or progress(), they expect an immediate render.
18073                         _lazyRender();
18074                     }
18075                 }
18076             }
18077             return this;
18078         };
18079
18080         p.progress = p.totalProgress = function(value, suppressEvents) {
18081             return (!arguments.length) ? this._time / this.duration() : this.totalTime(this.duration() * value, suppressEvents);
18082         };
18083
18084         p.startTime = function(value) {
18085             if (!arguments.length) {
18086                 return this._startTime;
18087             }
18088             if (value !== this._startTime) {
18089                 this._startTime = value;
18090                 if (this.timeline) if (this.timeline._sortChildren) {
18091                     this.timeline.add(this, value - this._delay); //ensures that any necessary re-sequencing of Animations in the timeline occurs to make sure the rendering order is correct.
18092                 }
18093             }
18094             return this;
18095         };
18096
18097         p.timeScale = function(value) {
18098             if (!arguments.length) {
18099                 return this._timeScale;
18100             }
18101             value = value || _tinyNum; //can't allow zero because it'll throw the math off
18102             if (this._timeline && this._timeline.smoothChildTiming) {
18103                 var pauseTime = this._pauseTime,
18104                     t = (pauseTime || pauseTime === 0) ? pauseTime : this._timeline.totalTime();
18105                 this._startTime = t - ((t - this._startTime) * this._timeScale / value);
18106             }
18107             this._timeScale = value;
18108             return this._uncache(false);
18109         };
18110
18111         p.reversed = function(value) {
18112             if (!arguments.length) {
18113                 return this._reversed;
18114             }
18115             if (value != this._reversed) {
18116                 this._reversed = value;
18117                 this.totalTime(((this._timeline && !this._timeline.smoothChildTiming) ? this.totalDuration() - this._totalTime : this._totalTime), true);
18118             }
18119             return this;
18120         };
18121
18122         p.paused = function(value) {
18123             if (!arguments.length) {
18124                 return this._paused;
18125             }
18126             if (value != this._paused) if (this._timeline) {
18127                 if (!_tickerActive && !value) {
18128                     _ticker.wake();
18129                 }
18130                 var tl = this._timeline,
18131                     raw = tl.rawTime(),
18132                     elapsed = raw - this._pauseTime;
18133                 if (!value && tl.smoothChildTiming) {
18134                     this._startTime += elapsed;
18135                     this._uncache(false);
18136                 }
18137                 this._pauseTime = value ? raw : null;
18138                 this._paused = value;
18139                 this._active = this.isActive();
18140                 if (!value && elapsed !== 0 && this._initted && this.duration()) {
18141                     this.render((tl.smoothChildTiming ? this._totalTime : (raw - this._startTime) / this._timeScale), true, true); //in case the target's properties changed via some other tween or manual update by the user, we should force a render.
18142                 }
18143             }
18144             if (this._gc && !value) {
18145                 this._enabled(true, false);
18146             }
18147             return this;
18148         };
18149
18150
18151 /*
18152  * ----------------------------------------------------------------
18153  * SimpleTimeline
18154  * ----------------------------------------------------------------
18155  */
18156         var SimpleTimeline = _class("core.SimpleTimeline", function(vars) {
18157             Animation.call(this, 0, vars);
18158             this.autoRemoveChildren = this.smoothChildTiming = true;
18159         });
18160
18161         p = SimpleTimeline.prototype = new Animation();
18162         p.constructor = SimpleTimeline;
18163         p.kill()._gc = false;
18164         p._first = p._last = null;
18165         p._sortChildren = false;
18166
18167         p.add = p.insert = function(child, position, align, stagger) {
18168             var prevTween, st;
18169             child._startTime = Number(position || 0) + child._delay;
18170             if (child._paused) if (this !== child._timeline) { //we only adjust the _pauseTime if it wasn't in this timeline already. Remember, sometimes a tween will be inserted again into the same timeline when its startTime is changed so that the tweens in the TimelineLite/Max are re-ordered properly in the linked list (so everything renders in the proper order).
18171                 child._pauseTime = child._startTime + ((this.rawTime() - child._startTime) / child._timeScale);
18172             }
18173             if (child.timeline) {
18174                 child.timeline._remove(child, true); //removes from existing timeline so that it can be properly added to this one.
18175             }
18176             child.timeline = child._timeline = this;
18177             if (child._gc) {
18178                 child._enabled(true, true);
18179             }
18180             prevTween = this._last;
18181             if (this._sortChildren) {
18182                 st = child._startTime;
18183                 while (prevTween && prevTween._startTime > st) {
18184                     prevTween = prevTween._prev;
18185                 }
18186             }
18187             if (prevTween) {
18188                 child._next = prevTween._next;
18189                 prevTween._next = child;
18190             } else {
18191                 child._next = this._first;
18192                 this._first = child;
18193             }
18194             if (child._next) {
18195                 child._next._prev = child;
18196             } else {
18197                 this._last = child;
18198             }
18199             child._prev = prevTween;
18200             if (this._timeline) {
18201                 this._uncache(true);
18202             }
18203             return this;
18204         };
18205
18206         p._remove = function(tween, skipDisable) {
18207             if (tween.timeline === this) {
18208                 if (!skipDisable) {
18209                     tween._enabled(false, true);
18210                 }
18211                 tween.timeline = null;
18212
18213                 if (tween._prev) {
18214                     tween._prev._next = tween._next;
18215                 } else if (this._first === tween) {
18216                     this._first = tween._next;
18217                 }
18218                 if (tween._next) {
18219                     tween._next._prev = tween._prev;
18220                 } else if (this._last === tween) {
18221                     this._last = tween._prev;
18222                 }
18223
18224                 if (this._timeline) {
18225                     this._uncache(true);
18226                 }
18227             }
18228             return this;
18229         };
18230
18231         p.render = function(time, suppressEvents, force) {
18232             var tween = this._first,
18233                 next;
18234             this._totalTime = this._time = this._rawPrevTime = time;
18235             while (tween) {
18236                 next = tween._next; //record it here because the value could change after rendering...
18237                 if (tween._active || (time >= tween._startTime && !tween._paused)) {
18238                     if (!tween._reversed) {
18239                         tween.render((time - tween._startTime) * tween._timeScale, suppressEvents, force);
18240                     } else {
18241                         tween.render(((!tween._dirty) ? tween._totalDuration : tween.totalDuration()) - ((time - tween._startTime) * tween._timeScale), suppressEvents, force);
18242                     }
18243                 }
18244                 tween = next;
18245             }
18246         };
18247
18248         p.rawTime = function() {
18249             if (!_tickerActive) {
18250                 _ticker.wake();
18251             }
18252             return this._totalTime;
18253         };
18254
18255 /*
18256  * ----------------------------------------------------------------
18257  * TweenLite
18258  * ----------------------------------------------------------------
18259  */
18260         var TweenLite = _class("TweenLite", function(target, duration, vars) {
18261                 Animation.call(this, duration, vars);
18262                 this.render = TweenLite.prototype.render; //speed optimization (avoid prototype lookup on this "hot" method)
18263
18264                 if (target == null) {
18265                     throw "Cannot tween a null target.";
18266                 }
18267
18268                 this.target = target = (typeof(target) !== "string") ? target : TweenLite.selector(target) || target;
18269
18270                 var isSelector = (target.jquery || (target.length && target !== window && target[0] && (target[0] === window || (target[0].nodeType && target[0].style && !target.nodeType)))),
18271                     overwrite = this.vars.overwrite,
18272                     i, targ, targets;
18273
18274                 this._overwrite = overwrite = (overwrite == null) ? _overwriteLookup[TweenLite.defaultOverwrite] : (typeof(overwrite) === "number") ? overwrite >> 0 : _overwriteLookup[overwrite];
18275
18276                 if ((isSelector || target instanceof Array || (target.push && _isArray(target))) && typeof(target[0]) !== "number") {
18277                     this._targets = targets = _slice.call(target, 0);
18278                     this._propLookup = [];
18279                     this._siblings = [];
18280                     for (i = 0; i < targets.length; i++) {
18281                         targ = targets[i];
18282                         if (!targ) {
18283                             targets.splice(i--, 1);
18284                             continue;
18285                         } else if (typeof(targ) === "string") {
18286                             targ = targets[i--] = TweenLite.selector(targ); //in case it's an array of strings
18287                             if (typeof(targ) === "string") {
18288                                 targets.splice(i+1, 1); //to avoid an endless loop (can't imagine why the selector would return a string, but just in case)
18289                             }
18290                             continue;
18291                         } else if (targ.length && targ !== window && targ[0] && (targ[0] === window || (targ[0].nodeType && targ[0].style && !targ.nodeType))) { //in case the user is passing in an array of selector objects (like jQuery objects), we need to check one more level and pull things out if necessary. Also note that <select> elements pass all the criteria regarding length and the first child having style, so we must also check to ensure the target isn't an HTML node itself.
18292                             targets.splice(i--, 1);
18293                             this._targets = targets = targets.concat(_slice.call(targ, 0));
18294                             continue;
18295                         }
18296                         this._siblings[i] = _register(targ, this, false);
18297                         if (overwrite === 1) if (this._siblings[i].length > 1) {
18298                             _applyOverwrite(targ, this, null, 1, this._siblings[i]);
18299                         }
18300                     }
18301
18302                 } else {
18303                     this._propLookup = {};
18304                     this._siblings = _register(target, this, false);
18305                     if (overwrite === 1) if (this._siblings.length > 1) {
18306                         _applyOverwrite(target, this, null, 1, this._siblings);
18307                     }
18308                 }
18309                 if (this.vars.immediateRender || (duration === 0 && this._delay === 0 && this.vars.immediateRender !== false)) {
18310                     this._time = -_tinyNum; //forces a render without having to set the render() "force" parameter to true because we want to allow lazying by default (using the "force" parameter always forces an immediate full render)
18311                     this.render(-this._delay);
18312                 }
18313             }, true),
18314             _isSelector = function(v) {
18315                 return (v.length && v !== window && v[0] && (v[0] === window || (v[0].nodeType && v[0].style && !v.nodeType))); //we cannot check "nodeType" if the target is window from within an iframe, otherwise it will trigger a security error in some browsers like Firefox.
18316             },
18317             _autoCSS = function(vars, target) {
18318                 var css = {},
18319                     p;
18320                 for (p in vars) {
18321                     if (!_reservedProps[p] && (!(p in target) || p === "transform" || p === "x" || p === "y" || p === "width" || p === "height" || p === "className" || p === "border") && (!_plugins[p] || (_plugins[p] && _plugins[p]._autoCSS))) { //note: <img> elements contain read-only "x" and "y" properties. We should also prioritize editing css width/height rather than the element's properties.
18322                         css[p] = vars[p];
18323                         delete vars[p];
18324                     }
18325                 }
18326                 vars.css = css;
18327             };
18328
18329         p = TweenLite.prototype = new Animation();
18330         p.constructor = TweenLite;
18331         p.kill()._gc = false;
18332
18333 //----TweenLite defaults, overwrite management, and root updates ----------------------------------------------------
18334
18335         p.ratio = 0;
18336         p._firstPT = p._targets = p._overwrittenProps = p._startAt = null;
18337         p._notifyPluginsOfEnabled = p._lazy = false;
18338
18339         TweenLite.version = "1.12.1";
18340         TweenLite.defaultEase = p._ease = new Ease(null, null, 1, 1);
18341         TweenLite.defaultOverwrite = "auto";
18342         TweenLite.ticker = _ticker;
18343         TweenLite.autoSleep = true;
18344         TweenLite.lagSmoothing = function(threshold, adjustedLag) {
18345             _ticker.lagSmoothing(threshold, adjustedLag);
18346         };
18347         TweenLite.selector = window.$ || window.jQuery || function(e) { if (window.$) { TweenLite.selector = window.$; return window.$(e); } return window.document ? window.document.getElementById((e.charAt(0) === "#") ? e.substr(1) : e) : e; };
18348
18349         var _lazyTweens = [],
18350             _lazyLookup = {},
18351             _internals = TweenLite._internals = {isArray:_isArray, isSelector:_isSelector, lazyTweens:_lazyTweens}, //gives us a way to expose certain private values to other GreenSock classes without contaminating tha main TweenLite object.
18352             _plugins = TweenLite._plugins = {},
18353             _tweenLookup = _internals.tweenLookup = {},
18354             _tweenLookupNum = 0,
18355             _reservedProps = _internals.reservedProps = {ease:1, delay:1, overwrite:1, onComplete:1, onCompleteParams:1, onCompleteScope:1, useFrames:1, runBackwards:1, startAt:1, onUpdate:1, onUpdateParams:1, onUpdateScope:1, onStart:1, onStartParams:1, onStartScope:1, onReverseComplete:1, onReverseCompleteParams:1, onReverseCompleteScope:1, onRepeat:1, onRepeatParams:1, onRepeatScope:1, easeParams:1, yoyo:1, immediateRender:1, repeat:1, repeatDelay:1, data:1, paused:1, reversed:1, autoCSS:1, lazy:1},
18356             _overwriteLookup = {none:0, all:1, auto:2, concurrent:3, allOnStart:4, preexisting:5, "true":1, "false":0},
18357             _rootFramesTimeline = Animation._rootFramesTimeline = new SimpleTimeline(),
18358             _rootTimeline = Animation._rootTimeline = new SimpleTimeline(),
18359             _lazyRender = function() {
18360                 var i = _lazyTweens.length;
18361                 _lazyLookup = {};
18362                 while (--i > -1) {
18363                     a = _lazyTweens[i];
18364                     if (a && a._lazy !== false) {
18365                         a.render(a._lazy, false, true);
18366                         a._lazy = false;
18367                     }
18368                 }
18369                 _lazyTweens.length = 0;
18370             };
18371
18372         _rootTimeline._startTime = _ticker.time;
18373         _rootFramesTimeline._startTime = _ticker.frame;
18374         _rootTimeline._active = _rootFramesTimeline._active = true;
18375         setTimeout(_lazyRender, 1); //on some mobile devices, there isn't a "tick" before code runs which means any lazy renders wouldn't run before the next official "tick".
18376
18377         Animation._updateRoot = TweenLite.render = function() {
18378                 var i, a, p;
18379                 if (_lazyTweens.length) { //if code is run outside of the requestAnimationFrame loop, there may be tweens queued AFTER the engine refreshed, so we need to ensure any pending renders occur before we refresh again.
18380                     _lazyRender();
18381                 }
18382                 _rootTimeline.render((_ticker.time - _rootTimeline._startTime) * _rootTimeline._timeScale, false, false);
18383                 _rootFramesTimeline.render((_ticker.frame - _rootFramesTimeline._startTime) * _rootFramesTimeline._timeScale, false, false);
18384                 if (_lazyTweens.length) {
18385                     _lazyRender();
18386                 }
18387                 if (!(_ticker.frame % 120)) { //dump garbage every 120 frames...
18388                     for (p in _tweenLookup) {
18389                         a = _tweenLookup[p].tweens;
18390                         i = a.length;
18391                         while (--i > -1) {
18392                             if (a[i]._gc) {
18393                                 a.splice(i, 1);
18394                             }
18395                         }
18396                         if (a.length === 0) {
18397                             delete _tweenLookup[p];
18398                         }
18399                     }
18400                     //if there are no more tweens in the root timelines, or if they're all paused, make the _timer sleep to reduce load on the CPU slightly
18401                     p = _rootTimeline._first;
18402                     if (!p || p._paused) if (TweenLite.autoSleep && !_rootFramesTimeline._first && _ticker._listeners.tick.length === 1) {
18403                         while (p && p._paused) {
18404                             p = p._next;
18405                         }
18406                         if (!p) {
18407                             _ticker.sleep();
18408                         }
18409                     }
18410                 }
18411             };
18412
18413         _ticker.addEventListener("tick", Animation._updateRoot);
18414
18415         var _register = function(target, tween, scrub) {
18416                 var id = target._gsTweenID, a, i;
18417                 if (!_tweenLookup[id || (target._gsTweenID = id = "t" + (_tweenLookupNum++))]) {
18418                     _tweenLookup[id] = {target:target, tweens:[]};
18419                 }
18420                 if (tween) {
18421                     a = _tweenLookup[id].tweens;
18422                     a[(i = a.length)] = tween;
18423                     if (scrub) {
18424                         while (--i > -1) {
18425                             if (a[i] === tween) {
18426                                 a.splice(i, 1);
18427                             }
18428                         }
18429                     }
18430                 }
18431                 return _tweenLookup[id].tweens;
18432             },
18433
18434             _applyOverwrite = function(target, tween, props, mode, siblings) {
18435                 var i, changed, curTween, l;
18436                 if (mode === 1 || mode >= 4) {
18437                     l = siblings.length;
18438                     for (i = 0; i < l; i++) {
18439                         if ((curTween = siblings[i]) !== tween) {
18440                             if (!curTween._gc) if (curTween._enabled(false, false)) {
18441                                 changed = true;
18442                             }
18443                         } else if (mode === 5) {
18444                             break;
18445                         }
18446                     }
18447                     return changed;
18448                 }
18449                 //NOTE: Add 0.0000000001 to overcome floating point errors that can cause the startTime to be VERY slightly off (when a tween's time() is set for example)
18450                 var startTime = tween._startTime + _tinyNum,
18451                     overlaps = [],
18452                     oCount = 0,
18453                     zeroDur = (tween._duration === 0),
18454                     globalStart;
18455                 i = siblings.length;
18456                 while (--i > -1) {
18457                     if ((curTween = siblings[i]) === tween || curTween._gc || curTween._paused) {
18458                         //ignore
18459                     } else if (curTween._timeline !== tween._timeline) {
18460                         globalStart = globalStart || _checkOverlap(tween, 0, zeroDur);
18461                         if (_checkOverlap(curTween, globalStart, zeroDur) === 0) {
18462                             overlaps[oCount++] = curTween;
18463                         }
18464                     } else if (curTween._startTime <= startTime) if (curTween._startTime + curTween.totalDuration() / curTween._timeScale > startTime) if (!((zeroDur || !curTween._initted) && startTime - curTween._startTime <= 0.0000000002)) {
18465                         overlaps[oCount++] = curTween;
18466                     }
18467                 }
18468
18469                 i = oCount;
18470                 while (--i > -1) {
18471                     curTween = overlaps[i];
18472                     if (mode === 2) if (curTween._kill(props, target)) {
18473                         changed = true;
18474                     }
18475                     if (mode !== 2 || (!curTween._firstPT && curTween._initted)) {
18476                         if (curTween._enabled(false, false)) { //if all property tweens have been overwritten, kill the tween.
18477                             changed = true;
18478                         }
18479                     }
18480                 }
18481                 return changed;
18482             },
18483
18484             _checkOverlap = function(tween, reference, zeroDur) {
18485                 var tl = tween._timeline,
18486                     ts = tl._timeScale,
18487                     t = tween._startTime;
18488                 while (tl._timeline) {
18489                     t += tl._startTime;
18490                     ts *= tl._timeScale;
18491                     if (tl._paused) {
18492                         return -100;
18493                     }
18494                     tl = tl._timeline;
18495                 }
18496                 t /= ts;
18497                 return (t > reference) ? t - reference : ((zeroDur && t === reference) || (!tween._initted && t - reference < 2 * _tinyNum)) ? _tinyNum : ((t += tween.totalDuration() / tween._timeScale / ts) > reference + _tinyNum) ? 0 : t - reference - _tinyNum;
18498             };
18499
18500
18501 //---- TweenLite instance methods -----------------------------------------------------------------------------
18502
18503         p._init = function() {
18504             var v = this.vars,
18505                 op = this._overwrittenProps,
18506                 dur = this._duration,
18507                 immediate = !!v.immediateRender,
18508                 ease = v.ease,
18509                 i, initPlugins, pt, p, startVars;
18510             if (v.startAt) {
18511                 if (this._startAt) {
18512                     this._startAt.render(-1, true); //if we've run a startAt previously (when the tween instantiated), we should revert it so that the values re-instantiate correctly particularly for relative tweens. Without this, a TweenLite.fromTo(obj, 1, {x:"+=100"}, {x:"-=100"}), for example, would actually jump to +=200 because the startAt would run twice, doubling the relative change.
18513                     this._startAt.kill();
18514                 }
18515                 startVars = {};
18516                 for (p in v.startAt) { //copy the properties/values into a new object to avoid collisions, like var to = {x:0}, from = {x:500}; timeline.fromTo(e, 1, from, to).fromTo(e, 1, to, from);
18517                     startVars[p] = v.startAt[p];
18518                 }
18519                 startVars.overwrite = false;
18520                 startVars.immediateRender = true;
18521                 startVars.lazy = (immediate && v.lazy !== false);
18522                 startVars.startAt = startVars.delay = null; //no nesting of startAt objects allowed (otherwise it could cause an infinite loop).
18523                 this._startAt = TweenLite.to(this.target, 0, startVars);
18524                 if (immediate) {
18525                     if (this._time > 0) {
18526                         this._startAt = null; //tweens that render immediately (like most from() and fromTo() tweens) shouldn't revert when their parent timeline's playhead goes backward past the startTime because the initial render could have happened anytime and it shouldn't be directly correlated to this tween's startTime. Imagine setting up a complex animation where the beginning states of various objects are rendered immediately but the tween doesn't happen for quite some time - if we revert to the starting values as soon as the playhead goes backward past the tween's startTime, it will throw things off visually. Reversion should only happen in TimelineLite/Max instances where immediateRender was false (which is the default in the convenience methods like from()).
18527                     } else if (dur !== 0) {
18528                         return; //we skip initialization here so that overwriting doesn't occur until the tween actually begins. Otherwise, if you create several immediateRender:true tweens of the same target/properties to drop into a TimelineLite or TimelineMax, the last one created would overwrite the first ones because they didn't get placed into the timeline yet before the first render occurs and kicks in overwriting.
18529                     }
18530                 }
18531             } else if (v.runBackwards && dur !== 0) {
18532                 //from() tweens must be handled uniquely: their beginning values must be rendered but we don't want overwriting to occur yet (when time is still 0). Wait until the tween actually begins before doing all the routines like overwriting. At that time, we should render at the END of the tween to ensure that things initialize correctly (remember, from() tweens go backwards)
18533                 if (this._startAt) {
18534                     this._startAt.render(-1, true);
18535                     this._startAt.kill();
18536                     this._startAt = null;
18537                 } else {
18538                     pt = {};
18539                     for (p in v) { //copy props into a new object and skip any reserved props, otherwise onComplete or onUpdate or onStart could fire. We should, however, permit autoCSS to go through.
18540                         if (!_reservedProps[p] || p === "autoCSS") {
18541                             pt[p] = v[p];
18542                         }
18543                     }
18544                     pt.overwrite = 0;
18545                     pt.data = "isFromStart"; //we tag the tween with as "isFromStart" so that if [inside a plugin] we need to only do something at the very END of a tween, we have a way of identifying this tween as merely the one that's setting the beginning values for a "from()" tween. For example, clearProps in CSSPlugin should only get applied at the very END of a tween and without this tag, from(...{height:100, clearProps:"height", delay:1}) would wipe the height at the beginning of the tween and after 1 second, it'd kick back in.
18546                     pt.lazy = (immediate && v.lazy !== false);
18547                     pt.immediateRender = immediate; //zero-duration tweens render immediately by default, but if we're not specifically instructed to render this tween immediately, we should skip this and merely _init() to record the starting values (rendering them immediately would push them to completion which is wasteful in that case - we'd have to render(-1) immediately after)
18548                     this._startAt = TweenLite.to(this.target, 0, pt);
18549                     if (!immediate) {
18550                         this._startAt._init(); //ensures that the initial values are recorded
18551                         this._startAt._enabled(false); //no need to have the tween render on the next cycle. Disable it because we'll always manually control the renders of the _startAt tween.
18552                     } else if (this._time === 0) {
18553                         return;
18554                     }
18555                 }
18556             }
18557             if (!ease) {
18558                 this._ease = TweenLite.defaultEase;
18559             } else if (ease instanceof Ease) {
18560                 this._ease = (v.easeParams instanceof Array) ? ease.config.apply(ease, v.easeParams) : ease;
18561             } else {
18562                 this._ease = (typeof(ease) === "function") ? new Ease(ease, v.easeParams) : _easeMap[ease] || TweenLite.defaultEase;
18563             }
18564             this._easeType = this._ease._type;
18565             this._easePower = this._ease._power;
18566             this._firstPT = null;
18567
18568             if (this._targets) {
18569                 i = this._targets.length;
18570                 while (--i > -1) {
18571                     if ( this._initProps( this._targets[i], (this._propLookup[i] = {}), this._siblings[i], (op ? op[i] : null)) ) {
18572                         initPlugins = true;
18573                     }
18574                 }
18575             } else {
18576                 initPlugins = this._initProps(this.target, this._propLookup, this._siblings, op);
18577             }
18578
18579             if (initPlugins) {
18580                 TweenLite._onPluginEvent("_onInitAllProps", this); //reorders the array in order of priority. Uses a static TweenPlugin method in order to minimize file size in TweenLite
18581             }
18582             if (op) if (!this._firstPT) if (typeof(this.target) !== "function") { //if all tweening properties have been overwritten, kill the tween. If the target is a function, it's probably a delayedCall so let it live.
18583                 this._enabled(false, false);
18584             }
18585             if (v.runBackwards) {
18586                 pt = this._firstPT;
18587                 while (pt) {
18588                     pt.s += pt.c;
18589                     pt.c = -pt.c;
18590                     pt = pt._next;
18591                 }
18592             }
18593             this._onUpdate = v.onUpdate;
18594             this._initted = true;
18595         };
18596
18597         p._initProps = function(target, propLookup, siblings, overwrittenProps) {
18598             var p, i, initPlugins, plugin, pt, v;
18599             if (target == null) {
18600                 return false;
18601             }
18602
18603             if (_lazyLookup[target._gsTweenID]) {
18604                 _lazyRender(); //if other tweens of the same target have recently initted but haven't rendered yet, we've got to force the render so that the starting values are correct (imagine populating a timeline with a bunch of sequential tweens and then jumping to the end)
18605             }
18606
18607             if (!this.vars.css) if (target.style) if (target !== window && target.nodeType) if (_plugins.css) if (this.vars.autoCSS !== false) { //it's so common to use TweenLite/Max to animate the css of DOM elements, we assume that if the target is a DOM element, that's what is intended (a convenience so that users don't have to wrap things in css:{}, although we still recommend it for a slight performance boost and better specificity). Note: we cannot check "nodeType" on the window inside an iframe.
18608                 _autoCSS(this.vars, target);
18609             }
18610             for (p in this.vars) {
18611                 v = this.vars[p];
18612                 if (_reservedProps[p]) {
18613                     if (v) if ((v instanceof Array) || (v.push && _isArray(v))) if (v.join("").indexOf("{self}") !== -1) {
18614                         this.vars[p] = v = this._swapSelfInParams(v, this);
18615                     }
18616
18617                 } else if (_plugins[p] && (plugin = new _plugins[p]())._onInitTween(target, this.vars[p], this)) {
18618
18619                     //t - target        [object]
18620                     //p - property      [string]
18621                     //s - start         [number]
18622                     //c - change        [number]
18623                     //f - isFunction    [boolean]
18624                     //n - name          [string]
18625                     //pg - isPlugin     [boolean]
18626                     //pr - priority     [number]
18627                     this._firstPT = pt = {_next:this._firstPT, t:plugin, p:"setRatio", s:0, c:1, f:true, n:p, pg:true, pr:plugin._priority};
18628                     i = plugin._overwriteProps.length;
18629                     while (--i > -1) {
18630                         propLookup[plugin._overwriteProps[i]] = this._firstPT;
18631                     }
18632                     if (plugin._priority || plugin._onInitAllProps) {
18633                         initPlugins = true;
18634                     }
18635                     if (plugin._onDisable || plugin._onEnable) {
18636                         this._notifyPluginsOfEnabled = true;
18637                     }
18638
18639                 } else {
18640                     this._firstPT = propLookup[p] = pt = {_next:this._firstPT, t:target, p:p, f:(typeof(target[p]) === "function"), n:p, pg:false, pr:0};
18641                     pt.s = (!pt.f) ? parseFloat(target[p]) : target[ ((p.indexOf("set") || typeof(target["get" + p.substr(3)]) !== "function") ? p : "get" + p.substr(3)) ]();
18642                     pt.c = (typeof(v) === "string" && v.charAt(1) === "=") ? parseInt(v.charAt(0) + "1", 10) * Number(v.substr(2)) : (Number(v) - pt.s) || 0;
18643                 }
18644                 if (pt) if (pt._next) {
18645                     pt._next._prev = pt;
18646                 }
18647             }
18648
18649             if (overwrittenProps) if (this._kill(overwrittenProps, target)) { //another tween may have tried to overwrite properties of this tween before init() was called (like if two tweens start at the same time, the one created second will run first)
18650                 return this._initProps(target, propLookup, siblings, overwrittenProps);
18651             }
18652             if (this._overwrite > 1) if (this._firstPT) if (siblings.length > 1) if (_applyOverwrite(target, this, propLookup, this._overwrite, siblings)) {
18653                 this._kill(propLookup, target);
18654                 return this._initProps(target, propLookup, siblings, overwrittenProps);
18655             }
18656             if (this._firstPT) if ((this.vars.lazy !== false && this._duration) || (this.vars.lazy && !this._duration)) { //zero duration tweens don't lazy render by default; everything else does.
18657                 _lazyLookup[target._gsTweenID] = true;
18658             }
18659             return initPlugins;
18660         };
18661
18662         p.render = function(time, suppressEvents, force) {
18663             var prevTime = this._time,
18664                 duration = this._duration,
18665                 prevRawPrevTime = this._rawPrevTime,
18666                 isComplete, callback, pt, rawPrevTime;
18667             if (time >= duration) {
18668                 this._totalTime = this._time = duration;
18669                 this.ratio = this._ease._calcEnd ? this._ease.getRatio(1) : 1;
18670                 if (!this._reversed ) {
18671                     isComplete = true;
18672                     callback = "onComplete";
18673                 }
18674                 if (duration === 0) if (this._initted || !this.vars.lazy || force) { //zero-duration tweens are tricky because we must discern the momentum/direction of time in order to determine whether the starting values should be rendered or the ending values. If the "playhead" of its timeline goes past the zero-duration tween in the forward direction or lands directly on it, the end values should be rendered, but if the timeline's "playhead" moves past it in the backward direction (from a postitive time to a negative time), the starting values must be rendered.
18675                     if (this._startTime === this._timeline._duration) { //if a zero-duration tween is at the VERY end of a timeline and that timeline renders at its end, it will typically add a tiny bit of cushion to the render time to prevent rounding errors from getting in the way of tweens rendering their VERY end. If we then reverse() that timeline, the zero-duration tween will trigger its onReverseComplete even though technically the playhead didn't pass over it again. It's a very specific edge case we must accommodate.
18676                         time = 0;
18677                     }
18678                     if (time === 0 || prevRawPrevTime < 0 || prevRawPrevTime === _tinyNum) if (prevRawPrevTime !== time) {
18679                         force = true;
18680                         if (prevRawPrevTime > _tinyNum) {
18681                             callback = "onReverseComplete";
18682                         }
18683                     }
18684                     this._rawPrevTime = rawPrevTime = (!suppressEvents || time || prevRawPrevTime === time) ? time : _tinyNum; //when the playhead arrives at EXACTLY time 0 (right on top) of a zero-duration tween, we need to discern if events are suppressed so that when the playhead moves again (next time), it'll trigger the callback. If events are NOT suppressed, obviously the callback would be triggered in this render. Basically, the callback should fire either when the playhead ARRIVES or LEAVES this exact spot, not both. Imagine doing a timeline.seek(0) and there's a callback that sits at 0. Since events are suppressed on that seek() by default, nothing will fire, but when the playhead moves off of that position, the callback should fire. This behavior is what people intuitively expect. We set the _rawPrevTime to be a precise tiny number to indicate this scenario rather than using another property/variable which would increase memory usage. This technique is less readable, but more efficient.
18685                 }
18686
18687             } else if (time < 0.0000001) { //to work around occasional floating point math artifacts, round super small values to 0.
18688                 this._totalTime = this._time = 0;
18689                 this.ratio = this._ease._calcEnd ? this._ease.getRatio(0) : 0;
18690                 if (prevTime !== 0 || (duration === 0 && prevRawPrevTime > 0 && prevRawPrevTime !== _tinyNum)) {
18691                     callback = "onReverseComplete";
18692                     isComplete = this._reversed;
18693                 }
18694                 if (time < 0) {
18695                     this._active = false;
18696                     if (duration === 0) if (this._initted || !this.vars.lazy || force) { //zero-duration tweens are tricky because we must discern the momentum/direction of time in order to determine whether the starting values should be rendered or the ending values. If the "playhead" of its timeline goes past the zero-duration tween in the forward direction or lands directly on it, the end values should be rendered, but if the timeline's "playhead" moves past it in the backward direction (from a postitive time to a negative time), the starting values must be rendered.
18697                         if (prevRawPrevTime >= 0) {
18698                             force = true;
18699                         }
18700                         this._rawPrevTime = rawPrevTime = (!suppressEvents || time || prevRawPrevTime === time) ? time : _tinyNum; //when the playhead arrives at EXACTLY time 0 (right on top) of a zero-duration tween, we need to discern if events are suppressed so that when the playhead moves again (next time), it'll trigger the callback. If events are NOT suppressed, obviously the callback would be triggered in this render. Basically, the callback should fire either when the playhead ARRIVES or LEAVES this exact spot, not both. Imagine doing a timeline.seek(0) and there's a callback that sits at 0. Since events are suppressed on that seek() by default, nothing will fire, but when the playhead moves off of that position, the callback should fire. This behavior is what people intuitively expect. We set the _rawPrevTime to be a precise tiny number to indicate this scenario rather than using another property/variable which would increase memory usage. This technique is less readable, but more efficient.
18701                     }
18702                 } else if (!this._initted) { //if we render the very beginning (time == 0) of a fromTo(), we must force the render (normal tweens wouldn't need to render at a time of 0 when the prevTime was also 0). This is also mandatory to make sure overwriting kicks in immediately.
18703                     force = true;
18704                 }
18705             } else {
18706                 this._totalTime = this._time = time;
18707
18708                 if (this._easeType) {
18709                     var r = time / duration, type = this._easeType, pow = this._easePower;
18710                     if (type === 1 || (type === 3 && r >= 0.5)) {
18711                         r = 1 - r;
18712                     }
18713                     if (type === 3) {
18714                         r *= 2;
18715                     }
18716                     if (pow === 1) {
18717                         r *= r;
18718                     } else if (pow === 2) {
18719                         r *= r * r;
18720                     } else if (pow === 3) {
18721                         r *= r * r * r;
18722                     } else if (pow === 4) {
18723                         r *= r * r * r * r;
18724                     }
18725
18726                     if (type === 1) {
18727                         this.ratio = 1 - r;
18728                     } else if (type === 2) {
18729                         this.ratio = r;
18730                     } else if (time / duration < 0.5) {
18731                         this.ratio = r / 2;
18732                     } else {
18733                         this.ratio = 1 - (r / 2);
18734                     }
18735
18736                 } else {
18737                     this.ratio = this._ease.getRatio(time / duration);
18738                 }
18739             }
18740
18741             if (this._time === prevTime && !force) {
18742                 return;
18743             } else if (!this._initted) {
18744                 this._init();
18745                 if (!this._initted || this._gc) { //immediateRender tweens typically won't initialize until the playhead advances (_time is greater than 0) in order to ensure that overwriting occurs properly. Also, if all of the tweening properties have been overwritten (which would cause _gc to be true, as set in _init()), we shouldn't continue otherwise an onStart callback could be called for example.
18746                     return;
18747                 } else if (!force && this._firstPT && ((this.vars.lazy !== false && this._duration) || (this.vars.lazy && !this._duration))) {
18748                     this._time = this._totalTime = prevTime;
18749                     this._rawPrevTime = prevRawPrevTime;
18750                     _lazyTweens.push(this);
18751                     this._lazy = time;
18752                     return;
18753                 }
18754                 //_ease is initially set to defaultEase, so now that init() has run, _ease is set properly and we need to recalculate the ratio. Overall this is faster than using conditional logic earlier in the method to avoid having to set ratio twice because we only init() once but renderTime() gets called VERY frequently.
18755                 if (this._time && !isComplete) {
18756                     this.ratio = this._ease.getRatio(this._time / duration);
18757                 } else if (isComplete && this._ease._calcEnd) {
18758                     this.ratio = this._ease.getRatio((this._time === 0) ? 0 : 1);
18759                 }
18760             }
18761             if (this._lazy !== false) { //in case a lazy render is pending, we should flush it because the new render is occuring now (imagine a lazy tween instantiating and then immediately the user calls tween.seek(tween.duration()), skipping to the end - the end render would be forced, and then if we didn't flush the lazy render, it'd fire AFTER the seek(), rendering it at the wrong time.
18762                 this._lazy = false;
18763             }
18764             if (!this._active) if (!this._paused && this._time !== prevTime && time >= 0) {
18765                 this._active = true;  //so that if the user renders a tween (as opposed to the timeline rendering it), the timeline is forced to re-render and align it with the proper time/frame on the next rendering cycle. Maybe the tween already finished but the user manually re-renders it as halfway done.
18766             }
18767             if (prevTime === 0) {
18768                 if (this._startAt) {
18769                     if (time >= 0) {
18770                         this._startAt.render(time, suppressEvents, force);
18771                     } else if (!callback) {
18772                         callback = "_dummyGS"; //if no callback is defined, use a dummy value just so that the condition at the end evaluates as true because _startAt should render AFTER the normal render loop when the time is negative. We could handle this in a more intuitive way, of course, but the render loop is the MOST important thing to optimize, so this technique allows us to avoid adding extra conditional logic in a high-frequency area.
18773                     }
18774                 }
18775                 if (this.vars.onStart) if (this._time !== 0 || duration === 0) if (!suppressEvents) {
18776                     this.vars.onStart.apply(this.vars.onStartScope || this, this.vars.onStartParams || _blankArray);
18777                 }
18778             }
18779
18780             pt = this._firstPT;
18781             while (pt) {
18782                 if (pt.f) {
18783                     pt.t[pt.p](pt.c * this.ratio + pt.s);
18784                 } else {
18785                     pt.t[pt.p] = pt.c * this.ratio + pt.s;
18786                 }
18787                 pt = pt._next;
18788             }
18789
18790             if (this._onUpdate) {
18791                 if (time < 0) if (this._startAt && this._startTime) { //if the tween is positioned at the VERY beginning (_startTime 0) of its parent timeline, it's illegal for the playhead to go back further, so we should not render the recorded startAt values.
18792                     this._startAt.render(time, suppressEvents, force); //note: for performance reasons, we tuck this conditional logic inside less traveled areas (most tweens don't have an onUpdate). We'd just have it at the end before the onComplete, but the values should be updated before any onUpdate is called, so we ALSO put it here and then if it's not called, we do so later near the onComplete.
18793                 }
18794                 if (!suppressEvents) if (this._time !== prevTime || isComplete) {
18795                     this._onUpdate.apply(this.vars.onUpdateScope || this, this.vars.onUpdateParams || _blankArray);
18796                 }
18797             }
18798
18799             if (callback) if (!this._gc) { //check _gc because there's a chance that kill() could be called in an onUpdate
18800                 if (time < 0 && this._startAt && !this._onUpdate && this._startTime) { //if the tween is positioned at the VERY beginning (_startTime 0) of its parent timeline, it's illegal for the playhead to go back further, so we should not render the recorded startAt values.
18801                     this._startAt.render(time, suppressEvents, force);
18802                 }
18803                 if (isComplete) {
18804                     if (this._timeline.autoRemoveChildren) {
18805                         this._enabled(false, false);
18806                     }
18807                     this._active = false;
18808                 }
18809                 if (!suppressEvents && this.vars[callback]) {
18810                     this.vars[callback].apply(this.vars[callback + "Scope"] || this, this.vars[callback + "Params"] || _blankArray);
18811                 }
18812                 if (duration === 0 && this._rawPrevTime === _tinyNum && rawPrevTime !== _tinyNum) { //the onComplete or onReverseComplete could trigger movement of the playhead and for zero-duration tweens (which must discern direction) that land directly back on their start time, we don't want to fire again on the next render. Think of several addPause()'s in a timeline that forces the playhead to a certain spot, but what if it's already paused and another tween is tweening the "time" of the timeline? Each time it moves [forward] past that spot, it would move back, and since suppressEvents is true, it'd reset _rawPrevTime to _tinyNum so that when it begins again, the callback would fire (so ultimately it could bounce back and forth during that tween). Again, this is a very uncommon scenario, but possible nonetheless.
18813                     this._rawPrevTime = 0;
18814                 }
18815             }
18816
18817         };
18818
18819         p._kill = function(vars, target) {
18820             if (vars === "all") {
18821                 vars = null;
18822             }
18823             if (vars == null) if (target == null || target === this.target) {
18824                 this._lazy = false;
18825                 return this._enabled(false, false);
18826             }
18827             target = (typeof(target) !== "string") ? (target || this._targets || this.target) : TweenLite.selector(target) || target;
18828             var i, overwrittenProps, p, pt, propLookup, changed, killProps, record;
18829             if ((_isArray(target) || _isSelector(target)) && typeof(target[0]) !== "number") {
18830                 i = target.length;
18831                 while (--i > -1) {
18832                     if (this._kill(vars, target[i])) {
18833                         changed = true;
18834                     }
18835                 }
18836             } else {
18837                 if (this._targets) {
18838                     i = this._targets.length;
18839                     while (--i > -1) {
18840                         if (target === this._targets[i]) {
18841                             propLookup = this._propLookup[i] || {};
18842                             this._overwrittenProps = this._overwrittenProps || [];
18843                             overwrittenProps = this._overwrittenProps[i] = vars ? this._overwrittenProps[i] || {} : "all";
18844                             break;
18845                         }
18846                     }
18847                 } else if (target !== this.target) {
18848                     return false;
18849                 } else {
18850                     propLookup = this._propLookup;
18851                     overwrittenProps = this._overwrittenProps = vars ? this._overwrittenProps || {} : "all";
18852                 }
18853
18854                 if (propLookup) {
18855                     killProps = vars || propLookup;
18856                     record = (vars !== overwrittenProps && overwrittenProps !== "all" && vars !== propLookup && (typeof(vars) !== "object" || !vars._tempKill)); //_tempKill is a super-secret way to delete a particular tweening property but NOT have it remembered as an official overwritten property (like in BezierPlugin)
18857                     for (p in killProps) {
18858                         if ((pt = propLookup[p])) {
18859                             if (pt.pg && pt.t._kill(killProps)) {
18860                                 changed = true; //some plugins need to be notified so they can perform cleanup tasks first
18861                             }
18862                             if (!pt.pg || pt.t._overwriteProps.length === 0) {
18863                                 if (pt._prev) {
18864                                     pt._prev._next = pt._next;
18865                                 } else if (pt === this._firstPT) {
18866                                     this._firstPT = pt._next;
18867                                 }
18868                                 if (pt._next) {
18869                                     pt._next._prev = pt._prev;
18870                                 }
18871                                 pt._next = pt._prev = null;
18872                             }
18873                             delete propLookup[p];
18874                         }
18875                         if (record) {
18876                             overwrittenProps[p] = 1;
18877                         }
18878                     }
18879                     if (!this._firstPT && this._initted) { //if all tweening properties are killed, kill the tween. Without this line, if there's a tween with multiple targets and then you killTweensOf() each target individually, the tween would technically still remain active and fire its onComplete even though there aren't any more properties tweening.
18880                         this._enabled(false, false);
18881                     }
18882                 }
18883             }
18884             return changed;
18885         };
18886
18887         p.invalidate = function() {
18888             if (this._notifyPluginsOfEnabled) {
18889                 TweenLite._onPluginEvent("_onDisable", this);
18890             }
18891             this._firstPT = null;
18892             this._overwrittenProps = null;
18893             this._onUpdate = null;
18894             this._startAt = null;
18895             this._initted = this._active = this._notifyPluginsOfEnabled = this._lazy = false;
18896             this._propLookup = (this._targets) ? {} : [];
18897             return this;
18898         };
18899
18900         p._enabled = function(enabled, ignoreTimeline) {
18901             if (!_tickerActive) {
18902                 _ticker.wake();
18903             }
18904             if (enabled && this._gc) {
18905                 var targets = this._targets,
18906                     i;
18907                 if (targets) {
18908                     i = targets.length;
18909                     while (--i > -1) {
18910                         this._siblings[i] = _register(targets[i], this, true);
18911                     }
18912                 } else {
18913                     this._siblings = _register(this.target, this, true);
18914                 }
18915             }
18916             Animation.prototype._enabled.call(this, enabled, ignoreTimeline);
18917             if (this._notifyPluginsOfEnabled) if (this._firstPT) {
18918                 return TweenLite._onPluginEvent((enabled ? "_onEnable" : "_onDisable"), this);
18919             }
18920             return false;
18921         };
18922
18923
18924 //----TweenLite static methods -----------------------------------------------------
18925
18926         TweenLite.to = function(target, duration, vars) {
18927             return new TweenLite(target, duration, vars);
18928         };
18929
18930         TweenLite.from = function(target, duration, vars) {
18931             vars.runBackwards = true;
18932             vars.immediateRender = (vars.immediateRender != false);
18933             return new TweenLite(target, duration, vars);
18934         };
18935
18936         TweenLite.fromTo = function(target, duration, fromVars, toVars) {
18937             toVars.startAt = fromVars;
18938             toVars.immediateRender = (toVars.immediateRender != false && fromVars.immediateRender != false);
18939             return new TweenLite(target, duration, toVars);
18940         };
18941
18942         TweenLite.delayedCall = function(delay, callback, params, scope, useFrames) {
18943             return new TweenLite(callback, 0, {delay:delay, onComplete:callback, onCompleteParams:params, onCompleteScope:scope, onReverseComplete:callback, onReverseCompleteParams:params, onReverseCompleteScope:scope, immediateRender:false, useFrames:useFrames, overwrite:0});
18944         };
18945
18946         TweenLite.set = function(target, vars) {
18947             return new TweenLite(target, 0, vars);
18948         };
18949
18950         TweenLite.getTweensOf = function(target, onlyActive) {
18951             if (target == null) { return []; }
18952             target = (typeof(target) !== "string") ? target : TweenLite.selector(target) || target;
18953             var i, a, j, t;
18954             if ((_isArray(target) || _isSelector(target)) && typeof(target[0]) !== "number") {
18955                 i = target.length;
18956                 a = [];
18957                 while (--i > -1) {
18958                     a = a.concat(TweenLite.getTweensOf(target[i], onlyActive));
18959                 }
18960                 i = a.length;
18961                 //now get rid of any duplicates (tweens of arrays of objects could cause duplicates)
18962                 while (--i > -1) {
18963                     t = a[i];
18964                     j = i;
18965                     while (--j > -1) {
18966                         if (t === a[j]) {
18967                             a.splice(i, 1);
18968                         }
18969                     }
18970                 }
18971             } else {
18972                 a = _register(target).concat();
18973                 i = a.length;
18974                 while (--i > -1) {
18975                     if (a[i]._gc || (onlyActive && !a[i].isActive())) {
18976                         a.splice(i, 1);
18977                     }
18978                 }
18979             }
18980             return a;
18981         };
18982
18983         TweenLite.killTweensOf = TweenLite.killDelayedCallsTo = function(target, onlyActive, vars) {
18984             if (typeof(onlyActive) === "object") {
18985                 vars = onlyActive; //for backwards compatibility (before "onlyActive" parameter was inserted)
18986                 onlyActive = false;
18987             }
18988             var a = TweenLite.getTweensOf(target, onlyActive),
18989                 i = a.length;
18990             while (--i > -1) {
18991                 a[i]._kill(vars, target);
18992             }
18993         };
18994
18995
18996
18997 /*
18998  * ----------------------------------------------------------------
18999  * TweenPlugin   (could easily be split out as a separate file/class, but included for ease of use (so that people don't need to include another <script> call before loading plugins which is easy to forget)
19000  * ----------------------------------------------------------------
19001  */
19002         var TweenPlugin = _class("plugins.TweenPlugin", function(props, priority) {
19003                     this._overwriteProps = (props || "").split(",");
19004                     this._propName = this._overwriteProps[0];
19005                     this._priority = priority || 0;
19006                     this._super = TweenPlugin.prototype;
19007                 }, true);
19008
19009         p = TweenPlugin.prototype;
19010         TweenPlugin.version = "1.10.1";
19011         TweenPlugin.API = 2;
19012         p._firstPT = null;
19013
19014         p._addTween = function(target, prop, start, end, overwriteProp, round) {
19015             var c, pt;
19016             if (end != null && (c = (typeof(end) === "number" || end.charAt(1) !== "=") ? Number(end) - start : parseInt(end.charAt(0) + "1", 10) * Number(end.substr(2)))) {
19017                 this._firstPT = pt = {_next:this._firstPT, t:target, p:prop, s:start, c:c, f:(typeof(target[prop]) === "function"), n:overwriteProp || prop, r:round};
19018                 if (pt._next) {
19019                     pt._next._prev = pt;
19020                 }
19021                 return pt;
19022             }
19023         };
19024
19025         p.setRatio = function(v) {
19026             var pt = this._firstPT,
19027                 min = 0.000001,
19028                 val;
19029             while (pt) {
19030                 val = pt.c * v + pt.s;
19031                 if (pt.r) {
19032                     val = Math.round(val);
19033                 } else if (val < min) if (val > -min) { //prevents issues with converting very small numbers to strings in the browser
19034                     val = 0;
19035                 }
19036                 if (pt.f) {
19037                     pt.t[pt.p](val);
19038                 } else {
19039                     pt.t[pt.p] = val;
19040                 }
19041                 pt = pt._next;
19042             }
19043         };
19044
19045         p._kill = function(lookup) {
19046             var a = this._overwriteProps,
19047                 pt = this._firstPT,
19048                 i;
19049             if (lookup[this._propName] != null) {
19050                 this._overwriteProps = [];
19051             } else {
19052                 i = a.length;
19053                 while (--i > -1) {
19054                     if (lookup[a[i]] != null) {
19055                         a.splice(i, 1);
19056                     }
19057                 }
19058             }
19059             while (pt) {
19060                 if (lookup[pt.n] != null) {
19061                     if (pt._next) {
19062                         pt._next._prev = pt._prev;
19063                     }
19064                     if (pt._prev) {
19065                         pt._prev._next = pt._next;
19066                         pt._prev = null;
19067                     } else if (this._firstPT === pt) {
19068                         this._firstPT = pt._next;
19069                     }
19070                 }
19071                 pt = pt._next;
19072             }
19073             return false;
19074         };
19075
19076         p._roundProps = function(lookup, value) {
19077             var pt = this._firstPT;
19078             while (pt) {
19079                 if (lookup[this._propName] || (pt.n != null && lookup[ pt.n.split(this._propName + "_").join("") ])) { //some properties that are very plugin-specific add a prefix named after the _propName plus an underscore, so we need to ignore that extra stuff here.
19080                     pt.r = value;
19081                 }
19082                 pt = pt._next;
19083             }
19084         };
19085
19086         TweenLite._onPluginEvent = function(type, tween) {
19087             var pt = tween._firstPT,
19088                 changed, pt2, first, last, next;
19089             if (type === "_onInitAllProps") {
19090                 //sorts the PropTween linked list in order of priority because some plugins need to render earlier/later than others, like MotionBlurPlugin applies its effects after all x/y/alpha tweens have rendered on each frame.
19091                 while (pt) {
19092                     next = pt._next;
19093                     pt2 = first;
19094                     while (pt2 && pt2.pr > pt.pr) {
19095                         pt2 = pt2._next;
19096                     }
19097                     if ((pt._prev = pt2 ? pt2._prev : last)) {
19098                         pt._prev._next = pt;
19099                     } else {
19100                         first = pt;
19101                     }
19102                     if ((pt._next = pt2)) {
19103                         pt2._prev = pt;
19104                     } else {
19105                         last = pt;
19106                     }
19107                     pt = next;
19108                 }
19109                 pt = tween._firstPT = first;
19110             }
19111             while (pt) {
19112                 if (pt.pg) if (typeof(pt.t[type]) === "function") if (pt.t[type]()) {
19113                     changed = true;
19114                 }
19115                 pt = pt._next;
19116             }
19117             return changed;
19118         };
19119
19120         TweenPlugin.activate = function(plugins) {
19121             var i = plugins.length;
19122             while (--i > -1) {
19123                 if (plugins[i].API === TweenPlugin.API) {
19124                     _plugins[(new plugins[i]())._propName] = plugins[i];
19125                 }
19126             }
19127             return true;
19128         };
19129
19130         //provides a more concise way to define plugins that have no dependencies besides TweenPlugin and TweenLite, wrapping common boilerplate stuff into one function (added in 1.9.0). You don't NEED to use this to define a plugin - the old way still works and can be useful in certain (rare) situations.
19131         _gsDefine.plugin = function(config) {
19132             if (!config || !config.propName || !config.init || !config.API) { throw "illegal plugin definition."; }
19133             var propName = config.propName,
19134                 priority = config.priority || 0,
19135                 overwriteProps = config.overwriteProps,
19136                 map = {init:"_onInitTween", set:"setRatio", kill:"_kill", round:"_roundProps", initAll:"_onInitAllProps"},
19137                 Plugin = _class("plugins." + propName.charAt(0).toUpperCase() + propName.substr(1) + "Plugin",
19138                     function() {
19139                         TweenPlugin.call(this, propName, priority);
19140                         this._overwriteProps = overwriteProps || [];
19141                     }, (config.global === true)),
19142                 p = Plugin.prototype = new TweenPlugin(propName),
19143                 prop;
19144             p.constructor = Plugin;
19145             Plugin.API = config.API;
19146             for (prop in map) {
19147                 if (typeof(config[prop]) === "function") {
19148                     p[map[prop]] = config[prop];
19149                 }
19150             }
19151             Plugin.version = config.version;
19152             TweenPlugin.activate([Plugin]);
19153             return Plugin;
19154         };
19155
19156
19157         //now run through all the dependencies discovered and if any are missing, log that to the console as a warning. This is why it's best to have TweenLite load last - it can check all the dependencies for you.
19158         a = window._gsQueue;
19159         if (a) {
19160             for (i = 0; i < a.length; i++) {
19161                 a[i]();
19162             }
19163             for (p in _defLookup) {
19164                 if (!_defLookup[p].func) {
19165                     //window.console.log("GSAP encountered missing dependency: com.greensock." + p);
19166                 }
19167             }
19168         }
19169
19170         _tickerActive = false; //ensures that the first official animation forces a ticker.tick() to update the time when it is instantiated
19171
19172 })(window);
19173
19174 angular.module('b2b.att.collapse', ['b2b.att.transition'])
19175
19176 // The collapsible directive indicates a block of html that will expand and collapse
19177 .directive('b2bCollapse', ['$transition', function($transition) {
19178     // CSS transitions don't work with height: auto, so we have to manually change the height to a
19179     // specific value and then once the animation completes, we can reset the height to auto.
19180     // Unfortunately if you do this while the CSS transitions are specified (i.e. in the CSS class
19181     // "collapse") then you trigger a change to height 0 in between.
19182     // The fix is to remove the "collapse" CSS class while changing the height back to auto - phew!
19183
19184     var props = {
19185         open: {
19186             marginTop: null,
19187             marginBottom: null,
19188             paddingTop: null,
19189             paddingBottom: null,
19190             display: 'block'
19191         },
19192         closed: {
19193             marginTop: 0,
19194             marginBottom: 0,
19195             paddingTop: 0,
19196             paddingBottom: 0,
19197             display: 'none'
19198         }
19199     };
19200
19201     var fixUpHeight = function(scope, element, height) {
19202         // We remove the collapse CSS class to prevent a transition when we change to height: auto
19203         element.removeClass('b2bCollapse');
19204         element.css({height: height});
19205         //adjusting for any margin or padding
19206         if (height === 0) {
19207             element.css(props.closed);
19208         } else {
19209             element.css(props.open);
19210         }
19211         // It appears that  reading offsetWidth makes the browser realise that we have changed the
19212         // height already :-/
19213         var x = element[0].offsetWidth;
19214         element.addClass('b2bCollapse');
19215     };
19216
19217     return {
19218         link: function(scope, element, attrs) {
19219             var isCollapsed;
19220             var initialAnimSkip = true;
19221             scope.$watch(function() {
19222                 return element[0].scrollHeight;
19223             }, function(value) {
19224                 //The listener is called when scrollHeight changes
19225                 //It actually does on 2 scenarios: 
19226                 // 1. Parent is set to display none
19227                 // 2. angular bindings inside are resolved
19228                 //When we have a change of scrollHeight we are setting again the correct height if the group is opened
19229                 if (element[0].scrollHeight !== 0) {
19230                     if (!isCollapsed) {
19231                         if (initialAnimSkip) {
19232                             fixUpHeight(scope, element, element[0].scrollHeight + 'px');
19233                         } else {
19234                             fixUpHeight(scope, element, 'auto');
19235                             element.css({overflow: 'visible'});
19236                         }
19237                     }
19238                 }
19239             });
19240
19241             scope.$watch(attrs.b2bCollapse, function(value) {
19242                 if (value) {
19243                     collapse();
19244                 } else {
19245                     expand();
19246                 }
19247             });
19248
19249
19250             var currentTransition;
19251             var doTransition = function(change) {
19252                 if (currentTransition) {
19253                     currentTransition.cancel();
19254                 }
19255                 currentTransition = $transition(element, change);
19256                 currentTransition.then(
19257                         function() {
19258                             currentTransition = undefined;
19259                         },
19260                         function() {
19261                             currentTransition = undefined;
19262                         }
19263                 );
19264                 return currentTransition;
19265             };
19266
19267             var expand = function() {
19268                 scope.postTransition = true; 
19269                 if (initialAnimSkip) {
19270                     initialAnimSkip = false;
19271                     if (!isCollapsed) {
19272                         fixUpHeight(scope, element, 'auto');
19273                     }
19274                 } else {
19275                     //doTransition({ height : element[0].scrollHeight + 'px' })
19276                     doTransition(angular.extend({height: element[0].scrollHeight + 'px'}, props.open))
19277                             .then(function() {
19278                                 // This check ensures that we don't accidentally update the height if the user has closed
19279                                 // the group while the animation was still running
19280                                 if (!isCollapsed) {
19281                                     fixUpHeight(scope, element, 'auto');
19282                                 }
19283                             });
19284                 }
19285                 isCollapsed = false;
19286             };
19287
19288             var collapse = function() {
19289                 isCollapsed = true;
19290                 if (initialAnimSkip) {
19291                     initialAnimSkip = false;
19292                     fixUpHeight(scope, element, 0);
19293                 } else {
19294                     fixUpHeight(scope, element, element[0].scrollHeight + 'px');
19295                     doTransition(angular.extend({height: 0}, props.closed)).then(function() {
19296                         scope.postTransition = false;
19297                     });
19298                     element.css({overflow: 'hidden'});
19299                 }
19300             };
19301         }
19302     };
19303 }]);
19304 angular.module('b2b.att.position', [])
19305
19306 .factory('$position', ['$document', '$window', function ($document, $window) {
19307     function getStyle(el, cssprop) {
19308         if (el.currentStyle) { //IE
19309             return el.currentStyle[cssprop];
19310         } else if ($window.getComputedStyle) {
19311             return $window.getComputedStyle(el)[cssprop];
19312         }
19313         // finally try and get inline style
19314         return el.style[cssprop];
19315     }
19316
19317     /**
19318      * Checks if a given element is statically positioned
19319      * @param element - raw DOM element
19320      */
19321     function isStaticPositioned(element) {
19322         return (getStyle(element, "position") || 'static') === 'static';
19323     }
19324
19325     /**
19326      * returns the closest, non-statically positioned parentOffset of a given element
19327      * @param element
19328      */
19329     var parentOffsetEl = function (element) {
19330         var docDomEl = $document[0];
19331         var offsetParent = element.offsetParent || docDomEl;
19332         while (offsetParent && offsetParent !== docDomEl && isStaticPositioned(offsetParent)) {
19333             offsetParent = offsetParent.offsetParent;
19334         }
19335         return offsetParent || docDomEl;
19336     };
19337
19338     return {
19339         /**
19340          * Provides read-only equivalent of jQuery's position function:
19341          * http://api.jquery.com/position/
19342          */
19343         position: function (element) {
19344             var elBCR = this.offset(element);
19345             var offsetParentBCR = {
19346                 top: 0,
19347                 left: 0
19348             };
19349             var offsetParentEl = parentOffsetEl(element[0]);
19350             if (offsetParentEl !== $document[0]) {
19351                 offsetParentBCR = this.offset(angular.element(offsetParentEl));
19352                 offsetParentBCR.top += offsetParentEl.clientTop - offsetParentEl.scrollTop;
19353                 offsetParentBCR.left += offsetParentEl.clientLeft - offsetParentEl.scrollLeft;
19354             }
19355
19356             return {
19357                 width: element.prop('offsetWidth'),
19358                 height: element.prop('offsetHeight'),
19359                 top: elBCR.top - offsetParentBCR.top,
19360                 left: elBCR.left - offsetParentBCR.left
19361             };
19362         },
19363
19364         /**
19365          * Provides read-only equivalent of jQuery's offset function:
19366          * http://api.jquery.com/offset/
19367          */
19368         offset: function (element) {
19369             var boundingClientRect = element[0].getBoundingClientRect();
19370             return {
19371                 width: element.prop('offsetWidth'),
19372                 height: element.prop('offsetHeight'),
19373                 top: boundingClientRect.top + ($window.pageYOffset || $document[0].body.scrollTop || $document[0].documentElement.scrollTop),
19374                 left: boundingClientRect.left + ($window.pageXOffset || $document[0].body.scrollLeft || $document[0].documentElement.scrollLeft)
19375             };
19376         },
19377         
19378          /**
19379          * Provides functionality to check whether an element is in view port.
19380          */
19381         isElementInViewport: function (element) {
19382             if (element) {
19383                 var rect = element[0].getBoundingClientRect();
19384                 return (
19385                     rect.top >= 0 &&
19386                     rect.left >= 0 &&
19387                     rect.bottom <= ($window.innerHeight || $document[0].documentElement.clientHeight) &&
19388                     rect.right <= ($window.innerWidth || $document[0].documentElement.clientWidth)
19389                 );
19390             } else {
19391                 return false;
19392             }
19393         }
19394     };
19395 }])
19396
19397 .factory('$isElement', [function () {
19398     var isElement = function (currentElem, targetElem, alternateElem) {
19399         if (currentElem[0] === targetElem[0]) {
19400             return true;
19401         } else if (currentElem[0] === alternateElem[0]) {
19402             return false;
19403         } else {
19404             return isElement((currentElem.parent()[0] && currentElem.parent()) || targetElem, targetElem, alternateElem);
19405         }
19406     };
19407
19408     return isElement;
19409 }])
19410
19411 .directive('attPosition', ['$position', function ($position) {
19412     return {
19413         restrict: 'A',
19414         link: function (scope, elem, attr) {
19415             scope.$watchCollection(function () {
19416                 return $position.position(elem);
19417             }, function (value) {
19418                 scope[attr.attPosition] = value;
19419             });
19420         }
19421     };
19422 }]);
19423
19424 angular.module('b2b.att.transition', [])
19425
19426 .factory('$transition', ['$q', '$timeout', '$rootScope', function($q, $timeout, $rootScope) {
19427
19428   var $transition = function(element, trigger, options) {
19429     options = options || {};
19430     var deferred = $q.defer();
19431     var endEventName = $transition[options.animation ? "animationEndEventName" : "transitionEndEventName"];
19432
19433     var transitionEndHandler = function() {
19434       $rootScope.$apply(function() {
19435         element.unbind(endEventName, transitionEndHandler);
19436         deferred.resolve(element);
19437       });
19438     };
19439
19440     if (endEventName) {
19441       element.bind(endEventName, transitionEndHandler);
19442     }
19443
19444     // Wrap in a timeout to allow the browser time to update the DOM before the transition is to occur
19445     $timeout(function() {
19446       if ( angular.isString(trigger) ) {
19447         element.addClass(trigger);
19448       } else if ( angular.isFunction(trigger) ) {
19449         trigger(element);
19450       } else if ( angular.isObject(trigger) ) {
19451         element.css(trigger);
19452       }
19453       //If browser does not support transitions, instantly resolve
19454       if ( !endEventName ) {
19455         deferred.resolve(element);
19456       }
19457     }, 100);
19458
19459     // Add our custom cancel function to the promise that is returned
19460     // We can call this if we are about to run a new transition, which we know will prevent this transition from ending,
19461     // i.e. it will therefore never raise a transitionEnd event for that transition
19462     deferred.promise.cancel = function() {
19463       if ( endEventName ) {
19464         element.unbind(endEventName, transitionEndHandler);
19465       }
19466       deferred.reject('Transition cancelled');
19467     };
19468
19469     return deferred.promise;
19470   };
19471
19472   // Work out the name of the transitionEnd event
19473   var transElement = document.createElement('trans');
19474   var transitionEndEventNames = {
19475     'WebkitTransition': 'webkitTransitionEnd',
19476     'MozTransition': 'transitionend',
19477     'OTransition': 'oTransitionEnd',
19478     'transition': 'transitionend'
19479   };
19480   var animationEndEventNames = {
19481     'WebkitTransition': 'webkitAnimationEnd',
19482     'MozTransition': 'animationend',
19483     'OTransition': 'oAnimationEnd',
19484     'transition': 'animationend'
19485   };
19486   function findEndEventName(endEventNames) {
19487     for (var name in endEventNames){
19488       if (transElement.style[name] !== undefined) {
19489         return endEventNames[name];
19490       }
19491     }
19492   }
19493   $transition.transitionEndEventName = findEndEventName(transitionEndEventNames);
19494   $transition.animationEndEventName = findEndEventName(animationEndEventNames);
19495   return $transition;
19496 }])
19497
19498 .factory('$scrollTo', ['$window', function($window) {
19499     var $scrollTo = function(offsetLeft, offsetTop, duration) {
19500         TweenMax.to($window, duration || 1, {scrollTo: {y: offsetTop, x: offsetLeft}, ease: Power4.easeOut});
19501     };
19502     return $scrollTo;
19503 }])
19504 .factory('animation', function(){
19505     return TweenMax;
19506 })
19507 .factory('$progressBar', function(){
19508
19509    //Provides a function to pass in code for closure purposes
19510    var loadingAnimationCreator = function(onUpdateCallback){
19511
19512       //Use closure to setup some resuable code
19513       var loadingAnimation = function(callback, duration){
19514           TweenMax.to({}, duration, {
19515               onUpdateParams: ["{self}"],
19516               onUpdate: onUpdateCallback,
19517               onComplete: callback
19518           });
19519       };
19520       //Returns a function that takes a callback function and a duration for the animation
19521       return (function(){
19522         return loadingAnimation;
19523       })();
19524     };
19525
19526   return loadingAnimationCreator;
19527 })
19528 .factory('$height', function(){
19529   var heightAnimation = function(element,duration,height,alpha){
19530     TweenMax.to(element,
19531       duration,
19532       {height:height, autoAlpha:alpha},
19533       0);
19534   };
19535   return heightAnimation;
19536 });
19537 angular.module('b2b.att.utilities', ['ngSanitize'])
19538 .constant('b2bUtilitiesConfig', {
19539     prev: '37',
19540     up: '38',
19541     next: '39',
19542     down: '40',
19543     type: 'list',
19544     columns: 1,
19545     enableSearch: false,
19546     searchTimer: 200,
19547     circularTraversal: false
19548 })
19549 .constant('b2bWhenScrollEndsConstants', {
19550     'threshold': 100,
19551     'width': 0,
19552     'height': 0
19553 })
19554 // All breakpoints ranges from >= min and < max
19555 .constant('b2bAwdBreakpoints', {
19556     breakpoints: {
19557         mobile: {
19558             min: 1,
19559             max: 768
19560         },
19561         tablet: {
19562             min: 768,
19563             max: 1025
19564         },
19565         desktop: {
19566             min: 1025,
19567             max: 1920
19568         }
19569     }
19570 })
19571 .filter('groupBy', function ($timeout) {
19572     //Custom GroupBy Filter for treeNav, returns key string and value.childarray as set of grouped elements
19573     return function (data, key) {
19574         if (!key) return data;
19575         var outputPropertyName = '__groupBy__' + key;
19576         if (!data[outputPropertyName]) {
19577             var result = {};
19578             for (var i = 0; i < data.length; i++) {
19579                 if (!result[data[i][key]])
19580                     result[data[i][key]] = {};
19581                 if (!result[data[i][key]].childArray) {
19582                     result[data[i][key]].childArray = [];
19583                 }
19584                 result[data[i][key]].childArray.push(data[i]);
19585                 if (data[i].activeGrp && data[i].activeGrp == true) {
19586                     console.log('make ' + data[i].grpChild + ' active');
19587                     result[data[i][key]].showGroup = true;
19588                 }
19589             }
19590             Object.defineProperty(result, 'length', {enumerable: false,value: Object.keys(result).length});
19591             Object.defineProperty(data, outputPropertyName, {enumerable: false,configurable: true,writable:false,value:result});
19592             $timeout(function(){delete data[outputPropertyName];},0,false);
19593         }
19594         return data[outputPropertyName];
19595     };
19596 })
19597 .filter('searchObjectPropertiesFilter', [function() {
19598     return function(items, searchText, attrs) {
19599         if(!searchText){
19600             return items;
19601         }
19602         var filtered = [];
19603         searchText = searchText.toLowerCase();
19604         angular.forEach(items, function(item) {
19605             angular.forEach(attrs, function(attr) {
19606                 if (item.hasOwnProperty(attr) && (item[attr].toLowerCase().indexOf(searchText) != -1)) {
19607                     filtered.push(item);
19608                     return;
19609                 }
19610             });
19611         });
19612         return filtered;
19613     };
19614 }])
19615 .filter('unsafe',[ '$sce', function ($sce) { 
19616     return function(val){ 
19617        return $sce.trustAsHtml(val); 
19618     }; 
19619 }])
19620 .filter('b2bHighlight', function () {
19621     function escapeRegexp(queryToEscape) {
19622         return queryToEscape.replace(/([.?*+^$[\]\\(){}|-])/g, '\\$1');
19623     }
19624     return function (matchItem, query, className) {
19625         return query && matchItem ? matchItem.replace(new RegExp(escapeRegexp(query), 'gi'), '<span class=\"' + className + '\">$&</span>') : matchItem;
19626     }
19627 })
19628 /*License (MIT)
19629 Copyright Â© 2013 Matt Diamond
19630 https://github.com/cwilso/AudioRecorder/blob/master/js/recorderjs/recorder.js
19631 */
19632 .factory('b2bRecorder', function() {
19633
19634     var Recorder = function(source, cfg) {
19635         var WORKER_PATH = 'recorderWorker.js';
19636         var config = cfg || {};
19637         var bufferLen = config.bufferLen || 4096;
19638         this.context = source.context;
19639         if(!this.context.createScriptProcessor) {
19640             this.node = this.context.createJavacriptProcessor(bufferLen, 2, 2);
19641         } else {
19642             this.node = this.context.createScriptProcessor(bufferLen, 2, 2);
19643         }
19644         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()}};';
19645         var blob = new Blob([workerCode]);
19646
19647         var worker = new Worker(window.URL.createObjectURL(blob)); //TODO: Use a blob instead
19648         worker.postMessage({
19649             command: 'init',
19650             config: {
19651                 sampleRate: this.context.sampleRate
19652             }
19653         });
19654         var recording = false,
19655             currCallback;
19656
19657         this.node.onaudioprocess = function(e) {
19658             if (!recording) return;
19659             worker.postMessage({
19660                 command: 'record',
19661                 buffer: [
19662                     e.inputBuffer.getChannelData(0),
19663                     e.inputBuffer.getChannelData(1)
19664                 ]
19665             });
19666         };
19667
19668         this.configure = function(cfg) {
19669             for (var prop in cfg) {//TODO: look into using angular.extend() here
19670                 if (cfg.hasOwnProperty(prop)) {
19671                     config[prop] = cfg[prop];
19672                 }
19673             }
19674         };
19675
19676         this.record = function() {
19677             recording = true;
19678         };
19679
19680         this.stop = function() {
19681             recording = false;
19682         };
19683
19684         this.clear = function() {
19685             worker.postMessage({ command: 'clear' });
19686             window.URL.revokeObjectURL(blob);
19687         };
19688
19689         this.getBuffers = function(cb) {
19690             currCallback = cb || config.callback;
19691             worker.postMessage({ command: 'getBuffers' });
19692         };
19693
19694         this.exportWAV = function(cb, type) {
19695             currCallback = cb || config.callback;
19696             type = type || config.type || 'audio/wav';
19697             if (!currCallback) throw new Error('[b2bRecorder]: Callback not set!');
19698             worker.postMessage({
19699                 command: 'exportWAV',
19700                 type: type
19701             });
19702         };
19703
19704         this.exportMonoWAV = function(cb, type) {
19705             currCallback = cb || config.callback;
19706             type = type || config.type || 'audio/wav';
19707             if (!currCallback) throw new Error('[b2bRecorder]: Callback not set!');
19708             worker.postMessage({
19709                 command: 'exportMonoWAV',
19710                 type: type
19711             });
19712         };
19713
19714         worker.onmessage = function(e) {
19715             var blob = e.data;
19716             currCallback(blob);
19717         };
19718
19719         source.connect(this.node);
19720         this.node.connect(this.context.destination); // if the script node is not connected to an output the "onaudioprocess" event is not triggerd in Chrome
19721
19722     };
19723
19724     return Recorder;
19725
19726 })
19727 .factory('b2bViewport', function() {
19728   /* Source: https://gist.github.com/bjankord/2399828 */
19729   var _viewportWidth = function() {
19730     var vpw;
19731     var webkit = (!(window.webkitConvertPointFromNodeToPage == null));
19732     
19733     // Webkit:
19734     if ( webkit ) {
19735       var vpwtest = document.createElement( "div" );
19736       // Sets test div to width 100%, !important overrides any other misc. box model styles that may be set in the CSS
19737       vpwtest.style.cssText = "width:100% !important; margin:0 !important; padding:0 !important; border:none !important;";
19738       document.documentElement.insertBefore( vpwtest, document.documentElement.firstChild );
19739       vpw = vpwtest.offsetWidth;
19740       document.documentElement.removeChild( vpwtest );
19741     }
19742     // IE 6-8:
19743     else if ( window.innerWidth === undefined ) { 
19744       vpw = document.documentElement.clientWidth; 
19745     }
19746     // Other:
19747     else{
19748       vpw =  window.innerWidth;
19749     }
19750
19751     return (vpw);
19752   }
19753   return {
19754     viewportWidth: _viewportWidth
19755   };
19756 })
19757 .directive('b2bWhenScrollEnds', function(b2bWhenScrollEndsConstants, $log) {
19758     return {
19759         restrict: 'A',
19760         link: function (scope, element, attrs) {
19761             /**
19762             * Exposed Attributes:
19763             *       threshold - integer - number of pixels before scrollbar hits end that callback is called
19764             *       width - integer - override for element's width (px)
19765             *       height - integer - override for element's height (px)
19766             *       axis - string - x/y for scroll bar axis
19767             */
19768             var threshold = parseInt(attrs.threshold, 10) || b2bWhenScrollEndsConstants.threshold;
19769
19770             if (!attrs.axis || attrs.axis === '') {
19771                 $log.warn('axis attribute must be defined for b2bWhenScrollEnds.');
19772                 return;
19773             }
19774
19775             if (attrs.axis === 'x') {
19776                 visibleWidth = parseInt(attrs.width, 10) || b2bWhenScrollEndsConstants.width;
19777                 if (element.css('width')) {
19778                     visibleWidth = element.css('width').split('px')[0];  
19779                 }
19780
19781                 element[0].addEventListener('scroll', function() {
19782                     var scrollableWidth = element.prop('scrollWidth');
19783                     if (scrollableWidth === undefined) {
19784                         scrollableWidth = 1;
19785                     }
19786                     var hiddenContentWidth = scrollableWidth - visibleWidth;
19787
19788                     if (hiddenContentWidth - element[0].scrollLeft <= threshold) {
19789                         /* Scroll almost at bottom, load more rows */
19790                         scope.$apply(attrs.b2bWhenScrollEnds);
19791                     }
19792                 });
19793             } else if (attrs.axis === 'y') {
19794                 visibleHeight = parseInt(attrs.height, 10) || b2bWhenScrollEndsConstants.height;
19795                 if (element.css('width')) {
19796                     visibleHeight = element.css('height').split('px')[0]; 
19797                 }
19798
19799                 element[0].addEventListener('scroll', function() {
19800                     var scrollableHeight = element.prop('scrollHeight');
19801                     if (scrollableHeight === undefined) {
19802                         scrollableHeight = 1;
19803                     }
19804                     var hiddenContentHeight = scrollableHeight - visibleHeight;
19805
19806                     if (hiddenContentHeight - element[0].scrollTop <= threshold) {
19807                         /* Scroll almost at bottom, load more rows */
19808                         scope.$apply(attrs.b2bWhenScrollEnds);
19809                     }
19810                 });
19811             }
19812         }
19813     };
19814 })
19815
19816 .factory('$windowBind', ['$window', '$timeout', function($window, $timeout) {
19817     var win = angular.element($window);
19818     var _scroll = function (flag, callbackFunc, scope) {
19819         scope.$watch(flag, function (val) {
19820             $timeout(function () {
19821                 if (val) {
19822                     win.bind('scroll', callbackFunc);
19823                 } else {
19824                     win.unbind('scroll', callbackFunc);
19825                 }
19826             });
19827         });
19828     };
19829
19830     var throttle = function(type, name, obj) {
19831         obj = obj || window;
19832         var running = false;
19833         var func = function() {
19834             if (running) { return; }
19835             running = true;
19836              requestAnimationFrame(function() {
19837                 obj.dispatchEvent(new CustomEvent(name));
19838                 running = false;
19839             });
19840         };
19841         obj.addEventListener(type, func);
19842     };
19843
19844     var _resize = function(callbackFunc, scope) {
19845         throttle("resize", "optimizedResize");
19846         window.addEventListener("optimizedResize", function(event) {
19847             callbackFunc();
19848             //win.bind(event, callbackFunc);
19849             if (!scope.$$phase) {
19850                 scope.$digest();
19851             }
19852         });
19853     };
19854
19855     var _click = function (flag, callbackFunc, scope) {
19856         scope.$watch(flag, function (val) {
19857             $timeout(function () {
19858                 if (val) {
19859                     win.bind('click', callbackFunc);
19860                 } else {
19861                     win.unbind('click', callbackFunc);
19862                 }
19863             });
19864         });
19865     };
19866
19867     var _event = function (event, flag, callbackFunc, scope, timeoutFlag, timeoutValue) {
19868         if (timeoutFlag) {
19869             if (!(timeoutValue)) {
19870                 timeoutValue = 0;
19871             }
19872             scope.$watch(flag, function (newVal, oldVal) {
19873                 if (newVal !== oldVal) {
19874                     $timeout(function () {
19875                         if (newVal) {
19876                             win.bind(event, callbackFunc);
19877                         } else {
19878                             win.unbind(event, callbackFunc);
19879                         }
19880                     }, timeoutValue);
19881                 }
19882             });
19883         } else {
19884             scope.$watch(flag, function (newVal, oldVal) {
19885                 if (newVal !== oldVal) {
19886                     if (newVal) {
19887                         win.bind(event, callbackFunc);
19888                     } else {
19889                         win.unbind(event, callbackFunc);
19890                     }
19891                 }
19892             });
19893         }
19894     };
19895
19896     return {
19897         click: _click,
19898         scroll: _scroll,
19899         event: _event, 
19900         resize: _resize
19901     };
19902 }])
19903
19904 .factory('keymap', function () {
19905     return {
19906         KEY: {
19907             TAB: 9,
19908             ENTER: 13,
19909             ESC: 27,
19910             SPACE: 32,
19911             LEFT: 37,
19912             UP: 38,
19913             RIGHT: 39,
19914             DOWN: 40,
19915             SHIFT: 16,
19916             CTRL: 17,
19917             ALT: 18,
19918             PAGE_UP: 33,
19919             PAGE_DOWN: 34,
19920             HOME: 36,
19921             END: 35,
19922             BACKSPACE: 8,
19923             DELETE: 46,
19924             COMMAND: 91
19925         },
19926         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 : "'"
19927         },
19928         isControl: function (e) {
19929             var k = e.keyCode;
19930             switch (k) {
19931             case this.KEY.COMMAND:
19932             case this.KEY.SHIFT:
19933             case this.KEY.CTRL:
19934             case this.KEY.ALT:
19935                 return true;
19936             default:;
19937             }
19938
19939             if (e.metaKey) {
19940                 return true;
19941             }
19942
19943             return false;
19944         },
19945         isFunctionKey: function (k) {
19946             k = k.keyCode ? k.keyCode : k;
19947             return k >= 112 && k <= 123;
19948         },
19949         isVerticalMovement: function (k) {
19950             return ~[this.KEY.UP, this.KEY.DOWN].indexOf(k);
19951         },
19952         isHorizontalMovement: function (k) {
19953             return ~[this.KEY.LEFT, this.KEY.RIGHT, this.KEY.BACKSPACE, this.KEY.DELETE].indexOf(k);
19954         },
19955         isAllowedKey: function (k) {
19956             return (~[this.KEY.SPACE, this.KEY.ESC, this.KEY.ENTER].indexOf(k)) || this.isHorizontalMovement(k) || this.isVerticalMovement(k);
19957         },
19958         isNumericKey: function (e) {
19959             var k = e.keyCode;
19960             if ((k >= 48 && k <= 57) || (k >= 96 && k <= 105)) {
19961                 return true;
19962             } else {
19963                 return false;
19964             }
19965         },
19966         isAlphaNumericKey: function (e) {
19967             var k = e.keyCode;
19968             if ((k >= 48 && k <= 57) || (k >= 96 && k <= 105) || (k >= 65 && k <= 90)) {
19969                 return true;
19970             } else {
19971                 return false;
19972             }
19973         }
19974     };
19975 })
19976
19977 .factory('$isElement', [function () {
19978     var isElement = function (currentElem, targetElem, alternateElem) {
19979         if (currentElem[0] === targetElem[0]) {
19980             return true;
19981         } else if (currentElem[0] === alternateElem[0]) {
19982             return false;
19983         } else {
19984             return isElement((currentElem.parent()[0] && currentElem.parent()) || targetElem, targetElem, alternateElem);
19985         }
19986     };
19987
19988     return isElement;
19989 }])
19990
19991 .factory('events', function () {
19992     var _stopPropagation = function (evt) {
19993         if (evt.stopPropagation) {
19994             evt.stopPropagation();
19995         } else {
19996             evt.returnValue = false;
19997         }
19998     };
19999     var _preventDefault = function (evt) {
20000         if (evt.preventDefault) {
20001             evt.preventDefault();
20002         } else {
20003             evt.returnValue = false;
20004         }
20005     }
20006     return {
20007         stopPropagation: _stopPropagation,
20008         preventDefault: _preventDefault
20009     };
20010 })
20011
20012
20013 .factory('$documentBind', ['$document', '$timeout', function ($document, $timeout) {
20014     var _click = function (flag, callbackFunc, scope) {
20015         scope.$watch(flag, function (val) {
20016             $timeout(function () {
20017                 if (val) {
20018                     $document.bind('click', callbackFunc);
20019                 } else {
20020                     $document.unbind('click', callbackFunc);
20021                 }
20022             });
20023         });
20024     };
20025
20026     var _touch = function (flag, callbackFunc, scope) {
20027         scope.$watch(flag, function (val) {
20028             $timeout(function () {
20029                 if (val) {
20030                     $document.bind('touchstart', callbackFunc);
20031                 } else {
20032                     $document.unbind('touchstart', callbackFunc);
20033                 }
20034             });
20035         });
20036     };
20037
20038     var _scroll = function (flag, callbackFunc, scope) {
20039         scope.$watch(flag, function (val) {
20040             $timeout(function () {
20041                 if (val) {
20042                     $document.bind('scroll', callbackFunc);
20043                 } else {
20044                     $document.unbind('scroll', callbackFunc);
20045                 }
20046             });
20047         });
20048     };
20049
20050     var _event = function (event, flag, callbackFunc, scope, timeoutFlag, timeoutValue) {
20051         if (timeoutFlag) {
20052             if (!(timeoutValue)) {
20053                 timeoutValue = 0;
20054             }
20055             scope.$watch(flag, function (newVal, oldVal) {
20056                 if (newVal !== oldVal) {
20057                     $timeout(function () {
20058                         if (newVal) {
20059                             $document.bind(event, callbackFunc);
20060                         } else {
20061                             $document.unbind(event, callbackFunc);
20062                         }
20063                     }, timeoutValue);
20064                 }
20065             });
20066         } else {
20067             scope.$watch(flag, function (newVal, oldVal) {
20068                 if (newVal !== oldVal) {
20069                     if (newVal) {
20070                         $document.bind(event, callbackFunc);
20071                     } else {
20072                         $document.unbind(event, callbackFunc);
20073                     }
20074                 }
20075             });
20076         }
20077     };
20078
20079     return {
20080         click: _click,
20081         touch: _touch,
20082         scroll: _scroll,
20083         event: _event
20084     };
20085 }])
20086
20087 .directive('b2bOnlyNums', function (keymap) {
20088     return {
20089         restrict: 'A',
20090         require: 'ngModel',
20091         link: function (scope, elm, attrs, ctrl) {
20092             var maxChars = attrs.b2bOnlyNums ? attrs.b2bOnlyNums : 4;
20093             elm.on('keydown', function (event) {
20094                 if ((event.which >= 48 && event.which <= 57) || (event.which >= 96 && event.which <= 105)) {
20095                     // check for maximum characters allowed
20096                     if (elm.val().length < maxChars){
20097                         return true;
20098                     } else {
20099                         event.preventDefault();
20100                         return false;
20101                     }
20102                 } else if ([8, 9, 13, 27, 37, 38, 39, 40].indexOf(event.which) > -1) {
20103                     // to allow backspace, tab, enter, escape, arrows
20104                     return true;
20105                 } else if (event.altKey || event.ctrlKey) {
20106                     // to allow alter, control, and shift keys
20107                     return true;
20108                 } else {
20109                     // to stop others
20110                     event.preventDefault();
20111                     return false;
20112                 }
20113             });
20114
20115             var validateString = function (value) {
20116                 if (angular.isUndefined(value) || value === null || value === '') {
20117                     return ctrl.$modelValue;
20118                 }
20119                 return value;
20120             };
20121             ctrl.$parsers.unshift(validateString);
20122         }
20123     }
20124 })
20125
20126 .directive('b2bKeyupClick', [ function () {
20127     return {
20128         restrict: 'A',
20129         link: function (scope, elem, attr) {
20130             var keyCode = [];
20131             attr.$observe('b2bKeyupClick', function (value) {
20132                 if (value) {
20133                     keyCode = value.split(',');
20134                 }
20135             });
20136             elem.bind('keydown keyup', function (ev) {
20137                 var keyCodeCondition = function () {
20138                     var flag = false;
20139                     if (!(ev.keyCode)) {
20140                         if (ev.which) {
20141                             ev.keyCode = ev.which;
20142                         } else if (ev.charCode) {
20143                             ev.keyCode = ev.charCode;
20144                         }
20145                     }
20146                     if ((ev.keyCode && keyCode.indexOf(ev.keyCode.toString()) > -1)) {
20147                         flag = true;
20148                     }
20149                     return flag;
20150                 };
20151                 if (ev.type === 'keydown' && keyCodeCondition()) {
20152                     ev.preventDefault();
20153                 }
20154                 else if (ev.type === 'keyup' && keyCodeCondition()) {
20155                     elem[0].click();
20156                 }
20157             });
20158         }
20159     };
20160 }])
20161
20162 .factory('b2bDOMHelper', function() {
20163
20164     var _isTabable = function(node) {
20165         var element = angular.element(node);
20166         var tagName = element[0].tagName.toUpperCase();
20167
20168         if (isHidden(element)) {
20169             return false;
20170         }
20171         if (element.attr('tabindex') !== undefined) {
20172             return (parseInt(element.attr('tabindex'), 10) >= 0);
20173         }
20174         if (tagName === 'A' || tagName === 'AREA' || tagName === 'BUTTON' || tagName === 'INPUT' || tagName === 'TEXTAREA' || tagName === 'SELECT') {
20175             if (tagName === 'A' || tagName === 'AREA') {
20176                 // anchors/areas without href are not focusable
20177                 return (element[0].href !== '');
20178             }
20179             return !(element[0].disabled || element[0].readOnly);
20180         }
20181         return false;
20182     };
20183
20184     function isValidChild(child) {
20185         return child.nodeType == 1 && child.nodeName != 'SCRIPT' && child.nodeName != 'STYLE';
20186     }
20187     
20188     function isHidden(obj) {
20189         var elem = angular.element(obj);
20190         var elemStyle = undefined;
20191         if(obj instanceof HTMLElement){
20192             elemStyle = window.getComputedStyle(obj);
20193         }
20194         else {
20195             elemStyle = window.getComputedStyle(obj[0]);
20196         }
20197         return elem.hasClass('ng-hide') || elem.css('display') === 'none' || elemStyle.display === 'none' || elemStyle.visibility === 'hidden' || elem.css('visibility') === 'hidden';
20198     }
20199
20200     function hasValidParent(obj) {
20201         return (isValidChild(obj) && obj.parentElement.nodeName !== 'BODY');
20202     }
20203
20204     function traverse(obj, fromTop) {
20205         var obj = obj || document.getElementsByTagName('body')[0];
20206         if (isValidChild(obj) && _isTabable(obj)) {
20207             return obj;
20208         }
20209         // If object is hidden, skip it's children
20210         if (isValidChild(obj) && isHidden(obj)) {
20211             return undefined;
20212         } 
20213         // If object is hidden, skip it's children
20214         if (angular.element(obj).hasClass('ng-hide')) {
20215             return undefined;
20216         }  
20217         if (obj.hasChildNodes()) {
20218             var child;
20219             if (fromTop) {
20220                 child = obj.firstChild;
20221             } else {
20222                 child = obj.lastChild;
20223             }
20224             while(child) {
20225                 var res =  traverse(child, fromTop);
20226                 if(res){
20227                     return res;
20228                 }
20229                 else{
20230                     if (fromTop) {
20231                         child = child.nextSibling;
20232                     } else {
20233                         child = child.previousSibling;
20234                     }
20235                 }
20236             }
20237         }
20238         else{
20239             return undefined;
20240         }
20241     }
20242
20243     var _previousElement = function(el, isFocusable){
20244
20245         var elem = el;
20246         if (el.hasOwnProperty('length')) {
20247             elem = el[0];
20248         }
20249
20250         var parent = elem.parentElement;
20251         var previousElem = undefined;
20252
20253         if(isFocusable) {
20254             if (hasValidParent(elem)) {
20255                 var siblings = angular.element(parent).children();
20256                 if (siblings.length > 0) {
20257                     // Good practice to splice out the elem from siblings if there, saving some time.
20258                     // We allow for a quick check for jumping to parent first before removing. 
20259                     if (siblings[0] === elem) {
20260                         // If we are looking at immidiate parent and elem is first child, we need to go higher
20261                         var e = _previousElement(angular.element(elem).parent(), isFocusable);
20262                         if (_isTabable(e)) {
20263                             return e;
20264                         }
20265                     } else {
20266                         // I need to filter myself and any nodes next to me from the siblings
20267                         var indexOfElem = Array.prototype.indexOf.call(siblings, elem);
20268                         siblings = Array.prototype.filter.call(siblings, function(item, itemIndex) {
20269                             if (!angular.equals(elem, item) && itemIndex < indexOfElem) {
20270                                 return true;
20271                             }
20272                         });
20273                     }
20274                     // We need to search backwards
20275                     for (var i = 0; i <= siblings.length-1; i++) {//for (var i = siblings.length-1; i >= 0; i--) {
20276                         var ret = traverse(siblings[i], false);
20277                         if (ret !== undefined) {
20278                             return ret;
20279                         }
20280                     }
20281
20282                     var e = _previousElement(angular.element(elem).parent(), isFocusable);
20283                     if (_isTabable(e)) {
20284                         return e;
20285                     }
20286                 }
20287             }
20288         } else {
20289             var siblings = angular.element(parent).children();
20290             if (siblings.length > 1) {
20291                 // Since indexOf is on Array.prototype and parent.children is a NodeList, we have to use call()
20292                 var index = Array.prototype.indexOf.call(siblings, elem);
20293                 previousElem = siblings[index-1];
20294             }
20295         }
20296         return previousElem;
20297     };
20298
20299     var _lastTabableElement = function(el) {
20300         /* This will return the first tabable element from the parent el */
20301         var elem = el;
20302         if (el.hasOwnProperty('length')) {
20303             elem = el[0];
20304         }
20305
20306         return traverse(elem, false);
20307     };
20308
20309     var _firstTabableElement = function(el) {
20310         /* This will return the first tabable element from the parent el */
20311         var elem = el;
20312         if (el.hasOwnProperty('length')) {
20313             elem = el[0];
20314         }
20315
20316         return traverse(elem, true);
20317     };
20318
20319     var _isInDOM = function(obj) {
20320       return document.documentElement.contains(obj);
20321     }
20322
20323     return {
20324         firstTabableElement: _firstTabableElement,
20325         lastTabableElement: _lastTabableElement,
20326         previousElement: _previousElement,
20327         isInDOM: _isInDOM,
20328         isTabable: _isTabable,
20329         isHidden: isHidden
20330     };
20331 })
20332
20333 .factory('trapFocusInElement', ['$document', '$isElement', 'DOMHelper', 'keymap', function ($document, $isElement, DOMHelper, keymap) {
20334     var elementStack = [];
20335     var stackHead = undefined;
20336     var trapFocusInElement = function (flag) {
20337         var bodyElements = $document.find('body').children();
20338
20339         var firstTabableElement = angular.element(DOMHelper.firstTabableElement(stackHead));
20340         var lastTabableElement = angular.element(DOMHelper.lastTabableElement(stackHead));
20341
20342         var trapKeyboardFocusInFirstElement = function (e) {
20343             if (!e.keyCode) {
20344                 e.keyCode = e.which;
20345             }
20346
20347             if (e.shiftKey === true && e.keyCode === keymap.KEY.TAB) {
20348                 lastTabableElement[0].focus();
20349                 e.preventDefault(e);
20350                 e.stopPropagation(e);
20351             }
20352
20353         };
20354
20355         var trapKeyboardFocusInLastElement = function (e) {
20356             if (!e.keyCode) {
20357                 e.keyCode = e.which;
20358             }
20359
20360             if (e.shiftKey === false && e.keyCode === keymap.KEY.TAB) {
20361                 firstTabableElement[0].focus();
20362                 e.preventDefault(e);
20363                 e.stopPropagation(e);
20364             }
20365         };
20366
20367         if (flag) {
20368             for (var i = 0; i < bodyElements.length; i++) {
20369                 if (bodyElements[i] !== stackHead[0]) {
20370                     bodyElements.eq(i).attr('aria-hidden', true);
20371                 }
20372             }
20373             firstTabableElement.bind('keydown', trapKeyboardFocusInFirstElement);
20374             lastTabableElement.bind('keydown', trapKeyboardFocusInLastElement);
20375         } else {
20376             for (var j = 0; j < bodyElements.length; j++) {
20377                 if (bodyElements[j] !== stackHead[0]) {
20378                     bodyElements.eq(j).removeAttr('aria-hidden');
20379                 }
20380             }
20381             firstTabableElement.unbind('keydown', trapKeyboardFocusInFirstElement);
20382             lastTabableElement.unbind('keydown', trapKeyboardFocusInLastElement);
20383         }
20384     };
20385     var toggleTrapFocusInElement = function (flag, element) {
20386         if (angular.isDefined(flag) && angular.isDefined(element)) {
20387             if (angular.isUndefined(stackHead)) {
20388                 stackHead = element;
20389                 trapFocusInElement(flag);
20390             } else {
20391                 if (flag) {
20392                     trapFocusInElement(false);
20393                     elementStack.push(stackHead);
20394                     stackHead = element;
20395                     trapFocusInElement(true);
20396                 } else {
20397                     if (stackHead.prop('$$hashKey') === element.prop('$$hashKey')) {
20398                         trapFocusInElement(false);
20399                         stackHead = elementStack.pop();
20400                         if (angular.isDefined(stackHead)) {
20401                             trapFocusInElement(true);
20402                         }
20403                     }
20404                 }
20405             }
20406         }
20407     };
20408
20409     return toggleTrapFocusInElement;
20410 }])
20411
20412 .factory('DOMHelper', function () {
20413
20414     var _isTabable = function (node) {
20415         var element = angular.element(node);
20416         var tagName = element[0].tagName.toUpperCase();
20417
20418         if (isHidden(element)) {
20419             return false;
20420         }
20421         if (element.attr('tabindex') !== undefined) {
20422             return (parseInt(element.attr('tabindex'), 10) >= 0);
20423         }
20424         if (tagName === 'A' || tagName === 'AREA' || tagName === 'BUTTON' || tagName === 'INPUT' || tagName === 'TEXTAREA' || tagName === 'SELECT') {
20425             if (tagName === 'A' || tagName === 'AREA') {
20426                 // anchors/areas without href are not focusable
20427                 return (element[0].href !== '');
20428             }
20429             return !(element[0].disabled || element[0].readOnly);
20430         }
20431         return false;
20432     };
20433
20434     function isValidChild(child) {
20435         return child.nodeType == 1 && child.nodeName != 'SCRIPT' && child.nodeName != 'STYLE';
20436     }
20437
20438     function isHidden(obj) {
20439         var elem = angular.element(obj);
20440         var style;
20441         try {
20442             style = window.getComputedStyle(obj);
20443         }
20444         catch(err) {
20445             style = window.getComputedStyle(obj[0]);
20446         }
20447
20448         // getComputedStyle() for Zepto object returns null
20449         if (style === null){
20450             return elem.hasClass('ng-hide') || elem.css('display') === 'none';
20451         }
20452
20453         return elem.hasClass('ng-hide') || elem.css('display') === 'none' || style.display === 'none' || style.display === 'hidden';
20454     }
20455
20456     function traverse(obj, fromTop) {
20457         var obj = obj || document.getElementsByTagName('body')[0];
20458
20459         if (isValidChild(obj) && _isTabable(obj)) {
20460             return obj;
20461         }
20462
20463         // If object is hidden, skip it's children
20464         if (isValidChild(obj) && isHidden(obj)) {
20465             return undefined;
20466         }
20467         // If object is hidden, skip it's children
20468         if (angular.element(obj).hasClass('ng-hide')) {
20469             return undefined;
20470         }
20471
20472         if (obj.hasChildNodes()) {
20473             var child;
20474             if (fromTop) {
20475                 child = obj.firstChild;
20476             } else {
20477                 child = obj.lastChild;
20478             }
20479             while (child) {
20480                 var res = traverse(child, fromTop);
20481                 if (res) {
20482                     return res;
20483                 } else {
20484                     if (fromTop) {
20485                         child = child.nextSibling;
20486                     } else {
20487                         child = child.previousSibling;
20488                     }
20489                 }
20490             }
20491         } else {
20492             return undefined;
20493         }
20494     }
20495
20496     var _lastTabableElement = function (el) {
20497         /* This will return the last tabable element from the parent el */
20498         var elem = el;
20499         if (el.hasOwnProperty('length')) {
20500             elem = el[0];
20501         }
20502
20503         return traverse(elem, false);
20504     };
20505
20506     var _firstTabableElement = function (el) {
20507         /* This will return the first tabable element from the parent el */
20508         var elem = el;
20509         if (el.hasOwnProperty('length')) {
20510             elem = el[0];
20511         }
20512
20513         return traverse(elem, true);
20514     };
20515
20516     return {
20517         firstTabableElement: _firstTabableElement,
20518         lastTabableElement: _lastTabableElement,
20519         isTabable: _isTabable
20520     };
20521 })
20522
20523 .factory('windowOrientation', ['$window', function ($window) {
20524     var _isPotrait = function () {
20525         if ($window.innerHeight > $window.innerWidth) {
20526             return true;
20527         } else {
20528             return false;
20529         }
20530     };
20531     var _isLandscape = function () {
20532         if ($window.innerHeight < $window.innerWidth) {
20533             return true;
20534         } else {
20535             return false;
20536         }
20537     };
20538
20539     return {
20540         isPotrait: _isPotrait,
20541         isLandscape: _isLandscape
20542     };
20543 }])
20544
20545 .directive('b2bNextElement', function() {
20546   return {
20547     restrict: 'A',
20548     transclude: false,
20549     link: function (scope, elem, attr, ctrls) {
20550
20551         var keys = attr.b2bNextElement.split(',');
20552
20553         elem.bind('keydown', function (e) {
20554             var nextElement = elem.next();
20555             if(e.keyCode == 39 || e.keyCode == 40){ // if e.keyCode in keys
20556                 if(nextElement.length) {
20557                     e.preventDefault();
20558                     nextElement[0].focus();
20559                 }
20560             }
20561         });
20562     }
20563   }
20564 })
20565
20566 .directive('b2bAccessibilityClick', [function () {
20567     return {
20568         restrict: 'A',
20569         link: function (scope, elem, attr, ctrl) {
20570             var keyCode = [];
20571             attr.$observe('b2bAccessibilityClick', function (value) {
20572                 if (value) {
20573                     keyCode = value.split(',');
20574                 }
20575             });
20576             elem.bind('keydown keypress', function (ev) {
20577                 var keyCodeCondition = function () {
20578                     var flag = false;
20579                     if (!(ev.keyCode)) {
20580                         if (ev.which) {
20581                             ev.keyCode = ev.which; 
20582                         } else if (ev.charCode) {
20583                             ev.keyCode = ev.charCode;
20584                         }
20585                     }
20586                     if ((ev.keyCode && keyCode.indexOf(ev.keyCode.toString()) > -1)) {
20587                         flag = true;
20588                     }
20589                     return flag;
20590                 };
20591                 if (keyCode.length > 0 && keyCodeCondition()) {
20592                     elem[0].click();
20593                     ev.preventDefault();
20594                 }
20595             });
20596         }
20597     };
20598 }])
20599
20600 .directive('b2bReset', ['$compile', function ($compile) {
20601         return {
20602             restrict: 'A',
20603             require: ['?ngModel', 'b2bReset'],
20604             controller: ['$scope', function ($scope) {
20605                 var resetButton = angular.element('<button type="button" class="reset-field" tabindex="-1" aria-label="Click to reset" aria-hidden="true" role="button"></button>');
20606
20607                 this.getResetButton = function () {
20608                     return resetButton;
20609                 };
20610             }],
20611             link: function (scope, element, attrs, ctrls) {
20612
20613                 var ngModelCtrl = ctrls[0];
20614                 var ctrl = ctrls[1];
20615
20616                 var resetButton = ctrl.getResetButton();
20617
20618
20619                 resetButton.on('click', function () {
20620                     element[0].value = '';
20621
20622                     if (ngModelCtrl) {
20623                         if (attrs.b2bReset) {
20624                             ngModelCtrl.$setViewValue(attrs.b2bReset);
20625                         } else {
20626                             ngModelCtrl.$setViewValue('');
20627                         }
20628                         element[0].value = ngModelCtrl.$viewValue;
20629                         ngModelCtrl.$render();
20630                         scope.$digest();
20631                     }
20632                     element[0].focus();
20633                     element[0].select();
20634                 });
20635
20636                 var addResetButton = function () {
20637                     element.after(resetButton);
20638                     element.unbind('focus input', addResetButton);
20639                 };
20640
20641                 element.bind('focus input', addResetButton);
20642             }
20643         };
20644     }])
20645
20646 .directive('b2bPrevElement', ['b2bDOMHelper', 'keymap', function (b2bDOMHelper, keymap) {
20647   return {
20648     restrict: 'A',
20649     transclude: false,
20650     link: function (scope, elem, attr) {
20651
20652         elem.bind('keydown', function (e) {
20653             if(e.keyCode == 37 || e.keyCode == 38){
20654                 var prev = b2bDOMHelper.previousElement(elem, false);
20655                 if(prev !== undefined) {
20656                     e.preventDefault();
20657                     prev.focus();
20658                 }
20659             }
20660         });
20661     }
20662   }
20663 }])
20664 /**
20665  * @param {integer} delay - Timeout before first and last focusable elements are found
20666  * @param {boolean} trigger - A variable on scope that will trigger refinding first/last focusable elements 
20667  */
20668 .directive('b2bTrapFocusInsideElement', ['$timeout', 'b2bDOMHelper', 'keymap', 'events', function ($timeout, b2bDOMHelper, keymap, events) {
20669     return {
20670         restrict: 'A',
20671         transclude: false,
20672         link: function (scope, elem, attr) {
20673
20674             var delay = parseInt(attr.delay, 10) || 10;
20675
20676             /* Before opening modal, find the focused element */
20677             var firstTabableElement = undefined,
20678                 lastTabableElement = undefined;
20679                 
20680             function init() {
20681                 $timeout(function () {
20682                     firstTabableElement = b2bDOMHelper.firstTabableElement(elem);
20683                     lastTabableElement = b2bDOMHelper.lastTabableElement(elem);
20684                     angular.element(firstTabableElement).bind('keydown', firstTabableElementKeyhandler);
20685                     angular.element(lastTabableElement).bind('keydown', lastTabableElementKeyhandler);
20686                 }, delay, false);
20687             }
20688
20689             if (attr.trigger !== undefined) {
20690                 scope.$watch('trigger', function() {
20691                     if (scope.trigger) {
20692                         init();
20693                     }
20694                 });
20695             }
20696
20697             var firstTabableElementKeyhandler = function(e) {
20698                 if (!e.keyCode) {
20699                     e.keyCode = e.which;
20700                 }
20701                 if (e.keyCode === keymap.KEY.TAB && e.shiftKey) {
20702                     if (attr.trapFocusInsideElement === 'true') {
20703                         var temp = b2bDOMHelper.lastTabableElement(elem);
20704                         if (lastTabableElement !== temp) {
20705                             // Unbind keydown handler on lastTabableElement
20706                             angular.element(lastTabableElement).unbind('keydown', lastTabableElementKeyhandler);
20707                             lastTabableElement = temp;
20708                             angular.element(lastTabableElement).bind('keydown', lastTabableElementKeyhandler);
20709                         }
20710                     }
20711                     lastTabableElement.focus();
20712                     events.preventDefault(e);
20713                     events.stopPropagation(e);
20714                 }
20715             };
20716
20717             var lastTabableElementKeyhandler = function(e) {
20718                 if (!e.keyCode) {
20719                     e.keyCode = e.which;
20720                 }
20721                 if (e.keyCode === keymap.KEY.TAB && !e.shiftKey) {
20722                     if (attr.trapFocusInsideElement === 'true') {
20723                         var temp = b2bDOMHelper.firstTabableElement(elem);
20724                         if (firstTabableElement !== temp) {
20725                             // Unbind keydown handler on firstTabableElement
20726                             angular.element(firstTabableElement).unbind('keydown', firstTabableElementKeyhandler);
20727                             firstTabableElement = temp;
20728                             angular.element(firstTabableElement).bind('keydown', firstTabableElementKeyhandler);
20729                         }
20730                     }
20731                     firstTabableElement.focus();
20732                     events.preventDefault(e);
20733                     events.stopPropagation(e);
20734                 }
20735             };
20736
20737             init();
20738         }
20739     };
20740 }])
20741
20742 .factory('trapFocusInElement', ['$document', '$isElement', 'DOMHelper', 'keymap', '$interval', function ($document, $isElement, DOMHelper, keymap, $interval) {
20743     var elementStack = [];
20744     var stackHead = undefined;
20745     var stopInterval = undefined;
20746     var intervalRequired = false;
20747     var interval = 1000;
20748     var firstTabableElement, lastTabableElement;
20749
20750     var trapKeyboardFocusInFirstElement = function (e) {
20751         if (!e.keyCode) {
20752             e.keyCode = e.which;
20753         }
20754
20755         if (e.shiftKey === true && e.keyCode === keymap.KEY.TAB) {
20756             lastTabableElement[0].focus();
20757             e.preventDefault(e);
20758             e.stopPropagation(e);
20759         }
20760
20761     };
20762
20763     var trapKeyboardFocusInLastElement = function (e) {
20764         if (!e.keyCode) {
20765             e.keyCode = e.which;
20766         }
20767
20768         if (e.shiftKey === false && e.keyCode === keymap.KEY.TAB) {
20769             firstTabableElement[0].focus();
20770             e.preventDefault(e);
20771             e.stopPropagation(e);
20772         }
20773     };
20774
20775     var trapFocusInElement = function (flag, firstTabableElementParam, lastTabableElementParam) {
20776         var bodyElements = $document.find('body').children();
20777
20778         firstTabableElement = firstTabableElementParam ? firstTabableElementParam : angular.element(DOMHelper.firstTabableElement(stackHead));
20779         lastTabableElement = lastTabableElementParam ? lastTabableElementParam : angular.element(DOMHelper.lastTabableElement(stackHead));
20780
20781         if (flag) {
20782             for (var i = 0; i < bodyElements.length; i++) {
20783                 if (bodyElements[i] !== stackHead[0]) {
20784                     bodyElements.eq(i).attr('aria-hidden', true);
20785                 }
20786             }
20787             firstTabableElement.bind('keydown', trapKeyboardFocusInFirstElement);
20788             lastTabableElement.bind('keydown', trapKeyboardFocusInLastElement);
20789         } else {
20790             for (var j = 0; j < bodyElements.length; j++) {
20791                 if (bodyElements[j] !== stackHead[0]) {
20792                     bodyElements.eq(j).removeAttr('aria-hidden');
20793                 }
20794             }
20795             firstTabableElement.unbind('keydown', trapKeyboardFocusInFirstElement);
20796             lastTabableElement.unbind('keydown', trapKeyboardFocusInLastElement);
20797         }
20798
20799         if (intervalRequired && flag) {
20800             stopInterval = $interval(function () {
20801                 var firstTabableElementTemp = angular.element(DOMHelper.firstTabableElement(stackHead));
20802                 var lastTabableElementTemp = angular.element(DOMHelper.lastTabableElement(stackHead));
20803                 if (firstTabableElementTemp[0] !== firstTabableElement[0] || lastTabableElementTemp[0] !== lastTabableElement[0]) {
20804                     $interval.cancel(stopInterval);
20805                     stopInterval = undefined;
20806                     trapFocusInElement(false, firstTabableElement, lastTabableElement);
20807                     trapFocusInElement(true, firstTabableElementTemp, lastTabableElementTemp);
20808                 }
20809             }, interval);
20810         } else {
20811             if (stopInterval) {
20812                 $interval.cancel(stopInterval);
20813                 stopInterval = undefined;
20814             }
20815         }
20816     };
20817     var toggleTrapFocusInElement = function (flag, element, intervalRequiredParam, intervalParam) {
20818         intervalRequired = intervalRequiredParam ? intervalRequiredParam : intervalRequired;
20819         interval = intervalParam ? intervalParam : interval;
20820         if (angular.isDefined(flag) && angular.isDefined(element)) {
20821             if (flag && angular.isUndefined(stackHead)) {
20822                 stackHead = element;
20823                 trapFocusInElement(flag);
20824             } else {
20825                 if (flag) {
20826                     trapFocusInElement(false);
20827                     elementStack.push(stackHead);
20828                     stackHead = element;
20829                     trapFocusInElement(true);
20830                 } else {
20831                     if (angular.isDefined(stackHead) && (stackHead[0] === element[0])) {
20832                         trapFocusInElement(false);
20833                         stackHead = elementStack.pop();
20834                         if (angular.isDefined(stackHead)) {
20835                             trapFocusInElement(true);
20836                         }
20837                     }
20838                 }
20839             }
20840         } else {
20841             if (angular.isDefined(stackHead)) {
20842                 trapFocusInElement(false, firstTabableElement, lastTabableElement);
20843                 trapFocusInElement(true);
20844             }
20845         }
20846     };
20847
20848     return toggleTrapFocusInElement;
20849 }])
20850
20851 .directive('b2bSetNextFocusOn', ['b2bDOMHelper', '$timeout', function(b2bDOMHelper, $timeout) {
20852     return {
20853         restrict: 'A',
20854         scope: true,
20855         link: function (scope, elem, attr) {
20856             elem.bind('click', function(){
20857                 var firstFocusableElement = undefined; 
20858                 var containerElem = undefined; 
20859                 var containerArray = [];
20860                 var timeout = parseInt(attr.setNextFocusTimeout, 0) | 100;
20861                 var index = parseInt(attr.b2bSetNextFocusIndex, 0) | 0;
20862
20863                  /*
20864                   *Fix for IE7 and lower 
20865                   *polyfill src: https://github.com/HubSpot/pace/issues/102
20866                   */
20867                 if (!document.querySelectorAll) {
20868                     document.querySelectorAll = function (selectors) {
20869                         var style = document.createElement('style'), elements = [], element;
20870                         document.documentElement.firstChild.appendChild(style);
20871                         document._qsa = [];
20872
20873                         style.styleSheet.cssText = selectors + '{x-qsa:expression(document._qsa && document._qsa.push(this))}';
20874                         window.scrollBy(0, 0);
20875                         style.parentNode.removeChild(style);
20876
20877                         while (document._qsa.length) {
20878                             element = document._qsa.shift();
20879                             element.style.removeAttribute('x-qsa');
20880                             elements.push(element);
20881                         }
20882                         document._qsa = null;
20883                         return elements;
20884                     };
20885                 }
20886
20887                 if (attr.b2bSetNextFocusOn === '') {
20888                     return;
20889                 } else {
20890                     containerArray = attr.b2bSetNextFocusOn.split(' ');
20891                 }
20892                 $timeout(function(){
20893                     var i = 0;
20894                     do { // cycles thru containerArray until finds a match in DOM to set focus to
20895                         containerElem = document.querySelectorAll(containerArray[i])[index]; 
20896                         i++;
20897                     } while ( (!containerElem) && (i < containerArray.length) );
20898                     if(containerElem){
20899                         if (!angular.isDefined(firstFocusableElement)) { 
20900                             firstFocusableElement = b2bDOMHelper.firstTabableElement(containerElem); 
20901                         }
20902                         firstFocusableElement.focus(); 
20903                     }
20904                 }, timeout, false)
20905             });
20906         }
20907
20908     };
20909 }])
20910
20911 .directive('b2bInputAllow', [function() {
20912     return {
20913         restrict: 'A',
20914         require: 'ngModel',
20915         link: function (scope, elem, attr, ctrl) {
20916             var regexExpression = null;
20917             attr.$observe('b2bInputAllow', function (value) {
20918                 if (value) {
20919                     regexExpression = new RegExp(value);
20920                 }
20921             });
20922             var isValid = function(str) {
20923                 if (regexExpression !== null) {
20924                     return regexExpression.test(str);
20925                 }
20926                 return false;
20927             };
20928             elem.bind('keypress', function($event) {
20929                 var charcode = String.fromCharCode($event.which || $event.keyCode);
20930                 if (!isValid(charcode)) {
20931                     $event.preventDefault();
20932                     $event.stopPropagation();
20933                 }
20934             });
20935             elem.bind('input', function (evt) {
20936                 var inputString = ctrl.$viewValue;
20937                 if (isValid(inputString)) {
20938                     ctrl.$setViewValue(inputString);
20939                     ctrl.$render();
20940                     scope.$apply();
20941                 }
20942             });
20943         }
20944     };
20945 }])
20946
20947 .directive('b2bInputDeny', [function() {
20948     return {
20949         restrict: 'A',
20950         require: 'ngModel',
20951         link: function (scope, elem, attr, ctrl) {
20952             var regexExpression = null;
20953             attr.$observe('b2bInputDeny', function (value) {
20954                 if (value) {
20955                     regexExpression = new RegExp(value, 'g');
20956                 }
20957             });
20958             elem.bind('input', function () {
20959                 var inputString = ctrl.$viewValue && ctrl.$viewValue.replace(regexExpression, '');
20960                 if (inputString !== ctrl.$viewValue) {
20961                     ctrl.$setViewValue(inputString);
20962                     ctrl.$render();
20963                     scope.$apply();
20964                 }
20965             });
20966         }
20967     };
20968 }])
20969
20970 .directive('b2bDragonInput', [function() {
20971     return {
20972         restrict: 'A',
20973         require: 'ngModel',
20974         link: function (scope, elem, attr, ctrl) {
20975             elem.on('focus keyup', function(){
20976                 elem.triggerHandler('change');
20977             });
20978         }
20979     };
20980 }])
20981
20982 .directive('b2bKey', ['b2bUtilitiesConfig', '$timeout', 'keymap', function (b2bUtilitiesConfig, $timeout, keymap) {
20983     return {
20984         restrict: 'EA',
20985         controller: ['$scope', '$element', '$attrs', function ($scope, $element,attr) {
20986             this.childElements = [];
20987             this.disableNodes = {};
20988             this.enableSearch = attr.enableSearch !== undefined ? true : b2bUtilitiesConfig.enableSearch;
20989             this.circularTraversal = attr.circularTraversal !== undefined ? true : b2bUtilitiesConfig.circularTraversal;
20990             this.counter = -1;
20991             if (this.enableSearch) {
20992                 this.searchKeys = [];
20993             }
20994             var searchString = '';
20995
20996             var selfCtrl = this;
20997
20998             this.childElementsList = [];
20999
21000             this.b2bKeyID = "";
21001
21002             if (angular.isDefined(attr.b2bKey)) {
21003                 this.b2bKeyID = attr.b2bKey;
21004             }
21005
21006             this.calculateChildElementsList = function () {
21007                 return $element[0].querySelectorAll("[b2b-key-item='" + this.b2bKeyID + "']:not([disabled])");
21008             };
21009
21010             this.resetChildElementsList = function () {
21011                 return $timeout(function () {
21012                     selfCtrl.childElementsList = selfCtrl.calculateChildElementsList();
21013                 });
21014             };
21015
21016             this.resetChildElementsList();
21017
21018             $scope.$on('b2b-key-reset-child-elements-list', function () {
21019                 selfCtrl.resetChildElementsList();
21020             });
21021
21022
21023             this.registerElement = function (childElement, searchKey) {
21024                 this.childElements.push(childElement);
21025                 if (this.enableSearch) {
21026                     this.searchKeys.push(searchKey);
21027                 }
21028                 var count = this.childElements.length - 1;
21029                 this.maxLength = count + 1;
21030                 return count;
21031             };
21032             this.toggleDisable = function (count, state) {
21033                 this.disableNodes[count] = state;
21034             };
21035             this.searchElement = function (searchExp) {
21036                 var regex = new RegExp("\\b" + searchExp, "gi");
21037                 var position = this.searchKeys.regexIndexOf(regex, this.counter + 1, true);
21038                 if (position > -1) {
21039                     this.counter = position;
21040                     this.moveFocus(this.counter);
21041                 }
21042             };
21043             this.startTimer = function (time) {
21044                 if (searchString === '') {
21045                     $timeout(function () {
21046                         searchString = '';
21047                     }, time);
21048                 }
21049             };
21050             this.resetCounter = function (count) {
21051                 this.counter = count;
21052             };
21053             this.moveNext = function (count) {
21054                 this.counter = (this.counter + count) < this.maxLength ? this.counter + count : (this.circularTraversal ? 0 : this.counter);
21055                 if (this.disableNodes[this.counter]) {
21056                     if ((this.counter + count) < this.maxLength) {
21057                         this.moveNext(count);
21058                     }
21059                 } else {
21060                     this.moveFocus(this.counter);
21061                 }
21062             };
21063             this.movePrev = function (count) {
21064                 this.counter = (this.counter - count) > -1 ? this.counter - count : (this.circularTraversal ? this.maxLength-1 : this.counter);
21065                 if (this.disableNodes[this.counter]) {
21066                     if ((this.counter - count) > -1) {
21067                         this.movePrev(count);
21068                     }
21069                 } else {
21070                     this.moveFocus(this.counter);
21071                 }
21072             };
21073             this.moveFocus = function (index) {
21074                 this.childElements[index][0].focus();
21075             };
21076
21077             this.keyDownHandler = function (ev, count) {
21078                 if (angular.isDefined(count) && !isNaN(count) && count !== this.counter) {
21079                     this.resetCounter(count);
21080                 }
21081                 if (!ev.keyCode) {
21082                     if (ev.which) {
21083                         ev.keyCode = ev.which;
21084                     } else if (ev.charCode) {
21085                         ev.keyCode = ev.charCode;
21086                     }
21087                 }
21088                 if (ev.keyCode) {
21089                     if (this.prev && this.prev.indexOf(ev.keyCode.toString()) > -1) {
21090                         this.movePrev(1);
21091                         ev.preventDefault();
21092                         ev.stopPropagation();
21093                     } else if (this.next && this.next.indexOf(ev.keyCode.toString()) > -1) {
21094                         this.moveNext(1);
21095                         ev.preventDefault();
21096                         ev.stopPropagation();
21097                     } else if (this.up && this.up.indexOf(ev.keyCode.toString()) > -1) {
21098                         if (this.type === 'table') {
21099                             this.movePrev(this.columns);
21100                             ev.preventDefault();
21101                             ev.stopPropagation();
21102                         }
21103                     } else if (this.down && this.down.indexOf(ev.keyCode.toString()) > -1) {
21104                         if (this.type === 'table') {
21105                             this.moveNext(this.columns);
21106                             ev.preventDefault();
21107                             ev.stopPropagation();
21108                         }
21109                     } else if (ev.keyCode === keymap.KEY.HOME) {
21110                         var firstIndex = 0;
21111                         while (this.disableNodes[firstIndex] !== false) {
21112                             firstIndex++;
21113                         };
21114                         var count = this.counter - firstIndex;
21115                         this.movePrev(count);
21116                         ev.preventDefault();
21117                         ev.stopPropagation();
21118                     } else if (ev.keyCode === keymap.KEY.END) {
21119                         var lastIndex = this.childElements.length - 1;
21120                         while (this.disableNodes[lastIndex] !== false) {
21121                             lastIndex--;
21122                         };
21123                         var count = lastIndex - this.counter;
21124                         this.moveNext(count);
21125                         ev.preventDefault();
21126                         ev.stopPropagation();
21127                     } else if (ev.keyCode >= 48 && ev.keyCode <= 105) {
21128                         if (this.enableSearch) {
21129                             this.startTimer(b2bUtilitiesConfig.searchTimer);
21130                             searchString = searchString + (keymap.MAP[ev.keyCode] || '');
21131                             this.searchElement(searchString);
21132                             ev.preventDefault();
21133                             ev.stopPropagation();
21134                         }
21135                     }
21136                 }
21137             };
21138         }],
21139         link: function (scope, elem, attr, ctrl) {
21140             ctrl.prev = attr.prev ? attr.prev.split(',') : b2bUtilitiesConfig.prev.split(',');
21141             ctrl.next = attr.next ? attr.next.split(',') : b2bUtilitiesConfig.next.split(',');
21142             ctrl.type = attr.type ? attr.type : b2bUtilitiesConfig.type;
21143             if (ctrl.type === 'table') {
21144                 ctrl.up = attr.up ? attr.up.split(',') : b2bUtilitiesConfig.up.split(',');
21145                 ctrl.down = attr.down ? attr.down.split(',') : b2bUtilitiesConfig.down.split(',');
21146                 ctrl.columns = attr.columns ? parseInt(attr.columns, 10) : b2bUtilitiesConfig.columns;
21147             }
21148
21149             elem.bind('keydown', function (ev) {
21150                 ctrl.keyDownHandler(ev);
21151             });
21152         }
21153     };
21154 }])
21155
21156 .directive('b2bKeyItem', [function () {
21157     return {
21158         restrict: 'EA',
21159         link: function (scope, elem, attr, ctrl) {
21160             var parentCtrl = (elem.parent() && elem.parent().controller('b2bKey')) || undefined;
21161             if (angular.isDefined(parentCtrl)) {
21162                 var count = parentCtrl.registerElement(elem, attr.searchKey);
21163                 elem.bind('keydown', function (ev) {
21164                     parentCtrl.keyDownHandler(ev, count);
21165                 });
21166                 scope.$watch(attr.b2bKeyItem, function (value) {
21167                     value = value === undefined ? true : value;
21168                     parentCtrl.toggleDisable(count, !value); 
21169                 });
21170                 scope.$on('$destroy', function () {
21171                     parentCtrl.toggleDisable(count, true);
21172                 });
21173             }
21174         }
21175     };
21176 }])
21177
21178 .directive('b2bElementFocus', [function () {
21179     return {
21180         restrict: 'A',
21181         link: function (scope, elem, attr, ctrl) {
21182             scope.$watch(attr.b2bElementFocus, function (value) {
21183                 if (value === true) {
21184                     elem[0].focus();
21185                 }
21186             });
21187         }
21188     };
21189 }])
21190
21191
21192 .directive('b2bAppendElement', ['$compile', function ($compile) {
21193     return {
21194         restrict: 'A',
21195         link: function (scope, elem, attr, ctrl) {
21196             var parameters = attr.b2bAppendElement.split(':');
21197             if (parameters.length === 1) {
21198                 elem.append(scope.$eval(parameters[0]));
21199             } else if (parameters.length === 2) {
21200                 if (parameters[1] === 'compile') {
21201                     var element = angular.element('<span>' + scope.$eval(parameters[0]) + '</span>');
21202                     elem.append($compile(element)(scope));
21203                 }
21204             }
21205
21206         }
21207     };
21208 }])
21209
21210 .directive('b2bKeyItemRefreshInNgRepeat', [function () {
21211     return {
21212         restrict: 'EA',
21213         require: '^^b2bKey',
21214         link: function (scope, elem, attr, parentCtrl) {
21215             if (angular.isDefined(parentCtrl)) {
21216
21217                 var attrToObserve = 'attrToObserve';
21218
21219                 if (attr.b2bKeyItemRefreshInNgRepeat) {
21220                     attrToObserve = 'b2bKeyItemRefreshInNgRepeat';
21221                 }
21222
21223                 attr.$observe(attrToObserve, function (newVal, oldVal) {
21224                     if (newVal && newVal !== oldVal) {
21225                         parentCtrl.resetChildElementsList();
21226                     }
21227                 });
21228             }
21229         }
21230     };
21231 }])
21232
21233 .constant('b2bMaskConfig', {
21234     maskDefinitions: {
21235         '9': /\d/,
21236         'A': /[a-zA-Z]/,
21237         '*': /[a-zA-Z0-9]/
21238     },
21239     clearOnBlur: false,
21240     clearOnBlurPlaceholder: false,
21241     escChar: '\\',
21242     eventsToHandle: ['input', 'keyup', 'click', 'focus'],
21243     addDefaultPlaceholder: true,
21244     allowInvalidValue: true
21245 })
21246 /**
21247  * @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.
21248  * @param {String} maskPlaceholder - Allows customizing the mask placeholder when a user has focused the input element and while typing in their value
21249  * @param {String} maskPlaceholderChar - Allows customizing the mask placeholder character. The default mask placeholder is _.
21250  * @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.
21251  */
21252 .directive('b2bMask', ['b2bMaskConfig', function(b2bMaskConfig) {
21253     return {
21254         require: 'ngModel',
21255         restrict: 'A',
21256         link: function(scope, element, attrs, ctrl) {
21257             var maskProcessed = false, eventsBound = false,
21258                 maskCaretMap, maskPatterns, maskPlaceholder, maskComponents,
21259                 // Minimum required length of the value to be considered valid
21260                 minRequiredLength,
21261                 value, valueMasked, isValid,
21262                 // Vars for initializing/uninitializing
21263                 originalPlaceholder = attrs.placeholder,
21264                 originalMaxlength = attrs.maxlength,
21265                 // Vars used exclusively in eventHandler()
21266                 oldValue, oldValueUnmasked, oldCaretPosition, oldSelectionLength,
21267                 // Used for communicating if a backspace operation should be allowed between
21268                 // keydownHandler and eventHandler
21269                 preventBackspace;
21270
21271             var options = b2bMaskConfig;
21272
21273             function isFocused (elem) {
21274               return elem === document.activeElement && (!document.hasFocus || document.hasFocus()) && !!(elem.type || elem.href || ~elem.tabIndex);
21275             }
21276
21277             var originalIsEmpty = ctrl.$isEmpty;
21278             ctrl.$isEmpty = function(value) {
21279                 if (maskProcessed) {
21280                     return originalIsEmpty(unmaskValue(value || ''));
21281                 } else {
21282                     return originalIsEmpty(value);
21283                 }
21284             };
21285
21286             function initialize(maskAttr) {
21287                 if (!angular.isDefined(maskAttr)) {
21288                     return uninitialize();
21289                 }
21290                 processRawMask(maskAttr);
21291                 if (!maskProcessed) {
21292                     return uninitialize();
21293                 }
21294                 initializeElement();
21295                 bindEventListeners();
21296                 return true;
21297             }
21298
21299             function initPlaceholder(placeholderAttr) {
21300                 if ( ! placeholderAttr) {
21301                     return;
21302                 }
21303                 maskPlaceholder = placeholderAttr;
21304                 /* If the mask is processed, then we need to update the value
21305                    but don't set the value if there is nothing entered into the element
21306                    and there is a placeholder attribute on the element because that
21307                    will only set the value as the blank maskPlaceholder
21308                    and override the placeholder on the element */
21309                 if (maskProcessed && !(element.val().length === 0 && angular.isDefined(attrs.placeholder))) {
21310                     element.val(maskValue(unmaskValue(element.val())));
21311                 }
21312             }
21313
21314             function initPlaceholderChar() {
21315                 return initialize(attrs.uiMask);
21316             }
21317
21318             var modelViewValue = false;
21319
21320             attrs.$observe('modelViewValue', function(val) {
21321                 if (val === 'true') {
21322                     modelViewValue = true;
21323                 }
21324             });
21325
21326             attrs.$observe('allowInvalidValue', function(val) {
21327                 linkOptions.allowInvalidValue = val === ''? true : !!val;
21328                 formatter(ctrl.$modelValue);
21329             });
21330
21331             function formatter(fromModelValue) {
21332                 if (!maskProcessed) {
21333                     return fromModelValue;
21334                 }
21335                 value = unmaskValue(fromModelValue || '');
21336                 isValid = validateValue(value);
21337                 ctrl.$setValidity('mask', isValid);
21338
21339                 if (!value.length) return undefined;
21340                 if (isValid || linkOptions.allowInvalidValue) {
21341                     return maskValue(value);
21342                 } else {
21343                     return undefined;
21344                 }
21345             }
21346
21347             function parser(fromViewValue) {
21348                 if (!maskProcessed) {
21349                     return fromViewValue;
21350                 }
21351                 value = unmaskValue(fromViewValue || '');
21352                 isValid = validateValue(value);
21353                 /* We have to set viewValue manually as the reformatting of the input
21354                    value performed by eventHandler() doesn't happen until after
21355                    this parser is called, which causes what the user sees in the input
21356                    to be out-of-sync with what the ctrl's $viewValue is set to. */
21357                 ctrl.$viewValue = value.length ? maskValue(value) : '';
21358                 ctrl.$setValidity('mask', isValid);
21359
21360                 if (isValid || linkOptions.allowInvalidValue) {
21361                     return modelViewValue ? ctrl.$viewValue : value;
21362                 }
21363             }
21364
21365             var linkOptions = {};
21366
21367             // to do 
21368             if (attrs.b2bMaskOptions) {
21369                 linkOptions = scope.$eval('[' + attrs.b2bMaskOptions + ']');
21370                 if (angular.isObject(linkOptions[0])) {
21371                     // we can't use angular.copy nor angular.extend, they lack the power to do a deep merge
21372                     linkOptions = (function(original, current) {
21373                         for (var i in original) {
21374                             if (Object.prototype.hasOwnProperty.call(original, i)) {
21375                                 if (current[i] === undefined) {
21376                                     current[i] = angular.copy(original[i]);
21377                                 } else {
21378                                     if (angular.isObject(current[i]) && !angular.isArray(current[i])) {
21379                                         current[i] = angular.extend({}, original[i], current[i]);
21380                                     }
21381                                 }
21382                             }
21383                         }
21384                         return current;
21385                     })(options, linkOptions[0]);
21386                 } else {
21387                     linkOptions = options;  //gotta be a better way to do this..
21388                 }
21389             } else {
21390                 linkOptions = options;
21391             }
21392
21393             attrs.$observe('b2bMask', initialize);
21394             if (angular.isDefined(attrs.maskPlaceholder)) {
21395                 attrs.$observe('maskPlaceholder', initPlaceholder);
21396             }
21397             else {
21398                 attrs.$observe('placeholder', initPlaceholder);
21399             }
21400             if (angular.isDefined(attrs.maskPlaceholderChar)) {
21401                 attrs.$observe('maskPlaceholderChar', initPlaceholderChar);
21402             }
21403
21404             ctrl.$formatters.unshift(formatter);
21405             ctrl.$parsers.unshift(parser);
21406
21407             function uninitialize() {
21408                 maskProcessed = false;
21409                 unbindEventListeners();
21410
21411                 if (angular.isDefined(originalPlaceholder)) {
21412                     element.attr('placeholder', originalPlaceholder);
21413                 } else {
21414                     element.removeAttr('placeholder');
21415                 }
21416
21417                 if (angular.isDefined(originalMaxlength)) {
21418                     element.attr('maxlength', originalMaxlength);
21419                 } else {
21420                     element.removeAttr('maxlength');
21421                 }
21422
21423                 element.val(ctrl.$modelValue);
21424                 ctrl.$viewValue = ctrl.$modelValue;
21425                 return false;
21426             }
21427
21428             function initializeElement() {
21429                 value = oldValueUnmasked = unmaskValue(ctrl.$modelValue || '');
21430                 valueMasked = oldValue = maskValue(value);
21431                 isValid = validateValue(value);
21432                 if (attrs.maxlength) { // Double maxlength to allow pasting new val at end of mask
21433                     element.attr('maxlength', maskCaretMap[maskCaretMap.length - 1] * 2);
21434                 }
21435                 if ( ! originalPlaceholder && linkOptions.addDefaultPlaceholder) {
21436                     element.attr('placeholder', maskPlaceholder);
21437                 }
21438                 var viewValue = ctrl.$modelValue;
21439                 var idx = ctrl.$formatters.length;
21440                 while(idx--) {
21441                     viewValue = ctrl.$formatters[idx](viewValue);
21442                 }
21443                 ctrl.$viewValue = viewValue || '';
21444                 ctrl.$render();
21445             }
21446
21447             function bindEventListeners() {
21448                 if (eventsBound) {
21449                     return;
21450                 }
21451                 element.bind('blur', blurHandler);
21452                 element.bind('mousedown mouseup', mouseDownUpHandler);
21453                 element.bind('keydown', keydownHandler);
21454                 element.bind(linkOptions.eventsToHandle.join(' '), eventHandler);
21455                 eventsBound = true;
21456             }
21457
21458             function unbindEventListeners() {
21459                 if (!eventsBound) {
21460                     return;
21461                 }
21462                 element.unbind('blur', blurHandler);
21463                 element.unbind('mousedown', mouseDownUpHandler);
21464                 element.unbind('mouseup', mouseDownUpHandler);
21465                 element.unbind('keydown', keydownHandler);
21466                 element.unbind('input', eventHandler);
21467                 element.unbind('keyup', eventHandler);
21468                 element.unbind('click', eventHandler);
21469                 element.unbind('focus', eventHandler);
21470                 eventsBound = false;
21471             }
21472
21473             function validateValue(value) {
21474                 // Zero-length value validity is ngRequired's determination
21475                 return value.length ? value.length >= minRequiredLength : true;
21476             }
21477
21478              function unmaskValue(value) {
21479                 var valueUnmasked = '',
21480                     input = element[0],
21481                     maskPatternsCopy = maskPatterns.slice(),
21482                     selectionStart = oldCaretPosition,
21483                     selectionEnd = selectionStart + getSelectionLength(input),
21484                     valueOffset, valueDelta, tempValue = '';
21485                 // Preprocess by stripping mask components from value
21486                 value = value.toString();
21487                 valueOffset = 0;
21488                 valueDelta = value.length - maskPlaceholder.length;
21489                 angular.forEach(maskComponents, function(component) {
21490                     var position = component.position;
21491                     //Only try and replace the component if the component position is not within the selected range
21492                     //If component was in selected range then it was removed with the user input so no need to try and remove that component
21493                     if (!(position >= selectionStart && position < selectionEnd)) {
21494                         if (position >= selectionStart) {
21495                             position += valueDelta;
21496                         }
21497                         if (value.substring(position, position + component.value.length) === component.value) {
21498                             tempValue += value.slice(valueOffset, position);// + value.slice(position + component.value.length);
21499                             valueOffset = position + component.value.length;
21500                         }
21501                     }
21502                 });
21503                 value = tempValue + value.slice(valueOffset);
21504                 angular.forEach(value.split(''), function(chr) {
21505                     if (maskPatternsCopy.length && maskPatternsCopy[0].test(chr)) {
21506                         valueUnmasked += chr;
21507                         maskPatternsCopy.shift();
21508                     }
21509                 });
21510
21511                 return valueUnmasked;
21512             }
21513
21514             function maskValue(unmaskedValue) {
21515                 var valueMasked = '',
21516                         maskCaretMapCopy = maskCaretMap.slice();
21517
21518                 angular.forEach(maskPlaceholder.split(''), function(chr, i) {
21519                     if (unmaskedValue.length && i === maskCaretMapCopy[0]) {
21520                         valueMasked += unmaskedValue.charAt(0) || '_';
21521                         unmaskedValue = unmaskedValue.substr(1);
21522                         maskCaretMapCopy.shift();
21523                     }
21524                     else {
21525                         valueMasked += chr;
21526                     }
21527                 });
21528                 return valueMasked;
21529             }
21530
21531             function getPlaceholderChar(i) {
21532                 var placeholder = angular.isDefined(attrs.uiMaskPlaceholder) ? attrs.uiMaskPlaceholder : attrs.placeholder,
21533                     defaultPlaceholderChar;
21534
21535                 if (angular.isDefined(placeholder) && placeholder[i]) {
21536                     return placeholder[i];
21537                 } else {
21538                     defaultPlaceholderChar = angular.isDefined(attrs.uiMaskPlaceholderChar) && attrs.uiMaskPlaceholderChar ? attrs.uiMaskPlaceholderChar : '_';
21539                     return (defaultPlaceholderChar.toLowerCase() === 'space') ? ' ' : defaultPlaceholderChar[0];
21540                 }
21541             }
21542
21543             /* Generate array of mask components that will be stripped from a masked value
21544                before processing to prevent mask components from being added to the unmasked value.
21545                E.g., a mask pattern of '+7 9999' won't have the 7 bleed into the unmasked value. */
21546             function getMaskComponents() {
21547                 var maskPlaceholderChars = maskPlaceholder.split(''),
21548                         maskPlaceholderCopy, components;
21549
21550                 /* maskCaretMap can have bad values if the input has the ui-mask attribute implemented as an obversable property, e.g. the demo page */
21551                 if (maskCaretMap && !isNaN(maskCaretMap[0])) {
21552                     /* Instead of trying to manipulate the RegEx based on the placeholder characters
21553                        we can simply replace the placeholder characters based on the already built
21554                        maskCaretMap to underscores and leave the original working RegEx to get the proper
21555                        mask components */
21556                     angular.forEach(maskCaretMap, function(value) {
21557                         maskPlaceholderChars[value] = '_';
21558                     });
21559                 }
21560                 maskPlaceholderCopy = maskPlaceholderChars.join('');
21561                 components = maskPlaceholderCopy.replace(/[_]+/g, '_').split('_');
21562                 components = components.filter(function(s) {
21563                     return s !== '';
21564                 });
21565
21566                 /* need a string search offset in cases where the mask contains multiple identical components
21567                    E.g., a mask of 99.99.99-999.99 */
21568                 var offset = 0;
21569                 return components.map(function(c) {
21570                     var componentPosition = maskPlaceholderCopy.indexOf(c, offset);
21571                     offset = componentPosition + 1;
21572                     return {
21573                         value: c,
21574                         position: componentPosition
21575                     };
21576                 });
21577             }
21578
21579             function processRawMask(mask) {
21580                 var characterCount = 0;
21581
21582                 maskCaretMap = [];
21583                 maskPatterns = [];
21584                 maskPlaceholder = '';
21585
21586                 if (angular.isString(mask)) {
21587                     minRequiredLength = 0;
21588
21589                     var isOptional = false,
21590                             numberOfOptionalCharacters = 0,
21591                             splitMask = mask.split('');
21592
21593                     var inEscape = false;
21594                     angular.forEach(splitMask, function(chr, i) {
21595                         if (inEscape) {
21596                             inEscape = false;
21597                             maskPlaceholder += chr;
21598                             characterCount++;
21599                         }
21600                         else if (linkOptions.escChar === chr) {
21601                             inEscape = true;
21602                         }
21603                         else if (linkOptions.maskDefinitions[chr]) {
21604                             maskCaretMap.push(characterCount);
21605
21606                             maskPlaceholder += getPlaceholderChar(i - numberOfOptionalCharacters);
21607                             maskPatterns.push(linkOptions.maskDefinitions[chr]);
21608
21609                             characterCount++;
21610                             if (!isOptional) {
21611                                 minRequiredLength++;
21612                             }
21613
21614                             isOptional = false;
21615                         }
21616                         else if (chr === '?') {
21617                             isOptional = true;
21618                             numberOfOptionalCharacters++;
21619                         }
21620                         else {
21621                             maskPlaceholder += chr;
21622                             characterCount++;
21623                         }
21624                     });
21625                 }
21626                 // Caret position immediately following last position is valid.
21627                 maskCaretMap.push(maskCaretMap.slice().pop() + 1);
21628
21629                 maskComponents = getMaskComponents();
21630                 maskProcessed = maskCaretMap.length > 1 ? true : false;
21631             }
21632
21633             var prevValue = element.val();
21634             function blurHandler() {
21635                 if (linkOptions.clearOnBlur || ((linkOptions.clearOnBlurPlaceholder) && (value.length === 0) && attrs.placeholder)) {
21636                     oldCaretPosition = 0;
21637                     oldSelectionLength = 0;
21638                     if (!isValid || value.length === 0) {
21639                         valueMasked = '';
21640                         element.val('');
21641                         scope.$apply(function() {
21642                             //only $setViewValue when not $pristine to avoid changing $pristine state.
21643                             if (!ctrl.$pristine) {
21644                                 ctrl.$setViewValue('');
21645                             }
21646                         });
21647                     }
21648                 }
21649                 //Check for different value and trigger change.
21650                 if (value !== prevValue) {
21651                     var currentVal = element.val();
21652                     var isTemporarilyEmpty = value === '' && currentVal && angular.isDefined(attrs.uiMaskPlaceholderChar) && attrs.uiMaskPlaceholderChar === 'space';
21653                     if(isTemporarilyEmpty) {
21654                         element.val('');
21655                     }
21656                     triggerChangeEvent(element[0]);
21657                     if(isTemporarilyEmpty) {
21658                         element.val(currentVal);
21659                     }
21660                 }
21661                 prevValue = value;
21662             }
21663
21664             function triggerChangeEvent(element) {
21665                 var change;
21666                 if (angular.isFunction(window.Event) && !element.fireEvent) {
21667                     // modern browsers and Edge
21668                     try {
21669                         change = new Event('change', {
21670                             view: window,
21671                             bubbles: true,
21672                             cancelable: false
21673                         });
21674                     } catch (ex) {
21675                         //this is for certain mobile browsers that have the Event object
21676                         //but don't support the Event constructor 
21677                         change = document.createEvent('HTMLEvents');
21678                         change.initEvent('change', false, true);
21679                     } finally {
21680                         element.dispatchEvent(change);
21681                     }
21682                 } else if ('createEvent' in document) {
21683                     // older browsers
21684                     change = document.createEvent('HTMLEvents');
21685                     change.initEvent('change', false, true);
21686                     element.dispatchEvent(change);
21687                 }
21688                 else if (element.fireEvent) {
21689                     // IE <= 11
21690                     element.fireEvent('onchange');
21691                 }
21692             }
21693
21694             function mouseDownUpHandler(e) {
21695                 if (e.type === 'mousedown') {
21696                     element.bind('mouseout', mouseoutHandler);
21697                 } else {
21698                     element.unbind('mouseout', mouseoutHandler);
21699                 }
21700             }
21701
21702             element.bind('mousedown mouseup', mouseDownUpHandler);
21703
21704             function mouseoutHandler() {
21705                 oldSelectionLength = getSelectionLength(this);
21706                 element.unbind('mouseout', mouseoutHandler);
21707             }
21708
21709             function keydownHandler(e) {
21710                 var isKeyBackspace = e.which === 8,
21711                 caretPos = getCaretPosition(this) - 1 || 0, //value in keydown is pre change so bump caret position back to simulate post change
21712                 isCtrlZ = e.which === 90 && e.ctrlKey; //ctrl+z pressed
21713
21714                 if (isKeyBackspace) {
21715                     while(caretPos >= 0) {
21716                         if (isValidCaretPosition(caretPos)) {
21717                             //re-adjust the caret position.
21718                             //Increment to account for the initial decrement to simulate post change caret position
21719                             setCaretPosition(this, caretPos + 1);
21720                             break;
21721                         }
21722                         caretPos--;
21723                     }
21724                     preventBackspace = caretPos === -1;
21725                 }
21726
21727                 if (isCtrlZ) {
21728                     // prevent IE bug - value should be returned to initial state
21729                     element.val('');
21730                     e.preventDefault();
21731                 }
21732             }
21733
21734             function eventHandler(e) {
21735                 e = e || {};
21736                 // Allows more efficient minification
21737                 var eventWhich = e.which,
21738                         eventType = e.type;
21739
21740                 // Prevent shift and ctrl from mucking with old values
21741                 if (eventWhich === 16 || eventWhich === 91) {
21742                     return;
21743                 }
21744
21745                 var val = element.val(),
21746                         valOld = oldValue,
21747                         valMasked,
21748                         valAltered = false,
21749                         valUnmasked = unmaskValue(val),
21750                         valUnmaskedOld = oldValueUnmasked,
21751                         caretPos = getCaretPosition(this) || 0,
21752                         caretPosOld = oldCaretPosition || 0,
21753                         caretPosDelta = caretPos - caretPosOld,
21754                         caretPosMin = maskCaretMap[0],
21755                         caretPosMax = maskCaretMap[valUnmasked.length] || maskCaretMap.slice().shift(),
21756                         selectionLenOld = oldSelectionLength || 0,
21757                         isSelected = getSelectionLength(this) > 0,
21758                         wasSelected = selectionLenOld > 0,
21759                         // Case: Typing a character to overwrite a selection
21760                         isAddition = (val.length > valOld.length) || (selectionLenOld && val.length > valOld.length - selectionLenOld),
21761                         // Case: Delete and backspace behave identically on a selection
21762                         isDeletion = (val.length < valOld.length) || (selectionLenOld && val.length === valOld.length - selectionLenOld),
21763                         isSelection = (eventWhich >= 37 && eventWhich <= 40) && e.shiftKey, // Arrow key codes
21764
21765                         isKeyLeftArrow = eventWhich === 37,
21766                         // Necessary due to "input" event not providing a key code
21767                         isKeyBackspace = eventWhich === 8 || (eventType !== 'keyup' && isDeletion && (caretPosDelta === -1)),
21768                         isKeyDelete = eventWhich === 46 || (eventType !== 'keyup' && isDeletion && (caretPosDelta === 0) && !wasSelected),
21769                         // Handles cases where caret is moved and placed in front of invalid maskCaretMap position. Logic below
21770                         // ensures that, on click or leftward caret placement, caret is moved leftward until directly right of
21771                         // non-mask character. Also applied to click since users are (arguably) more likely to backspace
21772                         // a character when clicking within a filled input.
21773                         caretBumpBack = (isKeyLeftArrow || isKeyBackspace || eventType === 'click') && caretPos > caretPosMin;
21774
21775                 oldSelectionLength = getSelectionLength(this);
21776
21777                 // These events don't require any action
21778                 if (isSelection || (isSelected && (eventType === 'click' || eventType === 'keyup' || eventType === 'focus'))) {
21779                     return;
21780                 }
21781
21782                 if (isKeyBackspace && preventBackspace) {
21783                     element.val(maskPlaceholder);
21784                     // This shouldn't be needed but for some reason after aggressive backspacing the ctrl $viewValue is incorrect.
21785                     // This keeps the $viewValue updated and correct.
21786                     scope.$apply(function () {
21787                         ctrl.$setViewValue(''); // $setViewValue should be run in angular context, otherwise the changes will be invisible to angular and user code.
21788                     });
21789                     setCaretPosition(this, caretPosOld);
21790                     return;
21791                 }
21792
21793                 // User attempted to delete but raw value was unaffected--correct this grievous offense
21794                 if ((eventType === 'input') && isDeletion && !wasSelected && valUnmasked === valUnmaskedOld) {
21795                     while (isKeyBackspace && caretPos > caretPosMin && !isValidCaretPosition(caretPos)) {
21796                         caretPos--;
21797                     }
21798                     while (isKeyDelete && caretPos < caretPosMax && maskCaretMap.indexOf(caretPos) === -1) {
21799                         caretPos++;
21800                     }
21801                     var charIndex = maskCaretMap.indexOf(caretPos);
21802                     // Strip out non-mask character that user would have deleted if mask hadn't been in the way.
21803                     valUnmasked = valUnmasked.substring(0, charIndex) + valUnmasked.substring(charIndex + 1);
21804
21805                     // If value has not changed, don't want to call $setViewValue, may be caused by IE raising input event due to placeholder
21806                     if (valUnmasked !== valUnmaskedOld)
21807                         valAltered = true;
21808                 }
21809
21810                 // Update values
21811                 valMasked = maskValue(valUnmasked);
21812
21813                 oldValue = valMasked;
21814                 oldValueUnmasked = valUnmasked;
21815
21816                 //additional check to fix the problem where the viewValue is out of sync with the value of the element.
21817                 //better fix for commit 2a83b5fb8312e71d220a497545f999fc82503bd9 (I think)
21818                 if (!valAltered && val.length > valMasked.length)
21819                     valAltered = true;
21820
21821                 element.val(valMasked);
21822
21823                 //we need this check.  What could happen if you don't have it is that you'll set the model value without the user
21824                 //actually doing anything.  Meaning, things like pristine and touched will be set.
21825                 if (valAltered) {
21826                     scope.$apply(function () {
21827                         ctrl.$setViewValue(valMasked); // $setViewValue should be run in angular context, otherwise the changes will be invisible to angular and user code.
21828                     });
21829                 }
21830
21831                 // Caret Repositioning
21832                 // Ensure that typing always places caret ahead of typed character in cases where the first char of
21833                 // the input is a mask char and the caret is placed at the 0 position.
21834                 if (isAddition && (caretPos <= caretPosMin)) {
21835                     caretPos = caretPosMin + 1;
21836                 }
21837
21838                 if (caretBumpBack) {
21839                     caretPos--;
21840                 }
21841
21842                 // Make sure caret is within min and max position limits
21843                 caretPos = caretPos > caretPosMax ? caretPosMax : caretPos < caretPosMin ? caretPosMin : caretPos;
21844
21845                 // Scoot the caret back or forth until it's in a non-mask position and within min/max position limits
21846                 while (!isValidCaretPosition(caretPos) && caretPos > caretPosMin && caretPos < caretPosMax) {
21847                     caretPos += caretBumpBack ? -1 : 1;
21848                 }
21849
21850                 if ((caretBumpBack && caretPos < caretPosMax) || (isAddition && !isValidCaretPosition(caretPosOld))) {
21851                     caretPos++;
21852                 }
21853                 oldCaretPosition = caretPos;
21854                 setCaretPosition(this, caretPos);
21855             }
21856
21857             function isValidCaretPosition(pos) {
21858                 return maskCaretMap.indexOf(pos) > -1;
21859             }
21860
21861             function getCaretPosition(input) {
21862                 if (!input)
21863                     return 0;
21864                 if (input.selectionStart !== undefined) {
21865                     return input.selectionStart;
21866                 } else if (document.selection) {
21867                     if (isFocused(element[0])) {
21868                         // For IE
21869                         input.focus();
21870                         var selection = document.selection.createRange();
21871                         selection.moveStart('character', input.value ? -input.value.length : 0);
21872                         return selection.text.length;
21873                     }
21874                 }
21875                 return 0;
21876             }
21877
21878             function setCaretPosition(input, pos) {
21879                 if (!input)
21880                     return 0;
21881                 if (input.offsetWidth === 0 || input.offsetHeight === 0) {
21882                     return; // Input's hidden
21883                 }
21884                 if (input.setSelectionRange) {
21885                     if (isFocused(element[0])) {
21886                         input.focus();
21887                         input.setSelectionRange(pos, pos);
21888                     }
21889                 }
21890                 else if (input.createTextRange) {
21891                     // For IE
21892                     var range = input.createTextRange();
21893                     range.collapse(true);
21894                     range.moveEnd('character', pos);
21895                     range.moveStart('character', pos);
21896                     range.select();
21897                 }
21898             }
21899
21900             function getSelectionLength(input) {
21901                 if (!input)
21902                     return 0;
21903                 if (input.selectionStart !== undefined) {
21904                     return (input.selectionEnd - input.selectionStart);
21905                 }
21906                 if (window.getSelection) {
21907                     return (window.getSelection().toString().length);
21908                 }
21909                 if (document.selection) {
21910                     return (document.selection.createRange().text.length);
21911                 }
21912                 return 0;
21913             }
21914         }
21915     };
21916 }])
21917 .filter('b2bMultiSepartorHighlight', function($sce) {
21918         return function(text, searchText, searchSeperator) {
21919             var splitText = function(string) {
21920                 if(angular.isDefined(searchSeperator)){
21921                     if (string.indexOf(searchSeperator) > -1) {
21922                         return string.split(searchSeperator);
21923                     } else {
21924                         return string
21925                     }
21926                 }else{
21927                     return string;
21928                 }
21929             }
21930             if (text) {
21931                 var newText = splitText(text);
21932                 var newPhrase = splitText(searchText);
21933                 if (angular.isArray(newPhrase)) {
21934                     for (var i = 0; i < newText.length; i++) {
21935                         if (i <= 0) {
21936                             text = newText[i].replace(new RegExp('(' + newPhrase[i] + ')', 'gi'),
21937                                 '<span class="b2b-search-hightlight">$1</span>');
21938                         } else {
21939                             text = text + searchSeperator + ' ' + (newPhrase[i] ? newText[i].replace(new RegExp('(' + newPhrase[i] + ')', 'gi'),
21940                                 '<span class="b2b-search-hightlight">$1</span>') : newText[i]);
21941                         }
21942                     }
21943                 } else {
21944                     text = text.replace(new RegExp('(' + searchText + ')', 'gi'),
21945                         '<span class="b2b-search-hightlight">$1</span>');
21946                 }
21947             }
21948             return $sce.trustAsHtml(text)
21949         }
21950     })
21951     
21952     .factory('b2bUserAgent', [function() {
21953         var _isMobile = function() {
21954             if(/Android/i.test(navigator.userAgent)){
21955                 return /Mobile/i.test(navigator.userAgent);
21956             }else{
21957                 return /Android|webOS|iPhone|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent);
21958             }
21959             
21960         };
21961         var _notMobile = function() {
21962             if(/Android/i.test(navigator.userAgent)){
21963                 return !/Mobile/i.test(navigator.userAgent);
21964             }else{
21965                 return !/Android|webOS|iPhone|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent);
21966             }
21967             
21968         };
21969         var _isIE = function() {
21970             return /msie|trident/i.test(navigator.userAgent);
21971         };
21972         var _isFF = function() {
21973             return /Firefox/.test(navigator.userAgent);
21974         };
21975         var _isChrome = function() {
21976             return /Google Inc/.test(navigator.vendor);
21977         };
21978         var _isSafari = function() {
21979             return /Safari/.test(navigator.userAgent) && !/Chrome/.test(navigator.userAgent);
21980         };
21981
21982         return {
21983             isMobile: _isMobile,
21984             notMobile: _notMobile,
21985             isIE: _isIE,
21986             isFF: _isFF,
21987             isChrome: _isChrome,
21988             isSafari: _isSafari
21989         };
21990     }])
21991     .run(['$document', 'b2bUserAgent', function($document, b2bUserAgent) {
21992         var html = $document.find('html').eq(0);
21993         if (b2bUserAgent.isIE()) {
21994             html.addClass('isIE');
21995         } else {
21996             html.removeClass('isIE');
21997         }
21998     }]);
21999     
22000
22001 (function () {
22002     String.prototype.toSnakeCase = function () {
22003         return this.replace(/([A-Z])/g, function ($1) {
22004             return "-" + $1.toLowerCase();
22005         });
22006     };
22007     var concat = function (character, times) {
22008         character = character || '';
22009         times = (!isNaN(times) && times) || 0;
22010         var finalChar = '';
22011         for (var i = 0; i < times; i++) {
22012             finalChar += character;
22013         }
22014         return finalChar;
22015     };
22016
22017     // direction: true for left and false for right
22018     var pad = function (actualString, width, character, direction) {
22019         actualString = actualString || '';
22020         width = (!isNaN(width) && width) || 0;
22021         character = character || '';
22022         if (width > actualString.length) {
22023             if (direction) {
22024                 return concat(character, (width - actualString.length)) + actualString;
22025             } else {
22026                 return actualString + concat(character, (width - actualString.length));
22027             }
22028         }
22029         return actualString;
22030     };
22031
22032     String.prototype.lPad = function (width, character) {
22033         return pad(this, width, character, true);
22034     };
22035
22036     String.prototype.rPad = function (width, character) {
22037         return pad(this, width, character, false);
22038     };
22039
22040     if (!Array.prototype.indexOf) {
22041         Array.prototype.indexOf = function (val) {
22042             for (var index = 0; index < this.length; index++) {
22043                 if (this[index] === val) {
22044                     return index;
22045                 }
22046             }
22047             return -1;
22048         };
22049     }
22050
22051     if (!Array.prototype.regexIndexOf) {
22052         Object.defineProperty(Array.prototype, 'regexIndexOf', {
22053             enumerable: false,
22054             value: function (regex, startIndex, loop) {
22055                 startIndex = startIndex && startIndex > -1 ? startIndex : 0;
22056                 for (var index = startIndex; index < this.length; index++) {
22057                     if (this[index].toString().match(regex)) {
22058                         return index;
22059                     }
22060                 }
22061                 if (loop) {
22062                     for (var index = 0; index < startIndex; index++) {
22063                         if (this[index].toString().match(regex)) {
22064                             return index;
22065                         }
22066                     }
22067                 }
22068                 return -1;
22069             }
22070         })
22071     }
22072 })();
22073 angular.module("b2bTemplate/audioPlayer/audioPlayer.html", []).run(["$templateCache", function($templateCache) {
22074   $templateCache.put("b2bTemplate/audioPlayer/audioPlayer.html",
22075     "<div class=\"b2b-audio\">\n" +
22076     "   <audio preload=\"auto\">\n" +
22077     "       <source ng-src=\"{{audio.mp3 | trustedAudioUrl}}\" type=\"audio/mp3\"></source>\n" +
22078     "       <i>Your browser does not support the audio element.</i>\n" +
22079     "    </audio>\n" +
22080     "\n" +
22081     "    <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" +
22082     "       <i class=\"icoControls-pointer\" ng-show='!isPlayInProgress'></i>\n" +
22083     "       <i class=\"icoControls-pause\" ng-show='isPlayInProgress'></i>\n" +
22084     "    </div>\n" +
22085     "\n" +
22086     "    <div class=\"seek-bar-container-wrapper\">\n" +
22087     "       <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" +
22088     "       <div class=\"timing-container\">\n" +
22089     "           <span class=\"timing-container-left\">{{timeFormatter(audio.currentTime)}}</span>\n" +
22090     "           <span class=\"timing-container-right\">{{timeFormatter(audio.duration)}}</span>\n" +
22091     "           <div class=\"timing-container-spacer\"></div>\n" +
22092     "       </div>\n" +
22093     "    </div>\n" +
22094     "       \n" +
22095     "    <b2b-flyout>\n" +
22096     "       <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" +
22097     "           <i class=\"icoControls-mutespeakers\" ng-show=\"audio.currentVolume === 0\"></i>\n" +
22098     "           <i class=\"icoControls-volumedown\" ng-show=\"audio.currentVolume > 0 && audio.currentVolume <= 50\"></i>\n" +
22099     "           <i class=\"icoControls-volumeup\" ng-show=\"audio.currentVolume > 50\"></i>\n" +
22100     "       </div> \n" +
22101     "       \n" +
22102     "       <b2b-flyout-content horizontal-placement=\"center\" flyout-style=\"width:70px; height:190px;\" vertical-placement=\"above\">\n" +
22103     "           <div class=\"b2b-audio-popover text-center\">\n" +
22104     "               <span>Max</span>\n" +
22105     "               <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" +
22106     "               <div class=\"min-label\">Min</div>\n" +
22107     "           </div>\n" +
22108     "       </b2b-flyout-content>\n" +
22109     "   </b2b-flyout>\n" +
22110     "</div>");
22111 }]);
22112
22113 angular.module("b2bTemplate/audioRecorder/audioRecorder.html", []).run(["$templateCache", function($templateCache) {
22114   $templateCache.put("b2bTemplate/audioRecorder/audioRecorder.html",
22115     "<div class=\"b2b-audio-recorder row\">\n" +
22116     "   <div class=\"b2b-elapsed-time span11\">\n" +
22117     "       <div ng-if=\"isRecording\">\n" +
22118     "           <span style=\"padding-right: 25px;\">{{config.whileRecordingMessage}}</span>\n" +
22119     "           <span>{{timeFormatter(elapsedTime)}}</span>\n" +
22120     "       </div>\n" +
22121     "       <span ng-if=\"!isRecording\">{{config.startRecordingMessage}}</span>\n" +
22122     "   </div>      \n" +
22123     "   <div class=\"b2b-controls\" title=\"{{isRecording ? 'Stop' : 'REC'}}\" b2b-accessibility-click=\"13,32\" ng-click=\"toggleRecording()\" role=\"button\">\n" +
22124     "           <i ng-if=\"isRecording\" class=\"icoControls-stop\" ></i>\n" +
22125     "           <i ng-if=\"!isRecording\" class=\"icoControls-record\"></i>\n" +
22126     "    </div>\n" +
22127     "</div>");
22128 }]);
22129
22130 angular.module("b2bTemplate/backToTop/backToTop.html", []).run(["$templateCache", function($templateCache) {
22131   $templateCache.put("b2bTemplate/backToTop/backToTop.html",
22132     "<button class=\"btn-arrow b2b-backtotop-button\" type=\"button\" aria-label=\"Back to top\">\n" +
22133     "    <div class=\"btn-secondary b2b-top-btn\">\n" +
22134     "        <i class=\"icoControls-upPRIMARY\" role=\"img\"></i>\n" +
22135     "    </div>\n" +
22136     "</button>\n" +
22137     "");
22138 }]);
22139
22140 angular.module("b2bTemplate/boardstrip/b2bAddBoard.html", []).run(["$templateCache", function($templateCache) {
22141   $templateCache.put("b2bTemplate/boardstrip/b2bAddBoard.html",
22142     "<div tabindex=\"0\" role=\"menuitem\" b2b-accessibility-click=\"13,32\" ng-click=\"addBoard()\" aria-label=\"Add Board\" class=\"boardstrip-item--add\">\n" +
22143     "    <div class=\"centered\"><i aria-hidden=\"true\" class=\"icoControls-add-maximize\"></i> Add board</div>\n" +
22144     "</div> ");
22145 }]);
22146
22147 angular.module("b2bTemplate/boardstrip/b2bBoard.html", []).run(["$templateCache", function($templateCache) {
22148   $templateCache.put("b2bTemplate/boardstrip/b2bBoard.html",
22149     "<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" +
22150     "    <div ng-transclude></div>\n" +
22151     "    <div class=\"board-caret\" ng-if=\"getCurrentIndex()===boardIndex\">\n" +
22152     "        <div class=\"board-caret-indicator\"></div>\n" +
22153     "        <div class=\"board-caret-arrow-up\"></div>\n" +
22154     "    </div>\n" +
22155     "</li>");
22156 }]);
22157
22158 angular.module("b2bTemplate/boardstrip/b2bBoardstrip.html", []).run(["$templateCache", function($templateCache) {
22159   $templateCache.put("b2bTemplate/boardstrip/b2bBoardstrip.html",
22160     "<div class=\"b2b-boardstrip\">\n" +
22161     "   <div class=\"boardstrip-reel\" role=\"menu\">\n" +
22162     "       <div class=\"prev-items\">\n" +
22163     "           <!-- <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" +
22164     "           <button class=\"btn-arrow arrow\" b2b-accessibility-click=\"13,32\" ng-click=\"prevBoard()\" ng-disabled=\"!isPrevBoard()\">\n" +
22165     "               <div class=\"btn btn-small btn-alt\"><i class=\"icon-primary-left\"></i>\n" +
22166     "               </div>\n" +
22167     "               <span class=\"offscreen-text\">Previous boards</span>\n" +
22168     "           </button>\n" +
22169     "       </div>\n" +
22170     "       <div b2b-add-board on-add-board=\"onAddBoard()\"></div>\n" +
22171     "       <div class=\"board-viewport\"><ul role=\"menu\" class=\"boardstrip-container\" ng-transclude></ul></div>\n" +
22172     "       <div class=\"next-items\">\n" +
22173     "           <!-- <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" +
22174     "           <button class=\"btn-arrow arrow\" b2b-accessibility-click=\"13,32\" ng-click=\"nextBoard()\" ng-disabled=\"!isNextBoard()\">\n" +
22175     "               <div class=\"btn btn-small btn-alt\"><i class=\"icon-primary-right\"></i>\n" +
22176     "               </div>\n" +
22177     "               <span class=\"offscreen-text\">Next boards</span>\n" +
22178     "           </button>\n" +
22179     "       </div>\n" +
22180     "   </div>\n" +
22181     "</div>\n" +
22182     "");
22183 }]);
22184
22185 angular.module("b2bTemplate/calendar/datepicker-popup.html", []).run(["$templateCache", function($templateCache) {
22186   $templateCache.put("b2bTemplate/calendar/datepicker-popup.html",
22187     "<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" +
22188     "    <div class=\"datepicker-days\" style=\"display: block;\">\n" +
22189     "        <div ng-repeat=\"header in headers\" class=\"text-left\" style=\"width: 100%;\" b2b-append-element=\"header\"></div>\n" +
22190     "        <table class=\"table-condensed\">\n" +
22191     "            <thead>\n" +
22192     "                <tr>\n" +
22193     "                    <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" +
22194     "                    <th id=\"month\" tabindex=\"-1\" aria-label=\"{{title}}\" class=\"datepicker-switch\" colspan=\"{{rows[0].length - 2}}\">{{title}}</th>\n" +
22195     "                    <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" +
22196     "                </tr>\n" +
22197     "                <tr ng-show=\"labels.length > 0\">\n" +
22198     "                    <th id=\"{{label.post}}\" class=\"dow\" ng-repeat=\"label in labels\" aria-hidden=\"true\"><span aria-hidden=\"true\">{{label.pre}}</span></th>\n" +
22199     "                </tr>\n" +
22200     "            </thead>\n" +
22201     "            <tbody>\n" +
22202     "                <tr ng-repeat=\"row in rows\">\n" +
22203     "                    <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" +
22204     "                        <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" +
22205     "                </tr>\n" +
22206     "            </tbody>\n" +
22207     "            <tfoot>\n" +
22208     "                <tr ng-repeat=\"footer in footers\">\n" +
22209     "                    <th colspan=\"7\" class=\"text-left\" style=\"width: 278px;\" b2b-append-element=\"footer\"></th>\n" +
22210     "                </tr>\n" +
22211     "            </tfoot>\n" +
22212     "        </table>\n" +
22213     "    </div>\n" +
22214     "</div>");
22215 }]);
22216
22217 angular.module("b2bTemplate/calendar/datepicker.html", []).run(["$templateCache", function($templateCache) {
22218   $templateCache.put("b2bTemplate/calendar/datepicker.html",
22219     "<div>\n" +
22220     "    <span class=\"icon-primary-calendar span12\" ng-class=\"{'disabled': ngDisabled}\" ng-transclude></span>\n" +
22221     "</div>");
22222 }]);
22223
22224 angular.module("b2bTemplate/coachmark/coachmark.html", []).run(["$templateCache", function($templateCache) {
22225   $templateCache.put("b2bTemplate/coachmark/coachmark.html",
22226     "<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" +
22227     "   <i class=\"b2b-coachmark-caret\"></i>\n" +
22228     "   <div class=\"b2b-coachmark-header\">\n" +
22229     "       <div class=\"b2b-coachmark-countlabel\"><span ng-if=\"coachmarkIndex !== 0\">{{coachmarkIndex}} of {{(coachmarks.length-1)}}<span></div>\n" +
22230     "       <div class=\"corner-button\">\n" +
22231     "           <button type=\"button\" ng-focus=\"closeButtonFocus()\" class=\"close\" title=\"close\" aria-label=\"Close\" ng-click=\"closeCoachmark()\"></button>\n" +
22232     "       </div>\n" +
22233     "   </div>\n" +
22234     "   <div class=\"b2b-coachmark-content\">   \n" +
22235     "       <i class=\"icon-misc-dimmer\"></i>\n" +
22236     "       <div class=\"b2b-coachmark-content-header\"><span class=\"offscreen-text\">{{currentCoachmark.offscreenText}}</span>{{currentCoachmark.contentHeader}}</div>\n" +
22237     "       <div class=\"b2b-coachmark-description\">{{currentCoachmark.content}}</div>\n" +
22238     "       <div class=\"b2b-coachmark-btn-group\">\n" +
22239     "           <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" +
22240     "           <button class=\"btn btn-alt\" ng-if=\"currentCoachmark.buttonLabel !== '' && currentCoachmark.buttonLabel !== undefined\" ng-click=\"actionCoachmark(currentCoachmark.buttonLabel)\">{{currentCoachmark.buttonLabel}}</button>\n" +
22241     "       </div>  \n" +
22242     "   </div>  \n" +
22243     "</div>");
22244 }]);
22245
22246 angular.module("b2bTemplate/dropdowns/b2bDropdownDesktop.html", []).run(["$templateCache", function($templateCache) {
22247   $templateCache.put("b2bTemplate/dropdowns/b2bDropdownDesktop.html",
22248     "<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" +
22249     "    <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" +
22250     "    <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" +
22251     "    <div ng-class=\"{'selectWrapper': (isInputDropdown), 'moduleWrapper': (!isInputDropdown)}\">\n" +
22252     "        <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" +
22253     "        <ul class=\"module-optinalcta\" ng-if=\"toggleFlag && optionalCta\" tabindex=\"-1\" aria-hidden=\"false\">\n" +
22254     "            <li class=\"module-list-item\" tabindex=\"-1\" role=\"menuitem\" value=\"\" aria-label=\"Optinal CTA\" aria-selected=\"true\">\n" +
22255     "                <span class=\"module-data\" b2b-append-element=\"optionalCta\"></span>\n" +
22256     "            </li>\n" +
22257     "        </ul>\n" +
22258     "</div>\n" +
22259     "<i class=\"icon-primary-down\" aria-hidden=\"true\"></i>\n" +
22260     "</span>");
22261 }]);
22262
22263 angular.module("b2bTemplate/dropdowns/b2bDropdownGroupDesktop.html", []).run(["$templateCache", function($templateCache) {
22264   $templateCache.put("b2bTemplate/dropdowns/b2bDropdownGroupDesktop.html",
22265     "<li b2b-dropdown-group-desktop class=\"optgroup-wrapper\">{{groupHeader}}\n" +
22266     "    <ul class=\"optgroup\" role=\"group\" aria-label=\"{{groupHeader}}\"></ul>\n" +
22267     "</li>");
22268 }]);
22269
22270 angular.module("b2bTemplate/dropdowns/b2bDropdownListDesktop.html", []).run(["$templateCache", function($templateCache) {
22271   $templateCache.put("b2bTemplate/dropdowns/b2bDropdownListDesktop.html",
22272     "<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>");
22273 }]);
22274
22275 angular.module("b2bTemplate/fileUpload/fileUpload.html", []).run(["$templateCache", function($templateCache) {
22276   $templateCache.put("b2bTemplate/fileUpload/fileUpload.html",
22277     "<label class=\"b2b-file-container\">\n" +
22278     "   <span class=\"b2b-upload-link\" ng-transclude></span>\n" +
22279     "   <input type=\"file\" b2b-file-change>\n" +
22280     "</label>");
22281 }]);
22282
22283 angular.module("b2bTemplate/flyout/flyout.html", []).run(["$templateCache", function($templateCache) {
22284   $templateCache.put("b2bTemplate/flyout/flyout.html",
22285     "<span class=\"b2b-flyout\"  b2b-flyout-trap-focus-inside>\n" +
22286     "    <span ng-transclude></span>\n" +
22287     "</span>");
22288 }]);
22289
22290 angular.module("b2bTemplate/flyout/flyoutContent.html", []).run(["$templateCache", function($templateCache) {
22291   $templateCache.put("b2bTemplate/flyout/flyoutContent.html",
22292     "<div class=\"b2b-flyout-container\" aria-live=\"polite\" aria-atomic=\"false\" ng-class=\"{'b2b-flyout-left':horizontalPlacement==='left',\n" +
22293     "                'b2b-flyout-center':horizontalPlacement==='center', \n" +
22294     "                'b2b-flyout-right':horizontalPlacement==='right',\n" +
22295     "                'b2b-flyout-centerLeft':horizontalPlacement==='centerLeft',\n" +
22296     "                'b2b-flyout-centerRight':horizontalPlacement==='centerRight',  \n" +
22297     "                'b2b-flyout-above':verticalPlacement==='above', \n" +
22298     "                'b2b-flyout-below':verticalPlacement==='below',\n" +
22299     "                'open-flyout': openFlyout,\n" +
22300     "                'b2b-close-flyout': !openFlyout}\">\n" +
22301     "    <i class=\"b2b-flyout-caret\" ng-class=\"{'open-flyout': openFlyout, \n" +
22302     "                                   'b2b-flyout-caret-above':verticalPlacement==='above',\n" +
22303     "                                   'b2b-flyout-caret-below':verticalPlacement==='below'}\"></i>\n" +
22304     "    <span ng-transclude></span>\n" +
22305     "</div>");
22306 }]);
22307
22308 angular.module("b2bTemplate/footer/footer_column_switch_tpl.html", []).run(["$templateCache", function($templateCache) {
22309   $templateCache.put("b2bTemplate/footer/footer_column_switch_tpl.html",
22310     "<div class=\"footer-columns \" ng-class=\"{'five-column':footerColumns===5, 'four-column':footerColumns===4, 'three-column':footerColumns===3}\" ng-repeat=\"item in footerLinkItems\">\n" +
22311     "    <h3 class=\"b2b-footer-header\">{{item.categoryName}}</h3>\n" +
22312     "    <ul>\n" +
22313     "        <li ng-repeat=\"i in item.values\">\n" +
22314     "            <a href=\"{{i.href}}\" title=\"{{item.categoryName}} - {{i.displayLink}}\">{{i.displayLink}}</a>  \n" +
22315     "        </li>\n" +
22316     "    </ul>\n" +
22317     "\n" +
22318     "</div>\n" +
22319     "\n" +
22320     "<div ng-transclude></div>\n" +
22321     "");
22322 }]);
22323
22324 angular.module("b2bTemplate/horizontalTable/horizontalTable.html", []).run(["$templateCache", function($templateCache) {
22325   $templateCache.put("b2bTemplate/horizontalTable/horizontalTable.html",
22326     "<div class=\"b2b-horizontal-table\">\n" +
22327     "    <div class=\"b2b-horizontal-table-arrows row span12\">\n" +
22328     "       <div class=\"span4 b2b-prev-link\">\n" +
22329     "           <a href=\"javascript:void(0)\" ng-click=\"moveViewportLeft()\" ddh-accessibility-click=\"13,32\" ng-if=\"!disableLeft\">Previous Set</a>\n" +
22330     "            <span ng-if=\"disableLeft\" class=\"b2b-disabled-text\">Previous Set</span>\n" +
22331     "        </div>\n" +
22332     "        \n" +
22333     "        <span class=\"span5 b2b-horizontal-table-column-info\" aria-live=\"polite\" tabindex=\"-1\">\n" +
22334     "           Displaying {{getColumnSet()[0]}} - {{getColumnSet()[1]}} of {{numOfCols}} (total) columns\n" +
22335     "        </span>\n" +
22336     "\n" +
22337     "        <div ng-if=\"legendContent\" class=\"span2 b2b-horizontal-table-legend\">\n" +
22338     "            <b2b-flyout>\n" +
22339     "                <div tabindex=\"0\" role=\"button\" aria-haspopup=\"true\" b2b-flyout-toggler b2b-accessibility-click=\"13,32\" aria-expanded=\"{{flyoutOpened ? 'true' : 'false'}}\">\n" +
22340     "                    Legend\n" +
22341     "                    <i class=\"icoControls-down\" role=\"img\"></i>\n" +
22342     "                </div>\n" +
22343     "              <b2b-flyout-content horizontal-placement=\"center\" vertical-placement=\"below\">\n" +
22344     "                <div ng-bind-html=\"legendContent\"></div>\n" +
22345     "              </b2b-flyout-content>\n" +
22346     "            </b2b-flyout>\n" +
22347     "        </div>\n" +
22348     "        \n" +
22349     "        <div class=\"span3 text-right b2b-next-link\">\n" +
22350     "            <a href=\"javascript:void(0)\" ng-click=\"moveViewportRight()\" ddh-accessibility-click=\"13,32\" ng-if=\"!disableRight\">Next Set</a>\n" +
22351     "            <span ng-if=\"disableRight\" class=\"b2b-disabled-text\">Next Set</span>\n" +
22352     "       </div>\n" +
22353     "    </div>\n" +
22354     "    <div class=\"b2b-horizontal-table-inner-container\">\n" +
22355     "        <span ng-transclude></span>\n" +
22356     "    </div>\n" +
22357     "</div>\n" +
22358     "\n" +
22359     "");
22360 }]);
22361
22362 angular.module("b2bTemplate/hourPicker/b2bHourpicker.html", []).run(["$templateCache", function($templateCache) {
22363   $templateCache.put("b2bTemplate/hourPicker/b2bHourpicker.html",
22364     "<div class=\"hp-container\">\n" +
22365     "    <div class=\"hp-selected\">\n" +
22366     "        <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" +
22367     "    </div>\n" +
22368     "    <div b2b-hourpicker-panel></div>\n" +
22369     "</div>");
22370 }]);
22371
22372 angular.module("b2bTemplate/hourPicker/b2bHourpickerPanel.html", []).run(["$templateCache", function($templateCache) {
22373   $templateCache.put("b2bTemplate/hourPicker/b2bHourpickerPanel.html",
22374     "<form name=\"{{'hourpickerForm' + $id}}\">\n" +
22375     "    <div class=\"hp-checkbox\" role=\"group\">\n" +
22376     "        <label class=\"checkbox\" for=\"checkbox_{{dayOption.title}}_{{$id}}\" ng-repeat=\"dayOption in hourpicker.dayOptions\">\n" +
22377     "            <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" +
22378     "        </label>\n" +
22379     "    </div>\n" +
22380     "    <div class=\"row hp-dropdowns\">\n" +
22381     "        <div class=\"span4\">\n" +
22382     "            <label for=\"{{'hourpickerStartTime' + $id}}\">From</label>\n" +
22383     "            <select id=\"{{'hourpickerStartTime' + $parent.$id}}\" b2b-dropdown type=\"\" ng-model=\"hourpickerPanelValue.startTime\">\n" +
22384     "                <option b2b-dropdown-list value=\"\">From</option>\n" +
22385     "                <option b2b-dropdown-list option-repeat=\"startTimeOption in hourpicker.startTimeOptions\" value=\"{{startTimeOption}}\">{{startTimeOption}}</option>\n" +
22386     "            </select>\n" +
22387     "        </div>\n" +
22388     "        <div class=\"span8 radio-buttons\" role=\"radiogroup\" aria-label=\"Select AM or PM\">\n" +
22389     "            <label class=\"radio\" for=\"hourpickerStartMeridiem_AM_{{$id}}\">\n" +
22390     "                <input type=\"radio\" id=\"hourpickerStartMeridiem_AM_{{$id}}\" name=\"{{'hourpickerStartMeridiem' + $id}}\" value=\"am\" ng-model=\"hourpickerPanelValue.startMeridiem\" /><i class=\"skin\"></i><span>AM</span>\n" +
22391     "            </label>\n" +
22392     "            <label class=\"radio\" for=\"hourpickerStartMeridiem_PM_{{$id}}\">\n" +
22393     "                <input type=\"radio\" id=\"hourpickerStartMeridiem_PM_{{$id}}\" name=\"{{'hourpickerStartMeridiem' + $id}}\" value=\"pm\" ng-model=\"hourpickerPanelValue.startMeridiem\" /><i class=\"skin\"></i><span>PM</span>\n" +
22394     "            </label>\n" +
22395     "        </div>\n" +
22396     "    </div>\n" +
22397     "    <div class=\"row hp-dropdowns\">\n" +
22398     "        <div class=\"span4\">\n" +
22399     "            <label for=\"{{'hourpickerEndTime' + $id}}\">To</label>\n" +
22400     "            <select id=\"{{'hourpickerEndTime' + $parent.$id}}\" b2b-dropdown ng-model=\"hourpickerPanelValue.endTime\">\n" +
22401     "                <option b2b-dropdown-list value=\"\">To</option>\n" +
22402     "                <option b2b-dropdown-list option-repeat=\"endTimeOption in hourpicker.endTimeOptions\" value=\"{{endTimeOption}}\">{{endTimeOption}}</option>\n" +
22403     "            </select>\n" +
22404     "        </div>\n" +
22405     "        <div class=\"span8 radio-buttons\" role=\"radiogroup\" aria-label=\"Select AM or PM\">\n" +
22406     "            <label class=\"radio\" for=\"hourpickerEndMeridiem_AM_{{$id}}\">\n" +
22407     "                <input type=\"radio\" id=\"hourpickerEndMeridiem_AM_{{$id}}\" name=\"{{'hourpickerEndMeridiem' + $id}}\" value=\"am\" ng-model=\"hourpickerPanelValue.endMeridiem\" /><i class=\"skin\"></i><span>AM</span>\n" +
22408     "            </label>\n" +
22409     "            <label class=\"radio\" for=\"hourpickerEndMeridiem_PM_{{$id}}\">\n" +
22410     "                <input type=\"radio\" id=\"hourpickerEndMeridiem_PM_{{$id}}\" name=\"{{'hourpickerEndMeridiem' + $id}}\" value=\"pm\" ng-model=\"hourpickerPanelValue.endMeridiem\" /><i class=\"skin\"></i><span>PM</span>\n" +
22411     "            </label>\n" +
22412     "        </div>\n" +
22413     "    </div>\n" +
22414     "    <div class=\"row hp-buttons\">\n" +
22415     "        <div class=\"span12\">\n" +
22416     "            <div style=\"float:right\">\n" +
22417     "                <button class=\"btn btn-secondary btn-small\" ng-click=\"resetHourpickerPanelValue()\">Clear</button>\n" +
22418     "                <button class=\"btn btn-alt btn-small\" ng-disabled = \"disableAddBtn\" ng-click=\"updateHourpickerValue()\">{{hourpicker.editMode > -1 ? 'Update' : 'Add'}}</button>\n" +
22419     "            </div>\n" +
22420     "        </div>\n" +
22421     "    </div>\n" +
22422     "</form>");
22423 }]);
22424
22425 angular.module("b2bTemplate/hourPicker/b2bHourpickerValue.html", []).run(["$templateCache", function($templateCache) {
22426   $templateCache.put("b2bTemplate/hourPicker/b2bHourpickerValue.html",
22427     "<div class=\"selected-days\">\n" +
22428     "    <span class=\"day\">{{hourpickerValue.days}} &nbsp; {{hourpickerValue.startTime}} {{hourpickerValue.startMeridiem}} - {{hourpickerValue.endTime}} {{hourpickerValue.endMeridiem}}</span>\n" +
22429     "    <span style=\"float:right\">\n" +
22430     "        <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" +
22431     "        <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" +
22432     "    </span>\n" +
22433     "    <div style=\"clear:both\"></div>\n" +
22434     "</div>");
22435 }]);
22436
22437 angular.module("b2bTemplate/leftNavigation/leftNavigation.html", []).run(["$templateCache", function($templateCache) {
22438   $templateCache.put("b2bTemplate/leftNavigation/leftNavigation.html",
22439     "<div class=\"b2b-nav-menu\">\n" +
22440     "    <div class=\"b2b-subnav-container\">\n" +
22441     "        <ul class=\"b2b-subnav-content\">\n" +
22442     "            <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" +
22443     "                <ul ng-class=\"{expand: idx==$index}\">\n" +
22444     "                    <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" +
22445     "                </ul>\n" +
22446     "            </li>\n" +
22447     "        </ul>\n" +
22448     "    </div>\n" +
22449     "</div>");
22450 }]);
22451
22452 angular.module("b2bTemplate/listbox/listbox.html", []).run(["$templateCache", function($templateCache) {
22453   $templateCache.put("b2bTemplate/listbox/listbox.html",
22454     "<div class=\"b2b-list-box\" tabindex=\"0\" role=\"listbox\" ng-transclude>\n" +
22455     "</div>");
22456 }]);
22457
22458 angular.module("b2bTemplate/modalsAndAlerts/b2b-backdrop.html", []).run(["$templateCache", function($templateCache) {
22459   $templateCache.put("b2bTemplate/modalsAndAlerts/b2b-backdrop.html",
22460     "<div class=\"b2b-modal-backdrop fade in hidden-by-modal\" aria-hidden=\"true\" tabindex=\"-1\"></div>");
22461 }]);
22462
22463 angular.module("b2bTemplate/modalsAndAlerts/b2b-window.html", []).run(["$templateCache", function($templateCache) {
22464   $templateCache.put("b2bTemplate/modalsAndAlerts/b2b-window.html",
22465     "<div class=\"modalwrapper active {{windowClass}}\" ng-class=\"{'modal-landscape': isModalLandscape}\" role=\"{{isNotifDialog?'alertdialog':'dialog'}}\" tabindex=\"-1\" aria-labelledby=\"{{title}}\" aria-describedby=\"{{content}}\">\n" +
22466     "    <div class=\"modal fade in {{sizeClass}}\" ng-transclude></div>\n" +
22467     "</div>");
22468 }]);
22469
22470 angular.module("b2bTemplate/monthSelector/monthSelector-popup.html", []).run(["$templateCache", function($templateCache) {
22471   $templateCache.put("b2bTemplate/monthSelector/monthSelector-popup.html",
22472     "<div id=\"monthselector{{$id}}\" class=\"datepicker dropdown-menu monthselector\" \n" +
22473     "     ng-class=\"{'datepicker-dropdown datepicker-orient-top': !inline, 'datepicker-orient-left': !inline && orientation === 'left', 'datepicker-orient-right': !inline && orientation === 'right'}\" \n" +
22474     "     ng-style=\"{position: inline && 'relative', 'z-index': inline && '0', top: !inline && position.top + 'px' || 0, left: !inline && position.left + 'px'}\" \n" +
22475     "     style=\"display: block;\" aria-hidden=\"false\" role=\"dialog presentation\" tabindex=\"-1\">\n" +
22476     "    <div class=\"datepicker-days\" style=\"display: block;\">\n" +
22477     "        <span class=\"offscreen-text\" aria-live=\"polite\" aria-atomic=\"true\">{{title}} displaying</span>\n" +
22478     "        <table class=\"table-condensed\" role=\"grid\">\n" +
22479     "            <thead>\n" +
22480     "                <tr ng-repeat=\"header in headers\">\n" +
22481     "                    <th colspan=\"7\" class=\"text-left\" style=\"width: 278px;\" b2b-append-element=\"header\"></th>\n" +
22482     "                </tr>\n" +
22483     "                <tr>\n" +
22484     "                    <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" +
22485     "                    <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" +
22486     "                    <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" +
22487     "                </tr>\n" +
22488     "                <tr ng-show=\"labels.length > 0\">\n" +
22489     "                    <th id=\"{{label.post}}\" class=\"dow\" ng-repeat=\"label in labels\" aria-hidden=\"true\"><span aria-hidden=\"true\">{{label.pre}}</span></th>\n" +
22490     "                </tr>\n" +
22491     "            </thead>\n" +
22492     "            <tbody b2b-key type=\"table\" columns=\"4\" >\n" +
22493     "                <tr ng-repeat=\"row in rows\">\n" +
22494     "                    <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" +
22495     "                        <div aria-hidden=\"true\"  tabindex=\"-1\" class=\"show-date\" ng-if=\"!(dt.oldMonth || dt.nextMonth)\">{{dt.label | limitTo: 3}}</div>\n" +
22496     "                    </td>\n" +
22497     "                </tr>\n" +
22498     "            </tbody>\n" +
22499     "            <tfoot>\n" +
22500     "                <tr ng-repeat=\"footer in footers\">\n" +
22501     "                    <th colspan=\"7\" class=\"text-left\" b2b-append-element=\"footer\"></th>\n" +
22502     "                </tr>\n" +
22503     "            </tfoot>\n" +
22504     "        </table>\n" +
22505     "    </div>\n" +
22506     "</div>");
22507 }]);
22508
22509 angular.module("b2bTemplate/monthSelector/monthSelector.html", []).run(["$templateCache", function($templateCache) {
22510   $templateCache.put("b2bTemplate/monthSelector/monthSelector.html",
22511     "<div>\n" +
22512     "    <span class=\"icon-primary-calendar span12\" ng-class=\"{'disabled': ngDisabled}\" ng-transclude></span>\n" +
22513     "</div>");
22514 }]);
22515
22516 angular.module("b2bTemplate/monthSelector/monthSelectorLink.html", []).run(["$templateCache", function($templateCache) {
22517   $templateCache.put("b2bTemplate/monthSelector/monthSelectorLink.html",
22518     "<div>\n" +
22519     "    <span class=\"span12\" ng-transclude></span>\n" +
22520     "</div>");
22521 }]);
22522
22523 angular.module("b2bTemplate/pagination/b2b-pagination.html", []).run(["$templateCache", function($templateCache) {
22524   $templateCache.put("b2bTemplate/pagination/b2b-pagination.html",
22525     "<div class=\"b2b-pager\">\n" +
22526     "    <div ng-if=\"notMobile && totalPages > 1\">\n" +
22527     "        <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" +
22528     "            <i class=\"icon-primary-left\"></i>\n" +
22529     "        </a>\n" +
22530     "        <a 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)\">\n" +
22531     "            1<span class=\"offscreen-text\" ng-if=\"currentPage === 1\"> is selected</span>\n" +
22532     "        </a>\n" +
22533     "        <a tabindex=\"{{currentPage === 2 ? -1 : 0}}\" ng-class=\"{'b2b-pager__item--noclick': currentPage === 2}\" class=\"b2b-pager__item b2b-pager__item--link\" title=\"Page 2{{checkSelectedPage(page) ? ' selected' : '' }}\" ng-if=\"totalPages > 10 && currentPage > 6\" b2b-accessibility-click=\"13,32\" ng-click=\"selectPage(2, $event)\">\n" +
22534     "            2<span class=\"offscreen-text\" ng-if=\"currentPage === 2\"> is selected</span></a>\n" +
22535     "\n" +
22536     "        <span class=\"b2b-pager__item\" ng-if=\"totalPages > 10 && currentPage > 6\">...</span>\n" +
22537     "\n" +
22538     "        <a 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-accessibility-click=\"13,32\" ng-click=\"!checkSelectedPage(page) && selectPage(page, $event)\">\n" +
22539     "            {{page}}<span class=\"offscreen-text\" ng-if=\"checkSelectedPage(page)\"> is selected</span></a>\n" +
22540     "\n" +
22541     "        <span class=\"b2b-pager__item\" ng-if=\"totalPages > 10 && currentPage <= totalPages - 5\">...</span>\n" +
22542     "\n" +
22543     "        <a tabindex=\"{{checkSelectedPage(page) ? -1 : 0}}\" href=\"javascript:void(0)\" ng-class=\"{'b2b-pager__item--noclick': checkSelectedPage(page)}\" class=\"b2b-pager__item b2b-pager__item--link\" title=\"Page {{totalPages-1}}\" ng-if=\"totalPages > 10 && currentPage <= totalPages - 5\" b2b-accessibility-click=\"13,32\" ng-click=\"selectPage(totalPages-1, $event)\">{{totalPages-1}}<span class=\"offscreen-text\" ng-if=\"checkSelectedPage(page)\"> is selected</span></a>\n" +
22544     "\n" +
22545     "        <a 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 - 5\" b2b-accessibility-click=\"13,32\" ng-click=\"selectPage(totalPages, $event)\">{{totalPages}}<span class=\"offscreen-text\" ng-if=\"checkSelectedPage(page)\"> is selected</span></a>\n" +
22546     "\n" +
22547     "        <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" +
22548     "            <i class=\"icon-primary-right\"></i>\n" +
22549     "        </a>\n" +
22550     "        \n" +
22551     "        <span class=\"fieldLabel\" ng-show=\"totalPages > 10 && showInput === true\">    \n" +
22552     "            <label for=\"{{inputId}}\">Go to Page:</label>\n" +
22553     "            <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" +
22554     "            <button class=\"btn-arrow\" ng-click=\"gotoBtnClick()\" ng-disabled=\"$parent.gotoPage !== 0 || $parent.gotoPage !== undefined\" aria-label=\"Go to\">\n" +
22555     "                <div class=\"btn btn-small btn-secondary\">\n" +
22556     "                    <i class=\"icon-primary-right\"></i>\n" +
22557     "                </div>\n" +
22558     "            </button>\n" +
22559     "        </span>\n" +
22560     "    </div>\n" +
22561     "    <div ng-if=\"isMobile && totalPages > 1\" ng-swipe-right=\"prev()\" ng-swipe-left=\"next()\" ng-class=\"isMobile ? 'b2b-mobile-view': '' \">\n" +
22562     "        <a 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" +
22563     "    </div>\n" +
22564     "</div>\n" +
22565     "");
22566 }]);
22567
22568 angular.module("b2bTemplate/paneSelector/paneSelector.html", []).run(["$templateCache", function($templateCache) {
22569   $templateCache.put("b2bTemplate/paneSelector/paneSelector.html",
22570     "<div class=\"panes\" ng-transclude></div>");
22571 }]);
22572
22573 angular.module("b2bTemplate/paneSelector/paneSelectorPane.html", []).run(["$templateCache", function($templateCache) {
22574   $templateCache.put("b2bTemplate/paneSelector/paneSelectorPane.html",
22575     "<div class=\"pane-block\" ng-transclude></div>");
22576 }]);
22577
22578 angular.module("b2bTemplate/profileCard/profileCard-addUser.html", []).run(["$templateCache", function($templateCache) {
22579   $templateCache.put("b2bTemplate/profileCard/profileCard-addUser.html",
22580     "<div  class=\"span3 b2b-profile-card b2b-add-user\">\n" +
22581     "    <div class=\"atcenter\">\n" +
22582     "        <div class=\"circle\"><i class=\"icon-primary-add-maximize\"></i></div>\n" +
22583     "        <div>Create new user</div>\n" +
22584     "    </div>\n" +
22585     "</div>");
22586 }]);
22587
22588 angular.module("b2bTemplate/profileCard/profileCard.html", []).run(["$templateCache", function($templateCache) {
22589   $templateCache.put("b2bTemplate/profileCard/profileCard.html",
22590     "<div class=\"span3 b2b-profile-card\">\n" +
22591     "    <div class=\"top-block\">\n" +
22592     "       <div class=\"profile-image\">\n" +
22593     "            <img ng-if=\"image\" profile-name=\"{{profile.name}}\" ng-src=\"{{profile.img}}\" alt=\"{{profile.name}}\">\n" +
22594     "            <span ng-if=\"!image\" class=\"default-img\">{{initials}}</span>\n" +
22595     "\n" +
22596     "            <h4 class=\"name\">{{profile.name}}</h4>\n" +
22597     "\n" +
22598     "            <p class=\"status\">\n" +
22599     "                <span class=\"status-icon\" ng-class=\"{'status-green':colorIcon==='green','status-red':colorIcon==='red','status-blue':colorIcon==='blue','status-yellow':colorIcon==='yellow'}\">   \n" +
22600     "                </span>\n" +
22601     "                <span>{{profile.state}}<span ng-if=\"badge\" class=\"status-badge\">Admin</span></span>\n" +
22602     "            </p>\n" +
22603     "        </div>\n" +
22604     "    </div>\n" +
22605     "    <div class=\"bottom-block\">\n" +
22606     "         <div class=\"profile-details\">\n" +
22607     "            <label>Username</label>\n" +
22608     "            <div ng-if=\"shouldClip(profile.userName)\" ng-mouseover=\"showUserNameTooltip = true;\" ng-mouseleave=\"showUserNameTooltip = false;\">\n" +
22609     "                <div ng-if=\"shouldClip(profile.userName)\" class=\"tooltip\" b2b-tooltip show-tooltip=\"showUserNameTooltip\">\n" +
22610     "                    {{profile.userName.slice(0, 25)+'...'}}\n" +
22611     "                    <div class=\"arrow\"></div>\n" +
22612     "                    <div class=\"tooltip-wrapper\" role=\"tooltip\" aria-live=\"polite\" aria-atomic=\"false\" style=\"z-index:1111\">\n" +
22613     "                        <div class=\"tooltip-size-control\">\n" +
22614     "                            <div class=\"helpertext\" tabindex=\"-1\" role=\"tooltip\">\n" +
22615     "                                {{profile.userName}}\n" +
22616     "                            </div>\n" +
22617     "                        </div>\n" +
22618     "                    </div>\n" +
22619     "                </div>\n" +
22620     "            </div>\n" +
22621     "            <div ng-if=\"!shouldClip(profile.userName)\">\n" +
22622     "                {{profile.userName}}\n" +
22623     "            </div>\n" +
22624     "            <label>Email</label>\n" +
22625     "            <div ng-if=\"shouldClip(profile.email)\" ng-mouseover=\"showEmailTooltip = true;\" ng-mouseleave=\"showEmailTooltip = false;\">\n" +
22626     "                <div ng-if=\"shouldClip(profile.email)\" class=\"tooltip\" data-placement=\"bottom\" b2b-tooltip show-tooltip=\"showEmailTooltip\">\n" +
22627     "                    {{profile.email.slice(0, 25)+'...'}}\n" +
22628     "                    <div class=\"arrow\"></div>\n" +
22629     "                    <div class=\"tooltip-wrapper\" role=\"tooltip\" aria-live=\"polite\" aria-atomic=\"false\" style=\"z-index:1111\">\n" +
22630     "                        <div class=\"tooltip-size-control\">\n" +
22631     "                            <div class=\"helpertext\" tabindex=\"-1\" role=\"tooltip\">\n" +
22632     "                                {{profile.email}}\n" +
22633     "                            </div>\n" +
22634     "                        </div>\n" +
22635     "                    </div>\n" +
22636     "                </div>\n" +
22637     "            </div>\n" +
22638     "            <div ng-if=\"!shouldClip(profile.email)\">\n" +
22639     "                {{profile.email}}\n" +
22640     "            </div>\n" +
22641     "            <label>Role</label>\n" +
22642     "            <div ng-if=\"shouldClip(profile.role)\" ng-mouseover=\"showRoleTooltip = true;\" ng-mouseleave=\"showRoleTooltip = false;\">\n" +
22643     "                <div ng-if=\"shouldClip(profile.role)\" class=\"tooltip\" b2b-tooltip show-tooltip=\"showRoleTooltip\">\n" +
22644     "                    {{profile.role.slice(0, 25)+'...'}}\n" +
22645     "                    <div class=\"tooltip-wrapper\" role=\"tooltip\" aria-live=\"polite\" aria-atomic=\"false\" style=\"z-index:1111\">\n" +
22646     "                        <div class=\"tooltip-size-control\">\n" +
22647     "                            <div class=\"helpertext\" tabindex=\"-1\" role=\"tooltip\">\n" +
22648     "                                {{profile.role}}\n" +
22649     "                            </div>\n" +
22650     "                        </div>\n" +
22651     "                    </div>\n" +
22652     "                </div>\n" +
22653     "            </div>\n" +
22654     "            <div ng-if=\"!shouldClip(profile.role)\">\n" +
22655     "                {{profile.role}}\n" +
22656     "            </div>\n" +
22657     "            <label>Last login</label>\n" +
22658     "            <div ng-if=\"shouldClip(profile.lastLogin)\" ng-mouseover=\"showLastLoginTooltip = true;\" ng-mouseleave=\"showLastLoginTooltip = false;\">\n" +
22659     "                <div ng-if=\"shouldClip(profile.lastLogin)\" class=\"tooltip\" b2b-tooltip show-tooltip=\"showLastLoginTooltip\">\n" +
22660     "                    {{profile.lastLogin.slice(0, 25)+'...'}}\n" +
22661     "                    <div class=\"tooltip-wrapper\" role=\"tooltip\" aria-live=\"polite\" aria-atomic=\"false\" style=\"z-index:1111\">\n" +
22662     "                        <div class=\"tooltip-size-control\">\n" +
22663     "                            <div class=\"helpertext\" tabindex=\"-1\" role=\"tooltip\">\n" +
22664     "                                {{profile.lastLogin}}\n" +
22665     "                            </div>\n" +
22666     "                        </div>\n" +
22667     "                    </div>\n" +
22668     "                </div>\n" +
22669     "            </div>\n" +
22670     "            <div ng-if=\"!shouldClip(profile.lastLogin)\">\n" +
22671     "                {{profile.lastLogin}}\n" +
22672     "            </div>\n" +
22673     "         </div>\n" +
22674     "    </div>\n" +
22675     "</div>");
22676 }]);
22677
22678 angular.module("b2bTemplate/searchField/searchField.html", []).run(["$templateCache", function($templateCache) {
22679   $templateCache.put("b2bTemplate/searchField/searchField.html",
22680     "<div class=\"search-bar\">\n" +
22681     "    <div class='input-container' ng-blur=\"blurInput()\">\n" +
22682     "        <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}}\"  aria-live=\"assertive\"/>\n" +
22683     "            <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" +
22684     "    </div>\n" +
22685     "    <div class=\"search-suggestion-wrapper\" ng-if=\"filterList.length >=0\" ng-show=\"showListFlag\">\n" +
22686     "        <ul class=\"search-suggestion-list\" role=\"listbox\">      \n" +
22687     "            <li class=\"no-result\" ng-if=\"filterList.length == 0 && configObj.display\">{{configObj.resultText}}</li>\n" +
22688     "            <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" +
22689     "                {{item.title}}     \n" +
22690     "            </li>\n" +
22691     "        </ul>\n" +
22692     "    </div>\n" +
22693     "</div>");
22694 }]);
22695
22696 angular.module("b2bTemplate/seekBar/seekBar.html", []).run(["$templateCache", function($templateCache) {
22697   $templateCache.put("b2bTemplate/seekBar/seekBar.html",
22698     "<div class=\"b2b-seek-bar-container\" ng-class=\"{vertical:verticalSeekBar}\">\n" +
22699     "    <div class=\"b2b-seek-bar-track-container\">\n" +
22700     "        <div class=\"b2b-seek-bar-track\"></div>\n" +
22701     "        <div class=\"b2b-seek-bar-track-fill\"></div>\n" +
22702     "    </div>\n" +
22703     "    <div class=\"b2b-seek-bar-knob-container\" role=\"menu\"  >\n" +
22704     "        <div class=\"b2b-seek-bar-knob\" tabindex=\"0\" role=\"menuitem\"></div>\n" +
22705     "    </div>\n" +
22706     "</div>");
22707 }]);
22708
22709 angular.module("b2bTemplate/slider/slider.html", []).run(["$templateCache", function($templateCache) {
22710   $templateCache.put("b2bTemplate/slider/slider.html",
22711     "<div class=\"b2b-slider-container\" ng-class=\"{'vertical':verticalSlider}\">\n" +
22712     "    <div class=\"slider-track-container\">\n" +
22713     "        <div class=\"slider-track\"></div>\n" +
22714     "        <div class=\"slider-track-fill\" ng-style=\"{backgroundColor:trackFillColor}\"></div>\n" +
22715     "    </div>\n" +
22716     "    <div class=\"slider-knob-container\" ng-class=\"{'slider-knob-hidden':hideKnob}\">\n" +
22717     "        <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" +
22718     "    </div>\n" +
22719     "</div>");
22720 }]);
22721
22722 angular.module("b2bTemplate/spinButton/spinButton.html", []).run(["$templateCache", function($templateCache) {
22723   $templateCache.put("b2bTemplate/spinButton/spinButton.html",
22724     "<div class=\"btn-group btn-spinbutton-toggle\" ng-class=\"{'disabled': disabledFlag}\">\n" +
22725     "    <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" +
22726     "    <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" +
22727     "    <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" +
22728     "</div>");
22729 }]);
22730
22731 angular.module("b2bTemplate/statusTracker/statusTracker.html", []).run(["$templateCache", function($templateCache) {
22732   $templateCache.put("b2bTemplate/statusTracker/statusTracker.html",
22733     "<div class=\"b2b-status-tracker row\">\n" +
22734     "   <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" +
22735     "       <div class=\"btn btn-small btn-secondary\">\n" +
22736     "           <i class=\"icon-primary-left\"></i>\n" +
22737     "       </div>\n" +
22738     "   </button>\n" +
22739     "   <div ng-repeat=\"status in statuses\" class=\"b2b-status-tracker-step {{ status.state }}\" ng-show=\"isInViewport($index)\">\n" +
22740     "       <p class=\"b2b-status-tracker-heading\">{{status.heading}}</p>\n" +
22741     "       <div class=\"progress\">\n" +
22742     "           <div class=\"progress-bar\">\n" +
22743     "               <span class=\"hidden-spoken\">\n" +
22744     "                   {{ removeCamelCase(status.state) }}\n" +
22745     "               </span>\n" +
22746     "           </div>\n" +
22747     "       </div>\n" +
22748     "       <div class=\"b2b-status-tracker-estimate {{status.state}}\">\n" +
22749     "           <i ng-show=\"status.estimate !== '' && status.estimate !== undefined\" ng-class=\"b2bStatusTrackerConfig.icons[status.state]\"></i>\n" +
22750     "           &nbsp;\n" +
22751     "           <span ng-bind-html=\"status.estimate\"></span>\n" +
22752     "       </div>\n" +
22753     "       \n" +
22754     "       <div class=\"b2b-status-tracker-description\" ng-bind-html=\"status.description\"> \n" +
22755     "       </div>\n" +
22756     "   </div>\n" +
22757     "   <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" +
22758     "       <div class=\"btn btn-small btn-secondary\">\n" +
22759     "           <i class=\"icon-primary-right\"></i>\n" +
22760     "       </div>\n" +
22761     "   </button>\n" +
22762     "</div>");
22763 }]);
22764
22765 angular.module("b2bTemplate/stepTracker/stepTracker.html", []).run(["$templateCache", function($templateCache) {
22766   $templateCache.put("b2bTemplate/stepTracker/stepTracker.html",
22767     "<div class=\"b2b-step-tracker\">\n" +
22768     "    <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" +
22769     "       <div class=\"btn btn-left btn-small btn-secondary\"><i class=\"icon-primary-left\"></i></div>\n" +
22770     "   </button>\n" +
22771     "    <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" +
22772     "       <div class=\"btn btn-small btn-right btn-secondary\"><i class=\"icon-primary-right\"></i></div>\n" +
22773     "   </button>\n" +
22774     "    <ul class=\"b2b-steps\">\n" +
22775     "        <li ng-class=\"{'b2b-step-done':$index < currentIndex - 1 ,'b2b-step-on':$index == currentIndex - 1}\" \n" +
22776     "            ng-repeat=\"stepsItem in stepsItemsObject\" ng-show=\"isInViewport($index)\">\n" +
22777     "           <p class=\"b2b-step-text\" data-sm-text=\"{{stepsItem.dataMobile}}\" data-large-text=\"{{stepsItem.dataDesktop}}\">{{stepsItem.text}}</p>\n" +
22778     "            <span class=\"hidden-spoken\">\n" +
22779     "                {{($index < currentIndex - 1)? 'Complete. '+stepsItem.text+' '+stepsItem.dataMobile:''}} \n" +
22780     "                {{($index == currentIndex - 1)? 'In Progress. '+stepsItem.text+' '+stepsItem.dataMobile:''}} \n" +
22781     "                {{($index > currentIndex - 1)? 'Incomplete. '+stepsItem.text+' '+stepsItem.dataMobile:''}}\n" +
22782     "            </span>\n" +
22783     "        </li>\n" +
22784     "    </ul>\n" +
22785     "</div>");
22786 }]);
22787
22788 angular.module("b2bTemplate/switches/switches-spanish.html", []).run(["$templateCache", function($templateCache) {
22789   $templateCache.put("b2bTemplate/switches/switches-spanish.html",
22790     "<div class=\"switch-overlay\" aria-hidden=\"true\">\n" +
22791     "    <span class=\"btn-slider-on\"><span class=\"hidden-spoken\">seleccione para hacer </span><i class=\"activo\">activo</i></span>\n" +
22792     "    <span class=\"switch-handle\"></span>\n" +
22793     "    <span class=\"btn-slider-off\"><span class=\"hidden-spoken\">seleccione para hacer </span><i class=\"inactivo\">inactivo</i></span>\n" +
22794     "</div>");
22795 }]);
22796
22797 angular.module("b2bTemplate/switches/switches.html", []).run(["$templateCache", function($templateCache) {
22798   $templateCache.put("b2bTemplate/switches/switches.html",
22799     "<div class=\"switch-overlay\" aria-hidden=\"true\">\n" +
22800     "    <span class=\"btn-slider-on\">On</span>\n" +
22801     "    <span class=\"switch-handle\"></span>\n" +
22802     "    <span class=\"btn-slider-off\">Off</span>\n" +
22803     "</div>");
22804 }]);
22805
22806 angular.module("b2bTemplate/tableMessages/tableMessage.html", []).run(["$templateCache", function($templateCache) {
22807   $templateCache.put("b2bTemplate/tableMessages/tableMessage.html",
22808     "<div class=\"b2b-table-message\">\n" +
22809     "    <div class=\"b2b-message\" ng-if=\"msgType === 'noMatchingResults'\">\n" +
22810     "        <div class=\"b2b-magnify-glass\"></div>\n" +
22811     "        <div>\n" +
22812     "            <div ng-transclude></div>\n" +
22813     "        </div>\n" +
22814     "    </div>\n" +
22815     "    <div class=\"b2b-message\" ng-if=\"msgType == 'infoCouldNotLoad'\">\n" +
22816     "        <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" +
22817     "        <div>Oops!</div>\n" +
22818     "        <div>The information could not load at this time.</div>\n" +
22819     "        <div>Please <a href=\"javascript:void(0)\" title=\"Refresh the page\" ng-click=\"refreshAction($event)\">refresh the page</a>\n" +
22820     "        </div>\n" +
22821     "    </div>\n" +
22822     "    <div class=\"b2b-message\" ng-if=\"msgType == 'magnifySearch'\">\n" +
22823     "        <div class=\"b2b-magnify-glass\"></div>\n" +
22824     "        <div>\n" +
22825     "            <p class=\"b2b-message-title\">Please input values to\n" +
22826     "                <br/> begin your search.</p>\n" +
22827     "        </div>\n" +
22828     "    </div>\n" +
22829     "    <div class=\"b2b-message\" ng-if=\"msgType === 'loadingTable'\">\n" +
22830     "        <div class=\"icon-primary-spinner-ddh b2b-loading-dots\"></div>\n" +
22831     "        <div ng-transclude></div>\n" +
22832     "    </div>\n" +
22833     "</div>\n" +
22834     "");
22835 }]);
22836
22837 angular.module("b2bTemplate/tableScrollbar/tableScrollbar.html", []).run(["$templateCache", function($templateCache) {
22838   $templateCache.put("b2bTemplate/tableScrollbar/tableScrollbar.html",
22839     "<div class=\"b2b-table-scrollbar\">\n" +
22840     "    <div class=\"b2b-scrollbar-arrows\">\n" +
22841     "        <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" +
22842     "        <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" +
22843     "    </div>\n" +
22844     "    <div class=\"b2b-table-inner-container\">\n" +
22845     "        <span ng-transclude></span>\n" +
22846     "    </div>\n" +
22847     "</div>");
22848 }]);
22849
22850 angular.module("b2bTemplate/tables/b2bTable.html", []).run(["$templateCache", function($templateCache) {
22851   $templateCache.put("b2bTemplate/tables/b2bTable.html",
22852     "<table ng-class=\"{'striped': responsive, 'complex-table': responsive && active}\" ng-transclude></table>");
22853 }]);
22854
22855 angular.module("b2bTemplate/tables/b2bTableBody.html", []).run(["$templateCache", function($templateCache) {
22856   $templateCache.put("b2bTemplate/tables/b2bTableBody.html",
22857     "<td ng-hide=\"isHidden()\" ng-transclude></td>");
22858 }]);
22859
22860 angular.module("b2bTemplate/tables/b2bTableHeaderSortable.html", []).run(["$templateCache", function($templateCache) {
22861   $templateCache.put("b2bTemplate/tables/b2bTableHeaderSortable.html",
22862     "<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" +
22863     "    <span ng-transclude></span>\n" +
22864     "    <i ng-class=\"{'icoArrows-sort-arrow active': sortPattern === 'ascending', 'icoArrows-sort-arrow active down': sortPattern === 'descending'}\"></i>\n" +
22865     "</th>");
22866 }]);
22867
22868 angular.module("b2bTemplate/tables/b2bTableHeaderUnsortable.html", []).run(["$templateCache", function($templateCache) {
22869   $templateCache.put("b2bTemplate/tables/b2bTableHeaderUnsortable.html",
22870     "<th scope=\"col\" ng-hide=\"isHidden()\" ng-transclude></th>");
22871 }]);
22872
22873 angular.module("b2bTemplate/tabs/b2bTab.html", []).run(["$templateCache", function($templateCache) {
22874   $templateCache.put("b2bTemplate/tabs/b2bTab.html",
22875     "<li role=\"tab\" aria-selected=\"{{isTabActive()}}\" aria-controls=\"{{tabPanelId}}\" class=\"tab\" \n" +
22876     "    ng-class=\"{'active': isTabActive()}\" ng-click=\"clickTab()\" ng-hide=\"tabItem.disabled\">\n" +
22877     "    <a href=\"javascript:void(0)\"  tabindex=\"{{(isTabActive() && tabItem.disabled)?-1:0}}\"\n" +
22878     "       ng-disabled=\"tabItem.disabled\" aria-expanded=\"{{(isTabActive() && !tabItem.disabled)}}\" \n" +
22879     "       b2b-accessibility-click=\"13,32\" ng-transclude></a>\n" +
22880     "    <span class=\"hidden-spoken\" ng-if=\"isTabActive()\">Active tab</span>\n" +
22881     "</li>");
22882 }]);
22883
22884 angular.module("b2bTemplate/tabs/b2bTabset.html", []).run(["$templateCache", function($templateCache) {
22885   $templateCache.put("b2bTemplate/tabs/b2bTabset.html",
22886     "<ul class=\"tabs promo-tabs\" role=\"tablist\" ng-transclude></ul>");
22887 }]);
22888
22889 angular.module("b2bTemplate/treeNav/groupedTree.html", []).run(["$templateCache", function($templateCache) {
22890   $templateCache.put("b2bTemplate/treeNav/groupedTree.html",
22891     "<ul role=\"group\">\n" +
22892     "    <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" +
22893     "        <ul role=\"group\">\n" +
22894     "            <b2b-member ng-repeat='member in value.childArray' member='member' time-delay='{{timeDelay}}' group-it='groupIt'></b2b-member>\n" +
22895     "        </ul>\n" +
22896     "    </li>\n" +
22897     "</ul>");
22898 }]);
22899
22900 angular.module("b2bTemplate/treeNav/treeMember.html", []).run(["$templateCache", function($templateCache) {
22901   $templateCache.put("b2bTemplate/treeNav/treeMember.html",
22902     "<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" +
22903     "    <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" +
22904     "        <span class=\"{{!member.child?'end':''}} b2b-tree-node-icon\">\n" +
22905     "            <i class=\"b2b-tree-expandCollapse-icon\" ng-class=\"{'icon-primary-expanded':member.active}\"></i>\n" +
22906     "        </span>\n" +
22907     "         <div id=\"description_{{$id}}\" class=\"offscreen-text\">\n" +
22908     "           {{member.descriptionText}}\n" +
22909     "        </div>\n" +
22910     "        <div class=\"b2b-tree-tooltip\" ng-if=\"member.showTooltip\">\n" +
22911     "           <span class=\"b2b-tree-arrow-left\"></span>\n" +
22912     "           <div class=\"b2b-tree-tooltip-content\">\n" +
22913     "               {{member.tooltipContent}}\n" +
22914     "           </div>  \n" +
22915     "        </div>\n" +
22916     "    </a>\n" +
22917     "</li>");
22918 }]);
22919
22920 angular.module("b2bTemplate/treeNav/ungroupedTree.html", []).run(["$templateCache", function($templateCache) {
22921   $templateCache.put("b2bTemplate/treeNav/ungroupedTree.html",
22922     "<ul role=\"{{setRole}}\"><b2b-member ng-repeat='member in collection' member='member' group-it='groupIt'></b2b-member></ul>");
22923 }]);
22924
22925 angular.module("b2bTemplate/treeNodeCheckbox/groupedTree.html", []).run(["$templateCache", function($templateCache) {
22926   $templateCache.put("b2bTemplate/treeNodeCheckbox/groupedTree.html",
22927     "<ul role=\"group\">\n" +
22928     "    <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" +
22929     "        <span class=\"ng-hide\">\n" +
22930     "            <label class=\"checkbox\">\n" +
22931     "                <input type=\"checkbox\" tabindex=\"-1\" class=\"treeCheckBox grpTreeCheckbox\" style=\"margin-top:2px;\"/><i class=\"skin\"></i><span> {{(key)?key:''}}</span>\n" +
22932     "            </label>\n" +
22933     "        </span>\n" +
22934     "        <span>\n" +
22935     "            {{(key)?key:''}}    \n" +
22936     "        </span>\n" +
22937     "        <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" +
22938     "        <ul role=\"group\">\n" +
22939     "            <b2b-tree-member ng-repeat='member in value.childArray' member='member' group-it='groupIt'></b2b-tree-member>\n" +
22940     "        </ul>\n" +
22941     "    </li>\n" +
22942     "</ul>");
22943 }]);
22944
22945 angular.module("b2bTemplate/treeNodeCheckbox/treeMember.html", []).run(["$templateCache", function($templateCache) {
22946   $templateCache.put("b2bTemplate/treeNodeCheckbox/treeMember.html",
22947     "<li role=\"treeitem\" aria-expanded=\"{{(member.active?true:false)}}\" aria-label=\"{{member.name}}\" ng-class=\"{'bg':member.selected}\"  b2b-tree-node-link>\n" +
22948     "    <a tabindex=\"-1\" title=\"{{member.name}}\" href=\"javascript:void(0)\" ng-class=\"{'active':member.active}\">\n" +
22949     "       <span ng-show=\"member.displayCheckbox\">\n" +
22950     "               <label class=\"checkbox\">\n" +
22951     "                <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" +
22952     "            </label>\n" +
22953     "        </span>\n" +
22954     "       <span ng-show=\"!member.displayCheckbox\">\n" +
22955     "           {{member.name}} \n" +
22956     "       </span>\n" +
22957     "        <span class=\"nodeIcon {{!member.child?'end':''}}\">\n" +
22958     "            <i class=\"expandCollapseIcon\" ng-class=\"{'icon-primary-expanded':member.active}\"></i>\n" +
22959     "        </span>\n" +
22960     "    </a>\n" +
22961     "</li>");
22962 }]);
22963
22964 angular.module("b2bTemplate/treeNodeCheckbox/ungroupedTree.html", []).run(["$templateCache", function($templateCache) {
22965   $templateCache.put("b2bTemplate/treeNodeCheckbox/ungroupedTree.html",
22966     "<ul role=\"{{setRole}}\"><b2b-tree-member ng-repeat='member in collection' member='member' group-it='groupIt'></b2b-tree-member></ul>");
22967 }]);