arnaucube
arnaucube/socketioMEANseedPublic Self-hosted repository
socketioMEANseed/www/bower_components/angular-material/modules/closure/navBar/navBar.js
www/bower_components/angular-material/modules/closure/navBar/navBar.jsRaw
  1/*!
  2 * Angular Material Design
  3 * https://github.com/angular/material
  4 * @license MIT
  5 * v1.1.3
  6 */
  7goog.provide('ngmaterial.components.navBar');
  8goog.require('ngmaterial.core');
  9/**
 10 * @ngdoc module
 11 * @name material.components.navBar
 12 */
 13
 14
 15MdNavBarController['$inject'] = ["$element", "$scope", "$timeout", "$mdConstant"];
 16MdNavItem['$inject'] = ["$mdAria", "$$rAF"];
 17MdNavItemController['$inject'] = ["$element"];
 18MdNavBar['$inject'] = ["$mdAria", "$mdTheming"];
 19angular.module('material.components.navBar', ['material.core'])
 20    .controller('MdNavBarController', MdNavBarController)
 21    .directive('mdNavBar', MdNavBar)
 22    .controller('MdNavItemController', MdNavItemController)
 23    .directive('mdNavItem', MdNavItem);
 24
 25
 26/*****************************************************************************
 27 *                            PUBLIC DOCUMENTATION                           *
 28 *****************************************************************************/
 29/**
 30 * @ngdoc directive
 31 * @name mdNavBar
 32 * @module material.components.navBar
 33 *
 34 * @restrict E
 35 *
 36 * @description
 37 * The `<md-nav-bar>` directive renders a list of material tabs that can be used
 38 * for top-level page navigation. Unlike `<md-tabs>`, it has no concept of a tab
 39 * body and no bar pagination.
 40 *
 41 * Because it deals with page navigation, certain routing concepts are built-in.
 42 * Route changes via via ng-href, ui-sref, or ng-click events are supported.
 43 * Alternatively, the user could simply watch currentNavItem for changes.
 44 *
 45 * Accessibility functionality is implemented as a site navigator with a
 46 * listbox, according to
 47 * https://www.w3.org/TR/wai-aria-practices/#Site_Navigator_Tabbed_Style
 48 *
 49 * @param {string=} mdSelectedNavItem The name of the current tab; this must
 50 *     match the name attribute of `<md-nav-item>`
 51 * @param {boolean=} mdNoInkBar If set to true, the ink bar will be hidden.
 52 * @param {string=} navBarAriaLabel An aria-label for the nav-bar
 53 *
 54 * @usage
 55 * <hljs lang="html">
 56 *  <md-nav-bar md-selected-nav-item="currentNavItem">
 57 *    <md-nav-item md-nav-click="goto('page1')" name="page1">
 58 *      Page One
 59 *    </md-nav-item>
 60 *    <md-nav-item md-nav-href="#page2" name="page3">Page Two</md-nav-item>
 61 *    <md-nav-item md-nav-sref="page3" name="page2">Page Three</md-nav-item>
 62 *    <md-nav-item
 63 *      md-nav-sref="app.page4"
 64 *      sref-opts="{reload: true, notify: true}"
 65 *      name="page4">
 66 *      Page Four
 67 *    </md-nav-item>
 68 *  </md-nav-bar>
 69 *</hljs>
 70 * <hljs lang="js">
 71 * (function() {
 72 *   'use strict';
 73 *
 74 *    $rootScope.$on('$routeChangeSuccess', function(event, current) {
 75 *      $scope.currentLink = getCurrentLinkFromRoute(current);
 76 *    });
 77 * });
 78 * </hljs>
 79 */
 80
 81/*****************************************************************************
 82 *                            mdNavItem
 83 *****************************************************************************/
 84/**
 85 * @ngdoc directive
 86 * @name mdNavItem
 87 * @module material.components.navBar
 88 *
 89 * @restrict E
 90 *
 91 * @description
 92 * `<md-nav-item>` describes a page navigation link within the `<md-nav-bar>`
 93 * component. It renders an md-button as the actual link.
 94 *
 95 * Exactly one of the mdNavClick, mdNavHref, mdNavSref attributes are required
 96 * to be specified.
 97 *
 98 * @param {Function=} mdNavClick Function which will be called when the
 99 *     link is clicked to change the page. Renders as an `ng-click`.
100 * @param {string=} mdNavHref url to transition to when this link is clicked.
101 *     Renders as an `ng-href`.
102 * @param {string=} mdNavSref Ui-router state to transition to when this link is
103 *     clicked. Renders as a `ui-sref`.
104 * @param {!Object=} srefOpts Ui-router options that are passed to the
105 *     `$state.go()` function. See the [Ui-router documentation for details]
106 *     (https://ui-router.github.io/docs/latest/interfaces/transition.transitionoptions.html).
107 * @param {string=} name The name of this link. Used by the nav bar to know
108 *     which link is currently selected.
109 * @param {string=} aria-label Adds alternative text for accessibility
110 *
111 * @usage
112 * See `<md-nav-bar>` for usage.
113 */
114
115
116/*****************************************************************************
117 *                                IMPLEMENTATION                             *
118 *****************************************************************************/
119
120function MdNavBar($mdAria, $mdTheming) {
121  return {
122    restrict: 'E',
123    transclude: true,
124    controller: MdNavBarController,
125    controllerAs: 'ctrl',
126    bindToController: true,
127    scope: {
128      'mdSelectedNavItem': '=?',
129      'mdNoInkBar': '=?',
130      'navBarAriaLabel': '@?',
131    },
132    template:
133      '<div class="md-nav-bar">' +
134        '<nav role="navigation">' +
135          '<ul class="_md-nav-bar-list" ng-transclude role="listbox"' +
136            'tabindex="0"' +
137            'ng-focus="ctrl.onFocus()"' +
138            'ng-keydown="ctrl.onKeydown($event)"' +
139            'aria-label="{{ctrl.navBarAriaLabel}}">' +
140          '</ul>' +
141        '</nav>' +
142        '<md-nav-ink-bar ng-hide="ctrl.mdNoInkBar"></md-nav-ink-bar>' +
143      '</div>',
144    link: function(scope, element, attrs, ctrl) {
145      $mdTheming(element);
146      if (!ctrl.navBarAriaLabel) {
147        $mdAria.expectAsync(element, 'aria-label', angular.noop);
148      }
149    },
150  };
151}
152
153/**
154 * Controller for the nav-bar component.
155 *
156 * Accessibility functionality is implemented as a site navigator with a
157 * listbox, according to
158 * https://www.w3.org/TR/wai-aria-practices/#Site_Navigator_Tabbed_Style
159 * @param {!angular.JQLite} $element
160 * @param {!angular.Scope} $scope
161 * @param {!angular.Timeout} $timeout
162 * @param {!Object} $mdConstant
163 * @constructor
164 * @final
165 * ngInject
166 */
167function MdNavBarController($element, $scope, $timeout, $mdConstant) {
168  // Injected variables
169  /** @private @const {!angular.Timeout} */
170  this._$timeout = $timeout;
171
172  /** @private @const {!angular.Scope} */
173  this._$scope = $scope;
174
175  /** @private @const {!Object} */
176  this._$mdConstant = $mdConstant;
177
178  // Data-bound variables.
179  /** @type {string} */
180  this.mdSelectedNavItem;
181
182  /** @type {string} */
183  this.navBarAriaLabel;
184
185  // State variables.
186
187  /** @type {?angular.JQLite} */
188  this._navBarEl = $element[0];
189
190  /** @type {?angular.JQLite} */
191  this._inkbar;
192
193  var self = this;
194  // need to wait for transcluded content to be available
195  var deregisterTabWatch = this._$scope.$watch(function() {
196    return self._navBarEl.querySelectorAll('._md-nav-button').length;
197  },
198  function(newLength) {
199    if (newLength > 0) {
200      self._initTabs();
201      deregisterTabWatch();
202    }
203  });
204}
205
206
207
208/**
209 * Initializes the tab components once they exist.
210 * @private
211 */
212MdNavBarController.prototype._initTabs = function() {
213  this._inkbar = angular.element(this._navBarEl.querySelector('md-nav-ink-bar'));
214
215  var self = this;
216  this._$timeout(function() {
217    self._updateTabs(self.mdSelectedNavItem, undefined);
218  });
219
220  this._$scope.$watch('ctrl.mdSelectedNavItem', function(newValue, oldValue) {
221    // Wait a digest before update tabs for products doing
222    // anything dynamic in the template.
223    self._$timeout(function() {
224      self._updateTabs(newValue, oldValue);
225    });
226  });
227};
228
229/**
230 * Set the current tab to be selected.
231 * @param {string|undefined} newValue New current tab name.
232 * @param {string|undefined} oldValue Previous tab name.
233 * @private
234 */
235MdNavBarController.prototype._updateTabs = function(newValue, oldValue) {
236  var self = this;
237  var tabs = this._getTabs();
238
239  // this._getTabs can return null if nav-bar has not yet been initialized
240  if(!tabs)
241    return;
242
243  var oldIndex = -1;
244  var newIndex = -1;
245  var newTab = this._getTabByName(newValue);
246  var oldTab = this._getTabByName(oldValue);
247
248  if (oldTab) {
249    oldTab.setSelected(false);
250    oldIndex = tabs.indexOf(oldTab);
251  }
252
253  if (newTab) {
254    newTab.setSelected(true);
255    newIndex = tabs.indexOf(newTab);
256  }
257
258  this._$timeout(function() {
259    self._updateInkBarStyles(newTab, newIndex, oldIndex);
260  });
261};
262
263/**
264 * Repositions the ink bar to the selected tab.
265 * @private
266 */
267MdNavBarController.prototype._updateInkBarStyles = function(tab, newIndex, oldIndex) {
268  this._inkbar.toggleClass('_md-left', newIndex < oldIndex)
269      .toggleClass('_md-right', newIndex > oldIndex);
270
271  this._inkbar.css({display: newIndex < 0 ? 'none' : ''});
272
273  if (tab) {
274    var tabEl = tab.getButtonEl();
275    var left = tabEl.offsetLeft;
276
277    this._inkbar.css({left: left + 'px', width: tabEl.offsetWidth + 'px'});
278  }
279};
280
281/**
282 * Returns an array of the current tabs.
283 * @return {!Array<!NavItemController>}
284 * @private
285 */
286MdNavBarController.prototype._getTabs = function() {
287  var controllers = Array.prototype.slice.call(
288    this._navBarEl.querySelectorAll('.md-nav-item'))
289    .map(function(el) {
290      return angular.element(el).controller('mdNavItem')
291    });
292  return controllers.indexOf(undefined) ? controllers : null;
293};
294
295/**
296 * Returns the tab with the specified name.
297 * @param {string} name The name of the tab, found in its name attribute.
298 * @return {!NavItemController|undefined}
299 * @private
300 */
301MdNavBarController.prototype._getTabByName = function(name) {
302  return this._findTab(function(tab) {
303    return tab.getName() == name;
304  });
305};
306
307/**
308 * Returns the selected tab.
309 * @return {!NavItemController|undefined}
310 * @private
311 */
312MdNavBarController.prototype._getSelectedTab = function() {
313  return this._findTab(function(tab) {
314    return tab.isSelected();
315  });
316};
317
318/**
319 * Returns the focused tab.
320 * @return {!NavItemController|undefined}
321 */
322MdNavBarController.prototype.getFocusedTab = function() {
323  return this._findTab(function(tab) {
324    return tab.hasFocus();
325  });
326};
327
328/**
329 * Find a tab that matches the specified function.
330 * @private
331 */
332MdNavBarController.prototype._findTab = function(fn) {
333  var tabs = this._getTabs();
334  for (var i = 0; i < tabs.length; i++) {
335    if (fn(tabs[i])) {
336      return tabs[i];
337    }
338  }
339
340  return null;
341};
342
343/**
344 * Direct focus to the selected tab when focus enters the nav bar.
345 */
346MdNavBarController.prototype.onFocus = function() {
347  var tab = this._getSelectedTab();
348  if (tab) {
349    tab.setFocused(true);
350  }
351};
352
353/**
354 * Move focus from oldTab to newTab.
355 * @param {!NavItemController} oldTab
356 * @param {!NavItemController} newTab
357 * @private
358 */
359MdNavBarController.prototype._moveFocus = function(oldTab, newTab) {
360  oldTab.setFocused(false);
361  newTab.setFocused(true);
362};
363
364/**
365 * Responds to keypress events.
366 * @param {!Event} e
367 */
368MdNavBarController.prototype.onKeydown = function(e) {
369  var keyCodes = this._$mdConstant.KEY_CODE;
370  var tabs = this._getTabs();
371  var focusedTab = this.getFocusedTab();
372  if (!focusedTab) return;
373
374  var focusedTabIndex = tabs.indexOf(focusedTab);
375
376  // use arrow keys to navigate between tabs
377  switch (e.keyCode) {
378    case keyCodes.UP_ARROW:
379    case keyCodes.LEFT_ARROW:
380      if (focusedTabIndex > 0) {
381        this._moveFocus(focusedTab, tabs[focusedTabIndex - 1]);
382      }
383      break;
384    case keyCodes.DOWN_ARROW:
385    case keyCodes.RIGHT_ARROW:
386      if (focusedTabIndex < tabs.length - 1) {
387        this._moveFocus(focusedTab, tabs[focusedTabIndex + 1]);
388      }
389      break;
390    case keyCodes.SPACE:
391    case keyCodes.ENTER:
392      // timeout to avoid a "digest already in progress" console error
393      this._$timeout(function() {
394        focusedTab.getButtonEl().click();
395      });
396      break;
397  }
398};
399
400/**
401 * ngInject
402 */
403function MdNavItem($mdAria, $$rAF) {
404  return {
405    restrict: 'E',
406    require: ['mdNavItem', '^mdNavBar'],
407    controller: MdNavItemController,
408    bindToController: true,
409    controllerAs: 'ctrl',
410    replace: true,
411    transclude: true,
412    template: function(tElement, tAttrs) {
413      var hasNavClick = tAttrs.mdNavClick;
414      var hasNavHref = tAttrs.mdNavHref;
415      var hasNavSref = tAttrs.mdNavSref;
416      var hasSrefOpts = tAttrs.srefOpts;
417      var navigationAttribute;
418      var navigationOptions;
419      var buttonTemplate;
420
421      // Cannot specify more than one nav attribute
422      if ((hasNavClick ? 1:0) + (hasNavHref ? 1:0) + (hasNavSref ? 1:0) > 1) {
423        throw Error(
424          'Must not specify more than one of the md-nav-click, md-nav-href, ' +
425          'or md-nav-sref attributes per nav-item directive.'
426        );
427      }
428
429      if (hasNavClick) {
430        navigationAttribute = 'ng-click="ctrl.mdNavClick()"';
431      } else if (hasNavHref) {
432        navigationAttribute = 'ng-href="{{ctrl.mdNavHref}}"';
433      } else if (hasNavSref) {
434        navigationAttribute = 'ui-sref="{{ctrl.mdNavSref}}"';
435      }
436
437      navigationOptions = hasSrefOpts ? 'ui-sref-opts="{{ctrl.srefOpts}}" ' : '';
438
439      if (navigationAttribute) {
440        buttonTemplate = '' +
441          '<md-button class="_md-nav-button md-accent" ' +
442            'ng-class="ctrl.getNgClassMap()" ' +
443            'ng-blur="ctrl.setFocused(false)" ' +
444            'tabindex="-1" ' +
445            navigationOptions +
446            navigationAttribute + '>' +
447            '<span ng-transclude class="_md-nav-button-text"></span>' +
448          '</md-button>';
449      }
450
451      return '' +
452        '<li class="md-nav-item" ' +
453          'role="option" ' +
454          'aria-selected="{{ctrl.isSelected()}}">' +
455          (buttonTemplate || '') +
456        '</li>';
457    },
458    scope: {
459      'mdNavClick': '&?',
460      'mdNavHref': '@?',
461      'mdNavSref': '@?',
462      'srefOpts': '=?',
463      'name': '@',
464    },
465    link: function(scope, element, attrs, controllers) {
466      // When accessing the element's contents synchronously, they
467      // may not be defined yet because of transclusion. There is a higher
468      // chance that it will be accessible if we wait one frame.
469      $$rAF(function() {
470        var mdNavItem = controllers[0];
471        var mdNavBar = controllers[1];
472        var navButton = angular.element(element[0].querySelector('._md-nav-button'));
473
474        if (!mdNavItem.name) {
475          mdNavItem.name = angular.element(element[0]
476              .querySelector('._md-nav-button-text')).text().trim();
477        }
478
479        navButton.on('click', function() {
480          mdNavBar.mdSelectedNavItem = mdNavItem.name;
481          scope.$apply();
482        });
483
484        $mdAria.expectWithText(element, 'aria-label');
485      });
486    }
487  };
488}
489
490/**
491 * Controller for the nav-item component.
492 * @param {!angular.JQLite} $element
493 * @constructor
494 * @final
495 * ngInject
496 */
497function MdNavItemController($element) {
498
499  /** @private @const {!angular.JQLite} */
500  this._$element = $element;
501
502  // Data-bound variables
503
504  /** @const {?Function} */
505  this.mdNavClick;
506
507  /** @const {?string} */
508  this.mdNavHref;
509
510  /** @const {?string} */
511  this.mdNavSref;
512  /** @const {?Object} */
513  this.srefOpts;
514  /** @const {?string} */
515  this.name;
516
517  // State variables
518  /** @private {boolean} */
519  this._selected = false;
520
521  /** @private {boolean} */
522  this._focused = false;
523}
524
525/**
526 * Returns a map of class names and values for use by ng-class.
527 * @return {!Object<string,boolean>}
528 */
529MdNavItemController.prototype.getNgClassMap = function() {
530  return {
531    'md-active': this._selected,
532    'md-primary': this._selected,
533    'md-unselected': !this._selected,
534    'md-focused': this._focused,
535  };
536};
537
538/**
539 * Get the name attribute of the tab.
540 * @return {string}
541 */
542MdNavItemController.prototype.getName = function() {
543  return this.name;
544};
545
546/**
547 * Get the button element associated with the tab.
548 * @return {!Element}
549 */
550MdNavItemController.prototype.getButtonEl = function() {
551  return this._$element[0].querySelector('._md-nav-button');
552};
553
554/**
555 * Set the selected state of the tab.
556 * @param {boolean} isSelected
557 */
558MdNavItemController.prototype.setSelected = function(isSelected) {
559  this._selected = isSelected;
560};
561
562/**
563 * @return {boolean}
564 */
565MdNavItemController.prototype.isSelected = function() {
566  return this._selected;
567};
568
569/**
570 * Set the focused state of the tab.
571 * @param {boolean} isFocused
572 */
573MdNavItemController.prototype.setFocused = function(isFocused) {
574  this._focused = isFocused;
575
576  if (isFocused) {
577    this.getButtonEl().focus();
578  }
579};
580
581/**
582 * @return {boolean}
583 */
584MdNavItemController.prototype.hasFocus = function() {
585  return this._focused;
586};
587
588ngmaterial.components.navBar = angular.module("material.components.navBar");