rails-angular-material 0.8.3 → 0.8.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: d240b37ee6edc13274fd406e416d209cc3b1b1c1
4
- data.tar.gz: 15035c9a5818925f89895e8af4eca0106eb931e1
3
+ metadata.gz: 7938d10e99d0dcaefd215dac8244e6e8613055fe
4
+ data.tar.gz: d64395f9565cae116aeb76a98edc0da0ebb44a37
5
5
  SHA512:
6
- metadata.gz: 504473336a002e7638296e7d0c1daa2071c768d3df26ea3c87b183ffe7de1dc0d7a161d6734b03254eeda6d509ee820d39c5bf4d15e06935dc12cdc13ff5f11f
7
- data.tar.gz: 3d41bdd3ae44e68d05ed3afe638a413b816c9ca33f8988a26d4fdd65b1cf03f511f0a5cc3bb500086c89694d6b3c472d731dc4451b16895fcd5b8f98378e645e
6
+ metadata.gz: 92e2ae36933f0664c142de1792aa08b6fdc1df4f71f0243ecc57d4738815782c44732e1ed9667dcb1a5203c971998e96587e1f6f35c0c687d2bf78ffbbcbfa75
7
+ data.tar.gz: 304a2385937077b6962d8e9d7d6ecb75ad51ec77aad07795ecde60e985a7654cad8dad42317e0ec4371455388a2c5d486285222bef8d0d8ece26e25cefa14bb8
data/README.md CHANGED
@@ -23,4 +23,4 @@ If you desire to require minified AngularMaterial files, add the following:
23
23
 
24
24
  ## Versioning
25
25
 
26
- Current version of AngularMaterial - 0.8.3
26
+ Current version of AngularMaterial - 0.8.3-74601d0
@@ -1,3 +1,3 @@
1
1
  module AngularMaterialRails
2
- VERSION = "0.8.3"
2
+ VERSION = "0.8.3.1"
3
3
  end
@@ -4,7 +4,7 @@
4
4
  * @license MIT
5
5
  * v0.8.3
6
6
  */
7
- angular.module('ngMaterial', ["ng","ngAnimate","ngAria","material.core","material.core.theming.palette","material.core.theming","material.components.autocomplete","material.components.backdrop","material.components.bottomSheet","material.components.button","material.components.card","material.components.checkbox","material.components.content","material.components.dialog","material.components.divider","material.components.gridList","material.components.icon","material.components.input","material.components.list","material.components.progressCircular","material.components.progressLinear","material.components.radioButton","material.components.select","material.components.sidenav","material.components.slider","material.components.sticky","material.components.subheader","material.components.swipe","material.components.switch","material.components.tabs","material.components.textField","material.components.toast","material.components.toolbar","material.components.tooltip","material.components.whiteframe"]);
7
+ angular.module('ngMaterial', ["ng","ngAnimate","ngAria","material.core","material.core.theming.palette","material.core.theming","material.components.autocomplete","material.components.backdrop","material.components.bottomSheet","material.components.button","material.components.card","material.components.checkbox","material.components.chips","material.components.content","material.components.dialog","material.components.divider","material.components.gridList","material.components.icon","material.components.input","material.components.list","material.components.progressCircular","material.components.progressLinear","material.components.radioButton","material.components.select","material.components.sidenav","material.components.slider","material.components.sticky","material.components.subheader","material.components.swipe","material.components.switch","material.components.tabs","material.components.textField","material.components.toast","material.components.toolbar","material.components.tooltip","material.components.whiteframe"]);
8
8
  /*!
9
9
  * Angular Material Design
10
10
  * https://github.com/angular/material
@@ -96,7 +96,9 @@ function MdConstantFactory($$rAF, $sniffer) {
96
96
  UP_ARROW : 38,
97
97
  RIGHT_ARROW : 39,
98
98
  DOWN_ARROW : 40,
99
- TAB : 9
99
+ TAB : 9,
100
+ BACKSPACE: 8,
101
+ DELETE: 46
100
102
  },
101
103
  CSS: {
102
104
  /* Constants */
@@ -538,7 +540,7 @@ angular.module('material.core')
538
540
 
539
541
  return Util = {
540
542
  now: window.performance ?
541
- angular.bind(window.performance, window.performance.now) :
543
+ angular.bind(window.performance, window.performance.now) :
542
544
  Date.now,
543
545
 
544
546
  clientRect: function(element, offsetParent, isOffsetRect) {
@@ -549,11 +551,11 @@ angular.module('material.core')
549
551
  // The user can ask for an offsetRect: a rect relative to the offsetParent,
550
552
  // or a clientRect: a rect relative to the page
551
553
  var offsetRect = isOffsetRect ?
552
- offsetParent.getBoundingClientRect() :
554
+ offsetParent.getBoundingClientRect() :
553
555
  { left: 0, top: 0, width: 0, height: 0 };
554
556
  return {
555
- left: nodeRect.left - offsetRect.left + offsetParent.scrollLeft,
556
- top: nodeRect.top - offsetRect.top + offsetParent.scrollTop,
557
+ left: nodeRect.left - offsetRect.left,
558
+ top: nodeRect.top - offsetRect.top,
557
559
  width: nodeRect.width,
558
560
  height: nodeRect.height
559
561
  };
@@ -764,7 +766,21 @@ angular.module('material.core')
764
766
  }
765
767
  } while (el = el.parentNode);
766
768
  return null;
769
+ },
770
+
771
+ /**
772
+ * Functional equivalent for $element.filter(‘md-bottom-sheet’)
773
+ * useful with interimElements where the element and its container are important...
774
+ */
775
+ extractElementByName : function (element, nodeName) {
776
+ for (var i = 0, len = element.length; i < len; i++) {
777
+ if (element[i].nodeName.toLowerCase() === nodeName){
778
+ return angular.element(element[i]);
779
+ }
780
+ }
781
+ return element;
767
782
  }
783
+
768
784
  };
769
785
 
770
786
  }]);
