summaryrefslogtreecommitdiffstats
path: root/ecomp-portal-FE/client/bower_components/angular-material/modules/js/core/core.js
diff options
context:
space:
mode:
Diffstat (limited to 'ecomp-portal-FE/client/bower_components/angular-material/modules/js/core/core.js')
-rw-r--r--ecomp-portal-FE/client/bower_components/angular-material/modules/js/core/core.js3779
1 files changed, 3779 insertions, 0 deletions
diff --git a/ecomp-portal-FE/client/bower_components/angular-material/modules/js/core/core.js b/ecomp-portal-FE/client/bower_components/angular-material/modules/js/core/core.js
new file mode 100644
index 00000000..66c2fc46
--- /dev/null
+++ b/ecomp-portal-FE/client/bower_components/angular-material/modules/js/core/core.js
@@ -0,0 +1,3779 @@
+/*!
+ * Angular Material Design
+ * https://github.com/angular/material
+ * @license MIT
+ * v0.9.8
+ */
+(function( window, angular, undefined ){
+"use strict";
+
+
+/**
+ * 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 queueArgs, alreadyQueued, queueCb, context;
+ return function debounced() {
+ queueArgs = arguments;
+ context = this;
+ queueCb = cb;
+ if (!alreadyQueued) {
+ alreadyQueued = true;
+ $delegate(function() {
+ queueCb.apply(context, queueArgs);
+ alreadyQueued = false;
+ });
+ }
+ };
+ };
+ return $delegate;
+}
+
+angular.module('material.core')
+.factory('$mdConstant', MdConstantFactory);
+
+function MdConstantFactory($$rAF, $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: 600px)',
+ 'gt-sm': '(min-width: 600px)',
+ 'md': '(min-width: 600px) and (max-width: 960px)',
+ 'gt-md': '(min-width: 960px)',
+ 'lg': '(min-width: 960px) and (max-width: 1200px)',
+ 'gt-lg': '(min-width: 1200px)'
+ },
+ MEDIA_PRIORITY: [
+ 'gt-lg',
+ 'lg',
+ 'gt-md',
+ 'md',
+ 'gt-sm',
+ 'sm'
+ ]
+ };
+}
+MdConstantFactory.$inject = ["$$rAF", "$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", "$window", "$mdConstant", function($cacheFactory, $document, $timeout, $q, $window, $mdConstant) {
+ var Util;
+
+ function getNode(el) {
+ return el[0] || el;
+ }
+
+ return Util = {
+ 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 Util.clientRect(element, offsetParent, true);
+ },
+ // Disables scroll around the passed element. Goes up the DOM to find a
+ // disableTarget (a md-content that is scrolling, or the body as a fallback)
+ // and uses CSS/JS to prevent it from scrolling
+ disableScrollAround: function(element) {
+ element = element instanceof angular.element ? element[0] : element;
+ var parentEl = element;
+ var disableTarget;
+
+ // Find the highest level scrolling md-content
+ while (parentEl = this.getClosest(parentEl, 'MD-CONTENT', true)) {
+ if (isScrolling(parentEl)) {
+ disableTarget = angular.element(parentEl)[0];
+ }
+ }
+
+ // Default to the body if no scrolling md-content
+ if (!disableTarget) {
+ disableTarget = $document[0].body;
+ if (!isScrolling(disableTarget)) return angular.noop;
+ }
+
+ if (disableTarget.nodeName == 'BODY') {
+ return disableBodyScroll();
+ } else {
+ return disableElementScroll();
+ }
+
+ // Creates a virtual scrolling mask to absorb touchmove, keyboard, scrollbar clicking, and wheel events
+ function disableElementScroll() {
+ var scrollMask = angular.element('<div class="md-scroll-mask"><div class="md-scroll-mask-bar"></div></div>');
+ var computedStyle = $window.getComputedStyle(disableTarget);
+ var disableRect = disableTarget.getBoundingClientRect();
+ var scrollWidth = disableRect.width - disableTarget.clientWidth;
+ applyStyles(scrollMask[0], {
+ zIndex: computedStyle.zIndex == 'auto' ? 2 : computedStyle.zIndex + 1,
+ width: disableRect.width + 'px',
+ height: disableRect.height + 'px',
+ top: disableRect.top + 'px',
+ left: disableRect.left + 'px'
+ });
+ scrollMask[0].firstElementChild.style.width = scrollWidth + 'px';
+ $document[0].body.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);
+ };
+
+ // Prevent keypresses from elements inside the disableTarget
+ // used to stop the keypresses that could cause the page to scroll
+ // (arrow keys, spacebar, tab, etc).
+ function disableKeyNav(e) {
+ if (disableTarget.contains(e.target)) {
+ e.preventDefault();
+ e.stopImmediatePropagation();
+ }
+ }
+
+ function preventDefault(e) {
+ e.preventDefault();
+ }
+ }
+
+ // Converts the disableTarget (body) to a position fixed block and translate it to the propper scroll position
+ function disableBodyScroll() {
+ var restoreStyle = disableTarget.getAttribute('style') || '';
+ var scrollOffset = disableTarget.scrollTop;
+
+ applyStyles(disableTarget, {
+ position: 'fixed',
+ width: '100%',
+ overflowY: 'scroll',
+ top: -scrollOffset + 'px'
+ });
+
+ return function restoreScroll() {
+ disableTarget.setAttribute('style', restoreStyle);
+ disableTarget.scrollTop = scrollOffset;
+ };
+ }
+
+ function applyStyles (el, styles) {
+ for (var key in styles) {
+ el.style[key] = styles[key];
+ }
+ }
+
+ function isScrolling(el) {
+ if (el instanceof angular.element) el = el[0];
+ return el.scrollHeight > el.offsetHeight;
+ }
+ },
+
+ 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);
+ },
+
+ transitionEndPromise: function(element, opts) {
+ opts = opts || {};
+ var deferred = $q.defer();
+ element.on($mdConstant.CSS.TRANSITIONEND, finished);
+ function finished(ev) {
+ // Make sure this transitionend didn't bubble up from a child
+ if (!ev || ev.target === element[0]) {
+ element.off($mdConstant.CSS.TRANSITIONEND, finished);
+ deferred.resolve();
+ }
+ }
+ if (opts.timeout) $timeout(finished, opts.timeout);
+ return deferred.promise;
+ },
+
+ 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 = Util.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 = Util.now();
+ cb();
+ return Util.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 by default
+ */
+ initOptionalProperties: function (scope, attr, defaults ) {
+ defaults = defaults || { };
+ angular.forEach(scope.$$isolateBindings, function (binding, key) {
+ if (binding.optional && angular.isUndefined(scope[key])) {
+ var hasKey = attr.hasOwnProperty(attr.$normalize(binding.attrName));
+
+ scope[key] = angular.isDefined(defaults[key]) ? defaults[key] : hasKey;
+ }
+ });
+ }
+
+ };
+
+}]);
+
+/*
+ * 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"];
+
+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 = options.resolve || {};
+ var locals = 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"];
+
+ 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')
+ .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"];
+ 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", "$animate", "$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, $animate, $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 interimElemnt.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
+ if (opts && 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 ) {
+ var startSymbol = $interpolate.startSymbol(),
+ endSymbol = $interpolate.endSymbol(),
+ usesStandardSymbols = ((startSymbol === '{{') && (endSymbol === '}}')),
+ processTemplate = usesStandardSymbols ? angular.identity : replaceInterpolationSymbols;
+
+ return function createInterimElementService() {
+ /*
+ * @ngdoc service
+ * @name $$interimElement.$service
+ *
+ * @description
+ * A service used to control inserting and removing an element into the DOM.
+ *
+ */
+ var stack = [];
+ var service;
+ 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 promise that will be resolved or rejected
+ * with hide or cancel, respectively.
+ *
+ * @param {*} options is hashMap of settings
+ * @returns a Promise
+ *
+ */
+ function show(options) {
+ if (stack.length) {
+ return service.cancel().then(function() {
+ return show(options);
+ });
+ } else {
+ var interimElement = new InterimElement(options);
+ stack.push(interimElement);
+ return interimElement.show().then(function() {
+ 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(response) {
+ var interimElement = stack.shift();
+ return interimElement && interimElement.remove().then(function() {
+ interimElement.deferred.resolve(response);
+ });
+ }
+
+ /*
+ * @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 interimElement = stack.shift();
+ return $q.when(interimElement && interimElement.remove().then(function() {
+ interimElement.deferred.reject(reason);
+ }));
+ }
+
+
+ /*
+ * Internal Interim Element Object
+ * Used internally to manage the DOM element and related data
+ */
+ function InterimElement(options) {
+ var self;
+ var hideTimeout, element, showDone, removeDone;
+
+ options = options || {};
+ options = angular.extend({
+ preserveScope: false,
+ scope: options.scope || $rootScope.$new(options.isolateScope),
+ onShow: function(scope, element, options) {
+ return $animate.enter(element, options.parent);
+ },
+ onRemove: function(scope, element, options) {
+ // Element could be undefined if a new element is shown before
+ // the old one finishes compiling.
+ return element && $animate.leave(element) || $q.when();
+ }
+ }, options);
+
+ if (options.template) {
+ options.template = processTemplate(options.template);
+ }
+
+ return self = {
+ options: options,
+ deferred: $q.defer(),
+ show: function() {
+ var compilePromise;
+ if (options.skipCompile) {
+ compilePromise = $q(function(resolve) {
+ resolve({
+ locals: {},
+ link: function() { return options.element; }
+ });
+ });
+ } else {
+ compilePromise = $mdCompiler.compile(options);
+ }
+
+ return showDone = compilePromise.then(function(compileData) {
+ angular.extend(compileData.locals, self.options);
+
+ element = compileData.link(options.scope);
+
+ // Search for parent at insertion time, if not specified
+ if (angular.isFunction(options.parent)) {
+ options.parent = options.parent(options.scope, element, options);
+ } else if (angular.isString(options.parent)) {
+ options.parent = angular.element($document[0].querySelector(options.parent));
+ }
+
+ // If parent querySelector/getter function fails, or it's just null,
+ // find a default.
+ if (!(options.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;
+ }
+ options.parent = angular.element(el);
+ }
+
+ if (options.themable) $mdTheming(element);
+ var ret = options.onShow(options.scope, element, options);
+ return $q.when(ret)
+ .then(function(){
+ // Issue onComplete callback when the `show()` finishes
+ (options.onComplete || angular.noop)(options.scope, element, options);
+ startHideTimeout();
+ });
+
+ function startHideTimeout() {
+ if (options.hideDelay) {
+ hideTimeout = $timeout(service.cancel, options.hideDelay) ;
+ }
+ }
+ }, function(reason) { showDone = true; self.deferred.reject(reason); });
+ },
+ cancelTimeout: function() {
+ if (hideTimeout) {
+ $timeout.cancel(hideTimeout);
+ hideTimeout = undefined;
+ }
+ },
+ remove: function() {
+ self.cancelTimeout();
+ return removeDone = $q.when(showDone).then(function() {
+ var ret = element ? options.onRemove(options.scope, element, options) : true;
+ return $q.when(ret).then(function() {
+ if (!options.preserveScope) options.scope.$destroy();
+ removeDone = true;
+ });
+ });
+ }
+ };
+ }
+ };
+
+ /*
+ * 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) {
+
+ 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) {
+ $timeout(function () { createRipple(0, 0); }, 0, false);
+ }
+ 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;
+
+ $timeout(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);
+ }, 0, 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 ];
+ $timeout(function () { updateElement(ripple); }, 0, false);
+ }
+
+ /**
+ * 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"];
+
+/**
+ * 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);
+ }
+ };
+
+ 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.md-opaque.md-THEME_NAME-theme { background-color: '{{foreground-4-0.5}}'; }md-bottom-sheet.md-THEME_NAME-theme { background-color: '{{background-50}}'; border-top-color: '{{background-300}}'; } md-bottom-sheet.md-THEME_NAME-theme.md-list md-list-item { color: '{{foreground-1}}'; } md-bottom-sheet.md-THEME_NAME-theme .md-subheader { background-color: '{{background-50}}'; } md-bottom-sheet.md-THEME_NAME-theme .md-subheader { color: '{{foreground-1}}'; }a.md-button.md-THEME_NAME-theme, .md-button.md-THEME_NAME-theme { border-radius: 3px; } a.md-button.md-THEME_NAME-theme:not([disabled]):hover, .md-button.md-THEME_NAME-theme:not([disabled]):hover { background-color: '{{background-500-0.2}}'; } a.md-button.md-THEME_NAME-theme:not([disabled]).md-focused, .md-button.md-THEME_NAME-theme:not([disabled]).md-focused { background-color: '{{background-500-0.2}}'; } a.md-button.md-THEME_NAME-theme:not([disabled]).md-icon-button:hover, .md-button.md-THEME_NAME-theme:not([disabled]).md-icon-button:hover { background-color: transparent; } a.md-button.md-THEME_NAME-theme.md-fab, .md-button.md-THEME_NAME-theme.md-fab { border-radius: 50%; background-color: '{{accent-color}}'; color: '{{accent-contrast}}'; } a.md-button.md-THEME_NAME-theme.md-fab md-icon, .md-button.md-THEME_NAME-theme.md-fab md-icon { color: '{{accent-contrast}}'; } a.md-button.md-THEME_NAME-theme.md-fab:not([disabled]):hover, .md-button.md-THEME_NAME-theme.md-fab:not([disabled]):hover { background-color: '{{accent-color}}'; } a.md-button.md-THEME_NAME-theme.md-fab:not([disabled]).md-focused, .md-button.md-THEME_NAME-theme.md-fab:not([disabled]).md-focused { background-color: '{{accent-A700}}'; } a.md-button.md-THEME_NAME-theme.md-icon-button, .md-button.md-THEME_NAME-theme.md-icon-button { border-radius: 50%; } a.md-button.md-THEME_NAME-theme.md-primary, .md-button.md-THEME_NAME-theme.md-primary { color: '{{primary-color}}'; } a.md-button.md-THEME_NAME-theme.md-primary.md-raised, a.md-button.md-THEME_NAME-theme.md-primary.md-fab, .md-button.md-THEME_NAME-theme.md-primary.md-raised, .md-button.md-THEME_NAME-theme.md-primary.md-fab { color: '{{primary-contrast}}'; background-color: '{{primary-color}}'; } a.md-button.md-THEME_NAME-theme.md-primary.md-raised:not([disabled]) md-icon, a.md-button.md-THEME_NAME-theme.md-primary.md-fab:not([disabled]) md-icon, .md-button.md-THEME_NAME-theme.md-primary.md-raised:not([disabled]) md-icon, .md-button.md-THEME_NAME-theme.md-primary.md-fab:not([disabled]) md-icon { color: '{{primary-contrast}}'; } a.md-button.md-THEME_NAME-theme.md-primary.md-raised:not([disabled]):hover, a.md-button.md-THEME_NAME-theme.md-primary.md-fab:not([disabled]):hover, .md-button.md-THEME_NAME-theme.md-primary.md-raised:not([disabled]):hover, .md-button.md-THEME_NAME-theme.md-primary.md-fab:not([disabled]):hover { background-color: '{{primary-color}}'; } a.md-button.md-THEME_NAME-theme.md-primary.md-raised:not([disabled]).md-focused, a.md-button.md-THEME_NAME-theme.md-primary.md-fab:not([disabled]).md-focused, .md-button.md-THEME_NAME-theme.md-primary.md-raised:not([disabled]).md-focused, .md-button.md-THEME_NAME-theme.md-primary.md-fab:not([disabled]).md-focused { background-color: '{{primary-600}}'; } a.md-button.md-THEME_NAME-theme.md-primary:not([disabled]) md-icon, .md-button.md-THEME_NAME-theme.md-primary:not([disabled]) md-icon { color: '{{primary-color}}'; } a.md-button.md-THEME_NAME-theme.md-fab, .md-button.md-THEME_NAME-theme.md-fab { border-radius: 50%; background-color: '{{accent-color}}'; color: '{{accent-contrast}}'; } a.md-button.md-THEME_NAME-theme.md-fab:not([disabled]) .md-icon, .md-button.md-THEME_NAME-theme.md-fab:not([disabled]) .md-icon { color: '{{accent-contrast}}'; } a.md-button.md-THEME_NAME-theme.md-fab:not([disabled]):hover, .md-button.md-THEME_NAME-theme.md-fab:not([disabled]):hover { background-color: '{{accent-color}}'; } a.md-button.md-THEME_NAME-theme.md-fab:not([disabled]).md-focused, .md-button.md-THEME_NAME-theme.md-fab:not([disabled]).md-focused { background-color: '{{accent-A700}}'; } a.md-button.md-THEME_NAME-theme.md-raised, .md-button.md-THEME_NAME-theme.md-raised { color: '{{background-contrast}}'; background-color: '{{background-50}}'; } a.md-button.md-THEME_NAME-theme.md-raised:not([disabled]) .md-icon, .md-button.md-THEME_NAME-theme.md-raised:not([disabled]) .md-icon { color: '{{background-contrast}}'; } a.md-button.md-THEME_NAME-theme.md-raised:not([disabled]):hover, .md-button.md-THEME_NAME-theme.md-raised:not([disabled]):hover { background-color: '{{background-50}}'; } a.md-button.md-THEME_NAME-theme.md-raised:not([disabled]).md-focused, .md-button.md-THEME_NAME-theme.md-raised:not([disabled]).md-focused { background-color: '{{background-200}}'; } a.md-button.md-THEME_NAME-theme.md-warn, .md-button.md-THEME_NAME-theme.md-warn { color: '{{warn-color}}'; } a.md-button.md-THEME_NAME-theme.md-warn.md-raised, a.md-button.md-THEME_NAME-theme.md-warn.md-fab, .md-button.md-THEME_NAME-theme.md-warn.md-raised, .md-button.md-THEME_NAME-theme.md-warn.md-fab { color: '{{warn-contrast}}'; background-color: '{{warn-color}}'; } a.md-button.md-THEME_NAME-theme.md-warn.md-raised:not([disabled]) md-icon, a.md-button.md-THEME_NAME-theme.md-warn.md-fab:not([disabled]) md-icon, .md-button.md-THEME_NAME-theme.md-warn.md-raised:not([disabled]) md-icon, .md-button.md-THEME_NAME-theme.md-warn.md-fab:not([disabled]) md-icon { color: '{{warn-contrast}}'; } a.md-button.md-THEME_NAME-theme.md-warn.md-raised:not([disabled]):hover, a.md-button.md-THEME_NAME-theme.md-warn.md-fab:not([disabled]):hover, .md-button.md-THEME_NAME-theme.md-warn.md-raised:not([disabled]):hover, .md-button.md-THEME_NAME-theme.md-warn.md-fab:not([disabled]):hover { background-color: '{{warn-color}}'; } a.md-button.md-THEME_NAME-theme.md-warn.md-raised:not([disabled]).md-focused, a.md-button.md-THEME_NAME-theme.md-warn.md-fab:not([disabled]).md-focused, .md-button.md-THEME_NAME-theme.md-warn.md-raised:not([disabled]).md-focused, .md-button.md-THEME_NAME-theme.md-warn.md-fab:not([disabled]).md-focused { background-color: '{{warn-700}}'; } a.md-button.md-THEME_NAME-theme.md-warn:not([disabled]) md-icon, .md-button.md-THEME_NAME-theme.md-warn:not([disabled]) md-icon { color: '{{warn-color}}'; } a.md-button.md-THEME_NAME-theme.md-accent, .md-button.md-THEME_NAME-theme.md-accent { color: '{{accent-color}}'; } a.md-button.md-THEME_NAME-theme.md-accent.md-raised, a.md-button.md-THEME_NAME-theme.md-accent.md-fab, .md-button.md-THEME_NAME-theme.md-accent.md-raised, .md-button.md-THEME_NAME-theme.md-accent.md-fab { color: '{{accent-contrast}}'; background-color: '{{accent-color}}'; } a.md-button.md-THEME_NAME-theme.md-accent.md-raised:not([disabled]) md-icon, a.md-button.md-THEME_NAME-theme.md-accent.md-fab:not([disabled]) md-icon, .md-button.md-THEME_NAME-theme.md-accent.md-raised:not([disabled]) md-icon, .md-button.md-THEME_NAME-theme.md-accent.md-fab:not([disabled]) md-icon { color: '{{accent-contrast}}'; } a.md-button.md-THEME_NAME-theme.md-accent.md-raised:not([disabled]):hover, a.md-button.md-THEME_NAME-theme.md-accent.md-fab:not([disabled]):hover, .md-button.md-THEME_NAME-theme.md-accent.md-raised:not([disabled]):hover, .md-button.md-THEME_NAME-theme.md-accent.md-fab:not([disabled]):hover { background-color: '{{accent-color}}'; } a.md-button.md-THEME_NAME-theme.md-accent.md-raised:not([disabled]).md-focused, a.md-button.md-THEME_NAME-theme.md-accent.md-fab:not([disabled]).md-focused, .md-button.md-THEME_NAME-theme.md-accent.md-raised:not([disabled]).md-focused, .md-button.md-THEME_NAME-theme.md-accent.md-fab:not([disabled]).md-focused { background-color: '{{accent-700}}'; } a.md-button.md-THEME_NAME-theme.md-accent:not([disabled]) md-icon, .md-button.md-THEME_NAME-theme.md-accent:not([disabled]) md-icon { color: '{{accent-color}}'; } a.md-button.md-THEME_NAME-theme[disabled], a.md-button.md-THEME_NAME-theme.md-raised[disabled], a.md-button.md-THEME_NAME-theme.md-fab[disabled], a.md-button.md-THEME_NAME-theme.md-accent[disabled], a.md-button.md-THEME_NAME-theme.md-warn[disabled], .md-button.md-THEME_NAME-theme[disabled], .md-button.md-THEME_NAME-theme.md-raised[disabled], .md-button.md-THEME_NAME-theme.md-fab[disabled], .md-button.md-THEME_NAME-theme.md-accent[disabled], .md-button.md-THEME_NAME-theme.md-warn[disabled] { color: '{{foreground-3}}'; cursor: not-allowed; } a.md-button.md-THEME_NAME-theme[disabled] md-icon, a.md-button.md-THEME_NAME-theme.md-raised[disabled] md-icon, a.md-button.md-THEME_NAME-theme.md-fab[disabled] md-icon, a.md-button.md-THEME_NAME-theme.md-accent[disabled] md-icon, a.md-button.md-THEME_NAME-theme.md-warn[disabled] md-icon, .md-button.md-THEME_NAME-theme[disabled] md-icon, .md-button.md-THEME_NAME-theme.md-raised[disabled] md-icon, .md-button.md-THEME_NAME-theme.md-fab[disabled] md-icon, .md-button.md-THEME_NAME-theme.md-accent[disabled] md-icon, .md-button.md-THEME_NAME-theme.md-warn[disabled] md-icon { color: '{{foreground-3}}'; } a.md-button.md-THEME_NAME-theme.md-raised[disabled], a.md-button.md-THEME_NAME-theme.md-fab[disabled], .md-button.md-THEME_NAME-theme.md-raised[disabled], .md-button.md-THEME_NAME-theme.md-fab[disabled] { background-color: '{{foreground-4}}'; } a.md-button.md-THEME_NAME-theme[disabled], .md-button.md-THEME_NAME-theme[disabled] { background-color: transparent; }md-card.md-THEME_NAME-theme { background-color: '{{background-color}}'; border-radius: 2px; } md-card.md-THEME_NAME-theme .md-card-image { border-radius: 2px 2px 0 0; }md-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-content.md-THEME_NAME-theme { background-color: '{{background-color}}'; }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-divider.md-THEME_NAME-theme { border-top-color: '{{foreground-4}}'; }md-icon.md-THEME_NAME-theme { color: '{{foreground-2}}'; } md-icon.md-THEME_NAME-theme.md-primary { color: '{{primary-color}}'; } md-icon.md-THEME_NAME-theme.md-accent { color: '{{accent-color}}'; } md-icon.md-THEME_NAME-theme.md-warn { color: '{{warn-color}}'; }md-input-container.md-THEME_NAME-theme .md-input { color: '{{foreground-1}}'; border-color: '{{foreground-4}}'; 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-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-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-sidenav.md-THEME_NAME-theme { background-color: '{{background-color}}'; }md-select.md-THEME_NAME-theme.ng-invalid.ng-dirty .md-select-label { color: '{{warn-500}}' !important; border-bottom-color: '{{warn-500}}' !important; }md-select.md-THEME_NAME-theme:not([disabled]):focus .md-select-label { border-bottom-color: '{{primary-color}}'; color: '{{ foreground-1 }}'; } md-select.md-THEME_NAME-theme:not([disabled]):focus .md-select-label.md-placeholder { color: '{{ foreground-1 }}'; }md-select.md-THEME_NAME-theme:not([disabled]):focus.md-accent .md-select-label { border-bottom-color: '{{accent-color}}'; }md-select.md-THEME_NAME-theme:not([disabled]):focus.md-warn .md-select-label { border-bottom-color: '{{warn-color}}'; }md-select.md-THEME_NAME-theme[disabled] .md-select-label { color: '{{foreground-3}}'; } md-select.md-THEME_NAME-theme[disabled] .md-select-label.md-placeholder { color: '{{foreground-3}}'; }md-select.md-THEME_NAME-theme .md-select-label { border-bottom-color: '{{foreground-4}}'; } md-select.md-THEME_NAME-theme .md-select-label.md-placeholder { color: '{{foreground-2}}'; }md-select-menu.md-THEME_NAME-theme md-optgroup { color: '{{foreground-2}}'; } md-select-menu.md-THEME_NAME-theme md-optgroup md-option { color: '{{foreground-1}}'; }md-select-menu.md-THEME_NAME-theme md-option[selected] { color: '{{primary-500}}'; } md-select-menu.md-THEME_NAME-theme md-option[selected]:focus { color: '{{primary-600}}'; } md-select-menu.md-THEME_NAME-theme md-option[selected].md-accent { color: '{{accent-500}}'; } md-select-menu.md-THEME_NAME-theme md-option[selected].md-accent:focus { color: '{{accent-600}}'; }md-select-menu.md-THEME_NAME-theme md-option:focus:not([selected]) { background: '{{background-200}}'; }md-slider.md-THEME_NAME-theme .md-track { background-color: '{{foreground-3}}'; }md-slider.md-THEME_NAME-theme .md-track-ticks { background-color: '{{foreground-4}}'; }md-slider.md-THEME_NAME-theme .md-focus-thumb { background-color: '{{foreground-2}}'; }md-slider.md-THEME_NAME-theme .md-focus-ring { border-color: '{{foreground-4}}'; }md-slider.md-THEME_NAME-theme .md-disabled-thumb { border-color: '{{background-color}}'; }md-slider.md-THEME_NAME-theme.md-min .md-thumb:after { background-color: '{{background-color}}'; }md-slider.md-THEME_NAME-theme .md-track.md-track-fill { background-color: '{{accent-color}}'; }md-slider.md-THEME_NAME-theme .md-thumb:after { border-color: '{{accent-color}}'; background-color: '{{accent-color}}'; }md-slider.md-THEME_NAME-theme .md-sign { background-color: '{{accent-color}}'; } md-slider.md-THEME_NAME-theme .md-sign:after { border-top-color: '{{accent-color}}'; }md-slider.md-THEME_NAME-theme .md-thumb-text { color: '{{accent-contrast}}'; }md-slider.md-THEME_NAME-theme.md-warn .md-track.md-track-fill { background-color: '{{warn-color}}'; }md-slider.md-THEME_NAME-theme.md-warn .md-thumb:after { border-color: '{{warn-color}}'; background-color: '{{warn-color}}'; }md-slider.md-THEME_NAME-theme.md-warn .md-sign { background-color: '{{warn-color}}'; } md-slider.md-THEME_NAME-theme.md-warn .md-sign:after { border-top-color: '{{warn-color}}'; }md-slider.md-THEME_NAME-theme.md-warn .md-thumb-text { color: '{{warn-contrast}}'; }md-slider.md-THEME_NAME-theme.md-primary .md-track.md-track-fill { background-color: '{{primary-color}}'; }md-slider.md-THEME_NAME-theme.md-primary .md-thumb:after { border-color: '{{primary-color}}'; background-color: '{{primary-color}}'; }md-slider.md-THEME_NAME-theme.md-primary .md-sign { background-color: '{{primary-color}}'; } md-slider.md-THEME_NAME-theme.md-primary .md-sign:after { border-top-color: '{{primary-color}}'; }md-slider.md-THEME_NAME-theme.md-primary .md-thumb-text { color: '{{primary-contrast}}'; }md-slider.md-THEME_NAME-theme[disabled] .md-thumb:after { border-color: '{{foreground-3}}'; }md-slider.md-THEME_NAME-theme[disabled]:not(.md-min) .md-thumb:after { background-color: '{{foreground-3}}'; }.md-subheader.md-THEME_NAME-theme { color: '{{ foreground-2-0.23 }}'; background-color: '{{background-color}}'; } .md-subheader.md-THEME_NAME-theme.md-primary { color: '{{primary-color}}'; } .md-subheader.md-THEME_NAME-theme.md-accent { color: '{{accent-color}}'; } .md-subheader.md-THEME_NAME-theme.md-warn { color: '{{warn-color}}'; }md-switch.md-THEME_NAME-theme .md-thumb { background-color: '{{background-50}}'; }md-switch.md-THEME_NAME-theme .md-bar { background-color: '{{background-500}}'; }md-switch.md-THEME_NAME-theme.md-checked .md-thumb { background-color: '{{accent-color}}'; }md-switch.md-THEME_NAME-theme.md-checked .md-bar { background-color: '{{accent-color-0.5}}'; }md-switch.md-THEME_NAME-theme.md-checked.md-focused .md-thumb:before { background-color: '{{accent-color-0.26}}'; }md-switch.md-THEME_NAME-theme.md-checked.md-primary .md-thumb { background-color: '{{primary-color}}'; }md-switch.md-THEME_NAME-theme.md-checked.md-primary .md-bar { background-color: '{{primary-color-0.5}}'; }md-switch.md-THEME_NAME-theme.md-checked.md-primary.md-focused .md-thumb:before { background-color: '{{primary-color-0.26}}'; }md-switch.md-THEME_NAME-theme.md-checked.md-warn .md-thumb { background-color: '{{warn-color}}'; }md-switch.md-THEME_NAME-theme.md-checked.md-warn .md-bar { background-color: '{{warn-color-0.5}}'; }md-switch.md-THEME_NAME-theme.md-checked.md-warn.md-focused .md-thumb:before { background-color: '{{warn-color-0.26}}'; }md-switch.md-THEME_NAME-theme[disabled] .md-thumb { background-color: '{{background-400}}'; }md-switch.md-THEME_NAME-theme[disabled] .md-bar { background-color: '{{foreground-4}}'; }md-tabs.md-THEME_NAME-theme md-tabs-wrapper { background-color: transparent; border-color: '{{foreground-4}}'; }md-tabs.md-THEME_NAME-theme .md-paginator md-icon { color: '{{primary-color}}'; }md-tabs.md-THEME_NAME-theme md-ink-bar { color: '{{accent-color}}'; background: '{{accent-color}}'; }md-tabs.md-THEME_NAME-theme .md-tab { color: '{{foreground-2}}'; } md-tabs.md-THEME_NAME-theme .md-tab[disabled] { color: '{{foreground-3}}'; } md-tabs.md-THEME_NAME-theme .md-tab.md-active, md-tabs.md-THEME_NAME-theme .md-tab.md-focused { color: '{{primary-color}}'; } md-tabs.md-THEME_NAME-theme .md-tab.md-focused { background: '{{primary-color-0.1}}'; } md-tabs.md-THEME_NAME-theme .md-tab .md-ripple-container { color: '{{accent-100}}'; }md-tabs.md-THEME_NAME-theme.md-accent md-tabs-wrapper { background-color: '{{accent-color}}'; }md-tabs.md-THEME_NAME-theme.md-accent md-tab-item:not([disabled]) { color: '{{accent-100}}'; } md-tabs.md-THEME_NAME-theme.md-accent md-tab-item:not([disabled]).md-active, md-tabs.md-THEME_NAME-theme.md-accent md-tab-item:not([disabled]).md-focused { color: '{{accent-contrast}}'; } md-tabs.md-THEME_NAME-theme.md-accent md-tab-item:not([disabled]).md-focused { background: '{{accent-contrast-0.1}}'; }md-tabs.md-THEME_NAME-theme.md-accent md-ink-bar { color: '{{primary-600-1}}'; background: '{{primary-600-1}}'; }md-tabs.md-THEME_NAME-theme.md-primary md-tabs-wrapper { background-color: '{{primary-color}}'; }md-tabs.md-THEME_NAME-theme.md-primary md-tab-item:not([disabled]) { color: '{{primary-100}}'; } md-tabs.md-THEME_NAME-theme.md-primary md-tab-item:not([disabled]).md-active, md-tabs.md-THEME_NAME-theme.md-primary md-tab-item:not([disabled]).md-focused { color: '{{primary-contrast}}'; } md-tabs.md-THEME_NAME-theme.md-primary md-tab-item:not([disabled]).md-focused { background: '{{primary-contrast-0.1}}'; }md-tabs.md-THEME_NAME-theme.md-warn md-tabs-wrapper { background-color: '{{warn-color}}'; }md-tabs.md-THEME_NAME-theme.md-warn md-tab-item:not([disabled]) { color: '{{warn-100}}'; } md-tabs.md-THEME_NAME-theme.md-warn md-tab-item:not([disabled]).md-active, md-tabs.md-THEME_NAME-theme.md-warn md-tab-item:not([disabled]).md-focused { color: '{{warn-contrast}}'; } md-tabs.md-THEME_NAME-theme.md-warn md-tab-item:not([disabled]).md-focused { background: '{{warn-contrast-0.1}}'; }md-toolbar > md-tabs.md-THEME_NAME-theme md-tabs-wrapper { background-color: '{{primary-color}}'; }md-toolbar > md-tabs.md-THEME_NAME-theme md-tab-item:not([disabled]) { color: '{{primary-100}}'; } md-toolbar > md-tabs.md-THEME_NAME-theme md-tab-item:not([disabled]).md-active, md-toolbar > md-tabs.md-THEME_NAME-theme md-tab-item:not([disabled]).md-focused { color: '{{primary-contrast}}'; } md-toolbar > md-tabs.md-THEME_NAME-theme md-tab-item:not([disabled]).md-focused { background: '{{primary-contrast-0.1}}'; }md-toolbar.md-accent > md-tabs.md-THEME_NAME-theme md-tabs-wrapper { background-color: '{{accent-color}}'; }md-toolbar.md-accent > md-tabs.md-THEME_NAME-theme md-tab-item:not([disabled]) { color: '{{accent-100}}'; } md-toolbar.md-accent > md-tabs.md-THEME_NAME-theme md-tab-item:not([disabled]).md-active, md-toolbar.md-accent > md-tabs.md-THEME_NAME-theme md-tab-item:not([disabled]).md-focused { color: '{{accent-contrast}}'; } md-toolbar.md-accent > md-tabs.md-THEME_NAME-theme md-tab-item:not([disabled]).md-focused { background: '{{accent-contrast-0.1}}'; }md-toolbar.md-accent > md-tabs.md-THEME_NAME-theme md-ink-bar { color: '{{primary-600-1}}'; background: '{{primary-600-1}}'; }md-toolbar.md-warn > md-tabs.md-THEME_NAME-theme md-tabs-wrapper { background-color: '{{warn-color}}'; }md-toolbar.md-warn > md-tabs.md-THEME_NAME-theme md-tab-item:not([disabled]) { color: '{{warn-100}}'; } md-toolbar.md-warn > md-tabs.md-THEME_NAME-theme md-tab-item:not([disabled]).md-active, md-toolbar.md-warn > md-tabs.md-THEME_NAME-theme md-tab-item:not([disabled]).md-focused { color: '{{warn-contrast}}'; } md-toolbar.md-warn > md-tabs.md-THEME_NAME-theme md-tab-item:not([disabled]).md-focused { background: '{{warn-contrast-0.1}}'; }md-toast.md-THEME_NAME-theme { background-color: #323232; color: '{{background-50}}'; } md-toast.md-THEME_NAME-theme .md-button { color: '{{background-50}}'; } md-toast.md-THEME_NAME-theme .md-button.md-highlight { color: '{{primary-A200}}'; } md-toast.md-THEME_NAME-theme .md-button.md-highlight.md-accent { color: '{{accent-A200}}'; } md-toast.md-THEME_NAME-theme .md-button.md-highlight.md-warn { color: '{{warn-A200}}'; }md-toolbar.md-THEME_NAME-theme { background-color: '{{primary-color}}'; color: '{{primary-contrast}}'; } md-toolbar.md-THEME_NAME-theme md-icon { color: '{{primary-contrast}}'; } md-toolbar.md-THEME_NAME-theme .md-button { color: '{{primary-contrast}}'; } md-toolbar.md-THEME_NAME-theme.md-accent { background-color: '{{accent-color}}'; color: '{{accent-contrast}}'; } md-toolbar.md-THEME_NAME-theme.md-warn { background-color: '{{warn-color}}'; color: '{{warn-contrast}}'; }md-tooltip.md-THEME_NAME-theme { color: '{{background-A100}}'; } md-tooltip.md-THEME_NAME-theme .md-background { background-color: '{{foreground-2}}'; }");
+})();
+
+
+})(window, window.angular); \ No newline at end of file