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");