@@ -821,7 +837,11 @@ function AriaService($$rAF, $log, $window) {
821
837
  function expect(element, attrName, defaultValue) {
822
838
  var node = element[0];
823
839
 
824
- if (!node.hasAttribute(attrName) && !childHasAttribute(node, attrName)) {
840
+ // if node exists and neither it nor its children have the attribute
841
+ if (node &&
842
+ ((!node.hasAttribute(attrName) ||
843
+ node.getAttribute(attrName).length === 0) &&
844
+ !childHasAttribute(node, attrName))) {
825
845
 
826
846
  defaultValue = angular.isString(defaultValue) ? defaultValue.trim() : '';
827
847
  if (defaultValue.length) {
@@ -939,11 +959,11 @@ function mdCompilerService($q, $http, $injector, $compile, $controller, $templat
939
959
  * * `key` - `{string}`: a name of a dependency to be injected into the controller.
940
960
  * * `factory` - `{string|function}`: If `string` then it is an alias for a service.
941
961
  * Otherwise if function, then it is injected and the return value is treated as the
942
- * dependency. If the result is a promise, it is resolved before its value is
962
+ * dependency. If the result is a promise, it is resolved before its value is
943
963
  * injected into the controller.
944
964
  *
945
965
  * @returns {object=} promise A promise, which will be resolved with a `compileData` object.
946
- * `compileData` has the following properties:
966
+ * `compileData` has the following properties:
947
967
  *
948
968
  * - `element` - `{element}`: an uncompiled element matching the provided template.
949
969
  * - `link` - `{function(scope)}`: A link function, which, when called, will compile
@@ -962,7 +982,7 @@ function mdCompilerService($q, $http, $injector, $compile, $controller, $templat
962
982
  var transformTemplate = options.transformTemplate || angular.identity;
963
983
  var bindToController = options.bindToController;
964
984
 
965
- // Take resolve values and invoke them.
985
+ // Take resolve values and invoke them.
966
986
  // Resolves can either be a string (value: 'MyRegisteredAngularConst'),
967
987
  // or an invokable 'factory' of sorts: (value: function ValueGetter($dependency) {})
968
988
  angular.forEach(resolve, function(value, key) {
@@ -1055,7 +1075,7 @@ if (shouldHijackClicks) {
1055
1075
  document.addEventListener('click', function(ev) {
1056
1076
  // Space/enter on a button, and submit events, can send clicks
1057
1077
  var isKeyClick = ev.clientX === 0 && ev.clientY === 0;
1058
- if (window.jQuery || isKeyClick || ev.$material) return;
1078
+ if (isKeyClick || ev.$material) return;
1059
1079
 
1060
1080
  // Prevent clicks unless they're sent by material
1061
1081
  ev.preventDefault();
@@ -1794,7 +1814,7 @@ function InterimElementProvider() {
1794
1814
  show: function() {
1795
1815
  var compilePromise;
1796
1816
  if (options.skipCompile) {
1797
- compilePromise = $q(function(resolve) {
1817
+ compilePromise = $q(function(resolve) {
1798
1818
  resolve({
1799
1819
  locals: {},
1800
1820
  link: function() { return options.element; }
@@ -2102,7 +2122,7 @@ function InkRippleService($window, $timeout) {
2102
2122
  isHeld = false,
2103
2123
  node = element[0],
2104
2124
  rippleSizeSetting = element.attr('md-ripple-size'),
2105
- color = parseColor(element.attr('md-ink-ripple')) || parseColor($window.getComputedStyle(options.colorElement[0]).color || 'rgb(0, 0, 0)');
2125
+ color = parseColor(element.attr('md-ink-ripple')) || parseColor(options.colorElement.length && $window.getComputedStyle(options.colorElement[0]).color || 'rgb(0, 0, 0)');
2106
2126
 
2107
2127
  switch (rippleSizeSetting) {
2108
2128
  case 'full':
@@ -2322,8 +2342,8 @@ function InkRippleService($window, $timeout) {
2322
2342
  * @returns {{backgroundColor: string, borderColor: string, width: string, height: string}}
2323
2343
  */
2324
2344
  function getRippleCss(size, left, top) {
2325
- var rect,
2326
- css = {
2345
+ var rect = node.getBoundingClientRect(),
2346
+ css = {
2327
2347
  backgroundColor: rgbaToRGB(color),
2328
2348
  borderColor: rgbaToRGB(color),
2329
2349
  width: size + 'px',
@@ -2340,7 +2360,6 @@ function InkRippleService($window, $timeout) {
2340
2360
  if (options.center) {
2341
2361
  css.left = css.top = '50%';
2342
2362
  } else {
2343
- rect = node.getBoundingClientRect();
2344
2363
  css.left = Math.round((left - rect.left) / container.prop('offsetWidth') * 100) + '%';
2345
2364
  css.top = Math.round((top - rect.top) / container.prop('offsetHeight') * 100) + '%';
2346
2365
  }
@@ -3012,7 +3031,7 @@ function ThemingProvider($mdColorPalette) {
3012
3031
 
3013
3032
  self.foregroundPalette = self.isDark ? LIGHT_FOREGROUND : DARK_FOREGROUND;
3014
3033
  self.foregroundShadow = self.isDark ? DARK_SHADOW : LIGHT_SHADOW;
3015
-
3034
+
3016
3035
  // Light and dark themes have different default hues.
3017
3036
  // Go through each existing color type for this theme, and for every
3018
3037
  // hue value that is still the default hue value from the previous light/dark setting,
@@ -3330,12 +3349,12 @@ function generateThemes($injector) {
3330
3349
  if (darkColors.indexOf(hueName) > -1) {
3331
3350
  return DARK_CONTRAST_COLOR;
3332
3351
  } else {
3333
- return strongLightColors.indexOf(hueName) > -1 ? STRONG_LIGHT_CONTRAST_COLOR
3352
+ return strongLightColors.indexOf(hueName) > -1 ? STRONG_LIGHT_CONTRAST_COLOR
3334
3353
  : LIGHT_CONTRAST_COLOR;
3335
3354
  }
3336
3355
  } else {
3337
3356
  if (lightColors.indexOf(hueName) > -1) {
3338
- return strongLightColors.indexOf(hueName) > -1 ? STRONG_LIGHT_CONTRAST_COLOR
3357
+ return strongLightColors.indexOf(hueName) > -1 ? STRONG_LIGHT_CONTRAST_COLOR
3339
3358
  : LIGHT_CONTRAST_COLOR;
3340
3359
  } else {
3341
3360
  return DARK_CONTRAST_COLOR;
@@ -3544,8 +3563,9 @@ function MdBottomSheetDirective() {
3544
3563
  * - `resolve` - `{object=}`: Similar to locals, except it takes promises as values
3545
3564
  * and the bottom sheet will not open until the promises resolve.
3546
3565
  * - `controllerAs` - `{string=}`: An alias to assign the controller to on the scope.
3547
- * - `parent` - `{element=}`: The element to append the bottom sheet to. Defaults to appending
3548
- * to the root element of the application.
3566
+ * - `parent` - `{element=}`: The element to append the bottom sheet to. The `parent` may be a `function`, `string`,
3567
+ * `object`, or null. Defaults to appending to the body of the root element (or the root element) of the application.
3568
+ * e.g. angular.element(document.getElementById('content')) or "#content"
3549
3569
  * - `disableParentScroll` - `{boolean=}`: Whether to disable scrolling while the bottom sheet is open.
3550
3570
  * Default true.
3551
3571
  *
@@ -3559,7 +3579,7 @@ function MdBottomSheetDirective() {
3559
3579
  *
3560
3580
  * @description
3561
3581
  * Hide the existing bottom sheet and resolve the promise returned from
3562
- * `$mdBottomSheet.show()`.
3582
+ * `$mdBottomSheet.show()`. This call will close the most recently opened/current bottomsheet (if any).
3563
3583
  *
3564
3584
  * @param {*=} response An argument for the resolved promise.
3565
3585
  *
@@ -3582,7 +3602,7 @@ function MdBottomSheetProvider($$interimElementProvider) {
3582
3602
  var CLOSING_VELOCITY = 0.5;
3583
3603
  var PADDING = 80; // same as css
3584
3604
 
3585
- bottomSheetDefaults.$inject = ["$animate", "$mdConstant", "$timeout", "$$rAF", "$compile", "$mdTheming", "$mdBottomSheet", "$rootElement", "$rootScope", "$mdGesture"];
3605
+ bottomSheetDefaults.$inject = ["$animate", "$mdConstant", "$mdUtil", "$timeout", "$compile", "$mdTheming", "$mdBottomSheet", "$rootElement", "$mdGesture"];
3586
3606
  return $$interimElementProvider('$mdBottomSheet')
3587
3607
  .setDefaults({
3588
3608
  methods: ['disableParentScroll', 'escapeToClose', 'targetEvent'],
@@ -3590,7 +3610,7 @@ function MdBottomSheetProvider($$interimElementProvider) {
3590
3610
  });
3591
3611
 
3592
3612
  /* @ngInject */
3593
- function bottomSheetDefaults($animate, $mdConstant, $timeout, $$rAF, $compile, $mdTheming, $mdBottomSheet, $rootElement, $rootScope, $mdGesture) {
3613
+ function bottomSheetDefaults($animate, $mdConstant, $mdUtil, $timeout, $compile, $mdTheming, $mdBottomSheet, $rootElement, $mdGesture) {
3594
3614
  var backdrop;
3595
3615
 
3596
3616
  return {
@@ -3602,13 +3622,16 @@ function MdBottomSheetProvider($$interimElementProvider) {
3602
3622
  disableParentScroll: true
3603
3623
  };
3604
3624
 
3625
+
3605
3626
  function onShow(scope, element, options) {
3627
+
3628
+ element = $mdUtil.extractElementByName(element, 'md-bottom-sheet');
3629
+
3606
3630
  // Add a backdrop that will close on click
3607
3631
  backdrop = $compile('<md-backdrop class="md-opaque md-bottom-sheet-backdrop">')(scope);
3608
3632
  backdrop.on('click', function() {
3609
3633
  $timeout($mdBottomSheet.cancel);
3610
3634
  });
3611
-
3612
3635
  $mdTheming.inherit(backdrop, options.parent);
3613
3636
 
3614
3637
  $animate.enter(backdrop, options.parent, null);
@@ -3647,8 +3670,8 @@ function MdBottomSheetProvider($$interimElementProvider) {
3647
3670
  }
3648
3671
 
3649
3672
  function onRemove(scope, element, options) {
3650
- var bottomSheet = options.bottomSheet;
3651
3673
 
3674
+ var bottomSheet = options.bottomSheet;
3652
3675
 
3653
3676
  $animate.leave(backdrop);
3654
3677
  return $animate.leave(bottomSheet.element).then(function() {
@@ -3765,13 +3788,22 @@ angular.module('material.components.button', [
3765
3788
  * @usage
3766
3789
  * <hljs lang="html">
3767
3790
  * <md-button>
3768
- * Button
3791
+ * Flat Button
3792
+ * </md-button>
3793
+ * <md-button href="http://google.com">
3794
+ * Flat link
3795
+ * </md-button>
3796
+ * <md-button class="md-raised">
3797
+ * Raised Button
3798
+ * </md-button>
3799
+ * <md-button ng-disabled="true">
3800
+ * Disabled Button
3769
3801
  * </md-button>
3770
- * <md-button href="http://google.com" class="md-button-colored">
3771
- * I'm a link
3802
+ * <md-button class="md-fab" aria-label="FAB">
3803
+ * <md-icon md-svg-src="your/icon.svg"></md-icon>
3772
3804
  * </md-button>
3773
- * <md-button ng-disabled="true" class="md-colored">
3774
- * I'm a disabled button
3805
+ * <md-button class="md-fab md-mini" aria-label="Mini FAB">
3806
+ * <md-icon md-svg-src="your/icon.svg"></md-icon>
3775
3807
  * </md-button>
3776
3808
  * </hljs>
3777
3809
  */
@@ -3786,9 +3818,9 @@ function MdButtonDirective($mdInkRipple, $mdTheming, $mdAria) {
3786
3818
  };
3787
3819
 
3788
3820
  function isAnchor(attr) {
3789
- return angular.isDefined(attr.href) || angular.isDefined(attr.ngHref);
3821
+ return angular.isDefined(attr.href) || angular.isDefined(attr.ngHref) || angular.isDefined(attr.uiSref);
3790
3822
  }
3791
-
3823
+
3792
3824
  function getTemplate(element, attr) {
3793
3825
  return isAnchor(attr) ?
3794
3826
  '<a class="md-button" ng-transclude></a>' :
@@ -3805,7 +3837,7 @@ function MdButtonDirective($mdInkRipple, $mdTheming, $mdAria) {
3805
3837
  $mdAria.expect(element, 'aria-label');
3806
3838
  }
3807
3839
 
3808
- // For anchor elements, we have to set tabindex manually when the
3840
+ // For anchor elements, we have to set tabindex manually when the
3809
3841
  // element is disabled
3810
3842
  if (isAnchor(attr) && angular.isDefined(attr.ngDisabled) ) {
3811
3843
  scope.$watch(attr.ngDisabled, function(isDisabled) {
@@ -3943,7 +3975,7 @@ function MdCheckboxDirective(inputDirective, $mdInkRipple, $mdAria, $mdConstant,
3943
3975
  restrict: 'E',
3944
3976
  transclude: true,
3945
3977
  require: '?ngModel',
3946
- template:
3978
+ template:
3947
3979
  '<div class="md-container" md-ink-ripple md-ink-ripple-checkbox>' +
3948
3980
  '<div class="md-icon"></div>' +
3949
3981
  '</div>' +
@@ -4018,6 +4050,26 @@ MdCheckboxDirective.$inject = ["inputDirective", "$mdInkRipple", "$mdAria", "$md
4018
4050
 
4019
4051
  })();
4020
4052
 
4053
+ /*!
4054
+ * Angular Material Design
4055
+ * https://github.com/angular/material
4056
+ * @license MIT
4057
+ * v0.8.3
4058
+ */
4059
+ (function () {
4060
+ 'use strict';
4061
+ /**
4062
+ * @ngdoc module
4063
+ * @name material.components.chips
4064
+ */
4065
+ /*
4066
+ * @see js folder for chips implementation
4067
+ */
4068
+ angular.module('material.components.chips', [
4069
+ 'material.core'
4070
+ ]);
4071
+ })();
4072
+
4021
4073
  /*!
4022
4074
  * Angular Material Design
4023
4075
  * https://github.com/angular/material
@@ -4156,6 +4208,12 @@ MdDialogDirective.$inject = ["$$rAF", "$mdTheming"];
4156
4208
  * - The dialog's template must have an outer `<md-dialog>` element.
4157
4209
  * Inside, use an `<md-content>` element for the dialog's content, and use
4158
4210
  * an element with class `md-actions` for the dialog's actions.
4211
+ * - Dialogs must cover the entire application to keep interactions inside of them.
4212
+ * Use the `parent` option to change where dialogs are appended.
4213
+ *
4214
+ * ## Sizing
4215
+ * - Complex dialogs can be sized with `flex="percentage"`, i.e. `flex="66"`.
4216
+ * - Default max-width is 80% of the `rootElement` or `parent`.
4159
4217
  *
4160
4218
  * @usage
4161
4219
  * ### HTML
@@ -4388,7 +4446,8 @@ MdDialogDirective.$inject = ["$$rAF", "$mdTheming"];
4388
4446
  * - `targetEvent` - `{DOMClickEvent=}`: A click's event object. When passed in as an option,
4389
4447
  * the location of the click will be used as the starting point for the opening animation
4390
4448
  * of the the dialog.
4391
- * - `scope` - `{object=}`: the scope to link the template / controller to. If none is specified, it will create a new isolate scope.
4449
+ * - `scope` - `{object=}`: the scope to link the template / controller to. If none is specified,
4450
+ * it will create a new isolate scope.
4392
4451
  * This scope will be destroyed when the dialog is removed unless `preserveScope` is set to true.
4393
4452
  * - `preserveScope` - `{boolean=}`: whether to preserve the scope when the element is removed. Default is false
4394
4453
  * - `disableParentScroll` - `{boolean=}`: Whether to disable scrolling while the dialog is open.
@@ -4396,16 +4455,20 @@ MdDialogDirective.$inject = ["$$rAF", "$mdTheming"];
4396
4455
  * - `hasBackdrop` - `{boolean=}`: Whether there should be an opaque backdrop behind the dialog.
4397
4456
  * Default true.
4398
4457
  * - `clickOutsideToClose` - `{boolean=}`: Whether the user can click outside the dialog to
4399
- * close it. Default true.
4458
+ * close it. Default false.
4400
4459
  * - `escapeToClose` - `{boolean=}`: Whether the user can press escape to close the dialog.
4401
4460
  * Default true.
4461
+ * - `focusOnOpen` - `{boolean=}`: An option to override focus behavior on open. Only disable if
4462
+ * focusing some other way, as focus management is required for dialogs to be accessible.
4463
+ * Defaults to true.
4402
4464
  * - `controller` - `{string=}`: The controller to associate with the dialog. The controller
4403
4465
  * will be injected with the local `$mdDialog`, which passes along a scope for the dialog.
4404
4466
  * - `locals` - `{object=}`: An object containing key/value pairs. The keys will be used as names
4405
4467
  * of values to inject into the controller. For example, `locals: {three: 3}` would inject
4406
4468
  * `three` into the controller, with the value 3. If `bindToController` is true, they will be
4407
4469
  * copied to the controller instead.
4408
- * - `bindToController` - `bool`: bind the locals to the controller, instead of passing them in. These values will not be available until after initialization.
4470
+ * - `bindToController` - `bool`: bind the locals to the controller, instead of passing them in.
4471
+ * These values will not be available until after initialization.
4409
4472
  * - `resolve` - `{object=}`: Similar to locals, except it takes promises as values, and the
4410
4473
  * dialog will not open until all of the promises resolve.
4411
4474
  * - `controllerAs` - `{string=}`: An alias to assign the controller to on the scope.
@@ -4443,10 +4506,10 @@ function MdDialogProvider($$interimElementProvider) {
4443
4506
  var alertDialogMethods = ['title', 'content', 'ariaLabel', 'ok'];
4444
4507
 
4445
4508
  advancedDialogOptions.$inject = ["$mdDialog", "$mdTheming"];
4446
- dialogDefaultOptions.$inject = ["$timeout", "$rootElement", "$compile", "$animate", "$mdAria", "$document", "$mdUtil", "$mdConstant", "$mdTheming", "$$rAF", "$q", "$mdDialog"];
4509
+ dialogDefaultOptions.$inject = ["$mdAria", "$document", "$mdUtil", "$mdConstant", "$mdTheming", "$mdDialog", "$timeout", "$rootElement", "$animate", "$$rAF", "$q"];
4447
4510
  return $$interimElementProvider('$mdDialog')
4448
4511
  .setDefaults({
4449
- methods: ['disableParentScroll', 'hasBackdrop', 'clickOutsideToClose', 'escapeToClose', 'targetEvent'],
4512
+ methods: ['disableParentScroll', 'hasBackdrop', 'clickOutsideToClose', 'escapeToClose', 'targetEvent', 'parent'],
4450
4513
  options: dialogDefaultOptions
4451
4514
  })
4452
4515
  .addPreset('alert', {
@@ -4463,8 +4526,8 @@ function MdDialogProvider($$interimElementProvider) {
4463
4526
  return {
4464
4527
  template: [
4465
4528
  '<md-dialog md-theme="{{ dialog.theme }}" aria-label="{{ dialog.ariaLabel }}">',
4466
- '<md-content>',
4467
- '<h2>{{ dialog.title }}</h2>',
4529
+ '<md-content role="document" tabIndex="0">',
4530
+ '<h2 class="md-title">{{ dialog.title }}</h2>',
4468
4531
  '<p>{{ dialog.content }}</p>',
4469
4532
  '</md-content>',
4470
4533
  '<div class="md-actions">',
@@ -4492,44 +4555,65 @@ function MdDialogProvider($$interimElementProvider) {
4492
4555
  }
4493
4556
 
4494
4557
  /* @ngInject */
4495
- function dialogDefaultOptions($timeout, $rootElement, $compile, $animate, $mdAria, $document,
4496
- $mdUtil, $mdConstant, $mdTheming, $$rAF, $q, $mdDialog) {
4558
+ function dialogDefaultOptions($mdAria, $document, $mdUtil, $mdConstant, $mdTheming, $mdDialog, $timeout, $rootElement, $animate, $$rAF, $q) {
4497
4559
  return {
4498
4560
  hasBackdrop: true,
4499
4561
  isolateScope: true,
4500
4562
  onShow: onShow,
4501
4563
  onRemove: onRemove,
4502
- clickOutsideToClose: true,
4564
+ clickOutsideToClose: false,
4503
4565
  escapeToClose: true,
4504
4566
  targetEvent: null,
4567
+ focusOnOpen: true,
4505
4568
  disableParentScroll: true,
4506
4569
  transformTemplate: function(template) {
4507
4570
  return '<div class="md-dialog-container">' + template + '</div>';
4508
4571
  }
4509
4572
  };
4510
4573
 
4574
+ function trapFocus(ev) {
4575
+ var dialog = document.querySelector('md-dialog');
4576
+
4577
+ if (dialog && !dialog.contains(ev.target)) {
4578
+ ev.stopImmediatePropagation();
4579
+ dialog.focus();
4580
+ }
4581
+ }
4511
4582
 
4512
4583
  // On show method for dialogs
4513
4584
  function onShow(scope, element, options) {
4585
+ element = $mdUtil.extractElementByName(element, 'md-dialog');
4586
+
4514
4587
  // Incase the user provides a raw dom element, always wrap it in jqLite
4515
4588
  options.parent = angular.element(options.parent);
4516
4589
 
4517
4590
  options.popInTarget = angular.element((options.targetEvent || {}).target);
4518
4591
  var closeButton = findCloseButton();
4519
4592
 
4520
- configureAria(element.find('md-dialog'));
4521
-
4522
4593
  if (options.hasBackdrop) {
4523
4594
  // Fix for IE 10
4524
4595
  var computeFrom = (options.parent[0] == $document[0].body && $document[0].documentElement
4525
- && $document[0].scrollTop) ? angular.element($document[0].documentElement) : options.parent;
4596
+ && $document[0].documentElement.scrollTop) ? angular.element($document[0].documentElement) : options.parent;
4526
4597
  var parentOffset = computeFrom.prop('scrollTop');
4527
4598
  options.backdrop = angular.element('<md-backdrop class="md-dialog-backdrop md-opaque">');
4599
+ options.backdrop.css('top', parentOffset +'px');
4528
4600
  $mdTheming.inherit(options.backdrop, options.parent);
4529
4601
  $animate.enter(options.backdrop, options.parent);
4530
4602
  element.css('top', parentOffset +'px');
4531
4603
  }
4532
4604
 
4605
+ var role = 'dialog',
4606
+ elementToFocus = closeButton;
4607
+
4608
+ if (options.$type === 'alert') {
4609
+ role = 'alertdialog';
4610
+ elementToFocus = element.find('md-content');
4611
+ }
4612
+
4613
+ configureAria(element.find('md-dialog'), role, options);
4614
+
4615
+ document.addEventListener('focus', trapFocus, true);
4616
+
4533
4617
  if (options.disableParentScroll) {
4534
4618
  options.lastOverflow = options.parent.css('overflow');
4535
4619
  options.parent.css('overflow', 'hidden');
@@ -4541,6 +4625,9 @@ function MdDialogProvider($$interimElementProvider) {
4541
4625
  options.popInTarget && options.popInTarget.length && options.popInTarget
4542
4626
  )
4543
4627
  .then(function() {
4628
+
4629
+ applyAriaToSiblings(element, true);
4630
+
4544
4631
  if (options.escapeToClose) {
4545
4632
  options.rootElementKeyupCallback = function(e) {
4546
4633
  if (e.keyCode === $mdConstant.KEY_CODE.ESCAPE) {
@@ -4559,7 +4646,10 @@ function MdDialogProvider($$interimElementProvider) {
4559
4646
  };
4560
4647
  element.on('click', options.dialogClickOutsideCallback);
4561
4648
  }
4562
- closeButton.focus();
4649
+
4650
+ if (options.focusOnOpen) {
4651
+ elementToFocus.focus();
4652
+ }
4563
4653
  });
4564
4654
 
4565
4655
 
@@ -4592,6 +4682,11 @@ function MdDialogProvider($$interimElementProvider) {
4592
4682
  if (options.clickOutsideToClose) {
4593
4683
  element.off('click', options.dialogClickOutsideCallback);
4594
4684
  }
4685
+
4686
+ applyAriaToSiblings(element, false);
4687
+
4688
+ document.removeEventListener('focus', trapFocus, true);
4689
+
4595
4690
  return dialogPopOut(
4596
4691
  element,
4597
4692
  options.parent,
@@ -4607,20 +4702,72 @@ function MdDialogProvider($$interimElementProvider) {
4607
4702
  /**
4608
4703
  * Inject ARIA-specific attributes appropriate for Dialogs
4609
4704
  */
4610
- function configureAria(element) {
4705
+ function configureAria(element, role, options) {
4706
+
4611
4707
  element.attr({
4612
- 'role': 'dialog'
4708
+ 'role': role,
4709
+ 'tabIndex': '-1'
4613
4710
  });
4614
4711
 
4615
4712
  var dialogContent = element.find('md-content');
4616
4713
  if (dialogContent.length === 0){
4617
4714
  dialogContent = element;
4618
4715
  }
4619
- $mdAria.expectAsync(element, 'aria-label', function() {
4620
- var words = dialogContent.text().split(/\s+/);
4621
- if (words.length > 3) words = words.slice(0,3).concat('...');
4622
- return words.join(' ');
4623
- });
4716
+
4717
+ var dialogId = element.attr('id') || ('dialog_' + $mdUtil.nextUid());
4718
+ dialogContent.attr('id', dialogId);
4719
+ element.attr('aria-describedby', dialogId);
4720
+
4721
+ if (options.ariaLabel) {
4722
+ $mdAria.expect(element, 'aria-label', options.ariaLabel);
4723
+ }
4724
+ else {
4725
+ $mdAria.expectAsync(element, 'aria-label', function() {
4726
+ var words = dialogContent.text().split(/\s+/);
4727
+ if (words.length > 3) words = words.slice(0,3).concat('...');
4728
+ return words.join(' ');
4729
+ });
4730
+ }
4731
+ }
4732
+ /**
4733
+ * Utility function to filter out raw DOM nodes
4734
+ */
4735
+ function isNodeOneOf(elem, nodeTypeArray) {
4736
+ if (nodeTypeArray.indexOf(elem.nodeName) !== -1) {
4737
+ return true;
4738
+ }
4739
+ }
4740
+ /**
4741
+ * Walk DOM to apply or remove aria-hidden on sibling nodes
4742
+ * and parent sibling nodes
4743
+ *
4744
+ * Prevents screen reader interaction behind modal window
4745
+ * on swipe interfaces
4746
+ */
4747
+ function applyAriaToSiblings(element, value) {
4748
+ var attribute = 'aria-hidden';
4749
+
4750
+ // get raw DOM node
4751
+ element = element[0];
4752
+
4753
+ function walkDOM(element) {
4754
+ while (element.parentNode) {
4755
+ if (element === document.body) {
4756
+ return;
4757
+ }
4758
+ var children = element.parentNode.children;
4759
+ for (var i = 0; i < children.length; i++) {
4760
+ // skip over child if it is an ascendant of the dialog
4761
+ // or a script or style tag
4762
+ if (element !== children[i] && !isNodeOneOf(children[i], ['SCRIPT', 'STYLE'])) {
4763
+ children[i].setAttribute(attribute, value);
4764
+ }
4765
+ }
4766
+
4767
+ walkDOM(element = element.parentNode);
4768
+ }
4769
+ }
4770
+ walkDOM(element);
4624
4771
  }
4625
4772
 
4626
4773
  function dialogPopIn(container, parentElement, clickElement) {
@@ -4834,7 +4981,7 @@ angular.module('material.components.gridList', ['material.core'])
4834
4981
  * </md-grid-list>
4835
4982
  * </hljs>
4836
4983
  */
4837
- function GridListDirective($interpolate, $mdConstant, $mdGridLayout, $mdMedia, $mdUtil) {
4984
+ function GridListDirective($interpolate, $mdConstant, $mdGridLayout, $mdMedia) {
4838
4985
  return {
4839
4986
  restrict: 'E',
4840
4987
  controller: GridListController,
@@ -4849,7 +4996,7 @@ function GridListDirective($interpolate, $mdConstant, $mdGridLayout, $mdMedia, $
4849
4996
  element.attr('role', 'list');
4850
4997
 
4851
4998
  // Provide the controller with a way to trigger layouts.
4852
- ctrl.layoutDelegate = layoutDelegate
4999
+ ctrl.layoutDelegate = layoutDelegate;
4853
5000
 
4854
5001
  var invalidateLayout = angular.bind(ctrl, ctrl.invalidateLayout),
4855
5002
  unwatchAttrs = watchMedia();
@@ -4865,7 +5012,7 @@ function GridListDirective($interpolate, $mdConstant, $mdGridLayout, $mdMedia, $
4865
5012
  .addListener(invalidateLayout);
4866
5013
  }
4867
5014
  return $mdMedia.watchResponsiveAttributes(
4868
- ['md-cols', 'md-row-height'], attrs, layoutIfMediaMatch);;
5015
+ ['md-cols', 'md-row-height'], attrs, layoutIfMediaMatch);
4869
5016
  }
4870
5017
 
4871
5018
  function unwatchMedia() {
@@ -4929,17 +5076,60 @@ function GridListDirective($interpolate, $mdConstant, $mdGridLayout, $mdMedia, $
4929
5076
  });
4930
5077
  }
4931
5078
 
4932
- var UNIT = $interpolate( "{{ share }}% - ({{ gutter }} * {{ gutterShare }})" );
4933
- var POSITION = $interpolate( "calc(({{ unit }}) * {{ offset }} + {{ offset }} * {{ gutter }})" );
4934
- var DIMENSION = $interpolate( "calc(({{ unit }}) * {{ span }} + ({{ span }} - 1) * {{ gutter }})" );
5079
+ // Use $interpolate to do some simple string interpolation as a convenience.
5080
+
5081
+ // The amount of space a single 1x1 tile would take up (either width or height), used as
5082
+ // a basis for other calculations. This consists of taking the base size percent (as would be
5083
+ // if evenly dividing the size between cells), and then subtracting the size of one gutter.
5084
+ // However, since there are no gutters on the edges, each tile only uses a fration
5085
+ // (gutterShare = numGutters / numCells) of the gutter size. (Imagine having one gutter per
5086
+ // tile, and then breaking up the extra gutter on the edge evenly among the cells).
5087
+ var UNIT = $interpolate('{{share}}% - ( {{gutter}} * {{gutterShare}} )');
5088
+
5089
+ // The horizontal or vertical position of a tile, e.g., the 'top' or 'left' property value.
5090
+ // The position comes the size of a 1x1 tile plus gutter for each previous tile in the
5091
+ // row/column (offset).
5092
+ var POSITION = $interpolate('calc( ( ({{unit}}) + {{gutter}} ) * {{offset}} ');
5093
+
5094
+ // The actual size of a tile, e.g., width or height, taking rowSpan or colSpan into account.
5095
+ // This is computed by multiplying the base unit by the rowSpan/colSpan, and then adding back
5096
+ // in the space that the gutter would normally have used (which was already accounted for in
5097
+ // the base unit calculation).
5098
+ var DIMENSION = $interpolate('calc(({{unit}}) * {{span}} + ({{span}} - 1) * {{gutter}})');
4935
5099
 
4936
5100
  // TODO(shyndman): Replace args with a ctx object.
5101
+
5102
+ /**
5103
+ * Gets the styles applied to a tile element described by the given parameters.
5104
+ * @param {{row: number, col: number}} position The row and column indices of the tile.
5105
+ * @param {{row: number, col: number}} spans The rowSpan and colSpan of the tile.
5106
+ * @param {number} colCount The number of columns.
5107
+ * @param {number} rowCount The number of rows.
5108
+ * @param {string} gutter The amount of space between tiles. This will be something like
5109
+ * '5px' or '2em'.
5110
+ * @param {string} rowMode The row height mode. Can be one of:
5111
+ * 'fixed': all rows have a fixed size, given by rowHeight,
5112
+ * 'ratio': row height defined as a ratio to width, or
5113
+ * 'fit': fit to the grid-list element height, divinding evenly among rows.
5114
+ * @param {string|number} rowHeight The height of a row. This is only used for 'fixed' mode and
5115
+ * for 'ratio' mode. For 'ratio' mode, this is the *ratio* of width-to-height (e.g., 0.75).
5116
+ * @returns {Object} Map of CSS properties to be applied to the style element. Will define
5117
+ * values for top, left, width, height, marginTop, and paddingTop.
5118
+ */
4937
5119
  function getTileStyle(position, spans, colCount, rowCount, gutter, rowMode, rowHeight) {
4938
5120
  // TODO(shyndman): There are style caching opportunities here.
4939
- var hShare = (1 / colCount) * 100,
4940
- hGutterShare = colCount === 1 ? 0 : (colCount - 1) / colCount,
4941
- hUnit = UNIT({ share: hShare, gutterShare: hGutterShare, gutter: gutter });
4942
5121
 
5122
+ // Percent of the available horizontal space that one column takes up.
5123
+ var hShare = (1 / colCount) * 100;
5124
+
5125
+ // Fraction of the gutter size that each column takes up.
5126
+ var hGutterShare = (colCount - 1) / colCount;
5127
+
5128
+ // Base horizontal size of a column.
5129
+ var hUnit = UNIT({share: hShare, gutterShare: hGutterShare, gutter: gutter});
5130
+
5131
+ // The width and horizontal position of each tile is always calculated the same way, but the
5132
+ // height and vertical position depends on the rowMode.
4943
5133
  var style = {
4944
5134
  left: POSITION({ unit: hUnit, offset: position.col, gutter: gutter }),
4945
5135
  width: DIMENSION({ unit: hUnit, span: spans.col, gutter: gutter }),
@@ -4952,26 +5142,38 @@ function GridListDirective($interpolate, $mdConstant, $mdGridLayout, $mdMedia, $
4952
5142
 
4953
5143
  switch (rowMode) {
4954
5144
  case 'fixed':
5145
+ // In fixed mode, simply use the given rowHeight.
4955
5146
  style.top = POSITION({ unit: rowHeight, offset: position.row, gutter: gutter });
4956
5147
  style.height = DIMENSION({ unit: rowHeight, span: spans.row, gutter: gutter });
4957
5148
  break;
4958
5149
 
4959
5150
  case 'ratio':
4960
- // rowHeight is width / height
4961
- var vShare = hShare * (1 / rowHeight),
4962
- vUnit = UNIT({ share: vShare, gutterShare: hGutterShare, gutter: gutter });
5151
+ // Percent of the available vertical space that one row takes up. Here, rowHeight holds
5152
+ // the ratio value. For example, if the width:height ratio is 4:3, rowHeight = 1.333.
5153
+ var vShare = hShare / rowHeight;
5154
+
5155
+ // Base veritcal size of a row.
5156
+ var vUnit = UNIT({ share: vShare, gutterShare: hGutterShare, gutter: gutter });
4963
5157
 
5158
+ // padidngTop and marginTop are used to maintain the given aspect ratio, as
5159
+ // a percentage-based value for these properties is applied to the *width* of the
5160
+ // containing block. See http://www.w3.org/TR/CSS2/box.html#margin-properties
4964
5161
  style.paddingTop = DIMENSION({ unit: vUnit, span: spans.row, gutter: gutter});
4965
5162
  style.marginTop = POSITION({ unit: vUnit, offset: position.row, gutter: gutter });
4966
5163
  break;
4967
5164
 
4968
5165
  case 'fit':
4969
- var vGutterShare = rowCount === 1 ? 0 : (rowCount - 1) / rowCount,
4970
- vShare = (1 / rowCount) * 100,
4971
- vUnit = UNIT({ share: vShare, gutterShare: vGutterShare, gutter: gutter });
5166
+ // Fraction of the gutter size that each column takes up.
5167
+ var vGutterShare = (rowCount - 1) / rowCount;
5168
+
5169
+ // Percent of the available vertical space that one row takes up.
5170
+ var vShare = (1 / rowCount) * 100;
5171
+
5172
+ // Base veritcal size of a row.
5173
+ var vUnit = UNIT({share: vShare, gutterShare: vGutterShare, gutter: gutter});
4972
5174
 
4973
- style.top = POSITION({ unit: vUnit, offset: position.row, gutter: gutter });
4974
- style.height = DIMENSION({ unit: vUnit, span: spans.row, gutter: gutter });
5175
+ style.top = POSITION({unit: vUnit, offset: position.row, gutter: gutter});
5176
+ style.height = DIMENSION({unit: vUnit, span: spans.row, gutter: gutter});
4975
5177
  break;
4976
5178
  }
4977
5179
 
@@ -5011,6 +5213,10 @@ function GridListDirective($interpolate, $mdConstant, $mdGridLayout, $mdMedia, $
5011
5213
  return ctrl.tiles.map(function(tile) { return tile.element });
5012
5214
  }
5013
5215
 
5216
+ /**
5217
+ * Gets an array of objects containing the rowspan and colspan for each tile.
5218
+ * @returns {Array<{row: number, col: number}>}
5219
+ */
5014
5220
  function getTileSpans() {
5015
5221
  return ctrl.tiles.map(function(tile) {
5016
5222
  return {
@@ -5063,7 +5269,7 @@ function GridListDirective($interpolate, $mdConstant, $mdGridLayout, $mdMedia, $
5063
5269
  }
5064
5270
  }
5065
5271
  }
5066
- GridListDirective.$inject = ["$interpolate", "$mdConstant", "$mdGridLayout", "$mdMedia", "$mdUtil"];
5272
+ GridListDirective.$inject = ["$interpolate", "$mdConstant", "$mdGridLayout", "$mdMedia"];
5067
5273
 
5068
5274
  /* @ngInject */
5069
5275
  function GridListController($timeout) {
@@ -5118,7 +5324,7 @@ GridListController.prototype = {
5118
5324
  }
5119
5325
  return -1;
5120
5326
  }
5121
- }
5327
+ };
5122
5328
 
5123
5329
 
5124
5330
  /* @ngInject */
@@ -5128,7 +5334,7 @@ function GridLayoutFactory($mdUtil) {
5128
5334
  /**
5129
5335
  * Set the reflow animator callback
5130
5336
  */
5131
- GridLayout.animateWith =function(customAnimator) {
5337
+ GridLayout.animateWith = function(customAnimator) {
5132
5338
  defaultAnimator = !angular.isFunction(customAnimator) ? GridTileAnimator : customAnimator;
5133
5339
  };
5134
5340
 
@@ -5138,7 +5344,7 @@ function GridLayoutFactory($mdUtil) {
5138
5344
  * Publish layout function
5139
5345
  */
5140
5346
  function GridLayout(colCount, tileSpans) {
5141
- var self, layoutInfo, gridStyles, layoutTime, mapTime, reflowTime, layoutInfo;
5347
+ var self, layoutInfo, gridStyles, layoutTime, mapTime, reflowTime;
5142
5348
 
5143
5349
  layoutTime = $mdUtil.time(function() {
5144
5350
  layoutInfo = calculateGridFor(colCount, tileSpans);
@@ -5241,7 +5447,7 @@ function GridLayoutFactory($mdUtil) {
5241
5447
  };
5242
5448
  }),
5243
5449
  rowCount: curRow + Math.max.apply(Math, spaceTracker)
5244
- }
5450
+ };
5245
5451
 
5246
5452
  function reserveSpace(spans, i) {
5247
5453
  if (spans.col > colCount) {
@@ -5754,17 +5960,27 @@ mdIconDirective.$inject = ["$mdIcon", "$mdTheming", "$mdAria"];
5754
5960
  {
5755
5961
  id : 'tabs-arrow',
5756
5962
  url: 'tabs-arrow.svg',
5757
- svg: '<svg version="1.1" x="0px" y="0px" viewBox="0 0 24 24"><g id="tabs-arrow"><polygon points="15.4,7.4 14,6 8,12 14,18 15.4,16.6 10.8,12 "/></g></svg>'
5963
+ svg: '<svg version="1.1" x="0px" y="0px" viewBox="0 0 24 24"><g><polygon points="15.4,7.4 14,6 8,12 14,18 15.4,16.6 10.8,12 "/></g></svg>'
5758
5964
  },
5759
5965
  {
5760
5966
  id : 'close',
5761
5967
  url: 'close.svg',
5762
- svg: '<svg version="1.1" x="0px" y="0px" viewBox="0 0 24 24"><g id="close"><path d="M19 6.41l-1.41-1.41-5.59 5.59-5.59-5.59-1.41 1.41 5.59 5.59-5.59 5.59 1.41 1.41 5.59-5.59 5.59 5.59 1.41-1.41-5.59-5.59z"/></g></svg>'
5968
+ svg: '<svg version="1.1" x="0px" y="0px" viewBox="0 0 24 24"><g><path d="M19 6.41l-1.41-1.41-5.59 5.59-5.59-5.59-1.41 1.41 5.59 5.59-5.59 5.59 1.41 1.41 5.59-5.59 5.59 5.59 1.41-1.41-5.59-5.59z"/></g></svg>'
5763
5969
  },
5764
5970
  {
5765
5971
  id: 'cancel',
5766
5972
  url: 'cancel.svg',
5767
- svg: '<svg version="1.1" x="0px" y="0px" viewBox="0 0 24 24"><g id="cancel"><path d="M12 2c-5.53 0-10 4.47-10 10s4.47 10 10 10 10-4.47 10-10-4.47-10-10-10zm5 13.59l-1.41 1.41-3.59-3.59-3.59 3.59-1.41-1.41 3.59-3.59-3.59-3.59 1.41-1.41 3.59 3.59 3.59-3.59 1.41 1.41-3.59 3.59 3.59 3.59z"/></g></svg>'
5973
+ svg: '<svg version="1.1" x="0px" y="0px" viewBox="0 0 24 24"><g><path d="M12 2c-5.53 0-10 4.47-10 10s4.47 10 10 10 10-4.47 10-10-4.47-10-10-10zm5 13.59l-1.41 1.41-3.59-3.59-3.59 3.59-1.41-1.41 3.59-3.59-3.59-3.59 1.41-1.41 3.59 3.59 3.59-3.59 1.41 1.41-3.59 3.59 3.59 3.59z"/></g></svg>'
5974
+ },
5975
+ {
5976
+ id: 'menu',
5977
+ url: 'menu.svg',
5978
+ svg: '<svg version="1.1" x="0px" y="0px" viewBox="0 0 100 100"><path d="M 50 0 L 100 14 L 92 80 L 50 100 L 8 80 L 0 14 Z" fill="#b2b2b2"></path><path d="M 50 5 L 6 18 L 13.5 77 L 50 94 Z" fill="#E42939"></path><path d="M 50 5 L 94 18 L 86.5 77 L 50 94 Z" fill="#B72833"></path><path d="M 50 7 L 83 75 L 72 75 L 65 59 L 50 59 L 50 50 L 61 50 L 50 26 Z" fill="#b2b2b2"></path><path d="M 50 7 L 17 75 L 28 75 L 35 59 L 50 59 L 50 50 L 39 50 L 50 26 Z" fill="#fff"></path></svg>'
5979
+ },
5980
+ {
5981
+ id: 'toggle-arrow',
5982
+ url: 'toggle-arrow-svg',
5983
+ svg: '<svg version="1.1" x="0px" y="0px" viewBox="0 0 48 48"><path d="M24 16l-12 12 2.83 2.83 9.17-9.17 9.17 9.17 2.83-2.83z"/><path d="M0 0h48v48h-48z" fill="none"/></svg>'
5768
5984
  }
5769
5985
  ];
5770
5986
 
@@ -5903,13 +6119,7 @@ mdIconDirective.$inject = ["$mdIcon", "$mdTheming", "$mdAria"];
5903
6119
  return $http
5904
6120
  .get(url, { cache: $templateCache })
5905
6121
  .then(function(response) {
5906
- var els = angular.element(response.data);
5907
- // Iterate to find first svg node, allowing for comments in loaded SVG (common with auto-generated SVGs)
5908
- for (var i = 0; i < els.length; ++i) {
5909
- if (els[i].nodeName == 'svg') {
5910
- return els[i];
5911
- }
5912
- }
6122
+ return angular.element('<div>').append(response.data).find('svg')[0];
5913
6123
  });
5914
6124
  }
5915
6125
 
@@ -5952,11 +6162,10 @@ mdIconDirective.$inject = ["$mdIcon", "$mdTheming", "$mdAria"];
5952
6162
  if (el.tagName != 'svg') {
5953
6163
  el = angular.element('<svg xmlns="http://www.w3.org/2000/svg">').append(el)[0];
5954
6164
  }
5955
- el = angular.element(el);
5956
6165
 
5957
6166
  // Inject the namespace if not available...
5958
- if ( !el.attr('xmlns') ) {
5959
- el.attr('xmlns', "http://www.w3.org/2000/svg");
6167
+ if ( !el.getAttribute('xmlns') ) {
6168
+ el.setAttribute('xmlns', "http://www.w3.org/2000/svg");
5960
6169
  }
5961
6170
 
5962
6171
  this.element = el;
@@ -5970,27 +6179,29 @@ mdIconDirective.$inject = ["$mdIcon", "$mdTheming", "$mdAria"];
5970
6179
  */
5971
6180
  function prepareAndStyle() {
5972
6181
  var iconSize = this.config ? this.config.iconSize : config.defaultIconSize;
5973
- var svg = angular.element( this.element );
5974
- svg.attr({
6182
+ angular.forEach({
5975
6183
  'fit' : '',
5976
6184
  'height': '100%',
5977
6185
  'width' : '100%',
5978
6186
  'preserveAspectRatio': 'xMidYMid meet',
5979
- 'viewBox' : svg.attr('viewBox') || ('0 0 ' + iconSize + ' ' + iconSize)
5980
- })
5981
- .css( {
6187
+ 'viewBox' : this.element.getAttribute('viewBox') || ('0 0 ' + iconSize + ' ' + iconSize)
6188
+ }, function(val, attr) {
6189
+ this.element.setAttribute(attr, val);
6190
+ }, this);
6191
+
6192
+ angular.forEach({
5982
6193
  'pointer-events' : 'none',
5983
6194
  'display' : 'block'
5984
- });
5985
-
5986
- this.element = svg;
6195
+ }, function(val, style) {
6196
+ this.element.style[style] = val;
6197
+ }, this);
5987
6198
  }
5988
6199
 
5989
6200
  /**
5990
- * Clone the Icon DOM element; which is stored as an angular.element()
6201
+ * Clone the Icon DOM element.
5991
6202
  */
5992
6203
  function cloneSVG(){
5993
- return angular.element( this.element[0].cloneNode(true) );
6204
+ return this.element.cloneNode(true);
5994
6205
  }
5995
6206
 
5996
6207
  }
@@ -6177,9 +6388,8 @@ function inputTextareaDirective($mdUtil, $window) {
6177
6388
  setupTextarea();
6178
6389
  }
6179
6390
 
6180
- var touched = false;
6181
6391
  var isErrorGetter = containerCtrl.isErrorGetter || function() {
6182
- return ngModelCtrl.$invalid && (touched || ngModelCtrl.$touched);
6392
+ return ngModelCtrl.$invalid && ngModelCtrl.$touched;
6183
6393
  };
6184
6394
  scope.$watch(isErrorGetter, containerCtrl.setInvalid);
6185
6395
 
@@ -6191,9 +6401,7 @@ function inputTextareaDirective($mdUtil, $window) {
6191
6401
  if (!isReadonly) {
6192
6402
  element
6193
6403
  .on('focus', function(ev) {
6194
- touched = true;
6195
6404
  containerCtrl.setFocused(true);
6196
- scope.$evalAsync();
6197
6405
  })
6198
6406
  .on('blur', function(ev) {
6199
6407
  containerCtrl.setFocused(false);
@@ -6202,6 +6410,9 @@ function inputTextareaDirective($mdUtil, $window) {
6202
6410
 
6203
6411
  }
6204
6412
 
6413
+ //ngModelCtrl.$setTouched();
6414
+ //if( ngModelCtrl.$invalid ) containerCtrl.setInvalid();
6415
+
6205
6416
  scope.$on('$destroy', function() {
6206
6417
  containerCtrl.setFocused(false);
6207
6418
  containerCtrl.setHasValue(false);
@@ -6396,10 +6607,8 @@ angular.module('material.components.list', [
6396
6607
  function mdListDirective() {
6397
6608
  return {
6398
6609
  restrict: 'E',
6399
- link: function($scope, $element, $attr) {
6400
- $element.attr({
6401
- 'role' : 'list'
6402
- });
6610
+ compile: function(element) {
6611
+ element[0].setAttribute('role', 'list');
6403
6612
  }
6404
6613
  };
6405
6614
  }
@@ -6427,10 +6636,8 @@ function mdListDirective() {
6427
6636
  function mdItemDirective() {
6428
6637
  return {
6429
6638
  restrict: 'E',
6430
- link: function($scope, $element, $attr) {
6431
- $element.attr({
6432
- 'role' : 'listitem'
6433
- });
6639
+ compile: function(element) {
6640
+ element[0].setAttribute('role', 'listitem');
6434
6641
  }
6435
6642
  };
6436
6643
  }
@@ -6462,14 +6669,20 @@ angular.module('material.components.progressCircular', [
6462
6669
  * @restrict E
6463
6670
  *
6464
6671
  * @description
6465
- * The circular progress directive is used to make loading content in your app as delightful and painless as possible by minimizing the amount of visual change a user sees before they can view and interact with content.
6672
+ * The circular progress directive is used to make loading content in your app as delightful and
6673
+ * painless as possible by minimizing the amount of visual change a user sees before they can view
6674
+ * and interact with content.
6466
6675
  *
6467
- * For operations where the percentage of the operation completed can be determined, use a determinate indicator. They give users a quick sense of how long an operation will take.
6676
+ * For operations where the percentage of the operation completed can be determined, use a
6677
+ * determinate indicator. They give users a quick sense of how long an operation will take.
6468
6678
  *
6469
- * For operations where the user is asked to wait a moment while something finishes up, and it’s not necessary to expose what's happening behind the scenes and how long it will take, use an indeterminate indicator.
6679
+ * For operations where the user is asked to wait a moment while something finishes up, and it’s
6680
+ * not necessary to expose what's happening behind the scenes and how long it will take, use an
6681
+ * indeterminate indicator.
6470
6682
  *
6471
6683
  * @param {string} md-mode Select from one of two modes: determinate and indeterminate.
6472
- * @param {number=} value In determinate mode, this number represents the percentage of the circular progress. Default: 0
6684
+ * @param {number=} value In determinate mode, this number represents the percentage of the
6685
+ * circular progress. Default: 0
6473
6686
  * @param {number=} md-diameter This specifies the diamter of the circular progress. Default: 48
6474
6687
  *
6475
6688
  * @usage
@@ -6483,21 +6696,12 @@ angular.module('material.components.progressCircular', [
6483
6696
  * <md-progress-circular md-mode="indeterminate"></md-progress-circular>
6484
6697
  * </hljs>
6485
6698
  */
6486
- function MdProgressCircularDirective($$rAF, $mdConstant, $mdTheming) {
6487
- var fillRotations = new Array(101),
6488
- fixRotations = new Array(101);
6489
-
6490
- for (var i = 0; i < 101; i++) {
6491
- var percent = i / 100;
6492
- var rotation = Math.floor(percent * 180);
6493
-
6494
- fillRotations[i] = 'rotate(' + rotation.toString() + 'deg)';
6495
- fixRotations[i] = 'rotate(' + (rotation * 2).toString() + 'deg)';
6496
- }
6497
-
6699
+ function MdProgressCircularDirective($mdConstant, $mdTheming) {
6498
6700
  return {
6499
6701
  restrict: 'E',
6500
6702
  template:
6703
+ // The progress 'circle' is composed of two half-circles: the left side and the right
6704
+ // side. Each side has CSS applied to 'fill-in' the half-circle to the appropriate progress.
6501
6705
  '<div class="md-spinner-wrapper">' +
6502
6706
  '<div class="md-inner">' +
6503
6707
  '<div class="md-gap"></div>' +
@@ -6512,7 +6716,9 @@ function MdProgressCircularDirective($$rAF, $mdConstant, $mdTheming) {
6512
6716
  compile: compile
6513
6717
  };
6514
6718
 
6515
- function compile(tElement, tAttrs, transclude) {
6719
+ function compile(tElement) {
6720
+ // The javascript in this file is mainly responsible for setting the correct aria attributes.
6721
+ // The animation of the progress spinner is done entirely with just CSS.
6516
6722
  tElement.attr('aria-valuemin', 0);
6517
6723
  tElement.attr('aria-valuemax', 100);
6518
6724
  tElement.attr('role', 'progressbar');
@@ -6522,46 +6728,29 @@ function MdProgressCircularDirective($$rAF, $mdConstant, $mdTheming) {
6522
6728
 
6523
6729
  function postLink(scope, element, attr) {
6524
6730
  $mdTheming(element);
6525
- var circle = element[0],
6526
- fill = circle.querySelectorAll('.md-fill, .md-mask.md-full'),
6527
- fix = circle.querySelectorAll('.md-fill.md-fix'),
6528
- i, clamped, fillRotation, fixRotation;
6731
+ var circle = element[0];
6529
6732
 
6733
+ // Scale the progress circle based on the default diameter.
6530
6734
  var diameter = attr.mdDiameter || 48;
6531
- var scale = diameter/48;
6532
-
6533
- circle.style[$mdConstant.CSS.TRANSFORM] = 'scale(' + scale.toString() + ')';
6735
+ var scale = diameter / 48;
6736
+ circle.style[$mdConstant.CSS.TRANSFORM] = 'scale(' + scale + ')';
6534
6737
 
6535
6738
  attr.$observe('value', function(value) {
6536
- clamped = clamp(value);
6537
- fillRotation = fillRotations[clamped];
6538
- fixRotation = fixRotations[clamped];
6539
-
6540
- element.attr('aria-valuenow', clamped);
6541
-
6542
- for (i = 0; i < fill.length; i++) {
6543
- fill[i].style[$mdConstant.CSS.TRANSFORM] = fillRotation;
6544
- }
6545
-
6546
- for (i = 0; i < fix.length; i++) {
6547
- fix[i].style[$mdConstant.CSS.TRANSFORM] = fixRotation;
6548
- }
6739
+ var percentValue = clamp(value);
6740
+ element.attr('aria-valuenow', percentValue);
6549
6741
  });
6550
6742
  }
6551
6743
 
6744
+ /**
6745
+ * Clamps the value to be between 0 and 100.
6746
+ * @param {number} value The value to clamp.
6747
+ * @returns {number}
6748
+ */
6552
6749
  function clamp(value) {
6553
- if (value > 100) {
6554
- return 100;
6555
- }
6556
-
6557
- if (value < 0) {
6558
- return 0;
6559
- }
6560
-
6561
- return Math.ceil(value || 0);
6750
+ return Math.max(0, Math.min(value || 0, 100));
6562
6751
  }
6563
6752
  }
6564
- MdProgressCircularDirective.$inject = ["$$rAF", "$mdConstant", "$mdTheming"];
6753
+ MdProgressCircularDirective.$inject = ["$mdConstant", "$mdTheming"];
6565
6754
  })();
6566
6755
 
6567
6756
  /*!
@@ -6625,7 +6814,7 @@ function MdProgressLinearDirective($$rAF, $mdConstant, $mdTheming) {
6625
6814
  '</div>',
6626
6815
  compile: compile
6627
6816
  };
6628
-
6817
+
6629
6818
  function compile(tElement, tAttrs, transclude) {
6630
6819
  tElement.attr('aria-valuemin', 0);
6631
6820
  tElement.attr('aria-valuemax', 100);
@@ -7013,7 +7202,6 @@ mdRadioButtonDirective.$inject = ["$mdAria", "$mdUtil", "$mdTheming"];
7013
7202
  - [ ] ng-model="foo" ng-model-options="{ trackBy: '$value.id' }" for objects
7014
7203
  - [ ] mdOption with value
7015
7204
  - [ ] Usage with input inside
7016
- - [ ] Usage with md-multiple
7017
7205
 
7018
7206
  ### TODO - POST RC1 ###
7019
7207
  - [ ] Abstract placement logic in $mdSelect service to $mdMenu service
@@ -7083,6 +7271,12 @@ function SelectDirective($mdSelect, $mdUtil, $mdTheming, $interpolate, $compile,
7083
7271
  // If not provided, we automatically make one
7084
7272
  if (!labelEl.length) {
7085
7273
  labelEl = angular.element('<md-select-label><span></span></md-select-label>');
7274
+ } else {
7275
+ if (!labelEl[0].firstElementChild) {
7276
+ var spanWrapper = angular.element('<span>');
7277
+ spanWrapper.append(labelEl.contents());
7278
+ labelEl.append(spanWrapper);
7279
+ }
7086
7280
  }
7087
7281
  labelEl.append('<span class="md-select-icon" aria-hidden="true"></span>');
7088
7282
  labelEl.addClass('md-select-label');
@@ -7117,6 +7311,7 @@ function SelectDirective($mdSelect, $mdUtil, $mdTheming, $interpolate, $compile,
7117
7311
 
7118
7312
  return function postLink(scope, element, attr, ctrls) {
7119
7313
  var isOpen;
7314
+ var isDisabled;
7120
7315
 
7121
7316
  var mdSelectCtrl = ctrls[0];
7122
7317
  var ngModel = ctrls[1];
@@ -7126,7 +7321,6 @@ function SelectDirective($mdSelect, $mdUtil, $mdTheming, $interpolate, $compile,
7126
7321
  createSelect();
7127
7322
 
7128
7323
  var originalRender = ngModel.$render;
7129
-
7130
7324
  ngModel.$render = function() {
7131
7325
  originalRender();
7132
7326
  syncLabelText();
@@ -7153,10 +7347,40 @@ function SelectDirective($mdSelect, $mdUtil, $mdTheming, $interpolate, $compile,
7153
7347
  }
7154
7348
  }
7155
7349
 
7350
+ var deregisterWatcher;
7351
+ attr.$observe('ngMultiple', function(val) {
7352
+ if (deregisterWatcher) deregisterWatcher();
7353
+ var parser = $parse(val);
7354
+ deregisterWatcher = scope.$watch(function() { return parser(scope); }, function(multiple, prevVal) {
7355
+ if (multiple === undefined && prevVal === undefined) return; // assume compiler did a good job
7356
+ if (multiple) {
7357
+ element.attr('multiple', 'multiple');
7358
+ } else {
7359
+ element.removeAttr('multiple');
7360
+ }
7361
+ if (selectContainer) {
7362
+ var selectMenuCtrl = selectContainer.find('md-select-menu').controller('mdSelectMenu');
7363
+ selectMenuCtrl.setMultiple(multiple);
7364
+ originalRender = ngModel.$render;
7365
+ ngModel.$render = function() {
7366
+ originalRender();
7367
+ syncLabelText();
7368
+ };
7369
+ selectMenuCtrl.refreshViewValue();
7370
+ ngModel.$render();
7371
+ }
7372
+ });
7373
+ });
7374
+
7156
7375
  attr.$observe('disabled', function(disabled) {
7157
7376
  if (typeof disabled == "string") {
7158
7377
  disabled = true;
7159
7378
  }
7379
+ // Prevent click event being registered twice
7380
+ if (isDisabled !== undefined && isDisabled === disabled) {
7381
+ return;
7382
+ }
7383
+ isDisabled = disabled;
7160
7384
  if (disabled) {
7161
7385
  element.attr('tabindex', -1);
7162
7386
  element.off('click', openSelect);
@@ -7305,6 +7529,33 @@ function SelectMenuDirective($parse, $mdUtil, $mdTheming) {
7305
7529
  // and values matching every option's controller.
7306
7530
  self.options = {};
7307
7531
 
7532
+ var deregisterCollectionWatch;
7533
+ self.setMultiple = function(isMultiple) {
7534
+ var ngModel = self.ngModel;
7535
+ self.isMultiple = isMultiple;
7536
+ if (deregisterCollectionWatch) deregisterCollectionWatch();
7537
+
7538
+ if (self.isMultiple) {
7539
+ ngModel.$validators['md-multiple'] = validateArray;
7540
+ ngModel.$render = renderMultiple;
7541
+
7542
+ // watchCollection on the model because by default ngModel only watches the model's
7543
+ // reference. This allowed the developer to also push and pop from their array.
7544
+ $scope.$watchCollection($attrs.ngModel, function(value) {
7545
+ if (validateArray(value)) renderMultiple(value);
7546
+ });
7547
+ } else {
7548
+ delete ngModel.$validators['md-multiple'];
7549
+ ngModel.$render = renderSingular;
7550
+ }
7551
+
7552
+ function validateArray(modelValue, viewValue) {
7553
+ // If a value is truthy but not an array, reject it.
7554
+ // If value is undefined/falsy, accept that it's an empty array.
7555
+ return angular.isArray(modelValue || viewValue || []);
7556
+ }
7557
+ };
7558
+
7308
7559
 
7309
7560
  self.init = function(ngModel) {
7310
7561
  self.ngModel = ngModel;
@@ -7328,25 +7579,7 @@ function SelectMenuDirective($parse, $mdUtil, $mdTheming) {
7328
7579
  return value;
7329
7580
  };
7330
7581
  }
7331
-
7332
- if (self.isMultiple) {
7333
- ngModel.$validators['md-multiple'] = validateArray;
7334
- ngModel.$render = renderMultiple;
7335
-
7336
- // watchCollection on the model because by default ngModel only watches the model's
7337
- // reference. This allowed the developer to also push and pop from their array.
7338
- $scope.$watchCollection($attrs.ngModel, function(value) {
7339
- if (validateArray(value)) renderMultiple(value);
7340
- });
7341
- } else {
7342
- ngModel.$render = renderSingular;
7343
- }
7344
-
7345
- function validateArray(modelValue, viewValue) {
7346
- // If a value is truthy but not an array, reject it.
7347
- // If value is undefined/falsy, accept that it's an empty array.
7348
- return angular.isArray(modelValue || viewValue || []);
7349
- }
7582
+ self.setMultiple(self.isMultiple);
7350
7583
  };
7351
7584
 
7352
7585
  self.selectedLabels = function() {
@@ -7568,7 +7801,7 @@ function SelectProvider($$interimElementProvider) {
7568
7801
  parent: angular.element(opts.parent),
7569
7802
  selectEl: element.find('md-select-menu'),
7570
7803
  contentEl: element.find('md-content'),
7571
- backdrop: opts.hasBackdrop && angular.element('<md-backdrop class="md-select-backdrop">')
7804
+ backdrop: opts.hasBackdrop && angular.element('<md-backdrop class="md-select-backdrop md-click-catcher">')
7572
7805
  });
7573
7806
 
7574
7807
  configureAria();
@@ -7687,19 +7920,20 @@ function SelectProvider($$interimElementProvider) {
7687
7920
  focusOption('prev');
7688
7921
  }
7689
7922
 
7690
- if (!selectCtrl.isMultiple) {
7691
- opts.selectEl.on('click', closeMenu);
7692
- opts.selectEl.on('keydown', function(e) {
7693
- if (e.keyCode == 32 || e.keyCode == 13) {
7694
- closeMenu();
7695
- }
7696
- });
7697
- }
7698
- function closeMenu() {
7699
- opts.restoreFocus = true;
7700
- scope.$evalAsync(function() {
7701
- $mdSelect.hide(selectCtrl.ngModel.$viewValue);
7702
- });
7923
+ opts.selectEl.on('click', checkCloseMenu);
7924
+ opts.selectEl.on('keydown', function(e) {
7925
+ if (e.keyCode == 32 || e.keyCode == 13) {
7926
+ checkCloseMenu();
7927
+ }
7928
+ });
7929
+
7930
+ function checkCloseMenu() {
7931
+ if (!selectCtrl.isMultiple) {
7932
+ opts.restoreFocus = true;
7933
+ scope.$evalAsync(function() {
7934
+ $mdSelect.hide(selectCtrl.ngModel.$viewValue);
7935
+ });
7936
+ }
7703
7937
  }
7704
7938
  }
7705
7939
 
@@ -7737,12 +7971,12 @@ function SelectProvider($$interimElementProvider) {
7737
7971
  selectNode = opts.selectEl[0],
7738
7972
  contentNode = opts.contentEl[0],
7739
7973
  parentRect = parentNode.getBoundingClientRect(),
7740
- targetRect = $mdUtil.clientRect(targetNode, parentNode),
7974
+ targetRect = targetNode.getBoundingClientRect(),
7741
7975
  shouldOpenAroundTarget = false,
7742
7976
  bounds = {
7743
- left: parentNode.scrollLeft + SELECT_EDGE_MARGIN,
7744
- top: parentNode.scrollTop + SELECT_EDGE_MARGIN,
7745
- bottom: parentRect.height + parentNode.scrollTop - SELECT_EDGE_MARGIN,
7977
+ left: parentRect.left + SELECT_EDGE_MARGIN,
7978
+ top: SELECT_EDGE_MARGIN,
7979
+ bottom: parentRect.height - SELECT_EDGE_MARGIN,
7746
7980
  right: parentRect.width - SELECT_EDGE_MARGIN
7747
7981
  },
7748
7982
  spaceAvailable = {
@@ -7799,10 +8033,6 @@ function SelectProvider($$interimElementProvider) {
7799
8033
  if ((focusedNode.tagName || '').toUpperCase() === 'MD-OPTGROUP') {
7800
8034
  focusedNode = optionNodes[0] || contentNode.firstElementChild || contentNode;
7801
8035
  }
7802
- if (focusedNode) {
7803
- opts.focusedNode = focusedNode;
7804
- focusedNode.focus();
7805
- }
7806
8036
 
7807
8037
  if (isScrollable) {
7808
8038
  var scrollBuffer = contentNode.offsetHeight / 2;
@@ -7853,9 +8083,14 @@ function SelectProvider($$interimElementProvider) {
7853
8083
  Math.min(targetRect.height / selectMenuRect.height, 1.0) +
7854
8084
  ')';
7855
8085
 
8086
+
7856
8087
  $$rAF(function() {
7857
8088
  element.addClass('md-active');
7858
8089
  selectNode.style[$mdConstant.CSS.TRANSFORM] = '';
8090
+ if (focusedNode) {
8091
+ opts.focusedNode = focusedNode;
8092
+ focusedNode.focus();
8093
+ }
7859
8094
  });
7860
8095
  }
7861
8096
 
@@ -8051,6 +8286,7 @@ function SidenavDirective($timeout, $animate, $parse, $log, $mdMedia, $mdConstan
8051
8286
  * Directive Post Link function...
8052
8287
  */
8053
8288
  function postLink(scope, element, attr, sidenavCtrl) {
8289
+ var lastParentOverFlow;
8054
8290
  var triggeringElement = null;
8055
8291
  var promise = $q.when(true);
8056
8292
 
@@ -8104,6 +8340,8 @@ function SidenavDirective($timeout, $animate, $parse, $log, $mdMedia, $mdConstan
8104
8340
  triggeringElement = $document[0].activeElement;
8105
8341
  }
8106
8342
 
8343
+ disableParentScroll(isOpen);
8344
+
8107
8345
  return promise = $q.all([
8108
8346
  $animate[isOpen ? 'enter' : 'leave'](backdrop, parent),
8109
8347
  $animate[isOpen ? 'removeClass' : 'addClass'](element, 'md-closed').then(function() {
@@ -8115,6 +8353,20 @@ function SidenavDirective($timeout, $animate, $parse, $log, $mdMedia, $mdConstan
8115
8353
  ]);
8116
8354
  }
8117
8355
 
8356
+ /**
8357
+ * Prevent parent scrolling (when the SideNav is open)
8358
+ */
8359
+ function disableParentScroll(disabled) {
8360
+ var parent = element.parent();
8361
+ if ( disabled ) {
8362
+ lastParentOverFlow = parent.css('overflow');
8363
+ parent.css('overflow', 'hidden');
8364
+ } else if (angular.isDefined(lastParentOverFlow)) {
8365
+ parent.css('overflow', lastParentOverFlow);
8366
+ lastParentOverFlow = undefined;
8367
+ }
8368
+ }
8369
+
8118
8370
  /**
8119
8371
  * Toggle the sideNav view and publish a promise to be resolved when
8120
8372
  * the view animation finishes.
@@ -8739,7 +8991,7 @@ function MdSticky($document, $mdConstant, $compile, $$rAF, $mdUtil) {
8739
8991
  return a.top < b.top ? -1 : 1;
8740
8992
  });
8741
8993
 
8742
- // Find which item in the list should be active,
8994
+ // Find which item in the list should be active,
8743
8995
  // based upon the content's current scroll position
8744
8996
  var item;
8745
8997
  var currentScrollTop = contentEl.prop('scrollTop');
@@ -8760,7 +9012,7 @@ function MdSticky($document, $mdConstant, $compile, $$rAF, $mdUtil) {
8760
9012
  // Find the `top` of an item relative to the content element,
8761
9013
  // and also the height.
8762
9014
  function refreshPosition(item) {
8763
- // Find the top of an item by adding to the offsetHeight until we reach the
9015
+ // Find the top of an item by adding to the offsetHeight until we reach the
8764
9016
  // content element.
8765
9017
  var current = item.element[0];
8766
9018
  item.top = 0;
@@ -8798,7 +9050,7 @@ function MdSticky($document, $mdConstant, $compile, $$rAF, $mdUtil) {
8798
9050
  translate(self.current, null);
8799
9051
  }
8800
9052
  }
8801
-
9053
+
8802
9054
  // Scrolling up with a current sticky item?
8803
9055
  } else if (!isScrollingDown && self.current) {
8804
9056
  if (scrollTop < self.current.top) {
@@ -8818,7 +9070,7 @@ function MdSticky($document, $mdConstant, $compile, $$rAF, $mdUtil) {
8818
9070
  }
8819
9071
  }
8820
9072
  }
8821
-
9073
+
8822
9074
  function setCurrentItem(item) {
8823
9075
  if (self.current === item) return;
8824
9076
  // Deactivate currently active item
@@ -8862,7 +9114,7 @@ function MdSticky($document, $mdConstant, $compile, $$rAF, $mdUtil) {
8862
9114
  } else {
8863
9115
  item.translateY = amount;
8864
9116
  item.clone.css(
8865
- $mdConstant.CSS.TRANSFORM,
9117
+ $mdConstant.CSS.TRANSFORM,
8866
9118
  'translate3d(' + item.left + 'px,' + amount + 'px,0)'
8867
9119
  );
8868
9120
  }
@@ -8889,7 +9141,7 @@ function MdSticky($document, $mdConstant, $compile, $$rAF, $mdUtil) {
8889
9141
 
8890
9142
  // Android 4.4 don't accurately give scroll events.
8891
9143
  // To fix this problem, we setup a fake scroll event. We say:
8892
- // > If a scroll or touchmove event has happened in the last DELAY milliseconds,
9144
+ // > If a scroll or touchmove event has happened in the last DELAY milliseconds,
8893
9145
  // then send a `$scroll` event every animationFrame.
8894
9146
  // Additionally, we add $scrollstart and $scrollend events.
8895
9147
  function setupAugmentedScrollEvents(element) {
@@ -8975,7 +9227,7 @@ function MdSubheaderDirective($mdSticky, $compile, $mdTheming) {
8975
9227
  restrict: 'E',
8976
9228
  replace: true,
8977
9229
  transclude: true,
8978
- template:
9230
+ template:
8979
9231
  '<h2 class="md-subheader">' +
8980
9232
  '<span class="md-subheader-content"></span>' +
8981
9233
  '</h2>',
@@ -9328,7 +9580,7 @@ function mdTextFloatDirective($mdTheming, $mdUtil, $parse, $log) {
9328
9580
  },
9329
9581
  compile : function(element, attr) {
9330
9582
 
9331
- $log.warn('<md-text-float> is deprecated. Please use `<md-input-container>` and `<input>`.' +
9583
+ $log.warn('<md-text-float> is deprecated. Please use `<md-input-container>` and `<input>`.' +
9332
9584
  'More information at http://material.angularjs.org/#/api/material.components.input/directive/mdInputContainer');
9333
9585
 
9334
9586
  if ( angular.isUndefined(attr.mdFid) ) {
@@ -9361,7 +9613,7 @@ function mdInputGroupDirective($log) {
9361
9613
  restrict: 'CE',
9362
9614
  controller: ['$element', function($element) {
9363
9615
 
9364
- $log.warn('<md-input-group> is deprecated. Please use `<md-input-container>` and `<input>`.' +
9616
+ $log.warn('<md-input-group> is deprecated. Please use `<md-input-container>` and `<input>`.' +
9365
9617
  'More information at http://material.angularjs.org/#/api/material.components.input/directive/mdInputContainer');
9366
9618
  this.setFocused = function(isFocused) {
9367
9619
  $element.toggleClass('md-input-focused', !!isFocused);
@@ -9384,7 +9636,7 @@ function mdInputDirective($mdUtil, $log) {
9384
9636
  link: function(scope, element, attr, ctrls) {
9385
9637
  if ( !ctrls[0] ) return;
9386
9638
 
9387
- $log.warn('<md-input> is deprecated. Please use `<md-input-container>` and `<input>`.' +
9639
+ $log.warn('<md-input> is deprecated. Please use `<md-input-container>` and `<input>`.' +
9388
9640
  'More information at http://material.angularjs.org/#/api/material.components.input/directive/mdInputContainer');
9389
9641
 
9390
9642
  var inputGroupCtrl = ctrls[0];
@@ -9504,7 +9756,7 @@ function MdToastDirective() {
9504
9756
  /**
9505
9757
  * @ngdoc method
9506
9758
  * @name $mdToast#showSimple
9507
- *
9759
+ *
9508
9760
  * @description
9509
9761
  * Convenience method which builds and shows a simple toast.
9510
9762
  *
@@ -9532,7 +9784,7 @@ function MdToastDirective() {
9532
9784
  /**
9533
9785
  * @ngdoc method
9534
9786
  * @name $mdToast#updateContent
9535
- *
9787
+ *
9536
9788
  * @description
9537
9789
  * Updates the content of an existing toast. Useful for updating things like counts, etc.
9538
9790
  *
@@ -9619,12 +9871,12 @@ function MdToastProvider($$interimElementProvider) {
9619
9871
  var activeToastContent;
9620
9872
  var $mdToast = $$interimElementProvider('$mdToast')
9621
9873
  .setDefaults({
9622
- methods: ['position', 'hideDelay', 'capsule'],
9874
+ methods: ['position', 'hideDelay', 'capsule' ],
9623
9875
  options: toastDefaultOptions
9624
9876
  })
9625
9877
  .addPreset('simple', {
9626
9878
  argOption: 'content',
9627
- methods: ['content', 'action', 'highlightAction', 'theme'],
9879
+ methods: ['content', 'action', 'highlightAction', 'theme', 'parent'],
9628
9880
  options: /* @ngInject */ ["$mdToast", "$mdTheming", function($mdToast, $mdTheming) {
9629
9881
  var opts = {
9630
9882
  template: [
@@ -9655,11 +9907,11 @@ function MdToastProvider($$interimElementProvider) {
9655
9907
  activeToastContent = newContent;
9656
9908
  });
9657
9909
 
9658
- toastDefaultOptions.$inject = ["$timeout", "$animate", "$mdToast"];
9910
+ toastDefaultOptions.$inject = ["$timeout", "$animate", "$mdToast", "$mdUtil"];
9659
9911
  return $mdToast;
9660
9912
 
9661
9913
  /* @ngInject */
9662
- function toastDefaultOptions($timeout, $animate, $mdToast) {
9914
+ function toastDefaultOptions($timeout, $animate, $mdToast, $mdUtil) {
9663
9915
  return {
9664
9916
  onShow: onShow,
9665
9917
  onRemove: onRemove,
@@ -9669,6 +9921,8 @@ function MdToastProvider($$interimElementProvider) {
9669
9921
  };
9670
9922
 
9671
9923
  function onShow(scope, element, options) {
9924
+ element = $mdUtil.extractElementByName(element, 'md-toast');
9925
+
9672
9926
  // 'top left' -> 'md-top md-left'
9673
9927
  activeToastContent = options.content;
9674
9928
  element.addClass(options.position.split(' ').map(function(pos) {
@@ -9874,10 +10128,9 @@ mdToolbarDirective.$inject = ["$$rAF", "$mdConstant", "$mdUtil", "$mdTheming"];
9874
10128
  * @ngdoc module
9875
10129
  * @name material.components.tooltip
9876
10130
  */
9877
- angular.module('material.components.tooltip', [
9878
- 'material.core'
9879
- ])
9880
- .directive('mdTooltip', MdTooltipDirective);
10131
+ angular
10132
+ .module('material.components.tooltip', [ 'material.core' ])
10133
+ .directive('mdTooltip', MdTooltipDirective);
9881
10134
 
9882
10135
  /**
9883
10136
  * @ngdoc directive
@@ -9903,78 +10156,95 @@ angular.module('material.components.tooltip', [
9903
10156
  * currently visible.
9904
10157
  * @param {number=} md-delay How many milliseconds to wait to show the tooltip after the user focuses, hovers, or touches the parent. Defaults to 400ms.
9905
10158
  * @param {string=} md-direction Which direction would you like the tooltip to go? Supports left, right, top, and bottom. Defaults to bottom.
10159
+ * @param {boolean=} md-autohide If present or provided with a boolean value, the tooltip will hide on mouse leave, regardless of focus
9906
10160
  */
9907
10161
  function MdTooltipDirective($timeout, $window, $$rAF, $document, $mdUtil, $mdTheming, $rootElement, $animate, $q) {
9908
10162
 
9909
- var TOOLTIP_SHOW_DELAY = 0;
10163
+ var TOOLTIP_SHOW_DELAY = 300;
9910
10164
  var TOOLTIP_WINDOW_EDGE_SPACE = 8;
9911
10165
 
9912
10166
  return {
9913
10167
  restrict: 'E',
9914
10168
  transclude: true,
9915
- template:
9916
- '<div class="md-background"></div>' +
9917
- '<div class="md-content" ng-transclude></div>',
10169
+ template: '\
10170
+ <div class="md-background"></div>\
10171
+ <div class="md-content" ng-transclude></div>',
9918
10172
  scope: {
9919
10173
  visible: '=?mdVisible',
9920
- delay: '=?mdDelay'
10174
+ delay: '=?mdDelay',
10175
+ autohide: '=?mdAutohide'
9921
10176
  },
9922
10177
  link: postLink
9923
10178
  };
9924
10179
 
9925
- function postLink(scope, element, attr, contentCtrl) {
10180
+ function postLink(scope, element, attr) {
10181
+
9926
10182
  $mdTheming(element);
9927
- var parent = element.parent();
9928
- var background = angular.element(element[0].getElementsByClassName('md-background')[0]);
9929
- var content = angular.element(element[0].getElementsByClassName('md-content')[0]);
9930
- var direction = attr.mdDirection;
9931
10183
 
9932
- // Keep looking for a higher parent if our current one has no pointer events
9933
- while ($window.getComputedStyle(parent[0])['pointer-events'] == 'none') {
9934
- parent = parent.parent();
9935
- }
10184
+ var parent = getParentWithPointerEvents(),
10185
+ background = angular.element(element[0].getElementsByClassName('md-background')[0]),
10186
+ content = angular.element(element[0].getElementsByClassName('md-content')[0]),
10187
+ direction = attr.mdDirection,
10188
+ current = getNearestContentElement(),
10189
+ tooltipParent = angular.element(current || document.body),
10190
+ debouncedOnResize = $$rAF.throttle(function () { if (scope.visible) positionTooltip(); });
9936
10191
 
9937
- // Look for the nearest parent md-content, stopping at the rootElement.
9938
- var current = element.parent()[0];
9939
- while (current && current !== $rootElement[0] && current !== document.body) {
9940
- if (current.tagName && current.tagName.toLowerCase() == 'md-content') break;
9941
- current = current.parentNode;
9942
- }
9943
- var tooltipParent = angular.element(current || document.body);
10192
+ return init();
9944
10193
 
9945
- if (!angular.isDefined(attr.mdDelay)) {
9946
- scope.delay = TOOLTIP_SHOW_DELAY;
10194
+ function init () {
10195
+ setDefaults();
10196
+ manipulateElement();
10197
+ bindEvents();
10198
+ configureWatchers();
9947
10199
  }
9948
10200
 
9949
- // We will re-attach tooltip when visible
9950
- element.detach();
9951
- element.attr('role', 'tooltip');
9952
- element.attr('id', attr.id || ('tooltip_' + $mdUtil.nextUid()));
10201
+ function setDefaults () {
10202
+ if (!angular.isDefined(attr.mdDelay)) scope.delay = TOOLTIP_SHOW_DELAY;
10203
+ }
9953
10204
 
9954
- parent.on('focus mouseenter touchstart', function() { setVisible(true); });
9955
- parent.on('blur mouseleave touchend touchcancel', function() { if ($document[0].activeElement !== parent[0]) setVisible(false); });
10205
+ function configureWatchers () {
10206
+ scope.$watch('visible', function (isVisible) {
10207
+ if (isVisible) showTooltip();
10208
+ else hideTooltip();
10209
+ });
10210
+ scope.$on('$destroy', function() {
10211
+ scope.visible = false;
10212
+ element.remove();
10213
+ angular.element($window).off('resize', debouncedOnResize);
10214
+ });
10215
+ }
9956
10216
 
9957
- scope.$watch('visible', function(isVisible) {
9958
- if (isVisible) showTooltip();
9959
- else hideTooltip();
9960
- });
10217
+ function manipulateElement () {
10218
+ element.detach();
10219
+ element.attr('role', 'tooltip');
10220
+ element.attr('id', attr.id || ('tooltip_' + $mdUtil.nextUid()));
10221
+ }
9961
10222
 
9962
- var debouncedOnResize = $$rAF.throttle(function () { if (scope.visible) positionTooltip(); });
9963
- angular.element($window).on('resize', debouncedOnResize);
10223
+ function getParentWithPointerEvents () {
10224
+ var parent = element.parent();
10225
+ while ($window.getComputedStyle(parent[0])['pointer-events'] == 'none') {
10226
+ parent = parent.parent();
10227
+ }
10228
+ return parent;
10229
+ }
9964
10230
 
9965
- // Be sure to completely cleanup the element on destroy
9966
- scope.$on('$destroy', function() {
9967
- scope.visible = false;
9968
- element.remove();
9969
- angular.element($window).off('resize', debouncedOnResize);
9970
- });
10231
+ function getNearestContentElement () {
10232
+ var current = element.parent()[0];
10233
+ // Look for the nearest parent md-content, stopping at the rootElement.
10234
+ while (current && current !== $rootElement[0] && current !== document.body) {
10235
+ if (current.tagName && current.tagName.toLowerCase() == 'md-content') break;
10236
+ current = current.parentNode;
10237
+ }
10238
+ return current;
10239
+ }
9971
10240
 
9972
- // *******
9973
- // Methods
9974
- // *******
10241
+ function bindEvents () {
10242
+ var autohide = scope.hasOwnProperty('autohide') ? scope.autohide : attr.hasOwnProperty('mdAutohide');
10243
+ parent.on('focus mouseenter touchstart', function() { setVisible(true); });
10244
+ parent.on('blur mouseleave touchend touchcancel', function() { if ($document[0].activeElement !== parent[0] || autohide) setVisible(false); });
10245
+ angular.element($window).on('resize', debouncedOnResize);
10246
+ }
9975
10247
 
9976
- // If setting visible to true, debounce to scope.delay ms
9977
- // If setting visible to false and no timeout is active, instantly hide the tooltip.
9978
10248
  function setVisible (value) {
9979
10249
  setVisible.value = !!value;
9980
10250
  if (!setVisible.queued) {
@@ -9984,7 +10254,6 @@ function MdTooltipDirective($timeout, $window, $$rAF, $document, $mdUtil, $mdThe
9984
10254
  scope.visible = setVisible.value;
9985
10255
  setVisible.queued = false;
9986
10256
  }, scope.delay);
9987
-
9988
10257
  } else {
9989
10258
  $timeout(function() { scope.visible = false; });
9990
10259
  }
@@ -9996,12 +10265,10 @@ function MdTooltipDirective($timeout, $window, $$rAF, $document, $mdUtil, $mdThe
9996
10265
  parent.attr('aria-describedby', element.attr('id'));
9997
10266
  tooltipParent.append(element);
9998
10267
 
9999
- // Wait until the element has been in the dom for two frames before fading it in.
10000
- // Additionally, we position the tooltip twice to avoid positioning bugs
10001
10268
  positionTooltip();
10002
- $animate.addClass(element, 'md-show');
10003
- $animate.addClass(background, 'md-show');
10004
- $animate.addClass(content, 'md-show');
10269
+ angular.forEach([element, background, content], function (element) {
10270
+ $animate.addClass(element, 'md-show');
10271
+ });
10005
10272
  }
10006
10273
 
10007
10274
  function hideTooltip() {
@@ -10024,7 +10291,7 @@ function MdTooltipDirective($timeout, $window, $$rAF, $document, $mdUtil, $mdThe
10024
10291
  // Otherwise, recalculate based on 'top' since default is 'bottom'
10025
10292
  if (direction) {
10026
10293
  newPosition = fitInParent(newPosition);
10027
- } else if (newPosition.top > tooltipParent.prop('scrollHeight') - tipRect.height - TOOLTIP_WINDOW_EDGE_SPACE) {
10294
+ } else if (newPosition.top > element.prop('offsetParent').scrollHeight - tipRect.height - TOOLTIP_WINDOW_EDGE_SPACE) {
10028
10295
  newPosition = fitInParent(getPosition('top'));
10029
10296
  }
10030
10297
 
@@ -10106,64 +10373,56 @@ angular.module('material.components.whiteframe', []);
10106
10373
  .module('material.components.autocomplete')
10107
10374
  .controller('MdAutocompleteCtrl', MdAutocompleteCtrl);
10108
10375
 
10109
- function MdAutocompleteCtrl ($scope, $element, $q, $mdUtil, $mdConstant) {
10376
+ function MdAutocompleteCtrl ($scope, $element, $mdUtil, $mdConstant, $timeout) {
10110
10377
 
10111
10378
  //-- private variables
10379
+
10112
10380
  var self = this,
10113
10381
  itemParts = $scope.itemsExpr.split(/ in /i),
10114
10382
  itemExpr = itemParts[1],
10115
- elements = {
10116
- main: $element[0],
10117
- ul: $element[0].getElementsByTagName('ul')[0],
10118
- input: $element[0].getElementsByTagName('input')[0]
10119
- },
10383
+ elements = null,
10120
10384
  promise = null,
10121
10385
  cache = {},
10122
10386
  noBlur = false;
10123
10387
 
10124
10388
  //-- public variables
10389
+
10125
10390
  self.scope = $scope;
10126
10391
  self.parent = $scope.$parent;
10127
10392
  self.itemName = itemParts[0];
10128
10393
  self.matches = [];
10129
10394
  self.loading = false;
10130
10395
  self.hidden = true;
10131
- self.index = 0;
10132
- self.keydown = keydown;
10133
- self.blur = blur;
10396
+ self.index = null;
10397
+ self.messages = [];
10398
+ self.id = $mdUtil.nextUid();
10399
+
10400
+ //-- public methods
10401
+
10402
+ self.keydown = keydown;
10403
+ self.blur = blur;
10134
10404
  self.clear = clearValue;
10135
10405
  self.select = select;
10136
10406
  self.getCurrentDisplayValue = getCurrentDisplayValue;
10137
10407
  self.fetch = $mdUtil.debounce(fetchResults);
10138
- self.messages = [];
10139
-
10140
- //-- While the mouse is inside of the dropdown, we don't want to handle input blur
10141
- //-- This is to allow the user to scroll the list without causing it to hide
10142
10408
  self.listEnter = function () { noBlur = true; };
10143
10409
  self.listLeave = function () { noBlur = false; };
10144
10410
  self.mouseUp = function () { elements.input.focus(); };
10145
10411
 
10146
10412
  return init();
10147
10413
 
10148
- //-- start method definitions
10414
+ //-- initialization methods
10415
+
10149
10416
  function init () {
10150
10417
  configureWatchers();
10151
- configureAria();
10152
- }
10153
-
10154
- function configureAria () {
10155
- var ul = angular.element(elements.ul),
10156
- input = angular.element(elements.input),
10157
- id = ul.attr('id') || 'ul_' + $mdUtil.nextUid();
10158
- ul.attr('id', id);
10159
- input.attr('aria-owns', id);
10418
+ $timeout(function () {
10419
+ gatherElements();
10420
+ focusElement();
10421
+ });
10160
10422
  }
10161
10423
 
10162
- function getItemScope (item) {
10163
- if (!item) return;
10164
- var locals = {};
10165
- if (self.itemName) locals[self.itemName] = item;
10166
- return locals;
10424
+ function focusElement () {
10425
+ if ($scope.autofocus) elements.input.focus();
10167
10426
  }
10168
10427
 
10169
10428
  function configureWatchers () {
@@ -10172,13 +10431,32 @@ angular.module('material.components.whiteframe', []);
10172
10431
  ? $mdUtil.debounce(handleSearchText, wait)
10173
10432
  : handleSearchText);
10174
10433
  $scope.$watch('selectedItem', function (selectedItem, previousSelectedItem) {
10434
+ if (selectedItem) {
10435
+ $scope.searchText = getDisplayValue(selectedItem);
10436
+ }
10175
10437
  if ($scope.itemChange && selectedItem !== previousSelectedItem)
10176
10438
  $scope.itemChange(getItemScope(selectedItem));
10177
10439
  });
10178
10440
  }
10179
10441
 
10442
+ function gatherElements () {
10443
+ elements = {
10444
+ main: $element[0],
10445
+ ul: $element[0].getElementsByTagName('ul')[0],
10446
+ input: $element[0].getElementsByTagName('input')[0]
10447
+ };
10448
+ }
10449
+
10450
+ //-- event/change handlers
10451
+
10180
10452
  function handleSearchText (searchText, previousSearchText) {
10181
- self.index = -1;
10453
+ self.index = getDefaultIndex();
10454
+ //-- do nothing on init if there is no initial value
10455
+ if (!searchText && searchText === previousSearchText) return;
10456
+ //-- clear selected item if search text no longer matches it
10457
+ if (searchText !== getDisplayValue($scope.selectedItem)) $scope.selectedItem = null;
10458
+ else return;
10459
+ //-- cancel results if search text is not long enough
10182
10460
  if (!searchText || searchText.length < Math.max(parseInt($scope.minLength, 10), 1)) {
10183
10461
  self.loading = false;
10184
10462
  self.matches = [];
@@ -10187,21 +10465,102 @@ angular.module('material.components.whiteframe', []);
10187
10465
  return;
10188
10466
  }
10189
10467
  var term = searchText.toLowerCase();
10468
+ //-- cancel promise if a promise is in progress
10190
10469
  if (promise && promise.cancel) {
10191
10470
  promise.cancel();
10192
10471
  promise = null;
10193
10472
  }
10473
+ //-- if results are cached, pull in cached results
10194
10474
  if (!$scope.noCache && cache[term]) {
10195
10475
  self.matches = cache[term];
10196
10476
  updateMessages();
10197
10477
  } else {
10198
- self.fetch(searchText);
10478
+ fetchResults(searchText);
10199
10479
  }
10200
10480
  self.hidden = shouldHide();
10201
10481
  if ($scope.textChange && searchText !== previousSearchText)
10202
10482
  $scope.textChange(getItemScope($scope.selectedItem));
10203
10483
  }
10204
10484
 
10485
+ function blur () {
10486
+ if (!noBlur) self.hidden = true;
10487
+ }
10488
+
10489
+ function keydown (event) {
10490
+ switch (event.keyCode) {
10491
+ case $mdConstant.KEY_CODE.DOWN_ARROW:
10492
+ if (self.loading) return;
10493
+ event.preventDefault();
10494
+ self.index = Math.min(self.index + 1, self.matches.length - 1);
10495
+ updateScroll();
10496
+ updateSelectionMessage();
10497
+ break;
10498
+ case $mdConstant.KEY_CODE.UP_ARROW:
10499
+ if (self.loading) return;
10500
+ event.preventDefault();
10501
+ self.index = Math.max(0, self.index - 1);
10502
+ updateScroll();
10503
+ updateSelectionMessage();
10504
+ break;
10505
+ case $mdConstant.KEY_CODE.ENTER:
10506
+ if (self.loading || self.index < 0) return;
10507
+ event.preventDefault();
10508
+ select(self.index);
10509
+ break;
10510
+ case $mdConstant.KEY_CODE.ESCAPE:
10511
+ self.matches = [];
10512
+ self.hidden = true;
10513
+ self.index = getDefaultIndex();
10514
+ break;
10515
+ case $mdConstant.KEY_CODE.TAB:
10516
+ break;
10517
+ default:
10518
+ }
10519
+ }
10520
+
10521
+ //-- getters
10522
+
10523
+ function getDisplayValue (item) {
10524
+ return (item && $scope.itemText) ? $scope.itemText(getItemScope(item)) : item;
10525
+ }
10526
+
10527
+ function getItemScope (item) {
10528
+ if (!item) return;
10529
+ var locals = {};
10530
+ if (self.itemName) locals[self.itemName] = item;
10531
+ return locals;
10532
+ }
10533
+
10534
+ function getDefaultIndex () {
10535
+ return $scope.autoselect ? 0 : -1;
10536
+ }
10537
+
10538
+ function shouldHide () {
10539
+ return self.matches.length === 1
10540
+ && $scope.searchText === getDisplayValue(self.matches[0])
10541
+ && $scope.selectedItem === self.matches[0];
10542
+ }
10543
+
10544
+ function getCurrentDisplayValue () {
10545
+ return getDisplayValue(self.matches[self.index]);
10546
+ }
10547
+
10548
+ //-- actions
10549
+
10550
+ function select (index) {
10551
+ $scope.selectedItem = self.matches[index];
10552
+ $scope.searchText = getDisplayValue($scope.selectedItem) || $scope.searchText;
10553
+ self.hidden = true;
10554
+ self.index = 0;
10555
+ self.matches = [];
10556
+ }
10557
+
10558
+ function clearValue () {
10559
+ $scope.searchText = '';
10560
+ select(-1);
10561
+ elements.input.focus();
10562
+ }
10563
+
10205
10564
  function fetchResults (searchText) {
10206
10565
  var items = $scope.$parent.$eval(itemExpr),
10207
10566
  term = searchText.toLowerCase();
@@ -10209,7 +10568,9 @@ angular.module('material.components.whiteframe', []);
10209
10568
  handleResults(items);
10210
10569
  } else {
10211
10570
  self.loading = true;
10212
- promise = $q.when(items).then(handleResults);
10571
+ if (items.success) items.success(handleResults);
10572
+ if (items.then) items.then(handleResults);
10573
+ if (items.error) items.error(function () { self.loading = false; });
10213
10574
  }
10214
10575
  function handleResults (matches) {
10215
10576
  cache[term] = matches;
@@ -10237,68 +10598,6 @@ angular.module('material.components.whiteframe', []);
10237
10598
  self.messages.push({ display: getCurrentDisplayValue() });
10238
10599
  }
10239
10600
 
10240
- function blur () {
10241
- if (!noBlur) self.hidden = true;
10242
- }
10243
-
10244
- function keydown (event) {
10245
- switch (event.keyCode) {
10246
- case $mdConstant.KEY_CODE.DOWN_ARROW:
10247
- if (self.loading) return;
10248
- event.preventDefault();
10249
- self.index = Math.min(self.index + 1, self.matches.length - 1);
10250
- updateScroll();
10251
- updateSelectionMessage();
10252
- break;
10253
- case $mdConstant.KEY_CODE.UP_ARROW:
10254
- if (self.loading) return;
10255
- event.preventDefault();
10256
- self.index = Math.max(0, self.index - 1);
10257
- updateScroll();
10258
- updateSelectionMessage();
10259
- break;
10260
- case $mdConstant.KEY_CODE.ENTER:
10261
- if (self.loading || self.index < 0) return;
10262
- event.preventDefault();
10263
- select(self.index);
10264
- break;
10265
- case $mdConstant.KEY_CODE.ESCAPE:
10266
- self.matches = [];
10267
- self.hidden = true;
10268
- self.index = -1;
10269
- break;
10270
- case $mdConstant.KEY_CODE.TAB:
10271
- break;
10272
- default:
10273
- }
10274
- }
10275
-
10276
- function clearValue () {
10277
- $scope.searchText = '';
10278
- select(-1);
10279
- elements.input.focus();
10280
- }
10281
-
10282
- function shouldHide () {
10283
- return self.matches.length === 1 && $scope.searchText === getDisplayValue(self.matches[0]);
10284
- }
10285
-
10286
- function getCurrentDisplayValue () {
10287
- return getDisplayValue(self.matches[self.index]);
10288
- }
10289
-
10290
- function getDisplayValue (item) {
10291
- return (item && $scope.itemText) ? $scope.itemText(getItemScope(item)) : item;
10292
- }
10293
-
10294
- function select (index) {
10295
- $scope.selectedItem = self.matches[index];
10296
- $scope.searchText = getDisplayValue($scope.selectedItem) || $scope.searchText;
10297
- self.hidden = true;
10298
- self.index = -1;
10299
- self.matches = [];
10300
- }
10301
-
10302
10601
  function updateScroll () {
10303
10602
  var top = 41 * self.index,
10304
10603
  bot = top + 41,
@@ -10311,7 +10610,7 @@ angular.module('material.components.whiteframe', []);
10311
10610
  }
10312
10611
 
10313
10612
  }
10314
- MdAutocompleteCtrl.$inject = ["$scope", "$element", "$q", "$mdUtil", "$mdConstant"];
10613
+ MdAutocompleteCtrl.$inject = ["$scope", "$element", "$mdUtil", "$mdConstant", "$timeout"];
10315
10614
  })();
10316
10615
 
10317
10616
  /*!
@@ -10346,6 +10645,8 @@ angular.module('material.components.whiteframe', []);
10346
10645
  * @param {boolean=} ng-disabled Determines whether or not to disable the input field
10347
10646
  * @param {number=} md-min-length Specifies the minimum length of text before autocomplete will make suggestions
10348
10647
  * @param {number=} md-delay Specifies the amount of time (in milliseconds) to wait before looking for results
10648
+ * @param {boolean=} md-autofocus If true, will immediately focus the input element
10649
+ * @param {boolean=} md-autoselect If true, the first item will be selected by default
10349
10650
  *
10350
10651
  * @usage
10351
10652
  * <hljs lang="html">
@@ -10361,14 +10662,57 @@ angular.module('material.components.whiteframe', []);
10361
10662
 
10362
10663
  function MdAutocomplete () {
10363
10664
  return {
10364
- template: '\
10665
+ transclude: true,
10666
+ controller: 'MdAutocompleteCtrl',
10667
+ controllerAs: '$mdAutocompleteCtrl',
10668
+ link: link,
10669
+ scope: {
10670
+ searchText: '=mdSearchText',
10671
+ selectedItem: '=mdSelectedItem',
10672
+ itemsExpr: '@mdItems',
10673
+ itemText: '&mdItemText',
10674
+ placeholder: '@placeholder',
10675
+ noCache: '=?mdNoCache',
10676
+ itemChange: '&mdSelectedItemChange',
10677
+ textChange: '&mdSearchTextChange',
10678
+ isDisabled: '=ngDisabled',
10679
+ minLength: '=mdMinLength',
10680
+ delay: '=mdDelay',
10681
+ autofocus: '=?mdAutofocus',
10682
+ floatingLabel: '@mdFloatingLabel',
10683
+ autoselect: '=?mdAutoselect'
10684
+ },
10685
+ template: '\
10365
10686
  <md-autocomplete-wrap role="listbox">\
10687
+ <md-input-container ng-if="floatingLabel">\
10688
+ <label>{{floatingLabel}}</label>\
10689
+ <input type="text"\
10690
+ id="fl-input-{{$mdAutocompleteCtrl.id}}"\
10691
+ name="fl-input-{{$mdAutocompleteCtrl.id}}"\
10692
+ autocomplete="off"\
10693
+ ng-disabled="isDisabled"\
10694
+ ng-model="$mdAutocompleteCtrl.scope.searchText"\
10695
+ ng-keydown="$mdAutocompleteCtrl.keydown($event)"\
10696
+ ng-blur="$mdAutocompleteCtrl.blur()"\
10697
+ aria-owns="ul-{{$mdAutocompleteCtrl.id}}"\
10698
+ aria-label="{{floatingLabel}}"\
10699
+ aria-autocomplete="list"\
10700
+ aria-haspopup="true"\
10701
+ aria-activedescendant=""\
10702
+ aria-expanded="{{!$mdAutocompleteCtrl.hidden}}"/>\
10703
+ \
10704
+ </md-input-container>\
10366
10705
  <input type="text"\
10706
+ id="input-{{$mdAutocompleteCtrl.id}}"\
10707
+ name="input-{{$mdAutocompleteCtrl.id}}"\
10708
+ ng-if="!floatingLabel"\
10709
+ autocomplete="off"\
10367
10710
  ng-disabled="isDisabled"\
10368
- ng-model="searchText"\
10711
+ ng-model="$mdAutocompleteCtrl.scope.searchText"\
10369
10712
  ng-keydown="$mdAutocompleteCtrl.keydown($event)"\
10370
10713
  ng-blur="$mdAutocompleteCtrl.blur()"\
10371
10714
  placeholder="{{placeholder}}"\
10715
+ aria-owns="ul-{{$mdAutocompleteCtrl.id}}"\
10372
10716
  aria-label="{{placeholder}}"\
10373
10717
  aria-autocomplete="list"\
10374
10718
  aria-haspopup="true"\
@@ -10376,7 +10720,7 @@ angular.module('material.components.whiteframe', []);
10376
10720
  aria-expanded="{{!$mdAutocompleteCtrl.hidden}}"/>\
10377
10721
  <button\
10378
10722
  type="button"\
10379
- ng-if="searchText"\
10723
+ ng-if="$mdAutocompleteCtrl.scope.searchText && !isDisabled"\
10380
10724
  ng-click="$mdAutocompleteCtrl.clear()">\
10381
10725
  <md-icon md-svg-icon="cancel"></md-icon>\
10382
10726
  <span class="visually-hidden">Clear</span>\
@@ -10384,42 +10728,35 @@ angular.module('material.components.whiteframe', []);
10384
10728
  <md-progress-linear\
10385
10729
  ng-if="$mdAutocompleteCtrl.loading"\
10386
10730
  md-mode="indeterminate"></md-progress-linear>\
10731
+ <ul role="presentation"\
10732
+ id="ul-{{$mdAutocompleteCtrl.id}}"\
10733
+ ng-mouseenter="$mdAutocompleteCtrl.listEnter()"\
10734
+ ng-mouseleave="$mdAutocompleteCtrl.listLeave()"\
10735
+ ng-mouseup="$mdAutocompleteCtrl.mouseUp()">\
10736
+ <li ng-repeat="(index, item) in $mdAutocompleteCtrl.matches"\
10737
+ ng-class="{ selected: index === $mdAutocompleteCtrl.index }"\
10738
+ ng-show="$mdAutocompleteCtrl.scope.searchText && !$mdAutocompleteCtrl.hidden"\
10739
+ ng-click="$mdAutocompleteCtrl.select(index)"\
10740
+ ng-transclude\
10741
+ md-autocomplete-list-item="$mdAutocompleteCtrl.itemName">\
10742
+ </li>\
10743
+ </ul>\
10387
10744
  </md-autocomplete-wrap>\
10388
- <ul role="presentation"\
10389
- ng-mouseenter="$mdAutocompleteCtrl.listEnter()"\
10390
- ng-mouseleave="$mdAutocompleteCtrl.listLeave()"\
10391
- ng-mouseup="$mdAutocompleteCtrl.mouseUp()">\
10392
- <li ng-repeat="(index, item) in $mdAutocompleteCtrl.matches"\
10393
- ng-class="{ selected: index === $mdAutocompleteCtrl.index }"\
10394
- ng-show="searchText && !$mdAutocompleteCtrl.hidden"\
10395
- ng-click="$mdAutocompleteCtrl.select(index)"\
10396
- ng-transclude\
10397
- md-autocomplete-list-item="$mdAutocompleteCtrl.itemName">\
10398
- </li>\
10399
- </ul>\
10400
10745
  <aria-status\
10401
10746
  class="visually-hidden"\
10402
10747
  role="status"\
10403
10748
  aria-live="assertive">\
10404
10749
  <p ng-repeat="message in $mdAutocompleteCtrl.messages">{{message.display}}</p>\
10405
- </aria-status>',
10406
- transclude: true,
10407
- controller: 'MdAutocompleteCtrl',
10408
- controllerAs: '$mdAutocompleteCtrl',
10409
- scope: {
10410
- searchText: '=mdSearchText',
10411
- selectedItem: '=mdSelectedItem',
10412
- itemsExpr: '@mdItems',
10413
- itemText: '&mdItemText',
10414
- placeholder: '@placeholder',
10415
- noCache: '=mdNoCache',
10416
- itemChange: '&mdSelectedItemChange',
10417
- textChange: '&mdSearchTextChange',
10418
- isDisabled: '=ngDisabled',
10419
- minLength: '=mdMinLength',
10420
- delay: '=mdDelay'
10421
- }
10750
+ </aria-status>'
10422
10751
  };
10752
+
10753
+ function link (scope, element, attr) {
10754
+ angular.forEach(scope.$$isolateBindings, function (binding, key) {
10755
+ if (binding.optional && angular.isUndefined(scope[key])) {
10756
+ scope[key] = attr.hasOwnProperty(attr.$normalize(binding.attrName));
10757
+ }
10758
+ });
10759
+ }
10423
10760
  }
10424
10761
  })();
10425
10762
 
@@ -10534,63 +10871,47 @@ angular.module('material.components.whiteframe', []);
10534
10871
  * @license MIT
10535
10872
  * v0.8.3
10536
10873
  */
10537
- (function() {
10538
- 'use strict';
10539
-
10540
- /**
10541
- * Conditionally configure ink bar animations when the
10542
- * tab selection changes. If `mdNoBar` then do not show the
10543
- * bar nor animate.
10544
- */
10545
- angular.module('material.components.tabs')
10546
- .directive('mdTabsInkBar', MdTabInkDirective);
10547
-
10548
- function MdTabInkDirective($$rAF) {
10549
-
10550
- var lastIndex = 0;
10551
-
10552
- return {
10553
- restrict: 'E',
10554
- require: ['^?mdNoBar', '^mdTabs'],
10555
- link: postLink
10556
- };
10557
-
10558
- function postLink(scope, element, attr, ctrls) {
10559
- var mdNoBar = !!ctrls[0];
10560
-
10561
- var tabsCtrl = ctrls[1],
10562
- debouncedUpdateBar = $$rAF.throttle(updateBar);
10563
-
10564
- tabsCtrl.inkBarElement = element;
10565
-
10566
- scope.$on('$mdTabsPaginationChanged', debouncedUpdateBar);
10567
-
10568
- function updateBar() {
10569
- var selected = tabsCtrl.getSelectedItem();
10570
- var hideInkBar = !selected || tabsCtrl.count() < 2 || mdNoBar;
10571
-
10572
- element.css('display', hideInkBar ? 'none' : 'block');
10573
-
10574
- if (hideInkBar) return;
10874
+ (function () {
10875
+ 'use strict';
10876
+ angular
10877
+ .module('material.components.chips')
10878
+ .directive('mdChipRemove', MdChipRemove);
10575
10879
 
10576
- if (scope.pagination && scope.pagination.tabData) {
10577
- var index = tabsCtrl.getSelectedIndex();
10578
- var data = scope.pagination.tabData.tabs[index] || { left: 0, right: 0, width: 0 };
10579
- var right = element.parent().prop('offsetWidth') - data.right;
10580
- var classNames = ['md-transition-left', 'md-transition-right', 'md-no-transition'];
10581
- var classIndex = lastIndex > index ? 0 : lastIndex < index ? 1 : 2;
10880
+ /**
10881
+ * @ngdoc directive
10882
+ * @name mdChipRemove
10883
+ * @module material.components.chips
10884
+ *
10885
+ * @description
10886
+ * `<element md-chip-remove>`
10887
+ * Identifies an element within an <md-chip> as the delete button. This
10888
+ * directive binds to that element's click event and removes the chip.
10889
+ *
10890
+ * @usage
10891
+ * <hljs lang="html">
10892
+ * <md-chip>{{$chip}}<button md-chip-remove>x</button></md-chip>
10893
+ * </hljs>
10894
+ */
10582
10895
 
10583
- element
10584
- .removeClass(classNames.join(' '))
10585
- .addClass(classNames[classIndex])
10586
- .css({ left: data.left + 'px', right: right + 'px' });
10896
+ function MdChipRemove () {
10897
+ return {
10898
+ restrict: 'A',
10899
+ require: ['^mdChips'],
10900
+ link: function postLink(scope, element, attrs, controllers) {
10901
+ var mdChipsCtrl = controllers[0];
10902
+ element.on('click', removeItemListener(mdChipsCtrl, scope));
10903
+ },
10904
+ scope: false
10905
+ };
10587
10906
 
10588
- lastIndex = index;
10589
- }
10907
+ function removeItemListener(chipsCtrl, scope) {
10908
+ return function() {
10909
+ scope.$apply(function() {
10910
+ chipsCtrl.removeChip(scope.$index);
10911
+ });
10912
+ };
10590
10913
  }
10591
10914
  }
10592
- }
10593
- MdTabInkDirective.$inject = ["$$rAF"];
10594
10915
  })();
10595
10916
 
10596
10917
  /*!
@@ -10599,257 +10920,202 @@ MdTabInkDirective.$inject = ["$$rAF"];
10599
10920
  * @license MIT
10600
10921
  * v0.8.3
10601
10922
  */
10602
- (function() {
10603
- 'use strict';
10604
-
10605
- angular.module('material.components.tabs')
10606
- .directive('mdTabsPagination', TabPaginationDirective);
10923
+ (function () {
10924
+ 'use strict';
10925
+ angular
10926
+ .module('material.components.chips')
10927
+ .controller('MdChipsCtrl', MdChipsCtrl);
10607
10928
 
10608
- function TabPaginationDirective($mdConstant, $window, $$rAF, $$q, $timeout, $mdMedia) {
10609
10929
 
10610
- // Must match (2 * width of paginators) in scss
10611
- var PAGINATORS_WIDTH = (8 * 4) * 2;
10612
10930
 
10613
- return {
10614
- restrict: 'A',
10615
- require: '^mdTabs',
10616
- link: postLink
10617
- };
10931
+ /**
10932
+ * Controller for the MdChips component. Responsible for adding to and
10933
+ * removing from the list of chips, marking chips as selected, and binding to
10934
+ * the models of various input components.
10935
+ *
10936
+ * @param $mdUtil
10937
+ * @param $mdConstant
10938
+ * @param $log
10939
+ * @ngInject
10940
+ * @constructor
10941
+ */
10942
+ function MdChipsCtrl ($mdUtil, $mdConstant, $log) {
10943
+ /** @type {Object} */
10944
+ this.$mdConstant = $mdConstant;
10618
10945
 
10619
- function postLink(scope, element, attr, tabsCtrl) {
10620
-
10621
- var tabs = element[0].getElementsByTagName('md-tab');
10622
- var debouncedUpdatePagination = $$rAF.throttle(updatePagination);
10623
- var tabsParent = element.children();
10624
- var locked = false;
10625
- var state = scope.pagination = {
10626
- page: -1,
10627
- active: false,
10628
- clickNext: function() { locked || userChangePage(+1); },
10629
- clickPrevious: function() { locked || userChangePage(-1); }
10630
- };
10946
+ /** @type {$log} */
10947
+ this.$log = $log;
10631
10948
 
10632
- scope.$on('$mdTabsChanged', debouncedUpdatePagination);
10633
- angular.element($window).on('resize', debouncedUpdatePagination);
10949
+ /** @type {angular.NgModelController} */
10950
+ this.ngModelCtrl = null;
10634
10951
 
10635
- scope.$on('$destroy', function() {
10636
- angular.element($window).off('resize', debouncedUpdatePagination);
10637
- });
10952
+ /** @type {Object} */
10953
+ this.mdAutocompleteCtrl = null;
10638
10954
 
10639
- scope.$watch(function() { return tabsCtrl.tabToFocus; }, onTabFocus);
10955
+ /** @type {Array.<Object>} */
10956
+ this.items = [];
10640
10957
 
10641
- // Make sure we don't focus an element on the next page
10642
- // before it's in view
10643
- function onTabFocus(tab, oldTab) {
10644
- if (!tab) return;
10958
+ /** @type {number} */
10959
+ this.selectedChip = -1;
10645
10960
 
10646
- var pageIndex = getPageForTab(tab);
10647
- if (!state.active || pageIndex === state.page) {
10648
- tab.element.focus();
10649
- } else {
10650
- // Go to the new page, wait for the page transition to end, then focus.
10651
- oldTab && oldTab.element.blur();
10652
- setPage(pageIndex).then(function() {
10653
- locked = false;
10654
- tab.element.focus();
10655
- });
10656
- }
10657
- }
10961
+ /**
10962
+ * Model used by the input element.
10963
+ * @type {string}
10964
+ */
10965
+ this.chipBuffer = '';
10658
10966
 
10659
- // Called when page is changed by a user action (click)
10660
- function userChangePage(increment) {
10661
- var sizeData = state.tabData;
10662
- var newPage = Math.max(0, Math.min(sizeData.pages.length - 1, state.page + increment));
10663
- var newTabIndex = sizeData.pages[newPage][ increment > 0 ? 'firstTabIndex' : 'lastTabIndex' ];
10664
- var newTab = tabsCtrl.itemAt(newTabIndex);
10665
- locked = true;
10666
- onTabFocus(newTab);
10667
- }
10967
+ /**
10968
+ * Whether to use the mdChipAppend expression to transform the chip buffer
10969
+ * before appending it to the list.
10970
+ * @type {boolean}
10971
+ */
10972
+ this.useMdChipAppend = false;
10668
10973
 
10669
- function updatePagination() {
10670
- if (!element.prop('offsetParent')) {
10671
- var watcher = waitForVisible();
10672
- return;
10673
- }
10974
+ /**
10975
+ * Whether the Chip buffer is driven by an input element provided by the
10976
+ * caller.
10977
+ * @type {boolean}
10978
+ */
10979
+ this.hasInputElement = false;
10980
+ }
10981
+ MdChipsCtrl.$inject = ["$mdUtil", "$mdConstant", "$log"];
10674
10982
 
10675
- var tabs = element.find('md-tab');
10676
10983
 
10677
- disablePagination();
10984
+ /**
10985
+ * Handles the keydown event on the input element: <enter> appends the
10986
+ * buffer to the chip list, while backspace removes the last chip in the list
10987
+ * if the current buffer is empty.
10988
+ * @param event
10989
+ */
10990
+ MdChipsCtrl.prototype.defaultInputKeydown = function(event) {
10991
+ switch (event.keyCode) {
10992
+ case this.$mdConstant.KEY_CODE.ENTER:
10993
+ event.preventDefault();
10994
+ this.appendChipBuffer();
10995
+ break;
10996
+ case this.$mdConstant.KEY_CODE.BACKSPACE: // backspace
10997
+ if (!this.chipBuffer) {
10998
+ event.preventDefault();
10999
+ // TODO(typotter): Probably want to open the previous one for edit instead.
11000
+ this.removeChip(this.items.length - 1);
11001
+ }
11002
+ break;
11003
+ default:
11004
+ }
11005
+ };
10678
11006
 
10679
- var sizeData = state.tabData = calculateTabData();
10680
- var needPagination = state.active = sizeData.pages.length > 1;
10681
11007
 
10682
- if (needPagination) { enablePagination(); }
11008
+ /**
11009
+ * Sets the selected chip index to -1.
11010
+ */
11011
+ MdChipsCtrl.prototype.resetSelectedChip = function() {
11012
+ this.selectedChip = -1;
11013
+ };
10683
11014
 
10684
- scope.$evalAsync(function () { scope.$broadcast('$mdTabsPaginationChanged'); });
10685
11015
 
10686
- function enablePagination() {
10687
- tabsParent.css('width', '9999px');
11016
+ /**
11017
+ * Append the contents of the buffer to the chip list. This method will first
11018
+ * call out to the md-chip-append method, if provided
11019
+ */
11020
+ MdChipsCtrl.prototype.appendChipBuffer = function() {
11021
+ var newChip = this.getChipBuffer();
11022
+ if (this.useMdChipAppend && this.mdChipAppend) {
11023
+ newChip = this.mdChipAppend({'$chip': newChip});
11024
+ }
11025
+ this.items.push(newChip);
11026
+ this.resetChipBuffer();
11027
+ };
10688
11028
 
10689
- //-- apply filler margins
10690
- angular.forEach(sizeData.tabs, function (tab) {
10691
- angular.element(tab.element).css('margin-left', tab.filler + 'px');
10692
- });
10693
11029
 
10694
- setPage(getPageForTab(tabsCtrl.getSelectedItem()));
10695
- }
11030
+ /**
11031
+ * Sets whether to use the md-chip-append expression. This expression is
11032
+ * bound to scope and controller in {@code MdChipsDirective} as
11033
+ * {@code mdChipAppend}. Due to the nature of directive scope bindings, the
11034
+ * controller cannot know on its own/from the scope whether an expression was
11035
+ * actually provided.
11036
+ */
11037
+ MdChipsCtrl.prototype.useMdChipAppendExpression = function() {
11038
+ this.useMdChipAppend = true;
11039
+ };
10696
11040
 
10697
- function disablePagination() {
10698
- slideTabButtons(0);
10699
- tabsParent.css('width', '');
10700
- tabs.css('width', '');
10701
- tabs.css('margin-left', '');
10702
- state.page = null;
10703
- state.active = false;
10704
- }
10705
11041
 
10706
- function waitForVisible() {
10707
- return watcher || scope.$watch(
10708
- function () {
10709
- $timeout(function () {
10710
- if (element[0].offsetParent) {
10711
- if (angular.isFunction(watcher)) {
10712
- watcher();
10713
- }
10714
- debouncedUpdatePagination();
10715
- watcher = null;
10716
- }
10717
- }, 0, false);
10718
- }
10719
- );
10720
- }
11042
+ /**
11043
+ * Gets the input buffer. The input buffer can be the model bound to the
11044
+ * default input item {@code this.chipBuffer}, the {@code selectedItem}
11045
+ * model of an {@code md-autocomplete}, or, through some magic, the model
11046
+ * bound to any inpput or text area element found within a
11047
+ * {@code md-input-container} element.
11048
+ * @return {Object|string}
11049
+ */
11050
+ MdChipsCtrl.prototype.getChipBuffer = function() {
11051
+ if (this.mdAutocompleteCtrl) {
11052
+ this.$log.error('md-autocomplete not yet supported');
11053
+ } else if (this.hasInputElement) {
11054
+ this.$log.error('user-provided inputs not yet supported');
11055
+ } else {
11056
+ return this.chipBuffer;
10721
11057
  }
11058
+ };
10722
11059
 
10723
- function slideTabButtons(x) {
10724
- if (tabsCtrl.pagingOffset === x) {
10725
- // Resolve instantly if no change
10726
- return $$q.when();
10727
- }
10728
11060
 
10729
- var deferred = $$q.defer();
11061
+ /**
11062
+ * Resets the input buffer.
11063
+ */
11064
+ MdChipsCtrl.prototype.resetChipBuffer = function() {
11065
+ if (this.mdAutocompleteCtrl) {
11066
+ this.$log.error('md-autocomplete not yet supported');
11067
+ } else {
11068
+ this.chipBuffer = '';
11069
+ }
11070
+ };
10730
11071
 
10731
- tabsCtrl.$$pagingOffset = x;
10732
- tabsParent.css($mdConstant.CSS.TRANSFORM, 'translate3d(' + x + 'px,0,0)');
10733
- tabsParent.on($mdConstant.CSS.TRANSITIONEND, onTabsParentTransitionEnd);
10734
11072
 
10735
- return deferred.promise;
11073
+ /**
11074
+ * Removes the chip at the given index.
11075
+ * @param index
11076
+ */
11077
+ MdChipsCtrl.prototype.removeChip = function(index) {
11078
+ this.items.splice(index, 1);
11079
+ };
10736
11080
 
10737
- function onTabsParentTransitionEnd(ev) {
10738
- // Make sure this event didn't bubble up from an animation in a child element.
10739
- if (ev.target === tabsParent[0]) {
10740
- tabsParent.off($mdConstant.CSS.TRANSITIONEND, onTabsParentTransitionEnd);
10741
- deferred.resolve();
10742
- }
10743
- }
11081
+
11082
+ /**
11083
+ * Marks the chip at the given index as selected.
11084
+ * @param index
11085
+ */
11086
+ MdChipsCtrl.prototype.selectChip = function(index) {
11087
+ if (index >= 0 && index <= this.items.length) {
11088
+ this.selectedChip = index;
11089
+ } else {
11090
+ this.$log.warn('Selected Chip index out of bounds; ignoring.');
10744
11091
  }
11092
+ };
10745
11093
 
10746
- function shouldStretchTabs() {
10747
- switch (scope.stretchTabs) {
10748
- case 'never': return false;
10749
- case 'always': return true;
10750
- default: return $mdMedia('sm');
10751
- }
10752
- }
10753
-
10754
- function calculateTabData(noAdjust) {
10755
- var clientWidth = element.parent().prop('offsetWidth');
10756
- var tabsWidth = clientWidth - PAGINATORS_WIDTH - 1;
10757
- var $tabs = angular.element(tabs);
10758
- var totalWidth = 0;
10759
- var max = 0;
10760
- var tabData = [];
10761
- var pages = [];
10762
- var currentPage;
10763
-
10764
- $tabs.css('max-width', '');
10765
- angular.forEach(tabs, function (tab, index) {
10766
- var tabWidth = Math.min(tabsWidth, tab.offsetWidth);
10767
- var data = {
10768
- element: tab,
10769
- left: totalWidth,
10770
- width: tabWidth,
10771
- right: totalWidth + tabWidth,
10772
- filler: 0
10773
- };
10774
11094
 
10775
- //-- This calculates the page for each tab. The first page will use the clientWidth, which
10776
- // does not factor in the pagination items. After the first page, tabsWidth is used
10777
- // because at this point, we know that the pagination buttons will be shown.
10778
- data.page = Math.ceil(data.right / ( pages.length === 1 && index === tabs.length - 1 ? clientWidth : tabsWidth )) - 1;
10779
-
10780
- if (data.page >= pages.length) {
10781
- data.filler = (tabsWidth * data.page) - data.left;
10782
- data.right += data.filler;
10783
- data.left += data.filler;
10784
- currentPage = {
10785
- left: data.left,
10786
- firstTabIndex: index,
10787
- lastTabIndex: index,
10788
- tabs: [ data ]
10789
- };
10790
- pages.push(currentPage);
10791
- } else {
10792
- currentPage.lastTabIndex = index;
10793
- currentPage.tabs.push(data);
10794
- }
10795
- totalWidth = data.right;
10796
- max = Math.max(max, tabWidth);
10797
- tabData.push(data);
10798
- });
10799
- $tabs.css('max-width', tabsWidth + 'px');
10800
-
10801
- if (!noAdjust && shouldStretchTabs()) {
10802
- return adjustForStretchedTabs();
10803
- } else {
10804
- return {
10805
- width: totalWidth,
10806
- max: max,
10807
- tabs: tabData,
10808
- pages: pages,
10809
- tabElements: tabs
10810
- };
10811
- }
10812
-
10813
-
10814
- function adjustForStretchedTabs() {
10815
- var canvasWidth = pages.length === 1 ? clientWidth : tabsWidth;
10816
- var tabsPerPage = Math.min(Math.floor(canvasWidth / max), tabs.length);
10817
- var tabWidth = Math.floor(canvasWidth / tabsPerPage);
10818
- $tabs.css('width', tabWidth + 'px');
10819
- return calculateTabData(true);
10820
- }
10821
- }
10822
-
10823
- function getPageForTab(tab) {
10824
- var tabIndex = tabsCtrl.indexOf(tab);
10825
- if (tabIndex === -1) return 0;
10826
-
10827
- var sizeData = state.tabData;
10828
-
10829
- return sizeData ? sizeData.tabs[tabIndex].page : 0;
10830
- }
10831
-
10832
- function setPage(page) {
10833
- if (page === state.page) return;
10834
-
10835
- var lastPage = state.tabData.pages.length - 1;
10836
-
10837
- if (page < 0) page = 0;
10838
- if (page > lastPage) page = lastPage;
10839
-
10840
- state.hasPrev = page > 0;
10841
- state.hasNext = page < lastPage;
10842
-
10843
- state.page = page;
11095
+ /**
11096
+ * Configures the required interactions with the ngModel Controller.
11097
+ * Specifically, set {@code this.items} to the {@code NgModelCtrl#$viewVale}.
11098
+ * @param ngModelCtrl
11099
+ */
11100
+ MdChipsCtrl.prototype.configureNgModel = function(ngModelCtrl) {
11101
+ this.ngModelCtrl = ngModelCtrl;
10844
11102
 
10845
- scope.$broadcast('$mdTabsPaginationChanged');
11103
+ var self = this;
11104
+ ngModelCtrl.$render = function() {
11105
+ // model is updated. do something.
11106
+ self.items = self.ngModelCtrl.$viewValue;
11107
+ };
11108
+ };
10846
11109
 
10847
- return slideTabButtons(-state.tabData.pages[page].left);
10848
- }
10849
- }
10850
11110
 
10851
- }
10852
- TabPaginationDirective.$inject = ["$mdConstant", "$window", "$$rAF", "$$q", "$timeout", "$mdMedia"];
11111
+ /**
11112
+ * Configure bindings with the MdAutocomplete control.
11113
+ * @param mdAutocompleteCtrl
11114
+ */
11115
+ MdChipsCtrl.prototype.configureMdAutocomplete = function(mdAutocompleteCtrl) {
11116
+ this.mdAutocompleteCtrl = mdAutocompleteCtrl;
11117
+ // TODO(typotter): create and register a selectedItem watcher with mdAutocompleteCtrl.
11118
+ };
10853
11119
  })();
10854
11120
 
10855
11121
  /*!
@@ -10858,115 +11124,243 @@ TabPaginationDirective.$inject = ["$mdConstant", "$window", "$$rAF", "$$q", "$ti
10858
11124
  * @license MIT
10859
11125
  * v0.8.3
10860
11126
  */
10861
- (function() {
10862
- 'use strict';
11127
+ (function () {
11128
+ 'use strict';
11129
+ angular
11130
+ .module('material.components.chips')
11131
+ .directive('mdChips', MdChips);
11132
+ /**
11133
+ * @ngdoc directive
11134
+ * @name mdChips
11135
+ * @module material.components.chips
11136
+ *
11137
+ * @description
11138
+ * `<md-chips>` is an input component for building lists of strings or objects. The list items are displayed as
11139
+ * 'chips'. This component can make use of an `<input>` element or an `<md-autocomplete>` element.
11140
+ *
11141
+ * <h3> Pending Features </h3>
11142
+ * <ul style="padding-left:20px;">
11143
+ * <ul>Expand input controls: Support md-autocomplete
11144
+ * <li>plain `<input>` tag as child</li>
11145
+ * <li>textarea input</li>
11146
+ * <li>md-input?</li>
11147
+ * </ul>
11148
+ *
11149
+ * <ul>List Manipulation
11150
+ * <li>delete item via DEL or backspace keys when selected</li>
11151
+ * </ul>
11152
+ *
11153
+ * <ul>Validation
11154
+ * <li>de-dupe values (or support duplicates, but fix the ng-repeat duplicate key issue)</li>
11155
+ * <li>allow a validation callback</li>
11156
+ * <li>hilighting style for invalid chips</li>
11157
+ * </ul>
11158
+ *
11159
+ * <ul>Item mutation
11160
+ * <li>Support `
11161
+ * <md-chip-edit>` template, show/hide the edit element on tap/click? double tap/double click?
11162
+ * </li>
11163
+ * </ul>
11164
+ *
11165
+ * <ul>Truncation and Disambiguation (?)
11166
+ * <li>Truncate chip text where possible, but do not truncate entries such that two are indistinguishable.</li>
11167
+ * </ul>
11168
+ *
11169
+ * <ul>Drag and Drop
11170
+ * <li>Drag and drop chips between related `
11171
+ * <md-chips>` elements.
11172
+ * </li>
11173
+ * </ul>
11174
+ * </ul>
11175
+ *
11176
+ * <span style="font-size:.8em;text-align:center">
11177
+ * Warning: This component is a WORK IN PROGRESS. If you use it now,
11178
+ * it will probably break on you in the future.
11179
+ * </span>
11180
+ *
11181
+ *
11182
+ * @param {string=|object=} ng-model A model to bind the list of items to
11183
+ * @param {string=} placeholder Placeholder text that will be forwarded to the input.
11184
+ * @param {string=} secondary-placeholder Placeholder text that will be forwarded to the input, displayed when there
11185
+ * is at least on item in the list
11186
+ * @param {boolean=} readonly Disables list manipulation (deleting or adding list items), hiding the input and delete
11187
+ * buttons
11188
+ * @param {expression} md-chip-append An expression expected to convert the input string into an object when adding
11189
+ * a chip.
11190
+ *
11191
+ * @usage
11192
+ * <hljs lang="html">
11193
+ * <md-chips
11194
+ * ng-model="myItems"
11195
+ * placeholder="Add an item"
11196
+ * readonly="isReadOnly">
11197
+ * </md-chips>
11198
+ * </hljs>
11199
+ *
11200
+ */
10863
11201
 
10864
11202
 
10865
- angular.module('material.components.tabs')
10866
- .controller('$mdTab', TabItemController);
11203
+ var MD_CHIPS_TEMPLATE = '\
11204
+ <md-chips-wrap ng-class="{readonly : $mdChipsCtrl.readonly}" class="md-chips">\
11205
+ <div role="presentation">\
11206
+ <div ng-repeat="$chip in $mdChipsCtrl.items"\
11207
+ ng-class="{selected: $mdChipsCtrl.selectedChip == $index}"\
11208
+ ng-click="!$mdChipsCtrl.readonly && $mdChipsCtrl.selectChip($index)"\
11209
+ class="md-chip">\
11210
+ </div>\
11211
+ <div ng-if="!$mdChipsCtrl.readonly" class="md-chip-worker"></div>\
11212
+ </div>\
11213
+ </md-chips-wrap>';
11214
+
11215
+ var CHIP_INPUT_TEMPLATE = '\
11216
+ <input\
11217
+ placeholder="{{$mdChipsCtrl.items.length == 0 ? $mdChipsCtrl.placeholder : $mdChipsCtrl.secondaryPlaceholder}}"\
11218
+ class="md-chip-input"\
11219
+ ng-model="$mdChipsCtrl.chipBuffer"\
11220
+ ng-focus="$mdChipsCtrl.resetSelectedChip()"\
11221
+ ng-keydown="$mdChipsCtrl.defaultInputKeydown($event)">';
11222
+
11223
+ var CHIP_DEFAULT_TEMPLATE = '\
11224
+ <md-chip>\
11225
+ <span>{{$chip}}</span>\
11226
+ <md-button ng-if="!$mdChipsCtrl.readonly"\
11227
+ md-chip-remove>x</md-button>\
11228
+ </md-chip>';
10867
11229
 
10868
- function TabItemController($scope, $element, $attrs, $compile, $animate, $mdUtil, $parse, $timeout) {
10869
- var self = this;
10870
- var tabsCtrl = $element.controller('mdTabs');
10871
-
10872
- // Properties
10873
- self.contentContainer = angular.element('<div class="md-tab-content ng-hide">');
10874
- self.element = $element;
10875
-
10876
- // Methods
10877
- self.isDisabled = isDisabled;
10878
- self.onAdd = onAdd;
10879
- self.onRemove = onRemove;
10880
- self.onSelect = onSelect;
10881
- self.onDeselect = onDeselect;
10882
-
10883
- var disabledParsed = $parse($attrs.ngDisabled);
10884
- function isDisabled() {
10885
- return disabledParsed($scope.$parent);
10886
- }
10887
11230
 
10888
11231
  /**
10889
- * Add the tab's content to the DOM container area in the tabs,
10890
- * @param contentArea the contentArea to add the content of the tab to
11232
+ * MDChips Directive Definition
11233
+ * @param $mdTheming
11234
+ * @param $log
11235
+ * @ngInject
10891
11236
  */
10892
- function onAdd(contentArea, shouldDisconnectScope) {
10893
- if (self.content.length) {
10894
- self.contentContainer.append(self.content);
10895
- self.contentScope = $scope.$parent.$new();
10896
- contentArea.append(self.contentContainer);
10897
-
10898
- $compile(self.contentContainer)(self.contentScope);
10899
- if (shouldDisconnectScope === true) {
10900
- $timeout(function () {
10901
- $mdUtil.disconnectScope(self.contentScope);
10902
- }, 0, false);
11237
+ function MdChips ($mdTheming, $log) {
11238
+ return {
11239
+ template: function(element, attrs) {
11240
+ // Clone the element into an attribute. By prepending the attribute
11241
+ // name with '$', Angular won't write it into the DOM. The cloned
11242
+ // element propagates to the link function via the attrs argument,
11243
+ // where various contained-elements can be consumed.
11244
+ attrs['$mdUserTemplate'] = element.clone();
11245
+ return MD_CHIPS_TEMPLATE;
11246
+ },
11247
+ require: ['ngModel', 'mdChips'],
11248
+ restrict: 'E',
11249
+ controller: 'MdChipsCtrl',
11250
+ controllerAs: '$mdChipsCtrl',
11251
+ bindToController: true,
11252
+ compile: compile,
11253
+ scope: {
11254
+ readonly: '=readonly',
11255
+ placeholder: '@',
11256
+ secondaryPlaceholder: '@',
11257
+ mdChipAppend: '&'
10903
11258
  }
10904
- }
10905
- }
11259
+ };
11260
+ function compile(element, attr) {
11261
+ var userTemplate = attr['$mdUserTemplate'];
11262
+ var chipEl = userTemplate.find('md-chip');
11263
+ if (chipEl.length === 0) {
11264
+ chipEl = angular.element(CHIP_DEFAULT_TEMPLATE);
11265
+ } else {
11266
+ // Warn if no remove button is included in the template.
11267
+ if (!chipEl[0].querySelector('[md-chip-remove]')) {
11268
+ $log.warn('md-chip-remove attribute not found in md-chip template.');
11269
+ }
11270
+ }
11271
+ var listNode = angular.element(element[0].querySelector('.md-chip'));
11272
+ listNode.append(chipEl);
10906
11273
 
10907
- function onRemove() {
10908
- $animate.leave(self.contentContainer).then(function() {
10909
- self.contentScope && self.contentScope.$destroy();
10910
- self.contentScope = null;
10911
- });
10912
- }
11274
+ // Input Element: Look for an autocomplete or an input.
11275
+ var inputEl = userTemplate.find('md-autocomplete');
11276
+ var hasAutocomplete = inputEl.length > 0;
10913
11277
 
10914
- function toggleAnimationClass(rightToLeft) {
10915
- self.contentContainer[rightToLeft ? 'addClass' : 'removeClass']('md-transition-rtl');
10916
- }
11278
+ if (!hasAutocomplete) {
11279
+ // TODO(typotter): Check for an input or a textarea
10917
11280
 
10918
- function onSelect(rightToLeft) {
10919
- // Resume watchers and events firing when tab is selected
10920
- $mdUtil.reconnectScope(self.contentScope);
11281
+ // Default element.
11282
+ inputEl = angular.element(CHIP_INPUT_TEMPLATE);
11283
+ var workerChip = angular.element(element[0].querySelector('.md-chip-worker'));
11284
+ workerChip.append(inputEl);
11285
+ }
10921
11286
 
10922
- $element
10923
- .addClass('active')
10924
- .attr({
10925
- 'aria-selected': true,
10926
- 'tabIndex': 0
10927
- })
10928
- .on('$md.swipeleft $md.swiperight', onSwipe);
11287
+ return function postLink(scope, element, attrs, controllers) {
11288
+ $mdTheming(element);
11289
+ var ngModelCtrl = controllers[0];
11290
+ var mdChipsCtrl = controllers[1];
11291
+ mdChipsCtrl.configureNgModel(ngModelCtrl);
10929
11292
 
10930
- toggleAnimationClass(rightToLeft);
10931
- $animate.removeClass(self.contentContainer, 'ng-hide');
11293
+ if (attrs.mdChipAppend) {
11294
+ mdChipsCtrl.useMdChipAppendExpression();
11295
+ }
10932
11296
 
10933
- $scope.onSelect();
11297
+ if (hasAutocomplete) {
11298
+ // TODO(typotter): Tell the mdChipsCtrl about the mdAutocompleteCtrl and have it
11299
+ // watch the selectedItem model.
11300
+ $log.error('md-autocomplete not yet supported');
11301
+ }
11302
+ };
11303
+ }
10934
11304
  }
11305
+ MdChips.$inject = ["$mdTheming", "$log"];
11306
+ })();
10935
11307
 
10936
- function onDeselect(rightToLeft) {
10937
- // Stop watchers & events from firing while tab is deselected
10938
- $mdUtil.disconnectScope(self.contentScope);
10939
-
10940
- $element
10941
- .removeClass('active')
10942
- .attr({
10943
- 'aria-selected': false,
10944
- 'tabIndex': -1
10945
- })
10946
- .off('$md.swipeleft $md.swiperight', onSwipe);
10947
-
10948
- toggleAnimationClass(rightToLeft);
10949
- $animate.addClass(self.contentContainer, 'ng-hide');
11308
+ /*!
11309
+ * Angular Material Design
11310
+ * https://github.com/angular/material
11311
+ * @license MIT
11312
+ * v0.8.3
11313
+ */
11314
+ (function () {
11315
+ 'use strict';
11316
+ angular
11317
+ .module('material.components.tabs')
11318
+ .directive('mdLabelTemplate', MdLabelTemplate);
10950
11319
 
10951
- $scope.onDeselect();
11320
+ function MdLabelTemplate ($compile) {
11321
+ return {
11322
+ restrict: 'A',
11323
+ link: link,
11324
+ scope: { template: '=mdLabelTemplate' },
11325
+ require: '^mdTabs'
11326
+ };
11327
+ function link (scope, element, attr, ctrl) {
11328
+ var index = scope.$parent.$index;
11329
+ scope.$watch('template', function (html) {
11330
+ element.html(html);
11331
+ $compile(element.contents())(ctrl.tabs[index].parent);
11332
+ });
11333
+ }
10952
11334
  }
11335
+ MdLabelTemplate.$inject = ["$compile"];
11336
+ })();
11337
+ /*!
11338
+ * Angular Material Design
11339
+ * https://github.com/angular/material
11340
+ * @license MIT
11341
+ * v0.8.3
11342
+ */
11343
+ (function () {
11344
+ 'use strict';
11345
+ angular
11346
+ .module('material.components.tabs')
11347
+ .directive('mdTabContent', MdTabContent);
10953
11348
 
10954
- ///// Private functions
10955
-
10956
- function onSwipe(ev) {
10957
- $scope.$apply(function() {
10958
- if (/left/.test(ev.type)) {
10959
- tabsCtrl.select(tabsCtrl.next());
10960
- } else {
10961
- tabsCtrl.select(tabsCtrl.previous());
10962
- }
10963
- });
11349
+ function MdTabContent ($compile, $mdUtil) {
11350
+ return {
11351
+ terminal: true,
11352
+ scope: {
11353
+ tab: '=mdTabData',
11354
+ active: '=mdActive'
11355
+ },
11356
+ link: link
11357
+ };
11358
+ function link (scope, element) {
11359
+ element.html(scope.tab.template);
11360
+ $compile(element.contents())(scope.tab.parent);
11361
+ }
10964
11362
  }
10965
-
10966
-
10967
- }
10968
- TabItemController.$inject = ["$scope", "$element", "$attrs", "$compile", "$animate", "$mdUtil", "$parse", "$timeout"];
10969
-
11363
+ MdTabContent.$inject = ["$compile", "$mdUtil"];
10970
11364
  })();
10971
11365
 
10972
11366
  /*!
@@ -10975,12 +11369,6 @@ TabItemController.$inject = ["$scope", "$element", "$attrs", "$compile", "$anima
10975
11369
  * @license MIT
10976
11370
  * v0.8.3
10977
11371
  */
10978
- (function() {
10979
- 'use strict';
10980
-
10981
- angular.module('material.components.tabs')
10982
- .directive('mdTab', MdTabDirective);
10983
-
10984
11372
  /**
10985
11373
  * @ngdoc directive
10986
11374
  * @name mdTab
@@ -11003,7 +11391,6 @@ angular.module('material.components.tabs')
11003
11391
  * be initiated via data binding changes, programmatic invocation, or user gestures.
11004
11392
  *
11005
11393
  * @param {string=} label Optional attribute to specify a simple string as the tab label
11006
- * @param {boolean=} md-active When evaluteing to true, selects the tab.
11007
11394
  * @param {boolean=} disabled If present, disabled tab selection.
11008
11395
  * @param {expression=} md-on-deselect Expression to be evaluated after the tab has been de-selected.
11009
11396
  * @param {expression=} md-on-select Expression to be evaluated after the tab has been selected.
@@ -11030,345 +11417,416 @@ angular.module('material.components.tabs')
11030
11417
  * </hljs>
11031
11418
  *
11032
11419
  */
11033
- function MdTabDirective($mdInkRipple, $compile, $mdUtil, $mdConstant, $timeout) {
11034
- return {
11035
- restrict: 'E',
11036
- require: ['mdTab', '^mdTabs'],
11037
- controller: '$mdTab',
11038
- scope: {
11039
- onSelect: '&mdOnSelect',
11040
- onDeselect: '&mdOnDeselect',
11041
- label: '@'
11042
- },
11043
- compile: compile
11044
- };
11045
-
11046
- function compile(element, attr) {
11047
- var tabLabel = element.find('md-tab-label');
11048
-
11049
- if (tabLabel.length) {
11050
- // If a tab label element is found, remove it for later re-use.
11051
- tabLabel.remove();
11052
-
11053
- } else if (angular.isDefined(attr.label)) {
11054
- // Otherwise, try to use attr.label as the label
11055
- tabLabel = angular.element('<md-tab-label>').html(attr.label);
11056
-
11057
- } else {
11058
- // If nothing is found, use the tab's content as the label
11059
- tabLabel = angular.element('<md-tab-label>')
11060
- .append(element.contents().remove());
11061
- }
11062
-
11063
- // Everything that's left as a child is the tab's content.
11064
- var tabContent = element.contents().remove();
11065
-
11066
- return function postLink(scope, element, attr, ctrls) {
11067
11420
 
11068
- var tabItemCtrl = ctrls[0]; // Controller for THIS tabItemCtrl
11069
- var tabsCtrl = ctrls[1]; // Controller for ALL tabs
11070
-
11071
- $timeout(element.addClass.bind(element, 'md-tab-themed'), 0, false);
11421
+ (function () {
11422
+ 'use strict';
11072
11423
 
11073
- scope.$watch(
11074
- function () { return attr.label; },
11075
- function () { $timeout(function () { tabsCtrl.scope.$broadcast('$mdTabsChanged'); }, 0, false); }
11076
- );
11424
+ angular
11425
+ .module('material.components.tabs')
11426
+ .directive('mdTab', MdTab);
11077
11427
 
11078
- transcludeTabContent();
11079
- configureAria();
11428
+ function MdTab () {
11429
+ return {
11430
+ require: '^mdTabs',
11431
+ terminal: true,
11432
+ scope: {
11433
+ label: '@',
11434
+ active: '=?mdActive',
11435
+ disabled: '=?ngDisabled'
11436
+ },
11437
+ link: link
11438
+ };
11080
11439
 
11081
- $mdInkRipple.attachTabBehavior(scope, element, {
11082
- colorElement: tabsCtrl.inkBarElement
11083
- });
11084
- tabsCtrl.add(tabItemCtrl);
11085
- scope.$on('$destroy', function() {
11086
- tabsCtrl.remove(tabItemCtrl);
11440
+ function link (scope, element, attr, ctrl) {
11441
+ var tabs = element.parent()[0].getElementsByTagName('md-tab'),
11442
+ index = Array.prototype.indexOf.call(tabs, element[0]),
11443
+ data = ctrl.insertTab({
11444
+ scope: scope,
11445
+ parent: scope.$parent,
11446
+ index: index,
11447
+ template: getTemplate(),
11448
+ label: getLabel()
11449
+ }, index);
11450
+
11451
+ scope.$watch('active', function (active) { if (active) ctrl.select(data.getIndex()); });
11452
+ scope.$watch('disabled', function () { ctrl.refreshIndex(); });
11453
+ scope.$watch(getTemplate, function (template, oldTemplate) {
11454
+ if (template === oldTemplate) return;
11455
+ data.template = template;
11456
+ ctrl.updateInkBarStyles();
11087
11457
  });
11088
- element.on('$destroy', function () {
11089
- //-- wait for item to be removed from the dom
11090
- $timeout(function () {
11091
- tabsCtrl.scope.$broadcast('$mdTabsChanged');
11092
- }, 0, false);
11458
+ scope.$watch(getLabel, function (label, oldLabel) {
11459
+ if (label === oldLabel) return;
11460
+ data.label = label;
11461
+ ctrl.updateInkBarStyles();
11093
11462
  });
11463
+ scope.$on('$destroy', function () { ctrl.removeTab(data); });
11094
11464
 
11095
- if (!angular.isDefined(attr.ngClick)) {
11096
- element.on('click', defaultClickListener);
11465
+ function getLabel () {
11466
+ return attr.label || (element.find('md-tab-label')[0] || element[0]).innerHTML;
11097
11467
  }
11098
- element.on('keydown', keydownListener);
11099
11468
 
11100
- if (angular.isNumber(scope.$parent.$index)) {
11101
- watchNgRepeatIndex();
11102
- }
11103
- if (angular.isDefined(attr.mdActive)) {
11104
- watchActiveAttribute();
11469
+ function getTemplate () {
11470
+ var content = element.find('md-tab-template');
11471
+ return content.length ? content.html() : attr.label ? element.html() : null;
11105
11472
  }
11106
- watchDisabled();
11473
+ }
11474
+ }
11475
+ })();
11107
11476
 
11108
- function transcludeTabContent() {
11109
- // Clone the label we found earlier, and $compile and append it
11110
- var label = tabLabel.clone();
11111
- element.append(label);
11112
- $compile(label)(scope.$parent);
11477
+ /*!
11478
+ * Angular Material Design
11479
+ * https://github.com/angular/material
11480
+ * @license MIT
11481
+ * v0.8.3
11482
+ */
11483
+ (function () {
11484
+ 'use strict';
11113
11485
 
11114
- // Clone the content we found earlier, and mark it for later placement into
11115
- // the proper content area.
11116
- tabItemCtrl.content = tabContent.clone();
11117
- }
11486
+ angular
11487
+ .module('material.components.tabs')
11488
+ .directive('mdTabItem', MdTabItem);
11118
11489
 
11119
- //defaultClickListener isn't applied if the user provides an ngClick expression.
11120
- function defaultClickListener() {
11121
- scope.$apply(function() {
11122
- tabsCtrl.select(tabItemCtrl);
11123
- tabsCtrl.focus(tabItemCtrl);
11124
- });
11125
- }
11126
- function keydownListener(ev) {
11127
- if (ev.keyCode == $mdConstant.KEY_CODE.SPACE || ev.keyCode == $mdConstant.KEY_CODE.ENTER ) {
11128
- // Fire the click handler to do normal selection if space is pressed
11129
- element.triggerHandler('click');
11130
- ev.preventDefault();
11131
- } else if (ev.keyCode === $mdConstant.KEY_CODE.LEFT_ARROW) {
11132
- scope.$evalAsync(function() {
11133
- tabsCtrl.focus(tabsCtrl.previous(tabItemCtrl));
11134
- });
11135
- } else if (ev.keyCode === $mdConstant.KEY_CODE.RIGHT_ARROW) {
11136
- scope.$evalAsync(function() {
11137
- tabsCtrl.focus(tabsCtrl.next(tabItemCtrl));
11138
- });
11139
- }
11140
- }
11490
+ function MdTabItem () {
11491
+ return { require: '^mdTabs', link: link };
11492
+ function link (scope, element, attr, ctrl) {
11493
+ ctrl.attachRipple(scope, element);
11494
+ }
11495
+ }
11496
+ })();
11497
+ /*!
11498
+ * Angular Material Design
11499
+ * https://github.com/angular/material
11500
+ * @license MIT
11501
+ * v0.8.3
11502
+ */
11503
+ (function () {
11504
+ 'use strict';
11505
+ angular.module('material.components.tabs')
11506
+ .directive('mdTabScroll', MdTabScroll);
11141
11507
 
11142
- // If tabItemCtrl is part of an ngRepeat, move the tabItemCtrl in our internal array
11143
- // when its $index changes
11144
- function watchNgRepeatIndex() {
11145
- // The tabItemCtrl has an isolate scope, so we watch the $index on the parent.
11146
- scope.$watch('$parent.$index', function $indexWatchAction(newIndex) {
11147
- tabsCtrl.move(tabItemCtrl, newIndex);
11508
+ function MdTabScroll () {
11509
+ return {
11510
+ restrict: 'A',
11511
+ link: function (scope, element, attr) {
11512
+ element.on('mousewheel', function (event) {
11513
+ var newScope = scope.$new();
11514
+ newScope.$event = event;
11515
+ newScope.$element = element;
11516
+ newScope.$apply(function () {
11517
+ newScope.$eval(attr.mdTabScroll);
11518
+ });
11148
11519
  });
11149
11520
  }
11150
11521
 
11151
- function watchActiveAttribute() {
11152
- var unwatch = scope.$parent.$watch('!!(' + attr.mdActive + ')', activeWatchAction);
11153
- scope.$on('$destroy', unwatch);
11154
-
11155
- function activeWatchAction(isActive) {
11156
- var isSelected = tabsCtrl.getSelectedItem() === tabItemCtrl;
11157
-
11158
- if (isActive && !isSelected) {
11159
- tabsCtrl.select(tabItemCtrl);
11160
- } else if (!isActive && isSelected) {
11161
- tabsCtrl.deselect(tabItemCtrl);
11162
- }
11163
- }
11164
- }
11522
+ }
11523
+ }
11524
+ })();
11525
+ /*!
11526
+ * Angular Material Design
11527
+ * https://github.com/angular/material
11528
+ * @license MIT
11529
+ * v0.8.3
11530
+ */
11531
+ (function () {
11532
+ 'use strict';
11165
11533
 
11166
- function watchDisabled() {
11167
- scope.$watch(tabItemCtrl.isDisabled, disabledWatchAction);
11534
+ angular
11535
+ .module('material.components.tabs')
11536
+ .controller('MdTabsController', MdTabsController);
11537
+
11538
+ function MdTabsController ($scope, $element, $window, $timeout, $mdConstant, $mdInkRipple, $mdUtil) {
11539
+ var ctrl = this,
11540
+ elements = getElements();
11541
+
11542
+ ctrl.scope = $scope;
11543
+ ctrl.parent = $scope.$parent;
11544
+ ctrl.tabs = [];
11545
+ ctrl.lastSelectedIndex = null;
11546
+ ctrl.focusIndex = 0;
11547
+ ctrl.offsetLeft = 0;
11548
+ ctrl.hasContent = true;
11549
+ ctrl.hasFocus = false;
11550
+ ctrl.lastClick = false;
11551
+
11552
+ ctrl.redirectFocus = redirectFocus;
11553
+ ctrl.attachRipple = attachRipple;
11554
+ ctrl.shouldStretchTabs = shouldStretchTabs;
11555
+ ctrl.shouldPaginate = shouldPaginate;
11556
+ ctrl.insertTab = insertTab;
11557
+ ctrl.removeTab = removeTab;
11558
+ ctrl.select = select;
11559
+ ctrl.scroll = scroll;
11560
+ ctrl.nextPage = nextPage;
11561
+ ctrl.previousPage = previousPage;
11562
+ ctrl.keydown = keydown;
11563
+ ctrl.canPageForward = canPageForward;
11564
+ ctrl.canPageBack = canPageBack;
11565
+ ctrl.refreshIndex = refreshIndex;
11566
+ ctrl.incrementSelectedIndex = incrementSelectedIndex;
11567
+ ctrl.updateInkBarStyles = updateInkBarStyles;
11568
+
11569
+ init();
11168
11570
 
11169
- function disabledWatchAction(isDisabled) {
11170
- element.attr('aria-disabled', isDisabled);
11571
+ function init () {
11572
+ $scope.$watch('selectedIndex', handleSelectedIndexChange);
11573
+ $scope.$watch('$mdTabsCtrl.focusIndex', handleFocusIndexChange);
11574
+ $scope.$watch('$mdTabsCtrl.offsetLeft', handleOffsetChange);
11575
+ angular.element($window).on('resize', function () { $scope.$apply(handleWindowResize); });
11576
+ $timeout(updateInkBarStyles, 0, false);
11577
+ }
11171
11578
 
11172
- // Auto select `next` tab when disabled
11173
- var isSelected = (tabsCtrl.getSelectedItem() === tabItemCtrl);
11174
- if (isSelected && isDisabled) {
11175
- tabsCtrl.select(tabsCtrl.next() || tabsCtrl.previous());
11176
- }
11579
+ function getElements () {
11580
+ var elements = {};
11581
+ elements.canvas = $element[0].getElementsByTagName('md-tabs-canvas')[0];
11582
+ elements.wrapper = elements.canvas.getElementsByTagName('md-pagination-wrapper')[0];
11583
+ elements.tabs = elements.wrapper.getElementsByTagName('md-tab-item');
11584
+ elements.dummies = elements.canvas.getElementsByTagName('md-dummy-tab');
11585
+ elements.inkBar = elements.wrapper.getElementsByTagName('md-ink-bar')[0];
11586
+ return elements;
11587
+ }
11177
11588
 
11178
- }
11589
+ function keydown (event) {
11590
+ switch (event.keyCode) {
11591
+ case $mdConstant.KEY_CODE.LEFT_ARROW:
11592
+ event.preventDefault();
11593
+ incrementSelectedIndex(-1, true);
11594
+ break;
11595
+ case $mdConstant.KEY_CODE.RIGHT_ARROW:
11596
+ event.preventDefault();
11597
+ incrementSelectedIndex(1, true);
11598
+ break;
11599
+ case $mdConstant.KEY_CODE.SPACE:
11600
+ case $mdConstant.KEY_CODE.ENTER:
11601
+ event.preventDefault();
11602
+ $scope.selectedIndex = ctrl.focusIndex;
11603
+ break;
11179
11604
  }
11605
+ ctrl.lastClick = false;
11606
+ }
11180
11607
 
11181
- function configureAria() {
11182
- // Link together the content area and tabItemCtrl with an id
11183
- var tabId = attr.id || ('tab_' + $mdUtil.nextUid());
11184
-
11185
- element.attr({
11186
- id: tabId,
11187
- role: 'tab',
11188
- tabIndex: -1 //this is also set on select/deselect in tabItemCtrl
11189
- });
11190
-
11191
- // Only setup the contentContainer's aria attributes if tab content is provided
11192
- if (tabContent.length) {
11193
- var tabContentId = 'content_' + tabId;
11194
- if (!element.attr('aria-controls')) {
11195
- element.attr('aria-controls', tabContentId);
11196
- }
11197
- tabItemCtrl.contentContainer.attr({
11198
- id: tabContentId,
11199
- role: 'tabpanel',
11200
- 'aria-labelledby': tabId
11201
- });
11202
- }
11608
+ function incrementSelectedIndex (inc, focus) {
11609
+ var newIndex,
11610
+ index = focus ? ctrl.focusIndex : $scope.selectedIndex;
11611
+ for (newIndex = index + inc;
11612
+ ctrl.tabs[newIndex] && ctrl.tabs[newIndex].scope.disabled;
11613
+ newIndex += inc) {}
11614
+ if (ctrl.tabs[newIndex]) {
11615
+ if (focus) ctrl.focusIndex = newIndex;
11616
+ else $scope.selectedIndex = newIndex;
11203
11617
  }
11618
+ }
11204
11619
 
11205
- };
11206
-
11207
- }
11208
-
11209
- }
11210
- MdTabDirective.$inject = ["$mdInkRipple", "$compile", "$mdUtil", "$mdConstant", "$timeout"];
11211
-
11212
- })();
11620
+ function handleOffsetChange (left) {
11621
+ angular.element(elements.wrapper).css('left', '-' + left + 'px');
11622
+ $scope.$broadcast('$mdTabsPaginationChanged');
11623
+ }
11213
11624
 
11214
- /*!
11215
- * Angular Material Design
11216
- * https://github.com/angular/material
11217
- * @license MIT
11218
- * v0.8.3
11219
- */
11220
- (function() {
11221
- 'use strict';
11625
+ function handleFocusIndexChange (newIndex, oldIndex) {
11626
+ if (newIndex === oldIndex) return;
11627
+ if (!elements.tabs[newIndex]) return;
11628
+ adjustOffset();
11629
+ redirectFocus();
11630
+ }
11222
11631
 
11223
- angular.module('material.components.tabs')
11224
- .controller('$mdTabs', MdTabsController);
11632
+ function redirectFocus () {
11633
+ elements.dummies[ctrl.focusIndex].focus();
11634
+ }
11225
11635
 
11226
- function MdTabsController($scope, $element, $mdUtil, $timeout) {
11636
+ function adjustOffset () {
11637
+ var tab = elements.tabs[ctrl.focusIndex],
11638
+ left = tab.offsetLeft,
11639
+ right = tab.offsetWidth + left;
11640
+ ctrl.offsetLeft = Math.max(ctrl.offsetLeft, fixOffset(right - elements.canvas.clientWidth));
11641
+ ctrl.offsetLeft = Math.min(ctrl.offsetLeft, fixOffset(left));
11642
+ }
11227
11643
 
11228
- var tabsList = $mdUtil.iterator([], false);
11229
- var self = this;
11644
+ function handleWindowResize () {
11645
+ ctrl.lastSelectedIndex = $scope.selectedIndex;
11646
+ updateInkBarStyles();
11647
+ ctrl.offsetLeft = fixOffset(ctrl.offsetLeft);
11648
+ }
11230
11649
 
11231
- // Properties
11232
- self.$element = $element;
11233
- self.scope = $scope;
11234
- // The section containing the tab content $elements
11235
- var contentArea = self.contentArea = angular.element($element[0].querySelector('.md-tabs-content'));
11236
-
11237
- // Methods from iterator
11238
- var inRange = self.inRange = tabsList.inRange;
11239
- var indexOf = self.indexOf = tabsList.indexOf;
11240
- var itemAt = self.itemAt = tabsList.itemAt;
11241
- self.count = tabsList.count;
11242
-
11243
- self.getSelectedItem = getSelectedItem;
11244
- self.getSelectedIndex = getSelectedIndex;
11245
- self.add = add;
11246
- self.remove = remove;
11247
- self.move = move;
11248
- self.select = select;
11249
- self.focus = focus;
11250
- self.deselect = deselect;
11251
-
11252
- self.next = next;
11253
- self.previous = previous;
11254
-
11255
- $scope.$on('$destroy', function() {
11256
- deselect(getSelectedItem());
11257
- for (var i = tabsList.count() - 1; i >= 0; i--) {
11258
- remove(tabsList[i], true);
11650
+ function insertTab (tabData, index) {
11651
+ var proto = {
11652
+ getIndex: function () { return ctrl.tabs.indexOf(tab); },
11653
+ isActive: function () { return this.getIndex() === $scope.selectedIndex; },
11654
+ isLeft: function () { return this.getIndex() < $scope.selectedIndex; },
11655
+ isRight: function () { return this.getIndex() > $scope.selectedIndex; },
11656
+ hasFocus: function () { return !ctrl.lastClick && ctrl.hasFocus && this.getIndex() === ctrl.focusIndex; },
11657
+ id: $mdUtil.nextUid()
11658
+ },
11659
+ tab = angular.extend(proto, tabData);
11660
+ if (!angular.isString(tabData.template)) {
11661
+ ctrl.hasContent = false;
11662
+ $element.addClass('md-no-tab-content');
11663
+ }
11664
+ if (angular.isDefined(index)) {
11665
+ ctrl.tabs.splice(index, 0, tab);
11666
+ } else {
11667
+ ctrl.tabs.push(tab);
11668
+ }
11669
+ return tab;
11259
11670
  }
11260
- });
11261
11671
 
11262
- // Get the selected tab
11263
- function getSelectedItem() {
11264
- return itemAt($scope.selectedIndex);
11265
- }
11672
+ function removeTab (tabData) {
11673
+ ctrl.tabs.splice(tabData.getIndex(), 1);
11674
+ refreshIndex();
11675
+ $timeout(function () {
11676
+ updateInkBarStyles();
11677
+ ctrl.offsetLeft = fixOffset(ctrl.offsetLeft);
11678
+ });
11679
+ }
11266
11680
 
11267
- function getSelectedIndex() {
11268
- return $scope.selectedIndex;
11269
- }
11681
+ function refreshIndex () {
11682
+ $scope.selectedIndex = getNearestSafeIndex($scope.selectedIndex);
11683
+ ctrl.focusIndex = getNearestSafeIndex(ctrl.focusIndex);
11684
+ }
11270
11685
 
11271
- // Add a new tab.
11272
- // Returns a method to remove the tab from the list.
11273
- function add(tab, index) {
11274
- tabsList.add(tab, index);
11275
-
11276
- // Select the new tab if we don't have a selectedIndex, or if the
11277
- // selectedIndex we've been waiting for is this tab
11278
- if (!angular.isDefined(tab.element.attr('md-active')) && ($scope.selectedIndex === -1 || !angular.isNumber($scope.selectedIndex) ||
11279
- $scope.selectedIndex === self.indexOf(tab))) {
11280
- tab.onAdd(self.contentArea, false);
11281
- self.select(tab);
11282
- } else {
11283
- tab.onAdd(self.contentArea, true);
11686
+ function handleSelectedIndexChange (newValue, oldValue) {
11687
+ if (newValue === oldValue) return;
11688
+ $scope.selectedIndex = getNearestSafeIndex(newValue);
11689
+ ctrl.lastSelectedIndex = oldValue;
11690
+ updateInkBarStyles();
11691
+ $scope.$broadcast('$mdTabsChanged');
11284
11692
  }
11285
11693
 
11286
- $scope.$broadcast('$mdTabsChanged');
11287
- }
11694
+ function handleResizeWhenVisible () {
11695
+ //-- if there is already a watcher waiting for resize, do nothing
11696
+ if (handleResizeWhenVisible.watcher) return;
11697
+ //-- otherwise, we will abuse the $watch function to check for visible
11698
+ handleResizeWhenVisible.watcher = $scope.$watch(function () {
11699
+ //-- since we are checking for DOM size, we use $timeout to wait for after the DOM updates
11700
+ $timeout(function () {
11701
+ //-- if the watcher has already run (ie. multiple digests in one cycle), do nothing
11702
+ if (!handleResizeWhenVisible.watcher) return;
11288
11703
 
11289
- function remove(tab, noReselect) {
11290
- if (!tabsList.contains(tab)) return;
11291
- if (noReselect) return;
11292
- var isSelectedItem = getSelectedItem() === tab,
11293
- newTab = previous() || next();
11704
+ if ($element.prop('offsetParent')) {
11705
+ handleResizeWhenVisible.watcher();
11706
+ handleResizeWhenVisible.watcher = null;
11294
11707
 
11295
- deselect(tab);
11296
- tabsList.remove(tab);
11297
- tab.onRemove();
11708
+ //-- we have to trigger our own $apply so that the DOM bindings will update
11709
+ $scope.$apply(handleWindowResize);
11710
+ }
11711
+ }, 0, false);
11712
+ });
11713
+ }
11298
11714
 
11299
- $scope.$broadcast('$mdTabsChanged');
11715
+ function updateInkBarStyles () {
11716
+ if (!ctrl.tabs.length) return;
11717
+ //-- if the element is not visible, we will not be able to calculate sizes until it is
11718
+ //-- we should treat that as a resize event rather than just updating the ink bar
11719
+ if (!$element.prop('offsetParent')) return handleResizeWhenVisible();
11720
+ var index = $scope.selectedIndex,
11721
+ totalWidth = elements.wrapper.offsetWidth,
11722
+ tab = elements.tabs[index],
11723
+ left = tab.offsetLeft,
11724
+ right = totalWidth - left - tab.offsetWidth;
11725
+ updateInkBarClassName();
11726
+ angular.element(elements.inkBar).css({ left: left + 'px', right: right + 'px' });
11300
11727
 
11301
- if (isSelectedItem) { select(newTab); }
11302
- }
11728
+ }
11303
11729
 
11304
- // Move a tab (used when ng-repeat order changes)
11305
- function move(tab, toIndex) {
11306
- var isSelected = getSelectedItem() === tab;
11730
+ function updateInkBarClassName () {
11731
+ var newIndex = $scope.selectedIndex,
11732
+ oldIndex = ctrl.lastSelectedIndex,
11733
+ ink = angular.element(elements.inkBar);
11734
+ ink.removeClass('md-left md-right');
11735
+ if (!angular.isNumber(oldIndex)) return;
11736
+ if (newIndex < oldIndex) {
11737
+ ink.addClass('md-left');
11738
+ } else if (newIndex > oldIndex) {
11739
+ ink.addClass('md-right');
11740
+ }
11741
+ }
11307
11742
 
11308
- tabsList.remove(tab);
11309
- tabsList.add(tab, toIndex);
11310
- if (isSelected) select(tab);
11743
+ function getNearestSafeIndex(newIndex) {
11744
+ var maxOffset = Math.max(ctrl.tabs.length - newIndex, newIndex),
11745
+ i, tab;
11746
+ for (i = 0; i <= maxOffset; i++) {
11747
+ tab = ctrl.tabs[newIndex + i];
11748
+ if (tab && (tab.scope.disabled !== true)) return tab.getIndex();
11749
+ tab = ctrl.tabs[newIndex - i];
11750
+ if (tab && (tab.scope.disabled !== true)) return tab.getIndex();
11751
+ }
11752
+ return newIndex;
11753
+ }
11311
11754
 
11312
- $scope.$broadcast('$mdTabsChanged');
11313
- }
11755
+ function shouldStretchTabs () {
11756
+ switch ($scope.stretchTabs) {
11757
+ case 'always': return true;
11758
+ case 'never': return false;
11759
+ default: return !shouldPaginate() && $window.matchMedia('(max-width: 600px)').matches;
11760
+ }
11761
+ }
11314
11762
 
11315
- function select(tab, rightToLeft) {
11316
- if (!tab || tab.isSelected || tab.isDisabled()) return;
11317
- if (!tabsList.contains(tab)) return;
11763
+ function shouldPaginate () {
11764
+ var canvasWidth = $element.prop('clientWidth');
11765
+ angular.forEach(elements.tabs, function (tab) { canvasWidth -= tab.offsetWidth; });
11766
+ return canvasWidth < 0;
11767
+ }
11318
11768
 
11319
- if (!angular.isDefined(rightToLeft)) {
11320
- rightToLeft = indexOf(tab) < $scope.selectedIndex;
11769
+ function select (index) {
11770
+ ctrl.focusIndex = $scope.selectedIndex = index;
11771
+ ctrl.lastClick = true;
11321
11772
  }
11322
- deselect(getSelectedItem(), rightToLeft);
11323
11773
 
11324
- $scope.selectedIndex = indexOf(tab);
11325
- tab.isSelected = true;
11326
- tab.onSelect(rightToLeft);
11774
+ function scroll (event) {
11775
+ if (!shouldPaginate()) return;
11776
+ event.preventDefault();
11777
+ ctrl.offsetLeft = fixOffset(ctrl.offsetLeft - event.wheelDelta);
11778
+ }
11327
11779
 
11328
- $scope.$broadcast('$mdTabsChanged');
11329
- }
11780
+ function fixOffset (value) {
11781
+ var lastTab = elements.tabs[elements.tabs.length - 1],
11782
+ totalWidth = lastTab.offsetLeft + lastTab.offsetWidth;
11783
+ value = Math.max(0, value);
11784
+ value = Math.min(totalWidth - elements.canvas.clientWidth, value);
11785
+ return value;
11786
+ }
11330
11787
 
11331
- function focus(tab) {
11332
- // this variable is watched by pagination
11333
- self.tabToFocus = tab;
11334
- }
11788
+ function nextPage () {
11789
+ var viewportWidth = elements.canvas.clientWidth,
11790
+ totalWidth = viewportWidth + ctrl.offsetLeft,
11791
+ i, tab;
11792
+ for (i = 0; i < elements.tabs.length; i++) {
11793
+ tab = elements.tabs[i];
11794
+ if (tab.offsetLeft + tab.offsetWidth > totalWidth) break;
11795
+ }
11796
+ ctrl.offsetLeft = fixOffset(tab.offsetLeft);
11797
+ }
11335
11798
 
11336
- function deselect(tab, rightToLeft) {
11337
- if (!tab || !tab.isSelected) return;
11338
- if (!tabsList.contains(tab)) return;
11799
+ function previousPage () {
11800
+ var i, tab;
11801
+ for (i = 0; i < elements.tabs.length; i++) {
11802
+ tab = elements.tabs[i];
11803
+ if (tab.offsetLeft + tab.offsetWidth >= ctrl.offsetLeft) break;
11804
+ }
11805
+ ctrl.offsetLeft = fixOffset(tab.offsetLeft + tab.offsetWidth - elements.canvas.clientWidth);
11806
+ }
11339
11807
 
11340
- $scope.selectedIndex = -1;
11341
- tab.isSelected = false;
11342
- tab.onDeselect(rightToLeft);
11343
- }
11808
+ function canPageBack () {
11809
+ return ctrl.offsetLeft > 0;
11810
+ }
11344
11811
 
11345
- function next(tab, filterFn) {
11346
- return tabsList.next(tab || getSelectedItem(), filterFn || isTabEnabled);
11347
- }
11348
- function previous(tab, filterFn) {
11349
- return tabsList.previous(tab || getSelectedItem(), filterFn || isTabEnabled);
11350
- }
11812
+ function canPageForward () {
11813
+ var lastTab = elements.tabs[elements.tabs.length - 1];
11814
+ return lastTab && lastTab.offsetLeft + lastTab.offsetWidth > elements.canvas.clientWidth + ctrl.offsetLeft;
11815
+ }
11351
11816
 
11352
- function isTabEnabled(tab) {
11353
- return tab && !tab.isDisabled();
11817
+ function attachRipple (scope, element) {
11818
+ var options = { colorElement: angular.element(elements.inkBar) };
11819
+ $mdInkRipple.attachTabBehavior(scope, element, options);
11820
+ }
11354
11821
  }
11355
-
11356
- }
11357
- MdTabsController.$inject = ["$scope", "$element", "$mdUtil", "$timeout"];
11822
+ MdTabsController.$inject = ["$scope", "$element", "$window", "$timeout", "$mdConstant", "$mdInkRipple", "$mdUtil"];
11358
11823
  })();
11359
-
11360
11824
  /*!
11361
11825
  * Angular Material Design
11362
11826
  * https://github.com/angular/material
11363
11827
  * @license MIT
11364
11828
  * v0.8.3
11365
11829
  */
11366
- (function() {
11367
- 'use strict';
11368
-
11369
- angular.module('material.components.tabs')
11370
- .directive('mdTabs', TabsDirective);
11371
-
11372
11830
  /**
11373
11831
  * @ngdoc directive
11374
11832
  * @name mdTabs
@@ -11401,14 +11859,10 @@ angular.module('material.components.tabs')
11401
11859
  * **Tabs with internal views** are the traditional usages where each tab has associated view content and the view switching is managed internally by the Tabs component.
11402
11860
  * **Tabs with external view content** is often useful when content associated with each tab is independently managed and data-binding notifications announce tab selection changes.
11403
11861
  *
11404
- * > As a performance bonus, if the tab content is managed internally then the non-active (non-visible) tab contents are temporarily disconnected from the `$scope.$digest()` processes; which restricts and optimizes DOM updates to only the currently active tab.
11405
- *
11406
11862
  * Additional features also include:
11407
11863
  *
11408
11864
  * * Content can include any markup.
11409
11865
  * * If a tab is disabled while active/selected, then the next tab will be auto-selected.
11410
- * * If the currently active tab is the last tab, then next() action will select the first tab.
11411
- * * Any markup (other than **`<md-tab>`** tags) will be transcluded into the tab header area BEFORE the tab buttons.
11412
11866
  *
11413
11867
  * ### Explanation of tab stretching
11414
11868
  *
@@ -11436,106 +11890,135 @@ angular.module('material.components.tabs')
11436
11890
  * <hljs lang="html">
11437
11891
  * <md-tabs md-selected="selectedIndex" >
11438
11892
  * <img ng-src="img/angular.png" class="centered">
11439
- *
11440
11893
  * <md-tab
11441
- * ng-repeat="tab in tabs | orderBy:predicate:reversed"
11442
- * md-on-select="onTabSelected(tab)"
11443
- * md-on-deselect="announceDeselected(tab)"
11444
- * disabled="tab.disabled" >
11445
- *
11446
- * <md-tab-label>
11447
- * {{tab.title}}
11448
- * <img src="img/removeTab.png"
11449
- * ng-click="removeTab(tab)"
11450
- * class="delete" >
11451
- * </md-tab-label>
11452
- *
11894
+ * ng-repeat="tab in tabs | orderBy:predicate:reversed"
11895
+ * md-on-select="onTabSelected(tab)"
11896
+ * md-on-deselect="announceDeselected(tab)"
11897
+ * ng-disabled="tab.disabled">
11898
+ * <md-tab-label>
11899
+ * {{tab.title}}
11900
+ * <img src="img/removeTab.png" ng-click="removeTab(tab)" class="delete">
11901
+ * </md-tab-label>
11902
+ * <md-tab-template>
11453
11903
  * {{tab.content}}
11454
- *
11904
+ * </md-tab-template>
11455
11905
  * </md-tab>
11456
- *
11457
11906
  * </md-tabs>
11458
11907
  * </hljs>
11459
11908
  *
11460
11909
  */
11461
- function TabsDirective($mdTheming) {
11462
- return {
11463
- restrict: 'E',
11464
- controller: '$mdTabs',
11465
- require: 'mdTabs',
11466
- transclude: true,
11467
- scope: {
11468
- selectedIndex: '=?mdSelected'
11469
- },
11470
- template:
11471
- '<section class="md-header" ' +
11472
- 'ng-class="{\'md-paginating\': pagination.active}">' +
11473
-
11474
- '<button class="md-paginator md-prev" ' +
11475
- 'ng-if="pagination.active && pagination.hasPrev" ' +
11476
- 'ng-click="pagination.clickPrevious()" ' +
11477
- 'aria-hidden="true">' +
11478
- '<md-icon md-svg-icon="tabs-arrow"></md-icon>' +
11479
- '</button>' +
11480
-
11481
- // overflow: hidden container when paginating
11482
- '<div class="md-header-items-container" md-tabs-pagination>' +
11483
- // flex container for <md-tab> elements
11484
- '<div class="md-header-items">' +
11485
- '<md-tabs-ink-bar></md-tabs-ink-bar>' +
11486
- '</div>' +
11487
- '</div>' +
11488
11910
 
11489
- '<button class="md-paginator md-next" ' +
11490
- 'ng-if="pagination.active && pagination.hasNext" ' +
11491
- 'ng-click="pagination.clickNext()" ' +
11492
- 'aria-hidden="true">' +
11493
- '<md-icon md-svg-icon="tabs-arrow"></md-icon>' +
11494
- '</button>' +
11495
-
11496
- '</section>' +
11497
- '<section class="md-tabs-content"></section>',
11498
- link: postLink
11499
- };
11500
-
11501
- function postLink(scope, element, attr, tabsCtrl, transclude) {
11502
-
11503
- scope.stretchTabs = attr.hasOwnProperty('mdStretchTabs') ? attr.mdStretchTabs || 'always' : 'auto';
11504
-
11505
- $mdTheming(element);
11506
- configureAria();
11507
- watchSelected();
11508
-
11509
- transclude(scope.$parent, function(clone) {
11510
- angular.element(element[0].querySelector('.md-header-items')).append(clone);
11511
- });
11512
-
11513
- function configureAria() {
11514
- element.attr('role', 'tablist');
11515
- }
11911
+ (function () {
11912
+ 'use strict';
11516
11913
 
11517
- function watchSelected() {
11518
- scope.$watch('selectedIndex', function watchSelectedIndex(newIndex, oldIndex) {
11519
- if (oldIndex == newIndex) return;
11520
- var rightToLeft = oldIndex > newIndex;
11521
- tabsCtrl.deselect(tabsCtrl.itemAt(oldIndex), rightToLeft);
11914
+ angular
11915
+ .module('material.components.tabs')
11916
+ .directive('mdTabs', MdTabs);
11522
11917
 
11523
- if (tabsCtrl.inRange(newIndex)) {
11524
- var newTab = tabsCtrl.itemAt(newIndex);
11525
- while (newTab && newTab.isDisabled()) {
11526
- newTab = newIndex > oldIndex
11527
- ? tabsCtrl.next(newTab)
11528
- : tabsCtrl.previous(newTab);
11529
- }
11530
- tabsCtrl.select(newTab, rightToLeft);
11531
- }
11532
- });
11533
- }
11918
+ function MdTabs ($mdTheming) {
11919
+ return {
11920
+ scope: {
11921
+ selectedIndex: '=?mdSelected',
11922
+ stretchTabs: '@?mdStretchTabs'
11923
+ },
11924
+ transclude: true,
11925
+ template: '\
11926
+ <md-tabs-wrapper ng-class="{ \'md-stretch-tabs\': $mdTabsCtrl.shouldStretchTabs() }">\
11927
+ <md-tab-data ng-transclude></md-tab-data>\
11928
+ <md-prev-button\
11929
+ tabindex="-1"\
11930
+ role="button"\
11931
+ aria-label="Previous Page"\
11932
+ aria-disabled="{{!$mdTabsCtrl.canPageBack()}}"\
11933
+ ng-class="{ \'md-disabled\': !$mdTabsCtrl.canPageBack() }"\
11934
+ ng-if="$mdTabsCtrl.shouldPaginate()"\
11935
+ ng-click="$mdTabsCtrl.previousPage()">\
11936
+ <md-icon md-svg-icon="tabs-arrow"></md-icon>\
11937
+ </md-prev-button>\
11938
+ <md-next-button\
11939
+ tabindex="-1"\
11940
+ role="button"\
11941
+ aria-label="Next Page"\
11942
+ aria-disabled="{{!$mdTabsCtrl.canPageForward()}}"\
11943
+ ng-class="{ \'md-disabled\': !$mdTabsCtrl.canPageForward() }"\
11944
+ ng-if="$mdTabsCtrl.shouldPaginate()"\
11945
+ ng-click="$mdTabsCtrl.nextPage()">\
11946
+ <md-icon md-svg-icon="tabs-arrow"></md-icon>\
11947
+ </md-next-button>\
11948
+ <md-tabs-canvas\
11949
+ tabindex="0"\
11950
+ aria-activedescendant="tab-item-{{$mdTabsCtrl.tabs[$mdTabsCtrl.focusIndex].id}}"\
11951
+ ng-focus="$mdTabsCtrl.redirectFocus()"\
11952
+ ng-class="{ \'md-paginated\': $mdTabsCtrl.shouldPaginate() }"\
11953
+ ng-keydown="$mdTabsCtrl.keydown($event)"\
11954
+ role="tablist">\
11955
+ <md-pagination-wrapper\
11956
+ md-tab-scroll="$mdTabsCtrl.scroll($event)">\
11957
+ <md-tab-item\
11958
+ tabindex="-1"\
11959
+ class="md-tab"\
11960
+ style="max-width: {{ tabWidth ? tabWidth + \'px\' : \'none\' }}"\
11961
+ role="tab"\
11962
+ aria-selected="{{tab.isActive()}}"\
11963
+ aria-disabled="{{tab.scope.disabled}}"\
11964
+ aria-controls="tab-content-{{tab.id}}"\
11965
+ ng-repeat="tab in $mdTabsCtrl.tabs"\
11966
+ ng-click="$mdTabsCtrl.select(tab.getIndex())"\
11967
+ ng-class="{ \'md-active\': tab.isActive(),\
11968
+ \'md-focus\': tab.hasFocus(),\
11969
+ \'md-disabled\': tab.scope.disabled }"\
11970
+ ng-disabled="tab.scope.disabled"\
11971
+ md-swipe-left="$mdTabsCtrl.nextPage()"\
11972
+ md-swipe-right="$mdTabsCtrl.previousPage()"\
11973
+ md-label-template="tab.label"></md-tab-item>\
11974
+ <md-ink-bar ng-hide="noInkBar"></md-ink-bar>\
11975
+ </md-pagination-wrapper>\
11976
+ <div class="visually-hidden">\
11977
+ <md-dummy-tab\
11978
+ tabindex="-1"\
11979
+ id="tab-item-{{tab.id}}"\
11980
+ role="tab"\
11981
+ aria-controls="tab-content-{{tab.id}}"\
11982
+ aria-selected="{{tab.isActive()}}"\
11983
+ aria-disabled="{{tab.scope.disabled}}"\
11984
+ ng-focus="$mdTabsCtrl.hasFocus = true"\
11985
+ ng-blur="$mdTabsCtrl.hasFocus = false"\
11986
+ ng-repeat="tab in $mdTabsCtrl.tabs"\
11987
+ md-label-template="tab.label"></md-dummy-tab>\
11988
+ </div>\
11989
+ </md-tabs-canvas>\
11990
+ </md-tabs-wrapper>\
11991
+ <md-tabs-content-wrapper ng-if="$mdTabsCtrl.hasContent">\
11992
+ <md-tab-content\
11993
+ ng-repeat="(index, tab) in $mdTabsCtrl.tabs" \
11994
+ md-tab-data="tab"\
11995
+ id="tab-content-{{tab.id}}"\
11996
+ aria-labelledby="tab-item-{{tab.id}}"\
11997
+ role="tabpanel"\
11998
+ md-swipe-left="$mdTabsCtrl.incrementSelectedIndex(1)"\
11999
+ md-swipe-right="$mdTabsCtrl.incrementSelectedIndex(-1)"\
12000
+ ng-class="{\
12001
+ \'md-no-transition\': $mdTabsCtrl.lastSelectedIndex == null,\
12002
+ \'md-active\': tab.isActive(),\
12003
+ \'md-left\': tab.isLeft(),\
12004
+ \'md-right\': tab.isRight()\
12005
+ }"></md-tab-content>\
12006
+ </md-tabs-content-wrapper>\
12007
+ ',
12008
+ controller: 'MdTabsController',
12009
+ controllerAs: '$mdTabsCtrl',
12010
+ link: function (scope, element, attr) {
12011
+ //-- watch attributes
12012
+ attr.$observe('mdNoBar', function (value) { scope.noInkBar = angular.isDefined(value); });
12013
+ //-- set default value for selectedIndex
12014
+ scope.selectedIndex = angular.isNumber(scope.selectedIndex) ? scope.selectedIndex : 0;
12015
+ //-- apply themes
12016
+ $mdTheming(element);
12017
+ }
12018
+ };
11534
12019
  }
11535
- }
11536
- TabsDirective.$inject = ["$mdTheming"];
11537
- })();
11538
-
11539
- (function(){
11540
- angular.module("material.core").constant("$MD_THEME_CSS", "md-autocomplete { background: '{{background-50}}'; } md-autocomplete button md-icon path { fill: '{{background-600}}'; } md-autocomplete button:after { background: '{{background-600-0.3}}'; } md-autocomplete ul { background: '{{background-50}}'; } md-autocomplete ul li { border-top: 1px solid '{{background-400}}'; color: '{{background-900}}'; } md-autocomplete ul li .highlight { color: '{{background-600}}'; } md-autocomplete ul li:hover, md-autocomplete ul li.selected { background: '{{background-200}}'; }md-backdrop.md-opaque.md-THEME_NAME-theme { background-color: '{{foreground-4-0.5}}'; }md-bottom-sheet.md-THEME_NAME-theme { background-color: '{{background-50}}'; border-top-color: '{{background-300}}'; } md-bottom-sheet.md-THEME_NAME-theme.md-list md-item { color: '{{foreground-1}}'; } md-bottom-sheet.md-THEME_NAME-theme .md-subheader { background-color: '{{background-50}}'; } md-bottom-sheet.md-THEME_NAME-theme .md-subheader { color: '{{foreground-1}}'; }md-toolbar .md-button.md-THEME_NAME-theme.md-fab { background-color: white; }.md-button.md-THEME_NAME-theme { border-radius: 3px; } .md-button.md-THEME_NAME-theme:not([disabled]):hover, .md-button.md-THEME_NAME-theme:not([disabled]):focus { background-color: '{{background-500-0.2}}'; } .md-button.md-THEME_NAME-theme.md-primary { color: '{{primary-color}}'; } .md-button.md-THEME_NAME-theme.md-primary.md-raised, .md-button.md-THEME_NAME-theme.md-primary.md-fab { color: '{{primary-contrast}}'; background-color: '{{primary-color}}'; } .md-button.md-THEME_NAME-theme.md-primary.md-raised:not([disabled]):hover, .md-button.md-THEME_NAME-theme.md-primary.md-raised:not([disabled]):focus, .md-button.md-THEME_NAME-theme.md-primary.md-fab:not([disabled]):hover, .md-button.md-THEME_NAME-theme.md-primary.md-fab:not([disabled]):focus { background-color: '{{primary-600}}'; } .md-button.md-THEME_NAME-theme.md-fab { border-radius: 50%; background-color: '{{accent-color}}'; color: '{{accent-contrast}}'; } .md-button.md-THEME_NAME-theme.md-fab:not([disabled]):hover, .md-button.md-THEME_NAME-theme.md-fab:not([disabled]):focus { background-color: '{{accent-A700}}'; } .md-button.md-THEME_NAME-theme.md-raised { color: '{{background-contrast}}'; background-color: '{{background-50}}'; } .md-button.md-THEME_NAME-theme.md-raised:not([disabled]):hover, .md-button.md-THEME_NAME-theme.md-raised:not([disabled]):focus { background-color: '{{background-200}}'; } .md-button.md-THEME_NAME-theme.md-warn { color: '{{warn-color}}'; } .md-button.md-THEME_NAME-theme.md-warn.md-raised, .md-button.md-THEME_NAME-theme.md-warn.md-fab { color: '{{warn-contrast}}'; background-color: '{{warn-color}}'; } .md-button.md-THEME_NAME-theme.md-warn.md-raised:not([disabled]):hover, .md-button.md-THEME_NAME-theme.md-warn.md-raised:not([disabled]):focus, .md-button.md-THEME_NAME-theme.md-warn.md-fab:not([disabled]):hover, .md-button.md-THEME_NAME-theme.md-warn.md-fab:not([disabled]):focus { background-color: '{{warn-700}}'; } .md-button.md-THEME_NAME-theme.md-accent { color: '{{accent-color}}'; } .md-button.md-THEME_NAME-theme.md-accent.md-raised, .md-button.md-THEME_NAME-theme.md-accent.md-fab { color: '{{accent-contrast}}'; background-color: '{{accent-color}}'; } .md-button.md-THEME_NAME-theme.md-accent.md-raised:not([disabled]):hover, .md-button.md-THEME_NAME-theme.md-accent.md-raised:not([disabled]):focus, .md-button.md-THEME_NAME-theme.md-accent.md-fab:not([disabled]):hover, .md-button.md-THEME_NAME-theme.md-accent.md-fab:not([disabled]):focus { background-color: '{{accent-700}}'; } .md-button.md-THEME_NAME-theme[disabled], .md-button.md-THEME_NAME-theme.md-raised[disabled], .md-button.md-THEME_NAME-theme.md-fab[disabled] { color: '{{foreground-3}}'; background-color: transparent; cursor: not-allowed; }md-card.md-THEME_NAME-theme { border-radius: 2px; } md-card.md-THEME_NAME-theme .md-card-image { border-radius: 2px 2px 0 0; }md-checkbox.md-THEME_NAME-theme .md-ripple { color: '{{accent-600}}'; }md-checkbox.md-THEME_NAME-theme.md-checked .md-ripple { color: '{{background-600}}'; }md-checkbox.md-THEME_NAME-theme .md-icon { border-color: '{{foreground-2}}'; }md-checkbox.md-THEME_NAME-theme.md-checked .md-icon { background-color: '{{accent-color-0.87}}'; }md-checkbox.md-THEME_NAME-theme.md-checked .md-icon:after { border-color: '{{background-200}}'; }md-checkbox.md-THEME_NAME-theme:not([disabled]).md-primary .md-ripple { color: '{{primary-600}}'; }md-checkbox.md-THEME_NAME-theme:not([disabled]).md-primary.md-checked .md-ripple { color: '{{background-600}}'; }md-checkbox.md-THEME_NAME-theme:not([disabled]).md-primary .md-icon { border-color: '{{foreground-2}}'; }md-checkbox.md-THEME_NAME-theme:not([disabled]).md-primary.md-checked .md-icon { background-color: '{{primary-color-0.87}}'; }md-checkbox.md-THEME_NAME-theme:not([disabled]).md-primary.md-checked .md-icon:after { border-color: '{{background-200}}'; }md-checkbox.md-THEME_NAME-theme:not([disabled]).md-warn .md-ripple { color: '{{warn-600}}'; }md-checkbox.md-THEME_NAME-theme:not([disabled]).md-warn .md-icon { border-color: '{{foreground-2}}'; }md-checkbox.md-THEME_NAME-theme:not([disabled]).md-warn.md-checked .md-icon { background-color: '{{warn-color-0.87}}'; }md-checkbox.md-THEME_NAME-theme:not([disabled]).md-warn.md-checked .md-icon:after { border-color: '{{background-200}}'; }md-checkbox.md-THEME_NAME-theme[disabled] .md-icon { border-color: '{{foreground-3}}'; }md-checkbox.md-THEME_NAME-theme[disabled].md-checked .md-icon { background-color: '{{foreground-3}}'; }md-content.md-THEME_NAME-theme { background-color: '{{background-hue-3}}'; }md-dialog.md-THEME_NAME-theme { border-radius: 4px; background-color: '{{background-hue-3}}'; } md-dialog.md-THEME_NAME-theme.md-content-overflow .md-actions { border-top-color: '{{foreground-4}}'; }md-divider.md-THEME_NAME-theme { border-top-color: '{{foreground-4}}'; }md-icon.md-THEME_NAME-theme.md-primary { color: '{{primary-color}}'; }md-icon.md-THEME_NAME-theme.md-accent { color: '{{accent-color}}'; }md-icon.md-THEME_NAME-theme.md-warn { color: '{{warn-color}}'; }md-icon.md-THEME_NAME-theme.md-danger { color: '{{danger-color}}'; }md-input-container.md-THEME_NAME-theme .md-input { color: '{{foreground-1}}'; border-color: '{{foreground-4}}'; text-shadow: '{{foreground-shadow}}'; } md-input-container.md-THEME_NAME-theme .md-input::-webkit-input-placeholder, md-input-container.md-THEME_NAME-theme .md-input::-moz-placeholder, md-input-container.md-THEME_NAME-theme .md-input:-moz-placeholder, md-input-container.md-THEME_NAME-theme .md-input:-ms-input-placeholder { color: '{{foreground-3}}'; }md-input-container.md-THEME_NAME-theme > md-icon { fill: '{{foreground-1}}'; }md-input-container.md-THEME_NAME-theme label, md-input-container.md-THEME_NAME-theme .md-placeholder { text-shadow: '{{foreground-shadow}}'; color: '{{foreground-3}}'; }md-input-container.md-THEME_NAME-theme:not(.md-input-invalid).md-input-has-value label { color: '{{foreground-2}}'; }md-input-container.md-THEME_NAME-theme:not(.md-input-invalid).md-input-focused .md-input { border-color: '{{primary-500}}'; }md-input-container.md-THEME_NAME-theme:not(.md-input-invalid).md-input-focused label { color: '{{primary-500}}'; }md-input-container.md-THEME_NAME-theme:not(.md-input-invalid).md-input-focused md-icon { fill: '{{primary-500}}'; }md-input-container.md-THEME_NAME-theme:not(.md-input-invalid).md-input-focused.md-accent .md-input { border-color: '{{accent-500}}'; }md-input-container.md-THEME_NAME-theme:not(.md-input-invalid).md-input-focused.md-accent label { color: '{{accent-500}}'; }md-input-container.md-THEME_NAME-theme:not(.md-input-invalid).md-input-focused.md-warn .md-input { border-color: '{{warn-500}}'; }md-input-container.md-THEME_NAME-theme:not(.md-input-invalid).md-input-focused.md-warn label { color: '{{warn-500}}'; }md-input-container.md-THEME_NAME-theme.md-input-invalid .md-input { border-color: '{{warn-500}}'; }md-input-container.md-THEME_NAME-theme.md-input-invalid label { color: '{{warn-500}}'; }md-input-container.md-THEME_NAME-theme.md-input-invalid ng-message, md-input-container.md-THEME_NAME-theme.md-input-invalid data-ng-message, md-input-container.md-THEME_NAME-theme.md-input-invalid x-ng-message, md-input-container.md-THEME_NAME-theme.md-input-invalid [ng-message], md-input-container.md-THEME_NAME-theme.md-input-invalid [data-ng-message], md-input-container.md-THEME_NAME-theme.md-input-invalid [x-ng-message], md-input-container.md-THEME_NAME-theme.md-input-invalid .md-char-counter { color: '{{warn-500}}'; }md-input-container.md-THEME_NAME-theme .md-input[disabled], [disabled] md-input-container.md-THEME_NAME-theme .md-input { border-bottom-color: transparent; color: '{{foreground-3}}'; background-image: linear-gradient(to right, '{{foreground-4}}' 0%, '{{foreground-4}}' 33%, transparent 0%); background-image: -ms-linear-gradient(left, transparent 0%, '{{foreground-4}}' 100%); }md-progress-circular.md-THEME_NAME-theme { background-color: transparent; } md-progress-circular.md-THEME_NAME-theme .md-inner .md-gap { border-top-color: '{{primary-color}}'; border-bottom-color: '{{primary-color}}'; } md-progress-circular.md-THEME_NAME-theme .md-inner .md-left .md-half-circle, md-progress-circular.md-THEME_NAME-theme .md-inner .md-right .md-half-circle { border-top-color: '{{primary-color}}'; } md-progress-circular.md-THEME_NAME-theme .md-inner .md-right .md-half-circle { border-right-color: '{{primary-color}}'; } md-progress-circular.md-THEME_NAME-theme .md-inner .md-left .md-half-circle { border-left-color: '{{primary-color}}'; } md-progress-circular.md-THEME_NAME-theme.md-warn .md-inner .md-gap { border-top-color: '{{warn-color}}'; border-bottom-color: '{{warn-color}}'; } md-progress-circular.md-THEME_NAME-theme.md-warn .md-inner .md-left .md-half-circle, md-progress-circular.md-THEME_NAME-theme.md-warn .md-inner .md-right .md-half-circle { border-top-color: '{{warn-color}}'; } md-progress-circular.md-THEME_NAME-theme.md-warn .md-inner .md-right .md-half-circle { border-right-color: '{{warn-color}}'; } md-progress-circular.md-THEME_NAME-theme.md-warn .md-inner .md-left .md-half-circle { border-left-color: '{{warn-color}}'; } md-progress-circular.md-THEME_NAME-theme.md-accent .md-inner .md-gap { border-top-color: '{{accent-color}}'; border-bottom-color: '{{accent-color}}'; } md-progress-circular.md-THEME_NAME-theme.md-accent .md-inner .md-left .md-half-circle, md-progress-circular.md-THEME_NAME-theme.md-accent .md-inner .md-right .md-half-circle { border-top-color: '{{accent-color}}'; } md-progress-circular.md-THEME_NAME-theme.md-accent .md-inner .md-right .md-half-circle { border-right-color: '{{accent-color}}'; } md-progress-circular.md-THEME_NAME-theme.md-accent .md-inner .md-left .md-half-circle { border-left-color: '{{accent-color}}'; }md-progress-linear.md-THEME_NAME-theme .md-container { background-color: '{{primary-100}}'; }md-progress-linear.md-THEME_NAME-theme .md-bar { background-color: '{{primary-color}}'; }md-progress-linear.md-THEME_NAME-theme.md-warn .md-container { background-color: '{{warn-100}}'; }md-progress-linear.md-THEME_NAME-theme.md-warn .md-bar { background-color: '{{warn-color}}'; }md-progress-linear.md-THEME_NAME-theme.md-accent .md-container { background-color: '{{accent-100}}'; }md-progress-linear.md-THEME_NAME-theme.md-accent .md-bar { background-color: '{{accent-color}}'; }md-progress-linear.md-THEME_NAME-theme[md-mode=buffer].md-warn .md-bar1 { background-color: '{{warn-100}}'; }md-progress-linear.md-THEME_NAME-theme[md-mode=buffer].md-warn .md-dashed:before { background: radial-gradient('{{warn-100}}' 0%, '{{warn-100}}' 16%, transparent 42%); }md-progress-linear.md-THEME_NAME-theme[md-mode=buffer].md-accent .md-bar1 { background-color: '{{accent-100}}'; }md-progress-linear.md-THEME_NAME-theme[md-mode=buffer].md-accent .md-dashed:before { background: radial-gradient('{{accent-100}}' 0%, '{{accent-100}}' 16%, transparent 42%); }md-radio-button.md-THEME_NAME-theme .md-off { border-color: '{{foreground-2}}'; }md-radio-button.md-THEME_NAME-theme .md-on { background-color: '{{accent-color-0.87}}'; }md-radio-button.md-THEME_NAME-theme.md-checked .md-off { border-color: '{{accent-color-0.87}}'; }md-radio-button.md-THEME_NAME-theme.md-checked .md-ink-ripple { color: '{{accent-color-0.87}}'; }md-radio-button.md-THEME_NAME-theme .md-container .md-ripple { color: '{{accent-600}}'; }md-radio-button.md-THEME_NAME-theme:not([disabled]).md-primary .md-on { background-color: '{{primary-color-0.87}}'; }md-radio-button.md-THEME_NAME-theme:not([disabled]).md-primary.md-checked .md-off { border-color: '{{primary-color-0.87}}'; }md-radio-button.md-THEME_NAME-theme:not([disabled]).md-primary.md-checked .md-ink-ripple { color: '{{primary-color-0.87}}'; }md-radio-button.md-THEME_NAME-theme:not([disabled]).md-primary .md-container .md-ripple { color: '{{primary-600}}'; }md-radio-button.md-THEME_NAME-theme:not([disabled]).md-warn .md-on { background-color: '{{warn-color-0.87}}'; }md-radio-button.md-THEME_NAME-theme:not([disabled]).md-warn.md-checked .md-off { border-color: '{{warn-color-0.87}}'; }md-radio-button.md-THEME_NAME-theme:not([disabled]).md-warn.md-checked .md-ink-ripple { color: '{{warn-color-0.87}}'; }md-radio-button.md-THEME_NAME-theme:not([disabled]).md-warn .md-container .md-ripple { color: '{{warn-600}}'; }md-radio-button.md-THEME_NAME-theme[disabled] .md-container .md-off { border-color: '{{foreground-3}}'; }md-radio-button.md-THEME_NAME-theme[disabled] .md-container .md-on { border-color: '{{foreground-3}}'; }md-radio-group.md-THEME_NAME-theme:focus:not(:empty) { border-color: '{{foreground-1}}'; }md-select.md-THEME_NAME-theme:not([disabled]):focus .md-select-label { border-bottom-color: '{{primary-color}}'; color: '{{ foreground-1 }}'; } md-select.md-THEME_NAME-theme:not([disabled]):focus .md-select-label.md-placeholder { color: '{{ foreground-1 }}'; }md-select.md-THEME_NAME-theme:not([disabled]):focus.md-accent .md-select-label { border-bottom-color: '{{accent-color}}'; }md-select.md-THEME_NAME-theme:not([disabled]):focus.md-warn .md-select-label { border-bottom-color: '{{warn-color}}'; }md-select.md-THEME_NAME-theme[disabled] .md-select-label { color: '{{foreground-3}}'; } md-select.md-THEME_NAME-theme[disabled] .md-select-label.md-placeholder { color: '{{foreground-3}}'; }md-select.md-THEME_NAME-theme .md-select-label { border-bottom-color: '{{foreground-4}}'; } md-select.md-THEME_NAME-theme .md-select-label.md-placeholder { color: '{{foreground-2}}'; }md-select-menu.md-THEME_NAME-theme md-optgroup { color: '{{foreground-2}}'; } md-select-menu.md-THEME_NAME-theme md-optgroup md-option { color: '{{foreground-1}}'; }md-select-menu.md-THEME_NAME-theme md-option[selected] { background-color: '{{primary-50}}'; } md-select-menu.md-THEME_NAME-theme md-option[selected]:focus { background-color: '{{primary-100}}'; } md-select-menu.md-THEME_NAME-theme md-option[selected].md-accent { background-color: '{{accent-50}}'; } md-select-menu.md-THEME_NAME-theme md-option[selected].md-accent:focus { background-color: '{{accent-100}}'; }md-select-menu.md-THEME_NAME-theme md-option:focus:not([selected]) { background: '{{background-200}}'; }md-sidenav.md-THEME_NAME-theme { background-color: '{{background-hue-3}}'; }md-slider.md-THEME_NAME-theme .md-track { background-color: '{{foreground-3}}'; }md-slider.md-THEME_NAME-theme .md-track-ticks { background-color: '{{foreground-4}}'; }md-slider.md-THEME_NAME-theme .md-focus-thumb { background-color: '{{foreground-2}}'; }md-slider.md-THEME_NAME-theme .md-focus-ring { border-color: '{{foreground-4}}'; }md-slider.md-THEME_NAME-theme .md-disabled-thumb { border-color: '{{background-hue-3}}'; }md-slider.md-THEME_NAME-theme.md-min .md-thumb:after { background-color: '{{background-hue-3}}'; }md-slider.md-THEME_NAME-theme .md-track.md-track-fill { background-color: '{{accent-color}}'; }md-slider.md-THEME_NAME-theme .md-thumb:after { border-color: '{{accent-color}}'; background-color: '{{accent-color}}'; }md-slider.md-THEME_NAME-theme .md-sign { background-color: '{{accent-color}}'; } md-slider.md-THEME_NAME-theme .md-sign:after { border-top-color: '{{accent-color}}'; }md-slider.md-THEME_NAME-theme .md-thumb-text { color: '{{accent-contrast}}'; }md-slider.md-THEME_NAME-theme.md-warn .md-track.md-track-fill { background-color: '{{warn-color}}'; }md-slider.md-THEME_NAME-theme.md-warn .md-thumb:after { border-color: '{{warn-color}}'; background-color: '{{warn-color}}'; }md-slider.md-THEME_NAME-theme.md-warn .md-sign { background-color: '{{warn-color}}'; } md-slider.md-THEME_NAME-theme.md-warn .md-sign:after { border-top-color: '{{warn-color}}'; }md-slider.md-THEME_NAME-theme.md-warn .md-thumb-text { color: '{{warn-contrast}}'; }md-slider.md-THEME_NAME-theme.md-primary .md-track.md-track-fill { background-color: '{{primary-color}}'; }md-slider.md-THEME_NAME-theme.md-primary .md-thumb:after { border-color: '{{primary-color}}'; background-color: '{{primary-color}}'; }md-slider.md-THEME_NAME-theme.md-primary .md-sign { background-color: '{{primary-color}}'; } md-slider.md-THEME_NAME-theme.md-primary .md-sign:after { border-top-color: '{{primary-color}}'; }md-slider.md-THEME_NAME-theme.md-primary .md-thumb-text { color: '{{primary-contrast}}'; }md-slider.md-THEME_NAME-theme[disabled] .md-thumb:after { border-color: '{{foreground-3}}'; }md-slider.md-THEME_NAME-theme[disabled]:not(.md-min) .md-thumb:after { background-color: '{{foreground-3}}'; }.md-subheader.md-THEME_NAME-theme { color: '{{ foreground-2-0.23 }}'; background-color: '{{background-hue-3}}'; } .md-subheader.md-THEME_NAME-theme.md-primary { color: '{{primary-color}}'; } .md-subheader.md-THEME_NAME-theme.md-accent { color: '{{accent-color}}'; } .md-subheader.md-THEME_NAME-theme.md-warn { color: '{{warn-color}}'; }md-switch.md-THEME_NAME-theme .md-thumb { background-color: '{{background-50}}'; }md-switch.md-THEME_NAME-theme .md-bar { background-color: '{{background-500}}'; }md-switch.md-THEME_NAME-theme.md-checked .md-thumb { background-color: '{{accent-color}}'; }md-switch.md-THEME_NAME-theme.md-checked .md-bar { background-color: '{{accent-color-0.5}}'; }md-switch.md-THEME_NAME-theme.md-checked.md-primary .md-thumb { background-color: '{{primary-color}}'; }md-switch.md-THEME_NAME-theme.md-checked.md-primary .md-bar { background-color: '{{primary-color-0.5}}'; }md-switch.md-THEME_NAME-theme.md-checked.md-warn .md-thumb { background-color: '{{warn-color}}'; }md-switch.md-THEME_NAME-theme.md-checked.md-warn .md-bar { background-color: '{{warn-color-0.5}}'; }md-switch.md-THEME_NAME-theme[disabled] .md-thumb { background-color: '{{background-400}}'; }md-switch.md-THEME_NAME-theme[disabled] .md-bar { background-color: '{{foreground-4}}'; }md-switch.md-THEME_NAME-theme:focus .md-label:not(:empty) { border-color: '{{foreground-1}}'; border-style: dotted; }md-tabs.md-THEME_NAME-theme .md-header { background-color: transparent; }md-tabs.md-THEME_NAME-theme .md-paginator md-icon { color: '{{primary-color}}'; }md-tabs.md-THEME_NAME-theme.md-accent .md-header { background-color: '{{accent-color}}'; }md-tabs.md-THEME_NAME-theme.md-accent md-tab:not([disabled]) { color: '{{accent-100}}'; } md-tabs.md-THEME_NAME-theme.md-accent md-tab:not([disabled]).active { color: '{{accent-contrast}}'; }md-tabs.md-THEME_NAME-theme.md-primary .md-header { background-color: '{{primary-color}}'; }md-tabs.md-THEME_NAME-theme.md-primary md-tab:not([disabled]) { color: '{{primary-100}}'; } md-tabs.md-THEME_NAME-theme.md-primary md-tab:not([disabled]).active { color: '{{primary-contrast}}'; }md-tabs.md-THEME_NAME-theme.md-primary md-tab { color: '{{primary-100}}'; } md-tabs.md-THEME_NAME-theme.md-primary md-tab[disabled] { color: '{{foreground-3}}'; } md-tabs.md-THEME_NAME-theme.md-primary md-tab:focus { color: '{{primary-contrast}}'; background-color: '{{primary-contrast-0.1}}'; } md-tabs.md-THEME_NAME-theme.md-primary md-tab.active { color: '{{primary-contrast}}'; } md-tabs.md-THEME_NAME-theme.md-primary md-tab .md-ripple-container { color: '{{primary-contrast}}'; }md-tabs.md-THEME_NAME-theme.md-warn .md-header { background-color: '{{warn-color}}'; }md-tabs.md-THEME_NAME-theme.md-warn md-tab:not([disabled]) { color: '{{warn-100}}'; } md-tabs.md-THEME_NAME-theme.md-warn md-tab:not([disabled]).active { color: '{{warn-contrast}}'; }md-tabs.md-THEME_NAME-theme md-tabs-ink-bar { color: '{{accent-color}}'; background: '{{accent-color}}'; }md-tabs.md-THEME_NAME-theme md-tab { color: '{{foreground-2}}'; } md-tabs.md-THEME_NAME-theme md-tab[disabled] { color: '{{foreground-3}}'; } md-tabs.md-THEME_NAME-theme md-tab:focus { color: '{{foreground-1}}'; } md-tabs.md-THEME_NAME-theme md-tab.active { color: '{{primary-color}}'; } md-tabs.md-THEME_NAME-theme md-tab .md-ripple-container { color: '{{accent-100}}'; }md-input-group.md-THEME_NAME-theme input, md-input-group.md-THEME_NAME-theme textarea { text-shadow: '{{foreground-shadow}}'; } md-input-group.md-THEME_NAME-theme input::-webkit-input-placeholder, md-input-group.md-THEME_NAME-theme input::-moz-placeholder, md-input-group.md-THEME_NAME-theme input:-moz-placeholder, md-input-group.md-THEME_NAME-theme input:-ms-input-placeholder, md-input-group.md-THEME_NAME-theme textarea::-webkit-input-placeholder, md-input-group.md-THEME_NAME-theme textarea::-moz-placeholder, md-input-group.md-THEME_NAME-theme textarea:-moz-placeholder, md-input-group.md-THEME_NAME-theme textarea:-ms-input-placeholder { color: '{{foreground-3}}'; }md-input-group.md-THEME_NAME-theme label { text-shadow: '{{foreground-shadow}}'; color: '{{foreground-3}}'; }md-input-group.md-THEME_NAME-theme input, md-input-group.md-THEME_NAME-theme textarea { color: '{{foreground-1}}'; border-color: '{{foreground-4}}'; }md-input-group.md-THEME_NAME-theme.md-input-focused input, md-input-group.md-THEME_NAME-theme.md-input-focused textarea { border-color: '{{primary-500}}'; }md-input-group.md-THEME_NAME-theme.md-input-focused label { color: '{{primary-500}}'; }md-input-group.md-THEME_NAME-theme.md-input-focused.md-accent input, md-input-group.md-THEME_NAME-theme.md-input-focused.md-accent textarea { border-color: '{{accent-500}}'; }md-input-group.md-THEME_NAME-theme.md-input-focused.md-accent label { color: '{{accent-500}}'; }md-input-group.md-THEME_NAME-theme.md-input-has-value:not(.md-input-focused) label { color: '{{foreground-2}}'; }md-input-group.md-THEME_NAME-theme .md-input[disabled] { border-bottom-color: '{{foreground-4}}'; color: '{{foreground-3}}'; }md-toast.md-THEME_NAME-theme { background-color: '{{foreground-1}}'; color: '{{background-50}}'; } md-toast.md-THEME_NAME-theme .md-button { color: '{{background-50}}'; } md-toast.md-THEME_NAME-theme .md-button.md-highlight { color: '{{primary-A200}}'; } md-toast.md-THEME_NAME-theme .md-button.md-highlight.md-accent { color: '{{accent-A200}}'; } md-toast.md-THEME_NAME-theme .md-button.md-highlight.md-warn { color: '{{warn-A200}}'; }md-toolbar.md-THEME_NAME-theme { background-color: '{{primary-color}}'; color: '{{primary-contrast}}'; } md-toolbar.md-THEME_NAME-theme .md-button { color: '{{primary-contrast}}'; } md-toolbar.md-THEME_NAME-theme.md-accent { background-color: '{{accent-color}}'; color: '{{accent-contrast}}'; } md-toolbar.md-THEME_NAME-theme.md-warn { background-color: '{{warn-color}}'; color: '{{warn-contrast}}'; }md-tooltip.md-THEME_NAME-theme { color: '{{background-A100}}'; } md-tooltip.md-THEME_NAME-theme .md-background { background-color: '{{foreground-2}}'; }");
12020
+ MdTabs.$inject = ["$mdTheming"];
11541
12021
  })();
12022
+ (function(){
12023
+ angular.module("material.core").constant("$MD_THEME_CSS", "md-autocomplete { background: '{{background-50}}'; } md-autocomplete button md-icon path { fill: '{{background-600}}'; } md-autocomplete button:after { background: '{{background-600-0.3}}'; } md-autocomplete ul { background: '{{background-50}}'; } md-autocomplete ul li { border-top: 1px solid '{{background-400}}'; color: '{{background-900}}'; } md-autocomplete ul li .highlight { color: '{{background-600}}'; } md-autocomplete ul li:hover, md-autocomplete ul li.selected { background: '{{background-200}}'; }md-backdrop.md-opaque.md-THEME_NAME-theme { background-color: '{{foreground-4-0.5}}'; }md-bottom-sheet.md-THEME_NAME-theme { background-color: '{{background-50}}'; border-top-color: '{{background-300}}'; } md-bottom-sheet.md-THEME_NAME-theme.md-list md-item { color: '{{foreground-1}}'; } md-bottom-sheet.md-THEME_NAME-theme .md-subheader { background-color: '{{background-50}}'; } md-bottom-sheet.md-THEME_NAME-theme .md-subheader { color: '{{foreground-1}}'; }.md-button.md-THEME_NAME-theme { border-radius: 3px; } .md-button.md-THEME_NAME-theme:not([disabled]):focus { background-color: '{{background-500-0.2}}'; } .md-button.md-THEME_NAME-theme.md-fab { border-radius: 50%; background-color: '{{accent-color}}'; color: '{{accent-contrast}}'; } .md-button.md-THEME_NAME-theme.md-fab:not([disabled]):focus { background-color: '{{accent-A700}}'; } .md-button.md-THEME_NAME-theme.md-primary { color: '{{primary-color}}'; } .md-button.md-THEME_NAME-theme.md-primary.md-raised, .md-button.md-THEME_NAME-theme.md-primary.md-fab { color: '{{primary-contrast}}'; background-color: '{{primary-color}}'; } .md-button.md-THEME_NAME-theme.md-primary.md-raised:not([disabled]):focus, .md-button.md-THEME_NAME-theme.md-primary.md-fab:not([disabled]):focus { background-color: '{{primary-600}}'; } .md-button.md-THEME_NAME-theme.md-raised { color: '{{background-contrast}}'; background-color: '{{background-50}}'; } .md-button.md-THEME_NAME-theme.md-raised:not([disabled]):focus { background-color: '{{background-200}}'; } .md-button.md-THEME_NAME-theme.md-warn { color: '{{warn-color}}'; } .md-button.md-THEME_NAME-theme.md-warn.md-raised, .md-button.md-THEME_NAME-theme.md-warn.md-fab { color: '{{warn-contrast}}'; background-color: '{{warn-color}}'; } .md-button.md-THEME_NAME-theme.md-warn.md-raised:not([disabled]):focus, .md-button.md-THEME_NAME-theme.md-warn.md-fab:not([disabled]):focus { background-color: '{{warn-700}}'; } .md-button.md-THEME_NAME-theme.md-accent { color: '{{accent-color}}'; } .md-button.md-THEME_NAME-theme.md-accent.md-raised, .md-button.md-THEME_NAME-theme.md-accent.md-fab { color: '{{accent-contrast}}'; background-color: '{{accent-color}}'; } .md-button.md-THEME_NAME-theme.md-accent.md-raised:not([disabled]):focus, .md-button.md-THEME_NAME-theme.md-accent.md-fab:not([disabled]):focus { background-color: '{{accent-700}}'; } .md-button.md-THEME_NAME-theme[disabled], .md-button.md-THEME_NAME-theme.md-raised[disabled], .md-button.md-THEME_NAME-theme.md-fab[disabled] { color: '{{foreground-2}}'; cursor: not-allowed; } .md-button.md-THEME_NAME-theme.md-raised[disabled], .md-button.md-THEME_NAME-theme.md-fab[disabled] { background-color: '{{foreground-4}}'; } .md-button.md-THEME_NAME-theme[disabled] { background-color: 'transparent'; }md-card.md-THEME_NAME-theme { border-radius: 2px; } md-card.md-THEME_NAME-theme .md-card-image { border-radius: 2px 2px 0 0; }md-checkbox.md-THEME_NAME-theme .md-ripple { color: '{{accent-600}}'; }md-checkbox.md-THEME_NAME-theme.md-checked .md-ripple { color: '{{background-600}}'; }md-checkbox.md-THEME_NAME-theme .md-icon { border-color: '{{foreground-2}}'; }md-checkbox.md-THEME_NAME-theme.md-checked .md-icon { background-color: '{{accent-color-0.87}}'; }md-checkbox.md-THEME_NAME-theme.md-checked .md-icon:after { border-color: '{{background-200}}'; }md-checkbox.md-THEME_NAME-theme:not([disabled]).md-primary .md-ripple { color: '{{primary-600}}'; }md-checkbox.md-THEME_NAME-theme:not([disabled]).md-primary.md-checked .md-ripple { color: '{{background-600}}'; }md-checkbox.md-THEME_NAME-theme:not([disabled]).md-primary .md-icon { border-color: '{{foreground-2}}'; }md-checkbox.md-THEME_NAME-theme:not([disabled]).md-primary.md-checked .md-icon { background-color: '{{primary-color-0.87}}'; }md-checkbox.md-THEME_NAME-theme:not([disabled]).md-primary.md-checked .md-icon:after { border-color: '{{background-200}}'; }md-checkbox.md-THEME_NAME-theme:not([disabled]).md-warn .md-ripple { color: '{{warn-600}}'; }md-checkbox.md-THEME_NAME-theme:not([disabled]).md-warn .md-icon { border-color: '{{foreground-2}}'; }md-checkbox.md-THEME_NAME-theme:not([disabled]).md-warn.md-checked .md-icon { background-color: '{{warn-color-0.87}}'; }md-checkbox.md-THEME_NAME-theme:not([disabled]).md-warn.md-checked .md-icon:after { border-color: '{{background-200}}'; }md-checkbox.md-THEME_NAME-theme[disabled] .md-icon { border-color: '{{foreground-3}}'; }md-checkbox.md-THEME_NAME-theme[disabled].md-checked .md-icon { background-color: '{{foreground-3}}'; }md-chips.md-THEME_NAME-theme .md-chip { color: '{{background-contrast}}'; background-color: '{{background-100}}'; }md-content.md-THEME_NAME-theme { background-color: '{{background-hue-3}}'; }md-dialog.md-THEME_NAME-theme { border-radius: 4px; background-color: '{{background-hue-3}}'; } md-dialog.md-THEME_NAME-theme.md-content-overflow .md-actions { border-top-color: '{{foreground-4}}'; }md-divider.md-THEME_NAME-theme { border-top-color: '{{foreground-4}}'; }md-icon.md-THEME_NAME-theme.md-primary { color: '{{primary-color}}'; }md-icon.md-THEME_NAME-theme.md-accent { color: '{{accent-color}}'; }md-icon.md-THEME_NAME-theme.md-warn { color: '{{warn-color}}'; }md-icon.md-THEME_NAME-theme.md-danger { color: '{{danger-color}}'; }md-input-container.md-THEME_NAME-theme .md-input { color: '{{foreground-1}}'; border-color: '{{foreground-4}}'; text-shadow: '{{foreground-shadow}}'; } md-input-container.md-THEME_NAME-theme .md-input::-webkit-input-placeholder, md-input-container.md-THEME_NAME-theme .md-input::-moz-placeholder, md-input-container.md-THEME_NAME-theme .md-input:-moz-placeholder, md-input-container.md-THEME_NAME-theme .md-input:-ms-input-placeholder { color: '{{foreground-3}}'; }md-input-container.md-THEME_NAME-theme > md-icon { color: '{{foreground-1}}'; }md-input-container.md-THEME_NAME-theme label, md-input-container.md-THEME_NAME-theme .md-placeholder { text-shadow: '{{foreground-shadow}}'; color: '{{foreground-3}}'; }md-input-container.md-THEME_NAME-theme div[ng-messages] { color: '{{warn-500}}'; }md-input-container.md-THEME_NAME-theme:not(.md-input-invalid).md-input-has-value label { color: '{{foreground-2}}'; }md-input-container.md-THEME_NAME-theme:not(.md-input-invalid).md-input-focused .md-input { border-color: '{{primary-500}}'; }md-input-container.md-THEME_NAME-theme:not(.md-input-invalid).md-input-focused label { color: '{{primary-500}}'; }md-input-container.md-THEME_NAME-theme:not(.md-input-invalid).md-input-focused md-icon { color: '{{primary-500}}'; }md-input-container.md-THEME_NAME-theme:not(.md-input-invalid).md-input-focused.md-accent .md-input { border-color: '{{accent-500}}'; }md-input-container.md-THEME_NAME-theme:not(.md-input-invalid).md-input-focused.md-accent label { color: '{{accent-500}}'; }md-input-container.md-THEME_NAME-theme:not(.md-input-invalid).md-input-focused.md-warn .md-input { border-color: '{{warn-500}}'; }md-input-container.md-THEME_NAME-theme:not(.md-input-invalid).md-input-focused.md-warn label { color: '{{warn-500}}'; }md-input-container.md-THEME_NAME-theme.md-input-invalid .md-input { border-color: '{{warn-500}}'; }md-input-container.md-THEME_NAME-theme.md-input-invalid label { color: '{{warn-500}}'; }md-input-container.md-THEME_NAME-theme.md-input-invalid ng-message, md-input-container.md-THEME_NAME-theme.md-input-invalid data-ng-message, md-input-container.md-THEME_NAME-theme.md-input-invalid x-ng-message, md-input-container.md-THEME_NAME-theme.md-input-invalid [ng-message], md-input-container.md-THEME_NAME-theme.md-input-invalid [data-ng-message], md-input-container.md-THEME_NAME-theme.md-input-invalid [x-ng-message], md-input-container.md-THEME_NAME-theme.md-input-invalid .md-char-counter { color: '{{warn-500}}'; }md-input-container.md-THEME_NAME-theme .md-input[disabled], [disabled] md-input-container.md-THEME_NAME-theme .md-input { border-bottom-color: transparent; color: '{{foreground-3}}'; background-image: linear-gradient(to right, '{{foreground-4}}' 0%, '{{foreground-4}}' 33%, transparent 0%); background-image: -ms-linear-gradient(left, transparent 0%, '{{foreground-4}}' 100%); }md-progress-circular.md-THEME_NAME-theme { background-color: transparent; } md-progress-circular.md-THEME_NAME-theme .md-inner .md-gap { border-top-color: '{{primary-color}}'; border-bottom-color: '{{primary-color}}'; } md-progress-circular.md-THEME_NAME-theme .md-inner .md-left .md-half-circle, md-progress-circular.md-THEME_NAME-theme .md-inner .md-right .md-half-circle { border-top-color: '{{primary-color}}'; } md-progress-circular.md-THEME_NAME-theme .md-inner .md-right .md-half-circle { border-right-color: '{{primary-color}}'; } md-progress-circular.md-THEME_NAME-theme .md-inner .md-left .md-half-circle { border-left-color: '{{primary-color}}'; } md-progress-circular.md-THEME_NAME-theme.md-warn .md-inner .md-gap { border-top-color: '{{warn-color}}'; border-bottom-color: '{{warn-color}}'; } md-progress-circular.md-THEME_NAME-theme.md-warn .md-inner .md-left .md-half-circle, md-progress-circular.md-THEME_NAME-theme.md-warn .md-inner .md-right .md-half-circle { border-top-color: '{{warn-color}}'; } md-progress-circular.md-THEME_NAME-theme.md-warn .md-inner .md-right .md-half-circle { border-right-color: '{{warn-color}}'; } md-progress-circular.md-THEME_NAME-theme.md-warn .md-inner .md-left .md-half-circle { border-left-color: '{{warn-color}}'; } md-progress-circular.md-THEME_NAME-theme.md-accent .md-inner .md-gap { border-top-color: '{{accent-color}}'; border-bottom-color: '{{accent-color}}'; } md-progress-circular.md-THEME_NAME-theme.md-accent .md-inner .md-left .md-half-circle, md-progress-circular.md-THEME_NAME-theme.md-accent .md-inner .md-right .md-half-circle { border-top-color: '{{accent-color}}'; } md-progress-circular.md-THEME_NAME-theme.md-accent .md-inner .md-right .md-half-circle { border-right-color: '{{accent-color}}'; } md-progress-circular.md-THEME_NAME-theme.md-accent .md-inner .md-left .md-half-circle { border-left-color: '{{accent-color}}'; }md-progress-linear.md-THEME_NAME-theme .md-container { background-color: '{{primary-100}}'; }md-progress-linear.md-THEME_NAME-theme .md-bar { background-color: '{{primary-color}}'; }md-progress-linear.md-THEME_NAME-theme.md-warn .md-container { background-color: '{{warn-100}}'; }md-progress-linear.md-THEME_NAME-theme.md-warn .md-bar { background-color: '{{warn-color}}'; }md-progress-linear.md-THEME_NAME-theme.md-accent .md-container { background-color: '{{accent-100}}'; }md-progress-linear.md-THEME_NAME-theme.md-accent .md-bar { background-color: '{{accent-color}}'; }md-progress-linear.md-THEME_NAME-theme[md-mode=buffer].md-warn .md-bar1 { background-color: '{{warn-100}}'; }md-progress-linear.md-THEME_NAME-theme[md-mode=buffer].md-warn .md-dashed:before { background: radial-gradient('{{warn-100}}' 0%, '{{warn-100}}' 16%, transparent 42%); }md-progress-linear.md-THEME_NAME-theme[md-mode=buffer].md-accent .md-bar1 { background-color: '{{accent-100}}'; }md-progress-linear.md-THEME_NAME-theme[md-mode=buffer].md-accent .md-dashed:before { background: radial-gradient('{{accent-100}}' 0%, '{{accent-100}}' 16%, transparent 42%); }md-radio-button.md-THEME_NAME-theme .md-off { border-color: '{{foreground-2}}'; }md-radio-button.md-THEME_NAME-theme .md-on { background-color: '{{accent-color-0.87}}'; }md-radio-button.md-THEME_NAME-theme.md-checked .md-off { border-color: '{{accent-color-0.87}}'; }md-radio-button.md-THEME_NAME-theme.md-checked .md-ink-ripple { color: '{{accent-color-0.87}}'; }md-radio-button.md-THEME_NAME-theme .md-container .md-ripple { color: '{{accent-600}}'; }md-radio-button.md-THEME_NAME-theme:not([disabled]).md-primary .md-on { background-color: '{{primary-color-0.87}}'; }md-radio-button.md-THEME_NAME-theme:not([disabled]).md-primary.md-checked .md-off { border-color: '{{primary-color-0.87}}'; }md-radio-button.md-THEME_NAME-theme:not([disabled]).md-primary.md-checked .md-ink-ripple { color: '{{primary-color-0.87}}'; }md-radio-button.md-THEME_NAME-theme:not([disabled]).md-primary .md-container .md-ripple { color: '{{primary-600}}'; }md-radio-button.md-THEME_NAME-theme:not([disabled]).md-warn .md-on { background-color: '{{warn-color-0.87}}'; }md-radio-button.md-THEME_NAME-theme:not([disabled]).md-warn.md-checked .md-off { border-color: '{{warn-color-0.87}}'; }md-radio-button.md-THEME_NAME-theme:not([disabled]).md-warn.md-checked .md-ink-ripple { color: '{{warn-color-0.87}}'; }md-radio-button.md-THEME_NAME-theme:not([disabled]).md-warn .md-container .md-ripple { color: '{{warn-600}}'; }md-radio-button.md-THEME_NAME-theme[disabled] .md-container .md-off { border-color: '{{foreground-3}}'; }md-radio-button.md-THEME_NAME-theme[disabled] .md-container .md-on { border-color: '{{foreground-3}}'; }md-radio-group.md-THEME_NAME-theme:focus:not(:empty) { border-color: '{{foreground-1}}'; }md-select.md-THEME_NAME-theme.ng-invalid.ng-dirty .md-select-label { color: '{{warn-500}}' !important; border-bottom-color: '{{warn-500}}' !important; }md-select.md-THEME_NAME-theme:not([disabled]):focus .md-select-label { border-bottom-color: '{{primary-color}}'; color: '{{ foreground-1 }}'; } md-select.md-THEME_NAME-theme:not([disabled]):focus .md-select-label.md-placeholder { color: '{{ foreground-1 }}'; }md-select.md-THEME_NAME-theme:not([disabled]):focus.md-accent .md-select-label { border-bottom-color: '{{accent-color}}'; }md-select.md-THEME_NAME-theme:not([disabled]):focus.md-warn .md-select-label { border-bottom-color: '{{warn-color}}'; }md-select.md-THEME_NAME-theme[disabled] .md-select-label { color: '{{foreground-3}}'; } md-select.md-THEME_NAME-theme[disabled] .md-select-label.md-placeholder { color: '{{foreground-3}}'; }md-select.md-THEME_NAME-theme .md-select-label { border-bottom-color: '{{foreground-4}}'; } md-select.md-THEME_NAME-theme .md-select-label.md-placeholder { color: '{{foreground-2}}'; }md-select-menu.md-THEME_NAME-theme md-optgroup { color: '{{foreground-2}}'; } md-select-menu.md-THEME_NAME-theme md-optgroup md-option { color: '{{foreground-1}}'; }md-select-menu.md-THEME_NAME-theme md-option[selected] { background-color: '{{primary-50}}'; } md-select-menu.md-THEME_NAME-theme md-option[selected]:focus { background-color: '{{primary-100}}'; } md-select-menu.md-THEME_NAME-theme md-option[selected].md-accent { background-color: '{{accent-50}}'; } md-select-menu.md-THEME_NAME-theme md-option[selected].md-accent:focus { background-color: '{{accent-100}}'; }md-select-menu.md-THEME_NAME-theme md-option:focus:not([selected]) { background: '{{background-200}}'; }md-sidenav.md-THEME_NAME-theme { background-color: '{{background-hue-3}}'; }md-slider.md-THEME_NAME-theme .md-track { background-color: '{{foreground-3}}'; }md-slider.md-THEME_NAME-theme .md-track-ticks { background-color: '{{foreground-4}}'; }md-slider.md-THEME_NAME-theme .md-focus-thumb { background-color: '{{foreground-2}}'; }md-slider.md-THEME_NAME-theme .md-focus-ring { border-color: '{{foreground-4}}'; }md-slider.md-THEME_NAME-theme .md-disabled-thumb { border-color: '{{background-hue-3}}'; }md-slider.md-THEME_NAME-theme.md-min .md-thumb:after { background-color: '{{background-hue-3}}'; }md-slider.md-THEME_NAME-theme .md-track.md-track-fill { background-color: '{{accent-color}}'; }md-slider.md-THEME_NAME-theme .md-thumb:after { border-color: '{{accent-color}}'; background-color: '{{accent-color}}'; }md-slider.md-THEME_NAME-theme .md-sign { background-color: '{{accent-color}}'; } md-slider.md-THEME_NAME-theme .md-sign:after { border-top-color: '{{accent-color}}'; }md-slider.md-THEME_NAME-theme .md-thumb-text { color: '{{accent-contrast}}'; }md-slider.md-THEME_NAME-theme.md-warn .md-track.md-track-fill { background-color: '{{warn-color}}'; }md-slider.md-THEME_NAME-theme.md-warn .md-thumb:after { border-color: '{{warn-color}}'; background-color: '{{warn-color}}'; }md-slider.md-THEME_NAME-theme.md-warn .md-sign { background-color: '{{warn-color}}'; } md-slider.md-THEME_NAME-theme.md-warn .md-sign:after { border-top-color: '{{warn-color}}'; }md-slider.md-THEME_NAME-theme.md-warn .md-thumb-text { color: '{{warn-contrast}}'; }md-slider.md-THEME_NAME-theme.md-primary .md-track.md-track-fill { background-color: '{{primary-color}}'; }md-slider.md-THEME_NAME-theme.md-primary .md-thumb:after { border-color: '{{primary-color}}'; background-color: '{{primary-color}}'; }md-slider.md-THEME_NAME-theme.md-primary .md-sign { background-color: '{{primary-color}}'; } md-slider.md-THEME_NAME-theme.md-primary .md-sign:after { border-top-color: '{{primary-color}}'; }md-slider.md-THEME_NAME-theme.md-primary .md-thumb-text { color: '{{primary-contrast}}'; }md-slider.md-THEME_NAME-theme[disabled] .md-thumb:after { border-color: '{{foreground-3}}'; }md-slider.md-THEME_NAME-theme[disabled]:not(.md-min) .md-thumb:after { background-color: '{{foreground-3}}'; }.md-subheader.md-THEME_NAME-theme { color: '{{ foreground-2-0.23 }}'; background-color: '{{background-hue-3}}'; } .md-subheader.md-THEME_NAME-theme.md-primary { color: '{{primary-color}}'; } .md-subheader.md-THEME_NAME-theme.md-accent { color: '{{accent-color}}'; } .md-subheader.md-THEME_NAME-theme.md-warn { color: '{{warn-color}}'; }md-switch.md-THEME_NAME-theme .md-thumb { background-color: '{{background-50}}'; }md-switch.md-THEME_NAME-theme .md-bar { background-color: '{{background-500}}'; }md-switch.md-THEME_NAME-theme.md-checked .md-thumb { background-color: '{{accent-color}}'; }md-switch.md-THEME_NAME-theme.md-checked .md-bar { background-color: '{{accent-color-0.5}}'; }md-switch.md-THEME_NAME-theme.md-checked.md-primary .md-thumb { background-color: '{{primary-color}}'; }md-switch.md-THEME_NAME-theme.md-checked.md-primary .md-bar { background-color: '{{primary-color-0.5}}'; }md-switch.md-THEME_NAME-theme.md-checked.md-warn .md-thumb { background-color: '{{warn-color}}'; }md-switch.md-THEME_NAME-theme.md-checked.md-warn .md-bar { background-color: '{{warn-color-0.5}}'; }md-switch.md-THEME_NAME-theme[disabled] .md-thumb { background-color: '{{background-400}}'; }md-switch.md-THEME_NAME-theme[disabled] .md-bar { background-color: '{{foreground-4}}'; }md-switch.md-THEME_NAME-theme:focus .md-label:not(:empty) { border-color: '{{foreground-1}}'; border-style: dotted; }md-tabs.md-THEME_NAME-theme md-tabs-wrapper { background-color: transparent; border-color: '{{foreground-4}}'; }md-tabs.md-THEME_NAME-theme .md-paginator md-icon { color: '{{primary-color}}'; }md-tabs.md-THEME_NAME-theme md-ink-bar { color: '{{accent-color}}'; background: '{{accent-color}}'; }md-tabs.md-THEME_NAME-theme .md-tab { color: '{{foreground-2}}'; } md-tabs.md-THEME_NAME-theme .md-tab[disabled] { color: '{{foreground-3}}'; } md-tabs.md-THEME_NAME-theme .md-tab.md-active, md-tabs.md-THEME_NAME-theme .md-tab.md-focus { color: '{{primary-color}}'; } md-tabs.md-THEME_NAME-theme .md-tab.md-focus { background: '{{primary-color-0.1}}'; } md-tabs.md-THEME_NAME-theme .md-tab .md-ripple-container { color: '{{accent-100}}'; }md-tabs.md-THEME_NAME-theme.md-accent md-tabs-wrapper { background-color: '{{accent-color}}'; }md-tabs.md-THEME_NAME-theme.md-accent md-tab:not([disabled]) { color: '{{accent-100}}'; } md-tabs.md-THEME_NAME-theme.md-accent md-tab:not([disabled]).md-active, md-tabs.md-THEME_NAME-theme.md-accent md-tab:not([disabled]).md-focus { color: '{{accent-contrast}}'; } md-tabs.md-THEME_NAME-theme.md-accent md-tab:not([disabled]).md-focus { background: '{{accent-contrast-0.1}}'; }md-tabs.md-THEME_NAME-theme.md-accent md-ink-bar { color: '{{primary-600-1}}'; background: '{{primary-600-1}}'; }md-tabs.md-THEME_NAME-theme.md-primary md-tabs-wrapper { background-color: '{{primary-color}}'; }md-tabs.md-THEME_NAME-theme.md-primary md-tab:not([disabled]) { color: '{{primary-100}}'; } md-tabs.md-THEME_NAME-theme.md-primary md-tab:not([disabled]).md-active, md-tabs.md-THEME_NAME-theme.md-primary md-tab:not([disabled]).md-focus { color: '{{primary-contrast}}'; } md-tabs.md-THEME_NAME-theme.md-primary md-tab:not([disabled]).md-focus { background: '{{primary-contrast-0.1}}'; }md-tabs.md-THEME_NAME-theme.md-warn md-tabs-wrapper { background-color: '{{warn-color}}'; }md-tabs.md-THEME_NAME-theme.md-warn md-tab:not([disabled]) { color: '{{warn-100}}'; } md-tabs.md-THEME_NAME-theme.md-warn md-tab:not([disabled]).md-active, md-tabs.md-THEME_NAME-theme.md-warn md-tab:not([disabled]).md-focus { color: '{{warn-contrast}}'; } md-tabs.md-THEME_NAME-theme.md-warn md-tab:not([disabled]).md-focus { background: '{{warn-contrast-0.1}}'; }md-input-group.md-THEME_NAME-theme input, md-input-group.md-THEME_NAME-theme textarea { text-shadow: '{{foreground-shadow}}'; } md-input-group.md-THEME_NAME-theme input::-webkit-input-placeholder, md-input-group.md-THEME_NAME-theme input::-moz-placeholder, md-input-group.md-THEME_NAME-theme input:-moz-placeholder, md-input-group.md-THEME_NAME-theme input:-ms-input-placeholder, md-input-group.md-THEME_NAME-theme textarea::-webkit-input-placeholder, md-input-group.md-THEME_NAME-theme textarea::-moz-placeholder, md-input-group.md-THEME_NAME-theme textarea:-moz-placeholder, md-input-group.md-THEME_NAME-theme textarea:-ms-input-placeholder { color: '{{foreground-3}}'; }md-input-group.md-THEME_NAME-theme label { text-shadow: '{{foreground-shadow}}'; color: '{{foreground-3}}'; }md-input-group.md-THEME_NAME-theme input, md-input-group.md-THEME_NAME-theme textarea { color: '{{foreground-1}}'; border-color: '{{foreground-4}}'; }md-input-group.md-THEME_NAME-theme.md-input-focused input, md-input-group.md-THEME_NAME-theme.md-input-focused textarea { border-color: '{{primary-500}}'; }md-input-group.md-THEME_NAME-theme.md-input-focused label { color: '{{primary-500}}'; }md-input-group.md-THEME_NAME-theme.md-input-focused.md-accent input, md-input-group.md-THEME_NAME-theme.md-input-focused.md-accent textarea { border-color: '{{accent-500}}'; }md-input-group.md-THEME_NAME-theme.md-input-focused.md-accent label { color: '{{accent-500}}'; }md-input-group.md-THEME_NAME-theme.md-input-has-value:not(.md-input-focused) label { color: '{{foreground-2}}'; }md-input-group.md-THEME_NAME-theme .md-input[disabled] { border-bottom-color: '{{foreground-4}}'; color: '{{foreground-3}}'; }md-toast.md-THEME_NAME-theme { background-color: '{{foreground-1}}'; color: '{{background-50}}'; } md-toast.md-THEME_NAME-theme .md-button { color: '{{background-50}}'; } md-toast.md-THEME_NAME-theme .md-button.md-highlight { color: '{{primary-A200}}'; } md-toast.md-THEME_NAME-theme .md-button.md-highlight.md-accent { color: '{{accent-A200}}'; } md-toast.md-THEME_NAME-theme .md-button.md-highlight.md-warn { color: '{{warn-A200}}'; }md-toolbar.md-THEME_NAME-theme { background-color: '{{primary-color}}'; color: '{{primary-contrast}}'; } md-toolbar.md-THEME_NAME-theme .md-button { color: '{{primary-contrast}}'; } md-toolbar.md-THEME_NAME-theme.md-accent { background-color: '{{accent-color}}'; color: '{{accent-contrast}}'; } md-toolbar.md-THEME_NAME-theme.md-warn { background-color: '{{warn-color}}'; color: '{{warn-contrast}}'; }md-tooltip.md-THEME_NAME-theme { color: '{{background-A100}}'; } md-tooltip.md-THEME_NAME-theme .md-background { background-color: '{{foreground-2}}'; }");
12024
+ })();