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