1 /*! b2b-angular-library - v1.0.5 - Last updated: 2017-05-24. Copyright (c) 2016 AT&T Services, Inc. */
2 angular.module("b2b.att.tpls", ['b2bTemplate/audioPlayer/audioPlayer.html', 'b2bTemplate/audioRecorder/audioRecorder.html', 'b2bTemplate/backToTop/backToTop.html', 'b2bTemplate/boardstrip/b2bAddBoard.html', 'b2bTemplate/boardstrip/b2bBoard.html', 'b2bTemplate/boardstrip/b2bBoardstrip.html', 'b2bTemplate/calendar/datepicker-popup.html', 'b2bTemplate/calendar/datepicker.html', 'b2bTemplate/coachmark/coachmark.html', 'b2bTemplate/dropdowns/b2bDropdownDesktop.html', 'b2bTemplate/dropdowns/b2bDropdownGroupDesktop.html', 'b2bTemplate/dropdowns/b2bDropdownListDesktop.html', 'b2bTemplate/fileUpload/fileUpload.html', 'b2bTemplate/flyout/flyout.html', 'b2bTemplate/flyout/flyoutContent.html', 'b2bTemplate/footer/footer_column_switch_tpl.html', 'b2bTemplate/horizontalTable/horizontalTable.html', 'b2bTemplate/hourPicker/b2bHourpicker.html', 'b2bTemplate/hourPicker/b2bHourpickerPanel.html', 'b2bTemplate/hourPicker/b2bHourpickerValue.html', 'b2bTemplate/leftNavigation/leftNavigation.html', 'b2bTemplate/listbox/listbox.html', 'b2bTemplate/modalsAndAlerts/b2b-backdrop.html', 'b2bTemplate/modalsAndAlerts/b2b-window.html', 'b2bTemplate/monthSelector/monthSelector-popup.html', 'b2bTemplate/monthSelector/monthSelector.html', 'b2bTemplate/monthSelector/monthSelectorLink.html', 'b2bTemplate/pagination/b2b-pagination.html', 'b2bTemplate/paneSelector/paneSelector.html', 'b2bTemplate/paneSelector/paneSelectorPane.html', 'b2bTemplate/profileCard/profileCard-addUser.html', 'b2bTemplate/profileCard/profileCard.html', 'b2bTemplate/searchField/searchField.html', 'b2bTemplate/seekBar/seekBar.html', 'b2bTemplate/slider/slider.html', 'b2bTemplate/spinButton/spinButton.html', 'b2bTemplate/statusTracker/statusTracker.html', 'b2bTemplate/stepTracker/stepTracker.html', 'b2bTemplate/switches/switches-spanish.html', 'b2bTemplate/switches/switches.html', 'b2bTemplate/tableMessages/tableMessage.html', 'b2bTemplate/tables/b2bTable.html', 'b2bTemplate/tables/b2bTableBody.html', 'b2bTemplate/tables/b2bTableHeaderSortable.html', 'b2bTemplate/tables/b2bTableHeaderUnsortable.html', 'b2bTemplate/tableScrollbar/tableScrollbar.html', 'b2bTemplate/tabs/b2bTab.html', 'b2bTemplate/tabs/b2bTabset.html', 'b2bTemplate/treeNav/groupedTree.html', 'b2bTemplate/treeNav/treeMember.html', 'b2bTemplate/treeNav/ungroupedTree.html', 'b2bTemplate/treeNodeCheckbox/groupedTree.html', 'b2bTemplate/treeNodeCheckbox/treeMember.html', 'b2bTemplate/treeNodeCheckbox/ungroupedTree.html']);angular.module("b2b.att", ["b2b.att.tpls", 'b2b.att.addressInputTemplate','b2b.att.arrows','b2b.att.audioPlayer','b2b.att.audioRecorder','b2b.att.backToTop','b2b.att.badgesForAlerts','b2b.att.boardstrip','b2b.att.bootstrapGridTemplate','b2b.att.breadcrumbs','b2b.att.buttonGroups','b2b.att.buttons','b2b.att.calendar','b2b.att.checkboxes','b2b.att.coachmark','b2b.att.configurationSection','b2b.att.directoryListingTemplate','b2b.att.dropdowns','b2b.att.fileUpload','b2b.att.filters','b2b.att.flyout','b2b.att.footer','b2b.att.header','b2b.att.headingsAndCopy','b2b.att.horizontalTable','b2b.att.hourPicker','b2b.att.inputTemplate','b2b.att.leftNavigation','b2b.att.links','b2b.att.listbox','b2b.att.loaderAnimation','b2b.att.messageWrapper','b2b.att.modalsAndAlerts','b2b.att.monthSelector','b2b.att.multiLevelNavigation','b2b.att.multipurposeExpander','b2b.att.notesMessagesAndErrors','b2b.att.notificationCardTemplate','b2b.att.orderConfirmationTemplate','b2b.att.pagination','b2b.att.paneSelector','b2b.att.phoneNumberInput','b2b.att.profileBlockTemplate','b2b.att.profileCard','b2b.att.radios','b2b.att.searchField','b2b.att.seekBar','b2b.att.separators','b2b.att.slider','b2b.att.spinButton','b2b.att.staticRouteTemplate','b2b.att.statusTracker','b2b.att.stepTracker','b2b.att.switches','b2b.att.tableDragAndDrop','b2b.att.tableMessages','b2b.att.tables','b2b.att.tableScrollbar','b2b.att.tabs','b2b.att.tagBadges','b2b.att.textArea','b2b.att.timeInputField','b2b.att.tooltipsForForms','b2b.att.treeNav','b2b.att.treeNodeCheckbox','b2b.att.utilities']);/**
4 * @name Template.att:Address Input
7 * <file src="src/addressInputTemplate/docs/readme.md" />
14 <example module="b2b.att">
15 <file src="src/addressInputTemplate/docs/demo.html" />
16 <file src="src/addressInputTemplate/docs/demo.js" />
21 angular.module('b2b.att.addressInputTemplate', ['ngMessages']);
24 * @name Buttons, links & UI controls.att:arrows
27 * <file src="src/arrows/docs/readme.md" />
30 * Please refer demo.html tab in Example section below.
34 <example module="b2b.att">
35 <file src="src/arrows/docs/demo.html" />
36 <file src="src/arrows/docs/demo.js" />
41 angular.module('b2b.att.arrows', []);
44 * @name Videos, audio & animation.att:Audio Player
46 * @param {string} audioSrcUrl - MP3 audio source URL or Blob URL
48 * <file src="src/audioPlayer/docs/readme.md" />
52 <div b2b-audio audio-src-url='audioSrcUrl'></div>
56 <example module="b2b.att">
57 <file src="src/audioPlayer/docs/demo.html" />
58 <file src="src/audioPlayer/docs/demo.js" />
64 angular.module('b2b.att.audioPlayer', ['b2b.att.utilities', 'b2b.att.seekBar'])
65 .constant('AudioPlayerConfig', {
67 'timeShiftInSeconds': 5
69 .filter('trustedAudioUrl', ['$sce', function ($sce) {
70 return function (audioFileFullPath) {
71 return audioFileFullPath ? $sce.trustAsResourceUrl(audioFileFullPath) : 'undefined';
74 .directive('b2bAudio', ['$log', '$timeout', 'AudioPlayerConfig', '$compile', 'events', function ($log, $timeout, AudioPlayerConfig, $compile, events) {
81 templateUrl: 'b2bTemplate/audioPlayer/audioPlayer.html',
82 controller: function ($scope) {
86 if (!angular.isDefined($scope.audioSrcUrl)) {
87 $log.warn('b2b-audio : audio-src-url undefined');
88 $scope.audioSrcUrl = undefined;
89 $scope.audio.mp3 = undefined;
93 link: function (scope, element) {
94 var audioElement = angular.element(element[0].querySelector('audio'))[0];
95 scope.audio.audioElement = audioElement;
97 function setAttributes(element, attributes) {
98 Object.keys(attributes).forEach(function (name) {
99 element.setAttribute(name, attributes[name]);
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'
115 setAttributes(seekBarKnob, tooltipObject);
116 $compile(seekBarKnob)(scope);
119 if (angular.isDefined(scope.audioSrcUrl)) {
120 scope.audio.mp3 = scope.audioSrcUrl;
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;
130 $timeout(function () {
132 audioElement.volume = scope.audio.currentVolume / 100;
135 scope.$watch('audioSrcUrl', function (newVal, oldVal) {
136 if (newVal !== oldVal) {
138 $log.warn('b2b-audio : audio-src-url undefined. Please provide a valid URL');
141 scope.audio.mp3 = newVal;
142 $timeout(function () {
148 scope.playAudio = function () {
154 audioElement.onplay = function () {
155 scope.isPlayInProgress = true;
159 scope.pauseAudio = function () {
160 audioElement.pause();
163 audioElement.onpause = function () {
164 scope.isPlayInProgress = false;
168 scope.toggleAudio = function () {
169 if (audioElement.paused) {
176 scope.volumeUp = function (delta) {
182 audioElement.muted = false;
183 if (audioElement.volume < 1) {
184 audioElement.volume = Math.min((Math.round((audioElement.volume + delta) * 100) / 100), 1);
186 scope.audio.currentVolume = audioElement.volume * 100;
187 return audioElement.volume;
190 scope.volumeDown = function (delta) {
196 audioElement.muted = false;
197 if (audioElement.volume > 0) {
198 audioElement.volume = Math.max((Math.round((audioElement.volume - delta) * 100) / 100), 0);
200 scope.audio.currentVolume = audioElement.volume * 100;
201 return audioElement.volume;
204 var volumeHandler = function (e) {
205 events.preventDefault(e);
206 if ((e.wheelDelta && e.wheelDelta > 0) || (e.detail && e.detail < 0)) {
216 scope.$watch('audio.currentVolume', function (newVal, oldVal) {
217 if (newVal !== oldVal) {
218 audioElement.volume = newVal / 100;
222 scope.setCurrentTime = function (timeInSec) {
223 audioElement.currentTime = timeInSec;
226 scope.setAudioPosition = function (val) {
228 scope.setCurrentTime(val);
229 scope.isAudioDragging = false;
233 function getTimestampArray(timestamp) {
234 var d = Math.abs(timestamp) / 1000; // delta
235 var r = {}; // result
236 var s = { // structure
243 Object.keys(s).forEach(function (key) {
244 r[key] = Math.floor(d / s[key]);
245 d -= r[key] * s[key];
251 scope.timeFormatter = function (timeInSec) {
252 var formattedTime = '00:00';
254 if (!timeInSec || timeInSec < 1) {
255 return formattedTime;
258 if (typeof timeInSec === 'string') {
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];
271 formattedTime = dateArray['minute'] + ':' + dateArray['second'];
273 if (dateArray['hour'] !== '00') {
274 formattedTime = dateArray['hour'] + ':' + formattedTime;
277 if (dateArray['day'] !== '00') {
278 formattedTime = dateArray['day'] + ':' + formattedTime;
281 return formattedTime;
284 audioElement.onloadedmetadata = function () {
285 scope.audio.duration = audioElement.duration;
289 audioElement.ontimeupdate = function () {
290 if (!scope.isAudioDragging) {
291 scope.audio.currentTime = audioElement.currentTime;
296 audioElement.onended = function () {
297 scope.setCurrentTime(0);
298 scope.audio.currentTime = 0;
299 if (!audioElement.paused) {
305 audioElement.oncanplay = function () {
306 scope.isReady = true;
307 scope.isPlayInProgress = !audioElement.paused;
311 var onloadstart = function () {
312 scope.isReady = false;
313 scope.isPlayInProgress = !audioElement.paused;
314 scope.audio.currentTime = 0;
315 scope.audio.duration = 0;
318 audioElement.addEventListener("loadstart", onloadstart);
324 * @name Videos, audio & animation.att:Audio Recorder
326 * @param {function} callback - A callback to handle the WAV blob
327 * @param {object} config - A config object with properties startRecordingMessage & whileRecordingMessage
329 * <file src="src/audioRecorder/docs/readme.md" />
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" />
341 angular.module('b2b.att.audioRecorder', ['b2b.att.utilities'])
342 .constant('AudioRecorderConfig', {
343 'startRecordingMessage': 'Click on REC icon to being recording',
344 'whileRecordingMessage': 'Recording...'
346 .directive('b2bAudioRecorder', ['$interval', 'AudioRecorderConfig', 'b2bUserAgent', 'b2bRecorder', function($interval, AudioRecorderConfig, b2bUserAgent, b2bRecorder) {
353 templateUrl: 'b2bTemplate/audioRecorder/audioRecorder.html',
354 controller: function($scope) {
356 function hasGetUserMedia() {
357 return !!(navigator.getUserMedia || navigator.webkitGetUserMedia ||
358 navigator.mozGetUserMedia || navigator.msGetUserMedia);
361 if (!hasGetUserMedia()) {
362 throw new Error('Your broswer does not support MediaRecorder API');
365 if (!(b2bUserAgent.isFF() || b2bUserAgent.isChrome())) {
366 throw new Error('b2bAudioRecorder does not support this browser!');
370 link: function(scope, element) {
371 scope.elapsedTime = 0;
372 scope.isRecording = false;
374 scope.config.startRecordingMessage = AudioRecorderConfig.startRecordingMessage;
375 scope.config.whileRecordingMessage = AudioRecorderConfig.whileRecordingMessage;
378 var timer = undefined; // Interval promise
379 navigator.getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.msGetUserMedia;
381 var audio = angular.element(element[0].querySelector('audio'))[0];
382 var recorder = undefined;
384 function startRecording() {
385 scope.isRecording = true;
386 navigator.mediaDevices.getUserMedia({
388 }).then(function(stream) {
389 //create the MediaStreamAudioSourceNode
390 context = new AudioContext();
391 source = context.createMediaStreamSource(stream);
392 recorder = new b2bRecorder(source);
395 timer = $interval(function() {
396 scope.elapsedTime += 1;
398 }).catch(function(err) {
404 function stopRecording() {
405 scope.isRecording = false;
408 recorder.exportWAV(function(s) {
409 audio.src = window.URL.createObjectURL(s);
410 context.close().then(function() {
412 $interval.cancel(timer);
414 scope.elapsedTime = 0;
417 recorder = undefined;
419 if (angular.isFunction(scope.callback)){
420 scope.callback({'data': audio});
427 scope.toggleRecording = function() {
428 if (scope.isRecording) {
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
448 Object.keys(s).forEach(function(key) {
449 r[key] = Math.floor(d / s[key]);
450 d -= r[key] * s[key];
455 scope.timeFormatter = function(timeInSec) {
456 var formattedTime = '00:00';
458 if (!timeInSec || timeInSec < 1) {
459 return formattedTime;
462 if (typeof timeInSec === 'string') {
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];
475 formattedTime = dateArray['minute'] + ':' + dateArray['second'];
477 if (dateArray['hour'] !== '00') {
478 formattedTime = dateArray['hour'] + ':' + formattedTime;
481 if (dateArray['day'] !== '00') {
482 formattedTime = dateArray['day'] + ':' + formattedTime;
485 return formattedTime;
488 scope.$on('$destroy', function() {
490 $interval.cancel(timer);
499 * @name Navigation.att:Back To Top
502 * <file src="src/backToTop/docs/readme.md" />
503 * @param {integer} scrollSpeed - Scroll speed in seconds, default is 1
507 <div ng-controller="backToTopController">
508 <div b2b-backtotop></div>
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" />
521 angular.module('b2b.att.backToTop', ['b2b.att.utilities','b2b.att.position'])
522 .directive('b2bBacktotopButton', [function () {
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}});
537 * @name Messages, modals & alerts.att:badgesForAlerts
540 * <file src="src/badgesForAlerts/docs/readme.md" />
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" />
550 angular.module('b2b.att.badgesForAlerts', []);
553 * @name Misc.att:boardstrip
556 * <file src="src/boardstrip/docs/readme.md" />
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" />
569 angular.module('b2b.att.boardstrip', ['b2b.att.utilities'])
570 .constant('BoardStripConfig', {
571 'maxVisibleBoards': 4,
573 /* These parameters are non-configurable and remain unaltered, until there is a change in corresponding CSS */
577 .directive('b2bBoard', [function () {
582 require: '^b2bBoardStrip',
587 templateUrl: 'b2bTemplate/boardstrip/b2bBoard.html',
588 link: function (scope, element, attrs, ctrls) {
590 var parentCtrl = ctrls;
592 scope.getCurrentIndex = function () {
593 return parentCtrl.getCurrentIndex();
595 scope.selectBoard = function (boardIndex) {
596 if (!isNaN(boardIndex)) {
597 parentCtrl.setCurrentIndex(boardIndex);
603 .directive('b2bBoardStrip', ['BoardStripConfig', '$timeout', function (BoardStripConfig, $timeout) {
608 require: ['?ngModel', 'b2bBoardStrip'],
610 boardsMasterArray: '=',
613 templateUrl: 'b2bTemplate/boardstrip/b2bBoardstrip.html',
614 controller: function ($scope) {
615 if (!angular.isDefined($scope.boardsMasterArray)) {
616 $scope.boardsMasterArray = [];
619 this.rectifyMaxVisibleBoards = function () {
620 if (this.maxVisibleIndex >= $scope.boardsMasterArray.length) {
621 this.maxVisibleIndex = $scope.boardsMasterArray.length - 1;
624 if (this.maxVisibleIndex < 0) {
625 this.maxVisibleIndex = 0;
629 this.resetBoardStrip = function () {
630 $scope.currentIndex = 0;
632 this.maxVisibleIndex = BoardStripConfig.maxVisibleBoards - 1;
633 this.minVisibleIndex = 0;
635 this.rectifyMaxVisibleBoards();
638 this.getCurrentIndex = function () {
639 return $scope.currentIndex;
641 this.setCurrentIndex = function (indx) {
642 $scope.currentIndex = indx;
645 this.getBoardsMasterArrayLength = function () {
646 return $scope.boardsMasterArray.length;
649 $scope.addBoardPressedFlag = false;
650 this.getAddBoardPressedFlag = function () {
651 return $scope.addBoardPressedFlag;
653 this.setAddBoardPressedFlag = function (booleanValue) {
654 $scope.addBoardPressedFlag = booleanValue;
658 link: function (scope, element, attrs, ctrls) {
660 var ngModelCtrl = ctrls[0];
664 var animationTimeout = 1000;
666 var getBoardViewportWidth = function (numberOfVisibleBoards) {
667 return numberOfVisibleBoards * (BoardStripConfig.boardLength + BoardStripConfig.boardMargin);
669 if (element[0].querySelector(".board-viewport")) {
670 angular.element(element[0].querySelector(".board-viewport")).css({
671 "width": getBoardViewportWidth(BoardStripConfig.maxVisibleBoards) + "px"
675 var getBoardstripContainerWidth = function (totalNumberOfBoards) {
676 return totalNumberOfBoards * (BoardStripConfig.boardLength + BoardStripConfig.boardMargin);
678 if (element[0].querySelector(".boardstrip-container")) {
679 angular.element(element[0].querySelector(".boardstrip-container")).css({
680 "width": getBoardstripContainerWidth(ctrl.getBoardsMasterArrayLength()) + "px"
682 angular.element(element[0].querySelector(".boardstrip-container")).css({
687 var calculateAndGetBoardstripContainerAdjustment = function () {
689 var calculatedAdjustmentValue;
691 if (ctrl.getBoardsMasterArrayLength() <= BoardStripConfig.maxVisibleBoards) {
692 calculatedAdjustmentValue = 0;
694 calculatedAdjustmentValue = (ctrl.minVisibleIndex * (BoardStripConfig.boardLength + BoardStripConfig.boardMargin)) * -1;
697 return calculatedAdjustmentValue;
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"
708 $timeout.cancel(oldTimeout);
709 oldTimeout = $timeout(function () {
710 elementToFocusAfterAnimation.focus();
711 }, animationTimeout);
713 elementToFocusAfterAnimation.focus();
717 var updateBoardsTabIndex = function (boardArray, minViewIndex, maxViewIndex) {
718 for (var i = 0; i < boardArray.length; i++) {
719 angular.element(boardArray[i]).attr('tabindex', '-1');
721 for (var j = minViewIndex; j <= maxViewIndex; j++) {
722 angular.element(boardArray[j]).attr('tabindex', '0');
726 $timeout(function () {
727 var currentBoardArray = element[0].querySelectorAll('[b2b-board]');
728 updateBoardsTabIndex(currentBoardArray, ctrl.minVisibleIndex, ctrl.maxVisibleIndex);
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 () {
738 var currentBoardArray = element[0].querySelectorAll('[b2b-board]');
739 if (currentBoardArray.length !== 0) {
740 animateBoardstripContainerAdjustment(currentBoardArray[0]);
742 element[0].querySelector('div.boardstrip-item--add').focus();
745 angular.element(element[0].querySelector(".boardstrip-container")).css({
746 "width": getBoardstripContainerWidth(ctrl.getBoardsMasterArrayLength()) + "px"
748 /* Update tabindecies to ensure keyboard navigation behaves correctly */
749 updateBoardsTabIndex(currentBoardArray, ctrl.minVisibleIndex, ctrl.maxVisibleIndex);
752 /* When a board is added */
754 if (ctrl.getAddBoardPressedFlag()) {
755 ctrl.maxVisibleIndex = ctrl.getBoardsMasterArrayLength() - 1;
756 ctrl.minVisibleIndex = Math.max(ctrl.maxVisibleIndex - BoardStripConfig.maxVisibleBoards + 1, 0);
758 ctrl.setCurrentIndex(ctrl.maxVisibleIndex);
760 $timeout(function () {
761 angular.element(element[0].querySelector(".boardstrip-container")).css({
762 "width": getBoardstripContainerWidth(ctrl.getBoardsMasterArrayLength()) + "px"
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);
771 if (ctrl.minVisibleIndex === 0 && ctrl.getBoardsMasterArrayLength() < BoardStripConfig.maxVisibleBoards + 1) {
772 ctrl.maxVisibleIndex = ctrl.getBoardsMasterArrayLength() - 1;
773 ctrl.rectifyMaxVisibleBoards();
776 $timeout(function () {
777 angular.element(element[0].querySelector(".boardstrip-container")).css({
778 "width": getBoardstripContainerWidth(ctrl.getBoardsMasterArrayLength()) + "px"
781 var currentBoardArray = element[0].querySelectorAll('[b2b-board]');
782 /* Update tabindecies to ensure keyboard navigation behaves correctly */
783 updateBoardsTabIndex(currentBoardArray, ctrl.minVisibleIndex, ctrl.maxVisibleIndex);
787 ctrl.setAddBoardPressedFlag(false);
792 scope.nextBoard = function () {
793 ctrl.maxVisibleIndex += BoardStripConfig.boardsToScroll;
794 ctrl.rectifyMaxVisibleBoards();
795 ctrl.minVisibleIndex = ctrl.maxVisibleIndex - (BoardStripConfig.maxVisibleBoards - 1);
797 $timeout.cancel(oldTimeout);
798 angular.element(element[0].querySelector(".boardstrip-container")).css({
799 "left": calculateAndGetBoardstripContainerAdjustment() + "px"
802 $timeout(function () {
803 var currentBoardArray = element[0].querySelectorAll('[b2b-board]');
805 /* Remove tabindex from non-visible boards */
806 updateBoardsTabIndex(currentBoardArray, ctrl.minVisibleIndex, ctrl.maxVisibleIndex);
808 if (!(scope.isNextBoard())) {
810 currentBoardArray[currentBoardArray.length - 1].focus();
811 } catch (e) { /* IE8 may throw exception */ }
813 }, animationTimeout);
815 scope.prevBoard = function () {
817 ctrl.minVisibleIndex -= BoardStripConfig.boardsToScroll;
818 if (ctrl.minVisibleIndex < 0) {
819 ctrl.minVisibleIndex = 0;
822 ctrl.maxVisibleIndex = ctrl.minVisibleIndex + BoardStripConfig.maxVisibleBoards - 1;
823 ctrl.rectifyMaxVisibleBoards();
825 $timeout.cancel(oldTimeout);
826 angular.element(element[0].querySelector(".boardstrip-container")).css({
827 "left": calculateAndGetBoardstripContainerAdjustment() + "px"
830 $timeout(function () {
831 var currentBoardArray = element[0].querySelectorAll('[b2b-board]');
833 /* Remove tabindex from non-visible boards */
834 updateBoardsTabIndex(currentBoardArray, ctrl.minVisibleIndex, ctrl.maxVisibleIndex);
836 if (ctrl.minVisibleIndex === 0) {
838 element[0].querySelector('div.boardstrip-item--add').focus();
839 } catch (e) { /* IE8 may throw exception */ }
844 scope.isPrevBoard = function () {
845 return (ctrl.minVisibleIndex > 0);
847 scope.isNextBoard = function () {
848 return (ctrl.getBoardsMasterArrayLength() - 1 > ctrl.maxVisibleIndex);
851 ngModelCtrl.$render = function () {
852 if (ngModelCtrl.$viewValue || ngModelCtrl.$viewValue === 0) {
853 var newCurrentIndex = ngModelCtrl.$viewValue;
855 if (!(newCurrentIndex = parseInt(newCurrentIndex, 10))) {
859 if (newCurrentIndex <= 0) {
860 ctrl.resetBoardStrip();
863 var currentBoardArray = element[0].querySelectorAll('[b2b-board]');
864 if (currentBoardArray.length !== 0) {
865 animateBoardstripContainerAdjustment(currentBoardArray[0]);
867 element[0].querySelector('div.boardstrip-item--add').focus();
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);
876 newCurrentIndex = ctrl.maxVisibleIndex;
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);
886 if (!(newCurrentIndex >= ctrl.minVisibleIndex && newCurrentIndex <= ctrl.maxVisibleIndex)) {
887 ctrl.minVisibleIndex = newCurrentIndex;
888 ctrl.maxVisibleIndex = ctrl.minVisibleIndex + BoardStripConfig.maxVisibleBoards - 1;
889 ctrl.rectifyMaxVisibleBoards();
891 if (ctrl.getBoardsMasterArrayLength() < BoardStripConfig.maxVisibleBoards) {
892 ctrl.minVisibleIndex = 0;
894 ctrl.minVisibleIndex = Math.max(ctrl.maxVisibleIndex - BoardStripConfig.maxVisibleBoards + 1, 0);
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);
905 scope.currentIndex = newCurrentIndex;
906 ngModelCtrl.$setViewValue(newCurrentIndex);
908 ctrl.resetBoardStrip();
909 ngModelCtrl.$setViewValue(0);
913 scope.$watch('currentIndex', function (newVal, oldVal) {
914 if (newVal !== oldVal && ngModelCtrl && ngModelCtrl.$viewValue !== newVal) {
915 ngModelCtrl.$setViewValue(newVal);
921 .directive('b2bAddBoard', ['BoardStripConfig', '$parse', function (BoardStripConfig, $parse) {
925 require: '^b2bBoardStrip',
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);
935 ctrl.setAddBoardPressedFlag(true);
941 .directive('b2bBoardNavigation', ['keymap', 'events', function (keymap, events) {
944 link: function (scope, elem) {
946 var prevElem = keymap.KEY.LEFT;
947 var nextElem = keymap.KEY.RIGHT;
949 elem.bind('keydown', function (ev) {
952 ev.keyCode = ev.which;
955 switch (ev.keyCode) {
957 events.preventDefault(ev);
958 events.stopPropagation(ev);
960 if (elem[0].nextElementSibling && parseInt(angular.element(elem[0].nextElementSibling).attr('tabindex')) >= 0) {
961 angular.element(elem[0])[0].nextElementSibling.focus();
964 var el = angular.element(elem[0])[0];
966 if (el.nextSibling) {
971 } while (el && el.tagName !== 'LI');
973 if (el.tagName && el.tagName === 'LI' && parseInt(angular.element(el).attr('tabindex')) >= 0) {
980 events.preventDefault(ev);
981 events.stopPropagation(ev);
983 if (elem[0].previousElementSibling && parseInt(angular.element(elem[0].previousElementSibling).attr('tabindex')) >= 0) {
984 angular.element(elem[0])[0].previousElementSibling.focus();
987 var el1 = angular.element(elem[0])[0];
989 if (el1.previousSibling) {
990 el1 = el1.previousSibling;
994 } while (el1 && el1.tagName !== 'LI');
996 if (el1.tagName && el1.tagName === 'LI' && parseInt(angular.element(el1).attr('tabindex')) >= 0) {
1010 * @name Template.att:Bootstrap Grid Template
1013 * <file src="src/bootstrapGridTemplate/docs/readme.md" />
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" />
1024 angular.module('b2b.att.bootstrapGridTemplate', [])
1028 * @name Navigation.att:breadcrumbs
1031 * <file src="src/breadcrumbs/docs/readme.md" />
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>
1037 <example module="b2b.att">
1038 <file src="src/breadcrumbs/docs/demo.html" />
1039 <file src="src/breadcrumbs/docs/demo.js" />
1042 angular.module('b2b.att.breadcrumbs',[])
1045 * @name Buttons, links & UI controls.att:buttonGroups
1048 * <file src="src/buttonGroups/docs/readme.md" />
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>
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>
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" />
1076 angular.module('b2b.att.buttonGroups', ['b2b.att.utilities'])
1077 .constant('buttonConfig', {
1078 activeClass: 'active',
1079 toggleEvent: 'click'
1081 .directive('b2bBtnRadio', ['buttonConfig', function (buttonConfig) {
1082 var activeClass = buttonConfig.activeClass || 'active';
1083 var toggleEvent = buttonConfig.toggleEvent || 'click';
1087 link: function (scope, element, attrs, ngModelCtrl) {
1088 var notMobile = !/Android|webOS|iPhone|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent);
1091 element.bind('focus', function () {
1092 scope.$apply(function () {
1093 ngModelCtrl.$setViewValue(scope.$eval(attrs.b2bBtnRadio));
1094 ngModelCtrl.$render();
1099 element.attr('role', 'radio');
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);
1107 element.attr("aria-checked", false);
1112 element.bind(toggleEvent, function () {
1113 if (!element.hasClass(activeClass)) {
1114 scope.$apply(function () {
1115 ngModelCtrl.$setViewValue(scope.$eval(attrs.b2bBtnRadio));
1116 ngModelCtrl.$render();
1123 .directive('b2bBtnCheckbox', ['buttonConfig', function (buttonConfig) {
1124 var activeClass = buttonConfig.activeClass || 'active';
1125 var toggleEvent = buttonConfig.toggleEvent || 'click';
1128 require: ['ngModel', '^^b2bButtonGroup'],
1129 link: function (scope, element, attrs, ctrls) {
1131 var ngModelCtrl = ctrls[0];
1132 var parentCtrl = ctrls[1];
1134 element.attr('role', 'checkbox');
1135 element.attr('aria-describedby', parentCtrl.getStateDescriptionElemId());
1137 function getTrueValue() {
1138 var trueValue = scope.$eval(attrs.b2bBtnCheckboxTrue);
1139 return angular.isDefined(trueValue) ? trueValue : true;
1142 function getFalseValue() {
1143 var falseValue = scope.$eval(attrs.b2bBtnCheckboxFalse);
1144 return angular.isDefined(falseValue) ? falseValue : false;
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);
1153 element.attr("aria-checked", false);
1158 element.bind(toggleEvent, function () {
1159 scope.$apply(function () {
1160 ngModelCtrl.$setViewValue(element.hasClass(activeClass) ? getFalseValue() : getTrueValue());
1161 ngModelCtrl.$render();
1167 .directive('b2bButtonGroup', ['$timeout', '$compile', function ($timeout, $compile) {
1172 ngModelButtonState: '=ngModel'
1174 controller: ['$scope', '$element', function ($scope, $element) {
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);
1181 this.getStateDescriptionElemId = function () {
1182 return stateDescriptionElem.attr('id');
1185 link: function (scope, element) {
1188 var executeFxn = function () {
1190 angular.forEach(scope.ngModelButtonState, function (value, key) {
1191 if (value === true) {
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);
1204 angular.forEach(element.children(), function (chd) {
1205 chd.disabled = false;
1206 chd.setAttribute('aria-disabled', false);
1212 $timeout(function () {
1215 element.bind('click', executeFxn);
1221 * @name Buttons, links & UI controls.att:buttons
1226 * <file src="src/buttons/docs/readme.md" />
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
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
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
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>
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>
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" />
1275 angular.module('b2b.att.buttons', ['b2b.att.utilities']);
1278 * @name Forms.att:calendar
1281 * <file src="src/calendar/docs/readme.md" />
1283 * <input type="text" ng-model="dt" b2b-datepicker>
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" />
1294 angular.module('b2b.att.calendar', ['b2b.att.position', 'b2b.att.utilities'])
1296 .constant('b2bDatepickerConfig', {
1297 dateFormat: 'MM/dd/yyyy',
1299 monthFormat: 'MMMM',
1301 dayHeaderFormat: 'EEEE',
1302 dayTitleFormat: 'MMMM yyyy',
1303 disableWeekend: false,
1304 disableSunday: false,
1306 onSelectClose: null,
1313 legendMessage: null,
1314 calendarDisabled: false,
1316 orientation: 'right',
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']
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);
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);
1343 var watchFunction = function (key, val) {
1344 scope.$parent.$watch(val, function (value) {
1347 scope.$watch(key, function (value) {
1348 scope.$parent[val] = value;
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);
1366 setAttributes: setAttributes,
1367 bindScope: bindScope
1371 .controller('b2bDatepickerController', ['$scope', '$attrs', 'dateFilter', '$element', '$position', 'b2bDatepickerConfig', function ($scope, $attrs, dateFilter, $element, $position, dtConfig) {
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)
1382 startingDay = getValue($attrs.startingDay, dtConfig.startingDay);
1384 if($attrs.disableDates !== undefined) {
1385 format.disableDates = $attrs.disableDates;
1387 format.disableDates = dtConfig.disableDates;
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);
1400 $scope.inline = $attrs.inline === 'true' ? true : dtConfig.inline;
1402 function getValue(value, defaultValue) {
1403 return angular.isDefined(value) ? $scope.$parent.$eval(value) : defaultValue;
1406 function getDaysInMonth(year, month) {
1407 return new Date(year, month, 0).getDate();
1410 function getDates(startDate, n) {
1411 var dates = new Array(n);
1412 var current = startDate,
1415 dates[i++] = new Date(current);
1416 current.setDate(current.getDate() + 1);
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'));
1427 this.isDateInRange = function(date) {
1428 if ((compare(date, $scope.minDate) >= 0) && (compare(date, $scope.maxDate) <= 0)) {
1436 this.isDisbaledDate = function(date) {
1437 if ($attrs.from && !angular.isDate($scope.fromDate)) {
1440 if (format.disableWeekend === true && (dateFilter(date, format.dayHeader) === "Saturday" || dateFilter(date, format.dayHeader) === "Sunday")) {
1443 if (format.disableSunday === true && (dateFilter(date, format.dayHeader) === "Sunday")) {
1447 return (($scope.minDate && compare(date, $scope.minDate) < 0) || ($scope.maxDate && compare(date, $scope.maxDate) > 0) || ($scope.datesCallBack({
1452 function isSelected(dt) {
1453 if (dt && angular.isDate($scope.currentDate) && compare(dt, $scope.currentDate) === 0) {
1459 function isFromDate(dt) {
1460 if (dt && angular.isDate($scope.fromDate) && compare(dt, $scope.fromDate) === 0) {
1466 function isDateRange(dt) {
1467 if (dt && $scope.fromDate && angular.isDate($scope.currentDate) && (compare(dt, $scope.fromDate) >= 0) && (compare(dt, $scope.currentDate) <= 0)) {
1469 } else if (dt && $scope.fromDate && compare(dt, $scope.fromDate) === 0) {
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())) {
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())) {
1491 function isPastDue(dt) {
1492 if ($scope.dueDate) {
1493 return (dt > $scope.dueDate);
1498 function isDueDate(dt) {
1499 if ($scope.dueDate) {
1500 return (dt.getTime() === $scope.dueDate.getTime());
1505 var isDisabled = function (date, currentMonthDate) {
1506 if ($attrs.from && !angular.isDate($scope.fromDate)) {
1509 if (format.disableWeekend === true && (dateFilter(date, format.dayHeader) === "Saturday" || dateFilter(date, format.dayHeader) === "Sunday")) {
1512 if (format.disableSunday === true && (dateFilter(date, format.dayHeader) === "Sunday")) {
1515 if (isOld(date, currentMonthDate) || isNew(date, currentMonthDate)) {
1518 return (($scope.minDate && compare(date, $scope.minDate) < 0) || ($scope.maxDate && compare(date, $scope.maxDate) > 0) || ($scope.datesCallBack({
1523 var compare = function (date1, date2) {
1524 return (new Date(date1.getFullYear(), date1.getMonth(), date1.getDate()) - new Date(date2.getFullYear(), date2.getMonth(), date2.getDate()));
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";
1532 $scope.disablePrev = false;
1533 $scope.visibilityPrev = "visible";
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";
1542 $scope.disableNext = false;
1543 $scope.visibilityNext = "visible";
1547 function getLabel(label) {
1550 pre: label.substr(0, 1).toUpperCase(),
1558 function makeDate(date, dayFormat, dayHeaderFormat, isSelected, isFromDate, isDateRange, isOld, isNew, isDisabled, dueDate, pastDue) {
1561 label: dateFilter(date, dayFormat),
1562 header: dateFilter(date, dayHeaderFormat),
1563 selected: !!isSelected,
1564 fromDate: !!isFromDate,
1565 dateRange: !!isDateRange,
1568 disabled: !!isDisabled,
1571 focusable: !((isDisabled && !(isSelected || isDateRange)) || (isOld || isNew))
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),
1588 if (numDisplayedFromPreviousMonth > 0) {
1589 firstDate.setDate(-numDisplayedFromPreviousMonth + 1);
1590 numDates += numDisplayedFromPreviousMonth; // Previous
1592 numDates += getDaysInMonth(year, month + 1); // Current
1593 numDates += (7 - numDates % 7) % 7; // Next
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,
1607 isDisabled(dt, date),
1611 for (var j = 0; j < 7; j++) {
1612 labels[j] = getLabel(dateFilter(days[j].date, format.dayHeader));
1614 isMinDateAvailable(firstDayOfMonth, lastDayOfMonth);
1615 isMaxDateAvailable(firstDayOfMonth, lastDayOfMonth);
1618 title: dateFilter(date, format.dayTitle),
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) {
1635 datesCallBack: '&disableDates',
1637 disabledInput: '=?ngDisabled'
1639 require: ['b2bDatepicker', 'ngModel', '?^b2bDatepickerGroup'],
1640 controller: 'b2bDatepickerController',
1641 link: function (scope, element, attrs, ctrls) {
1642 var datepickerCtrl = ctrls[0],
1644 b2bDatepickerGroupCtrl = ctrls[2];
1645 var b2bDatepickerPopupTemplate;
1646 var isCalendarOpened = false;
1647 if(scope.disabledInput === undefined || scope.disabledInput === '') {
1648 scope.disabledInput = false;
1650 if(attrs.inline == 'true'){
1651 element.after($compile($templateCache.get('b2bTemplate/calendar/datepicker-popup.html'))(scope));
1652 var temp = element.after();
1656 var buttonTabIndex = scope.disabledInput===true ? -1 : 0;
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);
1662 scope.$watch('model', function(val) {
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}$/ ;
1667 if(!date_regex.test(element[0].value)) {
1668 ngModel.$setValidity('datePattern', false);
1670 ngModel.$setValidity('datePattern', true);
1674 ngModel.$setValidity('datePattern', true);
1680 $log.error("ng-model is required.");
1681 return; // do nothing if no ng-model
1684 if(scope.model !== undefined && scope.model !== '') {
1685 element[0].value = $filter('date')(scope.model, "MM/dd/yyyy");
1688 // Configuration parameters
1691 scope.isOpen = false;
1692 var isValidDate = false;
1697 if (b2bDatepickerGroupCtrl) {
1698 b2bDatepickerGroupCtrl.registerDatepickerScope(scope);
1701 var calendarButton = angular.element(element[0].nextElementSibling);
1703 calendarButton.bind('click',function(){
1704 openCalendarPopup = false;
1705 if (!scope.ngDisabled) {
1706 scope.isOpen = !scope.isOpen;
1707 toggleCalendar(scope.isOpen);
1709 datepickerCtrl.updatePosition(b2bDatepickerPopupTemplate);
1710 $timeout(function () {
1711 // angular.element(element[0].querySelector('.datepicker-input')).scrollTop=0;
1715 var openCalendarPopup = false;
1717 element.bind('blur', function() {
1718 if(scope.model !== undefined && scope.model !== '') {
1719 var dateEntered = scope.model;
1721 var date_regex = /^(0[1-9]|1[0-2])\/(0[1-9]|1\d|2\d|3[01])\/(19|20)\d{2}$/ ;
1723 if(date_regex.test(dateEntered)) {
1724 var parts = dateEntered.split('/');
1725 var enteredDate = new Date(parts[2],parts[0]-1,parts[1]);
1727 if(datepickerCtrl.isDateInRange(enteredDate)) {
1729 ngModel.$setValidity('outOfRange', true);
1730 $timeout(function(){
1731 ngModel.$setValidity('outOfRange', true);
1734 if(!datepickerCtrl.isDisbaledDate(enteredDate)) {
1735 $timeout(function(){
1736 ngModel.$setValidity('disabledDate', true);
1738 scope.select(enteredDate);
1739 openCalendarPopup = true;
1741 $timeout(function(){
1742 ngModel.$setValidity('disabledDate', false);
1744 isValidDate = false;
1745 openCalendarPopup = false;
1750 $timeout(function(){
1751 ngModel.$setValidity('outOfRange', false);
1753 isValidDate = false;
1754 openCalendarPopup = false;
1761 var toggleCalendar = function (flag) {
1762 if (!scope.inline) {
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);
1772 $timeout(function () {
1773 scope.getFocus = false;
1778 angular.element(document.querySelector('.b2b-calendar-icon')).attr('aria-expanded','true');
1780 if(!openCalendarPopup) {
1781 b2bDatepickerPopupTemplate.unbind('keydown', keyPress);
1782 b2bDatepickerPopupTemplate.remove();
1785 scope.getFocus = false;
1786 angular.element(document.querySelector('.b2b-calendar-icon')).attr('aria-expanded','false');
1787 trapFocusInElement(flag, b2bDatepickerPopupTemplate);
1792 var handleTabEvent = function(){
1793 b2bDatepickerPopupTemplate.find('td').on('keydown', function (e) {
1794 if (e.keyCode == '9') {
1796 if(b2bDatepickerPopupTemplate.find('tr')[0].querySelector('th.next')){
1797 b2bDatepickerPopupTemplate.find('tr')[0].querySelector('th.next').focus();
1799 b2bDatepickerPopupTemplate.find('tr')[0].querySelector('th.datepicker-switch').focus()
1802 if(b2bDatepickerPopupTemplate.find('tr')[0].querySelector('th.prev')){
1803 b2bDatepickerPopupTemplate.find('tr')[0].querySelector('th.prev').focus();
1805 b2bDatepickerPopupTemplate.find('tr')[0].querySelector('th.datepicker-switch').focus()
1810 e.stopPropagation();
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);
1825 var keyPress = function (ev) {
1828 ev.keyCode = ev.which;
1829 } else if (ev.charCode) {
1830 ev.keyCode = ev.charCode;
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;
1844 $timeout(function () {
1845 scope.getFocus = false;
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;
1856 $timeout(function () {
1857 scope.getFocus = false;
1861 ev.preventDefault();
1862 ev.stopPropagation();
1868 $documentBind.click('isOpen', outsideClick, scope);
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);
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);
1887 if (((datepickerTextfield.top + datepickerTextfield.height) < modalBodyPosition.top || datepickerTextfield.top > (modalBodyPosition.top + modalBodyPosition.height)) && scope.isOpen) {
1888 scope.isOpen = false;
1889 toggleCalendar(scope.isOpen);
1895 var window = angular.element($window);
1896 window.bind('resize', function () {
1897 if (b2bDatepickerPopupTemplate) {
1898 datepickerCtrl.updatePosition(b2bDatepickerPopupTemplate);
1903 scope.$on('$destroy', function () {
1905 scope.isOpen = false;
1906 toggleCalendar(scope.isOpen);
1910 scope.resetTime = function (date) {
1911 if (typeof date === 'string') {
1912 date = date + 'T12:00:00';
1915 if (!isNaN(new Date(date))) {
1916 dt = new Date(date);
1920 return new Date(dt.getFullYear(), dt.getMonth(), dt.getDate());
1924 scope.$parent.$watch($parse(attrs.min), function (value) {
1925 scope.minDate = value ? scope.resetTime(value) : null;
1930 scope.$parent.$watch($parse(attrs.max), function (value) {
1931 scope.maxDate = value ? scope.resetTime(value) : null;
1936 scope.$parent.$watch($parse(attrs.due), function (value) {
1937 scope.dueDate = value ? scope.resetTime(value) : null;
1942 scope.$parent.$watch($parse(attrs.from), function (value) {
1943 scope.fromDate = value ? scope.resetTime(value) : null;
1948 if (attrs.legendIcon) {
1949 scope.$parent.$watch(attrs.legendIcon, function (value) {
1950 scope.legendIcon = value ? value : null;
1954 if (attrs.legendMessage) {
1955 scope.$parent.$watch(attrs.legendMessage, function (value) {
1956 scope.legendMessage = value ? value : null;
1960 if (attrs.ngDisabled) {
1961 scope.$parent.$watch(attrs.ngDisabled, function (value) {
1962 scope.ngDisabled = value ? value : null;
1966 // Split array into smaller arrays
1967 function split(arr, size) {
1969 while (arr.length > 0) {
1970 arrays.push(arr.splice(0, size));
1975 function refill(date) {
1976 if (angular.isDate(date) && !isNaN(date)) {
1977 selected = new Date(date);
1980 selected = new Date();
1985 var currentMode = datepickerCtrl.modes[mode],
1986 data = currentMode.getVisibleDates(selected);
1987 scope.rows = split(data.objects, currentMode.split);
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++) {
1994 if (scope.rows[i][j].label === "1" && !firstSelected) {
1995 firstSelected = true;
1996 var firstDay = scope.rows[i][j];
1999 if (scope.rows[i][j].selected === true) {
2009 firstDay.firstFocus = true;
2012 scope.labels = data.labels || [];
2013 scope.title = data.title;
2015 datepickerCtrl.updatePosition(b2bDatepickerPopupTemplate);
2019 scope.select = function (date) {
2020 var dt = new Date(date.getFullYear(), date.getMonth(), date.getDate());
2021 if (!scope.onSelectClose || (scope.onSelectClose && scope.onSelectClose({
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);
2033 scope.isOpen = false;
2034 toggleCalendar(scope.isOpen);
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));
2046 $timeout(function () {
2047 trapFocusInElement();
2051 $event.preventDefault();
2052 $event.stopPropagation();
2055 scope.trapFocus = function () {
2056 $timeout(function () {
2057 trapFocusInElement();
2061 scope.$watch('currentDate', function (value) {
2062 if (angular.isDefined(value) && value !== null) {
2067 ngModel.$setViewValue(value);
2070 ngModel.$render = function () {
2071 scope.currentDate = ngModel.$viewValue;
2074 var stringToDate = function (value) {
2075 if (!isNaN(new Date(value))) {
2076 value = new Date(value);
2080 ngModel.$formatters.unshift(stringToDate);
2086 .directive('b2bDatepickerGroup', [function () {
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;
2097 link: function (scope, elem, attr, ctrl) {}
2101 .directive('b2bFormatDate', ['dateFilter', function (dateFilter) {
2105 link: function (scope, elem, attr, ctrl) {
2106 var b2bFormatDate = "";
2107 attr.$observe('b2bFormatDate', function (value) {
2108 b2bFormatDate = value;
2110 var dateToString = function (value) {
2111 if (!isNaN(new Date(value))) {
2112 return dateFilter(new Date(value), b2bFormatDate);
2116 ctrl.$formatters.unshift(dateToString);
2121 .directive('b2bDatepickerHeader', [function () {
2124 require: '^b2bDatepickerGroup',
2128 compile: function (elem, attr, transclude) {
2129 return function link(scope, elem, attr, ctrl) {
2131 ctrl.$$headers.push(transclude(scope, function () {}));
2139 .directive('b2bDatepickerFooter', [function () {
2142 require: '^b2bDatepickerGroup',
2146 compile: function (elem, attr, transclude) {
2147 return function link(scope, elem, attr, ctrl) {
2149 ctrl.$$footers.push(transclude(scope, function () {}));
2158 * @name Forms.att:checkboxes
2161 * <file src="src/checkboxes/docs/readme.md" />
2165 <example module="b2b.att">
2166 <file src="src/checkboxes/docs/demo.html" />
2167 <file src="src/checkboxes/docs/demo.js" />
2170 angular.module('b2b.att.checkboxes', ['b2b.att.utilities'])
2171 .directive('b2bSelectGroup', [function (){
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;
2186 scope.$watch('checkboxes', function () {
2188 if(scope.checkboxes === undefined) {
2191 angular.forEach(scope.checkboxes, function (item) {
2192 if (item.isSelected) {
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');
2204 ctrl.$setViewValue(false);
2205 elem.addClass('indeterminate');
2206 elem.prop('indeterminate', true);
2215 * @name Misc.att:coachmark
2218 * <file src="src/coachmark/docs/readme.md" />
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>
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" />
2234 angular.module('b2b.att.coachmark', ['b2b.att.utilities','b2b.att.position'])
2236 .directive('b2bCoachmark', ['$document', '$compile', '$position', '$timeout', 'b2bViewport', 'keymap', function($document, $compile, $position, $timeout, b2bViewport, keymap) {
2241 coachmarkIndex: '=',
2242 startCoachmarkCallback: '&',
2243 endCoachmarkCallback: '&',
2244 actionCoachmarkCallback: '&'
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 ={
2260 scope.currentCoachmark = {};
2263 var coachmarkBackdrop = function(){
2264 backdropjqLiteEl = angular.element('<div class="b2b-modal-backdrop fade in hidden-by-modal"></div>');
2265 body.append(backdropjqLiteEl);
2267 backdropjqLiteEl.bind('click', function() {
2268 scope.closeCoachmark();
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;
2281 scope.actionCoachmark = function(action){
2282 scope.actionCoachmarkCallback({
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')
2295 if (angular.isFunction(scope.endCoachmarkCallback)){
2296 scope.endCoachmarkCallback();
2301 var realStyle = function(_elem, _style) {
2303 if ( typeof _elem.currentStyle != 'undefined' ) {
2304 computedStyle = _elem.currentStyle;
2306 computedStyle = document.defaultView.getComputedStyle(_elem, null);
2309 return _style ? computedStyle[_style] : computedStyle;
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
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;
2331 function showCoachmark(targetElement) {
2333 scope.currentCoachmark = targetElement;
2334 if(coachMarkElement !== undefined && coachMarkElement !==""){
2335 coachMarkElement.removeClass('b2b-coachmark-label')
2336 coackmarkContainer.remove();
2337 coachmarkHighlight.remove();
2339 coachMarkElement = angular.element(document.querySelector(targetElement.elementId));
2341 var elementPosition = $position.offset(coachMarkElement);
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'
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])
2361 coachmarkHighlight[0].appendChild(copy); // IE11 only supports appendChild, not append
2363 coachMarkElement.addClass('b2b-coachmark-label');
2366 body.append(coachmarkHighlight);
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);
2375 $timeout(function () {
2376 var currentCoachmarkContainer = document.getElementsByClassName('b2b-coachmark-container')[0];
2377 currentCoachmarkContainer.focus();
2379 newElem = angular.element(currentCoachmarkContainer);
2380 newElem.bind('keydown', function (e) {
2381 if(e.keyCode == keymap.KEY.TAB){
2383 if(e.target.className === 'b2b-coachmark-container'){
2385 e.stopPropagation();
2390 var coachmarkHeight = window.getComputedStyle(currentCoachmarkContainer).height.split('px')[0];
2391 var newOffsetHeight = Math.round(elementPosition.top) - elementPosition.height;
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}});
2400 element.bind('click', function (e) {
2401 initaitedCoachmark = true;
2403 scope.$watch('coachmarkIndex', function () {
2404 if(initaitedCoachmark === true){
2405 if(scope.coachmarkIndex === -1){
2406 scope.closeCoachmark();
2408 findAvailableCoachmark(scope.coachmarkIndex);
2409 showCoachmark(scope.coachmarks[scope.coachmarkIndex]);
2413 coachmarkBackdrop();
2414 var findAvailableCoachmark = function(index){
2416 scope.coachmarkIndex = 0;
2417 } else if(!angular.isDefined(scope.coachmarks[index]) || angular.element(document.querySelector(scope.coachmarks[index].elementId)).length === 0){
2418 findAvailableCoachmark(index-1);
2420 scope.coachmarkIndex = index;
2423 if (angular.isFunction(scope.startCoachmarkCallback)){
2424 scope.startCoachmarkCallback();
2426 findAvailableCoachmark(scope.coachmarkIndex);
2427 showCoachmark(scope.coachmarks[scope.coachmarkIndex]);
2429 $document.bind('keydown', function (evt) {
2430 if (evt.which === 27 && initaitedCoachmark) {
2431 scope.closeCoachmark();
2436 //performance technique to ensure scroll event doesn't cause lag
2437 var throttle = function(type, name, obj) {
2438 obj = obj || window;
2439 var running = false;
2440 var func = function() {
2441 if (running) { return; }
2443 requestAnimationFrame(function() {
2444 obj.dispatchEvent(new CustomEvent(name));
2448 obj.addEventListener(type, func);
2451 scope.viewportWidth = b2bViewport.viewportWidth();
2452 /* init - you can init any event */
2453 throttle("resize", "optimizedResize");
2454 window.addEventListener("optimizedResize", function() {
2455 if(initaitedCoachmark){
2456 showCoachmark(scope.coachmarks[scope.coachmarkIndex]);
2457 scope.viewportWidth = b2bViewport.viewportWidth();
2464 .directive('b2bCoachmarkContainer', ['$document', '$position', function($document, $position) {
2469 templateUrl: 'b2bTemplate/coachmark/coachmark.html',
2470 link: function (scope, element, attrs, ctrl) {
2479 * @name Template.att:Configuration Section
2482 * <file src="src/configurationSection/docs/readme.md" />
2485 * <section id="code">
2486 <b>HTML + AngularJS</b>
2487 <example module="b2b.att">
2488 <file src="src/configurationSection/docs/demo.html" />
2489 <file src="src/configurationSection/docs/demo.js" />
2494 angular.module('b2b.att.configurationSection', [])
2498 * @name Template.att:Directory Listing
2501 * <file src="src/directoryListingTemplate/docs/readme.md" />
2504 * <section id="code">
2505 <b>HTML + AngularJS</b>
2506 <example module="b2b.att">
2507 <file src="src/directoryListingTemplate/docs/demo.html" />
2508 <file src="src/directoryListingTemplate/docs/demo.js" />
2513 angular.module('b2b.att.directoryListingTemplate', [])
2517 * @name Forms.att:dropdowns
2520 * <file src="src/dropdowns/docs/readme.md" />
2525 <example module="b2b.att">
2526 <file src="src/dropdowns/docs/demo.html" />
2527 <file src="src/dropdowns/docs/demo.js" />
2531 angular.module('b2b.att.dropdowns', ['b2b.att.utilities', 'b2b.att.position', 'ngSanitize'])
2533 .constant('b2bDropdownConfig', {
2536 menuKeyword: 'menu',
2537 linkMenuKeyword: 'link-menu',
2538 largeKeyword: 'large',
2539 smallKeyword: 'small'
2542 .directive("b2bDropdown", ['$timeout', '$compile', '$templateCache', 'b2bUserAgent', 'b2bDropdownConfig', '$position', function ($timeout, $compile, $templateCache, b2bUserAgent, b2bDropdownConfig, $position) {
2547 controller: ['$scope', '$element', '$attrs', function (scope, elem, attr) {
2548 scope.isInputDropdown = true;
2549 scope.placeHoldertext = attr.placeholderText;
2551 if (attr.type.indexOf(b2bDropdownConfig.menuKeyword) > -1 || attr.type.indexOf(b2bDropdownConfig.linkMenuKeyword) > -1) {
2552 scope.isInputDropdown = false;
2553 if (attr.type.indexOf(b2bDropdownConfig.linkMenuKeyword) > -1) {
2554 scope.dropdownType = b2bDropdownConfig.linkMenuKeyword;
2555 } else if (attr.type.indexOf(b2bDropdownConfig.menuKeyword) > -1) {
2556 scope.dropdownType = b2bDropdownConfig.menuKeyword;
2559 if (attr.type.indexOf(b2bDropdownConfig.largeKeyword) > -1) {
2560 scope.dropdownSize = b2bDropdownConfig.largeKeyword;
2561 } else if (attr.type.indexOf(b2bDropdownConfig.smallKeyword) > -1) {
2562 scope.dropdownSize = b2bDropdownConfig.smallKeyword;
2566 scope.labelText = attr.labelText;
2568 scope.setBlur = function () {
2572 if ((scope.isInputDropdown && b2bUserAgent.notMobile()) || (!scope.isInputDropdown)) {
2573 var formCtrl = elem.controller('form');
2574 scope.setNgModelController = function (name, ngModelCtrl) {
2575 if (name && formCtrl && ngModelCtrl) {
2576 formCtrl[name] = ngModelCtrl;
2579 scope.setOptionalCta = function (optionalCta) {
2580 scope.optionalCta = optionalCta;
2582 var innerHtml = angular.element('<div></div>').append(elem.html());
2583 innerHtml = ($compile(innerHtml)(scope)).html();
2584 var template = angular.element($templateCache.get('b2bTemplate/dropdowns/b2bDropdownDesktop.html'));
2585 template.find('ul').eq(0).append(innerHtml);
2586 template = $compile(template)(scope);
2587 elem.replaceWith(template);
2588 } else if (scope.isInputDropdown && b2bUserAgent.isMobile()) {
2591 'filter': 'alpha(opacity=0)'
2593 elem.addClass('awd-select isWrapped');
2594 elem.wrap('<span class="selectWrap"></span>');
2595 var cover = angular.element('<span aria-hidden="true"><i class="icon-primary-down" aria-hidden="true"></i></span>');
2596 elem.parent().append(cover);
2597 elem.parent().append('<i class="icon-primary-down" aria-hidden="true"></i>');
2598 var set = function () {
2599 var sel = elem[0] ? elem[0] : elem;
2600 var selectedText = "";
2601 var selIndex = sel.selectedIndex;
2602 if (typeof selIndex !== 'undefined') {
2603 selectedText = sel.options[selIndex].text;
2605 cover.text(selectedText).append('<i class="icon-primary-down" aria-hidden="true"></i>');
2607 var update = function (value) {
2612 scope.$watch(attr.ngModel, function (newVal, oldVal) {
2616 elem.bind('keyup', function (ev) {
2617 if (ev.keyCode === keymap.KEY.TAB || ev.keyCode === keymap.KEY.ESC) {
2625 link: function (scope, elem, attr, ctrl) {
2626 if ((scope.isInputDropdown && b2bUserAgent.notMobile()) || (!scope.isInputDropdown)) {
2627 scope.updateModel = function () {
2628 ctrl.$setViewValue(scope.currentSelected.value);
2629 if (scope.dropdownRequired && scope.currentSelected.value === '') {
2630 scope.setRequired(false);
2632 scope.setRequired(true);
2635 if (scope.dropdownType === b2bDropdownConfig.linkMenuKeyword) {
2636 $timeout(function () {
2637 scope.appendCaretPositionStyle();
2641 ctrl.$render = function () {
2643 $timeout(function () {
2645 if ((angular.isUndefined(ctrl.$viewValue) || ctrl.$viewValue == '') && (angular.isUndefined(scope.placeHoldertext) || scope.placeHoldertext == '')) {
2646 scope.dropdownLists[ctrl.$viewValue] && scope.dropdownLists[ctrl.$viewValue][0].updateDropdownValue();
2647 } else if ((angular.isUndefined(scope.placeHoldertext) || scope.placeHoldertext == '') && ctrl.$viewValue !== '' ) {
2648 scope.dropdownLists[ctrl.$viewValue] && scope.dropdownLists[ctrl.$viewValue][0].updateDropdownValue();
2649 } else if ((angular.isUndefined(ctrl.$viewValue) || ctrl.$viewValue == '') && scope.placeHoldertext !== '' ) {
2650 scope.currentSelected.text = scope.placeHoldertext;
2651 ctrl.$setViewValue(scope.placeHoldertext);
2653 scope.dropdownLists[ctrl.$viewValue] && scope.dropdownLists[ctrl.$viewValue][0].updateDropdownValue();
2659 scope.disabled = false;
2660 scope.dropdownName = attr.name;
2661 scope.dropdownId = attr.id;
2662 scope.labelId = attr.ariaLabelledby;
2663 scope.dropdownDescribedBy = attr.ariaDescribedby;
2664 if (attr.required) {
2665 scope.dropdownRequired = true;
2667 scope.dropdownRequired = false;
2669 elem.removeAttr('name');
2670 elem.removeAttr('id');
2671 scope.$parent.$watch(attr.ngDisabled, function (val) {
2672 scope.disabled = val;
2679 .directive("b2bDropdownToggle", ['$document', '$documentBind', '$isElement', 'b2bDropdownConfig', 'keymap', 'b2bUtilitiesConfig', '$timeout', '$position', function ($document, $documentBind, $isElement, b2bDropdownConfig, keymap, b2bUtilitiesConfig, $timeout, $position) {
2682 require: '?^b2bKey',
2683 link: function (scope, elem, attr, ctrl) {
2684 scope.appendCaretPositionStyle = function () {
2685 while (document.querySelector('style.b2bDropdownCaret')) {
2686 document.querySelector('style.b2bDropdownCaret').remove();
2688 var caretPosition = $position.position(elem).width - 26;
2689 if (scope.dropdownType === b2bDropdownConfig.linkMenuKeyword) {
2690 var template = angular.element('<style class="b2bDropdownCaret" type="text/css">.linkSelectorModule .active+.moduleWrapper:before {left: ' + caretPosition + 'px;}</style>');
2691 $document.find('head').append(template);
2695 if (scope.isInputDropdown && (scope.labelText !== undefined)) {
2696 elem.attr('aria-label', scope.labelText);
2699 scope.toggleFlag = false;
2700 scope.dropdownLists = {};
2701 scope.dropdownListValues = [];
2705 scope.currentSelected = {
2711 scope.dropdownTextList = [];
2712 var searchString = '';
2714 scope.removeItem = function(value){
2715 delete scope.dropdownLists[value];
2716 var index = scope.dropdownListValues.indexOf(value);
2717 scope.dropdownListValues.splice(index,1);
2718 scope.dropdownTextList=[];
2719 scope.dropdown.totalIndex = scope.dropdownListValues.length-1;
2721 var getDropdownText = function(){
2722 var dropdownItems = elem.parent().find('ul').children();
2723 var count = dropdownItems.length;
2724 for(var i=0;i<count;i++){
2725 scope.dropdownTextList.push(dropdownItems.eq(i).text());
2728 var searchElement = function (searchExp) {
2729 if(scope.dropdownTextList.length ==0){
2732 var regex = new RegExp("^" + searchExp, "i");
2733 var position = scope.dropdownTextList.regexIndexOf(regex, scope.currentSelected.index + 1, true);
2734 if (position > -1) {
2739 var startTimer = function (time) {
2740 if (searchString === '') {
2741 $timeout(function () {
2746 scope.toggleDropdown = function (toggleFlag) {
2747 if (!scope.disabled) {
2748 if (angular.isDefined(toggleFlag)) {
2749 scope.toggleFlag = toggleFlag;
2751 scope.toggleFlag = !scope.toggleFlag;
2753 if (!scope.toggleFlag) {
2754 if (scope.isInputDropdown) {
2755 elem.parent().find('input')[0].focus();
2757 elem.parent().find('button')[0].focus();
2760 scope.dropdown.highlightedValue = scope.currentSelected.value;
2761 if (ctrl && ctrl.enableSearch) {
2762 if (angular.isDefined(scope.dropdownLists[scope.currentSelected.value])) {
2763 ctrl.resetCounter(scope.dropdownLists[scope.currentSelected.value][2]);
2766 $timeout(function () {
2767 if(scope.dropdownLists[scope.currentSelected.value] !== undefined){
2768 (scope.dropdownLists[scope.currentSelected.value][1])[0].focus();
2770 if (scope.isInputDropdown) {
2771 elem.parent().find('input')[0].focus();
2773 elem.parent().find('button')[0].focus();
2777 if (scope.dropdownType === b2bDropdownConfig.linkMenuKeyword) {
2778 scope.appendCaretPositionStyle();
2784 elem.bind('keydown', function (ev) {
2787 ev.keyCode = ev.which;
2788 } else if (ev.charCode) {
2789 ev.keyCode = ev.charCode;
2792 if (!scope.toggleFlag) {
2794 var currentIndex = scope.currentSelected.index;
2795 if (ev.keyCode === keymap.KEY.DOWN) {
2796 scope.toggleDropdown(true);
2797 ev.preventDefault();
2798 ev.stopPropagation();
2799 } else if (b2bDropdownConfig.prev.split(',').indexOf(ev.keyCode.toString()) > -1) {
2800 angular.isDefined(scope.dropdownListValues[currentIndex - 1]) ? scope.dropdownLists[scope.dropdownListValues[currentIndex - 1]][0].updateDropdownValue() : angular.noop();
2801 ev.preventDefault();
2802 ev.stopPropagation();
2803 } else if (b2bDropdownConfig.next.split(',').indexOf(ev.keyCode.toString()) > -1) {
2804 angular.isDefined(scope.dropdownListValues[currentIndex + 1]) ? scope.dropdownLists[scope.dropdownListValues[currentIndex + 1]][0].updateDropdownValue() : angular.noop();
2805 ev.preventDefault();
2806 ev.stopPropagation();
2807 } else if (ev.keyCode >= 48 && ev.keyCode <= 105) {
2808 startTimer(b2bUtilitiesConfig.searchTimer);
2809 searchString = searchString + (keymap.MAP[ev.keyCode] || '');
2810 var position = searchElement(searchString);
2811 angular.isDefined(scope.dropdownListValues[position]) ? scope.dropdownLists[scope.dropdownListValues[position]][0].updateDropdownValue() : angular.noop();
2812 ev.preventDefault();
2813 ev.stopPropagation();
2817 if (ev.altKey === true && ev.keyCode === keymap.KEY.UP) {
2818 scope.toggleDropdown(false);
2819 ev.preventDefault();
2820 ev.stopPropagation();
2821 } else if (ev.keyCode === keymap.KEY.TAB || ev.keyCode === keymap.KEY.ESC) {
2822 scope.toggleDropdown(false);
2823 ev.preventDefault();
2824 ev.stopPropagation();
2827 scope.$apply(); // TODO: Move this into each block to avoid expensive digest cycles
2829 var outsideClick = function (e) {
2830 var isElement = $isElement(angular.element(e.target), elem.parent(), $document);
2832 scope.toggleDropdown(false);
2836 $documentBind.click('toggleFlag', outsideClick, scope);
2837 $documentBind.touch('toggleFlag', outsideClick, scope);
2842 .directive("b2bDropdownGroup", ['$compile', '$templateCache', 'b2bUserAgent', function ($compile, $templateCache, b2bUserAgent) {
2845 controller: ['$scope', '$element', '$attrs', function (scope, elem, attr) {
2846 if ((scope.isInputDropdown && b2bUserAgent.notMobile()) || (!scope.isInputDropdown)) {
2847 var innerHtml = angular.element('<div></div>').append(elem.html());
2848 innerHtml = ($compile(innerHtml)(scope)).html();
2849 var template = angular.element($templateCache.get('b2bTemplate/dropdowns/b2bDropdownGroupDesktop.html'));
2850 template.attr('ng-repeat', attr.optGroupRepeat);
2851 template.attr('label', elem.attr('label'));
2852 template.find('ul').append(innerHtml);
2853 elem.replaceWith(template);
2854 } else if (scope.isInputDropdown && b2bUserAgent.isMobile()) {
2855 var template = angular.element(elem.prop('outerHTML'));
2856 template.attr('ng-repeat', attr.optGroupRepeat);
2857 template.removeAttr('b2b-dropdown-group');
2858 template.removeAttr('opt-group-repeat');
2859 template = $compile(template)(scope);
2860 elem.replaceWith(template);
2866 .directive("b2bDropdownGroupDesktop", [function () {
2870 link: function (scope, elem, attr, ctrl) {
2871 scope.groupHeader = attr.label;
2876 .directive("b2bDropdownList", ['$compile', '$templateCache', 'b2bUserAgent', function ($compile, $templateCache, b2bUserAgent) {
2879 controller: ['$scope', '$element', '$attrs', function (scope, elem, attr) {
2880 if ((scope.isInputDropdown && b2bUserAgent.notMobile()) || (!scope.isInputDropdown)) {
2881 var innerHtml = angular.element('<div></div>').append(elem.html());
2882 innerHtml = ($compile(innerHtml)(scope)).html();
2883 var template = angular.element($templateCache.get('b2bTemplate/dropdowns/b2bDropdownListDesktop.html'));
2884 template.attr('ng-repeat', attr.optionRepeat);
2885 template.attr('value', elem.attr('value'));
2886 template.attr('search-key', elem.text());
2887 if (elem.attr('aria-describedby')){
2888 template.attr('aria-describedby', attr.ariaDescribedby);
2890 if (elem.attr('imgsrc')) {
2891 if (elem.attr('imgalt')) {
2892 template.append('<img role="presentation" ng-src="' + elem.attr('imgsrc') + '" alt="' + elem.attr('imgalt') + '"/>');
2894 template.append('<img role="presentation" ng-src="' + elem.attr('imgsrc') + '" alt=""/>');
2897 template.append(innerHtml);
2898 elem.replaceWith(template);
2899 } else if (scope.isInputDropdown && b2bUserAgent.isMobile()) {
2900 var template = angular.element(elem.prop('outerHTML'));
2901 template.attr('ng-repeat', attr.optionRepeat);
2902 if (elem.attr('aria-describedby')){
2903 template.attr('aria-describedby', attr.ariaDescribedby);
2905 template.removeAttr('b2b-dropdown-list');
2906 template.removeAttr('option-repeat');
2907 template = $compile(template)(scope);
2908 elem.replaceWith(template);
2914 .directive("b2bDropdownListDesktop", ['$sce', 'keymap', 'b2bDropdownConfig', function ($sce, keymap, b2bDropdownConfig) {
2919 link: function (scope, elem, attr, ctrl) {
2920 var dropdownListValue = scope.dropdownListValue = attr.value;
2921 scope.dropdown.totalIndex++;
2922 var dropdownListIndex = scope.dropdown.totalIndex;
2923 scope.dropdownListValues.push(dropdownListValue);
2924 scope.dropdownLists[dropdownListValue] = [];
2925 scope.dropdownLists[dropdownListValue][0] = scope;
2926 scope.dropdownLists[dropdownListValue][1] = elem;
2927 scope.dropdownLists[dropdownListValue][2] = dropdownListIndex;
2928 scope.$parent.$parent.dropdownTextList=[];
2929 scope.updateDropdownValue = function () {
2930 scope.currentSelected.value = dropdownListValue;
2931 if (scope.isInputDropdown) {
2932 scope.currentSelected.text = elem.text();
2933 scope.currentSelected.label = elem.text();
2934 } else if ((scope.dropdownType === b2bDropdownConfig.linkMenuKeyword) || (scope.dropdownType === b2bDropdownConfig.menuKeyword && scope.dropdownSize === b2bDropdownConfig.smallKeyword)) {
2935 scope.currentSelected.text = dropdownListValue;
2936 scope.currentSelected.label = dropdownListValue;
2937 } else if (scope.dropdownType === b2bDropdownConfig.menuKeyword) {
2938 scope.currentSelected.text = $sce.trustAsHtml(elem.html());
2939 scope.currentSelected.label = elem.text();
2941 scope.currentSelected.index = dropdownListIndex;
2942 scope.updateModel();
2944 scope.selectDropdownItem = function () {
2946 scope.updateDropdownValue();
2947 scope.toggleDropdown(false);
2949 scope.highlightDropdown = function () {
2950 scope.dropdown.highlightedValue = dropdownListValue;
2952 elem.bind('mouseover', function (ev) {
2956 elem.bind('keydown', function (ev) {
2959 ev.keyCode = ev.which;
2960 } else if (ev.charCode) {
2961 ev.keyCode = ev.charCode;
2964 if (ev.altKey === true && ev.keyCode === keymap.KEY.UP) {
2965 scope.toggleDropdown(false);
2966 ev.preventDefault();
2967 ev.stopPropagation();
2968 } else if (ev.keyCode === keymap.KEY.TAB || ev.keyCode === keymap.KEY.ESC) {
2969 scope.toggleDropdown(false);
2970 ev.preventDefault();
2971 ev.stopPropagation();
2975 scope.$on('$destroy',function(){
2976 scope.removeItem(dropdownListValue);
2982 .directive("b2bDropdownRepeat", ['$compile', 'b2bUserAgent', function ($compile, b2bUserAgent) {
2985 controller: ['$scope', '$element', '$attrs', function (scope, elem, attr) {
2986 if ((scope.isInputDropdown && b2bUserAgent.notMobile()) || (!scope.isInputDropdown)) {
2987 var innerHtml = angular.element('<div></div>').append(elem.html());
2988 innerHtml = ($compile(innerHtml)(scope)).html();
2989 var template = angular.element('<div></div>');
2990 template.attr('ng-repeat', attr.b2bDropdownRepeat);
2991 template.append(innerHtml);
2992 elem.replaceWith(template);
2993 } else if (scope.isInputDropdown && b2bUserAgent.isMobile()) {
3000 .directive("b2bDropdownValidation", ['$timeout', function ($timeout) {
3004 link: function (scope, elem, attr, ctrl) {
3005 $timeout(function () {
3006 scope.setNgModelController(attr.name, ctrl);
3008 scope.setDirty = function () {
3009 if (ctrl.$dirty === false) {
3011 ctrl.$pristine = false;
3014 scope.setTouched = function () {
3015 if (ctrl.$touched === false) {
3016 ctrl.$touched = true;
3017 ctrl.$pristine = false;
3020 scope.setRequired = function (flag) {
3021 ctrl.$setValidity('required', flag);
3027 .directive('b2bDropdownOptionalCta', [function () {
3033 compile: function (elem, attr, transclude) {
3034 return function link(scope, elem, attr, ctrl) {
3035 if (scope.setOptionalCta) {
3036 scope.setOptionalCta(transclude(scope, function () {}));
3045 * @name Forms.att:File Upload
3048 * <file src="src/fileUpload/docs/readme.md" />
3052 <form id="dragDropFile">
3053 <div b2b-file-drop file-model="fileModel" on-drop="triggerFileUpload()" align="center">
3055 <br>To upload a file, drag & drop it here or
3056 <span b2b-file-link file-model="fileModel" on-file-select="triggerFileUpload()" >
3057 click here to select from your computer.
3064 * <section id="code">
3065 <example module="b2b.att">
3066 <file src="src/fileUpload/docs/demo.html" />
3067 <file src="src/fileUpload/docs/demo.js" />
3072 angular.module('b2b.att.fileUpload', ['b2b.att.utilities'])
3073 .directive('b2bFileDrop', [function() {
3080 controller: ['$scope', '$attrs', function($scope, $attrs) {
3081 this.onDrop = $scope.onDrop;
3083 link: function(scope, element) {
3084 element.addClass('b2b-dragdrop');
3088 if (e.originalEvent) {
3089 e.dataTransfer = e.originalEvent.dataTransfer;
3091 e.dataTransfer.dropEffect = 'move';
3092 // allows us to drop
3093 if (e.preventDefault) {
3096 element.addClass('b2b-dragdrop-over');
3103 // allows us to drop
3104 if (e.preventDefault) {
3107 element.addClass('b2b-dragdrop-over');
3114 element.removeClass('b2b-dragdrop-over');
3121 // Stops some browsers from redirecting.
3122 if (e.preventDefault) {
3125 if (e.stopPropagation) {
3126 e.stopPropagation();
3128 if (e.originalEvent) {
3129 e.dataTransfer = e.originalEvent.dataTransfer;
3131 element.removeClass('b2b-dragdrop-over');
3132 if (e.dataTransfer.files && e.dataTransfer.files.length > 0) {
3133 scope.fileModel = e.dataTransfer.files[0];
3135 if (angular.isFunction(scope.onDrop)) {
3145 .directive('b2bFileLink', [function() {
3148 require: '^?b2bFileDrop',
3151 templateUrl: 'b2bTemplate/fileUpload/fileUpload.html',
3156 controller: ['$scope', function($scope) {
3157 this.setFileModel = function(fileModel) {
3158 if ($scope.takeFileModelFromParent) {
3159 $scope.$parent.fileModel = fileModel;
3160 $scope.$parent.$apply();
3162 $scope.fileModel = fileModel;
3166 this.callbackFunction = function() {
3167 if (angular.isFunction($scope.onFileSelect)) {
3168 $scope.onFileSelect();
3173 link: function(scope, element, attr, b2bFileDropCtrl) {
3174 scope.takeFileModelFromParent = false;
3175 if (!(attr.fileModel) && b2bFileDropCtrl) {
3176 scope.takeFileModelFromParent = true;
3178 if (!(attr.onFileSelect) && b2bFileDropCtrl) {
3179 scope.onFileSelect = b2bFileDropCtrl.onDrop;
3184 .directive('b2bFileChange', ['$log', '$rootScope', function($log, $rootScope) {
3187 require: '^b2bFileLink',
3188 link: function(scope, element, attr, b2bFileLinkCtrl) {
3189 element.bind('change', changeFileModel);
3191 function changeFileModel(e) {
3192 if (e.target.files && e.target.files.length > 0) {
3193 b2bFileLinkCtrl.setFileModel(e.target.files[0]);
3194 b2bFileLinkCtrl.callbackFunction();
3196 var strFileName = e.target.value;
3198 var objFSO = new ActiveXObject("Scripting.FileSystemObject");
3199 b2bFileLinkCtrl.setFileModel(objFSO.getFile(strFileName));
3200 b2bFileLinkCtrl.callbackFunction();
3202 var errMsg = "There was an issue uploading " + strFileName + ". Please try again.";
3204 $rootScope.$broadcast('b2b-file-link-failure', errMsg);
3213 * @name Navigation.att:filters
3216 * <file src="src/filters/docs/readme.md" />
3219 * <div b2b-filters></div>
3222 * <section id="code">
3223 <b>HTML + AngularJS</b>
3224 <example module="b2b.att">
3225 <file src="src/filters/docs/demo.html" />
3226 <file src="src/filters/docs/demo.js" />
3231 angular.module('b2b.att.filters', ['b2b.att.utilities', 'b2b.att.multipurposeExpander'])
3232 .filter('filtersSelectedItemsFilter', [function () {
3233 return function (listOfItemsArray) {
3235 if (!listOfItemsArray) {
3236 listOfItemsArray = [];
3239 var returnArray = [];
3241 for (var i = 0; i < listOfItemsArray.length; i++) {
3242 for (var j = 0; j < listOfItemsArray[i].filterTypeItems.length; j++) {
3243 if (listOfItemsArray[i].filterTypeItems[j].selected && !listOfItemsArray[i].filterTypeItems[j].inProgress) {
3244 returnArray.push(listOfItemsArray[i].filterTypeItems[j]);
3254 * @name Messages, modals & alerts.att:flyout
3257 * <file src="src/flyout/docs/readme.md" />
3259 * <section id="code">
3260 <example module="b2b.att">
3261 <file src="src/flyout/docs/demo.html" />
3262 <file src="src/flyout/docs/demo.js" />
3267 angular.module('b2b.att.flyout', ['b2b.att.utilities', 'b2b.att.position'])
3268 .directive('b2bFlyout', ['$timeout', 'b2bDOMHelper', 'keymap', 'events', function ($timeout, b2bDOMHelper, keymap, events) {
3272 templateUrl: 'b2bTemplate/flyout/flyout.html',
3273 controller: ['$scope', '$element', '$attrs', function (scope, elem, attr) {
3274 scope.flyoutOpened = false;
3275 var contentScope = '';
3276 var togglerScope = '';
3277 this.registerContentScope = function (scp) {
3280 this.registerTogglerScope = function (scp) {
3284 this.toggleFlyoutState = function () {
3286 contentScope.toggleFlyout();
3289 this.getTogglerDimensions = function () {
3290 return togglerScope.getTogglerDimensions();
3292 this.setTogglerFocus = function () {
3293 return togglerScope.setTogglerFocus();
3296 this.closeFlyout = function (e) {
3297 contentScope.closeFromChild(e);
3299 this.gotFocus = function () {
3300 contentScope.gotFocus();
3303 this.updateAriaModel = function (val) {
3304 scope.flyoutOpened = val;
3307 var firstTabableElement = undefined,
3308 lastTabableElement = undefined;
3310 var firstTabableElementKeyhandler = function (e) {
3312 e.keyCode = e.which;
3314 if (e.keyCode === keymap.KEY.TAB && e.shiftKey && scope.flyoutOpened) {
3315 contentScope.gotFocus();
3316 events.preventDefault(e);
3317 events.stopPropagation(e);
3321 var lastTabableElementKeyhandler = function (e) {
3323 e.keyCode = e.which;
3325 if (e.keyCode === keymap.KEY.TAB && !e.shiftKey) {
3326 contentScope.gotFocus();
3327 events.preventDefault(e);
3328 events.stopPropagation(e);
3331 this.associateTabEvent = function(){
3332 $timeout(function () {
3333 var element = elem[0].getElementsByClassName('b2b-flyout-container')[0];
3334 firstTabableElement = b2bDOMHelper.firstTabableElement(element);
3335 lastTabableElement = b2bDOMHelper.lastTabableElement(element);
3336 if(angular.isUndefined(firstTabableElement)){
3337 angular.element(element).css('display','block');
3338 firstTabableElement = b2bDOMHelper.firstTabableElement(element);
3339 lastTabableElement = b2bDOMHelper.lastTabableElement(element);
3340 angular.element(element).css('display','none');
3342 angular.element(firstTabableElement).bind('keydown', firstTabableElementKeyhandler);
3343 angular.element(lastTabableElement).bind('keydown', lastTabableElementKeyhandler);
3346 this.updateTabbableElements = function(){
3347 $timeout(function () {
3348 var element = elem[0].getElementsByClassName('b2b-flyout-container')[0];
3349 angular.element(element).css('display','block');
3350 firstTabableElement = b2bDOMHelper.firstTabableElement(element);
3351 lastTabableElement = b2bDOMHelper.lastTabableElement(element);
3352 angular.element(firstTabableElement).bind('keydown', firstTabableElementKeyhandler);
3353 angular.element(lastTabableElement).bind('keydown', lastTabableElementKeyhandler);
3354 angular.element(element).css('display','none');
3357 this.unbindTabbaleEvents = function(){
3358 if(angular.isDefined(firstTabableElement)){
3359 angular.element(firstTabableElement).unbind('keydown', firstTabableElementKeyhandler);
3362 if(angular.isDefined(lastTabableElement)){
3363 angular.element(lastTabableElement).unbind('keydown', lastTabableElementKeyhandler);
3367 link: function (scope, element, attrs, ctrl) {
3372 .directive('b2bFlyoutToggler', [function () {
3375 require: '^b2bFlyout',
3376 link: function (scope, element, attrs, ctrl) {
3377 element.bind('click', function (e) {
3378 ctrl.toggleFlyoutState();
3381 scope.getTogglerDimensions = function () {
3382 return element[0].getBoundingClientRect();
3385 scope.setTogglerFocus = function () {
3389 ctrl.registerTogglerScope(scope);
3393 .directive('b2bFlyoutContent', ['$position', '$timeout', '$documentBind', '$isElement', '$document', function ($position, $timeout, $documentBind, $isElement, $document) {
3398 require: '^b2bFlyout',
3400 horizontalPlacement: '@',
3401 verticalPlacement: '@',
3404 contentUpdated: "=?"
3406 templateUrl: 'b2bTemplate/flyout/flyoutContent.html',
3407 link: function (scope, element, attrs, ctrl) {
3408 var flyoutStyleArray, eachCssProperty, cssPropertyKey, cssPropertyVal, temp;
3409 scope.openFlyout = false;
3410 if (!scope.horizontalPlacement) {
3411 scope.horizontalPlacement = 'center';
3413 if (!scope.verticalPlacement) {
3414 scope.verticalPlacement = 'below';
3417 scope.toggleFlyout = function () {
3419 scope.openFlyout = !scope.openFlyout;
3421 if (scope.openFlyout) {
3423 if (angular.isDefined(scope.flyoutStyle) && scope.flyoutStyle != "") {
3424 flyoutStyleArray = scope.flyoutStyle.split(";");
3425 for (i = 0; i < flyoutStyleArray.length; i++) {
3426 eachCssProperty = flyoutStyleArray[i].split(":");
3427 if (eachCssProperty.length == 2) {
3428 cssPropertyKey = eachCssProperty[0].trim();
3429 cssPropertyVal = eachCssProperty[1].trim();
3430 angular.element(element[0])[0].style[cssPropertyKey] = cssPropertyVal;
3435 angular.element(element[0]).css({
3440 var flyoutIcons = angular.element(document.querySelectorAll(".b2b-flyout-icon"));
3441 angular.forEach(flyoutIcons, function (elm) {
3442 angular.element(elm)[0].blur();
3445 $timeout(function () {
3446 ctrl.setTogglerFocus();
3448 var togglerDimensions = ctrl.getTogglerDimensions();
3449 var flyoutDimensions = element[0].getBoundingClientRect();
3451 switch (scope.horizontalPlacement) {
3453 angular.element(element[0]).css({
3454 'left': ((togglerDimensions.width / 2) - 26) + 'px'
3458 angular.element(element[0]).css({
3459 'right': ((togglerDimensions.width / 2) - 23) + 'px'
3464 var marginLeft = 10-(flyoutDimensions.width)-20;
3465 angular.element(element[0]).css({
3466 'margin-left': marginLeft + 'px'
3470 angular.element(element[0]).css({
3471 'left': ((togglerDimensions.width + 9 )) + 'px'
3476 var marginLeft = (togglerDimensions.width / 2) - (flyoutDimensions.width / 2) - 8;
3477 angular.element(element[0]).css({
3478 'margin-left': marginLeft + 'px'
3482 switch (scope.verticalPlacement) {
3484 angular.element(element[0]).css({
3485 'top': -(flyoutDimensions.height + 13) + 'px'
3489 angular.element(element[0]).css({
3490 'top': -((togglerDimensions.height-13))+ 'px'
3494 angular.element(element[0]).css({
3495 'top': -(flyoutDimensions.height - 23)+ 'px'
3499 angular.element(element[0]).css({
3500 'top': (togglerDimensions.height + 13) + 'px'
3504 angular.element(element[0]).css({
3513 scope.gotFocus = function () {
3514 scope.openFlyout = false;
3516 ctrl.setTogglerFocus();
3520 scope.closeFromChild = function (e) {
3521 scope.openFlyout = false;
3523 ctrl.setTogglerFocus();
3527 scope.hideFlyout = function () {
3528 angular.element(element[0]).css({
3534 scope.closeFlyout = function (e) {
3535 var isElement = $isElement(angular.element(e.target), element, $document);
3536 if ((e.type === "keydown" && e.which === 27) || ((e.type === "click" || e.type==="touchend") && !isElement)) {
3537 scope.openFlyout = false;
3539 ctrl.setTogglerFocus();
3544 scope.$watch('openFlyout', function () {
3545 ctrl.updateAriaModel(scope.openFlyout);
3548 $documentBind.click('openFlyout', scope.closeFlyout, scope);
3549 $documentBind.event('keydown', 'openFlyout', scope.closeFlyout, scope);
3550 $documentBind.event('touchend', 'openFlyout', scope.closeFlyout, scope);
3551 ctrl.registerContentScope(scope);
3553 if (angular.isDefined(scope.contentUpdated) && scope.contentUpdated !== null) {
3554 scope.$watch('contentUpdated', function (newVal, oldVal) {
3556 if (newVal !== oldVal) {
3557 ctrl.unbindTabbaleEvents();
3558 ctrl.associateTabEvent();
3560 scope.contentUpdated = false;
3568 .directive('b2bCloseFlyout', [function () {
3571 require: '^b2bFlyout',
3575 link: function (scope, element, attrs, ctrl) {
3576 element.bind('click touchstart', function (e) {
3577 scope.closeFlyout(e);
3578 ctrl.closeFlyout(e);
3583 .directive('b2bFlyoutTrapFocusInside', [function () {
3587 require: '^b2bFlyout',
3588 link: function (scope, elem, attr, ctrl) {
3589 /* Before opening modal, find the focused element */
3590 ctrl.updateTabbableElements();
3596 * @name Layouts.att:footer
3599 * <file src="src/footer/docs/readme.md" />
3603 <footer class="b2b-footer-wrapper" role="contentinfo" aria-label="footer">
3604 <div class="b2b-footer-container" b2b-column-switch-footer footer-link-items='footerItems'>
3606 <div class="divider-bottom-footer">
3607 <div class="span2 dispalyInline"> </div>
3608 <div class="span6 dispalyInline">
3609 <ul class="footer-nav-content">
3610 <li><a href="Terms_of_use.html" title="Terms of use" id="foot0">Terms of use</a>|</li>
3611 <li><a href="Privacy_policy.html" title="Privacy policy" id="foot1" class="active">Privacy policy</a>|</li>
3612 <li><a href="Tollfree_directory_assistance.html" title="Tollfree directory assistance" id="foot2">Tollfree directory assistance</a>|</li>
3613 <li><a href="compliance.html" title="Accessibility" id="foot3">Accessibility</a></li>
3616 <p><a href="//www.att.com/gen/privacy-policy?pid=2587" target="_blank">© <span class="copyright">2016</span> AT&T Intellectual Property</a>. All rights reserved. AT&T,the AT&T Globe logo and all other AT&T marks contained herein are tardemarks of AT&T intellectual property and/or AT&T affiliated companines.
3620 <div class="span3 footerLogo dispalyInline">
3621 <a href="index.html" class="footer-logo">
3622 <i class="icon-primary-att-globe"><span class="hidden-spoken">A T & T</span></i>
3623 <h2 class="logo-title">AT&T</h2>
3632 * <section id="code">
3633 <example module="b2b.att">
3634 <file src="src/footer/docs/demo.html" />
3635 <file src="src/footer/docs/demo.js" />
3640 angular.module('b2b.att.footer', ['b2b.att.utilities']).
3641 directive('b2bColumnSwitchFooter', [function() {
3646 footerLinkItems: "="
3648 templateUrl: 'b2bTemplate/footer/footer_column_switch_tpl.html',
3649 link: function(scope) {
3650 var tempFooterColumns = scope.footerLinkItems.length;
3651 scope.footerColumns = 3;
3652 if ( (tempFooterColumns === 5) || (tempFooterColumns === 4) ) {
3653 scope.footerColumns = tempFooterColumns;
3664 * @name Layouts.att:header
3667 * <file src="src/header/docs/readme.md" />
3670 * <li b2b-header-menu class="header__item b2b-headermenu" ng-repeat="item in tabItems" role="presentation">
3671 <a href="#" class="menu__item" role="menuitem">{{item.title}}</a>
3672 <div class="header-secondary-wrapper">
3673 <ul class="header-secondary" role="menu">
3674 <li class="header-subitem" b2b-header-submenu ng-repeat="i in item.subitems" role="presentation">
3675 <a href="#" class="menu__item" aria-haspopup="true" role="menuitem">{{i.value}}</a>
3676 <div class="header-tertiary-wrapper" ng-if="i.links">
3677 <ul class="header-tertiary" role="menu">
3678 <li b2b-header-tertiarymenu ng-repeat="link in i.links" role="presentation">
3679 <label>{{link.title}}</label>
3680 <div b2b-tertiary-link ng-repeat="title in link.value">
3681 <a href="{{link.href}}" class="header-tertiaryitem" ng-if="!title.subitems" aria-haspopup="false" role="menuitem"><span class="b2b-label-hide">{{link.title}}</span>{{title.title}}</a>
3682 <a href="{{link.href}}" class="header-tertiaryitem" b2b-header-togglemenu ng-if="title.subitems" aria-haspopup="true" role="menuitem"><span class="b2b-label-hide">{{link.title}}</span>{{title.title}}</a>
3683 <ul class="header-quarternary" role="menu" ng-if="title.subitems">
3684 <li b2b-header-quarternarymenu role="presentation">
3685 <a href="{{nav.href}}" ng-repeat="nav in title.subitems" role="menuitem" aria-haspopup="true">{{nav.title}}</a>
3698 * <section id="code">
3699 <example module="b2b.att.header">
3700 <file src="src/header/docs/demo.html" />
3701 <file src="src/header/docs/demo.js" />
3706 angular.module('b2b.att.header', ['b2b.att.dropdowns','b2b.att.utilities'])
3707 .directive('b2bHeaderMenu', ['keymap', '$documentBind', '$timeout', '$isElement', '$document', function (keymap, $documentBind, $timeout, $isElement, $document) {
3710 controller:['$scope',function($scope){
3711 this.nextSiblingFocus = function (elObj,flag) {
3712 if (elObj.nextElementSibling) {
3714 var nextmenuItem = this.getFirstElement(elObj.nextElementSibling,'a');
3715 nextmenuItem.focus();
3717 elObj.nextElementSibling.focus();
3722 this.previousSiblingFocus = function (elObj,flag) {
3723 if (elObj.previousElementSibling) {
3725 var prevmenuItem = this.getFirstElement(elObj.previousElementSibling,'a');
3726 prevmenuItem.focus();
3728 elObj.previousElementSibling.focus();
3733 this.getFirstElement = function(elmObj,selector){
3734 return elmObj.querySelector(selector);
3737 link: function (scope, elem,attr,ctrl) {
3738 scope.showMenu = false;
3739 var activeElm, subMenu, tertiaryMenu, el= angular.element(elem)[0],
3740 menuItem = angular.element(elem[0].children[0]);
3741 menuItem.bind('click', function () {
3742 elem.parent().children().removeClass('active');
3743 elem.addClass('active');
3744 var elems= this.parentElement.parentElement.querySelectorAll('li[b2b-header-menu]>a');
3745 for (var i=0; i<elems.length; i++) {
3746 elems[i].setAttribute("aria-expanded",false);
3748 scope.showMenu = true;
3749 var elmTofocus = ctrl.getFirstElement(this.parentElement,'li[b2b-header-submenu]');
3750 elmTofocus.firstElementChild.focus();
3751 this.setAttribute('aria-expanded',true);
3755 elem.bind('keydown', function (evt) {
3756 activeElm = document.activeElement;
3757 subMenu = ctrl.getFirstElement(activeElm.parentElement,'li[b2b-header-submenu]');
3758 tertiaryMenu = ctrl.getFirstElement(activeElm.parentElement,'li[b2b-header-tertiarymenu]');
3759 switch (evt.keyCode) {
3760 case keymap.KEY.ENTER:
3761 case keymap.KEY.SPACE:
3765 evt.stopPropagation();
3766 evt.preventDefault();
3767 if (activeElm.parentElement.hasAttribute('b2b-header-submenu')) {
3768 menuItem[0].focus();
3771 case keymap.KEY.DOWN:
3772 evt.stopPropagation();
3773 evt.preventDefault();
3775 subMenu.firstElementChild.focus();
3776 } else if (tertiaryMenu) {
3777 var firstSubitem = ctrl.getFirstElement(tertiaryMenu,'a.header-tertiaryitem');
3778 firstSubitem.focus();
3781 case keymap.KEY.RIGHT:
3782 evt.stopPropagation();
3783 evt.preventDefault();
3784 if (activeElm.parentElement.hasAttribute('b2b-header-submenu')) {
3785 var elm = angular.element(activeElm.parentElement)[0];
3786 ctrl.nextSiblingFocus(elm,true);
3787 } else if (activeElm.parentElement.parentElement.hasAttribute('b2b-header-tertiarymenu')) {
3788 var tertiaryLI = angular.element(activeElm.parentElement.parentElement)[0];
3789 if (tertiaryLI.nextElementSibling) {
3790 var nextElm = ctrl.getFirstElement(tertiaryLI.nextElementSibling,"a.header-tertiaryitem");
3794 else if(activeElm.parentElement.hasAttribute('b2b-header-menu')){
3795 ctrl.nextSiblingFocus(el,true);
3798 case keymap.KEY.LEFT:
3799 evt.stopPropagation();
3800 evt.preventDefault();
3801 if (activeElm.parentElement.hasAttribute('b2b-header-submenu')) {
3802 var previousElm = angular.element(activeElm.parentElement)[0];
3803 ctrl.previousSiblingFocus(previousElm,true);
3804 } else if (activeElm.parentElement.parentElement.hasAttribute('b2b-header-tertiarymenu')) {
3805 var tertiaryLI = angular.element(activeElm.parentElement.parentElement)[0];
3806 if (tertiaryLI.previousElementSibling) {
3807 var prevElm = ctrl.getFirstElement(tertiaryLI.previousElementSibling,"a.header-tertiaryitem");
3811 else if(activeElm.parentElement.hasAttribute('b2b-header-menu')) {
3812 ctrl.previousSiblingFocus(el,true);
3815 case keymap.KEY.ESC:
3816 evt.stopPropagation();
3817 evt.preventDefault();
3818 scope.showMenu = false;
3819 elem.removeClass('active');
3820 menuItem.attr('aria-expanded',false);
3821 $timeout(function(){
3822 menuItem[0].focus();
3830 var outsideClick = function (e) {
3831 var isElement = $isElement(angular.element(e.target), elem, $document);
3833 scope.showMenu = false;
3834 elem.removeClass('active');
3838 $documentBind.click('showMenu', outsideClick, scope);
3841 }]).directive('b2bHeaderSubmenu', ['$timeout',function ($timeout) {
3844 link: function (scope, elem) {
3845 var caretSign = angular.element("<i class='menuCaret'></i>");
3846 $timeout(function(){
3847 var menuItem = angular.element(elem[0].children[0]);
3848 menuItem.bind('focus mouseenter', function () {
3849 elem.parent().children().removeClass('active');
3850 elem.addClass('active');
3851 if(elem[0].childElementCount > 1){ // > 1 has third level menu
3852 menuItem.attr('aria-expanded',true);
3853 menuItem.attr('aria-haspopup',true);
3855 var caretLeft = (elem[0].offsetLeft + elem[0].offsetWidth/2) - 10;
3856 caretSign.css({left: caretLeft + 'px'});
3857 angular.element(caretSign);
3858 var tertiaryItems = elem[0].querySelectorAll('[b2b-header-tertiarymenu]');
3859 if(tertiaryItems.length >=1){
3860 elem.append(caretSign);
3863 menuItem.bind('blur', function () {
3864 $timeout(function () {
3865 var parentElm = document.activeElement.parentElement.parentElement;
3867 if (!(parentElm.hasAttribute('b2b-header-tertiarymenu'))) {
3868 elem.removeClass('active');
3869 if(elem[0].childElementCount > 1){ // > 1 has third level menu
3870 menuItem.attr('aria-expanded',false);
3872 var caret = elem[0].querySelector('.menuCaret');
3883 }]).directive('b2bHeaderTertiarymenu', ['$timeout','keymap', function ($timeout,keymap){
3886 require:'^b2bHeaderMenu',
3887 link: function (scope, elem,attr,ctrl) {
3889 elem.bind('keydown', function (evt) {
3890 var activeElm = document.activeElement;
3891 var activeParentElm = activeElm.parentElement;
3892 var activeParentObj = angular.element(activeParentElm)[0];
3894 if(activeParentElm.hasAttribute('b2b-tertiary-link')){
3895 var quarterNav = angular.element(activeParentElm)[0].querySelector('li[b2b-header-quarternarymenu]');
3897 var links = ctrl.getFirstElement(angular.element(quarterNav)[0],'a');
3900 var tertiaryMenu = activeElm.parentElement.parentElement.parentElement;
3901 var tertiaryMenuFlag = tertiaryMenu.hasAttribute('b2b-tertiary-link');
3903 switch (evt.keyCode) {
3904 case keymap.KEY.DOWN:
3905 evt.stopPropagation();
3906 evt.preventDefault();
3907 if (activeParentElm.hasAttribute('b2b-tertiary-link')) {
3908 if(angular.element(quarterNav).hasClass('active')){
3910 }else if(activeParentObj.nextElementSibling){
3911 ctrl.nextSiblingFocus(activeParentObj,true);
3914 else if(angular.element(activeParentElm).hasClass('active')){
3915 ctrl.nextSiblingFocus(activeElm);
3919 evt.stopPropagation();
3920 evt.preventDefault();
3921 if(activeParentElm.hasAttribute('b2b-tertiary-link')){
3922 if(activeParentObj.previousElementSibling.hasAttribute('b2b-tertiary-link')){
3923 ctrl.previousSiblingFocus(activeParentObj,true);
3925 var elm = angular.element(activeElm.parentElement.parentElement.parentElement.parentElement.parentElement)[0];
3926 ctrl.getFirstElement(elm,"a").focus();
3928 }else if(angular.element(activeParentElm).hasClass('active')){
3929 if (activeElm.previousElementSibling) {
3930 ctrl.previousSiblingFocus(activeElm);
3931 }else if (tertiaryMenuFlag) {
3932 var elm = angular.element(tertiaryMenu)[0];
3933 ctrl.getFirstElement(elm,"a.header-tertiaryitem").focus();
3943 }]).directive('b2bHeaderTogglemenu', ['$timeout', 'keymap', function ($timeout, keymap) {
3946 require: '^b2bHeaderMenu',
3947 link: function (scope, elem, attrs, ctrl) {
3949 $timeout(function () {
3950 quarterNav = angular.element(elem.parent())[0].querySelector('li[b2b-header-quarternarymenu]');
3951 elem.bind('click', function () {
3952 angular.element(quarterNav).toggleClass('active');
3957 }]).directive('b2bHeaderResponsive', ['$timeout',function ($timeout) {
3960 controller: function($scope){
3961 this.applyMediaQueries = function(value){
3962 document.querySelector('style').textContent +=
3963 "@media screen and (max-width:950px) { \
3964 .header__item.profile { right: " + value + "px; } \
3967 this.arrangeResponsiveHeader = function(children){
3969 * clientWidth of 1090 === max-width of 1100px
3970 * clientWidth of 920 === max-width of 950px
3971 * see b2b-angular.css for rest of responsive header CSS
3973 if (document.documentElement.clientWidth <= 920) {
3976 this.applyMediaQueries(200);
3979 this.applyMediaQueries(200);
3981 default: // anthing above 3, however, should not have more than 3 to date
3982 this.applyMediaQueries(200);
3987 link: function (scope, elem, attrs, ctrl) {
3992 $timeout(function(){
3993 profile = document.querySelector('li.header__item.profile');
3994 children = angular.element(profile).children().length;
3996 ctrl.arrangeResponsiveHeader(children); // shift right-side icon flyovers
4000 window.addEventListener('resize', function(event){ // caret adjustmet
4001 var activeSubmenu = elem[0].querySelector('[b2b-header-menu] [b2b-header-submenu].active');
4002 var activeSubmenuEl = angular.element(activeSubmenu);
4004 var caretSign = activeSubmenu.querySelector('i.menuCaret');
4006 var caretSignEl = angular.element(caretSign);
4007 var caretLeft = (activeSubmenu.offsetLeft + activeSubmenu.offsetWidth/2) - 10;
4008 caretSignEl.css({left: caretLeft + 'px'});
4012 ctrl.arrangeResponsiveHeader(children); // shift right-side icon flyovers
4020 * @name Layouts.att:headings & copy
4023 * <file src="src/headingsAndCopy/docs/readme.md" />
4027 <b>HTML + AngularJS</b>
4028 <example module="b2b.att">
4029 <file src="src/headingsAndCopy/docs/demo.html" />
4034 var b2bLegalCopy = angular.module('b2b.att.headingsAndCopy', []);
4037 * @name Tabs, tables & accordions.att:horizontalTable
4040 * <file src="src/horizontalTable/docs/readme.md" />
4043 * @param {int} sticky - Number of sticky columns to have. Maximum of 3.
4044 * @param {boolean} refresh - A boolean that when set to true will force a re-render of table. Only use when using 'bulk mode'
4045 * @param {string} legendContent - A string of html to fill in the legend flyout. This should generally be a <ul> with <li> and should not rely on Angular for repeating.
4046 * @param {boolean} retainColumnSet - A boolean that on re-render of the table, determines if the columns visible should reset to 0 or not. Default is false.
4048 * <section id="code">
4049 <example module="b2b.att">
4050 <file src="src/horizontalTable/docs/demo.html" />
4051 <file src="src/horizontalTable/docs/demo.js" />
4056 angular.module('b2b.att.horizontalTable', [])
4057 .constant('b2bHorizontalTableConfig', {
4058 'maxStickyColumns': 3
4060 .directive('b2bHorizontalTable', ['$timeout', 'b2bHorizontalTableConfig', 'b2bDOMHelper', function ($timeout, b2bHorizontalTableConfig, b2bDOMHelper) {
4066 numOfStickyCols: '=?sticky',
4068 legendContent: '=?',
4069 retainColumnSet: '=?'
4072 templateUrl: 'b2bTemplate/horizontalTable/horizontalTable.html',
4073 link: function (scope, element, attrs, ctrl) {
4074 scope.numOfStickyCols = scope.numOfStickyCols || 1;
4075 scope.viewportIndex = scope.numOfStickyCols;
4076 scope.countDisplayText = "";
4077 var tableElement = element.find('table');
4078 var thElements = element.find('th');
4079 var innerContainer = angular.element(element[0].querySelector('.b2b-horizontal-table-inner-container'));
4080 var outerContainer = angular.element(element[0].querySelector('.b2b-horizontal-table'));
4082 var tableColumns = [];
4083 var tableRows = element.find('tr');
4087 var totalWidth = element.children()[0].offsetWidth;
4088 var lastVisibleColumn = 0;
4089 var collectiveColumnWidth = [];
4090 var collectiveRowHeight = [];
4091 var columnSets = [];
4093 var stickyPixels = 0;
4095 var displayNoneCSS = {'display': 'none'};
4096 var displayBlockCSS = {'display': 'table-cell'};
4098 var init = function() {
4099 // Reset this from a previous execution
4101 collectiveColumnWidth = [];
4102 collectiveRowHeight = [];
4105 lastVisibleColumn = 0;
4107 if ((!!scope.retainColumnSet)) {
4110 visibleColumns = [];
4113 tableElement = element.find('table');
4114 thElements = element.find('th');
4115 innerContainer = angular.element(element[0].querySelector('.b2b-horizontal-table-inner-container'));
4116 outerContainer = angular.element(element[0].querySelector('.b2b-horizontal-table'));
4117 totalWidth = element.children()[0].offsetWidth;
4118 tableRows = element.find('tr');
4120 scope.disableLeft = true;
4121 scope.disableRight = false;
4123 if (scope.numOfStickyCols > b2bHorizontalTableConfig.maxStickyColumns) {
4124 throw new Error('Table can only support ' + b2bHorizontalTableConfig.maxStickyColumns + ' sticky columns.');
4126 scope.countDisplayText = "";
4127 for(var count = 1; count <= scope.numOfStickyCols; count++) {
4128 scope.countDisplayText = scope.countDisplayText + count + ", "
4130 angular.forEach(tableRows, function(row, rowIndex) {
4131 for(var j = 0; j < row.children.length; j++) {
4132 if (tableColumns[j] === undefined) {
4133 tableColumns[j] = [];
4135 tableColumns[j].push(row.children[j]);
4139 // We need to reset all the displayNones from previous runs, if applicable
4140 if (attrs.refresh !== undefined && attrs.refresh !== '') {
4141 for (var i = scope.numOfStickyCols+1; i < tableColumns.length; i++) {
4142 angular.element(tableColumns[i]).css(displayBlockCSS);
4146 // We must calculate here as we need cells to be reset after re-render.
4147 angular.forEach(tableRows, function(row, rowIndex) {
4148 collectiveRowHeight.push(findMax(row.children, 'height')); // BUG: Keeping this here now causes row height bugs
4152 for (var i = 0; i < tableColumns.length; i++) {
4153 collectiveColumnWidth.push(findMax(tableColumns[i], 'width')); //offsetWidth doesn't take into account custom css inside
4155 for(var i = 0; i < scope.numOfStickyCols; i++) {
4156 maxWidth += collectiveColumnWidth[i];
4159 // BUG: The code I put in to fix the table not spanning 100% width is now preventing
4160 // table cells from laying out more than stickyPixels and thus we have weird wrapping
4161 stickyPixels = totalWidth-maxWidth;
4163 // At this point, for each tr, I need to set the properties (height) and each numOfStickyCols children
4164 // should be set with sticky properties (margin-left and width)
4165 var width = maxWidth;
4168 if (angular.element(document).find('html').hasClass('isIE')) {
4171 var thObject = undefined;
4172 for(var i = 0; i < scope.numOfStickyCols; i++) {
4173 for (var j = 0; j < tableRows.length; j++) {
4174 thObject = angular.element(tableRows[j].children[i]);
4175 angular.element(thObject).css({
4176 'margin-left': -(width + 2) + 'px',
4177 'width': (collectiveColumnWidth[i] + 3) + 'px', // instead of taking the max width, grab max width for that column
4178 'height': (collectiveRowHeight[j] + additive) + 'px',
4179 'position': 'absolute',
4180 'background-color': '#F2F2F2'
4183 width -= collectiveColumnWidth[i];
4185 angular.element(tableRows[0]).css('height', collectiveRowHeight[0] + 'px');
4186 for(var i = 0; i < tableRows.length; i++) {
4187 angular.element(tableRows[i]).css('height', (collectiveRowHeight[i] + additive) + 'px');
4190 innerContainer.css({
4191 'padding-left': (maxWidth + 2) + 'px'
4195 // Let's precompute all the (set) combinations beforehand
4197 for (var i = scope.numOfStickyCols; i < tableColumns.length;) {
4198 visibleColumns = calculateVisibleColumns(i);
4199 if(visibleColumns === tableColumns.length){
4200 columnSets.push([i, visibleColumns-1]);
4202 columnSets.push([i, visibleColumns]);
4204 i = visibleColumns + 1;
4207 //columnSets = [[1, 1], [2,7]];
4209 updateCellDisplay(columnSets[setIndex]);
4210 checkScrollArrows();
4212 scope.numOfCols = tableColumns.length;
4216 // JM520E: This is a temporary hack until I solve the ngRepeat issue
4218 if (element.find('th').length < scope.numOfStickyCols) {
4219 // DOM ngRepeat is not ready, let's check back in 10 ms
4220 $timeout(hack, 10, false);
4222 if (scope.refresh !== undefined) {
4223 scope.$watch('refresh', function(oldVal, newVal) { // this watch calls too many times
4224 if (!angular.equals(oldVal, newVal)) { //hackFinished && oldVal < newVal
4225 // From testing it takes about 30 ms before ngRepeat executes, so let's set initial timeout
4226 // NOTE: May need to expose timeout to developers. Application is known to have digest cycle of 3-5k watches.
4227 $timeout(init, 100, false);
4228 scope.refresh = false;
4240 // Let's get started with some math!
4243 function calculateVisibleColumns(startingPoint) {
4245 visibleColumns = startingPoint || scope.numOfStickyCols;
4247 while(usedWidth < stickyPixels && visibleColumns < collectiveColumnWidth.length) {
4248 if (usedWidth+collectiveColumnWidth[visibleColumns] > stickyPixels) {
4249 if (startingPoint === visibleColumns) {
4250 return visibleColumns; // The next cell is too large to fit, it should be only thing to fit
4253 return visibleColumns;
4255 usedWidth += collectiveColumnWidth[visibleColumns];
4259 if (usedWidth > stickyPixels) {
4260 return --visibleColumns;
4262 return visibleColumns;
4265 function updateCellDisplay(set) {
4266 for (var i = scope.numOfStickyCols; i < tableColumns.length; i++) {
4267 angular.element(tableColumns[i]).css(displayNoneCSS);
4270 for (var i = set[0]; i <= set[1]; i++) {
4271 angular.element(tableColumns[i]).css(displayBlockCSS);
4275 function findMax(arr, prop) {
4280 for (var i = 0; i < arr.length; i++) {
4282 prevDisplay = angular.element(item).css('display');
4284 if (scope.$$phase) {
4287 // Remove inline styles, they will mess up calculations from original run
4288 angular.element(item).css('height', '');
4289 angular.element(item).css('width', '');
4290 if (prop === 'width') {
4291 // If we do not undo previous run's inline styles, this will grow widths on each re-render.
4292 localVal = Math.ceil(parseInt(window.getComputedStyle(item).width.split('px')[0], 10)) + 30; // 30 px is padding
4293 } else if (prop === 'offsetWidth') {
4294 localVal = item.offsetWidth;
4295 } else if (prop === 'height') {
4296 //localVal = item.offsetHeight;
4297 localVal = Math.ceil(parseInt(window.getComputedStyle(item).height.split('px')[0], 10))
4300 if (localVal >= max) {
4308 function checkScrollArrows() {
4309 scope.disableLeft = (setIndex === 0);
4310 scope.disableRight = !(setIndex < columnSets.length-1);
4313 scope.moveViewportLeft = function () {
4315 updateCellDisplay(columnSets[setIndex]);
4316 checkScrollArrows();
4318 if (scope.disableLeft) {
4319 element[0].querySelector('.b2b-horizontal-table-column-info').focus();
4323 scope.moveViewportRight = function () {
4325 updateCellDisplay(columnSets[setIndex]);
4326 checkScrollArrows();
4328 if (scope.disableRight) {
4329 element[0].querySelector('.b2b-horizontal-table-column-info').focus();
4333 scope.getColumnSet = function () {
4334 return columnSets[setIndex];
4337 innerContainer.bind('scroll', function () {
4338 $timeout(function () {
4339 checkScrollArrows();
4348 * @name Forms.att:hourPicker
4351 * <file src="src/hourPicker/docs/readme.md" />
4354 * <div b2b-hourpicker ng-model="hourpickerValue.value"></div>
4357 * <section id="code">
4358 <example module="b2b.att">
4359 <file src="src/hourPicker/docs/demo.html" />
4360 <file src="src/hourPicker/docs/demo.js" />
4365 angular.module('b2b.att.hourPicker', ['b2b.att.utilities'])
4367 .constant('b2bHourpickerConfig', {
4404 startTimeOptions: ['1:00', '2:00', '3:00', '4:00', '5:00', '6:00', '7:00', '8:00', '9:00', '10:00', '11:00', '12:00'],
4405 startTimeDefaultOptionIndex: -1,
4406 startTimeDefaultMeridiem: "am",
4407 endTimeOptions: ['1:00', '2:00', '3:00', '4:00', '5:00', '6:00', '7:00', '8:00', '9:00', '10:00', '11:00', '12:00'],
4408 endTimeDefaultOptionIndex: -1,
4409 endTimeDefaultMeridiem: "pm",
4413 .factory('b2bNormalizeHourpickerValues', [function () {
4414 var _normalize = function (hourpickerValues) {
4415 if (angular.isDefined(hourpickerValues) && hourpickerValues != null) {
4416 var finalHourpickerValues = [];
4417 var hourpickerValue = {};
4419 for (var i = 0; i < hourpickerValues.length; i++) {
4420 days = hourpickerValues[i].days ? hourpickerValues[i].days : {};
4421 hourpickerValue.startTime = hourpickerValues[i].startTime ? hourpickerValues[i].startTime : '';
4422 hourpickerValue.startMeridiem = hourpickerValues[i].startMeridiem ? hourpickerValues[i].startMeridiem : '';
4423 hourpickerValue.endTime = hourpickerValues[i].endTime ? hourpickerValues[i].endTime : '';
4424 hourpickerValue.endMeridiem = hourpickerValues[i].endMeridiem ? hourpickerValues[i].endMeridiem : '';
4425 hourpickerValue.days = [];
4427 var retrieveDaysText = function (daysDetails) {
4432 for (var i in days) {
4433 if (days[i].value) {
4438 first = daysTexts[0];
4439 last = daysTexts[0];
4441 hourpickerValue.days[index] = days[first].caption;
4442 if (daysTexts.length > 1) {
4443 for (var i = 1; i < daysTexts.length; i++) {
4444 if (daysTexts[i] - last === 1) {
4445 last = daysTexts[i];
4446 hourpickerValue.days[index] = days[first].caption + ' - ' + days[last].caption;
4449 first = last = daysTexts[i];
4450 hourpickerValue.days[index] = days[first].caption;
4457 finalHourpickerValues.push(angular.copy(hourpickerValue));
4460 return angular.copy(finalHourpickerValues);
4465 normalize: _normalize
4469 .directive('b2bHourpicker', ['b2bHourpickerConfig', 'b2bNormalizeHourpickerValues', function (b2bHourpickerConfig, b2bNormalizeHourpickerValues) {
4475 templateUrl: 'b2bTemplate/hourPicker/b2bHourpicker.html',
4476 controller: ['$scope', function (scope) {
4479 link: function (scope, elem, attr, ctrl) {
4480 scope.hourpicker = {};
4481 scope.hourpicker.dayOptions = attr.dayOptions ? scope.$parent.$eval(attr.dayOptions) : b2bHourpickerConfig.dayOptions;
4482 scope.hourpicker.startTimeOptions = attr.startTimeOptions ? scope.$parent.$eval(attr.startTimeOptions) : b2bHourpickerConfig.startTimeOptions;
4483 scope.hourpicker.endTimeOptions = attr.endTimeOptions ? scope.$parent.$eval(attr.endTimeOptions) : b2bHourpickerConfig.endTimeOptions;
4484 scope.hourpicker.startTimeDefaultOptionIndex = attr.startTimeDefaultOptionIndex ? scope.$parent.$eval(attr.startTimeDefaultOptionIndex) : b2bHourpickerConfig.startTimeDefaultOptionIndex;
4485 scope.hourpicker.endTimeDefaultOptionIndex = attr.endTimeDefaultOptionIndex ? scope.$parent.$eval(attr.endTimeDefaultOptionIndex) : b2bHourpickerConfig.endTimeDefaultOptionIndex;
4486 scope.hourpicker.startTimeDefaultMeridiem = attr.startTimeDefaultMeridiem ? scope.$parent.$eval(attr.startTimeDefaultMeridiem) : b2bHourpickerConfig.startTimeDefaultMeridiem;
4487 scope.hourpicker.endTimeDefaultMeridiem = attr.endTimeDefaultMeridiem ? scope.$parent.$eval(attr.endTimeDefaultMeridiem) : b2bHourpickerConfig.endTimeDefaultMeridiem;
4488 scope.hourpicker.sameDayOption = attr.sameDayOption ? scope.$parent.$eval(attr.sameDayOption) : b2bHourpickerConfig.sameDayOption;
4489 scope.hourpicker.editMode = -1;
4491 scope.hourpickerValues = [];
4492 scope.finalHourpickerValues = [];
4493 scope.addHourpickerValue = function (hourpickerPanelValue) {
4494 if (hourpickerPanelValue) {
4495 if (scope.hourpicker.editMode > -1) {
4496 scope.hourpickerValues[scope.hourpicker.editMode] = hourpickerPanelValue;
4497 scope.hourpicker.editMode = -1;
4499 scope.hourpickerValues.push(hourpickerPanelValue);
4502 scope.finalHourpickerValues = b2bNormalizeHourpickerValues.normalize(angular.copy(scope.hourpickerValues));
4503 ctrl.$setViewValue(angular.copy(scope.hourpickerValues));
4505 ctrl.$render = function () {
4506 if (angular.isDefined(ctrl.$modelValue)) {
4507 scope.hourpickerValues = angular.copy(ctrl.$modelValue);
4508 scope.finalHourpickerValues = b2bNormalizeHourpickerValues.normalize(angular.copy(scope.hourpickerValues));
4511 scope.editHourpickerValue = function (index) {
4512 scope.hourpickerPanelValue = angular.copy(scope.hourpickerValues[index]);
4513 scope.hourpicker.editMode = index;
4515 scope.deleteHourpickerValue = function (index) {
4516 scope.hourpickerValues.splice(index, 1);
4517 scope.resetHourpickerPanelValue();
4518 scope.addHourpickerValue();
4521 scope.setValidity = function (errorType, errorValue) {
4522 ctrl.$setValidity(errorType, errorValue);
4528 .directive('b2bHourpickerPanel', [function () {
4532 templateUrl: 'b2bTemplate/hourPicker/b2bHourpickerPanel.html',
4533 controller: ['$scope', function (scope) {
4536 link: function (scope, elem, attr, ctrl) {
4537 var hourpickerPanelValueTemplate = {
4540 startMeridiem: 'am',
4544 for (var i = 0; i < scope.hourpicker.dayOptions.length; i++) {
4545 hourpickerPanelValueTemplate.days[i] = {
4547 title: scope.hourpicker.dayOptions[i].title,
4548 caption: scope.hourpicker.dayOptions[i].caption
4551 scope.hourpickerPanelValue = {};
4552 scope.disableAddBtn = true;
4554 scope.$watch('hourpickerPanelValue.days', function(){
4555 for(var i in scope.hourpickerPanelValue.days)
4557 if(scope.hourpickerPanelValue.days[i].value)
4559 scope.disableAddBtn = false;
4562 scope.disableAddBtn = true;
4566 scope.resetHourpickerPanelValue = function () {
4567 scope.hourpickerPanelValue = angular.copy(hourpickerPanelValueTemplate);
4568 if (scope.hourpicker.startTimeDefaultOptionIndex > -1) {
4569 scope.hourpickerPanelValue.startTime = scope.hourpicker.startTimeOptions[scope.hourpicker.startTimeDefaultOptionIndex];
4571 if (scope.hourpicker.endTimeDefaultOptionIndex > -1) {
4572 scope.hourpickerPanelValue.endTime = scope.hourpicker.endTimeOptions[scope.hourpicker.endTimeDefaultOptionIndex];
4574 scope.hourpickerPanelValue.startMeridiem = scope.hourpicker.startTimeDefaultMeridiem;
4575 scope.hourpickerPanelValue.endMeridiem = scope.hourpicker.endTimeDefaultMeridiem;
4576 scope.hourpicker.editMode = -1;
4577 scope.setValidity('invalidHourpickerData', true);
4578 scope.setValidity('invalidHourpickerTimeRange', true);
4580 scope.resetHourpickerPanelValue();
4581 scope.updateHourpickerValue = function () {
4582 if (scope.isFormValid() && !scope.isTimeOverlap()) {
4583 scope.addHourpickerValue(angular.copy(scope.hourpickerPanelValue));
4584 scope.resetHourpickerPanelValue();
4588 scope.isFormValid = function () {
4589 var isStartTimeAvailable = scope.hourpickerPanelValue.startTime ? true : false;
4590 var isStartMeridiemAvailable = scope.hourpickerPanelValue.startMeridiem ? true : false;
4591 var isEndTimeAvailable = scope.hourpickerPanelValue.endTime ? true : false;
4592 var isEndMeridiemAvailable = scope.hourpickerPanelValue.endMeridiem ? true : false;
4593 var currentStartTime = getTime(scope.hourpickerPanelValue.startTime, scope.hourpickerPanelValue.startMeridiem);
4594 var currentEndTime = getTime(scope.hourpickerPanelValue.endTime, scope.hourpickerPanelValue.endMeridiem);
4595 var isTimeInProperSequence = currentEndTime > currentStartTime;
4596 var isDayChecked = false;
4597 for (var i in scope.hourpickerPanelValue.days) {
4598 if (scope.hourpickerPanelValue.days[i].value) {
4599 isDayChecked = true;
4604 if (isStartTimeAvailable && isStartMeridiemAvailable && isEndTimeAvailable && isEndMeridiemAvailable && isTimeInProperSequence && isDayChecked) {
4605 scope.setValidity('invalidHourpickerData', true);
4608 scope.setValidity('invalidHourpickerData', false);
4612 scope.isTimeOverlap = function () {
4613 var selectedDays = [];
4614 for (var i in scope.hourpickerPanelValue.days) {
4615 if (scope.hourpickerPanelValue.days[i].value) {
4616 selectedDays.push(i);
4620 var currentStartTime, currentEndTime, existingStartTime, existingEndTime;
4621 currentStartTime = getTime(scope.hourpickerPanelValue.startTime, scope.hourpickerPanelValue.startMeridiem);
4622 currentEndTime = getTime(scope.hourpickerPanelValue.endTime, scope.hourpickerPanelValue.endMeridiem);
4623 for (var i = 0; i < scope.hourpickerValues.length; i++) {
4625 if (i === scope.hourpicker.editMode) {
4629 for (var j = 0; j < selectedDays.length; j++) {
4630 existingStartTime = getTime(scope.hourpickerValues[i].startTime, scope.hourpickerValues[i].startMeridiem);
4631 existingEndTime = getTime(scope.hourpickerValues[i].endTime, scope.hourpickerValues[i].endMeridiem);
4632 if (scope.hourpickerValues[i].days[selectedDays[j]].value) {
4633 if(!scope.hourpicker.sameDayOption){
4634 scope.setValidity('dayAlreadySelected', false);
4636 } else if ((currentStartTime > existingStartTime && currentStartTime < existingEndTime) || (currentEndTime > existingStartTime && currentEndTime < existingEndTime)) {
4637 scope.setValidity('invalidHourpickerTimeRange', false);
4639 } else if ((existingStartTime > currentStartTime && existingStartTime < currentEndTime) || (existingEndTime > currentStartTime && existingEndTime < currentEndTime)) {
4640 scope.setValidity('invalidHourpickerTimeRange', false);
4642 } else if ((currentStartTime === existingStartTime) && (currentEndTime === existingEndTime)) {
4643 scope.setValidity('invalidHourpickerTimeRange', false);
4650 scope.setValidity('dayAlreadySelected', true);
4651 scope.setValidity('invalidHourpickerTimeRange', true);
4654 var getTime = function (timeString, meridiem) {
4655 var tempDate = new Date();
4656 if (timeString && meridiem) {
4657 var timeSplit = timeString.split(':');
4658 var hour = ((meridiem === 'PM' || meridiem === 'pm') && timeSplit[0] !== '12') ? parseInt(timeSplit[0], 10) + 12 : parseInt(timeSplit[0], 10);
4659 tempDate.setHours(hour, parseInt(timeSplit[1], 10), 0, 0);
4662 return tempDate.getTime();
4668 .directive('b2bHourpickerValue', [function () {
4672 templateUrl: 'b2bTemplate/hourPicker/b2bHourpickerValue.html',
4673 controller: ['$scope', function (scope) {
4676 link: function (scope, elem, attr, ctrl) {
4677 scope.hourpickerValue = {};
4678 scope.hourpickerValue.startTime = attr.startTime ? scope.$eval(attr.startTime) : '';
4679 scope.hourpickerValue.startMeridiem = attr.startMeridiem ? scope.$eval(attr.startMeridiem) : '';
4680 scope.hourpickerValue.endTime = attr.endTime ? scope.$eval(attr.endTime) : '';
4681 scope.hourpickerValue.endMeridiem = attr.endMeridiem ? scope.$eval(attr.endMeridiem) : '';
4682 scope.hourpickerValue.days = attr.days ? scope.$eval(attr.days).join(', ') : '';
4683 scope.hourpickerValue.index = attr.b2bHourpickerValue ? scope.$eval(attr.b2bHourpickerValue) : -1;
4689 * @name Template.att:inputTemplate
4692 * <file src="src/inputTemplate/docs/readme.md" />
4695 * <input type="text" id="fieldId" placeholder="placholder text here" class="span12 input-enhanced" name="fieldName">
4699 <b>HTML + AngularJS</b>
4700 <example module="b2b.att">
4701 <file src="src/inputTemplate/docs/demo.html" />
4702 <file src="src/inputTemplate/docs/demo.js" />
4706 angular.module('b2b.att.inputTemplate', []);
4710 * @name Navigation.att:leftNavigation
4713 * <file src="src/leftNavigation/docs/readme.md" />
4716 * <b2b-left-navigation data-menu="menuData"></b2b-left-navigation>
4719 * <section id="code">
4720 <example module="b2b.att">
4721 <file src="src/leftNavigation/docs/demo.html" />
4722 <file src="src/leftNavigation/docs/demo.js" />
4727 angular.module('b2b.att.leftNavigation', [])
4728 .directive('b2bLeftNavigation', [function () {
4731 templateUrl: 'b2bTemplate/leftNavigation/leftNavigation.html',
4735 link: function (scope, element, attrs, ctrl) {
4739 scope.toggleNav = function (val) {
4740 if (val === scope.idx) {
4746 scope.liveLink = function (evt, val1, val2) {
4747 scope.itemIdx = val1;
4748 scope.navIdx = val2;
4749 evt.stopPropagation();
4756 * @name Buttons, links & UI controls.att:links
4759 * <file src="src/links/docs/readme.md" />
4761 * <!-- See below examples for link implementation -->
4765 <b>HTML + AngularJS</b>
4766 <example module="b2b.att">
4767 <file src="src/links/docs/demo.html" />
4768 <file src="src/links/docs/demo.js" />
4772 angular.module('b2b.att.links', []);
4775 * @name Misc.att:listbox
4778 * <file src="src/listbox/docs/readme.md" />
4780 * @param {int} currentIndex - Current index of selected listbox item. Is not supported on multiselect listbox
4781 * @param {Array} listboxData - Data of listbox items. Should include full data regardless if HTML will be filtered.
4784 * <section id="code">
4785 <example module="b2b.att">
4786 <file src="src/listbox/docs/demo.html" />
4787 <file src="src/listbox/docs/demo.js" />
4792 angular.module('b2b.att.listbox', ['b2b.att.utilities'])
4793 .directive('b2bListBox', ['keymap', 'b2bDOMHelper', '$rootScope', function(keymap, b2bDOMHelper, $rootScope) {
4802 templateUrl: 'b2bTemplate/listbox/listbox.html',
4803 link: function(scope, elem, attr) {
4805 if (attr.ariaMultiselectable !== undefined || attr.ariaMultiselectable === 'true') {
4806 scope.multiselectable = true;
4808 scope.multiselectable = false;
4811 var shiftKey = false;
4813 var prevDirection = undefined; // previous direction is used for an edge case when shifting
4814 var shiftKeyPressed = false; // Used to handle shift clicking
4815 var ctrlKeyPressed = false;
4817 var currentIndexSet = {
4819 'listboxDataIndex': 0
4822 function isTrue(item) {
4823 if (item.selected === true) {
4828 function incrementIndex(elem) {
4829 $rootScope.$apply();
4831 var nextElem = elem.next();
4832 if (!angular.isDefined(nextElem) || nextElem.length === 0) {
4836 currentIndexSet.elementIndex += 1;
4837 currentIndexSet.listboxDataIndex = parseInt(nextElem.attr('data-index'), 10);
4838 scope.currentIndex = currentIndexSet.listboxDataIndex;
4840 if (currentIndexSet.elementIndex >= elements.length - 1) {
4841 currentIndexSet.elementIndex = elements.length-1;
4845 function decrementIndex(elem) {
4846 $rootScope.$apply();
4847 var prevElem = angular.element(b2bDOMHelper.previousElement(elem));
4848 if (!angular.isDefined(prevElem) || prevElem.length === 0) {
4852 currentIndexSet.elementIndex -= 1;
4853 currentIndexSet.listboxDataIndex = parseInt(prevElem.attr('data-index'), 10);
4854 scope.currentIndex = currentIndexSet.listboxDataIndex;
4856 if (currentIndexSet.elementIndex <= 0) {
4857 currentIndexSet.elementIndex = 0;
4861 var focusOnElement = function(index) {
4863 elements[index].focus();
4867 function selectItems(startIndex, endIndex, forceValue) {
4868 for (var i = startIndex; i < endIndex; i++) {
4869 if (forceValue === undefined) {
4870 // We will flip the value
4871 scope.listboxData[i].selected = !scope.listboxData[i].selected;
4873 scope.listboxData[i].selected = forceValue;
4877 if (!scope.$$phase) {
4882 elem.bind('focus', function(evt) {
4883 // If multiselectable or not and nothing is selected, put focus on first element
4884 // If multiselectable and a range is set, put focus on first element of range
4885 // If not multiselectable and something selected, put focus on element
4886 elements = elem.children();
4887 var selectedItems = scope.listboxData.filter(isTrue);
4888 var elementsIndies = Array.prototype.map.call(elements, function(item) {
4889 return parseInt(angular.element(item).attr('data-index'), 10);
4892 if (selectedItems.length == 0) {
4894 currentIndexSet.listboxDataIndex = 0;
4895 } else if (attr.ariaMultiselectable) {
4896 var index = scope.listboxData.indexOf(selectedItems[0]);
4897 var indies = elementsIndies.filter(function(item) {
4898 return (item === index);
4901 if (indies.length === 0 || indies[0] != index) {
4903 currentIndexSet.elementIndex = elementsIndies[0];
4904 currentIndexSet.listboxDataIndex = 0;
4905 focusOnElement(currentIndexSet.elementIndex);
4907 focusOnElement(indies[0]);
4908 currentIndexSet.elementIndex = indies[0];
4909 currentIndexSet.listboxDataIndex = index;
4912 focusOnElement(currentIndexSet.elementIndex);
4914 scope.currentIndex = currentIndexSet.listboxDataIndex;
4916 if (!scope.$$phase) {
4920 elem.bind('keyup', function(evt) {
4921 if (evt.keyCode === keymap.KEY.SHIFT) {
4922 shiftKeyPressed = false;
4923 } else if (evt.keyCode === keymap.KEY.CTRL) {
4924 ctrlKeyPressed = false;
4928 elem.bind('keydown', function(evt) {
4929 var keyCode = evt.keyCode;
4930 elements = elem.children();
4931 if (keyCode === keymap.KEY.SHIFT) {
4932 shiftKeyPressed = true;
4933 } else if (evt.keyCode === keymap.KEY.CTRL) {
4934 ctrlKeyPressed = true;
4940 if (scope.multiselectable && evt.ctrlKey) {
4941 var arr = scope.listboxData.filter(isTrue);
4942 var elementsIndies = Array.prototype.map.call(elements, function(item) {
4943 return parseInt(angular.element(item).attr('data-index'), 10);
4945 var val = !(arr.length === scope.listboxData.length);
4946 for (var i = 0; i < elementsIndies.length; i++) {
4947 scope.listboxData[elementsIndies[i]].selected = val;
4950 if (!scope.$$phase) {
4954 evt.preventDefault();
4955 evt.stopPropagation();
4959 case keymap.KEY.END:
4961 if (scope.multiselectable && evt.ctrlKey && evt.shiftKey) {
4962 var elementsIndies = Array.prototype.map.call(elements, function(item) {
4963 return parseInt(angular.element(item).attr('data-index'), 10);
4964 }).filter(function(item) {
4965 return (item >= currentIndexSet.listboxDataIndex);
4967 for (var i = 0; i < elementsIndies.length; i++) {
4968 scope.listboxData[elementsIndies[i]].selected = true;
4970 evt.preventDefault();
4971 evt.stopPropagation();
4973 if (!scope.$$phase) {
4979 case keymap.KEY.HOME:
4981 if (scope.multiselectable && evt.ctrlKey && evt.shiftKey) {
4982 selectItems(0, currentIndexSet.listboxDataIndex+1, true); // currentIndex+1 is what is being focused on
4983 evt.preventDefault();
4984 evt.stopPropagation();
4988 case keymap.KEY.LEFT:
4991 if (currentIndexSet.listboxDataIndex === 0) {
4992 evt.preventDefault();
4993 evt.stopPropagation();
4997 decrementIndex(elements.eq(currentIndexSet.elementIndex));
4998 if (scope.multiselectable && (evt.shiftKey || evt.ctrlKey)) {
5000 if (prevDirection === 'DOWN') {
5001 scope.listboxData[currentIndexSet.listboxDataIndex+1].selected = !scope.listboxData[currentIndexSet.listboxDataIndex+1].selected;
5003 scope.listboxData[currentIndexSet.listboxDataIndex].selected = !scope.listboxData[currentIndexSet.listboxDataIndex].selected;
5005 prevDirection = 'UP';
5007 // If no modifier keys are selected, all other items need to be unselected.
5008 prevDirection = undefined;
5009 selectItems(0, scope.listboxData.length, false);
5010 if(currentIndexSet.listboxDataIndex !== undefined && !isNaN(currentIndexSet.listboxDataIndex)){
5011 scope.listboxData[currentIndexSet.listboxDataIndex].selected = true;
5014 focusOnElement(currentIndexSet.elementIndex);
5015 if(!scope.$$phase) {
5018 evt.preventDefault();
5019 evt.stopPropagation();
5022 case keymap.KEY.RIGHT:
5023 case keymap.KEY.DOWN:
5025 if (currentIndexSet.listboxDataIndex === scope.listboxData.length-1) {
5026 evt.preventDefault();
5027 evt.stopPropagation();
5031 incrementIndex(elements.eq(currentIndexSet.elementIndex));
5033 if (scope.multiselectable && (evt.shiftKey || evt.ctrlKey)) {
5035 if (prevDirection === 'UP') {
5036 scope.listboxData[currentIndexSet.listboxDataIndex-1].selected = !scope.listboxData[currentIndexSet.listboxDataIndex-1].selected;
5039 scope.listboxData[currentIndexSet.listboxDataIndex].selected = !scope.listboxData[currentIndexSet.listboxDataIndex].selected;
5041 prevDirection = 'DOWN';
5043 // If no modifier keys are selected, all other items need to be unselected.
5044 prevDirection = undefined;
5045 selectItems(0, scope.listboxData.length, false);
5046 if(currentIndexSet.listboxDataIndex !== undefined && !isNaN(currentIndexSet.listboxDataIndex)){
5047 scope.listboxData[currentIndexSet.listboxDataIndex].selected = true;
5051 focusOnElement(currentIndexSet.elementIndex);
5052 if(!scope.$$phase) {
5055 evt.preventDefault();
5056 evt.stopPropagation();
5059 case keymap.KEY.TAB:
5061 var previousElement = b2bDOMHelper.previousElement(elem.parent().parent(), true);
5062 evt.preventDefault();
5063 previousElement.focus();
5071 elem.bind('click', function(evt) {
5072 var index = parseInt(evt.target.dataset.index, 10);
5073 if (index === undefined || isNaN(index)) {
5076 if (scope.multiselectable && currentIndexSet.listboxDataIndex !== undefined) {
5077 if (shiftKeyPressed) {
5078 var min = Math.min(index, currentIndexSet.listboxDataIndex);
5079 var max = Math.max(index, currentIndexSet.listboxDataIndex);
5081 if (index === min) { // clicking up
5082 var firstIndex = scope.listboxData.findIndex(function(item) { return item.selected === true;});
5083 // Given the firstIndex, let's find the matching element to get proper element match
5084 elements = elem.children();
5085 elements.eq(firstIndex)
5086 var elementsThatMatch = Array.prototype.filter.call(elements, function(item) {
5087 if (parseInt(angular.element(item).attr('data-index'), 10) === firstIndex) {
5091 firstIndex = parseInt(angular.element(elementsThatMatch).attr('data-index'), 10);
5093 if (index <= firstIndex && scope.listboxData.filter(isTrue).length > 1) {
5094 // Break the selection into 2
5095 selectItems(firstIndex + 1, max + 1, undefined); // + 1 needed because selectItems only selects up to MAX
5096 selectItems(min, firstIndex, undefined);
5097 } else if (scope.listboxData.filter(isTrue).length == 1){
5098 selectItems(min, max, undefined);
5100 selectItems(min + 1, max + 1, undefined);
5102 } else { // clicking down
5103 selectItems(min + 1, max + 1, scope.listboxData[min].selected);
5105 } else if (ctrlKeyPressed) {
5106 scope.listboxData[index].selected = !scope.listboxData[index].selected;
5108 selectItems(0, scope.listboxData.length, false);
5109 scope.listboxData[index].selected = !scope.listboxData[index].selected;
5112 selectItems(0, scope.listboxData.length, false);
5113 scope.listboxData[index].selected = !scope.listboxData[index].selected;
5115 currentIndexSet.elementIndex = index;
5116 currentIndexSet.listboxDataIndex = index;
5117 scope.currentIndex = currentIndexSet.listboxDataIndex;
5118 if (!scope.$$phase) {
5121 focusOnElement(index);
5128 * @name Videos, audio & animation.att:loaderAnimation
5131 * <file src="src/loaderAnimation/docs/readme.md" />
5134 * <!-- Below demo js shows-->
5135 * Angular library uses Global.css's icon-primary-spinner.
5138 * <section id="code">
5139 <example module="b2b.att">
5140 <file src="src/loaderAnimation/docs/demo.html" />
5141 <file src="src/loaderAnimation/docs/demo.js" />
5146 angular.module('b2b.att.loaderAnimation', [])
5147 .constant('b2bSpinnerConfig', {
5148 loadingText: 'Loading...',
5149 startEvent: 'startButtonSpinner',
5150 stopEvent: 'stopButtonSpinner'
5152 .constant("progressTrackerConfig", {
5153 loadingText: 'Loading...',
5155 activationDelay: "",
5156 minDurationPromise: "",
5157 activationDelayPromise: ""
5160 .provider('progressTracker', function () {
5161 this.$get = ['$q', '$timeout', function ($q, $timeout) {
5162 function cancelTimeout(promise) {
5164 $timeout.cancel(promise);
5167 return function ProgressTracker(options) {
5168 //do new if user doesn't
5169 if (!(this instanceof ProgressTracker)) {
5170 return new ProgressTracker(options);
5173 options = options || {};
5174 //Array of promises being tracked
5177 //Allow an optional "minimum duration" that the tracker has to stay active for.
5178 var minDuration = options.minDuration;
5179 //Allow a delay that will stop the tracker from activating until that time is reached
5180 var activationDelay = options.activationDelay;
5181 var minDurationPromise;
5182 var activationDelayPromise;
5183 self.active = function () {
5184 //Even if we have a promise in our tracker, we aren't active until delay is elapsed
5185 if (activationDelayPromise) {
5188 return tracked.length > 0;
5190 self.tracking = function () {
5191 //Even if we aren't active, we could still have a promise in our tracker
5192 return tracked.length > 0;
5194 self.destroy = self.cancel = function () {
5195 minDurationPromise = cancelTimeout(minDurationPromise);
5196 activationDelayPromise = cancelTimeout(activationDelayPromise);
5197 for (var i = tracked.length - 1; i >= 0; i--) {
5198 tracked[i].resolve();
5202 //Create a promise that will make our tracker active until it is resolved.
5203 // @return deferred - our deferred object that is being tracked
5204 self.createPromise = function () {
5205 var deferred = $q.defer();
5206 tracked.push(deferred);
5207 //If the tracker was just inactive and this the first in the list of promises, we reset our delay and minDuration again.
5208 if (tracked.length === 1) {
5209 if (activationDelay) {
5210 activationDelayPromise = $timeout(function () {
5211 activationDelayPromise = cancelTimeout(activationDelayPromise);
5213 }, activationDelay);
5218 deferred.promise.then(onDone(false), onDone(true));
5221 function startMinDuration() {
5223 minDurationPromise = $timeout(angular.noop, minDuration);
5226 //Create a callback for when this promise is done. It will remove our tracked promise from the array if once minDuration is complete
5228 return function () {
5229 (minDurationPromise || $q.when()).then(function () {
5230 var index = tracked.indexOf(deferred);
5231 tracked.splice(index, 1);
5232 //If this is the last promise, cleanup the timeouts for activationDelay
5233 if (tracked.length === 0) {
5234 activationDelayPromise = cancelTimeout(activationDelayPromise);
5240 self.addPromise = function (promise) {
5242 // we cannot assign then function in other var and then add the resolve and reject
5243 var thenFxn = promise && (promise.then || promise.$then || (promise.$promise && promise.$promise.then));
5245 throw new Error("progressTracker expects a promise object :: Not found");
5247 var deferred = self.createPromise();
5248 //When given promise is done, resolve our created promise
5249 //Allow $then for angular-resource objects
5251 promise.then(function (value) {
5252 deferred.resolve(value);
5254 }, function (value) {
5255 deferred.reject(value);
5256 return $q.reject(value);
5265 .config(['$httpProvider', function ($httpProvider) {
5266 $httpProvider.interceptors.push(['$q', 'progressTracker', function ($q) {
5268 request: function (config) {
5269 if (config.tracker) {
5270 if (!angular.isArray(config.tracker)) {
5271 config.tracker = [config.tracker];
5273 config.$promiseTrackerDeferred = config.$promiseTrackerDeferred || [];
5275 angular.forEach(config.tracker, function (tracker) {
5276 var deferred = tracker.createPromise();
5277 config.$promiseTrackerDeferred.push(deferred);
5280 return $q.when(config);
5282 response: function (response) {
5283 if (response.config && response.config.$promiseTrackerDeferred) {
5284 angular.forEach(response.config.$promiseTrackerDeferred, function (deferred) {
5285 deferred.resolve(response);
5288 return $q.when(response);
5290 responseError: function (response) {
5291 if (response.config && response.config.$promiseTrackerDeferred) {
5292 angular.forEach(response.config.$promiseTrackerDeferred, function (deferred) {
5293 deferred.reject(response);
5296 return $q.reject(response);
5302 .directive('b2bClickSpin', ['$timeout', '$parse', '$rootScope', 'progressTracker', function ($timeout, $parse, $rootScope, progressTracker) {
5305 link: function (scope, elm, attrs) {
5306 var fn = $parse(attrs.b2bClickSpin);
5307 elm.on('click', function (event) {
5308 var promise = $timeout(function () {console.log("inside Promise")}, 5000);
5309 scope.$apply(function () {
5314 //comment this line if not running unit test
5315 $rootScope.loadingTracker = progressTracker({
5318 $rootScope.loadingTracker.addPromise(promise);
5319 angular.forEach("$routeChangeSuccess $viewContentLoaded $locationChangeSuccess".split(" "), function (event) {
5320 $rootScope.$on(event, function () {
5322 $timeout.cancel(promise);
5330 .directive('b2bProgressTracker', ['progressTrackerConfig', function (ptc) {
5334 template: '<div><div ng-show="loadingTracker.active()" style="width:100%; text-align:center"><i class=\"icon-primary-spinner\"></i></div><div ng-show="loadingTracker.active()" style="width:100%;margin-top:10px; text-align:center">'+ ptc.loadingText+'</div></div>'
5338 .directive('b2bLoadButton', ['b2bSpinnerConfig', '$timeout', function (spinnerConfig, $timeout) {
5339 var spinButton = function (state, element, data) {
5341 var attr = element.html() ? 'html' : 'val';
5342 state = state + 'Text';
5343 if (state === 'loadingText') {
5344 element[attr](data[state]);
5345 element.attr("disabled",'disabled');
5346 element.addClass('disabled');
5347 } else if (state === 'resetText') {
5348 element[attr](data[state]);
5349 element.removeAttr("disabled");
5350 element.removeClass('disabled');
5358 promise: '=promise',
5359 startEvent: '@startEvent',
5360 stopEvent: '@stopEvent'
5362 link: function (scope, element, attr) {
5363 var validAttr = element.html() ? 'html' : 'val';
5369 var updateLoadingText = function (val) {
5370 var loadingText = val;
5371 if (!angular.isDefined(loadingText) || loadingText === "") {
5372 loadingText = spinnerConfig.loadingText;
5374 data.loadingText = validAttr === 'html' ? "<i class=\"icon-primary-spinner small\"></i>" + loadingText : loadingText;
5376 var updateResetText = function (val) {
5377 data.resetText = val;
5380 attr.$observe('b2bLoadButton', function (val) {
5381 updateLoadingText(val);
5383 $timeout(function () {
5384 updateResetText(element[validAttr]());
5387 if (!angular.isDefined(scope.startEvent) || scope.startEvent === "") {
5388 scope.startEvent = spinnerConfig.startEvent;
5391 if (!angular.isDefined(scope.stopEvent) || scope.stopEvent === "") {
5392 scope.stopEvent = spinnerConfig.stopEvent;
5395 scope.$watch('promise', function () {
5396 if (angular.isDefined(scope.promise) && angular.isFunction(scope.promise.then)) {
5397 spinButton('loading', element, data);
5398 scope.promise.then(function () {
5399 spinButton('reset', element, data);
5401 spinButton('reset', element, data);
5406 scope.$on(scope.startEvent, function () {
5407 spinButton('loading', element, data);
5408 scope.$on(scope.stopEvent, function () {
5409 spinButton('reset', element, data);
5417 * @name Misc.att:messageWrapper
5419 * @param {boolean} trigger - A boolean that triggers directive to switch focus
5420 * @param {integer} delay - Extra delay added to trigger code to allow for DOM to be ready. Default is 10ms.
5421 * @param {string} noFocus - Attribute-based API to trigger whether first focusable element receives focus on trigger or whole message (assumes tabindex="-1" set on first child)
5422 * @param {string} trapFocus - Attribute-based API to trap focus within the message. This should be enabled by default on all toast messages.
5424 * <file src="src/messageWrapper/docs/readme.md" />
5426 * <b2b-message-wrapper>Code that contains at least one focusable element and will be shown/hidden on some logic. This must have tabindex="-1".</b2b-message-wrapper>
5429 * <section id="code">
5430 <b>HTML + AngularJS</b>
5431 <example module="b2b.att">
5432 <file src="src/messageWrapper/docs/demo.html" />
5433 <file src="src/messageWrapper/docs/demo.js" />
5438 angular.module('b2b.att.messageWrapper', ['b2b.att.utilities'])
5439 .directive('b2bMessageWrapper', ['b2bDOMHelper', '$compile', '$timeout', '$log', function(b2bDOMHelper, $compile, $timeout, $log) {
5448 template: '<div ng-transclude></div>',
5449 link: function(scope, elem, attrs) {
5450 scope.delay = scope.delay || 10;
5452 if (attrs.trapFocus != undefined && !elem.children().eq(0).attr('b2b-trap-focus-inside-element')) {
5453 // Append b2bTrapFocusInsideElement onto first child and recompile
5454 elem.children().eq(0).attr('b2b-trap-focus-inside-element', 'false');
5455 elem.children().eq(0).attr('trigger', scope.trigger);
5456 $compile(elem.contents())(scope);
5459 var firstElement = undefined,
5460 launchingElement = undefined;
5462 scope.$watch('trigger', function(oldVal, newVal) {
5463 if (oldVal === newVal) return;
5464 if (!angular.isDefined(launchingElement)) {
5465 launchingElement = document.activeElement;
5467 $timeout(function() {
5468 if (scope.trigger) {
5470 if (attrs.noFocus === true || attrs.noFocus === "") {
5471 elem.children()[0].focus();
5473 firstElement = b2bDOMHelper.firstTabableElement(elem);
5475 if (angular.isDefined(firstElement)) {
5476 firstElement.focus();
5481 if (angular.isDefined(launchingElement) && launchingElement.nodeName !== 'BODY') {
5482 if (launchingElement === document.activeElement) {
5486 if (b2bDOMHelper.isInDOM(launchingElement) && b2bDOMHelper.isTabable(launchingElement)) {
5487 // At this point, launchingElement is still a valid element, but focus will fail and
5488 // activeElement will become body, hence we want to apply custom logic and find previousElement
5489 var prevLaunchingElement = launchingElement;
5490 launchingElement.focus();
5492 if (document.activeElement !== launchingElement || document.activeElement.nodeName === 'BODY') {
5493 launchingElement = b2bDOMHelper.previousElement(angular.element(prevLaunchingElement), true);
5494 launchingElement.focus();
5497 launchingElement = b2bDOMHelper.previousElement(launchingElement, true);
5498 launchingElement.focus();
5509 * @name Messages, modals & alerts.att:modalsAndAlerts
5512 * <file src="src/modalsAndAlerts/docs/readme.md" />
5515 * <button class="btn" b2b-modal="b2bTemplate/modalsAndAlerts/demo_modal.html" modal-ok="ok()" modal-cancel="cancel()">Launch demo modal</button>
5518 * <section id="code">
5519 <example module="b2b.att">
5520 <file src="src/modalsAndAlerts/docs/demo.html" />
5521 <file src="src/modalsAndAlerts/docs/demo.js" />
5526 angular.module('b2b.att.modalsAndAlerts', ['b2b.att.position', 'b2b.att.transition', 'b2b.att.utilities'])
5529 * A helper, internal data structure that acts as a map but also allows getting / removing
5530 * elements in the LIFO order
5532 .factory('$$stackedMap', function () {
5534 createNew: function () {
5538 add: function (key, value) {
5544 get: function (key) {
5545 for (var i = 0; i < stack.length; i++) {
5546 if (key === stack[i].key) {
5553 for (var i = 0; i < stack.length; i++) {
5554 keys.push(stack[i].key);
5559 return stack[stack.length - 1];
5561 remove: function (key) {
5563 for (var i = 0; i < stack.length; i++) {
5564 if (key === stack[i].key) {
5569 return stack.splice(idx, 1)[0];
5571 removeTop: function () {
5572 return stack.splice(stack.length - 1, 1)[0];
5574 length: function () {
5575 return stack.length;
5580 }).factory('trapFocusInElement', ['$document', '$isElement', 'b2bDOMHelper', 'keymap', function ($document, $isElement, b2bDOMHelper, keymap) {
5581 var elementStack = [];
5582 var stackHead = undefined;
5583 var firstTabableElement, lastTabableElement;
5585 var trapKeyboardFocusInFirstElement = function (e) {
5587 e.keyCode = e.which;
5590 if (e.shiftKey === true && e.keyCode === keymap.KEY.TAB) {
5591 lastTabableElement[0].focus();
5592 e.preventDefault(e);
5593 e.stopPropagation(e);
5598 var trapKeyboardFocusInLastElement = function (e) {
5600 e.keyCode = e.which;
5603 if (e.shiftKey === false && e.keyCode === keymap.KEY.TAB) {
5604 firstTabableElement[0].focus();
5605 e.preventDefault(e);
5606 e.stopPropagation(e);
5610 var trapFocusInElement = function (flag, firstTabableElementParam, lastTabableElementParam) {
5611 var bodyElements = $document.find('body').children();
5613 firstTabableElement = firstTabableElementParam ? firstTabableElementParam : angular.element(b2bDOMHelper.firstTabableElement(stackHead));
5614 lastTabableElement = lastTabableElementParam ? lastTabableElementParam : angular.element(b2bDOMHelper.lastTabableElement(stackHead));
5617 for (var i = 0; i < bodyElements.length; i++) {
5618 if (bodyElements[i] !== stackHead[0]) {
5619 bodyElements.eq(i).attr('aria-hidden', true);
5622 firstTabableElement.bind('keydown', trapKeyboardFocusInFirstElement);
5623 lastTabableElement.bind('keydown', trapKeyboardFocusInLastElement);
5625 for (var j = 0; j < bodyElements.length; j++) {
5626 if (bodyElements[j] !== stackHead[0]) {
5627 bodyElements.eq(j).removeAttr('aria-hidden');
5630 firstTabableElement.unbind('keydown', trapKeyboardFocusInFirstElement);
5631 lastTabableElement.unbind('keydown', trapKeyboardFocusInLastElement);
5634 var toggleTrapFocusInElement = function (flag, element) {
5635 if (angular.isDefined(flag) && angular.isDefined(element)) {
5636 if (angular.isUndefined(stackHead)) {
5637 stackHead = element;
5638 trapFocusInElement(flag);
5641 trapFocusInElement(false);
5642 elementStack.push(stackHead);
5643 stackHead = element;
5644 trapFocusInElement(true);
5646 if (stackHead.prop('$$hashKey') === element.prop('$$hashKey')) {
5647 trapFocusInElement(false);
5648 stackHead = elementStack.pop();
5649 if (angular.isDefined(stackHead)) {
5650 trapFocusInElement(true);
5656 if (angular.isDefined(stackHead)) {
5657 trapFocusInElement(false, firstTabableElement, lastTabableElement);
5658 trapFocusInElement(true);
5663 return toggleTrapFocusInElement;
5667 * A helper directive for the $modal service. It creates a backdrop element.
5669 .directive('b2bModalBackdrop', ['$modalStack', '$timeout', function ($modalStack, $timeout) {
5673 templateUrl: 'b2bTemplate/modalsAndAlerts/b2b-backdrop.html',
5674 link: function (scope, element, attrs) {
5675 scope.close = function (evt) {
5676 var modal = $modalStack.getTop();
5677 if (modal && modal.value.backdrop && modal.value.backdrop !== 'static') {
5678 evt.preventDefault();
5679 evt.stopPropagation();
5680 $modalStack.dismiss(modal.key, 'backdrop click');
5687 .directive('b2bModalWindow', ['$timeout', 'windowOrientation', '$window', 'keymap', function ($timeout, windowOrientation, $window, keymap) {
5695 templateUrl: 'b2bTemplate/modalsAndAlerts/b2b-window.html',
5696 controller: ['$scope', '$element', '$attrs', function (scope, element, attrs) {
5697 scope.windowClass = attrs.windowClass || '';
5698 scope.sizeClass = attrs.sizeClass || '';
5699 scope.isNotifDialog = false;
5700 scope.modalClose = attrs.modalClose || false;
5702 this.setTitle = function (title) {
5703 scope.title = title;
5705 this.setContent = function (content) {
5706 scope.content = content;
5707 scope.isNotifDialog = true;
5709 this.isDockedModal = scope.windowClass.indexOf('modal-docked') > -1;
5711 link: function (scope, element, attrs, ctrl) {
5712 if (ctrl.isDockedModal) {
5713 scope.isModalLandscape = false;
5715 var window = angular.element($window);
5716 scope.updateCss = function () {
5717 if (windowOrientation.isPotrait()) { // Potrait Mode
5718 scope.isModalLandscape = false;
5719 } else if (windowOrientation.isLandscape()) { // Landscape Mode
5720 scope.isModalLandscape = true;
5724 $timeout(function () {
5728 window.bind('orientationchange', function () {
5732 window.bind('resize', function () {
5737 angular.element(element[0].querySelectorAll(".awd-select-list")).css({
5738 "max-height": "200px"
5742 var isIE = /msie|trident/i.test(navigator.userAgent);
5744 if(angular.element(element[0].querySelector('.corner-button button.close')).length > 0){
5745 angular.element(element[0].querySelector('.corner-button button.close')).bind('focus', function () {
5746 angular.element(element[0].querySelector('.b2b-modal-header'))[0].scrollLeft = 0;
5747 angular.element(element[0].querySelector('.b2b-modal-header'))[0].scrollTop = 0;
5752 if(scope.modalClose){
5753 element.bind('keydown', function (e) {
5754 if(e.keyCode == keymap.KEY.ESC){
5756 e.stopPropagation();
5764 .directive('b2bModalTitle', [function () {
5767 require: '^b2bModalWindow',
5768 link: function (scope, elem, attr, ctrl) {
5769 ctrl.setTitle(attr.id);
5774 .directive('b2bModalContent', [function () {
5777 require: '^b2bModalWindow',
5778 link: function (scope, elem, attr, ctrl) {
5779 ctrl.setContent(attr.id);
5785 .directive('b2bModalBody', ['$timeout', '$position', '$document', '$window', 'windowOrientation', 'b2bAwdBreakpoints', function ($timeout, $position, $document, $window, windowOrientation, b2bAwdBreakpoints) {
5791 require: '^b2bModalWindow',
5792 link: function (scope, element, attrs, ctrl) {
5793 var window = angular.element($window);
5794 var body = $document.find('body').eq(0);
5795 scope.setModalHeight = function () {
5796 var modalHeaderHeight, modalFooterHeight, modalBodyHeight, windowHeight, windowWidth, modalHeight;
5797 modalHeaderHeight = 0;
5798 modalFooterHeight = 0;
5799 windowHeight = $window.innerHeight;
5800 windowWidth = $window.innerWidth;
5802 'height': windowHeight + 'px'
5805 if (ctrl.isDockedModal) {
5806 var modalElements = element.parent().children();
5807 for (var i = 0; i < modalElements.length; i++) {
5808 if (modalElements.eq(i).hasClass('b2b-modal-header')) {
5809 modalHeaderHeight = $position.position(modalElements.eq(i)).height;
5810 } else if (modalElements.eq(i).hasClass('b2b-modal-footer')) {
5811 modalFooterHeight = $position.position(modalElements.eq(i)).height;
5815 modalHeight = $position.position(element.parent()).height;
5817 modalBodyHeight = modalHeight - (modalHeaderHeight + modalFooterHeight) + 'px';
5819 if (windowOrientation.isPotrait()) { // Potrait Mode
5820 element.removeAttr('style').css({
5821 height: modalBodyHeight
5823 } else if (windowOrientation.isLandscape() && windowWidth < b2bAwdBreakpoints.breakpoints.mobile.max) { // Landscape Mode Mobile
5824 element.removeAttr('style');
5825 } else if (windowOrientation.isLandscape() && windowWidth >= b2bAwdBreakpoints.breakpoints.mobile.max) { // Landscape Mode Non-Mobile
5826 element.removeAttr('style').css({
5827 height: modalBodyHeight
5833 $timeout(function () {
5834 scope.setModalHeight();
5837 window.bind('orientationchange', function () {
5838 scope.setModalHeight();
5841 window.bind('resize', function () {
5842 scope.setModalHeight();
5849 .directive('b2bModalFooter', ['windowOrientation', '$window', function (windowOrientation, $window) {
5855 link: function (scope, element, attrs) {
5861 .factory('$modalStack', ['$document', '$compile', '$rootScope', '$$stackedMap', '$log', '$timeout', 'trapFocusInElement', function ($document, $compile, $rootScope, $$stackedMap, $log, $timeout, trapFocusInElement) {
5862 var backdropjqLiteEl, backdropDomEl;
5863 var backdropScope = $rootScope.$new(true);
5864 var body = $document.find('body').eq(0);
5865 var html = $document.find('html').eq(0);
5866 var openedWindows = $$stackedMap.createNew();
5867 var $modalStack = {};
5869 function backdropIndex() {
5870 var topBackdropIndex = -1;
5871 var opened = openedWindows.keys();
5872 for (var i = 0; i < opened.length; i++) {
5873 if (openedWindows.get(opened[i]).value.backdrop) {
5874 topBackdropIndex = i;
5877 return topBackdropIndex;
5880 $rootScope.$watch(backdropIndex, function (newBackdropIndex) {
5881 backdropScope.index = newBackdropIndex;
5884 function removeModalWindow(modalInstance) {
5885 //background scroll fix
5886 html.removeAttr('style');
5887 body.removeAttr('style');
5888 body.removeClass('styled-by-modal');
5890 var modalWindow = openedWindows.get(modalInstance).value;
5891 trapFocusInElement(false, modalWindow.modalDomEl);
5893 //clean up the stack
5894 openedWindows.remove(modalInstance);
5896 //remove window DOM element
5897 modalWindow.modalDomEl.remove();
5899 //remove backdrop if no longer needed
5900 if (backdropDomEl && backdropIndex() === -1) {
5901 backdropDomEl.remove();
5902 backdropDomEl = undefined;
5906 modalWindow.modalScope.$destroy();
5909 $document.bind('keydown', function (evt) {
5912 if (evt.which === 27) {
5913 modal = openedWindows.top();
5914 if (modal && modal.value.keyboard) {
5915 $rootScope.$apply(function () {
5916 $modalStack.dismiss(modal.key);
5922 $modalStack.open = function (modalInstance, modal) {
5924 openedWindows.add(modalInstance, {
5925 deferred: modal.deferred,
5926 modalScope: modal.scope,
5927 backdrop: modal.backdrop,
5928 keyboard: modal.keyboard
5931 var angularDomEl = angular.element('<div b2b-modal-window></div>');
5932 angularDomEl.attr('window-class', modal.windowClass);
5933 angularDomEl.attr('size-class', modal.sizeClass);
5934 angularDomEl.attr('index', openedWindows.length() - 1);
5935 angularDomEl.attr('modal-close', modal.modalClose);
5936 angularDomEl.html(modal.content);
5938 var modalDomEl = $compile(angularDomEl)(modal.scope);
5939 openedWindows.top().value.modalDomEl = modalDomEl;
5940 //background page scroll fix
5942 'overflow-y': 'hidden'
5945 'overflow-y': 'hidden',
5947 'height': window.innerHeight + 'px'
5949 body.addClass('styled-by-modal');
5950 body.append(modalDomEl);
5952 if (backdropIndex() >= 0 && !backdropDomEl) {
5953 backdropjqLiteEl = angular.element('<div b2b-modal-backdrop></div>');
5954 backdropDomEl = $compile(backdropjqLiteEl)(backdropScope);
5955 body.append(backdropDomEl);
5958 $timeout(function () {
5960 if (modal.scope.$$childHead.isNotifDialog) {
5961 angular.element(modalDomEl).find('button')[0].focus();
5963 angular.element(modalDomEl)[0].focus();
5965 trapFocusInElement(true, angular.element(modalDomEl).eq(0));
5969 $modalStack.close = function (modalInstance, result) {
5970 var modal = openedWindows.get(modalInstance);
5972 modal.value.deferred.resolve(result);
5973 removeModalWindow(modalInstance);
5977 $modalStack.dismiss = function (modalInstance, reason) {
5978 var modalWindow = openedWindows.get(modalInstance).value;
5980 modalWindow.deferred.reject(reason);
5981 removeModalWindow(modalInstance);
5985 $modalStack.getTop = function () {
5986 return openedWindows.top();
5992 .provider('$modal', function () {
5993 var $modalProvider = {
5995 backdrop: true, //can be also false or 'static'
5998 $get: ['$injector', '$rootScope', '$q', '$http', '$templateCache', '$controller', '$modalStack', function ($injector, $rootScope, $q, $http, $templateCache, $controller, $modalStack) {
6001 function getTemplatePromise(options) {
6002 return options.template ? $q.when(options.template) :
6003 $http.get(options.templateUrl, {
6004 cache: $templateCache
6005 }).then(function (result) {
6010 function getResolvePromises(resolves) {
6011 var promisesArr = [];
6012 angular.forEach(resolves, function (value, key) {
6013 if (angular.isFunction(value) || angular.isArray(value)) {
6014 promisesArr.push($q.when($injector.invoke(value)));
6020 $modal.open = function (modalOptions) {
6022 var modalResultDeferred = $q.defer();
6023 var modalOpenedDeferred = $q.defer();
6024 //prepare an instance of a modal to be injected into controllers and returned to a caller
6025 var modalInstance = {
6026 result: modalResultDeferred.promise,
6027 opened: modalOpenedDeferred.promise,
6028 close: function (result) {
6029 $modalStack.close(modalInstance, result);
6031 dismiss: function (reason) {
6032 $modalStack.dismiss(modalInstance, reason);
6036 //merge and clean up options
6037 modalOptions = angular.extend({}, $modalProvider.options, modalOptions);
6038 modalOptions.resolve = modalOptions.resolve || {};
6041 if (!modalOptions.template && !modalOptions.templateUrl) {
6042 throw new Error('One of template or templateUrl options is required.');
6045 var templateAndResolvePromise =
6046 $q.all([getTemplatePromise(modalOptions)].concat(getResolvePromises(modalOptions.resolve)));
6049 templateAndResolvePromise.then(function resolveSuccess(tplAndVars) {
6051 var modalScope = (modalOptions.scope || $rootScope).$new();
6052 modalScope.$close = modalInstance.close;
6053 modalScope.$dismiss = modalInstance.dismiss;
6055 var ctrlInstance, ctrlLocals = {};
6056 var resolveIter = 1;
6059 if (modalOptions.controller) {
6060 ctrlLocals.$scope = modalScope;
6061 ctrlLocals.$modalInstance = modalInstance;
6062 angular.forEach(modalOptions.resolve, function (value, key) {
6063 ctrlLocals[key] = tplAndVars[resolveIter++];
6066 ctrlInstance = $controller(modalOptions.controller, ctrlLocals);
6069 $modalStack.open(modalInstance, {
6071 deferred: modalResultDeferred,
6072 content: tplAndVars[0],
6073 backdrop: modalOptions.backdrop,
6074 keyboard: modalOptions.keyboard,
6075 windowClass: modalOptions.windowClass,
6076 sizeClass: modalOptions.sizeClass,
6077 modalClose: modalOptions.modalClose
6080 }, function resolveError(reason) {
6081 modalResultDeferred.reject(reason);
6084 templateAndResolvePromise.then(function () {
6085 modalOpenedDeferred.resolve(true);
6087 modalOpenedDeferred.reject(false);
6090 return modalInstance;
6097 return $modalProvider;
6100 .directive("b2bModal", ["$modal", "$log", '$scrollTo', function ($modal, $log, $scrollTo) {
6105 modalController: '@',
6112 link: function (scope, elm, attr) {
6113 elm.bind('click', function (ev) {
6114 var currentPosition = ev.pageY - ev.clientY;
6115 ev.preventDefault();
6116 if (angular.isDefined(elm.attr("href")) && elm.attr("href") !== "") {
6117 scope.b2bModal = elm.attr("href");
6120 templateUrl: scope.b2bModal,
6121 controller: scope.modalController,
6122 windowClass: scope.windowClass,
6123 sizeClass: scope.sizeClass,
6124 modalClose: scope.modalClose
6125 }).result.then(function (value) {
6130 }, function (value) {
6141 .directive("utilityFilter", ["$modal", "$log", '$scrollTo', function ($modal, $log, $scrollTo) {
6148 templateUrl: 'b2bTemplate/modal/u-filter.html',
6149 link: function (scope, element, attribute, ctrl) {
6150 //controller to be passed to $modal service
6151 scope.options = angular.copy(scope.$parent.$eval(attribute.ngModel));
6152 scope.$parent.$watch(attribute.ngModel, function (newVal, oldVal) {
6153 if (newVal !== oldVal) {
6154 scope.options = newVal;
6157 var modalCtrl = function ($scope, options) {
6158 $scope.options = angular.copy(options);
6161 if (angular.isDefined(scope.utilityFilter)) {
6162 scope.templateUrl = scope.utilityFilter;
6164 scope.templateUrl = 'b2bTemplate/modal/u-filter-window.html';
6166 element.bind('click', function (ev) {
6167 var currentPosition = ev.pageY - ev.clientY;
6169 templateUrl: scope.templateUrl,
6170 controller: modalCtrl,
6172 options: function () {
6173 return scope.options;
6176 }).result.then(function (value) {
6177 ctrl.$setViewValue(value);
6179 $scrollTo(0, currentPosition, 0);
6182 $scrollTo(0, currentPosition, 0);
6190 * @name Forms.att:monthSelector
6193 * <file src="src/monthSelector/docs/readme.md" />
6196 * <div b2b-monthpicker ng-model="dt" min="minDate" max="maxDate" mode="monthpicker"></div>
6199 * <section id="code">
6200 <example module="b2b.att">
6201 <file src="src/monthSelector/docs/demo.html" />
6202 <file src="src/monthSelector/docs/demo.js" />
6207 angular.module('b2b.att.monthSelector', ['b2b.att.position', 'b2b.att.utilities'])
6209 .constant('b2bMonthpickerConfig', {
6210 dateFormat: 'MM/dd/yyyy',
6212 monthFormat: 'MMMM',
6214 dayHeaderFormat: 'EEEE',
6215 dayTitleFormat: 'MMMM yyyy',
6216 disableWeekend: false,
6217 disableSunday: false,
6219 onSelectClose: null,
6226 legendMessage: null,
6227 calendarDisabled: false,
6229 orientation: 'left',
6232 helperText: 'The date you selected is $date. Double tap to open calendar. Select a date to close the calendar.',
6233 descriptionText: 'Use tab to navigate between previous button, next button and month. Use arrow keys to navigate between months. Use space or enter to select a month.',
6234 MonthpickerEvalAttributes: ['dateFormat', 'dayFormat', 'monthFormat', 'yearFormat', 'dayHeaderFormat', 'dayTitleFormat', 'disableWeekend', 'disableSunday', 'startingDay', 'collapseWait', 'orientation','mode','id'],
6235 MonthpickerWatchAttributes: ['min', 'max', 'due', 'from', 'legendIcon', 'legendMessage', 'ngDisabled'],
6236 MonthpickerFunctionAttributes: ['disableDates', 'onSelectClose']
6239 .factory('b2bMonthpickerService', ['b2bMonthpickerConfig', 'dateFilter', function (b2bMonthpickerConfig, dateFilter) {
6240 var setAttributes = function (attr, elem) {
6241 if (angular.isDefined(attr) && attr !== null && angular.isDefined(elem) && elem !== null) {
6242 var attributes = b2bMonthpickerConfig.MonthpickerEvalAttributes.concat(b2bMonthpickerConfig.MonthpickerWatchAttributes, b2bMonthpickerConfig.MonthpickerFunctionAttributes);
6243 for (var key in attr) {
6244 var val = attr[key];
6245 if (attributes.indexOf(key) !== -1 && angular.isDefined(val)) {
6246 elem.attr(key.toSnakeCase(), key);
6252 var bindScope = function (attr, scope) {
6253 if (angular.isDefined(attr) && attr !== null && angular.isDefined(scope) && scope !== null) {
6254 var evalFunction = function (key, val) {
6255 scope[key] = scope.$parent.$eval(val);
6258 var watchFunction = function (key, val) {
6259 scope.$parent.$watch(val, function (value) {
6262 scope.$watch(key, function (value) {
6263 scope.$parent[val] = value;
6267 var evalAttributes = b2bMonthpickerConfig.MonthpickerEvalAttributes;
6268 var watchAttributes = b2bMonthpickerConfig.MonthpickerWatchAttributes;
6269 for (var key in attr) {
6270 var val = attr[key];
6271 if (evalAttributes.indexOf(key) !== -1 && angular.isDefined(val)) {
6272 evalFunction(key, val);
6273 } else if (watchAttributes.indexOf(key) !== -1 && angular.isDefined(val)) {
6274 watchFunction(key, val);
6281 setAttributes: setAttributes,
6282 bindScope: bindScope
6286 .controller('b2bMonthpickerController', ['$scope', '$attrs', 'dateFilter', '$element', '$position', 'b2bMonthpickerConfig', function ($scope, $attrs, dateFilter, $element, $position, dtConfig) {
6288 date: getValue($attrs.dateFormat, dtConfig.dateFormat),
6289 day: getValue($attrs.dayFormat, dtConfig.dayFormat),
6290 month: getValue($attrs.monthFormat, dtConfig.monthFormat),
6291 year: getValue($attrs.yearFormat, dtConfig.yearFormat),
6292 dayHeader: getValue($attrs.dayHeaderFormat, dtConfig.dayHeaderFormat),
6293 dayTitle: getValue($attrs.dayTitleFormat, dtConfig.dayTitleFormat),
6294 disableWeekend: getValue($attrs.disableWeekend, dtConfig.disableWeekend),
6295 disableSunday: getValue($attrs.disableSunday, dtConfig.disableSunday),
6296 disableDates: getValue($attrs.disableDates, dtConfig.disableDates)
6298 startingDay = getValue($attrs.startingDay, dtConfig.startingDay);
6300 $scope.minDate = dtConfig.minDate ? $scope.resetTime(dtConfig.minDate) : null;
6301 $scope.maxDate = dtConfig.maxDate ? $scope.resetTime(dtConfig.maxDate) : null;
6302 $scope.dueDate = dtConfig.dueDate ? $scope.resetTime(dtConfig.dueDate) : null;
6303 $scope.fromDate = dtConfig.fromDate ? $scope.resetTime(dtConfig.fromDate) : null;
6304 $scope.legendIcon = dtConfig.legendIcon ? dtConfig.legendIcon : null;
6305 $scope.legendMessage = dtConfig.legendMessage ? dtConfig.legendMessage : null;
6306 $scope.ngDisabled = dtConfig.calendarDisabled ? dtConfig.calendarDisabled : null;
6307 $scope.collapseWait = getValue($attrs.collapseWait, dtConfig.collapseWait);
6308 $scope.orientation = getValue($attrs.orientation, dtConfig.orientation);
6309 $scope.onSelectClose = getValue($attrs.onSelectClose, dtConfig.onSelectClose);
6310 $scope.mode = getValue($attrs.mode, dtConfig.mode);
6312 $scope.inline = $attrs.inline === 'true' ? true : dtConfig.inline;
6314 function getValue(value, defaultValue) {
6315 return angular.isDefined(value) ? $scope.$parent.$eval(value) : defaultValue;
6318 function getDaysInMonth(year, month) {
6319 return new Date(year, month, 0).getDate();
6322 function getDates(startDate, n) {
6323 var dates = new Array(n);
6324 var current = startDate,
6327 dates[i++] = new Date(current);
6328 current.setDate(current.getDate() + 1);
6333 this.updatePosition = function (b2bMonthpickerPopupTemplate) {
6334 $scope.position = $position.offset($element);
6335 if($element.find('input').length > 0 ){
6336 $scope.position.top += $element.find('input').prop('offsetHeight');
6338 $scope.position.top += $element.find('a').prop('offsetHeight');
6341 if ($scope.orientation === 'right') {
6342 $scope.position.left -= (((b2bMonthpickerPopupTemplate && b2bMonthpickerPopupTemplate.prop('offsetWidth')) || 290) - $element.find('input').prop('offsetWidth'));
6346 function isSelected(dt) {
6347 if (dt && angular.isDate($scope.currentDate) && compare(dt, $scope.currentDate) === 0) {
6353 function isFromDate(dt) {
6354 if (dt && angular.isDate($scope.fromDate) && compare(dt, $scope.fromDate) === 0) {
6360 function isDateRange(dt) {
6361 if (dt && $scope.fromDate && angular.isDate($scope.currentDate) && (compare(dt, $scope.fromDate) >= 0) && (compare(dt, $scope.currentDate) <= 0)) {
6363 } else if (dt && $scope.fromDate && compare(dt, $scope.fromDate) === 0) {
6369 function isOld(date, currentMonthDate) {
6370 if (date && currentMonthDate && (new Date(date.getFullYear(), date.getMonth(), 1, 0, 0, 0).getTime() < new Date(currentMonthDate.getFullYear(), currentMonthDate.getMonth(), 1, 0, 0, 0).getTime())) {
6377 function isNew(date, currentMonthDate) {
6378 if (date && currentMonthDate && (new Date(date.getFullYear(), date.getMonth(), 1, 0, 0, 0).getTime() > new Date(currentMonthDate.getFullYear(), currentMonthDate.getMonth(), 1, 0, 0, 0).getTime())) {
6385 function isPastDue(dt) {
6386 if ($scope.dueDate) {
6387 return (dt > $scope.dueDate);
6392 function isDueDate(dt) {
6393 if ($scope.dueDate) {
6394 return (dt.getTime() === $scope.dueDate.getTime());
6399 var isDisabled = function (date, currentMonthDate) {
6400 if ($attrs.from && !angular.isDate($scope.fromDate)) {
6403 if (format.disableWeekend === true && (dateFilter(date, format.dayHeader) === "Saturday" || dateFilter(date, format.dayHeader) === "Sunday")) {
6406 if (format.disableSunday === true && (dateFilter(date, format.dayHeader) === "Sunday")) {
6409 if (isOld(date, currentMonthDate) || isNew(date, currentMonthDate)) {
6412 return (($scope.minDate && compare(date, $scope.minDate) < 0) || ($scope.maxDate && compare(date, $scope.maxDate) > 0) || (format.disableDates && format.disableDates({
6417 var isDisabledMonth = function (date, currentMonthDate) {
6418 if ($attrs.from && !angular.isDate($scope.fromDate)) {
6421 if (format.disableWeekend === true && (dateFilter(date, format.dayHeader) === "Saturday" || dateFilter(date, format.dayHeader) === "Sunday")) {
6424 if (format.disableSunday === true && (dateFilter(date, format.dayHeader) === "Sunday")) {
6427 return (($scope.minDate && compare(date, $scope.minDate) < 0) || ($scope.maxDate && compare(date, $scope.maxDate) > 0) || (format.disableDates && format.disableDates({
6432 var compare = function (date1, date2) {
6433 return (new Date(date1.getFullYear(), date1.getMonth(), date1.getDate()) - new Date(date2.getFullYear(), date2.getMonth(), date2.getDate()));
6436 function isMinDateAvailable(startDate, endDate) {
6437 if (($scope.minDate && $scope.minDate.getTime() >= startDate.getTime()) && ($scope.minDate.getTime() <= endDate.getTime())) {
6438 $scope.disablePrev = true;
6439 $scope.visibilityPrev = "hidden";
6441 $scope.disablePrev = false;
6442 $scope.visibilityPrev = "visible";
6446 function isMaxDateAvailable(startDate, endDate) {
6447 if (($scope.maxDate && $scope.maxDate.getTime() >= startDate.getTime()) && ($scope.maxDate.getTime() <= endDate.getTime())) {
6448 $scope.disableNext = true;
6449 $scope.visibilityNext = "hidden";
6451 $scope.disableNext = false;
6452 $scope.visibilityNext = "visible";
6456 function isYearInRange(currentYear) {
6458 if ($scope.minDate && currentYear === $scope.minDate.getFullYear()) {
6459 $scope.disablePrev = true;
6460 $scope.visibilityPrev = "hidden";
6462 $scope.disablePrev = false;
6463 $scope.visibilityPrev = "visible";
6466 if ($scope.maxDate && currentYear === $scope.maxDate.getFullYear()) {
6467 $scope.disableNext = true;
6468 $scope.visibilityNext = "hidden";
6470 $scope.disableNext = false;
6471 $scope.visibilityNext = "visible";
6476 this.focusNextPrev = function(b2bMonthpickerPopupTemplate,init){
6478 if (!$scope.disablePrev){
6479 b2bMonthpickerPopupTemplate[0].querySelector('th.prev').focus();
6480 }else if (!$scope.disableNext){
6481 b2bMonthpickerPopupTemplate[0].querySelector('th.next').focus();
6483 b2bMonthpickerPopupTemplate[0].querySelector('th.b2b-monthSelector-label').focus();
6486 if ($scope.disableNext || $scope.disablePrev){
6487 b2bMonthpickerPopupTemplate[0].querySelector('th.b2b-monthSelector-label').focus();
6492 function getLabel(label) {
6495 pre: label.substr(0, 1).toUpperCase(),
6503 function makeDate(date, dayFormat, dayHeaderFormat, isSelected, isFromDate, isDateRange, isOld, isNew, isDisabled, dueDate, pastDue) {
6506 label: dateFilter(date, dayFormat),
6507 header: dateFilter(date, dayHeaderFormat),
6508 selected: !!isSelected,
6509 fromDate: !!isFromDate,
6510 dateRange: !!isDateRange,
6513 disabled: !!isDisabled,
6516 focusable: !((isDisabled && !(isSelected || isDateRange)) || (isOld || isNew))
6523 getVisibleDates: function (date) {
6524 var year = date.getFullYear(),
6525 month = date.getMonth(),
6526 firstDayOfMonth = new Date(year, month, 1),
6527 lastDayOfMonth = new Date(year, month + 1, 0);
6528 var difference = startingDay - firstDayOfMonth.getDay(),
6529 numDisplayedFromPreviousMonth = (difference > 0) ? 7 - difference : -difference,
6530 firstDate = new Date(firstDayOfMonth),
6533 if (numDisplayedFromPreviousMonth > 0) {
6534 firstDate.setDate(-numDisplayedFromPreviousMonth + 1);
6535 numDates += numDisplayedFromPreviousMonth; // Previous
6537 numDates += getDaysInMonth(year, month + 1); // Current
6538 numDates += (7 - numDates % 7) % 7; // Next
6540 var days = getDates(firstDate, numDates),
6541 labels = new Array(7);
6542 for (var i = 0; i < numDates; i++) {
6543 var dt = new Date(days[i]);
6544 days[i] = makeDate(dt,
6552 isDisabled(dt, date),
6556 for (var j = 0; j < 7; j++) {
6557 labels[j] = getLabel(dateFilter(days[j].date, format.dayHeader));
6559 isMinDateAvailable(firstDayOfMonth, lastDayOfMonth);
6560 isMaxDateAvailable(firstDayOfMonth, lastDayOfMonth);
6563 title: dateFilter(date, format.dayTitle),
6574 getVisibleDates: function(date) {
6577 year = date.getFullYear();
6578 for (var i = 0; i < 12; i++) {
6579 var dt = new Date(year,i,1);
6580 months[i] = makeDate(dt,
6588 isDisabledMonth(dt, date),
6592 isYearInRange(year);
6593 return {objects: months, title: dateFilter(date, format.year), labels: labels};
6601 .directive('b2bMonthpickerPopup', ['$parse', '$log', '$timeout', '$document', '$documentBind', '$isElement', '$templateCache', '$compile','$interval', 'trapFocusInElement', 'keymap', function ($parse, $log, $timeout, $document, $documentBind, $isElement, $templateCache, $compile, $interval,trapFocusInElement, keymap) {
6609 templateUrl: function (elem, attr) {
6610 if (attr.inline === 'true') {
6611 return 'b2bTemplate/monthSelector/monthSelector-popup.html';
6612 }else if (attr.link === 'true') {
6613 return 'b2bTemplate/monthSelector/monthSelectorLink.html';
6615 return 'b2bTemplate/monthSelector/monthSelector.html';
6619 require: ['b2bMonthpickerPopup', 'ngModel', '?^b2bMonthpickerGroup'],
6620 controller: 'b2bMonthpickerController',
6621 link: function (scope, element, attrs, ctrls) {
6622 var MonthpickerCtrl = ctrls[0],
6624 b2bMonthpickerGroupCtrl = ctrls[2];
6625 var b2bMonthpickerPopupTemplate;
6628 $log.error("ng-model is required.");
6629 return; // do nothing if no ng-model
6632 // Configuration parameters
6633 var mode = scope.mode,
6635 scope.isOpen = false;
6639 scope.triggerInterval=undefined;
6642 if (b2bMonthpickerGroupCtrl) {
6643 b2bMonthpickerGroupCtrl.registerMonthpickerScope(scope);
6646 element.bind('keydown', function (ev) {
6649 ev.keyCode = ev.which;
6650 } else if (ev.charCode) {
6651 ev.keyCode = ev.charCode;
6654 if(ev.keyCode === keymap.KEY.ESC)
6656 scope.isOpen = false;
6657 toggleCalendar(scope.isOpen);
6662 element.find('button').bind('click', function () {
6666 element.find('a').bind('click', function () {
6671 element.find('input').bind('click', function () {
6675 var onClicked = function() {
6676 if (!scope.ngDisabled) {
6677 scope.isOpen = !scope.isOpen;
6678 toggleCalendar(scope.isOpen);
6679 MonthpickerCtrl.updatePosition(b2bMonthpickerPopupTemplate);
6684 var toggleCalendar = function (flag) {
6685 if (!scope.inline) {
6687 b2bMonthpickerPopupTemplate = angular.element($templateCache.get('b2bTemplate/monthSelector/monthSelector-popup.html'));
6688 b2bMonthpickerPopupTemplate.attr('b2b-trap-focus-inside-element', 'false');
6689 b2bMonthpickerPopupTemplate.attr('trigger', 'true');
6690 b2bMonthpickerPopupTemplate = $compile(b2bMonthpickerPopupTemplate)(scope);
6691 $document.find('body').append(b2bMonthpickerPopupTemplate);
6692 b2bMonthpickerPopupTemplate.bind('keydown', escPress);
6693 $timeout(function () {
6694 scope.getFocus = true;
6697 $timeout(function () {
6698 scope.getFocus = false;
6700 MonthpickerCtrl.focusNextPrev(b2bMonthpickerPopupTemplate,true);
6703 scope.triggerInterval = $interval(function () {
6704 //This value is updated to trigger init() function of directive on year change.
6705 scope.trigger=(scope.trigger === 0 ? 1 : 0);
6709 b2bMonthpickerPopupTemplate.unbind('keydown', escPress);
6710 if(scope.triggerInterval)
6712 $interval.cancel(scope.triggerInterval);
6713 scope.triggerInterval=undefined;
6715 b2bMonthpickerPopupTemplate.remove();
6716 if(element.find('button').length > 0){
6717 element.find('button')[0].focus();
6719 element.find('a')[0].focus();
6722 scope.getFocus = false;
6727 var outsideClick = function (e) {
6728 var isElement = $isElement(angular.element(e.target), element, $document);
6729 var isb2bMonthpickerPopupTemplate = $isElement(angular.element(e.target), b2bMonthpickerPopupTemplate, $document);
6730 if (!(isElement || isb2bMonthpickerPopupTemplate)) {
6731 scope.isOpen = false;
6732 toggleCalendar(scope.isOpen);
6737 var escPress = function (ev) {
6740 ev.keyCode = ev.which;
6741 } else if (ev.charCode) {
6742 ev.keyCode = ev.charCode;
6746 if (ev.keyCode === keymap.KEY.ESC) {
6747 scope.isOpen = false;
6748 toggleCalendar(scope.isOpen);
6749 ev.preventDefault();
6750 ev.stopPropagation();
6751 } else if (ev.keyCode === 33) {
6752 !scope.disablePrev && scope.move(-1);
6753 $timeout(function () {
6754 scope.getFocus = true;
6756 $timeout(function () {
6757 scope.getFocus = false;
6761 ev.preventDefault();
6762 ev.stopPropagation();
6763 } else if (ev.keyCode === 34) {
6764 !scope.disableNext && scope.move(1);
6765 $timeout(function () {
6766 scope.getFocus = true;
6768 $timeout(function () {
6769 scope.getFocus = false;
6773 ev.preventDefault();
6774 ev.stopPropagation();
6780 $documentBind.click('isOpen', outsideClick, scope);
6782 scope.$on('$destroy', function () {
6784 scope.isOpen = false;
6785 toggleCalendar(scope.isOpen);
6789 scope.resetTime = function (date) {
6790 if (typeof date === 'string') {
6791 date = date + 'T12:00:00';
6794 if (!isNaN(new Date(date))) {
6795 dt = new Date(date);
6796 if(scope.mode === 1){
6797 dt = new Date(dt.getFullYear(), dt.getMonth());
6799 dt = new Date(dt.getFullYear(), dt.getMonth(), dt.getDate());
6804 return new Date(dt.getFullYear(), dt.getMonth(), dt.getDate());
6808 scope.$parent.$watch($parse(attrs.min), function (value) {
6809 scope.minDate = value ? scope.resetTime(value) : null;
6814 scope.$parent.$watch($parse(attrs.max), function (value) {
6815 scope.maxDate = value ? scope.resetTime(value) : null;
6820 scope.$parent.$watch($parse(attrs.due), function (value) {
6821 scope.dueDate = value ? scope.resetTime(value) : null;
6826 scope.$parent.$watch($parse(attrs.from), function (value) {
6827 scope.fromDate = value ? scope.resetTime(value) : null;
6832 if (attrs.legendIcon) {
6833 scope.$parent.$watch(attrs.legendIcon, function (value) {
6834 scope.legendIcon = value ? value : null;
6838 if (attrs.legendMessage) {
6839 scope.$parent.$watch(attrs.legendMessage, function (value) {
6840 scope.legendMessage = value ? value : null;
6844 if (attrs.ngDisabled) {
6845 scope.$parent.$watch(attrs.ngDisabled, function (value) {
6846 scope.ngDisabled = value ? value : null;
6851 // Split array into smaller arrays
6852 function split(arr, size) {
6854 while (arr.length > 0) {
6855 arrays.push(arr.splice(0, size));
6860 var moveMonth = function(selectedDate, direction) {
6861 var step = MonthpickerCtrl.modes[scope.mode].step;
6862 selectedDate.setDate(1);
6863 selectedDate.setMonth(selectedDate.getMonth() + direction * (step.months || 0));
6864 selectedDate.setFullYear(selectedDate.getFullYear() + direction * (step.years || 0));
6866 return selectedDate;
6869 function refill(date) {
6870 if (angular.isDate(date) && !isNaN(date)) {
6871 selected = new Date(date);
6874 selected = new Date();
6879 var selectedCalendar;
6880 if(scope.mode === 1){
6881 if(!angular.isDate(selected))
6883 selected = new Date();
6885 selectedCalendar = moveMonth(angular.copy(selected), -1);
6887 selectedCalendar = angular.copy(selected);
6890 var currentMode = MonthpickerCtrl.modes[mode],
6891 data = currentMode.getVisibleDates(selected);
6893 scope.rows = split(data.objects, currentMode.split);
6896 var startFlag=false;
6897 var firstSelected = false;
6898 for(var i=0; i<scope.rows.length; i++)
6900 for(var j=0; j<scope.rows[i].length; j++)
6902 if(!scope.rows[i][j].disabled && !firstSelected)
6905 var firstDay = scope.rows[i][j];
6908 if(scope.rows[i][j].selected)
6919 if(!flag && firstSelected)
6921 firstDay.firstFocus=true;
6924 scope.labels = data.labels || [];
6925 scope.title = data.title;
6929 scope.select = function (date,$event) {
6930 var dt = new Date(date.getFullYear(), date.getMonth(), date.getDate());
6931 scope.currentDate = dt;
6932 if (!scope.onSelectClose || (scope.onSelectClose && scope.onSelectClose({
6935 if (angular.isNumber(scope.collapseWait)) {
6936 $timeout(function () {
6937 scope.isOpen = false;
6938 toggleCalendar(scope.isOpen);
6939 }, scope.collapseWait);
6941 scope.isOpen = false;
6942 toggleCalendar(scope.isOpen);
6947 scope.move = function (direction,$event) {
6948 var step = MonthpickerCtrl.modes[mode].step;
6949 selected.setDate(1);
6950 selected.setMonth(selected.getMonth() + direction * (step.months || 0));
6951 selected.setFullYear(selected.getFullYear() + direction * (step.years || 0));
6953 scope.getFocus = true;
6954 $timeout(function () {
6955 if (attrs.inline === 'true') {
6956 MonthpickerCtrl.focusNextPrev(element,false);
6958 MonthpickerCtrl.focusNextPrev(b2bMonthpickerPopupTemplate,false);
6961 $event.preventDefault();
6962 $event.stopPropagation();
6965 scope.$watch('currentDate', function (value) {
6966 if (angular.isDefined(value) && value !== null) {
6971 ngModel.$setViewValue(value);
6974 ngModel.$render = function () {
6975 scope.currentDate = ngModel.$viewValue;
6978 var stringToDate = function (value) {
6979 if (!isNaN(new Date(value))) {
6980 value = new Date(value);
6984 ngModel.$formatters.unshift(stringToDate);
6989 .directive('b2bMonthpicker', ['$compile', '$log', 'b2bMonthpickerConfig', 'b2bMonthpickerService', function ($compile, $log, b2bMonthpickerConfig, b2bMonthpickerService) {
6997 controller: ['$scope', '$element', '$attrs', function (scope, elem, attr) {
6998 var dateFormatString = angular.isDefined(attr.dateFormat) ? scope.$parent.$eval(attr.dateFormat) : b2bMonthpickerConfig.dateFormat;
6999 var helperText = angular.isDefined(attr.helperText) ? scope.$parent.$eval(attr.helperText) : b2bMonthpickerConfig.helperText;
7000 helperText = helperText.replace('$date', '{{dt | date : \'' + dateFormatString + '\'}}');
7002 var descriptionText = angular.isDefined(attr.descriptionText) ? scope.$parent.$eval(attr.descriptionText) : b2bMonthpickerConfig.descriptionText;
7006 if (elem.prop('nodeName') !== 'INPUT' && elem.prop('nodeName') !== 'A') {
7010 var selectedDateMessage = "";
7012 if (elem.prop('nodeName') !== 'A'){
7013 selectedDateMessage = '<button type="button" class="span12 faux-input" ng-disabled="ngDisabled" aria-describedby="monthpicker-description'+scope.$id+'"><span class="hidden-spoken" aria-live="assertive" aria-atomic="false">' + helperText + '</span></button>';
7014 elem.attr('tabindex', '-1');
7015 elem.attr('aria-hidden', 'true');
7016 elem.attr('readonly', 'true');
7018 selectedDateMessage = ''
7019 elem.attr('aria-label', helperText);
7022 var descriptionTextSpan = '<span class="offscreen-text" id="monthpicker-description'+scope.$id+'">'+descriptionText+'</span>';
7023 elem.removeAttr('b2b-Monthpicker');
7024 elem.removeAttr('ng-model');
7025 elem.removeAttr('ng-disabled');
7026 elem.addClass('Monthpicker-input');
7027 elem.attr('ng-model', 'dt');
7028 elem.attr('aria-describedby', 'monthpicker-description'+scope.$id);
7032 elem.attr('ng-disabled', 'ngDisabled');
7033 elem.attr('b2b-format-date', dateFormatString);
7035 var wrapperElement = angular.element('<div></div>');
7036 wrapperElement.attr('b2b-Monthpicker-popup', '');
7037 wrapperElement.attr('ng-model', 'dt');
7039 wrapperElement.attr('inline', inline);
7041 if (elem.prop('nodeName') === 'A'){
7042 wrapperElement.attr('link', true);
7044 b2bMonthpickerService.setAttributes(attr, wrapperElement);
7045 b2bMonthpickerService.bindScope(attr, scope);
7047 wrapperElement.html('');
7048 wrapperElement.append(selectedDateMessage);
7049 wrapperElement.append('');
7050 wrapperElement.append(descriptionTextSpan);
7051 wrapperElement.append('');
7052 wrapperElement.append(elem.prop('outerHTML'));
7054 var elm = wrapperElement.prop('outerHTML');
7055 elm = $compile(elm)(scope);
7056 elem.replaceWith(elm);
7058 link: function (scope, elem, attr, ctrl) {
7060 $log.error("ng-model is required.");
7061 return; // do nothing if no ng-model
7064 scope.$watch('dt', function (value) {
7065 ctrl.$setViewValue(value);
7067 ctrl.$render = function () {
7068 scope.dt = ctrl.$viewValue;
7074 .directive('b2bMonthpickerGroup', [function () {
7077 controller: ['$scope', '$element', '$attrs', function (scope, elem, attr) {
7078 this.$$headers = [];
7079 this.$$footers = [];
7080 this.registerMonthpickerScope = function (MonthpickerScope) {
7081 MonthpickerScope.headers = this.$$headers;
7082 MonthpickerScope.footers = this.$$footers;
7085 link: function (scope, elem, attr, ctrl) {}
7089 .directive('b2bFormatDate', ['dateFilter', function (dateFilter) {
7093 link: function (scope, elem, attr, ctrl) {
7094 var b2bFormatDate = "";
7095 attr.$observe('b2bFormatDate', function (value) {
7096 b2bFormatDate = value;
7098 var dateToString = function (value) {
7099 if (!isNaN(new Date(value))) {
7100 return dateFilter(new Date(value), b2bFormatDate);
7104 ctrl.$formatters.unshift(dateToString);
7109 .directive('b2bMonthpickerHeader', [function () {
7112 require: '^b2bMonthpickerGroup',
7116 compile: function (elem, attr, transclude) {
7117 return function link(scope, elem, attr, ctrl) {
7119 ctrl.$$headers.push(transclude(scope, function () {}));
7127 .directive('b2bMonthpickerFooter', [function () {
7130 require: '^b2bMonthpickerGroup',
7134 compile: function (elem, attr, transclude) {
7135 return function link(scope, elem, attr, ctrl) {
7137 ctrl.$$footers.push(transclude(scope, function () {}));
7146 * @name Navigation.att:multiLevelNavigation
7149 * <file src="src/multiLevelNavigation/docs/readme.md" />
7152 * <div class="b2b-ml-nav">
7154 * <li aria-label="{{child.name}}" tabindex="-1" b2b-ml-nav="{{child.type}}" role="treeitem" ng-repeat="child in treeStructure">
7155 * <a tabindex="-1" href="javascript:void(0);" title="{{child.name}}">{{child.name}}<span><i class="{{child.type=='endNode'?'icon-primary-circle':'icon-primary-collapsed'}}"></i></span></a>
7156 * <!-- Below UL tag is RECURSIVE to generate n-childs -->
7157 * <ul role="group" ng-if="child.child">
7158 * <li aria-label="{{child.name}}" b2b-ml-nav="{{child.type}}" tabindex="-1" role="treeitem" ng-repeat="child in child.child">
7159 * <a tabindex="-1" href="javascript:void(0);" title="{{child.name}}">{{child.name}}<span><i class="{{child.type=='endNode'?'icon-primary-circle':'icon-primary-collapsed'}}"></i></span></a>
7160 * <!-- RECURSIVE UL tag goes here -->
7168 * <section id="code">
7169 <example module="b2b.att">
7170 <file src="src/multiLevelNavigation/docs/demo.html" />
7171 <file src="src/multiLevelNavigation/docs/demo.js" />
7176 angular.module('b2b.att.multiLevelNavigation', ['b2b.att.utilities'])
7177 //directive b2bMlNav Test coverage 100% on 5/13
7178 .directive('b2bMlNav', ['keymap', function (keymap) {
7181 link: function (scope, element) {
7182 var rootE, parentE, upE, downE, lastE, homeE, endE;
7183 //default root tree element tabindex set zero
7184 if (element.parent().parent().hasClass('b2b-ml-nav') && (element[0].previousElementSibling === null)) {
7185 element.attr('tabindex', 0);
7187 //check root via class
7188 var isRoot = function (elem) {
7189 if (elem.parent().parent().eq(0).hasClass('b2b-ml-nav')) {
7196 //for any expandable tree item on click
7197 var toggleState = function (e) {
7199 if (angular.element(e.target).attr("b2b-ml-nav") !== "endNode") {
7200 var eLink = element.find('a').eq(0);
7201 if (eLink.hasClass('active')) {
7202 eLink.removeClass('active');
7203 eLink.parent().attr("aria-expanded", "false");
7204 eLink.find('i').eq(0).removeClass('icon-primary-expanded');
7205 eLink.find('i').eq(0).addClass('icon-primary-collapsed');
7207 eLink.addClass('active');
7208 eLink.parent().attr("aria-expanded", "true");
7209 eLink.find('i').eq(0).removeClass('icon-primary-collapsed');
7210 eLink.find('i').eq(0).addClass('icon-primary-expanded');
7214 //function finds the main root-item from particular tree-group
7215 var findRoot = function (elem) {
7220 if (elem.attr("b2b-ml-nav") === "middleNode" || elem.attr("b2b-ml-nav") === "endNode") {
7221 parentE = elem.parent().parent();
7225 if (parentE.attr("b2b-ml-nav") === "rootNode") {
7231 //finds the last visible node of the previous tree-group
7232 var findPreActive = function (elem) {
7233 if (!(elem.hasClass("active"))) {
7236 var childElems = angular.element(elem[0].nextElementSibling.children);
7237 lastE = angular.element(childElems[childElems.length - 1]);
7238 if (lastE.attr("b2b-ml-nav") === "middleNode" && lastE.find('a').eq(0).hasClass('active')) {
7239 findPreActive(lastE.find('a').eq(0));
7244 //find above visible link
7245 var findUp = function (elem) {
7246 if (elem[0].previousElementSibling !== null) {
7247 upE = angular.element(elem[0].previousElementSibling);
7249 upE = elem.parent().parent();
7251 if (isRoot(elem) || (upE.attr('b2b-ml-nav') === "middleNode" && upE[0] !== elem.parent().parent()[0])) {
7252 findPreActive(upE.find('a').eq(0));
7255 //find below visible link
7256 var findDown = function (elem) {
7257 if (elem.hasClass('active')) {
7258 downE = elem.next().find('li').eq(0);
7260 if (elem.parent().next().length !== 0) {
7261 downE = elem.parent().next().eq(0);
7263 if (elem.parent().parent().parent().next().length !== 0) {
7264 downE = elem.parent().parent().parent().next().eq(0);
7267 downE = elem.parent().eq(0);
7271 //finds last root-group element of the tree
7272 var findEnd = function (elem) {
7274 endE = angular.element(rootE.parent()[0].children[rootE.parent()[0].children.length - 1]);
7276 //finds first root element of tree
7277 var findHome = function (elem) {
7279 homeE = angular.element(rootE.parent()[0].children[0]);
7281 element.bind('click', function (e) {
7282 if(element.attr("b2b-ml-nav") !== "endNode") {
7285 if (rootE==undefined){
7288 var currSelected = rootE.parent()[0].querySelector('.selected');
7290 angular.element(currSelected).removeClass('selected');
7292 element.find('a').eq(0).addClass('selected');
7293 e.stopPropagation();
7295 element.bind('focus', function (e) {
7296 if(element.attr("b2b-ml-nav") !== "endNode") {
7297 if(element.find('a').eq(0).hasClass('active')) {
7298 element.attr("aria-expanded", true);
7301 element.attr("aria-expanded", false);
7306 //Keyboard functionality approach:
7308 //set set tabindex -1 on the current focus element
7309 //find the next element to be focussed, set tabindex 0 and throw focus
7310 element.bind('keydown', function (evt) {
7311 switch (evt.keyCode) {
7312 case keymap.KEY.ENTER:
7313 case keymap.KEY.SPACE:
7314 element.triggerHandler('click');
7315 evt.stopPropagation();
7316 evt.preventDefault();
7318 case keymap.KEY.END:
7319 evt.preventDefault();
7320 element.attr('tabindex', -1);
7322 endE.eq(0).attr('tabindex', 0);
7324 evt.stopPropagation();
7326 case keymap.KEY.HOME:
7327 evt.preventDefault();
7328 element.attr('tabindex', -1);
7330 homeE.eq(0).attr('tabindex', 0);
7332 evt.stopPropagation();
7334 case keymap.KEY.LEFT:
7335 evt.preventDefault();
7336 if (!isRoot(element)) {
7337 element.attr('tabindex', -1);
7338 parentE = element.parent().parent();
7339 parentE.eq(0).attr('tabindex', 0);
7341 parentE.eq(0).triggerHandler('click');
7343 if (element.find('a').eq(0).hasClass('active')) {
7344 element.triggerHandler('click');
7347 evt.stopPropagation();
7350 evt.preventDefault();
7351 if (!(isRoot(element) && element[0].previousElementSibling === null)) {
7352 element.attr('tabindex', -1);
7354 upE.eq(0).attr('tabindex', 0);
7357 evt.stopPropagation();
7359 case keymap.KEY.RIGHT:
7360 evt.preventDefault();
7361 if (element.attr("b2b-ml-nav") !== "endNode") {
7362 if (!element.find('a').eq(0).hasClass('active')) {
7363 element.triggerHandler('click');
7365 element.attr('tabindex', -1);
7366 findDown(element.find('a').eq(0));
7367 downE.eq(0).attr('tabindex', 0);
7370 evt.stopPropagation();
7372 case keymap.KEY.DOWN:
7373 evt.preventDefault();
7374 element.attr('tabindex', -1);
7375 if (!(element.attr("b2b-ml-nav") === "middleNode" && element.find('a').eq(0).hasClass('active')) && (element.next().length === 0)) {
7376 if(element.parent().parent().attr("b2b-ml-nav") !== "rootNode" && element.parent().parent()[0].nextElementSibling !== null)
7378 findDown(element.find('a').eq(0));
7379 downE.eq(0).attr('tabindex', 0);
7381 evt.stopPropagation();
7385 if (!(rootE.next().length === 0)) {
7386 rootE.next().eq(0).attr('tabindex', 0);
7387 rootE.next()[0].focus();
7389 rootE.eq(0).attr('tabindex', 0);
7392 evt.stopPropagation();
7395 findDown(element.find('a').eq(0));
7396 downE.eq(0).attr('tabindex', 0);
7398 evt.stopPropagation();
7409 * @name Tabs, tables & accordions.att:multipurposeExpander
7412 * <file src="src/multipurposeExpander/docs/readme.md" />
7415 * <!--With Close Other -->
7416 * <b2b-expander-group close-others="true">
7417 * <b2b-expanders class="mpc-expanders" is-open="testmpc">
7418 * <b2b-expander-heading ng-class=" { 'b2b-toggle-header-active': !testmpc, 'b2b-toggle-header-inactive': testmpc } ">Heading content goes here</b2b-expander-heading>
7419 * <b2b-expander-body>
7420 <p>body content goes here</p>
7421 </b2b-expander-body>
7423 * </b2b-expander-group>
7425 * <!-- Without Close Other -->
7426 * <b2b-expanders class="mpc-expanders" is-open="testmpc2">
7427 * <b2b-expander-heading ng-class=" { 'b2b-toggle-header-active': !testmpc2, 'b2b-toggle-header-inactive': testmpc2 } ">Heading content goes here</b2b-expander-heading>
7428 * <b2b-expander-body>
7429 <p>body content goes here</p>
7430 </b2b-expander-body>
7434 * <section id="code">
7435 <example module="b2b.att.multipurposeExpander">
7436 <file src="src/multipurposeExpander/docs/demo.html" />
7437 <file src="src/multipurposeExpander/docs/demo.js" />
7443 angular.module('b2b.att.multipurposeExpander', ['b2b.att', 'b2b.att.collapse'])
7444 .directive('b2bExpanderGroup', function () {
7448 template: "<ng-transclude></ng-transclude>",
7449 controller:['$scope','$attrs', function($scope,$attrs){
7452 this.scope = $scope;
7454 this.addGroup = function (groupScope) {
7456 groupScope.index = this.groups.length;
7457 this.groups.push(groupScope);
7458 if(this.groups.length > 0){
7461 groupScope.$on('$destroy', function () {
7462 that.removeGroup(groupScope);
7466 this.closeOthers = function (openGroup) {
7467 var closeOthers = angular.isDefined($attrs.closeOthers);
7468 if (closeOthers && !$scope.forceExpand) {
7469 angular.forEach(this.groups, function (group) {
7470 if (group !== openGroup) {
7471 group.isOpen = false;
7475 if (this.groups.indexOf(openGroup) === (this.groups.length - 1) && $scope.forceExpand) {
7476 $scope.forceExpand = false;
7479 this.removeGroup = function (group) {
7480 var index = this.groups.indexOf(group);
7482 this.groups.splice(this.groups.indexOf(group), 1);
7490 .directive('b2bExpanders', function () {
7494 require:['b2bExpanders','?^b2bExpanderGroup'],
7496 scope:{isOpen:'=?'},
7497 template: "<div ng-transclude></div>",
7498 controller: ['$scope', function ($scope){
7499 var bodyScope = null;
7500 var expanderScope = null;
7501 this.isOpened = function(){
7510 this.setScope = function (scope) {
7512 bodyScope.isOpen = $scope.isOpen;
7514 this.setExpanderScope = function (scope) {
7515 expanderScope = scope;
7517 this.toggle = function () {
7518 $scope.isOpen = bodyScope.isOpen = !bodyScope.isOpen;
7519 return bodyScope.isOpen;
7522 this.watchToggle = function(io){
7523 bodyScope.isOpen = io;
7524 expanderScope.updateIcons(io);
7527 link: function (scope, elem, attr, myCtrl)
7529 //scope.isOpen = false;
7531 myCtrl[1].addGroup(scope);
7533 scope.$watch('isOpen', function(val){
7534 myCtrl[0].watchToggle(scope.isOpen);
7535 if(val && myCtrl[1]){
7536 myCtrl[1].closeOthers(scope);
7543 .directive('b2bExpanderHeading', function () {
7545 require: "^b2bExpanders",
7550 template: "<div style='padding:10px 10px 10px 0 !important' ng-transclude></div>"
7554 .directive('b2bExpanderBody', function () {
7557 require: "^b2bExpanders",
7561 template: "<div b2b-collapse='!isOpen' ><div ng-transclude></div></div>",
7562 link: function (scope, elem, attr, myCtrl) {
7563 scope.isOpen = false;
7564 myCtrl.setScope(scope);
7569 .directive('b2bExpanderToggle', function () {
7572 require: "^b2bExpanders",
7578 link: function (scope, element, attr, myCtrl)
7580 myCtrl.setExpanderScope(scope);
7581 var isOpen = myCtrl.isOpened();
7583 scope.setIcon = function () {
7584 element.attr("role", "button");
7586 if (scope.expandIcon && scope.collapseIcon)
7589 element.removeClass(scope.expandIcon);
7590 element.addClass(scope.collapseIcon);
7592 element.attr("aria-expanded", "true");
7595 element.removeClass(scope.collapseIcon);
7596 element.addClass(scope.expandIcon);
7598 element.attr("aria-expanded", "false");
7603 element.bind('click', function (){
7606 scope.updateIcons = function(nStat){
7610 scope.toggleit = function (){
7611 isOpen = myCtrl.toggle();
7621 * @name Messages, modals & alerts.att:notesMessagesAndErrors
7624 * <file src="src/notesMessagesAndErrors/docs/readme.md" />
7630 * <section id="code">
7631 <example module="b2b.att">
7632 <file src="src/notesMessagesAndErrors/docs/demo.html" />
7633 <file src="src/notesMessagesAndErrors/docs/demo.js" />
7638 angular.module('b2b.att.notesMessagesAndErrors', []);
7641 * @name Template.att:Notification Card
7644 * <file src="src/notificationCardTemplate/docs/readme.md" />
7647 * <section id="code">
7648 <b>HTML + AngularJS</b>
7649 <example module="b2b.att">
7650 <file src="src/notificationCardTemplate/docs/demo.html" />
7651 <file src="src/notificationCardTemplate/docs/demo.js" />
7656 angular.module('b2b.att.notificationCardTemplate', [])
7660 * @name Template.att:Order Confirmation Template
7663 * <file src="src/orderConfirmationTemplate/docs/readme.md" />
7666 * <section id="code">
7667 <b>HTML + AngularJS</b>
7668 <example module="b2b.att">
7669 <file src="src/orderConfirmationTemplate/docs/demo.html" />
7670 <file src="src/orderConfirmationTemplate/docs/demo.js" />
7675 angular.module('b2b.att.orderConfirmationTemplate', []);
7679 * @name Navigation.att:pagination
7682 * <file src="src/pagination/docs/readme.md" />
7683 * @param {int} total-pages - Total # of pages, set in your controller $scope
7684 * @param {int} current-page - Current selected page, set in your controller $scope
7685 * @param {function} click-handler - Handler function on click of page number, defined in your controller $scope
7686 * @param {string} input-id - _UNIQUE ID_ __MUST__ be provided for 508 compliance, set in your HTML as static text
7687 * @param {string} input-class - optional class that can be given to use for the go to page container
7690 * <div b2b-pagination="" input-id="goto-page-2" total-pages="totalPages1" current-page="currentPage1" click-handler="customHandler"></div>
7693 * <section id="code">
7694 <example module="b2b.att">
7695 <file src="src/pagination/docs/demo.html" />
7696 <file src="src/pagination/docs/demo.js" />
7701 angular.module('b2b.att.pagination', ['b2b.att.utilities', 'ngTouch'])
7702 .directive('b2bPagination', ['b2bUserAgent', 'keymap', '$window', '$timeout', function (b2bUserAgent, keymap, $window, $timeout) {
7713 templateUrl: 'b2bTemplate/pagination/b2b-pagination.html',
7714 link: function (scope, elem, attr) {
7715 scope.isMobile = b2bUserAgent.isMobile();
7716 scope.notMobile = b2bUserAgent.notMobile();
7719 scope.inputClass = attr.inputClass;
7720 scope.droppableAttribute = scope.isDroppable ? true : false;
7721 scope.$watch('totalPages', function (value) {
7722 if (angular.isDefined(value) && value !== null) {
7725 scope.totalPages = 1;
7729 for (var i = 1; i <= value; i++) {
7730 scope.pages.push(i);
7732 } else if (value > 10) {
7733 var midVal = Math.ceil(value / 2);
7734 scope.pages = [midVal - 2, midVal - 1, midVal, midVal + 1, midVal + 2];
7736 if(scope.currentPage === undefined || scope.currentPage === 1)
7738 currentPageChanged(1);
7742 scope.$watch('currentPage', function (value) {
7743 currentPageChanged(value);
7744 callbackHandler(value);
7746 var callbackHandler = function (num) {
7747 if (angular.isFunction(scope.clickHandler)) {
7748 scope.clickHandler(num);
7751 var getBoundary = function(value){
7752 if ( value < 100 ) {
7754 } else if ( 100 <= value && value < 1000 ) {
7756 } else if ( 1000 <= value ) {
7762 function currentPageChanged(value) {
7763 if (angular.isDefined(value) && value !== null) {
7764 if (!value || value < 1) {
7767 if (value > scope.totalPages) {
7768 value = scope.totalPages;
7770 if (scope.currentPage !== value) {
7771 scope.currentPage = value;
7772 callbackHandler(scope.currentPage);
7774 if (scope.totalPages > 10) {
7775 var val = parseInt(value);
7776 scope.boundary = getBoundary(val);
7777 if (val <= 6) { // Left (first) section
7778 scope.pages = [1, 2, 3, 4, 5, 6, 7];
7779 } else if ( val <= (scope.totalPages - scope.boundary) ) { // Middle section
7780 if ( 7 <= val && val < 9 ) {
7781 if(scope.totalPages < 100) {
7782 scope.pages = [val - 3, val - 2, val - 1, val, val + 1, val + 2];
7783 } else if(scope.totalPages < 1000) {
7784 scope.pages = [val - 2, val - 1, val, val + 1, val + 2];
7785 } else if(scope.totalPages < 1000) {
7786 scope.pages = [val - 2, val - 1, val, val + 1, val + 2];
7788 } else if ( 9 <= val && val < 100 ) {
7789 scope.pages = [val - 3, val - 2, val - 1, val, val + 1, val + 2];
7790 } else if ( 100 <= val && val < 1000 ) {
7791 scope.pages = [val - 2, val - 1, val, val + 1];
7792 } else if ( 1000 <= val ) {
7793 scope.pages = [val - 1, val, val + 1];
7795 } else if ( (scope.totalPages - scope.boundary) < val ) { // Right (last) section
7797 scope.pages = [scope.totalPages - 5, scope.totalPages - 4, scope.totalPages - 3, scope.totalPages - 2, scope.totalPages - 1, scope.totalPages];
7798 } else if ( 100 <= val && val < 1000 ) {
7799 scope.pages = [scope.totalPages - 4, scope.totalPages - 3, scope.totalPages - 2, scope.totalPages - 1, scope.totalPages];
7800 } else if ( 1000 <= val ) {
7801 scope.pages = [scope.totalPages - 3, scope.totalPages - 2, scope.totalPages - 1, scope.totalPages];
7805 if (scope.isMobile) {
7806 var inWidth = $window.innerWidth;
7808 if (inWidth <= 400) {
7810 } else if (inWidth > 400 && inWidth < 500) {
7812 } else if (inWidth >= 500 && inWidth < 600) {
7814 } else if (inWidth >= 600 && inWidth < 700) {
7816 } else if (inWidth >= 700 && inWidth < 800) {
7820 var val = parseInt(value);
7822 scope.meanVal = Math.floor(viewLimit / 2);
7823 var lowerLimit = (val - scope.meanVal) < 1 ? 1 : val - scope.meanVal;
7824 var upperLimit = (lowerLimit + viewLimit - 1) > scope.totalPages ? scope.totalPages : lowerLimit + viewLimit - 1;
7826 for (var i = lowerLimit; i <= upperLimit; i++) {
7827 scope.pages.push(i);
7832 scope.gotoKeyClick = function (keyEvent) {
7833 if (keyEvent.which === keymap.KEY.ENTER) {
7834 scope.gotoBtnClick()
7837 scope.gotoBtnClick = function () {
7838 currentPageChanged(parseInt(scope.gotoPage));
7839 callbackHandler(scope.currentPage);
7840 var qResult = elem[0].querySelector('button');
7841 angular.element(qResult).attr('disabled','true');
7842 $timeout(function(){
7843 elem[0].querySelector('.b2b-pager__item--active').focus();
7845 scope.gotoPage = null;
7847 scope.onfocusIn = function(evt)
7849 var qResult = elem[0].querySelector('button');
7850 angular.element(qResult).removeAttr('disabled');
7852 scope.onfocusOut = function(evt)
7854 if(evt.target.value === "")
7856 var qResult = elem[0].querySelector('button');
7857 angular.element(qResult).attr('disabled','true');
7860 scope.next = function (event) {
7861 if (event != undefined) {
7862 event.preventDefault();
7864 if (scope.currentPage < scope.totalPages) {
7865 scope.currentPage += 1;
7866 callbackHandler(scope.currentPage);
7869 scope.prev = function (event) {
7870 if (event != undefined) {
7871 event.preventDefault();
7873 if (scope.currentPage > 1) {
7874 scope.currentPage -= 1;
7875 callbackHandler(scope.currentPage);
7878 scope.selectPage = function (value, event) {
7879 event.preventDefault();
7880 scope.currentPage = value;
7881 scope.focusedPage = value;
7882 callbackHandler(scope.currentPage);
7884 scope.checkSelectedPage = function (value) {
7885 if (scope.currentPage === value) {
7890 scope.isFocused = function (page) {
7891 return scope.focusedPage === page;
7899 * @name Navigation.att:paneSelector
7902 * <file src="src/paneSelector/docs/readme.md" />
7905 * Please refer demo.html tab in Example section below.
7909 <b>HTML + AngularJS</b>
7910 <example module="b2b.att">
7911 <file src="src/paneSelector/docs/demo.html" />
7912 <file src="src/paneSelector/docs/demo.js" />
7917 angular.module('b2b.att.paneSelector', ['b2b.att.tabs', 'b2b.att.utilities'])
7919 .filter('paneSelectorSelectedItemsFilter', [function () {
7920 return function (listOfItemsArray) {
7922 if (!listOfItemsArray) {
7923 listOfItemsArray = [];
7926 var returnArray = [];
7928 for (var i = 0; i < listOfItemsArray.length; i++) {
7929 if (listOfItemsArray[i].isSelected) {
7930 returnArray.push(listOfItemsArray[i]);
7938 .filter('paneSelectorFetchChildItemsFilter', [function () {
7939 return function (listOfItemsArray) {
7941 if (!listOfItemsArray) {
7942 listOfItemsArray = [];
7945 var returnArray = [];
7947 for (var i = 0; i < listOfItemsArray.length; i++) {
7948 for (var j = 0; j < listOfItemsArray[i].childItems.length; j++) {
7949 returnArray.push(listOfItemsArray[i].childItems[j]);
7957 .directive('b2bPaneSelector', [function () {
7961 templateUrl: 'b2bTemplate/paneSelector/paneSelector.html',
7967 .directive('b2bPaneSelectorPane', [ function () {
7971 templateUrl: 'b2bTemplate/paneSelector/paneSelectorPane.html',
7977 .directive('b2bTabVertical', ['$timeout', 'keymap', function ($timeout, keymap) {
7981 link: function (scope, element, attr, b2bTabCtrl) {
7987 // retreive the isolateScope
7988 var iScope = angular.element(element).isolateScope();
7990 $timeout(function () {
7991 angular.element(element[0].querySelector('a')).unbind('keydown');
7992 angular.element(element[0].querySelector('a')).bind('keydown', function (evt) {
7994 if (!(evt.keyCode)) {
7995 evt.keyCode = evt.which;
7998 switch (evt.keyCode) {
7999 case keymap.KEY.DOWN:
8000 evt.preventDefault();
8005 evt.preventDefault();
8006 iScope.previousKey();
8018 * @name Forms.att:phoneNumberInput
8021 * <file src="src/phoneNumberInput/docs/readme.md" />
8024 <form name="userForm1">
8025 <div class="form-row" ng-class="{'error':!userForm1.text.$valid && userForm1.text.$dirty}">
8026 <label>Phone Mask<span style="color:red">*</span>: (npa) nxx-line Model Value: {{mask.text}}</label>
8028 <input b2b-phone-mask="phoneMask" name="text" ng-model="mask.text" type="text" placeholder="e.g. (123) 456-7890" title="Phone Number" class="b2b-phone-mask-input" required />
8029 <div ng-messages="userForm1.text.$error" class="error-msg" aria-live="polite" aria-atomic="true">
8030 <span ng-message="required" role="alert">This field is mandatory!</span>
8031 <span ng-message="invalidPhoneNumber" role="alert">Please enter valid phone number!</span>
8032 <span ng-message="mask" role="alert">Please enter valid phone number!</span>
8039 * <section id="code">
8040 <example module="b2b.att">
8041 <file src="src/phoneNumberInput/docs/demo.html" />
8042 <file src="src/phoneNumberInput/docs/demo.js" />
8047 angular.module('b2b.att.phoneNumberInput', ['ngMessages', 'b2b.att.utilities'])
8048 .constant("CoreFormsUiConfig", {
8049 phoneMask: '(___) ___-____',
8050 phoneMaskDot: '___.___.____',
8051 phoneMaskHyphen: '___-___-____'
8053 .directive('b2bPhoneMask', ['$parse', 'CoreFormsUiConfig', 'keymap', 'b2bUserAgent', function ($parse, CoreFormsUiConfig, keymap, b2bUserAgent) {
8059 link: function (scope, iElement, iAttrs, ctrl) {
8062 var validPhoneNumber = false;
8063 var currentKey = '';
8064 if (b2bUserAgent.isMobile()) {
8065 mask = "__________";
8067 switch (iAttrs.b2bPhoneMask) {
8069 mask = CoreFormsUiConfig.phoneMask;
8071 case "phoneMaskDot":
8072 mask = CoreFormsUiConfig.phoneMaskDot;
8074 case "phoneMaskHyphen":
8075 mask = CoreFormsUiConfig.phoneMaskHyphen;
8078 mask = CoreFormsUiConfig.phoneMask;
8081 iElement.attr("maxlength", mask.length);
8082 var checkValidity = function (unmaskedValue, rawValue) {
8084 if (angular.isUndefined(rawValue) || rawValue === '') {
8086 } else if (unmaskedValue) {
8087 valid = (unmaskedValue.length === 10);
8089 ctrl.$setValidity('invalidPhoneNumber', validPhoneNumber);
8090 ctrl.$setValidity('mask', valid);
8093 var handleKeyup = function (evt) {
8095 if (evt && evt.keyCode === keymap.KEY.SHIFT) {
8099 var index, formattedNumber;
8100 if (ctrl.$modelValue) {
8101 formattedNumber = ctrl.$modelValue;
8103 formattedNumber = iElement.val();
8105 if (!formattedNumber.length && currentKey === '') {
8108 var maskLength, inputNumbers, maskArray, tempArray, maskArrayLength;
8110 maskArray = mask.split("");
8111 maskArrayLength = maskArray.length;
8112 maskLength = formattedNumber.substring(0, mask.length);
8113 inputNumbers = formattedNumber.replace(/[^0-9]/g, "").split("");
8114 for (index = 0; index < maskArrayLength; index++) {
8115 tempArray.push(maskArray[index] === "_" ? inputNumbers.shift() : maskArray[index]);
8116 if (inputNumbers.length === 0) {
8120 formattedNumber = tempArray.join("");
8121 if (formattedNumber === '(') {
8122 formattedNumber = '';
8125 if ( (angular.isDefined(evt) && evt.which) && currentKey !== '') {
8126 if (maskArray[0] === currentKey && formattedNumber === '') {
8127 formattedNumber = '(';
8128 } else if (evt.which === keymap.KEY.SPACE && currentKey === ' ') {
8129 formattedNumber = formattedNumber + ') ';
8130 } else if (maskArray[0] === currentKey && formattedNumber === '') {
8131 formattedNumber = formattedNumber + currentKey;
8132 } else if (maskArray[formattedNumber.length] === currentKey) {
8133 formattedNumber = formattedNumber + currentKey;
8138 ctrl.$setViewValue(formattedNumber);
8140 return formattedNumber;
8144 // since we are only allowing 0-9, why even let the keypress go forward?
8145 // also added in delete... in case they want to delete :)
8146 var handlePress = function (e) {
8148 if ((e.which < 48 || e.which > 57) && (e.which < 96 || e.which > 105)) {
8149 if (e.which !== keymap.KEY.BACKSPACE && e.which !== keymap.KEY.TAB && e.which !== keymap.KEY.DELETE && e.which !== keymap.KEY.ENTER && e.which !== keymap.KEY.LEFT && e.which !== keymap.KEY.RIGHT &&
8151 (!(e.ctrlKey) && (e.which !== '118' || e.which !== '86')) &&
8153 (!(e.ctrlKey) && (e.which !== '99' || e.which !== '67')) &&
8155 (!(e.ctrlKey) && (e.which !== '120' || e.which !== '88')) &&
8156 /* 229 key code will sent as placeholder key for andriod devices */
8157 (e.which != 229 )) {
8158 e.preventDefault ? e.preventDefault() : e.returnValue = false;
8159 validPhoneNumber = false;
8162 validPhoneNumber = true;
8169 // i moved this out because i thought i might need focus as well..
8170 // to handle setting the model as the view changes
8171 var parser = function (fromViewValue) {
8172 var letters = /^[A-Za-z]+$/;
8173 var numbers = /^[0-9]+$/;
8174 if (angular.isUndefined(fromViewValue) || fromViewValue === '') {
8175 validPhoneNumber = true;
8177 if (fromViewValue.match(letters)) {
8178 validPhoneNumber = false;
8180 if (fromViewValue.match(numbers)) {
8181 validPhoneNumber = true;
8185 if (fromViewValue && fromViewValue.length > 0) {
8186 clean = fromViewValue.replace(/[^0-9]/g, '');
8188 checkValidity(clean, fromViewValue);
8192 //to handle reading the model and formatting it
8193 var formatter = function (fromModelView) {
8195 checkValidity(fromModelView);
8196 if (fromModelView) {
8197 input = handleKeyup();
8202 var setCurrentKey = function (e) {
8213 if (e.shiftKey === true) {
8218 if (e.shiftKey === true) {
8228 if (angular.isDefined(scope.ngModel)) {
8229 parser(scope.ngModel);
8232 ctrl.$parsers.push(parser);
8233 ctrl.$formatters.push(formatter);
8234 iElement.bind('keyup', handleKeyup);
8235 iElement.bind('keydown', handlePress);
8241 * @name Template.att:Profile Blocks
8244 * <file src="src/profileBlockTemplate/docs/readme.md" />
8246 * <section id="code">
8247 <example module="b2b.att">
8248 <file src="src/profileBlockTemplate/docs/demo.html" />
8249 <file src="src/profileBlockTemplate/docs/demo.js" />
8255 angular.module('b2b.att.profileBlockTemplate', [])
8261 * @name Layouts.att:profileCard
8264 * <file src="src/profileCard/docs/readme.md" />
8267 * <b2b-profile-card></b2b-profile-card>
8271 <example module="b2b.att">
8272 <file src="src/profileCard/docs/demo.html" />
8273 <file src="src/profileCard/docs/demo.js" />
8278 angular.module('b2b.att.profileCard', ['b2b.att'])
8279 .constant('profileStatus',{
8286 status: "Deactivated",
8302 role: "COMPANY ADMINISTRATOR"
8305 .directive('b2bProfileCard',['$http','$q','profileStatus', function($http,$q,profileStatus) {
8309 templateUrl: function(element, attrs){
8311 return 'b2bTemplate/profileCard/profileCard.html';
8314 return 'b2bTemplate/profileCard/profileCard-addUser.html';
8321 link: function(scope, elem, attr){
8322 scope.characterLimit = parseInt(attr.characterLimit, 10) || 25;
8323 scope.shouldClip = function(str) {
8324 return str.length > scope.characterLimit;
8327 scope.showEmailTooltip = false;
8330 function isImage(src) {
8331 var deferred = $q.defer();
8332 var image = new Image();
8333 image.onerror = function() {
8334 deferred.reject(false);
8336 image.onload = function() {
8337 deferred.resolve(true);
8339 if(src !== undefined && src.length>0 ){
8342 deferred.reject(false);
8344 return deferred.promise;
8348 isImage(scope.profile.img).then(function(img) {
8351 var splitName=(scope.profile.name).split(' ');
8353 for(var i=0;i<splitName.length;i++){
8354 scope.initials += splitName[i][0];
8356 if(scope.profile.role.toUpperCase() === profileStatus.role){
8359 var profileState=profileStatus.status[scope.profile.state.toUpperCase()];
8361 scope.profile.state=profileStatus.status[scope.profile.state.toUpperCase()].status;
8362 scope.colorIcon=profileStatus.status[scope.profile.state.toUpperCase()].color;
8363 if(scope.profile.state.toUpperCase()===profileStatus.status.PENDING.status.toUpperCase()||scope.profile.state.toUpperCase()===profileStatus.status.LOCKED.status.toUpperCase()){
8364 scope.profile.lastLogin=scope.profile.state;
8367 var today=new Date().getTime();
8368 var lastlogin=new Date(scope.profile.lastLogin).getTime();
8369 var diff=(today-lastlogin)/(1000*60*60*24);
8371 scope.profile.lastLogin="Today";
8374 scope.profile.lastLogin="Yesterday";
8382 * @name Forms.att:radios
8385 * <file src="src/radios/docs/readme.md" />
8390 * @param {boolean} refreshRadioGroup - A trigger that recalculates and updates the accessibility roles on radios in a group.
8394 <b>HTML + AngularJS</b>
8395 <example module="b2b.att">
8396 <file src="src/radios/docs/demo.html" />
8397 <file src="src/radios/docs/demo.js" />
8401 angular.module('b2b.att.radios', ['b2b.att.utilities'])
8402 .directive('b2bRadioGroupAccessibility', ['$timeout', 'b2bUserAgent', function($timeout, b2bUserAgent) {
8406 refreshRadioGroup: "=",
8408 link: function(scope, ele, attr) {
8410 var roleRadioElement, radioProductSelectElement, radioInputTypeElement;
8412 $timeout(calculateNumberOfRadio);
8414 scope.$watch('refreshRadioGroup', function(value) {
8415 if (value === true) {
8416 addingRoleAttribute();
8417 $timeout(calculateNumberOfRadio);
8418 scope.refreshRadioGroup = false;
8425 function calculateNumberOfRadio() {
8426 roleRadioElement = ele[0].querySelectorAll('[role="radio"]');
8428 radioProductSelectElement = ele[0].querySelectorAll('[role="radiogroup"] li.radio-box');
8430 radioInputTypeElement = ele[0].querySelectorAll('input[type="radio"]');
8432 for (var i = 0; i < radioInputTypeElement.length; i++) {
8433 var isChecked = radioInputTypeElement[i].checked ? 'true' : 'false';
8434 var isDisabled = radioInputTypeElement[i].disabled ? 'true' : 'false';
8435 var numOfx = i + 1 + ' of ' + radioInputTypeElement.length;
8436 angular.element(roleRadioElement[i]).attr({
8437 'aria-checked': isChecked,
8438 'aria-disabled': isDisabled,
8439 'data-opNum': numOfx
8441 if (b2bUserAgent.notMobile()) {
8442 angular.element(roleRadioElement[i]).removeAttr("role");
8445 if (radioProductSelectElement.length) {
8446 isChecked === 'true' ? angular.element(radioProductSelectElement[i]).addClass('active') : angular.element(radioProductSelectElement[i]).removeClass('active');
8449 if (/Android/i.test(navigator.userAgent)) {
8450 angular.element(roleRadioElement[i]).append('<span class="hidden-spoken">. ' + numOfx + '.</span>');
8454 angular.element(radioInputTypeElement[i]).bind('click', radioStateChangeonClick);
8459 function addingRoleAttribute() {
8460 for (var i = 0; i < radioInputTypeElement.length; i++) {
8461 if (b2bUserAgent.notMobile()) {
8462 angular.element(roleRadioElement[i]).attr("role", "radio");
8467 function radioStateChangeonClick() {
8468 for (var i = 0; i < radioInputTypeElement.length; i++) {
8469 var isChecked = radioInputTypeElement[i].checked ? 'true' : 'false';
8470 var isDisabled = radioInputTypeElement[i].disabled ? 'true' : 'false';
8471 if (radioProductSelectElement.length) {
8472 isChecked === 'true' ? angular.element(radioProductSelectElement[i]).addClass('active') : angular.element(radioProductSelectElement[i]).removeClass('active');
8474 angular.element(roleRadioElement[i]).attr({
8475 'aria-checked': isChecked,
8476 'aria-disabled': isDisabled
8488 * @name Forms.att:searchField
8491 * <file src="src/searchField/docs/readme.md" />
8494 * <div b2b-search-field dropdown-list="listdata" on-click-callback="clickFn(value)" class="span12" input-model='typedString' config-obj='keyConfigObj'></div>
8498 <example module="b2b.att">
8499 <file src="src/searchField/docs/demo.html" />
8500 <file src="src/searchField/docs/demo.js" />
8505 angular.module('b2b.att.searchField', ['b2b.att.utilities', 'b2b.att.position'])
8506 .filter('b2bFilterInput', [function() {
8507 return function(list, str, keyArray, displayListKey, isContainsSearch, searchSeperator) {
8510 var searchCondition;
8511 var conditionCheck = function(searchSeperator, listItem, displayListKey, splitString) {
8512 var displayTitle = null;
8514 for (var i = 0; i < displayListKey.length; i++) {
8516 displayTitle = listItem[displayListKey[i]].toLowerCase().indexOf(splitString[i].toLowerCase()) > -1;
8518 displayTitle = (splitString[i]) ? displayTitle && listItem[displayListKey[i]].toLowerCase().indexOf(splitString[i].toLowerCase().trim()) > -1 : displayTitle;
8522 angular.forEach(displayListKey, function(value) {
8523 if (!displayTitle) {
8524 displayTitle = listItem[value];
8526 displayTitle = displayTitle + (listItem[value] ? searchSeperator + ' ' + listItem[value] : '');
8530 return displayTitle;
8532 angular.forEach(list, function(listItem) {
8533 var splitString = str.indexOf(searchSeperator) > -1 ? str.split(searchSeperator) : false;
8534 var displayList = conditionCheck(searchSeperator, listItem, displayListKey, splitString)
8535 for (var i = 0; i < keyArray.length; i++) {
8536 searchLabel = keyArray[i];
8537 if (listItem[searchLabel]) {
8538 if (isContainsSearch) {
8539 var displaySearchList = listItem[searchLabel].toLowerCase().indexOf(str.toLowerCase()) > -1;
8540 if (splitString.length > 1) {
8541 displaySearchList = (splitString.length <= keyArray.length) ? displayList : false;
8543 searchCondition = displaySearchList;
8545 searchCondition = listItem[searchLabel].match(new RegExp('^' + str, 'gi'));
8547 if (searchCondition) {
8549 'title': conditionCheck(searchSeperator, listItem, displayListKey),
8550 'valueObj': listItem
8559 }]).directive('b2bSearchField', ['$filter', 'b2bFilterInputFilter', 'keymap', '$documentBind', '$isElement', '$document', 'events', '$timeout', function($filter, b2bFilterInput, keymap, $documentBind, $isElement, $document, events, $timeout) {
8563 dataList: '=dropdownList',
8564 onClickCallback: '&',
8572 templateUrl: 'b2bTemplate/searchField/searchField.html',
8573 controller: ['$scope', function($scope) {
8574 this.searchKeyArray = [];
8575 if ($scope.configObj.searchKeys) {
8576 this.searchKeyArray = $scope.configObj.searchKeys;
8578 if (angular.isUndefined($scope.disabled)) {
8579 $scope.disabled = false;
8581 this.triggerInput = function(searchString) {
8582 $scope.originalInputModel = searchString;
8583 if (searchString === '') {
8584 $scope.currentIndex = -1;
8585 $scope.filterList = [];
8586 $scope.showListFlag = false;
8587 } else if (searchString !== '' && !$scope.isFilterEnabled) {
8588 $scope.filterList = $filter('b2bFilterInput')($scope.dataList, searchString, this.searchKeyArray, $scope.configObj.displayListDataKey, $scope.configObj.isContainsSearch, $scope.configObj.searchSeperator);
8589 $scope.showListFlag = true;
8592 this.denyRegex = function() {
8593 return $scope.inputDeny;
8596 link: function(scope, elem) {
8597 scope.isFilterEnabled = false;
8598 scope.showListFlag = false;
8599 scope.currentIndex = -1;
8600 scope.setCurrentIdx = function(idx) {
8601 scope.currentIndex = idx;
8603 scope.inputModel = scope.filterList[idx].title;
8604 scope.objModel = scope.filterList[idx];
8607 scope.isActive = function(index, dropdownLength) {
8608 scope.dropdownLength = dropdownLength;
8609 return scope.currentIndex === index;
8611 scope.selectItem = function(idx) {
8612 scope.setCurrentIdx(idx);
8613 scope.onClickCallback({
8614 value: scope.inputModel,
8615 objValue: scope.objModel
8617 scope.showListFlag = false;
8618 $timeout(function() {
8619 elem.find('div').find('input')[0].focus();
8622 scope.startSearch = function() {
8623 scope.onClickCallback({
8624 value: scope.inputModel,
8625 objValue: scope.objModel
8628 scope.selectPrev = function() {
8629 if (scope.currentIndex > 0 && scope.filterList.length > 0) {
8630 scope.currentIndex = scope.currentIndex - 1;
8631 scope.setCurrentIdx(scope.currentIndex);
8632 } else if (scope.currentIndex === 0) {
8633 scope.currentIndex = scope.currentIndex - 1;
8634 scope.inputModel = scope.originalInputModel;
8635 scope.isFilterEnabled = true;
8638 scope.selectNext = function() {
8639 if (scope.currentIndex < scope.configObj.noOfItemsDisplay - 1) {
8640 if (scope.currentIndex < scope.filterList.length - 1) {
8641 scope.currentIndex = scope.currentIndex + 1;
8642 scope.setCurrentIdx(scope.currentIndex);
8646 scope.selectCurrent = function() {
8647 scope.selectItem(scope.currentIndex);
8649 scope.selectionIndex = function(e) {
8650 switch (e.keyCode) {
8651 case keymap.KEY.DOWN:
8652 events.preventDefault(e);
8653 scope.isFilterEnabled = true;
8657 events.preventDefault(e);
8658 scope.isFilterEnabled = true;
8661 case keymap.KEY.ENTER:
8662 events.preventDefault(e);
8663 scope.isFilterEnabled = true;
8664 scope.selectCurrent();
8666 case keymap.KEY.ESC:
8667 events.preventDefault(e);
8668 scope.isFilterEnabled = false;
8669 scope.showListFlag = false;
8670 scope.inputModel = '';
8673 scope.isFilterEnabled = false;
8676 if (elem[0].querySelector('.filtercontainer')) {
8677 elem[0].querySelector('.filtercontainer').scrollTop = (scope.currentIndex - 1) * 35;
8680 scope.$watch('filterList', function(newVal, oldVal) {
8681 if (newVal !== oldVal) {
8682 scope.currentIndex = -1;
8685 scope.blurInput = function() {
8686 $timeout(function() {
8687 scope.showListFlag = false;
8690 var outsideClick = function(e) {
8691 var isElement = $isElement(angular.element(e.target), elem.find('ul').eq(0), $document);
8692 if (!isElement && document.activeElement.tagName.toLowerCase() !== 'input') {
8693 scope.showListFlag = false;
8697 $documentBind.click('showListFlag', outsideClick, scope);
8701 .directive('b2bSearchInput', [function() {
8704 require: ['ngModel', '^b2bSearchField'],
8705 link: function(scope, elem, attr, ctrl) {
8706 var ngModelCtrl = ctrl[0];
8707 var attSearchBarCtrl = ctrl[1];
8708 var REGEX = ctrl[1].denyRegex();
8709 var parser = function(viewValue) {
8710 attSearchBarCtrl.triggerInput(viewValue);
8713 ngModelCtrl.$parsers.push(parser);
8715 if (REGEX !== undefined || REGEX !== '') {
8716 elem.bind('input', function() {
8717 var inputString = ngModelCtrl.$viewValue && ngModelCtrl.$viewValue.replace(REGEX, '');
8718 if (inputString !== ngModelCtrl.$viewValue) {
8719 ngModelCtrl.$setViewValue(inputString);
8720 ngModelCtrl.$render();
8731 * @name Buttons, links & UI controls.att:Seek bar
8734 * <file src="src/seekBar/docs/readme.md" />
8737 * Horizontal Seek Bar
8738 * <b2b-seek-bar min="0" max="400" step="1" skip-interval="1" data-ng-model="horizontalSeekBarVal" style="width:180px; margin: auto;" on-drag-end="onDragEnd()" on-drag-init="onDragStart()"></b2b-seek-bar>
8741 * <b2b-seek-bar min="0" max="1" step="0.01" skip-interval="0.1" vertical data-ng-model="verticalSeekBarVal" style=" width: 6px; height: 180px; margin: auto;"></b2b-seek-bar>
8745 <b>HTML + AngularJS</b>
8746 <example module="b2b.att">
8747 <file src="src/seekBar/docs/demo.html" />
8748 <file src="src/seekBar/docs/demo.js" />
8753 angular.module('b2b.att.seekBar', ['b2b.att.utilities','b2b.att.position'])
8754 .constant('b2bSeekBarConfig', {
8760 .directive('b2bSeekBar', ['$documentBind', 'events', 'b2bSeekBarConfig', 'keymap', '$compile', function($documentBind, events, b2bSeekBarConfig, keymap, $compile) {
8765 templateUrl: 'b2bTemplate/seekBar/seekBar.html',
8770 link: function(scope, elm, attr, ngModelCtrl) {
8771 scope.isDragging = false;
8772 scope.verticalSeekBar = false;
8775 var step = b2bSeekBarConfig.step;
8776 var skipInterval = b2bSeekBarConfig.skipInterval;
8777 var knob = angular.element(elm[0].querySelector('.b2b-seek-bar-knob-container'));
8778 var seekBarKnob = angular.element(knob[0].querySelector('.b2b-seek-bar-knob'));
8779 var trackContainer = angular.element(elm[0].querySelector('.b2b-seek-bar-track-container'));
8780 var trackFill = angular.element(elm[0].querySelector('.b2b-seek-bar-track-fill'));
8781 var trackContainerRect = {};
8783 var trackFillOrderPositioning;
8785 if (angular.isDefined(attr.vertical)) {
8786 scope.verticalSeekBar = true;
8787 axisPosition = "clientY";
8790 scope.verticalSeekBar = false;
8791 axisPosition = "clientX";
8793 var getValidStep = function(val) {
8794 val = parseFloat(val);
8795 // in case $modelValue came in string number
8796 if (angular.isNumber(val)) {
8797 val = Math.round((val - min) / step) * step + min;
8798 return Math.round(val * 1000) / 1000;
8802 var getPositionToPercent = function(x) {
8803 if (scope.verticalSeekBar) {
8804 return Math.max(0, Math.min(1, (trackContainerRect.bottom - x) / (trackFillOrderPositioning)));
8807 return Math.max(0, Math.min(1, (x - trackContainerRect.left) / (trackFillOrderPositioning)));
8811 var getPercentToValue = function(percent) {
8812 return (min + percent * (max - min));
8815 var getValueToPercent = function(val) {
8816 return (val - min) / (max - min);
8819 var getValidMinMax = function(val) {
8820 return Math.max(min, Math.min(max, val));
8823 var updateTrackContainerRect = function() {
8824 trackContainerRect = trackContainer[0].getBoundingClientRect();
8825 if (scope.verticalSeekBar) {
8826 if (!trackContainerRect.height) {
8827 trackFillOrderPositioning = trackContainer[0].scrollHeight;
8829 trackFillOrderPositioning = trackContainerRect.height;
8833 if (!trackContainerRect.width) {
8834 trackFillOrderPositioning = trackContainer[0].scrollWidth;
8836 trackFillOrderPositioning = trackContainerRect.width;
8843 var updateKnobPosition = function(percent) {
8844 var percentStr = (percent * 100) + '%';
8845 if (scope.verticalSeekBar) {
8846 knob.css('bottom', percentStr);
8847 trackFill.css('height', percentStr);
8850 knob.css('left', percentStr);
8851 trackFill.css('width', percentStr);
8855 var modelRenderer = function() {
8856 if (isNaN(ngModelCtrl.$viewValue)) {
8857 ngModelCtrl.$viewValue = ngModelCtrl.$modelValue || min;
8860 var viewVal = ngModelCtrl.$viewValue;
8861 scope.currentModelValue = viewVal;
8863 //wait for min, max and step to be set then only update UI to avoid NaN on percent calculation
8864 if ((min || min === 0) && max && step) {
8865 updateKnobPosition(getValueToPercent(viewVal));
8869 var setModelValue = function(val) {
8870 scope.currentModelValue = getValidMinMax(getValidStep(val));
8871 ngModelCtrl.$setViewValue(scope.currentModelValue);
8874 var updateMin = function(val) {
8875 min = parseFloat(val);
8877 min = b2bSeekBarConfig.min;
8882 var updateMax = function(val) {
8883 max = parseFloat(val);
8885 max = b2bSeekBarConfig.max;
8890 var updateStep = function(val) {
8891 step = parseFloat(val);
8892 if (!attr['skipInterval']) {
8893 skipInterval = step;
8897 var updateSkipInterval = function(val) {
8898 skipInterval = step * Math.ceil(val / (step!==0?step:1));
8901 angular.isDefined(attr.min) ? attr.$observe('min', updateMin) : updateMin(b2bSeekBarConfig.min);
8902 angular.isDefined(attr.max) ? attr.$observe('max', updateMax) : updateMax(b2bSeekBarConfig.max);
8903 if (angular.isDefined(attr.step)) {
8904 attr.$observe('step', updateStep);
8906 if (angular.isDefined(attr.skipInterval)) {
8907 attr.$observe('skipInterval', updateSkipInterval);
8909 scope.currentModelValue = getValidMinMax(getValidStep(min));
8910 var onMouseDown = function(e) {
8913 // left mouse button
8917 // right or middle mouse button
8922 scope.isDragging = true;
8923 seekBarKnob[0].focus();
8924 updateTrackContainerRect();
8925 if (attr['onDragInit']) {
8928 events.stopPropagation(e);
8929 events.preventDefault(e);
8930 scope.$apply(function() {
8931 setModelValue(getPercentToValue(getPositionToPercent(e[axisPosition])));
8935 var onMouseUp = function() {
8937 if (attr['onDragEnd']) {
8940 scope.isDragging = false;
8944 var onMouseMove = function(e) {
8945 if (scope.isDragging) {
8946 events.stopPropagation(e);
8947 events.preventDefault(e);
8949 scope.$apply(function() {
8950 setModelValue(getPercentToValue(getPositionToPercent(e[axisPosition])));
8955 function onKeyDown(e) {
8957 e.keyCode = e.which;
8960 switch (e.keyCode) {
8961 case keymap.KEY.LEFT:
8962 if (!scope.verticalSeekBar) {
8963 updateStep = -skipInterval;
8966 case keymap.KEY.RIGHT:
8967 if (!scope.verticalSeekBar) {
8968 updateStep = skipInterval;
8972 if (scope.verticalSeekBar) {
8973 updateStep = skipInterval;
8976 case keymap.KEY.DOWN:
8977 if (scope.verticalSeekBar) {
8978 updateStep = -skipInterval;
8986 events.stopPropagation(e);
8987 events.preventDefault(e);
8988 scope.$apply(function() {
8989 setModelValue(ngModelCtrl.$viewValue + updateStep);
8991 if (attr['onDragEnd']) {
8997 elm.on('keydown', onKeyDown);
8998 elm.on('mousedown', onMouseDown);
9000 $documentBind.event('mousemove', 'isDragging', onMouseMove, scope, true, 0);
9001 $documentBind.event('mouseup', 'isDragging', onMouseUp, scope, true, 0);
9003 ngModelCtrl.$render = function() {
9004 if (!scope.isDragging) {
9008 ngModelCtrl.$viewChangeListeners.push(modelRenderer);
9009 ngModelCtrl.$formatters.push(getValidMinMax);
9010 ngModelCtrl.$formatters.push(getValidStep);
9016 * @name Layouts.att:separators
9019 * <file src="src/separators/docs/readme.md" />
9025 * <section id="code">
9026 <b>HTML + AngularJS</b>
9027 <example module="b2b.att">
9028 <file src="src/separators/docs/demo.html" />
9029 <file src="src/separators/docs/demo.js" />
9035 angular.module('b2b.att.separators', []);
9038 * @name Buttons, links & UI controls.att:slider
9041 * <file src="src/slider/docs/readme.md" />
9044 * <b2b-slider min="0" max="400" step="1" no-aria-label skip-interval="1" ng-model="horizontalSliderVal" style="width:180px; margin: auto;" on-drag-end="onDragEnd()" on-drag-init="onDragStart()" label-id="slider-label" post-aria-label="postAriaLabel"></b2b-slider>
9048 <b>HTML + AngularJS</b>
9049 <example module="b2b.att">
9050 <file src="src/slider/docs/demo.html" />
9051 <file src="src/slider/docs/demo.js" />
9056 angular.module('b2b.att.slider', ['b2b.att.utilities'])
9057 .constant('SliderConfig', {
9063 .directive('b2bSlider', ['$documentBind', 'SliderConfig', 'keymap', '$compile', '$log', function($documentBind, SliderConfig, keymap, $compile, $log) {
9068 templateUrl: 'b2bTemplate/slider/slider.html',
9072 trackFillColor: '=?',
9074 postAriaLabel: '=?',
9076 sliderSnapPoints: '=?',
9077 customAriaLabel: '=?',
9080 link: function(scope, elm, attr, ngModelCtrl) {
9081 scope.isDragging = false;
9082 scope.verticalSlider = false;
9085 var step = SliderConfig.step;
9086 var skipInterval = SliderConfig.skipInterval;
9087 var knob = angular.element(elm[0].querySelector('.slider-knob-container'));
9088 var sliderKnob = angular.element(knob[0].querySelector('.slider-knob'));
9089 var trackContainer = angular.element(elm[0].querySelector('.slider-track-container'));
9090 var trackFill = angular.element(elm[0].querySelector('.slider-track-fill'));
9091 var trackContainerRect = {};
9092 var axisPosition = "clientX";
9093 var trackFillOrderPositioning;
9095 //Forcefully disabling the vertical Slider code.
9096 if (angular.isDefined(attr.vertical)) {
9097 scope.verticalSlider = true;
9098 axisPosition = "clientY";
9101 if (angular.isDefined(scope.noAriaLabel) && scope.noAriaLabel !== '') {
9102 $log.warn('no-aria-label has been deprecated. This will be removed in v0.6.0.');
9104 if (angular.isDefined(scope.preAriaLabel) && scope.preAriaLabel !== '') {
9105 $log.warn('pre-aria-label has been deprecated. Please use label-id instead. This will be removed in v0.6.0.');
9107 if (angular.isDefined(scope.customAriaLabel) && scope.customAriaLabel !== '') {
9108 $log.warn('custom-aria-label has been deprecated. Please use label-id and post-aria-label instead. This will be removed in v0.6.0.');
9111 var binarySearchNearest = function (num, arr) {
9114 var hi = arr.length - 1;
9116 while (hi - lo > 1) {
9117 mid = Math.floor((lo + hi) / 2);
9118 if (arr[mid] < num) {
9124 if (num - arr[lo] < arr[hi] - num) {
9130 var getValidStep = function(val) {
9131 val = parseFloat(val);
9132 // in case $modelValue came in string number
9135 if(attr['sliderSnapPoints'] && scope.sliderSnapPoints.length > 0) {
9136 val = binarySearchNearest(val, scope.sliderSnapPoints);
9139 val = Math.round((val - min) / step) * step + min;
9142 return Math.round(val * 1000) / 1000;
9146 var getPositionToPercent = function(x) {
9147 if (scope.verticalSlider) {
9148 return Math.max(0, Math.min(1, (trackContainerRect.bottom - x) / (trackFillOrderPositioning)));
9151 return Math.max(0, Math.min(1, (x - trackContainerRect.left) / (trackFillOrderPositioning)));
9155 var getPercentToValue = function(percent) {
9156 return (min + percent * (max - min));
9159 var getValueToPercent = function(val) {
9160 return (val - min) / (max - min);
9163 var getValidMinMax = function(val) {
9164 return Math.max(min, Math.min(max, val));
9167 var updateTrackContainerRect = function() {
9168 trackContainerRect = trackContainer[0].getBoundingClientRect();
9169 if (scope.verticalSlider) {
9170 if (!trackContainerRect.height) {
9171 trackFillOrderPositioning = trackContainer[0].scrollHeight;
9173 trackFillOrderPositioning = trackContainerRect.height;
9177 if (!trackContainerRect.width) {
9178 trackFillOrderPositioning = trackContainer[0].scrollWidth;
9180 trackFillOrderPositioning = trackContainerRect.width;
9187 var updateKnobPosition = function(percent) {
9188 var percentStr = (percent * 100) + '%';
9189 if (scope.verticalSlider) {
9190 knob.css('bottom', percentStr);
9191 trackFill.css('height', percentStr);
9194 knob.css('left', percentStr);
9195 trackFill.css('width', percentStr);
9199 var modelRenderer = function() {
9201 if(attr['disabled']){
9205 if (isNaN(ngModelCtrl.$viewValue)) {
9206 ngModelCtrl.$viewValue = ngModelCtrl.$modelValue || min;
9209 var viewVal = ngModelCtrl.$viewValue;
9210 scope.currentModelValue = viewVal;
9212 //wait for min, max and step to be set then only update UI to avoid NaN on percent calculation
9213 if ((min || min === 0) && max && step) {
9214 updateKnobPosition(getValueToPercent(viewVal));
9218 var setModelValue = function(val) {
9219 scope.currentModelValue = getValidMinMax(getValidStep(val));
9220 ngModelCtrl.$setViewValue(scope.currentModelValue);
9223 var updateMin = function(val) {
9224 min = parseFloat(val);
9226 min = SliderConfig.min;
9232 var updateMax = function(val) {
9233 max = parseFloat(val);
9235 max = SliderConfig.max;
9241 var updateStep = function(val) {
9242 step = parseFloat(val);
9243 if (!attr['skipInterval']) {
9244 skipInterval = step;
9248 var updateSkipInterval = function(val) {
9249 skipInterval = step * Math.ceil(val / (step!==0?step:1));
9252 angular.isDefined(attr.min) ? attr.$observe('min', updateMin) : updateMin(SliderConfig.min);
9253 angular.isDefined(attr.max) ? attr.$observe('max', updateMax) : updateMax(SliderConfig.max);
9254 if (angular.isDefined(attr.step)) {
9255 attr.$observe('step', updateStep);
9257 if (angular.isDefined(attr.skipInterval)) {
9258 attr.$observe('skipInterval', updateSkipInterval);
9260 scope.currentModelValue = getValidMinMax(getValidStep(min));
9261 var onMouseDown = function(e) {
9263 if(attr['disabled']){
9269 // left mouse button
9273 // right or middle mouse button
9277 scope.isDragging = true;
9278 sliderKnob[0].focus();
9279 updateTrackContainerRect();
9280 if (attr['onDragInit']) {
9283 e.stopPropagation();
9285 scope.$apply(function() {
9286 setModelValue(getPercentToValue(getPositionToPercent(e[axisPosition])));
9290 var onMouseUp = function() {
9292 if (attr['onDragEnd']) {
9295 scope.isDragging = false;
9299 var onMouseMove = function(e) {
9300 if (scope.isDragging) {
9301 e.stopPropagation();
9304 scope.$apply(function() {
9305 setModelValue(getPercentToValue(getPositionToPercent(e[axisPosition])));
9310 function onKeyDown(e) {
9312 e.keyCode = e.which;
9315 switch (e.keyCode) {
9316 case keymap.KEY.DOWN:
9317 case keymap.KEY.LEFT:
9318 if(attr['sliderSnapPoints'] && scope.sliderSnapPoints.length > 0) {
9319 var currentIndex = scope.sliderSnapPoints.indexOf(ngModelCtrl.$viewValue);
9320 if (currentIndex > 0) {
9323 updateStep = scope.sliderSnapPoints[currentIndex];
9326 updateStep = ngModelCtrl.$viewValue - skipInterval;
9330 case keymap.KEY.RIGHT:
9331 if(attr['sliderSnapPoints'] && scope.sliderSnapPoints.length > 0) {
9332 var currentIndex = scope.sliderSnapPoints.indexOf(ngModelCtrl.$viewValue);
9333 if (currentIndex < scope.sliderSnapPoints.length-1) {
9336 updateStep = scope.sliderSnapPoints[currentIndex];
9339 updateStep = ngModelCtrl.$viewValue + skipInterval;
9342 case keymap.KEY.END:
9343 if(attr['sliderSnapPoints'] && scope.sliderSnapPoints.length > 0) {
9344 currentIndex = scope.sliderSnapPoints.length-1;
9345 updateStep = scope.sliderSnapPoints[currentIndex];
9347 setModelValue(scope.max);
9350 e.stopPropagation();
9352 case keymap.KEY.HOME:
9353 if(attr['sliderSnapPoints'] && scope.sliderSnapPoints.length > 0) {
9355 updateStep = scope.sliderSnapPoints[currentIndex];
9357 setModelValue(scope.min);
9360 e.stopPropagation();
9366 if (angular.isNumber(updateStep) && !attr['disabled']) {
9367 e.stopPropagation();
9369 scope.$apply(function() {
9370 setModelValue(updateStep);
9372 if (attr['onDragEnd']) {
9378 elm.on('keydown', onKeyDown);
9379 elm.on('mousedown', onMouseDown);
9381 $documentBind.event('mousemove', 'isDragging', onMouseMove, scope, true, 0);
9382 $documentBind.event('mouseup', 'isDragging', onMouseUp, scope, true, 0);
9384 attr.$observe('disabled', function (disabled) {
9386 sliderKnob.removeAttr('tabindex');
9388 sliderKnob.attr('tabindex', '0');
9392 elm.toggleClass("slider-disabled", disabled);
9394 if (angular.isDefined(attr.hideDisabledKnob)) {
9395 scope.hideKnob = disabled;
9399 ngModelCtrl.$render = function() {
9400 if (!scope.isDragging) {
9402 if (attr['onRenderEnd'] && !attr['disabled']) {
9403 scope.onRenderEnd({currentModelValue: scope.currentModelValue});
9407 ngModelCtrl.$viewChangeListeners.push(modelRenderer);
9408 ngModelCtrl.$formatters.push(getValidMinMax);
9409 ngModelCtrl.$formatters.push(getValidStep);
9415 * @name Forms.att:spinButton
9417 * @param {String} spin-button-id - An ID for the input field
9418 * @param {Integer} min - Minimum value for the input
9419 * @param {Integer} max - Maximum value for the input
9420 * @param {Integer} step - Value by which input field increments or decrements on up/down arrow keys
9421 * @param {Integer} page-step - Value by which input field increments or decrements on page up/down keys
9422 * @param {boolean} input-model-key - Default value for input field
9423 * @param {boolean} disabled-flag - A boolean that triggers directive once the min or max value has reached
9426 * <file src="src/spinButton/docs/readme.md" />
9429 * <section id="code">
9430 <example module="b2b.att">
9431 <file src="src/spinButton/docs/demo.html" />
9432 <file src="src/spinButton/docs/demo.js" />
9437 angular.module('b2b.att.spinButton', ['b2b.att.utilities'])
9438 .constant('b2bSpinButtonConfig', {
9443 inputModelKey: 'value',
9446 .directive('b2bSpinButton', ['keymap', 'b2bSpinButtonConfig', 'b2bUserAgent', function (keymap, b2bSpinButtonConfig, userAgent) {
9449 require: '?ngModel',
9456 pageStep: '=pageStep',
9458 inputValue: '=ngModel',
9462 templateUrl: 'b2bTemplate/spinButton/spinButton.html',
9463 controller: ['$scope', '$element', '$attrs', function (scope, element, attrs) {
9465 scope.isMobile = userAgent.isMobile();
9466 scope.notMobile = userAgent.notMobile();
9468 scope.min = attrs.min ? scope.min : b2bSpinButtonConfig.min;
9469 scope.max = attrs.max ? scope.max : b2bSpinButtonConfig.max;
9470 scope.step = attrs.step ? scope.step : b2bSpinButtonConfig.step;
9471 scope.pageStep = attrs.pageStep ? scope.pageStep : b2bSpinButtonConfig.pageStep;
9472 scope.inputModelKey = attrs.inputModelKey ? scope.inputModelKey : b2bSpinButtonConfig.inputModelKey;
9473 scope.disabledFlag = attrs.disabledFlag ? scope.disabledFlag : b2bSpinButtonConfig.disabledFlag;
9475 if (scope.min < 0) {
9478 if (scope.max > 999) {
9482 scope.isPlusDisabled = function () {
9483 return (scope.disabledFlag || scope.inputValue[scope.inputModelKey] >= scope.max);
9485 scope.isMinusDisabled = function () {
9486 return (scope.disabledFlag || scope.inputValue[scope.inputModelKey] <= scope.min);
9489 scope.getValidateInputValue = function (value) {
9490 if (value <= scope.min) {
9492 } else if (value >= scope.max) {
9499 scope.plus = function () {
9500 scope.inputValue[scope.inputModelKey] = scope.getValidateInputValue(parseInt(scope.inputValue[scope.inputModelKey], 10) + scope.step);
9502 scope.minus = function () {
9503 scope.inputValue[scope.inputModelKey] = scope.getValidateInputValue(parseInt(scope.inputValue[scope.inputModelKey], 10) - scope.step);
9505 scope.pagePlus = function () {
9506 scope.inputValue[scope.inputModelKey] = scope.getValidateInputValue(parseInt(scope.inputValue[scope.inputModelKey], 10) + scope.pageStep);
9508 scope.pageMinus = function () {
9509 scope.inputValue[scope.inputModelKey] = scope.getValidateInputValue(parseInt(scope.inputValue[scope.inputModelKey], 10) - scope.pageStep);
9513 link: function (scope, elem) {
9515 if (scope.notMobile) {
9516 angular.element(elem).find('input').attr('aria-live', 'off');
9517 angular.element(elem).find('input').attr('role', 'spinbutton');
9520 elem.find('input').bind('keydown', function (e) {
9521 if (e.keyCode === keymap.KEY.UP) {
9523 } else if (e.keyCode === keymap.KEY.DOWN){
9525 } else if (e.keyCode === keymap.KEY.HOME) {
9526 scope.inputValue[scope.inputModelKey] = parseInt(scope.min);
9527 } else if (e.keyCode === keymap.KEY.END) {
9528 scope.inputValue[scope.inputModelKey] = parseInt(scope.max);
9529 } else if (e.keyCode === keymap.KEY.PAGE_UP) {
9531 } else if (e.keyCode === keymap.KEY.PAGE_DOWN) {
9537 elem.find('input').bind('keyup', function () {
9538 if (scope.inputValue[scope.inputModelKey] === null ||
9539 scope.inputValue[scope.inputModelKey] === '' ||
9540 scope.inputValue[scope.inputModelKey] < scope.min) {
9541 scope.inputValue[scope.inputModelKey] = scope.min;
9543 } else if (angular.isUndefined(scope.inputValue[scope.inputModelKey]) ||
9544 scope.inputValue[scope.inputModelKey] > scope.max) {
9545 scope.inputValue[scope.inputModelKey] = scope.max;
9550 scope.focusInputSpinButton = function (evt) {
9551 evt.preventDefault();
9552 if (scope.notMobile) {
9553 elem[0].querySelector('input').focus();
9562 * @name Template.att:Static Route
9565 * <file src="src/staticRouteTemplate/docs/readme.md" />
9568 * <section id="code">
9569 <example module="b2b.att">
9570 <file src="src/staticRouteTemplate/docs/demo.html" />
9571 <file src="src/staticRouteTemplate/docs/demo.js" />
9576 angular.module('b2b.att.staticRouteTemplate', ['b2b.att.utilities'])
9580 * @name Progress & usage indicators.att:statusTracker
9583 * @param {array} statusObject - An array of status objects that accept heading, estimate, description and state
9585 * <file src="src/statusTracker/docs/readme.md" />
9589 <div ng-controller="statusTrackerController">
9590 <b2b-status-tracker statuses="statusObject"></b2b-status-tracker>
9595 <example module="b2b.att">
9596 <file src="src/statusTracker/docs/demo.html" />
9597 <file src="src/statusTracker/docs/demo.js" />
9602 angular.module('b2b.att.statusTracker', ['b2b.att.utilities'])
9603 .constant('b2bStatusTrackerConfig', {
9606 'complete': 'icoControls-approval',
9607 'current': 'icon-misc-time',
9608 'pending': 'icoControls-statusokay',
9609 'actionRequired': 'icon-primary-securityalerts-alert',
9610 'notAvailable': 'icoControls-restricted'
9613 .directive('b2bStatusTracker', ['b2bStatusTrackerConfig', function(b2bStatusTrackerConfig) {
9621 templateUrl: function(scope) {
9622 return 'b2bTemplate/statusTracker/statusTracker.html';
9624 link: function(scope, element, attr) {
9625 scope.currentViewIndex = 0;
9626 scope.b2bStatusTrackerConfig = b2bStatusTrackerConfig;
9628 scope.nextStatus = function() {
9629 if (scope.currentViewIndex+1 <= scope.statuses.length) {
9630 scope.currentViewIndex++;
9633 scope.previousStatus = function() {
9634 if (scope.currentViewIndex-1 >= 0) {
9635 scope.currentViewIndex--;
9638 scope.isInViewport = function(index) {
9639 return (index < scope.currentViewIndex+3 && index >= scope.currentViewIndex); // && index > scope.currentViewIndex-2
9642 scope.removeCamelCase = function(str) {
9643 return str.replace(/([a-z])([A-Z])/g, '$1 $2').toLowerCase();
9650 * @name Progress & usage indicators.att:stepTracker
9653 * @param {array} stepsItemsObject - An array of step objects
9654 * @param {Integer} currenIindex - This indicates the current running step
9655 * @param {Integer} viewportIndex - This is optional. This can used to start the view port rather than 1 item.
9657 * <file src="src/stepTracker/docs/readme.md" />
9661 * <b2b-step-tracker steps-items-object="stepsObject" current-index="currentStepIndex" step-indicator-heading="stepHeading"></b2b-step-tracker>
9666 <b>HTML + AngularJS</b>
9667 <example module="b2b.att">
9668 <file src="src/stepTracker/docs/demo.html" />
9669 <file src="src/stepTracker/docs/demo.js" />
9673 angular.module('b2b.att.stepTracker', ['b2b.att.utilities'])
9674 .constant('b2bStepTrackerConfig', {
9677 .directive('b2bStepTracker', ['b2bStepTrackerConfig', function(b2bStepTrackerConfig) {
9682 stepsItemsObject:"=",
9686 templateUrl: 'b2bTemplate/stepTracker/stepTracker.html',
9687 link: function(scope, ele, attr) {
9688 if (angular.isDefined(scope.viewportIndex)) {
9689 scope.currentViewIndex = scope.viewportIndex - 1;
9691 scope.currentViewIndex = 0;
9694 scope.b2bStepTrackerConfig = b2bStepTrackerConfig;
9695 scope.nextStatus = function() {
9696 if (scope.currentViewIndex+1 <= scope.stepsItemsObject.length) {
9697 scope.currentViewIndex++;
9700 scope.previousStatus = function() {
9701 if (scope.currentViewIndex-1 >= 0) {
9702 scope.currentViewIndex--;
9705 scope.isInViewport = function(index) {
9706 return (index < scope.currentViewIndex+b2bStepTrackerConfig.maxViewItems && index >= scope.currentViewIndex);
9714 * @name Buttons, links & UI controls.att:switches
9717 * <file src="src/switches/docs/readme.md" />
9721 * <!-- On / Off Toggle switch -->
9722 * <label for="switch1" class="controlled-text-wrap"> This is ON
9723 * <input type="checkbox" role="checkbox" b2b-switches id="switch1" ng-model="switch1.value">
9726 * <!-- On / Off Toggle switch and DISABLED -->
9727 * <label for="switch2" class="controlled-text-wrap"> This is ON (disabled)
9728 * <input type="checkbox" role="checkbox" b2b-switches id="switch2" ng-model="switch1.value" ng-disabled="true" >
9733 * <section id="code">
9734 <b>HTML + AngularJS</b>
9735 <example module="b2b.att">
9736 <file src="src/switches/docs/demo.js" />
9737 <file src="src/switches/docs/demo.html" />
9741 angular.module('b2b.att.switches', ['b2b.att.utilities'])
9742 .directive('b2bSwitches', ['$compile', '$templateCache', 'keymap', 'events', function ($compile, $templateCache, keymap, events) {
9745 require: ['ngModel'],
9746 link: function (scope, element, attrs, ctrl) {
9747 var ngModelController = ctrl[0];
9749 element.parent().bind("keydown", function (e) {
9750 if (!attrs.disabled && (e.keyCode === keymap.KEY.ENTER || e.keyCode === keymap.KEY.SPACE)) {
9751 events.preventDefault(e);
9752 ngModelController.$setViewValue(!ngModelController.$viewValue);
9753 element.prop("checked", ngModelController.$viewValue);
9757 element.wrap('<div class="btn-switch">');
9758 //element.attr("tabindex", -1);
9759 if (navigator.userAgent.match(/iphone/i)){
9760 element.attr("aria-live", "polite");
9763 element.removeAttr('aria-live');
9766 var templateSwitch = angular.element($templateCache.get("b2bTemplate/switches/switches.html"));
9767 if (angular.isDefined(attrs.typeSpanish)) {
9768 templateSwitch = angular.element($templateCache.get("b2bTemplate/switches/switches-spanish.html"));
9771 templateSwitch = $compile(templateSwitch)(scope);
9772 element.parent().append(templateSwitch);
9774 element.bind("focus", function (e) {
9775 element.parent().addClass('focused');
9778 element.bind("blur", function (e) {
9779 element.parent().removeClass('focused');
9786 * @name Template.att:Table with Drag and Drop
9789 * <file src="src/tableDragAndDrop/docs/readme.md" />
9792 * <section id="code">
9793 <example module="b2b.att">
9794 <file src="src/tableDragAndDrop/docs/demo.html" />
9795 <file src="src/tableDragAndDrop/docs/demo.js" />
9800 angular.module('b2b.att.tableDragAndDrop', ['b2b.att.utilities','b2b.att.tables'])
9804 * @name Messages, modals & alerts.att:tableMessages
9807 * <file src="src/tableMessages/docs/readme.md" />
9810 <!-- no matching results -->
9811 <b2b-table-message msg-type="'noMatchingResults'">
9812 <p>No Matching Results</p>
9813 </b2b-table-message>
9815 <!-- info could not load -->
9816 <b2b-table-message msg-type="'infoCouldNotLoad'" on-refresh-click="refreshClicked()">
9817 </b2b-table-message>
9819 <!-- magnify search -->
9820 <b2b-table-message msg-type="'magnifySearch'">
9821 </b2b-table-message>
9823 <!-- loading data -->
9824 <b2b-table-message msg-type="'loadingTable'">
9825 <!-- custom html -->
9826 <p>The data is currently loading...</p>
9827 </b2b-table-message>
9831 <b>HTML + AngularJS</b>
9832 <example module="b2b.att">
9833 <file src="src/tableMessages/docs/demo.html" />
9834 <file src="src/tableMessages/docs/demo.js" />
9838 angular.module('b2b.att.tableMessages', [])
9839 .directive('b2bTableMessage', [function() {
9848 templateUrl: 'b2bTemplate/tableMessages/tableMessage.html',
9849 link: function(scope) {
9850 scope.refreshAction = function(evt) {
9851 scope.onRefreshClick(evt);
9859 * @name Tabs, tables & accordions.att:tableScrollbar
9862 * <file src="src/tableScrollbar/docs/readme.md" />
9866 <b2b-table-scrollbar>
9868 <thead type="header">
9870 <th role="columnheader" scope="col" key="Id" id="col1">Id</th>
9876 <td id="rowheader0" headers="col1">1002</td>
9881 </b2b-table-scrollbar>
9884 * <section id="code">
9885 <example module="b2b.att">
9886 <file src="src/tableScrollbar/docs/demo.html" />
9887 <file src="src/tableScrollbar/docs/demo.js" />
9892 angular.module('b2b.att.tableScrollbar', [])
9893 .directive('b2bTableScrollbar', ['$timeout', function ($timeout) {
9898 templateUrl: 'b2bTemplate/tableScrollbar/tableScrollbar.html',
9899 link: function (scope, element, attrs, ctrl) {
9900 var firstThWidth, firstTdWidth, firstColumnWidth, firstColumnHeight, trHeight = 0;
9901 var pxToScroll = '';
9902 var tableElement = element.find('table');
9903 var thElements = element.find('th');
9904 var tdElements = element.find('td');
9905 var innerContainer = angular.element(element[0].querySelector('.b2b-table-inner-container'));
9906 var outerContainer = angular.element(element[0].querySelector('.b2b-table-scrollbar'));
9908 scope.disableLeft = true;
9909 scope.disableRight = false;
9911 if (angular.isDefined(thElements[0])) {
9912 firstThWidth = thElements[0].offsetWidth;
9914 if (angular.isDefined(tdElements[0])) {
9915 firstTdWidth = tdElements[0].offsetWidth;
9917 firstColumnWidth = (firstThWidth > firstTdWidth) ? firstThWidth : firstTdWidth;
9919 innerContainer.css({
9920 'padding-left': (firstColumnWidth + 2) + 'px'
9923 angular.forEach(element.find('tr'), function (eachTr, index) {
9924 trObject = angular.element(eachTr);
9925 firstColumn = angular.element(trObject.children()[0]);
9927 angular.element(firstColumn).css({
9929 'width': (firstColumnWidth + 2) + 'px',
9930 'position': 'absolute'
9933 trHeight = trObject[0].offsetHeight;
9934 firstColumnHeight = firstColumn[0].offsetHeight;
9935 if (navigator.userAgent.toLowerCase().indexOf('firefox') > -1) {
9936 firstColumnHeight += 1;
9939 if (trHeight !== firstColumnHeight - 1) {
9940 if (trHeight > firstColumnHeight) {
9941 if (navigator.userAgent.toLowerCase().indexOf('firefox') > -1) {
9944 angular.element(firstColumn).css({
9945 'height': (trHeight + 1) + 'px'
9948 angular.element(trObject).css({
9949 'height': (firstColumnHeight - 1) + 'px'
9956 pxToScroll = outerContainer[0].offsetWidth - firstColumnWidth;
9958 scope.scrollLeft = function () {
9959 innerContainer[0].scrollLeft = innerContainer[0].scrollLeft + 20 - pxToScroll;
9962 scope.scrollRight = function () {
9963 innerContainer[0].scrollLeft = innerContainer[0].scrollLeft + pxToScroll - 20;
9966 scope.checkScrollArrows = function () {
9967 if (innerContainer[0].scrollLeft == 0) {
9968 scope.disableLeft = true;
9970 scope.disableLeft = false;
9973 if (((innerContainer[0].offsetWidth - firstColumnWidth) + innerContainer[0].scrollLeft) >= tableElement[0].offsetWidth) {
9974 scope.disableRight = true;
9976 scope.disableRight = false;
9981 innerContainer.bind('scroll', function () {
9982 $timeout(function () {
9983 scope.checkScrollArrows();
9992 * @name Tabs, tables & accordions.att:tables
9995 * <file src="src/tables/docs/readme.md" />
10000 <table b2b-table table-data="tableData" search-string="searchString">
10001 <thead b2b-table-row type="header">
10003 <th b2b-table-header key="requestId" default-sort="a" id="col1">Header 1</th>
10004 <th b2b-table-header key="requestType" sortable="false" id="col2">Header 2</th>
10007 <tbody b2b-table-row type="body" row-repeat="rowData in tableData">
10009 <td b2b-table-body id="rowheader{{$index}}" headers="col1" ng-bind="rowData['requestId']"> </td>
10010 <td b2b-table-body ng-bind="rowData['requestType']"></td>
10016 * <section id="code">
10017 <example module="b2b.att">
10018 <file src="src/tables/docs/demo.html" />
10019 <file src="src/tables/docs/demo.js" />
10024 angular.module('b2b.att.tables', ['b2b.att.utilities'])
10025 .constant('b2bTableConfig', {
10026 defaultSortPattern: false, // true for descending & false for ascending
10027 highlightSearchStringClass: 'tablesorter-search-highlight',
10028 zebraStripCutOff: 6, // > zebraStripCutOff
10029 tableBreakpoints: [ // breakpoints are >= min and < max
10052 .directive('b2bTable', ['$filter', '$window', 'b2bTableConfig', '$timeout', function ($filter, $window, b2bTableConfig, $timeout) {
10062 searchCategory: "=",
10066 require: 'b2bTable',
10067 templateUrl: 'b2bTemplate/tables/b2bTable.html',
10068 controller: ['$scope', '$attrs', function ($scope, $attrs) {
10070 this.currentSortIndex = null;
10071 this.responsive = $scope.responsive = $attrs.responsive;
10072 this.maxTableColumns = -1;
10073 this.totalTableColums = 0;
10074 this.active = $scope.active = false;
10075 this.responsiveRowScopes = [];
10076 this.hideColumnPriority = [];
10077 this.hiddenColumn = [];
10078 this.setIndex = function (headerScope, priority) {
10079 this.headers.push(headerScope);
10080 if (this.responsive) {
10081 this.totalTableColums++;
10082 if (!isNaN(priority)) {
10083 this.hideColumnPriority[priority] = this.totalTableColums - 1;
10085 this.hideColumnPriority[this.totalTableColums - 1] = this.totalTableColums - 1;
10088 return this.totalTableColums - 1;
10090 this.getIndex = function (headerName) {
10091 for (var i = 0; i < this.headers.length; i++) {
10092 if (this.headers[i].headerName === headerName) {
10093 return this.headers[i].index;
10098 this.setResponsiveRow = function (responsiveRowScope) {
10099 this.responsiveRowScopes.push(responsiveRowScope);
10101 $scope.nextSort = '';
10102 this.sortData = function (columnIndex, reverse, externalSort) {
10103 if ($scope.$parent && $scope.$parent !== undefined) {
10104 $scope.$parent.columnIndex = columnIndex;
10105 $scope.$parent.reverse = reverse;
10107 this.currentSortIndex = columnIndex;
10108 if (externalSort === true) {
10110 $scope.nextSort = 'd'
10112 $scope.nextSort = 'a'
10115 $scope.currentPage = 1;
10116 this.resetSortPattern();
10118 this.getSearchString = function () {
10119 return $scope.searchString;
10121 this.resetSortPattern = function () {
10122 for (var i = 0; i < this.headers.length; i++) {
10123 var currentScope = this.headers[i];
10124 if (currentScope.index !== this.currentSortIndex) {
10125 currentScope.resetSortPattern();
10130 $scope.$watch('nextSort', function (val) {
10131 if ($scope.$parent && $scope.$parent !== undefined) {
10132 $scope.$parent.nextSort = val;
10137 link: function (scope, elem, attr, ctrl) {
10138 scope.searchCriteria = {};
10139 scope.tableBreakpoints = attr.tableConfig ? scope.$parent.$eval(attr.tableConfig) : angular.copy(b2bTableConfig.tableBreakpoints);
10140 scope.$watchCollection('tableData', function (value) {
10141 if (value && !isNaN(value.length)) {
10142 scope.totalRows = value.length;
10145 scope.$watch('currentPage', function (val) {
10146 if (scope.$parent && scope.$parent !== undefined) {
10147 scope.$parent.currentPage = val;
10151 scope.$watch('viewPerPage', function (val) {
10152 if (scope.$parent && scope.$parent !== undefined) {
10153 scope.$parent.viewPerPage = val;
10156 scope.$watch('totalRows', function (val) {
10157 if (scope.$parent && scope.$parent !== undefined) {
10158 if (val > b2bTableConfig.zebraStripCutOff) {
10159 scope.$parent.zebraStripFlag = true;
10161 scope.$parent.zebraStripFlag = false;
10165 scope.$watch(function () {
10166 return scope.totalRows / scope.viewPerPage;
10167 }, function (value) {
10168 if (!isNaN(value)) {
10169 scope.totalPage = Math.ceil(value);
10170 scope.currentPage = 1;
10173 var searchValCheck = function (val) {
10174 if (angular.isDefined(val) && val !== null && val !== "") {
10178 var setSearchCriteria = function (v1, v2) {
10179 if (searchValCheck(v1) && searchValCheck(v2)) {
10180 var index = ctrl.getIndex(v2);
10181 scope.searchCriteria = {};
10182 if (index !== null) {
10183 scope.searchCriteria[index] = v1;
10185 } else if (searchValCheck(v1) && (!angular.isDefined(v2) || v2 === null || v2 === "")) {
10186 scope.searchCriteria = {
10190 scope.searchCriteria = {};
10193 scope.$watch('searchCategory', function (newVal, oldVal) {
10194 if (newVal !== oldVal) {
10195 setSearchCriteria(scope.searchString, newVal);
10198 scope.$watch('searchString', function (newVal, oldVal) {
10199 if (newVal !== oldVal) {
10200 setSearchCriteria(newVal, scope.searchCategory);
10203 scope.$watchCollection('searchCriteria', function (val) {
10204 if (scope.$parent && scope.$parent !== undefined) {
10205 scope.$parent.searchCriteria = val;
10207 scope.totalRows = scope.tableData && ($filter('filter')(scope.tableData, val, false)).length || 0;
10208 scope.currentPage = 1;
10210 var window = angular.element($window);
10211 var findMaxTableColumns = function () {
10213 windowWidth = $window.innerWidth;
10214 ctrl.maxTableColumns = -1;
10215 for (var i in scope.tableBreakpoints) {
10216 if (windowWidth >= scope.tableBreakpoints[i].min && windowWidth < scope.tableBreakpoints[i].max) {
10217 ctrl.maxTableColumns = scope.tableBreakpoints[i].columns;
10221 if (ctrl.maxTableColumns > -1 && ctrl.totalTableColums > ctrl.maxTableColumns) {
10222 ctrl.active = true;
10224 ctrl.active = false;
10226 for (var i in ctrl.responsiveRowScopes) {
10227 ctrl.responsiveRowScopes[i].setActive(ctrl.active);
10230 var findHiddenColumn = function () {
10231 var columnDiffenence = ctrl.maxTableColumns > -1 ? ctrl.totalTableColums - ctrl.maxTableColumns : 0;
10232 ctrl.hiddenColumn = [];
10233 if (columnDiffenence > 0) {
10234 var tempHideColumnPriority = angular.copy(ctrl.hideColumnPriority);
10235 for (var i = 0; i < columnDiffenence; i++) {
10236 ctrl.hiddenColumn.push(tempHideColumnPriority.pop());
10240 var resizeListener = function () {
10241 findMaxTableColumns();
10242 findHiddenColumn();
10244 if (ctrl.responsive) {
10245 window.bind('resize', function () {
10249 $timeout(function () {
10256 .directive('b2bTableRow', [function () {
10259 compile: function (elem, attr) {
10260 if (attr.type === 'header') {
10262 } else if (attr.type === 'body') {
10263 var html = elem.children();
10264 if (attr.rowRepeat) {
10265 html.attr('ng-repeat', attr.rowRepeat.concat(" | orderBy : (reverse?'-':'')+ columnIndex | filter : searchCriteria : false "));
10267 html.attr('ng-class', "{'odd': $odd && zebraStripFlag}");
10268 html.attr('class', 'data-row');
10269 html.attr('b2b-responsive-row', '{{$index}}');
10275 .directive('b2bTableHeader', ['b2bTableConfig', function (b2bTableConfig) {
10285 require: '^b2bTable',
10286 templateUrl: function (elem, attr) {
10287 if (attr.sortable === 'false') {
10288 return 'b2bTemplate/tables/b2bTableHeaderUnsortable.html';
10290 return 'b2bTemplate/tables/b2bTableHeaderSortable.html';
10293 link: function (scope, elem, attr, ctrl) {
10294 var reverse = b2bTableConfig.defaultSortPattern;
10295 scope.headerName = elem.text();
10296 scope.headerId = elem.attr('id');
10297 scope.sortPattern = null;
10298 var priority = parseInt(attr.priority, 10);
10299 scope.columnIndex = ctrl.setIndex(scope, priority);
10301 scope.isHidden = function () {
10302 return (ctrl.hiddenColumn.indexOf(scope.columnIndex) > -1);
10305 scope.$watch(function () {
10306 return elem.text();
10307 }, function (value) {
10308 scope.headerName = value;
10310 scope.sort = function (sortType) {
10311 if (typeof sortType === 'boolean') {
10312 reverse = sortType;
10314 ctrl.sortData(scope.index, reverse, false);
10315 scope.sortPattern = reverse ? 'descending' : 'ascending';
10316 reverse = !reverse;
10318 scope.$watch(function () {
10319 return ctrl.currentSortIndex;
10320 }, function (value) {
10321 if (value !== scope.index) {
10322 scope.sortPattern = null;
10326 if (scope.sortable === undefined || scope.sortable === 'true' || scope.sortable === true) {
10327 scope.sortable = 'true';
10328 } else if (scope.sortable === false || scope.sortable === 'false') {
10329 scope.sortable = 'false';
10332 if (scope.sortable !== 'false') {
10333 if (scope.defaultSort === 'A' || scope.defaultSort === 'a') {
10335 } else if (scope.defaultSort === 'D' || scope.defaultSort === 'd') {
10339 scope.resetSortPattern = function () {
10340 reverse = b2bTableConfig.defaultSortPattern;
10345 .directive('b2bResponsiveRow', ['$templateCache', '$timeout', '$compile', function ($templateCache, $timeout, $compile) {
10348 require: '^b2bTable',
10349 controller: ['$scope', function ($scope) {
10350 this.rowValues = $scope.rowValues = [];
10351 this.setRowValues = function (rowValue) {
10352 this.rowValues.push(rowValue);
10354 var columnIndexCounter = -1;
10355 this.getIndex = function () {
10356 columnIndexCounter++;
10357 return columnIndexCounter;
10360 link: function (scope, elem, attr, ctrl) {
10361 if (ctrl.responsive) {
10362 scope.rowIndex = attr.b2bResponsiveRow;
10363 scope.active = false;
10364 scope.expandFlag = false;
10365 scope.headerValues = ctrl.headers;
10366 ctrl.setResponsiveRow(scope);
10367 var firstTd = elem.find('td').eq(0);
10368 scope.setActive = function (activeFlag) {
10369 scope.active = activeFlag;
10370 if (scope.active) {
10371 elem.addClass('has-button');
10372 firstTd.attr('role', 'rowheader');
10373 firstTd.parent().attr('role', 'row');
10375 elem.removeClass('has-button');
10376 firstTd.removeAttr('role');
10377 firstTd.parent().removeAttr('role');
10380 scope.toggleExpandFlag = function (expandFlag) {
10381 if (angular.isDefined(expandFlag)) {
10382 scope.expandFlag = expandFlag;
10384 scope.expandFlag = !scope.expandFlag;
10386 if (scope.expandFlag) {
10387 elem.addClass('opened');
10389 elem.removeClass('opened');
10393 firstTd.attr('scope', 'row');
10394 firstTd.addClass('col-1');
10395 scope.$on('$destroy', function () {
10396 elem.next().remove();
10398 $timeout(function () {
10399 scope.firstTdId = firstTd.attr('id');
10400 var firstTdContent = firstTd.html();
10401 var toggleButtonTemplate = '<span ng-show="!active">' + firstTdContent + '</span><button type="button" aria-describedby="sup-actNum{{$id}}" aria-expanded="{{expandFlag}}" ng-show="active" ng-click="toggleExpandFlag()"><i ng-class="{\'icon-primary-accordion-plus\': !expandFlag, \'icon-primary-accordion-minus\': expandFlag}" aria-hidden="true"></i>' + firstTdContent + '</button><span id="sup-actNum{{$id}}" style="display:none">{{expandFlag && "Hide row below." || "Show row below."}}</span>';
10402 toggleButtonTemplate = $compile(toggleButtonTemplate)(scope);
10404 firstTd.prepend(toggleButtonTemplate);
10406 var template = $templateCache.get('b2bTemplate/tables/b2bResponsiveRow.html');
10407 template = $compile(template)(scope);
10408 elem.after(template);
10414 .directive('b2bResponsiveList', ['$templateCache', '$timeout', '$compile', function ($templateCache, $timeout, $compile) {
10417 require: '^b2bTable',
10418 link: function (scope, elem, attr, ctrl) {
10419 scope.columnIndex = parseInt(attr.b2bResponsiveList, 10);
10420 scope.isVisible = function () {
10421 return (ctrl.hiddenColumn.indexOf(scope.columnIndex) > -1);
10426 .directive('b2bTableBody', ['$filter', '$timeout', 'b2bTableConfig', function ($filter, $timeout, b2bTableConfig) {
10429 require: ['^b2bTable', '?^b2bResponsiveRow'],
10433 templateUrl: 'b2bTemplate/tables/b2bTableBody.html',
10434 link: function (scope, elem, attr, ctrl) {
10435 var b2bTableCtrl = ctrl[0];
10436 var b2bResponsiveRowCtrl = ctrl[1];
10437 var highlightSearchStringClass = b2bTableConfig.highlightSearchStringClass;
10438 var searchString = "";
10439 var wrapElement = function (elem) {
10440 var text = elem.text();
10441 elem.html($filter('b2bHighlight')(text, searchString, highlightSearchStringClass));
10443 var traverse = function (elem) {
10444 var innerHtml = elem.children();
10445 if (innerHtml.length > 0) {
10446 for (var i = 0; i < innerHtml.length; i++) {
10447 traverse(innerHtml.eq(i));
10454 var clearWrap = function (elem) {
10455 var elems = elem.find('*');
10456 for (var i = 0; i < elems.length; i++) {
10457 if (elems.eq(i).attr('class') && elems.eq(i).attr('class').indexOf(highlightSearchStringClass) !== -1) {
10458 var text = elems.eq(i).text();
10459 elems.eq(i).replaceWith(text);
10463 if (b2bResponsiveRowCtrl) {
10464 scope.columnIndex = b2bResponsiveRowCtrl.getIndex();
10465 scope.isHidden = function () {
10466 return (b2bTableCtrl.hiddenColumn.indexOf(scope.columnIndex) > -1);
10469 $timeout(function () {
10470 var actualHtml = elem.children();
10471 scope.$watch(function () {
10472 return b2bTableCtrl.getSearchString();
10473 }, function (val) {
10474 searchString = val;
10476 if (actualHtml.length > 0) {
10482 if (b2bResponsiveRowCtrl) {
10483 b2bResponsiveRowCtrl.setRowValues(elem.html());
10489 .directive('b2bTableSort', ['b2bTableConfig','$timeout', function (b2bTableConfig,$timeout) {
10493 require: '^b2bTable',
10494 link: function (scope, elem, attr, ctrl) {
10495 var initialSort = '',
10498 initialSort = attr.initialSort;
10500 scope.sortTable = function (msg,trigger) {
10501 if(trigger == 'dropdown'){
10502 if (nextSort === 'd' || nextSort === 'D') {
10503 ctrl.sortData(msg, false, false);
10505 ctrl.sortData(msg, true, false);
10509 $timeout(function(){
10510 if (nextSort.length > 0) {
10512 if (nextSort === 'd' || nextSort === 'D') {
10513 tempsort = nextSort
10514 ctrl.sortData(msg, true, true);
10516 $timeout(function(){
10517 if(!angular.isUndefined(elem[0].querySelector('.sortButton')) || elem[0].querySelector('.sortButton') !== null ){
10518 angular.element(elem[0].querySelector('.sortButton'))[0].focus();
10523 tempsort = nextSort
10524 ctrl.sortData(msg, false, true);
10526 $timeout(function(){
10527 if(!angular.isUndefined(elem[0].querySelector('.sortButton')) || elem[0].querySelector('.sortButton') !== null ){
10528 angular.element(elem[0].querySelector('.sortButton'))[0].focus();
10532 } else if (initialSort.length > 0) {
10534 if (initialSort === 'd' || initialSort === 'D') {
10535 tempsort = nextSort
10536 ctrl.sortData(msg, true, true);
10538 $timeout(function(){
10539 if(!angular.isUndefined(elem[0].querySelector('.sortButton')) || elem[0].querySelector('.sortButton') !== null ){
10540 angular.element(elem[0].querySelector('.sortButton'))[0].focus();
10545 tempsort = nextSort
10546 ctrl.sortData(msg, false, true);
10548 $timeout(function(){
10549 if(!angular.isUndefined(elem[0].querySelector('.sortButton')) || elem[0].querySelector('.sortButton') !== null ){
10550 angular.element(elem[0].querySelector('.sortButton'))[0].focus();
10561 scope.sortDropdown = function(msg) {
10563 if(tempsort==='') {
10567 if(tempsort === 'd' || tempsort === 'D' ) {
10568 ctrl.sortData(msg, true, false);
10570 ctrl.sortData(msg, false, false);
10579 * @name Tabs, tables & accordions.att:tabs
10582 * <file src="src/tabs/docs/readme.md" />
10585 * <b2b-tabset tab-id-selected="activeTabsId">
10586 <b2b-tab ng-repeat="tab in gTabs" tab-item="tab"
10587 id="{{tab.uniqueId}}" aria-controls="{{tab.tabPanelId}}"
10588 ng-disabled="tab.disabled">
10594 * <section id="code">
10595 <example module="b2b.att">
10596 <file src="src/tabs/docs/demo.html" />
10597 <file src="src/tabs/docs/demo.js" />
10603 angular.module('b2b.att.tabs', ['b2b.att.utilities'])
10604 .directive('b2bTabset', function () {
10612 templateUrl: 'b2bTemplate/tabs/b2bTabset.html',
10613 controller: ['$scope', function ($scope) {
10615 this.setTabIdSelected = function (tab) {
10616 $scope.tabIdSelected = tab.id;
10619 this.getTabIdSelected = function () {
10620 return $scope.tabIdSelected;
10625 .directive('b2bTab', ['keymap', function (keymap) {
10630 require: '^b2bTabset',
10634 templateUrl: 'b2bTemplate/tabs/b2bTab.html',
10635 controller: [function(){}],
10636 link: function (scope, element, attr, b2bTabsetCtrl) {
10638 if (scope.tabItem && !scope.tabItem.disabled) {
10639 scope.tabItem.disabled = false;
10642 scope.isTabActive = function () {
10643 return (scope.tabItem.id === b2bTabsetCtrl.getTabIdSelected());
10646 scope.clickTab = function () {
10647 if (attr.disabled) {
10650 b2bTabsetCtrl.setTabIdSelected(scope.tabItem);
10653 scope.nextKey = function () {
10654 var el = angular.element(element[0])[0];
10655 var elementToFocus = null;
10656 while (el && el.nextElementSibling) {
10657 el = el.nextElementSibling;
10658 if (!el.querySelector('a').disabled) {
10659 elementToFocus = el.querySelector('a');
10664 if (!elementToFocus) {
10665 var childTabs = element.parent().children();
10666 for (var i = 0; i < childTabs.length; i++) {
10667 if (!childTabs[i].querySelector('a').disabled) {
10668 elementToFocus = childTabs[i].querySelector('a');
10674 if (elementToFocus) {
10675 elementToFocus.focus();
10679 scope.previousKey = function () {
10680 var el = angular.element(element[0])[0];
10681 var elementToFocus = null;
10683 while (el && el.previousElementSibling) {
10684 el = el.previousElementSibling;
10685 if (!el.querySelector('a').disabled) {
10686 elementToFocus = el.querySelector('a');
10691 if (!elementToFocus) {
10692 var childTabs = element.parent().children();
10693 for (var i = childTabs.length - 1; i > 0; i--) {
10694 if (!childTabs[i].querySelector('a').disabled) {
10695 elementToFocus = childTabs[i].querySelector('a');
10701 if (elementToFocus) {
10702 elementToFocus.focus();
10706 angular.element(element[0].querySelector('a')).bind('keydown', function (evt) {
10708 if (!(evt.keyCode)) {
10709 evt.keyCode = evt.which;
10712 switch (evt.keyCode) {
10713 case keymap.KEY.RIGHT:
10714 evt.preventDefault();
10718 case keymap.KEY.LEFT:
10719 evt.preventDefault();
10720 scope.previousKey();
10731 * @name Messages, modals & alerts.att:tagBadges
10734 * <file src="src/tagBadges/docs/readme.md" />
10737 * <section id="code">
10738 <example module="b2b.att">
10739 <file src="src/tagBadges/docs/demo.html" />
10740 <file src="src/tagBadges/docs/demo.js" />
10745 angular.module('b2b.att.tagBadges', ['b2b.att.utilities'])
10746 .directive('b2bTagBadge',['$timeout',function($timeout){
10749 link: function(scope,elem,attr,ctrl){
10750 elem.addClass('b2b-tags');
10751 if(angular.element(elem[0].querySelector('.icon-primary-close')).length>0) {
10752 var item = angular.element(elem[0].querySelector('.icon-primary-close'));
10753 item.bind('click',function(){
10754 elem.css({'height':'0','width':'0','padding':'0','border':'0'});
10755 elem.attr('tabindex','0');
10757 item.parent().remove();
10758 elem[0].bind('blur',function(){
10772 * @name Forms.att:textArea
10775 * <file src="src/textArea/docs/readme.md" />
10778 * <textarea b2b-reset b2b-reset-textarea ng-model="textAreaModel" ng-disabled="disabled" ng-trim="false" placeholder="{{placeholderText}}" rows="{{textAreaRows}}" maxlength="{{textAreaMaxlength}}" role="textarea"></textarea>
10781 <section id="code">
10782 <b>HTML + AngularJS</b>
10783 <example module="b2b.att">
10784 <file src="src/textArea/docs/demo.html" />
10785 <file src="src/textArea/docs/demo.js" />
10789 angular.module('b2b.att.textArea', ['b2b.att.utilities'])
10791 .directive('b2bResetTextarea', [ function () {
10794 require: 'b2bReset',
10795 link: function (scope, element, attrs, ctrl) {
10797 var resetButton = ctrl.getResetButton();
10799 var computeScrollbarAndAddClass = function () {
10800 if (element.prop('scrollHeight') > element[0].clientHeight) {
10801 element.addClass('hasScrollbar');
10803 element.removeClass('hasScrollbar');
10807 computeScrollbarAndAddClass();
10809 element.on('focus keyup', function(){
10810 computeScrollbarAndAddClass();
10818 * @name Forms.att:timeInputField
10821 * <file src="src/timeInputField/docs/readme.md" />
10825 * <section id="code">
10826 <example module="b2b.att">
10827 <file src="src/timeInputField/docs/demo.html" />
10828 <file src="src/timeInputField/docs/demo.js" />
10833 angular.module('b2b.att.timeInputField',['ngMessages', 'b2b.att.utilities']).directive('b2bTimeFormat',function(){
10836 require : '^ngModel',
10837 link : function(scope,elem,attr,ctrl){
10838 elem.on('keyup',function(evt){
10839 var modelValue = ctrl.$modelValue;
10840 var format = attr.b2bTimeFormat;
10841 modelValue = modelValue.split(':');
10842 if(format == "12"){
10843 if(!(modelValue[0] <= 12 && modelValue[0] > 0 ) || !(modelValue[1] <= 59)){
10844 ctrl.$setValidity('inValidTime',false);
10846 ctrl.$setValidity('inValidTime',true);
10848 }else if(format =="24"){
10849 if(!(modelValue[0] <= 23) || !(modelValue[1] <= 59)){
10850 ctrl.$setValidity('inValidTime',false);
10852 ctrl.$setValidity('inValidTime',true);
10863 * @name Forms.att:tooltipsForForms
10866 * <file src="src/tooltipsForForms/docs/readme.md" />
10869 <example module="b2b.att">
10870 <file src="src/tooltipsForForms/docs/demo.html" />
10871 <file src="src/tooltipsForForms/docs/demo.js" />
10874 angular.module('b2b.att.tooltipsForForms', ['b2b.att.utilities'])
10875 .directive('b2bTooltip', ['$document', '$window', '$isElement', function ($document, $window, $isElement) {
10878 link: function (scope, elem, attr, ctrl) {
10879 var icon = elem[0].querySelector('a.tooltip-element');
10880 var btnIcon = elem[0].querySelector('.btn.tooltip-element');
10881 var tooltipText = elem[0].querySelector('.helpertext');
10882 var tooltipWrapper = elem[0].querySelector('.tooltip-size-control');
10883 if (elem.hasClass('tooltip-onfocus')) {
10884 var inputElm = angular.element(elem[0].querySelector("input"));
10885 var textAreaElm = angular.element(elem[0].querySelector("textarea"));
10887 angular.element(icon).attr({'aria-expanded': false});
10888 angular.element(btnIcon).attr({'aria-expanded': false});
10889 var calcTooltip = function () {
10890 if (!elem.hasClass('tooltip active')) {
10891 if (elem.hasClass('tooltip-onfocus')) {
10892 angular.element(elem[0].querySelector("input")).triggerHandler('focusout');
10894 if (elem.hasClass('tooltip-onclick')) {
10897 angular.element(icon).removeClass('active');
10898 angular.element(icon).attr({'aria-expanded': true});
10899 angular.element(icon).attr({'aria-describedby': angular.element(tooltipText).attr('id')});
10900 angular.element(tooltipText).attr({'aria-hidden': false});
10901 elem.addClass('active');
10903 var tooltipIconPos = angular.element(icon).prop('offsetLeft'),
10904 tooltipPosition = angular.element(tooltipText).prop('offsetWidth') / 2,
10905 tipOffset = (tooltipIconPos - 30) - tooltipPosition,
10906 maxRightPos = (($window.innerWidth - 72) - (tooltipPosition * 2)) - 14.5;
10908 if ($window.innerWidth >= '768') {
10909 if (tipOffset < 0) {// if icon on far left side of page
10912 else if (tooltipIconPos > maxRightPos) {// if icon is far right side of page
10913 tipOffset = maxRightPos;
10915 else {// if tooltip in the middle somewhere
10916 tipOffset = tipOffset;
10918 angular.element(tooltipWrapper).css({left: tipOffset + 'px'});
10923 // TOOLTIP LINK ONCLICK AND FOCUS
10924 angular.element(icon).on('click mouseover mouseout focus blur', function (e) {
10925 if (e.type == 'mouseover') {
10928 else if (e.type == 'mouseout' && elem.hasClass('active')) {
10929 if (!elem.hasClass('activeClick')) {
10930 angular.element(tooltipText).attr({
10931 'aria-hidden': true,
10934 elem.removeClass('active');
10935 } else if (elem.hasClass('activeClick') && navigator.userAgent.match(/iphone/i)) {
10936 elem.removeClass('active activeClick');
10941 if (elem.hasClass('activeClick')) {
10942 angular.element(icon).attr({'aria-expanded': false});
10943 angular.element(tooltipText).attr({'aria-hidden': true});
10944 angular.element(icon).removeAttr('aria-describedby');
10945 elem.removeClass('activeClick active');
10946 e.preventDefault();
10948 else if (e.type == 'click') {
10949 elem.addClass('activeClick');
10951 e.preventDefault();
10954 angular.element(icon).on('keydown', function (e) {
10955 if (e.keyCode == '32') {
10956 (elem.hasClass('active')) ? elem.removeClass('active') : elem.addClass('value');
10957 angular.element(icon).triggerHandler('click');
10958 e.preventDefault();
10959 } else if (e.keyCode == '27') {
10960 (elem.hasClass('active')) ? elem.removeClass('active activeClick') : elem.addClass('value');
10963 e.preventDefault();
10966 e.preventDefault();
10969 // TOOLTIP BUTTON INSIDE A TEXT FIELD
10970 angular.element(btnIcon).on('click', function (e) {
10971 var $this = angular.element(this);
10972 if ($this.hasClass('active') && elem.hasClass('tooltip-onclick')) {
10973 elem.removeClass('active');
10974 $this.removeClass('active');
10975 angular.element(tooltipText).removeAttr('aria-live');
10976 $this.attr({'aria-expanded': 'false'});
10977 $this.removeAttr('aria-describedby');
10979 elem.addClass('active');
10980 $this.addClass('active');
10981 $this.attr({'aria-expanded': 'true', 'aria-describedby': angular.element(tooltipText).attr('id')});
10982 angular.element(tooltipText).attr({'aria-live': 'polite'});
10986 angular.element(btnIcon).on('blur', function (e) {
10987 var $this = angular.element(this);
10988 if ($this.hasClass('active') && elem.hasClass('tooltip-onclick')) {
10989 elem.removeClass('active');
10990 $this.removeClass('active');
10991 angular.element(tooltipText).removeAttr('aria-live');
10992 $this.attr({'aria-expanded': 'false'});
10993 $this.removeAttr('aria-describedby');
10997 angular.element(btnIcon).on('keydown', function (e) {
10998 var $this = angular.element(this);
10999 if (e.keyCode == '27') {
11000 var $this = angular.element(this);
11001 if ($this.hasClass('active') && elem.hasClass('tooltip-onclick')) {
11002 elem.removeClass('active');
11003 $this.removeClass('active');
11004 angular.element(tooltipText).removeAttr('aria-live');
11005 $this.attr({'aria-expanded': 'false'});
11006 $this.removeAttr('aria-describedby');
11011 // close all tooltips if clicking something else
11012 $document.bind('click', function (e) {
11013 var isElement = $isElement(angular.element(e.target), elem, $document);
11015 elem.removeClass('active');
11016 angular.element(elem[0].querySelector('.tooltip-element')).removeClass('active');
11017 angular.element(tooltipText).removeAttr('aria-live');
11018 angular.element(elem[0].querySelector('.tooltip-element')).attr({'aria-expanded': 'false'});
11019 angular.element(elem[0].querySelector('.tooltip-element')).removeAttr('aria-describedby');
11023 angular.element(inputElm).on('keydown', function (e) {
11024 if (e.keyCode == '27'){
11025 elem.removeClass('active');
11026 angular.element(tooltipText).css('display', 'none');
11027 angular.element(tooltipText).removeAttr('aria-live');
11029 if (angular.element(this).attr('aria-describedby') === undefined){
11033 else if ((spaceIndex = angular.element(this).attr('aria-describedby').lastIndexOf(' ')) >= 0){
11035 var describedByValue = angular.element(this).attr('aria-describedby').slice(0, spaceIndex);
11037 angular.element(this).attr('aria-describedby', describedByValue);
11041 angular.element(this).removeAttr('aria-describedby');
11046 angular.element(textAreaElm).on('keydown', function (e) {
11047 if (e.keyCode == '27'){
11048 elem.removeClass('active');
11049 angular.element(tooltipText).css('display', 'none');
11050 angular.element(tooltipText).removeAttr('aria-live');
11051 if (angular.element(this).attr('aria-describedby') === undefined){
11055 else if ((spaceIndex = angular.element(this).attr('aria-describedby').lastIndexOf(' ')) >= 0){
11057 var describedByValue = angular.element(this).attr('aria-describedby').slice(0, spaceIndex);
11059 angular.element(this).attr('aria-describedby', describedByValue);
11063 angular.element(this).removeAttr('aria-describedby');
11068 // TOOLTIP TRIGGERED AUTOMATICALLY INSIDE A TEXT FIELD
11069 angular.element(inputElm).on('focus', function (e) {
11070 var allTooltip = $document[0].querySelectorAll('[class*="tooltip"]');
11071 for (var i = 0; i < allTooltip.length; i++) {
11072 if (angular.element(allTooltip[i]).hasClass('active')) {
11073 angular.element(allTooltip[i]).triggerHandler('click');
11076 angular.element(this).attr({'aria-describedby': angular.element(tooltipText).attr('id')});
11077 angular.element(tooltipText).css('display', 'block');
11078 angular.element(tooltipText).attr({'aria-live': 'polite'});
11079 elem.addClass('active');
11081 angular.element(inputElm).on('blur', function (e) {
11082 elem.removeClass('active');
11083 angular.element(tooltipText).css('display', 'none');
11084 angular.element(tooltipText).removeAttr('aria-live');
11085 angular.element(this).removeAttr('aria-describedby');
11088 // TOOLTIP TRIGGERED AUTOMATICALLY INSIDE A TEXTAREA
11089 angular.element(textAreaElm).on('focus', function (e) {
11090 var allTooltip = $document[0].querySelectorAll('[class*="tooltip"]');
11091 for (var i = 0; i < allTooltip.length; i++) {
11092 if (angular.element(allTooltip[i]).hasClass('active')) {
11093 angular.element(allTooltip[i]).triggerHandler('click');
11096 elem.addClass('active');
11097 angular.element(tooltipText).css('display', 'block');
11098 angular.element(tooltipText).attr({'aria-live': 'polite'});
11099 angular.element(this).attr({'aria-describedby': angular.element(tooltipText).attr('id')});
11101 angular.element(textAreaElm).on('blur', function (e) {
11102 elem.removeClass('active');
11103 angular.element(tooltipText).css('display', 'none');
11104 angular.element(tooltipText).removeAttr('aria-live');
11105 angular.element(this).removeAttr('aria-describedby');
11112 * @name Navigation.att:TreeNavigation
11116 * @param {String} setRole - This value needs to be "tree". This is required to incorporate CATO requirements.
11117 * @param {Boolean} groupIt - This value needs to be "false" for top-level tree rendered.
11120 * <file src="src/treeNav/docs/readme.md" />
11123 * <div class="b2b-tree">
11124 * <b2b-tree-nav collection="treeStructure" set-role="tree" group-it="false"></b2b-tree-nav>
11127 * <section id="code">
11128 <example module="b2b.att">
11129 <file src="src/treeNav/docs/demo.html" />
11130 <file src="src/treeNav/docs/demo.js" />
11135 angular.module('b2b.att.treeNav', ['b2b.att.utilities'])
11136 .directive('b2bTreeNav', function () {
11145 templateUrl: function (element, attrs) {
11146 if (attrs.groupIt === 'true') {
11147 return "b2bTemplate/treeNav/groupedTree.html";
11149 return "b2bTemplate/treeNav/ungroupedTree.html";
11152 link: function (scope) {
11153 if (!(scope.setRole === 'tree')) {
11154 scope.setRole = 'group';
11159 .directive('b2bMember', ['$compile', '$timeout', 'keymap', function ($compile, $timeout, keymap) {
11167 templateUrl: 'b2bTemplate/treeNav/treeMember.html',
11168 link: function (scope, element, attrs) {
11169 scope.elemArr = [];
11170 var removeRootTabIndex = function (elem) {
11171 if (elem.parent().parent().eq(0).hasClass('b2b-tree')) {
11172 elem.attr('tabindex', -1);
11175 removeRootTabIndex(elem.parent());
11177 scope.$watch('member.child', function(newVal, oldVal){
11178 if(newVal !== oldVal){
11182 scope.showChild = function () {
11183 if (!element.hasClass('grouped')) {
11184 if (angular.isArray(scope.member.child) && scope.member.child.length > 0 && (scope.member.divide === undefined || scope.member.child.length < scope.member.divide)) {
11185 scope.groupIt = false;
11186 element.addClass('grouped');
11187 element.append("<b2b-tree-nav collection='member.child' group-it='" + scope.groupIt + "'></b2b-tree-nav>");
11188 $compile(element.contents())(scope);
11189 if(scope.member.active && scope.member.active === true){
11190 element.find('i').eq(0).removeClass('icon-primary-collapsed');
11192 if(scope.member.selected && scope.member.selected === true){
11193 element.attr('aria-selected', true);
11194 element.attr('tabindex', 0);
11195 removeRootTabIndex(element);
11197 if(scope.member.active && scope.member.active == undefined){
11198 element.find('i').eq(0).addClass('icon-primary-collapsed');
11200 } else if (scope.member.child && scope.member.divide && scope.member.child.length > scope.member.divide) {
11201 element.addClass('grouped');
11202 scope.groupIt = true;
11203 // FILTER - GROUPBY - APPROACH
11206 if(scope.member.child[0].groupName !== undefined){
11207 grpName = scope.member.child[0].groupName;
11210 var toSlice = scope.member.child[0].name.search(' ');
11211 grpName = scope.member.child[0].name.slice(0, toSlice);
11214 for (i = 0; i < scope.member.child.length; i += scope.member.divide) {
11216 for (j = j + i; j < (i + scope.member.divide); j++) {
11217 if (j === scope.member.child.length) {
11218 scope.member.child[j - 1].grpChild = grpName + ' ' + (i + 1) + ' - ' + (scope.member.child.length);
11221 if(scope.member.child[j-1].active && scope.member.child[j-1].active===true){
11222 scope.member.child[j-1].activeGrp = true;
11226 if (i + scope.member.divide > scope.member.child.length) {
11227 scope.member.child[j].grpChild = grpName + ' ' + (i + 1) + ' - ' + (scope.member.child.length);
11228 if(scope.member.child[j].active && scope.member.child[j].active===true){
11229 scope.member.child[j].activeGrp = true;
11233 scope.member.child[j].grpChild = grpName + ' ' + (i + 1) + ' - ' + (i + scope.member.divide);
11234 if(scope.member.child[j].active && scope.member.child[j].active===true){
11235 scope.member.child[j].activeGrp = true;
11240 if(scope.member.divide){
11241 element.append("<b2b-tree-nav collection='member.child' group-it='" + scope.groupIt + "'></b2b-tree-nav>");
11243 element.append("<b2b-tree-nav collection='member.child' group-it='" + scope.groupIt + "'></b2b-tree-nav>");
11245 $compile(element.contents())(scope);
11246 if(scope.member.active && scope.member.active === true){
11247 element.find('i').eq(0).removeClass('icon-primary-collapsed');
11249 if(scope.member.selected && scope.member.selected === true){
11250 element.attr('aria-selected', true);
11252 if( scope.member.active && scope.member.active == undefined){
11253 element.find('i').eq(0).addClass('icon-primary-collapsed');
11258 //Below condition opens node for opening on json load.
11259 if(scope.member.active && scope.member.active == true){
11262 if(scope.member.active == undefined && !element.find('a').eq(0).hasClass('active') && scope.member.child !== undefined){
11263 element.find('i').eq(0).addClass('icon-primary-collapsed');
11265 else if(scope.member.child == undefined){
11266 element.find('i').eq(0).addClass('icon-primary-circle');
11268 element.bind('keydown', function (evt) {
11269 switch (evt.keyCode) {
11270 case keymap.KEY.ENTER:
11271 if (element.hasClass('bg') && scope.member.onSelect !== undefined) {
11272 scope.member.onSelect(scope.member);
11274 evt.stopPropagation();
11281 //else getting true in every case .. so better use switch case .. that makes more sense you dumb.
11282 element.bind('click', function (evt) {
11284 var expandFunc = scope.member.onExpand;
11287 if (element.hasClass('bg') && scope.member.onSelect !== undefined) {
11288 scope.member.onSelect(scope.member);
11290 if (element.find('a').eq(0).hasClass('active') && scope.member.onExpand !== undefined) {
11291 var eValue = scope.member.onExpand(scope.member);
11293 if (!element.find('a').eq(0).hasClass('active') && scope.member.onCollapse !== undefined) {
11294 scope.member.onCollapse(scope.member);
11300 .directive('b2bTreeLink', ['keymap', '$timeout', function (keymap, $timeout) {
11303 link: function (scope, element, attr, ctrl) {
11304 var rootE, parentE, upE, downE;
11305 var closeOthersUp = function (elem,isKeyPress,passiveClose) {
11306 //For accordion functionality on sibling nodes
11307 if (elem.find('a').eq(0).hasClass('active')) {
11308 activeToggle(elem,isKeyPress,passiveClose);
11311 if (elem.hasClass('bg') && !isKeyPress) {
11312 elem.removeClass('bg');
11313 if (elem.attr('aria-selected')) {
11314 elem.attr('aria-selected', 'false');
11317 if (elem[0].previousElementSibling !== null) {
11318 closeOthersUp(angular.element(elem[0].previousElementSibling),isKeyPress);
11321 var closeOthersDown = function (elem,isKeyPress,passiveClose) {
11322 //For accordion functionality on sibling nodes
11323 if (elem.find('a').eq(0).hasClass('active')) {
11324 activeToggle(elem,isKeyPress,passiveClose);
11327 if (elem.hasClass('bg') && !isKeyPress) {
11328 elem.removeClass('bg');
11329 if (elem.attr('aria-selected')) {
11330 elem.attr('aria-selected', 'false');
11333 if (elem[0].nextElementSibling !== null) {
11334 closeOthersDown(angular.element(elem[0].nextElementSibling),isKeyPress);
11339 var removeBackground = function(elem){
11341 if(elem.hasClass('b2b-tree')){
11342 angular.element(elem[0].getElementsByClassName('bg')).removeClass('bg');
11345 removeBackground(elem.parent().parent());
11351 * These two functions used for setting heights on parent nodes as the child node closes
11352 * Retaining this code for future reference
11354 var addParentHeight = function(e, h) {
11355 var parentLi = e.parent().parent();
11356 var parentUl = e.parent();
11357 if(!parentLi.hasClass('b2b-tree')) {
11358 var addHeight = parentUl[0].offsetHeight + h;
11359 parentLi.find('ul').eq(0).css({
11360 height: addHeight+'px'
11362 addParentHeight(parentLi, h);
11366 var removeParentHeight = function(e, h) {
11367 var parentLi = e.parent().parent();
11368 var parentUl = e.parent();
11369 if(!parentLi.hasClass('b2b-tree')) {
11370 var addHeight = parentUl[0].offsetHeight - h;
11371 parentLi.find('ul').eq(0).css({
11372 height: addHeight+'px'
11374 removeParentHeight(parentLi, h);
11379 // isKeyPress - to notify that the function is called by Right Key press
11380 // passiveClose - prevents firing of oncollapse events during the action
11381 // of expand function(check the function definition)
11383 var activeToggle = function (elem,isKeyPress,passiveClose) {
11384 var element = elem.find('a').eq(0);
11385 if (element.hasClass('active')) {
11387 elem.removeClass('bg');
11390 if (elem.attr('aria-selected') && !isKeyPress) {
11391 elem.attr('aria-selected', 'false');
11393 if (!element.find('i').eq(0).hasClass('icon-primary-circle')) {
11394 if(isKeyPress && scope.member){
11395 if (scope.member.onCollapse !== undefined && !passiveClose) {
11396 scope.member.onCollapse(scope.member);
11399 element.removeClass('active');
11400 elem.attr('aria-expanded', 'false');
11401 element.find('i').eq(0).removeClass('icon-primary-expanded');
11402 element.find('i').eq(0).addClass('icon-primary-collapsed');
11403 //For Animation: below commented code is used to manually set height of UL to zero
11404 //retaining code for future reference
11406 var totalHeight = elem.find('ul')[0].scrollHeight;
11407 removeParentHeight(elem, totalHeight);
11408 elem.find('ul').eq(0).css({
11414 elem.addClass('bg');
11415 elem.attr('aria-selected', 'true');
11418 if (!element.find('i').eq(0).hasClass('icon-primary-circle')) {
11420 if(typeof scope.showChild === 'function' ){
11424 if (scope.member.onExpand !== undefined) {
11425 scope.member.onExpand(scope.member);
11429 element.addClass('active');
11430 elem.attr('aria-expanded', 'true');
11431 element.find('i').eq(0).removeClass('icon-primary-collapsed');
11432 element.find('i').eq(0).addClass('icon-primary-expanded');
11433 //For Animation: below commented code is used to manually set height of the ul generatedon the click of parent LI.
11434 //retaining code for future reference
11436 var totalHeight = elem.find('ul')[0].scrollHeight;
11437 addParentHeight(elem, totalHeight);
11438 elem.find('ul').eq(0).css({
11439 height: totalHeight+'px'
11445 element.bind('click', function (evt) {
11446 //first we close others and then we open the clicked element
11447 if (element[0].previousElementSibling) {
11448 closeOthersUp(angular.element(element[0].previousElementSibling));
11450 if (element[0].nextElementSibling) {
11451 closeOthersDown(angular.element(element[0].nextElementSibling));
11453 removeBackground(element);
11454 activeToggle(element);
11456 evt.stopPropagation();
11458 //default root tree element tabindex set zero
11459 if (element.parent().parent().hasClass('b2b-tree') && (element.parent()[0].previousElementSibling === null)) {
11460 element.attr('tabindex', 0);
11462 //check root via class
11463 var isRoot = function (elem) {
11464 if (elem.parent().parent().eq(0).hasClass('b2b-tree')) {
11470 var findRoot = function (elem) {
11471 if (isRoot(elem)) {
11475 findRoot(elem.parent());
11478 var findPreActive = function (elem) {
11480 if (!(elem.hasClass("active"))) {
11483 var childElems = angular.element(elem[0].nextElementSibling.children);
11484 lastE = angular.element(childElems[childElems.length - 1]);
11485 if (lastE.find('a').eq(0).hasClass('active')) {
11486 findPreActive(lastE.find('a').eq(0));
11492 var findUp = function (elem) {
11493 if (isRoot(elem)) {
11497 if (elem[0].previousElementSibling !== null && !angular.element(elem[0].previousElementSibling).hasClass('tree-hide')) {
11498 upE = angular.element(elem[0].previousElementSibling);
11499 if (upE.find('a').eq(0).hasClass('active')) {
11500 findPreActive(upE.find('a').eq(0));
11503 upE = elem.parent().parent();
11507 var downElement = function (elem) {
11508 if (elem.next().hasClass('tree-hide')) {
11509 downElement(elem.next());
11511 downE = elem.next();
11514 var isBottomElem = false;
11515 var downParent = function(liElem){
11516 if(liElem.eq(0).parent().parent().eq(0).hasClass('b2b-tree')){
11517 isBottomElem = true;
11520 if(liElem.next().length !== 0){
11521 downE = liElem.next().eq(0);
11525 downParent(liElem.parent().parent());
11529 var findDown = function (elem) {
11530 if (isRoot(elem.parent()) && !elem.hasClass('active')) {
11531 downE = elem.parent();
11534 if (elem.hasClass('active')) {
11535 downE = elem.next().find('li').eq(0);
11536 if (downE.hasClass('tree-hide')) {
11537 downElement(downE);
11541 downParent(elem.parent());
11542 if(isBottomElem === true){
11543 downE = elem.parent();
11544 isBottomElem = false;
11550 var resetTabPosition = function(element){
11552 angular.element(rootE.parent().parent()[0].querySelector("li[tabindex='0']")).attr('tabindex','-1');
11553 var elemToFocus = rootE.parent().parent()[0].querySelector(".bg")|| rootE;
11555 angular.element(elemToFocus).attr('tabindex','0');
11557 // Function to control the expansion of nodes when the user tabs into the tree and
11558 // the slected node is not visible
11559 var expand = function(elemArr){
11560 var elem= elemArr.pop();
11561 var element = elem.find('a').eq(0);
11562 var selectedNode = elem.parent().parent()[0].querySelector(".bg");
11563 if(selectedNode != null){
11565 element = elem.find('a').eq(0);
11566 if(!element.hasClass('active') ){
11569 if (elem[0].previousElementSibling) {
11570 closeOthersUp(angular.element(elem[0].previousElementSibling),true,true);
11572 if (elem[0].nextElementSibling) {
11573 closeOthersDown(angular.element(elem[0].nextElementSibling),true,true);
11576 if (!element.find('i').eq(0).hasClass('icon-primary-circle')) {
11577 if(typeof scope.showChild === 'function' ){
11580 element.addClass('active');
11581 elem.attr('aria-expanded', 'true');
11582 element.find('i').eq(0).removeClass('icon-primary-collapsed');
11583 element.find('i').eq(0).addClass('icon-primary-expanded');
11587 elem = elemArr.pop();
11595 element.find('a').eq(0).bind('mouseenter', function (evt) {
11596 angular.forEach(document.querySelectorAll('.activeTooltip'), function(value, key) {
11597 angular.element(value).removeClass('activeTooltip')
11599 element.addClass('activeTooltip');
11601 element.find('a').eq(0).bind('mouseleave', function (evt) {
11602 element.removeClass('activeTooltip');
11604 element.bind('focus', function (evt) {
11605 angular.forEach(document.querySelectorAll('.activeTooltip'), function(value, key) {
11606 angular.element(value).removeClass('activeTooltip')
11608 element.addClass('activeTooltip');
11610 element.bind('blur', function (evt) {
11611 element.removeClass('activeTooltip');
11613 element.bind('keydown', function (evt) {
11614 switch (evt.keyCode) {
11615 case keymap.KEY.HOME:
11616 evt.preventDefault();
11617 evt.stopPropagation();
11618 element.attr('tabindex', -1);
11620 rootE.eq(0).attr('tabindex', 0);
11623 case keymap.KEY.LEFT:
11624 evt.preventDefault();
11625 evt.stopPropagation();
11627 if(element.find('a').eq(0).hasClass('active')){
11628 if (element[0].previousElementSibling) {
11629 closeOthersUp(angular.element(element[0].previousElementSibling),true);
11631 if (element[0].nextElementSibling) {
11632 closeOthersDown(angular.element(element[0].nextElementSibling),true);
11634 activeToggle(element,true);
11637 element.attr('tabindex', -1);
11638 parentE = element.parent().parent();
11639 parentE.attr('tabindex', 0);
11640 parentE[0].focus();
11642 case keymap.KEY.UP:
11643 evt.preventDefault();
11644 evt.stopPropagation();
11645 element.attr('tabindex', -1);
11647 upE.eq(0).attr('tabindex', 0);
11650 case keymap.KEY.RIGHT:
11651 evt.preventDefault();
11652 evt.stopPropagation();
11653 if(element.find('i').eq(0).hasClass('icon-primary-circle')){
11656 if (!element.find('a').eq(0).hasClass('active')) {
11657 if (element[0].previousElementSibling) {
11658 closeOthersUp(angular.element(element[0].previousElementSibling),true);
11660 if (element[0].nextElementSibling) {
11661 closeOthersDown(angular.element(element[0].nextElementSibling),true);
11663 activeToggle(element,true);
11667 element.attr('tabindex', -1);
11668 findDown(element.find('a').eq(0));
11669 downE.eq(0).attr('tabindex', 0);
11673 case keymap.KEY.DOWN:
11674 evt.preventDefault();
11675 element.attr('tabindex', -1);
11676 findDown(element.find('a').eq(0));
11677 downE.eq(0).attr('tabindex', 0);
11679 evt.stopPropagation();
11681 case keymap.KEY.ENTER:
11682 var isSelectedElem = element.hasClass('bg');
11683 var enterFunc = function(element){
11684 if (isSelectedElem) {
11685 element.removeClass('bg');
11686 if (element.attr('aria-selected')) {
11687 element.attr('aria-selected', 'false');
11691 element.addClass('bg');
11692 element.attr('aria-selected', 'true');
11695 if (element[0].previousElementSibling) {
11696 closeOthersUp(angular.element(element[0].previousElementSibling));
11698 if (element[0].nextElementSibling) {
11699 closeOthersDown(angular.element(element[0].nextElementSibling));
11702 removeBackground(element);
11703 enterFunc(element);
11704 evt.stopPropagation();
11706 case keymap.KEY.TAB:
11707 $timeout(function(){
11708 resetTabPosition(element);
11710 evt.stopPropagation();
11717 element.bind('keyup',function(evt){
11718 if(evt.keyCode === keymap.KEY.TAB){
11720 var tempElem = element;
11722 while(!tempElem.hasClass('b2b-tree')){
11723 elemArr.push(tempElem);
11724 tempElem = tempElem.parent().parent();
11726 elemArr.push(tempElem);
11730 evt.stopPropagation();
11737 * @name Navigation.att:Tree nodes with checkboxes
11739 * @param {String} setRole - The value needs to be "tree". This is required to incorporate CATO requirements.
11740 * @param {boolean} groupIt - The value needs to be "false" for top-level tree rendered.
11741 * @param {Object} collection - The JSON object of tree to be rendered.
11743 * <file src="src/treeNodeCheckbox/docs/readme.md" />
11746 * <div class="b2b-tree-checkbox">
11747 * <b2b-tree-node-checkbox collection="treeStructure" set-role="tree" group-it="false"></b2b-tree-node-checkbox>
11750 * <section id="code">
11751 <example module="b2b.att">
11752 <file src="src/treeNodeCheckbox/docs/demo.html" />
11753 <file src="src/treeNodeCheckbox/docs/demo.js" />
11758 angular.module('b2b.att.treeNodeCheckbox', ['b2b.att.utilities'])
11759 .directive('b2bTreeNodeCheckbox', function () {
11768 templateUrl: function (element, attrs) {
11769 if (attrs.groupIt === 'true') {
11770 return "b2bTemplate/treeNodeCheckbox/groupedTree.html";
11772 return "b2bTemplate/treeNodeCheckbox/ungroupedTree.html";
11775 link: function (scope) {
11776 if (!(scope.setRole === 'tree')) {
11777 scope.setRole = 'group';
11782 .directive('b2bTreeMember', ['$compile', '$timeout', 'keymap', function ($compile, $timeout, keymap) {
11790 templateUrl: 'b2bTemplate/treeNodeCheckbox/treeMember.html',
11791 link: function (scope, element, attrs) {
11792 scope.elemArr = [];
11793 var removeRootTabIndex = function (elem) {
11794 if (elem.parent().parent().eq(0).hasClass('b2b-tree-checkbox')) {
11795 elem.attr('tabindex', -1);
11798 removeRootTabIndex(elem.parent());
11800 scope.$watch('member.child', function(newVal, oldVal){
11801 if(newVal !== oldVal){
11806 var checkedCount = 0;
11807 var nonCheckedCount = 0;
11808 var checkBoxesCount = 0;
11810 if(element.find('a').eq(0).find('input')){
11811 if(scope.member.indeterminate){
11812 element.find('a').eq(0).find('input').prop('indeterminate', true);
11813 element.attr('aria-checked',"mixed");
11815 element.attr('aria-checked',scope.member.isSelected);
11818 element.find('a').eq(0).find('input').bind('change',function(){
11819 scope.member.indeterminate = false;
11820 downwardModalUpdate(scope.member);
11821 downwardSelection(element);
11822 upwardSelection(element);
11823 element.attr('aria-checked',scope.member.isSelected);
11824 if (scope.member.onSelect !== undefined) {
11825 scope.member.onSelect(scope.member);
11829 element.find('a').eq(0).find('input').bind('click',function(){
11830 var elem = angular.element(this);
11831 if(scope.member.indeterminate){
11832 scope.member.indeterminate = false;
11833 scope.member.isSelected = true;
11834 elem.prop('indeterminate', false);
11835 elem.prop('checked', true);
11836 elem.triggerHandler('change');
11840 var groupNode = false;
11841 var checkedTreeNode = false;
11843 var isCheckboxSelected = function(elem){
11844 checkedTreeNode = false;
11845 checkedTreeNode = angular.element(angular.element(elem).find('a').eq(0))[0].querySelector('input.treeCheckBox').checked;
11848 var findCheckbox = function(elem){
11849 return angular.element(angular.element(elem).find('a').eq(0))[0].querySelector('input.treeCheckBox');
11852 var updateGrpNodeCheckboxes = function(elem, checked){
11853 angular.element(angular.element(elem).find('a').eq(0))[0].querySelector('input.treeCheckBox').checked = checked;
11857 var isGroupNode = function(elem){
11859 if(angular.element(angular.element(elem).find('a').eq(0))[0].querySelector('input.grpTreeCheckbox')){
11864 var downwardModalUpdate = function(curMember){
11865 angular.forEach(curMember.child, function(childMember, key) {
11866 childMember.isSelected = curMember.isSelected;
11867 childMember.indeterminate = false;
11868 if(angular.isArray(childMember.child) && scope.member.child.length > 0){
11869 downwardModalUpdate(childMember);
11874 var downwardSelection = function(elem){
11875 if(findCheckbox(elem)){
11876 isCheckboxSelected(elem)
11878 if(angular.element(elem).find('ul').length > 0){
11879 var childNodes = angular.element(elem).find('ul').eq(0).children('li');
11880 for(var i=0; i<childNodes.length; i++){
11881 if(findCheckbox(childNodes[i])){
11882 isGroupNode(childNodes[i]);
11883 angular.element(findCheckbox(childNodes[i])).prop('indeterminate', false);
11884 angular.element(childNodes[i]).attr('aria-checked',checkedTreeNode);
11886 updateGrpNodeCheckboxes(childNodes[i],checkedTreeNode);
11888 angular.element(childNodes[i]).scope().member.isSelected = checkedTreeNode;
11889 angular.element(childNodes[i]).scope().member.indeterminate = false
11890 angular.element(childNodes[i]).scope().$apply();
11892 downwardSelection(childNodes[i]);
11898 var upwardSelection = function(elem){
11899 var childNodes = elem.parent().parent().find('ul').eq(0).children('li');
11901 nonCheckedCount = 0;
11902 checkBoxesCount = 0;
11903 for(i=0; i<childNodes.length; i++){
11904 if(findCheckbox(childNodes[i])){
11905 isGroupNode(childNodes[i]);
11906 isCheckboxSelected(childNodes[i]);
11908 if(checkedTreeNode){
11910 }else if(!angular.element(angular.element(angular.element(childNodes[i]).find('a').eq(0))[0].querySelector('input.treeCheckBox')).prop('indeterminate')){
11915 var parentNodeScope;
11916 parentNodeScope = angular.element(elem.parent().parent()).scope();
11917 if(findCheckbox(elem.parent().parent())){
11918 if(nonCheckedCount == checkBoxesCount){
11919 angular.element(findCheckbox(elem.parent().parent())).prop('indeterminate', false);
11920 if(parentNodeScope && parentNodeScope.member){
11921 parentNodeScope.member.isSelected = false;
11922 parentNodeScope.member.indeterminate = false;
11924 updateGrpNodeCheckboxes(elem.parent().parent(),false);
11926 angular.element(elem.parent().parent()).attr('aria-checked',false);
11927 }else if(checkedCount == checkBoxesCount){
11928 angular.element(findCheckbox(elem.parent().parent())).prop('indeterminate', false);
11929 if(parentNodeScope && parentNodeScope.member){
11930 parentNodeScope.member.isSelected = true;
11931 parentNodeScope.member.indeterminate = false;
11933 updateGrpNodeCheckboxes(elem.parent().parent(),true);
11935 angular.element(elem.parent().parent()).attr('aria-checked',true);
11937 angular.element(findCheckbox(elem.parent().parent())).prop('indeterminate', true);
11938 if(parentNodeScope && parentNodeScope.member){
11939 parentNodeScope.member.isSelected = false;
11940 parentNodeScope.member.indeterminate = true;
11942 updateGrpNodeCheckboxes(elem.parent().parent(),false);
11944 angular.element(elem.parent().parent()).attr('aria-checked',"mixed");
11946 if(parentNodeScope && parentNodeScope.member){
11947 parentNodeScope.$apply();
11953 if(elem.parent().parent().attr('role') == "treeitem"){
11954 upwardSelection(elem.parent().parent());
11958 scope.showChild = function () {
11959 if (!element.hasClass('grouped')) {
11960 if (angular.isArray(scope.member.child) && scope.member.child.length > 0 && (scope.member.divide === undefined || scope.member.child.length < scope.member.divide)) {
11961 scope.groupIt = false;
11962 element.addClass('grouped');
11963 element.append("<b2b-tree-node-checkbox collection='member.child' group-it='" + scope.groupIt + "'></b2b-tree-node-checkbox>");
11964 $compile(element.contents())(scope);
11965 if(scope.member.active && scope.member.active === true){
11966 angular.element(element[0].querySelectorAll('i.expandCollapseIcon')).eq(0).removeClass('icon-primary-collapsed');
11968 if(scope.member.selected && scope.member.selected === true){
11969 element.attr('tabindex', 0);
11970 removeRootTabIndex(element);
11972 if(scope.member.active && scope.member.active == undefined){
11973 angular.element(element[0].querySelectorAll('i.expandCollapseIcon')).eq(0).addClass('icon-primary-collapsed');
11975 } else if (scope.member.child && scope.member.divide && scope.member.child.length > scope.member.divide) {
11976 element.addClass('grouped');
11977 scope.groupIt = true;
11980 if(scope.member.child[0].groupName !== undefined){
11981 grpName = scope.member.child[0].groupName;
11984 var toSlice = scope.member.child[0].name.search(' ');
11985 grpName = scope.member.child[0].name.slice(0, toSlice);
11988 for (i = 0; i < scope.member.child.length; i += scope.member.divide) {
11990 for (j = j + i; j < (i + scope.member.divide); j++) {
11991 if (j === scope.member.child.length) {
11992 scope.member.child[j - 1].grpChild = grpName + ' ' + (i + 1) + ' - ' + (scope.member.child.length);
11995 if(scope.member.child[j-1].active && scope.member.child[j-1].active===true){
11996 scope.member.child[j-1].activeGrp = true;
12000 if (i + scope.member.divide > scope.member.child.length) {
12001 scope.member.child[j].grpChild = grpName + ' ' + (i + 1) + ' - ' + (scope.member.child.length);
12002 if(scope.member.child[j].active && scope.member.child[j].active===true){
12003 scope.member.child[j].activeGrp = true;
12007 scope.member.child[j].grpChild = grpName + ' ' + (i + 1) + ' - ' + (i + scope.member.divide);
12008 if(scope.member.child[j].active && scope.member.child[j].active===true){
12009 scope.member.child[j].activeGrp = true;
12014 if(scope.member.divide){
12015 element.append("<b2b-tree-node-checkbox collection='member.child' group-it='" + scope.groupIt + "'></b2b-tree-node-checkbox>");
12017 element.append("<b2b-tree-node-checkbox collection='member.child' group-it='" + scope.groupIt + "'></b2b-tree-node-checkbox>");
12019 $compile(element.contents())(scope);
12020 if(scope.member.active && scope.member.active === true){
12021 angular.element(element[0].querySelectorAll('i.expandCollapseIcon')).eq(0).removeClass('icon-primary-collapsed');
12024 if( scope.member.active && scope.member.active == undefined){
12025 angular.element(element[0].querySelectorAll('i.expandCollapseIcon')).eq(0).addClass('icon-primary-collapsed');
12029 $timeout(function () {
12030 if(!scope.member.indeterminate){
12031 downwardSelection(element);
12037 if(scope.member.active && scope.member.active == true){
12040 if(scope.member.active == undefined && !element.find('a').eq(0).hasClass('active') && scope.member.child !== undefined){
12041 angular.element(element[0].querySelectorAll('i.expandCollapseIcon')).eq(0).addClass('icon-primary-collapsed');
12043 else if(scope.member.child == undefined){
12044 angular.element(element[0].querySelectorAll('i.expandCollapseIcon')).eq(0).addClass('icon-primary-circle');
12045 if(scope.$parent.$index === 0) {
12046 element.find('a').eq(0).append('<span class="first-link"></span>');
12050 angular.element(element[0].querySelectorAll('i.expandCollapseIcon')).eq(0).bind('click', function (evt) {
12052 var expandFunc = scope.member.onExpand;
12053 if (element.find('a').eq(0).hasClass('active') && scope.member.onExpand !== undefined) {
12054 var eValue = scope.member.onExpand(scope.member);
12056 if (!element.find('a').eq(0).hasClass('active') && scope.member.onCollapse !== undefined) {
12057 scope.member.onCollapse(scope.member);
12061 angular.element(element[0].querySelectorAll('.treeNodeName')).eq(0).bind('click', function (evt) {
12068 .directive('b2bTreeNodeLink', ['keymap', '$timeout', function (keymap, $timeout) {
12071 link: function (scope, element, attr, ctrl) {
12072 var rootE, parentE, upE, downE;
12073 var closeOthersUp = function (elem) {
12075 if (elem.find('a').eq(0).hasClass('active')) {
12076 activeToggle(elem);
12079 if (elem.hasClass('bg')) {
12080 elem.removeClass('bg');
12082 if (elem[0].previousElementSibling !== null) {
12083 closeOthersUp(angular.element(elem[0].previousElementSibling));
12086 var closeOthersDown = function (elem) {
12088 if (elem.find('a').eq(0).hasClass('active')) {
12089 activeToggle(elem);
12092 if (elem.hasClass('bg')) {
12093 elem.removeClass('bg');
12095 if (elem[0].nextElementSibling !== null) {
12096 closeOthersDown(angular.element(elem[0].nextElementSibling));
12100 var removeBackgroundUp = function (elem) {
12102 if (elem.hasClass('b2b-tree-checkbox')) {
12105 elem.parent().parent().removeClass('bg');
12106 removeBackgroundUp(elem.parent().parent());
12110 var removeBackgroundDown = function (elem) {
12112 angular.element(elem[0].querySelector('.bg')).removeClass('bg');
12117 var activeToggle = function (elem) {
12118 var element = elem.find('a').eq(0);
12119 if (element.hasClass('active')) {
12120 elem.removeClass('bg');
12121 if (!angular.element(element[0].querySelectorAll('i.expandCollapseIcon')).eq(0).hasClass('icon-primary-circle')) {
12122 element.removeClass('active');
12123 elem.attr('aria-expanded', 'false');
12124 angular.element(element[0].querySelectorAll('i.expandCollapseIcon')).eq(0).removeClass('icon-primary-expanded');
12125 angular.element(element[0].querySelectorAll('i.expandCollapseIcon')).eq(0).addClass('icon-primary-collapsed');
12128 elem.addClass('bg');
12129 if (!angular.element(element[0].querySelectorAll('i.expandCollapseIcon')).eq(0).hasClass('icon-primary-circle')) {
12130 element.addClass('active');
12131 elem.attr('aria-expanded', 'true');
12132 angular.element(element[0].querySelectorAll('i.expandCollapseIcon')).eq(0).removeClass('icon-primary-collapsed');
12133 angular.element(element[0].querySelectorAll('i.expandCollapseIcon')).eq(0).addClass('icon-primary-expanded');
12137 angular.element(element[0].querySelectorAll('i.expandCollapseIcon')).eq(0).bind('click', function (evt) {
12139 if (element[0].previousElementSibling) {
12140 closeOthersUp(angular.element(element[0].previousElementSibling));
12142 if (element[0].nextElementSibling) {
12143 closeOthersDown(angular.element(element[0].nextElementSibling));
12146 activeToggle(element);
12148 removeBackgroundDown(element);
12149 removeBackgroundUp(element);
12150 evt.stopPropagation();
12153 if (element.parent().parent().hasClass('b2b-tree-checkbox') && (element.parent()[0].previousElementSibling === null)) {
12154 element.attr('tabindex', 0);
12157 var isRoot = function (elem) {
12158 if (elem.parent().parent().eq(0).hasClass('b2b-tree-checkbox')) {
12164 var findRoot = function (elem) {
12165 if (isRoot(elem)) {
12169 findRoot(elem.parent());
12172 var findPreActive = function (elem) {
12174 if (!(elem.hasClass("active"))) {
12177 var childElems = angular.element(elem[0].nextElementSibling.children);
12178 lastE = angular.element(childElems[childElems.length - 1]);
12179 if (lastE.find('a').eq(0).hasClass('active')) {
12180 findPreActive(lastE.find('a').eq(0));
12186 var findUp = function (elem) {
12187 if (isRoot(elem)) {
12191 if (elem[0].previousElementSibling !== null && !angular.element(elem[0].previousElementSibling).hasClass('tree-hide')) {
12192 upE = angular.element(elem[0].previousElementSibling);
12193 if (upE.find('a').eq(0).hasClass('active')) {
12194 findPreActive(upE.find('a').eq(0));
12197 upE = elem.parent().parent();
12201 var downElement = function (elem) {
12202 if (elem.next().hasClass('tree-hide')) {
12203 downElement(elem.next());
12205 downE = elem.next();
12208 var isBottomElem = false;
12209 var downParent = function(liElem){
12210 if(liElem.eq(0).parent().parent().eq(0).hasClass('b2b-tree-checkbox')){
12211 isBottomElem = true;
12214 if(liElem.next().length !== 0){
12215 downE = liElem.next().eq(0);
12219 downParent(liElem.parent().parent());
12223 var findDown = function (elem) {
12224 if (isRoot(elem.parent()) && !elem.hasClass('active')) {
12225 downE = elem.parent();
12228 if (elem.hasClass('active')) {
12229 downE = elem.next().find('li').eq(0);
12230 if (downE.hasClass('tree-hide')) {
12231 downElement(downE);
12235 downParent(elem.parent());
12236 if(isBottomElem === true){
12237 downE = elem.parent();
12238 isBottomElem = false;
12242 element.bind('keydown', function (evt) {
12243 switch (evt.keyCode) {
12244 case keymap.KEY.HOME:
12245 evt.preventDefault();
12246 evt.stopPropagation();
12247 element.attr('tabindex', -1);
12249 rootE.eq(0).attr('tabindex', 0);
12252 case keymap.KEY.LEFT:
12253 evt.preventDefault();
12254 evt.stopPropagation();
12255 if (!isRoot(element)) {
12256 if(element.find('a').eq(0).hasClass('active')){
12257 angular.element(element[0].querySelectorAll('i.expandCollapseIcon')).eq(0).triggerHandler('click');
12260 element.attr('tabindex', -1);
12261 parentE = element.parent().parent();
12262 parentE.attr('tabindex', 0);
12263 parentE[0].focus();
12265 if (element.find('a').eq(0).hasClass('active')) {
12266 angular.element(element[0].querySelectorAll('i.expandCollapseIcon')).eq(0).triggerHandler('click');
12270 case keymap.KEY.UP:
12271 evt.preventDefault();
12272 evt.stopPropagation();
12273 element.attr('tabindex', -1);
12275 upE.eq(0).attr('tabindex', 0);
12278 case keymap.KEY.RIGHT:
12279 evt.preventDefault();
12280 evt.stopPropagation();
12281 if(angular.element(element[0].querySelectorAll('i.expandCollapseIcon')).eq(0).hasClass('icon-primary-circle')){
12284 if (!element.find('a').eq(0).hasClass('active')) {
12285 angular.element(element[0].querySelectorAll('i.expandCollapseIcon')).eq(0).triggerHandler('click');
12288 element.attr('tabindex', -1);
12289 findDown(element.find('a').eq(0));
12290 downE.eq(0).attr('tabindex', 0);
12294 case keymap.KEY.DOWN:
12295 evt.preventDefault();
12296 element.attr('tabindex', -1);
12297 findDown(element.find('a').eq(0));
12298 downE.eq(0).attr('tabindex', 0);
12300 evt.stopPropagation();
12302 case keymap.KEY.SPACE:
12303 case keymap.KEY.ENTER:
12304 evt.preventDefault();
12305 evt.stopPropagation();
12306 if(angular.isDefined(element.scope().member.isSelected)){
12307 element.scope().member.isSelected = !element.scope().member.isSelected;
12308 element.scope().member.indeterminate = false;
12309 element.scope().$apply();
12310 element.find('a').eq(0).find('input').prop('indeterminate', false);
12311 element.find('a').eq(0).find('input').triggerHandler('change');
12324 * UPDATES AND DOCS AT: http://www.greensock.com
12326 * @license Copyright (c) 2008-2014, GreenSock. All rights reserved.
12327 * This work is subject to the terms at http://www.greensock.com/terms_of_use.html or for
12328 * Club GreenSock members, the software agreement that was issued with your membership.
12330 * @author: Jack Doyle, jack@greensock.com
12332 (window._gsQueue || (window._gsQueue = [])).push( function() {
12336 var _doc = document.documentElement,
12338 _max = function(element, axis) {
12339 var dim = (axis === "x") ? "Width" : "Height",
12340 scroll = "scroll" + dim,
12341 client = "client" + dim,
12342 body = document.body;
12343 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];
12346 ScrollToPlugin = window._gsDefine.plugin({
12347 propName: "scrollTo",
12351 //called when the tween renders for the first time. This is where initial values should be recorded and any setup routines should run.
12352 init: function(target, value, tween) {
12353 this._wdw = (target === _window);
12354 this._target = target;
12355 this._tween = tween;
12356 if (typeof(value) !== "object") {
12357 value = {y:value}; //if we don't receive an object as the parameter, assume the user intends "y".
12359 this._autoKill = (value.autoKill !== false);
12360 this.x = this.xPrev = this.getX();
12361 this.y = this.yPrev = this.getY();
12362 if (value.x != null) {
12363 this._addTween(this, "x", this.x, (value.x === "max") ? _max(target, "x") : value.x, "scrollTo_x", true);
12364 this._overwriteProps.push("scrollTo_x");
12368 if (value.y != null) {
12369 this._addTween(this, "y", this.y, (value.y === "max") ? _max(target, "y") : value.y, "scrollTo_y", true);
12370 this._overwriteProps.push("scrollTo_y");
12377 //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.)
12379 this._super.setRatio.call(this, v);
12381 var x = (this._wdw || !this.skipX) ? this.getX() : this.xPrev,
12382 y = (this._wdw || !this.skipY) ? this.getY() : this.yPrev,
12383 yDif = y - this.yPrev,
12384 xDif = x - this.xPrev;
12386 if (this._autoKill) {
12387 //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.
12388 if (!this.skipX && (xDif > 7 || xDif < -7) && x < _max(this._target, "x")) {
12389 this.skipX = true; //if the user scrolls separately, we should stop tweening!
12391 if (!this.skipY && (yDif > 7 || yDif < -7) && y < _max(this._target, "y")) {
12392 this.skipY = true; //if the user scrolls separately, we should stop tweening!
12394 if (this.skipX && this.skipY) {
12395 this._tween.kill();
12399 _window.scrollTo((!this.skipX) ? this.x : x, (!this.skipY) ? this.y : y);
12402 this._target.scrollTop = this.y;
12405 this._target.scrollLeft = this.x;
12408 this.xPrev = this.x;
12409 this.yPrev = this.y;
12413 p = ScrollToPlugin.prototype;
12415 ScrollToPlugin.max = _max;
12417 p.getX = function() {
12418 return (!this._wdw) ? this._target.scrollLeft : (_window.pageXOffset != null) ? _window.pageXOffset : (_doc.scrollLeft != null) ? _doc.scrollLeft : document.body.scrollLeft;
12421 p.getY = function() {
12422 return (!this._wdw) ? this._target.scrollTop : (_window.pageYOffset != null) ? _window.pageYOffset : (_doc.scrollTop != null) ? _doc.scrollTop : document.body.scrollTop;
12425 p._kill = function(lookup) {
12426 if (lookup.scrollTo_x) {
12429 if (lookup.scrollTo_y) {
12432 return this._super._kill.call(this, lookup);
12435 }); if (window._gsDefine) { window._gsQueue.pop()(); }
12439 * UPDATES AND DOCS AT: http://www.greensock.com
12441 * Includes all of the following: TweenLite, TweenMax, TimelineLite, TimelineMax, EasePack, CSSPlugin, RoundPropsPlugin, BezierPlugin, AttrPlugin, DirectionalRotationPlugin
12443 * @license Copyright (c) 2008-2014, GreenSock. All rights reserved.
12444 * This work is subject to the terms at http://www.greensock.com/terms_of_use.html or for
12445 * Club GreenSock members, the software agreement that was issued with your membership.
12447 * @author: Jack Doyle, jack@greensock.com
12450 (window._gsQueue || (window._gsQueue = [])).push( function() {
12454 window._gsDefine("TweenMax", ["core.Animation","core.SimpleTimeline","TweenLite"], function(Animation, SimpleTimeline, TweenLite) {
12456 var _slice = [].slice,
12457 TweenMax = function(target, duration, vars) {
12458 TweenLite.call(this, target, duration, vars);
12460 this._yoyo = (this.vars.yoyo === true);
12461 this._repeat = this.vars.repeat || 0;
12462 this._repeatDelay = this.vars.repeatDelay || 0;
12463 this._dirty = true; //ensures that if there is any repeat, the totalDuration will get recalculated to accurately report it.
12464 this.render = TweenMax.prototype.render; //speed optimization (avoid prototype lookup on this "hot" method)
12466 _tinyNum = 0.0000000001,
12467 TweenLiteInternals = TweenLite._internals,
12468 _isSelector = TweenLiteInternals.isSelector,
12469 _isArray = TweenLiteInternals.isArray,
12470 p = TweenMax.prototype = TweenLite.to({}, 0.1, {}),
12473 TweenMax.version = "1.12.1";
12474 p.constructor = TweenMax;
12475 p.kill()._gc = false;
12476 TweenMax.killTweensOf = TweenMax.killDelayedCallsTo = TweenLite.killTweensOf;
12477 TweenMax.getTweensOf = TweenLite.getTweensOf;
12478 TweenMax.lagSmoothing = TweenLite.lagSmoothing;
12479 TweenMax.ticker = TweenLite.ticker;
12480 TweenMax.render = TweenLite.render;
12482 p.invalidate = function() {
12483 this._yoyo = (this.vars.yoyo === true);
12484 this._repeat = this.vars.repeat || 0;
12485 this._repeatDelay = this.vars.repeatDelay || 0;
12486 this._uncache(true);
12487 return TweenLite.prototype.invalidate.call(this);
12490 p.updateTo = function(vars, resetDuration) {
12491 var curRatio = this.ratio, p;
12492 if (resetDuration && this._startTime < this._timeline._time) {
12493 this._startTime = this._timeline._time;
12494 this._uncache(false);
12496 this._enabled(true, false);
12498 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.
12502 this.vars[p] = vars[p];
12504 if (this._initted) {
12505 if (resetDuration) {
12506 this._initted = false;
12509 this._enabled(true, false);
12511 if (this._notifyPluginsOfEnabled && this._firstPT) {
12512 TweenLite._onPluginEvent("_onDisable", this); //in case a plugin like MotionBlur must perform some cleanup tasks
12514 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.
12515 var prevTime = this._time;
12516 this.render(0, true, false);
12517 this._initted = false;
12518 this.render(prevTime, true, false);
12519 } else if (this._time > 0) {
12520 this._initted = false;
12522 var inv = 1 / (1 - curRatio),
12523 pt = this._firstPT, endValue;
12525 endValue = pt.s + pt.c;
12527 pt.s = endValue - pt.c;
12536 p.render = function(time, suppressEvents, force) {
12537 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.
12540 var totalDur = (!this._dirty) ? this._totalDuration : this.totalDuration(),
12541 prevTime = this._time,
12542 prevTotalTime = this._totalTime,
12543 prevCycle = this._cycle,
12544 duration = this._duration,
12545 prevRawPrevTime = this._rawPrevTime,
12546 isComplete, callback, pt, cycleDuration, r, type, pow, rawPrevTime, i;
12547 if (time >= totalDur) {
12548 this._totalTime = totalDur;
12549 this._cycle = this._repeat;
12550 if (this._yoyo && (this._cycle & 1) !== 0) {
12552 this.ratio = this._ease._calcEnd ? this._ease.getRatio(0) : 0;
12554 this._time = duration;
12555 this.ratio = this._ease._calcEnd ? this._ease.getRatio(1) : 1;
12557 if (!this._reversed) {
12559 callback = "onComplete";
12561 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.
12562 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.
12565 if (time === 0 || prevRawPrevTime < 0 || prevRawPrevTime === _tinyNum) if (prevRawPrevTime !== time) {
12567 if (prevRawPrevTime > _tinyNum) {
12568 callback = "onReverseComplete";
12571 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.
12574 } else if (time < 0.0000001) { //to work around occasional floating point math artifacts, round super small values to 0.
12575 this._totalTime = this._time = this._cycle = 0;
12576 this.ratio = this._ease._calcEnd ? this._ease.getRatio(0) : 0;
12577 if (prevTotalTime !== 0 || (duration === 0 && prevRawPrevTime > 0 && prevRawPrevTime !== _tinyNum)) {
12578 callback = "onReverseComplete";
12579 isComplete = this._reversed;
12582 this._active = false;
12583 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.
12584 if (prevRawPrevTime >= 0) {
12587 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.
12589 } 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.
12593 this._totalTime = this._time = time;
12595 if (this._repeat !== 0) {
12596 cycleDuration = duration + this._repeatDelay;
12597 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!)
12598 if (this._cycle !== 0) if (this._cycle === this._totalTime / cycleDuration) {
12599 this._cycle--; //otherwise when rendered exactly at the end time, it will act as though it is repeating (at the beginning)
12601 this._time = this._totalTime - (this._cycle * cycleDuration);
12602 if (this._yoyo) if ((this._cycle & 1) !== 0) {
12603 this._time = duration - this._time;
12605 if (this._time > duration) {
12606 this._time = duration;
12607 } else if (this._time < 0) {
12612 if (this._easeType) {
12613 r = this._time / duration;
12614 type = this._easeType;
12615 pow = this._easePower;
12616 if (type === 1 || (type === 3 && r >= 0.5)) {
12624 } else if (pow === 2) {
12626 } else if (pow === 3) {
12628 } else if (pow === 4) {
12629 r *= r * r * r * r;
12633 this.ratio = 1 - r;
12634 } else if (type === 2) {
12636 } else if (this._time / duration < 0.5) {
12637 this.ratio = r / 2;
12639 this.ratio = 1 - (r / 2);
12643 this.ratio = this._ease.getRatio(this._time / duration);
12648 if (prevTime === this._time && !force && prevCycle === this._cycle) {
12649 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.
12650 this._onUpdate.apply(this.vars.onUpdateScope || this, this.vars.onUpdateParams || _blankArray);
12653 } else if (!this._initted) {
12655 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.
12657 } 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.
12658 this._time = prevTime;
12659 this._totalTime = prevTotalTime;
12660 this._rawPrevTime = prevRawPrevTime;
12661 this._cycle = prevCycle;
12662 TweenLiteInternals.lazyTweens.push(this);
12666 //_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.
12667 if (this._time && !isComplete) {
12668 this.ratio = this._ease.getRatio(this._time / duration);
12669 } else if (isComplete && this._ease._calcEnd) {
12670 this.ratio = this._ease.getRatio((this._time === 0) ? 0 : 1);
12673 if (this._lazy !== false) {
12674 this._lazy = false;
12677 if (!this._active) if (!this._paused && this._time !== prevTime && time >= 0) {
12678 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.
12680 if (prevTotalTime === 0) {
12681 if (this._initted === 2 && time > 0) {
12682 //this.invalidate();
12683 this._init(); //will just apply overwriting since _initted of (2) means it was a from() tween that had immediateRender:true
12685 if (this._startAt) {
12687 this._startAt.render(time, suppressEvents, force);
12688 } else if (!callback) {
12689 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.
12692 if (this.vars.onStart) if (this._totalTime !== 0 || duration === 0) if (!suppressEvents) {
12693 this.vars.onStart.apply(this.vars.onStartScope || this, this.vars.onStartParams || _blankArray);
12697 pt = this._firstPT;
12700 pt.t[pt.p](pt.c * this.ratio + pt.s);
12702 pt.t[pt.p] = pt.c * this.ratio + pt.s;
12707 if (this._onUpdate) {
12708 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.
12709 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.
12711 if (!suppressEvents) if (this._totalTime !== prevTotalTime || isComplete) {
12712 this._onUpdate.apply(this.vars.onUpdateScope || this, this.vars.onUpdateParams || _blankArray);
12715 if (this._cycle !== prevCycle) if (!suppressEvents) if (!this._gc) if (this.vars.onRepeat) {
12716 this.vars.onRepeat.apply(this.vars.onRepeatScope || this, this.vars.onRepeatParams || _blankArray);
12718 if (callback) if (!this._gc) { //check gc because there's a chance that kill() could be called in an onUpdate
12719 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.
12720 this._startAt.render(time, suppressEvents, force);
12723 if (this._timeline.autoRemoveChildren) {
12724 this._enabled(false, false);
12726 this._active = false;
12728 if (!suppressEvents && this.vars[callback]) {
12729 this.vars[callback].apply(this.vars[callback + "Scope"] || this, this.vars[callback + "Params"] || _blankArray);
12731 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.
12732 this._rawPrevTime = 0;
12737 //---- STATIC FUNCTIONS -----------------------------------------------------------------------------------------------------------
12739 TweenMax.to = function(target, duration, vars) {
12740 return new TweenMax(target, duration, vars);
12743 TweenMax.from = function(target, duration, vars) {
12744 vars.runBackwards = true;
12745 vars.immediateRender = (vars.immediateRender != false);
12746 return new TweenMax(target, duration, vars);
12749 TweenMax.fromTo = function(target, duration, fromVars, toVars) {
12750 toVars.startAt = fromVars;
12751 toVars.immediateRender = (toVars.immediateRender != false && fromVars.immediateRender != false);
12752 return new TweenMax(target, duration, toVars);
12755 TweenMax.staggerTo = TweenMax.allTo = function(targets, duration, vars, stagger, onCompleteAll, onCompleteAllParams, onCompleteAllScope) {
12756 stagger = stagger || 0;
12757 var delay = vars.delay || 0,
12759 finalComplete = function() {
12760 if (vars.onComplete) {
12761 vars.onComplete.apply(vars.onCompleteScope || this, arguments);
12763 onCompleteAll.apply(onCompleteAllScope || this, onCompleteAllParams || _blankArray);
12766 if (!_isArray(targets)) {
12767 if (typeof(targets) === "string") {
12768 targets = TweenLite.selector(targets) || targets;
12770 if (_isSelector(targets)) {
12771 targets = _slice.call(targets, 0);
12774 l = targets.length;
12775 for (i = 0; i < l; i++) {
12780 copy.delay = delay;
12781 if (i === l - 1 && onCompleteAll) {
12782 copy.onComplete = finalComplete;
12784 a[i] = new TweenMax(targets[i], duration, copy);
12790 TweenMax.staggerFrom = TweenMax.allFrom = function(targets, duration, vars, stagger, onCompleteAll, onCompleteAllParams, onCompleteAllScope) {
12791 vars.runBackwards = true;
12792 vars.immediateRender = (vars.immediateRender != false);
12793 return TweenMax.staggerTo(targets, duration, vars, stagger, onCompleteAll, onCompleteAllParams, onCompleteAllScope);
12796 TweenMax.staggerFromTo = TweenMax.allFromTo = function(targets, duration, fromVars, toVars, stagger, onCompleteAll, onCompleteAllParams, onCompleteAllScope) {
12797 toVars.startAt = fromVars;
12798 toVars.immediateRender = (toVars.immediateRender != false && fromVars.immediateRender != false);
12799 return TweenMax.staggerTo(targets, duration, toVars, stagger, onCompleteAll, onCompleteAllParams, onCompleteAllScope);
12802 TweenMax.delayedCall = function(delay, callback, params, scope, useFrames) {
12803 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});
12806 TweenMax.set = function(target, vars) {
12807 return new TweenMax(target, 0, vars);
12810 TweenMax.isTweening = function(target) {
12811 return (TweenLite.getTweensOf(target, true).length > 0);
12814 var _getChildrenOf = function(timeline, includeTimelines) {
12817 tween = timeline._first;
12819 if (tween instanceof TweenLite) {
12822 if (includeTimelines) {
12825 a = a.concat(_getChildrenOf(tween, includeTimelines));
12828 tween = tween._next;
12832 getAllTweens = TweenMax.getAllTweens = function(includeTimelines) {
12833 return _getChildrenOf(Animation._rootTimeline, includeTimelines).concat( _getChildrenOf(Animation._rootFramesTimeline, includeTimelines) );
12836 TweenMax.killAll = function(complete, tweens, delayedCalls, timelines) {
12837 if (tweens == null) {
12840 if (delayedCalls == null) {
12841 delayedCalls = true;
12843 var a = getAllTweens((timelines != false)),
12845 allTrue = (tweens && delayedCalls && timelines),
12847 for (i = 0; i < l; i++) {
12849 if (allTrue || (tween instanceof SimpleTimeline) || ((isDC = (tween.target === tween.vars.onComplete)) && delayedCalls) || (tweens && !isDC)) {
12851 tween.totalTime(tween._reversed ? 0 : tween.totalDuration());
12853 tween._enabled(false, false);
12859 TweenMax.killChildTweensOf = function(parent, complete) {
12860 if (parent == null) {
12863 var tl = TweenLiteInternals.tweenLookup,
12864 a, curParent, p, i, l;
12865 if (typeof(parent) === "string") {
12866 parent = TweenLite.selector(parent) || parent;
12868 if (_isSelector(parent)) {
12869 parent = _slice.call(parent, 0);
12871 if (_isArray(parent)) {
12874 TweenMax.killChildTweensOf(parent[i], complete);
12880 curParent = tl[p].target.parentNode;
12881 while (curParent) {
12882 if (curParent === parent) {
12883 a = a.concat(tl[p].tweens);
12885 curParent = curParent.parentNode;
12889 for (i = 0; i < l; i++) {
12891 a[i].totalTime(a[i].totalDuration());
12893 a[i]._enabled(false, false);
12897 var _changePause = function(pause, tweens, delayedCalls, timelines) {
12898 tweens = (tweens !== false);
12899 delayedCalls = (delayedCalls !== false);
12900 timelines = (timelines !== false);
12901 var a = getAllTweens(timelines),
12902 allTrue = (tweens && delayedCalls && timelines),
12907 if (allTrue || (tween instanceof SimpleTimeline) || ((isDC = (tween.target === tween.vars.onComplete)) && delayedCalls) || (tweens && !isDC)) {
12908 tween.paused(pause);
12913 TweenMax.pauseAll = function(tweens, delayedCalls, timelines) {
12914 _changePause(true, tweens, delayedCalls, timelines);
12917 TweenMax.resumeAll = function(tweens, delayedCalls, timelines) {
12918 _changePause(false, tweens, delayedCalls, timelines);
12921 TweenMax.globalTimeScale = function(value) {
12922 var tl = Animation._rootTimeline,
12923 t = TweenLite.ticker.time;
12924 if (!arguments.length) {
12925 return tl._timeScale;
12927 value = value || _tinyNum; //can't allow zero because it'll throw the math off
12928 tl._startTime = t - ((t - tl._startTime) * tl._timeScale / value);
12929 tl = Animation._rootFramesTimeline;
12930 t = TweenLite.ticker.frame;
12931 tl._startTime = t - ((t - tl._startTime) * tl._timeScale / value);
12932 tl._timeScale = Animation._rootTimeline._timeScale = value;
12937 //---- GETTERS / SETTERS ----------------------------------------------------------------------------------------------------------
12939 p.progress = function(value) {
12940 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);
12943 p.totalProgress = function(value) {
12944 return (!arguments.length) ? this._totalTime / this.totalDuration() : this.totalTime( this.totalDuration() * value, false);
12947 p.time = function(value, suppressEvents) {
12948 if (!arguments.length) {
12952 this.totalDuration();
12954 if (value > this._duration) {
12955 value = this._duration;
12957 if (this._yoyo && (this._cycle & 1) !== 0) {
12958 value = (this._duration - value) + (this._cycle * (this._duration + this._repeatDelay));
12959 } else if (this._repeat !== 0) {
12960 value += this._cycle * (this._duration + this._repeatDelay);
12962 return this.totalTime(value, suppressEvents);
12965 p.duration = function(value) {
12966 if (!arguments.length) {
12967 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.
12969 return Animation.prototype.duration.call(this, value);
12972 p.totalDuration = function(value) {
12973 if (!arguments.length) {
12975 //instead of Infinity, we use 999999999999 so that we can accommodate reverses
12976 this._totalDuration = (this._repeat === -1) ? 999999999999 : this._duration * (this._repeat + 1) + (this._repeatDelay * this._repeat);
12977 this._dirty = false;
12979 return this._totalDuration;
12981 return (this._repeat === -1) ? this : this.duration( (value - (this._repeat * this._repeatDelay)) / (this._repeat + 1) );
12984 p.repeat = function(value) {
12985 if (!arguments.length) {
12986 return this._repeat;
12988 this._repeat = value;
12989 return this._uncache(true);
12992 p.repeatDelay = function(value) {
12993 if (!arguments.length) {
12994 return this._repeatDelay;
12996 this._repeatDelay = value;
12997 return this._uncache(true);
13000 p.yoyo = function(value) {
13001 if (!arguments.length) {
13004 this._yoyo = value;
13021 * ----------------------------------------------------------------
13023 * ----------------------------------------------------------------
13025 window._gsDefine("TimelineLite", ["core.Animation","core.SimpleTimeline","TweenLite"], function(Animation, SimpleTimeline, TweenLite) {
13027 var TimelineLite = function(vars) {
13028 SimpleTimeline.call(this, vars);
13030 this.autoRemoveChildren = (this.vars.autoRemoveChildren === true);
13031 this.smoothChildTiming = (this.vars.smoothChildTiming === true);
13032 this._sortChildren = true;
13033 this._onUpdate = this.vars.onUpdate;
13038 if (_isArray(val)) if (val.join("").indexOf("{self}") !== -1) {
13039 v[p] = this._swapSelfInParams(val);
13042 if (_isArray(v.tweens)) {
13043 this.add(v.tweens, 0, v.align, v.stagger);
13046 _tinyNum = 0.0000000001,
13047 _isSelector = TweenLite._internals.isSelector,
13048 _isArray = TweenLite._internals.isArray,
13050 _globals = window._gsDefine.globals,
13051 _copy = function(vars) {
13058 _pauseCallback = function(tween, callback, params, scope) {
13059 tween._timeline.pause(tween._startTime);
13061 callback.apply(scope || tween._timeline, params || _blankArray);
13064 _slice = _blankArray.slice,
13065 p = TimelineLite.prototype = new SimpleTimeline();
13067 TimelineLite.version = "1.12.1";
13068 p.constructor = TimelineLite;
13069 p.kill()._gc = false;
13071 p.to = function(target, duration, vars, position) {
13072 var Engine = (vars.repeat && _globals.TweenMax) || TweenLite;
13073 return duration ? this.add( new Engine(target, duration, vars), position) : this.set(target, vars, position);
13076 p.from = function(target, duration, vars, position) {
13077 return this.add( ((vars.repeat && _globals.TweenMax) || TweenLite).from(target, duration, vars), position);
13080 p.fromTo = function(target, duration, fromVars, toVars, position) {
13081 var Engine = (toVars.repeat && _globals.TweenMax) || TweenLite;
13082 return duration ? this.add( Engine.fromTo(target, duration, fromVars, toVars), position) : this.set(target, toVars, position);
13085 p.staggerTo = function(targets, duration, vars, stagger, position, onCompleteAll, onCompleteAllParams, onCompleteAllScope) {
13086 var tl = new TimelineLite({onComplete:onCompleteAll, onCompleteParams:onCompleteAllParams, onCompleteScope:onCompleteAllScope, smoothChildTiming:this.smoothChildTiming}),
13088 if (typeof(targets) === "string") {
13089 targets = TweenLite.selector(targets) || targets;
13091 if (_isSelector(targets)) { //senses if the targets object is a selector. If it is, we should translate it into an array.
13092 targets = _slice.call(targets, 0);
13094 stagger = stagger || 0;
13095 for (i = 0; i < targets.length; i++) {
13096 if (vars.startAt) {
13097 vars.startAt = _copy(vars.startAt);
13099 tl.to(targets[i], duration, _copy(vars), i * stagger);
13101 return this.add(tl, position);
13104 p.staggerFrom = function(targets, duration, vars, stagger, position, onCompleteAll, onCompleteAllParams, onCompleteAllScope) {
13105 vars.immediateRender = (vars.immediateRender != false);
13106 vars.runBackwards = true;
13107 return this.staggerTo(targets, duration, vars, stagger, position, onCompleteAll, onCompleteAllParams, onCompleteAllScope);
13110 p.staggerFromTo = function(targets, duration, fromVars, toVars, stagger, position, onCompleteAll, onCompleteAllParams, onCompleteAllScope) {
13111 toVars.startAt = fromVars;
13112 toVars.immediateRender = (toVars.immediateRender != false && fromVars.immediateRender != false);
13113 return this.staggerTo(targets, duration, toVars, stagger, position, onCompleteAll, onCompleteAllParams, onCompleteAllScope);
13116 p.call = function(callback, params, scope, position) {
13117 return this.add( TweenLite.delayedCall(0, callback, params, scope), position);
13120 p.set = function(target, vars, position) {
13121 position = this._parseTimeOrLabel(position, 0, true);
13122 if (vars.immediateRender == null) {
13123 vars.immediateRender = (position === this._time && !this._paused);
13125 return this.add( new TweenLite(target, 0, vars), position);
13128 TimelineLite.exportRoot = function(vars, ignoreDelayedCalls) {
13130 if (vars.smoothChildTiming == null) {
13131 vars.smoothChildTiming = true;
13133 var tl = new TimelineLite(vars),
13134 root = tl._timeline,
13136 if (ignoreDelayedCalls == null) {
13137 ignoreDelayedCalls = true;
13139 root._remove(tl, true);
13141 tl._rawPrevTime = tl._time = tl._totalTime = root._time;
13142 tween = root._first;
13144 next = tween._next;
13145 if (!ignoreDelayedCalls || !(tween instanceof TweenLite && tween.target === tween.vars.onComplete)) {
13146 tl.add(tween, tween._startTime - tween._delay);
13154 p.add = function(value, position, align, stagger) {
13155 var curTime, l, i, child, tl, beforeRawTime;
13156 if (typeof(position) !== "number") {
13157 position = this._parseTimeOrLabel(position, 0, true, value);
13159 if (!(value instanceof Animation)) {
13160 if ((value instanceof Array) || (value && value.push && _isArray(value))) {
13161 align = align || "normal";
13162 stagger = stagger || 0;
13163 curTime = position;
13165 for (i = 0; i < l; i++) {
13166 if (_isArray(child = value[i])) {
13167 child = new TimelineLite({tweens:child});
13169 this.add(child, curTime);
13170 if (typeof(child) !== "string" && typeof(child) !== "function") {
13171 if (align === "sequence") {
13172 curTime = child._startTime + (child.totalDuration() / child._timeScale);
13173 } else if (align === "start") {
13174 child._startTime -= child.delay();
13177 curTime += stagger;
13179 return this._uncache(true);
13180 } else if (typeof(value) === "string") {
13181 return this.addLabel(value, position);
13182 } else if (typeof(value) === "function") {
13183 value = TweenLite.delayedCall(0, value);
13185 throw("Cannot add " + value + " into the timeline; it is not a tween, timeline, function, or string.");
13189 SimpleTimeline.prototype.add.call(this, value, position);
13191 //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.
13192 if (this._gc || this._time === this._duration) if (!this._paused) if (this._duration < this.duration()) {
13193 //in case any of the ancestors had completed but should now be enabled...
13195 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.
13196 while (tl._timeline) {
13197 if (beforeRawTime && tl._timeline.smoothChildTiming) {
13198 tl.totalTime(tl._totalTime, true); //moves the timeline (shifts its startTime) if necessary, and also enables it.
13199 } else if (tl._gc) {
13200 tl._enabled(true, false);
13209 p.remove = function(value) {
13210 if (value instanceof Animation) {
13211 return this._remove(value, false);
13212 } else if (value instanceof Array || (value && value.push && _isArray(value))) {
13213 var i = value.length;
13215 this.remove(value[i]);
13218 } else if (typeof(value) === "string") {
13219 return this.removeLabel(value);
13221 return this.kill(null, value);
13224 p._remove = function(tween, skipDisable) {
13225 SimpleTimeline.prototype._remove.call(this, tween, skipDisable);
13226 var last = this._last;
13228 this._time = this._totalTime = this._duration = this._totalDuration = 0;
13229 } else if (this._time > last._startTime + last._totalDuration / last._timeScale) {
13230 this._time = this.duration();
13231 this._totalTime = this._totalDuration;
13236 p.append = function(value, offsetOrLabel) {
13237 return this.add(value, this._parseTimeOrLabel(null, offsetOrLabel, true, value));
13240 p.insert = p.insertMultiple = function(value, position, align, stagger) {
13241 return this.add(value, position || 0, align, stagger);
13244 p.appendMultiple = function(tweens, offsetOrLabel, align, stagger) {
13245 return this.add(tweens, this._parseTimeOrLabel(null, offsetOrLabel, true, tweens), align, stagger);
13248 p.addLabel = function(label, position) {
13249 this._labels[label] = this._parseTimeOrLabel(position);
13253 p.addPause = function(position, callback, params, scope) {
13254 return this.call(_pauseCallback, ["{self}", callback, params, scope], this, position);
13257 p.removeLabel = function(label) {
13258 delete this._labels[label];
13262 p.getLabelTime = function(label) {
13263 return (this._labels[label] != null) ? this._labels[label] : -1;
13266 p._parseTimeOrLabel = function(timeOrLabel, offsetOrLabel, appendIfAbsent, ignore) {
13268 //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().
13269 if (ignore instanceof Animation && ignore.timeline === this) {
13270 this.remove(ignore);
13271 } else if (ignore && ((ignore instanceof Array) || (ignore.push && _isArray(ignore)))) {
13274 if (ignore[i] instanceof Animation && ignore[i].timeline === this) {
13275 this.remove(ignore[i]);
13279 if (typeof(offsetOrLabel) === "string") {
13280 return this._parseTimeOrLabel(offsetOrLabel, (appendIfAbsent && typeof(timeOrLabel) === "number" && this._labels[offsetOrLabel] == null) ? timeOrLabel - this.duration() : 0, appendIfAbsent);
13282 offsetOrLabel = offsetOrLabel || 0;
13283 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).
13284 i = timeOrLabel.indexOf("=");
13286 if (this._labels[timeOrLabel] == null) {
13287 return appendIfAbsent ? (this._labels[timeOrLabel] = this.duration() + offsetOrLabel) : offsetOrLabel;
13289 return this._labels[timeOrLabel] + offsetOrLabel;
13291 offsetOrLabel = parseInt(timeOrLabel.charAt(i-1) + "1", 10) * Number(timeOrLabel.substr(i+1));
13292 timeOrLabel = (i > 1) ? this._parseTimeOrLabel(timeOrLabel.substr(0, i-1), 0, appendIfAbsent) : this.duration();
13293 } else if (timeOrLabel == null) {
13294 timeOrLabel = this.duration();
13296 return Number(timeOrLabel) + offsetOrLabel;
13299 p.seek = function(position, suppressEvents) {
13300 return this.totalTime((typeof(position) === "number") ? position : this._parseTimeOrLabel(position), (suppressEvents !== false));
13303 p.stop = function() {
13304 return this.paused(true);
13307 p.gotoAndPlay = function(position, suppressEvents) {
13308 return this.play(position, suppressEvents);
13311 p.gotoAndStop = function(position, suppressEvents) {
13312 return this.pause(position, suppressEvents);
13315 p.render = function(time, suppressEvents, force) {
13317 this._enabled(true, false);
13319 var totalDur = (!this._dirty) ? this._totalDuration : this.totalDuration(),
13320 prevTime = this._time,
13321 prevStart = this._startTime,
13322 prevTimeScale = this._timeScale,
13323 prevPaused = this._paused,
13324 tween, isComplete, next, callback, internalForce;
13325 if (time >= totalDur) {
13326 this._totalTime = this._time = totalDur;
13327 if (!this._reversed) if (!this._hasPausedChild()) {
13329 callback = "onComplete";
13330 if (this._duration === 0) if (time === 0 || this._rawPrevTime < 0 || this._rawPrevTime === _tinyNum) if (this._rawPrevTime !== time && this._first) {
13331 internalForce = true;
13332 if (this._rawPrevTime > _tinyNum) {
13333 callback = "onReverseComplete";
13337 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.
13338 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.
13340 } else if (time < 0.0000001) { //to work around occasional floating point math artifacts, round super small values to 0.
13341 this._totalTime = this._time = 0;
13342 if (prevTime !== 0 || (this._duration === 0 && this._rawPrevTime !== _tinyNum && (this._rawPrevTime > 0 || (time < 0 && this._rawPrevTime >= 0)))) {
13343 callback = "onReverseComplete";
13344 isComplete = this._reversed;
13347 this._active = false;
13348 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.
13349 internalForce = true;
13351 this._rawPrevTime = time;
13353 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.
13355 time = 0; //to avoid occasional floating point rounding errors (could cause problems especially with zero-duration tweens at the very beginning of the timeline)
13356 if (!this._initted) {
13357 internalForce = true;
13362 this._totalTime = this._time = this._rawPrevTime = time;
13364 if ((this._time === prevTime || !this._first) && !force && !internalForce) {
13366 } else if (!this._initted) {
13367 this._initted = true;
13370 if (!this._active) if (!this._paused && this._time !== prevTime && time > 0) {
13371 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.
13374 if (prevTime === 0) if (this.vars.onStart) if (this._time !== 0) if (!suppressEvents) {
13375 this.vars.onStart.apply(this.vars.onStartScope || this, this.vars.onStartParams || _blankArray);
13378 if (this._time >= prevTime) {
13379 tween = this._first;
13381 next = tween._next; //record it here because the value could change after rendering...
13382 if (this._paused && !prevPaused) { //in case a tween pauses the timeline when rendering
13384 } else if (tween._active || (tween._startTime <= this._time && !tween._paused && !tween._gc)) {
13385 if (!tween._reversed) {
13386 tween.render((time - tween._startTime) * tween._timeScale, suppressEvents, force);
13388 tween.render(((!tween._dirty) ? tween._totalDuration : tween.totalDuration()) - ((time - tween._startTime) * tween._timeScale), suppressEvents, force);
13394 tween = this._last;
13396 next = tween._prev; //record it here because the value could change after rendering...
13397 if (this._paused && !prevPaused) { //in case a tween pauses the timeline when rendering
13399 } else if (tween._active || (tween._startTime <= prevTime && !tween._paused && !tween._gc)) {
13400 if (!tween._reversed) {
13401 tween.render((time - tween._startTime) * tween._timeScale, suppressEvents, force);
13403 tween.render(((!tween._dirty) ? tween._totalDuration : tween.totalDuration()) - ((time - tween._startTime) * tween._timeScale), suppressEvents, force);
13410 if (this._onUpdate) if (!suppressEvents) {
13411 this._onUpdate.apply(this.vars.onUpdateScope || this, this.vars.onUpdateParams || _blankArray);
13414 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
13416 if (this._timeline.autoRemoveChildren) {
13417 this._enabled(false, false);
13419 this._active = false;
13421 if (!suppressEvents && this.vars[callback]) {
13422 this.vars[callback].apply(this.vars[callback + "Scope"] || this, this.vars[callback + "Params"] || _blankArray);
13427 p._hasPausedChild = function() {
13428 var tween = this._first;
13430 if (tween._paused || ((tween instanceof TimelineLite) && tween._hasPausedChild())) {
13433 tween = tween._next;
13438 p.getChildren = function(nested, tweens, timelines, ignoreBeforeTime) {
13439 ignoreBeforeTime = ignoreBeforeTime || -9999999999;
13441 tween = this._first,
13444 if (tween._startTime < ignoreBeforeTime) {
13446 } else if (tween instanceof TweenLite) {
13447 if (tweens !== false) {
13451 if (timelines !== false) {
13454 if (nested !== false) {
13455 a = a.concat(tween.getChildren(true, tweens, timelines));
13459 tween = tween._next;
13464 p.getTweensOf = function(target, nested) {
13465 var disabled = this._gc,
13470 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.
13472 tweens = TweenLite.getTweensOf(target);
13475 if (tweens[i].timeline === this || (nested && this._contains(tweens[i]))) {
13476 a[cnt++] = tweens[i];
13480 this._enabled(false, true);
13485 p._contains = function(tween) {
13486 var tl = tween.timeline;
13496 p.shiftChildren = function(amount, adjustLabels, ignoreBeforeTime) {
13497 ignoreBeforeTime = ignoreBeforeTime || 0;
13498 var tween = this._first,
13499 labels = this._labels,
13502 if (tween._startTime >= ignoreBeforeTime) {
13503 tween._startTime += amount;
13505 tween = tween._next;
13507 if (adjustLabels) {
13508 for (p in labels) {
13509 if (labels[p] >= ignoreBeforeTime) {
13510 labels[p] += amount;
13514 return this._uncache(true);
13517 p._kill = function(vars, target) {
13518 if (!vars && !target) {
13519 return this._enabled(false, false);
13521 var tweens = (!target) ? this.getChildren(true, true, false) : this.getTweensOf(target),
13525 if (tweens[i]._kill(vars, target)) {
13532 p.clear = function(labels) {
13533 var tweens = this.getChildren(false, true, true),
13535 this._time = this._totalTime = 0;
13537 tweens[i]._enabled(false, false);
13539 if (labels !== false) {
13542 return this._uncache(true);
13545 p.invalidate = function() {
13546 var tween = this._first;
13548 tween.invalidate();
13549 tween = tween._next;
13554 p._enabled = function(enabled, ignoreTimeline) {
13555 if (enabled === this._gc) {
13556 var tween = this._first;
13558 tween._enabled(enabled, true);
13559 tween = tween._next;
13562 return SimpleTimeline.prototype._enabled.call(this, enabled, ignoreTimeline);
13565 p.duration = function(value) {
13566 if (!arguments.length) {
13568 this.totalDuration(); //just triggers recalculation
13570 return this._duration;
13572 if (this.duration() !== 0 && value !== 0) {
13573 this.timeScale(this._duration / value);
13578 p.totalDuration = function(value) {
13579 if (!arguments.length) {
13582 tween = this._last,
13583 prevStart = 999999999999,
13586 prev = tween._prev; //record it here in case the tween changes position in the sequence...
13587 if (tween._dirty) {
13588 tween.totalDuration(); //could change the tween._startTime, so make sure the tween's cache is clean before analyzing it.
13590 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
13591 this.add(tween, tween._startTime - tween._delay);
13593 prevStart = tween._startTime;
13595 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.
13596 max -= tween._startTime;
13597 if (this._timeline.smoothChildTiming) {
13598 this._startTime += tween._startTime / this._timeScale;
13600 this.shiftChildren(-tween._startTime, false, -9999999999);
13603 end = tween._startTime + (tween._totalDuration / tween._timeScale);
13609 this._duration = this._totalDuration = max;
13610 this._dirty = false;
13612 return this._totalDuration;
13614 if (this.totalDuration() !== 0) if (value !== 0) {
13615 this.timeScale(this._totalDuration / value);
13620 p.usesFrames = function() {
13621 var tl = this._timeline;
13622 while (tl._timeline) {
13625 return (tl === Animation._rootFramesTimeline);
13628 p.rawTime = function() {
13629 return this._paused ? this._totalTime : (this._timeline.rawTime() - this._startTime) * this._timeScale;
13632 return TimelineLite;
13649 * ----------------------------------------------------------------
13651 * ----------------------------------------------------------------
13653 window._gsDefine("TimelineMax", ["TimelineLite","TweenLite","easing.Ease"], function(TimelineLite, TweenLite, Ease) {
13655 var TimelineMax = function(vars) {
13656 TimelineLite.call(this, vars);
13657 this._repeat = this.vars.repeat || 0;
13658 this._repeatDelay = this.vars.repeatDelay || 0;
13660 this._yoyo = (this.vars.yoyo === true);
13661 this._dirty = true;
13663 _tinyNum = 0.0000000001,
13665 _easeNone = new Ease(null, null, 1, 0),
13666 p = TimelineMax.prototype = new TimelineLite();
13668 p.constructor = TimelineMax;
13669 p.kill()._gc = false;
13670 TimelineMax.version = "1.12.1";
13672 p.invalidate = function() {
13673 this._yoyo = (this.vars.yoyo === true);
13674 this._repeat = this.vars.repeat || 0;
13675 this._repeatDelay = this.vars.repeatDelay || 0;
13676 this._uncache(true);
13677 return TimelineLite.prototype.invalidate.call(this);
13680 p.addCallback = function(callback, position, params, scope) {
13681 return this.add( TweenLite.delayedCall(0, callback, params, scope), position);
13684 p.removeCallback = function(callback, position) {
13686 if (position == null) {
13687 this._kill(null, callback);
13689 var a = this.getTweensOf(callback, false),
13691 time = this._parseTimeOrLabel(position);
13693 if (a[i]._startTime === time) {
13694 a[i]._enabled(false, false);
13702 p.tweenTo = function(position, vars) {
13704 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.
13709 copy.time = this._parseTimeOrLabel(position);
13710 duration = (Math.abs(Number(copy.time) - this._time) / this._timeScale) || 0.001;
13711 t = new TweenLite(this, duration, copy);
13712 copy.onStart = function() {
13713 t.target.paused(true);
13714 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.
13715 t.duration( Math.abs( t.vars.time - t.target.time()) / t.target._timeScale );
13717 if (vars.onStart) { //in case the user had an onStart in the vars - we don't want to overwrite it.
13718 vars.onStart.apply(vars.onStartScope || t, vars.onStartParams || _blankArray);
13724 p.tweenFromTo = function(fromPosition, toPosition, vars) {
13726 fromPosition = this._parseTimeOrLabel(fromPosition);
13727 vars.startAt = {onComplete:this.seek, onCompleteParams:[fromPosition], onCompleteScope:this};
13728 vars.immediateRender = (vars.immediateRender !== false);
13729 var t = this.tweenTo(toPosition, vars);
13730 return t.duration((Math.abs( t.vars.time - fromPosition) / this._timeScale) || 0.001);
13733 p.render = function(time, suppressEvents, force) {
13735 this._enabled(true, false);
13737 var totalDur = (!this._dirty) ? this._totalDuration : this.totalDuration(),
13738 dur = this._duration,
13739 prevTime = this._time,
13740 prevTotalTime = this._totalTime,
13741 prevStart = this._startTime,
13742 prevTimeScale = this._timeScale,
13743 prevRawPrevTime = this._rawPrevTime,
13744 prevPaused = this._paused,
13745 prevCycle = this._cycle,
13746 tween, isComplete, next, callback, internalForce, cycleDuration;
13747 if (time >= totalDur) {
13748 if (!this._locked) {
13749 this._totalTime = totalDur;
13750 this._cycle = this._repeat;
13752 if (!this._reversed) if (!this._hasPausedChild()) {
13754 callback = "onComplete";
13755 if (this._duration === 0) if (time === 0 || prevRawPrevTime < 0 || prevRawPrevTime === _tinyNum) if (prevRawPrevTime !== time && this._first) {
13756 internalForce = true;
13757 if (prevRawPrevTime > _tinyNum) {
13758 callback = "onReverseComplete";
13762 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.
13763 if (this._yoyo && (this._cycle & 1) !== 0) {
13764 this._time = time = 0;
13767 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.
13770 } else if (time < 0.0000001) { //to work around occasional floating point math artifacts, round super small values to 0.
13771 if (!this._locked) {
13772 this._totalTime = this._cycle = 0;
13775 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)
13776 callback = "onReverseComplete";
13777 isComplete = this._reversed;
13780 this._active = false;
13781 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.
13782 internalForce = true;
13784 this._rawPrevTime = time;
13786 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.
13787 time = 0; //to avoid occasional floating point rounding errors (could cause problems especially with zero-duration tweens at the very beginning of the timeline)
13788 if (!this._initted) {
13789 internalForce = true;
13794 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.
13795 internalForce = true;
13797 this._time = this._rawPrevTime = time;
13798 if (!this._locked) {
13799 this._totalTime = time;
13800 if (this._repeat !== 0) {
13801 cycleDuration = dur + this._repeatDelay;
13802 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!)
13803 if (this._cycle !== 0) if (this._cycle === this._totalTime / cycleDuration) {
13804 this._cycle--; //otherwise when rendered exactly at the end time, it will act as though it is repeating (at the beginning)
13806 this._time = this._totalTime - (this._cycle * cycleDuration);
13807 if (this._yoyo) if ((this._cycle & 1) !== 0) {
13808 this._time = dur - this._time;
13810 if (this._time > dur) {
13812 time = dur + 0.0001; //to avoid occasional floating point rounding error
13813 } else if (this._time < 0) {
13814 this._time = time = 0;
13822 if (this._cycle !== prevCycle) if (!this._locked) {
13824 make sure children at the end/beginning of the timeline are rendered properly. If, for example,
13825 a 3-second long timeline rendered at 2.9 seconds previously, and now renders at 3.2 seconds (which
13826 would get transated to 2.8 seconds if the timeline yoyos or 0.2 seconds if it just repeats), there
13827 could be a callback or a short tween that's at 2.95 or 3 seconds in which wouldn't render. So
13828 we need to push the timeline to the end (and/or beginning depending on its yoyo value). Also we must
13829 ensure that zero-duration tweens at the very beginning or end of the TimelineMax work.
13831 var backwards = (this._yoyo && (prevCycle & 1) !== 0),
13832 wrap = (backwards === (this._yoyo && (this._cycle & 1) !== 0)),
13833 recTotalTime = this._totalTime,
13834 recCycle = this._cycle,
13835 recRawPrevTime = this._rawPrevTime,
13836 recTime = this._time;
13838 this._totalTime = prevCycle * dur;
13839 if (this._cycle < prevCycle) {
13840 backwards = !backwards;
13842 this._totalTime += dur;
13844 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.
13846 this._rawPrevTime = (dur === 0) ? prevRawPrevTime - 0.0001 : prevRawPrevTime;
13847 this._cycle = prevCycle;
13848 this._locked = true; //prevents changes to totalTime and skips repeat/yoyo behavior when we recursively call render()
13849 prevTime = (backwards) ? 0 : dur;
13850 this.render(prevTime, suppressEvents, (dur === 0));
13851 if (!suppressEvents) if (!this._gc) {
13852 if (this.vars.onRepeat) {
13853 this.vars.onRepeat.apply(this.vars.onRepeatScope || this, this.vars.onRepeatParams || _blankArray);
13857 prevTime = (backwards) ? dur + 0.0001 : -0.0001;
13858 this.render(prevTime, true, false);
13860 this._locked = false;
13861 if (this._paused && !prevPaused) { //if the render() triggered callback that paused this timeline, we should abort (very rare, but possible)
13864 this._time = recTime;
13865 this._totalTime = recTotalTime;
13866 this._cycle = recCycle;
13867 this._rawPrevTime = recRawPrevTime;
13870 if ((this._time === prevTime || !this._first) && !force && !internalForce) {
13871 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.
13872 this._onUpdate.apply(this.vars.onUpdateScope || this, this.vars.onUpdateParams || _blankArray);
13875 } else if (!this._initted) {
13876 this._initted = true;
13879 if (!this._active) if (!this._paused && this._totalTime !== prevTotalTime && time > 0) {
13880 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.
13883 if (prevTotalTime === 0) if (this.vars.onStart) if (this._totalTime !== 0) if (!suppressEvents) {
13884 this.vars.onStart.apply(this.vars.onStartScope || this, this.vars.onStartParams || _blankArray);
13887 if (this._time >= prevTime) {
13888 tween = this._first;
13890 next = tween._next; //record it here because the value could change after rendering...
13891 if (this._paused && !prevPaused) { //in case a tween pauses the timeline when rendering
13893 } else if (tween._active || (tween._startTime <= this._time && !tween._paused && !tween._gc)) {
13894 if (!tween._reversed) {
13895 tween.render((time - tween._startTime) * tween._timeScale, suppressEvents, force);
13897 tween.render(((!tween._dirty) ? tween._totalDuration : tween.totalDuration()) - ((time - tween._startTime) * tween._timeScale), suppressEvents, force);
13904 tween = this._last;
13906 next = tween._prev; //record it here because the value could change after rendering...
13907 if (this._paused && !prevPaused) { //in case a tween pauses the timeline when rendering
13909 } else if (tween._active || (tween._startTime <= prevTime && !tween._paused && !tween._gc)) {
13910 if (!tween._reversed) {
13911 tween.render((time - tween._startTime) * tween._timeScale, suppressEvents, force);
13913 tween.render(((!tween._dirty) ? tween._totalDuration : tween.totalDuration()) - ((time - tween._startTime) * tween._timeScale), suppressEvents, force);
13920 if (this._onUpdate) if (!suppressEvents) {
13921 this._onUpdate.apply(this.vars.onUpdateScope || this, this.vars.onUpdateParams || _blankArray);
13923 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
13925 if (this._timeline.autoRemoveChildren) {
13926 this._enabled(false, false);
13928 this._active = false;
13930 if (!suppressEvents && this.vars[callback]) {
13931 this.vars[callback].apply(this.vars[callback + "Scope"] || this, this.vars[callback + "Params"] || _blankArray);
13936 p.getActive = function(nested, tweens, timelines) {
13937 if (nested == null) {
13940 if (tweens == null) {
13943 if (timelines == null) {
13947 all = this.getChildren(nested, tweens, timelines),
13951 for (i = 0; i < l; i++) {
13953 if (tween.isActive()) {
13961 p.getLabelAfter = function(time) {
13962 if (!time) if (time !== 0) { //faster than isNan()
13965 var labels = this.getLabelsArray(),
13968 for (i = 0; i < l; i++) {
13969 if (labels[i].time > time) {
13970 return labels[i].name;
13976 p.getLabelBefore = function(time) {
13977 if (time == null) {
13980 var labels = this.getLabelsArray(),
13983 if (labels[i].time < time) {
13984 return labels[i].name;
13990 p.getLabelsArray = function() {
13994 for (p in this._labels) {
13995 a[cnt++] = {time:this._labels[p], name:p};
13997 a.sort(function(a,b) {
13998 return a.time - b.time;
14004 //---- GETTERS / SETTERS -------------------------------------------------------------------------------------------------------
14006 p.progress = function(value) {
14007 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);
14010 p.totalProgress = function(value) {
14011 return (!arguments.length) ? this._totalTime / this.totalDuration() : this.totalTime( this.totalDuration() * value, false);
14014 p.totalDuration = function(value) {
14015 if (!arguments.length) {
14017 TimelineLite.prototype.totalDuration.call(this); //just forces refresh
14018 //Instead of Infinity, we use 999999999999 so that we can accommodate reverses.
14019 this._totalDuration = (this._repeat === -1) ? 999999999999 : this._duration * (this._repeat + 1) + (this._repeatDelay * this._repeat);
14021 return this._totalDuration;
14023 return (this._repeat === -1) ? this : this.duration( (value - (this._repeat * this._repeatDelay)) / (this._repeat + 1) );
14026 p.time = function(value, suppressEvents) {
14027 if (!arguments.length) {
14031 this.totalDuration();
14033 if (value > this._duration) {
14034 value = this._duration;
14036 if (this._yoyo && (this._cycle & 1) !== 0) {
14037 value = (this._duration - value) + (this._cycle * (this._duration + this._repeatDelay));
14038 } else if (this._repeat !== 0) {
14039 value += this._cycle * (this._duration + this._repeatDelay);
14041 return this.totalTime(value, suppressEvents);
14044 p.repeat = function(value) {
14045 if (!arguments.length) {
14046 return this._repeat;
14048 this._repeat = value;
14049 return this._uncache(true);
14052 p.repeatDelay = function(value) {
14053 if (!arguments.length) {
14054 return this._repeatDelay;
14056 this._repeatDelay = value;
14057 return this._uncache(true);
14060 p.yoyo = function(value) {
14061 if (!arguments.length) {
14064 this._yoyo = value;
14068 p.currentLabel = function(value) {
14069 if (!arguments.length) {
14070 return this.getLabelBefore(this._time + 0.00000001);
14072 return this.seek(value, true);
14075 return TimelineMax;
14091 * ----------------------------------------------------------------
14093 * ----------------------------------------------------------------
14097 var _RAD2DEG = 180 / Math.PI,
14102 Segment = function(a, b, c, d) {
14111 _correlate = ",x,y,z,left,top,right,bottom,marginTop,marginLeft,marginRight,marginBottom,paddingLeft,paddingTop,paddingRight,paddingBottom,backgroundPosition,backgroundPosition_y,",
14112 cubicToQuadratic = function(a, b, c, d) {
14120 mabc = (mab + mbc) / 2,
14121 mbcd = (mbc + mcd) / 2,
14122 m8 = (mbcd - mabc) / 8;
14123 q1.b = mab + (a - mab) / 4;
14125 q1.c = q2.a = (q1.b + q2.b) / 2;
14126 q2.c = q3.a = (mabc + mbcd) / 2;
14128 q4.b = mcd + (d - mcd) / 4;
14129 q3.c = q4.a = (q3.b + q4.b) / 2;
14130 return [q1, q2, q3, q4];
14132 _calculateControlPoints = function(a, curviness, quad, basic, correlate) {
14133 var l = a.length - 1,
14136 i, p1, p2, p3, seg, m1, m2, mm, cp2, qb, r1, r2, tl;
14137 for (i = 0; i < l; i++) {
14146 tl = ((r2 + r1) * curviness * 0.25) / (basic ? 0.5 : _r3[i] || 0.5);
14147 m1 = p2 - (p2 - p1) * (basic ? curviness * 0.5 : (r1 !== 0 ? tl / r1 : 0));
14148 m2 = p2 + (p3 - p2) * (basic ? curviness * 0.5 : (r2 !== 0 ? tl / r2 : 0));
14149 mm = p2 - (m1 + (((m2 - m1) * ((r1 * 3 / (r1 + r2)) + 0.5) / 4) || 0));
14151 m1 = p2 - (p2 - p1) * curviness * 0.5;
14152 m2 = p2 + (p3 - p2) * curviness * 0.5;
14153 mm = p2 - (m1 + m2) / 2;
14162 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.
14170 qb = cubicToQuadratic(p1, cp1, cp2, p2);
14171 a.splice(ii, 1, qb[0], qb[1], qb[2], qb[3]);
14181 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.
14182 seg.da = seg.d - seg.a;
14183 seg.ca = seg.c - seg.a;
14184 seg.ba = cp1 - seg.a;
14186 qb = cubicToQuadratic(seg.a, cp1, seg.c, seg.d);
14187 a.splice(ii, 1, qb[0], qb[1], qb[2], qb[3]);
14190 _parseAnchors = function(values, p, correlate, prepend) {
14192 l, i, p1, p2, p3, tmp;
14194 values = [prepend].concat(values);
14197 if (typeof( (tmp = values[i][p]) ) === "string") if (tmp.charAt(1) === "=") {
14198 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
14202 l = values.length - 2;
14204 a[0] = new Segment(values[0][p], 0, 0, values[(l < -1) ? 0 : 1][p]);
14207 for (i = 0; i < l; i++) {
14209 p2 = values[i+1][p];
14210 a[i] = new Segment(p1, 0, 0, p2);
14212 p3 = values[i+2][p];
14213 _r1[i] = (_r1[i] || 0) + (p2 - p1) * (p2 - p1);
14214 _r2[i] = (_r2[i] || 0) + (p3 - p2) * (p3 - p2);
14217 a[i] = new Segment(values[i][p], 0, 0, values[i+1][p]);
14220 bezierThrough = function(values, curviness, quadratic, basic, correlate, prepend) {
14223 first = prepend || values[0],
14224 i, p, a, j, r, l, seamless, last;
14225 correlate = (typeof(correlate) === "string") ? ","+correlate+"," : _correlate;
14226 if (curviness == null) {
14229 for (p in values[0]) {
14232 //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)
14233 if (values.length > 1) {
14234 last = values[values.length - 1];
14239 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
14245 values = values.concat(); //duplicate the array to avoid contaminating the original which the user may be reusing for other tweens
14247 values.unshift(prepend);
14249 values.push(values[1]);
14250 prepend = values[values.length - 3];
14253 _r1.length = _r2.length = _r3.length = 0;
14257 _corProps[p] = (correlate.indexOf(","+p+",") !== -1);
14258 obj[p] = _parseAnchors(values, p, _corProps[p], prepend);
14262 _r1[i] = Math.sqrt(_r1[i]);
14263 _r2[i] = Math.sqrt(_r2[i]);
14268 if (_corProps[p]) {
14271 for (j = 0; j < l; j++) {
14272 r = a[j+1].da / _r2[j] + a[j].da / _r1[j];
14273 _r3[j] = (_r3[j] || 0) + r * r;
14279 _r3[i] = Math.sqrt(_r3[i]);
14283 j = quadratic ? 4 : 1;
14287 _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
14290 a.splice(a.length - j, j);
14295 _parseBezierData = function(values, type, prepend) {
14296 type = type || "soft";
14298 inc = (type === "cubic") ? 3 : 2,
14299 soft = (type === "soft"),
14301 a, b, c, d, cur, i, j, l, p, cnt, tmp;
14302 if (soft && prepend) {
14303 values = [prepend].concat(values);
14305 if (values == null || values.length < inc + 1) { throw "invalid Bezier data"; }
14306 for (p in values[0]) {
14315 for (j = 0; j < l; j++) {
14316 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);
14317 if (soft) if (j > 1) if (j < l - 1) {
14318 cur[cnt++] = (a + cur[cnt-2]) / 2;
14324 for (j = 0; j < l; j += inc) {
14328 d = (inc === 2) ? 0 : cur[j+3];
14329 cur[cnt++] = tmp = (inc === 3) ? new Segment(a, b, c, d) : new Segment(a, (2 * b + a) / 3, (2 * b + c) / 3, c);
14335 _addCubicLengths = function(a, steps, resolution) {
14336 var inc = 1 / resolution,
14338 d, d1, s, da, ca, ba, p, i, inv, bez, index;
14346 for (i = 1; i <= resolution; i++) {
14349 d = d1 - (d1 = (p * p * da + 3 * inv * (p * ca + inv * ba)) * p);
14350 index = j * resolution + i - 1;
14351 steps[index] = (steps[index] || 0) + d * d;
14355 _parseLengthData = function(obj, resolution) {
14356 resolution = resolution >> 0 || 6;
14361 threshold = resolution - 1,
14363 curLS = [], //current length segments array
14366 _addCubicLengths(obj[p], a, resolution);
14369 for (i = 0; i < l; i++) {
14370 d += Math.sqrt(a[i]);
14371 index = i % resolution;
14373 if (index === threshold) {
14375 index = (i / resolution) >> 0;
14376 segments[index] = curLS;
14377 lengths[index] = total;
14382 return {length:total, lengths:lengths, segments:segments};
14387 BezierPlugin = window._gsDefine.plugin({
14388 propName: "bezier",
14394 //gets called when the tween renders for the first time. This is where initial values should be recorded and any setup routines should run.
14395 init: function(target, vars, tween) {
14396 this._target = target;
14397 if (vars instanceof Array) {
14398 vars = {values:vars};
14403 this._timeRes = (vars.timeResolution == null) ? 6 : parseInt(vars.timeResolution, 10);
14404 var values = vars.values || [],
14406 second = values[0],
14407 autoRotate = vars.autoRotate || tween.vars.orientToBezier,
14408 p, isFunc, i, j, prepend;
14410 this._autoRotate = autoRotate ? (autoRotate instanceof Array) ? autoRotate : [["x","y","rotation",((autoRotate === true) ? 0 : Number(autoRotate) || 0)]] : null;
14411 for (p in second) {
14412 this._props.push(p);
14415 i = this._props.length;
14417 p = this._props[i];
14419 this._overwriteProps.push(p);
14420 isFunc = this._func[p] = (typeof(target[p]) === "function");
14421 first[p] = (!isFunc) ? parseFloat(target[p]) : target[ ((p.indexOf("set") || typeof(target["get" + p.substr(3)]) !== "function") ? p : "get" + p.substr(3)) ]();
14422 if (!prepend) if (first[p] !== values[0][p]) {
14426 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);
14427 this._segCount = this._beziers[p].length;
14429 if (this._timeRes) {
14430 var ld = _parseLengthData(this._beziers, this._timeRes);
14431 this._length = ld.length;
14432 this._lengths = ld.lengths;
14433 this._segments = ld.segments;
14434 this._l1 = this._li = this._s1 = this._si = 0;
14435 this._l2 = this._lengths[0];
14436 this._curSeg = this._segments[0];
14437 this._s2 = this._curSeg[0];
14438 this._prec = 1 / this._curSeg.length;
14441 if ((autoRotate = this._autoRotate)) {
14442 this._initialRotations = [];
14443 if (!(autoRotate[0] instanceof Array)) {
14444 this._autoRotate = autoRotate = [autoRotate];
14446 i = autoRotate.length;
14448 for (j = 0; j < 3; j++) {
14449 p = autoRotate[i][j];
14450 this._func[p] = (typeof(target[p]) === "function") ? target[ ((p.indexOf("set") || typeof(target["get" + p.substr(3)]) !== "function") ? p : "get" + p.substr(3)) ] : false;
14452 p = autoRotate[i][2];
14453 this._initialRotations[i] = this._func[p] ? this._func[p].call(this._target) : this._target[p];
14456 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.
14460 //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.)
14462 var segments = this._segCount,
14464 target = this._target,
14465 notStart = (v !== this._startRatio),
14466 curIndex, inv, i, p, b, t, val, l, lengths, curSeg;
14467 if (!this._timeRes) {
14468 curIndex = (v < 0) ? 0 : (v >= 1) ? segments - 1 : (segments * v) >> 0;
14469 t = (v - (curIndex * (1 / segments))) * segments;
14471 lengths = this._lengths;
14472 curSeg = this._curSeg;
14475 //find the appropriate segment (if the currently cached one isn't correct)
14476 if (v > this._l2 && i < segments - 1) {
14478 while (i < l && (this._l2 = lengths[++i]) <= v) { }
14479 this._l1 = lengths[i-1];
14481 this._curSeg = curSeg = this._segments[i];
14482 this._s2 = curSeg[(this._s1 = this._si = 0)];
14483 } else if (v < this._l1 && i > 0) {
14484 while (i > 0 && (this._l1 = lengths[--i]) >= v) { }
14485 if (i === 0 && v < this._l1) {
14490 this._l2 = lengths[i];
14492 this._curSeg = curSeg = this._segments[i];
14493 this._s1 = curSeg[(this._si = curSeg.length - 1) - 1] || 0;
14494 this._s2 = curSeg[this._si];
14497 //now find the appropriate sub-segment (we split it into the number of pieces that was defined by "precision" and measured each one)
14500 if (v > this._s2 && i < curSeg.length - 1) {
14501 l = curSeg.length - 1;
14502 while (i < l && (this._s2 = curSeg[++i]) <= v) { }
14503 this._s1 = curSeg[i-1];
14505 } else if (v < this._s1 && i > 0) {
14506 while (i > 0 && (this._s1 = curSeg[--i]) >= v) { }
14507 if (i === 0 && v < this._s1) {
14512 this._s2 = curSeg[i];
14515 t = (i + (v - this._s1) / (this._s2 - this._s1)) * this._prec;
14519 i = this._props.length;
14521 p = this._props[i];
14522 b = this._beziers[p][curIndex];
14523 val = (t * t * b.da + 3 * inv * (t * b.ca + inv * b.ba)) * t + b.a;
14524 if (this._round[p]) {
14525 val = Math.round(val);
14534 if (this._autoRotate) {
14535 var ar = this._autoRotate,
14536 b2, x1, y1, x2, y2, add, conv;
14540 add = ar[i][3] || 0;
14541 conv = (ar[i][4] === true) ? 1 : _RAD2DEG;
14542 b = this._beziers[ar[i][0]];
14543 b2 = this._beziers[ar[i][1]];
14545 if (b && b2) { //in case one of the properties got overwritten.
14549 x1 = b.a + (b.b - b.a) * t;
14550 x2 = b.b + (b.c - b.b) * t;
14551 x1 += (x2 - x1) * t;
14552 x2 += ((b.c + (b.d - b.c) * t) - x2) * t;
14554 y1 = b2.a + (b2.b - b2.a) * t;
14555 y2 = b2.b + (b2.c - b2.b) * t;
14556 y1 += (y2 - y1) * t;
14557 y2 += ((b2.c + (b2.d - b2.c) * t) - y2) * t;
14559 val = notStart ? Math.atan2(y2 - y1, x2 - x1) * conv + add : this._initialRotations[i];
14571 p = BezierPlugin.prototype;
14574 BezierPlugin.bezierThrough = bezierThrough;
14575 BezierPlugin.cubicToQuadratic = cubicToQuadratic;
14576 BezierPlugin._autoCSS = true; //indicates that this plugin can be inserted into the "css" object using the autoCSS feature of TweenLite
14577 BezierPlugin.quadraticToCubic = function(a, b, c) {
14578 return new Segment(a, (2 * b + a) / 3, (2 * b + c) / 3, c);
14581 BezierPlugin._cssRegister = function() {
14582 var CSSPlugin = window._gsDefine.globals.CSSPlugin;
14586 var _internals = CSSPlugin._internals,
14587 _parseToProxy = _internals._parseToProxy,
14588 _setPluginRatio = _internals._setPluginRatio,
14589 CSSPropTween = _internals.CSSPropTween;
14590 _internals._registerComplexSpecialProp("bezier", {parser:function(t, e, prop, cssp, pt, plugin) {
14591 if (e instanceof Array) {
14594 plugin = new BezierPlugin();
14595 var values = e.values,
14596 l = values.length - 1,
14603 for (i = 0; i <= l; i++) {
14604 data = _parseToProxy(t, values[i], cssp, pt, plugin, (l !== i));
14605 pluginValues[i] = data.end;
14608 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.
14610 v.values = pluginValues;
14611 pt = new CSSPropTween(t, "bezier", 0, 0, data.pt, 2);
14613 pt.plugin = plugin;
14614 pt.setRatio = _setPluginRatio;
14615 if (v.autoRotate === 0) {
14616 v.autoRotate = true;
14618 if (v.autoRotate && !(v.autoRotate instanceof Array)) {
14619 i = (v.autoRotate === true) ? 0 : Number(v.autoRotate);
14620 v.autoRotate = (data.end.left != null) ? [["left","top","rotation",i,false]] : (data.end.x != null) ? [["x","y","rotation",i,false]] : false;
14622 if (v.autoRotate) {
14623 if (!cssp._transform) {
14624 cssp._enableTransforms(false);
14626 data.autoRotate = cssp._target._gsTransform;
14628 plugin._onInitTween(data.proxy, v, cssp._tween);
14633 p._roundProps = function(lookup, value) {
14634 var op = this._overwriteProps,
14637 if (lookup[op[i]] || lookup.bezier || lookup.bezierThrough) {
14638 this._round[op[i]] = value;
14643 p._kill = function(lookup) {
14644 var a = this._props,
14646 for (p in this._beziers) {
14648 delete this._beziers[p];
14649 delete this._func[p];
14658 return this._super._kill.call(this, lookup);
14677 * ----------------------------------------------------------------
14679 * ----------------------------------------------------------------
14681 window._gsDefine("plugins.CSSPlugin", ["plugins.TweenPlugin","TweenLite"], function(TweenPlugin, TweenLite) {
14683 /** @constructor **/
14684 var CSSPlugin = function() {
14685 TweenPlugin.call(this, "css");
14686 this._overwriteProps.length = 0;
14687 this.setRatio = CSSPlugin.prototype.setRatio; //speed optimization (avoid prototype lookup on this "hot" method)
14689 _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.
14690 _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
14691 _cs, //computed style (we store this in a shared variable to conserve memory and make minification tighter
14692 _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.
14693 _specialProps = {},
14694 p = CSSPlugin.prototype = new TweenPlugin("css");
14696 p.constructor = CSSPlugin;
14697 CSSPlugin.version = "1.12.1";
14699 CSSPlugin.defaultTransformPerspective = 0;
14700 CSSPlugin.defaultSkewType = "compensated";
14701 p = "px"; //we'll reuse the "p" variable to keep file size down
14702 CSSPlugin.suffixMap = {top:p, right:p, bottom:p, left:p, width:p, height:p, fontSize:p, padding:p, margin:p, perspective:p, lineHeight:""};
14705 var _numExp = /(?:\d|\-\d|\.\d|\-\.\d)+/g,
14706 _relNumExp = /(?:\d|\-\d|\.\d|\-\.\d|\+=\d|\-=\d|\+=.\d|\-=\.\d)+/g,
14707 _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)"
14708 _NaNExp = /[^\d\-\.]/g,
14709 _suffixExp = /(?:\d|\-|\+|=|#|\.)*/g,
14710 _opacityExp = /opacity *= *([^)]*)/i,
14711 _opacityValExp = /opacity:([^;]*)/i,
14712 _alphaFilterExp = /alpha\(opacity *=.+?\)/i,
14713 _rgbhslExp = /^(rgb|hsl)/,
14714 _capsExp = /([A-Z])/g,
14715 _camelExp = /-([a-z])/gi,
14716 _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)
14717 _camelFunc = function(s, g) { return g.toUpperCase(); },
14718 _horizExp = /(?:Left|Right|Width)/i,
14719 _ieGetMatrixExp = /(M11|M12|M21|M22)=[\d\-\.e]+/gi,
14720 _ieSetMatrixExp = /progid\:DXImageTransform\.Microsoft\.Matrix\(.+?\)/i,
14721 _commasOutsideParenExp = /,(?=[^\)]*(?:\(|$))/gi, //finds any commas that are not within parenthesis
14722 _DEG2RAD = Math.PI / 180,
14723 _RAD2DEG = 180 / Math.PI,
14726 _tempDiv = _doc.createElement("div"),
14727 _tempImg = _doc.createElement("img"),
14728 _internals = CSSPlugin._internals = {_specialProps:_specialProps}, //provides a hook to a few internal methods that we need to access from inside other plugins
14729 _agent = navigator.userAgent,
14731 _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).
14734 _isFirefox, //Firefox has a bug that causes 3D transformed elements to randomly disappear unless a repaint is forced after each update on each element.
14735 _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!)
14737 _supportsOpacity = (function() { //we set _isSafari, _ieVers, _isFirefox, and _supportsOpacity all in one function here to reduce file size slightly, especially in the minified version.
14738 var i = _agent.indexOf("Android"),
14739 d = _doc.createElement("div"), a;
14741 _isSafari = (_agent.indexOf("Safari") !== -1 && _agent.indexOf("Chrome") === -1 && (i === -1 || Number(_agent.substr(i+8, 1)) > 3));
14742 _isSafariLT6 = (_isSafari && (Number(_agent.substr(_agent.indexOf("Version/")+8, 1)) < 6));
14743 _isFirefox = (_agent.indexOf("Firefox") !== -1);
14745 if ((/MSIE ([0-9]{1,}[\.0-9]{0,})/).exec(_agent)) {
14746 _ieVers = parseFloat( RegExp.$1 );
14749 d.innerHTML = "<a title='' style='top:1px;opacity:.55;'>a</a>";
14750 a = d.getElementsByTagName("a")[0];
14751 return a ? /^0.55/.test(a.style.opacity) : false;
14753 _getIEOpacity = function(v) {
14754 return (_opacityExp.test( ((typeof(v) === "string") ? v : (v.currentStyle ? v.currentStyle.filter : v.style.filter) || "") ) ? ( parseFloat( RegExp.$1 ) / 100 ) : 1);
14756 _log = function(s) {//for logging messages, but in a way that won't throw errors in old versions of IE.
14757 if (window.console) {
14761 _prefixCSS = "", //the non-camelCase vendor prefix like "-o-", "-moz-", "-ms-", or "-webkit-"
14762 _prefix = "", //camelCase vendor prefix like "O", "ms", "Webkit", or "Moz".
14764 // @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)
14765 _checkPropPrefix = function(p, e) {
14769 if (s[p] !== undefined) {
14772 p = p.charAt(0).toUpperCase() + p.substr(1);
14773 a = ["O","Moz","ms","Ms","Webkit"];
14775 while (--i > -1 && s[a[i]+p] === undefined) { }
14777 _prefix = (i === 3) ? "ms" : a[i];
14778 _prefixCSS = "-" + _prefix.toLowerCase() + "-";
14779 return _prefix + p;
14784 _getComputedStyle = _doc.defaultView ? _doc.defaultView.getComputedStyle : function() {},
14787 * @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:
14788 * var currentLeft = CSSPlugin.getStyle( document.getElementById("myElement"), "left");
14790 * @param {!Object} t Target element whose style property you want to query
14791 * @param {!string} p Property name (like "left" or "top" or "marginTop", etc.)
14792 * @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.
14793 * @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.
14794 * @param {string=} dflt Default value that should be returned in the place of null, "none", "auto" or "auto auto".
14795 * @return {?string} The current property value
14797 _getStyle = CSSPlugin.getStyle = function(t, p, cs, calc, dflt) {
14799 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.
14800 return _getIEOpacity(t);
14802 if (!calc && t.style[p]) {
14804 } else if ((cs = cs || _getComputedStyle(t))) {
14805 rv = cs[p] || cs.getPropertyValue(p) || cs.getPropertyValue(p.replace(_capsExp, "-$1").toLowerCase());
14806 } else if (t.currentStyle) {
14807 rv = t.currentStyle[p];
14809 return (dflt != null && (!rv || rv === "none" || rv === "auto" || rv === "auto auto")) ? dflt : rv;
14813 * @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.
14814 * @param {!Object} t Target element
14815 * @param {!string} p Property name (like "left", "top", "marginLeft", etc.)
14816 * @param {!number} v Value
14817 * @param {string=} sfx Suffix (like "px" or "%" or "em")
14818 * @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.
14819 * @return {number} value in pixels
14821 _convertToPixels = _internals.convertToPixels = function(t, p, v, sfx, recurse) {
14822 if (sfx === "px" || !sfx) { return v; }
14823 if (sfx === "auto" || !v) { return 0; }
14824 var horiz = _horizExp.test(p),
14826 style = _tempDiv.style,
14832 if (sfx === "%" && p.indexOf("border") !== -1) {
14833 pix = (v / 100) * (horiz ? t.clientWidth : t.clientHeight);
14835 style.cssText = "border:0 solid red;position:" + _getStyle(t, "position") + ";line-height:0;";
14836 if (sfx === "%" || !node.appendChild) {
14837 node = t.parentNode || _doc.body;
14838 cache = node._gsCache;
14839 time = TweenLite.ticker.frame;
14840 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)
14841 return cache.width * v / 100;
14843 style[(horiz ? "width" : "height")] = v + sfx;
14845 style[(horiz ? "borderLeftWidth" : "borderTopWidth")] = v + sfx;
14847 node.appendChild(_tempDiv);
14848 pix = parseFloat(_tempDiv[(horiz ? "offsetWidth" : "offsetHeight")]);
14849 node.removeChild(_tempDiv);
14850 if (horiz && sfx === "%" && CSSPlugin.cacheWidths !== false) {
14851 cache = node._gsCache = node._gsCache || {};
14853 cache.width = pix / v * 100;
14855 if (pix === 0 && !recurse) {
14856 pix = _convertToPixels(t, p, v, sfx, true);
14859 return neg ? -pix : pix;
14861 _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
14862 if (_getStyle(t, "position", cs) !== "absolute") { return 0; }
14863 var dim = ((p === "left") ? "Left" : "Top"),
14864 v = _getStyle(t, "margin" + dim, cs);
14865 return t["offset" + dim] - (_convertToPixels(t, p, parseFloat(v), v.replace(_suffixExp, "")) || 0);
14868 // @private returns at object containing ALL of the style properties in camelCase and their associated values.
14869 _getAllStyles = function(t, cs) {
14872 if ((cs = cs || _getComputedStyle(t, null))) {
14873 if ((i = cs.length)) {
14875 s[cs[i].replace(_camelExp, _camelFunc)] = cs.getPropertyValue(cs[i]);
14877 } else { //Opera behaves differently - cs.length is always 0, so we must do a for...in loop.
14882 } else if ((cs = t.currentStyle || t.style)) {
14884 if (typeof(i) === "string" && s[i] === undefined) {
14885 s[i.replace(_camelExp, _camelFunc)] = cs[i];
14889 if (!_supportsOpacity) {
14890 s.opacity = _getIEOpacity(t);
14892 tr = _getTransform(t, cs, false);
14893 s.rotation = tr.rotation;
14894 s.skewX = tr.skewX;
14895 s.scaleX = tr.scaleX;
14896 s.scaleY = tr.scaleY;
14901 s.rotationX = tr.rotationX;
14902 s.rotationY = tr.rotationY;
14903 s.scaleZ = tr.scaleZ;
14911 // @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.
14912 _cssDif = function(t, s1, s2, vars, forceLookup) {
14917 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") {
14918 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.
14919 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.
14920 mpt = new MiniPropTween(style, p, style[p], mpt);
14925 for (p in vars) { //copy properties (except className)
14926 if (p !== "className") {
14931 return {difs:difs, firstMPT:mpt};
14933 _dimensions = {width:["Left","Right"], height:["Top","Bottom"]},
14934 _margins = ["marginLeft","marginRight","marginTop","marginBottom"],
14937 * @private Gets the width or height of an element
14938 * @param {!Object} t Target element
14939 * @param {!string} p Property name ("width" or "height")
14940 * @param {Object=} cs Computed style object (if one exists). Just a speed optimization.
14941 * @return {number} Dimension (in pixels)
14943 _getDimension = function(t, p, cs) {
14944 var v = parseFloat((p === "width") ? t.offsetWidth : t.offsetHeight),
14945 a = _dimensions[p],
14947 cs = cs || _getComputedStyle(t, null);
14949 v -= parseFloat( _getStyle(t, "padding" + a[i], cs, true) ) || 0;
14950 v -= parseFloat( _getStyle(t, "border" + a[i] + "Width", cs, true) ) || 0;
14955 // @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)
14956 _parsePosition = function(v, recObj) {
14957 if (v == null || v === "" || v === "auto" || v === "auto auto") { //note: Firefox uses "auto auto" as default whereas Chrome uses "auto".
14960 var a = v.split(" "),
14961 x = (v.indexOf("left") !== -1) ? "0%" : (v.indexOf("right") !== -1) ? "100%" : a[0],
14962 y = (v.indexOf("top") !== -1) ? "0%" : (v.indexOf("bottom") !== -1) ? "100%" : a[1];
14965 } else if (y === "center") {
14968 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.
14972 recObj.oxp = (x.indexOf("%") !== -1);
14973 recObj.oyp = (y.indexOf("%") !== -1);
14974 recObj.oxr = (x.charAt(1) === "=");
14975 recObj.oyr = (y.charAt(1) === "=");
14976 recObj.ox = parseFloat(x.replace(_NaNExp, ""));
14977 recObj.oy = parseFloat(y.replace(_NaNExp, ""));
14979 return x + " " + y + ((a.length > 2) ? " " + a[2] : "");
14983 * @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!)
14984 * @param {(number|string)} e End value which is typically a string, but could be a number
14985 * @param {(number|string)} b Beginning value which is typically a string but could be a number
14986 * @return {number} Amount of change between the beginning and ending values (relative values that have a "+=" or "-=" are recognized)
14988 _parseChange = function(e, b) {
14989 return (typeof(e) === "string" && e.charAt(1) === "=") ? parseInt(e.charAt(0) + "1", 10) * parseFloat(e.substr(2)) : parseFloat(e) - parseFloat(b);
14993 * @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.
14994 * @param {Object} v Value to be parsed
14995 * @param {!number} d Default value (which is also used for relative calculations if "+=" or "-=" is found in the first parameter)
14996 * @return {number} Parsed value
14998 _parseVal = function(v, d) {
14999 return (v == null) ? d : (typeof(v) === "string" && v.charAt(1) === "=") ? parseInt(v.charAt(0) + "1", 10) * Number(v.substr(2)) + d : parseFloat(v);
15003 * @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.
15004 * @param {Object} v Value to be parsed
15005 * @param {!number} d Default value (which is also used for relative calculations if "+=" or "-=" is found in the first parameter)
15006 * @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"
15007 * @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.
15008 * @return {number} parsed angle in radians
15010 _parseAngle = function(v, d, p, directionalEnd) {
15011 var min = 0.000001,
15012 cap, split, dif, result;
15015 } else if (typeof(v) === "number") {
15019 split = v.split("_");
15020 dif = Number(split[0].replace(_NaNExp, "")) * ((v.indexOf("rad") === -1) ? 1 : _RAD2DEG) - ((v.charAt(1) === "=") ? 0 : d);
15021 if (split.length) {
15022 if (directionalEnd) {
15023 directionalEnd[p] = d + dif;
15025 if (v.indexOf("short") !== -1) {
15027 if (dif !== dif % (cap / 2)) {
15028 dif = (dif < 0) ? dif + cap : dif - cap;
15031 if (v.indexOf("_cw") !== -1 && dif < 0) {
15032 dif = ((dif + cap * 9999999999) % cap) - ((dif / cap) | 0) * cap;
15033 } else if (v.indexOf("ccw") !== -1 && dif > 0) {
15034 dif = ((dif - cap * 9999999999) % cap) - ((dif / cap) | 0) * cap;
15039 if (result < min && result > -min) {
15045 _colorLookup = {aqua:[0,255,255],
15047 silver:[192,192,192],
15053 white:[255,255,255],
15054 fuchsia:[255,0,255],
15056 yellow:[255,255,0],
15057 orange:[255,165,0],
15058 gray:[128,128,128],
15059 purple:[128,0,128],
15062 pink:[255,192,203],
15064 transparent:[255,255,255,0]},
15066 _hue = function(h, m1, m2) {
15067 h = (h < 0) ? h + 1 : (h > 1) ? h - 1 : h;
15068 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;
15072 * @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)
15073 * @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.
15074 * @return {Array.<number>} An array containing red, green, and blue (and optionally alpha) in that order.
15076 _parseColor = function(v) {
15077 var c1, c2, c3, h, s, l;
15078 if (!v || v === "") {
15079 return _colorLookup.black;
15081 if (typeof(v) === "number") {
15082 return [v >> 16, (v >> 8) & 255, v & 255];
15084 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.
15085 v = v.substr(0, v.length - 1);
15087 if (_colorLookup[v]) {
15088 return _colorLookup[v];
15090 if (v.charAt(0) === "#") {
15091 if (v.length === 4) { //for shorthand like #9F0
15095 v = "#" + c1 + c1 + c2 + c2 + c3 + c3;
15097 v = parseInt(v.substr(1), 16);
15098 return [v >> 16, (v >> 8) & 255, v & 255];
15100 if (v.substr(0, 3) === "hsl") {
15101 v = v.match(_numExp);
15102 h = (Number(v[0]) % 360) / 360;
15103 s = Number(v[1]) / 100;
15104 l = Number(v[2]) / 100;
15105 c2 = (l <= 0.5) ? l * (s + 1) : l + s - l * s;
15107 if (v.length > 3) {
15108 v[3] = Number(v[3]);
15110 v[0] = _hue(h + 1 / 3, c1, c2);
15111 v[1] = _hue(h, c1, c2);
15112 v[2] = _hue(h - 1 / 3, c1, c2);
15115 v = v.match(_numExp) || _colorLookup.transparent;
15116 v[0] = Number(v[0]);
15117 v[1] = Number(v[1]);
15118 v[2] = Number(v[2]);
15119 if (v.length > 3) {
15120 v[3] = Number(v[3]);
15124 _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.
15126 for (p in _colorLookup) {
15127 _colorExp += "|" + p + "\\b";
15129 _colorExp = new RegExp(_colorExp+")", "gi");
15132 * @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.
15133 * @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.
15134 * @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.
15135 * @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.
15136 * @return {Function} formatter function
15138 var _getFormatter = function(dflt, clr, collapsible, multi) {
15139 if (dflt == null) {
15140 return function(v) {return v;};
15142 var dColor = clr ? (dflt.match(_colorExp) || [""])[0] : "",
15143 dVals = dflt.split(dColor).join("").match(_valuesExp) || [],
15144 pfx = dflt.substr(0, dflt.indexOf(dVals[0])),
15145 sfx = (dflt.charAt(dflt.length - 1) === ")") ? ")" : "",
15146 delim = (dflt.indexOf(" ") !== -1) ? " " : ",",
15147 numVals = dVals.length,
15148 dSfx = (numVals > 0) ? dVals[0].replace(_numExp, "") : "",
15151 return function(v) {return v;};
15154 formatter = function(v) {
15155 var color, vals, i, a;
15156 if (typeof(v) === "number") {
15158 } else if (multi && _commasOutsideParenExp.test(v)) {
15159 a = v.replace(_commasOutsideParenExp, "|").split("|");
15160 for (i = 0; i < a.length; i++) {
15161 a[i] = formatter(a[i]);
15163 return a.join(",");
15165 color = (v.match(_colorExp) || [dColor])[0];
15166 vals = v.split(color).join("").match(_valuesExp) || [];
15168 if (numVals > i--) {
15169 while (++i < numVals) {
15170 vals[i] = collapsible ? vals[(((i - 1) / 2) | 0)] : dVals[i];
15173 return pfx + vals.join(delim) + delim + color + sfx + (v.indexOf("inset") !== -1 ? " inset" : "");
15178 formatter = function(v) {
15180 if (typeof(v) === "number") {
15182 } else if (multi && _commasOutsideParenExp.test(v)) {
15183 a = v.replace(_commasOutsideParenExp, "|").split("|");
15184 for (i = 0; i < a.length; i++) {
15185 a[i] = formatter(a[i]);
15187 return a.join(",");
15189 vals = v.match(_valuesExp) || [];
15191 if (numVals > i--) {
15192 while (++i < numVals) {
15193 vals[i] = collapsible ? vals[(((i - 1) / 2) | 0)] : dVals[i];
15196 return pfx + vals.join(delim) + sfx;
15202 * @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.
15203 * @param {!string} props a comma-delimited list of property names in order from top to left, like "marginTop,marginRight,marginBottom,marginLeft"
15204 * @return {Function} a formatter function
15206 _getEdgeParser = function(props) {
15207 props = props.split(",");
15208 return function(t, e, p, cssp, pt, plugin, vars) {
15209 var a = (e + "").split(" "),
15212 for (i = 0; i < 4; i++) {
15213 vars[props[i]] = a[i] = a[i] || a[(((i - 1) / 2) >> 0)];
15215 return cssp.parse(t, vars, pt, plugin);
15219 // @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.
15220 _setPluginRatio = _internals._setPluginRatio = function(v) {
15221 this.plugin.setRatio(v);
15228 val = proxy[mpt.v];
15230 val = Math.round(val);
15231 } else if (val < min && val > -min) {
15234 mpt.t[mpt.p] = val;
15237 if (d.autoRotate) {
15238 d.autoRotate.rotation = proxy.rotation;
15240 //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.
15246 pt.e = pt.s + pt.xs0;
15247 } else if (pt.type === 1) {
15248 str = pt.xs0 + pt.s + pt.xs1;
15249 for (i = 1; i < pt.l; i++) {
15250 str += pt["xn"+i] + pt["xs"+(i+1)];
15260 * @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.
15261 * @param {!Object} t target object whose property we're tweening (often a CSSPropTween)
15262 * @param {!string} p property name
15263 * @param {(number|string|object)} v value
15264 * @param {MiniPropTween=} next next MiniPropTween in the linked list
15265 * @param {boolean=} r if true, the tweened value should be rounded to the nearest integer
15267 MiniPropTween = function(t, p, v, next, r) {
15279 * @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.
15280 * This method returns an object that has the following properties:
15281 * - 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
15282 * - 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
15283 * - firstMPT: the first MiniPropTween in the linked list
15284 * - 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.
15285 * @param {!Object} t target object to be tweened
15286 * @param {!(Object|string)} vars the object containing the information about the tweening values (typically the end/destination values) that should be parsed
15287 * @param {!CSSPlugin} cssp The CSSPlugin instance
15288 * @param {CSSPropTween=} pt the next CSSPropTween in the linked list
15289 * @param {TweenPlugin=} plugin the external TweenPlugin instance that will be handling tweening the numeric values
15290 * @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.
15291 * @return An object containing the following properties: proxy, end, firstMPT, and pt (see above for descriptions)
15293 _parseToProxy = _internals._parseToProxy = function(t, vars, cssp, pt, plugin, shallow) {
15297 transform = cssp._transform,
15298 oldForce = _forcePT,
15299 i, p, xp, mpt, firstPT;
15300 cssp._transform = null;
15302 pt = firstPT = cssp.parse(t, vars, pt, plugin);
15303 _forcePT = oldForce;
15304 //break off from the linked list so the new ones are isolated.
15306 cssp._transform = transform;
15310 bpt._prev._next = null;
15314 while (pt && pt !== bpt) {
15315 if (pt.type <= 1) {
15317 end[p] = pt.s + pt.c;
15320 mpt = new MiniPropTween(pt, "s", p, mpt, pt.r);
15323 if (pt.type === 1) {
15327 p = pt.p + "_" + xp;
15328 end[p] = pt.data[xp];
15331 mpt = new MiniPropTween(pt, xp, p, mpt, pt.rxp[xp]);
15338 return {proxy:start, end:end, firstMPT:mpt, pt:firstPT};
15344 * @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.
15345 * CSSPropTweens have the following optional properties as well (not defined through the constructor):
15346 * - 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.
15347 * - 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)
15348 * - 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.
15349 * - 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.
15350 * - 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.
15351 * @param {!Object} t Target object whose property will be tweened. Often a DOM element, but not always. It could be anything.
15352 * @param {string} p Property to tween (name). For example, to tween element.width, p would be "width".
15353 * @param {number} s Starting numeric value
15354 * @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.
15355 * @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.
15356 * @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.
15357 * @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"
15358 * @param {boolean=} r If true, the value(s) should be rounded
15359 * @param {number=} pr Priority in the linked list order. Higher priority CSSPropTweens will be updated before lower priority ones. The default priority is 0.
15360 * @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.
15361 * @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.
15363 CSSPropTween = _internals.CSSPropTween = function(t, p, s, c, next, type, n, r, pr, b, e) {
15364 this.t = t; //target
15365 this.p = p; //property
15366 this.s = s; //starting value
15367 this.c = c; //change value
15368 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)
15369 if (!(t instanceof CSSPropTween)) {
15370 _overwriteProps.push(this.n);
15372 this.r = r; //round (boolean)
15373 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
15376 _hasPriority = true;
15378 this.b = (b === undefined) ? s : b;
15379 this.e = (e === undefined) ? s + c : e;
15387 * 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:
15388 * 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);
15389 * 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().
15390 * 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.
15392 * @param {!Object} t Target whose property will be tweened
15393 * @param {!string} p Property that will be tweened (its name, like "left" or "backgroundColor" or "boxShadow")
15394 * @param {string} b Beginning value
15395 * @param {string} e Ending value
15396 * @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)
15397 * @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
15398 * @param {?CSSPropTween} pt CSSPropTween instance that is the current head of the linked list (we'll prepend to this).
15399 * @param {number=} pr Priority in the linked list order. Higher priority properties will be updated before lower priority ones. The default priority is 0.
15400 * @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}
15401 * @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.
15402 * @return {CSSPropTween} The first CSSPropTween in the linked list which includes the new one(s) added by the parseComplex() call.
15404 _parseComplex = CSSPlugin.parseComplex = function(t, p, b, e, clrs, dflt, pt, pr, plugin, setRatio) {
15405 //DEBUG: _log("parseComplex: "+p+", b: "+b+", e: "+e);
15406 b = b || dflt || "";
15407 pt = new CSSPropTween(t, p, 0, 0, pt, (setRatio ? 2 : 1), null, false, pr, b, e);
15408 e += ""; //ensures it's a string
15409 var ba = b.split(", ").join(",").split(" "), //beginning array
15410 ea = e.split(", ").join(",").split(" "), //ending array
15412 autoRound = (_autoRound !== false),
15413 i, xi, ni, bv, ev, bnums, enums, bn, rgba, temp, cv, str;
15414 if (e.indexOf(",") !== -1 || b.indexOf(",") !== -1) {
15415 ba = ba.join(" ").replace(_commasOutsideParenExp, ", ").split(" ");
15416 ea = ea.join(" ").replace(_commasOutsideParenExp, ", ").split(" ");
15419 if (l !== ea.length) {
15420 //DEBUG: _log("mismatched formatting detected on " + p + " (" + b + " vs " + e + ")");
15421 ba = (dflt || "").split(" ");
15424 pt.plugin = plugin;
15425 pt.setRatio = setRatio;
15426 for (i = 0; i < l; i++) {
15429 bn = parseFloat(bv);
15431 //if the value begins with a number (most common). It's fine if it has a suffix like px
15432 if (bn || bn === 0) {
15433 pt.appendXtra("", bn, _parseChange(ev, bn), ev.replace(_relNumExp, ""), (autoRound && ev.indexOf("px") !== -1), true);
15435 //if the value is a color
15436 } else if (clrs && (bv.charAt(0) === "#" || _colorLookup[bv] || _rgbhslExp.test(bv))) {
15437 str = ev.charAt(ev.length - 1) === "," ? ")," : ")"; //if there's a comma at the end, retain it.
15438 bv = _parseColor(bv);
15439 ev = _parseColor(ev);
15440 rgba = (bv.length + ev.length > 6);
15441 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
15442 pt["xs" + pt.l] += pt.l ? " transparent" : "transparent";
15443 pt.e = pt.e.split(ea[i]).join("transparent");
15445 if (!_supportsOpacity) { //old versions of IE don't support rgba().
15448 pt.appendXtra((rgba ? "rgba(" : "rgb("), bv[0], ev[0] - bv[0], ",", true, true)
15449 .appendXtra("", bv[1], ev[1] - bv[1], ",", true)
15450 .appendXtra("", bv[2], ev[2] - bv[2], (rgba ? "," : str), true);
15452 bv = (bv.length < 4) ? 1 : bv[3];
15453 pt.appendXtra("", bv, ((ev.length < 4) ? 1 : ev[3]) - bv, str, false);
15458 bnums = bv.match(_numExp); //gets each group of numbers in the beginning value string and drops them into an array
15460 //if no number is found, treat it as a non-tweening value and just append the string to the current xs.
15462 pt["xs" + pt.l] += pt.l ? " " + bv : bv;
15464 //loop through all the numbers that are found and construct the extra values on the pt.
15466 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
15467 if (!enums || enums.length !== bnums.length) {
15468 //DEBUG: _log("mismatched formatting detected on " + p + " (" + b + " vs " + e + ")");
15472 for (xi = 0; xi < bnums.length; xi++) {
15474 temp = bv.indexOf(cv, ni);
15475 pt.appendXtra(bv.substr(ni, temp - ni), Number(cv), _parseChange(enums[xi], cv), "", (autoRound && bv.substr(temp + cv.length, 2) === "px"), (xi === 0));
15476 ni = temp + cv.length;
15478 pt["xs" + pt.l] += bv.substr(ni);
15482 //if there are relative values ("+=" or "-=" prefix), we need to adjust the ending value to eliminate the prefixes and combine the values properly.
15483 if (e.indexOf("=") !== -1) if (pt.data) {
15484 str = pt.xs0 + pt.data.s;
15485 for (i = 1; i < pt.l; i++) {
15486 str += pt["xs" + i] + pt.data["xn" + i];
15488 pt.e = str + pt["xs" + i];
15494 return pt.xfirst || pt;
15499 p = CSSPropTween.prototype;
15500 p.l = p.pr = 0; //length (number of extra properties like xn1, xn2, xn3, etc.
15506 p._next = p._prev = p.xfirst = p.data = p.plugin = p.setRatio = p.rxp = null;
15510 * 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:
15511 * xs0:"rect(", s:10, xs1:"px, ", xn1:5, xs2:"px, ", xn2:0, xs3:"px, ", xn3:20, xn4:"px)"
15512 * And they'd all get joined together when the CSSPlugin renders (in the setRatio() method).
15513 * @param {string=} pfx Prefix (if any)
15514 * @param {!number} s Starting value
15515 * @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.
15516 * @param {string=} sfx Suffix (if any)
15517 * @param {boolean=} r Round (if true).
15518 * @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.
15519 * @return {CSSPropTween} returns itself so that multiple methods can be chained together.
15521 p.appendXtra = function(pfx, s, c, sfx, r, pad) {
15524 pt["xs" + l] += (pad && l) ? " " + pfx : pfx || "";
15525 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!
15526 pt["xs" + l] += s + (sfx || "");
15530 pt.type = pt.setRatio ? 2 : 1;
15531 pt["xs" + pt.l] = sfx || "";
15533 pt.data["xn" + l] = s + c;
15534 pt.rxp["xn" + l] = r; //round extra property (we need to tap into this in the _parseToProxy() method)
15537 pt.xfirst = new CSSPropTween(pt, "xn" + l, s, c, pt.xfirst || pt, 0, pt.n, r, pt.pr);
15538 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.
15542 pt.data = {s:s + c};
15551 * @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.
15552 * @param {!string} p Property name (like "boxShadow" or "throwProps")
15553 * @param {Object=} options An object containing any of the following configuration options:
15554 * - defaultValue: the default value
15555 * - 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)
15556 * - 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.)
15557 * - prefix: if true, we'll determine whether or not this property requires a vendor prefix (like Webkit or Moz or ms or O)
15558 * - color: set this to true if the value for this SpecialProp may contain color-related values like rgb(), rgba(), etc.
15559 * - priority: priority in the linked list order. Higher priority SpecialProps will be updated before lower priority ones. The default priority is 0.
15560 * - multi: if true, the formatter should accommodate a comma-delimited list of values, like boxShadow could have multiple boxShadows listed out.
15561 * - 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.
15562 * - 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).
15564 var SpecialProp = function(p, options) {
15565 options = options || {};
15566 this.p = options.prefix ? _checkPropPrefix(p) || p : p;
15567 _specialProps[p] = _specialProps[this.p] = this;
15568 this.format = options.formatter || _getFormatter(options.defaultValue, options.color, options.collapsible, options.multi);
15569 if (options.parser) {
15570 this.parse = options.parser;
15572 this.clrs = options.color;
15573 this.multi = options.multi;
15574 this.keyword = options.keyword;
15575 this.dflt = options.defaultValue;
15576 this.pr = options.priority || 0;
15579 //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.
15580 _registerComplexSpecialProp = _internals._registerComplexSpecialProp = function(p, options, defaults) {
15581 if (typeof(options) !== "object") {
15582 options = {parser:defaults}; //to make backwards compatible with older versions of BezierPlugin and ThrowPropsPlugin
15584 var a = p.split(","),
15585 d = options.defaultValue,
15587 defaults = defaults || [d];
15588 for (i = 0; i < a.length; i++) {
15589 options.prefix = (i === 0 && options.prefix);
15590 options.defaultValue = defaults[i] || d;
15591 temp = new SpecialProp(a[i], options);
15595 //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.
15596 _registerPluginProp = function(p) {
15597 if (!_specialProps[p]) {
15598 var pluginName = p.charAt(0).toUpperCase() + p.substr(1) + "Plugin";
15599 _registerComplexSpecialProp(p, {parser:function(t, e, p, cssp, pt, plugin, vars) {
15600 var pluginClass = (window.GreenSockGlobals || window).com.greensock.plugins[pluginName];
15601 if (!pluginClass) {
15602 _log("Error: " + pluginName + " js file not loaded.");
15605 pluginClass._cssRegister();
15606 return _specialProps[p].parse(t, e, p, cssp, pt, plugin, vars);
15612 p = SpecialProp.prototype;
15615 * 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)
15616 * @param {!Object} t target element
15617 * @param {(string|number|object)} b beginning value
15618 * @param {(string|number|object)} e ending (destination) value
15619 * @param {CSSPropTween=} pt next CSSPropTween in the linked list
15620 * @param {TweenPlugin=} plugin If another plugin will be tweening the complex value, that TweenPlugin instance goes here.
15621 * @param {function=} setRatio If a custom setRatio() method should be used to handle this complex value, that goes here.
15622 * @return {CSSPropTween=} First CSSPropTween in the linked list
15624 p.parseComplex = function(t, b, e, pt, plugin, setRatio) {
15625 var kwd = this.keyword,
15626 i, ba, ea, l, bi, ei;
15627 //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)
15628 if (this.multi) if (_commasOutsideParenExp.test(e) || _commasOutsideParenExp.test(b)) {
15629 ba = b.replace(_commasOutsideParenExp, "|").split("|");
15630 ea = e.replace(_commasOutsideParenExp, "|").split("|");
15636 l = (ea.length > ba.length) ? ea.length : ba.length;
15637 for (i = 0; i < l; i++) {
15638 b = ba[i] = ba[i] || this.dflt;
15639 e = ea[i] = ea[i] || this.dflt;
15641 bi = b.indexOf(kwd);
15642 ei = e.indexOf(kwd);
15644 e = (ei === -1) ? ea : ba;
15652 return _parseComplex(t, this.p, b, e, this.clrs, this.dflt, pt, this.pr, plugin, setRatio);
15656 * 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:
15657 * this._firstPT = sp.parse(element, "5px 10px 20px rgb(2550,102,51)", "boxShadow", this);
15658 * 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).
15659 * @param {!Object} t Target object whose property is being tweened
15660 * @param {Object} e End value as provided in the vars object (typically a string, but not always - like a throwProps would be an object).
15661 * @param {!string} p Property name
15662 * @param {!CSSPlugin} cssp The CSSPlugin instance that should be associated with this tween.
15663 * @param {?CSSPropTween} pt The CSSPropTween that is the current head of the linked list (we'll prepend to it)
15664 * @param {TweenPlugin=} plugin If a plugin will be used to tween the parsed value, this is the plugin instance.
15665 * @param {Object=} vars Original vars object that contains the data for parsing.
15666 * @return {CSSPropTween} The first CSSPropTween in the linked list which includes the new one(s) added by the parse() call.
15668 p.parse = function(t, e, p, cssp, pt, plugin, vars) {
15669 return this.parseComplex(t.style, this.format(_getStyle(t, this.p, _cs, false, this.dflt)), this.format(e), pt, plugin);
15673 * 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:
15674 * 1) Target object whose property should be tweened (typically a DOM element)
15675 * 2) The end/destination value (could be a string, number, object, or whatever you want)
15676 * 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)
15678 * 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:
15680 * CSSPlugin.registerSpecialProp("myCustomProp", function(target, value, tween) {
15681 * var start = target.style.width;
15682 * return function(ratio) {
15683 * target.style.width = (start + value * ratio) + "px";
15684 * console.log("set width to " + target.style.width);
15688 * Then, when I do this tween, it will trigger my special property:
15690 * TweenLite.to(element, 1, {css:{myCustomProp:100}});
15692 * In the example, of course, we're just changing the width, but you can do anything you want.
15694 * @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}})
15695 * @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.
15696 * @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.
15698 CSSPlugin.registerSpecialProp = function(name, onInitTween, priority) {
15699 _registerComplexSpecialProp(name, {parser:function(t, e, p, cssp, pt, plugin, vars) {
15700 var rv = new CSSPropTween(t, p, 0, 0, pt, 2, p, false, priority);
15701 rv.plugin = plugin;
15702 rv.setRatio = onInitTween(t, e, cssp._tween, p);
15704 }, priority:priority});
15714 //transform-related methods and properties
15715 var _transformProps = ("scaleX,scaleY,scaleZ,x,y,z,skewX,skewY,rotation,rotationX,rotationY,perspective").split(","),
15716 _transformProp = _checkPropPrefix("transform"), //the Javascript (camelCase) transform property, like msTransform, WebkitTransform, MozTransform, or OTransform.
15717 _transformPropCSS = _prefixCSS + "transform",
15718 _transformOriginProp = _checkPropPrefix("transformOrigin"),
15719 _supports3D = (_checkPropPrefix("perspective") !== null),
15720 Transform = _internals.Transform = function() {
15725 * 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.
15726 * @param {!Object} t target element
15727 * @param {Object=} cs computed style object (optional)
15728 * @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...}
15729 * @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)
15730 * @return {object} object containing all of the transform properties/values like {x:0, y:0, z:0, scaleX:1...}
15732 _getTransform = _internals.getTransform = function(t, cs, rec, parse) {
15733 if (t._gsTransform && rec && !parse) {
15734 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.
15736 var tm = rec ? t._gsTransform || new Transform() : new Transform(),
15737 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.
15741 minPI = minAngle * _DEG2RAD,
15742 zOrigin = _supports3D ? parseFloat(_getStyle(t, _transformOriginProp, cs, false, "0 0 0").split(" ")[2]) || tm.zOrigin || 0 : 0,
15743 s, m, i, n, dec, scaleX, scaleY, rotation, skewX, difX, difY, difR, difS;
15744 if (_transformProp) {
15745 s = _getStyle(t, _transformPropCSS, cs, true);
15746 } else if (t.currentStyle) {
15747 //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.
15748 s = t.currentStyle.filter.match(_ieGetMatrixExp);
15749 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(",") : "";
15751 //split the matrix values out into an array (m for matrix)
15752 m = (s || "").match(/(?:\-|\b)[\d\-\.e]+\b/gi) || [];
15756 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).
15758 if (m.length === 16) {
15760 //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)
15761 var a13 = m[8], a23 = m[9], a33 = m[10],
15762 a14 = m[12], a24 = m[13], a34 = m[14];
15764 //we manually compensate for non-zero z component of transformOrigin to work around bugs in Safari
15767 a14 = a13*a34-m[12];
15768 a24 = a23*a34-m[13];
15769 a34 = a33*a34+tm.zOrigin-m[14];
15772 //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.
15773 if (!rec || parse || tm.rotationX == null) {
15774 var a11 = m[0], a21 = m[1], a31 = m[2], a41 = m[3],
15775 a12 = m[4], a22 = m[5], a32 = m[6], a42 = m[7],
15777 angle = Math.atan2(a32, a33),
15778 xFlip = (angle < -minPI || angle > minPI),
15779 t1, t2, t3, cos, sin, yFlip, zFlip;
15780 tm.rotationX = angle * _RAD2DEG;
15783 cos = Math.cos(-angle);
15784 sin = Math.sin(-angle);
15785 t1 = a12*cos+a13*sin;
15786 t2 = a22*cos+a23*sin;
15787 t3 = a32*cos+a33*sin;
15788 a13 = a12*-sin+a13*cos;
15789 a23 = a22*-sin+a23*cos;
15790 a33 = a32*-sin+a33*cos;
15791 a43 = a42*-sin+a43*cos;
15797 angle = Math.atan2(a13, a11);
15798 tm.rotationY = angle * _RAD2DEG;
15800 yFlip = (angle < -minPI || angle > minPI);
15801 cos = Math.cos(-angle);
15802 sin = Math.sin(-angle);
15803 t1 = a11*cos-a13*sin;
15804 t2 = a21*cos-a23*sin;
15805 t3 = a31*cos-a33*sin;
15806 a23 = a21*sin+a23*cos;
15807 a33 = a31*sin+a33*cos;
15808 a43 = a41*sin+a43*cos;
15814 angle = Math.atan2(a21, a22);
15815 tm.rotation = angle * _RAD2DEG;
15817 zFlip = (angle < -minPI || angle > minPI);
15818 cos = Math.cos(-angle);
15819 sin = Math.sin(-angle);
15820 a11 = a11*cos+a12*sin;
15821 t2 = a21*cos+a22*sin;
15822 a22 = a21*-sin+a22*cos;
15823 a32 = a31*-sin+a32*cos;
15827 if (zFlip && xFlip) {
15828 tm.rotation = tm.rotationX = 0;
15829 } else if (zFlip && yFlip) {
15830 tm.rotation = tm.rotationY = 0;
15831 } else if (yFlip && xFlip) {
15832 tm.rotationY = tm.rotationX = 0;
15835 tm.scaleX = ((Math.sqrt(a11 * a11 + a21 * a21) * rnd + 0.5) | 0) / rnd;
15836 tm.scaleY = ((Math.sqrt(a22 * a22 + a23 * a23) * rnd + 0.5) | 0) / rnd;
15837 tm.scaleZ = ((Math.sqrt(a32 * a32 + a33 * a33) * rnd + 0.5) | 0) / rnd;
15839 tm.perspective = a43 ? 1 / ((a43 < 0) ? -a43 : a43) : 0;
15845 } 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.
15846 var k = (m.length >= 6),
15853 scaleX = Math.sqrt(a * a + b * b);
15854 scaleY = Math.sqrt(d * d + c * c);
15855 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).
15856 skewX = (c || d) ? Math.atan2(c, d) * _RAD2DEG + rotation : tm.skewX || 0;
15857 difX = scaleX - Math.abs(tm.scaleX || 0);
15858 difY = scaleY - Math.abs(tm.scaleY || 0);
15859 if (Math.abs(skewX) > 90 && Math.abs(skewX) < 270) {
15862 skewX += (rotation <= 0) ? 180 : -180;
15863 rotation += (rotation <= 0) ? 180 : -180;
15866 skewX += (skewX <= 0) ? 180 : -180;
15869 difR = (rotation - tm.rotation) % 180; //note: matching ranges would be very small (+/-0.0001) or very close to 180.
15870 difS = (skewX - tm.skewX) % 180;
15871 //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.
15872 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)) {
15873 tm.scaleX = scaleX;
15874 tm.scaleY = scaleY;
15875 tm.rotation = rotation;
15879 tm.rotationX = tm.rotationY = tm.z = 0;
15880 tm.perspective = parseFloat(CSSPlugin.defaultTransformPerspective) || 0;
15884 tm.zOrigin = zOrigin;
15886 //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.
15888 if (tm[i] < min) if (tm[i] > -min) {
15892 //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);
15894 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)
15899 //for setting 2D transforms in IE6, IE7, and IE8 (must use a "filter" to emulate the behavior of modern day browser transforms)
15900 _setIETransformRatio = function(v) {
15901 var t = this.data, //refers to the element's _gsTransform object
15902 ang = -t.rotation * _DEG2RAD,
15903 skew = ang + t.skewX * _DEG2RAD,
15905 a = ((Math.cos(ang) * t.scaleX * rnd) | 0) / rnd,
15906 b = ((Math.sin(ang) * t.scaleX * rnd) | 0) / rnd,
15907 c = ((Math.sin(skew) * -t.scaleY * rnd) | 0) / rnd,
15908 d = ((Math.cos(skew) * t.scaleY * rnd) | 0) / rnd,
15909 style = this.t.style,
15910 cs = this.t.currentStyle,
15915 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)
15918 filters = cs.filter;
15919 style.filter = ""; //remove filters so that we can accurately measure offsetWidth/offsetHeight
15920 var w = this.t.offsetWidth,
15921 h = this.t.offsetHeight,
15922 clip = (cs.position !== "absolute"),
15923 m = "progid:DXImageTransform.Microsoft.Matrix(M11=" + a + ", M12=" + b + ", M21=" + c + ", M22=" + d,
15928 //if transformOrigin is being used, adjust the offset x and y
15929 if (t.ox != null) {
15930 dx = ((t.oxp) ? w * t.ox * 0.01 : t.ox) - w / 2;
15931 dy = ((t.oyp) ? h * t.oy * 0.01 : t.oy) - h / 2;
15932 ox += dx - (dx * a + dy * b);
15933 oy += dy - (dx * c + dy * d);
15937 m += ", sizingMethod='auto expand')";
15941 //translate to ensure that transformations occur around the correct origin (default is center).
15942 m += ", Dx=" + (dx - (dx * a + dy * b) + ox) + ", Dy=" + (dy - (dx * c + dy * d) + oy) + ")";
15944 if (filters.indexOf("DXImageTransform.Microsoft.Matrix(") !== -1) {
15945 style.filter = filters.replace(_ieSetMatrixExp, m);
15947 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.
15950 //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.
15951 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) {
15952 style.removeAttribute("filter");
15955 //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).
15957 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
15959 dx = t.ieOffsetX || 0;
15960 dy = t.ieOffsetY || 0;
15961 t.ieOffsetX = Math.round((w - ((a < 0 ? -a : a) * w + (b < 0 ? -b : b) * h)) / 2 + ox);
15962 t.ieOffsetY = Math.round((h - ((d < 0 ? -d : d) * h + (c < 0 ? -c : c) * w)) / 2 + oy);
15963 for (i = 0; i < 4; i++) {
15964 prop = _margins[i];
15966 //we need to get the current margin in case it is being tweened separately (we want to respect that tween's changes)
15967 val = (marg.indexOf("px") !== -1) ? parseFloat(marg) : _convertToPixels(this.t, prop, parseFloat(marg), marg.replace(_suffixExp, "")) || 0;
15968 if (val !== t[prop]) {
15969 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.
15971 dif = (i < 2) ? dx - t.ieOffsetX : dy - t.ieOffsetY;
15973 style[prop] = (t[prop] = Math.round( val - dif * ((i === 0 || i === 2) ? 1 : mult) )) + "px";
15978 _set3DTransformRatio = _internals.set3DTransformRatio = function(v) {
15979 var t = this.data, //refers to the element's _gsTransform object
15980 style = this.t.style,
15981 angle = t.rotation * _DEG2RAD,
15985 perspective = t.perspective,
15986 a11, a12, a13, a14, a21, a22, a23, a24, a31, a32, a33, a34, a41, a42, a43,
15987 zOrigin, rnd, cos, sin, t1, t2, t3, t4;
15988 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
15989 _set2DTransformRatio.call(this, v);
15994 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.
15997 if (sy < n && sy > -n) {
16000 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).
16004 if (angle || t.skewX) {
16005 cos = Math.cos(angle);
16006 sin = Math.sin(angle);
16010 angle -= t.skewX * _DEG2RAD;
16011 cos = Math.cos(angle);
16012 sin = Math.sin(angle);
16013 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
16014 t1 = Math.tan(t.skewX * _DEG2RAD);
16015 t1 = Math.sqrt(1 + t1 * t1);
16023 } else if (!t.rotationY && !t.rotationX && sz === 1 && !perspective) { //if we're only translating and/or 2D scaling, this is faster...
16024 style[_transformProp] = "translate3d(" + t.x + "px," + t.y + "px," + t.z +"px)" + ((sx !== 1 || sy !== 1) ? " scale(" + sx + "," + sy + ")" : "");
16031 a13 = a14 = a23 = a24 = a31 = a32 = a34 = a41 = a42 = 0;
16032 a43 = (perspective) ? -1 / perspective : 0;
16033 zOrigin = t.zOrigin;
16035 angle = t.rotationY * _DEG2RAD;
16037 cos = Math.cos(angle);
16038 sin = Math.sin(angle);
16048 angle = t.rotationX * _DEG2RAD;
16050 cos = Math.cos(angle);
16051 sin = Math.sin(angle);
16052 t1 = a12*cos+a13*sin;
16053 t2 = a22*cos+a23*sin;
16054 t3 = a32*cos+a33*sin;
16055 t4 = a42*cos+a43*sin;
16056 a13 = a12*-sin+a13*cos;
16057 a23 = a22*-sin+a23*cos;
16058 a33 = a32*-sin+a33*cos;
16059 a43 = a42*-sin+a43*cos;
16087 a34 = a33*a34+zOrigin;
16089 //we round the x, y, and z slightly differently to allow even larger values.
16090 a14 = (t1 = (a14 += t.x) - (a14 |= 0)) ? ((t1 * rnd + (t1 < 0 ? -0.5 : 0.5)) | 0) / rnd + a14 : a14;
16091 a24 = (t1 = (a24 += t.y) - (a24 |= 0)) ? ((t1 * rnd + (t1 < 0 ? -0.5 : 0.5)) | 0) / rnd + a24 : a24;
16092 a34 = (t1 = (a34 += t.z) - (a34 |= 0)) ? ((t1 * rnd + (t1 < 0 ? -0.5 : 0.5)) | 0) / rnd + a34 : a34;
16093 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(",") + ")";
16096 _set2DTransformRatio = _internals.set2DTransformRatio = function(v) {
16097 var t = this.data, //refers to the element's _gsTransform object
16099 style = targ.style,
16100 ang, skew, rnd, sx, sy;
16101 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.
16102 this.setRatio = _set3DTransformRatio;
16103 _set3DTransformRatio.call(this, v);
16106 if (!t.rotation && !t.skewX) {
16107 style[_transformProp] = "matrix(" + t.scaleX + ",0,0," + t.scaleY + "," + t.x + "," + t.y + ")";
16109 ang = t.rotation * _DEG2RAD;
16110 skew = ang - t.skewX * _DEG2RAD;
16112 sx = t.scaleX * rnd;
16113 sy = t.scaleY * rnd;
16114 //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.
16115 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 + ")";
16119 _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) {
16120 if (cssp._transform) { return pt; } //only need to parse the transform once, and only if the browser supports it.
16121 var m1 = cssp._transform = _getTransform(t, _cs, true, vars.parseTransform),
16124 i = _transformProps.length,
16127 m2, skewY, copy, orig, has3D, hasChange, dr;
16128 if (typeof(v.transform) === "string" && _transformProp) { //for values like transform:"rotate(60deg) scale(0.5, 0.8)"
16129 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.
16130 copy[_transformProp] = v.transform;
16131 copy.display = "block"; //if display is "none", the browser often refuses to report the transform properties correctly.
16132 copy.position = "absolute";
16133 _doc.body.appendChild(_tempDiv);
16134 m2 = _getTransform(_tempDiv, null, false);
16135 _doc.body.removeChild(_tempDiv);
16136 } else if (typeof(v) === "object") { //for values like scaleX, scaleY, rotation, x, y, skewX, and skewY or transform:{...} (object)
16137 m2 = {scaleX:_parseVal((v.scaleX != null) ? v.scaleX : v.scale, m1.scaleX),
16138 scaleY:_parseVal((v.scaleY != null) ? v.scaleY : v.scale, m1.scaleY),
16139 scaleZ:_parseVal(v.scaleZ, m1.scaleZ),
16140 x:_parseVal(v.x, m1.x),
16141 y:_parseVal(v.y, m1.y),
16142 z:_parseVal(v.z, m1.z),
16143 perspective:_parseVal(v.transformPerspective, m1.perspective)};
16144 dr = v.directionalRotation;
16146 if (typeof(dr) === "object") {
16148 v[copy] = dr[copy];
16154 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);
16156 m2.rotationX = _parseAngle(("rotationX" in v) ? v.rotationX : ("shortRotationX" in v) ? v.shortRotationX + "_short" : m1.rotationX || 0, m1.rotationX, "rotationX", endRotations);
16157 m2.rotationY = _parseAngle(("rotationY" in v) ? v.rotationY : ("shortRotationY" in v) ? v.shortRotationY + "_short" : m1.rotationY || 0, m1.rotationY, "rotationY", endRotations);
16159 m2.skewX = (v.skewX == null) ? m1.skewX : _parseAngle(v.skewX, m1.skewX);
16161 //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.
16162 m2.skewY = (v.skewY == null) ? m1.skewY : _parseAngle(v.skewY, m1.skewY);
16163 if ((skewY = m2.skewY - m1.skewY)) {
16165 m2.rotation += skewY;
16169 if (_supports3D && v.force3D != null) {
16170 m1.force3D = v.force3D;
16174 m1.skewType = v.skewType || m1.skewType || CSSPlugin.defaultSkewType;
16176 has3D = (m1.force3D || m1.z || m1.rotationX || m1.rotationY || m2.z || m2.rotationX || m2.rotationY || m2.perspective);
16177 if (!has3D && v.scale != null) {
16178 m2.scaleZ = 1; //no need to tween scaleZ.
16182 p = _transformProps[i];
16183 orig = m2[p] - m1[p];
16184 if (orig > min || orig < -min || _forcePT[p] != null) {
16186 pt = new CSSPropTween(m1, p, m1[p], orig, pt);
16187 if (p in endRotations) {
16188 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
16190 pt.xs0 = 0; //ensures the value stays numeric in setRatio()
16191 pt.plugin = plugin;
16192 cssp._overwriteProps.push(pt.n);
16196 orig = v.transformOrigin;
16197 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).
16198 if (_transformProp) {
16200 p = _transformOriginProp;
16201 orig = (orig || _getStyle(t, p, _cs, false, "50% 50%")) + ""; //cast as string to avoid errors
16202 pt = new CSSPropTween(style, p, 0, 0, pt, -1, "transformOrigin");
16204 pt.plugin = plugin;
16207 orig = orig.split(" ");
16208 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.
16209 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)!
16210 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)
16212 pt.xs0 = pt.e = m1.zOrigin;
16214 pt.xs0 = pt.e = orig;
16217 //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).
16219 _parsePosition(orig + "", m1);
16224 cssp._transformType = (has3D || this._transformType === 3) ? 3 : 2; //quicker than calling cssp._enableTransforms();
16229 _registerComplexSpecialProp("boxShadow", {defaultValue:"0px 0px 0px 0px #999", prefix:true, color:true, multi:true, keyword:"inset"});
16231 _registerComplexSpecialProp("borderRadius", {defaultValue:"0px", parser:function(t, e, p, cssp, pt, plugin) {
16232 e = this.format(e);
16233 var props = ["borderTopLeftRadius","borderTopRightRadius","borderBottomRightRadius","borderBottomLeftRadius"],
16235 ea1, i, es2, bs2, bs, es, bn, en, w, h, esfx, bsfx, rel, hn, vn, em;
16236 w = parseFloat(t.offsetWidth);
16237 h = parseFloat(t.offsetHeight);
16238 ea1 = e.split(" ");
16239 for (i = 0; i < props.length; i++) { //if we're dealing with percentages, we must convert things separately for the horizontal and vertical axis!
16240 if (this.p.indexOf("border")) { //older browsers used a prefix
16241 props[i] = _checkPropPrefix(props[i]);
16243 bs = bs2 = _getStyle(t, props[i], _cs, false, "0px");
16244 if (bs.indexOf(" ") !== -1) {
16245 bs2 = bs.split(" ");
16250 bn = parseFloat(bs);
16251 bsfx = bs.substr((bn + "").length);
16252 rel = (es.charAt(1) === "=");
16254 en = parseInt(es.charAt(0)+"1", 10);
16256 en *= parseFloat(es);
16257 esfx = es.substr((en + "").length - (en < 0 ? 1 : 0)) || "";
16259 en = parseFloat(es);
16260 esfx = es.substr((en + "").length);
16263 esfx = _suffixMap[p] || bsfx;
16265 if (esfx !== bsfx) {
16266 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.
16267 vn = _convertToPixels(t, "borderTop", bn, bsfx); //vertical number
16268 if (esfx === "%") {
16269 bs = (hn / w * 100) + "%";
16270 bs2 = (vn / h * 100) + "%";
16271 } else if (esfx === "em") {
16272 em = _convertToPixels(t, "borderLeft", 1, "em");
16273 bs = (hn / em) + "em";
16274 bs2 = (vn / em) + "em";
16280 es = (parseFloat(bs) + en) + esfx;
16281 es2 = (parseFloat(bs2) + en) + esfx;
16284 pt = _parseComplex(style, props[i], bs + " " + bs2, es + " " + es2, false, "0px", pt);
16287 }, prefix:true, formatter:_getFormatter("0px 0px 0px 0px", false, true)});
16288 _registerComplexSpecialProp("backgroundPosition", {defaultValue:"0 0", parser:function(t, e, p, cssp, pt, plugin) {
16289 var bp = "background-position",
16290 cs = (_cs || _getComputedStyle(t, null)),
16291 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
16292 es = this.format(e),
16293 ba, ea, i, pct, overlap, src;
16294 if ((bs.indexOf("%") !== -1) !== (es.indexOf("%") !== -1)) {
16295 src = _getStyle(t, "backgroundImage").replace(_urlExp, "");
16296 if (src && src !== "none") {
16297 ba = bs.split(" ");
16298 ea = es.split(" ");
16299 _tempImg.setAttribute("src", src); //set the temp <img>'s src to the background-image so that we can measure its width/height
16303 pct = (bs.indexOf("%") !== -1);
16304 if (pct !== (ea[i].indexOf("%") !== -1)) {
16305 overlap = (i === 0) ? t.offsetWidth - _tempImg.width : t.offsetHeight - _tempImg.height;
16306 ba[i] = pct ? (parseFloat(bs) / 100 * overlap) + "px" : (parseFloat(bs) / overlap * 100) + "%";
16312 return this.parseComplex(t.style, bs, es, pt, plugin);
16313 }, formatter:_parsePosition});
16314 _registerComplexSpecialProp("backgroundSize", {defaultValue:"0 0", formatter:_parsePosition});
16315 _registerComplexSpecialProp("perspective", {defaultValue:"0px", prefix:true});
16316 _registerComplexSpecialProp("perspectiveOrigin", {defaultValue:"50% 50%", prefix:true});
16317 _registerComplexSpecialProp("transformStyle", {prefix:true});
16318 _registerComplexSpecialProp("backfaceVisibility", {prefix:true});
16319 _registerComplexSpecialProp("userSelect", {prefix:true});
16320 _registerComplexSpecialProp("margin", {parser:_getEdgeParser("marginTop,marginRight,marginBottom,marginLeft")});
16321 _registerComplexSpecialProp("padding", {parser:_getEdgeParser("paddingTop,paddingRight,paddingBottom,paddingLeft")});
16322 _registerComplexSpecialProp("clip", {defaultValue:"rect(0px,0px,0px,0px)", parser:function(t, e, p, cssp, pt, plugin){
16324 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.
16325 cs = t.currentStyle;
16326 delim = _ieVers < 8 ? " " : ",";
16327 b = "rect(" + cs.clipTop + delim + cs.clipRight + delim + cs.clipBottom + delim + cs.clipLeft + ")";
16328 e = this.format(e).split(",").join(delim);
16330 b = this.format(_getStyle(t, this.p, _cs, false, this.dflt));
16331 e = this.format(e);
16333 return this.parseComplex(t.style, b, e, pt, plugin);
16335 _registerComplexSpecialProp("textShadow", {defaultValue:"0px 0px 0px #999", color:true, multi:true});
16336 _registerComplexSpecialProp("autoRound,strictUnits", {parser:function(t, e, p, cssp, pt) {return pt;}}); //just so that we can ignore these properties (not tween them)
16337 _registerComplexSpecialProp("border", {defaultValue:"0px solid #000", parser:function(t, e, p, cssp, pt, plugin) {
16338 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);
16339 }, color:true, formatter:function(v) {
16340 var a = v.split(" ");
16341 return a[0] + " " + (a[1] || "solid") + " " + (v.match(_colorExp) || ["#000"])[0];
16343 _registerComplexSpecialProp("borderWidth", {parser:_getEdgeParser("borderTopWidth,borderRightWidth,borderBottomWidth,borderLeftWidth")}); //Firefox doesn't pick up on borderWidth set in style sheets (only inline).
16344 _registerComplexSpecialProp("float,cssFloat,styleFloat", {parser:function(t, e, p, cssp, pt, plugin) {
16346 prop = ("cssFloat" in s) ? "cssFloat" : "styleFloat";
16347 return new CSSPropTween(s, prop, 0, 0, pt, -1, p, false, 0, s[prop], e);
16351 var _setIEOpacityRatio = function(v) {
16352 var t = this.t, //refers to the element's style property
16353 filters = t.filter || _getStyle(this.data, "filter"),
16354 val = (this.s + this.c * v) | 0,
16356 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.
16357 if (filters.indexOf("atrix(") === -1 && filters.indexOf("radient(") === -1 && filters.indexOf("oader(") === -1) {
16358 t.removeAttribute("filter");
16359 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.
16361 t.filter = filters.replace(_alphaFilterExp, "");
16367 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.
16369 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
16370 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)
16371 t.filter = filters + " alpha(opacity=" + val + ")"; //we round the value because otherwise, bugs in IE7/8 can prevent "visibility" changes from being applied properly.
16374 t.filter = filters.replace(_opacityExp, "opacity=" + val);
16378 _registerComplexSpecialProp("opacity,alpha,autoAlpha", {defaultValue:"1", parser:function(t, e, p, cssp, pt, plugin) {
16379 var b = parseFloat(_getStyle(t, "opacity", _cs, false, "1")),
16381 isAutoAlpha = (p === "autoAlpha");
16382 if (typeof(e) === "string" && e.charAt(1) === "=") {
16383 e = ((e.charAt(0) === "-") ? -1 : 1) * parseFloat(e.substr(2)) + b;
16385 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)
16388 if (_supportsOpacity) {
16389 pt = new CSSPropTween(style, "opacity", b, e - b, pt);
16391 pt = new CSSPropTween(style, "opacity", b * 100, (e - b) * 100, pt);
16392 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.
16393 style.zoom = 1; //helps correct an IE issue.
16395 pt.b = "alpha(opacity=" + pt.s + ")";
16396 pt.e = "alpha(opacity=" + (pt.s + pt.c) + ")";
16398 pt.plugin = plugin;
16399 pt.setRatio = _setIEOpacityRatio;
16401 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
16402 pt = new CSSPropTween(style, "visibility", 0, 0, pt, -1, null, false, 0, ((b !== 0) ? "inherit" : "hidden"), ((e === 0) ? "hidden" : "inherit"));
16403 pt.xs0 = "inherit";
16404 cssp._overwriteProps.push(pt.n);
16405 cssp._overwriteProps.push(p);
16411 var _removeProp = function(s, p) {
16413 if (s.removeProperty) {
16414 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)
16415 p = "M" + p.substr(1);
16417 s.removeProperty(p.replace(_capsExp, "-$1").toLowerCase());
16418 } else { //note: old versions of IE use "removeAttribute()" instead of "removeProperty()"
16419 s.removeAttribute(p);
16423 _setClassNameRatio = function(v) {
16424 this.t._gsClassPT = this;
16425 if (v === 1 || v === 0) {
16426 this.t.setAttribute("class", (v === 0) ? this.b : this.e);
16427 var mpt = this.data, //first MiniPropTween
16431 _removeProp(s, mpt.p);
16437 if (v === 1 && this.t._gsClassPT === this) {
16438 this.t._gsClassPT = null;
16440 } else if (this.t.getAttribute("class") !== this.e) {
16441 this.t.setAttribute("class", this.e);
16444 _registerComplexSpecialProp("className", {parser:function(t, e, p, cssp, pt, plugin, vars) {
16445 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.
16446 cssText = t.style.cssText,
16447 difData, bs, cnpt, cnptLookup, mpt;
16448 pt = cssp._classNamePT = new CSSPropTween(t, p, 0, 0, pt, 2);
16449 pt.setRatio = _setClassNameRatio;
16451 _hasPriority = true;
16453 bs = _getAllStyles(t, _cs);
16454 //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)
16455 cnpt = t._gsClassPT;
16458 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.
16460 cnptLookup[mpt.p] = 1;
16466 pt.e = (e.charAt(1) !== "=") ? e : b.replace(new RegExp("\\s*\\b" + e.substr(2) + "\\b"), "") + ((e.charAt(0) === "+") ? " " + e.substr(2) : "");
16467 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.
16468 t.setAttribute("class", pt.e);
16469 difData = _cssDif(t, bs, _getAllStyles(t), vars, cnptLookup);
16470 t.setAttribute("class", b);
16471 pt.data = difData.firstMPT;
16472 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).
16473 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)
16479 var _setClearPropsRatio = function(v) {
16480 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).
16481 var s = this.t.style,
16482 transformParse = _specialProps.transform.parse,
16483 a, p, i, clearTransform;
16484 if (this.e === "all") {
16486 clearTransform = true;
16488 a = this.e.split(",");
16492 if (_specialProps[p]) {
16493 if (_specialProps[p].parse === transformParse) {
16494 clearTransform = true;
16496 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"
16502 if (clearTransform) {
16503 _removeProp(s, _transformProp);
16504 if (this.t._gsTransform) {
16505 delete this.t._gsTransform;
16511 _registerComplexSpecialProp("clearProps", {parser:function(t, e, p, cssp, pt) {
16512 pt = new CSSPropTween(t, p, 0, 0, pt, 2);
16513 pt.setRatio = _setClearPropsRatio;
16516 pt.data = cssp._tween;
16517 _hasPriority = true;
16521 p = "bezier,throwProps,physicsProps,physics2D".split(",");
16524 _registerPluginProp(p[i]);
16534 p = CSSPlugin.prototype;
16537 //gets called when the tween renders for the first time. This kicks everything off, recording start/end values, etc.
16538 p._onInitTween = function(target, vars, tween) {
16539 if (!target.nodeType) { //css is only for dom elements
16542 this._target = target;
16543 this._tween = tween;
16545 _autoRound = vars.autoRound;
16546 _hasPriority = false;
16547 _suffixMap = vars.suffixMap || CSSPlugin.suffixMap;
16548 _cs = _getComputedStyle(target, "");
16549 _overwriteProps = this._overwriteProps;
16550 var style = target.style,
16551 v, pt, pt2, first, last, next, zIndex, tpt, threeD;
16552 if (_reqSafariFix) if (style.zIndex === "") {
16553 v = _getStyle(target, "zIndex", _cs);
16554 if (v === "auto" || v === "") {
16555 //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.
16556 this._addLazySet(style, "zIndex", 0);
16560 if (typeof(vars) === "string") {
16561 first = style.cssText;
16562 v = _getAllStyles(target, _cs);
16563 style.cssText = first + ";" + vars;
16564 v = _cssDif(target, v, _getAllStyles(target)).difs;
16565 if (!_supportsOpacity && _opacityValExp.test(vars)) {
16566 v.opacity = parseFloat( RegExp.$1 );
16569 style.cssText = first;
16571 this._firstPT = pt = this.parse(target, vars, null);
16573 if (this._transformType) {
16574 threeD = (this._transformType === 3);
16575 if (!_transformProp) {
16576 style.zoom = 1; //helps correct an IE issue.
16577 } else if (_isSafari) {
16578 _reqSafariFix = true;
16579 //if zIndex isn't set, iOS Safari doesn't repaint things correctly sometimes (seemingly at random).
16580 if (style.zIndex === "") {
16581 zIndex = _getStyle(target, "zIndex", _cs);
16582 if (zIndex === "auto" || zIndex === "") {
16583 this._addLazySet(style, "zIndex", 0);
16586 //Setting WebkitBackfaceVisibility corrects 3 bugs:
16587 // 1) [non-Android] Safari skips rendering changes to "top" and "left" that are made on the same frame/render as a transform update.
16588 // 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.
16589 // 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.
16590 //Note: we allow the user to override the auto-setting by defining WebkitBackfaceVisibility in the vars of the tween.
16591 if (_isSafariLT6) {
16592 this._addLazySet(style, "WebkitBackfaceVisibility", this._vars.WebkitBackfaceVisibility || (threeD ? "visible" : "hidden"));
16596 while (pt2 && pt2._next) {
16599 tpt = new CSSPropTween(target, "transform", 0, 0, null, 2);
16600 this._linkCSSP(tpt, null, pt2);
16601 tpt.setRatio = (threeD && _supports3D) ? _set3DTransformRatio : _transformProp ? _set2DTransformRatio : _setIETransformRatio;
16602 tpt.data = this._transform || _getTransform(target, _cs, true);
16603 _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.
16606 if (_hasPriority) {
16607 //reorders the linked list in order of pr (priority)
16611 while (pt2 && pt2.pr > pt.pr) {
16614 if ((pt._prev = pt2 ? pt2._prev : last)) {
16615 pt._prev._next = pt;
16619 if ((pt._next = pt2)) {
16626 this._firstPT = first;
16632 p.parse = function(target, vars, pt, plugin) {
16633 var style = target.style,
16634 p, sp, bn, en, bs, es, bsfx, esfx, isStr, rel;
16636 es = vars[p]; //ending value string
16637 sp = _specialProps[p]; //SpecialProp lookup.
16639 pt = sp.parse(target, es, p, this, pt, plugin, vars);
16642 bs = _getStyle(target, p, _cs) + "";
16643 isStr = (typeof(es) === "string");
16644 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:
16646 es = _parseColor(es);
16647 es = ((es.length > 3) ? "rgba(" : "rgb(") + es.join(",") + ")";
16649 pt = _parseComplex(style, p, bs, es, true, "transparent", pt, 0, plugin);
16651 } else if (isStr && (es.indexOf(" ") !== -1 || es.indexOf(",") !== -1)) {
16652 pt = _parseComplex(style, p, bs, es, true, null, pt, 0, plugin);
16655 bn = parseFloat(bs);
16656 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.
16658 if (bs === "" || bs === "auto") {
16659 if (p === "width" || p === "height") {
16660 bn = _getDimension(target, p, _cs);
16662 } else if (p === "left" || p === "top") {
16663 bn = _calculateOffset(target, p, _cs);
16666 bn = (p !== "opacity") ? 0 : 1;
16671 rel = (isStr && es.charAt(1) === "=");
16673 en = parseInt(es.charAt(0) + "1", 10);
16675 en *= parseFloat(es);
16676 esfx = es.replace(_suffixExp, "");
16678 en = parseFloat(es);
16679 esfx = isStr ? es.substr((en + "").length) || "" : "";
16683 esfx = (p in _suffixMap) ? _suffixMap[p] : bsfx; //populate the end suffix, prioritizing the map, then if none is found, use the beginning suffix.
16686 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.
16688 //if the beginning/ending suffixes don't match, normalize them...
16689 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!
16690 bn = _convertToPixels(target, p, bn, bsfx);
16691 if (esfx === "%") {
16692 bn /= _convertToPixels(target, p, 100, "%") / 100;
16693 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.
16697 } else if (esfx === "em") {
16698 bn /= _convertToPixels(target, p, 1, "em");
16700 //otherwise convert to pixels.
16701 } else if (esfx !== "px") {
16702 en = _convertToPixels(target, p, en, esfx);
16703 esfx = "px"; //we don't use bsfx after this, so we don't need to set it to px too.
16705 if (rel) if (en || en === 0) {
16706 es = (en + bn) + esfx; //the changes we made affect relative calculations, so adjust the end value here.
16714 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.
16715 pt = new CSSPropTween(style, p, bn, en - bn, pt, 0, p, (_autoRound !== false && (esfx === "px" || p === "zIndex")), 0, bs, es);
16717 //DEBUG: _log("tween "+p+" from "+pt.b+" ("+bn+esfx+") to "+pt.e+" with suffix: "+pt.xs0);
16718 } else if (style[p] === undefined || !es && (es + "" === "NaN" || es == null)) {
16719 _log("invalid " + p + " tween value: " + vars[p]);
16721 pt = new CSSPropTween(style, p, en || bn || 0, 0, pt, -1, p, false, 0, bs, es);
16722 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.
16723 //DEBUG: _log("non-tweening value "+p+": "+pt.xs0);
16727 if (plugin) if (pt && !pt.plugin) {
16728 pt.plugin = plugin;
16735 //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.
16736 p.setRatio = function(v) {
16737 var pt = this._firstPT,
16741 //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).
16742 if (v === 1 && (this._tween._time === this._tween._duration || this._tween._time === 0)) {
16744 if (pt.type !== 2) {
16752 } else if (v || !(this._tween._time === this._tween._duration || this._tween._time === 0) || this._tween._rawPrevTime === -0.000001) {
16754 val = pt.c * v + pt.s;
16756 val = Math.round(val);
16757 } else if (val < min) if (val > -min) {
16761 pt.t[pt.p] = val + pt.xs0;
16762 } else if (pt.type === 1) { //complex value (one that typically has multiple numbers inside a string, like "rect(5px,10px,20px,25px)"
16765 pt.t[pt.p] = pt.xs0 + val + pt.xs1 + pt.xn1 + pt.xs2;
16766 } else if (i === 3) {
16767 pt.t[pt.p] = pt.xs0 + val + pt.xs1 + pt.xn1 + pt.xs2 + pt.xn2 + pt.xs3;
16768 } else if (i === 4) {
16769 pt.t[pt.p] = pt.xs0 + val + pt.xs1 + pt.xn1 + pt.xs2 + pt.xn2 + pt.xs3 + pt.xn3 + pt.xs4;
16770 } else if (i === 5) {
16771 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;
16773 str = pt.xs0 + val + pt.xs1;
16774 for (i = 1; i < pt.l; i++) {
16775 str += pt["xn"+i] + pt["xs"+(i+1)];
16780 } else if (pt.type === -1) { //non-tweening value
16781 pt.t[pt.p] = pt.xs0;
16783 } else if (pt.setRatio) { //custom setRatio() for things like SpecialProps, external plugins, etc.
16789 //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).
16792 if (pt.type !== 2) {
16804 * Forces rendering of the target's transforms (rotation, scale, etc.) whenever the CSSPlugin's setRatio() is called.
16805 * Basically, this tells the CSSPlugin to create a CSSPropTween (type 2) after instantiation that runs last in the linked
16806 * list and calls the appropriate (3D or 2D) rendering function. We separate this into its own method so that we can call
16807 * it from other plugins like BezierPlugin if, for example, it needs to apply an autoRotation and this CSSPlugin
16808 * doesn't have any transform-related properties of its own. You can call this method as many times as you
16809 * want and it won't create duplicate CSSPropTweens.
16811 * @param {boolean} threeD if true, it should apply 3D tweens (otherwise, just 2D ones are fine and typically faster)
16813 p._enableTransforms = function(threeD) {
16814 this._transformType = (threeD || this._transformType === 3) ? 3 : 2;
16815 this._transform = this._transform || _getTransform(this._target, _cs, true); //ensures that the element has a _gsTransform property with the appropriate values.
16818 var lazySet = function(v) {
16819 this.t[this.p] = this.e;
16820 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.
16822 /** @private Gives us a way to set a value on the first render (and only the first render). **/
16823 p._addLazySet = function(t, p, v) {
16824 var pt = this._firstPT = new CSSPropTween(t, p, 0, 0, this._firstPT, 2);
16826 pt.setRatio = lazySet;
16831 p._linkCSSP = function(pt, next, prev, remove) {
16837 pt._next._prev = pt._prev;
16840 pt._prev._next = pt._next;
16841 } else if (this._firstPT === pt) {
16842 this._firstPT = pt._next;
16843 remove = true; //just to prevent resetting this._firstPT 5 lines down in case pt._next is null. (optimized for speed)
16847 } else if (!remove && this._firstPT === null) {
16848 this._firstPT = pt;
16856 //we need to make sure that if alpha or autoAlpha is killed, opacity is too. And autoAlpha affects the "visibility" property.
16857 p._kill = function(lookup) {
16860 if (lookup.autoAlpha || lookup.alpha) {
16862 for (p in lookup) { //copy the lookup so that we're not changing the original which may be passed elsewhere.
16863 copy[p] = lookup[p];
16866 if (copy.autoAlpha) {
16867 copy.visibility = 1;
16870 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".
16871 xfirst = pt.xfirst;
16872 if (xfirst && xfirst._prev) {
16873 this._linkCSSP(xfirst._prev, pt._next, xfirst._prev._prev); //break off the prev
16874 } else if (xfirst === this._firstPT) {
16875 this._firstPT = pt._next;
16878 this._linkCSSP(pt._next, pt._next._next, xfirst._prev);
16880 this._classNamePT = null;
16882 return TweenPlugin.prototype._kill.call(this, copy);
16887 //used by cascadeTo() for gathering all the style properties of each child element into an array for comparison.
16888 var _getChildStyles = function(e, props, targets) {
16889 var children, i, child, type;
16893 _getChildStyles(e[i], props, targets);
16897 children = e.childNodes;
16898 i = children.length;
16900 child = children[i];
16903 props.push(_getAllStyles(child));
16905 targets.push(child);
16908 if ((type === 1 || type === 9 || type === 11) && child.childNodes.length) {
16909 _getChildStyles(child, props, targets);
16915 * Typically only useful for className tweens that may affect child elements, this method creates a TweenLite
16916 * and then compares the style properties of all the target's child elements at the tween's start and end, and
16917 * if any are different, it also creates tweens for those and returns an array containing ALL of the resulting
16918 * tweens (so that you can easily add() them to a TimelineLite, for example). The reason this functionality is
16919 * wrapped into a separate static method of CSSPlugin instead of being integrated into all regular className tweens
16920 * is because it creates entirely new tweens that may have completely different targets than the original tween,
16921 * so if they were all lumped into the original tween instance, it would be inconsistent with the rest of the API
16922 * and it would create other problems. For example:
16923 * - 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)
16924 * - 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.
16925 * - 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.
16927 * @param {Object} target object to be tweened
16928 * @param {number} Duration in seconds (or frames for frames-based tweens)
16929 * @param {Object} Object containing the end values, like {className:"newClass", ease:Linear.easeNone}
16930 * @return {Array} An array of TweenLite instances
16932 CSSPlugin.cascadeTo = function(target, duration, vars) {
16933 var tween = TweenLite.to(target, duration, vars),
16938 _reservedProps = TweenLite._internals.reservedProps,
16940 target = tween._targets || tween.target;
16941 _getChildStyles(target, b, targets);
16942 tween.render(duration, true);
16943 _getChildStyles(target, e);
16944 tween.render(0, true);
16945 tween._enabled(true);
16946 i = targets.length;
16948 difs = _cssDif(targets[i], b[i], e[i]);
16949 if (difs.firstMPT) {
16952 if (_reservedProps[p]) {
16956 results.push( TweenLite.to(targets[i], duration, difs) );
16962 TweenPlugin.activate([CSSPlugin]);
16978 * ----------------------------------------------------------------
16980 * ----------------------------------------------------------------
16984 var RoundPropsPlugin = window._gsDefine.plugin({
16985 propName: "roundProps",
16989 //called when the tween renders for the first time. This is where initial values should be recorded and any setup routines should run.
16990 init: function(target, value, tween) {
16991 this._tween = tween;
16996 p = RoundPropsPlugin.prototype;
16998 p._onInitAllProps = function() {
16999 var tween = this._tween,
17000 rp = (tween.vars.roundProps instanceof Array) ? tween.vars.roundProps : tween.vars.roundProps.split(","),
17003 rpt = tween._propLookup.roundProps,
17011 pt = tween._firstPT;
17013 next = pt._next; //record here, because it may get removed
17015 pt.t._roundProps(lookup, true);
17016 } else if (pt.n === prop) {
17017 this._add(pt.t, prop, pt.s, pt.c);
17018 //remove from linked list
17020 next._prev = pt._prev;
17023 pt._prev._next = next;
17024 } else if (tween._firstPT === pt) {
17025 tween._firstPT = next;
17027 pt._next = pt._prev = null;
17028 tween._propLookup[prop] = rpt;
17036 p._add = function(target, p, s, c) {
17037 this._addTween(target, p, s, s + c, p, true);
17038 this._overwriteProps.push(p);
17053 * ----------------------------------------------------------------
17055 * ----------------------------------------------------------------
17057 window._gsDefine.plugin({
17062 //called when the tween renders for the first time. This is where initial values should be recorded and any setup routines should run.
17063 init: function(target, value, tween) {
17065 if (typeof(target.setAttribute) !== "function") {
17068 this._target = target;
17070 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.
17073 this._start[p] = this._proxy[p] = start = target.getAttribute(p);
17074 end = this._addTween(this._proxy, p, parseFloat(start), value[p], p);
17075 this._end[p] = end ? end.s + end.c : value[p];
17076 this._overwriteProps.push(p);
17081 //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.)
17082 set: function(ratio) {
17083 this._super.setRatio.call(this, ratio);
17084 var props = this._overwriteProps,
17086 lookup = (ratio === 1) ? this._end : ratio ? this._proxy : this._start,
17090 this._target.setAttribute(p, lookup[p] + "");
17106 * ----------------------------------------------------------------
17107 * DirectionalRotationPlugin
17108 * ----------------------------------------------------------------
17110 window._gsDefine.plugin({
17111 propName: "directionalRotation",
17115 //called when the tween renders for the first time. This is where initial values should be recorded and any setup routines should run.
17116 init: function(target, value, tween) {
17117 if (typeof(value) !== "object") {
17118 value = {rotation:value};
17121 var cap = (value.useRadians === true) ? Math.PI * 2 : 360,
17123 p, v, start, end, dif, split;
17125 if (p !== "useRadians") {
17126 split = (value[p] + "").split("_");
17128 start = parseFloat( (typeof(target[p]) !== "function") ? target[p] : target[ ((p.indexOf("set") || typeof(target["get" + p.substr(3)]) !== "function") ? p : "get" + p.substr(3)) ]() );
17129 end = this.finals[p] = (typeof(v) === "string" && v.charAt(1) === "=") ? start + parseInt(v.charAt(0) + "1", 10) * Number(v.substr(2)) : Number(v) || 0;
17131 if (split.length) {
17132 v = split.join("_");
17133 if (v.indexOf("short") !== -1) {
17135 if (dif !== dif % (cap / 2)) {
17136 dif = (dif < 0) ? dif + cap : dif - cap;
17139 if (v.indexOf("_cw") !== -1 && dif < 0) {
17140 dif = ((dif + cap * 9999999999) % cap) - ((dif / cap) | 0) * cap;
17141 } else if (v.indexOf("ccw") !== -1 && dif > 0) {
17142 dif = ((dif - cap * 9999999999) % cap) - ((dif / cap) | 0) * cap;
17145 if (dif > min || dif < -min) {
17146 this._addTween(target, p, start, start + dif, p);
17147 this._overwriteProps.push(p);
17154 //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.)
17155 set: function(ratio) {
17158 this._super.setRatio.call(this, ratio);
17160 pt = this._firstPT;
17163 pt.t[pt.p](this.finals[pt.p]);
17165 pt.t[pt.p] = this.finals[pt.p];
17172 })._autoCSS = true;
17185 * ----------------------------------------------------------------
17187 * ----------------------------------------------------------------
17189 window._gsDefine("easing.Back", ["easing.Ease"], function(Ease) {
17191 var w = (window.GreenSockGlobals || window),
17192 gs = w.com.greensock,
17193 _2PI = Math.PI * 2,
17194 _HALF_PI = Math.PI / 2,
17195 _class = gs._class,
17196 _create = function(n, f) {
17197 var C = _class("easing." + n, function(){}, true),
17198 p = C.prototype = new Ease();
17203 _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.
17204 _wrap = function(name, EaseOut, EaseIn, EaseInOut, aliases) {
17205 var C = _class("easing."+name, {
17206 easeOut:new EaseOut(),
17207 easeIn:new EaseIn(),
17208 easeInOut:new EaseInOut()
17213 EasePoint = function(time, value, next) {
17219 this.c = next.v - value;
17220 this.gap = next.t - time;
17225 _createBack = function(n, f) {
17226 var C = _class("easing." + n, function(overshoot) {
17227 this._p1 = (overshoot || overshoot === 0) ? overshoot : 1.70158;
17228 this._p2 = this._p1 * 1.525;
17230 p = C.prototype = new Ease();
17233 p.config = function(overshoot) {
17234 return new C(overshoot);
17239 Back = _wrap("Back",
17240 _createBack("BackOut", function(p) {
17241 return ((p = p - 1) * p * ((this._p1 + 1) * p + this._p1) + 1);
17243 _createBack("BackIn", function(p) {
17244 return p * p * ((this._p1 + 1) * p - this._p1);
17246 _createBack("BackInOut", function(p) {
17247 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);
17253 SlowMo = _class("easing.SlowMo", function(linearRatio, power, yoyoMode) {
17254 power = (power || power === 0) ? power : 0.7;
17255 if (linearRatio == null) {
17257 } else if (linearRatio > 1) {
17260 this._p = (linearRatio !== 1) ? power : 0;
17261 this._p1 = (1 - linearRatio) / 2;
17262 this._p2 = linearRatio;
17263 this._p3 = this._p1 + this._p2;
17264 this._calcEnd = (yoyoMode === true);
17266 p = SlowMo.prototype = new Ease(),
17267 SteppedEase, RoughEase, _createElastic;
17269 p.constructor = SlowMo;
17270 p.getRatio = function(p) {
17271 var r = p + (0.5 - p) * this._p;
17272 if (p < this._p1) {
17273 return this._calcEnd ? 1 - ((p = 1 - (p / this._p1)) * p) : r - ((p = 1 - (p / this._p1)) * p * p * p * r);
17274 } else if (p > this._p3) {
17275 return this._calcEnd ? 1 - (p = (p - this._p3) / this._p1) * p : r + ((p - r) * (p = (p - this._p3) / this._p1) * p * p * p);
17277 return this._calcEnd ? 1 : r;
17279 SlowMo.ease = new SlowMo(0.7, 0.7);
17281 p.config = SlowMo.config = function(linearRatio, power, yoyoMode) {
17282 return new SlowMo(linearRatio, power, yoyoMode);
17287 SteppedEase = _class("easing.SteppedEase", function(steps) {
17288 steps = steps || 1;
17289 this._p1 = 1 / steps;
17290 this._p2 = steps + 1;
17292 p = SteppedEase.prototype = new Ease();
17293 p.constructor = SteppedEase;
17294 p.getRatio = function(p) {
17297 } else if (p >= 1) {
17300 return ((this._p2 * p) >> 0) * this._p1;
17302 p.config = SteppedEase.config = function(steps) {
17303 return new SteppedEase(steps);
17308 RoughEase = _class("easing.RoughEase", function(vars) {
17310 var taper = vars.taper || "none",
17313 points = (vars.points || 20) | 0,
17315 randomize = (vars.randomize !== false),
17316 clamp = (vars.clamp === true),
17317 template = (vars.template instanceof Ease) ? vars.template : null,
17318 strength = (typeof(vars.strength) === "number") ? vars.strength * 0.4 : 0.4,
17319 x, y, bump, invX, obj, pnt;
17321 x = randomize ? Math.random() : (1 / points) * i;
17322 y = template ? template.getRatio(x) : x;
17323 if (taper === "none") {
17325 } else if (taper === "out") {
17327 bump = invX * invX * strength;
17328 } else if (taper === "in") {
17329 bump = x * x * strength;
17330 } else if (x < 0.5) { //"both" (start)
17332 bump = invX * invX * 0.5 * strength;
17333 } else { //"both" (end)
17334 invX = (1 - x) * 2;
17335 bump = invX * invX * 0.5 * strength;
17338 y += (Math.random() * bump) - (bump * 0.5);
17339 } else if (i % 2) {
17347 } else if (y < 0) {
17351 a[cnt++] = {x:x, y:y};
17353 a.sort(function(a, b) {
17357 pnt = new EasePoint(1, 1, null);
17361 pnt = new EasePoint(obj.x, obj.y, pnt);
17364 this._prev = new EasePoint(0, 0, (pnt.t !== 0) ? pnt : pnt.next);
17366 p = RoughEase.prototype = new Ease();
17367 p.constructor = RoughEase;
17368 p.getRatio = function(p) {
17369 var pnt = this._prev;
17371 while (pnt.next && p >= pnt.t) {
17376 while (pnt.prev && p <= pnt.t) {
17381 return (pnt.v + ((p - pnt.t) / pnt.gap) * pnt.c);
17383 p.config = function(vars) {
17384 return new RoughEase(vars);
17386 RoughEase.ease = new RoughEase();
17391 _create("BounceOut", function(p) {
17392 if (p < 1 / 2.75) {
17393 return 7.5625 * p * p;
17394 } else if (p < 2 / 2.75) {
17395 return 7.5625 * (p -= 1.5 / 2.75) * p + 0.75;
17396 } else if (p < 2.5 / 2.75) {
17397 return 7.5625 * (p -= 2.25 / 2.75) * p + 0.9375;
17399 return 7.5625 * (p -= 2.625 / 2.75) * p + 0.984375;
17401 _create("BounceIn", function(p) {
17402 if ((p = 1 - p) < 1 / 2.75) {
17403 return 1 - (7.5625 * p * p);
17404 } else if (p < 2 / 2.75) {
17405 return 1 - (7.5625 * (p -= 1.5 / 2.75) * p + 0.75);
17406 } else if (p < 2.5 / 2.75) {
17407 return 1 - (7.5625 * (p -= 2.25 / 2.75) * p + 0.9375);
17409 return 1 - (7.5625 * (p -= 2.625 / 2.75) * p + 0.984375);
17411 _create("BounceInOut", function(p) {
17412 var invert = (p < 0.5);
17418 if (p < 1 / 2.75) {
17419 p = 7.5625 * p * p;
17420 } else if (p < 2 / 2.75) {
17421 p = 7.5625 * (p -= 1.5 / 2.75) * p + 0.75;
17422 } else if (p < 2.5 / 2.75) {
17423 p = 7.5625 * (p -= 2.25 / 2.75) * p + 0.9375;
17425 p = 7.5625 * (p -= 2.625 / 2.75) * p + 0.984375;
17427 return invert ? (1 - p) * 0.5 : p * 0.5 + 0.5;
17434 _create("CircOut", function(p) {
17435 return Math.sqrt(1 - (p = p - 1) * p);
17437 _create("CircIn", function(p) {
17438 return -(Math.sqrt(1 - (p * p)) - 1);
17440 _create("CircInOut", function(p) {
17441 return ((p*=2) < 1) ? -0.5 * (Math.sqrt(1 - p * p) - 1) : 0.5 * (Math.sqrt(1 - (p -= 2) * p) + 1);
17447 _createElastic = function(n, f, def) {
17448 var C = _class("easing." + n, function(amplitude, period) {
17449 this._p1 = amplitude || 1;
17450 this._p2 = period || def;
17451 this._p3 = this._p2 / _2PI * (Math.asin(1 / this._p1) || 0);
17453 p = C.prototype = new Ease();
17456 p.config = function(amplitude, period) {
17457 return new C(amplitude, period);
17462 _createElastic("ElasticOut", function(p) {
17463 return this._p1 * Math.pow(2, -10 * p) * Math.sin( (p - this._p3) * _2PI / this._p2 ) + 1;
17465 _createElastic("ElasticIn", function(p) {
17466 return -(this._p1 * Math.pow(2, 10 * (p -= 1)) * Math.sin( (p - this._p3) * _2PI / this._p2 ));
17468 _createElastic("ElasticInOut", function(p) {
17469 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;
17476 _create("ExpoOut", function(p) {
17477 return 1 - Math.pow(2, -10 * p);
17479 _create("ExpoIn", function(p) {
17480 return Math.pow(2, 10 * (p - 1)) - 0.001;
17482 _create("ExpoInOut", function(p) {
17483 return ((p *= 2) < 1) ? 0.5 * Math.pow(2, 10 * (p - 1)) : 0.5 * (2 - Math.pow(2, -10 * (p - 1)));
17490 _create("SineOut", function(p) {
17491 return Math.sin(p * _HALF_PI);
17493 _create("SineIn", function(p) {
17494 return -Math.cos(p * _HALF_PI) + 1;
17496 _create("SineInOut", function(p) {
17497 return -0.5 * (Math.cos(Math.PI * p) - 1);
17501 _class("easing.EaseLookup", {
17503 return Ease.map[s];
17507 //register the non-standard eases
17508 _easeReg(w.SlowMo, "SlowMo", "ease,");
17509 _easeReg(RoughEase, "RoughEase", "ease,");
17510 _easeReg(SteppedEase, "SteppedEase", "ease,");
17530 * ----------------------------------------------------------------
17531 * Base classes like TweenLite, SimpleTimeline, Ease, Ticker, etc.
17532 * ----------------------------------------------------------------
17534 (function(window) {
17537 var _globals = window.GreenSockGlobals || window;
17538 if (_globals.TweenLite) {
17539 return; //in case the core set of classes is already loaded, don't instantiate twice.
17541 var _namespace = function(ns) {
17542 var a = ns.split("."),
17544 for (i = 0; i < a.length; i++) {
17545 p[a[i]] = p = p[a[i]] || {};
17549 gs = _namespace("com.greensock"),
17550 _tinyNum = 0.0000000001,
17552 _emptyFunc = function() {},
17553 _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)
17554 var toString = Object.prototype.toString,
17555 array = toString.call([]);
17556 return function(obj) {
17557 return obj != null && (obj instanceof Array || (typeof(obj) === "object" && !!obj.push && toString.call(obj) === array));
17560 a, i, p, _ticker, _tickerActive,
17565 * Defines a GreenSock class, optionally with an array of dependencies that must be instantiated first and passed into the definition.
17566 * This allows users to load GreenSock JS files in any order even if they have interdependencies (like CSSPlugin extends TweenPlugin which is
17567 * inside TweenLite.js, but if CSSPlugin is loaded first, it should wait to run its code until TweenLite.js loads and instantiates TweenPlugin
17568 * and then pass TweenPlugin to CSSPlugin's definition). This is all done automatically and internally.
17570 * Every definition will be added to a "com.greensock" global object (typically window, but if a window.GreenSockGlobals object is found,
17571 * 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,
17572 * it is ALSO referenced at window.TweenLite. However some classes aren't considered global, like the base com.greensock.core.Animation class, so
17573 * those will only be at the package like window.com.greensock.core.Animation. Again, if you define a GreenSockGlobals object on the window, everything
17574 * gets tucked neatly inside there instead of on the window directly. This allows you to do advanced things like load multiple versions of GreenSock
17575 * 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
17576 * sandbox the banner one like:
17579 * 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.
17581 * <script src="js/greensock/v1.7/TweenMax.js"></script>
17583 * 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(...)
17585 * <script src="js/greensock/v1.6/TweenMax.js"></script>
17587 * gs.TweenLite.to(...); //would use v1.7
17588 * TweenLite.to(...); //would use v1.6
17591 * @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".
17592 * @param {!Array.<string>} dependencies An array of dependencies (described as their namespaces minus "com.greensock." prefix). For example ["TweenLite","plugins.TweenPlugin","core.Animation"]
17593 * @param {!function():Object} func The function that should be called and passed the resolved dependencies which will return the actual class for this definition.
17594 * @param {boolean=} global If true, the class will be added to the global scope (typically window unless you define a window.GreenSockGlobals object)
17596 Definition = function(ns, dependencies, func, global) {
17597 this.sc = (_defLookup[ns]) ? _defLookup[ns].sc : []; //subclasses
17598 _defLookup[ns] = this;
17599 this.gsClass = null;
17602 this.check = function(init) {
17603 var i = dependencies.length,
17607 if ((cur = _defLookup[dependencies[i]] || new Definition(dependencies[i], [])).gsClass) {
17608 _classes[i] = cur.gsClass;
17614 if (missing === 0 && func) {
17615 a = ("com.greensock." + ns).split(".");
17617 cl = _namespace(a.join("."))[n] = this.gsClass = func.apply(func, _classes);
17619 //exports to multiple environments
17621 _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.)
17622 if (typeof(define) === "function" && define.amd){ //AMD
17623 define((window.GreenSockAMDPath ? window.GreenSockAMDPath + "/" : "") + ns.split(".").join("/"), [], function() { return cl; });
17624 } else if (typeof(module) !== "undefined" && module.exports){ //node
17625 module.exports = cl;
17628 for (i = 0; i < this.sc.length; i++) {
17629 this.sc[i].check();
17636 //used to create Definition instances (which basically registers a class that has dependencies).
17637 _gsDefine = window._gsDefine = function(ns, dependencies, func, global) {
17638 return new Definition(ns, dependencies, func, global);
17641 //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).
17642 _class = gs._class = function(ns, func, global) {
17643 func = func || function() {};
17644 _gsDefine(ns, [], function(){ return func; }, global);
17648 _gsDefine.globals = _globals;
17653 * ----------------------------------------------------------------
17655 * ----------------------------------------------------------------
17657 var _baseParams = [0, 0, 1, 1],
17659 Ease = _class("easing.Ease", function(func, extraParams, type, power) {
17661 this._type = type || 0;
17662 this._power = power || 0;
17663 this._params = extraParams ? _baseParams.concat(extraParams) : _baseParams;
17665 _easeMap = Ease.map = {},
17666 _easeReg = Ease.register = function(ease, names, types, create) {
17667 var na = names.split(","),
17669 ta = (types || "easeIn,easeOut,easeInOut").split(","),
17673 e = create ? _class("easing."+name, null, true) : gs.easing[name] || {};
17677 _easeMap[name + "." + type] = _easeMap[type + name] = e[type] = ease.getRatio ? ease : ease[type] || new ease();
17682 p = Ease.prototype;
17683 p._calcEnd = false;
17684 p.getRatio = function(p) {
17686 this._params[0] = p;
17687 return this._func.apply(null, this._params);
17689 var t = this._type,
17691 r = (t === 1) ? 1 - p : (t === 2) ? p : (p < 0.5) ? p * 2 : (1 - p) * 2;
17694 } else if (pw === 2) {
17696 } else if (pw === 3) {
17698 } else if (pw === 4) {
17699 r *= r * r * r * r;
17701 return (t === 1) ? 1 - r : (t === 2) ? r : (p < 0.5) ? r / 2 : 1 - (r / 2);
17704 //create all the standard eases like Linear, Quad, Cubic, Quart, Quint, Strong, Power0, Power1, Power2, Power3, and Power4 (each with easeIn, easeOut, and easeInOut)
17705 a = ["Linear","Quad","Cubic","Quart","Quint,Strong"];
17708 p = a[i]+",Power"+i;
17709 _easeReg(new Ease(null,null,1,i), p, "easeOut", true);
17710 _easeReg(new Ease(null,null,2,i), p, "easeIn" + ((i === 0) ? ",easeNone" : ""));
17711 _easeReg(new Ease(null,null,3,i), p, "easeInOut");
17713 _easeMap.linear = gs.easing.Linear.easeIn;
17714 _easeMap.swing = gs.easing.Quad.easeInOut; //for jQuery folks
17718 * ----------------------------------------------------------------
17720 * ----------------------------------------------------------------
17722 var EventDispatcher = _class("events.EventDispatcher", function(target) {
17723 this._listeners = {};
17724 this._eventTarget = target || this;
17726 p = EventDispatcher.prototype;
17728 p.addEventListener = function(type, callback, scope, useParam, priority) {
17729 priority = priority || 0;
17730 var list = this._listeners[type],
17733 if (list == null) {
17734 this._listeners[type] = list = [];
17738 listener = list[i];
17739 if (listener.c === callback && listener.s === scope) {
17741 } else if (index === 0 && listener.pr < priority) {
17745 list.splice(index, 0, {c:callback, s:scope, up:useParam, pr:priority});
17746 if (this === _ticker && !_tickerActive) {
17751 p.removeEventListener = function(type, callback) {
17752 var list = this._listeners[type], i;
17756 if (list[i].c === callback) {
17764 p.dispatchEvent = function(type) {
17765 var list = this._listeners[type],
17769 t = this._eventTarget;
17771 listener = list[i];
17773 listener.c.call(listener.s || t, {type:type, target:t});
17775 listener.c.call(listener.s || t);
17783 * ----------------------------------------------------------------
17785 * ----------------------------------------------------------------
17787 var _reqAnimFrame = window.requestAnimationFrame,
17788 _cancelAnimFrame = window.cancelAnimationFrame,
17789 _getTime = Date.now || function() {return new Date().getTime();},
17790 _lastUpdate = _getTime();
17792 //now try to determine the requestAnimationFrame and cancelAnimationFrame functions and if none are found, we'll use a setTimeout()/clearTimeout() polyfill.
17793 a = ["ms","moz","webkit","o"];
17795 while (--i > -1 && !_reqAnimFrame) {
17796 _reqAnimFrame = window[a[i] + "RequestAnimationFrame"];
17797 _cancelAnimFrame = window[a[i] + "CancelAnimationFrame"] || window[a[i] + "CancelRequestAnimationFrame"];
17800 _class("Ticker", function(fps, useRAF) {
17802 _startTime = _getTime(),
17803 _useRAF = (useRAF !== false && _reqAnimFrame),
17804 _lagThreshold = 500,
17806 _fps, _req, _id, _gap, _nextTime,
17807 _tick = function(manual) {
17808 var elapsed = _getTime() - _lastUpdate,
17810 if (elapsed > _lagThreshold) {
17811 _startTime += elapsed - _adjustedLag;
17813 _lastUpdate += elapsed;
17814 _self.time = (_lastUpdate - _startTime) / 1000;
17815 overlap = _self.time - _nextTime;
17816 if (!_fps || overlap > 0 || manual === true) {
17818 _nextTime += overlap + (overlap >= _gap ? 0.004 : _gap - overlap);
17821 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.
17825 _self.dispatchEvent("tick");
17829 EventDispatcher.call(_self);
17830 _self.time = _self.frame = 0;
17831 _self.tick = function() {
17835 _self.lagSmoothing = function(threshold, adjustedLag) {
17836 _lagThreshold = threshold || (1 / _tinyNum); //zero should be interpreted as basically unlimited
17837 _adjustedLag = Math.min(adjustedLag, _lagThreshold, 0);
17840 _self.sleep = function() {
17844 if (!_useRAF || !_cancelAnimFrame) {
17847 _cancelAnimFrame(_id);
17851 if (_self === _ticker) {
17852 _tickerActive = false;
17856 _self.wake = function() {
17857 if (_id !== null) {
17859 } 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().
17860 _lastUpdate = _getTime() - _lagThreshold + 5;
17862 _req = (_fps === 0) ? _emptyFunc : (!_useRAF || !_reqAnimFrame) ? function(f) { return setTimeout(f, ((_nextTime - _self.time) * 1000 + 1) | 0); } : _reqAnimFrame;
17863 if (_self === _ticker) {
17864 _tickerActive = true;
17869 _self.fps = function(value) {
17870 if (!arguments.length) {
17874 _gap = 1 / (_fps || 60);
17875 _nextTime = this.time + _gap;
17879 _self.useRAF = function(value) {
17880 if (!arguments.length) {
17889 //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.
17890 setTimeout(function() {
17891 if (_useRAF && (!_id || _self.frame < 5)) {
17892 _self.useRAF(false);
17897 p = gs.Ticker.prototype = new gs.events.EventDispatcher();
17898 p.constructor = gs.Ticker;
17902 * ----------------------------------------------------------------
17904 * ----------------------------------------------------------------
17906 var Animation = _class("core.Animation", function(duration, vars) {
17907 this.vars = vars = vars || {};
17908 this._duration = this._totalDuration = duration || 0;
17909 this._delay = Number(vars.delay) || 0;
17910 this._timeScale = 1;
17911 this._active = (vars.immediateRender === true);
17912 this.data = vars.data;
17913 this._reversed = (vars.reversed === true);
17915 if (!_rootTimeline) {
17918 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.
17922 var tl = this.vars.useFrames ? _rootFramesTimeline : _rootTimeline;
17923 tl.add(this, tl._time);
17925 if (this.vars.paused) {
17930 _ticker = Animation.ticker = new gs.Ticker();
17931 p = Animation.prototype;
17932 p._dirty = p._gc = p._initted = p._paused = false;
17933 p._totalTime = p._time = 0;
17934 p._rawPrevTime = -1;
17935 p._next = p._last = p._onUpdate = p._timeline = p.timeline = null;
17939 //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.
17940 var _checkTimeout = function() {
17941 if (_tickerActive && _getTime() - _lastUpdate > 2000) {
17944 setTimeout(_checkTimeout, 2000);
17949 p.play = function(from, suppressEvents) {
17950 if (from != null) {
17951 this.seek(from, suppressEvents);
17953 return this.reversed(false).paused(false);
17956 p.pause = function(atTime, suppressEvents) {
17957 if (atTime != null) {
17958 this.seek(atTime, suppressEvents);
17960 return this.paused(true);
17963 p.resume = function(from, suppressEvents) {
17964 if (from != null) {
17965 this.seek(from, suppressEvents);
17967 return this.paused(false);
17970 p.seek = function(time, suppressEvents) {
17971 return this.totalTime(Number(time), suppressEvents !== false);
17974 p.restart = function(includeDelay, suppressEvents) {
17975 return this.reversed(false).paused(false).totalTime(includeDelay ? -this._delay : 0, (suppressEvents !== false), true);
17978 p.reverse = function(from, suppressEvents) {
17979 if (from != null) {
17980 this.seek((from || this.totalDuration()), suppressEvents);
17982 return this.reversed(true).paused(false);
17985 p.render = function(time, suppressEvents, force) {
17986 //stub - we override this method in subclasses.
17989 p.invalidate = function() {
17993 p.isActive = function() {
17994 var tl = this._timeline, //the 2 root timelines won't have a _timeline; they're always active.
17995 startTime = this._startTime,
17997 return (!tl || (!this._gc && !this._paused && tl.isActive() && (rawTime = tl.rawTime()) >= startTime && rawTime < startTime + this.totalDuration() / this._timeScale));
18000 p._enabled = function (enabled, ignoreTimeline) {
18001 if (!_tickerActive) {
18004 this._gc = !enabled;
18005 this._active = this.isActive();
18006 if (ignoreTimeline !== true) {
18007 if (enabled && !this.timeline) {
18008 this._timeline.add(this, this._startTime - this._delay);
18009 } else if (!enabled && this.timeline) {
18010 this._timeline._remove(this, true);
18017 p._kill = function(vars, target) {
18018 return this._enabled(false, false);
18021 p.kill = function(vars, target) {
18022 this._kill(vars, target);
18026 p._uncache = function(includeSelf) {
18027 var tween = includeSelf ? this : this.timeline;
18029 tween._dirty = true;
18030 tween = tween.timeline;
18035 p._swapSelfInParams = function(params) {
18036 var i = params.length,
18037 copy = params.concat();
18039 if (params[i] === "{self}") {
18046 //----Animation getters/setters --------------------------------------------------------
18048 p.eventCallback = function(type, callback, params, scope) {
18049 if ((type || "").substr(0,2) === "on") {
18051 if (arguments.length === 1) {
18054 if (callback == null) {
18057 v[type] = callback;
18058 v[type + "Params"] = (_isArray(params) && params.join("").indexOf("{self}") !== -1) ? this._swapSelfInParams(params) : params;
18059 v[type + "Scope"] = scope;
18061 if (type === "onUpdate") {
18062 this._onUpdate = callback;
18068 p.delay = function(value) {
18069 if (!arguments.length) {
18070 return this._delay;
18072 if (this._timeline.smoothChildTiming) {
18073 this.startTime( this._startTime + value - this._delay );
18075 this._delay = value;
18079 p.duration = function(value) {
18080 if (!arguments.length) {
18081 this._dirty = false;
18082 return this._duration;
18084 this._duration = this._totalDuration = value;
18085 this._uncache(true); //true in case it's a TweenMax or TimelineMax that has a repeat - we'll need to refresh the totalDuration.
18086 if (this._timeline.smoothChildTiming) if (this._time > 0) if (this._time < this._duration) if (value !== 0) {
18087 this.totalTime(this._totalTime * (value / this._duration), true);
18092 p.totalDuration = function(value) {
18093 this._dirty = false;
18094 return (!arguments.length) ? this._totalDuration : this.duration(value);
18097 p.time = function(value, suppressEvents) {
18098 if (!arguments.length) {
18102 this.totalDuration();
18104 return this.totalTime((value > this._duration) ? this._duration : value, suppressEvents);
18107 p.totalTime = function(time, suppressEvents, uncapped) {
18108 if (!_tickerActive) {
18111 if (!arguments.length) {
18112 return this._totalTime;
18114 if (this._timeline) {
18115 if (time < 0 && !uncapped) {
18116 time += this.totalDuration();
18118 if (this._timeline.smoothChildTiming) {
18120 this.totalDuration();
18122 var totalDuration = this._totalDuration,
18123 tl = this._timeline;
18124 if (time > totalDuration && !uncapped) {
18125 time = totalDuration;
18127 this._startTime = (this._paused ? this._pauseTime : tl._time) - ((!this._reversed ? time : totalDuration - time) / this._timeScale);
18128 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.
18129 this._uncache(false);
18131 //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.
18132 if (tl._timeline) {
18133 while (tl._timeline) {
18134 if (tl._timeline._time !== (tl._startTime + tl._totalTime) / tl._timeScale) {
18135 tl.totalTime(tl._totalTime, true);
18142 this._enabled(true, false);
18144 if (this._totalTime !== time || this._duration === 0) {
18145 this.render(time, suppressEvents, false);
18146 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.
18154 p.progress = p.totalProgress = function(value, suppressEvents) {
18155 return (!arguments.length) ? this._time / this.duration() : this.totalTime(this.duration() * value, suppressEvents);
18158 p.startTime = function(value) {
18159 if (!arguments.length) {
18160 return this._startTime;
18162 if (value !== this._startTime) {
18163 this._startTime = value;
18164 if (this.timeline) if (this.timeline._sortChildren) {
18165 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.
18171 p.timeScale = function(value) {
18172 if (!arguments.length) {
18173 return this._timeScale;
18175 value = value || _tinyNum; //can't allow zero because it'll throw the math off
18176 if (this._timeline && this._timeline.smoothChildTiming) {
18177 var pauseTime = this._pauseTime,
18178 t = (pauseTime || pauseTime === 0) ? pauseTime : this._timeline.totalTime();
18179 this._startTime = t - ((t - this._startTime) * this._timeScale / value);
18181 this._timeScale = value;
18182 return this._uncache(false);
18185 p.reversed = function(value) {
18186 if (!arguments.length) {
18187 return this._reversed;
18189 if (value != this._reversed) {
18190 this._reversed = value;
18191 this.totalTime(((this._timeline && !this._timeline.smoothChildTiming) ? this.totalDuration() - this._totalTime : this._totalTime), true);
18196 p.paused = function(value) {
18197 if (!arguments.length) {
18198 return this._paused;
18200 if (value != this._paused) if (this._timeline) {
18201 if (!_tickerActive && !value) {
18204 var tl = this._timeline,
18205 raw = tl.rawTime(),
18206 elapsed = raw - this._pauseTime;
18207 if (!value && tl.smoothChildTiming) {
18208 this._startTime += elapsed;
18209 this._uncache(false);
18211 this._pauseTime = value ? raw : null;
18212 this._paused = value;
18213 this._active = this.isActive();
18214 if (!value && elapsed !== 0 && this._initted && this.duration()) {
18215 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.
18218 if (this._gc && !value) {
18219 this._enabled(true, false);
18226 * ----------------------------------------------------------------
18228 * ----------------------------------------------------------------
18230 var SimpleTimeline = _class("core.SimpleTimeline", function(vars) {
18231 Animation.call(this, 0, vars);
18232 this.autoRemoveChildren = this.smoothChildTiming = true;
18235 p = SimpleTimeline.prototype = new Animation();
18236 p.constructor = SimpleTimeline;
18237 p.kill()._gc = false;
18238 p._first = p._last = null;
18239 p._sortChildren = false;
18241 p.add = p.insert = function(child, position, align, stagger) {
18243 child._startTime = Number(position || 0) + child._delay;
18244 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).
18245 child._pauseTime = child._startTime + ((this.rawTime() - child._startTime) / child._timeScale);
18247 if (child.timeline) {
18248 child.timeline._remove(child, true); //removes from existing timeline so that it can be properly added to this one.
18250 child.timeline = child._timeline = this;
18252 child._enabled(true, true);
18254 prevTween = this._last;
18255 if (this._sortChildren) {
18256 st = child._startTime;
18257 while (prevTween && prevTween._startTime > st) {
18258 prevTween = prevTween._prev;
18262 child._next = prevTween._next;
18263 prevTween._next = child;
18265 child._next = this._first;
18266 this._first = child;
18269 child._next._prev = child;
18271 this._last = child;
18273 child._prev = prevTween;
18274 if (this._timeline) {
18275 this._uncache(true);
18280 p._remove = function(tween, skipDisable) {
18281 if (tween.timeline === this) {
18282 if (!skipDisable) {
18283 tween._enabled(false, true);
18285 tween.timeline = null;
18288 tween._prev._next = tween._next;
18289 } else if (this._first === tween) {
18290 this._first = tween._next;
18293 tween._next._prev = tween._prev;
18294 } else if (this._last === tween) {
18295 this._last = tween._prev;
18298 if (this._timeline) {
18299 this._uncache(true);
18305 p.render = function(time, suppressEvents, force) {
18306 var tween = this._first,
18308 this._totalTime = this._time = this._rawPrevTime = time;
18310 next = tween._next; //record it here because the value could change after rendering...
18311 if (tween._active || (time >= tween._startTime && !tween._paused)) {
18312 if (!tween._reversed) {
18313 tween.render((time - tween._startTime) * tween._timeScale, suppressEvents, force);
18315 tween.render(((!tween._dirty) ? tween._totalDuration : tween.totalDuration()) - ((time - tween._startTime) * tween._timeScale), suppressEvents, force);
18322 p.rawTime = function() {
18323 if (!_tickerActive) {
18326 return this._totalTime;
18330 * ----------------------------------------------------------------
18332 * ----------------------------------------------------------------
18334 var TweenLite = _class("TweenLite", function(target, duration, vars) {
18335 Animation.call(this, duration, vars);
18336 this.render = TweenLite.prototype.render; //speed optimization (avoid prototype lookup on this "hot" method)
18338 if (target == null) {
18339 throw "Cannot tween a null target.";
18342 this.target = target = (typeof(target) !== "string") ? target : TweenLite.selector(target) || target;
18344 var isSelector = (target.jquery || (target.length && target !== window && target[0] && (target[0] === window || (target[0].nodeType && target[0].style && !target.nodeType)))),
18345 overwrite = this.vars.overwrite,
18348 this._overwrite = overwrite = (overwrite == null) ? _overwriteLookup[TweenLite.defaultOverwrite] : (typeof(overwrite) === "number") ? overwrite >> 0 : _overwriteLookup[overwrite];
18350 if ((isSelector || target instanceof Array || (target.push && _isArray(target))) && typeof(target[0]) !== "number") {
18351 this._targets = targets = _slice.call(target, 0);
18352 this._propLookup = [];
18353 this._siblings = [];
18354 for (i = 0; i < targets.length; i++) {
18357 targets.splice(i--, 1);
18359 } else if (typeof(targ) === "string") {
18360 targ = targets[i--] = TweenLite.selector(targ); //in case it's an array of strings
18361 if (typeof(targ) === "string") {
18362 targets.splice(i+1, 1); //to avoid an endless loop (can't imagine why the selector would return a string, but just in case)
18365 } 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.
18366 targets.splice(i--, 1);
18367 this._targets = targets = targets.concat(_slice.call(targ, 0));
18370 this._siblings[i] = _register(targ, this, false);
18371 if (overwrite === 1) if (this._siblings[i].length > 1) {
18372 _applyOverwrite(targ, this, null, 1, this._siblings[i]);
18377 this._propLookup = {};
18378 this._siblings = _register(target, this, false);
18379 if (overwrite === 1) if (this._siblings.length > 1) {
18380 _applyOverwrite(target, this, null, 1, this._siblings);
18383 if (this.vars.immediateRender || (duration === 0 && this._delay === 0 && this.vars.immediateRender !== false)) {
18384 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)
18385 this.render(-this._delay);
18388 _isSelector = function(v) {
18389 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.
18391 _autoCSS = function(vars, target) {
18395 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.
18403 p = TweenLite.prototype = new Animation();
18404 p.constructor = TweenLite;
18405 p.kill()._gc = false;
18407 //----TweenLite defaults, overwrite management, and root updates ----------------------------------------------------
18410 p._firstPT = p._targets = p._overwrittenProps = p._startAt = null;
18411 p._notifyPluginsOfEnabled = p._lazy = false;
18413 TweenLite.version = "1.12.1";
18414 TweenLite.defaultEase = p._ease = new Ease(null, null, 1, 1);
18415 TweenLite.defaultOverwrite = "auto";
18416 TweenLite.ticker = _ticker;
18417 TweenLite.autoSleep = true;
18418 TweenLite.lagSmoothing = function(threshold, adjustedLag) {
18419 _ticker.lagSmoothing(threshold, adjustedLag);
18421 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; };
18423 var _lazyTweens = [],
18425 _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.
18426 _plugins = TweenLite._plugins = {},
18427 _tweenLookup = _internals.tweenLookup = {},
18428 _tweenLookupNum = 0,
18429 _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},
18430 _overwriteLookup = {none:0, all:1, auto:2, concurrent:3, allOnStart:4, preexisting:5, "true":1, "false":0},
18431 _rootFramesTimeline = Animation._rootFramesTimeline = new SimpleTimeline(),
18432 _rootTimeline = Animation._rootTimeline = new SimpleTimeline(),
18433 _lazyRender = function() {
18434 var i = _lazyTweens.length;
18437 a = _lazyTweens[i];
18438 if (a && a._lazy !== false) {
18439 a.render(a._lazy, false, true);
18443 _lazyTweens.length = 0;
18446 _rootTimeline._startTime = _ticker.time;
18447 _rootFramesTimeline._startTime = _ticker.frame;
18448 _rootTimeline._active = _rootFramesTimeline._active = true;
18449 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".
18451 Animation._updateRoot = TweenLite.render = function() {
18453 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.
18456 _rootTimeline.render((_ticker.time - _rootTimeline._startTime) * _rootTimeline._timeScale, false, false);
18457 _rootFramesTimeline.render((_ticker.frame - _rootFramesTimeline._startTime) * _rootFramesTimeline._timeScale, false, false);
18458 if (_lazyTweens.length) {
18461 if (!(_ticker.frame % 120)) { //dump garbage every 120 frames...
18462 for (p in _tweenLookup) {
18463 a = _tweenLookup[p].tweens;
18470 if (a.length === 0) {
18471 delete _tweenLookup[p];
18474 //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
18475 p = _rootTimeline._first;
18476 if (!p || p._paused) if (TweenLite.autoSleep && !_rootFramesTimeline._first && _ticker._listeners.tick.length === 1) {
18477 while (p && p._paused) {
18487 _ticker.addEventListener("tick", Animation._updateRoot);
18489 var _register = function(target, tween, scrub) {
18490 var id = target._gsTweenID, a, i;
18491 if (!_tweenLookup[id || (target._gsTweenID = id = "t" + (_tweenLookupNum++))]) {
18492 _tweenLookup[id] = {target:target, tweens:[]};
18495 a = _tweenLookup[id].tweens;
18496 a[(i = a.length)] = tween;
18499 if (a[i] === tween) {
18505 return _tweenLookup[id].tweens;
18508 _applyOverwrite = function(target, tween, props, mode, siblings) {
18509 var i, changed, curTween, l;
18510 if (mode === 1 || mode >= 4) {
18511 l = siblings.length;
18512 for (i = 0; i < l; i++) {
18513 if ((curTween = siblings[i]) !== tween) {
18514 if (!curTween._gc) if (curTween._enabled(false, false)) {
18517 } else if (mode === 5) {
18523 //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)
18524 var startTime = tween._startTime + _tinyNum,
18527 zeroDur = (tween._duration === 0),
18529 i = siblings.length;
18531 if ((curTween = siblings[i]) === tween || curTween._gc || curTween._paused) {
18533 } else if (curTween._timeline !== tween._timeline) {
18534 globalStart = globalStart || _checkOverlap(tween, 0, zeroDur);
18535 if (_checkOverlap(curTween, globalStart, zeroDur) === 0) {
18536 overlaps[oCount++] = curTween;
18538 } else if (curTween._startTime <= startTime) if (curTween._startTime + curTween.totalDuration() / curTween._timeScale > startTime) if (!((zeroDur || !curTween._initted) && startTime - curTween._startTime <= 0.0000000002)) {
18539 overlaps[oCount++] = curTween;
18545 curTween = overlaps[i];
18546 if (mode === 2) if (curTween._kill(props, target)) {
18549 if (mode !== 2 || (!curTween._firstPT && curTween._initted)) {
18550 if (curTween._enabled(false, false)) { //if all property tweens have been overwritten, kill the tween.
18558 _checkOverlap = function(tween, reference, zeroDur) {
18559 var tl = tween._timeline,
18560 ts = tl._timeScale,
18561 t = tween._startTime;
18562 while (tl._timeline) {
18563 t += tl._startTime;
18564 ts *= tl._timeScale;
18571 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;
18575 //---- TweenLite instance methods -----------------------------------------------------------------------------
18577 p._init = function() {
18579 op = this._overwrittenProps,
18580 dur = this._duration,
18581 immediate = !!v.immediateRender,
18583 i, initPlugins, pt, p, startVars;
18585 if (this._startAt) {
18586 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.
18587 this._startAt.kill();
18590 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);
18591 startVars[p] = v.startAt[p];
18593 startVars.overwrite = false;
18594 startVars.immediateRender = true;
18595 startVars.lazy = (immediate && v.lazy !== false);
18596 startVars.startAt = startVars.delay = null; //no nesting of startAt objects allowed (otherwise it could cause an infinite loop).
18597 this._startAt = TweenLite.to(this.target, 0, startVars);
18599 if (this._time > 0) {
18600 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()).
18601 } else if (dur !== 0) {
18602 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.
18605 } else if (v.runBackwards && dur !== 0) {
18606 //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)
18607 if (this._startAt) {
18608 this._startAt.render(-1, true);
18609 this._startAt.kill();
18610 this._startAt = null;
18613 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.
18614 if (!_reservedProps[p] || p === "autoCSS") {
18619 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.
18620 pt.lazy = (immediate && v.lazy !== false);
18621 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)
18622 this._startAt = TweenLite.to(this.target, 0, pt);
18624 this._startAt._init(); //ensures that the initial values are recorded
18625 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.
18626 } else if (this._time === 0) {
18632 this._ease = TweenLite.defaultEase;
18633 } else if (ease instanceof Ease) {
18634 this._ease = (v.easeParams instanceof Array) ? ease.config.apply(ease, v.easeParams) : ease;
18636 this._ease = (typeof(ease) === "function") ? new Ease(ease, v.easeParams) : _easeMap[ease] || TweenLite.defaultEase;
18638 this._easeType = this._ease._type;
18639 this._easePower = this._ease._power;
18640 this._firstPT = null;
18642 if (this._targets) {
18643 i = this._targets.length;
18645 if ( this._initProps( this._targets[i], (this._propLookup[i] = {}), this._siblings[i], (op ? op[i] : null)) ) {
18646 initPlugins = true;
18650 initPlugins = this._initProps(this.target, this._propLookup, this._siblings, op);
18654 TweenLite._onPluginEvent("_onInitAllProps", this); //reorders the array in order of priority. Uses a static TweenPlugin method in order to minimize file size in TweenLite
18656 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.
18657 this._enabled(false, false);
18659 if (v.runBackwards) {
18660 pt = this._firstPT;
18667 this._onUpdate = v.onUpdate;
18668 this._initted = true;
18671 p._initProps = function(target, propLookup, siblings, overwrittenProps) {
18672 var p, i, initPlugins, plugin, pt, v;
18673 if (target == null) {
18677 if (_lazyLookup[target._gsTweenID]) {
18678 _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)
18681 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.
18682 _autoCSS(this.vars, target);
18684 for (p in this.vars) {
18686 if (_reservedProps[p]) {
18687 if (v) if ((v instanceof Array) || (v.push && _isArray(v))) if (v.join("").indexOf("{self}") !== -1) {
18688 this.vars[p] = v = this._swapSelfInParams(v, this);
18691 } else if (_plugins[p] && (plugin = new _plugins[p]())._onInitTween(target, this.vars[p], this)) {
18693 //t - target [object]
18694 //p - property [string]
18695 //s - start [number]
18696 //c - change [number]
18697 //f - isFunction [boolean]
18698 //n - name [string]
18699 //pg - isPlugin [boolean]
18700 //pr - priority [number]
18701 this._firstPT = pt = {_next:this._firstPT, t:plugin, p:"setRatio", s:0, c:1, f:true, n:p, pg:true, pr:plugin._priority};
18702 i = plugin._overwriteProps.length;
18704 propLookup[plugin._overwriteProps[i]] = this._firstPT;
18706 if (plugin._priority || plugin._onInitAllProps) {
18707 initPlugins = true;
18709 if (plugin._onDisable || plugin._onEnable) {
18710 this._notifyPluginsOfEnabled = true;
18714 this._firstPT = propLookup[p] = pt = {_next:this._firstPT, t:target, p:p, f:(typeof(target[p]) === "function"), n:p, pg:false, pr:0};
18715 pt.s = (!pt.f) ? parseFloat(target[p]) : target[ ((p.indexOf("set") || typeof(target["get" + p.substr(3)]) !== "function") ? p : "get" + p.substr(3)) ]();
18716 pt.c = (typeof(v) === "string" && v.charAt(1) === "=") ? parseInt(v.charAt(0) + "1", 10) * Number(v.substr(2)) : (Number(v) - pt.s) || 0;
18718 if (pt) if (pt._next) {
18719 pt._next._prev = pt;
18723 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)
18724 return this._initProps(target, propLookup, siblings, overwrittenProps);
18726 if (this._overwrite > 1) if (this._firstPT) if (siblings.length > 1) if (_applyOverwrite(target, this, propLookup, this._overwrite, siblings)) {
18727 this._kill(propLookup, target);
18728 return this._initProps(target, propLookup, siblings, overwrittenProps);
18730 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.
18731 _lazyLookup[target._gsTweenID] = true;
18733 return initPlugins;
18736 p.render = function(time, suppressEvents, force) {
18737 var prevTime = this._time,
18738 duration = this._duration,
18739 prevRawPrevTime = this._rawPrevTime,
18740 isComplete, callback, pt, rawPrevTime;
18741 if (time >= duration) {
18742 this._totalTime = this._time = duration;
18743 this.ratio = this._ease._calcEnd ? this._ease.getRatio(1) : 1;
18744 if (!this._reversed ) {
18746 callback = "onComplete";
18748 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.
18749 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.
18752 if (time === 0 || prevRawPrevTime < 0 || prevRawPrevTime === _tinyNum) if (prevRawPrevTime !== time) {
18754 if (prevRawPrevTime > _tinyNum) {
18755 callback = "onReverseComplete";
18758 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.
18761 } else if (time < 0.0000001) { //to work around occasional floating point math artifacts, round super small values to 0.
18762 this._totalTime = this._time = 0;
18763 this.ratio = this._ease._calcEnd ? this._ease.getRatio(0) : 0;
18764 if (prevTime !== 0 || (duration === 0 && prevRawPrevTime > 0 && prevRawPrevTime !== _tinyNum)) {
18765 callback = "onReverseComplete";
18766 isComplete = this._reversed;
18769 this._active = false;
18770 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.
18771 if (prevRawPrevTime >= 0) {
18774 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.
18776 } 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.
18780 this._totalTime = this._time = time;
18782 if (this._easeType) {
18783 var r = time / duration, type = this._easeType, pow = this._easePower;
18784 if (type === 1 || (type === 3 && r >= 0.5)) {
18792 } else if (pow === 2) {
18794 } else if (pow === 3) {
18796 } else if (pow === 4) {
18797 r *= r * r * r * r;
18801 this.ratio = 1 - r;
18802 } else if (type === 2) {
18804 } else if (time / duration < 0.5) {
18805 this.ratio = r / 2;
18807 this.ratio = 1 - (r / 2);
18811 this.ratio = this._ease.getRatio(time / duration);
18815 if (this._time === prevTime && !force) {
18817 } else if (!this._initted) {
18819 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.
18821 } else if (!force && this._firstPT && ((this.vars.lazy !== false && this._duration) || (this.vars.lazy && !this._duration))) {
18822 this._time = this._totalTime = prevTime;
18823 this._rawPrevTime = prevRawPrevTime;
18824 _lazyTweens.push(this);
18828 //_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.
18829 if (this._time && !isComplete) {
18830 this.ratio = this._ease.getRatio(this._time / duration);
18831 } else if (isComplete && this._ease._calcEnd) {
18832 this.ratio = this._ease.getRatio((this._time === 0) ? 0 : 1);
18835 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.
18836 this._lazy = false;
18838 if (!this._active) if (!this._paused && this._time !== prevTime && time >= 0) {
18839 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.
18841 if (prevTime === 0) {
18842 if (this._startAt) {
18844 this._startAt.render(time, suppressEvents, force);
18845 } else if (!callback) {
18846 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.
18849 if (this.vars.onStart) if (this._time !== 0 || duration === 0) if (!suppressEvents) {
18850 this.vars.onStart.apply(this.vars.onStartScope || this, this.vars.onStartParams || _blankArray);
18854 pt = this._firstPT;
18857 pt.t[pt.p](pt.c * this.ratio + pt.s);
18859 pt.t[pt.p] = pt.c * this.ratio + pt.s;
18864 if (this._onUpdate) {
18865 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.
18866 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.
18868 if (!suppressEvents) if (this._time !== prevTime || isComplete) {
18869 this._onUpdate.apply(this.vars.onUpdateScope || this, this.vars.onUpdateParams || _blankArray);
18873 if (callback) if (!this._gc) { //check _gc because there's a chance that kill() could be called in an onUpdate
18874 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.
18875 this._startAt.render(time, suppressEvents, force);
18878 if (this._timeline.autoRemoveChildren) {
18879 this._enabled(false, false);
18881 this._active = false;
18883 if (!suppressEvents && this.vars[callback]) {
18884 this.vars[callback].apply(this.vars[callback + "Scope"] || this, this.vars[callback + "Params"] || _blankArray);
18886 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.
18887 this._rawPrevTime = 0;
18893 p._kill = function(vars, target) {
18894 if (vars === "all") {
18897 if (vars == null) if (target == null || target === this.target) {
18898 this._lazy = false;
18899 return this._enabled(false, false);
18901 target = (typeof(target) !== "string") ? (target || this._targets || this.target) : TweenLite.selector(target) || target;
18902 var i, overwrittenProps, p, pt, propLookup, changed, killProps, record;
18903 if ((_isArray(target) || _isSelector(target)) && typeof(target[0]) !== "number") {
18906 if (this._kill(vars, target[i])) {
18911 if (this._targets) {
18912 i = this._targets.length;
18914 if (target === this._targets[i]) {
18915 propLookup = this._propLookup[i] || {};
18916 this._overwrittenProps = this._overwrittenProps || [];
18917 overwrittenProps = this._overwrittenProps[i] = vars ? this._overwrittenProps[i] || {} : "all";
18921 } else if (target !== this.target) {
18924 propLookup = this._propLookup;
18925 overwrittenProps = this._overwrittenProps = vars ? this._overwrittenProps || {} : "all";
18929 killProps = vars || propLookup;
18930 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)
18931 for (p in killProps) {
18932 if ((pt = propLookup[p])) {
18933 if (pt.pg && pt.t._kill(killProps)) {
18934 changed = true; //some plugins need to be notified so they can perform cleanup tasks first
18936 if (!pt.pg || pt.t._overwriteProps.length === 0) {
18938 pt._prev._next = pt._next;
18939 } else if (pt === this._firstPT) {
18940 this._firstPT = pt._next;
18943 pt._next._prev = pt._prev;
18945 pt._next = pt._prev = null;
18947 delete propLookup[p];
18950 overwrittenProps[p] = 1;
18953 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.
18954 this._enabled(false, false);
18961 p.invalidate = function() {
18962 if (this._notifyPluginsOfEnabled) {
18963 TweenLite._onPluginEvent("_onDisable", this);
18965 this._firstPT = null;
18966 this._overwrittenProps = null;
18967 this._onUpdate = null;
18968 this._startAt = null;
18969 this._initted = this._active = this._notifyPluginsOfEnabled = this._lazy = false;
18970 this._propLookup = (this._targets) ? {} : [];
18974 p._enabled = function(enabled, ignoreTimeline) {
18975 if (!_tickerActive) {
18978 if (enabled && this._gc) {
18979 var targets = this._targets,
18982 i = targets.length;
18984 this._siblings[i] = _register(targets[i], this, true);
18987 this._siblings = _register(this.target, this, true);
18990 Animation.prototype._enabled.call(this, enabled, ignoreTimeline);
18991 if (this._notifyPluginsOfEnabled) if (this._firstPT) {
18992 return TweenLite._onPluginEvent((enabled ? "_onEnable" : "_onDisable"), this);
18998 //----TweenLite static methods -----------------------------------------------------
19000 TweenLite.to = function(target, duration, vars) {
19001 return new TweenLite(target, duration, vars);
19004 TweenLite.from = function(target, duration, vars) {
19005 vars.runBackwards = true;
19006 vars.immediateRender = (vars.immediateRender != false);
19007 return new TweenLite(target, duration, vars);
19010 TweenLite.fromTo = function(target, duration, fromVars, toVars) {
19011 toVars.startAt = fromVars;
19012 toVars.immediateRender = (toVars.immediateRender != false && fromVars.immediateRender != false);
19013 return new TweenLite(target, duration, toVars);
19016 TweenLite.delayedCall = function(delay, callback, params, scope, useFrames) {
19017 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});
19020 TweenLite.set = function(target, vars) {
19021 return new TweenLite(target, 0, vars);
19024 TweenLite.getTweensOf = function(target, onlyActive) {
19025 if (target == null) { return []; }
19026 target = (typeof(target) !== "string") ? target : TweenLite.selector(target) || target;
19028 if ((_isArray(target) || _isSelector(target)) && typeof(target[0]) !== "number") {
19032 a = a.concat(TweenLite.getTweensOf(target[i], onlyActive));
19035 //now get rid of any duplicates (tweens of arrays of objects could cause duplicates)
19046 a = _register(target).concat();
19049 if (a[i]._gc || (onlyActive && !a[i].isActive())) {
19057 TweenLite.killTweensOf = TweenLite.killDelayedCallsTo = function(target, onlyActive, vars) {
19058 if (typeof(onlyActive) === "object") {
19059 vars = onlyActive; //for backwards compatibility (before "onlyActive" parameter was inserted)
19060 onlyActive = false;
19062 var a = TweenLite.getTweensOf(target, onlyActive),
19065 a[i]._kill(vars, target);
19072 * ----------------------------------------------------------------
19073 * 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)
19074 * ----------------------------------------------------------------
19076 var TweenPlugin = _class("plugins.TweenPlugin", function(props, priority) {
19077 this._overwriteProps = (props || "").split(",");
19078 this._propName = this._overwriteProps[0];
19079 this._priority = priority || 0;
19080 this._super = TweenPlugin.prototype;
19083 p = TweenPlugin.prototype;
19084 TweenPlugin.version = "1.10.1";
19085 TweenPlugin.API = 2;
19088 p._addTween = function(target, prop, start, end, overwriteProp, round) {
19090 if (end != null && (c = (typeof(end) === "number" || end.charAt(1) !== "=") ? Number(end) - start : parseInt(end.charAt(0) + "1", 10) * Number(end.substr(2)))) {
19091 this._firstPT = pt = {_next:this._firstPT, t:target, p:prop, s:start, c:c, f:(typeof(target[prop]) === "function"), n:overwriteProp || prop, r:round};
19093 pt._next._prev = pt;
19099 p.setRatio = function(v) {
19100 var pt = this._firstPT,
19104 val = pt.c * v + pt.s;
19106 val = Math.round(val);
19107 } else if (val < min) if (val > -min) { //prevents issues with converting very small numbers to strings in the browser
19119 p._kill = function(lookup) {
19120 var a = this._overwriteProps,
19121 pt = this._firstPT,
19123 if (lookup[this._propName] != null) {
19124 this._overwriteProps = [];
19128 if (lookup[a[i]] != null) {
19134 if (lookup[pt.n] != null) {
19136 pt._next._prev = pt._prev;
19139 pt._prev._next = pt._next;
19141 } else if (this._firstPT === pt) {
19142 this._firstPT = pt._next;
19150 p._roundProps = function(lookup, value) {
19151 var pt = this._firstPT;
19153 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.
19160 TweenLite._onPluginEvent = function(type, tween) {
19161 var pt = tween._firstPT,
19162 changed, pt2, first, last, next;
19163 if (type === "_onInitAllProps") {
19164 //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.
19168 while (pt2 && pt2.pr > pt.pr) {
19171 if ((pt._prev = pt2 ? pt2._prev : last)) {
19172 pt._prev._next = pt;
19176 if ((pt._next = pt2)) {
19183 pt = tween._firstPT = first;
19186 if (pt.pg) if (typeof(pt.t[type]) === "function") if (pt.t[type]()) {
19194 TweenPlugin.activate = function(plugins) {
19195 var i = plugins.length;
19197 if (plugins[i].API === TweenPlugin.API) {
19198 _plugins[(new plugins[i]())._propName] = plugins[i];
19204 //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.
19205 _gsDefine.plugin = function(config) {
19206 if (!config || !config.propName || !config.init || !config.API) { throw "illegal plugin definition."; }
19207 var propName = config.propName,
19208 priority = config.priority || 0,
19209 overwriteProps = config.overwriteProps,
19210 map = {init:"_onInitTween", set:"setRatio", kill:"_kill", round:"_roundProps", initAll:"_onInitAllProps"},
19211 Plugin = _class("plugins." + propName.charAt(0).toUpperCase() + propName.substr(1) + "Plugin",
19213 TweenPlugin.call(this, propName, priority);
19214 this._overwriteProps = overwriteProps || [];
19215 }, (config.global === true)),
19216 p = Plugin.prototype = new TweenPlugin(propName),
19218 p.constructor = Plugin;
19219 Plugin.API = config.API;
19220 for (prop in map) {
19221 if (typeof(config[prop]) === "function") {
19222 p[map[prop]] = config[prop];
19225 Plugin.version = config.version;
19226 TweenPlugin.activate([Plugin]);
19231 //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.
19232 a = window._gsQueue;
19234 for (i = 0; i < a.length; i++) {
19237 for (p in _defLookup) {
19238 if (!_defLookup[p].func) {
19239 //window.console.log("GSAP encountered missing dependency: com.greensock." + p);
19244 _tickerActive = false; //ensures that the first official animation forces a ticker.tick() to update the time when it is instantiated
19248 angular.module('b2b.att.collapse', ['b2b.att.transition'])
19250 // The collapsible directive indicates a block of html that will expand and collapse
19251 .directive('b2bCollapse', ['$transition', function($transition) {
19252 // CSS transitions don't work with height: auto, so we have to manually change the height to a
19253 // specific value and then once the animation completes, we can reset the height to auto.
19254 // Unfortunately if you do this while the CSS transitions are specified (i.e. in the CSS class
19255 // "collapse") then you trigger a change to height 0 in between.
19256 // The fix is to remove the "collapse" CSS class while changing the height back to auto - phew!
19261 marginBottom: null,
19263 paddingBottom: null,
19275 var fixUpHeight = function(scope, element, height) {
19276 // We remove the collapse CSS class to prevent a transition when we change to height: auto
19277 element.removeClass('b2bCollapse');
19278 element.css({height: height});
19279 //adjusting for any margin or padding
19280 if (height === 0) {
19281 element.css(props.closed);
19283 element.css(props.open);
19285 // It appears that reading offsetWidth makes the browser realise that we have changed the
19286 // height already :-/
19287 var x = element[0].offsetWidth;
19288 element.addClass('b2bCollapse');
19292 link: function(scope, element, attrs) {
19294 var initialAnimSkip = true;
19295 scope.$watch(function() {
19296 return element[0].scrollHeight;
19297 }, function(value) {
19298 //The listener is called when scrollHeight changes
19299 //It actually does on 2 scenarios:
19300 // 1. Parent is set to display none
19301 // 2. angular bindings inside are resolved
19302 //When we have a change of scrollHeight we are setting again the correct height if the group is opened
19303 if (element[0].scrollHeight !== 0) {
19304 if (!isCollapsed) {
19305 if (initialAnimSkip) {
19306 fixUpHeight(scope, element, element[0].scrollHeight + 'px');
19308 fixUpHeight(scope, element, 'auto');
19309 element.css({overflow: 'visible'});
19315 scope.$watch(attrs.b2bCollapse, function(value) {
19324 var currentTransition;
19325 var doTransition = function(change) {
19326 if (currentTransition) {
19327 currentTransition.cancel();
19329 currentTransition = $transition(element, change);
19330 currentTransition.then(
19332 currentTransition = undefined;
19335 currentTransition = undefined;
19338 return currentTransition;
19341 var expand = function() {
19342 scope.postTransition = true;
19343 if (initialAnimSkip) {
19344 initialAnimSkip = false;
19345 if (!isCollapsed) {
19346 fixUpHeight(scope, element, 'auto');
19349 //doTransition({ height : element[0].scrollHeight + 'px' })
19350 doTransition(angular.extend({height: element[0].scrollHeight + 'px'}, props.open))
19352 // This check ensures that we don't accidentally update the height if the user has closed
19353 // the group while the animation was still running
19354 if (!isCollapsed) {
19355 fixUpHeight(scope, element, 'auto');
19359 isCollapsed = false;
19362 var collapse = function() {
19363 isCollapsed = true;
19364 if (initialAnimSkip) {
19365 initialAnimSkip = false;
19366 fixUpHeight(scope, element, 0);
19368 fixUpHeight(scope, element, element[0].scrollHeight + 'px');
19369 doTransition(angular.extend({height: 0}, props.closed)).then(function() {
19370 scope.postTransition = false;
19372 element.css({overflow: 'hidden'});
19378 angular.module('b2b.att.position', [])
19380 .factory('$position', ['$document', '$window', function ($document, $window) {
19381 function getStyle(el, cssprop) {
19382 if (el.currentStyle) { //IE
19383 return el.currentStyle[cssprop];
19384 } else if ($window.getComputedStyle) {
19385 return $window.getComputedStyle(el)[cssprop];
19387 // finally try and get inline style
19388 return el.style[cssprop];
19392 * Checks if a given element is statically positioned
19393 * @param element - raw DOM element
19395 function isStaticPositioned(element) {
19396 return (getStyle(element, "position") || 'static') === 'static';
19400 * returns the closest, non-statically positioned parentOffset of a given element
19403 var parentOffsetEl = function (element) {
19404 var docDomEl = $document[0];
19405 var offsetParent = element.offsetParent || docDomEl;
19406 while (offsetParent && offsetParent !== docDomEl && isStaticPositioned(offsetParent)) {
19407 offsetParent = offsetParent.offsetParent;
19409 return offsetParent || docDomEl;
19414 * Provides read-only equivalent of jQuery's position function:
19415 * http://api.jquery.com/position/
19417 position: function (element) {
19418 var elBCR = this.offset(element);
19419 var offsetParentBCR = {
19423 var offsetParentEl = parentOffsetEl(element[0]);
19424 if (offsetParentEl !== $document[0]) {
19425 offsetParentBCR = this.offset(angular.element(offsetParentEl));
19426 offsetParentBCR.top += offsetParentEl.clientTop - offsetParentEl.scrollTop;
19427 offsetParentBCR.left += offsetParentEl.clientLeft - offsetParentEl.scrollLeft;
19431 width: element.prop('offsetWidth'),
19432 height: element.prop('offsetHeight'),
19433 top: elBCR.top - offsetParentBCR.top,
19434 left: elBCR.left - offsetParentBCR.left
19439 * Provides read-only equivalent of jQuery's offset function:
19440 * http://api.jquery.com/offset/
19442 offset: function (element) {
19443 var boundingClientRect = element[0].getBoundingClientRect();
19445 width: element.prop('offsetWidth'),
19446 height: element.prop('offsetHeight'),
19447 top: boundingClientRect.top + ($window.pageYOffset || $document[0].body.scrollTop || $document[0].documentElement.scrollTop),
19448 left: boundingClientRect.left + ($window.pageXOffset || $document[0].body.scrollLeft || $document[0].documentElement.scrollLeft)
19453 * Provides functionality to check whether an element is in view port.
19455 isElementInViewport: function (element) {
19457 var rect = element[0].getBoundingClientRect();
19461 rect.bottom <= ($window.innerHeight || $document[0].documentElement.clientHeight) &&
19462 rect.right <= ($window.innerWidth || $document[0].documentElement.clientWidth)
19471 .factory('$isElement', [function () {
19472 var isElement = function (currentElem, targetElem, alternateElem) {
19473 if (currentElem[0] === targetElem[0]) {
19475 } else if (currentElem[0] === alternateElem[0]) {
19478 return isElement((currentElem.parent()[0] && currentElem.parent()) || targetElem, targetElem, alternateElem);
19485 .directive('attPosition', ['$position', function ($position) {
19488 link: function (scope, elem, attr) {
19489 scope.$watchCollection(function () {
19490 return $position.position(elem);
19491 }, function (value) {
19492 scope[attr.attPosition] = value;
19498 angular.module('b2b.att.transition', [])
19500 .factory('$transition', ['$q', '$timeout', '$rootScope', function($q, $timeout, $rootScope) {
19502 var $transition = function(element, trigger, options) {
19503 options = options || {};
19504 var deferred = $q.defer();
19505 var endEventName = $transition[options.animation ? "animationEndEventName" : "transitionEndEventName"];
19507 var transitionEndHandler = function() {
19508 $rootScope.$apply(function() {
19509 element.unbind(endEventName, transitionEndHandler);
19510 deferred.resolve(element);
19514 if (endEventName) {
19515 element.bind(endEventName, transitionEndHandler);
19518 // Wrap in a timeout to allow the browser time to update the DOM before the transition is to occur
19519 $timeout(function() {
19520 if ( angular.isString(trigger) ) {
19521 element.addClass(trigger);
19522 } else if ( angular.isFunction(trigger) ) {
19524 } else if ( angular.isObject(trigger) ) {
19525 element.css(trigger);
19527 //If browser does not support transitions, instantly resolve
19528 if ( !endEventName ) {
19529 deferred.resolve(element);
19533 // Add our custom cancel function to the promise that is returned
19534 // We can call this if we are about to run a new transition, which we know will prevent this transition from ending,
19535 // i.e. it will therefore never raise a transitionEnd event for that transition
19536 deferred.promise.cancel = function() {
19537 if ( endEventName ) {
19538 element.unbind(endEventName, transitionEndHandler);
19540 deferred.reject('Transition cancelled');
19543 return deferred.promise;
19546 // Work out the name of the transitionEnd event
19547 var transElement = document.createElement('trans');
19548 var transitionEndEventNames = {
19549 'WebkitTransition': 'webkitTransitionEnd',
19550 'MozTransition': 'transitionend',
19551 'OTransition': 'oTransitionEnd',
19552 'transition': 'transitionend'
19554 var animationEndEventNames = {
19555 'WebkitTransition': 'webkitAnimationEnd',
19556 'MozTransition': 'animationend',
19557 'OTransition': 'oAnimationEnd',
19558 'transition': 'animationend'
19560 function findEndEventName(endEventNames) {
19561 for (var name in endEventNames){
19562 if (transElement.style[name] !== undefined) {
19563 return endEventNames[name];
19567 $transition.transitionEndEventName = findEndEventName(transitionEndEventNames);
19568 $transition.animationEndEventName = findEndEventName(animationEndEventNames);
19569 return $transition;
19572 .factory('$scrollTo', ['$window', function($window) {
19573 var $scrollTo = function(offsetLeft, offsetTop, duration) {
19574 TweenMax.to($window, duration || 1, {scrollTo: {y: offsetTop, x: offsetLeft}, ease: Power4.easeOut});
19578 .factory('animation', function(){
19581 .factory('$progressBar', function(){
19583 //Provides a function to pass in code for closure purposes
19584 var loadingAnimationCreator = function(onUpdateCallback){
19586 //Use closure to setup some resuable code
19587 var loadingAnimation = function(callback, duration){
19588 TweenMax.to({}, duration, {
19589 onUpdateParams: ["{self}"],
19590 onUpdate: onUpdateCallback,
19591 onComplete: callback
19594 //Returns a function that takes a callback function and a duration for the animation
19595 return (function(){
19596 return loadingAnimation;
19600 return loadingAnimationCreator;
19602 .factory('$height', function(){
19603 var heightAnimation = function(element,duration,height,alpha){
19604 TweenMax.to(element,
19606 {height:height, autoAlpha:alpha},
19609 return heightAnimation;
19611 angular.module('b2b.att.utilities', ['ngSanitize'])
19612 .constant('b2bUtilitiesConfig', {
19619 enableSearch: false,
19621 circularTraversal: false
19623 .constant('b2bWhenScrollEndsConstants', {
19628 // All breakpoints ranges from >= min and < max
19629 .constant('b2bAwdBreakpoints', {
19645 .filter('groupBy', function ($timeout) {
19646 //Custom GroupBy Filter for treeNav, returns key string and value.childarray as set of grouped elements
19647 return function (data, key) {
19648 if (!key) return data;
19649 var outputPropertyName = '__groupBy__' + key;
19650 if (!data[outputPropertyName]) {
19652 for (var i = 0; i < data.length; i++) {
19653 if (!result[data[i][key]])
19654 result[data[i][key]] = {};
19655 if (!result[data[i][key]].childArray) {
19656 result[data[i][key]].childArray = [];
19658 result[data[i][key]].childArray.push(data[i]);
19659 if (data[i].activeGrp && data[i].activeGrp == true) {
19660 result[data[i][key]].showGroup = true;
19663 Object.defineProperty(result, 'length', {enumerable: false,value: Object.keys(result).length});
19664 Object.defineProperty(data, outputPropertyName, {enumerable: false,configurable: true,writable:false,value:result});
19665 $timeout(function(){delete data[outputPropertyName];},0,false);
19667 return data[outputPropertyName];
19670 .filter('searchObjectPropertiesFilter', [function() {
19671 return function(items, searchText, attrs) {
19676 searchText = searchText.toLowerCase();
19677 angular.forEach(items, function(item) {
19678 angular.forEach(attrs, function(attr) {
19679 if (item.hasOwnProperty(attr) && (item[attr].toLowerCase().indexOf(searchText) != -1)) {
19680 filtered.push(item);
19688 .filter('unsafe',[ '$sce', function ($sce) {
19689 return function(val){
19690 return $sce.trustAsHtml(val);
19693 .filter('b2bHighlight', function () {
19694 function escapeRegexp(queryToEscape) {
19695 return queryToEscape.replace(/([.?*+^$[\]\\(){}|-])/g, '\\$1');
19697 return function (matchItem, query, className) {
19698 return query && matchItem ? matchItem.replace(new RegExp(escapeRegexp(query), 'gi'), '<span class=\"' + className + '\">$&</span>') : matchItem;
19702 Copyright © 2013 Matt Diamond
19703 https://github.com/cwilso/AudioRecorder/blob/master/js/recorderjs/recorder.js
19705 .factory('b2bRecorder', function() {
19707 var Recorder = function(source, cfg) {
19708 var WORKER_PATH = 'recorderWorker.js';
19709 var config = cfg || {};
19710 var bufferLen = config.bufferLen || 4096;
19711 this.context = source.context;
19712 if(!this.context.createScriptProcessor) {
19713 this.node = this.context.createJavacriptProcessor(bufferLen, 2, 2);
19715 this.node = this.context.createScriptProcessor(bufferLen, 2, 2);
19717 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()}};';
19718 var blob = new Blob([workerCode]);
19720 var worker = new Worker(window.URL.createObjectURL(blob)); //TODO: Use a blob instead
19721 worker.postMessage({
19724 sampleRate: this.context.sampleRate
19727 var recording = false,
19730 this.node.onaudioprocess = function(e) {
19731 if (!recording) return;
19732 worker.postMessage({
19735 e.inputBuffer.getChannelData(0),
19736 e.inputBuffer.getChannelData(1)
19741 this.configure = function(cfg) {
19742 for (var prop in cfg) {//TODO: look into using angular.extend() here
19743 if (cfg.hasOwnProperty(prop)) {
19744 config[prop] = cfg[prop];
19749 this.record = function() {
19753 this.stop = function() {
19757 this.clear = function() {
19758 worker.postMessage({ command: 'clear' });
19759 window.URL.revokeObjectURL(blob);
19762 this.getBuffers = function(cb) {
19763 currCallback = cb || config.callback;
19764 worker.postMessage({ command: 'getBuffers' });
19767 this.exportWAV = function(cb, type) {
19768 currCallback = cb || config.callback;
19769 type = type || config.type || 'audio/wav';
19770 if (!currCallback) throw new Error('[b2bRecorder]: Callback not set!');
19771 worker.postMessage({
19772 command: 'exportWAV',
19777 this.exportMonoWAV = function(cb, type) {
19778 currCallback = cb || config.callback;
19779 type = type || config.type || 'audio/wav';
19780 if (!currCallback) throw new Error('[b2bRecorder]: Callback not set!');
19781 worker.postMessage({
19782 command: 'exportMonoWAV',
19787 worker.onmessage = function(e) {
19789 currCallback(blob);
19792 source.connect(this.node);
19793 this.node.connect(this.context.destination); // if the script node is not connected to an output the "onaudioprocess" event is not triggerd in Chrome
19800 .factory('b2bViewport', function() {
19801 /* Source: https://gist.github.com/bjankord/2399828 */
19802 var _viewportWidth = function() {
19804 var webkit = (!(window.webkitConvertPointFromNodeToPage == null));
19808 var vpwtest = document.createElement( "div" );
19809 // Sets test div to width 100%, !important overrides any other misc. box model styles that may be set in the CSS
19810 vpwtest.style.cssText = "width:100% !important; margin:0 !important; padding:0 !important; border:none !important;";
19811 document.documentElement.insertBefore( vpwtest, document.documentElement.firstChild );
19812 vpw = vpwtest.offsetWidth;
19813 document.documentElement.removeChild( vpwtest );
19816 else if ( window.innerWidth === undefined ) {
19817 vpw = document.documentElement.clientWidth;
19821 vpw = window.innerWidth;
19827 viewportWidth: _viewportWidth
19830 .directive('b2bWhenScrollEnds', function(b2bWhenScrollEndsConstants, $log) {
19833 link: function (scope, element, attrs) {
19835 * Exposed Attributes:
19836 * threshold - integer - number of pixels before scrollbar hits end that callback is called
19837 * width - integer - override for element's width (px)
19838 * height - integer - override for element's height (px)
19839 * axis - string - x/y for scroll bar axis
19841 var threshold = parseInt(attrs.threshold, 10) || b2bWhenScrollEndsConstants.threshold;
19843 if (!attrs.axis || attrs.axis === '') {
19844 $log.warn('axis attribute must be defined for b2bWhenScrollEnds.');
19848 if (attrs.axis === 'x') {
19849 visibleWidth = parseInt(attrs.width, 10) || b2bWhenScrollEndsConstants.width;
19850 if (element.css('width')) {
19851 visibleWidth = element.css('width').split('px')[0];
19854 element[0].addEventListener('scroll', function() {
19855 var scrollableWidth = element.prop('scrollWidth');
19856 if (scrollableWidth === undefined) {
19857 scrollableWidth = 1;
19859 var hiddenContentWidth = scrollableWidth - visibleWidth;
19861 if (hiddenContentWidth - element[0].scrollLeft <= threshold) {
19862 /* Scroll almost at bottom, load more rows */
19863 scope.$apply(attrs.b2bWhenScrollEnds);
19866 } else if (attrs.axis === 'y') {
19867 visibleHeight = parseInt(attrs.height, 10) || b2bWhenScrollEndsConstants.height;
19868 if (element.css('width')) {
19869 visibleHeight = element.css('height').split('px')[0];
19872 element[0].addEventListener('scroll', function() {
19873 var scrollableHeight = element.prop('scrollHeight');
19874 if (scrollableHeight === undefined) {
19875 scrollableHeight = 1;
19877 var hiddenContentHeight = scrollableHeight - visibleHeight;
19879 if (hiddenContentHeight - element[0].scrollTop <= threshold) {
19880 /* Scroll almost at bottom, load more rows */
19881 scope.$apply(attrs.b2bWhenScrollEnds);
19889 .factory('$windowBind', ['$window', '$timeout', function($window, $timeout) {
19890 var win = angular.element($window);
19891 var _scroll = function (flag, callbackFunc, scope) {
19892 scope.$watch(flag, function (val) {
19893 $timeout(function () {
19895 win.bind('scroll', callbackFunc);
19897 win.unbind('scroll', callbackFunc);
19903 var throttle = function(type, name, obj) {
19904 obj = obj || window;
19905 var running = false;
19906 var func = function() {
19907 if (running) { return; }
19909 requestAnimationFrame(function() {
19910 obj.dispatchEvent(new CustomEvent(name));
19914 obj.addEventListener(type, func);
19917 var _resize = function(callbackFunc, scope) {
19918 throttle("resize", "optimizedResize");
19919 window.addEventListener("optimizedResize", function(event) {
19921 //win.bind(event, callbackFunc);
19922 if (!scope.$$phase) {
19928 var _click = function (flag, callbackFunc, scope) {
19929 scope.$watch(flag, function (val) {
19930 $timeout(function () {
19932 win.bind('click', callbackFunc);
19934 win.unbind('click', callbackFunc);
19940 var _event = function (event, flag, callbackFunc, scope, timeoutFlag, timeoutValue) {
19942 if (!(timeoutValue)) {
19945 scope.$watch(flag, function (newVal, oldVal) {
19946 if (newVal !== oldVal) {
19947 $timeout(function () {
19949 win.bind(event, callbackFunc);
19951 win.unbind(event, callbackFunc);
19957 scope.$watch(flag, function (newVal, oldVal) {
19958 if (newVal !== oldVal) {
19960 win.bind(event, callbackFunc);
19962 win.unbind(event, callbackFunc);
19977 .factory('keymap', function () {
19999 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 : "'"
20001 isControl: function (e) {
20004 case this.KEY.COMMAND:
20005 case this.KEY.SHIFT:
20006 case this.KEY.CTRL:
20018 isFunctionKey: function (k) {
20019 k = k.keyCode ? k.keyCode : k;
20020 return k >= 112 && k <= 123;
20022 isVerticalMovement: function (k) {
20023 return ~[this.KEY.UP, this.KEY.DOWN].indexOf(k);
20025 isHorizontalMovement: function (k) {
20026 return ~[this.KEY.LEFT, this.KEY.RIGHT, this.KEY.BACKSPACE, this.KEY.DELETE].indexOf(k);
20028 isAllowedKey: function (k) {
20029 return (~[this.KEY.SPACE, this.KEY.ESC, this.KEY.ENTER].indexOf(k)) || this.isHorizontalMovement(k) || this.isVerticalMovement(k);
20031 isNumericKey: function (e) {
20033 if ((k >= 48 && k <= 57) || (k >= 96 && k <= 105)) {
20039 isAlphaNumericKey: function (e) {
20041 if ((k >= 48 && k <= 57) || (k >= 96 && k <= 105) || (k >= 65 && k <= 90)) {
20050 .factory('$isElement', [function () {
20051 var isElement = function (currentElem, targetElem, alternateElem) {
20052 if (currentElem[0] === targetElem[0]) {
20054 } else if (currentElem[0] === alternateElem[0]) {
20057 return isElement((currentElem.parent()[0] && currentElem.parent()) || targetElem, targetElem, alternateElem);
20064 .factory('events', function () {
20065 var _stopPropagation = function (evt) {
20066 if (evt.stopPropagation) {
20067 evt.stopPropagation();
20069 evt.returnValue = false;
20072 var _preventDefault = function (evt) {
20073 if (evt.preventDefault) {
20074 evt.preventDefault();
20076 evt.returnValue = false;
20080 stopPropagation: _stopPropagation,
20081 preventDefault: _preventDefault
20086 .factory('isHighContrast', function () {
20087 var _isHighContrast = function (idval)
20091 var objDiv, objImage, strColor, strWidth, strReady;
20092 var strImageID = idval; // ID of image on the page
20094 // Create a test div
20095 objDiv = document.createElement('div');
20097 //Set its color style to something unusual
20098 objDiv.style.color = 'rgb(31, 41, 59)';
20100 // Attach to body so we can inspect it
20101 document.body.appendChild(objDiv);
20103 // Read computed color value
20104 strColor = document.defaultView ? document.defaultView.getComputedStyle(objDiv, null).color : objDiv.currentStyle.color;
20105 strColor = strColor.replace(/ /g, '');
20107 // Delete the test DIV
20108 document.body.removeChild(objDiv);
20110 // Check if we get the color back that we set. If not, we're in
20111 // high contrast mode.
20112 if (strColor !== 'rgb(31,41,59)') {
20119 return _isHighContrast;
20122 .run(['isHighContrast', '$document', function (isHighContrast, $document) {
20123 var html = $document.find('html').eq(0);
20124 if (isHighContrast()) {
20125 html.addClass('ds2-no-colors');
20127 html.removeClass('ds2-no-colors');
20131 .factory('$documentBind', ['$document', '$timeout', function ($document, $timeout) {
20132 var _click = function (flag, callbackFunc, scope) {
20133 scope.$watch(flag, function (val) {
20134 $timeout(function () {
20136 $document.bind('click', callbackFunc);
20138 $document.unbind('click', callbackFunc);
20144 var _touch = function (flag, callbackFunc, scope) {
20145 scope.$watch(flag, function (val) {
20146 $timeout(function () {
20148 $document.bind('touchstart', callbackFunc);
20150 $document.unbind('touchstart', callbackFunc);
20156 var _scroll = function (flag, callbackFunc, scope) {
20157 scope.$watch(flag, function (val) {
20158 $timeout(function () {
20160 $document.bind('scroll', callbackFunc);
20162 $document.unbind('scroll', callbackFunc);
20168 var _event = function (event, flag, callbackFunc, scope, timeoutFlag, timeoutValue) {
20170 if (!(timeoutValue)) {
20173 scope.$watch(flag, function (newVal, oldVal) {
20174 if (newVal !== oldVal) {
20175 $timeout(function () {
20177 $document.bind(event, callbackFunc);
20179 $document.unbind(event, callbackFunc);
20185 scope.$watch(flag, function (newVal, oldVal) {
20186 if (newVal !== oldVal) {
20188 $document.bind(event, callbackFunc);
20190 $document.unbind(event, callbackFunc);
20205 .directive('b2bOnlyNums', function (keymap) {
20208 require: 'ngModel',
20209 link: function (scope, elm, attrs, ctrl) {
20210 var maxChars = attrs.b2bOnlyNums ? attrs.b2bOnlyNums : 4;
20211 elm.on('keydown', function (event) {
20212 if ((event.which >= 48 && event.which <= 57) || (event.which >= 96 && event.which <= 105)) {
20213 // check for maximum characters allowed
20214 if (elm.val().length < maxChars){
20217 event.preventDefault();
20220 } else if ([8, 9, 13, 27, 37, 38, 39, 40].indexOf(event.which) > -1) {
20221 // to allow backspace, tab, enter, escape, arrows
20223 } else if (event.altKey || event.ctrlKey) {
20224 // to allow alter, control, and shift keys
20228 event.preventDefault();
20233 var validateString = function (value) {
20234 if (angular.isUndefined(value) || value === null || value === '') {
20235 return ctrl.$modelValue;
20239 ctrl.$parsers.unshift(validateString);
20244 .directive('b2bKeyupClick', [ function () {
20247 link: function (scope, elem, attr) {
20249 attr.$observe('b2bKeyupClick', function (value) {
20251 keyCode = value.split(',');
20254 elem.bind('keydown keyup', function (ev) {
20255 var keyCodeCondition = function () {
20257 if (!(ev.keyCode)) {
20259 ev.keyCode = ev.which;
20260 } else if (ev.charCode) {
20261 ev.keyCode = ev.charCode;
20264 if ((ev.keyCode && keyCode.indexOf(ev.keyCode.toString()) > -1)) {
20269 if (ev.type === 'keydown' && keyCodeCondition()) {
20270 ev.preventDefault();
20272 else if (ev.type === 'keyup' && keyCodeCondition()) {
20280 .factory('b2bDOMHelper', function() {
20282 var _isTabable = function(node) {
20283 var element = angular.element(node);
20284 var tagName = element[0].tagName.toUpperCase();
20286 if (isHidden(element)) {
20289 if (element.attr('tabindex') !== undefined) {
20290 return (parseInt(element.attr('tabindex'), 10) >= 0);
20292 if (tagName === 'A' || tagName === 'AREA' || tagName === 'BUTTON' || tagName === 'INPUT' || tagName === 'TEXTAREA' || tagName === 'SELECT') {
20293 if (tagName === 'A' || tagName === 'AREA') {
20294 // anchors/areas without href are not focusable
20295 return (element[0].href !== '');
20297 return !(element[0].disabled);
20302 function isValidChild(child) {
20303 return child.nodeType == 1 && child.nodeName != 'SCRIPT' && child.nodeName != 'STYLE';
20306 function isHidden(obj) {
20307 var elem = angular.element(obj);
20308 var elemStyle = undefined;
20309 if(obj instanceof HTMLElement){
20310 elemStyle = window.getComputedStyle(obj);
20313 elemStyle = window.getComputedStyle(obj[0]);
20315 return elem.hasClass('ng-hide') || elem.css('display') === 'none' || elemStyle.display === 'none' || elemStyle.visibility === 'hidden' || elem.css('visibility') === 'hidden';
20318 function hasValidParent(obj) {
20319 return (isValidChild(obj) && obj.parentElement.nodeName !== 'BODY');
20322 function traverse(obj, fromTop) {
20323 var obj = obj || document.getElementsByTagName('body')[0];
20324 if (isValidChild(obj) && _isTabable(obj)) {
20327 // If object is hidden, skip it's children
20328 if (isValidChild(obj) && isHidden(obj)) {
20331 // If object is hidden, skip it's children
20332 if (angular.element(obj).hasClass('ng-hide')) {
20335 if (obj.hasChildNodes()) {
20338 child = obj.firstChild;
20340 child = obj.lastChild;
20343 var res = traverse(child, fromTop);
20349 child = child.nextSibling;
20351 child = child.previousSibling;
20361 var _previousElement = function(el, isFocusable){
20364 if (el.hasOwnProperty('length')) {
20368 var parent = elem.parentElement;
20369 var previousElem = undefined;
20372 if (hasValidParent(elem)) {
20373 var siblings = angular.element(parent).children();
20374 if (siblings.length > 0) {
20375 // Good practice to splice out the elem from siblings if there, saving some time.
20376 // We allow for a quick check for jumping to parent first before removing.
20377 if (siblings[0] === elem) {
20378 // If we are looking at immidiate parent and elem is first child, we need to go higher
20379 var e = _previousElement(angular.element(elem).parent(), isFocusable);
20380 if (_isTabable(e)) {
20384 // I need to filter myself and any nodes next to me from the siblings
20385 var indexOfElem = Array.prototype.indexOf.call(siblings, elem);
20386 siblings = Array.prototype.filter.call(siblings, function(item, itemIndex) {
20387 if (!angular.equals(elem, item) && itemIndex < indexOfElem) {
20392 // We need to search backwards
20393 for (var i = 0; i <= siblings.length-1; i++) {//for (var i = siblings.length-1; i >= 0; i--) {
20394 var ret = traverse(siblings[i], false);
20395 if (ret !== undefined) {
20400 var e = _previousElement(angular.element(elem).parent(), isFocusable);
20401 if (_isTabable(e)) {
20407 var siblings = angular.element(parent).children();
20408 if (siblings.length > 1) {
20409 // Since indexOf is on Array.prototype and parent.children is a NodeList, we have to use call()
20410 var index = Array.prototype.indexOf.call(siblings, elem);
20411 previousElem = siblings[index-1];
20414 return previousElem;
20417 var _lastTabableElement = function(el) {
20418 /* This will return the first tabable element from the parent el */
20420 if (el.hasOwnProperty('length')) {
20424 return traverse(elem, false);
20427 var _firstTabableElement = function(el) {
20428 /* This will return the first tabable element from the parent el */
20430 if (el.hasOwnProperty('length')) {
20434 return traverse(elem, true);
20437 var _isInDOM = function(obj) {
20438 return document.documentElement.contains(obj);
20442 firstTabableElement: _firstTabableElement,
20443 lastTabableElement: _lastTabableElement,
20444 previousElement: _previousElement,
20446 isTabable: _isTabable,
20451 .factory('windowOrientation', ['$window', function ($window) {
20452 var _isPotrait = function () {
20453 if ($window.innerHeight > $window.innerWidth) {
20459 var _isLandscape = function () {
20460 if ($window.innerHeight < $window.innerWidth) {
20468 isPotrait: _isPotrait,
20469 isLandscape: _isLandscape
20472 .directive('b2bNextElement', function() {
20476 link: function (scope, elem, attr, ctrls) {
20478 var keys = attr.b2bNextElement.split(',');
20480 elem.bind('keydown', function (e) {
20481 var nextElement = elem.next();
20482 if(e.keyCode == 39 || e.keyCode == 40){ // if e.keyCode in keys
20483 if(nextElement.length) {
20484 e.preventDefault();
20485 nextElement[0].focus();
20493 .directive('b2bAccessibilityClick', [function () {
20496 link: function (scope, elem, attr, ctrl) {
20498 attr.$observe('b2bAccessibilityClick', function (value) {
20500 keyCode = value.split(',');
20503 elem.bind('keydown keypress', function (ev) {
20504 var keyCodeCondition = function () {
20506 if (!(ev.keyCode)) {
20508 ev.keyCode = ev.which;
20509 } else if (ev.charCode) {
20510 ev.keyCode = ev.charCode;
20513 if ((ev.keyCode && keyCode.indexOf(ev.keyCode.toString()) > -1)) {
20518 if (keyCode.length > 0 && keyCodeCondition()) {
20520 ev.preventDefault();
20527 .directive('b2bReset', ['$compile', function ($compile) {
20530 require: ['?ngModel', 'b2bReset'],
20531 controller: ['$scope', function ($scope) {
20532 var resetButton = angular.element('<button type="button" class="reset-field" tabindex="-1" aria-label="Click to reset" aria-hidden="true" role="button"></button>');
20534 this.getResetButton = function () {
20535 return resetButton;
20538 link: function (scope, element, attrs, ctrls) {
20540 var ngModelCtrl = ctrls[0];
20541 var ctrl = ctrls[1];
20543 var resetButton = ctrl.getResetButton();
20546 resetButton.on('click', function () {
20547 element[0].value = '';
20550 if (attrs.b2bReset) {
20551 ngModelCtrl.$setViewValue(attrs.b2bReset);
20553 ngModelCtrl.$setViewValue('');
20555 element[0].value = ngModelCtrl.$viewValue;
20556 ngModelCtrl.$render();
20559 element[0].focus();
20560 element[0].select();
20563 var addResetButton = function () {
20564 element.after(resetButton);
20565 element.unbind('focus input', addResetButton);
20568 element.bind('focus input', addResetButton);
20573 .directive('b2bPrevElement', ['b2bDOMHelper', 'keymap', function (b2bDOMHelper, keymap) {
20577 link: function (scope, elem, attr) {
20579 elem.bind('keydown', function (e) {
20580 if(e.keyCode == 37 || e.keyCode == 38){
20581 var prev = b2bDOMHelper.previousElement(elem, false);
20582 if(prev !== undefined) {
20583 e.preventDefault();
20592 * @param {integer} delay - Timeout before first and last focusable elements are found
20593 * @param {boolean} trigger - A variable on scope that will trigger refinding first/last focusable elements
20595 .directive('b2bTrapFocusInsideElement', ['$timeout', 'b2bDOMHelper', 'keymap', 'events', function ($timeout, b2bDOMHelper, keymap, events) {
20599 link: function (scope, elem, attr) {
20601 var delay = parseInt(attr.delay, 10) || 10;
20603 /* Before opening modal, find the focused element */
20604 var firstTabableElement = undefined,
20605 lastTabableElement = undefined;
20608 $timeout(function () {
20609 firstTabableElement = b2bDOMHelper.firstTabableElement(elem);
20610 lastTabableElement = b2bDOMHelper.lastTabableElement(elem);
20611 angular.element(firstTabableElement).bind('keydown', firstTabableElementKeyhandler);
20612 angular.element(lastTabableElement).bind('keydown', lastTabableElementKeyhandler);
20616 if (attr.trigger !== undefined) {
20617 scope.$watch('trigger', function() {
20618 if (scope.trigger) {
20624 var firstTabableElementKeyhandler = function(e) {
20626 e.keyCode = e.which;
20628 if (e.keyCode === keymap.KEY.TAB && e.shiftKey) {
20629 if (attr.trapFocusInsideElement === 'true') {
20630 var temp = b2bDOMHelper.lastTabableElement(elem);
20631 if (lastTabableElement !== temp) {
20632 // Unbind keydown handler on lastTabableElement
20633 angular.element(lastTabableElement).unbind('keydown', lastTabableElementKeyhandler);
20634 lastTabableElement = temp;
20635 angular.element(lastTabableElement).bind('keydown', lastTabableElementKeyhandler);
20638 lastTabableElement.focus();
20639 events.preventDefault(e);
20640 events.stopPropagation(e);
20644 var lastTabableElementKeyhandler = function(e) {
20646 e.keyCode = e.which;
20648 if (e.keyCode === keymap.KEY.TAB && !e.shiftKey) {
20649 if (attr.trapFocusInsideElement === 'true') {
20650 var temp = b2bDOMHelper.firstTabableElement(elem);
20651 if (firstTabableElement !== temp) {
20652 // Unbind keydown handler on firstTabableElement
20653 angular.element(firstTabableElement).unbind('keydown', firstTabableElementKeyhandler);
20654 firstTabableElement = temp;
20655 angular.element(firstTabableElement).bind('keydown', firstTabableElementKeyhandler);
20658 firstTabableElement.focus();
20659 events.preventDefault(e);
20660 events.stopPropagation(e);
20669 .factory('trapFocusInElement', ['$document', '$isElement', 'b2bDOMHelper', 'keymap', '$interval', function ($document, $isElement, b2bDOMHelper, keymap, $interval) {
20670 var elementStack = [];
20671 var stackHead = undefined;
20672 var stopInterval = undefined;
20673 var intervalRequired = false;
20674 var interval = 1000;
20675 var firstTabableElement, lastTabableElement;
20677 var trapKeyboardFocusInFirstElement = function (e) {
20679 e.keyCode = e.which;
20682 if (e.shiftKey === true && e.keyCode === keymap.KEY.TAB) {
20683 lastTabableElement[0].focus();
20684 e.preventDefault(e);
20685 e.stopPropagation(e);
20690 var trapKeyboardFocusInLastElement = function (e) {
20692 e.keyCode = e.which;
20695 if (e.shiftKey === false && e.keyCode === keymap.KEY.TAB) {
20696 firstTabableElement[0].focus();
20697 e.preventDefault(e);
20698 e.stopPropagation(e);
20702 var trapFocusInElement = function (flag, firstTabableElementParam, lastTabableElementParam) {
20703 var bodyElements = $document.find('body').children();
20705 firstTabableElement = firstTabableElementParam ? firstTabableElementParam : angular.element(b2bDOMHelper.firstTabableElement(stackHead));
20706 lastTabableElement = lastTabableElementParam ? lastTabableElementParam : angular.element(b2bDOMHelper.lastTabableElement(stackHead));
20709 for (var i = 0; i < bodyElements.length; i++) {
20710 if (bodyElements[i] !== stackHead[0]) {
20711 bodyElements.eq(i).attr('aria-hidden', true);
20714 firstTabableElement.bind('keydown', trapKeyboardFocusInFirstElement);
20715 lastTabableElement.bind('keydown', trapKeyboardFocusInLastElement);
20717 for (var j = 0; j < bodyElements.length; j++) {
20718 if (bodyElements[j] !== stackHead[0]) {
20719 bodyElements.eq(j).removeAttr('aria-hidden');
20722 firstTabableElement.unbind('keydown', trapKeyboardFocusInFirstElement);
20723 lastTabableElement.unbind('keydown', trapKeyboardFocusInLastElement);
20726 if (intervalRequired && flag) {
20727 stopInterval = $interval(function () {
20728 var firstTabableElementTemp = angular.element(b2bDOMHelper.firstTabableElement(stackHead));
20729 var lastTabableElementTemp = angular.element(b2bDOMHelper.lastTabableElement(stackHead));
20730 if (firstTabableElementTemp[0] !== firstTabableElement[0] || lastTabableElementTemp[0] !== lastTabableElement[0]) {
20731 $interval.cancel(stopInterval);
20732 stopInterval = undefined;
20733 trapFocusInElement(false, firstTabableElement, lastTabableElement);
20734 trapFocusInElement(true, firstTabableElementTemp, lastTabableElementTemp);
20738 if (stopInterval) {
20739 $interval.cancel(stopInterval);
20740 stopInterval = undefined;
20744 var toggleTrapFocusInElement = function (flag, element, intervalRequiredParam, intervalParam) {
20745 intervalRequired = intervalRequiredParam ? intervalRequiredParam : intervalRequired;
20746 interval = intervalParam ? intervalParam : interval;
20747 if (angular.isDefined(flag) && angular.isDefined(element)) {
20748 if (flag && angular.isUndefined(stackHead)) {
20749 stackHead = element;
20750 trapFocusInElement(flag);
20753 trapFocusInElement(false);
20754 elementStack.push(stackHead);
20755 stackHead = element;
20756 trapFocusInElement(true);
20758 if (angular.isDefined(stackHead) && (stackHead[0] === element[0])) {
20759 trapFocusInElement(false);
20760 stackHead = elementStack.pop();
20761 if (angular.isDefined(stackHead)) {
20762 trapFocusInElement(true);
20768 if (angular.isDefined(stackHead)) {
20769 trapFocusInElement(false, firstTabableElement, lastTabableElement);
20770 trapFocusInElement(true);
20775 return toggleTrapFocusInElement;
20777 .factory('draggedElement', function(){
20778 var draggedElement;
20780 setElement: function(data){
20781 draggedElement = data;
20783 getElement: function(){
20784 return draggedElement;
20789 .directive('draggable', ['draggedElement',function (draggedElement) {
20790 return function(scope, element) {
20792 element[0].draggable = true;
20794 element.bind('dragstart', function(e) {
20795 draggedElement.setElement(this.parentElement.parentElement);
20796 e.dataTransfer.effectAllowed = 'move';
20797 e.dataTransfer.setDragImage(this.parentElement.parentElement, 0, 0);
20798 this.parentElement.parentElement.classList.add('b2-drag-element');
20802 element.bind('dragend', function(e) {
20803 draggedElement.setElement(e);
20804 this.parentElement.parentElement.classList.remove('b2-drag-element');
20810 .directive('droppable', ['draggedElement',function (draggedElement) {
20817 link: function(scope, element, attr) {
20818 if(attr.droppable === 'true') {
20819 element.bind('dragover', function(e) {
20820 e.dataTransfer.dropEffect = 'move';
20821 this.classList.add('b2b-drag-over')
20822 if (e.preventDefault) e.preventDefault(); // allows us to drop
20826 element.bind('dragstart', function(e) {
20827 if(!e.target.parentElement.classList.contains('b2b-draggable')) {
20828 e.preventDefault();
20833 element.bind('dragenter', function(e) {
20834 if(e.target.getAttribute('droppable') ==='true') {
20839 element.bind('dragleave', function(e) {
20840 this.classList.remove('b2b-drag-over');
20844 element.bind('drop', function(e) {
20845 var ele = draggedElement.getElement();
20846 if (e.stopPropagation) e.stopPropagation();
20847 if (e.preventDefault) e.preventDefault();
20848 this.classList.remove('b2b-drag-over');
20850 if(ele && ele.hasAttribute('data-index')){
20851 var element = scope.rowData[parseInt(ele.getAttribute('data-index'))];
20852 if(element !== undefined) {
20853 scope.rowData.splice(parseInt(ele.getAttribute('data-index')), 1);
20854 scope.rowData.splice(parseInt(e.currentTarget.getAttribute('data-index')), 0 , element);
20865 .directive('b2bSetNextFocusOn', ['b2bDOMHelper', '$timeout', function(b2bDOMHelper, $timeout) {
20869 link: function (scope, elem, attr) {
20870 elem.bind('click', function(){
20871 var firstFocusableElement = undefined;
20872 var containerElem = undefined;
20873 var containerArray = [];
20874 var timeout = parseInt(attr.setNextFocusTimeout, 0) | 100;
20875 var index = parseInt(attr.b2bSetNextFocusIndex, 0) | 0;
20878 *Fix for IE7 and lower
20879 *polyfill src: https://github.com/HubSpot/pace/issues/102
20881 if (!document.querySelectorAll) {
20882 document.querySelectorAll = function (selectors) {
20883 var style = document.createElement('style'), elements = [], element;
20884 document.documentElement.firstChild.appendChild(style);
20885 document._qsa = [];
20887 style.styleSheet.cssText = selectors + '{x-qsa:expression(document._qsa && document._qsa.push(this))}';
20888 window.scrollBy(0, 0);
20889 style.parentNode.removeChild(style);
20891 while (document._qsa.length) {
20892 element = document._qsa.shift();
20893 element.style.removeAttribute('x-qsa');
20894 elements.push(element);
20896 document._qsa = null;
20901 if (attr.b2bSetNextFocusOn === '') {
20904 containerArray = attr.b2bSetNextFocusOn.split(' ');
20906 $timeout(function(){
20908 do { // cycles thru containerArray until finds a match in DOM to set focus to
20909 containerElem = document.querySelectorAll(containerArray[i])[index];
20911 } while ( (!containerElem) && (i < containerArray.length) );
20913 if (!angular.isDefined(firstFocusableElement)) {
20914 firstFocusableElement = b2bDOMHelper.firstTabableElement(containerElem);
20916 firstFocusableElement.focus();
20925 .directive('b2bInputAllow', [function() {
20928 require: 'ngModel',
20929 link: function (scope, elem, attr, ctrl) {
20930 var regexExpression = null;
20931 attr.$observe('b2bInputAllow', function (value) {
20933 regexExpression = new RegExp(value);
20936 var isValid = function(str) {
20937 if (regexExpression !== null) {
20938 return regexExpression.test(str);
20942 elem.bind('keypress', function($event) {
20943 var charcode = String.fromCharCode($event.which || $event.keyCode);
20944 if (!isValid(charcode)) {
20945 $event.preventDefault();
20946 $event.stopPropagation();
20949 elem.bind('input', function (evt) {
20950 var inputString = ctrl.$viewValue;
20951 if (isValid(inputString)) {
20952 ctrl.$setViewValue(inputString);
20961 .directive('b2bInputDeny', [function() {
20964 require: 'ngModel',
20965 link: function (scope, elem, attr, ctrl) {
20966 var regexExpression = null;
20967 attr.$observe('b2bInputDeny', function (value) {
20969 regexExpression = new RegExp(value, 'g');
20972 elem.bind('input', function () {
20973 var inputString = ctrl.$viewValue && ctrl.$viewValue.replace(regexExpression, '');
20974 if (inputString !== ctrl.$viewValue) {
20975 ctrl.$setViewValue(inputString);
20984 .directive('b2bDragonInput', [function() {
20987 require: 'ngModel',
20988 link: function (scope, elem, attr, ctrl) {
20989 elem.on('focus keyup', function(){
20990 elem.triggerHandler('change');
20996 .directive('b2bKey', ['b2bUtilitiesConfig', '$timeout', 'keymap', function (b2bUtilitiesConfig, $timeout, keymap) {
20999 controller: ['$scope', '$element', '$attrs', function ($scope, $element,attr) {
21000 this.childElements = [];
21001 this.disableNodes = {};
21002 this.enableSearch = attr.enableSearch !== undefined ? true : b2bUtilitiesConfig.enableSearch;
21003 this.circularTraversal = attr.circularTraversal !== undefined ? true : b2bUtilitiesConfig.circularTraversal;
21005 if (this.enableSearch) {
21006 this.searchKeys = [];
21008 var searchString = '';
21010 var selfCtrl = this;
21012 this.childElementsList = [];
21014 this.b2bKeyID = "";
21016 if (angular.isDefined(attr.b2bKey)) {
21017 this.b2bKeyID = attr.b2bKey;
21020 this.calculateChildElementsList = function () {
21021 return $element[0].querySelectorAll("[b2b-key-item='" + this.b2bKeyID + "']:not([disabled])");
21024 this.resetChildElementsList = function () {
21025 return $timeout(function () {
21026 selfCtrl.childElementsList = selfCtrl.calculateChildElementsList();
21030 this.resetChildElementsList();
21032 $scope.$on('b2b-key-reset-child-elements-list', function () {
21033 selfCtrl.resetChildElementsList();
21037 this.registerElement = function (childElement, searchKey) {
21038 this.childElements.push(childElement);
21039 if (this.enableSearch) {
21040 this.searchKeys.push(searchKey);
21042 var count = this.childElements.length - 1;
21043 this.maxLength = count + 1;
21046 this.toggleDisable = function (count, state) {
21047 this.disableNodes[count] = state;
21049 this.searchElement = function (searchExp) {
21050 var regex = new RegExp("\\b" + searchExp, "gi");
21051 var position = this.searchKeys.regexIndexOf(regex, this.counter + 1, true);
21052 if (position > -1) {
21053 this.counter = position;
21054 this.moveFocus(this.counter);
21057 this.startTimer = function (time) {
21058 if (searchString === '') {
21059 $timeout(function () {
21064 this.resetCounter = function (count) {
21065 this.counter = count;
21067 this.moveNext = function (count) {
21068 this.counter = (this.counter + count) < this.maxLength ? this.counter + count : (this.circularTraversal ? 0 : this.counter);
21069 if (this.disableNodes[this.counter]) {
21070 if ((this.counter + count) < this.maxLength) {
21071 this.moveNext(count);
21074 this.moveFocus(this.counter);
21077 this.movePrev = function (count) {
21078 this.counter = (this.counter - count) > -1 ? this.counter - count : (this.circularTraversal ? this.maxLength-1 : this.counter);
21079 if (this.disableNodes[this.counter]) {
21080 if ((this.counter - count) > -1) {
21081 this.movePrev(count);
21084 this.moveFocus(this.counter);
21087 this.moveFocus = function (index) {
21088 this.childElements[index][0].focus();
21091 this.keyDownHandler = function (ev, count) {
21092 if (angular.isDefined(count) && !isNaN(count) && count !== this.counter) {
21093 this.resetCounter(count);
21097 ev.keyCode = ev.which;
21098 } else if (ev.charCode) {
21099 ev.keyCode = ev.charCode;
21103 if (this.prev && this.prev.indexOf(ev.keyCode.toString()) > -1) {
21105 ev.preventDefault();
21106 ev.stopPropagation();
21107 } else if (this.next && this.next.indexOf(ev.keyCode.toString()) > -1) {
21109 ev.preventDefault();
21110 ev.stopPropagation();
21111 } else if (this.up && this.up.indexOf(ev.keyCode.toString()) > -1) {
21112 if (this.type === 'table') {
21113 this.movePrev(this.columns);
21114 ev.preventDefault();
21115 ev.stopPropagation();
21117 } else if (this.down && this.down.indexOf(ev.keyCode.toString()) > -1) {
21118 if (this.type === 'table') {
21119 this.moveNext(this.columns);
21120 ev.preventDefault();
21121 ev.stopPropagation();
21123 } else if (ev.keyCode === keymap.KEY.HOME) {
21124 var firstIndex = 0;
21125 while (this.disableNodes[firstIndex] !== false) {
21128 var count = this.counter - firstIndex;
21129 this.movePrev(count);
21130 ev.preventDefault();
21131 ev.stopPropagation();
21132 } else if (ev.keyCode === keymap.KEY.END) {
21133 var lastIndex = this.childElements.length - 1;
21134 while (this.disableNodes[lastIndex] !== false) {
21137 var count = lastIndex - this.counter;
21138 this.moveNext(count);
21139 ev.preventDefault();
21140 ev.stopPropagation();
21141 } else if (ev.keyCode >= 48 && ev.keyCode <= 105) {
21142 if (this.enableSearch) {
21143 this.startTimer(b2bUtilitiesConfig.searchTimer);
21144 searchString = searchString + (keymap.MAP[ev.keyCode] || '');
21145 this.searchElement(searchString);
21146 ev.preventDefault();
21147 ev.stopPropagation();
21153 link: function (scope, elem, attr, ctrl) {
21154 ctrl.prev = attr.prev ? attr.prev.split(',') : b2bUtilitiesConfig.prev.split(',');
21155 ctrl.next = attr.next ? attr.next.split(',') : b2bUtilitiesConfig.next.split(',');
21156 ctrl.type = attr.type ? attr.type : b2bUtilitiesConfig.type;
21157 if (ctrl.type === 'table') {
21158 ctrl.up = attr.up ? attr.up.split(',') : b2bUtilitiesConfig.up.split(',');
21159 ctrl.down = attr.down ? attr.down.split(',') : b2bUtilitiesConfig.down.split(',');
21160 ctrl.columns = attr.columns ? parseInt(attr.columns, 10) : b2bUtilitiesConfig.columns;
21163 elem.bind('keydown', function (ev) {
21164 ctrl.keyDownHandler(ev);
21170 .directive('b2bKeyItem', [function () {
21173 link: function (scope, elem, attr, ctrl) {
21174 var parentCtrl = (elem.parent() && elem.parent().controller('b2bKey')) || undefined;
21175 if (angular.isDefined(parentCtrl)) {
21176 var count = parentCtrl.registerElement(elem, attr.searchKey);
21177 elem.bind('keydown', function (ev) {
21178 parentCtrl.keyDownHandler(ev, count);
21180 scope.$watch(attr.b2bKeyItem, function (value) {
21181 value = value === undefined ? true : value;
21182 parentCtrl.toggleDisable(count, !value);
21184 scope.$on('$destroy', function () {
21185 parentCtrl.toggleDisable(count, true);
21192 .directive('b2bElementFocus', [function () {
21195 link: function (scope, elem, attr, ctrl) {
21196 scope.$watch(attr.b2bElementFocus, function (value) {
21197 if (value === true) {
21206 .directive('b2bAppendElement', ['$compile', function ($compile) {
21209 link: function (scope, elem, attr, ctrl) {
21210 var parameters = attr.b2bAppendElement.split(':');
21211 if (parameters.length === 1) {
21212 elem.append(scope.$eval(parameters[0]));
21213 } else if (parameters.length === 2) {
21214 if (parameters[1] === 'compile') {
21215 var element = angular.element('<span>' + scope.$eval(parameters[0]) + '</span>');
21216 elem.append($compile(element)(scope));
21224 .directive('b2bKeyItemRefreshInNgRepeat', [function () {
21227 require: '^^b2bKey',
21228 link: function (scope, elem, attr, parentCtrl) {
21229 if (angular.isDefined(parentCtrl)) {
21231 var attrToObserve = 'attrToObserve';
21233 if (attr.b2bKeyItemRefreshInNgRepeat) {
21234 attrToObserve = 'b2bKeyItemRefreshInNgRepeat';
21237 attr.$observe(attrToObserve, function (newVal, oldVal) {
21238 if (newVal && newVal !== oldVal) {
21239 parentCtrl.resetChildElementsList();
21247 .constant('b2bMaskConfig', {
21253 clearOnBlur: false,
21254 clearOnBlurPlaceholder: false,
21256 eventsToHandle: ['input', 'keyup', 'click', 'focus'],
21257 addDefaultPlaceholder: true,
21258 allowInvalidValue: true
21261 * @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.
21262 * @param {String} maskPlaceholder - Allows customizing the mask placeholder when a user has focused the input element and while typing in their value
21263 * @param {String} maskPlaceholderChar - Allows customizing the mask placeholder character. The default mask placeholder is _.
21264 * @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.
21266 .directive('b2bMask', ['b2bMaskConfig', function(b2bMaskConfig) {
21268 require: 'ngModel',
21270 link: function(scope, element, attrs, ctrl) {
21271 var maskProcessed = false, eventsBound = false,
21272 maskCaretMap, maskPatterns, maskPlaceholder, maskComponents,
21273 // Minimum required length of the value to be considered valid
21275 value, valueMasked, isValid,
21276 // Vars for initializing/uninitializing
21277 originalPlaceholder = attrs.placeholder,
21278 originalMaxlength = attrs.maxlength,
21279 // Vars used exclusively in eventHandler()
21280 oldValue, oldValueUnmasked, oldCaretPosition, oldSelectionLength,
21281 // Used for communicating if a backspace operation should be allowed between
21282 // keydownHandler and eventHandler
21285 var options = b2bMaskConfig;
21287 function isFocused (elem) {
21288 return elem === document.activeElement && (!document.hasFocus || document.hasFocus()) && !!(elem.type || elem.href || ~elem.tabIndex);
21291 var originalIsEmpty = ctrl.$isEmpty;
21292 ctrl.$isEmpty = function(value) {
21293 if (maskProcessed) {
21294 return originalIsEmpty(unmaskValue(value || ''));
21296 return originalIsEmpty(value);
21300 function initialize(maskAttr) {
21301 if (!angular.isDefined(maskAttr)) {
21302 return uninitialize();
21304 processRawMask(maskAttr);
21305 if (!maskProcessed) {
21306 return uninitialize();
21308 initializeElement();
21309 bindEventListeners();
21313 function initPlaceholder(placeholderAttr) {
21314 if ( ! placeholderAttr) {
21317 maskPlaceholder = placeholderAttr;
21318 /* If the mask is processed, then we need to update the value
21319 but don't set the value if there is nothing entered into the element
21320 and there is a placeholder attribute on the element because that
21321 will only set the value as the blank maskPlaceholder
21322 and override the placeholder on the element */
21323 if (maskProcessed && !(element.val().length === 0 && angular.isDefined(attrs.placeholder))) {
21324 element.val(maskValue(unmaskValue(element.val())));
21328 function initPlaceholderChar() {
21329 return initialize(attrs.uiMask);
21332 var modelViewValue = false;
21334 attrs.$observe('modelViewValue', function(val) {
21335 if (val === 'true') {
21336 modelViewValue = true;
21340 attrs.$observe('allowInvalidValue', function(val) {
21341 linkOptions.allowInvalidValue = val === ''? true : !!val;
21342 formatter(ctrl.$modelValue);
21345 function formatter(fromModelValue) {
21346 if (!maskProcessed) {
21347 return fromModelValue;
21349 value = unmaskValue(fromModelValue || '');
21350 isValid = validateValue(value);
21351 ctrl.$setValidity('mask', isValid);
21353 if (!value.length) return undefined;
21354 if (isValid || linkOptions.allowInvalidValue) {
21355 return maskValue(value);
21361 function parser(fromViewValue) {
21362 if (!maskProcessed) {
21363 return fromViewValue;
21365 value = unmaskValue(fromViewValue || '');
21366 isValid = validateValue(value);
21367 /* We have to set viewValue manually as the reformatting of the input
21368 value performed by eventHandler() doesn't happen until after
21369 this parser is called, which causes what the user sees in the input
21370 to be out-of-sync with what the ctrl's $viewValue is set to. */
21371 ctrl.$viewValue = value.length ? maskValue(value) : '';
21372 ctrl.$setValidity('mask', isValid);
21374 if (isValid || linkOptions.allowInvalidValue) {
21375 return modelViewValue ? ctrl.$viewValue : value;
21379 var linkOptions = {};
21382 if (attrs.b2bMaskOptions) {
21383 linkOptions = scope.$eval('[' + attrs.b2bMaskOptions + ']');
21384 if (angular.isObject(linkOptions[0])) {
21385 // we can't use angular.copy nor angular.extend, they lack the power to do a deep merge
21386 linkOptions = (function(original, current) {
21387 for (var i in original) {
21388 if (Object.prototype.hasOwnProperty.call(original, i)) {
21389 if (current[i] === undefined) {
21390 current[i] = angular.copy(original[i]);
21392 if (angular.isObject(current[i]) && !angular.isArray(current[i])) {
21393 current[i] = angular.extend({}, original[i], current[i]);
21399 })(options, linkOptions[0]);
21401 linkOptions = options; //gotta be a better way to do this..
21404 linkOptions = options;
21407 attrs.$observe('b2bMask', initialize);
21408 if (angular.isDefined(attrs.maskPlaceholder)) {
21409 attrs.$observe('maskPlaceholder', initPlaceholder);
21412 attrs.$observe('placeholder', initPlaceholder);
21414 if (angular.isDefined(attrs.maskPlaceholderChar)) {
21415 attrs.$observe('maskPlaceholderChar', initPlaceholderChar);
21418 ctrl.$formatters.unshift(formatter);
21419 ctrl.$parsers.unshift(parser);
21421 function uninitialize() {
21422 maskProcessed = false;
21423 unbindEventListeners();
21425 if (angular.isDefined(originalPlaceholder)) {
21426 element.attr('placeholder', originalPlaceholder);
21428 element.removeAttr('placeholder');
21431 if (angular.isDefined(originalMaxlength)) {
21432 element.attr('maxlength', originalMaxlength);
21434 element.removeAttr('maxlength');
21437 element.val(ctrl.$modelValue);
21438 ctrl.$viewValue = ctrl.$modelValue;
21442 function initializeElement() {
21443 value = oldValueUnmasked = unmaskValue(ctrl.$modelValue || '');
21444 valueMasked = oldValue = maskValue(value);
21445 isValid = validateValue(value);
21446 if (attrs.maxlength) { // Double maxlength to allow pasting new val at end of mask
21447 element.attr('maxlength', maskCaretMap[maskCaretMap.length - 1] * 2);
21449 if ( ! originalPlaceholder && linkOptions.addDefaultPlaceholder) {
21450 element.attr('placeholder', maskPlaceholder);
21452 var viewValue = ctrl.$modelValue;
21453 var idx = ctrl.$formatters.length;
21455 viewValue = ctrl.$formatters[idx](viewValue);
21457 ctrl.$viewValue = viewValue || '';
21461 function bindEventListeners() {
21465 element.bind('blur', blurHandler);
21466 element.bind('mousedown mouseup', mouseDownUpHandler);
21467 element.bind('keydown', keydownHandler);
21468 element.bind(linkOptions.eventsToHandle.join(' '), eventHandler);
21469 eventsBound = true;
21472 function unbindEventListeners() {
21473 if (!eventsBound) {
21476 element.unbind('blur', blurHandler);
21477 element.unbind('mousedown', mouseDownUpHandler);
21478 element.unbind('mouseup', mouseDownUpHandler);
21479 element.unbind('keydown', keydownHandler);
21480 element.unbind('input', eventHandler);
21481 element.unbind('keyup', eventHandler);
21482 element.unbind('click', eventHandler);
21483 element.unbind('focus', eventHandler);
21484 eventsBound = false;
21487 function validateValue(value) {
21488 // Zero-length value validity is ngRequired's determination
21489 return value.length ? value.length >= minRequiredLength : true;
21492 function unmaskValue(value) {
21493 var valueUnmasked = '',
21494 input = element[0],
21495 maskPatternsCopy = maskPatterns.slice(),
21496 selectionStart = oldCaretPosition,
21497 selectionEnd = selectionStart + getSelectionLength(input),
21498 valueOffset, valueDelta, tempValue = '';
21499 // Preprocess by stripping mask components from value
21500 value = value.toString();
21502 valueDelta = value.length - maskPlaceholder.length;
21503 angular.forEach(maskComponents, function(component) {
21504 var position = component.position;
21505 //Only try and replace the component if the component position is not within the selected range
21506 //If component was in selected range then it was removed with the user input so no need to try and remove that component
21507 if (!(position >= selectionStart && position < selectionEnd)) {
21508 if (position >= selectionStart) {
21509 position += valueDelta;
21511 if (value.substring(position, position + component.value.length) === component.value) {
21512 tempValue += value.slice(valueOffset, position);// + value.slice(position + component.value.length);
21513 valueOffset = position + component.value.length;
21517 value = tempValue + value.slice(valueOffset);
21518 angular.forEach(value.split(''), function(chr) {
21519 if (maskPatternsCopy.length && maskPatternsCopy[0].test(chr)) {
21520 valueUnmasked += chr;
21521 maskPatternsCopy.shift();
21525 return valueUnmasked;
21528 function maskValue(unmaskedValue) {
21529 var valueMasked = '',
21530 maskCaretMapCopy = maskCaretMap.slice();
21532 angular.forEach(maskPlaceholder.split(''), function(chr, i) {
21533 if (unmaskedValue.length && i === maskCaretMapCopy[0]) {
21534 valueMasked += unmaskedValue.charAt(0) || '_';
21535 unmaskedValue = unmaskedValue.substr(1);
21536 maskCaretMapCopy.shift();
21539 valueMasked += chr;
21542 return valueMasked;
21545 function getPlaceholderChar(i) {
21546 var placeholder = angular.isDefined(attrs.uiMaskPlaceholder) ? attrs.uiMaskPlaceholder : attrs.placeholder,
21547 defaultPlaceholderChar;
21549 if (angular.isDefined(placeholder) && placeholder[i]) {
21550 return placeholder[i];
21552 defaultPlaceholderChar = angular.isDefined(attrs.uiMaskPlaceholderChar) && attrs.uiMaskPlaceholderChar ? attrs.uiMaskPlaceholderChar : '_';
21553 return (defaultPlaceholderChar.toLowerCase() === 'space') ? ' ' : defaultPlaceholderChar[0];
21557 /* Generate array of mask components that will be stripped from a masked value
21558 before processing to prevent mask components from being added to the unmasked value.
21559 E.g., a mask pattern of '+7 9999' won't have the 7 bleed into the unmasked value. */
21560 function getMaskComponents() {
21561 var maskPlaceholderChars = maskPlaceholder.split(''),
21562 maskPlaceholderCopy, components;
21564 /* maskCaretMap can have bad values if the input has the ui-mask attribute implemented as an obversable property, e.g. the demo page */
21565 if (maskCaretMap && !isNaN(maskCaretMap[0])) {
21566 /* Instead of trying to manipulate the RegEx based on the placeholder characters
21567 we can simply replace the placeholder characters based on the already built
21568 maskCaretMap to underscores and leave the original working RegEx to get the proper
21570 angular.forEach(maskCaretMap, function(value) {
21571 maskPlaceholderChars[value] = '_';
21574 maskPlaceholderCopy = maskPlaceholderChars.join('');
21575 components = maskPlaceholderCopy.replace(/[_]+/g, '_').split('_');
21576 components = components.filter(function(s) {
21580 /* need a string search offset in cases where the mask contains multiple identical components
21581 E.g., a mask of 99.99.99-999.99 */
21583 return components.map(function(c) {
21584 var componentPosition = maskPlaceholderCopy.indexOf(c, offset);
21585 offset = componentPosition + 1;
21588 position: componentPosition
21593 function processRawMask(mask) {
21594 var characterCount = 0;
21598 maskPlaceholder = '';
21600 if (angular.isString(mask)) {
21601 minRequiredLength = 0;
21603 var isOptional = false,
21604 numberOfOptionalCharacters = 0,
21605 splitMask = mask.split('');
21607 var inEscape = false;
21608 angular.forEach(splitMask, function(chr, i) {
21611 maskPlaceholder += chr;
21614 else if (linkOptions.escChar === chr) {
21617 else if (linkOptions.maskDefinitions[chr]) {
21618 maskCaretMap.push(characterCount);
21620 maskPlaceholder += getPlaceholderChar(i - numberOfOptionalCharacters);
21621 maskPatterns.push(linkOptions.maskDefinitions[chr]);
21625 minRequiredLength++;
21628 isOptional = false;
21630 else if (chr === '?') {
21632 numberOfOptionalCharacters++;
21635 maskPlaceholder += chr;
21640 // Caret position immediately following last position is valid.
21641 maskCaretMap.push(maskCaretMap.slice().pop() + 1);
21643 maskComponents = getMaskComponents();
21644 maskProcessed = maskCaretMap.length > 1 ? true : false;
21647 var prevValue = element.val();
21648 function blurHandler() {
21649 if (linkOptions.clearOnBlur || ((linkOptions.clearOnBlurPlaceholder) && (value.length === 0) && attrs.placeholder)) {
21650 oldCaretPosition = 0;
21651 oldSelectionLength = 0;
21652 if (!isValid || value.length === 0) {
21655 scope.$apply(function() {
21656 //only $setViewValue when not $pristine to avoid changing $pristine state.
21657 if (!ctrl.$pristine) {
21658 ctrl.$setViewValue('');
21663 //Check for different value and trigger change.
21664 if (value !== prevValue) {
21665 var currentVal = element.val();
21666 var isTemporarilyEmpty = value === '' && currentVal && angular.isDefined(attrs.uiMaskPlaceholderChar) && attrs.uiMaskPlaceholderChar === 'space';
21667 if(isTemporarilyEmpty) {
21670 triggerChangeEvent(element[0]);
21671 if(isTemporarilyEmpty) {
21672 element.val(currentVal);
21678 function triggerChangeEvent(element) {
21680 if (angular.isFunction(window.Event) && !element.fireEvent) {
21681 // modern browsers and Edge
21683 change = new Event('change', {
21689 //this is for certain mobile browsers that have the Event object
21690 //but don't support the Event constructor
21691 change = document.createEvent('HTMLEvents');
21692 change.initEvent('change', false, true);
21694 element.dispatchEvent(change);
21696 } else if ('createEvent' in document) {
21698 change = document.createEvent('HTMLEvents');
21699 change.initEvent('change', false, true);
21700 element.dispatchEvent(change);
21702 else if (element.fireEvent) {
21704 element.fireEvent('onchange');
21708 function mouseDownUpHandler(e) {
21709 if (e.type === 'mousedown') {
21710 element.bind('mouseout', mouseoutHandler);
21712 element.unbind('mouseout', mouseoutHandler);
21716 element.bind('mousedown mouseup', mouseDownUpHandler);
21718 function mouseoutHandler() {
21719 oldSelectionLength = getSelectionLength(this);
21720 element.unbind('mouseout', mouseoutHandler);
21723 function keydownHandler(e) {
21724 var isKeyBackspace = e.which === 8,
21725 caretPos = getCaretPosition(this) - 1 || 0, //value in keydown is pre change so bump caret position back to simulate post change
21726 isCtrlZ = e.which === 90 && e.ctrlKey; //ctrl+z pressed
21728 if (isKeyBackspace) {
21729 while(caretPos >= 0) {
21730 if (isValidCaretPosition(caretPos)) {
21731 //re-adjust the caret position.
21732 //Increment to account for the initial decrement to simulate post change caret position
21733 setCaretPosition(this, caretPos + 1);
21738 preventBackspace = caretPos === -1;
21742 // prevent IE bug - value should be returned to initial state
21744 e.preventDefault();
21748 function eventHandler(e) {
21750 // Allows more efficient minification
21751 var eventWhich = e.which,
21752 eventType = e.type;
21754 // Prevent shift and ctrl from mucking with old values
21755 if (eventWhich === 16 || eventWhich === 91) {
21759 var val = element.val(),
21762 valAltered = false,
21763 valUnmasked = unmaskValue(val),
21764 valUnmaskedOld = oldValueUnmasked,
21765 caretPos = getCaretPosition(this) || 0,
21766 caretPosOld = oldCaretPosition || 0,
21767 caretPosDelta = caretPos - caretPosOld,
21768 caretPosMin = maskCaretMap[0],
21769 caretPosMax = maskCaretMap[valUnmasked.length] || maskCaretMap.slice().shift(),
21770 selectionLenOld = oldSelectionLength || 0,
21771 isSelected = getSelectionLength(this) > 0,
21772 wasSelected = selectionLenOld > 0,
21773 // Case: Typing a character to overwrite a selection
21774 isAddition = (val.length > valOld.length) || (selectionLenOld && val.length > valOld.length - selectionLenOld),
21775 // Case: Delete and backspace behave identically on a selection
21776 isDeletion = (val.length < valOld.length) || (selectionLenOld && val.length === valOld.length - selectionLenOld),
21777 isSelection = (eventWhich >= 37 && eventWhich <= 40) && e.shiftKey, // Arrow key codes
21779 isKeyLeftArrow = eventWhich === 37,
21780 // Necessary due to "input" event not providing a key code
21781 isKeyBackspace = eventWhich === 8 || (eventType !== 'keyup' && isDeletion && (caretPosDelta === -1)),
21782 isKeyDelete = eventWhich === 46 || (eventType !== 'keyup' && isDeletion && (caretPosDelta === 0) && !wasSelected),
21783 // Handles cases where caret is moved and placed in front of invalid maskCaretMap position. Logic below
21784 // ensures that, on click or leftward caret placement, caret is moved leftward until directly right of
21785 // non-mask character. Also applied to click since users are (arguably) more likely to backspace
21786 // a character when clicking within a filled input.
21787 caretBumpBack = (isKeyLeftArrow || isKeyBackspace || eventType === 'click') && caretPos > caretPosMin;
21789 oldSelectionLength = getSelectionLength(this);
21791 // These events don't require any action
21792 if (isSelection || (isSelected && (eventType === 'click' || eventType === 'keyup' || eventType === 'focus'))) {
21796 if (isKeyBackspace && preventBackspace) {
21797 element.val(maskPlaceholder);
21798 // This shouldn't be needed but for some reason after aggressive backspacing the ctrl $viewValue is incorrect.
21799 // This keeps the $viewValue updated and correct.
21800 scope.$apply(function () {
21801 ctrl.$setViewValue(''); // $setViewValue should be run in angular context, otherwise the changes will be invisible to angular and user code.
21803 setCaretPosition(this, caretPosOld);
21807 // User attempted to delete but raw value was unaffected--correct this grievous offense
21808 if ((eventType === 'input') && isDeletion && !wasSelected && valUnmasked === valUnmaskedOld) {
21809 while (isKeyBackspace && caretPos > caretPosMin && !isValidCaretPosition(caretPos)) {
21812 while (isKeyDelete && caretPos < caretPosMax && maskCaretMap.indexOf(caretPos) === -1) {
21815 var charIndex = maskCaretMap.indexOf(caretPos);
21816 // Strip out non-mask character that user would have deleted if mask hadn't been in the way.
21817 valUnmasked = valUnmasked.substring(0, charIndex) + valUnmasked.substring(charIndex + 1);
21819 // If value has not changed, don't want to call $setViewValue, may be caused by IE raising input event due to placeholder
21820 if (valUnmasked !== valUnmaskedOld)
21825 valMasked = maskValue(valUnmasked);
21827 oldValue = valMasked;
21828 oldValueUnmasked = valUnmasked;
21830 //additional check to fix the problem where the viewValue is out of sync with the value of the element.
21831 //better fix for commit 2a83b5fb8312e71d220a497545f999fc82503bd9 (I think)
21832 if (!valAltered && val.length > valMasked.length)
21835 element.val(valMasked);
21837 //we need this check. What could happen if you don't have it is that you'll set the model value without the user
21838 //actually doing anything. Meaning, things like pristine and touched will be set.
21840 scope.$apply(function () {
21841 ctrl.$setViewValue(valMasked); // $setViewValue should be run in angular context, otherwise the changes will be invisible to angular and user code.
21845 // Caret Repositioning
21846 // Ensure that typing always places caret ahead of typed character in cases where the first char of
21847 // the input is a mask char and the caret is placed at the 0 position.
21848 if (isAddition && (caretPos <= caretPosMin)) {
21849 caretPos = caretPosMin + 1;
21852 if (caretBumpBack) {
21856 // Make sure caret is within min and max position limits
21857 caretPos = caretPos > caretPosMax ? caretPosMax : caretPos < caretPosMin ? caretPosMin : caretPos;
21859 // Scoot the caret back or forth until it's in a non-mask position and within min/max position limits
21860 while (!isValidCaretPosition(caretPos) && caretPos > caretPosMin && caretPos < caretPosMax) {
21861 caretPos += caretBumpBack ? -1 : 1;
21864 if ((caretBumpBack && caretPos < caretPosMax) || (isAddition && !isValidCaretPosition(caretPosOld))) {
21867 oldCaretPosition = caretPos;
21868 setCaretPosition(this, caretPos);
21871 function isValidCaretPosition(pos) {
21872 return maskCaretMap.indexOf(pos) > -1;
21875 function getCaretPosition(input) {
21878 if (input.selectionStart !== undefined) {
21879 return input.selectionStart;
21880 } else if (document.selection) {
21881 if (isFocused(element[0])) {
21884 var selection = document.selection.createRange();
21885 selection.moveStart('character', input.value ? -input.value.length : 0);
21886 return selection.text.length;
21892 function setCaretPosition(input, pos) {
21895 if (input.offsetWidth === 0 || input.offsetHeight === 0) {
21896 return; // Input's hidden
21898 if (input.setSelectionRange) {
21899 if (isFocused(element[0])) {
21901 input.setSelectionRange(pos, pos);
21904 else if (input.createTextRange) {
21906 var range = input.createTextRange();
21907 range.collapse(true);
21908 range.moveEnd('character', pos);
21909 range.moveStart('character', pos);
21914 function getSelectionLength(input) {
21917 if (input.selectionStart !== undefined) {
21918 return (input.selectionEnd - input.selectionStart);
21920 if (window.getSelection) {
21921 return (window.getSelection().toString().length);
21923 if (document.selection) {
21924 return (document.selection.createRange().text.length);
21931 .filter('b2bMultiSepartorHighlight', function($sce) {
21932 return function(text, searchText, searchSeperator) {
21933 var splitText = function(string) {
21934 if(angular.isDefined(searchSeperator)){
21935 if (string.indexOf(searchSeperator) > -1) {
21936 return string.split(searchSeperator);
21945 var newText = splitText(text);
21946 var newPhrase = splitText(searchText);
21947 if (angular.isArray(newPhrase)) {
21948 for (var i = 0; i < newText.length; i++) {
21950 text = newText[i].replace(new RegExp('(' + newPhrase[i] + ')', 'gi'),
21951 '<span class="b2b-search-hightlight">$1</span>');
21953 text = text + searchSeperator + ' ' + (newPhrase[i] ? newText[i].replace(new RegExp('(' + newPhrase[i] + ')', 'gi'),
21954 '<span class="b2b-search-hightlight">$1</span>') : newText[i]);
21958 text = text.replace(new RegExp('(' + searchText + ')', 'gi'),
21959 '<span class="b2b-search-hightlight">$1</span>');
21962 return $sce.trustAsHtml(text)
21966 .factory('b2bUserAgent', [function() {
21967 var _isMobile = function() {
21968 if(/Android/i.test(navigator.userAgent)){
21969 return /Mobile/i.test(navigator.userAgent);
21971 return /Android|webOS|iPhone|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent);
21975 var _notMobile = function() {
21976 if(/Android/i.test(navigator.userAgent)){
21977 return !/Mobile/i.test(navigator.userAgent);
21979 return !/Android|webOS|iPhone|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent);
21983 var _isIE = function() {
21984 return /msie|trident/i.test(navigator.userAgent);
21986 var _isFF = function() {
21987 return /Firefox/.test(navigator.userAgent);
21989 var _isChrome = function() {
21990 return /Chrome/.test(navigator.userAgent);
21992 var _isSafari = function() {
21993 return /Safari/.test(navigator.userAgent) && !/Chrome/.test(navigator.userAgent);
21997 isMobile: _isMobile,
21998 notMobile: _notMobile,
22001 isChrome: _isChrome,
22002 isSafari: _isSafari
22005 .run(['$document', 'b2bUserAgent', function($document, b2bUserAgent) {
22006 var html = $document.find('html').eq(0);
22007 if (b2bUserAgent.isIE()) {
22008 html.addClass('isIE');
22010 html.removeClass('isIE');
22016 String.prototype.toSnakeCase = function () {
22017 return this.replace(/([A-Z])/g, function ($1) {
22018 return "-" + $1.toLowerCase();
22021 var concat = function (character, times) {
22022 character = character || '';
22023 times = (!isNaN(times) && times) || 0;
22024 var finalChar = '';
22025 for (var i = 0; i < times; i++) {
22026 finalChar += character;
22031 // direction: true for left and false for right
22032 var pad = function (actualString, width, character, direction) {
22033 actualString = actualString || '';
22034 width = (!isNaN(width) && width) || 0;
22035 character = character || '';
22036 if (width > actualString.length) {
22038 return concat(character, (width - actualString.length)) + actualString;
22040 return actualString + concat(character, (width - actualString.length));
22043 return actualString;
22046 String.prototype.lPad = function (width, character) {
22047 return pad(this, width, character, true);
22050 String.prototype.rPad = function (width, character) {
22051 return pad(this, width, character, false);
22054 if (!Array.prototype.indexOf) {
22055 Array.prototype.indexOf = function (val) {
22056 for (var index = 0; index < this.length; index++) {
22057 if (this[index] === val) {
22065 if (!Array.prototype.regexIndexOf) {
22066 Object.defineProperty(Array.prototype, 'regexIndexOf', {
22068 value: function (regex, startIndex, loop) {
22069 startIndex = startIndex && startIndex > -1 ? startIndex : 0;
22070 for (var index = startIndex; index < this.length; index++) {
22071 if (this[index].toString().match(regex)) {
22076 for (var index = 0; index < startIndex; index++) {
22077 if (this[index].toString().match(regex)) {
22087 angular.module("b2bTemplate/audioPlayer/audioPlayer.html", []).run(["$templateCache", function($templateCache) {
22088 $templateCache.put("b2bTemplate/audioPlayer/audioPlayer.html",
22089 "<div class=\"b2b-audio\">\n" +
22090 " <audio preload=\"auto\">\n" +
22091 " <source ng-src=\"{{audio.mp3 | trustedAudioUrl}}\" type=\"audio/mp3\"></source>\n" +
22092 " <i>Your browser does not support the audio element.</i>\n" +
22095 " <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" +
22096 " <i class=\"icoControls-pointer\" ng-show='!isPlayInProgress'></i>\n" +
22097 " <i class=\"icoControls-pause\" ng-show='isPlayInProgress'></i>\n" +
22100 " <div class=\"seek-bar-container-wrapper\">\n" +
22101 " <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" +
22102 " <div class=\"timing-container\">\n" +
22103 " <span class=\"timing-container-left\">{{timeFormatter(audio.currentTime)}}</span>\n" +
22104 " <span class=\"timing-container-right\">{{timeFormatter(audio.duration)}}</span>\n" +
22105 " <div class=\"timing-container-spacer\"></div>\n" +
22109 " <b2b-flyout>\n" +
22110 " <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" +
22111 " <i class=\"icoControls-mutespeakers\" ng-show=\"audio.currentVolume === 0\"></i>\n" +
22112 " <i class=\"icoControls-volumedown\" ng-show=\"audio.currentVolume > 0 && audio.currentVolume <= 50\"></i>\n" +
22113 " <i class=\"icoControls-volumeup\" ng-show=\"audio.currentVolume > 50\"></i>\n" +
22116 " <b2b-flyout-content horizontal-placement=\"center\" flyout-style=\"width:70px; height:190px;\" vertical-placement=\"above\">\n" +
22117 " <div class=\"b2b-audio-popover text-center\">\n" +
22118 " <span>Max</span>\n" +
22119 " <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" +
22120 " <div class=\"min-label\">Min</div>\n" +
22122 " </b2b-flyout-content>\n" +
22123 " </b2b-flyout>\n" +
22127 angular.module("b2bTemplate/audioRecorder/audioRecorder.html", []).run(["$templateCache", function($templateCache) {
22128 $templateCache.put("b2bTemplate/audioRecorder/audioRecorder.html",
22129 "<div class=\"b2b-audio-recorder row\">\n" +
22130 " <div class=\"b2b-elapsed-time span11\">\n" +
22131 " <div ng-if=\"isRecording\">\n" +
22132 " <span style=\"padding-right: 25px;\">{{config.whileRecordingMessage}}</span>\n" +
22133 " <span>{{timeFormatter(elapsedTime)}}</span>\n" +
22135 " <span ng-if=\"!isRecording\">{{config.startRecordingMessage}}</span>\n" +
22137 " <div class=\"b2b-controls\" title=\"{{isRecording ? 'Stop' : 'REC'}}\" b2b-accessibility-click=\"13,32\" ng-click=\"toggleRecording()\" role=\"button\">\n" +
22138 " <i ng-if=\"isRecording\" class=\"icoControls-stop\" ></i>\n" +
22139 " <i ng-if=\"!isRecording\" class=\"icoControls-record\"></i>\n" +
22144 angular.module("b2bTemplate/backToTop/backToTop.html", []).run(["$templateCache", function($templateCache) {
22145 $templateCache.put("b2bTemplate/backToTop/backToTop.html",
22146 "<button class=\"btn-arrow b2b-backtotop-button\" type=\"button\" aria-label=\"Back to top\">\n" +
22147 " <div class=\"btn-secondary b2b-top-btn\">\n" +
22148 " <i class=\"icoControls-upPRIMARY\" role=\"img\"></i>\n" +
22154 angular.module("b2bTemplate/boardstrip/b2bAddBoard.html", []).run(["$templateCache", function($templateCache) {
22155 $templateCache.put("b2bTemplate/boardstrip/b2bAddBoard.html",
22156 "<div tabindex=\"0\" role=\"menuitem\" b2b-accessibility-click=\"13,32\" ng-click=\"addBoard()\" aria-label=\"Add Board\" class=\"boardstrip-item--add\">\n" +
22157 " <div class=\"centered\"><i aria-hidden=\"true\" class=\"icoControls-add-maximize\"></i> Add board</div>\n" +
22161 angular.module("b2bTemplate/boardstrip/b2bBoard.html", []).run(["$templateCache", function($templateCache) {
22162 $templateCache.put("b2bTemplate/boardstrip/b2bBoard.html",
22163 "<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" +
22164 " <div ng-transclude></div>\n" +
22165 " <div class=\"board-caret\" ng-if=\"getCurrentIndex()===boardIndex\">\n" +
22166 " <div class=\"board-caret-indicator\"></div>\n" +
22167 " <div class=\"board-caret-arrow-up\"></div>\n" +
22172 angular.module("b2bTemplate/boardstrip/b2bBoardstrip.html", []).run(["$templateCache", function($templateCache) {
22173 $templateCache.put("b2bTemplate/boardstrip/b2bBoardstrip.html",
22174 "<div class=\"b2b-boardstrip\">\n" +
22175 " <div class=\"boardstrip-reel\" role=\"menu\">\n" +
22176 " <div class=\"prev-items\">\n" +
22177 " <!-- <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" +
22178 " <button class=\"btn-arrow arrow\" b2b-accessibility-click=\"13,32\" ng-click=\"prevBoard()\" ng-disabled=\"!isPrevBoard()\">\n" +
22179 " <div class=\"btn btn-small btn-alt\"><i class=\"icon-primary-left\"></i>\n" +
22181 " <span class=\"offscreen-text\">Previous boards</span>\n" +
22184 " <div b2b-add-board on-add-board=\"onAddBoard()\"></div>\n" +
22185 " <div class=\"board-viewport\"><ul role=\"menu\" class=\"boardstrip-container\" ng-transclude></ul></div>\n" +
22186 " <div class=\"next-items\">\n" +
22187 " <!-- <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" +
22188 " <button class=\"btn-arrow arrow\" b2b-accessibility-click=\"13,32\" ng-click=\"nextBoard()\" ng-disabled=\"!isNextBoard()\">\n" +
22189 " <div class=\"btn btn-small btn-alt\"><i class=\"icon-primary-right\"></i>\n" +
22191 " <span class=\"offscreen-text\">Next boards</span>\n" +
22199 angular.module("b2bTemplate/calendar/datepicker-popup.html", []).run(["$templateCache", function($templateCache) {
22200 $templateCache.put("b2bTemplate/calendar/datepicker-popup.html",
22201 "<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" +
22202 " <div class=\"datepicker-days\" style=\"display: block;\">\n" +
22203 " <div ng-repeat=\"header in headers\" class=\"text-left\" style=\"width: 100%;\" b2b-append-element=\"header\"></div>\n" +
22204 " <table class=\"table-condensed\">\n" +
22207 " <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" +
22208 " <th id=\"month\" tabindex=\"-1\" aria-label=\"{{title}}\" class=\"datepicker-switch\" colspan=\"{{rows[0].length - 2}}\">{{title}}</th>\n" +
22209 " <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" +
22211 " <tr ng-show=\"labels.length > 0\">\n" +
22212 " <th id=\"{{label.post}}\" class=\"dow\" ng-repeat=\"label in labels\" aria-hidden=\"true\"><span aria-hidden=\"true\">{{label.pre}}</span></th>\n" +
22216 " <tr ng-repeat=\"row in rows\">\n" +
22217 " <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" +
22218 " <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" +
22222 " <tr ng-repeat=\"footer in footers\">\n" +
22223 " <th colspan=\"7\" class=\"text-left\" style=\"width: 278px;\" b2b-append-element=\"footer\"></th>\n" +
22231 angular.module("b2bTemplate/calendar/datepicker.html", []).run(["$templateCache", function($templateCache) {
22232 $templateCache.put("b2bTemplate/calendar/datepicker.html",
22234 " <span class=\"icon-primary-calendar span12\" ng-class=\"{'disabled': ngDisabled}\" ng-transclude></span>\n" +
22238 angular.module("b2bTemplate/coachmark/coachmark.html", []).run(["$templateCache", function($templateCache) {
22239 $templateCache.put("b2bTemplate/coachmark/coachmark.html",
22240 "<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" +
22241 " <i class=\"b2b-coachmark-caret\"></i>\n" +
22242 " <div class=\"b2b-coachmark-header\">\n" +
22243 " <div class=\"b2b-coachmark-countlabel\"><span ng-if=\"coachmarkIndex !== 0\">{{coachmarkIndex}} of {{(coachmarks.length-1)}}<span></div>\n" +
22244 " <div class=\"corner-button\">\n" +
22245 " <button type=\"button\" ng-focus=\"closeButtonFocus()\" class=\"close\" title=\"close\" aria-label=\"Close\" ng-click=\"closeCoachmark()\"></button>\n" +
22248 " <div class=\"b2b-coachmark-content\"> \n" +
22249 " <i class=\"icon-misc-dimmer\"></i>\n" +
22250 " <div class=\"b2b-coachmark-content-header\"><span class=\"offscreen-text\">{{currentCoachmark.offscreenText}}</span>{{currentCoachmark.contentHeader}}</div>\n" +
22251 " <div class=\"b2b-coachmark-description\">{{currentCoachmark.content}}</div>\n" +
22252 " <div class=\"b2b-coachmark-btn-group\">\n" +
22253 " <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" +
22254 " <button class=\"btn btn-alt\" ng-if=\"currentCoachmark.buttonLabel !== '' && currentCoachmark.buttonLabel !== undefined\" ng-click=\"actionCoachmark(currentCoachmark.buttonLabel)\">{{currentCoachmark.buttonLabel}}</button>\n" +
22260 angular.module("b2bTemplate/dropdowns/b2bDropdownDesktop.html", []).run(["$templateCache", function($templateCache) {
22261 $templateCache.put("b2bTemplate/dropdowns/b2bDropdownDesktop.html",
22262 "<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" +
22263 " <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" +
22264 " <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" +
22265 " <div ng-class=\"{'selectWrapper': (isInputDropdown), 'moduleWrapper': (!isInputDropdown)}\">\n" +
22266 " <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" +
22267 " <ul class=\"module-optinalcta\" ng-if=\"toggleFlag && optionalCta\" tabindex=\"-1\" aria-hidden=\"false\">\n" +
22268 " <li class=\"module-list-item\" tabindex=\"-1\" role=\"menuitem\" value=\"\" aria-label=\"Optinal CTA\" aria-selected=\"true\">\n" +
22269 " <span class=\"module-data\" b2b-append-element=\"optionalCta\"></span>\n" +
22273 "<i class=\"icon-primary-down\" aria-hidden=\"true\"></i>\n" +
22277 angular.module("b2bTemplate/dropdowns/b2bDropdownGroupDesktop.html", []).run(["$templateCache", function($templateCache) {
22278 $templateCache.put("b2bTemplate/dropdowns/b2bDropdownGroupDesktop.html",
22279 "<li b2b-dropdown-group-desktop class=\"optgroup-wrapper\">{{groupHeader}}\n" +
22280 " <ul class=\"optgroup\" role=\"group\" aria-label=\"{{groupHeader}}\"></ul>\n" +
22284 angular.module("b2bTemplate/dropdowns/b2bDropdownListDesktop.html", []).run(["$templateCache", function($templateCache) {
22285 $templateCache.put("b2bTemplate/dropdowns/b2bDropdownListDesktop.html",
22286 "<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>");
22289 angular.module("b2bTemplate/fileUpload/fileUpload.html", []).run(["$templateCache", function($templateCache) {
22290 $templateCache.put("b2bTemplate/fileUpload/fileUpload.html",
22291 "<label class=\"b2b-file-container\">\n" +
22292 " <span class=\"b2b-upload-link\" ng-transclude></span>\n" +
22293 " <input type=\"file\" b2b-file-change>\n" +
22297 angular.module("b2bTemplate/flyout/flyout.html", []).run(["$templateCache", function($templateCache) {
22298 $templateCache.put("b2bTemplate/flyout/flyout.html",
22299 "<span class=\"b2b-flyout\" b2b-flyout-trap-focus-inside>\n" +
22300 " <span ng-transclude></span>\n" +
22304 angular.module("b2bTemplate/flyout/flyoutContent.html", []).run(["$templateCache", function($templateCache) {
22305 $templateCache.put("b2bTemplate/flyout/flyoutContent.html",
22306 "<div class=\"b2b-flyout-container\" aria-live=\"polite\" aria-atomic=\"false\" ng-class=\"{'b2b-flyout-left':horizontalPlacement==='left',\n" +
22307 " 'b2b-flyout-center':horizontalPlacement==='center', \n" +
22308 " 'b2b-flyout-right':horizontalPlacement==='right',\n" +
22309 " 'b2b-flyout-centerLeft':horizontalPlacement==='centerLeft',\n" +
22310 " 'b2b-flyout-centerRight':horizontalPlacement==='centerRight', \n" +
22311 " 'b2b-flyout-above':verticalPlacement==='above', \n" +
22312 " 'b2b-flyout-below':verticalPlacement==='below',\n" +
22313 " 'open-flyout': openFlyout,\n" +
22314 " 'b2b-close-flyout': !openFlyout}\">\n" +
22315 " <i class=\"b2b-flyout-caret\" ng-class=\"{'open-flyout': openFlyout, \n" +
22316 " 'b2b-flyout-caret-above':verticalPlacement==='above',\n" +
22317 " 'b2b-flyout-caret-below':verticalPlacement==='below'}\"></i>\n" +
22318 " <span ng-transclude></span>\n" +
22322 angular.module("b2bTemplate/footer/footer_column_switch_tpl.html", []).run(["$templateCache", function($templateCache) {
22323 $templateCache.put("b2bTemplate/footer/footer_column_switch_tpl.html",
22324 "<div class=\"footer-columns \" ng-class=\"{'five-column':footerColumns===5, 'four-column':footerColumns===4, 'three-column':footerColumns===3}\" ng-repeat=\"item in footerLinkItems\">\n" +
22325 " <h3 class=\"b2b-footer-header\">{{item.categoryName}}</h3>\n" +
22327 " <li ng-repeat=\"i in item.values\">\n" +
22328 " <a href=\"{{i.href}}\" title=\"{{item.categoryName}} - {{i.displayLink}}\">{{i.displayLink}}</a> \n" +
22334 "<div ng-transclude></div>\n" +
22338 angular.module("b2bTemplate/horizontalTable/horizontalTable.html", []).run(["$templateCache", function($templateCache) {
22339 $templateCache.put("b2bTemplate/horizontalTable/horizontalTable.html",
22340 "<div class=\"b2b-horizontal-table\">\n" +
22341 " <div class=\"b2b-horizontal-table-arrows row span12\">\n" +
22342 " <div class=\"span4 b2b-prev-link\">\n" +
22343 " <a href=\"javascript:void(0)\" ng-click=\"moveViewportLeft()\" ddh-accessibility-click=\"13,32\" ng-if=\"!disableLeft\">Previous</a>\n" +
22344 " <span ng-if=\"disableLeft\" class=\"b2b-disabled-text\">Previous</span>\n" +
22347 " <span class=\"span5 b2b-horizontal-table-column-info\" aria-live=\"polite\" tabindex=\"-1\">\n" +
22348 " Showing {{countDisplayText}} {{getColumnSet()[0]+1}}-{{getColumnSet()[1]+1}} of {{numOfCols}} columns\n" +
22351 " <div ng-if=\"legendContent\" class=\"span2 b2b-horizontal-table-legend\">\n" +
22352 " | <b2b-flyout>\n" +
22353 " <div tabindex=\"0\" role=\"button\" aria-haspopup=\"true\" b2b-flyout-toggler b2b-accessibility-click=\"13,32\" aria-expanded=\"{{flyoutOpened ? 'true' : 'false'}}\">\n" +
22355 " <i class=\"icoControls-down\" role=\"img\"></i>\n" +
22357 " <b2b-flyout-content horizontal-placement=\"center\" vertical-placement=\"below\">\n" +
22358 " <div ng-bind-html=\"legendContent\"></div>\n" +
22359 " </b2b-flyout-content>\n" +
22360 " </b2b-flyout>\n" +
22363 " <div class=\"span3 text-right b2b-next-link\">\n" +
22364 " <a href=\"javascript:void(0)\" ng-click=\"moveViewportRight()\" ddh-accessibility-click=\"13,32\" ng-if=\"!disableRight\">Next</a>\n" +
22365 " <span ng-if=\"disableRight\" class=\"b2b-disabled-text\">Next</span>\n" +
22368 " <div class=\"b2b-horizontal-table-inner-container\">\n" +
22369 " <span ng-transclude></span>\n" +
22374 angular.module("b2bTemplate/hourPicker/b2bHourpicker.html", []).run(["$templateCache", function($templateCache) {
22375 $templateCache.put("b2bTemplate/hourPicker/b2bHourpicker.html",
22376 "<div class=\"hp-container\">\n" +
22377 " <div class=\"hp-selected\">\n" +
22378 " <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" +
22380 " <div b2b-hourpicker-panel></div>\n" +
22384 angular.module("b2bTemplate/hourPicker/b2bHourpickerPanel.html", []).run(["$templateCache", function($templateCache) {
22385 $templateCache.put("b2bTemplate/hourPicker/b2bHourpickerPanel.html",
22386 "<form name=\"{{'hourpickerForm' + $id}}\">\n" +
22387 " <div class=\"hp-checkbox\" role=\"group\">\n" +
22388 " <label class=\"checkbox\" for=\"checkbox_{{dayOption.title}}_{{$id}}\" ng-repeat=\"dayOption in hourpicker.dayOptions\">\n" +
22389 " <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" +
22392 " <div class=\"row hp-dropdowns\">\n" +
22393 " <div class=\"span4\">\n" +
22394 " <label for=\"{{'hourpickerStartTime' + $id}}\">From</label>\n" +
22395 " <select id=\"{{'hourpickerStartTime' + $parent.$id}}\" b2b-dropdown type=\"\" ng-model=\"hourpickerPanelValue.startTime\">\n" +
22396 " <option b2b-dropdown-list value=\"\">From</option>\n" +
22397 " <option b2b-dropdown-list option-repeat=\"startTimeOption in hourpicker.startTimeOptions\" value=\"{{startTimeOption}}\">{{startTimeOption}}</option>\n" +
22400 " <div class=\"span8 radio-buttons\" role=\"radiogroup\" aria-label=\"Select AM or PM\">\n" +
22401 " <label class=\"radio\" for=\"hourpickerStartMeridiem_AM_{{$id}}\">\n" +
22402 " <input type=\"radio\" id=\"hourpickerStartMeridiem_AM_{{$id}}\" name=\"{{'hourpickerStartMeridiem' + $id}}\" value=\"am\" ng-model=\"hourpickerPanelValue.startMeridiem\" /><i class=\"skin\"></i><span>AM</span>\n" +
22404 " <label class=\"radio\" for=\"hourpickerStartMeridiem_PM_{{$id}}\">\n" +
22405 " <input type=\"radio\" id=\"hourpickerStartMeridiem_PM_{{$id}}\" name=\"{{'hourpickerStartMeridiem' + $id}}\" value=\"pm\" ng-model=\"hourpickerPanelValue.startMeridiem\" /><i class=\"skin\"></i><span>PM</span>\n" +
22409 " <div class=\"row hp-dropdowns\">\n" +
22410 " <div class=\"span4\">\n" +
22411 " <label for=\"{{'hourpickerEndTime' + $id}}\">To</label>\n" +
22412 " <select id=\"{{'hourpickerEndTime' + $parent.$id}}\" b2b-dropdown ng-model=\"hourpickerPanelValue.endTime\">\n" +
22413 " <option b2b-dropdown-list value=\"\">To</option>\n" +
22414 " <option b2b-dropdown-list option-repeat=\"endTimeOption in hourpicker.endTimeOptions\" value=\"{{endTimeOption}}\">{{endTimeOption}}</option>\n" +
22417 " <div class=\"span8 radio-buttons\" role=\"radiogroup\" aria-label=\"Select AM or PM\">\n" +
22418 " <label class=\"radio\" for=\"hourpickerEndMeridiem_AM_{{$id}}\">\n" +
22419 " <input type=\"radio\" id=\"hourpickerEndMeridiem_AM_{{$id}}\" name=\"{{'hourpickerEndMeridiem' + $id}}\" value=\"am\" ng-model=\"hourpickerPanelValue.endMeridiem\" /><i class=\"skin\"></i><span>AM</span>\n" +
22421 " <label class=\"radio\" for=\"hourpickerEndMeridiem_PM_{{$id}}\">\n" +
22422 " <input type=\"radio\" id=\"hourpickerEndMeridiem_PM_{{$id}}\" name=\"{{'hourpickerEndMeridiem' + $id}}\" value=\"pm\" ng-model=\"hourpickerPanelValue.endMeridiem\" /><i class=\"skin\"></i><span>PM</span>\n" +
22426 " <div class=\"row hp-buttons\">\n" +
22427 " <div class=\"span12\">\n" +
22428 " <div style=\"float:right\">\n" +
22429 " <button class=\"btn btn-secondary btn-small\" ng-click=\"resetHourpickerPanelValue()\">Clear</button>\n" +
22430 " <button class=\"btn btn-alt btn-small\" ng-disabled = \"disableAddBtn\" ng-click=\"updateHourpickerValue()\">{{hourpicker.editMode > -1 ? 'Update' : 'Add'}}</button>\n" +
22437 angular.module("b2bTemplate/hourPicker/b2bHourpickerValue.html", []).run(["$templateCache", function($templateCache) {
22438 $templateCache.put("b2bTemplate/hourPicker/b2bHourpickerValue.html",
22439 "<div class=\"selected-days\">\n" +
22440 " <span class=\"day\">{{hourpickerValue.days}} {{hourpickerValue.startTime}} {{hourpickerValue.startMeridiem}} - {{hourpickerValue.endTime}} {{hourpickerValue.endMeridiem}}</span>\n" +
22441 " <span style=\"float:right\">\n" +
22442 " <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" +
22443 " <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" +
22445 " <div style=\"clear:both\"></div>\n" +
22449 angular.module("b2bTemplate/leftNavigation/leftNavigation.html", []).run(["$templateCache", function($templateCache) {
22450 $templateCache.put("b2bTemplate/leftNavigation/leftNavigation.html",
22451 "<div class=\"b2b-nav-menu\">\n" +
22452 " <div class=\"b2b-subnav-container\">\n" +
22453 " <ul class=\"b2b-subnav-content\">\n" +
22454 " <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" +
22455 " <ul ng-class=\"{expand: idx==$index}\">\n" +
22456 " <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" +
22464 angular.module("b2bTemplate/listbox/listbox.html", []).run(["$templateCache", function($templateCache) {
22465 $templateCache.put("b2bTemplate/listbox/listbox.html",
22466 "<div class=\"b2b-list-box\" tabindex=\"0\" role=\"listbox\" ng-transclude>\n" +
22470 angular.module("b2bTemplate/modalsAndAlerts/b2b-backdrop.html", []).run(["$templateCache", function($templateCache) {
22471 $templateCache.put("b2bTemplate/modalsAndAlerts/b2b-backdrop.html",
22472 "<div class=\"b2b-modal-backdrop fade in hidden-by-modal\" aria-hidden=\"true\" tabindex=\"-1\"></div>");
22475 angular.module("b2bTemplate/modalsAndAlerts/b2b-window.html", []).run(["$templateCache", function($templateCache) {
22476 $templateCache.put("b2bTemplate/modalsAndAlerts/b2b-window.html",
22477 "<div class=\"modalwrapper active {{windowClass}}\" ng-class=\"{'modal-landscape': isModalLandscape}\" role=\"{{isNotifDialog?'alertdialog':'dialog'}}\" tabindex=\"-1\" aria-labelledby=\"{{title}}\" aria-describedby=\"{{content}}\">\n" +
22478 " <div class=\"modal fade in {{sizeClass}}\" ng-transclude></div>\n" +
22482 angular.module("b2bTemplate/monthSelector/monthSelector-popup.html", []).run(["$templateCache", function($templateCache) {
22483 $templateCache.put("b2bTemplate/monthSelector/monthSelector-popup.html",
22484 "<div id=\"monthselector{{$id}}\" class=\"datepicker dropdown-menu monthselector\" \n" +
22485 " ng-class=\"{'datepicker-dropdown datepicker-orient-top': !inline, 'datepicker-orient-left': !inline && orientation === 'left', 'datepicker-orient-right': !inline && orientation === 'right'}\" \n" +
22486 " ng-style=\"{position: inline && 'relative', 'z-index': inline && '0', top: !inline && position.top + 'px' || 0, left: !inline && position.left + 'px'}\" \n" +
22487 " style=\"display: block;\" aria-hidden=\"false\" role=\"dialog presentation\" tabindex=\"-1\">\n" +
22488 " <div class=\"datepicker-days\" style=\"display: block;\">\n" +
22489 " <span class=\"offscreen-text\" aria-live=\"polite\" aria-atomic=\"true\">{{title}} displaying</span>\n" +
22490 " <table class=\"table-condensed\" role=\"grid\">\n" +
22492 " <tr ng-repeat=\"header in headers\">\n" +
22493 " <th colspan=\"7\" class=\"text-left\" style=\"width: 278px;\" b2b-append-element=\"header\"></th>\n" +
22496 " <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" +
22497 " <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" +
22498 " <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" +
22500 " <tr ng-show=\"labels.length > 0\">\n" +
22501 " <th id=\"{{label.post}}\" class=\"dow\" ng-repeat=\"label in labels\" aria-hidden=\"true\"><span aria-hidden=\"true\">{{label.pre}}</span></th>\n" +
22504 " <tbody b2b-key type=\"table\" columns=\"4\" >\n" +
22505 " <tr ng-repeat=\"row in rows\">\n" +
22506 " <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" +
22507 " <div aria-hidden=\"true\" tabindex=\"-1\" class=\"show-date\" ng-if=\"!(dt.oldMonth || dt.nextMonth)\">{{dt.label | limitTo: 3}}</div>\n" +
22512 " <tr ng-repeat=\"footer in footers\">\n" +
22513 " <th colspan=\"7\" class=\"text-left\" b2b-append-element=\"footer\"></th>\n" +
22521 angular.module("b2bTemplate/monthSelector/monthSelector.html", []).run(["$templateCache", function($templateCache) {
22522 $templateCache.put("b2bTemplate/monthSelector/monthSelector.html",
22524 " <span class=\"icon-primary-calendar span12\" ng-class=\"{'disabled': ngDisabled}\" ng-transclude></span>\n" +
22528 angular.module("b2bTemplate/monthSelector/monthSelectorLink.html", []).run(["$templateCache", function($templateCache) {
22529 $templateCache.put("b2bTemplate/monthSelector/monthSelectorLink.html",
22531 " <span class=\"span12\" ng-transclude></span>\n" +
22535 angular.module("b2bTemplate/pagination/b2b-pagination.html", []).run(["$templateCache", function($templateCache) {
22536 $templateCache.put("b2bTemplate/pagination/b2b-pagination.html",
22537 "<div class=\"b2b-pager\">\n" +
22538 " <div ng-if=\"notMobile && totalPages > 1\">\n" +
22539 " <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" +
22540 " <i class=\"icon-primary-left\"></i>\n" +
22542 " <a droppable=\"{{droppableAttribute}}\" tabindex=\"{{currentPage === 1 ? -1 : 0}}\" ng-class=\"{'b2b-pager__item--noclick': currentPage === 1}\" class=\"b2b-pager__item b2b-pager__item--link\" title=\"Page 1{{checkSelectedPage(page) ? ' selected' : '' }}\" ng-if=\"totalPages > 10 && currentPage > 6\" b2b-accessibility-click=\"13,32\" ng-click=\"selectPage(1, $event)\" aria-atomic=\"true\" aria-live=\"polite\">\n" +
22543 " 1<span class=\"offscreen-text\" ng-if=\"currentPage === 1\"> is selected</span>\n" +
22546 " <span class=\"b2b-pager__item\" ng-if=\"totalPages > 10 && currentPage > 6\">...</span>\n" +
22548 " <a droppable=\"{{droppableAttribute}}\" tabindex=\"{{checkSelectedPage(page) ? -1 : 0}}\" href=\"javascript:void(0);\" class=\"b2b-pager__item b2b-pager__item--link\" title=\"Page {{page}}\" b2b-element-focus=\"isFocused(page)\" ng-repeat=\"page in pages\" ng-class=\"{'b2b-pager__item--active': checkSelectedPage(page), 'b2b-pager__item--noclick': checkSelectedPage(page),'b2b-pager__item--droppable': droppableAttribute == true}\" b2b-accessibility-click=\"13,32\" ng-click=\"!checkSelectedPage(page) && selectPage(page, $event)\" aria-atomic=\"true\" aria-live=\"polite\">\n" +
22549 " {{page}}<span class=\"offscreen-text\" ng-if=\"checkSelectedPage(page)\"> is selected</span>\n" +
22552 " <span class=\"b2b-pager__item\" ng-if=\"totalPages > 10 && currentPage <= (totalPages - boundary)\">...</span>\n" +
22554 " <a droppable=\"{{droppableAttribute}}\" tabindex=\"{{currentPage === totalPages ? -1 : 0}}\" href=\"javascript:void(0);\" ng-class=\"{'b2b-pager__item--noclick': currentPage === totalPages}\" class=\"b2b-pager__item b2b-pager__item--link\" title=\"Page {{totalPages}}\" ng-if=\"totalPages > 10 && currentPage <= (totalPages - boundary)\" b2b-accessibility-click=\"13,32\" ng-click=\"selectPage(totalPages, $event)\" aria-atomic=\"true\" aria-live=\"polite\">\n" +
22555 " {{totalPages}}<span class=\"offscreen-text\" ng-if=\"checkSelectedPage(page)\"> is selected</span>\n" +
22559 " <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" +
22560 " <i class=\"icon-primary-right\"></i>\n" +
22563 " <div class=\"fieldLabel b2b-go-to-page\" ng-class=\"{'b2b-go-to-page-inline' : inputClass !== undefined }\" ng-show=\"totalPages > 20\"> \n" +
22564 " <label for=\"{{inputId}}\">Go to Page:</label>\n" +
22565 " <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" +
22566 " <button class=\"btn-arrow\" ng-click=\"gotoBtnClick()\" ng-disabled=\"$parent.gotoPage !== 0 || $parent.gotoPage !== undefined\" aria-label=\"Go to\">\n" +
22567 " <div class=\"btn btn-small btn-secondary\">\n" +
22568 " <i class=\"icon-primary-right\"></i>\n" +
22573 " <div ng-if=\"isMobile && totalPages > 1\" ng-swipe-right=\"prev()\" ng-swipe-left=\"next()\" ng-class=\"isMobile ? 'b2b-mobile-view': '' \">\n" +
22574 " <a droppable=\"{{droppableAttribute}}\" tabindex=\"{{checkSelectedPage(page) ? -1 : 0}}\" href=\"javascript:void(0);\" class=\"b2b-pager__item b2b-pager__item--link\" title=\"Page {{page}}\" b2b-element-focus=\"isFocused(page)\" ng-repeat=\"page in pages\" ng-class=\"{'b2b-pager__item--active': checkSelectedPage(page), fade1: ($index == 0 && currentPage > meanVal+1), fade2: ($index == 1 && currentPage > meanVal+1), fadesl: ($index == pages.length-2 && currentPage < totalPages - meanVal), fadel: ($last && currentPage < totalPages - meanVal), 'b2b-pager__item--noclick': checkSelectedPage(page)}\" b2b-accessibility-click=\"13,32\" ng-click=\"selectPage(page, $event)\">{{page}}</a>\n" +
22580 angular.module("b2bTemplate/paneSelector/paneSelector.html", []).run(["$templateCache", function($templateCache) {
22581 $templateCache.put("b2bTemplate/paneSelector/paneSelector.html",
22582 "<div class=\"panes\" ng-transclude></div>");
22585 angular.module("b2bTemplate/paneSelector/paneSelectorPane.html", []).run(["$templateCache", function($templateCache) {
22586 $templateCache.put("b2bTemplate/paneSelector/paneSelectorPane.html",
22587 "<div class=\"pane-block\" ng-transclude></div>");
22590 angular.module("b2bTemplate/profileCard/profileCard-addUser.html", []).run(["$templateCache", function($templateCache) {
22591 $templateCache.put("b2bTemplate/profileCard/profileCard-addUser.html",
22592 "<div class=\"span3 b2b-profile-card b2b-add-user\">\n" +
22593 " <div class=\"atcenter\">\n" +
22594 " <div class=\"circle\"><i class=\"icon-primary-add-maximize\"></i></div>\n" +
22595 " <div>Create new user</div>\n" +
22600 angular.module("b2bTemplate/profileCard/profileCard.html", []).run(["$templateCache", function($templateCache) {
22601 $templateCache.put("b2bTemplate/profileCard/profileCard.html",
22602 "<div class=\"span3 b2b-profile-card\">\n" +
22603 " <div class=\"top-block\">\n" +
22604 " <div class=\"profile-image\">\n" +
22605 " <img ng-if=\"image\" profile-name=\"{{profile.name}}\" ng-src=\"{{profile.img}}\" alt=\"{{profile.name}}\">\n" +
22606 " <span ng-if=\"!image\" class=\"default-img\">{{initials}}</span>\n" +
22608 " <h4 class=\"name\">{{profile.name}}</h4>\n" +
22610 " <p class=\"status\">\n" +
22611 " <span class=\"status-icon\" ng-class=\"{'status-green':colorIcon==='green','status-red':colorIcon==='red','status-blue':colorIcon==='blue','status-yellow':colorIcon==='yellow'}\"> \n" +
22613 " <span>{{profile.state}}<span ng-if=\"badge\" class=\"status-badge\">Admin</span></span>\n" +
22617 " <div class=\"bottom-block\">\n" +
22618 " <div class=\"profile-details\">\n" +
22619 " <label>Username</label>\n" +
22620 " <div ng-if=\"shouldClip(profile.userName)\" ng-mouseover=\"showUserNameTooltip = true;\" ng-mouseleave=\"showUserNameTooltip = false;\">\n" +
22621 " <div ng-if=\"shouldClip(profile.userName)\" class=\"tooltip\" b2b-tooltip show-tooltip=\"showUserNameTooltip\">\n" +
22622 " {{profile.userName.slice(0, 25)+'...'}}\n" +
22623 " <div class=\"arrow\"></div>\n" +
22624 " <div class=\"tooltip-wrapper\" role=\"tooltip\" aria-live=\"polite\" aria-atomic=\"false\" style=\"z-index:1111\">\n" +
22625 " <div class=\"tooltip-size-control\">\n" +
22626 " <div class=\"helpertext\" tabindex=\"-1\" role=\"tooltip\">\n" +
22627 " {{profile.userName}}\n" +
22633 " <div ng-if=\"!shouldClip(profile.userName)\">\n" +
22634 " {{profile.userName}}\n" +
22636 " <label>Email</label>\n" +
22637 " <div ng-if=\"shouldClip(profile.email)\" ng-mouseover=\"showEmailTooltip = true;\" ng-mouseleave=\"showEmailTooltip = false;\">\n" +
22638 " <div ng-if=\"shouldClip(profile.email)\" class=\"tooltip\" data-placement=\"bottom\" b2b-tooltip show-tooltip=\"showEmailTooltip\">\n" +
22639 " {{profile.email.slice(0, 25)+'...'}}\n" +
22640 " <div class=\"arrow\"></div>\n" +
22641 " <div class=\"tooltip-wrapper\" role=\"tooltip\" aria-live=\"polite\" aria-atomic=\"false\" style=\"z-index:1111\">\n" +
22642 " <div class=\"tooltip-size-control\">\n" +
22643 " <div class=\"helpertext\" tabindex=\"-1\" role=\"tooltip\">\n" +
22644 " {{profile.email}}\n" +
22650 " <div ng-if=\"!shouldClip(profile.email)\">\n" +
22651 " {{profile.email}}\n" +
22653 " <label>Role</label>\n" +
22654 " <div ng-if=\"shouldClip(profile.role)\" ng-mouseover=\"showRoleTooltip = true;\" ng-mouseleave=\"showRoleTooltip = false;\">\n" +
22655 " <div ng-if=\"shouldClip(profile.role)\" class=\"tooltip\" b2b-tooltip show-tooltip=\"showRoleTooltip\">\n" +
22656 " {{profile.role.slice(0, 25)+'...'}}\n" +
22657 " <div class=\"tooltip-wrapper\" role=\"tooltip\" aria-live=\"polite\" aria-atomic=\"false\" style=\"z-index:1111\">\n" +
22658 " <div class=\"tooltip-size-control\">\n" +
22659 " <div class=\"helpertext\" tabindex=\"-1\" role=\"tooltip\">\n" +
22660 " {{profile.role}}\n" +
22666 " <div ng-if=\"!shouldClip(profile.role)\">\n" +
22667 " {{profile.role}}\n" +
22669 " <label>Last login</label>\n" +
22670 " <div ng-if=\"shouldClip(profile.lastLogin)\" ng-mouseover=\"showLastLoginTooltip = true;\" ng-mouseleave=\"showLastLoginTooltip = false;\">\n" +
22671 " <div ng-if=\"shouldClip(profile.lastLogin)\" class=\"tooltip\" b2b-tooltip show-tooltip=\"showLastLoginTooltip\">\n" +
22672 " {{profile.lastLogin.slice(0, 25)+'...'}}\n" +
22673 " <div class=\"tooltip-wrapper\" role=\"tooltip\" aria-live=\"polite\" aria-atomic=\"false\" style=\"z-index:1111\">\n" +
22674 " <div class=\"tooltip-size-control\">\n" +
22675 " <div class=\"helpertext\" tabindex=\"-1\" role=\"tooltip\">\n" +
22676 " {{profile.lastLogin}}\n" +
22682 " <div ng-if=\"!shouldClip(profile.lastLogin)\">\n" +
22683 " {{profile.lastLogin}}\n" +
22690 angular.module("b2bTemplate/searchField/searchField.html", []).run(["$templateCache", function($templateCache) {
22691 $templateCache.put("b2bTemplate/searchField/searchField.html",
22692 "<div class=\"search-bar\">\n" +
22693 " <div class='input-container' ng-blur=\"blurInput()\">\n" +
22694 " <input type=\"text\" class=\"innershadow b2b-search-input-field\" id=\"{{configObj.labelId}}\" b2b-search-input ng-model=\"inputModel\" ng-disabled=\"disabled\" b2b-reset ng-keydown=\"selectionIndex($event)\" placeholder=\"{{configObj.ghostText}}\" style=\"width:100%\" maxlength=\"{{configObj.maxLength}}\" title=\"{{inputModel}}\" aria-label=\"{{inputModel.length>0?inputModel:configObj.ghostText}}\" />\n" +
22695 " <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" +
22697 " <div class=\"search-suggestion-wrapper\" ng-if=\"filterList.length >=0\" ng-show=\"showListFlag\">\n" +
22698 " <ul class=\"search-suggestion-list\" role=\"listbox\"> \n" +
22699 " <li class=\"no-result\" ng-if=\"filterList.length == 0 && configObj.display\">{{configObj.resultText}}</li>\n" +
22700 " <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" +
22701 " {{item.title}} \n" +
22708 angular.module("b2bTemplate/seekBar/seekBar.html", []).run(["$templateCache", function($templateCache) {
22709 $templateCache.put("b2bTemplate/seekBar/seekBar.html",
22710 "<div class=\"b2b-seek-bar-container\" ng-class=\"{vertical:verticalSeekBar}\">\n" +
22711 " <div class=\"b2b-seek-bar-track-container\">\n" +
22712 " <div class=\"b2b-seek-bar-track\"></div>\n" +
22713 " <div class=\"b2b-seek-bar-track-fill\"></div>\n" +
22715 " <div class=\"b2b-seek-bar-knob-container\" role=\"menu\" >\n" +
22716 " <div class=\"b2b-seek-bar-knob\" tabindex=\"0\" role=\"menuitem\"></div>\n" +
22721 angular.module("b2bTemplate/slider/slider.html", []).run(["$templateCache", function($templateCache) {
22722 $templateCache.put("b2bTemplate/slider/slider.html",
22723 "<div class=\"b2b-slider-container\" ng-class=\"{'vertical':verticalSlider}\">\n" +
22724 " <div class=\"slider-track-container\">\n" +
22725 " <div class=\"slider-track\"></div>\n" +
22726 " <div class=\"slider-track-fill\" ng-style=\"{backgroundColor:trackFillColor}\"></div>\n" +
22728 " <div class=\"slider-knob-container\" ng-class=\"{'slider-knob-hidden':hideKnob}\">\n" +
22729 " <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" +
22734 angular.module("b2bTemplate/spinButton/spinButton.html", []).run(["$templateCache", function($templateCache) {
22735 $templateCache.put("b2bTemplate/spinButton/spinButton.html",
22736 "<div class=\"btn-group btn-spinbutton-toggle\" ng-class=\"{'disabled': disabledFlag}\">\n" +
22737 " <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" +
22738 " <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" +
22739 " <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" +
22743 angular.module("b2bTemplate/statusTracker/statusTracker.html", []).run(["$templateCache", function($templateCache) {
22744 $templateCache.put("b2bTemplate/statusTracker/statusTracker.html",
22745 "<div class=\"b2b-status-tracker row\">\n" +
22746 " <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" +
22747 " <div class=\"btn btn-small btn-secondary\">\n" +
22748 " <i class=\"icon-primary-left\"></i>\n" +
22751 " <div ng-repeat=\"status in statuses\" class=\"b2b-status-tracker-step {{ status.state }}\" ng-show=\"isInViewport($index)\">\n" +
22752 " <p class=\"b2b-status-tracker-heading\">{{status.heading}}</p>\n" +
22753 " <div class=\"progress\">\n" +
22754 " <div class=\"progress-bar\">\n" +
22755 " <span class=\"hidden-spoken\">\n" +
22756 " {{ removeCamelCase(status.state) }}\n" +
22760 " <div class=\"b2b-status-tracker-estimate {{status.state}}\">\n" +
22761 " <i ng-show=\"status.estimate !== '' && status.estimate !== undefined\" ng-class=\"b2bStatusTrackerConfig.icons[status.state]\"></i>\n" +
22763 " <span ng-bind-html=\"status.estimate\"></span>\n" +
22766 " <div class=\"b2b-status-tracker-description\" ng-bind-html=\"status.description\"> \n" +
22769 " <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" +
22770 " <div class=\"btn btn-small btn-secondary\">\n" +
22771 " <i class=\"icon-primary-right\"></i>\n" +
22777 angular.module("b2bTemplate/stepTracker/stepTracker.html", []).run(["$templateCache", function($templateCache) {
22778 $templateCache.put("b2bTemplate/stepTracker/stepTracker.html",
22779 "<div class=\"b2b-step-tracker\">\n" +
22780 " <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" +
22781 " <div class=\"btn btn-left btn-small btn-secondary\"><i class=\"icon-primary-left\"></i></div>\n" +
22783 " <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" +
22784 " <div class=\"btn btn-small btn-right btn-secondary\"><i class=\"icon-primary-right\"></i></div>\n" +
22786 " <ul class=\"b2b-steps\">\n" +
22787 " <li ng-class=\"{'b2b-step-done':$index < currentIndex - 1 ,'b2b-step-on':$index == currentIndex - 1}\" \n" +
22788 " ng-repeat=\"stepsItem in stepsItemsObject\" ng-show=\"isInViewport($index)\">\n" +
22789 " <p class=\"b2b-step-text\" data-sm-text=\"{{stepsItem.dataMobile}}\" data-large-text=\"{{stepsItem.dataDesktop}}\">{{stepsItem.text}}</p>\n" +
22790 " <span class=\"hidden-spoken\">\n" +
22791 " {{($index < currentIndex - 1)? 'Complete. '+stepsItem.text+' '+stepsItem.dataMobile:''}} \n" +
22792 " {{($index == currentIndex - 1)? 'In Progress. '+stepsItem.text+' '+stepsItem.dataMobile:''}} \n" +
22793 " {{($index > currentIndex - 1)? 'Incomplete. '+stepsItem.text+' '+stepsItem.dataMobile:''}}\n" +
22800 angular.module("b2bTemplate/switches/switches-spanish.html", []).run(["$templateCache", function($templateCache) {
22801 $templateCache.put("b2bTemplate/switches/switches-spanish.html",
22802 "<div class=\"switch-overlay\" aria-hidden=\"true\">\n" +
22803 " <span class=\"btn-slider-on\"><span class=\"hidden-spoken\">seleccione para hacer </span><i class=\"activo\">activo</i></span>\n" +
22804 " <span class=\"switch-handle\"></span>\n" +
22805 " <span class=\"btn-slider-off\"><span class=\"hidden-spoken\">seleccione para hacer </span><i class=\"inactivo\">inactivo</i></span>\n" +
22809 angular.module("b2bTemplate/switches/switches.html", []).run(["$templateCache", function($templateCache) {
22810 $templateCache.put("b2bTemplate/switches/switches.html",
22811 "<div class=\"switch-overlay\" aria-hidden=\"true\">\n" +
22812 " <span class=\"btn-slider-on\">On</span>\n" +
22813 " <span class=\"switch-handle\"></span>\n" +
22814 " <span class=\"btn-slider-off\">Off</span>\n" +
22818 angular.module("b2bTemplate/tableMessages/tableMessage.html", []).run(["$templateCache", function($templateCache) {
22819 $templateCache.put("b2bTemplate/tableMessages/tableMessage.html",
22820 "<div class=\"b2b-table-message\">\n" +
22821 " <div class=\"b2b-message\" ng-if=\"msgType === 'noMatchingResults'\">\n" +
22822 " <div class=\"b2b-magnify-glass\"></div>\n" +
22824 " <div ng-transclude></div>\n" +
22827 " <div class=\"b2b-message\" ng-if=\"msgType == 'infoCouldNotLoad'\">\n" +
22828 " <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" +
22829 " <div>Oops!</div>\n" +
22830 " <div>The information could not load at this time.</div>\n" +
22831 " <div>Please <a href=\"javascript:void(0)\" title=\"Refresh the page\" ng-click=\"refreshAction($event)\">refresh the page</a>\n" +
22834 " <div class=\"b2b-message\" ng-if=\"msgType == 'magnifySearch'\">\n" +
22835 " <div class=\"b2b-magnify-glass\"></div>\n" +
22837 " <p class=\"b2b-message-title\">Please input values to\n" +
22838 " <br/> begin your search.</p>\n" +
22841 " <div class=\"b2b-message\" ng-if=\"msgType === 'loadingTable'\">\n" +
22842 " <div class=\"icon-primary-spinner-ddh b2b-loading-dots\"></div>\n" +
22843 " <div ng-transclude></div>\n" +
22849 angular.module("b2bTemplate/tableScrollbar/tableScrollbar.html", []).run(["$templateCache", function($templateCache) {
22850 $templateCache.put("b2bTemplate/tableScrollbar/tableScrollbar.html",
22851 "<div class=\"b2b-table-scrollbar\">\n" +
22852 " <div class=\"b2b-scrollbar-arrows\">\n" +
22853 " <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" +
22854 " <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" +
22856 " <div class=\"b2b-table-inner-container\">\n" +
22857 " <span ng-transclude></span>\n" +
22862 angular.module("b2bTemplate/tables/b2bTable.html", []).run(["$templateCache", function($templateCache) {
22863 $templateCache.put("b2bTemplate/tables/b2bTable.html",
22864 "<table ng-class=\"{'striped': responsive, 'complex-table': responsive && active}\" ng-transclude></table>");
22867 angular.module("b2bTemplate/tables/b2bTableBody.html", []).run(["$templateCache", function($templateCache) {
22868 $templateCache.put("b2bTemplate/tables/b2bTableBody.html",
22869 "<td ng-hide=\"isHidden()\" ng-transclude></td>");
22872 angular.module("b2bTemplate/tables/b2bTableHeaderSortable.html", []).run(["$templateCache", function($templateCache) {
22873 $templateCache.put("b2bTemplate/tables/b2bTableHeaderSortable.html",
22874 "<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" +
22875 " <span ng-transclude></span>\n" +
22876 " <i ng-class=\"{'icon-controls-upPRIMARY active': sortPattern === 'ascending', 'icon-controls-down active down': sortPattern === 'descending'}\"></i>\n" +
22880 angular.module("b2bTemplate/tables/b2bTableHeaderUnsortable.html", []).run(["$templateCache", function($templateCache) {
22881 $templateCache.put("b2bTemplate/tables/b2bTableHeaderUnsortable.html",
22882 "<th scope=\"col\" ng-hide=\"isHidden()\" ng-transclude></th>");
22885 angular.module("b2bTemplate/tabs/b2bTab.html", []).run(["$templateCache", function($templateCache) {
22886 $templateCache.put("b2bTemplate/tabs/b2bTab.html",
22887 "<li role=\"tab\" aria-selected=\"{{isTabActive()}}\" aria-controls=\"{{tabPanelId}}\" class=\"tab\" \n" +
22888 " ng-class=\"{'active': isTabActive()}\" ng-click=\"clickTab()\" ng-hide=\"tabItem.disabled\">\n" +
22889 " <a href=\"javascript:void(0)\" tabindex=\"{{(isTabActive() && tabItem.disabled)?-1:0}}\"\n" +
22890 " ng-disabled=\"tabItem.disabled\" aria-expanded=\"{{(isTabActive() && !tabItem.disabled)}}\" \n" +
22891 " b2b-accessibility-click=\"13,32\" ng-transclude></a>\n" +
22892 " <span class=\"hidden-spoken\" ng-if=\"isTabActive()\">Active tab</span>\n" +
22896 angular.module("b2bTemplate/tabs/b2bTabset.html", []).run(["$templateCache", function($templateCache) {
22897 $templateCache.put("b2bTemplate/tabs/b2bTabset.html",
22898 "<ul class=\"tabs promo-tabs\" role=\"tablist\" ng-transclude></ul>");
22901 angular.module("b2bTemplate/treeNav/groupedTree.html", []).run(["$templateCache", function($templateCache) {
22902 $templateCache.put("b2bTemplate/treeNav/groupedTree.html",
22903 "<ul role=\"group\">\n" +
22904 " <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" +
22905 " <ul role=\"group\">\n" +
22906 " <b2b-member ng-repeat='member in value.childArray' member='member' time-delay='{{timeDelay}}' group-it='groupIt'></b2b-member>\n" +
22912 angular.module("b2bTemplate/treeNav/treeMember.html", []).run(["$templateCache", function($templateCache) {
22913 $templateCache.put("b2bTemplate/treeNav/treeMember.html",
22914 "<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" +
22915 " <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" +
22916 " <span class=\"{{!member.child?'end':''}} b2b-tree-node-icon\">\n" +
22917 " <i class=\"b2b-tree-expandCollapse-icon\" ng-class=\"{'icon-primary-expanded':member.active}\"></i>\n" +
22919 " <div id=\"description_{{$id}}\" class=\"offscreen-text\">\n" +
22920 " {{member.descriptionText}}\n" +
22922 " <div class=\"b2b-tree-tooltip\" ng-if=\"member.showTooltip\">\n" +
22923 " <span class=\"b2b-tree-arrow-left\"></span>\n" +
22924 " <div class=\"b2b-tree-tooltip-content\">\n" +
22925 " {{member.tooltipContent}}\n" +
22932 angular.module("b2bTemplate/treeNav/ungroupedTree.html", []).run(["$templateCache", function($templateCache) {
22933 $templateCache.put("b2bTemplate/treeNav/ungroupedTree.html",
22934 "<ul role=\"{{setRole}}\"><b2b-member ng-repeat='member in collection' member='member' group-it='groupIt'></b2b-member></ul>");
22937 angular.module("b2bTemplate/treeNodeCheckbox/groupedTree.html", []).run(["$templateCache", function($templateCache) {
22938 $templateCache.put("b2bTemplate/treeNodeCheckbox/groupedTree.html",
22939 "<ul role=\"group\">\n" +
22940 " <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" +
22941 " <span class=\"ng-hide\">\n" +
22942 " <label class=\"checkbox\">\n" +
22943 " <input type=\"checkbox\" tabindex=\"-1\" class=\"treeCheckBox grpTreeCheckbox\" style=\"margin-top:2px;\"/><i class=\"skin\"></i><span> {{(key)?key:''}}</span>\n" +
22947 " {{(key)?key:''}} \n" +
22949 " <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" +
22950 " <ul role=\"group\">\n" +
22951 " <b2b-tree-member ng-repeat='member in value.childArray' member='member' group-it='groupIt'></b2b-tree-member>\n" +
22957 angular.module("b2bTemplate/treeNodeCheckbox/treeMember.html", []).run(["$templateCache", function($templateCache) {
22958 $templateCache.put("b2bTemplate/treeNodeCheckbox/treeMember.html",
22959 "<li role=\"treeitem\" aria-expanded=\"{{(member.active?true:false)}}\" aria-label=\"{{member.name}}\" ng-class=\"{'bg':member.selected}\" b2b-tree-node-link>\n" +
22960 " <a tabindex=\"-1\" title=\"{{member.name}}\" href=\"javascript:void(0)\" ng-class=\"{'active':member.active}\">\n" +
22961 " <span ng-show=\"member.displayCheckbox\">\n" +
22962 " <label class=\"checkbox\">\n" +
22963 " <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" +
22966 " <span ng-show=\"!member.displayCheckbox\">\n" +
22967 " {{member.name}} \n" +
22969 " <span class=\"nodeIcon {{!member.child?'end':''}}\">\n" +
22970 " <i class=\"expandCollapseIcon\" ng-class=\"{'icon-primary-expanded':member.active}\"></i>\n" +
22976 angular.module("b2bTemplate/treeNodeCheckbox/ungroupedTree.html", []).run(["$templateCache", function($templateCache) {
22977 $templateCache.put("b2bTemplate/treeNodeCheckbox/ungroupedTree.html",
22978 "<ul role=\"{{setRole}}\"><b2b-tree-member ng-repeat='member in collection' member='member' group-it='groupIt'></b2b-tree-member></ul>");