commonroutesApp/www/lib/angular-material/modules/js/core/core.js
www/lib/angular-material/modules/js/core/core.jsRaw
1/*!
2 * Angular Material Design
3 * https://github.com/angular/material
4 * @license MIT
5 * v1.1.1
6 */
7(function( window, angular, undefined ){
8"use strict";
9
10/**
11 * Initialization function that validates environment
12 * requirements.
13 */
14DetectNgTouch.$inject = ["$log", "$injector"];
15MdCoreConfigure.$inject = ["$provide", "$mdThemingProvider"];
16rAFDecorator.$inject = ["$delegate"];
17angular
18 .module('material.core', [
19 'ngAnimate',
20 'material.core.animate',
21 'material.core.layout',
22 'material.core.gestures',
23 'material.core.theming'
24 ])
25 .config(MdCoreConfigure)
26 .run(DetectNgTouch);
27
28
29/**
30 * Detect if the ng-Touch module is also being used.
31 * Warn if detected.
32 * ngInject
33 */
34function DetectNgTouch($log, $injector) {
35 if ( $injector.has('$swipe') ) {
36 var msg = "" +
37 "You are using the ngTouch module. \n" +
38 "Angular Material already has mobile click, tap, and swipe support... \n" +
39 "ngTouch is not supported with Angular Material!";
40 $log.warn(msg);
41 }
42}
43
44/**
45 * ngInject
46 */
47function MdCoreConfigure($provide, $mdThemingProvider) {
48
49 $provide.decorator('$$rAF', ["$delegate", rAFDecorator]);
50
51 $mdThemingProvider.theme('default')
52 .primaryPalette('indigo')
53 .accentPalette('pink')
54 .warnPalette('deep-orange')
55 .backgroundPalette('grey');
56}
57
58/**
59 * ngInject
60 */
61function rAFDecorator($delegate) {
62 /**
63 * Use this to throttle events that come in often.
64 * The throttled function will always use the *last* invocation before the
65 * coming frame.
66 *
67 * For example, window resize events that fire many times a second:
68 * If we set to use an raf-throttled callback on window resize, then
69 * our callback will only be fired once per frame, with the last resize
70 * event that happened before that frame.
71 *
72 * @param {function} callback function to debounce
73 */
74 $delegate.throttle = function(cb) {
75 var queuedArgs, alreadyQueued, queueCb, context;
76 return function debounced() {
77 queuedArgs = arguments;
78 context = this;
79 queueCb = cb;
80 if (!alreadyQueued) {
81 alreadyQueued = true;
82 $delegate(function() {
83 queueCb.apply(context, Array.prototype.slice.call(queuedArgs));
84 alreadyQueued = false;
85 });
86 }
87 };
88 };
89 return $delegate;
90}
91
92angular.module('material.core')
93 .directive('mdAutofocus', MdAutofocusDirective)
94
95 // Support the deprecated md-auto-focus and md-sidenav-focus as well
96 .directive('mdAutoFocus', MdAutofocusDirective)
97 .directive('mdSidenavFocus', MdAutofocusDirective);
98
99/**
100 * @ngdoc directive
101 * @name mdAutofocus
102 * @module material.core.util
103 *
104 * @description
105 *
106 * `[md-autofocus]` provides an optional way to identify the focused element when a `$mdDialog`,
107 * `$mdBottomSheet`, `$mdMenu` or `$mdSidenav` opens or upon page load for input-like elements.
108 *
109 * When one of these opens, it will find the first nested element with the `[md-autofocus]`
110 * attribute directive and optional expression. An expression may be specified as the directive
111 * value to enable conditional activation of the autofocus.
112 *
113 * @usage
114 *
115 * ### Dialog
116 * <hljs lang="html">
117 * <md-dialog>
118 * <form>
119 * <md-input-container>
120 * <label for="testInput">Label</label>
121 * <input id="testInput" type="text" md-autofocus>
122 * </md-input-container>
123 * </form>
124 * </md-dialog>
125 * </hljs>
126 *
127 * ### Bottomsheet
128 * <hljs lang="html">
129 * <md-bottom-sheet class="md-list md-has-header">
130 * <md-subheader>Comment Actions</md-subheader>
131 * <md-list>
132 * <md-list-item ng-repeat="item in items">
133 *
134 * <md-button md-autofocus="$index == 2">
135 * <md-icon md-svg-src="{{item.icon}}"></md-icon>
136 * <span class="md-inline-list-icon-label">{{ item.name }}</span>
137 * </md-button>
138 *
139 * </md-list-item>
140 * </md-list>
141 * </md-bottom-sheet>
142 * </hljs>
143 *
144 * ### Autocomplete
145 * <hljs lang="html">
146 * <md-autocomplete
147 * md-autofocus
148 * md-selected-item="selectedItem"
149 * md-search-text="searchText"
150 * md-items="item in getMatches(searchText)"
151 * md-item-text="item.display">
152 * <span md-highlight-text="searchText">{{item.display}}</span>
153 * </md-autocomplete>
154 * </hljs>
155 *
156 * ### Sidenav
157 * <hljs lang="html">
158 * <div layout="row" ng-controller="MyController">
159 * <md-sidenav md-component-id="left" class="md-sidenav-left">
160 * Left Nav!
161 * </md-sidenav>
162 *
163 * <md-content>
164 * Center Content
165 * <md-button ng-click="openLeftMenu()">
166 * Open Left Menu
167 * </md-button>
168 * </md-content>
169 *
170 * <md-sidenav md-component-id="right"
171 * md-is-locked-open="$mdMedia('min-width: 333px')"
172 * class="md-sidenav-right">
173 * <form>
174 * <md-input-container>
175 * <label for="testInput">Test input</label>
176 * <input id="testInput" type="text"
177 * ng-model="data" md-autofocus>
178 * </md-input-container>
179 * </form>
180 * </md-sidenav>
181 * </div>
182 * </hljs>
183 **/
184function MdAutofocusDirective() {
185 return {
186 restrict: 'A',
187
188 link: postLink
189 }
190}
191
192function postLink(scope, element, attrs) {
193 var attr = attrs.mdAutoFocus || attrs.mdAutofocus || attrs.mdSidenavFocus;
194
195 // Setup a watcher on the proper attribute to update a class we can check for in $mdUtil
196 scope.$watch(attr, function(canAutofocus) {
197 element.toggleClass('md-autofocus', canAutofocus);
198 });
199}
200
201/**
202 * @ngdoc module
203 * @name material.core.colorUtil
204 * @description
205 * Color Util
206 */
207angular
208 .module('material.core')
209 .factory('$mdColorUtil', ColorUtilFactory);
210
211function ColorUtilFactory() {
212 /**
213 * Converts hex value to RGBA string
214 * @param color {string}
215 * @returns {string}
216 */
217 function hexToRgba (color) {
218 var hex = color[ 0 ] === '#' ? color.substr(1) : color,
219 dig = hex.length / 3,
220 red = hex.substr(0, dig),
221 green = hex.substr(dig, dig),
222 blue = hex.substr(dig * 2);
223 if (dig === 1) {
224 red += red;
225 green += green;
226 blue += blue;
227 }
228 return 'rgba(' + parseInt(red, 16) + ',' + parseInt(green, 16) + ',' + parseInt(blue, 16) + ',0.1)';
229 }
230
231 /**
232 * Converts rgba value to hex string
233 * @param color {string}
234 * @returns {string}
235 */
236 function rgbaToHex(color) {
237 color = color.match(/^rgba?[\s+]?\([\s+]?(\d+)[\s+]?,[\s+]?(\d+)[\s+]?,[\s+]?(\d+)[\s+]?/i);
238
239 var hex = (color && color.length === 4) ? "#" +
240 ("0" + parseInt(color[1],10).toString(16)).slice(-2) +
241 ("0" + parseInt(color[2],10).toString(16)).slice(-2) +
242 ("0" + parseInt(color[3],10).toString(16)).slice(-2) : '';
243
244 return hex.toUpperCase();
245 }
246
247 /**
248 * Converts an RGB color to RGBA
249 * @param color {string}
250 * @returns {string}
251 */
252 function rgbToRgba (color) {
253 return color.replace(')', ', 0.1)').replace('(', 'a(');
254 }
255
256 /**
257 * Converts an RGBA color to RGB
258 * @param color {string}
259 * @returns {string}
260 */
261 function rgbaToRgb (color) {
262 return color
263 ? color.replace('rgba', 'rgb').replace(/,[^\),]+\)/, ')')
264 : 'rgb(0,0,0)';
265 }
266
267 return {
268 rgbaToHex: rgbaToHex,
269 hexToRgba: hexToRgba,
270 rgbToRgba: rgbToRgba,
271 rgbaToRgb: rgbaToRgb
272 }
273}
274
275MdConstantFactory.$inject = ["$sniffer", "$window", "$document"];angular.module('material.core')
276.factory('$mdConstant', MdConstantFactory);
277
278/**
279 * Factory function that creates the grab-bag $mdConstant service.
280 * ngInject
281 */
282function MdConstantFactory($sniffer, $window, $document) {
283
284 var vendorPrefix = $sniffer.vendorPrefix;
285 var isWebkit = /webkit/i.test(vendorPrefix);
286 var SPECIAL_CHARS_REGEXP = /([:\-_]+(.))/g;
287 var prefixTestEl = document.createElement('div');
288
289 function vendorProperty(name) {
290 // Add a dash between the prefix and name, to be able to transform the string into camelcase.
291 var prefixedName = vendorPrefix + '-' + name;
292 var ucPrefix = camelCase(prefixedName);
293 var lcPrefix = ucPrefix.charAt(0).toLowerCase() + ucPrefix.substring(1);
294
295 return hasStyleProperty(name) ? name : // The current browser supports the un-prefixed property
296 hasStyleProperty(ucPrefix) ? ucPrefix : // The current browser only supports the prefixed property.
297 hasStyleProperty(lcPrefix) ? lcPrefix : name; // Some browsers are only supporting the prefix in lowercase.
298 }
299
300 function hasStyleProperty(property) {
301 return angular.isDefined(prefixTestEl.style[property]);
302 }
303
304 function camelCase(input) {
305 return input.replace(SPECIAL_CHARS_REGEXP, function(matches, separator, letter, offset) {
306 return offset ? letter.toUpperCase() : letter;
307 });
308 }
309
310 var self = {
311 isInputKey : function(e) { return (e.keyCode >= 31 && e.keyCode <= 90); },
312 isNumPadKey : function (e){ return (3 === e.location && e.keyCode >= 97 && e.keyCode <= 105); },
313 isNavigationKey : function(e) {
314 var kc = self.KEY_CODE, NAVIGATION_KEYS = [kc.SPACE, kc.ENTER, kc.UP_ARROW, kc.DOWN_ARROW];
315 return (NAVIGATION_KEYS.indexOf(e.keyCode) != -1);
316 },
317
318 KEY_CODE: {
319 COMMA: 188,
320 SEMICOLON : 186,
321 ENTER: 13,
322 ESCAPE: 27,
323 SPACE: 32,
324 PAGE_UP: 33,
325 PAGE_DOWN: 34,
326 END: 35,
327 HOME: 36,
328 LEFT_ARROW : 37,
329 UP_ARROW : 38,
330 RIGHT_ARROW : 39,
331 DOWN_ARROW : 40,
332 TAB : 9,
333 BACKSPACE: 8,
334 DELETE: 46
335 },
336 CSS: {
337 /* Constants */
338 TRANSITIONEND: 'transitionend' + (isWebkit ? ' webkitTransitionEnd' : ''),
339 ANIMATIONEND: 'animationend' + (isWebkit ? ' webkitAnimationEnd' : ''),
340
341 TRANSFORM: vendorProperty('transform'),
342 TRANSFORM_ORIGIN: vendorProperty('transformOrigin'),
343 TRANSITION: vendorProperty('transition'),
344 TRANSITION_DURATION: vendorProperty('transitionDuration'),
345 ANIMATION_PLAY_STATE: vendorProperty('animationPlayState'),
346 ANIMATION_DURATION: vendorProperty('animationDuration'),
347 ANIMATION_NAME: vendorProperty('animationName'),
348 ANIMATION_TIMING: vendorProperty('animationTimingFunction'),
349 ANIMATION_DIRECTION: vendorProperty('animationDirection')
350 },
351 /**
352 * As defined in core/style/variables.scss
353 *
354 * $layout-breakpoint-xs: 600px !default;
355 * $layout-breakpoint-sm: 960px !default;
356 * $layout-breakpoint-md: 1280px !default;
357 * $layout-breakpoint-lg: 1920px !default;
358 *
359 */
360 MEDIA: {
361 'xs' : '(max-width: 599px)' ,
362 'gt-xs' : '(min-width: 600px)' ,
363 'sm' : '(min-width: 600px) and (max-width: 959px)' ,
364 'gt-sm' : '(min-width: 960px)' ,
365 'md' : '(min-width: 960px) and (max-width: 1279px)' ,
366 'gt-md' : '(min-width: 1280px)' ,
367 'lg' : '(min-width: 1280px) and (max-width: 1919px)',
368 'gt-lg' : '(min-width: 1920px)' ,
369 'xl' : '(min-width: 1920px)' ,
370 'landscape' : '(orientation: landscape)' ,
371 'portrait' : '(orientation: portrait)' ,
372 'print' : 'print'
373 },
374 MEDIA_PRIORITY: [
375 'xl',
376 'gt-lg',
377 'lg',
378 'gt-md',
379 'md',
380 'gt-sm',
381 'sm',
382 'gt-xs',
383 'xs',
384 'landscape',
385 'portrait',
386 'print'
387 ]
388 };
389
390 return self;
391}
392
393 angular
394 .module('material.core')
395 .config( ["$provide", function($provide){
396 $provide.decorator('$mdUtil', ['$delegate', function ($delegate){
397 /**
398 * Inject the iterator facade to easily support iteration and accessors
399 * @see iterator below
400 */
401 $delegate.iterator = MdIterator;
402
403 return $delegate;
404 }
405 ]);
406 }]);
407
408 /**
409 * iterator is a list facade to easily support iteration and accessors
410 *
411 * @param items Array list which this iterator will enumerate
412 * @param reloop Boolean enables iterator to consider the list as an endless reloop
413 */
414 function MdIterator(items, reloop) {
415 var trueFn = function() { return true; };
416
417 if (items && !angular.isArray(items)) {
418 items = Array.prototype.slice.call(items);
419 }
420
421 reloop = !!reloop;
422 var _items = items || [ ];
423
424 // Published API
425 return {
426 items: getItems,
427 count: count,
428
429 inRange: inRange,
430 contains: contains,
431 indexOf: indexOf,
432 itemAt: itemAt,
433
434 findBy: findBy,
435
436 add: add,
437 remove: remove,
438
439 first: first,
440 last: last,
441 next: angular.bind(null, findSubsequentItem, false),
442 previous: angular.bind(null, findSubsequentItem, true),
443
444 hasPrevious: hasPrevious,
445 hasNext: hasNext
446
447 };
448
449 /**
450 * Publish copy of the enumerable set
451 * @returns {Array|*}
452 */
453 function getItems() {
454 return [].concat(_items);
455 }
456
457 /**
458 * Determine length of the list
459 * @returns {Array.length|*|number}
460 */
461 function count() {
462 return _items.length;
463 }
464
465 /**
466 * Is the index specified valid
467 * @param index
468 * @returns {Array.length|*|number|boolean}
469 */
470 function inRange(index) {
471 return _items.length && ( index > -1 ) && (index < _items.length );
472 }
473
474 /**
475 * Can the iterator proceed to the next item in the list; relative to
476 * the specified item.
477 *
478 * @param item
479 * @returns {Array.length|*|number|boolean}
480 */
481 function hasNext(item) {
482 return item ? inRange(indexOf(item) + 1) : false;
483 }
484
485 /**
486 * Can the iterator proceed to the previous item in the list; relative to
487 * the specified item.
488 *
489 * @param item
490 * @returns {Array.length|*|number|boolean}
491 */
492 function hasPrevious(item) {
493 return item ? inRange(indexOf(item) - 1) : false;
494 }
495
496 /**
497 * Get item at specified index/position
498 * @param index
499 * @returns {*}
500 */
501 function itemAt(index) {
502 return inRange(index) ? _items[index] : null;
503 }
504
505 /**
506 * Find all elements matching the key/value pair
507 * otherwise return null
508 *
509 * @param val
510 * @param key
511 *
512 * @return array
513 */
514 function findBy(key, val) {
515 return _items.filter(function(item) {
516 return item[key] === val;
517 });
518 }
519
520 /**
521 * Add item to list
522 * @param item
523 * @param index
524 * @returns {*}
525 */
526 function add(item, index) {
527 if ( !item ) return -1;
528
529 if (!angular.isNumber(index)) {
530 index = _items.length;
531 }
532
533 _items.splice(index, 0, item);
534
535 return indexOf(item);
536 }
537
538 /**
539 * Remove item from list...
540 * @param item
541 */
542 function remove(item) {
543 if ( contains(item) ){
544 _items.splice(indexOf(item), 1);
545 }
546 }
547
548 /**
549 * Get the zero-based index of the target item
550 * @param item
551 * @returns {*}
552 */
553 function indexOf(item) {
554 return _items.indexOf(item);
555 }
556
557 /**
558 * Boolean existence check
559 * @param item
560 * @returns {boolean}
561 */
562 function contains(item) {
563 return item && (indexOf(item) > -1);
564 }
565
566 /**
567 * Return first item in the list
568 * @returns {*}
569 */
570 function first() {
571 return _items.length ? _items[0] : null;
572 }
573
574 /**
575 * Return last item in the list...
576 * @returns {*}
577 */
578 function last() {
579 return _items.length ? _items[_items.length - 1] : null;
580 }
581
582 /**
583 * Find the next item. If reloop is true and at the end of the list, it will go back to the
584 * first item. If given, the `validate` callback will be used to determine whether the next item
585 * is valid. If not valid, it will try to find the next item again.
586 *
587 * @param {boolean} backwards Specifies the direction of searching (forwards/backwards)
588 * @param {*} item The item whose subsequent item we are looking for
589 * @param {Function=} validate The `validate` function
590 * @param {integer=} limit The recursion limit
591 *
592 * @returns {*} The subsequent item or null
593 */
594 function findSubsequentItem(backwards, item, validate, limit) {
595 validate = validate || trueFn;
596
597 var curIndex = indexOf(item);
598 while (true) {
599 if (!inRange(curIndex)) return null;
600
601 var nextIndex = curIndex + (backwards ? -1 : 1);
602 var foundItem = null;
603 if (inRange(nextIndex)) {
604 foundItem = _items[nextIndex];
605 } else if (reloop) {
606 foundItem = backwards ? last() : first();
607 nextIndex = indexOf(foundItem);
608 }
609
610 if ((foundItem === null) || (nextIndex === limit)) return null;
611 if (validate(foundItem)) return foundItem;
612
613 if (angular.isUndefined(limit)) limit = nextIndex;
614
615 curIndex = nextIndex;
616 }
617 }
618 }
619
620
621
622mdMediaFactory.$inject = ["$mdConstant", "$rootScope", "$window"];angular.module('material.core')
623.factory('$mdMedia', mdMediaFactory);
624
625/**
626 * @ngdoc service
627 * @name $mdMedia
628 * @module material.core
629 *
630 * @description
631 * `$mdMedia` is used to evaluate whether a given media query is true or false given the
632 * current device's screen / window size. The media query will be re-evaluated on resize, allowing
633 * you to register a watch.
634 *
635 * `$mdMedia` also has pre-programmed support for media queries that match the layout breakpoints:
636 *
637 * <table class="md-api-table">
638 * <thead>
639 * <tr>
640 * <th>Breakpoint</th>
641 * <th>mediaQuery</th>
642 * </tr>
643 * </thead>
644 * <tbody>
645 * <tr>
646 * <td>xs</td>
647 * <td>(max-width: 599px)</td>
648 * </tr>
649 * <tr>
650 * <td>gt-xs</td>
651 * <td>(min-width: 600px)</td>
652 * </tr>
653 * <tr>
654 * <td>sm</td>
655 * <td>(min-width: 600px) and (max-width: 959px)</td>
656 * </tr>
657 * <tr>
658 * <td>gt-sm</td>
659 * <td>(min-width: 960px)</td>
660 * </tr>
661 * <tr>
662 * <td>md</td>
663 * <td>(min-width: 960px) and (max-width: 1279px)</td>
664 * </tr>
665 * <tr>
666 * <td>gt-md</td>
667 * <td>(min-width: 1280px)</td>
668 * </tr>
669 * <tr>
670 * <td>lg</td>
671 * <td>(min-width: 1280px) and (max-width: 1919px)</td>
672 * </tr>
673 * <tr>
674 * <td>gt-lg</td>
675 * <td>(min-width: 1920px)</td>
676 * </tr>
677 * <tr>
678 * <td>xl</td>
679 * <td>(min-width: 1920px)</td>
680 * </tr>
681 * <tr>
682 * <td>landscape</td>
683 * <td>landscape</td>
684 * </tr>
685 * <tr>
686 * <td>portrait</td>
687 * <td>portrait</td>
688 * </tr>
689 * <tr>
690 * <td>print</td>
691 * <td>print</td>
692 * </tr>
693 * </tbody>
694 * </table>
695 *
696 * See Material Design's <a href="https://material.google.com/layout/responsive-ui.html">Layout - Adaptive UI</a> for more details.
697 *
698 * <a href="https://www.google.com/design/spec/layout/adaptive-ui.html">
699 * <img src="https://material-design.storage.googleapis.com/publish/material_v_4/material_ext_publish/0B8olV15J7abPSGFxemFiQVRtb1k/layout_adaptive_breakpoints_01.png" width="100%" height="100%"></img>
700 * </a>
701 *
702 * @returns {boolean} a boolean representing whether or not the given media query is true or false.
703 *
704 * @usage
705 * <hljs lang="js">
706 * app.controller('MyController', function($mdMedia, $scope) {
707 * $scope.$watch(function() { return $mdMedia('lg'); }, function(big) {
708 * $scope.bigScreen = big;
709 * });
710 *
711 * $scope.screenIsSmall = $mdMedia('sm');
712 * $scope.customQuery = $mdMedia('(min-width: 1234px)');
713 * $scope.anotherCustom = $mdMedia('max-width: 300px');
714 * });
715 * </hljs>
716 */
717
718/* ngInject */
719function mdMediaFactory($mdConstant, $rootScope, $window) {
720 var queries = {};
721 var mqls = {};
722 var results = {};
723 var normalizeCache = {};
724
725 $mdMedia.getResponsiveAttribute = getResponsiveAttribute;
726 $mdMedia.getQuery = getQuery;
727 $mdMedia.watchResponsiveAttributes = watchResponsiveAttributes;
728
729 return $mdMedia;
730
731 function $mdMedia(query) {
732 var validated = queries[query];
733 if (angular.isUndefined(validated)) {
734 validated = queries[query] = validate(query);
735 }
736
737 var result = results[validated];
738 if (angular.isUndefined(result)) {
739 result = add(validated);
740 }
741
742 return result;
743 }
744
745 function validate(query) {
746 return $mdConstant.MEDIA[query] ||
747 ((query.charAt(0) !== '(') ? ('(' + query + ')') : query);
748 }
749
750 function add(query) {
751 var result = mqls[query];
752 if ( !result ) {
753 result = mqls[query] = $window.matchMedia(query);
754 }
755
756 result.addListener(onQueryChange);
757 return (results[result.media] = !!result.matches);
758 }
759
760 function onQueryChange(query) {
761 $rootScope.$evalAsync(function() {
762 results[query.media] = !!query.matches;
763 });
764 }
765
766 function getQuery(name) {
767 return mqls[name];
768 }
769
770 function getResponsiveAttribute(attrs, attrName) {
771 for (var i = 0; i < $mdConstant.MEDIA_PRIORITY.length; i++) {
772 var mediaName = $mdConstant.MEDIA_PRIORITY[i];
773 if (!mqls[queries[mediaName]].matches) {
774 continue;
775 }
776
777 var normalizedName = getNormalizedName(attrs, attrName + '-' + mediaName);
778 if (attrs[normalizedName]) {
779 return attrs[normalizedName];
780 }
781 }
782
783 // fallback on unprefixed
784 return attrs[getNormalizedName(attrs, attrName)];
785 }
786
787 function watchResponsiveAttributes(attrNames, attrs, watchFn) {
788 var unwatchFns = [];
789 attrNames.forEach(function(attrName) {
790 var normalizedName = getNormalizedName(attrs, attrName);
791 if (angular.isDefined(attrs[normalizedName])) {
792 unwatchFns.push(
793 attrs.$observe(normalizedName, angular.bind(void 0, watchFn, null)));
794 }
795
796 for (var mediaName in $mdConstant.MEDIA) {
797 normalizedName = getNormalizedName(attrs, attrName + '-' + mediaName);
798 if (angular.isDefined(attrs[normalizedName])) {
799 unwatchFns.push(
800 attrs.$observe(normalizedName, angular.bind(void 0, watchFn, mediaName)));
801 }
802 }
803 });
804
805 return function unwatch() {
806 unwatchFns.forEach(function(fn) { fn(); })
807 };
808 }
809
810 // Improves performance dramatically
811 function getNormalizedName(attrs, attrName) {
812 return normalizeCache[attrName] ||
813 (normalizeCache[attrName] = attrs.$normalize(attrName));
814 }
815}
816
817angular
818 .module('material.core')
819 .config( ["$provide", function($provide) {
820 $provide.decorator('$mdUtil', ['$delegate', function ($delegate) {
821
822 // Inject the prefixer into our original $mdUtil service.
823 $delegate.prefixer = MdPrefixer;
824
825 return $delegate;
826 }]);
827 }]);
828
829function MdPrefixer(initialAttributes, buildSelector) {
830 var PREFIXES = ['data', 'x'];
831
832 if (initialAttributes) {
833 // The prefixer also accepts attributes as a parameter, and immediately builds a list or selector for
834 // the specified attributes.
835 return buildSelector ? _buildSelector(initialAttributes) : _buildList(initialAttributes);
836 }
837
838 return {
839 buildList: _buildList,
840 buildSelector: _buildSelector,
841 hasAttribute: _hasAttribute,
842 removeAttribute: _removeAttribute
843 };
844
845 function _buildList(attributes) {
846 attributes = angular.isArray(attributes) ? attributes : [attributes];
847
848 attributes.forEach(function(item) {
849 PREFIXES.forEach(function(prefix) {
850 attributes.push(prefix + '-' + item);
851 });
852 });
853
854 return attributes;
855 }
856
857 function _buildSelector(attributes) {
858 attributes = angular.isArray(attributes) ? attributes : [attributes];
859
860 return _buildList(attributes)
861 .map(function(item) {
862 return '[' + item + ']'
863 })
864 .join(',');
865 }
866
867 function _hasAttribute(element, attribute) {
868 element = _getNativeElement(element);
869
870 if (!element) {
871 return false;
872 }
873
874 var prefixedAttrs = _buildList(attribute);
875
876 for (var i = 0; i < prefixedAttrs.length; i++) {
877 if (element.hasAttribute(prefixedAttrs[i])) {
878 return true;
879 }
880 }
881
882 return false;
883 }
884
885 function _removeAttribute(element, attribute) {
886 element = _getNativeElement(element);
887
888 if (!element) {
889 return;
890 }
891
892 _buildList(attribute).forEach(function(prefixedAttribute) {
893 element.removeAttribute(prefixedAttribute);
894 });
895 }
896
897 /**
898 * Transforms a jqLite or DOM element into a HTML element.
899 * This is useful when supporting jqLite elements and DOM elements at
900 * same time.
901 * @param element {JQLite|Element} Element to be parsed
902 * @returns {HTMLElement} Parsed HTMLElement
903 */
904 function _getNativeElement(element) {
905 element = element[0] || element;
906
907 if (element.nodeType) {
908 return element;
909 }
910 }
911
912}
913/*
914 * This var has to be outside the angular factory, otherwise when
915 * there are multiple material apps on the same page, each app
916 * will create its own instance of this array and the app's IDs
917 * will not be unique.
918 */
919UtilFactory.$inject = ["$document", "$timeout", "$compile", "$rootScope", "$$mdAnimate", "$interpolate", "$log", "$rootElement", "$window", "$$rAF"];
920var nextUniqueId = 0;
921
922/**
923 * @ngdoc module
924 * @name material.core.util
925 * @description
926 * Util
927 */
928angular
929 .module('material.core')
930 .factory('$mdUtil', UtilFactory);
931
932/**
933 * ngInject
934 */
935function UtilFactory($document, $timeout, $compile, $rootScope, $$mdAnimate, $interpolate, $log, $rootElement, $window, $$rAF) {
936 // Setup some core variables for the processTemplate method
937 var startSymbol = $interpolate.startSymbol(),
938 endSymbol = $interpolate.endSymbol(),
939 usesStandardSymbols = ((startSymbol === '{{') && (endSymbol === '}}'));
940
941 /**
942 * Checks if the target element has the requested style by key
943 * @param {DOMElement|JQLite} target Target element
944 * @param {string} key Style key
945 * @param {string=} expectedVal Optional expected value
946 * @returns {boolean} Whether the target element has the style or not
947 */
948 var hasComputedStyle = function (target, key, expectedVal) {
949 var hasValue = false;
950
951 if ( target && target.length ) {
952 var computedStyles = $window.getComputedStyle(target[0]);
953 hasValue = angular.isDefined(computedStyles[key]) && (expectedVal ? computedStyles[key] == expectedVal : true);
954 }
955
956 return hasValue;
957 };
958
959 function validateCssValue(value) {
960 return !value ? '0' :
961 hasPx(value) || hasPercent(value) ? value : value + 'px';
962 }
963
964 function hasPx(value) {
965 return String(value).indexOf('px') > -1;
966 }
967
968 function hasPercent(value) {
969 return String(value).indexOf('%') > -1;
970
971 }
972
973 var $mdUtil = {
974 dom: {},
975 now: window.performance ?
976 angular.bind(window.performance, window.performance.now) : Date.now || function() {
977 return new Date().getTime();
978 },
979
980 /**
981 * Bi-directional accessor/mutator used to easily update an element's
982 * property based on the current 'dir'ectional value.
983 */
984 bidi : function(element, property, lValue, rValue) {
985 var ltr = !($document[0].dir == 'rtl' || $document[0].body.dir == 'rtl');
986
987 // If accessor
988 if ( arguments.length == 0 ) return ltr ? 'ltr' : 'rtl';
989
990 // If mutator
991 var elem = angular.element(element);
992
993 if ( ltr && angular.isDefined(lValue)) {
994 elem.css(property, validateCssValue(lValue));
995 }
996 else if ( !ltr && angular.isDefined(rValue)) {
997 elem.css(property, validateCssValue(rValue) );
998 }
999 },
1000
1001 bidiProperty: function (element, lProperty, rProperty, value) {
1002 var ltr = !($document[0].dir == 'rtl' || $document[0].body.dir == 'rtl');
1003
1004 var elem = angular.element(element);
1005
1006 if ( ltr && angular.isDefined(lProperty)) {
1007 elem.css(lProperty, validateCssValue(value));
1008 elem.css(rProperty, '');
1009 }
1010 else if ( !ltr && angular.isDefined(rProperty)) {
1011 elem.css(rProperty, validateCssValue(value) );
1012 elem.css(lProperty, '');
1013 }
1014 },
1015
1016 clientRect: function(element, offsetParent, isOffsetRect) {
1017 var node = getNode(element);
1018 offsetParent = getNode(offsetParent || node.offsetParent || document.body);
1019 var nodeRect = node.getBoundingClientRect();
1020
1021 // The user can ask for an offsetRect: a rect relative to the offsetParent,
1022 // or a clientRect: a rect relative to the page
1023 var offsetRect = isOffsetRect ?
1024 offsetParent.getBoundingClientRect() :
1025 {left: 0, top: 0, width: 0, height: 0};
1026 return {
1027 left: nodeRect.left - offsetRect.left,
1028 top: nodeRect.top - offsetRect.top,
1029 width: nodeRect.width,
1030 height: nodeRect.height
1031 };
1032 },
1033 offsetRect: function(element, offsetParent) {
1034 return $mdUtil.clientRect(element, offsetParent, true);
1035 },
1036
1037 // Annoying method to copy nodes to an array, thanks to IE
1038 nodesToArray: function(nodes) {
1039 nodes = nodes || [];
1040
1041 var results = [];
1042 for (var i = 0; i < nodes.length; ++i) {
1043 results.push(nodes.item(i));
1044 }
1045 return results;
1046 },
1047
1048 /**
1049 * Calculate the positive scroll offset
1050 * TODO: Check with pinch-zoom in IE/Chrome;
1051 * https://code.google.com/p/chromium/issues/detail?id=496285
1052 */
1053 scrollTop: function(element) {
1054 element = angular.element(element || $document[0].body);
1055
1056 var body = (element[0] == $document[0].body) ? $document[0].body : undefined;
1057 var scrollTop = body ? body.scrollTop + body.parentElement.scrollTop : 0;
1058
1059 // Calculate the positive scroll offset
1060 return scrollTop || Math.abs(element[0].getBoundingClientRect().top);
1061 },
1062
1063 /**
1064 * Finds the proper focus target by searching the DOM.
1065 *
1066 * @param containerEl
1067 * @param attributeVal
1068 * @returns {*}
1069 */
1070 findFocusTarget: function(containerEl, attributeVal) {
1071 var AUTO_FOCUS = this.prefixer('md-autofocus', true);
1072 var elToFocus;
1073
1074 elToFocus = scanForFocusable(containerEl, attributeVal || AUTO_FOCUS);
1075
1076 if ( !elToFocus && attributeVal != AUTO_FOCUS) {
1077 // Scan for deprecated attribute
1078 elToFocus = scanForFocusable(containerEl, this.prefixer('md-auto-focus', true));
1079
1080 if ( !elToFocus ) {
1081 // Scan for fallback to 'universal' API
1082 elToFocus = scanForFocusable(containerEl, AUTO_FOCUS);
1083 }
1084 }
1085
1086 return elToFocus;
1087
1088 /**
1089 * Can target and nested children for specified Selector (attribute)
1090 * whose value may be an expression that evaluates to True/False.
1091 */
1092 function scanForFocusable(target, selector) {
1093 var elFound, items = target[0].querySelectorAll(selector);
1094
1095 // Find the last child element with the focus attribute
1096 if ( items && items.length ){
1097 items.length && angular.forEach(items, function(it) {
1098 it = angular.element(it);
1099
1100 // Check the element for the md-autofocus class to ensure any associated expression
1101 // evaluated to true.
1102 var isFocusable = it.hasClass('md-autofocus');
1103 if (isFocusable) elFound = it;
1104 });
1105 }
1106 return elFound;
1107 }
1108 },
1109
1110 /**
1111 * Disables scroll around the passed parent element.
1112 * @param element Unused
1113 * @param {!Element|!angular.JQLite} parent Element to disable scrolling within.
1114 * Defaults to body if none supplied.
1115 * @param options Object of options to modify functionality
1116 * - disableScrollMask Boolean of whether or not to create a scroll mask element or
1117 * use the passed parent element.
1118 */
1119 disableScrollAround: function(element, parent, options) {
1120 $mdUtil.disableScrollAround._count = $mdUtil.disableScrollAround._count || 0;
1121 ++$mdUtil.disableScrollAround._count;
1122 if ($mdUtil.disableScrollAround._enableScrolling) return $mdUtil.disableScrollAround._enableScrolling;
1123 var body = $document[0].body,
1124 restoreBody = disableBodyScroll(),
1125 restoreElement = disableElementScroll(parent);
1126
1127 return $mdUtil.disableScrollAround._enableScrolling = function() {
1128 if (!--$mdUtil.disableScrollAround._count) {
1129 restoreBody();
1130 restoreElement();
1131 delete $mdUtil.disableScrollAround._enableScrolling;
1132 }
1133 };
1134
1135 // Creates a virtual scrolling mask to absorb touchmove, keyboard, scrollbar clicking, and wheel events
1136 function disableElementScroll(element) {
1137 element = angular.element(element || body);
1138 var scrollMask;
1139 if (options && options.disableScrollMask) {
1140 scrollMask = element;
1141 } else {
1142 element = element[0];
1143 scrollMask = angular.element(
1144 '<div class="md-scroll-mask">' +
1145 ' <div class="md-scroll-mask-bar"></div>' +
1146 '</div>');
1147 element.appendChild(scrollMask[0]);
1148 }
1149
1150 scrollMask.on('wheel', preventDefault);
1151 scrollMask.on('touchmove', preventDefault);
1152
1153 return function restoreScroll() {
1154 scrollMask.off('wheel');
1155 scrollMask.off('touchmove');
1156 scrollMask[0].parentNode.removeChild(scrollMask[0]);
1157 delete $mdUtil.disableScrollAround._enableScrolling;
1158 };
1159
1160 function preventDefault(e) {
1161 e.preventDefault();
1162 }
1163 }
1164
1165 // Converts the body to a position fixed block and translate it to the proper scroll position
1166 function disableBodyScroll() {
1167 var htmlNode = body.parentNode;
1168 var restoreHtmlStyle = htmlNode.style.cssText || '';
1169 var restoreBodyStyle = body.style.cssText || '';
1170 var scrollOffset = $mdUtil.scrollTop(body);
1171 var clientWidth = body.clientWidth;
1172
1173 if (body.scrollHeight > body.clientHeight + 1) {
1174 applyStyles(body, {
1175 position: 'fixed',
1176 width: '100%',
1177 top: -scrollOffset + 'px'
1178 });
1179
1180 htmlNode.style.overflowY = 'scroll';
1181 }
1182
1183 if (body.clientWidth < clientWidth) applyStyles(body, {overflow: 'hidden'});
1184
1185 return function restoreScroll() {
1186 body.style.cssText = restoreBodyStyle;
1187 htmlNode.style.cssText = restoreHtmlStyle;
1188 body.scrollTop = scrollOffset;
1189 htmlNode.scrollTop = scrollOffset;
1190 };
1191 }
1192
1193 function applyStyles(el, styles) {
1194 for (var key in styles) {
1195 el.style[key] = styles[key];
1196 }
1197 }
1198 },
1199 enableScrolling: function() {
1200 var method = this.disableScrollAround._enableScrolling;
1201 method && method();
1202 },
1203 floatingScrollbars: function() {
1204 if (this.floatingScrollbars.cached === undefined) {
1205 var tempNode = angular.element('<div><div></div></div>').css({
1206 width: '100%',
1207 'z-index': -1,
1208 position: 'absolute',
1209 height: '35px',
1210 'overflow-y': 'scroll'
1211 });
1212 tempNode.children().css('height', '60px');
1213
1214 $document[0].body.appendChild(tempNode[0]);
1215 this.floatingScrollbars.cached = (tempNode[0].offsetWidth == tempNode[0].childNodes[0].offsetWidth);
1216 tempNode.remove();
1217 }
1218 return this.floatingScrollbars.cached;
1219 },
1220
1221 // Mobile safari only allows you to set focus in click event listeners...
1222 forceFocus: function(element) {
1223 var node = element[0] || element;
1224
1225 document.addEventListener('click', function focusOnClick(ev) {
1226 if (ev.target === node && ev.$focus) {
1227 node.focus();
1228 ev.stopImmediatePropagation();
1229 ev.preventDefault();
1230 node.removeEventListener('click', focusOnClick);
1231 }
1232 }, true);
1233
1234 var newEvent = document.createEvent('MouseEvents');
1235 newEvent.initMouseEvent('click', false, true, window, {}, 0, 0, 0, 0,
1236 false, false, false, false, 0, null);
1237 newEvent.$material = true;
1238 newEvent.$focus = true;
1239 node.dispatchEvent(newEvent);
1240 },
1241
1242 /**
1243 * facade to build md-backdrop element with desired styles
1244 * NOTE: Use $compile to trigger backdrop postLink function
1245 */
1246 createBackdrop: function(scope, addClass) {
1247 return $compile($mdUtil.supplant('<md-backdrop class="{0}">', [addClass]))(scope);
1248 },
1249
1250 /**
1251 * supplant() method from Crockford's `Remedial Javascript`
1252 * Equivalent to use of $interpolate; without dependency on
1253 * interpolation symbols and scope. Note: the '{<token>}' can
1254 * be property names, property chains, or array indices.
1255 */
1256 supplant: function(template, values, pattern) {
1257 pattern = pattern || /\{([^\{\}]*)\}/g;
1258 return template.replace(pattern, function(a, b) {
1259 var p = b.split('.'),
1260 r = values;
1261 try {
1262 for (var s in p) {
1263 if (p.hasOwnProperty(s) ) {
1264 r = r[p[s]];
1265 }
1266 }
1267 } catch (e) {
1268 r = a;
1269 }
1270 return (typeof r === 'string' || typeof r === 'number') ? r : a;
1271 });
1272 },
1273
1274 fakeNgModel: function() {
1275 return {
1276 $fake: true,
1277 $setTouched: angular.noop,
1278 $setViewValue: function(value) {
1279 this.$viewValue = value;
1280 this.$render(value);
1281 this.$viewChangeListeners.forEach(function(cb) {
1282 cb();
1283 });
1284 },
1285 $isEmpty: function(value) {
1286 return ('' + value).length === 0;
1287 },
1288 $parsers: [],
1289 $formatters: [],
1290 $viewChangeListeners: [],
1291 $render: angular.noop
1292 };
1293 },
1294
1295 // Returns a function, that, as long as it continues to be invoked, will not
1296 // be triggered. The function will be called after it stops being called for
1297 // N milliseconds.
1298 // @param wait Integer value of msecs to delay (since last debounce reset); default value 10 msecs
1299 // @param invokeApply should the $timeout trigger $digest() dirty checking
1300 debounce: function(func, wait, scope, invokeApply) {
1301 var timer;
1302
1303 return function debounced() {
1304 var context = scope,
1305 args = Array.prototype.slice.call(arguments);
1306
1307 $timeout.cancel(timer);
1308 timer = $timeout(function() {
1309
1310 timer = undefined;
1311 func.apply(context, args);
1312
1313 }, wait || 10, invokeApply);
1314 };
1315 },
1316
1317 // Returns a function that can only be triggered every `delay` milliseconds.
1318 // In other words, the function will not be called unless it has been more
1319 // than `delay` milliseconds since the last call.
1320 throttle: function throttle(func, delay) {
1321 var recent;
1322 return function throttled() {
1323 var context = this;
1324 var args = arguments;
1325 var now = $mdUtil.now();
1326
1327 if (!recent || (now - recent > delay)) {
1328 func.apply(context, args);
1329 recent = now;
1330 }
1331 };
1332 },
1333
1334 /**
1335 * Measures the number of milliseconds taken to run the provided callback
1336 * function. Uses a high-precision timer if available.
1337 */
1338 time: function time(cb) {
1339 var start = $mdUtil.now();
1340 cb();
1341 return $mdUtil.now() - start;
1342 },
1343
1344 /**
1345 * Create an implicit getter that caches its `getter()`
1346 * lookup value
1347 */
1348 valueOnUse : function (scope, key, getter) {
1349 var value = null, args = Array.prototype.slice.call(arguments);
1350 var params = (args.length > 3) ? args.slice(3) : [ ];
1351
1352 Object.defineProperty(scope, key, {
1353 get: function () {
1354 if (value === null) value = getter.apply(scope, params);
1355 return value;
1356 }
1357 });
1358 },
1359
1360 /**
1361 * Get a unique ID.
1362 *
1363 * @returns {string} an unique numeric string
1364 */
1365 nextUid: function() {
1366 return '' + nextUniqueId++;
1367 },
1368
1369 // Stop watchers and events from firing on a scope without destroying it,
1370 // by disconnecting it from its parent and its siblings' linked lists.
1371 disconnectScope: function disconnectScope(scope) {
1372 if (!scope) return;
1373
1374 // we can't destroy the root scope or a scope that has been already destroyed
1375 if (scope.$root === scope) return;
1376 if (scope.$$destroyed) return;
1377
1378 var parent = scope.$parent;
1379 scope.$$disconnected = true;
1380
1381 // See Scope.$destroy
1382 if (parent.$$childHead === scope) parent.$$childHead = scope.$$nextSibling;
1383 if (parent.$$childTail === scope) parent.$$childTail = scope.$$prevSibling;
1384 if (scope.$$prevSibling) scope.$$prevSibling.$$nextSibling = scope.$$nextSibling;
1385 if (scope.$$nextSibling) scope.$$nextSibling.$$prevSibling = scope.$$prevSibling;
1386
1387 scope.$$nextSibling = scope.$$prevSibling = null;
1388
1389 },
1390
1391 // Undo the effects of disconnectScope above.
1392 reconnectScope: function reconnectScope(scope) {
1393 if (!scope) return;
1394
1395 // we can't disconnect the root node or scope already disconnected
1396 if (scope.$root === scope) return;
1397 if (!scope.$$disconnected) return;
1398
1399 var child = scope;
1400
1401 var parent = child.$parent;
1402 child.$$disconnected = false;
1403 // See Scope.$new for this logic...
1404 child.$$prevSibling = parent.$$childTail;
1405 if (parent.$$childHead) {
1406 parent.$$childTail.$$nextSibling = child;
1407 parent.$$childTail = child;
1408 } else {
1409 parent.$$childHead = parent.$$childTail = child;
1410 }
1411 },
1412
1413 /*
1414 * getClosest replicates jQuery.closest() to walk up the DOM tree until it finds a matching nodeName
1415 *
1416 * @param el Element to start walking the DOM from
1417 * @param check Either a string or a function. If a string is passed, it will be evaluated against
1418 * each of the parent nodes' tag name. If a function is passed, the loop will call it with each of
1419 * the parents and will use the return value to determine whether the node is a match.
1420 * @param onlyParent Only start checking from the parent element, not `el`.
1421 */
1422 getClosest: function getClosest(el, validateWith, onlyParent) {
1423 if ( angular.isString(validateWith) ) {
1424 var tagName = validateWith.toUpperCase();
1425 validateWith = function(el) {
1426 return el.nodeName === tagName;
1427 };
1428 }
1429
1430 if (el instanceof angular.element) el = el[0];
1431 if (onlyParent) el = el.parentNode;
1432 if (!el) return null;
1433
1434 do {
1435 if (validateWith(el)) {
1436 return el;
1437 }
1438 } while (el = el.parentNode);
1439
1440 return null;
1441 },
1442
1443 /**
1444 * Build polyfill for the Node.contains feature (if needed)
1445 */
1446 elementContains: function(node, child) {
1447 var hasContains = (window.Node && window.Node.prototype && Node.prototype.contains);
1448 var findFn = hasContains ? angular.bind(node, node.contains) : angular.bind(node, function(arg) {
1449 // compares the positions of two nodes and returns a bitmask
1450 return (node === child) || !!(this.compareDocumentPosition(arg) & 16)
1451 });
1452
1453 return findFn(child);
1454 },
1455
1456 /**
1457 * Functional equivalent for $element.filter(‘md-bottom-sheet’)
1458 * useful with interimElements where the element and its container are important...
1459 *
1460 * @param {[]} elements to scan
1461 * @param {string} name of node to find (e.g. 'md-dialog')
1462 * @param {boolean=} optional flag to allow deep scans; defaults to 'false'.
1463 * @param {boolean=} optional flag to enable log warnings; defaults to false
1464 */
1465 extractElementByName: function(element, nodeName, scanDeep, warnNotFound) {
1466 var found = scanTree(element);
1467 if (!found && !!warnNotFound) {
1468 $log.warn( $mdUtil.supplant("Unable to find node '{0}' in element '{1}'.",[nodeName, element[0].outerHTML]) );
1469 }
1470
1471 return angular.element(found || element);
1472
1473 /**
1474 * Breadth-First tree scan for element with matching `nodeName`
1475 */
1476 function scanTree(element) {
1477 return scanLevel(element) || (!!scanDeep ? scanChildren(element) : null);
1478 }
1479
1480 /**
1481 * Case-insensitive scan of current elements only (do not descend).
1482 */
1483 function scanLevel(element) {
1484 if ( element ) {
1485 for (var i = 0, len = element.length; i < len; i++) {
1486 if (element[i].nodeName.toLowerCase() === nodeName) {
1487 return element[i];
1488 }
1489 }
1490 }
1491 return null;
1492 }
1493
1494 /**
1495 * Scan children of specified node
1496 */
1497 function scanChildren(element) {
1498 var found;
1499 if ( element ) {
1500 for (var i = 0, len = element.length; i < len; i++) {
1501 var target = element[i];
1502 if ( !found ) {
1503 for (var j = 0, numChild = target.childNodes.length; j < numChild; j++) {
1504 found = found || scanTree([target.childNodes[j]]);
1505 }
1506 }
1507 }
1508 }
1509 return found;
1510 }
1511
1512 },
1513
1514 /**
1515 * Give optional properties with no value a boolean true if attr provided or false otherwise
1516 */
1517 initOptionalProperties: function(scope, attr, defaults) {
1518 defaults = defaults || {};
1519 angular.forEach(scope.$$isolateBindings, function(binding, key) {
1520 if (binding.optional && angular.isUndefined(scope[key])) {
1521 var attrIsDefined = angular.isDefined(attr[binding.attrName]);
1522 scope[key] = angular.isDefined(defaults[key]) ? defaults[key] : attrIsDefined;
1523 }
1524 });
1525 },
1526
1527 /**
1528 * Alternative to $timeout calls with 0 delay.
1529 * nextTick() coalesces all calls within a single frame
1530 * to minimize $digest thrashing
1531 *
1532 * @param callback
1533 * @param digest
1534 * @returns {*}
1535 */
1536 nextTick: function(callback, digest, scope) {
1537 //-- grab function reference for storing state details
1538 var nextTick = $mdUtil.nextTick;
1539 var timeout = nextTick.timeout;
1540 var queue = nextTick.queue || [];
1541
1542 //-- add callback to the queue
1543 queue.push({scope: scope, callback: callback});
1544
1545 //-- set default value for digest
1546 if (digest == null) digest = true;
1547
1548 //-- store updated digest/queue values
1549 nextTick.digest = nextTick.digest || digest;
1550 nextTick.queue = queue;
1551
1552 //-- either return existing timeout or create a new one
1553 return timeout || (nextTick.timeout = $timeout(processQueue, 0, false));
1554
1555 /**
1556 * Grab a copy of the current queue
1557 * Clear the queue for future use
1558 * Process the existing queue
1559 * Trigger digest if necessary
1560 */
1561 function processQueue() {
1562 var queue = nextTick.queue;
1563 var digest = nextTick.digest;
1564
1565 nextTick.queue = [];
1566 nextTick.timeout = null;
1567 nextTick.digest = false;
1568
1569 queue.forEach(function(queueItem) {
1570 var skip = queueItem.scope && queueItem.scope.$$destroyed;
1571 if (!skip) {
1572 queueItem.callback();
1573 }
1574 });
1575
1576 if (digest) $rootScope.$digest();
1577 }
1578 },
1579
1580 /**
1581 * Processes a template and replaces the start/end symbols if the application has
1582 * overriden them.
1583 *
1584 * @param template The template to process whose start/end tags may be replaced.
1585 * @returns {*}
1586 */
1587 processTemplate: function(template) {
1588 if (usesStandardSymbols) {
1589 return template;
1590 } else {
1591 if (!template || !angular.isString(template)) return template;
1592 return template.replace(/\{\{/g, startSymbol).replace(/}}/g, endSymbol);
1593 }
1594 },
1595
1596 /**
1597 * Scan up dom hierarchy for enabled parent;
1598 */
1599 getParentWithPointerEvents: function (element) {
1600 var parent = element.parent();
1601
1602 // jqLite might return a non-null, but still empty, parent; so check for parent and length
1603 while (hasComputedStyle(parent, 'pointer-events', 'none')) {
1604 parent = parent.parent();
1605 }
1606
1607 return parent;
1608 },
1609
1610 getNearestContentElement: function (element) {
1611 var current = element.parent()[0];
1612 // Look for the nearest parent md-content, stopping at the rootElement.
1613 while (current && current !== $rootElement[0] && current !== document.body && current.nodeName.toUpperCase() !== 'MD-CONTENT') {
1614 current = current.parentNode;
1615 }
1616 return current;
1617 },
1618
1619 /**
1620 * Checks if the current browser is natively supporting the `sticky` position.
1621 * @returns {string} supported sticky property name
1622 */
1623 checkStickySupport: function() {
1624 var stickyProp;
1625 var testEl = angular.element('<div>');
1626 $document[0].body.appendChild(testEl[0]);
1627
1628 var stickyProps = ['sticky', '-webkit-sticky'];
1629 for (var i = 0; i < stickyProps.length; ++i) {
1630 testEl.css({
1631 position: stickyProps[i],
1632 top: 0,
1633 'z-index': 2
1634 });
1635
1636 if (testEl.css('position') == stickyProps[i]) {
1637 stickyProp = stickyProps[i];
1638 break;
1639 }
1640 }
1641
1642 testEl.remove();
1643
1644 return stickyProp;
1645 },
1646
1647 /**
1648 * Parses an attribute value, mostly a string.
1649 * By default checks for negated values and returns `false´ if present.
1650 * Negated values are: (native falsy) and negative strings like:
1651 * `false` or `0`.
1652 * @param value Attribute value which should be parsed.
1653 * @param negatedCheck When set to false, won't check for negated values.
1654 * @returns {boolean}
1655 */
1656 parseAttributeBoolean: function(value, negatedCheck) {
1657 return value === '' || !!value && (negatedCheck === false || value !== 'false' && value !== '0');
1658 },
1659
1660 hasComputedStyle: hasComputedStyle,
1661
1662 /**
1663 * Returns true if the parent form of the element has been submitted.
1664 *
1665 * @param element An Angular or HTML5 element.
1666 *
1667 * @returns {boolean}
1668 */
1669 isParentFormSubmitted: function(element) {
1670 var parent = $mdUtil.getClosest(element, 'form');
1671 var form = parent ? angular.element(parent).controller('form') : null;
1672
1673 return form ? form.$submitted : false;
1674 },
1675
1676 /**
1677 * Animate the requested element's scrollTop to the requested scrollPosition with basic easing.
1678 *
1679 * @param element The element to scroll.
1680 * @param scrollEnd The new/final scroll position.
1681 */
1682 animateScrollTo: function(element, scrollEnd) {
1683 var scrollStart = element.scrollTop;
1684 var scrollChange = scrollEnd - scrollStart;
1685 var scrollingDown = scrollStart < scrollEnd;
1686 var startTime = $mdUtil.now();
1687
1688 $$rAF(scrollChunk);
1689
1690 function scrollChunk() {
1691 var newPosition = calculateNewPosition();
1692
1693 element.scrollTop = newPosition;
1694
1695 if (scrollingDown ? newPosition < scrollEnd : newPosition > scrollEnd) {
1696 $$rAF(scrollChunk);
1697 }
1698 }
1699
1700 function calculateNewPosition() {
1701 var duration = 1000;
1702 var currentTime = $mdUtil.now() - startTime;
1703
1704 return ease(currentTime, scrollStart, scrollChange, duration);
1705 }
1706
1707 function ease(currentTime, start, change, duration) {
1708 // If the duration has passed (which can occur if our app loses focus due to $$rAF), jump
1709 // straight to the proper position
1710 if (currentTime > duration) {
1711 return start + change;
1712 }
1713
1714 var ts = (currentTime /= duration) * currentTime;
1715 var tc = ts * currentTime;
1716
1717 return start + change * (-2 * tc + 3 * ts);
1718 }
1719 }
1720 };
1721
1722
1723// Instantiate other namespace utility methods
1724
1725 $mdUtil.dom.animator = $$mdAnimate($mdUtil);
1726
1727 return $mdUtil;
1728
1729 function getNode(el) {
1730 return el[0] || el;
1731 }
1732
1733}
1734
1735/*
1736 * Since removing jQuery from the demos, some code that uses `element.focus()` is broken.
1737 * We need to add `element.focus()`, because it's testable unlike `element[0].focus`.
1738 */
1739
1740angular.element.prototype.focus = angular.element.prototype.focus || function() {
1741 if (this.length) {
1742 this[0].focus();
1743 }
1744 return this;
1745 };
1746angular.element.prototype.blur = angular.element.prototype.blur || function() {
1747 if (this.length) {
1748 this[0].blur();
1749 }
1750 return this;
1751 };
1752
1753/**
1754 * @ngdoc module
1755 * @name material.core.aria
1756 * @description
1757 * Aria Expectations for ngMaterial components.
1758 */
1759MdAriaService.$inject = ["$$rAF", "$log", "$window", "$interpolate"];
1760angular
1761 .module('material.core')
1762 .provider('$mdAria', MdAriaProvider);
1763
1764/**
1765 * @ngdoc service
1766 * @name $mdAriaProvider
1767 * @module material.core.aria
1768 *
1769 * @description
1770 *
1771 * Modify options of the `$mdAria` service, which will be used by most of the Angular Material
1772 * components.
1773 *
1774 * You are able to disable `$mdAria` warnings, by using the following markup.
1775 *
1776 * <hljs lang="js">
1777 * app.config(function($mdAriaProvider) {
1778 * // Globally disables all ARIA warnings.
1779 * $mdAriaProvider.disableWarnings();
1780 * });
1781 * </hljs>
1782 *
1783 */
1784function MdAriaProvider() {
1785
1786 var self = this;
1787
1788 /**
1789 * Whether we should show ARIA warnings in the console, if labels are missing on the element
1790 * By default the warnings are enabled
1791 */
1792 self.showWarnings = true;
1793
1794 return {
1795 disableWarnings: disableWarnings,
1796 $get: ["$$rAF", "$log", "$window", "$interpolate", function($$rAF, $log, $window, $interpolate) {
1797 return MdAriaService.apply(self, arguments);
1798 }]
1799 };
1800
1801 /**
1802 * @ngdoc method
1803 * @name $mdAriaProvider#disableWarnings
1804 * @description Disables all ARIA warnings generated by Angular Material.
1805 */
1806 function disableWarnings() {
1807 self.showWarnings = false;
1808 }
1809}
1810
1811/*
1812 * ngInject
1813 */
1814function MdAriaService($$rAF, $log, $window, $interpolate) {
1815
1816 // Load the showWarnings option from the current context and store it inside of a scope variable,
1817 // because the context will be probably lost in some function calls.
1818 var showWarnings = this.showWarnings;
1819
1820 return {
1821 expect: expect,
1822 expectAsync: expectAsync,
1823 expectWithText: expectWithText,
1824 expectWithoutText: expectWithoutText
1825 };
1826
1827 /**
1828 * Check if expected attribute has been specified on the target element or child
1829 * @param element
1830 * @param attrName
1831 * @param {optional} defaultValue What to set the attr to if no value is found
1832 */
1833 function expect(element, attrName, defaultValue) {
1834
1835 var node = angular.element(element)[0] || element;
1836
1837 // if node exists and neither it nor its children have the attribute
1838 if (node &&
1839 ((!node.hasAttribute(attrName) ||
1840 node.getAttribute(attrName).length === 0) &&
1841 !childHasAttribute(node, attrName))) {
1842
1843 defaultValue = angular.isString(defaultValue) ? defaultValue.trim() : '';
1844 if (defaultValue.length) {
1845 element.attr(attrName, defaultValue);
1846 } else if (showWarnings) {
1847 $log.warn('ARIA: Attribute "', attrName, '", required for accessibility, is missing on node:', node);
1848 }
1849
1850 }
1851 }
1852
1853 function expectAsync(element, attrName, defaultValueGetter) {
1854 // Problem: when retrieving the element's contents synchronously to find the label,
1855 // the text may not be defined yet in the case of a binding.
1856 // There is a higher chance that a binding will be defined if we wait one frame.
1857 $$rAF(function() {
1858 expect(element, attrName, defaultValueGetter());
1859 });
1860 }
1861
1862 function expectWithText(element, attrName) {
1863 var content = getText(element) || "";
1864 var hasBinding = content.indexOf($interpolate.startSymbol()) > -1;
1865
1866 if ( hasBinding ) {
1867 expectAsync(element, attrName, function() {
1868 return getText(element);
1869 });
1870 } else {
1871 expect(element, attrName, content);
1872 }
1873 }
1874
1875 function expectWithoutText(element, attrName) {
1876 var content = getText(element);
1877 var hasBinding = content.indexOf($interpolate.startSymbol()) > -1;
1878
1879 if ( !hasBinding && !content) {
1880 expect(element, attrName, content);
1881 }
1882 }
1883
1884 function getText(element) {
1885 element = element[0] || element;
1886 var walker = document.createTreeWalker(element, NodeFilter.SHOW_TEXT, null, false);
1887 var text = '';
1888
1889 var node;
1890 while (node = walker.nextNode()) {
1891 if (!isAriaHiddenNode(node)) {
1892 text += node.textContent;
1893 }
1894 }
1895
1896 return text.trim() || '';
1897
1898 function isAriaHiddenNode(node) {
1899 while (node.parentNode && (node = node.parentNode) !== element) {
1900 if (node.getAttribute && node.getAttribute('aria-hidden') === 'true') {
1901 return true;
1902 }
1903 }
1904 }
1905 }
1906
1907 function childHasAttribute(node, attrName) {
1908 var hasChildren = node.hasChildNodes(),
1909 hasAttr = false;
1910
1911 function isHidden(el) {
1912 var style = el.currentStyle ? el.currentStyle : $window.getComputedStyle(el);
1913 return (style.display === 'none');
1914 }
1915
1916 if (hasChildren) {
1917 var children = node.childNodes;
1918 for (var i=0; i < children.length; i++) {
1919 var child = children[i];
1920 if (child.nodeType === 1 && child.hasAttribute(attrName)) {
1921 if (!isHidden(child)) {
1922 hasAttr = true;
1923 }
1924 }
1925 }
1926 }
1927
1928 return hasAttr;
1929 }
1930}
1931
1932
1933mdCompilerService.$inject = ["$q", "$templateRequest", "$injector", "$compile", "$controller"];angular
1934 .module('material.core')
1935 .service('$mdCompiler', mdCompilerService);
1936
1937function mdCompilerService($q, $templateRequest, $injector, $compile, $controller) {
1938 /* jshint validthis: true */
1939
1940 /*
1941 * @ngdoc service
1942 * @name $mdCompiler
1943 * @module material.core
1944 * @description
1945 * The $mdCompiler service is an abstraction of angular's compiler, that allows the developer
1946 * to easily compile an element with a templateUrl, controller, and locals.
1947 *
1948 * @usage
1949 * <hljs lang="js">
1950 * $mdCompiler.compile({
1951 * templateUrl: 'modal.html',
1952 * controller: 'ModalCtrl',
1953 * locals: {
1954 * modal: myModalInstance;
1955 * }
1956 * }).then(function(compileData) {
1957 * compileData.element; // modal.html's template in an element
1958 * compileData.link(myScope); //attach controller & scope to element
1959 * });
1960 * </hljs>
1961 */
1962
1963 /*
1964 * @ngdoc method
1965 * @name $mdCompiler#compile
1966 * @description A helper to compile an HTML template/templateUrl with a given controller,
1967 * locals, and scope.
1968 * @param {object} options An options object, with the following properties:
1969 *
1970 * - `controller` - `{(string=|function()=}` Controller fn that should be associated with
1971 * newly created scope or the name of a registered controller if passed as a string.
1972 * - `controllerAs` - `{string=}` A controller alias name. If present the controller will be
1973 * published to scope under the `controllerAs` name.
1974 * - `template` - `{string=}` An html template as a string.
1975 * - `templateUrl` - `{string=}` A path to an html template.
1976 * - `transformTemplate` - `{function(template)=}` A function which transforms the template after
1977 * it is loaded. It will be given the template string as a parameter, and should
1978 * return a a new string representing the transformed template.
1979 * - `resolve` - `{Object.<string, function>=}` - An optional map of dependencies which should
1980 * be injected into the controller. If any of these dependencies are promises, the compiler
1981 * will wait for them all to be resolved, or if one is rejected before the controller is
1982 * instantiated `compile()` will fail..
1983 * * `key` - `{string}`: a name of a dependency to be injected into the controller.
1984 * * `factory` - `{string|function}`: If `string` then it is an alias for a service.
1985 * Otherwise if function, then it is injected and the return value is treated as the
1986 * dependency. If the result is a promise, it is resolved before its value is
1987 * injected into the controller.
1988 *
1989 * @returns {object=} promise A promise, which will be resolved with a `compileData` object.
1990 * `compileData` has the following properties:
1991 *
1992 * - `element` - `{element}`: an uncompiled element matching the provided template.
1993 * - `link` - `{function(scope)}`: A link function, which, when called, will compile
1994 * the element and instantiate the provided controller (if given).
1995 * - `locals` - `{object}`: The locals which will be passed into the controller once `link` is
1996 * called. If `bindToController` is true, they will be coppied to the ctrl instead
1997 * - `bindToController` - `bool`: bind the locals to the controller, instead of passing them in.
1998 */
1999 this.compile = function(options) {
2000 var templateUrl = options.templateUrl;
2001 var template = options.template || '';
2002 var controller = options.controller;
2003 var controllerAs = options.controllerAs;
2004 var resolve = angular.extend({}, options.resolve || {});
2005 var locals = angular.extend({}, options.locals || {});
2006 var transformTemplate = options.transformTemplate || angular.identity;
2007 var bindToController = options.bindToController;
2008
2009 // Take resolve values and invoke them.
2010 // Resolves can either be a string (value: 'MyRegisteredAngularConst'),
2011 // or an invokable 'factory' of sorts: (value: function ValueGetter($dependency) {})
2012 angular.forEach(resolve, function(value, key) {
2013 if (angular.isString(value)) {
2014 resolve[key] = $injector.get(value);
2015 } else {
2016 resolve[key] = $injector.invoke(value);
2017 }
2018 });
2019 //Add the locals, which are just straight values to inject
2020 //eg locals: { three: 3 }, will inject three into the controller
2021 angular.extend(resolve, locals);
2022
2023 if (templateUrl) {
2024 resolve.$template = $templateRequest(templateUrl)
2025 .then(function(response) {
2026 return response;
2027 });
2028 } else {
2029 resolve.$template = $q.when(template);
2030 }
2031
2032 // Wait for all the resolves to finish if they are promises
2033 return $q.all(resolve).then(function(locals) {
2034
2035 var compiledData;
2036 var template = transformTemplate(locals.$template, options);
2037 var element = options.element || angular.element('<div>').html(template.trim()).contents();
2038 var linkFn = $compile(element);
2039
2040 // Return a linking function that can be used later when the element is ready
2041 return compiledData = {
2042 locals: locals,
2043 element: element,
2044 link: function link(scope) {
2045 locals.$scope = scope;
2046
2047 //Instantiate controller if it exists, because we have scope
2048 if (controller) {
2049 var invokeCtrl = $controller(controller, locals, true, controllerAs);
2050 if (bindToController) {
2051 angular.extend(invokeCtrl.instance, locals);
2052 }
2053 var ctrl = invokeCtrl();
2054 //See angular-route source for this logic
2055 element.data('$ngControllerController', ctrl);
2056 element.children().data('$ngControllerController', ctrl);
2057
2058 // Publish reference to this controller
2059 compiledData.controller = ctrl;
2060 }
2061 return linkFn(scope);
2062 }
2063 };
2064 });
2065
2066 };
2067}
2068
2069
2070MdGesture.$inject = ["$$MdGestureHandler", "$$rAF", "$timeout"];
2071attachToDocument.$inject = ["$mdGesture", "$$MdGestureHandler"];var HANDLERS = {};
2072
2073/* The state of the current 'pointer'
2074 * The pointer represents the state of the current touch.
2075 * It contains normalized x and y coordinates from DOM events,
2076 * as well as other information abstracted from the DOM.
2077 */
2078
2079var pointer, lastPointer, forceSkipClickHijack = false;
2080
2081/**
2082 * The position of the most recent click if that click was on a label element.
2083 * @type {{x: number, y: number}?}
2084 */
2085var lastLabelClickPos = null;
2086
2087// Used to attach event listeners once when multiple ng-apps are running.
2088var isInitialized = false;
2089
2090angular
2091 .module('material.core.gestures', [ ])
2092 .provider('$mdGesture', MdGestureProvider)
2093 .factory('$$MdGestureHandler', MdGestureHandler)
2094 .run( attachToDocument );
2095
2096/**
2097 * @ngdoc service
2098 * @name $mdGestureProvider
2099 * @module material.core.gestures
2100 *
2101 * @description
2102 * In some scenarios on Mobile devices (without jQuery), the click events should NOT be hijacked.
2103 * `$mdGestureProvider` is used to configure the Gesture module to ignore or skip click hijacking on mobile
2104 * devices.
2105 *
2106 * <hljs lang="js">
2107 * app.config(function($mdGestureProvider) {
2108 *
2109 * // For mobile devices without jQuery loaded, do not
2110 * // intercept click events during the capture phase.
2111 * $mdGestureProvider.skipClickHijack();
2112 *
2113 * });
2114 * </hljs>
2115 *
2116 */
2117function MdGestureProvider() { }
2118
2119MdGestureProvider.prototype = {
2120
2121 // Publish access to setter to configure a variable BEFORE the
2122 // $mdGesture service is instantiated...
2123 skipClickHijack: function() {
2124 return forceSkipClickHijack = true;
2125 },
2126
2127 /**
2128 * $get is used to build an instance of $mdGesture
2129 * ngInject
2130 */
2131 $get : ["$$MdGestureHandler", "$$rAF", "$timeout", function($$MdGestureHandler, $$rAF, $timeout) {
2132 return new MdGesture($$MdGestureHandler, $$rAF, $timeout);
2133 }]
2134};
2135
2136
2137
2138/**
2139 * MdGesture factory construction function
2140 * ngInject
2141 */
2142function MdGesture($$MdGestureHandler, $$rAF, $timeout) {
2143 var userAgent = navigator.userAgent || navigator.vendor || window.opera;
2144 var isIos = userAgent.match(/ipad|iphone|ipod/i);
2145 var isAndroid = userAgent.match(/android/i);
2146 var touchActionProperty = getTouchAction();
2147 var hasJQuery = (typeof window.jQuery !== 'undefined') && (angular.element === window.jQuery);
2148
2149 var self = {
2150 handler: addHandler,
2151 register: register,
2152 isIos: isIos,
2153 isAndroid: isAndroid,
2154 // On mobile w/out jQuery, we normally intercept clicks. Should we skip that?
2155 isHijackingClicks: (isIos || isAndroid) && !hasJQuery && !forceSkipClickHijack
2156 };
2157
2158 if (self.isHijackingClicks) {
2159 var maxClickDistance = 6;
2160 self.handler('click', {
2161 options: {
2162 maxDistance: maxClickDistance
2163 },
2164 onEnd: checkDistanceAndEmit('click')
2165 });
2166
2167 self.handler('focus', {
2168 options: {
2169 maxDistance: maxClickDistance
2170 },
2171 onEnd: function(ev, pointer) {
2172 if (pointer.distance < this.state.options.maxDistance) {
2173 if (canFocus(ev.target)) {
2174 this.dispatchEvent(ev, 'focus', pointer);
2175 ev.target.focus();
2176 }
2177 }
2178
2179 function canFocus(element) {
2180 var focusableElements = ['INPUT', 'SELECT', 'BUTTON', 'TEXTAREA', 'VIDEO', 'AUDIO'];
2181
2182 return (element.getAttribute('tabindex') != '-1') &&
2183 !element.hasAttribute('DISABLED') &&
2184 (element.hasAttribute('tabindex') || element.hasAttribute('href') || element.isContentEditable ||
2185 (focusableElements.indexOf(element.nodeName) != -1));
2186 }
2187 }
2188 });
2189
2190 self.handler('mouseup', {
2191 options: {
2192 maxDistance: maxClickDistance
2193 },
2194 onEnd: checkDistanceAndEmit('mouseup')
2195 });
2196
2197 self.handler('mousedown', {
2198 onStart: function(ev) {
2199 this.dispatchEvent(ev, 'mousedown');
2200 }
2201 });
2202 }
2203
2204 function checkDistanceAndEmit(eventName) {
2205 return function(ev, pointer) {
2206 if (pointer.distance < this.state.options.maxDistance) {
2207 this.dispatchEvent(ev, eventName, pointer);
2208 }
2209 };
2210 }
2211
2212 /*
2213 * Register an element to listen for a handler.
2214 * This allows an element to override the default options for a handler.
2215 * Additionally, some handlers like drag and hold only dispatch events if
2216 * the domEvent happens inside an element that's registered to listen for these events.
2217 *
2218 * @see GestureHandler for how overriding of default options works.
2219 * @example $mdGesture.register(myElement, 'drag', { minDistance: 20, horziontal: false })
2220 */
2221 function register(element, handlerName, options) {
2222 var handler = HANDLERS[handlerName.replace(/^\$md./, '')];
2223 if (!handler) {
2224 throw new Error('Failed to register element with handler ' + handlerName + '. ' +
2225 'Available handlers: ' + Object.keys(HANDLERS).join(', '));
2226 }
2227 return handler.registerElement(element, options);
2228 }
2229
2230 /*
2231 * add a handler to $mdGesture. see below.
2232 */
2233 function addHandler(name, definition) {
2234 var handler = new $$MdGestureHandler(name);
2235 angular.extend(handler, definition);
2236 HANDLERS[name] = handler;
2237
2238 return self;
2239 }
2240
2241 /*
2242 * Register handlers. These listen to touch/start/move events, interpret them,
2243 * and dispatch gesture events depending on options & conditions. These are all
2244 * instances of GestureHandler.
2245 * @see GestureHandler
2246 */
2247 return self
2248 /*
2249 * The press handler dispatches an event on touchdown/touchend.
2250 * It's a simple abstraction of touch/mouse/pointer start and end.
2251 */
2252 .handler('press', {
2253 onStart: function (ev, pointer) {
2254 this.dispatchEvent(ev, '$md.pressdown');
2255 },
2256 onEnd: function (ev, pointer) {
2257 this.dispatchEvent(ev, '$md.pressup');
2258 }
2259 })
2260
2261 /*
2262 * The hold handler dispatches an event if the user keeps their finger within
2263 * the same <maxDistance> area for <delay> ms.
2264 * The hold handler will only run if a parent of the touch target is registered
2265 * to listen for hold events through $mdGesture.register()
2266 */
2267 .handler('hold', {
2268 options: {
2269 maxDistance: 6,
2270 delay: 500
2271 },
2272 onCancel: function () {
2273 $timeout.cancel(this.state.timeout);
2274 },
2275 onStart: function (ev, pointer) {
2276 // For hold, require a parent to be registered with $mdGesture.register()
2277 // Because we prevent scroll events, this is necessary.
2278 if (!this.state.registeredParent) return this.cancel();
2279
2280 this.state.pos = {x: pointer.x, y: pointer.y};
2281 this.state.timeout = $timeout(angular.bind(this, function holdDelayFn() {
2282 this.dispatchEvent(ev, '$md.hold');
2283 this.cancel(); //we're done!
2284 }), this.state.options.delay, false);
2285 },
2286 onMove: function (ev, pointer) {
2287 // Don't scroll while waiting for hold.
2288 // If we don't preventDefault touchmove events here, Android will assume we don't
2289 // want to listen to anymore touch events. It will start scrolling and stop sending
2290 // touchmove events.
2291 if (!touchActionProperty && ev.type === 'touchmove') ev.preventDefault();
2292
2293 // If the user moves greater than <maxDistance> pixels, stop the hold timer
2294 // set in onStart
2295 var dx = this.state.pos.x - pointer.x;
2296 var dy = this.state.pos.y - pointer.y;
2297 if (Math.sqrt(dx * dx + dy * dy) > this.options.maxDistance) {
2298 this.cancel();
2299 }
2300 },
2301 onEnd: function () {
2302 this.onCancel();
2303 }
2304 })
2305
2306 /*
2307 * The drag handler dispatches a drag event if the user holds and moves his finger greater than
2308 * <minDistance> px in the x or y direction, depending on options.horizontal.
2309 * The drag will be cancelled if the user moves his finger greater than <minDistance>*<cancelMultiplier> in
2310 * the perpendicular direction. Eg if the drag is horizontal and the user moves his finger <minDistance>*<cancelMultiplier>
2311 * pixels vertically, this handler won't consider the move part of a drag.
2312 */
2313 .handler('drag', {
2314 options: {
2315 minDistance: 6,
2316 horizontal: true,
2317 cancelMultiplier: 1.5
2318 },
2319 onSetup: function(element, options) {
2320 if (touchActionProperty) {
2321 // We check for horizontal to be false, because otherwise we would overwrite the default opts.
2322 this.oldTouchAction = element[0].style[touchActionProperty];
2323 element[0].style[touchActionProperty] = options.horizontal === false ? 'pan-y' : 'pan-x';
2324 }
2325 },
2326 onCleanup: function(element) {
2327 if (this.oldTouchAction) {
2328 element[0].style[touchActionProperty] = this.oldTouchAction;
2329 }
2330 },
2331 onStart: function (ev) {
2332 // For drag, require a parent to be registered with $mdGesture.register()
2333 if (!this.state.registeredParent) this.cancel();
2334 },
2335 onMove: function (ev, pointer) {
2336 var shouldStartDrag, shouldCancel;
2337 // Don't scroll while deciding if this touchmove qualifies as a drag event.
2338 // If we don't preventDefault touchmove events here, Android will assume we don't
2339 // want to listen to anymore touch events. It will start scrolling and stop sending
2340 // touchmove events.
2341 if (!touchActionProperty && ev.type === 'touchmove') ev.preventDefault();
2342
2343 if (!this.state.dragPointer) {
2344 if (this.state.options.horizontal) {
2345 shouldStartDrag = Math.abs(pointer.distanceX) > this.state.options.minDistance;
2346 shouldCancel = Math.abs(pointer.distanceY) > this.state.options.minDistance * this.state.options.cancelMultiplier;
2347 } else {
2348 shouldStartDrag = Math.abs(pointer.distanceY) > this.state.options.minDistance;
2349 shouldCancel = Math.abs(pointer.distanceX) > this.state.options.minDistance * this.state.options.cancelMultiplier;
2350 }
2351
2352 if (shouldStartDrag) {
2353 // Create a new pointer representing this drag, starting at this point where the drag started.
2354 this.state.dragPointer = makeStartPointer(ev);
2355 updatePointerState(ev, this.state.dragPointer);
2356 this.dispatchEvent(ev, '$md.dragstart', this.state.dragPointer);
2357
2358 } else if (shouldCancel) {
2359 this.cancel();
2360 }
2361 } else {
2362 this.dispatchDragMove(ev);
2363 }
2364 },
2365 // Only dispatch dragmove events every frame; any more is unnecessary
2366 dispatchDragMove: $$rAF.throttle(function (ev) {
2367 // Make sure the drag didn't stop while waiting for the next frame
2368 if (this.state.isRunning) {
2369 updatePointerState(ev, this.state.dragPointer);
2370 this.dispatchEvent(ev, '$md.drag', this.state.dragPointer);
2371 }
2372 }),
2373 onEnd: function (ev, pointer) {
2374 if (this.state.dragPointer) {
2375 updatePointerState(ev, this.state.dragPointer);
2376 this.dispatchEvent(ev, '$md.dragend', this.state.dragPointer);
2377 }
2378 }
2379 })
2380
2381 /*
2382 * The swipe handler will dispatch a swipe event if, on the end of a touch,
2383 * the velocity and distance were high enough.
2384 */
2385 .handler('swipe', {
2386 options: {
2387 minVelocity: 0.65,
2388 minDistance: 10
2389 },
2390 onEnd: function (ev, pointer) {
2391 var eventType;
2392
2393 if (Math.abs(pointer.velocityX) > this.state.options.minVelocity &&
2394 Math.abs(pointer.distanceX) > this.state.options.minDistance) {
2395 eventType = pointer.directionX == 'left' ? '$md.swipeleft' : '$md.swiperight';
2396 this.dispatchEvent(ev, eventType);
2397 }
2398 else if (Math.abs(pointer.velocityY) > this.state.options.minVelocity &&
2399 Math.abs(pointer.distanceY) > this.state.options.minDistance) {
2400 eventType = pointer.directionY == 'up' ? '$md.swipeup' : '$md.swipedown';
2401 this.dispatchEvent(ev, eventType);
2402 }
2403 }
2404 });
2405
2406 function getTouchAction() {
2407 var testEl = document.createElement('div');
2408 var vendorPrefixes = ['', 'webkit', 'Moz', 'MS', 'ms', 'o'];
2409
2410 for (var i = 0; i < vendorPrefixes.length; i++) {
2411 var prefix = vendorPrefixes[i];
2412 var property = prefix ? prefix + 'TouchAction' : 'touchAction';
2413 if (angular.isDefined(testEl.style[property])) {
2414 return property;
2415 }
2416 }
2417 }
2418
2419}
2420
2421/**
2422 * MdGestureHandler
2423 * A GestureHandler is an object which is able to dispatch custom dom events
2424 * based on native dom {touch,pointer,mouse}{start,move,end} events.
2425 *
2426 * A gesture will manage its lifecycle through the start,move,end, and cancel
2427 * functions, which are called by native dom events.
2428 *
2429 * A gesture has the concept of 'options' (eg a swipe's required velocity), which can be
2430 * overridden by elements registering through $mdGesture.register()
2431 */
2432function GestureHandler (name) {
2433 this.name = name;
2434 this.state = {};
2435}
2436
2437function MdGestureHandler() {
2438 var hasJQuery = (typeof window.jQuery !== 'undefined') && (angular.element === window.jQuery);
2439
2440 GestureHandler.prototype = {
2441 options: {},
2442 // jQuery listeners don't work with custom DOMEvents, so we have to dispatch events
2443 // differently when jQuery is loaded
2444 dispatchEvent: hasJQuery ? jQueryDispatchEvent : nativeDispatchEvent,
2445
2446 // These are overridden by the registered handler
2447 onSetup: angular.noop,
2448 onCleanup: angular.noop,
2449 onStart: angular.noop,
2450 onMove: angular.noop,
2451 onEnd: angular.noop,
2452 onCancel: angular.noop,
2453
2454 // onStart sets up a new state for the handler, which includes options from the
2455 // nearest registered parent element of ev.target.
2456 start: function (ev, pointer) {
2457 if (this.state.isRunning) return;
2458 var parentTarget = this.getNearestParent(ev.target);
2459 // Get the options from the nearest registered parent
2460 var parentTargetOptions = parentTarget && parentTarget.$mdGesture[this.name] || {};
2461
2462 this.state = {
2463 isRunning: true,
2464 // Override the default options with the nearest registered parent's options
2465 options: angular.extend({}, this.options, parentTargetOptions),
2466 // Pass in the registered parent node to the state so the onStart listener can use
2467 registeredParent: parentTarget
2468 };
2469 this.onStart(ev, pointer);
2470 },
2471 move: function (ev, pointer) {
2472 if (!this.state.isRunning) return;
2473 this.onMove(ev, pointer);
2474 },
2475 end: function (ev, pointer) {
2476 if (!this.state.isRunning) return;
2477 this.onEnd(ev, pointer);
2478 this.state.isRunning = false;
2479 },
2480 cancel: function (ev, pointer) {
2481 this.onCancel(ev, pointer);
2482 this.state = {};
2483 },
2484
2485 // Find and return the nearest parent element that has been registered to
2486 // listen for this handler via $mdGesture.register(element, 'handlerName').
2487 getNearestParent: function (node) {
2488 var current = node;
2489 while (current) {
2490 if ((current.$mdGesture || {})[this.name]) {
2491 return current;
2492 }
2493 current = current.parentNode;
2494 }
2495 return null;
2496 },
2497
2498 // Called from $mdGesture.register when an element registers itself with a handler.
2499 // Store the options the user gave on the DOMElement itself. These options will
2500 // be retrieved with getNearestParent when the handler starts.
2501 registerElement: function (element, options) {
2502 var self = this;
2503 element[0].$mdGesture = element[0].$mdGesture || {};
2504 element[0].$mdGesture[this.name] = options || {};
2505 element.on('$destroy', onDestroy);
2506
2507 self.onSetup(element, options || {});
2508
2509 return onDestroy;
2510
2511 function onDestroy() {
2512 delete element[0].$mdGesture[self.name];
2513 element.off('$destroy', onDestroy);
2514
2515 self.onCleanup(element, options || {});
2516 }
2517 }
2518 };
2519
2520 return GestureHandler;
2521
2522 /*
2523 * Dispatch an event with jQuery
2524 * TODO: Make sure this sends bubbling events
2525 *
2526 * @param srcEvent the original DOM touch event that started this.
2527 * @param eventType the name of the custom event to send (eg 'click' or '$md.drag')
2528 * @param eventPointer the pointer object that matches this event.
2529 */
2530 function jQueryDispatchEvent(srcEvent, eventType, eventPointer) {
2531 eventPointer = eventPointer || pointer;
2532 var eventObj = new angular.element.Event(eventType);
2533
2534 eventObj.$material = true;
2535 eventObj.pointer = eventPointer;
2536 eventObj.srcEvent = srcEvent;
2537
2538 angular.extend(eventObj, {
2539 clientX: eventPointer.x,
2540 clientY: eventPointer.y,
2541 screenX: eventPointer.x,
2542 screenY: eventPointer.y,
2543 pageX: eventPointer.x,
2544 pageY: eventPointer.y,
2545 ctrlKey: srcEvent.ctrlKey,
2546 altKey: srcEvent.altKey,
2547 shiftKey: srcEvent.shiftKey,
2548 metaKey: srcEvent.metaKey
2549 });
2550 angular.element(eventPointer.target).trigger(eventObj);
2551 }
2552
2553 /*
2554 * NOTE: nativeDispatchEvent is very performance sensitive.
2555 * @param srcEvent the original DOM touch event that started this.
2556 * @param eventType the name of the custom event to send (eg 'click' or '$md.drag')
2557 * @param eventPointer the pointer object that matches this event.
2558 */
2559 function nativeDispatchEvent(srcEvent, eventType, eventPointer) {
2560 eventPointer = eventPointer || pointer;
2561 var eventObj;
2562
2563 if (eventType === 'click' || eventType == 'mouseup' || eventType == 'mousedown' ) {
2564 eventObj = document.createEvent('MouseEvents');
2565 eventObj.initMouseEvent(
2566 eventType, true, true, window, srcEvent.detail,
2567 eventPointer.x, eventPointer.y, eventPointer.x, eventPointer.y,
2568 srcEvent.ctrlKey, srcEvent.altKey, srcEvent.shiftKey, srcEvent.metaKey,
2569 srcEvent.button, srcEvent.relatedTarget || null
2570 );
2571
2572 } else {
2573 eventObj = document.createEvent('CustomEvent');
2574 eventObj.initCustomEvent(eventType, true, true, {});
2575 }
2576 eventObj.$material = true;
2577 eventObj.pointer = eventPointer;
2578 eventObj.srcEvent = srcEvent;
2579 eventPointer.target.dispatchEvent(eventObj);
2580 }
2581
2582}
2583
2584/**
2585 * Attach Gestures: hook document and check shouldHijack clicks
2586 * ngInject
2587 */
2588function attachToDocument( $mdGesture, $$MdGestureHandler ) {
2589
2590 // Polyfill document.contains for IE11.
2591 // TODO: move to util
2592 document.contains || (document.contains = function (node) {
2593 return document.body.contains(node);
2594 });
2595
2596 if (!isInitialized && $mdGesture.isHijackingClicks ) {
2597 /*
2598 * If hijack clicks is true, we preventDefault any click that wasn't
2599 * sent by ngMaterial. This is because on older Android & iOS, a false, or 'ghost',
2600 * click event will be sent ~400ms after a touchend event happens.
2601 * The only way to know if this click is real is to prevent any normal
2602 * click events, and add a flag to events sent by material so we know not to prevent those.
2603 *
2604 * Two exceptions to click events that should be prevented are:
2605 * - click events sent by the keyboard (eg form submit)
2606 * - events that originate from an Ionic app
2607 */
2608 document.addEventListener('click' , clickHijacker , true);
2609 document.addEventListener('mouseup' , mouseInputHijacker, true);
2610 document.addEventListener('mousedown', mouseInputHijacker, true);
2611 document.addEventListener('focus' , mouseInputHijacker, true);
2612
2613 isInitialized = true;
2614 }
2615
2616 function mouseInputHijacker(ev) {
2617 var isKeyClick = !ev.clientX && !ev.clientY;
2618 if (!isKeyClick && !ev.$material && !ev.isIonicTap
2619 && !isInputEventFromLabelClick(ev)) {
2620 ev.preventDefault();
2621 ev.stopPropagation();
2622 }
2623 }
2624
2625 function clickHijacker(ev) {
2626 var isKeyClick = ev.clientX === 0 && ev.clientY === 0;
2627 if (!isKeyClick && !ev.$material && !ev.isIonicTap
2628 && !isInputEventFromLabelClick(ev)) {
2629 ev.preventDefault();
2630 ev.stopPropagation();
2631 lastLabelClickPos = null;
2632 } else {
2633 lastLabelClickPos = null;
2634 if (ev.target.tagName.toLowerCase() == 'label') {
2635 lastLabelClickPos = {x: ev.x, y: ev.y};
2636 }
2637 }
2638 }
2639
2640
2641 // Listen to all events to cover all platforms.
2642 var START_EVENTS = 'mousedown touchstart pointerdown';
2643 var MOVE_EVENTS = 'mousemove touchmove pointermove';
2644 var END_EVENTS = 'mouseup mouseleave touchend touchcancel pointerup pointercancel';
2645
2646 angular.element(document)
2647 .on(START_EVENTS, gestureStart)
2648 .on(MOVE_EVENTS, gestureMove)
2649 .on(END_EVENTS, gestureEnd)
2650 // For testing
2651 .on('$$mdGestureReset', function gestureClearCache () {
2652 lastPointer = pointer = null;
2653 });
2654
2655 /*
2656 * When a DOM event happens, run all registered gesture handlers' lifecycle
2657 * methods which match the DOM event.
2658 * Eg when a 'touchstart' event happens, runHandlers('start') will call and
2659 * run `handler.cancel()` and `handler.start()` on all registered handlers.
2660 */
2661 function runHandlers(handlerEvent, event) {
2662 var handler;
2663 for (var name in HANDLERS) {
2664 handler = HANDLERS[name];
2665 if( handler instanceof $$MdGestureHandler ) {
2666
2667 if (handlerEvent === 'start') {
2668 // Run cancel to reset any handlers' state
2669 handler.cancel();
2670 }
2671 handler[handlerEvent](event, pointer);
2672
2673 }
2674 }
2675 }
2676
2677 /*
2678 * gestureStart vets if a start event is legitimate (and not part of a 'ghost click' from iOS/Android)
2679 * If it is legitimate, we initiate the pointer state and mark the current pointer's type
2680 * For example, for a touchstart event, mark the current pointer as a 'touch' pointer, so mouse events
2681 * won't effect it.
2682 */
2683 function gestureStart(ev) {
2684 // If we're already touched down, abort
2685 if (pointer) return;
2686
2687 var now = +Date.now();
2688
2689 // iOS & old android bug: after a touch event, a click event is sent 350 ms later.
2690 // If <400ms have passed, don't allow an event of a different type than the previous event
2691 if (lastPointer && !typesMatch(ev, lastPointer) && (now - lastPointer.endTime < 1500)) {
2692 return;
2693 }
2694
2695 pointer = makeStartPointer(ev);
2696
2697 runHandlers('start', ev);
2698 }
2699 /*
2700 * If a move event happens of the right type, update the pointer and run all the move handlers.
2701 * "of the right type": if a mousemove happens but our pointer started with a touch event, do nothing.
2702 */
2703 function gestureMove(ev) {
2704 if (!pointer || !typesMatch(ev, pointer)) return;
2705
2706 updatePointerState(ev, pointer);
2707 runHandlers('move', ev);
2708 }
2709 /*
2710 * If an end event happens of the right type, update the pointer, run endHandlers, and save the pointer as 'lastPointer'
2711 */
2712 function gestureEnd(ev) {
2713 if (!pointer || !typesMatch(ev, pointer)) return;
2714
2715 updatePointerState(ev, pointer);
2716 pointer.endTime = +Date.now();
2717
2718 runHandlers('end', ev);
2719
2720 lastPointer = pointer;
2721 pointer = null;
2722 }
2723
2724}
2725
2726// ********************
2727// Module Functions
2728// ********************
2729
2730/*
2731 * Initiate the pointer. x, y, and the pointer's type.
2732 */
2733function makeStartPointer(ev) {
2734 var point = getEventPoint(ev);
2735 var startPointer = {
2736 startTime: +Date.now(),
2737 target: ev.target,
2738 // 'p' for pointer events, 'm' for mouse, 't' for touch
2739 type: ev.type.charAt(0)
2740 };
2741 startPointer.startX = startPointer.x = point.pageX;
2742 startPointer.startY = startPointer.y = point.pageY;
2743 return startPointer;
2744}
2745
2746/*
2747 * return whether the pointer's type matches the event's type.
2748 * Eg if a touch event happens but the pointer has a mouse type, return false.
2749 */
2750function typesMatch(ev, pointer) {
2751 return ev && pointer && ev.type.charAt(0) === pointer.type;
2752}
2753
2754/**
2755 * Gets whether the given event is an input event that was caused by clicking on an
2756 * associated label element.
2757 *
2758 * This is necessary because the browser will, upon clicking on a label element, fire an
2759 * *extra* click event on its associated input (if any). mdGesture is able to flag the label
2760 * click as with `$material` correctly, but not the second input click.
2761 *
2762 * In order to determine whether an input event is from a label click, we compare the (x, y) for
2763 * the event to the (x, y) for the most recent label click (which is cleared whenever a non-label
2764 * click occurs). Unfortunately, there are no event properties that tie the input and the label
2765 * together (such as relatedTarget).
2766 *
2767 * @param {MouseEvent} event
2768 * @returns {boolean}
2769 */
2770function isInputEventFromLabelClick(event) {
2771 return lastLabelClickPos
2772 && lastLabelClickPos.x == event.x
2773 && lastLabelClickPos.y == event.y;
2774}
2775
2776/*
2777 * Update the given pointer based upon the given DOMEvent.
2778 * Distance, velocity, direction, duration, etc
2779 */
2780function updatePointerState(ev, pointer) {
2781 var point = getEventPoint(ev);
2782 var x = pointer.x = point.pageX;
2783 var y = pointer.y = point.pageY;
2784
2785 pointer.distanceX = x - pointer.startX;
2786 pointer.distanceY = y - pointer.startY;
2787 pointer.distance = Math.sqrt(
2788 pointer.distanceX * pointer.distanceX + pointer.distanceY * pointer.distanceY
2789 );
2790
2791 pointer.directionX = pointer.distanceX > 0 ? 'right' : pointer.distanceX < 0 ? 'left' : '';
2792 pointer.directionY = pointer.distanceY > 0 ? 'down' : pointer.distanceY < 0 ? 'up' : '';
2793
2794 pointer.duration = +Date.now() - pointer.startTime;
2795 pointer.velocityX = pointer.distanceX / pointer.duration;
2796 pointer.velocityY = pointer.distanceY / pointer.duration;
2797}
2798
2799/*
2800 * Normalize the point where the DOM event happened whether it's touch or mouse.
2801 * @returns point event obj with pageX and pageY on it.
2802 */
2803function getEventPoint(ev) {
2804 ev = ev.originalEvent || ev; // support jQuery events
2805 return (ev.touches && ev.touches[0]) ||
2806 (ev.changedTouches && ev.changedTouches[0]) ||
2807 ev;
2808}
2809
2810angular.module('material.core')
2811 .provider('$$interimElement', InterimElementProvider);
2812
2813/*
2814 * @ngdoc service
2815 * @name $$interimElement
2816 * @module material.core
2817 *
2818 * @description
2819 *
2820 * Factory that contructs `$$interimElement.$service` services.
2821 * Used internally in material design for elements that appear on screen temporarily.
2822 * The service provides a promise-like API for interacting with the temporary
2823 * elements.
2824 *
2825 * ```js
2826 * app.service('$mdToast', function($$interimElement) {
2827 * var $mdToast = $$interimElement(toastDefaultOptions);
2828 * return $mdToast;
2829 * });
2830 * ```
2831 * @param {object=} defaultOptions Options used by default for the `show` method on the service.
2832 *
2833 * @returns {$$interimElement.$service}
2834 *
2835 */
2836
2837function InterimElementProvider() {
2838 InterimElementFactory.$inject = ["$document", "$q", "$$q", "$rootScope", "$timeout", "$rootElement", "$animate", "$mdUtil", "$mdCompiler", "$mdTheming", "$injector"];
2839 createInterimElementProvider.$get = InterimElementFactory;
2840 return createInterimElementProvider;
2841
2842 /**
2843 * Returns a new provider which allows configuration of a new interimElement
2844 * service. Allows configuration of default options & methods for options,
2845 * as well as configuration of 'preset' methods (eg dialog.basic(): basic is a preset method)
2846 */
2847 function createInterimElementProvider(interimFactoryName) {
2848 factory.$inject = ["$$interimElement", "$injector"];
2849 var EXPOSED_METHODS = ['onHide', 'onShow', 'onRemove'];
2850
2851 var customMethods = {};
2852 var providerConfig = {
2853 presets: {}
2854 };
2855
2856 var provider = {
2857 setDefaults: setDefaults,
2858 addPreset: addPreset,
2859 addMethod: addMethod,
2860 $get: factory
2861 };
2862
2863 /**
2864 * all interim elements will come with the 'build' preset
2865 */
2866 provider.addPreset('build', {
2867 methods: ['controller', 'controllerAs', 'resolve',
2868 'template', 'templateUrl', 'themable', 'transformTemplate', 'parent']
2869 });
2870
2871 return provider;
2872
2873 /**
2874 * Save the configured defaults to be used when the factory is instantiated
2875 */
2876 function setDefaults(definition) {
2877 providerConfig.optionsFactory = definition.options;
2878 providerConfig.methods = (definition.methods || []).concat(EXPOSED_METHODS);
2879 return provider;
2880 }
2881
2882 /**
2883 * Add a method to the factory that isn't specific to any interim element operations
2884 */
2885
2886 function addMethod(name, fn) {
2887 customMethods[name] = fn;
2888 return provider;
2889 }
2890
2891 /**
2892 * Save the configured preset to be used when the factory is instantiated
2893 */
2894 function addPreset(name, definition) {
2895 definition = definition || {};
2896 definition.methods = definition.methods || [];
2897 definition.options = definition.options || function() { return {}; };
2898
2899 if (/^cancel|hide|show$/.test(name)) {
2900 throw new Error("Preset '" + name + "' in " + interimFactoryName + " is reserved!");
2901 }
2902 if (definition.methods.indexOf('_options') > -1) {
2903 throw new Error("Method '_options' in " + interimFactoryName + " is reserved!");
2904 }
2905 providerConfig.presets[name] = {
2906 methods: definition.methods.concat(EXPOSED_METHODS),
2907 optionsFactory: definition.options,
2908 argOption: definition.argOption
2909 };
2910 return provider;
2911 }
2912
2913 function addPresetMethod(presetName, methodName, method) {
2914 providerConfig.presets[presetName][methodName] = method;
2915 }
2916
2917 /**
2918 * Create a factory that has the given methods & defaults implementing interimElement
2919 */
2920 /* ngInject */
2921 function factory($$interimElement, $injector) {
2922 var defaultMethods;
2923 var defaultOptions;
2924 var interimElementService = $$interimElement();
2925
2926 /*
2927 * publicService is what the developer will be using.
2928 * It has methods hide(), cancel(), show(), build(), and any other
2929 * presets which were set during the config phase.
2930 */
2931 var publicService = {
2932 hide: interimElementService.hide,
2933 cancel: interimElementService.cancel,
2934 show: showInterimElement,
2935
2936 // Special internal method to destroy an interim element without animations
2937 // used when navigation changes causes a $scope.$destroy() action
2938 destroy : destroyInterimElement
2939 };
2940
2941
2942 defaultMethods = providerConfig.methods || [];
2943 // This must be invoked after the publicService is initialized
2944 defaultOptions = invokeFactory(providerConfig.optionsFactory, {});
2945
2946 // Copy over the simple custom methods
2947 angular.forEach(customMethods, function(fn, name) {
2948 publicService[name] = fn;
2949 });
2950
2951 angular.forEach(providerConfig.presets, function(definition, name) {
2952 var presetDefaults = invokeFactory(definition.optionsFactory, {});
2953 var presetMethods = (definition.methods || []).concat(defaultMethods);
2954
2955 // Every interimElement built with a preset has a field called `$type`,
2956 // which matches the name of the preset.
2957 // Eg in preset 'confirm', options.$type === 'confirm'
2958 angular.extend(presetDefaults, { $type: name });
2959
2960 // This creates a preset class which has setter methods for every
2961 // method given in the `.addPreset()` function, as well as every
2962 // method given in the `.setDefaults()` function.
2963 //
2964 // @example
2965 // .setDefaults({
2966 // methods: ['hasBackdrop', 'clickOutsideToClose', 'escapeToClose', 'targetEvent'],
2967 // options: dialogDefaultOptions
2968 // })
2969 // .addPreset('alert', {
2970 // methods: ['title', 'ok'],
2971 // options: alertDialogOptions
2972 // })
2973 //
2974 // Set values will be passed to the options when interimElement.show() is called.
2975 function Preset(opts) {
2976 this._options = angular.extend({}, presetDefaults, opts);
2977 }
2978 angular.forEach(presetMethods, function(name) {
2979 Preset.prototype[name] = function(value) {
2980 this._options[name] = value;
2981 return this;
2982 };
2983 });
2984
2985 // Create shortcut method for one-linear methods
2986 if (definition.argOption) {
2987 var methodName = 'show' + name.charAt(0).toUpperCase() + name.slice(1);
2988 publicService[methodName] = function(arg) {
2989 var config = publicService[name](arg);
2990 return publicService.show(config);
2991 };
2992 }
2993
2994 // eg $mdDialog.alert() will return a new alert preset
2995 publicService[name] = function(arg) {
2996 // If argOption is supplied, eg `argOption: 'content'`, then we assume
2997 // if the argument is not an options object then it is the `argOption` option.
2998 //
2999 // @example `$mdToast.simple('hello')` // sets options.content to hello
3000 // // because argOption === 'content'
3001 if (arguments.length && definition.argOption &&
3002 !angular.isObject(arg) && !angular.isArray(arg)) {
3003
3004 return (new Preset())[definition.argOption](arg);
3005
3006 } else {
3007 return new Preset(arg);
3008 }
3009
3010 };
3011 });
3012
3013 return publicService;
3014
3015 /**
3016 *
3017 */
3018 function showInterimElement(opts) {
3019 // opts is either a preset which stores its options on an _options field,
3020 // or just an object made up of options
3021 opts = opts || { };
3022 if (opts._options) opts = opts._options;
3023
3024 return interimElementService.show(
3025 angular.extend({}, defaultOptions, opts)
3026 );
3027 }
3028
3029 /**
3030 * Special method to hide and destroy an interimElement WITHOUT
3031 * any 'leave` or hide animations ( an immediate force hide/remove )
3032 *
3033 * NOTE: This calls the onRemove() subclass method for each component...
3034 * which must have code to respond to `options.$destroy == true`
3035 */
3036 function destroyInterimElement(opts) {
3037 return interimElementService.destroy(opts);
3038 }
3039
3040 /**
3041 * Helper to call $injector.invoke with a local of the factory name for
3042 * this provider.
3043 * If an $mdDialog is providing options for a dialog and tries to inject
3044 * $mdDialog, a circular dependency error will happen.
3045 * We get around that by manually injecting $mdDialog as a local.
3046 */
3047 function invokeFactory(factory, defaultVal) {
3048 var locals = {};
3049 locals[interimFactoryName] = publicService;
3050 return $injector.invoke(factory || function() { return defaultVal; }, {}, locals);
3051 }
3052
3053 }
3054
3055 }
3056
3057 /* ngInject */
3058 function InterimElementFactory($document, $q, $$q, $rootScope, $timeout, $rootElement, $animate,
3059 $mdUtil, $mdCompiler, $mdTheming, $injector ) {
3060 return function createInterimElementService() {
3061 var SHOW_CANCELLED = false;
3062
3063 /*
3064 * @ngdoc service
3065 * @name $$interimElement.$service
3066 *
3067 * @description
3068 * A service used to control inserting and removing an element into the DOM.
3069 *
3070 */
3071 var service, stack = [];
3072
3073 // Publish instance $$interimElement service;
3074 // ... used as $mdDialog, $mdToast, $mdMenu, and $mdSelect
3075
3076 return service = {
3077 show: show,
3078 hide: hide,
3079 cancel: cancel,
3080 destroy : destroy,
3081 $injector_: $injector
3082 };
3083
3084 /*
3085 * @ngdoc method
3086 * @name $$interimElement.$service#show
3087 * @kind function
3088 *
3089 * @description
3090 * Adds the `$interimElement` to the DOM and returns a special promise that will be resolved or rejected
3091 * with hide or cancel, respectively. To external cancel/hide, developers should use the
3092 *
3093 * @param {*} options is hashMap of settings
3094 * @returns a Promise
3095 *
3096 */
3097 function show(options) {
3098 options = options || {};
3099 var interimElement = new InterimElement(options || {});
3100 // When an interim element is currently showing, we have to cancel it.
3101 // Just hiding it, will resolve the InterimElement's promise, the promise should be
3102 // rejected instead.
3103 var hideExisting = !options.skipHide && stack.length ? service.cancel() : $q.when(true);
3104
3105 // This hide()s only the current interim element before showing the next, new one
3106 // NOTE: this is not reversible (e.g. interim elements are not stackable)
3107
3108 hideExisting.finally(function() {
3109
3110 stack.push(interimElement);
3111 interimElement
3112 .show()
3113 .catch(function( reason ) {
3114 //$log.error("InterimElement.show() error: " + reason );
3115 return reason;
3116 });
3117
3118 });
3119
3120 // Return a promise that will be resolved when the interim
3121 // element is hidden or cancelled...
3122
3123 return interimElement.deferred.promise;
3124 }
3125
3126 /*
3127 * @ngdoc method
3128 * @name $$interimElement.$service#hide
3129 * @kind function
3130 *
3131 * @description
3132 * Removes the `$interimElement` from the DOM and resolves the promise returned from `show`
3133 *
3134 * @param {*} resolveParam Data to resolve the promise with
3135 * @returns a Promise that will be resolved after the element has been removed.
3136 *
3137 */
3138 function hide(reason, options) {
3139 if ( !stack.length ) return $q.when(reason);
3140 options = options || {};
3141
3142 if (options.closeAll) {
3143 var promise = $q.all(stack.reverse().map(closeElement));
3144 stack = [];
3145 return promise;
3146 } else if (options.closeTo !== undefined) {
3147 return $q.all(stack.splice(options.closeTo).map(closeElement));
3148 } else {
3149 var interim = stack.pop();
3150 return closeElement(interim);
3151 }
3152
3153 function closeElement(interim) {
3154 interim
3155 .remove(reason, false, options || { })
3156 .catch(function( reason ) {
3157 //$log.error("InterimElement.hide() error: " + reason );
3158 return reason;
3159 });
3160 return interim.deferred.promise;
3161 }
3162 }
3163
3164 /*
3165 * @ngdoc method
3166 * @name $$interimElement.$service#cancel
3167 * @kind function
3168 *
3169 * @description
3170 * Removes the `$interimElement` from the DOM and rejects the promise returned from `show`
3171 *
3172 * @param {*} reason Data to reject the promise with
3173 * @returns Promise that will be resolved after the element has been removed.
3174 *
3175 */
3176 function cancel(reason, options) {
3177 var interim = stack.pop();
3178 if ( !interim ) return $q.when(reason);
3179
3180 interim
3181 .remove(reason, true, options || { })
3182 .catch(function( reason ) {
3183 //$log.error("InterimElement.cancel() error: " + reason );
3184 return reason;
3185 });
3186
3187 // Since Angular 1.6.7, promises will be logged to $exceptionHandler when the promise
3188 // is not handling the rejection. We create a pseudo catch handler, which will prevent the
3189 // promise from being logged to the $exceptionHandler.
3190 return interim.deferred.promise.catch(angular.noop);
3191 }
3192
3193 /*
3194 * Special method to quick-remove the interim element without animations
3195 * Note: interim elements are in "interim containers"
3196 */
3197 function destroy(target) {
3198 var interim = !target ? stack.shift() : null;
3199 var cntr = angular.element(target).length ? angular.element(target)[0].parentNode : null;
3200
3201 if (cntr) {
3202 // Try to find the interim element in the stack which corresponds to the supplied DOM element.
3203 var filtered = stack.filter(function(entry) {
3204 var currNode = entry.options.element[0];
3205 return (currNode === cntr);
3206 });
3207
3208 // Note: this function might be called when the element already has been removed, in which
3209 // case we won't find any matches. That's ok.
3210 if (filtered.length > 0) {
3211 interim = filtered[0];
3212 stack.splice(stack.indexOf(interim), 1);
3213 }
3214 }
3215
3216 return interim ? interim.remove(SHOW_CANCELLED, false, {'$destroy':true}) : $q.when(SHOW_CANCELLED);
3217 }
3218
3219 /*
3220 * Internal Interim Element Object
3221 * Used internally to manage the DOM element and related data
3222 */
3223 function InterimElement(options) {
3224 var self, element, showAction = $q.when(true);
3225
3226 options = configureScopeAndTransitions(options);
3227
3228 return self = {
3229 options : options,
3230 deferred: $q.defer(),
3231 show : createAndTransitionIn,
3232 remove : transitionOutAndRemove
3233 };
3234
3235 /**
3236 * Compile, link, and show this interim element
3237 * Use optional autoHided and transition-in effects
3238 */
3239 function createAndTransitionIn() {
3240 return $q(function(resolve, reject) {
3241
3242 // Trigger onCompiling callback before the compilation starts.
3243 // This is useful, when modifying options, which can be influenced by developers.
3244 options.onCompiling && options.onCompiling(options);
3245
3246 compileElement(options)
3247 .then(function( compiledData ) {
3248 element = linkElement( compiledData, options );
3249
3250 showAction = showElement(element, options, compiledData.controller)
3251 .then(resolve, rejectAll);
3252
3253 }, rejectAll);
3254
3255 function rejectAll(fault) {
3256 // Force the '$md<xxx>.show()' promise to reject
3257 self.deferred.reject(fault);
3258
3259 // Continue rejection propagation
3260 reject(fault);
3261 }
3262 });
3263 }
3264
3265 /**
3266 * After the show process has finished/rejected:
3267 * - announce 'removing',
3268 * - perform the transition-out, and
3269 * - perform optional clean up scope.
3270 */
3271 function transitionOutAndRemove(response, isCancelled, opts) {
3272
3273 // abort if the show() and compile failed
3274 if ( !element ) return $q.when(false);
3275
3276 options = angular.extend(options || {}, opts || {});
3277 options.cancelAutoHide && options.cancelAutoHide();
3278 options.element.triggerHandler('$mdInterimElementRemove');
3279
3280 if ( options.$destroy === true ) {
3281
3282 return hideElement(options.element, options).then(function(){
3283 (isCancelled && rejectAll(response)) || resolveAll(response);
3284 });
3285
3286 } else {
3287
3288 $q.when(showAction)
3289 .finally(function() {
3290 hideElement(options.element, options).then(function() {
3291
3292 (isCancelled && rejectAll(response)) || resolveAll(response);
3293
3294 }, rejectAll);
3295 });
3296
3297 return self.deferred.promise;
3298 }
3299
3300
3301 /**
3302 * The `show()` returns a promise that will be resolved when the interim
3303 * element is hidden or cancelled...
3304 */
3305 function resolveAll(response) {
3306 self.deferred.resolve(response);
3307 }
3308
3309 /**
3310 * Force the '$md<xxx>.show()' promise to reject
3311 */
3312 function rejectAll(fault) {
3313 self.deferred.reject(fault);
3314 }
3315 }
3316
3317 /**
3318 * Prepare optional isolated scope and prepare $animate with default enter and leave
3319 * transitions for the new element instance.
3320 */
3321 function configureScopeAndTransitions(options) {
3322 options = options || { };
3323 if ( options.template ) {
3324 options.template = $mdUtil.processTemplate(options.template);
3325 }
3326
3327 return angular.extend({
3328 preserveScope: false,
3329 cancelAutoHide : angular.noop,
3330 scope: options.scope || $rootScope.$new(options.isolateScope),
3331
3332 /**
3333 * Default usage to enable $animate to transition-in; can be easily overridden via 'options'
3334 */
3335 onShow: function transitionIn(scope, element, options) {
3336 return $animate.enter(element, options.parent);
3337 },
3338
3339 /**
3340 * Default usage to enable $animate to transition-out; can be easily overridden via 'options'
3341 */
3342 onRemove: function transitionOut(scope, element) {
3343 // Element could be undefined if a new element is shown before
3344 // the old one finishes compiling.
3345 return element && $animate.leave(element) || $q.when();
3346 }
3347 }, options );
3348
3349 }
3350
3351 /**
3352 * Compile an element with a templateUrl, controller, and locals
3353 */
3354 function compileElement(options) {
3355
3356 var compiled = !options.skipCompile ? $mdCompiler.compile(options) : null;
3357
3358 return compiled || $q(function (resolve) {
3359 resolve({
3360 locals: {},
3361 link: function () {
3362 return options.element;
3363 }
3364 });
3365 });
3366 }
3367
3368 /**
3369 * Link an element with compiled configuration
3370 */
3371 function linkElement(compileData, options){
3372 angular.extend(compileData.locals, options);
3373
3374 var element = compileData.link(options.scope);
3375
3376 // Search for parent at insertion time, if not specified
3377 options.element = element;
3378 options.parent = findParent(element, options);
3379 if (options.themable) $mdTheming(element);
3380
3381 return element;
3382 }
3383
3384 /**
3385 * Search for parent at insertion time, if not specified
3386 */
3387 function findParent(element, options) {
3388 var parent = options.parent;
3389
3390 // Search for parent at insertion time, if not specified
3391 if (angular.isFunction(parent)) {
3392 parent = parent(options.scope, element, options);
3393 } else if (angular.isString(parent)) {
3394 parent = angular.element($document[0].querySelector(parent));
3395 } else {
3396 parent = angular.element(parent);
3397 }
3398
3399 // If parent querySelector/getter function fails, or it's just null,
3400 // find a default.
3401 if (!(parent || {}).length) {
3402 var el;
3403 if ($rootElement[0] && $rootElement[0].querySelector) {
3404 el = $rootElement[0].querySelector(':not(svg) > body');
3405 }
3406 if (!el) el = $rootElement[0];
3407 if (el.nodeName == '#comment') {
3408 el = $document[0].body;
3409 }
3410 return angular.element(el);
3411 }
3412
3413 return parent;
3414 }
3415
3416 /**
3417 * If auto-hide is enabled, start timer and prepare cancel function
3418 */
3419 function startAutoHide() {
3420 var autoHideTimer, cancelAutoHide = angular.noop;
3421
3422 if (options.hideDelay) {
3423 autoHideTimer = $timeout(service.hide, options.hideDelay) ;
3424 cancelAutoHide = function() {
3425 $timeout.cancel(autoHideTimer);
3426 }
3427 }
3428
3429 // Cache for subsequent use
3430 options.cancelAutoHide = function() {
3431 cancelAutoHide();
3432 options.cancelAutoHide = undefined;
3433 }
3434 }
3435
3436 /**
3437 * Show the element ( with transitions), notify complete and start
3438 * optional auto-Hide
3439 */
3440 function showElement(element, options, controller) {
3441 // Trigger onShowing callback before the `show()` starts
3442 var notifyShowing = options.onShowing || angular.noop;
3443 // Trigger onComplete callback when the `show()` finishes
3444 var notifyComplete = options.onComplete || angular.noop;
3445
3446 notifyShowing(options.scope, element, options, controller);
3447
3448 return $q(function (resolve, reject) {
3449 try {
3450 // Start transitionIn
3451 $q.when(options.onShow(options.scope, element, options, controller))
3452 .then(function () {
3453 notifyComplete(options.scope, element, options);
3454 startAutoHide();
3455
3456 resolve(element);
3457
3458 }, reject );
3459
3460 } catch(e) {
3461 reject(e.message);
3462 }
3463 });
3464 }
3465
3466 function hideElement(element, options) {
3467 var announceRemoving = options.onRemoving || angular.noop;
3468
3469 return $$q(function (resolve, reject) {
3470 try {
3471 // Start transitionIn
3472 var action = $$q.when( options.onRemove(options.scope, element, options) || true );
3473
3474 // Trigger callback *before* the remove operation starts
3475 announceRemoving(element, action);
3476
3477 if ( options.$destroy == true ) {
3478
3479 // For $destroy, onRemove should be synchronous
3480 resolve(element);
3481
3482 } else {
3483
3484 // Wait until transition-out is done
3485 action.then(function () {
3486
3487 if (!options.preserveScope && options.scope ) {
3488 options.scope.$destroy();
3489 }
3490
3491 resolve(element);
3492
3493 }, reject );
3494 }
3495
3496 } catch(e) {
3497 reject(e);
3498 }
3499 });
3500 }
3501
3502 }
3503 };
3504
3505 }
3506
3507}
3508
3509(function() {
3510 'use strict';
3511
3512 var $mdUtil, $interpolate, $log;
3513
3514 var SUFFIXES = /(-gt)?-(sm|md|lg|print)/g;
3515 var WHITESPACE = /\s+/g;
3516
3517 var FLEX_OPTIONS = ['grow', 'initial', 'auto', 'none', 'noshrink', 'nogrow' ];
3518 var LAYOUT_OPTIONS = ['row', 'column'];
3519 var ALIGNMENT_MAIN_AXIS= [ "", "start", "center", "end", "stretch", "space-around", "space-between" ];
3520 var ALIGNMENT_CROSS_AXIS= [ "", "start", "center", "end", "stretch" ];
3521
3522 var config = {
3523 /**
3524 * Enable directive attribute-to-class conversions
3525 * Developers can use `<body md-layout-css />` to quickly
3526 * disable the Layout directives and prohibit the injection of Layout classNames
3527 */
3528 enabled: true,
3529
3530 /**
3531 * List of mediaQuery breakpoints and associated suffixes
3532 *
3533 * [
3534 * { suffix: "sm", mediaQuery: "screen and (max-width: 599px)" },
3535 * { suffix: "md", mediaQuery: "screen and (min-width: 600px) and (max-width: 959px)" }
3536 * ]
3537 */
3538 breakpoints: []
3539 };
3540
3541 registerLayoutAPI( angular.module('material.core.layout', ['ng']) );
3542
3543 /**
3544 * registerLayoutAPI()
3545 *
3546 * The original ngMaterial Layout solution used attribute selectors and CSS.
3547 *
3548 * ```html
3549 * <div layout="column"> My Content </div>
3550 * ```
3551 *
3552 * ```css
3553 * [layout] {
3554 * box-sizing: border-box;
3555 * display:flex;
3556 * }
3557 * [layout=column] {
3558 * flex-direction : column
3559 * }
3560 * ```
3561 *
3562 * Use of attribute selectors creates significant performance impacts in some
3563 * browsers... mainly IE.
3564 *
3565 * This module registers directives that allow the same layout attributes to be
3566 * interpreted and converted to class selectors. The directive will add equivalent classes to each element that
3567 * contains a Layout directive.
3568 *
3569 * ```html
3570 * <div layout="column" class="layout layout-column"> My Content </div>
3571 *```
3572 *
3573 * ```css
3574 * .layout {
3575 * box-sizing: border-box;
3576 * display:flex;
3577 * }
3578 * .layout-column {
3579 * flex-direction : column
3580 * }
3581 * ```
3582 */
3583 function registerLayoutAPI(module){
3584 var PREFIX_REGEXP = /^((?:x|data)[\:\-_])/i;
3585 var SPECIAL_CHARS_REGEXP = /([\:\-\_]+(.))/g;
3586
3587 // NOTE: these are also defined in constants::MEDIA_PRIORITY and constants::MEDIA
3588 var BREAKPOINTS = [ "", "xs", "gt-xs", "sm", "gt-sm", "md", "gt-md", "lg", "gt-lg", "xl", "print" ];
3589 var API_WITH_VALUES = [ "layout", "flex", "flex-order", "flex-offset", "layout-align" ];
3590 var API_NO_VALUES = [ "show", "hide", "layout-padding", "layout-margin" ];
3591
3592
3593 // Build directive registration functions for the standard Layout API... for all breakpoints.
3594 angular.forEach(BREAKPOINTS, function(mqb) {
3595
3596 // Attribute directives with expected, observable value(s)
3597 angular.forEach( API_WITH_VALUES, function(name){
3598 var fullName = mqb ? name + "-" + mqb : name;
3599 module.directive( directiveNormalize(fullName), attributeWithObserve(fullName));
3600 });
3601
3602 // Attribute directives with no expected value(s)
3603 angular.forEach( API_NO_VALUES, function(name){
3604 var fullName = mqb ? name + "-" + mqb : name;
3605 module.directive( directiveNormalize(fullName), attributeWithoutValue(fullName));
3606 });
3607
3608 });
3609
3610 // Register other, special directive functions for the Layout features:
3611 module
3612
3613 .provider('$$mdLayout' , function() {
3614 // Publish internal service for Layouts
3615 return {
3616 $get : angular.noop,
3617 validateAttributeValue : validateAttributeValue,
3618 validateAttributeUsage : validateAttributeUsage,
3619 /**
3620 * Easy way to disable/enable the Layout API.
3621 * When disabled, this stops all attribute-to-classname generations
3622 */
3623 disableLayouts : function(isDisabled) {
3624 config.enabled = (isDisabled !== true);
3625 }
3626 };
3627 })
3628
3629 .directive('mdLayoutCss' , disableLayoutDirective )
3630 .directive('ngCloak' , buildCloakInterceptor('ng-cloak'))
3631
3632 .directive('layoutWrap' , attributeWithoutValue('layout-wrap'))
3633 .directive('layoutNowrap' , attributeWithoutValue('layout-nowrap'))
3634 .directive('layoutNoWrap' , attributeWithoutValue('layout-no-wrap'))
3635 .directive('layoutFill' , attributeWithoutValue('layout-fill'))
3636
3637 // !! Deprecated attributes: use the `-lt` (aka less-than) notations
3638
3639 .directive('layoutLtMd' , warnAttrNotSupported('layout-lt-md', true))
3640 .directive('layoutLtLg' , warnAttrNotSupported('layout-lt-lg', true))
3641 .directive('flexLtMd' , warnAttrNotSupported('flex-lt-md', true))
3642 .directive('flexLtLg' , warnAttrNotSupported('flex-lt-lg', true))
3643
3644 .directive('layoutAlignLtMd', warnAttrNotSupported('layout-align-lt-md'))
3645 .directive('layoutAlignLtLg', warnAttrNotSupported('layout-align-lt-lg'))
3646 .directive('flexOrderLtMd' , warnAttrNotSupported('flex-order-lt-md'))
3647 .directive('flexOrderLtLg' , warnAttrNotSupported('flex-order-lt-lg'))
3648 .directive('offsetLtMd' , warnAttrNotSupported('flex-offset-lt-md'))
3649 .directive('offsetLtLg' , warnAttrNotSupported('flex-offset-lt-lg'))
3650
3651 .directive('hideLtMd' , warnAttrNotSupported('hide-lt-md'))
3652 .directive('hideLtLg' , warnAttrNotSupported('hide-lt-lg'))
3653 .directive('showLtMd' , warnAttrNotSupported('show-lt-md'))
3654 .directive('showLtLg' , warnAttrNotSupported('show-lt-lg'))
3655
3656 // Determine if
3657 .config( detectDisabledLayouts );
3658
3659 /**
3660 * Converts snake_case to camelCase.
3661 * Also there is special case for Moz prefix starting with upper case letter.
3662 * @param name Name to normalize
3663 */
3664 function directiveNormalize(name) {
3665 return name
3666 .replace(PREFIX_REGEXP, '')
3667 .replace(SPECIAL_CHARS_REGEXP, function(_, separator, letter, offset) {
3668 return offset ? letter.toUpperCase() : letter;
3669 });
3670 }
3671
3672 }
3673
3674
3675 /**
3676 * Detect if any of the HTML tags has a [md-layouts-disabled] attribute;
3677 * If yes, then immediately disable all layout API features
3678 *
3679 * Note: this attribute should be specified on either the HTML or BODY tags
3680 */
3681 /**
3682 * ngInject
3683 */
3684 function detectDisabledLayouts() {
3685 var isDisabled = !!document.querySelector('[md-layouts-disabled]');
3686 config.enabled = !isDisabled;
3687 }
3688
3689 /**
3690 * Special directive that will disable ALL Layout conversions of layout
3691 * attribute(s) to classname(s).
3692 *
3693 * <link rel="stylesheet" href="angular-material.min.css">
3694 * <link rel="stylesheet" href="angular-material.layout.css">
3695 *
3696 * <body md-layout-css>
3697 * ...
3698 * </body>
3699 *
3700 * Note: Using md-layout-css directive requires the developer to load the Material
3701 * Layout Attribute stylesheet (which only uses attribute selectors):
3702 *
3703 * `angular-material.layout.css`
3704 *
3705 * Another option is to use the LayoutProvider to configure and disable the attribute
3706 * conversions; this would obviate the use of the `md-layout-css` directive
3707 *
3708 */
3709 function disableLayoutDirective() {
3710 // Return a 1x-only, first-match attribute directive
3711 config.enabled = false;
3712
3713 return {
3714 restrict : 'A',
3715 priority : '900'
3716 };
3717 }
3718
3719 /**
3720 * Tail-hook ngCloak to delay the uncloaking while Layout transformers
3721 * finish processing. Eliminates flicker with Material.Layouts
3722 */
3723 function buildCloakInterceptor(className) {
3724 return [ '$timeout', function($timeout){
3725 return {
3726 restrict : 'A',
3727 priority : -10, // run after normal ng-cloak
3728 compile : function( element ) {
3729 if (!config.enabled) return angular.noop;
3730
3731 // Re-add the cloak
3732 element.addClass(className);
3733
3734 return function( scope, element ) {
3735 // Wait while layout injectors configure, then uncloak
3736 // NOTE: $rAF does not delay enough... and this is a 1x-only event,
3737 // $timeout is acceptable.
3738 $timeout( function(){
3739 element.removeClass(className);
3740 }, 10, false);
3741 };
3742 }
3743 };
3744 }];
3745 }
3746
3747
3748 // *********************************************************************************
3749 //
3750 // These functions create registration functions for ngMaterial Layout attribute directives
3751 // This provides easy translation to switch ngMaterial attribute selectors to
3752 // CLASS selectors and directives; which has huge performance implications
3753 // for IE Browsers
3754 //
3755 // *********************************************************************************
3756
3757 /**
3758 * Creates a directive registration function where a possible dynamic attribute
3759 * value will be observed/watched.
3760 * @param {string} className attribute name; eg `layout-gt-md` with value ="row"
3761 */
3762 function attributeWithObserve(className) {
3763
3764 return ['$mdUtil', '$interpolate', "$log", function(_$mdUtil_, _$interpolate_, _$log_) {
3765 $mdUtil = _$mdUtil_;
3766 $interpolate = _$interpolate_;
3767 $log = _$log_;
3768
3769 return {
3770 restrict: 'A',
3771 compile: function(element, attr) {
3772 var linkFn;
3773 if (config.enabled) {
3774 // immediately replace static (non-interpolated) invalid values...
3775
3776 validateAttributeUsage(className, attr, element, $log);
3777
3778 validateAttributeValue( className,
3779 getNormalizedAttrValue(className, attr, ""),
3780 buildUpdateFn(element, className, attr)
3781 );
3782
3783 linkFn = translateWithValueToCssClass;
3784 }
3785
3786 // Use for postLink to account for transforms after ng-transclude.
3787 return linkFn || angular.noop;
3788 }
3789 };
3790 }];
3791
3792 /**
3793 * Add as transformed class selector(s), then
3794 * remove the deprecated attribute selector
3795 */
3796 function translateWithValueToCssClass(scope, element, attrs) {
3797 var updateFn = updateClassWithValue(element, className, attrs);
3798 var unwatch = attrs.$observe(attrs.$normalize(className), updateFn);
3799
3800 updateFn(getNormalizedAttrValue(className, attrs, ""));
3801 scope.$on("$destroy", function() { unwatch(); });
3802 }
3803 }
3804
3805 /**
3806 * Creates a registration function for ngMaterial Layout attribute directive.
3807 * This is a `simple` transpose of attribute usage to class usage; where we ignore
3808 * any attribute value
3809 */
3810 function attributeWithoutValue(className) {
3811 return ['$mdUtil', '$interpolate', "$log", function(_$mdUtil_, _$interpolate_, _$log_) {
3812 $mdUtil = _$mdUtil_;
3813 $interpolate = _$interpolate_;
3814 $log = _$log_;
3815
3816 return {
3817 restrict: 'A',
3818 compile: function(element, attr) {
3819 var linkFn;
3820 if (config.enabled) {
3821 // immediately replace static (non-interpolated) invalid values...
3822
3823 validateAttributeValue( className,
3824 getNormalizedAttrValue(className, attr, ""),
3825 buildUpdateFn(element, className, attr)
3826 );
3827
3828 translateToCssClass(null, element);
3829
3830 // Use for postLink to account for transforms after ng-transclude.
3831 linkFn = translateToCssClass;
3832 }
3833
3834 return linkFn || angular.noop;
3835 }
3836 };
3837 }];
3838
3839 /**
3840 * Add as transformed class selector, then
3841 * remove the deprecated attribute selector
3842 */
3843 function translateToCssClass(scope, element) {
3844 element.addClass(className);
3845 }
3846 }
3847
3848
3849
3850 /**
3851 * After link-phase, do NOT remove deprecated layout attribute selector.
3852 * Instead watch the attribute so interpolated data-bindings to layout
3853 * selectors will continue to be supported.
3854 *
3855 * $observe() the className and update with new class (after removing the last one)
3856 *
3857 * e.g. `layout="{{layoutDemo.direction}}"` will update...
3858 *
3859 * NOTE: The value must match one of the specified styles in the CSS.
3860 * For example `flex-gt-md="{{size}}` where `scope.size == 47` will NOT work since
3861 * only breakpoints for 0, 5, 10, 15... 100, 33, 34, 66, 67 are defined.
3862 *
3863 */
3864 function updateClassWithValue(element, className) {
3865 var lastClass;
3866
3867 return function updateClassFn(newValue) {
3868 var value = validateAttributeValue(className, newValue || "");
3869 if ( angular.isDefined(value) ) {
3870 if (lastClass) element.removeClass(lastClass);
3871 lastClass = !value ? className : className + "-" + value.replace(WHITESPACE, "-");
3872 element.addClass(lastClass);
3873 }
3874 };
3875 }
3876
3877 /**
3878 * Provide console warning that this layout attribute has been deprecated
3879 *
3880 */
3881 function warnAttrNotSupported(className) {
3882 var parts = className.split("-");
3883 return ["$log", function($log) {
3884 $log.warn(className + "has been deprecated. Please use a `" + parts[0] + "-gt-<xxx>` variant.");
3885 return angular.noop;
3886 }];
3887 }
3888
3889 /**
3890 * Centralize warnings for known flexbox issues (especially IE-related issues)
3891 */
3892 function validateAttributeUsage(className, attr, element, $log){
3893 var message, usage, url;
3894 var nodeName = element[0].nodeName.toLowerCase();
3895
3896 switch(className.replace(SUFFIXES,"")) {
3897 case "flex":
3898 if ((nodeName == "md-button") || (nodeName == "fieldset")){
3899 // @see https://github.com/philipwalton/flexbugs#9-some-html-elements-cant-be-flex-containers
3900 // Use <div flex> wrapper inside (preferred) or outside
3901
3902 usage = "<" + nodeName + " " + className + "></" + nodeName + ">";
3903 url = "https://github.com/philipwalton/flexbugs#9-some-html-elements-cant-be-flex-containers";
3904 message = "Markup '{0}' may not work as expected in IE Browsers. Consult '{1}' for details.";
3905
3906 $log.warn( $mdUtil.supplant(message, [usage, url]) );
3907 }
3908 }
3909
3910 }
3911
3912
3913 /**
3914 * For the Layout attribute value, validate or replace with default
3915 * fallback value
3916 */
3917 function validateAttributeValue(className, value, updateFn) {
3918 var origValue = value;
3919
3920 if (!needsInterpolation(value)) {
3921 switch (className.replace(SUFFIXES,"")) {
3922 case 'layout' :
3923 if ( !findIn(value, LAYOUT_OPTIONS) ) {
3924 value = LAYOUT_OPTIONS[0]; // 'row';
3925 }
3926 break;
3927
3928 case 'flex' :
3929 if (!findIn(value, FLEX_OPTIONS)) {
3930 if (isNaN(value)) {
3931 value = '';
3932 }
3933 }
3934 break;
3935
3936 case 'flex-offset' :
3937 case 'flex-order' :
3938 if (!value || isNaN(+value)) {
3939 value = '0';
3940 }
3941 break;
3942
3943 case 'layout-align' :
3944 var axis = extractAlignAxis(value);
3945 value = $mdUtil.supplant("{main}-{cross}",axis);
3946 break;
3947
3948 case 'layout-padding' :
3949 case 'layout-margin' :
3950 case 'layout-fill' :
3951 case 'layout-wrap' :
3952 case 'layout-nowrap' :
3953 case 'layout-nowrap' :
3954 value = '';
3955 break;
3956 }
3957
3958 if (value != origValue) {
3959 (updateFn || angular.noop)(value);
3960 }
3961 }
3962
3963 return value;
3964 }
3965
3966 /**
3967 * Replace current attribute value with fallback value
3968 */
3969 function buildUpdateFn(element, className, attrs) {
3970 return function updateAttrValue(fallback) {
3971 if (!needsInterpolation(fallback)) {
3972 // Do not modify the element's attribute value; so
3973 // uses '<ui-layout layout="/api/sidebar.html" />' will not
3974 // be affected. Just update the attrs value.
3975 attrs[attrs.$normalize(className)] = fallback;
3976 }
3977 };
3978 }
3979
3980 /**
3981 * See if the original value has interpolation symbols:
3982 * e.g. flex-gt-md="{{triggerPoint}}"
3983 */
3984 function needsInterpolation(value) {
3985 return (value || "").indexOf($interpolate.startSymbol()) > -1;
3986 }
3987
3988 function getNormalizedAttrValue(className, attrs, defaultVal) {
3989 var normalizedAttr = attrs.$normalize(className);
3990 return attrs[normalizedAttr] ? attrs[normalizedAttr].replace(WHITESPACE, "-") : defaultVal || null;
3991 }
3992
3993 function findIn(item, list, replaceWith) {
3994 item = replaceWith && item ? item.replace(WHITESPACE, replaceWith) : item;
3995
3996 var found = false;
3997 if (item) {
3998 list.forEach(function(it) {
3999 it = replaceWith ? it.replace(WHITESPACE, replaceWith) : it;
4000 found = found || (it === item);
4001 });
4002 }
4003 return found;
4004 }
4005
4006 function extractAlignAxis(attrValue) {
4007 var axis = {
4008 main : "start",
4009 cross: "stretch"
4010 }, values;
4011
4012 attrValue = (attrValue || "");
4013
4014 if ( attrValue.indexOf("-") === 0 || attrValue.indexOf(" ") === 0) {
4015 // For missing main-axis values
4016 attrValue = "none" + attrValue;
4017 }
4018
4019 values = attrValue.toLowerCase().trim().replace(WHITESPACE, "-").split("-");
4020 if ( values.length && (values[0] === "space") ) {
4021 // for main-axis values of "space-around" or "space-between"
4022 values = [ values[0]+"-"+values[1],values[2] ];
4023 }
4024
4025 if ( values.length > 0 ) axis.main = values[0] || axis.main;
4026 if ( values.length > 1 ) axis.cross = values[1] || axis.cross;
4027
4028 if ( ALIGNMENT_MAIN_AXIS.indexOf(axis.main) < 0 ) axis.main = "start";
4029 if ( ALIGNMENT_CROSS_AXIS.indexOf(axis.cross) < 0 ) axis.cross = "stretch";
4030
4031 return axis;
4032 }
4033
4034
4035})();
4036
4037/**
4038 * @ngdoc service
4039 * @name $$mdMeta
4040 * @module material.core.meta
4041 *
4042 * @description
4043 *
4044 * A provider and a service that simplifies meta tags access
4045 *
4046 * Note: This is intended only for use with dynamic meta tags such as browser color and title.
4047 * Tags that are only processed when the page is rendered (such as `charset`, and `http-equiv`)
4048 * will not work since `$$mdMeta` adds the tags after the page has already been loaded.
4049 *
4050 * ```js
4051 * app.config(function($$mdMetaProvider) {
4052 * var removeMeta = $$mdMetaProvider.setMeta('meta-name', 'content');
4053 * var metaValue = $$mdMetaProvider.getMeta('meta-name'); // -> 'content'
4054 *
4055 * removeMeta();
4056 * });
4057 *
4058 * app.controller('myController', function($$mdMeta) {
4059 * var removeMeta = $$mdMeta.setMeta('meta-name', 'content');
4060 * var metaValue = $$mdMeta.getMeta('meta-name'); // -> 'content'
4061 *
4062 * removeMeta();
4063 * });
4064 * ```
4065 *
4066 * @returns {$$mdMeta.$service}
4067 *
4068 */
4069angular.module('material.core.meta', [])
4070 .provider('$$mdMeta', function () {
4071 var head = angular.element(document.head);
4072 var metaElements = {};
4073
4074 /**
4075 * Checks if the requested element was written manually and maps it
4076 *
4077 * @param {string} name meta tag 'name' attribute value
4078 * @returns {boolean} returns true if there is an element with the requested name
4079 */
4080 function mapExistingElement(name) {
4081 if (metaElements[name]) {
4082 return true;
4083 }
4084
4085 var element = document.getElementsByName(name)[0];
4086
4087 if (!element) {
4088 return false;
4089 }
4090
4091 metaElements[name] = angular.element(element);
4092
4093 return true;
4094 }
4095
4096 /**
4097 * @ngdoc method
4098 * @name $$mdMeta#setMeta
4099 *
4100 * @description
4101 * Creates meta element with the 'name' and 'content' attributes,
4102 * if the meta tag is already created than we replace the 'content' value
4103 *
4104 * @param {string} name meta tag 'name' attribute value
4105 * @param {string} content meta tag 'content' attribute value
4106 * @returns {function} remove function
4107 *
4108 */
4109 function setMeta(name, content) {
4110 mapExistingElement(name);
4111
4112 if (!metaElements[name]) {
4113 var newMeta = angular.element('<meta name="' + name + '" content="' + content + '"/>');
4114 head.append(newMeta);
4115 metaElements[name] = newMeta;
4116 }
4117 else {
4118 metaElements[name].attr('content', content);
4119 }
4120
4121 return function () {
4122 metaElements[name].attr('content', '');
4123 metaElements[name].remove();
4124 delete metaElements[name];
4125 };
4126 }
4127
4128 /**
4129 * @ngdoc method
4130 * @name $$mdMeta#getMeta
4131 *
4132 * @description
4133 * Gets the 'content' attribute value of the wanted meta element
4134 *
4135 * @param {string} name meta tag 'name' attribute value
4136 * @returns {string} content attribute value
4137 */
4138 function getMeta(name) {
4139 if (!mapExistingElement(name)) {
4140 throw Error('$$mdMeta: could not find a meta tag with the name \'' + name + '\'');
4141 }
4142
4143 return metaElements[name].attr('content');
4144 }
4145
4146 var module = {
4147 setMeta: setMeta,
4148 getMeta: getMeta
4149 };
4150
4151 return angular.extend({}, module, {
4152 $get: function () {
4153 return module;
4154 }
4155 });
4156 });
4157 /**
4158 * @ngdoc module
4159 * @name material.core.componentRegistry
4160 *
4161 * @description
4162 * A component instance registration service.
4163 * Note: currently this as a private service in the SideNav component.
4164 */
4165 ComponentRegistry.$inject = ["$log", "$q"];
4166 angular.module('material.core')
4167 .factory('$mdComponentRegistry', ComponentRegistry);
4168
4169 /*
4170 * @private
4171 * @ngdoc factory
4172 * @name ComponentRegistry
4173 * @module material.core.componentRegistry
4174 *
4175 */
4176 function ComponentRegistry($log, $q) {
4177
4178 var self;
4179 var instances = [ ];
4180 var pendings = { };
4181
4182 return self = {
4183 /**
4184 * Used to print an error when an instance for a handle isn't found.
4185 */
4186 notFoundError: function(handle, msgContext) {
4187 $log.error( (msgContext || "") + 'No instance found for handle', handle);
4188 },
4189 /**
4190 * Return all registered instances as an array.
4191 */
4192 getInstances: function() {
4193 return instances;
4194 },
4195
4196 /**
4197 * Get a registered instance.
4198 * @param handle the String handle to look up for a registered instance.
4199 */
4200 get: function(handle) {
4201 if ( !isValidID(handle) ) return null;
4202
4203 var i, j, instance;
4204 for(i = 0, j = instances.length; i < j; i++) {
4205 instance = instances[i];
4206 if(instance.$$mdHandle === handle) {
4207 return instance;
4208 }
4209 }
4210 return null;
4211 },
4212
4213 /**
4214 * Register an instance.
4215 * @param instance the instance to register
4216 * @param handle the handle to identify the instance under.
4217 */
4218 register: function(instance, handle) {
4219 if ( !handle ) return angular.noop;
4220
4221 instance.$$mdHandle = handle;
4222 instances.push(instance);
4223 resolveWhen();
4224
4225 return deregister;
4226
4227 /**
4228 * Remove registration for an instance
4229 */
4230 function deregister() {
4231 var index = instances.indexOf(instance);
4232 if (index !== -1) {
4233 instances.splice(index, 1);
4234 }
4235 }
4236
4237 /**
4238 * Resolve any pending promises for this instance
4239 */
4240 function resolveWhen() {
4241 var dfd = pendings[handle];
4242 if ( dfd ) {
4243 dfd.forEach(function (promise) {
4244 promise.resolve(instance);
4245 });
4246 delete pendings[handle];
4247 }
4248 }
4249 },
4250
4251 /**
4252 * Async accessor to registered component instance
4253 * If not available then a promise is created to notify
4254 * all listeners when the instance is registered.
4255 */
4256 when : function(handle) {
4257 if ( isValidID(handle) ) {
4258 var deferred = $q.defer();
4259 var instance = self.get(handle);
4260
4261 if ( instance ) {
4262 deferred.resolve( instance );
4263 } else {
4264 if (pendings[handle] === undefined) {
4265 pendings[handle] = [];
4266 }
4267 pendings[handle].push(deferred);
4268 }
4269
4270 return deferred.promise;
4271 }
4272 return $q.reject("Invalid `md-component-id` value.");
4273 }
4274
4275 };
4276
4277 function isValidID(handle){
4278 return handle && (handle !== "");
4279 }
4280
4281 }
4282
4283(function() {
4284 'use strict';
4285
4286 /**
4287 * @ngdoc service
4288 * @name $mdButtonInkRipple
4289 * @module material.core
4290 *
4291 * @description
4292 * Provides ripple effects for md-button. See $mdInkRipple service for all possible configuration options.
4293 *
4294 * @param {object=} scope Scope within the current context
4295 * @param {object=} element The element the ripple effect should be applied to
4296 * @param {object=} options (Optional) Configuration options to override the default ripple configuration
4297 */
4298
4299 MdButtonInkRipple.$inject = ["$mdInkRipple"];
4300 angular.module('material.core')
4301 .factory('$mdButtonInkRipple', MdButtonInkRipple);
4302
4303 function MdButtonInkRipple($mdInkRipple) {
4304 return {
4305 attach: function attachRipple(scope, element, options) {
4306 options = angular.extend(optionsForElement(element), options);
4307
4308 return $mdInkRipple.attach(scope, element, options);
4309 }
4310 };
4311
4312 function optionsForElement(element) {
4313 if (element.hasClass('md-icon-button')) {
4314 return {
4315 isMenuItem: element.hasClass('md-menu-item'),
4316 fitRipple: true,
4317 center: true
4318 };
4319 } else {
4320 return {
4321 isMenuItem: element.hasClass('md-menu-item'),
4322 dimBackground: true
4323 }
4324 }
4325 };
4326 };
4327})();
4328
4329(function() {
4330 'use strict';
4331
4332 /**
4333 * @ngdoc service
4334 * @name $mdCheckboxInkRipple
4335 * @module material.core
4336 *
4337 * @description
4338 * Provides ripple effects for md-checkbox. See $mdInkRipple service for all possible configuration options.
4339 *
4340 * @param {object=} scope Scope within the current context
4341 * @param {object=} element The element the ripple effect should be applied to
4342 * @param {object=} options (Optional) Configuration options to override the defaultripple configuration
4343 */
4344
4345 MdCheckboxInkRipple.$inject = ["$mdInkRipple"];
4346 angular.module('material.core')
4347 .factory('$mdCheckboxInkRipple', MdCheckboxInkRipple);
4348
4349 function MdCheckboxInkRipple($mdInkRipple) {
4350 return {
4351 attach: attach
4352 };
4353
4354 function attach(scope, element, options) {
4355 return $mdInkRipple.attach(scope, element, angular.extend({
4356 center: true,
4357 dimBackground: false,
4358 fitRipple: true
4359 }, options));
4360 };
4361 };
4362})();
4363
4364(function() {
4365 'use strict';
4366
4367 /**
4368 * @ngdoc service
4369 * @name $mdListInkRipple
4370 * @module material.core
4371 *
4372 * @description
4373 * Provides ripple effects for md-list. See $mdInkRipple service for all possible configuration options.
4374 *
4375 * @param {object=} scope Scope within the current context
4376 * @param {object=} element The element the ripple effect should be applied to
4377 * @param {object=} options (Optional) Configuration options to override the defaultripple configuration
4378 */
4379
4380 MdListInkRipple.$inject = ["$mdInkRipple"];
4381 angular.module('material.core')
4382 .factory('$mdListInkRipple', MdListInkRipple);
4383
4384 function MdListInkRipple($mdInkRipple) {
4385 return {
4386 attach: attach
4387 };
4388
4389 function attach(scope, element, options) {
4390 return $mdInkRipple.attach(scope, element, angular.extend({
4391 center: false,
4392 dimBackground: true,
4393 outline: false,
4394 rippleSize: 'full'
4395 }, options));
4396 };
4397 };
4398})();
4399
4400/**
4401 * @ngdoc module
4402 * @name material.core.ripple
4403 * @description
4404 * Ripple
4405 */
4406InkRippleCtrl.$inject = ["$scope", "$element", "rippleOptions", "$window", "$timeout", "$mdUtil", "$mdColorUtil"];
4407InkRippleDirective.$inject = ["$mdButtonInkRipple", "$mdCheckboxInkRipple"];
4408angular.module('material.core')
4409 .provider('$mdInkRipple', InkRippleProvider)
4410 .directive('mdInkRipple', InkRippleDirective)
4411 .directive('mdNoInk', attrNoDirective)
4412 .directive('mdNoBar', attrNoDirective)
4413 .directive('mdNoStretch', attrNoDirective);
4414
4415var DURATION = 450;
4416
4417/**
4418 * @ngdoc directive
4419 * @name mdInkRipple
4420 * @module material.core.ripple
4421 *
4422 * @description
4423 * The `md-ink-ripple` directive allows you to specify the ripple color or id a ripple is allowed.
4424 *
4425 * @param {string|boolean} md-ink-ripple A color string `#FF0000` or boolean (`false` or `0`) for preventing ripple
4426 *
4427 * @usage
4428 * ### String values
4429 * <hljs lang="html">
4430 * <ANY md-ink-ripple="#FF0000">
4431 * Ripples in red
4432 * </ANY>
4433 *
4434 * <ANY md-ink-ripple="false">
4435 * Not rippling
4436 * </ANY>
4437 * </hljs>
4438 *
4439 * ### Interpolated values
4440 * <hljs lang="html">
4441 * <ANY md-ink-ripple="{{ randomColor() }}">
4442 * Ripples with the return value of 'randomColor' function
4443 * </ANY>
4444 *
4445 * <ANY md-ink-ripple="{{ canRipple() }}">
4446 * Ripples if 'canRipple' function return value is not 'false' or '0'
4447 * </ANY>
4448 * </hljs>
4449 */
4450function InkRippleDirective ($mdButtonInkRipple, $mdCheckboxInkRipple) {
4451 return {
4452 controller: angular.noop,
4453 link: function (scope, element, attr) {
4454 attr.hasOwnProperty('mdInkRippleCheckbox')
4455 ? $mdCheckboxInkRipple.attach(scope, element)
4456 : $mdButtonInkRipple.attach(scope, element);
4457 }
4458 };
4459}
4460
4461/**
4462 * @ngdoc service
4463 * @name $mdInkRipple
4464 * @module material.core.ripple
4465 *
4466 * @description
4467 * `$mdInkRipple` is a service for adding ripples to any element
4468 *
4469 * @usage
4470 * <hljs lang="js">
4471 * app.factory('$myElementInkRipple', function($mdInkRipple) {
4472 * return {
4473 * attach: function (scope, element, options) {
4474 * return $mdInkRipple.attach(scope, element, angular.extend({
4475 * center: false,
4476 * dimBackground: true
4477 * }, options));
4478 * }
4479 * };
4480 * });
4481 *
4482 * app.controller('myController', function ($scope, $element, $myElementInkRipple) {
4483 * $scope.onClick = function (ev) {
4484 * $myElementInkRipple.attach($scope, angular.element(ev.target), { center: true });
4485 * }
4486 * });
4487 * </hljs>
4488 *
4489 * ### Disabling ripples globally
4490 * If you want to disable ink ripples globally, for all components, you can call the
4491 * `disableInkRipple` method in your app's config.
4492 *
4493 * <hljs lang="js">
4494 * app.config(function ($mdInkRippleProvider) {
4495 * $mdInkRippleProvider.disableInkRipple();
4496 * });
4497 */
4498
4499function InkRippleProvider () {
4500 var isDisabledGlobally = false;
4501
4502 return {
4503 disableInkRipple: disableInkRipple,
4504 $get: ["$injector", function($injector) {
4505 return { attach: attach };
4506
4507 /**
4508 * @ngdoc method
4509 * @name $mdInkRipple#attach
4510 *
4511 * @description
4512 * Attaching given scope, element and options to inkRipple controller
4513 *
4514 * @param {object=} scope Scope within the current context
4515 * @param {object=} element The element the ripple effect should be applied to
4516 * @param {object=} options (Optional) Configuration options to override the defaultRipple configuration
4517 * * `center` - Whether the ripple should start from the center of the container element
4518 * * `dimBackground` - Whether the background should be dimmed with the ripple color
4519 * * `colorElement` - The element the ripple should take its color from, defined by css property `color`
4520 * * `fitRipple` - Whether the ripple should fill the element
4521 */
4522 function attach (scope, element, options) {
4523 if (isDisabledGlobally || element.controller('mdNoInk')) return angular.noop;
4524 return $injector.instantiate(InkRippleCtrl, {
4525 $scope: scope,
4526 $element: element,
4527 rippleOptions: options
4528 });
4529 }
4530 }]
4531 };
4532
4533 /**
4534 * @ngdoc method
4535 * @name $mdInkRipple#disableInkRipple
4536 *
4537 * @description
4538 * A config-time method that, when called, disables ripples globally.
4539 */
4540 function disableInkRipple () {
4541 isDisabledGlobally = true;
4542 }
4543}
4544
4545/**
4546 * Controller used by the ripple service in order to apply ripples
4547 * ngInject
4548 */
4549function InkRippleCtrl ($scope, $element, rippleOptions, $window, $timeout, $mdUtil, $mdColorUtil) {
4550 this.$window = $window;
4551 this.$timeout = $timeout;
4552 this.$mdUtil = $mdUtil;
4553 this.$mdColorUtil = $mdColorUtil;
4554 this.$scope = $scope;
4555 this.$element = $element;
4556 this.options = rippleOptions;
4557 this.mousedown = false;
4558 this.ripples = [];
4559 this.timeout = null; // Stores a reference to the most-recent ripple timeout
4560 this.lastRipple = null;
4561
4562 $mdUtil.valueOnUse(this, 'container', this.createContainer);
4563
4564 this.$element.addClass('md-ink-ripple');
4565
4566 // attach method for unit tests
4567 ($element.controller('mdInkRipple') || {}).createRipple = angular.bind(this, this.createRipple);
4568 ($element.controller('mdInkRipple') || {}).setColor = angular.bind(this, this.color);
4569
4570 this.bindEvents();
4571}
4572
4573
4574/**
4575 * Either remove or unlock any remaining ripples when the user mouses off of the element (either by
4576 * mouseup or mouseleave event)
4577 */
4578function autoCleanup (self, cleanupFn) {
4579
4580 if ( self.mousedown || self.lastRipple ) {
4581 self.mousedown = false;
4582 self.$mdUtil.nextTick( angular.bind(self, cleanupFn), false);
4583 }
4584
4585}
4586
4587
4588/**
4589 * Returns the color that the ripple should be (either based on CSS or hard-coded)
4590 * @returns {string}
4591 */
4592InkRippleCtrl.prototype.color = function (value) {
4593 var self = this;
4594
4595 // If assigning a color value, apply it to background and the ripple color
4596 if (angular.isDefined(value)) {
4597 self._color = self._parseColor(value);
4598 }
4599
4600 // If color lookup, use assigned, defined, or inherited
4601 return self._color || self._parseColor( self.inkRipple() ) || self._parseColor( getElementColor() );
4602
4603 /**
4604 * Finds the color element and returns its text color for use as default ripple color
4605 * @returns {string}
4606 */
4607 function getElementColor () {
4608 var items = self.options && self.options.colorElement ? self.options.colorElement : [];
4609 var elem = items.length ? items[ 0 ] : self.$element[ 0 ];
4610
4611 return elem ? self.$window.getComputedStyle(elem).color : 'rgb(0,0,0)';
4612 }
4613};
4614
4615/**
4616 * Updating the ripple colors based on the current inkRipple value
4617 * or the element's computed style color
4618 */
4619InkRippleCtrl.prototype.calculateColor = function () {
4620 return this.color();
4621};
4622
4623
4624/**
4625 * Takes a string color and converts it to RGBA format
4626 * @param color {string}
4627 * @param [multiplier] {int}
4628 * @returns {string}
4629 */
4630
4631InkRippleCtrl.prototype._parseColor = function parseColor (color, multiplier) {
4632 multiplier = multiplier || 1;
4633 var colorUtil = this.$mdColorUtil;
4634
4635 if (!color) return;
4636 if (color.indexOf('rgba') === 0) return color.replace(/\d?\.?\d*\s*\)\s*$/, (0.1 * multiplier).toString() + ')');
4637 if (color.indexOf('rgb') === 0) return colorUtil.rgbToRgba(color);
4638 if (color.indexOf('#') === 0) return colorUtil.hexToRgba(color);
4639
4640};
4641
4642/**
4643 * Binds events to the root element for
4644 */
4645InkRippleCtrl.prototype.bindEvents = function () {
4646 this.$element.on('mousedown', angular.bind(this, this.handleMousedown));
4647 this.$element.on('mouseup touchend', angular.bind(this, this.handleMouseup));
4648 this.$element.on('mouseleave', angular.bind(this, this.handleMouseup));
4649 this.$element.on('touchmove', angular.bind(this, this.handleTouchmove));
4650};
4651
4652/**
4653 * Create a new ripple on every mousedown event from the root element
4654 * @param event {MouseEvent}
4655 */
4656InkRippleCtrl.prototype.handleMousedown = function (event) {
4657 if ( this.mousedown ) return;
4658
4659 // When jQuery is loaded, we have to get the original event
4660 if (event.hasOwnProperty('originalEvent')) event = event.originalEvent;
4661 this.mousedown = true;
4662 if (this.options.center) {
4663 this.createRipple(this.container.prop('clientWidth') / 2, this.container.prop('clientWidth') / 2);
4664 } else {
4665
4666 // We need to calculate the relative coordinates if the target is a sublayer of the ripple element
4667 if (event.srcElement !== this.$element[0]) {
4668 var layerRect = this.$element[0].getBoundingClientRect();
4669 var layerX = event.clientX - layerRect.left;
4670 var layerY = event.clientY - layerRect.top;
4671
4672 this.createRipple(layerX, layerY);
4673 } else {
4674 this.createRipple(event.offsetX, event.offsetY);
4675 }
4676 }
4677};
4678
4679/**
4680 * Either remove or unlock any remaining ripples when the user mouses off of the element (either by
4681 * mouseup, touchend or mouseleave event)
4682 */
4683InkRippleCtrl.prototype.handleMouseup = function () {
4684 autoCleanup(this, this.clearRipples);
4685};
4686
4687/**
4688 * Either remove or unlock any remaining ripples when the user mouses off of the element (by
4689 * touchmove)
4690 */
4691InkRippleCtrl.prototype.handleTouchmove = function () {
4692 autoCleanup(this, this.deleteRipples);
4693};
4694
4695/**
4696 * Cycles through all ripples and attempts to remove them.
4697 */
4698InkRippleCtrl.prototype.deleteRipples = function () {
4699 for (var i = 0; i < this.ripples.length; i++) {
4700 this.ripples[ i ].remove();
4701 }
4702};
4703
4704/**
4705 * Cycles through all ripples and attempts to remove them with fade.
4706 * Depending on logic within `fadeInComplete`, some removals will be postponed.
4707 */
4708InkRippleCtrl.prototype.clearRipples = function () {
4709 for (var i = 0; i < this.ripples.length; i++) {
4710 this.fadeInComplete(this.ripples[ i ]);
4711 }
4712};
4713
4714/**
4715 * Creates the ripple container element
4716 * @returns {*}
4717 */
4718InkRippleCtrl.prototype.createContainer = function () {
4719 var container = angular.element('<div class="md-ripple-container"></div>');
4720 this.$element.append(container);
4721 return container;
4722};
4723
4724InkRippleCtrl.prototype.clearTimeout = function () {
4725 if (this.timeout) {
4726 this.$timeout.cancel(this.timeout);
4727 this.timeout = null;
4728 }
4729};
4730
4731InkRippleCtrl.prototype.isRippleAllowed = function () {
4732 var element = this.$element[0];
4733 do {
4734 if (!element.tagName || element.tagName === 'BODY') break;
4735
4736 if (element && angular.isFunction(element.hasAttribute)) {
4737 if (element.hasAttribute('disabled')) return false;
4738 if (this.inkRipple() === 'false' || this.inkRipple() === '0') return false;
4739 }
4740
4741 } while (element = element.parentNode);
4742 return true;
4743};
4744
4745/**
4746 * The attribute `md-ink-ripple` may be a static or interpolated
4747 * color value OR a boolean indicator (used to disable ripples)
4748 */
4749InkRippleCtrl.prototype.inkRipple = function () {
4750 return this.$element.attr('md-ink-ripple');
4751};
4752
4753/**
4754 * Creates a new ripple and adds it to the container. Also tracks ripple in `this.ripples`.
4755 * @param left
4756 * @param top
4757 */
4758InkRippleCtrl.prototype.createRipple = function (left, top) {
4759 if (!this.isRippleAllowed()) return;
4760
4761 var ctrl = this;
4762 var colorUtil = ctrl.$mdColorUtil;
4763 var ripple = angular.element('<div class="md-ripple"></div>');
4764 var width = this.$element.prop('clientWidth');
4765 var height = this.$element.prop('clientHeight');
4766 var x = Math.max(Math.abs(width - left), left) * 2;
4767 var y = Math.max(Math.abs(height - top), top) * 2;
4768 var size = getSize(this.options.fitRipple, x, y);
4769 var color = this.calculateColor();
4770
4771 ripple.css({
4772 left: left + 'px',
4773 top: top + 'px',
4774 background: 'black',
4775 width: size + 'px',
4776 height: size + 'px',
4777 backgroundColor: colorUtil.rgbaToRgb(color),
4778 borderColor: colorUtil.rgbaToRgb(color)
4779 });
4780 this.lastRipple = ripple;
4781
4782 // we only want one timeout to be running at a time
4783 this.clearTimeout();
4784 this.timeout = this.$timeout(function () {
4785 ctrl.clearTimeout();
4786 if (!ctrl.mousedown) ctrl.fadeInComplete(ripple);
4787 }, DURATION * 0.35, false);
4788
4789 if (this.options.dimBackground) this.container.css({ backgroundColor: color });
4790 this.container.append(ripple);
4791 this.ripples.push(ripple);
4792 ripple.addClass('md-ripple-placed');
4793
4794 this.$mdUtil.nextTick(function () {
4795
4796 ripple.addClass('md-ripple-scaled md-ripple-active');
4797 ctrl.$timeout(function () {
4798 ctrl.clearRipples();
4799 }, DURATION, false);
4800
4801 }, false);
4802
4803 function getSize (fit, x, y) {
4804 return fit
4805 ? Math.max(x, y)
4806 : Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2));
4807 }
4808};
4809
4810
4811
4812/**
4813 * After fadeIn finishes, either kicks off the fade-out animation or queues the element for removal on mouseup
4814 * @param ripple
4815 */
4816InkRippleCtrl.prototype.fadeInComplete = function (ripple) {
4817 if (this.lastRipple === ripple) {
4818 if (!this.timeout && !this.mousedown) {
4819 this.removeRipple(ripple);
4820 }
4821 } else {
4822 this.removeRipple(ripple);
4823 }
4824};
4825
4826/**
4827 * Kicks off the animation for removing a ripple
4828 * @param ripple {Element}
4829 */
4830InkRippleCtrl.prototype.removeRipple = function (ripple) {
4831 var ctrl = this;
4832 var index = this.ripples.indexOf(ripple);
4833 if (index < 0) return;
4834 this.ripples.splice(this.ripples.indexOf(ripple), 1);
4835 ripple.removeClass('md-ripple-active');
4836 ripple.addClass('md-ripple-remove');
4837 if (this.ripples.length === 0) this.container.css({ backgroundColor: '' });
4838 // use a 2-second timeout in order to allow for the animation to finish
4839 // we don't actually care how long the animation takes
4840 this.$timeout(function () {
4841 ctrl.fadeOutComplete(ripple);
4842 }, DURATION, false);
4843};
4844
4845/**
4846 * Removes the provided ripple from the DOM
4847 * @param ripple
4848 */
4849InkRippleCtrl.prototype.fadeOutComplete = function (ripple) {
4850 ripple.remove();
4851 this.lastRipple = null;
4852};
4853
4854/**
4855 * Used to create an empty directive. This is used to track flag-directives whose children may have
4856 * functionality based on them.
4857 *
4858 * Example: `md-no-ink` will potentially be used by all child directives.
4859 */
4860function attrNoDirective () {
4861 return { controller: angular.noop };
4862}
4863
4864(function() {
4865 'use strict';
4866
4867 /**
4868 * @ngdoc service
4869 * @name $mdTabInkRipple
4870 * @module material.core
4871 *
4872 * @description
4873 * Provides ripple effects for md-tabs. See $mdInkRipple service for all possible configuration options.
4874 *
4875 * @param {object=} scope Scope within the current context
4876 * @param {object=} element The element the ripple effect should be applied to
4877 * @param {object=} options (Optional) Configuration options to override the defaultripple configuration
4878 */
4879
4880 MdTabInkRipple.$inject = ["$mdInkRipple"];
4881 angular.module('material.core')
4882 .factory('$mdTabInkRipple', MdTabInkRipple);
4883
4884 function MdTabInkRipple($mdInkRipple) {
4885 return {
4886 attach: attach
4887 };
4888
4889 function attach(scope, element, options) {
4890 return $mdInkRipple.attach(scope, element, angular.extend({
4891 center: false,
4892 dimBackground: true,
4893 outline: false,
4894 rippleSize: 'full'
4895 }, options));
4896 };
4897 };
4898})();
4899
4900angular.module('material.core.theming.palette', [])
4901.constant('$mdColorPalette', {
4902 'red': {
4903 '50': '#ffebee',
4904 '100': '#ffcdd2',
4905 '200': '#ef9a9a',
4906 '300': '#e57373',
4907 '400': '#ef5350',
4908 '500': '#f44336',
4909 '600': '#e53935',
4910 '700': '#d32f2f',
4911 '800': '#c62828',
4912 '900': '#b71c1c',
4913 'A100': '#ff8a80',
4914 'A200': '#ff5252',
4915 'A400': '#ff1744',
4916 'A700': '#d50000',
4917 'contrastDefaultColor': 'light',
4918 'contrastDarkColors': '50 100 200 300 A100',
4919 'contrastStrongLightColors': '400 500 600 700 A200 A400 A700'
4920 },
4921 'pink': {
4922 '50': '#fce4ec',
4923 '100': '#f8bbd0',
4924 '200': '#f48fb1',
4925 '300': '#f06292',
4926 '400': '#ec407a',
4927 '500': '#e91e63',
4928 '600': '#d81b60',
4929 '700': '#c2185b',
4930 '800': '#ad1457',
4931 '900': '#880e4f',
4932 'A100': '#ff80ab',
4933 'A200': '#ff4081',
4934 'A400': '#f50057',
4935 'A700': '#c51162',
4936 'contrastDefaultColor': 'light',
4937 'contrastDarkColors': '50 100 200 A100',
4938 'contrastStrongLightColors': '500 600 A200 A400 A700'
4939 },
4940 'purple': {
4941 '50': '#f3e5f5',
4942 '100': '#e1bee7',
4943 '200': '#ce93d8',
4944 '300': '#ba68c8',
4945 '400': '#ab47bc',
4946 '500': '#9c27b0',
4947 '600': '#8e24aa',
4948 '700': '#7b1fa2',
4949 '800': '#6a1b9a',
4950 '900': '#4a148c',
4951 'A100': '#ea80fc',
4952 'A200': '#e040fb',
4953 'A400': '#d500f9',
4954 'A700': '#aa00ff',
4955 'contrastDefaultColor': 'light',
4956 'contrastDarkColors': '50 100 200 A100',
4957 'contrastStrongLightColors': '300 400 A200 A400 A700'
4958 },
4959 'deep-purple': {
4960 '50': '#ede7f6',
4961 '100': '#d1c4e9',
4962 '200': '#b39ddb',
4963 '300': '#9575cd',
4964 '400': '#7e57c2',
4965 '500': '#673ab7',
4966 '600': '#5e35b1',
4967 '700': '#512da8',
4968 '800': '#4527a0',
4969 '900': '#311b92',
4970 'A100': '#b388ff',
4971 'A200': '#7c4dff',
4972 'A400': '#651fff',
4973 'A700': '#6200ea',
4974 'contrastDefaultColor': 'light',
4975 'contrastDarkColors': '50 100 200 A100',
4976 'contrastStrongLightColors': '300 400 A200'
4977 },
4978 'indigo': {
4979 '50': '#e8eaf6',
4980 '100': '#c5cae9',
4981 '200': '#9fa8da',
4982 '300': '#7986cb',
4983 '400': '#5c6bc0',
4984 '500': '#3f51b5',
4985 '600': '#3949ab',
4986 '700': '#303f9f',
4987 '800': '#283593',
4988 '900': '#1a237e',
4989 'A100': '#8c9eff',
4990 'A200': '#536dfe',
4991 'A400': '#3d5afe',
4992 'A700': '#304ffe',
4993 'contrastDefaultColor': 'light',
4994 'contrastDarkColors': '50 100 200 A100',
4995 'contrastStrongLightColors': '300 400 A200 A400'
4996 },
4997 'blue': {
4998 '50': '#e3f2fd',
4999 '100': '#bbdefb',
5000 '200': '#90caf9',
5001 '300': '#64b5f6',
5002 '400': '#42a5f5',
5003 '500': '#2196f3',
5004 '600': '#1e88e5',
5005 '700': '#1976d2',
5006 '800': '#1565c0',
5007 '900': '#0d47a1',
5008 'A100': '#82b1ff',
5009 'A200': '#448aff',
5010 'A400': '#2979ff',
5011 'A700': '#2962ff',
5012 'contrastDefaultColor': 'light',
5013 'contrastDarkColors': '50 100 200 300 400 A100',
5014 'contrastStrongLightColors': '500 600 700 A200 A400 A700'
5015 },
5016 'light-blue': {
5017 '50': '#e1f5fe',
5018 '100': '#b3e5fc',
5019 '200': '#81d4fa',
5020 '300': '#4fc3f7',
5021 '400': '#29b6f6',
5022 '500': '#03a9f4',
5023 '600': '#039be5',
5024 '700': '#0288d1',
5025 '800': '#0277bd',
5026 '900': '#01579b',
5027 'A100': '#80d8ff',
5028 'A200': '#40c4ff',
5029 'A400': '#00b0ff',
5030 'A700': '#0091ea',
5031 'contrastDefaultColor': 'dark',
5032 'contrastLightColors': '600 700 800 900 A700',
5033 'contrastStrongLightColors': '600 700 800 A700'
5034 },
5035 'cyan': {
5036 '50': '#e0f7fa',
5037 '100': '#b2ebf2',
5038 '200': '#80deea',
5039 '300': '#4dd0e1',
5040 '400': '#26c6da',
5041 '500': '#00bcd4',
5042 '600': '#00acc1',
5043 '700': '#0097a7',
5044 '800': '#00838f',
5045 '900': '#006064',
5046 'A100': '#84ffff',
5047 'A200': '#18ffff',
5048 'A400': '#00e5ff',
5049 'A700': '#00b8d4',
5050 'contrastDefaultColor': 'dark',
5051 'contrastLightColors': '700 800 900',
5052 'contrastStrongLightColors': '700 800 900'
5053 },
5054 'teal': {
5055 '50': '#e0f2f1',
5056 '100': '#b2dfdb',
5057 '200': '#80cbc4',
5058 '300': '#4db6ac',
5059 '400': '#26a69a',
5060 '500': '#009688',
5061 '600': '#00897b',
5062 '700': '#00796b',
5063 '800': '#00695c',
5064 '900': '#004d40',
5065 'A100': '#a7ffeb',
5066 'A200': '#64ffda',
5067 'A400': '#1de9b6',
5068 'A700': '#00bfa5',
5069 'contrastDefaultColor': 'dark',
5070 'contrastLightColors': '500 600 700 800 900',
5071 'contrastStrongLightColors': '500 600 700'
5072 },
5073 'green': {
5074 '50': '#e8f5e9',
5075 '100': '#c8e6c9',
5076 '200': '#a5d6a7',
5077 '300': '#81c784',
5078 '400': '#66bb6a',
5079 '500': '#4caf50',
5080 '600': '#43a047',
5081 '700': '#388e3c',
5082 '800': '#2e7d32',
5083 '900': '#1b5e20',
5084 'A100': '#b9f6ca',
5085 'A200': '#69f0ae',
5086 'A400': '#00e676',
5087 'A700': '#00c853',
5088 'contrastDefaultColor': 'dark',
5089 'contrastLightColors': '500 600 700 800 900',
5090 'contrastStrongLightColors': '500 600 700'
5091 },
5092 'light-green': {
5093 '50': '#f1f8e9',
5094 '100': '#dcedc8',
5095 '200': '#c5e1a5',
5096 '300': '#aed581',
5097 '400': '#9ccc65',
5098 '500': '#8bc34a',
5099 '600': '#7cb342',
5100 '700': '#689f38',
5101 '800': '#558b2f',
5102 '900': '#33691e',
5103 'A100': '#ccff90',
5104 'A200': '#b2ff59',
5105 'A400': '#76ff03',
5106 'A700': '#64dd17',
5107 'contrastDefaultColor': 'dark',
5108 'contrastLightColors': '700 800 900',
5109 'contrastStrongLightColors': '700 800 900'
5110 },
5111 'lime': {
5112 '50': '#f9fbe7',
5113 '100': '#f0f4c3',
5114 '200': '#e6ee9c',
5115 '300': '#dce775',
5116 '400': '#d4e157',
5117 '500': '#cddc39',
5118 '600': '#c0ca33',
5119 '700': '#afb42b',
5120 '800': '#9e9d24',
5121 '900': '#827717',
5122 'A100': '#f4ff81',
5123 'A200': '#eeff41',
5124 'A400': '#c6ff00',
5125 'A700': '#aeea00',
5126 'contrastDefaultColor': 'dark',
5127 'contrastLightColors': '900',
5128 'contrastStrongLightColors': '900'
5129 },
5130 'yellow': {
5131 '50': '#fffde7',
5132 '100': '#fff9c4',
5133 '200': '#fff59d',
5134 '300': '#fff176',
5135 '400': '#ffee58',
5136 '500': '#ffeb3b',
5137 '600': '#fdd835',
5138 '700': '#fbc02d',
5139 '800': '#f9a825',
5140 '900': '#f57f17',
5141 'A100': '#ffff8d',
5142 'A200': '#ffff00',
5143 'A400': '#ffea00',
5144 'A700': '#ffd600',
5145 'contrastDefaultColor': 'dark'
5146 },
5147 'amber': {
5148 '50': '#fff8e1',
5149 '100': '#ffecb3',
5150 '200': '#ffe082',
5151 '300': '#ffd54f',
5152 '400': '#ffca28',
5153 '500': '#ffc107',
5154 '600': '#ffb300',
5155 '700': '#ffa000',
5156 '800': '#ff8f00',
5157 '900': '#ff6f00',
5158 'A100': '#ffe57f',
5159 'A200': '#ffd740',
5160 'A400': '#ffc400',
5161 'A700': '#ffab00',
5162 'contrastDefaultColor': 'dark'
5163 },
5164 'orange': {
5165 '50': '#fff3e0',
5166 '100': '#ffe0b2',
5167 '200': '#ffcc80',
5168 '300': '#ffb74d',
5169 '400': '#ffa726',
5170 '500': '#ff9800',
5171 '600': '#fb8c00',
5172 '700': '#f57c00',
5173 '800': '#ef6c00',
5174 '900': '#e65100',
5175 'A100': '#ffd180',
5176 'A200': '#ffab40',
5177 'A400': '#ff9100',
5178 'A700': '#ff6d00',
5179 'contrastDefaultColor': 'dark',
5180 'contrastLightColors': '800 900',
5181 'contrastStrongLightColors': '800 900'
5182 },
5183 'deep-orange': {
5184 '50': '#fbe9e7',
5185 '100': '#ffccbc',
5186 '200': '#ffab91',
5187 '300': '#ff8a65',
5188 '400': '#ff7043',
5189 '500': '#ff5722',
5190 '600': '#f4511e',
5191 '700': '#e64a19',
5192 '800': '#d84315',
5193 '900': '#bf360c',
5194 'A100': '#ff9e80',
5195 'A200': '#ff6e40',
5196 'A400': '#ff3d00',
5197 'A700': '#dd2c00',
5198 'contrastDefaultColor': 'light',
5199 'contrastDarkColors': '50 100 200 300 400 A100 A200',
5200 'contrastStrongLightColors': '500 600 700 800 900 A400 A700'
5201 },
5202 'brown': {
5203 '50': '#efebe9',
5204 '100': '#d7ccc8',
5205 '200': '#bcaaa4',
5206 '300': '#a1887f',
5207 '400': '#8d6e63',
5208 '500': '#795548',
5209 '600': '#6d4c41',
5210 '700': '#5d4037',
5211 '800': '#4e342e',
5212 '900': '#3e2723',
5213 'A100': '#d7ccc8',
5214 'A200': '#bcaaa4',
5215 'A400': '#8d6e63',
5216 'A700': '#5d4037',
5217 'contrastDefaultColor': 'light',
5218 'contrastDarkColors': '50 100 200 A100 A200',
5219 'contrastStrongLightColors': '300 400'
5220 },
5221 'grey': {
5222 '50': '#fafafa',
5223 '100': '#f5f5f5',
5224 '200': '#eeeeee',
5225 '300': '#e0e0e0',
5226 '400': '#bdbdbd',
5227 '500': '#9e9e9e',
5228 '600': '#757575',
5229 '700': '#616161',
5230 '800': '#424242',
5231 '900': '#212121',
5232 'A100': '#ffffff',
5233 'A200': '#000000',
5234 'A400': '#303030',
5235 'A700': '#616161',
5236 'contrastDefaultColor': 'dark',
5237 'contrastLightColors': '600 700 800 900 A200 A400 A700'
5238 },
5239 'blue-grey': {
5240 '50': '#eceff1',
5241 '100': '#cfd8dc',
5242 '200': '#b0bec5',
5243 '300': '#90a4ae',
5244 '400': '#78909c',
5245 '500': '#607d8b',
5246 '600': '#546e7a',
5247 '700': '#455a64',
5248 '800': '#37474f',
5249 '900': '#263238',
5250 'A100': '#cfd8dc',
5251 'A200': '#b0bec5',
5252 'A400': '#78909c',
5253 'A700': '#455a64',
5254 'contrastDefaultColor': 'light',
5255 'contrastDarkColors': '50 100 200 300 A100 A200',
5256 'contrastStrongLightColors': '400 500 700'
5257 }
5258});
5259
5260(function(angular) {
5261 'use strict';
5262/**
5263 * @ngdoc module
5264 * @name material.core.theming
5265 * @description
5266 * Theming
5267 */
5268detectDisabledThemes.$inject = ["$mdThemingProvider"];
5269ThemingDirective.$inject = ["$mdTheming", "$interpolate", "$log"];
5270ThemableDirective.$inject = ["$mdTheming"];
5271ThemingProvider.$inject = ["$mdColorPalette", "$$mdMetaProvider"];
5272generateAllThemes.$inject = ["$injector", "$mdTheming"];
5273angular.module('material.core.theming', ['material.core.theming.palette', 'material.core.meta'])
5274 .directive('mdTheme', ThemingDirective)
5275 .directive('mdThemable', ThemableDirective)
5276 .directive('mdThemesDisabled', disableThemesDirective )
5277 .provider('$mdTheming', ThemingProvider)
5278 .config( detectDisabledThemes )
5279 .run(generateAllThemes);
5280
5281/**
5282 * Detect if the HTML or the BODY tags has a [md-themes-disabled] attribute
5283 * If yes, then immediately disable all theme stylesheet generation and DOM injection
5284 */
5285/**
5286 * ngInject
5287 */
5288function detectDisabledThemes($mdThemingProvider) {
5289 var isDisabled = !!document.querySelector('[md-themes-disabled]');
5290 $mdThemingProvider.disableTheming(isDisabled);
5291}
5292
5293/**
5294 * @ngdoc service
5295 * @name $mdThemingProvider
5296 * @module material.core.theming
5297 *
5298 * @description Provider to configure the `$mdTheming` service.
5299 *
5300 * ### Default Theme
5301 * The `$mdThemingProvider` uses by default the following theme configuration:
5302 *
5303 * - Primary Palette: `Primary`
5304 * - Accent Palette: `Pink`
5305 * - Warn Palette: `Deep-Orange`
5306 * - Background Palette: `Grey`
5307 *
5308 * If you don't want to use the `md-theme` directive on the elements itself, you may want to overwrite
5309 * the default theme.<br/>
5310 * This can be done by using the following markup.
5311 *
5312 * <hljs lang="js">
5313 * myAppModule.config(function($mdThemingProvider) {
5314 * $mdThemingProvider
5315 * .theme('default')
5316 * .primaryPalette('blue')
5317 * .accentPalette('teal')
5318 * .warnPalette('red')
5319 * .backgroundPalette('grey');
5320 * });
5321 * </hljs>
5322 *
5323
5324 * ### Dynamic Themes
5325 *
5326 * By default, if you change a theme at runtime, the `$mdTheming` service will not detect those changes.<br/>
5327 * If you have an application, which changes its theme on runtime, you have to enable theme watching.
5328 *
5329 * <hljs lang="js">
5330 * myAppModule.config(function($mdThemingProvider) {
5331 * // Enable theme watching.
5332 * $mdThemingProvider.alwaysWatchTheme(true);
5333 * });
5334 * </hljs>
5335 *
5336 * ### Custom Theme Styles
5337 *
5338 * Sometimes you may want to use your own theme styles for some custom components.<br/>
5339 * You are able to register your own styles by using the following markup.
5340 *
5341 * <hljs lang="js">
5342 * myAppModule.config(function($mdThemingProvider) {
5343 * // Register our custom stylesheet into the theming provider.
5344 * $mdThemingProvider.registerStyles(STYLESHEET);
5345 * });
5346 * </hljs>
5347 *
5348 * The `registerStyles` method only accepts strings as value, so you're actually not able to load an external
5349 * stylesheet file into the `$mdThemingProvider`.
5350 *
5351 * If it's necessary to load an external stylesheet, we suggest using a bundler, which supports including raw content,
5352 * like [raw-loader](https://github.com/webpack/raw-loader) for `webpack`.
5353 *
5354 * <hljs lang="js">
5355 * myAppModule.config(function($mdThemingProvider) {
5356 * // Register your custom stylesheet into the theming provider.
5357 * $mdThemingProvider.registerStyles(require('../styles/my-component.theme.css'));
5358 * });
5359 * </hljs>
5360 *
5361 * ### Browser color
5362 *
5363 * Enables browser header coloring
5364 * for more info please visit:
5365 * https://developers.google.com/web/fundamentals/design-and-ui/browser-customization/theme-color
5366 *
5367 * Options parameter: <br/>
5368 * `theme` - A defined theme via `$mdThemeProvider` to use the palettes from. Default is `default` theme. <br/>
5369 * `palette` - Can be any one of the basic material design palettes, extended defined palettes and 'primary',
5370 * 'accent', 'background' and 'warn'. Default is `primary`. <br/>
5371 * `hue` - The hue from the selected palette. Default is `800`<br/>
5372 *
5373 * <hljs lang="js">
5374 * myAppModule.config(function($mdThemingProvider) {
5375 * // Enable browser color
5376 * $mdThemingProvider.enableBrowserColor({
5377 * theme: 'myTheme', // Default is 'default'
5378 * palette: 'accent', // Default is 'primary', any basic material palette and extended palettes are available
5379 * hue: '200' // Default is '800'
5380 * });
5381 * });
5382 * </hljs>
5383 */
5384
5385/**
5386 * @ngdoc method
5387 * @name $mdThemingProvider#registerStyles
5388 * @param {string} styles The styles to be appended to Angular Material's built in theme css.
5389 */
5390/**
5391 * @ngdoc method
5392 * @name $mdThemingProvider#setNonce
5393 * @param {string} nonceValue The nonce to be added as an attribute to the theme style tags.
5394 * Setting a value allows the use of CSP policy without using the unsafe-inline directive.
5395 */
5396
5397/**
5398 * @ngdoc method
5399 * @name $mdThemingProvider#setDefaultTheme
5400 * @param {string} themeName Default theme name to be applied to elements. Default value is `default`.
5401 */
5402
5403/**
5404 * @ngdoc method
5405 * @name $mdThemingProvider#alwaysWatchTheme
5406 * @param {boolean} watch Whether or not to always watch themes for changes and re-apply
5407 * classes when they change. Default is `false`. Enabling can reduce performance.
5408 */
5409
5410/**
5411 * @ngdoc method
5412 * @name $mdThemingProvider#enableBrowserColor
5413 * @param {Object=} options Options object for the browser color<br/>
5414 * `theme` - A defined theme via `$mdThemeProvider` to use the palettes from. Default is `default` theme. <br/>
5415 * `palette` - Can be any one of the basic material design palettes, extended defined palettes and 'primary',
5416 * 'accent', 'background' and 'warn'. Default is `primary`. <br/>
5417 * `hue` - The hue from the selected palette. Default is `800`<br/>
5418 * @returns {Function} remove function of the browser color
5419 */
5420
5421/* Some Example Valid Theming Expressions
5422 * =======================================
5423 *
5424 * Intention group expansion: (valid for primary, accent, warn, background)
5425 *
5426 * {{primary-100}} - grab shade 100 from the primary palette
5427 * {{primary-100-0.7}} - grab shade 100, apply opacity of 0.7
5428 * {{primary-100-contrast}} - grab shade 100's contrast color
5429 * {{primary-hue-1}} - grab the shade assigned to hue-1 from the primary palette
5430 * {{primary-hue-1-0.7}} - apply 0.7 opacity to primary-hue-1
5431 * {{primary-color}} - Generates .md-hue-1, .md-hue-2, .md-hue-3 with configured shades set for each hue
5432 * {{primary-color-0.7}} - Apply 0.7 opacity to each of the above rules
5433 * {{primary-contrast}} - Generates .md-hue-1, .md-hue-2, .md-hue-3 with configured contrast (ie. text) color shades set for each hue
5434 * {{primary-contrast-0.7}} - Apply 0.7 opacity to each of the above rules
5435 *
5436 * Foreground expansion: Applies rgba to black/white foreground text
5437 *
5438 * {{foreground-1}} - used for primary text
5439 * {{foreground-2}} - used for secondary text/divider
5440 * {{foreground-3}} - used for disabled text
5441 * {{foreground-4}} - used for dividers
5442 *
5443 */
5444
5445// In memory generated CSS rules; registered by theme.name
5446var GENERATED = { };
5447
5448// In memory storage of defined themes and color palettes (both loaded by CSS, and user specified)
5449var PALETTES;
5450
5451// Text Colors on light and dark backgrounds
5452// @see https://www.google.com/design/spec/style/color.html#color-text-background-colors
5453var DARK_FOREGROUND = {
5454 name: 'dark',
5455 '1': 'rgba(0,0,0,0.87)',
5456 '2': 'rgba(0,0,0,0.54)',
5457 '3': 'rgba(0,0,0,0.38)',
5458 '4': 'rgba(0,0,0,0.12)'
5459};
5460var LIGHT_FOREGROUND = {
5461 name: 'light',
5462 '1': 'rgba(255,255,255,1.0)',
5463 '2': 'rgba(255,255,255,0.7)',
5464 '3': 'rgba(255,255,255,0.5)',
5465 '4': 'rgba(255,255,255,0.12)'
5466};
5467
5468var DARK_SHADOW = '1px 1px 0px rgba(0,0,0,0.4), -1px -1px 0px rgba(0,0,0,0.4)';
5469var LIGHT_SHADOW = '';
5470
5471var DARK_CONTRAST_COLOR = colorToRgbaArray('rgba(0,0,0,0.87)');
5472var LIGHT_CONTRAST_COLOR = colorToRgbaArray('rgba(255,255,255,0.87)');
5473var STRONG_LIGHT_CONTRAST_COLOR = colorToRgbaArray('rgb(255,255,255)');
5474
5475var THEME_COLOR_TYPES = ['primary', 'accent', 'warn', 'background'];
5476var DEFAULT_COLOR_TYPE = 'primary';
5477
5478// A color in a theme will use these hues by default, if not specified by user.
5479var LIGHT_DEFAULT_HUES = {
5480 'accent': {
5481 'default': 'A200',
5482 'hue-1': 'A100',
5483 'hue-2': 'A400',
5484 'hue-3': 'A700'
5485 },
5486 'background': {
5487 'default': '50',
5488 'hue-1': 'A100',
5489 'hue-2': '100',
5490 'hue-3': '300'
5491 }
5492};
5493
5494var DARK_DEFAULT_HUES = {
5495 'background': {
5496 'default': 'A400',
5497 'hue-1': '800',
5498 'hue-2': '900',
5499 'hue-3': 'A200'
5500 }
5501};
5502THEME_COLOR_TYPES.forEach(function(colorType) {
5503 // Color types with unspecified default hues will use these default hue values
5504 var defaultDefaultHues = {
5505 'default': '500',
5506 'hue-1': '300',
5507 'hue-2': '800',
5508 'hue-3': 'A100'
5509 };
5510 if (!LIGHT_DEFAULT_HUES[colorType]) LIGHT_DEFAULT_HUES[colorType] = defaultDefaultHues;
5511 if (!DARK_DEFAULT_HUES[colorType]) DARK_DEFAULT_HUES[colorType] = defaultDefaultHues;
5512});
5513
5514var VALID_HUE_VALUES = [
5515 '50', '100', '200', '300', '400', '500', '600',
5516 '700', '800', '900', 'A100', 'A200', 'A400', 'A700'
5517];
5518
5519var themeConfig = {
5520 disableTheming : false, // Generate our themes at run time; also disable stylesheet DOM injection
5521 generateOnDemand : false, // Whether or not themes are to be generated on-demand (vs. eagerly).
5522 registeredStyles : [], // Custom styles registered to be used in the theming of custom components.
5523 nonce : null // Nonce to be added as an attribute to the generated themes style tags.
5524};
5525
5526/**
5527 *
5528 */
5529function ThemingProvider($mdColorPalette, $$mdMetaProvider) {
5530 ThemingService.$inject = ["$rootScope", "$log"];
5531 PALETTES = { };
5532 var THEMES = { };
5533
5534 var themingProvider;
5535
5536 var alwaysWatchTheme = false;
5537 var defaultTheme = 'default';
5538
5539 // Load JS Defined Palettes
5540 angular.extend(PALETTES, $mdColorPalette);
5541
5542 // Default theme defined in core.js
5543
5544 /**
5545 * Adds `theme-color` and `msapplication-navbutton-color` meta tags with the color parameter
5546 * @param {string} color Hex value of the wanted browser color
5547 * @returns {Function} Remove function of the meta tags
5548 */
5549 var setBrowserColor = function (color) {
5550 // Chrome, Firefox OS and Opera
5551 var removeChrome = $$mdMetaProvider.setMeta('theme-color', color);
5552 // Windows Phone
5553 var removeWindows = $$mdMetaProvider.setMeta('msapplication-navbutton-color', color);
5554
5555 return function () {
5556 removeChrome();
5557 removeWindows();
5558 }
5559 };
5560
5561 /**
5562 * Enables browser header coloring
5563 * for more info please visit:
5564 * https://developers.google.com/web/fundamentals/design-and-ui/browser-customization/theme-color
5565 *
5566 * The default color is `800` from `primary` palette of the `default` theme
5567 *
5568 * options are:
5569 * `theme` - A defined theme via `$mdThemeProvider` to use the palettes from. Default is `default` theme
5570 * `palette` - Can be any one of the basic material design palettes, extended defined palettes and 'primary',
5571 * 'accent', 'background' and 'warn'. Default is `primary`
5572 * `hue` - The hue from the selected palette. Default is `800`
5573 *
5574 * @param {Object=} options Options object for the browser color
5575 * @returns {Function} remove function of the browser color
5576 */
5577 var enableBrowserColor = function (options) {
5578 options = angular.isObject(options) ? options : {};
5579
5580 var theme = options.theme || 'default';
5581 var hue = options.hue || '800';
5582
5583 var palette = PALETTES[options.palette] ||
5584 PALETTES[THEMES[theme].colors[options.palette || 'primary'].name];
5585
5586 var color = angular.isObject(palette[hue]) ? palette[hue].hex : palette[hue];
5587
5588 return setBrowserColor(color);
5589 };
5590
5591 return themingProvider = {
5592 definePalette: definePalette,
5593 extendPalette: extendPalette,
5594 theme: registerTheme,
5595
5596 /**
5597 * return a read-only clone of the current theme configuration
5598 */
5599 configuration : function() {
5600 return angular.extend( { }, themeConfig, {
5601 defaultTheme : defaultTheme,
5602 alwaysWatchTheme : alwaysWatchTheme,
5603 registeredStyles : [].concat(themeConfig.registeredStyles)
5604 });
5605 },
5606
5607 /**
5608 * Easy way to disable theming without having to use
5609 * `.constant("$MD_THEME_CSS","");` This disables
5610 * all dynamic theme style sheet generations and injections...
5611 */
5612 disableTheming: function(isDisabled) {
5613 themeConfig.disableTheming = angular.isUndefined(isDisabled) || !!isDisabled;
5614 },
5615
5616 registerStyles: function(styles) {
5617 themeConfig.registeredStyles.push(styles);
5618 },
5619
5620 setNonce: function(nonceValue) {
5621 themeConfig.nonce = nonceValue;
5622 },
5623
5624 generateThemesOnDemand: function(onDemand) {
5625 themeConfig.generateOnDemand = onDemand;
5626 },
5627
5628 setDefaultTheme: function(theme) {
5629 defaultTheme = theme;
5630 },
5631
5632 alwaysWatchTheme: function(alwaysWatch) {
5633 alwaysWatchTheme = alwaysWatch;
5634 },
5635
5636 enableBrowserColor: enableBrowserColor,
5637
5638 $get: ThemingService,
5639 _LIGHT_DEFAULT_HUES: LIGHT_DEFAULT_HUES,
5640 _DARK_DEFAULT_HUES: DARK_DEFAULT_HUES,
5641 _PALETTES: PALETTES,
5642 _THEMES: THEMES,
5643 _parseRules: parseRules,
5644 _rgba: rgba
5645 };
5646
5647 // Example: $mdThemingProvider.definePalette('neonRed', { 50: '#f5fafa', ... });
5648 function definePalette(name, map) {
5649 map = map || {};
5650 PALETTES[name] = checkPaletteValid(name, map);
5651 return themingProvider;
5652 }
5653
5654 // Returns an new object which is a copy of a given palette `name` with variables from
5655 // `map` overwritten
5656 // Example: var neonRedMap = $mdThemingProvider.extendPalette('red', { 50: '#f5fafafa' });
5657 function extendPalette(name, map) {
5658 return checkPaletteValid(name, angular.extend({}, PALETTES[name] || {}, map) );
5659 }
5660
5661 // Make sure that palette has all required hues
5662 function checkPaletteValid(name, map) {
5663 var missingColors = VALID_HUE_VALUES.filter(function(field) {
5664 return !map[field];
5665 });
5666 if (missingColors.length) {
5667 throw new Error("Missing colors %1 in palette %2!"
5668 .replace('%1', missingColors.join(', '))
5669 .replace('%2', name));
5670 }
5671
5672 return map;
5673 }
5674
5675 // Register a theme (which is a collection of color palettes to use with various states
5676 // ie. warn, accent, primary )
5677 // Optionally inherit from an existing theme
5678 // $mdThemingProvider.theme('custom-theme').primaryPalette('red');
5679 function registerTheme(name, inheritFrom) {
5680 if (THEMES[name]) return THEMES[name];
5681
5682 inheritFrom = inheritFrom || 'default';
5683
5684 var parentTheme = typeof inheritFrom === 'string' ? THEMES[inheritFrom] : inheritFrom;
5685 var theme = new Theme(name);
5686
5687 if (parentTheme) {
5688 angular.forEach(parentTheme.colors, function(color, colorType) {
5689 theme.colors[colorType] = {
5690 name: color.name,
5691 // Make sure a COPY of the hues is given to the child color,
5692 // not the same reference.
5693 hues: angular.extend({}, color.hues)
5694 };
5695 });
5696 }
5697 THEMES[name] = theme;
5698
5699 return theme;
5700 }
5701
5702 function Theme(name) {
5703 var self = this;
5704 self.name = name;
5705 self.colors = {};
5706
5707 self.dark = setDark;
5708 setDark(false);
5709
5710 function setDark(isDark) {
5711 isDark = arguments.length === 0 ? true : !!isDark;
5712
5713 // If no change, abort
5714 if (isDark === self.isDark) return;
5715
5716 self.isDark = isDark;
5717
5718 self.foregroundPalette = self.isDark ? LIGHT_FOREGROUND : DARK_FOREGROUND;
5719 self.foregroundShadow = self.isDark ? DARK_SHADOW : LIGHT_SHADOW;
5720
5721 // Light and dark themes have different default hues.
5722 // Go through each existing color type for this theme, and for every
5723 // hue value that is still the default hue value from the previous light/dark setting,
5724 // set it to the default hue value from the new light/dark setting.
5725 var newDefaultHues = self.isDark ? DARK_DEFAULT_HUES : LIGHT_DEFAULT_HUES;
5726 var oldDefaultHues = self.isDark ? LIGHT_DEFAULT_HUES : DARK_DEFAULT_HUES;
5727 angular.forEach(newDefaultHues, function(newDefaults, colorType) {
5728 var color = self.colors[colorType];
5729 var oldDefaults = oldDefaultHues[colorType];
5730 if (color) {
5731 for (var hueName in color.hues) {
5732 if (color.hues[hueName] === oldDefaults[hueName]) {
5733 color.hues[hueName] = newDefaults[hueName];
5734 }
5735 }
5736 }
5737 });
5738
5739 return self;
5740 }
5741
5742 THEME_COLOR_TYPES.forEach(function(colorType) {
5743 var defaultHues = (self.isDark ? DARK_DEFAULT_HUES : LIGHT_DEFAULT_HUES)[colorType];
5744 self[colorType + 'Palette'] = function setPaletteType(paletteName, hues) {
5745 var color = self.colors[colorType] = {
5746 name: paletteName,
5747 hues: angular.extend({}, defaultHues, hues)
5748 };
5749
5750 Object.keys(color.hues).forEach(function(name) {
5751 if (!defaultHues[name]) {
5752 throw new Error("Invalid hue name '%1' in theme %2's %3 color %4. Available hue names: %4"
5753 .replace('%1', name)
5754 .replace('%2', self.name)
5755 .replace('%3', paletteName)
5756 .replace('%4', Object.keys(defaultHues).join(', '))
5757 );
5758 }
5759 });
5760 Object.keys(color.hues).map(function(key) {
5761 return color.hues[key];
5762 }).forEach(function(hueValue) {
5763 if (VALID_HUE_VALUES.indexOf(hueValue) == -1) {
5764 throw new Error("Invalid hue value '%1' in theme %2's %3 color %4. Available hue values: %5"
5765 .replace('%1', hueValue)
5766 .replace('%2', self.name)
5767 .replace('%3', colorType)
5768 .replace('%4', paletteName)
5769 .replace('%5', VALID_HUE_VALUES.join(', '))
5770 );
5771 }
5772 });
5773 return self;
5774 };
5775
5776 self[colorType + 'Color'] = function() {
5777 var args = Array.prototype.slice.call(arguments);
5778 console.warn('$mdThemingProviderTheme.' + colorType + 'Color() has been deprecated. ' +
5779 'Use $mdThemingProviderTheme.' + colorType + 'Palette() instead.');
5780 return self[colorType + 'Palette'].apply(self, args);
5781 };
5782 });
5783 }
5784
5785 /**
5786 * @ngdoc service
5787 * @name $mdTheming
5788 *
5789 * @description
5790 *
5791 * Service that makes an element apply theming related classes to itself.
5792 *
5793 * ```js
5794 * app.directive('myFancyDirective', function($mdTheming) {
5795 * return {
5796 * restrict: 'e',
5797 * link: function(scope, el, attrs) {
5798 * $mdTheming(el);
5799 * }
5800 * };
5801 * });
5802 * ```
5803 * @param {el=} element to apply theming to
5804 */
5805 /* ngInject */
5806 function ThemingService($rootScope, $log) {
5807 // Allow us to be invoked via a linking function signature.
5808 var applyTheme = function (scope, el) {
5809 if (el === undefined) { el = scope; scope = undefined; }
5810 if (scope === undefined) { scope = $rootScope; }
5811 applyTheme.inherit(el, el);
5812 };
5813
5814 applyTheme.THEMES = angular.extend({}, THEMES);
5815 applyTheme.PALETTES = angular.extend({}, PALETTES);
5816 applyTheme.inherit = inheritTheme;
5817 applyTheme.registered = registered;
5818 applyTheme.defaultTheme = function() { return defaultTheme; };
5819 applyTheme.generateTheme = function(name) { generateTheme(THEMES[name], name, themeConfig.nonce); };
5820 applyTheme.setBrowserColor = enableBrowserColor;
5821
5822 return applyTheme;
5823
5824 /**
5825 * Determine is specified theme name is a valid, registered theme
5826 */
5827 function registered(themeName) {
5828 if (themeName === undefined || themeName === '') return true;
5829 return applyTheme.THEMES[themeName] !== undefined;
5830 }
5831
5832 /**
5833 * Get theme name for the element, then update with Theme CSS class
5834 */
5835 function inheritTheme (el, parent) {
5836 var ctrl = parent.controller('mdTheme');
5837 var attrThemeValue = el.attr('md-theme-watch');
5838 var watchTheme = (alwaysWatchTheme || angular.isDefined(attrThemeValue)) && attrThemeValue != 'false';
5839
5840 updateThemeClass(lookupThemeName());
5841
5842 if ((alwaysWatchTheme && !registerChangeCallback()) || (!alwaysWatchTheme && watchTheme)) {
5843 el.on('$destroy', $rootScope.$watch(lookupThemeName, updateThemeClass) );
5844 }
5845
5846 /**
5847 * Find the theme name from the parent controller or element data
5848 */
5849 function lookupThemeName() {
5850 // As a few components (dialog) add their controllers later, we should also watch for a controller init.
5851 ctrl = parent.controller('mdTheme') || el.data('$mdThemeController');
5852 return ctrl && ctrl.$mdTheme || (defaultTheme == 'default' ? '' : defaultTheme);
5853 }
5854
5855 /**
5856 * Remove old theme class and apply a new one
5857 * NOTE: if not a valid theme name, then the current name is not changed
5858 */
5859 function updateThemeClass(theme) {
5860 if (!theme) return;
5861 if (!registered(theme)) {
5862 $log.warn('Attempted to use unregistered theme \'' + theme + '\'. ' +
5863 'Register it with $mdThemingProvider.theme().');
5864 }
5865
5866 var oldTheme = el.data('$mdThemeName');
5867 if (oldTheme) el.removeClass('md-' + oldTheme +'-theme');
5868 el.addClass('md-' + theme + '-theme');
5869 el.data('$mdThemeName', theme);
5870 if (ctrl) {
5871 el.data('$mdThemeController', ctrl);
5872 }
5873 }
5874
5875 /**
5876 * Register change callback with parent mdTheme controller
5877 */
5878 function registerChangeCallback() {
5879 var parentController = parent.controller('mdTheme');
5880 if (!parentController) return false;
5881 el.on('$destroy', parentController.registerChanges( function() {
5882 updateThemeClass(lookupThemeName());
5883 }));
5884 return true;
5885 }
5886 }
5887
5888 }
5889}
5890
5891function ThemingDirective($mdTheming, $interpolate, $log) {
5892 return {
5893 priority: 100,
5894 link: {
5895 pre: function(scope, el, attrs) {
5896 var registeredCallbacks = [];
5897 var ctrl = {
5898 registerChanges: function (cb, context) {
5899 if (context) {
5900 cb = angular.bind(context, cb);
5901 }
5902
5903 registeredCallbacks.push(cb);
5904
5905 return function () {
5906 var index = registeredCallbacks.indexOf(cb);
5907
5908 if (index > -1) {
5909 registeredCallbacks.splice(index, 1);
5910 }
5911 };
5912 },
5913 $setTheme: function (theme) {
5914 if (!$mdTheming.registered(theme)) {
5915 $log.warn('attempted to use unregistered theme \'' + theme + '\'');
5916 }
5917 ctrl.$mdTheme = theme;
5918
5919 registeredCallbacks.forEach(function (cb) {
5920 cb();
5921 });
5922 }
5923 };
5924 el.data('$mdThemeController', ctrl);
5925 ctrl.$setTheme($interpolate(attrs.mdTheme)(scope));
5926 attrs.$observe('mdTheme', ctrl.$setTheme);
5927 }
5928 }
5929 };
5930}
5931
5932/**
5933 * Special directive that will disable ALL runtime Theme style generation and DOM injection
5934 *
5935 * <link rel="stylesheet" href="angular-material.min.css">
5936 * <link rel="stylesheet" href="angular-material.themes.css">
5937 *
5938 * <body md-themes-disabled>
5939 * ...
5940 * </body>
5941 *
5942 * Note: Using md-themes-css directive requires the developer to load external
5943 * theme stylesheets; e.g. custom themes from Material-Tools:
5944 *
5945 * `angular-material.themes.css`
5946 *
5947 * Another option is to use the ThemingProvider to configure and disable the attribute
5948 * conversions; this would obviate the use of the `md-themes-css` directive
5949 *
5950 */
5951function disableThemesDirective() {
5952 themeConfig.disableTheming = true;
5953
5954 // Return a 1x-only, first-match attribute directive
5955 return {
5956 restrict : 'A',
5957 priority : '900'
5958 };
5959}
5960
5961function ThemableDirective($mdTheming) {
5962 return $mdTheming;
5963}
5964
5965function parseRules(theme, colorType, rules) {
5966 checkValidPalette(theme, colorType);
5967
5968 rules = rules.replace(/THEME_NAME/g, theme.name);
5969 var generatedRules = [];
5970 var color = theme.colors[colorType];
5971
5972 var themeNameRegex = new RegExp('\\.md-' + theme.name + '-theme', 'g');
5973 // Matches '{{ primary-color }}', etc
5974 var hueRegex = new RegExp('(\'|")?{{\\s*(' + colorType + ')-(color|contrast)-?(\\d\\.?\\d*)?\\s*}}(\"|\')?','g');
5975 var simpleVariableRegex = /'?"?\{\{\s*([a-zA-Z]+)-(A?\d+|hue\-[0-3]|shadow|default)-?(\d\.?\d*)?(contrast)?\s*\}\}'?"?/g;
5976 var palette = PALETTES[color.name];
5977
5978 // find and replace simple variables where we use a specific hue, not an entire palette
5979 // eg. "{{primary-100}}"
5980 //\(' + THEME_COLOR_TYPES.join('\|') + '\)'
5981 rules = rules.replace(simpleVariableRegex, function(match, colorType, hue, opacity, contrast) {
5982 if (colorType === 'foreground') {
5983 if (hue == 'shadow') {
5984 return theme.foregroundShadow;
5985 } else {
5986 return theme.foregroundPalette[hue] || theme.foregroundPalette['1'];
5987 }
5988 }
5989
5990 // `default` is also accepted as a hue-value, because the background palettes are
5991 // using it as a name for the default hue.
5992 if (hue.indexOf('hue') === 0 || hue === 'default') {
5993 hue = theme.colors[colorType].hues[hue];
5994 }
5995
5996 return rgba( (PALETTES[ theme.colors[colorType].name ][hue] || '')[contrast ? 'contrast' : 'value'], opacity );
5997 });
5998
5999 // For each type, generate rules for each hue (ie. default, md-hue-1, md-hue-2, md-hue-3)
6000 angular.forEach(color.hues, function(hueValue, hueName) {
6001 var newRule = rules
6002 .replace(hueRegex, function(match, _, colorType, hueType, opacity) {
6003 return rgba(palette[hueValue][hueType === 'color' ? 'value' : 'contrast'], opacity);
6004 });
6005 if (hueName !== 'default') {
6006 newRule = newRule.replace(themeNameRegex, '.md-' + theme.name + '-theme.md-' + hueName);
6007 }
6008
6009 // Don't apply a selector rule to the default theme, making it easier to override
6010 // styles of the base-component
6011 if (theme.name == 'default') {
6012 var themeRuleRegex = /((?:(?:(?: |>|\.|\w|-|:|\(|\)|\[|\]|"|'|=)+) )?)((?:(?:\w|\.|-)+)?)\.md-default-theme((?: |>|\.|\w|-|:|\(|\)|\[|\]|"|'|=)*)/g;
6013 newRule = newRule.replace(themeRuleRegex, function(match, prefix, target, suffix) {
6014 return match + ', ' + prefix + target + suffix;
6015 });
6016 }
6017 generatedRules.push(newRule);
6018 });
6019
6020 return generatedRules;
6021}
6022
6023var rulesByType = {};
6024
6025// Generate our themes at run time given the state of THEMES and PALETTES
6026function generateAllThemes($injector, $mdTheming) {
6027 var head = document.head;
6028 var firstChild = head ? head.firstElementChild : null;
6029 var themeCss = !themeConfig.disableTheming && $injector.has('$MD_THEME_CSS') ? $injector.get('$MD_THEME_CSS') : '';
6030
6031 // Append our custom registered styles to the theme stylesheet.
6032 themeCss += themeConfig.registeredStyles.join('');
6033
6034 if ( !firstChild ) return;
6035 if (themeCss.length === 0) return; // no rules, so no point in running this expensive task
6036
6037 // Expose contrast colors for palettes to ensure that text is always readable
6038 angular.forEach(PALETTES, sanitizePalette);
6039
6040 // MD_THEME_CSS is a string generated by the build process that includes all the themable
6041 // components as templates
6042
6043 // Break the CSS into individual rules
6044 var rules = themeCss
6045 .split(/\}(?!(\}|'|"|;))/)
6046 .filter(function(rule) { return rule && rule.trim().length; })
6047 .map(function(rule) { return rule.trim() + '}'; });
6048
6049
6050 var ruleMatchRegex = new RegExp('md-(' + THEME_COLOR_TYPES.join('|') + ')', 'g');
6051
6052 THEME_COLOR_TYPES.forEach(function(type) {
6053 rulesByType[type] = '';
6054 });
6055
6056
6057 // Sort the rules based on type, allowing us to do color substitution on a per-type basis
6058 rules.forEach(function(rule) {
6059 var match = rule.match(ruleMatchRegex);
6060 // First: test that if the rule has '.md-accent', it goes into the accent set of rules
6061 for (var i = 0, type; type = THEME_COLOR_TYPES[i]; i++) {
6062 if (rule.indexOf('.md-' + type) > -1) {
6063 return rulesByType[type] += rule;
6064 }
6065 }
6066
6067 // If no eg 'md-accent' class is found, try to just find 'accent' in the rule and guess from
6068 // there
6069 for (i = 0; type = THEME_COLOR_TYPES[i]; i++) {
6070 if (rule.indexOf(type) > -1) {
6071 return rulesByType[type] += rule;
6072 }
6073 }
6074
6075 // Default to the primary array
6076 return rulesByType[DEFAULT_COLOR_TYPE] += rule;
6077 });
6078
6079 // If themes are being generated on-demand, quit here. The user will later manually
6080 // call generateTheme to do this on a theme-by-theme basis.
6081 if (themeConfig.generateOnDemand) return;
6082
6083 angular.forEach($mdTheming.THEMES, function(theme) {
6084 if (!GENERATED[theme.name] && !($mdTheming.defaultTheme() !== 'default' && theme.name === 'default')) {
6085 generateTheme(theme, theme.name, themeConfig.nonce);
6086 }
6087 });
6088
6089
6090 // *************************
6091 // Internal functions
6092 // *************************
6093
6094 // The user specifies a 'default' contrast color as either light or dark,
6095 // then explicitly lists which hues are the opposite contrast (eg. A100 has dark, A200 has light)
6096 function sanitizePalette(palette, name) {
6097 var defaultContrast = palette.contrastDefaultColor;
6098 var lightColors = palette.contrastLightColors || [];
6099 var strongLightColors = palette.contrastStrongLightColors || [];
6100 var darkColors = palette.contrastDarkColors || [];
6101
6102 // These colors are provided as space-separated lists
6103 if (typeof lightColors === 'string') lightColors = lightColors.split(' ');
6104 if (typeof strongLightColors === 'string') strongLightColors = strongLightColors.split(' ');
6105 if (typeof darkColors === 'string') darkColors = darkColors.split(' ');
6106
6107 // Cleanup after ourselves
6108 delete palette.contrastDefaultColor;
6109 delete palette.contrastLightColors;
6110 delete palette.contrastStrongLightColors;
6111 delete palette.contrastDarkColors;
6112
6113 // Change { 'A100': '#fffeee' } to { 'A100': { value: '#fffeee', contrast:DARK_CONTRAST_COLOR }
6114 angular.forEach(palette, function(hueValue, hueName) {
6115 if (angular.isObject(hueValue)) return; // Already converted
6116 // Map everything to rgb colors
6117 var rgbValue = colorToRgbaArray(hueValue);
6118 if (!rgbValue) {
6119 throw new Error("Color %1, in palette %2's hue %3, is invalid. Hex or rgb(a) color expected."
6120 .replace('%1', hueValue)
6121 .replace('%2', palette.name)
6122 .replace('%3', hueName));
6123 }
6124
6125 palette[hueName] = {
6126 hex: palette[hueName],
6127 value: rgbValue,
6128 contrast: getContrastColor()
6129 };
6130 function getContrastColor() {
6131 if (defaultContrast === 'light') {
6132 if (darkColors.indexOf(hueName) > -1) {
6133 return DARK_CONTRAST_COLOR;
6134 } else {
6135 return strongLightColors.indexOf(hueName) > -1 ? STRONG_LIGHT_CONTRAST_COLOR
6136 : LIGHT_CONTRAST_COLOR;
6137 }
6138 } else {
6139 if (lightColors.indexOf(hueName) > -1) {
6140 return strongLightColors.indexOf(hueName) > -1 ? STRONG_LIGHT_CONTRAST_COLOR
6141 : LIGHT_CONTRAST_COLOR;
6142 } else {
6143 return DARK_CONTRAST_COLOR;
6144 }
6145 }
6146 }
6147 });
6148 }
6149}
6150
6151function generateTheme(theme, name, nonce) {
6152 var head = document.head;
6153 var firstChild = head ? head.firstElementChild : null;
6154
6155 if (!GENERATED[name]) {
6156 // For each theme, use the color palettes specified for
6157 // `primary`, `warn` and `accent` to generate CSS rules.
6158 THEME_COLOR_TYPES.forEach(function(colorType) {
6159 var styleStrings = parseRules(theme, colorType, rulesByType[colorType]);
6160 while (styleStrings.length) {
6161 var styleContent = styleStrings.shift();
6162 if (styleContent) {
6163 var style = document.createElement('style');
6164 style.setAttribute('md-theme-style', '');
6165 if (nonce) {
6166 style.setAttribute('nonce', nonce);
6167 }
6168 style.appendChild(document.createTextNode(styleContent));
6169 head.insertBefore(style, firstChild);
6170 }
6171 }
6172 });
6173
6174 GENERATED[theme.name] = true;
6175 }
6176
6177}
6178
6179
6180function checkValidPalette(theme, colorType) {
6181 // If theme attempts to use a palette that doesnt exist, throw error
6182 if (!PALETTES[ (theme.colors[colorType] || {}).name ]) {
6183 throw new Error(
6184 "You supplied an invalid color palette for theme %1's %2 palette. Available palettes: %3"
6185 .replace('%1', theme.name)
6186 .replace('%2', colorType)
6187 .replace('%3', Object.keys(PALETTES).join(', '))
6188 );
6189 }
6190}
6191
6192function colorToRgbaArray(clr) {
6193 if (angular.isArray(clr) && clr.length == 3) return clr;
6194 if (/^rgb/.test(clr)) {
6195 return clr.replace(/(^\s*rgba?\(|\)\s*$)/g, '').split(',').map(function(value, i) {
6196 return i == 3 ? parseFloat(value, 10) : parseInt(value, 10);
6197 });
6198 }
6199 if (clr.charAt(0) == '#') clr = clr.substring(1);
6200 if (!/^([a-fA-F0-9]{3}){1,2}$/g.test(clr)) return;
6201
6202 var dig = clr.length / 3;
6203 var red = clr.substr(0, dig);
6204 var grn = clr.substr(dig, dig);
6205 var blu = clr.substr(dig * 2);
6206 if (dig === 1) {
6207 red += red;
6208 grn += grn;
6209 blu += blu;
6210 }
6211 return [parseInt(red, 16), parseInt(grn, 16), parseInt(blu, 16)];
6212}
6213
6214function rgba(rgbArray, opacity) {
6215 if ( !rgbArray ) return "rgb('0,0,0')";
6216
6217 if (rgbArray.length == 4) {
6218 rgbArray = angular.copy(rgbArray);
6219 opacity ? rgbArray.pop() : opacity = rgbArray.pop();
6220 }
6221 return opacity && (typeof opacity == 'number' || (typeof opacity == 'string' && opacity.length)) ?
6222 'rgba(' + rgbArray.join(',') + ',' + opacity + ')' :
6223 'rgb(' + rgbArray.join(',') + ')';
6224}
6225
6226
6227})(window.angular);
6228
6229// Polyfill angular < 1.4 (provide $animateCss)
6230angular
6231 .module('material.core')
6232 .factory('$$mdAnimate', ["$q", "$timeout", "$mdConstant", "$animateCss", function($q, $timeout, $mdConstant, $animateCss){
6233
6234 // Since $$mdAnimate is injected into $mdUtil... use a wrapper function
6235 // to subsequently inject $mdUtil as an argument to the AnimateDomUtils
6236
6237 return function($mdUtil) {
6238 return AnimateDomUtils( $mdUtil, $q, $timeout, $mdConstant, $animateCss);
6239 };
6240 }]);
6241
6242/**
6243 * Factory function that requires special injections
6244 */
6245function AnimateDomUtils($mdUtil, $q, $timeout, $mdConstant, $animateCss) {
6246 var self;
6247 return self = {
6248 /**
6249 *
6250 */
6251 translate3d : function( target, from, to, options ) {
6252 return $animateCss(target,{
6253 from:from,
6254 to:to,
6255 addClass:options.transitionInClass,
6256 removeClass:options.transitionOutClass
6257 })
6258 .start()
6259 .then(function(){
6260 // Resolve with reverser function...
6261 return reverseTranslate;
6262 });
6263
6264 /**
6265 * Specific reversal of the request translate animation above...
6266 */
6267 function reverseTranslate (newFrom) {
6268 return $animateCss(target, {
6269 to: newFrom || from,
6270 addClass: options.transitionOutClass,
6271 removeClass: options.transitionInClass
6272 }).start();
6273
6274 }
6275 },
6276
6277 /**
6278 * Listen for transitionEnd event (with optional timeout)
6279 * Announce completion or failure via promise handlers
6280 */
6281 waitTransitionEnd: function (element, opts) {
6282 var TIMEOUT = 3000; // fallback is 3 secs
6283
6284 return $q(function(resolve, reject){
6285 opts = opts || { };
6286
6287 // If there is no transition is found, resolve immediately
6288 //
6289 // NOTE: using $mdUtil.nextTick() causes delays/issues
6290 if (noTransitionFound(opts.cachedTransitionStyles)) {
6291 TIMEOUT = 0;
6292 }
6293
6294 var timer = $timeout(finished, opts.timeout || TIMEOUT);
6295 element.on($mdConstant.CSS.TRANSITIONEND, finished);
6296
6297 /**
6298 * Upon timeout or transitionEnd, reject or resolve (respectively) this promise.
6299 * NOTE: Make sure this transitionEnd didn't bubble up from a child
6300 */
6301 function finished(ev) {
6302 if ( ev && ev.target !== element[0]) return;
6303
6304 if ( ev ) $timeout.cancel(timer);
6305 element.off($mdConstant.CSS.TRANSITIONEND, finished);
6306
6307 // Never reject since ngAnimate may cause timeouts due missed transitionEnd events
6308 resolve();
6309
6310 }
6311
6312 /**
6313 * Checks whether or not there is a transition.
6314 *
6315 * @param styles The cached styles to use for the calculation. If null, getComputedStyle()
6316 * will be used.
6317 *
6318 * @returns {boolean} True if there is no transition/duration; false otherwise.
6319 */
6320 function noTransitionFound(styles) {
6321 styles = styles || window.getComputedStyle(element[0]);
6322
6323 return styles.transitionDuration == '0s' || (!styles.transition && !styles.transitionProperty);
6324 }
6325
6326 });
6327 },
6328
6329 calculateTransformValues: function (element, originator) {
6330 var origin = originator.element;
6331 var bounds = originator.bounds;
6332
6333 if (origin || bounds) {
6334 var originBnds = origin ? self.clientRect(origin) || currentBounds() : self.copyRect(bounds);
6335 var dialogRect = self.copyRect(element[0].getBoundingClientRect());
6336 var dialogCenterPt = self.centerPointFor(dialogRect);
6337 var originCenterPt = self.centerPointFor(originBnds);
6338
6339 return {
6340 centerX: originCenterPt.x - dialogCenterPt.x,
6341 centerY: originCenterPt.y - dialogCenterPt.y,
6342 scaleX: Math.round(100 * Math.min(0.5, originBnds.width / dialogRect.width)) / 100,
6343 scaleY: Math.round(100 * Math.min(0.5, originBnds.height / dialogRect.height)) / 100
6344 };
6345 }
6346 return {centerX: 0, centerY: 0, scaleX: 0.5, scaleY: 0.5};
6347
6348 /**
6349 * This is a fallback if the origin information is no longer valid, then the
6350 * origin bounds simply becomes the current bounds for the dialogContainer's parent
6351 */
6352 function currentBounds() {
6353 var cntr = element ? element.parent() : null;
6354 var parent = cntr ? cntr.parent() : null;
6355
6356 return parent ? self.clientRect(parent) : null;
6357 }
6358 },
6359
6360 /**
6361 * Calculate the zoom transform from dialog to origin.
6362 *
6363 * We use this to set the dialog position immediately;
6364 * then the md-transition-in actually translates back to
6365 * `translate3d(0,0,0) scale(1.0)`...
6366 *
6367 * NOTE: all values are rounded to the nearest integer
6368 */
6369 calculateZoomToOrigin: function (element, originator) {
6370 var zoomTemplate = "translate3d( {centerX}px, {centerY}px, 0 ) scale( {scaleX}, {scaleY} )";
6371 var buildZoom = angular.bind(null, $mdUtil.supplant, zoomTemplate);
6372
6373 return buildZoom(self.calculateTransformValues(element, originator));
6374 },
6375
6376 /**
6377 * Calculate the slide transform from panel to origin.
6378 * NOTE: all values are rounded to the nearest integer
6379 */
6380 calculateSlideToOrigin: function (element, originator) {
6381 var slideTemplate = "translate3d( {centerX}px, {centerY}px, 0 )";
6382 var buildSlide = angular.bind(null, $mdUtil.supplant, slideTemplate);
6383
6384 return buildSlide(self.calculateTransformValues(element, originator));
6385 },
6386
6387 /**
6388 * Enhance raw values to represent valid css stylings...
6389 */
6390 toCss : function( raw ) {
6391 var css = { };
6392 var lookups = 'left top right bottom width height x y min-width min-height max-width max-height';
6393
6394 angular.forEach(raw, function(value,key) {
6395 if ( angular.isUndefined(value) ) return;
6396
6397 if ( lookups.indexOf(key) >= 0 ) {
6398 css[key] = value + 'px';
6399 } else {
6400 switch (key) {
6401 case 'transition':
6402 convertToVendor(key, $mdConstant.CSS.TRANSITION, value);
6403 break;
6404 case 'transform':
6405 convertToVendor(key, $mdConstant.CSS.TRANSFORM, value);
6406 break;
6407 case 'transformOrigin':
6408 convertToVendor(key, $mdConstant.CSS.TRANSFORM_ORIGIN, value);
6409 break;
6410 case 'font-size':
6411 css['font-size'] = value; // font sizes aren't always in px
6412 break;
6413 }
6414 }
6415 });
6416
6417 return css;
6418
6419 function convertToVendor(key, vendor, value) {
6420 angular.forEach(vendor.split(' '), function (key) {
6421 css[key] = value;
6422 });
6423 }
6424 },
6425
6426 /**
6427 * Convert the translate CSS value to key/value pair(s).
6428 */
6429 toTransformCss: function (transform, addTransition, transition) {
6430 var css = {};
6431 angular.forEach($mdConstant.CSS.TRANSFORM.split(' '), function (key) {
6432 css[key] = transform;
6433 });
6434
6435 if (addTransition) {
6436 transition = transition || "all 0.4s cubic-bezier(0.25, 0.8, 0.25, 1) !important";
6437 css.transition = transition;
6438 }
6439
6440 return css;
6441 },
6442
6443 /**
6444 * Clone the Rect and calculate the height/width if needed
6445 */
6446 copyRect: function (source, destination) {
6447 if (!source) return null;
6448
6449 destination = destination || {};
6450
6451 angular.forEach('left top right bottom width height'.split(' '), function (key) {
6452 destination[key] = Math.round(source[key]);
6453 });
6454
6455 destination.width = destination.width || (destination.right - destination.left);
6456 destination.height = destination.height || (destination.bottom - destination.top);
6457
6458 return destination;
6459 },
6460
6461 /**
6462 * Calculate ClientRect of element; return null if hidden or zero size
6463 */
6464 clientRect: function (element) {
6465 var bounds = angular.element(element)[0].getBoundingClientRect();
6466 var isPositiveSizeClientRect = function (rect) {
6467 return rect && (rect.width > 0) && (rect.height > 0);
6468 };
6469
6470 // If the event origin element has zero size, it has probably been hidden.
6471 return isPositiveSizeClientRect(bounds) ? self.copyRect(bounds) : null;
6472 },
6473
6474 /**
6475 * Calculate 'rounded' center point of Rect
6476 */
6477 centerPointFor: function (targetRect) {
6478 return targetRect ? {
6479 x: Math.round(targetRect.left + (targetRect.width / 2)),
6480 y: Math.round(targetRect.top + (targetRect.height / 2))
6481 } : { x : 0, y : 0 };
6482 }
6483
6484 };
6485}
6486
6487
6488"use strict";
6489
6490if (angular.version.minor >= 4) {
6491 angular.module('material.core.animate', []);
6492} else {
6493(function() {
6494
6495 var forEach = angular.forEach;
6496
6497 var WEBKIT = angular.isDefined(document.documentElement.style.WebkitAppearance);
6498 var TRANSITION_PROP = WEBKIT ? 'WebkitTransition' : 'transition';
6499 var ANIMATION_PROP = WEBKIT ? 'WebkitAnimation' : 'animation';
6500 var PREFIX = WEBKIT ? '-webkit-' : '';
6501
6502 var TRANSITION_EVENTS = (WEBKIT ? 'webkitTransitionEnd ' : '') + 'transitionend';
6503 var ANIMATION_EVENTS = (WEBKIT ? 'webkitAnimationEnd ' : '') + 'animationend';
6504
6505 var $$ForceReflowFactory = ['$document', function($document) {
6506 return function() {
6507 return $document[0].body.clientWidth + 1;
6508 }
6509 }];
6510
6511 var $$rAFMutexFactory = ['$$rAF', function($$rAF) {
6512 return function() {
6513 var passed = false;
6514 $$rAF(function() {
6515 passed = true;
6516 });
6517 return function(fn) {
6518 passed ? fn() : $$rAF(fn);
6519 };
6520 };
6521 }];
6522
6523 var $$AnimateRunnerFactory = ['$q', '$$rAFMutex', function($q, $$rAFMutex) {
6524 var INITIAL_STATE = 0;
6525 var DONE_PENDING_STATE = 1;
6526 var DONE_COMPLETE_STATE = 2;
6527
6528 function AnimateRunner(host) {
6529 this.setHost(host);
6530
6531 this._doneCallbacks = [];
6532 this._runInAnimationFrame = $$rAFMutex();
6533 this._state = 0;
6534 }
6535
6536 AnimateRunner.prototype = {
6537 setHost: function(host) {
6538 this.host = host || {};
6539 },
6540
6541 done: function(fn) {
6542 if (this._state === DONE_COMPLETE_STATE) {
6543 fn();
6544 } else {
6545 this._doneCallbacks.push(fn);
6546 }
6547 },
6548
6549 progress: angular.noop,
6550
6551 getPromise: function() {
6552 if (!this.promise) {
6553 var self = this;
6554 this.promise = $q(function(resolve, reject) {
6555 self.done(function(status) {
6556 status === false ? reject() : resolve();
6557 });
6558 });
6559 }
6560 return this.promise;
6561 },
6562
6563 then: function(resolveHandler, rejectHandler) {
6564 return this.getPromise().then(resolveHandler, rejectHandler);
6565 },
6566
6567 'catch': function(handler) {
6568 return this.getPromise()['catch'](handler);
6569 },
6570
6571 'finally': function(handler) {
6572 return this.getPromise()['finally'](handler);
6573 },
6574
6575 pause: function() {
6576 if (this.host.pause) {
6577 this.host.pause();
6578 }
6579 },
6580
6581 resume: function() {
6582 if (this.host.resume) {
6583 this.host.resume();
6584 }
6585 },
6586
6587 end: function() {
6588 if (this.host.end) {
6589 this.host.end();
6590 }
6591 this._resolve(true);
6592 },
6593
6594 cancel: function() {
6595 if (this.host.cancel) {
6596 this.host.cancel();
6597 }
6598 this._resolve(false);
6599 },
6600
6601 complete: function(response) {
6602 var self = this;
6603 if (self._state === INITIAL_STATE) {
6604 self._state = DONE_PENDING_STATE;
6605 self._runInAnimationFrame(function() {
6606 self._resolve(response);
6607 });
6608 }
6609 },
6610
6611 _resolve: function(response) {
6612 if (this._state !== DONE_COMPLETE_STATE) {
6613 forEach(this._doneCallbacks, function(fn) {
6614 fn(response);
6615 });
6616 this._doneCallbacks.length = 0;
6617 this._state = DONE_COMPLETE_STATE;
6618 }
6619 }
6620 };
6621
6622 // Polyfill AnimateRunner.all which is used by input animations
6623 AnimateRunner.all = function(runners, callback) {
6624 var count = 0;
6625 var status = true;
6626 forEach(runners, function(runner) {
6627 runner.done(onProgress);
6628 });
6629
6630 function onProgress(response) {
6631 status = status && response;
6632 if (++count === runners.length) {
6633 callback(status);
6634 }
6635 }
6636 };
6637
6638 return AnimateRunner;
6639 }];
6640
6641 angular
6642 .module('material.core.animate', [])
6643 .factory('$$forceReflow', $$ForceReflowFactory)
6644 .factory('$$AnimateRunner', $$AnimateRunnerFactory)
6645 .factory('$$rAFMutex', $$rAFMutexFactory)
6646 .factory('$animateCss', ['$window', '$$rAF', '$$AnimateRunner', '$$forceReflow', '$$jqLite', '$timeout', '$animate',
6647 function($window, $$rAF, $$AnimateRunner, $$forceReflow, $$jqLite, $timeout, $animate) {
6648
6649 function init(element, options) {
6650
6651 var temporaryStyles = [];
6652 var node = getDomNode(element);
6653 var areAnimationsAllowed = node && $animate.enabled();
6654
6655 var hasCompleteStyles = false;
6656 var hasCompleteClasses = false;
6657
6658 if (areAnimationsAllowed) {
6659 if (options.transitionStyle) {
6660 temporaryStyles.push([PREFIX + 'transition', options.transitionStyle]);
6661 }
6662
6663 if (options.keyframeStyle) {
6664 temporaryStyles.push([PREFIX + 'animation', options.keyframeStyle]);
6665 }
6666
6667 if (options.delay) {
6668 temporaryStyles.push([PREFIX + 'transition-delay', options.delay + 's']);
6669 }
6670
6671 if (options.duration) {
6672 temporaryStyles.push([PREFIX + 'transition-duration', options.duration + 's']);
6673 }
6674
6675 hasCompleteStyles = options.keyframeStyle ||
6676 (options.to && (options.duration > 0 || options.transitionStyle));
6677 hasCompleteClasses = !!options.addClass || !!options.removeClass;
6678
6679 blockTransition(element, true);
6680 }
6681
6682 var hasCompleteAnimation = areAnimationsAllowed && (hasCompleteStyles || hasCompleteClasses);
6683
6684 applyAnimationFromStyles(element, options);
6685
6686 var animationClosed = false;
6687 var events, eventFn;
6688
6689 return {
6690 close: $window.close,
6691 start: function() {
6692 var runner = new $$AnimateRunner();
6693 waitUntilQuiet(function() {
6694 blockTransition(element, false);
6695 if (!hasCompleteAnimation) {
6696 return close();
6697 }
6698
6699 forEach(temporaryStyles, function(entry) {
6700 var key = entry[0];
6701 var value = entry[1];
6702 node.style[camelCase(key)] = value;
6703 });
6704
6705 applyClasses(element, options);
6706
6707 var timings = computeTimings(element);
6708 if (timings.duration === 0) {
6709 return close();
6710 }
6711
6712 var moreStyles = [];
6713
6714 if (options.easing) {
6715 if (timings.transitionDuration) {
6716 moreStyles.push([PREFIX + 'transition-timing-function', options.easing]);
6717 }
6718 if (timings.animationDuration) {
6719 moreStyles.push([PREFIX + 'animation-timing-function', options.easing]);
6720 }
6721 }
6722
6723 if (options.delay && timings.animationDelay) {
6724 moreStyles.push([PREFIX + 'animation-delay', options.delay + 's']);
6725 }
6726
6727 if (options.duration && timings.animationDuration) {
6728 moreStyles.push([PREFIX + 'animation-duration', options.duration + 's']);
6729 }
6730
6731 forEach(moreStyles, function(entry) {
6732 var key = entry[0];
6733 var value = entry[1];
6734 node.style[camelCase(key)] = value;
6735 temporaryStyles.push(entry);
6736 });
6737
6738 var maxDelay = timings.delay;
6739 var maxDelayTime = maxDelay * 1000;
6740 var maxDuration = timings.duration;
6741 var maxDurationTime = maxDuration * 1000;
6742 var startTime = Date.now();
6743
6744 events = [];
6745 if (timings.transitionDuration) {
6746 events.push(TRANSITION_EVENTS);
6747 }
6748 if (timings.animationDuration) {
6749 events.push(ANIMATION_EVENTS);
6750 }
6751 events = events.join(' ');
6752 eventFn = function(event) {
6753 event.stopPropagation();
6754 var ev = event.originalEvent || event;
6755 var timeStamp = ev.timeStamp || Date.now();
6756 var elapsedTime = parseFloat(ev.elapsedTime.toFixed(3));
6757 if (Math.max(timeStamp - startTime, 0) >= maxDelayTime && elapsedTime >= maxDuration) {
6758 close();
6759 }
6760 };
6761 element.on(events, eventFn);
6762
6763 applyAnimationToStyles(element, options);
6764
6765 $timeout(close, maxDelayTime + maxDurationTime * 1.5, false);
6766 });
6767
6768 return runner;
6769
6770 function close() {
6771 if (animationClosed) return;
6772 animationClosed = true;
6773
6774 if (events && eventFn) {
6775 element.off(events, eventFn);
6776 }
6777 applyClasses(element, options);
6778 applyAnimationStyles(element, options);
6779 forEach(temporaryStyles, function(entry) {
6780 node.style[camelCase(entry[0])] = '';
6781 });
6782 runner.complete(true);
6783 return runner;
6784 }
6785 }
6786 }
6787 }
6788
6789 function applyClasses(element, options) {
6790 if (options.addClass) {
6791 $$jqLite.addClass(element, options.addClass);
6792 options.addClass = null;
6793 }
6794 if (options.removeClass) {
6795 $$jqLite.removeClass(element, options.removeClass);
6796 options.removeClass = null;
6797 }
6798 }
6799
6800 function computeTimings(element) {
6801 var node = getDomNode(element);
6802 var cs = $window.getComputedStyle(node)
6803 var tdr = parseMaxTime(cs[prop('transitionDuration')]);
6804 var adr = parseMaxTime(cs[prop('animationDuration')]);
6805 var tdy = parseMaxTime(cs[prop('transitionDelay')]);
6806 var ady = parseMaxTime(cs[prop('animationDelay')]);
6807
6808 adr *= (parseInt(cs[prop('animationIterationCount')], 10) || 1);
6809 var duration = Math.max(adr, tdr);
6810 var delay = Math.max(ady, tdy);
6811
6812 return {
6813 duration: duration,
6814 delay: delay,
6815 animationDuration: adr,
6816 transitionDuration: tdr,
6817 animationDelay: ady,
6818 transitionDelay: tdy
6819 };
6820
6821 function prop(key) {
6822 return WEBKIT ? 'Webkit' + key.charAt(0).toUpperCase() + key.substr(1)
6823 : key;
6824 }
6825 }
6826
6827 function parseMaxTime(str) {
6828 var maxValue = 0;
6829 var values = (str || "").split(/\s*,\s*/);
6830 forEach(values, function(value) {
6831 // it's always safe to consider only second values and omit `ms` values since
6832 // getComputedStyle will always handle the conversion for us
6833 if (value.charAt(value.length - 1) == 's') {
6834 value = value.substring(0, value.length - 1);
6835 }
6836 value = parseFloat(value) || 0;
6837 maxValue = maxValue ? Math.max(value, maxValue) : value;
6838 });
6839 return maxValue;
6840 }
6841
6842 var cancelLastRAFRequest;
6843 var rafWaitQueue = [];
6844 function waitUntilQuiet(callback) {
6845 if (cancelLastRAFRequest) {
6846 cancelLastRAFRequest(); //cancels the request
6847 }
6848 rafWaitQueue.push(callback);
6849 cancelLastRAFRequest = $$rAF(function() {
6850 cancelLastRAFRequest = null;
6851
6852 // DO NOT REMOVE THIS LINE OR REFACTOR OUT THE `pageWidth` variable.
6853 // PLEASE EXAMINE THE `$$forceReflow` service to understand why.
6854 var pageWidth = $$forceReflow();
6855
6856 // we use a for loop to ensure that if the queue is changed
6857 // during this looping then it will consider new requests
6858 for (var i = 0; i < rafWaitQueue.length; i++) {
6859 rafWaitQueue[i](pageWidth);
6860 }
6861 rafWaitQueue.length = 0;
6862 });
6863 }
6864
6865 function applyAnimationStyles(element, options) {
6866 applyAnimationFromStyles(element, options);
6867 applyAnimationToStyles(element, options);
6868 }
6869
6870 function applyAnimationFromStyles(element, options) {
6871 if (options.from) {
6872 element.css(options.from);
6873 options.from = null;
6874 }
6875 }
6876
6877 function applyAnimationToStyles(element, options) {
6878 if (options.to) {
6879 element.css(options.to);
6880 options.to = null;
6881 }
6882 }
6883
6884 function getDomNode(element) {
6885 for (var i = 0; i < element.length; i++) {
6886 if (element[i].nodeType === 1) return element[i];
6887 }
6888 }
6889
6890 function blockTransition(element, bool) {
6891 var node = getDomNode(element);
6892 var key = camelCase(PREFIX + 'transition-delay');
6893 node.style[key] = bool ? '-9999s' : '';
6894 }
6895
6896 return init;
6897 }]);
6898
6899 /**
6900 * Older browsers [FF31] expect camelCase
6901 * property keys.
6902 * e.g.
6903 * animation-duration --> animationDuration
6904 */
6905 function camelCase(str) {
6906 return str.replace(/-[a-z]/g, function(str) {
6907 return str.charAt(1).toUpperCase();
6908 });
6909 }
6910
6911})();
6912
6913}
6914
6915(function(){
6916angular.module("material.core").constant("$MD_THEME_CSS", "/* Only used with Theme processes */html.md-THEME_NAME-theme, body.md-THEME_NAME-theme { color: '{{foreground-1}}'; background-color: '{{background-color}}'; }md-autocomplete.md-THEME_NAME-theme { background: '{{background-A100}}'; } md-autocomplete.md-THEME_NAME-theme[disabled]:not([md-floating-label]) { background: '{{background-100}}'; } md-autocomplete.md-THEME_NAME-theme button md-icon path { fill: '{{background-600}}'; } md-autocomplete.md-THEME_NAME-theme button:after { background: '{{background-600-0.3}}'; }.md-autocomplete-suggestions-container.md-THEME_NAME-theme { background: '{{background-A100}}'; } .md-autocomplete-suggestions-container.md-THEME_NAME-theme li { color: '{{background-900}}'; } .md-autocomplete-suggestions-container.md-THEME_NAME-theme li .highlight { color: '{{background-600}}'; } .md-autocomplete-suggestions-container.md-THEME_NAME-theme li:hover, .md-autocomplete-suggestions-container.md-THEME_NAME-theme li.selected { background: '{{background-200}}'; }md-backdrop { background-color: '{{background-900-0.0}}'; } md-backdrop.md-opaque.md-THEME_NAME-theme { background-color: '{{background-900-1.0}}'; }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-list-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:not([disabled]):hover { background-color: '{{background-500-0.2}}'; }.md-button.md-THEME_NAME-theme:not([disabled]).md-focused { background-color: '{{background-500-0.2}}'; }.md-button.md-THEME_NAME-theme:not([disabled]).md-icon-button:hover { background-color: transparent; }.md-button.md-THEME_NAME-theme.md-fab { background-color: '{{accent-color}}'; color: '{{accent-contrast}}'; } .md-button.md-THEME_NAME-theme.md-fab md-icon { color: '{{accent-contrast}}'; } .md-button.md-THEME_NAME-theme.md-fab:not([disabled]):hover { background-color: '{{accent-A700}}'; } .md-button.md-THEME_NAME-theme.md-fab:not([disabled]).md-focused { 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]) md-icon, .md-button.md-THEME_NAME-theme.md-primary.md-fab:not([disabled]) md-icon { color: '{{primary-contrast}}'; } .md-button.md-THEME_NAME-theme.md-primary.md-raised:not([disabled]):hover, .md-button.md-THEME_NAME-theme.md-primary.md-fab:not([disabled]):hover { background-color: '{{primary-600}}'; } .md-button.md-THEME_NAME-theme.md-primary.md-raised:not([disabled]).md-focused, .md-button.md-THEME_NAME-theme.md-primary.md-fab:not([disabled]).md-focused { background-color: '{{primary-600}}'; } .md-button.md-THEME_NAME-theme.md-primary:not([disabled]) md-icon { color: '{{primary-color}}'; }.md-button.md-THEME_NAME-theme.md-fab { background-color: '{{accent-color}}'; color: '{{accent-contrast}}'; } .md-button.md-THEME_NAME-theme.md-fab:not([disabled]) .md-icon { color: '{{accent-contrast}}'; } .md-button.md-THEME_NAME-theme.md-fab:not([disabled]):hover { background-color: '{{accent-A700}}'; } .md-button.md-THEME_NAME-theme.md-fab:not([disabled]).md-focused { background-color: '{{accent-A700}}'; }.md-button.md-THEME_NAME-theme.md-raised { color: '{{background-900}}'; background-color: '{{background-50}}'; } .md-button.md-THEME_NAME-theme.md-raised:not([disabled]) md-icon { color: '{{background-900}}'; } .md-button.md-THEME_NAME-theme.md-raised:not([disabled]):hover { background-color: '{{background-50}}'; } .md-button.md-THEME_NAME-theme.md-raised:not([disabled]).md-focused { 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]) md-icon, .md-button.md-THEME_NAME-theme.md-warn.md-fab:not([disabled]) md-icon { color: '{{warn-contrast}}'; } .md-button.md-THEME_NAME-theme.md-warn.md-raised:not([disabled]):hover, .md-button.md-THEME_NAME-theme.md-warn.md-fab:not([disabled]):hover { background-color: '{{warn-600}}'; } .md-button.md-THEME_NAME-theme.md-warn.md-raised:not([disabled]).md-focused, .md-button.md-THEME_NAME-theme.md-warn.md-fab:not([disabled]).md-focused { background-color: '{{warn-600}}'; } .md-button.md-THEME_NAME-theme.md-warn:not([disabled]) md-icon { color: '{{warn-color}}'; }.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]) md-icon, .md-button.md-THEME_NAME-theme.md-accent.md-fab:not([disabled]) md-icon { color: '{{accent-contrast}}'; } .md-button.md-THEME_NAME-theme.md-accent.md-raised:not([disabled]):hover, .md-button.md-THEME_NAME-theme.md-accent.md-fab:not([disabled]):hover { background-color: '{{accent-A700}}'; } .md-button.md-THEME_NAME-theme.md-accent.md-raised:not([disabled]).md-focused, .md-button.md-THEME_NAME-theme.md-accent.md-fab:not([disabled]).md-focused { background-color: '{{accent-A700}}'; } .md-button.md-THEME_NAME-theme.md-accent:not([disabled]) md-icon { color: '{{accent-color}}'; }.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], .md-button.md-THEME_NAME-theme.md-accent[disabled], .md-button.md-THEME_NAME-theme.md-warn[disabled] { color: '{{foreground-3}}'; cursor: default; } .md-button.md-THEME_NAME-theme[disabled] md-icon, .md-button.md-THEME_NAME-theme.md-raised[disabled] md-icon, .md-button.md-THEME_NAME-theme.md-fab[disabled] md-icon, .md-button.md-THEME_NAME-theme.md-accent[disabled] md-icon, .md-button.md-THEME_NAME-theme.md-warn[disabled] md-icon { color: '{{foreground-3}}'; }.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 a.md-THEME_NAME-theme:not(.md-button).md-primary { color: '{{primary-color}}'; } ._md a.md-THEME_NAME-theme:not(.md-button).md-primary:hover { color: '{{primary-700}}'; }._md a.md-THEME_NAME-theme:not(.md-button).md-accent { color: '{{accent-color}}'; } ._md a.md-THEME_NAME-theme:not(.md-button).md-accent:hover { color: '{{accent-700}}'; }._md a.md-THEME_NAME-theme:not(.md-button).md-accent { color: '{{accent-color}}'; } ._md a.md-THEME_NAME-theme:not(.md-button).md-accent:hover { color: '{{accent-A700}}'; }._md a.md-THEME_NAME-theme:not(.md-button).md-warn { color: '{{warn-color}}'; } ._md a.md-THEME_NAME-theme:not(.md-button).md-warn:hover { color: '{{warn-700}}'; }md-card.md-THEME_NAME-theme { color: '{{foreground-1}}'; background-color: '{{background-hue-1}}'; border-radius: 2px; } md-card.md-THEME_NAME-theme .md-card-image { border-radius: 2px 2px 0 0; } md-card.md-THEME_NAME-theme md-card-header md-card-avatar md-icon { color: '{{background-color}}'; background-color: '{{foreground-3}}'; } md-card.md-THEME_NAME-theme md-card-header md-card-header-text .md-subhead { color: '{{foreground-2}}'; } md-card.md-THEME_NAME-theme md-card-title md-card-title-text:not(:only-child) .md-subhead { color: '{{foreground-2}}'; }md-checkbox.md-THEME_NAME-theme .md-ripple { color: '{{accent-A700}}'; }md-checkbox.md-THEME_NAME-theme.md-checked .md-ripple { color: '{{background-600}}'; }md-checkbox.md-THEME_NAME-theme.md-checked.md-focused .md-container:before { background-color: '{{accent-color-0.26}}'; }md-checkbox.md-THEME_NAME-theme .md-ink-ripple { color: '{{foreground-2}}'; }md-checkbox.md-THEME_NAME-theme.md-checked .md-ink-ripple { color: '{{accent-color-0.87}}'; }md-checkbox.md-THEME_NAME-theme:not(.md-checked) .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: '{{accent-contrast-0.87}}'; }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-ink-ripple { color: '{{foreground-2}}'; }md-checkbox.md-THEME_NAME-theme:not([disabled]).md-primary.md-checked .md-ink-ripple { color: '{{primary-color-0.87}}'; }md-checkbox.md-THEME_NAME-theme:not([disabled]).md-primary:not(.md-checked) .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-focused .md-container:before { background-color: '{{primary-color-0.26}}'; }md-checkbox.md-THEME_NAME-theme:not([disabled]).md-primary.md-checked .md-icon:after { border-color: '{{primary-contrast-0.87}}'; }md-checkbox.md-THEME_NAME-theme:not([disabled]).md-primary .md-indeterminate[disabled] .md-container { color: '{{foreground-3}}'; }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-ink-ripple { color: '{{foreground-2}}'; }md-checkbox.md-THEME_NAME-theme:not([disabled]).md-warn.md-checked .md-ink-ripple { color: '{{warn-color-0.87}}'; }md-checkbox.md-THEME_NAME-theme:not([disabled]).md-warn:not(.md-checked) .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-focused:not([disabled]) .md-container:before { background-color: '{{warn-color-0.26}}'; }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]:not(.md-checked) .md-icon { border-color: '{{foreground-3}}'; }md-checkbox.md-THEME_NAME-theme[disabled].md-checked .md-icon { background-color: '{{foreground-3}}'; }md-checkbox.md-THEME_NAME-theme[disabled].md-checked .md-icon:after { border-color: '{{background-200}}'; }md-checkbox.md-THEME_NAME-theme[disabled] .md-icon:after { border-color: '{{foreground-3}}'; }md-checkbox.md-THEME_NAME-theme[disabled] .md-label { color: '{{foreground-3}}'; }md-chips.md-THEME_NAME-theme .md-chips { box-shadow: 0 1px '{{foreground-4}}'; } md-chips.md-THEME_NAME-theme .md-chips.md-focused { box-shadow: 0 2px '{{primary-color}}'; } md-chips.md-THEME_NAME-theme .md-chips .md-chip-input-container input { color: '{{foreground-1}}'; } md-chips.md-THEME_NAME-theme .md-chips .md-chip-input-container input::-webkit-input-placeholder { color: '{{foreground-3}}'; } md-chips.md-THEME_NAME-theme .md-chips .md-chip-input-container input:-moz-placeholder { color: '{{foreground-3}}'; } md-chips.md-THEME_NAME-theme .md-chips .md-chip-input-container input::-moz-placeholder { color: '{{foreground-3}}'; } md-chips.md-THEME_NAME-theme .md-chips .md-chip-input-container input:-ms-input-placeholder { color: '{{foreground-3}}'; } md-chips.md-THEME_NAME-theme .md-chips .md-chip-input-container input::-webkit-input-placeholder { color: '{{foreground-3}}'; }md-chips.md-THEME_NAME-theme md-chip { background: '{{background-300}}'; color: '{{background-800}}'; } md-chips.md-THEME_NAME-theme md-chip md-icon { color: '{{background-700}}'; } md-chips.md-THEME_NAME-theme md-chip.md-focused { background: '{{primary-color}}'; color: '{{primary-contrast}}'; } md-chips.md-THEME_NAME-theme md-chip.md-focused md-icon { color: '{{primary-contrast}}'; } md-chips.md-THEME_NAME-theme md-chip._md-chip-editing { background: transparent; color: '{{background-800}}'; }md-chips.md-THEME_NAME-theme md-chip-remove .md-button md-icon path { fill: '{{background-500}}'; }.md-contact-suggestion span.md-contact-email { color: '{{background-400}}'; }md-content.md-THEME_NAME-theme { color: '{{foreground-1}}'; background-color: '{{background-default}}'; }/** Theme styles for mdCalendar. */.md-calendar.md-THEME_NAME-theme { background: '{{background-A100}}'; color: '{{background-A200-0.87}}'; } .md-calendar.md-THEME_NAME-theme tr:last-child td { border-bottom-color: '{{background-200}}'; }.md-THEME_NAME-theme .md-calendar-day-header { background: '{{background-300}}'; color: '{{background-A200-0.87}}'; }.md-THEME_NAME-theme .md-calendar-date.md-calendar-date-today .md-calendar-date-selection-indicator { border: 1px solid '{{primary-500}}'; }.md-THEME_NAME-theme .md-calendar-date.md-calendar-date-today.md-calendar-date-disabled { color: '{{primary-500-0.6}}'; }.md-calendar-date.md-focus .md-THEME_NAME-theme .md-calendar-date-selection-indicator, .md-THEME_NAME-theme .md-calendar-date-selection-indicator:hover { background: '{{background-300}}'; }.md-THEME_NAME-theme .md-calendar-date.md-calendar-selected-date .md-calendar-date-selection-indicator,.md-THEME_NAME-theme .md-calendar-date.md-focus.md-calendar-selected-date .md-calendar-date-selection-indicator { background: '{{primary-500}}'; color: '{{primary-500-contrast}}'; border-color: transparent; }.md-THEME_NAME-theme .md-calendar-date-disabled,.md-THEME_NAME-theme .md-calendar-month-label-disabled { color: '{{background-A200-0.435}}'; }/** Theme styles for mdDatepicker. */.md-THEME_NAME-theme .md-datepicker-input { color: '{{foreground-1}}'; } .md-THEME_NAME-theme .md-datepicker-input::-webkit-input-placeholder { color: '{{foreground-3}}'; } .md-THEME_NAME-theme .md-datepicker-input:-moz-placeholder { color: '{{foreground-3}}'; } .md-THEME_NAME-theme .md-datepicker-input::-moz-placeholder { color: '{{foreground-3}}'; } .md-THEME_NAME-theme .md-datepicker-input:-ms-input-placeholder { color: '{{foreground-3}}'; } .md-THEME_NAME-theme .md-datepicker-input::-webkit-input-placeholder { color: '{{foreground-3}}'; }.md-THEME_NAME-theme .md-datepicker-input-container { border-bottom-color: '{{foreground-4}}'; } .md-THEME_NAME-theme .md-datepicker-input-container.md-datepicker-focused { border-bottom-color: '{{primary-color}}'; } .md-accent .md-THEME_NAME-theme .md-datepicker-input-container.md-datepicker-focused { border-bottom-color: '{{accent-color}}'; } .md-warn .md-THEME_NAME-theme .md-datepicker-input-container.md-datepicker-focused { border-bottom-color: '{{warn-A700}}'; } .md-THEME_NAME-theme .md-datepicker-input-container.md-datepicker-invalid { border-bottom-color: '{{warn-A700}}'; }.md-THEME_NAME-theme .md-datepicker-calendar-pane { border-color: '{{background-hue-1}}'; }.md-THEME_NAME-theme .md-datepicker-triangle-button .md-datepicker-expand-triangle { border-top-color: '{{foreground-3}}'; }.md-THEME_NAME-theme .md-datepicker-triangle-button:hover .md-datepicker-expand-triangle { border-top-color: '{{foreground-2}}'; }.md-THEME_NAME-theme .md-datepicker-open .md-datepicker-calendar-icon { color: '{{primary-color}}'; }.md-THEME_NAME-theme .md-datepicker-open.md-accent .md-datepicker-calendar-icon, .md-accent .md-THEME_NAME-theme .md-datepicker-open .md-datepicker-calendar-icon { color: '{{accent-color}}'; }.md-THEME_NAME-theme .md-datepicker-open.md-warn .md-datepicker-calendar-icon, .md-warn .md-THEME_NAME-theme .md-datepicker-open .md-datepicker-calendar-icon { color: '{{warn-A700}}'; }.md-THEME_NAME-theme .md-datepicker-calendar { background: '{{background-A100}}'; }.md-THEME_NAME-theme .md-datepicker-input-mask-opaque { box-shadow: 0 0 0 9999px \"{{background-hue-1}}\"; }.md-THEME_NAME-theme .md-datepicker-open .md-datepicker-input-container { background: \"{{background-hue-1}}\"; }md-dialog.md-THEME_NAME-theme { border-radius: 4px; background-color: '{{background-hue-1}}'; color: '{{foreground-1}}'; } md-dialog.md-THEME_NAME-theme.md-content-overflow .md-actions, md-dialog.md-THEME_NAME-theme.md-content-overflow md-dialog-actions { border-top-color: '{{foreground-4}}'; }md-divider.md-THEME_NAME-theme { border-top-color: '{{foreground-4}}'; }.layout-row > md-divider.md-THEME_NAME-theme,.layout-xs-row > md-divider.md-THEME_NAME-theme, .layout-gt-xs-row > md-divider.md-THEME_NAME-theme,.layout-sm-row > md-divider.md-THEME_NAME-theme, .layout-gt-sm-row > md-divider.md-THEME_NAME-theme,.layout-md-row > md-divider.md-THEME_NAME-theme, .layout-gt-md-row > md-divider.md-THEME_NAME-theme,.layout-lg-row > md-divider.md-THEME_NAME-theme, .layout-gt-lg-row > md-divider.md-THEME_NAME-theme,.layout-xl-row > md-divider.md-THEME_NAME-theme { border-right-color: '{{foreground-4}}'; }md-icon.md-THEME_NAME-theme { color: '{{foreground-2}}'; } 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-input-container.md-THEME_NAME-theme .md-input { color: '{{foreground-1}}'; border-color: '{{foreground-4}}'; } md-input-container.md-THEME_NAME-theme .md-input::-webkit-input-placeholder { color: '{{foreground-3}}'; } md-input-container.md-THEME_NAME-theme .md-input:-moz-placeholder { color: '{{foreground-3}}'; } md-input-container.md-THEME_NAME-theme .md-input::-moz-placeholder { color: '{{foreground-3}}'; } md-input-container.md-THEME_NAME-theme .md-input:-ms-input-placeholder { color: '{{foreground-3}}'; } md-input-container.md-THEME_NAME-theme .md-input::-webkit-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 { color: '{{foreground-3}}'; }md-input-container.md-THEME_NAME-theme label.md-required:after { color: '{{warn-A700}}'; }md-input-container.md-THEME_NAME-theme:not(.md-input-focused):not(.md-input-invalid) label.md-required:after { color: '{{foreground-2}}'; }md-input-container.md-THEME_NAME-theme .md-input-messages-animation, md-input-container.md-THEME_NAME-theme .md-input-message-animation { color: '{{warn-A700}}'; } md-input-container.md-THEME_NAME-theme .md-input-messages-animation .md-char-counter, md-input-container.md-THEME_NAME-theme .md-input-message-animation .md-char-counter { color: '{{foreground-1}}'; }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, md-input-container.md-THEME_NAME-theme:not(.md-input-invalid).md-input-resized .md-input { border-color: '{{primary-color}}'; }md-input-container.md-THEME_NAME-theme:not(.md-input-invalid).md-input-focused label,md-input-container.md-THEME_NAME-theme:not(.md-input-invalid).md-input-focused md-icon { color: '{{primary-color}}'; }md-input-container.md-THEME_NAME-theme:not(.md-input-invalid).md-input-focused.md-accent .md-input { border-color: '{{accent-color}}'; }md-input-container.md-THEME_NAME-theme:not(.md-input-invalid).md-input-focused.md-accent label,md-input-container.md-THEME_NAME-theme:not(.md-input-invalid).md-input-focused.md-accent md-icon { color: '{{accent-color}}'; }md-input-container.md-THEME_NAME-theme:not(.md-input-invalid).md-input-focused.md-warn .md-input { border-color: '{{warn-A700}}'; }md-input-container.md-THEME_NAME-theme:not(.md-input-invalid).md-input-focused.md-warn label,md-input-container.md-THEME_NAME-theme:not(.md-input-invalid).md-input-focused.md-warn md-icon { color: '{{warn-A700}}'; }md-input-container.md-THEME_NAME-theme.md-input-invalid .md-input { border-color: '{{warn-A700}}'; }md-input-container.md-THEME_NAME-theme.md-input-invalid label,md-input-container.md-THEME_NAME-theme.md-input-invalid .md-input-message-animation,md-input-container.md-THEME_NAME-theme.md-input-invalid .md-char-counter { color: '{{warn-A700}}'; }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-3}}\" 0%, \"{{foreground-3}}\" 33%, transparent 0%); background-image: -ms-linear-gradient(left, transparent 0%, \"{{foreground-3}}\" 100%); }md-list.md-THEME_NAME-theme md-list-item.md-2-line .md-list-item-text h3, md-list.md-THEME_NAME-theme md-list-item.md-2-line .md-list-item-text h4,md-list.md-THEME_NAME-theme md-list-item.md-3-line .md-list-item-text h3,md-list.md-THEME_NAME-theme md-list-item.md-3-line .md-list-item-text h4 { color: '{{foreground-1}}'; }md-list.md-THEME_NAME-theme md-list-item.md-2-line .md-list-item-text p,md-list.md-THEME_NAME-theme md-list-item.md-3-line .md-list-item-text p { color: '{{foreground-2}}'; }md-list.md-THEME_NAME-theme .md-proxy-focus.md-focused div.md-no-style { background-color: '{{background-100}}'; }md-list.md-THEME_NAME-theme md-list-item .md-avatar-icon { background-color: '{{foreground-3}}'; color: '{{background-color}}'; }md-list.md-THEME_NAME-theme md-list-item > md-icon { color: '{{foreground-2}}'; } md-list.md-THEME_NAME-theme md-list-item > md-icon.md-highlight { color: '{{primary-color}}'; } md-list.md-THEME_NAME-theme md-list-item > md-icon.md-highlight.md-accent { color: '{{accent-color}}'; }md-menu-content.md-THEME_NAME-theme { background-color: '{{background-A100}}'; } md-menu-content.md-THEME_NAME-theme md-menu-item { color: '{{background-A200-0.87}}'; } md-menu-content.md-THEME_NAME-theme md-menu-item md-icon { color: '{{background-A200-0.54}}'; } md-menu-content.md-THEME_NAME-theme md-menu-item .md-button[disabled] { color: '{{background-A200-0.25}}'; } md-menu-content.md-THEME_NAME-theme md-menu-item .md-button[disabled] md-icon { color: '{{background-A200-0.25}}'; } md-menu-content.md-THEME_NAME-theme md-menu-divider { background-color: '{{background-A200-0.11}}'; }md-menu-bar.md-THEME_NAME-theme > button.md-button { color: '{{foreground-2}}'; border-radius: 2px; }md-menu-bar.md-THEME_NAME-theme md-menu.md-open > button, md-menu-bar.md-THEME_NAME-theme md-menu > button:focus { outline: none; background: '{{background-200}}'; }md-menu-bar.md-THEME_NAME-theme.md-open:not(.md-keyboard-mode) md-menu:hover > button { background-color: '{{ background-500-0.2}}'; }md-menu-bar.md-THEME_NAME-theme:not(.md-keyboard-mode):not(.md-open) md-menu button:hover,md-menu-bar.md-THEME_NAME-theme:not(.md-keyboard-mode):not(.md-open) md-menu button:focus { background: transparent; }md-menu-content.md-THEME_NAME-theme .md-menu > .md-button:after { color: '{{background-A200-0.54}}'; }md-menu-content.md-THEME_NAME-theme .md-menu.md-open > .md-button { background-color: '{{ background-500-0.2}}'; }md-toolbar.md-THEME_NAME-theme.md-menu-toolbar { background-color: '{{background-A100}}'; color: '{{background-A200}}'; } md-toolbar.md-THEME_NAME-theme.md-menu-toolbar md-toolbar-filler { background-color: '{{primary-color}}'; color: '{{background-A100-0.87}}'; } md-toolbar.md-THEME_NAME-theme.md-menu-toolbar md-toolbar-filler md-icon { color: '{{background-A100-0.87}}'; }md-nav-bar.md-THEME_NAME-theme .md-nav-bar { background-color: transparent; border-color: '{{foreground-4}}'; }md-nav-bar.md-THEME_NAME-theme .md-button._md-nav-button.md-unselected { color: '{{foreground-2}}'; }md-nav-bar.md-THEME_NAME-theme md-nav-ink-bar { color: '{{accent-color}}'; background: '{{accent-color}}'; }.md-panel { background-color: '{{background-900-0.0}}'; } .md-panel._md-panel-backdrop.md-THEME_NAME-theme { background-color: '{{background-900-1.0}}'; }md-progress-circular.md-THEME_NAME-theme path { stroke: '{{primary-color}}'; }md-progress-circular.md-THEME_NAME-theme.md-warn path { stroke: '{{warn-color}}'; }md-progress-circular.md-THEME_NAME-theme.md-accent path { stroke: '{{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-A700}}'; }md-radio-group.md-THEME_NAME-theme:not([disabled]) .md-primary .md-on, md-radio-group.md-THEME_NAME-theme:not([disabled]).md-primary .md-on,md-radio-button.md-THEME_NAME-theme:not([disabled]) .md-primary .md-on,md-radio-button.md-THEME_NAME-theme:not([disabled]).md-primary .md-on { background-color: '{{primary-color-0.87}}'; }md-radio-group.md-THEME_NAME-theme:not([disabled]) .md-primary .md-checked .md-off, md-radio-group.md-THEME_NAME-theme:not([disabled]) .md-primary.md-checked .md-off, md-radio-group.md-THEME_NAME-theme:not([disabled]).md-primary .md-checked .md-off, md-radio-group.md-THEME_NAME-theme:not([disabled]).md-primary.md-checked .md-off,md-radio-button.md-THEME_NAME-theme:not([disabled]) .md-primary .md-checked .md-off,md-radio-button.md-THEME_NAME-theme:not([disabled]) .md-primary.md-checked .md-off,md-radio-button.md-THEME_NAME-theme:not([disabled]).md-primary .md-checked .md-off,md-radio-button.md-THEME_NAME-theme:not([disabled]).md-primary.md-checked .md-off { border-color: '{{primary-color-0.87}}'; }md-radio-group.md-THEME_NAME-theme:not([disabled]) .md-primary .md-checked .md-ink-ripple, md-radio-group.md-THEME_NAME-theme:not([disabled]) .md-primary.md-checked .md-ink-ripple, md-radio-group.md-THEME_NAME-theme:not([disabled]).md-primary .md-checked .md-ink-ripple, md-radio-group.md-THEME_NAME-theme:not([disabled]).md-primary.md-checked .md-ink-ripple,md-radio-button.md-THEME_NAME-theme:not([disabled]) .md-primary .md-checked .md-ink-ripple,md-radio-button.md-THEME_NAME-theme:not([disabled]) .md-primary.md-checked .md-ink-ripple,md-radio-button.md-THEME_NAME-theme:not([disabled]).md-primary .md-checked .md-ink-ripple,md-radio-button.md-THEME_NAME-theme:not([disabled]).md-primary.md-checked .md-ink-ripple { color: '{{primary-color-0.87}}'; }md-radio-group.md-THEME_NAME-theme:not([disabled]) .md-primary .md-container .md-ripple, md-radio-group.md-THEME_NAME-theme:not([disabled]).md-primary .md-container .md-ripple,md-radio-button.md-THEME_NAME-theme:not([disabled]) .md-primary .md-container .md-ripple,md-radio-button.md-THEME_NAME-theme:not([disabled]).md-primary .md-container .md-ripple { color: '{{primary-600}}'; }md-radio-group.md-THEME_NAME-theme:not([disabled]) .md-warn .md-on, md-radio-group.md-THEME_NAME-theme:not([disabled]).md-warn .md-on,md-radio-button.md-THEME_NAME-theme:not([disabled]) .md-warn .md-on,md-radio-button.md-THEME_NAME-theme:not([disabled]).md-warn .md-on { background-color: '{{warn-color-0.87}}'; }md-radio-group.md-THEME_NAME-theme:not([disabled]) .md-warn .md-checked .md-off, md-radio-group.md-THEME_NAME-theme:not([disabled]) .md-warn.md-checked .md-off, md-radio-group.md-THEME_NAME-theme:not([disabled]).md-warn .md-checked .md-off, md-radio-group.md-THEME_NAME-theme:not([disabled]).md-warn.md-checked .md-off,md-radio-button.md-THEME_NAME-theme:not([disabled]) .md-warn .md-checked .md-off,md-radio-button.md-THEME_NAME-theme:not([disabled]) .md-warn.md-checked .md-off,md-radio-button.md-THEME_NAME-theme:not([disabled]).md-warn .md-checked .md-off,md-radio-button.md-THEME_NAME-theme:not([disabled]).md-warn.md-checked .md-off { border-color: '{{warn-color-0.87}}'; }md-radio-group.md-THEME_NAME-theme:not([disabled]) .md-warn .md-checked .md-ink-ripple, md-radio-group.md-THEME_NAME-theme:not([disabled]) .md-warn.md-checked .md-ink-ripple, md-radio-group.md-THEME_NAME-theme:not([disabled]).md-warn .md-checked .md-ink-ripple, md-radio-group.md-THEME_NAME-theme:not([disabled]).md-warn.md-checked .md-ink-ripple,md-radio-button.md-THEME_NAME-theme:not([disabled]) .md-warn .md-checked .md-ink-ripple,md-radio-button.md-THEME_NAME-theme:not([disabled]) .md-warn.md-checked .md-ink-ripple,md-radio-button.md-THEME_NAME-theme:not([disabled]).md-warn .md-checked .md-ink-ripple,md-radio-button.md-THEME_NAME-theme:not([disabled]).md-warn.md-checked .md-ink-ripple { color: '{{warn-color-0.87}}'; }md-radio-group.md-THEME_NAME-theme:not([disabled]) .md-warn .md-container .md-ripple, md-radio-group.md-THEME_NAME-theme:not([disabled]).md-warn .md-container .md-ripple,md-radio-button.md-THEME_NAME-theme:not([disabled]) .md-warn .md-container .md-ripple,md-radio-button.md-THEME_NAME-theme:not([disabled]).md-warn .md-container .md-ripple { color: '{{warn-600}}'; }md-radio-group.md-THEME_NAME-theme[disabled],md-radio-button.md-THEME_NAME-theme[disabled] { color: '{{foreground-3}}'; } md-radio-group.md-THEME_NAME-theme[disabled] .md-container .md-off, md-radio-button.md-THEME_NAME-theme[disabled] .md-container .md-off { border-color: '{{foreground-3}}'; } md-radio-group.md-THEME_NAME-theme[disabled] .md-container .md-on, md-radio-button.md-THEME_NAME-theme[disabled] .md-container .md-on { border-color: '{{foreground-3}}'; }md-radio-group.md-THEME_NAME-theme .md-checked .md-ink-ripple { color: '{{accent-color-0.26}}'; }md-radio-group.md-THEME_NAME-theme.md-primary .md-checked:not([disabled]) .md-ink-ripple, md-radio-group.md-THEME_NAME-theme .md-checked:not([disabled]).md-primary .md-ink-ripple { color: '{{primary-color-0.26}}'; }md-radio-group.md-THEME_NAME-theme .md-checked.md-primary .md-ink-ripple { color: '{{warn-color-0.26}}'; }md-radio-group.md-THEME_NAME-theme.md-focused:not(:empty) .md-checked .md-container:before { background-color: '{{accent-color-0.26}}'; }md-radio-group.md-THEME_NAME-theme.md-focused:not(:empty).md-primary .md-checked .md-container:before,md-radio-group.md-THEME_NAME-theme.md-focused:not(:empty) .md-checked.md-primary .md-container:before { background-color: '{{primary-color-0.26}}'; }md-radio-group.md-THEME_NAME-theme.md-focused:not(:empty).md-warn .md-checked .md-container:before,md-radio-group.md-THEME_NAME-theme.md-focused:not(:empty) .md-checked.md-warn .md-container:before { background-color: '{{warn-color-0.26}}'; }md-input-container md-select.md-THEME_NAME-theme .md-select-value span:first-child:after { color: '{{warn-A700}}'; }md-input-container:not(.md-input-focused):not(.md-input-invalid) md-select.md-THEME_NAME-theme .md-select-value span:first-child:after { color: '{{foreground-3}}'; }md-input-container.md-input-focused:not(.md-input-has-value) md-select.md-THEME_NAME-theme .md-select-value { color: '{{primary-color}}'; } md-input-container.md-input-focused:not(.md-input-has-value) md-select.md-THEME_NAME-theme .md-select-value.md-select-placeholder { color: '{{primary-color}}'; }md-input-container.md-input-invalid md-select.md-THEME_NAME-theme .md-select-value { color: '{{warn-A700}}' !important; border-bottom-color: '{{warn-A700}}' !important; }md-input-container.md-input-invalid md-select.md-THEME_NAME-theme.md-no-underline .md-select-value { border-bottom-color: transparent !important; }md-select.md-THEME_NAME-theme[disabled] .md-select-value { border-bottom-color: transparent; background-image: linear-gradient(to right, \"{{foreground-3}}\" 0%, \"{{foreground-3}}\" 33%, transparent 0%); background-image: -ms-linear-gradient(left, transparent 0%, \"{{foreground-3}}\" 100%); }md-select.md-THEME_NAME-theme .md-select-value { border-bottom-color: '{{foreground-4}}'; } md-select.md-THEME_NAME-theme .md-select-value.md-select-placeholder { color: '{{foreground-3}}'; } md-select.md-THEME_NAME-theme .md-select-value span:first-child:after { color: '{{warn-A700}}'; }md-select.md-THEME_NAME-theme.md-no-underline .md-select-value { border-bottom-color: transparent !important; }md-select.md-THEME_NAME-theme.ng-invalid.ng-touched .md-select-value { color: '{{warn-A700}}' !important; border-bottom-color: '{{warn-A700}}' !important; }md-select.md-THEME_NAME-theme.ng-invalid.ng-touched.md-no-underline .md-select-value { border-bottom-color: transparent !important; }md-select.md-THEME_NAME-theme:not([disabled]):focus .md-select-value { border-bottom-color: '{{primary-color}}'; color: '{{ foreground-1 }}'; } md-select.md-THEME_NAME-theme:not([disabled]):focus .md-select-value.md-select-placeholder { color: '{{ foreground-1 }}'; }md-select.md-THEME_NAME-theme:not([disabled]):focus.md-no-underline .md-select-value { border-bottom-color: transparent !important; }md-select.md-THEME_NAME-theme:not([disabled]):focus.md-accent .md-select-value { border-bottom-color: '{{accent-color}}'; }md-select.md-THEME_NAME-theme:not([disabled]):focus.md-warn .md-select-value { border-bottom-color: '{{warn-color}}'; }md-select.md-THEME_NAME-theme[disabled] .md-select-value { color: '{{foreground-3}}'; } md-select.md-THEME_NAME-theme[disabled] .md-select-value.md-select-placeholder { color: '{{foreground-3}}'; }md-select-menu.md-THEME_NAME-theme md-content { background: '{{background-A100}}'; } md-select-menu.md-THEME_NAME-theme md-content md-optgroup { color: '{{background-600-0.87}}'; } md-select-menu.md-THEME_NAME-theme md-content md-option { color: '{{background-900-0.87}}'; } md-select-menu.md-THEME_NAME-theme md-content md-option[disabled] .md-text { color: '{{background-400-0.87}}'; } md-select-menu.md-THEME_NAME-theme md-content md-option:not([disabled]):focus, md-select-menu.md-THEME_NAME-theme md-content md-option:not([disabled]):hover { background: '{{background-200}}'; } md-select-menu.md-THEME_NAME-theme md-content md-option[selected] { color: '{{primary-500}}'; } md-select-menu.md-THEME_NAME-theme md-content md-option[selected]:focus { color: '{{primary-600}}'; } md-select-menu.md-THEME_NAME-theme md-content md-option[selected].md-accent { color: '{{accent-color}}'; } md-select-menu.md-THEME_NAME-theme md-content md-option[selected].md-accent:focus { color: '{{accent-A700}}'; }.md-checkbox-enabled.md-THEME_NAME-theme .md-ripple { color: '{{primary-600}}'; }.md-checkbox-enabled.md-THEME_NAME-theme[selected] .md-ripple { color: '{{background-600}}'; }.md-checkbox-enabled.md-THEME_NAME-theme .md-ink-ripple { color: '{{foreground-2}}'; }.md-checkbox-enabled.md-THEME_NAME-theme[selected] .md-ink-ripple { color: '{{primary-color-0.87}}'; }.md-checkbox-enabled.md-THEME_NAME-theme:not(.md-checked) .md-icon { border-color: '{{foreground-2}}'; }.md-checkbox-enabled.md-THEME_NAME-theme[selected] .md-icon { background-color: '{{primary-color-0.87}}'; }.md-checkbox-enabled.md-THEME_NAME-theme[selected].md-focused .md-container:before { background-color: '{{primary-color-0.26}}'; }.md-checkbox-enabled.md-THEME_NAME-theme[selected] .md-icon:after { border-color: '{{primary-contrast-0.87}}'; }.md-checkbox-enabled.md-THEME_NAME-theme .md-indeterminate[disabled] .md-container { color: '{{foreground-3}}'; }.md-checkbox-enabled.md-THEME_NAME-theme md-option .md-text { color: '{{background-900-0.87}}'; }md-sidenav.md-THEME_NAME-theme, md-sidenav.md-THEME_NAME-theme md-content { background-color: '{{background-hue-1}}'; }md-slider.md-THEME_NAME-theme .md-track { background-color: '{{foreground-3}}'; }md-slider.md-THEME_NAME-theme .md-track-ticks { color: '{{background-contrast}}'; }md-slider.md-THEME_NAME-theme .md-focus-ring { background-color: '{{accent-A200-0.2}}'; }md-slider.md-THEME_NAME-theme .md-disabled-thumb { border-color: '{{background-color}}'; background-color: '{{background-color}}'; }md-slider.md-THEME_NAME-theme.md-min .md-thumb:after { background-color: '{{background-color}}'; border-color: '{{foreground-3}}'; }md-slider.md-THEME_NAME-theme.md-min .md-focus-ring { background-color: '{{foreground-3-0.38}}'; }md-slider.md-THEME_NAME-theme.md-min[md-discrete] .md-thumb:after { background-color: '{{background-contrast}}'; border-color: transparent; }md-slider.md-THEME_NAME-theme.md-min[md-discrete] .md-sign { background-color: '{{background-400}}'; } md-slider.md-THEME_NAME-theme.md-min[md-discrete] .md-sign:after { border-top-color: '{{background-400}}'; }md-slider.md-THEME_NAME-theme.md-min[md-discrete][md-vertical] .md-sign:after { border-top-color: transparent; border-left-color: '{{background-400}}'; }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-vertical] .md-sign:after { border-top-color: transparent; border-left-color: '{{accent-color}}'; }md-slider.md-THEME_NAME-theme .md-thumb-text { color: '{{accent-contrast}}'; }md-slider.md-THEME_NAME-theme.md-warn .md-focus-ring { background-color: '{{warn-200-0.38}}'; }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-vertical] .md-sign:after { border-top-color: transparent; border-left-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-focus-ring { background-color: '{{primary-200-0.38}}'; }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-vertical] .md-sign:after { border-top-color: transparent; border-left-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: transparent; }md-slider.md-THEME_NAME-theme[disabled]:not(.md-min) .md-thumb:after, md-slider.md-THEME_NAME-theme[disabled][md-discrete] .md-thumb:after { background-color: '{{foreground-3}}'; border-color: transparent; }md-slider.md-THEME_NAME-theme[disabled][readonly] .md-sign { background-color: '{{background-400}}'; } md-slider.md-THEME_NAME-theme[disabled][readonly] .md-sign:after { border-top-color: '{{background-400}}'; }md-slider.md-THEME_NAME-theme[disabled][readonly][md-vertical] .md-sign:after { border-top-color: transparent; border-left-color: '{{background-400}}'; }md-slider.md-THEME_NAME-theme[disabled][readonly] .md-disabled-thumb { border-color: transparent; background-color: transparent; }md-slider-container[disabled] > *:first-child:not(md-slider),md-slider-container[disabled] > *:last-child:not(md-slider) { color: '{{foreground-3}}'; }.md-subheader.md-THEME_NAME-theme { color: '{{ foreground-2-0.23 }}'; background-color: '{{background-default}}'; } .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-ink-ripple { color: '{{background-500}}'; }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-ink-ripple { color: '{{accent-color}}'; }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-focused .md-thumb:before { background-color: '{{accent-color-0.26}}'; }md-switch.md-THEME_NAME-theme.md-checked.md-primary .md-ink-ripple { color: '{{primary-color}}'; }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-primary.md-focused .md-thumb:before { background-color: '{{primary-color-0.26}}'; }md-switch.md-THEME_NAME-theme.md-checked.md-warn .md-ink-ripple { color: '{{warn-color}}'; }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.md-checked.md-warn.md-focused .md-thumb:before { background-color: '{{warn-color-0.26}}'; }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-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], md-tabs.md-THEME_NAME-theme .md-tab[disabled] md-icon { color: '{{foreground-3}}'; } md-tabs.md-THEME_NAME-theme .md-tab.md-active, md-tabs.md-THEME_NAME-theme .md-tab.md-active md-icon, md-tabs.md-THEME_NAME-theme .md-tab.md-focused, md-tabs.md-THEME_NAME-theme .md-tab.md-focused md-icon { color: '{{primary-color}}'; } md-tabs.md-THEME_NAME-theme .md-tab.md-focused { background: '{{primary-color-0.1}}'; } md-tabs.md-THEME_NAME-theme .md-tab .md-ripple-container { color: '{{accent-A100}}'; }md-tabs.md-THEME_NAME-theme.md-accent > md-tabs-wrapper { background-color: '{{accent-color}}'; } md-tabs.md-THEME_NAME-theme.md-accent > md-tabs-wrapper > md-tabs-canvas > md-pagination-wrapper > md-tab-item:not([disabled]) { color: '{{accent-A100}}'; } md-tabs.md-THEME_NAME-theme.md-accent > md-tabs-wrapper > md-tabs-canvas > md-pagination-wrapper > md-tab-item:not([disabled]).md-active, md-tabs.md-THEME_NAME-theme.md-accent > md-tabs-wrapper > md-tabs-canvas > md-pagination-wrapper > md-tab-item:not([disabled]).md-active md-icon, md-tabs.md-THEME_NAME-theme.md-accent > md-tabs-wrapper > md-tabs-canvas > md-pagination-wrapper > md-tab-item:not([disabled]).md-focused, md-tabs.md-THEME_NAME-theme.md-accent > md-tabs-wrapper > md-tabs-canvas > md-pagination-wrapper > md-tab-item:not([disabled]).md-focused md-icon { color: '{{accent-contrast}}'; } md-tabs.md-THEME_NAME-theme.md-accent > md-tabs-wrapper > md-tabs-canvas > md-pagination-wrapper > md-tab-item:not([disabled]).md-focused { background: '{{accent-contrast-0.1}}'; } md-tabs.md-THEME_NAME-theme.md-accent > md-tabs-wrapper > md-tabs-canvas > md-pagination-wrapper > 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-tabs-wrapper > md-tabs-canvas > md-pagination-wrapper > md-tab-item:not([disabled]) { color: '{{primary-100}}'; } md-tabs.md-THEME_NAME-theme.md-primary > md-tabs-wrapper > md-tabs-canvas > md-pagination-wrapper > md-tab-item:not([disabled]).md-active, md-tabs.md-THEME_NAME-theme.md-primary > md-tabs-wrapper > md-tabs-canvas > md-pagination-wrapper > md-tab-item:not([disabled]).md-active md-icon, md-tabs.md-THEME_NAME-theme.md-primary > md-tabs-wrapper > md-tabs-canvas > md-pagination-wrapper > md-tab-item:not([disabled]).md-focused, md-tabs.md-THEME_NAME-theme.md-primary > md-tabs-wrapper > md-tabs-canvas > md-pagination-wrapper > md-tab-item:not([disabled]).md-focused md-icon { color: '{{primary-contrast}}'; } md-tabs.md-THEME_NAME-theme.md-primary > md-tabs-wrapper > md-tabs-canvas > md-pagination-wrapper > md-tab-item:not([disabled]).md-focused { 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-tabs-wrapper > md-tabs-canvas > md-pagination-wrapper > md-tab-item:not([disabled]) { color: '{{warn-100}}'; } md-tabs.md-THEME_NAME-theme.md-warn > md-tabs-wrapper > md-tabs-canvas > md-pagination-wrapper > md-tab-item:not([disabled]).md-active, md-tabs.md-THEME_NAME-theme.md-warn > md-tabs-wrapper > md-tabs-canvas > md-pagination-wrapper > md-tab-item:not([disabled]).md-active md-icon, md-tabs.md-THEME_NAME-theme.md-warn > md-tabs-wrapper > md-tabs-canvas > md-pagination-wrapper > md-tab-item:not([disabled]).md-focused, md-tabs.md-THEME_NAME-theme.md-warn > md-tabs-wrapper > md-tabs-canvas > md-pagination-wrapper > md-tab-item:not([disabled]).md-focused md-icon { color: '{{warn-contrast}}'; } md-tabs.md-THEME_NAME-theme.md-warn > md-tabs-wrapper > md-tabs-canvas > md-pagination-wrapper > md-tab-item:not([disabled]).md-focused { background: '{{warn-contrast-0.1}}'; }md-toolbar > md-tabs.md-THEME_NAME-theme > md-tabs-wrapper { background-color: '{{primary-color}}'; } md-toolbar > md-tabs.md-THEME_NAME-theme > md-tabs-wrapper > md-tabs-canvas > md-pagination-wrapper > md-tab-item:not([disabled]) { color: '{{primary-100}}'; } md-toolbar > md-tabs.md-THEME_NAME-theme > md-tabs-wrapper > md-tabs-canvas > md-pagination-wrapper > md-tab-item:not([disabled]).md-active, md-toolbar > md-tabs.md-THEME_NAME-theme > md-tabs-wrapper > md-tabs-canvas > md-pagination-wrapper > md-tab-item:not([disabled]).md-active md-icon, md-toolbar > md-tabs.md-THEME_NAME-theme > md-tabs-wrapper > md-tabs-canvas > md-pagination-wrapper > md-tab-item:not([disabled]).md-focused, md-toolbar > md-tabs.md-THEME_NAME-theme > md-tabs-wrapper > md-tabs-canvas > md-pagination-wrapper > md-tab-item:not([disabled]).md-focused md-icon { color: '{{primary-contrast}}'; } md-toolbar > md-tabs.md-THEME_NAME-theme > md-tabs-wrapper > md-tabs-canvas > md-pagination-wrapper > md-tab-item:not([disabled]).md-focused { background: '{{primary-contrast-0.1}}'; }md-toolbar.md-accent > md-tabs.md-THEME_NAME-theme > md-tabs-wrapper { background-color: '{{accent-color}}'; } md-toolbar.md-accent > md-tabs.md-THEME_NAME-theme > md-tabs-wrapper > md-tabs-canvas > md-pagination-wrapper > md-tab-item:not([disabled]) { color: '{{accent-A100}}'; } md-toolbar.md-accent > md-tabs.md-THEME_NAME-theme > md-tabs-wrapper > md-tabs-canvas > md-pagination-wrapper > md-tab-item:not([disabled]).md-active, md-toolbar.md-accent > md-tabs.md-THEME_NAME-theme > md-tabs-wrapper > md-tabs-canvas > md-pagination-wrapper > md-tab-item:not([disabled]).md-active md-icon, md-toolbar.md-accent > md-tabs.md-THEME_NAME-theme > md-tabs-wrapper > md-tabs-canvas > md-pagination-wrapper > md-tab-item:not([disabled]).md-focused, md-toolbar.md-accent > md-tabs.md-THEME_NAME-theme > md-tabs-wrapper > md-tabs-canvas > md-pagination-wrapper > md-tab-item:not([disabled]).md-focused md-icon { color: '{{accent-contrast}}'; } md-toolbar.md-accent > md-tabs.md-THEME_NAME-theme > md-tabs-wrapper > md-tabs-canvas > md-pagination-wrapper > md-tab-item:not([disabled]).md-focused { background: '{{accent-contrast-0.1}}'; } md-toolbar.md-accent > md-tabs.md-THEME_NAME-theme > md-tabs-wrapper > md-tabs-canvas > md-pagination-wrapper > md-ink-bar { color: '{{primary-600-1}}'; background: '{{primary-600-1}}'; }md-toolbar.md-warn > md-tabs.md-THEME_NAME-theme > md-tabs-wrapper { background-color: '{{warn-color}}'; } md-toolbar.md-warn > md-tabs.md-THEME_NAME-theme > md-tabs-wrapper > md-tabs-canvas > md-pagination-wrapper > md-tab-item:not([disabled]) { color: '{{warn-100}}'; } md-toolbar.md-warn > md-tabs.md-THEME_NAME-theme > md-tabs-wrapper > md-tabs-canvas > md-pagination-wrapper > md-tab-item:not([disabled]).md-active, md-toolbar.md-warn > md-tabs.md-THEME_NAME-theme > md-tabs-wrapper > md-tabs-canvas > md-pagination-wrapper > md-tab-item:not([disabled]).md-active md-icon, md-toolbar.md-warn > md-tabs.md-THEME_NAME-theme > md-tabs-wrapper > md-tabs-canvas > md-pagination-wrapper > md-tab-item:not([disabled]).md-focused, md-toolbar.md-warn > md-tabs.md-THEME_NAME-theme > md-tabs-wrapper > md-tabs-canvas > md-pagination-wrapper > md-tab-item:not([disabled]).md-focused md-icon { color: '{{warn-contrast}}'; } md-toolbar.md-warn > md-tabs.md-THEME_NAME-theme > md-tabs-wrapper > md-tabs-canvas > md-pagination-wrapper > md-tab-item:not([disabled]).md-focused { background: '{{warn-contrast-0.1}}'; }md-toast.md-THEME_NAME-theme .md-toast-content { background-color: #323232; color: '{{background-50}}'; } md-toast.md-THEME_NAME-theme .md-toast-content .md-button { color: '{{background-50}}'; } md-toast.md-THEME_NAME-theme .md-toast-content .md-button.md-highlight { color: '{{accent-color}}'; } md-toast.md-THEME_NAME-theme .md-toast-content .md-button.md-highlight.md-primary { color: '{{primary-color}}'; } md-toast.md-THEME_NAME-theme .md-toast-content .md-button.md-highlight.md-warn { color: '{{warn-color}}'; }md-toolbar.md-THEME_NAME-theme:not(.md-menu-toolbar) { background-color: '{{primary-color}}'; color: '{{primary-contrast}}'; } md-toolbar.md-THEME_NAME-theme:not(.md-menu-toolbar) md-icon { color: '{{primary-contrast}}'; fill: '{{primary-contrast}}'; } md-toolbar.md-THEME_NAME-theme:not(.md-menu-toolbar) .md-button[disabled] md-icon { color: '{{primary-contrast-0.26}}'; fill: '{{primary-contrast-0.26}}'; } md-toolbar.md-THEME_NAME-theme:not(.md-menu-toolbar).md-accent { background-color: '{{accent-color}}'; color: '{{accent-contrast}}'; } md-toolbar.md-THEME_NAME-theme:not(.md-menu-toolbar).md-accent .md-ink-ripple { color: '{{accent-contrast}}'; } md-toolbar.md-THEME_NAME-theme:not(.md-menu-toolbar).md-accent md-icon { color: '{{accent-contrast}}'; fill: '{{accent-contrast}}'; } md-toolbar.md-THEME_NAME-theme:not(.md-menu-toolbar).md-accent .md-button[disabled] md-icon { color: '{{accent-contrast-0.26}}'; fill: '{{accent-contrast-0.26}}'; } md-toolbar.md-THEME_NAME-theme:not(.md-menu-toolbar).md-warn { background-color: '{{warn-color}}'; color: '{{warn-contrast}}'; }md-tooltip.md-THEME_NAME-theme { color: '{{background-700-contrast}}'; } md-tooltip.md-THEME_NAME-theme .md-content { background-color: '{{background-700}}'; }");
6917})();
6918
6919
6920})(window, window.angular);