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