diff --git a/UI/WebServerResources/js/vendor/angular-material.js b/UI/WebServerResources/js/vendor/angular-material.js index e53b71f6a..2075c58c2 100644 --- a/UI/WebServerResources/js/vendor/angular-material.js +++ b/UI/WebServerResources/js/vendor/angular-material.js @@ -2,7 +2,7 @@ * Angular Material Design * https://github.com/angular/material * @license MIT - * v1.1.0-rc2-master-0fc0f3e + * v1.0.5-master-a0c066c */ (function( window, angular, undefined ){ "use strict"; @@ -779,35 +779,6 @@ function UtilFactory($document, $timeout, $compile, $rootScope, $$mdAnimate, $in return new Date().getTime(); }, - /** - * Bi-directional accessor/mutator used to easily update an element's - * property based on the current 'dir'ectional value. - */ - bidi : function(element, property, lValue, rValue) { - var ltr = !($document[0].dir == 'rtl' || $document[0].body.dir == 'rtl'); - - // If accessor - if ( arguments.length == 0 ) return ltr ? 'ltr' : 'rtl'; - - // If mutator - if ( ltr && angular.isDefined(lValue)) { - angular.element(element).css(property, validate(lValue)); - } - else if ( !ltr && angular.isDefined(rValue)) { - angular.element(element).css(property, validate(rValue) ); - } - - // Internal utils - - function validate(value) { - return !value ? '0' : - hasPx(value) ? value : value + 'px'; - } - function hasPx(value) { - return String(value).indexOf('px') > -1; - } - }, - clientRect: function(element, offsetParent, isOffsetRect) { var node = getNode(element); offsetParent = getNode(offsetParent || node.offsetParent || document.body); @@ -1391,17 +1362,17 @@ function UtilFactory($document, $timeout, $compile, $rootScope, $$mdAnimate, $in }, /** - * Parses an attribute value, mostly a string. - * By default checks for negated values and returns `false´ if present. - * Negated values are: (native falsy) and negative strings like: - * `false` or `0`. - * @param value Attribute value which should be parsed. - * @param negatedCheck When set to false, won't check for negated values. - * @returns {boolean} - */ - parseAttributeBoolean: function(value, negatedCheck) { - return value === '' || !!value && (negatedCheck === false || value !== 'false' && value !== '0'); - }, + * Parses an attribute value, mostly a string. + * By default checks for negated values and returns `false´ if present. + * Negated values are: (native falsy) and negative strings like: + * `false` or `0`. + * @param value Attribute value which should be parsed. + * @param negatedCheck When set to false, won't check for negated values. + * @returns {boolean} + */ + parseAttributeBoolean: function(value, negatedCheck) { + return value === '' || !!value && (negatedCheck === false || value !== 'false' && value !== '0'); + }, hasComputedStyle: hasComputedStyle }; @@ -4539,8 +4510,8 @@ angular.module('material.core.theming.palette', []) 'A400': '#00e676', 'A700': '#00c853', 'contrastDefaultColor': 'dark', - 'contrastLightColors': '500 600 700 800 900', - 'contrastStrongLightColors': '500 600 700' + 'contrastLightColors': '600 700 800 900', + 'contrastStrongLightColors': '600 700' }, 'light-green': { '50': '#f1f8e9', @@ -4682,9 +4653,10 @@ angular.module('material.core.theming.palette', []) '700': '#616161', '800': '#424242', '900': '#212121', + '1000': '#000000', 'A100': '#ffffff', - 'A200': '#000000', - 'A400': '#303030', + 'A200': '#eeeeee', + 'A400': '#bdbdbd', 'A700': '#616161', 'contrastDefaultColor': 'dark', 'contrastLightColors': '600 700 800 900' @@ -4705,8 +4677,8 @@ angular.module('material.core.theming.palette', []) 'A400': '#78909c', 'A700': '#455a64', 'contrastDefaultColor': 'light', - 'contrastDarkColors': '50 100 200 300 700', - 'contrastStrongLightColors': '400 500 700' + 'contrastDarkColors': '50 100 200 300', + 'contrastStrongLightColors': '400 500' } }); @@ -4783,14 +4755,14 @@ var DARK_FOREGROUND = { name: 'dark', '1': 'rgba(0,0,0,0.87)', '2': 'rgba(0,0,0,0.54)', - '3': 'rgba(0,0,0,0.38)', + '3': 'rgba(0,0,0,0.26)', '4': 'rgba(0,0,0,0.12)' }; var LIGHT_FOREGROUND = { name: 'light', '1': 'rgba(255,255,255,1.0)', '2': 'rgba(255,255,255,0.7)', - '3': 'rgba(255,255,255,0.5)', + '3': 'rgba(255,255,255,0.3)', '4': 'rgba(255,255,255,0.12)' }; @@ -4813,19 +4785,19 @@ var LIGHT_DEFAULT_HUES = { 'hue-3': 'A700' }, 'background': { - 'default': '50', - 'hue-1': 'A100', - 'hue-2': '100', - 'hue-3': '300' + 'default': 'A100', + 'hue-1': '300', + 'hue-2': '800', + 'hue-3': '900' } }; var DARK_DEFAULT_HUES = { 'background': { - 'default': 'A400', - 'hue-1': '800', - 'hue-2': '900', - 'hue-3': 'A200' + 'default': '800', + 'hue-1': '600', + 'hue-2': '300', + 'hue-3': '900' } }; THEME_COLOR_TYPES.forEach(function(colorType) { @@ -5271,7 +5243,7 @@ function generateAllThemes($injector) { // The user specifies a 'default' contrast color as either light or dark, // then explicitly lists which hues are the opposite contrast (eg. A100 has dark, A200 has light) - function sanitizePalette(palette, name) { + function sanitizePalette(palette) { var defaultContrast = palette.contrastDefaultColor; var lightColors = palette.contrastLightColors || []; var strongLightColors = palette.contrastStrongLightColors || []; @@ -5408,6 +5380,7 @@ function rgba(rgbArray, opacity) { 'rgb(' + rgbArray.join(',') + ')'; } + })(); (function(){ "use strict"; @@ -6451,7 +6424,6 @@ angular * the `md-primary` class. * * @param {boolean=} md-no-ink If present, disable ripple ink effects. - * @param {boolean=} md-no-focus-style If present, disable focus style on button * @param {expression=} ng-disabled En/Disable based on the expression * @param {string=} md-ripple-size Overrides the default ripple size logic. Options: `full`, `partial`, `auto` * @param {string=} aria-label Adds alternative text to button for accessibility, useful for icon buttons. @@ -6535,10 +6507,9 @@ function MdButtonDirective($mdButtonInkRipple, $mdTheming, $mdAria, $timeout) { } }); - if (!angular.isDefined(attr.mdNoFocusStyle)) { - // restrict focus styles to the keyboard - scope.mouseActive = false; - element.on('mousedown', function() { + // restrict focus styles to the keyboard + scope.mouseActive = false; + element.on('mousedown', function() { scope.mouseActive = true; $timeout(function(){ scope.mouseActive = false; @@ -6552,7 +6523,6 @@ function MdButtonDirective($mdButtonInkRipple, $mdTheming, $mdAria, $timeout) { .on('blur', function(ev) { element.removeClass('md-focused'); }); - } } } @@ -6726,12 +6696,7 @@ angular * @param {string=} ng-change Angular expression to be executed when input changes due to user interaction with the input element. * @param {boolean=} md-no-ink Use of attribute indicates use of ripple ink effects * @param {string=} aria-label Adds label to checkbox for accessibility. - * Defaults to checkbox's text. If no default text is found, a warning will be logged. - * @param {expression=} md-indeterminate This determines when the checkbox should be rendered as 'indeterminate'. - * If a truthy expression or no value is passed in the checkbox renders in the md-indeterminate state. - * If falsy expression is passed in it just looks like a normal unchecked checkbox. - * The indeterminate, checked, and unchecked states are mutually exclusive. A box cannot be in any two states at the same time. - * When a checkbox is indeterminate that overrides any checked/unchecked rendering logic. + * Defaults to checkbox's text. If no default text is found, a warning will be logged. * * @usage * @@ -6759,11 +6724,11 @@ function MdCheckboxDirective(inputDirective, $mdAria, $mdConstant, $mdTheming, $ transclude: true, require: '?ngModel', priority: 210, // Run before ngAria - template: - '
' + - '
' + + template: + '
' + + '
' + '
' + - '
', + '
', compile: compile }; @@ -6773,7 +6738,6 @@ function MdCheckboxDirective(inputDirective, $mdAria, $mdConstant, $mdTheming, $ function compile (tElement, tAttrs) { var container = tElement.children(); - var mdIndeterminateStateEnabled = tAttrs.hasOwnProperty('mdIndeterminate'); tAttrs.type = 'checkbox'; tAttrs.tabindex = tAttrs.tabindex || '0'; @@ -6794,13 +6758,8 @@ function MdCheckboxDirective(inputDirective, $mdAria, $mdConstant, $mdTheming, $ }); return function postLink(scope, element, attr, ngModelCtrl) { - var isIndeterminate; ngModelCtrl = ngModelCtrl || $mdUtil.fakeNgModel(); $mdTheming(element); - if (mdIndeterminateStateEnabled) { - setIndeterminateState(); - scope.$watch(attr.mdIndeterminate, setIndeterminateState); - } if (attr.ngChecked) { scope.$watch( @@ -6866,7 +6825,6 @@ function MdCheckboxDirective(inputDirective, $mdAria, $mdConstant, $mdTheming, $ listener(ev); } } - function listener(ev) { if (element[0].hasAttribute('disabled')) { return; @@ -6882,20 +6840,12 @@ function MdCheckboxDirective(inputDirective, $mdAria, $mdConstant, $mdTheming, $ } function render() { - if(ngModelCtrl.$viewValue && !isIndeterminate) { + if(ngModelCtrl.$viewValue) { element.addClass(CHECKED_CSS); } else { element.removeClass(CHECKED_CSS); } } - - function setIndeterminateState(newValue) { - isIndeterminate = newValue !== false; - if (isIndeterminate) { - element.attr('aria-checked', 'mixed'); - } - element.toggleClass('md-indeterminate', isIndeterminate); - } }; } } @@ -6941,23 +6891,11 @@ angular.module('material.components.content', [ * @restrict E * * @description - * - * The `` directive is a container element useful for scrollable content. It achieves - * this by setting the CSS `overflow` property to `auto` so that content can properly scroll. - * - * In general, `` components are not designed to be nested inside one another. If - * possible, it is better to make them siblings. This often results in a better user experience as - * having nested scrollbars may confuse the user. - * - * ## Troubleshooting - * - * In some cases, you may wish to apply the `md-no-momentum` class to ensure that Safari's - * momentum scrolling is disabled. Momentum scrolling can cause flickering issues while scrolling - * SVG icons and some other components. + * The `` directive is a container element useful for scrollable content * * @usage * - * Add the `[layout-padding]` attribute to make the content padded. + * - Add the `[layout-padding]` attribute to make the content padded. * * * @@ -9524,9 +9462,9 @@ function MdDialogProvider($$interimElementProvider) { '', ' ', '

{{ dialog.title }}

', - '
', - '
', + '
', '

{{::dialog.mdTextContent}}

', '
', ' ', @@ -9874,7 +9812,7 @@ function MdDialogProvider($$interimElementProvider) { } if (options.hasBackdrop) { - options.backdrop = $mdUtil.createBackdrop(scope, "_md-dialog-backdrop md-opaque"); + options.backdrop = $mdUtil.createBackdrop(scope, "md-dialog-backdrop md-opaque"); $animate.enter(options.backdrop, options.parent); } @@ -9931,7 +9869,7 @@ function MdDialogProvider($$interimElementProvider) { // Set up elements before and after the dialog content to capture focus and // redirect back into the dialog. topFocusTrap = document.createElement('div'); - topFocusTrap.classList.add('_md-dialog-focus-trap'); + topFocusTrap.classList.add('md-dialog-focus-trap'); topFocusTrap.tabIndex = 0; bottomFocusTrap = topFocusTrap.cloneNode(false); @@ -10018,7 +9956,7 @@ function MdDialogProvider($$interimElementProvider) { var dialogEl = container.find('md-dialog'); var animator = $mdUtil.dom.animator; var buildTranslateToOrigin = animator.calculateZoomToOrigin; - var translateOptions = {transitionInClass: '_md-transition-in', transitionOutClass: '_md-transition-out'}; + var translateOptions = {transitionInClass: 'md-transition-in', transitionOutClass: 'md-transition-out'}; var from = animator.toTransformCss(buildTranslateToOrigin(dialogEl, options.openFrom || options.origin)); var to = animator.toTransformCss(""); // defaults to center display (or parent or $rootElement) @@ -10035,7 +9973,7 @@ function MdDialogProvider($$interimElementProvider) { if (options.closeTo) { // Using the opposite classes to create a close animation to the closeTo element - translateOptions = {transitionInClass: '_md-transition-out', transitionOutClass: '_md-transition-in'}; + translateOptions = {transitionInClass: 'md-transition-out', transitionOutClass: 'md-transition-in'}; from = to; to = animator.toTransformCss(buildTranslateToOrigin(dialogEl, options.closeTo)); @@ -10182,9 +10120,9 @@ MdDividerDirective.$inject = ["$mdTheming"]; 'use strict'; angular.module('material.components.fabShared', ['material.core']) - .controller('MdFabController', MdFabController); + .controller('FabController', FabController); - function MdFabController($scope, $element, $animate, $mdUtil, $mdConstant, $timeout) { + function FabController($scope, $element, $animate, $mdUtil, $mdConstant, $timeout) { var vm = this; // NOTE: We use async eval(s) below to avoid conflicts with any existing digest loops @@ -10224,7 +10162,7 @@ MdDividerDirective.$inject = ["$mdTheming"]; resetActionIndex(); // Add an animations waiting class so we know not to run - $element.addClass('_md-animations-waiting'); + $element.addClass('md-animations-waiting'); } function setupListeners() { @@ -10322,9 +10260,9 @@ MdDividerDirective.$inject = ["$mdTheming"]; // If the element is actually visible on the screen if ($element[0].scrollHeight > 0) { // Fire our animation - $animate.addClass($element, '_md-animations-ready').then(function() { + $animate.addClass($element, 'md-animations-ready').then(function() { // Remove the waiting class - $element.removeClass('_md-animations-waiting'); + $element.removeClass('md-animations-waiting'); }); } @@ -10473,7 +10411,7 @@ MdDividerDirective.$inject = ["$mdTheming"]; return $element.find('md-fab-actions'); } } - MdFabController.$inject = ["$scope", "$element", "$animate", "$mdUtil", "$mdConstant", "$timeout"]; + FabController.$inject = ["$scope", "$element", "$animate", "$mdUtil", "$mdConstant", "$timeout"]; })(); })(); @@ -10588,7 +10526,7 @@ MdDividerDirective.$inject = ["$mdTheming"]; }, bindToController: true, - controller: 'MdFabController', + controller: 'FabController', controllerAs: 'vm', link: FabSpeedDialLink @@ -10596,7 +10534,7 @@ MdDividerDirective.$inject = ["$mdTheming"]; function FabSpeedDialLink(scope, element) { // Prepend an element to hold our CSS variables so we can use them in the animations below - element.prepend('
'); + element.prepend('
'); } } @@ -10605,7 +10543,7 @@ MdDividerDirective.$inject = ["$mdTheming"]; function runAnimation(element) { // Don't run if we are still waiting and we are not ready - if (element.hasClass('_md-animations-waiting') && !element.hasClass('_md-animations-ready')) { + if (element.hasClass('md-animations-waiting') && !element.hasClass('md-animations-ready')) { return; } @@ -10617,7 +10555,7 @@ MdDividerDirective.$inject = ["$mdTheming"]; var triggerElement = el.querySelector('md-fab-trigger'); // Grab our element which stores CSS variables - var variablesElement = el.querySelector('._md-css-variables'); + var variablesElement = el.querySelector('.md-css-variables'); // Setup JS variables based on our CSS variables var startZIndex = parseInt(window.getComputedStyle(variablesElement).zIndex); @@ -10703,7 +10641,7 @@ MdDividerDirective.$inject = ["$mdTheming"]; var items = el.querySelectorAll('.md-fab-action-item'); // Grab our element which stores CSS variables - var variablesElement = el.querySelector('._md-css-variables'); + var variablesElement = el.querySelector('.md-css-variables'); // Setup JS variables based on our CSS variables var startZIndex = parseInt(window.getComputedStyle(variablesElement).zIndex); @@ -10817,8 +10755,8 @@ MdDividerDirective.$inject = ["$mdTheming"]; return { restrict: 'E', transclude: true, - template: '
' + - '
' + + template: '
' + + '
' + '
', scope: { @@ -10827,7 +10765,7 @@ MdDividerDirective.$inject = ["$mdTheming"]; }, bindToController: true, - controller: 'MdFabController', + controller: 'FabController', controllerAs: 'vm', link: link @@ -10839,7 +10777,7 @@ MdDividerDirective.$inject = ["$mdTheming"]; // Prepend the background element to the trigger's button element.find('md-fab-trigger').find('button') - .prepend('
'); + .prepend('
'); } } @@ -10855,7 +10793,7 @@ MdDividerDirective.$inject = ["$mdTheming"]; var ctrl = element.controller('mdFabToolbar'); // Grab the relevant child elements - var backgroundElement = el.querySelector('._md-fab-toolbar-background'); + var backgroundElement = el.querySelector('.md-fab-toolbar-background'); var triggerElement = el.querySelector('md-fab-trigger button'); var toolbarElement = el.querySelector('md-toolbar'); var iconElement = el.querySelector('md-fab-trigger button md-icon'); @@ -11832,17 +11770,6 @@ angular.module('material.components.input', [ * */ function mdInputContainerDirective($mdTheming, $parse) { - - var INPUT_TAGS = ['INPUT', 'TEXTAREA', 'SELECT', 'MD-SELECT']; - - var LEFT_SELECTORS = INPUT_TAGS.reduce(function(selectors, isel) { - return selectors.concat(['md-icon ~ ' + isel, '.md-icon ~ ' + isel]); - }, []).join(","); - - var RIGHT_SELECTORS = INPUT_TAGS.reduce(function(selectors, isel) { - return selectors.concat([isel + ' ~ md-icon', isel + ' ~ .md-icon']); - }, []).join(","); - ContainerCtrl.$inject = ["$scope", "$element", "$attrs", "$animate"]; return { restrict: 'E', @@ -11850,15 +11777,9 @@ function mdInputContainerDirective($mdTheming, $parse) { controller: ContainerCtrl }; - function postLink(scope, element) { + function postLink(scope, element, attr) { $mdTheming(element); - - // Check for both a left & right icon - var leftIcon = element[0].querySelector(LEFT_SELECTORS); - var rightIcon = element[0].querySelector(RIGHT_SELECTORS); - - if (leftIcon) { element.addClass('md-icon-left'); } - if (rightIcon) { element.addClass('md-icon-right'); } + if (element.find('md-icon').length) element.addClass('md-has-icon'); } function ContainerCtrl($scope, $element, $attrs, $animate) { @@ -11902,7 +11823,7 @@ function labelDirective() { restrict: 'E', require: '^?mdInputContainer', link: function(scope, element, attr, containerCtrl) { - if (!containerCtrl || attr.mdNoFloat || element.hasClass('_md-container-ignore')) return; + if (!containerCtrl || attr.mdNoFloat || element.hasClass('md-container-ignore')) return; containerCtrl.label = element; scope.$on('$destroy', function() { @@ -11926,15 +11847,12 @@ function labelDirective() { * specified, a character counter will be shown underneath the input.

* The purpose of **`md-maxlength`** is exactly to show the max length counter text. If you don't * want the counter text and only need "plain" validation, you can use the "simple" `ng-maxlength` - * or maxlength attributes.

- * **Note:** Only valid for text/string inputs (not numeric). - * + * or maxlength attributes. * @param {string=} aria-label Aria-label is required when no label is present. A warning message * will be logged in the console if not present. * @param {string=} placeholder An alternative approach to using aria-label when the label is not * PRESENT. The placeholder text is copied to the aria-label attribute. * @param md-no-autogrow {boolean=} When present, textareas will not grow automatically. - * @param md-no-asterisk {boolean=} When present, asterisk will not be appended to required inputs label * @param md-detect-hidden {boolean=} When present, textareas will be sized properly when they are * revealed after being hidden. This is off by default for performance reasons because it * guarantees a reflow every digest cycle. @@ -12022,7 +11940,7 @@ function labelDirective() { * */ -function inputTextareaDirective($mdUtil, $window, $mdAria, $timeout) { +function inputTextareaDirective($mdUtil, $window, $mdAria) { return { restrict: 'E', require: ['^?mdInputContainer', '?ngModel'], @@ -12035,8 +11953,6 @@ function inputTextareaDirective($mdUtil, $window, $mdAria, $timeout) { var hasNgModel = !!ctrls[1]; var ngModelCtrl = ctrls[1] || $mdUtil.fakeNgModel(); var isReadonly = angular.isDefined(attr.readonly); - var mdNoAsterisk = $mdUtil.parseAttributeBoolean(attr.mdNoAsterisk); - if (!containerCtrl) return; if (attr.type === 'hidden') { @@ -12047,8 +11963,6 @@ function inputTextareaDirective($mdUtil, $window, $mdAria, $timeout) { } containerCtrl.input = element; - setupAttributeWatchers(); - // Add an error spacer div after our input to provide space for the char counter and any ng-messages var errorsSpacer = angular.element('
'); element.after(errorsSpacer); @@ -12123,16 +12037,6 @@ function inputTextareaDirective($mdUtil, $window, $mdAria, $timeout) { return arg; } - function setupAttributeWatchers() { - if (containerCtrl.label) { - attr.$observe('required', function (value) { - // We don't need to parse the required value, it's always a boolean because of angular's - // required directive. - containerCtrl.label.toggleClass('md-required', value && !mdNoAsterisk); - }); - } - } - function inputCheckValue() { // An input's value counts if its length > 0, // or if the input's validity state says it has bad input (eg string in a number input) @@ -12140,81 +12044,84 @@ function inputTextareaDirective($mdUtil, $window, $mdAria, $timeout) { } function setupTextarea() { - if (attr.hasOwnProperty('mdNoAutogrow')) { + if (angular.isDefined(element.attr('md-no-autogrow'))) { return; } - // Can't check if height was or not explicity set, - // so rows attribute will take precedence if present - var minRows = attr.hasOwnProperty('rows') ? parseInt(attr.rows) : NaN; - var lineHeight = null; var node = element[0]; + var container = containerCtrl.element[0]; - // This timeout is necessary, because the browser needs a little bit - // of time to calculate the `clientHeight` and `scrollHeight`. - $timeout(function() { - $mdUtil.nextTick(growTextarea); - }, 10, false); + var min_rows = NaN; + var lineHeight = null; + // can't check if height was or not explicity set, + // so rows attribute will take precedence if present + if (node.hasAttribute('rows')) { + min_rows = parseInt(node.getAttribute('rows')); + } - // We can hook into Angular's pipeline, instead of registering a new listener. - // Note that we should use `$parsers`, as opposed to `$viewChangeListeners` which - // was used before, because `$viewChangeListeners` don't fire if the input is - // invalid. - if (hasNgModel) { - ngModelCtrl.$formatters.unshift(pipelineListener); - ngModelCtrl.$parsers.unshift(pipelineListener); + var onChangeTextarea = $mdUtil.debounce(growTextarea, 1); + + function pipelineListener(value) { + onChangeTextarea(); + return value; + } + + if (ngModelCtrl) { + ngModelCtrl.$formatters.push(pipelineListener); + ngModelCtrl.$viewChangeListeners.push(pipelineListener); } else { - // Note that it's safe to use the `input` event since we're not supporting IE9 and below. - element.on('input', growTextarea); + onChangeTextarea(); + } + element.on('keydown input', onChangeTextarea); + + if (isNaN(min_rows)) { + element.attr('rows', '1'); + + element.on('scroll', onScroll); } - if (!minRows) { - element - .attr('rows', 1) - .on('scroll', onScroll); - } - - angular.element($window).on('resize', growTextarea); + angular.element($window).on('resize', onChangeTextarea); scope.$on('$destroy', function() { - angular.element($window).off('resize', growTextarea); + angular.element($window).off('resize', onChangeTextarea); }); function growTextarea() { - // temporarily disables element's flex so its height 'runs free' - element - .addClass('md-no-flex') - .attr('rows', 1); + // sets the md-input-container height to avoid jumping around + container.style.height = container.offsetHeight + 'px'; + + // temporarily disables element's flex so its height 'runs free' + element.addClass('md-no-flex'); + + if (isNaN(min_rows)) { + node.style.height = "auto"; + node.scrollTop = 0; + var height = getHeight(); + if (height) node.style.height = height + 'px'; + } else { + node.setAttribute("rows", 1); - if (minRows) { if (!lineHeight) { - node.style.minHeight = 0; + node.style.minHeight = '0'; + lineHeight = element.prop('clientHeight'); + node.style.minHeight = null; } - var newRows = Math.round( Math.round(getHeight() / lineHeight) ); - var rowsToSet = Math.min(newRows, minRows); - - element - .css('height', lineHeight * rowsToSet + 'px') - .attr('rows', rowsToSet) - .toggleClass('_md-textarea-scrollable', newRows >= minRows); - - } else { - element.css('height', 'auto'); - node.scrollTop = 0; - var height = getHeight(); - if (height) element.css('height', height + 'px'); + var rows = Math.min(min_rows, Math.round(node.scrollHeight / lineHeight)); + node.setAttribute("rows", rows); + node.style.height = lineHeight * rows + "px"; } + // reset everything back to normal element.removeClass('md-no-flex'); + container.style.height = 'auto'; } function getHeight() { - var offsetHeight = node.offsetHeight; - var line = node.scrollHeight - offsetHeight; - return offsetHeight + (line > 0 ? line : 0); + var line = node.scrollHeight - node.offsetHeight; + return node.offsetHeight + (line > 0 ? line : 0); } function onScroll(e) { @@ -12225,13 +12132,8 @@ function inputTextareaDirective($mdUtil, $window, $mdAria, $timeout) { node.style.height = height + 'px'; } - function pipelineListener(value) { - growTextarea(); - return value; - } - // Attach a watcher to detect when the textarea gets shown. - if (attr.hasOwnProperty('mdDetectHidden')) { + if (angular.isDefined(element.attr('md-detect-hidden'))) { var handleHiddenChange = function() { var wasHidden = false; @@ -12257,7 +12159,7 @@ function inputTextareaDirective($mdUtil, $window, $mdAria, $timeout) { } } } -inputTextareaDirective.$inject = ["$mdUtil", "$window", "$mdAria", "$timeout"]; +inputTextareaDirective.$inject = ["$mdUtil", "$window", "$mdAria"]; function mdMaxlengthDirective($animate, $mdUtil) { return { @@ -12396,7 +12298,7 @@ placeholderDirective.$inject = ["$log"]; * * */ -function mdSelectOnFocusDirective($timeout) { +function mdSelectOnFocusDirective() { return { restrict: 'A', @@ -12406,44 +12308,18 @@ function mdSelectOnFocusDirective($timeout) { function postLink(scope, element, attr) { if (element[0].nodeName !== 'INPUT' && element[0].nodeName !== "TEXTAREA") return; - var preventMouseUp = false; - - element - .on('focus', onFocus) - .on('mouseup', onMouseUp); + element.on('focus', onFocus); scope.$on('$destroy', function() { - element - .off('focus', onFocus) - .off('mouseup', onMouseUp); + element.off('focus', onFocus); }); function onFocus() { - preventMouseUp = true; - - $timeout(function() { - // Use HTMLInputElement#select to fix firefox select issues. - // The debounce is here for Edge's sake, otherwise the selection doesn't work. - element[0].select(); - - // This should be reset from inside the `focus`, because the event might - // have originated from something different than a click, e.g. a keyboard event. - preventMouseUp = false; - }, 1, false); - } - - // Prevents the default action of the first `mouseup` after a focus. - // This is necessary, because browsers fire a `mouseup` right after the element - // has been focused. In some browsers (Firefox in particular) this can clear the - // selection. There are examples of the problem in issue #7487. - function onMouseUp(event) { - if (preventMouseUp) { - event.preventDefault(); - } + // Use HTMLInputElement#select to fix firefox select issues + element[0].select(); } } } -mdSelectOnFocusDirective.$inject = ["$timeout"]; var visibilityDirectives = ['ngIf', 'ngShow', 'ngHide', 'ngSwitchWhen', 'ngSwitchDefault']; function ngMessagesDirective() { @@ -12513,7 +12389,7 @@ function mdInputInvalidMessagesAnimation($q, $animateCss) { } // NOTE: We do not need the removeClass method, because the message ng-leave animation will fire - }; + } } mdInputInvalidMessagesAnimation.$inject = ["$q", "$animateCss"]; @@ -12632,8 +12508,10 @@ function getInputElement(element) { function getMessagesElement(element) { var input = getInputElement(element); + var selector = 'ng-messages,data-ng-messages,x-ng-messages,' + + '[ng-messages],[data-ng-messages],[x-ng-messages]'; - return angular.element(input[0].querySelector('.md-input-messages-animation')); + return angular.element(input[0].querySelector(selector)); } })(); @@ -12696,7 +12574,7 @@ mdListDirective.$inject = ["$mdTheming"]; * * @description * The `` directive is a container intended for row items in a `` container. - * The `md-2-line` and `md-3-line` classes can be added to a `` + * The `md-2-line` and `md-3-line` classes can be added to a `` * to increase the height with 22px and 40px respectively. * * ## CSS @@ -12731,12 +12609,10 @@ function mdListItemDirective($mdAria, $mdConstant, $mdUtil, $timeout) { restrict: 'E', controller: 'MdListController', compile: function(tEl, tAttrs) { - // Check for proxy controls (no ng-click on parent, and a control inside) - var secondaryItems = tEl[0].querySelectorAll('.md-secondary'); + var secondaryItem = tEl[0].querySelector('.md-secondary'); var hasProxiedElement; var proxyElement; - var itemContainer = tEl; tEl[0].setAttribute('role', 'listitem'); @@ -12752,10 +12628,10 @@ function mdListItemDirective($mdAria, $mdConstant, $mdUtil, $timeout) { if (hasProxiedElement) { wrapIn('div'); } else if (!tEl[0].querySelector('md-button:not(.md-secondary):not(.md-exclude)')) { - tEl.addClass('_md-no-proxy'); + tEl.addClass('md-no-proxy'); } } - wrapSecondaryItems(); + wrapSecondary(); setupToggleAria(); @@ -12775,75 +12651,46 @@ function mdListItemDirective($mdAria, $mdConstant, $mdUtil, $timeout) { } function wrapIn(type) { + var container; if (type == 'div') { - itemContainer = angular.element('
'); - itemContainer.append(tEl.contents()); - tEl.addClass('_md-proxy-focus'); + container = angular.element('
'); + container.append(tEl.contents()); + tEl.addClass('md-proxy-focus'); } else { - // Element which holds the default list-item content. - itemContainer = angular.element( - '
'+ - '
'+ - '
' - ); - - // Button which shows ripple and executes primary action. - var buttonWrap = angular.element( - '' - ); - - buttonWrap[0].setAttribute('aria-label', tEl[0].textContent); - copyAttributes(tEl[0], buttonWrap[0]); - - // Append the button wrap before our list-item content, because it will overlay in relative. - itemContainer.prepend(buttonWrap); - itemContainer.children().eq(1).append(tEl.contents()); - - tEl.addClass('_md-button-wrap'); + container = angular.element('
'); + copyAttributes(tEl[0], container[0]); + container.children().eq(0).append(tEl.contents()); } tEl[0].setAttribute('tabindex', '-1'); - tEl.append(itemContainer); + tEl.append(container); } - function wrapSecondaryItems() { - var secondaryItemsWrapper = angular.element('
'); - - angular.forEach(secondaryItems, function(secondaryItem) { - wrapSecondaryItem(secondaryItem, secondaryItemsWrapper); - }); - - // Since the secondary item container is static we need to fill the remaing space. - var spaceFiller = angular.element('
'); - itemContainer.append(spaceFiller); - - itemContainer.append(secondaryItemsWrapper); - } - - function wrapSecondaryItem(secondaryItem, container) { + function wrapSecondary() { if (secondaryItem && !isButton(secondaryItem) && secondaryItem.hasAttribute('ng-click')) { $mdAria.expect(secondaryItem, 'aria-label'); - var buttonWrapper = angular.element(''); + var buttonWrapper = angular.element(''); copyAttributes(secondaryItem, buttonWrapper[0]); secondaryItem.setAttribute('tabindex', '-1'); + secondaryItem.classList.remove('md-secondary'); buttonWrapper.append(secondaryItem); secondaryItem = buttonWrapper[0]; } - if (secondaryItem && (!hasClickEvent(secondaryItem) || (!tAttrs.ngClick && isProxiedElement(secondaryItem)))) { - // In this case we remove the secondary class, so we can identify it later, when we searching for the - // proxy items. - angular.element(secondaryItem).removeClass('md-secondary'); + // Check for a secondary item and move it outside + if ( secondaryItem && ( + secondaryItem.hasAttribute('ng-click') || + ( tAttrs.ngClick && + isProxiedElement(secondaryItem) ) + )) { + tEl.addClass('md-with-secondary'); + tEl.append(secondaryItem); } - - tEl.addClass('md-with-secondary'); - container.append(secondaryItem); } function copyAttributes(item, wrapper) { var copiedAttrs = ['ng-if', 'ng-click', 'aria-label', 'ng-disabled', - 'ui-sref', 'href', 'ng-href', 'ng-attr-ui-sref', 'ui-sref-opts']; - + 'ui-sref', 'href', 'ng-href', 'ng-attr-ui-sref']; angular.forEach(copiedAttrs, function(attr) { if (item.hasAttribute(attr)) { wrapper.setAttribute(attr, item.getAttribute(attr)); @@ -12862,28 +12709,18 @@ function mdListItemDirective($mdAria, $mdConstant, $mdUtil, $timeout) { return nodeName == "MD-BUTTON" || nodeName == "BUTTON"; } - function hasClickEvent (element) { - var attr = element.attributes; - for (var i = 0; i < attr.length; i++) { - if (tAttrs.$normalize(attr[i].name) === 'ngClick') return true; - } - return false; - } - return postLink; function postLink($scope, $element, $attr, ctrl) { - var proxies = [], - firstElement = $element[0].firstElementChild, - isButtonWrap = $element.hasClass('_md-button-wrap'), - clickChild = isButtonWrap ? firstElement.firstElementChild : firstElement, - hasClick = clickChild && hasClickEvent(clickChild); + var proxies = [], + firstChild = $element[0].firstElementChild, + hasClick = firstChild && hasClickEvent(firstChild); computeProxies(); computeClickable(); - if ($element.hasClass('_md-proxy-focus') && proxies.length) { + if ($element.hasClass('md-proxy-focus') && proxies.length) { angular.forEach(proxies, function(proxy) { proxy = angular.element(proxy); @@ -12904,19 +12741,22 @@ function mdListItemDirective($mdAria, $mdConstant, $mdUtil, $timeout) { }); } + function hasClickEvent (element) { + var attr = element.attributes; + for (var i = 0; i < attr.length; i++) { + if ($attr.$normalize(attr[i].name) === 'ngClick') return true; + } + return false; + } function computeProxies() { - if (firstElement && firstElement.children && !hasClick) { - + var children = $element.children(); + if (children.length && !children[0].hasAttribute('ng-click')) { angular.forEach(proxiedTypes, function(type) { - - // All elements which are not capable for being used a proxy have the .md-secondary class - // applied. These items had been sorted out in the secondary wrap function. - angular.forEach(firstElement.querySelectorAll(type + ':not(.md-secondary)'), function(child) { + angular.forEach(firstChild.querySelectorAll(type), function(child) { proxies.push(child); }); }); - } } function computeClickable() { @@ -12924,17 +12764,17 @@ function mdListItemDirective($mdAria, $mdConstant, $mdUtil, $timeout) { $element.addClass('md-clickable'); if (!hasClick) { - ctrl.attachRipple($scope, angular.element($element[0].querySelector('._md-no-style'))); + ctrl.attachRipple($scope, angular.element($element[0].querySelector('.md-no-style'))); } } } - var clickChildKeypressListener = function(e) { + var firstChildKeypressListener = function(e) { if (e.target.nodeName != 'INPUT' && e.target.nodeName != 'TEXTAREA' && !e.target.isContentEditable) { var keyCode = e.which || e.keyCode; if (keyCode == $mdConstant.KEY_CODE.SPACE) { - if (clickChild) { - clickChild.click(); + if (firstChild) { + firstChild.click(); e.preventDefault(); e.stopPropagation(); } @@ -12943,16 +12783,16 @@ function mdListItemDirective($mdAria, $mdConstant, $mdUtil, $timeout) { }; if (!hasClick && !proxies.length) { - clickChild && clickChild.addEventListener('keypress', clickChildKeypressListener); + firstChild && firstChild.addEventListener('keypress', firstChildKeypressListener); } $element.off('click'); $element.off('keypress'); - if (proxies.length == 1 && clickChild) { + if (proxies.length == 1 && firstChild) { $element.children().eq(0).on('click', function(e) { var parentButton = $mdUtil.getClosest(e.target, 'BUTTON'); - if (!parentButton && clickChild.contains(e.target)) { + if (!parentButton && firstChild.contains(e.target)) { angular.forEach(proxies, function(proxy) { if (e.target !== proxy && !proxy.contains(e.target)) { angular.element(proxy).triggerHandler('click'); @@ -12963,7 +12803,7 @@ function mdListItemDirective($mdAria, $mdConstant, $mdUtil, $timeout) { } $scope.$on('$destroy', function () { - clickChild && clickChild.removeEventListener('keypress', clickChildKeypressListener); + firstChild && firstChild.removeEventListener('keypress', firstChildKeypressListener); }); } } @@ -13025,10 +12865,261 @@ angular.module('material.components.menuBar', [ /** * @ngdoc module * @name material.components.progressCircular - * @description Module for a circular progressbar + * @description Circular Progress module! */ +angular.module('material.components.progressCircular', [ + 'material.core' +]) + .directive('mdProgressCircular', MdProgressCircularDirective); -angular.module('material.components.progressCircular', ['material.core']); +/** + * @ngdoc directive + * @name mdProgressCircular + * @module material.components.progressCircular + * @restrict E + * +* @description + * 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. + * + * 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. + * + * 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. + * + * @param {string} md-mode Select from one of two modes: **'determinate'** and **'indeterminate'**. + * + * Note: if the `md-mode` value is set as undefined or specified as not 1 of the two (2) valid modes, then `.ng-hide` + * will be auto-applied as a style to the component. + * + * Note: if not configured, the `md-mode="indeterminate"` will be auto injected as an attribute. + * If `value=""` is also specified, however, then `md-mode="determinate"` would be auto-injected instead. + * @param {number=} value In determinate mode, this number represents the percentage of the + * circular progress. Default: 0 + * @param {number=} md-diameter This specifies the diameter of the circular progress. The value + * may be a percentage (eg '25%') or a pixel-size value (eg '48'). If this attribute is + * not present then a default value of '48px' is assumed. + * + * @usage + * + * + * + * + * + * + * + * + * + */ +function MdProgressCircularDirective($mdTheming, $mdUtil, $log) { + var DEFAULT_PROGRESS_SIZE = 100; + var DEFAULT_SCALING = 0.5; + + var MODE_DETERMINATE = "determinate", + MODE_INDETERMINATE = "indeterminate"; + + + return { + restrict: 'E', + scope : true, + template: + // The progress 'circle' is composed of two half-circles: the left side and the right + // side. Each side has CSS applied to 'fill-in' the half-circle to the appropriate progress. + '
' + + '
' + + '
' + + '
' + + '
' + + '
' + + '
' + + '
' + + '
' + + '
' + + '
' + + '
' + + '
', + compile: compile + }; + + function compile(tElement) { + // The javascript in this file is mainly responsible for setting the correct aria attributes. + // The animation of the progress spinner is done entirely with just CSS. + tElement.attr('aria-valuemin', 0); + tElement.attr('aria-valuemax', 100); + tElement.attr('role', 'progressbar'); + + return postLink; + } + + function postLink(scope, element, attr) { + $mdTheming(element); + + var circle = element; + var spinnerWrapper = angular.element(element.children()[0]); + var lastMode, toVendorCSS = $mdUtil.dom.animator.toCss; + + element.attr('md-mode', mode()); + + updateScale(); + validateMode(); + watchAttributes(); + + /** + * Watch the value and md-mode attributes + */ + function watchAttributes() { + attr.$observe('value', function(value) { + var percentValue = clamp(value); + element.attr('aria-valuenow', percentValue); + + if (mode() == MODE_DETERMINATE) { + animateIndicator(percentValue); + } + }); + attr.$observe('mdMode',function(mode){ + switch( mode ) { + case MODE_DETERMINATE: + case MODE_INDETERMINATE: + spinnerWrapper.removeClass('ng-hide'); + if (lastMode) spinnerWrapper.removeClass(lastMode); + spinnerWrapper.addClass( lastMode = "md-mode-" + mode ); + break; + default: + if (lastMode) spinnerWrapper.removeClass( lastMode ); + spinnerWrapper.addClass('ng-hide'); + lastMode = undefined; + break; + } + }); + } + + /** + * Update size/scaling of the progress indicator + * Watch the "value" and "md-mode" attributes + */ + function updateScale() { + // set the outer container to the size the user specified + circle.css({ + width: (100 * getDiameterRatio()) + 'px', + height: (100 * getDiameterRatio()) + 'px' + }); + // the internal element is still 100px, so we have to scale it down to match the size + circle.children().eq(0).css(toVendorCSS({ + transform : $mdUtil.supplant('translate(-50%, -50%) scale( {0} )',[getDiameterRatio()]) + })); + } + + /** + * Auto-defaults the mode to either `determinate` or `indeterminate` mode; if not specified + */ + function validateMode() { + if ( angular.isUndefined(attr.mdMode) ) { + var hasValue = angular.isDefined(attr.value); + var mode = hasValue ? MODE_DETERMINATE : MODE_INDETERMINATE; + var info = "Auto-adding the missing md-mode='{0}' to the ProgressCircular element"; + + $log.debug( $mdUtil.supplant(info, [mode]) ); + + element.attr("md-mode",mode); + attr['mdMode'] = mode; + } + } + + var leftC, rightC, gap; + + /** + * Manually animate the Determinate indicator based on the specified + * percentage value (0-100). + * + * Note: this animation was previously done using SCSS. + * - generated 54K of styles + * - use attribute selectors which had poor performances in IE + */ + function animateIndicator(value) { + if ( !mode() ) return; + + leftC = leftC || angular.element(element[0].querySelector('.md-left > .md-half-circle')); + rightC = rightC || angular.element(element[0].querySelector('.md-right > .md-half-circle')); + gap = gap || angular.element(element[0].querySelector('.md-gap')); + + var gapStyles = removeEmptyValues({ + borderBottomColor: (value <= 50) ? "transparent !important" : "", + transition: (value <= 50) ? "" : "borderBottomColor 0.1s linear" + }), + leftStyles = removeEmptyValues({ + transition: (value <= 50) ? "transform 0.1s linear" : "", + transform: $mdUtil.supplant("rotate({0}deg)", [value <= 50 ? 135 : (((value - 50) / 50 * 180) + 135)]) + }), + rightStyles = removeEmptyValues({ + transition: (value >= 50) ? "transform 0.1s linear" : "", + transform: $mdUtil.supplant("rotate({0}deg)", [value >= 50 ? 45 : (value / 50 * 180 - 135)]) + }); + + leftC.css(toVendorCSS(leftStyles)); + rightC.css(toVendorCSS(rightStyles)); + gap.css(toVendorCSS(gapStyles)); + + } + + /** + * We will scale the progress circle based on the default diameter. + * + * Determine the diameter percentage (defaults to 100%) + * May be express as float, percentage, or integer + */ + function getDiameterRatio() { + if ( !attr.mdDiameter ) return DEFAULT_SCALING; + + var match = /([0-9]*)%/.exec(attr.mdDiameter); + var value = Math.max(0, (match && match[1]/100) || parseFloat(attr.mdDiameter)); + + // should return ratio; DEFAULT_PROGRESS_SIZE === 100px is default size + return (value > 1) ? value / DEFAULT_PROGRESS_SIZE : value; + } + + /** + * Is the md-mode a valid option? + */ + function mode() { + var value = (attr.mdMode || "").trim(); + if ( value ) { + switch(value) { + case MODE_DETERMINATE : + case MODE_INDETERMINATE : + break; + default: + value = undefined; + break; + } + } + return value; + } + + } + + /** + * Clamps the value to be between 0 and 100. + * @param {number} value The value to clamp. + * @returns {number} + */ + function clamp(value) { + return Math.max(0, Math.min(value || 0, 100)); + } + + function removeEmptyValues(target) { + for (var key in target) { + if (target.hasOwnProperty(key)) { + if ( target[key] == "" ) delete target[key]; + } + } + + return target; + } +} +MdProgressCircularDirective.$inject = ["$mdTheming", "$mdUtil", "$log"]; })(); (function(){ @@ -13071,8 +13162,8 @@ angular.module('material.components.progressLinear', [ * * @param {string} md-mode Select from one of four modes: determinate, indeterminate, buffer or query. * - * Note: if the `md-mode` value is set as undefined or specified as 1 of the four (4) valid modes, then `indeterminate` - * will be auto-applied as the mode. + * Note: if the `md-mode` value is set as undefined or specified as 1 of the four (4) valid modes, then `.ng-hide` + * will be auto-applied as a style to the component. * * Note: if not configured, the `md-mode="indeterminate"` will be auto injected as an attribute. If `value=""` is also specified, however, * then `md-mode="determinate"` would be auto-injected instead. @@ -13100,10 +13191,10 @@ function MdProgressLinearDirective($mdTheming, $mdUtil, $log) { return { restrict: 'E', - template: '
' + - '
' + - '
' + - '
' + + template: '
' + + '
' + + '
' + + '
' + '
', compile: compile }; @@ -13119,9 +13210,9 @@ function MdProgressLinearDirective($mdTheming, $mdUtil, $log) { $mdTheming(element); var lastMode, toVendorCSS = $mdUtil.dom.animator.toCss; - var bar1 = angular.element(element[0].querySelector('._md-bar1')), - bar2 = angular.element(element[0].querySelector('._md-bar2')), - container = angular.element(element[0].querySelector('._md-container')); + var bar1 = angular.element(element[0].querySelector('.md-bar1')), + bar2 = angular.element(element[0].querySelector('.md-bar2')), + container = angular.element(element[0].querySelector('.md-container')); element.attr('md-mode', mode()); @@ -13144,17 +13235,18 @@ function MdProgressLinearDirective($mdTheming, $mdUtil, $log) { }); attr.$observe('mdMode',function(mode){ - if (lastMode) container.removeClass( lastMode ); - switch( mode ) { case MODE_QUERY: case MODE_BUFFER: case MODE_DETERMINATE: case MODE_INDETERMINATE: - container.addClass( lastMode = "_md-mode-" + mode ); + container.removeClass( 'ng-hide' + ' ' + lastMode ); + container.addClass( lastMode = "md-mode-" + mode ); break; default: - container.addClass( lastMode = "_md-mode-" + MODE_INDETERMINATE ); + if (lastMode) container.removeClass( lastMode ); + container.addClass('ng-hide'); + lastMode = undefined; break; } }); @@ -13189,7 +13281,7 @@ function MdProgressLinearDirective($mdTheming, $mdUtil, $log) { case MODE_QUERY: break; default: - value = MODE_INDETERMINATE; + value = undefined; break; } } @@ -13475,11 +13567,11 @@ function mdRadioButtonDirective($mdAria, $mdUtil, $mdTheming) { restrict: 'E', require: '^mdRadioGroup', transclude: true, - template: '
' + - '
' + - '
' + + template: '
' + + '
' + + '
' + '
' + - '
', + '
', link: link }; @@ -13600,8 +13692,6 @@ mdRadioButtonDirective.$inject = ["$mdAria", "$mdUtil", "$mdTheming"]; var SELECT_EDGE_MARGIN = 8; var selectNextId = 0; -var CHECKBOX_SELECTION_INDICATOR = - angular.element('
'); angular.module('material.components.select', [ 'material.core', @@ -13626,12 +13716,10 @@ angular.module('material.components.select', [ * @param {expression=} md-on-close Expression to be evaluated when the select is closed. * @param {expression=} md-on-open Expression to be evaluated when opening the select. * Will hide the select options and show a spinner until the evaluated promise resolves. - * @param {expression=} md-selected-text Expression to be evaluated that will return a string - * to be displayed as a placeholder in the select input box when it is closed. * @param {string=} placeholder Placeholder hint text. * @param {string=} aria-label Optional label for accessibility. Only necessary if no placeholder or * explicit label is present. - * @param {string=} md-container-class Class list to get applied to the `._md-select-menu-container` + * @param {string=} md-container-class Class list to get applied to the `.md-select-menu-container` * element (for custom styling). * * @usage @@ -13715,8 +13803,8 @@ function SelectDirective($mdSelect, $mdUtil, $mdTheming, $mdAria, $compile, $par function compile(element, attr) { // add the select value that will hold our placeholder or selected option value var valueEl = angular.element(''); - valueEl.append(''); - valueEl.addClass('_md-select-value'); + valueEl.append(''); + valueEl.addClass('md-select-value'); if (!valueEl[0].hasAttribute('id')) { valueEl.attr('id', 'select_value_label_' + $mdUtil.nextUid()); } @@ -13747,7 +13835,7 @@ function SelectDirective($mdSelect, $mdUtil, $mdTheming, $mdAria, $compile, $par } if (attr.name) { - var autofillClone = angular.element(''); autofillClone.attr({ 'name': '.' + attr.name, 'ng-model': attr.ngModel, @@ -13765,16 +13853,14 @@ function SelectDirective($mdSelect, $mdUtil, $mdTheming, $mdAria, $compile, $par element.parent().append(autofillClone); } - var isMultiple = $mdUtil.parseAttributeBoolean(attr.multiple); - // Use everything that's left inside element.contents() as the contents of the menu - var multipleContent = isMultiple ? 'multiple' : ''; + var multiple = angular.isDefined(attr.multiple) ? 'multiple' : ''; var selectTemplate = '' + - '