var app = angular.module('plunker', ['ngMaterial']);
app.controller('MainCtrl', function($scope, $mdDialog) {
$scope.openDialog = function(){
var elementWrapper = {};
elementWrapper.target = document.getElementById('someOtherElement');
$mdDialog.show({
controller: function($scope, $mdDialog){
// do something with dialog scope
},
template: '<md-dialog aria-label="My Dialog" style="position: absolute; top: 0; right: 0;">'+
'<md-dialog-content class="sticky-container">Blah Blah' +
'</md-dialog-content>' +
'<md-button ng-click=close()>Close</md-button>' +
'</md-dialog>',
targetEvent: elementWrapper
});
};
});
<!DOCTYPE html>
<html ng-app="plunker">
<head>
<meta charset="utf-8" />
<title>AngularJS material-sidenav Plunker</title>
<!-- Angular Material CSS now available via Google CDN; version 0.8 used here -->
<link rel="stylesheet" href="https://ajax.googleapis.com/ajax/libs/angular_material/0.8.3/angular-material.min.css">
<link rel="stylesheet" href="style.css" />
<!-- Angular Material Dependencies -->
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.15/angular.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.15/angular-animate.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.15/angular-aria.min.js"></script>
<!-- Angular Material Javascript now available via Google CDN; version 0.8 used here -->
<script src="https://ajax.googleapis.com/ajax/libs/angular_material/0.8.3/angular-material.min.js"></script>
<script src="app.js"></script>
</head>
<body ng-controller="MainCtrl">
<md-button ng-click="openDialog()">Open</md-button>
<div id="someOtherElement">Other Element</div>
</body>
</html>
/* Put your css in here */
#someOtherElement{
position: relative;
top: 0;
left: 350px;
background-color: red;
}
/*!
* Angular Material Design
* https://github.com/angular/material
* @license MIT
* v0.4.0
*/
(function(){
angular.module('ngMaterial', [ 'ng', 'ngAnimate', 'ngAria', 'material.core', 'material.services.attrBind', 'material.services.compiler', 'material.services.registry', 'material.decorators', 'material.services.aria', "material.components.bottomSheet","material.components.button","material.components.card","material.components.checkbox","material.components.circularProgress","material.components.content","material.components.dialog","material.components.divider","material.components.icon","material.components.linearProgress","material.components.list","material.components.radioButton","material.components.sidenav","material.components.slider","material.components.subheader","material.components.switch","material.components.tabs","material.components.textField","material.components.toast","material.components.toolbar","material.components.tooltip","material.components.whiteframe"]);
var Constant = {
KEY_CODE: {
ENTER: 13,
ESCAPE: 27,
SPACE: 32,
LEFT_ARROW : 37,
UP_ARROW : 38,
RIGHT_ARROW : 39,
DOWN_ARROW : 40
}
};
/**
* Angular Materials initialization function that validates environment
* requirements.
*/
angular.module('material.core', ['ngAria'] )
.run(function validateEnvironment() {
if (angular.isUndefined( window.Hammer )) {
throw new Error(
'$materialSwipe requires HammerJS to be preloaded.'
);
}
});
/*
* 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 iterator(items, reloop) {
var trueFn = function() { return true; };
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: next,
previous: previous,
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);
}
/*
* 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
* determine whether the next item is valid. If not valid, it will try to find the
* next item again.
* @param item
* @param {optional} validate
* @returns {*}
*/
function next(item, validate) {
validate = validate || trueFn;
if (contains(item)) {
var index = indexOf(item) + 1,
found = inRange(index) ? _items[ index ] : (reloop ? first() : null);
return validate(found) ? found : next(found, validate);
}
return null;
}
/*
* Find the previous item. If reloop is true and at the beginning of the list, it will
* go back to the last item. If given ,the `validate` callback will be used
* determine whether the previous item is valid. If not valid, it will try to find the
* previous item again.
* @param item
* @param {optional} validate
* @returns {*}
*/
function previous(item, validate) {
validate = validate || trueFn;
if (contains(item)) {
var index = indexOf(item) - 1,
found = inRange(index) ? _items[ index ] : (reloop ? last() : null);
return validate(found) ? found : previous(found, validate);
}
return null;
}
/*
* 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;
}
}
var SPECIAL_CHARS_REGEXP = /([\:\-\_]+(.))/g;
/* for nextUid() function below */
var uid = ['0','0','0'];
var Util = {
now: window.performance ? angular.bind(window.performance, window.performance.now) : Date.now,
/**
* Checks if the specified element has an ancestor (ancestor being parent, grandparent, etc)
* with the given attribute defined.
*
* Also pass in an optional `limit` (levels of ancestry to scan), default 4.
*/
ancestorHasAttribute: function ancestorHasAttribute(element, attrName, limit) {
limit = limit || 4;
var current = element;
while (limit-- && current.length) {
if (current[0].hasAttribute && current[0].hasAttribute(attrName)) {
return true;
}
current = current.parent();
}
return false;
},
/**
* Checks to see if the element or its parents are disabled.
* @param element DOM element to start scanning for `disabled` attribute
* @param limit Number of parent levels that should be scanned; defaults to 4
* @returns {*} Boolean
*/
isParentDisabled: function isParentDisabled(element, limit) {
return Util.ancestorHasAttribute(element, 'disabled', limit);
},
/**
* Checks if two elements have the same parent
*/
elementIsSibling: function elementIsSibling(element, otherElement) {
return element.parent().length &&
(element.parent()[0] === otherElement.parent()[0]);
},
/**
* Converts snake_case to camelCase.
* @param name Name to normalize
*/
camelCase: function camelCase(name) {
return name
.replace(SPECIAL_CHARS_REGEXP, function(_, separator, letter, offset) {
return offset ? letter.toUpperCase() : letter;
});
},
/**
* Selects 'n' words from a string
* for use in an HTML attribute
*/
stringFromTextBody: function stringFromTextBody(textBody, numWords) {
var string = textBody.trim();
if(string.split(/\s+/).length > numWords){
string = textBody.split(/\s+/).slice(1, (numWords + 1)).join(" ") + '...';
}
return string;
},
/**
* Publish the iterator facade to easily support iteration and accessors
* @see iterator.js
*/
iterator: iterator,
// 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. If `immediate` is passed, trigger the function on the
// leading edge, instead of the trailing.
debounce: function debounce(func, wait, immediate) {
var timeout;
return function debounced() {
var context = this, args = arguments;
clearTimeout(timeout);
timeout = setTimeout(function() {
timeout = null;
if (!immediate) func.apply(context, args);
}, wait);
if (immediate && !timeout) func.apply(context, args);
};
},
// 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 || recent - now > delay) {
func.apply(context, args);
recent = now;
}
};
},
/**
* Wraps an element with a tag
*
* @param el element to wrap
* @param tag tag to wrap it with
* @param [className] optional class to apply to the wrapper
* @returns new element
*
*/
wrap: function(el, tag, className) {
if(el.hasOwnProperty(0)) { el = el[0]; }
var wrapper = document.createElement(tag);
wrapper.className += className;
wrapper.appendChild(el.parentNode.replaceChild(wrapper, el));
return angular.element(wrapper);
},
/**
* nextUid, from angular.js.
* A consistent way of creating unique IDs in angular. The ID is a sequence of alpha numeric
* characters such as '012ABC'. The reason why we are not using simply a number counter is that
* the number string gets longer over time, and it can also overflow, where as the nextId
* will grow much slower, it is a string, and it will never overflow.
*
* @returns an unique alpha-numeric string
*/
nextUid: function() {
var index = uid.length;
var digit;
while(index) {
index--;
digit = uid[index].charCodeAt(0);
if (digit == 57 /*'9'*/) {
uid[index] = 'A';
return uid.join('');
}
if (digit == 90 /*'Z'*/) {
uid[index] = '0';
} else {
uid[index] = String.fromCharCode(digit + 1);
return uid.join('');
}
}
uid.unshift('0');
return uid.join('');
},
// 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;
}
}
};
/*
* 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;
};
/**
* @ngdoc module
* @name material.components.animate
* @description
*
* Ink and Popup Effects
*/
angular.module('material.animations', [])
.service('$materialEffects', [
'$rootElement',
'$$rAF',
'$sniffer',
'$q',
MaterialEffects
]);
/**
* @ngdoc service
* @name $materialEffects
* @module material.components.animate
*
* @description
* The `$materialEffects` service provides a simple API for various
* Material Design effects.
*
* @returns A `$materialEffects` object with the following properties:
* - `{function(element,styles,duration)}` `inkBar` - starts ink bar
* animation on specified DOM element
* - `{function(element,parentElement,clickElement)}` `popIn` - animated show of element overlayed on parent element
* - `{function(element,parentElement)}` `popOut` - animated close of popup overlay
*
*/
function MaterialEffects($rootElement, $$rAF, $sniffer, $q) {
var webkit = /webkit/i.test($sniffer.vendorPrefix);
function vendorProperty(name) {
return webkit ?
('webkit' + name.charAt(0).toUpperCase() + name.substring(1)) :
name;
}
var self;
// Publish API for effects...
return self = {
popIn: popIn,
/* Constants */
TRANSITIONEND_EVENT: 'transitionend' + (webkit ? ' webkitTransitionEnd' : ''),
ANIMATIONEND_EVENT: 'animationend' + (webkit ? ' webkitAnimationEnd' : ''),
TRANSFORM: vendorProperty('transform'),
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')
};
// **********************************************************
// API Methods
// **********************************************************
function popIn(element, parentElement, clickElement) {
var deferred = $q.defer();
parentElement.append(element);
var startPos;
if (clickElement) {
var clickRect = clickElement[0].getBoundingClientRect();
startPos = translateString(
clickRect.left - element[0].offsetWidth,
clickRect.top - element[0].offsetHeight,
0
) + ' scale(0.2)';
} else {
startPos = 'translate3d(0,100%,0) scale(0.5)';
}
element
.css(self.TRANSFORM, startPos)
.css('opacity', 0);
$$rAF(function() {
$$rAF(function() {
element
.addClass('active')
.css(self.TRANSFORM, '')
.css('opacity', '')
.on(self.TRANSITIONEND_EVENT, finished);
});
});
function finished(ev) {
//Make sure this transitionend didn't bubble up from a child
if (ev.target === element[0]) {
element.off(self.TRANSITIONEND_EVENT, finished);
deferred.resolve();
}
}
return deferred.promise;
}
// **********************************************************
// Utility Methods
// **********************************************************
function translateString(x, y, z) {
return 'translate3d(' + Math.floor(x) + 'px,' + Math.floor(y) + 'px,' + Math.floor(z) + 'px)';
}
}
(function() {
angular.module('material.animations')
/**
* 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 noink>
* <child detect-no>
* </child>
* </parent>
* </hljs>
*
* <hljs lang="js">
* myApp.directive('detectNo', function() {
* return {
* require: ['^?noink', ^?nobar'],
* link: function(scope, element, attr, ctrls) {
* var noinkCtrl = ctrls[0];
* var nobarCtrl = ctrls[1];
* if (noInkCtrl) {
* alert("the noink flag has been specified on an ancestor!");
* }
* if (nobarCtrl) {
* alert("the nobar flag has been specified on an ancestor!");
* }
* }
* };
* });
* </hljs>
*/
.directive({
noink: attrNoDirective(),
nobar: attrNoDirective(),
nostretch: attrNoDirective()
});
function attrNoDirective() {
return function() {
return {
controller: angular.noop
};
};
}
})();
angular.module('material.animations')
.directive('inkRipple', [
'$materialInkRipple',
InkRippleDirective
])
.factory('$materialInkRipple', [
'$window',
'$$rAF',
'$materialEffects',
'$timeout',
InkRippleService
]);
function InkRippleDirective($materialInkRipple) {
return function(scope, element, attr) {
if (attr.inkRipple == 'checkbox') {
$materialInkRipple.attachCheckboxBehavior(element);
} else {
$materialInkRipple.attachButtonBehavior(element);
}
};
}
function InkRippleService($window, $$rAF, $materialEffects, $timeout) {
// TODO fix this. doesn't support touch AND click devices (eg chrome pixel)
var hasTouch = !!('ontouchend' in document);
var POINTERDOWN_EVENT = hasTouch ? 'touchstart' : 'mousedown';
var POINTERUP_EVENT = hasTouch ? 'touchend touchcancel' : 'mouseup mouseleave';
return {
attachButtonBehavior: attachButtonBehavior,
attachCheckboxBehavior: attachCheckboxBehavior,
attach: attach
};
function attachButtonBehavior(element) {
return attach(element, {
mousedown: true,
center: false,
animationDuration: 350,
mousedownPauseTime: 175,
animationName: 'inkRippleButton',
animationTimingFunction: 'linear'
});
}
function attachCheckboxBehavior(element) {
return attach(element, {
mousedown: true,
center: true,
animationDuration: 300,
mousedownPauseTime: 180,
animationName: 'inkRippleCheckbox',
animationTimingFunction: 'linear'
});
}
function attach(element, options) {
// Parent element with noink attr? Abort.
if (element.controller('noink')) return angular.noop;
options = angular.extend({
mousedown: true,
hover: true,
focus: true,
center: false,
animationDuration: 300,
mousedownPauseTime: 150,
animationName: '',
animationTimingFunction: 'linear'
}, options || {});
var rippleContainer;
var node = element[0];
if (options.mousedown) {
listenPointerDown(true);
}
// Publish self-detach method if desired...
return function detach() {
listenPointerDown(false);
if (rippleContainer) {
rippleContainer.remove();
}
};
function listenPointerDown(shouldListen) {
element[shouldListen ? 'on' : 'off'](POINTERDOWN_EVENT, onPointerDown);
}
function rippleIsAllowed() {
return !Util.isParentDisabled(element);
}
function createRipple(left, top, positionsAreAbsolute) {
var rippleEl = angular.element('<div class="material-ripple">')
.css($materialEffects.ANIMATION_DURATION, options.animationDuration + 'ms')
.css($materialEffects.ANIMATION_NAME, options.animationName)
.css($materialEffects.ANIMATION_TIMING, options.animationTimingFunction)
.on($materialEffects.ANIMATIONEND_EVENT, function() {
rippleEl.remove();
});
if (!rippleContainer) {
rippleContainer = angular.element('<div class="material-ripple-container">');
element.append(rippleContainer);
}
rippleContainer.append(rippleEl);
var containerWidth = rippleContainer.prop('offsetWidth');
if (options.center) {
left = containerWidth / 2;
top = rippleContainer.prop('offsetHeight') / 2;
} else if (positionsAreAbsolute) {
var elementRect = node.getBoundingClientRect();
left -= elementRect.left;
top -= elementRect.top;
}
var css = {
'background-color': $window.getComputedStyle(rippleEl[0]).color ||
$window.getComputedStyle(node).color,
'border-radius': (containerWidth / 2) + 'px',
left: (left - containerWidth / 2) + 'px',
width: containerWidth + 'px',
top: (top - containerWidth / 2) + 'px',
height: containerWidth + 'px'
};
css[$materialEffects.ANIMATION_DURATION] = options.fadeoutDuration + 'ms';
rippleEl.css(css);
return rippleEl;
}
function onPointerDown(ev) {
if (!rippleIsAllowed()) return;
var rippleEl = createRippleFromEvent(ev);
var ripplePauseTimeout = $timeout(pauseRipple, options.mousedownPauseTime, false);
rippleEl.on('$destroy', cancelRipplePause);
// Stop listening to pointer down for now, until the user lifts their finger/mouse
listenPointerDown(false);
element.on(POINTERUP_EVENT, onPointerUp);
function onPointerUp() {
cancelRipplePause();
rippleEl.css($materialEffects.ANIMATION_PLAY_STATE, 'running');
element.off(POINTERUP_EVENT, onPointerUp);
listenPointerDown(true);
}
function pauseRipple() {
rippleEl.css($materialEffects.ANIMATION_PLAY_STATE, 'paused');
}
function cancelRipplePause() {
$timeout.cancel(ripplePauseTimeout);
}
function createRippleFromEvent(ev) {
ev = ev.touches ? ev.touches[0] : ev;
return createRipple(ev.pageX, ev.pageY, true);
}
}
}
}
/**
* @ngdoc module
* @name material.components.sticky
* @description
*
* Sticky effects for material
*/
angular.module('material.components.sticky', [
'material.components.content',
'material.decorators',
'material.animations'
])
.factory('$materialSticky', [
'$document',
'$materialEffects',
'$compile',
'$$rAF',
MaterialSticky
]);
/**
* @ngdoc factory
* @name $materialSticky
* @module material.components.sticky
*
* @description
* The `$materialSticky`service provides a mixin to make elements sticky.
*
* @returns A `$materialSticky` function that takes three arguments:
* - `scope`
* - `element`: The element that will be 'sticky'
* - `{optional}` `clone`: A clone of the element, that will be shown
* when the user starts scrolling past the original element.
* If not provided, it will use the result of `element.clone()`.
*/
function MaterialSticky($document, $materialEffects, $compile, $$rAF) {
var browserStickySupport = checkStickySupport();
/**
* Registers an element as sticky, used internally by directives to register themselves
*/
return function registerStickyElement(scope, element, stickyClone) {
var contentCtrl = element.controller('materialContent');
if (!contentCtrl) return;
if (browserStickySupport) {
element.css({
position: browserStickySupport,
top: 0,
'z-index': 2
});
} else {
var $$sticky = contentCtrl.$element.data('$$sticky');
if (!$$sticky) {
$$sticky = setupSticky(contentCtrl);
contentCtrl.$element.data('$$sticky', $$sticky);
}
var deregister = $$sticky.add(element, stickyClone || element.clone());
scope.$on('$destroy', deregister);
}
};
function setupSticky(contentCtrl) {
var contentEl = contentCtrl.$element;
// Refresh elements is very expensive, so we use the debounced
// version when possible.
var debouncedRefreshElements = $$rAF.debounce(refreshElements);
// setupAugmentedScrollEvents gives us `$scrollstart` and `$scroll`,
// more reliable than `scroll` on android.
setupAugmentedScrollEvents(contentEl);
contentEl.on('$scrollstart', debouncedRefreshElements);
contentEl.on('$scroll', onScroll);
var self;
return self = {
prev: null,
current: null, //the currently stickied item
next: null,
items: [],
add: add,
refreshElements: refreshElements
};
/***************
* Public
***************/
// Add an element and its sticky clone to this content's sticky collection
function add(element, stickyClone) {
stickyClone.addClass('material-sticky-clone');
var item = {
element: element,
clone: stickyClone
};
self.items.push(item);
contentEl.parent().prepend(item.clone);
debouncedRefreshElements();
return function remove() {
self.items.forEach(function(item, index) {
if (item.element[0] === element[0]) {
self.items.splice(index, 1);
item.clone.remove();
}
});
debouncedRefreshElements();
};
}
function refreshElements() {
var contentRect = contentEl[0].getBoundingClientRect();
// Sort our collection of elements by their current position in the DOM.
// We need to do this because our elements' order of being added may not
// be the same as their order of display.
self.items.forEach(refreshPosition);
self.items = self.items.sort(function(a, b) {
return a.top > b.top;
});
// Find which item in the list should be active,
// based upon the content's current scroll position
var item;
var currentScrollTop = contentEl.prop('scrollTop');
for (var i = self.items.length - 1; i >= 0; i--) {
if (currentScrollTop > self.items[i].top) {
item = self.items[i];
break;
}
}
setCurrentItem(item);
}
/***************
* Private
***************/
// Find the `top` of an item relative to the content element,
// and also the height.
function refreshPosition(item) {
// Find the top of an item by adding to the offsetHeight until we reach the
// content element.
var current = item.element[0];
item.top = 0;
item.left = 0;
while (current && current !== contentEl[0]) {
item.top += current.offsetTop;
item.left += current.offsetLeft;
current = current.offsetParent;
}
item.height = item.element.prop('offsetHeight');
item.clone.css('margin-left', item.left + 'px');
}
// As we scroll, push in and select the correct sticky element.
function onScroll() {
var scrollTop = contentEl.prop('scrollTop');
var isScrollingDown = scrollTop > (onScroll.prevScrollTop || 0);
onScroll.prevScrollTop = scrollTop;
// At the top?
if (scrollTop === 0) {
setCurrentItem(null);
// Going to next item?
} else if (isScrollingDown && self.next) {
if (self.next.top - scrollTop <= 0) {
// Sticky the next item if we've scrolled past its position.
setCurrentItem(self.next);
} else if (self.current) {
// Push the current item up when we're almost at the next item.
if (self.next.top - scrollTop <= self.next.height) {
translate(self.current, self.next.top - self.next.height - scrollTop);
} else {
translate(self.current, null);
}
}
// Scrolling up with a current sticky item?
} else if (!isScrollingDown && self.current) {
if (scrollTop < self.current.top) {
// Sticky the previous item if we've scrolled up past
// the original position of the currently stickied item.
setCurrentItem(self.prev);
}
// Scrolling up, and just bumping into the item above (just set to current)?
// If we have a next item bumping into the current item, translate
// the current item up from the top as it scrolls into view.
if (self.current && self.next) {
if (scrollTop >= self.next.top - self.current.height) {
translate(self.current, self.next.top - scrollTop - self.current.height);
} else {
translate(self.current, null);
}
}
}
}
function setCurrentItem(item) {
if (self.current === item) return;
// Deactivate currently active item
if (self.current) {
translate(self.current, null);
setStickyState(self.current, null);
}
// Activate new item if given
if (item) {
setStickyState(item, 'active');
}
self.current = item;
var index = self.items.indexOf(item);
// If index === -1, index + 1 = 0. It works out.
self.next = self.items[index + 1];
self.prev = self.items[index - 1];
setStickyState(self.next, 'next');
setStickyState(self.prev, 'prev');
}
function setStickyState(item, state) {
if (!item || item.state === state) return;
if (item.state) {
item.clone.attr('sticky-prev-state', item.state);
item.element.attr('sticky-prev-state', item.state);
}
item.clone.attr('sticky-state', state);
item.element.attr('sticky-state', state);
item.state = state;
}
function translate(item, amount) {
if (!item) return;
if (amount === null || amount === undefined) {
if (item.translateY) {
item.translateY = null;
item.clone.css($materialEffects.TRANSFORM, '');
}
} else {
item.translateY = amount;
item.clone.css(
$materialEffects.TRANSFORM,
'translate3d(' + item.left + 'px,' + amount + 'px,0)'
);
}
}
}
// Function to check for browser sticky support
function checkStickySupport($el) {
var stickyProp;
var testEl = angular.element('<div>');
$document[0].body.appendChild(testEl[0]);
var stickyProps = ['sticky', '-webkit-sticky'];
for (var i = 0; i < stickyProps.length; ++i) {
testEl.css({position: stickyProps[i], top: 0, 'z-index': 2});
if (testEl.css('position') == stickyProps[i]) {
stickyProp = stickyProps[i];
break;
}
}
testEl.remove();
return stickyProp;
}
// Android 4.4 don't accurately give scroll events.
// To fix this problem, we setup a fake scroll event. We say:
// > If a scroll or touchmove event has happened in the last DELAY milliseconds,
// then send a `$scroll` event every animationFrame.
// Additionally, we add $scrollstart and $scrollend events.
function setupAugmentedScrollEvents(element) {
var SCROLL_END_DELAY = 200;
var isScrolling;
var lastScrollTime;
element.on('scroll touchmove', function() {
if (!isScrolling) {
isScrolling = true;
$$rAF(loopScrollEvent);
element.triggerHandler('$scrollstart');
}
element.triggerHandler('$scroll');
lastScrollTime = +Util.now();
});
function loopScrollEvent() {
if (+Util.now() - lastScrollTime > SCROLL_END_DELAY) {
isScrolling = false;
element.triggerHandler('$scrollend');
} else {
element.triggerHandler('$scroll');
$$rAF(loopScrollEvent);
}
}
}
}
/**
* @ngdoc module
* @name material.components.bottomSheet
* @description
* BottomSheet
*/
angular.module('material.components.bottomSheet', [
'material.services.interimElement'
])
.directive('materialBottomSheet', [
MaterialBottomSheetDirective
])
.factory('$materialBottomSheet', [
'$$interimElement',
'$animate',
'$materialEffects',
'$timeout',
'$$rAF',
MaterialBottomSheet
]);
function MaterialBottomSheetDirective() {
return {
restrict: 'E'
};
}
/**
* @ngdoc service
* @name $materialBottomSheet
* @module material.components.bottomSheet
*
* @description
* Used to open a bottom sheet on the screen, `$materialBottomSheet` is a service
* created by `$$interimElement` and provides a simple promise-based, behavior API:
*
* - `$materialBottomSheet.show()`
* - `$materialBottomSheet.hide()`
* - `$materialBottomSheet.cancel()`
*
* #### Notes:
*
* Only one bottom sheet may ever be active at any time. If a new sheet is
* shown while a different one is active, the previous sheet will be automatically
* hidden.
* The bottom sheet's template must have an outer `<material-bottom-sheet>` element.
*
* @usage
* <hljs lang="html">
* <div ng-controller="MyController">
* <material-button ng-click="openBottomSheet()">
* Open a Bottom Sheet!
* </material-button>
* </div>
* </hljs>
* <hljs lang="js">
* var app = angular.module('app', ['ngMaterial']);
* app.controller('MyController', function($scope, $materialBottomSheet) {
* $scope.openBottomSheet = function() {
* $materialBottomSheet.show({
* template: '<material-bottom-sheet>Hello!</material-bottom-sheet>'
* });
* };
* });
* </hljs>
*/
/**
* @ngdoc method
* @name $materialBottomSheet#show
*
* @description
* Show a bottom sheet with the specified options.
*
* @paramType Options
* @param {string=} templateUrl The url of an html template file that will
* be used as the content of the bottom sheet. Restrictions: the template must
* have an outer `material-bottom-sheet` element.
* @param {string=} template Same as templateUrl, except this is an actual
* template string.
* @param {string=} controller The controller to associate with this bottom sheet.
* @param {string=} locals An object containing key/value pairs. The keys will
* be used as names of values to inject into the controller. For example,
* `locals: {three: 3}` would inject `three` into the controller with the value
* of 3.
* @param {element=} parent The element to append the bottomSheet to. Defaults to appending
* to the root element of the application.
* @param {DOMClickEvent=} targetEvent A click's event object. When passed in as an option,
* the location of the click will be used as the starting point for the opening animation
* of the the dialog.
* @param {object=} resolve Similar to locals, except it takes promises as values
* and the bottom sheet will not open until the promises resolve.
* @param {string=} controllerAs An alias to assign the controller to on the scope.
*
* @returns {Promise} Returns a promise that will be resolved or rejected when
* `$materialBottomSheet.hide()` or `$materialBottomSheet.cancel()` is called respectively.
*/
/**
* @ngdoc method
* @name $materialBottomSheet#hide
*
* @description
* Hide the existing bottom sheet and `resolve` the promise returned from
* `$materialBottomSheet.show()`.
*
* @param {*} arg An argument to resolve the promise with.
*
*/
/**
* @ngdoc method
* @name $materialBottomSheet#cancel
*
* @description
* Hide the existing bottom sheet and `reject` the promise returned from
* `$materialBottomSheet.show()`.
*
* @param {*} arg An argument to reject the promise with.
*
*/
function MaterialBottomSheet($$interimElement, $animate, $materialEffects, $timeout, $$rAF) {
var backdrop;
var $materialBottomSheet = $$interimElement({
targetEvent: null,
onShow: onShow,
onRemove: onRemove,
});
return $materialBottomSheet;
function onShow(scope, element, options) {
// Add a backdrop that will close on click
backdrop = angular.element('<material-backdrop class="opaque ng-enter">');
backdrop.on('click touchstart', function() {
$timeout($materialBottomSheet.cancel);
});
$animate.enter(backdrop, options.parent, null);
var bottomSheet = new BottomSheet(element);
options.bottomSheet = bottomSheet;
// Give up focus on calling item
options.targetEvent && angular.element(options.targetEvent.target).blur();
return $animate.enter(bottomSheet.element, options.parent);
}
function onRemove(scope, element, options) {
var bottomSheet = options.bottomSheet;
$animate.leave(backdrop);
return $animate.leave(bottomSheet.element).then(function() {
bottomSheet.cleanup();
// Restore focus
options.targetEvent && angular.element(options.targetEvent.target).focus();
});
}
/**
* BottomSheet class to apply bottom-sheet behavior to an element
*/
function BottomSheet(element) {
var MAX_OFFSET = 80; // amount past the bottom of the element that we can drag down, this is same as in _bottomSheet.scss
var WIGGLE_AMOUNT = 20; // point where it starts to get "harder" to drag
var CLOSING_VELOCITY = 10; // how fast we need to flick down to close the sheet
var startY, lastY, velocity, transitionDelay, startTarget;
// coercion incase $materialCompiler returns multiple elements
element = element.eq(0);
element.on('touchstart', onTouchStart);
element.on('touchmove', onTouchMove);
element.on('touchend', onTouchEnd);
return {
element: element,
cleanup: function cleanup() {
element.off('touchstart', onTouchStart);
element.off('touchmove', onTouchMove);
element.off('touchend', onTouchEnd);
}
};
function onTouchStart(e) {
e.preventDefault();
startTarget = e.target;
startY = getY(e);
// Disable transitions on transform so that it feels fast
transitionDelay = element.css($materialEffects.TRANSITION_DURATION);
element.css($materialEffects.TRANSITION_DURATION, '0s');
}
function onTouchEnd(e) {
// Re-enable the transitions on transforms
element.css($materialEffects.TRANSITION_DURATION, transitionDelay);
var currentY = getY(e);
// If we didn't scroll much, and we didn't change targets, assume its a click
if ( Math.abs(currentY - startY) < 5 && e.target == startTarget) {
angular.element(e.target).triggerHandler('click');
} else {
// If they went fast enough, trigger a close.
if (velocity > CLOSING_VELOCITY) {
$timeout($materialBottomSheet.cancel);
// Otherwise, untransform so that we go back to our normal position
} else {
setTransformY(undefined);
}
}
}
function onTouchMove(e) {
var currentY = getY(e);
var delta = currentY - startY;
velocity = currentY - lastY;
lastY = currentY;
// Do some conversion on delta to get a friction-like effect
delta = adjustedDelta(delta);
setTransformY(delta + MAX_OFFSET);
}
/**
* Helper function to find the Y aspect of various touch events.
**/
function getY(e) {
var touch = e.touches && e.touches.length ? e.touches[0] : e.changedTouches[0];
return touch.clientY;
}
/**
* Transform the element along the y-axis
**/
function setTransformY(amt) {
if (amt === null || amt === undefined) {
element.css($materialEffects.TRANSFORM, '');
} else {
element.css($materialEffects.TRANSFORM, 'translate3d(0, ' + amt + 'px, 0)');
}
}
// Returns a new value for delta that will never exceed MAX_OFFSET_AMOUNT
// Will get harder to exceed it as you get closer to it
function adjustedDelta(delta) {
if ( delta < 0 && delta < -MAX_OFFSET + WIGGLE_AMOUNT) {
delta = -delta;
var base = MAX_OFFSET - WIGGLE_AMOUNT;
delta = Math.max(-MAX_OFFSET, -Math.min(MAX_OFFSET - 5, base + ( WIGGLE_AMOUNT * (delta - base)) / MAX_OFFSET) - delta / 50);
}
return delta;
}
}
}
/**
* @ngdoc module
* @name material.components.buttons
* @description
*
* Button
*/
angular.module('material.components.button', [
'material.animations',
'material.services.aria'
])
.directive('materialButton', [
'ngHrefDirective',
'$materialInkRipple',
'$materialAria',
MaterialButtonDirective
]);
/**
* @ngdoc directive
* @name materialButton
* @order 0
*
* @restrict E
*
* @description
* `<material-button>` is a button directive with optional ink ripples (default enabled).
*
* @param {boolean=} noink If present, disable ripple ink effects.
* @param {boolean=} disabled If present, disable tab selection.
* @param {string=} type Optional attribute to specific button types (useful for forms); such as 'submit', etc.
* @param {string=} ng-href Optional attribute to support both ARIA and link navigation
* @param {string=} href Optional attribute to support both ARIA and link navigation
* @param {string=} ariaLabel Publish the button label used by screen-readers for accessibility. Defaults to the button's text.
*
* @usage
* <hljs lang="html">
* <material-button>Button</material-button>
* <br/>
* <material-button noink class="material-button-colored">
* Button (noInk)
* </material-button>
* <br/>
* <material-button disabled class="material-button-colored">
* Colored (disabled)
* </material-button>
* </hljs>
*/
function MaterialButtonDirective(ngHrefDirectives, $materialInkRipple, $materialAria ) {
var ngHrefDirective = ngHrefDirectives[0];
return {
restrict: 'E',
compile: function(element, attr) {
var innerElement;
var attributesToCopy;
// Add an inner anchor if the element has a `href` or `ngHref` attribute,
// so this element can be clicked like a normal `<a>`.
if (attr.ngHref || attr.href) {
innerElement = angular.element('<a>');
attributesToCopy = ['ng-href', 'href', 'rel', 'target'];
// Otherwise, just add an inner button element (for form submission etc)
} else {
innerElement = angular.element('<button>');
attributesToCopy = ['type', 'disabled', 'ng-disabled', 'form'];
}
angular.forEach(attributesToCopy, function(name) {
var camelCaseName = Util.camelCase(name);
if (attr.hasOwnProperty(camelCaseName)) {
innerElement.attr(name, attr[camelCaseName]);
}
});
innerElement
.addClass('material-button-inner')
.append(element.contents())
// Since we're always passing focus to the inner element,
// add a focus class to the outer element so we can still style
// it with focus.
.on('focus', function() {
element.addClass('focus');
})
.on('blur', function() {
element.removeClass('focus');
});
element.
append(innerElement)
.attr('tabIndex', -1)
//Always pass focus to innerElement
.on('focus', function() {
innerElement.focus();
});
return function postLink(scope, element, attr) {
$materialAria.expect(element, 'aria-label', element.text());
$materialInkRipple.attachButtonBehavior(element);
};
}
};
}
/**
* @ngdoc module
* @name material.components.card
*
* @description
* Card components.
*/
angular.module('material.components.card', [
])
.directive('materialCard', [
materialCardDirective
]);
/**
* @ngdoc directive
* @name materialCard
* @module material.components.card
*
* @restrict E
*
* @description
* The `<material-card>` directive is a container element used within `<material-content>` containers.
*
* Cards have constant width and variable heights; where the maximum height is limited to what can
* fit within a single view on a platform, but it can temporarily expand as needed
*
* @usage
* <hljs lang="html">
* <material-card>
* <img src="/img/washedout.png" class="material-card-image">
* <h2>Paracosm</h2>
* <p>
* The titles of Washed Out's breakthrough song and the first single from Paracosm share the * two most important words in Ernest Greene's musical language: feel it. It's a simple request, as well...
* </p>
* </material-card>
* </hljs>
*
*/
function materialCardDirective() {
return {
restrict: 'E',
link: function($scope, $element, $attr) {
}
};
}
/**
* @ngdoc module
* @name material.components.checkbox
* @description Checkbox module!
*/
angular.module('material.components.checkbox', [
'material.animations',
'material.services.aria'
])
.directive('materialCheckbox', [
'inputDirective',
'$materialInkRipple',
'$materialAria',
MaterialCheckboxDirective
]);
/**
* @ngdoc directive
* @name materialCheckbox
* @module material.components.checkbox
* @restrict E
*
* @description
* The checkbox directive is used like the normal [angular checkbox](https://docs.angularjs.org/api/ng/input/input%5Bcheckbox%5D).
*
* @param {string} ngModel Assignable angular expression to data-bind to.
* @param {string=} name Property name of the form under which the control is published.
* @param {expression=} ngTrueValue The value to which the expression should be set when selected.
* @param {expression=} ngFalseValue The value to which the expression should be set when not selected.
* @param {string=} ngChange Angular expression to be executed when input changes due to user interaction with the input element.
* @param {boolean=} noink Use of attribute indicates use of ripple ink effects
* @param {boolean=} disabled Use of attribute indicates the switch is disabled: no ink effects and not selectable
* @param {string=} ariaLabel Publish the button label used by screen-readers for accessibility. Defaults to the checkbox's text.
*
* @usage
* <hljs lang="html">
* <material-checkbox ng-model="isChecked" aria-label="Finished?">
* Finished ?
* </material-checkbox>
*
* <material-checkbox noink ng-model="hasInk" aria-label="No Ink Effects">
* No Ink Effects
* </material-checkbox>
*
* <material-checkbox disabled ng-model="isDisabled" aria-label="Disabled">
* Disabled
* </material-checkbox>
*
* </hljs>
*
*/
function MaterialCheckboxDirective(inputDirectives, $materialInkRipple, $materialAria) {
var inputDirective = inputDirectives[0];
var CHECKED_CSS = 'material-checked';
return {
restrict: 'E',
transclude: true,
require: '?ngModel',
template:
'<div class="material-container" ink-ripple="checkbox">' +
'<div class="material-icon"></div>' +
'</div>' +
'<div ng-transclude class="material-label"></div>',
compile: compile
};
// **********************************************************
// Private Methods
// **********************************************************
function compile (tElement, tAttrs) {
tAttrs.type = 'checkbox';
tAttrs.tabIndex = 0;
tElement.attr('role', tAttrs.type);
$materialAria.expect(tElement, 'aria-label', tElement.text());
return function postLink(scope, element, attr, ngModelCtrl) {
var checked = false;
// Create a mock ngModel if the user doesn't provide one
ngModelCtrl = ngModelCtrl || {
$setViewValue: function(value) {
this.$viewValue = value;
},
$parsers: [],
$formatters: []
};
// Reuse the original input[type=checkbox] directive from Angular core.
// This is a bit hacky as we need our own event listener and own render
// function.
inputDirective.link.pre(scope, {
on: angular.noop,
0: {}
}, attr, [ngModelCtrl]);
element.on('click', listener);
element.on('keypress', keypressHandler);
ngModelCtrl.$render = render;
function keypressHandler(ev) {
if(ev.which === Constant.KEY_CODE.SPACE) {
ev.preventDefault();
listener(ev);
}
}
function listener(ev) {
if (element[0].hasAttribute('disabled')) return;
scope.$apply(function() {
checked = !checked;
ngModelCtrl.$setViewValue(checked, ev && ev.type);
ngModelCtrl.$render();
});
}
function render() {
checked = ngModelCtrl.$viewValue;
// element.attr('aria-checked', checked);
if(checked) {
element.addClass(CHECKED_CSS);
} else {
element.removeClass(CHECKED_CSS);
}
}
}
}
}
/**
* @ngdoc module
* @name material.components.content
*
* @description
* Scrollable content
*/
angular.module('material.components.content', [
'material.services.registry'
])
.directive('materialContent', [
materialContentDirective
]);
/**
* @ngdoc directive
* @name materialContent
* @module material.components.content
*
* @restrict E
*
* @description
* The `<material-content>` directive is a container element useful for scrollable content
*
* @usage
* <hljs lang="html">
* <material-content class="material-content-padding">
* Lorem ipsum dolor sit amet, ne quod novum mei.
* </material-content>
* </hljs>
*
*/
function materialContentDirective() {
return {
restrict: 'E',
controller: ['$scope', '$element', ContentController],
link: function($scope, $element, $attr) {
$scope.$broadcast('$materialContentLoaded', $element);
}
};
function ContentController($scope, $element) {
this.$scope = $scope;
this.$element = $element;
}
}
/**
* @ngdoc module
* @name material.components.dialog
*/
angular.module('material.components.dialog', [
'material.animations',
'material.services.compiler',
'material.services.aria',
'material.services.interimElement',
])
.directive('materialDialog', [
'$$rAF',
MaterialDialogDirective
])
.factory('$materialDialog', [
'$timeout',
'$rootElement',
'$materialEffects',
'$animate',
'$materialAria',
'$$interimElement',
MaterialDialogService
]);
function MaterialDialogDirective($$rAF) {
return {
restrict: 'E',
link: function(scope, element, attr) {
$$rAF(function() {
var content = element[0].querySelector('.dialog-content');
if (content && content.scrollHeight > content.clientHeight) {
element.addClass('dialog-content-overflow');
}
});
}
};
}
/**
* @ngdoc service
* @name $materialDialog
* @module material.components.dialog
*
* @description
*
* Used to open a dialog over top of the app, `$materialDialog` is a service created
* by `$$interimElement` and provides a simple promise-based, behavior API:
*
* - `$materialDialog.show()`
* - `$materialDialog.hide()`
* - `$materialDialog.cancel()`
*
* #### Notes:
*
* The dialog is always given an isolate scope.
*
* The dialog's template must have an outer `<material-dialog>` element.
* Inside, use an element with class `dialog-content` for the dialog's content, and use
* an element with class `dialog-actions` for the dialog's actions.
*
* When opened, the `dialog-actions` area will attempt to focus the first button found with
* class `dialog-close`. If no button with `dialog-close` class is found, it will focus the
* last button in the `dialog-actions` area.
*
* @usage
* <hljs lang="html">
* <div ng-controller="MyController">
* <material-button ng-click="openDialog($event)">
* Open a Dialog from this button!
* </material-button>
* </div>
* </hljs>
* <hljs lang="js">
* var app = angular.module('app', ['ngMaterial']);
* app.controller('MyController', function($scope, $materialDialog) {
* $scope.openDialog = function($event) {
* $materialDialog.show({
* template: '<material-dialog>Hello!</material-dialog>',
* targetEvent: $event
* });
* };
* });
* </hljs>
*
*/
/**
*
* @ngdoc method
* @name $materialDialog#show
*
* @description
* Show a dialog with the specified options
*
* @paramType Options
* @param {string=} templateUrl The url of a template that will be used as the content
* of the dialog.
* @param {string=} template Same as templateUrl, except this is an actual template string.
* @param {DOMClickEvent=} targetEvent A click's event object. When passed in as an option,
* the location of the click will be used as the starting point for the opening animation
* of the the dialog.
* @param {boolean=} hasBackdrop Whether there should be an opaque backdrop behind the dialog.
* Default true.
* @param {boolean=} clickOutsideToClose Whether the user can click outside the dialog to
* close it. Default true.
* @param {boolean=} escapeToClose Whether the user can press escape to close the dialog.
* Default true.
* @param {string=} controller The controller to associate with the dialog.
* @param {object=} locals An object containing key/value pairs. The keys will be used as names
* of values to inject into the controller. For example, `locals: {three: 3}` would inject
* `three` into the controller, with the value 3.
* @param {object=} resolve Similar to locals, except it takes promises as values, and the
* toast will not open until all of the promises resolve.
* @param {string=} controllerAs An alias to assign the controller to on the scope.
* @param {element=} parent The element to append the dialog to. Defaults to appending
* to the root element of the application.
*
* @returns {Promise} Returns a promise that will be resolved or rejected when
* `$materialDialog.hide()` or `$materialDialog.cancel()` is called respectively.
*/
/**
* @ngdoc method
* @name $materialDialog#hide
*
* @description
* Hide an existing dialog and `resolve` the promise returned from `$materialDialog.show()`.
*
* @param {*} arg An argument to resolve the promise with.
*
*/
/**
* @ngdoc method
* @name $materialDialog#cancel
*
* @description
* Hide an existing dialog and `reject` the promise returned from `$materialDialog.show()`.
*
* @param {*} arg An argument to reject the promise with.
*
*/
function MaterialDialogService($timeout, $rootElement, $materialEffects, $animate, $materialAria, $$interimElement) {
var $dialogService;
return $dialogService = $$interimElement({
hasBackdrop: true,
isolateScope: true,
onShow: onShow,
onRemove: onRemove,
clickOutsideToClose: true,
escapeToClose: true,
targetEvent: null,
transformTemplate: function(template) {
return '<div class="material-dialog-container">' + template + '</div>';
}
});
function onShow(scope, element, options) {
// Incase the user provides a raw dom element, always wrap it in jqLite
options.parent = angular.element(options.parent);
options.popInTarget = angular.element((options.targetEvent || {}).target);
var closeButton = findCloseButton();
configureAria(element.find('material-dialog'));
if (options.hasBackdrop) {
var backdrop = angular.element('<material-backdrop class="opaque ng-enter">');
$animate.enter(backdrop, options.parent, null);
options.backdrop = backdrop;
}
return $materialEffects.popIn(
element,
options.parent,
options.popInTarget.length && options.popInTarget
)
.then(function() {
if (options.escapeToClose) {
options.rootElementKeyupCallback = function(e) {
if (e.keyCode === Constant.KEY_CODE.ESCAPE) {
$timeout($dialogService.cancel);
}
};
$rootElement.on('keyup', options.rootElementKeyupCallback);
}
if (options.clickOutsideToClose) {
options.dialogClickOutsideCallback = function(e) {
// Only close if we click the flex container outside the backdrop
if (e.target === element[0]) {
$timeout($dialogService.cancel);
}
};
element.on('click', options.dialogClickOutsideCallback);
}
closeButton.focus();
});
function findCloseButton() {
//If no element with class dialog-close, try to find the last
//button child in dialog-actions and assume it is a close button
var closeButton = element[0].querySelector('.dialog-close');
if (!closeButton) {
var actionButtons = element[0].querySelectorAll('.dialog-actions button');
closeButton = actionButtons[ actionButtons.length - 1 ];
}
return angular.element(closeButton);
}
}
function onRemove(scope, element, options) {
if (options.backdrop) {
$animate.leave(options.backdrop);
element.data('backdrop', undefined);
}
if (options.escapeToClose) {
$rootElement.off('keyup', options.rootElementKeyupCallback);
}
if (options.clickOutsideToClose) {
element.off('click', options.dialogClickOutsideCallback);
}
return $animate.leave(element).then(function() {
element.remove();
options.popInTarget && options.popInTarget.focus();
});
}
/**
* Inject ARIA-specific attributes appropriate for Dialogs
*/
function configureAria(element) {
element.attr({
'role': 'dialog'
});
var dialogContent = element.find('.dialog-content');
if (dialogContent.length === 0){
dialogContent = element;
}
var defaultText = Util.stringFromTextBody(dialogContent.text(), 3);
$materialAria.expect(element, 'aria-label', defaultText);
}
}
/**
* @ngdoc module
* @name material.components.textField
* @description
* Form
*/
angular.module('material.components.textField', [])
.directive('materialInputGroup', [
materialInputGroupDirective
])
.directive('materialInput', [
materialInputDirective
]);
/**
* @ngdoc directive
* @name materialInputGroup
* @module material.components.textField
* @restrict E
* @description
* Use the `<material-input-group>` directive as the grouping parent of a `<material-input>` element.
*
* @usage
* <hljs lang="html">
* <material-input-group ng-disabled="isDisabled">
* <label for="{{fid}}">{{someLabel}}</label>
* <material-input id="{{fid}}" type="text" ng-model="someText"></material-input>
* </material-input-group>
* </hljs>
*/
function materialInputGroupDirective() {
return {
restrict: 'CE',
controller: ['$element', function($element) {
this.setFocused = function(isFocused) {
$element.toggleClass('material-input-focused', !!isFocused);
};
this.setHasValue = function(hasValue) {
$element.toggleClass('material-input-has-value', !!hasValue);
};
}]
};
}
/**
* @ngdoc directive
* @name materialInput
* @module material.components.textField
*
* @restrict E
*
* @description
* Use the `<material-input>` directive as elements within a `<material-input-group>` container
*
* @usage
* <hljs lang="html">
* <material-input-group ng-disabled="user.isLocked">
* <label for="i1">FirstName</label>
* <material-input id="i1" ng-model="user.firstName"></material-input>
* </material-input-group>
* </hljs>
*/
function materialInputDirective() {
return {
restrict: 'E',
replace: true,
template: '<input >',
require: ['^?materialInputGroup', '?ngModel'],
link: function(scope, element, attr, ctrls) {
var inputGroupCtrl = ctrls[0];
var ngModelCtrl = ctrls[1];
if (!inputGroupCtrl) {
return;
}
// scan for disabled and transpose the `type` value to the <input> element
var isDisabled = Util.isParentDisabled(element);
element.attr('tabindex', isDisabled ? -1 : 0 );
element.attr('type', attr.type || element.parent().attr('type') || "text" );
// When the input value changes, check if it "has" a value, and
// set the appropriate class on the input group
if (ngModelCtrl) {
//Add a $formatter so we don't use up the render function
ngModelCtrl.$formatters.push(function(value) {
inputGroupCtrl.setHasValue(!!value);
return value;
});
}
element.on('input', function() {
inputGroupCtrl.setHasValue(!!element.val());
});
// When the input focuses, add the focused class to the group
element.on('focus', function(e) {
inputGroupCtrl.setFocused(true);
});
// When the input blurs, remove the focused class from the group
element.on('blur', function(e) {
inputGroupCtrl.setFocused(false);
});
scope.$on('$destroy', function() {
inputGroupCtrl.setFocused(false);
inputGroupCtrl.setHasValue(false);
});
}
};
}
/**
* @ngdoc module
* @name material.components.icon
* @description
* Icon
*/
angular.module('material.components.icon', [])
.directive('materialIcon', [
materialIconDirective
]);
/**
* @ngdoc directive
* @name materialIcon
* @module material.components.icon
*
* @restrict E
*
* @description
* The `<material-icon>` directive is an element useful for SVG icons
*
* @usage
* <hljs lang="html">
* <material-icon icon="/img/icons/ic_access_time_24px.svg">
* </material-icon>
* </hljs>
*
*/
function materialIconDirective() {
return {
restrict: 'E',
template: '<object class="material-icon"></object>',
compile: function(element, attr) {
var object = angular.element(element[0].children[0]);
if(angular.isDefined(attr.icon)) {
object.attr('data', attr.icon);
}
}
};
}
/**
* @ngdoc module
* @name material.components.list
* @description
* List module
*/
angular.module('material.components.list', [])
.directive('materialList', [
materialListDirective
])
.directive('materialItem', [
materialItemDirective
]);
/**
* @ngdoc directive
* @name materialList
* @module material.components.list
*
* @restrict E
*
* @description
* The `<material-list>` directive is a list container for 1..n `<material-item>` tags.
*
* @usage
* <hljs lang="html">
* <material-list>
* <material-item ng-repeat="item in todos">
* <div class="material-tile-left">
* <img ng-src="{{item.face}}" class="face" alt="{{item.who}}">
* </div>
* <div class="material-tile-content">
* <h3>{{item.what}}</h3>
* <h4>{{item.who}}</h4>
* <p>
* {{item.notes}}
* </p>
* </div>
*
* </material-item>
* </material-list>
* </hljs>
*
*/
function materialListDirective() {
return {
restrict: 'E',
link: function($scope, $element, $attr) {
$element.attr({
'role' : 'list'
});
}
};
}
/**
* @ngdoc directive
* @name materialItem
* @module material.components.list
*
* @restrict E
*
* @description
* The `<material-item>` directive is a container intended for row items in a `<material-list>` container.
*
* @usage
* <hljs lang="html">
* <material-list>
* <material-item>
* Item content in list
* </material-item>
* </material-list>
* </hljs>
*
*/
function materialItemDirective() {
return {
restrict: 'E',
link: function($scope, $element, $attr) {
$element.attr({
'role' : 'listitem'
});
}
};
}
/**
* @ngdoc module
* @name material.components.radioButton
* @description radioButton module!
*/
angular.module('material.components.radioButton', [
'material.animations',
'material.services.aria'
])
.directive('materialRadioGroup', [
materialRadioGroupDirective
])
.directive('materialRadioButton', [
'$materialAria',
materialRadioButtonDirective
]);
/**
* @ngdoc directive
* @module material.components.radioButton
* @name materialRadioGroup
*
* @order 0
* @restrict E
*
* @description
* The `<material-radio-group>` directive identifies a grouping
* container for the 1..n grouped material radio buttons; specified using nested
* `<material-radio-button>` tags.
*
* @param {string} ngModel Assignable angular expression to data-bind to.
* @param {boolean=} noink Use of attribute indicates flag to disable ink ripple effects.
*
* @usage
* <hljs lang="html">
* <material-radio-group ng-model="selected">
*
* <material-radio-button
* ng-repeat="d in colorOptions"
* ng-value="d.value" aria-label="{{ d.label }}">
*
* {{ d.label }}
*
* </material-radio-button>
*
* </material-radio-group>
* </hljs>
*
*/
function materialRadioGroupDirective() {
RadioGroupController.prototype = createRadioGroupControllerProto();
return {
restrict: 'E',
controller: ['$element', RadioGroupController],
require: ['materialRadioGroup', '?ngModel'],
link: link
};
function link(scope, element, attr, ctrls) {
var rgCtrl = ctrls[0],
ngModelCtrl = ctrls[1] || {
$setViewValue: angular.noop
};
function keydownListener(ev) {
if (ev.which === Constant.KEY_CODE.LEFT_ARROW || ev.which === Constant.KEY_CODE.UP_ARROW) {
ev.preventDefault();
rgCtrl.selectPrevious();
}
else if (ev.which === Constant.KEY_CODE.RIGHT_ARROW || ev.which === Constant.KEY_CODE.DOWN_ARROW) {
ev.preventDefault();
rgCtrl.selectNext();
}
}
rgCtrl.init(ngModelCtrl);
element.attr({
'role': 'radiogroup',
'tabIndex': '0'
})
.on('keydown', keydownListener);
}
function RadioGroupController($element) {
this._radioButtonRenderFns = [];
this.$element = $element;
}
function createRadioGroupControllerProto() {
return {
init: function(ngModelCtrl) {
this._ngModelCtrl = ngModelCtrl;
this._ngModelCtrl.$render = angular.bind(this, this.render);
},
add: function(rbRender) {
this._radioButtonRenderFns.push(rbRender);
},
remove: function(rbRender) {
var index = this._radioButtonRenderFns.indexOf(rbRender);
if (index !== -1) {
this._radioButtonRenderFns.splice(index, 1);
}
},
render: function() {
this._radioButtonRenderFns.forEach(function(rbRender) {
rbRender();
});
},
setViewValue: function(value, eventType) {
this._ngModelCtrl.$setViewValue(value, eventType);
// update the other radio buttons as well
this.render();
},
getViewValue: function() {
return this._ngModelCtrl.$viewValue;
},
selectNext: function() {
return changeSelectedButton(this.$element, 1);
},
selectPrevious : function() {
return changeSelectedButton(this.$element, -1);
},
setActiveDescendant: function (radioId) {
this.$element.attr('aria-activedescendant', radioId);
}
};
}
/**
* Change the radio group's selected button by a given increment.
* If no button is selected, select the first button.
*/
function changeSelectedButton(parent, increment) {
// Coerce all child radio buttons into an array, then wrap then in an iterator
var buttons = Util.iterator(
Array.prototype.slice.call(parent[0].querySelectorAll('material-radio-button')),
true
);
if (buttons.count()) {
var selected = parent[0].querySelector('material-radio-button.material-checked');
var target = buttons[increment < 0 ? 'previous' : 'next'](selected) ||
buttons.first();
// Activate radioButton's click listener (triggerHandler won't create a real click event)
angular.element(target).triggerHandler('click');
}
}
}
/**
* @ngdoc directive
* @module material.components.radioButton
* @name materialRadioButton
*
* @order 1
* @restrict E
*
* @description
* The `<material-radio-button>`directive is the child directive required to be used within `<material-radioo-group>` elements.
*
* While similar to the `<input type="radio" ng-model="" value="">` directive,
* the `<material-radio-button>` directive provides material ink effects, ARIA support, and
* supports use within named radio groups.
*
* @param {string} ngModel Assignable angular expression to data-bind to.
* @param {string=} ngChange Angular expression to be executed when input changes due to user
* interaction with the input element.
* @param {string} ngValue Angular expression which sets the value to which the expression should
* be set when selected.*
* @param {string} value The value to which the expression should be set when selected.
* @param {string=} name Property name of the form under which the control is published.
* @param {string=} ariaLabel Publish the button label used by screen-readers for accessibility. Defaults to the radio button's text.
*
* @usage
* <hljs lang="html">
*
* <material-radio-button value="1" aria-label="Label 1">
* Label 1
* </material-radio-button>
*
* <material-radio-button ng-model="color" ng-value="specialValue" aria-label="Green">
* Green
* </material-radio-button>
*
* </hljs>
*
*/
function materialRadioButtonDirective($materialAria) {
var CHECKED_CSS = 'material-checked';
return {
restrict: 'E',
require: '^materialRadioGroup',
transclude: true,
template: '<div class="material-container" ink-ripple="checkbox">' +
'<div class="material-off"></div>' +
'<div class="material-on"></div>' +
'</div>' +
'<div ng-transclude class="material-label"></div>',
link: link
};
function link(scope, element, attr, rgCtrl) {
var lastChecked;
configureAria(element, scope);
rgCtrl.add(render);
attr.$observe('value', render);
element
.on('click', listener)
.on('$destroy', function() {
rgCtrl.remove(render);
});
function listener(ev) {
if (element[0].hasAttribute('disabled')) return;
scope.$apply(function() {
rgCtrl.setViewValue(attr.value, ev && ev.type);
});
}
function render() {
var checked = (rgCtrl.getViewValue() === attr.value);
if (checked === lastChecked) {
return;
}
lastChecked = checked;
element.attr('aria-checked', checked);
if (checked) {
element.addClass(CHECKED_CSS);
rgCtrl.setActiveDescendant(element.attr('id'));
} else {
element.removeClass(CHECKED_CSS);
}
}
/**
* Inject ARIA-specific attributes appropriate for each radio button
*/
function configureAria( element, scope ){
scope.ariaId = buildAriaID();
element.attr({
'id' : scope.ariaId,
'role' : 'radio',
'aria-checked' : 'false'
});
$materialAria.expect(element, 'aria-label', element.text());
/**
* Build a unique ID for each radio button that will be used with aria-activedescendant.
* Preserve existing ID if already specified.
* @returns {*|string}
*/
function buildAriaID() {
return attr.id || ( 'radio' + "_" + Util.nextUid() );
}
}
}
}
/**
* @ngdoc module
* @name material.components.sidenav
*
* @description
* A Sidenav QP component.
*/
angular.module('material.components.sidenav', [
'material.services.registry',
'material.animations'
])
.factory('$materialSidenav', [
'$materialComponentRegistry',
materialSidenavService
])
.directive('materialSidenav', [
'$timeout',
'$materialEffects',
'$$rAF',
materialSidenavDirective
])
.controller('$materialSidenavController', [
'$scope',
'$element',
'$attrs',
'$timeout',
'$materialSidenav',
'$materialComponentRegistry',
materialSidenavController
]);
/**
* @private
* @ngdoc object
* @name materialSidenavController
* @module material.components.sidenav
*
* @description
* The controller for materialSidenav components.
*/
function materialSidenavController($scope, $element, $attrs, $timeout, $materialSidenav, $materialComponentRegistry) {
var self = this;
$materialComponentRegistry.register(this, $attrs.componentId);
this.isOpen = function() {
return !!$scope.isOpen;
};
/**
* Toggle the side menu to open or close depending on its current state.
*/
this.toggle = function() {
$scope.isOpen = !$scope.isOpen;
};
/**
* Open the side menu
*/
this.open = function() {
$scope.isOpen = true;
};
/**
* Close the side menu
*/
this.close = function() {
$scope.isOpen = false;
};
}
/**
* @private
* @ngdoc service
* @name $materialSidenav
* @module material.components.sidenav
*
* @description
* $materialSidenav makes it easy to interact with multiple sidenavs
* in an app.
*
* @usage
*
* ```javascript
* // Toggle the given sidenav
* $materialSidenav(componentId).toggle();
*
* // Open the given sidenav
* $materialSidenav(componentId).open();
*
* // Close the given sidenav
* $materialSidenav(componentId).close();
* ```
*/
function materialSidenavService($materialComponentRegistry) {
return function(handle) {
var instance = $materialComponentRegistry.get(handle);
if(!instance) {
$materialComponentRegistry.notFoundError(handle);
}
return {
isOpen: function() {
if (!instance) { return; }
return instance.isOpen();
},
/**
* Toggle the given sidenav
* @param handle the specific sidenav to toggle
*/
toggle: function() {
if(!instance) { return; }
instance.toggle();
},
/**
* Open the given sidenav
* @param handle the specific sidenav to open
*/
open: function(handle) {
if(!instance) { return; }
instance.open();
},
/**
* Close the given sidenav
* @param handle the specific sidenav to close
*/
close: function(handle) {
if(!instance) { return; }
instance.close();
}
};
};
}
/**
* @ngdoc directive
* @name materialSidenav
* @module material.components.sidenav
* @restrict E
*
* @description
*
* A Sidenav component that can be opened and closed programatically.
*
* When used properly with a layout, it will seamleslly stay open on medium
* and larger screens, while being hidden by default on mobile devices.
*
* @usage
* <hljs lang="html">
* <div layout="horizontal" ng-controller="MyController">
* <material-sidenav component-id="left" class="material-sidenav-left">
* Left Nav!
* </material-sidenav>
*
* <material-content>
* Center Content
* <material-button ng-click="openLeftMenu()">
* Open Left Menu
* </material-button>
* </material-content>
*
* <material-sidenav component-id="right" class="material-sidenav-right">
* Right Nav!
* </material-sidenav>
* </div>
* </hljs>
*
* <hljs lang="js">
* var app = angular.module('myApp', ['ngMaterial']);
* app.controller('MainController', function($scope, $materialSidenav) {
* $scope.openLeftMenu = function() {
* $materialSidenav('left').toggle();
* };
* });
* </hljs>
*/
function materialSidenavDirective($timeout, $materialEffects, $$rAF) {
return {
restrict: 'E',
scope: {},
controller: '$materialSidenavController',
compile: compile
};
function compile(element, attr) {
element.addClass('closed');
return postLink;
}
function postLink(scope, element, attr, sidenavCtrl) {
var backdrop = angular.element('<material-backdrop class="material-sidenav-backdrop">');
scope.$watch('isOpen', onShowHideSide);
element.on($materialEffects.TRANSITIONEND_EVENT, onTransitionEnd);
/**
* Toggle the SideNav view and attach/detach listeners
* @param isOpen
*/
function onShowHideSide(isOpen) {
var parent = element.parent();
if (isOpen) {
element.removeClass('closed');
parent.append(backdrop);
backdrop.on('click', close);
parent.on('keydown', onKeyDown);
} else {
backdrop.remove();
backdrop.off('click', close);
parent.off('keydown', onKeyDown);
}
// Wait until the next frame, so that if the `closed` class was just removed the
// element has a chance to 're-initialize' from being display: none.
$$rAF(function() {
element.toggleClass('open', !!scope.isOpen);
});
}
function onTransitionEnd(ev) {
if (ev.target === element[0] && !scope.isOpen) {
element.addClass('closed');
}
}
/**
* Auto-close sideNav when the `escape` key is pressed.
* @param evt
*/
function onKeyDown(evt) {
if(evt.which === Constant.KEY_CODE.ESCAPE){
close();
evt.preventDefault();
evt.stopPropagation();
}
}
/**
* With backdrop `clicks` or `escape` key-press, immediately
* apply the CSS close transition... Then notify the controller
* to close() and perform its own actions.
*/
function close() {
onShowHideSide( false );
$timeout(function(){
sidenavCtrl.close();
});
}
}
}
/**
* @ngdoc module
* @name material.components.slider
*/
angular.module('material.components.slider', [
'material.animations',
'material.services.aria'
])
.directive('materialSlider', [
SliderDirective
]);
/**
* @ngdoc directive
* @name materialSlider
* @module material.components.slider
* @restrict E
* @description
* The `<material-slider>` component allows the user to choose from a range of
* values.
*
* It has two modes: 'normal' mode, where the user slides between a wide range
* of values, and 'discrete' mode, where the user slides between only a few
* select values.
*
* To enable discrete mode, add the `discrete` attribute to a slider,
* and use the `step` attribute to change the distance between
* values the user is allowed to pick.
*
* @usage
* <h4>Normal Mode</h4>
* <hljs lang="html">
* <material-slider ng-model="myValue" min="5" max="500">
* </material-slider>
* </hljs>
* <h4>Discrete Mode</h4>
* <hljs lang="html">
* <material-slider discrete ng-model="myDiscreteValue" step="10" min="10" max="130">
* </material-slider>
* </hljs>
*
* @param {boolean=} discrete Whether to enable discrete mode.
* @param {number=} step The distance between values the user is allowed to pick. Default 1.
* @param {number=} min The minimum value the user is allowed to pick. Default 0.
* @param {number=} max The maximum value the user is allowed to pick. Default 100.
*/
function SliderDirective() {
return {
scope: {},
require: ['?ngModel', 'materialSlider'],
controller: [
'$scope',
'$element',
'$attrs',
'$$rAF',
'$window',
'$materialEffects',
'$materialAria',
SliderController
],
template:
'<div class="slider-track-container">' +
'<div class="slider-track"></div>' +
'<div class="slider-track slider-track-fill"></div>' +
'<div class="slider-track-ticks"></div>' +
'</div>' +
'<div class="slider-thumb-container">' +
'<div class="slider-thumb"></div>' +
'<div class="slider-focus-thumb"></div>' +
'<div class="slider-focus-ring"></div>' +
'<div class="slider-sign">' +
'<span class="slider-thumb-text" ng-bind="modelValue"></span>' +
'</div>' +
'<div class="slider-disabled-thumb"></div>' +
'</div>',
link: postLink
};
function postLink(scope, element, attr, ctrls) {
var ngModelCtrl = ctrls[0] || {
// Mock ngModelController if it doesn't exist to give us
// the minimum functionality needed
$setViewValue: function(val) {
this.$viewValue = val;
this.$viewChangeListeners.forEach(function(cb) { cb(); });
},
$parsers: [],
$formatters: [],
$viewChangeListeners: []
};
var sliderCtrl = ctrls[1];
sliderCtrl.init(ngModelCtrl);
}
}
/**
* We use a controller for all the logic so that we can expose a few
* things to unit tests
*/
function SliderController(scope, element, attr, $$rAF, $window, $materialEffects, $materialAria) {
this.init = function init(ngModelCtrl) {
var thumb = angular.element(element[0].querySelector('.slider-thumb'));
var thumbContainer = thumb.parent();
var trackContainer = angular.element(element[0].querySelector('.slider-track-container'));
var activeTrack = angular.element(element[0].querySelector('.slider-track-fill'));
var tickContainer = angular.element(element[0].querySelector('.slider-track-ticks'));
// Default values, overridable by attrs
attr.min ? attr.$observe('min', updateMin) : updateMin(0);
attr.max ? attr.$observe('max', updateMax) : updateMax(100);
attr.step ? attr.$observe('step', updateStep) : updateStep(1);
// We have to manually stop the $watch on ngDisabled because it exists
// on the parent scope, and won't be automatically destroyed when
// the component is destroyed.
var stopDisabledWatch = angular.noop;
if (attr.ngDisabled) {
stopDisabledWatch = scope.$parent.$watch(attr.ngDisabled, updateAriaDisabled);
} else {
updateAriaDisabled(!!attr.disabled);
}
$materialAria.expect(element, 'aria-label');
element.attr('tabIndex', 0);
element.attr('role', 'slider');
element.on('keydown', keydownListener);
var hammertime = new Hammer(element[0], {
recognizers: [
[Hammer.Pan, { direction: Hammer.DIRECTION_HORIZONTAL }]
]
});
hammertime.on('hammer.input', onInput);
hammertime.on('panstart', onPanStart);
hammertime.on('pan', onPan);
hammertime.on('panend', onPanEnd);
// On resize, recalculate the slider's dimensions and re-render
var updateAll = $$rAF.debounce(function() {
refreshSliderDimensions();
ngModelRender();
redrawTicks();
});
updateAll();
angular.element($window).on('resize', updateAll);
scope.$on('$destroy', function() {
angular.element($window).off('resize', updateAll);
hammertime.destroy();
stopDisabledWatch();
});
ngModelCtrl.$render = ngModelRender;
ngModelCtrl.$viewChangeListeners.push(ngModelRender);
ngModelCtrl.$formatters.push(minMaxValidator);
ngModelCtrl.$formatters.push(stepValidator);
/**
* Attributes
*/
var min;
var max;
var step;
function updateMin(value) {
min = parseFloat(value);
element.attr('aria-valuemin', value);
}
function updateMax(value) {
max = parseFloat(value);
element.attr('aria-valuemax', value);
}
function updateStep(value) {
step = parseFloat(value);
redrawTicks();
}
function updateAriaDisabled(isDisabled) {
element.attr('aria-disabled', !!isDisabled);
}
// Draw the ticks with canvas.
// The alternative to drawing ticks with canvas is to draw one element for each tick,
// which could quickly become a performance bottleneck.
var tickCanvas, tickCtx;
function redrawTicks() {
if (!angular.isDefined(attr.discrete)) return;
var numSteps = Math.floor( (max - min) / step );
if (!tickCanvas) {
tickCanvas = angular.element('<canvas style="position:absolute;">');
tickCtx = tickCanvas[0].getContext('2d');
tickCtx.fillStyle = 'black';
tickContainer.append(tickCanvas);
}
var dimensions = getSliderDimensions();
tickCanvas[0].width = dimensions.width;
tickCanvas[0].height = dimensions.height;
var distance;
for (var i = 0; i <= numSteps; i++) {
distance = Math.floor(dimensions.width * (i / numSteps));
tickCtx.fillRect(distance - 1, 0, 2, dimensions.height);
}
}
/**
* Refreshing Dimensions
*/
var sliderDimensions = {};
var throttledRefreshDimensions = Util.throttle(refreshSliderDimensions, 5000);
refreshSliderDimensions();
function refreshSliderDimensions() {
sliderDimensions = trackContainer[0].getBoundingClientRect();
}
function getSliderDimensions() {
throttledRefreshDimensions();
return sliderDimensions;
}
/**
* left/right arrow listener
*/
function keydownListener(ev) {
var changeAmount;
if (ev.which === Constant.KEY_CODE.LEFT_ARROW) {
changeAmount = -step;
} else if (ev.which === Constant.KEY_CODE.RIGHT_ARROW) {
changeAmount = step;
}
if (changeAmount) {
if (ev.metaKey || ev.ctrlKey || ev.altKey) {
changeAmount *= 4;
}
ev.preventDefault();
ev.stopPropagation();
scope.$evalAsync(function() {
setModelValue(ngModelCtrl.$viewValue + changeAmount);
});
}
}
/**
* ngModel setters and validators
*/
function setModelValue(value) {
ngModelCtrl.$setViewValue( minMaxValidator(stepValidator(value)) );
}
function ngModelRender() {
var percent = (ngModelCtrl.$viewValue - min) / (max - min);
scope.modelValue = ngModelCtrl.$viewValue;
element.attr('aria-valuenow', ngModelCtrl.$viewValue);
setSliderPercent(percent);
}
function minMaxValidator(value) {
if (angular.isNumber(value)) {
return Math.max(min, Math.min(max, value));
}
}
function stepValidator(value) {
if (angular.isNumber(value)) {
return Math.round(value / step) * step;
}
}
/**
* @param percent 0-1
*/
function setSliderPercent(percent) {
activeTrack.css('width', (percent * 100) + '%');
thumbContainer.css(
$materialEffects.TRANSFORM,
'translate3d(' + getSliderDimensions().width * percent + 'px,0,0)'
);
element.toggleClass('slider-min', percent === 0);
}
/**
* Slide listeners
*/
var isSliding = false;
var isDiscrete = angular.isDefined(attr.discrete);
function onInput(ev) {
if (!isSliding && ev.eventType === Hammer.INPUT_START &&
!element[0].hasAttribute('disabled')) {
isSliding = true;
element.addClass('active');
element[0].focus();
refreshSliderDimensions();
onPan(ev);
ev.srcEvent.stopPropagation();
} else if (isSliding && ev.eventType === Hammer.INPUT_END) {
if ( isDiscrete ) onPanEnd(ev);
isSliding = false;
element.removeClass('panning active');
}
}
function onPanStart() {
if (!isSliding) return;
element.addClass('panning');
}
function onPan(ev) {
if (!isSliding) return;
// While panning discrete, update only the
// visual positioning but not the model value.
if ( isDiscrete ) adjustThumbPosition( ev.center.x );
else doSlide( ev.center.x );
ev.preventDefault();
ev.srcEvent.stopPropagation();
}
function onPanEnd(ev) {
if ( isDiscrete ) {
// Convert exact to closest discrete value.
// Slide animate the thumb... and then update the model value.
var exactVal = percentToValue( positionToPercent( ev.center.x ));
var closestVal = minMaxValidator( stepValidator(exactVal) );
setSliderPercent( valueToPercent(closestVal));
$$rAF(function(){
setModelValue( closestVal );
});
ev.preventDefault();
ev.srcEvent.stopPropagation();
}
}
/**
* Expose for testing
*/
this._onInput = onInput;
this._onPanStart = onPanStart;
this._onPan = onPan;
/**
* Slide the UI by changing the model value
* @param x
*/
function doSlide( x ) {
scope.$evalAsync( function() {
setModelValue( percentToValue( positionToPercent(x) ));
});
}
/**
* Slide the UI without changing the model (while dragging/panning)
* @param x
*/
function adjustThumbPosition( x ) {
setSliderPercent( positionToPercent(x) );
}
/**
* Convert horizontal position on slider to percentage value of offset from beginning...
* @param x
* @returns {number}
*/
function positionToPercent( x ) {
return (x - sliderDimensions.left) / (sliderDimensions.width);
}
/**
* Convert percentage offset on slide to equivalent model value
* @param percent
* @returns {*}
*/
function percentToValue( percent ) {
return (min + percent * (max - min));
}
function valueToPercent( val ) {
return (val - min)/(max - min);
}
};
}
/**
* @private
* @ngdoc module
* @name material.components.switch
*/
angular.module('material.components.switch', [
'material.components.checkbox',
'material.components.radioButton'
])
.directive('materialSwitch', [
'materialCheckboxDirective',
'materialRadioButtonDirective',
MaterialSwitch
]);
/**
* @private
* @ngdoc directive
* @module material.components.switch
* @name materialSwitch
* @restrict E
*
* The switch directive is used very much like the normal [angular checkbox](https://docs.angularjs.org/api/ng/input/input%5Bcheckbox%5D).
*
* @param {string} ngModel Assignable angular expression to data-bind to.
* @param {string=} name Property name of the form under which the control is published.
* @param {expression=} ngTrueValue The value to which the expression should be set when selected.
* @param {expression=} ngFalseValue The value to which the expression should be set when not selected.
* @param {string=} ngChange Angular expression to be executed when input changes due to user interaction with the input element.
* @param {boolean=} noink Use of attribute indicates use of ripple ink effects.
* @param {boolean=} disabled Use of attribute indicates the switch is disabled: no ink effects and not selectable
* @param {string=} ariaLabel Publish the button label used by screen-readers for accessibility. Defaults to the switch's text.
*
* @usage
* <hljs lang="html">
* <material-switch ng-model="isActive" aria-label="Finished?">
* Finished ?
* </material-switch>
*
* <material-switch noink ng-model="hasInk" aria-label="No Ink Effects">
* No Ink Effects
* </material-switch>
*
* <material-switch disabled ng-model="isDisabled" aria-label="Disabled">
* Disabled
* </material-switch>
*
* </hljs>
*/
function MaterialSwitch(checkboxDirectives, radioButtonDirectives) {
var checkboxDirective = checkboxDirectives[0];
var radioButtonDirective = radioButtonDirectives[0];
return {
restrict: 'E',
transclude: true,
template:
'<div class="material-switch-bar"></div>' +
'<div class="material-switch-thumb">' +
radioButtonDirective.template +
'</div>',
require: '?ngModel',
compile: compile
};
function compile(element, attr) {
var thumb = angular.element(element[0].querySelector('.material-switch-thumb'));
//Copy down disabled attributes for checkboxDirective to use
thumb.attr('disabled', attr.disabled);
thumb.attr('ngDisabled', attr.ngDisabled);
var link = checkboxDirective.compile(thumb, attr);
return function (scope, element, attr, ngModelCtrl) {
var thumb = angular.element(element[0].querySelector('.material-switch-thumb'));
return link(scope, thumb, attr, ngModelCtrl)
};
}
}
/**
* @ngdoc module
* @name material.components.subheader
* @description
* SubHeader module
*/
angular.module('material.components.subheader', [
'material.components.sticky'
])
.directive('materialSubheader', [
'$materialSticky',
'$compile',
MaterialSubheaderDirective
]);
/**
* @ngdoc directive
* @name materialSubheader
* @module material.components.subheader
*
* @restrict E
*
* @description
* The `<material-subheader>` directive is a subheader for a section
*
* @usage
* <hljs lang="html">
* <material-subheader>Online Friends</material-subheader>
* </hljs>
*/
function MaterialSubheaderDirective($materialSticky, $compile) {
return {
restrict: 'E',
replace: true,
transclude: true,
template:
'<h2 class="material-subheader">' +
'<span class="material-subheader-content"></span>' +
'</h2>',
compile: function(element, attr, transclude) {
var outerHTML = element[0].outerHTML;
return function postLink(scope, element, attr) {
function getContent(el) {
return angular.element(el[0].querySelector('.material-subheader-content'));
}
// Transclude the user-given contents of the subheader
// the conventional way.
transclude(scope, function(clone) {
getContent(element).append(clone);
});
// Create another clone, that uses the outer and inner contents
// of the element, that will be 'stickied' as the user scrolls.
transclude(scope, function(clone) {
var stickyClone = $compile(angular.element(outerHTML))(scope);
getContent(stickyClone).append(clone);
$materialSticky(scope, element, stickyClone);
});
};
}
};
}
/**
* @ngdoc module
* @name material.components.tabs
* @description
*
* Tabs
*/
angular.module('material.components.tabs', [
'material.animations',
'material.components.swipe'
]);
/**
* Conditionally configure ink bar animations when the
* tab selection changes. If `nobar` then do not show the
* bar nor animate.
*/
angular.module('material.components.tabs')
.directive('materialTabsInkBar', [
'$materialEffects',
'$window',
'$$rAF',
'$timeout',
MaterialTabInkDirective
]);
function MaterialTabInkDirective($materialEffects, $window, $$rAF, $timeout) {
return {
restrict: 'E',
require: ['^?nobar', '^materialTabs'],
link: postLink
};
function postLink(scope, element, attr, ctrls) {
var nobar = ctrls[0];
var tabsCtrl = ctrls[1];
if (nobar) return;
var debouncedUpdateBar = $$rAF.debounce(updateBar);
scope.$watch(tabsCtrl.selected, updateBar);
scope.$on('$materialTabsChanged', debouncedUpdateBar);
scope.$on('$materialTabsPaginationChanged', debouncedUpdateBar);
angular.element($window).on('resize', onWindowResize);
function onWindowResize() {
debouncedUpdateBar();
$timeout(debouncedUpdateBar, 100, false);
}
scope.$on('$destroy', function() {
angular.element($window).off('resize', onWindowResize);
});
function updateBar() {
var selectedElement = tabsCtrl.selected() && tabsCtrl.selected().element;
if (!selectedElement || tabsCtrl.count() < 2) {
element.css({
display : 'none',
width : '0px'
});
} else {
var width = selectedElement.prop('offsetWidth');
var left = selectedElement.prop('offsetLeft') + (tabsCtrl.$$pagingOffset || 0);
element.css({
display : width > 0 ? 'block' : 'none',
width: width + 'px'
});
element.css($materialEffects.TRANSFORM, 'translate3d(' + left + 'px,0,0)');
}
}
}
}
angular.module('material.components.tabs')
.directive('materialTabsPagination', [
'$materialEffects',
'$window',
'$$rAF',
'$$q',
'$timeout',
TabPaginationDirective
]);
function TabPaginationDirective($materialEffects, $window, $$rAF, $$q, $timeout) {
// TODO allow configuration of TAB_MIN_WIDTH
// Must match tab min-width rule in _tabs.scss
var TAB_MIN_WIDTH = 8 * 12;
// Must match (2 * width of paginators) in scss
var PAGINATORS_WIDTH = (8 * 4) * 2;
return {
restrict: 'A',
require: '^materialTabs',
link: postLink
};
function postLink(scope, element, attr, tabsCtrl) {
var tabsParent = element.children();
var state = scope.pagination = {
page: -1,
active: false,
clickNext: function() { userChangePage(+1); },
clickPrevious: function() { userChangePage(-1); }
};
var debouncedUpdatePagination = $$rAF.debounce(updatePagination);
scope.$on('$materialTabsChanged', debouncedUpdatePagination);
angular.element($window).on('resize', debouncedUpdatePagination);
// Listen to focus events bubbling up from material-tab elements
tabsParent.on('focusin', onTabsFocusIn);
scope.$on('$destroy', function() {
angular.element($window).off('resize', debouncedUpdatePagination);
tabsParent.off('focusin', onTabsFocusIn);
});
scope.$watch(tabsCtrl.selected, onSelectedTabChange);
// Allows pagination through focus change.
function onTabsFocusIn(ev) {
if (!state.active) return;
var tab = angular.element(ev.target).controller('materialTab');
var pageIndex = getPageForTab(tab);
if (pageIndex !== state.page) {
// If the focused element is on a new page, don't focus yet.
tab.element.blur();
// Go to the new page, wait for the page transition to end, then focus.
setPage(pageIndex).then(function() {
tab.element.focus();
});
}
}
function onSelectedTabChange(selectedTab) {
if (!selectedTab) return;
if (state.active) {
var selectedTabPage = getPageForTab(selectedTab);
setPage(selectedTabPage);
} else {
debouncedUpdatePagination();
}
}
// Called when page is changed by a user action (click)
function userChangePage(increment) {
var newPage = state.page + increment;
var newTab;
if (!tabsCtrl.selected() || getPageForTab(tabsCtrl.selected()) !== newPage) {
var startIndex;
if (increment < 0) {
// If going backward, select the previous available tab, starting from
// the first item on the page after newPage.
startIndex = (newPage + 1) * state.itemsPerPage;
newTab = tabsCtrl.previous( tabsCtrl.itemAt(startIndex) );
} else {
// If going forward, select the next available tab, starting with the
// last item before newPage.
startIndex = (newPage * state.itemsPerPage) - 1;
newTab = tabsCtrl.next( tabsCtrl.itemAt(startIndex) );
}
}
setPage(newPage).then(function() {
newTab && newTab.element.focus();
});
newTab && tabsCtrl.select(newTab);
}
function updatePagination() {
var tabs = element.find('material-tab');
var tabsWidth = element.parent().prop('clientWidth') - PAGINATORS_WIDTH;
var needPagination = tabsWidth && TAB_MIN_WIDTH * tabsCtrl.count() > tabsWidth;
var paginationToggled = needPagination !== state.active;
state.active = needPagination;
if (needPagination) {
state.pagesCount = Math.ceil((TAB_MIN_WIDTH * tabsCtrl.count()) / tabsWidth);
state.itemsPerPage = Math.max(1, Math.floor(tabsCtrl.count() / state.pagesCount));
state.tabWidth = tabsWidth / state.itemsPerPage;
tabsParent.css('width', state.tabWidth * tabsCtrl.count() + 'px');
tabs.css('width', state.tabWidth + 'px');
var selectedTabPage = getPageForTab(tabsCtrl.selected());
setPage(selectedTabPage);
} else {
if (paginationToggled) {
$timeout(function() {
tabsParent.css('width', '');
tabs.css('width', '');
slideTabButtons(0);
state.page = -1;
});
}
}
}
function slideTabButtons(x) {
if (tabsCtrl.pagingOffset === x) {
// Resolve instantly if no change
return $$q.when();
}
var deferred = $$q.defer();
tabsCtrl.$$pagingOffset = x;
tabsParent.css($materialEffects.TRANSFORM, 'translate3d(' + x + 'px,0,0)');
tabsParent.on($materialEffects.TRANSITIONEND_EVENT, onTabsParentTransitionEnd);
return deferred.promise;
function onTabsParentTransitionEnd(ev) {
// Make sure this event didn't bubble up from an animation in a child element.
if (ev.target === tabsParent[0]) {
tabsParent.off($materialEffects.TRANSITIONEND_EVENT, onTabsParentTransitionEnd);
deferred.resolve();
}
}
}
function getPageForTab(tab) {
var tabIndex = tabsCtrl.indexOf(tab);
if (tabIndex === -1) return 0;
return Math.floor(tabIndex / state.itemsPerPage);
}
function setPage(page) {
if (page === state.page) return;
var lastPage = state.pagesCount;
if (page < 0) page = 0;
if (page > lastPage) page = lastPage;
state.hasPrev = page > 0;
state.hasNext = ((page + 1) * state.itemsPerPage) < tabsCtrl.count();
state.page = page;
$timeout(function() {
scope.$broadcast('$materialTabsPaginationChanged');
});
return slideTabButtons(-page * state.itemsPerPage * state.tabWidth);
}
}
}
angular.module('material.components.tabs')
.controller('$materialTab', [
'$scope',
'$element',
'$compile',
'$animate',
'$materialSwipe',
TabItemController
]);
function TabItemController(scope, element, $compile, $animate, $materialSwipe) {
var self = this;
var detachSwipe = angular.noop;
var attachSwipe = function() { return detachSwipe };
var eventTypes = "swipeleft swiperight" ;
var configureSwipe = $materialSwipe( scope, eventTypes );
// special callback assigned by TabsController
self.$$onSwipe = angular.noop;
// Properties
self.contentContainer = angular.element('<div class="tab-content ng-hide">');
self.element = element;
// Methods
self.isDisabled = isDisabled;
self.onAdd = onAdd;
self.onRemove = onRemove;
self.onSelect = onSelect;
self.onDeselect = onDeselect;
function isDisabled() {
return element[0].hasAttribute('disabled');
}
/**
* Add the tab's content to the DOM container area in the tabs,
* @param contentArea the contentArea to add the content of the tab to
*/
function onAdd(contentArea) {
if (self.content.length) {
self.contentContainer.append(self.content);
self.contentScope = scope.$parent.$new();
contentArea.append(self.contentContainer);
$compile(self.contentContainer)(self.contentScope);
Util.disconnectScope(self.contentScope);
// For internal tab views we only use the `$materialSwipe`
// so we can easily attach()/detach() when the tab view is active/inactive
attachSwipe = configureSwipe( self.contentContainer, function(ev) {
self.$$onSwipe(ev.type);
}, true );
}
}
/**
* Usually called when a Tab is programmatically removed; such
* as in an ng-repeat
*/
function onRemove() {
$animate.leave(self.contentContainer).then(function()
{
self.contentScope && self.contentScope.$destroy();
self.contentScope = null;
});
}
function onSelect() {
// Resume watchers and events firing when tab is selected
Util.reconnectScope(self.contentScope);
detachSwipe = attachSwipe();
element.addClass('active');
element.attr('aria-selected', true);
element.attr('tabIndex', 0);
$animate.removeClass(self.contentContainer, 'ng-hide');
scope.onSelect();
}
function onDeselect() {
// Stop watchers & events from firing while tab is deselected
Util.disconnectScope(self.contentScope);
detachSwipe();
element.removeClass('active');
element.attr('aria-selected', false);
// Only allow tabbing to the active tab
element.attr('tabIndex', -1);
$animate.addClass(self.contentContainer, 'ng-hide');
scope.onDeselect();
}
}
angular.module('material.components.tabs')
.directive('materialTab', [
'$materialInkRipple',
'$compile',
'$materialAria',
MaterialTabDirective
]);
/**
* @ngdoc directive
* @name materialTab
* @module material.components.tabs
* @order 1
*
* @restrict E
*
* @description
* `<material-tab>` is the nested directive used [within `<material-tabs>`] to specify each tab with a **label** and optional *view content*.
*
* If the `label` attribute is not specified, then an optional `<material-tab-label>` tag can be used to specified more
* complex tab header markup. If neither the **label** nor the **material-tab-label** are specified, then the nested
* markup of the `<material-tab>` is used as the tab header markup.
*
* If a tab **label** has been identified, then any **non-**`<material-tab-label>` markup
* will be considered tab content and will be transcluded to the internal `<div class="tabs-content">` container.
*
* This container is used by the TabsController to show/hide the active tab's content view. This synchronization is
* automatically managed by the internal TabsController whenever the tab selection changes. Selection changes can
* be initiated via data binding changes, programmatic invocation, or user gestures.
*
* @param {string=} label Optional attribute to specify a simple string as the tab label
* @param {boolean=} active When evaluteing to true, selects the tab.
* @param {boolean=} disabled If present, disabled tab selection.
* @param {expression=} deselected Expression to be evaluated after the tab has been de-selected.
* @param {expression=} selected Expression to be evaluated after the tab has been selected.
*
*
* @usage
*
* <hljs lang="html">
* <material-tab label="" disabled="" selected="" deselected="" >
* <h3>My Tab content</h3>
* </material-tab>
*
* <material-tab >
* <material-tab-label>
* <h3>My Tab content</h3>
* </material-tab-label>
* <p>
* Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium,
* totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae
* dicta sunt explicabo. Nemo enim ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit,
* sed quia consequuntur magni dolores eos qui ratione voluptatem sequi nesciunt.
* </p>
* </material-tab>
* </hljs>
*
*/
function MaterialTabDirective($materialInkRipple, $compile, $materialAria) {
return {
restrict: 'E',
require: ['materialTab', '^materialTabs'],
controller: '$materialTab',
scope: {
onSelect: '&',
onDeselect: '&',
label: '@'
},
compile: compile
};
function compile(element, attr) {
var tabLabel = element.find('material-tab-label');
// If a tab label element is found, remove it for later re-use.
if (tabLabel.length) {
tabLabel.remove();
// Otherwise, try to use attr.label as the label
} else if (angular.isDefined(attr.label)) {
tabLabel = angular.element('<material-tab-label>').html(attr.label);
// If nothing is found, use the tab's content as the label
} else {
tabLabel = angular.element('<material-tab-label>')
.append(element.contents().remove());
}
// Everything that's left as a child is the tab's content.
var tabContent = element.contents().remove();
return function postLink(scope, element, attr, ctrls) {
var tabItemCtrl = ctrls[0]; // Controller for THIS tabItemCtrl
var tabsCtrl = ctrls[1]; // Controller for ALL tabs
transcludeTabContent();
var detachRippleFn = $materialInkRipple.attachButtonBehavior(element);
tabsCtrl.add(tabItemCtrl);
scope.$on('$destroy', function() {
detachRippleFn();
tabsCtrl.remove(tabItemCtrl);
});
if (!angular.isDefined(attr.ngClick)) element.on('click', defaultClickListener);
element.on('keydown', keydownListener);
if (angular.isNumber(scope.$parent.$index)) watchNgRepeatIndex();
if (angular.isDefined(attr.active)) watchActiveAttribute();
watchDisabled();
configureAria();
function transcludeTabContent() {
// Clone the label we found earlier, and $compile and append it
var label = tabLabel.clone();
element.append(label);
$compile(label)(scope.$parent);
// Clone the content we found earlier, and mark it for later placement into
// the proper content area.
tabItemCtrl.content = tabContent.clone();
}
//defaultClickListener isn't applied if the user provides an ngClick expression.
function defaultClickListener() {
scope.$apply(function() {
tabsCtrl.select(tabItemCtrl);
tabItemCtrl.element.focus();
});
}
function keydownListener(ev) {
if (ev.which == Constant.KEY_CODE.SPACE ) {
// Fire the click handler to do normal selection if space is pressed
element.triggerHandler('click');
ev.preventDefault();
} else if (ev.which === Constant.KEY_CODE.LEFT_ARROW) {
var previous = tabsCtrl.previous(tabItemCtrl);
previous && previous.element.focus();
} else if (ev.which === Constant.KEY_CODE.RIGHT_ARROW) {
var next = tabsCtrl.next(tabItemCtrl);
next && next.element.focus();
}
}
// If tabItemCtrl is part of an ngRepeat, move the tabItemCtrl in our internal array
// when its $index changes
function watchNgRepeatIndex() {
// The tabItemCtrl has an isolate scope, so we watch the $index on the parent.
scope.$watch('$parent.$index', function $indexWatchAction(newIndex) {
tabsCtrl.move(tabItemCtrl, newIndex);
});
}
function watchActiveAttribute() {
var unwatch = scope.$parent.$watch('!!(' + attr.active + ')', activeWatchAction);
scope.$on('$destroy', unwatch);
function activeWatchAction(isActive) {
var isSelected = tabsCtrl.selected() === tabItemCtrl;
if (isActive && !isSelected) {
tabsCtrl.select(tabItemCtrl);
} else if (!isActive && isSelected) {
tabsCtrl.deselect(tabItemCtrl);
}
}
}
function watchDisabled() {
scope.$watch(tabItemCtrl.isDisabled, disabledWatchAction);
function disabledWatchAction(isDisabled) {
element.attr('aria-disabled', isDisabled);
// Auto select `next` tab when disabled
var isSelected = (tabsCtrl.selected() === tabItemCtrl);
if (isSelected && isDisabled) {
tabsCtrl.select(tabsCtrl.next() || tabsCtrl.previous());
}
}
}
function configureAria() {
// Link together the content area and tabItemCtrl with an id
var tabId = attr.id || Util.nextUid();
var tabContentId = 'content_' + tabId;
element.attr({
id: tabId,
role: 'tabItemCtrl',
tabIndex: '-1', //this is also set on select/deselect in tabItemCtrl
'aria-controls': tabContentId
});
tabItemCtrl.contentContainer.attr({
id: tabContentId,
role: 'tabpanel',
'aria-labelledby': tabId
});
$materialAria.expect(element, 'aria-label', element.text());
}
};
}
}
angular.module('material.components.tabs')
.controller('$materialTabs', [
'$scope',
'$element',
MaterialTabsController
]);
function MaterialTabsController(scope, element) {
var tabsList = Util.iterator([], false);
var self = this;
// Properties
self.element = element;
// The section containing the tab content elements
self.contentArea = angular.element(element[0].querySelector('.tabs-content'));
// Methods from iterator
self.inRange = tabsList.inRange;
self.indexOf = tabsList.indexOf;
self.itemAt = tabsList.itemAt;
self.count = tabsList.count;
self.selected = selected;
self.add = add;
self.remove = remove;
self.move = move;
self.select = select;
self.deselect = deselect;
self.next = next;
self.previous = previous;
self.swipe = swipe;
// Get the selected tab
function selected() {
return self.itemAt(scope.selectedIndex);
}
// Add a new tab.
// Returns a method to remove the tab from the list.
function add(tab, index) {
tabsList.add(tab, index);
tab.onAdd(self.contentArea);
// Register swipe feature
tab.$$onSwipe = swipe;
// Select the new tab if we don't have a selectedIndex, or if the
// selectedIndex we've been waiting for is this tab
if (scope.selectedIndex === -1 || scope.selectedIndex === self.indexOf(tab)) {
self.select(tab);
}
scope.$broadcast('$materialTabsChanged');
}
function remove(tab) {
if (!tabsList.contains(tab)) return;
if (self.selected() === tab) {
if (tabsList.count() > 1) {
self.select(self.previous() || self.next());
} else {
self.deselect(tab);
}
}
tabsList.remove(tab);
tab.onRemove();
scope.$broadcast('$materialTabsChanged');
}
// Move a tab (used when ng-repeat order changes)
function move(tab, toIndex) {
var isSelected = self.selected() === tab;
tabsList.remove(tab);
tabsList.add(tab, toIndex);
if (isSelected) self.select(tab);
scope.$broadcast('$materialTabsChanged');
}
function select(tab) {
if (!tab || tab.isSelected || tab.isDisabled()) return;
if (!tabsList.contains(tab)) return;
self.deselect(self.selected());
scope.selectedIndex = self.indexOf(tab);
tab.isSelected = true;
tab.onSelect();
}
function deselect(tab) {
if (!tab || !tab.isSelected) return;
if (!tabsList.contains(tab)) return;
scope.selectedIndex = -1;
tab.isSelected = false;
tab.onDeselect();
}
function next(tab, filterFn) {
return tabsList.next(tab || self.selected(), filterFn || isTabEnabled);
}
function previous(tab, filterFn) {
return tabsList.previous(tab || self.selected(), filterFn || isTabEnabled);
}
function isTabEnabled(tab) {
return tab && !tab.isDisabled();
}
/*
* attach a swipe listen
* if it's not selected, abort
* check the direction
* if it is right
* it pan right
* Now select
*/
function swipe(direction) {
if ( !self.selected() ) return;
// check the direction
switch(direction) {
case "swiperight": // if it is right
case "panright" : // it pan right
// Now do this...
self.select( self.previous() );
break;
case "swipeleft":
case "panleft" :
self.select( self.next() );
break;
}
}
}
angular.module('material.components.tabs')
/**
* @ngdoc directive
* @name materialTabs
* @module material.components.tabs
* @order 0
*
* @restrict E
*
* @description
* The `<material-tabs>` directive serves as the container for 1..n `<material-tab>` child directives to produces a Tabs components.
* In turn, the nested `<material-tab>` directive is used to specify a tab label for the **header button** and a [optional] tab view
* content that will be associated with each tab button.
*
* Below is the markup for its simplest usage:
*
* <hljs lang="html">
* <material-tabs>
* <material-tab label="Tab #1"></material-tab>
* <material-tab label="Tab #2"></material-tab>
* <material-tab label="Tab #3"></material-tab>
* <material-tabs>
* </hljs>
*
* Tabs supports three (3) usage scenarios:
*
* 1. Tabs (buttons only)
* 2. Tabs with internal view content
* 3. Tabs with external view content
*
* **Tab-only** support is useful when tab buttons are used for custom navigation regardless of any other components, content, or views.
* **Tabs with internal views** are the traditional usages where each tab has associated view content and the view switching is managed internally by the Tabs component.
* **Tabs with external view content** is often useful when content associated with each tab is independently managed and data-binding notifications announce tab selection changes.
*
* > As a performance bonus, if the tab content is managed internally then the non-active (non-visible) tab contents are temporarily disconnected from the `$scope.$digest()` processes; which restricts and optimizes DOM updates to only the currently active tab.
*
* Additional features also include:
*
* * Content can include any markup.
* * If a tab is disabled while active/selected, then the next tab will be auto-selected.
* * If the currently active tab is the last tab, then next() action will select the first tab.
* * Any markup (other than **`<material-tab>`** tags) will be transcluded into the tab header area BEFORE the tab buttons.
*
* @param {integer=} selected Index of the active/selected tab
* @param {boolean=} noink If present, disables ink ripple effects.
* @param {boolean=} nobar If present, disables the selection ink bar.
* @param {string=} align-tabs Attribute to indicate position of tab buttons: bottom or top; default is `top`
*
* @usage
* <hljs lang="html">
* <material-tabs selected="selectedIndex" >
* <img ng-src="/img/angular.png" class="centered">
*
* <material-tab
* ng-repeat="tab in tabs | orderBy:predicate:reversed"
* on-select="onTabSelected(tab)"
* on-deselect="announceDeselected(tab)"
* disabled="tab.disabled" >
*
* <material-tab-label>
* {{tab.title}}
* <img src="/img/removeTab.png"
* ng-click="removeTab(tab)"
* class="delete" >
* </material-tab-label>
*
* {{tab.content}}
*
* </material-tab>
*
* </material-tabs>
* </hljs>
*
*/
.directive('materialTabs', [
'$parse',
TabsDirective
]);
function TabsDirective($parse) {
return {
restrict: 'E',
controller: '$materialTabs',
require: 'materialTabs',
transclude: true,
scope: {
selectedIndex: '=?selected'
},
template:
'<section class="tabs-header" ' +
'ng-class="{\'tab-paginating\': pagination.active}">' +
'<div class="tab-paginator prev" ' +
'ng-if="pagination.active && pagination.hasPrev" ' +
'ng-click="pagination.clickPrevious()">' +
'</div>' +
// overflow: hidden container when paginating
'<div class="tabs-header-items-container" material-tabs-pagination>' +
// flex container for <material-tab> elements
'<div class="tabs-header-items" ng-transclude></div>' +
'<material-tabs-ink-bar></material-tabs-ink-bar>' +
'</div>' +
'<div class="tab-paginator next" ' +
'ng-if="pagination.active && pagination.hasNext" ' +
'ng-click="pagination.clickNext()">' +
'</div>' +
'</section>' +
'<section class="tabs-content"></section>',
link: postLink
};
function postLink(scope, element, attr, tabsCtrl) {
configureAria();
watchSelected();
function configureAria() {
element.attr({
role: 'tablist'
});
}
function watchSelected() {
scope.$watch('selectedIndex', function watchSelectedIndex(newIndex, oldIndex) {
// Note: if the user provides an invalid newIndex, all tabs will be deselected
// and the associated view will be hidden.
tabsCtrl.deselect( tabsCtrl.itemAt(oldIndex) );
if (tabsCtrl.inRange(newIndex)) {
var newTab = tabsCtrl.itemAt(newIndex);
// If the newTab is disabled, find an enabled one to go to.
if (newTab && newTab.isDisabled()) {
newTab = newIndex > oldIndex ?
tabsCtrl.next(newTab) :
tabsCtrl.previous(newTab);
}
tabsCtrl.select(newTab);
}
});
}
}
}
/**
* @ngdoc module
* @name material.components.toast
* @description
* Toast
*/
angular.module('material.components.toast', [
'material.services.interimElement',
'material.components.swipe'
])
.directive('materialToast', [
MaterialToastDirective
])
.factory('$materialToast', [
'$timeout',
'$$interimElement',
'$animate',
'$materialSwipe',
MaterialToastService
]);
function MaterialToastDirective() {
return {
restrict: 'E'
};
}
/**
* @ngdoc service
* @name $materialToast
* @module material.components.toast
*
* @description
*
* Used to open a toast notification on any position on the screen [with an optional
* duration], `$materialToast` is a service created by `$$interimElement` and provides a
* simple promise-based, behavior API:
*
* - `$materialToast.show()`
* - `$materialToast.hide()`
* - `$materialToast.cancel()`
*
* #### Notes:
*
* Only one toast notification may ever be active at any time. If a new toast is
* shown while a different toast is active, the old toast will be automatically
* hidden.
*
* @usage
* <hljs lang="html">
* <script type="text/javascript">
* var app = angular.module('app', ['ngMaterial']);
* app.controller('MyController', function($scope, $materialToast) {
* $scope.openToast = function($event) {
* $materialToast.show({
* template: '<material-toast>Hello!</material-toast>',
* hideDelay: 3000
* });
* };
* });
* </script>
*
* <div ng-controller="MyController">
* <material-button ng-click="openToast()">
* Open a Toast!
* </material-button>
* </div>
* </hljs>
*/
/**
* @ngdoc method
* @name $materialToast#show
*
* @description
* Show a toast dialog with the specified options.
*
* @paramType Options
* @param {string=} templateUrl The url of an html template file that will
* be used as the content of the toast. Restrictions: the template must
* have an outer `material-toast` element.
* @param {string=} template Same as templateUrl, except this is an actual
* template string.
* @param {number=} hideDelay How many milliseconds the toast should stay
* active before automatically closing. Set to 0 to disable duration.
* Default: 3000.
* @param {string=} position Where to place the toast. Available: any combination
* of 'bottom', 'left', 'top', 'right', 'fit'. Default: 'bottom left'.
* @param {string=} controller The controller to associate with this toast.
* @param {string=} locals An object containing key/value pairs. The keys will
* be used as names of values to inject into the controller. For example,
* `locals: {three: 3}` would inject `three` into the controller with the value
* of 3.
* @param {object=} resolve Similar to locals, except it takes promises as values
* and the toast will not open until the promises resolve.
* @param {string=} controllerAs An alias to assign the controller to on the scope.
* @param {element=} parent The element to append the bottomSheet to. Defaults to appending
* to the root element of the application.
*
* @returns {Promise} Returns a promise that will be resolved or rejected when
* `$materialToast.hide()` or `$materialToast.cancel()` is called respectively.
*/
/**
* @ngdoc method
* @name $materialToast#hide
*
* @description
* Hide an existing toast and `resolve` the promise returned from `$materialToast.show()`.
*
* @param {*} arg An argument to resolve the promise with.
*
*/
/**
* @ngdoc method
* @name $materialToast#cancel
*
* @description
* Hide an existing toast and `reject` the promise returned from `$materialToast.show()`.
*
* @param {*} arg An argument to reject the promise with.
*
*/
function MaterialToastService($timeout, $$interimElement, $animate, $materialSwipe) {
var factoryDef = {
onShow: onShow,
onRemove: onRemove,
position: 'bottom left',
hideDelay: 3000,
};
var $materialToast = $$interimElement(factoryDef);
return $materialToast;
function onShow(scope, element, options) {
element.addClass(options.position);
options.parent.addClass(toastOpenClass(options.position));
var configureSwipe = $materialSwipe(scope, 'swipeleft swiperight');
options.detachSwipe = configureSwipe(element, function(ev) {
//Add swipeleft/swiperight class to element so it can animate correctly
element.addClass(ev.type);
$timeout($materialToast.hide);
});
return $animate.enter(element, options.parent);
}
function onRemove(scope, element, options) {
options.detachSwipe();
options.parent.removeClass(toastOpenClass(options.position));
return $animate.leave(element);
}
function toastOpenClass(position) {
return 'material-toast-open-' +
(position.indexOf('top') > -1 ? 'top' : 'bottom');
}
}
/**
* @ngdoc module
* @name material.components.toolbar
*/
angular.module('material.components.toolbar', [
'material.components.content',
'material.animations'
])
.directive('materialToolbar', [
'$$rAF',
'$materialEffects',
materialToolbarDirective
]);
/**
* @ngdoc directive
* @name materialToolbar
* @restrict E
* @description
* `material-toolbar` is used to place a toolbar in your app.
*
* Toolbars are usually used above a content area to display the title of the
* current page, and show relevant action buttons for that page.
*
* You can change the height of the toolbar by adding either the
* `material-medium-tall` or `material-tall` class to the toolbar.
*
* @usage
* <hljs lang="html">
* <div layout="vertical" layout-fill>
* <material-toolbar>
*
* <div class="material-toolbar-tools">
* <span>My App's Title</span>
*
* <!-- fill up the space between left and right area -->
* <span flex></span>
*
* <material-button>
* Right Bar Button
* </material-button>
* </div>
*
* </material-toolbar>
* <material-content>
* Hello!
* </material-content>
* </div>
* </hljs>
*
* @param {boolean=} scrollShrink Whether the header should shrink away as
* the user scrolls down, and reveal itself as the user scrolls up.
* Note: for scrollShrink to work, the toolbar must be a sibling of a
* `material-content` element, placed before it. See the scroll shrink demo.
*
*
* @param {number=} shrinkSpeedFactor How much to change the speed of the toolbar's
* shrinking by. For example, if 0.25 is given then the toolbar will shrink
* at one fourth the rate at which the user scrolls down. Default 0.5.
*/
function materialToolbarDirective($$rAF, $materialEffects) {
return {
restrict: 'E',
controller: angular.noop,
link: function(scope, element, attr) {
if (angular.isDefined(attr.scrollShrink)) {
setupScrollShrink();
}
function setupScrollShrink() {
// Current "y" position of scroll
var y = 0;
// Store the last scroll top position
var prevScrollTop = 0;
var shrinkSpeedFactor = attr.shrinkSpeedFactor || 0.5;
var toolbarHeight;
var contentElement;
var debouncedContentScroll = $$rAF.debounce(onContentScroll);
var debouncedUpdateHeight = Util.debounce(updateToolbarHeight, 5 * 1000);
// Wait for $materialContentLoaded event from materialContent directive.
// If the materialContent element is a sibling of our toolbar, hook it up
// to scroll events.
scope.$on('$materialContentLoaded', onMaterialContentLoad);
function onMaterialContentLoad($event, newContentEl) {
if (Util.elementIsSibling(element, newContentEl)) {
// unhook old content event listener if exists
if (contentElement) {
contentElement.off('scroll', debouncedContentScroll);
}
newContentEl.on('scroll', debouncedContentScroll);
newContentEl.attr('scroll-shrink', 'true');
contentElement = newContentEl;
$$rAF(updateToolbarHeight);
}
}
function updateToolbarHeight() {
toolbarHeight = element.prop('offsetHeight');
// Add a negative margin-top the size of the toolbar to the content el.
// The content will start transformed down the toolbarHeight amount,
// so everything looks normal.
//
// As the user scrolls down, the content will be transformed up slowly
// to put the content underneath where the toolbar was.
contentElement.css(
'margin-top',
(-toolbarHeight * shrinkSpeedFactor) + 'px'
);
onContentScroll();
}
function onContentScroll(e) {
var scrollTop = e ? e.target.scrollTop : prevScrollTop;
debouncedUpdateHeight();
y = Math.min(
toolbarHeight / shrinkSpeedFactor,
Math.max(0, y + scrollTop - prevScrollTop)
);
element.css(
$materialEffects.TRANSFORM,
'translate3d(0,' + (-y * shrinkSpeedFactor) + 'px,0)'
);
contentElement.css(
$materialEffects.TRANSFORM,
'translate3d(0,' + ((toolbarHeight - y) * shrinkSpeedFactor) + 'px,0)'
);
prevScrollTop = scrollTop;
}
}
}
};
}
/**
* @ngdoc module
* @name material.components.tooltip
*/
angular.module('material.components.tooltip', [])
.directive('materialTooltip', [
'$timeout',
'$window',
'$$rAF',
'$document',
MaterialTooltipDirective
]);
/**
* @ngdoc directive
* @name materialTooltip
* @module material.components.tooltip
* @description
* Tooltips are used to describe elements that are interactive and primarily graphical (not textual).
*
* Place a `<material-tooltip>` as a child of the element it describes.
*
* A tooltip will activate when the user focuses, hovers over, or touches the parent.
*
* @usage
* <hljs lang="html">
* <material-icon icon="/img/icons/ic_play_arrow_24px.svg">
* <material-tooltip>
* Play Music
* </material-tooltip>
* </material-icon>
* </hljs>
*
* @param {expression=} visible Boolean bound to whether the tooltip is
* currently visible.
*/
function MaterialTooltipDirective($timeout, $window, $$rAF, $document) {
var TOOLTIP_SHOW_DELAY = 400;
var TOOLTIP_WINDOW_EDGE_SPACE = 8;
// We have to append tooltips to the body, because we use
// getBoundingClientRect().
// to find where to append the tooltip.
var tooltipParent = angular.element(document.body);
return {
restrict: 'E',
transclude: true,
require: '^?materialContent',
template:
'<div class="tooltip-background"></div>' +
'<div class="tooltip-content" ng-transclude></div>',
scope: {
visible: '=?'
},
link: postLink
};
function postLink(scope, element, attr, contentCtrl) {
var parent = element.parent();
// We will re-attach tooltip when visible
element.detach();
element.attr('role', 'tooltip');
element.attr('id', attr.id || ('tooltip_' + Util.nextUid()));
parent.on('focus mouseenter touchstart', function() {
setVisible(true);
});
parent.on('blur mouseleave touchend touchcancel', function() {
// Don't hide the tooltip if the parent is still focused.
if (document.activeElement === parent[0]) return;
setVisible(false);
});
scope.$watch('visible', function(isVisible) {
if (isVisible) showTooltip();
else hideTooltip();
});
var debouncedOnResize = $$rAF.debounce(onWindowResize);
angular.element($window).on('resize', debouncedOnResize);
function onWindowResize() {
// Reposition on resize
if (scope.visible) positionTooltip();
}
// Be sure to completely cleanup the element on destroy
scope.$on('$destroy', function() {
scope.visible = false;
element.remove();
angular.element($window).off('resize', debouncedOnResize);
});
// *******
// Methods
// *******
// If setting visible to true, debounce to TOOLTIP_SHOW_DELAY ms
// If setting visible to false and no timeout is active, instantly hide the tooltip.
function setVisible(value) {
setVisible.value = !!value;
if (!setVisible.queued) {
if (value) {
setVisible.queued = true;
$timeout(function() {
scope.visible = setVisible.value;
setVisible.queued = false;
}, TOOLTIP_SHOW_DELAY);
} else {
$timeout(function() { scope.visible = false; });
}
}
}
function showTooltip() {
// Insert the element before positioning it, so we can get position
// (tooltip is hidden by default)
element.removeClass('tooltip-hide');
parent.attr('aria-describedby', element.attr('id'));
tooltipParent.append(element);
// Wait until the element has been in the dom for two frames before
// fading it in.
// Additionally, we position the tooltip twice to avoid positioning bugs
//positionTooltip();
$$rAF(function() {
$$rAF(function() {
positionTooltip();
if (!scope.visible) return;
element.addClass('tooltip-show');
});
});
}
function hideTooltip() {
element.removeClass('tooltip-show').addClass('tooltip-hide');
parent.removeAttr('aria-describedby');
$timeout(function() {
if (scope.visible) return;
element.detach();
}, 200, false);
}
function positionTooltip(rerun) {
var tipRect = element[0].getBoundingClientRect();
var parentRect = parent[0].getBoundingClientRect();
if (contentCtrl) {
parentRect.top += contentCtrl.$element.prop('scrollTop');
parentRect.left += contentCtrl.$element.prop('scrollLeft');
}
// Default to bottom position if possible
var tipDirection = 'bottom';
var newPosition = {
left: parentRect.left + parentRect.width / 2 - tipRect.width / 2,
top: parentRect.top + parentRect.height
};
// If element bleeds over left/right of the window, place it on the edge of the window.
newPosition.left = Math.min(
newPosition.left,
$window.innerWidth - tipRect.width - TOOLTIP_WINDOW_EDGE_SPACE
);
newPosition.left = Math.max(newPosition.left, TOOLTIP_WINDOW_EDGE_SPACE);
// If element bleeds over the bottom of the window, place it above the parent.
if (newPosition.top + tipRect.height > $window.innerHeight) {
newPosition.top = parentRect.top - tipRect.height;
tipDirection = 'top';
}
element.css({top: newPosition.top + 'px', left: newPosition.left + 'px'});
// Tell the CSS the size of this tooltip, as a multiple of 32.
element.attr('width-32', Math.ceil(tipRect.width / 32));
element.attr('tooltip-direction', tipDirection);
}
}
}
angular.module('material.components.whiteframe', []);
/**
* @ngdoc module
* @name material.components.divider
* @description Divider module!
*/
angular.module('material.components.divider', [
'material.animations',
'material.services.aria'
])
.directive('materialDivider', MaterialDividerDirective);
function MaterialDividerController(){}
/**
* @ngdoc directive
* @name materialDivider
* @module material.components.divider
* @restrict E
*
* @description
* Dividers group and separate content within lists and page layouts using strong visual and spatial distinctions. This divider is a thin rule, lightweight enough to not distract the user from content.
*
* @param {boolean=} inset Add this attribute to activate the inset divider style.
* @usage
* <hljs lang="html">
* <material-divider></material-divider>
*
* <material-divider inset></material-divider>
* </hljs>
*
*/
function MaterialDividerDirective() {
return {
restrict: 'E',
controller: [MaterialDividerController]
};
}
/**
* @ngdoc module
* @name material.components.linearProgress
* @description Linear Progress module!
*/
angular.module('material.components.linearProgress', [
'material.animations',
'material.services.aria'
])
.directive('materialLinearProgress', [
'$$rAF',
'$materialEffects',
MaterialLinearProgressDirective
]);
/**
* @ngdoc directive
* @name materialLinearProgress
* @module material.components.linearProgress
* @restrict E
*
* @description
* The linear progress directive is used to make loading content in your app as delightful and painless as possible by minimizing the amount of visual change a user sees before they can view and interact with content. Each operation should only be represented by one activity indicator—for example, one refresh operation should not display both a refresh bar and an activity circle.
*
* For operations where the percentage of the operation completed can be determined, use a determinate indicator. They give users a quick sense of how long an operation will take.
*
* For operations where the user is asked to wait a moment while something finishes up, and it’s not necessary to expose what's happening behind the scenes and how long it will take, use an indeterminate indicator.
*
* @param {string} mode Select from one of four modes: determinate, indeterminate, buffer or query.
* @param {number=} value In determinate and buffer modes, this number represents the percentage of the primary progress bar. Default: 0
* @param {number=} secondaryValue In the buffer mode, this number represents the precentage of the secondary progress bar. Default: 0
*
* @usage
* <hljs lang="html">
* <material-linear-progress mode="determinate" value="..."></material-linear-progress>
*
* <material-linear-progress mode="determinate" ng-value="..."></material-linear-progress>
*
* <material-linear-progress mode="indeterminate"></material-linear-progress>
*
* <material-linear-progress mode="buffer" value="..." secondaryValue="..."></material-linear-progress>
*
* <material-linear-progress mode="query"></material-linear-progress>
* </hljs>
*/
function MaterialLinearProgressDirective($$rAF, $materialEffects) {
return {
restrict: 'E',
template: '<div class="container">' +
'<div class="dashed"></div>' +
'<div class="bar bar1"></div>' +
'<div class="bar bar2"></div>' +
'</div>',
compile: compile
};
function compile(tElement, tAttrs, transclude) {
tElement.attr('aria-valuemin', 0);
tElement.attr('aria-valuemax', 100);
tElement.attr('role', 'progressbar');
return postLink;
}
function postLink(scope, element, attr) {
var bar1Style = element[0].querySelector('.bar1').style,
bar2Style = element[0].querySelector('.bar2').style,
container = angular.element(element[0].querySelector('.container'));
attr.$observe('value', function(value) {
if (attr.mode == 'query') {
return;
}
var clamped = clamp(value);
element.attr('aria-valuenow', clamped);
bar2Style[$materialEffects.TRANSFORM] = linearProgressTransforms[clamped];
});
attr.$observe('secondaryvalue', function(value) {
bar1Style[$materialEffects.TRANSFORM] = linearProgressTransforms[clamp(value)];
});
$$rAF(function() {
container.addClass('ready');
});
}
function clamp(value) {
if (value > 100) {
return 100;
}
if (value < 0) {
return 0;
}
return Math.ceil(value || 0);
}
}
// **********************************************************
// Private Methods
// **********************************************************
var linearProgressTransforms = (function() {
var values = new Array(101);
for(var i = 0; i < 101; i++){
values[i] = makeTransform(i);
}
return values;
function makeTransform(value){
var scale = value/100;
var translateX = (value-100)/2;
return 'translateX(' + translateX.toString() + '%) scale(' + scale.toString() + ', 1)';
}
})();
/**
* @ngdoc module
* @name material.components.circularProgress
* @description Circular Progress module!
*/
angular.module('material.components.circularProgress', [
'material.animations',
'material.services.aria'
])
.directive('materialCircularProgress', [
'$$rAF',
'$materialEffects',
MaterialCircularProgressDirective
]);
/**
* @ngdoc directive
* @name materialCircularProgress
* @module material.components.circularProgress
* @restrict E
*
* @description
* The circular progress directive is used to make loading content in your app as delightful and painless as possible by minimizing the amount of visual change a user sees before they can view and interact with content.
*
* For operations where the percentage of the operation completed can be determined, use a determinate indicator. They give users a quick sense of how long an operation will take.
*
* For operations where the user is asked to wait a moment while something finishes up, and it’s not necessary to expose what's happening behind the scenes and how long it will take, use an indeterminate indicator.
*
* @param {string} mode Select from one of two modes: determinate and indeterminate.
* @param {number=} value In determinate mode, this number represents the percentage of the circular progress. Default: 0
* @param {number=} diameter This specifies the diamter of the circular progress. Default: 48
*
* @usage
* <hljs lang="html">
* <material-circular-progress mode="determinate" value="..."></material-circular-progress>
*
* <material-circular-progress mode="determinate" ng-value="..."></material-circular-progress>
*
* <material-circular-progress mode="determinate" value="..." diameter="100"></material-circular-progress>
*
* <material-circular-progress mode="indeterminate"></material-circular-progress>
* </hljs>
*/
function MaterialCircularProgressDirective($$rAF, $materialEffects) {
var fillRotations = new Array(101),
fixRotations = new Array(101);
for (var i = 0; i < 101; i++) {
var percent = i / 100;
var rotation = Math.floor(percent * 180);
fillRotations[i] = 'rotate(' + rotation.toString() + 'deg)';
fixRotations[i] = 'rotate(' + (rotation * 2).toString() + 'deg)';
}
return {
restrict: 'E',
template:
'<div class="wrapper1"><div class="wrapper2"><div class="circle">' +
'<div class="mask full">' +
'<div class="fill"></div>' +
'</div>' +
'<div class="mask half">' +
'<div class="fill"></div>' +
'<div class="fill fix"></div>' +
'</div>' +
'<div class="shadow"></div>' +
'</div>' +
'<div class="inset"></div></div></div>',
compile: compile
};
function compile(tElement, tAttrs, transclude) {
tElement.attr('aria-valuemin', 0);
tElement.attr('aria-valuemax', 100);
tElement.attr('role', 'progressbar');
return postLink;
}
function postLink(scope, element, attr) {
var circle = element[0],
fill = circle.querySelectorAll('.fill, .mask.full'),
fix = circle.querySelectorAll('.fill.fix'),
i, clamped, fillRotation, fixRotation;
var diameter = attr.diameter || 48;
var scale = diameter/48;
circle.style[$materialEffects.TRANSFORM] = 'scale(' + scale.toString() + ')';
attr.$observe('value', function(value) {
clamped = clamp(value);
fillRotation = fillRotations[clamped];
fixRotation = fixRotations[clamped];
element.attr('aria-valuenow', clamped);
for (i = 0; i < fill.length; i++) {
fill[i].style[$materialEffects.TRANSFORM] = fillRotation;
}
for (i = 0; i < fix.length; i++) {
fix[i].style[$materialEffects.TRANSFORM] = fixRotation;
}
});
}
function clamp(value) {
if (value > 100) {
return 100;
}
if (value < 0) {
return 0;
}
return Math.ceil(value || 0);
}
}
(function() {
/**
* @ngdoc module
* @name material.components.swipe
* @description Swipe module!
*/
angular.module('material.components.swipe',['ng'])
/**
* @ngdoc directive
* @module material.components.swipe
* @name $materialSwipe
*
* This service allows directives to easily attach swipe and pan listeners to
* the specified element.
*
* @private
*/
.factory("$materialSwipe", function() {
// match expected API functionality
var attachNoop = function(){ return angular.noop; };
/**
* SwipeService constructor pre-captures scope and customized event types
*
* @param scope
* @param eventTypes
* @returns {*}
* @constructor
*/
return function SwipeService(scope, eventTypes) {
if ( !eventTypes ) eventTypes = "swipeleft swiperight";
// publish configureFor() method for specific element instance
return function configureFor(element, onSwipeCallback, attachLater ) {
var hammertime = new Hammer(element[0], {
recognizers : addRecognizers([], eventTypes )
});
// Attach swipe listeners now
if ( !attachLater ) attachSwipe();
// auto-disconnect during destroy
scope.$on('$destroy', function() {
hammertime.destroy();
});
return attachSwipe;
// **********************
// Internal methods
// **********************
/**
* Delegate swipe event to callback function
* and ensure $digest is triggered.
*
* @param ev HammerEvent
*/
function swipeHandler(ev) {
// Prevent triggering parent hammer listeners
ev.srcEvent.stopPropagation();
if ( angular.isFunction(onSwipeCallback) ) {
scope.$apply(function() {
onSwipeCallback(ev);
});
}
}
/**
* Enable listeners and return detach() fn
*/
function attachSwipe() {
hammertime.on(eventTypes, swipeHandler );
return function detachSwipe() {
hammertime.off( eventTypes );
};
}
/**
* Add optional recognizers such as panleft, panright
*/
function addRecognizers(list, events) {
var hasPanning = (events.indexOf("pan") > -1);
var hasSwipe = (events.indexOf("swipe") > -1);
if (hasPanning) {
list.push([ Hammer.Pan, { direction: Hammer.DIRECTION_HORIZONTAL } ]);
}
if (hasSwipe) {
list.push([ Hammer.Swipe, { direction: Hammer.DIRECTION_HORIZONTAL } ]);
}
return list;
}
};
};
})
/**
* @ngdoc directive
* @module material.components.swipe
* @name materialSwipeLeft
*
* @order 0
* @restrict A
*
* @description
* The `<div material-swipe-left="<expression" >` directive identifies an element on which
* HammerJS horizontal swipe left and pan left support will be active. The swipe/pan action
* can result in custom activity trigger by evaluating `<expression>`.
*
* @param {boolean=} noPan Use of attribute indicates flag to disable detection of `panleft` activity
*
* @usage
* <hljs lang="html">
*
* <div class="animate-switch-container"
* ng-switch on="data.selectedIndex"
* material-swipe-left="data.selectedIndex+=1;"
* material-swipe-right="data.selectedIndex-=1;" >
*
* </div>
* </hljs>
*
*/
.directive("materialSwipeLeft", ['$parse', '$materialSwipe',
function MaterialSwipeLeft($parse, $materialSwipe) {
return {
restrict: 'A',
link : swipePostLink( $parse, $materialSwipe, "SwipeLeft" )
};
}])
/**
* @ngdoc directive
* @module material.components.swipe
* @name materialSwipeRight
*
* @order 1
* @restrict A
*
* @description
* The `<div material-swipe-right="<expression" >` directive identifies functionality
* that attaches HammerJS horizontal swipe right and pan right support to an element. The swipe/pan action
* can result in activity trigger by evaluating `<expression>`
*
* @param {boolean=} noPan Use of attribute indicates flag to disable detection of `panright` activity
*
* @usage
* <hljs lang="html">
*
* <div class="animate-switch-container"
* ng-switch on="data.selectedIndex"
* material-swipe-left="data.selectedIndex+=1;"
* material-swipe-right="data.selectedIndex-=1;" >
*
* </div>
* </hljs>
*
*/
.directive( "materialSwipeRight", ['$parse', '$materialSwipe',
function MaterialSwipeRight($parse, $materialSwipe) {
return {
restrict: 'A',
link: swipePostLink( $parse, $materialSwipe, "SwipeRight" )
};
}
]);
/**
* Factory to build PostLink function specific to Swipe or Pan direction
*
* @param $parse
* @param $materialSwipe
* @param name
* @returns {Function}
*/
function swipePostLink($parse, $materialSwipe, name ) {
return function(scope, element, attrs) {
var direction = name.toLowerCase();
var directiveName= "material" + name;
var parentGetter = $parse(attrs[directiveName]) || angular.noop;
var configureSwipe = $materialSwipe(scope, direction);
var requestSwipe = function(locals) {
// build function to request scope-specific swipe response
parentGetter(scope, locals);
};
configureSwipe( element, function onHandleSwipe(ev) {
if ( ev.type == direction ) {
requestSwipe();
}
});
}
}
})();
angular.module('material.decorators', [])
.config(['$provide', function($provide) {
$provide.decorator('$$rAF', ['$delegate', '$rootScope', rAFDecorator]);
function rAFDecorator($$rAF, $rootScope) {
/**
* Use this to debounce events that come in often.
* The debounced 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-debounced 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
*/
$$rAF.debounce = function(cb) {
var queueArgs, alreadyQueued, queueCb, context;
return function debounced() {
queueArgs = arguments;
context = this;
queueCb = cb;
if (!alreadyQueued) {
alreadyQueued = true;
$$rAF(function() {
queueCb.apply(context, queueArgs);
alreadyQueued = false;
});
}
};
};
return $$rAF;
}
}]);
angular.module('material.services.aria', [])
.service('$materialAria', [
'$log',
AriaService
]);
function AriaService($log) {
var messageTemplate = 'ARIA: Attribute "%s", required for accessibility, is missing on "%s"!';
var defaultValueTemplate = 'Default value was set: %s="%s".';
return {
expect : expectAttribute,
};
/**
* Check if expected ARIA has been specified on the target element
* @param element
* @param attrName
* @param defaultValue
*/
function expectAttribute(element, attrName, defaultValue) {
var node = element[0];
if (!node.hasAttribute(attrName)) {
var hasDefault = angular.isDefined(defaultValue);
if (hasDefault) {
defaultValue = String(defaultValue).trim();
// $log.warn(messageTemplate + ' ' + defaultValueTemplate,
// attrName, getTagString(node), attrName, defaultValue);
element.attr(attrName, defaultValue);
} else {
// $log.warn(messageTemplate, attrName, getTagString(node));
}
}
}
/**
* Gets the tag definition from a node's outerHTML
* @example getTagString(
* '<material-button foo="bar">Hello</material-button>'
* ) // => '<material-button foo="bar">'
*/
function getTagString(node) {
var html = node.outerHTML;
var closingIndex = html.indexOf('>');
return html.substring(0, closingIndex + 1);
}
}
angular.module('material.services.attrBind', [
])
.factory('$attrBind', [
'$parse',
'$interpolate',
MaterialAttrBind
]);
/**
* This service allows directives to easily databind attributes to private scope properties.
*
* @private
*/
function MaterialAttrBind($parse, $interpolate) {
var LOCAL_REGEXP = /^\s*([@=&])(\??)\s*(\w*)\s*$/;
return function (scope, attrs, bindDefinition, bindDefaults) {
angular.forEach(bindDefinition || {}, function (definition, scopeName) {
//Adapted from angular.js $compile
var match = definition.match(LOCAL_REGEXP) || [],
attrName = match[3] || scopeName,
mode = match[1], // @, =, or &
parentGet,
unWatchFn;
switch (mode) {
case '@': // One-way binding from attribute into scope
attrs.$observe(attrName, function (value) {
scope[scopeName] = value;
});
attrs.$$observers[attrName].$$scope = scope;
if (!bypassWithDefaults(attrName, scopeName)) {
// we trigger an interpolation to ensure
// the value is there for use immediately
scope[scopeName] = $interpolate(attrs[attrName])(scope);
}
break;
case '=': // Two-way binding...
if (!bypassWithDefaults(attrName, scopeName)) {
// Immediate evaluation
scope[scopeName] = (attrs[attrName] === "") ? true : scope.$eval(attrs[attrName]);
// Data-bind attribute to scope (incoming) and
// auto-release watcher when scope is destroyed
unWatchFn = scope.$watch(attrs[attrName], function (value) {
scope[scopeName] = value;
});
scope.$on('$destroy', unWatchFn);
}
break;
case '&': // execute an attribute-defined expression in the context of the parent scope
if (!bypassWithDefaults(attrName, scopeName, angular.noop)) {
/* jshint -W044 */
if (attrs[attrName] && attrs[attrName].match(RegExp(scopeName + '\(.*?\)'))) {
throw new Error('& expression binding "' + scopeName + '" looks like it will recursively call "' +
attrs[attrName] + '" and cause a stack overflow! Please choose a different scopeName.');
}
parentGet = $parse(attrs[attrName]);
scope[scopeName] = function (locals) {
return parentGet(scope, locals);
};
}
break;
}
});
/**
* Optional fallback value if attribute is not specified on element
* @param scopeName
*/
function bypassWithDefaults(attrName, scopeName, defaultVal) {
if (!angular.isDefined(attrs[attrName])) {
var hasDefault = bindDefaults && bindDefaults.hasOwnProperty(scopeName);
scope[scopeName] = hasDefault ? bindDefaults[scopeName] : defaultVal;
return true;
}
return false;
}
};
}
angular.module('material.services.compiler', [
])
.service('$materialCompiler', [
'$q',
'$http',
'$injector',
'$compile',
'$controller',
'$templateCache',
materialCompilerService
]);
function materialCompilerService($q, $http, $injector, $compile, $controller, $templateCache) {
/**
* @ngdoc service
* @name $materialCompiler
* @module material.services.compiler
*
* @description
* The $materialCompiler service is an abstraction of angular's compiler, that allows the developer
* to easily compile an element with a templateUrl, controller, and locals.
*/
/**
* @ngdoc method
* @name $materialCompiler#compile
* @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 {@link angular.Module#controller 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=}` – html template as a string or a function that
* returns an html template as a string which should be used by {@link
* ngRoute.directive:ngView ngView} or {@link ng.directive:ngInclude ngInclude} directives.
* This property takes precedence over `templateUrl`.
*
* - `templateUrl` – `{string=}` – path or function that returns a path to an html
* template that should be used by {@link ngRoute.directive:ngView ngView}.
*
* - `transformTemplate` – `{function=} – a function which can be used to transform
* the templateUrl or template provided after it is fetched. It will be given one
* parameter, the template, and should return a 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 one to be rejected before the controller is
* instantiated.
*
* - `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 {@link api/AUTO.$injector#invoke 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 promsie which will be resolved with a `compileData` object,
* with the following properties:
*
* - `{element}` – `element` – an uncompiled angular element compiled using the provided template.
*
* - `{function(scope)}` – `link` – A link function, which, when called, will compile
* the elmeent and instantiate options.controller.
*
* - `{object}` – `locals` – The locals which will be passed into the controller once `link` is
* called.
*
* @usage
* $materialCompiler.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
* });
*/
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;
// 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 = angular.element('<div>').html(template).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 ctrl = $controller(controller, locals);
//See angular-route source for this logic
element.data('$ngControllerController', ctrl);
element.children().data('$ngControllerController', ctrl);
if (controllerAs) {
scope[controllerAs] = ctrl;
}
}
return linkFn(scope);
}
};
});
};
}
/**
* @ngdoc module
* @name material.services.interimElement
* @description InterimElement
*/
angular.module('material.services.interimElement', [
'material.services.compiler'
])
.factory('$$interimElement', [
'$q',
'$rootScope',
'$timeout',
'$rootElement',
'$animate',
'$materialCompiler',
InterimElementFactory
]);
/**
* @ngdoc service
* @name $$interimElement
*
* @description
*
* Factory that contructs `$$interimElement.$service` services.
* Used internally in material for elements that appear on screen temporarily.
* The service provides a promise-like API for interacting with the temporary
* elements.
*
* ```js
* app.service('$materialToast', function($$interimElement) {
* var $materialToast = $$interimElement(toastDefaultOptions);
* return $materialToast;
* });
* ```
* @param {object=} defaultOptions Options used by default for the `show` method on the service.
*
* @returns {$$interimElement.$service}
*
*/
function InterimElementFactory($q, $rootScope, $timeout, $rootElement, $animate, $materialCompiler) {
return function createInterimElementService(defaults) {
/**
* @ngdoc service
* @name $$interimElement.$service
*
* @description
* A service used to control inserting and removing an element into the DOM.
*
*/
var stack = [];
var parent = $rootElement.find('body');
if (!parent.length) parent = $rootElement;
defaults = angular.extend({
parent: parent,
onShow: function(scope, $el, options) {
return $animate.enter($el, options.parent);
},
onRemove: function(scope, $el, options) {
return $animate.leave($el);
},
}, defaults || {});
var service;
return service = {
show: show,
hide: hide,
cancel: cancel
};
/**
* @ngdoc method
* @name $$interimElement.$service#show
* @kind function
*
* @description
* Compiles and inserts an element into the DOM.
*
* @param {Object} options Options object to compile with.
*
* @returns {Promise} Promise that will resolve when the service
* has `#close()` or `#cancel()` called.
*
*/
function show(options) {
if (stack.length) {
service.hide();
}
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 undefined data that resolves after the element has been removed.
*
*/
function hide(success) {
var interimElement = stack.shift();
interimElement && interimElement.remove().then(function() {
interimElement.deferred.resolve(success);
});
}
/**
* @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 undefined
*
*/
function cancel(reason) {
var interimElement = stack.shift();
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;
options = options || {};
options = angular.extend({
scope: options.scope || $rootScope.$new(options.isolateScope)
}, defaults, options);
self = {
options: options,
deferred: $q.defer(),
show: function() {
return $materialCompiler.compile(options).then(function(compiledData) {
element = compiledData.link(options.scope);
var ret = options.onShow(options.scope, element, options);
return $q.when(ret)
.then(startHideTimeout);
function startHideTimeout() {
if (options.hideDelay) {
hideTimeout = $timeout(service.hide, options.hideDelay) ;
}
}
});
},
cancelTimeout: function() {
if (hideTimeout) {
$timeout.cancel(hideTimeout);
hideTimeout = undefined;
}
},
remove: function() {
self.cancelTimeout();
var ret = options.onRemove(options.scope, element, options);
return $q.when(ret).then(function() {
options.scope.$destroy();
});
}
};
return self;
}
};
}
/**
* @ngdoc overview
* @name material.services.registry
*
* @description
* A component registry system for accessing various component instances in an app.
*/
angular.module('material.services.registry', [
])
.factory('$materialComponentRegistry', [
'$log',
materialComponentRegistry
]);
/**
* @ngdoc service
* @name material.services.registry.service:$materialComponentRegistry
*
* @description
* $materialComponentRegistry enables the user to interact with multiple instances of
* certain complex components in a running app.
*/
function materialComponentRegistry($log) {
var instances = [];
return {
/**
* 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) {
var i, j, instance;
for(i = 0, j = instances.length; i < j; i++) {
instance = instances[i];
if(instance.$$materialHandle === 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) {
instance.$$materialHandle = handle;
instances.push(instance);
return function deregister() {
var index = instances.indexOf(instance);
if (index !== -1) {
instances.splice(index, 1);
}
};
}
}
}
})();
/*!
* Angular Material Design
* https://github.com/angular/material
* @license MIT
* v0.4.0
*/
@font-face {
font-family: 'RobotoDraft';
font-style: normal;
font-weight: 100;
src: local('RobotoDraft Thin'), local('RobotoDraft-Thin'), url(//fonts.gstatic.com/s/robotodraft/v1/hope9NW9iJ5hh8P5PM_EAyeJLMOzE6CCkidNEpZOseY.woff) format('woff'); }
@font-face {
font-family: 'RobotoDraft';
font-style: normal;
font-weight: 300;
src: local('RobotoDraft Light'), local('RobotoDraft-Light'), url(//fonts.gstatic.com/s/robotodraft/v1/u0_CMoUf3y3-4Ss4ci-VwR_xHqYgAV9Bl_ZQbYUxnQU.woff) format('woff'); }
@font-face {
font-family: 'RobotoDraft';
font-style: normal;
font-weight: 400;
src: local('RobotoDraft'), local('RobotoDraft-Regular'), url(//fonts.gstatic.com/s/robotodraft/v1/0xES5Sl_v6oyT7dAKuoni4bN6UDyHWBl620a-IRfuBk.woff) format('woff'); }
@font-face {
font-family: 'RobotoDraft';
font-style: normal;
font-weight: 500;
src: local('RobotoDraft Medium'), local('RobotoDraft-Medium'), url(//fonts.gstatic.com/s/robotodraft/v1/u0_CMoUf3y3-4Ss4ci-VwSqHEX2q--o2so14pIEl08w.woff) format('woff'); }
@font-face {
font-family: 'RobotoDraft';
font-style: normal;
font-weight: 700;
src: local('RobotoDraft Bold'), local('RobotoDraft-Bold'), url(//fonts.gstatic.com/s/robotodraft/v1/u0_CMoUf3y3-4Ss4ci-VwTqR_3kx9_hJXbbyU8S6IN0.woff) format('woff'); }
@font-face {
font-family: 'RobotoDraft';
font-style: normal;
font-weight: 900;
src: local('RobotoDraft Black'), local('RobotoDraft-Black'), url(//fonts.gstatic.com/s/robotodraft/v1/u0_CMoUf3y3-4Ss4ci-VwUExzZ44ka2Lr5i-x5aWr0E.woff) format('woff'); }
@font-face {
font-family: 'RobotoDraft';
font-style: italic;
font-weight: 400;
src: local('RobotoDraft Italic'), local('RobotoDraft-Italic'), url(//fonts.gstatic.com/s/robotodraft/v1/er-TIW55l9KWsTS1x9bTftkZXW4sYc4BjuAIFc1SXII.woff) format('woff'); }
@font-face {
font-family: 'RobotoDraft';
font-style: italic;
font-weight: 700;
src: local('RobotoDraft Bold Italic'), local('RobotoDraft-BoldItalic'), url(//fonts.gstatic.com/s/robotodraft/v1/5SAvdU0uYYlH8OURAykt5wRV2F9RPTaqyJ4QibDfkzM.woff) format('woff'); }
/*
@mixin material-theme($name, $t100, $t300, $t500, $t700, $t900) {
}
@include material-theme('google-red', #f3c6C2, #e57b72, #da4336, #c43828, #a42613);
@include material-theme('pink', #f7bacf, #ef6191, #e81d62, #c1175a, #870d4e);
@include material-theme('indigo', #c4c9e8, #7885ca, #3e50b4, #2f3e9e, #19227d);
@include material-theme('google-blue', #c5d9fb, #7aa9f6, #4184f3, #3266d5, #1b39ab);
@include material-theme('light-blue', #b2e4fb, #4ec2f6, #02a8f3, #0187d0, #00569A);
@include material-theme('teal', #b1deda, #4cb5ab, #009587, #00786a, #004c3f);
@include material-theme('google-green', #b6e0cc, #56ba89, #0E9C57, #0a7f42, #045423);
@include material-theme('yellow', #fff8c3, #fff075, #ffea3a, #fabf2c, #f47e16);
@include material-theme('google-yellow', #fbe7b1, #f6ca4c, #f3b300, #ef9200, #e96000);
@include material-theme('orange', #ffdfb1, #ffb64c, #ff9700, #f47b00, #e55000);
@include material-theme('deep-orange', #ffcbbb, #ff8964, #ff5621, #e54918, #be350b);
@include material-theme('grey', #f0f0f0, #e0e0e0, #a2a2a2, #636363, #252525);
*/
.material-theme-dark {
background-color: #3f51b5 !important;
color: #f1f1f1 !important; }
.material-theme-light {
background-color: #4285f4 !important;
color: #f1f1f1 !important; }
.material-theme-light-blue {
background-color: #03a9f4 !important;
color: #f1f1f1 !important; }
.material-theme-indigo {
background-color: #3e50b4 !important;
color: #f1f1f1 !important; }
.material-theme-green {
background-color: #0f9d58 !important;
color: #f1f1f1 !important; }
.material-theme-yellow {
background-color: #ffeb3b !important;
color: #111111 !important; }
.material-theme-orange {
background-color: #ff5722 !important;
color: #f1f1f1 !important; }
.material-theme-purple {
background-color: #9c27b0 !important;
color: #f1f1f1 !important; }
.material-theme-red {
background-color: #db4437 !important;
color: #f1f1f1 !important; }
.material-input-group-theme-light.material-input-has-value input, .material-input-group-theme-light.material-input-focused input {
border-color: #4285f4; }
.material-input-group-theme-light.material-input-has-value label, .material-input-group-theme-light.material-input-focused label {
color: #4285f4; }
.material-input-group-theme-light-blue.material-input-has-value input, .material-input-group-theme-light-blue.material-input-focused input {
border-color: #03a9f4; }
.material-input-group-theme-light-blue.material-input-has-value label, .material-input-group-theme-light-blue.material-input-focused label {
color: #03a9f4; }
.material-input-group-theme-dark.material-input-has-value input, .material-input-group-theme-dark.material-input-focused input {
border-color: #3f51b5; }
.material-input-group-theme-dark.material-input-has-value label, .material-input-group-theme-dark.material-input-focused label {
color: #3f51b5; }
.material-input-group-theme-green.material-input-has-value input, .material-input-group-theme-green.material-input-focused input {
border-color: #0f9d58; }
.material-input-group-theme-green.material-input-has-value label, .material-input-group-theme-green.material-input-focused label {
color: #0f9d58; }
.material-input-group-theme-yellow.material-input-has-value input, .material-input-group-theme-yellow.material-input-focused input {
border-color: #ffeb3b; }
.material-input-group-theme-yellow.material-input-has-value label, .material-input-group-theme-yellow.material-input-focused label {
color: #ffeb3b; }
.material-input-group-theme-orange.material-input-has-value input, .material-input-group-theme-orange.material-input-focused input {
border-color: #ff5722; }
.material-input-group-theme-orange.material-input-has-value label, .material-input-group-theme-orange.material-input-focused label {
color: #ff5722; }
.material-input-group-theme-purple.material-input-has-value input, .material-input-group-theme-purple.material-input-focused input {
border-color: #9c27b0; }
.material-input-group-theme-purple.material-input-has-value label, .material-input-group-theme-purple.material-input-focused label {
color: #9c27b0; }
.material-input-group-theme-red.material-input-has-value input, .material-input-group-theme-red.material-input-focused input {
border-color: #db4437; }
.material-input-group-theme-red.material-input-has-value label, .material-input-group-theme-red.material-input-focused label {
color: #db4437; }
.material-shadow {
position: absolute;
top: 0;
left: 0;
bottom: 0;
right: 0;
border-radius: inherit;
pointer-events: none; }
.material-shadow-bottom-z-1, material-button.material-button-raised:not([disabled]), material-button.material-button-fab {
box-shadow: 0 2px 5px 0 rgba(0, 0, 0, 0.26); }
.material-shadow-bottom-z-2, material-button:not([disabled]).material-button-raised.focus, material-button:not([disabled]).material-button-raised:hover, material-button:not([disabled]).material-button-fab.focus, material-button:not([disabled]).material-button-fab:hover {
box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.4); }
.material-shadow-animated.material-shadow {
transition: box-shadow 0.28s cubic-bezier(0.4, 0, 0.2, 1); }
*, *:before, *:after {
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box; }
html, body {
height: 100%;
color: #444;
background: #fff;
-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
-webkit-touch-callout: none;
-webkit-text-size-adjust: 100%;
-webkit-font-smoothing: antialiased;
text-rendering: optimizeLegibility; }
html h3, body h3 {
display: block;
-webkit-margin-before: 1em;
-webkit-margin-after: 1em;
-webkit-margin-start: 0px;
-webkit-margin-end: 0px;
font-size: 1.17em;
font-weight: bold; }
button, select, html, textarea, input {
font-family: 'RobotoDraft', 'Helvetica Neue', Helvetica, Arial; }
body {
margin: 0;
padding: 0;
outline: none; }
.inset {
padding: 10px; }
button {
font-family: 'RobotoDraft', 'Helvetica Neue', Helvetica, Arial; }
a {
background: transparent;
outline: none; }
h1 {
font-size: 2em;
margin: 0.67em 0; }
h2 {
font-size: 1.5em;
margin: 0.83em 0; }
h3 {
font-size: 1.17em;
margin: 1em 0; }
h4 {
font-size: 1em;
margin: 1.33em 0; }
h5 {
font-size: 0.83em;
margin: 1.67em 0; }
h6 {
font-size: 0.75em;
margin: 2.33em 0; }
select, button, textarea, input {
margin: 0;
font-size: 100%;
font-family: inherit;
vertical-align: baseline; }
input[type="reset"], input[type="submit"], html input[type="button"], button {
cursor: pointer;
-webkit-appearance: button; }
input[type="reset"][disabled], input[type="submit"][disabled], html input[type="button"][disabled], button[disabled] {
cursor: default; }
textarea {
vertical-align: top;
overflow: auto; }
input[type="radio"], input[type="checkbox"] {
padding: 0;
box-sizing: border-box; }
input[type="search"] {
-webkit-appearance: textfield;
box-sizing: content-box;
-webkit-box-sizing: content-box; }
input[type="search"]::-webkit-search-decoration, input[type="search"]::-webkit-search-cancel-button {
-webkit-appearance: none; }
table {
margin-bottom: 20px;
max-width: 100%;
width: 100%;
border-spacing: 0;
border-collapse: collapse;
background-color: transparent; }
td, th {
padding: 8px 16px;
border-top: 1px solid #ddd;
vertical-align: top; }
th {
border-bottom: 2px solid #ddd;
vertical-align: bottom; }
[layout] {
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
display: -webkit-box;
display: -webkit-flex;
display: -moz-box;
display: -moz-flex;
display: -ms-flexbox;
display: flex; }
[layout=vertical] {
-webkit-box-direction: normal;
-webkit-box-orient: vertical;
-webkit-flex-direction: column;
-moz-flex-direction: column;
-ms-flex-direction: column;
flex-direction: column; }
[layout-padding], [layout][layout-padding] > [flex] {
padding: 8px; }
[layout-padding] + [layout-padding] {
margin-top: -8px;
padding-top: 0; }
[layout-fill] {
margin: 0;
height: 100%;
width: 100%; }
[layout-align="center"], [layout-align="center center"], [layout-align="center start"], [layout-align="center end"] {
-webkit-box-pack: center;
-ms-flex-pack: center;
-webkit-justify-content: center;
-moz-justify-content: center;
justify-content: center; }
[layout-align="end"], [layout-align="end center"], [layout-align="end start"], [layout-align="end end"] {
-webkit-box-pack: end;
-ms-flex-pack: end;
-webkit-justify-content: flex-end;
-moz-justify-content: flex-end;
justify-content: flex-end; }
[layout-align="space-around"], [layout-align="space-around center"], [layout-align="space-around start"], [layout-align="space-around end"] {
-webkit-box-pack: space-around;
-ms-flex-pack: space-around;
-webkit-justify-content: space-around;
-moz-justify-content: space-around;
justify-content: space-around; }
[layout-align="space-between"], [layout-align="space-between center"], [layout-align="space-between start"], [layout-align="space-between end"] {
-webkit-box-pack: justify;
-ms-flex-pack: justify;
-webkit-justify-content: space-between;
-moz-justify-content: space-between;
justify-content: space-between; }
[layout-align="center center"], [layout-align="start center"], [layout-align="end center"], [layout-align="space-between center"], [layout-align="space-around center"] {
-webkit-box-align: center;
-ms-flex-align: center;
-webkit-align-items: center;
-moz-align-items: center;
align-items: center; }
[layout-align="center start"], [layout-align="start start"], [layout-align="end start"], [layout-align="space-between start"], [layout-align="space-around start"] {
-webkit-box-align: start;
-ms-flex-align: start;
-webkit-align-items: flex-start;
-moz-align-items: flex-start;
align-items: flex-start; }
[layout-align="center end"], [layout-align="start end"], [layout-align="end end"], [layout-align="space-between end"], [layout-align="space-around end"] {
-webkit-box-align: end;
-ms-flex-align: end;
-webkit-align-items: flex-end;
-moz-align-items: flex-end;
align-items: flex-end; }
[block] {
display: block; }
[inline-block] {
display: inline-block; }
[hide] {
display: none; }
[show] {
display: inherit; }
[flex] {
-webkit-box-flex: 1;
-webkit-flex: 1;
-moz-box-flex: 1;
-moz-flex: 1;
-ms-flex: 1;
flex: 1; }
[flex="5"] {
-webkit-box-flex: 0;
-webkit-flex: 0 0 5%;
-moz-box-flex: 0;
-moz-flex: 0 0 5%;
-ms-flex: 0 0 5%;
flex: 0 0 5%;
max-width: 5%; }
[flex="10"] {
-webkit-box-flex: 0;
-webkit-flex: 0 0 10%;
-moz-box-flex: 0;
-moz-flex: 0 0 10%;
-ms-flex: 0 0 10%;
flex: 0 0 10%;
max-width: 10%; }
[flex="15"] {
-webkit-box-flex: 0;
-webkit-flex: 0 0 15%;
-moz-box-flex: 0;
-moz-flex: 0 0 15%;
-ms-flex: 0 0 15%;
flex: 0 0 15%;
max-width: 15%; }
[flex="20"] {
-webkit-box-flex: 0;
-webkit-flex: 0 0 20%;
-moz-box-flex: 0;
-moz-flex: 0 0 20%;
-ms-flex: 0 0 20%;
flex: 0 0 20%;
max-width: 20%; }
[flex="25"] {
-webkit-box-flex: 0;
-webkit-flex: 0 0 25%;
-moz-box-flex: 0;
-moz-flex: 0 0 25%;
-ms-flex: 0 0 25%;
flex: 0 0 25%;
max-width: 25%; }
[flex="30"] {
-webkit-box-flex: 0;
-webkit-flex: 0 0 30%;
-moz-box-flex: 0;
-moz-flex: 0 0 30%;
-ms-flex: 0 0 30%;
flex: 0 0 30%;
max-width: 30%; }
[flex="35"] {
-webkit-box-flex: 0;
-webkit-flex: 0 0 35%;
-moz-box-flex: 0;
-moz-flex: 0 0 35%;
-ms-flex: 0 0 35%;
flex: 0 0 35%;
max-width: 35%; }
[flex="40"] {
-webkit-box-flex: 0;
-webkit-flex: 0 0 40%;
-moz-box-flex: 0;
-moz-flex: 0 0 40%;
-ms-flex: 0 0 40%;
flex: 0 0 40%;
max-width: 40%; }
[flex="45"] {
-webkit-box-flex: 0;
-webkit-flex: 0 0 45%;
-moz-box-flex: 0;
-moz-flex: 0 0 45%;
-ms-flex: 0 0 45%;
flex: 0 0 45%;
max-width: 45%; }
[flex="50"] {
-webkit-box-flex: 0;
-webkit-flex: 0 0 50%;
-moz-box-flex: 0;
-moz-flex: 0 0 50%;
-ms-flex: 0 0 50%;
flex: 0 0 50%;
max-width: 50%; }
[flex="55"] {
-webkit-box-flex: 0;
-webkit-flex: 0 0 55%;
-moz-box-flex: 0;
-moz-flex: 0 0 55%;
-ms-flex: 0 0 55%;
flex: 0 0 55%;
max-width: 55%; }
[flex="60"] {
-webkit-box-flex: 0;
-webkit-flex: 0 0 60%;
-moz-box-flex: 0;
-moz-flex: 0 0 60%;
-ms-flex: 0 0 60%;
flex: 0 0 60%;
max-width: 60%; }
[flex="65"] {
-webkit-box-flex: 0;
-webkit-flex: 0 0 65%;
-moz-box-flex: 0;
-moz-flex: 0 0 65%;
-ms-flex: 0 0 65%;
flex: 0 0 65%;
max-width: 65%; }
[flex="70"] {
-webkit-box-flex: 0;
-webkit-flex: 0 0 70%;
-moz-box-flex: 0;
-moz-flex: 0 0 70%;
-ms-flex: 0 0 70%;
flex: 0 0 70%;
max-width: 70%; }
[flex="75"] {
-webkit-box-flex: 0;
-webkit-flex: 0 0 75%;
-moz-box-flex: 0;
-moz-flex: 0 0 75%;
-ms-flex: 0 0 75%;
flex: 0 0 75%;
max-width: 75%; }
[flex="80"] {
-webkit-box-flex: 0;
-webkit-flex: 0 0 80%;
-moz-box-flex: 0;
-moz-flex: 0 0 80%;
-ms-flex: 0 0 80%;
flex: 0 0 80%;
max-width: 80%; }
[flex="85"] {
-webkit-box-flex: 0;
-webkit-flex: 0 0 85%;
-moz-box-flex: 0;
-moz-flex: 0 0 85%;
-ms-flex: 0 0 85%;
flex: 0 0 85%;
max-width: 85%; }
[flex="90"] {
-webkit-box-flex: 0;
-webkit-flex: 0 0 90%;
-moz-box-flex: 0;
-moz-flex: 0 0 90%;
-ms-flex: 0 0 90%;
flex: 0 0 90%;
max-width: 90%; }
[flex="95"] {
-webkit-box-flex: 0;
-webkit-flex: 0 0 95%;
-moz-box-flex: 0;
-moz-flex: 0 0 95%;
-ms-flex: 0 0 95%;
flex: 0 0 95%;
max-width: 95%; }
[flex="33"], [flex="34"] {
-webkit-box-flex: 0;
-webkit-flex: 0 0 33.33%;
-moz-box-flex: 0;
-moz-flex: 0 0 33.33%;
-ms-flex: 0 0 33.33%;
flex: 0 0 33.33%;
max-width: 33.33%; }
[flex="66"], [flex="67"] {
-webkit-box-flex: 0;
-webkit-flex: 0 0 66.66%;
-moz-box-flex: 0;
-moz-flex: 0 0 66.66%;
-ms-flex: 0 0 66.66%;
flex: 0 0 66.66%;
max-width: 66.66%; }
[offset="5"] {
margin-left: 5%; }
[offset="10"] {
margin-left: 10%; }
[offset="15"] {
margin-left: 15%; }
[offset="20"] {
margin-left: 20%; }
[offset="25"] {
margin-left: 25%; }
[offset="30"] {
margin-left: 30%; }
[offset="35"] {
margin-left: 35%; }
[offset="40"] {
margin-left: 40%; }
[offset="45"] {
margin-left: 45%; }
[offset="50"] {
margin-left: 50%; }
[offset="55"] {
margin-left: 55%; }
[offset="60"] {
margin-left: 60%; }
[offset="65"] {
margin-left: 65%; }
[offset="70"] {
margin-left: 70%; }
[offset="75"] {
margin-left: 75%; }
[offset="80"] {
margin-left: 80%; }
[offset="85"] {
margin-left: 85%; }
[offset="90"] {
margin-left: 90%; }
[offset="95"] {
margin-left: 95%; }
[offset="33"], [offset="34"] {
margin-left: 33.33%; }
[offset="66"], [offset="67"] {
margin-left: 66.66%; }
[layout-order="1"] {
-webkit-order: 1;
-ms-flex-order: 1;
order: 1; }
[layout-order="2"] {
-webkit-order: 2;
-ms-flex-order: 2;
order: 2; }
[layout-order="3"] {
-webkit-order: 3;
-ms-flex-order: 3;
order: 3; }
[layout-order="4"] {
-webkit-order: 4;
-ms-flex-order: 4;
order: 4; }
[layout-order="5"] {
-webkit-order: 5;
-ms-flex-order: 5;
order: 5; }
[layout-order="6"] {
-webkit-order: 6;
-ms-flex-order: 6;
order: 6; }
@media (min-width: 600px) {
[flex-sm] {
-webkit-box-flex: 1;
-webkit-flex: 1;
-moz-box-flex: 1;
-moz-flex: 1;
-ms-flex: 1;
flex: 1; }
[flex-sm="5"] {
-webkit-box-flex: 0;
-webkit-flex: 0 0 5%;
-moz-box-flex: 0;
-moz-flex: 0 0 5%;
-ms-flex: 0 0 5%;
flex: 0 0 5%;
max-width: 5%; }
[flex-sm="10"] {
-webkit-box-flex: 0;
-webkit-flex: 0 0 10%;
-moz-box-flex: 0;
-moz-flex: 0 0 10%;
-ms-flex: 0 0 10%;
flex: 0 0 10%;
max-width: 10%; }
[flex-sm="15"] {
-webkit-box-flex: 0;
-webkit-flex: 0 0 15%;
-moz-box-flex: 0;
-moz-flex: 0 0 15%;
-ms-flex: 0 0 15%;
flex: 0 0 15%;
max-width: 15%; }
[flex-sm="20"] {
-webkit-box-flex: 0;
-webkit-flex: 0 0 20%;
-moz-box-flex: 0;
-moz-flex: 0 0 20%;
-ms-flex: 0 0 20%;
flex: 0 0 20%;
max-width: 20%; }
[flex-sm="25"] {
-webkit-box-flex: 0;
-webkit-flex: 0 0 25%;
-moz-box-flex: 0;
-moz-flex: 0 0 25%;
-ms-flex: 0 0 25%;
flex: 0 0 25%;
max-width: 25%; }
[flex-sm="30"] {
-webkit-box-flex: 0;
-webkit-flex: 0 0 30%;
-moz-box-flex: 0;
-moz-flex: 0 0 30%;
-ms-flex: 0 0 30%;
flex: 0 0 30%;
max-width: 30%; }
[flex-sm="35"] {
-webkit-box-flex: 0;
-webkit-flex: 0 0 35%;
-moz-box-flex: 0;
-moz-flex: 0 0 35%;
-ms-flex: 0 0 35%;
flex: 0 0 35%;
max-width: 35%; }
[flex-sm="40"] {
-webkit-box-flex: 0;
-webkit-flex: 0 0 40%;
-moz-box-flex: 0;
-moz-flex: 0 0 40%;
-ms-flex: 0 0 40%;
flex: 0 0 40%;
max-width: 40%; }
[flex-sm="45"] {
-webkit-box-flex: 0;
-webkit-flex: 0 0 45%;
-moz-box-flex: 0;
-moz-flex: 0 0 45%;
-ms-flex: 0 0 45%;
flex: 0 0 45%;
max-width: 45%; }
[flex-sm="50"] {
-webkit-box-flex: 0;
-webkit-flex: 0 0 50%;
-moz-box-flex: 0;
-moz-flex: 0 0 50%;
-ms-flex: 0 0 50%;
flex: 0 0 50%;
max-width: 50%; }
[flex-sm="55"] {
-webkit-box-flex: 0;
-webkit-flex: 0 0 55%;
-moz-box-flex: 0;
-moz-flex: 0 0 55%;
-ms-flex: 0 0 55%;
flex: 0 0 55%;
max-width: 55%; }
[flex-sm="60"] {
-webkit-box-flex: 0;
-webkit-flex: 0 0 60%;
-moz-box-flex: 0;
-moz-flex: 0 0 60%;
-ms-flex: 0 0 60%;
flex: 0 0 60%;
max-width: 60%; }
[flex-sm="65"] {
-webkit-box-flex: 0;
-webkit-flex: 0 0 65%;
-moz-box-flex: 0;
-moz-flex: 0 0 65%;
-ms-flex: 0 0 65%;
flex: 0 0 65%;
max-width: 65%; }
[flex-sm="70"] {
-webkit-box-flex: 0;
-webkit-flex: 0 0 70%;
-moz-box-flex: 0;
-moz-flex: 0 0 70%;
-ms-flex: 0 0 70%;
flex: 0 0 70%;
max-width: 70%; }
[flex-sm="75"] {
-webkit-box-flex: 0;
-webkit-flex: 0 0 75%;
-moz-box-flex: 0;
-moz-flex: 0 0 75%;
-ms-flex: 0 0 75%;
flex: 0 0 75%;
max-width: 75%; }
[flex-sm="80"] {
-webkit-box-flex: 0;
-webkit-flex: 0 0 80%;
-moz-box-flex: 0;
-moz-flex: 0 0 80%;
-ms-flex: 0 0 80%;
flex: 0 0 80%;
max-width: 80%; }
[flex-sm="85"] {
-webkit-box-flex: 0;
-webkit-flex: 0 0 85%;
-moz-box-flex: 0;
-moz-flex: 0 0 85%;
-ms-flex: 0 0 85%;
flex: 0 0 85%;
max-width: 85%; }
[flex-sm="90"] {
-webkit-box-flex: 0;
-webkit-flex: 0 0 90%;
-moz-box-flex: 0;
-moz-flex: 0 0 90%;
-ms-flex: 0 0 90%;
flex: 0 0 90%;
max-width: 90%; }
[flex-sm="95"] {
-webkit-box-flex: 0;
-webkit-flex: 0 0 95%;
-moz-box-flex: 0;
-moz-flex: 0 0 95%;
-ms-flex: 0 0 95%;
flex: 0 0 95%;
max-width: 95%; }
[flex-sm="33"], [flex-sm="34"] {
-webkit-box-flex: 0;
-webkit-flex: 0 0 33.33%;
-moz-box-flex: 0;
-moz-flex: 0 0 33.33%;
-ms-flex: 0 0 33.33%;
flex: 0 0 33.33%;
max-width: 33.33%; }
[flex-sm="66"], [flex-sm="67"] {
-webkit-box-flex: 0;
-webkit-flex: 0 0 66.66%;
-moz-box-flex: 0;
-moz-flex: 0 0 66.66%;
-ms-flex: 0 0 66.66%;
flex: 0 0 66.66%;
max-width: 66.66%; }
[offset-sm="5"] {
margin-left: 5%; }
[offset-sm="10"] {
margin-left: 10%; }
[offset-sm="15"] {
margin-left: 15%; }
[offset-sm="20"] {
margin-left: 20%; }
[offset-sm="25"] {
margin-left: 25%; }
[offset-sm="30"] {
margin-left: 30%; }
[offset-sm="35"] {
margin-left: 35%; }
[offset-sm="40"] {
margin-left: 40%; }
[offset-sm="45"] {
margin-left: 45%; }
[offset-sm="50"] {
margin-left: 50%; }
[offset-sm="55"] {
margin-left: 55%; }
[offset-sm="60"] {
margin-left: 60%; }
[offset-sm="65"] {
margin-left: 65%; }
[offset-sm="70"] {
margin-left: 70%; }
[offset-sm="75"] {
margin-left: 75%; }
[offset-sm="80"] {
margin-left: 80%; }
[offset-sm="85"] {
margin-left: 85%; }
[offset-sm="90"] {
margin-left: 90%; }
[offset-sm="95"] {
margin-left: 95%; }
[offset-sm="33"], [offset-sm="34"] {
margin-left: 33.33%; }
[offset-sm="66"], [offset-sm="67"] {
margin-left: 66.66%; }
[layout-order-sm="1"] {
-webkit-order: 1;
-ms-flex-order: 1;
order: 1; }
[layout-order-sm="2"] {
-webkit-order: 2;
-ms-flex-order: 2;
order: 2; }
[layout-order-sm="3"] {
-webkit-order: 3;
-ms-flex-order: 3;
order: 3; }
[layout-order-sm="4"] {
-webkit-order: 4;
-ms-flex-order: 4;
order: 4; }
[layout-order-sm="5"] {
-webkit-order: 5;
-ms-flex-order: 5;
order: 5; }
[layout-order-sm="6"] {
-webkit-order: 6;
-ms-flex-order: 6;
order: 6; }
[layout-sm] {
-webkit-box-direction: normal;
-webkit-box-orient: horizontal;
-webkit-flex-direction: row;
-moz-flex-direction: row;
-ms-flex-direction: row;
flex-direction: row; }
[layout-sm=vertical] {
-webkit-box-direction: normal;
-webkit-box-orient: vertical;
-webkit-flex-direction: column;
-moz-flex-direction: column;
-ms-flex-direction: column;
flex-direction: column; }
[block-sm] {
display: block; }
[inline-block-sm] {
display: inline-block; }
[show-sm] {
display: inherit; }
[hide-sm] {
display: none; } }
@media (min-width: 960px) {
[flex-md] {
-webkit-box-flex: 1;
-webkit-flex: 1;
-moz-box-flex: 1;
-moz-flex: 1;
-ms-flex: 1;
flex: 1; }
[flex-md="5"] {
-webkit-box-flex: 0;
-webkit-flex: 0 0 5%;
-moz-box-flex: 0;
-moz-flex: 0 0 5%;
-ms-flex: 0 0 5%;
flex: 0 0 5%;
max-width: 5%; }
[flex-md="10"] {
-webkit-box-flex: 0;
-webkit-flex: 0 0 10%;
-moz-box-flex: 0;
-moz-flex: 0 0 10%;
-ms-flex: 0 0 10%;
flex: 0 0 10%;
max-width: 10%; }
[flex-md="15"] {
-webkit-box-flex: 0;
-webkit-flex: 0 0 15%;
-moz-box-flex: 0;
-moz-flex: 0 0 15%;
-ms-flex: 0 0 15%;
flex: 0 0 15%;
max-width: 15%; }
[flex-md="20"] {
-webkit-box-flex: 0;
-webkit-flex: 0 0 20%;
-moz-box-flex: 0;
-moz-flex: 0 0 20%;
-ms-flex: 0 0 20%;
flex: 0 0 20%;
max-width: 20%; }
[flex-md="25"] {
-webkit-box-flex: 0;
-webkit-flex: 0 0 25%;
-moz-box-flex: 0;
-moz-flex: 0 0 25%;
-ms-flex: 0 0 25%;
flex: 0 0 25%;
max-width: 25%; }
[flex-md="30"] {
-webkit-box-flex: 0;
-webkit-flex: 0 0 30%;
-moz-box-flex: 0;
-moz-flex: 0 0 30%;
-ms-flex: 0 0 30%;
flex: 0 0 30%;
max-width: 30%; }
[flex-md="35"] {
-webkit-box-flex: 0;
-webkit-flex: 0 0 35%;
-moz-box-flex: 0;
-moz-flex: 0 0 35%;
-ms-flex: 0 0 35%;
flex: 0 0 35%;
max-width: 35%; }
[flex-md="40"] {
-webkit-box-flex: 0;
-webkit-flex: 0 0 40%;
-moz-box-flex: 0;
-moz-flex: 0 0 40%;
-ms-flex: 0 0 40%;
flex: 0 0 40%;
max-width: 40%; }
[flex-md="45"] {
-webkit-box-flex: 0;
-webkit-flex: 0 0 45%;
-moz-box-flex: 0;
-moz-flex: 0 0 45%;
-ms-flex: 0 0 45%;
flex: 0 0 45%;
max-width: 45%; }
[flex-md="50"] {
-webkit-box-flex: 0;
-webkit-flex: 0 0 50%;
-moz-box-flex: 0;
-moz-flex: 0 0 50%;
-ms-flex: 0 0 50%;
flex: 0 0 50%;
max-width: 50%; }
[flex-md="55"] {
-webkit-box-flex: 0;
-webkit-flex: 0 0 55%;
-moz-box-flex: 0;
-moz-flex: 0 0 55%;
-ms-flex: 0 0 55%;
flex: 0 0 55%;
max-width: 55%; }
[flex-md="60"] {
-webkit-box-flex: 0;
-webkit-flex: 0 0 60%;
-moz-box-flex: 0;
-moz-flex: 0 0 60%;
-ms-flex: 0 0 60%;
flex: 0 0 60%;
max-width: 60%; }
[flex-md="65"] {
-webkit-box-flex: 0;
-webkit-flex: 0 0 65%;
-moz-box-flex: 0;
-moz-flex: 0 0 65%;
-ms-flex: 0 0 65%;
flex: 0 0 65%;
max-width: 65%; }
[flex-md="70"] {
-webkit-box-flex: 0;
-webkit-flex: 0 0 70%;
-moz-box-flex: 0;
-moz-flex: 0 0 70%;
-ms-flex: 0 0 70%;
flex: 0 0 70%;
max-width: 70%; }
[flex-md="75"] {
-webkit-box-flex: 0;
-webkit-flex: 0 0 75%;
-moz-box-flex: 0;
-moz-flex: 0 0 75%;
-ms-flex: 0 0 75%;
flex: 0 0 75%;
max-width: 75%; }
[flex-md="80"] {
-webkit-box-flex: 0;
-webkit-flex: 0 0 80%;
-moz-box-flex: 0;
-moz-flex: 0 0 80%;
-ms-flex: 0 0 80%;
flex: 0 0 80%;
max-width: 80%; }
[flex-md="85"] {
-webkit-box-flex: 0;
-webkit-flex: 0 0 85%;
-moz-box-flex: 0;
-moz-flex: 0 0 85%;
-ms-flex: 0 0 85%;
flex: 0 0 85%;
max-width: 85%; }
[flex-md="90"] {
-webkit-box-flex: 0;
-webkit-flex: 0 0 90%;
-moz-box-flex: 0;
-moz-flex: 0 0 90%;
-ms-flex: 0 0 90%;
flex: 0 0 90%;
max-width: 90%; }
[flex-md="95"] {
-webkit-box-flex: 0;
-webkit-flex: 0 0 95%;
-moz-box-flex: 0;
-moz-flex: 0 0 95%;
-ms-flex: 0 0 95%;
flex: 0 0 95%;
max-width: 95%; }
[flex-md="33"], [flex-md="34"] {
-webkit-box-flex: 0;
-webkit-flex: 0 0 33.33%;
-moz-box-flex: 0;
-moz-flex: 0 0 33.33%;
-ms-flex: 0 0 33.33%;
flex: 0 0 33.33%;
max-width: 33.33%; }
[flex-md="66"], [flex-md="67"] {
-webkit-box-flex: 0;
-webkit-flex: 0 0 66.66%;
-moz-box-flex: 0;
-moz-flex: 0 0 66.66%;
-ms-flex: 0 0 66.66%;
flex: 0 0 66.66%;
max-width: 66.66%; }
[offset-md="5"] {
margin-left: 5%; }
[offset-md="10"] {
margin-left: 10%; }
[offset-md="15"] {
margin-left: 15%; }
[offset-md="20"] {
margin-left: 20%; }
[offset-md="25"] {
margin-left: 25%; }
[offset-md="30"] {
margin-left: 30%; }
[offset-md="35"] {
margin-left: 35%; }
[offset-md="40"] {
margin-left: 40%; }
[offset-md="45"] {
margin-left: 45%; }
[offset-md="50"] {
margin-left: 50%; }
[offset-md="55"] {
margin-left: 55%; }
[offset-md="60"] {
margin-left: 60%; }
[offset-md="65"] {
margin-left: 65%; }
[offset-md="70"] {
margin-left: 70%; }
[offset-md="75"] {
margin-left: 75%; }
[offset-md="80"] {
margin-left: 80%; }
[offset-md="85"] {
margin-left: 85%; }
[offset-md="90"] {
margin-left: 90%; }
[offset-md="95"] {
margin-left: 95%; }
[offset-md="33"], [offset-md="34"] {
margin-left: 33.33%; }
[offset-md="66"], [offset-md="67"] {
margin-left: 66.66%; }
[layout-order-md="1"] {
-webkit-order: 1;
-ms-flex-order: 1;
order: 1; }
[layout-order-md="2"] {
-webkit-order: 2;
-ms-flex-order: 2;
order: 2; }
[layout-order-md="3"] {
-webkit-order: 3;
-ms-flex-order: 3;
order: 3; }
[layout-order-md="4"] {
-webkit-order: 4;
-ms-flex-order: 4;
order: 4; }
[layout-order-md="5"] {
-webkit-order: 5;
-ms-flex-order: 5;
order: 5; }
[layout-order-md="6"] {
-webkit-order: 6;
-ms-flex-order: 6;
order: 6; }
[layout-md] {
-webkit-box-direction: normal;
-webkit-box-orient: horizontal;
-webkit-flex-direction: row;
-moz-flex-direction: row;
-ms-flex-direction: row;
flex-direction: row; }
[layout-md=vertical] {
-webkit-box-direction: normal;
-webkit-box-orient: vertical;
-webkit-flex-direction: column;
-moz-flex-direction: column;
-ms-flex-direction: column;
flex-direction: column; }
[block-md] {
display: block; }
[inline-block-md] {
display: inline-block; }
[show-md] {
display: inherit; }
[hide-md] {
display: none; } }
@media (min-width: 1200px) {
[flex-lg] {
-webkit-box-flex: 1;
-webkit-flex: 1;
-moz-box-flex: 1;
-moz-flex: 1;
-ms-flex: 1;
flex: 1; }
[flex-lg="5"] {
-webkit-box-flex: 0;
-webkit-flex: 0 0 5%;
-moz-box-flex: 0;
-moz-flex: 0 0 5%;
-ms-flex: 0 0 5%;
flex: 0 0 5%;
max-width: 5%; }
[flex-lg="10"] {
-webkit-box-flex: 0;
-webkit-flex: 0 0 10%;
-moz-box-flex: 0;
-moz-flex: 0 0 10%;
-ms-flex: 0 0 10%;
flex: 0 0 10%;
max-width: 10%; }
[flex-lg="15"] {
-webkit-box-flex: 0;
-webkit-flex: 0 0 15%;
-moz-box-flex: 0;
-moz-flex: 0 0 15%;
-ms-flex: 0 0 15%;
flex: 0 0 15%;
max-width: 15%; }
[flex-lg="20"] {
-webkit-box-flex: 0;
-webkit-flex: 0 0 20%;
-moz-box-flex: 0;
-moz-flex: 0 0 20%;
-ms-flex: 0 0 20%;
flex: 0 0 20%;
max-width: 20%; }
[flex-lg="25"] {
-webkit-box-flex: 0;
-webkit-flex: 0 0 25%;
-moz-box-flex: 0;
-moz-flex: 0 0 25%;
-ms-flex: 0 0 25%;
flex: 0 0 25%;
max-width: 25%; }
[flex-lg="30"] {
-webkit-box-flex: 0;
-webkit-flex: 0 0 30%;
-moz-box-flex: 0;
-moz-flex: 0 0 30%;
-ms-flex: 0 0 30%;
flex: 0 0 30%;
max-width: 30%; }
[flex-lg="35"] {
-webkit-box-flex: 0;
-webkit-flex: 0 0 35%;
-moz-box-flex: 0;
-moz-flex: 0 0 35%;
-ms-flex: 0 0 35%;
flex: 0 0 35%;
max-width: 35%; }
[flex-lg="40"] {
-webkit-box-flex: 0;
-webkit-flex: 0 0 40%;
-moz-box-flex: 0;
-moz-flex: 0 0 40%;
-ms-flex: 0 0 40%;
flex: 0 0 40%;
max-width: 40%; }
[flex-lg="45"] {
-webkit-box-flex: 0;
-webkit-flex: 0 0 45%;
-moz-box-flex: 0;
-moz-flex: 0 0 45%;
-ms-flex: 0 0 45%;
flex: 0 0 45%;
max-width: 45%; }
[flex-lg="50"] {
-webkit-box-flex: 0;
-webkit-flex: 0 0 50%;
-moz-box-flex: 0;
-moz-flex: 0 0 50%;
-ms-flex: 0 0 50%;
flex: 0 0 50%;
max-width: 50%; }
[flex-lg="55"] {
-webkit-box-flex: 0;
-webkit-flex: 0 0 55%;
-moz-box-flex: 0;
-moz-flex: 0 0 55%;
-ms-flex: 0 0 55%;
flex: 0 0 55%;
max-width: 55%; }
[flex-lg="60"] {
-webkit-box-flex: 0;
-webkit-flex: 0 0 60%;
-moz-box-flex: 0;
-moz-flex: 0 0 60%;
-ms-flex: 0 0 60%;
flex: 0 0 60%;
max-width: 60%; }
[flex-lg="65"] {
-webkit-box-flex: 0;
-webkit-flex: 0 0 65%;
-moz-box-flex: 0;
-moz-flex: 0 0 65%;
-ms-flex: 0 0 65%;
flex: 0 0 65%;
max-width: 65%; }
[flex-lg="70"] {
-webkit-box-flex: 0;
-webkit-flex: 0 0 70%;
-moz-box-flex: 0;
-moz-flex: 0 0 70%;
-ms-flex: 0 0 70%;
flex: 0 0 70%;
max-width: 70%; }
[flex-lg="75"] {
-webkit-box-flex: 0;
-webkit-flex: 0 0 75%;
-moz-box-flex: 0;
-moz-flex: 0 0 75%;
-ms-flex: 0 0 75%;
flex: 0 0 75%;
max-width: 75%; }
[flex-lg="80"] {
-webkit-box-flex: 0;
-webkit-flex: 0 0 80%;
-moz-box-flex: 0;
-moz-flex: 0 0 80%;
-ms-flex: 0 0 80%;
flex: 0 0 80%;
max-width: 80%; }
[flex-lg="85"] {
-webkit-box-flex: 0;
-webkit-flex: 0 0 85%;
-moz-box-flex: 0;
-moz-flex: 0 0 85%;
-ms-flex: 0 0 85%;
flex: 0 0 85%;
max-width: 85%; }
[flex-lg="90"] {
-webkit-box-flex: 0;
-webkit-flex: 0 0 90%;
-moz-box-flex: 0;
-moz-flex: 0 0 90%;
-ms-flex: 0 0 90%;
flex: 0 0 90%;
max-width: 90%; }
[flex-lg="95"] {
-webkit-box-flex: 0;
-webkit-flex: 0 0 95%;
-moz-box-flex: 0;
-moz-flex: 0 0 95%;
-ms-flex: 0 0 95%;
flex: 0 0 95%;
max-width: 95%; }
[flex-lg="33"], [flex-lg="34"] {
-webkit-box-flex: 0;
-webkit-flex: 0 0 33.33%;
-moz-box-flex: 0;
-moz-flex: 0 0 33.33%;
-ms-flex: 0 0 33.33%;
flex: 0 0 33.33%;
max-width: 33.33%; }
[flex-lg="66"], [flex-lg="67"] {
-webkit-box-flex: 0;
-webkit-flex: 0 0 66.66%;
-moz-box-flex: 0;
-moz-flex: 0 0 66.66%;
-ms-flex: 0 0 66.66%;
flex: 0 0 66.66%;
max-width: 66.66%; }
[offset-lg="5"] {
margin-left: 5%; }
[offset-lg="10"] {
margin-left: 10%; }
[offset-lg="15"] {
margin-left: 15%; }
[offset-lg="20"] {
margin-left: 20%; }
[offset-lg="25"] {
margin-left: 25%; }
[offset-lg="30"] {
margin-left: 30%; }
[offset-lg="35"] {
margin-left: 35%; }
[offset-lg="40"] {
margin-left: 40%; }
[offset-lg="45"] {
margin-left: 45%; }
[offset-lg="50"] {
margin-left: 50%; }
[offset-lg="55"] {
margin-left: 55%; }
[offset-lg="60"] {
margin-left: 60%; }
[offset-lg="65"] {
margin-left: 65%; }
[offset-lg="70"] {
margin-left: 70%; }
[offset-lg="75"] {
margin-left: 75%; }
[offset-lg="80"] {
margin-left: 80%; }
[offset-lg="85"] {
margin-left: 85%; }
[offset-lg="90"] {
margin-left: 90%; }
[offset-lg="95"] {
margin-left: 95%; }
[offset-lg="33"], [offset-lg="34"] {
margin-left: 33.33%; }
[offset-lg="66"], [offset-lg="67"] {
margin-left: 66.66%; }
[layout-order-lg="1"] {
-webkit-order: 1;
-ms-flex-order: 1;
order: 1; }
[layout-order-lg="2"] {
-webkit-order: 2;
-ms-flex-order: 2;
order: 2; }
[layout-order-lg="3"] {
-webkit-order: 3;
-ms-flex-order: 3;
order: 3; }
[layout-order-lg="4"] {
-webkit-order: 4;
-ms-flex-order: 4;
order: 4; }
[layout-order-lg="5"] {
-webkit-order: 5;
-ms-flex-order: 5;
order: 5; }
[layout-order-lg="6"] {
-webkit-order: 6;
-ms-flex-order: 6;
order: 6; }
[layout-lg] {
-webkit-box-direction: normal;
-webkit-box-orient: horizontal;
-webkit-flex-direction: row;
-moz-flex-direction: row;
-ms-flex-direction: row;
flex-direction: row; }
[layout-lg=vertical] {
-webkit-box-direction: normal;
-webkit-box-orient: vertical;
-webkit-flex-direction: column;
-moz-flex-direction: column;
-ms-flex-direction: column;
flex-direction: column; }
[block-lg] {
display: block; }
[inline-block-lg] {
display: inline-block; }
[show-lg] {
display: inherit; }
[hide-lg] {
display: none; } }
material-tabs-ink-bar {
height: 2px;
background: #ffff85;
position: absolute;
margin-top: -2px; }
@-webkit-keyframes inkRippleButton {
0% {
-webkit-transform: scale(0);
transform: scale(0);
opacity: 0.15; }
50% {
-webkit-transform: scale(0.75);
transform: scale(0.75);
opacity: 0.15; }
100% {
-webkit-transform: scale(2);
transform: scale(2);
opacity: 0; } }
@keyframes inkRippleButton {
0% {
-webkit-transform: scale(0);
transform: scale(0);
opacity: 0.15; }
50% {
-webkit-transform: scale(0.75);
transform: scale(0.75);
opacity: 0.15; }
100% {
-webkit-transform: scale(2);
transform: scale(2);
opacity: 0; } }
@-webkit-keyframes inkRippleCheckbox {
0% {
-webkit-transform: scale(0);
transform: scale(0);
opacity: 0.4; }
50% {
-webkit-transform: scale(1);
transform: scale(1);
opacity: 0.4; }
100% {
-webkit-transform: scale(1);
transform: scale(1);
opacity: 0; } }
@keyframes inkRippleCheckbox {
0% {
-webkit-transform: scale(0);
transform: scale(0);
opacity: 0.4; }
50% {
-webkit-transform: scale(1);
transform: scale(1);
opacity: 0.4; }
100% {
-webkit-transform: scale(1);
transform: scale(1);
opacity: 0; } }
/*
* A container inside of a rippling element (eg a button),
* which contains all of the individual ripples
*/
.material-ripple-container {
pointer-events: none;
position: absolute;
overflow: hidden;
left: 0;
top: 0;
width: 100%;
height: 100%; }
.material-ripple {
position: absolute; }
material-backdrop {
z-index: 7;
background-color: rgba(0, 0, 0, 0);
position: fixed;
left: 0;
top: 0;
right: 0;
bottom: 0;
-webkit-transition: all 0.2s ease-out;
transition: all 0.2s ease-out; }
material-backdrop.opaque {
background-color: rgba(0, 0, 0, 0.4); }
material-backdrop.ng-enter {
-webkit-transition-delay: 0.1s;
transition-delay: 0.1s; }
material-backdrop.ng-enter, material-backdrop.ng-leave.ng-leave-active {
opacity: 0; }
material-backdrop.ng-leave, material-backdrop.ng-enter.ng-enter-active {
opacity: 1; }
material-bottom-sheet {
position: absolute;
left: 0;
right: 0;
bottom: 0;
padding: 8px 16px 88px 16px;
z-index: 8;
background-color: #FAFAFA;
border-top: 1px solid #BCBBBB;
-webkit-transform: translate3d(0, 80px, 0);
transform: translate3d(0, 80px, 0);
-webkit-transition: 0.2s linear;
transition: 0.2s linear;
-webkit-transition-property: -webkit-transform;
transition-property: transform; }
material-bottom-sheet.has-header {
padding-top: 0; }
material-bottom-sheet.ng-enter {
opacity: 0;
-webkit-transform: translate3d(0, 100%, 0);
transform: translate3d(0, 100%, 0); }
material-bottom-sheet.ng-enter-active {
opacity: 1;
display: block;
-webkit-transform: translate3d(0, 80px, 0) !important;
transform: translate3d(0, 80px, 0) !important; }
material-bottom-sheet.ng-leave-active {
-webkit-transform: translate3d(0, 100%, 0) !important;
transform: translate3d(0, 100%, 0) !important; }
material-bottom-sheet h2.material-subheader {
background-color: transparent;
font-family: 'RobotoDraft', 'Helvetica Neue', Helvetica, Arial;
color: #999;
line-height: 56px;
padding: 0;
white-space: nowrap; }
material-bottom-sheet material-inline-icon {
display: inline-block;
height: 24px;
width: 24px;
fill: #444; }
material-bottom-sheet material-item {
display: -webkit-box;
display: -webkit-flex;
display: -moz-box;
display: -moz-flex;
display: -ms-flexbox;
display: flex;
outline: none; }
material-bottom-sheet material-item:hover {
cursor: pointer; }
material-bottom-sheet.list material-item {
-webkit-box-align: center;
-ms-flex-align: center;
-webkit-align-items: center;
-moz-align-items: center;
align-items: center;
height: 48px;
color: #999; }
material-bottom-sheet.list material-item div.material-icon-container {
display: inline-block;
height: 24px;
margin-right: 32px; }
material-bottom-sheet.grid {
padding-left: 24px;
padding-right: 24px;
padding-top: 0; }
material-bottom-sheet.grid material-list {
display: -webkit-box;
display: -webkit-flex;
display: -moz-box;
display: -moz-flex;
display: -ms-flexbox;
display: flex;
-webkit-box-direction: normal;
-webkit-box-orient: horizontal;
-webkit-flex-direction: row;
-moz-flex-direction: row;
-ms-flex-direction: row;
flex-direction: row;
-webkit-flex-wrap: wrap;
-moz-flex-wrap: wrap;
-ms-flex-wrap: wrap;
flex-wrap: wrap;
-webkit-transition: all 0.5s;
transition: all 0.5s; }
material-bottom-sheet.grid material-item {
-webkit-box-direction: normal;
-webkit-box-orient: vertical;
-webkit-flex-direction: column;
-moz-flex-direction: column;
-ms-flex-direction: column;
flex-direction: column;
-webkit-box-align: center;
-ms-flex-align: center;
-webkit-align-items: center;
-moz-align-items: center;
align-items: center;
-webkit-transition: all 0.5s;
transition: all 0.5s;
height: 80px;
margin-top: 8px;
margin-bottom: 8px;
/* Mixin for how many grid items to show per row */ }
@media screen and (max-width: 600px) {
material-bottom-sheet.grid material-item {
-webkit-box-flex: 1;
-webkit-flex: 1 1 33.33333%;
-moz-box-flex: 1;
-moz-flex: 1 1 33.33333%;
-ms-flex: 1 1 33.33333%;
flex: 1 1 33.33333%;
max-width: 33.33333%; }
material-bottom-sheet.grid material-item:nth-of-type(3n+1) {
-webkit-box-align: start;
-ms-flex-align: start;
-webkit-align-items: flex-start;
-moz-align-items: flex-start;
align-items: flex-start; }
material-bottom-sheet.grid material-item:nth-of-type(3n) {
-webkit-box-align: end;
-ms-flex-align: end;
-webkit-align-items: flex-end;
-moz-align-items: flex-end;
align-items: flex-end; } }
@media screen and (min-width: 600px) and (max-width: 960px) {
material-bottom-sheet.grid material-item {
-webkit-box-flex: 1;
-webkit-flex: 1 1 25%;
-moz-box-flex: 1;
-moz-flex: 1 1 25%;
-ms-flex: 1 1 25%;
flex: 1 1 25%;
max-width: 25%; } }
@media screen and (min-width: 960px) and (max-width: 1200px) {
material-bottom-sheet.grid material-item {
-webkit-box-flex: 1;
-webkit-flex: 1 1 16.66667%;
-moz-box-flex: 1;
-moz-flex: 1 1 16.66667%;
-ms-flex: 1 1 16.66667%;
flex: 1 1 16.66667%;
max-width: 16.66667%; } }
@media screen and (min-width: 1200px) {
material-bottom-sheet.grid material-item {
-webkit-box-flex: 1;
-webkit-flex: 1 1 14.28571%;
-moz-box-flex: 1;
-moz-flex: 1 1 14.28571%;
-ms-flex: 1 1 14.28571%;
flex: 1 1 14.28571%;
max-width: 14.28571%; } }
material-bottom-sheet.grid material-item .material-item-content {
display: -webkit-box;
display: -webkit-flex;
display: -moz-box;
display: -moz-flex;
display: -ms-flexbox;
display: flex;
-webkit-box-direction: normal;
-webkit-box-orient: vertical;
-webkit-flex-direction: column;
-moz-flex-direction: column;
-ms-flex-direction: column;
flex-direction: column;
-webkit-box-align: center;
-ms-flex-align: center;
-webkit-align-items: center;
-moz-align-items: center;
align-items: center;
width: 48px; }
material-bottom-sheet.grid material-item .material-icon-container {
display: inline-block;
box-sizing: border-box;
height: 48px;
width: 48px;
margin: 8px 0; }
material-bottom-sheet.grid material-item p.grid-text {
font-weight: 300;
line-height: 16px;
font-size: 13px;
margin: 0;
white-space: nowrap;
width: 48px;
text-align: center; }
material-content {
display: block;
position: relative;
overflow: auto;
-webkit-overflow-scrolling: touch; }
material-content[scroll-y] {
overflow-y: auto;
overflow-x: hidden; }
material-content[scroll-x] {
overflow-x: auto;
overflow-y: hidden; }
material-content.material-content-padding {
padding: 8px; }
@media (min-width: 600px) {
material-content.material-content-padding {
padding: 16px; } }
material-icon {
margin: auto;
padding: 0;
display: inline-block;
margin-top: 5px;
background-repeat: no-repeat no-repeat;
pointer-events: none; }
svg, object {
fill: currentColor;
color-fill: currentColor; }
/**
* Position a FAB button.
*/
material-button {
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
position: relative;
display: inline-block;
outline: none;
border: 0;
padding: 0;
margin: 0;
border-radius: 3px;
text-align: center;
text-transform: uppercase;
font-weight: inherit;
font-style: inherit;
font-variant: inherit;
font-size: inherit;
font-family: inherit;
line-height: inherit;
cursor: pointer;
overflow: hidden; }
material-button .material-button-inner {
display: block;
left: 0;
top: 0;
width: 100%;
height: 100%;
background: transparent;
border: none;
color: inherit;
outline: none;
text-transform: inherit;
font-weight: inherit;
font-style: inherit;
font-variant: inherit;
font-size: inherit;
font-family: inherit;
line-height: inherit;
white-space: nowrap;
padding: 6px; }
material-button a {
text-decoration: none; }
material-button[href] {
pointer-events: none; }
material-button[href] .material-button-inner {
pointer-events: auto; }
material-button.material-button-colored {
color: #4285f4;
fill: #4285f4; }
material-button.material-button-cornered {
border-radius: 0; }
material-button:not([disabled]):hover, material-button:not([disabled]).focus {
background-color: #e4e4e4; }
material-button[disabled] {
background-color: #eaeaea !important;
color: #a8a8a8 !important;
cursor: auto; }
material-button.material-button-icon {
padding: 0;
background: none; }
material-button.material-button-raised {
background-color: #dfdfe9;
-webkit-transition: 0.2s linear;
-webkit-transition-property: box-shadow, -webkit-transform, background-color;
transition: 0.2s linear transform;
transition-property: box-shadow, transform, background-color;
-webkit-transform: translate3d(0, 0, 0);
transform: translate3d(0, 0, 0); }
material-button.material-button-raised:not([disabled]):hover, material-button.material-button-raised:not([disabled]).focus {
background-color: #d6d6d6; }
material-button.material-button-raised.material-button-colored {
background-color: #4285f4;
color: #fff; }
material-button.material-button-raised.material-button-colored:hover:not([disabled]), material-button.material-button-raised.material-button-colored.focus {
background-color: #3367d6; }
material-button.material-button-fab {
z-index: 2;
width: 56px;
height: 56px;
border-radius: 28px;
border-radius: 50%;
overflow: hidden;
-webkit-transform: translate3d(0, 0, 0);
transform: translate3d(0, 0, 0);
-webkit-transition: 0.2s linear;
-webkit-transition-property: -webkit-transform, box-shadow;
transition: transform 0.2s linear;
transition-property: transform, box-shadow; }
material-button.material-button-fab.material-button-fab-bottom-right {
top: auto;
right: 28px;
bottom: 28px;
left: auto;
position: absolute; }
material-button.material-button-fab.material-button-fab-bottom-left {
top: auto;
right: auto;
bottom: 28px;
left: 28px;
position: absolute; }
material-button.material-button-fab.material-button-fab-top-right {
top: 28px;
right: 28px;
bottom: auto;
left: auto;
position: absolute; }
material-button.material-button-fab.material-button-fab-top-left {
top: 28px;
right: auto;
bottom: auto;
left: 28px;
position: absolute; }
material-button:not([disabled]).material-button-raised.focus, material-button:not([disabled]).material-button-raised:hover, material-button:not([disabled]).material-button-fab.focus, material-button:not([disabled]).material-button-fab:hover {
-webkit-transform: translate3d(0, -1px, 0);
transform: translate3d(0, -1px, 0); }
.material-toast-open-top material-button.material-button-fab-top-left, .material-toast-open-top material-button.material-button-fab-top-right {
-webkit-transform: translate3d(0, 32px, 0);
transform: translate3d(0, 32px, 0); }
.material-toast-open-top material-button.material-button-fab-top-left.focus, .material-toast-open-top material-button.material-button-fab-top-left:hover:not([disabled]), .material-toast-open-top material-button.material-button-fab-top-right.focus, .material-toast-open-top material-button.material-button-fab-top-right:hover:not([disabled]) {
-webkit-transform: translate3d(0, 31px, 0);
transform: translate3d(0, 31px, 0); }
.material-toast-open-bottom material-button.material-button-fab-bottom-left, .material-toast-open-bottom material-button.material-button-fab-bottom-right {
-webkit-transform: translate3d(0, -32px, 0);
transform: translate3d(0, -32px, 0); }
.material-toast-open-bottom material-button.material-button-fab-bottom-left.focus, .material-toast-open-bottom material-button.material-button-fab-bottom-left:hover, .material-toast-open-bottom material-button.material-button-fab-bottom-right.focus, .material-toast-open-bottom material-button.material-button-fab-bottom-right:hover {
-webkit-transform: translate3d(0, -33px, 0);
transform: translate3d(0, -33px, 0); }
.material-button-group {
display: -webkit-box;
display: -webkit-flex;
display: -moz-box;
display: -moz-flex;
display: -ms-flexbox;
display: flex;
-webkit-box-flex: 1;
-webkit-flex: 1;
-moz-box-flex: 1;
-moz-flex: 1;
-ms-flex: 1;
flex: 1;
width: 100%; }
.material-button-group > .material-button {
-webkit-box-flex: 1;
-webkit-flex: 1;
-moz-box-flex: 1;
-moz-flex: 1;
-ms-flex: 1;
flex: 1;
display: block;
overflow: hidden;
width: 0;
border-width: 1px 0px 1px 1px;
border-radius: 0;
text-align: center;
text-overflow: ellipsis;
white-space: nowrap; }
.material-button-group > .material-button:first-child {
border-radius: 2px 0px 0px 2px; }
.material-button-group > .material-button:last-child {
border-right-width: 1px;
border-radius: 0px 2px 2px 0px; }
.material-dialog-container {
display: -webkit-box;
display: -webkit-flex;
display: -moz-box;
display: -moz-flex;
display: -ms-flexbox;
display: flex;
-webkit-box-pack: center;
-ms-flex-pack: center;
-webkit-justify-content: center;
-moz-justify-content: center;
justify-content: center;
-webkit-box-align: center;
-ms-flex-align: center;
-webkit-align-items: center;
-moz-align-items: center;
align-items: center;
position: fixed;
left: 0;
top: 0;
right: 0;
bottom: 0;
z-index: 10; }
.material-dialog-container.active {
-webkit-transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);
transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);
-webkit-transform: translate3d(0, 0, 0) scale(1);
transform: translate3d(0, 0, 0) scale(1);
opacity: 1; }
.material-dialog-container.ng-leave.ng-leave-active {
-webkit-transform: translate3d(0, 100%, 0) scale(0.5);
transform: translate3d(0, 100%, 0) scale(0.5);
opacity: 0; }
material-dialog {
min-width: 240px;
max-width: 80%;
max-height: 80%;
margin: auto;
position: relative;
background-color: #fff;
border-radius: 4px;
box-shadow: 0px 27px 24px 0 rgba(0, 0, 0, 0.2);
display: -webkit-box;
display: -webkit-flex;
display: -moz-box;
display: -moz-flex;
display: -ms-flexbox;
display: flex;
-webkit-box-direction: normal;
-webkit-box-orient: vertical;
-webkit-flex-direction: column;
-moz-flex-direction: column;
-ms-flex-direction: column;
flex-direction: column; }
material-dialog .dialog-content {
-webkit-order: 1;
-ms-flex-order: 1;
order: 1;
padding: 24px;
overflow: auto;
-webkit-overflow-scrolling: touch; }
material-dialog .dialog-actions {
-webkit-order: 2;
-ms-flex-order: 2;
order: 2;
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
display: -webkit-box;
display: -webkit-flex;
display: -moz-box;
display: -moz-flex;
display: -ms-flexbox;
display: flex;
-webkit-box-align: center;
-ms-flex-align: center;
-webkit-align-items: center;
-moz-align-items: center;
align-items: center;
-webkit-box-pack: end;
-ms-flex-pack: end;
-webkit-justify-content: flex-end;
-moz-justify-content: flex-end;
justify-content: flex-end;
padding: 8px 16px;
min-height: 40px; }
material-dialog .dialog-actions > * {
margin-left: 8px; }
material-dialog.dialog-content-overflow .dialog-actions {
border-top: 1px solid #eee; }
material-divider {
display: block;
border-top: 1px solid rgba(0, 0, 0, 0.12) !important;
margin: 0; }
material-divider[inset] {
margin-left: 80px; }
material-checkbox {
display: block;
margin: 15px;
white-space: nowrap;
cursor: pointer;
outline: none; }
material-checkbox .material-container {
position: relative;
top: 4px;
display: inline-block;
width: 18px;
height: 18px; }
material-checkbox .material-container .material-ripple-container {
position: absolute;
display: block;
width: 54px;
height: 54px;
left: -18px;
top: -18px; }
material-checkbox .material-container .material-ripple {
color: #0f9d58; }
material-checkbox.material-checked .material-ripple {
color: #5a5f5a; }
material-checkbox .circle {
border-radius: 50%; }
material-checkbox .material-icon {
-webkit-transition: 240ms;
transition: 240ms;
position: absolute;
top: 0;
left: 0;
width: 18px;
height: 18px;
border: 2px solid #6D6D6D;
border-radius: 2px; }
material-checkbox.material-checked .material-icon {
border-color: #4CAF50;
background: #4CAF50; }
material-checkbox[disabled] {
cursor: no-drop; }
material-checkbox[disabled] .material-icon {
border-color: #a0a0a0; }
material-checkbox[disabled].material-checked .material-icon {
background: #a0a0a0; }
material-checkbox:focus .material-label {
border-color: black; }
material-checkbox.material-checked .material-icon:after {
-webkit-transform: rotate(45deg);
transform: rotate(45deg);
position: absolute;
left: 4px;
display: table;
width: 6px;
height: 12px;
border: 2px solid #eeeeee;
border-top: 0;
border-left: 0;
content: ' '; }
material-checkbox .material-label {
border: 1px dotted transparent;
position: relative;
display: inline-block;
margin-left: 10px;
vertical-align: middle;
white-space: normal;
pointer-events: none; }
material-card {
display: block;
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
box-shadow: 0px 2px 5px 0 rgba(0, 0, 0, 0.26);
margin: 8px;
padding: 8px;
border-radius: 2px; }
material-card .material-card-image {
display: block;
width: 100%;
border-radius: 2px 2px 0 0; }
material-radio-button, .material-switch-thumb {
display: block;
margin: 15px;
white-space: nowrap;
cursor: pointer; }
material-radio-button input, .material-switch-thumb input {
display: none; }
material-radio-button .material-container, .material-switch-thumb .material-container {
position: relative;
top: 4px;
display: inline-block;
width: 16px;
height: 16px;
cursor: pointer; }
material-radio-button .material-container .material-ripple-container, .material-switch-thumb .material-container .material-ripple-container {
position: absolute;
display: block;
width: 48px;
height: 48px;
left: -16px;
top: -16px; }
material-radio-button .material-container .material-ripple, .material-switch-thumb .material-container .material-ripple {
color: #0f9d58; }
material-radio-button .material-off, .material-switch-thumb .material-off {
position: absolute;
top: 0px;
left: 0px;
width: 16px;
height: 16px;
border: solid 2px;
border-color: #5a5a5a;
border-radius: 50%; }
material-radio-button .material-on, .material-switch-thumb .material-on {
position: absolute;
top: 0;
left: 0;
width: 16px;
height: 16px;
border-radius: 50%;
background-color: #0f9d58;
transition: -webkit-transform ease 0.28s;
transition: transform ease 0.28s;
-webkit-transform: scale(0);
transform: scale(0); }
material-radio-button.material-checked .material-on, .material-switch-thumb.material-checked .material-on {
-webkit-transform: scale(1.1);
transform: scale(1.1); }
material-radio-button.material-checked .material-ink-ripple, .material-switch-thumb.material-checked .material-ink-ripple {
color: #119957; }
material-radio-button .material-label, .material-switch-thumb .material-label {
position: relative;
display: inline-block;
margin-left: 10px;
vertical-align: middle;
white-space: normal;
pointer-events: none;
width: auto; }
material-radio-button .circle, .material-switch-thumb .circle {
border-radius: 50%; }
material-radio-group {
border: 1px dotted transparent;
display: block;
outline: none; }
material-radio-group:focus {
border-color: #111111; }
material-sidenav {
position: absolute;
width: 304px;
bottom: 0;
background-color: white;
-webkit-transition: -webkit-transform 0.3s ease-in-out;
transition: transform 0.3s ease-in-out; }
.material-sidenav-left, material-sidenav {
left: 0;
top: 0;
-webkit-transform: translate3d(-100%, 0, 0);
transform: translate3d(-100%, 0, 0); }
.material-sidenav-left.open, material-sidenav.open {
-webkit-transform: translate3d(0%, 0, 0);
transform: translate3d(0%, 0, 0);
z-index: 8; }
.material-sidenav-right {
left: 100%;
top: 0;
-webkit-transform: translate3d(100%, 0, 0);
transform: translate3d(100%, 0, 0); }
.material-sidenav-right.open {
-webkit-transform: translate3d(-100%, 0, 0);
transform: translate3d(-100%, 0, 0);
z-index: 8; }
@media (min-width: 960px) {
material-sidenav {
position: static;
-webkit-transform: translate3d(0, 0, 0) !important;
transform: translate3d(0, 0, 0) !important; }
.material-sidenav-backdrop {
display: none !important; } }
@media (max-width: 416px) {
material-sidenav {
max-width: 75%; } }
@-webkit-keyframes sliderFocusThumb {
0% {
opacity: 0;
-webkit-transform: scale(0);
transform: scale(0); }
50% {
-webkit-transform: scale(1);
transform: scale(1);
opacity: 1; }
100% {
opacity: 0; } }
@keyframes sliderFocusThumb {
0% {
opacity: 0;
-webkit-transform: scale(0);
transform: scale(0); }
50% {
-webkit-transform: scale(1);
transform: scale(1);
opacity: 1; }
100% {
opacity: 0; } }
material-slider {
height: 48px;
position: relative;
display: block;
margin-left: 4px;
margin-right: 4px;
/**
* Track
*/
/**
* Slider thumb
*/
/* The sign that's focused in discrete mode */
/**
* The border/background that comes in when focused in non-discrete mode
*/
/* Don't animate left/right while panning */ }
material-slider .slider-track-container {
width: 100%;
position: absolute;
top: 23px;
height: 2px; }
material-slider .slider-track {
position: absolute;
left: 0;
right: 0;
height: 100%;
background: #C8C8C8; }
material-slider .slider-track-fill {
background: #03A9F4;
-webkit-transition: width 0.05s linear;
transition: width 0.05s linear; }
material-slider .slider-track-ticks {
position: absolute;
left: 0;
right: 0;
height: 100%; }
material-slider .slider-thumb-container {
position: absolute;
left: 0;
top: 0;
-webkit-transform: translate3d(0, 0, 0);
transform: translate3d(0, 0, 0);
transition: transform 0.1s linear;
-webkit-transition: -webkit-transform 0.1s linear; }
material-slider .slider-thumb {
z-index: 1;
position: absolute;
left: -19px;
top: 5px;
width: 38px;
height: 38px;
border-radius: 38px;
-webkit-transform: scale(0.5);
transform: scale(0.5);
-webkit-transition: all 0.1s linear;
transition: all 0.1s linear; }
material-slider .slider-thumb:after {
content: '';
position: absolute;
left: 3px;
top: 3px;
width: 32px;
height: 32px;
border-radius: 32px;
border: 3px solid #03a9f4;
background-color: #03a9f4; }
material-slider .slider-sign {
/* Center the children (slider-thumb-text) */
display: -webkit-box;
display: -webkit-flex;
display: -moz-box;
display: -moz-flex;
display: -ms-flexbox;
display: flex;
-webkit-box-align: center;
-ms-flex-align: center;
-webkit-align-items: center;
-moz-align-items: center;
align-items: center;
-webkit-box-pack: center;
-ms-flex-pack: center;
-webkit-justify-content: center;
-moz-justify-content: center;
justify-content: center;
position: absolute;
left: -14px;
top: -20px;
width: 28px;
height: 28px;
border-radius: 28px;
background-color: #03a9f4;
-webkit-transform: scale(0.4) translate3d(0, 70px, 0);
transform: scale(0.4) translate3d(0, 70px, 0);
-webkit-transition: all 0.2s ease-in-out;
transition: all 0.2s ease-in-out;
/* The arrow pointing down under the sign */ }
material-slider .slider-sign:after {
position: absolute;
content: '';
left: -0px;
border-radius: 16px;
top: 19px;
border-left: 14px solid transparent;
border-right: 14px solid transparent;
border-top: 16px solid #03a9f4;
opacity: 0;
-webkit-transform: translate3d(0, -8px, 0);
transform: translate3d(0, -8px, 0);
-webkit-transition: all 0.2s ease-in-out;
transition: all 0.2s ease-in-out; }
material-slider .slider-sign .slider-thumb-text {
z-index: 1;
color: white;
font-size: 12px;
font-weight: bold; }
material-slider .slider-focus-thumb {
position: absolute;
left: -24px;
top: 0px;
width: 48px;
height: 48px;
border-radius: 48px;
display: none;
opacity: 0;
background-color: #C0C0C0;
-webkit-animation: sliderFocusThumb 0.4s linear;
animation: sliderFocusThumb 0.4s linear; }
material-slider .slider-focus-ring {
position: absolute;
left: -24px;
top: 0px;
width: 48px;
height: 48px;
border-radius: 48px;
border: 2px solid #D6D6D6;
background-color: transparent;
-webkit-transform: scale(0);
transform: scale(0);
-webkit-transition: all 0.2s linear;
transition: all 0.2s linear; }
material-slider .slider-disabled-thumb {
position: absolute;
left: -22px;
top: 2px;
width: 44px;
height: 44px;
border-radius: 44px;
-webkit-transform: scale(0.35);
transform: scale(0.35);
border: 6px solid white;
display: none; }
material-slider.slider-min .slider-thumb:after {
background-color: white; }
material-slider.slider-min .slider-sign {
opacity: 0; }
material-slider:focus {
outline: none; }
material-slider.panning .slider-thumb-container, material-slider.panning .slider-track-fill {
-webkit-transition: none;
transition: none; }
material-slider:not([discrete]) {
/* Hide the sign and ticks in non-discrete mode */ }
material-slider:not([discrete]) .slider-track-ticks, material-slider:not([discrete]) .slider-sign {
display: none; }
material-slider:not([discrete]):not([disabled]):hover .slider-thumb {
-webkit-transform: scale(0.6);
transform: scale(0.6); }
material-slider:not([discrete]):not([disabled]):focus .slider-focus-thumb, material-slider:not([discrete]):not([disabled]).active .slider-focus-thumb {
display: block; }
material-slider:not([discrete]):not([disabled]):focus .slider-focus-ring, material-slider:not([discrete]):not([disabled]).active .slider-focus-ring {
-webkit-transform: scale(1);
transform: scale(1); }
material-slider:not([discrete]):not([disabled]):focus .slider-thumb, material-slider:not([discrete]):not([disabled]).active .slider-thumb {
-webkit-transform: scale(0.85);
transform: scale(0.85); }
material-slider[discrete] {
/* Hide the focus thumb in discrete mode */ }
material-slider[discrete] .slider-focus-thumb, material-slider[discrete] .slider-focus-ring {
display: none; }
material-slider[discrete]:not([disabled]):focus .slider-sign, material-slider[discrete]:not([disabled]):focus .slider-sign:after, material-slider[discrete]:not([disabled]).active .slider-sign, material-slider[discrete]:not([disabled]).active .slider-sign:after {
opacity: 1;
-webkit-transform: translate3d(0, 0, 0) scale(1);
transform: translate3d(0, 0, 0) scale(1); }
material-slider[disabled] .slider-track-fill {
display: none; }
material-slider[disabled] .slider-thumb {
-webkit-transform: scale(0.35);
transform: scale(0.35); }
material-slider[disabled] .slider-thumb:after {
border-color: #BEBEBE; }
material-slider[disabled]:not(.slider-min) .slider-thumb:after {
background-color: #BEBEBE; }
material-slider[disabled] .slider-disabled-thumb {
display: block; }
@-webkit-keyframes subheaderStickyHoverIn {
0% {
box-shadow: 0 0 0 0 transparent; }
100% {
box-shadow: 0px 2px 4px 0 rgba(0, 0, 0, 0.16); } }
@keyframes subheaderStickyHoverIn {
0% {
box-shadow: 0 0 0 0 transparent; }
100% {
box-shadow: 0px 2px 4px 0 rgba(0, 0, 0, 0.16); } }
@-webkit-keyframes subheaderStickyHoverOut {
0% {
box-shadow: 0px 2px 4px 0 rgba(0, 0, 0, 0.16); }
100% {
box-shadow: 0 0 0 0 transparent; } }
@keyframes subheaderStickyHoverOut {
0% {
box-shadow: 0px 2px 4px 0 rgba(0, 0, 0, 0.16); }
100% {
box-shadow: 0 0 0 0 transparent; } }
.material-subheader {
color: #999;
display: block;
font-size: 0.9em;
font-weight: 400;
line-height: 1em;
padding: 16px 0px 16px 16px;
margin: 0 0 0 0;
margin-right: 16px;
background-color: white;
position: relative; }
.material-subheader.material-subheader-colored {
color: #03a9f4; }
.material-subheader:not(.sticky-no-effect) {
-webkit-transition: 0.2s ease-out margin;
transition: 0.2s ease-out margin; }
.material-subheader:not(.sticky-no-effect):after {
position: absolute;
left: 0;
bottom: 0;
top: 0;
right: -16px;
content: ''; }
.material-subheader:not(.sticky-no-effect)[sticky-state="active"] {
margin-top: -2px; }
.material-subheader:not(.sticky-no-effect):not(.material-sticky-clone)[sticky-prev-state="active"]:after {
-webkit-animation: subheaderStickyHoverOut 0.3s ease-out both;
animation: subheaderStickyHoverOut 0.3s ease-out both; }
.material-sticky-clone {
z-index: 1;
top: 0;
left: 0;
right: 0;
position: absolute;
-webkit-transform: translate3d(-9999px, -9999px, 0);
transform: translate3d(-9999px, -9999px, 0); }
.material-sticky-clone[sticky-state="active"] {
-webkit-transform: translate3d(0, 0, 0);
transform: translate3d(0, 0, 0); }
.material-sticky-clone[sticky-state="active"]:not(.sticky-no-effect):after {
-webkit-animation: subheaderStickyHoverIn 0.3s ease-out both;
animation: subheaderStickyHoverIn 0.3s ease-out both; }
material-switch {
display: block;
position: relative;
height: 24px;
margin: 8px;
display: -webkit-box;
display: -webkit-flex;
display: -moz-box;
display: -moz-flex;
display: -ms-flexbox;
display: flex;
-webkit-box-align: center;
-ms-flex-align: center;
-webkit-align-items: center;
-moz-align-items: center;
align-items: center;
/* used also in _radio-button.scss */ }
material-switch .material-switch-bar {
position: absolute;
left: 16px;
top: 12px;
width: 32px;
height: 1px;
background-color: #5a5a5a;
pointer-events: none; }
material-switch .material-switch-thumb {
position: absolute;
margin: 0;
left: 0;
top: 0;
outline: none; }
material-switch .material-switch-thumb .material-container {
position: absolute;
-webkit-transition: -webkit-transform 0.2s linear;
transition: transform 0.2s linear;
-webkit-transform: translate3d(0, 0, 0);
transform: translate3d(0, 0, 0); }
material-switch .material-switch-thumb.material-checked .material-container {
-webkit-transform: translate3d(48px, 0, 0);
transform: translate3d(48px, 0, 0); }
material-switch .material-switch-thumb:focus .material-label {
border: 1px dotted black; }
material-switch .material-switch-thumb .material-label {
margin-left: 72px; }
label {
display: block;
font-size: 0.75em; }
textarea, input[type="text"], input[type="password"], input[type="datetime"], input[type="datetime-local"], input[type="date"], input[type="month"], input[type="time"], input[type="week"], input[type="number"], input[type="email"], input[type="url"], input[type="search"], input[type="tel"], input[type="color"] {
display: block;
border-width: 0 0 1px 0;
padding-top: 2px;
line-height: 26px; }
textarea:focus, input[type="text"]:focus, input[type="password"]:focus, input[type="datetime"]:focus, input[type="datetime-local"]:focus, input[type="date"]:focus, input[type="month"]:focus, input[type="time"]:focus, input[type="week"]:focus, input[type="number"]:focus, input[type="email"]:focus, input[type="url"]:focus, input[type="search"]:focus, input[type="tel"]:focus, input[type="color"]:focus {
outline: 0; }
input, textarea {
background: none; }
input:-ms-input-placeholder, textarea:-ms-input-placeholder {
color: #a4a4a4; }
input::-webkit-input-placeholder, textarea::-webkit-input-placeholder {
color: #a4a4a4; }
material-input-group, .material-input-group {
margin: 10px 0 10px 0;
position: relative;
display: block; }
material-input-group label, .material-input-group label {
z-index: 1;
pointer-events: none;
-webkit-font-smoothing: antialiased; }
material-input-group label:hover, .material-input-group label:hover {
cursor: text; }
material-input-group label, .material-input-group label {
color: #999;
-webkit-transform: translate3d(0, 22px, 0);
transform: translate3d(0, 22px, 0);
-webkit-transform-origin: left center;
transform-origin: left center;
-webkit-transition: all ease-out 150ms;
transition: all ease-out 150ms;
-webkit-transition: all ease-out 150ms;
transition: all ease-out 150ms; }
material-input-group input, material-input-group textarea, .material-input-group input, .material-input-group textarea {
color: #2f2f2f;
border-color: #c7c7c7;
border-bottom-width: 1px;
-webkit-transition: all ease-out 150ms;
transition: all ease-out 150ms; }
material-input-group.material-input-focused label, .material-input-group.material-input-focused label {
color: #EE008B;
-webkit-transform: translate3d(0, 4px, 0) scale(0.75);
transform: translate3d(0, 4px, 0) scale(0.75); }
material-input-group.material-input-focused input, material-input-group.material-input-focused textarea, .material-input-group.material-input-focused input, .material-input-group.material-input-focused textarea {
color: #2f2f2f;
border-color: #EE008B;
border-bottom-width: 2px; }
material-input-group.material-input-has-value label, .material-input-group.material-input-has-value label {
color: #EE008B;
-webkit-transform: translate3d(0, 4px, 0) scale(0.75);
transform: translate3d(0, 4px, 0) scale(0.75); }
material-input-group.material-input-has-value:not(.material-input-focused) label, .material-input-group.material-input-has-value:not(.material-input-focused) label {
color: #999;
-webkit-transform: translate3d(0, 4px, 0) scale(0.75);
transform: translate3d(0, 4px, 0) scale(0.75); }
material-input-group[disabled] input, material-input-group[disabled] textarea, .material-input-group[disabled] input, .material-input-group[disabled] textarea {
color: #bcbcbc;
border-color: #bcbcbc;
border-bottom-width: 0px; }
material-input-group[disabled] input, material-input-group[disabled] textarea, .material-input-group[disabled] input, .material-input-group[disabled] textarea {
background-image: linear-gradient(to right, #cfcfcf 0%, #cfcfcf 50%, rgba(0, 0, 0, 0) 0%);
background-position: 0 bottom;
background-size: 2px 1px;
background-repeat: repeat-x;
background-size: 3px 1px;
pointer-events: none;
tabindex: -1; }
material-input-group[disabled] label, .material-input-group[disabled] label {
color: #b0b0b0;
-webkit-transform: translate3d(0, 4px, 0) scale(0.75);
transform: translate3d(0, 4px, 0) scale(0.75); }
material-input-group[disabled] *:not(.material-input-has-value) label, .material-input-group[disabled] *:not(.material-input-has-value) label {
color: #b0b0b0;
-webkit-transform: translate3d(0, 22px, 0);
transform: translate3d(0, 22px, 0);
-webkit-transform-origin: left center;
transform-origin: left center;
-webkit-transition: all ease-out 150ms;
transition: all ease-out 150ms; }
.dark material-input-group label, .dark .material-input-group label {
color: #5d5d5d;
-webkit-transform: translate3d(0, 22px, 0);
transform: translate3d(0, 22px, 0);
-webkit-transform-origin: left center;
transform-origin: left center;
-webkit-transition: all ease-out 150ms;
transition: all ease-out 150ms;
-webkit-transition: all ease-out 150ms;
transition: all ease-out 150ms; }
.dark material-input-group input, .dark material-input-group textarea, .dark .material-input-group input, .dark .material-input-group textarea {
color: #d6d6d6;
border-color: #4c4c4c;
border-bottom-width: 1px;
-webkit-transition: all ease-out 150ms;
transition: all ease-out 150ms; }
.dark material-input-group.material-input-focused label, .dark .material-input-group.material-input-focused label {
color: #1194f6;
-webkit-transform: translate3d(0, 4px, 0) scale(0.75);
transform: translate3d(0, 4px, 0) scale(0.75); }
.dark material-input-group.material-input-focused input, .dark material-input-group.material-input-focused textarea, .dark .material-input-group.material-input-focused input, .dark .material-input-group.material-input-focused textarea {
color: #d6d6d6;
border-color: #1194f6;
border-bottom-width: 2px; }
.dark material-input-group.material-input-has-value label, .dark .material-input-group.material-input-has-value label {
color: #1194f6;
-webkit-transform: translate3d(0, 4px, 0) scale(0.75);
transform: translate3d(0, 4px, 0) scale(0.75); }
.dark material-input-group.material-input-has-value:not(.material-input-focused) label, .dark .material-input-group.material-input-has-value:not(.material-input-focused) label {
color: #5d5d5d;
-webkit-transform: translate3d(0, 4px, 0) scale(0.75);
transform: translate3d(0, 4px, 0) scale(0.75); }
.dark material-input-group[disabled] input, .dark material-input-group[disabled] textarea, .dark .material-input-group[disabled] input, .dark .material-input-group[disabled] textarea {
color: #5d5d5d;
border-color: #333333;
border-bottom-width: 0px; }
.dark material-input-group[disabled] input, .dark material-input-group[disabled] textarea, .dark .material-input-group[disabled] input, .dark .material-input-group[disabled] textarea {
background-image: linear-gradient(to right, #525252 0%, #525252 50%, rgba(0, 0, 0, 0) 0%);
background-position: 0 bottom;
background-size: 2px 1px;
background-repeat: repeat-x;
background-size: 3px 1px;
pointer-events: none;
tabindex: -1; }
.dark material-input-group[disabled] label, .dark .material-input-group[disabled] label {
color: #858585;
-webkit-transform: translate3d(0, 4px, 0) scale(0.75);
transform: translate3d(0, 4px, 0) scale(0.75); }
.dark material-input-group[disabled] *:not(.material-input-has-value) label, .dark .material-input-group[disabled] *:not(.material-input-has-value) label {
color: #858585;
-webkit-transform: translate3d(0, 22px, 0);
transform: translate3d(0, 22px, 0);
-webkit-transform-origin: left center;
transform-origin: left center;
-webkit-transition: all ease-out 150ms;
transition: all ease-out 150ms; }
.theme-dark-tff {
background-color: #333 !important;
color: #999 !important; }
material-toast {
display: -webkit-box;
display: -webkit-flex;
display: -moz-box;
display: -moz-flex;
display: -ms-flexbox;
display: flex;
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
-webkit-box-align: center;
-ms-flex-align: center;
-webkit-align-items: center;
-moz-align-items: center;
align-items: center;
background: #323232;
color: #f1f1f1;
min-height: 48px;
padding-left: 24px;
padding-right: 24px;
height: 24px;
box-shadow: 0 2px 5px 0 rgba(0, 0, 0, 0.26);
border-radius: 2px;
font-size: 14px;
cursor: default;
position: fixed;
max-width: 879px;
max-height: 40px;
z-index: 9;
opacity: 1;
-webkit-transform: translate3d(0, 0, 0) rotateZ(0deg);
transform: translate3d(0, 0, 0) rotateZ(0deg);
-webkit-transition: 0.2s linear;
transition: 0.2s linear;
transition-property: transform, opacity;
-webkit-transition-property: -webkit-transform, opacity;
/* Transition differently when swiping */ }
material-toast material-button {
color: white; }
material-toast.capsule {
border-radius: 24px; }
material-toast.swipeleft, material-toast.swiperight, material-toast.swipeup, material-toast.swipedown {
-webkit-transition: 0.15s ease-out;
transition: 0.15s ease-out; }
material-toast.ng-enter {
-webkit-transform: translate3d(0, 100%, 0);
transform: translate3d(0, 100%, 0);
opacity: 0; }
material-toast.ng-enter.top {
-webkit-transform: translate3d(0, -100%, 0);
transform: translate3d(0, -100%, 0); }
material-toast.ng-enter.ng-enter-active {
-webkit-transform: translate3d(0, 0, 0);
transform: translate3d(0, 0, 0);
opacity: 1; }
material-toast.ng-leave.ng-leave-active {
opacity: 0;
-webkit-transform: translate3d(0, 100%, 0);
transform: translate3d(0, 100%, 0); }
material-toast.ng-leave.ng-leave-active.top {
-webkit-transform: translate3d(0, -100%, 0);
transform: translate3d(0, -100%, 0); }
material-toast.ng-leave.ng-leave-active.swipeleft {
-webkit-transform: translate3d(-100%, 0, 0);
transform: translate3d(-100%, 0, 0); }
material-toast.ng-leave.ng-leave-active.swiperight {
-webkit-transform: translate3d(100%, 0, 0);
transform: translate3d(100%, 0, 0); }
.toast-action {
line-height: 19px;
color: #eeff41;
padding-left: 24px;
cursor: pointer;
text-transform: uppercase;
float: right; }
@media (max-width: 600px) {
material-toast {
left: 0;
right: 0;
width: 100%;
max-width: 100%;
min-width: 0;
border-radius: 0;
bottom: 0; }
material-toast.top {
bottom: auto;
top: 0; } }
@media (min-width: 600px) {
material-toast {
min-width: 288px;
/*
* When the toast doesn't take up the whole screen,
* make it rotate when the user swipes it away
*/ }
material-toast.bottom {
bottom: 8px; }
material-toast.left {
left: 8px; }
material-toast.right {
right: 8px; }
material-toast.top {
top: 8px; }
material-toast.ng-leave.ng-leave-active.swipeleft {
-webkit-transform: translate3d(-100%, 25%, 0) rotateZ(-15deg);
transform: translate3d(-100%, 25%, 0) rotateZ(-15deg); }
material-toast.ng-leave.ng-leave-active.swiperight {
-webkit-transform: translate3d(100%, 25%, 0) rotateZ(15deg);
transform: translate3d(100%, 25%, 0) rotateZ(15deg); }
material-toast.ng-leave.ng-leave-active.top.swipeleft {
-webkit-transform: translate3d(-100%, 0, 0) rotateZ(-15deg);
transform: translate3d(-100%, 0, 0) rotateZ(-15deg); }
material-toast.ng-leave.ng-leave-active.top.swiperight {
-webkit-transform: translate3d(100%, 0, 0) rotateZ(15deg);
transform: translate3d(100%, 0, 0) rotateZ(15deg); } }
material-toolbar {
display: -webkit-box;
display: -webkit-flex;
display: -moz-box;
display: -moz-flex;
display: -ms-flexbox;
display: flex;
-webkit-box-direction: normal;
-webkit-box-orient: vertical;
-webkit-flex-direction: column;
-moz-flex-direction: column;
-ms-flex-direction: column;
flex-direction: column;
position: relative;
z-index: 2;
font-size: 1.3em;
min-height: 64px;
width: 100%; }
material-toolbar.material-tall {
height: 192px; }
material-toolbar.material-medium-tall {
height: 88px; }
material-toolbar.material-medium-tall .material-toolbar-tools {
height: 48px; }
material-toolbar .material-indent {
margin-left: 64px; }
.material-toolbar-tools {
display: -webkit-box;
display: -webkit-flex;
display: -moz-box;
display: -moz-flex;
display: -ms-flexbox;
display: flex;
-webkit-box-align: center;
-ms-flex-align: center;
-webkit-align-items: center;
-moz-align-items: center;
align-items: center;
-webkit-box-direction: normal;
-webkit-box-orient: horizontal;
-webkit-flex-direction: row;
-moz-flex-direction: row;
-ms-flex-direction: row;
flex-direction: row;
width: 100%;
height: 64px;
font-size: inherit;
font-weight: normal;
padding: 0 10px;
margin: 0; }
.material-toolbar-tools > * {
font-size: inherit;
margin: 0 8px; }
.material-toolbar-tools h2, .material-toolbar-tools h3 {
font-weight: normal; }
.material-toolbar-tools a {
color: inherit;
text-decoration: none; }
.material-toolbar-tools material-button {
font-size: 16px; }
@-webkit-keyframes tooltipBackgroundShow {
0% {
-webkit-transform: scale(0.2);
transform: scale(0.2);
opacity: 0.25; }
50% {
opacity: 1; }
100% {
-webkit-transform: scale(1);
transform: scale(1);
opacity: 1; } }
@keyframes tooltipBackgroundShow {
0% {
-webkit-transform: scale(0.2);
transform: scale(0.2);
opacity: 0.25; }
50% {
opacity: 1; }
100% {
-webkit-transform: scale(1);
transform: scale(1);
opacity: 1; } }
@-webkit-keyframes tooltipBackgroundHide {
0% {
opacity: 1; }
100% {
opacity: 0; } }
@keyframes tooltipBackgroundHide {
0% {
opacity: 1; }
100% {
opacity: 0; } }
material-tooltip {
position: absolute;
font-size: 14px;
z-index: 6;
overflow: hidden;
pointer-events: none;
color: white;
border-radius: 4px;
/**
* Depending on the tooltip's size as a multiple of 32 (set by JS),
* change the background's animation duration.
* The larger the tooltip, the less time the background should take to ripple outwards.
*/ }
material-tooltip[tooltip-direction="bottom"] {
-webkit-transform: translate3d(0, -30%, 0);
transform: translate3d(0, -30%, 0);
margin-top: 8px; }
material-tooltip[tooltip-direction="top"] {
-webkit-transform: translate3d(0, 30%, 0);
transform: translate3d(0, 30%, 0);
margin-bottom: 8px; }
material-tooltip .tooltip-background {
background: #737373;
position: absolute;
left: 50%;
width: 256px;
height: 256px;
margin-left: -128px;
margin-top: -128px;
border-radius: 256px;
opacity: 0.25;
-webkit-transform: scale(0.2);
transform: scale(0.2); }
material-tooltip .tooltip-content {
max-width: 240px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
padding: 8px;
background: transparent;
opacity: 0.3;
-webkit-transition: inherit;
transition: inherit; }
material-tooltip.tooltip-show, material-tooltip.tooltip-hide {
-webkit-transition: 0.2s ease-out;
transition: 0.2s ease-out;
transition-property: transform, opacity;
-webkit-transition-property: -webkit-transform, opacity; }
material-tooltip.tooltip-show {
pointer-events: auto;
-webkit-transform: translate3d(0, 0, 0);
transform: translate3d(0, 0, 0); }
material-tooltip.tooltip-show .tooltip-background {
-webkit-transform: scale(1);
transform: scale(1);
opacity: 1;
-webkit-animation: tooltipBackgroundShow linear;
animation: tooltipBackgroundShow linear; }
material-tooltip.tooltip-show .tooltip-content {
opacity: 0.99; }
material-tooltip.tooltip-hide .tooltip-background {
-webkit-transform: scale(1);
transform: scale(1);
opacity: 0;
-webkit-animation: tooltipBackgroundHide 0.2s linear;
animation: tooltipBackgroundHide 0.2s linear; }
material-tooltip[width-32="1"].tooltip-show .tooltip-background {
animation-duration: 900ms;
-webkit-animation-duration: 900ms; }
material-tooltip[width-32="2"].tooltip-show .tooltip-background {
animation-duration: 800ms;
-webkit-animation-duration: 800ms; }
material-tooltip[width-32="3"].tooltip-show .tooltip-background {
animation-duration: 700ms;
-webkit-animation-duration: 700ms; }
material-tooltip[width-32="4"].tooltip-show .tooltip-background {
animation-duration: 600ms;
-webkit-animation-duration: 600ms; }
material-tooltip[width-32="5"].tooltip-show .tooltip-background {
animation-duration: 500ms;
-webkit-animation-duration: 500ms; }
material-tooltip[width-32="6"].tooltip-show .tooltip-background {
animation-duration: 400ms;
-webkit-animation-duration: 400ms; }
material-tooltip[width-32="7"].tooltip-show .tooltip-background {
animation-duration: 300ms;
-webkit-animation-duration: 300ms; }
material-tooltip[width-32="8"].tooltip-show .tooltip-background {
animation-duration: 200ms;
-webkit-animation-duration: 200ms; }
material-tabs {
display: block;
width: 100%;
font-weight: 500; }
.tabs-header {
width: 100%;
height: 48px;
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
position: relative;
background-color: #4285F4; }
.tab-paginator {
z-index: 1;
margin-right: -2px;
display: -webkit-box;
display: -webkit-flex;
display: -moz-box;
display: -moz-flex;
display: -ms-flexbox;
display: flex;
-webkit-box-pack: center;
-ms-flex-pack: center;
-webkit-justify-content: center;
-moz-justify-content: center;
justify-content: center;
-webkit-box-align: center;
-ms-flex-align: center;
-webkit-align-items: center;
-moz-align-items: center;
align-items: center;
width: 32px;
min-height: 100%;
cursor: pointer;
background-repeat: no-repeat;
background-position: center center;
position: absolute;
/* TODO Once we have a better way to inline svg images, change this
to use svgs correctly */ }
.tab-paginator.prev {
left: 0; }
.tab-paginator.next {
right: 0; }
.tab-paginator.prev {
background-image: url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4gPCEtLSBHZW5lcmF0b3I6IEFkb2JlIElsbHVzdHJhdG9yIDE3LjEuMCwgU1ZHIEV4cG9ydCBQbHVnLUluIC4gU1ZHIFZlcnNpb246IDYuMDAgQnVpbGQgMCkgIC0tPiA8IURPQ1RZUEUgc3ZnIFBVQkxJQyAiLS8vVzNDLy9EVEQgU1ZHIDEuMS8vRU4iICJodHRwOi8vd3d3LnczLm9yZy9HcmFwaGljcy9TVkcvMS4xL0RURC9zdmcxMS5kdGQiPiA8c3ZnIHZlcnNpb249IjEuMSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgeD0iMHB4IiB5PSIwcHgiIHdpZHRoPSIyNHB4IiBoZWlnaHQ9IjI0cHgiIHZpZXdCb3g9IjAgMCAyNCAyNCIgZW5hYmxlLWJhY2tncm91bmQ9Im5ldyAwIDAgMjQgMjQiIHhtbDpzcGFjZT0icHJlc2VydmUiPiA8ZyBpZD0iSGVhZGVyIj4gPGc+IDxyZWN0IHg9Ii02MTgiIHk9Ii0xMjA4IiBmaWxsPSJub25lIiB3aWR0aD0iMTQwMCIgaGVpZ2h0PSIzNjAwIi8+IDwvZz4gPC9nPiA8ZyBpZD0iTGFiZWwiPiA8L2c+IDxnIGlkPSJJY29uIj4gPGc+IDxwb2x5Z29uIHBvaW50cz0iMTUuNCw3LjQgMTQsNiA4LDEyIDE0LDE4IDE1LjQsMTYuNiAxMC44LDEyIAkJIiBzdHlsZT0iZmlsbDp3aGl0ZTsiLz4gPHJlY3QgZmlsbD0ibm9uZSIgd2lkdGg9IjI0IiBoZWlnaHQ9IjI0Ii8+IDwvZz4gPC9nPiA8ZyBpZD0iR3JpZCIgZGlzcGxheT0ibm9uZSI+IDxnIGRpc3BsYXk9ImlubGluZSI+IDwvZz4gPC9nPiA8L3N2Zz4NCg=='); }
.tab-paginator.next {
background-image: url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4gPCEtLSBHZW5lcmF0b3I6IEFkb2JlIElsbHVzdHJhdG9yIDE3LjEuMCwgU1ZHIEV4cG9ydCBQbHVnLUluIC4gU1ZHIFZlcnNpb246IDYuMDAgQnVpbGQgMCkgIC0tPiA8IURPQ1RZUEUgc3ZnIFBVQkxJQyAiLS8vVzNDLy9EVEQgU1ZHIDEuMS8vRU4iICJodHRwOi8vd3d3LnczLm9yZy9HcmFwaGljcy9TVkcvMS4xL0RURC9zdmcxMS5kdGQiPiA8c3ZnIHZlcnNpb249IjEuMSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgeD0iMHB4IiB5PSIwcHgiIHdpZHRoPSIyNHB4IiBoZWlnaHQ9IjI0cHgiIHZpZXdCb3g9IjAgMCAyNCAyNCIgZW5hYmxlLWJhY2tncm91bmQ9Im5ldyAwIDAgMjQgMjQiIHhtbDpzcGFjZT0icHJlc2VydmUiPiA8ZyBpZD0iSGVhZGVyIj4gPGc+IDxyZWN0IHg9Ii02MTgiIHk9Ii0xMzM2IiBmaWxsPSJub25lIiB3aWR0aD0iMTQwMCIgaGVpZ2h0PSIzNjAwIi8+IDwvZz4gPC9nPiA8ZyBpZD0iTGFiZWwiPiA8L2c+IDxnIGlkPSJJY29uIj4gPGc+IDxwb2x5Z29uIHBvaW50cz0iMTAsNiA4LjYsNy40IDEzLjIsMTIgOC42LDE2LjYgMTAsMTggMTYsMTIgCQkiIHN0eWxlPSJmaWxsOndoaXRlOyIvPiA8cmVjdCBmaWxsPSJub25lIiB3aWR0aD0iMjQiIGhlaWdodD0iMjQiLz4gPC9nPiA8L2c+IDxnIGlkPSJHcmlkIiBkaXNwbGF5PSJub25lIj4gPGcgZGlzcGxheT0iaW5saW5lIj4gPC9nPiA8L2c+IDwvc3ZnPg0K'); }
/* If `center` justified, change to left-justify if paginating */
material-tabs[center] .tabs-header:not(.tab-paginating) .tabs-header-items {
-webkit-box-pack: center;
-ms-flex-pack: center;
-webkit-justify-content: center;
-moz-justify-content: center;
justify-content: center; }
.tab-paginating .tabs-header-items-container {
left: 32px;
right: 32px; }
.tabs-header-items-container {
overflow: hidden;
position: absolute;
left: 0;
right: 0;
height: 100%;
white-space: nowrap;
font-size: 16px;
font-weight: 500;
text-transform: uppercase;
margin: auto; }
.tabs-header-items-container .tabs-header-items {
display: -webkit-box;
display: -webkit-flex;
display: -moz-box;
display: -moz-flex;
display: -ms-flexbox;
display: flex;
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
-webkit-transition: -webkit-transform 0.2s linear;
transition: transform 0.2s linear;
-webkit-transform: translate3d(0, 0, 0);
transform: translate3d(0, 0, 0);
width: 100%;
height: 100%; }
.tabs-content {
overflow: hidden;
width: 100%; }
.tabs-content .tab-content {
height: 100%; }
material-tabs-ink-bar {
left: 0;
bottom: 0;
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
-webkit-transition: all 0.2s linear;
transition: all 0.2s linear; }
material-tab {
-webkit-box-flex: 1;
-webkit-flex: 1;
-moz-box-flex: 1;
-moz-flex: 1;
-ms-flex: 1;
flex: 1;
display: -webkit-box;
display: -webkit-flex;
display: -moz-box;
display: -moz-flex;
display: -ms-flexbox;
display: flex;
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
-webkit-box-align: center;
-ms-flex-align: center;
-webkit-align-items: center;
-moz-align-items: center;
align-items: center;
-webkit-box-pack: center;
-ms-flex-pack: center;
-webkit-justify-content: center;
-moz-justify-content: center;
justify-content: center;
position: relative;
z-index: 0;
overflow: hidden;
height: 100%;
text-align: center;
cursor: pointer;
min-width: 96px;
border: 1px dotted transparent;
color: #9FC1FB;
-webkit-touch-callout: none;
-webkit-user-select: none;
-khtml-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none; }
material-tab.active {
color: white; }
material-tab[disabled] {
pointer-events: none;
color: #6a6a6a;
opacity: 0.5;
cursor: default; }
material-tab:focus {
border-color: #111111;
outline: none; }
material-tab material-tab-label {
-webkit-box-flex: 1;
-webkit-flex: 1;
-moz-box-flex: 1;
-moz-flex: 1;
-ms-flex: 1;
flex: 1;
z-index: 100;
opacity: 1;
overflow: hidden;
text-overflow: ellipsis; }
material-tab .material-ripple-container {
color: #ffff85; }
shadow {
box-shadow: 0px 3px 2px rgba(0, 0, 0, 0.2); }
material-list {
padding: 8px 0px 8px 0px; }
material-list.with-subheader {
padding-top: 0px; }
material-item-content {
display: -webkit-box;
display: -webkit-flex;
display: -moz-box;
display: -moz-flex;
display: -ms-flexbox;
display: flex;
-webkit-box-align: center;
-ms-flex-align: center;
-webkit-align-items: center;
-moz-align-items: center;
align-items: center;
-webkit-box-direction: normal;
-webkit-box-orient: horizontal;
-webkit-flex-direction: row;
-moz-flex-direction: row;
-ms-flex-direction: row;
flex-direction: row;
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
position: relative;
padding: 0px 0px 0px 0px; }
/**
* The left tile for a list item.
*/
.material-tile-left {
min-width: 56px;
margin-right: -16px; }
/**
* The center content tile for a list item.
*/
.material-tile-content {
-webkit-box-flex: 1;
-webkit-flex: 1;
-moz-box-flex: 1;
-moz-flex: 1;
-ms-flex: 1;
flex: 1;
padding: 16px;
text-overflow: ellipsis; }
.material-tile-content h3 {
margin: 0 0 3px 0;
font-weight: 400;
font-size: 1.1em; }
.material-tile-content h4 {
margin: 0 0 3px 0;
font-weight: 400;
font-size: 0.9em; }
.material-tile-content p {
margin: 0 0 3px 0;
font-size: 0.75em; }
/**
* The right tile for a list item.
*/
.material-tile-right {
padding-right: 0px; }
material-divider {
display: block;
border-top: 1px solid rgba(0, 0, 0, 0.12) !important;
margin: 0; }
material-divider[inset] {
margin-left: 80px; }
.material-whiteframe-z1 {
z-index: 1;
box-shadow: 0px 2px 5px 0 rgba(0, 0, 0, 0.26); }
.material-whiteframe-z2 {
z-index: 2;
box-shadow: 0px 8px 17px rgba(0, 0, 0, 0.2); }
.material-whiteframe-z3 {
z-index: 3;
box-shadow: 0px 17px 50px rgba(0, 0, 0, 0.19); }
.material-whiteframe-z4 {
z-index: 4;
box-shadow: 0px 16px 28px 0 rgba(0, 0, 0, 0.22); }
.material-whiteframe-z5 {
z-index: 5;
box-shadow: 0px 27px 24px 0 rgba(0, 0, 0, 0.2); }
material-linear-progress {
display: block;
width: 100%;
height: 5px; }
material-linear-progress .container {
overflow: hidden;
position: relative;
height: 5px;
background-color: #d0d9ff;
top: 5px;
-webkit-transform: translate(0, 5px) scale(1, 0);
transform: translate(0, 5px) scale(1, 0);
-webkit-transition: all 0.3s linear;
transition: all 0.3s linear; }
material-linear-progress .container.ready {
-webkit-transform: translate(0, 0) scale(1, 1);
transform: translate(0, 0) scale(1, 1); }
material-linear-progress .bar {
height: 5px;
background-color: #5677fc;
position: absolute;
width: 100%; }
material-linear-progress .bar1, material-linear-progress .bar2 {
-webkit-transition: all 0.2s linear;
transition: all 0.2s linear; }
material-linear-progress[mode=determinate] .bar1 {
display: none; }
material-linear-progress[mode=indeterminate] .bar1 {
-webkit-animation: indeterminate1 4s infinite linear;
animation: indeterminate1 4s infinite linear; }
material-linear-progress[mode=indeterminate] .bar2 {
-webkit-animation: indeterminate2 4s infinite linear;
animation: indeterminate2 4s infinite linear; }
material-linear-progress[mode=buffer] .container {
background-color: transparent; }
material-linear-progress[mode=buffer] .dashed:before {
content: "";
display: block;
height: 5px;
width: 100%;
margin-top: 0px;
position: absolute;
background: radial-gradient(#d0d9ff 0%, #d0d9ff 16%, transparent 42%);
background-color: transparent;
background-size: 10px 10px;
background-position: 0px -23px;
-webkit-animation: buffer 3s infinite linear;
animation: buffer 3s infinite linear; }
material-linear-progress[mode=buffer] .bar1 {
background-color: #d0d9ff; }
material-linear-progress[mode=query] .bar2 {
-webkit-animation: query 0.8s infinite cubic-bezier(0.39, 0.575, 0.565, 1);
animation: query 0.8s infinite cubic-bezier(0.39, 0.575, 0.565, 1); }
@-webkit-keyframes indeterminate1 {
0% {
-webkit-transform: translateX(-25%) scale(0.5, 1);
transform: translateX(-25%) scale(0.5, 1); }
10% {
-webkit-transform: translateX(25%) scale(0.5, 1);
transform: translateX(25%) scale(0.5, 1); }
19.99% {
-webkit-transform: translateX(50%) scale(0, 1);
transform: translateX(50%) scale(0, 1); }
20% {
-webkit-transform: translateX(-37.5%) scale(0.25, 1);
transform: translateX(-37.5%) scale(0.25, 1); }
30% {
-webkit-transform: translateX(37.5%) scale(0.25, 1);
transform: translateX(37.5%) scale(0.25, 1); }
34.99% {
-webkit-transform: translateX(50%) scale(0, 1);
transform: translateX(50%) scale(0, 1); }
36.99% {
-webkit-transform: translateX(50%) scale(0, 1);
transform: translateX(50%) scale(0, 1); }
37% {
-webkit-transform: translateX(-37.5%) scale(0.25, 1);
transform: translateX(-37.5%) scale(0.25, 1); }
47% {
-webkit-transform: translateX(20%) scale(0.25, 1);
transform: translateX(20%) scale(0.25, 1); }
52% {
-webkit-transform: translateX(35%) scale(0.05, 1);
transform: translateX(35%) scale(0.05, 1); }
55% {
-webkit-transform: translateX(35%) scale(0.1, 1);
transform: translateX(35%) scale(0.1, 1); }
58% {
-webkit-transform: translateX(50%) scale(0.1, 1);
transform: translateX(50%) scale(0.1, 1); }
61.99% {
-webkit-transform: translateX(50%) scale(0, 1);
transform: translateX(50%) scale(0, 1); }
69.99% {
-webkit-transform: translateX(50%) scale(0, 1);
transform: translateX(50%) scale(0, 1); }
70% {
-webkit-transform: translateX(-37.5%) scale(0.25, 1);
transform: translateX(-37.5%) scale(0.25, 1); }
80% {
-webkit-transform: translateX(20%) scale(0.25, 1);
transform: translateX(20%) scale(0.25, 1); }
85% {
-webkit-transform: translateX(35%) scale(0.05, 1);
transform: translateX(35%) scale(0.05, 1); }
88% {
-webkit-transform: translateX(35%) scale(0.1, 1);
transform: translateX(35%) scale(0.1, 1); }
91% {
-webkit-transform: translateX(50%) scale(0.1, 1);
transform: translateX(50%) scale(0.1, 1); }
92.99% {
-webkit-transform: translateX(50%) scale(0, 1);
transform: translateX(50%) scale(0, 1); }
93% {
-webkit-transform: translateX(-50%) scale(0, 1);
transform: translateX(-50%) scale(0, 1); }
100% {
-webkit-transform: translateX(-25%) scale(0.5, 1);
transform: translateX(-25%) scale(0.5, 1); } }
@keyframes indeterminate1 {
0% {
-webkit-transform: translateX(-25%) scale(0.5, 1);
transform: translateX(-25%) scale(0.5, 1); }
10% {
-webkit-transform: translateX(25%) scale(0.5, 1);
transform: translateX(25%) scale(0.5, 1); }
19.99% {
-webkit-transform: translateX(50%) scale(0, 1);
transform: translateX(50%) scale(0, 1); }
20% {
-webkit-transform: translateX(-37.5%) scale(0.25, 1);
transform: translateX(-37.5%) scale(0.25, 1); }
30% {
-webkit-transform: translateX(37.5%) scale(0.25, 1);
transform: translateX(37.5%) scale(0.25, 1); }
34.99% {
-webkit-transform: translateX(50%) scale(0, 1);
transform: translateX(50%) scale(0, 1); }
36.99% {
-webkit-transform: translateX(50%) scale(0, 1);
transform: translateX(50%) scale(0, 1); }
37% {
-webkit-transform: translateX(-37.5%) scale(0.25, 1);
transform: translateX(-37.5%) scale(0.25, 1); }
47% {
-webkit-transform: translateX(20%) scale(0.25, 1);
transform: translateX(20%) scale(0.25, 1); }
52% {
-webkit-transform: translateX(35%) scale(0.05, 1);
transform: translateX(35%) scale(0.05, 1); }
55% {
-webkit-transform: translateX(35%) scale(0.1, 1);
transform: translateX(35%) scale(0.1, 1); }
58% {
-webkit-transform: translateX(50%) scale(0.1, 1);
transform: translateX(50%) scale(0.1, 1); }
61.99% {
-webkit-transform: translateX(50%) scale(0, 1);
transform: translateX(50%) scale(0, 1); }
69.99% {
-webkit-transform: translateX(50%) scale(0, 1);
transform: translateX(50%) scale(0, 1); }
70% {
-webkit-transform: translateX(-37.5%) scale(0.25, 1);
transform: translateX(-37.5%) scale(0.25, 1); }
80% {
-webkit-transform: translateX(20%) scale(0.25, 1);
transform: translateX(20%) scale(0.25, 1); }
85% {
-webkit-transform: translateX(35%) scale(0.05, 1);
transform: translateX(35%) scale(0.05, 1); }
88% {
-webkit-transform: translateX(35%) scale(0.1, 1);
transform: translateX(35%) scale(0.1, 1); }
91% {
-webkit-transform: translateX(50%) scale(0.1, 1);
transform: translateX(50%) scale(0.1, 1); }
92.99% {
-webkit-transform: translateX(50%) scale(0, 1);
transform: translateX(50%) scale(0, 1); }
93% {
-webkit-transform: translateX(-50%) scale(0, 1);
transform: translateX(-50%) scale(0, 1); }
100% {
-webkit-transform: translateX(-25%) scale(0.5, 1);
transform: translateX(-25%) scale(0.5, 1); } }
@-webkit-keyframes indeterminate2 {
0% {
-webkit-transform: translateX(-50%) scale(0, 1);
transform: translateX(-50%) scale(0, 1); }
25.99% {
-webkit-transform: translateX(-50%) scale(0, 1);
transform: translateX(-50%) scale(0, 1); }
28% {
-webkit-transform: translateX(-37.5%) scale(0.25, 1);
transform: translateX(-37.5%) scale(0.25, 1); }
38% {
-webkit-transform: translateX(37.5%) scale(0.25, 1);
transform: translateX(37.5%) scale(0.25, 1); }
42.99% {
-webkit-transform: translateX(50%) scale(0, 1);
transform: translateX(50%) scale(0, 1); }
46.99% {
-webkit-transform: translateX(50%) scale(0, 1);
transform: translateX(50%) scale(0, 1); }
49.99% {
-webkit-transform: translateX(50%) scale(0, 1);
transform: translateX(50%) scale(0, 1); }
50% {
-webkit-transform: translateX(-50%) scale(0, 1);
transform: translateX(-50%) scale(0, 1); }
60% {
-webkit-transform: translateX(-25%) scale(0.5, 1);
transform: translateX(-25%) scale(0.5, 1); }
70% {
-webkit-transform: translateX(25%) scale(0.5, 1);
transform: translateX(25%) scale(0.5, 1); }
79.99% {
-webkit-transform: translateX(50%) scale(0, 1);
transform: translateX(50%) scale(0, 1); } }
@keyframes indeterminate2 {
0% {
-webkit-transform: translateX(-50%) scale(0, 1);
transform: translateX(-50%) scale(0, 1); }
25.99% {
-webkit-transform: translateX(-50%) scale(0, 1);
transform: translateX(-50%) scale(0, 1); }
28% {
-webkit-transform: translateX(-37.5%) scale(0.25, 1);
transform: translateX(-37.5%) scale(0.25, 1); }
38% {
-webkit-transform: translateX(37.5%) scale(0.25, 1);
transform: translateX(37.5%) scale(0.25, 1); }
42.99% {
-webkit-transform: translateX(50%) scale(0, 1);
transform: translateX(50%) scale(0, 1); }
46.99% {
-webkit-transform: translateX(50%) scale(0, 1);
transform: translateX(50%) scale(0, 1); }
49.99% {
-webkit-transform: translateX(50%) scale(0, 1);
transform: translateX(50%) scale(0, 1); }
50% {
-webkit-transform: translateX(-50%) scale(0, 1);
transform: translateX(-50%) scale(0, 1); }
60% {
-webkit-transform: translateX(-25%) scale(0.5, 1);
transform: translateX(-25%) scale(0.5, 1); }
70% {
-webkit-transform: translateX(25%) scale(0.5, 1);
transform: translateX(25%) scale(0.5, 1); }
79.99% {
-webkit-transform: translateX(50%) scale(0, 1);
transform: translateX(50%) scale(0, 1); } }
@-webkit-keyframes query {
0% {
opacity: 1;
-webkit-transform: translateX(35%) scale(0.3, 1);
transform: translateX(35%) scale(0.3, 1); }
100% {
opacity: 0;
-webkit-transform: translateX(-50%) scale(0, 1);
transform: translateX(-50%) scale(0, 1); } }
@keyframes query {
0% {
opacity: 1;
-webkit-transform: translateX(35%) scale(0.3, 1);
transform: translateX(35%) scale(0.3, 1); }
100% {
opacity: 0;
-webkit-transform: translateX(-50%) scale(0, 1);
transform: translateX(-50%) scale(0, 1); } }
@-webkit-keyframes buffer {
0% {
opacity: 1;
background-position: 0px -23px; }
50% {
opacity: 0; }
100% {
opacity: 1;
background-position: -200px -23px; } }
@keyframes buffer {
0% {
opacity: 1;
background-position: 0px -23px; }
50% {
opacity: 0; }
100% {
opacity: 1;
background-position: -200px -23px; } }
material-circular-progress {
display: block;
width: 54px;
height: 54px;
background-color: transparent;
border-radius: 50%;
padding: 3px; }
material-circular-progress .wrapper1, material-circular-progress .wrapper2 {
width: 48px;
height: 48px;
position: absolute;
border-radius: 50%; }
material-circular-progress .circle .mask, material-circular-progress .circle .fill, material-circular-progress .circle .shadow {
width: 48px;
height: 48px;
position: absolute;
border-radius: 50%; }
material-circular-progress .circle .mask, material-circular-progress .circle .fill {
-webkit-backface-visibility: hidden;
transition: -webkit-transform 0.3s;
transition: -ms-transform 0.3s;
transition: transform 0.3s; }
material-circular-progress .circle .mask {
clip: rect(0px, 48px, 48px, 24px); }
material-circular-progress .circle .mask .fill {
clip: rect(0px, 24px, 48px, 0px);
background-color: #5677fc; }
material-circular-progress .inset {
width: 36px;
height: 36px;
position: absolute;
margin-left: 6px;
margin-top: 6px;
background-color: white;
border-radius: 50%; }
material-circular-progress[mode=indeterminate] .wrapper1, material-circular-progress[mode=indeterminate] .wrapper2 {
-ms-transform-origin: 50% 50%;
/* IE 9 */
webkit-transform-origin: 50% 50%;
/* Chrome, Safari, Opera */
transform-origin: 50% 50%; }
material-circular-progress[mode=indeterminate] .wrapper1 {
-webkit-animation: indeterminate_rotate1 3s infinite linear;
animation: indeterminate_rotate1 3s infinite linear; }
material-circular-progress[mode=indeterminate] .wrapper2 {
-webkit-animation: indeterminate_rotate2 1.5s infinite linear;
animation: indeterminate_rotate2 1.5s infinite linear; }
material-circular-progress[mode=indeterminate] .fill, material-circular-progress[mode=indeterminate] .mask.full {
-webkit-animation: indeterminate_size_fill 1.5s infinite linear;
animation: indeterminate_size_fill 1.5s infinite linear; }
material-circular-progress[mode=indeterminate] .fill.fix {
-webkit-animation: indeterminate_size_fix 1.5s infinite linear;
animation: indeterminate_size_fix 1.5s infinite linear; }
@-webkit-keyframes indeterminate_rotate1 {
0% {
-webkit-transform: rotate(0deg);
transform: rotate(0deg); }
100% {
-webkit-transform: rotate(360deg);
transform: rotate(360deg); } }
@keyframes indeterminate_rotate1 {
0% {
-webkit-transform: rotate(0deg);
transform: rotate(0deg); }
100% {
-webkit-transform: rotate(360deg);
transform: rotate(360deg); } }
@-webkit-keyframes indeterminate_rotate2 {
0% {
-webkit-transform: rotate(0deg);
transform: rotate(0deg); }
70% {
-webkit-transform: rotate(0deg);
transform: rotate(0deg); }
100% {
-webkit-transform: rotate(360deg);
transform: rotate(360deg); } }
@keyframes indeterminate_rotate2 {
0% {
-webkit-transform: rotate(0deg);
transform: rotate(0deg); }
70% {
-webkit-transform: rotate(0deg);
transform: rotate(0deg); }
100% {
-webkit-transform: rotate(360deg);
transform: rotate(360deg); } }
@-webkit-keyframes indeterminate_size_fill {
0% {
-webkit-transform: rotate(5deg);
transform: rotate(5deg); }
10% {
-webkit-transform: rotate(5deg);
transform: rotate(5deg); }
50% {
-webkit-transform: rotate(135deg);
transform: rotate(135deg); }
70% {
-webkit-transform: rotate(135deg);
transform: rotate(135deg); }
100% {
-webkit-transform: rotate(5deg);
transform: rotate(5deg); } }
@keyframes indeterminate_size_fill {
0% {
-webkit-transform: rotate(5deg);
transform: rotate(5deg); }
10% {
-webkit-transform: rotate(5deg);
transform: rotate(5deg); }
50% {
-webkit-transform: rotate(135deg);
transform: rotate(135deg); }
70% {
-webkit-transform: rotate(135deg);
transform: rotate(135deg); }
100% {
-webkit-transform: rotate(5deg);
transform: rotate(5deg); } }
@-webkit-keyframes indeterminate_size_fix {
0% {
-webkit-transform: rotate(10deg);
transform: rotate(10deg); }
10% {
-webkit-transform: rotate(10deg);
transform: rotate(10deg); }
50% {
-webkit-transform: rotate(270deg);
transform: rotate(270deg); }
70% {
-webkit-transform: rotate(270deg);
transform: rotate(270deg); }
100% {
-webkit-transform: rotate(10deg);
transform: rotate(10deg); } }
@keyframes indeterminate_size_fix {
0% {
-webkit-transform: rotate(10deg);
transform: rotate(10deg); }
10% {
-webkit-transform: rotate(10deg);
transform: rotate(10deg); }
50% {
-webkit-transform: rotate(270deg);
transform: rotate(270deg); }
70% {
-webkit-transform: rotate(270deg);
transform: rotate(270deg); }
100% {
-webkit-transform: rotate(10deg);
transform: rotate(10deg); } }