1 /*! b2b-angular-library - v1.0.5 - Last updated: 2017-05-24. Copyright (c) 2016 AT&T Services, Inc. */
2 angular.module("b2b.att.tpls", ['b2bTemplate/audioPlayer/audioPlayer.html', 'b2bTemplate/audioRecorder/audioRecorder.html', 'b2bTemplate/backToTop/backToTop.html', 'b2bTemplate/boardstrip/b2bAddBoard.html', 'b2bTemplate/boardstrip/b2bBoard.html', 'b2bTemplate/boardstrip/b2bBoardstrip.html', 'b2bTemplate/calendar/datepicker-popup.html', 'b2bTemplate/calendar/datepicker.html', 'b2bTemplate/coachmark/coachmark.html', 'b2bTemplate/dropdowns/b2bDropdownDesktop.html', 'b2bTemplate/dropdowns/b2bDropdownGroupDesktop.html', 'b2bTemplate/dropdowns/b2bDropdownListDesktop.html', 'b2bTemplate/fileUpload/fileUpload.html', 'b2bTemplate/flyout/flyout.html', 'b2bTemplate/flyout/flyoutContent.html', 'b2bTemplate/footer/footer_column_switch_tpl.html', 'b2bTemplate/horizontalTable/horizontalTable.html', 'b2bTemplate/hourPicker/b2bHourpicker.html', 'b2bTemplate/hourPicker/b2bHourpickerPanel.html', 'b2bTemplate/hourPicker/b2bHourpickerValue.html', 'b2bTemplate/leftNavigation/leftNavigation.html', 'b2bTemplate/listbox/listbox.html', 'b2bTemplate/modalsAndAlerts/b2b-backdrop.html', 'b2bTemplate/modalsAndAlerts/b2b-window.html', 'b2bTemplate/monthSelector/monthSelector-popup.html', 'b2bTemplate/monthSelector/monthSelector.html', 'b2bTemplate/monthSelector/monthSelectorLink.html', 'b2bTemplate/pagination/b2b-pagination.html', 'b2bTemplate/paneSelector/paneSelector.html', 'b2bTemplate/paneSelector/paneSelectorPane.html', 'b2bTemplate/profileCard/profileCard-addUser.html', 'b2bTemplate/profileCard/profileCard.html', 'b2bTemplate/searchField/searchField.html', 'b2bTemplate/seekBar/seekBar.html', 'b2bTemplate/slider/slider.html', 'b2bTemplate/spinButton/spinButton.html', 'b2bTemplate/statusTracker/statusTracker.html', 'b2bTemplate/stepTracker/stepTracker.html', 'b2bTemplate/switches/switches-spanish.html', 'b2bTemplate/switches/switches.html', 'b2bTemplate/tableMessages/tableMessage.html', 'b2bTemplate/tables/b2bTable.html', 'b2bTemplate/tables/b2bTableBody.html', 'b2bTemplate/tables/b2bTableHeaderSortable.html', 'b2bTemplate/tables/b2bTableHeaderUnsortable.html', 'b2bTemplate/tableScrollbar/tableScrollbar.html', 'b2bTemplate/tabs/b2bTab.html', 'b2bTemplate/tabs/b2bTabset.html', 'b2bTemplate/treeNav/groupedTree.html', 'b2bTemplate/treeNav/treeMember.html', 'b2bTemplate/treeNav/ungroupedTree.html', 'b2bTemplate/treeNodeCheckbox/groupedTree.html', 'b2bTemplate/treeNodeCheckbox/treeMember.html', 'b2bTemplate/treeNodeCheckbox/ungroupedTree.html']);angular.module("b2b.att", ["b2b.att.tpls", 'b2b.att.addressInputTemplate','b2b.att.arrows','b2b.att.audioPlayer','b2b.att.audioRecorder','b2b.att.backToTop','b2b.att.badgesForAlerts','b2b.att.boardstrip','b2b.att.bootstrapGridTemplate','b2b.att.breadcrumbs','b2b.att.buttonGroups','b2b.att.buttons','b2b.att.calendar','b2b.att.checkboxes','b2b.att.coachmark','b2b.att.configurationSection','b2b.att.directoryListingTemplate','b2b.att.dropdowns','b2b.att.fileUpload','b2b.att.filters','b2b.att.flyout','b2b.att.footer','b2b.att.header','b2b.att.headingsAndCopy','b2b.att.horizontalTable','b2b.att.hourPicker','b2b.att.inputTemplate','b2b.att.leftNavigation','b2b.att.links','b2b.att.listbox','b2b.att.loaderAnimation','b2b.att.messageWrapper','b2b.att.modalsAndAlerts','b2b.att.monthSelector','b2b.att.multiLevelNavigation','b2b.att.multipurposeExpander','b2b.att.notesMessagesAndErrors','b2b.att.notificationCardTemplate','b2b.att.orderConfirmationTemplate','b2b.att.pagination','b2b.att.paneSelector','b2b.att.phoneNumberInput','b2b.att.profileBlockTemplate','b2b.att.profileCard','b2b.att.radios','b2b.att.searchField','b2b.att.seekBar','b2b.att.separators','b2b.att.slider','b2b.att.spinButton','b2b.att.staticRouteTemplate','b2b.att.statusTracker','b2b.att.stepTracker','b2b.att.switches','b2b.att.tableDragAndDrop','b2b.att.tableMessages','b2b.att.tables','b2b.att.tableScrollbar','b2b.att.tabs','b2b.att.tagBadges','b2b.att.textArea','b2b.att.timeInputField','b2b.att.tooltipsForForms','b2b.att.treeNav','b2b.att.treeNodeCheckbox','b2b.att.utilities']);/**
4 * @name Template.att:Address Input
7 * <file src="src/addressInputTemplate/docs/readme.md" />
14 <example module="b2b.att">
15 <file src="src/addressInputTemplate/docs/demo.html" />
16 <file src="src/addressInputTemplate/docs/demo.js" />
21 angular.module('b2b.att.addressInputTemplate', ['ngMessages']);
24 * @name Buttons, links & UI controls.att:arrows
27 * <file src="src/arrows/docs/readme.md" />
30 * Please refer demo.html tab in Example section below.
34 <example module="b2b.att">
35 <file src="src/arrows/docs/demo.html" />
36 <file src="src/arrows/docs/demo.js" />
41 angular.module('b2b.att.arrows', []);
44 * @name Videos, audio & animation.att:Audio Player
46 * @param {string} audioSrcUrl - MP3 audio source URL or Blob URL
48 * <file src="src/audioPlayer/docs/readme.md" />
52 <div b2b-audio audio-src-url='audioSrcUrl'></div>
56 <example module="b2b.att">
57 <file src="src/audioPlayer/docs/demo.html" />
58 <file src="src/audioPlayer/docs/demo.js" />
64 angular.module('b2b.att.audioPlayer', ['b2b.att.utilities', 'b2b.att.seekBar'])
65 .constant('AudioPlayerConfig', {
67 'timeShiftInSeconds': 5
69 .filter('trustedAudioUrl', ['$sce', function ($sce) {
70 return function (audioFileFullPath) {
71 return audioFileFullPath ? $sce.trustAsResourceUrl(audioFileFullPath) : 'undefined';
74 .directive('b2bAudio', ['$log', '$timeout', 'AudioPlayerConfig', '$compile', 'events', function ($log, $timeout, AudioPlayerConfig, $compile, events) {
81 templateUrl: 'b2bTemplate/audioPlayer/audioPlayer.html',
82 controller: function ($scope) {
86 if (!angular.isDefined($scope.audioSrcUrl)) {
87 $log.warn('b2b-audio : audio-src-url undefined');
88 $scope.audioSrcUrl = undefined;
89 $scope.audio.mp3 = undefined;
93 link: function (scope, element) {
94 var audioElement = angular.element(element[0].querySelector('audio'))[0];
95 scope.audio.audioElement = audioElement;
97 function setAttributes(element, attributes) {
98 Object.keys(attributes).forEach(function (name) {
99 element.setAttribute(name, attributes[name]);
103 $timeout(function () {
104 // TODO: Replace with DDA Tooltip
105 var seekBarKnob = element[0].querySelector('.b2b-seek-bar-knob');
106 var tooltipObject = {
107 'tooltip': '{{timeFormatter(audio.currentTime)}}',
108 'tooltip-placement': 'above',
109 'tooltip-style': 'blue',
110 'tooltip-trigger': 'mousedown',
111 'tooltip-append-to-body': 'false',
112 'tooltip-offset': '-10',
113 'refer-by': 'seek-bar-tooltip'
115 setAttributes(seekBarKnob, tooltipObject);
116 $compile(seekBarKnob)(scope);
119 if (angular.isDefined(scope.audioSrcUrl)) {
120 scope.audio.mp3 = scope.audioSrcUrl;
123 scope.audio.currentTime = 0;
124 scope.audio.currentVolume = AudioPlayerConfig.defaultVolume;
125 scope.audio.timeShiftInSeconds = AudioPlayerConfig.timeShiftInSeconds;
126 scope.isPlayInProgress = false;
127 scope.isReady = false;
128 scope.isAudioDragging = false;
130 $timeout(function () {
132 audioElement.volume = scope.audio.currentVolume / 100;
135 scope.$watch('audioSrcUrl', function (newVal, oldVal) {
136 if (newVal !== oldVal) {
138 $log.warn('b2b-audio : audio-src-url undefined. Please provide a valid URL');
141 scope.audio.mp3 = newVal;
142 $timeout(function () {
148 scope.playAudio = function () {
154 audioElement.onplay = function () {
155 scope.isPlayInProgress = true;
159 scope.pauseAudio = function () {
160 audioElement.pause();
163 audioElement.onpause = function () {
164 scope.isPlayInProgress = false;
168 scope.toggleAudio = function () {
169 if (audioElement.paused) {
176 scope.volumeUp = function (delta) {
182 audioElement.muted = false;
183 if (audioElement.volume < 1) {
184 audioElement.volume = Math.min((Math.round((audioElement.volume + delta) * 100) / 100), 1);
186 scope.audio.currentVolume = audioElement.volume * 100;
187 return audioElement.volume;
190 scope.volumeDown = function (delta) {
196 audioElement.muted = false;
197 if (audioElement.volume > 0) {
198 audioElement.volume = Math.max((Math.round((audioElement.volume - delta) * 100) / 100), 0);
200 scope.audio.currentVolume = audioElement.volume * 100;
201 return audioElement.volume;
204 var volumeHandler = function (e) {
205 events.preventDefault(e);
206 if ((e.wheelDelta && e.wheelDelta > 0) || (e.detail && e.detail < 0)) {
216 scope.$watch('audio.currentVolume', function (newVal, oldVal) {
217 if (newVal !== oldVal) {
218 audioElement.volume = newVal / 100;
222 scope.setCurrentTime = function (timeInSec) {
223 audioElement.currentTime = timeInSec;
226 scope.setAudioPosition = function (val) {
228 scope.setCurrentTime(val);
229 scope.isAudioDragging = false;
233 function getTimestampArray(timestamp) {
234 var d = Math.abs(timestamp) / 1000; // delta
235 var r = {}; // result
236 var s = { // structure
243 Object.keys(s).forEach(function (key) {
244 r[key] = Math.floor(d / s[key]);
245 d -= r[key] * s[key];
251 scope.timeFormatter = function (timeInSec) {
252 var formattedTime = '00:00';
254 if (!timeInSec || timeInSec < 1) {
255 return formattedTime;
258 if (typeof timeInSec === 'string') {
262 var dateArray = getTimestampArray(timeInSec * 1000);
263 Object.keys(dateArray).forEach(function (key) {
264 if (dateArray[key] === 0) {
265 dateArray[key] = '00';
266 } else if (dateArray[key] < 10) {
267 dateArray[key] = '0' + dateArray[key];
271 formattedTime = dateArray['minute'] + ':' + dateArray['second'];
273 if (dateArray['hour'] !== '00') {
274 formattedTime = dateArray['hour'] + ':' + formattedTime;
277 if (dateArray['day'] !== '00') {
278 formattedTime = dateArray['day'] + ':' + formattedTime;
281 return formattedTime;
284 audioElement.onloadedmetadata = function () {
285 scope.audio.duration = audioElement.duration;
289 audioElement.ontimeupdate = function () {
290 if (!scope.isAudioDragging) {
291 scope.audio.currentTime = audioElement.currentTime;
296 audioElement.onended = function () {
297 scope.setCurrentTime(0);
298 scope.audio.currentTime = 0;
299 if (!audioElement.paused) {
305 audioElement.oncanplay = function () {
306 scope.isReady = true;
307 scope.isPlayInProgress = !audioElement.paused;
311 var onloadstart = function () {
312 scope.isReady = false;
313 scope.isPlayInProgress = !audioElement.paused;
314 scope.audio.currentTime = 0;
315 scope.audio.duration = 0;
318 audioElement.addEventListener("loadstart", onloadstart);
324 * @name Videos, audio & animation.att:Audio Recorder
326 * @param {function} callback - A callback to handle the WAV blob
327 * @param {object} config - A config object with properties startRecordingMessage & whileRecordingMessage
329 * <file src="src/audioRecorder/docs/readme.md" />
333 * <section id="code">
334 <example module="b2b.att">
335 <file src="src/audioRecorder/docs/demo.html" />
336 <file src="src/audioRecorder/docs/demo.js" />
341 angular.module('b2b.att.audioRecorder', ['b2b.att.utilities'])
342 .constant('AudioRecorderConfig', {
343 'startRecordingMessage': 'Click on REC icon to being recording',
344 'whileRecordingMessage': 'Recording...'
346 .directive('b2bAudioRecorder', ['$interval', 'AudioRecorderConfig', 'b2bUserAgent', 'b2bRecorder', function($interval, AudioRecorderConfig, b2bUserAgent, b2bRecorder) {
353 templateUrl: 'b2bTemplate/audioRecorder/audioRecorder.html',
354 controller: function($scope) {
356 function hasGetUserMedia() {
357 return !!(navigator.getUserMedia || navigator.webkitGetUserMedia ||
358 navigator.mozGetUserMedia || navigator.msGetUserMedia);
361 if (!hasGetUserMedia()) {
362 throw new Error('Your broswer does not support MediaRecorder API');
365 if (!(b2bUserAgent.isFF() || b2bUserAgent.isChrome())) {
366 throw new Error('b2bAudioRecorder does not support this browser!');
370 link: function(scope, element) {
371 scope.elapsedTime = 0;
372 scope.isRecording = false;
374 scope.config.startRecordingMessage = AudioRecorderConfig.startRecordingMessage;
375 scope.config.whileRecordingMessage = AudioRecorderConfig.whileRecordingMessage;
378 var timer = undefined; // Interval promise
379 navigator.getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.msGetUserMedia;
381 var audio = angular.element(element[0].querySelector('audio'))[0];
382 var recorder = undefined;
384 function startRecording() {
385 scope.isRecording = true;
386 navigator.mediaDevices.getUserMedia({
388 }).then(function(stream) {
389 //create the MediaStreamAudioSourceNode
390 context = new AudioContext();
391 source = context.createMediaStreamSource(stream);
392 recorder = new b2bRecorder(source);
395 timer = $interval(function() {
396 scope.elapsedTime += 1;
398 }).catch(function(err) {
404 function stopRecording() {
405 scope.isRecording = false;
408 recorder.exportWAV(function(s) {
409 audio.src = window.URL.createObjectURL(s);
410 context.close().then(function() {
412 $interval.cancel(timer);
414 scope.elapsedTime = 0;
417 recorder = undefined;
419 if (angular.isFunction(scope.callback)){
420 scope.callback({'data': audio});
427 scope.toggleRecording = function() {
428 if (scope.isRecording) {
437 //TODO: Move this into utilities
438 function getTimestampArray(timestamp) {
439 var d = Math.abs(timestamp) / 1000; // delta
440 var r = {}; // result
441 var s = { // structure
448 Object.keys(s).forEach(function(key) {
449 r[key] = Math.floor(d / s[key]);
450 d -= r[key] * s[key];
455 scope.timeFormatter = function(timeInSec) {
456 var formattedTime = '00:00';
458 if (!timeInSec || timeInSec < 1) {
459 return formattedTime;
462 if (typeof timeInSec === 'string') {
466 var dateArray = getTimestampArray(timeInSec * 1000);
467 Object.keys(dateArray).forEach(function(key) {
468 if (dateArray[key] === 0) {
469 dateArray[key] = '00';
470 } else if (dateArray[key] < 10) {
471 dateArray[key] = '0' + dateArray[key];
475 formattedTime = dateArray['minute'] + ':' + dateArray['second'];
477 if (dateArray['hour'] !== '00') {
478 formattedTime = dateArray['hour'] + ':' + formattedTime;
481 if (dateArray['day'] !== '00') {
482 formattedTime = dateArray['day'] + ':' + formattedTime;
485 return formattedTime;
488 scope.$on('$destroy', function() {
490 $interval.cancel(timer);
499 * @name Navigation.att:Back To Top
502 * <file src="src/backToTop/docs/readme.md" />
503 * @param {integer} scrollSpeed - Scroll speed in seconds, default is 1
507 <div ng-controller="backToTopController">
508 <div b2b-backtotop></div>
512 * <section id="code">
513 <example module="b2b.att">
514 <file src="src/backToTop/docs/demo.html" />
515 <file src="src/backToTop/docs/demo.js" />
521 angular.module('b2b.att.backToTop', ['b2b.att.utilities','b2b.att.position'])
522 .directive('b2bBacktotopButton', [function () {
526 templateUrl: 'b2bTemplate/backToTop/backToTop.html',
527 link: function (scope, elem, attr) {
528 elem.bind('click', function(evt) {
529 var scrollSpeed = parseInt(attr.scrollSpeed) || 1;
530 TweenLite.to(window, scrollSpeed, {scrollTo:{x: 0, y: 0}});
537 * @name Messages, modals & alerts.att:badgesForAlerts
540 * <file src="src/badgesForAlerts/docs/readme.md" />
542 * <section id="code">
543 <example module="b2b.att">
544 <file src="src/badgesForAlerts/docs/demo.html" />
545 <file src="src/badgesForAlerts/docs/demo.js" />
550 angular.module('b2b.att.badgesForAlerts', []);
553 * @name Misc.att:boardstrip
556 * <file src="src/boardstrip/docs/readme.md" />
562 <b>HTML + AngularJS</b>
563 <example module="b2b.att">
564 <file src="src/boardstrip/docs/demo.html" />
565 <file src="src/boardstrip/docs/demo.js" />
569 angular.module('b2b.att.boardstrip', ['b2b.att.utilities'])
570 .constant('BoardStripConfig', {
571 'maxVisibleBoards': 4,
573 /* These parameters are non-configurable and remain unaltered, until there is a change in corresponding CSS */
577 .directive('b2bBoard', [function () {
582 require: '^b2bBoardStrip',
587 templateUrl: 'b2bTemplate/boardstrip/b2bBoard.html',
588 link: function (scope, element, attrs, ctrls) {
590 var parentCtrl = ctrls;
592 scope.getCurrentIndex = function () {
593 return parentCtrl.getCurrentIndex();
595 scope.selectBoard = function (boardIndex) {
596 if (!isNaN(boardIndex)) {
597 parentCtrl.setCurrentIndex(boardIndex);
603 .directive('b2bBoardStrip', ['BoardStripConfig', '$timeout', function (BoardStripConfig, $timeout) {
608 require: ['?ngModel', 'b2bBoardStrip'],
610 boardsMasterArray: '=',
613 templateUrl: 'b2bTemplate/boardstrip/b2bBoardstrip.html',
614 controller: function ($scope) {
615 if (!angular.isDefined($scope.boardsMasterArray)) {
616 $scope.boardsMasterArray = [];
619 this.rectifyMaxVisibleBoards = function () {
620 if (this.maxVisibleIndex >= $scope.boardsMasterArray.length) {
621 this.maxVisibleIndex = $scope.boardsMasterArray.length - 1;
624 if (this.maxVisibleIndex < 0) {
625 this.maxVisibleIndex = 0;
629 this.resetBoardStrip = function () {
630 $scope.currentIndex = 0;
632 this.maxVisibleIndex = BoardStripConfig.maxVisibleBoards - 1;
633 this.minVisibleIndex = 0;
635 this.rectifyMaxVisibleBoards();
638 this.getCurrentIndex = function () {
639 return $scope.currentIndex;
641 this.setCurrentIndex = function (indx) {
642 $scope.currentIndex = indx;
645 this.getBoardsMasterArrayLength = function () {
646 return $scope.boardsMasterArray.length;
649 $scope.addBoardPressedFlag = false;
650 this.getAddBoardPressedFlag = function () {
651 return $scope.addBoardPressedFlag;
653 this.setAddBoardPressedFlag = function (booleanValue) {
654 $scope.addBoardPressedFlag = booleanValue;
658 link: function (scope, element, attrs, ctrls) {
660 var ngModelCtrl = ctrls[0];
664 var animationTimeout = 1000;
666 var getBoardViewportWidth = function (numberOfVisibleBoards) {
667 return numberOfVisibleBoards * (BoardStripConfig.boardLength + BoardStripConfig.boardMargin);
669 if (element[0].querySelector(".board-viewport")) {
670 angular.element(element[0].querySelector(".board-viewport")).css({
671 "width": getBoardViewportWidth(BoardStripConfig.maxVisibleBoards) + "px"
675 var getBoardstripContainerWidth = function (totalNumberOfBoards) {
676 return totalNumberOfBoards * (BoardStripConfig.boardLength + BoardStripConfig.boardMargin);
678 if (element[0].querySelector(".boardstrip-container")) {
679 angular.element(element[0].querySelector(".boardstrip-container")).css({
680 "width": getBoardstripContainerWidth(ctrl.getBoardsMasterArrayLength()) + "px"
682 angular.element(element[0].querySelector(".boardstrip-container")).css({
687 var calculateAndGetBoardstripContainerAdjustment = function () {
689 var calculatedAdjustmentValue;
691 if (ctrl.getBoardsMasterArrayLength() <= BoardStripConfig.maxVisibleBoards) {
692 calculatedAdjustmentValue = 0;
694 calculatedAdjustmentValue = (ctrl.minVisibleIndex * (BoardStripConfig.boardLength + BoardStripConfig.boardMargin)) * -1;
697 return calculatedAdjustmentValue;
700 var animateBoardstripContainerAdjustment = function (elementToFocusAfterAnimation) {
701 var oldContainerAdjustment = angular.element(element[0].querySelector(".boardstrip-container"))[0].style.left;
702 var containerAdjustment = calculateAndGetBoardstripContainerAdjustment();
703 if (oldContainerAdjustment !== containerAdjustment + 'px') {
704 angular.element(element[0].querySelector(".boardstrip-container")).css({
705 "left": containerAdjustment + "px"
708 $timeout.cancel(oldTimeout);
709 oldTimeout = $timeout(function () {
710 elementToFocusAfterAnimation.focus();
711 }, animationTimeout);
713 elementToFocusAfterAnimation.focus();
717 var updateBoardsTabIndex = function (boardArray, minViewIndex, maxViewIndex) {
718 for (var i = 0; i < boardArray.length; i++) {
719 angular.element(boardArray[i]).attr('tabindex', '-1');
721 for (var j = minViewIndex; j <= maxViewIndex; j++) {
722 angular.element(boardArray[j]).attr('tabindex', '0');
726 $timeout(function () {
727 var currentBoardArray = element[0].querySelectorAll('[b2b-board]');
728 updateBoardsTabIndex(currentBoardArray, ctrl.minVisibleIndex, ctrl.maxVisibleIndex);
731 scope.$watchCollection('boardsMasterArray', function (newVal, oldVal) {
732 if (newVal !== oldVal) {
733 /* When a board is removed */
734 if (newVal.length < oldVal.length) {
735 ctrl.resetBoardStrip();
736 $timeout(function () {
738 var currentBoardArray = element[0].querySelectorAll('[b2b-board]');
739 if (currentBoardArray.length !== 0) {
740 animateBoardstripContainerAdjustment(currentBoardArray[0]);
742 element[0].querySelector('div.boardstrip-item--add').focus();
745 angular.element(element[0].querySelector(".boardstrip-container")).css({
746 "width": getBoardstripContainerWidth(ctrl.getBoardsMasterArrayLength()) + "px"
748 /* Update tabindecies to ensure keyboard navigation behaves correctly */
749 updateBoardsTabIndex(currentBoardArray, ctrl.minVisibleIndex, ctrl.maxVisibleIndex);
752 /* When a board is added */
754 if (ctrl.getAddBoardPressedFlag()) {
755 ctrl.maxVisibleIndex = ctrl.getBoardsMasterArrayLength() - 1;
756 ctrl.minVisibleIndex = Math.max(ctrl.maxVisibleIndex - BoardStripConfig.maxVisibleBoards + 1, 0);
758 ctrl.setCurrentIndex(ctrl.maxVisibleIndex);
760 $timeout(function () {
761 angular.element(element[0].querySelector(".boardstrip-container")).css({
762 "width": getBoardstripContainerWidth(ctrl.getBoardsMasterArrayLength()) + "px"
765 var currentBoardArray = element[0].querySelectorAll('[b2b-board]');
766 animateBoardstripContainerAdjustment(currentBoardArray[currentBoardArray.length - 1]);
767 /* Update tabindecies to ensure keyboard navigation behaves correctly */
768 updateBoardsTabIndex(currentBoardArray, ctrl.minVisibleIndex, ctrl.maxVisibleIndex);
771 if (ctrl.minVisibleIndex === 0 && ctrl.getBoardsMasterArrayLength() < BoardStripConfig.maxVisibleBoards + 1) {
772 ctrl.maxVisibleIndex = ctrl.getBoardsMasterArrayLength() - 1;
773 ctrl.rectifyMaxVisibleBoards();
776 $timeout(function () {
777 angular.element(element[0].querySelector(".boardstrip-container")).css({
778 "width": getBoardstripContainerWidth(ctrl.getBoardsMasterArrayLength()) + "px"
781 var currentBoardArray = element[0].querySelectorAll('[b2b-board]');
782 /* Update tabindecies to ensure keyboard navigation behaves correctly */
783 updateBoardsTabIndex(currentBoardArray, ctrl.minVisibleIndex, ctrl.maxVisibleIndex);
787 ctrl.setAddBoardPressedFlag(false);
792 scope.nextBoard = function () {
793 ctrl.maxVisibleIndex += BoardStripConfig.boardsToScroll;
794 ctrl.rectifyMaxVisibleBoards();
795 ctrl.minVisibleIndex = ctrl.maxVisibleIndex - (BoardStripConfig.maxVisibleBoards - 1);
797 $timeout.cancel(oldTimeout);
798 angular.element(element[0].querySelector(".boardstrip-container")).css({
799 "left": calculateAndGetBoardstripContainerAdjustment() + "px"
802 $timeout(function () {
803 var currentBoardArray = element[0].querySelectorAll('[b2b-board]');
805 /* Remove tabindex from non-visible boards */
806 updateBoardsTabIndex(currentBoardArray, ctrl.minVisibleIndex, ctrl.maxVisibleIndex);
808 if (!(scope.isNextBoard())) {
810 currentBoardArray[currentBoardArray.length - 1].focus();
811 } catch (e) { /* IE8 may throw exception */ }
813 }, animationTimeout);
815 scope.prevBoard = function () {
817 ctrl.minVisibleIndex -= BoardStripConfig.boardsToScroll;
818 if (ctrl.minVisibleIndex < 0) {
819 ctrl.minVisibleIndex = 0;
822 ctrl.maxVisibleIndex = ctrl.minVisibleIndex + BoardStripConfig.maxVisibleBoards - 1;
823 ctrl.rectifyMaxVisibleBoards();
825 $timeout.cancel(oldTimeout);
826 angular.element(element[0].querySelector(".boardstrip-container")).css({
827 "left": calculateAndGetBoardstripContainerAdjustment() + "px"
830 $timeout(function () {
831 var currentBoardArray = element[0].querySelectorAll('[b2b-board]');
833 /* Remove tabindex from non-visible boards */
834 updateBoardsTabIndex(currentBoardArray, ctrl.minVisibleIndex, ctrl.maxVisibleIndex);
836 if (ctrl.minVisibleIndex === 0) {
838 element[0].querySelector('div.boardstrip-item--add').focus();
839 } catch (e) { /* IE8 may throw exception */ }
844 scope.isPrevBoard = function () {
845 return (ctrl.minVisibleIndex > 0);
847 scope.isNextBoard = function () {
848 return (ctrl.getBoardsMasterArrayLength() - 1 > ctrl.maxVisibleIndex);
851 ngModelCtrl.$render = function () {
852 if (ngModelCtrl.$viewValue || ngModelCtrl.$viewValue === 0) {
853 var newCurrentIndex = ngModelCtrl.$viewValue;
855 if (!(newCurrentIndex = parseInt(newCurrentIndex, 10))) {
859 if (newCurrentIndex <= 0) {
860 ctrl.resetBoardStrip();
863 var currentBoardArray = element[0].querySelectorAll('[b2b-board]');
864 if (currentBoardArray.length !== 0) {
865 animateBoardstripContainerAdjustment(currentBoardArray[0]);
867 element[0].querySelector('div.boardstrip-item--add').focus();
869 /* Update tabindecies to ensure keyboard navigation behaves correctly */
870 updateBoardsTabIndex(currentBoardArray, ctrl.minVisibleIndex, ctrl.maxVisibleIndex);
871 } else if (newCurrentIndex >= ctrl.getBoardsMasterArrayLength()) {
872 ctrl.maxVisibleIndex = ctrl.getBoardsMasterArrayLength() - 1;
873 ctrl.rectifyMaxVisibleBoards();
874 ctrl.minVisibleIndex = Math.max(ctrl.maxVisibleIndex - BoardStripConfig.maxVisibleBoards + 1, 0);
876 newCurrentIndex = ctrl.maxVisibleIndex;
878 $timeout(function () {
879 var currentBoardArray = element[0].querySelectorAll('[b2b-board]');
880 animateBoardstripContainerAdjustment(currentBoardArray[newCurrentIndex]);
881 /* Update tabindecies to ensure keyboard navigation behaves correctly */
882 updateBoardsTabIndex(currentBoardArray, ctrl.minVisibleIndex, ctrl.maxVisibleIndex);
886 if (!(newCurrentIndex >= ctrl.minVisibleIndex && newCurrentIndex <= ctrl.maxVisibleIndex)) {
887 ctrl.minVisibleIndex = newCurrentIndex;
888 ctrl.maxVisibleIndex = ctrl.minVisibleIndex + BoardStripConfig.maxVisibleBoards - 1;
889 ctrl.rectifyMaxVisibleBoards();
891 if (ctrl.getBoardsMasterArrayLength() < BoardStripConfig.maxVisibleBoards) {
892 ctrl.minVisibleIndex = 0;
894 ctrl.minVisibleIndex = Math.max(ctrl.maxVisibleIndex - BoardStripConfig.maxVisibleBoards + 1, 0);
897 $timeout(function () {
898 var currentBoardArray = element[0].querySelectorAll('[b2b-board]');
899 animateBoardstripContainerAdjustment(currentBoardArray[newCurrentIndex]);
900 /* Update tabindecies to ensure keyboard navigation behaves correctly */
901 updateBoardsTabIndex(currentBoardArray, ctrl.minVisibleIndex, ctrl.maxVisibleIndex);
905 scope.currentIndex = newCurrentIndex;
906 ngModelCtrl.$setViewValue(newCurrentIndex);
908 ctrl.resetBoardStrip();
909 ngModelCtrl.$setViewValue(0);
913 scope.$watch('currentIndex', function (newVal, oldVal) {
914 if (newVal !== oldVal && ngModelCtrl && ngModelCtrl.$viewValue !== newVal) {
915 ngModelCtrl.$setViewValue(newVal);
921 .directive('b2bAddBoard', ['BoardStripConfig', '$parse', function (BoardStripConfig, $parse) {
925 require: '^b2bBoardStrip',
929 templateUrl: 'b2bTemplate/boardstrip/b2bAddBoard.html',
930 link: function (scope, element, attrs, ctrl) {
931 scope.addBoard = function () {
932 if (attrs['onAddBoard']) {
933 scope.onAddBoard = $parse(scope.onAddBoard);
935 ctrl.setAddBoardPressedFlag(true);
941 .directive('b2bBoardNavigation', ['keymap', 'events', function (keymap, events) {
944 link: function (scope, elem) {
946 var prevElem = keymap.KEY.LEFT;
947 var nextElem = keymap.KEY.RIGHT;
949 elem.bind('keydown', function (ev) {
952 ev.keyCode = ev.which;
955 switch (ev.keyCode) {
957 events.preventDefault(ev);
958 events.stopPropagation(ev);
960 if (elem[0].nextElementSibling && parseInt(angular.element(elem[0].nextElementSibling).attr('tabindex')) >= 0) {
961 angular.element(elem[0])[0].nextElementSibling.focus();
964 var el = angular.element(elem[0])[0];
966 if (el.nextSibling) {
971 } while (el && el.tagName !== 'LI');
973 if (el.tagName && el.tagName === 'LI' && parseInt(angular.element(el).attr('tabindex')) >= 0) {
980 events.preventDefault(ev);
981 events.stopPropagation(ev);
983 if (elem[0].previousElementSibling && parseInt(angular.element(elem[0].previousElementSibling).attr('tabindex')) >= 0) {
984 angular.element(elem[0])[0].previousElementSibling.focus();
987 var el1 = angular.element(elem[0])[0];
989 if (el1.previousSibling) {
990 el1 = el1.previousSibling;
994 } while (el1 && el1.tagName !== 'LI');
996 if (el1.tagName && el1.tagName === 'LI' && parseInt(angular.element(el1).attr('tabindex')) >= 0) {
1010 * @name Template.att:Bootstrap Grid Template
1013 * <file src="src/bootstrapGridTemplate/docs/readme.md" />
1016 * <section id="code">
1017 <example module="b2b.att">
1018 <file src="src/bootstrapGridTemplate/docs/demo.html" />
1019 <file src="src/bootstrapGridTemplate/docs/demo.js" />
1024 angular.module('b2b.att.bootstrapGridTemplate', [])
1028 * @name Navigation.att:breadcrumbs
1031 * <file src="src/breadcrumbs/docs/readme.md" />
1033 <ul class="breadcrumb">
1034 <li ng-repeat="link in breadCrumbsLink"><a tabindex="{{(idx==$index)?-1:0}}" href='javascript:void(0)' ng-click="clickActive($index)" ng-class="{'active':idx==$index, '': idx!=$index}">{{link.title}}</a></li>
1037 <example module="b2b.att">
1038 <file src="src/breadcrumbs/docs/demo.html" />
1039 <file src="src/breadcrumbs/docs/demo.js" />
1042 angular.module('b2b.att.breadcrumbs',[])
1045 * @name Buttons, links & UI controls.att:buttonGroups
1048 * <file src="src/buttonGroups/docs/readme.md" />
1051 <h2>Radio Aproach</h2>
1052 <div class="btn-group" b2b-key prev="37,38" next="39,40" circular-traversal role="radiogroup">
1053 <button type="button" class="btn btn-secondary" b2b-key-item ng-focus="radioModel='Button 1'" ng-model="radioModel" b2b-btn-radio="'Button 1'" tabindex="{{(!radioModel || 'Button 1'===radioModel)?0:-1}}">Button 1</button>
1054 <button type="button" class="btn btn-secondary" b2b-key-item ng-focus="radioModel='Button 2'" ng-model="radioModel" b2b-btn-radio="'Button 2'" tabindex="{{(!radioModel || 'Button 2'===radioModel)?0:-1}}">Button 2</button>
1055 <button type="button" class="btn btn-secondary" b2b-key-item ng-focus="radioModel='Button 3'" ng-model="radioModel" b2b-btn-radio="'Button 3'" tabindex="{{(!radioModel || 'Button 3'===radioModel)?0:-1}}">Button 3</button>
1058 <h2>Checkbox Aproach</h2>
1059 <span b2b-button-group class="btn-group btn-fullwidth" role="group" max-select="3" ng-model="checkModel1">
1060 <button type="button" class="btn btn-secondary" ng-model="checkModel1.Button1" b2b-btn-checkbox>Button1</button>
1061 <button type="button" class="btn btn-secondary" ng-model="checkModel1.Button2" b2b-btn-checkbox>Button2</button>
1062 <button type="button" class="btn btn-secondary" ng-model="checkModel1.Button3" b2b-btn-checkbox>Button3</button>
1063 <button type="button" class="btn btn-secondary" ng-model="checkModel1.Button4" b2b-btn-checkbox>Button4</button>
1064 <button type="button" class="btn btn-secondary" ng-model="checkModel1.Button5" b2b-btn-checkbox>Button5</button>
1068 * <section id="code">
1069 <example module="b2b.att">
1070 <file src="src/buttonGroups/docs/demo.html" />
1071 <file src="src/buttonGroups/docs/demo.js" />
1076 angular.module('b2b.att.buttonGroups', ['b2b.att.utilities'])
1077 .constant('buttonConfig', {
1078 activeClass: 'active',
1079 toggleEvent: 'click'
1081 .directive('b2bBtnRadio', ['buttonConfig', function (buttonConfig) {
1082 var activeClass = buttonConfig.activeClass || 'active';
1083 var toggleEvent = buttonConfig.toggleEvent || 'click';
1087 link: function (scope, element, attrs, ngModelCtrl) {
1088 var notMobile = !/Android|webOS|iPhone|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent);
1091 element.bind('focus', function () {
1092 scope.$apply(function () {
1093 ngModelCtrl.$setViewValue(scope.$eval(attrs.b2bBtnRadio));
1094 ngModelCtrl.$render();
1099 element.attr('role', 'radio');
1102 ngModelCtrl.$render = function () {
1103 element.toggleClass(activeClass, angular.equals(ngModelCtrl.$modelValue, scope.$eval(attrs.b2bBtnRadio)));
1104 if (angular.equals(ngModelCtrl.$modelValue, scope.$eval(attrs.b2bBtnRadio))) {
1105 element.attr("aria-checked", true);
1107 element.attr("aria-checked", false);
1112 element.bind(toggleEvent, function () {
1113 if (!element.hasClass(activeClass)) {
1114 scope.$apply(function () {
1115 ngModelCtrl.$setViewValue(scope.$eval(attrs.b2bBtnRadio));
1116 ngModelCtrl.$render();
1123 .directive('b2bBtnCheckbox', ['buttonConfig', function (buttonConfig) {
1124 var activeClass = buttonConfig.activeClass || 'active';
1125 var toggleEvent = buttonConfig.toggleEvent || 'click';
1128 require: ['ngModel', '^^b2bButtonGroup'],
1129 link: function (scope, element, attrs, ctrls) {
1131 var ngModelCtrl = ctrls[0];
1132 var parentCtrl = ctrls[1];
1134 element.attr('role', 'checkbox');
1135 element.attr('aria-describedby', parentCtrl.getStateDescriptionElemId());
1137 function getTrueValue() {
1138 var trueValue = scope.$eval(attrs.b2bBtnCheckboxTrue);
1139 return angular.isDefined(trueValue) ? trueValue : true;
1142 function getFalseValue() {
1143 var falseValue = scope.$eval(attrs.b2bBtnCheckboxFalse);
1144 return angular.isDefined(falseValue) ? falseValue : false;
1148 ngModelCtrl.$render = function () {
1149 element.toggleClass(activeClass, angular.equals(ngModelCtrl.$modelValue, getTrueValue()));
1150 if ((angular.equals(ngModelCtrl.$modelValue, getTrueValue()))) {
1151 element.attr("aria-checked", true);
1153 element.attr("aria-checked", false);
1158 element.bind(toggleEvent, function () {
1159 scope.$apply(function () {
1160 ngModelCtrl.$setViewValue(element.hasClass(activeClass) ? getFalseValue() : getTrueValue());
1161 ngModelCtrl.$render();
1167 .directive('b2bButtonGroup', ['$timeout', '$compile', function ($timeout, $compile) {
1172 ngModelButtonState: '=ngModel'
1174 controller: ['$scope', '$element', function ($scope, $element) {
1177 var stateDescriptionElem = angular.element('<span id="b2b_button_group_' + $scope.$id + '" class="hide" aria-hidden="true">{{nSel}} of {{maxSelect}} options selected.</span>');
1178 $compile(stateDescriptionElem)($scope);
1179 $element.after(stateDescriptionElem);
1181 this.getStateDescriptionElemId = function () {
1182 return stateDescriptionElem.attr('id');
1185 link: function (scope, element) {
1188 var executeFxn = function () {
1190 angular.forEach(scope.ngModelButtonState, function (value, key) {
1191 if (value === true) {
1196 if (scope.nSel >= scope.maxSelect) {
1197 angular.forEach(element.children(), function (chd) {
1198 if (chd.className.indexOf('active') < 0) {
1199 chd.disabled = true;
1200 chd.setAttribute('aria-disabled', true);
1204 angular.forEach(element.children(), function (chd) {
1205 chd.disabled = false;
1206 chd.setAttribute('aria-disabled', false);
1212 $timeout(function () {
1215 element.bind('click', executeFxn);
1221 * @name Buttons, links & UI controls.att:buttons
1226 * <file src="src/buttons/docs/readme.md" />
1230 <button class="btn" type="button">Button</button> button.btn (button shape only)
1231 <button aria-label="Custom aria label" class="btn" type="button">Button</button> button.btn (button shape only) with custom aria label
1232 <button aria-label="Click on button/Press enter" class="btn" type="button" onclick="javascript:alert('It works!');">Click on button/Press enter</button> button.btn with click functionality
1233 <a b2b-keyup-click="32" href="javascript:void(0)" class="btn" role="button">Button</a> a.btn (button shape only)
1234 <button class="btn btn-primary">Button</button> .btn-primary
1235 <a b2b-keyup-click="32" href="javascript:void(0)" class="btn btn-primary" role="button">Button</a> a.btn-primary
1238 <button class="btn btn-secondary">Button</button> .btn-secondary
1239 <a b2b-keyup-click="32" href="javascript:void(0)" class="btn btn-secondary" role="button">Button</a> a.btn-secondary
1240 <button class="btn btn-alt">Button</button> .btn-alt
1241 <a b2b-keyup-click="32" href="javascript:void(0)" class="btn btn-alt" role="button">Button</a> a.btn-alt
1242 <button class="btn btn-specialty">Button</button> .btn-specialty
1243 <a b2b-keyup-click="32" href="javascript:void(0)" class="btn btn-specialty" role="button">Button</a> a.btn-specialty
1244 <button class="btn btn-specialty" disabled="">Button</button> disabled="disabled"
1245 <a b2b-keyup-click="32" aria-disabled="true" href="javascript:void(0)" class="btn btn-primary disabled" role="button">Button</a> a.disabled
1248 <button class="btn btn-secondary">Button</button> .btn is default and 46px height
1249 <button class="btn btn-secondary btn-medium">Button</button> .btn-medium is 42px
1250 <button class="btn btn-secondary btn-small">Button</button> .btn-small is 36px
1252 .row-nowrap 2 up buttons
1253 <div class="row-nowrap">
1254 <button class="btn btn-secondary btn-fullwidth" type="button">Cancel</button>
1255 <button class="btn btn-primary btn-fullwidth" type="button">Continue</button>
1258 .row 2 up buttons (desktop) stacked (mobile) (different order)
1259 <div class="row cta-button-group">
1260 <button class="span btn btn-secondary btn-fullwidth hidden-phone" type="button">Cancel</button>
1261 <button class="span btn btn-primary btn-fullwidth" type="button">Continue</button>
1262 <button class="span btn btn-secondary btn-fullwidth visible-phone" type="button">Cancel</button>
1266 * <section id="code">
1267 <b>HTML + AngularJS</b>
1268 * <example module="b2b.att">
1269 * <file src="src/buttons/docs/demo.html" />
1270 <file src="src/buttons/docs/demo.js" />
1275 angular.module('b2b.att.buttons', ['b2b.att.utilities']);
1278 * @name Forms.att:calendar
1281 * <file src="src/calendar/docs/readme.md" />
1283 * <input type="text" ng-model="dt" b2b-datepicker>
1287 <b>HTML + AngularJS</b>
1288 <example module="b2b.att">
1289 <file src="src/calendar/docs/demo.html" />
1290 <file src="src/calendar/docs/demo.js" />
1294 angular.module('b2b.att.calendar', ['b2b.att.position', 'b2b.att.utilities'])
1296 .constant('b2bDatepickerConfig', {
1297 dateFormat: 'MM/dd/yyyy',
1299 monthFormat: 'MMMM',
1301 dayHeaderFormat: 'EEEE',
1302 dayTitleFormat: 'MMMM yyyy',
1303 disableWeekend: false,
1304 disableSunday: false,
1306 onSelectClose: null,
1313 legendMessage: null,
1314 calendarDisabled: false,
1316 orientation: 'right',
1318 helperText: 'The date you selected is $date. In case of mobile double tap to open calendar. Select a date to close the calendar.',
1319 datepickerEvalAttributes: ['dateFormat', 'dayFormat', 'monthFormat', 'yearFormat', 'dayHeaderFormat', 'dayTitleFormat', 'disableWeekend', 'disableSunday', 'startingDay', 'collapseWait', 'orientation'],
1320 datepickerWatchAttributes: ['min', 'max', 'due', 'from', 'legendIcon', 'legendMessage', 'ngDisabled'],
1321 datepickerFunctionAttributes: ['disableDates', 'onSelectClose']
1324 .factory('b2bDatepickerService', ['b2bDatepickerConfig', 'dateFilter', function (b2bDatepickerConfig, dateFilter) {
1325 var setAttributes = function (attr, elem) {
1326 if (angular.isDefined(attr) && attr !== null && angular.isDefined(elem) && elem !== null) {
1327 var attributes = b2bDatepickerConfig.datepickerEvalAttributes.concat(b2bDatepickerConfig.datepickerWatchAttributes, b2bDatepickerConfig.datepickerFunctionAttributes);
1328 for (var key in attr) {
1329 var val = attr[key];
1330 if (attributes.indexOf(key) !== -1 && angular.isDefined(val)) {
1331 elem.attr(key.toSnakeCase(), key);
1337 var bindScope = function (attr, scope) {
1338 if (angular.isDefined(attr) && attr !== null && angular.isDefined(scope) && scope !== null) {
1339 var evalFunction = function (key, val) {
1340 scope[key] = scope.$parent.$eval(val);
1343 var watchFunction = function (key, val) {
1344 scope.$parent.$watch(val, function (value) {
1347 scope.$watch(key, function (value) {
1348 scope.$parent[val] = value;
1352 var evalAttributes = b2bDatepickerConfig.datepickerEvalAttributes;
1353 var watchAttributes = b2bDatepickerConfig.datepickerWatchAttributes;
1354 for (var key in attr) {
1355 var val = attr[key];
1356 if (evalAttributes.indexOf(key) !== -1 && angular.isDefined(val)) {
1357 evalFunction(key, val);
1358 } else if (watchAttributes.indexOf(key) !== -1 && angular.isDefined(val)) {
1359 watchFunction(key, val);
1366 setAttributes: setAttributes,
1367 bindScope: bindScope
1371 .controller('b2bDatepickerController', ['$scope', '$attrs', 'dateFilter', '$element', '$position', 'b2bDatepickerConfig', function ($scope, $attrs, dateFilter, $element, $position, dtConfig) {
1373 date: getValue($attrs.dateFormat, dtConfig.dateFormat),
1374 day: getValue($attrs.dayFormat, dtConfig.dayFormat),
1375 month: getValue($attrs.monthFormat, dtConfig.monthFormat),
1376 year: getValue($attrs.yearFormat, dtConfig.yearFormat),
1377 dayHeader: getValue($attrs.dayHeaderFormat, dtConfig.dayHeaderFormat),
1378 dayTitle: getValue($attrs.dayTitleFormat, dtConfig.dayTitleFormat),
1379 disableWeekend: getValue($attrs.disableWeekend, dtConfig.disableWeekend),
1380 disableSunday: getValue($attrs.disableSunday, dtConfig.disableSunday)
1382 startingDay = getValue($attrs.startingDay, dtConfig.startingDay);
1384 if($attrs.disableDates !== undefined) {
1385 format.disableDates = $attrs.disableDates;
1387 format.disableDates = dtConfig.disableDates;
1389 $scope.minDate = dtConfig.minDate ? $scope.resetTime(dtConfig.minDate) : null;
1390 $scope.maxDate = dtConfig.maxDate ? $scope.resetTime(dtConfig.maxDate) : null;
1391 $scope.dueDate = dtConfig.dueDate ? $scope.resetTime(dtConfig.dueDate) : null;
1392 $scope.fromDate = dtConfig.fromDate ? $scope.resetTime(dtConfig.fromDate) : null;
1393 $scope.legendIcon = dtConfig.legendIcon ? dtConfig.legendIcon : null;
1394 $scope.legendMessage = dtConfig.legendMessage ? dtConfig.legendMessage : null;
1395 $scope.ngDisabled = dtConfig.calendarDisabled ? dtConfig.calendarDisabled : null;
1396 $scope.collapseWait = getValue($attrs.collapseWait, dtConfig.collapseWait);
1397 $scope.orientation = getValue($attrs.orientation, dtConfig.orientation);
1398 $scope.onSelectClose = getValue($attrs.onSelectClose, dtConfig.onSelectClose);
1400 $scope.inline = $attrs.inline === 'true' ? true : dtConfig.inline;
1402 function getValue(value, defaultValue) {
1403 return angular.isDefined(value) ? $scope.$parent.$eval(value) : defaultValue;
1406 function getDaysInMonth(year, month) {
1407 return new Date(year, month, 0).getDate();
1410 function getDates(startDate, n) {
1411 var dates = new Array(n);
1412 var current = startDate,
1415 dates[i++] = new Date(current);
1416 current.setDate(current.getDate() + 1);
1421 this.updatePosition = function (b2bDatepickerPopupTemplate) {
1422 $scope.position = $position.offset($element);
1423 $scope.position.top = $scope.position.top + $element.prop('offsetHeight');
1424 $scope.position.left = $scope.position.left - (((b2bDatepickerPopupTemplate && b2bDatepickerPopupTemplate.prop('offsetWidth')) || 290) - $element.prop('offsetWidth'));
1427 this.isDateInRange = function(date) {
1428 if ((compare(date, $scope.minDate) >= 0) && (compare(date, $scope.maxDate) <= 0)) {
1436 this.isDisbaledDate = function(date) {
1437 if ($attrs.from && !angular.isDate($scope.fromDate)) {
1440 if (format.disableWeekend === true && (dateFilter(date, format.dayHeader) === "Saturday" || dateFilter(date, format.dayHeader) === "Sunday")) {
1443 if (format.disableSunday === true && (dateFilter(date, format.dayHeader) === "Sunday")) {
1447 return (($scope.minDate && compare(date, $scope.minDate) < 0) || ($scope.maxDate && compare(date, $scope.maxDate) > 0) || ($scope.datesCallBack({
1452 function isSelected(dt) {
1453 if (dt && angular.isDate($scope.currentDate) && compare(dt, $scope.currentDate) === 0) {
1459 function isFromDate(dt) {
1460 if (dt && angular.isDate($scope.fromDate) && compare(dt, $scope.fromDate) === 0) {
1466 function isDateRange(dt) {
1467 if (dt && $scope.fromDate && angular.isDate($scope.currentDate) && (compare(dt, $scope.fromDate) >= 0) && (compare(dt, $scope.currentDate) <= 0)) {
1469 } else if (dt && $scope.fromDate && compare(dt, $scope.fromDate) === 0) {
1475 function isOld(date, currentMonthDate) {
1476 if (date && currentMonthDate && (new Date(date.getFullYear(), date.getMonth(), 1, 0, 0, 0).getTime() < new Date(currentMonthDate.getFullYear(), currentMonthDate.getMonth(), 1, 0, 0, 0).getTime())) {
1483 function isNew(date, currentMonthDate) {
1484 if (date && currentMonthDate && (new Date(date.getFullYear(), date.getMonth(), 1, 0, 0, 0).getTime() > new Date(currentMonthDate.getFullYear(), currentMonthDate.getMonth(), 1, 0, 0, 0).getTime())) {
1491 function isPastDue(dt) {
1492 if ($scope.dueDate) {
1493 return (dt > $scope.dueDate);
1498 function isDueDate(dt) {
1499 if ($scope.dueDate) {
1500 return (dt.getTime() === $scope.dueDate.getTime());
1505 var isDisabled = function (date, currentMonthDate) {
1506 if ($attrs.from && !angular.isDate($scope.fromDate)) {
1509 if (format.disableWeekend === true && (dateFilter(date, format.dayHeader) === "Saturday" || dateFilter(date, format.dayHeader) === "Sunday")) {
1512 if (format.disableSunday === true && (dateFilter(date, format.dayHeader) === "Sunday")) {
1515 if (isOld(date, currentMonthDate) || isNew(date, currentMonthDate)) {
1518 return (($scope.minDate && compare(date, $scope.minDate) < 0) || ($scope.maxDate && compare(date, $scope.maxDate) > 0) || ($scope.datesCallBack({
1523 var compare = function (date1, date2) {
1524 return (new Date(date1.getFullYear(), date1.getMonth(), date1.getDate()) - new Date(date2.getFullYear(), date2.getMonth(), date2.getDate()));
1527 function isMinDateAvailable(startDate, endDate) {
1528 if (($scope.minDate && $scope.minDate.getTime() >= startDate.getTime()) && ($scope.minDate.getTime() <= endDate.getTime())) {
1529 $scope.disablePrev = true;
1530 $scope.visibilityPrev = "hidden";
1532 $scope.disablePrev = false;
1533 $scope.visibilityPrev = "visible";
1537 function isMaxDateAvailable(startDate, endDate) {
1538 if (($scope.maxDate && $scope.maxDate.getTime() >= startDate.getTime()) && ($scope.maxDate.getTime() <= endDate.getTime())) {
1539 $scope.disableNext = true;
1540 $scope.visibilityNext = "hidden";
1542 $scope.disableNext = false;
1543 $scope.visibilityNext = "visible";
1547 function getLabel(label) {
1550 pre: label.substr(0, 1).toUpperCase(),
1558 function makeDate(date, dayFormat, dayHeaderFormat, isSelected, isFromDate, isDateRange, isOld, isNew, isDisabled, dueDate, pastDue) {
1561 label: dateFilter(date, dayFormat),
1562 header: dateFilter(date, dayHeaderFormat),
1563 selected: !!isSelected,
1564 fromDate: !!isFromDate,
1565 dateRange: !!isDateRange,
1568 disabled: !!isDisabled,
1571 focusable: !((isDisabled && !(isSelected || isDateRange)) || (isOld || isNew))
1578 getVisibleDates: function (date) {
1579 var year = date.getFullYear(),
1580 month = date.getMonth(),
1581 firstDayOfMonth = new Date(year, month, 1),
1582 lastDayOfMonth = new Date(year, month + 1, 0);
1583 var difference = startingDay - firstDayOfMonth.getDay(),
1584 numDisplayedFromPreviousMonth = (difference > 0) ? 7 - difference : -difference,
1585 firstDate = new Date(firstDayOfMonth),
1588 if (numDisplayedFromPreviousMonth > 0) {
1589 firstDate.setDate(-numDisplayedFromPreviousMonth + 1);
1590 numDates += numDisplayedFromPreviousMonth; // Previous
1592 numDates += getDaysInMonth(year, month + 1); // Current
1593 numDates += (7 - numDates % 7) % 7; // Next
1595 var days = getDates(firstDate, numDates),
1596 labels = new Array(7);
1597 for (var i = 0; i < numDates; i++) {
1598 var dt = new Date(days[i]);
1599 days[i] = makeDate(dt,
1607 isDisabled(dt, date),
1611 for (var j = 0; j < 7; j++) {
1612 labels[j] = getLabel(dateFilter(days[j].date, format.dayHeader));
1614 isMinDateAvailable(firstDayOfMonth, lastDayOfMonth);
1615 isMaxDateAvailable(firstDayOfMonth, lastDayOfMonth);
1618 title: dateFilter(date, format.dayTitle),
1630 .directive('b2bDatepicker', ['$parse', '$log', '$timeout', '$document', '$documentBind', '$isElement', '$templateCache', '$compile', 'trapFocusInElement', '$position', '$window', '$filter', 'b2bDatepickerConfig', function ($parse, $log, $timeout, $document, $documentBind, $isElement, $templateCache, $compile, trapFocusInElement, $position, $window, $filter, b2bDatepickerConfig) {
1635 datesCallBack: '&disableDates',
1637 disabledInput: '=?ngDisabled'
1639 require: ['b2bDatepicker', 'ngModel', '?^b2bDatepickerGroup'],
1640 controller: 'b2bDatepickerController',
1641 link: function (scope, element, attrs, ctrls) {
1642 var datepickerCtrl = ctrls[0],
1644 b2bDatepickerGroupCtrl = ctrls[2];
1645 var b2bDatepickerPopupTemplate;
1646 var isCalendarOpened = false;
1647 if(scope.disabledInput === undefined || scope.disabledInput === '') {
1648 scope.disabledInput = false;
1650 if(attrs.inline == 'true'){
1651 element.after($compile($templateCache.get('b2bTemplate/calendar/datepicker-popup.html'))(scope));
1652 var temp = element.after();
1656 var buttonTabIndex = scope.disabledInput===true ? -1 : 0;
1658 element.after($compile('<button class="btn-calendar-icon" ng-disabled='+scope.disabledInput+' ><i class="icon-primary-calendar b2b-calendar-icon" aria-haspopup="true" aria-expanded="false" ng-class=\"{\'disabled\': '+scope.disabledInput+'}\" ></i></button>')(scope));
1659 element.attr('placeholder', 'MM/dd/yyyy');
1660 element.attr('b2b-format-date', b2bDatepickerConfig.dateFormat);
1662 scope.$watch('model', function(val) {
1664 if(val !== undefined && val !== '') {
1665 var date_regex = /^(0[1-9]|1[0-2])\/(0[1-9]|1\d|2\d|3[01])\/(19|20)\d{2}$/ ;
1667 if(!date_regex.test(element[0].value)) {
1668 ngModel.$setValidity('datePattern', false);
1670 ngModel.$setValidity('datePattern', true);
1674 ngModel.$setValidity('datePattern', true);
1680 $log.error("ng-model is required.");
1681 return; // do nothing if no ng-model
1684 if(scope.model !== undefined && scope.model !== '') {
1685 element[0].value = $filter('date')(scope.model, "MM/dd/yyyy");
1688 // Configuration parameters
1691 scope.isOpen = false;
1692 var isValidDate = false;
1697 if (b2bDatepickerGroupCtrl) {
1698 b2bDatepickerGroupCtrl.registerDatepickerScope(scope);
1701 var calendarButton = angular.element(element[0].nextElementSibling);
1703 calendarButton.bind('click',function(){
1704 openCalendarPopup = false;
1705 if (!scope.ngDisabled) {
1706 scope.isOpen = !scope.isOpen;
1707 toggleCalendar(scope.isOpen);
1709 datepickerCtrl.updatePosition(b2bDatepickerPopupTemplate);
1710 $timeout(function () {
1711 // angular.element(element[0].querySelector('.datepicker-input')).scrollTop=0;
1715 var openCalendarPopup = false;
1717 element.bind('blur', function() {
1718 if(scope.model !== undefined && scope.model !== '') {
1719 var dateEntered = scope.model;
1721 var date_regex = /^(0[1-9]|1[0-2])\/(0[1-9]|1\d|2\d|3[01])\/(19|20)\d{2}$/ ;
1723 if(date_regex.test(dateEntered)) {
1724 var parts = dateEntered.split('/');
1725 var enteredDate = new Date(parts[2],parts[0]-1,parts[1]);
1727 if(datepickerCtrl.isDateInRange(enteredDate)) {
1729 ngModel.$setValidity('outOfRange', true);
1730 $timeout(function(){
1731 ngModel.$setValidity('outOfRange', true);
1734 if(!datepickerCtrl.isDisbaledDate(enteredDate)) {
1735 $timeout(function(){
1736 ngModel.$setValidity('disabledDate', true);
1738 scope.select(enteredDate);
1739 openCalendarPopup = true;
1741 $timeout(function(){
1742 ngModel.$setValidity('disabledDate', false);
1744 isValidDate = false;
1745 openCalendarPopup = false;
1750 $timeout(function(){
1751 ngModel.$setValidity('outOfRange', false);
1753 isValidDate = false;
1754 openCalendarPopup = false;
1761 var toggleCalendar = function (flag) {
1762 if (!scope.inline) {
1764 b2bDatepickerPopupTemplate = angular.element($templateCache.get('b2bTemplate/calendar/datepicker-popup.html'));
1765 b2bDatepickerPopupTemplate = $compile(b2bDatepickerPopupTemplate)(scope);
1766 $document.find('body').append(b2bDatepickerPopupTemplate);
1767 b2bDatepickerPopupTemplate.bind('keydown', keyPress);
1768 $timeout(function () {
1769 scope.getFocus = true;
1770 trapFocusInElement(flag, b2bDatepickerPopupTemplate);
1772 $timeout(function () {
1773 scope.getFocus = false;
1778 angular.element(document.querySelector('.b2b-calendar-icon')).attr('aria-expanded','true');
1780 if(!openCalendarPopup) {
1781 b2bDatepickerPopupTemplate.unbind('keydown', keyPress);
1782 b2bDatepickerPopupTemplate.remove();
1785 scope.getFocus = false;
1786 angular.element(document.querySelector('.b2b-calendar-icon')).attr('aria-expanded','false');
1787 trapFocusInElement(flag, b2bDatepickerPopupTemplate);
1792 var handleTabEvent = function(){
1793 b2bDatepickerPopupTemplate.find('td').on('keydown', function (e) {
1794 if (e.keyCode == '9') {
1796 if(b2bDatepickerPopupTemplate.find('tr')[0].querySelector('th.next')){
1797 b2bDatepickerPopupTemplate.find('tr')[0].querySelector('th.next').focus();
1799 b2bDatepickerPopupTemplate.find('tr')[0].querySelector('th.datepicker-switch').focus()
1802 if(b2bDatepickerPopupTemplate.find('tr')[0].querySelector('th.prev')){
1803 b2bDatepickerPopupTemplate.find('tr')[0].querySelector('th.prev').focus();
1805 b2bDatepickerPopupTemplate.find('tr')[0].querySelector('th.datepicker-switch').focus()
1810 e.stopPropagation();
1815 var outsideClick = function (e) {
1816 var isElement = $isElement(angular.element(e.target), element, $document);
1817 var isb2bDatepickerPopupTemplate = $isElement(angular.element(e.target), b2bDatepickerPopupTemplate, $document);
1818 if (!(isElement || isb2bDatepickerPopupTemplate)) {
1819 scope.isOpen = false;
1820 toggleCalendar(scope.isOpen);
1825 var keyPress = function (ev) {
1828 ev.keyCode = ev.which;
1829 } else if (ev.charCode) {
1830 ev.keyCode = ev.charCode;
1834 if (ev.keyCode === 27) {
1835 scope.isOpen = false;
1836 toggleCalendar(scope.isOpen);
1837 ev.preventDefault();
1838 ev.stopPropagation();
1839 } else if (ev.keyCode === 33) {
1840 !scope.disablePrev && scope.move(-1);
1841 $timeout(function () {
1842 scope.getFocus = true;
1844 $timeout(function () {
1845 scope.getFocus = false;
1849 ev.preventDefault();
1850 ev.stopPropagation();
1851 } else if (ev.keyCode === 34) {
1852 !scope.disableNext && scope.move(1);
1853 $timeout(function () {
1854 scope.getFocus = true;
1856 $timeout(function () {
1857 scope.getFocus = false;
1861 ev.preventDefault();
1862 ev.stopPropagation();
1868 $documentBind.click('isOpen', outsideClick, scope);
1870 var modalContainer = angular.element(document.querySelector('.modalwrapper'));
1871 var modalBodyContainer = angular.element(document.querySelector('.modal-body'));
1872 if (modalContainer) {
1873 modalContainer.bind('scroll', function () {
1874 if (b2bDatepickerPopupTemplate) {
1875 datepickerCtrl.updatePosition(b2bDatepickerPopupTemplate);
1880 if (modalBodyContainer) {
1881 modalBodyContainer.bind('scroll', function () {
1882 if (b2bDatepickerPopupTemplate) {
1883 datepickerCtrl.updatePosition(b2bDatepickerPopupTemplate);
1884 var datepickerTextfield = $position.offset(element.find('input'));
1885 var modalBodyPosition = $position.offset(modalBodyContainer);
1887 if (((datepickerTextfield.top + datepickerTextfield.height) < modalBodyPosition.top || datepickerTextfield.top > (modalBodyPosition.top + modalBodyPosition.height)) && scope.isOpen) {
1888 scope.isOpen = false;
1889 toggleCalendar(scope.isOpen);
1895 var window = angular.element($window);
1896 window.bind('resize', function () {
1897 if (b2bDatepickerPopupTemplate) {
1898 datepickerCtrl.updatePosition(b2bDatepickerPopupTemplate);
1903 scope.$on('$destroy', function () {
1905 scope.isOpen = false;
1906 toggleCalendar(scope.isOpen);
1910 scope.resetTime = function (date) {
1911 if (typeof date === 'string') {
1912 date = date + 'T12:00:00';
1915 if (!isNaN(new Date(date))) {
1916 dt = new Date(date);
1920 return new Date(dt.getFullYear(), dt.getMonth(), dt.getDate());
1924 scope.$parent.$watch($parse(attrs.min), function (value) {
1925 scope.minDate = value ? scope.resetTime(value) : null;
1930 scope.$parent.$watch($parse(attrs.max), function (value) {
1931 scope.maxDate = value ? scope.resetTime(value) : null;
1936 scope.$parent.$watch($parse(attrs.due), function (value) {
1937 scope.dueDate = value ? scope.resetTime(value) : null;
1942 scope.$parent.$watch($parse(attrs.from), function (value) {
1943 scope.fromDate = value ? scope.resetTime(value) : null;
1948 if (attrs.legendIcon) {
1949 scope.$parent.$watch(attrs.legendIcon, function (value) {
1950 scope.legendIcon = value ? value : null;
1954 if (attrs.legendMessage) {
1955 scope.$parent.$watch(attrs.legendMessage, function (value) {
1956 scope.legendMessage = value ? value : null;
1960 if (attrs.ngDisabled) {
1961 scope.$parent.$watch(attrs.ngDisabled, function (value) {
1962 scope.ngDisabled = value ? value : null;
1966 // Split array into smaller arrays
1967 function split(arr, size) {
1969 while (arr.length > 0) {
1970 arrays.push(arr.splice(0, size));
1975 function refill(date) {
1976 if (angular.isDate(date) && !isNaN(date)) {
1977 selected = new Date(date);
1980 selected = new Date();
1985 var currentMode = datepickerCtrl.modes[mode],
1986 data = currentMode.getVisibleDates(selected);
1987 scope.rows = split(data.objects, currentMode.split);
1989 var startFlag = false;
1990 var firstSelected = false;
1991 for (var i = 0; i < scope.rows.length; i++) {
1992 for (var j = 0; j < scope.rows[i].length; j++) {
1994 if (scope.rows[i][j].label === "1" && !firstSelected) {
1995 firstSelected = true;
1996 var firstDay = scope.rows[i][j];
1999 if (scope.rows[i][j].selected === true) {
2009 firstDay.firstFocus = true;
2012 scope.labels = data.labels || [];
2013 scope.title = data.title;
2015 datepickerCtrl.updatePosition(b2bDatepickerPopupTemplate);
2019 scope.select = function (date) {
2020 var dt = new Date(date.getFullYear(), date.getMonth(), date.getDate());
2021 if (!scope.onSelectClose || (scope.onSelectClose && scope.onSelectClose({
2024 scope.currentDate = dt;
2025 element[0].value = $filter('date')(dt, "MM/dd/yyyy");
2026 ngModel.$setValidity('outOfRange', true);
2027 if (angular.isNumber(scope.collapseWait)) {
2028 $timeout(function () {
2029 scope.isOpen = false;
2030 toggleCalendar(scope.isOpen);
2031 }, scope.collapseWait);
2033 scope.isOpen = false;
2034 toggleCalendar(scope.isOpen);
2039 scope.move = function (direction,$event) {
2040 var step = datepickerCtrl.modes[mode].step;
2041 selected.setDate(1);
2042 selected.setMonth(selected.getMonth() + direction * (step.months || 0));
2043 selected.setFullYear(selected.getFullYear() + direction * (step.years || 0));
2046 $timeout(function () {
2047 trapFocusInElement();
2051 $event.preventDefault();
2052 $event.stopPropagation();
2055 scope.trapFocus = function () {
2056 $timeout(function () {
2057 trapFocusInElement();
2061 scope.$watch('currentDate', function (value) {
2062 if (angular.isDefined(value) && value !== null) {
2067 ngModel.$setViewValue(value);
2070 ngModel.$render = function () {
2071 scope.currentDate = ngModel.$viewValue;
2074 var stringToDate = function (value) {
2075 if (!isNaN(new Date(value))) {
2076 value = new Date(value);
2080 ngModel.$formatters.unshift(stringToDate);
2086 .directive('b2bDatepickerGroup', [function () {
2089 controller: ['$scope', '$element', '$attrs', function (scope, elem, attr) {
2090 this.$$headers = [];
2091 this.$$footers = [];
2092 this.registerDatepickerScope = function (datepickerScope) {
2093 datepickerScope.headers = this.$$headers;
2094 datepickerScope.footers = this.$$footers;
2097 link: function (scope, elem, attr, ctrl) {}
2101 .directive('b2bFormatDate', ['dateFilter', function (dateFilter) {
2105 link: function (scope, elem, attr, ctrl) {
2106 var b2bFormatDate = "";
2107 attr.$observe('b2bFormatDate', function (value) {
2108 b2bFormatDate = value;
2110 var dateToString = function (value) {
2111 if (!isNaN(new Date(value))) {
2112 return dateFilter(new Date(value), b2bFormatDate);
2116 ctrl.$formatters.unshift(dateToString);
2121 .directive('b2bDatepickerHeader', [function () {
2124 require: '^b2bDatepickerGroup',
2128 compile: function (elem, attr, transclude) {
2129 return function link(scope, elem, attr, ctrl) {
2131 ctrl.$$headers.push(transclude(scope, function () {}));
2139 .directive('b2bDatepickerFooter', [function () {
2142 require: '^b2bDatepickerGroup',
2146 compile: function (elem, attr, transclude) {
2147 return function link(scope, elem, attr, ctrl) {
2149 ctrl.$$footers.push(transclude(scope, function () {}));
2158 * @name Forms.att:checkboxes
2161 * <file src="src/checkboxes/docs/readme.md" />
2165 <example module="b2b.att">
2166 <file src="src/checkboxes/docs/demo.html" />
2167 <file src="src/checkboxes/docs/demo.js" />
2170 angular.module('b2b.att.checkboxes', ['b2b.att.utilities'])
2171 .directive('b2bSelectGroup', [function (){
2178 link: function (scope, elem, attr, ctrl) {
2179 elem.bind('change', function () {
2180 var isChecked = elem.prop('checked');
2181 angular.forEach(scope.checkboxes, function (item) {
2182 item.isSelected = isChecked;
2186 scope.$watch('checkboxes', function () {
2188 if(scope.checkboxes === undefined) {
2191 angular.forEach(scope.checkboxes, function (item) {
2192 if (item.isSelected) {
2196 elem.prop('indeterminate', false);
2197 if ( scope.checkboxes !==undefined && setBoxes === scope.checkboxes.length && scope.checkboxes.length > 0) {
2198 ctrl.$setViewValue(true);
2199 elem.removeClass('indeterminate');
2200 } else if (setBoxes === 0) {
2201 ctrl.$setViewValue(false);
2202 elem.removeClass('indeterminate');
2204 ctrl.$setViewValue(false);
2205 elem.addClass('indeterminate');
2206 elem.prop('indeterminate', true);
2215 * @name Misc.att:coachmark
2218 * <file src="src/coachmark/docs/readme.md" />
2222 <button b2b-coachmark start-coachmark-callback="startCoachmark()" end-coachmark-callback="endCoachmark()" action-coachmark-callback="actionCoachmark(action)" coachmark-index="coachmarkIndex" coachmarks="coachmarkElements" id="coachmark0" class="btn btn-alt">Initiate tour</button>
2226 <b>HTML + AngularJS</b>
2227 <example module="b2b.att">
2228 <file src="src/coachmark/docs/demo.html" />
2229 <file src="src/coachmark/docs/demo.js" />
2234 angular.module('b2b.att.coachmark', ['b2b.att.utilities','b2b.att.position'])
2236 .directive('b2bCoachmark', ['$document', '$compile', '$position', '$timeout', 'b2bViewport', 'keymap', function($document, $compile, $position, $timeout, b2bViewport, keymap) {
2241 coachmarkIndex: '=',
2242 startCoachmarkCallback: '&',
2243 endCoachmarkCallback: '&',
2244 actionCoachmarkCallback: '&'
2246 link: function (scope, element, attrs, ctrl) {
2247 var coachmarkItems = scope.coachmarks;
2248 var body = $document.find('body').eq(0);
2249 var coackmarkJqContainer;
2250 var coackmarkContainer;
2251 var coachMarkElement;
2252 var backdropjqLiteEl;
2253 var coachmarkHighlight;
2254 var initaitedCoachmark = false;
2255 scope.coackmarkElPos ={
2260 scope.currentCoachmark = {};
2263 var coachmarkBackdrop = function(){
2264 backdropjqLiteEl = angular.element('<div class="b2b-modal-backdrop fade in hidden-by-modal"></div>');
2265 body.append(backdropjqLiteEl);
2267 backdropjqLiteEl.bind('click', function() {
2268 scope.closeCoachmark();
2274 scope.closeButtonFocus = function(){
2275 if(document.getElementsByClassName('b2b-coachmark-header').length >0){
2276 document.getElementsByClassName('b2b-coachmark-header')[0].scrollLeft = 0;
2277 document.getElementsByClassName('b2b-coachmark-header')[0].scrollTop = 0;
2281 scope.actionCoachmark = function(action){
2282 scope.actionCoachmarkCallback({
2287 scope.closeCoachmark = function(){
2288 initaitedCoachmark = false;
2289 backdropjqLiteEl.remove();
2290 coackmarkContainer.remove();
2291 coachmarkHighlight.remove();
2292 if(coachMarkElement !== undefined && coachMarkElement !==""){
2293 coachMarkElement.removeClass('b2b-coachmark-label')
2295 if (angular.isFunction(scope.endCoachmarkCallback)){
2296 scope.endCoachmarkCallback();
2301 var realStyle = function(_elem, _style) {
2303 if ( typeof _elem.currentStyle != 'undefined' ) {
2304 computedStyle = _elem.currentStyle;
2306 computedStyle = document.defaultView.getComputedStyle(_elem, null);
2309 return _style ? computedStyle[_style] : computedStyle;
2312 var copyComputedStyle = function(src, dest) {
2313 var s = realStyle(src);
2314 for ( var i in s ) {
2315 // Do not use `hasOwnProperty`, nothing will get copied
2316 if ( typeof i == "string" && i != "cssText" && !/\d/.test(i) && i.indexOf('webkit') !== 0 ) {
2317 // The try is for setter only properties
2319 dest.style[i] = s[i];
2320 // `fontSize` comes before `font` If `font` is empty, `fontSize` gets
2321 // overwritten. So make sure to reset this property. (hackyhackhack)
2322 // Other properties may need similar treatment
2323 if ( i == "font" ) {
2324 dest.style.fontSize = s.fontSize;
2331 function showCoachmark(targetElement) {
2333 scope.currentCoachmark = targetElement;
2334 if(coachMarkElement !== undefined && coachMarkElement !==""){
2335 coachMarkElement.removeClass('b2b-coachmark-label')
2336 coackmarkContainer.remove();
2337 coachmarkHighlight.remove();
2339 coachMarkElement = angular.element(document.querySelector(targetElement.elementId));
2341 var elementPosition = $position.offset(coachMarkElement);
2343 coachmarkHighlight = angular.element('<div class="b2b-coachmark-highlight"></div><div class="b2b-coachmark-highlight b2b-coachmark-highlight-mask"></div>');
2344 coachmarkHighlight.css({
2345 'width': (elementPosition.width + 25) +'px',
2346 'top': (elementPosition.top -10) + 'px',
2347 'left': (elementPosition.left - 10) + 'px',
2348 'height': (elementPosition.height + 20) +'px'
2350 if(targetElement.cloneHtml){
2351 var copy = coachMarkElement[0].cloneNode(true);
2352 copy.id = "b2b-unique-"+targetElement.elementId.slice(1);
2353 copyComputedStyle(coachMarkElement[0],copy);
2354 var copychildNodes = copy.childNodes;
2355 var coachmarkChildNodes = coachMarkElement[0].childNodes;
2356 for(i=0;i<copychildNodes.length;i++){
2357 if(copychildNodes[i].nodeType === '3'){
2358 copyComputedStyle(coachmarkChildNodes[i],copychildNodes[i])
2361 coachmarkHighlight[0].appendChild(copy); // IE11 only supports appendChild, not append
2363 coachMarkElement.addClass('b2b-coachmark-label');
2366 body.append(coachmarkHighlight);
2368 scope.coackmarkElPos.top = (elementPosition.top + elementPosition.height + 32) + 'px';
2369 scope.coackmarkElPos.left = (elementPosition.left - 158 + elementPosition.width / 2 ) + 'px';
2370 coackmarkJqContainer = angular.element('<div b2b-coachmark-container b2b-trap-focus-inside-element="true"></div>');
2371 coackmarkContainer = $compile(coackmarkJqContainer)(scope);
2372 body.append(coackmarkContainer);
2375 $timeout(function () {
2376 var currentCoachmarkContainer = document.getElementsByClassName('b2b-coachmark-container')[0];
2377 currentCoachmarkContainer.focus();
2379 newElem = angular.element(currentCoachmarkContainer);
2380 newElem.bind('keydown', function (e) {
2381 if(e.keyCode == keymap.KEY.TAB){
2383 if(e.target.className === 'b2b-coachmark-container'){
2385 e.stopPropagation();
2390 var coachmarkHeight = window.getComputedStyle(currentCoachmarkContainer).height.split('px')[0];
2391 var newOffsetHeight = Math.round(elementPosition.top) - elementPosition.height;
2393 // We need a slight offset to show the lightboxed item
2394 if(!targetElement.cloneHtml){
2395 TweenLite.to(window, 2, {scrollTo:{x: (scope.coackmarkElPos.left.split('px')[0] - 100), y: newOffsetHeight-200}});
2400 element.bind('click', function (e) {
2401 initaitedCoachmark = true;
2403 scope.$watch('coachmarkIndex', function () {
2404 if(initaitedCoachmark === true){
2405 if(scope.coachmarkIndex === -1){
2406 scope.closeCoachmark();
2408 findAvailableCoachmark(scope.coachmarkIndex);
2409 showCoachmark(scope.coachmarks[scope.coachmarkIndex]);
2413 coachmarkBackdrop();
2414 var findAvailableCoachmark = function(index){
2416 scope.coachmarkIndex = 0;
2417 } else if(!angular.isDefined(scope.coachmarks[index]) || angular.element(document.querySelector(scope.coachmarks[index].elementId)).length === 0){
2418 findAvailableCoachmark(index-1);
2420 scope.coachmarkIndex = index;
2423 if (angular.isFunction(scope.startCoachmarkCallback)){
2424 scope.startCoachmarkCallback();
2426 findAvailableCoachmark(scope.coachmarkIndex);
2427 showCoachmark(scope.coachmarks[scope.coachmarkIndex]);
2429 $document.bind('keydown', function (evt) {
2430 if (evt.which === 27 && initaitedCoachmark) {
2431 scope.closeCoachmark();
2436 //performance technique to ensure scroll event doesn't cause lag
2437 var throttle = function(type, name, obj) {
2438 obj = obj || window;
2439 var running = false;
2440 var func = function() {
2441 if (running) { return; }
2443 requestAnimationFrame(function() {
2444 obj.dispatchEvent(new CustomEvent(name));
2448 obj.addEventListener(type, func);
2451 scope.viewportWidth = b2bViewport.viewportWidth();
2452 /* init - you can init any event */
2453 throttle("resize", "optimizedResize");
2454 window.addEventListener("optimizedResize", function() {
2455 if(initaitedCoachmark){
2456 showCoachmark(scope.coachmarks[scope.coachmarkIndex]);
2457 scope.viewportWidth = b2bViewport.viewportWidth();
2464 .directive('b2bCoachmarkContainer', ['$document', '$position', function($document, $position) {
2469 templateUrl: 'b2bTemplate/coachmark/coachmark.html',
2470 link: function (scope, element, attrs, ctrl) {
2479 * @name Template.att:Configuration Section
2482 * <file src="src/configurationSection/docs/readme.md" />
2485 * <section id="code">
2486 <b>HTML + AngularJS</b>
2487 <example module="b2b.att">
2488 <file src="src/configurationSection/docs/demo.html" />
2489 <file src="src/configurationSection/docs/demo.js" />
2494 angular.module('b2b.att.configurationSection', [])
2498 * @name Template.att:Directory Listing
2501 * <file src="src/directoryListingTemplate/docs/readme.md" />
2504 * <section id="code">
2505 <b>HTML + AngularJS</b>
2506 <example module="b2b.att">
2507 <file src="src/directoryListingTemplate/docs/demo.html" />
2508 <file src="src/directoryListingTemplate/docs/demo.js" />
2513 angular.module('b2b.att.directoryListingTemplate', [])
2517 * @name Forms.att:dropdowns
2520 * <file src="src/dropdowns/docs/readme.md" />
2525 <example module="b2b.att">
2526 <file src="src/dropdowns/docs/demo.html" />
2527 <file src="src/dropdowns/docs/demo.js" />
2531 angular.module('b2b.att.dropdowns', ['b2b.att.utilities', 'b2b.att.position', 'ngSanitize'])
2533 .constant('b2bDropdownConfig', {
2536 menuKeyword: 'menu',
2537 linkMenuKeyword: 'link-menu',
2538 largeKeyword: 'large',
2539 smallKeyword: 'small'
2542 .directive("b2bDropdown", ['$timeout', '$compile', '$templateCache', 'b2bUserAgent', 'b2bDropdownConfig', '$position', function ($timeout, $compile, $templateCache, b2bUserAgent, b2bDropdownConfig, $position) {
2547 controller: ['$scope', '$element', '$attrs', function (scope, elem, attr) {
2548 scope.isInputDropdown = true;
2549 scope.placeHoldertext = attr.placeholderText;
2551 if (attr.type.indexOf(b2bDropdownConfig.menuKeyword) > -1 || attr.type.indexOf(b2bDropdownConfig.linkMenuKeyword) > -1) {
2552 scope.isInputDropdown = false;
2553 if (attr.type.indexOf(b2bDropdownConfig.linkMenuKeyword) > -1) {
2554 scope.dropdownType = b2bDropdownConfig.linkMenuKeyword;
2555 } else if (attr.type.indexOf(b2bDropdownConfig.menuKeyword) > -1) {
2556 scope.dropdownType = b2bDropdownConfig.menuKeyword;
2559 if (attr.type.indexOf(b2bDropdownConfig.largeKeyword) > -1) {
2560 scope.dropdownSize = b2bDropdownConfig.largeKeyword;
2561 } else if (attr.type.indexOf(b2bDropdownConfig.smallKeyword) > -1) {
2562 scope.dropdownSize = b2bDropdownConfig.smallKeyword;
2566 scope.labelText = attr.labelText;
2568 scope.setBlur = function () {
2572 if ((scope.isInputDropdown && b2bUserAgent.notMobile()) || (!scope.isInputDropdown)) {
2573 var formCtrl = elem.controller('form');
2574 scope.setNgModelController = function (name, ngModelCtrl) {
2575 if (name && formCtrl && ngModelCtrl) {
2576 formCtrl[name] = ngModelCtrl;
2579 scope.setOptionalCta = function (optionalCta) {
2580 scope.optionalCta = optionalCta;
2582 var innerHtml = angular.element('<div></div>').append(elem.html());
2583 innerHtml = ($compile(innerHtml)(scope)).html();
2584 var template = angular.element($templateCache.get('b2bTemplate/dropdowns/b2bDropdownDesktop.html'));
2585 template.find('ul').eq(0).append(innerHtml);
2586 template = $compile(template)(scope);
2587 elem.replaceWith(template);
2588 } else if (scope.isInputDropdown && b2bUserAgent.isMobile()) {
2591 'filter': 'alpha(opacity=0)'
2593 elem.addClass('awd-select isWrapped');
2594 elem.wrap('<span class="selectWrap"></span>');
2595 var cover = angular.element('<span aria-hidden="true"><i class="icon-primary-down" aria-hidden="true"></i></span>');
2596 elem.parent().append(cover);
2597 elem.parent().append('<i class="icon-primary-down" aria-hidden="true"></i>');
2598 var set = function () {
2599 var sel = elem[0] ? elem[0] : elem;
2600 var selectedText = "";
2601 var selIndex = sel.selectedIndex;
2602 if (typeof selIndex !== 'undefined') {
2603 selectedText = sel.options[selIndex].text;
2605 cover.text(selectedText).append('<i class="icon-primary-down" aria-hidden="true"></i>');
2607 var update = function (value) {
2612 scope.$watch(attr.ngModel, function (newVal, oldVal) {
2616 elem.bind('keyup', function (ev) {
2617 if (ev.keyCode === keymap.KEY.TAB || ev.keyCode === keymap.KEY.ESC) {
2625 link: function (scope, elem, attr, ctrl) {
2626 if ((scope.isInputDropdown && b2bUserAgent.notMobile()) || (!scope.isInputDropdown)) {
2627 scope.updateModel = function () {
2628 ctrl.$setViewValue(scope.currentSelected.value);
2629 if (scope.dropdownRequired && scope.currentSelected.value === '') {
2630 scope.setRequired(false);
2632 scope.setRequired(true);
2635 if (scope.dropdownType === b2bDropdownConfig.linkMenuKeyword) {
2636 $timeout(function () {
2637 scope.appendCaretPositionStyle();
2641 ctrl.$render = function () {
2643 $timeout(function () {
2645 if ((angular.isUndefined(ctrl.$viewValue) || ctrl.$viewValue == '') && (angular.isUndefined(scope.placeHoldertext) || scope.placeHoldertext == '')) {
2646 scope.dropdownLists[ctrl.$viewValue] && scope.dropdownLists[ctrl.$viewValue][0].updateDropdownValue();
2647 } else if ((angular.isUndefined(scope.placeHoldertext) || scope.placeHoldertext == '') && ctrl.$viewValue !== '' ) {
2648 scope.dropdownLists[ctrl.$viewValue] && scope.dropdownLists[ctrl.$viewValue][0].updateDropdownValue();
2649 } else if ((angular.isUndefined(ctrl.$viewValue) || ctrl.$viewValue == '') && scope.placeHoldertext !== '' ) {
2650 scope.currentSelected.text = scope.placeHoldertext;
2651 ctrl.$setViewValue(scope.placeHoldertext);
2653 scope.dropdownLists[ctrl.$viewValue] && scope.dropdownLists[ctrl.$viewValue][0].updateDropdownValue();
2659 scope.disabled = false;
2660 scope.dropdownName = attr.name;
2661 scope.dropdownId = attr.id;
2662 scope.labelId = attr.ariaLabelledby;
2663 scope.dropdownDescribedBy = attr.ariaDescribedby;
2664 if (attr.required) {
2665 scope.dropdownRequired = true;
2667 scope.dropdownRequired = false;
2669 elem.removeAttr('name');
2670 elem.removeAttr('id');
2671 scope.$parent.$watch(attr.ngDisabled, function (val) {
2672 scope.disabled = val;
2679 .directive("b2bDropdownToggle", ['$document', '$documentBind', '$isElement', 'b2bDropdownConfig', 'keymap', 'b2bUtilitiesConfig', '$timeout', '$position', function ($document, $documentBind, $isElement, b2bDropdownConfig, keymap, b2bUtilitiesConfig, $timeout, $position) {
2682 require: '?^b2bKey',
2683 link: function (scope, elem, attr, ctrl) {
2684 scope.appendCaretPositionStyle = function () {
2685 while (document.querySelector('style.b2bDropdownCaret')) {
2686 document.querySelector('style.b2bDropdownCaret').remove();
2688 var caretPosition = $position.position(elem).width - 26;
2689 if (scope.dropdownType === b2bDropdownConfig.linkMenuKeyword) {
2690 var template = angular.element('<style class="b2bDropdownCaret" type="text/css">.linkSelectorModule .active+.moduleWrapper:before {left: ' + caretPosition + 'px;}</style>');
2691 $document.find('head').append(template);
2695 if (scope.isInputDropdown && (scope.labelText !== undefined)) {
2696 elem.attr('aria-label', scope.labelText);
2699 scope.toggleFlag = false;
2700 scope.dropdownLists = {};
2701 scope.dropdownListValues = [];
2705 scope.currentSelected = {
2711 scope.dropdownTextList = [];
2712 var searchString = '';
2714 scope.removeItem = function(value){
2715 delete scope.dropdownLists[value];
2716 var index = scope.dropdownListValues.indexOf(value);
2717 scope.dropdownListValues.splice(index,1);
2718 scope.dropdownTextList=[];
2719 scope.dropdown.totalIndex = scope.dropdownListValues.length-1;
2721 var getDropdownText = function(){
2722 var dropdownItems = elem.parent().find('ul').children();
2723 var count = dropdownItems.length;
2724 for(var i=0;i<count;i++){
2725 scope.dropdownTextList.push(dropdownItems.eq(i).text());
2728 var searchElement = function (searchExp) {
2729 if(scope.dropdownTextList.length ==0){
2732 var regex = new RegExp("^" + searchExp, "i");
2733 var position = scope.dropdownTextList.regexIndexOf(regex, scope.currentSelected.index + 1, true);
2734 if (position > -1) {
2739 var startTimer = function (time) {
2740 if (searchString === '') {
2741 $timeout(function () {
2746 scope.toggleDropdown = function (toggleFlag) {
2747 if (!scope.disabled) {
2748 if (angular.isDefined(toggleFlag)) {
2749 scope.toggleFlag = toggleFlag;
2751 scope.toggleFlag = !scope.toggleFlag;
2753 if (!scope.toggleFlag) {
2754 if (scope.isInputDropdown) {
2755 elem.parent().find('input')[0].focus();
2757 elem.parent().find('button')[0].focus();
2760 scope.dropdown.highlightedValue = scope.currentSelected.value;
2761 if (ctrl && ctrl.enableSearch) {
2762 if (angular.isDefined(scope.dropdownLists[scope.currentSelected.value])) {
2763 ctrl.resetCounter(scope.dropdownLists[scope.currentSelected.value][2]);
2766 $timeout(function () {
2767 if(scope.dropdownLists[scope.currentSelected.value] !== undefined){
2768 (scope.dropdownLists[scope.currentSelected.value][1])[0].focus();
2770 if (scope.isInputDropdown) {
2771 elem.parent().find('input')[0].focus();
2773 elem.parent().find('button')[0].focus();
2777 if (scope.dropdownType === b2bDropdownConfig.linkMenuKeyword) {
2778 scope.appendCaretPositionStyle();
2784 elem.bind('keydown', function (ev) {
2787 ev.keyCode = ev.which;
2788 } else if (ev.charCode) {
2789 ev.keyCode = ev.charCode;
2792 if (!scope.toggleFlag) {
2794 var currentIndex = scope.currentSelected.index;
2795 if (ev.keyCode === keymap.KEY.DOWN) {
2796 scope.toggleDropdown(true);
2797 ev.preventDefault();
2798 ev.stopPropagation();
2799 } else if (b2bDropdownConfig.prev.split(',').indexOf(ev.keyCode.toString()) > -1) {
2800 angular.isDefined(scope.dropdownListValues[currentIndex - 1]) ? scope.dropdownLists[scope.dropdownListValues[currentIndex - 1]][0].updateDropdownValue() : angular.noop();
2801 ev.preventDefault();
2802 ev.stopPropagation();
2803 } else if (b2bDropdownConfig.next.split(',').indexOf(ev.keyCode.toString()) > -1) {
2804 angular.isDefined(scope.dropdownListValues[currentIndex + 1]) ? scope.dropdownLists[scope.dropdownListValues[currentIndex + 1]][0].updateDropdownValue() : angular.noop();
2805 ev.preventDefault();
2806 ev.stopPropagation();
2807 } else if (ev.keyCode >= 48 && ev.keyCode <= 105) {
2808 startTimer(b2bUtilitiesConfig.searchTimer);
2809 searchString = searchString + (keymap.MAP[ev.keyCode] || '');
2810 var position = searchElement(searchString);
2811 angular.isDefined(scope.dropdownListValues[position]) ? scope.dropdownLists[scope.dropdownListValues[position]][0].updateDropdownValue() : angular.noop();
2812 ev.preventDefault();
2813 ev.stopPropagation();
2817 if (ev.altKey === true && ev.keyCode === keymap.KEY.UP) {
2818 scope.toggleDropdown(false);
2819 ev.preventDefault();
2820 ev.stopPropagation();
2821 } else if (ev.keyCode === keymap.KEY.TAB || ev.keyCode === keymap.KEY.ESC) {
2822 scope.toggleDropdown(false);
2823 ev.preventDefault();
2824 ev.stopPropagation();
2827 scope.$apply(); // TODO: Move this into each block to avoid expensive digest cycles
2829 var outsideClick = function (e) {
2830 var isElement = $isElement(angular.element(e.target), elem.parent(), $document);
2832 scope.toggleDropdown(false);
2836 $documentBind.click('toggleFlag', outsideClick, scope);
2837 $documentBind.touch('toggleFlag', outsideClick, scope);
2842 .directive("b2bDropdownGroup", ['$compile', '$templateCache', 'b2bUserAgent', function ($compile, $templateCache, b2bUserAgent) {
2845 controller: ['$scope', '$element', '$attrs', function (scope, elem, attr) {
2846 if ((scope.isInputDropdown && b2bUserAgent.notMobile()) || (!scope.isInputDropdown)) {
2847 var innerHtml = angular.element('<div></div>').append(elem.html());
2848 innerHtml = ($compile(innerHtml)(scope)).html();
2849 var template = angular.element($templateCache.get('b2bTemplate/dropdowns/b2bDropdownGroupDesktop.html'));
2850 template.attr('ng-repeat', attr.optGroupRepeat);
2851 template.attr('label', elem.attr('label'));
2852 template.find('ul').append(innerHtml);
2853 elem.replaceWith(template);
2854 } else if (scope.isInputDropdown && b2bUserAgent.isMobile()) {
2855 var template = angular.element(elem.prop('outerHTML'));
2856 template.attr('ng-repeat', attr.optGroupRepeat);
2857 template.removeAttr('b2b-dropdown-group');
2858 template.removeAttr('opt-group-repeat');
2859 template = $compile(template)(scope);
2860 elem.replaceWith(template);
2866 .directive("b2bDropdownGroupDesktop", [function () {
2870 link: function (scope, elem, attr, ctrl) {
2871 scope.groupHeader = attr.label;
2876 .directive("b2bDropdownList", ['$compile', '$templateCache', 'b2bUserAgent', function ($compile, $templateCache, b2bUserAgent) {
2879 controller: ['$scope', '$element', '$attrs', function (scope, elem, attr) {
2880 if ((scope.isInputDropdown && b2bUserAgent.notMobile()) || (!scope.isInputDropdown)) {
2881 var innerHtml = angular.element('<div></div>').append(elem.html());
2882 innerHtml = ($compile(innerHtml)(scope)).html();
2883 var template = angular.element($templateCache.get('b2bTemplate/dropdowns/b2bDropdownListDesktop.html'));
2884 template.attr('ng-repeat', attr.optionRepeat);
2885 template.attr('value', elem.attr('value'));
2886 template.attr('search-key', elem.text());
2887 if (elem.attr('aria-describedby')){
2888 template.attr('aria-describedby', attr.ariaDescribedby);
2890 if (elem.attr('imgsrc')) {
2891 if (elem.attr('imgalt')) {
2892 template.append('<img role="presentation" ng-src="' + elem.attr('imgsrc') + '" alt="' + elem.attr('imgalt') + '"/>');
2894 template.append('<img role="presentation" ng-src="' + elem.attr('imgsrc') + '" alt=""/>');
2897 template.append(innerHtml);
2898 elem.replaceWith(template);
2899 } else if (scope.isInputDropdown && b2bUserAgent.isMobile()) {
2900 var template = angular.element(elem.prop('outerHTML'));
2901 template.attr('ng-repeat', attr.optionRepeat);
2902 if (elem.attr('aria-describedby')){
2903 template.attr('aria-describedby', attr.ariaDescribedby);
2905 template.removeAttr('b2b-dropdown-list');
2906 template.removeAttr('option-repeat');
2907 template = $compile(template)(scope);
2908 elem.replaceWith(template);
2914 .directive("b2bDropdownListDesktop", ['$sce', 'keymap', 'b2bDropdownConfig', function ($sce, keymap, b2bDropdownConfig) {
2919 link: function (scope, elem, attr, ctrl) {
2920 var dropdownListValue = scope.dropdownListValue = attr.value;
2921 scope.dropdown.totalIndex++;
2922 var dropdownListIndex = scope.dropdown.totalIndex;
2923 scope.dropdownListValues.push(dropdownListValue);
2924 scope.dropdownLists[dropdownListValue] = [];
2925 scope.dropdownLists[dropdownListValue][0] = scope;
2926 scope.dropdownLists[dropdownListValue][1] = elem;
2927 scope.dropdownLists[dropdownListValue][2] = dropdownListIndex;
2928 scope.$parent.$parent.dropdownTextList=[];
2929 scope.updateDropdownValue = function () {
2930 scope.currentSelected.value = dropdownListValue;
2931 if (scope.isInputDropdown) {
2932 scope.currentSelected.text = elem.text();
2933 scope.currentSelected.label = elem.text();
2934 } else if ((scope.dropdownType === b2bDropdownConfig.linkMenuKeyword) || (scope.dropdownType === b2bDropdownConfig.menuKeyword && scope.dropdownSize === b2bDropdownConfig.smallKeyword)) {
2935 scope.currentSelected.text = dropdownListValue;
2936 scope.currentSelected.label = dropdownListValue;
2937 } else if (scope.dropdownType === b2bDropdownConfig.menuKeyword) {
2938 scope.currentSelected.text = $sce.trustAsHtml(elem.html());
2939 scope.currentSelected.label = elem.text();
2941 scope.currentSelected.index = dropdownListIndex;
2942 scope.updateModel();
2944 scope.selectDropdownItem = function () {
2946 scope.updateDropdownValue();
2947 scope.toggleDropdown(false);
2949 scope.highlightDropdown = function () {
2950 scope.dropdown.highlightedValue = dropdownListValue;
2952 elem.bind('mouseover', function (ev) {
2956 elem.bind('keydown', function (ev) {
2959 ev.keyCode = ev.which;
2960 } else if (ev.charCode) {
2961 ev.keyCode = ev.charCode;
2964 if (ev.altKey === true && ev.keyCode === keymap.KEY.UP) {
2965 scope.toggleDropdown(false);
2966 ev.preventDefault();
2967 ev.stopPropagation();
2968 } else if (ev.keyCode === keymap.KEY.TAB || ev.keyCode === keymap.KEY.ESC) {
2969 scope.toggleDropdown(false);
2970 ev.preventDefault();
2971 ev.stopPropagation();
2975 scope.$on('$destroy',function(){
2976 scope.removeItem(dropdownListValue);
2982 .directive("b2bDropdownRepeat", ['$compile', 'b2bUserAgent', function ($compile, b2bUserAgent) {
2985 controller: ['$scope', '$element', '$attrs', function (scope, elem, attr) {
2986 if ((scope.isInputDropdown && b2bUserAgent.notMobile()) || (!scope.isInputDropdown)) {
2987 var innerHtml = angular.element('<div></div>').append(elem.html());
2988 innerHtml = ($compile(innerHtml)(scope)).html();
2989 var template = angular.element('<div></div>');
2990 template.attr('ng-repeat', attr.b2bDropdownRepeat);
2991 template.append(innerHtml);
2992 elem.replaceWith(template);
2993 } else if (scope.isInputDropdown && b2bUserAgent.isMobile()) {
3000 .directive("b2bDropdownValidation", ['$timeout', function ($timeout) {
3004 link: function (scope, elem, attr, ctrl) {
3005 $timeout(function () {
3006 scope.setNgModelController(attr.name, ctrl);
3008 scope.setDirty = function () {
3009 if (ctrl.$dirty === false) {
3011 ctrl.$pristine = false;
3014 scope.setTouched = function () {
3015 if (ctrl.$touched === false) {
3016 ctrl.$touched = true;
3017 ctrl.$pristine = false;
3020 scope.setRequired = function (flag) {
3021 ctrl.$setValidity('required', flag);
3027 .directive('b2bDropdownOptionalCta', [function () {
3033 compile: function (elem, attr, transclude) {
3034 return function link(scope, elem, attr, ctrl) {
3035 if (scope.setOptionalCta) {
3036 scope.setOptionalCta(transclude(scope, function () {}));
3045 * @name Forms.att:File Upload
3048 * <file src="src/fileUpload/docs/readme.md" />
3052 <form id="dragDropFile">
3053 <div b2b-file-drop file-model="fileModel" on-drop="triggerFileUpload()" align="center">
3055 <br>To upload a file, drag & drop it here or
3056 <span b2b-file-link file-model="fileModel" on-file-select="triggerFileUpload()" >
3057 click here to select from your computer.
3064 * <section id="code">
3065 <example module="b2b.att">
3066 <file src="src/fileUpload/docs/demo.html" />
3067 <file src="src/fileUpload/docs/demo.js" />
3072 angular.module('b2b.att.fileUpload', ['b2b.att.utilities'])
3073 .directive('b2bFileDrop', [function() {
3080 controller: ['$scope', '$attrs', function($scope, $attrs) {
3081 this.onDrop = $scope.onDrop;
3083 link: function(scope, element) {
3084 element.addClass('b2b-dragdrop');
3088 if (e.originalEvent) {
3089 e.dataTransfer = e.originalEvent.dataTransfer;
3091 e.dataTransfer.dropEffect = 'move';
3092 // allows us to drop
3093 if (e.preventDefault) {
3096 element.addClass('b2b-dragdrop-over');
3103 // allows us to drop
3104 if (e.preventDefault) {
3107 element.addClass('b2b-dragdrop-over');
3114 element.removeClass('b2b-dragdrop-over');
3121 // Stops some browsers from redirecting.
3122 if (e.preventDefault) {
3125 if (e.stopPropagation) {
3126 e.stopPropagation();
3128 if (e.originalEvent) {
3129 e.dataTransfer = e.originalEvent.dataTransfer;
3131 element.removeClass('b2b-dragdrop-over');
3132 if (e.dataTransfer.files && e.dataTransfer.files.length > 0) {
3133 scope.fileModel = e.dataTransfer.files[0];
3135 if (angular.isFunction(scope.onDrop)) {
3145 .directive('b2bFileLink', [function() {
3148 require: '^?b2bFileDrop',
3151 templateUrl: 'b2bTemplate/fileUpload/fileUpload.html',
3156 controller: ['$scope', function($scope) {
3157 this.setFileModel = function(fileModel) {
3158 if ($scope.takeFileModelFromParent) {
3159 $scope.$parent.fileModel = fileModel;
3160 $scope.$parent.$apply();
3162 $scope.fileModel = fileModel;
3166 this.callbackFunction = function() {
3167 if (angular.isFunction($scope.onFileSelect)) {
3168 $scope.onFileSelect();
3173 link: function(scope, element, attr, b2bFileDropCtrl) {
3174 scope.takeFileModelFromParent = false;
3175 if (!(attr.fileModel) && b2bFileDropCtrl) {
3176 scope.takeFileModelFromParent = true;
3178 if (!(attr.onFileSelect) && b2bFileDropCtrl) {
3179 scope.onFileSelect = b2bFileDropCtrl.onDrop;
3184 .directive('b2bFileChange', ['$log', '$rootScope', function($log, $rootScope) {
3187 require: '^b2bFileLink',
3188 link: function(scope, element, attr, b2bFileLinkCtrl) {
3189 element.bind('change', changeFileModel);
3191 function changeFileModel(e) {
3192 if (e.target.files && e.target.files.length > 0) {
3193 b2bFileLinkCtrl.setFileModel(e.target.files[0]);
3194 b2bFileLinkCtrl.callbackFunction();
3196 var strFileName = e.target.value;
3198 var objFSO = new ActiveXObject("Scripting.FileSystemObject");
3199 b2bFileLinkCtrl.setFileModel(objFSO.getFile(strFileName));
3200 b2bFileLinkCtrl.callbackFunction();
3202 var errMsg = "There was an issue uploading " + strFileName + ". Please try again.";
3204 $rootScope.$broadcast('b2b-file-link-failure', errMsg);
3213 * @name Navigation.att:filters
3216 * <file src="src/filters/docs/readme.md" />
3219 * <div b2b-filters></div>
3222 * <section id="code">
3223 <b>HTML + AngularJS</b>
3224 <example module="b2b.att">
3225 <file src="src/filters/docs/demo.html" />
3226 <file src="src/filters/docs/demo.js" />
3231 angular.module('b2b.att.filters', ['b2b.att.utilities', 'b2b.att.multipurposeExpander'])
3232 .filter('filtersSelectedItemsFilter', [function () {
3233 return function (listOfItemsArray) {
3235 if (!listOfItemsArray) {
3236 listOfItemsArray = [];
3239 var returnArray = [];
3241 for (var i = 0; i < listOfItemsArray.length; i++) {
3242 for (var j = 0; j < listOfItemsArray[i].filterTypeItems.length; j++) {
3243 if (listOfItemsArray[i].filterTypeItems[j].selected && !listOfItemsArray[i].filterTypeItems[j].inProgress) {
3244 returnArray.push(listOfItemsArray[i].filterTypeItems[j]);
3254 * @name Messages, modals & alerts.att:flyout
3257 * <file src="src/flyout/docs/readme.md" />
3259 * <section id="code">
3260 <example module="b2b.att">
3261 <file src="src/flyout/docs/demo.html" />
3262 <file src="src/flyout/docs/demo.js" />
3267 angular.module('b2b.att.flyout', ['b2b.att.utilities', 'b2b.att.position'])
3268 .directive('b2bFlyout', ['$timeout', 'b2bDOMHelper', 'keymap', 'events', function ($timeout, b2bDOMHelper, keymap, events) {
3272 templateUrl: 'b2bTemplate/flyout/flyout.html',
3273 controller: ['$scope', '$element', '$attrs', function (scope, elem, attr) {
3274 scope.flyoutOpened = false;
3275 var contentScope = '';
3276 var togglerScope = '';
3277 this.registerContentScope = function (scp) {
3280 this.registerTogglerScope = function (scp) {
3284 this.toggleFlyoutState = function () {
3286 contentScope.toggleFlyout();
3289 this.getTogglerDimensions = function () {
3290 return togglerScope.getTogglerDimensions();
3292 this.setTogglerFocus = function () {
3293 return togglerScope.setTogglerFocus();
3296 this.closeFlyout = function (e) {
3297 contentScope.closeFromChild(e);
3299 this.gotFocus = function () {
3300 contentScope.gotFocus();
3303 this.updateAriaModel = function (val) {
3304 scope.flyoutOpened = val;
3307 var firstTabableElement = undefined,
3308 lastTabableElement = undefined;
3310 var firstTabableElementKeyhandler = function (e) {
3312 e.keyCode = e.which;
3314 if (e.keyCode === keymap.KEY.TAB && e.shiftKey && scope.flyoutOpened) {
3315 contentScope.gotFocus();
3316 events.preventDefault(e);
3317 events.stopPropagation(e);
3321 var lastTabableElementKeyhandler = function (e) {
3323 e.keyCode = e.which;
3325 if (e.keyCode === keymap.KEY.TAB && !e.shiftKey) {
3326 contentScope.gotFocus();
3327 events.preventDefault(e);
3328 events.stopPropagation(e);
3331 this.associateTabEvent = function(){
3332 $timeout(function () {
3333 var element = elem[0].getElementsByClassName('b2b-flyout-container')[0];
3334 firstTabableElement = b2bDOMHelper.firstTabableElement(element);
3335 lastTabableElement = b2bDOMHelper.lastTabableElement(element);
3336 if(angular.isUndefined(firstTabableElement)){
3337 angular.element(element).css('display','block');
3338 firstTabableElement = b2bDOMHelper.firstTabableElement(element);
3339 lastTabableElement = b2bDOMHelper.lastTabableElement(element);
3340 angular.element(element).css('display','none');
3342 angular.element(firstTabableElement).bind('keydown', firstTabableElementKeyhandler);
3343 angular.element(lastTabableElement).bind('keydown', lastTabableElementKeyhandler);
3346 this.updateTabbableElements = function(){
3347 $timeout(function () {
3348 var element = elem[0].getElementsByClassName('b2b-flyout-container')[0];
3349 angular.element(element).css('display','block');
3350 firstTabableElement = b2bDOMHelper.firstTabableElement(element);
3351 lastTabableElement = b2bDOMHelper.lastTabableElement(element);
3352 angular.element(firstTabableElement).bind('keydown', firstTabableElementKeyhandler);
3353 angular.element(lastTabableElement).bind('keydown', lastTabableElementKeyhandler);
3354 angular.element(element).css('display','none');
3357 this.unbindTabbaleEvents = function(){
3358 if(angular.isDefined(firstTabableElement)){
3359 angular.element(firstTabableElement).unbind('keydown', firstTabableElementKeyhandler);
3362 if(angular.isDefined(lastTabableElement)){
3363 angular.element(lastTabableElement).unbind('keydown', lastTabableElementKeyhandler);
3367 link: function (scope, element, attrs, ctrl) {
3372 .directive('b2bFlyoutToggler', [function () {
3375 require: '^b2bFlyout',
3376 link: function (scope, element, attrs, ctrl) {
3377 element.bind('click', function (e) {
3378 ctrl.toggleFlyoutState();
3381 scope.getTogglerDimensions = function () {
3382 return element[0].getBoundingClientRect();
3385 scope.setTogglerFocus = function () {
3389 ctrl.registerTogglerScope(scope);
3393 .directive('b2bFlyoutContent', ['$position', '$timeout', '$documentBind', '$isElement', '$document', function ($position, $timeout, $documentBind, $isElement, $document) {
3398 require: '^b2bFlyout',
3400 horizontalPlacement: '@',
3401 verticalPlacement: '@',
3404 contentUpdated: "=?"
3406 templateUrl: 'b2bTemplate/flyout/flyoutContent.html',
3407 link: function (scope, element, attrs, ctrl) {
3408 var flyoutStyleArray, eachCssProperty, cssPropertyKey, cssPropertyVal, temp;
3409 scope.openFlyout = false;
3410 if (!scope.horizontalPlacement) {
3411 scope.horizontalPlacement = 'center';
3413 if (!scope.verticalPlacement) {
3414 scope.verticalPlacement = 'below';
3417 scope.toggleFlyout = function () {
3419 scope.openFlyout = !scope.openFlyout;
3421 if (scope.openFlyout) {
3423 if (angular.isDefined(scope.flyoutStyle) && scope.flyoutStyle != "") {
3424 flyoutStyleArray = scope.flyoutStyle.split(";");
3425 for (i = 0; i < flyoutStyleArray.length; i++) {
3426 eachCssProperty = flyoutStyleArray[i].split(":");
3427 if (eachCssProperty.length == 2) {
3428 cssPropertyKey = eachCssProperty[0].trim();
3429 cssPropertyVal = eachCssProperty[1].trim();
3430 angular.element(element[0])[0].style[cssPropertyKey] = cssPropertyVal;
3435 angular.element(element[0]).css({
3440 var flyoutIcons = angular.element(document.querySelectorAll(".b2b-flyout-icon"));
3441 angular.forEach(flyoutIcons, function (elm) {
3442 angular.element(elm)[0].blur();
3445 $timeout(function () {
3446 ctrl.setTogglerFocus();
3448 var togglerDimensions = ctrl.getTogglerDimensions();
3449 var flyoutDimensions = element[0].getBoundingClientRect();
3451 switch (scope.horizontalPlacement) {
3453 angular.element(element[0]).css({
3454 'left': ((togglerDimensions.width / 2) - 26) + 'px'
3458 angular.element(element[0]).css({
3459 'right': ((togglerDimensions.width / 2) - 23) + 'px'
3464 var marginLeft = 10-(flyoutDimensions.width)-20;
3465 angular.element(element[0]).css({
3466 'margin-left': marginLeft + 'px'
3470 angular.element(element[0]).css({
3471 'left': ((togglerDimensions.width + 9 )) + 'px'
3476 var marginLeft = (togglerDimensions.width / 2) - (flyoutDimensions.width / 2) - 8;
3477 angular.element(element[0]).css({
3478 'margin-left': marginLeft + 'px'
3482 switch (scope.verticalPlacement) {
3484 angular.element(element[0]).css({
3485 'top': -(flyoutDimensions.height + 13) + 'px'
3489 angular.element(element[0]).css({
3490 'top': -((togglerDimensions.height-13))+ 'px'
3494 angular.element(element[0]).css({
3495 'top': -(flyoutDimensions.height - 23)+ 'px'
3499 angular.element(element[0]).css({
3500 'top': (togglerDimensions.height + 13) + 'px'
3504 angular.element(element[0]).css({
3513 scope.gotFocus = function () {
3514 scope.openFlyout = false;
3516 ctrl.setTogglerFocus();
3520 scope.closeFromChild = function (e) {
3521 scope.openFlyout = false;
3523 ctrl.setTogglerFocus();
3527 scope.hideFlyout = function () {
3528 angular.element(element[0]).css({
3534 scope.closeFlyout = function (e) {
3535 var isElement = $isElement(angular.element(e.target), element, $document);
3536 if ((e.type === "keydown" && e.which === 27) || ((e.type === "click" || e.type==="touchend") && !isElement)) {
3537 scope.openFlyout = false;
3539 ctrl.setTogglerFocus();
3544 scope.$watch('openFlyout', function () {
3545 ctrl.updateAriaModel(scope.openFlyout);
3548 $documentBind.click('openFlyout', scope.closeFlyout, scope);
3549 $documentBind.event('keydown', 'openFlyout', scope.closeFlyout, scope);
3550 $documentBind.event('touchend', 'openFlyout', scope.closeFlyout, scope);
3551 ctrl.registerContentScope(scope);
3553 if (angular.isDefined(scope.contentUpdated) && scope.contentUpdated !== null) {
3554 scope.$watch('contentUpdated', function (newVal, oldVal) {
3556 if (newVal !== oldVal) {
3557 ctrl.unbindTabbaleEvents();
3558 ctrl.associateTabEvent();
3560 scope.contentUpdated = false;
3568 .directive('b2bCloseFlyout', [function () {
3571 require: '^b2bFlyout',
3575 link: function (scope, element, attrs, ctrl) {
3576 element.bind('click touchstart', function (e) {
3577 scope.closeFlyout(e);
3578 ctrl.closeFlyout(e);
3583 .directive('b2bFlyoutTrapFocusInside', [function () {
3587 require: '^b2bFlyout',
3588 link: function (scope, elem, attr, ctrl) {
3589 /* Before opening modal, find the focused element */
3590 ctrl.updateTabbableElements();
3596 * @name Layouts.att:footer
3599 * <file src="src/footer/docs/readme.md" />
3603 <footer class="b2b-footer-wrapper" role="contentinfo" aria-label="footer">
3604 <div class="b2b-footer-container" b2b-column-switch-footer footer-link-items='footerItems'>
3606 <div class="divider-bottom-footer">
3607 <div class="span2 dispalyInline"> </div>
3608 <div class="span6 dispalyInline">
3609 <ul class="footer-nav-content">
3610 <li><a href="Terms_of_use.html" title="Terms of use" id="foot0">Terms of use</a>|</li>
3611 <li><a href="Privacy_policy.html" title="Privacy policy" id="foot1" class="active">Privacy policy</a>|</li>
3612 <li><a href="Tollfree_directory_assistance.html" title="Tollfree directory assistance" id="foot2">Tollfree directory assistance</a>|</li>
3613 <li><a href="compliance.html" title="Accessibility" id="foot3">Accessibility</a></li>
3616 <p><a href="//www.att.com/gen/privacy-policy?pid=2587" target="_blank">© <span class="copyright">2016</span> AT&T Intellectual Property</a>. All rights reserved. AT&T,the AT&T Globe logo and all other AT&T marks contained herein are tardemarks of AT&T intellectual property and/or AT&T affiliated companines.
3620 <div class="span3 footerLogo dispalyInline">
3621 <a href="index.html" class="footer-logo">
3622 <i class="icon-primary-att-globe"><span class="hidden-spoken">A T & T</span></i>
3623 <h2 class="logo-title">AT&T</h2>
3632 * <section id="code">
3633 <example module="b2b.att">
3634 <file src="src/footer/docs/demo.html" />
3635 <file src="src/footer/docs/demo.js" />
3640 angular.module('b2b.att.footer', ['b2b.att.utilities']).
3641 directive('b2bColumnSwitchFooter', [function() {
3646 footerLinkItems: "="
3648 templateUrl: 'b2bTemplate/footer/footer_column_switch_tpl.html',
3649 link: function(scope) {
3650 var tempFooterColumns = scope.footerLinkItems.length;
3651 scope.footerColumns = 3;
3652 if ( (tempFooterColumns === 5) || (tempFooterColumns === 4) ) {
3653 scope.footerColumns = tempFooterColumns;
3664 * @name Layouts.att:header
3667 * <file src="src/header/docs/readme.md" />
3670 * <li b2b-header-menu class="header__item b2b-headermenu" ng-repeat="item in tabItems" role="presentation">
3671 <a href="#" class="menu__item" role="menuitem">{{item.title}}</a>
3672 <div class="header-secondary-wrapper">
3673 <ul class="header-secondary" role="menu">
3674 <li class="header-subitem" b2b-header-submenu ng-repeat="i in item.subitems" role="presentation">
3675 <a href="#" class="menu__item" aria-haspopup="true" role="menuitem">{{i.value}}</a>
3676 <div class="header-tertiary-wrapper" ng-if="i.links">
3677 <ul class="header-tertiary" role="menu">
3678 <li b2b-header-tertiarymenu ng-repeat="link in i.links" role="presentation">
3679 <label>{{link.title}}</label>
3680 <div b2b-tertiary-link ng-repeat="title in link.value">
3681 <a href="{{link.href}}" class="header-tertiaryitem" ng-if="!title.subitems" aria-haspopup="false" role="menuitem"><span class="b2b-label-hide">{{link.title}}</span>{{title.title}}</a>
3682 <a href="{{link.href}}" class="header-tertiaryitem" b2b-header-togglemenu ng-if="title.subitems" aria-haspopup="true" role="menuitem"><span class="b2b-label-hide">{{link.title}}</span>{{title.title}}</a>
3683 <ul class="header-quarternary" role="menu" ng-if="title.subitems">
3684 <li b2b-header-quarternarymenu role="presentation">
3685 <a href="{{nav.href}}" ng-repeat="nav in title.subitems" role="menuitem" aria-haspopup="true">{{nav.title}}</a>
3698 * <section id="code">
3699 <example module="b2b.att.header">
3700 <file src="src/header/docs/demo.html" />
3701 <file src="src/header/docs/demo.js" />
3706 angular.module('b2b.att.header', ['b2b.att.dropdowns','b2b.att.utilities'])
3707 .directive('b2bHeaderMenu', ['keymap', '$documentBind', '$timeout', '$isElement', '$document', function (keymap, $documentBind, $timeout, $isElement, $document) {
3710 controller:['$scope',function($scope){
3711 this.nextSiblingFocus = function (elObj,flag) {
3712 if (elObj.nextElementSibling) {
3714 var nextmenuItem = this.getFirstElement(elObj.nextElementSibling,'a');
3715 nextmenuItem.focus();
3717 elObj.nextElementSibling.focus();
3722 this.previousSiblingFocus = function (elObj,flag) {
3723 if (elObj.previousElementSibling) {
3725 var prevmenuItem = this.getFirstElement(elObj.previousElementSibling,'a');
3726 prevmenuItem.focus();
3728 elObj.previousElementSibling.focus();
3733 this.getFirstElement = function(elmObj,selector){
3734 return elmObj.querySelector(selector);
3737 link: function (scope, elem,attr,ctrl) {
3738 scope.showMenu = false;
3739 var activeElm, subMenu, tertiaryMenu, el= angular.element(elem)[0],
3740 menuItem = angular.element(elem[0].children[0]);
3741 menuItem.bind('click', function () {
3742 elem.parent().children().removeClass('active');
3743 elem.addClass('active');
3744 var elems= this.parentElement.parentElement.querySelectorAll('li[b2b-header-menu]>a');
3745 for (var i=0; i<elems.length; i++) {
3746 elems[i].setAttribute("aria-expanded",false);
3748 scope.showMenu = true;
3749 var elmTofocus = ctrl.getFirstElement(this.parentElement,'li[b2b-header-submenu]');
3750 elmTofocus.firstElementChild.focus();
3751 this.setAttribute('aria-expanded',true);
3755 elem.bind('keydown', function (evt) {
3756 activeElm = document.activeElement;
3757 subMenu = ctrl.getFirstElement(activeElm.parentElement,'li[b2b-header-submenu]');
3758 tertiaryMenu = ctrl.getFirstElement(activeElm.parentElement,'li[b2b-header-tertiarymenu]');
3759 switch (evt.keyCode) {
3760 case keymap.KEY.ENTER:
3761 case keymap.KEY.SPACE:
3765 evt.stopPropagation();
3766 evt.preventDefault();
3767 if (activeElm.parentElement.hasAttribute('b2b-header-submenu')) {
3768 menuItem[0].focus();
3771 case keymap.KEY.DOWN:
3772 evt.stopPropagation();
3773 evt.preventDefault();
3775 subMenu.firstElementChild.focus();
3776 } else if (tertiaryMenu) {
3777 var firstSubitem = ctrl.getFirstElement(tertiaryMenu,'a.header-tertiaryitem');
3778 firstSubitem.focus();
3781 case keymap.KEY.RIGHT:
3782 evt.stopPropagation();
3783 evt.preventDefault();
3784 if (activeElm.parentElement.hasAttribute('b2b-header-submenu')) {
3785 var elm = angular.element(activeElm.parentElement)[0];
3786 ctrl.nextSiblingFocus(elm,true);
3787 } else if (activeElm.parentElement.parentElement.hasAttribute('b2b-header-tertiarymenu')) {
3788 var tertiaryLI = angular.element(activeElm.parentElement.parentElement)[0];
3789 if (tertiaryLI.nextElementSibling) {
3790 var nextElm = ctrl.getFirstElement(tertiaryLI.nextElementSibling,"a.header-tertiaryitem");
3794 else if(activeElm.parentElement.hasAttribute('b2b-header-menu')){
3795 ctrl.nextSiblingFocus(el,true);
3798 case keymap.KEY.LEFT:
3799 evt.stopPropagation();
3800 evt.preventDefault();
3801 if (activeElm.parentElement.hasAttribute('b2b-header-submenu')) {
3802 var previousElm = angular.element(activeElm.parentElement)[0];
3803 ctrl.previousSiblingFocus(previousElm,true);
3804 } else if (activeElm.parentElement.parentElement.hasAttribute('b2b-header-tertiarymenu')) {
3805 var tertiaryLI = angular.element(activeElm.parentElement.parentElement)[0];
3806 if (tertiaryLI.previousElementSibling) {
3807 var prevElm = ctrl.getFirstElement(tertiaryLI.previousElementSibling,"a.header-tertiaryitem");
3811 else if(activeElm.parentElement.hasAttribute('b2b-header-menu')) {
3812 ctrl.previousSiblingFocus(el,true);
3815 case keymap.KEY.ESC:
3816 evt.stopPropagation();
3817 evt.preventDefault();
3818 scope.showMenu = false;
3819 elem.removeClass('active');
3820 menuItem.attr('aria-expanded',false);
3821 $timeout(function(){
3822 menuItem[0].focus();
3830 var outsideClick = function (e) {
3831 var isElement = $isElement(angular.element(e.target), elem, $document);
3833 scope.showMenu = false;
3834 elem.removeClass('active');
3838 $documentBind.click('showMenu', outsideClick, scope);
3841 }]).directive('b2bHeaderSubmenu', ['$timeout',function ($timeout) {
3844 link: function (scope, elem) {
3845 var caretSign = angular.element("<i class='menuCaret'></i>");
3846 $timeout(function(){
3847 var menuItem = angular.element(elem[0].children[0]);
3848 menuItem.bind('focus mouseenter', function () {
3849 elem.parent().children().removeClass('active');
3850 elem.addClass('active');
3851 if(elem[0].childElementCount > 1){ // > 1 has third level menu
3852 menuItem.attr('aria-expanded',true);
3853 menuItem.attr('aria-haspopup',true);
3855 var caretLeft = (elem[0].offsetLeft + elem[0].offsetWidth/2) - 10;
3856 caretSign.css({left: caretLeft + 'px'});
3857 angular.element(caretSign);
3858 var tertiaryItems = elem[0].querySelectorAll('[b2b-header-tertiarymenu]');
3859 if(tertiaryItems.length >=1){
3860 elem.append(caretSign);
3863 menuItem.bind('blur', function () {
3864 $timeout(function () {
3865 var parentElm = document.activeElement.parentElement.parentElement;
3867 if (!(parentElm.hasAttribute('b2b-header-tertiarymenu'))) {
3868 elem.removeClass('active');
3869 if(elem[0].childElementCount > 1){ // > 1 has third level menu
3870 menuItem.attr('aria-expanded',false);
3872 var caret = elem[0].querySelector('.menuCaret');
3883 }]).directive('b2bHeaderTertiarymenu', ['$timeout','keymap', function ($timeout,keymap){
3886 require:'^b2bHeaderMenu',
3887 link: function (scope, elem,attr,ctrl) {
3889 elem.bind('keydown', function (evt) {
3890 var activeElm = document.activeElement;
3891 var activeParentElm = activeElm.parentElement;
3892 var activeParentObj = angular.element(activeParentElm)[0];
3894 if(activeParentElm.hasAttribute('b2b-tertiary-link')){
3895 var quarterNav = angular.element(activeParentElm)[0].querySelector('li[b2b-header-quarternarymenu]');
3897 var links = ctrl.getFirstElement(angular.element(quarterNav)[0],'a');
3900 var tertiaryMenu = activeElm.parentElement.parentElement.parentElement;
3901 var tertiaryMenuFlag = tertiaryMenu.hasAttribute('b2b-tertiary-link');
3903 switch (evt.keyCode) {
3904 case keymap.KEY.DOWN:
3905 evt.stopPropagation();
3906 evt.preventDefault();
3907 if (activeParentElm.hasAttribute('b2b-tertiary-link')) {
3908 if(angular.element(quarterNav).hasClass('active')){
3910 }else if(activeParentObj.nextElementSibling){
3911 ctrl.nextSiblingFocus(activeParentObj,true);
3914 else if(angular.element(activeParentElm).hasClass('active')){
3915 ctrl.nextSiblingFocus(activeElm);
3919 evt.stopPropagation();
3920 evt.preventDefault();
3921 if(activeParentElm.hasAttribute('b2b-tertiary-link')){
3922 if(activeParentObj.previousElementSibling.hasAttribute('b2b-tertiary-link')){
3923 ctrl.previousSiblingFocus(activeParentObj,true);
3925 var elm = angular.element(activeElm.parentElement.parentElement.parentElement.parentElement.parentElement)[0];
3926 ctrl.getFirstElement(elm,"a").focus();
3928 }else if(angular.element(activeParentElm).hasClass('active')){
3929 if (activeElm.previousElementSibling) {
3930 ctrl.previousSiblingFocus(activeElm);
3931 }else if (tertiaryMenuFlag) {
3932 var elm = angular.element(tertiaryMenu)[0];
3933 ctrl.getFirstElement(elm,"a.header-tertiaryitem").focus();
3943 }]).directive('b2bHeaderTogglemenu', ['$timeout', 'keymap', function ($timeout, keymap) {
3946 require: '^b2bHeaderMenu',
3947 link: function (scope, elem, attrs, ctrl) {
3949 $timeout(function () {
3950 quarterNav = angular.element(elem.parent())[0].querySelector('li[b2b-header-quarternarymenu]');
3951 elem.bind('click', function () {
3952 angular.element(quarterNav).toggleClass('active');
3957 }]).directive('b2bHeaderResponsive', ['$timeout',function ($timeout) {
3960 controller: function($scope){
3961 this.applyMediaQueries = function(value){
3962 document.querySelector('style').textContent +=
3963 "@media screen and (max-width:950px) { \
3964 .header__item.profile { right: " + value + "px; } \
3967 this.arrangeResponsiveHeader = function(children){
3969 * clientWidth of 1090 === max-width of 1100px
3970 * clientWidth of 920 === max-width of 950px
3971 * see b2b-angular.css for rest of responsive header CSS
3973 if (document.documentElement.clientWidth <= 920) {
3976 this.applyMediaQueries(200);
3979 this.applyMediaQueries(200);
3981 default: // anthing above 3, however, should not have more than 3 to date
3982 this.applyMediaQueries(200);
3987 link: function (scope, elem, attrs, ctrl) {
3992 $timeout(function(){
3993 profile = document.querySelector('li.header__item.profile');
3994 children = angular.element(profile).children().length;
3996 ctrl.arrangeResponsiveHeader(children); // shift right-side icon flyovers
4000 window.addEventListener('resize', function(event){ // caret adjustmet
4001 var activeSubmenu = elem[0].querySelector('[b2b-header-menu] [b2b-header-submenu].active');
4002 var activeSubmenuEl = angular.element(activeSubmenu);
4004 var caretSign = activeSubmenu.querySelector('i.menuCaret');
4006 var caretSignEl = angular.element(caretSign);
4007 var caretLeft = (activeSubmenu.offsetLeft + activeSubmenu.offsetWidth/2) - 10;
4008 caretSignEl.css({left: caretLeft + 'px'});
4012 ctrl.arrangeResponsiveHeader(children); // shift right-side icon flyovers
4020 * @name Layouts.att:headings & copy
4023 * <file src="src/headingsAndCopy/docs/readme.md" />
4027 <b>HTML + AngularJS</b>
4028 <example module="b2b.att">
4029 <file src="src/headingsAndCopy/docs/demo.html" />
4034 var b2bLegalCopy = angular.module('b2b.att.headingsAndCopy', []);
4037 * @name Tabs, tables & accordions.att:horizontalTable
4040 * <file src="src/horizontalTable/docs/readme.md" />
4043 * @param {int} sticky - Number of sticky columns to have. Maximum of 3.
4044 * @param {boolean} refresh - A boolean that when set to true will force a re-render of table. Only use when using 'bulk mode'
4045 * @param {string} legendContent - A string of html to fill in the legend flyout. This should generally be a <ul> with <li> and should not rely on Angular for repeating.
4046 * @param {boolean} retainColumnSet - A boolean that on re-render of the table, determines if the columns visible should reset to 0 or not. Default is false.
4048 * <section id="code">
4049 <example module="b2b.att">
4050 <file src="src/horizontalTable/docs/demo.html" />
4051 <file src="src/horizontalTable/docs/demo.js" />
4056 angular.module('b2b.att.horizontalTable', [])
4057 .constant('b2bHorizontalTableConfig', {
4058 'maxStickyColumns': 3
4060 .directive('b2bHorizontalTable', ['$timeout', 'b2bHorizontalTableConfig', 'b2bDOMHelper', function ($timeout, b2bHorizontalTableConfig, b2bDOMHelper) {
4066 numOfStickyCols: '=?sticky',
4068 legendContent: '=?',
4069 retainColumnSet: '=?'
4072 templateUrl: 'b2bTemplate/horizontalTable/horizontalTable.html',
4073 link: function (scope, element, attrs, ctrl) {
4074 scope.numOfStickyCols = scope.numOfStickyCols || 1;
4075 scope.viewportIndex = scope.numOfStickyCols;
4076 scope.countDisplayText = "";
4077 var tableElement = element.find('table');
4078 var thElements = element.find('th');
4079 var innerContainer = angular.element(element[0].querySelector('.b2b-horizontal-table-inner-container'));
4080 var outerContainer = angular.element(element[0].querySelector('.b2b-horizontal-table'));
4082 var tableColumns = [];
4083 var tableRows = element.find('tr');
4087 var totalWidth = element.children()[0].offsetWidth;
4088 var lastVisibleColumn = 0;
4089 var collectiveColumnWidth = [];
4090 var collectiveRowHeight = [];
4091 var columnSets = [];
4093 var stickyPixels = 0;
4095 var displayNoneCSS = {'display': 'none'};
4096 var displayBlockCSS = {'display': 'table-cell'};
4098 var init = function() {
4099 // Reset this from a previous execution
4101 collectiveColumnWidth = [];
4102 collectiveRowHeight = [];
4105 lastVisibleColumn = 0;
4107 if ((!!scope.retainColumnSet)) {
4110 visibleColumns = [];
4113 tableElement = element.find('table');
4114 thElements = element.find('th');
4115 innerContainer = angular.element(element[0].querySelector('.b2b-horizontal-table-inner-container'));
4116 outerContainer = angular.element(element[0].querySelector('.b2b-horizontal-table'));
4117 totalWidth = element.children()[0].offsetWidth;
4118 tableRows = element.find('tr');
4120 scope.disableLeft = true;
4121 scope.disableRight = false;
4123 if (scope.numOfStickyCols > b2bHorizontalTableConfig.maxStickyColumns) {
4124 throw new Error('Table can only support ' + b2bHorizontalTableConfig.maxStickyColumns + ' sticky columns.');
4126 scope.countDisplayText = "";
4127 for(var count = 1; count <= scope.numOfStickyCols; count++) {
4128 scope.countDisplayText = scope.countDisplayText + count + ", "
4130 angular.forEach(tableRows, function(row, rowIndex) {
4131 for(var j = 0; j < row.children.length; j++) {
4132 if (tableColumns[j] === undefined) {
4133 tableColumns[j] = [];
4135 tableColumns[j].push(row.children[j]);
4139 // We need to reset all the displayNones from previous runs, if applicable
4140 if (attrs.refresh !== undefined && attrs.refresh !== '') {
4141 for (var i = scope.numOfStickyCols+1; i < tableColumns.length; i++) {
4142 angular.element(tableColumns[i]).css(displayBlockCSS);
4146 // We must calculate here as we need cells to be reset after re-render.
4147 angular.forEach(tableRows, function(row, rowIndex) {
4148 collectiveRowHeight.push(findMax(row.children, 'height')); // BUG: Keeping this here now causes row height bugs
4152 for (var i = 0; i < tableColumns.length; i++) {
4153 collectiveColumnWidth.push(findMax(tableColumns[i], 'width')); //offsetWidth doesn't take into account custom css inside
4155 for(var i = 0; i < scope.numOfStickyCols; i++) {
4156 maxWidth += collectiveColumnWidth[i];
4159 // BUG: The code I put in to fix the table not spanning 100% width is now preventing
4160 // table cells from laying out more than stickyPixels and thus we have weird wrapping
4161 stickyPixels = totalWidth-maxWidth;
4163 // At this point, for each tr, I need to set the properties (height) and each numOfStickyCols children
4164 // should be set with sticky properties (margin-left and width)
4165 var width = maxWidth;
4168 if (angular.element(document).find('html').hasClass('isIE')) {
4171 var thObject = undefined;
4172 for(var i = 0; i < scope.numOfStickyCols; i++) {
4173 for (var j = 0; j < tableRows.length; j++) {
4174 thObject = angular.element(tableRows[j].children[i]);
4175 angular.element(thObject).css({
4176 'margin-left': -(width + 2) + 'px',
4177 'width': (collectiveColumnWidth[i] + 3) + 'px', // instead of taking the max width, grab max width for that column
4178 'height': (collectiveRowHeight[j] + additive) + 'px',
4179 'position': 'absolute',
4180 'background-color': '#F2F2F2'
4183 width -= collectiveColumnWidth[i];
4185 angular.element(tableRows[0]).css('height', collectiveRowHeight[0] + 'px');
4186 for(var i = 0; i < tableRows.length; i++) {
4187 angular.element(tableRows[i]).css('height', (collectiveRowHeight[i] + additive) + 'px');
4190 innerContainer.css({
4191 'padding-left': (maxWidth + 2) + 'px'
4195 // Let's precompute all the (set) combinations beforehand
4197 for (var i = scope.numOfStickyCols; i < tableColumns.length;) {
4198 visibleColumns = calculateVisibleColumns(i);
4199 if(visibleColumns === tableColumns.length){
4200 columnSets.push([i, visibleColumns-1]);
4202 columnSets.push([i, visibleColumns]);
4204 i = visibleColumns + 1;
4207 //columnSets = [[1, 1], [2,7]];
4209 updateCellDisplay(columnSets[setIndex]);
4210 checkScrollArrows();
4212 scope.numOfCols = tableColumns.length;
4216 // JM520E: This is a temporary hack until I solve the ngRepeat issue
4218 if (element.find('th').length < scope.numOfStickyCols) {
4219 // DOM ngRepeat is not ready, let's check back in 10 ms
4220 $timeout(hack, 10, false);
4222 if (scope.refresh !== undefined) {
4223 scope.$watch('refresh', function(oldVal, newVal) { // this watch calls too many times
4224 if (!angular.equals(oldVal, newVal)) { //hackFinished && oldVal < newVal
4225 // From testing it takes about 30 ms before ngRepeat executes, so let's set initial timeout
4226 // NOTE: May need to expose timeout to developers. Application is known to have digest cycle of 3-5k watches.
4227 $timeout(init, 100, false);
4228 scope.refresh = false;
4240 // Let's get started with some math!
4243 function calculateVisibleColumns(startingPoint) {
4245 visibleColumns = startingPoint || scope.numOfStickyCols;
4247 while(usedWidth < stickyPixels && visibleColumns < collectiveColumnWidth.length) {
4248 if (usedWidth+collectiveColumnWidth[visibleColumns] > stickyPixels) {
4249 if (startingPoint === visibleColumns) {
4250 return visibleColumns; // The next cell is too large to fit, it should be only thing to fit
4253 return visibleColumns;
4255 usedWidth += collectiveColumnWidth[visibleColumns];
4259 if (usedWidth > stickyPixels) {
4260 return --visibleColumns;
4262 return visibleColumns;
4265 function updateCellDisplay(set) {
4266 for (var i = scope.numOfStickyCols; i < tableColumns.length; i++) {
4267 angular.element(tableColumns[i]).css(displayNoneCSS);
4270 for (var i = set[0]; i <= set[1]; i++) {
4271 angular.element(tableColumns[i]).css(displayBlockCSS);
4275 function findMax(arr, prop) {
4280 for (var i = 0; i < arr.length; i++) {
4282 prevDisplay = angular.element(item).css('display');
4284 if (scope.$$phase) {
4287 // Remove inline styles, they will mess up calculations from original run
4288 angular.element(item).css('height', '');
4289 angular.element(item).css('width', '');
4290 if (prop === 'width') {
4291 // If we do not undo previous run's inline styles, this will grow widths on each re-render.
4292 localVal = Math.ceil(parseInt(window.getComputedStyle(item).width.split('px')[0], 10)) + 30; // 30 px is padding
4293 } else if (prop === 'offsetWidth') {
4294 localVal = item.offsetWidth;
4295 } else if (prop === 'height') {
4296 //localVal = item.offsetHeight;
4297 localVal = Math.ceil(parseInt(window.getComputedStyle(item).height.split('px')[0], 10))
4300 if (localVal >= max) {
4308 function checkScrollArrows() {
4309 scope.disableLeft = (setIndex === 0);
4310 scope.disableRight = !(setIndex < columnSets.length-1);
4313 scope.moveViewportLeft = function () {
4315 updateCellDisplay(columnSets[setIndex]);
4316 checkScrollArrows();
4318 if (scope.disableLeft) {
4319 element[0].querySelector('.b2b-horizontal-table-column-info').focus();
4323 scope.moveViewportRight = function () {
4325 updateCellDisplay(columnSets[setIndex]);
4326 checkScrollArrows();
4328 if (scope.disableRight) {
4329 element[0].querySelector('.b2b-horizontal-table-column-info').focus();
4333 scope.getColumnSet = function () {
4334 return columnSets[setIndex];
4337 innerContainer.bind('scroll', function () {
4338 $timeout(function () {
4339 checkScrollArrows();
4348 * @name Forms.att:hourPicker
4351 * <file src="src/hourPicker/docs/readme.md" />
4354 * <div b2b-hourpicker ng-model="hourpickerValue.value"></div>
4357 * <section id="code">
4358 <example module="b2b.att">
4359 <file src="src/hourPicker/docs/demo.html" />
4360 <file src="src/hourPicker/docs/demo.js" />
4365 angular.module('b2b.att.hourPicker', ['b2b.att.utilities'])
4367 .constant('b2bHourpickerConfig', {
4404 startTimeOptions: ['1:00', '2:00', '3:00', '4:00', '5:00', '6:00', '7:00', '8:00', '9:00', '10:00', '11:00', '12:00'],
4405 startTimeDefaultOptionIndex: -1,
4406 startTimeDefaultMeridiem: "am",
4407 endTimeOptions: ['1:00', '2:00', '3:00', '4:00', '5:00', '6:00', '7:00', '8:00', '9:00', '10:00', '11:00', '12:00'],
4408 endTimeDefaultOptionIndex: -1,
4409 endTimeDefaultMeridiem: "pm",
4413 .factory('b2bNormalizeHourpickerValues', [function () {
4414 var _normalize = function (hourpickerValues) {
4415 if (angular.isDefined(hourpickerValues) && hourpickerValues != null) {
4416 var finalHourpickerValues = [];
4417 var hourpickerValue = {};
4419 for (var i = 0; i < hourpickerValues.length; i++) {
4420 days = hourpickerValues[i].days ? hourpickerValues[i].days : {};
4421 hourpickerValue.startTime = hourpickerValues[i].startTime ? hourpickerValues[i].startTime : '';
4422 hourpickerValue.startMeridiem = hourpickerValues[i].startMeridiem ? hourpickerValues[i].startMeridiem : '';
4423 hourpickerValue.endTime = hourpickerValues[i].endTime ? hourpickerValues[i].endTime : '';
4424 hourpickerValue.endMeridiem = hourpickerValues[i].endMeridiem ? hourpickerValues[i].endMeridiem : '';
4425 hourpickerValue.days = [];
4427 var retrieveDaysText = function (daysDetails) {
4432 for (var i in days) {
4433 if (days[i].value) {
4438 first = daysTexts[0];
4439 last = daysTexts[0];
4441 hourpickerValue.days[index] = days[first].caption;
4442 if (daysTexts.length > 1) {
4443 for (var i = 1; i < daysTexts.length; i++) {
4444 if (daysTexts[i] - last === 1) {
4445 last = daysTexts[i];
4446 hourpickerValue.days[index] = days[first].caption + ' - ' + days[last].caption;
4449 first = last = daysTexts[i];
4450 hourpickerValue.days[index] = days[first].caption;
4457 finalHourpickerValues.push(angular.copy(hourpickerValue));
4460 return angular.copy(finalHourpickerValues);
4465 normalize: _normalize
4469 .directive('b2bHourpicker', ['b2bHourpickerConfig', 'b2bNormalizeHourpickerValues', function (b2bHourpickerConfig, b2bNormalizeHourpickerValues) {
4475 templateUrl: 'b2bTemplate/hourPicker/b2bHourpicker.html',
4476 controller: ['$scope', function (scope) {
4479 link: function (scope, elem, attr, ctrl) {
4480 scope.hourpicker = {};
4481 scope.hourpicker.dayOptions = attr.dayOptions ? scope.$parent.$eval(attr.dayOptions) : b2bHourpickerConfig.dayOptions;
4482 scope.hourpicker.startTimeOptions = attr.startTimeOptions ? scope.$parent.$eval(attr.startTimeOptions) : b2bHourpickerConfig.startTimeOptions;
4483 scope.hourpicker.endTimeOptions = attr.endTimeOptions ? scope.$parent.$eval(attr.endTimeOptions) : b2bHourpickerConfig.endTimeOptions;
4484 scope.hourpicker.startTimeDefaultOptionIndex = attr.startTimeDefaultOptionIndex ? scope.$parent.$eval(attr.startTimeDefaultOptionIndex) : b2bHourpickerConfig.startTimeDefaultOptionIndex;
4485 scope.hourpicker.endTimeDefaultOptionIndex = attr.endTimeDefaultOptionIndex ? scope.$parent.$eval(attr.endTimeDefaultOptionIndex) : b2bHourpickerConfig.endTimeDefaultOptionIndex;
4486 scope.hourpicker.startTimeDefaultMeridiem = attr.startTimeDefaultMeridiem ? scope.$parent.$eval(attr.startTimeDefaultMeridiem) : b2bHourpickerConfig.startTimeDefaultMeridiem;
4487 scope.hourpicker.endTimeDefaultMeridiem = attr.endTimeDefaultMeridiem ? scope.$parent.$eval(attr.endTimeDefaultMeridiem) : b2bHourpickerConfig.endTimeDefaultMeridiem;
4488 scope.hourpicker.sameDayOption = attr.sameDayOption ? scope.$parent.$eval(attr.sameDayOption) : b2bHourpickerConfig.sameDayOption;
4489 scope.hourpicker.editMode = -1;
4491 scope.hourpickerValues = [];
4492 scope.finalHourpickerValues = [];
4493 scope.addHourpickerValue = function (hourpickerPanelValue) {
4494 if (hourpickerPanelValue) {
4495 if (scope.hourpicker.editMode > -1) {
4496 scope.hourpickerValues[scope.hourpicker.editMode] = hourpickerPanelValue;
4497 scope.hourpicker.editMode = -1;
4499 scope.hourpickerValues.push(hourpickerPanelValue);
4502 scope.finalHourpickerValues = b2bNormalizeHourpickerValues.normalize(angular.copy(scope.hourpickerValues));
4503 ctrl.$setViewValue(angular.copy(scope.hourpickerValues));
4505 ctrl.$render = function () {
4506 if (angular.isDefined(ctrl.$modelValue)) {
4507 scope.hourpickerValues = angular.copy(ctrl.$modelValue);
4508 scope.finalHourpickerValues = b2bNormalizeHourpickerValues.normalize(angular.copy(scope.hourpickerValues));
4511 scope.editHourpickerValue = function (index) {
4512 scope.hourpickerPanelValue = angular.copy(scope.hourpickerValues[index]);
4513 scope.hourpicker.editMode = index;
4515 scope.deleteHourpickerValue = function (index) {
4516 scope.hourpickerValues.splice(index, 1);
4517 scope.resetHourpickerPanelValue();
4518 scope.addHourpickerValue();
4521 scope.setValidity = function (errorType, errorValue) {
4522 ctrl.$setValidity(errorType, errorValue);
4528 .directive('b2bHourpickerPanel', [function () {
4532 templateUrl: 'b2bTemplate/hourPicker/b2bHourpickerPanel.html',
4533 controller: ['$scope', function (scope) {
4536 link: function (scope, elem, attr, ctrl) {
4537 var hourpickerPanelValueTemplate = {
4540 startMeridiem: 'am',
4544 for (var i = 0; i < scope.hourpicker.dayOptions.length; i++) {
4545 hourpickerPanelValueTemplate.days[i] = {
4547 title: scope.hourpicker.dayOptions[i].title,
4548 caption: scope.hourpicker.dayOptions[i].caption
4551 scope.hourpickerPanelValue = {};
4552 scope.disableAddBtn = true;
4554 scope.$watch('hourpickerPanelValue.days', function(){
4555 for(var i in scope.hourpickerPanelValue.days)
4557 if(scope.hourpickerPanelValue.days[i].value)
4559 scope.disableAddBtn = false;
4562 scope.disableAddBtn = true;
4566 scope.resetHourpickerPanelValue = function () {
4567 scope.hourpickerPanelValue = angular.copy(hourpickerPanelValueTemplate);
4568 if (scope.hourpicker.startTimeDefaultOptionIndex > -1) {
4569 scope.hourpickerPanelValue.startTime = scope.hourpicker.startTimeOptions[scope.hourpicker.startTimeDefaultOptionIndex];
4571 if (scope.hourpicker.endTimeDefaultOptionIndex > -1) {
4572 scope.hourpickerPanelValue.endTime = scope.hourpicker.endTimeOptions[scope.hourpicker.endTimeDefaultOptionIndex];
4574 scope.hourpickerPanelValue.startMeridiem = scope.hourpicker.startTimeDefaultMeridiem;
4575 scope.hourpickerPanelValue.endMeridiem = scope.hourpicker.endTimeDefaultMeridiem;
4576 scope.hourpicker.editMode = -1;
4577 scope.setValidity('invalidHourpickerData', true);
4578 scope.setValidity('invalidHourpickerTimeRange', true);
4580 scope.resetHourpickerPanelValue();
4581 scope.updateHourpickerValue = function () {
4582 if (scope.isFormValid() && !scope.isTimeOverlap()) {
4583 scope.addHourpickerValue(angular.copy(scope.hourpickerPanelValue));
4584 scope.resetHourpickerPanelValue();
4588 scope.isFormValid = function () {
4589 var isStartTimeAvailable = scope.hourpickerPanelValue.startTime ? true : false;
4590 var isStartMeridiemAvailable = scope.hourpickerPanelValue.startMeridiem ? true : false;
4591 var isEndTimeAvailable = scope.hourpickerPanelValue.endTime ? true : false;
4592 var isEndMeridiemAvailable = scope.hourpickerPanelValue.endMeridiem ? true : false;
4593 var currentStartTime = getTime(scope.hourpickerPanelValue.startTime, scope.hourpickerPanelValue.startMeridiem);
4594 var currentEndTime = getTime(scope.hourpickerPanelValue.endTime, scope.hourpickerPanelValue.endMeridiem);
4595 var isTimeInProperSequence = currentEndTime > currentStartTime;
4596 var isDayChecked = false;
4597 for (var i in scope.hourpickerPanelValue.days) {
4598 if (scope.hourpickerPanelValue.days[i].value) {
4599 isDayChecked = true;
4604 if (isStartTimeAvailable && isStartMeridiemAvailable && isEndTimeAvailable && isEndMeridiemAvailable && isTimeInProperSequence && isDayChecked) {
4605 scope.setValidity('invalidHourpickerData', true);
4608 scope.setValidity('invalidHourpickerData', false);
4612 scope.isTimeOverlap = function () {
4613 var selectedDays = [];
4614 for (var i in scope.hourpickerPanelValue.days) {
4615 if (scope.hourpickerPanelValue.days[i].value) {
4616 selectedDays.push(i);
4620 var currentStartTime, currentEndTime, existingStartTime, existingEndTime;
4621 currentStartTime = getTime(scope.hourpickerPanelValue.startTime, scope.hourpickerPanelValue.startMeridiem);
4622 currentEndTime = getTime(scope.hourpickerPanelValue.endTime, scope.hourpickerPanelValue.endMeridiem);
4623 for (var i = 0; i < scope.hourpickerValues.length; i++) {
4625 if (i === scope.hourpicker.editMode) {
4629 for (var j = 0; j < selectedDays.length; j++) {
4630 existingStartTime = getTime(scope.hourpickerValues[i].startTime, scope.hourpickerValues[i].startMeridiem);
4631 existingEndTime = getTime(scope.hourpickerValues[i].endTime, scope.hourpickerValues[i].endMeridiem);
4632 if (scope.hourpickerValues[i].days[selectedDays[j]].value) {
4633 if(!scope.hourpicker.sameDayOption){
4634 scope.setValidity('dayAlreadySelected', false);
4636 } else if ((currentStartTime > existingStartTime && currentStartTime < existingEndTime) || (currentEndTime > existingStartTime && currentEndTime < existingEndTime)) {
4637 scope.setValidity('invalidHourpickerTimeRange', false);
4639 } else if ((existingStartTime > currentStartTime && existingStartTime < currentEndTime) || (existingEndTime > currentStartTime && existingEndTime < currentEndTime)) {
4640 scope.setValidity('invalidHourpickerTimeRange', false);
4642 } else if ((currentStartTime === existingStartTime) && (currentEndTime === existingEndTime)) {
4643 scope.setValidity('invalidHourpickerTimeRange', false);
4650 scope.setValidity('dayAlreadySelected', true);
4651 scope.setValidity('invalidHourpickerTimeRange', true);
4654 var getTime = function (timeString, meridiem) {
4655 var tempDate = new Date();
4656 if (timeString && meridiem) {
4657 var timeSplit = timeString.split(':');
4658 var hour = ((meridiem === 'PM' || meridiem === 'pm') && timeSplit[0] !== '12') ? parseInt(timeSplit[0], 10) + 12 : parseInt(timeSplit[0], 10);
4659 tempDate.setHours(hour, parseInt(timeSplit[1], 10), 0, 0);
4662 return tempDate.getTime();
4668 .directive('b2bHourpickerValue', [function () {
4672 templateUrl: 'b2bTemplate/hourPicker/b2bHourpickerValue.html',
4673 controller: ['$scope', function (scope) {
4676 link: function (scope, elem, attr, ctrl) {
4677 scope.hourpickerValue = {};
4678 scope.hourpickerValue.startTime = attr.startTime ? scope.$eval(attr.startTime) : '';
4679 scope.hourpickerValue.startMeridiem = attr.startMeridiem ? scope.$eval(attr.startMeridiem) : '';
4680 scope.hourpickerValue.endTime = attr.endTime ? scope.$eval(attr.endTime) : '';
4681 scope.hourpickerValue.endMeridiem = attr.endMeridiem ? scope.$eval(attr.endMeridiem) : '';
4682 scope.hourpickerValue.days = attr.days ? scope.$eval(attr.days).join(', ') : '';
4683 scope.hourpickerValue.index = attr.b2bHourpickerValue ? scope.$eval(attr.b2bHourpickerValue) : -1;
4689 * @name Template.att:inputTemplate
4692 * <file src="src/inputTemplate/docs/readme.md" />
4695 * <input type="text" id="fieldId" placeholder="placholder text here" class="span12 input-enhanced" name="fieldName">
4699 <b>HTML + AngularJS</b>
4700 <example module="b2b.att">
4701 <file src="src/inputTemplate/docs/demo.html" />
4702 <file src="src/inputTemplate/docs/demo.js" />
4706 angular.module('b2b.att.inputTemplate', []);
4710 * @name Navigation.att:leftNavigation
4713 * <file src="src/leftNavigation/docs/readme.md" />
4716 * <b2b-left-navigation data-menu="menuData"></b2b-left-navigation>
4719 * <section id="code">
4720 <example module="b2b.att">
4721 <file src="src/leftNavigation/docs/demo.html" />
4722 <file src="src/leftNavigation/docs/demo.js" />
4727 angular.module('b2b.att.leftNavigation', [])
4728 .directive('b2bLeftNavigation', [function () {
4731 templateUrl: 'b2bTemplate/leftNavigation/leftNavigation.html',
4735 link: function (scope, element, attrs, ctrl) {
4739 scope.toggleNav = function (val) {
4740 if (val === scope.idx) {
4746 scope.liveLink = function (evt, val1, val2) {
4747 scope.itemIdx = val1;
4748 scope.navIdx = val2;
4749 evt.stopPropagation();
4756 * @name Buttons, links & UI controls.att:links
4759 * <file src="src/links/docs/readme.md" />
4761 * <!-- See below examples for link implementation -->
4765 <b>HTML + AngularJS</b>
4766 <example module="b2b.att">
4767 <file src="src/links/docs/demo.html" />
4768 <file src="src/links/docs/demo.js" />
4772 angular.module('b2b.att.links', []);
4775 * @name Misc.att:listbox
4778 * <file src="src/listbox/docs/readme.md" />
4780 * @param {int} currentIndex - Current index of selected listbox item. Is not supported on multiselect listbox
4781 * @param {Array} listboxData - Data of listbox items. Should include full data regardless if HTML will be filtered.
4784 * <section id="code">
4785 <example module="b2b.att">
4786 <file src="src/listbox/docs/demo.html" />
4787 <file src="src/listbox/docs/demo.js" />
4792 angular.module('b2b.att.listbox', ['b2b.att.utilities'])
4793 .directive('b2bListBox', ['keymap', 'b2bDOMHelper', '$rootScope', function(keymap, b2bDOMHelper, $rootScope) {
4802 templateUrl: 'b2bTemplate/listbox/listbox.html',
4803 link: function(scope, elem, attr) {
4805 if (attr.ariaMultiselectable !== undefined || attr.ariaMultiselectable === 'true') {
4806 scope.multiselectable = true;
4808 scope.multiselectable = false;
4811 var shiftKey = false;
4813 var prevDirection = undefined; // previous direction is used for an edge case when shifting
4814 var shiftKeyPressed = false; // Used to handle shift clicking
4815 var ctrlKeyPressed = false;
4817 var currentIndexSet = {
4819 'listboxDataIndex': 0
4822 function isTrue(item) {
4823 if (item.selected === true) {
4828 function incrementIndex(elem) {
4829 $rootScope.$apply();
4831 var nextElem = elem.next();
4832 if (!angular.isDefined(nextElem) || nextElem.length === 0) {
4836 currentIndexSet.elementIndex += 1;
4837 currentIndexSet.listboxDataIndex = parseInt(nextElem.attr('data-index'), 10);
4838 scope.currentIndex = currentIndexSet.listboxDataIndex;
4840 if (currentIndexSet.elementIndex >= elements.length - 1) {
4841 currentIndexSet.elementIndex = elements.length-1;
4845 function decrementIndex(elem) {
4846 $rootScope.$apply();
4847 var prevElem = angular.element(b2bDOMHelper.previousElement(elem));
4848 if (!angular.isDefined(prevElem) || prevElem.length === 0) {
4852 currentIndexSet.elementIndex -= 1;
4853 currentIndexSet.listboxDataIndex = parseInt(prevElem.attr('data-index'), 10);
4854 scope.currentIndex = currentIndexSet.listboxDataIndex;
4856 if (currentIndexSet.elementIndex <= 0) {
4857 currentIndexSet.elementIndex = 0;
4861 var focusOnElement = function(index) {
4863 elements[index].focus();
4867 function selectItems(startIndex, endIndex, forceValue) {
4868 for (var i = startIndex; i < endIndex; i++) {
4869 if (forceValue === undefined) {
4870 // We will flip the value
4871 scope.listboxData[i].selected = !scope.listboxData[i].selected;
4873 scope.listboxData[i].selected = forceValue;
4877 if (!scope.$$phase) {
4882 elem.bind('focus', function(evt) {
4883 // If multiselectable or not and nothing is selected, put focus on first element
4884 // If multiselectable and a range is set, put focus on first element of range
4885 // If not multiselectable and something selected, put focus on element
4886 elements = elem.children();
4887 var selectedItems = scope.listboxData.filter(isTrue);
4888 var elementsIndies = Array.prototype.map.call(elements, function(item) {
4889 return parseInt(angular.element(item).attr('data-index'), 10);
4892 if (selectedItems.length == 0) {
4894 currentIndexSet.listboxDataIndex = 0;
4895 } else if (attr.ariaMultiselectable) {
4896 var index = scope.listboxData.indexOf(selectedItems[0]);
4897 var indies = elementsIndies.filter(function(item) {
4898 return (item === index);
4901 if (indies.length === 0 || indies[0] != index) {
4903 currentIndexSet.elementIndex = elementsIndies[0];
4904 currentIndexSet.listboxDataIndex = 0;
4905 focusOnElement(currentIndexSet.elementIndex);
4907 focusOnElement(indies[0]);
4908 currentIndexSet.elementIndex = indies[0];
4909 currentIndexSet.listboxDataIndex = index;
4912 focusOnElement(currentIndexSet.elementIndex);
4914 scope.currentIndex = currentIndexSet.listboxDataIndex;
4916 if (!scope.$$phase) {
4920 elem.bind('keyup', function(evt) {
4921 if (evt.keyCode === keymap.KEY.SHIFT) {
4922 shiftKeyPressed = false;
4923 } else if (evt.keyCode === keymap.KEY.CTRL) {
4924 ctrlKeyPressed = false;
4928 elem.bind('keydown', function(evt) {
4929 var keyCode = evt.keyCode;
4930 elements = elem.children();
4931 if (keyCode === keymap.KEY.SHIFT) {
4932 shiftKeyPressed = true;
4933 } else if (evt.keyCode === keymap.KEY.CTRL) {
4934 ctrlKeyPressed = true;
4940 if (scope.multiselectable && evt.ctrlKey) {
4941 var arr = scope.listboxData.filter(isTrue);
4942 var elementsIndies = Array.prototype.map.call(elements, function(item) {
4943 return parseInt(angular.element(item).attr('data-index'), 10);
4945 var val = !(arr.length === scope.listboxData.length);
4946 for (var i = 0; i < elementsIndies.length; i++) {
4947 scope.listboxData[elementsIndies[i]].selected = val;
4950 if (!scope.$$phase) {
4954 evt.preventDefault();
4955 evt.stopPropagation();
4959 case keymap.KEY.END:
4961 if (scope.multiselectable && evt.ctrlKey && evt.shiftKey) {
4962 var elementsIndies = Array.prototype.map.call(elements, function(item) {
4963 return parseInt(angular.element(item).attr('data-index'), 10);
4964 }).filter(function(item) {
4965 return (item >= currentIndexSet.listboxDataIndex);
4967 for (var i = 0; i < elementsIndies.length; i++) {
4968 scope.listboxData[elementsIndies[i]].selected = true;
4970 evt.preventDefault();
4971 evt.stopPropagation();
4973 if (!scope.$$phase) {
4979 case keymap.KEY.HOME:
4981 if (scope.multiselectable && evt.ctrlKey && evt.shiftKey) {
4982 selectItems(0, currentIndexSet.listboxDataIndex+1, true); // currentIndex+1 is what is being focused on
4983 evt.preventDefault();
4984 evt.stopPropagation();
4988 case keymap.KEY.LEFT:
4991 if (currentIndexSet.listboxDataIndex === 0) {
4992 evt.preventDefault();
4993 evt.stopPropagation();
4997 decrementIndex(elements.eq(currentIndexSet.elementIndex));
4998 if (scope.multiselectable && (evt.shiftKey || evt.ctrlKey)) {
5000 if (prevDirection === 'DOWN') {
5001 scope.listboxData[currentIndexSet.listboxDataIndex+1].selected = !scope.listboxData[currentIndexSet.listboxDataIndex+1].selected;
5003 scope.listboxData[currentIndexSet.listboxDataIndex].selected = !scope.listboxData[currentIndexSet.listboxDataIndex].selected;
5005 prevDirection = 'UP';
5007 // If no modifier keys are selected, all other items need to be unselected.
5008 prevDirection = undefined;
5009 selectItems(0, scope.listboxData.length, false);
5010 if(currentIndexSet.listboxDataIndex !== undefined && !isNaN(currentIndexSet.listboxDataIndex)){
5011 scope.listboxData[currentIndexSet.listboxDataIndex].selected = true;
5014 focusOnElement(currentIndexSet.elementIndex);
5015 if(!scope.$$phase) {
5018 evt.preventDefault();
5019 evt.stopPropagation();
5022 case keymap.KEY.RIGHT:
5023 case keymap.KEY.DOWN:
5025 if (currentIndexSet.listboxDataIndex === scope.listboxData.length-1) {
5026 evt.preventDefault();
5027 evt.stopPropagation();
5031 incrementIndex(elements.eq(currentIndexSet.elementIndex));
5033 if (scope.multiselectable && (evt.shiftKey || evt.ctrlKey)) {
5035 if (prevDirection === 'UP') {
5036 scope.listboxData[currentIndexSet.listboxDataIndex-1].selected = !scope.listboxData[currentIndexSet.listboxDataIndex-1].selected;
5039 scope.listboxData[currentIndexSet.listboxDataIndex].selected = !scope.listboxData[currentIndexSet.listboxDataIndex].selected;
5041 prevDirection = 'DOWN';
5043 // If no modifier keys are selected, all other items need to be unselected.
5044 prevDirection = undefined;
5045 selectItems(0, scope.listboxData.length, false);
5046 if(currentIndexSet.listboxDataIndex !== undefined && !isNaN(currentIndexSet.listboxDataIndex)){
5047 scope.listboxData[currentIndexSet.listboxDataIndex].selected = true;
5051 focusOnElement(currentIndexSet.elementIndex);
5052 if(!scope.$$phase) {
5055 evt.preventDefault();
5056 evt.stopPropagation();
5059 case keymap.KEY.TAB:
5061 var previousElement = b2bDOMHelper.previousElement(elem.parent().parent(), true);
5062 evt.preventDefault();
5063 previousElement.focus();
5071 elem.bind('click', function(evt) {
5072 var index = parseInt(evt.target.dataset.index, 10);
5073 if (index === undefined || isNaN(index)) {
5076 if (scope.multiselectable && currentIndexSet.listboxDataIndex !== undefined) {
5077 if (shiftKeyPressed) {
5078 var min = Math.min(index, currentIndexSet.listboxDataIndex);
5079 var max = Math.max(index, currentIndexSet.listboxDataIndex);
5081 if (index === min) { // clicking up
5082 var firstIndex = scope.listboxData.findIndex(function(item) { return item.selected === true;});
5083 // Given the firstIndex, let's find the matching element to get proper element match
5084 elements = elem.children();
5085 elements.eq(firstIndex)
5086 var elementsThatMatch = Array.prototype.filter.call(elements, function(item) {
5087 if (parseInt(angular.element(item).attr('data-index'), 10) === firstIndex) {
5091 firstIndex = parseInt(angular.element(elementsThatMatch).attr('data-index'), 10);
5093 if (index <= firstIndex && scope.listboxData.filter(isTrue).length > 1) {
5094 // Break the selection into 2
5095 selectItems(firstIndex + 1, max + 1, undefined); // + 1 needed because selectItems only selects up to MAX
5096 selectItems(min, firstIndex, undefined);
5097 } else if (scope.listboxData.filter(isTrue).length == 1){
5098 selectItems(min, max, undefined);
5100 selectItems(min + 1, max + 1, undefined);
5102 } else { // clicking down
5103 selectItems(min + 1, max + 1, scope.listboxData[min].selected);
5105 } else if (ctrlKeyPressed) {
5106 scope.listboxData[index].selected = !scope.listboxData[index].selected;
5108 selectItems(0, scope.listboxData.length, false);
5109 scope.listboxData[index].selected = !scope.listboxData[index].selected;
5112 selectItems(0, scope.listboxData.length, false);
5113 scope.listboxData[index].selected = !scope.listboxData[index].selected;
5115 currentIndexSet.elementIndex = index;
5116 currentIndexSet.listboxDataIndex = index;
5117 scope.currentIndex = currentIndexSet.listboxDataIndex;
5118 if (!scope.$$phase) {
5121 focusOnElement(index);
5128 * @name Videos, audio & animation.att:loaderAnimation
5131 * <file src="src/loaderAnimation/docs/readme.md" />
5134 * <!-- Below demo js shows-->
5135 * Angular library uses Global.css's icon-primary-spinner.
5138 * <section id="code">
5139 <example module="b2b.att">
5140 <file src="src/loaderAnimation/docs/demo.html" />
5141 <file src="src/loaderAnimation/docs/demo.js" />
5146 angular.module('b2b.att.loaderAnimation', [])
5147 .constant('b2bSpinnerConfig', {
5148 loadingText: 'Loading...',
5149 startEvent: 'startButtonSpinner',
5150 stopEvent: 'stopButtonSpinner'
5152 .constant("progressTrackerConfig", {
5153 loadingText: 'Loading...',
5155 activationDelay: "",
5156 minDurationPromise: "",
5157 activationDelayPromise: ""
5160 .provider('progressTracker', function () {
5161 this.$get = ['$q', '$timeout', function ($q, $timeout) {
5162 function cancelTimeout(promise) {
5164 $timeout.cancel(promise);
5167 return function ProgressTracker(options) {
5168 //do new if user doesn't
5169 if (!(this instanceof ProgressTracker)) {
5170 return new ProgressTracker(options);
5173 options = options || {};
5174 //Array of promises being tracked
5177 //Allow an optional "minimum duration" that the tracker has to stay active for.
5178 var minDuration = options.minDuration;
5179 //Allow a delay that will stop the tracker from activating until that time is reached
5180 var activationDelay = options.activationDelay;
5181 var minDurationPromise;
5182 var activationDelayPromise;
5183 self.active = function () {
5184 //Even if we have a promise in our tracker, we aren't active until delay is elapsed
5185 if (activationDelayPromise) {
5188 return tracked.length > 0;
5190 self.tracking = function () {
5191 //Even if we aren't active, we could still have a promise in our tracker
5192 return tracked.length > 0;
5194 self.destroy = self.cancel = function () {
5195 minDurationPromise = cancelTimeout(minDurationPromise);
5196 activationDelayPromise = cancelTimeout(activationDelayPromise);
5197 for (var i = tracked.length - 1; i >= 0; i--) {
5198 tracked[i].resolve();
5202 //Create a promise that will make our tracker active until it is resolved.
5203 // @return deferred - our deferred object that is being tracked
5204 self.createPromise = function () {
5205 var deferred = $q.defer();
5206 tracked.push(deferred);
5207 //If the tracker was just inactive and this the first in the list of promises, we reset our delay and minDuration again.
5208 if (tracked.length === 1) {
5209 if (activationDelay) {
5210 activationDelayPromise = $timeout(function () {
5211 activationDelayPromise = cancelTimeout(activationDelayPromise);
5213 }, activationDelay);
5218 deferred.promise.then(onDone(false), onDone(true));
5221 function startMinDuration() {
5223 minDurationPromise = $timeout(angular.noop, minDuration);
5226 //Create a callback for when this promise is done. It will remove our tracked promise from the array if once minDuration is complete
5228 return function () {
5229 (minDurationPromise || $q.when()).then(function () {
5230 var index = tracked.indexOf(deferred);
5231 tracked.splice(index, 1);
5232 //If this is the last promise, cleanup the timeouts for activationDelay
5233 if (tracked.length === 0) {
5234 activationDelayPromise = cancelTimeout(activationDelayPromise);
5240 self.addPromise = function (promise) {
5242 // we cannot assign then function in other var and then add the resolve and reject
5243 var thenFxn = promise && (promise.then || promise.$then || (promise.$promise && promise.$promise.then));
5245 throw new Error("progressTracker expects a promise object :: Not found");
5247 var deferred = self.createPromise();
5248 //When given promise is done, resolve our created promise
5249 //Allow $then for angular-resource objects
5251 promise.then(function (value) {
5252 deferred.resolve(value);
5254 }, function (value) {
5255 deferred.reject(value);
5256 return $q.reject(value);
5265 .config(['$httpProvider', function ($httpProvider) {
5266 $httpProvider.interceptors.push(['$q', 'progressTracker', function ($q) {
5268 request: function (config) {
5269 if (config.tracker) {
5270 if (!angular.isArray(config.tracker)) {
5271 config.tracker = [config.tracker];
5273 config.$promiseTrackerDeferred = config.$promiseTrackerDeferred || [];
5275 angular.forEach(config.tracker, function (tracker) {
5276 var deferred = tracker.createPromise();
5277 config.$promiseTrackerDeferred.push(deferred);
5280 return $q.when(config);
5282 response: function (response) {
5283 if (response.config && response.config.$promiseTrackerDeferred) {
5284 angular.forEach(response.config.$promiseTrackerDeferred, function (deferred) {
5285 deferred.resolve(response);
5288 return $q.when(response);
5290 responseError: function (response) {
5291 if (response.config && response.config.$promiseTrackerDeferred) {
5292 angular.forEach(response.config.$promiseTrackerDeferred, function (deferred) {
5293 deferred.reject(response);
5296 return $q.reject(response);
5302 .directive('b2bClickSpin', ['$timeout', '$parse', '$rootScope', 'progressTracker', function ($timeout, $parse, $rootScope, progressTracker) {
5305 link: function (scope, elm, attrs) {
5306 var fn = $parse(attrs.b2bClickSpin);
5307 elm.on('click', function (event) {
5308 var promise = $timeout(function () {console.log("inside Promise")}, 5000);
5309 scope.$apply(function () {
5314 //comment this line if not running unit test
5315 $rootScope.loadingTracker = progressTracker({
5318 $rootScope.loadingTracker.addPromise(promise);
5319 angular.forEach("$routeChangeSuccess $viewContentLoaded $locationChangeSuccess".split(" "), function (event) {
5320 $rootScope.$on(event, function () {
5322 $timeout.cancel(promise);
5330 .directive('b2bProgressTracker', ['progressTrackerConfig', function (ptc) {
5334 template: '<div><div ng-show="loadingTracker.active()" style="width:100%; text-align:center"><i class=\"icon-primary-spinner\"></i></div><div ng-show="loadingTracker.active()" style="width:100%;margin-top:10px; text-align:center">'+ ptc.loadingText+'</div></div>'
5338 .directive('b2bLoadButton', ['b2bSpinnerConfig', '$timeout', function (spinnerConfig, $timeout) {
5339 var spinButton = function (state, element, data) {
5341 var attr = element.html() ? 'html' : 'val';
5342 state = state + 'Text';
5343 if (state === 'loadingText') {
5344 element[attr](data[state]);
5345 element.attr("disabled",'disabled');
5346 element.addClass('disabled');
5347 } else if (state === 'resetText') {
5348 element[attr](data[state]);
5349 element.removeAttr("disabled");
5350 element.removeClass('disabled');
5358 promise: '=promise',
5359 startEvent: '@startEvent',
5360 stopEvent: '@stopEvent'
5362 link: function (scope, element, attr) {
5363 var validAttr = element.html() ? 'html' : 'val';
5369 var updateLoadingText = function (val) {
5370 var loadingText = val;
5371 if (!angular.isDefined(loadingText) || loadingText === "") {
5372 loadingText = spinnerConfig.loadingText;
5374 data.loadingText = validAttr === 'html' ? "<i class=\"icon-primary-spinner small\"></i>" + loadingText : loadingText;
5376 var updateResetText = function (val) {
5377 data.resetText = val;
5380 attr.$observe('b2bLoadButton', function (val) {
5381 updateLoadingText(val);
5383 $timeout(function () {
5384 updateResetText(element[validAttr]());
5387 if (!angular.isDefined(scope.startEvent) || scope.startEvent === "") {
5388 scope.startEvent = spinnerConfig.startEvent;
5391 if (!angular.isDefined(scope.stopEvent) || scope.stopEvent === "") {
5392 scope.stopEvent = spinnerConfig.stopEvent;
5395 scope.$watch('promise', function () {
5396 if (angular.isDefined(scope.promise) && angular.isFunction(scope.promise.then)) {
5397 spinButton('loading', element, data);
5398 scope.promise.then(function () {
5399 spinButton('reset', element, data);
5401 spinButton('reset', element, data);
5406 scope.$on(scope.startEvent, function () {
5407 spinButton('loading', element, data);
5408 scope.$on(scope.stopEvent, function () {
5409 spinButton('reset', element, data);
5417 * @name Misc.att:messageWrapper
5419 * @param {boolean} trigger - A boolean that triggers directive to switch focus
5420 * @param {integer} delay - Extra delay added to trigger code to allow for DOM to be ready. Default is 10ms.
5421 * @param {string} noFocus - Attribute-based API to trigger whether first focusable element receives focus on trigger or whole message (assumes tabindex="-1" set on first child)
5422 * @param {string} trapFocus - Attribute-based API to trap focus within the message. This should be enabled by default on all toast messages.
5424 * <file src="src/messageWrapper/docs/readme.md" />
5426 * <b2b-message-wrapper>Code that contains at least one focusable element and will be shown/hidden on some logic. This must have tabindex="-1".</b2b-message-wrapper>
5429 * <section id="code">
5430 <b>HTML + AngularJS</b>
5431 <example module="b2b.att">
5432 <file src="src/messageWrapper/docs/demo.html" />
5433 <file src="src/messageWrapper/docs/demo.js" />
5438 angular.module('b2b.att.messageWrapper', ['b2b.att.utilities'])
5439 .directive('b2bMessageWrapper', ['b2bDOMHelper', '$compile', '$timeout', '$log', function(b2bDOMHelper, $compile, $timeout, $log) {
5448 template: '<div ng-transclude></div>',
5449 link: function(scope, elem, attrs) {
5450 scope.delay = scope.delay || 10;
5452 if (attrs.trapFocus != undefined && !elem.children().eq(0).attr('b2b-trap-focus-inside-element')) {
5453 // Append b2bTrapFocusInsideElement onto first child and recompile
5454 elem.children().eq(0).attr('b2b-trap-focus-inside-element', 'false');
5455 elem.children().eq(0).attr('trigger', scope.trigger);
5456 $compile(elem.contents())(scope);
5459 var firstElement = undefined,
5460 launchingElement = undefined;
5462 scope.$watch('trigger', function(oldVal, newVal) {
5463 if (oldVal === newVal) return;
5464 if (!angular.isDefined(launchingElement)) {
5465 launchingElement = document.activeElement;
5467 $timeout(function() {
5468 if (scope.trigger) {
5470 if (attrs.noFocus === true || attrs.noFocus === "") {
5471 elem.children()[0].focus();
5473 firstElement = b2bDOMHelper.firstTabableElement(elem);
5475 if (angular.isDefined(firstElement)) {
5476 firstElement.focus();
5481 if (angular.isDefined(launchingElement) && launchingElement.nodeName !== 'BODY') {
5482 if (launchingElement === document.activeElement) {
5486 if (b2bDOMHelper.isInDOM(launchingElement) && b2bDOMHelper.isTabable(launchingElement)) {
5487 // At this point, launchingElement is still a valid element, but focus will fail and
5488 // activeElement will become body, hence we want to apply custom logic and find previousElement
5489 var prevLaunchingElement = launchingElement;
5490 launchingElement.focus();
5492 if (document.activeElement !== launchingElement || document.activeElement.nodeName === 'BODY') {
5493 launchingElement = b2bDOMHelper.previousElement(angular.element(prevLaunchingElement), true);
5494 launchingElement.focus();
5497 launchingElement = b2bDOMHelper.previousElement(launchingElement, true);
5498 launchingElement.focus();
5509 * @name Messages, modals & alerts.att:modalsAndAlerts
5512 * <file src="src/modalsAndAlerts/docs/readme.md" />
5515 * <button class="btn" b2b-modal="b2bTemplate/modalsAndAlerts/demo_modal.html" modal-ok="ok()" modal-cancel="cancel()">Launch demo modal</button>
5518 * <section id="code">
5519 <example module="b2b.att">
5520 <file src="src/modalsAndAlerts/docs/demo.html" />
5521 <file src="src/modalsAndAlerts/docs/demo.js" />
5526 angular.module('b2b.att.modalsAndAlerts', ['b2b.att.position', 'b2b.att.transition', 'b2b.att.utilities'])
5529 * A helper, internal data structure that acts as a map but also allows getting / removing
5530 * elements in the LIFO order
5532 .factory('$$stackedMap', function () {
5534 createNew: function () {
5538 add: function (key, value) {
5544 get: function (key) {
5545 for (var i = 0; i < stack.length; i++) {
5546 if (key === stack[i].key) {
5553 for (var i = 0; i < stack.length; i++) {
5554 keys.push(stack[i].key);
5559 return stack[stack.length - 1];
5561 remove: function (key) {
5563 for (var i = 0; i < stack.length; i++) {
5564 if (key === stack[i].key) {
5569 return stack.splice(idx, 1)[0];
5571 removeTop: function () {
5572 return stack.splice(stack.length - 1, 1)[0];
5574 length: function () {
5575 return stack.length;
5580 }).factory('trapFocusInElement', ['$document', '$isElement', 'b2bDOMHelper', 'keymap', function ($document, $isElement, b2bDOMHelper, keymap) {
5581 var elementStack = [];
5582 var stackHead = undefined;
5583 var firstTabableElement, lastTabableElement;
5585 var trapKeyboardFocusInFirstElement = function (e) {
5587 e.keyCode = e.which;
5590 if (e.shiftKey === true && e.keyCode === keymap.KEY.TAB) {
5591 lastTabableElement[0].focus();
5592 e.preventDefault(e);
5593 e.stopPropagation(e);
5598 var trapKeyboardFocusInLastElement = function (e) {
5600 e.keyCode = e.which;
5603 if (e.shiftKey === false && e.keyCode === keymap.KEY.TAB) {
5604 firstTabableElement[0].focus();
5605 e.preventDefault(e);
5606 e.stopPropagation(e);
5610 var trapFocusInElement = function (flag, firstTabableElementParam, lastTabableElementParam) {
5611 var bodyElements = $document.find('body').children();
5613 firstTabableElement = firstTabableElementParam ? firstTabableElementParam : angular.element(b2bDOMHelper.firstTabableElement(stackHead));
5614 lastTabableElement = lastTabableElementParam ? lastTabableElementParam : angular.element(b2bDOMHelper.lastTabableElement(stackHead));
5617 for (var i = 0; i < bodyElements.length; i++) {
5618 if (bodyElements[i] !== stackHead[0]) {
5619 bodyElements.eq(i).attr('aria-hidden', true);
5622 firstTabableElement.bind('keydown', trapKeyboardFocusInFirstElement);
5623 lastTabableElement.bind('keydown', trapKeyboardFocusInLastElement);
5625 for (var j = 0; j < bodyElements.length; j++) {
5626 if (bodyElements[j] !== stackHead[0]) {
5627 bodyElements.eq(j).removeAttr('aria-hidden');
5630 firstTabableElement.unbind('keydown', trapKeyboardFocusInFirstElement);
5631 lastTabableElement.unbind('keydown', trapKeyboardFocusInLastElement);
5634 var toggleTrapFocusInElement = function (flag, element) {
5635 if (angular.isDefined(flag) && angular.isDefined(element)) {
5636 if (angular.isUndefined(stackHead)) {
5637 stackHead = element;
5638 trapFocusInElement(flag);
5641 trapFocusInElement(false);
5642 elementStack.push(stackHead);
5643 stackHead = element;
5644 trapFocusInElement(true);
5646 if (stackHead.prop('$$hashKey') === element.prop('$$hashKey')) {
5647 trapFocusInElement(false);
5648 stackHead = elementStack.pop();
5649 if (angular.isDefined(stackHead)) {
5650 trapFocusInElement(true);
5656 if (angular.isDefined(stackHead)) {
5657 trapFocusInElement(false, firstTabableElement, lastTabableElement);
5658 trapFocusInElement(true);
5663 return toggleTrapFocusInElement;
5667 * A helper directive for the $modal service. It creates a backdrop element.
5669 .directive('b2bModalBackdrop', ['$modalStack', '$timeout', function ($modalStack, $timeout) {
5673 templateUrl: 'b2bTemplate/modalsAndAlerts/b2b-backdrop.html',
5674 link: function (scope, element, attrs) {
5675 scope.close = function (evt) {
5676 var modal = $modalStack.getTop();
5677 if (modal && modal.value.backdrop && modal.value.backdrop !== 'static') {
5678 evt.preventDefault();
5679 evt.stopPropagation();
5680 $modalStack.dismiss(modal.key, 'backdrop click');
5687 .directive('b2bModalWindow', ['$timeout', 'windowOrientation', '$window', 'keymap', function ($timeout, windowOrientation, $window, keymap) {
5695 templateUrl: 'b2bTemplate/modalsAndAlerts/b2b-window.html',
5696 controller: ['$scope', '$element', '$attrs', function (scope, element, attrs) {
5697 scope.windowClass = attrs.windowClass || '';
5698 scope.sizeClass = attrs.sizeClass || '';
5699 scope.isNotifDialog = false;
5700 scope.modalClose = attrs.modalClose || false;
5702 this.setTitle = function (title) {
5703 scope.title = title;
5705 this.setContent = function (content) {
5706 scope.content = content;
5707 scope.isNotifDialog = true;
5709 this.isDockedModal = scope.windowClass.indexOf('modal-docked') > -1;
5711 link: function (scope, element, attrs, ctrl) {
5712 if (ctrl.isDockedModal) {
5713 scope.isModalLandscape = false;
5715 var window = angular.element($window);
5716 scope.updateCss = function () {
5717 if (windowOrientation.isPotrait()) { // Potrait Mode
5718 scope.isModalLandscape = false;
5719 } else if (windowOrientation.isLandscape()) { // Landscape Mode
5720 scope.isModalLandscape = true;
5724 $timeout(function () {
5728 window.bind('orientationchange', function () {
5732 window.bind('resize', function () {
5737 angular.element(element[0].querySelectorAll(".awd-select-list")).css({
5738 "max-height": "200px"
5742 var isIE = /msie|trident/i.test(navigator.userAgent);
5744 if(angular.element(element[0].querySelector('.corner-button button.close')).length > 0){
5745 angular.element(element[0].querySelector('.corner-button button.close')).bind('focus', function () {
5746 angular.element(element[0].querySelector('.b2b-modal-header'))[0].scrollLeft = 0;
5747 angular.element(element[0].querySelector('.b2b-modal-header'))[0].scrollTop = 0;
5752 if(scope.modalClose){
5753 element.bind('keydown', function (e) {
5754 if(e.keyCode == keymap.KEY.ESC){
5756 e.stopPropagation();
5764 .directive('b2bModalTitle', [function () {
5767 require: '^b2bModalWindow',
5768 link: function (scope, elem, attr, ctrl) {
5769 ctrl.setTitle(attr.id);
5774 .directive('b2bModalContent', [function () {
5777 require: '^b2bModalWindow',
5778 link: function (scope, elem, attr, ctrl) {
5779 ctrl.setContent(attr.id);
5785 .directive('b2bModalBody', ['$timeout', '$position', '$document', '$window', 'windowOrientation', 'b2bAwdBreakpoints', function ($timeout, $position, $document, $window, windowOrientation, b2bAwdBreakpoints) {
5791 require: '^b2bModalWindow',
5792 link: function (scope, element, attrs, ctrl) {
5793 var window = angular.element($window);
5794 var body = $document.find('body').eq(0);
5795 scope.setModalHeight = function () {
5796 var modalHeaderHeight, modalFooterHeight, modalBodyHeight, windowHeight, windowWidth, modalHeight;
5797 modalHeaderHeight = 0;
5798 modalFooterHeight = 0;
5799 windowHeight = $window.innerHeight;
5800 windowWidth = $window.innerWidth;
5802 'height': windowHeight + 'px'
5805 if (ctrl.isDockedModal) {
5806 var modalElements = element.parent().children();
5807 for (var i = 0; i < modalElements.length; i++) {
5808 if (modalElements.eq(i).hasClass('b2b-modal-header')) {
5809 modalHeaderHeight = $position.position(modalElements.eq(i)).height;
5810 } else if (modalElements.eq(i).hasClass('b2b-modal-footer')) {
5811 modalFooterHeight = $position.position(modalElements.eq(i)).height;
5815 modalHeight = $position.position(element.parent()).height;
5817 modalBodyHeight = modalHeight - (modalHeaderHeight + modalFooterHeight) + 'px';
5819 if (windowOrientation.isPotrait()) { // Potrait Mode
5820 element.removeAttr('style').css({
5821 height: modalBodyHeight
5823 } else if (windowOrientation.isLandscape() && windowWidth < b2bAwdBreakpoints.breakpoints.mobile.max) { // Landscape Mode Mobile
5824 element.removeAttr('style');
5825 } else if (windowOrientation.isLandscape() && windowWidth >= b2bAwdBreakpoints.breakpoints.mobile.max) { // Landscape Mode Non-Mobile
5826 element.removeAttr('style').css({
5827 height: modalBodyHeight
5833 $timeout(function () {
5834 scope.setModalHeight();
5837 window.bind('orientationchange', function () {
5838 scope.setModalHeight();
5841 window.bind('resize', function () {
5842 scope.setModalHeight();
5849 .directive('b2bModalFooter', ['windowOrientation', '$window', function (windowOrientation, $window) {
5855 link: function (scope, element, attrs) {
5861 .factory('$modalStack', ['$document', '$compile', '$rootScope', '$$stackedMap', '$log', '$timeout', 'trapFocusInElement', function ($document, $compile, $rootScope, $$stackedMap, $log, $timeout, trapFocusInElement) {
5862 var backdropjqLiteEl, backdropDomEl;
5863 var backdropScope = $rootScope.$new(true);
5864 var body = $document.find('body').eq(0);
5865 var html = $document.find('html').eq(0);
5866 var openedWindows = $$stackedMap.createNew();
5867 var $modalStack = {};
5869 function backdropIndex() {
5870 var topBackdropIndex = -1;
5871 var opened = openedWindows.keys();
5872 for (var i = 0; i < opened.length; i++) {
5873 if (openedWindows.get(opened[i]).value.backdrop) {
5874 topBackdropIndex = i;
5877 return topBackdropIndex;
5880 $rootScope.$watch(backdropIndex, function (newBackdropIndex) {
5881 backdropScope.index = newBackdropIndex;
5884 function removeModalWindow(modalInstance) {
5885 //background scroll fix
5886 html.removeAttr('style');
5887 body.removeAttr('style');
5888 body.removeClass('styled-by-modal');
5890 var modalWindow = openedWindows.get(modalInstance).value;
5891 trapFocusInElement(false, modalWindow.modalDomEl);
5893 //clean up the stack
5894 openedWindows.remove(modalInstance);
5896 //remove window DOM element
5897 modalWindow.modalDomEl.remove();
5899 //remove backdrop if no longer needed
5900 if (backdropDomEl && backdropIndex() === -1) {
5901 backdropDomEl.remove();
5902 backdropDomEl = undefined;
5906 modalWindow.modalScope.$destroy();
5909 $document.bind('keydown', function (evt) {
5912 if (evt.which === 27) {
5913 modal = openedWindows.top();
5914 if (modal && modal.value.keyboard) {
5915 $rootScope.$apply(function () {
5916 $modalStack.dismiss(modal.key);
5922 $modalStack.open = function (modalInstance, modal) {
5924 openedWindows.add(modalInstance, {
5925 deferred: modal.deferred,
5926 modalScope: modal.scope,
5927 backdrop: modal.backdrop,
5928 keyboard: modal.keyboard
5931 var angularDomEl = angular.element('<div b2b-modal-window></div>');
5932 angularDomEl.attr('window-class', modal.windowClass);
5933 angularDomEl.attr('size-class', modal.sizeClass);
5934 angularDomEl.attr('index', openedWindows.length() - 1);
5935 angularDomEl.attr('modal-close', modal.modalClose);
5936 angularDomEl.html(modal.content);
5938 var modalDomEl = $compile(angularDomEl)(modal.scope);
5939 openedWindows.top().value.modalDomEl = modalDomEl;
5940 //background page scroll fix
5942 'overflow-y': 'hidden'
5945 'overflow-y': 'hidden',
5947 'height': window.innerHeight + 'px'
5949 body.addClass('styled-by-modal');
5950 body.append(modalDomEl);
5952 if (backdropIndex() >= 0 && !backdropDomEl) {
5953 backdropjqLiteEl = angular.element('<div b2b-modal-backdrop></div>');
5954 backdropDomEl = $compile(backdropjqLiteEl)(backdropScope);
5955 body.append(backdropDomEl);
5958 $timeout(function () {
5960 if (modal.scope.$$childHead.isNotifDialog) {
5961 angular.element(modalDomEl).find('button')[0].focus();
5963 angular.element(modalDomEl)[0].focus();
5965 trapFocusInElement(true, angular.element(modalDomEl).eq(0));
5969 $modalStack.close = function (modalInstance, result) {
5970 var modal = openedWindows.get(modalInstance);
5972 modal.value.deferred.resolve(result);
5973 removeModalWindow(modalInstance);
5977 $modalStack.dismiss = function (modalInstance, reason) {
5978 var modalWindow = openedWindows.get(modalInstance).value;
5980 modalWindow.deferred.reject(reason);
5981 removeModalWindow(modalInstance);
5985 $modalStack.getTop = function () {
5986 return openedWindows.top();
5992 .provider('$modal', function () {
5993 var $modalProvider = {
5995 backdrop: true, //can be also false or 'static'
5998 $get: ['$injector', '$rootScope', '$q', '$http', '$templateCache', '$controller', '$modalStack', function ($injector, $rootScope, $q, $http, $templateCache, $controller, $modalStack) {
6001 function getTemplatePromise(options) {
6002 return options.template ? $q.when(options.template) :
6003 $http.get(options.templateUrl, {
6004 cache: $templateCache
6005 }).then(function (result) {
6010 function getResolvePromises(resolves) {
6011 var promisesArr = [];
6012 angular.forEach(resolves, function (value, key) {
6013 if (angular.isFunction(value) || angular.isArray(value)) {
6014 promisesArr.push($q.when($injector.invoke(value)));
6020 $modal.open = function (modalOptions) {
6022 var modalResultDeferred = $q.defer();
6023 var modalOpenedDeferred = $q.defer();
6024 //prepare an instance of a modal to be injected into controllers and returned to a caller
6025 var modalInstance = {
6026 result: modalResultDeferred.promise,
6027 opened: modalOpenedDeferred.promise,
6028 close: function (result) {
6029 $modalStack.close(modalInstance, result);
6031 dismiss: function (reason) {
6032 $modalStack.dismiss(modalInstance, reason);
6036 //merge and clean up options
6037 modalOptions = angular.extend({}, $modalProvider.options, modalOptions);
6038 modalOptions.resolve = modalOptions.resolve || {};
6041 if (!modalOptions.template && !modalOptions.templateUrl) {
6042 throw new Error('One of template or templateUrl options is required.');
6045 var templateAndResolvePromise =
6046 $q.all([getTemplatePromise(modalOptions)].concat(getResolvePromises(modalOptions.resolve)));
6049 templateAndResolvePromise.then(function resolveSuccess(tplAndVars) {
6051 var modalScope = (modalOptions.scope || $rootScope).$new();
6052 modalScope.$close = modalInstance.close;
6053 modalScope.$dismiss = modalInstance.dismiss;
6055 var ctrlInstance, ctrlLocals = {};
6056 var resolveIter = 1;
6059 if (modalOptions.controller) {
6060 ctrlLocals.$scope = modalScope;
6061 ctrlLocals.$modalInstance = modalInstance;
6062 angular.forEach(modalOptions.resolve, function (value, key) {
6063 ctrlLocals[key] = tplAndVars[resolveIter++];
6066 ctrlInstance = $controller(modalOptions.controller, ctrlLocals);
6069 $modalStack.open(modalInstance, {
6071 deferred: modalResultDeferred,
6072 content: tplAndVars[0],
6073 backdrop: modalOptions.backdrop,
6074 keyboard: modalOptions.keyboard,
6075 windowClass: modalOptions.windowClass,
6076 sizeClass: modalOptions.sizeClass,
6077 modalClose: modalOptions.modalClose
6080 }, function resolveError(reason) {
6081 modalResultDeferred.reject(reason);
6084 templateAndResolvePromise.then(function () {
6085 modalOpenedDeferred.resolve(true);
6087 modalOpenedDeferred.reject(false);
6090 return modalInstance;
6097 return $modalProvider;
6100 .directive("b2bModal", ["$modal", "$log", '$scrollTo', function ($modal, $log, $scrollTo) {
6105 modalController: '@',
6112 link: function (scope, elm, attr) {
6113 elm.bind('click', function (ev) {
6114 var currentPosition = ev.pageY - ev.clientY;
6115 ev.preventDefault();
6116 if (angular.isDefined(elm.attr("href")) && elm.attr("href") !== "") {
6117 scope.b2bModal = elm.attr("href");
6120 templateUrl: scope.b2bModal,
6121 controller: scope.modalController,
6122 windowClass: scope.windowClass,
6123 sizeClass: scope.sizeClass,
6124 modalClose: scope.modalClose
6125 }).result.then(function (value) {
6130 }, function (value) {
6141 .directive("utilityFilter", ["$modal", "$log", '$scrollTo', function ($modal, $log, $scrollTo) {
6148 templateUrl: 'b2bTemplate/modal/u-filter.html',
6149 link: function (scope, element, attribute, ctrl) {
6150 //controller to be passed to $modal service
6151 scope.options = angular.copy(scope.$parent.$eval(attribute.ngModel));
6152 scope.$parent.$watch(attribute.ngModel, function (newVal, oldVal) {
6153 if (newVal !== oldVal) {
6154 scope.options = newVal;
6157 var modalCtrl = function ($scope, options) {
6158 $scope.options = angular.copy(options);
6161 if (angular.isDefined(scope.utilityFilter)) {
6162 scope.templateUrl = scope.utilityFilter;
6164 scope.templateUrl = 'b2bTemplate/modal/u-filter-window.html';
6166 element.bind('click', function (ev) {
6167 var currentPosition = ev.pageY - ev.clientY;
6169 templateUrl: scope.templateUrl,
6170 controller: modalCtrl,
6172 options: function () {
6173 return scope.options;
6176 }).result.then(function (value) {
6177 ctrl.$setViewValue(value);
6179 $scrollTo(0, currentPosition, 0);
6182 $scrollTo(0, currentPosition, 0);
6190 * @name Forms.att:monthSelector
6193 * <file src="src/monthSelector/docs/readme.md" />
6196 * <div b2b-monthpicker ng-model="dt" min="minDate" max="maxDate" mode="monthpicker"></div>
6199 * <section id="code">
6200 <example module="b2b.att">
6201 <file src="src/monthSelector/docs/demo.html" />
6202 <file src="src/monthSelector/docs/demo.js" />
6207 angular.module('b2b.att.monthSelector', ['b2b.att.position', 'b2b.att.utilities'])
6209 .constant('b2bMonthpickerConfig', {
6210 dateFormat: 'MM/dd/yyyy',
6212 monthFormat: 'MMMM',
6214 dayHeaderFormat: 'EEEE',
6215 dayTitleFormat: 'MMMM yyyy',
6216 disableWeekend: false,
6217 disableSunday: false,
6219 onSelectClose: null,
6226 legendMessage: null,
6227 calendarDisabled: false,
6229 orientation: 'left',
6232 helperText: 'The date you selected is $date. Double tap to open calendar. Select a date to close the calendar.',
6233 descriptionText: 'Use tab to navigate between previous button, next button and month. Use arrow keys to navigate between months. Use space or enter to select a month.',
6234 MonthpickerEvalAttributes: ['dateFormat', 'dayFormat', 'monthFormat', 'yearFormat', 'dayHeaderFormat', 'dayTitleFormat', 'disableWeekend', 'disableSunday', 'startingDay', 'collapseWait', 'orientation','mode','id'],
6235 MonthpickerWatchAttributes: ['min', 'max', 'due', 'from', 'legendIcon', 'legendMessage', 'ngDisabled'],
6236 MonthpickerFunctionAttributes: ['disableDates', 'onSelectClose']
6239 .factory('b2bMonthpickerService', ['b2bMonthpickerConfig', 'dateFilter', function (b2bMonthpickerConfig, dateFilter) {
6240 var setAttributes = function (attr, elem) {
6241 if (angular.isDefined(attr) && attr !== null && angular.isDefined(elem) && elem !== null) {
6242 var attributes = b2bMonthpickerConfig.MonthpickerEvalAttributes.concat(b2bMonthpickerConfig.MonthpickerWatchAttributes, b2bMonthpickerConfig.MonthpickerFunctionAttributes);
6243 for (var key in attr) {
6244 var val = attr[key];
6245 if (attributes.indexOf(key) !== -1 && angular.isDefined(val)) {
6246 elem.attr(key.toSnakeCase(), key);
6252 var bindScope = function (attr, scope) {
6253 if (angular.isDefined(attr) && attr !== null && angular.isDefined(scope) && scope !== null) {
6254 var evalFunction = function (key, val) {
6255 scope[key] = scope.$parent.$eval(val);
6258 var watchFunction = function (key, val) {
6259 scope.$parent.$watch(val, function (value) {
6262 scope.$watch(key, function (value) {
6263 scope.$parent[val] = value;
6267 var evalAttributes = b2bMonthpickerConfig.MonthpickerEvalAttributes;
6268 var watchAttributes = b2bMonthpickerConfig.MonthpickerWatchAttributes;
6269 for (var key in attr) {
6270 var val = attr[key];
6271 if (evalAttributes.indexOf(key) !== -1 && angular.isDefined(val)) {
6272 evalFunction(key, val);
6273 } else if (watchAttributes.indexOf(key) !== -1 && angular.isDefined(val)) {
6274 watchFunction(key, val);
6281 setAttributes: setAttributes,
6282 bindScope: bindScope
6286 .controller('b2bMonthpickerController', ['$scope', '$attrs', 'dateFilter', '$element', '$position', 'b2bMonthpickerConfig', function ($scope, $attrs, dateFilter, $element, $position, dtConfig) {
6288 date: getValue($attrs.dateFormat, dtConfig.dateFormat),
6289 day: getValue($attrs.dayFormat, dtConfig.dayFormat),
6290 month: getValue($attrs.monthFormat, dtConfig.monthFormat),
6291 year: getValue($attrs.yearFormat, dtConfig.yearFormat),
6292 dayHeader: getValue($attrs.dayHeaderFormat, dtConfig.dayHeaderFormat),
6293 dayTitle: getValue($attrs.dayTitleFormat, dtConfig.dayTitleFormat),
6294 disableWeekend: getValue($attrs.disableWeekend, dtConfig.disableWeekend),
6295 disableSunday: getValue($attrs.disableSunday, dtConfig.disableSunday),
6296 disableDates: getValue($attrs.disableDates, dtConfig.disableDates)
6298 startingDay = getValue($attrs.startingDay, dtConfig.startingDay);
6300 $scope.minDate = dtConfig.minDate ? $scope.resetTime(dtConfig.minDate) : null;
6301 $scope.maxDate = dtConfig.maxDate ? $scope.resetTime(dtConfig.maxDate) : null;
6302 $scope.dueDate = dtConfig.dueDate ? $scope.resetTime(dtConfig.dueDate) : null;
6303 $scope.fromDate = dtConfig.fromDate ? $scope.resetTime(dtConfig.fromDate) : null;
6304 $scope.legendIcon = dtConfig.legendIcon ? dtConfig.legendIcon : null;
6305 $scope.legendMessage = dtConfig.legendMessage ? dtConfig.legendMessage : null;
6306 $scope.ngDisabled = dtConfig.calendarDisabled ? dtConfig.calendarDisabled : null;
6307 $scope.collapseWait = getValue($attrs.collapseWait, dtConfig.collapseWait);
6308 $scope.orientation = getValue($attrs.orientation, dtConfig.orientation);
6309 $scope.onSelectClose = getValue($attrs.onSelectClose, dtConfig.onSelectClose);
6310 $scope.mode = getValue($attrs.mode, dtConfig.mode);
6312 $scope.inline = $attrs.inline === 'true' ? true : dtConfig.inline;
6314 function getValue(value, defaultValue) {
6315 return angular.isDefined(value) ? $scope.$parent.$eval(value) : defaultValue;
6318 function getDaysInMonth(year, month) {
6319 return new Date(year, month, 0).getDate();
6322 function getDates(startDate, n) {
6323 var dates = new Array(n);
6324 var current = startDate,
6327 dates[i++] = new Date(current);
6328 current.setDate(current.getDate() + 1);
6333 this.updatePosition = function (b2bMonthpickerPopupTemplate) {
6334 $scope.position = $position.offset($element);
6335 if($element.find('input').length > 0 ){
6336 $scope.position.top += $element.find('input').prop('offsetHeight');
6338 $scope.position.top += $element.find('a').prop('offsetHeight');
6341 if ($scope.orientation === 'right') {
6342 $scope.position.left -= (((b2bMonthpickerPopupTemplate && b2bMonthpickerPopupTemplate.prop('offsetWidth')) || 290) - $element.find('input').prop('offsetWidth'));
6346 function isSelected(dt) {
6347 if (dt && angular.isDate($scope.currentDate) && compare(dt, $scope.currentDate) === 0) {
6353 function isFromDate(dt) {
6354 if (dt && angular.isDate($scope.fromDate) && compare(dt, $scope.fromDate) === 0) {
6360 function isDateRange(dt) {
6361 if (dt && $scope.fromDate && angular.isDate($scope.currentDate) && (compare(dt, $scope.fromDate) >= 0) && (compare(dt, $scope.currentDate) <= 0)) {
6363 } else if (dt && $scope.fromDate && compare(dt, $scope.fromDate) === 0) {
6369 function isOld(date, currentMonthDate) {
6370 if (date && currentMonthDate && (new Date(date.getFullYear(), date.getMonth(), 1, 0, 0, 0).getTime() < new Date(currentMonthDate.getFullYear(), currentMonthDate.getMonth(), 1, 0, 0, 0).getTime())) {
6377 function isNew(date, currentMonthDate) {
6378 if (date && currentMonthDate && (new Date(date.getFullYear(), date.getMonth(), 1, 0, 0, 0).getTime() > new Date(currentMonthDate.getFullYear(), currentMonthDate.getMonth(), 1, 0, 0, 0).getTime())) {
6385 function isPastDue(dt) {
6386 if ($scope.dueDate) {
6387 return (dt > $scope.dueDate);
6392 function isDueDate(dt) {
6393 if ($scope.dueDate) {
6394 return (dt.getTime() === $scope.dueDate.getTime());
6399 var isDisabled = function (date, currentMonthDate) {
6400 if ($attrs.from && !angular.isDate($scope.fromDate)) {
6403 if (format.disableWeekend === true && (dateFilter(date, format.dayHeader) === "Saturday" || dateFilter(date, format.dayHeader) === "Sunday")) {
6406 if (format.disableSunday === true && (dateFilter(date, format.dayHeader) === "Sunday")) {
6409 if (isOld(date, currentMonthDate) || isNew(date, currentMonthDate)) {
6412 return (($scope.minDate && compare(date, $scope.minDate) < 0) || ($scope.maxDate && compare(date, $scope.maxDate) > 0) || (format.disableDates && format.disableDates({
6417 var isDisabledMonth = function (date, currentMonthDate) {
6418 if ($attrs.from && !angular.isDate($scope.fromDate)) {
6421 if (format.disableWeekend === true && (dateFilter(date, format.dayHeader) === "Saturday" || dateFilter(date, format.dayHeader) === "Sunday")) {
6424 if (format.disableSunday === true && (dateFilter(date, format.dayHeader) === "Sunday")) {
6427 return (($scope.minDate && compare(date, $scope.minDate) < 0) || ($scope.maxDate && compare(date, $scope.maxDate) > 0) || (format.disableDates && format.disableDates({
6432 var compare = function (date1, date2) {
6433 return (new Date(date1.getFullYear(), date1.getMonth(), date1.getDate()) - new Date(date2.getFullYear(), date2.getMonth(), date2.getDate()));
6436 function isMinDateAvailable(startDate, endDate) {
6437 if (($scope.minDate && $scope.minDate.getTime() >= startDate.getTime()) && ($scope.minDate.getTime() <= endDate.getTime())) {
6438 $scope.disablePrev = true;
6439 $scope.visibilityPrev = "hidden";
6441 $scope.disablePrev = false;
6442 $scope.visibilityPrev = "visible";
6446 function isMaxDateAvailable(startDate, endDate) {
6447 if (($scope.maxDate && $scope.maxDate.getTime() >= startDate.getTime()) && ($scope.maxDate.getTime() <= endDate.getTime())) {
6448 $scope.disableNext = true;
6449 $scope.visibilityNext = "hidden";
6451 $scope.disableNext = false;
6452 $scope.visibilityNext = "visible";
6456 function isYearInRange(currentYear) {
6458 if ($scope.minDate && currentYear === $scope.minDate.getFullYear()) {
6459 $scope.disablePrev = true;
6460 $scope.visibilityPrev = "hidden";
6462 $scope.disablePrev = false;
6463 $scope.visibilityPrev = "visible";
6466 if ($scope.maxDate && currentYear === $scope.maxDate.getFullYear()) {
6467 $scope.disableNext = true;
6468 $scope.visibilityNext = "hidden";
6470 $scope.disableNext = false;
6471 $scope.visibilityNext = "visible";
6476 this.focusNextPrev = function(b2bMonthpickerPopupTemplate,init){
6478 if (!$scope.disablePrev){
6479 b2bMonthpickerPopupTemplate[0].querySelector('th.prev').focus();
6480 }else if (!$scope.disableNext){
6481 b2bMonthpickerPopupTemplate[0].querySelector('th.next').focus();
6483 b2bMonthpickerPopupTemplate[0].querySelector('th.b2b-monthSelector-label').focus();
6486 if ($scope.disableNext || $scope.disablePrev){
6487 b2bMonthpickerPopupTemplate[0].querySelector('th.b2b-monthSelector-label').focus();
6492 function getLabel(label) {
6495 pre: label.substr(0, 1).toUpperCase(),
6503 function makeDate(date, dayFormat, dayHeaderFormat, isSelected, isFromDate, isDateRange, isOld, isNew, isDisabled, dueDate, pastDue) {
6506 label: dateFilter(date, dayFormat),
6507 header: dateFilter(date, dayHeaderFormat),
6508 selected: !!isSelected,
6509 fromDate: !!isFromDate,
6510 dateRange: !!isDateRange,
6513 disabled: !!isDisabled,
6516 focusable: !((isDisabled && !(isSelected || isDateRange)) || (isOld || isNew))
6523 getVisibleDates: function (date) {
6524 var year = date.getFullYear(),
6525 month = date.getMonth(),
6526 firstDayOfMonth = new Date(year, month, 1),
6527 lastDayOfMonth = new Date(year, month + 1, 0);
6528 var difference = startingDay - firstDayOfMonth.getDay(),
6529 numDisplayedFromPreviousMonth = (difference > 0) ? 7 - difference : -difference,
6530 firstDate = new Date(firstDayOfMonth),
6533 if (numDisplayedFromPreviousMonth > 0) {
6534 firstDate.setDate(-numDisplayedFromPreviousMonth + 1);
6535 numDates += numDisplayedFromPreviousMonth; // Previous
6537 numDates += getDaysInMonth(year, month + 1); // Current
6538 numDates += (7 - numDates % 7) % 7; // Next
6540 var days = getDates(firstDate, numDates),
6541 labels = new Array(7);
6542 for (var i = 0; i < numDates; i++) {
6543 var dt = new Date(days[i]);
6544 days[i] = makeDate(dt,
6552 isDisabled(dt, date),
6556 for (var j = 0; j < 7; j++) {
6557 labels[j] = getLabel(dateFilter(days[j].date, format.dayHeader));
6559 isMinDateAvailable(firstDayOfMonth, lastDayOfMonth);
6560 isMaxDateAvailable(firstDayOfMonth, lastDayOfMonth);
6563 title: dateFilter(date, format.dayTitle),
6574 getVisibleDates: function(date) {
6577 year = date.getFullYear();
6578 for (var i = 0; i < 12; i++) {
6579 var dt = new Date(year,i,1);
6580 months[i] = makeDate(dt,
6588 isDisabledMonth(dt, date),
6592 isYearInRange(year);
6593 return {objects: months, title: dateFilter(date, format.year), labels: labels};
6601 .directive('b2bMonthpickerPopup', ['$parse', '$log', '$timeout', '$document', '$documentBind', '$isElement', '$templateCache', '$compile','$interval', 'trapFocusInElement', 'keymap', function ($parse, $log, $timeout, $document, $documentBind, $isElement, $templateCache, $compile, $interval,trapFocusInElement, keymap) {
6609 templateUrl: function (elem, attr) {
6610 if (attr.inline === 'true') {
6611 return 'b2bTemplate/monthSelector/monthSelector-popup.html';
6612 }else if (attr.link === 'true') {
6613 return 'b2bTemplate/monthSelector/monthSelectorLink.html';
6615 return 'b2bTemplate/monthSelector/monthSelector.html';
6619 require: ['b2bMonthpickerPopup', 'ngModel', '?^b2bMonthpickerGroup'],
6620 controller: 'b2bMonthpickerController',
6621 link: function (scope, element, attrs, ctrls) {
6622 var MonthpickerCtrl = ctrls[0],
6624 b2bMonthpickerGroupCtrl = ctrls[2];
6625 var b2bMonthpickerPopupTemplate;
6628 $log.error("ng-model is required.");
6629 return; // do nothing if no ng-model
6632 // Configuration parameters
6633 var mode = scope.mode,
6635 scope.isOpen = false;
6639 scope.triggerInterval=undefined;
6642 if (b2bMonthpickerGroupCtrl) {
6643 b2bMonthpickerGroupCtrl.registerMonthpickerScope(scope);
6646 element.bind('keydown', function (ev) {
6649 ev.keyCode = ev.which;
6650 } else if (ev.charCode) {
6651 ev.keyCode = ev.charCode;
6654 if(ev.keyCode === keymap.KEY.ESC)
6656 scope.isOpen = false;
6657 toggleCalendar(scope.isOpen);
6662 element.find('button').bind('click', function () {
6666 element.find('a').bind('click', function () {
6671 element.find('input').bind('click', function () {
6675 var onClicked = function() {
6676 if (!scope.ngDisabled) {
6677 scope.isOpen = !scope.isOpen;
6678 toggleCalendar(scope.isOpen);
6679 MonthpickerCtrl.updatePosition(b2bMonthpickerPopupTemplate);
6684 var toggleCalendar = function (flag) {
6685 if (!scope.inline) {
6687 b2bMonthpickerPopupTemplate = angular.element($templateCache.get('b2bTemplate/monthSelector/monthSelector-popup.html'));
6688 b2bMonthpickerPopupTemplate.attr('b2b-trap-focus-inside-element', 'false');
6689 b2bMonthpickerPopupTemplate.attr('trigger', 'true');
6690 b2bMonthpickerPopupTemplate = $compile(b2bMonthpickerPopupTemplate)(scope);
6691 $document.find('body').append(b2bMonthpickerPopupTemplate);
6692 b2bMonthpickerPopupTemplate.bind('keydown', escPress);
6693 $timeout(function () {
6694 scope.getFocus = true;
6697 $timeout(function () {
6698 scope.getFocus = false;
6700 MonthpickerCtrl.focusNextPrev(b2bMonthpickerPopupTemplate,true);
6703 scope.triggerInterval = $interval(function () {
6704 //This value is updated to trigger init() function of directive on year change.
6705 scope.trigger=(scope.trigger === 0 ? 1 : 0);
6709 b2bMonthpickerPopupTemplate.unbind('keydown', escPress);
6710 if(scope.triggerInterval)
6712 $interval.cancel(scope.triggerInterval);
6713 scope.triggerInterval=undefined;
6715 b2bMonthpickerPopupTemplate.remove();
6716 if(element.find('button').length > 0){
6717 element.find('button')[0].focus();
6719 element.find('a')[0].focus();
6722 scope.getFocus = false;
6727 var outsideClick = function (e) {
6728 var isElement = $isElement(angular.element(e.target), element, $document);
6729 var isb2bMonthpickerPopupTemplate = $isElement(angular.element(e.target), b2bMonthpickerPopupTemplate, $document);
6730 if (!(isElement || isb2bMonthpickerPopupTemplate)) {
6731 scope.isOpen = false;
6732 toggleCalendar(scope.isOpen);
6737 var escPress = function (ev) {
6740 ev.keyCode = ev.which;
6741 } else if (ev.charCode) {
6742 ev.keyCode = ev.charCode;
6746 if (ev.keyCode === keymap.KEY.ESC) {
6747 scope.isOpen = false;
6748 toggleCalendar(scope.isOpen);
6749 ev.preventDefault();
6750 ev.stopPropagation();
6751 } else if (ev.keyCode === 33) {
6752 !scope.disablePrev && scope.move(-1);
6753 $timeout(function () {
6754 scope.getFocus = true;
6756 $timeout(function () {
6757 scope.getFocus = false;
6761 ev.preventDefault();
6762 ev.stopPropagation();
6763 } else if (ev.keyCode === 34) {
6764 !scope.disableNext && scope.move(1);
6765 $timeout(function () {
6766 scope.getFocus = true;
6768 $timeout(function () {
6769 scope.getFocus = false;
6773 ev.preventDefault();
6774 ev.stopPropagation();
6780 $documentBind.click('isOpen', outsideClick, scope);
6782 scope.$on('$destroy', function () {
6784 scope.isOpen = false;
6785 toggleCalendar(scope.isOpen);
6789 scope.resetTime = function (date) {
6790 if (typeof date === 'string') {
6791 date = date + 'T12:00:00';
6794 if (!isNaN(new Date(date))) {
6795 dt = new Date(date);
6796 if(scope.mode === 1){
6797 dt = new Date(dt.getFullYear(), dt.getMonth());
6799 dt = new Date(dt.getFullYear(), dt.getMonth(), dt.getDate());
6804 return new Date(dt.getFullYear(), dt.getMonth(), dt.getDate());
6808 scope.$parent.$watch($parse(attrs.min), function (value) {
6809 scope.minDate = value ? scope.resetTime(value) : null;
6814 scope.$parent.$watch($parse(attrs.max), function (value) {
6815 scope.maxDate = value ? scope.resetTime(value) : null;
6820 scope.$parent.$watch($parse(attrs.due), function (value) {
6821 scope.dueDate = value ? scope.resetTime(value) : null;
6826 scope.$parent.$watch($parse(attrs.from), function (value) {
6827 scope.fromDate = value ? scope.resetTime(value) : null;
6832 if (attrs.legendIcon) {
6833 scope.$parent.$watch(attrs.legendIcon, function (value) {
6834 scope.legendIcon = value ? value : null;
6838 if (attrs.legendMessage) {
6839 scope.$parent.$watch(attrs.legendMessage, function (value) {
6840 scope.legendMessage = value ? value : null;
6844 if (attrs.ngDisabled) {
6845 scope.$parent.$watch(attrs.ngDisabled, function (value) {
6846 scope.ngDisabled = value ? value : null;
6851 // Split array into smaller arrays
6852 function split(arr, size) {
6854 while (arr.length > 0) {
6855 arrays.push(arr.splice(0, size));
6860 var moveMonth = function(selectedDate, direction) {
6861 var step = MonthpickerCtrl.modes[scope.mode].step;
6862 selectedDate.setDate(1);
6863 selectedDate.setMonth(selectedDate.getMonth() + direction * (step.months || 0));
6864 selectedDate.setFullYear(selectedDate.getFullYear() + direction * (step.years || 0));
6866 return selectedDate;
6869 function refill(date) {
6870 if (angular.isDate(date) && !isNaN(date)) {
6871 selected = new Date(date);
6874 selected = new Date();
6879 var selectedCalendar;
6880 if(scope.mode === 1){
6881 if(!angular.isDate(selected))
6883 selected = new Date();
6885 selectedCalendar = moveMonth(angular.copy(selected), -1);
6887 selectedCalendar = angular.copy(selected);
6890 var currentMode = MonthpickerCtrl.modes[mode],
6891 data = currentMode.getVisibleDates(selected);
6893 scope.rows = split(data.objects, currentMode.split);
6896 var startFlag=false;
6897 var firstSelected = false;
6898 for(var i=0; i<scope.rows.length; i++)
6900 for(var j=0; j<scope.rows[i].length; j++)
6902 if(!scope.rows[i][j].disabled && !firstSelected)
6905 var firstDay = scope.rows[i][j];
6908 if(scope.rows[i][j].selected)
6919 if(!flag && firstSelected)
6921 firstDay.firstFocus=true;
6924 scope.labels = data.labels || [];
6925 scope.title = data.title;
6929 scope.select = function (date,$event) {
6930 var dt = new Date(date.getFullYear(), date.getMonth(), date.getDate());
6931 scope.currentDate = dt;
6932 if (!scope.onSelectClose || (scope.onSelectClose && scope.onSelectClose({
6935 if (angular.isNumber(scope.collapseWait)) {
6936 $timeout(function () {
6937 scope.isOpen = false;
6938 toggleCalendar(scope.isOpen);
6939 }, scope.collapseWait);
6941 scope.isOpen = false;
6942 toggleCalendar(scope.isOpen);
6947 scope.move = function (direction,$event) {
6948 var step = MonthpickerCtrl.modes[mode].step;
6949 selected.setDate(1);
6950 selected.setMonth(selected.getMonth() + direction * (step.months || 0));
6951 selected.setFullYear(selected.getFullYear() + direction * (step.years || 0));
6953 scope.getFocus = true;
6954 $timeout(function () {
6955 if (attrs.inline === 'true') {
6956 MonthpickerCtrl.focusNextPrev(element,false);
6958 MonthpickerCtrl.focusNextPrev(b2bMonthpickerPopupTemplate,false);
6961 $event.preventDefault();
6962 $event.stopPropagation();
6965 scope.$watch('currentDate', function (value) {
6966 if (angular.isDefined(value) && value !== null) {
6971 ngModel.$setViewValue(value);
6974 ngModel.$render = function () {
6975 scope.currentDate = ngModel.$viewValue;
6978 var stringToDate = function (value) {
6979 if (!isNaN(new Date(value))) {
6980 value = new Date(value);
6984 ngModel.$formatters.unshift(stringToDate);
6989 .directive('b2bMonthpicker', ['$compile', '$log', 'b2bMonthpickerConfig', 'b2bMonthpickerService', function ($compile, $log, b2bMonthpickerConfig, b2bMonthpickerService) {
6997 controller: ['$scope', '$element', '$attrs', function (scope, elem, attr) {
6998 var dateFormatString = angular.isDefined(attr.dateFormat) ? scope.$parent.$eval(attr.dateFormat) : b2bMonthpickerConfig.dateFormat;
6999 var helperText = angular.isDefined(attr.helperText) ? scope.$parent.$eval(attr.helperText) : b2bMonthpickerConfig.helperText;
7000 helperText = helperText.replace('$date', '{{dt | date : \'' + dateFormatString + '\'}}');
7002 var descriptionText = angular.isDefined(attr.descriptionText) ? scope.$parent.$eval(attr.descriptionText) : b2bMonthpickerConfig.descriptionText;
7006 if (elem.prop('nodeName') !== 'INPUT' && elem.prop('nodeName') !== 'A') {
7010 var selectedDateMessage = "";
7012 if (elem.prop('nodeName') !== 'A'){
7013 selectedDateMessage = '<button type="button" class="span12 faux-input" ng-disabled="ngDisabled" aria-describedby="monthpicker-description'+scope.$id+'"><span class="hidden-spoken" aria-live="assertive" aria-atomic="false">' + helperText + '</span></button>';
7014 elem.attr('tabindex', '-1');
7015 elem.attr('aria-hidden', 'true');
7016 elem.attr('readonly', 'true');
7018 selectedDateMessage = ''
7019 elem.attr('aria-label', helperText);
7022 var descriptionTextSpan = '<span class="offscreen-text" id="monthpicker-description'+scope.$id+'">'+descriptionText+'</span>';
7023 elem.removeAttr('b2b-Monthpicker');
7024 elem.removeAttr('ng-model');
7025 elem.removeAttr('ng-disabled');
7026 elem.addClass('Monthpicker-input');
7027 elem.attr('ng-model', 'dt');
7028 elem.attr('aria-describedby', 'monthpicker-description'+scope.$id);
7032 elem.attr('ng-disabled', 'ngDisabled');
7033 elem.attr('b2b-format-date', dateFormatString);
7035 var wrapperElement = angular.element('<div></div>');
7036 wrapperElement.attr('b2b-Monthpicker-popup', '');
7037 wrapperElement.attr('ng-model', 'dt');
7039 wrapperElement.attr('inline', inline);
7041 if (elem.prop('nodeName') === 'A'){
7042 wrapperElement.attr('link', true);
7044 b2bMonthpickerService.setAttributes(attr, wrapperElement);
7045 b2bMonthpickerService.bindScope(attr, scope);
7047 wrapperElement.html('');
7048 wrapperElement.append(selectedDateMessage);
7049 wrapperElement.append('');
7050 wrapperElement.append(descriptionTextSpan);
7051 wrapperElement.append('');
7052 wrapperElement.append(elem.prop('outerHTML'));
7054 var elm = wrapperElement.prop('outerHTML');
7055 elm = $compile(elm)(scope);
7056 elem.replaceWith(elm);
7058 link: function (scope, elem, attr, ctrl) {
7060 $log.error("ng-model is required.");
7061 return; // do nothing if no ng-model
7064 scope.$watch('dt', function (value) {
7065 ctrl.$setViewValue(value);
7067 ctrl.$render = function () {
7068 scope.dt = ctrl.$viewValue;
7074 .directive('b2bMonthpickerGroup', [function () {
7077 controller: ['$scope', '$element', '$attrs', function (scope, elem, attr) {
7078 this.$$headers = [];
7079 this.$$footers = [];
7080 this.registerMonthpickerScope = function (MonthpickerScope) {
7081 MonthpickerScope.headers = this.$$headers;
7082 MonthpickerScope.footers = this.$$footers;
7085 link: function (scope, elem, attr, ctrl) {}
7089 .directive('b2bFormatDate', ['dateFilter', function (dateFilter) {
7093 link: function (scope, elem, attr, ctrl) {
7094 var b2bFormatDate = "";
7095 attr.$observe('b2bFormatDate', function (value) {
7096 b2bFormatDate = value;
7098 var dateToString = function (value) {
7099 if (!isNaN(new Date(value))) {
7100 return dateFilter(new Date(value), b2bFormatDate);
7104 ctrl.$formatters.unshift(dateToString);
7109 .directive('b2bMonthpickerHeader', [function () {
7112 require: '^b2bMonthpickerGroup',
7116 compile: function (elem, attr, transclude) {
7117 return function link(scope, elem, attr, ctrl) {
7119 ctrl.$$headers.push(transclude(scope, function () {}));
7127 .directive('b2bMonthpickerFooter', [function () {
7130 require: '^b2bMonthpickerGroup',
7134 compile: function (elem, attr, transclude) {
7135 return function link(scope, elem, attr, ctrl) {
7137 ctrl.$$footers.push(transclude(scope, function () {}));
7146 * @name Navigation.att:multiLevelNavigation
7149 * <file src="src/multiLevelNavigation/docs/readme.md" />
7152 * <div class="b2b-ml-nav">
7154 * <li aria-label="{{child.name}}" tabindex="-1" b2b-ml-nav="{{child.type}}" role="treeitem" ng-repeat="child in treeStructure">
7155 * <a tabindex="-1" href="javascript:void(0);" title="{{child.name}}">{{child.name}}<span><i class="{{child.type=='endNode'?'icon-primary-circle':'icon-primary-collapsed'}}"></i></span></a>
7156 * <!-- Below UL tag is RECURSIVE to generate n-childs -->
7157 * <ul role="group" ng-if="child.child">
7158 * <li aria-label="{{child.name}}" b2b-ml-nav="{{child.type}}" tabindex="-1" role="treeitem" ng-repeat="child in child.child">
7159 * <a tabindex="-1" href="javascript:void(0);" title="{{child.name}}">{{child.name}}<span><i class="{{child.type=='endNode'?'icon-primary-circle':'icon-primary-collapsed'}}"></i></span></a>
7160 * <!-- RECURSIVE UL tag goes here -->
7168 * <section id="code">
7169 <example module="b2b.att">
7170 <file src="src/multiLevelNavigation/docs/demo.html" />
7171 <file src="src/multiLevelNavigation/docs/demo.js" />
7176 angular.module('b2b.att.multiLevelNavigation', ['b2b.att.utilities'])
7177 //directive b2bMlNav Test coverage 100% on 5/13
7178 .directive('b2bMlNav', ['keymap', function (keymap) {
7181 link: function (scope, element) {
7182 var rootE, parentE, upE, downE, lastE, homeE, endE;
7183 //default root tree element tabindex set zero
7184 if (element.parent().parent().hasClass('b2b-ml-nav') && (element[0].previousElementSibling === null)) {
7185 element.attr('tabindex', 0);
7187 //check root via class
7188 var isRoot = function (elem) {
7189 if (elem.parent().parent().eq(0).hasClass('b2b-ml-nav')) {
7196 //for any expandable tree item on click
7197 var toggleState = function (e) {
7199 if (angular.element(e.target).attr("b2b-ml-nav") !== "endNode") {
7200 var eLink = element.find('a').eq(0);
7201 if (eLink.hasClass('active')) {
7202 eLink.removeClass('active');
7203 eLink.parent().attr("aria-expanded", "false");
7204 eLink.find('i').eq(0).removeClass('icon-primary-expanded');
7205 eLink.find('i').eq(0).addClass('icon-primary-collapsed');
7207 eLink.addClass('active');
7208 eLink.parent().attr("aria-expanded", "true");
7209 eLink.find('i').eq(0).removeClass('icon-primary-collapsed');
7210 eLink.find('i').eq(0).addClass('icon-primary-expanded');
7214 //function finds the main root-item from particular tree-group
7215 var findRoot = function (elem) {
7220 if (elem.attr("b2b-ml-nav") === "middleNode" || elem.attr("b2b-ml-nav") === "endNode") {
7221 parentE = elem.parent().parent();
7225 if (parentE.attr("b2b-ml-nav") === "rootNode") {
7231 //finds the last visible node of the previous tree-group
7232 var findPreActive = function (elem) {
7233 if (!(elem.hasClass("active"))) {
7236 var childElems = angular.element(elem[0].nextElementSibling.children);
7237 lastE = angular.element(childElems[childElems.length - 1]);
7238 if (lastE.attr("b2b-ml-nav") === "middleNode" && lastE.find('a').eq(0).hasClass('active')) {
7239 findPreActive(lastE.find('a').eq(0));
7244 //find above visible link
7245 var findUp = function (elem) {
7246 if (elem[0].previousElementSibling !== null) {
7247 upE = angular.element(elem[0].previousElementSibling);
7249 upE = elem.parent().parent();
7251 if (isRoot(elem) || (upE.attr('b2b-ml-nav') === "middleNode" && upE[0] !== elem.parent().parent()[0])) {
7252 findPreActive(upE.find('a').eq(0));
7255 //find below visible link
7256 var findDown = function (elem) {
7257 if (elem.hasClass('active')) {
7258 downE = elem.next().find('li').eq(0);
7260 if (elem.parent().next().length !== 0) {
7261 downE = elem.parent().next().eq(0);
7263 if (elem.parent().parent().parent().next().length !== 0) {
7264 downE = elem.parent().parent().parent().next().eq(0);
7267 downE = elem.parent().eq(0);
7271 //finds last root-group element of the tree
7272 var findEnd = function (elem) {
7274 endE = angular.element(rootE.parent()[0].children[rootE.parent()[0].children.length - 1]);
7276 //finds first root element of tree
7277 var findHome = function (elem) {
7279 homeE = angular.element(rootE.parent()[0].children[0]);
7281 element.bind('click', function (e) {
7282 if(element.attr("b2b-ml-nav") !== "endNode") {
7285 if (rootE==undefined){
7288 var currSelected = rootE.parent()[0].querySelector('.selected');
7290 angular.element(currSelected).removeClass('selected');
7292 element.find('a').eq(0).addClass('selected');
7293 e.stopPropagation();
7295 element.bind('focus', function (e) {
7296 if(element.attr("b2b-ml-nav") !== "endNode") {
7297 if(element.find('a').eq(0).hasClass('active')) {
7298 element.attr("aria-expanded", true);
7301 element.attr("aria-expanded", false);
7306 //Keyboard functionality approach:
7308 //set set tabindex -1 on the current focus element
7309 //find the next element to be focussed, set tabindex 0 and throw focus
7310 element.bind('keydown', function (evt) {
7311 switch (evt.keyCode) {
7312 case keymap.KEY.ENTER:
7313 case keymap.KEY.SPACE:
7314 element.triggerHandler('click');
7315 evt.stopPropagation();
7316 evt.preventDefault();
7318 case keymap.KEY.END:
7319 evt.preventDefault();
7320 element.attr('tabindex', -1);
7322 endE.eq(0).attr('tabindex', 0);
7324 evt.stopPropagation();
7326 case keymap.KEY.HOME:
7327 evt.preventDefault();
7328 element.attr('tabindex', -1);
7330 homeE.eq(0).attr('tabindex', 0);
7332 evt.stopPropagation();
7334 case keymap.KEY.LEFT:
7335 evt.preventDefault();
7336 if (!isRoot(element)) {
7337 element.attr('tabindex', -1);
7338 parentE = element.parent().parent();
7339 parentE.eq(0).attr('tabindex', 0);
7341 parentE.eq(0).triggerHandler('click');
7343 if (element.find('a').eq(0).hasClass('active')) {
7344 element.triggerHandler('click');
7347 evt.stopPropagation();
7350 evt.preventDefault();
7351 if (!(isRoot(element) && element[0].previousElementSibling === null)) {
7352 element.attr('tabindex', -1);
7354 upE.eq(0).attr('tabindex', 0);
7357 evt.stopPropagation();
7359 case keymap.KEY.RIGHT:
7360 evt.preventDefault();
7361 if (element.attr("b2b-ml-nav") !== "endNode") {
7362 if (!element.find('a').eq(0).hasClass('active')) {
7363 element.triggerHandler('click');
7365 element.attr('tabindex', -1);
7366 findDown(element.find('a').eq(0));
7367 downE.eq(0).attr('tabindex', 0);
7370 evt.stopPropagation();
7372 case keymap.KEY.DOWN:
7373 evt.preventDefault();
7374 element.attr('tabindex', -1);
7375 if (!(element.attr("b2b-ml-nav") === "middleNode" && element.find('a').eq(0).hasClass('active')) && (element.next().length === 0)) {
7376 if(element.parent().parent().attr("b2b-ml-nav") !== "rootNode" && element.parent().parent()[0].nextElementSibling !== null)
7378 findDown(element.find('a').eq(0));
7379 downE.eq(0).attr('tabindex', 0);
7381 evt.stopPropagation();
7385 if (!(rootE.next().length === 0)) {
7386 rootE.next().eq(0).attr('tabindex', 0);
7387 rootE.next()[0].focus();
7389 rootE.eq(0).attr('tabindex', 0);
7392 evt.stopPropagation();
7395 findDown(element.find('a').eq(0));
7396 downE.eq(0).attr('tabindex', 0);
7398 evt.stopPropagation();
7409 * @name Tabs, tables & accordions.att:multipurposeExpander
7412 * <file src="src/multipurposeExpander/docs/readme.md" />
7415 * <!--With Close Other -->
7416 * <b2b-expander-group close-others="true">
7417 * <b2b-expanders class="mpc-expanders" is-open="testmpc">
7418 * <b2b-expander-heading ng-class=" { 'b2b-toggle-header-active': !testmpc, 'b2b-toggle-header-inactive': testmpc } ">Heading content goes here</b2b-expander-heading>
7419 * <b2b-expander-body>
7420 <p>body content goes here</p>
7421 </b2b-expander-body>
7423 * </b2b-expander-group>
7425 * <!-- Without Close Other -->
7426 * <b2b-expanders class="mpc-expanders" is-open="testmpc2">
7427 * <b2b-expander-heading ng-class=" { 'b2b-toggle-header-active': !testmpc2, 'b2b-toggle-header-inactive': testmpc2 } ">Heading content goes here</b2b-expander-heading>
7428 * <b2b-expander-body>
7429 <p>body content goes here</p>
7430 </b2b-expander-body>
7434 * <section id="code">
7435 <example module="b2b.att.multipurposeExpander">
7436 <file src="src/multipurposeExpander/docs/demo.html" />
7437 <file src="src/multipurposeExpander/docs/demo.js" />
7443 angular.module('b2b.att.multipurposeExpander', ['b2b.att', 'b2b.att.collapse'])
7444 .directive('b2bExpanderGroup', function () {
7448 template: "<ng-transclude></ng-transclude>",
7449 controller:['$scope','$attrs', function($scope,$attrs){
7452 this.scope = $scope;
7454 this.addGroup = function (groupScope) {
7456 groupScope.index = this.groups.length;
7457 this.groups.push(groupScope);
7458 if(this.groups.length > 0){
7461 groupScope.$on('$destroy', function () {
7462 that.removeGroup(groupScope);
7466 this.closeOthers = function (openGroup) {
7467 var closeOthers = angular.isDefined($attrs.closeOthers);
7468 if (closeOthers && !$scope.forceExpand) {
7469 angular.forEach(this.groups, function (group) {
7470 if (group !== openGroup) {
7471 group.isOpen = false;
7475 if (this.groups.indexOf(openGroup) === (this.groups.length - 1) && $scope.forceExpand) {
7476 $scope.forceExpand = false;
7479 this.removeGroup = function (group) {
7480 var index = this.groups.indexOf(group);
7482 this.groups.splice(this.groups.indexOf(group), 1);
7490 .directive('b2bExpanders', function () {
7494 require:['b2bExpanders','?^b2bExpanderGroup'],
7496 scope:{isOpen:'=?'},
7497 template: "<div ng-transclude></div>",
7498 controller: ['$scope', function ($scope){
7499 var bodyScope = null;
7500 var expanderScope = null;
7501 this.isOpened = function(){
7510 this.setScope = function (scope) {
7512 bodyScope.isOpen = $scope.isOpen;
7514 this.setExpanderScope = function (scope) {
7515 expanderScope = scope;
7517 this.toggle = function () {
7518 $scope.isOpen = bodyScope.isOpen = !bodyScope.isOpen;
7519 return bodyScope.isOpen;
7522 this.watchToggle = function(io){
7523 bodyScope.isOpen = io;
7524 expanderScope.updateIcons(io);
7527 link: function (scope, elem, attr, myCtrl)
7529 //scope.isOpen = false;
7531 myCtrl[1].addGroup(scope);
7533 scope.$watch('isOpen', function(val){
7534 myCtrl[0].watchToggle(scope.isOpen);
7535 if(val && myCtrl[1]){
7536 myCtrl[1].closeOthers(scope);
7543 .directive('b2bExpanderHeading', function () {
7545 require: "^b2bExpanders",
7550 template: "<div style='padding:10px 10px 10px 0 !important' ng-transclude></div>"
7554 .directive('b2bExpanderBody', function () {
7557 require: "^b2bExpanders",
7561 template: "<div b2b-collapse='!isOpen' ><div ng-transclude></div></div>",
7562 link: function (scope, elem, attr, myCtrl) {
7563 scope.isOpen = false;
7564 myCtrl.setScope(scope);
7569 .directive('b2bExpanderToggle', function () {
7572 require: "^b2bExpanders",
7578 link: function (scope, element, attr, myCtrl)
7580 myCtrl.setExpanderScope(scope);
7581 var isOpen = myCtrl.isOpened();
7583 scope.setIcon = function () {
7584 element.attr("role", "button");
7586 if (scope.expandIcon && scope.collapseIcon)
7589 element.removeClass(scope.expandIcon);
7590 element.addClass(scope.collapseIcon);
7592 element.attr("aria-expanded", "true");
7595 element.removeClass(scope.collapseIcon);
7596 element.addClass(scope.expandIcon);
7598 element.attr("aria-expanded", "false");
7603 element.bind('click', function (){
7606 scope.updateIcons = function(nStat){
7610 scope.toggleit = function (){
7611 isOpen = myCtrl.toggle();
7621 * @name Messages, modals & alerts.att:notesMessagesAndErrors
7624 * <file src="src/notesMessagesAndErrors/docs/readme.md" />
7630 * <section id="code">
7631 <example module="b2b.att">
7632 <file src="src/notesMessagesAndErrors/docs/demo.html" />
7633 <file src="src/notesMessagesAndErrors/docs/demo.js" />
7638 angular.module('b2b.att.notesMessagesAndErrors', []);
7641 * @name Template.att:Notification Card
7644 * <file src="src/notificationCardTemplate/docs/readme.md" />
7647 * <section id="code">
7648 <b>HTML + AngularJS</b>
7649 <example module="b2b.att">
7650 <file src="src/notificationCardTemplate/docs/demo.html" />
7651 <file src="src/notificationCardTemplate/docs/demo.js" />
7656 angular.module('b2b.att.notificationCardTemplate', [])
7660 * @name Template.att:Order Confirmation Template
7663 * <file src="src/orderConfirmationTemplate/docs/readme.md" />
7666 * <section id="code">
7667 <b>HTML + AngularJS</b>
7668 <example module="b2b.att">
7669 <file src="src/orderConfirmationTemplate/docs/demo.html" />
7670 <file src="src/orderConfirmationTemplate/docs/demo.js" />
7675 angular.module('b2b.att.orderConfirmationTemplate', []);
7679 * @name Navigation.att:pagination
7682 * <file src="src/pagination/docs/readme.md" />
7683 * @param {int} total-pages - Total # of pages, set in your controller $scope
7684 * @param {int} current-page - Current selected page, set in your controller $scope
7685 * @param {function} click-handler - Handler function on click of page number, defined in your controller $scope
7686 * @param {string} input-id - _UNIQUE ID_ __MUST__ be provided for 508 compliance, set in your HTML as static text
7687 * @param {string} input-class - optional class that can be given to use for the go to page container
7690 * <div b2b-pagination="" input-id="goto-page-2" total-pages="totalPages1" current-page="currentPage1" click-handler="customHandler"></div>
7693 * <section id="code">
7694 <example module="b2b.att">
7695 <file src="src/pagination/docs/demo.html" />
7696 <file src="src/pagination/docs/demo.js" />
7701 angular.module('b2b.att.pagination', ['b2b.att.utilities', 'ngTouch'])
7702 .directive('b2bPagination', ['b2bUserAgent', 'keymap', '$window', '$timeout', function (b2bUserAgent, keymap, $window, $timeout) {
7713 templateUrl: 'b2bTemplate/pagination/b2b-pagination.html',
7714 link: function (scope, elem, attr) {
7715 scope.isMobile = b2bUserAgent.isMobile();
7716 scope.notMobile = b2bUserAgent.notMobile();
7719 scope.inputClass = attr.inputClass;
7720 scope.droppableAttribute = scope.isDroppable ? true : false;
7721 scope.$watch('totalPages', function (value) {
7722 if (angular.isDefined(value) && value !== null) {
7725 scope.totalPages = 1;
7729 for (var i = 1; i <= value; i++) {
7730 scope.pages.push(i);
7732 } else if (value > 10) {
7733 var midVal = Math.ceil(value / 2);
7734 scope.pages = [midVal - 2, midVal - 1, midVal, midVal + 1, midVal + 2];
7736 if(scope.currentPage === undefined || scope.currentPage === 1)
7738 currentPageChanged(1);
7742 scope.$watch('currentPage', function (value) {
7743 currentPageChanged(value);
7744 callbackHandler(value);
7746 var callbackHandler = function (num) {
7747 if (angular.isFunction(scope.clickHandler)) {
7748 scope.clickHandler(num);
7751 var getBoundary = function(value){
7752 if ( value < 100 ) {
7754 } else if ( 100 <= value && value < 1000 ) {
7756 } else if ( 1000 <= value ) {
7762 function currentPageChanged(value) {
7763 if (angular.isDefined(value) && value !== null) {
7764 if (!value || value < 1) {
7767 if (value > scope.totalPages) {
7768 value = scope.totalPages;
7770 if (scope.currentPage !== value) {
7771 scope.currentPage = value;
7772 callbackHandler(scope.currentPage);
7774 if (scope.totalPages > 10) {
7775 var val = parseInt(value);
7776 scope.boundary = getBoundary(val);
7777 if (val <= 6) { // Left (first) section
7778 scope.pages = [1, 2, 3, 4, 5, 6, 7];
7779 } else if ( val <= (scope.totalPages - scope.boundary) ) { // Middle section
7780 if ( 7 <= val && val < 9 ) {
7781 if(scope.totalPages < 100) {
7782 scope.pages = [val - 3, val - 2, val - 1, val, val + 1, val + 2];
7783 } else if(scope.totalPages < 1000) {
7784 scope.pages = [val - 2, val - 1, val, val + 1, val + 2];
7785 } else if(scope.totalPages < 1000) {
7786 scope.pages = [val - 2, val - 1, val, val + 1, val + 2];
7788 } else if ( 9 <= val && val < 100 ) {
7789 scope.pages = [val - 3, val - 2, val - 1, val, val + 1, val + 2];
7790 } else if ( 100 <= val && val < 1000 ) {
7791 scope.pages = [val - 2, val - 1, val, val + 1];
7792 } else if ( 1000 <= val ) {
7793 scope.pages = [val - 1, val, val + 1];
7795 } else if ( (scope.totalPages - scope.boundary) < val ) { // Right (last) section
7797 scope.pages = [scope.totalPages - 5, scope.totalPages - 4, scope.totalPages - 3, scope.totalPages - 2, scope.totalPages - 1, scope.totalPages];
7798 } else if ( 100 <= val && val < 1000 ) {
7799 scope.pages = [scope.totalPages - 4, scope.totalPages - 3, scope.totalPages - 2, scope.totalPages - 1, scope.totalPages];
7800 } else if ( 1000 <= val ) {
7801 scope.pages = [scope.totalPages - 3, scope.totalPages - 2, scope.totalPages - 1, scope.totalPages];
7805 if (scope.isMobile) {
7806 var inWidth = $window.innerWidth;
7808 if (inWidth <= 400) {
7810 } else if (inWidth > 400 && inWidth < 500) {
7812 } else if (inWidth >= 500 && inWidth < 600) {
7814 } else if (inWidth >= 600 && inWidth < 700) {
7816 } else if (inWidth >= 700 && inWidth < 800) {
7820 var val = parseInt(value);
7822 scope.meanVal = Math.floor(viewLimit / 2);
7823 var lowerLimit = (val - scope.meanVal) < 1 ? 1 : val - scope.meanVal;
7824 var upperLimit = (lowerLimit + viewLimit - 1) > scope.totalPages ? scope.totalPages : lowerLimit + viewLimit - 1;
7826 for (var i = lowerLimit; i <= upperLimit; i++) {
7827 scope.pages.push(i);
7832 scope.gotoKeyClick = function (keyEvent) {
7833 if (keyEvent.which === keymap.KEY.ENTER) {
7834 scope.gotoBtnClick()
7837 scope.gotoBtnClick = function () {
7838 currentPageChanged(parseInt(scope.gotoPage));
7839 callbackHandler(scope.currentPage);
7840 var qResult = elem[0].querySelector('button');
7841 angular.element(qResult).attr('disabled','true');
7842 $timeout(function(){
7843 elem[0].querySelector('.b2b-pager__item--active').focus();
7845 scope.gotoPage = null;
7847 scope.onfocusIn = function(evt)
7849 var qResult = elem[0].querySelector('button');
7850 angular.element(qResult).removeAttr('disabled');
7852 scope.onfocusOut = function(evt)
7854 if(evt.target.value === "")
7856 var qResult = elem[0].querySelector('button');
7857 angular.element(qResult).attr('disabled','true');
7860 scope.next = function (event) {
7861 if (event != undefined) {
7862 event.preventDefault();
7864 if (scope.currentPage < scope.totalPages) {
7865 scope.currentPage += 1;
7866 callbackHandler(scope.currentPage);
7869 scope.prev = function (event) {
7870 if (event != undefined) {
7871 event.preventDefault();
7873 if (scope.currentPage > 1) {
7874 scope.currentPage -= 1;
7875 callbackHandler(scope.currentPage);
7878 scope.selectPage = function (value, event) {
7879 event.preventDefault();
7880 scope.currentPage = value;
7881 scope.focusedPage = value;
7882 callbackHandler(scope.currentPage);
7884 scope.checkSelectedPage = function (value) {
7885 if (scope.currentPage === value) {
7890 scope.isFocused = function (page) {
7891 return scope.focusedPage === page;
7899 * @name Navigation.att:paneSelector
7902 * <file src="src/paneSelector/docs/readme.md" />
7905 * Please refer demo.html tab in Example section below.
7909 <b>HTML + AngularJS</b>
7910 <example module="b2b.att">
7911 <file src="src/paneSelector/docs/demo.html" />
7912 <file src="src/paneSelector/docs/demo.js" />
7917 angular.module('b2b.att.paneSelector', ['b2b.att.tabs', 'b2b.att.utilities'])
7919 .filter('paneSelectorSelectedItemsFilter', [function () {
7920 return function (listOfItemsArray) {
7922 if (!listOfItemsArray) {
7923 listOfItemsArray = [];
7926 var returnArray = [];
7928 for (var i = 0; i < listOfItemsArray.length; i++) {
7929 if (listOfItemsArray[i].isSelected) {
7930 returnArray.push(listOfItemsArray[i]);
7938 .filter('paneSelectorFetchChildItemsFilter', [function () {
7939 return function (listOfItemsArray) {
7941 if (!listOfItemsArray) {
7942 listOfItemsArray = [];
7945 var returnArray = [];
7947 for (var i = 0; i < listOfItemsArray.length; i++) {
7948 for (var j = 0; j < listOfItemsArray[i].childItems.length; j++) {
7949 returnArray.push(listOfItemsArray[i].childItems[j]);
7957 .directive('b2bPaneSelector', [function () {
7961 templateUrl: 'b2bTemplate/paneSelector/paneSelector.html',
7967 .directive('b2bPaneSelectorPane', [ function () {
7971 templateUrl: 'b2bTemplate/paneSelector/paneSelectorPane.html',
7977 .directive('b2bTabVertical', ['$timeout', 'keymap', function ($timeout, keymap) {
7981 link: function (scope, element, attr, b2bTabCtrl) {
7987 // retreive the isolateScope
7988 var iScope = angular.element(element).isolateScope();
7990 $timeout(function () {
7991 angular.element(element[0].querySelector('a')).unbind('keydown');
7992 angular.element(element[0].querySelector('a')).bind('keydown', function (evt) {
7994 if (!(evt.keyCode)) {
7995 evt.keyCode = evt.which;
7998 switch (evt.keyCode) {
7999 case keymap.KEY.DOWN:
8000 evt.preventDefault();
8005 evt.preventDefault();
8006 iScope.previousKey();
8018 * @name Forms.att:phoneNumberInput
8021 * <file src="src/phoneNumberInput/docs/readme.md" />
8024 <form name="userForm1">
8025 <div class="form-row" ng-class="{'error':!userForm1.text.$valid && userForm1.text.$dirty}">
8026 <label>Phone Mask<span style="color:red">*</span>: (npa) nxx-line Model Value: {{mask.text}}</label>
8028 <input b2b-phone-mask="phoneMask" name="text" ng-model="mask.text" type="text" placeholder="e.g. (123) 456-7890" title="Phone Number" class="b2b-phone-mask-input" required />
8029 <div ng-messages="userForm1.text.$error" class="error-msg" aria-live="polite" aria-atomic="true">
8030 <span ng-message="required" role="alert">This field is mandatory!</span>
8031 <span ng-message="invalidPhoneNumber" role="alert">Please enter valid phone number!</span>
8032 <span ng-message="mask" role="alert">Please enter valid phone number!</span>
8039 * <section id="code">
8040 <example module="b2b.att">
8041 <file src="src/phoneNumberInput/docs/demo.html" />
8042 <file src="src/phoneNumberInput/docs/demo.js" />
8047 angular.module('b2b.att.phoneNumberInput', ['ngMessages', 'b2b.att.utilities'])
8048 .constant("CoreFormsUiConfig", {
8049 phoneMask: '(___) ___-____',
8050 phoneMaskDot: '___.___.____',
8051 phoneMaskHyphen: '___-___-____'
8053 .directive('b2bPhoneMask', ['$parse', 'CoreFormsUiConfig', 'keymap', 'b2bUserAgent', function ($parse, CoreFormsUiConfig, keymap, b2bUserAgent) {
8059 link: function (scope, iElement, iAttrs, ctrl) {
8062 var validPhoneNumber = false;
8063 var currentKey = '';
8064 if (b2bUserAgent.isMobile()) {
8065 mask = "__________";
8067 switch (iAttrs.b2bPhoneMask) {
8069 mask = CoreFormsUiConfig.phoneMask;
8071 case "phoneMaskDot":
8072 mask = CoreFormsUiConfig.phoneMaskDot;
8074 case "phoneMaskHyphen":
8075 mask = CoreFormsUiConfig.phoneMaskHyphen;
8078 mask = CoreFormsUiConfig.phoneMask;
8081 iElement.attr("maxlength", mask.length);
8082 var checkValidity = function (unmaskedValue, rawValue) {
8084 if (angular.isUndefined(rawValue) || rawValue === '') {
8086 } else if (unmaskedValue) {
8087 valid = (unmaskedValue.length === 10);
8089 ctrl.$setValidity('invalidPhoneNumber', validPhoneNumber);
8090 ctrl.$setValidity('mask', valid);
8093 var handleKeyup = function (evt) {
8095 if (evt && evt.keyCode === keymap.KEY.SHIFT) {
8099 var index, formattedNumber;
8100 if (ctrl.$modelValue) {
8101 formattedNumber = ctrl.$modelValue;
8103 formattedNumber = iElement.val();
8105 if (!formattedNumber.length && currentKey === '') {
8108 var maskLength, inputNumbers, maskArray, tempArray, maskArrayLength;
8110 maskArray = mask.split("");
8111 maskArrayLength = maskArray.length;
8112 maskLength = formattedNumber.substring(0, mask.length);
8113 inputNumbers = formattedNumber.replace(/[^0-9]/g, "").split("");
8114 for (index = 0; index < maskArrayLength; index++) {
8115 tempArray.push(maskArray[index] === "_" ? inputNumbers.shift() : maskArray[index]);
8116 if (inputNumbers.length === 0) {
8120 formattedNumber = tempArray.join("");
8121 if (formattedNumber === '(') {
8122 formattedNumber = '';
8125 if ( (angular.isDefined(evt) && evt.which) && currentKey !== '') {
8126 if (maskArray[0] === currentKey && formattedNumber === '') {
8127 formattedNumber = '(';
8128 } else if (evt.which === keymap.KEY.SPACE && currentKey === ' ') {
8129 formattedNumber = formattedNumber + ') ';
8130 } else if (maskArray[0] === currentKey && formattedNumber === '') {
8131 formattedNumber = formattedNumber + currentKey;
8132 } else if (maskArray[formattedNumber.length] === currentKey) {
8133 formattedNumber = formattedNumber + currentKey;
8138 ctrl.$setViewValue(formattedNumber);
8140 return formattedNumber;
8144 // since we are only allowing 0-9, why even let the keypress go forward?
8145 // also added in delete... in case they want to delete :)
8146 var handlePress = function (e) {
8148 if ((e.which < 48 || e.which > 57) && (e.which < 96 || e.which > 105)) {
8149 if (e.which !== keymap.KEY.BACKSPACE && e.which !== keymap.KEY.TAB && e.which !== keymap.KEY.DELETE && e.which !== keymap.KEY.ENTER && e.which !== keymap.KEY.LEFT && e.which !== keymap.KEY.RIGHT &&
8151 (!(e.ctrlKey) && (e.which !== '118' || e.which !== '86')) &&
8153 (!(e.ctrlKey) && (e.which !== '99' || e.which !== '67')) &&
8155 (!(e.ctrlKey) && (e.which !== '120' || e.which !== '88')) &&
8156 /* 229 key code will sent as placeholder key for andriod devices */
8157 (e.which != 229 )) {
8158 e.preventDefault ? e.preventDefault() : e.returnValue = false;
8159 validPhoneNumber = false;
8162 validPhoneNumber = true;
8169 // i moved this out because i thought i might need focus as well..
8170 // to handle setting the model as the view changes
8171 var parser = function (fromViewValue) {
8172 var letters = /^[A-Za-z]+$/;
8173 var numbers = /^[0-9]+$/;
8174 if (angular.isUndefined(fromViewValue) || fromViewValue === '') {
8175 validPhoneNumber = true;
8177 if (fromViewValue.match(letters)) {
8178 validPhoneNumber = false;
8180 if (fromViewValue.match(numbers)) {
8181 validPhoneNumber = true;
8185 if (fromViewValue && fromViewValue.length > 0) {
8186 clean = fromViewValue.replace(/[^0-9]/g, '');
8188 checkValidity(clean, fromViewValue);
8192 //to handle reading the model and formatting it
8193 var formatter = function (fromModelView) {
8195 checkValidity(fromModelView);
8196 if (fromModelView) {
8197 input = handleKeyup();
8202 var setCurrentKey = function (e) {
8213 if (e.shiftKey === true) {
8218 if (e.shiftKey === true) {
8228 if (angular.isDefined(scope.ngModel)) {
8229 parser(scope.ngModel);
8232 ctrl.$parsers.push(parser);
8233 ctrl.$formatters.push(formatter);
8234 iElement.bind('keyup', handleKeyup);
8235 iElement.bind('keydown', handlePress);
8241 * @name Template.att:Profile Blocks
8244 * <file src="src/profileBlockTemplate/docs/readme.md" />
8246 * <section id="code">
8247 <example module="b2b.att">
8248 <file src="src/profileBlockTemplate/docs/demo.html" />
8249 <file src="src/profileBlockTemplate/docs/demo.js" />
8255 angular.module('b2b.att.profileBlockTemplate', [])
8261 * @name Layouts.att:profileCard
8264 * <file src="src/profileCard/docs/readme.md" />
8267 * <b2b-profile-card></b2b-profile-card>
8271 <example module="b2b.att">
8272 <file src="src/profileCard/docs/demo.html" />
8273 <file src="src/profileCard/docs/demo.js" />
8278 angular.module('b2b.att.profileCard', ['b2b.att'])
8279 .constant('profileStatus',{
8286 status: "Deactivated",
8302 role: "COMPANY ADMINISTRATOR"
8305 .directive('b2bProfileCard',['$http','$q','profileStatus', function($http,$q,profileStatus) {
8309 templateUrl: function(element, attrs){
8311 return 'b2bTemplate/profileCard/profileCard.html';
8314 return 'b2bTemplate/profileCard/profileCard-addUser.html';
8321 link: function(scope, elem, attr){
8322 scope.characterLimit = parseInt(attr.characterLimit, 10) || 25;
8323 scope.shouldClip = function(str) {
8324 return str.length > scope.characterLimit;
8327 scope.showEmailTooltip = false;
8330 function isImage(src) {
8331 var deferred = $q.defer();
8332 var image = new Image();
8333 image.onerror = function() {
8334 deferred.reject(false);
8336 image.onload = function() {
8337 deferred.resolve(true);
8339 if(src !== undefined && src.length>0 ){
8342 deferred.reject(false);
8344 return deferred.promise;
8348 isImage(scope.profile.img).then(function(img) {
8351 var splitName=(scope.profile.name).split(' ');
8353 for(var i=0;i<splitName.length;i++){
8354 scope.initials += splitName[i][0];
8356 if(scope.profile.role.toUpperCase() === profileStatus.role){
8359 var profileState=profileStatus.status[scope.profile.state.toUpperCase()];
8361 scope.profile.state=profileStatus.status[scope.profile.state.toUpperCase()].status;
8362 scope.colorIcon=profileStatus.status[scope.profile.state.toUpperCase()].color;
8363 if(scope.profile.state.toUpperCase()===profileStatus.status.PENDING.status.toUpperCase()||scope.profile.state.toUpperCase()===profileStatus.status.LOCKED.status.toUpperCase()){
8364 scope.profile.lastLogin=scope.profile.state;
8367 var today=new Date().getTime();
8368 var lastlogin=new Date(scope.profile.lastLogin).getTime();
8369 var diff=(today-lastlogin)/(1000*60*60*24);
8371 scope.profile.lastLogin="Today";
8374 scope.profile.lastLogin="Yesterday";
8382 * @name Forms.att:radios
8385 * <file src="src/radios/docs/readme.md" />
8390 * @param {boolean} refreshRadioGroup - A trigger that recalculates and updates the accessibility roles on radios in a group.
8394 <b>HTML + AngularJS</b>
8395 <example module="b2b.att">
8396 <file src="src/radios/docs/demo.html" />
8397 <file src="src/radios/docs/demo.js" />
8401 angular.module('b2b.att.radios', ['b2b.att.utilities'])
8402 .directive('b2bRadioGroupAccessibility', ['$timeout', 'b2bUserAgent', function($timeout, b2bUserAgent) {
8406 refreshRadioGroup: "=",
8408 link: function(scope, ele, attr) {
8410 var roleRadioElement, radioProductSelectElement, radioInputTypeElement;
8412 $timeout(calculateNumberOfRadio);
8414 scope.$watch('refreshRadioGroup', function(value) {
8415 if (value === true) {
8416 addingRoleAttribute();
8417 $timeout(calculateNumberOfRadio);
8418 scope.refreshRadioGroup = false;
8425 function calculateNumberOfRadio() {
8426 roleRadioElement = ele[0].querySelectorAll('[role="radio"]');
8428 radioProductSelectElement = ele[0].querySelectorAll('[role="radiogroup"] li.radio-box');
8430 radioInputTypeElement = ele[0].querySelectorAll('input[type="radio"]');
8432 for (var i = 0; i < radioInputTypeElement.length; i++) {
8433 var isChecked = radioInputTypeElement[i].checked ? 'true' : 'false';
8434 var isDisabled = radioInputTypeElement[i].disabled ? 'true' : 'false';
8435 var numOfx = i + 1 + ' of ' + radioInputTypeElement.length;
8436 angular.element(roleRadioElement[i]).attr({
8437 'aria-checked': isChecked,
8438 'aria-disabled': isDisabled,
8439 'data-opNum': numOfx
8441 if (b2bUserAgent.notMobile()) {
8442 angular.element(roleRadioElement[i]).removeAttr("role");
8445 if (radioProductSelectElement.length) {
8446 isChecked === 'true' ? angular.element(radioProductSelectElement[i]).addClass('active') : angular.element(radioProductSelectElement[i]).removeClass('active');
8449 if (/Android/i.test(navigator.userAgent)) {
8450 angular.element(roleRadioElement[i]).append('<span class="hidden-spoken">. ' + numOfx + '.</span>');
8454 angular.element(radioInputTypeElement[i]).bind('click', radioStateChangeonClick);
8459 function addingRoleAttribute() {
8460 for (var i = 0; i < radioInputTypeElement.length; i++) {
8461 if (b2bUserAgent.notMobile()) {
8462 angular.element(roleRadioElement[i]).attr("role", "radio");
8467 function radioStateChangeonClick() {
8468 for (var i = 0; i < radioInputTypeElement.length; i++) {
8469 var isChecked = radioInputTypeElement[i].checked ? 'true' : 'false';
8470 var isDisabled = radioInputTypeElement[i].disabled ? 'true' : 'false';
8471 if (radioProductSelectElement.length) {
8472 isChecked === 'true' ? angular.element(radioProductSelectElement[i]).addClass('active') : angular.element(radioProductSelectElement[i]).removeClass('active');
8474 angular.element(roleRadioElement[i]).attr({
8475 'aria-checked': isChecked,
8476 'aria-disabled': isDisabled
8488 * @name Forms.att:searchField
8491 * <file src="src/searchField/docs/readme.md" />
8494 * <div b2b-search-field dropdown-list="listdata" on-click-callback="clickFn(value)" class="span12" input-model='typedString' config-obj='keyConfigObj'></div>
8498 <example module="b2b.att">
8499 <file src="src/searchField/docs/demo.html" />
8500 <file src="src/searchField/docs/demo.js" />
8505 angular.module('b2b.att.searchField', ['b2b.att.utilities', 'b2b.att.position'])
8506 .filter('b2bFilterInput', [function() {
8507 return function(list, str, keyArray, displayListKey, isContainsSearch, searchSeperator) {
8510 var searchCondition;
8511 var conditionCheck = function(searchSeperator, listItem, displayListKey, splitString) {
8512 var displayTitle = null;
8514 for (var i = 0; i < displayListKey.length; i++) {
8516 displayTitle = listItem[displayListKey[i]].toLowerCase().indexOf(splitString[i].toLowerCase()) > -1;
8518 displayTitle = (splitString[i]) ? displayTitle && listItem[displayListKey[i]].toLowerCase().indexOf(splitString[i].toLowerCase().trim()) > -1 : displayTitle;
8522 angular.forEach(displayListKey, function(value) {
8523 if (!displayTitle) {
8524 displayTitle = listItem[value];
8526 displayTitle = displayTitle + (listItem[value] ? searchSeperator + ' ' + listItem[value] : '');
8530 return displayTitle;
8532 angular.forEach(list, function(listItem) {
8533 var splitString = str.indexOf(searchSeperator) > -1 ? str.split(searchSeperator) : false;
8534 var displayList = conditionCheck(searchSeperator, listItem, displayListKey, splitString)
8535 for (var i = 0; i < keyArray.length; i++) {
8536 searchLabel = keyArray[i];
8537 if (listItem[searchLabel]) {
8538 if (isContainsSearch) {
8539 var displaySearchList = listItem[searchLabel].toLowerCase().indexOf(str.toLowerCase()) > -1;
8540 if (splitString.length > 1) {
8541 displaySearchList = (splitString.length <= keyArray.length) ? displayList : false;
8543 searchCondition = displaySearchList;
8545 searchCondition = listItem[searchLabel].match(new RegExp('^' + str, 'gi'));
8547 if (searchCondition) {
8549 'title': conditionCheck(searchSeperator, listItem, displayListKey),
8550 'valueObj': listItem
8559 }]).directive('b2bSearchField', ['$filter', 'b2bFilterInputFilter', 'keymap', '$documentBind', '$isElement', '$document', 'events', '$timeout', function($filter, b2bFilterInput, keymap, $documentBind, $isElement, $document, events, $timeout) {
8563 dataList: '=dropdownList',
8564 onClickCallback: '&',
8572 templateUrl: 'b2bTemplate/searchField/searchField.html',
8573 controller: ['$scope', function($scope) {
8574 this.searchKeyArray = [];
8575 if ($scope.configObj.searchKeys) {
8576 this.searchKeyArray = $scope.configObj.searchKeys;
8578 if (angular.isUndefined($scope.disabled)) {
8579 $scope.disabled = false;
8581 this.triggerInput = function(searchString) {
8582 $scope.originalInputModel = searchString;
8583 if (searchString === '') {
8584 $scope.currentIndex = -1;
8585 $scope.filterList = [];
8586 $scope.showListFlag = false;
8587 } else if (searchString !== '' && !$scope.isFilterEnabled) {
8588 $scope.filterList = $filter('b2bFilterInput')($scope.dataList, searchString, this.searchKeyArray, $scope.configObj.displayListDataKey, $scope.configObj.isContainsSearch, $scope.configObj.searchSeperator);
8589 $scope.showListFlag = true;
8592 this.denyRegex = function() {
8593 return $scope.inputDeny;
8596 link: function(scope, elem) {
8597 scope.isFilterEnabled = false;
8598 scope.showListFlag = false;
8599 scope.currentIndex = -1;
8600 scope.setCurrentIdx = function(idx) {
8601 scope.currentIndex = idx;
8603 scope.inputModel = scope.filterList[idx].title;
8604 scope.objModel = scope.filterList[idx];
8607 scope.isActive = function(index, dropdownLength) {
8608 scope.dropdownLength = dropdownLength;
8609 return scope.currentIndex === index;
8611 scope.selectItem = function(idx) {
8612 scope.setCurrentIdx(idx);
8613 scope.onClickCallback({
8614 value: scope.inputModel,
8615 objValue: scope.objModel
8617 scope.showListFlag = false;
8618 $timeout(function() {
8619 elem.find('div').find('input')[0].focus();
8622 scope.startSearch = function() {
8623 scope.onClickCallback({
8624 value: scope.inputModel,
8625 objValue: scope.objModel
8628 scope.selectPrev = function() {
8629 if (scope.currentIndex > 0 && scope.filterList.length > 0) {
8630 scope.currentIndex = scope.currentIndex - 1;
8631 scope.setCurrentIdx(scope.currentIndex);
8632 } else if (scope.currentIndex === 0) {
8633 scope.currentIndex = scope.currentIndex - 1;
8634 scope.inputModel = scope.originalInputModel;
8635 scope.isFilterEnabled = true;
8638 scope.selectNext = function() {
8639 if (scope.currentIndex < scope.configObj.noOfItemsDisplay - 1) {
8640 if (scope.currentIndex < scope.filterList.length - 1) {
8641 scope.currentIndex = scope.currentIndex + 1;
8642 scope.setCurrentIdx(scope.currentIndex);
8646 scope.selectCurrent = function() {
8647 scope.selectItem(scope.currentIndex);
8649 scope.selectionIndex = function(e) {
8650 switch (e.keyCode) {
8651 case keymap.KEY.DOWN:
8652 events.preventDefault(e);
8653 scope.isFilterEnabled = true;
8657 events.preventDefault(e);
8658 scope.isFilterEnabled = true;
8661 case keymap.KEY.ENTER:
8662 events.preventDefault(e);
8663 scope.isFilterEnabled = true;
8664 scope.selectCurrent();
8666 case keymap.KEY.ESC:
8667 events.preventDefault(e);
8668 scope.isFilterEnabled = false;
8669 scope.showListFlag = false;
8670 scope.inputModel = '';
8673 scope.isFilterEnabled = false;
8676 if (elem[0].querySelector('.filtercontainer')) {
8677 elem[0].querySelector('.filtercontainer').scrollTop = (scope.currentIndex - 1) * 35;
8680 scope.$watch('filterList', function(newVal, oldVal) {
8681 if (newVal !== oldVal) {
8682 scope.currentIndex = -1;
8685 scope.blurInput = function() {
8686 $timeout(function() {
8687 scope.showListFlag = false;
8690 var outsideClick = function(e) {
8691 var isElement = $isElement(angular.element(e.target), elem.find('ul').eq(0), $document);
8692 if (!isElement && document.activeElement.tagName.toLowerCase() !== 'input') {
8693 scope.showListFlag = false;
8697 $documentBind.click('showListFlag', outsideClick, scope);
8701 .directive('b2bSearchInput', [function() {
8704 require: ['ngModel', '^b2bSearchField'],
8705 link: function(scope, elem, attr, ctrl) {
8706 var ngModelCtrl = ctrl[0];
8707 var attSearchBarCtrl = ctrl[1];
8708 var REGEX = ctrl[1].denyRegex();
8709 var parser = function(viewValue) {
8710 attSearchBarCtrl.triggerInput(viewValue);
8713 ngModelCtrl.$parsers.push(parser);
8715 if (REGEX !== undefined || REGEX !== '') {
8716 elem.bind('input', function() {
8717 var inputString = ngModelCtrl.$viewValue && ngModelCtrl.$viewValue.replace(REGEX, '');
8718 if (inputString !== ngModelCtrl.$viewValue) {
8719 ngModelCtrl.$setViewValue(inputString);
8720 ngModelCtrl.$render();
8731 * @name Buttons, links & UI controls.att:Seek bar
8734 * <file src="src/seekBar/docs/readme.md" />
8737 * Horizontal Seek Bar
8738 * <b2b-seek-bar min="0" max="400" step="1" skip-interval="1" data-ng-model="horizontalSeekBarVal" style="width:180px; margin: auto;" on-drag-end="onDragEnd()" on-drag-init="onDragStart()"></b2b-seek-bar>
8741 * <b2b-seek-bar min="0" max="1" step="0.01" skip-interval="0.1" vertical data-ng-model="verticalSeekBarVal" style=" width: 6px; height: 180px; margin: auto;"></b2b-seek-bar>
8745 <b>HTML + AngularJS</b>
8746 <example module="b2b.att">
8747 <file src="src/seekBar/docs/demo.html" />
8748 <file src="src/seekBar/docs/demo.js" />
8753 angular.module('b2b.att.seekBar', ['b2b.att.utilities','b2b.att.position'])
8754 .constant('b2bSeekBarConfig', {
8760 .directive('b2bSeekBar', ['$documentBind', 'events', 'b2bSeekBarConfig', 'keymap', '$compile', function($documentBind, events, b2bSeekBarConfig, keymap, $compile) {
8765 templateUrl: 'b2bTemplate/seekBar/seekBar.html',
8770 link: function(scope, elm, attr, ngModelCtrl) {
8771 scope.isDragging = false;
8772 scope.verticalSeekBar = false;
8775 var step = b2bSeekBarConfig.step;
8776 var skipInterval = b2bSeekBarConfig.skipInterval;
8777 var knob = angular.element(elm[0].querySelector('.b2b-seek-bar-knob-container'));
8778 var seekBarKnob = angular.element(knob[0].querySelector('.b2b-seek-bar-knob'));
8779 var trackContainer = angular.element(elm[0].querySelector('.b2b-seek-bar-track-container'));
8780 var trackFill = angular.element(elm[0].querySelector('.b2b-seek-bar-track-fill'));
8781 var trackContainerRect = {};
8783 var trackFillOrderPositioning;
8785 if (angular.isDefined(attr.vertical)) {
8786 scope.verticalSeekBar = true;
8787 axisPosition = "clientY";
8790 scope.verticalSeekBar = false;
8791 axisPosition = "clientX";
8793 var getValidStep = function(val) {
8794 val = parseFloat(val);
8795 // in case $modelValue came in string number
8796 if (angular.isNumber(val)) {
8797 val = Math.round((val - min) / step) * step + min;
8798 return Math.round(val * 1000) / 1000;
8802 var getPositionToPercent = function(x) {
8803 if (scope.verticalSeekBar) {
8804 return Math.max(0, Math.min(1, (trackContainerRect.bottom - x) / (trackFillOrderPositioning)));
8807 return Math.max(0, Math.min(1, (x - trackContainerRect.left) / (trackFillOrderPositioning)));
8811 var getPercentToValue = function(percent) {
8812 return (min + percent * (max - min));
8815 var getValueToPercent = function(val) {
8816 return (val - min) / (max - min);
8819 var getValidMinMax = function(val) {
8820 return Math.max(min, Math.min(max, val));
8823 var updateTrackContainerRect = function() {
8824 trackContainerRect = trackContainer[0].getBoundingClientRect();
8825 if (scope.verticalSeekBar) {
8826 if (!trackContainerRect.height) {
8827 trackFillOrderPositioning = trackContainer[0].scrollHeight;
8829 trackFillOrderPositioning = trackContainerRect.height;
8833 if (!trackContainerRect.width) {
8834 trackFillOrderPositioning = trackContainer[0].scrollWidth;
8836 trackFillOrderPositioning = trackContainerRect.width;
8843 var updateKnobPosition = function(percent) {
8844 var percentStr = (percent * 100) + '%';
8845 if (scope.verticalSeekBar) {
8846 knob.css('bottom', percentStr);
8847 trackFill.css('height', percentStr);
8850 knob.css('left', percentStr);
8851 trackFill.css('width', percentStr);
8855 var modelRenderer = function() {
8856 if (isNaN(ngModelCtrl.$viewValue)) {
8857 ngModelCtrl.$viewValue = ngModelCtrl.$modelValue || min;
8860 var viewVal = ngModelCtrl.$viewValue;
8861 scope.currentModelValue = viewVal;
8863 //wait for min, max and step to be set then only update UI to avoid NaN on percent calculation
8864 if ((min || min === 0) && max && step) {
8865 updateKnobPosition(getValueToPercent(viewVal));
8869 var setModelValue = function(val) {
8870 scope.currentModelValue = getValidMinMax(getValidStep(val));
8871 ngModelCtrl.$setViewValue(scope.currentModelValue);
8874 var updateMin = function(val) {
8875 min = parseFloat(val);
8877 min = b2bSeekBarConfig.min;
8882 var updateMax = function(val) {
8883 max = parseFloat(val);
8885 max = b2bSeekBarConfig.max;
8890 var updateStep = function(val) {
8891 step = parseFloat(val);
8892 if (!attr['skipInterval']) {
8893 skipInterval = step;
8897 var updateSkipInterval = function(val) {
8898 skipInterval = step * Math.ceil(val / (step!==0?step:1));
8901 angular.isDefined(attr.min) ? attr.$observe('min', updateMin) : updateMin(b2bSeekBarConfig.min);
8902 angular.isDefined(attr.max) ? attr.$observe('max', updateMax) : updateMax(b2bSeekBarConfig.max);
8903 if (angular.isDefined(attr.step)) {
8904 attr.$observe('step', updateStep);
8906 if (angular.isDefined(attr.skipInterval)) {
8907 attr.$observe('skipInterval', updateSkipInterval);
8909 scope.currentModelValue = getValidMinMax(getValidStep(min));
8910 var onMouseDown = function(e) {
8913 // left mouse button
8917 // right or middle mouse button
8922 scope.isDragging = true;
8923 seekBarKnob[0].focus();
8924 updateTrackContainerRect();
8925 if (attr['onDragInit']) {
8928 events.stopPropagation(e);
8929 events.preventDefault(e);
8930 scope.$apply(function() {
8931 setModelValue(getPercentToValue(getPositionToPercent(e[axisPosition])));
8935 var onMouseUp = function() {
8937 if (attr['onDragEnd']) {
8940 scope.isDragging = false;
8944 var onMouseMove = function(e) {
8945 if (scope.isDragging) {
8946 events.stopPropagation(e);
8947 events.preventDefault(e);
8949 scope.$apply(function() {
8950 setModelValue(getPercentToValue(getPositionToPercent(e[axisPosition])));
8955 function onKeyDown(e) {
8957 e.keyCode = e.which;
8960 switch (e.keyCode) {
8961 case keymap.KEY.LEFT:
8962 if (!scope.verticalSeekBar) {
8963 updateStep = -skipInterval;
8966 case keymap.KEY.RIGHT:
8967 if (!scope.verticalSeekBar) {
8968 updateStep = skipInterval;
8972 if (scope.verticalSeekBar) {
8973 updateStep = skipInterval;
8976 case keymap.KEY.DOWN:
8977 if (scope.verticalSeekBar) {
8978 updateStep = -skipInterval;
8986 events.stopPropagation(e);
8987 events.preventDefault(e);
8988 scope.$apply(function() {
8989 setModelValue(ngModelCtrl.$viewValue + updateStep);
8991 if (attr['onDragEnd']) {
8997 elm.on('keydown', onKeyDown);
8998 elm.on('mousedown', onMouseDown);
9000 $documentBind.event('mousemove', 'isDragging', onMouseMove, scope, true, 0);
9001 $documentBind.event('mouseup', 'isDragging', onMouseUp, scope, true, 0);
9003 ngModelCtrl.$render = function() {
9004 if (!scope.isDragging) {
9008 ngModelCtrl.$viewChangeListeners.push(modelRenderer);
9009 ngModelCtrl.$formatters.push(getValidMinMax);
9010 ngModelCtrl.$formatters.push(getValidStep);
9016 * @name Layouts.att:separators
9019 * <file src="src/separators/docs/readme.md" />
9025 * <section id="code">
9026 <b>HTML + AngularJS</b>
9027 <example module="b2b.att">
9028 <file src="src/separators/docs/demo.html" />
9029 <file src="src/separators/docs/demo.js" />
9035 angular.module('b2b.att.separators', []);
9038 * @name Buttons, links & UI controls.att:slider
9041 * <file src="src/slider/docs/readme.md" />
9044 * <b2b-slider min="0" max="400" step="1" no-aria-label skip-interval="1" ng-model="horizontalSliderVal" style="width:180px; margin: auto;" on-drag-end="onDragEnd()" on-drag-init="onDragStart()" label-id="slider-label" post-aria-label="postAriaLabel"></b2b-slider>
9048 <b>HTML + AngularJS</b>
9049 <example module="b2b.att">
9050 <file src="src/slider/docs/demo.html" />
9051 <file src="src/slider/docs/demo.js" />
9056 angular.module('b2b.att.slider', ['b2b.att.utilities'])
9057 .constant('SliderConfig', {
9063 .directive('b2bSlider', ['$documentBind', 'SliderConfig', 'keymap', '$compile', '$log', function($documentBind, SliderConfig, keymap, $compile, $log) {
9068 templateUrl: 'b2bTemplate/slider/slider.html',
9072 trackFillColor: '=?',
9074 postAriaLabel: '=?',
9076 sliderSnapPoints: '=?',
9077 customAriaLabel: '=?',
9080 link: function(scope, elm, attr, ngModelCtrl) {
9081 scope.isDragging = false;
9082 scope.verticalSlider = false;
9085 var step = SliderConfig.step;
9086 var skipInterval = SliderConfig.skipInterval;
9087 var knob = angular.element(elm[0].querySelector('.slider-knob-container'));
9088 var sliderKnob = angular.element(knob[0].querySelector('.slider-knob'));
9089 var trackContainer = angular.element(elm[0].querySelector('.slider-track-container'));
9090 var trackFill = angular.element(elm[0].querySelector('.slider-track-fill'));
9091 var trackContainerRect = {};
9092 var axisPosition = "clientX";
9093 var trackFillOrderPositioning;
9095 //Forcefully disabling the vertical Slider code.
9096 if (angular.isDefined(attr.vertical)) {
9097 scope.verticalSlider = true;
9098 axisPosition = "clientY";
9101 if (angular.isDefined(scope.noAriaLabel) && scope.noAriaLabel !== '') {
9102 $log.warn('no-aria-label has been deprecated. This will be removed in v0.6.0.');
9104 if (angular.isDefined(scope.preAriaLabel) && scope.preAriaLabel !== '') {
9105 $log.warn('pre-aria-label has been deprecated. Please use label-id instead. This will be removed in v0.6.0.');
9107 if (angular.isDefined(scope.customAriaLabel) && scope.customAriaLabel !== '') {
9108 $log.warn('custom-aria-label has been deprecated. Please use label-id and post-aria-label instead. This will be removed in v0.6.0.');
9111 var binarySearchNearest = function (num, arr) {
9114 var hi = arr.length - 1;
9116 while (hi - lo > 1) {
9117 mid = Math.floor((lo + hi) / 2);
9118 if (arr[mid] < num) {
9124 if (num - arr[lo] < arr[hi] - num) {
9130 var getValidStep = function(val) {
9131 val = parseFloat(val);
9132 // in case $modelValue came in string number
9135 if(attr['sliderSnapPoints'] && scope.sliderSnapPoints.length > 0) {
9136 val = binarySearchNearest(val, scope.sliderSnapPoints);
9139 val = Math.round((val - min) / step) * step + min;
9142 return Math.round(val * 1000) / 1000;
9146 var getPositionToPercent = function(x) {
9147 if (scope.verticalSlider) {
9148 return Math.max(0, Math.min(1, (trackContainerRect.bottom - x) / (trackFillOrderPositioning)));
9151 return Math.max(0, Math.min(1, (x - trackContainerRect.left) / (trackFillOrderPositioning)));
9155 var getPercentToValue = function(percent) {
9156 return (min + percent * (max - min));
9159 var getValueToPercent = function(val) {
9160 return (val - min) / (max - min);
9163 var getValidMinMax = function(val) {
9164 return Math.max(min, Math.min(max, val));
9167 var updateTrackContainerRect = function() {
9168 trackContainerRect = trackContainer[0].getBoundingClientRect();
9169 if (scope.verticalSlider) {
9170 if (!trackContainerRect.height) {
9171 trackFillOrderPositioning = trackContainer[0].scrollHeight;
9173 trackFillOrderPositioning = trackContainerRect.height;
9177 if (!trackContainerRect.width) {
9178 trackFillOrderPositioning = trackContainer[0].scrollWidth;
9180 trackFillOrderPositioning = trackContainerRect.width;
9187 var updateKnobPosition = function(percent) {
9188 var percentStr = (percent * 100) + '%';
9189 if (scope.verticalSlider) {
9190 knob.css('bottom', percentStr);
9191 trackFill.css('height', percentStr);
9194 knob.css('left', percentStr);
9195 trackFill.css('width', percentStr);
9199 var modelRenderer = function() {
9201 if(attr['disabled']){
9205 if (isNaN(ngModelCtrl.$viewValue)) {
9206 ngModelCtrl.$viewValue = ngModelCtrl.$modelValue || min;
9209 var viewVal = ngModelCtrl.$viewValue;
9210 scope.currentModelValue = viewVal;
9212 //wait for min, max and step to be set then only update UI to avoid NaN on percent calculation
9213 if ((min || min === 0) && max && step) {
9214 updateKnobPosition(getValueToPercent(viewVal));
9218 var setModelValue = function(val) {
9219 scope.currentModelValue = getValidMinMax(getValidStep(val));
9220 ngModelCtrl.$setViewValue(scope.currentModelValue);
9223 var updateMin = function(val) {
9224 min = parseFloat(val);
9226 min = SliderConfig.min;
9232 var updateMax = function(val) {
9233 max = parseFloat(val);
9235 max = SliderConfig.max;
9241 var updateStep = function(val) {
9242 step = parseFloat(val);
9243 if (!attr['skipInterval']) {
9244 skipInterval = step;
9248 var updateSkipInterval = function(val) {
9249 skipInterval = step * Math.ceil(val / (step!==0?step:1));
9252 angular.isDefined(attr.min) ? attr.$observe('min', updateMin) : updateMin(SliderConfig.min);
9253 angular.isDefined(attr.max) ? attr.$observe('max', updateMax) : updateMax(SliderConfig.max);
9254 if (angular.isDefined(attr.step)) {
9255 attr.$observe('step', updateStep);
9257 if (angular.isDefined(attr.skipInterval)) {
9258 attr.$observe('skipInterval', updateSkipInterval);
9260 scope.currentModelValue = getValidMinMax(getValidStep(min));
9261 var onMouseDown = function(e) {
9263 if(attr['disabled']){
9269 // left mouse button
9273 // right or middle mouse button
9277 scope.isDragging = true;
9278 sliderKnob[0].focus();
9279 updateTrackContainerRect();
9280 if (attr['onDragInit']) {
9283 e.stopPropagation();
9285 scope.$apply(function() {
9286 setModelValue(getPercentToValue(getPositionToPercent(e[axisPosition])));
9290 var onMouseUp = function() {
9292 if (attr['onDragEnd']) {
9295 scope.isDragging = false;
9299 var onMouseMove = function(e) {
9300 if (scope.isDragging) {
9301 e.stopPropagation();
9304 scope.$apply(function() {
9305 setModelValue(getPercentToValue(getPositionToPercent(e[axisPosition])));
9310 function onKeyDown(e) {
9312 e.keyCode = e.which;
9315 switch (e.keyCode) {
9316 case keymap.KEY.DOWN:
9317 case keymap.KEY.LEFT:
9318 if(attr['sliderSnapPoints'] && scope.sliderSnapPoints.length > 0) {
9319 var currentIndex = scope.sliderSnapPoints.indexOf(ngModelCtrl.$viewValue);
9320 if (currentIndex > 0) {
9323 updateStep = scope.sliderSnapPoints[currentIndex];
9326 updateStep = ngModelCtrl.$viewValue - skipInterval;
9330 case keymap.KEY.RIGHT:
9331 if(attr['sliderSnapPoints'] && scope.sliderSnapPoints.length > 0) {
9332 var currentIndex = scope.sliderSnapPoints.indexOf(ngModelCtrl.$viewValue);
9333 if (currentIndex < scope.sliderSnapPoints.length-1) {
9336 updateStep = scope.sliderSnapPoints[currentIndex];
9339 updateStep = ngModelCtrl.$viewValue + skipInterval;
9342 case keymap.KEY.END:
9343 if(attr['sliderSnapPoints'] && scope.sliderSnapPoints.length > 0) {
9344 currentIndex = scope.sliderSnapPoints.length-1;
9345 updateStep = scope.sliderSnapPoints[currentIndex];
9347 setModelValue(scope.max);
9350 e.stopPropagation();
9352 case keymap.KEY.HOME:
9353 if(attr['sliderSnapPoints'] && scope.sliderSnapPoints.length > 0) {
9355 updateStep = scope.sliderSnapPoints[currentIndex];
9357 setModelValue(scope.min);
9360 e.stopPropagation();
9366 if (angular.isNumber(updateStep) && !attr['disabled']) {
9367 e.stopPropagation();
9369 scope.$apply(function() {
9370 setModelValue(updateStep);
9372 if (attr['onDragEnd']) {
9378 elm.on('keydown', onKeyDown);
9379 elm.on('mousedown', onMouseDown);
9381 $documentBind.event('mousemove', 'isDragging', onMouseMove, scope, true, 0);
9382 $documentBind.event('mouseup', 'isDragging', onMouseUp, scope, true, 0);
9384 attr.$observe('disabled', function (disabled) {
9386 sliderKnob.removeAttr('tabindex');
9388 sliderKnob.attr('tabindex', '0');
9392 elm.toggleClass("slider-disabled", disabled);
9394 if (angular.isDefined(attr.hideDisabledKnob)) {
9395 scope.hideKnob = disabled;
9399 ngModelCtrl.$render = function() {
9400 if (!scope.isDragging) {
9402 if (attr['onRenderEnd'] && !attr['disabled']) {
9403 scope.onRenderEnd({currentModelValue: scope.currentModelValue});
9407 ngModelCtrl.$viewChangeListeners.push(modelRenderer);
9408 ngModelCtrl.$formatters.push(getValidMinMax);
9409 ngModelCtrl.$formatters.push(getValidStep);
9415 * @name Forms.att:spinButton
9417 * @param {String} spin-button-id - An ID for the input field
9418 * @param {Integer} min - Minimum value for the input
9419 * @param {Integer} max - Maximum value for the input
9420 * @param {Integer} step - Value by which input field increments or decrements on up/down arrow keys
9421 * @param {Integer} page-step - Value by which input field increments or decrements on page up/down keys
9422 * @param {boolean} input-model-key - Default value for input field
9423 * @param {boolean} disabled-flag - A boolean that triggers directive once the min or max value has reached
9426 * <file src="src/spinButton/docs/readme.md" />
9429 * <section id="code">
9430 <example module="b2b.att">
9431 <file src="src/spinButton/docs/demo.html" />
9432 <file src="src/spinButton/docs/demo.js" />
9437 angular.module('b2b.att.spinButton', ['b2b.att.utilities'])
9438 .constant('b2bSpinButtonConfig', {
9443 inputModelKey: 'value',
9446 .directive('b2bSpinButton', ['keymap', 'b2bSpinButtonConfig', 'b2bUserAgent', function (keymap, b2bSpinButtonConfig, userAgent) {
9449 require: '?ngModel',
9456 pageStep: '=pageStep',
9458 inputValue: '=ngModel',
9462 templateUrl: 'b2bTemplate/spinButton/spinButton.html',
9463 controller: ['$scope', '$element', '$attrs', function (scope, element, attrs) {
9465 scope.isMobile = userAgent.isMobile();
9466 scope.notMobile = userAgent.notMobile();
9468 scope.min = attrs.min ? scope.min : b2bSpinButtonConfig.min;
9469 scope.max = attrs.max ? scope.max : b2bSpinButtonConfig.max;
9470 scope.step = attrs.step ? scope.step : b2bSpinButtonConfig.step;
9471 scope.pageStep = attrs.pageStep ? scope.pageStep : b2bSpinButtonConfig.pageStep;
9472 scope.inputModelKey = attrs.inputModelKey ? scope.inputModelKey : b2bSpinButtonConfig.inputModelKey;
9473 scope.disabledFlag = attrs.disabledFlag ? scope.disabledFlag : b2bSpinButtonConfig.disabledFlag;
9475 if (scope.min < 0) {
9478 if (scope.max > 999) {
9482 scope.isPlusDisabled = function () {
9483 return (scope.disabledFlag || scope.inputValue[scope.inputModelKey] >= scope.max);
9485 scope.isMinusDisabled = function () {
9486 return (scope.disabledFlag || scope.inputValue[scope.inputModelKey] <= scope.min);
9489 scope.getValidateInputValue = function (value) {
9490 if (value <= scope.min) {
9492 } else if (value >= scope.max) {
9499 scope.plus = function () {
9500 scope.inputValue[scope.inputModelKey] = scope.getValidateInputValue(parseInt(scope.inputValue[scope.inputModelKey], 10) + scope.step);
9502 scope.minus = function () {
9503 scope.inputValue[scope.inputModelKey] = scope.getValidateInputValue(parseInt(scope.inputValue[scope.inputModelKey], 10) - scope.step);
9505 scope.pagePlus = function () {
9506 scope.inputValue[scope.inputModelKey] = scope.getValidateInputValue(parseInt(scope.inputValue[scope.inputModelKey], 10) + scope.pageStep);
9508 scope.pageMinus = function () {
9509 scope.inputValue[scope.inputModelKey] = scope.getValidateInputValue(parseInt(scope.inputValue[scope.inputModelKey], 10) - scope.pageStep);
9513 link: function (scope, elem) {
9515 if (scope.notMobile) {
9516 angular.element(elem).find('input').attr('aria-live', 'off');
9517 angular.element(elem).find('input').attr('role', 'spinbutton');
9520 elem.find('input').bind('keydown', function (e) {
9521 if (e.keyCode === keymap.KEY.UP) {
9523 } else if (e.keyCode === keymap.KEY.DOWN){
9525 } else if (e.keyCode === keymap.KEY.HOME) {
9526 scope.inputValue[scope.inputModelKey] = parseInt(scope.min);
9527 } else if (e.keyCode === keymap.KEY.END) {
9528 scope.inputValue[scope.inputModelKey] = parseInt(scope.max);
9529 } else if (e.keyCode === keymap.KEY.PAGE_UP) {
9531 } else if (e.keyCode === keymap.KEY.PAGE_DOWN) {
9537 elem.find('input').bind('keyup', function () {
9538 if (scope.inputValue[scope.inputModelKey] === null ||
9539 scope.inputValue[scope.inputModelKey] === '' ||
9540 scope.inputValue[scope.inputModelKey] < scope.min) {
9541 scope.inputValue[scope.inputModelKey] = scope.min;
9543 } else if (angular.isUndefined(scope.inputValue[scope.inputModelKey]) ||
9544 scope.inputValue[scope.inputModelKey] > scope.max) {
9545 scope.inputValue[scope.inputModelKey] = scope.max;
9550 scope.focusInputSpinButton = function (evt) {
9551 evt.preventDefault();
9552 if (scope.notMobile) {
9553 elem[0].querySelector('input').focus();
9562 * @name Template.att:Static Route
9565 * <file src="src/staticRouteTemplate/docs/readme.md" />
9568 * <section id="code">
9569 <example module="b2b.att">
9570 <file src="src/staticRouteTemplate/docs/demo.html" />
9571 <file src="src/staticRouteTemplate/docs/demo.js" />
9576 angular.module('b2b.att.staticRouteTemplate', ['b2b.att.utilities'])
9580 * @name Progress & usage indicators.att:statusTracker
9583 * @param {array} statusObject - An array of status objects that accept heading, estimate, description and state
9585 * <file src="src/statusTracker/docs/readme.md" />
9589 <div ng-controller="statusTrackerController">
9590 <b2b-status-tracker statuses="statusObject"></b2b-status-tracker>
9595 <example module="b2b.att">
9596 <file src="src/statusTracker/docs/demo.html" />
9597 <file src="src/statusTracker/docs/demo.js" />
9602 angular.module('b2b.att.statusTracker', ['b2b.att.utilities'])
9603 .constant('b2bStatusTrackerConfig', {
9606 'complete': 'icoControls-approval',
9607 'current': 'icon-misc-time',
9608 'pending': 'icoControls-statusokay',
9609 'actionRequired': 'icon-primary-securityalerts-alert',
9610 'notAvailable': 'icoControls-restricted'
9613 .directive('b2bStatusTracker', ['b2bStatusTrackerConfig', function(b2bStatusTrackerConfig) {
9621 templateUrl: function(scope) {
9622 return 'b2bTemplate/statusTracker/statusTracker.html';
9624 link: function(scope, element, attr) {
9625 scope.currentViewIndex = 0;
9626 scope.b2bStatusTrackerConfig = b2bStatusTrackerConfig;
9628 scope.nextStatus = function() {
9629 if (scope.currentViewIndex+1 <= scope.statuses.length) {
9630 scope.currentViewIndex++;
9633 scope.previousStatus = function() {
9634 if (scope.currentViewIndex-1 >= 0) {
9635 scope.currentViewIndex--;
9638 scope.isInViewport = function(index) {
9639 return (index < scope.currentViewIndex+3 && index >= scope.currentViewIndex); // && index > scope.currentViewIndex-2
9642 scope.removeCamelCase = function(str) {
9643 return str.replace(/([a-z])([A-Z])/g, '$1 $2').toLowerCase();
9650 * @name Progress & usage indicators.att:stepTracker
9653 * @param {array} stepsItemsObject - An array of step objects
9654 * @param {Integer} currenIindex - This indicates the current running step
9655 * @param {Integer} viewportIndex - This is optional. This can used to start the view port rather than 1 item.
9657 * <file src="src/stepTracker/docs/readme.md" />
9661 * <b2b-step-tracker steps-items-object="stepsObject" current-index="currentStepIndex" step-indicator-heading="stepHeading"></b2b-step-tracker>
9666 <b>HTML + AngularJS</b>
9667 <example module="b2b.att">
9668 <file src="src/stepTracker/docs/demo.html" />
9669 <file src="src/stepTracker/docs/demo.js" />
9673 angular.module('b2b.att.stepTracker', ['b2b.att.utilities'])
9674 .constant('b2bStepTrackerConfig', {
9677 .directive('b2bStepTracker', ['b2bStepTrackerConfig', function(b2bStepTrackerConfig) {
9682 stepsItemsObject:"=",
9686 templateUrl: 'b2bTemplate/stepTracker/stepTracker.html',
9687 link: function(scope, ele, attr) {
9688 if (angular.isDefined(scope.viewportIndex)) {
9689 scope.currentViewIndex = scope.viewportIndex - 1;
9691 scope.currentViewIndex = 0;
9694 scope.b2bStepTrackerConfig = b2bStepTrackerConfig;
9695 scope.nextStatus = function() {
9696 if (scope.currentViewIndex+1 <= scope.stepsItemsObject.length) {
9697 scope.currentViewIndex++;
9700 scope.previousStatus = function() {
9701 if (scope.currentViewIndex-1 >= 0) {
9702 scope.currentViewIndex--;
9705 scope.isInViewport = function(index) {
9706 return (index < scope.currentViewIndex+b2bStepTrackerConfig.maxViewItems && index >= scope.currentViewIndex);
9714 * @name Buttons, links & UI controls.att:switches
9717 * <file src="src/switches/docs/readme.md" />
9721 * <!-- On / Off Toggle switch -->
9722 * <label for="switch1" class="controlled-text-wrap"> This is ON
9723 * <input type="checkbox" role="checkbox" b2b-switches id="switch1" ng-model="switch1.value">
9726 * <!-- On / Off Toggle switch and DISABLED -->
9727 * <label for="switch2" class="controlled-text-wrap"> This is ON (disabled)
9728 * <input type="checkbox" role="checkbox" b2b-switches id="switch2" ng-model="switch1.value" ng-disabled="true" >
9733 * <section id="code">
9734 <b>HTML + AngularJS</b>
9735 <example module="b2b.att">
9736 <file src="src/switches/docs/demo.js" />
9737 <file src="src/switches/docs/demo.html" />
9741 angular.module('b2b.att.switches', ['b2b.att.utilities'])
9742 .directive('b2bSwitches', ['$compile', '$templateCache', 'keymap', 'events', function ($compile, $templateCache, keymap, events) {
9745 require: ['ngModel'],
9746 link: function (scope, element, attrs, ctrl) {
9747 var ngModelController = ctrl[0];
9749 element.parent().bind("keydown", function (e) {
9750 if (!attrs.disabled && (e.keyCode === keymap.KEY.ENTER || e.keyCode === keymap.KEY.SPACE)) {
9751 events.preventDefault(e);
9752 ngModelController.$setViewValue(!ngModelController.$viewValue);
9753 element.prop("checked", ngModelController.$viewValue);
9757 element.wrap('<div class="btn-switch">');
9758 //element.attr("tabindex", -1);
9759 if (navigator.userAgent.match(/iphone/i)){
9760 element.attr("aria-live", "polite");
9763 element.removeAttr('aria-live');
9766 var templateSwitch = angular.element($templateCache.get("b2bTemplate/switches/switches.html"));
9767 if (angular.isDefined(attrs.typeSpanish)) {
9768 templateSwitch = angular.element($templateCache.get("b2bTemplate/switches/switches-spanish.html"));
9771 templateSwitch = $compile(templateSwitch)(scope);
9772 element.parent().append(templateSwitch);
9774 element.bind("focus", function (e) {
9775 element.parent().addClass('focused');
9778 element.bind("blur", function (e) {
9779 element.parent().removeClass('focused');
9786 * @name Template.att:Table with Drag and Drop
9789 * <file src="src/tableDragAndDrop/docs/readme.md" />
9792 * <section id="code">
9793 <example module="b2b.att">
9794 <file src="src/tableDragAndDrop/docs/demo.html" />
9795 <file src="src/tableDragAndDrop/docs/demo.js" />
9800 angular.module('b2b.att.tableDragAndDrop', ['b2b.att.utilities','b2b.att.tables'])
9804 * @name Messages, modals & alerts.att:tableMessages
9807 * <file src="src/tableMessages/docs/readme.md" />
9810 <!-- no matching results -->
9811 <b2b-table-message msg-type="'noMatchingResults'">
9812 <p>No Matching Results</p>
9813 </b2b-table-message>
9815 <!-- info could not load -->
9816 <b2b-table-message msg-type="'infoCouldNotLoad'" on-refresh-click="refreshClicked()">
9817 </b2b-table-message>
9819 <!-- magnify search -->
9820 <b2b-table-message msg-type="'magnifySearch'">
9821 </b2b-table-message>
9823 <!-- loading data -->
9824 <b2b-table-message msg-type="'loadingTable'">
9825 <!-- custom html -->
9826 <p>The data is currently loading...</p>
9827 </b2b-table-message>
9831 <b>HTML + AngularJS</b>
9832 <example module="b2b.att">
9833 <file src="src/tableMessages/docs/demo.html" />
9834 <file src="src/tableMessages/docs/demo.js" />
9838 angular.module('b2b.att.tableMessages', [])
9839 .directive('b2bTableMessage', [function() {
9848 templateUrl: 'b2bTemplate/tableMessages/tableMessage.html',
9849 link: function(scope) {
9850 scope.refreshAction = function(evt) {
9851 scope.onRefreshClick(evt);
9859 * @name Tabs, tables & accordions.att:tableScrollbar
9862 * <file src="src/tableScrollbar/docs/readme.md" />
9866 <b2b-table-scrollbar>
9868 <thead type="header">
9870 <th role="columnheader" scope="col" key="Id" id="col1">Id</th>
9876 <td id="rowheader0" headers="col1">1002</td>
9881 </b2b-table-scrollbar>
9884 * <section id="code">
9885 <example module="b2b.att">
9886 <file src="src/tableScrollbar/docs/demo.html" />
9887 <file src="src/tableScrollbar/docs/demo.js" />
9892 angular.module('b2b.att.tableScrollbar', [])
9893 .directive('b2bTableScrollbar', ['$timeout', function ($timeout) {
9898 templateUrl: 'b2bTemplate/tableScrollbar/tableScrollbar.html',
9899 link: function (scope, element, attrs, ctrl) {
9900 var firstThWidth, firstTdWidth, firstColumnWidth, firstColumnHeight, trHeight = 0;
9901 var pxToScroll = '';
9902 var tableElement = element.find('table');
9903 var thElements = element.find('th');
9904 var tdElements = element.find('td');
9905 var innerContainer = angular.element(element[0].querySelector('.b2b-table-inner-container'));
9906 var outerContainer = angular.element(element[0].querySelector('.b2b-table-scrollbar'));
9908 scope.disableLeft = true;
9909 scope.disableRight = false;
9911 if (angular.isDefined(thElements[0])) {
9912 firstThWidth = thElements[0].offsetWidth;
9914 if (angular.isDefined(tdElements[0])) {
9915 firstTdWidth = tdElements[0].offsetWidth;
9917 firstColumnWidth = (firstThWidth > firstTdWidth) ? firstThWidth : firstTdWidth;
9919 innerContainer.css({
9920 'padding-left': (firstColumnWidth + 2) + 'px'
9923 angular.forEach(element.find('tr'), function (eachTr, index) {
9924 trObject = angular.element(eachTr);
9925 firstColumn = angular.element(trObject.children()[0]);
9927 angular.element(firstColumn).css({
9929 'width': (firstColumnWidth + 2) + 'px',
9930 'position': 'absolute'
9933 trHeight = trObject[0].offsetHeight;
9934 firstColumnHeight = firstColumn[0].offsetHeight;
9935 if (navigator.userAgent.toLowerCase().indexOf('firefox') > -1) {
9936 firstColumnHeight += 1;
9939 if (trHeight !== firstColumnHeight - 1) {
9940 if (trHeight > firstColumnHeight) {
9941 if (navigator.userAgent.toLowerCase().indexOf('firefox') > -1) {
9944 angular.element(firstColumn).css({
9945 'height': (trHeight + 1) + 'px'
9948 angular.element(trObject).css({
9949 'height': (firstColumnHeight - 1) + 'px'
9956 pxToScroll = outerContainer[0].offsetWidth - firstColumnWidth;
9958 scope.scrollLeft = function () {
9959 innerContainer[0].scrollLeft = innerContainer[0].scrollLeft + 20 - pxToScroll;
9962 scope.scrollRight = function () {
9963 innerContainer[0].scrollLeft = innerContainer[0].scrollLeft + pxToScroll - 20;
9966 scope.checkScrollArrows = function () {
9967 if (innerContainer[0].scrollLeft == 0) {
9968 scope.disableLeft = true;
9970 scope.disableLeft = false;
9973 if (((innerContainer[0].offsetWidth - firstColumnWidth) + innerContainer[0].scrollLeft) >= tableElement[0].offsetWidth) {
9974 scope.disableRight = true;
9976 scope.disableRight = false;
9981 innerContainer.bind('scroll', function () {
9982 $timeout(function () {
9983 scope.checkScrollArrows();
9992 * @name Tabs, tables & accordions.att:tables
9995 * <file src="src/tables/docs/readme.md" />
10000 <table b2b-table table-data="tableData" search-string="searchString">
10001 <thead b2b-table-row type="header">
10003 <th b2b-table-header key="requestId" default-sort="a" id="col1">Header 1</th>
10004 <th b2b-table-header key="requestType" sortable="false" id="col2">Header 2</th>
10007 <tbody b2b-table-row type="body" row-repeat="rowData in tableData">
10009 <td b2b-table-body id="rowheader{{$index}}" headers="col1" ng-bind="rowData['requestId']"> </td>
10010 <td b2b-table-body ng-bind="rowData['requestType']"></td>
10016 * <section id="code">
10017 <example module="b2b.att">
10018 <file src="src/tables/docs/demo.html" />
10019 <file src="src/tables/docs/demo.js" />
10024 angular.module('b2b.att.tables', ['b2b.att.utilities'])
10025 .constant('b2bTableConfig', {
10026 defaultSortPattern: false, // true for descending & false for ascending
10027 highlightSearchStringClass: 'tablesorter-search-highlight',
10028 zebraStripCutOff: 6, // > zebraStripCutOff
10029 tableBreakpoints: [ // breakpoints are >= min and < max
10052 .directive('b2bTable', ['$filter', '$window', 'b2bTableConfig', '$timeout', function ($filter, $window, b2bTableConfig, $timeout) {
10062 searchCategory: "=",
10066 require: 'b2bTable',
10067 templateUrl: 'b2bTemplate/tables/b2bTable.html',
10068 controller: ['$scope', '$attrs', function ($scope, $attrs) {
10070 this.currentSortIndex = null;
10071 this.responsive = $scope.responsive = $attrs.responsive;
10072 this.maxTableColumns = -1;
10073 this.totalTableColums = 0;
10074 this.active = $scope.active = false;
10075 this.responsiveRowScopes = [];
10076 this.hideColumnPriority = [];
10077 this.hiddenColumn = [];
10078 this.setIndex = function (headerScope, priority) {
10079 this.headers.push(headerScope);
10080 if (this.responsive) {
10081 this.totalTableColums++;
10082 if (!isNaN(priority)) {
10083 this.hideColumnPriority[priority] = this.totalTableColums - 1;
10085 this.hideColumnPriority[this.totalTableColums - 1] = this.totalTableColums - 1;
10088 return this.totalTableColums - 1;
10090 this.getIndex = function (headerName) {
10091 for (var i = 0; i < this.headers.length; i++) {
10092 if (this.headers[i].headerName === headerName) {
10093 return this.headers[i].index;
10098 this.setResponsiveRow = function (responsiveRowScope) {
10099 this.responsiveRowScopes.push(responsiveRowScope);
10101 $scope.nextSort = '';
10102 this.sortData = function (columnIndex, reverse, externalSort) {
10103 if ($scope.$parent && $scope.$parent !== undefined) {
10104 $scope.$parent.columnIndex = columnIndex;
10105 $scope.$parent.reverse = reverse;
10107 this.currentSortIndex = columnIndex;
10108 if (externalSort === true) {
10110 $scope.nextSort = 'd'
10112 $scope.nextSort = 'a'
10115 $scope.currentPage = 1;
10116 this.resetSortPattern();
10118 this.getSearchString = function () {
10119 return $scope.searchString;
10121 this.resetSortPattern = function () {
10122 for (var i = 0; i < this.headers.length; i++) {
10123 var currentScope = this.headers[i];
10124 if (currentScope.index !== this.currentSortIndex) {
10125 currentScope.resetSortPattern();
10130 $scope.$watch('nextSort', function (val) {
10131 if ($scope.$parent && $scope.$parent !== undefined) {
10132 $scope.$parent.nextSort = val;
10137 link: function (scope, elem, attr, ctrl) {
10138 scope.searchCriteria = {};
10139 scope.tableBreakpoints = attr.tableConfig ? scope.$parent.$eval(attr.tableConfig) : angular.copy(b2bTableConfig.tableBreakpoints);
10140 scope.$watchCollection('tableData', function (value) {
10141 if (value && !isNaN(value.length)) {
10142 scope.totalRows = value.length;
10145 scope.$watch('currentPage', function (val) {
10146 if (scope.$parent && scope.$parent !== undefined) {
10147 scope.$parent.currentPage = val;
10151 scope.$watch('viewPerPage', function (val) {
10152 if (scope.$parent && scope.$parent !== undefined) {
10153 scope.$parent.viewPerPage = val;
10156 scope.$watch('totalRows', function (val) {
10157 if (scope.$parent && scope.$parent !== undefined) {
10158 if (val > b2bTableConfig.zebraStripCutOff) {
10159 scope.$parent.zebraStripFlag = true;
10161 scope.$parent.zebraStripFlag = false;
10165 scope.$watch(function () {
10166 return scope.totalRows / scope.viewPerPage;
10167 }, function (value) {
10168 if (!isNaN(value)) {
10169 scope.totalPage = Math.ceil(value);
10170 scope.currentPage = 1;
10173 var searchValCheck = function (val) {
10174 if (angular.isDefined(val) && val !== null && val !== "") {
10178 var setSearchCriteria = function (v1, v2) {
10179 if (searchValCheck(v1) && searchValCheck(v2)) {
10180 var index = ctrl.getIndex(v2);
10181 scope.searchCriteria = {};
10182 if (index !== null) {
10183 scope.searchCriteria[index] = v1;
10185 } else if (searchValCheck(v1) && (!angular.isDefined(v2) || v2 === null || v2 === "")) {
10186 scope.searchCriteria = {
10190 scope.searchCriteria = {};
10193 scope.$watch('searchCategory', function (newVal, oldVal) {
10194 if (newVal !== oldVal) {
10195 setSearchCriteria(scope.searchString, newVal);
10198 scope.$watch('searchString', function (newVal, oldVal) {
10199 if (newVal !== oldVal) {
10200 setSearchCriteria(newVal, scope.searchCategory);
10203 scope.$watchCollection('searchCriteria', function (val) {
10204 if (scope.$parent && scope.$parent !== undefined) {
10205 scope.$parent.searchCriteria = val;
10207 scope.totalRows = scope.tableData && ($filter('filter')(scope.tableData, val, false)).length || 0;
10208 scope.currentPage = 1;
10210 var window = angular.element($window);
10211 var findMaxTableColumns = function () {
10213 windowWidth = $window.innerWidth;
10214 ctrl.maxTableColumns = -1;
10215 for (var i in scope.tableBreakpoints) {
10216 if (windowWidth >= scope.tableBreakpoints[i].min && windowWidth < scope.tableBreakpoints[i].max) {
10217 ctrl.maxTableColumns = scope.tableBreakpoints[i].columns;
10221 if (ctrl.maxTableColumns > -1 && ctrl.totalTableColums > ctrl.maxTableColumns) {
10222 ctrl.active = true;
10224 ctrl.active = false;
10226 for (var i in ctrl.responsiveRowScopes) {
10227 ctrl.responsiveRowScopes[i].setActive(ctrl.active);
10230 var findHiddenColumn = function () {
10231 var columnDiffenence = ctrl.maxTableColumns > -1 ? ctrl.totalTableColums - ctrl.maxTableColumns : 0;
10232 ctrl.hiddenColumn = [];
10233 if (columnDiffenence > 0) {
10234 var tempHideColumnPriority = angular.copy(ctrl.hideColumnPriority);
10235 for (var i = 0; i < columnDiffenence; i++) {
10236 ctrl.hiddenColumn.push(tempHideColumnPriority.pop());
10240 var resizeListener = function () {
10241 findMaxTableColumns();
10242 findHiddenColumn();
10244 if (ctrl.responsive) {
10245 window.bind('resize', function () {
10249 $timeout(function () {
10256 .directive('b2bTableRow', [function () {
10259 compile: function (elem, attr) {
10260 if (attr.type === 'header') {
10262 } else if (attr.type === 'body') {
10263 var html = elem.children();
10264 if (attr.rowRepeat) {
10265 html.attr('ng-repeat', attr.rowRepeat.concat(" | orderBy : (reverse?'-':'')+ columnIndex | filter : searchCriteria : false "));
10267 html.attr('ng-class', "{'odd': $odd && zebraStripFlag}");
10268 html.attr('class', 'data-row');
10269 html.attr('b2b-responsive-row', '{{$index}}');
10275 .directive('b2bTableHeader', ['b2bTableConfig', function (b2bTableConfig) {
10285 require: '^b2bTable',
10286 templateUrl: function (elem, attr) {
10287 if (attr.sortable === 'false') {
10288 return 'b2bTemplate/tables/b2bTableHeaderUnsortable.html';
10290 return 'b2bTemplate/tables/b2bTableHeaderSortable.html';
10293 link: function (scope, elem, attr, ctrl) {
10294 var reverse = b2bTableConfig.defaultSortPattern;
10295 scope.headerName = elem.text();
10296 scope.headerId = elem.attr('id');
10297 scope.sortPattern = null;
10298 var priority = parseInt(attr.priority, 10);
10299 scope.columnIndex = ctrl.setIndex(scope, priority);
10301 scope.isHidden = function () {
10302 return (ctrl.hiddenColumn.indexOf(scope.columnIndex) > -1);
10305 scope.$watch(function () {
10306 return elem.text();
10307 }, function (value) {
10308 scope.headerName = value;
10310 scope.sort = function (sortType) {
10311 if (typeof sortType === 'boolean') {
10312 reverse = sortType;
10314 ctrl.sortData(scope.index, reverse, false);
10315 scope.sortPattern = reverse ? 'descending' : 'ascending';
10316 reverse = !reverse;
10318 scope.$watch(function () {
10319 return ctrl.currentSortIndex;
10320 }, function (value) {
10321 if (value !== scope.index) {
10322 scope.sortPattern = null;
10326 if (scope.sortable === undefined || scope.sortable === 'true' || scope.sortable === true) {
10327 scope.sortable = 'true';
10328 } else if (scope.sortable === false || scope.sortable === 'false') {
10329 scope.sortable = 'false';
10332 if (scope.sortable !== 'false') {
10333 if (scope.defaultSort === 'A' || scope.defaultSort === 'a') {
10335 } else if (scope.defaultSort === 'D' || scope.defaultSort === 'd') {
10339 scope.resetSortPattern = function () {
10340 reverse = b2bTableConfig.defaultSortPattern;
10345 .directive('b2bResponsiveRow', ['$templateCache', '$timeout', '$compile', function ($templateCache, $timeout, $compile) {
10348 require: '^b2bTable',
10349 controller: ['$scope', function ($scope) {
10350 this.rowValues = $scope.rowValues = [];
10351 this.setRowValues = function (rowValue) {
10352 this.rowValues.push(rowValue);
10354 var columnIndexCounter = -1;
10355 this.getIndex = function () {
10356 columnIndexCounter++;
10357 return columnIndexCounter;
10360 link: function (scope, elem, attr, ctrl) {
10361 if (ctrl.responsive) {
10362 scope.rowIndex = attr.b2bResponsiveRow;
10363 scope.active = false;
10364 scope.expandFlag = false;
10365 scope.headerValues = ctrl.headers;
10366 ctrl.setResponsiveRow(scope);
10367 var firstTd = elem.find('td').eq(0);
10368 scope.setActive = function (activeFlag) {
10369 scope.active = activeFlag;
10370 if (scope.active) {
10371 elem.addClass('has-button');
10372 firstTd.attr('role', 'rowheader');
10373 firstTd.parent().attr('role', 'row');
10375 elem.removeClass('has-button');
10376 firstTd.removeAttr('role');
10377 firstTd.parent().removeAttr('role');
10380 scope.toggleExpandFlag = function (expandFlag) {
10381 if (angular.isDefined(expandFlag)) {
10382 scope.expandFlag = expandFlag;
10384 scope.expandFlag = !scope.expandFlag;
10386 if (scope.expandFlag) {
10387 elem.addClass('opened');
10389 elem.removeClass('opened');
10393 firstTd.attr('scope', 'row');
10394 firstTd.addClass('col-1');
10395 scope.$on('$destroy', function () {
10396 elem.next().remove();
10398 $timeout(function () {
10399 scope.firstTdId = firstTd.attr('id');
10400 var firstTdContent = firstTd.html();
10401 var toggleButtonTemplate = '<span ng-show="!active">' + firstTdContent + '</span><button type="button" aria-describedby="sup-actNum{{$id}}" aria-expanded="{{expandFlag}}" ng-show="active" ng-click="toggleExpandFlag()"><i ng-class="{\'icon-primary-accordion-plus\': !expandFlag, \'icon-primary-accordion-minus\': expandFlag}" aria-hidden="true"></i>' + firstTdContent + '</button><span id="sup-actNum{{$id}}" style="display:none">{{expandFlag && "Hide row below." || "Show row below."}}</span>';
10402 toggleButtonTemplate = $compile(toggleButtonTemplate)(scope);
10404 firstTd.prepend(toggleButtonTemplate);
10406 var template = $templateCache.get('b2bTemplate/tables/b2bResponsiveRow.html');
10407 template = $compile(template)(scope);
10408 elem.after(template);
10414 .directive('b2bResponsiveList', ['$templateCache', '$timeout', '$compile', function ($templateCache, $timeout, $compile) {
10417 require: '^b2bTable',
10418 link: function (scope, elem, attr, ctrl) {
10419 scope.columnIndex = parseInt(attr.b2bResponsiveList, 10);
10420 scope.isVisible = function () {
10421 return (ctrl.hiddenColumn.indexOf(scope.columnIndex) > -1);
10426 .directive('b2bTableBody', ['$filter', '$timeout', 'b2bTableConfig', function ($filter, $timeout, b2bTableConfig) {
10429 require: ['^b2bTable', '?^b2bResponsiveRow'],
10433 templateUrl: 'b2bTemplate/tables/b2bTableBody.html',
10434 link: function (scope, elem, attr, ctrl) {
10435 var b2bTableCtrl = ctrl[0];
10436 var b2bResponsiveRowCtrl = ctrl[1];
10437 var highlightSearchStringClass = b2bTableConfig.highlightSearchStringClass;
10438 var searchString = "";
10439 var wrapElement = function (elem) {
10440 var text = elem.text();
10441 elem.html($filter('b2bHighlight')(text, searchString, highlightSearchStringClass));
10443 var traverse = function (elem) {
10444 var innerHtml = elem.children();
10445 if (innerHtml.length > 0) {
10446 for (var i = 0; i < innerHtml.length; i++) {
10447 traverse(innerHtml.eq(i));
10454 var clearWrap = function (elem) {
10455 var elems = elem.find('*');
10456 for (var i = 0; i < elems.length; i++) {
10457 if (elems.eq(i).attr('class') && elems.eq(i).attr('class').indexOf(highlightSearchStringClass) !== -1) {
10458 var text = elems.eq(i).text();
10459 elems.eq(i).replaceWith(text);
10463 if (b2bResponsiveRowCtrl) {
10464 scope.columnIndex = b2bResponsiveRowCtrl.getIndex();
10465 scope.isHidden = function () {
10466 return (b2bTableCtrl.hiddenColumn.indexOf(scope.columnIndex) > -1);
10469 $timeout(function () {
10470 var actualHtml = elem.children();
10471 scope.$watch(function () {
10472 return b2bTableCtrl.getSearchString();
10473 }, function (val) {
10474 searchString = val;
10476 if (actualHtml.length > 0) {
10482 if (b2bResponsiveRowCtrl) {
10483 b2bResponsiveRowCtrl.setRowValues(elem.html());
10489 .directive('b2bTableSort', ['b2bTableConfig','$timeout', function (b2bTableConfig,$timeout) {
10493 require: '^b2bTable',
10494 link: function (scope, elem, attr, ctrl) {
10495 var initialSort = '',
10498 initialSort = attr.initialSort;
10500 scope.sortTable = function (msg,trigger) {
10501 if(trigger == 'dropdown'){
10502 if (nextSort === 'd' || nextSort === 'D') {
10503 ctrl.sortData(msg, false, false);
10505 ctrl.sortData(msg, true, false);
10509 $timeout(function(){
10510 if (nextSort.length > 0) {
10512 if (nextSort === 'd' || nextSort === 'D') {
10513 tempsort = nextSort
10514 ctrl.sortData(msg, true, true);
10516 $timeout(function(){
10517 if(!angular.isUndefined(elem[0].querySelector('.sortButton')) || elem[0].querySelector('.sortButton') !== null ){
10518 angular.element(elem[0].querySelector('.sortButton'))[0].focus();
10523 tempsort = nextSort
10524 ctrl.sortData(msg, false, true);
10526 $timeout(function(){
10527 if(!angular.isUndefined(elem[0].querySelector('.sortButton')) || elem[0].querySelector('.sortButton') !== null ){
10528 angular.element(elem[0].querySelector('.sortButton'))[0].focus();
10532 } else if (initialSort.length > 0) {
10534 if (initialSort === 'd' || initialSort === 'D') {
10535 tempsort = nextSort
10536 ctrl.sortData(msg, true, true);
10538 $timeout(function(){
10539 if(!angular.isUndefined(elem[0].querySelector('.sortButton')) || elem[0].querySelector('.sortButton') !== null ){
10540 angular.element(elem[0].querySelector('.sortButton'))[0].focus();
10545 tempsort = nextSort
10546 ctrl.sortData(msg, false, true);
10548 $timeout(function(){
10549 if(!angular.isUndefined(elem[0].querySelector('.sortButton')) || elem[0].querySelector('.sortButton') !== null ){
10550 angular.element(elem[0].querySelector('.sortButton'))[0].focus();
10561 scope.sortDropdown = function(msg) {
10563 if(tempsort==='') {
10567 if(tempsort === 'd' || tempsort === 'D' ) {
10568 ctrl.sortData(msg, true, false);
10570 ctrl.sortData(msg, false, false);
10579 * @name Tabs, tables & accordions.att:tabs
10582 * <file src="src/tabs/docs/readme.md" />
10585 * <b2b-tabset tab-id-selected="activeTabsId">
10586 <b2b-tab ng-repeat="tab in gTabs" tab-item="tab"
10587 id="{{tab.uniqueId}}" aria-controls="{{tab.tabPanelId}}"
10588 ng-disabled="tab.disabled">
10594 * <section id="code">
10595 <example module="b2b.att">
10596 <file src="src/tabs/docs/demo.html" />
10597 <file src="src/tabs/docs/demo.js" />
10603 angular.module('b2b.att.tabs', ['b2b.att.utilities'])
10604 .directive('b2bTabset', function () {
10612 templateUrl: 'b2bTemplate/tabs/b2bTabset.html',
10613 controller: ['$scope', function ($scope) {
10615 this.setTabIdSelected = function (tab) {
10616 $scope.tabIdSelected = tab.id;
10619 this.getTabIdSelected = function () {
10620 return $scope.tabIdSelected;
10625 .directive('b2bTab', ['keymap', function (keymap) {
10630 require: '^b2bTabset',
10634 templateUrl: 'b2bTemplate/tabs/b2bTab.html',
10635 controller: [function(){}],
10636 link: function (scope, element, attr, b2bTabsetCtrl) {
10638 if (scope.tabItem && !scope.tabItem.disabled) {
10639 scope.tabItem.disabled = false;
10642 scope.isTabActive = function () {
10643 return (scope.tabItem.id === b2bTabsetCtrl.getTabIdSelected());
10646 scope.clickTab = function () {
10647 if (attr.disabled) {
10650 b2bTabsetCtrl.setTabIdSelected(scope.tabItem);
10653 scope.nextKey = function () {
10654 var el = angular.element(element[0])[0];
10655 var elementToFocus = null;
10656 while (el && el.nextElementSibling) {
10657 el = el.nextElementSibling;
10658 if (!el.querySelector('a').disabled) {
10659 elementToFocus = el.querySelector('a');
10664 if (!elementToFocus) {
10665 var childTabs = element.parent().children();
10666 for (var i = 0; i < childTabs.length; i++) {
10667 if (!childTabs[i].querySelector('a').disabled) {
10668 elementToFocus = childTabs[i].querySelector('a');
10674 if (elementToFocus) {
10675 elementToFocus.focus();
10679 scope.previousKey = function () {
10680 var el = angular.element(element[0])[0];
10681 var elementToFocus = null;
10683 while (el && el.previousElementSibling) {
10684 el = el.previousElementSibling;
10685 if (!el.querySelector('a').disabled) {
10686 elementToFocus = el.querySelector('a');
10691 if (!elementToFocus) {
10692 var childTabs = element.parent().children();
10693 for (var i = childTabs.length - 1; i > 0; i--) {
10694 if (!childTabs[i].querySelector('a').disabled) {
10695 elementToFocus = childTabs[i].querySelector('a');
10701 if (elementToFocus) {
10702 elementToFocus.focus();
10706 angular.element(element[0].querySelector('a')).bind('keydown', function (evt) {
10708 if (!(evt.keyCode)) {
10709 evt.keyCode = evt.which;
10712 switch (evt.keyCode) {
10713 case keymap.KEY.RIGHT:
10714 evt.preventDefault();
10718 case keymap.KEY.LEFT:
10719 evt.preventDefault();
10720 scope.previousKey();
10731 * @name Messages, modals & alerts.att:tagBadges
10734 * <file src="src/tagBadges/docs/readme.md" />
10737 * <section id="code">
10738 <example module="b2b.att">
10739 <file src="src/tagBadges/docs/demo.html" />
10740 <file src="src/tagBadges/docs/demo.js" />
10745 angular.module('b2b.att.tagBadges', ['b2b.att.utilities'])
10746 .directive('b2bTagBadge',['$timeout',function($timeout){
10749 link: function(scope,elem,attr,ctrl){
10750 elem.addClass('b2b-tags');
10751 if(angular.element(elem[0].querySelector('.icon-primary-close')).length>0) {
10752 var item = angular.element(elem[0].querySelector('.icon-primary-close'));
10753 item.bind('click',function(){
10754 elem.css({'height':'0','width':'0','padding':'0','border':'0'});
10755 elem.attr('tabindex','0');
10757 item.parent().remove();
10758 elem[0].bind('blur',function(){
10772 * @name Forms.att:textArea
10775 * <file src="src/textArea/docs/readme.md" />
10778 * <textarea b2b-reset b2b-reset-textarea ng-model="textAreaModel" ng-disabled="disabled" ng-trim="false" placeholder="{{placeholderText}}" rows="{{textAreaRows}}" maxlength="{{textAreaMaxlength}}" role="textarea"></textarea>
10781 <section id="code">
10782 <b>HTML + AngularJS</b>
10783 <example module="b2b.att">
10784 <file src="src/textArea/docs/demo.html" />
10785 <file src="src/textArea/docs/demo.js" />
10789 angular.module('b2b.att.textArea', ['b2b.att.utilities'])
10791 .directive('b2bResetTextarea', [ function () {
10794 require: 'b2bReset',
10795 link: function (scope, element, attrs, ctrl) {
10797 var resetButton = ctrl.getResetButton();
10799 var computeScrollbarAndAddClass = function () {
10800 if (element.prop('scrollHeight') > element[0].clientHeight) {
10801 element.addClass('hasScrollbar');
10803 element.removeClass('hasScrollbar');
10807 computeScrollbarAndAddClass();
10809 element.on('focus keyup', function(){
10810 computeScrollbarAndAddClass();
10818 * @name Forms.att:timeInputField
10821 * <file src="src/timeInputField/docs/readme.md" />
10825 * <section id="code">
10826 <example module="b2b.att">
10827 <file src="src/timeInputField/docs/demo.html" />
10828 <file src="src/timeInputField/docs/demo.js" />
10833 angular.module('b2b.att.timeInputField',['ngMessages', 'b2b.att.utilities']).directive('b2bTimeFormat',function(){
10836 require : '^ngModel',
10837 link : function(scope,elem,attr,ctrl){
10838 elem.on('keyup',function(evt){
10839 var modelValue = ctrl.$modelValue;
10840 var format = attr.b2bTimeFormat;
10841 modelValue = modelValue.split(':');
10842 if(format == "12"){
10843 if(!(modelValue[0] <= 12 && modelValue[0] > 0 ) || !(modelValue[1] <= 59)){
10844 ctrl.$setValidity('inValidTime',false);
10846 ctrl.$setValidity('inValidTime',true);
10848 }else if(format =="24"){
10849 if(!(modelValue[0] <= 23) || !(modelValue[1] <= 59)){
10850 ctrl.$setValidity('inValidTime',false);
10852 ctrl.$setValidity('inValidTime',true);
10863 * @name Forms.att:tooltipsForForms
10866 * <file src="src/tooltipsForForms/docs/readme.md" />
10869 <example module="b2b.att">
10870 <file src="src/tooltipsForForms/docs/demo.html" />
10871 <file src="src/tooltipsForForms/docs/demo.js" />
10874 angular.module('b2b.att.tooltipsForForms', ['b2b.att.utilities'])
10875 .directive('b2bTooltip', ['$document', '$window', '$isElement', function ($document, $window, $isElement) {
10878 link: function (scope, elem, attr, ctrl) {
10879 var icon = elem[0].querySelector('a.tooltip-element');
10880 var btnIcon = elem[0].querySelector('.btn.tooltip-element');
10881 var tooltipText = elem[0].querySelector('.helpertext');
10882 var tooltipWrapper = elem[0].querySelector('.tooltip-size-control');
10883 if (elem.hasClass('tooltip-onfocus')) {
10884 var inputElm = angular.element(elem[0].querySelector("input"));
10885 var textAreaElm = angular.element(elem[0].querySelector("textarea"));
10887 angular.element(icon).attr({'aria-expanded': false});
10888 angular.element(btnIcon).attr({'aria-expanded': false});
10889 var calcTooltip = function () {
10890 if (!elem.hasClass('tooltip active')) {
10891 if (elem.hasClass('tooltip-onfocus')) {
10892 angular.element(elem[0].querySelector("input")).triggerHandler('focusout');
10894 if (elem.hasClass('tooltip-onclick')) {
10897 angular.element(icon).removeClass('active');
10898 angular.element(icon).attr({'aria-expanded': true});
10899 angular.element(icon).attr({'aria-describedby': angular.element(tooltipText).attr('id')});
10900 angular.element(tooltipText).attr({'aria-hidden': false});
10901 elem.addClass('active');
10903 var tooltipIconPos = angular.element(icon).prop('offsetLeft'),
10904 tooltipPosition = angular.element(tooltipText).prop('offsetWidth') / 2,
10905 tipOffset = (tooltipIconPos - 30) - tooltipPosition,
10906 maxRightPos = (($window.innerWidth - 72) - (tooltipPosition * 2)) - 14.5;
10908 if ($window.innerWidth >= '768') {
10909 if (tipOffset < 0) {// if icon on far left side of page
10912 else if (tooltipIconPos > maxRightPos) {// if icon is far right side of page
10913 tipOffset = maxRightPos;
10915 else {// if tooltip in the middle somewhere
10916 tipOffset = tipOffset;
10918 angular.element(tooltipWrapper).css({left: tipOffset + 'px'});
10923 // TOOLTIP LINK ONCLICK AND FOCUS
10924 angular.element(icon).on('click mouseover mouseout focus blur', function (e) {
10925 if (e.type == 'mouseover') {
10928 else if (e.type == 'mouseout' && elem.hasClass('active')) {
10929 if (!elem.hasClass('activeClick')) {
10930 angular.element(tooltipText).attr({
10931 'aria-hidden': true,
10934 elem.removeClass('active');
10935 } else if (elem.hasClass('activeClick') && navigator.userAgent.match(/iphone/i)) {
10936 elem.removeClass('active activeClick');
10941 if (elem.hasClass('activeClick')) {
10942 angular.element(icon).attr({'aria-expanded': false});
10943 angular.element(tooltipText).attr({'aria-hidden': true});
10944 angular.element(icon).removeAttr('aria-describedby');
10945 elem.removeClass('activeClick active');
10946 e.preventDefault();
10948 else if (e.type == 'click') {
10949 elem.addClass('activeClick');
10951 e.preventDefault();
10954 angular.element(icon).on('keydown', function (e) {
10955 if (e.keyCode == '32') {
10956 (elem.hasClass('active')) ? elem.removeClass('active') : elem.addClass('value');
10957 angular.element(icon).triggerHandler('click');
10958 e.preventDefault();
10959 } else if (e.keyCode == '27') {
10960 (elem.hasClass('active')) ? elem.removeClass('active activeClick') : elem.addClass('value');
10963 e.preventDefault();
10966 e.preventDefault();
10969 // TOOLTIP BUTTON INSIDE A TEXT FIELD
10970 angular.element(btnIcon).on('click', function (e) {
10971 var $this = angular.element(this);
10972 if ($this.hasClass('active') && elem.hasClass('tooltip-onclick')) {
10973 elem.removeClass('active');
10974 $this.removeClass('active');
10975 angular.element(tooltipText).removeAttr('aria-live');
10976 $this.attr({'aria-expanded': 'false'});
10977 $this.removeAttr('aria-describedby');
10979 elem.addClass('active');
10980 $this.addClass('active');
10981 $this.attr({'aria-expanded': 'true', 'aria-describedby': angular.element(tooltipText).attr('id')});
10982 angular.element(tooltipText).attr({'aria-live': 'polite'});
10986 angular.element(btnIcon).on('blur', function (e) {
10987 var $this = angular.element(this);
10988 if ($this.hasClass('active') && elem.hasClass('tooltip-onclick')) {
10989 elem.removeClass('active');
10990 $this.removeClass('active');
10991 angular.element(tooltipText).removeAttr('aria-live');
10992 $this.attr({'aria-expanded': 'false'});
10993 $this.removeAttr('aria-describedby');
10997 angular.element(btnIcon).on('keydown', function (e) {
10998 var $this = angular.element(this);
10999 if (e.keyCode == '27') {
11000 var $this = angular.element(this);
11001 if ($this.hasClass('active') && elem.hasClass('tooltip-onclick')) {
11002 elem.removeClass('active');
11003 $this.removeClass('active');
11004 angular.element(tooltipText).removeAttr('aria-live');
11005 $this.attr({'aria-expanded': 'false'});
11006 $this.removeAttr('aria-describedby');
11011 // close all tooltips if clicking something else
11012 $document.bind('click', function (e) {
11013 var isElement = $isElement(angular.element(e.target), elem, $document);
11015 elem.removeClass('active');
11016 angular.element(elem[0].querySelector('.tooltip-element')).removeClass('active');
11017 angular.element(tooltipText).removeAttr('aria-live');
11018 angular.element(elem[0].querySelector('.tooltip-element')).attr({'aria-expanded': 'false'});
11019 angular.element(elem[0].querySelector('.tooltip-element')).removeAttr('aria-describedby');
11023 angular.element(inputElm).on('keydown', function (e) {
11024 if (e.keyCode == '27'){
11025 elem.removeClass('active');
11026 angular.element(tooltipText).css('display', 'none');
11027 angular.element(tooltipText).removeAttr('aria-live');
11029 if (angular.element(this).attr('aria-describedby') === undefined){
11033 else if ((spaceIndex = angular.element(this).attr('aria-describedby').lastIndexOf(' ')) >= 0){
11035 var describedByValue = angular.element(this).attr('aria-describedby').slice(0, spaceIndex);
11037 angular.element(this).attr('aria-describedby', describedByValue);
11041 angular.element(this).removeAttr('aria-describedby');
11046 angular.element(textAreaElm).on('keydown', function (e) {
11047 if (e.keyCode == '27'){
11048 elem.removeClass('active');
11049 angular.element(tooltipText).css('display', 'none');
11050 angular.element(tooltipText).removeAttr('aria-live');
11051 if (angular.element(this).attr('aria-describedby') === undefined){
11055 else if ((spaceIndex = angular.element(this).attr('aria-describedby').lastIndexOf(' ')) >= 0){
11057 var describedByValue = angular.element(this).attr('aria-describedby').slice(0, spaceIndex);
11059 angular.element(this).attr('aria-describedby', describedByValue);
11063 angular.element(this).removeAttr('aria-describedby');
11068 // TOOLTIP TRIGGERED AUTOMATICALLY INSIDE A TEXT FIELD
11069 angular.element(inputElm).on('focus', function (e) {
11070 var allTooltip = $document[0].querySelectorAll('[class*="tooltip"]');
11071 for (var i = 0; i < allTooltip.length; i++) {
11072 if (angular.element(allTooltip[i]).hasClass('active')) {
11073 angular.element(allTooltip[i]).triggerHandler('click');
11076 angular.element(this).attr({'aria-describedby': angular.element(tooltipText).attr('id')});
11077 angular.element(tooltipText).css('display', 'block');
11078 angular.element(tooltipText).attr({'aria-live': 'polite'});
11079 elem.addClass('active');
11081 angular.element(inputElm).on('blur', function (e) {
11082 elem.removeClass('active');
11083 angular.element(tooltipText).css('display', 'none');
11084 angular.element(tooltipText).removeAttr('aria-live');
11085 angular.element(this).removeAttr('aria-describedby');
11088 // TOOLTIP TRIGGERED AUTOMATICALLY INSIDE A TEXTAREA
11089 angular.element(textAreaElm).on('focus', function (e) {
11090 var allTooltip = $document[0].querySelectorAll('[class*="tooltip"]');
11091 for (var i = 0; i < allTooltip.length; i++) {
11092 if (angular.element(allTooltip[i]).hasClass('active')) {
11093 angular.element(allTooltip[i]).triggerHandler('click');
11096 elem.addClass('active');
11097 angular.element(tooltipText).css('display', 'block');
11098 angular.element(tooltipText).attr({'aria-live': 'polite'});
11099 angular.element(this).attr({'aria-describedby': angular.element(tooltipText).attr('id')});
11101 angular.element(textAreaElm).on('blur', function (e) {
11102 elem.removeClass('active');
11103 angular.element(tooltipText).css('display', 'none');
11104 angular.element(tooltipText).removeAttr('aria-live');
11105 angular.element(this).removeAttr('aria-describedby');
11112 * @name Navigation.att:TreeNavigation
11116 * @param {String} setRole - This value needs to be "tree". This is required to incorporate CATO requirements.
11117 * @param {Boolean} groupIt - This value needs to be "false" for top-level tree rendered.
11120 * <file src="src/treeNav/docs/readme.md" />
11123 * <div class="b2b-tree">
11124 * <b2b-tree-nav collection="treeStructure" set-role="tree" group-it="false"></b2b-tree-nav>
11127 * <section id="code">
11128 <example module="b2b.att">
11129 <file src="src/treeNav/docs/demo.html" />
11130 <file src="src/treeNav/docs/demo.js" />
11135 angular.module('b2b.att.treeNav', ['b2b.att.utilities'])
11136 .directive('b2bTreeNav', function () {
11145 templateUrl: function (element, attrs) {
11146 if (attrs.groupIt === 'true') {
11147 return "b2bTemplate/treeNav/groupedTree.html";
11149 return "b2bTemplate/treeNav/ungroupedTree.html";
11152 link: function (scope) {
11153 if (!(scope.setRole === 'tree')) {
11154 scope.setRole = 'group';
11159 .directive('b2bMember', ['$compile', '$timeout', 'keymap', function ($compile, $timeout, keymap) {
11167 templateUrl: 'b2bTemplate/treeNav/treeMember.html',
11168 link: function (scope, element, attrs) {
11169 scope.elemArr = [];
11170 var removeRootTabIndex = function (elem) {
11171 if (elem.parent().parent().eq(0).hasClass('b2b-tree')) {
11172 elem.attr('tabindex', -1);
11175 removeRootTabIndex(elem.parent());
11177 scope.$watch('member.child', function(newVal, oldVal){
11178 if(newVal !== oldVal){
11182 scope.showChild = function () {
11183 if (!element.hasClass('grouped')) {
11184 if (angular.isArray(scope.member.child) && scope.member.child.length > 0 && (scope.member.divide === undefined || scope.member.child.length < scope.member.divide)) {
11185 scope.groupIt = false;
11186 element.addClass('grouped');
11187 element.append("<b2b-tree-nav collection='member.child' group-it='" + scope.groupIt + "'></b2b-tree-nav>");
11188 $compile(element.contents())(scope);
11189 if(scope.member.active && scope.member.active === true){
11190 element.find('i').eq(0).removeClass('icon-primary-collapsed');
11192 if(scope.member.selected && scope.member.selected === true){
11193 element.attr('aria-selected', true);
11194 element.attr('tabindex', 0);
11195 removeRootTabIndex(element);
11197 if(scope.member.active && scope.member.active == undefined){
11198 element.find('i').eq(0).addClass('icon-primary-collapsed');
11200 } else if (scope.member.child && scope.member.divide && scope.member.child.length > scope.member.divide) {
11201 element.addClass('grouped');
11202 scope.groupIt = true;
11203 // FILTER - GROUPBY - APPROACH
11206 if(scope.member.child[0].groupName !== undefined){
11207 grpName = scope.member.child[0].groupName;
11210 var toSlice = scope.member.child[0].name.search(' ');
11211 grpName = scope.member.child[0].name.slice(0, toSlice);
11214 for (i = 0; i < scope.member.child.length; i += scope.member.divide) {
11216 for (j = j + i; j < (i + scope.member.divide); j++) {
11217 if (j === scope.member.child.length) {
11218 scope.member.child[j - 1].grpChild = grpName + ' ' + (i + 1) + ' - ' + (scope.member.child.length);
11221 if(scope.member.child[j-1].active && scope.member.child[j-1].active===true){
11222 scope.member.child[j-1].activeGrp = true;
11226 if (i + scope.member.divide > scope.member.child.length) {
11227 scope.member.child[j].grpChild = grpName + ' ' + (i + 1) + ' - ' + (scope.member.child.length);
11228 if(scope.member.child[j].active && scope.member.child[j].active===true){
11229 scope.member.child[j].activeGrp = true;
11233 scope.member.child[j].grpChild = grpName + ' ' + (i + 1) + ' - ' + (i + scope.member.divide);
11234 if(scope.member.child[j].active && scope.member.child[j].active===true){
11235 scope.member.child[j].activeGrp = true;
11240 if(scope.member.divide){
11241 element.append("<b2b-tree-nav collection='member.child' group-it='" + scope.groupIt + "'></b2b-tree-nav>");
11243 element.append("<b2b-tree-nav collection='member.child' group-it='" + scope.groupIt + "'></b2b-tree-nav>");
11245 $compile(element.contents())(scope);
11246 if(scope.member.active && scope.member.active === true){
11247 element.find('i').eq(0).removeClass('icon-primary-collapsed');
11249 if(scope.member.selected && scope.member.selected === true){
11250 element.attr('aria-selected', true);
11252 if( scope.member.active && scope.member.active == undefined){
11253 element.find('i').eq(0).addClass('icon-primary-collapsed');
11258 //Below condition opens node for opening on json load.
11259 if(scope.member.active && scope.member.active == true){
11262 if(scope.member.active == undefined && !element.find('a').eq(0).hasClass('active') && scope.member.child !== undefined){
11263 element.find('i').eq(0).addClass('icon-primary-collapsed');
11265 else if(scope.member.child == undefined){
11266 element.find('i').eq(0).addClass('icon-primary-circle');
11268 element.bind('keydown', function (evt) {
11269 switch (evt.keyCode) {
11270 case keymap.KEY.ENTER:
11271 if (element.hasClass('bg') && scope.member.onSelect !== undefined) {
11272 scope.member.onSelect(scope.member);
11274 evt.stopPropagation();
11281 //else getting true in every case .. so better use switch case .. that makes more sense you dumb.
11282 element.bind('click', function (evt) {
11284 var expandFunc = scope.member.onExpand;
11287 if (element.hasClass('bg') && scope.member.onSelect !== undefined) {
11288 scope.member.onSelect(scope.member);
11290 if (element.find('a').eq(0).hasClass('active') && scope.member.onExpand !== undefined) {
11291 var eValue = scope.member.onExpand(scope.member);
11293 if (!element.find('a').eq(0).hasClass('active') && scope.member.onCollapse !== undefined) {
11294 scope.member.onCollapse(scope.member);
11300 .directive('b2bTreeLink', ['keymap', '$timeout', function (keymap, $timeout) {
11303 link: function (scope, element, attr, ctrl) {
11304 var rootE, parentE, upE, downE;
11305 var closeOthersUp = function (elem,isKeyPress,passiveClose) {
11306 //For accordion functionality on sibling nodes
11307 if (elem.find('a').eq(0).hasClass('active')) {
11308 activeToggle(elem,isKeyPress,passiveClose);
11311 if (elem.hasClass('bg') && !isKeyPress) {
11312 elem.removeClass('bg');
11313 if (elem.attr('aria-selected')) {
11314 elem.attr('aria-selected', 'false');
11317 if (elem[0].previousElementSibling !== null) {
11318 closeOthersUp(angular.element(elem[0].previousElementSibling),isKeyPress);
11321 var closeOthersDown = function (elem,isKeyPress,passiveClose) {
11322 //For accordion functionality on sibling nodes
11323 if (elem.find('a').eq(0).hasClass('active')) {
11324 activeToggle(elem,isKeyPress,passiveClose);
11327 if (elem.hasClass('bg') && !isKeyPress) {
11328 elem.removeClass('bg');
11329 if (elem.attr('aria-selected')) {
11330 elem.attr('aria-selected', 'false');
11333 if (elem[0].nextElementSibling !== null) {
11334 closeOthersDown(angular.element(elem[0].nextElementSibling),isKeyPress);
11339 var removeBackground = function(elem){
11341 if(elem.hasClass('b2b-tree')){
11342 angular.element(elem[0].getElementsByClassName('bg')).removeClass('bg');
11345 removeBackground(elem.parent().parent());
11351 * These two functions used for setting heights on parent nodes as the child node closes
11352 * Retaining this code for future reference
11354 var addParentHeight = function(e, h) {
11355 var parentLi = e.parent().parent();
11356 var parentUl = e.parent();
11357 if(!parentLi.hasClass('b2b-tree')) {
11358 var addHeight = parentUl[0].offsetHeight + h;
11359 parentLi.find('ul').eq(0).css({
11360 height: addHeight+'px'
11362 addParentHeight(parentLi, h);
11366 var removeParentHeight = function(e, h) {
11367 var parentLi = e.parent().parent();
11368 var parentUl = e.parent();
11369 if(!parentLi.hasClass('b2b-tree')) {
11370 var addHeight = parentUl[0].offsetHeight - h;
11371 parentLi.find('ul').eq(0).css({
11372 height: addHeight+'px'
11374 removeParentHeight(parentLi, h);
11379 // isKeyPress - to notify that the function is called by Right Key press
11380 // passiveClose - prevents firing of oncollapse events during the action
11381 // of expand function(check the function definition)
11383 var activeToggle = function (elem,isKeyPress,passiveClose) {
11384 var element = elem.find('a').eq(0);
11385 if (element.hasClass('active')) {
11387 elem.removeClass('bg');
11390 if (elem.attr('aria-selected') && !isKeyPress) {
11391 elem.attr('aria-selected', 'false');
11393 if (!element.find('i').eq(0).hasClass('icon-primary-circle')) {
11394 if(isKeyPress && scope.member){
11395 if (scope.member.onCollapse !== undefined && !passiveClose) {
11396 scope.member.onCollapse(scope.member);
11399 element.removeClass('active');
11400 elem.attr('aria-expanded', 'false');
11401 element.find('i').eq(0).removeClass('icon-primary-expanded');
11402 element.find('i').eq(0).addClass('icon-primary-collapsed');
11403 //For Animation: below commented code is used to manually set height of UL to zero
11404 //retaining code for future reference
11406 var totalHeight = elem.find('ul')[0].scrollHeight;
11407 removeParentHeight(elem, totalHeight);
11408 elem.find('ul').eq(0).css({
11414 elem.addClass('bg');
11415 elem.attr('aria-selected', 'true');
11418 if (!element.find('i').eq(0).hasClass('icon-primary-circle')) {
11420 if(typeof scope.showChild === 'function' ){
11424 if (scope.member.onExpand !== undefined) {
11425 scope.member.onExpand(scope.member);
11429 element.addClass('active');
11430 elem.attr('aria-expanded', 'true');
11431 element.find('i').eq(0).removeClass('icon-primary-collapsed');
11432 element.find('i').eq(0).addClass('icon-primary-expanded');
11433 //For Animation: below commented code is used to manually set height of the ul generatedon the click of parent LI.
11434 //retaining code for future reference
11436 var totalHeight = elem.find('ul')[0].scrollHeight;
11437 addParentHeight(elem, totalHeight);
11438 elem.find('ul').eq(0).css({
11439 height: totalHeight+'px'
11445 element.bind('click', function (evt) {
11446 //first we close others and then we open the clicked element
11447 if (element[0].previousElementSibling) {
11448 closeOthersUp(angular.element(element[0].previousElementSibling));
11450 if (element[0].nextElementSibling) {
11451 closeOthersDown(angular.element(element[0].nextElementSibling));
11453 removeBackground(element);
11454 activeToggle(element);
11456 evt.stopPropagation();
11458 //default root tree element tabindex set zero
11459 if (element.parent().parent().hasClass('b2b-tree') && (element.parent()[0].previousElementSibling === null)) {
11460 element.attr('tabindex', 0);
11462 //check root via class
11463 var isRoot = function (elem) {
11464 if (elem.parent().parent().eq(0).hasClass('b2b-tree')) {
11470 var findRoot = function (elem) {
11471 if (isRoot(elem)) {
11475 findRoot(elem.parent());
11478 var findPreActive = function (elem) {
11480 if (!(elem.hasClass("active"))) {
11483 var childElems = angular.element(elem[0].nextElementSibling.children);
11484 lastE = angular.element(childElems[childElems.length - 1]);
11485 if (lastE.find('a').eq(0).hasClass('active')) {
11486 findPreActive(lastE.find('a').eq(0));
11492 var findUp = function (elem) {
11493 if (isRoot(elem)) {
11497 if (elem[0].previousElementSibling !== null && !angular.element(elem[0].previousElementSibling).hasClass('tree-hide')) {
11498 upE = angular.element(elem[0].previousElementSibling);
11499 if (upE.find('a').eq(0).hasClass('active')) {
11500 findPreActive(upE.find('a').eq(0));
11503 upE = elem.parent().parent();
11507 var downElement = function (elem) {
11508 if (elem.next().hasClass('tree-hide')) {
11509 downElement(elem.next());
11511 downE = elem.next();
11514 var isBottomElem = false;
11515 var downParent = function(liElem){
11516 if(liElem.eq(0).parent().parent().eq(0).hasClass('b2b-tree')){
11517 isBottomElem = true;
11520 if(liElem.next().length !== 0){
11521 downE = liElem.next().eq(0);
11525 downParent(liElem.parent().parent());
11529 var findDown = function (elem) {
11530 if (isRoot(elem.parent()) && !elem.hasClass('active')) {
11531 downE = elem.parent();
11534 if (elem.hasClass('active')) {
11535 downE = elem.next().find('li').eq(0);
11536 if (downE.hasClass('tree-hide')) {
11537 downElement(downE);
11541 downParent(elem.parent());
11542 if(isBottomElem === true){
11543 downE = elem.parent();
11544 isBottomElem = false;
11550 var resetTabPosition = function(element){
11552 angular.element(rootE.parent().parent()[0].querySelector("li[tabindex='0']")).attr('tabindex','-1');
11553 var elemToFocus = rootE.parent().parent()[0].querySelector(".bg")|| rootE;
11555 angular.element(elemToFocus).attr('tabindex','0');
11557 // Function to control the expansion of nodes when the user tabs into the tree and
11558 // the slected node is not visible
11559 var expand = function(elemArr){
11560 var elem= elemArr.pop();
11561 var element = elem.find('a').eq(0);
11562 var selectedNode = elem.parent().parent()[0].querySelector(".bg");
11563 if(selectedNode != null){
11565 element = elem.find('a').eq(0);
11566 if(!element.hasClass('active') ){
11569 if (elem[0].previousElementSibling) {
11570 closeOthersUp(angular.element(elem[0].previousElementSibling),true,true);
11572 if (elem[0].nextElementSibling) {
11573 closeOthersDown(angular.element(elem[0].nextElementSibling),true,true);
11576 if (!element.find('i').eq(0).hasClass('icon-primary-circle')) {
11577 if(typeof scope.showChild === 'function' ){
11580 element.addClass('active');
11581 elem.attr('aria-expanded', 'true');
11582 element.find('i').eq(0).removeClass('icon-primary-collapsed');
11583 element.find('i').eq(0).addClass('icon-primary-expanded');
11587 elem = elemArr.pop();
11595 element.find('a').eq(0).bind('mouseenter', function (evt) {
11596 angular.forEach(document.querySelectorAll('.activeTooltip'), function(value, key) {
11597 angular.element(value).removeClass('activeTooltip')
11599 element.addClass('activeTooltip');
11601 element.find('a').eq(0).bind('mouseleave', function (evt) {
11602 element.removeClass('activeTooltip');
11604 element.bind('focus', function (evt) {
11605 angular.forEach(document.querySelectorAll('.activeTooltip'), function(value, key) {
11606 angular.element(value).removeClass('activeTooltip')
11608 element.addClass('activeTooltip');
11610 element.bind('blur', function (evt) {
11611 element.removeClass('activeTooltip');
11613 element.bind('keydown', function (evt) {
11614 switch (evt.keyCode) {
11615 case keymap.KEY.HOME:
11616 evt.preventDefault();
11617 evt.stopPropagation();
11618 element.attr('tabindex', -1);
11620 rootE.eq(0).attr('tabindex', 0);
11623 case keymap.KEY.LEFT:
11624 evt.preventDefault();
11625 evt.stopPropagation();
11627 if(element.find('a').eq(0).hasClass('active')){
11628 if (element[0].previousElementSibling) {
11629 closeOthersUp(angular.element(element[0].previousElementSibling),true);
11631 if (element[0].nextElementSibling) {
11632 closeOthersDown(angular.element(element[0].nextElementSibling),true);
11634 activeToggle(element,true);
11637 element.attr('tabindex', -1);
11638 parentE = element.parent().parent();
11639 parentE.attr('tabindex', 0);
11640 parentE[0].focus();
11642 case keymap.KEY.UP:
11643 evt.preventDefault();
11644 evt.stopPropagation();
11645 element.attr('tabindex', -1);
11647 upE.eq(0).attr('tabindex', 0);
11650 case keymap.KEY.RIGHT:
11651 evt.preventDefault();
11652 evt.stopPropagation();
11653 if(element.find('i').eq(0).hasClass('icon-primary-circle')){
11656 if (!element.find('a').eq(0).hasClass('active')) {
11657 if (element[0].previousElementSibling) {
11658 closeOthersUp(angular.element(element[0].previousElementSibling),true);
11660 if (element[0].nextElementSibling) {
11661 closeOthersDown(angular.element(element[0].nextElementSibling),true);
11663 activeToggle(element,true);
11667 element.attr('tabindex', -1);
11668 findDown(element.find('a').eq(0));
11669 downE.eq(0).attr('tabindex', 0);
11673 case keymap.KEY.DOWN:
11674 evt.preventDefault();
11675 element.attr('tabindex', -1);
11676 findDown(element.find('a').eq(0));
11677 downE.eq(0).attr('tabindex', 0);
11679 evt.stopPropagation();
11681 case keymap.KEY.ENTER:
11682 var isSelectedElem = element.hasClass('bg');
11683 var enterFunc = function(element){
11684 if (isSelectedElem) {
11685 element.removeClass('bg');
11686 if (element.attr('aria-selected')) {
11687 element.attr('aria-selected', 'false');
11691 element.addClass('bg');
11692 element.attr('aria-selected', 'true');
11695 if (element[0].previousElementSibling) {
11696 closeOthersUp(angular.element(element[0].previousElementSibling));
11698 if (element[0].nextElementSibling) {
11699 closeOthersDown(angular.element(element[0].nextElementSibling));
11702 removeBackground(element);
11703 enterFunc(element);
11704 evt.stopPropagation();
11706 case keymap.KEY.TAB:
11707 $timeout(function(){
11708 resetTabPosition(element);
11710 evt.stopPropagation();
11717 element.bind('keyup',function(evt){
11718 if(evt.keyCode === keymap.KEY.TAB){
11720 var tempElem = element;
11722 while(!tempElem.hasClass('b2b-tree')){
11723 elemArr.push(tempElem);
11724 tempElem = tempElem.parent().parent();
11726 elemArr.push(tempElem);
11730 evt.stopPropagation();
11737 * @name Navigation.att:Tree nodes with checkboxes
11739 * @param {String} setRole - The value needs to be "tree". This is required to incorporate CATO requirements.
11740 * @param {boolean} groupIt - The value needs to be "false" for top-level tree rendered.
11741 * @param {Object} collection - The JSON object of tree to be rendered.
11743 * <file src="src/treeNodeCheckbox/docs/readme.md" />
11746 * <div class="b2b-tree-checkbox">
11747 * <b2b-tree-node-checkbox collection="treeStructure" set-role="tree" group-it="false"></b2b-tree-node-checkbox>
11750 * <section id="code">
11751 <example module="b2b.att">
11752 <file src="src/treeNodeCheckbox/docs/demo.html" />
11753 <file src="src/treeNodeCheckbox/docs/demo.js" />
11758 angular.module('b2b.att.treeNodeCheckbox', ['b2b.att.utilities'])
11759 .directive('b2bTreeNodeCheckbox', function () {
11768 templateUrl: function (element, attrs) {
11769 if (attrs.groupIt === 'true') {
11770 return "b2bTemplate/treeNodeCheckbox/groupedTree.html";
11772 return "b2bTemplate/treeNodeCheckbox/ungroupedTree.html";
11775 link: function (scope) {
11776 if (!(scope.setRole === 'tree')) {
11777 scope.setRole = 'group';
11782 .directive('b2bTreeMember', ['$compile', '$timeout', 'keymap', function ($compile, $timeout, keymap) {
11790 templateUrl: 'b2bTemplate/treeNodeCheckbox/treeMember.html',
11791 link: function (scope, element, attrs) {
11792 scope.elemArr = [];
11793 var removeRootTabIndex = function (elem) {
11794 if (elem.parent().parent().eq(0).hasClass('b2b-tree-checkbox')) {
11795 elem.attr('tabindex', -1);
11798 removeRootTabIndex(elem.parent());
11800 scope.$watch('member.child', function(newVal, oldVal){
11801 if(newVal !== oldVal){
11806 var checkedCount = 0;
11807 var nonCheckedCount = 0;
11808 var checkBoxesCount = 0;
11810 if(element.find('a').eq(0).find('input')){
11811 if(scope.member.indeterminate){
11812 element.find('a').eq(0).find('input').prop('indeterminate', true);
11813 element.attr('aria-checked',"mixed");
11815 element.attr('aria-checked',scope.member.isSelected);
11818 element.find('a').eq(0).find('input').bind('change',function(){
11819 scope.member.indeterminate = false;
11820 downwardModalUpdate(scope.member);
11821 downwardSelection(element);
11822 upwardSelection(element);
11823 element.attr('aria-checked',scope.member.isSelected);
11824 if (scope.member.onSelect !== undefined) {
11825 scope.member.onSelect(scope.member);
11829 element.find('a').eq(0).find('input').bind('click',function(){
11830 var elem = angular.element(this);
11831 if(scope.member.indeterminate){
11832 scope.member.indeterminate = false;
11833 scope.member.isSelected = true;
11834 elem.prop('indeterminate', false);
11835 elem.prop('checked', true);
11836 elem.triggerHandler('change');
11840 var groupNode = false;
11841 var checkedTreeNode = false;
11843 var isCheckboxSelected = function(elem){
11844 checkedTreeNode = false;
11845 checkedTreeNode = angular.element(angular.element(elem).find('a').eq(0))[0].querySelector('input.treeCheckBox').checked;
11848 var findCheckbox = function(elem){
11849 return angular.element(angular.element(elem).find('a').eq(0))[0].querySelector('input.treeCheckBox');
11852 var updateGrpNodeCheckboxes = function(elem, checked){
11853 angular.element(angular.element(elem).find('a').eq(0))[0].querySelector('input.treeCheckBox').checked = checked;
11857 var isGroupNode = function(elem){
11859 if(angular.element(angular.element(elem).find('a').eq(0))[0].querySelector('input.grpTreeCheckbox')){
11864 var downwardModalUpdate = function(curMember){
11865 angular.forEach(curMember.child, function(childMember, key) {
11866 childMember.isSelected = curMember.isSelected;
11867 childMember.indeterminate = false;
11868 if(angular.isArray(childMember.child) && scope.member.child.length > 0){
11869 downwardModalUpdate(childMember);
11874 var downwardSelection = function(elem){
11875 if(findCheckbox(elem)){
11876 isCheckboxSelected(elem)
11878 if(angular.element(elem).find('ul').length > 0){
11879 var childNodes = angular.element(elem).find('ul').eq(0).children('li');
11880 for(var i=0; i<childNodes.length; i++){
11881 if(findCheckbox(childNodes[i])){
11882 isGroupNode(childNodes[i]);
11883 angular.element(findCheckbox(childNodes[i])).prop('indeterminate', false);
11884 angular.element(childNodes[i]).attr('aria-checked',checkedTreeNode);
11886 updateGrpNodeCheckboxes(childNodes[i],checkedTreeNode);
11888 angular.element(childNodes[i]).scope().member.isSelected = checkedTreeNode;
11889 angular.element(childNodes[i]).scope().member.indeterminate = false
11890 angular.element(childNodes[i]).scope().$apply();
11892 downwardSelection(childNodes[i]);
11898 var upwardSelection = function(elem){
11899 var childNodes = elem.parent().parent().find('ul').eq(0).children('li');
11901 nonCheckedCount = 0;
11902 checkBoxesCount = 0;
11903 for(i=0; i<childNodes.length; i++){
11904 if(findCheckbox(childNodes[i])){
11905 isGroupNode(childNodes[i]);
11906 isCheckboxSelected(childNodes[i]);
11908 if(checkedTreeNode){
11910 }else if(!angular.element(angular.element(angular.element(childNodes[i]).find('a').eq(0))[0].querySelector('input.treeCheckBox')).prop('indeterminate')){
11915 var parentNodeScope;
11916 parentNodeScope = angular.element(elem.parent().parent()).scope();
11917 if(findCheckbox(elem.parent().parent())){
11918 if(nonCheckedCount == checkBoxesCount){
11919 angular.element(findCheckbox(elem.parent().parent())).prop('indeterminate', false);
11920 if(parentNodeScope && parentNodeScope.member){
11921 parentNodeScope.member.isSelected = false;
11922 parentNodeScope.member.indeterminate = false;
11924 updateGrpNodeCheckboxes(elem.parent().parent(),false);
11926 angular.element(elem.parent().parent()).attr('aria-checked',false);
11927 }else if(checkedCount == checkBoxesCount){
11928 angular.element(findCheckbox(elem.parent().parent())).prop('indeterminate', false);
11929 if(parentNodeScope && parentNodeScope.member){
11930 parentNodeScope.member.isSelected = true;
11931 parentNodeScope.member.indeterminate = false;
11933 updateGrpNodeCheckboxes(elem.parent().parent(),true);
11935 angular.element(elem.parent().parent()).attr('aria-checked',true);
11937 angular.element(findCheckbox(elem.parent().parent())).prop('indeterminate', true);
11938 if(parentNodeScope && parentNodeScope.member){
11939 parentNodeScope.member.isSelected = false;
11940 parentNodeScope.member.indeterminate = true;
11942 updateGrpNodeCheckboxes(elem.parent().parent(),false);
11944 angular.element(elem.parent().parent()).attr('aria-checked',"mixed");
11946 if(parentNodeScope && parentNodeScope.member){
11947 parentNodeScope.$apply();
11953 if(elem.parent().parent().attr('role') == "treeitem"){
11954 upwardSelection(elem.parent().parent());
11958 scope.showChild = function () {
11959 if (!element.hasClass('grouped')) {
11960 if (angular.isArray(scope.member.child) && scope.member.child.length > 0 && (scope.member.divide === undefined || scope.member.child.length < scope.member.divide)) {
11961 scope.groupIt = false;
11962 element.addClass('grouped');
11963 element.append("<b2b-tree-node-checkbox collection='member.child' group-it='" + scope.groupIt + "'></b2b-tree-node-checkbox>");
11964 $compile(element.contents())(scope);
11965 if(scope.member.active && scope.member.active === true){
11966 angular.element(element[0].querySelectorAll('i.expandCollapseIcon')).eq(0).removeClass('icon-primary-collapsed');
11968 if(scope.member.selected && scope.member.selected === true){
11969 element.attr('tabindex', 0);
11970 removeRootTabIndex(element);
11972 if(scope.member.active && scope.member.active == undefined){
11973 angular.element(element[0].querySelectorAll('i.expandCollapseIcon')).eq(0).addClass('icon-primary-collapsed');
11975 } else if (scope.member.child && scope.member.divide && scope.member.child.length > scope.member.divide) {
11976 element.addClass('grouped');
11977 scope.groupIt = true;
11980 if(scope.member.child[0].groupName !== undefined){
11981 grpName = scope.member.child[0].groupName;
11984 var toSlice = scope.member.child[0].name.search(' ');
11985 grpName = scope.member.child[0].name.slice(0, toSlice);
11988 for (i = 0; i < scope.member.child.length; i += scope.member.divide) {
11990 for (j = j + i; j < (i + scope.member.divide); j++) {
11991 if (j === scope.member.child.length) {
11992 scope.member.child[j - 1].grpChild = grpName + ' ' + (i + 1) + ' - ' + (scope.member.child.length);
11995 if(scope.member.child[j-1].active && scope.member.child[j-1].active===true){
11996 scope.member.child[j-1].activeGrp = true;
12000 if (i + scope.member.divide > scope.member.child.length) {
12001 scope.member.child[j].grpChild = grpName + ' ' + (i + 1) + ' - ' + (scope.member.child.length);
12002 if(scope.member.child[j].active && scope.member.child[j].active===true){
12003 scope.member.child[j].activeGrp = true;
12007 scope.member.child[j].grpChild = grpName + ' ' + (i + 1) + ' - ' + (i + scope.member.divide);
12008 if(scope.member.child[j].active && scope.member.child[j].active===true){
12009 scope.member.child[j].activeGrp = true;
12014 if(scope.member.divide){
12015 element.append("<b2b-tree-node-checkbox collection='member.child' group-it='" + scope.groupIt + "'></b2b-tree-node-checkbox>");
12017 element.append("<b2b-tree-node-checkbox collection='member.child' group-it='" + scope.groupIt + "'></b2b-tree-node-checkbox>");
12019 $compile(element.contents())(scope);
12020 if(scope.member.active && scope.member.active === true){
12021 angular.element(element[0].querySelectorAll('i.expandCollapseIcon')).eq(0).removeClass('icon-primary-collapsed');
12024 if( scope.member.active && scope.member.active == undefined){
12025 angular.element(element[0].querySelectorAll('i.expandCollapseIcon')).eq(0).addClass('icon-primary-collapsed');
12029 $timeout(function () {
12030 if(!scope.member.indeterminate){
12031 downwardSelection(element);
12037 if(scope.member.active && scope.member.active == true){
12040 if(scope.member.active == undefined && !element.find('a').eq(0).hasClass('active') && scope.member.child !== undefined){
12041 angular.element(element[0].querySelectorAll('i.expandCollapseIcon')).eq(0).addClass('icon-primary-collapsed');
12043 else if(scope.member.child == undefined){
12044 angular.element(element[0].querySelectorAll('i.expandCollapseIcon')).eq(0).addClass('icon-primary-circle');
12045 if(scope.$parent.$index === 0) {
12046 element.find('a').eq(0).append('<span class="first-link"></span>');
12050 angular.element(element[0].querySelectorAll('i.expandCollapseIcon')).eq(0).bind('click', function (evt) {
12052 var expandFunc = scope.member.onExpand;
12053 if (element.find('a').eq(0).hasClass('active') && scope.member.onExpand !== undefined) {
12054 var eValue = scope.member.onExpand(scope.member);
12056 if (!element.find('a').eq(0).hasClass('active') && scope.member.onCollapse !== undefined) {
12057 scope.member.onCollapse(scope.member);
12061 angular.element(element[0].querySelectorAll('.treeNodeName')).eq(0).bind('click', function (evt) {
12068 .directive('b2bTreeNodeLink', ['keymap', '$timeout', function (keymap, $timeout) {
12071 link: function (scope, element, attr, ctrl) {
12072 var rootE, parentE, upE, downE;
12073 var closeOthersUp = function (elem) {
12075 if (elem.find('a').eq(0).hasClass('active')) {
12076 activeToggle(elem);
12079 if (elem.hasClass('bg')) {
12080 elem.removeClass('bg');
12082 if (elem[0].previousElementSibling !== null) {
12083 closeOthersUp(angular.element(elem[0].previousElementSibling));
12086 var closeOthersDown = function (elem) {
12088 if (elem.find('a').eq(0).hasClass('active')) {
12089 activeToggle(elem);
12092 if (elem.hasClass('bg')) {
12093 elem.removeClass('bg');
12095 if (elem[0].nextElementSibling !== null) {
12096 closeOthersDown(angular.element(elem[0].nextElementSibling));
12100 var removeBackgroundUp = function (elem) {
12102 if (elem.hasClass('b2b-tree-checkbox')) {
12105 elem.parent().parent().removeClass('bg');
12106 removeBackgroundUp(elem.parent().parent());
12110 var removeBackgroundDown = function (elem) {
12112 angular.element(elem[0].querySelector('.bg')).removeClass('bg');
12117 var activeToggle = function (elem) {
12118 var element = elem.find('a').eq(0);
12119 if (element.hasClass('active')) {
12120 elem.removeClass('bg');
12121 if (!angular.element(element[0].querySelectorAll('i.expandCollapseIcon')).eq(0).hasClass('icon-primary-circle')) {
12122 element.removeClass('active');
12123 elem.attr('aria-expanded', 'false');
12124 angular.element(element[0].querySelectorAll('i.expandCollapseIcon')).eq(0).removeClass('icon-primary-expanded');
12125 angular.element(element[0].querySelectorAll('i.expandCollapseIcon')).eq(0).addClass('icon-primary-collapsed');
12128 elem.addClass('bg');
12129 if (!angular.element(element[0].querySelectorAll('i.expandCollapseIcon')).eq(0).hasClass('icon-primary-circle')) {
12130 element.addClass('active');
12131 elem.attr('aria-expanded', 'true');
12132 angular.element(element[0].querySelectorAll('i.expandCollapseIcon')).eq(0).removeClass('icon-primary-collapsed');
12133 angular.element(element[0].querySelectorAll('i.expandCollapseIcon')).eq(0).addClass('icon-primary-expanded');
12137 angular.element(element[0].querySelectorAll('i.expandCollapseIcon')).eq(0).bind('click', function (evt) {
12139 if (element[0].previousElementSibling) {
12140 closeOthersUp(angular.element(element[0].previousElementSibling));
12142 if (element[0].nextElementSibling) {
12143 closeOthersDown(angular.element(element[0].nextElementSibling));
12146 activeToggle(element);
12148 removeBackgroundDown(element);
12149 removeBackgroundUp(element);
12150 evt.stopPropagation();
12153 if (element.parent().parent().hasClass('b2b-tree-checkbox') && (element.parent()[0].previousElementSibling === null)) {
12154 element.attr('tabindex', 0);
12157 var isRoot = function (elem) {
12158 if (elem.parent().parent().eq(0).hasClass('b2b-tree-checkbox')) {
12164 var findRoot = function (elem) {
12165 if (isRoot(elem)) {
12169 findRoot(elem.parent());
12172 var findPreActive = function (elem) {
12174 if (!(elem.hasClass("active"))) {
12177 var childElems = angular.element(elem[0].nextElementSibling.children);
12178 lastE = angular.element(childElems[childElems.length - 1]);
12179 if (lastE.find('a').eq(0).hasClass('active')) {
12180 findPreActive(lastE.find('a').eq(0));
12186 var findUp = function (elem) {
12187 if (isRoot(elem)) {
12191 if (elem[0].previousElementSibling !== null && !angular.element(elem[0].previousElementSibling).hasClass('tree-hide')) {
12192 upE = angular.element(elem[0].previousElementSibling);
12193 if (upE.find('a').eq(0).hasClass('active')) {
12194 findPreActive(upE.find('a').eq(0));
12197 upE = elem.parent().parent();
12201 var downElement = function (elem) {
12202 if (elem.next().hasClass('tree-hide')) {
12203 downElement(elem.next());
12205 downE = elem.next();
12208 var isBottomElem = false;
12209 var downParent = function(liElem){
12210 if(liElem.eq(0).parent().parent().eq(0).hasClass('b2b-tree-checkbox')){
12211 isBottomElem = true;
12214 if(liElem.next().length !== 0){
12215 downE = liElem.next().eq(0);
12219 downParent(liElem.parent().parent());
12223 var findDown = function (elem) {
12224 if (isRoot(elem.parent()) && !elem.hasClass('active')) {
12225 downE = elem.parent();
12228 if (elem.hasClass('active')) {
12229 downE = elem.next().find('li').eq(0);
12230 if (downE.hasClass('tree-hide')) {
12231 downElement(downE);
12235 downParent(elem.parent());
12236 if(isBottomElem === true){
12237 downE = elem.parent();
12238 isBottomElem = false;
12242 element.bind('keydown', function (evt) {
12243 switch (evt.keyCode) {
12244 case keymap.KEY.HOME:
12245 evt.preventDefault();
12246 evt.stopPropagation();
12247 element.attr('tabindex', -1);
12249 rootE.eq(0).attr('tabindex', 0);
12252 case keymap.KEY.LEFT:
12253 evt.preventDefault();
12254 evt.stopPropagation();
12255 if (!isRoot(element)) {
12256 if(element.find('a').eq(0).hasClass('active')){
12257 angular.element(element[0].querySelectorAll('i.expandCollapseIcon')).eq(0).triggerHandler('click');
12260 element.attr('tabindex', -1);
12261 parentE = element.parent().parent();
12262 parentE.attr('tabindex', 0);
12263 parentE[0].focus();
12265 if (element.find('a').eq(0).hasClass('active')) {
12266 angular.element(element[0].querySelectorAll('i.expandCollapseIcon')).eq(0).triggerHandler('click');
12270 case keymap.KEY.UP:
12271 evt.preventDefault();
12272 evt.stopPropagation();
12273 element.attr('tabindex', -1);
12275 upE.eq(0).attr('tabindex', 0);
12278 case keymap.KEY.RIGHT:
12279 evt.preventDefault();
12280 evt.stopPropagation();
12281 if(angular.element(element[0].querySelectorAll('i.expandCollapseIcon')).eq(0).hasClass('icon-primary-circle')){
12284 if (!element.find('a').eq(0).hasClass('active')) {
12285 angular.element(element[0].querySelectorAll('i.expandCollapseIcon')).eq(0).triggerHandler('click');
12288 element.attr('tabindex', -1);
12289 findDown(element.find('a').eq(0));
12290 downE.eq(0).attr('tabindex', 0);
12294 case keymap.KEY.DOWN:
12295 evt.preventDefault();
12296 element.attr('tabindex', -1);
12297 findDown(element.find('a').eq(0));
12298 downE.eq(0).attr('tabindex', 0);
12300 evt.stopPropagation();
12302 case keymap.KEY.SPACE:
12303 case keymap.KEY.ENTER:
12304 evt.preventDefault();
12305 evt.stopPropagation();
12306 if(angular.isDefined(element.scope().member.isSelected)){
12307 element.scope().member.isSelected = !element.scope().member.isSelected;
12308 element.scope().member.indeterminate = false;
12309 element.scope().$apply();
12310 element.find('a').eq(0).find('input').prop('indeterminate', false);
12311 element.find('a').eq(0).find('input').triggerHandler('change');
12322 angular.module('b2b.att.collapse', ['b2b.att.transition'])
12324 // The collapsible directive indicates a block of html that will expand and collapse
12325 .directive('b2bCollapse', ['$transition', function($transition) {
12326 // CSS transitions don't work with height: auto, so we have to manually change the height to a
12327 // specific value and then once the animation completes, we can reset the height to auto.
12328 // Unfortunately if you do this while the CSS transitions are specified (i.e. in the CSS class
12329 // "collapse") then you trigger a change to height 0 in between.
12330 // The fix is to remove the "collapse" CSS class while changing the height back to auto - phew!
12335 marginBottom: null,
12337 paddingBottom: null,
12349 var fixUpHeight = function(scope, element, height) {
12350 // We remove the collapse CSS class to prevent a transition when we change to height: auto
12351 element.removeClass('b2bCollapse');
12352 element.css({height: height});
12353 //adjusting for any margin or padding
12354 if (height === 0) {
12355 element.css(props.closed);
12357 element.css(props.open);
12359 // It appears that reading offsetWidth makes the browser realise that we have changed the
12360 // height already :-/
12361 var x = element[0].offsetWidth;
12362 element.addClass('b2bCollapse');
12366 link: function(scope, element, attrs) {
12368 var initialAnimSkip = true;
12369 scope.$watch(function() {
12370 return element[0].scrollHeight;
12371 }, function(value) {
12372 //The listener is called when scrollHeight changes
12373 //It actually does on 2 scenarios:
12374 // 1. Parent is set to display none
12375 // 2. angular bindings inside are resolved
12376 //When we have a change of scrollHeight we are setting again the correct height if the group is opened
12377 if (element[0].scrollHeight !== 0) {
12378 if (!isCollapsed) {
12379 if (initialAnimSkip) {
12380 fixUpHeight(scope, element, element[0].scrollHeight + 'px');
12382 fixUpHeight(scope, element, 'auto');
12383 element.css({overflow: 'visible'});
12389 scope.$watch(attrs.b2bCollapse, function(value) {
12398 var currentTransition;
12399 var doTransition = function(change) {
12400 if (currentTransition) {
12401 currentTransition.cancel();
12403 currentTransition = $transition(element, change);
12404 currentTransition.then(
12406 currentTransition = undefined;
12409 currentTransition = undefined;
12412 return currentTransition;
12415 var expand = function() {
12416 scope.postTransition = true;
12417 if (initialAnimSkip) {
12418 initialAnimSkip = false;
12419 if (!isCollapsed) {
12420 fixUpHeight(scope, element, 'auto');
12423 //doTransition({ height : element[0].scrollHeight + 'px' })
12424 doTransition(angular.extend({height: element[0].scrollHeight + 'px'}, props.open))
12426 // This check ensures that we don't accidentally update the height if the user has closed
12427 // the group while the animation was still running
12428 if (!isCollapsed) {
12429 fixUpHeight(scope, element, 'auto');
12433 isCollapsed = false;
12436 var collapse = function() {
12437 isCollapsed = true;
12438 if (initialAnimSkip) {
12439 initialAnimSkip = false;
12440 fixUpHeight(scope, element, 0);
12442 fixUpHeight(scope, element, element[0].scrollHeight + 'px');
12443 doTransition(angular.extend({height: 0}, props.closed)).then(function() {
12444 scope.postTransition = false;
12446 element.css({overflow: 'hidden'});
12452 angular.module('b2b.att.position', [])
12454 .factory('$position', ['$document', '$window', function ($document, $window) {
12455 function getStyle(el, cssprop) {
12456 if (el.currentStyle) { //IE
12457 return el.currentStyle[cssprop];
12458 } else if ($window.getComputedStyle) {
12459 return $window.getComputedStyle(el)[cssprop];
12461 // finally try and get inline style
12462 return el.style[cssprop];
12466 * Checks if a given element is statically positioned
12467 * @param element - raw DOM element
12469 function isStaticPositioned(element) {
12470 return (getStyle(element, "position") || 'static') === 'static';
12474 * returns the closest, non-statically positioned parentOffset of a given element
12477 var parentOffsetEl = function (element) {
12478 var docDomEl = $document[0];
12479 var offsetParent = element.offsetParent || docDomEl;
12480 while (offsetParent && offsetParent !== docDomEl && isStaticPositioned(offsetParent)) {
12481 offsetParent = offsetParent.offsetParent;
12483 return offsetParent || docDomEl;
12488 * Provides read-only equivalent of jQuery's position function:
12489 * http://api.jquery.com/position/
12491 position: function (element) {
12492 var elBCR = this.offset(element);
12493 var offsetParentBCR = {
12497 var offsetParentEl = parentOffsetEl(element[0]);
12498 if (offsetParentEl !== $document[0]) {
12499 offsetParentBCR = this.offset(angular.element(offsetParentEl));
12500 offsetParentBCR.top += offsetParentEl.clientTop - offsetParentEl.scrollTop;
12501 offsetParentBCR.left += offsetParentEl.clientLeft - offsetParentEl.scrollLeft;
12505 width: element.prop('offsetWidth'),
12506 height: element.prop('offsetHeight'),
12507 top: elBCR.top - offsetParentBCR.top,
12508 left: elBCR.left - offsetParentBCR.left
12513 * Provides read-only equivalent of jQuery's offset function:
12514 * http://api.jquery.com/offset/
12516 offset: function (element) {
12517 var boundingClientRect = element[0].getBoundingClientRect();
12519 width: element.prop('offsetWidth'),
12520 height: element.prop('offsetHeight'),
12521 top: boundingClientRect.top + ($window.pageYOffset || $document[0].body.scrollTop || $document[0].documentElement.scrollTop),
12522 left: boundingClientRect.left + ($window.pageXOffset || $document[0].body.scrollLeft || $document[0].documentElement.scrollLeft)
12527 * Provides functionality to check whether an element is in view port.
12529 isElementInViewport: function (element) {
12531 var rect = element[0].getBoundingClientRect();
12535 rect.bottom <= ($window.innerHeight || $document[0].documentElement.clientHeight) &&
12536 rect.right <= ($window.innerWidth || $document[0].documentElement.clientWidth)
12545 .factory('$isElement', [function () {
12546 var isElement = function (currentElem, targetElem, alternateElem) {
12547 if (currentElem[0] === targetElem[0]) {
12549 } else if (currentElem[0] === alternateElem[0]) {
12552 return isElement((currentElem.parent()[0] && currentElem.parent()) || targetElem, targetElem, alternateElem);
12559 .directive('attPosition', ['$position', function ($position) {
12562 link: function (scope, elem, attr) {
12563 scope.$watchCollection(function () {
12564 return $position.position(elem);
12565 }, function (value) {
12566 scope[attr.attPosition] = value;
12572 angular.module('b2b.att.transition', [])
12574 .factory('$transition', ['$q', '$timeout', '$rootScope', function($q, $timeout, $rootScope) {
12576 var $transition = function(element, trigger, options) {
12577 options = options || {};
12578 var deferred = $q.defer();
12579 var endEventName = $transition[options.animation ? "animationEndEventName" : "transitionEndEventName"];
12581 var transitionEndHandler = function() {
12582 $rootScope.$apply(function() {
12583 element.unbind(endEventName, transitionEndHandler);
12584 deferred.resolve(element);
12588 if (endEventName) {
12589 element.bind(endEventName, transitionEndHandler);
12592 // Wrap in a timeout to allow the browser time to update the DOM before the transition is to occur
12593 $timeout(function() {
12594 if ( angular.isString(trigger) ) {
12595 element.addClass(trigger);
12596 } else if ( angular.isFunction(trigger) ) {
12598 } else if ( angular.isObject(trigger) ) {
12599 element.css(trigger);
12601 //If browser does not support transitions, instantly resolve
12602 if ( !endEventName ) {
12603 deferred.resolve(element);
12607 // Add our custom cancel function to the promise that is returned
12608 // We can call this if we are about to run a new transition, which we know will prevent this transition from ending,
12609 // i.e. it will therefore never raise a transitionEnd event for that transition
12610 deferred.promise.cancel = function() {
12611 if ( endEventName ) {
12612 element.unbind(endEventName, transitionEndHandler);
12614 deferred.reject('Transition cancelled');
12617 return deferred.promise;
12620 // Work out the name of the transitionEnd event
12621 var transElement = document.createElement('trans');
12622 var transitionEndEventNames = {
12623 'WebkitTransition': 'webkitTransitionEnd',
12624 'MozTransition': 'transitionend',
12625 'OTransition': 'oTransitionEnd',
12626 'transition': 'transitionend'
12628 var animationEndEventNames = {
12629 'WebkitTransition': 'webkitAnimationEnd',
12630 'MozTransition': 'animationend',
12631 'OTransition': 'oAnimationEnd',
12632 'transition': 'animationend'
12634 function findEndEventName(endEventNames) {
12635 for (var name in endEventNames){
12636 if (transElement.style[name] !== undefined) {
12637 return endEventNames[name];
12641 $transition.transitionEndEventName = findEndEventName(transitionEndEventNames);
12642 $transition.animationEndEventName = findEndEventName(animationEndEventNames);
12643 return $transition;
12646 .factory('$scrollTo', ['$window', function($window) {
12647 var $scrollTo = function(offsetLeft, offsetTop, duration) {
12648 TweenMax.to($window, duration || 1, {scrollTo: {y: offsetTop, x: offsetLeft}, ease: Power4.easeOut});
12652 .factory('animation', function(){
12655 .factory('$progressBar', function(){
12657 //Provides a function to pass in code for closure purposes
12658 var loadingAnimationCreator = function(onUpdateCallback){
12660 //Use closure to setup some resuable code
12661 var loadingAnimation = function(callback, duration){
12662 TweenMax.to({}, duration, {
12663 onUpdateParams: ["{self}"],
12664 onUpdate: onUpdateCallback,
12665 onComplete: callback
12668 //Returns a function that takes a callback function and a duration for the animation
12669 return (function(){
12670 return loadingAnimation;
12674 return loadingAnimationCreator;
12676 .factory('$height', function(){
12677 var heightAnimation = function(element,duration,height,alpha){
12678 TweenMax.to(element,
12680 {height:height, autoAlpha:alpha},
12683 return heightAnimation;
12685 angular.module('b2b.att.utilities', ['ngSanitize'])
12686 .constant('b2bUtilitiesConfig', {
12693 enableSearch: false,
12695 circularTraversal: false
12697 .constant('b2bWhenScrollEndsConstants', {
12702 // All breakpoints ranges from >= min and < max
12703 .constant('b2bAwdBreakpoints', {
12719 .filter('groupBy', function ($timeout) {
12720 //Custom GroupBy Filter for treeNav, returns key string and value.childarray as set of grouped elements
12721 return function (data, key) {
12722 if (!key) return data;
12723 var outputPropertyName = '__groupBy__' + key;
12724 if (!data[outputPropertyName]) {
12726 for (var i = 0; i < data.length; i++) {
12727 if (!result[data[i][key]])
12728 result[data[i][key]] = {};
12729 if (!result[data[i][key]].childArray) {
12730 result[data[i][key]].childArray = [];
12732 result[data[i][key]].childArray.push(data[i]);
12733 if (data[i].activeGrp && data[i].activeGrp == true) {
12734 result[data[i][key]].showGroup = true;
12737 Object.defineProperty(result, 'length', {enumerable: false,value: Object.keys(result).length});
12738 Object.defineProperty(data, outputPropertyName, {enumerable: false,configurable: true,writable:false,value:result});
12739 $timeout(function(){delete data[outputPropertyName];},0,false);
12741 return data[outputPropertyName];
12744 .filter('searchObjectPropertiesFilter', [function() {
12745 return function(items, searchText, attrs) {
12750 searchText = searchText.toLowerCase();
12751 angular.forEach(items, function(item) {
12752 angular.forEach(attrs, function(attr) {
12753 if (item.hasOwnProperty(attr) && (item[attr].toLowerCase().indexOf(searchText) != -1)) {
12754 filtered.push(item);
12762 .filter('unsafe',[ '$sce', function ($sce) {
12763 return function(val){
12764 return $sce.trustAsHtml(val);
12767 .filter('b2bHighlight', function () {
12768 function escapeRegexp(queryToEscape) {
12769 return queryToEscape.replace(/([.?*+^$[\]\\(){}|-])/g, '\\$1');
12771 return function (matchItem, query, className) {
12772 return query && matchItem ? matchItem.replace(new RegExp(escapeRegexp(query), 'gi'), '<span class=\"' + className + '\">$&</span>') : matchItem;
12776 Copyright © 2013 Matt Diamond
12777 https://github.com/cwilso/AudioRecorder/blob/master/js/recorderjs/recorder.js
12779 .factory('b2bRecorder', function() {
12781 var Recorder = function(source, cfg) {
12782 var WORKER_PATH = 'recorderWorker.js';
12783 var config = cfg || {};
12784 var bufferLen = config.bufferLen || 4096;
12785 this.context = source.context;
12786 if(!this.context.createScriptProcessor) {
12787 this.node = this.context.createJavacriptProcessor(bufferLen, 2, 2);
12789 this.node = this.context.createScriptProcessor(bufferLen, 2, 2);
12791 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()}};';
12792 var blob = new Blob([workerCode]);
12794 var worker = new Worker(window.URL.createObjectURL(blob)); //TODO: Use a blob instead
12795 worker.postMessage({
12798 sampleRate: this.context.sampleRate
12801 var recording = false,
12804 this.node.onaudioprocess = function(e) {
12805 if (!recording) return;
12806 worker.postMessage({
12809 e.inputBuffer.getChannelData(0),
12810 e.inputBuffer.getChannelData(1)
12815 this.configure = function(cfg) {
12816 for (var prop in cfg) {//TODO: look into using angular.extend() here
12817 if (cfg.hasOwnProperty(prop)) {
12818 config[prop] = cfg[prop];
12823 this.record = function() {
12827 this.stop = function() {
12831 this.clear = function() {
12832 worker.postMessage({ command: 'clear' });
12833 window.URL.revokeObjectURL(blob);
12836 this.getBuffers = function(cb) {
12837 currCallback = cb || config.callback;
12838 worker.postMessage({ command: 'getBuffers' });
12841 this.exportWAV = function(cb, type) {
12842 currCallback = cb || config.callback;
12843 type = type || config.type || 'audio/wav';
12844 if (!currCallback) throw new Error('[b2bRecorder]: Callback not set!');
12845 worker.postMessage({
12846 command: 'exportWAV',
12851 this.exportMonoWAV = function(cb, type) {
12852 currCallback = cb || config.callback;
12853 type = type || config.type || 'audio/wav';
12854 if (!currCallback) throw new Error('[b2bRecorder]: Callback not set!');
12855 worker.postMessage({
12856 command: 'exportMonoWAV',
12861 worker.onmessage = function(e) {
12863 currCallback(blob);
12866 source.connect(this.node);
12867 this.node.connect(this.context.destination); // if the script node is not connected to an output the "onaudioprocess" event is not triggerd in Chrome
12874 .factory('b2bViewport', function() {
12875 /* Source: https://gist.github.com/bjankord/2399828 */
12876 var _viewportWidth = function() {
12878 var webkit = (!(window.webkitConvertPointFromNodeToPage == null));
12882 var vpwtest = document.createElement( "div" );
12883 // Sets test div to width 100%, !important overrides any other misc. box model styles that may be set in the CSS
12884 vpwtest.style.cssText = "width:100% !important; margin:0 !important; padding:0 !important; border:none !important;";
12885 document.documentElement.insertBefore( vpwtest, document.documentElement.firstChild );
12886 vpw = vpwtest.offsetWidth;
12887 document.documentElement.removeChild( vpwtest );
12890 else if ( window.innerWidth === undefined ) {
12891 vpw = document.documentElement.clientWidth;
12895 vpw = window.innerWidth;
12901 viewportWidth: _viewportWidth
12904 .directive('b2bWhenScrollEnds', function(b2bWhenScrollEndsConstants, $log) {
12907 link: function (scope, element, attrs) {
12909 * Exposed Attributes:
12910 * threshold - integer - number of pixels before scrollbar hits end that callback is called
12911 * width - integer - override for element's width (px)
12912 * height - integer - override for element's height (px)
12913 * axis - string - x/y for scroll bar axis
12915 var threshold = parseInt(attrs.threshold, 10) || b2bWhenScrollEndsConstants.threshold;
12917 if (!attrs.axis || attrs.axis === '') {
12918 $log.warn('axis attribute must be defined for b2bWhenScrollEnds.');
12922 if (attrs.axis === 'x') {
12923 visibleWidth = parseInt(attrs.width, 10) || b2bWhenScrollEndsConstants.width;
12924 if (element.css('width')) {
12925 visibleWidth = element.css('width').split('px')[0];
12928 element[0].addEventListener('scroll', function() {
12929 var scrollableWidth = element.prop('scrollWidth');
12930 if (scrollableWidth === undefined) {
12931 scrollableWidth = 1;
12933 var hiddenContentWidth = scrollableWidth - visibleWidth;
12935 if (hiddenContentWidth - element[0].scrollLeft <= threshold) {
12936 /* Scroll almost at bottom, load more rows */
12937 scope.$apply(attrs.b2bWhenScrollEnds);
12940 } else if (attrs.axis === 'y') {
12941 visibleHeight = parseInt(attrs.height, 10) || b2bWhenScrollEndsConstants.height;
12942 if (element.css('width')) {
12943 visibleHeight = element.css('height').split('px')[0];
12946 element[0].addEventListener('scroll', function() {
12947 var scrollableHeight = element.prop('scrollHeight');
12948 if (scrollableHeight === undefined) {
12949 scrollableHeight = 1;
12951 var hiddenContentHeight = scrollableHeight - visibleHeight;
12953 if (hiddenContentHeight - element[0].scrollTop <= threshold) {
12954 /* Scroll almost at bottom, load more rows */
12955 scope.$apply(attrs.b2bWhenScrollEnds);
12963 .factory('$windowBind', ['$window', '$timeout', function($window, $timeout) {
12964 var win = angular.element($window);
12965 var _scroll = function (flag, callbackFunc, scope) {
12966 scope.$watch(flag, function (val) {
12967 $timeout(function () {
12969 win.bind('scroll', callbackFunc);
12971 win.unbind('scroll', callbackFunc);
12977 var throttle = function(type, name, obj) {
12978 obj = obj || window;
12979 var running = false;
12980 var func = function() {
12981 if (running) { return; }
12983 requestAnimationFrame(function() {
12984 obj.dispatchEvent(new CustomEvent(name));
12988 obj.addEventListener(type, func);
12991 var _resize = function(callbackFunc, scope) {
12992 throttle("resize", "optimizedResize");
12993 window.addEventListener("optimizedResize", function(event) {
12995 //win.bind(event, callbackFunc);
12996 if (!scope.$$phase) {
13002 var _click = function (flag, callbackFunc, scope) {
13003 scope.$watch(flag, function (val) {
13004 $timeout(function () {
13006 win.bind('click', callbackFunc);
13008 win.unbind('click', callbackFunc);
13014 var _event = function (event, flag, callbackFunc, scope, timeoutFlag, timeoutValue) {
13016 if (!(timeoutValue)) {
13019 scope.$watch(flag, function (newVal, oldVal) {
13020 if (newVal !== oldVal) {
13021 $timeout(function () {
13023 win.bind(event, callbackFunc);
13025 win.unbind(event, callbackFunc);
13031 scope.$watch(flag, function (newVal, oldVal) {
13032 if (newVal !== oldVal) {
13034 win.bind(event, callbackFunc);
13036 win.unbind(event, callbackFunc);
13051 .factory('keymap', function () {
13073 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 : "'"
13075 isControl: function (e) {
13078 case this.KEY.COMMAND:
13079 case this.KEY.SHIFT:
13080 case this.KEY.CTRL:
13092 isFunctionKey: function (k) {
13093 k = k.keyCode ? k.keyCode : k;
13094 return k >= 112 && k <= 123;
13096 isVerticalMovement: function (k) {
13097 return ~[this.KEY.UP, this.KEY.DOWN].indexOf(k);
13099 isHorizontalMovement: function (k) {
13100 return ~[this.KEY.LEFT, this.KEY.RIGHT, this.KEY.BACKSPACE, this.KEY.DELETE].indexOf(k);
13102 isAllowedKey: function (k) {
13103 return (~[this.KEY.SPACE, this.KEY.ESC, this.KEY.ENTER].indexOf(k)) || this.isHorizontalMovement(k) || this.isVerticalMovement(k);
13105 isNumericKey: function (e) {
13107 if ((k >= 48 && k <= 57) || (k >= 96 && k <= 105)) {
13113 isAlphaNumericKey: function (e) {
13115 if ((k >= 48 && k <= 57) || (k >= 96 && k <= 105) || (k >= 65 && k <= 90)) {
13124 .factory('$isElement', [function () {
13125 var isElement = function (currentElem, targetElem, alternateElem) {
13126 if (currentElem[0] === targetElem[0]) {
13128 } else if (currentElem[0] === alternateElem[0]) {
13131 return isElement((currentElem.parent()[0] && currentElem.parent()) || targetElem, targetElem, alternateElem);
13138 .factory('events', function () {
13139 var _stopPropagation = function (evt) {
13140 if (evt.stopPropagation) {
13141 evt.stopPropagation();
13143 evt.returnValue = false;
13146 var _preventDefault = function (evt) {
13147 if (evt.preventDefault) {
13148 evt.preventDefault();
13150 evt.returnValue = false;
13154 stopPropagation: _stopPropagation,
13155 preventDefault: _preventDefault
13160 .factory('isHighContrast', function () {
13161 var _isHighContrast = function (idval)
13165 var objDiv, objImage, strColor, strWidth, strReady;
13166 var strImageID = idval; // ID of image on the page
13168 // Create a test div
13169 objDiv = document.createElement('div');
13171 //Set its color style to something unusual
13172 objDiv.style.color = 'rgb(31, 41, 59)';
13174 // Attach to body so we can inspect it
13175 document.body.appendChild(objDiv);
13177 // Read computed color value
13178 strColor = document.defaultView ? document.defaultView.getComputedStyle(objDiv, null).color : objDiv.currentStyle.color;
13179 strColor = strColor.replace(/ /g, '');
13181 // Delete the test DIV
13182 document.body.removeChild(objDiv);
13184 // Check if we get the color back that we set. If not, we're in
13185 // high contrast mode.
13186 if (strColor !== 'rgb(31,41,59)') {
13193 return _isHighContrast;
13196 .run(['isHighContrast', '$document', function (isHighContrast, $document) {
13197 var html = $document.find('html').eq(0);
13198 if (isHighContrast()) {
13199 html.addClass('ds2-no-colors');
13201 html.removeClass('ds2-no-colors');
13205 .factory('$documentBind', ['$document', '$timeout', function ($document, $timeout) {
13206 var _click = function (flag, callbackFunc, scope) {
13207 scope.$watch(flag, function (val) {
13208 $timeout(function () {
13210 $document.bind('click', callbackFunc);
13212 $document.unbind('click', callbackFunc);
13218 var _touch = function (flag, callbackFunc, scope) {
13219 scope.$watch(flag, function (val) {
13220 $timeout(function () {
13222 $document.bind('touchstart', callbackFunc);
13224 $document.unbind('touchstart', callbackFunc);
13230 var _scroll = function (flag, callbackFunc, scope) {
13231 scope.$watch(flag, function (val) {
13232 $timeout(function () {
13234 $document.bind('scroll', callbackFunc);
13236 $document.unbind('scroll', callbackFunc);
13242 var _event = function (event, flag, callbackFunc, scope, timeoutFlag, timeoutValue) {
13244 if (!(timeoutValue)) {
13247 scope.$watch(flag, function (newVal, oldVal) {
13248 if (newVal !== oldVal) {
13249 $timeout(function () {
13251 $document.bind(event, callbackFunc);
13253 $document.unbind(event, callbackFunc);
13259 scope.$watch(flag, function (newVal, oldVal) {
13260 if (newVal !== oldVal) {
13262 $document.bind(event, callbackFunc);
13264 $document.unbind(event, callbackFunc);
13279 .directive('b2bOnlyNums', function (keymap) {
13282 require: 'ngModel',
13283 link: function (scope, elm, attrs, ctrl) {
13284 var maxChars = attrs.b2bOnlyNums ? attrs.b2bOnlyNums : 4;
13285 elm.on('keydown', function (event) {
13286 if ((event.which >= 48 && event.which <= 57) || (event.which >= 96 && event.which <= 105)) {
13287 // check for maximum characters allowed
13288 if (elm.val().length < maxChars){
13291 event.preventDefault();
13294 } else if ([8, 9, 13, 27, 37, 38, 39, 40].indexOf(event.which) > -1) {
13295 // to allow backspace, tab, enter, escape, arrows
13297 } else if (event.altKey || event.ctrlKey) {
13298 // to allow alter, control, and shift keys
13302 event.preventDefault();
13307 var validateString = function (value) {
13308 if (angular.isUndefined(value) || value === null || value === '') {
13309 return ctrl.$modelValue;
13313 ctrl.$parsers.unshift(validateString);
13318 .directive('b2bKeyupClick', [ function () {
13321 link: function (scope, elem, attr) {
13323 attr.$observe('b2bKeyupClick', function (value) {
13325 keyCode = value.split(',');
13328 elem.bind('keydown keyup', function (ev) {
13329 var keyCodeCondition = function () {
13331 if (!(ev.keyCode)) {
13333 ev.keyCode = ev.which;
13334 } else if (ev.charCode) {
13335 ev.keyCode = ev.charCode;
13338 if ((ev.keyCode && keyCode.indexOf(ev.keyCode.toString()) > -1)) {
13343 if (ev.type === 'keydown' && keyCodeCondition()) {
13344 ev.preventDefault();
13346 else if (ev.type === 'keyup' && keyCodeCondition()) {
13354 .factory('b2bDOMHelper', function() {
13356 var _isTabable = function(node) {
13357 var element = angular.element(node);
13358 var tagName = element[0].tagName.toUpperCase();
13360 if (isHidden(element)) {
13363 if (element.attr('tabindex') !== undefined) {
13364 return (parseInt(element.attr('tabindex'), 10) >= 0);
13366 if (tagName === 'A' || tagName === 'AREA' || tagName === 'BUTTON' || tagName === 'INPUT' || tagName === 'TEXTAREA' || tagName === 'SELECT') {
13367 if (tagName === 'A' || tagName === 'AREA') {
13368 // anchors/areas without href are not focusable
13369 return (element[0].href !== '');
13371 return !(element[0].disabled);
13376 function isValidChild(child) {
13377 return child.nodeType == 1 && child.nodeName != 'SCRIPT' && child.nodeName != 'STYLE';
13380 function isHidden(obj) {
13381 var elem = angular.element(obj);
13382 var elemStyle = undefined;
13383 if(obj instanceof HTMLElement){
13384 elemStyle = window.getComputedStyle(obj);
13387 elemStyle = window.getComputedStyle(obj[0]);
13389 return elem.hasClass('ng-hide') || elem.css('display') === 'none' || elemStyle.display === 'none' || elemStyle.visibility === 'hidden' || elem.css('visibility') === 'hidden';
13392 function hasValidParent(obj) {
13393 return (isValidChild(obj) && obj.parentElement.nodeName !== 'BODY');
13396 function traverse(obj, fromTop) {
13397 var obj = obj || document.getElementsByTagName('body')[0];
13398 if (isValidChild(obj) && _isTabable(obj)) {
13401 // If object is hidden, skip it's children
13402 if (isValidChild(obj) && isHidden(obj)) {
13405 // If object is hidden, skip it's children
13406 if (angular.element(obj).hasClass('ng-hide')) {
13409 if (obj.hasChildNodes()) {
13412 child = obj.firstChild;
13414 child = obj.lastChild;
13417 var res = traverse(child, fromTop);
13423 child = child.nextSibling;
13425 child = child.previousSibling;
13435 var _previousElement = function(el, isFocusable){
13438 if (el.hasOwnProperty('length')) {
13442 var parent = elem.parentElement;
13443 var previousElem = undefined;
13446 if (hasValidParent(elem)) {
13447 var siblings = angular.element(parent).children();
13448 if (siblings.length > 0) {
13449 // Good practice to splice out the elem from siblings if there, saving some time.
13450 // We allow for a quick check for jumping to parent first before removing.
13451 if (siblings[0] === elem) {
13452 // If we are looking at immidiate parent and elem is first child, we need to go higher
13453 var e = _previousElement(angular.element(elem).parent(), isFocusable);
13454 if (_isTabable(e)) {
13458 // I need to filter myself and any nodes next to me from the siblings
13459 var indexOfElem = Array.prototype.indexOf.call(siblings, elem);
13460 siblings = Array.prototype.filter.call(siblings, function(item, itemIndex) {
13461 if (!angular.equals(elem, item) && itemIndex < indexOfElem) {
13466 // We need to search backwards
13467 for (var i = 0; i <= siblings.length-1; i++) {//for (var i = siblings.length-1; i >= 0; i--) {
13468 var ret = traverse(siblings[i], false);
13469 if (ret !== undefined) {
13474 var e = _previousElement(angular.element(elem).parent(), isFocusable);
13475 if (_isTabable(e)) {
13481 var siblings = angular.element(parent).children();
13482 if (siblings.length > 1) {
13483 // Since indexOf is on Array.prototype and parent.children is a NodeList, we have to use call()
13484 var index = Array.prototype.indexOf.call(siblings, elem);
13485 previousElem = siblings[index-1];
13488 return previousElem;
13491 var _lastTabableElement = function(el) {
13492 /* This will return the first tabable element from the parent el */
13494 if (el.hasOwnProperty('length')) {
13498 return traverse(elem, false);
13501 var _firstTabableElement = function(el) {
13502 /* This will return the first tabable element from the parent el */
13504 if (el.hasOwnProperty('length')) {
13508 return traverse(elem, true);
13511 var _isInDOM = function(obj) {
13512 return document.documentElement.contains(obj);
13516 firstTabableElement: _firstTabableElement,
13517 lastTabableElement: _lastTabableElement,
13518 previousElement: _previousElement,
13520 isTabable: _isTabable,
13525 .factory('windowOrientation', ['$window', function ($window) {
13526 var _isPotrait = function () {
13527 if ($window.innerHeight > $window.innerWidth) {
13533 var _isLandscape = function () {
13534 if ($window.innerHeight < $window.innerWidth) {
13542 isPotrait: _isPotrait,
13543 isLandscape: _isLandscape
13546 .directive('b2bNextElement', function() {
13550 link: function (scope, elem, attr, ctrls) {
13552 var keys = attr.b2bNextElement.split(',');
13554 elem.bind('keydown', function (e) {
13555 var nextElement = elem.next();
13556 if(e.keyCode == 39 || e.keyCode == 40){ // if e.keyCode in keys
13557 if(nextElement.length) {
13558 e.preventDefault();
13559 nextElement[0].focus();
13567 .directive('b2bAccessibilityClick', [function () {
13570 link: function (scope, elem, attr, ctrl) {
13572 attr.$observe('b2bAccessibilityClick', function (value) {
13574 keyCode = value.split(',');
13577 elem.bind('keydown keypress', function (ev) {
13578 var keyCodeCondition = function () {
13580 if (!(ev.keyCode)) {
13582 ev.keyCode = ev.which;
13583 } else if (ev.charCode) {
13584 ev.keyCode = ev.charCode;
13587 if ((ev.keyCode && keyCode.indexOf(ev.keyCode.toString()) > -1)) {
13592 if (keyCode.length > 0 && keyCodeCondition()) {
13594 ev.preventDefault();
13601 .directive('b2bReset', ['$compile', function ($compile) {
13604 require: ['?ngModel', 'b2bReset'],
13605 controller: ['$scope', function ($scope) {
13606 var resetButton = angular.element('<button type="button" class="reset-field" tabindex="-1" aria-label="Click to reset" aria-hidden="true" role="button"></button>');
13608 this.getResetButton = function () {
13609 return resetButton;
13612 link: function (scope, element, attrs, ctrls) {
13614 var ngModelCtrl = ctrls[0];
13615 var ctrl = ctrls[1];
13617 var resetButton = ctrl.getResetButton();
13620 resetButton.on('click', function () {
13621 element[0].value = '';
13624 if (attrs.b2bReset) {
13625 ngModelCtrl.$setViewValue(attrs.b2bReset);
13627 ngModelCtrl.$setViewValue('');
13629 element[0].value = ngModelCtrl.$viewValue;
13630 ngModelCtrl.$render();
13633 element[0].focus();
13634 element[0].select();
13637 var addResetButton = function () {
13638 element.after(resetButton);
13639 element.unbind('focus input', addResetButton);
13642 element.bind('focus input', addResetButton);
13647 .directive('b2bPrevElement', ['b2bDOMHelper', 'keymap', function (b2bDOMHelper, keymap) {
13651 link: function (scope, elem, attr) {
13653 elem.bind('keydown', function (e) {
13654 if(e.keyCode == 37 || e.keyCode == 38){
13655 var prev = b2bDOMHelper.previousElement(elem, false);
13656 if(prev !== undefined) {
13657 e.preventDefault();
13666 * @param {integer} delay - Timeout before first and last focusable elements are found
13667 * @param {boolean} trigger - A variable on scope that will trigger refinding first/last focusable elements
13669 .directive('b2bTrapFocusInsideElement', ['$timeout', 'b2bDOMHelper', 'keymap', 'events', function ($timeout, b2bDOMHelper, keymap, events) {
13673 link: function (scope, elem, attr) {
13675 var delay = parseInt(attr.delay, 10) || 10;
13677 /* Before opening modal, find the focused element */
13678 var firstTabableElement = undefined,
13679 lastTabableElement = undefined;
13682 $timeout(function () {
13683 firstTabableElement = b2bDOMHelper.firstTabableElement(elem);
13684 lastTabableElement = b2bDOMHelper.lastTabableElement(elem);
13685 angular.element(firstTabableElement).bind('keydown', firstTabableElementKeyhandler);
13686 angular.element(lastTabableElement).bind('keydown', lastTabableElementKeyhandler);
13690 if (attr.trigger !== undefined) {
13691 scope.$watch('trigger', function() {
13692 if (scope.trigger) {
13698 var firstTabableElementKeyhandler = function(e) {
13700 e.keyCode = e.which;
13702 if (e.keyCode === keymap.KEY.TAB && e.shiftKey) {
13703 if (attr.trapFocusInsideElement === 'true') {
13704 var temp = b2bDOMHelper.lastTabableElement(elem);
13705 if (lastTabableElement !== temp) {
13706 // Unbind keydown handler on lastTabableElement
13707 angular.element(lastTabableElement).unbind('keydown', lastTabableElementKeyhandler);
13708 lastTabableElement = temp;
13709 angular.element(lastTabableElement).bind('keydown', lastTabableElementKeyhandler);
13712 lastTabableElement.focus();
13713 events.preventDefault(e);
13714 events.stopPropagation(e);
13718 var lastTabableElementKeyhandler = function(e) {
13720 e.keyCode = e.which;
13722 if (e.keyCode === keymap.KEY.TAB && !e.shiftKey) {
13723 if (attr.trapFocusInsideElement === 'true') {
13724 var temp = b2bDOMHelper.firstTabableElement(elem);
13725 if (firstTabableElement !== temp) {
13726 // Unbind keydown handler on firstTabableElement
13727 angular.element(firstTabableElement).unbind('keydown', firstTabableElementKeyhandler);
13728 firstTabableElement = temp;
13729 angular.element(firstTabableElement).bind('keydown', firstTabableElementKeyhandler);
13732 firstTabableElement.focus();
13733 events.preventDefault(e);
13734 events.stopPropagation(e);
13743 .factory('trapFocusInElement', ['$document', '$isElement', 'b2bDOMHelper', 'keymap', '$interval', function ($document, $isElement, b2bDOMHelper, keymap, $interval) {
13744 var elementStack = [];
13745 var stackHead = undefined;
13746 var stopInterval = undefined;
13747 var intervalRequired = false;
13748 var interval = 1000;
13749 var firstTabableElement, lastTabableElement;
13751 var trapKeyboardFocusInFirstElement = function (e) {
13753 e.keyCode = e.which;
13756 if (e.shiftKey === true && e.keyCode === keymap.KEY.TAB) {
13757 lastTabableElement[0].focus();
13758 e.preventDefault(e);
13759 e.stopPropagation(e);
13764 var trapKeyboardFocusInLastElement = function (e) {
13766 e.keyCode = e.which;
13769 if (e.shiftKey === false && e.keyCode === keymap.KEY.TAB) {
13770 firstTabableElement[0].focus();
13771 e.preventDefault(e);
13772 e.stopPropagation(e);
13776 var trapFocusInElement = function (flag, firstTabableElementParam, lastTabableElementParam) {
13777 var bodyElements = $document.find('body').children();
13779 firstTabableElement = firstTabableElementParam ? firstTabableElementParam : angular.element(b2bDOMHelper.firstTabableElement(stackHead));
13780 lastTabableElement = lastTabableElementParam ? lastTabableElementParam : angular.element(b2bDOMHelper.lastTabableElement(stackHead));
13783 for (var i = 0; i < bodyElements.length; i++) {
13784 if (bodyElements[i] !== stackHead[0]) {
13785 bodyElements.eq(i).attr('aria-hidden', true);
13788 firstTabableElement.bind('keydown', trapKeyboardFocusInFirstElement);
13789 lastTabableElement.bind('keydown', trapKeyboardFocusInLastElement);
13791 for (var j = 0; j < bodyElements.length; j++) {
13792 if (bodyElements[j] !== stackHead[0]) {
13793 bodyElements.eq(j).removeAttr('aria-hidden');
13796 firstTabableElement.unbind('keydown', trapKeyboardFocusInFirstElement);
13797 lastTabableElement.unbind('keydown', trapKeyboardFocusInLastElement);
13800 if (intervalRequired && flag) {
13801 stopInterval = $interval(function () {
13802 var firstTabableElementTemp = angular.element(b2bDOMHelper.firstTabableElement(stackHead));
13803 var lastTabableElementTemp = angular.element(b2bDOMHelper.lastTabableElement(stackHead));
13804 if (firstTabableElementTemp[0] !== firstTabableElement[0] || lastTabableElementTemp[0] !== lastTabableElement[0]) {
13805 $interval.cancel(stopInterval);
13806 stopInterval = undefined;
13807 trapFocusInElement(false, firstTabableElement, lastTabableElement);
13808 trapFocusInElement(true, firstTabableElementTemp, lastTabableElementTemp);
13812 if (stopInterval) {
13813 $interval.cancel(stopInterval);
13814 stopInterval = undefined;
13818 var toggleTrapFocusInElement = function (flag, element, intervalRequiredParam, intervalParam) {
13819 intervalRequired = intervalRequiredParam ? intervalRequiredParam : intervalRequired;
13820 interval = intervalParam ? intervalParam : interval;
13821 if (angular.isDefined(flag) && angular.isDefined(element)) {
13822 if (flag && angular.isUndefined(stackHead)) {
13823 stackHead = element;
13824 trapFocusInElement(flag);
13827 trapFocusInElement(false);
13828 elementStack.push(stackHead);
13829 stackHead = element;
13830 trapFocusInElement(true);
13832 if (angular.isDefined(stackHead) && (stackHead[0] === element[0])) {
13833 trapFocusInElement(false);
13834 stackHead = elementStack.pop();
13835 if (angular.isDefined(stackHead)) {
13836 trapFocusInElement(true);
13842 if (angular.isDefined(stackHead)) {
13843 trapFocusInElement(false, firstTabableElement, lastTabableElement);
13844 trapFocusInElement(true);
13849 return toggleTrapFocusInElement;
13851 .factory('draggedElement', function(){
13852 var draggedElement;
13854 setElement: function(data){
13855 draggedElement = data;
13857 getElement: function(){
13858 return draggedElement;
13863 .directive('draggable', ['draggedElement',function (draggedElement) {
13864 return function(scope, element) {
13866 element[0].draggable = true;
13868 element.bind('dragstart', function(e) {
13869 draggedElement.setElement(this.parentElement.parentElement);
13870 e.dataTransfer.effectAllowed = 'move';
13871 e.dataTransfer.setDragImage(this.parentElement.parentElement, 0, 0);
13872 this.parentElement.parentElement.classList.add('b2-drag-element');
13876 element.bind('dragend', function(e) {
13877 draggedElement.setElement(e);
13878 this.parentElement.parentElement.classList.remove('b2-drag-element');
13884 .directive('droppable', ['draggedElement',function (draggedElement) {
13891 link: function(scope, element, attr) {
13892 if(attr.droppable === 'true') {
13893 element.bind('dragover', function(e) {
13894 e.dataTransfer.dropEffect = 'move';
13895 this.classList.add('b2b-drag-over')
13896 if (e.preventDefault) e.preventDefault(); // allows us to drop
13900 element.bind('dragstart', function(e) {
13901 if(!e.target.parentElement.classList.contains('b2b-draggable')) {
13902 e.preventDefault();
13907 element.bind('dragenter', function(e) {
13908 if(e.target.getAttribute('droppable') ==='true') {
13913 element.bind('dragleave', function(e) {
13914 this.classList.remove('b2b-drag-over');
13918 element.bind('drop', function(e) {
13919 var ele = draggedElement.getElement();
13920 if (e.stopPropagation) e.stopPropagation();
13921 if (e.preventDefault) e.preventDefault();
13922 this.classList.remove('b2b-drag-over');
13924 if(ele && ele.hasAttribute('data-index')){
13925 var element = scope.rowData[parseInt(ele.getAttribute('data-index'))];
13926 if(element !== undefined) {
13927 scope.rowData.splice(parseInt(ele.getAttribute('data-index')), 1);
13928 scope.rowData.splice(parseInt(e.currentTarget.getAttribute('data-index')), 0 , element);
13939 .directive('b2bSetNextFocusOn', ['b2bDOMHelper', '$timeout', function(b2bDOMHelper, $timeout) {
13943 link: function (scope, elem, attr) {
13944 elem.bind('click', function(){
13945 var firstFocusableElement = undefined;
13946 var containerElem = undefined;
13947 var containerArray = [];
13948 var timeout = parseInt(attr.setNextFocusTimeout, 0) | 100;
13949 var index = parseInt(attr.b2bSetNextFocusIndex, 0) | 0;
13952 *Fix for IE7 and lower
13953 *polyfill src: https://github.com/HubSpot/pace/issues/102
13955 if (!document.querySelectorAll) {
13956 document.querySelectorAll = function (selectors) {
13957 var style = document.createElement('style'), elements = [], element;
13958 document.documentElement.firstChild.appendChild(style);
13959 document._qsa = [];
13961 style.styleSheet.cssText = selectors + '{x-qsa:expression(document._qsa && document._qsa.push(this))}';
13962 window.scrollBy(0, 0);
13963 style.parentNode.removeChild(style);
13965 while (document._qsa.length) {
13966 element = document._qsa.shift();
13967 element.style.removeAttribute('x-qsa');
13968 elements.push(element);
13970 document._qsa = null;
13975 if (attr.b2bSetNextFocusOn === '') {
13978 containerArray = attr.b2bSetNextFocusOn.split(' ');
13980 $timeout(function(){
13982 do { // cycles thru containerArray until finds a match in DOM to set focus to
13983 containerElem = document.querySelectorAll(containerArray[i])[index];
13985 } while ( (!containerElem) && (i < containerArray.length) );
13987 if (!angular.isDefined(firstFocusableElement)) {
13988 firstFocusableElement = b2bDOMHelper.firstTabableElement(containerElem);
13990 firstFocusableElement.focus();
13999 .directive('b2bInputAllow', [function() {
14002 require: 'ngModel',
14003 link: function (scope, elem, attr, ctrl) {
14004 var regexExpression = null;
14005 attr.$observe('b2bInputAllow', function (value) {
14007 regexExpression = new RegExp(value);
14010 var isValid = function(str) {
14011 if (regexExpression !== null) {
14012 return regexExpression.test(str);
14016 elem.bind('keypress', function($event) {
14017 var charcode = String.fromCharCode($event.which || $event.keyCode);
14018 if (!isValid(charcode)) {
14019 $event.preventDefault();
14020 $event.stopPropagation();
14023 elem.bind('input', function (evt) {
14024 var inputString = ctrl.$viewValue;
14025 if (isValid(inputString)) {
14026 ctrl.$setViewValue(inputString);
14035 .directive('b2bInputDeny', [function() {
14038 require: 'ngModel',
14039 link: function (scope, elem, attr, ctrl) {
14040 var regexExpression = null;
14041 attr.$observe('b2bInputDeny', function (value) {
14043 regexExpression = new RegExp(value, 'g');
14046 elem.bind('input', function () {
14047 var inputString = ctrl.$viewValue && ctrl.$viewValue.replace(regexExpression, '');
14048 if (inputString !== ctrl.$viewValue) {
14049 ctrl.$setViewValue(inputString);
14058 .directive('b2bDragonInput', [function() {
14061 require: 'ngModel',
14062 link: function (scope, elem, attr, ctrl) {
14063 elem.on('focus keyup', function(){
14064 elem.triggerHandler('change');
14070 .directive('b2bKey', ['b2bUtilitiesConfig', '$timeout', 'keymap', function (b2bUtilitiesConfig, $timeout, keymap) {
14073 controller: ['$scope', '$element', '$attrs', function ($scope, $element,attr) {
14074 this.childElements = [];
14075 this.disableNodes = {};
14076 this.enableSearch = attr.enableSearch !== undefined ? true : b2bUtilitiesConfig.enableSearch;
14077 this.circularTraversal = attr.circularTraversal !== undefined ? true : b2bUtilitiesConfig.circularTraversal;
14079 if (this.enableSearch) {
14080 this.searchKeys = [];
14082 var searchString = '';
14084 var selfCtrl = this;
14086 this.childElementsList = [];
14088 this.b2bKeyID = "";
14090 if (angular.isDefined(attr.b2bKey)) {
14091 this.b2bKeyID = attr.b2bKey;
14094 this.calculateChildElementsList = function () {
14095 return $element[0].querySelectorAll("[b2b-key-item='" + this.b2bKeyID + "']:not([disabled])");
14098 this.resetChildElementsList = function () {
14099 return $timeout(function () {
14100 selfCtrl.childElementsList = selfCtrl.calculateChildElementsList();
14104 this.resetChildElementsList();
14106 $scope.$on('b2b-key-reset-child-elements-list', function () {
14107 selfCtrl.resetChildElementsList();
14111 this.registerElement = function (childElement, searchKey) {
14112 this.childElements.push(childElement);
14113 if (this.enableSearch) {
14114 this.searchKeys.push(searchKey);
14116 var count = this.childElements.length - 1;
14117 this.maxLength = count + 1;
14120 this.toggleDisable = function (count, state) {
14121 this.disableNodes[count] = state;
14123 this.searchElement = function (searchExp) {
14124 var regex = new RegExp("\\b" + searchExp, "gi");
14125 var position = this.searchKeys.regexIndexOf(regex, this.counter + 1, true);
14126 if (position > -1) {
14127 this.counter = position;
14128 this.moveFocus(this.counter);
14131 this.startTimer = function (time) {
14132 if (searchString === '') {
14133 $timeout(function () {
14138 this.resetCounter = function (count) {
14139 this.counter = count;
14141 this.moveNext = function (count) {
14142 this.counter = (this.counter + count) < this.maxLength ? this.counter + count : (this.circularTraversal ? 0 : this.counter);
14143 if (this.disableNodes[this.counter]) {
14144 if ((this.counter + count) < this.maxLength) {
14145 this.moveNext(count);
14148 this.moveFocus(this.counter);
14151 this.movePrev = function (count) {
14152 this.counter = (this.counter - count) > -1 ? this.counter - count : (this.circularTraversal ? this.maxLength-1 : this.counter);
14153 if (this.disableNodes[this.counter]) {
14154 if ((this.counter - count) > -1) {
14155 this.movePrev(count);
14158 this.moveFocus(this.counter);
14161 this.moveFocus = function (index) {
14162 this.childElements[index][0].focus();
14165 this.keyDownHandler = function (ev, count) {
14166 if (angular.isDefined(count) && !isNaN(count) && count !== this.counter) {
14167 this.resetCounter(count);
14171 ev.keyCode = ev.which;
14172 } else if (ev.charCode) {
14173 ev.keyCode = ev.charCode;
14177 if (this.prev && this.prev.indexOf(ev.keyCode.toString()) > -1) {
14179 ev.preventDefault();
14180 ev.stopPropagation();
14181 } else if (this.next && this.next.indexOf(ev.keyCode.toString()) > -1) {
14183 ev.preventDefault();
14184 ev.stopPropagation();
14185 } else if (this.up && this.up.indexOf(ev.keyCode.toString()) > -1) {
14186 if (this.type === 'table') {
14187 this.movePrev(this.columns);
14188 ev.preventDefault();
14189 ev.stopPropagation();
14191 } else if (this.down && this.down.indexOf(ev.keyCode.toString()) > -1) {
14192 if (this.type === 'table') {
14193 this.moveNext(this.columns);
14194 ev.preventDefault();
14195 ev.stopPropagation();
14197 } else if (ev.keyCode === keymap.KEY.HOME) {
14198 var firstIndex = 0;
14199 while (this.disableNodes[firstIndex] !== false) {
14202 var count = this.counter - firstIndex;
14203 this.movePrev(count);
14204 ev.preventDefault();
14205 ev.stopPropagation();
14206 } else if (ev.keyCode === keymap.KEY.END) {
14207 var lastIndex = this.childElements.length - 1;
14208 while (this.disableNodes[lastIndex] !== false) {
14211 var count = lastIndex - this.counter;
14212 this.moveNext(count);
14213 ev.preventDefault();
14214 ev.stopPropagation();
14215 } else if (ev.keyCode >= 48 && ev.keyCode <= 105) {
14216 if (this.enableSearch) {
14217 this.startTimer(b2bUtilitiesConfig.searchTimer);
14218 searchString = searchString + (keymap.MAP[ev.keyCode] || '');
14219 this.searchElement(searchString);
14220 ev.preventDefault();
14221 ev.stopPropagation();
14227 link: function (scope, elem, attr, ctrl) {
14228 ctrl.prev = attr.prev ? attr.prev.split(',') : b2bUtilitiesConfig.prev.split(',');
14229 ctrl.next = attr.next ? attr.next.split(',') : b2bUtilitiesConfig.next.split(',');
14230 ctrl.type = attr.type ? attr.type : b2bUtilitiesConfig.type;
14231 if (ctrl.type === 'table') {
14232 ctrl.up = attr.up ? attr.up.split(',') : b2bUtilitiesConfig.up.split(',');
14233 ctrl.down = attr.down ? attr.down.split(',') : b2bUtilitiesConfig.down.split(',');
14234 ctrl.columns = attr.columns ? parseInt(attr.columns, 10) : b2bUtilitiesConfig.columns;
14237 elem.bind('keydown', function (ev) {
14238 ctrl.keyDownHandler(ev);
14244 .directive('b2bKeyItem', [function () {
14247 link: function (scope, elem, attr, ctrl) {
14248 var parentCtrl = (elem.parent() && elem.parent().controller('b2bKey')) || undefined;
14249 if (angular.isDefined(parentCtrl)) {
14250 var count = parentCtrl.registerElement(elem, attr.searchKey);
14251 elem.bind('keydown', function (ev) {
14252 parentCtrl.keyDownHandler(ev, count);
14254 scope.$watch(attr.b2bKeyItem, function (value) {
14255 value = value === undefined ? true : value;
14256 parentCtrl.toggleDisable(count, !value);
14258 scope.$on('$destroy', function () {
14259 parentCtrl.toggleDisable(count, true);
14266 .directive('b2bElementFocus', [function () {
14269 link: function (scope, elem, attr, ctrl) {
14270 scope.$watch(attr.b2bElementFocus, function (value) {
14271 if (value === true) {
14280 .directive('b2bAppendElement', ['$compile', function ($compile) {
14283 link: function (scope, elem, attr, ctrl) {
14284 var parameters = attr.b2bAppendElement.split(':');
14285 if (parameters.length === 1) {
14286 elem.append(scope.$eval(parameters[0]));
14287 } else if (parameters.length === 2) {
14288 if (parameters[1] === 'compile') {
14289 var element = angular.element('<span>' + scope.$eval(parameters[0]) + '</span>');
14290 elem.append($compile(element)(scope));
14298 .directive('b2bKeyItemRefreshInNgRepeat', [function () {
14301 require: '^^b2bKey',
14302 link: function (scope, elem, attr, parentCtrl) {
14303 if (angular.isDefined(parentCtrl)) {
14305 var attrToObserve = 'attrToObserve';
14307 if (attr.b2bKeyItemRefreshInNgRepeat) {
14308 attrToObserve = 'b2bKeyItemRefreshInNgRepeat';
14311 attr.$observe(attrToObserve, function (newVal, oldVal) {
14312 if (newVal && newVal !== oldVal) {
14313 parentCtrl.resetChildElementsList();
14321 .constant('b2bMaskConfig', {
14327 clearOnBlur: false,
14328 clearOnBlurPlaceholder: false,
14330 eventsToHandle: ['input', 'keyup', 'click', 'focus'],
14331 addDefaultPlaceholder: true,
14332 allowInvalidValue: true
14335 * @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.
14336 * @param {String} maskPlaceholder - Allows customizing the mask placeholder when a user has focused the input element and while typing in their value
14337 * @param {String} maskPlaceholderChar - Allows customizing the mask placeholder character. The default mask placeholder is _.
14338 * @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.
14340 .directive('b2bMask', ['b2bMaskConfig', function(b2bMaskConfig) {
14342 require: 'ngModel',
14344 link: function(scope, element, attrs, ctrl) {
14345 var maskProcessed = false, eventsBound = false,
14346 maskCaretMap, maskPatterns, maskPlaceholder, maskComponents,
14347 // Minimum required length of the value to be considered valid
14349 value, valueMasked, isValid,
14350 // Vars for initializing/uninitializing
14351 originalPlaceholder = attrs.placeholder,
14352 originalMaxlength = attrs.maxlength,
14353 // Vars used exclusively in eventHandler()
14354 oldValue, oldValueUnmasked, oldCaretPosition, oldSelectionLength,
14355 // Used for communicating if a backspace operation should be allowed between
14356 // keydownHandler and eventHandler
14359 var options = b2bMaskConfig;
14361 function isFocused (elem) {
14362 return elem === document.activeElement && (!document.hasFocus || document.hasFocus()) && !!(elem.type || elem.href || ~elem.tabIndex);
14365 var originalIsEmpty = ctrl.$isEmpty;
14366 ctrl.$isEmpty = function(value) {
14367 if (maskProcessed) {
14368 return originalIsEmpty(unmaskValue(value || ''));
14370 return originalIsEmpty(value);
14374 function initialize(maskAttr) {
14375 if (!angular.isDefined(maskAttr)) {
14376 return uninitialize();
14378 processRawMask(maskAttr);
14379 if (!maskProcessed) {
14380 return uninitialize();
14382 initializeElement();
14383 bindEventListeners();
14387 function initPlaceholder(placeholderAttr) {
14388 if ( ! placeholderAttr) {
14391 maskPlaceholder = placeholderAttr;
14392 /* If the mask is processed, then we need to update the value
14393 but don't set the value if there is nothing entered into the element
14394 and there is a placeholder attribute on the element because that
14395 will only set the value as the blank maskPlaceholder
14396 and override the placeholder on the element */
14397 if (maskProcessed && !(element.val().length === 0 && angular.isDefined(attrs.placeholder))) {
14398 element.val(maskValue(unmaskValue(element.val())));
14402 function initPlaceholderChar() {
14403 return initialize(attrs.uiMask);
14406 var modelViewValue = false;
14408 attrs.$observe('modelViewValue', function(val) {
14409 if (val === 'true') {
14410 modelViewValue = true;
14414 attrs.$observe('allowInvalidValue', function(val) {
14415 linkOptions.allowInvalidValue = val === ''? true : !!val;
14416 formatter(ctrl.$modelValue);
14419 function formatter(fromModelValue) {
14420 if (!maskProcessed) {
14421 return fromModelValue;
14423 value = unmaskValue(fromModelValue || '');
14424 isValid = validateValue(value);
14425 ctrl.$setValidity('mask', isValid);
14427 if (!value.length) return undefined;
14428 if (isValid || linkOptions.allowInvalidValue) {
14429 return maskValue(value);
14435 function parser(fromViewValue) {
14436 if (!maskProcessed) {
14437 return fromViewValue;
14439 value = unmaskValue(fromViewValue || '');
14440 isValid = validateValue(value);
14441 /* We have to set viewValue manually as the reformatting of the input
14442 value performed by eventHandler() doesn't happen until after
14443 this parser is called, which causes what the user sees in the input
14444 to be out-of-sync with what the ctrl's $viewValue is set to. */
14445 ctrl.$viewValue = value.length ? maskValue(value) : '';
14446 ctrl.$setValidity('mask', isValid);
14448 if (isValid || linkOptions.allowInvalidValue) {
14449 return modelViewValue ? ctrl.$viewValue : value;
14453 var linkOptions = {};
14456 if (attrs.b2bMaskOptions) {
14457 linkOptions = scope.$eval('[' + attrs.b2bMaskOptions + ']');
14458 if (angular.isObject(linkOptions[0])) {
14459 // we can't use angular.copy nor angular.extend, they lack the power to do a deep merge
14460 linkOptions = (function(original, current) {
14461 for (var i in original) {
14462 if (Object.prototype.hasOwnProperty.call(original, i)) {
14463 if (current[i] === undefined) {
14464 current[i] = angular.copy(original[i]);
14466 if (angular.isObject(current[i]) && !angular.isArray(current[i])) {
14467 current[i] = angular.extend({}, original[i], current[i]);
14473 })(options, linkOptions[0]);
14475 linkOptions = options; //gotta be a better way to do this..
14478 linkOptions = options;
14481 attrs.$observe('b2bMask', initialize);
14482 if (angular.isDefined(attrs.maskPlaceholder)) {
14483 attrs.$observe('maskPlaceholder', initPlaceholder);
14486 attrs.$observe('placeholder', initPlaceholder);
14488 if (angular.isDefined(attrs.maskPlaceholderChar)) {
14489 attrs.$observe('maskPlaceholderChar', initPlaceholderChar);
14492 ctrl.$formatters.unshift(formatter);
14493 ctrl.$parsers.unshift(parser);
14495 function uninitialize() {
14496 maskProcessed = false;
14497 unbindEventListeners();
14499 if (angular.isDefined(originalPlaceholder)) {
14500 element.attr('placeholder', originalPlaceholder);
14502 element.removeAttr('placeholder');
14505 if (angular.isDefined(originalMaxlength)) {
14506 element.attr('maxlength', originalMaxlength);
14508 element.removeAttr('maxlength');
14511 element.val(ctrl.$modelValue);
14512 ctrl.$viewValue = ctrl.$modelValue;
14516 function initializeElement() {
14517 value = oldValueUnmasked = unmaskValue(ctrl.$modelValue || '');
14518 valueMasked = oldValue = maskValue(value);
14519 isValid = validateValue(value);
14520 if (attrs.maxlength) { // Double maxlength to allow pasting new val at end of mask
14521 element.attr('maxlength', maskCaretMap[maskCaretMap.length - 1] * 2);
14523 if ( ! originalPlaceholder && linkOptions.addDefaultPlaceholder) {
14524 element.attr('placeholder', maskPlaceholder);
14526 var viewValue = ctrl.$modelValue;
14527 var idx = ctrl.$formatters.length;
14529 viewValue = ctrl.$formatters[idx](viewValue);
14531 ctrl.$viewValue = viewValue || '';
14535 function bindEventListeners() {
14539 element.bind('blur', blurHandler);
14540 element.bind('mousedown mouseup', mouseDownUpHandler);
14541 element.bind('keydown', keydownHandler);
14542 element.bind(linkOptions.eventsToHandle.join(' '), eventHandler);
14543 eventsBound = true;
14546 function unbindEventListeners() {
14547 if (!eventsBound) {
14550 element.unbind('blur', blurHandler);
14551 element.unbind('mousedown', mouseDownUpHandler);
14552 element.unbind('mouseup', mouseDownUpHandler);
14553 element.unbind('keydown', keydownHandler);
14554 element.unbind('input', eventHandler);
14555 element.unbind('keyup', eventHandler);
14556 element.unbind('click', eventHandler);
14557 element.unbind('focus', eventHandler);
14558 eventsBound = false;
14561 function validateValue(value) {
14562 // Zero-length value validity is ngRequired's determination
14563 return value.length ? value.length >= minRequiredLength : true;
14566 function unmaskValue(value) {
14567 var valueUnmasked = '',
14568 input = element[0],
14569 maskPatternsCopy = maskPatterns.slice(),
14570 selectionStart = oldCaretPosition,
14571 selectionEnd = selectionStart + getSelectionLength(input),
14572 valueOffset, valueDelta, tempValue = '';
14573 // Preprocess by stripping mask components from value
14574 value = value.toString();
14576 valueDelta = value.length - maskPlaceholder.length;
14577 angular.forEach(maskComponents, function(component) {
14578 var position = component.position;
14579 //Only try and replace the component if the component position is not within the selected range
14580 //If component was in selected range then it was removed with the user input so no need to try and remove that component
14581 if (!(position >= selectionStart && position < selectionEnd)) {
14582 if (position >= selectionStart) {
14583 position += valueDelta;
14585 if (value.substring(position, position + component.value.length) === component.value) {
14586 tempValue += value.slice(valueOffset, position);// + value.slice(position + component.value.length);
14587 valueOffset = position + component.value.length;
14591 value = tempValue + value.slice(valueOffset);
14592 angular.forEach(value.split(''), function(chr) {
14593 if (maskPatternsCopy.length && maskPatternsCopy[0].test(chr)) {
14594 valueUnmasked += chr;
14595 maskPatternsCopy.shift();
14599 return valueUnmasked;
14602 function maskValue(unmaskedValue) {
14603 var valueMasked = '',
14604 maskCaretMapCopy = maskCaretMap.slice();
14606 angular.forEach(maskPlaceholder.split(''), function(chr, i) {
14607 if (unmaskedValue.length && i === maskCaretMapCopy[0]) {
14608 valueMasked += unmaskedValue.charAt(0) || '_';
14609 unmaskedValue = unmaskedValue.substr(1);
14610 maskCaretMapCopy.shift();
14613 valueMasked += chr;
14616 return valueMasked;
14619 function getPlaceholderChar(i) {
14620 var placeholder = angular.isDefined(attrs.uiMaskPlaceholder) ? attrs.uiMaskPlaceholder : attrs.placeholder,
14621 defaultPlaceholderChar;
14623 if (angular.isDefined(placeholder) && placeholder[i]) {
14624 return placeholder[i];
14626 defaultPlaceholderChar = angular.isDefined(attrs.uiMaskPlaceholderChar) && attrs.uiMaskPlaceholderChar ? attrs.uiMaskPlaceholderChar : '_';
14627 return (defaultPlaceholderChar.toLowerCase() === 'space') ? ' ' : defaultPlaceholderChar[0];
14631 /* Generate array of mask components that will be stripped from a masked value
14632 before processing to prevent mask components from being added to the unmasked value.
14633 E.g., a mask pattern of '+7 9999' won't have the 7 bleed into the unmasked value. */
14634 function getMaskComponents() {
14635 var maskPlaceholderChars = maskPlaceholder.split(''),
14636 maskPlaceholderCopy, components;
14638 /* maskCaretMap can have bad values if the input has the ui-mask attribute implemented as an obversable property, e.g. the demo page */
14639 if (maskCaretMap && !isNaN(maskCaretMap[0])) {
14640 /* Instead of trying to manipulate the RegEx based on the placeholder characters
14641 we can simply replace the placeholder characters based on the already built
14642 maskCaretMap to underscores and leave the original working RegEx to get the proper
14644 angular.forEach(maskCaretMap, function(value) {
14645 maskPlaceholderChars[value] = '_';
14648 maskPlaceholderCopy = maskPlaceholderChars.join('');
14649 components = maskPlaceholderCopy.replace(/[_]+/g, '_').split('_');
14650 components = components.filter(function(s) {
14654 /* need a string search offset in cases where the mask contains multiple identical components
14655 E.g., a mask of 99.99.99-999.99 */
14657 return components.map(function(c) {
14658 var componentPosition = maskPlaceholderCopy.indexOf(c, offset);
14659 offset = componentPosition + 1;
14662 position: componentPosition
14667 function processRawMask(mask) {
14668 var characterCount = 0;
14672 maskPlaceholder = '';
14674 if (angular.isString(mask)) {
14675 minRequiredLength = 0;
14677 var isOptional = false,
14678 numberOfOptionalCharacters = 0,
14679 splitMask = mask.split('');
14681 var inEscape = false;
14682 angular.forEach(splitMask, function(chr, i) {
14685 maskPlaceholder += chr;
14688 else if (linkOptions.escChar === chr) {
14691 else if (linkOptions.maskDefinitions[chr]) {
14692 maskCaretMap.push(characterCount);
14694 maskPlaceholder += getPlaceholderChar(i - numberOfOptionalCharacters);
14695 maskPatterns.push(linkOptions.maskDefinitions[chr]);
14699 minRequiredLength++;
14702 isOptional = false;
14704 else if (chr === '?') {
14706 numberOfOptionalCharacters++;
14709 maskPlaceholder += chr;
14714 // Caret position immediately following last position is valid.
14715 maskCaretMap.push(maskCaretMap.slice().pop() + 1);
14717 maskComponents = getMaskComponents();
14718 maskProcessed = maskCaretMap.length > 1 ? true : false;
14721 var prevValue = element.val();
14722 function blurHandler() {
14723 if (linkOptions.clearOnBlur || ((linkOptions.clearOnBlurPlaceholder) && (value.length === 0) && attrs.placeholder)) {
14724 oldCaretPosition = 0;
14725 oldSelectionLength = 0;
14726 if (!isValid || value.length === 0) {
14729 scope.$apply(function() {
14730 //only $setViewValue when not $pristine to avoid changing $pristine state.
14731 if (!ctrl.$pristine) {
14732 ctrl.$setViewValue('');
14737 //Check for different value and trigger change.
14738 if (value !== prevValue) {
14739 var currentVal = element.val();
14740 var isTemporarilyEmpty = value === '' && currentVal && angular.isDefined(attrs.uiMaskPlaceholderChar) && attrs.uiMaskPlaceholderChar === 'space';
14741 if(isTemporarilyEmpty) {
14744 triggerChangeEvent(element[0]);
14745 if(isTemporarilyEmpty) {
14746 element.val(currentVal);
14752 function triggerChangeEvent(element) {
14754 if (angular.isFunction(window.Event) && !element.fireEvent) {
14755 // modern browsers and Edge
14757 change = new Event('change', {
14763 //this is for certain mobile browsers that have the Event object
14764 //but don't support the Event constructor
14765 change = document.createEvent('HTMLEvents');
14766 change.initEvent('change', false, true);
14768 element.dispatchEvent(change);
14770 } else if ('createEvent' in document) {
14772 change = document.createEvent('HTMLEvents');
14773 change.initEvent('change', false, true);
14774 element.dispatchEvent(change);
14776 else if (element.fireEvent) {
14778 element.fireEvent('onchange');
14782 function mouseDownUpHandler(e) {
14783 if (e.type === 'mousedown') {
14784 element.bind('mouseout', mouseoutHandler);
14786 element.unbind('mouseout', mouseoutHandler);
14790 element.bind('mousedown mouseup', mouseDownUpHandler);
14792 function mouseoutHandler() {
14793 oldSelectionLength = getSelectionLength(this);
14794 element.unbind('mouseout', mouseoutHandler);
14797 function keydownHandler(e) {
14798 var isKeyBackspace = e.which === 8,
14799 caretPos = getCaretPosition(this) - 1 || 0, //value in keydown is pre change so bump caret position back to simulate post change
14800 isCtrlZ = e.which === 90 && e.ctrlKey; //ctrl+z pressed
14802 if (isKeyBackspace) {
14803 while(caretPos >= 0) {
14804 if (isValidCaretPosition(caretPos)) {
14805 //re-adjust the caret position.
14806 //Increment to account for the initial decrement to simulate post change caret position
14807 setCaretPosition(this, caretPos + 1);
14812 preventBackspace = caretPos === -1;
14816 // prevent IE bug - value should be returned to initial state
14818 e.preventDefault();
14822 function eventHandler(e) {
14824 // Allows more efficient minification
14825 var eventWhich = e.which,
14826 eventType = e.type;
14828 // Prevent shift and ctrl from mucking with old values
14829 if (eventWhich === 16 || eventWhich === 91) {
14833 var val = element.val(),
14836 valAltered = false,
14837 valUnmasked = unmaskValue(val),
14838 valUnmaskedOld = oldValueUnmasked,
14839 caretPos = getCaretPosition(this) || 0,
14840 caretPosOld = oldCaretPosition || 0,
14841 caretPosDelta = caretPos - caretPosOld,
14842 caretPosMin = maskCaretMap[0],
14843 caretPosMax = maskCaretMap[valUnmasked.length] || maskCaretMap.slice().shift(),
14844 selectionLenOld = oldSelectionLength || 0,
14845 isSelected = getSelectionLength(this) > 0,
14846 wasSelected = selectionLenOld > 0,
14847 // Case: Typing a character to overwrite a selection
14848 isAddition = (val.length > valOld.length) || (selectionLenOld && val.length > valOld.length - selectionLenOld),
14849 // Case: Delete and backspace behave identically on a selection
14850 isDeletion = (val.length < valOld.length) || (selectionLenOld && val.length === valOld.length - selectionLenOld),
14851 isSelection = (eventWhich >= 37 && eventWhich <= 40) && e.shiftKey, // Arrow key codes
14853 isKeyLeftArrow = eventWhich === 37,
14854 // Necessary due to "input" event not providing a key code
14855 isKeyBackspace = eventWhich === 8 || (eventType !== 'keyup' && isDeletion && (caretPosDelta === -1)),
14856 isKeyDelete = eventWhich === 46 || (eventType !== 'keyup' && isDeletion && (caretPosDelta === 0) && !wasSelected),
14857 // Handles cases where caret is moved and placed in front of invalid maskCaretMap position. Logic below
14858 // ensures that, on click or leftward caret placement, caret is moved leftward until directly right of
14859 // non-mask character. Also applied to click since users are (arguably) more likely to backspace
14860 // a character when clicking within a filled input.
14861 caretBumpBack = (isKeyLeftArrow || isKeyBackspace || eventType === 'click') && caretPos > caretPosMin;
14863 oldSelectionLength = getSelectionLength(this);
14865 // These events don't require any action
14866 if (isSelection || (isSelected && (eventType === 'click' || eventType === 'keyup' || eventType === 'focus'))) {
14870 if (isKeyBackspace && preventBackspace) {
14871 element.val(maskPlaceholder);
14872 // This shouldn't be needed but for some reason after aggressive backspacing the ctrl $viewValue is incorrect.
14873 // This keeps the $viewValue updated and correct.
14874 scope.$apply(function () {
14875 ctrl.$setViewValue(''); // $setViewValue should be run in angular context, otherwise the changes will be invisible to angular and user code.
14877 setCaretPosition(this, caretPosOld);
14881 // User attempted to delete but raw value was unaffected--correct this grievous offense
14882 if ((eventType === 'input') && isDeletion && !wasSelected && valUnmasked === valUnmaskedOld) {
14883 while (isKeyBackspace && caretPos > caretPosMin && !isValidCaretPosition(caretPos)) {
14886 while (isKeyDelete && caretPos < caretPosMax && maskCaretMap.indexOf(caretPos) === -1) {
14889 var charIndex = maskCaretMap.indexOf(caretPos);
14890 // Strip out non-mask character that user would have deleted if mask hadn't been in the way.
14891 valUnmasked = valUnmasked.substring(0, charIndex) + valUnmasked.substring(charIndex + 1);
14893 // If value has not changed, don't want to call $setViewValue, may be caused by IE raising input event due to placeholder
14894 if (valUnmasked !== valUnmaskedOld)
14899 valMasked = maskValue(valUnmasked);
14901 oldValue = valMasked;
14902 oldValueUnmasked = valUnmasked;
14904 //additional check to fix the problem where the viewValue is out of sync with the value of the element.
14905 //better fix for commit 2a83b5fb8312e71d220a497545f999fc82503bd9 (I think)
14906 if (!valAltered && val.length > valMasked.length)
14909 element.val(valMasked);
14911 //we need this check. What could happen if you don't have it is that you'll set the model value without the user
14912 //actually doing anything. Meaning, things like pristine and touched will be set.
14914 scope.$apply(function () {
14915 ctrl.$setViewValue(valMasked); // $setViewValue should be run in angular context, otherwise the changes will be invisible to angular and user code.
14919 // Caret Repositioning
14920 // Ensure that typing always places caret ahead of typed character in cases where the first char of
14921 // the input is a mask char and the caret is placed at the 0 position.
14922 if (isAddition && (caretPos <= caretPosMin)) {
14923 caretPos = caretPosMin + 1;
14926 if (caretBumpBack) {
14930 // Make sure caret is within min and max position limits
14931 caretPos = caretPos > caretPosMax ? caretPosMax : caretPos < caretPosMin ? caretPosMin : caretPos;
14933 // Scoot the caret back or forth until it's in a non-mask position and within min/max position limits
14934 while (!isValidCaretPosition(caretPos) && caretPos > caretPosMin && caretPos < caretPosMax) {
14935 caretPos += caretBumpBack ? -1 : 1;
14938 if ((caretBumpBack && caretPos < caretPosMax) || (isAddition && !isValidCaretPosition(caretPosOld))) {
14941 oldCaretPosition = caretPos;
14942 setCaretPosition(this, caretPos);
14945 function isValidCaretPosition(pos) {
14946 return maskCaretMap.indexOf(pos) > -1;
14949 function getCaretPosition(input) {
14952 if (input.selectionStart !== undefined) {
14953 return input.selectionStart;
14954 } else if (document.selection) {
14955 if (isFocused(element[0])) {
14958 var selection = document.selection.createRange();
14959 selection.moveStart('character', input.value ? -input.value.length : 0);
14960 return selection.text.length;
14966 function setCaretPosition(input, pos) {
14969 if (input.offsetWidth === 0 || input.offsetHeight === 0) {
14970 return; // Input's hidden
14972 if (input.setSelectionRange) {
14973 if (isFocused(element[0])) {
14975 input.setSelectionRange(pos, pos);
14978 else if (input.createTextRange) {
14980 var range = input.createTextRange();
14981 range.collapse(true);
14982 range.moveEnd('character', pos);
14983 range.moveStart('character', pos);
14988 function getSelectionLength(input) {
14991 if (input.selectionStart !== undefined) {
14992 return (input.selectionEnd - input.selectionStart);
14994 if (window.getSelection) {
14995 return (window.getSelection().toString().length);
14997 if (document.selection) {
14998 return (document.selection.createRange().text.length);
15005 .filter('b2bMultiSepartorHighlight', function($sce) {
15006 return function(text, searchText, searchSeperator) {
15007 var splitText = function(string) {
15008 if(angular.isDefined(searchSeperator)){
15009 if (string.indexOf(searchSeperator) > -1) {
15010 return string.split(searchSeperator);
15019 var newText = splitText(text);
15020 var newPhrase = splitText(searchText);
15021 if (angular.isArray(newPhrase)) {
15022 for (var i = 0; i < newText.length; i++) {
15024 text = newText[i].replace(new RegExp('(' + newPhrase[i] + ')', 'gi'),
15025 '<span class="b2b-search-hightlight">$1</span>');
15027 text = text + searchSeperator + ' ' + (newPhrase[i] ? newText[i].replace(new RegExp('(' + newPhrase[i] + ')', 'gi'),
15028 '<span class="b2b-search-hightlight">$1</span>') : newText[i]);
15032 text = text.replace(new RegExp('(' + searchText + ')', 'gi'),
15033 '<span class="b2b-search-hightlight">$1</span>');
15036 return $sce.trustAsHtml(text)
15040 .factory('b2bUserAgent', [function() {
15041 var _isMobile = function() {
15042 if(/Android/i.test(navigator.userAgent)){
15043 return /Mobile/i.test(navigator.userAgent);
15045 return /Android|webOS|iPhone|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent);
15049 var _notMobile = function() {
15050 if(/Android/i.test(navigator.userAgent)){
15051 return !/Mobile/i.test(navigator.userAgent);
15053 return !/Android|webOS|iPhone|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent);
15057 var _isIE = function() {
15058 return /msie|trident/i.test(navigator.userAgent);
15060 var _isFF = function() {
15061 return /Firefox/.test(navigator.userAgent);
15063 var _isChrome = function() {
15064 return /Chrome/.test(navigator.userAgent);
15066 var _isSafari = function() {
15067 return /Safari/.test(navigator.userAgent) && !/Chrome/.test(navigator.userAgent);
15071 isMobile: _isMobile,
15072 notMobile: _notMobile,
15075 isChrome: _isChrome,
15076 isSafari: _isSafari
15079 .run(['$document', 'b2bUserAgent', function($document, b2bUserAgent) {
15080 var html = $document.find('html').eq(0);
15081 if (b2bUserAgent.isIE()) {
15082 html.addClass('isIE');
15084 html.removeClass('isIE');
15090 String.prototype.toSnakeCase = function () {
15091 return this.replace(/([A-Z])/g, function ($1) {
15092 return "-" + $1.toLowerCase();
15095 var concat = function (character, times) {
15096 character = character || '';
15097 times = (!isNaN(times) && times) || 0;
15098 var finalChar = '';
15099 for (var i = 0; i < times; i++) {
15100 finalChar += character;
15105 // direction: true for left and false for right
15106 var pad = function (actualString, width, character, direction) {
15107 actualString = actualString || '';
15108 width = (!isNaN(width) && width) || 0;
15109 character = character || '';
15110 if (width > actualString.length) {
15112 return concat(character, (width - actualString.length)) + actualString;
15114 return actualString + concat(character, (width - actualString.length));
15117 return actualString;
15120 String.prototype.lPad = function (width, character) {
15121 return pad(this, width, character, true);
15124 String.prototype.rPad = function (width, character) {
15125 return pad(this, width, character, false);
15128 if (!Array.prototype.indexOf) {
15129 Array.prototype.indexOf = function (val) {
15130 for (var index = 0; index < this.length; index++) {
15131 if (this[index] === val) {
15139 if (!Array.prototype.regexIndexOf) {
15140 Object.defineProperty(Array.prototype, 'regexIndexOf', {
15142 value: function (regex, startIndex, loop) {
15143 startIndex = startIndex && startIndex > -1 ? startIndex : 0;
15144 for (var index = startIndex; index < this.length; index++) {
15145 if (this[index].toString().match(regex)) {
15150 for (var index = 0; index < startIndex; index++) {
15151 if (this[index].toString().match(regex)) {
15161 angular.module("b2bTemplate/audioPlayer/audioPlayer.html", []).run(["$templateCache", function($templateCache) {
15162 $templateCache.put("b2bTemplate/audioPlayer/audioPlayer.html",
15163 "<div class=\"b2b-audio\">\n" +
15164 " <audio preload=\"auto\">\n" +
15165 " <source ng-src=\"{{audio.mp3 | trustedAudioUrl}}\" type=\"audio/mp3\"></source>\n" +
15166 " <i>Your browser does not support the audio element.</i>\n" +
15169 " <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" +
15170 " <i class=\"icoControls-pointer\" ng-show='!isPlayInProgress'></i>\n" +
15171 " <i class=\"icoControls-pause\" ng-show='isPlayInProgress'></i>\n" +
15174 " <div class=\"seek-bar-container-wrapper\">\n" +
15175 " <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" +
15176 " <div class=\"timing-container\">\n" +
15177 " <span class=\"timing-container-left\">{{timeFormatter(audio.currentTime)}}</span>\n" +
15178 " <span class=\"timing-container-right\">{{timeFormatter(audio.duration)}}</span>\n" +
15179 " <div class=\"timing-container-spacer\"></div>\n" +
15183 " <b2b-flyout>\n" +
15184 " <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" +
15185 " <i class=\"icoControls-mutespeakers\" ng-show=\"audio.currentVolume === 0\"></i>\n" +
15186 " <i class=\"icoControls-volumedown\" ng-show=\"audio.currentVolume > 0 && audio.currentVolume <= 50\"></i>\n" +
15187 " <i class=\"icoControls-volumeup\" ng-show=\"audio.currentVolume > 50\"></i>\n" +
15190 " <b2b-flyout-content horizontal-placement=\"center\" flyout-style=\"width:70px; height:190px;\" vertical-placement=\"above\">\n" +
15191 " <div class=\"b2b-audio-popover text-center\">\n" +
15192 " <span>Max</span>\n" +
15193 " <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" +
15194 " <div class=\"min-label\">Min</div>\n" +
15196 " </b2b-flyout-content>\n" +
15197 " </b2b-flyout>\n" +
15201 angular.module("b2bTemplate/audioRecorder/audioRecorder.html", []).run(["$templateCache", function($templateCache) {
15202 $templateCache.put("b2bTemplate/audioRecorder/audioRecorder.html",
15203 "<div class=\"b2b-audio-recorder row\">\n" +
15204 " <div class=\"b2b-elapsed-time span11\">\n" +
15205 " <div ng-if=\"isRecording\">\n" +
15206 " <span style=\"padding-right: 25px;\">{{config.whileRecordingMessage}}</span>\n" +
15207 " <span>{{timeFormatter(elapsedTime)}}</span>\n" +
15209 " <span ng-if=\"!isRecording\">{{config.startRecordingMessage}}</span>\n" +
15211 " <div class=\"b2b-controls\" title=\"{{isRecording ? 'Stop' : 'REC'}}\" b2b-accessibility-click=\"13,32\" ng-click=\"toggleRecording()\" role=\"button\">\n" +
15212 " <i ng-if=\"isRecording\" class=\"icoControls-stop\" ></i>\n" +
15213 " <i ng-if=\"!isRecording\" class=\"icoControls-record\"></i>\n" +
15218 angular.module("b2bTemplate/backToTop/backToTop.html", []).run(["$templateCache", function($templateCache) {
15219 $templateCache.put("b2bTemplate/backToTop/backToTop.html",
15220 "<button class=\"btn-arrow b2b-backtotop-button\" type=\"button\" aria-label=\"Back to top\">\n" +
15221 " <div class=\"btn-secondary b2b-top-btn\">\n" +
15222 " <i class=\"icoControls-upPRIMARY\" role=\"img\"></i>\n" +
15228 angular.module("b2bTemplate/boardstrip/b2bAddBoard.html", []).run(["$templateCache", function($templateCache) {
15229 $templateCache.put("b2bTemplate/boardstrip/b2bAddBoard.html",
15230 "<div tabindex=\"0\" role=\"menuitem\" b2b-accessibility-click=\"13,32\" ng-click=\"addBoard()\" aria-label=\"Add Board\" class=\"boardstrip-item--add\">\n" +
15231 " <div class=\"centered\"><i aria-hidden=\"true\" class=\"icoControls-add-maximize\"></i> Add board</div>\n" +
15235 angular.module("b2bTemplate/boardstrip/b2bBoard.html", []).run(["$templateCache", function($templateCache) {
15236 $templateCache.put("b2bTemplate/boardstrip/b2bBoard.html",
15237 "<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" +
15238 " <div ng-transclude></div>\n" +
15239 " <div class=\"board-caret\" ng-if=\"getCurrentIndex()===boardIndex\">\n" +
15240 " <div class=\"board-caret-indicator\"></div>\n" +
15241 " <div class=\"board-caret-arrow-up\"></div>\n" +
15246 angular.module("b2bTemplate/boardstrip/b2bBoardstrip.html", []).run(["$templateCache", function($templateCache) {
15247 $templateCache.put("b2bTemplate/boardstrip/b2bBoardstrip.html",
15248 "<div class=\"b2b-boardstrip\">\n" +
15249 " <div class=\"boardstrip-reel\" role=\"menu\">\n" +
15250 " <div class=\"prev-items\">\n" +
15251 " <!-- <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" +
15252 " <button class=\"btn-arrow arrow\" b2b-accessibility-click=\"13,32\" ng-click=\"prevBoard()\" ng-disabled=\"!isPrevBoard()\">\n" +
15253 " <div class=\"btn btn-small btn-alt\"><i class=\"icon-primary-left\"></i>\n" +
15255 " <span class=\"offscreen-text\">Previous boards</span>\n" +
15258 " <div b2b-add-board on-add-board=\"onAddBoard()\"></div>\n" +
15259 " <div class=\"board-viewport\"><ul role=\"menu\" class=\"boardstrip-container\" ng-transclude></ul></div>\n" +
15260 " <div class=\"next-items\">\n" +
15261 " <!-- <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" +
15262 " <button class=\"btn-arrow arrow\" b2b-accessibility-click=\"13,32\" ng-click=\"nextBoard()\" ng-disabled=\"!isNextBoard()\">\n" +
15263 " <div class=\"btn btn-small btn-alt\"><i class=\"icon-primary-right\"></i>\n" +
15265 " <span class=\"offscreen-text\">Next boards</span>\n" +
15273 angular.module("b2bTemplate/calendar/datepicker-popup.html", []).run(["$templateCache", function($templateCache) {
15274 $templateCache.put("b2bTemplate/calendar/datepicker-popup.html",
15275 "<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" +
15276 " <div class=\"datepicker-days\" style=\"display: block;\">\n" +
15277 " <div ng-repeat=\"header in headers\" class=\"text-left\" style=\"width: 100%;\" b2b-append-element=\"header\"></div>\n" +
15278 " <table class=\"table-condensed\">\n" +
15281 " <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" +
15282 " <th id=\"month\" tabindex=\"-1\" aria-label=\"{{title}}\" class=\"datepicker-switch\" colspan=\"{{rows[0].length - 2}}\">{{title}}</th>\n" +
15283 " <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" +
15285 " <tr ng-show=\"labels.length > 0\">\n" +
15286 " <th id=\"{{label.post}}\" class=\"dow\" ng-repeat=\"label in labels\" aria-hidden=\"true\"><span aria-hidden=\"true\">{{label.pre}}</span></th>\n" +
15290 " <tr ng-repeat=\"row in rows\">\n" +
15291 " <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" +
15292 " <div aria-hidden=\"true\" tabindex=\"-1\" class=\"show-date\" ng-if=\"!(dt.oldMonth || dt.nextMonth)\">{{dt.label}}</div>{{(dt.focusable && dt.date | date : 'EEEE, MMMM d yyyy') || ''}}{{(dt.focusable && dt.dueDate && '. Bill-due-date.. ') || ''}}{{(dt.focusable && dt.pastDue && legendMessage) || ''}}</td>\n" +
15296 " <tr ng-repeat=\"footer in footers\">\n" +
15297 " <th colspan=\"7\" class=\"text-left\" style=\"width: 278px;\" b2b-append-element=\"footer\"></th>\n" +
15305 angular.module("b2bTemplate/calendar/datepicker.html", []).run(["$templateCache", function($templateCache) {
15306 $templateCache.put("b2bTemplate/calendar/datepicker.html",
15308 " <span class=\"icon-primary-calendar span12\" ng-class=\"{'disabled': ngDisabled}\" ng-transclude></span>\n" +
15312 angular.module("b2bTemplate/coachmark/coachmark.html", []).run(["$templateCache", function($templateCache) {
15313 $templateCache.put("b2bTemplate/coachmark/coachmark.html",
15314 "<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" +
15315 " <i class=\"b2b-coachmark-caret\"></i>\n" +
15316 " <div class=\"b2b-coachmark-header\">\n" +
15317 " <div class=\"b2b-coachmark-countlabel\"><span ng-if=\"coachmarkIndex !== 0\">{{coachmarkIndex}} of {{(coachmarks.length-1)}}<span></div>\n" +
15318 " <div class=\"corner-button\">\n" +
15319 " <button type=\"button\" ng-focus=\"closeButtonFocus()\" class=\"close\" title=\"close\" aria-label=\"Close\" ng-click=\"closeCoachmark()\"></button>\n" +
15322 " <div class=\"b2b-coachmark-content\"> \n" +
15323 " <i class=\"icon-misc-dimmer\"></i>\n" +
15324 " <div class=\"b2b-coachmark-content-header\"><span class=\"offscreen-text\">{{currentCoachmark.offscreenText}}</span>{{currentCoachmark.contentHeader}}</div>\n" +
15325 " <div class=\"b2b-coachmark-description\">{{currentCoachmark.content}}</div>\n" +
15326 " <div class=\"b2b-coachmark-btn-group\">\n" +
15327 " <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" +
15328 " <button class=\"btn btn-alt\" ng-if=\"currentCoachmark.buttonLabel !== '' && currentCoachmark.buttonLabel !== undefined\" ng-click=\"actionCoachmark(currentCoachmark.buttonLabel)\">{{currentCoachmark.buttonLabel}}</button>\n" +
15334 angular.module("b2bTemplate/dropdowns/b2bDropdownDesktop.html", []).run(["$templateCache", function($templateCache) {
15335 $templateCache.put("b2bTemplate/dropdowns/b2bDropdownDesktop.html",
15336 "<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" +
15337 " <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" +
15338 " <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" +
15339 " <div ng-class=\"{'selectWrapper': (isInputDropdown), 'moduleWrapper': (!isInputDropdown)}\">\n" +
15340 " <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" +
15341 " <ul class=\"module-optinalcta\" ng-if=\"toggleFlag && optionalCta\" tabindex=\"-1\" aria-hidden=\"false\">\n" +
15342 " <li class=\"module-list-item\" tabindex=\"-1\" role=\"menuitem\" value=\"\" aria-label=\"Optinal CTA\" aria-selected=\"true\">\n" +
15343 " <span class=\"module-data\" b2b-append-element=\"optionalCta\"></span>\n" +
15347 "<i class=\"icon-primary-down\" aria-hidden=\"true\"></i>\n" +
15351 angular.module("b2bTemplate/dropdowns/b2bDropdownGroupDesktop.html", []).run(["$templateCache", function($templateCache) {
15352 $templateCache.put("b2bTemplate/dropdowns/b2bDropdownGroupDesktop.html",
15353 "<li b2b-dropdown-group-desktop class=\"optgroup-wrapper\">{{groupHeader}}\n" +
15354 " <ul class=\"optgroup\" role=\"group\" aria-label=\"{{groupHeader}}\"></ul>\n" +
15358 angular.module("b2bTemplate/dropdowns/b2bDropdownListDesktop.html", []).run(["$templateCache", function($templateCache) {
15359 $templateCache.put("b2bTemplate/dropdowns/b2bDropdownListDesktop.html",
15360 "<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>");
15363 angular.module("b2bTemplate/fileUpload/fileUpload.html", []).run(["$templateCache", function($templateCache) {
15364 $templateCache.put("b2bTemplate/fileUpload/fileUpload.html",
15365 "<label class=\"b2b-file-container\">\n" +
15366 " <span class=\"b2b-upload-link\" ng-transclude></span>\n" +
15367 " <input type=\"file\" b2b-file-change>\n" +
15371 angular.module("b2bTemplate/flyout/flyout.html", []).run(["$templateCache", function($templateCache) {
15372 $templateCache.put("b2bTemplate/flyout/flyout.html",
15373 "<span class=\"b2b-flyout\" b2b-flyout-trap-focus-inside>\n" +
15374 " <span ng-transclude></span>\n" +
15378 angular.module("b2bTemplate/flyout/flyoutContent.html", []).run(["$templateCache", function($templateCache) {
15379 $templateCache.put("b2bTemplate/flyout/flyoutContent.html",
15380 "<div class=\"b2b-flyout-container\" aria-live=\"polite\" aria-atomic=\"false\" ng-class=\"{'b2b-flyout-left':horizontalPlacement==='left',\n" +
15381 " 'b2b-flyout-center':horizontalPlacement==='center', \n" +
15382 " 'b2b-flyout-right':horizontalPlacement==='right',\n" +
15383 " 'b2b-flyout-centerLeft':horizontalPlacement==='centerLeft',\n" +
15384 " 'b2b-flyout-centerRight':horizontalPlacement==='centerRight', \n" +
15385 " 'b2b-flyout-above':verticalPlacement==='above', \n" +
15386 " 'b2b-flyout-below':verticalPlacement==='below',\n" +
15387 " 'open-flyout': openFlyout,\n" +
15388 " 'b2b-close-flyout': !openFlyout}\">\n" +
15389 " <i class=\"b2b-flyout-caret\" ng-class=\"{'open-flyout': openFlyout, \n" +
15390 " 'b2b-flyout-caret-above':verticalPlacement==='above',\n" +
15391 " 'b2b-flyout-caret-below':verticalPlacement==='below'}\"></i>\n" +
15392 " <span ng-transclude></span>\n" +
15396 angular.module("b2bTemplate/footer/footer_column_switch_tpl.html", []).run(["$templateCache", function($templateCache) {
15397 $templateCache.put("b2bTemplate/footer/footer_column_switch_tpl.html",
15398 "<div class=\"footer-columns \" ng-class=\"{'five-column':footerColumns===5, 'four-column':footerColumns===4, 'three-column':footerColumns===3}\" ng-repeat=\"item in footerLinkItems\">\n" +
15399 " <h3 class=\"b2b-footer-header\">{{item.categoryName}}</h3>\n" +
15401 " <li ng-repeat=\"i in item.values\">\n" +
15402 " <a href=\"{{i.href}}\" title=\"{{item.categoryName}} - {{i.displayLink}}\">{{i.displayLink}}</a> \n" +
15408 "<div ng-transclude></div>\n" +
15412 angular.module("b2bTemplate/horizontalTable/horizontalTable.html", []).run(["$templateCache", function($templateCache) {
15413 $templateCache.put("b2bTemplate/horizontalTable/horizontalTable.html",
15414 "<div class=\"b2b-horizontal-table\">\n" +
15415 " <div class=\"b2b-horizontal-table-arrows row span12\">\n" +
15416 " <div class=\"span4 b2b-prev-link\">\n" +
15417 " <a href=\"javascript:void(0)\" ng-click=\"moveViewportLeft()\" ddh-accessibility-click=\"13,32\" ng-if=\"!disableLeft\">Previous</a>\n" +
15418 " <span ng-if=\"disableLeft\" class=\"b2b-disabled-text\">Previous</span>\n" +
15421 " <span class=\"span5 b2b-horizontal-table-column-info\" aria-live=\"polite\" tabindex=\"-1\">\n" +
15422 " Showing {{countDisplayText}} {{getColumnSet()[0]+1}}-{{getColumnSet()[1]+1}} of {{numOfCols}} columns\n" +
15425 " <div ng-if=\"legendContent\" class=\"span2 b2b-horizontal-table-legend\">\n" +
15426 " | <b2b-flyout>\n" +
15427 " <div tabindex=\"0\" role=\"button\" aria-haspopup=\"true\" b2b-flyout-toggler b2b-accessibility-click=\"13,32\" aria-expanded=\"{{flyoutOpened ? 'true' : 'false'}}\">\n" +
15429 " <i class=\"icoControls-down\" role=\"img\"></i>\n" +
15431 " <b2b-flyout-content horizontal-placement=\"center\" vertical-placement=\"below\">\n" +
15432 " <div ng-bind-html=\"legendContent\"></div>\n" +
15433 " </b2b-flyout-content>\n" +
15434 " </b2b-flyout>\n" +
15437 " <div class=\"span3 text-right b2b-next-link\">\n" +
15438 " <a href=\"javascript:void(0)\" ng-click=\"moveViewportRight()\" ddh-accessibility-click=\"13,32\" ng-if=\"!disableRight\">Next</a>\n" +
15439 " <span ng-if=\"disableRight\" class=\"b2b-disabled-text\">Next</span>\n" +
15442 " <div class=\"b2b-horizontal-table-inner-container\">\n" +
15443 " <span ng-transclude></span>\n" +
15448 angular.module("b2bTemplate/hourPicker/b2bHourpicker.html", []).run(["$templateCache", function($templateCache) {
15449 $templateCache.put("b2bTemplate/hourPicker/b2bHourpicker.html",
15450 "<div class=\"hp-container\">\n" +
15451 " <div class=\"hp-selected\">\n" +
15452 " <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" +
15454 " <div b2b-hourpicker-panel></div>\n" +
15458 angular.module("b2bTemplate/hourPicker/b2bHourpickerPanel.html", []).run(["$templateCache", function($templateCache) {
15459 $templateCache.put("b2bTemplate/hourPicker/b2bHourpickerPanel.html",
15460 "<form name=\"{{'hourpickerForm' + $id}}\">\n" +
15461 " <div class=\"hp-checkbox\" role=\"group\">\n" +
15462 " <label class=\"checkbox\" for=\"checkbox_{{dayOption.title}}_{{$id}}\" ng-repeat=\"dayOption in hourpicker.dayOptions\">\n" +
15463 " <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" +
15466 " <div class=\"row hp-dropdowns\">\n" +
15467 " <div class=\"span4\">\n" +
15468 " <label for=\"{{'hourpickerStartTime' + $id}}\">From</label>\n" +
15469 " <select id=\"{{'hourpickerStartTime' + $parent.$id}}\" b2b-dropdown type=\"\" ng-model=\"hourpickerPanelValue.startTime\">\n" +
15470 " <option b2b-dropdown-list value=\"\">From</option>\n" +
15471 " <option b2b-dropdown-list option-repeat=\"startTimeOption in hourpicker.startTimeOptions\" value=\"{{startTimeOption}}\">{{startTimeOption}}</option>\n" +
15474 " <div class=\"span8 radio-buttons\" role=\"radiogroup\" aria-label=\"Select AM or PM\">\n" +
15475 " <label class=\"radio\" for=\"hourpickerStartMeridiem_AM_{{$id}}\">\n" +
15476 " <input type=\"radio\" id=\"hourpickerStartMeridiem_AM_{{$id}}\" name=\"{{'hourpickerStartMeridiem' + $id}}\" value=\"am\" ng-model=\"hourpickerPanelValue.startMeridiem\" /><i class=\"skin\"></i><span>AM</span>\n" +
15478 " <label class=\"radio\" for=\"hourpickerStartMeridiem_PM_{{$id}}\">\n" +
15479 " <input type=\"radio\" id=\"hourpickerStartMeridiem_PM_{{$id}}\" name=\"{{'hourpickerStartMeridiem' + $id}}\" value=\"pm\" ng-model=\"hourpickerPanelValue.startMeridiem\" /><i class=\"skin\"></i><span>PM</span>\n" +
15483 " <div class=\"row hp-dropdowns\">\n" +
15484 " <div class=\"span4\">\n" +
15485 " <label for=\"{{'hourpickerEndTime' + $id}}\">To</label>\n" +
15486 " <select id=\"{{'hourpickerEndTime' + $parent.$id}}\" b2b-dropdown ng-model=\"hourpickerPanelValue.endTime\">\n" +
15487 " <option b2b-dropdown-list value=\"\">To</option>\n" +
15488 " <option b2b-dropdown-list option-repeat=\"endTimeOption in hourpicker.endTimeOptions\" value=\"{{endTimeOption}}\">{{endTimeOption}}</option>\n" +
15491 " <div class=\"span8 radio-buttons\" role=\"radiogroup\" aria-label=\"Select AM or PM\">\n" +
15492 " <label class=\"radio\" for=\"hourpickerEndMeridiem_AM_{{$id}}\">\n" +
15493 " <input type=\"radio\" id=\"hourpickerEndMeridiem_AM_{{$id}}\" name=\"{{'hourpickerEndMeridiem' + $id}}\" value=\"am\" ng-model=\"hourpickerPanelValue.endMeridiem\" /><i class=\"skin\"></i><span>AM</span>\n" +
15495 " <label class=\"radio\" for=\"hourpickerEndMeridiem_PM_{{$id}}\">\n" +
15496 " <input type=\"radio\" id=\"hourpickerEndMeridiem_PM_{{$id}}\" name=\"{{'hourpickerEndMeridiem' + $id}}\" value=\"pm\" ng-model=\"hourpickerPanelValue.endMeridiem\" /><i class=\"skin\"></i><span>PM</span>\n" +
15500 " <div class=\"row hp-buttons\">\n" +
15501 " <div class=\"span12\">\n" +
15502 " <div style=\"float:right\">\n" +
15503 " <button class=\"btn btn-secondary btn-small\" ng-click=\"resetHourpickerPanelValue()\">Clear</button>\n" +
15504 " <button class=\"btn btn-alt btn-small\" ng-disabled = \"disableAddBtn\" ng-click=\"updateHourpickerValue()\">{{hourpicker.editMode > -1 ? 'Update' : 'Add'}}</button>\n" +
15511 angular.module("b2bTemplate/hourPicker/b2bHourpickerValue.html", []).run(["$templateCache", function($templateCache) {
15512 $templateCache.put("b2bTemplate/hourPicker/b2bHourpickerValue.html",
15513 "<div class=\"selected-days\">\n" +
15514 " <span class=\"day\">{{hourpickerValue.days}} {{hourpickerValue.startTime}} {{hourpickerValue.startMeridiem}} - {{hourpickerValue.endTime}} {{hourpickerValue.endMeridiem}}</span>\n" +
15515 " <span style=\"float:right\">\n" +
15516 " <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" +
15517 " <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" +
15519 " <div style=\"clear:both\"></div>\n" +
15523 angular.module("b2bTemplate/leftNavigation/leftNavigation.html", []).run(["$templateCache", function($templateCache) {
15524 $templateCache.put("b2bTemplate/leftNavigation/leftNavigation.html",
15525 "<div class=\"b2b-nav-menu\">\n" +
15526 " <div class=\"b2b-subnav-container\">\n" +
15527 " <ul class=\"b2b-subnav-content\">\n" +
15528 " <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" +
15529 " <ul ng-class=\"{expand: idx==$index}\">\n" +
15530 " <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" +
15538 angular.module("b2bTemplate/listbox/listbox.html", []).run(["$templateCache", function($templateCache) {
15539 $templateCache.put("b2bTemplate/listbox/listbox.html",
15540 "<div class=\"b2b-list-box\" tabindex=\"0\" role=\"listbox\" ng-transclude>\n" +
15544 angular.module("b2bTemplate/modalsAndAlerts/b2b-backdrop.html", []).run(["$templateCache", function($templateCache) {
15545 $templateCache.put("b2bTemplate/modalsAndAlerts/b2b-backdrop.html",
15546 "<div class=\"b2b-modal-backdrop fade in hidden-by-modal\" aria-hidden=\"true\" tabindex=\"-1\"></div>");
15549 angular.module("b2bTemplate/modalsAndAlerts/b2b-window.html", []).run(["$templateCache", function($templateCache) {
15550 $templateCache.put("b2bTemplate/modalsAndAlerts/b2b-window.html",
15551 "<div class=\"modalwrapper active {{windowClass}}\" ng-class=\"{'modal-landscape': isModalLandscape}\" role=\"{{isNotifDialog?'alertdialog':'dialog'}}\" tabindex=\"-1\" aria-labelledby=\"{{title}}\" aria-describedby=\"{{content}}\">\n" +
15552 " <div class=\"modal fade in {{sizeClass}}\" ng-transclude></div>\n" +
15556 angular.module("b2bTemplate/monthSelector/monthSelector-popup.html", []).run(["$templateCache", function($templateCache) {
15557 $templateCache.put("b2bTemplate/monthSelector/monthSelector-popup.html",
15558 "<div id=\"monthselector{{$id}}\" class=\"datepicker dropdown-menu monthselector\" \n" +
15559 " ng-class=\"{'datepicker-dropdown datepicker-orient-top': !inline, 'datepicker-orient-left': !inline && orientation === 'left', 'datepicker-orient-right': !inline && orientation === 'right'}\" \n" +
15560 " ng-style=\"{position: inline && 'relative', 'z-index': inline && '0', top: !inline && position.top + 'px' || 0, left: !inline && position.left + 'px'}\" \n" +
15561 " style=\"display: block;\" aria-hidden=\"false\" role=\"dialog presentation\" tabindex=\"-1\">\n" +
15562 " <div class=\"datepicker-days\" style=\"display: block;\">\n" +
15563 " <span class=\"offscreen-text\" aria-live=\"polite\" aria-atomic=\"true\">{{title}} displaying</span>\n" +
15564 " <table class=\"table-condensed\" role=\"grid\">\n" +
15566 " <tr ng-repeat=\"header in headers\">\n" +
15567 " <th colspan=\"7\" class=\"text-left\" style=\"width: 278px;\" b2b-append-element=\"header\"></th>\n" +
15570 " <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" +
15571 " <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" +
15572 " <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" +
15574 " <tr ng-show=\"labels.length > 0\">\n" +
15575 " <th id=\"{{label.post}}\" class=\"dow\" ng-repeat=\"label in labels\" aria-hidden=\"true\"><span aria-hidden=\"true\">{{label.pre}}</span></th>\n" +
15578 " <tbody b2b-key type=\"table\" columns=\"4\" >\n" +
15579 " <tr ng-repeat=\"row in rows\">\n" +
15580 " <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" +
15581 " <div aria-hidden=\"true\" tabindex=\"-1\" class=\"show-date\" ng-if=\"!(dt.oldMonth || dt.nextMonth)\">{{dt.label | limitTo: 3}}</div>\n" +
15586 " <tr ng-repeat=\"footer in footers\">\n" +
15587 " <th colspan=\"7\" class=\"text-left\" b2b-append-element=\"footer\"></th>\n" +
15595 angular.module("b2bTemplate/monthSelector/monthSelector.html", []).run(["$templateCache", function($templateCache) {
15596 $templateCache.put("b2bTemplate/monthSelector/monthSelector.html",
15598 " <span class=\"icon-primary-calendar span12\" ng-class=\"{'disabled': ngDisabled}\" ng-transclude></span>\n" +
15602 angular.module("b2bTemplate/monthSelector/monthSelectorLink.html", []).run(["$templateCache", function($templateCache) {
15603 $templateCache.put("b2bTemplate/monthSelector/monthSelectorLink.html",
15605 " <span class=\"span12\" ng-transclude></span>\n" +
15609 angular.module("b2bTemplate/pagination/b2b-pagination.html", []).run(["$templateCache", function($templateCache) {
15610 $templateCache.put("b2bTemplate/pagination/b2b-pagination.html",
15611 "<div class=\"b2b-pager\">\n" +
15612 " <div ng-if=\"notMobile && totalPages > 1\">\n" +
15613 " <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" +
15614 " <i class=\"icon-primary-left\"></i>\n" +
15616 " <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" +
15617 " 1<span class=\"offscreen-text\" ng-if=\"currentPage === 1\"> is selected</span>\n" +
15620 " <span class=\"b2b-pager__item\" ng-if=\"totalPages > 10 && currentPage > 6\">...</span>\n" +
15622 " <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" +
15623 " {{page}}<span class=\"offscreen-text\" ng-if=\"checkSelectedPage(page)\"> is selected</span>\n" +
15626 " <span class=\"b2b-pager__item\" ng-if=\"totalPages > 10 && currentPage <= (totalPages - boundary)\">...</span>\n" +
15628 " <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" +
15629 " {{totalPages}}<span class=\"offscreen-text\" ng-if=\"checkSelectedPage(page)\"> is selected</span>\n" +
15633 " <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" +
15634 " <i class=\"icon-primary-right\"></i>\n" +
15637 " <div class=\"fieldLabel b2b-go-to-page\" ng-class=\"{'b2b-go-to-page-inline' : inputClass !== undefined }\" ng-show=\"totalPages > 20\"> \n" +
15638 " <label for=\"{{inputId}}\">Go to Page:</label>\n" +
15639 " <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" +
15640 " <button class=\"btn-arrow\" ng-click=\"gotoBtnClick()\" ng-disabled=\"$parent.gotoPage !== 0 || $parent.gotoPage !== undefined\" aria-label=\"Go to\">\n" +
15641 " <div class=\"btn btn-small btn-secondary\">\n" +
15642 " <i class=\"icon-primary-right\"></i>\n" +
15647 " <div ng-if=\"isMobile && totalPages > 1\" ng-swipe-right=\"prev()\" ng-swipe-left=\"next()\" ng-class=\"isMobile ? 'b2b-mobile-view': '' \">\n" +
15648 " <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" +
15654 angular.module("b2bTemplate/paneSelector/paneSelector.html", []).run(["$templateCache", function($templateCache) {
15655 $templateCache.put("b2bTemplate/paneSelector/paneSelector.html",
15656 "<div class=\"panes\" ng-transclude></div>");
15659 angular.module("b2bTemplate/paneSelector/paneSelectorPane.html", []).run(["$templateCache", function($templateCache) {
15660 $templateCache.put("b2bTemplate/paneSelector/paneSelectorPane.html",
15661 "<div class=\"pane-block\" ng-transclude></div>");
15664 angular.module("b2bTemplate/profileCard/profileCard-addUser.html", []).run(["$templateCache", function($templateCache) {
15665 $templateCache.put("b2bTemplate/profileCard/profileCard-addUser.html",
15666 "<div class=\"span3 b2b-profile-card b2b-add-user\">\n" +
15667 " <div class=\"atcenter\">\n" +
15668 " <div class=\"circle\"><i class=\"icon-primary-add-maximize\"></i></div>\n" +
15669 " <div>Create new user</div>\n" +
15674 angular.module("b2bTemplate/profileCard/profileCard.html", []).run(["$templateCache", function($templateCache) {
15675 $templateCache.put("b2bTemplate/profileCard/profileCard.html",
15676 "<div class=\"span3 b2b-profile-card\">\n" +
15677 " <div class=\"top-block\">\n" +
15678 " <div class=\"profile-image\">\n" +
15679 " <img ng-if=\"image\" profile-name=\"{{profile.name}}\" ng-src=\"{{profile.img}}\" alt=\"{{profile.name}}\">\n" +
15680 " <span ng-if=\"!image\" class=\"default-img\">{{initials}}</span>\n" +
15682 " <h4 class=\"name\">{{profile.name}}</h4>\n" +
15684 " <p class=\"status\">\n" +
15685 " <span class=\"status-icon\" ng-class=\"{'status-green':colorIcon==='green','status-red':colorIcon==='red','status-blue':colorIcon==='blue','status-yellow':colorIcon==='yellow'}\"> \n" +
15687 " <span>{{profile.state}}<span ng-if=\"badge\" class=\"status-badge\">Admin</span></span>\n" +
15691 " <div class=\"bottom-block\">\n" +
15692 " <div class=\"profile-details\">\n" +
15693 " <label>Username</label>\n" +
15694 " <div ng-if=\"shouldClip(profile.userName)\" ng-mouseover=\"showUserNameTooltip = true;\" ng-mouseleave=\"showUserNameTooltip = false;\">\n" +
15695 " <div ng-if=\"shouldClip(profile.userName)\" class=\"tooltip\" b2b-tooltip show-tooltip=\"showUserNameTooltip\">\n" +
15696 " {{profile.userName.slice(0, 25)+'...'}}\n" +
15697 " <div class=\"arrow\"></div>\n" +
15698 " <div class=\"tooltip-wrapper\" role=\"tooltip\" aria-live=\"polite\" aria-atomic=\"false\" style=\"z-index:1111\">\n" +
15699 " <div class=\"tooltip-size-control\">\n" +
15700 " <div class=\"helpertext\" tabindex=\"-1\" role=\"tooltip\">\n" +
15701 " {{profile.userName}}\n" +
15707 " <div ng-if=\"!shouldClip(profile.userName)\">\n" +
15708 " {{profile.userName}}\n" +
15710 " <label>Email</label>\n" +
15711 " <div ng-if=\"shouldClip(profile.email)\" ng-mouseover=\"showEmailTooltip = true;\" ng-mouseleave=\"showEmailTooltip = false;\">\n" +
15712 " <div ng-if=\"shouldClip(profile.email)\" class=\"tooltip\" data-placement=\"bottom\" b2b-tooltip show-tooltip=\"showEmailTooltip\">\n" +
15713 " {{profile.email.slice(0, 25)+'...'}}\n" +
15714 " <div class=\"arrow\"></div>\n" +
15715 " <div class=\"tooltip-wrapper\" role=\"tooltip\" aria-live=\"polite\" aria-atomic=\"false\" style=\"z-index:1111\">\n" +
15716 " <div class=\"tooltip-size-control\">\n" +
15717 " <div class=\"helpertext\" tabindex=\"-1\" role=\"tooltip\">\n" +
15718 " {{profile.email}}\n" +
15724 " <div ng-if=\"!shouldClip(profile.email)\">\n" +
15725 " {{profile.email}}\n" +
15727 " <label>Role</label>\n" +
15728 " <div ng-if=\"shouldClip(profile.role)\" ng-mouseover=\"showRoleTooltip = true;\" ng-mouseleave=\"showRoleTooltip = false;\">\n" +
15729 " <div ng-if=\"shouldClip(profile.role)\" class=\"tooltip\" b2b-tooltip show-tooltip=\"showRoleTooltip\">\n" +
15730 " {{profile.role.slice(0, 25)+'...'}}\n" +
15731 " <div class=\"tooltip-wrapper\" role=\"tooltip\" aria-live=\"polite\" aria-atomic=\"false\" style=\"z-index:1111\">\n" +
15732 " <div class=\"tooltip-size-control\">\n" +
15733 " <div class=\"helpertext\" tabindex=\"-1\" role=\"tooltip\">\n" +
15734 " {{profile.role}}\n" +
15740 " <div ng-if=\"!shouldClip(profile.role)\">\n" +
15741 " {{profile.role}}\n" +
15743 " <label>Last login</label>\n" +
15744 " <div ng-if=\"shouldClip(profile.lastLogin)\" ng-mouseover=\"showLastLoginTooltip = true;\" ng-mouseleave=\"showLastLoginTooltip = false;\">\n" +
15745 " <div ng-if=\"shouldClip(profile.lastLogin)\" class=\"tooltip\" b2b-tooltip show-tooltip=\"showLastLoginTooltip\">\n" +
15746 " {{profile.lastLogin.slice(0, 25)+'...'}}\n" +
15747 " <div class=\"tooltip-wrapper\" role=\"tooltip\" aria-live=\"polite\" aria-atomic=\"false\" style=\"z-index:1111\">\n" +
15748 " <div class=\"tooltip-size-control\">\n" +
15749 " <div class=\"helpertext\" tabindex=\"-1\" role=\"tooltip\">\n" +
15750 " {{profile.lastLogin}}\n" +
15756 " <div ng-if=\"!shouldClip(profile.lastLogin)\">\n" +
15757 " {{profile.lastLogin}}\n" +
15764 angular.module("b2bTemplate/searchField/searchField.html", []).run(["$templateCache", function($templateCache) {
15765 $templateCache.put("b2bTemplate/searchField/searchField.html",
15766 "<div class=\"search-bar\">\n" +
15767 " <div class='input-container' ng-blur=\"blurInput()\">\n" +
15768 " <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" +
15769 " <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" +
15771 " <div class=\"search-suggestion-wrapper\" ng-if=\"filterList.length >=0\" ng-show=\"showListFlag\">\n" +
15772 " <ul class=\"search-suggestion-list\" role=\"listbox\"> \n" +
15773 " <li class=\"no-result\" ng-if=\"filterList.length == 0 && configObj.display\">{{configObj.resultText}}</li>\n" +
15774 " <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" +
15775 " {{item.title}} \n" +
15782 angular.module("b2bTemplate/seekBar/seekBar.html", []).run(["$templateCache", function($templateCache) {
15783 $templateCache.put("b2bTemplate/seekBar/seekBar.html",
15784 "<div class=\"b2b-seek-bar-container\" ng-class=\"{vertical:verticalSeekBar}\">\n" +
15785 " <div class=\"b2b-seek-bar-track-container\">\n" +
15786 " <div class=\"b2b-seek-bar-track\"></div>\n" +
15787 " <div class=\"b2b-seek-bar-track-fill\"></div>\n" +
15789 " <div class=\"b2b-seek-bar-knob-container\" role=\"menu\" >\n" +
15790 " <div class=\"b2b-seek-bar-knob\" tabindex=\"0\" role=\"menuitem\"></div>\n" +
15795 angular.module("b2bTemplate/slider/slider.html", []).run(["$templateCache", function($templateCache) {
15796 $templateCache.put("b2bTemplate/slider/slider.html",
15797 "<div class=\"b2b-slider-container\" ng-class=\"{'vertical':verticalSlider}\">\n" +
15798 " <div class=\"slider-track-container\">\n" +
15799 " <div class=\"slider-track\"></div>\n" +
15800 " <div class=\"slider-track-fill\" ng-style=\"{backgroundColor:trackFillColor}\"></div>\n" +
15802 " <div class=\"slider-knob-container\" ng-class=\"{'slider-knob-hidden':hideKnob}\">\n" +
15803 " <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" +
15808 angular.module("b2bTemplate/spinButton/spinButton.html", []).run(["$templateCache", function($templateCache) {
15809 $templateCache.put("b2bTemplate/spinButton/spinButton.html",
15810 "<div class=\"btn-group btn-spinbutton-toggle\" ng-class=\"{'disabled': disabledFlag}\">\n" +
15811 " <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" +
15812 " <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" +
15813 " <button type=\"button\" tabindex=\"{{isMobile?'0':'-1'}}\" aria-hidden=\"{{notMobile}}\" class=\"btn btn-secondary btn-next icon-primary-add-maximize\" ng-click=\"plus();focusInputSpinButton($event)\" aria-label=\"Add {{step}}\" aria-controls=\"{{spinButtonId}}\" ng-class=\"{'disabled': isPlusDisabled()}\" ng-disabled=\"isPlusDisabled()\" aria-disabled=\"{{isPlusDisabled()}}\" role=\"spinbutton\"></button>\n" +
15817 angular.module("b2bTemplate/statusTracker/statusTracker.html", []).run(["$templateCache", function($templateCache) {
15818 $templateCache.put("b2bTemplate/statusTracker/statusTracker.html",
15819 "<div class=\"b2b-status-tracker row\">\n" +
15820 " <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" +
15821 " <div class=\"btn btn-small btn-secondary\">\n" +
15822 " <i class=\"icon-primary-left\"></i>\n" +
15825 " <div ng-repeat=\"status in statuses\" class=\"b2b-status-tracker-step {{ status.state }}\" ng-show=\"isInViewport($index)\">\n" +
15826 " <p class=\"b2b-status-tracker-heading\">{{status.heading}}</p>\n" +
15827 " <div class=\"progress\">\n" +
15828 " <div class=\"progress-bar\">\n" +
15829 " <span class=\"hidden-spoken\">\n" +
15830 " {{ removeCamelCase(status.state) }}\n" +
15834 " <div class=\"b2b-status-tracker-estimate {{status.state}}\">\n" +
15835 " <i ng-show=\"status.estimate !== '' && status.estimate !== undefined\" ng-class=\"b2bStatusTrackerConfig.icons[status.state]\"></i>\n" +
15837 " <span ng-bind-html=\"status.estimate\"></span>\n" +
15840 " <div class=\"b2b-status-tracker-description\" ng-bind-html=\"status.description\"> \n" +
15843 " <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" +
15844 " <div class=\"btn btn-small btn-secondary\">\n" +
15845 " <i class=\"icon-primary-right\"></i>\n" +
15851 angular.module("b2bTemplate/stepTracker/stepTracker.html", []).run(["$templateCache", function($templateCache) {
15852 $templateCache.put("b2bTemplate/stepTracker/stepTracker.html",
15853 "<div class=\"b2b-step-tracker\">\n" +
15854 " <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" +
15855 " <div class=\"btn btn-left btn-small btn-secondary\"><i class=\"icon-primary-left\"></i></div>\n" +
15857 " <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" +
15858 " <div class=\"btn btn-small btn-right btn-secondary\"><i class=\"icon-primary-right\"></i></div>\n" +
15860 " <ul class=\"b2b-steps\">\n" +
15861 " <li ng-class=\"{'b2b-step-done':$index < currentIndex - 1 ,'b2b-step-on':$index == currentIndex - 1}\" \n" +
15862 " ng-repeat=\"stepsItem in stepsItemsObject\" ng-show=\"isInViewport($index)\">\n" +
15863 " <p class=\"b2b-step-text\" data-sm-text=\"{{stepsItem.dataMobile}}\" data-large-text=\"{{stepsItem.dataDesktop}}\">{{stepsItem.text}}</p>\n" +
15864 " <span class=\"hidden-spoken\">\n" +
15865 " {{($index < currentIndex - 1)? 'Complete. '+stepsItem.text+' '+stepsItem.dataMobile:''}} \n" +
15866 " {{($index == currentIndex - 1)? 'In Progress. '+stepsItem.text+' '+stepsItem.dataMobile:''}} \n" +
15867 " {{($index > currentIndex - 1)? 'Incomplete. '+stepsItem.text+' '+stepsItem.dataMobile:''}}\n" +
15874 angular.module("b2bTemplate/switches/switches-spanish.html", []).run(["$templateCache", function($templateCache) {
15875 $templateCache.put("b2bTemplate/switches/switches-spanish.html",
15876 "<div class=\"switch-overlay\" aria-hidden=\"true\">\n" +
15877 " <span class=\"btn-slider-on\"><span class=\"hidden-spoken\">seleccione para hacer </span><i class=\"activo\">activo</i></span>\n" +
15878 " <span class=\"switch-handle\"></span>\n" +
15879 " <span class=\"btn-slider-off\"><span class=\"hidden-spoken\">seleccione para hacer </span><i class=\"inactivo\">inactivo</i></span>\n" +
15883 angular.module("b2bTemplate/switches/switches.html", []).run(["$templateCache", function($templateCache) {
15884 $templateCache.put("b2bTemplate/switches/switches.html",
15885 "<div class=\"switch-overlay\" aria-hidden=\"true\">\n" +
15886 " <span class=\"btn-slider-on\">On</span>\n" +
15887 " <span class=\"switch-handle\"></span>\n" +
15888 " <span class=\"btn-slider-off\">Off</span>\n" +
15892 angular.module("b2bTemplate/tableMessages/tableMessage.html", []).run(["$templateCache", function($templateCache) {
15893 $templateCache.put("b2bTemplate/tableMessages/tableMessage.html",
15894 "<div class=\"b2b-table-message\">\n" +
15895 " <div class=\"b2b-message\" ng-if=\"msgType === 'noMatchingResults'\">\n" +
15896 " <div class=\"b2b-magnify-glass\"></div>\n" +
15898 " <div ng-transclude></div>\n" +
15901 " <div class=\"b2b-message\" ng-if=\"msgType == 'infoCouldNotLoad'\">\n" +
15902 " <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" +
15903 " <div>Oops!</div>\n" +
15904 " <div>The information could not load at this time.</div>\n" +
15905 " <div>Please <a href=\"javascript:void(0)\" title=\"Refresh the page\" ng-click=\"refreshAction($event)\">refresh the page</a>\n" +
15908 " <div class=\"b2b-message\" ng-if=\"msgType == 'magnifySearch'\">\n" +
15909 " <div class=\"b2b-magnify-glass\"></div>\n" +
15911 " <p class=\"b2b-message-title\">Please input values to\n" +
15912 " <br/> begin your search.</p>\n" +
15915 " <div class=\"b2b-message\" ng-if=\"msgType === 'loadingTable'\">\n" +
15916 " <div class=\"icon-primary-spinner-ddh b2b-loading-dots\"></div>\n" +
15917 " <div ng-transclude></div>\n" +
15923 angular.module("b2bTemplate/tableScrollbar/tableScrollbar.html", []).run(["$templateCache", function($templateCache) {
15924 $templateCache.put("b2bTemplate/tableScrollbar/tableScrollbar.html",
15925 "<div class=\"b2b-table-scrollbar\">\n" +
15926 " <div class=\"b2b-scrollbar-arrows\">\n" +
15927 " <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" +
15928 " <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" +
15930 " <div class=\"b2b-table-inner-container\">\n" +
15931 " <span ng-transclude></span>\n" +
15936 angular.module("b2bTemplate/tables/b2bTable.html", []).run(["$templateCache", function($templateCache) {
15937 $templateCache.put("b2bTemplate/tables/b2bTable.html",
15938 "<table ng-class=\"{'striped': responsive, 'complex-table': responsive && active}\" ng-transclude></table>");
15941 angular.module("b2bTemplate/tables/b2bTableBody.html", []).run(["$templateCache", function($templateCache) {
15942 $templateCache.put("b2bTemplate/tables/b2bTableBody.html",
15943 "<td ng-hide=\"isHidden()\" ng-transclude></td>");
15946 angular.module("b2bTemplate/tables/b2bTableHeaderSortable.html", []).run(["$templateCache", function($templateCache) {
15947 $templateCache.put("b2bTemplate/tables/b2bTableHeaderSortable.html",
15948 "<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" +
15949 " <span ng-transclude></span>\n" +
15950 " <i ng-class=\"{'icon-controls-upPRIMARY active': sortPattern === 'ascending', 'icon-controls-down active down': sortPattern === 'descending'}\"></i>\n" +
15954 angular.module("b2bTemplate/tables/b2bTableHeaderUnsortable.html", []).run(["$templateCache", function($templateCache) {
15955 $templateCache.put("b2bTemplate/tables/b2bTableHeaderUnsortable.html",
15956 "<th scope=\"col\" ng-hide=\"isHidden()\" ng-transclude></th>");
15959 angular.module("b2bTemplate/tabs/b2bTab.html", []).run(["$templateCache", function($templateCache) {
15960 $templateCache.put("b2bTemplate/tabs/b2bTab.html",
15961 "<li role=\"tab\" aria-selected=\"{{isTabActive()}}\" aria-controls=\"{{tabPanelId}}\" class=\"tab\" \n" +
15962 " ng-class=\"{'active': isTabActive()}\" ng-click=\"clickTab()\" ng-hide=\"tabItem.disabled\">\n" +
15963 " <a href=\"javascript:void(0)\" tabindex=\"{{(isTabActive() && tabItem.disabled)?-1:0}}\"\n" +
15964 " ng-disabled=\"tabItem.disabled\" aria-expanded=\"{{(isTabActive() && !tabItem.disabled)}}\" \n" +
15965 " b2b-accessibility-click=\"13,32\" ng-transclude></a>\n" +
15966 " <span class=\"hidden-spoken\" ng-if=\"isTabActive()\">Active tab</span>\n" +
15970 angular.module("b2bTemplate/tabs/b2bTabset.html", []).run(["$templateCache", function($templateCache) {
15971 $templateCache.put("b2bTemplate/tabs/b2bTabset.html",
15972 "<ul class=\"tabs promo-tabs\" role=\"tablist\" ng-transclude></ul>");
15975 angular.module("b2bTemplate/treeNav/groupedTree.html", []).run(["$templateCache", function($templateCache) {
15976 $templateCache.put("b2bTemplate/treeNav/groupedTree.html",
15977 "<ul role=\"group\">\n" +
15978 " <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" +
15979 " <ul role=\"group\">\n" +
15980 " <b2b-member ng-repeat='member in value.childArray' member='member' time-delay='{{timeDelay}}' group-it='groupIt'></b2b-member>\n" +
15986 angular.module("b2bTemplate/treeNav/treeMember.html", []).run(["$templateCache", function($templateCache) {
15987 $templateCache.put("b2bTemplate/treeNav/treeMember.html",
15988 "<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" +
15989 " <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" +
15990 " <span class=\"{{!member.child?'end':''}} b2b-tree-node-icon\">\n" +
15991 " <i class=\"b2b-tree-expandCollapse-icon\" ng-class=\"{'icon-primary-expanded':member.active}\"></i>\n" +
15993 " <div id=\"description_{{$id}}\" class=\"offscreen-text\">\n" +
15994 " {{member.descriptionText}}\n" +
15996 " <div class=\"b2b-tree-tooltip\" ng-if=\"member.showTooltip\">\n" +
15997 " <span class=\"b2b-tree-arrow-left\"></span>\n" +
15998 " <div class=\"b2b-tree-tooltip-content\">\n" +
15999 " {{member.tooltipContent}}\n" +
16006 angular.module("b2bTemplate/treeNav/ungroupedTree.html", []).run(["$templateCache", function($templateCache) {
16007 $templateCache.put("b2bTemplate/treeNav/ungroupedTree.html",
16008 "<ul role=\"{{setRole}}\"><b2b-member ng-repeat='member in collection' member='member' group-it='groupIt'></b2b-member></ul>");
16011 angular.module("b2bTemplate/treeNodeCheckbox/groupedTree.html", []).run(["$templateCache", function($templateCache) {
16012 $templateCache.put("b2bTemplate/treeNodeCheckbox/groupedTree.html",
16013 "<ul role=\"group\">\n" +
16014 " <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" +
16015 " <span class=\"ng-hide\">\n" +
16016 " <label class=\"checkbox\">\n" +
16017 " <input type=\"checkbox\" tabindex=\"-1\" class=\"treeCheckBox grpTreeCheckbox\" style=\"margin-top:2px;\"/><i class=\"skin\"></i><span> {{(key)?key:''}}</span>\n" +
16021 " {{(key)?key:''}} \n" +
16023 " <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" +
16024 " <ul role=\"group\">\n" +
16025 " <b2b-tree-member ng-repeat='member in value.childArray' member='member' group-it='groupIt'></b2b-tree-member>\n" +
16031 angular.module("b2bTemplate/treeNodeCheckbox/treeMember.html", []).run(["$templateCache", function($templateCache) {
16032 $templateCache.put("b2bTemplate/treeNodeCheckbox/treeMember.html",
16033 "<li role=\"treeitem\" aria-expanded=\"{{(member.active?true:false)}}\" aria-label=\"{{member.name}}\" ng-class=\"{'bg':member.selected}\" b2b-tree-node-link>\n" +
16034 " <a tabindex=\"-1\" title=\"{{member.name}}\" href=\"javascript:void(0)\" ng-class=\"{'active':member.active}\">\n" +
16035 " <span ng-show=\"member.displayCheckbox\">\n" +
16036 " <label class=\"checkbox\">\n" +
16037 " <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" +
16040 " <span ng-show=\"!member.displayCheckbox\">\n" +
16041 " {{member.name}} \n" +
16043 " <span class=\"nodeIcon {{!member.child?'end':''}}\">\n" +
16044 " <i class=\"expandCollapseIcon\" ng-class=\"{'icon-primary-expanded':member.active}\"></i>\n" +
16050 angular.module("b2bTemplate/treeNodeCheckbox/ungroupedTree.html", []).run(["$templateCache", function($templateCache) {
16051 $templateCache.put("b2bTemplate/treeNodeCheckbox/ungroupedTree.html",
16052 "<ul role=\"{{setRole}}\"><b2b-tree-member ng-repeat='member in collection' member='member' group-it='groupIt'></b2b-tree-member></ul>");