UNPKG

ionic-angular

Version:

[![Circle CI](https://circleci.com/gh/driftyco/ionic.svg?style=svg)](https://circleci.com/gh/driftyco/ionic)

384 lines (310 loc) 11.2 kB
IonicModule .controller('$ionicHeaderBar', [ '$scope', '$element', '$attrs', '$q', '$ionicConfig', '$ionicHistory', function($scope, $element, $attrs, $q, $ionicConfig, $ionicHistory) { var TITLE = 'title'; var BACK_TEXT = 'back-text'; var BACK_BUTTON = 'back-button'; var DEFAULT_TITLE = 'default-title'; var PREVIOUS_TITLE = 'previous-title'; var HIDE = 'hide'; var self = this; var titleText = ''; var previousTitleText = ''; var titleLeft = 0; var titleRight = 0; var titleCss = ''; var isBackEnabled = false; var isBackShown = true; var isNavBackShown = true; var isBackElementShown = false; var titleTextWidth = 0; self.beforeEnter = function(viewData) { $scope.$broadcast('$ionicView.beforeEnter', viewData); }; self.title = function(newTitleText) { if (arguments.length && newTitleText !== titleText) { getEle(TITLE).innerHTML = newTitleText; titleText = newTitleText; titleTextWidth = 0; } return titleText; }; self.enableBack = function(shouldEnable, disableReset) { // whether or not the back button show be visible, according // to the navigation and history if (arguments.length) { isBackEnabled = shouldEnable; if (!disableReset) self.updateBackButton(); } return isBackEnabled; }; self.showBack = function(shouldShow, disableReset) { // different from enableBack() because this will always have the back // visually hidden if false, even if the history says it should show if (arguments.length) { isBackShown = shouldShow; if (!disableReset) self.updateBackButton(); } return isBackShown; }; self.showNavBack = function(shouldShow) { // different from showBack() because this is for the entire nav bar's // setting for all of it's child headers. For internal use. isNavBackShown = shouldShow; self.updateBackButton(); }; self.updateBackButton = function() { var ele; if ((isBackShown && isNavBackShown && isBackEnabled) !== isBackElementShown) { isBackElementShown = isBackShown && isNavBackShown && isBackEnabled; ele = getEle(BACK_BUTTON); ele && ele.classList[ isBackElementShown ? 'remove' : 'add' ](HIDE); } if (isBackEnabled) { ele = ele || getEle(BACK_BUTTON); if (ele) { if (self.backButtonIcon !== $ionicConfig.backButton.icon()) { ele = getEle(BACK_BUTTON + ' .icon'); if (ele) { self.backButtonIcon = $ionicConfig.backButton.icon(); ele.className = 'icon ' + self.backButtonIcon; } } if (self.backButtonText !== $ionicConfig.backButton.text()) { ele = getEle(BACK_BUTTON + ' .back-text'); if (ele) { ele.textContent = self.backButtonText = $ionicConfig.backButton.text(); } } } } }; self.titleTextWidth = function() { var element = getEle(TITLE); if ( element ) { // If the element has a nav-bar-title, use that instead // to calculate the width of the title var children = angular.element(element).children(); for ( var i = 0; i < children.length; i++ ) { if ( angular.element(children[i]).hasClass('nav-bar-title') ) { element = children[i]; break; } } } var bounds = ionic.DomUtil.getTextBounds(element); titleTextWidth = Math.min(bounds && bounds.width || 30); return titleTextWidth; }; self.titleWidth = function() { var titleWidth = self.titleTextWidth(); var offsetWidth = getEle(TITLE).offsetWidth; if (offsetWidth < titleWidth) { titleWidth = offsetWidth + (titleLeft - titleRight - 5); } return titleWidth; }; self.titleTextX = function() { return ($element[0].offsetWidth / 2) - (self.titleWidth() / 2); }; self.titleLeftRight = function() { return titleLeft - titleRight; }; self.backButtonTextLeft = function() { var offsetLeft = 0; var ele = getEle(BACK_TEXT); while (ele) { offsetLeft += ele.offsetLeft; ele = ele.parentElement; } return offsetLeft; }; self.resetBackButton = function(viewData) { if ($ionicConfig.backButton.previousTitleText()) { var previousTitleEle = getEle(PREVIOUS_TITLE); if (previousTitleEle) { previousTitleEle.classList.remove(HIDE); var view = (viewData && $ionicHistory.getViewById(viewData.viewId)); var newPreviousTitleText = $ionicHistory.backTitle(view); if (newPreviousTitleText !== previousTitleText) { previousTitleText = previousTitleEle.innerHTML = newPreviousTitleText; } } var defaultTitleEle = getEle(DEFAULT_TITLE); if (defaultTitleEle) { defaultTitleEle.classList.remove(HIDE); } } }; self.align = function(textAlign) { var titleEle = getEle(TITLE); textAlign = textAlign || $attrs.alignTitle || $ionicConfig.navBar.alignTitle(); var widths = self.calcWidths(textAlign, false); if (isBackShown && previousTitleText && $ionicConfig.backButton.previousTitleText()) { var previousTitleWidths = self.calcWidths(textAlign, true); var availableTitleWidth = $element[0].offsetWidth - previousTitleWidths.titleLeft - previousTitleWidths.titleRight; if (self.titleTextWidth() <= availableTitleWidth) { widths = previousTitleWidths; } } return self.updatePositions(titleEle, widths.titleLeft, widths.titleRight, widths.buttonsLeft, widths.buttonsRight, widths.css, widths.showPrevTitle); }; self.calcWidths = function(textAlign, isPreviousTitle) { var titleEle = getEle(TITLE); var backBtnEle = getEle(BACK_BUTTON); var x, y, z, b, c, d, childSize, bounds; var childNodes = $element[0].childNodes; var buttonsLeft = 0; var buttonsRight = 0; var isCountRightOfTitle; var updateTitleLeft = 0; var updateTitleRight = 0; var updateCss = ''; var backButtonWidth = 0; // Compute how wide the left children are // Skip all titles (there may still be two titles, one leaving the dom) // Once we encounter a titleEle, realize we are now counting the right-buttons, not left for (x = 0; x < childNodes.length; x++) { c = childNodes[x]; childSize = 0; if (c.nodeType == 1) { // element node if (c === titleEle) { isCountRightOfTitle = true; continue; } if (c.classList.contains(HIDE)) { continue; } if (isBackShown && c === backBtnEle) { for (y = 0; y < c.childNodes.length; y++) { b = c.childNodes[y]; if (b.nodeType == 1) { if (b.classList.contains(BACK_TEXT)) { for (z = 0; z < b.children.length; z++) { d = b.children[z]; if (isPreviousTitle) { if (d.classList.contains(DEFAULT_TITLE)) continue; backButtonWidth += d.offsetWidth; } else { if (d.classList.contains(PREVIOUS_TITLE)) continue; backButtonWidth += d.offsetWidth; } } } else { backButtonWidth += b.offsetWidth; } } else if (b.nodeType == 3 && b.nodeValue.trim()) { bounds = ionic.DomUtil.getTextBounds(b); backButtonWidth += bounds && bounds.width || 0; } } childSize = backButtonWidth || c.offsetWidth; } else { // not the title, not the back button, not a hidden element childSize = c.offsetWidth; } } else if (c.nodeType == 3 && c.nodeValue.trim()) { // text node bounds = ionic.DomUtil.getTextBounds(c); childSize = bounds && bounds.width || 0; } if (isCountRightOfTitle) { buttonsRight += childSize; } else { buttonsLeft += childSize; } } // Size and align the header titleEle based on the sizes of the left and // right children, and the desired alignment mode if (textAlign == 'left') { updateCss = 'title-left'; if (buttonsLeft) { updateTitleLeft = buttonsLeft + 15; } if (buttonsRight) { updateTitleRight = buttonsRight + 15; } } else if (textAlign == 'right') { updateCss = 'title-right'; if (buttonsLeft) { updateTitleLeft = buttonsLeft + 15; } if (buttonsRight) { updateTitleRight = buttonsRight + 15; } } else { // center the default var margin = Math.max(buttonsLeft, buttonsRight) + 10; if (margin > 10) { updateTitleLeft = updateTitleRight = margin; } } return { backButtonWidth: backButtonWidth, buttonsLeft: buttonsLeft, buttonsRight: buttonsRight, titleLeft: updateTitleLeft, titleRight: updateTitleRight, showPrevTitle: isPreviousTitle, css: updateCss }; }; self.updatePositions = function(titleEle, updateTitleLeft, updateTitleRight, buttonsLeft, buttonsRight, updateCss, showPreviousTitle) { var deferred = $q.defer(); // only make DOM updates when there are actual changes if (titleEle) { if (updateTitleLeft !== titleLeft) { titleEle.style.left = updateTitleLeft ? updateTitleLeft + 'px' : ''; titleLeft = updateTitleLeft; } if (updateTitleRight !== titleRight) { titleEle.style.right = updateTitleRight ? updateTitleRight + 'px' : ''; titleRight = updateTitleRight; } if (updateCss !== titleCss) { updateCss && titleEle.classList.add(updateCss); titleCss && titleEle.classList.remove(titleCss); titleCss = updateCss; } } if ($ionicConfig.backButton.previousTitleText()) { var prevTitle = getEle(PREVIOUS_TITLE); var defaultTitle = getEle(DEFAULT_TITLE); prevTitle && prevTitle.classList[ showPreviousTitle ? 'remove' : 'add'](HIDE); defaultTitle && defaultTitle.classList[ showPreviousTitle ? 'add' : 'remove'](HIDE); } ionic.requestAnimationFrame(function() { if (titleEle && titleEle.offsetWidth + 10 < titleEle.scrollWidth) { var minRight = buttonsRight + 5; var testRight = $element[0].offsetWidth - titleLeft - self.titleTextWidth() - 20; updateTitleRight = testRight < minRight ? minRight : testRight; if (updateTitleRight !== titleRight) { titleEle.style.right = updateTitleRight + 'px'; titleRight = updateTitleRight; } } deferred.resolve(); }); return deferred.promise; }; self.setCss = function(elementClassname, css) { ionic.DomUtil.cachedStyles(getEle(elementClassname), css); }; var eleCache = {}; function getEle(className) { if (!eleCache[className]) { eleCache[className] = $element[0].querySelector('.' + className); } return eleCache[className]; } $scope.$on('$destroy', function() { for (var n in eleCache) eleCache[n] = null; }); }]);