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 if(!elem.parent().parent().eq(0).hasClass('b2b-tree-checkbox')){
11900 var childNodes = elem.parent().parent().find('ul').eq(0).children('li');
11902 nonCheckedCount = 0;
11903 checkBoxesCount = 0;
11904 for(i=0; i<childNodes.length; i++){
11905 if(findCheckbox(childNodes[i])){
11906 isGroupNode(childNodes[i]);
11907 isCheckboxSelected(childNodes[i]);
11909 if(checkedTreeNode){
11911 }else if(!angular.element(angular.element(angular.element(childNodes[i]).find('a').eq(0))[0].querySelector('input.treeCheckBox')).prop('indeterminate')){
11916 var parentNodeScope;
11917 parentNodeScope = angular.element(elem.parent().parent()).scope();
11918 if(findCheckbox(elem.parent().parent())){
11919 if(nonCheckedCount == checkBoxesCount){
11920 angular.element(findCheckbox(elem.parent().parent())).prop('indeterminate', false);
11921 if(parentNodeScope && parentNodeScope.member){
11922 parentNodeScope.member.isSelected = false;
11923 parentNodeScope.member.indeterminate = false;
11925 updateGrpNodeCheckboxes(elem.parent().parent(),false);
11927 angular.element(elem.parent().parent()).attr('aria-checked',false);
11928 }else if(checkedCount == checkBoxesCount){
11929 angular.element(findCheckbox(elem.parent().parent())).prop('indeterminate', false);
11930 if(parentNodeScope && parentNodeScope.member){
11931 parentNodeScope.member.isSelected = true;
11932 parentNodeScope.member.indeterminate = false;
11934 updateGrpNodeCheckboxes(elem.parent().parent(),true);
11936 angular.element(elem.parent().parent()).attr('aria-checked',true);
11938 angular.element(findCheckbox(elem.parent().parent())).prop('indeterminate', true);
11939 if(parentNodeScope && parentNodeScope.member){
11940 parentNodeScope.member.isSelected = false;
11941 parentNodeScope.member.indeterminate = true;
11943 updateGrpNodeCheckboxes(elem.parent().parent(),false);
11945 angular.element(elem.parent().parent()).attr('aria-checked',"mixed");
11947 if(parentNodeScope && parentNodeScope.member){
11948 parentNodeScope.$apply();
11954 if(elem.parent().parent().attr('role') == "treeitem"){
11955 upwardSelection(elem.parent().parent());
11960 scope.showChild = function () {
11961 if (!element.hasClass('grouped')) {
11962 if (angular.isArray(scope.member.child) && scope.member.child.length > 0 && (scope.member.divide === undefined || scope.member.child.length < scope.member.divide)) {
11963 scope.groupIt = false;
11964 element.addClass('grouped');
11965 element.append("<b2b-tree-node-checkbox collection='member.child' group-it='" + scope.groupIt + "'></b2b-tree-node-checkbox>");
11966 $compile(element.contents())(scope);
11967 if(scope.member.active && scope.member.active === true){
11968 angular.element(element[0].querySelectorAll('i.expandCollapseIcon')).eq(0).removeClass('icon-primary-collapsed');
11970 if(scope.member.selected && scope.member.selected === true){
11971 element.attr('tabindex', 0);
11972 removeRootTabIndex(element);
11974 if(scope.member.active && scope.member.active == undefined){
11975 angular.element(element[0].querySelectorAll('i.expandCollapseIcon')).eq(0).addClass('icon-primary-collapsed');
11977 } else if (scope.member.child && scope.member.divide && scope.member.child.length > scope.member.divide) {
11978 element.addClass('grouped');
11979 scope.groupIt = true;
11982 if(scope.member.child[0].groupName !== undefined){
11983 grpName = scope.member.child[0].groupName;
11986 var toSlice = scope.member.child[0].name.search(' ');
11987 grpName = scope.member.child[0].name.slice(0, toSlice);
11990 for (i = 0; i < scope.member.child.length; i += scope.member.divide) {
11992 for (j = j + i; j < (i + scope.member.divide); j++) {
11993 if (j === scope.member.child.length) {
11994 scope.member.child[j - 1].grpChild = grpName + ' ' + (i + 1) + ' - ' + (scope.member.child.length);
11997 if(scope.member.child[j-1].active && scope.member.child[j-1].active===true){
11998 scope.member.child[j-1].activeGrp = true;
12002 if (i + scope.member.divide > scope.member.child.length) {
12003 scope.member.child[j].grpChild = grpName + ' ' + (i + 1) + ' - ' + (scope.member.child.length);
12004 if(scope.member.child[j].active && scope.member.child[j].active===true){
12005 scope.member.child[j].activeGrp = true;
12009 scope.member.child[j].grpChild = grpName + ' ' + (i + 1) + ' - ' + (i + scope.member.divide);
12010 if(scope.member.child[j].active && scope.member.child[j].active===true){
12011 scope.member.child[j].activeGrp = true;
12016 if(scope.member.divide){
12017 element.append("<b2b-tree-node-checkbox collection='member.child' group-it='" + scope.groupIt + "'></b2b-tree-node-checkbox>");
12019 element.append("<b2b-tree-node-checkbox collection='member.child' group-it='" + scope.groupIt + "'></b2b-tree-node-checkbox>");
12021 $compile(element.contents())(scope);
12022 if(scope.member.active && scope.member.active === true){
12023 angular.element(element[0].querySelectorAll('i.expandCollapseIcon')).eq(0).removeClass('icon-primary-collapsed');
12026 if( scope.member.active && scope.member.active == undefined){
12027 angular.element(element[0].querySelectorAll('i.expandCollapseIcon')).eq(0).addClass('icon-primary-collapsed');
12031 $timeout(function () {
12032 if(!scope.member.indeterminate){
12033 downwardSelection(element);
12039 if(scope.member.active && scope.member.active == true){
12042 if(scope.member.active == undefined && !element.find('a').eq(0).hasClass('active') && scope.member.child !== undefined){
12043 angular.element(element[0].querySelectorAll('i.expandCollapseIcon')).eq(0).addClass('icon-primary-collapsed');
12045 else if(scope.member.child == undefined){
12046 angular.element(element[0].querySelectorAll('i.expandCollapseIcon')).eq(0).addClass('icon-primary-circle');
12047 if(scope.$parent.$index === 0) {
12048 element.find('a').eq(0).append('<span class="first-link"></span>');
12052 angular.element(element[0].querySelectorAll('i.expandCollapseIcon')).eq(0).bind('click', function (evt) {
12054 var expandFunc = scope.member.onExpand;
12055 if (element.find('a').eq(0).hasClass('active') && scope.member.onExpand !== undefined) {
12056 var eValue = scope.member.onExpand(scope.member);
12058 if (!element.find('a').eq(0).hasClass('active') && scope.member.onCollapse !== undefined) {
12059 scope.member.onCollapse(scope.member);
12063 angular.element(element[0].querySelectorAll('.treeNodeName')).eq(0).bind('click', function (evt) {
12070 .directive('b2bTreeNodeLink', ['keymap', '$timeout', function (keymap, $timeout) {
12073 link: function (scope, element, attr, ctrl) {
12074 var rootE, parentE, upE, downE;
12075 var closeOthersUp = function (elem) {
12077 if (elem.find('a').eq(0).hasClass('active')) {
12078 activeToggle(elem);
12081 if (elem.hasClass('bg')) {
12082 elem.removeClass('bg');
12084 if (elem[0].previousElementSibling !== null) {
12085 closeOthersUp(angular.element(elem[0].previousElementSibling));
12088 var closeOthersDown = function (elem) {
12090 if (elem.find('a').eq(0).hasClass('active')) {
12091 activeToggle(elem);
12094 if (elem.hasClass('bg')) {
12095 elem.removeClass('bg');
12097 if (elem[0].nextElementSibling !== null) {
12098 closeOthersDown(angular.element(elem[0].nextElementSibling));
12102 var removeBackgroundUp = function (elem) {
12104 if (elem.hasClass('b2b-tree-checkbox')) {
12107 elem.parent().parent().removeClass('bg');
12108 removeBackgroundUp(elem.parent().parent());
12112 var removeBackgroundDown = function (elem) {
12114 angular.element(elem[0].querySelector('.bg')).removeClass('bg');
12119 var activeToggle = function (elem) {
12120 var element = elem.find('a').eq(0);
12121 if (element.hasClass('active')) {
12122 elem.removeClass('bg');
12123 if (!angular.element(element[0].querySelectorAll('i.expandCollapseIcon')).eq(0).hasClass('icon-primary-circle')) {
12124 element.removeClass('active');
12125 elem.attr('aria-expanded', 'false');
12126 angular.element(element[0].querySelectorAll('i.expandCollapseIcon')).eq(0).removeClass('icon-primary-expanded');
12127 angular.element(element[0].querySelectorAll('i.expandCollapseIcon')).eq(0).addClass('icon-primary-collapsed');
12130 elem.addClass('bg');
12131 if (!angular.element(element[0].querySelectorAll('i.expandCollapseIcon')).eq(0).hasClass('icon-primary-circle')) {
12132 element.addClass('active');
12133 elem.attr('aria-expanded', 'true');
12134 angular.element(element[0].querySelectorAll('i.expandCollapseIcon')).eq(0).removeClass('icon-primary-collapsed');
12135 angular.element(element[0].querySelectorAll('i.expandCollapseIcon')).eq(0).addClass('icon-primary-expanded');
12139 angular.element(element[0].querySelectorAll('i.expandCollapseIcon')).eq(0).bind('click', function (evt) {
12141 if (element[0].previousElementSibling) {
12142 closeOthersUp(angular.element(element[0].previousElementSibling));
12144 if (element[0].nextElementSibling) {
12145 closeOthersDown(angular.element(element[0].nextElementSibling));
12148 activeToggle(element);
12150 removeBackgroundDown(element);
12151 removeBackgroundUp(element);
12152 evt.stopPropagation();
12155 if (element.parent().parent().hasClass('b2b-tree-checkbox') && (element.parent()[0].previousElementSibling === null)) {
12156 element.attr('tabindex', 0);
12159 var isRoot = function (elem) {
12160 if (elem.parent().parent().eq(0).hasClass('b2b-tree-checkbox')) {
12166 var findRoot = function (elem) {
12167 if (isRoot(elem)) {
12171 findRoot(elem.parent());
12174 var findPreActive = function (elem) {
12176 if (!(elem.hasClass("active"))) {
12179 var childElems = angular.element(elem[0].nextElementSibling.children);
12180 lastE = angular.element(childElems[childElems.length - 1]);
12181 if (lastE.find('a').eq(0).hasClass('active')) {
12182 findPreActive(lastE.find('a').eq(0));
12188 var findUp = function (elem) {
12189 if (isRoot(elem)) {
12193 if (elem[0].previousElementSibling !== null && !angular.element(elem[0].previousElementSibling).hasClass('tree-hide')) {
12194 upE = angular.element(elem[0].previousElementSibling);
12195 if (upE.find('a').eq(0).hasClass('active')) {
12196 findPreActive(upE.find('a').eq(0));
12199 upE = elem.parent().parent();
12203 var downElement = function (elem) {
12204 if (elem.next().hasClass('tree-hide')) {
12205 downElement(elem.next());
12207 downE = elem.next();
12210 var isBottomElem = false;
12211 var downParent = function(liElem){
12212 if(liElem.eq(0).parent().parent().eq(0).hasClass('b2b-tree-checkbox')){
12213 isBottomElem = true;
12216 if(liElem.next().length !== 0){
12217 downE = liElem.next().eq(0);
12221 downParent(liElem.parent().parent());
12225 var findDown = function (elem) {
12226 if (isRoot(elem.parent()) && !elem.hasClass('active')) {
12227 downE = elem.parent();
12230 if (elem.hasClass('active')) {
12231 downE = elem.next().find('li').eq(0);
12232 if (downE.hasClass('tree-hide')) {
12233 downElement(downE);
12237 downParent(elem.parent());
12238 if(isBottomElem === true){
12239 downE = elem.parent();
12240 isBottomElem = false;
12244 element.bind('keydown', function (evt) {
12245 switch (evt.keyCode) {
12246 case keymap.KEY.HOME:
12247 evt.preventDefault();
12248 evt.stopPropagation();
12249 element.attr('tabindex', -1);
12251 rootE.eq(0).attr('tabindex', 0);
12254 case keymap.KEY.LEFT:
12255 evt.preventDefault();
12256 evt.stopPropagation();
12257 if (!isRoot(element)) {
12258 if(element.find('a').eq(0).hasClass('active')){
12259 angular.element(element[0].querySelectorAll('i.expandCollapseIcon')).eq(0).triggerHandler('click');
12262 element.attr('tabindex', -1);
12263 parentE = element.parent().parent();
12264 parentE.attr('tabindex', 0);
12265 parentE[0].focus();
12267 if (element.find('a').eq(0).hasClass('active')) {
12268 angular.element(element[0].querySelectorAll('i.expandCollapseIcon')).eq(0).triggerHandler('click');
12272 case keymap.KEY.UP:
12273 evt.preventDefault();
12274 evt.stopPropagation();
12275 element.attr('tabindex', -1);
12277 upE.eq(0).attr('tabindex', 0);
12280 case keymap.KEY.RIGHT:
12281 evt.preventDefault();
12282 evt.stopPropagation();
12283 if(angular.element(element[0].querySelectorAll('i.expandCollapseIcon')).eq(0).hasClass('icon-primary-circle')){
12286 if (!element.find('a').eq(0).hasClass('active')) {
12287 angular.element(element[0].querySelectorAll('i.expandCollapseIcon')).eq(0).triggerHandler('click');
12290 element.attr('tabindex', -1);
12291 findDown(element.find('a').eq(0));
12292 downE.eq(0).attr('tabindex', 0);
12296 case keymap.KEY.DOWN:
12297 evt.preventDefault();
12298 element.attr('tabindex', -1);
12299 findDown(element.find('a').eq(0));
12300 downE.eq(0).attr('tabindex', 0);
12302 evt.stopPropagation();
12304 case keymap.KEY.SPACE:
12305 case keymap.KEY.ENTER:
12306 evt.preventDefault();
12307 evt.stopPropagation();
12308 if(angular.isDefined(element.scope().member.isSelected)){
12309 element.scope().member.isSelected = !element.scope().member.isSelected;
12310 element.scope().member.indeterminate = false;
12311 element.scope().$apply();
12312 element.find('a').eq(0).find('input').prop('indeterminate', false);
12313 element.find('a').eq(0).find('input').triggerHandler('change');
12325 angular.module('b2b.att.collapse', ['b2b.att.transition'])
12327 // The collapsible directive indicates a block of html that will expand and collapse
12328 .directive('b2bCollapse', ['$transition', function($transition) {
12329 // CSS transitions don't work with height: auto, so we have to manually change the height to a
12330 // specific value and then once the animation completes, we can reset the height to auto.
12331 // Unfortunately if you do this while the CSS transitions are specified (i.e. in the CSS class
12332 // "collapse") then you trigger a change to height 0 in between.
12333 // The fix is to remove the "collapse" CSS class while changing the height back to auto - phew!
12338 marginBottom: null,
12340 paddingBottom: null,
12352 var fixUpHeight = function(scope, element, height) {
12353 // We remove the collapse CSS class to prevent a transition when we change to height: auto
12354 element.removeClass('b2bCollapse');
12355 element.css({height: height});
12356 //adjusting for any margin or padding
12357 if (height === 0) {
12358 element.css(props.closed);
12360 element.css(props.open);
12362 // It appears that reading offsetWidth makes the browser realise that we have changed the
12363 // height already :-/
12364 var x = element[0].offsetWidth;
12365 element.addClass('b2bCollapse');
12369 link: function(scope, element, attrs) {
12371 var initialAnimSkip = true;
12372 scope.$watch(function() {
12373 return element[0].scrollHeight;
12374 }, function(value) {
12375 //The listener is called when scrollHeight changes
12376 //It actually does on 2 scenarios:
12377 // 1. Parent is set to display none
12378 // 2. angular bindings inside are resolved
12379 //When we have a change of scrollHeight we are setting again the correct height if the group is opened
12380 if (element[0].scrollHeight !== 0) {
12381 if (!isCollapsed) {
12382 if (initialAnimSkip) {
12383 fixUpHeight(scope, element, element[0].scrollHeight + 'px');
12385 fixUpHeight(scope, element, 'auto');
12386 element.css({overflow: 'visible'});
12392 scope.$watch(attrs.b2bCollapse, function(value) {
12401 var currentTransition;
12402 var doTransition = function(change) {
12403 if (currentTransition) {
12404 currentTransition.cancel();
12406 currentTransition = $transition(element, change);
12407 currentTransition.then(
12409 currentTransition = undefined;
12412 currentTransition = undefined;
12415 return currentTransition;
12418 var expand = function() {
12419 scope.postTransition = true;
12420 if (initialAnimSkip) {
12421 initialAnimSkip = false;
12422 if (!isCollapsed) {
12423 fixUpHeight(scope, element, 'auto');
12426 //doTransition({ height : element[0].scrollHeight + 'px' })
12427 doTransition(angular.extend({height: element[0].scrollHeight + 'px'}, props.open))
12429 // This check ensures that we don't accidentally update the height if the user has closed
12430 // the group while the animation was still running
12431 if (!isCollapsed) {
12432 fixUpHeight(scope, element, 'auto');
12436 isCollapsed = false;
12439 var collapse = function() {
12440 isCollapsed = true;
12441 if (initialAnimSkip) {
12442 initialAnimSkip = false;
12443 fixUpHeight(scope, element, 0);
12445 fixUpHeight(scope, element, element[0].scrollHeight + 'px');
12446 doTransition(angular.extend({height: 0}, props.closed)).then(function() {
12447 scope.postTransition = false;
12449 element.css({overflow: 'hidden'});
12455 angular.module('b2b.att.position', [])
12457 .factory('$position', ['$document', '$window', function ($document, $window) {
12458 function getStyle(el, cssprop) {
12459 if (el.currentStyle) { //IE
12460 return el.currentStyle[cssprop];
12461 } else if ($window.getComputedStyle) {
12462 return $window.getComputedStyle(el)[cssprop];
12464 // finally try and get inline style
12465 return el.style[cssprop];
12469 * Checks if a given element is statically positioned
12470 * @param element - raw DOM element
12472 function isStaticPositioned(element) {
12473 return (getStyle(element, "position") || 'static') === 'static';
12477 * returns the closest, non-statically positioned parentOffset of a given element
12480 var parentOffsetEl = function (element) {
12481 var docDomEl = $document[0];
12482 var offsetParent = element.offsetParent || docDomEl;
12483 while (offsetParent && offsetParent !== docDomEl && isStaticPositioned(offsetParent)) {
12484 offsetParent = offsetParent.offsetParent;
12486 return offsetParent || docDomEl;
12491 * Provides read-only equivalent of jQuery's position function:
12492 * http://api.jquery.com/position/
12494 position: function (element) {
12495 var elBCR = this.offset(element);
12496 var offsetParentBCR = {
12500 var offsetParentEl = parentOffsetEl(element[0]);
12501 if (offsetParentEl !== $document[0]) {
12502 offsetParentBCR = this.offset(angular.element(offsetParentEl));
12503 offsetParentBCR.top += offsetParentEl.clientTop - offsetParentEl.scrollTop;
12504 offsetParentBCR.left += offsetParentEl.clientLeft - offsetParentEl.scrollLeft;
12508 width: element.prop('offsetWidth'),
12509 height: element.prop('offsetHeight'),
12510 top: elBCR.top - offsetParentBCR.top,
12511 left: elBCR.left - offsetParentBCR.left
12516 * Provides read-only equivalent of jQuery's offset function:
12517 * http://api.jquery.com/offset/
12519 offset: function (element) {
12520 var boundingClientRect = element[0].getBoundingClientRect();
12522 width: element.prop('offsetWidth'),
12523 height: element.prop('offsetHeight'),
12524 top: boundingClientRect.top + ($window.pageYOffset || $document[0].body.scrollTop || $document[0].documentElement.scrollTop),
12525 left: boundingClientRect.left + ($window.pageXOffset || $document[0].body.scrollLeft || $document[0].documentElement.scrollLeft)
12530 * Provides functionality to check whether an element is in view port.
12532 isElementInViewport: function (element) {
12534 var rect = element[0].getBoundingClientRect();
12538 rect.bottom <= ($window.innerHeight || $document[0].documentElement.clientHeight) &&
12539 rect.right <= ($window.innerWidth || $document[0].documentElement.clientWidth)
12548 .factory('$isElement', [function () {
12549 var isElement = function (currentElem, targetElem, alternateElem) {
12550 if (currentElem[0] === targetElem[0]) {
12552 } else if (currentElem[0] === alternateElem[0]) {
12555 return isElement((currentElem.parent()[0] && currentElem.parent()) || targetElem, targetElem, alternateElem);
12562 .directive('attPosition', ['$position', function ($position) {
12565 link: function (scope, elem, attr) {
12566 scope.$watchCollection(function () {
12567 return $position.position(elem);
12568 }, function (value) {
12569 scope[attr.attPosition] = value;
12575 angular.module('b2b.att.transition', [])
12577 .factory('$transition', ['$q', '$timeout', '$rootScope', function($q, $timeout, $rootScope) {
12579 var $transition = function(element, trigger, options) {
12580 options = options || {};
12581 var deferred = $q.defer();
12582 var endEventName = $transition[options.animation ? "animationEndEventName" : "transitionEndEventName"];
12584 var transitionEndHandler = function() {
12585 $rootScope.$apply(function() {
12586 element.unbind(endEventName, transitionEndHandler);
12587 deferred.resolve(element);
12591 if (endEventName) {
12592 element.bind(endEventName, transitionEndHandler);
12595 // Wrap in a timeout to allow the browser time to update the DOM before the transition is to occur
12596 $timeout(function() {
12597 if ( angular.isString(trigger) ) {
12598 element.addClass(trigger);
12599 } else if ( angular.isFunction(trigger) ) {
12601 } else if ( angular.isObject(trigger) ) {
12602 element.css(trigger);
12604 //If browser does not support transitions, instantly resolve
12605 if ( !endEventName ) {
12606 deferred.resolve(element);
12610 // Add our custom cancel function to the promise that is returned
12611 // We can call this if we are about to run a new transition, which we know will prevent this transition from ending,
12612 // i.e. it will therefore never raise a transitionEnd event for that transition
12613 deferred.promise.cancel = function() {
12614 if ( endEventName ) {
12615 element.unbind(endEventName, transitionEndHandler);
12617 deferred.reject('Transition cancelled');
12620 return deferred.promise;
12623 // Work out the name of the transitionEnd event
12624 var transElement = document.createElement('trans');
12625 var transitionEndEventNames = {
12626 'WebkitTransition': 'webkitTransitionEnd',
12627 'MozTransition': 'transitionend',
12628 'OTransition': 'oTransitionEnd',
12629 'transition': 'transitionend'
12631 var animationEndEventNames = {
12632 'WebkitTransition': 'webkitAnimationEnd',
12633 'MozTransition': 'animationend',
12634 'OTransition': 'oAnimationEnd',
12635 'transition': 'animationend'
12637 function findEndEventName(endEventNames) {
12638 for (var name in endEventNames){
12639 if (transElement.style[name] !== undefined) {
12640 return endEventNames[name];
12644 $transition.transitionEndEventName = findEndEventName(transitionEndEventNames);
12645 $transition.animationEndEventName = findEndEventName(animationEndEventNames);
12646 return $transition;
12649 .factory('$scrollTo', ['$window', function($window) {
12650 var $scrollTo = function(offsetLeft, offsetTop, duration) {
12651 TweenMax.to($window, duration || 1, {scrollTo: {y: offsetTop, x: offsetLeft}, ease: Power4.easeOut});
12655 .factory('animation', function(){
12658 .factory('$progressBar', function(){
12660 //Provides a function to pass in code for closure purposes
12661 var loadingAnimationCreator = function(onUpdateCallback){
12663 //Use closure to setup some resuable code
12664 var loadingAnimation = function(callback, duration){
12665 TweenMax.to({}, duration, {
12666 onUpdateParams: ["{self}"],
12667 onUpdate: onUpdateCallback,
12668 onComplete: callback
12671 //Returns a function that takes a callback function and a duration for the animation
12672 return (function(){
12673 return loadingAnimation;
12677 return loadingAnimationCreator;
12679 .factory('$height', function(){
12680 var heightAnimation = function(element,duration,height,alpha){
12681 TweenMax.to(element,
12683 {height:height, autoAlpha:alpha},
12686 return heightAnimation;
12688 angular.module('b2b.att.utilities', ['ngSanitize'])
12689 .constant('b2bUtilitiesConfig', {
12696 enableSearch: false,
12698 circularTraversal: false
12700 .constant('b2bWhenScrollEndsConstants', {
12705 // All breakpoints ranges from >= min and < max
12706 .constant('b2bAwdBreakpoints', {
12722 .filter('groupBy', function ($timeout) {
12723 //Custom GroupBy Filter for treeNav, returns key string and value.childarray as set of grouped elements
12724 return function (data, key) {
12725 if (!key) return data;
12726 var outputPropertyName = '__groupBy__' + key;
12727 if (!data[outputPropertyName]) {
12729 for (var i = 0; i < data.length; i++) {
12730 if (!result[data[i][key]])
12731 result[data[i][key]] = {};
12732 if (!result[data[i][key]].childArray) {
12733 result[data[i][key]].childArray = [];
12735 result[data[i][key]].childArray.push(data[i]);
12736 if (data[i].activeGrp && data[i].activeGrp == true) {
12737 result[data[i][key]].showGroup = true;
12740 Object.defineProperty(result, 'length', {enumerable: false,value: Object.keys(result).length});
12741 Object.defineProperty(data, outputPropertyName, {enumerable: false,configurable: true,writable:false,value:result});
12742 $timeout(function(){delete data[outputPropertyName];},0,false);
12744 return data[outputPropertyName];
12747 .filter('searchObjectPropertiesFilter', [function() {
12748 return function(items, searchText, attrs) {
12753 searchText = searchText.toLowerCase();
12754 angular.forEach(items, function(item) {
12755 angular.forEach(attrs, function(attr) {
12756 if (item.hasOwnProperty(attr) && (item[attr].toLowerCase().indexOf(searchText) != -1)) {
12757 filtered.push(item);
12765 .filter('unsafe',[ '$sce', function ($sce) {
12766 return function(val){
12767 return $sce.trustAsHtml(val);
12770 .filter('b2bHighlight', function () {
12771 function escapeRegexp(queryToEscape) {
12772 return queryToEscape.replace(/([.?*+^$[\]\\(){}|-])/g, '\\$1');
12774 return function (matchItem, query, className) {
12775 return query && matchItem ? matchItem.replace(new RegExp(escapeRegexp(query), 'gi'), '<span class=\"' + className + '\">$&</span>') : matchItem;
12779 Copyright © 2013 Matt Diamond
12780 https://github.com/cwilso/AudioRecorder/blob/master/js/recorderjs/recorder.js
12782 .factory('b2bRecorder', function() {
12784 var Recorder = function(source, cfg) {
12785 var WORKER_PATH = 'recorderWorker.js';
12786 var config = cfg || {};
12787 var bufferLen = config.bufferLen || 4096;
12788 this.context = source.context;
12789 if(!this.context.createScriptProcessor) {
12790 this.node = this.context.createJavacriptProcessor(bufferLen, 2, 2);
12792 this.node = this.context.createScriptProcessor(bufferLen, 2, 2);
12794 var workerCode = 'function init(a){sampleRate=a.sampleRate}function record(a){recBuffersL.push(a[0]),recBuffersR.push(a[1]),recLength+=a[0].length}function exportWAV(a){var b=mergeBuffers(recBuffersL,recLength),c=mergeBuffers(recBuffersR,recLength),d=interleave(b,c),e=encodeWAV(d),f=new Blob([e],{type:a});this.postMessage(f)}function exportMonoWAV(a){var b=mergeBuffers(recBuffersL,recLength),c=encodeWAV(b,!0),d=new Blob([c],{type:a});this.postMessage(d)}function getBuffers(){var a=[];a.push(mergeBuffers(recBuffersL,recLength)),a.push(mergeBuffers(recBuffersR,recLength)),this.postMessage(a)}function clear(){recLength=0,recBuffersL=[],recBuffersR=[]}function mergeBuffers(a,b){for(var c=new Float32Array(b),d=0,e=0;e<a.length;e++)c.set(a[e],d),d+=a[e].length;return c}function interleave(a,b){for(var c=a.length+b.length,d=new Float32Array(c),e=0,f=0;e<c;)d[e++]=a[f],d[e++]=b[f],f++;return d}function floatTo16BitPCM(a,b,c){for(var d=0;d<c.length;d++,b+=2){var e=Math.max(-1,Math.min(1,c[d]));a.setInt16(b,e<0?32768*e:32767*e,!0)}}function writeString(a,b,c){for(var d=0;d<c.length;d++)a.setUint8(b+d,c.charCodeAt(d))}function encodeWAV(a,b){var c=new ArrayBuffer(44+2*a.length),d=new DataView(c);return writeString(d,0,"RIFF"),d.setUint32(4,32+2*a.length,!0),writeString(d,8,"WAVE"),writeString(d,12,"fmt "),d.setUint32(16,16,!0),d.setUint16(20,1,!0),d.setUint16(22,b?1:2,!0),d.setUint32(24,sampleRate,!0),d.setUint32(28,4*sampleRate,!0),d.setUint16(32,4,!0),d.setUint16(34,16,!0),writeString(d,36,"data"),d.setUint32(40,2*a.length,!0),floatTo16BitPCM(d,44,a),d}var recLength=0,recBuffersL=[],recBuffersR=[],sampleRate;this.onmessage=function(a){switch(a.data.command){case"init":init(a.data.config);break;case"record":record(a.data.buffer);break;case"exportWAV":exportWAV(a.data.type);break;case"exportMonoWAV":exportMonoWAV(a.data.type);break;case"getBuffers":getBuffers();break;case"clear":clear()}};';
12795 var blob = new Blob([workerCode]);
12797 var worker = new Worker(window.URL.createObjectURL(blob)); //TODO: Use a blob instead
12798 worker.postMessage({
12801 sampleRate: this.context.sampleRate
12804 var recording = false,
12807 this.node.onaudioprocess = function(e) {
12808 if (!recording) return;
12809 worker.postMessage({
12812 e.inputBuffer.getChannelData(0),
12813 e.inputBuffer.getChannelData(1)
12818 this.configure = function(cfg) {
12819 for (var prop in cfg) {//TODO: look into using angular.extend() here
12820 if (cfg.hasOwnProperty(prop)) {
12821 config[prop] = cfg[prop];
12826 this.record = function() {
12830 this.stop = function() {
12834 this.clear = function() {
12835 worker.postMessage({ command: 'clear' });
12836 window.URL.revokeObjectURL(blob);
12839 this.getBuffers = function(cb) {
12840 currCallback = cb || config.callback;
12841 worker.postMessage({ command: 'getBuffers' });
12844 this.exportWAV = function(cb, type) {
12845 currCallback = cb || config.callback;
12846 type = type || config.type || 'audio/wav';
12847 if (!currCallback) throw new Error('[b2bRecorder]: Callback not set!');
12848 worker.postMessage({
12849 command: 'exportWAV',
12854 this.exportMonoWAV = function(cb, type) {
12855 currCallback = cb || config.callback;
12856 type = type || config.type || 'audio/wav';
12857 if (!currCallback) throw new Error('[b2bRecorder]: Callback not set!');
12858 worker.postMessage({
12859 command: 'exportMonoWAV',
12864 worker.onmessage = function(e) {
12866 currCallback(blob);
12869 source.connect(this.node);
12870 this.node.connect(this.context.destination); // if the script node is not connected to an output the "onaudioprocess" event is not triggerd in Chrome
12877 .factory('b2bViewport', function() {
12878 /* Source: https://gist.github.com/bjankord/2399828 */
12879 var _viewportWidth = function() {
12881 var webkit = (!(window.webkitConvertPointFromNodeToPage == null));
12885 var vpwtest = document.createElement( "div" );
12886 // Sets test div to width 100%, !important overrides any other misc. box model styles that may be set in the CSS
12887 vpwtest.style.cssText = "width:100% !important; margin:0 !important; padding:0 !important; border:none !important;";
12888 document.documentElement.insertBefore( vpwtest, document.documentElement.firstChild );
12889 vpw = vpwtest.offsetWidth;
12890 document.documentElement.removeChild( vpwtest );
12893 else if ( window.innerWidth === undefined ) {
12894 vpw = document.documentElement.clientWidth;
12898 vpw = window.innerWidth;
12904 viewportWidth: _viewportWidth
12907 .directive('b2bWhenScrollEnds', function(b2bWhenScrollEndsConstants, $log) {
12910 link: function (scope, element, attrs) {
12912 * Exposed Attributes:
12913 * threshold - integer - number of pixels before scrollbar hits end that callback is called
12914 * width - integer - override for element's width (px)
12915 * height - integer - override for element's height (px)
12916 * axis - string - x/y for scroll bar axis
12918 var threshold = parseInt(attrs.threshold, 10) || b2bWhenScrollEndsConstants.threshold;
12920 if (!attrs.axis || attrs.axis === '') {
12921 $log.warn('axis attribute must be defined for b2bWhenScrollEnds.');
12925 if (attrs.axis === 'x') {
12926 visibleWidth = parseInt(attrs.width, 10) || b2bWhenScrollEndsConstants.width;
12927 if (element.css('width')) {
12928 visibleWidth = element.css('width').split('px')[0];
12931 element[0].addEventListener('scroll', function() {
12932 var scrollableWidth = element.prop('scrollWidth');
12933 if (scrollableWidth === undefined) {
12934 scrollableWidth = 1;
12936 var hiddenContentWidth = scrollableWidth - visibleWidth;
12938 if (hiddenContentWidth - element[0].scrollLeft <= threshold) {
12939 /* Scroll almost at bottom, load more rows */
12940 scope.$apply(attrs.b2bWhenScrollEnds);
12943 } else if (attrs.axis === 'y') {
12944 visibleHeight = parseInt(attrs.height, 10) || b2bWhenScrollEndsConstants.height;
12945 if (element.css('width')) {
12946 visibleHeight = element.css('height').split('px')[0];
12949 element[0].addEventListener('scroll', function() {
12950 var scrollableHeight = element.prop('scrollHeight');
12951 if (scrollableHeight === undefined) {
12952 scrollableHeight = 1;
12954 var hiddenContentHeight = scrollableHeight - visibleHeight;
12956 if (hiddenContentHeight - element[0].scrollTop <= threshold) {
12957 /* Scroll almost at bottom, load more rows */
12958 scope.$apply(attrs.b2bWhenScrollEnds);
12966 .factory('$windowBind', ['$window', '$timeout', function($window, $timeout) {
12967 var win = angular.element($window);
12968 var _scroll = function (flag, callbackFunc, scope) {
12969 scope.$watch(flag, function (val) {
12970 $timeout(function () {
12972 win.bind('scroll', callbackFunc);
12974 win.unbind('scroll', callbackFunc);
12980 var throttle = function(type, name, obj) {
12981 obj = obj || window;
12982 var running = false;
12983 var func = function() {
12984 if (running) { return; }
12986 requestAnimationFrame(function() {
12987 obj.dispatchEvent(new CustomEvent(name));
12991 obj.addEventListener(type, func);
12994 var _resize = function(callbackFunc, scope) {
12995 throttle("resize", "optimizedResize");
12996 window.addEventListener("optimizedResize", function(event) {
12998 //win.bind(event, callbackFunc);
12999 if (!scope.$$phase) {
13005 var _click = function (flag, callbackFunc, scope) {
13006 scope.$watch(flag, function (val) {
13007 $timeout(function () {
13009 win.bind('click', callbackFunc);
13011 win.unbind('click', callbackFunc);
13017 var _event = function (event, flag, callbackFunc, scope, timeoutFlag, timeoutValue) {
13019 if (!(timeoutValue)) {
13022 scope.$watch(flag, function (newVal, oldVal) {
13023 if (newVal !== oldVal) {
13024 $timeout(function () {
13026 win.bind(event, callbackFunc);
13028 win.unbind(event, callbackFunc);
13034 scope.$watch(flag, function (newVal, oldVal) {
13035 if (newVal !== oldVal) {
13037 win.bind(event, callbackFunc);
13039 win.unbind(event, callbackFunc);
13054 .factory('keymap', function () {
13076 MAP: { 91 : "COMMAND", 8 : "BACKSPACE" , 9 : "TAB" , 13 : "ENTER" , 16 : "SHIFT" , 17 : "CTRL" , 18 : "ALT" , 19 : "PAUSEBREAK" , 20 : "CAPSLOCK" , 27 : "ESC" , 32 : "SPACE" , 33 : "PAGE_UP", 34 : "PAGE_DOWN" , 35 : "END" , 36 : "HOME" , 37 : "LEFT" , 38 : "UP" , 39 : "RIGHT" , 40 : "DOWN" , 43 : "+" , 44 : "PRINTSCREEN" , 45 : "INSERT" , 46 : "DELETE", 48 : "0" , 49 : "1" , 50 : "2" , 51 : "3" , 52 : "4" , 53 : "5" , 54 : "6" , 55 : "7" , 56 : "8" , 57 : "9" , 59 : ";", 61 : "=" , 65 : "A" , 66 : "B" , 67 : "C" , 68 : "D" , 69 : "E" , 70 : "F" , 71 : "G" , 72 : "H" , 73 : "I" , 74 : "J" , 75 : "K" , 76 : "L", 77 : "M" , 78 : "N" , 79 : "O" , 80 : "P" , 81 : "Q" , 82 : "R" , 83 : "S" , 84 : "T" , 85 : "U" , 86 : "V" , 87 : "W" , 88 : "X" , 89 : "Y" , 90 : "Z", 96 : "0" , 97 : "1" , 98 : "2" , 99 : "3" , 100 : "4" , 101 : "5" , 102 : "6" , 103 : "7" , 104 : "8" , 105 : "9", 106 : "*" , 107 : "+" , 109 : "-" , 110 : "." , 111 : "/", 112 : "F1" , 113 : "F2" , 114 : "F3" , 115 : "F4" , 116 : "F5" , 117 : "F6" , 118 : "F7" , 119 : "F8" , 120 : "F9" , 121 : "F10" , 122 : "F11" , 123 : "F12", 144 : "NUMLOCK" , 145 : "SCROLLLOCK" , 186 : ";" , 187 : "=" , 188 : "," , 189 : "-" , 190 : "." , 191 : "/" , 192 : "`" , 219 : "[" , 220 : "\\" , 221 : "]" , 222 : "'"
13078 isControl: function (e) {
13081 case this.KEY.COMMAND:
13082 case this.KEY.SHIFT:
13083 case this.KEY.CTRL:
13095 isFunctionKey: function (k) {
13096 k = k.keyCode ? k.keyCode : k;
13097 return k >= 112 && k <= 123;
13099 isVerticalMovement: function (k) {
13100 return ~[this.KEY.UP, this.KEY.DOWN].indexOf(k);
13102 isHorizontalMovement: function (k) {
13103 return ~[this.KEY.LEFT, this.KEY.RIGHT, this.KEY.BACKSPACE, this.KEY.DELETE].indexOf(k);
13105 isAllowedKey: function (k) {
13106 return (~[this.KEY.SPACE, this.KEY.ESC, this.KEY.ENTER].indexOf(k)) || this.isHorizontalMovement(k) || this.isVerticalMovement(k);
13108 isNumericKey: function (e) {
13110 if ((k >= 48 && k <= 57) || (k >= 96 && k <= 105)) {
13116 isAlphaNumericKey: function (e) {
13118 if ((k >= 48 && k <= 57) || (k >= 96 && k <= 105) || (k >= 65 && k <= 90)) {
13127 .factory('$isElement', [function () {
13128 var isElement = function (currentElem, targetElem, alternateElem) {
13129 if (currentElem[0] === targetElem[0]) {
13131 } else if (currentElem[0] === alternateElem[0]) {
13134 return isElement((currentElem.parent()[0] && currentElem.parent()) || targetElem, targetElem, alternateElem);
13141 .factory('events', function () {
13142 var _stopPropagation = function (evt) {
13143 if (evt.stopPropagation) {
13144 evt.stopPropagation();
13146 evt.returnValue = false;
13149 var _preventDefault = function (evt) {
13150 if (evt.preventDefault) {
13151 evt.preventDefault();
13153 evt.returnValue = false;
13157 stopPropagation: _stopPropagation,
13158 preventDefault: _preventDefault
13163 .factory('isHighContrast', function () {
13164 var _isHighContrast = function (idval)
13168 var objDiv, objImage, strColor, strWidth, strReady;
13169 var strImageID = idval; // ID of image on the page
13171 // Create a test div
13172 objDiv = document.createElement('div');
13174 //Set its color style to something unusual
13175 objDiv.style.color = 'rgb(31, 41, 59)';
13177 // Attach to body so we can inspect it
13178 document.body.appendChild(objDiv);
13180 // Read computed color value
13181 strColor = document.defaultView ? document.defaultView.getComputedStyle(objDiv, null).color : objDiv.currentStyle.color;
13182 strColor = strColor.replace(/ /g, '');
13184 // Delete the test DIV
13185 document.body.removeChild(objDiv);
13187 // Check if we get the color back that we set. If not, we're in
13188 // high contrast mode.
13189 if (strColor !== 'rgb(31,41,59)') {
13196 return _isHighContrast;
13199 .run(['isHighContrast', '$document', function (isHighContrast, $document) {
13200 var html = $document.find('html').eq(0);
13201 if (isHighContrast()) {
13202 html.addClass('ds2-no-colors');
13204 html.removeClass('ds2-no-colors');
13208 .factory('$documentBind', ['$document', '$timeout', function ($document, $timeout) {
13209 var _click = function (flag, callbackFunc, scope) {
13210 scope.$watch(flag, function (val) {
13211 $timeout(function () {
13213 $document.bind('click', callbackFunc);
13215 $document.unbind('click', callbackFunc);
13221 var _touch = function (flag, callbackFunc, scope) {
13222 scope.$watch(flag, function (val) {
13223 $timeout(function () {
13225 $document.bind('touchstart', callbackFunc);
13227 $document.unbind('touchstart', callbackFunc);
13233 var _scroll = function (flag, callbackFunc, scope) {
13234 scope.$watch(flag, function (val) {
13235 $timeout(function () {
13237 $document.bind('scroll', callbackFunc);
13239 $document.unbind('scroll', callbackFunc);
13245 var _event = function (event, flag, callbackFunc, scope, timeoutFlag, timeoutValue) {
13247 if (!(timeoutValue)) {
13250 scope.$watch(flag, function (newVal, oldVal) {
13251 if (newVal !== oldVal) {
13252 $timeout(function () {
13254 $document.bind(event, callbackFunc);
13256 $document.unbind(event, callbackFunc);
13262 scope.$watch(flag, function (newVal, oldVal) {
13263 if (newVal !== oldVal) {
13265 $document.bind(event, callbackFunc);
13267 $document.unbind(event, callbackFunc);
13282 .directive('b2bOnlyNums', function (keymap) {
13285 require: 'ngModel',
13286 link: function (scope, elm, attrs, ctrl) {
13287 var maxChars = attrs.b2bOnlyNums ? attrs.b2bOnlyNums : 4;
13288 elm.on('keydown', function (event) {
13289 if ((event.which >= 48 && event.which <= 57) || (event.which >= 96 && event.which <= 105)) {
13290 // check for maximum characters allowed
13291 if (elm.val().length < maxChars){
13294 event.preventDefault();
13297 } else if ([8, 9, 13, 27, 37, 38, 39, 40].indexOf(event.which) > -1) {
13298 // to allow backspace, tab, enter, escape, arrows
13300 } else if (event.altKey || event.ctrlKey) {
13301 // to allow alter, control, and shift keys
13305 event.preventDefault();
13310 var validateString = function (value) {
13311 if (angular.isUndefined(value) || value === null || value === '') {
13312 return ctrl.$modelValue;
13316 ctrl.$parsers.unshift(validateString);
13321 .directive('b2bKeyupClick', [ function () {
13324 link: function (scope, elem, attr) {
13326 attr.$observe('b2bKeyupClick', function (value) {
13328 keyCode = value.split(',');
13331 elem.bind('keydown keyup', function (ev) {
13332 var keyCodeCondition = function () {
13334 if (!(ev.keyCode)) {
13336 ev.keyCode = ev.which;
13337 } else if (ev.charCode) {
13338 ev.keyCode = ev.charCode;
13341 if ((ev.keyCode && keyCode.indexOf(ev.keyCode.toString()) > -1)) {
13346 if (ev.type === 'keydown' && keyCodeCondition()) {
13347 ev.preventDefault();
13349 else if (ev.type === 'keyup' && keyCodeCondition()) {
13357 .factory('b2bDOMHelper', function() {
13359 var _isTabable = function(node) {
13360 var element = angular.element(node);
13361 var tagName = element[0].tagName.toUpperCase();
13363 if (isHidden(element)) {
13366 if (element.attr('tabindex') !== undefined) {
13367 return (parseInt(element.attr('tabindex'), 10) >= 0);
13369 if (tagName === 'A' || tagName === 'AREA' || tagName === 'BUTTON' || tagName === 'INPUT' || tagName === 'TEXTAREA' || tagName === 'SELECT') {
13370 if (tagName === 'A' || tagName === 'AREA') {
13371 // anchors/areas without href are not focusable
13372 return (element[0].href !== '');
13374 return !(element[0].disabled);
13379 function isValidChild(child) {
13380 return child.nodeType == 1 && child.nodeName != 'SCRIPT' && child.nodeName != 'STYLE';
13383 function isHidden(obj) {
13384 var elem = angular.element(obj);
13385 var elemStyle = undefined;
13386 if(obj instanceof HTMLElement){
13387 elemStyle = window.getComputedStyle(obj);
13390 elemStyle = window.getComputedStyle(obj[0]);
13392 return elem.hasClass('ng-hide') || elem.css('display') === 'none' || elemStyle.display === 'none' || elemStyle.visibility === 'hidden' || elem.css('visibility') === 'hidden';
13395 function hasValidParent(obj) {
13396 return (isValidChild(obj) && obj.parentElement.nodeName !== 'BODY');
13399 function traverse(obj, fromTop) {
13400 var obj = obj || document.getElementsByTagName('body')[0];
13401 if (isValidChild(obj) && _isTabable(obj)) {
13404 // If object is hidden, skip it's children
13405 if (isValidChild(obj) && isHidden(obj)) {
13408 // If object is hidden, skip it's children
13409 if (angular.element(obj).hasClass('ng-hide')) {
13412 if (obj.hasChildNodes()) {
13415 child = obj.firstChild;
13417 child = obj.lastChild;
13420 var res = traverse(child, fromTop);
13426 child = child.nextSibling;
13428 child = child.previousSibling;
13438 var _previousElement = function(el, isFocusable){
13441 if (el.hasOwnProperty('length')) {
13445 var parent = elem.parentElement;
13446 var previousElem = undefined;
13449 if (hasValidParent(elem)) {
13450 var siblings = angular.element(parent).children();
13451 if (siblings.length > 0) {
13452 // Good practice to splice out the elem from siblings if there, saving some time.
13453 // We allow for a quick check for jumping to parent first before removing.
13454 if (siblings[0] === elem) {
13455 // If we are looking at immidiate parent and elem is first child, we need to go higher
13456 var e = _previousElement(angular.element(elem).parent(), isFocusable);
13457 if (_isTabable(e)) {
13461 // I need to filter myself and any nodes next to me from the siblings
13462 var indexOfElem = Array.prototype.indexOf.call(siblings, elem);
13463 siblings = Array.prototype.filter.call(siblings, function(item, itemIndex) {
13464 if (!angular.equals(elem, item) && itemIndex < indexOfElem) {
13469 // We need to search backwards
13470 for (var i = 0; i <= siblings.length-1; i++) {//for (var i = siblings.length-1; i >= 0; i--) {
13471 var ret = traverse(siblings[i], false);
13472 if (ret !== undefined) {
13477 var e = _previousElement(angular.element(elem).parent(), isFocusable);
13478 if (_isTabable(e)) {
13484 var siblings = angular.element(parent).children();
13485 if (siblings.length > 1) {
13486 // Since indexOf is on Array.prototype and parent.children is a NodeList, we have to use call()
13487 var index = Array.prototype.indexOf.call(siblings, elem);
13488 previousElem = siblings[index-1];
13491 return previousElem;
13494 var _lastTabableElement = function(el) {
13495 /* This will return the first tabable element from the parent el */
13497 if (el.hasOwnProperty('length')) {
13501 return traverse(elem, false);
13504 var _firstTabableElement = function(el) {
13505 /* This will return the first tabable element from the parent el */
13507 if (el.hasOwnProperty('length')) {
13511 return traverse(elem, true);
13514 var _isInDOM = function(obj) {
13515 return document.documentElement.contains(obj);
13519 firstTabableElement: _firstTabableElement,
13520 lastTabableElement: _lastTabableElement,
13521 previousElement: _previousElement,
13523 isTabable: _isTabable,
13528 .factory('windowOrientation', ['$window', function ($window) {
13529 var _isPotrait = function () {
13530 if ($window.innerHeight > $window.innerWidth) {
13536 var _isLandscape = function () {
13537 if ($window.innerHeight < $window.innerWidth) {
13545 isPotrait: _isPotrait,
13546 isLandscape: _isLandscape
13549 .directive('b2bNextElement', function() {
13553 link: function (scope, elem, attr, ctrls) {
13555 var keys = attr.b2bNextElement.split(',');
13557 elem.bind('keydown', function (e) {
13558 var nextElement = elem.next();
13559 if(e.keyCode == 39 || e.keyCode == 40){ // if e.keyCode in keys
13560 if(nextElement.length) {
13561 e.preventDefault();
13562 nextElement[0].focus();
13570 .directive('b2bAccessibilityClick', [function () {
13573 link: function (scope, elem, attr, ctrl) {
13575 attr.$observe('b2bAccessibilityClick', function (value) {
13577 keyCode = value.split(',');
13580 elem.bind('keydown keypress', function (ev) {
13581 var keyCodeCondition = function () {
13583 if (!(ev.keyCode)) {
13585 ev.keyCode = ev.which;
13586 } else if (ev.charCode) {
13587 ev.keyCode = ev.charCode;
13590 if ((ev.keyCode && keyCode.indexOf(ev.keyCode.toString()) > -1)) {
13595 if (keyCode.length > 0 && keyCodeCondition()) {
13597 ev.preventDefault();
13604 .directive('b2bReset', ['$compile', function ($compile) {
13607 require: ['?ngModel', 'b2bReset'],
13608 controller: ['$scope', function ($scope) {
13609 var resetButton = angular.element('<button type="button" class="reset-field" tabindex="-1" aria-label="Click to reset" aria-hidden="true" role="button"></button>');
13611 this.getResetButton = function () {
13612 return resetButton;
13615 link: function (scope, element, attrs, ctrls) {
13617 var ngModelCtrl = ctrls[0];
13618 var ctrl = ctrls[1];
13620 var resetButton = ctrl.getResetButton();
13623 resetButton.on('click', function () {
13624 element[0].value = '';
13627 if (attrs.b2bReset) {
13628 ngModelCtrl.$setViewValue(attrs.b2bReset);
13630 ngModelCtrl.$setViewValue('');
13632 element[0].value = ngModelCtrl.$viewValue;
13633 ngModelCtrl.$render();
13636 element[0].focus();
13637 element[0].select();
13640 var addResetButton = function () {
13641 element.after(resetButton);
13642 element.unbind('focus input', addResetButton);
13645 element.bind('focus input', addResetButton);
13650 .directive('b2bPrevElement', ['b2bDOMHelper', 'keymap', function (b2bDOMHelper, keymap) {
13654 link: function (scope, elem, attr) {
13656 elem.bind('keydown', function (e) {
13657 if(e.keyCode == 37 || e.keyCode == 38){
13658 var prev = b2bDOMHelper.previousElement(elem, false);
13659 if(prev !== undefined) {
13660 e.preventDefault();
13669 * @param {integer} delay - Timeout before first and last focusable elements are found
13670 * @param {boolean} trigger - A variable on scope that will trigger refinding first/last focusable elements
13672 .directive('b2bTrapFocusInsideElement', ['$timeout', 'b2bDOMHelper', 'keymap', 'events', function ($timeout, b2bDOMHelper, keymap, events) {
13676 link: function (scope, elem, attr) {
13678 var delay = parseInt(attr.delay, 10) || 10;
13680 /* Before opening modal, find the focused element */
13681 var firstTabableElement = undefined,
13682 lastTabableElement = undefined;
13685 $timeout(function () {
13686 firstTabableElement = b2bDOMHelper.firstTabableElement(elem);
13687 lastTabableElement = b2bDOMHelper.lastTabableElement(elem);
13688 angular.element(firstTabableElement).bind('keydown', firstTabableElementKeyhandler);
13689 angular.element(lastTabableElement).bind('keydown', lastTabableElementKeyhandler);
13693 if (attr.trigger !== undefined) {
13694 scope.$watch('trigger', function() {
13695 if (scope.trigger) {
13701 var firstTabableElementKeyhandler = function(e) {
13703 e.keyCode = e.which;
13705 if (e.keyCode === keymap.KEY.TAB && e.shiftKey) {
13706 if (attr.trapFocusInsideElement === 'true') {
13707 var temp = b2bDOMHelper.lastTabableElement(elem);
13708 if (lastTabableElement !== temp) {
13709 // Unbind keydown handler on lastTabableElement
13710 angular.element(lastTabableElement).unbind('keydown', lastTabableElementKeyhandler);
13711 lastTabableElement = temp;
13712 angular.element(lastTabableElement).bind('keydown', lastTabableElementKeyhandler);
13715 lastTabableElement.focus();
13716 events.preventDefault(e);
13717 events.stopPropagation(e);
13721 var lastTabableElementKeyhandler = function(e) {
13723 e.keyCode = e.which;
13725 if (e.keyCode === keymap.KEY.TAB && !e.shiftKey) {
13726 if (attr.trapFocusInsideElement === 'true') {
13727 var temp = b2bDOMHelper.firstTabableElement(elem);
13728 if (firstTabableElement !== temp) {
13729 // Unbind keydown handler on firstTabableElement
13730 angular.element(firstTabableElement).unbind('keydown', firstTabableElementKeyhandler);
13731 firstTabableElement = temp;
13732 angular.element(firstTabableElement).bind('keydown', firstTabableElementKeyhandler);
13735 firstTabableElement.focus();
13736 events.preventDefault(e);
13737 events.stopPropagation(e);
13746 .factory('trapFocusInElement', ['$document', '$isElement', 'b2bDOMHelper', 'keymap', '$interval', function ($document, $isElement, b2bDOMHelper, keymap, $interval) {
13747 var elementStack = [];
13748 var stackHead = undefined;
13749 var stopInterval = undefined;
13750 var intervalRequired = false;
13751 var interval = 1000;
13752 var firstTabableElement, lastTabableElement;
13754 var trapKeyboardFocusInFirstElement = function (e) {
13756 e.keyCode = e.which;
13759 if (e.shiftKey === true && e.keyCode === keymap.KEY.TAB) {
13760 lastTabableElement[0].focus();
13761 e.preventDefault(e);
13762 e.stopPropagation(e);
13767 var trapKeyboardFocusInLastElement = function (e) {
13769 e.keyCode = e.which;
13772 if (e.shiftKey === false && e.keyCode === keymap.KEY.TAB) {
13773 firstTabableElement[0].focus();
13774 e.preventDefault(e);
13775 e.stopPropagation(e);
13779 var trapFocusInElement = function (flag, firstTabableElementParam, lastTabableElementParam) {
13780 var bodyElements = $document.find('body').children();
13782 firstTabableElement = firstTabableElementParam ? firstTabableElementParam : angular.element(b2bDOMHelper.firstTabableElement(stackHead));
13783 lastTabableElement = lastTabableElementParam ? lastTabableElementParam : angular.element(b2bDOMHelper.lastTabableElement(stackHead));
13786 for (var i = 0; i < bodyElements.length; i++) {
13787 if (bodyElements[i] !== stackHead[0]) {
13788 bodyElements.eq(i).attr('aria-hidden', true);
13791 firstTabableElement.bind('keydown', trapKeyboardFocusInFirstElement);
13792 lastTabableElement.bind('keydown', trapKeyboardFocusInLastElement);
13794 for (var j = 0; j < bodyElements.length; j++) {
13795 if (bodyElements[j] !== stackHead[0]) {
13796 bodyElements.eq(j).removeAttr('aria-hidden');
13799 firstTabableElement.unbind('keydown', trapKeyboardFocusInFirstElement);
13800 lastTabableElement.unbind('keydown', trapKeyboardFocusInLastElement);
13803 if (intervalRequired && flag) {
13804 stopInterval = $interval(function () {
13805 var firstTabableElementTemp = angular.element(b2bDOMHelper.firstTabableElement(stackHead));
13806 var lastTabableElementTemp = angular.element(b2bDOMHelper.lastTabableElement(stackHead));
13807 if (firstTabableElementTemp[0] !== firstTabableElement[0] || lastTabableElementTemp[0] !== lastTabableElement[0]) {
13808 $interval.cancel(stopInterval);
13809 stopInterval = undefined;
13810 trapFocusInElement(false, firstTabableElement, lastTabableElement);
13811 trapFocusInElement(true, firstTabableElementTemp, lastTabableElementTemp);
13815 if (stopInterval) {
13816 $interval.cancel(stopInterval);
13817 stopInterval = undefined;
13821 var toggleTrapFocusInElement = function (flag, element, intervalRequiredParam, intervalParam) {
13822 intervalRequired = intervalRequiredParam ? intervalRequiredParam : intervalRequired;
13823 interval = intervalParam ? intervalParam : interval;
13824 if (angular.isDefined(flag) && angular.isDefined(element)) {
13825 if (flag && angular.isUndefined(stackHead)) {
13826 stackHead = element;
13827 trapFocusInElement(flag);
13830 trapFocusInElement(false);
13831 elementStack.push(stackHead);
13832 stackHead = element;
13833 trapFocusInElement(true);
13835 if (angular.isDefined(stackHead) && (stackHead[0] === element[0])) {
13836 trapFocusInElement(false);
13837 stackHead = elementStack.pop();
13838 if (angular.isDefined(stackHead)) {
13839 trapFocusInElement(true);
13845 if (angular.isDefined(stackHead)) {
13846 trapFocusInElement(false, firstTabableElement, lastTabableElement);
13847 trapFocusInElement(true);
13852 return toggleTrapFocusInElement;
13854 .factory('draggedElement', function(){
13855 var draggedElement;
13857 setElement: function(data){
13858 draggedElement = data;
13860 getElement: function(){
13861 return draggedElement;
13866 .directive('draggable', ['draggedElement',function (draggedElement) {
13867 return function(scope, element) {
13869 element[0].draggable = true;
13871 element.bind('dragstart', function(e) {
13872 draggedElement.setElement(this.parentElement.parentElement);
13873 e.dataTransfer.effectAllowed = 'move';
13874 e.dataTransfer.setDragImage(this.parentElement.parentElement, 0, 0);
13875 this.parentElement.parentElement.classList.add('b2-drag-element');
13879 element.bind('dragend', function(e) {
13880 draggedElement.setElement(e);
13881 this.parentElement.parentElement.classList.remove('b2-drag-element');
13887 .directive('droppable', ['draggedElement',function (draggedElement) {
13894 link: function(scope, element, attr) {
13895 if(attr.droppable === 'true') {
13896 element.bind('dragover', function(e) {
13897 e.dataTransfer.dropEffect = 'move';
13898 this.classList.add('b2b-drag-over')
13899 if (e.preventDefault) e.preventDefault(); // allows us to drop
13903 element.bind('dragstart', function(e) {
13904 if(!e.target.parentElement.classList.contains('b2b-draggable')) {
13905 e.preventDefault();
13910 element.bind('dragenter', function(e) {
13911 if(e.target.getAttribute('droppable') ==='true') {
13916 element.bind('dragleave', function(e) {
13917 this.classList.remove('b2b-drag-over');
13921 element.bind('drop', function(e) {
13922 var ele = draggedElement.getElement();
13923 if (e.stopPropagation) e.stopPropagation();
13924 if (e.preventDefault) e.preventDefault();
13925 this.classList.remove('b2b-drag-over');
13927 if(ele && ele.hasAttribute('data-index')){
13928 var element = scope.rowData[parseInt(ele.getAttribute('data-index'))];
13929 if(element !== undefined) {
13930 scope.rowData.splice(parseInt(ele.getAttribute('data-index')), 1);
13931 scope.rowData.splice(parseInt(e.currentTarget.getAttribute('data-index')), 0 , element);
13942 .directive('b2bSetNextFocusOn', ['b2bDOMHelper', '$timeout', function(b2bDOMHelper, $timeout) {
13946 link: function (scope, elem, attr) {
13947 elem.bind('click', function(){
13948 var firstFocusableElement = undefined;
13949 var containerElem = undefined;
13950 var containerArray = [];
13951 var timeout = parseInt(attr.setNextFocusTimeout, 0) | 100;
13952 var index = parseInt(attr.b2bSetNextFocusIndex, 0) | 0;
13955 *Fix for IE7 and lower
13956 *polyfill src: https://github.com/HubSpot/pace/issues/102
13958 if (!document.querySelectorAll) {
13959 document.querySelectorAll = function (selectors) {
13960 var style = document.createElement('style'), elements = [], element;
13961 document.documentElement.firstChild.appendChild(style);
13962 document._qsa = [];
13964 style.styleSheet.cssText = selectors + '{x-qsa:expression(document._qsa && document._qsa.push(this))}';
13965 window.scrollBy(0, 0);
13966 style.parentNode.removeChild(style);
13968 while (document._qsa.length) {
13969 element = document._qsa.shift();
13970 element.style.removeAttribute('x-qsa');
13971 elements.push(element);
13973 document._qsa = null;
13978 if (attr.b2bSetNextFocusOn === '') {
13981 containerArray = attr.b2bSetNextFocusOn.split(' ');
13983 $timeout(function(){
13985 do { // cycles thru containerArray until finds a match in DOM to set focus to
13986 containerElem = document.querySelectorAll(containerArray[i])[index];
13988 } while ( (!containerElem) && (i < containerArray.length) );
13990 if (!angular.isDefined(firstFocusableElement)) {
13991 firstFocusableElement = b2bDOMHelper.firstTabableElement(containerElem);
13993 firstFocusableElement.focus();
14002 .directive('b2bInputAllow', [function() {
14005 require: 'ngModel',
14006 link: function (scope, elem, attr, ctrl) {
14007 var regexExpression = null;
14008 attr.$observe('b2bInputAllow', function (value) {
14010 regexExpression = new RegExp(value);
14013 var isValid = function(str) {
14014 if (regexExpression !== null) {
14015 return regexExpression.test(str);
14019 elem.bind('keypress', function($event) {
14020 var charcode = String.fromCharCode($event.which || $event.keyCode);
14021 if (!isValid(charcode)) {
14022 $event.preventDefault();
14023 $event.stopPropagation();
14026 elem.bind('input', function (evt) {
14027 var inputString = ctrl.$viewValue;
14028 if (isValid(inputString)) {
14029 ctrl.$setViewValue(inputString);
14038 .directive('b2bInputDeny', [function() {
14041 require: 'ngModel',
14042 link: function (scope, elem, attr, ctrl) {
14043 var regexExpression = null;
14044 attr.$observe('b2bInputDeny', function (value) {
14046 regexExpression = new RegExp(value, 'g');
14049 elem.bind('input', function () {
14050 var inputString = ctrl.$viewValue && ctrl.$viewValue.replace(regexExpression, '');
14051 if (inputString !== ctrl.$viewValue) {
14052 ctrl.$setViewValue(inputString);
14061 .directive('b2bDragonInput', [function() {
14064 require: 'ngModel',
14065 link: function (scope, elem, attr, ctrl) {
14066 elem.on('focus keyup', function(){
14067 elem.triggerHandler('change');
14073 .directive('b2bKey', ['b2bUtilitiesConfig', '$timeout', 'keymap', function (b2bUtilitiesConfig, $timeout, keymap) {
14076 controller: ['$scope', '$element', '$attrs', function ($scope, $element,attr) {
14077 this.childElements = [];
14078 this.disableNodes = {};
14079 this.enableSearch = attr.enableSearch !== undefined ? true : b2bUtilitiesConfig.enableSearch;
14080 this.circularTraversal = attr.circularTraversal !== undefined ? true : b2bUtilitiesConfig.circularTraversal;
14082 if (this.enableSearch) {
14083 this.searchKeys = [];
14085 var searchString = '';
14087 var selfCtrl = this;
14089 this.childElementsList = [];
14091 this.b2bKeyID = "";
14093 if (angular.isDefined(attr.b2bKey)) {
14094 this.b2bKeyID = attr.b2bKey;
14097 this.calculateChildElementsList = function () {
14098 return $element[0].querySelectorAll("[b2b-key-item='" + this.b2bKeyID + "']:not([disabled])");
14101 this.resetChildElementsList = function () {
14102 return $timeout(function () {
14103 selfCtrl.childElementsList = selfCtrl.calculateChildElementsList();
14107 this.resetChildElementsList();
14109 $scope.$on('b2b-key-reset-child-elements-list', function () {
14110 selfCtrl.resetChildElementsList();
14114 this.registerElement = function (childElement, searchKey) {
14115 this.childElements.push(childElement);
14116 if (this.enableSearch) {
14117 this.searchKeys.push(searchKey);
14119 var count = this.childElements.length - 1;
14120 this.maxLength = count + 1;
14123 this.toggleDisable = function (count, state) {
14124 this.disableNodes[count] = state;
14126 this.searchElement = function (searchExp) {
14127 var regex = new RegExp("\\b" + searchExp, "gi");
14128 var position = this.searchKeys.regexIndexOf(regex, this.counter + 1, true);
14129 if (position > -1) {
14130 this.counter = position;
14131 this.moveFocus(this.counter);
14134 this.startTimer = function (time) {
14135 if (searchString === '') {
14136 $timeout(function () {
14141 this.resetCounter = function (count) {
14142 this.counter = count;
14144 this.moveNext = function (count) {
14145 this.counter = (this.counter + count) < this.maxLength ? this.counter + count : (this.circularTraversal ? 0 : this.counter);
14146 if (this.disableNodes[this.counter]) {
14147 if ((this.counter + count) < this.maxLength) {
14148 this.moveNext(count);
14151 this.moveFocus(this.counter);
14154 this.movePrev = function (count) {
14155 this.counter = (this.counter - count) > -1 ? this.counter - count : (this.circularTraversal ? this.maxLength-1 : this.counter);
14156 if (this.disableNodes[this.counter]) {
14157 if ((this.counter - count) > -1) {
14158 this.movePrev(count);
14161 this.moveFocus(this.counter);
14164 this.moveFocus = function (index) {
14165 this.childElements[index][0].focus();
14168 this.keyDownHandler = function (ev, count) {
14169 if (angular.isDefined(count) && !isNaN(count) && count !== this.counter) {
14170 this.resetCounter(count);
14174 ev.keyCode = ev.which;
14175 } else if (ev.charCode) {
14176 ev.keyCode = ev.charCode;
14180 if (this.prev && this.prev.indexOf(ev.keyCode.toString()) > -1) {
14182 ev.preventDefault();
14183 ev.stopPropagation();
14184 } else if (this.next && this.next.indexOf(ev.keyCode.toString()) > -1) {
14186 ev.preventDefault();
14187 ev.stopPropagation();
14188 } else if (this.up && this.up.indexOf(ev.keyCode.toString()) > -1) {
14189 if (this.type === 'table') {
14190 this.movePrev(this.columns);
14191 ev.preventDefault();
14192 ev.stopPropagation();
14194 } else if (this.down && this.down.indexOf(ev.keyCode.toString()) > -1) {
14195 if (this.type === 'table') {
14196 this.moveNext(this.columns);
14197 ev.preventDefault();
14198 ev.stopPropagation();
14200 } else if (ev.keyCode === keymap.KEY.HOME) {
14201 var firstIndex = 0;
14202 while (this.disableNodes[firstIndex] !== false) {
14205 var count = this.counter - firstIndex;
14206 this.movePrev(count);
14207 ev.preventDefault();
14208 ev.stopPropagation();
14209 } else if (ev.keyCode === keymap.KEY.END) {
14210 var lastIndex = this.childElements.length - 1;
14211 while (this.disableNodes[lastIndex] !== false) {
14214 var count = lastIndex - this.counter;
14215 this.moveNext(count);
14216 ev.preventDefault();
14217 ev.stopPropagation();
14218 } else if (ev.keyCode >= 48 && ev.keyCode <= 105) {
14219 if (this.enableSearch) {
14220 this.startTimer(b2bUtilitiesConfig.searchTimer);
14221 searchString = searchString + (keymap.MAP[ev.keyCode] || '');
14222 this.searchElement(searchString);
14223 ev.preventDefault();
14224 ev.stopPropagation();
14230 link: function (scope, elem, attr, ctrl) {
14231 ctrl.prev = attr.prev ? attr.prev.split(',') : b2bUtilitiesConfig.prev.split(',');
14232 ctrl.next = attr.next ? attr.next.split(',') : b2bUtilitiesConfig.next.split(',');
14233 ctrl.type = attr.type ? attr.type : b2bUtilitiesConfig.type;
14234 if (ctrl.type === 'table') {
14235 ctrl.up = attr.up ? attr.up.split(',') : b2bUtilitiesConfig.up.split(',');
14236 ctrl.down = attr.down ? attr.down.split(',') : b2bUtilitiesConfig.down.split(',');
14237 ctrl.columns = attr.columns ? parseInt(attr.columns, 10) : b2bUtilitiesConfig.columns;
14240 elem.bind('keydown', function (ev) {
14241 ctrl.keyDownHandler(ev);
14247 .directive('b2bKeyItem', [function () {
14250 link: function (scope, elem, attr, ctrl) {
14251 var parentCtrl = (elem.parent() && elem.parent().controller('b2bKey')) || undefined;
14252 if (angular.isDefined(parentCtrl)) {
14253 var count = parentCtrl.registerElement(elem, attr.searchKey);
14254 elem.bind('keydown', function (ev) {
14255 parentCtrl.keyDownHandler(ev, count);
14257 scope.$watch(attr.b2bKeyItem, function (value) {
14258 value = value === undefined ? true : value;
14259 parentCtrl.toggleDisable(count, !value);
14261 scope.$on('$destroy', function () {
14262 parentCtrl.toggleDisable(count, true);
14269 .directive('b2bElementFocus', [function () {
14272 link: function (scope, elem, attr, ctrl) {
14273 scope.$watch(attr.b2bElementFocus, function (value) {
14274 if (value === true) {
14283 .directive('b2bAppendElement', ['$compile', function ($compile) {
14286 link: function (scope, elem, attr, ctrl) {
14287 var parameters = attr.b2bAppendElement.split(':');
14288 if (parameters.length === 1) {
14289 elem.append(scope.$eval(parameters[0]));
14290 } else if (parameters.length === 2) {
14291 if (parameters[1] === 'compile') {
14292 var element = angular.element('<span>' + scope.$eval(parameters[0]) + '</span>');
14293 elem.append($compile(element)(scope));
14301 .directive('b2bKeyItemRefreshInNgRepeat', [function () {
14304 require: '^^b2bKey',
14305 link: function (scope, elem, attr, parentCtrl) {
14306 if (angular.isDefined(parentCtrl)) {
14308 var attrToObserve = 'attrToObserve';
14310 if (attr.b2bKeyItemRefreshInNgRepeat) {
14311 attrToObserve = 'b2bKeyItemRefreshInNgRepeat';
14314 attr.$observe(attrToObserve, function (newVal, oldVal) {
14315 if (newVal && newVal !== oldVal) {
14316 parentCtrl.resetChildElementsList();
14324 .constant('b2bMaskConfig', {
14330 clearOnBlur: false,
14331 clearOnBlurPlaceholder: false,
14333 eventsToHandle: ['input', 'keyup', 'click', 'focus'],
14334 addDefaultPlaceholder: true,
14335 allowInvalidValue: true
14338 * @param {boolean} modelViewValue - If this is set to true, then the model value bound with ng-model will be the same as the $viewValue meaning it will contain any static mask characters present in the mask definition. This will not set the model value to a $viewValue that is considered invalid.
14339 * @param {String} maskPlaceholder - Allows customizing the mask placeholder when a user has focused the input element and while typing in their value
14340 * @param {String} maskPlaceholderChar - Allows customizing the mask placeholder character. The default mask placeholder is _.
14341 * @param {boolean} addDefaultPlaceholder - The default placeholder is constructed from the ui-mask definition so a mask of 999-9999 would have a default placeholder of ___-____; unless you have overridden the default placeholder character.
14343 .directive('b2bMask', ['b2bMaskConfig', function(b2bMaskConfig) {
14345 require: 'ngModel',
14347 link: function(scope, element, attrs, ctrl) {
14348 var maskProcessed = false, eventsBound = false,
14349 maskCaretMap, maskPatterns, maskPlaceholder, maskComponents,
14350 // Minimum required length of the value to be considered valid
14352 value, valueMasked, isValid,
14353 // Vars for initializing/uninitializing
14354 originalPlaceholder = attrs.placeholder,
14355 originalMaxlength = attrs.maxlength,
14356 // Vars used exclusively in eventHandler()
14357 oldValue, oldValueUnmasked, oldCaretPosition, oldSelectionLength,
14358 // Used for communicating if a backspace operation should be allowed between
14359 // keydownHandler and eventHandler
14362 var options = b2bMaskConfig;
14364 function isFocused (elem) {
14365 return elem === document.activeElement && (!document.hasFocus || document.hasFocus()) && !!(elem.type || elem.href || ~elem.tabIndex);
14368 var originalIsEmpty = ctrl.$isEmpty;
14369 ctrl.$isEmpty = function(value) {
14370 if (maskProcessed) {
14371 return originalIsEmpty(unmaskValue(value || ''));
14373 return originalIsEmpty(value);
14377 function initialize(maskAttr) {
14378 if (!angular.isDefined(maskAttr)) {
14379 return uninitialize();
14381 processRawMask(maskAttr);
14382 if (!maskProcessed) {
14383 return uninitialize();
14385 initializeElement();
14386 bindEventListeners();
14390 function initPlaceholder(placeholderAttr) {
14391 if ( ! placeholderAttr) {
14394 maskPlaceholder = placeholderAttr;
14395 /* If the mask is processed, then we need to update the value
14396 but don't set the value if there is nothing entered into the element
14397 and there is a placeholder attribute on the element because that
14398 will only set the value as the blank maskPlaceholder
14399 and override the placeholder on the element */
14400 if (maskProcessed && !(element.val().length === 0 && angular.isDefined(attrs.placeholder))) {
14401 element.val(maskValue(unmaskValue(element.val())));
14405 function initPlaceholderChar() {
14406 return initialize(attrs.uiMask);
14409 var modelViewValue = false;
14411 attrs.$observe('modelViewValue', function(val) {
14412 if (val === 'true') {
14413 modelViewValue = true;
14417 attrs.$observe('allowInvalidValue', function(val) {
14418 linkOptions.allowInvalidValue = val === ''? true : !!val;
14419 formatter(ctrl.$modelValue);
14422 function formatter(fromModelValue) {
14423 if (!maskProcessed) {
14424 return fromModelValue;
14426 value = unmaskValue(fromModelValue || '');
14427 isValid = validateValue(value);
14428 ctrl.$setValidity('mask', isValid);
14430 if (!value.length) return undefined;
14431 if (isValid || linkOptions.allowInvalidValue) {
14432 return maskValue(value);
14438 function parser(fromViewValue) {
14439 if (!maskProcessed) {
14440 return fromViewValue;
14442 value = unmaskValue(fromViewValue || '');
14443 isValid = validateValue(value);
14444 /* We have to set viewValue manually as the reformatting of the input
14445 value performed by eventHandler() doesn't happen until after
14446 this parser is called, which causes what the user sees in the input
14447 to be out-of-sync with what the ctrl's $viewValue is set to. */
14448 ctrl.$viewValue = value.length ? maskValue(value) : '';
14449 ctrl.$setValidity('mask', isValid);
14451 if (isValid || linkOptions.allowInvalidValue) {
14452 return modelViewValue ? ctrl.$viewValue : value;
14456 var linkOptions = {};
14459 if (attrs.b2bMaskOptions) {
14460 linkOptions = scope.$eval('[' + attrs.b2bMaskOptions + ']');
14461 if (angular.isObject(linkOptions[0])) {
14462 // we can't use angular.copy nor angular.extend, they lack the power to do a deep merge
14463 linkOptions = (function(original, current) {
14464 for (var i in original) {
14465 if (Object.prototype.hasOwnProperty.call(original, i)) {
14466 if (current[i] === undefined) {
14467 current[i] = angular.copy(original[i]);
14469 if (angular.isObject(current[i]) && !angular.isArray(current[i])) {
14470 current[i] = angular.extend({}, original[i], current[i]);
14476 })(options, linkOptions[0]);
14478 linkOptions = options; //gotta be a better way to do this..
14481 linkOptions = options;
14484 attrs.$observe('b2bMask', initialize);
14485 if (angular.isDefined(attrs.maskPlaceholder)) {
14486 attrs.$observe('maskPlaceholder', initPlaceholder);
14489 attrs.$observe('placeholder', initPlaceholder);
14491 if (angular.isDefined(attrs.maskPlaceholderChar)) {
14492 attrs.$observe('maskPlaceholderChar', initPlaceholderChar);
14495 ctrl.$formatters.unshift(formatter);
14496 ctrl.$parsers.unshift(parser);
14498 function uninitialize() {
14499 maskProcessed = false;
14500 unbindEventListeners();
14502 if (angular.isDefined(originalPlaceholder)) {
14503 element.attr('placeholder', originalPlaceholder);
14505 element.removeAttr('placeholder');
14508 if (angular.isDefined(originalMaxlength)) {
14509 element.attr('maxlength', originalMaxlength);
14511 element.removeAttr('maxlength');
14514 element.val(ctrl.$modelValue);
14515 ctrl.$viewValue = ctrl.$modelValue;
14519 function initializeElement() {
14520 value = oldValueUnmasked = unmaskValue(ctrl.$modelValue || '');
14521 valueMasked = oldValue = maskValue(value);
14522 isValid = validateValue(value);
14523 if (attrs.maxlength) { // Double maxlength to allow pasting new val at end of mask
14524 element.attr('maxlength', maskCaretMap[maskCaretMap.length - 1] * 2);
14526 if ( ! originalPlaceholder && linkOptions.addDefaultPlaceholder) {
14527 element.attr('placeholder', maskPlaceholder);
14529 var viewValue = ctrl.$modelValue;
14530 var idx = ctrl.$formatters.length;
14532 viewValue = ctrl.$formatters[idx](viewValue);
14534 ctrl.$viewValue = viewValue || '';
14538 function bindEventListeners() {
14542 element.bind('blur', blurHandler);
14543 element.bind('mousedown mouseup', mouseDownUpHandler);
14544 element.bind('keydown', keydownHandler);
14545 element.bind(linkOptions.eventsToHandle.join(' '), eventHandler);
14546 eventsBound = true;
14549 function unbindEventListeners() {
14550 if (!eventsBound) {
14553 element.unbind('blur', blurHandler);
14554 element.unbind('mousedown', mouseDownUpHandler);
14555 element.unbind('mouseup', mouseDownUpHandler);
14556 element.unbind('keydown', keydownHandler);
14557 element.unbind('input', eventHandler);
14558 element.unbind('keyup', eventHandler);
14559 element.unbind('click', eventHandler);
14560 element.unbind('focus', eventHandler);
14561 eventsBound = false;
14564 function validateValue(value) {
14565 // Zero-length value validity is ngRequired's determination
14566 return value.length ? value.length >= minRequiredLength : true;
14569 function unmaskValue(value) {
14570 var valueUnmasked = '',
14571 input = element[0],
14572 maskPatternsCopy = maskPatterns.slice(),
14573 selectionStart = oldCaretPosition,
14574 selectionEnd = selectionStart + getSelectionLength(input),
14575 valueOffset, valueDelta, tempValue = '';
14576 // Preprocess by stripping mask components from value
14577 value = value.toString();
14579 valueDelta = value.length - maskPlaceholder.length;
14580 angular.forEach(maskComponents, function(component) {
14581 var position = component.position;
14582 //Only try and replace the component if the component position is not within the selected range
14583 //If component was in selected range then it was removed with the user input so no need to try and remove that component
14584 if (!(position >= selectionStart && position < selectionEnd)) {
14585 if (position >= selectionStart) {
14586 position += valueDelta;
14588 if (value.substring(position, position + component.value.length) === component.value) {
14589 tempValue += value.slice(valueOffset, position);// + value.slice(position + component.value.length);
14590 valueOffset = position + component.value.length;
14594 value = tempValue + value.slice(valueOffset);
14595 angular.forEach(value.split(''), function(chr) {
14596 if (maskPatternsCopy.length && maskPatternsCopy[0].test(chr)) {
14597 valueUnmasked += chr;
14598 maskPatternsCopy.shift();
14602 return valueUnmasked;
14605 function maskValue(unmaskedValue) {
14606 var valueMasked = '',
14607 maskCaretMapCopy = maskCaretMap.slice();
14609 angular.forEach(maskPlaceholder.split(''), function(chr, i) {
14610 if (unmaskedValue.length && i === maskCaretMapCopy[0]) {
14611 valueMasked += unmaskedValue.charAt(0) || '_';
14612 unmaskedValue = unmaskedValue.substr(1);
14613 maskCaretMapCopy.shift();
14616 valueMasked += chr;
14619 return valueMasked;
14622 function getPlaceholderChar(i) {
14623 var placeholder = angular.isDefined(attrs.uiMaskPlaceholder) ? attrs.uiMaskPlaceholder : attrs.placeholder,
14624 defaultPlaceholderChar;
14626 if (angular.isDefined(placeholder) && placeholder[i]) {
14627 return placeholder[i];
14629 defaultPlaceholderChar = angular.isDefined(attrs.uiMaskPlaceholderChar) && attrs.uiMaskPlaceholderChar ? attrs.uiMaskPlaceholderChar : '_';
14630 return (defaultPlaceholderChar.toLowerCase() === 'space') ? ' ' : defaultPlaceholderChar[0];
14634 /* Generate array of mask components that will be stripped from a masked value
14635 before processing to prevent mask components from being added to the unmasked value.
14636 E.g., a mask pattern of '+7 9999' won't have the 7 bleed into the unmasked value. */
14637 function getMaskComponents() {
14638 var maskPlaceholderChars = maskPlaceholder.split(''),
14639 maskPlaceholderCopy, components;
14641 /* maskCaretMap can have bad values if the input has the ui-mask attribute implemented as an obversable property, e.g. the demo page */
14642 if (maskCaretMap && !isNaN(maskCaretMap[0])) {
14643 /* Instead of trying to manipulate the RegEx based on the placeholder characters
14644 we can simply replace the placeholder characters based on the already built
14645 maskCaretMap to underscores and leave the original working RegEx to get the proper
14647 angular.forEach(maskCaretMap, function(value) {
14648 maskPlaceholderChars[value] = '_';
14651 maskPlaceholderCopy = maskPlaceholderChars.join('');
14652 components = maskPlaceholderCopy.replace(/[_]+/g, '_').split('_');
14653 components = components.filter(function(s) {
14657 /* need a string search offset in cases where the mask contains multiple identical components
14658 E.g., a mask of 99.99.99-999.99 */
14660 return components.map(function(c) {
14661 var componentPosition = maskPlaceholderCopy.indexOf(c, offset);
14662 offset = componentPosition + 1;
14665 position: componentPosition
14670 function processRawMask(mask) {
14671 var characterCount = 0;
14675 maskPlaceholder = '';
14677 if (angular.isString(mask)) {
14678 minRequiredLength = 0;
14680 var isOptional = false,
14681 numberOfOptionalCharacters = 0,
14682 splitMask = mask.split('');
14684 var inEscape = false;
14685 angular.forEach(splitMask, function(chr, i) {
14688 maskPlaceholder += chr;
14691 else if (linkOptions.escChar === chr) {
14694 else if (linkOptions.maskDefinitions[chr]) {
14695 maskCaretMap.push(characterCount);
14697 maskPlaceholder += getPlaceholderChar(i - numberOfOptionalCharacters);
14698 maskPatterns.push(linkOptions.maskDefinitions[chr]);
14702 minRequiredLength++;
14705 isOptional = false;
14707 else if (chr === '?') {
14709 numberOfOptionalCharacters++;
14712 maskPlaceholder += chr;
14717 // Caret position immediately following last position is valid.
14718 maskCaretMap.push(maskCaretMap.slice().pop() + 1);
14720 maskComponents = getMaskComponents();
14721 maskProcessed = maskCaretMap.length > 1 ? true : false;
14724 var prevValue = element.val();
14725 function blurHandler() {
14726 if (linkOptions.clearOnBlur || ((linkOptions.clearOnBlurPlaceholder) && (value.length === 0) && attrs.placeholder)) {
14727 oldCaretPosition = 0;
14728 oldSelectionLength = 0;
14729 if (!isValid || value.length === 0) {
14732 scope.$apply(function() {
14733 //only $setViewValue when not $pristine to avoid changing $pristine state.
14734 if (!ctrl.$pristine) {
14735 ctrl.$setViewValue('');
14740 //Check for different value and trigger change.
14741 if (value !== prevValue) {
14742 var currentVal = element.val();
14743 var isTemporarilyEmpty = value === '' && currentVal && angular.isDefined(attrs.uiMaskPlaceholderChar) && attrs.uiMaskPlaceholderChar === 'space';
14744 if(isTemporarilyEmpty) {
14747 triggerChangeEvent(element[0]);
14748 if(isTemporarilyEmpty) {
14749 element.val(currentVal);
14755 function triggerChangeEvent(element) {
14757 if (angular.isFunction(window.Event) && !element.fireEvent) {
14758 // modern browsers and Edge
14760 change = new Event('change', {
14766 //this is for certain mobile browsers that have the Event object
14767 //but don't support the Event constructor
14768 change = document.createEvent('HTMLEvents');
14769 change.initEvent('change', false, true);
14771 element.dispatchEvent(change);
14773 } else if ('createEvent' in document) {
14775 change = document.createEvent('HTMLEvents');
14776 change.initEvent('change', false, true);
14777 element.dispatchEvent(change);
14779 else if (element.fireEvent) {
14781 element.fireEvent('onchange');
14785 function mouseDownUpHandler(e) {
14786 if (e.type === 'mousedown') {
14787 element.bind('mouseout', mouseoutHandler);
14789 element.unbind('mouseout', mouseoutHandler);
14793 element.bind('mousedown mouseup', mouseDownUpHandler);
14795 function mouseoutHandler() {
14796 oldSelectionLength = getSelectionLength(this);
14797 element.unbind('mouseout', mouseoutHandler);
14800 function keydownHandler(e) {
14801 var isKeyBackspace = e.which === 8,
14802 caretPos = getCaretPosition(this) - 1 || 0, //value in keydown is pre change so bump caret position back to simulate post change
14803 isCtrlZ = e.which === 90 && e.ctrlKey; //ctrl+z pressed
14805 if (isKeyBackspace) {
14806 while(caretPos >= 0) {
14807 if (isValidCaretPosition(caretPos)) {
14808 //re-adjust the caret position.
14809 //Increment to account for the initial decrement to simulate post change caret position
14810 setCaretPosition(this, caretPos + 1);
14815 preventBackspace = caretPos === -1;
14819 // prevent IE bug - value should be returned to initial state
14821 e.preventDefault();
14825 function eventHandler(e) {
14827 // Allows more efficient minification
14828 var eventWhich = e.which,
14829 eventType = e.type;
14831 // Prevent shift and ctrl from mucking with old values
14832 if (eventWhich === 16 || eventWhich === 91) {
14836 var val = element.val(),
14839 valAltered = false,
14840 valUnmasked = unmaskValue(val),
14841 valUnmaskedOld = oldValueUnmasked,
14842 caretPos = getCaretPosition(this) || 0,
14843 caretPosOld = oldCaretPosition || 0,
14844 caretPosDelta = caretPos - caretPosOld,
14845 caretPosMin = maskCaretMap[0],
14846 caretPosMax = maskCaretMap[valUnmasked.length] || maskCaretMap.slice().shift(),
14847 selectionLenOld = oldSelectionLength || 0,
14848 isSelected = getSelectionLength(this) > 0,
14849 wasSelected = selectionLenOld > 0,
14850 // Case: Typing a character to overwrite a selection
14851 isAddition = (val.length > valOld.length) || (selectionLenOld && val.length > valOld.length - selectionLenOld),
14852 // Case: Delete and backspace behave identically on a selection
14853 isDeletion = (val.length < valOld.length) || (selectionLenOld && val.length === valOld.length - selectionLenOld),
14854 isSelection = (eventWhich >= 37 && eventWhich <= 40) && e.shiftKey, // Arrow key codes
14856 isKeyLeftArrow = eventWhich === 37,
14857 // Necessary due to "input" event not providing a key code
14858 isKeyBackspace = eventWhich === 8 || (eventType !== 'keyup' && isDeletion && (caretPosDelta === -1)),
14859 isKeyDelete = eventWhich === 46 || (eventType !== 'keyup' && isDeletion && (caretPosDelta === 0) && !wasSelected),
14860 // Handles cases where caret is moved and placed in front of invalid maskCaretMap position. Logic below
14861 // ensures that, on click or leftward caret placement, caret is moved leftward until directly right of
14862 // non-mask character. Also applied to click since users are (arguably) more likely to backspace
14863 // a character when clicking within a filled input.
14864 caretBumpBack = (isKeyLeftArrow || isKeyBackspace || eventType === 'click') && caretPos > caretPosMin;
14866 oldSelectionLength = getSelectionLength(this);
14868 // These events don't require any action
14869 if (isSelection || (isSelected && (eventType === 'click' || eventType === 'keyup' || eventType === 'focus'))) {
14873 if (isKeyBackspace && preventBackspace) {
14874 element.val(maskPlaceholder);
14875 // This shouldn't be needed but for some reason after aggressive backspacing the ctrl $viewValue is incorrect.
14876 // This keeps the $viewValue updated and correct.
14877 scope.$apply(function () {
14878 ctrl.$setViewValue(''); // $setViewValue should be run in angular context, otherwise the changes will be invisible to angular and user code.
14880 setCaretPosition(this, caretPosOld);
14884 // User attempted to delete but raw value was unaffected--correct this grievous offense
14885 if ((eventType === 'input') && isDeletion && !wasSelected && valUnmasked === valUnmaskedOld) {
14886 while (isKeyBackspace && caretPos > caretPosMin && !isValidCaretPosition(caretPos)) {
14889 while (isKeyDelete && caretPos < caretPosMax && maskCaretMap.indexOf(caretPos) === -1) {
14892 var charIndex = maskCaretMap.indexOf(caretPos);
14893 // Strip out non-mask character that user would have deleted if mask hadn't been in the way.
14894 valUnmasked = valUnmasked.substring(0, charIndex) + valUnmasked.substring(charIndex + 1);
14896 // If value has not changed, don't want to call $setViewValue, may be caused by IE raising input event due to placeholder
14897 if (valUnmasked !== valUnmaskedOld)
14902 valMasked = maskValue(valUnmasked);
14904 oldValue = valMasked;
14905 oldValueUnmasked = valUnmasked;
14907 //additional check to fix the problem where the viewValue is out of sync with the value of the element.
14908 //better fix for commit 2a83b5fb8312e71d220a497545f999fc82503bd9 (I think)
14909 if (!valAltered && val.length > valMasked.length)
14912 element.val(valMasked);
14914 //we need this check. What could happen if you don't have it is that you'll set the model value without the user
14915 //actually doing anything. Meaning, things like pristine and touched will be set.
14917 scope.$apply(function () {
14918 ctrl.$setViewValue(valMasked); // $setViewValue should be run in angular context, otherwise the changes will be invisible to angular and user code.
14922 // Caret Repositioning
14923 // Ensure that typing always places caret ahead of typed character in cases where the first char of
14924 // the input is a mask char and the caret is placed at the 0 position.
14925 if (isAddition && (caretPos <= caretPosMin)) {
14926 caretPos = caretPosMin + 1;
14929 if (caretBumpBack) {
14933 // Make sure caret is within min and max position limits
14934 caretPos = caretPos > caretPosMax ? caretPosMax : caretPos < caretPosMin ? caretPosMin : caretPos;
14936 // Scoot the caret back or forth until it's in a non-mask position and within min/max position limits
14937 while (!isValidCaretPosition(caretPos) && caretPos > caretPosMin && caretPos < caretPosMax) {
14938 caretPos += caretBumpBack ? -1 : 1;
14941 if ((caretBumpBack && caretPos < caretPosMax) || (isAddition && !isValidCaretPosition(caretPosOld))) {
14944 oldCaretPosition = caretPos;
14945 setCaretPosition(this, caretPos);
14948 function isValidCaretPosition(pos) {
14949 return maskCaretMap.indexOf(pos) > -1;
14952 function getCaretPosition(input) {
14955 if (input.selectionStart !== undefined) {
14956 return input.selectionStart;
14957 } else if (document.selection) {
14958 if (isFocused(element[0])) {
14961 var selection = document.selection.createRange();
14962 selection.moveStart('character', input.value ? -input.value.length : 0);
14963 return selection.text.length;
14969 function setCaretPosition(input, pos) {
14972 if (input.offsetWidth === 0 || input.offsetHeight === 0) {
14973 return; // Input's hidden
14975 if (input.setSelectionRange) {
14976 if (isFocused(element[0])) {
14978 input.setSelectionRange(pos, pos);
14981 else if (input.createTextRange) {
14983 var range = input.createTextRange();
14984 range.collapse(true);
14985 range.moveEnd('character', pos);
14986 range.moveStart('character', pos);
14991 function getSelectionLength(input) {
14994 if (input.selectionStart !== undefined) {
14995 return (input.selectionEnd - input.selectionStart);
14997 if (window.getSelection) {
14998 return (window.getSelection().toString().length);
15000 if (document.selection) {
15001 return (document.selection.createRange().text.length);
15008 .filter('b2bMultiSepartorHighlight', function($sce) {
15009 return function(text, searchText, searchSeperator) {
15010 var splitText = function(string) {
15011 if(angular.isDefined(searchSeperator)){
15012 if (string.indexOf(searchSeperator) > -1) {
15013 return string.split(searchSeperator);
15022 var newText = splitText(text);
15023 var newPhrase = splitText(searchText);
15024 if (angular.isArray(newPhrase)) {
15025 for (var i = 0; i < newText.length; i++) {
15027 text = newText[i].replace(new RegExp('(' + newPhrase[i] + ')', 'gi'),
15028 '<span class="b2b-search-hightlight">$1</span>');
15030 text = text + searchSeperator + ' ' + (newPhrase[i] ? newText[i].replace(new RegExp('(' + newPhrase[i] + ')', 'gi'),
15031 '<span class="b2b-search-hightlight">$1</span>') : newText[i]);
15035 text = text.replace(new RegExp('(' + searchText + ')', 'gi'),
15036 '<span class="b2b-search-hightlight">$1</span>');
15039 return $sce.trustAsHtml(text)
15043 .factory('b2bUserAgent', [function() {
15044 var _isMobile = function() {
15045 if(/Android/i.test(navigator.userAgent)){
15046 return /Mobile/i.test(navigator.userAgent);
15048 return /Android|webOS|iPhone|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent);
15052 var _notMobile = function() {
15053 if(/Android/i.test(navigator.userAgent)){
15054 return !/Mobile/i.test(navigator.userAgent);
15056 return !/Android|webOS|iPhone|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent);
15060 var _isIE = function() {
15061 return /msie|trident/i.test(navigator.userAgent);
15063 var _isFF = function() {
15064 return /Firefox/.test(navigator.userAgent);
15066 var _isChrome = function() {
15067 return /Chrome/.test(navigator.userAgent);
15069 var _isSafari = function() {
15070 return /Safari/.test(navigator.userAgent) && !/Chrome/.test(navigator.userAgent);
15074 isMobile: _isMobile,
15075 notMobile: _notMobile,
15078 isChrome: _isChrome,
15079 isSafari: _isSafari
15082 .run(['$document', 'b2bUserAgent', function($document, b2bUserAgent) {
15083 var html = $document.find('html').eq(0);
15084 if (b2bUserAgent.isIE()) {
15085 html.addClass('isIE');
15087 html.removeClass('isIE');
15093 String.prototype.toSnakeCase = function () {
15094 return this.replace(/([A-Z])/g, function ($1) {
15095 return "-" + $1.toLowerCase();
15098 var concat = function (character, times) {
15099 character = character || '';
15100 times = (!isNaN(times) && times) || 0;
15101 var finalChar = '';
15102 for (var i = 0; i < times; i++) {
15103 finalChar += character;
15108 // direction: true for left and false for right
15109 var pad = function (actualString, width, character, direction) {
15110 actualString = actualString || '';
15111 width = (!isNaN(width) && width) || 0;
15112 character = character || '';
15113 if (width > actualString.length) {
15115 return concat(character, (width - actualString.length)) + actualString;
15117 return actualString + concat(character, (width - actualString.length));
15120 return actualString;
15123 String.prototype.lPad = function (width, character) {
15124 return pad(this, width, character, true);
15127 String.prototype.rPad = function (width, character) {
15128 return pad(this, width, character, false);
15131 if (!Array.prototype.indexOf) {
15132 Array.prototype.indexOf = function (val) {
15133 for (var index = 0; index < this.length; index++) {
15134 if (this[index] === val) {
15142 if (!Array.prototype.regexIndexOf) {
15143 Object.defineProperty(Array.prototype, 'regexIndexOf', {
15145 value: function (regex, startIndex, loop) {
15146 startIndex = startIndex && startIndex > -1 ? startIndex : 0;
15147 for (var index = startIndex; index < this.length; index++) {
15148 if (this[index].toString().match(regex)) {
15153 for (var index = 0; index < startIndex; index++) {
15154 if (this[index].toString().match(regex)) {
15164 angular.module("b2bTemplate/audioPlayer/audioPlayer.html", []).run(["$templateCache", function($templateCache) {
15165 $templateCache.put("b2bTemplate/audioPlayer/audioPlayer.html",
15166 "<div class=\"b2b-audio\">\n" +
15167 " <audio preload=\"auto\">\n" +
15168 " <source ng-src=\"{{audio.mp3 | trustedAudioUrl}}\" type=\"audio/mp3\"></source>\n" +
15169 " <i>Your browser does not support the audio element.</i>\n" +
15172 " <div audio-play-pause-icon class=\"controls-wrapper\" ng-click=\"toggleAudio()\" tabindex=\"0\" b2b-accessibility-click=\"13,32\" role=\"button\" aria-label=\"{{isPlayInProgress?'pause':'play'}}\">\n" +
15173 " <i class=\"icoControls-pointer\" ng-show='!isPlayInProgress'></i>\n" +
15174 " <i class=\"icoControls-pause\" ng-show='isPlayInProgress'></i>\n" +
15177 " <div class=\"seek-bar-container-wrapper\">\n" +
15178 " <b2b-seek-bar min=\"0\" max=\"{{audio.duration}}\" step=\"1\" skip-interval=\"{{audio.timeShiftInSeconds}}\" no-aria-label ng-model=\"audio.currentTime\" on-drag-init=\"isAudioDragging=true\" on-drag-end=\"setAudioPosition(audio.currentTime)\"></b2b-seek-bar>\n" +
15179 " <div class=\"timing-container\">\n" +
15180 " <span class=\"timing-container-left\">{{timeFormatter(audio.currentTime)}}</span>\n" +
15181 " <span class=\"timing-container-right\">{{timeFormatter(audio.duration)}}</span>\n" +
15182 " <div class=\"timing-container-spacer\"></div>\n" +
15186 " <b2b-flyout>\n" +
15187 " <div tabindex=\"0\" b2b-accessibility-click=\"13,32\" role=\"button\" aria-label=\"Volume pop-over. Volume set at {{audio.currentVolume}}%\" class=\"controls-wrapper audio-volume-control\" b2b-flyout-toggler>\n" +
15188 " <i class=\"icoControls-mutespeakers\" ng-show=\"audio.currentVolume === 0\"></i>\n" +
15189 " <i class=\"icoControls-volumedown\" ng-show=\"audio.currentVolume > 0 && audio.currentVolume <= 50\"></i>\n" +
15190 " <i class=\"icoControls-volumeup\" ng-show=\"audio.currentVolume > 50\"></i>\n" +
15193 " <b2b-flyout-content horizontal-placement=\"center\" flyout-style=\"width:70px; height:190px;\" vertical-placement=\"above\">\n" +
15194 " <div class=\"b2b-audio-popover text-center\">\n" +
15195 " <span>Max</span>\n" +
15196 " <b2b-seek-bar min=\"0\" max=\"100\" step=\"1\" skip-interval=\"10\" vertical data-ng-model=\"audio.currentVolume\" class='volume-popover'></b2b-seek-bar>\n" +
15197 " <div class=\"min-label\">Min</div>\n" +
15199 " </b2b-flyout-content>\n" +
15200 " </b2b-flyout>\n" +
15204 angular.module("b2bTemplate/audioRecorder/audioRecorder.html", []).run(["$templateCache", function($templateCache) {
15205 $templateCache.put("b2bTemplate/audioRecorder/audioRecorder.html",
15206 "<div class=\"b2b-audio-recorder row\">\n" +
15207 " <div class=\"b2b-elapsed-time span11\">\n" +
15208 " <div ng-if=\"isRecording\">\n" +
15209 " <span style=\"padding-right: 25px;\">{{config.whileRecordingMessage}}</span>\n" +
15210 " <span>{{timeFormatter(elapsedTime)}}</span>\n" +
15212 " <span ng-if=\"!isRecording\">{{config.startRecordingMessage}}</span>\n" +
15214 " <div class=\"b2b-controls\" title=\"{{isRecording ? 'Stop' : 'REC'}}\" b2b-accessibility-click=\"13,32\" ng-click=\"toggleRecording()\" role=\"button\">\n" +
15215 " <i ng-if=\"isRecording\" class=\"icoControls-stop\" ></i>\n" +
15216 " <i ng-if=\"!isRecording\" class=\"icoControls-record\"></i>\n" +
15221 angular.module("b2bTemplate/backToTop/backToTop.html", []).run(["$templateCache", function($templateCache) {
15222 $templateCache.put("b2bTemplate/backToTop/backToTop.html",
15223 "<button class=\"btn-arrow b2b-backtotop-button\" type=\"button\" aria-label=\"Back to top\">\n" +
15224 " <div class=\"btn-secondary b2b-top-btn\">\n" +
15225 " <i class=\"icoControls-upPRIMARY\" role=\"img\"></i>\n" +
15231 angular.module("b2bTemplate/boardstrip/b2bAddBoard.html", []).run(["$templateCache", function($templateCache) {
15232 $templateCache.put("b2bTemplate/boardstrip/b2bAddBoard.html",
15233 "<div tabindex=\"0\" role=\"menuitem\" b2b-accessibility-click=\"13,32\" ng-click=\"addBoard()\" aria-label=\"Add Board\" class=\"boardstrip-item--add\">\n" +
15234 " <div class=\"centered\"><i aria-hidden=\"true\" class=\"icoControls-add-maximize\"></i> Add board</div>\n" +
15238 angular.module("b2bTemplate/boardstrip/b2bBoard.html", []).run(["$templateCache", function($templateCache) {
15239 $templateCache.put("b2bTemplate/boardstrip/b2bBoard.html",
15240 "<li b2b-board-navigation tabindex=\"-1\" role=\"menuitem\" aria-label=\"{{boardLabel}} {{getCurrentIndex()===boardIndex?'selected':''}}\" b2b-accessibility-click=\"13,32\" ng-click=\"selectBoard(boardIndex)\" class=\"board-item\" ng-class=\"{'selected': getCurrentIndex()===boardIndex}\">\n" +
15241 " <div ng-transclude></div>\n" +
15242 " <div class=\"board-caret\" ng-if=\"getCurrentIndex()===boardIndex\">\n" +
15243 " <div class=\"board-caret-indicator\"></div>\n" +
15244 " <div class=\"board-caret-arrow-up\"></div>\n" +
15249 angular.module("b2bTemplate/boardstrip/b2bBoardstrip.html", []).run(["$templateCache", function($templateCache) {
15250 $templateCache.put("b2bTemplate/boardstrip/b2bBoardstrip.html",
15251 "<div class=\"b2b-boardstrip\">\n" +
15252 " <div class=\"boardstrip-reel\" role=\"menu\">\n" +
15253 " <div class=\"prev-items\">\n" +
15254 " <!-- <i tabindex=\"{{isPrevBoard() ? 0 : -1}}\" ng-class=\"{'disabled': !isPrevBoard()}\" role=\"menuitem\" aria-label=\"See previous boards\" b2b-accessibility-click=\"13,32\" ng-click=\"prevBoard()\" class=\"arrow icoControls-left\"></i> -->\n" +
15255 " <button class=\"btn-arrow arrow\" b2b-accessibility-click=\"13,32\" ng-click=\"prevBoard()\" ng-disabled=\"!isPrevBoard()\">\n" +
15256 " <div class=\"btn btn-small btn-alt\"><i class=\"icon-primary-left\"></i>\n" +
15258 " <span class=\"offscreen-text\">Previous boards</span>\n" +
15261 " <div b2b-add-board on-add-board=\"onAddBoard()\"></div>\n" +
15262 " <div class=\"board-viewport\"><ul role=\"menu\" class=\"boardstrip-container\" ng-transclude></ul></div>\n" +
15263 " <div class=\"next-items\">\n" +
15264 " <!-- <i tabindex=\"{{isNextBoard() ? 0 : -1}}\" ng-class=\"{'disabled': !isNextBoard()}\" role=\"menuitem\" aria-label=\"See next boards\" b2b-accessibility-click=\"13,32\" ng-click=\"nextBoard()\" class=\"arrow icoControls-right\"></i> -->\n" +
15265 " <button class=\"btn-arrow arrow\" b2b-accessibility-click=\"13,32\" ng-click=\"nextBoard()\" ng-disabled=\"!isNextBoard()\">\n" +
15266 " <div class=\"btn btn-small btn-alt\"><i class=\"icon-primary-right\"></i>\n" +
15268 " <span class=\"offscreen-text\">Next boards</span>\n" +
15276 angular.module("b2bTemplate/calendar/datepicker-popup.html", []).run(["$templateCache", function($templateCache) {
15277 $templateCache.put("b2bTemplate/calendar/datepicker-popup.html",
15278 "<div id=\"datepicker\" class=\"datepicker dropdown-menu\" ng-class=\"{'datepicker-dropdown datepicker-orient-top': !inline, 'datepicker-orient-left': !inline && orientation === 'left', 'datepicker-orient-right': !inline && orientation === 'right'}\" ng-style=\"{position: inline && 'relative', 'z-index': inline && '0', top: !inline && position.top + 'px' || 0, left: !inline && position.left + 'px'}\" style=\"display: block;\" aria-hidden=\"false\" role=\"dialog\" tabindex=\"-1\" b2b-key type=\"table\" columns=\"7\">\n" +
15279 " <div class=\"datepicker-days\" style=\"display: block;\">\n" +
15280 " <div ng-repeat=\"header in headers\" class=\"text-left\" style=\"width: 100%;\" b2b-append-element=\"header\"></div>\n" +
15281 " <table class=\"table-condensed\">\n" +
15284 " <th id=\"prev\" class=\"prev\" tabindex=\"0\" b2b-accessibility-click=\"13,32\" aria-label=\"Previous Month\" role=\"button\" b2b-element-focus=\"!disablePrev && getFocus\" ng-style=\"{visibility: visibilityPrev}\" ng-click=\"!disablePrev && move(-1,$event)\"><i class=\"icon-primary-left\" aria-hidden=\"true\"></i></th>\n" +
15285 " <th id=\"month\" tabindex=\"-1\" aria-label=\"{{title}}\" class=\"datepicker-switch\" colspan=\"{{rows[0].length - 2}}\">{{title}}</th>\n" +
15286 " <th id=\"next\" class=\"next\" tabindex=\"0\" b2b-accessibility-click=\"13,32\" b2b-element-focus=\"disablePrev && getFocus\" aria-label=\"Next Month\" role=\"button\" ng-style=\"{visibility: visibilityNext}\" ng-click=\"!disableNext && move(1,$event)\"><i class=\"icon-primary-right\" aria-hidden=\"true\"></i></th>\n" +
15288 " <tr ng-show=\"labels.length > 0\">\n" +
15289 " <th id=\"{{label.post}}\" class=\"dow\" ng-repeat=\"label in labels\" aria-hidden=\"true\"><span aria-hidden=\"true\">{{label.pre}}</span></th>\n" +
15293 " <tr ng-repeat=\"row in rows\">\n" +
15294 " <td headers=\"{{dt.header}}\" b2b-key-item=\"dt.focusable\" b2b-accessibility-click=\"13\" b2b-element-focus=\"disablePrev && disableNext && getFocus && (dt.selected || dt.firstFocus || dt.fromDate || dt.dateRange)\" tabindex=\"{{(!(dt.focusable && (dt.selected || dt.firstFocus || dt.fromDate || currFocus)) && -1) || 0}}\" aria-hidden=\"{{(!dt.focusable && true) || false}}\" role=\"{{(dt.focusable && 'gridcell') || ''}}\" aria-label=\"{{(dt.focusable && dt.date | date : 'EEEE, MMMM d') || ''}}\" aria-selected=\"{{(dt.focusable && (dt.selected || dt.dateRange) && true) || false}}\" ng-repeat=\"dt in row\" class=\"day magic\" ng-class=\"{'active': dt.focusable && dt.selected && !dt.dateRange, 'start-date': dt.focusable && dt.fromDate && dt.dateRange, 'between-date': dt.focusable && !dt.fromDate && !dt.selected && dt.dateRange, 'end-date': dt.focusable && dt.selected && dt.dateRange, 'old': dt.oldMonth, 'new': dt.nextMonth, 'disabled': dt.disabled, 'due-date': dt.dueDate, 'late-fee': dt.pastDue}\" ng-focus=\"currFocus=true; trapFocus();\" ng-blur=\"currFocus=false; trapFocus();\" title=\"{{(dt.focusable && dt.pastDue && legendMessage) || ''}}\" ng-click=\"!ngDisabled && !dt.disabled && select(dt.date)\">\n" +
15295 " <div aria-hidden=\"true\" tabindex=\"-1\" class=\"show-date\" ng-if=\"!(dt.oldMonth || dt.nextMonth)\">{{dt.label}}</div>{{(dt.focusable && dt.date | date : 'EEEE, MMMM d yyyy') || ''}}{{(dt.focusable && dt.dueDate && '. Bill-due-date.. ') || ''}}{{(dt.focusable && dt.pastDue && legendMessage) || ''}}</td>\n" +
15299 " <tr ng-repeat=\"footer in footers\">\n" +
15300 " <th colspan=\"7\" class=\"text-left\" style=\"width: 278px;\" b2b-append-element=\"footer\"></th>\n" +
15308 angular.module("b2bTemplate/calendar/datepicker.html", []).run(["$templateCache", function($templateCache) {
15309 $templateCache.put("b2bTemplate/calendar/datepicker.html",
15311 " <span class=\"icon-primary-calendar span12\" ng-class=\"{'disabled': ngDisabled}\" ng-transclude></span>\n" +
15315 angular.module("b2bTemplate/coachmark/coachmark.html", []).run(["$templateCache", function($templateCache) {
15316 $templateCache.put("b2bTemplate/coachmark/coachmark.html",
15317 "<div class=\"b2b-coachmark-container\" tabindex=\"-1\" role=\"dialog\" aria-label=\"{{currentCoachmark.contentHeader}} {{currentCoachmark.content}} {{currentCoachmark.offscreenText}}\" ng-style=\"{'top':coackmarkElPos.top,'left':coackmarkElPos.left}\" aria-describeby=\"{{currentCoachmark}}\">\n" +
15318 " <i class=\"b2b-coachmark-caret\"></i>\n" +
15319 " <div class=\"b2b-coachmark-header\">\n" +
15320 " <div class=\"b2b-coachmark-countlabel\"><span ng-if=\"coachmarkIndex !== 0\">{{coachmarkIndex}} of {{(coachmarks.length-1)}}<span></div>\n" +
15321 " <div class=\"corner-button\">\n" +
15322 " <button type=\"button\" ng-focus=\"closeButtonFocus()\" class=\"close\" title=\"close\" aria-label=\"Close\" ng-click=\"closeCoachmark()\"></button>\n" +
15325 " <div class=\"b2b-coachmark-content\"> \n" +
15326 " <i class=\"icon-misc-dimmer\"></i>\n" +
15327 " <div class=\"b2b-coachmark-content-header\"><span class=\"offscreen-text\">{{currentCoachmark.offscreenText}}</span>{{currentCoachmark.contentHeader}}</div>\n" +
15328 " <div class=\"b2b-coachmark-description\">{{currentCoachmark.content}}</div>\n" +
15329 " <div class=\"b2b-coachmark-btn-group\">\n" +
15330 " <a class=\"b2b-coachmark-link\" href=\"javascript:void(0)\" ng-if=\"currentCoachmark.linkLabel !== '' && currentCoachmark.linkLabel !== undefined\" ng-click=\"actionCoachmark(currentCoachmark.linkLabel)\">{{currentCoachmark.linkLabel}}</a>\n" +
15331 " <button class=\"btn btn-alt\" ng-if=\"currentCoachmark.buttonLabel !== '' && currentCoachmark.buttonLabel !== undefined\" ng-click=\"actionCoachmark(currentCoachmark.buttonLabel)\">{{currentCoachmark.buttonLabel}}</button>\n" +
15337 angular.module("b2bTemplate/dropdowns/b2bDropdownDesktop.html", []).run(["$templateCache", function($templateCache) {
15338 $templateCache.put("b2bTemplate/dropdowns/b2bDropdownDesktop.html",
15339 "<span b2b-key prev=\"38\" next=\"40\" enable-search ng-class=\"{'large': (dropdownSize === 'large'), 'disabled': (disabled), 'selectWrap': (isInputDropdown), 'selectorModule': (dropdownType === 'menu'), 'linkSelectorModule': (dropdownType === 'link-menu')}\">\n" +
15340 " <input b2b-dropdown-toggle b2b-dropdown-validation ng-disabled=\"disabled\" type=\"text\" id=\"{{dropdownId}}\" name=\"{{dropdownName}}\" class=\"awd-select isWrapped\" ng-required=\"dropdownRequired\" ng-model=\"currentSelected.text\" role=\"combobox\" aria-owns=\"listbox{{$id}}\" aria-expanded=\"{{toggleFlag}}\" ng-click=\"toggleDropdown()\" ng-blur=\"setBlur();\" ng-class=\"{'active': toggleFlag, 'closed': !toggleFlag, 'large': (dropdownSize === 'large')}\" style=\"width:100%;\" value=\"{{currentSelected.text}}\" ng-show=\"isInputDropdown\" aria-describedby=\"{{dropdownDescribedBy}}\" readonly=\"readonly\">\n" +
15341 " <button type=\"button\" b2b-dropdown-toggle ng-disabled=\"disabled\" class=\"selectModule\" aria-label=\"{{labelText}} {{currentSelected.label}}\" aria-expanded=\"{{toggleFlag}}\" aria-haspopup=\"true\" ng-click=\"toggleDropdown()\" ng-blur=\"setBlur()\" ng-class=\"{'active opened': toggleFlag, 'closed': !toggleFlag, 'large': (dropdownSize === 'large')}\" ng-bind-html=\"currentSelected.text\" ng-show=\"!isInputDropdown\"></button>\n" +
15342 " <div ng-class=\"{'selectWrapper': (isInputDropdown), 'moduleWrapper': (!isInputDropdown)}\">\n" +
15343 " <ul id=\"listbox{{$id}}\" role=\"{{isInputDropdown?'listbox':'menu'}}\" ng-class=\"{'awd-select-list': (isInputDropdown), 'awd-module-list': (!isInputDropdown)}\" tabindex=\"-1\" ng-show=\"toggleFlag\" aria-label=\"Choose options\"></ul>\n" +
15344 " <ul class=\"module-optinalcta\" ng-if=\"toggleFlag && optionalCta\" tabindex=\"-1\" aria-hidden=\"false\">\n" +
15345 " <li class=\"module-list-item\" tabindex=\"-1\" role=\"menuitem\" value=\"\" aria-label=\"Optinal CTA\" aria-selected=\"true\">\n" +
15346 " <span class=\"module-data\" b2b-append-element=\"optionalCta\"></span>\n" +
15350 "<i class=\"icon-primary-down\" aria-hidden=\"true\"></i>\n" +
15354 angular.module("b2bTemplate/dropdowns/b2bDropdownGroupDesktop.html", []).run(["$templateCache", function($templateCache) {
15355 $templateCache.put("b2bTemplate/dropdowns/b2bDropdownGroupDesktop.html",
15356 "<li b2b-dropdown-group-desktop class=\"optgroup-wrapper\">{{groupHeader}}\n" +
15357 " <ul class=\"optgroup\" role=\"group\" aria-label=\"{{groupHeader}}\"></ul>\n" +
15361 angular.module("b2bTemplate/dropdowns/b2bDropdownListDesktop.html", []).run(["$templateCache", function($templateCache) {
15362 $templateCache.put("b2bTemplate/dropdowns/b2bDropdownListDesktop.html",
15363 "<li b2b-dropdown-list-desktop b2b-key-item b2b-accessibility-click=\"13\" aria-selected=\"{{currentSelected.value === dropdownListValue}}\" data-hover=\"{{dropdown.highlightedValue === dropdownListValue}}\" ng-class=\"{'awd-select-list-item': (isInputDropdown), 'module-list-item': (!isInputDropdown)}\" tabindex=\"0\" role=\"{{isInputDropdown?'option':'menuitem'}}\" ng-click=\"selectDropdownItem()\" ng-focus=\"highlightDropdown()\"></li>");
15366 angular.module("b2bTemplate/fileUpload/fileUpload.html", []).run(["$templateCache", function($templateCache) {
15367 $templateCache.put("b2bTemplate/fileUpload/fileUpload.html",
15368 "<label class=\"b2b-file-container\">\n" +
15369 " <span class=\"b2b-upload-link\" ng-transclude></span>\n" +
15370 " <input type=\"file\" b2b-file-change>\n" +
15374 angular.module("b2bTemplate/flyout/flyout.html", []).run(["$templateCache", function($templateCache) {
15375 $templateCache.put("b2bTemplate/flyout/flyout.html",
15376 "<span class=\"b2b-flyout\" b2b-flyout-trap-focus-inside>\n" +
15377 " <span ng-transclude></span>\n" +
15381 angular.module("b2bTemplate/flyout/flyoutContent.html", []).run(["$templateCache", function($templateCache) {
15382 $templateCache.put("b2bTemplate/flyout/flyoutContent.html",
15383 "<div class=\"b2b-flyout-container\" aria-live=\"polite\" aria-atomic=\"false\" ng-class=\"{'b2b-flyout-left':horizontalPlacement==='left',\n" +
15384 " 'b2b-flyout-center':horizontalPlacement==='center', \n" +
15385 " 'b2b-flyout-right':horizontalPlacement==='right',\n" +
15386 " 'b2b-flyout-centerLeft':horizontalPlacement==='centerLeft',\n" +
15387 " 'b2b-flyout-centerRight':horizontalPlacement==='centerRight', \n" +
15388 " 'b2b-flyout-above':verticalPlacement==='above', \n" +
15389 " 'b2b-flyout-below':verticalPlacement==='below',\n" +
15390 " 'open-flyout': openFlyout,\n" +
15391 " 'b2b-close-flyout': !openFlyout}\">\n" +
15392 " <i class=\"b2b-flyout-caret\" ng-class=\"{'open-flyout': openFlyout, \n" +
15393 " 'b2b-flyout-caret-above':verticalPlacement==='above',\n" +
15394 " 'b2b-flyout-caret-below':verticalPlacement==='below'}\"></i>\n" +
15395 " <span ng-transclude></span>\n" +
15399 angular.module("b2bTemplate/footer/footer_column_switch_tpl.html", []).run(["$templateCache", function($templateCache) {
15400 $templateCache.put("b2bTemplate/footer/footer_column_switch_tpl.html",
15401 "<div class=\"footer-columns \" ng-class=\"{'five-column':footerColumns===5, 'four-column':footerColumns===4, 'three-column':footerColumns===3}\" ng-repeat=\"item in footerLinkItems\">\n" +
15402 " <h3 class=\"b2b-footer-header\">{{item.categoryName}}</h3>\n" +
15404 " <li ng-repeat=\"i in item.values\">\n" +
15405 " <a href=\"{{i.href}}\" title=\"{{item.categoryName}} - {{i.displayLink}}\">{{i.displayLink}}</a> \n" +
15411 "<div ng-transclude></div>\n" +
15415 angular.module("b2bTemplate/horizontalTable/horizontalTable.html", []).run(["$templateCache", function($templateCache) {
15416 $templateCache.put("b2bTemplate/horizontalTable/horizontalTable.html",
15417 "<div class=\"b2b-horizontal-table\">\n" +
15418 " <div class=\"b2b-horizontal-table-arrows row span12\">\n" +
15419 " <div class=\"span4 b2b-prev-link\">\n" +
15420 " <a href=\"javascript:void(0)\" ng-click=\"moveViewportLeft()\" ddh-accessibility-click=\"13,32\" ng-if=\"!disableLeft\">Previous</a>\n" +
15421 " <span ng-if=\"disableLeft\" class=\"b2b-disabled-text\">Previous</span>\n" +
15424 " <span class=\"span5 b2b-horizontal-table-column-info\" aria-live=\"polite\" tabindex=\"-1\">\n" +
15425 " Showing {{countDisplayText}} {{getColumnSet()[0]+1}}-{{getColumnSet()[1]+1}} of {{numOfCols}} columns\n" +
15428 " <div ng-if=\"legendContent\" class=\"span2 b2b-horizontal-table-legend\">\n" +
15429 " | <b2b-flyout>\n" +
15430 " <div tabindex=\"0\" role=\"button\" aria-haspopup=\"true\" b2b-flyout-toggler b2b-accessibility-click=\"13,32\" aria-expanded=\"{{flyoutOpened ? 'true' : 'false'}}\">\n" +
15432 " <i class=\"icoControls-down\" role=\"img\"></i>\n" +
15434 " <b2b-flyout-content horizontal-placement=\"center\" vertical-placement=\"below\">\n" +
15435 " <div ng-bind-html=\"legendContent\"></div>\n" +
15436 " </b2b-flyout-content>\n" +
15437 " </b2b-flyout>\n" +
15440 " <div class=\"span3 text-right b2b-next-link\">\n" +
15441 " <a href=\"javascript:void(0)\" ng-click=\"moveViewportRight()\" ddh-accessibility-click=\"13,32\" ng-if=\"!disableRight\">Next</a>\n" +
15442 " <span ng-if=\"disableRight\" class=\"b2b-disabled-text\">Next</span>\n" +
15445 " <div class=\"b2b-horizontal-table-inner-container\">\n" +
15446 " <span ng-transclude></span>\n" +
15451 angular.module("b2bTemplate/hourPicker/b2bHourpicker.html", []).run(["$templateCache", function($templateCache) {
15452 $templateCache.put("b2bTemplate/hourPicker/b2bHourpicker.html",
15453 "<div class=\"hp-container\">\n" +
15454 " <div class=\"hp-selected\">\n" +
15455 " <div b2b-hourpicker-value=\"$index\" days=\"value.days\" start-time=\"value.startTime\" start-meridiem=\"value.startMeridiem\" end-time=\"value.endTime\" end-meridiem=\"value.endMeridiem\" ng-repeat=\"value in finalHourpickerValues\"></div>\n" +
15457 " <div b2b-hourpicker-panel></div>\n" +
15461 angular.module("b2bTemplate/hourPicker/b2bHourpickerPanel.html", []).run(["$templateCache", function($templateCache) {
15462 $templateCache.put("b2bTemplate/hourPicker/b2bHourpickerPanel.html",
15463 "<form name=\"{{'hourpickerForm' + $id}}\">\n" +
15464 " <div class=\"hp-checkbox\" role=\"group\">\n" +
15465 " <label class=\"checkbox\" for=\"checkbox_{{dayOption.title}}_{{$id}}\" ng-repeat=\"dayOption in hourpicker.dayOptions\">\n" +
15466 " <input type=\"checkbox\" id=\"checkbox_{{dayOption.title}}_{{$id}}\" name=\"{{'hourpickerDays' + $id}}\" ng-model=\"hourpickerPanelValue.days[$index].value\" ng-disabled=\"dayOption.disabled\" /><i class=\"skin\"></i><span>{{dayOption.label}}</span>\n" +
15469 " <div class=\"row hp-dropdowns\">\n" +
15470 " <div class=\"span4\">\n" +
15471 " <label for=\"{{'hourpickerStartTime' + $id}}\">From</label>\n" +
15472 " <select id=\"{{'hourpickerStartTime' + $parent.$id}}\" b2b-dropdown type=\"\" ng-model=\"hourpickerPanelValue.startTime\">\n" +
15473 " <option b2b-dropdown-list value=\"\">From</option>\n" +
15474 " <option b2b-dropdown-list option-repeat=\"startTimeOption in hourpicker.startTimeOptions\" value=\"{{startTimeOption}}\">{{startTimeOption}}</option>\n" +
15477 " <div class=\"span8 radio-buttons\" role=\"radiogroup\" aria-label=\"Select AM or PM\">\n" +
15478 " <label class=\"radio\" for=\"hourpickerStartMeridiem_AM_{{$id}}\">\n" +
15479 " <input type=\"radio\" id=\"hourpickerStartMeridiem_AM_{{$id}}\" name=\"{{'hourpickerStartMeridiem' + $id}}\" value=\"am\" ng-model=\"hourpickerPanelValue.startMeridiem\" /><i class=\"skin\"></i><span>AM</span>\n" +
15481 " <label class=\"radio\" for=\"hourpickerStartMeridiem_PM_{{$id}}\">\n" +
15482 " <input type=\"radio\" id=\"hourpickerStartMeridiem_PM_{{$id}}\" name=\"{{'hourpickerStartMeridiem' + $id}}\" value=\"pm\" ng-model=\"hourpickerPanelValue.startMeridiem\" /><i class=\"skin\"></i><span>PM</span>\n" +
15486 " <div class=\"row hp-dropdowns\">\n" +
15487 " <div class=\"span4\">\n" +
15488 " <label for=\"{{'hourpickerEndTime' + $id}}\">To</label>\n" +
15489 " <select id=\"{{'hourpickerEndTime' + $parent.$id}}\" b2b-dropdown ng-model=\"hourpickerPanelValue.endTime\">\n" +
15490 " <option b2b-dropdown-list value=\"\">To</option>\n" +
15491 " <option b2b-dropdown-list option-repeat=\"endTimeOption in hourpicker.endTimeOptions\" value=\"{{endTimeOption}}\">{{endTimeOption}}</option>\n" +
15494 " <div class=\"span8 radio-buttons\" role=\"radiogroup\" aria-label=\"Select AM or PM\">\n" +
15495 " <label class=\"radio\" for=\"hourpickerEndMeridiem_AM_{{$id}}\">\n" +
15496 " <input type=\"radio\" id=\"hourpickerEndMeridiem_AM_{{$id}}\" name=\"{{'hourpickerEndMeridiem' + $id}}\" value=\"am\" ng-model=\"hourpickerPanelValue.endMeridiem\" /><i class=\"skin\"></i><span>AM</span>\n" +
15498 " <label class=\"radio\" for=\"hourpickerEndMeridiem_PM_{{$id}}\">\n" +
15499 " <input type=\"radio\" id=\"hourpickerEndMeridiem_PM_{{$id}}\" name=\"{{'hourpickerEndMeridiem' + $id}}\" value=\"pm\" ng-model=\"hourpickerPanelValue.endMeridiem\" /><i class=\"skin\"></i><span>PM</span>\n" +
15503 " <div class=\"row hp-buttons\">\n" +
15504 " <div class=\"span12\">\n" +
15505 " <div style=\"float:right\">\n" +
15506 " <button class=\"btn btn-secondary btn-small\" ng-click=\"resetHourpickerPanelValue()\">Clear</button>\n" +
15507 " <button class=\"btn btn-alt btn-small\" ng-disabled = \"disableAddBtn\" ng-click=\"updateHourpickerValue()\">{{hourpicker.editMode > -1 ? 'Update' : 'Add'}}</button>\n" +
15514 angular.module("b2bTemplate/hourPicker/b2bHourpickerValue.html", []).run(["$templateCache", function($templateCache) {
15515 $templateCache.put("b2bTemplate/hourPicker/b2bHourpickerValue.html",
15516 "<div class=\"selected-days\">\n" +
15517 " <span class=\"day\">{{hourpickerValue.days}} {{hourpickerValue.startTime}} {{hourpickerValue.startMeridiem}} - {{hourpickerValue.endTime}} {{hourpickerValue.endMeridiem}}</span>\n" +
15518 " <span style=\"float:right\">\n" +
15519 " <i class=\"icon-misc-pen\" role=\"button\" aria-label=\"Edit {{hourpickerValue.days}} {{hourpickerValue.startTime}} {{hourpickerValue.startMeridiem}} - {{hourpickerValue.endTime}} {{hourpickerValue.endMeridiem}}\" title=\"Edit\" tabindex=\"0\" b2b-accessibility-click=\"13,32\" ng-click=\"editHourpickerValue(hourpickerValue.index)\"></i>\n" +
15520 " <i class=\"icon-misc-trash\" role=\"button\" aria-label=\"Delete {{hourpickerValue.days}} {{hourpickerValue.startTime}} {{hourpickerValue.startMeridiem}} - {{hourpickerValue.endTime}} {{hourpickerValue.endMeridiem}}\" title=\"Delete\" tabindex=\"0\" b2b-accessibility-click=\"13,32\" ng-click=\"deleteHourpickerValue(hourpickerValue.index)\"></i>\n" +
15522 " <div style=\"clear:both\"></div>\n" +
15526 angular.module("b2bTemplate/leftNavigation/leftNavigation.html", []).run(["$templateCache", function($templateCache) {
15527 $templateCache.put("b2bTemplate/leftNavigation/leftNavigation.html",
15528 "<div class=\"b2b-nav-menu\">\n" +
15529 " <div class=\"b2b-subnav-container\">\n" +
15530 " <ul class=\"b2b-subnav-content\">\n" +
15531 " <li ng-repeat=\"menu in menuData\" ng-click=\"toggleNav($index)\"><a ng-class=\"{'expand': idx==$index}\" aria-label=\"{{menu.name}}\" title=\" \" aria-expanded=\"{{(idx==$index)?true:false;}}\" href=\"javascript:void(0);\">{{menu.name}}<i class=\"b2b-icon-primary-plus-minus\" ng-class=\"idx==$index ? 'icon-primary-expanded' : 'icon-primary-collapsed'\"></i></a>\n" +
15532 " <ul ng-class=\"{expand: idx==$index}\">\n" +
15533 " <li ng-repeat=\"menuItem in menu.menuItems\" ng-click=\"liveLink($event, $index, $parent.$index)\"><a aria-hidden=\"{{!(idx==$parent.$index)}}\" aria-label=\"{{menuItem.name}}\" title=\" \" href=\"{{menuItem.href}}\" tabindex=\"{{(idx==$parent.$index)?0:-1;}}\" ng-class=\"{active: itemIdx==$index && navIdx==$parent.$index}\">{{menuItem.name}}</a></li>\n" +
15541 angular.module("b2bTemplate/listbox/listbox.html", []).run(["$templateCache", function($templateCache) {
15542 $templateCache.put("b2bTemplate/listbox/listbox.html",
15543 "<div class=\"b2b-list-box\" tabindex=\"0\" role=\"listbox\" ng-transclude>\n" +
15547 angular.module("b2bTemplate/modalsAndAlerts/b2b-backdrop.html", []).run(["$templateCache", function($templateCache) {
15548 $templateCache.put("b2bTemplate/modalsAndAlerts/b2b-backdrop.html",
15549 "<div class=\"b2b-modal-backdrop fade in hidden-by-modal\" aria-hidden=\"true\" tabindex=\"-1\"></div>");
15552 angular.module("b2bTemplate/modalsAndAlerts/b2b-window.html", []).run(["$templateCache", function($templateCache) {
15553 $templateCache.put("b2bTemplate/modalsAndAlerts/b2b-window.html",
15554 "<div class=\"modalwrapper active {{windowClass}}\" ng-class=\"{'modal-landscape': isModalLandscape}\" role=\"{{isNotifDialog?'alertdialog':'dialog'}}\" tabindex=\"-1\" aria-labelledby=\"{{title}}\" aria-describedby=\"{{content}}\">\n" +
15555 " <div class=\"modal fade in {{sizeClass}}\" ng-transclude></div>\n" +
15559 angular.module("b2bTemplate/monthSelector/monthSelector-popup.html", []).run(["$templateCache", function($templateCache) {
15560 $templateCache.put("b2bTemplate/monthSelector/monthSelector-popup.html",
15561 "<div id=\"monthselector{{$id}}\" class=\"datepicker dropdown-menu monthselector\" \n" +
15562 " ng-class=\"{'datepicker-dropdown datepicker-orient-top': !inline, 'datepicker-orient-left': !inline && orientation === 'left', 'datepicker-orient-right': !inline && orientation === 'right'}\" \n" +
15563 " ng-style=\"{position: inline && 'relative', 'z-index': inline && '0', top: !inline && position.top + 'px' || 0, left: !inline && position.left + 'px'}\" \n" +
15564 " style=\"display: block;\" aria-hidden=\"false\" role=\"dialog presentation\" tabindex=\"-1\">\n" +
15565 " <div class=\"datepicker-days\" style=\"display: block;\">\n" +
15566 " <span class=\"offscreen-text\" aria-live=\"polite\" aria-atomic=\"true\">{{title}} displaying</span>\n" +
15567 " <table class=\"table-condensed\" role=\"grid\">\n" +
15569 " <tr ng-repeat=\"header in headers\">\n" +
15570 " <th colspan=\"7\" class=\"text-left\" style=\"width: 278px;\" b2b-append-element=\"header\"></th>\n" +
15573 " <th id=\"prev{{$id}}\" role=\"button\" tabindex=\"0\" b2b-accessibility-click=\"13\" aria-label=\"Previous Year\" ng-class=\"{'prev': !disablePrev}\" ng-style=\"{visibility: visibilityPrev}\" ng-click=\"!disablePrev && move(-1,$event)\"><i class=\"icon-primary-left\"></i></th>\n" +
15574 " <th id=\"year{{$id}}\" role=\"heading\" tabindex=\"-1\" aria-label=\"{{title}}\" class=\"datepicker-switch b2b-monthSelector-label\" colspan=\"{{rows[0].length - 2}}\">{{title}}</th>\n" +
15575 " <th id=\"next{{$id}}\" role=\"button\" tabindex=\"0\" b2b-accessibility-click=\"13\" aria-label=\"Next Year\" ng-class=\"{'next': !disableNext}\" ng-style=\"{visibility: visibilityNext}\" ng-click=\"!disableNext && move(1,$event)\"><i class=\"icon-primary-right\"></i></th>\n" +
15577 " <tr ng-show=\"labels.length > 0\">\n" +
15578 " <th id=\"{{label.post}}\" class=\"dow\" ng-repeat=\"label in labels\" aria-hidden=\"true\"><span aria-hidden=\"true\">{{label.pre}}</span></th>\n" +
15581 " <tbody b2b-key type=\"table\" columns=\"4\" >\n" +
15582 " <tr ng-repeat=\"row in rows\">\n" +
15583 " <td headers=\"{{dt.header}}\" b2b-key-item=\"dt.focusable\" b2b-accessibility-click=\"13,32\" tabindex=\"{{(!(dt.focusable && (dt.selected || dt.firstFocus || currFocus)) && -1) || 0}}\" aria-hidden=\"{{(!dt.focusable && true) || false}}\" role=\"{{(dt.focusable && 'gridcell') || ''}}\" aria-label=\"{{(dt.focusable && dt.date | date : 'MMMM, y') || ''}}\" aria-selected=\"{{(dt.focusable && (dt.selected || dt.dateRange) && true) || false}}\" ng-repeat=\"dt in row\" class=\"day magic\" ng-class=\"{'active': dt.focusable && dt.selected && !dt.dateRange, 'start-date': dt.focusable && dt.fromDate && dt.dateRange, 'between-date': dt.focusable && !dt.fromDate && !dt.selected && dt.dateRange, 'end-date': dt.focusable && dt.selected && dt.dateRange, 'old': dt.oldMonth, 'new': dt.nextMonth, 'disabled': dt.disabled, 'due-date': dt.dueDate, 'late-fee': dt.pastDue}\" ng-focus=\"currFocus=true\" ng-blur=\"currFocus=false\" title=\"{{(dt.focusable && dt.pastDue && legendMessage) || ''}}\" b2b-element-focus=\"inline && dt.focusable && dt.selected && !dt.dateRange\" ng-click=\"!ngDisabled && !dt.disabled && select(dt.date)\">\n" +
15584 " <div aria-hidden=\"true\" tabindex=\"-1\" class=\"show-date\" ng-if=\"!(dt.oldMonth || dt.nextMonth)\">{{dt.label | limitTo: 3}}</div>\n" +
15589 " <tr ng-repeat=\"footer in footers\">\n" +
15590 " <th colspan=\"7\" class=\"text-left\" b2b-append-element=\"footer\"></th>\n" +
15598 angular.module("b2bTemplate/monthSelector/monthSelector.html", []).run(["$templateCache", function($templateCache) {
15599 $templateCache.put("b2bTemplate/monthSelector/monthSelector.html",
15601 " <span class=\"icon-primary-calendar span12\" ng-class=\"{'disabled': ngDisabled}\" ng-transclude></span>\n" +
15605 angular.module("b2bTemplate/monthSelector/monthSelectorLink.html", []).run(["$templateCache", function($templateCache) {
15606 $templateCache.put("b2bTemplate/monthSelector/monthSelectorLink.html",
15608 " <span class=\"span12\" ng-transclude></span>\n" +
15612 angular.module("b2bTemplate/pagination/b2b-pagination.html", []).run(["$templateCache", function($templateCache) {
15613 $templateCache.put("b2bTemplate/pagination/b2b-pagination.html",
15614 "<div class=\"b2b-pager\">\n" +
15615 " <div ng-if=\"notMobile && totalPages > 1\">\n" +
15616 " <a tabindex=\"{{currentPage <= 1 ? -1 : 0 }}\" class=\"b2b-pager__item--prev\" b2b-accessibility-click=\"13,32\" title=\"Previous Page\" ng-click=\"prev($event)\" ng-if=\"totalPages > 10\" ng-class=\"currentPage <= 1 ? 'b2b-pager__item--prev-disabled': '' \">\n" +
15617 " <i class=\"icon-primary-left\"></i>\n" +
15619 " <a droppable=\"{{droppableAttribute}}\" tabindex=\"{{currentPage === 1 ? -1 : 0}}\" ng-class=\"{'b2b-pager__item--noclick': currentPage === 1}\" class=\"b2b-pager__item b2b-pager__item--link\" title=\"Page 1{{checkSelectedPage(page) ? ' selected' : '' }}\" ng-if=\"totalPages > 10 && currentPage > 6\" b2b-accessibility-click=\"13,32\" ng-click=\"selectPage(1, $event)\" aria-atomic=\"true\" aria-live=\"polite\">\n" +
15620 " 1<span class=\"offscreen-text\" ng-if=\"currentPage === 1\"> is selected</span>\n" +
15623 " <span class=\"b2b-pager__item\" ng-if=\"totalPages > 10 && currentPage > 6\">...</span>\n" +
15625 " <a droppable=\"{{droppableAttribute}}\" tabindex=\"{{checkSelectedPage(page) ? -1 : 0}}\" href=\"javascript:void(0);\" class=\"b2b-pager__item b2b-pager__item--link\" title=\"Page {{page}}\" b2b-element-focus=\"isFocused(page)\" ng-repeat=\"page in pages\" ng-class=\"{'b2b-pager__item--active': checkSelectedPage(page), 'b2b-pager__item--noclick': checkSelectedPage(page),'b2b-pager__item--droppable': droppableAttribute == true}\" b2b-accessibility-click=\"13,32\" ng-click=\"!checkSelectedPage(page) && selectPage(page, $event)\" aria-atomic=\"true\" aria-live=\"polite\">\n" +
15626 " {{page}}<span class=\"offscreen-text\" ng-if=\"checkSelectedPage(page)\"> is selected</span>\n" +
15629 " <span class=\"b2b-pager__item\" ng-if=\"totalPages > 10 && currentPage <= (totalPages - boundary)\">...</span>\n" +
15631 " <a droppable=\"{{droppableAttribute}}\" tabindex=\"{{currentPage === totalPages ? -1 : 0}}\" href=\"javascript:void(0);\" ng-class=\"{'b2b-pager__item--noclick': currentPage === totalPages}\" class=\"b2b-pager__item b2b-pager__item--link\" title=\"Page {{totalPages}}\" ng-if=\"totalPages > 10 && currentPage <= (totalPages - boundary)\" b2b-accessibility-click=\"13,32\" ng-click=\"selectPage(totalPages, $event)\" aria-atomic=\"true\" aria-live=\"polite\">\n" +
15632 " {{totalPages}}<span class=\"offscreen-text\" ng-if=\"checkSelectedPage(page)\"> is selected</span>\n" +
15636 " <a tabindex=\"{{currentPage >= totalPages ? -1 : 0 }}\" href=\"javascript:void(0);\" class=\"b2b-pager__item--next\" b2b-accessibility-click=\"13,32\" title=\"Next Page\" ng-click=\"next($event)\" ng-if=\"totalPages > 10\" ng-class=\"currentPage >= totalPages ? 'b2b-pager__item--next-disabled' :'' \">\n" +
15637 " <i class=\"icon-primary-right\"></i>\n" +
15640 " <div class=\"fieldLabel b2b-go-to-page\" ng-class=\"{'b2b-go-to-page-inline' : inputClass !== undefined }\" ng-show=\"totalPages > 20\"> \n" +
15641 " <label for=\"{{inputId}}\">Go to Page:</label>\n" +
15642 " <input id=\"{{inputId}}\" class=\"b2b-pager__item--input\" type=\"number\" ng-blur=\"onfocusOut($event)\" ng-focus=\"onfocusIn($event)\" ng-keypress=\"gotoKeyClick($event)\" b2b-only-nums=\"4\" ng-model=\"$parent.gotoPage\" />\n" +
15643 " <button class=\"btn-arrow\" ng-click=\"gotoBtnClick()\" ng-disabled=\"$parent.gotoPage !== 0 || $parent.gotoPage !== undefined\" aria-label=\"Go to\">\n" +
15644 " <div class=\"btn btn-small btn-secondary\">\n" +
15645 " <i class=\"icon-primary-right\"></i>\n" +
15650 " <div ng-if=\"isMobile && totalPages > 1\" ng-swipe-right=\"prev()\" ng-swipe-left=\"next()\" ng-class=\"isMobile ? 'b2b-mobile-view': '' \">\n" +
15651 " <a droppable=\"{{droppableAttribute}}\" tabindex=\"{{checkSelectedPage(page) ? -1 : 0}}\" href=\"javascript:void(0);\" class=\"b2b-pager__item b2b-pager__item--link\" title=\"Page {{page}}\" b2b-element-focus=\"isFocused(page)\" ng-repeat=\"page in pages\" ng-class=\"{'b2b-pager__item--active': checkSelectedPage(page), fade1: ($index == 0 && currentPage > meanVal+1), fade2: ($index == 1 && currentPage > meanVal+1), fadesl: ($index == pages.length-2 && currentPage < totalPages - meanVal), fadel: ($last && currentPage < totalPages - meanVal), 'b2b-pager__item--noclick': checkSelectedPage(page)}\" b2b-accessibility-click=\"13,32\" ng-click=\"selectPage(page, $event)\">{{page}}</a>\n" +
15657 angular.module("b2bTemplate/paneSelector/paneSelector.html", []).run(["$templateCache", function($templateCache) {
15658 $templateCache.put("b2bTemplate/paneSelector/paneSelector.html",
15659 "<div class=\"panes\" ng-transclude></div>");
15662 angular.module("b2bTemplate/paneSelector/paneSelectorPane.html", []).run(["$templateCache", function($templateCache) {
15663 $templateCache.put("b2bTemplate/paneSelector/paneSelectorPane.html",
15664 "<div class=\"pane-block\" ng-transclude></div>");
15667 angular.module("b2bTemplate/profileCard/profileCard-addUser.html", []).run(["$templateCache", function($templateCache) {
15668 $templateCache.put("b2bTemplate/profileCard/profileCard-addUser.html",
15669 "<div class=\"span3 b2b-profile-card b2b-add-user\">\n" +
15670 " <div class=\"atcenter\">\n" +
15671 " <div class=\"circle\"><i class=\"icon-primary-add-maximize\"></i></div>\n" +
15672 " <div>Create new user</div>\n" +
15677 angular.module("b2bTemplate/profileCard/profileCard.html", []).run(["$templateCache", function($templateCache) {
15678 $templateCache.put("b2bTemplate/profileCard/profileCard.html",
15679 "<div class=\"span3 b2b-profile-card\">\n" +
15680 " <div class=\"top-block\">\n" +
15681 " <div class=\"profile-image\">\n" +
15682 " <img ng-if=\"image\" profile-name=\"{{profile.name}}\" ng-src=\"{{profile.img}}\" alt=\"{{profile.name}}\">\n" +
15683 " <span ng-if=\"!image\" class=\"default-img\">{{initials}}</span>\n" +
15685 " <h4 class=\"name\">{{profile.name}}</h4>\n" +
15687 " <p class=\"status\">\n" +
15688 " <span class=\"status-icon\" ng-class=\"{'status-green':colorIcon==='green','status-red':colorIcon==='red','status-blue':colorIcon==='blue','status-yellow':colorIcon==='yellow'}\"> \n" +
15690 " <span>{{profile.state}}<span ng-if=\"badge\" class=\"status-badge\">Admin</span></span>\n" +
15694 " <div class=\"bottom-block\">\n" +
15695 " <div class=\"profile-details\">\n" +
15696 " <label>Username</label>\n" +
15697 " <div ng-if=\"shouldClip(profile.userName)\" ng-mouseover=\"showUserNameTooltip = true;\" ng-mouseleave=\"showUserNameTooltip = false;\">\n" +
15698 " <div ng-if=\"shouldClip(profile.userName)\" class=\"tooltip\" b2b-tooltip show-tooltip=\"showUserNameTooltip\">\n" +
15699 " {{profile.userName.slice(0, 25)+'...'}}\n" +
15700 " <div class=\"arrow\"></div>\n" +
15701 " <div class=\"tooltip-wrapper\" role=\"tooltip\" aria-live=\"polite\" aria-atomic=\"false\" style=\"z-index:1111\">\n" +
15702 " <div class=\"tooltip-size-control\">\n" +
15703 " <div class=\"helpertext\" tabindex=\"-1\" role=\"tooltip\">\n" +
15704 " {{profile.userName}}\n" +
15710 " <div ng-if=\"!shouldClip(profile.userName)\">\n" +
15711 " {{profile.userName}}\n" +
15713 " <label>Email</label>\n" +
15714 " <div ng-if=\"shouldClip(profile.email)\" ng-mouseover=\"showEmailTooltip = true;\" ng-mouseleave=\"showEmailTooltip = false;\">\n" +
15715 " <div ng-if=\"shouldClip(profile.email)\" class=\"tooltip\" data-placement=\"bottom\" b2b-tooltip show-tooltip=\"showEmailTooltip\">\n" +
15716 " {{profile.email.slice(0, 25)+'...'}}\n" +
15717 " <div class=\"arrow\"></div>\n" +
15718 " <div class=\"tooltip-wrapper\" role=\"tooltip\" aria-live=\"polite\" aria-atomic=\"false\" style=\"z-index:1111\">\n" +
15719 " <div class=\"tooltip-size-control\">\n" +
15720 " <div class=\"helpertext\" tabindex=\"-1\" role=\"tooltip\">\n" +
15721 " {{profile.email}}\n" +
15727 " <div ng-if=\"!shouldClip(profile.email)\">\n" +
15728 " {{profile.email}}\n" +
15730 " <label>Role</label>\n" +
15731 " <div ng-if=\"shouldClip(profile.role)\" ng-mouseover=\"showRoleTooltip = true;\" ng-mouseleave=\"showRoleTooltip = false;\">\n" +
15732 " <div ng-if=\"shouldClip(profile.role)\" class=\"tooltip\" b2b-tooltip show-tooltip=\"showRoleTooltip\">\n" +
15733 " {{profile.role.slice(0, 25)+'...'}}\n" +
15734 " <div class=\"tooltip-wrapper\" role=\"tooltip\" aria-live=\"polite\" aria-atomic=\"false\" style=\"z-index:1111\">\n" +
15735 " <div class=\"tooltip-size-control\">\n" +
15736 " <div class=\"helpertext\" tabindex=\"-1\" role=\"tooltip\">\n" +
15737 " {{profile.role}}\n" +
15743 " <div ng-if=\"!shouldClip(profile.role)\">\n" +
15744 " {{profile.role}}\n" +
15746 " <label>Last login</label>\n" +
15747 " <div ng-if=\"shouldClip(profile.lastLogin)\" ng-mouseover=\"showLastLoginTooltip = true;\" ng-mouseleave=\"showLastLoginTooltip = false;\">\n" +
15748 " <div ng-if=\"shouldClip(profile.lastLogin)\" class=\"tooltip\" b2b-tooltip show-tooltip=\"showLastLoginTooltip\">\n" +
15749 " {{profile.lastLogin.slice(0, 25)+'...'}}\n" +
15750 " <div class=\"tooltip-wrapper\" role=\"tooltip\" aria-live=\"polite\" aria-atomic=\"false\" style=\"z-index:1111\">\n" +
15751 " <div class=\"tooltip-size-control\">\n" +
15752 " <div class=\"helpertext\" tabindex=\"-1\" role=\"tooltip\">\n" +
15753 " {{profile.lastLogin}}\n" +
15759 " <div ng-if=\"!shouldClip(profile.lastLogin)\">\n" +
15760 " {{profile.lastLogin}}\n" +
15767 angular.module("b2bTemplate/searchField/searchField.html", []).run(["$templateCache", function($templateCache) {
15768 $templateCache.put("b2bTemplate/searchField/searchField.html",
15769 "<div class=\"search-bar\">\n" +
15770 " <div class='input-container' ng-blur=\"blurInput()\">\n" +
15771 " <input type=\"text\" class=\"innershadow b2b-search-input-field\" id=\"{{configObj.labelId}}\" b2b-search-input ng-model=\"inputModel\" ng-disabled=\"disabled\" b2b-reset ng-keydown=\"selectionIndex($event)\" placeholder=\"{{configObj.ghostText}}\" style=\"width:100%\" maxlength=\"{{configObj.maxLength}}\" title=\"{{inputModel}}\" aria-label=\"{{inputModel.length>0?inputModel:configObj.ghostText}}\" />\n" +
15772 " <button class=\"btn-search\" ng-disabled=\"disabled\" ng-click=\"startSearch()\" b2b-accessibility-click=\"13,32\" aria-label=\"Search\" ng-focus=\"showListFlag = false\" type=\"button\"><i class=\"icoControls-magnifyingglass\"></i></button>\n" +
15774 " <div class=\"search-suggestion-wrapper\" ng-if=\"filterList.length >=0\" ng-show=\"showListFlag\">\n" +
15775 " <ul class=\"search-suggestion-list\" role=\"listbox\"> \n" +
15776 " <li class=\"no-result\" ng-if=\"filterList.length == 0 && configObj.display\">{{configObj.resultText}}</li>\n" +
15777 " <li class=\"search-suggestion-item\" ng-if=\"filterList.length\" ng-repeat=\"item in filterList | limitTo:configObj.noOfItemsDisplay track by $index\" ng-bind-html=\"item.title | b2bMultiSepartorHighlight:inputModel:configObj.searchSeperator\" ng-class=\"{active:isActive($index,filterList.length)}\" ng-click=\"selectItem($index,item.title)\">\n" +
15778 " {{item.title}} \n" +
15785 angular.module("b2bTemplate/seekBar/seekBar.html", []).run(["$templateCache", function($templateCache) {
15786 $templateCache.put("b2bTemplate/seekBar/seekBar.html",
15787 "<div class=\"b2b-seek-bar-container\" ng-class=\"{vertical:verticalSeekBar}\">\n" +
15788 " <div class=\"b2b-seek-bar-track-container\">\n" +
15789 " <div class=\"b2b-seek-bar-track\"></div>\n" +
15790 " <div class=\"b2b-seek-bar-track-fill\"></div>\n" +
15792 " <div class=\"b2b-seek-bar-knob-container\" role=\"menu\" >\n" +
15793 " <div class=\"b2b-seek-bar-knob\" tabindex=\"0\" role=\"menuitem\"></div>\n" +
15798 angular.module("b2bTemplate/slider/slider.html", []).run(["$templateCache", function($templateCache) {
15799 $templateCache.put("b2bTemplate/slider/slider.html",
15800 "<div class=\"b2b-slider-container\" ng-class=\"{'vertical':verticalSlider}\">\n" +
15801 " <div class=\"slider-track-container\">\n" +
15802 " <div class=\"slider-track\"></div>\n" +
15803 " <div class=\"slider-track-fill\" ng-style=\"{backgroundColor:trackFillColor}\"></div>\n" +
15805 " <div class=\"slider-knob-container\" ng-class=\"{'slider-knob-hidden':hideKnob}\">\n" +
15806 " <div class=\"slider-knob\" role=\"slider\" aria-valuemin=\"{{min}}\" aria-valuemax=\"{{max}}\" aria-labelledby=\"{{labelId}}\" aria-valuenow=\"{{currentModelValue}}\" aria-valuetext=\"{{currentModelValue}}{{postAriaLabel}}\" aria-orientation=\"{{verticalSlider ? 'vertical' : 'horizontal'}}\"></div>\n" +
15811 angular.module("b2bTemplate/spinButton/spinButton.html", []).run(["$templateCache", function($templateCache) {
15812 $templateCache.put("b2bTemplate/spinButton/spinButton.html",
15813 "<div class=\"btn-group btn-spinbutton-toggle\" ng-class=\"{'disabled': disabledFlag}\">\n" +
15814 " <button type=\"button\" tabindex=\"{{isMobile?'0':'-1'}}\" aria-hidden=\"{{notMobile}}\" class=\"btn btn-secondary btn-prev icon-primary-subtractminimize\" ng-click=\"minus();focusInputSpinButton($event)\" aria-label=\"Remove {{step}}\" aria-controls=\"{{spinButtonId}}\" ng-class=\"{'disabled': isMinusDisabled()}\" ng-disabled=\"isMinusDisabled()\" aria-disabled=\"{{isMinusDisabled()}}\" role=\"spinbutton\"></button>\n" +
15815 " <input class=\"btn pull-left\" id=\"{{spinButtonId}}\" type=\"tel\" b2b-only-nums=\"3\" maxlength=\"3\" min={{min}} max={{max}} data-max-value=\"{{max}}\" ng-model=\"inputValue[inputModelKey]\" value=\"{{inputValue[inputModelKey]}}\" aria-live=\"polite\" aria-valuenow=\"{{inputValue[inputModelKey]}}\" aria-valuemin=\"{{min}}\" aria-valuemax=\"{{max}}\" ng-disabled=\"disabledFlag\">\n" +
15816 " <button type=\"button\" tabindex=\"{{isMobile?'0':'-1'}}\" aria-hidden=\"{{notMobile}}\" class=\"btn btn-secondary btn-next icon-primary-add-maximize\" ng-click=\"plus();focusInputSpinButton($event)\" aria-label=\"Add {{step}}\" aria-controls=\"{{spinButtonId}}\" ng-class=\"{'disabled': isPlusDisabled()}\" ng-disabled=\"isPlusDisabled()\" aria-disabled=\"{{isPlusDisabled()}}\" role=\"spinbutton\"></button>\n" +
15820 angular.module("b2bTemplate/statusTracker/statusTracker.html", []).run(["$templateCache", function($templateCache) {
15821 $templateCache.put("b2bTemplate/statusTracker/statusTracker.html",
15822 "<div class=\"b2b-status-tracker row\">\n" +
15823 " <button tabindex=\"0\" ng-disabled=\"currentViewIndex === 0\" ng-if=\"statuses.length > b2bStatusTrackerConfig.maxViewItems\" class=\"btn-arrow\" type=\"button\" aria-label=\"Previous status\" ng-click=\"previousStatus()\">\n" +
15824 " <div class=\"btn btn-small btn-secondary\">\n" +
15825 " <i class=\"icon-primary-left\"></i>\n" +
15828 " <div ng-repeat=\"status in statuses\" class=\"b2b-status-tracker-step {{ status.state }}\" ng-show=\"isInViewport($index)\">\n" +
15829 " <p class=\"b2b-status-tracker-heading\">{{status.heading}}</p>\n" +
15830 " <div class=\"progress\">\n" +
15831 " <div class=\"progress-bar\">\n" +
15832 " <span class=\"hidden-spoken\">\n" +
15833 " {{ removeCamelCase(status.state) }}\n" +
15837 " <div class=\"b2b-status-tracker-estimate {{status.state}}\">\n" +
15838 " <i ng-show=\"status.estimate !== '' && status.estimate !== undefined\" ng-class=\"b2bStatusTrackerConfig.icons[status.state]\"></i>\n" +
15840 " <span ng-bind-html=\"status.estimate\"></span>\n" +
15843 " <div class=\"b2b-status-tracker-description\" ng-bind-html=\"status.description\"> \n" +
15846 " <button tabindex=\"0\" ng-disabled=\"currentViewIndex + b2bStatusTrackerConfig.maxViewItems === statuses.length\" ng-if=\"statuses.length > b2bStatusTrackerConfig.maxViewItems\" class=\"btn-arrow\" type=\"button\" aria-label=\"Next status\" ng-click=\"nextStatus()\">\n" +
15847 " <div class=\"btn btn-small btn-secondary\">\n" +
15848 " <i class=\"icon-primary-right\"></i>\n" +
15854 angular.module("b2bTemplate/stepTracker/stepTracker.html", []).run(["$templateCache", function($templateCache) {
15855 $templateCache.put("b2bTemplate/stepTracker/stepTracker.html",
15856 "<div class=\"b2b-step-tracker\">\n" +
15857 " <button class=\"btn-arrow b2b-left-arrow\" ng-click=\"previousStatus()\" ng-disabled=\"currentViewIndex === 0\" ng-if=\"stepsItemsObject.length > b2bStepTrackerConfig.maxViewItems\" aria-label=\"Previous step\">\n" +
15858 " <div class=\"btn btn-left btn-small btn-secondary\"><i class=\"icon-primary-left\"></i></div>\n" +
15860 " <button class=\"btn-arrow b2b-right-arrow\" ng-click=\"nextStatus()\" ng-disabled=\"currentViewIndex + b2bStepTrackerConfig.maxViewItems === stepsItemsObject.length\" ng-if=\"stepsItemsObject.length > b2bStepTrackerConfig.maxViewItems\" aria-label=\"Next step\">\n" +
15861 " <div class=\"btn btn-small btn-right btn-secondary\"><i class=\"icon-primary-right\"></i></div>\n" +
15863 " <ul class=\"b2b-steps\">\n" +
15864 " <li ng-class=\"{'b2b-step-done':$index < currentIndex - 1 ,'b2b-step-on':$index == currentIndex - 1}\" \n" +
15865 " ng-repeat=\"stepsItem in stepsItemsObject\" ng-show=\"isInViewport($index)\">\n" +
15866 " <p class=\"b2b-step-text\" data-sm-text=\"{{stepsItem.dataMobile}}\" data-large-text=\"{{stepsItem.dataDesktop}}\">{{stepsItem.text}}</p>\n" +
15867 " <span class=\"hidden-spoken\">\n" +
15868 " {{($index < currentIndex - 1)? 'Complete. '+stepsItem.text+' '+stepsItem.dataMobile:''}} \n" +
15869 " {{($index == currentIndex - 1)? 'In Progress. '+stepsItem.text+' '+stepsItem.dataMobile:''}} \n" +
15870 " {{($index > currentIndex - 1)? 'Incomplete. '+stepsItem.text+' '+stepsItem.dataMobile:''}}\n" +
15877 angular.module("b2bTemplate/switches/switches-spanish.html", []).run(["$templateCache", function($templateCache) {
15878 $templateCache.put("b2bTemplate/switches/switches-spanish.html",
15879 "<div class=\"switch-overlay\" aria-hidden=\"true\">\n" +
15880 " <span class=\"btn-slider-on\"><span class=\"hidden-spoken\">seleccione para hacer </span><i class=\"activo\">activo</i></span>\n" +
15881 " <span class=\"switch-handle\"></span>\n" +
15882 " <span class=\"btn-slider-off\"><span class=\"hidden-spoken\">seleccione para hacer </span><i class=\"inactivo\">inactivo</i></span>\n" +
15886 angular.module("b2bTemplate/switches/switches.html", []).run(["$templateCache", function($templateCache) {
15887 $templateCache.put("b2bTemplate/switches/switches.html",
15888 "<div class=\"switch-overlay\" aria-hidden=\"true\">\n" +
15889 " <span class=\"btn-slider-on\">On</span>\n" +
15890 " <span class=\"switch-handle\"></span>\n" +
15891 " <span class=\"btn-slider-off\">Off</span>\n" +
15895 angular.module("b2bTemplate/tableMessages/tableMessage.html", []).run(["$templateCache", function($templateCache) {
15896 $templateCache.put("b2bTemplate/tableMessages/tableMessage.html",
15897 "<div class=\"b2b-table-message\">\n" +
15898 " <div class=\"b2b-message\" ng-if=\"msgType === 'noMatchingResults'\">\n" +
15899 " <div class=\"b2b-magnify-glass\"></div>\n" +
15901 " <div ng-transclude></div>\n" +
15904 " <div class=\"b2b-message\" ng-if=\"msgType == 'infoCouldNotLoad'\">\n" +
15905 " <div class=\"icon-primary-alert b2b-alert\" aria-label=\"Oops! The information could not load at this time. Please click link to refresh the page.\"></div>\n" +
15906 " <div>Oops!</div>\n" +
15907 " <div>The information could not load at this time.</div>\n" +
15908 " <div>Please <a href=\"javascript:void(0)\" title=\"Refresh the page\" ng-click=\"refreshAction($event)\">refresh the page</a>\n" +
15911 " <div class=\"b2b-message\" ng-if=\"msgType == 'magnifySearch'\">\n" +
15912 " <div class=\"b2b-magnify-glass\"></div>\n" +
15914 " <p class=\"b2b-message-title\">Please input values to\n" +
15915 " <br/> begin your search.</p>\n" +
15918 " <div class=\"b2b-message\" ng-if=\"msgType === 'loadingTable'\">\n" +
15919 " <div class=\"icon-primary-spinner-ddh b2b-loading-dots\"></div>\n" +
15920 " <div ng-transclude></div>\n" +
15926 angular.module("b2bTemplate/tableScrollbar/tableScrollbar.html", []).run(["$templateCache", function($templateCache) {
15927 $templateCache.put("b2bTemplate/tableScrollbar/tableScrollbar.html",
15928 "<div class=\"b2b-table-scrollbar\">\n" +
15929 " <div class=\"b2b-scrollbar-arrows\">\n" +
15930 " <button class=\"btn-arrow b2b-scrollbar-arrow-left\" type=\"button\" ng-attr-aria-label=\"{{disableLeft ? 'Scroll Left Disabled' : 'Scroll Left'}}\" ng-click=\"scrollLeft()\" ng-disabled=\"disableLeft\"><div class=\"btn btn-alt\"><i class=\"icon-primary-left\"></i></div></button>\n" +
15931 " <button class=\"btn-arrow b2b-scrollbar-arrow-right\" ng-attr-aria-label=\"{{disableRight ? 'Scroll Right Disabled' : 'Scroll Right'}}\" ng-click=\"scrollRight()\" ng-disabled=\"disableRight\" type=\"button\"><div class=\"btn btn-alt\"><i class=\"icon-primary-right\"></i></div></button>\n" +
15933 " <div class=\"b2b-table-inner-container\">\n" +
15934 " <span ng-transclude></span>\n" +
15939 angular.module("b2bTemplate/tables/b2bTable.html", []).run(["$templateCache", function($templateCache) {
15940 $templateCache.put("b2bTemplate/tables/b2bTable.html",
15941 "<table ng-class=\"{'striped': responsive, 'complex-table': responsive && active}\" ng-transclude></table>");
15944 angular.module("b2bTemplate/tables/b2bTableBody.html", []).run(["$templateCache", function($templateCache) {
15945 $templateCache.put("b2bTemplate/tables/b2bTableBody.html",
15946 "<td ng-hide=\"isHidden()\" ng-transclude></td>");
15949 angular.module("b2bTemplate/tables/b2bTableHeaderSortable.html", []).run(["$templateCache", function($templateCache) {
15950 $templateCache.put("b2bTemplate/tables/b2bTableHeaderSortable.html",
15951 "<th scope=\"col\" role=\"columnheader\" aria-sort=\"{{sortPattern !== 'null' && 'none' || sortPattern}}\" aria-label=\"{{headerName}} {{sortable !== 'false' && ': activate to sort' || ' '}} {{sortPattern !== 'null' && '' || sortPattern}}\" tabindex=\"{{sortable !== 'false' && '0' || '-1'}}\" b2b-accessibility-click=\"13,32\" ng-click=\"(sortable !== 'false') && sort();\" ng-hide=\"isHidden()\">\n" +
15952 " <span ng-transclude></span>\n" +
15953 " <i ng-class=\"{'icoArrows-sort-arrow active': sortPattern === 'ascending', 'icoArrows-sort-arrow active down': sortPattern === 'descending'}\"></i>\n" +
15957 angular.module("b2bTemplate/tables/b2bTableHeaderUnsortable.html", []).run(["$templateCache", function($templateCache) {
15958 $templateCache.put("b2bTemplate/tables/b2bTableHeaderUnsortable.html",
15959 "<th scope=\"col\" ng-hide=\"isHidden()\" ng-transclude></th>");
15962 angular.module("b2bTemplate/tabs/b2bTab.html", []).run(["$templateCache", function($templateCache) {
15963 $templateCache.put("b2bTemplate/tabs/b2bTab.html",
15964 "<li role=\"tab\" aria-selected=\"{{isTabActive()}}\" aria-controls=\"{{tabPanelId}}\" class=\"tab\" \n" +
15965 " ng-class=\"{'active': isTabActive()}\" ng-click=\"clickTab()\" ng-hide=\"tabItem.disabled\">\n" +
15966 " <a href=\"javascript:void(0)\" tabindex=\"{{(isTabActive() && tabItem.disabled)?-1:0}}\"\n" +
15967 " ng-disabled=\"tabItem.disabled\" aria-expanded=\"{{(isTabActive() && !tabItem.disabled)}}\" \n" +
15968 " b2b-accessibility-click=\"13,32\" ng-transclude></a>\n" +
15969 " <span class=\"hidden-spoken\" ng-if=\"isTabActive()\">Active tab</span>\n" +
15973 angular.module("b2bTemplate/tabs/b2bTabset.html", []).run(["$templateCache", function($templateCache) {
15974 $templateCache.put("b2bTemplate/tabs/b2bTabset.html",
15975 "<ul class=\"tabs promo-tabs\" role=\"tablist\" ng-transclude></ul>");
15978 angular.module("b2bTemplate/treeNav/groupedTree.html", []).run(["$templateCache", function($templateCache) {
15979 $templateCache.put("b2bTemplate/treeNav/groupedTree.html",
15980 "<ul role=\"group\">\n" +
15981 " <li aria-expanded=\"{{(member.active?true:false)}}\" role=\"treeitem\" aria-label=\"{{key}}\" ng-repeat='(key,value) in collection | groupBy : \"grpChild\"' b2b-tree-link><a class=\"grp\" ng-class=\"{'active': value.showGroup == true}\" tabindex=\"-1\" href='javascript:void(0);'>{{(key)?key:''}}<span class=\"b2b-tree-node-icon\"><i ng-class=\"{'icon-primary-expanded': value.showGroup == true, 'icon-primary-collapsed': value.showGroup == undefined || value.showGroup == false }\"></i></span></a>\n" +
15982 " <ul role=\"group\">\n" +
15983 " <b2b-member ng-repeat='member in value.childArray' member='member' time-delay='{{timeDelay}}' group-it='groupIt'></b2b-member>\n" +
15989 angular.module("b2bTemplate/treeNav/treeMember.html", []).run(["$templateCache", function($templateCache) {
15990 $templateCache.put("b2bTemplate/treeNav/treeMember.html",
15991 "<li role=\"treeitem\" aria-expanded=\"{{(member.active?true:false)}}\" aria-label=\"{{member.tooltipContent ? member.tooltipContent : member.name}}\" aria-describedby=\"description_{{$id}}\" ng-class=\"{'bg':member.selected}\" b2b-tree-link>\n" +
15992 " <a tabindex=\"-1\" title=\"{{member.tooltipContent ? member.tooltipContent : member.name}}\" href=\"javascript:void(0)\" ng-class=\"{'active':member.active,'b2b-locked-node':member.locked}\"> <span class=\"b2b-tree-node-name\">{{member.name}}</span>\n" +
15993 " <span class=\"{{!member.child?'end':''}} b2b-tree-node-icon\">\n" +
15994 " <i class=\"b2b-tree-expandCollapse-icon\" ng-class=\"{'icon-primary-expanded':member.active}\"></i>\n" +
15996 " <div id=\"description_{{$id}}\" class=\"offscreen-text\">\n" +
15997 " {{member.descriptionText}}\n" +
15999 " <div class=\"b2b-tree-tooltip\" ng-if=\"member.showTooltip\">\n" +
16000 " <span class=\"b2b-tree-arrow-left\"></span>\n" +
16001 " <div class=\"b2b-tree-tooltip-content\">\n" +
16002 " {{member.tooltipContent}}\n" +
16009 angular.module("b2bTemplate/treeNav/ungroupedTree.html", []).run(["$templateCache", function($templateCache) {
16010 $templateCache.put("b2bTemplate/treeNav/ungroupedTree.html",
16011 "<ul role=\"{{setRole}}\"><b2b-member ng-repeat='member in collection' member='member' group-it='groupIt'></b2b-member></ul>");
16014 angular.module("b2bTemplate/treeNodeCheckbox/groupedTree.html", []).run(["$templateCache", function($templateCache) {
16015 $templateCache.put("b2bTemplate/treeNodeCheckbox/groupedTree.html",
16016 "<ul role=\"group\">\n" +
16017 " <li aria-expanded=\"{{(member.active?true:false)}}\" role=\"treeitem\" aria-label=\"{{key}}\" ng-repeat='(key,value) in collection | groupBy : \"grpChild\"' b2b-tree-node-link><a class=\"grp\" ng-class=\"{'active': value.showGroup == true}\" tabindex=\"-1\" href='javascript:void(0);'>\n" +
16018 " <span class=\"ng-hide\">\n" +
16019 " <label class=\"checkbox\">\n" +
16020 " <input type=\"checkbox\" tabindex=\"-1\" class=\"treeCheckBox grpTreeCheckbox\" style=\"margin-top:2px;\"/><i class=\"skin\"></i><span> {{(key)?key:''}}</span>\n" +
16024 " {{(key)?key:''}} \n" +
16026 " <span class=\"nodeIcon\"><i class=\"expandCollapseIcon\" ng-class=\"{'icon-primary-expanded': value.showGroup == true, 'icon-primary-collapsed': value.showGroup == undefined || value.showGroup == false }\"></i></span></a>\n" +
16027 " <ul role=\"group\">\n" +
16028 " <b2b-tree-member ng-repeat='member in value.childArray' member='member' group-it='groupIt'></b2b-tree-member>\n" +
16034 angular.module("b2bTemplate/treeNodeCheckbox/treeMember.html", []).run(["$templateCache", function($templateCache) {
16035 $templateCache.put("b2bTemplate/treeNodeCheckbox/treeMember.html",
16036 "<li role=\"treeitem\" aria-expanded=\"{{(member.active?true:false)}}\" aria-label=\"{{member.name}}\" ng-class=\"{'bg':member.selected}\" b2b-tree-node-link>\n" +
16037 " <a tabindex=\"-1\" title=\"{{member.name}}\" href=\"javascript:void(0)\" ng-class=\"{'active':member.active}\">\n" +
16038 " <span ng-show=\"member.displayCheckbox\">\n" +
16039 " <label class=\"checkbox\">\n" +
16040 " <input type=\"checkbox\" tabindex=\"-1\" ng-model=\"member.isSelected\" ng-class=\"{'treeCheckBox': (member.displayCheckbox !== undefined)}\" style=\"margin-top:2px;\"/><i class=\"skin\"></i><span> {{member.name}}</span>\n" +
16043 " <span ng-show=\"!member.displayCheckbox\">\n" +
16044 " {{member.name}} \n" +
16046 " <span class=\"nodeIcon {{!member.child?'end':''}}\">\n" +
16047 " <i class=\"expandCollapseIcon\" ng-class=\"{'icon-primary-expanded':member.active}\"></i>\n" +
16053 angular.module("b2bTemplate/treeNodeCheckbox/ungroupedTree.html", []).run(["$templateCache", function($templateCache) {
16054 $templateCache.put("b2bTemplate/treeNodeCheckbox/ungroupedTree.html",
16055 "<ul role=\"{{setRole}}\"><b2b-tree-member ng-repeat='member in collection' member='member' group-it='groupIt'></b2b-tree-member></ul>");