From 34f706fedc5536caf2a218dbcd809991a1b484dd Mon Sep 17 00:00:00 2001 From: st782s Date: Wed, 24 May 2017 10:20:53 -0400 Subject: [PORTAL-10] Enhancing Drill Down Changes have been made to improve the stability of Drill down capabilities. Change-Id: I6d4831b69f19f9b33cf43b65acc6dd7c5deebb67 Signed-off-by: st782s --- .../external/b2b/js/b2b-angular/b2b-library.min.js | 1753 +++++++++++++++----- 1 file changed, 1333 insertions(+), 420 deletions(-) (limited to 'ecomp-sdk/epsdk-app-overlay/src/main/webapp/app/fusion/external/b2b/js/b2b-angular') diff --git a/ecomp-sdk/epsdk-app-overlay/src/main/webapp/app/fusion/external/b2b/js/b2b-angular/b2b-library.min.js b/ecomp-sdk/epsdk-app-overlay/src/main/webapp/app/fusion/external/b2b/js/b2b-angular/b2b-library.min.js index 0e62a349..3d211ec7 100644 --- a/ecomp-sdk/epsdk-app-overlay/src/main/webapp/app/fusion/external/b2b/js/b2b-angular/b2b-library.min.js +++ b/ecomp-sdk/epsdk-app-overlay/src/main/webapp/app/fusion/external/b2b/js/b2b-angular/b2b-library.min.js @@ -1,5 +1,5 @@ -/*! b2b-angular-library - v1.0.1 - Last updated: 2017-03-02. Copyright (c) 2016 AT&T Services, Inc. */ -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.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.headings','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.selectorModule','b2b.att.separators','b2b.att.slider','b2b.att.spinButton','b2b.att.staticRouteTemplate','b2b.att.statusTracker','b2b.att.stepTracker','b2b.att.switches','b2b.att.tableMessages','b2b.att.tables','b2b.att.tableScrollbar','b2b.att.tabs','b2b.att.tagBadges','b2b.att.textArea','b2b.att.tooltipsForForms','b2b.att.treeNav','b2b.att.treeNodeCheckbox','b2b.att.utilities']);/** +/*! b2b-angular-library - v1.0.4 - Last updated: 2017-05-03. Copyright (c) 2016 AT&T Services, Inc. */ +angular.module("b2b.att.tpls", ['b2bTemplate/audioPlayer/audioPlayer.html', 'b2bTemplate/audioRecorder/audioRecorder.html', 'b2bTemplate/backToTop/backToTop.html', 'b2bTemplate/boardstrip/b2bAddBoard.html', 'b2bTemplate/boardstrip/b2bBoard.html', 'b2bTemplate/boardstrip/b2bBoardstrip.html', 'b2bTemplate/calendar/datepicker-popup.html', 'b2bTemplate/calendar/datepicker.html', 'b2bTemplate/coachmark/coachmark.html', 'b2bTemplate/dropdowns/b2bDropdownDesktop.html', 'b2bTemplate/dropdowns/b2bDropdownGroupDesktop.html', 'b2bTemplate/dropdowns/b2bDropdownListDesktop.html', 'b2bTemplate/fileUpload/fileUpload.html', 'b2bTemplate/flyout/flyout.html', 'b2bTemplate/flyout/flyoutContent.html', 'b2bTemplate/footer/footer_column_switch_tpl.html', 'b2bTemplate/horizontalTable/horizontalTable.html', 'b2bTemplate/hourPicker/b2bHourpicker.html', 'b2bTemplate/hourPicker/b2bHourpickerPanel.html', 'b2bTemplate/hourPicker/b2bHourpickerValue.html', 'b2bTemplate/leftNavigation/leftNavigation.html', 'b2bTemplate/listbox/listbox.html', 'b2bTemplate/modalsAndAlerts/b2b-backdrop.html', 'b2bTemplate/modalsAndAlerts/b2b-window.html', 'b2bTemplate/monthSelector/monthSelector-popup.html', 'b2bTemplate/monthSelector/monthSelector.html', 'b2bTemplate/monthSelector/monthSelectorLink.html', 'b2bTemplate/pagination/b2b-pagination.html', 'b2bTemplate/paneSelector/paneSelector.html', 'b2bTemplate/paneSelector/paneSelectorPane.html', 'b2bTemplate/profileCard/profileCard-addUser.html', 'b2bTemplate/profileCard/profileCard.html', 'b2bTemplate/searchField/searchField.html', 'b2bTemplate/seekBar/seekBar.html', 'b2bTemplate/slider/slider.html', 'b2bTemplate/spinButton/spinButton.html', 'b2bTemplate/statusTracker/statusTracker.html', 'b2bTemplate/stepTracker/stepTracker.html', 'b2bTemplate/switches/switches-spanish.html', 'b2bTemplate/switches/switches.html', 'b2bTemplate/tableMessages/tableMessage.html', 'b2bTemplate/tables/b2bTable.html', 'b2bTemplate/tables/b2bTableBody.html', 'b2bTemplate/tables/b2bTableHeaderSortable.html', 'b2bTemplate/tables/b2bTableHeaderUnsortable.html', 'b2bTemplate/tableScrollbar/tableScrollbar.html', 'b2bTemplate/tabs/b2bTab.html', 'b2bTemplate/tabs/b2bTabset.html', 'b2bTemplate/treeNav/groupedTree.html', 'b2bTemplate/treeNav/treeMember.html', 'b2bTemplate/treeNav/ungroupedTree.html', 'b2bTemplate/treeNodeCheckbox/groupedTree.html', 'b2bTemplate/treeNodeCheckbox/treeMember.html', 'b2bTemplate/treeNodeCheckbox/ungroupedTree.html']);angular.module("b2b.att", ["b2b.att.tpls", 'b2b.att.addressInputTemplate','b2b.att.arrows','b2b.att.audioPlayer','b2b.att.audioRecorder','b2b.att.backToTop','b2b.att.badgesForAlerts','b2b.att.boardstrip','b2b.att.bootstrapGridTemplate','b2b.att.breadcrumbs','b2b.att.buttonGroups','b2b.att.buttons','b2b.att.calendar','b2b.att.checkboxes','b2b.att.coachmark','b2b.att.configurationSection','b2b.att.directoryListingTemplate','b2b.att.dropdowns','b2b.att.dropdowns','b2b.att.fileUpload','b2b.att.filters','b2b.att.flyout','b2b.att.footer','b2b.att.header','b2b.att.headingsAndCopy','b2b.att.horizontalTable','b2b.att.hourPicker','b2b.att.inputTemplate','b2b.att.leftNavigation','b2b.att.links','b2b.att.listbox','b2b.att.loaderAnimation','b2b.att.messageWrapper','b2b.att.modalsAndAlerts','b2b.att.monthSelector','b2b.att.multiLevelNavigation','b2b.att.multipurposeExpander','b2b.att.notesMessagesAndErrors','b2b.att.notificationCardTemplate','b2b.att.orderConfirmationTemplate','b2b.att.pagination','b2b.att.paneSelector','b2b.att.phoneNumberInput','b2b.att.profileBlockTemplate','b2b.att.profileCard','b2b.att.radios','b2b.att.searchField','b2b.att.seekBar','b2b.att.separators','b2b.att.slider','b2b.att.spinButton','b2b.att.staticRouteTemplate','b2b.att.statusTracker','b2b.att.stepTracker','b2b.att.switches','b2b.att.tableMessages','b2b.att.tables','b2b.att.tableScrollbar','b2b.att.tabs','b2b.att.tagBadges','b2b.att.textArea','b2b.att.timeInputField','b2b.att.tooltipsForForms','b2b.att.treeNav','b2b.att.treeNodeCheckbox','b2b.att.utilities']);/** * @ngdoc directive * @name Template.att:Address Input * @@ -1005,6 +1005,24 @@ angular.module('b2b.att.boardstrip', ['b2b.att.utilities']) } }; }]); +/** + * @ngdoc directive + * @name Template.att:Bootstrap Grid Template + * + * @description + * + * + * @example + *
+ + + + +
+ * + */ +angular.module('b2b.att.bootstrapGridTemplate', []) + /** * @ngdoc directive * @name Navigation.att:breadcrumbs @@ -1295,7 +1313,7 @@ angular.module('b2b.att.calendar', ['b2b.att.position', 'b2b.att.utilities']) legendMessage: null, calendarDisabled: false, collapseWait: 0, - orientation: 'left', + orientation: 'right', inline: false, helperText: 'The date you selected is $date. In case of mobile double tap to open calendar. Select a date to close the calendar.', datepickerEvalAttributes: ['dateFormat', 'dayFormat', 'monthFormat', 'yearFormat', 'dayHeaderFormat', 'dayTitleFormat', 'disableWeekend', 'disableSunday', 'startingDay', 'collapseWait', 'orientation'], @@ -1359,11 +1377,15 @@ angular.module('b2b.att.calendar', ['b2b.att.position', 'b2b.att.utilities']) dayHeader: getValue($attrs.dayHeaderFormat, dtConfig.dayHeaderFormat), dayTitle: getValue($attrs.dayTitleFormat, dtConfig.dayTitleFormat), disableWeekend: getValue($attrs.disableWeekend, dtConfig.disableWeekend), - disableSunday: getValue($attrs.disableSunday, dtConfig.disableSunday), - disableDates: getValue($attrs.disableDates, dtConfig.disableDates) + disableSunday: getValue($attrs.disableSunday, dtConfig.disableSunday) }, startingDay = getValue($attrs.startingDay, dtConfig.startingDay); + if($attrs.disableDates !== undefined) { + format.disableDates = $attrs.disableDates; + } else { + format.disableDates = dtConfig.disableDates; + } $scope.minDate = dtConfig.minDate ? $scope.resetTime(dtConfig.minDate) : null; $scope.maxDate = dtConfig.maxDate ? $scope.resetTime(dtConfig.maxDate) : null; $scope.dueDate = dtConfig.dueDate ? $scope.resetTime(dtConfig.dueDate) : null; @@ -1398,12 +1420,35 @@ angular.module('b2b.att.calendar', ['b2b.att.position', 'b2b.att.utilities']) this.updatePosition = function (b2bDatepickerPopupTemplate) { $scope.position = $position.offset($element); - $scope.position.top = $scope.position.top + $element.find('input').prop('offsetHeight'); - if ($scope.orientation === 'right') { - $scope.position.left = $scope.position.left - (((b2bDatepickerPopupTemplate && b2bDatepickerPopupTemplate.prop('offsetWidth')) || 290) - $element.find('input').prop('offsetWidth')); - } + $scope.position.top = $scope.position.top + $element.prop('offsetHeight'); + $scope.position.left = $scope.position.left - (((b2bDatepickerPopupTemplate && b2bDatepickerPopupTemplate.prop('offsetWidth')) || 290) - $element.prop('offsetWidth')); }; + this.isDateInRange = function(date) { + if ((compare(date, $scope.minDate) >= 0) && (compare(date, $scope.maxDate) <= 0)) { + return true; + } else { + return false; + } + return false; + } + + this.isDisbaledDate = function(date) { + if ($attrs.from && !angular.isDate($scope.fromDate)) { + return true; + } + if (format.disableWeekend === true && (dateFilter(date, format.dayHeader) === "Saturday" || dateFilter(date, format.dayHeader) === "Sunday")) { + return true; + } + if (format.disableSunday === true && (dateFilter(date, format.dayHeader) === "Sunday")) { + return true; + } + + return (($scope.minDate && compare(date, $scope.minDate) < 0) || ($scope.maxDate && compare(date, $scope.maxDate) > 0) || ($scope.datesCallBack({ + date: date + }))); + + } function isSelected(dt) { if (dt && angular.isDate($scope.currentDate) && compare(dt, $scope.currentDate) === 0) { return true; @@ -1470,7 +1515,7 @@ angular.module('b2b.att.calendar', ['b2b.att.position', 'b2b.att.utilities']) if (isOld(date, currentMonthDate) || isNew(date, currentMonthDate)) { return true; } - return (($scope.minDate && compare(date, $scope.minDate) < 0) || ($scope.maxDate && compare(date, $scope.maxDate) > 0) || (format.disableDates && format.disableDates({ + return (($scope.minDate && compare(date, $scope.minDate) < 0) || ($scope.maxDate && compare(date, $scope.maxDate) > 0) || ($scope.datesCallBack({ date: date }))); }; @@ -1582,36 +1627,69 @@ angular.module('b2b.att.calendar', ['b2b.att.position', 'b2b.att.utilities']) ]; }]) -.directive('b2bDatepickerPopup', ['$parse', '$log', '$timeout', '$document', '$documentBind', '$isElement', '$templateCache', '$compile', 'trapFocusInElement', '$position', '$window', function ($parse, $log, $timeout, $document, $documentBind, $isElement, $templateCache, $compile, trapFocusInElement, $position, $window) { +.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) { return { restrict: 'EA', - replace: true, - transclude: true, - templateUrl: function (elem, attr) { - if (attr.inline === 'true') { - return 'b2bTemplate/calendar/datepicker-popup.html'; - } else { - return 'b2bTemplate/calendar/datepicker.html'; - } + scope: { + model: '=ngModel', + datesCallBack: '&disableDates', + onSelectClose: '&', + disabledInput: '=?ngDisabled' }, - scope: {}, - require: ['b2bDatepickerPopup', 'ngModel', '?^b2bDatepickerGroup'], + require: ['b2bDatepicker', 'ngModel', '?^b2bDatepickerGroup'], controller: 'b2bDatepickerController', link: function (scope, element, attrs, ctrls) { var datepickerCtrl = ctrls[0], ngModel = ctrls[1], b2bDatepickerGroupCtrl = ctrls[2]; var b2bDatepickerPopupTemplate; + var isCalendarOpened = false; + if(scope.disabledInput === undefined || scope.disabledInput === '') { + scope.disabledInput = false; + } + if(attrs.inline == 'true'){ + element.after($compile($templateCache.get('b2bTemplate/calendar/datepicker-popup.html'))(scope)); + var temp = element.after(); + element.remove(); + element = temp; + } else { + var buttonTabIndex = scope.disabledInput===true ? -1 : 0; + + element.after($compile('')(scope)); + element.attr('placeholder', 'MM/dd/yyyy'); + element.attr('b2b-format-date', b2bDatepickerConfig.dateFormat); + } + scope.$watch('model', function(val) { + + if(val !== undefined && val !== '') { + var date_regex = /^(0[1-9]|1[0-2])\/(0[1-9]|1\d|2\d|3[01])\/(19|20)\d{2}$/ ; + + if(!date_regex.test(element[0].value)) { + ngModel.$setValidity('datePattern', false); + } else { + ngModel.$setValidity('datePattern', true); + } + + } else { + ngModel.$setValidity('datePattern', true); + } + + }); if (!ngModel) { $log.error("ng-model is required."); return; // do nothing if no ng-model } + if(scope.model !== undefined && scope.model !== '') { + element[0].value = $filter('date')(scope.model, "MM/dd/yyyy"); + } + // Configuration parameters var mode = 0, selected; scope.isOpen = false; + var isValidDate = false; scope.headers = []; scope.footers = []; @@ -1620,21 +1698,66 @@ angular.module('b2b.att.calendar', ['b2b.att.position', 'b2b.att.utilities']) b2bDatepickerGroupCtrl.registerDatepickerScope(scope); } - element.find('button').bind('click', function () { - element.find('input')[0].click(); - }); + var calendarButton = angular.element(element[0].nextElementSibling); - element.find('input').bind('click', function () { + calendarButton.bind('click',function(){ + openCalendarPopup = false; if (!scope.ngDisabled) { scope.isOpen = !scope.isOpen; toggleCalendar(scope.isOpen); scope.$apply(); datepickerCtrl.updatePosition(b2bDatepickerPopupTemplate); $timeout(function () { - angular.element(element[0].querySelector('.datepicker-input')).scrollTop=0; + // angular.element(element[0].querySelector('.datepicker-input')).scrollTop=0; },10); } - }); + }) + var openCalendarPopup = false; + + element.bind('blur', function() { + if(scope.model !== undefined && scope.model !== '') { + var dateEntered = scope.model; + + var date_regex = /^(0[1-9]|1[0-2])\/(0[1-9]|1\d|2\d|3[01])\/(19|20)\d{2}$/ ; + + if(date_regex.test(dateEntered)) { + var parts = dateEntered.split('/'); + var enteredDate = new Date(parts[2],parts[0]-1,parts[1]); + + if(datepickerCtrl.isDateInRange(enteredDate)) { + isValidDate -= 1; + ngModel.$setValidity('outOfRange', true); + $timeout(function(){ + ngModel.$setValidity('outOfRange', true); + },10); + isValidDate = true; + if(!datepickerCtrl.isDisbaledDate(enteredDate)) { + $timeout(function(){ + ngModel.$setValidity('disabledDate', true); + },10); + scope.select(enteredDate); + openCalendarPopup = true; + } else { + $timeout(function(){ + ngModel.$setValidity('disabledDate', false); + },10); + isValidDate = false; + openCalendarPopup = false; + } + + } else { + isValidDate += 1; + $timeout(function(){ + ngModel.$setValidity('outOfRange', false); + },10); + isValidDate = false; + openCalendarPopup = false; + } + + } + } + }); + var toggleCalendar = function (flag) { if (!scope.inline) { if (flag) { @@ -1650,17 +1773,45 @@ angular.module('b2b.att.calendar', ['b2b.att.position', 'b2b.att.utilities']) scope.getFocus = false; scope.$apply(); }, 100); + handleTabEvent(); }); + angular.element(document.querySelector('.b2b-calendar-icon')).attr('aria-expanded','true'); } else { - b2bDatepickerPopupTemplate.unbind('keydown', keyPress); - b2bDatepickerPopupTemplate.remove(); - element.find('button')[0].focus(); + if(!openCalendarPopup) { + b2bDatepickerPopupTemplate.unbind('keydown', keyPress); + b2bDatepickerPopupTemplate.remove(); + } + element[0].focus(); scope.getFocus = false; + angular.element(document.querySelector('.b2b-calendar-icon')).attr('aria-expanded','false'); trapFocusInElement(flag, b2bDatepickerPopupTemplate); } } }; + var handleTabEvent = function(){ + b2bDatepickerPopupTemplate.find('td').on('keydown', function (e) { + if (e.keyCode == '9') { + if(e.shiftKey){ + if(b2bDatepickerPopupTemplate.find('tr')[0].querySelector('th.next')){ + b2bDatepickerPopupTemplate.find('tr')[0].querySelector('th.next').focus(); + }else{ + b2bDatepickerPopupTemplate.find('tr')[0].querySelector('th.datepicker-switch').focus() + } + }else{ + if(b2bDatepickerPopupTemplate.find('tr')[0].querySelector('th.prev')){ + b2bDatepickerPopupTemplate.find('tr')[0].querySelector('th.prev').focus(); + }else{ + b2bDatepickerPopupTemplate.find('tr')[0].querySelector('th.datepicker-switch').focus() + } + } + + e.preventDefault(); + e.stopPropagation(); + } + }); + } + var outsideClick = function (e) { var isElement = $isElement(angular.element(e.target), element, $document); var isb2bDatepickerPopupTemplate = $isElement(angular.element(e.target), b2bDatepickerPopupTemplate, $document); @@ -1871,6 +2022,8 @@ angular.module('b2b.att.calendar', ['b2b.att.position', 'b2b.att.utilities']) date: dt }) !== false)) { scope.currentDate = dt; + element[0].value = $filter('date')(dt, "MM/dd/yyyy"); + ngModel.$setValidity('outOfRange', true); if (angular.isNumber(scope.collapseWait)) { $timeout(function () { scope.isOpen = false; @@ -1892,6 +2045,7 @@ angular.module('b2b.att.calendar', ['b2b.att.position', 'b2b.att.utilities']) $timeout(function () { trapFocusInElement(); + handleTabEvent(); }, 100); $event.preventDefault(); @@ -1928,73 +2082,6 @@ angular.module('b2b.att.calendar', ['b2b.att.position', 'b2b.att.utilities']) }; }]) -.directive('b2bDatepicker', ['$compile', '$log', 'b2bDatepickerConfig', 'b2bDatepickerService', function ($compile, $log, b2bDatepickerConfig, b2bDatepickerService) { - return { - restrict: 'A', - scope: { - disableDates: '&', - onSelectClose: '&' - }, - require: 'ngModel', - controller: ['$scope', '$element', '$attrs', function (scope, elem, attr) { - var dateFormatString = angular.isDefined(attr.dateFormat) ? scope.$parent.$eval(attr.dateFormat) : b2bDatepickerConfig.dateFormat; - var helperText = angular.isDefined(attr.helperText) ? scope.$parent.$eval(attr.helperText) : b2bDatepickerConfig.helperText; - helperText = helperText.replace('$date', '{{dt | date : \'' + dateFormatString + '\'}}'); - - var inline = false; - if (elem.prop('nodeName') !== 'INPUT') { - inline = true; - } - - var calendarIcon = '' - var selectedDateMessage = ''; - - elem.removeAttr('b2b-datepicker'); - elem.removeAttr('ng-model'); - elem.removeAttr('ng-disabled'); - elem.addClass('datepicker-input'); - elem.attr('ng-model', 'dt'); - elem.attr('aria-describedby', 'datepicker'); - elem.attr('aria-hidden', 'true'); - elem.attr('tabindex', '-1'); - elem.attr('readonly', 'true'); - elem.attr('ng-disabled', 'ngDisabled'); - elem.attr('b2b-format-date', dateFormatString); - - var wrapperElement = angular.element('
'); - wrapperElement.attr('b2b-datepicker-popup', ''); - wrapperElement.attr('ng-model', 'dt'); - if (inline) { - wrapperElement.attr('inline', inline); - } - - b2bDatepickerService.setAttributes(attr, wrapperElement); - b2bDatepickerService.bindScope(attr, scope); - - wrapperElement.html(''); - wrapperElement.append(calendarIcon); - wrapperElement.append(selectedDateMessage); - wrapperElement.append(elem.prop('outerHTML')); - - var elm = wrapperElement.prop('outerHTML'); - elm = $compile(elm)(scope); - elem.replaceWith(elm); - }], - link: function (scope, elem, attr, ctrl) { - if (!ctrl) { - $log.error("ng-model is required."); - return; // do nothing if no ng-model - } - - scope.$watch('dt', function (value) { - ctrl.$setViewValue(value); - }); - ctrl.$render = function () { - scope.dt = ctrl.$viewValue; - }; - } - }; -}]) .directive('b2bDatepickerGroup', [function () { return { @@ -2146,7 +2233,7 @@ angular.module('b2b.att.checkboxes', ['b2b.att.utilities']) angular.module('b2b.att.coachmark', ['b2b.att.utilities','b2b.att.position']) - .directive('b2bCoachmark', ['$document', '$compile', '$position', '$timeout', function($document, $compile, $position, $timeout) { + .directive('b2bCoachmark', ['$document', '$compile', '$position', '$timeout', 'b2bViewport', 'keymap', function($document, $compile, $position, $timeout, b2bViewport, keymap) { return { restrict: 'A', scope: { @@ -2210,8 +2297,39 @@ angular.module('b2b.att.coachmark', ['b2b.att.utilities','b2b.att.position']) } element[0].focus(); } + + var realStyle = function(_elem, _style) { + var computedStyle; + if ( typeof _elem.currentStyle != 'undefined' ) { + computedStyle = _elem.currentStyle; + } else { + computedStyle = document.defaultView.getComputedStyle(_elem, null); + } + + return _style ? computedStyle[_style] : computedStyle; + }; + + var copyComputedStyle = function(src, dest) { + var s = realStyle(src); + for ( var i in s ) { + // Do not use `hasOwnProperty`, nothing will get copied + if ( typeof i == "string" && i != "cssText" && !/\d/.test(i) && i.indexOf('webkit') !== 0 ) { + // The try is for setter only properties + try { + dest.style[i] = s[i]; + // `fontSize` comes before `font` If `font` is empty, `fontSize` gets + // overwritten. So make sure to reset this property. (hackyhackhack) + // Other properties may need similar treatment + if ( i == "font" ) { + dest.style.fontSize = s.fontSize; + } + } catch (e) {} + } + } + }; function showCoachmark(targetElement) { + scope.currentCoachmark = targetElement; if(coachMarkElement !== undefined && coachMarkElement !==""){ coachMarkElement.removeClass('b2b-coachmark-label') @@ -2219,16 +2337,32 @@ angular.module('b2b.att.coachmark', ['b2b.att.utilities','b2b.att.position']) coachmarkHighlight.remove(); } coachMarkElement = angular.element(document.querySelector(targetElement.elementId)); - coachMarkElement.addClass('b2b-coachmark-label'); + var elementPosition = $position.offset(coachMarkElement); coachmarkHighlight = angular.element('
'); coachmarkHighlight.css({ - 'width': (elementPosition.width + 20) +'px', + 'width': (elementPosition.width + 25) +'px', 'top': (elementPosition.top -10) + 'px', 'left': (elementPosition.left - 10) + 'px', 'height': (elementPosition.height + 20) +'px' - }); + }); + if(targetElement.cloneHtml){ + var copy = coachMarkElement[0].cloneNode(true); + copy.id = "b2b-unique-"+targetElement.elementId.slice(1); + copyComputedStyle(coachMarkElement[0],copy); + var copychildNodes = copy.childNodes; + var coachmarkChildNodes = coachMarkElement[0].childNodes; + for(i=0;i -1) { return position; } @@ -2561,6 +2747,17 @@ angular.module('b2b.att.dropdowns', ['b2b.att.utilities', 'b2b.att.position', 'n ctrl.resetCounter(scope.dropdownLists[scope.currentSelected.value][2]); } } + $timeout(function () { + if(scope.dropdownLists[scope.currentSelected.value] !== undefined){ + (scope.dropdownLists[scope.currentSelected.value][1])[0].focus(); + } else { + if (scope.isInputDropdown) { + elem.parent().find('input')[0].focus(); + } else { + elem.parent().find('button')[0].focus(); + } + } + }, 100); if (scope.dropdownType === b2bDropdownConfig.linkMenuKeyword) { scope.appendCaretPositionStyle(); } @@ -2579,7 +2776,7 @@ angular.module('b2b.att.dropdowns', ['b2b.att.utilities', 'b2b.att.position', 'n if (!scope.toggleFlag) { if (ev.keyCode) { var currentIndex = scope.currentSelected.index; - if (ev.altKey === true && ev.keyCode === keymap.KEY.DOWN) { + if (ev.keyCode === keymap.KEY.DOWN) { scope.toggleDropdown(true); ev.preventDefault(); ev.stopPropagation(); @@ -2621,6 +2818,7 @@ angular.module('b2b.att.dropdowns', ['b2b.att.utilities', 'b2b.att.position', 'n } }; $documentBind.click('toggleFlag', outsideClick, scope); + $documentBind.touch('toggleFlag', outsideClick, scope); } }; }]) @@ -2663,13 +2861,28 @@ angular.module('b2b.att.dropdowns', ['b2b.att.utilities', 'b2b.att.position', 'n return { restrict: 'A', controller: ['$scope', '$element', '$attrs', function (scope, elem, attr) { + if(attr.optionRepeat && !scope.watchRegistered){ + var arrayName = attr.optionRepeat.split(" ").pop(); + if(arrayName.indexOf(']') == -1){ + scope.$watch(arrayName,function(){ + /*scope.dropdownLists ={};*/ + scope.dropdownListValues =[]; + scope.dropdown = { + totalIndex: -1 + }; + scope.dropdownTextList = []; + },true); + scope.watchRegistered = true; + } + } + if ((scope.isInputDropdown && b2bUserAgent.notMobile()) || (!scope.isInputDropdown)) { var innerHtml = angular.element('
').append(elem.html()); innerHtml = ($compile(innerHtml)(scope)).html(); var template = angular.element($templateCache.get('b2bTemplate/dropdowns/b2bDropdownListDesktop.html')); template.attr('ng-repeat', attr.optionRepeat); template.attr('value', elem.attr('value')); - template.attr('search-key', elem.attr('value')); + template.attr('search-key', elem.text()); if (elem.attr('aria-describedby')){ template.attr('aria-describedby', attr.ariaDescribedby); } @@ -2701,6 +2914,7 @@ angular.module('b2b.att.dropdowns', ['b2b.att.utilities', 'b2b.att.position', 'n return { restrict: 'A', scope: true, + link: function (scope, elem, attr, ctrl) { var dropdownListValue = scope.dropdownListValue = attr.value; scope.dropdown.totalIndex++; @@ -2710,20 +2924,17 @@ angular.module('b2b.att.dropdowns', ['b2b.att.utilities', 'b2b.att.position', 'n scope.dropdownLists[dropdownListValue][0] = scope; scope.dropdownLists[dropdownListValue][1] = elem; scope.dropdownLists[dropdownListValue][2] = dropdownListIndex; + scope.updateDropdownValue = function () { scope.currentSelected.value = dropdownListValue; if (scope.isInputDropdown) { scope.currentSelected.text = elem.text(); + scope.currentSelected.label = elem.text(); } else if ((scope.dropdownType === b2bDropdownConfig.linkMenuKeyword) || (scope.dropdownType === b2bDropdownConfig.menuKeyword && scope.dropdownSize === b2bDropdownConfig.smallKeyword)) { scope.currentSelected.text = dropdownListValue; - } else if (scope.dropdownType === b2bDropdownConfig.menuKeyword) { - scope.currentSelected.text = $sce.trustAsHtml(elem.html()); - } - if (scope.isInputDropdown) { - scope.currentSelected.label = elem.text(); - } else if (scope.dropdownType === b2bDropdownConfig.linkMenuKeyword) { scope.currentSelected.label = dropdownListValue; } else if (scope.dropdownType === b2bDropdownConfig.menuKeyword) { + scope.currentSelected.text = $sce.trustAsHtml(elem.html()); scope.currentSelected.label = elem.text(); } scope.currentSelected.index = dropdownListIndex; @@ -2740,13 +2951,14 @@ angular.module('b2b.att.dropdowns', ['b2b.att.utilities', 'b2b.att.position', 'n elem.bind('mouseover', function (ev) { elem[0].focus(); }); + elem.bind('keydown', function (ev) { if (!ev.keyCode) { if (ev.which) { ev.keyCode = ev.which; } else if (ev.charCode) { ev.keyCode = ev.charCode; - } + } } if (ev.altKey === true && ev.keyCode === keymap.KEY.UP) { scope.toggleDropdown(false); @@ -3357,7 +3569,7 @@ angular.module('b2b.att.flyout', ['b2b.att.utilities', 'b2b.att.position']) closeFlyout: '&' }, link: function (scope, element, attrs, ctrl) { - element.bind('click', function (e) { + element.bind('click touchstart', function (e) { scope.closeFlyout(e); ctrl.closeFlyout(e); }); @@ -3801,42 +4013,21 @@ angular.module('b2b.att.header', ['b2b.att.dropdowns','b2b.att.utilities']) /** * @ngdoc directive - * @name Layouts.att:headings + * @name Layouts.att:headings & copy * * @description - * + * * - * @usage -

38px page heading

-

30px major section heading

-

24px sub-section heading

-

20px medium heading

-

20px medium emphasis heading

-

18px small heading

-

18px small emphasis heading

-

13px micro heading

- -

Lead

-

This is 38px heading

-

This is lead text...The next big thing since the last big thing we announced.

-

Eyebrow

-

EYEBROW TEXT

-

This is a 30px heading

-

EYEBROW TEXT

-

24px sub-section heading

-

Subheading

-

This is a 30px heading

-

A subheading here to support what was said above

* @example
HTML + AngularJS - +
*/ -var b2bLegalCopy = angular.module('b2b.att.headings', []); +var b2bLegalCopy = angular.module('b2b.att.headingsAndCopy', []); /** * @ngdoc directive * @name Tabs, tables & accordions.att:horizontalTable @@ -3847,6 +4038,8 @@ var b2bLegalCopy = angular.module('b2b.att.headings', []); * @usage * @param {int} sticky - Number of sticky columns to have. Maximum of 3. * @param {boolean} refresh - A boolean that when set to true will force a re-render of table. Only use when using 'bulk mode' + * @param {string} legendContent - A string of html to fill in the legend flyout. This should generally be a
    with
  • and should not rely on Angular for repeating. + * @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. * @example *
    @@ -3867,7 +4060,9 @@ angular.module('b2b.att.horizontalTable', []) transclude: true, scope: { numOfStickyCols: '=?sticky', - refresh: '=?' + refresh: '=?', + legendContent: '=?', + retainColumnSet: '=?' }, templateUrl: 'b2bTemplate/horizontalTable/horizontalTable.html', @@ -3875,29 +4070,6 @@ angular.module('b2b.att.horizontalTable', []) scope.numOfStickyCols = scope.numOfStickyCols || 1; scope.viewportIndex = scope.numOfStickyCols; - // JM520E: This is a temporary hack until I solve the ngRepeat issue - function hack() { - if (element.find('th').length < scope.numOfStickyCols) { - // DOM ngRepeat is not ready, let's check back in 10 ms - console.info('THs are not ready, trying again in 10ms'); - $timeout(hack, 10, false); - } else { - init(); - } - } - hack(); - - if (attrs.refresh !== undefined && attrs.refresh !== '') { - scope.$watch('refresh', function(oldVal, newVal) { - if (scope.refresh) { - // From testing it takes about 30 ms before ngRepeat executes, so let's set initial timeout - // NOTE: May need to expose timeout to developers. Application is known to have digest cycle of 3-5k watches. - $timeout(init, 100, false); - scope.refresh = false; - } - }); - } - var tableElement = element.find('table'); var thElements = element.find('th'); var innerContainer = angular.element(element[0].querySelector('.b2b-horizontal-table-inner-container')); @@ -3919,72 +4091,28 @@ angular.module('b2b.att.horizontalTable', []) var displayNoneCSS = {'display': 'none'}; var displayBlockCSS = {'display': 'table-cell'}; - function calculateVisibleColumns(startingPoint) { - var usedWidth = 0, - visibleColumns = startingPoint || scope.numOfStickyCols; - - while(usedWidth < stickyPixels && visibleColumns < collectiveColumnWidth.length) { - if (usedWidth+collectiveColumnWidth[visibleColumns] > stickyPixels) { - if (startingPoint === visibleColumns) { - return visibleColumns; // The next cell is too large to fit, it should be only thing to fit - } - visibleColumns--; - return visibleColumns; - } - usedWidth += collectiveColumnWidth[visibleColumns]; - visibleColumns++; - } - - if (usedWidth > stickyPixels) { - return --visibleColumns; - } - return visibleColumns; - } - - function updateCellDisplay(set) { - for (var i = scope.numOfStickyCols; i < tableColumns.length; i++) { - angular.element(tableColumns[i]).css(displayNoneCSS); - } - - for (var i = set[0]; i <= set[1]; i++) { - angular.element(tableColumns[i]).css(displayBlockCSS); - } - } - - function forceDigest() { - if (!scope.$$phase) { - scope.$digest(); - } - } - - function findMax(arr, prop) { - var max = 0; - var localVal = 0; - var prevDisplay; - var item; - for (var i = 0; i < arr.length; i++) { - item = arr[i]; - prevDisplay = angular.element(item).css('display'); - if (scope.$$phase) { - scope.$digest(); - } - if (prop === 'width') { - localVal = Math.ceil(parseInt(window.getComputedStyle(item).width.split('px')[0], 10)) + 30; // 30 px is padding - } else if (prop === 'offsetWidth') { - localVal = item.offsetWidth; - } else if (prop === 'height') { - localVal = item.offsetHeight; + // JM520E: This is a temporary hack until I solve the ngRepeat issue + function hack() { + if (element.find('th').length < scope.numOfStickyCols) { + // DOM ngRepeat is not ready, let's check back in 10 ms + $timeout(hack, 10, false); + } else { + if (scope.refresh !== undefined) { + scope.$watch('refresh', function(oldVal, newVal) { // this watch calls too many times + if (!angular.equals(oldVal, newVal)) { //hackFinished && oldVal < newVal + // From testing it takes about 30 ms before ngRepeat executes, so let's set initial timeout + // NOTE: May need to expose timeout to developers. Application is known to have digest cycle of 3-5k watches. + $timeout(init, 100, false); + scope.refresh = false; + } + }); } - if (localVal >= max) { - max = localVal; - } + init(); } - - return max; } - function init() { + var init = function() { // Reset this from a previous execution tableColumns = []; collectiveColumnWidth = []; @@ -3993,7 +4121,9 @@ angular.module('b2b.att.horizontalTable', []) maxHeight = 0; lastVisibleColumn = 0; columnSets = []; - setIndex = 0; + if ((!!scope.retainColumnSet)) { + setIndex = 0; + } visibleColumns = []; stickyPixels = 0; @@ -4002,20 +4132,16 @@ angular.module('b2b.att.horizontalTable', []) innerContainer = angular.element(element[0].querySelector('.b2b-horizontal-table-inner-container')); outerContainer = angular.element(element[0].querySelector('.b2b-horizontal-table')); totalWidth = element.children()[0].offsetWidth; - - tableRows = element.find('tr'); - totalWidth = element.children()[0].offsetWidth; scope.disableLeft = true; scope.disableRight = false; if (scope.numOfStickyCols > b2bHorizontalTableConfig.maxStickyColumns) { - throw new Error("Table can only support 3 sticky columns."); - } + throw new Error('Table can only support ' + b2bHorizontalTableConfig.maxStickyColumns + ' sticky columns.'); + } angular.forEach(tableRows, function(row, rowIndex) { - collectiveRowHeight.push(findMax(row.children, 'height')); for(var j = 0; j < row.children.length; j++) { if (tableColumns[j] === undefined) { tableColumns[j] = []; @@ -4029,38 +4155,52 @@ angular.module('b2b.att.horizontalTable', []) for (var i = scope.numOfStickyCols+1; i < tableColumns.length; i++) { angular.element(tableColumns[i]).css(displayBlockCSS); } - } + } + // We must calculate here as we need cells to be reset after re-render. + angular.forEach(tableRows, function(row, rowIndex) { + collectiveRowHeight.push(findMax(row.children, 'height')); // BUG: Keeping this here now causes row height bugs + }); + + console.log('tableColumns:', tableColumns); for (var i = 0; i < tableColumns.length; i++) { collectiveColumnWidth.push(findMax(tableColumns[i], 'width')); //offsetWidth doesn't take into account custom css inside } for(var i = 0; i < scope.numOfStickyCols; i++) { maxWidth += collectiveColumnWidth[i]; } - - stickyPixels = totalWidth-maxWidth; + // BUG: The code I put in to fix the table not spanning 100% width is now preventing + // table cells from laying out more than stickyPixels and thus we have weird wrapping + stickyPixels = totalWidth-maxWidth; // At this point, for each tr, I need to set the properties (height) and each numOfStickyCols children // should be set with sticky properties (margin-left and width) var width = maxWidth; + var additive = 0; + + if (angular.element(document).find('html').hasClass('isIE')) { + additive = 25; + } + var thObject = undefined; for(var i = 0; i < scope.numOfStickyCols; i++) { for (var j = 0; j < tableRows.length; j++) { - trObject = angular.element(tableRows[j].children[i]); - - angular.element(trObject).css({ + thObject = angular.element(tableRows[j].children[i]); + angular.element(thObject).css({ 'margin-left': -(width + 2) + 'px', 'width': (collectiveColumnWidth[i] + 3) + 'px', // instead of taking the max width, grab max width for that column - 'height': collectiveRowHeight[j] + 'px', + 'height': (collectiveRowHeight[j] + additive) + 'px', 'position': 'absolute', - 'background-color': 'lightgrey' + 'background-color': '#F2F2F2' }); } - - width -= collectiveColumnWidth[i]; } - + angular.element(tableRows[0]).css('height', collectiveRowHeight[0] + 'px'); + for(var i = 0; i < tableRows.length; i++) { + angular.element(tableRows[i]).css('height', (collectiveRowHeight[i] + additive) + 'px'); + } + innerContainer.css({ 'padding-left': (maxWidth + 2) + 'px' }); @@ -4074,23 +4214,93 @@ angular.module('b2b.att.horizontalTable', []) i = visibleColumns + 1; } + //columnSets = [[1, 1], [2,7]]; + updateCellDisplay(columnSets[setIndex]); checkScrollArrows(); scope.numOfCols = tableColumns.length; - console.log('Bulk Mode is ' + (attrs.bulkMode ? 'enabled': 'disabled')); - console.log('tableColumns', tableColumns); - console.log('collectiveColumnWidth: ', collectiveColumnWidth); - console.log('maxWidth: ', maxWidth); + console.log('collectiveColumnWidth', collectiveColumnWidth); + console.log('collectiveRowHeight: ', collectiveRowHeight); + } + + + // Let's get started with some math! + hack(); + + function calculateVisibleColumns(startingPoint) { + var usedWidth = 0, + visibleColumns = startingPoint || scope.numOfStickyCols; + + while(usedWidth < stickyPixels && visibleColumns < collectiveColumnWidth.length) { + if (usedWidth+collectiveColumnWidth[visibleColumns] > stickyPixels) { + if (startingPoint === visibleColumns) { + return visibleColumns; // The next cell is too large to fit, it should be only thing to fit + } + visibleColumns--; + return visibleColumns; + } + usedWidth += collectiveColumnWidth[visibleColumns]; + visibleColumns++; + } + + if (usedWidth > stickyPixels) { + return --visibleColumns; + } + return visibleColumns; + } + + function updateCellDisplay(set) { + for (var i = scope.numOfStickyCols; i < tableColumns.length; i++) { + angular.element(tableColumns[i]).css(displayNoneCSS); + } + + for (var i = set[0]; i <= set[1]; i++) { + angular.element(tableColumns[i]).css(displayBlockCSS); + } + } + + function findMax(arr, prop) { + var max = 0; + var localVal = 0; + var prevDisplay; + var item; + for (var i = 0; i < arr.length; i++) { + item = arr[i]; + prevDisplay = angular.element(item).css('display'); + + if (scope.$$phase) { + scope.$digest(); + } + // Remove inline styles, they will mess up calculations from original run + angular.element(item).css('height', ''); + angular.element(item).css('width', ''); + if (prop === 'width') { + // If we do not undo previous run's inline styles, this will grow widths on each re-render. + localVal = Math.ceil(parseInt(window.getComputedStyle(item).width.split('px')[0], 10)) + 30; // 30 px is padding + } else if (prop === 'offsetWidth') { + localVal = item.offsetWidth; + } else if (prop === 'height') { + //localVal = item.offsetHeight; + localVal = Math.ceil(parseInt(window.getComputedStyle(item).height.split('px')[0], 10)) + } + + if (localVal >= max) { + max = localVal; + } + } + + return max; } + + function checkScrollArrows() { scope.disableLeft = (setIndex === 0); scope.disableRight = !(setIndex < columnSets.length-1); } - scope.moveViewportLeft = function () { setIndex--; updateCellDisplay(columnSets[setIndex]); @@ -4517,29 +4727,13 @@ angular.module('b2b.att.leftNavigation', []) scope.idx = -1; scope.itemIdx = -1; scope.navIdx = -1; - scope.toggleNav = function (val,link) { - /**Added for ECOMP: make parent menu a link if no child menus.**/ - if(link!=null && link!=''){ - location.href = link; - return; - } - /**Ended**/ - if (val === scope.idx) { + scope.toggleNav = function (val) { + if (val === scope.idx) { scope.idx = -1; return; } scope.idx = val; }; - /*New function for ECOMP*/ - scope.toggleDrawer = function(showmenu){ - scope.idx=-1; /*hide the sunmenus*/ - if(showmenu){ - //scope.openList.length=0; - document.getElementById('page-content').style.marginLeft = "50px"; - } - else - document.getElementById('page-content').style.marginLeft = "250px"; - }; scope.liveLink = function (evt, val1, val2) { scope.itemIdx = val1; scope.navIdx = val2; @@ -4616,29 +4810,6 @@ angular.module('b2b.att.listbox', ['b2b.att.utilities']) 'listboxDataIndex': 0 }; - /*scope.$watch('currentIndex', function(oldVal, newVal) { - if (angular.equals(oldVal, newVal)) return; - if (!scope.multiselectable) { - // This doesn't garuntee anything. index will update on focus based on rules - currentIndexSet.listboxDataIndex = scope.currentIndex; - // Should this occur? - //scope.listboxData[currentIndexSet.listboxDataIndex].selected = true; - - // Update elementIndex - elements = elem.children(); - var indecies = Array.prototype.map.call(elements, function(item) { - return parseInt(angular.element(item).attr('data-index'), 10); - }).filter(function(item) { - return item === scope.currentIndex; - }); - currentIndex.elementIndex = indecies[0]; - //focusOnElement(currentIndexSet.elementIndex); // This isn't shifting focus - if (!scope.$$phase) { - scope.$apply(); - } - } - });*/ - function isTrue(item) { if (item.selected === true) { return true; @@ -4827,7 +4998,9 @@ angular.module('b2b.att.listbox', ['b2b.att.utilities']) // If no modifier keys are selected, all other items need to be unselected. prevDirection = undefined; selectItems(0, scope.listboxData.length, false); - scope.listboxData[currentIndexSet.listboxDataIndex].selected = true; + if(currentIndexSet.listboxDataIndex !== undefined && !isNaN(currentIndexSet.listboxDataIndex)){ + scope.listboxData[currentIndexSet.listboxDataIndex].selected = true; + } } focusOnElement(currentIndexSet.elementIndex); if(!scope.$$phase) { @@ -4861,7 +5034,9 @@ angular.module('b2b.att.listbox', ['b2b.att.utilities']) // If no modifier keys are selected, all other items need to be unselected. prevDirection = undefined; selectItems(0, scope.listboxData.length, false); - scope.listboxData[currentIndexSet.listboxDataIndex].selected = true; + if(currentIndexSet.listboxDataIndex !== undefined && !isNaN(currentIndexSet.listboxDataIndex)){ + scope.listboxData[currentIndexSet.listboxDataIndex].selected = true; + } } focusOnElement(currentIndexSet.elementIndex); @@ -5227,10 +5402,7 @@ angular.module('b2b.att.loaderAnimation', []) }); } }; -}]) - - -; +}]); /** * @ngdoc directive * @name Misc.att:messageWrapper @@ -5503,7 +5675,7 @@ angular.module('b2b.att.modalsAndAlerts', ['b2b.att.position', 'b2b.att.transiti }; }]) -.directive('b2bModalWindow', ['$timeout', 'windowOrientation', '$window', function ($timeout, windowOrientation, $window) { +.directive('b2bModalWindow', ['$timeout', 'windowOrientation', '$window', 'keymap', function ($timeout, windowOrientation, $window, keymap) { return { restrict: 'EA', scope: { @@ -5516,6 +5688,7 @@ angular.module('b2b.att.modalsAndAlerts', ['b2b.att.position', 'b2b.att.transiti scope.windowClass = attrs.windowClass || ''; scope.sizeClass = attrs.sizeClass || ''; scope.isNotifDialog = false; + scope.modalClose = attrs.modalClose || false; this.setTitle = function (title) { scope.title = title; @@ -5566,7 +5739,15 @@ angular.module('b2b.att.modalsAndAlerts', ['b2b.att.position', 'b2b.att.transiti }); } } - + + if(scope.modalClose){ + element.bind('keydown', function (e) { + if(e.keyCode == keymap.KEY.ESC){ + e.preventDefault(); + e.stopPropagation(); + } + }); + } } }; }]) @@ -5742,6 +5923,7 @@ angular.module('b2b.att.modalsAndAlerts', ['b2b.att.position', 'b2b.att.transiti angularDomEl.attr('window-class', modal.windowClass); angularDomEl.attr('size-class', modal.sizeClass); angularDomEl.attr('index', openedWindows.length() - 1); + angularDomEl.attr('modal-close', modal.modalClose); angularDomEl.html(modal.content); var modalDomEl = $compile(angularDomEl)(modal.scope); @@ -5882,7 +6064,8 @@ angular.module('b2b.att.modalsAndAlerts', ['b2b.att.position', 'b2b.att.transiti backdrop: modalOptions.backdrop, keyboard: modalOptions.keyboard, windowClass: modalOptions.windowClass, - sizeClass: modalOptions.sizeClass + sizeClass: modalOptions.sizeClass, + modalClose: modalOptions.modalClose }); }, function resolveError(reason) { @@ -5914,7 +6097,8 @@ angular.module('b2b.att.modalsAndAlerts', ['b2b.att.position', 'b2b.att.transiti modalOk: '&', modalCancel: '&', windowClass: '@', - sizeClass: '@' + sizeClass: '@', + modalClose: '@' }, link: function (scope, elm, attr) { elm.bind('click', function (ev) { @@ -5927,7 +6111,8 @@ angular.module('b2b.att.modalsAndAlerts', ['b2b.att.position', 'b2b.att.transiti templateUrl: scope.b2bModal, controller: scope.modalController, windowClass: scope.windowClass, - sizeClass: scope.sizeClass + sizeClass: scope.sizeClass, + modalClose: scope.modalClose }).result.then(function (value) { scope.modalOk({ value: value @@ -7001,6 +7186,7 @@ angular.module('b2b.att.multiLevelNavigation', ['b2b.att.utilities']) } //for any expandable tree item on click var toggleState = function (e) { + if (angular.element(e.target).attr("b2b-ml-nav") !== "endNode") { var eLink = element.find('a').eq(0); if (eLink.hasClass('active')) { @@ -7087,6 +7273,14 @@ angular.module('b2b.att.multiLevelNavigation', ['b2b.att.utilities']) if(element.attr("b2b-ml-nav") !== "endNode") { toggleState(e); } + if (rootE==undefined){ + findRoot(element); + } + var currSelected = rootE.parent()[0].querySelector('.selected'); + if(currSelected){ + angular.element(currSelected).removeClass('selected'); + } + element.find('a').eq(0).addClass('selected'); e.stopPropagation(); }); element.bind('focus', function (e) { @@ -7808,20 +8002,18 @@ angular.module('b2b.att.phoneNumberInput', ['ngMessages', 'b2b.att.utilities']) phoneMaskDot: '___.___.____', phoneMaskHyphen: '___-___-____' }) - .directive('b2bPhoneMask', ['$parse', 'CoreFormsUiConfig', 'keymap', function ($parse, CoreFormsUiConfig, keymap) { + .directive('b2bPhoneMask', ['$parse', 'CoreFormsUiConfig', 'keymap', 'b2bUserAgent', function ($parse, CoreFormsUiConfig, keymap, b2bUserAgent) { return { require: 'ngModel', scope: { ngModel: '=' }, link: function (scope, iElement, iAttrs, ctrl) { - var navigatorAgent = navigator.userAgent.toLowerCase(), - isAndroid = navigatorAgent.indexOf("android") > -1, - oldIE = navigatorAgent.indexOf('msie 8.0') !== -1; + var mask = ''; var validPhoneNumber = false; var currentKey = ''; - if (isAndroid) { + if (b2bUserAgent.isMobile()) { mask = "__________"; } else { switch (iAttrs.b2bPhoneMask) { @@ -7912,13 +8104,13 @@ angular.module('b2b.att.phoneNumberInput', ['ngMessages', 'b2b.att.utilities']) // Allow: Ctrl+C/c (!(e.ctrlKey) && (e.which !== '99' || e.which !== '67')) && // Allow: Ctrl+X/x - (!(e.ctrlKey) && (e.which !== '120' || e.which !== '88'))) { + (!(e.ctrlKey) && (e.which !== '120' || e.which !== '88')) && + /* 229 key code will sent as placeholder key for andriod devices */ + (e.which != 229 )) { e.preventDefault ? e.preventDefault() : e.returnValue = false; - iElement.attr("aria-label", "Only numbers are allowed"); validPhoneNumber = false; } } else { - iElement.removeAttr("aria-label"); validPhoneNumber = true; } @@ -8771,38 +8963,6 @@ angular.module('b2b.att.seekBar', ['b2b.att.utilities','b2b.att.position']) } }; }]); -/** - * @ngdoc directive - * @name Forms.att:selectorModule - * - * @description - * - * - * @usage - * - * - * - * - * - * - * @example - *
    - - - - -
    - * - */ -angular.module('b2b.att.selectorModule', ['b2b.att.dropdowns']); /** * @ngdoc directive * @name Layouts.att:separators @@ -9372,7 +9532,7 @@ angular.module('b2b.att.staticRouteTemplate', ['b2b.att.utilities']) * @name Progress & usage indicators.att:statusTracker * * @scope - * @param {array} statuses - An array of status objects + * @param {array} statusObject - An array of status objects that accept heading, estimate, description and state * @description * * @@ -9384,7 +9544,6 @@ angular.module('b2b.att.staticRouteTemplate', ['b2b.att.utilities']) * @example
    - HTML + AngularJS @@ -9394,7 +9553,14 @@ angular.module('b2b.att.staticRouteTemplate', ['b2b.att.utilities']) angular.module('b2b.att.statusTracker', ['b2b.att.utilities']) .constant('b2bStatusTrackerConfig', { - 'maxViewItems': 3 + 'maxViewItems': 3, + 'icons': { + 'complete': 'icoControls-approval', + 'current': 'icon-misc-time', + 'pending': 'icoControls-statusokay', + 'actionRequired': 'icon-primary-securityalerts-alert', + 'notAvailable': 'icoControls-restricted' + } }) .directive('b2bStatusTracker', ['b2bStatusTrackerConfig', function(b2bStatusTrackerConfig) { return { @@ -9424,19 +9590,10 @@ angular.module('b2b.att.statusTracker', ['b2b.att.utilities']) scope.isInViewport = function(index) { return (index < scope.currentViewIndex+3 && index >= scope.currentViewIndex); // && index > scope.currentViewIndex-2 }; - scope.currentStatus = function(index) { - if(index != undefined){ - if(!scope.statuses[index].complete) { - if(index > 0 && scope.statuses[index-1].complete) { - return true; - } else if(index == 0 && !scope.statuses[index].complete){ - return true; - } else { - return false; - } - } - } - }; + + scope.removeCamelCase = function(str) { + return str.replace(/([a-z])([A-Z])/g, '$1 $2').toLowerCase(); + } } }; }]); @@ -9702,7 +9859,7 @@ angular.module('b2b.att.tableScrollbar', []) firstColumn = angular.element(trObject.children()[0]); angular.element(firstColumn).css({ - 'margin-left': -(firstColumnWidth + 2) + 'px', + 'left': '0px', 'width': (firstColumnWidth + 2) + 'px', 'position': 'absolute' }); @@ -9754,6 +9911,7 @@ angular.module('b2b.att.tableScrollbar', []) } }; + innerContainer.bind('scroll', function () { $timeout(function () { scope.checkScrollArrows(); @@ -10038,7 +10196,7 @@ angular.module('b2b.att.tables', ['b2b.att.utilities']) } else if (attr.type === 'body') { var html = elem.children(); if (attr.rowRepeat) { - html.attr('ng-repeat', attr.rowRepeat.concat(" | orderBy : columnIndex : reverse | filter : searchCriteria : false ")); + html.attr('ng-repeat', attr.rowRepeat.concat(" | orderBy : (reverse?'-':'')+ columnIndex | filter : searchCriteria : false ")); } html.attr('ng-class', "{'odd': $odd && zebraStripFlag}"); html.attr('class', 'data-row'); @@ -10581,6 +10739,51 @@ angular.module('b2b.att.textArea', ['b2b.att.utilities']) }; }]); +/** + * @ngdoc directive + * @name Forms.att:timeInputField + * + * @description + * + * + + * @example + *
    + + + + +
    + * + */ +angular.module('b2b.att.timeInputField',['ngMessages', 'b2b.att.utilities']).directive('b2bTimeFormat',function(){ + return{ + restrict : 'A', + require : '^ngModel', + link : function(scope,elem,attr,ctrl){ + elem.on('keyup',function(evt){ + var modelValue = ctrl.$modelValue; + var format = attr.b2bTimeFormat; + modelValue = modelValue.split(':'); + if(format == "12"){ + if(!(modelValue[0] <= 12 && modelValue[0] > 0 ) || !(modelValue[1] <= 59)){ + ctrl.$setValidity('inValidTime',false); + }else{ + ctrl.$setValidity('inValidTime',true); + } + }else if(format =="24"){ + if(!(modelValue[0] <= 23) || !(modelValue[1] <= 59)){ + ctrl.$setValidity('inValidTime',false); + }else{ + ctrl.$setValidity('inValidTime',true); + } + } + scope.$apply(); + }); + } + } +}); + /** * @ngdoc directive * @name Forms.att:tooltipsForForms @@ -19400,7 +19603,7 @@ angular.module('b2b.att.utilities', ['ngSanitize']) searchText = searchText.toLowerCase(); angular.forEach(items, function(item) { angular.forEach(attrs, function(attr) { - if (item.hasOwnProperty(attr) && item[attr].toLowerCase().includes(searchText)) { + if (item.hasOwnProperty(attr) && (item[attr].toLowerCase().indexOf(searchText) != -1)) { filtered.push(item); return; } @@ -19820,6 +20023,18 @@ https://github.com/cwilso/AudioRecorder/blob/master/js/recorderjs/recorder.js }); }; + var _touch = function (flag, callbackFunc, scope) { + scope.$watch(flag, function (val) { + $timeout(function () { + if (val) { + $document.bind('touchstart', callbackFunc); + } else { + $document.unbind('touchstart', callbackFunc); + } + }); + }); + }; + var _scroll = function (flag, callbackFunc, scope) { scope.$watch(flag, function (val) { $timeout(function () { @@ -19863,6 +20078,7 @@ https://github.com/cwilso/AudioRecorder/blob/master/js/recorderjs/recorder.js return { click: _click, + touch: _touch, scroll: _scroll, event: _event }; @@ -21014,6 +21230,690 @@ https://github.com/cwilso/AudioRecorder/blob/master/js/recorderjs/recorder.js }; }]) +.constant('b2bMaskConfig', { + maskDefinitions: { + '9': /\d/, + 'A': /[a-zA-Z]/, + '*': /[a-zA-Z0-9]/ + }, + clearOnBlur: false, + clearOnBlurPlaceholder: false, + escChar: '\\', + eventsToHandle: ['input', 'keyup', 'click', 'focus'], + addDefaultPlaceholder: true, + allowInvalidValue: true +}) +/** + * @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. + * @param {String} maskPlaceholder - Allows customizing the mask placeholder when a user has focused the input element and while typing in their value + * @param {String} maskPlaceholderChar - Allows customizing the mask placeholder character. The default mask placeholder is _. + * @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. + */ +.directive('b2bMask', ['b2bMaskConfig', function(b2bMaskConfig) { + return { + require: 'ngModel', + restrict: 'A', + link: function(scope, element, attrs, ctrl) { + var maskProcessed = false, eventsBound = false, + maskCaretMap, maskPatterns, maskPlaceholder, maskComponents, + // Minimum required length of the value to be considered valid + minRequiredLength, + value, valueMasked, isValid, + // Vars for initializing/uninitializing + originalPlaceholder = attrs.placeholder, + originalMaxlength = attrs.maxlength, + // Vars used exclusively in eventHandler() + oldValue, oldValueUnmasked, oldCaretPosition, oldSelectionLength, + // Used for communicating if a backspace operation should be allowed between + // keydownHandler and eventHandler + preventBackspace; + + var options = b2bMaskConfig; + + function isFocused (elem) { + return elem === document.activeElement && (!document.hasFocus || document.hasFocus()) && !!(elem.type || elem.href || ~elem.tabIndex); + } + + var originalIsEmpty = ctrl.$isEmpty; + ctrl.$isEmpty = function(value) { + if (maskProcessed) { + return originalIsEmpty(unmaskValue(value || '')); + } else { + return originalIsEmpty(value); + } + }; + + function initialize(maskAttr) { + if (!angular.isDefined(maskAttr)) { + return uninitialize(); + } + processRawMask(maskAttr); + if (!maskProcessed) { + return uninitialize(); + } + initializeElement(); + bindEventListeners(); + return true; + } + + function initPlaceholder(placeholderAttr) { + if ( ! placeholderAttr) { + return; + } + maskPlaceholder = placeholderAttr; + /* If the mask is processed, then we need to update the value + but don't set the value if there is nothing entered into the element + and there is a placeholder attribute on the element because that + will only set the value as the blank maskPlaceholder + and override the placeholder on the element */ + if (maskProcessed && !(element.val().length === 0 && angular.isDefined(attrs.placeholder))) { + element.val(maskValue(unmaskValue(element.val()))); + } + } + + function initPlaceholderChar() { + return initialize(attrs.uiMask); + } + + var modelViewValue = false; + + attrs.$observe('modelViewValue', function(val) { + if (val === 'true') { + modelViewValue = true; + } + }); + + attrs.$observe('allowInvalidValue', function(val) { + linkOptions.allowInvalidValue = val === ''? true : !!val; + formatter(ctrl.$modelValue); + }); + + function formatter(fromModelValue) { + if (!maskProcessed) { + return fromModelValue; + } + value = unmaskValue(fromModelValue || ''); + isValid = validateValue(value); + ctrl.$setValidity('mask', isValid); + + if (!value.length) return undefined; + if (isValid || linkOptions.allowInvalidValue) { + return maskValue(value); + } else { + return undefined; + } + } + + function parser(fromViewValue) { + if (!maskProcessed) { + return fromViewValue; + } + value = unmaskValue(fromViewValue || ''); + isValid = validateValue(value); + /* We have to set viewValue manually as the reformatting of the input + value performed by eventHandler() doesn't happen until after + this parser is called, which causes what the user sees in the input + to be out-of-sync with what the ctrl's $viewValue is set to. */ + ctrl.$viewValue = value.length ? maskValue(value) : ''; + ctrl.$setValidity('mask', isValid); + + if (isValid || linkOptions.allowInvalidValue) { + return modelViewValue ? ctrl.$viewValue : value; + } + } + + var linkOptions = {}; + + // to do + if (attrs.b2bMaskOptions) { + linkOptions = scope.$eval('[' + attrs.b2bMaskOptions + ']'); + if (angular.isObject(linkOptions[0])) { + // we can't use angular.copy nor angular.extend, they lack the power to do a deep merge + linkOptions = (function(original, current) { + for (var i in original) { + if (Object.prototype.hasOwnProperty.call(original, i)) { + if (current[i] === undefined) { + current[i] = angular.copy(original[i]); + } else { + if (angular.isObject(current[i]) && !angular.isArray(current[i])) { + current[i] = angular.extend({}, original[i], current[i]); + } + } + } + } + return current; + })(options, linkOptions[0]); + } else { + linkOptions = options; //gotta be a better way to do this.. + } + } else { + linkOptions = options; + } + + attrs.$observe('b2bMask', initialize); + if (angular.isDefined(attrs.maskPlaceholder)) { + attrs.$observe('maskPlaceholder', initPlaceholder); + } + else { + attrs.$observe('placeholder', initPlaceholder); + } + if (angular.isDefined(attrs.maskPlaceholderChar)) { + attrs.$observe('maskPlaceholderChar', initPlaceholderChar); + } + + ctrl.$formatters.unshift(formatter); + ctrl.$parsers.unshift(parser); + + function uninitialize() { + maskProcessed = false; + unbindEventListeners(); + + if (angular.isDefined(originalPlaceholder)) { + element.attr('placeholder', originalPlaceholder); + } else { + element.removeAttr('placeholder'); + } + + if (angular.isDefined(originalMaxlength)) { + element.attr('maxlength', originalMaxlength); + } else { + element.removeAttr('maxlength'); + } + + element.val(ctrl.$modelValue); + ctrl.$viewValue = ctrl.$modelValue; + return false; + } + + function initializeElement() { + value = oldValueUnmasked = unmaskValue(ctrl.$modelValue || ''); + valueMasked = oldValue = maskValue(value); + isValid = validateValue(value); + if (attrs.maxlength) { // Double maxlength to allow pasting new val at end of mask + element.attr('maxlength', maskCaretMap[maskCaretMap.length - 1] * 2); + } + if ( ! originalPlaceholder && linkOptions.addDefaultPlaceholder) { + element.attr('placeholder', maskPlaceholder); + } + var viewValue = ctrl.$modelValue; + var idx = ctrl.$formatters.length; + while(idx--) { + viewValue = ctrl.$formatters[idx](viewValue); + } + ctrl.$viewValue = viewValue || ''; + ctrl.$render(); + } + + function bindEventListeners() { + if (eventsBound) { + return; + } + element.bind('blur', blurHandler); + element.bind('mousedown mouseup', mouseDownUpHandler); + element.bind('keydown', keydownHandler); + element.bind(linkOptions.eventsToHandle.join(' '), eventHandler); + eventsBound = true; + } + + function unbindEventListeners() { + if (!eventsBound) { + return; + } + element.unbind('blur', blurHandler); + element.unbind('mousedown', mouseDownUpHandler); + element.unbind('mouseup', mouseDownUpHandler); + element.unbind('keydown', keydownHandler); + element.unbind('input', eventHandler); + element.unbind('keyup', eventHandler); + element.unbind('click', eventHandler); + element.unbind('focus', eventHandler); + eventsBound = false; + } + + function validateValue(value) { + // Zero-length value validity is ngRequired's determination + return value.length ? value.length >= minRequiredLength : true; + } + + function unmaskValue(value) { + var valueUnmasked = '', + input = element[0], + maskPatternsCopy = maskPatterns.slice(), + selectionStart = oldCaretPosition, + selectionEnd = selectionStart + getSelectionLength(input), + valueOffset, valueDelta, tempValue = ''; + // Preprocess by stripping mask components from value + value = value.toString(); + valueOffset = 0; + valueDelta = value.length - maskPlaceholder.length; + angular.forEach(maskComponents, function(component) { + var position = component.position; + //Only try and replace the component if the component position is not within the selected range + //If component was in selected range then it was removed with the user input so no need to try and remove that component + if (!(position >= selectionStart && position < selectionEnd)) { + if (position >= selectionStart) { + position += valueDelta; + } + if (value.substring(position, position + component.value.length) === component.value) { + tempValue += value.slice(valueOffset, position);// + value.slice(position + component.value.length); + valueOffset = position + component.value.length; + } + } + }); + value = tempValue + value.slice(valueOffset); + angular.forEach(value.split(''), function(chr) { + if (maskPatternsCopy.length && maskPatternsCopy[0].test(chr)) { + valueUnmasked += chr; + maskPatternsCopy.shift(); + } + }); + + return valueUnmasked; + } + + function maskValue(unmaskedValue) { + var valueMasked = '', + maskCaretMapCopy = maskCaretMap.slice(); + + angular.forEach(maskPlaceholder.split(''), function(chr, i) { + if (unmaskedValue.length && i === maskCaretMapCopy[0]) { + valueMasked += unmaskedValue.charAt(0) || '_'; + unmaskedValue = unmaskedValue.substr(1); + maskCaretMapCopy.shift(); + } + else { + valueMasked += chr; + } + }); + return valueMasked; + } + + function getPlaceholderChar(i) { + var placeholder = angular.isDefined(attrs.uiMaskPlaceholder) ? attrs.uiMaskPlaceholder : attrs.placeholder, + defaultPlaceholderChar; + + if (angular.isDefined(placeholder) && placeholder[i]) { + return placeholder[i]; + } else { + defaultPlaceholderChar = angular.isDefined(attrs.uiMaskPlaceholderChar) && attrs.uiMaskPlaceholderChar ? attrs.uiMaskPlaceholderChar : '_'; + return (defaultPlaceholderChar.toLowerCase() === 'space') ? ' ' : defaultPlaceholderChar[0]; + } + } + + /* Generate array of mask components that will be stripped from a masked value + before processing to prevent mask components from being added to the unmasked value. + E.g., a mask pattern of '+7 9999' won't have the 7 bleed into the unmasked value. */ + function getMaskComponents() { + var maskPlaceholderChars = maskPlaceholder.split(''), + maskPlaceholderCopy, components; + + /* maskCaretMap can have bad values if the input has the ui-mask attribute implemented as an obversable property, e.g. the demo page */ + if (maskCaretMap && !isNaN(maskCaretMap[0])) { + /* Instead of trying to manipulate the RegEx based on the placeholder characters + we can simply replace the placeholder characters based on the already built + maskCaretMap to underscores and leave the original working RegEx to get the proper + mask components */ + angular.forEach(maskCaretMap, function(value) { + maskPlaceholderChars[value] = '_'; + }); + } + maskPlaceholderCopy = maskPlaceholderChars.join(''); + components = maskPlaceholderCopy.replace(/[_]+/g, '_').split('_'); + components = components.filter(function(s) { + return s !== ''; + }); + + /* need a string search offset in cases where the mask contains multiple identical components + E.g., a mask of 99.99.99-999.99 */ + var offset = 0; + return components.map(function(c) { + var componentPosition = maskPlaceholderCopy.indexOf(c, offset); + offset = componentPosition + 1; + return { + value: c, + position: componentPosition + }; + }); + } + + function processRawMask(mask) { + var characterCount = 0; + + maskCaretMap = []; + maskPatterns = []; + maskPlaceholder = ''; + + if (angular.isString(mask)) { + minRequiredLength = 0; + + var isOptional = false, + numberOfOptionalCharacters = 0, + splitMask = mask.split(''); + + var inEscape = false; + angular.forEach(splitMask, function(chr, i) { + if (inEscape) { + inEscape = false; + maskPlaceholder += chr; + characterCount++; + } + else if (linkOptions.escChar === chr) { + inEscape = true; + } + else if (linkOptions.maskDefinitions[chr]) { + maskCaretMap.push(characterCount); + + maskPlaceholder += getPlaceholderChar(i - numberOfOptionalCharacters); + maskPatterns.push(linkOptions.maskDefinitions[chr]); + + characterCount++; + if (!isOptional) { + minRequiredLength++; + } + + isOptional = false; + } + else if (chr === '?') { + isOptional = true; + numberOfOptionalCharacters++; + } + else { + maskPlaceholder += chr; + characterCount++; + } + }); + } + // Caret position immediately following last position is valid. + maskCaretMap.push(maskCaretMap.slice().pop() + 1); + + maskComponents = getMaskComponents(); + maskProcessed = maskCaretMap.length > 1 ? true : false; + } + + var prevValue = element.val(); + function blurHandler() { + if (linkOptions.clearOnBlur || ((linkOptions.clearOnBlurPlaceholder) && (value.length === 0) && attrs.placeholder)) { + oldCaretPosition = 0; + oldSelectionLength = 0; + if (!isValid || value.length === 0) { + valueMasked = ''; + element.val(''); + scope.$apply(function() { + //only $setViewValue when not $pristine to avoid changing $pristine state. + if (!ctrl.$pristine) { + ctrl.$setViewValue(''); + } + }); + } + } + //Check for different value and trigger change. + if (value !== prevValue) { + var currentVal = element.val(); + var isTemporarilyEmpty = value === '' && currentVal && angular.isDefined(attrs.uiMaskPlaceholderChar) && attrs.uiMaskPlaceholderChar === 'space'; + if(isTemporarilyEmpty) { + element.val(''); + } + triggerChangeEvent(element[0]); + if(isTemporarilyEmpty) { + element.val(currentVal); + } + } + prevValue = value; + } + + function triggerChangeEvent(element) { + var change; + if (angular.isFunction(window.Event) && !element.fireEvent) { + // modern browsers and Edge + try { + change = new Event('change', { + view: window, + bubbles: true, + cancelable: false + }); + } catch (ex) { + //this is for certain mobile browsers that have the Event object + //but don't support the Event constructor + change = document.createEvent('HTMLEvents'); + change.initEvent('change', false, true); + } finally { + element.dispatchEvent(change); + } + } else if ('createEvent' in document) { + // older browsers + change = document.createEvent('HTMLEvents'); + change.initEvent('change', false, true); + element.dispatchEvent(change); + } + else if (element.fireEvent) { + // IE <= 11 + element.fireEvent('onchange'); + } + } + + function mouseDownUpHandler(e) { + if (e.type === 'mousedown') { + element.bind('mouseout', mouseoutHandler); + } else { + element.unbind('mouseout', mouseoutHandler); + } + } + + element.bind('mousedown mouseup', mouseDownUpHandler); + + function mouseoutHandler() { + oldSelectionLength = getSelectionLength(this); + element.unbind('mouseout', mouseoutHandler); + } + + function keydownHandler(e) { + var isKeyBackspace = e.which === 8, + caretPos = getCaretPosition(this) - 1 || 0, //value in keydown is pre change so bump caret position back to simulate post change + isCtrlZ = e.which === 90 && e.ctrlKey; //ctrl+z pressed + + if (isKeyBackspace) { + while(caretPos >= 0) { + if (isValidCaretPosition(caretPos)) { + //re-adjust the caret position. + //Increment to account for the initial decrement to simulate post change caret position + setCaretPosition(this, caretPos + 1); + break; + } + caretPos--; + } + preventBackspace = caretPos === -1; + } + + if (isCtrlZ) { + // prevent IE bug - value should be returned to initial state + element.val(''); + e.preventDefault(); + } + } + + function eventHandler(e) { + e = e || {}; + // Allows more efficient minification + var eventWhich = e.which, + eventType = e.type; + + // Prevent shift and ctrl from mucking with old values + if (eventWhich === 16 || eventWhich === 91) { + return; + } + + var val = element.val(), + valOld = oldValue, + valMasked, + valAltered = false, + valUnmasked = unmaskValue(val), + valUnmaskedOld = oldValueUnmasked, + caretPos = getCaretPosition(this) || 0, + caretPosOld = oldCaretPosition || 0, + caretPosDelta = caretPos - caretPosOld, + caretPosMin = maskCaretMap[0], + caretPosMax = maskCaretMap[valUnmasked.length] || maskCaretMap.slice().shift(), + selectionLenOld = oldSelectionLength || 0, + isSelected = getSelectionLength(this) > 0, + wasSelected = selectionLenOld > 0, + // Case: Typing a character to overwrite a selection + isAddition = (val.length > valOld.length) || (selectionLenOld && val.length > valOld.length - selectionLenOld), + // Case: Delete and backspace behave identically on a selection + isDeletion = (val.length < valOld.length) || (selectionLenOld && val.length === valOld.length - selectionLenOld), + isSelection = (eventWhich >= 37 && eventWhich <= 40) && e.shiftKey, // Arrow key codes + + isKeyLeftArrow = eventWhich === 37, + // Necessary due to "input" event not providing a key code + isKeyBackspace = eventWhich === 8 || (eventType !== 'keyup' && isDeletion && (caretPosDelta === -1)), + isKeyDelete = eventWhich === 46 || (eventType !== 'keyup' && isDeletion && (caretPosDelta === 0) && !wasSelected), + // Handles cases where caret is moved and placed in front of invalid maskCaretMap position. Logic below + // ensures that, on click or leftward caret placement, caret is moved leftward until directly right of + // non-mask character. Also applied to click since users are (arguably) more likely to backspace + // a character when clicking within a filled input. + caretBumpBack = (isKeyLeftArrow || isKeyBackspace || eventType === 'click') && caretPos > caretPosMin; + + oldSelectionLength = getSelectionLength(this); + + // These events don't require any action + if (isSelection || (isSelected && (eventType === 'click' || eventType === 'keyup' || eventType === 'focus'))) { + return; + } + + if (isKeyBackspace && preventBackspace) { + element.val(maskPlaceholder); + // This shouldn't be needed but for some reason after aggressive backspacing the ctrl $viewValue is incorrect. + // This keeps the $viewValue updated and correct. + scope.$apply(function () { + ctrl.$setViewValue(''); // $setViewValue should be run in angular context, otherwise the changes will be invisible to angular and user code. + }); + setCaretPosition(this, caretPosOld); + return; + } + + // User attempted to delete but raw value was unaffected--correct this grievous offense + if ((eventType === 'input') && isDeletion && !wasSelected && valUnmasked === valUnmaskedOld) { + while (isKeyBackspace && caretPos > caretPosMin && !isValidCaretPosition(caretPos)) { + caretPos--; + } + while (isKeyDelete && caretPos < caretPosMax && maskCaretMap.indexOf(caretPos) === -1) { + caretPos++; + } + var charIndex = maskCaretMap.indexOf(caretPos); + // Strip out non-mask character that user would have deleted if mask hadn't been in the way. + valUnmasked = valUnmasked.substring(0, charIndex) + valUnmasked.substring(charIndex + 1); + + // If value has not changed, don't want to call $setViewValue, may be caused by IE raising input event due to placeholder + if (valUnmasked !== valUnmaskedOld) + valAltered = true; + } + + // Update values + valMasked = maskValue(valUnmasked); + + oldValue = valMasked; + oldValueUnmasked = valUnmasked; + + //additional check to fix the problem where the viewValue is out of sync with the value of the element. + //better fix for commit 2a83b5fb8312e71d220a497545f999fc82503bd9 (I think) + if (!valAltered && val.length > valMasked.length) + valAltered = true; + + element.val(valMasked); + + //we need this check. What could happen if you don't have it is that you'll set the model value without the user + //actually doing anything. Meaning, things like pristine and touched will be set. + if (valAltered) { + scope.$apply(function () { + ctrl.$setViewValue(valMasked); // $setViewValue should be run in angular context, otherwise the changes will be invisible to angular and user code. + }); + } + + // Caret Repositioning + // Ensure that typing always places caret ahead of typed character in cases where the first char of + // the input is a mask char and the caret is placed at the 0 position. + if (isAddition && (caretPos <= caretPosMin)) { + caretPos = caretPosMin + 1; + } + + if (caretBumpBack) { + caretPos--; + } + + // Make sure caret is within min and max position limits + caretPos = caretPos > caretPosMax ? caretPosMax : caretPos < caretPosMin ? caretPosMin : caretPos; + + // Scoot the caret back or forth until it's in a non-mask position and within min/max position limits + while (!isValidCaretPosition(caretPos) && caretPos > caretPosMin && caretPos < caretPosMax) { + caretPos += caretBumpBack ? -1 : 1; + } + + if ((caretBumpBack && caretPos < caretPosMax) || (isAddition && !isValidCaretPosition(caretPosOld))) { + caretPos++; + } + oldCaretPosition = caretPos; + setCaretPosition(this, caretPos); + } + + function isValidCaretPosition(pos) { + return maskCaretMap.indexOf(pos) > -1; + } + + function getCaretPosition(input) { + if (!input) + return 0; + if (input.selectionStart !== undefined) { + return input.selectionStart; + } else if (document.selection) { + if (isFocused(element[0])) { + // For IE + input.focus(); + var selection = document.selection.createRange(); + selection.moveStart('character', input.value ? -input.value.length : 0); + return selection.text.length; + } + } + return 0; + } + + function setCaretPosition(input, pos) { + if (!input) + return 0; + if (input.offsetWidth === 0 || input.offsetHeight === 0) { + return; // Input's hidden + } + if (input.setSelectionRange) { + if (isFocused(element[0])) { + input.focus(); + input.setSelectionRange(pos, pos); + } + } + else if (input.createTextRange) { + // For IE + var range = input.createTextRange(); + range.collapse(true); + range.moveEnd('character', pos); + range.moveStart('character', pos); + range.select(); + } + } + + function getSelectionLength(input) { + if (!input) + return 0; + if (input.selectionStart !== undefined) { + return (input.selectionEnd - input.selectionStart); + } + if (window.getSelection) { + return (window.getSelection().toString().length); + } + if (document.selection) { + return (document.selection.createRange().text.length); + } + return 0; + } + } + }; +}]) .filter('b2bMultiSepartorHighlight', function($sce) { return function(text, searchText, searchSeperator) { var splitText = function(string) { @@ -21051,10 +21951,20 @@ https://github.com/cwilso/AudioRecorder/blob/master/js/recorderjs/recorder.js .factory('b2bUserAgent', [function() { var _isMobile = function() { - return /Android|webOS|iPhone|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent); + if(/Android/i.test(navigator.userAgent)){ + return /Mobile/i.test(navigator.userAgent); + }else{ + return /Android|webOS|iPhone|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent); + } + }; var _notMobile = function() { - return !/Android|webOS|iPhone|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent); + if(/Android/i.test(navigator.userAgent)){ + return !/Mobile/i.test(navigator.userAgent); + }else{ + return !/Android|webOS|iPhone|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent); + } + }; var _isIE = function() { return /msie|trident/i.test(navigator.userAgent); @@ -21280,9 +22190,9 @@ angular.module("b2bTemplate/calendar/datepicker-popup.html", []).run(["$template " \n" + " \n" + " \n" + - " \n" + + " \n" + " \n" + - " \n" + + " \n" + " \n" + " 0\">\n" + " \n" + @@ -21336,7 +22246,7 @@ angular.module("b2bTemplate/coachmark/coachmark.html", []).run(["$templateCache" angular.module("b2bTemplate/dropdowns/b2bDropdownDesktop.html", []).run(["$templateCache", function($templateCache) { $templateCache.put("b2bTemplate/dropdowns/b2bDropdownDesktop.html", "\n" + - " \n" + + " \n" + " \n" + "
    \n" + "
      \n" + @@ -21347,7 +22257,7 @@ angular.module("b2bTemplate/dropdowns/b2bDropdownDesktop.html", []).run(["$templ " \n" + "
      \n" + "\n" + - "
      "); + ""); }]); angular.module("b2bTemplate/dropdowns/b2bDropdownGroupDesktop.html", []).run(["$templateCache", function($templateCache) { @@ -21359,7 +22269,7 @@ angular.module("b2bTemplate/dropdowns/b2bDropdownGroupDesktop.html", []).run(["$ angular.module("b2bTemplate/dropdowns/b2bDropdownListDesktop.html", []).run(["$templateCache", function($templateCache) { $templateCache.put("b2bTemplate/dropdowns/b2bDropdownListDesktop.html", - "
    • "); + "
    • "); }]); angular.module("b2bTemplate/fileUpload/fileUpload.html", []).run(["$templateCache", function($templateCache) { @@ -21415,16 +22325,30 @@ angular.module("b2bTemplate/horizontalTable/horizontalTable.html", []).run(["$te $templateCache.put("b2bTemplate/horizontalTable/horizontalTable.html", "
      \n" + "
      \n" + - "
      \n" + - " \n" + + "
      \n" + + " Previous Set\n" + + " Previous Set\n" + "
      \n" + " \n" + " \n" + " Displaying {{getColumnSet()[0]}} - {{getColumnSet()[1]}} of {{numOfCols}} (total) columns\n" + " \n" + + "\n" + + "
      \n" + + " \n" + + "
      \n" + + " Legend\n" + + " \n" + + "
      \n" + + " \n" + + "
      \n" + + "
      \n" + + "
      \n" + + "
      \n" + " \n" + - "
      \n" + - " \n" + + "
      \n" + + " Next Set\n" + + " Next Set\n" + "
      \n" + "
      \n" + "
      \n" + @@ -21449,7 +22373,7 @@ angular.module("b2bTemplate/hourPicker/b2bHourpickerPanel.html", []).run(["$temp $templateCache.put("b2bTemplate/hourPicker/b2bHourpickerPanel.html", "
      \n" + "
      \n" + - "
      \n" + @@ -21503,8 +22427,8 @@ angular.module("b2bTemplate/hourPicker/b2bHourpickerValue.html", []).run(["$temp "
      \n" + " {{hourpickerValue.days}}   {{hourpickerValue.startTime}} {{hourpickerValue.startMeridiem}} - {{hourpickerValue.endTime}} {{hourpickerValue.endMeridiem}}\n" + " \n" + - " \n" + - " \n" + + " \n" + + " \n" + " \n" + "
      \n" + "
      "); @@ -21512,26 +22436,13 @@ angular.module("b2bTemplate/hourPicker/b2bHourpickerValue.html", []).run(["$temp angular.module("b2bTemplate/leftNavigation/leftNavigation.html", []).run(["$templateCache", function($templateCache) { $templateCache.put("b2bTemplate/leftNavigation/leftNavigation.html", - "
      \n" + + "
      \n" + "
      \n" + " \n" + "
      \n" + @@ -21616,20 +22527,22 @@ angular.module("b2bTemplate/pagination/b2b-pagination.html", []).run(["$template " 10\" ng-class=\"currentPage <= 1 ? 'b2b-pager__item--prev-disabled': '' \">\n" + " \n" + " \n" + - " 10 && currentPage > 6\" b2b-accessibility-click=\"13,32\" ng-click=\"selectPage(1, $event)\">\n" + + " 10 && currentPage > 6\" b2b-accessibility-click=\"13,32\" ng-click=\"selectPage(1, $event)\">\n" + " 1 is selected\n" + " \n" + - " 10 && currentPage > 6\" b2b-accessibility-click=\"13,32\" ng-click=\"selectPage(2, $event)\">2 is selected\n" + + " 10 && currentPage > 6\" b2b-accessibility-click=\"13,32\" ng-click=\"selectPage(2, $event)\">\n" + + " 2 is selected\n" + "\n" + " 10 && currentPage > 6\">...\n" + "\n" + - " {{page}} is selected\n" + + " \n" + + " {{page}} is selected\n" + "\n" + " 10 && currentPage <= totalPages - 5\">...\n" + "\n" + - " 10 && currentPage <= totalPages - 5\" b2b-accessibility-click=\"13,32\" ng-click=\"selectPage(totalPages-1, $event)\">{{totalPages-1}} is selected\n" + + " 10 && currentPage <= totalPages - 5\" b2b-accessibility-click=\"13,32\" ng-click=\"selectPage(totalPages-1, $event)\">{{totalPages-1}} is selected\n" + "\n" + - " 10 && currentPage <= totalPages - 5\" b2b-accessibility-click=\"13,32\" ng-click=\"selectPage(totalPages, $event)\">{{totalPages}} is selected\n" + + " 10 && currentPage <= totalPages - 5\" b2b-accessibility-click=\"13,32\" ng-click=\"selectPage(totalPages, $event)\">{{totalPages}} is selected\n" + "\n" + " = 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" + " \n" + @@ -21823,22 +22736,22 @@ angular.module("b2bTemplate/statusTracker/statusTracker.html", []).run(["$templa " \n" + "
      \n" + " \n" + - "
      \n" + + "
      \n" + "

      {{status.heading}}

      \n" + "
      \n" + "
      \n" + " \n" + - " {{status.complete ? 'Complete' : 'Incomplete'}}\n" + + " {{ removeCamelCase(status.state) }}\n" + " \n" + "
      \n" + "
      \n" + - "
      \n" + - " {{status.complete ? 'Complete' : status.estimate}}\n" + + "
      \n" + + " \n" + "  \n" + + " \n" + "
      \n" + " \n" + - "
      \n" + - " {{status.description}}\n" + + "
      \n" + "
      \n" + "
      \n" + "
      "); }]); -- cgit 1.2.3-korg
      {{title}}
      {{label.pre}}\n" + " \n" + - " \n" + + " \n" + "