text: /*!
   * Angular Material Design
   * https://github.com/angular/material
   * @license MIT
   * v0.10.1
   */
  goog.provide('ng.material.core');
  
  
  
  /**
   * Initialization function that validates environment
   * requirements.
   */
  angular
    .module('material.core', [ 'material.core.gestures', 'material.core.theming' ])
    .config( MdCoreConfigure );
  
  
  function MdCoreConfigure($provide, $mdThemingProvider) {
  
    $provide.decorator('$$rAF', ["$delegate", rAFDecorator]);
  
    $mdThemingProvider.theme('default')
      .primaryPalette('indigo')
      .accentPalette('pink')
      .warnPalette('red')
      .backgroundPalette('grey');
  }
  MdCoreConfigure.$inject = ["$provide", "$mdThemingProvider"];
  
  function rAFDecorator( $delegate ) {
    /**
     * Use this to throttle events that come in often.
     * The throttled function will always use the *last* invocation before the
     * coming frame.
     *
     * For example, window resize events that fire many times a second:
     * If we set to use an raf-throttled callback on window resize, then
     * our callback will only be fired once per frame, with the last resize
     * event that happened before that frame.
     *
     * @param {function} callback function to debounce
     */
    $delegate.throttle = function(cb) {
      var queuedArgs, alreadyQueued, queueCb, context;
      return function debounced() {
        queuedArgs = arguments;
        context = this;
        queueCb = cb;
        if (!alreadyQueued) {
          alreadyQueued = true;
          $delegate(function() {
            queueCb.apply(context, Array.prototype.slice.call(queuedArgs));
            alreadyQueued = false;
          });
        }
      };
    };
    return $delegate;
  }
  
  angular
    .module('material.core')
    .factory('$$mdAnimate', ["$$rAF", "$q", "$timeout", "$mdConstant", function($$rAF, $q, $timeout, $mdConstant){
  
       // Since $$mdAnimate is injected into $mdUtil... use a wrapper function
       // to subsequently inject $mdUtil as an argument to the AnimateDomUtils
  
       return function($mdUtil) {
         return AnimateDomUtils( $mdUtil, $$rAF, $q, $timeout, $mdConstant);
       };
     }]);
  
  /**
   * Factory function that requires special injections
   */
  function AnimateDomUtils($mdUtil, $$rAF, $q, $timeout, $mdConstant) {
    var self;
    return self = {
      /**
       *
       */
      translate3d : function( target, from, to, options ) {
        // Set translate3d style to start at the `from` origin
        target.css(from);
  
        // Wait while CSS takes affect
        // Set the `to` styles and run the transition-in styles
        $$rAF(function () {
          target.css(to).addClass(options.transitionInClass);
        });
  
        return self
          .waitTransitionEnd(target)
          .then(function(){
              // Resolve with reverser function...
              return reverseTranslate;
          });
  
        /**
         * Specific reversal of the request translate animation above...
         */
        function reverseTranslate (newFrom) {
          target.removeClass(options.transitionInClass)
                .addClass(options.transitionOutClass)
                .css( newFrom || from );
          return self.waitTransitionEnd(target);
        }
    },
  
      /**
       * Listen for transitionEnd event (with optional timeout)
       * Announce completion or failure via promise handlers
       */
      waitTransitionEnd: function (element, opts) {
          var TIMEOUT = 3000; // fallback is 3 secs
  
          return $q(function(resolve, reject){
            opts = opts || { };
  
            var timer = $timeout(finished, opts.timeout || TIMEOUT);
            element.on($mdConstant.CSS.TRANSITIONEND, finished);
  
            /**
             * Upon timeout or transitionEnd, reject or resolve (respectively) this promise.
             * NOTE: Make sure this transitionEnd didn't bubble up from a child
             */
            function finished(ev) {
              if ( ev && ev.target !== element[0]) return;
  
              if ( ev  ) $timeout.cancel(timer);
              element.off($mdConstant.CSS.TRANSITIONEND, finished);
  
              // Never reject since ngAnimate may cause timeouts due missed transitionEnd events
              resolve();
  
            }
  
          });
        },
  
      /**
       * Calculate the zoom transform from dialog to origin.
       *
       * We use this to set the dialog position immediately;
       * then the md-transition-in actually translates back to
       * `translate3d(0,0,0) scale(1.0)`...
       *
       * NOTE: all values are rounded to the nearest integer
       */
      calculateZoomToOrigin: function (element, originator) {
        var origin = originator.element;
        var zoomTemplate = "translate3d( {centerX}px, {centerY}px, 0 ) scale( {scaleX}, {scaleY} )";
        var buildZoom = angular.bind(null, $mdUtil.supplant, zoomTemplate);
        var zoomStyle = buildZoom({centerX: 0, centerY: 0, scaleX: 0.5, scaleY: 0.5});
  
        if (origin) {
          var originBnds = self.clientRect(origin) || self.copyRect(originator.bounds);
          var dialogRect = self.copyRect(element[0].getBoundingClientRect());
          var dialogCenterPt = self.centerPointFor(dialogRect);
          var originCenterPt = self.centerPointFor(originBnds);
  
          // Build the transform to zoom from the dialog center to the origin center
  
          zoomStyle = buildZoom({
            centerX: originCenterPt.x - dialogCenterPt.x,
            centerY: originCenterPt.y - dialogCenterPt.y,
            scaleX: Math.min(0.5, originBnds.width / dialogRect.width),
            scaleY: Math.min(0.5, originBnds.height / dialogRect.height)
          });
        }
  
        return zoomStyle;
      },
  
      /**
       * Convert the translate CSS value to key/value pair(s).
       */
      toTransformCss: function (transform, addTransition) {
        var css = {};
        angular.forEach($mdConstant.CSS.TRANSFORM.split(' '), function (key) {
          css[key] = transform;
        });
  
        if (addTransition) css['transition'] = "all 0.4s cubic-bezier(0.25, 0.8, 0.25, 1) !important";
  
        return css;
      },
  
      /**
       *  Clone the Rect and calculate the height/width if needed
       */
      copyRect: function (source, destination) {
        if (!source) return null;
  
        destination = destination || {};
  
        angular.forEach('left top right bottom width height'.split(' '), function (key) {
          destination[key] = Math.round(source[key])
        });
  
        destination.width = destination.width || (destination.right - destination.left);
        destination.height = destination.height || (destination.bottom - destination.top);
  
        return destination;
      },
  
      /**
       * Calculate ClientRect of element; return null if hidden or zero size
       */
      clientRect: function (element) {
        var bounds = angular.element(element)[0].getBoundingClientRect();
        var isPositiveSizeClientRect = function (rect) {
          return rect && (rect.width > 0) && (rect.height > 0);
        };
  
        // If the event origin element has zero size, it has probably been hidden.
        return isPositiveSizeClientRect(bounds) ? self.copyRect(bounds) : null;
      },
  
      /**
       *  Calculate 'rounded' center point of Rect
       */
      centerPointFor: function (targetRect) {
        return {
          x: Math.round(targetRect.left + (targetRect.width / 2)),
          y: Math.round(targetRect.top + (targetRect.height / 2))
        }
      }
  
    };
  };
  
  
  angular.module('material.core')
  .factory('$mdConstant', MdConstantFactory);
  
  function MdConstantFactory($sniffer) {
  
    var webkit = /webkit/i.test($sniffer.vendorPrefix);
    function vendorProperty(name) {
      return webkit ?  ('webkit' + name.charAt(0).toUpperCase() + name.substring(1)) : name;
    }
  
    return {
      KEY_CODE: {
        ENTER: 13,
        ESCAPE: 27,
        SPACE: 32,
        LEFT_ARROW : 37,
        UP_ARROW : 38,
        RIGHT_ARROW : 39,
        DOWN_ARROW : 40,
        TAB : 9,
        BACKSPACE: 8,
        DELETE: 46
      },
      CSS: {
        /* Constants */
        TRANSITIONEND: 'transitionend' + (webkit ? ' webkitTransitionEnd' : ''),
        ANIMATIONEND: 'animationend' + (webkit ? ' webkitAnimationEnd' : ''),
  
        TRANSFORM: vendorProperty('transform'),
        TRANSFORM_ORIGIN: vendorProperty('transformOrigin'),
        TRANSITION: vendorProperty('transition'),
        TRANSITION_DURATION: vendorProperty('transitionDuration'),
        ANIMATION_PLAY_STATE: vendorProperty('animationPlayState'),
        ANIMATION_DURATION: vendorProperty('animationDuration'),
        ANIMATION_NAME: vendorProperty('animationName'),
        ANIMATION_TIMING: vendorProperty('animationTimingFunction'),
        ANIMATION_DIRECTION: vendorProperty('animationDirection')
      },
      MEDIA: {
        'sm': '(max-width: 599px)',
        'gt-sm': '(min-width: 600px)',
        'md': '(min-width: 600px) and (max-width: 959px)',
        'gt-md': '(min-width: 960px)',
        'lg': '(min-width: 960px) and (max-width: 1199px)',
        'gt-lg': '(min-width: 1200px)'
      },
      MEDIA_PRIORITY: [
        'gt-lg',
        'lg',
        'gt-md',
        'md',
        'gt-sm',
        'sm'
      ]
    };
  }
  MdConstantFactory.$inject = ["$sniffer"];
  
    angular
      .module('material.core')
      .config( ["$provide", function($provide){
         $provide.decorator('$mdUtil', ['$delegate', function ($delegate){
             /**
              * Inject the iterator facade to easily support iteration and accessors
              * @see iterator below
              */
             $delegate.iterator = MdIterator;
  
             return $delegate;
           }
         ]);
       }]);
  
    /**
     * iterator is a list facade to easily support iteration and accessors
     *
     * @param items Array list which this iterator will enumerate
     * @param reloop Boolean enables iterator to consider the list as an endless reloop
     */
    function MdIterator(items, reloop) {
      var trueFn = function() { return true; };
  
      if (items && !angular.isArray(items)) {
        items = Array.prototype.slice.call(items);
      }
  
      reloop = !!reloop;
      var _items = items || [ ];
  
      // Published API
      return {
        items: getItems,
        count: count,
  
        inRange: inRange,
        contains: contains,
        indexOf: indexOf,
        itemAt: itemAt,
  
        findBy: findBy,
  
        add: add,
        remove: remove,
  
        first: first,
        last: last,
        next: angular.bind(null, findSubsequentItem, false),
        previous: angular.bind(null, findSubsequentItem, true),
  
        hasPrevious: hasPrevious,
        hasNext: hasNext
  
      };
  
      /**
       * Publish copy of the enumerable set
       * @returns {Array|*}
       */
      function getItems() {
        return [].concat(_items);
      }
  
      /**
       * Determine length of the list
       * @returns {Array.length|*|number}
       */
      function count() {
        return _items.length;
      }
  
      /**
       * Is the index specified valid
       * @param index
       * @returns {Array.length|*|number|boolean}
       */
      function inRange(index) {
        return _items.length && ( index > -1 ) && (index < _items.length );
      }
  
      /**
       * Can the iterator proceed to the next item in the list; relative to
       * the specified item.
       *
       * @param item
       * @returns {Array.length|*|number|boolean}
       */
      function hasNext(item) {
        return item ? inRange(indexOf(item) + 1) : false;
      }
  
      /**
       * Can the iterator proceed to the previous item in the list; relative to
       * the specified item.
       *
       * @param item
       * @returns {Array.length|*|number|boolean}
       */
      function hasPrevious(item) {
        return item ? inRange(indexOf(item) - 1) : false;
      }
  
      /**
       * Get item at specified index/position
       * @param index
       * @returns {*}
       */
      function itemAt(index) {
        return inRange(index) ? _items[index] : null;
      }
  
      /**
       * Find all elements matching the key/value pair
       * otherwise return null
       *
       * @param val
       * @param key
       *
       * @return array
       */
      function findBy(key, val) {
        return _items.filter(function(item) {
          return item[key] === val;
        });
      }
  
      /**
       * Add item to list
       * @param item
       * @param index
       * @returns {*}
       */
      function add(item, index) {
        if ( !item ) return -1;
  
        if (!angular.isNumber(index)) {
          index = _items.length;
        }
  
        _items.splice(index, 0, item);
  
        return indexOf(item);
      }
  
      /**
       * Remove item from list...
       * @param item
       */
      function remove(item) {
        if ( contains(item) ){
          _items.splice(indexOf(item), 1);
        }
      }
  
      /**
       * Get the zero-based index of the target item
       * @param item
       * @returns {*}
       */
      function indexOf(item) {
        return _items.indexOf(item);
      }
  
      /**
       * Boolean existence check
       * @param item
       * @returns {boolean}
       */
      function contains(item) {
        return item && (indexOf(item) > -1);
      }
  
      /**
       * Return first item in the list
       * @returns {*}
       */
      function first() {
        return _items.length ? _items[0] : null;
      }
  
      /**
       * Return last item in the list...
       * @returns {*}
       */
      function last() {
        return _items.length ? _items[_items.length - 1] : null;
      }
  
      /**
       * Find the next item. If reloop is true and at the end of the list, it will go back to the
       * first item. If given, the `validate` callback will be used to determine whether the next item
       * is valid. If not valid, it will try to find the next item again.
       *
       * @param {boolean} backwards Specifies the direction of searching (forwards/backwards)
       * @param {*} item The item whose subsequent item we are looking for
       * @param {Function=} validate The `validate` function
       * @param {integer=} limit The recursion limit
       *
       * @returns {*} The subsequent item or null
       */
      function findSubsequentItem(backwards, item, validate, limit) {
        validate = validate || trueFn;
  
        var curIndex = indexOf(item);
        while (true) {
          if (!inRange(curIndex)) return null;
  
          var nextIndex = curIndex + (backwards ? -1 : 1);
          var foundItem = null;
          if (inRange(nextIndex)) {
            foundItem = _items[nextIndex];
          } else if (reloop) {
            foundItem = backwards ? last() : first();
            nextIndex = indexOf(foundItem);
          }
  
          if ((foundItem === null) || (nextIndex === limit)) return null;
          if (validate(foundItem)) return foundItem;
  
          if (angular.isUndefined(limit)) limit = nextIndex;
  
          curIndex = nextIndex;
        }
      }
    }
  
  
  angular.module('material.core')
  .factory('$mdMedia', mdMediaFactory);
  
  /**
   * @ngdoc service
   * @name $mdMedia
   * @module material.core
   *
   * @description
   * `$mdMedia` is used to evaluate whether a given media query is true or false given the
   * current device's screen / window size. The media query will be re-evaluated on resize, allowing
   * you to register a watch.
   *
   * `$mdMedia` also has pre-programmed support for media queries that match the layout breakpoints.
   *  (`sm`, `gt-sm`, `md`, `gt-md`, `lg`, `gt-lg`).
   *
   * @returns {boolean} a boolean representing whether or not the given media query is true or false.
   *
   * @usage
   * <hljs lang="js">
   * app.controller('MyController', function($mdMedia, $scope) {
   *   $scope.$watch(function() { return $mdMedia('lg'); }, function(big) {
   *     $scope.bigScreen = big;
   *   });
   *
   *   $scope.screenIsSmall = $mdMedia('sm');
   *   $scope.customQuery = $mdMedia('(min-width: 1234px)');
   *   $scope.anotherCustom = $mdMedia('max-width: 300px');
   * });
   * </hljs>
   */
  
  function mdMediaFactory($mdConstant, $rootScope, $window) {
    var queries = {};
    var mqls = {};
    var results = {};
    var normalizeCache = {};
  
    $mdMedia.getResponsiveAttribute = getResponsiveAttribute;
    $mdMedia.getQuery = getQuery;
    $mdMedia.watchResponsiveAttributes = watchResponsiveAttributes;
  
    return $mdMedia;
  
    function $mdMedia(query) {
      var validated = queries[query];
      if (angular.isUndefined(validated)) {
        validated = queries[query] = validate(query);
      }
  
      var result = results[validated];
      if (angular.isUndefined(result)) {
        result = add(validated);
      }
  
      return result;
    }
  
    function validate(query) {
      return $mdConstant.MEDIA[query] ||
             ((query.charAt(0) !== '(') ? ('(' + query + ')') : query);
    }
  
    function add(query) {
      var result = mqls[query] = $window.matchMedia(query);
      result.addListener(onQueryChange);
      return (results[result.media] = !!result.matches);
    }
  
    function onQueryChange(query) {
      $rootScope.$evalAsync(function() {
        results[query.media] = !!query.matches;
      });
    }
  
    function getQuery(name) {
      return mqls[name];
    }
  
    function getResponsiveAttribute(attrs, attrName) {
      for (var i = 0; i < $mdConstant.MEDIA_PRIORITY.length; i++) {
        var mediaName = $mdConstant.MEDIA_PRIORITY[i];
        if (!mqls[queries[mediaName]].matches) {
          continue;
        }
  
        var normalizedName = getNormalizedName(attrs, attrName + '-' + mediaName);
        if (attrs[normalizedName]) {
          return attrs[normalizedName];
        }
      }
  
      // fallback on unprefixed
      return attrs[getNormalizedName(attrs, attrName)];
    }
  
    function watchResponsiveAttributes(attrNames, attrs, watchFn) {
      var unwatchFns = [];
      attrNames.forEach(function(attrName) {
        var normalizedName = getNormalizedName(attrs, attrName);
        if (attrs[normalizedName]) {
          unwatchFns.push(
              attrs.$observe(normalizedName, angular.bind(void 0, watchFn, null)));
        }
  
        for (var mediaName in $mdConstant.MEDIA) {
          normalizedName = getNormalizedName(attrs, attrName + '-' + mediaName);
          if (!attrs[normalizedName]) {
            return;
          }
  
          unwatchFns.push(attrs.$observe(normalizedName, angular.bind(void 0, watchFn, mediaName)));
        }
      });
  
      return function unwatch() {
        unwatchFns.forEach(function(fn) { fn(); })
      };
    }
  
    // Improves performance dramatically
    function getNormalizedName(attrs, attrName) {
      return normalizeCache[attrName] ||
          (normalizeCache[attrName] = attrs.$normalize(attrName));
    }
  }
  mdMediaFactory.$inject = ["$mdConstant", "$rootScope", "$window"];
  
  /*
   * This var has to be outside the angular factory, otherwise when
   * there are multiple material apps on the same page, each app
   * will create its own instance of this array and the app's IDs
   * will not be unique.
   */
  var nextUniqueId = 0;
  
  angular.module('material.core')
    .factory('$mdUtil', ["$cacheFactory", "$document", "$timeout", "$q", "$compile", "$window", "$mdConstant", "$$rAF", "$rootScope", "$$mdAnimate", function ($cacheFactory, $document, $timeout, $q, $compile, $window, $mdConstant, $$rAF, $rootScope, $$mdAnimate) {
      var $mdUtil = {
            dom : { },
            now: window.performance ?
              angular.bind(window.performance, window.performance.now) :
              Date.now,
  
            clientRect: function (element, offsetParent, isOffsetRect) {
              var node = getNode(element);
              offsetParent = getNode(offsetParent || node.offsetParent || document.body);
              var nodeRect = node.getBoundingClientRect();
  
              // The user can ask for an offsetRect: a rect relative to the offsetParent,
              // or a clientRect: a rect relative to the page
              var offsetRect = isOffsetRect ?
                offsetParent.getBoundingClientRect() :
              {left: 0, top: 0, width: 0, height: 0};
              return {
                left: nodeRect.left - offsetRect.left,
                top: nodeRect.top - offsetRect.top,
                width: nodeRect.width,
                height: nodeRect.height
              };
            },
            offsetRect: function (element, offsetParent) {
              return $mdUtil.clientRect(element, offsetParent, true);
            },
  
            // Annoying method to copy nodes to an array, thanks to IE
            nodesToArray: function (nodes) {
              nodes = nodes || [ ];
  
              var results = [];
              for (var i = 0; i < nodes.length; ++i) {
                results.push(nodes.item(i));
              }
              return results;
            },
  
            /**
             * Calculate the positive scroll offset
             * TODO: Check with pinch-zoom in IE/Chrome;
             *       https://code.google.com/p/chromium/issues/detail?id=496285
             */
            scrollTop : function(element) {
              element = angular.element(element || $document[0].body);
  
              var body = (element[0] == $document[0].body) ? $document[0].body : undefined;
              var scrollTop = body ? body.scrollTop + body.parentElement.scrollTop : 0;
  
               // Calculate the positive scroll offset
              return scrollTop || Math.abs(element[0].getBoundingClientRect().top);
            },
  
            // Disables scroll around the passed element.
            disableScrollAround: function (element, parent) {
              $mdUtil.disableScrollAround._count = $mdUtil.disableScrollAround._count || 0;
              ++$mdUtil.disableScrollAround._count;
              if ($mdUtil.disableScrollAround._enableScrolling) return $mdUtil.disableScrollAround._enableScrolling;
              element = angular.element(element);
              var body = $document[0].body,
                restoreBody = disableBodyScroll(),
                restoreElement = disableElementScroll(parent);
  
              return $mdUtil.disableScrollAround._enableScrolling = function () {
                if (!--$mdUtil.disableScrollAround._count) {
                  restoreBody();
                  restoreElement();
                  delete $mdUtil.disableScrollAround._enableScrolling;
                }
              };
  
              // Creates a virtual scrolling mask to absorb touchmove, keyboard, scrollbar clicking, and wheel events
              function disableElementScroll(element) {
                element = angular.element(element || body)[0];
                var zIndex = 50;
                var scrollMask = angular.element(
                  '<div class="md-scroll-mask" style="z-index: ' + zIndex + '">' +
                  '  <div class="md-scroll-mask-bar"></div>' +
                  '</div>');
                element.appendChild(scrollMask[0]);
  
                scrollMask.on('wheel', preventDefault);
                scrollMask.on('touchmove', preventDefault);
                $document.on('keydown', disableKeyNav);
  
                return function restoreScroll() {
                  scrollMask.off('wheel');
                  scrollMask.off('touchmove');
                  scrollMask[0].parentNode.removeChild(scrollMask[0]);
                  $document.off('keydown', disableKeyNav);
                  delete $mdUtil.disableScrollAround._enableScrolling;
                };
  
                // Prevent keypresses from elements inside the body
                // used to stop the keypresses that could cause the page to scroll
                // (arrow keys, spacebar, tab, etc).
                function disableKeyNav(e) {
                  //-- temporarily removed this logic, will possibly re-add at a later date
                  return;
                  if (!element[0].contains(e.target)) {
                    e.preventDefault();
                    e.stopImmediatePropagation();
                  }
                }
  
                function preventDefault(e) {
                  e.preventDefault();
                }
              }
  
              // Converts the body to a position fixed block and translate it to the proper scroll
              // position
              function disableBodyScroll() {
                var htmlNode = body.parentNode;
                var restoreHtmlStyle = htmlNode.getAttribute('style') || '';
                var restoreBodyStyle = body.getAttribute('style') || '';
                var scrollOffset = $mdUtil.scrollTop(body);
                var clientWidth = body.clientWidth;
  
                if (body.scrollHeight > body.clientHeight) {
                  applyStyles(body, {
                    position: 'fixed',
                    width: '100%',
                    top: -scrollOffset + 'px'
                  });
  
                  applyStyles(htmlNode, {
                    overflowY: 'scroll'
                  });
                }
  
  
                if (body.clientWidth < clientWidth) applyStyles(body, {overflow: 'hidden'});
  
                return function restoreScroll() {
                  body.setAttribute('style', restoreBodyStyle);
                  htmlNode.setAttribute('style', restoreHtmlStyle);
                  body.scrollTop = scrollOffset;
                };
              }
  
              function applyStyles(el, styles) {
                for (var key in styles) {
                  el.style[key] = styles[key];
                }
              }
            },
            enableScrolling: function () {
              var method = this.disableScrollAround._enableScrolling;
              method && method();
            },
            floatingScrollbars: function () {
              if (this.floatingScrollbars.cached === undefined) {
                var tempNode = angular.element('<div style="width: 100%; z-index: -1; position: absolute; height: 35px; overflow-y: scroll"><div style="height: 60;"></div></div>');
                $document[0].body.appendChild(tempNode[0]);
                this.floatingScrollbars.cached = (tempNode[0].offsetWidth == tempNode[0].childNodes[0].offsetWidth);
                tempNode.remove();
              }
              return this.floatingScrollbars.cached;
            },
  
            // Mobile safari only allows you to set focus in click event listeners...
            forceFocus: function (element) {
              var node = element[0] || element;
  
              document.addEventListener('click', function focusOnClick(ev) {
                if (ev.target === node && ev.$focus) {
                  node.focus();
                  ev.stopImmediatePropagation();
                  ev.preventDefault();
                  node.removeEventListener('click', focusOnClick);
                }
              }, true);
  
              var newEvent = document.createEvent('MouseEvents');
              newEvent.initMouseEvent('click', false, true, window, {}, 0, 0, 0, 0,
                false, false, false, false, 0, null);
              newEvent.$material = true;
              newEvent.$focus = true;
              node.dispatchEvent(newEvent);
            },
  
            /**
             * facade to build md-backdrop element with desired styles
             * NOTE: Use $compile to trigger backdrop postLink function
             */
            createBackdrop : function(scope, addClass){
              return $compile( $mdUtil.supplant('<md-backdrop class="{0}">',[addClass]) )(scope);
            },
  
            /**
             * supplant() method from Crockford's `Remedial Javascript`
             * Equivalent to use of $interpolate; without dependency on
             * interpolation symbols and scope. Note: the '{<token>}' can
             * be property names, property chains, or array indices.
             */
            supplant : function( template, values, pattern ) {
                pattern = pattern || /\{([^\{\}]*)\}/g;
                return template.replace(pattern, function(a, b) {
                    var p = b.split('.'),
                        r = values;
                    try {
                        for (var s in p) { r = r[p[s]];  }
                    } catch(e){
                        r = a;
                    }
                    return (typeof r === 'string' || typeof r === 'number') ? r : a;
                });
            },
  
            fakeNgModel: function () {
              return {
                $fake: true,
                $setTouched: angular.noop,
                $setViewValue: function (value) {
                  this.$viewValue = value;
                  this.$render(value);
                  this.$viewChangeListeners.forEach(function (cb) {
                    cb();
                  });
                },
                $isEmpty: function (value) {
                  return ('' + value).length === 0;
                },
                $parsers: [],
                $formatters: [],
                $viewChangeListeners: [],
                $render: angular.noop
              };
            },
  
            // Returns a function, that, as long as it continues to be invoked, will not
            // be triggered. The function will be called after it stops being called for
            // N milliseconds.
            // @param wait Integer value of msecs to delay (since last debounce reset); default value 10 msecs
            // @param invokeApply should the $timeout trigger $digest() dirty checking
            debounce: function (func, wait, scope, invokeApply) {
              var timer;
  
              return function debounced() {
                var context = scope,
                  args = Array.prototype.slice.call(arguments);
  
                $timeout.cancel(timer);
                timer = $timeout(function () {
  
                  timer = undefined;
                  func.apply(context, args);
  
                }, wait || 10, invokeApply);
              };
            },
  
            // Returns a function that can only be triggered every `delay` milliseconds.
            // In other words, the function will not be called unless it has been more
            // than `delay` milliseconds since the last call.
            throttle: function throttle(func, delay) {
              var recent;
              return function throttled() {
                var context = this;
                var args = arguments;
                var now = $mdUtil.now();
  
                if (!recent || (now - recent > delay)) {
                  func.apply(context, args);
                  recent = now;
                }
              };
            },
  
            /**
             * Measures the number of milliseconds taken to run the provided callback
             * function. Uses a high-precision timer if available.
             */
            time: function time(cb) {
              var start = $mdUtil.now();
              cb();
              return $mdUtil.now() - start;
            },
  
            /**
             * Get a unique ID.
             *
             * @returns {string} an unique numeric string
             */
            nextUid: function () {
              return '' + nextUniqueId++;
            },
  
            // Stop watchers and events from firing on a scope without destroying it,
            // by disconnecting it from its parent and its siblings' linked lists.
            disconnectScope: function disconnectScope(scope) {
              if (!scope) return;
  
              // we can't destroy the root scope or a scope that has been already destroyed
              if (scope.$root === scope) return;
              if (scope.$$destroyed) return;
  
              var parent = scope.$parent;
              scope.$$disconnected = true;
  
              // See Scope.$destroy
              if (parent.$$childHead === scope) parent.$$childHead = scope.$$nextSibling;
              if (parent.$$childTail === scope) parent.$$childTail = scope.$$prevSibling;
              if (scope.$$prevSibling) scope.$$prevSibling.$$nextSibling = scope.$$nextSibling;
              if (scope.$$nextSibling) scope.$$nextSibling.$$prevSibling = scope.$$prevSibling;
  
              scope.$$nextSibling = scope.$$prevSibling = null;
  
            },
  
            // Undo the effects of disconnectScope above.
            reconnectScope: function reconnectScope(scope) {
              if (!scope) return;
  
              // we can't disconnect the root node or scope already disconnected
              if (scope.$root === scope) return;
              if (!scope.$$disconnected) return;
  
              var child = scope;
  
              var parent = child.$parent;
              child.$$disconnected = false;
              // See Scope.$new for this logic...
              child.$$prevSibling = parent.$$childTail;
              if (parent.$$childHead) {
                parent.$$childTail.$$nextSibling = child;
                parent.$$childTail = child;
              } else {
                parent.$$childHead = parent.$$childTail = child;
              }
            },
  
            /*
             * getClosest replicates jQuery.closest() to walk up the DOM tree until it finds a matching nodeName
             *
             * @param el Element to start walking the DOM from
             * @param tagName Tag name to find closest to el, such as 'form'
             */
            getClosest: function getClosest(el, tagName, onlyParent) {
              if (el instanceof angular.element) el = el[0];
              tagName = tagName.toUpperCase();
              if (onlyParent) el = el.parentNode;
              if (!el) return null;
              do {
                if (el.nodeName === tagName) {
                  return el;
                }
              } while (el = el.parentNode);
              return null;
            },
  
            /**
             * Functional equivalent for $element.filter(‘md-bottom-sheet’)
             * useful with interimElements where the element and its container are important...
             */
            extractElementByName: function (element, nodeName) {
              for (var i = 0, len = element.length; i < len; i++) {
                if (element[i].nodeName.toLowerCase() === nodeName) {
                  return angular.element(element[i]);
                }
              }
              return element;
            },
  
            /**
             * Give optional properties with no value a boolean true if attr provided or false otherwise
             */
            initOptionalProperties: function (scope, attr, defaults) {
              defaults = defaults || {};
              angular.forEach(scope.$$isolateBindings, function (binding, key) {
                if (binding.optional && angular.isUndefined(scope[key])) {
                  var attrIsDefined = angular.isDefined(attr[binding.attrName]);
                  scope[key] = angular.isDefined(defaults[key]) ? defaults[key] : attrIsDefined;
                }
              });
            },
  
            /**
             * Alternative to $timeout calls with 0 delay.
             * nextTick() coalesces all calls within a single frame
             * to minimize $digest thrashing
             *
             * @param callback
             * @param digest
             * @returns {*}
             */
            nextTick: function (callback, digest) {
              //-- grab function reference for storing state details
              var nextTick = $mdUtil.nextTick;
              var timeout = nextTick.timeout;
              var queue = nextTick.queue || [];
  
              //-- add callback to the queue
              queue.push(callback);
  
              //-- set default value for digest
              if (digest == null) digest = true;
  
              //-- store updated digest/queue values
              nextTick.digest = nextTick.digest || digest;
              nextTick.queue = queue;
  
              //-- either return existing timeout or create a new one
              return timeout || (nextTick.timeout = $timeout(processQueue, 0, false));
  
              /**
               * Grab a copy of the current queue
               * Clear the queue for future use
               * Process the existing queue
               * Trigger digest if necessary
               */
              function processQueue () {
                var queue = nextTick.queue;
                var digest = nextTick.digest;
  
                nextTick.queue = [];
                nextTick.timeout = null;
                nextTick.digest = false;
  
                queue.forEach(function (callback) { callback(); });
  
                if (digest) $rootScope.$digest();
              }
            }
  
          };
  
      // Instantiate other namespace utility methods
  
      $mdUtil.dom.animator = $$mdAnimate($mdUtil);
  
      return $mdUtil;
  
      function getNode(el) {
        return el[0] || el;
      }
  
    }]);
  
  /*
   * Since removing jQuery from the demos, some code that uses `element.focus()` is broken.
   *
   * We need to add `element.focus()`, because it's testable unlike `element[0].focus`.
   *
   * TODO(ajoslin): This should be added in a better place later.
   */
  
  angular.element.prototype.focus = angular.element.prototype.focus || function () {
      if (this.length) {
        this[0].focus();
      }
      return this;
    };
  angular.element.prototype.blur = angular.element.prototype.blur || function () {
      if (this.length) {
        this[0].blur();
      }
      return this;
    };
  
  
  angular.module('material.core')
    .service('$mdAria', AriaService);
  
  /*
   * ngInject
   */
  function AriaService($$rAF, $log, $window) {
  
    return {
      expect: expect,
      expectAsync: expectAsync,
      expectWithText: expectWithText
    };
  
    /**
     * Check if expected attribute has been specified on the target element or child
     * @param element
     * @param attrName
     * @param {optional} defaultValue What to set the attr to if no value is found
     */
    function expect(element, attrName, defaultValue) {
      var node = element[0] || element;
  
      // if node exists and neither it nor its children have the attribute
      if (node &&
         ((!node.hasAttribute(attrName) ||
          node.getAttribute(attrName).length === 0) &&
          !childHasAttribute(node, attrName))) {
  
        defaultValue = angular.isString(defaultValue) ? defaultValue.trim() : '';
        if (defaultValue.length) {
          element.attr(attrName, defaultValue);
        } else {
          $log.warn('ARIA: Attribute "', attrName, '", required for accessibility, is missing on node:', node);
        }
  
      }
    }
  
    function expectAsync(element, attrName, defaultValueGetter) {
      // Problem: when retrieving the element's contents synchronously to find the label,
      // the text may not be defined yet in the case of a binding.
      // There is a higher chance that a binding will be defined if we wait one frame.
      $$rAF(function() {
        expect(element, attrName, defaultValueGetter());
      });
    }
  
    function expectWithText(element, attrName) {
      expectAsync(element, attrName, function() {
        return getText(element);
      });
    }
  
    function getText(element) {
      return element.text().trim();
    }
  
    function childHasAttribute(node, attrName) {
      var hasChildren = node.hasChildNodes(),
          hasAttr = false;
  
      function isHidden(el) {
        var style = el.currentStyle ? el.currentStyle : $window.getComputedStyle(el);
        return (style.display === 'none');
      }
  
      if(hasChildren) {
        var children = node.childNodes;
        for(var i=0; i<children.length; i++){
          var child = children[i];
          if(child.nodeType === 1 && child.hasAttribute(attrName)) {
            if(!isHidden(child)){
              hasAttr = true;
            }
          }
        }
      }
      return hasAttr;
    }
  }
  AriaService.$inject = ["$$rAF", "$log", "$window"];
  
    var HANDLERS = {};
    /* The state of the current 'pointer'
     * The pointer represents the state of the current touch.
     * It contains normalized x and y coordinates from DOM events,
     * as well as other information abstracted from the DOM.
     */
    var pointer, lastPointer, forceSkipClickHijack = false;
  
    // Used to attach event listeners once when multiple ng-apps are running.
    var isInitialized = false;
    
    angular
      .module('material.core.gestures', [ ])
      .provider('$mdGesture', MdGestureProvider)
      .factory('$$MdGestureHandler', MdGestureHandler)
      .run( attachToDocument );
  
    /**
       * @ngdoc service
       * @name $mdGestureProvider
       * @module material.core.gestures
       *
       * @description
       * In some scenarios on Mobile devices (without jQuery), the click events should NOT be hijacked.
       * `$mdGestureProvider` is used to configure the Gesture module to ignore or skip click hijacking on mobile
       * devices.
       *
       * <hljs lang="js">
       *   app.config(function($mdGestureProvider) {
       *
       *     // For mobile devices without jQuery loaded, do not
       *     // intercept click events during the capture phase.
       *     $mdGestureProvider.skipClickHijack();
       *
       *   });
       * </hljs>
       *
       */
    function MdGestureProvider() { }
  
    MdGestureProvider.prototype = {
  
      // Publish access to setter to configure a variable  BEFORE the
      // $mdGesture service is instantiated...
      skipClickHijack: function() {
        return forceSkipClickHijack = true;
      },
  
      /**
       * $get is used to build an instance of $mdGesture
       * ngInject
       */
      $get : ["$$MdGestureHandler", "$$rAF", "$timeout", function($$MdGestureHandler, $$rAF, $timeout) {
           return new MdGesture($$MdGestureHandler, $$rAF, $timeout);
      }]
    };
  
  
  
    /**
     * MdGesture factory construction function
     * ngInject
     */
    function MdGesture($$MdGestureHandler, $$rAF, $timeout) {
      var userAgent = navigator.userAgent || navigator.vendor || window.opera;
      var isIos = userAgent.match(/ipad|iphone|ipod/i);
      var isAndroid = userAgent.match(/android/i);
      var hasJQuery =  (typeof window.jQuery !== 'undefined') && (angular.element === window.jQuery);
  
      var self = {
        handler: addHandler,
        register: register,
        // On mobile w/out jQuery, we normally intercept clicks. Should we skip that?
        isHijackingClicks: (isIos || isAndroid) && !hasJQuery && !forceSkipClickHijack
      };
  
      if (self.isHijackingClicks) {
        self.handler('click', {
          options: {
            maxDistance: 6
          },
          onEnd: function (ev, pointer) {
            if (pointer.distance < this.state.options.maxDistance) {
              this.dispatchEvent(ev, 'click');
            }
          }
        });
      }
  
      /*
       * Register an element to listen for a handler.
       * This allows an element to override the default options for a handler.
       * Additionally, some handlers like drag and hold only dispatch events if
       * the domEvent happens inside an element that's registered to listen for these events.
       *
       * @see GestureHandler for how overriding of default options works.
       * @example $mdGesture.register(myElement, 'drag', { minDistance: 20, horziontal: false })
       */
      function register(element, handlerName, options) {
        var handler = HANDLERS[handlerName.replace(/^\$md./, '')];
        if (!handler) {
          throw new Error('Failed to register element with handler ' + handlerName + '. ' +
          'Available handlers: ' + Object.keys(HANDLERS).join(', '));
        }
        return handler.registerElement(element, options);
      }
  
      /*
       * add a handler to $mdGesture. see below.
       */
      function addHandler(name, definition) {
        var handler = new $$MdGestureHandler(name);
        angular.extend(handler, definition);
        HANDLERS[name] = handler;
  
        return self;
      }
  
      /*
       * Register handlers. These listen to touch/start/move events, interpret them,
       * and dispatch gesture events depending on options & conditions. These are all
       * instances of GestureHandler.
       * @see GestureHandler 
       */
      return self
        /*
         * The press handler dispatches an event on touchdown/touchend.
         * It's a simple abstraction of touch/mouse/pointer start and end.
         */
        .handler('press', {
          onStart: function (ev, pointer) {
            this.dispatchEvent(ev, '$md.pressdown');
          },
          onEnd: function (ev, pointer) {
            this.dispatchEvent(ev, '$md.pressup');
          }
        })
  
        /*
         * The hold handler dispatches an event if the user keeps their finger within
         * the same <maxDistance> area for <delay> ms.
         * The hold handler will only run if a parent of the touch target is registered
         * to listen for hold events through $mdGesture.register()
         */
        .handler('hold', {
          options: {
            maxDistance: 6,
            delay: 500
          },
          onCancel: function () {
            $timeout.cancel(this.state.timeout);
          },
          onStart: function (ev, pointer) {
            // For hold, require a parent to be registered with $mdGesture.register()
            // Because we prevent scroll events, this is necessary.
            if (!this.state.registeredParent) return this.cancel();
  
            this.state.pos = {x: pointer.x, y: pointer.y};
            this.state.timeout = $timeout(angular.bind(this, function holdDelayFn() {
              this.dispatchEvent(ev, '$md.hold');
              this.cancel(); //we're done!
            }), this.state.options.delay, false);
          },
          onMove: function (ev, pointer) {
            // Don't scroll while waiting for hold.
            // If we don't preventDefault touchmove events here, Android will assume we don't
            // want to listen to anymore touch events. It will start scrolling and stop sending
            // touchmove events.
            ev.preventDefault();
  
            // If the user moves greater than <maxDistance> pixels, stop the hold timer
            // set in onStart
            var dx = this.state.pos.x - pointer.x;
            var dy = this.state.pos.y - pointer.y;
            if (Math.sqrt(dx * dx + dy * dy) > this.options.maxDistance) {
              this.cancel();
            }
          },
          onEnd: function () {
            this.onCancel();
          }
        })
  
        /*
         * The drag handler dispatches a drag event if the user holds and moves his finger greater than
         * <minDistance> px in the x or y direction, depending on options.horizontal.
         * The drag will be cancelled if the user moves his finger greater than <minDistance>*<cancelMultiplier> in
         * the perpindicular direction. Eg if the drag is horizontal and the user moves his finger <minDistance>*<cancelMultiplier>
         * pixels vertically, this handler won't consider the move part of a drag.
         */
        .handler('drag', {
          options: {
            minDistance: 6,
            horizontal: true,
            cancelMultiplier: 1.5
          },
          onStart: function (ev) {
            // For drag, require a parent to be registered with $mdGesture.register()
            if (!this.state.registeredParent) this.cancel();
          },
          onMove: function (ev, pointer) {
            var shouldStartDrag, shouldCancel;
            // Don't scroll while deciding if this touchmove qualifies as a drag event.
            // If we don't preventDefault touchmove events here, Android will assume we don't
            // want to listen to anymore touch events. It will start scrolling and stop sending
            // touchmove events.
            ev.preventDefault();
  
            if (!this.state.dragPointer) {
              if (this.state.options.horizontal) {
                shouldStartDrag = Math.abs(pointer.distanceX) > this.state.options.minDistance;
                shouldCancel = Math.abs(pointer.distanceY) > this.state.options.minDistance * this.state.options.cancelMultiplier;
              } else {
                shouldStartDrag = Math.abs(pointer.distanceY) > this.state.options.minDistance;
                shouldCancel = Math.abs(pointer.distanceX) > this.state.options.minDistance * this.state.options.cancelMultiplier;
              }
  
              if (shouldStartDrag) {
                // Create a new pointer representing this drag, starting at this point where the drag started.
                this.state.dragPointer = makeStartPointer(ev);
                updatePointerState(ev, this.state.dragPointer);
                this.dispatchEvent(ev, '$md.dragstart', this.state.dragPointer);
  
              } else if (shouldCancel) {
                this.cancel();
              }
            } else {
              this.dispatchDragMove(ev);
            }
          },
          // Only dispatch dragmove events every frame; any more is unnecessray
          dispatchDragMove: $$rAF.throttle(function (ev) {
            // Make sure the drag didn't stop while waiting for the next frame
            if (this.state.isRunning) {
              updatePointerState(ev, this.state.dragPointer);
              this.dispatchEvent(ev, '$md.drag', this.state.dragPointer);
            }
          }),
          onEnd: function (ev, pointer) {
            if (this.state.dragPointer) {
              updatePointerState(ev, this.state.dragPointer);
              this.dispatchEvent(ev, '$md.dragend', this.state.dragPointer);
            }
          }
        })
  
        /*
         * The swipe handler will dispatch a swipe event if, on the end of a touch,
         * the velocity and distance were high enough.
         * TODO: add vertical swiping with a `horizontal` option similar to the drag handler.
         */
        .handler('swipe', {
          options: {
            minVelocity: 0.65,
            minDistance: 10
          },
          onEnd: function (ev, pointer) {
            if (Math.abs(pointer.velocityX) > this.state.options.minVelocity &&
              Math.abs(pointer.distanceX) > this.state.options.minDistance) {
              var eventType = pointer.directionX == 'left' ? '$md.swipeleft' : '$md.swiperight';
              this.dispatchEvent(ev, eventType);
            }
          }
        });
  
    }
    MdGesture.$inject = ["$$MdGestureHandler", "$$rAF", "$timeout"];
  
    /**
     * MdGestureHandler
     * A GestureHandler is an object which is able to dispatch custom dom events
     * based on native dom {touch,pointer,mouse}{start,move,end} events.
     *
     * A gesture will manage its lifecycle through the start,move,end, and cancel
     * functions, which are called by native dom events.
     *
     * A gesture has the concept of 'options' (eg a swipe's required velocity), which can be
     * overridden by elements registering through $mdGesture.register()
     */
    function GestureHandler (name) {
      this.name = name;
      this.state = {};
    }
  
    function MdGestureHandler() {
      var hasJQuery =  (typeof window.jQuery !== 'undefined') && (angular.element === window.jQuery);
  
      GestureHandler.prototype = {
        options: {},
        // jQuery listeners don't work with custom DOMEvents, so we have to dispatch events
        // differently when jQuery is loaded
        dispatchEvent: hasJQuery ?  jQueryDispatchEvent : nativeDispatchEvent,
  
        // These are overridden by the registered handler
        onStart: angular.noop,
        onMove: angular.noop,
        onEnd: angular.noop,
        onCancel: angular.noop,
  
        // onStart sets up a new state for the handler, which includes options from the
        // nearest registered parent element of ev.target.
        start: function (ev, pointer) {
          if (this.state.isRunning) return;
          var parentTarget = this.getNearestParent(ev.target);
          // Get the options from the nearest registered parent
          var parentTargetOptions = parentTarget && parentTarget.$mdGesture[this.name] || {};
  
          this.state = {
            isRunning: true,
            // Override the default options with the nearest registered parent's options
            options: angular.extend({}, this.options, parentTargetOptions),
            // Pass in the registered parent node to the state so the onStart listener can use
            registeredParent: parentTarget
          };
          this.onStart(ev, pointer);
        },
        move: function (ev, pointer) {
          if (!this.state.isRunning) return;
          this.onMove(ev, pointer);
        },
        end: function (ev, pointer) {
          if (!this.state.isRunning) return;
          this.onEnd(ev, pointer);
          this.state.isRunning = false;
        },
        cancel: function (ev, pointer) {
          this.onCancel(ev, pointer);
          this.state = {};
        },
  
        // Find and return the nearest parent element that has been registered to
        // listen for this handler via $mdGesture.register(element, 'handlerName').
        getNearestParent: function (node) {
          var current = node;
          while (current) {
            if ((current.$mdGesture || {})[this.name]) {
              return current;
            }
            current = current.parentNode;
          }
          return null;
        },
  
        // Called from $mdGesture.register when an element reigsters itself with a handler.
        // Store the options the user gave on the DOMElement itself. These options will
        // be retrieved with getNearestParent when the handler starts.
        registerElement: function (element, options) {
          var self = this;
          element[0].$mdGesture = element[0].$mdGesture || {};
          element[0].$mdGesture[this.name] = options || {};
          element.on('$destroy', onDestroy);
  
          return onDestroy;
  
          function onDestroy() {
            delete element[0].$mdGesture[self.name];
            element.off('$destroy', onDestroy);
          }
        }
      };
  
      return GestureHandler;
  
      /*
       * Dispatch an event with jQuery
       * TODO: Make sure this sends bubbling events
       *
       * @param srcEvent the original DOM touch event that started this.
       * @param eventType the name of the custom event to send (eg 'click' or '$md.drag')
       * @param eventPointer the pointer object that matches this event.
       */
      function jQueryDispatchEvent(srcEvent, eventType, eventPointer) {
        eventPointer = eventPointer || pointer;
        var eventObj = new angular.element.Event(eventType);
  
        eventObj.$material = true;
        eventObj.pointer = eventPointer;
        eventObj.srcEvent = srcEvent;
  
        angular.extend(eventObj, {
          clientX: eventPointer.x,
          clientY: eventPointer.y,
          screenX: eventPointer.x,
          screenY: eventPointer.y,
          pageX: eventPointer.x,
          pageY: eventPointer.y,
          ctrlKey: srcEvent.ctrlKey,
          altKey: srcEvent.altKey,
          shiftKey: srcEvent.shiftKey,
          metaKey: srcEvent.metaKey
        });
        angular.element(eventPointer.target).trigger(eventObj);
      }
  
      /*
       * NOTE: nativeDispatchEvent is very performance sensitive.
       * @param srcEvent the original DOM touch event that started this.
       * @param eventType the name of the custom event to send (eg 'click' or '$md.drag')
       * @param eventPointer the pointer object that matches this event.
       */
      function nativeDispatchEvent(srcEvent, eventType, eventPointer) {
        eventPointer = eventPointer || pointer;
        var eventObj;
  
        if (eventType === 'click') {
          eventObj = document.createEvent('MouseEvents');
          eventObj.initMouseEvent(
            'click', true, true, window, srcEvent.detail,
            eventPointer.x, eventPointer.y, eventPointer.x, eventPointer.y,
            srcEvent.ctrlKey, srcEvent.altKey, srcEvent.shiftKey, srcEvent.metaKey,
            srcEvent.button, srcEvent.relatedTarget || null
          );
  
        } else {
          eventObj = document.createEvent('CustomEvent');
          eventObj.initCustomEvent(eventType, true, true, {});
        }
        eventObj.$material = true;
        eventObj.pointer = eventPointer;
        eventObj.srcEvent = srcEvent;
        eventPointer.target.dispatchEvent(eventObj);
      }
  
    }
  
    /**
     * Attach Gestures: hook document and check shouldHijack clicks
     * ngInject
     */
    function attachToDocument( $mdGesture, $$MdGestureHandler ) {
  
      // Polyfill document.contains for IE11.
      // TODO: move to util
      document.contains || (document.contains = function (node) {
        return document.body.contains(node);
      });
  
      if (!isInitialized && $mdGesture.isHijackingClicks ) {
        /*
         * If hijack clicks is true, we preventDefault any click that wasn't
         * sent by ngMaterial. This is because on older Android & iOS, a false, or 'ghost',
         * click event will be sent ~400ms after a touchend event happens.
         * The only way to know if this click is real is to prevent any normal
         * click events, and add a flag to events sent by material so we know not to prevent those.
         * 
         * Two exceptions to click events that should be prevented are:
         *  - click events sent by the keyboard (eg form submit)
         *  - events that originate from an Ionic app
         */
        document.addEventListener('click', function clickHijacker(ev) {
          var isKeyClick = ev.clientX === 0 && ev.clientY === 0;
          if (!isKeyClick && !ev.$material && !ev.isIonicTap) {
            ev.preventDefault();
            ev.stopPropagation();
          }
        }, true);
        
        isInitialized = true;
      }
  
      // Listen to all events to cover all platforms.
      var START_EVENTS = 'mousedown touchstart pointerdown';
      var MOVE_EVENTS = 'mousemove touchmove pointermove';
      var END_EVENTS = 'mouseup mouseleave touchend touchcancel pointerup pointercancel';
  
      angular.element(document)
        .on(START_EVENTS, gestureStart)
        .on(MOVE_EVENTS, gestureMove)
        .on(END_EVENTS, gestureEnd)
        // For testing
        .on('$$mdGestureReset', function gestureClearCache () {
          lastPointer = pointer = null;
        });
  
      /*
       * When a DOM event happens, run all registered gesture handlers' lifecycle
       * methods which match the DOM event.
       * Eg when a 'touchstart' event happens, runHandlers('start') will call and
       * run `handler.cancel()` and `handler.start()` on all registered handlers.
       */
      function runHandlers(handlerEvent, event) {
        var handler;
        for (var name in HANDLERS) {
          handler = HANDLERS[name];
          if( handler instanceof $$MdGestureHandler ) {
  
            if (handlerEvent === 'start') {
              // Run cancel to reset any handlers' state
              handler.cancel();
            }
            handler[handlerEvent](event, pointer);
  
          }
        }
      }
  
      /*
       * gestureStart vets if a start event is legitimate (and not part of a 'ghost click' from iOS/Android)
       * If it is legitimate, we initiate the pointer state and mark the current pointer's type
       * For example, for a touchstart event, mark the current pointer as a 'touch' pointer, so mouse events
       * won't effect it.
       */
      function gestureStart(ev) {
        // If we're already touched down, abort
        if (pointer) return;
  
        var now = +Date.now();
  
        // iOS & old android bug: after a touch event, a click event is sent 350 ms later.
        // If <400ms have passed, don't allow an event of a different type than the previous event
        if (lastPointer && !typesMatch(ev, lastPointer) && (now - lastPointer.endTime < 1500)) {
          return;
        }
  
        pointer = makeStartPointer(ev);
  
        runHandlers('start', ev);
      }
      /*
       * If a move event happens of the right type, update the pointer and run all the move handlers.
       * "of the right type": if a mousemove happens but our pointer started with a touch event, do nothing.
       */
      function gestureMove(ev) {
        if (!pointer || !typesMatch(ev, pointer)) return;
  
        updatePointerState(ev, pointer);
        runHandlers('move', ev);
      }
      /*
       * If an end event happens of the right type, update the pointer, run endHandlers, and save the pointer as 'lastPointer'
       */
      function gestureEnd(ev) {
        if (!pointer || !typesMatch(ev, pointer)) return;
  
        updatePointerState(ev, pointer);
        pointer.endTime = +Date.now();
  
        runHandlers('end', ev);
  
        lastPointer = pointer;
        pointer = null;
      }
  
    }
    attachToDocument.$inject = ["$mdGesture", "$$MdGestureHandler"];
  
    // ********************
    // Module Functions
    // ********************
  
    /*
     * Initiate the pointer. x, y, and the pointer's type.
     */
    function makeStartPointer(ev) {
      var point = getEventPoint(ev);
      var startPointer = {
        startTime: +Date.now(),
        target: ev.target,
        // 'p' for pointer events, 'm' for mouse, 't' for touch
        type: ev.type.charAt(0)
      };
      startPointer.startX = startPointer.x = point.pageX;
      startPointer.startY = startPointer.y = point.pageY;
      return startPointer;
    }
  
    /*
     * return whether the pointer's type matches the event's type.
     * Eg if a touch event happens but the pointer has a mouse type, return false.
     */
    function typesMatch(ev, pointer) {
      return ev && pointer && ev.type.charAt(0) === pointer.type;
    }
  
    /*
     * Update the given pointer based upon the given DOMEvent.
     * Distance, velocity, direction, duration, etc
     */
    function updatePointerState(ev, pointer) {
      var point = getEventPoint(ev);
      var x = pointer.x = point.pageX;
      var y = pointer.y = point.pageY;
  
      pointer.distanceX = x - pointer.startX;
      pointer.distanceY = y - pointer.startY;
      pointer.distance = Math.sqrt(
        pointer.distanceX * pointer.distanceX + pointer.distanceY * pointer.distanceY
      );
  
      pointer.directionX = pointer.distanceX > 0 ? 'right' : pointer.distanceX < 0 ? 'left' : '';
      pointer.directionY = pointer.distanceY > 0 ? 'up' : pointer.distanceY < 0 ? 'down' : '';
  
      pointer.duration = +Date.now() - pointer.startTime;
      pointer.velocityX = pointer.distanceX / pointer.duration;
      pointer.velocityY = pointer.distanceY / pointer.duration;
    }
  
    /*
     * Normalize the point where the DOM event happened whether it's touch or mouse.
     * @returns point event obj with pageX and pageY on it.
     */
    function getEventPoint(ev) {
      ev = ev.originalEvent || ev; // support jQuery events
      return (ev.touches && ev.touches[0]) ||
        (ev.changedTouches && ev.changedTouches[0]) ||
        ev;
    }
  
  angular
    .module('material.core')
    .service('$mdCompiler', mdCompilerService);
  
  function mdCompilerService($q, $http, $injector, $compile, $controller, $templateCache) {
    /* jshint validthis: true */
  
    /*
     * @ngdoc service
     * @name $mdCompiler
     * @module material.core
     * @description
     * The $mdCompiler service is an abstraction of angular's compiler, that allows the developer
     * to easily compile an element with a templateUrl, controller, and locals.
     *
     * @usage
     * <hljs lang="js">
     * $mdCompiler.compile({
     *   templateUrl: 'modal.html',
     *   controller: 'ModalCtrl',
     *   locals: {
     *     modal: myModalInstance;
     *   }
     * }).then(function(compileData) {
     *   compileData.element; // modal.html's template in an element
     *   compileData.link(myScope); //attach controller & scope to element
     * });
     * </hljs>
     */
  
     /*
      * @ngdoc method
      * @name $mdCompiler#compile
      * @description A helper to compile an HTML template/templateUrl with a given controller,
      * locals, and scope.
      * @param {object} options An options object, with the following properties:
      *
      *    - `controller` - `{(string=|function()=}` Controller fn that should be associated with
      *      newly created scope or the name of a registered controller if passed as a string.
      *    - `controllerAs` - `{string=}` A controller alias name. If present the controller will be
      *      published to scope under the `controllerAs` name.
      *    - `template` - `{string=}` An html template as a string.
      *    - `templateUrl` - `{string=}` A path to an html template.
      *    - `transformTemplate` - `{function(template)=}` A function which transforms the template after
      *      it is loaded. It will be given the template string as a parameter, and should
      *      return a a new string representing the transformed template.
      *    - `resolve` - `{Object.<string, function>=}` - An optional map of dependencies which should
      *      be injected into the controller. If any of these dependencies are promises, the compiler
      *      will wait for them all to be resolved, or if one is rejected before the controller is
      *      instantiated `compile()` will fail..
      *      * `key` - `{string}`: a name of a dependency to be injected into the controller.
      *      * `factory` - `{string|function}`: If `string` then it is an alias for a service.
      *        Otherwise if function, then it is injected and the return value is treated as the
      *        dependency. If the result is a promise, it is resolved before its value is 
      *        injected into the controller.
      *
      * @returns {object=} promise A promise, which will be resolved with a `compileData` object.
      * `compileData` has the following properties: 
      *
      *   - `element` - `{element}`: an uncompiled element matching the provided template.
      *   - `link` - `{function(scope)}`: A link function, which, when called, will compile
      *     the element and instantiate the provided controller (if given).
      *   - `locals` - `{object}`: The locals which will be passed into the controller once `link` is
      *     called. If `bindToController` is true, they will be coppied to the ctrl instead
      *   - `bindToController` - `bool`: bind the locals to the controller, instead of passing them in.
      */
    this.compile = function(options) {
      var templateUrl = options.templateUrl;
      var template = options.template || '';
      var controller = options.controller;
      var controllerAs = options.controllerAs;
      var resolve = angular.extend({}, options.resolve || {});
      var locals = angular.extend({}, options.locals || {});
      var transformTemplate = options.transformTemplate || angular.identity;
      var bindToController = options.bindToController;
  
      // Take resolve values and invoke them.  
      // Resolves can either be a string (value: 'MyRegisteredAngularConst'),
      // or an invokable 'factory' of sorts: (value: function ValueGetter($dependency) {})
      angular.forEach(resolve, function(value, key) {
        if (angular.isString(value)) {
          resolve[key] = $injector.get(value);
        } else {
          resolve[key] = $injector.invoke(value);
        }
      });
      //Add the locals, which are just straight values to inject
      //eg locals: { three: 3 }, will inject three into the controller
      angular.extend(resolve, locals);
  
      if (templateUrl) {
        resolve.$template = $http.get(templateUrl, {cache: $templateCache})
          .then(function(response) {
            return response.data;
          });
      } else {
        resolve.$template = $q.when(template);
      }
  
      // Wait for all the resolves to finish if they are promises
      return $q.all(resolve).then(function(locals) {
  
        var template = transformTemplate(locals.$template);
        var element = options.element || angular.element('<div>').html(template.trim()).contents();
        var linkFn = $compile(element);
  
        //Return a linking function that can be used later when the element is ready
        return {
          locals: locals,
          element: element,
          link: function link(scope) {
            locals.$scope = scope;
  
            //Instantiate controller if it exists, because we have scope
            if (controller) {
              var invokeCtrl = $controller(controller, locals, true);
              if (bindToController) {
                angular.extend(invokeCtrl.instance, locals);
              }
              var ctrl = invokeCtrl();
              //See angular-route source for this logic
              element.data('$ngControllerController', ctrl);
              element.children().data('$ngControllerController', ctrl);
  
              if (controllerAs) {
                scope[controllerAs] = ctrl;
              }
            }
            return linkFn(scope);
          }
        };
      });
  
    };
  }
  mdCompilerService.$inject = ["$q", "$http", "$injector", "$compile", "$controller", "$templateCache"];
  
  angular.module('material.core')
    .provider('$$interimElement', InterimElementProvider);
  
  /*
   * @ngdoc service
   * @name $$interimElement
   * @module material.core
   *
   * @description
   *
   * Factory that contructs `$$interimElement.$service` services.
   * Used internally in material design for elements that appear on screen temporarily.
   * The service provides a promise-like API for interacting with the temporary
   * elements.
   *
   * ```js
   * app.service('$mdToast', function($$interimElement) {
   *   var $mdToast = $$interimElement(toastDefaultOptions);
   *   return $mdToast;
   * });
   * ```
   * @param {object=} defaultOptions Options used by default for the `show` method on the service.
   *
   * @returns {$$interimElement.$service}
   *
   */
  
  function InterimElementProvider() {
    createInterimElementProvider.$get = InterimElementFactory;
    InterimElementFactory.$inject = ["$document", "$q", "$rootScope", "$timeout", "$rootElement", "$animate", "$interpolate", "$mdCompiler", "$mdTheming", "$log"];
    return createInterimElementProvider;
  
    /**
     * Returns a new provider which allows configuration of a new interimElement
     * service. Allows configuration of default options & methods for options,
     * as well as configuration of 'preset' methods (eg dialog.basic(): basic is a preset method)
     */
    function createInterimElementProvider(interimFactoryName) {
      var EXPOSED_METHODS = ['onHide', 'onShow', 'onRemove'];
  
      var customMethods = {};
      var providerConfig = {
        presets: {}
      };
  
      var provider = {
        setDefaults: setDefaults,
        addPreset: addPreset,
        addMethod: addMethod,
        $get: factory
      };
  
      /**
       * all interim elements will come with the 'build' preset
       */
      provider.addPreset('build', {
        methods: ['controller', 'controllerAs', 'resolve',
          'template', 'templateUrl', 'themable', 'transformTemplate', 'parent']
      });
  
      factory.$inject = ["$$interimElement", "$injector"];
      return provider;
  
      /**
       * Save the configured defaults to be used when the factory is instantiated
       */
      function setDefaults(definition) {
        providerConfig.optionsFactory = definition.options;
        providerConfig.methods = (definition.methods || []).concat(EXPOSED_METHODS);
        return provider;
      }
  
      /**
       * Add a method to the factory that isn't specific to any interim element operations
       */
  
      function addMethod(name, fn) {
        customMethods[name] = fn;
        return provider;
      }
  
      /**
       * Save the configured preset to be used when the factory is instantiated
       */
      function addPreset(name, definition) {
        definition = definition || {};
        definition.methods = definition.methods || [];
        definition.options = definition.options || function() { return {}; };
  
        if (/^cancel|hide|show$/.test(name)) {
          throw new Error("Preset '" + name + "' in " + interimFactoryName + " is reserved!");
        }
        if (definition.methods.indexOf('_options') > -1) {
          throw new Error("Method '_options' in " + interimFactoryName + " is reserved!");
        }
        providerConfig.presets[name] = {
          methods: definition.methods.concat(EXPOSED_METHODS),
          optionsFactory: definition.options,
          argOption: definition.argOption
        };
        return provider;
      }
  
      /**
       * Create a factory that has the given methods & defaults implementing interimElement
       */
      /* ngInject */
      function factory($$interimElement, $injector) {
        var defaultMethods;
        var defaultOptions;
        var interimElementService = $$interimElement();
  
        /*
         * publicService is what the developer will be using.
         * It has methods hide(), cancel(), show(), build(), and any other
         * presets which were set during the config phase.
         */
        var publicService = {
          hide: interimElementService.hide,
          cancel: interimElementService.cancel,
          show: showInterimElement
        };
  
        defaultMethods = providerConfig.methods || [];
        // This must be invoked after the publicService is initialized
        defaultOptions = invokeFactory(providerConfig.optionsFactory, {});
  
        // Copy over the simple custom methods
        angular.forEach(customMethods, function(fn, name) {
          publicService[name] = fn;
        });
  
        angular.forEach(providerConfig.presets, function(definition, name) {
          var presetDefaults = invokeFactory(definition.optionsFactory, {});
          var presetMethods = (definition.methods || []).concat(defaultMethods);
  
          // Every interimElement built with a preset has a field called `$type`,
          // which matches the name of the preset.
          // Eg in preset 'confirm', options.$type === 'confirm'
          angular.extend(presetDefaults, { $type: name });
  
          // This creates a preset class which has setter methods for every
          // method given in the `.addPreset()` function, as well as every
          // method given in the `.setDefaults()` function.
          //
          // @example
          // .setDefaults({
          //   methods: ['hasBackdrop', 'clickOutsideToClose', 'escapeToClose', 'targetEvent'],
          //   options: dialogDefaultOptions
          // })
          // .addPreset('alert', {
          //   methods: ['title', 'ok'],
          //   options: alertDialogOptions
          // })
          //
          // Set values will be passed to the options when interimElement.show() is called.
          function Preset(opts) {
            this._options = angular.extend({}, presetDefaults, opts);
          }
          angular.forEach(presetMethods, function(name) {
            Preset.prototype[name] = function(value) {
              this._options[name] = value;
              return this;
            };
          });
  
          // Create shortcut method for one-linear methods
          if (definition.argOption) {
            var methodName = 'show' + name.charAt(0).toUpperCase() + name.slice(1);
            publicService[methodName] = function(arg) {
              var config = publicService[name](arg);
              return publicService.show(config);
            };
          }
  
          // eg $mdDialog.alert() will return a new alert preset
          publicService[name] = function(arg) {
            // If argOption is supplied, eg `argOption: 'content'`, then we assume
            // if the argument is not an options object then it is the `argOption` option.
            //
            // @example `$mdToast.simple('hello')` // sets options.content to hello
            //                                     // because argOption === 'content'
            if (arguments.length && definition.argOption && !angular.isObject(arg) &&
                !angular.isArray(arg)) {
              return (new Preset())[definition.argOption](arg);
            } else {
              return new Preset(arg);
            }
  
          };
        });
  
        return publicService;
  
        function showInterimElement(opts) {
          // opts is either a preset which stores its options on an _options field,
          // or just an object made up of options
          opts = opts || { };
          if (opts._options) opts = opts._options;
  
          return interimElementService.show(
            angular.extend({}, defaultOptions, opts)
          );
        }
  
        /**
         * Helper to call $injector.invoke with a local of the factory name for
         * this provider.
         * If an $mdDialog is providing options for a dialog and tries to inject
         * $mdDialog, a circular dependency error will happen.
         * We get around that by manually injecting $mdDialog as a local.
         */
        function invokeFactory(factory, defaultVal) {
          var locals = {};
          locals[interimFactoryName] = publicService;
          return $injector.invoke(factory || function() { return defaultVal; }, {}, locals);
        }
  
      }
  
    }
  
    /* ngInject */
    function InterimElementFactory($document, $q, $rootScope, $timeout, $rootElement, $animate,
                                   $interpolate, $mdCompiler, $mdTheming, $log ) {
      var startSymbol = $interpolate.startSymbol(),
          endSymbol = $interpolate.endSymbol(),
          usesStandardSymbols = ((startSymbol === '{{') && (endSymbol === '}}')),
          processTemplate  = usesStandardSymbols ? angular.identity : replaceInterpolationSymbols;
  
      return function createInterimElementService() {
        var SHOW_CANCELLED = false;
        var SHOW_CLOSED = true;
  
        /*
         * @ngdoc service
         * @name $$interimElement.$service
         *
         * @description
         * A service used to control inserting and removing an element into the DOM.
         *
         */
        var service, stack = [];
  
        // Publish instance $$interimElement service;
        // ... used as $mdDialog, $mdToast, $mdMenu, and $mdSelect
  
        return service = {
          show: show,
          hide: hide,
          cancel: cancel
        };
  
        /*
         * @ngdoc method
         * @name $$interimElement.$service#show
         * @kind function
         *
         * @description
         * Adds the `$interimElement` to the DOM and returns a special promise that will be resolved or rejected
         * with hide or cancel, respectively. To external cancel/hide, developers should use the
         *
         * @param {*} options is hashMap of settings
         * @returns a Promise
         *
         */
        function show(options) {
          var interimElement = new InterimElement(options);
          var hideExisting = stack.length ? service.hide() : $q.when(true);
  
          // This hide()s only the current interim element before showing the next, new one
          // NOTE: this is not reversible (e.g. interim elements are not stackable)
  
          hideExisting.finally(function() {
  
            stack.push(interimElement);
            interimElement
              .show()
              .catch(function( reason ) {
                // $log.error("InterimElement.show() error: " + reason );
              });
  
          });
  
          // Return a promise that will be resolved when the interim
          // element is hidden or cancelled...
  
          return interimElement.deferred.promise;
        }
  
        /*
         * @ngdoc method
         * @name $$interimElement.$service#hide
         * @kind function
         *
         * @description
         * Removes the `$interimElement` from the DOM and resolves the promise returned from `show`
         *
         * @param {*} resolveParam Data to resolve the promise with
         * @returns a Promise that will be resolved after the element has been removed.
         *
         */
        function hide(reason) {
          var interim = stack.shift();
          if ( !interim ) return $q.when(reason);
  
          interim
            .remove(reason || SHOW_CLOSED, false)
            .catch(function( reason ) {
              // $log.error("InterimElement.hide() error: " + reason );
            });
  
          return interim.deferred.promise;
        }
  
        /*
         * @ngdoc method
         * @name $$interimElement.$service#cancel
         * @kind function
         *
         * @description
         * Removes the `$interimElement` from the DOM and rejects the promise returned from `show`
         *
         * @param {*} reason Data to reject the promise with
         * @returns Promise that will be resolved after the element has been removed.
         *
         */
        function cancel(reason) {
          var interim = stack.shift();
          if ( !interim ) return $q.when(reason);
  
          interim
            .remove(reason || SHOW_CANCELLED, true)
            .catch(function( reason ) {
              // $log.error("InterimElement.cancel() error: " + reason );
            });
  
          return interim.deferred.promise;
        }
  
  
        /*
         * Internal Interim Element Object
         * Used internally to manage the DOM element and related data
         */
        function InterimElement(options) {
          var self, element, showAction = $q.when(true);
  
          options = configureScopeAndTransitions(options);
  
          return self = {
            options : options,
            deferred: $q.defer(),
            show    : createAndTransitionIn,
            remove  : transitionOutAndRemove
          };
  
          /**
           * Compile, link, and show this interim element
           * Use optional autoHided and transition-in effects
           */
          function createAndTransitionIn() {
            return $q(function(resolve, reject){
  
              compileElement(options)
                .then(function( compiledData ) {
                  element = linkElement( compiledData, options );
  
                  showAction = showElement(element, options)
                    .then(resolve, rejectAll );
  
                });
  
              function rejectAll(fault) {
                // Force the '$md<xxx>.show()' promise to reject
                self.deferred.reject(fault);
  
                // Continue rejection propagation
                reject(fault);
              }
            });
          }
  
          /**
           * After the show process has finished/rejected:
           * - announce 'removing',
           * - perform the transition-out, and
           * - perform optional clean up scope.
           */
          function transitionOutAndRemove(response, isCancelled) {
            options.cancelAutoHide && options.cancelAutoHide();
  
            return $q(function(resolve, reject){
  
              $q.when(showAction).finally(function(){
  
                hideElement(options.element, options).then( function() {
  
                  (isCancelled && rejectAll(response)) || resolveAll();
  
                }, rejectAll );
  
              });
  
              function resolveAll() {
                // The `show()` returns a promise that will be resolved when the interim
                // element is hidden or cancelled...
                self.deferred.resolve(response);
  
                // Now resolve the `.hide()` promise itself (optional)
                resolve(response);
              }
  
              function rejectAll(fault) {
                // Force the '$md<xxx>.show()' promise to reject
                self.deferred.reject(fault);
  
                // Continue rejection propagation
                reject(fault);
              }
  
            });
          }
  
          /**
           * Prepare optional isolated scope and prepare $animate with default enter and leave
           * transitions for the new element instance.
           */
          function configureScopeAndTransitions(options) {
            options = options || { };
            if ( options.template ) {
              options.template = processTemplate(options.template);
            }
  
            return angular.extend({
              preserveScope: false,
              cancelAutoHide : angular.noop,
              scope: options.scope || $rootScope.$new(options.isolateScope),
  
              /**
               * Default usage to enable $animate to transition-in; can be easily overridden via 'options'
               */
              onShow: function transitionIn(scope, element, options) {
                return $animate.enter(element, options.parent);
              },
  
              /**
               * Default usage to enable $animate to transition-out; can be easily overridden via 'options'
               */
              onRemove: function transitionOut(scope, element) {
                // Element could be undefined if a new element is shown before
                // the old one finishes compiling.
                return element && $animate.leave(element) || $q.when();
              }
            }, options );
  
          }
  
          /**
           * Compile an element with a templateUrl, controller, and locals
           */
          function compileElement(options) {
  
            var compiled = !options.skipCompile ? $mdCompiler.compile(options) : null;
  
            return compiled || $q(function (resolve) {
                resolve({
                  locals: {},
                  link: function () {
                    return options.element;
                  }
                });
              });
          }
  
          /**
           *  Link an element with compiled configuration
           */
          function linkElement(compileData, options){
            angular.extend(compileData.locals, options);
  
            var element = compileData.link(options.scope);
  
            // Search for parent at insertion time, if not specified
            options.element = element;
            options.parent = findParent(element, options);
            if (options.themable) $mdTheming(element);
  
            return element;
          }
  
          /**
           * Search for parent at insertion time, if not specified
           */
          function findParent(element, options) {
            var parent = options.parent;
  
            // Search for parent at insertion time, if not specified
            if (angular.isFunction(parent)) {
              parent = parent(options.scope, element, options);
            } else if (angular.isString(parent)) {
              parent = angular.element($document[0].querySelector(parent));
            } else {
              parent = angular.element(parent);
            }
  
            // If parent querySelector/getter function fails, or it's just null,
            // find a default.
            if (!(parent || {}).length) {
              var el;
              if ($rootElement[0] && $rootElement[0].querySelector) {
                el = $rootElement[0].querySelector(':not(svg) > body');
              }
              if (!el) el = $rootElement[0];
              if (el.nodeName == '#comment') {
                el = $document[0].body;
              }
              return angular.element(el);
            }
  
            return parent;
          }
  
          /**
           * If auto-hide is enabled, start timer and prepare cancel function
           */
          function startAutoHide() {
            var autoHideTimer, cancelAutoHide = angular.noop;
  
            if (options.hideDelay) {
              autoHideTimer = $timeout(service.hide, options.hideDelay) ;
              cancelAutoHide = function() {
                $timeout.cancel(autoHideTimer);
              }
            }
  
            // Cache for subsequent use
            options.cancelAutoHide = function() {
              cancelAutoHide();
              options.cancelAutoHide = undefined;
            }
          }
  
          /**
           * Show the element ( with transitions), notify complete and start
           * optional auto-Hide
           */
          function showElement(element, options) {
            // Trigger onComplete callback when the `show()` finishes
            var notifyComplete = options.onComplete || angular.noop;
  
            return $q(function (resolve, reject) {
              try {
  
                // Start transitionIn
                $q.when(options.onShow(options.scope, element, options))
                  .then(function () {
  
                    notifyComplete(options.scope, element, options);
                    startAutoHide();
  
                    resolve(element);
  
                  }, reject );
  
              } catch(e) {
                reject(e.message);
              }
            });
          }
  
          function hideElement(element, options) {
            var announceRemoving = options.onRemoving || angular.noop;
  
            return $q(function (resolve, reject) {
              try {
                // Start transitionIn
                var action = $q.when(element ? options.onRemove(options.scope, element, options) : true);
  
                // Trigger callback *before* the remove operation starts
                announceRemoving(element, action);
  
                // Wait until transition-out is done
                action.then(function () {
  
                  !options.preserveScope && options.scope.$destroy();
                  resolve(element);
  
                }, reject );
  
              } catch(e) {
                reject(e.message);
              }
            });
          }
  
        }
      };
  
      /**
       * Replace `{{` and `}}` in a string (usually a template) with the actual start-/endSymbols used
       * for interpolation. This allows pre-defined templates (for components such as dialog, toast etc)
       * to continue to work in apps that use custom interpolation start-/endSymbols.
       *
       * @param {string} text The text in which to replace `{{` / `}}`
       * @returns {string} The modified string using the actual interpolation start-/endSymbols
       */
      function replaceInterpolationSymbols(text) {
        if (!text || !angular.isString(text)) return text;
        return text.replace(/\{\{/g, startSymbol).replace(/}}/g, endSymbol);
      }
  
    }
  
  }
  
    /**
     * @ngdoc module
     * @name material.core.componentRegistry
     *
     * @description
     * A component instance registration service.
     * Note: currently this as a private service in the SideNav component.
     */
    angular.module('material.core')
      .factory('$mdComponentRegistry', ComponentRegistry);
  
    /*
     * @private
     * @ngdoc factory
     * @name ComponentRegistry
     * @module material.core.componentRegistry
     *
     */
    function ComponentRegistry($log, $q) {
  
      var self;
      var instances = [ ];
      var pendings = { };
  
      return self = {
        /**
         * Used to print an error when an instance for a handle isn't found.
         */
        notFoundError: function(handle) {
          $log.error('No instance found for handle', handle);
        },
        /**
         * Return all registered instances as an array.
         */
        getInstances: function() {
          return instances;
        },
  
        /**
         * Get a registered instance.
         * @param handle the String handle to look up for a registered instance.
         */
        get: function(handle) {
          if ( !isValidID(handle) ) return null;
  
          var i, j, instance;
          for(i = 0, j = instances.length; i < j; i++) {
            instance = instances[i];
            if(instance.$$mdHandle === handle) {
              return instance;
            }
          }
          return null;
        },
  
        /**
         * Register an instance.
         * @param instance the instance to register
         * @param handle the handle to identify the instance under.
         */
        register: function(instance, handle) {
          if ( !handle ) return angular.noop;
  
          instance.$$mdHandle = handle;
          instances.push(instance);
          resolveWhen();
  
          return deregister;
  
          /**
           * Remove registration for an instance
           */
          function deregister() {
            var index = instances.indexOf(instance);
            if (index !== -1) {
              instances.splice(index, 1);
            }
          }
  
          /**
           * Resolve any pending promises for this instance
           */
          function resolveWhen() {
            var dfd = pendings[handle];
            if ( dfd ) {
              dfd.resolve( instance );
              delete pendings[handle];
            }
          }
        },
  
        /**
         * Async accessor to registered component instance
         * If not available then a promise is created to notify
         * all listeners when the instance is registered.
         */
        when : function(handle) {
          if ( isValidID(handle) ) {
            var deferred = $q.defer();
            var instance = self.get(handle);
  
            if ( instance )  {
              deferred.resolve( instance );
            } else {
              pendings[handle] = deferred;
            }
  
            return deferred.promise;
          }
          return $q.reject("Invalid `md-component-id` value.");
        }
  
      };
  
      function isValidID(handle){
        return handle && (handle !== "");
      }
  
    }
    ComponentRegistry.$inject = ["$log", "$q"];
  
  (function() {
    'use strict';
  
    /**
     * @ngdoc service
     * @name $mdButtonInkRipple
     * @module material.core
     *
     * @description
     * Provides ripple effects for md-button.  See $mdInkRipple service for all possible configuration options.
     *
     * @param {object=} scope Scope within the current context
     * @param {object=} element The element the ripple effect should be applied to
     * @param {object=} options (Optional) Configuration options to override the defaultripple configuration
     */
  
    angular.module('material.core')
      .factory('$mdButtonInkRipple', MdButtonInkRipple);
  
    function MdButtonInkRipple($mdInkRipple) {
      return {
        attach: attach
      };
  
      function attach(scope, element, options) {
        var elementOptions = optionsForElement(element);
        return $mdInkRipple.attach(scope, element, angular.extend(elementOptions, options));
      };
  
      function optionsForElement(element) {
        if (element.hasClass('md-icon-button')) {
          return {
            isMenuItem: element.hasClass('md-menu-item'),
            fitRipple: true,
            center: true
          };
        } else {
          return {
            isMenuItem: element.hasClass('md-menu-item'),
            dimBackground: true
          }
        }
      };
    }
    MdButtonInkRipple.$inject = ["$mdInkRipple"];;
  })();
  
  (function() {
    'use strict';
  
      /**
     * @ngdoc service
     * @name $mdCheckboxInkRipple
     * @module material.core
     *
     * @description
     * Provides ripple effects for md-checkbox.  See $mdInkRipple service for all possible configuration options.
     *
     * @param {object=} scope Scope within the current context
     * @param {object=} element The element the ripple effect should be applied to
     * @param {object=} options (Optional) Configuration options to override the defaultripple configuration
     */
  
    angular.module('material.core')
      .factory('$mdCheckboxInkRipple', MdCheckboxInkRipple);
  
    function MdCheckboxInkRipple($mdInkRipple) {
      return {
        attach: attach
      };
  
      function attach(scope, element, options) {
        return $mdInkRipple.attach(scope, element, angular.extend({
          center: true,
          dimBackground: false,
          fitRipple: true
        }, options));
      };
    }
    MdCheckboxInkRipple.$inject = ["$mdInkRipple"];;
  })();
  
  (function() {
    'use strict';
  
    /**
     * @ngdoc service
     * @name $mdListInkRipple
     * @module material.core
     *
     * @description
     * Provides ripple effects for md-list.  See $mdInkRipple service for all possible configuration options.
     *
     * @param {object=} scope Scope within the current context
     * @param {object=} element The element the ripple effect should be applied to
     * @param {object=} options (Optional) Configuration options to override the defaultripple configuration
     */
  
    angular.module('material.core')
      .factory('$mdListInkRipple', MdListInkRipple);
  
    function MdListInkRipple($mdInkRipple) {
      return {
        attach: attach
      };
  
      function attach(scope, element, options) {
        return $mdInkRipple.attach(scope, element, angular.extend({
          center: false,
          dimBackground: true,
          outline: false,
          rippleSize: 'full'
        }, options));
      };
    }
    MdListInkRipple.$inject = ["$mdInkRipple"];;
  })();
  
  angular.module('material.core')
    .factory('$mdInkRipple', InkRippleService)
    .directive('mdInkRipple', InkRippleDirective)
    .directive('mdNoInk', attrNoDirective())
    .directive('mdNoBar', attrNoDirective())
    .directive('mdNoStretch', attrNoDirective());
  
  function InkRippleDirective($mdButtonInkRipple, $mdCheckboxInkRipple) {
    return {
      controller: angular.noop,
      link: function (scope, element, attr) {
        if (attr.hasOwnProperty('mdInkRippleCheckbox')) {
          $mdCheckboxInkRipple.attach(scope, element);
        } else {
          $mdButtonInkRipple.attach(scope, element);
        }
      }
    };
  }
  InkRippleDirective.$inject = ["$mdButtonInkRipple", "$mdCheckboxInkRipple"];
  
  function InkRippleService($window, $timeout, $mdUtil) {
  
    return {
      attach: attach
    };
  
    function attach(scope, element, options) {
      if (element.controller('mdNoInk')) return angular.noop;
  
      options = angular.extend({
        colorElement: element,
        mousedown: true,
        hover: true,
        focus: true,
        center: false,
        mousedownPauseTime: 150,
        dimBackground: false,
        outline: false,
        fullRipple: true,
        isMenuItem: false,
        fitRipple: false
      }, options);
  
      var rippleSize,
          controller = element.controller('mdInkRipple') || {},
          counter = 0,
          ripples = [],
          states = [],
          isActiveExpr = element.attr('md-highlight'),
          isActive = false,
          isHeld = false,
          node = element[0],
          rippleSizeSetting = element.attr('md-ripple-size'),
          color = parseColor(element.attr('md-ink-ripple')) || parseColor(options.colorElement.length && $window.getComputedStyle(options.colorElement[0]).color || 'rgb(0, 0, 0)');
  
      switch (rippleSizeSetting) {
        case 'full':
          options.fullRipple = true;
          break;
        case 'partial':
          options.fullRipple = false;
          break;
      }
  
      // expose onInput for ripple testing
      if (options.mousedown) {
        element.on('$md.pressdown', onPressDown)
          .on('$md.pressup', onPressUp);
      }
  
      controller.createRipple = createRipple;
  
      if (isActiveExpr) {
        scope.$watch(isActiveExpr, function watchActive(newValue) {
          isActive = newValue;
          if (isActive && !ripples.length) {
            $mdUtil.nextTick(function () { createRipple(0, 0); });
          }
          angular.forEach(ripples, updateElement);
        });
      }
  
      // Publish self-detach method if desired...
      return function detach() {
        element.off('$md.pressdown', onPressDown)
          .off('$md.pressup', onPressUp);
        getRippleContainer().remove();
      };
  
      /**
       * Gets the current ripple container
       * If there is no ripple container, it creates one and returns it
       *
       * @returns {angular.element} ripple container element
       */
      function getRippleContainer() {
        var container = element.data('$mdRippleContainer');
        if (container) return container;
        container = angular.element('<div class="md-ripple-container">');
        element.append(container);
        element.data('$mdRippleContainer', container);
        return container;
      }
  
      function parseColor(color) {
        if (!color) return;
        if (color.indexOf('rgba') === 0) return color.replace(/\d?\.?\d*\s*\)\s*$/, '0.1)');
        if (color.indexOf('rgb')  === 0) return rgbToRGBA(color);
        if (color.indexOf('#')    === 0) return hexToRGBA(color);
  
        /**
         * Converts a hex value to an rgba string
         *
         * @param {string} hex value (3 or 6 digits) to be converted
         *
         * @returns {string} rgba color with 0.1 alpha
         */
        function hexToRGBA(color) {
          var hex = color.charAt(0) === '#' ? color.substr(1) : color,
            dig = hex.length / 3,
            red = hex.substr(0, dig),
            grn = hex.substr(dig, dig),
            blu = hex.substr(dig * 2);
          if (dig === 1) {
            red += red;
            grn += grn;
            blu += blu;
          }
          return 'rgba(' + parseInt(red, 16) + ',' + parseInt(grn, 16) + ',' + parseInt(blu, 16) + ',0.1)';
        }
  
        /**
         * Converts rgb value to rgba string
         *
         * @param {string} rgb color string
         *
         * @returns {string} rgba color with 0.1 alpha
         */
        function rgbToRGBA(color) {
          return color.replace(')', ', 0.1)').replace('(', 'a(');
        }
  
      }
  
      function removeElement(elem, wait) {
        ripples.splice(ripples.indexOf(elem), 1);
        if (ripples.length === 0) {
          getRippleContainer().css({ backgroundColor: '' });
        }
        $timeout(function () { elem.remove(); }, wait, false);
      }
  
      function updateElement(elem) {
        var index = ripples.indexOf(elem),
            state = states[index] || {},
            elemIsActive = ripples.length > 1 ? false : isActive,
            elemIsHeld   = ripples.length > 1 ? false : isHeld;
        if (elemIsActive || state.animating || elemIsHeld) {
          elem.addClass('md-ripple-visible');
        } else if (elem) {
          elem.removeClass('md-ripple-visible');
          if (options.outline) {
            elem.css({
              width: rippleSize + 'px',
              height: rippleSize + 'px',
              marginLeft: (rippleSize * -1) + 'px',
              marginTop: (rippleSize * -1) + 'px'
            });
          }
          removeElement(elem, options.outline ? 450 : 650);
        }
      }
  
      /**
       * Creates a ripple at the provided coordinates
       *
       * @param {number} left cursor position
       * @param {number} top cursor position
       *
       * @returns {angular.element} the generated ripple element
       */
      function createRipple(left, top) {
  
        color = parseColor(element.attr('md-ink-ripple')) || parseColor($window.getComputedStyle(options.colorElement[0]).color || 'rgb(0, 0, 0)');
  
        var container = getRippleContainer(),
            size = getRippleSize(left, top),
            css = getRippleCss(size, left, top),
            elem = getRippleElement(css),
            index = ripples.indexOf(elem),
            state = states[index] || {};
  
        rippleSize = size;
  
        state.animating = true;
  
        $mdUtil.nextTick(function () {
          if (options.dimBackground) {
            container.css({ backgroundColor: color });
          }
          elem.addClass('md-ripple-placed md-ripple-scaled');
          if (options.outline) {
            elem.css({
              borderWidth: (size * 0.5) + 'px',
              marginLeft: (size * -0.5) + 'px',
              marginTop: (size * -0.5) + 'px'
            });
          } else {
            elem.css({ left: '50%', top: '50%' });
          }
          updateElement(elem);
          $timeout(function () {
            state.animating = false;
            updateElement(elem);
          }, (options.outline ? 450 : 225), false);
        });
  
        return elem;
  
        /**
         * Creates the ripple element with the provided css
         *
         * @param {object} css properties to be applied
         *
         * @returns {angular.element} the generated ripple element
         */
        function getRippleElement(css) {
          var elem = angular.element('<div class="md-ripple" data-counter="' + counter++ + '">');
          ripples.unshift(elem);
          states.unshift({ animating: true });
          container.append(elem);
          css && elem.css(css);
          return elem;
        }
  
        /**
         * Calculate the ripple size
         *
         * @returns {number} calculated ripple diameter
         */
        function getRippleSize(left, top) {
          var width = container.prop('offsetWidth'),
              height = container.prop('offsetHeight'),
              multiplier, size, rect;
          if (options.isMenuItem) {
            size = Math.sqrt(Math.pow(width, 2) + Math.pow(height, 2));
          } else if (options.outline) {
            rect = node.getBoundingClientRect();
            left -= rect.left;
            top -= rect.top;
            width = Math.max(left, width - left);
            height = Math.max(top, height - top);
            size = 2 * Math.sqrt(Math.pow(width, 2) + Math.pow(height, 2));
          } else {
            multiplier = options.fullRipple ? 1.1 : 0.8;
            size = Math.sqrt(Math.pow(width, 2) + Math.pow(height, 2)) * multiplier;
            if (options.fitRipple) {
              size = Math.min(height, width, size);
            }
          }
          return size;
        }
  
        /**
         * Generates the ripple css
         *
         * @param {number} the diameter of the ripple
         * @param {number} the left cursor offset
         * @param {number} the top cursor offset
         *
         * @returns {{backgroundColor: string, borderColor: string, width: string, height: string}}
         */
        function getRippleCss(size, left, top) {
          var rect = node.getBoundingClientRect(),
              css  = {
                backgroundColor: rgbaToRGB(color),
                borderColor: rgbaToRGB(color),
                width: size + 'px',
                height: size + 'px'
              };
  
          if (options.outline) {
            css.width = 0;
            css.height = 0;
          } else {
            css.marginLeft = css.marginTop = (size * -0.5) + 'px';
          }
  
          if (options.center) {
            css.left = css.top = '50%';
          } else {
            css.left = Math.round((left - rect.left) / container.prop('offsetWidth') * 100) + '%';
            css.top = Math.round((top - rect.top) / container.prop('offsetHeight') * 100) + '%';
          }
  
          return css;
  
          /**
           * Converts rgba string to rgb, removing the alpha value
           *
           * @param {string} rgba color
           *
           * @returns {string} rgb color
           */
          function rgbaToRGB(color) {
            return color.replace('rgba', 'rgb').replace(/,[^\),]+\)/, ')');
          }
        }
      }
  
      /**
       * Handles user input start and stop events
       *
       */
      function onPressDown(ev) {
        if (!isRippleAllowed()) return;
  
        createRipple(ev.pointer.x, ev.pointer.y);
        isHeld = true;
      }
      function onPressUp() {
        isHeld = false;
        var ripple = ripples[ ripples.length - 1 ];
        $mdUtil.nextTick(function () { updateElement(ripple); });
      }
  
      /**
       * Determines if the ripple is allowed
       *
       * @returns {boolean} true if the ripple is allowed, false if not
       */
      function isRippleAllowed() {
        var parent = node.parentNode;
        var grandparent = parent && parent.parentNode;
        var ancestor = grandparent && grandparent.parentNode;
        return !isDisabled(node) && !isDisabled(parent) && !isDisabled(grandparent) && !isDisabled(ancestor);
        function isDisabled (elem) {
          return elem && elem.hasAttribute && elem.hasAttribute('disabled');
        }
      }
  
    }
  }
  InkRippleService.$inject = ["$window", "$timeout", "$mdUtil"];
  
  /**
   * noink/nobar/nostretch directive: make any element that has one of
   * these attributes be given a controller, so that other directives can
   * `require:` these and see if there is a `no<xxx>` parent attribute.
   *
   * @usage
   * <hljs lang="html">
   * <parent md-no-ink>
   *   <child detect-no>
   *   </child>
   * </parent>
   * </hljs>
   *
   * <hljs lang="js">
   * myApp.directive('detectNo', function() {
   *   return {
   *     require: ['^?mdNoInk', ^?mdNoBar'],
   *     link: function(scope, element, attr, ctrls) {
   *       var noinkCtrl = ctrls[0];
   *       var nobarCtrl = ctrls[1];
   *       if (noInkCtrl) {
   *         alert("the md-no-ink flag has been specified on an ancestor!");
   *       }
   *       if (nobarCtrl) {
   *         alert("the md-no-bar flag has been specified on an ancestor!");
   *       }
   *     }
   *   };
   * });
   * </hljs>
   */
  function attrNoDirective() {
    return function() {
      return {
        controller: angular.noop
      };
    };
  }
  
  (function() {
    'use strict';
  
      /**
     * @ngdoc service
     * @name $mdTabInkRipple
     * @module material.core
     *
     * @description
     * Provides ripple effects for md-tabs.  See $mdInkRipple service for all possible configuration options.
     *
     * @param {object=} scope Scope within the current context
     * @param {object=} element The element the ripple effect should be applied to
     * @param {object=} options (Optional) Configuration options to override the defaultripple configuration
     */
  
    angular.module('material.core')
      .factory('$mdTabInkRipple', MdTabInkRipple);
  
    function MdTabInkRipple($mdInkRipple) {
      return {
        attach: attach
      };
  
      function attach(scope, element, options) {
        return $mdInkRipple.attach(scope, element, angular.extend({
          center: false,
          dimBackground: true,
          outline: false,
          rippleSize: 'full'
        }, options));
      };
    }
    MdTabInkRipple.$inject = ["$mdInkRipple"];;
  })();
  
  angular.module('material.core.theming.palette', [])
  .constant('$mdColorPalette', {
    'red': {
      '50': '#ffebee',
      '100': '#ffcdd2',
      '200': '#ef9a9a',
      '300': '#e57373',
      '400': '#ef5350',
      '500': '#f44336',
      '600': '#e53935',
      '700': '#d32f2f',
      '800': '#c62828',
      '900': '#b71c1c',
      'A100': '#ff8a80',
      'A200': '#ff5252',
      'A400': '#ff1744',
      'A700': '#d50000',
      'contrastDefaultColor': 'light',
      'contrastDarkColors': '50 100 200 300 400 A100',
      'contrastStrongLightColors': '500 600 700 A200 A400 A700'
    },
    'pink': {
      '50': '#fce4ec',
      '100': '#f8bbd0',
      '200': '#f48fb1',
      '300': '#f06292',
      '400': '#ec407a',
      '500': '#e91e63',
      '600': '#d81b60',
      '700': '#c2185b',
      '800': '#ad1457',
      '900': '#880e4f',
      'A100': '#ff80ab',
      'A200': '#ff4081',
      'A400': '#f50057',
      'A700': '#c51162',
      'contrastDefaultColor': 'light',
      'contrastDarkColors': '50 100 200 300 400 A100',
      'contrastStrongLightColors': '500 600 A200 A400 A700'
    },
    'purple': {
      '50': '#f3e5f5',
      '100': '#e1bee7',
      '200': '#ce93d8',
      '300': '#ba68c8',
      '400': '#ab47bc',
      '500': '#9c27b0',
      '600': '#8e24aa',
      '700': '#7b1fa2',
      '800': '#6a1b9a',
      '900': '#4a148c',
      'A100': '#ea80fc',
      'A200': '#e040fb',
      'A400': '#d500f9',
      'A700': '#aa00ff',
      'contrastDefaultColor': 'light',
      'contrastDarkColors': '50 100 200 A100',
      'contrastStrongLightColors': '300 400 A200 A400 A700'
    },
    'deep-purple': {
      '50': '#ede7f6',
      '100': '#d1c4e9',
      '200': '#b39ddb',
      '300': '#9575cd',
      '400': '#7e57c2',
      '500': '#673ab7',
      '600': '#5e35b1',
      '700': '#512da8',
      '800': '#4527a0',
      '900': '#311b92',
      'A100': '#b388ff',
      'A200': '#7c4dff',
      'A400': '#651fff',
      'A700': '#6200ea',
      'contrastDefaultColor': 'light',
      'contrastDarkColors': '50 100 200 A100',
      'contrastStrongLightColors': '300 400 A200'
    },
    'indigo': {
      '50': '#e8eaf6',
      '100': '#c5cae9',
      '200': '#9fa8da',
      '300': '#7986cb',
      '400': '#5c6bc0',
      '500': '#3f51b5',
      '600': '#3949ab',
      '700': '#303f9f',
      '800': '#283593',
      '900': '#1a237e',
      'A100': '#8c9eff',
      'A200': '#536dfe',
      'A400': '#3d5afe',
      'A700': '#304ffe',
      'contrastDefaultColor': 'light',
      'contrastDarkColors': '50 100 200 A100',
      'contrastStrongLightColors': '300 400 A200 A400'
    },
    'blue': {
      '50': '#e3f2fd',
      '100': '#bbdefb',
      '200': '#90caf9',
      '300': '#64b5f6',
      '400': '#42a5f5',
      '500': '#2196f3',
      '600': '#1e88e5',
      '700': '#1976d2',
      '800': '#1565c0',
      '900': '#0d47a1',
      'A100': '#82b1ff',
      'A200': '#448aff',
      'A400': '#2979ff',
      'A700': '#2962ff',
      'contrastDefaultColor': 'light',
      'contrastDarkColors': '100 200 300 400 A100',
      'contrastStrongLightColors': '500 600 700 A200 A400 A700'
    },
    'light-blue': {
      '50': '#e1f5fe',
      '100': '#b3e5fc',
      '200': '#81d4fa',
      '300': '#4fc3f7',
      '400': '#29b6f6',
      '500': '#03a9f4',
      '600': '#039be5',
      '700': '#0288d1',
      '800': '#0277bd',
      '900': '#01579b',
      'A100': '#80d8ff',
      'A200': '#40c4ff',
      'A400': '#00b0ff',
      'A700': '#0091ea',
      'contrastDefaultColor': 'dark',
      'contrastLightColors': '500 600 700 800 900 A700',
      'contrastStrongLightColors': '500 600 700 800 A700'
    },
    'cyan': {
      '50': '#e0f7fa',
      '100': '#b2ebf2',
      '200': '#80deea',
      '300': '#4dd0e1',
      '400': '#26c6da',
      '500': '#00bcd4',
      '600': '#00acc1',
      '700': '#0097a7',
      '800': '#00838f',
      '900': '#006064',
      'A100': '#84ffff',
      'A200': '#18ffff',
      'A400': '#00e5ff',
      'A700': '#00b8d4',
      'contrastDefaultColor': 'dark',
      'contrastLightColors': '500 600 700 800 900',
      'contrastStrongLightColors': '500 600 700 800'
    },
    'teal': {
      '50': '#e0f2f1',
      '100': '#b2dfdb',
      '200': '#80cbc4',
      '300': '#4db6ac',
      '400': '#26a69a',
      '500': '#009688',
      '600': '#00897b',
      '700': '#00796b',
      '800': '#00695c',
      '900': '#004d40',
      'A100': '#a7ffeb',
      'A200': '#64ffda',
      'A400': '#1de9b6',
      'A700': '#00bfa5',
      'contrastDefaultColor': 'dark',
      'contrastLightColors': '500 600 700 800 900',
      'contrastStrongLightColors': '500 600 700'
    },
    'green': {
      '50': '#e8f5e9',
      '100': '#c8e6c9',
      '200': '#a5d6a7',
      '300': '#81c784',
      '400': '#66bb6a',
      '500': '#4caf50',
      '600': '#43a047',
      '700': '#388e3c',
      '800': '#2e7d32',
      '900': '#1b5e20',
      'A100': '#b9f6ca',
      'A200': '#69f0ae',
      'A400': '#00e676',
      'A700': '#00c853',
      'contrastDefaultColor': 'dark',
      'contrastLightColors': '500 600 700 800 900',
      'contrastStrongLightColors': '500 600 700'
    },
    'light-green': {
      '50': '#f1f8e9',
      '100': '#dcedc8',
      '200': '#c5e1a5',
      '300': '#aed581',
      '400': '#9ccc65',
      '500': '#8bc34a',
      '600': '#7cb342',
      '700': '#689f38',
      '800': '#558b2f',
      '900': '#33691e',
      'A100': '#ccff90',
      'A200': '#b2ff59',
      'A400': '#76ff03',
      'A700': '#64dd17',
      'contrastDefaultColor': 'dark',
      'contrastLightColors': '800 900',
      'contrastStrongLightColors': '800 900'
    },
    'lime': {
      '50': '#f9fbe7',
      '100': '#f0f4c3',
      '200': '#e6ee9c',
      '300': '#dce775',
      '400': '#d4e157',
      '500': '#cddc39',
      '600': '#c0ca33',
      '700': '#afb42b',
      '800': '#9e9d24',
      '900': '#827717',
      'A100': '#f4ff81',
      'A200': '#eeff41',
      'A400': '#c6ff00',
      'A700': '#aeea00',
      'contrastDefaultColor': 'dark',
      'contrastLightColors': '900',
      'contrastStrongLightColors': '900'
    },
    'yellow': {
      '50': '#fffde7',
      '100': '#fff9c4',
      '200': '#fff59d',
      '300': '#fff176',
      '400': '#ffee58',
      '500': '#ffeb3b',
      '600': '#fdd835',
      '700': '#fbc02d',
      '800': '#f9a825',
      '900': '#f57f17',
      'A100': '#ffff8d',
      'A200': '#ffff00',
      'A400': '#ffea00',
      'A700': '#ffd600',
      'contrastDefaultColor': 'dark'
    },
    'amber': {
      '50': '#fff8e1',
      '100': '#ffecb3',
      '200': '#ffe082',
      '300': '#ffd54f',
      '400': '#ffca28',
      '500': '#ffc107',
      '600': '#ffb300',
      '700': '#ffa000',
      '800': '#ff8f00',
      '900': '#ff6f00',
      'A100': '#ffe57f',
      'A200': '#ffd740',
      'A400': '#ffc400',
      'A700': '#ffab00',
      'contrastDefaultColor': 'dark'
    },
    'orange': {
      '50': '#fff3e0',
      '100': '#ffe0b2',
      '200': '#ffcc80',
      '300': '#ffb74d',
      '400': '#ffa726',
      '500': '#ff9800',
      '600': '#fb8c00',
      '700': '#f57c00',
      '800': '#ef6c00',
      '900': '#e65100',
      'A100': '#ffd180',
      'A200': '#ffab40',
      'A400': '#ff9100',
      'A700': '#ff6d00',
      'contrastDefaultColor': 'dark',
      'contrastLightColors': '800 900',
      'contrastStrongLightColors': '800 900'
    },
    'deep-orange': {
      '50': '#fbe9e7',
      '100': '#ffccbc',
      '200': '#ffab91',
      '300': '#ff8a65',
      '400': '#ff7043',
      '500': '#ff5722',
      '600': '#f4511e',
      '700': '#e64a19',
      '800': '#d84315',
      '900': '#bf360c',
      'A100': '#ff9e80',
      'A200': '#ff6e40',
      'A400': '#ff3d00',
      'A700': '#dd2c00',
      'contrastDefaultColor': 'light',
      'contrastDarkColors': '50 100 200 300 400 A100 A200',
      'contrastStrongLightColors': '500 600 700 800 900 A400 A700'
    },
    'brown': {
      '50': '#efebe9',
      '100': '#d7ccc8',
      '200': '#bcaaa4',
      '300': '#a1887f',
      '400': '#8d6e63',
      '500': '#795548',
      '600': '#6d4c41',
      '700': '#5d4037',
      '800': '#4e342e',
      '900': '#3e2723',
      'A100': '#d7ccc8',
      'A200': '#bcaaa4',
      'A400': '#8d6e63',
      'A700': '#5d4037',
      'contrastDefaultColor': 'light',
      'contrastDarkColors': '50 100 200',
      'contrastStrongLightColors': '300 400'
    },
    'grey': {
      '50': '#fafafa',
      '100': '#f5f5f5',
      '200': '#eeeeee',
      '300': '#e0e0e0',
      '400': '#bdbdbd',
      '500': '#9e9e9e',
      '600': '#757575',
      '700': '#616161',
      '800': '#424242',
      '900': '#212121',
      '1000': '#000000',
      'A100': '#ffffff',
      'A200': '#eeeeee',
      'A400': '#bdbdbd',
      'A700': '#616161',
      'contrastDefaultColor': 'dark',
      'contrastLightColors': '600 700 800 900'
    },
    'blue-grey': {
      '50': '#eceff1',
      '100': '#cfd8dc',
      '200': '#b0bec5',
      '300': '#90a4ae',
      '400': '#78909c',
      '500': '#607d8b',
      '600': '#546e7a',
      '700': '#455a64',
      '800': '#37474f',
      '900': '#263238',
      'A100': '#cfd8dc',
      'A200': '#b0bec5',
      'A400': '#78909c',
      'A700': '#455a64',
      'contrastDefaultColor': 'light',
      'contrastDarkColors': '50 100 200 300',
      'contrastStrongLightColors': '400 500'
    }
  });
  
  angular.module('material.core.theming', ['material.core.theming.palette'])
    .directive('mdTheme', ThemingDirective)
    .directive('mdThemable', ThemableDirective)
    .provider('$mdTheming', ThemingProvider)
    .run(generateThemes);
  
  /**
   * @ngdoc provider
   * @name $mdThemingProvider
   * @module material.core
   *
   * @description Provider to configure the `$mdTheming` service.
   */
  
  /**
   * @ngdoc method
   * @name $mdThemingProvider#setDefaultTheme
   * @param {string} themeName Default theme name to be applied to elements. Default value is `default`.
   */
  
  /**
   * @ngdoc method
   * @name $mdThemingProvider#alwaysWatchTheme
   * @param {boolean} watch Whether or not to always watch themes for changes and re-apply
   * classes when they change. Default is `false`. Enabling can reduce performance.
   */
  
  /* Some Example Valid Theming Expressions
   * =======================================
   *
   * Intention group expansion: (valid for primary, accent, warn, background)
   *
   * {{primary-100}} - grab shade 100 from the primary palette
   * {{primary-100-0.7}} - grab shade 100, apply opacity of 0.7
   * {{primary-hue-1}} - grab the shade assigned to hue-1 from the primary palette
   * {{primary-hue-1-0.7}} - apply 0.7 opacity to primary-hue-1
   * {{primary-color}} - Generates .md-hue-1, .md-hue-2, .md-hue-3 with configured shades set for each hue
   * {{primary-color-0.7}} - Apply 0.7 opacity to each of the above rules
   * {{primary-contrast}} - Generates .md-hue-1, .md-hue-2, .md-hue-3 with configured contrast (ie. text) color shades set for each hue
   * {{primary-contrast-0.7}} - Apply 0.7 opacity to each of the above rules
   *
   * Foreground expansion: Applies rgba to black/white foreground text
   *
   * {{foreground-1}} - used for primary text
   * {{foreground-2}} - used for secondary text/divider
   * {{foreground-3}} - used for disabled text
   * {{foreground-4}} - used for dividers
   *
   */
  
  // In memory generated CSS rules; registered by theme.name
  var GENERATED = { };
  
  // In memory storage of defined themes and color palettes (both loaded by CSS, and user specified)
  var PALETTES;
  var THEMES;
  
  var DARK_FOREGROUND = {
    name: 'dark',
    '1': 'rgba(0,0,0,0.87)',
    '2': 'rgba(0,0,0,0.54)',
    '3': 'rgba(0,0,0,0.26)',
    '4': 'rgba(0,0,0,0.12)'
  };
  var LIGHT_FOREGROUND = {
    name: 'light',
    '1': 'rgba(255,255,255,1.0)',
    '2': 'rgba(255,255,255,0.7)',
    '3': 'rgba(255,255,255,0.3)',
    '4': 'rgba(255,255,255,0.12)'
  };
  
  var DARK_SHADOW = '1px 1px 0px rgba(0,0,0,0.4), -1px -1px 0px rgba(0,0,0,0.4)';
  var LIGHT_SHADOW = '';
  
  var DARK_CONTRAST_COLOR = colorToRgbaArray('rgba(0,0,0,0.87)');
  var LIGHT_CONTRAST_COLOR = colorToRgbaArray('rgba(255,255,255,0.87');
  var STRONG_LIGHT_CONTRAST_COLOR = colorToRgbaArray('rgb(255,255,255)');
  
  var THEME_COLOR_TYPES = ['primary', 'accent', 'warn', 'background'];
  var DEFAULT_COLOR_TYPE = 'primary';
  
  // A color in a theme will use these hues by default, if not specified by user.
  var LIGHT_DEFAULT_HUES = {
    'accent': {
      'default': 'A200',
      'hue-1': 'A100',
      'hue-2': 'A400',
      'hue-3': 'A700'
    },
    'background': {
      'default': 'A100',
      'hue-1': '300',
      'hue-2': '800',
      'hue-3': '900'
    }
  };
  
  var DARK_DEFAULT_HUES = {
    'background': {
      'default': '800',
      'hue-1': '300',
      'hue-2': '600',
      'hue-3': '900'
    }
  };
  THEME_COLOR_TYPES.forEach(function(colorType) {
    // Color types with unspecified default hues will use these default hue values
    var defaultDefaultHues = {
      'default': '500',
      'hue-1': '300',
      'hue-2': '800',
      'hue-3': 'A100'
    };
    if (!LIGHT_DEFAULT_HUES[colorType]) LIGHT_DEFAULT_HUES[colorType] = defaultDefaultHues;
    if (!DARK_DEFAULT_HUES[colorType]) DARK_DEFAULT_HUES[colorType] = defaultDefaultHues;
  });
  
  var VALID_HUE_VALUES = [
    '50', '100', '200', '300', '400', '500', '600',
    '700', '800', '900', 'A100', 'A200', 'A400', 'A700'
  ];
  
  function ThemingProvider($mdColorPalette) {
    PALETTES = { };
    THEMES = { };
  
    var themingProvider;
    var defaultTheme = 'default';
    var alwaysWatchTheme = false;
  
    // Load JS Defined Palettes
    angular.extend(PALETTES, $mdColorPalette);
  
    // Default theme defined in core.js
  
    ThemingService.$inject = ["$rootScope", "$log"];
    return themingProvider = {
      definePalette: definePalette,
      extendPalette: extendPalette,
      theme: registerTheme,
  
      setDefaultTheme: function(theme) {
        defaultTheme = theme;
      },
      alwaysWatchTheme: function(alwaysWatch) {
        alwaysWatchTheme = alwaysWatch;
      },
      $get: ThemingService,
      _LIGHT_DEFAULT_HUES: LIGHT_DEFAULT_HUES,
      _DARK_DEFAULT_HUES: DARK_DEFAULT_HUES,
      _PALETTES: PALETTES,
      _THEMES: THEMES,
      _parseRules: parseRules,
      _rgba: rgba
    };
  
    // Example: $mdThemingProvider.definePalette('neonRed', { 50: '#f5fafa', ... });
    function definePalette(name, map) {
      map = map || {};
      PALETTES[name] = checkPaletteValid(name, map);
      return themingProvider;
    }
  
    // Returns an new object which is a copy of a given palette `name` with variables from
    // `map` overwritten
    // Example: var neonRedMap = $mdThemingProvider.extendPalette('red', { 50: '#f5fafafa' });
    function extendPalette(name, map) {
      return checkPaletteValid(name,  angular.extend({}, PALETTES[name] || {}, map) );
    }
  
    // Make sure that palette has all required hues
    function checkPaletteValid(name, map) {
      var missingColors = VALID_HUE_VALUES.filter(function(field) {
        return !map[field];
      });
      if (missingColors.length) {
        throw new Error("Missing colors %1 in palette %2!"
                        .replace('%1', missingColors.join(', '))
                        .replace('%2', name));
      }
  
      return map;
    }
  
    // Register a theme (which is a collection of color palettes to use with various states
    // ie. warn, accent, primary )
    // Optionally inherit from an existing theme
    // $mdThemingProvider.theme('custom-theme').primaryPalette('red');
    function registerTheme(name, inheritFrom) {
      if (THEMES[name]) return THEMES[name];
  
      inheritFrom = inheritFrom || 'default';
  
      var parentTheme = typeof inheritFrom === 'string' ? THEMES[inheritFrom] : inheritFrom;
      var theme = new Theme(name);
  
      if (parentTheme) {
        angular.forEach(parentTheme.colors, function(color, colorType) {
          theme.colors[colorType] = {
            name: color.name,
            // Make sure a COPY of the hues is given to the child color,
            // not the same reference.
            hues: angular.extend({}, color.hues)
          };
        });
      }
      THEMES[name] = theme;
  
      return theme;
    }
  
    function Theme(name) {
      var self = this;
      self.name = name;
      self.colors = {};
  
      self.dark = setDark;
      setDark(false);
  
      function setDark(isDark) {
        isDark = arguments.length === 0 ? true : !!isDark;
  
        // If no change, abort
        if (isDark === self.isDark) return;
  
        self.isDark = isDark;
  
        self.foregroundPalette = self.isDark ? LIGHT_FOREGROUND : DARK_FOREGROUND;
        self.foregroundShadow = self.isDark ? DARK_SHADOW : LIGHT_SHADOW;
  
        // Light and dark themes have different default hues.
        // Go through each existing color type for this theme, and for every
        // hue value that is still the default hue value from the previous light/dark setting,
        // set it to the default hue value from the new light/dark setting.
        var newDefaultHues = self.isDark ? DARK_DEFAULT_HUES : LIGHT_DEFAULT_HUES;
        var oldDefaultHues = self.isDark ? LIGHT_DEFAULT_HUES : DARK_DEFAULT_HUES;
        angular.forEach(newDefaultHues, function(newDefaults, colorType) {
          var color = self.colors[colorType];
          var oldDefaults = oldDefaultHues[colorType];
          if (color) {
            for (var hueName in color.hues) {
              if (color.hues[hueName] === oldDefaults[hueName]) {
                color.hues[hueName] = newDefaults[hueName];
              }
            }
          }
        });
  
        return self;
      }
  
      THEME_COLOR_TYPES.forEach(function(colorType) {
        var defaultHues = (self.isDark ? DARK_DEFAULT_HUES : LIGHT_DEFAULT_HUES)[colorType];
        self[colorType + 'Palette'] = function setPaletteType(paletteName, hues) {
          var color = self.colors[colorType] = {
            name: paletteName,
            hues: angular.extend({}, defaultHues, hues)
          };
  
          Object.keys(color.hues).forEach(function(name) {
            if (!defaultHues[name]) {
              throw new Error("Invalid hue name '%1' in theme %2's %3 color %4. Available hue names: %4"
                .replace('%1', name)
                .replace('%2', self.name)
                .replace('%3', paletteName)
                .replace('%4', Object.keys(defaultHues).join(', '))
              );
            }
          });
          Object.keys(color.hues).map(function(key) {
            return color.hues[key];
          }).forEach(function(hueValue) {
            if (VALID_HUE_VALUES.indexOf(hueValue) == -1) {
              throw new Error("Invalid hue value '%1' in theme %2's %3 color %4. Available hue values: %5"
                .replace('%1', hueValue)
                .replace('%2', self.name)
                .replace('%3', colorType)
                .replace('%4', paletteName)
                .replace('%5', VALID_HUE_VALUES.join(', '))
              );
            }
          });
          return self;
        };
  
        self[colorType + 'Color'] = function() {
          var args = Array.prototype.slice.call(arguments);
          console.warn('$mdThemingProviderTheme.' + colorType + 'Color() has been deprecated. ' +
                       'Use $mdThemingProviderTheme.' + colorType + 'Palette() instead.');
          return self[colorType + 'Palette'].apply(self, args);
        };
      });
    }
  
    /**
     * @ngdoc service
     * @name $mdTheming
     *
     * @description
     *
     * Service that makes an element apply theming related classes to itself.
     *
     * ```js
     * app.directive('myFancyDirective', function($mdTheming) {
     *   return {
     *     restrict: 'e',
     *     link: function(scope, el, attrs) {
     *       $mdTheming(el);
     *     }
     *   };
     * });
     * ```
     * @param {el=} element to apply theming to
     */
    /* ngInject */
    function ThemingService($rootScope, $log) {
  
      applyTheme.inherit = function(el, parent) {
        var ctrl = parent.controller('mdTheme');
  
        var attrThemeValue = el.attr('md-theme-watch');
        if ( (alwaysWatchTheme || angular.isDefined(attrThemeValue)) && attrThemeValue != 'false') {
          var deregisterWatch = $rootScope.$watch(function() {
            return ctrl && ctrl.$mdTheme || defaultTheme;
          }, changeTheme);
          el.on('$destroy', deregisterWatch);
        } else {
          var theme = ctrl && ctrl.$mdTheme || defaultTheme;
          changeTheme(theme);
        }
  
        function changeTheme(theme) {
          if (!registered(theme)) {
            $log.warn('Attempted to use unregistered theme \'' + theme + '\'. ' +
                      'Register it with $mdThemingProvider.theme().');
          }
          var oldTheme = el.data('$mdThemeName');
          if (oldTheme) el.removeClass('md-' + oldTheme +'-theme');
          el.addClass('md-' + theme + '-theme');
          el.data('$mdThemeName', theme);
          if (ctrl) {
            el.data('$mdThemeController', ctrl);
          }
        }
      };
  
      applyTheme.THEMES = angular.extend({}, THEMES);
      applyTheme.defaultTheme = function() { return defaultTheme; };
      applyTheme.registered = registered;
  
      return applyTheme;
  
      function registered(themeName) {
        if (themeName === undefined || themeName === '') return true;
        return applyTheme.THEMES[themeName] !== undefined;
      }
  
      function applyTheme(scope, el) {
        // Allow us to be invoked via a linking function signature.
        if (el === undefined) {
          el = scope;
          scope = undefined;
        }
        if (scope === undefined) {
          scope = $rootScope;
        }
        applyTheme.inherit(el, el);
      }
    }
  }
  ThemingProvider.$inject = ["$mdColorPalette"];
  
  function ThemingDirective($mdTheming, $interpolate, $log) {
    return {
      priority: 100,
      link: {
        pre: function(scope, el, attrs) {
          var ctrl = {
            $setTheme: function(theme) {
              if (!$mdTheming.registered(theme)) {
                $log.warn('attempted to use unregistered theme \'' + theme + '\'');
              }
              ctrl.$mdTheme = theme;
            }
          };
          el.data('$mdThemeController', ctrl);
          ctrl.$setTheme($interpolate(attrs.mdTheme)(scope));
          attrs.$observe('mdTheme', ctrl.$setTheme);
        }
      }
    };
  }
  ThemingDirective.$inject = ["$mdTheming", "$interpolate", "$log"];
  
  function ThemableDirective($mdTheming) {
    return $mdTheming;
  }
  ThemableDirective.$inject = ["$mdTheming"];
  
  function parseRules(theme, colorType, rules) {
    checkValidPalette(theme, colorType);
  
    rules = rules.replace(/THEME_NAME/g, theme.name);
    var generatedRules = [];
    var color = theme.colors[colorType];
  
    var themeNameRegex = new RegExp('.md-' + theme.name + '-theme', 'g');
    // Matches '{{ primary-color }}', etc
    var hueRegex = new RegExp('(\'|")?{{\\s*(' + colorType + ')-(color|contrast)-?(\\d\\.?\\d*)?\\s*}}(\"|\')?','g');
    var simpleVariableRegex = /'?"?\{\{\s*([a-zA-Z]+)-(A?\d+|hue\-[0-3]|shadow)-?(\d\.?\d*)?\s*\}\}'?"?/g;
    var palette = PALETTES[color.name];
  
    // find and replace simple variables where we use a specific hue, not an entire palette
    // eg. "{{primary-100}}"
    //\(' + THEME_COLOR_TYPES.join('\|') + '\)'
    rules = rules.replace(simpleVariableRegex, function(match, colorType, hue, opacity) {
      if (colorType === 'foreground') {
        if (hue == 'shadow') {
          return theme.foregroundShadow;
        } else {
          return theme.foregroundPalette[hue] || theme.foregroundPalette['1'];
        }
      }
      if (hue.indexOf('hue') === 0) {
        hue = theme.colors[colorType].hues[hue];
      }
      return rgba( (PALETTES[ theme.colors[colorType].name ][hue] || '').value, opacity );
    });
  
    // For each type, generate rules for each hue (ie. default, md-hue-1, md-hue-2, md-hue-3)
    angular.forEach(color.hues, function(hueValue, hueName) {
      var newRule = rules
        .replace(hueRegex, function(match, _, colorType, hueType, opacity) {
          return rgba(palette[hueValue][hueType === 'color' ? 'value' : 'contrast'], opacity);
        });
      if (hueName !== 'default') {
        newRule = newRule.replace(themeNameRegex, '.md-' + theme.name + '-theme.md-' + hueName);
      }
  
      // Don't apply a selector rule to the default theme, making it easier to override
      // styles of the base-component
      if (theme.name == 'default') {
        newRule = newRule.replace(/\.md-default-theme/g, '');
      }
      generatedRules.push(newRule);
    });
  
    return generatedRules;
  }
  
  // Generate our themes at run time given the state of THEMES and PALETTES
  function generateThemes($injector) {
  
    var head = document.getElementsByTagName('head')[0];
    var firstChild = head ? head.firstElementChild : null;
    var themeCss = $injector.has('$MD_THEME_CSS') ? $injector.get('$MD_THEME_CSS') : '';
  
    if ( !firstChild ) return;
    if (themeCss.length === 0) return; // no rules, so no point in running this expensive task
  
    // Expose contrast colors for palettes to ensure that text is always readable
    angular.forEach(PALETTES, sanitizePalette);
  
    // MD_THEME_CSS is a string generated by the build process that includes all the themable
    // components as templates
  
    // Break the CSS into individual rules
    var rulesByType = {};
    var rules = themeCss
                    .split(/\}(?!(\}|'|"|;))/)
                    .filter(function(rule) { return rule && rule.length; })
                    .map(function(rule) { return rule.trim() + '}'; });
  
  
    var ruleMatchRegex = new RegExp('md-(' + THEME_COLOR_TYPES.join('|') + ')', 'g');
  
    THEME_COLOR_TYPES.forEach(function(type) {
      rulesByType[type] = '';
    });
  
  
    // Sort the rules based on type, allowing us to do color substitution on a per-type basis
    rules.forEach(function(rule) {
      var match = rule.match(ruleMatchRegex);
      // First: test that if the rule has '.md-accent', it goes into the accent set of rules
      for (var i = 0, type; type = THEME_COLOR_TYPES[i]; i++) {
        if (rule.indexOf('.md-' + type) > -1) {
          return rulesByType[type] += rule;
        }
      }
  
      // If no eg 'md-accent' class is found, try to just find 'accent' in the rule and guess from
      // there
      for (i = 0; type = THEME_COLOR_TYPES[i]; i++) {
        if (rule.indexOf(type) > -1) {
          return rulesByType[type] += rule;
        }
      }
  
      // Default to the primary array
      return rulesByType[DEFAULT_COLOR_TYPE] += rule;
    });
  
      // For each theme, use the color palettes specified for
      // `primary`, `warn` and `accent` to generate CSS rules.
  
      angular.forEach(THEMES, function(theme) {
        if ( !GENERATED[theme.name] ) {
  
  
          THEME_COLOR_TYPES.forEach(function(colorType) {
            var styleStrings = parseRules(theme, colorType, rulesByType[colorType]);
            while (styleStrings.length) {
              var style = document.createElement('style');
                  style.setAttribute('type', 'text/css');
              style.appendChild(document.createTextNode(styleStrings.shift()));
              head.insertBefore(style, firstChild);
            }
          });
  
  
          if (theme.colors.primary.name == theme.colors.accent.name) {
            console.warn("$mdThemingProvider: Using the same palette for primary and" +
                         " accent. This violates the material design spec.");
          }
  
          GENERATED[theme.name] = true;
        }
      });
  
  
    // *************************
    // Internal functions
    // *************************
  
    // The user specifies a 'default' contrast color as either light or dark,
    // then explicitly lists which hues are the opposite contrast (eg. A100 has dark, A200 has light)
    function sanitizePalette(palette) {
      var defaultContrast = palette.contrastDefaultColor;
      var lightColors = palette.contrastLightColors || [];
      var strongLightColors = palette.contrastStrongLightColors || [];
      var darkColors = palette.contrastDarkColors || [];
  
      // These colors are provided as space-separated lists
      if (typeof lightColors === 'string') lightColors = lightColors.split(' ');
      if (typeof strongLightColors === 'string') strongLightColors = strongLightColors.split(' ');
      if (typeof darkColors === 'string') darkColors = darkColors.split(' ');
  
      // Cleanup after ourselves
      delete palette.contrastDefaultColor;
      delete palette.contrastLightColors;
      delete palette.contrastStrongLightColors;
      delete palette.contrastDarkColors;
  
      // Change { 'A100': '#fffeee' } to { 'A100': { value: '#fffeee', contrast:DARK_CONTRAST_COLOR }
      angular.forEach(palette, function(hueValue, hueName) {
        if (angular.isObject(hueValue)) return; // Already converted
        // Map everything to rgb colors
        var rgbValue = colorToRgbaArray(hueValue);
        if (!rgbValue) {
          throw new Error("Color %1, in palette %2's hue %3, is invalid. Hex or rgb(a) color expected."
                          .replace('%1', hueValue)
                          .replace('%2', palette.name)
                          .replace('%3', hueName));
        }
  
        palette[hueName] = {
          value: rgbValue,
          contrast: getContrastColor()
        };
        function getContrastColor() {
          if (defaultContrast === 'light') {
            if (darkColors.indexOf(hueName) > -1) {
              return DARK_CONTRAST_COLOR;
            } else {
              return strongLightColors.indexOf(hueName) > -1 ? STRONG_LIGHT_CONTRAST_COLOR
                : LIGHT_CONTRAST_COLOR;
            }
          } else {
            if (lightColors.indexOf(hueName) > -1) {
              return strongLightColors.indexOf(hueName) > -1 ? STRONG_LIGHT_CONTRAST_COLOR
                : LIGHT_CONTRAST_COLOR;
            } else {
              return DARK_CONTRAST_COLOR;
            }
          }
        }
      });
    }
  
  
  }
  generateThemes.$inject = ["$injector"];
  
  function checkValidPalette(theme, colorType) {
    // If theme attempts to use a palette that doesnt exist, throw error
    if (!PALETTES[ (theme.colors[colorType] || {}).name ]) {
      throw new Error(
        "You supplied an invalid color palette for theme %1's %2 palette. Available palettes: %3"
                      .replace('%1', theme.name)
                      .replace('%2', colorType)
                      .replace('%3', Object.keys(PALETTES).join(', '))
      );
    }
  }
  
  function colorToRgbaArray(clr) {
    if (angular.isArray(clr) && clr.length == 3) return clr;
    if (/^rgb/.test(clr)) {
      return clr.replace(/(^\s*rgba?\(|\)\s*$)/g, '').split(',').map(function(value, i) {
        return i == 3 ? parseFloat(value, 10) : parseInt(value, 10);
      });
    }
    if (clr.charAt(0) == '#') clr = clr.substring(1);
    if (!/^([a-fA-F0-9]{3}){1,2}$/g.test(clr)) return;
  
    var dig = clr.length / 3;
    var red = clr.substr(0, dig);
    var grn = clr.substr(dig, dig);
    var blu = clr.substr(dig * 2);
    if (dig === 1) {
      red += red;
      grn += grn;
      blu += blu;
    }
    return [parseInt(red, 16), parseInt(grn, 16), parseInt(blu, 16)];
  }
  
  function rgba(rgbArray, opacity) {
    if ( !rgbArray ) return "rgb('0,0,0')";
  
    if (rgbArray.length == 4) {
      rgbArray = angular.copy(rgbArray);
      opacity ? rgbArray.pop() : opacity = rgbArray.pop();
    }
    return opacity && (typeof opacity == 'number' || (typeof opacity == 'string' && opacity.length)) ?
      'rgba(' + rgbArray.join(',') + ',' + opacity + ')' :
      'rgb(' + rgbArray.join(',') + ')';
  }
  
  
  (function(){ 
  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 {  background-color: '{{background-900-0.0}}'; }  md-backdrop.md-opaque.md-THEME_NAME-theme {    background-color: '{{background-900-0.48}}'; }  md-backdrop.md-opaque.md-THEME_NAME-theme.ng-enter {    background-color: '{{background-900-0.0}}'; }    md-backdrop.md-opaque.md-THEME_NAME-theme.ng-enter.md-opaque.md-THEME_NAME-theme.ng-enter-active {      background-color: '{{background-900-0.48}}'; }  md-backdrop.md-opaque.md-THEME_NAME-theme.ng-leave.md-opaque.md-THEME_NAME-theme.ng-leave-active {    background-color: '{{background-900-0.0}}'; }md-bottom-sheet.md-THEME_NAME-theme {  background-color: '{{background-50}}';  border-top-color: '{{background-300}}'; }  md-bottom-sheet.md-THEME_NAME-theme.md-list md-list-item {    color: '{{foreground-1}}'; }  md-bottom-sheet.md-THEME_NAME-theme .md-subheader {    background-color: '{{background-50}}'; }  md-bottom-sheet.md-THEME_NAME-theme .md-subheader {    color: '{{foreground-1}}'; }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-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-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-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 {  color: '{{foreground-1}}';  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-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-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-menu-content.md-THEME_NAME-theme {  background-color: '{{background-color}}'; }  md-menu-content.md-THEME_NAME-theme md-menu-divider {    background-color: '{{foreground-4}}'; }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 .md-select-value {  border-bottom-color: '{{foreground-4}}'; }  md-select.md-THEME_NAME-theme .md-select-value.md-select-placeholder {    color: '{{foreground-3}}'; }md-select.md-THEME_NAME-theme.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-value {  border-bottom-color: '{{primary-color}}';  color: '{{ foreground-1 }}'; }  md-select.md-THEME_NAME-theme:not([disabled]):focus .md-select-value.md-select-placeholder {    color: '{{ foreground-1 }}'; }md-select.md-THEME_NAME-theme:not([disabled]):focus.md-accent .md-select-value {  border-bottom-color: '{{accent-color}}'; }md-select.md-THEME_NAME-theme:not([disabled]):focus.md-warn .md-select-value {  border-bottom-color: '{{warn-color}}'; }md-select.md-THEME_NAME-theme[disabled] .md-select-value {  color: '{{foreground-3}}'; }  md-select.md-THEME_NAME-theme[disabled] .md-select-value.md-select-placeholder {    color: '{{foreground-3}}'; }md-select-menu.md-THEME_NAME-theme md-option[disabled] {  color: '{{foreground-3}}'; }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-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-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-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-tabs-wrapper > md-tabs-canvas > md-pagination-wrapper > md-tab-item:not([disabled]) {    color: '{{accent-100}}'; }    md-tabs.md-THEME_NAME-theme.md-accent > md-tabs-wrapper > md-tabs-canvas > md-pagination-wrapper > md-tab-item:not([disabled]).md-active, md-tabs.md-THEME_NAME-theme.md-accent > md-tabs-wrapper > md-tabs-canvas > md-pagination-wrapper > md-tab-item:not([disabled]).md-focused {      color: '{{accent-contrast}}'; }    md-tabs.md-THEME_NAME-theme.md-accent > md-tabs-wrapper > md-tabs-canvas > md-pagination-wrapper > md-tab-item:not([disabled]).md-focused {      background: '{{accent-contrast-0.1}}'; }  md-tabs.md-THEME_NAME-theme.md-accent > md-tabs-wrapper > md-tabs-canvas > md-pagination-wrapper > md-ink-bar {    color: '{{primary-600-1}}';    background: '{{primary-600-1}}'; }md-tabs.md-THEME_NAME-theme.md-primary > md-tabs-wrapper {  background-color: '{{primary-color}}'; }  md-tabs.md-THEME_NAME-theme.md-primary > md-tabs-wrapper > md-tabs-canvas > md-pagination-wrapper > md-tab-item:not([disabled]) {    color: '{{primary-100}}'; }    md-tabs.md-THEME_NAME-theme.md-primary > md-tabs-wrapper > md-tabs-canvas > md-pagination-wrapper > md-tab-item:not([disabled]).md-active, md-tabs.md-THEME_NAME-theme.md-primary > md-tabs-wrapper > md-tabs-canvas > md-pagination-wrapper > md-tab-item:not([disabled]).md-focused {      color: '{{primary-contrast}}'; }    md-tabs.md-THEME_NAME-theme.md-primary > md-tabs-wrapper > md-tabs-canvas > md-pagination-wrapper > md-tab-item:not([disabled]).md-focused {      background: '{{primary-contrast-0.1}}'; }md-tabs.md-THEME_NAME-theme.md-warn > md-tabs-wrapper {  background-color: '{{warn-color}}'; }  md-tabs.md-THEME_NAME-theme.md-warn > md-tabs-wrapper > md-tabs-canvas > md-pagination-wrapper > md-tab-item:not([disabled]) {    color: '{{warn-100}}'; }    md-tabs.md-THEME_NAME-theme.md-warn > md-tabs-wrapper > md-tabs-canvas > md-pagination-wrapper > md-tab-item:not([disabled]).md-active, md-tabs.md-THEME_NAME-theme.md-warn > md-tabs-wrapper > md-tabs-canvas > md-pagination-wrapper > md-tab-item:not([disabled]).md-focused {      color: '{{warn-contrast}}'; }    md-tabs.md-THEME_NAME-theme.md-warn > md-tabs-wrapper > md-tabs-canvas > md-pagination-wrapper > md-tab-item:not([disabled]).md-focused {      background: '{{warn-contrast-0.1}}'; }md-toolbar > md-tabs.md-THEME_NAME-theme > md-tabs-wrapper {  background-color: '{{primary-color}}'; }  md-toolbar > md-tabs.md-THEME_NAME-theme > md-tabs-wrapper > md-tabs-canvas > md-pagination-wrapper > md-tab-item:not([disabled]) {    color: '{{primary-100}}'; }    md-toolbar > md-tabs.md-THEME_NAME-theme > md-tabs-wrapper > md-tabs-canvas > md-pagination-wrapper > md-tab-item:not([disabled]).md-active, md-toolbar > md-tabs.md-THEME_NAME-theme > md-tabs-wrapper > md-tabs-canvas > md-pagination-wrapper > md-tab-item:not([disabled]).md-focused {      color: '{{primary-contrast}}'; }    md-toolbar > md-tabs.md-THEME_NAME-theme > md-tabs-wrapper > md-tabs-canvas > md-pagination-wrapper > md-tab-item:not([disabled]).md-focused {      background: '{{primary-contrast-0.1}}'; }md-toolbar.md-accent > md-tabs.md-THEME_NAME-theme > md-tabs-wrapper {  background-color: '{{accent-color}}'; }  md-toolbar.md-accent > md-tabs.md-THEME_NAME-theme > md-tabs-wrapper > md-tabs-canvas > md-pagination-wrapper > md-tab-item:not([disabled]) {    color: '{{accent-100}}'; }    md-toolbar.md-accent > md-tabs.md-THEME_NAME-theme > md-tabs-wrapper > md-tabs-canvas > md-pagination-wrapper > md-tab-item:not([disabled]).md-active, md-toolbar.md-accent > md-tabs.md-THEME_NAME-theme > md-tabs-wrapper > md-tabs-canvas > md-pagination-wrapper > md-tab-item:not([disabled]).md-focused {      color: '{{accent-contrast}}'; }    md-toolbar.md-accent > md-tabs.md-THEME_NAME-theme > md-tabs-wrapper > md-tabs-canvas > md-pagination-wrapper > md-tab-item:not([disabled]).md-focused {      background: '{{accent-contrast-0.1}}'; }  md-toolbar.md-accent > md-tabs.md-THEME_NAME-theme > md-tabs-wrapper > md-tabs-canvas > md-pagination-wrapper > md-ink-bar {    color: '{{primary-600-1}}';    background: '{{primary-600-1}}'; }md-toolbar.md-warn > md-tabs.md-THEME_NAME-theme > md-tabs-wrapper {  background-color: '{{warn-color}}'; }  md-toolbar.md-warn > md-tabs.md-THEME_NAME-theme > md-tabs-wrapper > md-tabs-canvas > md-pagination-wrapper > md-tab-item:not([disabled]) {    color: '{{warn-100}}'; }    md-toolbar.md-warn > md-tabs.md-THEME_NAME-theme > md-tabs-wrapper > md-tabs-canvas > md-pagination-wrapper > md-tab-item:not([disabled]).md-active, md-toolbar.md-warn > md-tabs.md-THEME_NAME-theme > md-tabs-wrapper > md-tabs-canvas > md-pagination-wrapper > md-tab-item:not([disabled]).md-focused {      color: '{{warn-contrast}}'; }    md-toolbar.md-warn > md-tabs.md-THEME_NAME-theme > md-tabs-wrapper > md-tabs-canvas > md-pagination-wrapper > md-tab-item:not([disabled]).md-focused {      background: '{{warn-contrast-0.1}}'; }md-toast.md-THEME_NAME-theme {  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}}'; }"); 
  })();
  
  
  ng.material.core = angular.module("material.core");