nexus site path corrected
[portal.git] / ecomp-portal-FE / client / bower_components / angular-material / modules / closure / core / core.js
1 /*!
2  * Angular Material Design
3  * https://github.com/angular/material
4  * @license MIT
5  * v0.9.8
6  */
7 goog.provide('ng.material.core');
8
9
10
11 /**
12  * Initialization function that validates environment
13  * requirements.
14  */
15 angular
16   .module('material.core', [ 'material.core.gestures', 'material.core.theming' ])
17   .config( MdCoreConfigure );
18
19
20 function MdCoreConfigure($provide, $mdThemingProvider) {
21
22   $provide.decorator('$$rAF', ["$delegate", rAFDecorator]);
23
24   $mdThemingProvider.theme('default')
25     .primaryPalette('indigo')
26     .accentPalette('pink')
27     .warnPalette('red')
28     .backgroundPalette('grey');
29 }
30 MdCoreConfigure.$inject = ["$provide", "$mdThemingProvider"];
31
32 function rAFDecorator( $delegate ) {
33   /**
34    * Use this to throttle events that come in often.
35    * The throttled function will always use the *last* invocation before the
36    * coming frame.
37    *
38    * For example, window resize events that fire many times a second:
39    * If we set to use an raf-throttled callback on window resize, then
40    * our callback will only be fired once per frame, with the last resize
41    * event that happened before that frame.
42    *
43    * @param {function} callback function to debounce
44    */
45   $delegate.throttle = function(cb) {
46     var queueArgs, alreadyQueued, queueCb, context;
47     return function debounced() {
48       queueArgs = arguments;
49       context = this;
50       queueCb = cb;
51       if (!alreadyQueued) {
52         alreadyQueued = true;
53         $delegate(function() {
54           queueCb.apply(context, queueArgs);
55           alreadyQueued = false;
56         });
57       }
58     };
59   };
60   return $delegate;
61 }
62
63 angular.module('material.core')
64 .factory('$mdConstant', MdConstantFactory);
65
66 function MdConstantFactory($$rAF, $sniffer) {
67
68   var webkit = /webkit/i.test($sniffer.vendorPrefix);
69   function vendorProperty(name) {
70     return webkit ?  ('webkit' + name.charAt(0).toUpperCase() + name.substring(1)) : name;
71   }
72
73   return {
74     KEY_CODE: {
75       ENTER: 13,
76       ESCAPE: 27,
77       SPACE: 32,
78       LEFT_ARROW : 37,
79       UP_ARROW : 38,
80       RIGHT_ARROW : 39,
81       DOWN_ARROW : 40,
82       TAB : 9,
83       BACKSPACE: 8,
84       DELETE: 46
85     },
86     CSS: {
87       /* Constants */
88       TRANSITIONEND: 'transitionend' + (webkit ? ' webkitTransitionEnd' : ''),
89       ANIMATIONEND: 'animationend' + (webkit ? ' webkitAnimationEnd' : ''),
90
91       TRANSFORM: vendorProperty('transform'),
92       TRANSFORM_ORIGIN: vendorProperty('transformOrigin'),
93       TRANSITION: vendorProperty('transition'),
94       TRANSITION_DURATION: vendorProperty('transitionDuration'),
95       ANIMATION_PLAY_STATE: vendorProperty('animationPlayState'),
96       ANIMATION_DURATION: vendorProperty('animationDuration'),
97       ANIMATION_NAME: vendorProperty('animationName'),
98       ANIMATION_TIMING: vendorProperty('animationTimingFunction'),
99       ANIMATION_DIRECTION: vendorProperty('animationDirection')
100     },
101     MEDIA: {
102       'sm': '(max-width: 600px)',
103       'gt-sm': '(min-width: 600px)',
104       'md': '(min-width: 600px) and (max-width: 960px)',
105       'gt-md': '(min-width: 960px)',
106       'lg': '(min-width: 960px) and (max-width: 1200px)',
107       'gt-lg': '(min-width: 1200px)'
108     },
109     MEDIA_PRIORITY: [
110       'gt-lg',
111       'lg',
112       'gt-md',
113       'md',
114       'gt-sm',
115       'sm'
116     ]
117   };
118 }
119 MdConstantFactory.$inject = ["$$rAF", "$sniffer"];
120
121   angular
122     .module('material.core')
123     .config( ["$provide", function($provide){
124        $provide.decorator('$mdUtil', ['$delegate', function ($delegate){
125            /**
126             * Inject the iterator facade to easily support iteration and accessors
127             * @see iterator below
128             */
129            $delegate.iterator = MdIterator;
130
131            return $delegate;
132          }
133        ]);
134      }]);
135
136   /**
137    * iterator is a list facade to easily support iteration and accessors
138    *
139    * @param items Array list which this iterator will enumerate
140    * @param reloop Boolean enables iterator to consider the list as an endless reloop
141    */
142   function MdIterator(items, reloop) {
143     var trueFn = function() { return true; };
144
145     if (items && !angular.isArray(items)) {
146       items = Array.prototype.slice.call(items);
147     }
148
149     reloop = !!reloop;
150     var _items = items || [ ];
151
152     // Published API
153     return {
154       items: getItems,
155       count: count,
156
157       inRange: inRange,
158       contains: contains,
159       indexOf: indexOf,
160       itemAt: itemAt,
161
162       findBy: findBy,
163
164       add: add,
165       remove: remove,
166
167       first: first,
168       last: last,
169       next: angular.bind(null, findSubsequentItem, false),
170       previous: angular.bind(null, findSubsequentItem, true),
171
172       hasPrevious: hasPrevious,
173       hasNext: hasNext
174
175     };
176
177     /**
178      * Publish copy of the enumerable set
179      * @returns {Array|*}
180      */
181     function getItems() {
182       return [].concat(_items);
183     }
184
185     /**
186      * Determine length of the list
187      * @returns {Array.length|*|number}
188      */
189     function count() {
190       return _items.length;
191     }
192
193     /**
194      * Is the index specified valid
195      * @param index
196      * @returns {Array.length|*|number|boolean}
197      */
198     function inRange(index) {
199       return _items.length && ( index > -1 ) && (index < _items.length );
200     }
201
202     /**
203      * Can the iterator proceed to the next item in the list; relative to
204      * the specified item.
205      *
206      * @param item
207      * @returns {Array.length|*|number|boolean}
208      */
209     function hasNext(item) {
210       return item ? inRange(indexOf(item) + 1) : false;
211     }
212
213     /**
214      * Can the iterator proceed to the previous item in the list; relative to
215      * the specified item.
216      *
217      * @param item
218      * @returns {Array.length|*|number|boolean}
219      */
220     function hasPrevious(item) {
221       return item ? inRange(indexOf(item) - 1) : false;
222     }
223
224     /**
225      * Get item at specified index/position
226      * @param index
227      * @returns {*}
228      */
229     function itemAt(index) {
230       return inRange(index) ? _items[index] : null;
231     }
232
233     /**
234      * Find all elements matching the key/value pair
235      * otherwise return null
236      *
237      * @param val
238      * @param key
239      *
240      * @return array
241      */
242     function findBy(key, val) {
243       return _items.filter(function(item) {
244         return item[key] === val;
245       });
246     }
247
248     /**
249      * Add item to list
250      * @param item
251      * @param index
252      * @returns {*}
253      */
254     function add(item, index) {
255       if ( !item ) return -1;
256
257       if (!angular.isNumber(index)) {
258         index = _items.length;
259       }
260
261       _items.splice(index, 0, item);
262
263       return indexOf(item);
264     }
265
266     /**
267      * Remove item from list...
268      * @param item
269      */
270     function remove(item) {
271       if ( contains(item) ){
272         _items.splice(indexOf(item), 1);
273       }
274     }
275
276     /**
277      * Get the zero-based index of the target item
278      * @param item
279      * @returns {*}
280      */
281     function indexOf(item) {
282       return _items.indexOf(item);
283     }
284
285     /**
286      * Boolean existence check
287      * @param item
288      * @returns {boolean}
289      */
290     function contains(item) {
291       return item && (indexOf(item) > -1);
292     }
293
294     /**
295      * Return first item in the list
296      * @returns {*}
297      */
298     function first() {
299       return _items.length ? _items[0] : null;
300     }
301
302     /**
303      * Return last item in the list...
304      * @returns {*}
305      */
306     function last() {
307       return _items.length ? _items[_items.length - 1] : null;
308     }
309
310     /**
311      * Find the next item. If reloop is true and at the end of the list, it will go back to the
312      * first item. If given, the `validate` callback will be used to determine whether the next item
313      * is valid. If not valid, it will try to find the next item again.
314      *
315      * @param {boolean} backwards Specifies the direction of searching (forwards/backwards)
316      * @param {*} item The item whose subsequent item we are looking for
317      * @param {Function=} validate The `validate` function
318      * @param {integer=} limit The recursion limit
319      *
320      * @returns {*} The subsequent item or null
321      */
322     function findSubsequentItem(backwards, item, validate, limit) {
323       validate = validate || trueFn;
324
325       var curIndex = indexOf(item);
326       while (true) {
327         if (!inRange(curIndex)) return null;
328
329         var nextIndex = curIndex + (backwards ? -1 : 1);
330         var foundItem = null;
331         if (inRange(nextIndex)) {
332           foundItem = _items[nextIndex];
333         } else if (reloop) {
334           foundItem = backwards ? last() : first();
335           nextIndex = indexOf(foundItem);
336         }
337
338         if ((foundItem === null) || (nextIndex === limit)) return null;
339         if (validate(foundItem)) return foundItem;
340
341         if (angular.isUndefined(limit)) limit = nextIndex;
342
343         curIndex = nextIndex;
344       }
345     }
346   }
347
348
349 angular.module('material.core')
350 .factory('$mdMedia', mdMediaFactory);
351
352 /**
353  * @ngdoc service
354  * @name $mdMedia
355  * @module material.core
356  *
357  * @description
358  * `$mdMedia` is used to evaluate whether a given media query is true or false given the
359  * current device's screen / window size. The media query will be re-evaluated on resize, allowing
360  * you to register a watch.
361  *
362  * `$mdMedia` also has pre-programmed support for media queries that match the layout breakpoints.
363  *  (`sm`, `gt-sm`, `md`, `gt-md`, `lg`, `gt-lg`).
364  *
365  * @returns {boolean} a boolean representing whether or not the given media query is true or false.
366  *
367  * @usage
368  * <hljs lang="js">
369  * app.controller('MyController', function($mdMedia, $scope) {
370  *   $scope.$watch(function() { return $mdMedia('lg'); }, function(big) {
371  *     $scope.bigScreen = big;
372  *   });
373  *
374  *   $scope.screenIsSmall = $mdMedia('sm');
375  *   $scope.customQuery = $mdMedia('(min-width: 1234px)');
376  *   $scope.anotherCustom = $mdMedia('max-width: 300px');
377  * });
378  * </hljs>
379  */
380
381 function mdMediaFactory($mdConstant, $rootScope, $window) {
382   var queries = {};
383   var mqls = {};
384   var results = {};
385   var normalizeCache = {};
386
387   $mdMedia.getResponsiveAttribute = getResponsiveAttribute;
388   $mdMedia.getQuery = getQuery;
389   $mdMedia.watchResponsiveAttributes = watchResponsiveAttributes;
390
391   return $mdMedia;
392
393   function $mdMedia(query) {
394     var validated = queries[query];
395     if (angular.isUndefined(validated)) {
396       validated = queries[query] = validate(query);
397     }
398
399     var result = results[validated];
400     if (angular.isUndefined(result)) {
401       result = add(validated);
402     }
403
404     return result;
405   }
406
407   function validate(query) {
408     return $mdConstant.MEDIA[query] ||
409            ((query.charAt(0) !== '(') ? ('(' + query + ')') : query);
410   }
411
412   function add(query) {
413     var result = mqls[query] = $window.matchMedia(query);
414     result.addListener(onQueryChange);
415     return (results[result.media] = !!result.matches);
416   }
417
418   function onQueryChange(query) {
419     $rootScope.$evalAsync(function() {
420       results[query.media] = !!query.matches;
421     });
422   }
423
424   function getQuery(name) {
425     return mqls[name];
426   }
427
428   function getResponsiveAttribute(attrs, attrName) {
429     for (var i = 0; i < $mdConstant.MEDIA_PRIORITY.length; i++) {
430       var mediaName = $mdConstant.MEDIA_PRIORITY[i];
431       if (!mqls[queries[mediaName]].matches) {
432         continue;
433       }
434
435       var normalizedName = getNormalizedName(attrs, attrName + '-' + mediaName);
436       if (attrs[normalizedName]) {
437         return attrs[normalizedName];
438       }
439     }
440
441     // fallback on unprefixed
442     return attrs[getNormalizedName(attrs, attrName)];
443   }
444
445   function watchResponsiveAttributes(attrNames, attrs, watchFn) {
446     var unwatchFns = [];
447     attrNames.forEach(function(attrName) {
448       var normalizedName = getNormalizedName(attrs, attrName);
449       if (attrs[normalizedName]) {
450         unwatchFns.push(
451             attrs.$observe(normalizedName, angular.bind(void 0, watchFn, null)));
452       }
453
454       for (var mediaName in $mdConstant.MEDIA) {
455         normalizedName = getNormalizedName(attrs, attrName + '-' + mediaName);
456         if (!attrs[normalizedName]) {
457           return;
458         }
459
460         unwatchFns.push(attrs.$observe(normalizedName, angular.bind(void 0, watchFn, mediaName)));
461       }
462     });
463
464     return function unwatch() {
465       unwatchFns.forEach(function(fn) { fn(); })
466     };
467   }
468
469   // Improves performance dramatically
470   function getNormalizedName(attrs, attrName) {
471     return normalizeCache[attrName] ||
472         (normalizeCache[attrName] = attrs.$normalize(attrName));
473   }
474 }
475 mdMediaFactory.$inject = ["$mdConstant", "$rootScope", "$window"];
476
477 /*
478  * This var has to be outside the angular factory, otherwise when
479  * there are multiple material apps on the same page, each app
480  * will create its own instance of this array and the app's IDs
481  * will not be unique.
482  */
483 var nextUniqueId = 0;
484
485 angular.module('material.core')
486 .factory('$mdUtil', ["$cacheFactory", "$document", "$timeout", "$q", "$window", "$mdConstant", function($cacheFactory, $document, $timeout, $q, $window, $mdConstant) {
487   var Util;
488
489   function getNode(el) {
490     return el[0] || el;
491   }
492
493   return Util = {
494     now: window.performance ?
495       angular.bind(window.performance, window.performance.now) :
496       Date.now,
497
498     clientRect: function(element, offsetParent, isOffsetRect) {
499       var node = getNode(element);
500       offsetParent = getNode(offsetParent || node.offsetParent || document.body);
501       var nodeRect = node.getBoundingClientRect();
502
503       // The user can ask for an offsetRect: a rect relative to the offsetParent,
504       // or a clientRect: a rect relative to the page
505       var offsetRect = isOffsetRect ?
506         offsetParent.getBoundingClientRect() :
507         { left: 0, top: 0, width: 0, height: 0 };
508       return {
509         left: nodeRect.left - offsetRect.left,
510         top: nodeRect.top - offsetRect.top,
511         width: nodeRect.width,
512         height: nodeRect.height
513       };
514     },
515     offsetRect: function(element, offsetParent) {
516       return Util.clientRect(element, offsetParent, true);
517     },
518     // Disables scroll around the passed element. Goes up the DOM to find a
519     // disableTarget (a md-content that is scrolling, or the body as a fallback)
520     // and uses CSS/JS to prevent it from scrolling
521     disableScrollAround: function(element) {
522       element = element instanceof angular.element ? element[0] : element;
523       var parentEl = element;
524       var disableTarget;
525
526       // Find the highest level scrolling md-content
527       while (parentEl = this.getClosest(parentEl, 'MD-CONTENT', true)) {
528         if (isScrolling(parentEl)) {
529           disableTarget = angular.element(parentEl)[0];
530         }
531       }
532
533       // Default to the body if no scrolling md-content
534       if (!disableTarget) {
535         disableTarget = $document[0].body;
536         if (!isScrolling(disableTarget)) return angular.noop;
537       }
538
539       if (disableTarget.nodeName == 'BODY') {
540         return disableBodyScroll();
541       } else {
542         return disableElementScroll();
543       }
544
545       // Creates a virtual scrolling mask to absorb touchmove, keyboard, scrollbar clicking, and wheel events
546       function disableElementScroll() {
547         var scrollMask = angular.element('<div class="md-scroll-mask"><div class="md-scroll-mask-bar"></div></div>');
548         var computedStyle = $window.getComputedStyle(disableTarget);
549         var disableRect = disableTarget.getBoundingClientRect();
550         var scrollWidth = disableRect.width - disableTarget.clientWidth;
551         applyStyles(scrollMask[0], {
552           zIndex: computedStyle.zIndex == 'auto' ? 2 : computedStyle.zIndex + 1,
553           width: disableRect.width + 'px',
554           height: disableRect.height + 'px',
555           top: disableRect.top + 'px',
556           left: disableRect.left + 'px'
557         });
558         scrollMask[0].firstElementChild.style.width = scrollWidth + 'px';
559         $document[0].body.appendChild(scrollMask[0]);
560
561         scrollMask.on('wheel', preventDefault);
562         scrollMask.on('touchmove', preventDefault);
563         $document.on('keydown', disableKeyNav);
564
565         return function restoreScroll() {
566           scrollMask.off('wheel');
567           scrollMask.off('touchmove');
568           scrollMask[0].parentNode.removeChild(scrollMask[0]);
569           $document.off('keydown', disableKeyNav);
570         };
571
572         // Prevent keypresses from elements inside the disableTarget
573         // used to stop the keypresses that could cause the page to scroll
574         // (arrow keys, spacebar, tab, etc).
575         function disableKeyNav(e) {
576           if (disableTarget.contains(e.target)) {
577             e.preventDefault();
578             e.stopImmediatePropagation();
579           }
580         }
581
582         function preventDefault(e) {
583           e.preventDefault();
584         }
585       }
586
587       // Converts the disableTarget (body) to a position fixed block and translate it to the propper scroll position
588       function disableBodyScroll() {
589         var restoreStyle = disableTarget.getAttribute('style') || '';
590         var scrollOffset = disableTarget.scrollTop;
591
592         applyStyles(disableTarget, {
593           position: 'fixed',
594           width: '100%',
595           overflowY: 'scroll',
596           top: -scrollOffset + 'px'
597         });
598
599         return function restoreScroll() {
600           disableTarget.setAttribute('style', restoreStyle);
601           disableTarget.scrollTop = scrollOffset;
602         };
603       }
604
605       function applyStyles (el, styles) {
606         for (var key in styles) {
607           el.style[key] = styles[key];
608         }
609       }
610
611       function isScrolling(el) {
612         if (el instanceof angular.element) el = el[0];
613         return el.scrollHeight > el.offsetHeight;
614       }
615     },
616
617     floatingScrollbars: function() {
618       if (this.floatingScrollbars.cached === undefined) {
619         var tempNode = angular.element('<div style="width: 100%; z-index: -1; position: absolute; height: 35px; overflow-y: scroll"><div style="height: 60;"></div></div>');
620         $document[0].body.appendChild(tempNode[0]);
621         this.floatingScrollbars.cached = (tempNode[0].offsetWidth == tempNode[0].childNodes[0].offsetWidth);
622         tempNode.remove();
623       }
624       return this.floatingScrollbars.cached;
625     },
626
627     // Mobile safari only allows you to set focus in click event listeners...
628     forceFocus: function(element) {
629       var node = element[0] || element;
630
631       document.addEventListener('click', function focusOnClick(ev) {
632         if (ev.target === node && ev.$focus) {
633           node.focus();
634           ev.stopImmediatePropagation();
635           ev.preventDefault();
636           node.removeEventListener('click', focusOnClick);
637         }
638       }, true);
639
640       var newEvent = document.createEvent('MouseEvents');
641       newEvent.initMouseEvent('click', false, true, window, {}, 0, 0, 0, 0,
642                        false, false, false, false, 0, null);
643       newEvent.$material = true;
644       newEvent.$focus = true;
645       node.dispatchEvent(newEvent);
646     },
647
648     transitionEndPromise: function(element, opts) {
649       opts = opts || {};
650       var deferred = $q.defer();
651       element.on($mdConstant.CSS.TRANSITIONEND, finished);
652       function finished(ev) {
653         // Make sure this transitionend didn't bubble up from a child
654         if (!ev || ev.target === element[0]) {
655           element.off($mdConstant.CSS.TRANSITIONEND, finished);
656           deferred.resolve();
657         }
658       }
659       if (opts.timeout) $timeout(finished, opts.timeout);
660       return deferred.promise;
661     },
662
663     fakeNgModel: function() {
664       return {
665         $fake: true,
666         $setTouched: angular.noop,
667         $setViewValue: function(value) {
668           this.$viewValue = value;
669           this.$render(value);
670           this.$viewChangeListeners.forEach(function(cb) { cb(); });
671         },
672         $isEmpty: function(value) {
673           return ('' + value).length === 0;
674         },
675         $parsers: [],
676         $formatters: [],
677         $viewChangeListeners: [],
678         $render: angular.noop
679       };
680     },
681
682     // Returns a function, that, as long as it continues to be invoked, will not
683     // be triggered. The function will be called after it stops being called for
684     // N milliseconds.
685     // @param wait Integer value of msecs to delay (since last debounce reset); default value 10 msecs
686     // @param invokeApply should the $timeout trigger $digest() dirty checking
687     debounce: function (func, wait, scope, invokeApply) {
688       var timer;
689
690       return function debounced() {
691         var context = scope,
692           args = Array.prototype.slice.call(arguments);
693
694         $timeout.cancel(timer);
695         timer = $timeout(function() {
696
697           timer = undefined;
698           func.apply(context, args);
699
700         }, wait || 10, invokeApply );
701       };
702     },
703
704     // Returns a function that can only be triggered every `delay` milliseconds.
705     // In other words, the function will not be called unless it has been more
706     // than `delay` milliseconds since the last call.
707     throttle: function throttle(func, delay) {
708       var recent;
709       return function throttled() {
710         var context = this;
711         var args = arguments;
712         var now = Util.now();
713
714         if (!recent || (now - recent > delay)) {
715           func.apply(context, args);
716           recent = now;
717         }
718       };
719     },
720
721     /**
722      * Measures the number of milliseconds taken to run the provided callback
723      * function. Uses a high-precision timer if available.
724      */
725     time: function time(cb) {
726       var start = Util.now();
727       cb();
728       return Util.now() - start;
729     },
730
731     /**
732      * Get a unique ID.
733      *
734      * @returns {string} an unique numeric string
735      */
736     nextUid: function() {
737       return '' + nextUniqueId++;
738     },
739
740     // Stop watchers and events from firing on a scope without destroying it,
741     // by disconnecting it from its parent and its siblings' linked lists.
742     disconnectScope: function disconnectScope(scope) {
743       if (!scope) return;
744
745       // we can't destroy the root scope or a scope that has been already destroyed
746       if (scope.$root === scope) return;
747       if (scope.$$destroyed ) return;
748
749       var parent = scope.$parent;
750       scope.$$disconnected = true;
751
752       // See Scope.$destroy
753       if (parent.$$childHead === scope) parent.$$childHead = scope.$$nextSibling;
754       if (parent.$$childTail === scope) parent.$$childTail = scope.$$prevSibling;
755       if (scope.$$prevSibling) scope.$$prevSibling.$$nextSibling = scope.$$nextSibling;
756       if (scope.$$nextSibling) scope.$$nextSibling.$$prevSibling = scope.$$prevSibling;
757
758       scope.$$nextSibling = scope.$$prevSibling = null;
759
760     },
761
762     // Undo the effects of disconnectScope above.
763     reconnectScope: function reconnectScope(scope) {
764       if (!scope) return;
765
766       // we can't disconnect the root node or scope already disconnected
767       if (scope.$root === scope) return;
768       if (!scope.$$disconnected) return;
769
770       var child = scope;
771
772       var parent = child.$parent;
773       child.$$disconnected = false;
774       // See Scope.$new for this logic...
775       child.$$prevSibling = parent.$$childTail;
776       if (parent.$$childHead) {
777         parent.$$childTail.$$nextSibling = child;
778         parent.$$childTail = child;
779       } else {
780         parent.$$childHead = parent.$$childTail = child;
781       }
782     },
783
784     /*
785      * getClosest replicates jQuery.closest() to walk up the DOM tree until it finds a matching nodeName
786      *
787      * @param el Element to start walking the DOM from
788      * @param tagName Tag name to find closest to el, such as 'form'
789      */
790     getClosest: function getClosest(el, tagName, onlyParent) {
791       if (el instanceof angular.element) el = el[0];
792       tagName = tagName.toUpperCase();
793       if (onlyParent) el = el.parentNode;
794       if (!el) return null;
795       do {
796         if (el.nodeName === tagName) {
797           return el;
798         }
799       } while (el = el.parentNode);
800       return null;
801     },
802
803     /**
804      * Functional equivalent for $element.filter(‘md-bottom-sheet’)
805      * useful with interimElements where the element and its container are important...
806      */
807     extractElementByName: function (element, nodeName) {
808       for (var i = 0, len = element.length; i < len; i++) {
809         if (element[i].nodeName.toLowerCase() === nodeName){
810           return angular.element(element[i]);
811         }
812       }
813       return element;
814     },
815
816     /**
817      * Give optional properties with no value a boolean true by default
818      */
819     initOptionalProperties: function (scope, attr, defaults ) {
820        defaults = defaults || { };
821        angular.forEach(scope.$$isolateBindings, function (binding, key) {
822          if (binding.optional && angular.isUndefined(scope[key])) {
823            var hasKey = attr.hasOwnProperty(attr.$normalize(binding.attrName));
824
825            scope[key] = angular.isDefined(defaults[key]) ? defaults[key] : hasKey;
826          }
827        });
828     }
829
830   };
831
832 }]);
833
834 /*
835  * Since removing jQuery from the demos, some code that uses `element.focus()` is broken.
836  *
837  * We need to add `element.focus()`, because it's testable unlike `element[0].focus`.
838  *
839  * TODO(ajoslin): This should be added in a better place later.
840  */
841
842 angular.element.prototype.focus = angular.element.prototype.focus || function() {
843   if (this.length) {
844     this[0].focus();
845   }
846   return this;
847 };
848 angular.element.prototype.blur = angular.element.prototype.blur || function() {
849   if (this.length) {
850     this[0].blur();
851   }
852   return this;
853 };
854
855
856 angular.module('material.core')
857   .service('$mdAria', AriaService);
858
859 /*
860  * ngInject
861  */
862 function AriaService($$rAF, $log, $window) {
863
864   return {
865     expect: expect,
866     expectAsync: expectAsync,
867     expectWithText: expectWithText
868   };
869
870   /**
871    * Check if expected attribute has been specified on the target element or child
872    * @param element
873    * @param attrName
874    * @param {optional} defaultValue What to set the attr to if no value is found
875    */
876   function expect(element, attrName, defaultValue) {
877     var node = element[0] || element;
878
879     // if node exists and neither it nor its children have the attribute
880     if (node &&
881        ((!node.hasAttribute(attrName) ||
882         node.getAttribute(attrName).length === 0) &&
883         !childHasAttribute(node, attrName))) {
884
885       defaultValue = angular.isString(defaultValue) ? defaultValue.trim() : '';
886       if (defaultValue.length) {
887         element.attr(attrName, defaultValue);
888       } else {
889         $log.warn('ARIA: Attribute "', attrName, '", required for accessibility, is missing on node:', node);
890       }
891
892     }
893   }
894
895   function expectAsync(element, attrName, defaultValueGetter) {
896     // Problem: when retrieving the element's contents synchronously to find the label,
897     // the text may not be defined yet in the case of a binding.
898     // There is a higher chance that a binding will be defined if we wait one frame.
899     $$rAF(function() {
900       expect(element, attrName, defaultValueGetter());
901     });
902   }
903
904   function expectWithText(element, attrName) {
905     expectAsync(element, attrName, function() {
906       return getText(element);
907     });
908   }
909
910   function getText(element) {
911     return element.text().trim();
912   }
913
914   function childHasAttribute(node, attrName) {
915     var hasChildren = node.hasChildNodes(),
916         hasAttr = false;
917
918     function isHidden(el) {
919       var style = el.currentStyle ? el.currentStyle : $window.getComputedStyle(el);
920       return (style.display === 'none');
921     }
922
923     if(hasChildren) {
924       var children = node.childNodes;
925       for(var i=0; i<children.length; i++){
926         var child = children[i];
927         if(child.nodeType === 1 && child.hasAttribute(attrName)) {
928           if(!isHidden(child)){
929             hasAttr = true;
930           }
931         }
932       }
933     }
934     return hasAttr;
935   }
936 }
937 AriaService.$inject = ["$$rAF", "$log", "$window"];
938
939 angular.module('material.core')
940   .service('$mdCompiler', mdCompilerService);
941
942 function mdCompilerService($q, $http, $injector, $compile, $controller, $templateCache) {
943   /* jshint validthis: true */
944
945   /*
946    * @ngdoc service
947    * @name $mdCompiler
948    * @module material.core
949    * @description
950    * The $mdCompiler service is an abstraction of angular's compiler, that allows the developer
951    * to easily compile an element with a templateUrl, controller, and locals.
952    *
953    * @usage
954    * <hljs lang="js">
955    * $mdCompiler.compile({
956    *   templateUrl: 'modal.html',
957    *   controller: 'ModalCtrl',
958    *   locals: {
959    *     modal: myModalInstance;
960    *   }
961    * }).then(function(compileData) {
962    *   compileData.element; // modal.html's template in an element
963    *   compileData.link(myScope); //attach controller & scope to element
964    * });
965    * </hljs>
966    */
967
968    /*
969     * @ngdoc method
970     * @name $mdCompiler#compile
971     * @description A helper to compile an HTML template/templateUrl with a given controller,
972     * locals, and scope.
973     * @param {object} options An options object, with the following properties:
974     *
975     *    - `controller` - `{(string=|function()=}` Controller fn that should be associated with
976     *      newly created scope or the name of a registered controller if passed as a string.
977     *    - `controllerAs` - `{string=}` A controller alias name. If present the controller will be
978     *      published to scope under the `controllerAs` name.
979     *    - `template` - `{string=}` An html template as a string.
980     *    - `templateUrl` - `{string=}` A path to an html template.
981     *    - `transformTemplate` - `{function(template)=}` A function which transforms the template after
982     *      it is loaded. It will be given the template string as a parameter, and should
983     *      return a a new string representing the transformed template.
984     *    - `resolve` - `{Object.<string, function>=}` - An optional map of dependencies which should
985     *      be injected into the controller. If any of these dependencies are promises, the compiler
986     *      will wait for them all to be resolved, or if one is rejected before the controller is
987     *      instantiated `compile()` will fail..
988     *      * `key` - `{string}`: a name of a dependency to be injected into the controller.
989     *      * `factory` - `{string|function}`: If `string` then it is an alias for a service.
990     *        Otherwise if function, then it is injected and the return value is treated as the
991     *        dependency. If the result is a promise, it is resolved before its value is 
992     *        injected into the controller.
993     *
994     * @returns {object=} promise A promise, which will be resolved with a `compileData` object.
995     * `compileData` has the following properties: 
996     *
997     *   - `element` - `{element}`: an uncompiled element matching the provided template.
998     *   - `link` - `{function(scope)}`: A link function, which, when called, will compile
999     *     the element and instantiate the provided controller (if given).
1000     *   - `locals` - `{object}`: The locals which will be passed into the controller once `link` is
1001     *     called. If `bindToController` is true, they will be coppied to the ctrl instead
1002     *   - `bindToController` - `bool`: bind the locals to the controller, instead of passing them in.
1003     */
1004   this.compile = function(options) {
1005     var templateUrl = options.templateUrl;
1006     var template = options.template || '';
1007     var controller = options.controller;
1008     var controllerAs = options.controllerAs;
1009     var resolve = options.resolve || {};
1010     var locals = options.locals || {};
1011     var transformTemplate = options.transformTemplate || angular.identity;
1012     var bindToController = options.bindToController;
1013
1014     // Take resolve values and invoke them.  
1015     // Resolves can either be a string (value: 'MyRegisteredAngularConst'),
1016     // or an invokable 'factory' of sorts: (value: function ValueGetter($dependency) {})
1017     angular.forEach(resolve, function(value, key) {
1018       if (angular.isString(value)) {
1019         resolve[key] = $injector.get(value);
1020       } else {
1021         resolve[key] = $injector.invoke(value);
1022       }
1023     });
1024     //Add the locals, which are just straight values to inject
1025     //eg locals: { three: 3 }, will inject three into the controller
1026     angular.extend(resolve, locals);
1027
1028     if (templateUrl) {
1029       resolve.$template = $http.get(templateUrl, {cache: $templateCache})
1030         .then(function(response) {
1031           return response.data;
1032         });
1033     } else {
1034       resolve.$template = $q.when(template);
1035     }
1036
1037     // Wait for all the resolves to finish if they are promises
1038     return $q.all(resolve).then(function(locals) {
1039
1040       var template = transformTemplate(locals.$template);
1041       var element = options.element || angular.element('<div>').html(template.trim()).contents();
1042       var linkFn = $compile(element);
1043
1044       //Return a linking function that can be used later when the element is ready
1045       return {
1046         locals: locals,
1047         element: element,
1048         link: function link(scope) {
1049           locals.$scope = scope;
1050
1051           //Instantiate controller if it exists, because we have scope
1052           if (controller) {
1053             var invokeCtrl = $controller(controller, locals, true);
1054             if (bindToController) {
1055               angular.extend(invokeCtrl.instance, locals);
1056             }
1057             var ctrl = invokeCtrl();
1058             //See angular-route source for this logic
1059             element.data('$ngControllerController', ctrl);
1060             element.children().data('$ngControllerController', ctrl);
1061
1062             if (controllerAs) {
1063               scope[controllerAs] = ctrl;
1064             }
1065           }
1066           return linkFn(scope);
1067         }
1068       };
1069     });
1070
1071   };
1072 }
1073 mdCompilerService.$inject = ["$q", "$http", "$injector", "$compile", "$controller", "$templateCache"];
1074
1075 angular.module('material.core')
1076   .provider('$$interimElement', InterimElementProvider);
1077
1078 /*
1079  * @ngdoc service
1080  * @name $$interimElement
1081  * @module material.core
1082  *
1083  * @description
1084  *
1085  * Factory that contructs `$$interimElement.$service` services.
1086  * Used internally in material design for elements that appear on screen temporarily.
1087  * The service provides a promise-like API for interacting with the temporary
1088  * elements.
1089  *
1090  * ```js
1091  * app.service('$mdToast', function($$interimElement) {
1092  *   var $mdToast = $$interimElement(toastDefaultOptions);
1093  *   return $mdToast;
1094  * });
1095  * ```
1096  * @param {object=} defaultOptions Options used by default for the `show` method on the service.
1097  *
1098  * @returns {$$interimElement.$service}
1099  *
1100  */
1101
1102 function InterimElementProvider() {
1103   createInterimElementProvider.$get = InterimElementFactory;
1104   InterimElementFactory.$inject = ["$document", "$q", "$rootScope", "$timeout", "$rootElement", "$animate", "$interpolate", "$mdCompiler", "$mdTheming"];
1105   return createInterimElementProvider;
1106
1107   /**
1108    * Returns a new provider which allows configuration of a new interimElement
1109    * service. Allows configuration of default options & methods for options,
1110    * as well as configuration of 'preset' methods (eg dialog.basic(): basic is a preset method)
1111    */
1112   function createInterimElementProvider(interimFactoryName) {
1113     var EXPOSED_METHODS = ['onHide', 'onShow', 'onRemove'];
1114
1115     var customMethods = {};
1116     var providerConfig = {
1117       presets: {}
1118     };
1119
1120     var provider = {
1121       setDefaults: setDefaults,
1122       addPreset: addPreset,
1123       addMethod: addMethod,
1124       $get: factory
1125     };
1126
1127     /**
1128      * all interim elements will come with the 'build' preset
1129      */
1130     provider.addPreset('build', {
1131       methods: ['controller', 'controllerAs', 'resolve',
1132         'template', 'templateUrl', 'themable', 'transformTemplate', 'parent']
1133     });
1134
1135     factory.$inject = ["$$interimElement", "$animate", "$injector"];
1136     return provider;
1137
1138     /**
1139      * Save the configured defaults to be used when the factory is instantiated
1140      */
1141     function setDefaults(definition) {
1142       providerConfig.optionsFactory = definition.options;
1143       providerConfig.methods = (definition.methods || []).concat(EXPOSED_METHODS);
1144       return provider;
1145     }
1146
1147     /**
1148      * Add a method to the factory that isn't specific to any interim element operations
1149      */
1150
1151     function addMethod(name, fn) {
1152       customMethods[name] = fn;
1153       return provider;
1154     }
1155
1156     /**
1157      * Save the configured preset to be used when the factory is instantiated
1158      */
1159     function addPreset(name, definition) {
1160       definition = definition || {};
1161       definition.methods = definition.methods || [];
1162       definition.options = definition.options || function() { return {}; };
1163
1164       if (/^cancel|hide|show$/.test(name)) {
1165         throw new Error("Preset '" + name + "' in " + interimFactoryName + " is reserved!");
1166       }
1167       if (definition.methods.indexOf('_options') > -1) {
1168         throw new Error("Method '_options' in " + interimFactoryName + " is reserved!");
1169       }
1170       providerConfig.presets[name] = {
1171         methods: definition.methods.concat(EXPOSED_METHODS),
1172         optionsFactory: definition.options,
1173         argOption: definition.argOption
1174       };
1175       return provider;
1176     }
1177
1178     /**
1179      * Create a factory that has the given methods & defaults implementing interimElement
1180      */
1181     /* ngInject */
1182     function factory($$interimElement, $animate, $injector) {
1183       var defaultMethods;
1184       var defaultOptions;
1185       var interimElementService = $$interimElement();
1186
1187       /*
1188        * publicService is what the developer will be using.
1189        * It has methods hide(), cancel(), show(), build(), and any other
1190        * presets which were set during the config phase.
1191        */
1192       var publicService = {
1193         hide: interimElementService.hide,
1194         cancel: interimElementService.cancel,
1195         show: showInterimElement
1196       };
1197
1198       defaultMethods = providerConfig.methods || [];
1199       // This must be invoked after the publicService is initialized
1200       defaultOptions = invokeFactory(providerConfig.optionsFactory, {});
1201
1202       // Copy over the simple custom methods
1203       angular.forEach(customMethods, function(fn, name) {
1204         publicService[name] = fn;
1205       });
1206
1207       angular.forEach(providerConfig.presets, function(definition, name) {
1208         var presetDefaults = invokeFactory(definition.optionsFactory, {});
1209         var presetMethods = (definition.methods || []).concat(defaultMethods);
1210
1211         // Every interimElement built with a preset has a field called `$type`,
1212         // which matches the name of the preset.
1213         // Eg in preset 'confirm', options.$type === 'confirm'
1214         angular.extend(presetDefaults, { $type: name });
1215
1216         // This creates a preset class which has setter methods for every
1217         // method given in the `.addPreset()` function, as well as every
1218         // method given in the `.setDefaults()` function.
1219         //
1220         // @example
1221         // .setDefaults({
1222         //   methods: ['hasBackdrop', 'clickOutsideToClose', 'escapeToClose', 'targetEvent'],
1223         //   options: dialogDefaultOptions
1224         // })
1225         // .addPreset('alert', {
1226         //   methods: ['title', 'ok'],
1227         //   options: alertDialogOptions
1228         // })
1229         //
1230         // Set values will be passed to the options when interimElemnt.show() is called.
1231         function Preset(opts) {
1232           this._options = angular.extend({}, presetDefaults, opts);
1233         }
1234         angular.forEach(presetMethods, function(name) {
1235           Preset.prototype[name] = function(value) {
1236             this._options[name] = value;
1237             return this;
1238           };
1239         });
1240
1241         // Create shortcut method for one-linear methods
1242         if (definition.argOption) {
1243           var methodName = 'show' + name.charAt(0).toUpperCase() + name.slice(1);
1244           publicService[methodName] = function(arg) {
1245             var config = publicService[name](arg);
1246             return publicService.show(config);
1247           };
1248         }
1249
1250         // eg $mdDialog.alert() will return a new alert preset
1251         publicService[name] = function(arg) {
1252           // If argOption is supplied, eg `argOption: 'content'`, then we assume
1253           // if the argument is not an options object then it is the `argOption` option.
1254           //
1255           // @example `$mdToast.simple('hello')` // sets options.content to hello
1256           //                                     // because argOption === 'content'
1257           if (arguments.length && definition.argOption && !angular.isObject(arg) &&
1258               !angular.isArray(arg)) {
1259             return (new Preset())[definition.argOption](arg);
1260           } else {
1261             return new Preset(arg);
1262           }
1263
1264         };
1265       });
1266
1267       return publicService;
1268
1269       function showInterimElement(opts) {
1270         // opts is either a preset which stores its options on an _options field,
1271         // or just an object made up of options
1272         if (opts && opts._options) opts = opts._options;
1273         return interimElementService.show(
1274           angular.extend({}, defaultOptions, opts)
1275         );
1276       }
1277
1278       /**
1279        * Helper to call $injector.invoke with a local of the factory name for
1280        * this provider.
1281        * If an $mdDialog is providing options for a dialog and tries to inject
1282        * $mdDialog, a circular dependency error will happen.
1283        * We get around that by manually injecting $mdDialog as a local.
1284        */
1285       function invokeFactory(factory, defaultVal) {
1286         var locals = {};
1287         locals[interimFactoryName] = publicService;
1288         return $injector.invoke(factory || function() { return defaultVal; }, {}, locals);
1289       }
1290
1291     }
1292
1293   }
1294
1295   /* ngInject */
1296   function InterimElementFactory($document, $q, $rootScope, $timeout, $rootElement, $animate,
1297                                  $interpolate, $mdCompiler, $mdTheming ) {
1298     var startSymbol = $interpolate.startSymbol(),
1299         endSymbol = $interpolate.endSymbol(),
1300         usesStandardSymbols = ((startSymbol === '{{') && (endSymbol === '}}')),
1301         processTemplate  = usesStandardSymbols ? angular.identity : replaceInterpolationSymbols;
1302
1303     return function createInterimElementService() {
1304       /*
1305        * @ngdoc service
1306        * @name $$interimElement.$service
1307        *
1308        * @description
1309        * A service used to control inserting and removing an element into the DOM.
1310        *
1311        */
1312       var stack = [];
1313       var service;
1314       return service = {
1315         show: show,
1316         hide: hide,
1317         cancel: cancel
1318       };
1319
1320       /*
1321        * @ngdoc method
1322        * @name $$interimElement.$service#show
1323        * @kind function
1324        *
1325        * @description
1326        * Adds the `$interimElement` to the DOM and returns a promise that will be resolved or rejected
1327        * with hide or cancel, respectively.
1328        *
1329        * @param {*} options is hashMap of settings
1330        * @returns a Promise
1331        *
1332        */
1333       function show(options) {
1334         if (stack.length) {
1335           return service.cancel().then(function() {
1336             return show(options);
1337           });
1338         } else {
1339           var interimElement = new InterimElement(options);
1340           stack.push(interimElement);
1341           return interimElement.show().then(function() {
1342             return interimElement.deferred.promise;
1343           });
1344         }
1345       }
1346
1347       /*
1348        * @ngdoc method
1349        * @name $$interimElement.$service#hide
1350        * @kind function
1351        *
1352        * @description
1353        * Removes the `$interimElement` from the DOM and resolves the promise returned from `show`
1354        *
1355        * @param {*} resolveParam Data to resolve the promise with
1356        * @returns a Promise that will be resolved after the element has been removed.
1357        *
1358        */
1359       function hide(response) {
1360         var interimElement = stack.shift();
1361         return interimElement && interimElement.remove().then(function() {
1362           interimElement.deferred.resolve(response);
1363         });
1364       }
1365
1366       /*
1367        * @ngdoc method
1368        * @name $$interimElement.$service#cancel
1369        * @kind function
1370        *
1371        * @description
1372        * Removes the `$interimElement` from the DOM and rejects the promise returned from `show`
1373        *
1374        * @param {*} reason Data to reject the promise with
1375        * @returns Promise that will be resolved after the element has been removed.
1376        *
1377        */
1378       function cancel(reason) {
1379         var interimElement = stack.shift();
1380         return $q.when(interimElement && interimElement.remove().then(function() {
1381           interimElement.deferred.reject(reason);
1382         }));
1383       }
1384
1385
1386       /*
1387        * Internal Interim Element Object
1388        * Used internally to manage the DOM element and related data
1389        */
1390       function InterimElement(options) {
1391         var self;
1392         var hideTimeout, element, showDone, removeDone;
1393
1394         options = options || {};
1395         options = angular.extend({
1396           preserveScope: false,
1397           scope: options.scope || $rootScope.$new(options.isolateScope),
1398           onShow: function(scope, element, options) {
1399             return $animate.enter(element, options.parent);
1400           },
1401           onRemove: function(scope, element, options) {
1402             // Element could be undefined if a new element is shown before
1403             // the old one finishes compiling.
1404             return element && $animate.leave(element) || $q.when();
1405           }
1406         }, options);
1407
1408         if (options.template) {
1409           options.template = processTemplate(options.template);
1410         }
1411
1412         return self = {
1413           options: options,
1414           deferred: $q.defer(),
1415           show: function() {
1416             var compilePromise;
1417             if (options.skipCompile) {
1418               compilePromise = $q(function(resolve) { 
1419                 resolve({
1420                   locals: {},
1421                   link: function() { return options.element; }
1422                 });
1423               });
1424             } else {
1425               compilePromise = $mdCompiler.compile(options);
1426             }
1427
1428             return showDone = compilePromise.then(function(compileData) {
1429               angular.extend(compileData.locals, self.options);
1430
1431               element = compileData.link(options.scope);
1432
1433               // Search for parent at insertion time, if not specified
1434               if (angular.isFunction(options.parent)) {
1435                 options.parent = options.parent(options.scope, element, options);
1436               } else if (angular.isString(options.parent)) {
1437                 options.parent = angular.element($document[0].querySelector(options.parent));
1438               }
1439
1440               // If parent querySelector/getter function fails, or it's just null,
1441               // find a default.
1442               if (!(options.parent || {}).length) {
1443                 var el;
1444                 if ($rootElement[0] && $rootElement[0].querySelector) {
1445                   el = $rootElement[0].querySelector(':not(svg) > body');
1446                 }
1447                 if (!el) el = $rootElement[0];
1448                 if (el.nodeName == '#comment') {
1449                   el = $document[0].body;
1450                 }
1451                 options.parent = angular.element(el);
1452               }
1453
1454               if (options.themable) $mdTheming(element);
1455               var ret = options.onShow(options.scope, element, options);
1456               return $q.when(ret)
1457                 .then(function(){
1458                   // Issue onComplete callback when the `show()` finishes
1459                   (options.onComplete || angular.noop)(options.scope, element, options);
1460                   startHideTimeout();
1461                 });
1462
1463               function startHideTimeout() {
1464                 if (options.hideDelay) {
1465                   hideTimeout = $timeout(service.cancel, options.hideDelay) ;
1466                 }
1467               }
1468             }, function(reason) { showDone = true; self.deferred.reject(reason); });
1469           },
1470           cancelTimeout: function() {
1471             if (hideTimeout) {
1472               $timeout.cancel(hideTimeout);
1473               hideTimeout = undefined;
1474             }
1475           },
1476           remove: function() {
1477             self.cancelTimeout();
1478             return removeDone = $q.when(showDone).then(function() {
1479               var ret = element ? options.onRemove(options.scope, element, options) : true;
1480               return $q.when(ret).then(function() {
1481                 if (!options.preserveScope) options.scope.$destroy();
1482                 removeDone = true;
1483               });
1484             });
1485           }
1486         };
1487       }
1488     };
1489
1490     /*
1491      * Replace `{{` and `}}` in a string (usually a template) with the actual start-/endSymbols used
1492      * for interpolation. This allows pre-defined templates (for components such as dialog, toast etc)
1493      * to continue to work in apps that use custom interpolation start-/endSymbols.
1494      *
1495      * @param {string} text The text in which to replace `{{` / `}}`
1496      * @returns {string} The modified string using the actual interpolation start-/endSymbols
1497      */
1498     function replaceInterpolationSymbols(text) {
1499       if (!text || !angular.isString(text)) return text;
1500       return text.replace(/\{\{/g, startSymbol).replace(/}}/g, endSymbol);
1501     }
1502   }
1503
1504 }
1505
1506   var HANDLERS = {};
1507   /* The state of the current 'pointer'
1508    * The pointer represents the state of the current touch.
1509    * It contains normalized x and y coordinates from DOM events,
1510    * as well as other information abstracted from the DOM.
1511    */
1512   var pointer, lastPointer, forceSkipClickHijack = false;
1513
1514   // Used to attach event listeners once when multiple ng-apps are running.
1515   var isInitialized = false;
1516   
1517   angular
1518     .module('material.core.gestures', [ ])
1519     .provider('$mdGesture', MdGestureProvider)
1520     .factory('$$MdGestureHandler', MdGestureHandler)
1521     .run( attachToDocument );
1522
1523   /**
1524      * @ngdoc service
1525      * @name $mdGestureProvider
1526      * @module material.core.gestures
1527      *
1528      * @description
1529      * In some scenarios on Mobile devices (without jQuery), the click events should NOT be hijacked.
1530      * `$mdGestureProvider` is used to configure the Gesture module to ignore or skip click hijacking on mobile
1531      * devices.
1532      *
1533      * <hljs lang="js">
1534      *   app.config(function($mdGestureProvider) {
1535      *
1536      *     // For mobile devices without jQuery loaded, do not
1537      *     // intercept click events during the capture phase.
1538      *     $mdGestureProvider.skipClickHijack();
1539      *
1540      *   });
1541      * </hljs>
1542      *
1543      */
1544   function MdGestureProvider() { }
1545
1546   MdGestureProvider.prototype = {
1547
1548     // Publish access to setter to configure a variable  BEFORE the
1549     // $mdGesture service is instantiated...
1550     skipClickHijack: function() {
1551       return forceSkipClickHijack = true;
1552     },
1553
1554     /**
1555      * $get is used to build an instance of $mdGesture
1556      * ngInject
1557      */
1558     $get : ["$$MdGestureHandler", "$$rAF", "$timeout", function($$MdGestureHandler, $$rAF, $timeout) {
1559          return new MdGesture($$MdGestureHandler, $$rAF, $timeout);
1560     }]
1561   };
1562
1563
1564
1565   /**
1566    * MdGesture factory construction function
1567    * ngInject
1568    */
1569   function MdGesture($$MdGestureHandler, $$rAF, $timeout) {
1570     var userAgent = navigator.userAgent || navigator.vendor || window.opera;
1571     var isIos = userAgent.match(/ipad|iphone|ipod/i);
1572     var isAndroid = userAgent.match(/android/i);
1573     var hasJQuery =  (typeof window.jQuery !== 'undefined') && (angular.element === window.jQuery);
1574
1575     var self = {
1576       handler: addHandler,
1577       register: register,
1578       // On mobile w/out jQuery, we normally intercept clicks. Should we skip that?
1579       isHijackingClicks: (isIos || isAndroid) && !hasJQuery && !forceSkipClickHijack
1580     };
1581
1582     if (self.isHijackingClicks) {
1583       self.handler('click', {
1584         options: {
1585           maxDistance: 6
1586         },
1587         onEnd: function (ev, pointer) {
1588           if (pointer.distance < this.state.options.maxDistance) {
1589             this.dispatchEvent(ev, 'click');
1590           }
1591         }
1592       });
1593     }
1594
1595     /*
1596      * Register an element to listen for a handler.
1597      * This allows an element to override the default options for a handler.
1598      * Additionally, some handlers like drag and hold only dispatch events if
1599      * the domEvent happens inside an element that's registered to listen for these events.
1600      *
1601      * @see GestureHandler for how overriding of default options works.
1602      * @example $mdGesture.register(myElement, 'drag', { minDistance: 20, horziontal: false })
1603      */
1604     function register(element, handlerName, options) {
1605       var handler = HANDLERS[handlerName.replace(/^\$md./, '')];
1606       if (!handler) {
1607         throw new Error('Failed to register element with handler ' + handlerName + '. ' +
1608         'Available handlers: ' + Object.keys(HANDLERS).join(', '));
1609       }
1610       return handler.registerElement(element, options);
1611     }
1612
1613     /*
1614      * add a handler to $mdGesture. see below.
1615      */
1616     function addHandler(name, definition) {
1617       var handler = new $$MdGestureHandler(name);
1618       angular.extend(handler, definition);
1619       HANDLERS[name] = handler;
1620
1621       return self;
1622     }
1623
1624     /*
1625      * Register handlers. These listen to touch/start/move events, interpret them,
1626      * and dispatch gesture events depending on options & conditions. These are all
1627      * instances of GestureHandler.
1628      * @see GestureHandler 
1629      */
1630     return self
1631       /*
1632        * The press handler dispatches an event on touchdown/touchend.
1633        * It's a simple abstraction of touch/mouse/pointer start and end.
1634        */
1635       .handler('press', {
1636         onStart: function (ev, pointer) {
1637           this.dispatchEvent(ev, '$md.pressdown');
1638         },
1639         onEnd: function (ev, pointer) {
1640           this.dispatchEvent(ev, '$md.pressup');
1641         }
1642       })
1643
1644       /*
1645        * The hold handler dispatches an event if the user keeps their finger within
1646        * the same <maxDistance> area for <delay> ms.
1647        * The hold handler will only run if a parent of the touch target is registered
1648        * to listen for hold events through $mdGesture.register()
1649        */
1650       .handler('hold', {
1651         options: {
1652           maxDistance: 6,
1653           delay: 500
1654         },
1655         onCancel: function () {
1656           $timeout.cancel(this.state.timeout);
1657         },
1658         onStart: function (ev, pointer) {
1659           // For hold, require a parent to be registered with $mdGesture.register()
1660           // Because we prevent scroll events, this is necessary.
1661           if (!this.state.registeredParent) return this.cancel();
1662
1663           this.state.pos = {x: pointer.x, y: pointer.y};
1664           this.state.timeout = $timeout(angular.bind(this, function holdDelayFn() {
1665             this.dispatchEvent(ev, '$md.hold');
1666             this.cancel(); //we're done!
1667           }), this.state.options.delay, false);
1668         },
1669         onMove: function (ev, pointer) {
1670           // Don't scroll while waiting for hold.
1671           // If we don't preventDefault touchmove events here, Android will assume we don't
1672           // want to listen to anymore touch events. It will start scrolling and stop sending
1673           // touchmove events.
1674           ev.preventDefault();
1675
1676           // If the user moves greater than <maxDistance> pixels, stop the hold timer
1677           // set in onStart
1678           var dx = this.state.pos.x - pointer.x;
1679           var dy = this.state.pos.y - pointer.y;
1680           if (Math.sqrt(dx * dx + dy * dy) > this.options.maxDistance) {
1681             this.cancel();
1682           }
1683         },
1684         onEnd: function () {
1685           this.onCancel();
1686         }
1687       })
1688
1689       /*
1690        * The drag handler dispatches a drag event if the user holds and moves his finger greater than
1691        * <minDistance> px in the x or y direction, depending on options.horizontal.
1692        * The drag will be cancelled if the user moves his finger greater than <minDistance>*<cancelMultiplier> in
1693        * the perpindicular direction. Eg if the drag is horizontal and the user moves his finger <minDistance>*<cancelMultiplier>
1694        * pixels vertically, this handler won't consider the move part of a drag.
1695        */
1696       .handler('drag', {
1697         options: {
1698           minDistance: 6,
1699           horizontal: true,
1700           cancelMultiplier: 1.5
1701         },
1702         onStart: function (ev) {
1703           // For drag, require a parent to be registered with $mdGesture.register()
1704           if (!this.state.registeredParent) this.cancel();
1705         },
1706         onMove: function (ev, pointer) {
1707           var shouldStartDrag, shouldCancel;
1708           // Don't scroll while deciding if this touchmove qualifies as a drag event.
1709           // If we don't preventDefault touchmove events here, Android will assume we don't
1710           // want to listen to anymore touch events. It will start scrolling and stop sending
1711           // touchmove events.
1712           ev.preventDefault();
1713
1714           if (!this.state.dragPointer) {
1715             if (this.state.options.horizontal) {
1716               shouldStartDrag = Math.abs(pointer.distanceX) > this.state.options.minDistance;
1717               shouldCancel = Math.abs(pointer.distanceY) > this.state.options.minDistance * this.state.options.cancelMultiplier;
1718             } else {
1719               shouldStartDrag = Math.abs(pointer.distanceY) > this.state.options.minDistance;
1720               shouldCancel = Math.abs(pointer.distanceX) > this.state.options.minDistance * this.state.options.cancelMultiplier;
1721             }
1722
1723             if (shouldStartDrag) {
1724               // Create a new pointer representing this drag, starting at this point where the drag started.
1725               this.state.dragPointer = makeStartPointer(ev);
1726               updatePointerState(ev, this.state.dragPointer);
1727               this.dispatchEvent(ev, '$md.dragstart', this.state.dragPointer);
1728
1729             } else if (shouldCancel) {
1730               this.cancel();
1731             }
1732           } else {
1733             this.dispatchDragMove(ev);
1734           }
1735         },
1736         // Only dispatch dragmove events every frame; any more is unnecessray
1737         dispatchDragMove: $$rAF.throttle(function (ev) {
1738           // Make sure the drag didn't stop while waiting for the next frame
1739           if (this.state.isRunning) {
1740             updatePointerState(ev, this.state.dragPointer);
1741             this.dispatchEvent(ev, '$md.drag', this.state.dragPointer);
1742           }
1743         }),
1744         onEnd: function (ev, pointer) {
1745           if (this.state.dragPointer) {
1746             updatePointerState(ev, this.state.dragPointer);
1747             this.dispatchEvent(ev, '$md.dragend', this.state.dragPointer);
1748           }
1749         }
1750       })
1751
1752       /*
1753        * The swipe handler will dispatch a swipe event if, on the end of a touch,
1754        * the velocity and distance were high enough.
1755        * TODO: add vertical swiping with a `horizontal` option similar to the drag handler.
1756        */
1757       .handler('swipe', {
1758         options: {
1759           minVelocity: 0.65,
1760           minDistance: 10
1761         },
1762         onEnd: function (ev, pointer) {
1763           if (Math.abs(pointer.velocityX) > this.state.options.minVelocity &&
1764             Math.abs(pointer.distanceX) > this.state.options.minDistance) {
1765             var eventType = pointer.directionX == 'left' ? '$md.swipeleft' : '$md.swiperight';
1766             this.dispatchEvent(ev, eventType);
1767           }
1768         }
1769       });
1770
1771   }
1772   MdGesture.$inject = ["$$MdGestureHandler", "$$rAF", "$timeout"];
1773
1774   /**
1775    * MdGestureHandler
1776    * A GestureHandler is an object which is able to dispatch custom dom events
1777    * based on native dom {touch,pointer,mouse}{start,move,end} events.
1778    *
1779    * A gesture will manage its lifecycle through the start,move,end, and cancel
1780    * functions, which are called by native dom events.
1781    *
1782    * A gesture has the concept of 'options' (eg a swipe's required velocity), which can be
1783    * overridden by elements registering through $mdGesture.register()
1784    */
1785   function GestureHandler (name) {
1786     this.name = name;
1787     this.state = {};
1788   }
1789
1790   function MdGestureHandler() {
1791     var hasJQuery =  (typeof window.jQuery !== 'undefined') && (angular.element === window.jQuery);
1792
1793     GestureHandler.prototype = {
1794       options: {},
1795       // jQuery listeners don't work with custom DOMEvents, so we have to dispatch events
1796       // differently when jQuery is loaded
1797       dispatchEvent: hasJQuery ?  jQueryDispatchEvent : nativeDispatchEvent,
1798
1799       // These are overridden by the registered handler
1800       onStart: angular.noop,
1801       onMove: angular.noop,
1802       onEnd: angular.noop,
1803       onCancel: angular.noop,
1804
1805       // onStart sets up a new state for the handler, which includes options from the
1806       // nearest registered parent element of ev.target.
1807       start: function (ev, pointer) {
1808         if (this.state.isRunning) return;
1809         var parentTarget = this.getNearestParent(ev.target);
1810         // Get the options from the nearest registered parent
1811         var parentTargetOptions = parentTarget && parentTarget.$mdGesture[this.name] || {};
1812
1813         this.state = {
1814           isRunning: true,
1815           // Override the default options with the nearest registered parent's options
1816           options: angular.extend({}, this.options, parentTargetOptions),
1817           // Pass in the registered parent node to the state so the onStart listener can use
1818           registeredParent: parentTarget
1819         };
1820         this.onStart(ev, pointer);
1821       },
1822       move: function (ev, pointer) {
1823         if (!this.state.isRunning) return;
1824         this.onMove(ev, pointer);
1825       },
1826       end: function (ev, pointer) {
1827         if (!this.state.isRunning) return;
1828         this.onEnd(ev, pointer);
1829         this.state.isRunning = false;
1830       },
1831       cancel: function (ev, pointer) {
1832         this.onCancel(ev, pointer);
1833         this.state = {};
1834       },
1835
1836       // Find and return the nearest parent element that has been registered to
1837       // listen for this handler via $mdGesture.register(element, 'handlerName').
1838       getNearestParent: function (node) {
1839         var current = node;
1840         while (current) {
1841           if ((current.$mdGesture || {})[this.name]) {
1842             return current;
1843           }
1844           current = current.parentNode;
1845         }
1846         return null;
1847       },
1848
1849       // Called from $mdGesture.register when an element reigsters itself with a handler.
1850       // Store the options the user gave on the DOMElement itself. These options will
1851       // be retrieved with getNearestParent when the handler starts.
1852       registerElement: function (element, options) {
1853         var self = this;
1854         element[0].$mdGesture = element[0].$mdGesture || {};
1855         element[0].$mdGesture[this.name] = options || {};
1856         element.on('$destroy', onDestroy);
1857
1858         return onDestroy;
1859
1860         function onDestroy() {
1861           delete element[0].$mdGesture[self.name];
1862           element.off('$destroy', onDestroy);
1863         }
1864       }
1865     };
1866
1867     return GestureHandler;
1868
1869     /*
1870      * Dispatch an event with jQuery
1871      * TODO: Make sure this sends bubbling events
1872      *
1873      * @param srcEvent the original DOM touch event that started this.
1874      * @param eventType the name of the custom event to send (eg 'click' or '$md.drag')
1875      * @param eventPointer the pointer object that matches this event.
1876      */
1877     function jQueryDispatchEvent(srcEvent, eventType, eventPointer) {
1878       eventPointer = eventPointer || pointer;
1879       var eventObj = new angular.element.Event(eventType);
1880
1881       eventObj.$material = true;
1882       eventObj.pointer = eventPointer;
1883       eventObj.srcEvent = srcEvent;
1884
1885       angular.extend(eventObj, {
1886         clientX: eventPointer.x,
1887         clientY: eventPointer.y,
1888         screenX: eventPointer.x,
1889         screenY: eventPointer.y,
1890         pageX: eventPointer.x,
1891         pageY: eventPointer.y,
1892         ctrlKey: srcEvent.ctrlKey,
1893         altKey: srcEvent.altKey,
1894         shiftKey: srcEvent.shiftKey,
1895         metaKey: srcEvent.metaKey
1896       });
1897       angular.element(eventPointer.target).trigger(eventObj);
1898     }
1899
1900     /*
1901      * NOTE: nativeDispatchEvent is very performance sensitive.
1902      * @param srcEvent the original DOM touch event that started this.
1903      * @param eventType the name of the custom event to send (eg 'click' or '$md.drag')
1904      * @param eventPointer the pointer object that matches this event.
1905      */
1906     function nativeDispatchEvent(srcEvent, eventType, eventPointer) {
1907       eventPointer = eventPointer || pointer;
1908       var eventObj;
1909
1910       if (eventType === 'click') {
1911         eventObj = document.createEvent('MouseEvents');
1912         eventObj.initMouseEvent(
1913           'click', true, true, window, srcEvent.detail,
1914           eventPointer.x, eventPointer.y, eventPointer.x, eventPointer.y,
1915           srcEvent.ctrlKey, srcEvent.altKey, srcEvent.shiftKey, srcEvent.metaKey,
1916           srcEvent.button, srcEvent.relatedTarget || null
1917         );
1918
1919       } else {
1920         eventObj = document.createEvent('CustomEvent');
1921         eventObj.initCustomEvent(eventType, true, true, {});
1922       }
1923       eventObj.$material = true;
1924       eventObj.pointer = eventPointer;
1925       eventObj.srcEvent = srcEvent;
1926       eventPointer.target.dispatchEvent(eventObj);
1927     }
1928
1929   }
1930
1931   /**
1932    * Attach Gestures: hook document and check shouldHijack clicks
1933    * ngInject
1934    */
1935   function attachToDocument( $mdGesture, $$MdGestureHandler ) {
1936
1937     // Polyfill document.contains for IE11.
1938     // TODO: move to util
1939     document.contains || (document.contains = function (node) {
1940       return document.body.contains(node);
1941     });
1942
1943     if (!isInitialized && $mdGesture.isHijackingClicks ) {
1944       /*
1945        * If hijack clicks is true, we preventDefault any click that wasn't
1946        * sent by ngMaterial. This is because on older Android & iOS, a false, or 'ghost',
1947        * click event will be sent ~400ms after a touchend event happens.
1948        * The only way to know if this click is real is to prevent any normal
1949        * click events, and add a flag to events sent by material so we know not to prevent those.
1950        * 
1951        * Two exceptions to click events that should be prevented are:
1952        *  - click events sent by the keyboard (eg form submit)
1953        *  - events that originate from an Ionic app
1954        */
1955       document.addEventListener('click', function clickHijacker(ev) {
1956         var isKeyClick = ev.clientX === 0 && ev.clientY === 0;
1957         if (!isKeyClick && !ev.$material && !ev.isIonicTap) {
1958           ev.preventDefault();
1959           ev.stopPropagation();
1960         }
1961       }, true);
1962       
1963       isInitialized = true;
1964     }
1965
1966     // Listen to all events to cover all platforms.
1967     var START_EVENTS = 'mousedown touchstart pointerdown';
1968     var MOVE_EVENTS = 'mousemove touchmove pointermove';
1969     var END_EVENTS = 'mouseup mouseleave touchend touchcancel pointerup pointercancel';
1970
1971     angular.element(document)
1972       .on(START_EVENTS, gestureStart)
1973       .on(MOVE_EVENTS, gestureMove)
1974       .on(END_EVENTS, gestureEnd)
1975       // For testing
1976       .on('$$mdGestureReset', function gestureClearCache () {
1977         lastPointer = pointer = null;
1978       });
1979
1980     /*
1981      * When a DOM event happens, run all registered gesture handlers' lifecycle
1982      * methods which match the DOM event.
1983      * Eg when a 'touchstart' event happens, runHandlers('start') will call and
1984      * run `handler.cancel()` and `handler.start()` on all registered handlers.
1985      */
1986     function runHandlers(handlerEvent, event) {
1987       var handler;
1988       for (var name in HANDLERS) {
1989         handler = HANDLERS[name];
1990         if( handler instanceof $$MdGestureHandler ) {
1991
1992           if (handlerEvent === 'start') {
1993             // Run cancel to reset any handlers' state
1994             handler.cancel();
1995           }
1996           handler[handlerEvent](event, pointer);
1997
1998         }
1999       }
2000     }
2001
2002     /*
2003      * gestureStart vets if a start event is legitimate (and not part of a 'ghost click' from iOS/Android)
2004      * If it is legitimate, we initiate the pointer state and mark the current pointer's type
2005      * For example, for a touchstart event, mark the current pointer as a 'touch' pointer, so mouse events
2006      * won't effect it.
2007      */
2008     function gestureStart(ev) {
2009       // If we're already touched down, abort
2010       if (pointer) return;
2011
2012       var now = +Date.now();
2013
2014       // iOS & old android bug: after a touch event, a click event is sent 350 ms later.
2015       // If <400ms have passed, don't allow an event of a different type than the previous event
2016       if (lastPointer && !typesMatch(ev, lastPointer) && (now - lastPointer.endTime < 1500)) {
2017         return;
2018       }
2019
2020       pointer = makeStartPointer(ev);
2021
2022       runHandlers('start', ev);
2023     }
2024     /*
2025      * If a move event happens of the right type, update the pointer and run all the move handlers.
2026      * "of the right type": if a mousemove happens but our pointer started with a touch event, do nothing.
2027      */
2028     function gestureMove(ev) {
2029       if (!pointer || !typesMatch(ev, pointer)) return;
2030
2031       updatePointerState(ev, pointer);
2032       runHandlers('move', ev);
2033     }
2034     /*
2035      * If an end event happens of the right type, update the pointer, run endHandlers, and save the pointer as 'lastPointer'
2036      */
2037     function gestureEnd(ev) {
2038       if (!pointer || !typesMatch(ev, pointer)) return;
2039
2040       updatePointerState(ev, pointer);
2041       pointer.endTime = +Date.now();
2042
2043       runHandlers('end', ev);
2044
2045       lastPointer = pointer;
2046       pointer = null;
2047     }
2048
2049   }
2050   attachToDocument.$inject = ["$mdGesture", "$$MdGestureHandler"];
2051
2052   // ********************
2053   // Module Functions
2054   // ********************
2055
2056   /*
2057    * Initiate the pointer. x, y, and the pointer's type.
2058    */
2059   function makeStartPointer(ev) {
2060     var point = getEventPoint(ev);
2061     var startPointer = {
2062       startTime: +Date.now(),
2063       target: ev.target,
2064       // 'p' for pointer events, 'm' for mouse, 't' for touch
2065       type: ev.type.charAt(0)
2066     };
2067     startPointer.startX = startPointer.x = point.pageX;
2068     startPointer.startY = startPointer.y = point.pageY;
2069     return startPointer;
2070   }
2071
2072   /*
2073    * return whether the pointer's type matches the event's type.
2074    * Eg if a touch event happens but the pointer has a mouse type, return false.
2075    */
2076   function typesMatch(ev, pointer) {
2077     return ev && pointer && ev.type.charAt(0) === pointer.type;
2078   }
2079
2080   /*
2081    * Update the given pointer based upon the given DOMEvent.
2082    * Distance, velocity, direction, duration, etc
2083    */
2084   function updatePointerState(ev, pointer) {
2085     var point = getEventPoint(ev);
2086     var x = pointer.x = point.pageX;
2087     var y = pointer.y = point.pageY;
2088
2089     pointer.distanceX = x - pointer.startX;
2090     pointer.distanceY = y - pointer.startY;
2091     pointer.distance = Math.sqrt(
2092       pointer.distanceX * pointer.distanceX + pointer.distanceY * pointer.distanceY
2093     );
2094
2095     pointer.directionX = pointer.distanceX > 0 ? 'right' : pointer.distanceX < 0 ? 'left' : '';
2096     pointer.directionY = pointer.distanceY > 0 ? 'up' : pointer.distanceY < 0 ? 'down' : '';
2097
2098     pointer.duration = +Date.now() - pointer.startTime;
2099     pointer.velocityX = pointer.distanceX / pointer.duration;
2100     pointer.velocityY = pointer.distanceY / pointer.duration;
2101   }
2102
2103   /*
2104    * Normalize the point where the DOM event happened whether it's touch or mouse.
2105    * @returns point event obj with pageX and pageY on it.
2106    */
2107   function getEventPoint(ev) {
2108     ev = ev.originalEvent || ev; // support jQuery events
2109     return (ev.touches && ev.touches[0]) ||
2110       (ev.changedTouches && ev.changedTouches[0]) ||
2111       ev;
2112   }
2113
2114   /**
2115    * @ngdoc module
2116    * @name material.core.componentRegistry
2117    *
2118    * @description
2119    * A component instance registration service.
2120    * Note: currently this as a private service in the SideNav component.
2121    */
2122   angular.module('material.core')
2123     .factory('$mdComponentRegistry', ComponentRegistry);
2124
2125   /*
2126    * @private
2127    * @ngdoc factory
2128    * @name ComponentRegistry
2129    * @module material.core.componentRegistry
2130    *
2131    */
2132   function ComponentRegistry($log, $q) {
2133
2134     var self;
2135     var instances = [ ];
2136     var pendings = { };
2137
2138     return self = {
2139       /**
2140        * Used to print an error when an instance for a handle isn't found.
2141        */
2142       notFoundError: function(handle) {
2143         $log.error('No instance found for handle', handle);
2144       },
2145       /**
2146        * Return all registered instances as an array.
2147        */
2148       getInstances: function() {
2149         return instances;
2150       },
2151
2152       /**
2153        * Get a registered instance.
2154        * @param handle the String handle to look up for a registered instance.
2155        */
2156       get: function(handle) {
2157         if ( !isValidID(handle) ) return null;
2158
2159         var i, j, instance;
2160         for(i = 0, j = instances.length; i < j; i++) {
2161           instance = instances[i];
2162           if(instance.$$mdHandle === handle) {
2163             return instance;
2164           }
2165         }
2166         return null;
2167       },
2168
2169       /**
2170        * Register an instance.
2171        * @param instance the instance to register
2172        * @param handle the handle to identify the instance under.
2173        */
2174       register: function(instance, handle) {
2175         if ( !handle ) return angular.noop;
2176
2177         instance.$$mdHandle = handle;
2178         instances.push(instance);
2179         resolveWhen();
2180
2181         return deregister;
2182
2183         /**
2184          * Remove registration for an instance
2185          */
2186         function deregister() {
2187           var index = instances.indexOf(instance);
2188           if (index !== -1) {
2189             instances.splice(index, 1);
2190           }
2191         }
2192
2193         /**
2194          * Resolve any pending promises for this instance
2195          */
2196         function resolveWhen() {
2197           var dfd = pendings[handle];
2198           if ( dfd ) {
2199             dfd.resolve( instance );
2200             delete pendings[handle];
2201           }
2202         }
2203       },
2204
2205       /**
2206        * Async accessor to registered component instance
2207        * If not available then a promise is created to notify
2208        * all listeners when the instance is registered.
2209        */
2210       when : function(handle) {
2211         if ( isValidID(handle) ) {
2212           var deferred = $q.defer();
2213           var instance = self.get(handle);
2214
2215           if ( instance )  {
2216             deferred.resolve( instance );
2217           } else {
2218             pendings[handle] = deferred;
2219           }
2220
2221           return deferred.promise;
2222         }
2223         return $q.reject("Invalid `md-component-id` value.");
2224       }
2225
2226     };
2227
2228     function isValidID(handle){
2229       return handle && (handle !== "");
2230     }
2231
2232   }
2233   ComponentRegistry.$inject = ["$log", "$q"];
2234
2235 (function() {
2236   'use strict';
2237
2238   /**
2239    * @ngdoc service
2240    * @name $mdButtonInkRipple
2241    * @module material.core
2242    *
2243    * @description
2244    * Provides ripple effects for md-button.  See $mdInkRipple service for all possible configuration options.
2245    *
2246    * @param {object=} scope Scope within the current context
2247    * @param {object=} element The element the ripple effect should be applied to
2248    * @param {object=} options (Optional) Configuration options to override the defaultripple configuration
2249    */
2250
2251   angular.module('material.core')
2252     .factory('$mdButtonInkRipple', MdButtonInkRipple);
2253
2254   function MdButtonInkRipple($mdInkRipple) {
2255     return {
2256       attach: attach
2257     };
2258
2259     function attach(scope, element, options) {
2260       var elementOptions = optionsForElement(element);
2261       return $mdInkRipple.attach(scope, element, angular.extend(elementOptions, options));
2262     };
2263
2264     function optionsForElement(element) {
2265       if (element.hasClass('md-icon-button')) {
2266         return {
2267           isMenuItem: element.hasClass('md-menu-item'),
2268           fitRipple: true,
2269           center: true
2270         };
2271       } else {
2272         return {
2273           isMenuItem: element.hasClass('md-menu-item'),
2274           dimBackground: true
2275         }
2276       }
2277     };
2278   }
2279   MdButtonInkRipple.$inject = ["$mdInkRipple"];;
2280 })();
2281
2282 (function() {
2283   'use strict';
2284
2285     /**
2286    * @ngdoc service
2287    * @name $mdCheckboxInkRipple
2288    * @module material.core
2289    *
2290    * @description
2291    * Provides ripple effects for md-checkbox.  See $mdInkRipple service for all possible configuration options.
2292    *
2293    * @param {object=} scope Scope within the current context
2294    * @param {object=} element The element the ripple effect should be applied to
2295    * @param {object=} options (Optional) Configuration options to override the defaultripple configuration
2296    */
2297
2298   angular.module('material.core')
2299     .factory('$mdCheckboxInkRipple', MdCheckboxInkRipple);
2300
2301   function MdCheckboxInkRipple($mdInkRipple) {
2302     return {
2303       attach: attach
2304     };
2305
2306     function attach(scope, element, options) {
2307       return $mdInkRipple.attach(scope, element, angular.extend({
2308         center: true,
2309         dimBackground: false,
2310         fitRipple: true
2311       }, options));
2312     };
2313   }
2314   MdCheckboxInkRipple.$inject = ["$mdInkRipple"];;
2315 })();
2316
2317 (function() {
2318   'use strict';
2319
2320   /**
2321    * @ngdoc service
2322    * @name $mdListInkRipple
2323    * @module material.core
2324    *
2325    * @description
2326    * Provides ripple effects for md-list.  See $mdInkRipple service for all possible configuration options.
2327    *
2328    * @param {object=} scope Scope within the current context
2329    * @param {object=} element The element the ripple effect should be applied to
2330    * @param {object=} options (Optional) Configuration options to override the defaultripple configuration
2331    */
2332
2333   angular.module('material.core')
2334     .factory('$mdListInkRipple', MdListInkRipple);
2335
2336   function MdListInkRipple($mdInkRipple) {
2337     return {
2338       attach: attach
2339     };
2340
2341     function attach(scope, element, options) {
2342       return $mdInkRipple.attach(scope, element, angular.extend({
2343         center: false,
2344         dimBackground: true,
2345         outline: false,
2346         rippleSize: 'full'
2347       }, options));
2348     };
2349   }
2350   MdListInkRipple.$inject = ["$mdInkRipple"];;
2351 })();
2352
2353 angular.module('material.core')
2354   .factory('$mdInkRipple', InkRippleService)
2355   .directive('mdInkRipple', InkRippleDirective)
2356   .directive('mdNoInk', attrNoDirective())
2357   .directive('mdNoBar', attrNoDirective())
2358   .directive('mdNoStretch', attrNoDirective());
2359
2360 function InkRippleDirective($mdButtonInkRipple, $mdCheckboxInkRipple) {
2361   return {
2362     controller: angular.noop,
2363     link: function (scope, element, attr) {
2364       if (attr.hasOwnProperty('mdInkRippleCheckbox')) {
2365         $mdCheckboxInkRipple.attach(scope, element);
2366       } else {
2367         $mdButtonInkRipple.attach(scope, element);
2368       }
2369     }
2370   };
2371 }
2372 InkRippleDirective.$inject = ["$mdButtonInkRipple", "$mdCheckboxInkRipple"];
2373
2374 function InkRippleService($window, $timeout) {
2375
2376   return {
2377     attach: attach
2378   };
2379
2380   function attach(scope, element, options) {
2381     if (element.controller('mdNoInk')) return angular.noop;
2382
2383     options = angular.extend({
2384       colorElement: element,
2385       mousedown: true,
2386       hover: true,
2387       focus: true,
2388       center: false,
2389       mousedownPauseTime: 150,
2390       dimBackground: false,
2391       outline: false,
2392       fullRipple: true,
2393       isMenuItem: false,
2394       fitRipple: false
2395     }, options);
2396
2397     var rippleSize,
2398         controller = element.controller('mdInkRipple') || {},
2399         counter = 0,
2400         ripples = [],
2401         states = [],
2402         isActiveExpr = element.attr('md-highlight'),
2403         isActive = false,
2404         isHeld = false,
2405         node = element[0],
2406         rippleSizeSetting = element.attr('md-ripple-size'),
2407         color = parseColor(element.attr('md-ink-ripple')) || parseColor(options.colorElement.length && $window.getComputedStyle(options.colorElement[0]).color || 'rgb(0, 0, 0)');
2408
2409     switch (rippleSizeSetting) {
2410       case 'full':
2411         options.fullRipple = true;
2412         break;
2413       case 'partial':
2414         options.fullRipple = false;
2415         break;
2416     }
2417
2418     // expose onInput for ripple testing
2419     if (options.mousedown) {
2420       element.on('$md.pressdown', onPressDown)
2421         .on('$md.pressup', onPressUp);
2422     }
2423
2424     controller.createRipple = createRipple;
2425
2426     if (isActiveExpr) {
2427       scope.$watch(isActiveExpr, function watchActive(newValue) {
2428         isActive = newValue;
2429         if (isActive && !ripples.length) {
2430           $timeout(function () { createRipple(0, 0); }, 0, false);
2431         }
2432         angular.forEach(ripples, updateElement);
2433       });
2434     }
2435
2436     // Publish self-detach method if desired...
2437     return function detach() {
2438       element.off('$md.pressdown', onPressDown)
2439         .off('$md.pressup', onPressUp);
2440       getRippleContainer().remove();
2441     };
2442
2443     /**
2444      * Gets the current ripple container
2445      * If there is no ripple container, it creates one and returns it
2446      *
2447      * @returns {angular.element} ripple container element
2448      */
2449     function getRippleContainer() {
2450       var container = element.data('$mdRippleContainer');
2451       if (container) return container;
2452       container = angular.element('<div class="md-ripple-container">');
2453       element.append(container);
2454       element.data('$mdRippleContainer', container);
2455       return container;
2456     }
2457
2458     function parseColor(color) {
2459       if (!color) return;
2460       if (color.indexOf('rgba') === 0) return color.replace(/\d?\.?\d*\s*\)\s*$/, '0.1)');
2461       if (color.indexOf('rgb')  === 0) return rgbToRGBA(color);
2462       if (color.indexOf('#')    === 0) return hexToRGBA(color);
2463
2464       /**
2465        * Converts a hex value to an rgba string
2466        *
2467        * @param {string} hex value (3 or 6 digits) to be converted
2468        *
2469        * @returns {string} rgba color with 0.1 alpha
2470        */
2471       function hexToRGBA(color) {
2472         var hex = color.charAt(0) === '#' ? color.substr(1) : color,
2473           dig = hex.length / 3,
2474           red = hex.substr(0, dig),
2475           grn = hex.substr(dig, dig),
2476           blu = hex.substr(dig * 2);
2477         if (dig === 1) {
2478           red += red;
2479           grn += grn;
2480           blu += blu;
2481         }
2482         return 'rgba(' + parseInt(red, 16) + ',' + parseInt(grn, 16) + ',' + parseInt(blu, 16) + ',0.1)';
2483       }
2484
2485       /**
2486        * Converts rgb value to rgba string
2487        *
2488        * @param {string} rgb color string
2489        *
2490        * @returns {string} rgba color with 0.1 alpha
2491        */
2492       function rgbToRGBA(color) {
2493         return color.replace(')', ', 0.1)').replace('(', 'a(');
2494       }
2495
2496     }
2497
2498     function removeElement(elem, wait) {
2499       ripples.splice(ripples.indexOf(elem), 1);
2500       if (ripples.length === 0) {
2501         getRippleContainer().css({ backgroundColor: '' });
2502       }
2503       $timeout(function () { elem.remove(); }, wait, false);
2504     }
2505
2506     function updateElement(elem) {
2507       var index = ripples.indexOf(elem),
2508           state = states[index] || {},
2509           elemIsActive = ripples.length > 1 ? false : isActive,
2510           elemIsHeld   = ripples.length > 1 ? false : isHeld;
2511       if (elemIsActive || state.animating || elemIsHeld) {
2512         elem.addClass('md-ripple-visible');
2513       } else if (elem) {
2514         elem.removeClass('md-ripple-visible');
2515         if (options.outline) {
2516           elem.css({
2517             width: rippleSize + 'px',
2518             height: rippleSize + 'px',
2519             marginLeft: (rippleSize * -1) + 'px',
2520             marginTop: (rippleSize * -1) + 'px'
2521           });
2522         }
2523         removeElement(elem, options.outline ? 450 : 650);
2524       }
2525     }
2526
2527     /**
2528      * Creates a ripple at the provided coordinates
2529      *
2530      * @param {number} left cursor position
2531      * @param {number} top cursor position
2532      *
2533      * @returns {angular.element} the generated ripple element
2534      */
2535     function createRipple(left, top) {
2536
2537       color = parseColor(element.attr('md-ink-ripple')) || parseColor($window.getComputedStyle(options.colorElement[0]).color || 'rgb(0, 0, 0)');
2538
2539       var container = getRippleContainer(),
2540           size = getRippleSize(left, top),
2541           css = getRippleCss(size, left, top),
2542           elem = getRippleElement(css),
2543           index = ripples.indexOf(elem),
2544           state = states[index] || {};
2545
2546       rippleSize = size;
2547
2548       state.animating = true;
2549
2550       $timeout(function () {
2551         if (options.dimBackground) {
2552           container.css({ backgroundColor: color });
2553         }
2554         elem.addClass('md-ripple-placed md-ripple-scaled');
2555         if (options.outline) {
2556           elem.css({
2557             borderWidth: (size * 0.5) + 'px',
2558             marginLeft: (size * -0.5) + 'px',
2559             marginTop: (size * -0.5) + 'px'
2560           });
2561         } else {
2562           elem.css({ left: '50%', top: '50%' });
2563         }
2564         updateElement(elem);
2565         $timeout(function () {
2566           state.animating = false;
2567           updateElement(elem);
2568         }, (options.outline ? 450 : 225), false);
2569       }, 0, false);
2570
2571       return elem;
2572
2573       /**
2574        * Creates the ripple element with the provided css
2575        *
2576        * @param {object} css properties to be applied
2577        *
2578        * @returns {angular.element} the generated ripple element
2579        */
2580       function getRippleElement(css) {
2581         var elem = angular.element('<div class="md-ripple" data-counter="' + counter++ + '">');
2582         ripples.unshift(elem);
2583         states.unshift({ animating: true });
2584         container.append(elem);
2585         css && elem.css(css);
2586         return elem;
2587       }
2588
2589       /**
2590        * Calculate the ripple size
2591        *
2592        * @returns {number} calculated ripple diameter
2593        */
2594       function getRippleSize(left, top) {
2595         var width = container.prop('offsetWidth'),
2596             height = container.prop('offsetHeight'),
2597             multiplier, size, rect;
2598         if (options.isMenuItem) {
2599           size = Math.sqrt(Math.pow(width, 2) + Math.pow(height, 2));
2600         } else if (options.outline) {
2601           rect = node.getBoundingClientRect();
2602           left -= rect.left;
2603           top -= rect.top;
2604           width = Math.max(left, width - left);
2605           height = Math.max(top, height - top);
2606           size = 2 * Math.sqrt(Math.pow(width, 2) + Math.pow(height, 2));
2607         } else {
2608           multiplier = options.fullRipple ? 1.1 : 0.8;
2609           size = Math.sqrt(Math.pow(width, 2) + Math.pow(height, 2)) * multiplier;
2610           if (options.fitRipple) {
2611             size = Math.min(height, width, size);
2612           }
2613         }
2614         return size;
2615       }
2616
2617       /**
2618        * Generates the ripple css
2619        *
2620        * @param {number} the diameter of the ripple
2621        * @param {number} the left cursor offset
2622        * @param {number} the top cursor offset
2623        *
2624        * @returns {{backgroundColor: string, borderColor: string, width: string, height: string}}
2625        */
2626       function getRippleCss(size, left, top) {
2627         var rect = node.getBoundingClientRect(),
2628             css  = {
2629               backgroundColor: rgbaToRGB(color),
2630               borderColor: rgbaToRGB(color),
2631               width: size + 'px',
2632               height: size + 'px'
2633             };
2634
2635         if (options.outline) {
2636           css.width = 0;
2637           css.height = 0;
2638         } else {
2639           css.marginLeft = css.marginTop = (size * -0.5) + 'px';
2640         }
2641
2642         if (options.center) {
2643           css.left = css.top = '50%';
2644         } else {
2645           css.left = Math.round((left - rect.left) / container.prop('offsetWidth') * 100) + '%';
2646           css.top = Math.round((top - rect.top) / container.prop('offsetHeight') * 100) + '%';
2647         }
2648
2649         return css;
2650
2651         /**
2652          * Converts rgba string to rgb, removing the alpha value
2653          *
2654          * @param {string} rgba color
2655          *
2656          * @returns {string} rgb color
2657          */
2658         function rgbaToRGB(color) {
2659           return color.replace('rgba', 'rgb').replace(/,[^\),]+\)/, ')');
2660         }
2661       }
2662     }
2663
2664     /**
2665      * Handles user input start and stop events
2666      *
2667      */
2668     function onPressDown(ev) {
2669       if (!isRippleAllowed()) return;
2670
2671       createRipple(ev.pointer.x, ev.pointer.y);
2672       isHeld = true;
2673     }
2674     function onPressUp() {
2675       isHeld = false;
2676       var ripple = ripples[ ripples.length - 1 ];
2677       $timeout(function () { updateElement(ripple); }, 0, false);
2678     }
2679
2680     /**
2681      * Determines if the ripple is allowed
2682      *
2683      * @returns {boolean} true if the ripple is allowed, false if not
2684      */
2685     function isRippleAllowed() {
2686       var parent = node.parentNode;
2687       var grandparent = parent && parent.parentNode;
2688       var ancestor = grandparent && grandparent.parentNode;
2689       return !isDisabled(node) && !isDisabled(parent) && !isDisabled(grandparent) && !isDisabled(ancestor);
2690       function isDisabled (elem) {
2691         return elem && elem.hasAttribute && elem.hasAttribute('disabled');
2692       }
2693     }
2694
2695   }
2696 }
2697 InkRippleService.$inject = ["$window", "$timeout"];
2698
2699 /**
2700  * noink/nobar/nostretch directive: make any element that has one of
2701  * these attributes be given a controller, so that other directives can
2702  * `require:` these and see if there is a `no<xxx>` parent attribute.
2703  *
2704  * @usage
2705  * <hljs lang="html">
2706  * <parent md-no-ink>
2707  *   <child detect-no>
2708  *   </child>
2709  * </parent>
2710  * </hljs>
2711  *
2712  * <hljs lang="js">
2713  * myApp.directive('detectNo', function() {
2714  *   return {
2715  *     require: ['^?mdNoInk', ^?mdNoBar'],
2716  *     link: function(scope, element, attr, ctrls) {
2717  *       var noinkCtrl = ctrls[0];
2718  *       var nobarCtrl = ctrls[1];
2719  *       if (noInkCtrl) {
2720  *         alert("the md-no-ink flag has been specified on an ancestor!");
2721  *       }
2722  *       if (nobarCtrl) {
2723  *         alert("the md-no-bar flag has been specified on an ancestor!");
2724  *       }
2725  *     }
2726  *   };
2727  * });
2728  * </hljs>
2729  */
2730 function attrNoDirective() {
2731   return function() {
2732     return {
2733       controller: angular.noop
2734     };
2735   };
2736 }
2737
2738 (function() {
2739   'use strict';
2740
2741     /**
2742    * @ngdoc service
2743    * @name $mdTabInkRipple
2744    * @module material.core
2745    *
2746    * @description
2747    * Provides ripple effects for md-tabs.  See $mdInkRipple service for all possible configuration options.
2748    *
2749    * @param {object=} scope Scope within the current context
2750    * @param {object=} element The element the ripple effect should be applied to
2751    * @param {object=} options (Optional) Configuration options to override the defaultripple configuration
2752    */
2753
2754   angular.module('material.core')
2755     .factory('$mdTabInkRipple', MdTabInkRipple);
2756
2757   function MdTabInkRipple($mdInkRipple) {
2758     return {
2759       attach: attach
2760     };
2761
2762     function attach(scope, element, options) {
2763       return $mdInkRipple.attach(scope, element, angular.extend({
2764         center: false,
2765         dimBackground: true,
2766         outline: false,
2767         rippleSize: 'full'
2768       }, options));
2769     };
2770   }
2771   MdTabInkRipple.$inject = ["$mdInkRipple"];;
2772 })();
2773
2774 angular.module('material.core.theming.palette', [])
2775 .constant('$mdColorPalette', {
2776   'red': {
2777     '50': '#ffebee',
2778     '100': '#ffcdd2',
2779     '200': '#ef9a9a',
2780     '300': '#e57373',
2781     '400': '#ef5350',
2782     '500': '#f44336',
2783     '600': '#e53935',
2784     '700': '#d32f2f',
2785     '800': '#c62828',
2786     '900': '#b71c1c',
2787     'A100': '#ff8a80',
2788     'A200': '#ff5252',
2789     'A400': '#ff1744',
2790     'A700': '#d50000',
2791     'contrastDefaultColor': 'light',
2792     'contrastDarkColors': '50 100 200 300 400 A100',
2793     'contrastStrongLightColors': '500 600 700 A200 A400 A700'
2794   },
2795   'pink': {
2796     '50': '#fce4ec',
2797     '100': '#f8bbd0',
2798     '200': '#f48fb1',
2799     '300': '#f06292',
2800     '400': '#ec407a',
2801     '500': '#e91e63',
2802     '600': '#d81b60',
2803     '700': '#c2185b',
2804     '800': '#ad1457',
2805     '900': '#880e4f',
2806     'A100': '#ff80ab',
2807     'A200': '#ff4081',
2808     'A400': '#f50057',
2809     'A700': '#c51162',
2810     'contrastDefaultColor': 'light',
2811     'contrastDarkColors': '50 100 200 300 400 A100',
2812     'contrastStrongLightColors': '500 600 A200 A400 A700'
2813   },
2814   'purple': {
2815     '50': '#f3e5f5',
2816     '100': '#e1bee7',
2817     '200': '#ce93d8',
2818     '300': '#ba68c8',
2819     '400': '#ab47bc',
2820     '500': '#9c27b0',
2821     '600': '#8e24aa',
2822     '700': '#7b1fa2',
2823     '800': '#6a1b9a',
2824     '900': '#4a148c',
2825     'A100': '#ea80fc',
2826     'A200': '#e040fb',
2827     'A400': '#d500f9',
2828     'A700': '#aa00ff',
2829     'contrastDefaultColor': 'light',
2830     'contrastDarkColors': '50 100 200 A100',
2831     'contrastStrongLightColors': '300 400 A200 A400 A700'
2832   },
2833   'deep-purple': {
2834     '50': '#ede7f6',
2835     '100': '#d1c4e9',
2836     '200': '#b39ddb',
2837     '300': '#9575cd',
2838     '400': '#7e57c2',
2839     '500': '#673ab7',
2840     '600': '#5e35b1',
2841     '700': '#512da8',
2842     '800': '#4527a0',
2843     '900': '#311b92',
2844     'A100': '#b388ff',
2845     'A200': '#7c4dff',
2846     'A400': '#651fff',
2847     'A700': '#6200ea',
2848     'contrastDefaultColor': 'light',
2849     'contrastDarkColors': '50 100 200 A100',
2850     'contrastStrongLightColors': '300 400 A200'
2851   },
2852   'indigo': {
2853     '50': '#e8eaf6',
2854     '100': '#c5cae9',
2855     '200': '#9fa8da',
2856     '300': '#7986cb',
2857     '400': '#5c6bc0',
2858     '500': '#3f51b5',
2859     '600': '#3949ab',
2860     '700': '#303f9f',
2861     '800': '#283593',
2862     '900': '#1a237e',
2863     'A100': '#8c9eff',
2864     'A200': '#536dfe',
2865     'A400': '#3d5afe',
2866     'A700': '#304ffe',
2867     'contrastDefaultColor': 'light',
2868     'contrastDarkColors': '50 100 200 A100',
2869     'contrastStrongLightColors': '300 400 A200 A400'
2870   },
2871   'blue': {
2872     '50': '#e3f2fd',
2873     '100': '#bbdefb',
2874     '200': '#90caf9',
2875     '300': '#64b5f6',
2876     '400': '#42a5f5',
2877     '500': '#2196f3',
2878     '600': '#1e88e5',
2879     '700': '#1976d2',
2880     '800': '#1565c0',
2881     '900': '#0d47a1',
2882     'A100': '#82b1ff',
2883     'A200': '#448aff',
2884     'A400': '#2979ff',
2885     'A700': '#2962ff',
2886     'contrastDefaultColor': 'light',
2887     'contrastDarkColors': '100 200 300 400 A100',
2888     'contrastStrongLightColors': '500 600 700 A200 A400 A700'
2889   },
2890   'light-blue': {
2891     '50': '#e1f5fe',
2892     '100': '#b3e5fc',
2893     '200': '#81d4fa',
2894     '300': '#4fc3f7',
2895     '400': '#29b6f6',
2896     '500': '#03a9f4',
2897     '600': '#039be5',
2898     '700': '#0288d1',
2899     '800': '#0277bd',
2900     '900': '#01579b',
2901     'A100': '#80d8ff',
2902     'A200': '#40c4ff',
2903     'A400': '#00b0ff',
2904     'A700': '#0091ea',
2905     'contrastDefaultColor': 'dark',
2906     'contrastLightColors': '500 600 700 800 900 A700',
2907     'contrastStrongLightColors': '500 600 700 800 A700'
2908   },
2909   'cyan': {
2910     '50': '#e0f7fa',
2911     '100': '#b2ebf2',
2912     '200': '#80deea',
2913     '300': '#4dd0e1',
2914     '400': '#26c6da',
2915     '500': '#00bcd4',
2916     '600': '#00acc1',
2917     '700': '#0097a7',
2918     '800': '#00838f',
2919     '900': '#006064',
2920     'A100': '#84ffff',
2921     'A200': '#18ffff',
2922     'A400': '#00e5ff',
2923     'A700': '#00b8d4',
2924     'contrastDefaultColor': 'dark',
2925     'contrastLightColors': '500 600 700 800 900',
2926     'contrastStrongLightColors': '500 600 700 800'
2927   },
2928   'teal': {
2929     '50': '#e0f2f1',
2930     '100': '#b2dfdb',
2931     '200': '#80cbc4',
2932     '300': '#4db6ac',
2933     '400': '#26a69a',
2934     '500': '#009688',
2935     '600': '#00897b',
2936     '700': '#00796b',
2937     '800': '#00695c',
2938     '900': '#004d40',
2939     'A100': '#a7ffeb',
2940     'A200': '#64ffda',
2941     'A400': '#1de9b6',
2942     'A700': '#00bfa5',
2943     'contrastDefaultColor': 'dark',
2944     'contrastLightColors': '500 600 700 800 900',
2945     'contrastStrongLightColors': '500 600 700'
2946   },
2947   'green': {
2948     '50': '#e8f5e9',
2949     '100': '#c8e6c9',
2950     '200': '#a5d6a7',
2951     '300': '#81c784',
2952     '400': '#66bb6a',
2953     '500': '#4caf50',
2954     '600': '#43a047',
2955     '700': '#388e3c',
2956     '800': '#2e7d32',
2957     '900': '#1b5e20',
2958     'A100': '#b9f6ca',
2959     'A200': '#69f0ae',
2960     'A400': '#00e676',
2961     'A700': '#00c853',
2962     'contrastDefaultColor': 'dark',
2963     'contrastLightColors': '500 600 700 800 900',
2964     'contrastStrongLightColors': '500 600 700'
2965   },
2966   'light-green': {
2967     '50': '#f1f8e9',
2968     '100': '#dcedc8',
2969     '200': '#c5e1a5',
2970     '300': '#aed581',
2971     '400': '#9ccc65',
2972     '500': '#8bc34a',
2973     '600': '#7cb342',
2974     '700': '#689f38',
2975     '800': '#558b2f',
2976     '900': '#33691e',
2977     'A100': '#ccff90',
2978     'A200': '#b2ff59',
2979     'A400': '#76ff03',
2980     'A700': '#64dd17',
2981     'contrastDefaultColor': 'dark',
2982     'contrastLightColors': '800 900',
2983     'contrastStrongLightColors': '800 900'
2984   },
2985   'lime': {
2986     '50': '#f9fbe7',
2987     '100': '#f0f4c3',
2988     '200': '#e6ee9c',
2989     '300': '#dce775',
2990     '400': '#d4e157',
2991     '500': '#cddc39',
2992     '600': '#c0ca33',
2993     '700': '#afb42b',
2994     '800': '#9e9d24',
2995     '900': '#827717',
2996     'A100': '#f4ff81',
2997     'A200': '#eeff41',
2998     'A400': '#c6ff00',
2999     'A700': '#aeea00',
3000     'contrastDefaultColor': 'dark',
3001     'contrastLightColors': '900',
3002     'contrastStrongLightColors': '900'
3003   },
3004   'yellow': {
3005     '50': '#fffde7',
3006     '100': '#fff9c4',
3007     '200': '#fff59d',
3008     '300': '#fff176',
3009     '400': '#ffee58',
3010     '500': '#ffeb3b',
3011     '600': '#fdd835',
3012     '700': '#fbc02d',
3013     '800': '#f9a825',
3014     '900': '#f57f17',
3015     'A100': '#ffff8d',
3016     'A200': '#ffff00',
3017     'A400': '#ffea00',
3018     'A700': '#ffd600',
3019     'contrastDefaultColor': 'dark'
3020   },
3021   'amber': {
3022     '50': '#fff8e1',
3023     '100': '#ffecb3',
3024     '200': '#ffe082',
3025     '300': '#ffd54f',
3026     '400': '#ffca28',
3027     '500': '#ffc107',
3028     '600': '#ffb300',
3029     '700': '#ffa000',
3030     '800': '#ff8f00',
3031     '900': '#ff6f00',
3032     'A100': '#ffe57f',
3033     'A200': '#ffd740',
3034     'A400': '#ffc400',
3035     'A700': '#ffab00',
3036     'contrastDefaultColor': 'dark'
3037   },
3038   'orange': {
3039     '50': '#fff3e0',
3040     '100': '#ffe0b2',
3041     '200': '#ffcc80',
3042     '300': '#ffb74d',
3043     '400': '#ffa726',
3044     '500': '#ff9800',
3045     '600': '#fb8c00',
3046     '700': '#f57c00',
3047     '800': '#ef6c00',
3048     '900': '#e65100',
3049     'A100': '#ffd180',
3050     'A200': '#ffab40',
3051     'A400': '#ff9100',
3052     'A700': '#ff6d00',
3053     'contrastDefaultColor': 'dark',
3054     'contrastLightColors': '800 900',
3055     'contrastStrongLightColors': '800 900'
3056   },
3057   'deep-orange': {
3058     '50': '#fbe9e7',
3059     '100': '#ffccbc',
3060     '200': '#ffab91',
3061     '300': '#ff8a65',
3062     '400': '#ff7043',
3063     '500': '#ff5722',
3064     '600': '#f4511e',
3065     '700': '#e64a19',
3066     '800': '#d84315',
3067     '900': '#bf360c',
3068     'A100': '#ff9e80',
3069     'A200': '#ff6e40',
3070     'A400': '#ff3d00',
3071     'A700': '#dd2c00',
3072     'contrastDefaultColor': 'light',
3073     'contrastDarkColors': '50 100 200 300 400 A100 A200',
3074     'contrastStrongLightColors': '500 600 700 800 900 A400 A700'
3075   },
3076   'brown': {
3077     '50': '#efebe9',
3078     '100': '#d7ccc8',
3079     '200': '#bcaaa4',
3080     '300': '#a1887f',
3081     '400': '#8d6e63',
3082     '500': '#795548',
3083     '600': '#6d4c41',
3084     '700': '#5d4037',
3085     '800': '#4e342e',
3086     '900': '#3e2723',
3087     'A100': '#d7ccc8',
3088     'A200': '#bcaaa4',
3089     'A400': '#8d6e63',
3090     'A700': '#5d4037',
3091     'contrastDefaultColor': 'light',
3092     'contrastDarkColors': '50 100 200',
3093     'contrastStrongLightColors': '300 400'
3094   },
3095   'grey': {
3096     '50': '#fafafa',
3097     '100': '#f5f5f5',
3098     '200': '#eeeeee',
3099     '300': '#e0e0e0',
3100     '400': '#bdbdbd',
3101     '500': '#9e9e9e',
3102     '600': '#757575',
3103     '700': '#616161',
3104     '800': '#424242',
3105     '900': '#212121',
3106     '1000': '#000000',
3107     'A100': '#ffffff',
3108     'A200': '#eeeeee',
3109     'A400': '#bdbdbd',
3110     'A700': '#616161',
3111     'contrastDefaultColor': 'dark',
3112     'contrastLightColors': '600 700 800 900'
3113   },
3114   'blue-grey': {
3115     '50': '#eceff1',
3116     '100': '#cfd8dc',
3117     '200': '#b0bec5',
3118     '300': '#90a4ae',
3119     '400': '#78909c',
3120     '500': '#607d8b',
3121     '600': '#546e7a',
3122     '700': '#455a64',
3123     '800': '#37474f',
3124     '900': '#263238',
3125     'A100': '#cfd8dc',
3126     'A200': '#b0bec5',
3127     'A400': '#78909c',
3128     'A700': '#455a64',
3129     'contrastDefaultColor': 'light',
3130     'contrastDarkColors': '50 100 200 300',
3131     'contrastStrongLightColors': '400 500'
3132   }
3133 });
3134
3135 angular.module('material.core.theming', ['material.core.theming.palette'])
3136   .directive('mdTheme', ThemingDirective)
3137   .directive('mdThemable', ThemableDirective)
3138   .provider('$mdTheming', ThemingProvider)
3139   .run(generateThemes);
3140
3141 /**
3142  * @ngdoc provider
3143  * @name $mdThemingProvider
3144  * @module material.core
3145  *
3146  * @description Provider to configure the `$mdTheming` service.
3147  */
3148
3149 /**
3150  * @ngdoc method
3151  * @name $mdThemingProvider#setDefaultTheme
3152  * @param {string} themeName Default theme name to be applied to elements. Default value is `default`.
3153  */
3154
3155 /**
3156  * @ngdoc method
3157  * @name $mdThemingProvider#alwaysWatchTheme
3158  * @param {boolean} watch Whether or not to always watch themes for changes and re-apply
3159  * classes when they change. Default is `false`. Enabling can reduce performance.
3160  */
3161
3162 /* Some Example Valid Theming Expressions
3163  * =======================================
3164  *
3165  * Intention group expansion: (valid for primary, accent, warn, background)
3166  *
3167  * {{primary-100}} - grab shade 100 from the primary palette
3168  * {{primary-100-0.7}} - grab shade 100, apply opacity of 0.7
3169  * {{primary-hue-1}} - grab the shade assigned to hue-1 from the primary palette
3170  * {{primary-hue-1-0.7}} - apply 0.7 opacity to primary-hue-1
3171  * {{primary-color}} - Generates .md-hue-1, .md-hue-2, .md-hue-3 with configured shades set for each hue
3172  * {{primary-color-0.7}} - Apply 0.7 opacity to each of the above rules
3173  * {{primary-contrast}} - Generates .md-hue-1, .md-hue-2, .md-hue-3 with configured contrast (ie. text) color shades set for each hue
3174  * {{primary-contrast-0.7}} - Apply 0.7 opacity to each of the above rules
3175  *
3176  * Foreground expansion: Applies rgba to black/white foreground text
3177  *
3178  * {{foreground-1}} - used for primary text
3179  * {{foreground-2}} - used for secondary text/divider
3180  * {{foreground-3}} - used for disabled text
3181  * {{foreground-4}} - used for dividers
3182  *
3183  */
3184
3185 // In memory generated CSS rules; registered by theme.name
3186 var GENERATED = { };
3187
3188 // In memory storage of defined themes and color palettes (both loaded by CSS, and user specified)
3189 var PALETTES;
3190 var THEMES;
3191
3192 var DARK_FOREGROUND = {
3193   name: 'dark',
3194   '1': 'rgba(0,0,0,0.87)',
3195   '2': 'rgba(0,0,0,0.54)',
3196   '3': 'rgba(0,0,0,0.26)',
3197   '4': 'rgba(0,0,0,0.12)'
3198 };
3199 var LIGHT_FOREGROUND = {
3200   name: 'light',
3201   '1': 'rgba(255,255,255,1.0)',
3202   '2': 'rgba(255,255,255,0.7)',
3203   '3': 'rgba(255,255,255,0.3)',
3204   '4': 'rgba(255,255,255,0.12)'
3205 };
3206
3207 var DARK_SHADOW = '1px 1px 0px rgba(0,0,0,0.4), -1px -1px 0px rgba(0,0,0,0.4)';
3208 var LIGHT_SHADOW = '';
3209
3210 var DARK_CONTRAST_COLOR = colorToRgbaArray('rgba(0,0,0,0.87)');
3211 var LIGHT_CONTRAST_COLOR = colorToRgbaArray('rgba(255,255,255,0.87');
3212 var STRONG_LIGHT_CONTRAST_COLOR = colorToRgbaArray('rgb(255,255,255)');
3213
3214 var THEME_COLOR_TYPES = ['primary', 'accent', 'warn', 'background'];
3215 var DEFAULT_COLOR_TYPE = 'primary';
3216
3217 // A color in a theme will use these hues by default, if not specified by user.
3218 var LIGHT_DEFAULT_HUES = {
3219   'accent': {
3220     'default': 'A200',
3221     'hue-1': 'A100',
3222     'hue-2': 'A400',
3223     'hue-3': 'A700'
3224   },
3225   'background': {
3226     'default': 'A100',
3227     'hue-1': '300',
3228     'hue-2': '800',
3229     'hue-3': '900'
3230   }
3231 };
3232
3233 var DARK_DEFAULT_HUES = {
3234   'background': {
3235     'default': '800',
3236     'hue-1': '300',
3237     'hue-2': '600',
3238     'hue-3': '900'
3239   }
3240 };
3241 THEME_COLOR_TYPES.forEach(function(colorType) {
3242   // Color types with unspecified default hues will use these default hue values
3243   var defaultDefaultHues = {
3244     'default': '500',
3245     'hue-1': '300',
3246     'hue-2': '800',
3247     'hue-3': 'A100'
3248   };
3249   if (!LIGHT_DEFAULT_HUES[colorType]) LIGHT_DEFAULT_HUES[colorType] = defaultDefaultHues;
3250   if (!DARK_DEFAULT_HUES[colorType]) DARK_DEFAULT_HUES[colorType] = defaultDefaultHues;
3251 });
3252
3253 var VALID_HUE_VALUES = [
3254   '50', '100', '200', '300', '400', '500', '600',
3255   '700', '800', '900', 'A100', 'A200', 'A400', 'A700'
3256 ];
3257
3258 function ThemingProvider($mdColorPalette) {
3259   PALETTES = { };
3260   THEMES = { };
3261
3262   var themingProvider;
3263   var defaultTheme = 'default';
3264   var alwaysWatchTheme = false;
3265
3266   // Load JS Defined Palettes
3267   angular.extend(PALETTES, $mdColorPalette);
3268
3269   // Default theme defined in core.js
3270
3271   ThemingService.$inject = ["$rootScope", "$log"];
3272   return themingProvider = {
3273     definePalette: definePalette,
3274     extendPalette: extendPalette,
3275     theme: registerTheme,
3276
3277     setDefaultTheme: function(theme) {
3278       defaultTheme = theme;
3279     },
3280     alwaysWatchTheme: function(alwaysWatch) {
3281       alwaysWatchTheme = alwaysWatch;
3282     },
3283     $get: ThemingService,
3284     _LIGHT_DEFAULT_HUES: LIGHT_DEFAULT_HUES,
3285     _DARK_DEFAULT_HUES: DARK_DEFAULT_HUES,
3286     _PALETTES: PALETTES,
3287     _THEMES: THEMES,
3288     _parseRules: parseRules,
3289     _rgba: rgba
3290   };
3291
3292   // Example: $mdThemingProvider.definePalette('neonRed', { 50: '#f5fafa', ... });
3293   function definePalette(name, map) {
3294     map = map || {};
3295     PALETTES[name] = checkPaletteValid(name, map);
3296     return themingProvider;
3297   }
3298
3299   // Returns an new object which is a copy of a given palette `name` with variables from
3300   // `map` overwritten
3301   // Example: var neonRedMap = $mdThemingProvider.extendPalette('red', { 50: '#f5fafafa' });
3302   function extendPalette(name, map) {
3303     return checkPaletteValid(name,  angular.extend({}, PALETTES[name] || {}, map) );
3304   }
3305
3306   // Make sure that palette has all required hues
3307   function checkPaletteValid(name, map) {
3308     var missingColors = VALID_HUE_VALUES.filter(function(field) {
3309       return !map[field];
3310     });
3311     if (missingColors.length) {
3312       throw new Error("Missing colors %1 in palette %2!"
3313                       .replace('%1', missingColors.join(', '))
3314                       .replace('%2', name));
3315     }
3316
3317     return map;
3318   }
3319
3320   // Register a theme (which is a collection of color palettes to use with various states
3321   // ie. warn, accent, primary )
3322   // Optionally inherit from an existing theme
3323   // $mdThemingProvider.theme('custom-theme').primaryPalette('red');
3324   function registerTheme(name, inheritFrom) {
3325     if (THEMES[name]) return THEMES[name];
3326
3327     inheritFrom = inheritFrom || 'default';
3328
3329     var parentTheme = typeof inheritFrom === 'string' ? THEMES[inheritFrom] : inheritFrom;
3330     var theme = new Theme(name);
3331
3332     if (parentTheme) {
3333       angular.forEach(parentTheme.colors, function(color, colorType) {
3334         theme.colors[colorType] = {
3335           name: color.name,
3336           // Make sure a COPY of the hues is given to the child color,
3337           // not the same reference.
3338           hues: angular.extend({}, color.hues)
3339         };
3340       });
3341     }
3342     THEMES[name] = theme;
3343
3344     return theme;
3345   }
3346
3347   function Theme(name) {
3348     var self = this;
3349     self.name = name;
3350     self.colors = {};
3351
3352     self.dark = setDark;
3353     setDark(false);
3354
3355     function setDark(isDark) {
3356       isDark = arguments.length === 0 ? true : !!isDark;
3357
3358       // If no change, abort
3359       if (isDark === self.isDark) return;
3360
3361       self.isDark = isDark;
3362
3363       self.foregroundPalette = self.isDark ? LIGHT_FOREGROUND : DARK_FOREGROUND;
3364       self.foregroundShadow = self.isDark ? DARK_SHADOW : LIGHT_SHADOW;
3365
3366       // Light and dark themes have different default hues.
3367       // Go through each existing color type for this theme, and for every
3368       // hue value that is still the default hue value from the previous light/dark setting,
3369       // set it to the default hue value from the new light/dark setting.
3370       var newDefaultHues = self.isDark ? DARK_DEFAULT_HUES : LIGHT_DEFAULT_HUES;
3371       var oldDefaultHues = self.isDark ? LIGHT_DEFAULT_HUES : DARK_DEFAULT_HUES;
3372       angular.forEach(newDefaultHues, function(newDefaults, colorType) {
3373         var color = self.colors[colorType];
3374         var oldDefaults = oldDefaultHues[colorType];
3375         if (color) {
3376           for (var hueName in color.hues) {
3377             if (color.hues[hueName] === oldDefaults[hueName]) {
3378               color.hues[hueName] = newDefaults[hueName];
3379             }
3380           }
3381         }
3382       });
3383
3384       return self;
3385     }
3386
3387     THEME_COLOR_TYPES.forEach(function(colorType) {
3388       var defaultHues = (self.isDark ? DARK_DEFAULT_HUES : LIGHT_DEFAULT_HUES)[colorType];
3389       self[colorType + 'Palette'] = function setPaletteType(paletteName, hues) {
3390         var color = self.colors[colorType] = {
3391           name: paletteName,
3392           hues: angular.extend({}, defaultHues, hues)
3393         };
3394
3395         Object.keys(color.hues).forEach(function(name) {
3396           if (!defaultHues[name]) {
3397             throw new Error("Invalid hue name '%1' in theme %2's %3 color %4. Available hue names: %4"
3398               .replace('%1', name)
3399               .replace('%2', self.name)
3400               .replace('%3', paletteName)
3401               .replace('%4', Object.keys(defaultHues).join(', '))
3402             );
3403           }
3404         });
3405         Object.keys(color.hues).map(function(key) {
3406           return color.hues[key];
3407         }).forEach(function(hueValue) {
3408           if (VALID_HUE_VALUES.indexOf(hueValue) == -1) {
3409             throw new Error("Invalid hue value '%1' in theme %2's %3 color %4. Available hue values: %5"
3410               .replace('%1', hueValue)
3411               .replace('%2', self.name)
3412               .replace('%3', colorType)
3413               .replace('%4', paletteName)
3414               .replace('%5', VALID_HUE_VALUES.join(', '))
3415             );
3416           }
3417         });
3418         return self;
3419       };
3420
3421       self[colorType + 'Color'] = function() {
3422         var args = Array.prototype.slice.call(arguments);
3423         console.warn('$mdThemingProviderTheme.' + colorType + 'Color() has been deprecated. ' +
3424                      'Use $mdThemingProviderTheme.' + colorType + 'Palette() instead.');
3425         return self[colorType + 'Palette'].apply(self, args);
3426       };
3427     });
3428   }
3429
3430   /**
3431    * @ngdoc service
3432    * @name $mdTheming
3433    *
3434    * @description
3435    *
3436    * Service that makes an element apply theming related classes to itself.
3437    *
3438    * ```js
3439    * app.directive('myFancyDirective', function($mdTheming) {
3440    *   return {
3441    *     restrict: 'e',
3442    *     link: function(scope, el, attrs) {
3443    *       $mdTheming(el);
3444    *     }
3445    *   };
3446    * });
3447    * ```
3448    * @param {el=} element to apply theming to
3449    */
3450   /* ngInject */
3451   function ThemingService($rootScope, $log) {
3452
3453     applyTheme.inherit = function(el, parent) {
3454       var ctrl = parent.controller('mdTheme');
3455
3456       var attrThemeValue = el.attr('md-theme-watch');
3457       if ( (alwaysWatchTheme || angular.isDefined(attrThemeValue)) && attrThemeValue != 'false') {
3458         var deregisterWatch = $rootScope.$watch(function() {
3459           return ctrl && ctrl.$mdTheme || defaultTheme;
3460         }, changeTheme);
3461         el.on('$destroy', deregisterWatch);
3462       } else {
3463         var theme = ctrl && ctrl.$mdTheme || defaultTheme;
3464         changeTheme(theme);
3465       }
3466
3467       function changeTheme(theme) {
3468         if (!registered(theme)) {
3469           $log.warn('Attempted to use unregistered theme \'' + theme + '\'. ' +
3470                     'Register it with $mdThemingProvider.theme().');
3471         }
3472         var oldTheme = el.data('$mdThemeName');
3473         if (oldTheme) el.removeClass('md-' + oldTheme +'-theme');
3474         el.addClass('md-' + theme + '-theme');
3475         el.data('$mdThemeName', theme);
3476       }
3477     };
3478
3479     applyTheme.THEMES = angular.extend({}, THEMES);
3480     applyTheme.defaultTheme = function() { return defaultTheme; };
3481     applyTheme.registered = registered;
3482
3483     return applyTheme;
3484
3485     function registered(themeName) {
3486       if (themeName === undefined || themeName === '') return true;
3487       return applyTheme.THEMES[themeName] !== undefined;
3488     }
3489
3490     function applyTheme(scope, el) {
3491       // Allow us to be invoked via a linking function signature.
3492       if (el === undefined) {
3493         el = scope;
3494         scope = undefined;
3495       }
3496       if (scope === undefined) {
3497         scope = $rootScope;
3498       }
3499       applyTheme.inherit(el, el);
3500     }
3501   }
3502 }
3503 ThemingProvider.$inject = ["$mdColorPalette"];
3504
3505 function ThemingDirective($mdTheming, $interpolate, $log) {
3506   return {
3507     priority: 100,
3508     link: {
3509       pre: function(scope, el, attrs) {
3510         var ctrl = {
3511           $setTheme: function(theme) {
3512             if (!$mdTheming.registered(theme)) {
3513               $log.warn('attempted to use unregistered theme \'' + theme + '\'');
3514             }
3515             ctrl.$mdTheme = theme;
3516           }
3517         };
3518         el.data('$mdThemeController', ctrl);
3519         ctrl.$setTheme($interpolate(attrs.mdTheme)(scope));
3520         attrs.$observe('mdTheme', ctrl.$setTheme);
3521       }
3522     }
3523   };
3524 }
3525 ThemingDirective.$inject = ["$mdTheming", "$interpolate", "$log"];
3526
3527 function ThemableDirective($mdTheming) {
3528   return $mdTheming;
3529 }
3530 ThemableDirective.$inject = ["$mdTheming"];
3531
3532 function parseRules(theme, colorType, rules) {
3533   checkValidPalette(theme, colorType);
3534
3535   rules = rules.replace(/THEME_NAME/g, theme.name);
3536   var generatedRules = [];
3537   var color = theme.colors[colorType];
3538
3539   var themeNameRegex = new RegExp('.md-' + theme.name + '-theme', 'g');
3540   // Matches '{{ primary-color }}', etc
3541   var hueRegex = new RegExp('(\'|")?{{\\s*(' + colorType + ')-(color|contrast)-?(\\d\\.?\\d*)?\\s*}}(\"|\')?','g');
3542   var simpleVariableRegex = /'?"?\{\{\s*([a-zA-Z]+)-(A?\d+|hue\-[0-3]|shadow)-?(\d\.?\d*)?\s*\}\}'?"?/g;
3543   var palette = PALETTES[color.name];
3544
3545   // find and replace simple variables where we use a specific hue, not an entire palette
3546   // eg. "{{primary-100}}"
3547   //\(' + THEME_COLOR_TYPES.join('\|') + '\)'
3548   rules = rules.replace(simpleVariableRegex, function(match, colorType, hue, opacity) {
3549     if (colorType === 'foreground') {
3550       if (hue == 'shadow') {
3551         return theme.foregroundShadow;
3552       } else {
3553         return theme.foregroundPalette[hue] || theme.foregroundPalette['1'];
3554       }
3555     }
3556     if (hue.indexOf('hue') === 0) {
3557       hue = theme.colors[colorType].hues[hue];
3558     }
3559     return rgba( (PALETTES[ theme.colors[colorType].name ][hue] || '').value, opacity );
3560   });
3561
3562   // For each type, generate rules for each hue (ie. default, md-hue-1, md-hue-2, md-hue-3)
3563   angular.forEach(color.hues, function(hueValue, hueName) {
3564     var newRule = rules
3565       .replace(hueRegex, function(match, _, colorType, hueType, opacity) {
3566         return rgba(palette[hueValue][hueType === 'color' ? 'value' : 'contrast'], opacity);
3567       });
3568     if (hueName !== 'default') {
3569       newRule = newRule.replace(themeNameRegex, '.md-' + theme.name + '-theme.md-' + hueName);
3570     }
3571
3572     // Don't apply a selector rule to the default theme, making it easier to override
3573     // styles of the base-component
3574     if (theme.name == 'default') {
3575       newRule = newRule.replace(/\.md-default-theme/g, '');
3576     }
3577     generatedRules.push(newRule);
3578   });
3579
3580   return generatedRules;
3581 }
3582
3583 // Generate our themes at run time given the state of THEMES and PALETTES
3584 function generateThemes($injector) {
3585
3586   var head = document.getElementsByTagName('head')[0];
3587   var firstChild = head ? head.firstElementChild : null;
3588   var themeCss = $injector.has('$MD_THEME_CSS') ? $injector.get('$MD_THEME_CSS') : '';
3589
3590   if ( !firstChild ) return;
3591   if (themeCss.length === 0) return; // no rules, so no point in running this expensive task
3592
3593   // Expose contrast colors for palettes to ensure that text is always readable
3594   angular.forEach(PALETTES, sanitizePalette);
3595
3596   // MD_THEME_CSS is a string generated by the build process that includes all the themable
3597   // components as templates
3598
3599   // Break the CSS into individual rules
3600   var rulesByType = {};
3601   var rules = themeCss
3602                   .split(/\}(?!(\}|'|"|;))/)
3603                   .filter(function(rule) { return rule && rule.length; })
3604                   .map(function(rule) { return rule.trim() + '}'; });
3605
3606
3607   var ruleMatchRegex = new RegExp('md-(' + THEME_COLOR_TYPES.join('|') + ')', 'g');
3608
3609   THEME_COLOR_TYPES.forEach(function(type) {
3610     rulesByType[type] = '';
3611   });
3612
3613
3614   // Sort the rules based on type, allowing us to do color substitution on a per-type basis
3615   rules.forEach(function(rule) {
3616     var match = rule.match(ruleMatchRegex);
3617     // First: test that if the rule has '.md-accent', it goes into the accent set of rules
3618     for (var i = 0, type; type = THEME_COLOR_TYPES[i]; i++) {
3619       if (rule.indexOf('.md-' + type) > -1) {
3620         return rulesByType[type] += rule;
3621       }
3622     }
3623
3624     // If no eg 'md-accent' class is found, try to just find 'accent' in the rule and guess from
3625     // there
3626     for (i = 0; type = THEME_COLOR_TYPES[i]; i++) {
3627       if (rule.indexOf(type) > -1) {
3628         return rulesByType[type] += rule;
3629       }
3630     }
3631
3632     // Default to the primary array
3633     return rulesByType[DEFAULT_COLOR_TYPE] += rule;
3634   });
3635
3636     // For each theme, use the color palettes specified for
3637     // `primary`, `warn` and `accent` to generate CSS rules.
3638
3639     angular.forEach(THEMES, function(theme) {
3640       if ( !GENERATED[theme.name] ) {
3641
3642
3643         THEME_COLOR_TYPES.forEach(function(colorType) {
3644           var styleStrings = parseRules(theme, colorType, rulesByType[colorType]);
3645           while (styleStrings.length) {
3646             var style = document.createElement('style');
3647                 style.setAttribute('type', 'text/css');
3648             style.appendChild(document.createTextNode(styleStrings.shift()));
3649             head.insertBefore(style, firstChild);
3650           }
3651         });
3652
3653
3654         if (theme.colors.primary.name == theme.colors.accent.name) {
3655           console.warn("$mdThemingProvider: Using the same palette for primary and" +
3656                        " accent. This violates the material design spec.");
3657         }
3658
3659         GENERATED[theme.name] = true;
3660       }
3661     });
3662
3663
3664   // *************************
3665   // Internal functions
3666   // *************************
3667
3668   // The user specifies a 'default' contrast color as either light or dark,
3669   // then explicitly lists which hues are the opposite contrast (eg. A100 has dark, A200 has light)
3670   function sanitizePalette(palette) {
3671     var defaultContrast = palette.contrastDefaultColor;
3672     var lightColors = palette.contrastLightColors || [];
3673     var strongLightColors = palette.contrastStrongLightColors || [];
3674     var darkColors = palette.contrastDarkColors || [];
3675
3676     // These colors are provided as space-separated lists
3677     if (typeof lightColors === 'string') lightColors = lightColors.split(' ');
3678     if (typeof strongLightColors === 'string') strongLightColors = strongLightColors.split(' ');
3679     if (typeof darkColors === 'string') darkColors = darkColors.split(' ');
3680
3681     // Cleanup after ourselves
3682     delete palette.contrastDefaultColor;
3683     delete palette.contrastLightColors;
3684     delete palette.contrastStrongLightColors;
3685     delete palette.contrastDarkColors;
3686
3687     // Change { 'A100': '#fffeee' } to { 'A100': { value: '#fffeee', contrast:DARK_CONTRAST_COLOR }
3688     angular.forEach(palette, function(hueValue, hueName) {
3689       if (angular.isObject(hueValue)) return; // Already converted
3690       // Map everything to rgb colors
3691       var rgbValue = colorToRgbaArray(hueValue);
3692       if (!rgbValue) {
3693         throw new Error("Color %1, in palette %2's hue %3, is invalid. Hex or rgb(a) color expected."
3694                         .replace('%1', hueValue)
3695                         .replace('%2', palette.name)
3696                         .replace('%3', hueName));
3697       }
3698
3699       palette[hueName] = {
3700         value: rgbValue,
3701         contrast: getContrastColor()
3702       };
3703       function getContrastColor() {
3704         if (defaultContrast === 'light') {
3705           if (darkColors.indexOf(hueName) > -1) {
3706             return DARK_CONTRAST_COLOR;
3707           } else {
3708             return strongLightColors.indexOf(hueName) > -1 ? STRONG_LIGHT_CONTRAST_COLOR
3709               : LIGHT_CONTRAST_COLOR;
3710           }
3711         } else {
3712           if (lightColors.indexOf(hueName) > -1) {
3713             return strongLightColors.indexOf(hueName) > -1 ? STRONG_LIGHT_CONTRAST_COLOR
3714               : LIGHT_CONTRAST_COLOR;
3715           } else {
3716             return DARK_CONTRAST_COLOR;
3717           }
3718         }
3719       }
3720     });
3721   }
3722
3723
3724 }
3725 generateThemes.$inject = ["$injector"];
3726
3727 function checkValidPalette(theme, colorType) {
3728   // If theme attempts to use a palette that doesnt exist, throw error
3729   if (!PALETTES[ (theme.colors[colorType] || {}).name ]) {
3730     throw new Error(
3731       "You supplied an invalid color palette for theme %1's %2 palette. Available palettes: %3"
3732                     .replace('%1', theme.name)
3733                     .replace('%2', colorType)
3734                     .replace('%3', Object.keys(PALETTES).join(', '))
3735     );
3736   }
3737 }
3738
3739 function colorToRgbaArray(clr) {
3740   if (angular.isArray(clr) && clr.length == 3) return clr;
3741   if (/^rgb/.test(clr)) {
3742     return clr.replace(/(^\s*rgba?\(|\)\s*$)/g, '').split(',').map(function(value, i) {
3743       return i == 3 ? parseFloat(value, 10) : parseInt(value, 10);
3744     });
3745   }
3746   if (clr.charAt(0) == '#') clr = clr.substring(1);
3747   if (!/^([a-fA-F0-9]{3}){1,2}$/g.test(clr)) return;
3748
3749   var dig = clr.length / 3;
3750   var red = clr.substr(0, dig);
3751   var grn = clr.substr(dig, dig);
3752   var blu = clr.substr(dig * 2);
3753   if (dig === 1) {
3754     red += red;
3755     grn += grn;
3756     blu += blu;
3757   }
3758   return [parseInt(red, 16), parseInt(grn, 16), parseInt(blu, 16)];
3759 }
3760
3761 function rgba(rgbArray, opacity) {
3762   if ( !rgbArray ) return "rgb('0,0,0')";
3763
3764   if (rgbArray.length == 4) {
3765     rgbArray = angular.copy(rgbArray);
3766     opacity ? rgbArray.pop() : opacity = rgbArray.pop();
3767   }
3768   return opacity && (typeof opacity == 'number' || (typeof opacity == 'string' && opacity.length)) ?
3769     'rgba(' + rgbArray.join(',') + ',' + opacity + ')' :
3770     'rgb(' + rgbArray.join(',') + ')';
3771 }
3772
3773
3774 (function(){ 
3775 angular.module("material.core").constant("$MD_THEME_CSS", "/* mixin definition ; sets LTR and RTL within the same style call */md-autocomplete.md-THEME_NAME-theme {  background: '{{background-50}}'; }  md-autocomplete.md-THEME_NAME-theme[disabled] {    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.md-THEME_NAME-theme {  background: '{{background-50}}'; }  .md-autocomplete-suggestions.md-THEME_NAME-theme li {    color: '{{background-900}}'; }    .md-autocomplete-suggestions.md-THEME_NAME-theme li .highlight {      color: '{{background-600}}'; }    .md-autocomplete-suggestions.md-THEME_NAME-theme li:hover, .md-autocomplete-suggestions.md-THEME_NAME-theme li.selected {      background: '{{background-200}}'; }md-backdrop.md-opaque.md-THEME_NAME-theme {  background-color: '{{foreground-4-0.5}}'; }md-bottom-sheet.md-THEME_NAME-theme {  background-color: '{{background-50}}';  border-top-color: '{{background-300}}'; }  md-bottom-sheet.md-THEME_NAME-theme.md-list md-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}}'; }a.md-button.md-THEME_NAME-theme, .md-button.md-THEME_NAME-theme {  border-radius: 3px; }  a.md-button.md-THEME_NAME-theme:not([disabled]):hover, .md-button.md-THEME_NAME-theme:not([disabled]):hover {    background-color: '{{background-500-0.2}}'; }  a.md-button.md-THEME_NAME-theme:not([disabled]).md-focused, .md-button.md-THEME_NAME-theme:not([disabled]).md-focused {    background-color: '{{background-500-0.2}}'; }  a.md-button.md-THEME_NAME-theme:not([disabled]).md-icon-button:hover, .md-button.md-THEME_NAME-theme:not([disabled]).md-icon-button:hover {    background-color: transparent; }  a.md-button.md-THEME_NAME-theme.md-fab, .md-button.md-THEME_NAME-theme.md-fab {    border-radius: 50%;    background-color: '{{accent-color}}';    color: '{{accent-contrast}}'; }    a.md-button.md-THEME_NAME-theme.md-fab md-icon, .md-button.md-THEME_NAME-theme.md-fab md-icon {      color: '{{accent-contrast}}'; }    a.md-button.md-THEME_NAME-theme.md-fab:not([disabled]):hover, .md-button.md-THEME_NAME-theme.md-fab:not([disabled]):hover {      background-color: '{{accent-color}}'; }    a.md-button.md-THEME_NAME-theme.md-fab:not([disabled]).md-focused, .md-button.md-THEME_NAME-theme.md-fab:not([disabled]).md-focused {      background-color: '{{accent-A700}}'; }  a.md-button.md-THEME_NAME-theme.md-icon-button, .md-button.md-THEME_NAME-theme.md-icon-button {    border-radius: 50%; }  a.md-button.md-THEME_NAME-theme.md-primary, .md-button.md-THEME_NAME-theme.md-primary {    color: '{{primary-color}}'; }    a.md-button.md-THEME_NAME-theme.md-primary.md-raised, a.md-button.md-THEME_NAME-theme.md-primary.md-fab, .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}}'; }      a.md-button.md-THEME_NAME-theme.md-primary.md-raised:not([disabled]) md-icon, a.md-button.md-THEME_NAME-theme.md-primary.md-fab:not([disabled]) md-icon, .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}}'; }      a.md-button.md-THEME_NAME-theme.md-primary.md-raised:not([disabled]):hover, a.md-button.md-THEME_NAME-theme.md-primary.md-fab:not([disabled]):hover, .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-color}}'; }      a.md-button.md-THEME_NAME-theme.md-primary.md-raised:not([disabled]).md-focused, a.md-button.md-THEME_NAME-theme.md-primary.md-fab:not([disabled]).md-focused, .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}}'; }    a.md-button.md-THEME_NAME-theme.md-primary:not([disabled]) md-icon, .md-button.md-THEME_NAME-theme.md-primary:not([disabled]) md-icon {      color: '{{primary-color}}'; }  a.md-button.md-THEME_NAME-theme.md-fab, .md-button.md-THEME_NAME-theme.md-fab {    border-radius: 50%;    background-color: '{{accent-color}}';    color: '{{accent-contrast}}'; }    a.md-button.md-THEME_NAME-theme.md-fab:not([disabled]) .md-icon, .md-button.md-THEME_NAME-theme.md-fab:not([disabled]) .md-icon {      color: '{{accent-contrast}}'; }    a.md-button.md-THEME_NAME-theme.md-fab:not([disabled]):hover, .md-button.md-THEME_NAME-theme.md-fab:not([disabled]):hover {      background-color: '{{accent-color}}'; }    a.md-button.md-THEME_NAME-theme.md-fab:not([disabled]).md-focused, .md-button.md-THEME_NAME-theme.md-fab:not([disabled]).md-focused {      background-color: '{{accent-A700}}'; }  a.md-button.md-THEME_NAME-theme.md-raised, .md-button.md-THEME_NAME-theme.md-raised {    color: '{{background-contrast}}';    background-color: '{{background-50}}'; }    a.md-button.md-THEME_NAME-theme.md-raised:not([disabled]) .md-icon, .md-button.md-THEME_NAME-theme.md-raised:not([disabled]) .md-icon {      color: '{{background-contrast}}'; }    a.md-button.md-THEME_NAME-theme.md-raised:not([disabled]):hover, .md-button.md-THEME_NAME-theme.md-raised:not([disabled]):hover {      background-color: '{{background-50}}'; }    a.md-button.md-THEME_NAME-theme.md-raised:not([disabled]).md-focused, .md-button.md-THEME_NAME-theme.md-raised:not([disabled]).md-focused {      background-color: '{{background-200}}'; }  a.md-button.md-THEME_NAME-theme.md-warn, .md-button.md-THEME_NAME-theme.md-warn {    color: '{{warn-color}}'; }    a.md-button.md-THEME_NAME-theme.md-warn.md-raised, a.md-button.md-THEME_NAME-theme.md-warn.md-fab, .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}}'; }      a.md-button.md-THEME_NAME-theme.md-warn.md-raised:not([disabled]) md-icon, a.md-button.md-THEME_NAME-theme.md-warn.md-fab:not([disabled]) md-icon, .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}}'; }      a.md-button.md-THEME_NAME-theme.md-warn.md-raised:not([disabled]):hover, a.md-button.md-THEME_NAME-theme.md-warn.md-fab:not([disabled]):hover, .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-color}}'; }      a.md-button.md-THEME_NAME-theme.md-warn.md-raised:not([disabled]).md-focused, a.md-button.md-THEME_NAME-theme.md-warn.md-fab:not([disabled]).md-focused, .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-700}}'; }    a.md-button.md-THEME_NAME-theme.md-warn:not([disabled]) md-icon, .md-button.md-THEME_NAME-theme.md-warn:not([disabled]) md-icon {      color: '{{warn-color}}'; }  a.md-button.md-THEME_NAME-theme.md-accent, .md-button.md-THEME_NAME-theme.md-accent {    color: '{{accent-color}}'; }    a.md-button.md-THEME_NAME-theme.md-accent.md-raised, a.md-button.md-THEME_NAME-theme.md-accent.md-fab, .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}}'; }      a.md-button.md-THEME_NAME-theme.md-accent.md-raised:not([disabled]) md-icon, a.md-button.md-THEME_NAME-theme.md-accent.md-fab:not([disabled]) md-icon, .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}}'; }      a.md-button.md-THEME_NAME-theme.md-accent.md-raised:not([disabled]):hover, a.md-button.md-THEME_NAME-theme.md-accent.md-fab:not([disabled]):hover, .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-color}}'; }      a.md-button.md-THEME_NAME-theme.md-accent.md-raised:not([disabled]).md-focused, a.md-button.md-THEME_NAME-theme.md-accent.md-fab:not([disabled]).md-focused, .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-700}}'; }    a.md-button.md-THEME_NAME-theme.md-accent:not([disabled]) md-icon, .md-button.md-THEME_NAME-theme.md-accent:not([disabled]) md-icon {      color: '{{accent-color}}'; }  a.md-button.md-THEME_NAME-theme[disabled], a.md-button.md-THEME_NAME-theme.md-raised[disabled], a.md-button.md-THEME_NAME-theme.md-fab[disabled], a.md-button.md-THEME_NAME-theme.md-accent[disabled], a.md-button.md-THEME_NAME-theme.md-warn[disabled], .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: not-allowed; }    a.md-button.md-THEME_NAME-theme[disabled] md-icon, a.md-button.md-THEME_NAME-theme.md-raised[disabled] md-icon, a.md-button.md-THEME_NAME-theme.md-fab[disabled] md-icon, a.md-button.md-THEME_NAME-theme.md-accent[disabled] md-icon, a.md-button.md-THEME_NAME-theme.md-warn[disabled] md-icon, .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}}'; }  a.md-button.md-THEME_NAME-theme.md-raised[disabled], a.md-button.md-THEME_NAME-theme.md-fab[disabled], .md-button.md-THEME_NAME-theme.md-raised[disabled], .md-button.md-THEME_NAME-theme.md-fab[disabled] {    background-color: '{{foreground-4}}'; }  a.md-button.md-THEME_NAME-theme[disabled], .md-button.md-THEME_NAME-theme[disabled] {    background-color: transparent; }md-card.md-THEME_NAME-theme {  background-color: '{{background-color}}';  border-radius: 2px; }  md-card.md-THEME_NAME-theme .md-card-image {    border-radius: 2px 2px 0 0; }md-checkbox.md-THEME_NAME-theme .md-ripple {  color: '{{accent-600}}'; }md-checkbox.md-THEME_NAME-theme.md-checked .md-ripple {  color: '{{background-600}}'; }md-checkbox.md-THEME_NAME-theme.md-checked.md-focused .md-container:before {  background-color: '{{accent-color-0.26}}'; }md-checkbox.md-THEME_NAME-theme .md-icon {  border-color: '{{foreground-2}}'; }md-checkbox.md-THEME_NAME-theme.md-checked .md-icon {  background-color: '{{accent-color-0.87}}'; }md-checkbox.md-THEME_NAME-theme.md-checked .md-icon:after {  border-color: '{{background-200}}'; }md-checkbox.md-THEME_NAME-theme:not([disabled]).md-primary .md-ripple {  color: '{{primary-600}}'; }md-checkbox.md-THEME_NAME-theme:not([disabled]).md-primary.md-checked .md-ripple {  color: '{{background-600}}'; }md-checkbox.md-THEME_NAME-theme:not([disabled]).md-primary .md-icon {  border-color: '{{foreground-2}}'; }md-checkbox.md-THEME_NAME-theme:not([disabled]).md-primary.md-checked .md-icon {  background-color: '{{primary-color-0.87}}'; }md-checkbox.md-THEME_NAME-theme:not([disabled]).md-primary.md-checked.md-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: '{{background-200}}'; }md-checkbox.md-THEME_NAME-theme:not([disabled]).md-warn .md-ripple {  color: '{{warn-600}}'; }md-checkbox.md-THEME_NAME-theme:not([disabled]).md-warn .md-icon {  border-color: '{{foreground-2}}'; }md-checkbox.md-THEME_NAME-theme:not([disabled]).md-warn.md-checked .md-icon {  background-color: '{{warn-color-0.87}}'; }md-checkbox.md-THEME_NAME-theme:not([disabled]).md-warn.md-checked.md-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] .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-label {  color: '{{foreground-3}}'; }md-chips.md-THEME_NAME-theme .md-chips {  box-shadow: 0 1px '{{background-300}}'; }  md-chips.md-THEME_NAME-theme .md-chips.md-focused {    box-shadow: 0 2px '{{primary-color}}'; }md-chips.md-THEME_NAME-theme .md-chip {  background: '{{background-300}}';  color: '{{background-800}}'; }  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-remove .md-button md-icon path {  fill: '{{background-500}}'; }.md-contact-suggestion span.md-contact-email {  color: '{{background-400}}'; }md-dialog.md-THEME_NAME-theme {  border-radius: 4px;  background-color: '{{background-color}}'; }  md-dialog.md-THEME_NAME-theme.md-content-overflow .md-actions {    border-top-color: '{{foreground-4}}'; }md-content.md-THEME_NAME-theme {  background-color: '{{background-color}}'; }md-divider.md-THEME_NAME-theme {  border-top-color: '{{foreground-4}}'; }md-input-container.md-THEME_NAME-theme .md-input {  color: '{{foreground-1}}';  border-color: '{{foreground-4}}';  text-shadow: '{{foreground-shadow}}'; }  md-input-container.md-THEME_NAME-theme .md-input::-webkit-input-placeholder, md-input-container.md-THEME_NAME-theme .md-input::-moz-placeholder, md-input-container.md-THEME_NAME-theme .md-input:-moz-placeholder, md-input-container.md-THEME_NAME-theme .md-input:-ms-input-placeholder {    color: '{{foreground-3}}'; }md-input-container.md-THEME_NAME-theme > md-icon {  color: '{{foreground-1}}'; }md-input-container.md-THEME_NAME-theme label, md-input-container.md-THEME_NAME-theme .md-placeholder {  text-shadow: '{{foreground-shadow}}';  color: '{{foreground-3}}'; }md-input-container.md-THEME_NAME-theme ng-messages, md-input-container.md-THEME_NAME-theme [ng-message], md-input-container.md-THEME_NAME-theme [data-ng-message], md-input-container.md-THEME_NAME-theme [x-ng-message] {  color: '{{warn-500}}'; }md-input-container.md-THEME_NAME-theme:not(.md-input-invalid).md-input-has-value label {  color: '{{foreground-2}}'; }md-input-container.md-THEME_NAME-theme:not(.md-input-invalid).md-input-focused .md-input {  border-color: '{{primary-500}}'; }md-input-container.md-THEME_NAME-theme:not(.md-input-invalid).md-input-focused label {  color: '{{primary-500}}'; }md-input-container.md-THEME_NAME-theme:not(.md-input-invalid).md-input-focused md-icon {  color: '{{primary-500}}'; }md-input-container.md-THEME_NAME-theme:not(.md-input-invalid).md-input-focused.md-accent .md-input {  border-color: '{{accent-500}}'; }md-input-container.md-THEME_NAME-theme:not(.md-input-invalid).md-input-focused.md-accent label {  color: '{{accent-500}}'; }md-input-container.md-THEME_NAME-theme:not(.md-input-invalid).md-input-focused.md-warn .md-input {  border-color: '{{warn-500}}'; }md-input-container.md-THEME_NAME-theme:not(.md-input-invalid).md-input-focused.md-warn label {  color: '{{warn-500}}'; }md-input-container.md-THEME_NAME-theme.md-input-invalid .md-input {  border-color: '{{warn-500}}'; }md-input-container.md-THEME_NAME-theme.md-input-invalid.md-input-focused label {  color: '{{warn-500}}'; }md-input-container.md-THEME_NAME-theme.md-input-invalid ng-message, md-input-container.md-THEME_NAME-theme.md-input-invalid data-ng-message, md-input-container.md-THEME_NAME-theme.md-input-invalid x-ng-message, md-input-container.md-THEME_NAME-theme.md-input-invalid [ng-message], md-input-container.md-THEME_NAME-theme.md-input-invalid [data-ng-message], md-input-container.md-THEME_NAME-theme.md-input-invalid [x-ng-message], md-input-container.md-THEME_NAME-theme.md-input-invalid .md-char-counter {  color: '{{warn-500}}'; }md-input-container.md-THEME_NAME-theme .md-input[disabled], [disabled] md-input-container.md-THEME_NAME-theme .md-input {  border-bottom-color: transparent;  color: '{{foreground-3}}';  background-image: linear-gradient(to right, '{{foreground-3}}' 0%, '{{foreground-3}}' 33%, transparent 0%);  background-image: -ms-linear-gradient(left, transparent 0%, '{{foreground-3}}' 100%); }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-progress-circular.md-THEME_NAME-theme {  background-color: transparent; }  md-progress-circular.md-THEME_NAME-theme .md-inner .md-gap {    border-top-color: '{{primary-color}}';    border-bottom-color: '{{primary-color}}'; }  md-progress-circular.md-THEME_NAME-theme .md-inner .md-left .md-half-circle, md-progress-circular.md-THEME_NAME-theme .md-inner .md-right .md-half-circle {    border-top-color: '{{primary-color}}'; }  md-progress-circular.md-THEME_NAME-theme .md-inner .md-right .md-half-circle {    border-right-color: '{{primary-color}}'; }  md-progress-circular.md-THEME_NAME-theme .md-inner .md-left .md-half-circle {    border-left-color: '{{primary-color}}'; }  md-progress-circular.md-THEME_NAME-theme.md-warn .md-inner .md-gap {    border-top-color: '{{warn-color}}';    border-bottom-color: '{{warn-color}}'; }  md-progress-circular.md-THEME_NAME-theme.md-warn .md-inner .md-left .md-half-circle, md-progress-circular.md-THEME_NAME-theme.md-warn .md-inner .md-right .md-half-circle {    border-top-color: '{{warn-color}}'; }  md-progress-circular.md-THEME_NAME-theme.md-warn .md-inner .md-right .md-half-circle {    border-right-color: '{{warn-color}}'; }  md-progress-circular.md-THEME_NAME-theme.md-warn .md-inner .md-left .md-half-circle {    border-left-color: '{{warn-color}}'; }  md-progress-circular.md-THEME_NAME-theme.md-accent .md-inner .md-gap {    border-top-color: '{{accent-color}}';    border-bottom-color: '{{accent-color}}'; }  md-progress-circular.md-THEME_NAME-theme.md-accent .md-inner .md-left .md-half-circle, md-progress-circular.md-THEME_NAME-theme.md-accent .md-inner .md-right .md-half-circle {    border-top-color: '{{accent-color}}'; }  md-progress-circular.md-THEME_NAME-theme.md-accent .md-inner .md-right .md-half-circle {    border-right-color: '{{accent-color}}'; }  md-progress-circular.md-THEME_NAME-theme.md-accent .md-inner .md-left .md-half-circle {    border-left-color: '{{accent-color}}'; }md-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-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-list.md-THEME_NAME-theme md-list-item button {  background-color: '{{background-color}}'; }  md-list.md-THEME_NAME-theme md-list-item button.md-button:not([disabled]):hover {    background-color: '{{background-color}}'; }md-progress-linear.md-THEME_NAME-theme .md-container {  background-color: '{{primary-100}}'; }md-progress-linear.md-THEME_NAME-theme .md-bar {  background-color: '{{primary-color}}'; }md-progress-linear.md-THEME_NAME-theme.md-warn .md-container {  background-color: '{{warn-100}}'; }md-progress-linear.md-THEME_NAME-theme.md-warn .md-bar {  background-color: '{{warn-color}}'; }md-progress-linear.md-THEME_NAME-theme.md-accent .md-container {  background-color: '{{accent-100}}'; }md-progress-linear.md-THEME_NAME-theme.md-accent .md-bar {  background-color: '{{accent-color}}'; }md-progress-linear.md-THEME_NAME-theme[md-mode=buffer].md-warn .md-bar1 {  background-color: '{{warn-100}}'; }md-progress-linear.md-THEME_NAME-theme[md-mode=buffer].md-warn .md-dashed:before {  background: radial-gradient('{{warn-100}}' 0%, '{{warn-100}}' 16%, transparent 42%); }md-progress-linear.md-THEME_NAME-theme[md-mode=buffer].md-accent .md-bar1 {  background-color: '{{accent-100}}'; }md-progress-linear.md-THEME_NAME-theme[md-mode=buffer].md-accent .md-dashed:before {  background: radial-gradient('{{accent-100}}' 0%, '{{accent-100}}' 16%, transparent 42%); }md-radio-button.md-THEME_NAME-theme .md-off {  border-color: '{{foreground-2}}'; }md-radio-button.md-THEME_NAME-theme .md-on {  background-color: '{{accent-color-0.87}}'; }md-radio-button.md-THEME_NAME-theme.md-checked .md-off {  border-color: '{{accent-color-0.87}}'; }md-radio-button.md-THEME_NAME-theme.md-checked .md-ink-ripple {  color: '{{accent-color-0.87}}'; }md-radio-button.md-THEME_NAME-theme .md-container .md-ripple {  color: '{{accent-600}}'; }md-radio-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-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-checked:not([disabled]).md-primary .md-container:before {  background-color: '{{primary-color-0.26}}'; }md-radio-group.md-THEME_NAME-theme.md-focused:not(:empty) .md-checked.md-primary .md-container:before {  background-color: '{{warn-color-0.26}}'; }md-select.md-THEME_NAME-theme.ng-invalid.ng-dirty .md-select-label {  color: '{{warn-500}}' !important;  border-bottom-color: '{{warn-500}}' !important; }md-select.md-THEME_NAME-theme:not([disabled]):focus .md-select-label {  border-bottom-color: '{{primary-color}}';  color: '{{ foreground-1 }}'; }  md-select.md-THEME_NAME-theme:not([disabled]):focus .md-select-label.md-placeholder {    color: '{{ foreground-1 }}'; }md-select.md-THEME_NAME-theme:not([disabled]):focus.md-accent .md-select-label {  border-bottom-color: '{{accent-color}}'; }md-select.md-THEME_NAME-theme:not([disabled]):focus.md-warn .md-select-label {  border-bottom-color: '{{warn-color}}'; }md-select.md-THEME_NAME-theme[disabled] .md-select-label {  color: '{{foreground-3}}'; }  md-select.md-THEME_NAME-theme[disabled] .md-select-label.md-placeholder {    color: '{{foreground-3}}'; }md-select.md-THEME_NAME-theme .md-select-label {  border-bottom-color: '{{foreground-4}}'; }  md-select.md-THEME_NAME-theme .md-select-label.md-placeholder {    color: '{{foreground-2}}'; }md-select-menu.md-THEME_NAME-theme md-optgroup {  color: '{{foreground-2}}'; }  md-select-menu.md-THEME_NAME-theme md-optgroup md-option {    color: '{{foreground-1}}'; }md-select-menu.md-THEME_NAME-theme md-option[selected] {  color: '{{primary-500}}'; }  md-select-menu.md-THEME_NAME-theme md-option[selected]:focus {    color: '{{primary-600}}'; }  md-select-menu.md-THEME_NAME-theme md-option[selected].md-accent {    color: '{{accent-500}}'; }    md-select-menu.md-THEME_NAME-theme md-option[selected].md-accent:focus {      color: '{{accent-600}}'; }md-select-menu.md-THEME_NAME-theme md-option:focus:not([selected]) {  background: '{{background-200}}'; }md-sidenav.md-THEME_NAME-theme {  background-color: '{{background-color}}'; }md-slider.md-THEME_NAME-theme .md-track {  background-color: '{{foreground-3}}'; }md-slider.md-THEME_NAME-theme .md-track-ticks {  background-color: '{{foreground-4}}'; }md-slider.md-THEME_NAME-theme .md-focus-thumb {  background-color: '{{foreground-2}}'; }md-slider.md-THEME_NAME-theme .md-focus-ring {  border-color: '{{foreground-4}}'; }md-slider.md-THEME_NAME-theme .md-disabled-thumb {  border-color: '{{background-color}}'; }md-slider.md-THEME_NAME-theme.md-min .md-thumb:after {  background-color: '{{background-color}}'; }md-slider.md-THEME_NAME-theme .md-track.md-track-fill {  background-color: '{{accent-color}}'; }md-slider.md-THEME_NAME-theme .md-thumb:after {  border-color: '{{accent-color}}';  background-color: '{{accent-color}}'; }md-slider.md-THEME_NAME-theme .md-sign {  background-color: '{{accent-color}}'; }  md-slider.md-THEME_NAME-theme .md-sign:after {    border-top-color: '{{accent-color}}'; }md-slider.md-THEME_NAME-theme .md-thumb-text {  color: '{{accent-contrast}}'; }md-slider.md-THEME_NAME-theme.md-warn .md-track.md-track-fill {  background-color: '{{warn-color}}'; }md-slider.md-THEME_NAME-theme.md-warn .md-thumb:after {  border-color: '{{warn-color}}';  background-color: '{{warn-color}}'; }md-slider.md-THEME_NAME-theme.md-warn .md-sign {  background-color: '{{warn-color}}'; }  md-slider.md-THEME_NAME-theme.md-warn .md-sign:after {    border-top-color: '{{warn-color}}'; }md-slider.md-THEME_NAME-theme.md-warn .md-thumb-text {  color: '{{warn-contrast}}'; }md-slider.md-THEME_NAME-theme.md-primary .md-track.md-track-fill {  background-color: '{{primary-color}}'; }md-slider.md-THEME_NAME-theme.md-primary .md-thumb:after {  border-color: '{{primary-color}}';  background-color: '{{primary-color}}'; }md-slider.md-THEME_NAME-theme.md-primary .md-sign {  background-color: '{{primary-color}}'; }  md-slider.md-THEME_NAME-theme.md-primary .md-sign:after {    border-top-color: '{{primary-color}}'; }md-slider.md-THEME_NAME-theme.md-primary .md-thumb-text {  color: '{{primary-contrast}}'; }md-slider.md-THEME_NAME-theme[disabled] .md-thumb:after {  border-color: '{{foreground-3}}'; }md-slider.md-THEME_NAME-theme[disabled]:not(.md-min) .md-thumb:after {  background-color: '{{foreground-3}}'; }.md-subheader.md-THEME_NAME-theme {  color: '{{ foreground-2-0.23 }}';  background-color: '{{background-color}}'; }  .md-subheader.md-THEME_NAME-theme.md-primary {    color: '{{primary-color}}'; }  .md-subheader.md-THEME_NAME-theme.md-accent {    color: '{{accent-color}}'; }  .md-subheader.md-THEME_NAME-theme.md-warn {    color: '{{warn-color}}'; }md-switch.md-THEME_NAME-theme .md-thumb {  background-color: '{{background-50}}'; }md-switch.md-THEME_NAME-theme .md-bar {  background-color: '{{background-500}}'; }md-switch.md-THEME_NAME-theme.md-checked .md-thumb {  background-color: '{{accent-color}}'; }md-switch.md-THEME_NAME-theme.md-checked .md-bar {  background-color: '{{accent-color-0.5}}'; }md-switch.md-THEME_NAME-theme.md-checked.md-focused .md-thumb:before {  background-color: '{{accent-color-0.26}}'; }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-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] {    color: '{{foreground-3}}'; }  md-tabs.md-THEME_NAME-theme .md-tab.md-active, md-tabs.md-THEME_NAME-theme .md-tab.md-focused {    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-100}}'; }md-tabs.md-THEME_NAME-theme.md-accent md-tabs-wrapper {  background-color: '{{accent-color}}'; }md-tabs.md-THEME_NAME-theme.md-accent md-tab-item:not([disabled]) {  color: '{{accent-100}}'; }  md-tabs.md-THEME_NAME-theme.md-accent md-tab-item:not([disabled]).md-active, md-tabs.md-THEME_NAME-theme.md-accent md-tab-item:not([disabled]).md-focused {    color: '{{accent-contrast}}'; }  md-tabs.md-THEME_NAME-theme.md-accent md-tab-item:not([disabled]).md-focused {    background: '{{accent-contrast-0.1}}'; }md-tabs.md-THEME_NAME-theme.md-accent md-ink-bar {  color: '{{primary-600-1}}';  background: '{{primary-600-1}}'; }md-tabs.md-THEME_NAME-theme.md-primary md-tabs-wrapper {  background-color: '{{primary-color}}'; }md-tabs.md-THEME_NAME-theme.md-primary md-tab-item:not([disabled]) {  color: '{{primary-100}}'; }  md-tabs.md-THEME_NAME-theme.md-primary md-tab-item:not([disabled]).md-active, md-tabs.md-THEME_NAME-theme.md-primary md-tab-item:not([disabled]).md-focused {    color: '{{primary-contrast}}'; }  md-tabs.md-THEME_NAME-theme.md-primary 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-tab-item:not([disabled]) {  color: '{{warn-100}}'; }  md-tabs.md-THEME_NAME-theme.md-warn md-tab-item:not([disabled]).md-active, md-tabs.md-THEME_NAME-theme.md-warn md-tab-item:not([disabled]).md-focused {    color: '{{warn-contrast}}'; }  md-tabs.md-THEME_NAME-theme.md-warn 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-tab-item:not([disabled]) {  color: '{{primary-100}}'; }  md-toolbar > md-tabs.md-THEME_NAME-theme md-tab-item:not([disabled]).md-active, md-toolbar > md-tabs.md-THEME_NAME-theme md-tab-item:not([disabled]).md-focused {    color: '{{primary-contrast}}'; }  md-toolbar > md-tabs.md-THEME_NAME-theme 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-tab-item:not([disabled]) {  color: '{{accent-100}}'; }  md-toolbar.md-accent > md-tabs.md-THEME_NAME-theme md-tab-item:not([disabled]).md-active, md-toolbar.md-accent > md-tabs.md-THEME_NAME-theme md-tab-item:not([disabled]).md-focused {    color: '{{accent-contrast}}'; }  md-toolbar.md-accent > md-tabs.md-THEME_NAME-theme md-tab-item:not([disabled]).md-focused {    background: '{{accent-contrast-0.1}}'; }md-toolbar.md-accent > md-tabs.md-THEME_NAME-theme 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-tab-item:not([disabled]) {  color: '{{warn-100}}'; }  md-toolbar.md-warn > md-tabs.md-THEME_NAME-theme md-tab-item:not([disabled]).md-active, md-toolbar.md-warn > md-tabs.md-THEME_NAME-theme md-tab-item:not([disabled]).md-focused {    color: '{{warn-contrast}}'; }  md-toolbar.md-warn > md-tabs.md-THEME_NAME-theme md-tab-item:not([disabled]).md-focused {    background: '{{warn-contrast-0.1}}'; }md-toast.md-THEME_NAME-theme {  background-color: #323232;  color: '{{background-50}}'; }  md-toast.md-THEME_NAME-theme .md-button {    color: '{{background-50}}'; }    md-toast.md-THEME_NAME-theme .md-button.md-highlight {      color: '{{primary-A200}}'; }      md-toast.md-THEME_NAME-theme .md-button.md-highlight.md-accent {        color: '{{accent-A200}}'; }      md-toast.md-THEME_NAME-theme .md-button.md-highlight.md-warn {        color: '{{warn-A200}}'; }md-toolbar.md-THEME_NAME-theme {  background-color: '{{primary-color}}';  color: '{{primary-contrast}}'; }  md-toolbar.md-THEME_NAME-theme md-icon {    color: '{{primary-contrast}}'; }  md-toolbar.md-THEME_NAME-theme .md-button {    color: '{{primary-contrast}}'; }  md-toolbar.md-THEME_NAME-theme.md-accent {    background-color: '{{accent-color}}';    color: '{{accent-contrast}}'; }  md-toolbar.md-THEME_NAME-theme.md-warn {    background-color: '{{warn-color}}';    color: '{{warn-contrast}}'; }md-tooltip.md-THEME_NAME-theme {  color: '{{background-A100}}'; }  md-tooltip.md-THEME_NAME-theme .md-background {    background-color: '{{foreground-2}}'; }"); 
3776 })();
3777
3778
3779 ng.material.core = angular.module("material.core");