2014-09-30 12:29:53 +02:00
/ * !
* Angular Material Design
* https : //github.com/angular/material
* @ license MIT
2015-02-04 16:08:32 +01:00
* v0 . 7.1
* /
angular . module ( 'ngMaterial' , [ "ng" , "ngAnimate" , "ngAria" , "material.core" , "material.core.theming.palette" , "material.core.theming" , "material.components.backdrop" , "material.components.bottomSheet" , "material.components.button" , "material.components.card" , "material.components.checkbox" , "material.components.content" , "material.components.dialog" , "material.components.divider" , "material.components.icon" , "material.components.input" , "material.components.list" , "material.components.progressCircular" , "material.components.progressLinear" , "material.components.radioButton" , "material.components.sidenav" , "material.components.slider" , "material.components.sticky" , "material.components.subheader" , "material.components.swipe" , "material.components.switch" , "material.components.tabs" , "material.components.textField" , "material.components.toast" , "material.components.toolbar" , "material.components.tooltip" , "material.components.whiteframe" ] ) ;
/ * !
* Angular Material Design
* https : //github.com/angular/material
* @ license MIT
* v0 . 7.1
2014-09-30 12:29:53 +02:00
* /
2014-11-03 17:32:25 +01:00
( function ( ) {
2015-02-04 16:08:32 +01:00
'use strict' ;
2014-10-12 19:07:47 +02:00
2015-02-04 16:08:32 +01:00
/ * *
* Initialization function that validates environment
* requirements .
* /
angular
. module ( 'material.core' , [ 'material.core.theming' ] )
. config ( MdCoreConfigure ) ;
2014-10-12 19:07:47 +02:00
2015-02-04 16:08:32 +01:00
function MdCoreConfigure ( $provide , $mdThemingProvider ) {
$provide . decorator ( '$$rAF' , [ "$delegate" , rAFDecorator ] ) ;
2014-10-12 19:07:47 +02:00
2015-02-04 16:08:32 +01:00
$mdThemingProvider . theme ( 'default' )
. primaryPalette ( 'indigo' )
. accentPalette ( 'pink' )
. warnPalette ( 'red' )
. backgroundPalette ( 'grey' ) ;
}
MdCoreConfigure . $inject = [ "$provide" , "$mdThemingProvider" ] ;
2014-10-12 19:07:47 +02:00
2015-02-04 16:08:32 +01:00
function rAFDecorator ( $delegate ) {
/ * *
* Use this to throttle events that come in often .
* The throttled function will always use the * last * invocation before the
* coming frame .
*
* For example , window resize events that fire many times a second :
* If we set to use an raf - throttled callback on window resize , then
* our callback will only be fired once per frame , with the last resize
* event that happened before that frame .
*
* @ param { function } callback function to debounce
* /
$delegate . throttle = function ( cb ) {
var queueArgs , alreadyQueued , queueCb , context ;
return function debounced ( ) {
queueArgs = arguments ;
context = this ;
queueCb = cb ;
if ( ! alreadyQueued ) {
alreadyQueued = true ;
$delegate ( function ( ) {
queueCb . apply ( context , queueArgs ) ;
alreadyQueued = false ;
} ) ;
}
} ;
} ;
return $delegate ;
}
2014-10-12 19:07:47 +02:00
2014-11-03 17:32:25 +01:00
} ) ( ) ;
2014-10-12 19:07:47 +02:00
2015-02-04 16:08:32 +01:00
/ * !
* Angular Material Design
* https : //github.com/angular/material
* @ license MIT
* v0 . 7.1
* /
2014-11-03 17:32:25 +01:00
( function ( ) {
2015-02-04 16:08:32 +01:00
'use strict' ;
2014-09-30 12:29:53 +02:00
2014-11-03 17:32:25 +01:00
angular . module ( 'material.core' )
2015-02-04 16:08:32 +01:00
. factory ( '$mdConstant' , MdConstantFactory ) ;
2014-09-30 12:29:53 +02:00
2015-02-04 16:08:32 +01:00
function MdConstantFactory ( $$rAF , $sniffer ) {
2014-09-30 12:29:53 +02:00
2015-02-04 16:08:32 +01:00
var webkit = /webkit/i . test ( $sniffer . vendorPrefix ) ;
function vendorProperty ( name ) {
return webkit ? ( 'webkit' + name . charAt ( 0 ) . toUpperCase ( ) + name . substring ( 1 ) ) : name ;
}
2014-11-03 17:32:25 +01:00
2015-02-04 16:08:32 +01:00
return {
KEY _CODE : {
ENTER : 13 ,
ESCAPE : 27 ,
SPACE : 32 ,
LEFT _ARROW : 37 ,
UP _ARROW : 38 ,
RIGHT _ARROW : 39 ,
DOWN _ARROW : 40
2014-11-03 17:32:25 +01:00
} ,
2015-02-04 16:08:32 +01:00
CSS : {
/* Constants */
TRANSITIONEND : 'transitionend' + ( webkit ? ' webkitTransitionEnd' : '' ) ,
ANIMATIONEND : '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' )
2014-11-03 17:32:25 +01:00
} ,
2015-02-04 16:08:32 +01:00
MEDIA : {
'sm' : '(max-width: 600px)' ,
'gt-sm' : '(min-width: 600px)' ,
'md' : '(min-width: 600px) and (max-width: 960px)' ,
'gt-md' : '(min-width: 960px)' ,
'lg' : '(min-width: 960px) and (max-width: 1200px)' ,
'gt-lg' : '(min-width: 1200px)'
2014-11-03 17:32:25 +01:00
}
} ;
2015-02-04 16:08:32 +01:00
}
MdConstantFactory . $inject = [ "$$rAF" , "$sniffer" ] ;
2014-09-30 12:29:53 +02:00
2015-02-04 16:08:32 +01:00
} ) ( ) ;
/ * !
* Angular Material Design
* https : //github.com/angular/material
* @ license MIT
* v0 . 7.1
* /
( function ( ) {
angular
. module ( 'material.core' )
. config ( [ "$provide" , function ( $provide ) {
$provide . decorator ( '$mdUtil' , [ '$delegate' , function ( $delegate ) {
/ * *
* Inject the iterator facade to easily support iteration and accessors
* @ see iterator below
* /
$delegate . iterator = Iterator ;
return $delegate ;
}
] ) ;
} ] ) ;
/ * *
2014-11-03 17:32:25 +01:00
* 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
2014-10-12 19:07:47 +02:00
* /
2015-02-04 16:08:32 +01:00
function Iterator ( items , reloop ) {
2014-11-03 17:32:25 +01:00
var trueFn = function ( ) { return true ; } ;
2014-09-30 12:29:53 +02:00
2014-11-03 17:32:25 +01:00
reloop = ! ! reloop ;
var _items = items || [ ] ;
2014-09-30 12:29:53 +02:00
2014-11-03 17:32:25 +01:00
// Published API
return {
items : getItems ,
count : count ,
2014-09-30 12:29:53 +02:00
2014-11-03 17:32:25 +01:00
inRange : inRange ,
contains : contains ,
indexOf : indexOf ,
itemAt : itemAt ,
2014-09-30 12:29:53 +02:00
2014-11-03 17:32:25 +01:00
findBy : findBy ,
2014-09-30 12:29:53 +02:00
2014-11-03 17:32:25 +01:00
add : add ,
remove : remove ,
2014-09-30 12:29:53 +02:00
2014-11-03 17:32:25 +01:00
first : first ,
last : last ,
2015-02-04 16:08:32 +01:00
next : angular . bind ( null , findSubsequentItem , false ) ,
previous : angular . bind ( null , findSubsequentItem , true ) ,
2014-09-30 12:29:53 +02:00
2014-11-03 17:32:25 +01:00
hasPrevious : hasPrevious ,
hasNext : hasNext
2014-09-30 12:29:53 +02:00
2014-11-03 17:32:25 +01:00
} ;
2014-09-30 12:29:53 +02:00
2015-02-04 16:08:32 +01:00
/ * *
2014-11-03 17:32:25 +01:00
* Publish copy of the enumerable set
* @ returns { Array | * }
* /
function getItems ( ) {
return [ ] . concat ( _items ) ;
}
2014-09-30 12:29:53 +02:00
2015-02-04 16:08:32 +01:00
/ * *
2014-11-03 17:32:25 +01:00
* Determine length of the list
* @ returns { Array . length | * | number }
* /
function count ( ) {
return _items . length ;
}
2014-09-30 12:29:53 +02:00
2015-02-04 16:08:32 +01:00
/ * *
2014-11-03 17:32:25 +01:00
* Is the index specified valid
* @ param index
* @ returns { Array . length | * | number | boolean }
* /
function inRange ( index ) {
return _items . length && ( index > - 1 ) && ( index < _items . length ) ;
}
2014-09-30 12:29:53 +02:00
2015-02-04 16:08:32 +01:00
/ * *
2014-11-03 17:32:25 +01:00
* 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 ;
}
2014-10-12 19:07:47 +02:00
2015-02-04 16:08:32 +01:00
/ * *
2014-11-03 17:32:25 +01:00
* 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 ;
}
2014-10-12 19:07:47 +02:00
2015-02-04 16:08:32 +01:00
/ * *
2014-11-03 17:32:25 +01:00
* Get item at specified index / position
* @ param index
* @ returns { * }
* /
function itemAt ( index ) {
return inRange ( index ) ? _items [ index ] : null ;
2014-09-30 12:29:53 +02:00
}
2015-02-04 16:08:32 +01:00
/ * *
2014-11-03 17:32:25 +01:00
* 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 ;
} ) ;
}
2014-10-12 19:07:47 +02:00
2015-02-04 16:08:32 +01:00
/ * *
2014-11-03 17:32:25 +01:00
* Add item to list
* @ param item
* @ param index
* @ returns { * }
* /
function add ( item , index ) {
if ( ! item ) return - 1 ;
2014-09-30 12:29:53 +02:00
2014-11-03 17:32:25 +01:00
if ( ! angular . isNumber ( index ) ) {
index = _items . length ;
}
2014-10-12 19:07:47 +02:00
2014-11-03 17:32:25 +01:00
_items . splice ( index , 0 , item ) ;
2014-09-30 12:29:53 +02:00
2014-11-03 17:32:25 +01:00
return indexOf ( item ) ;
2014-09-30 12:29:53 +02:00
}
2015-02-04 16:08:32 +01:00
/ * *
2014-11-03 17:32:25 +01:00
* Remove item from list ...
* @ param item
* /
function remove ( item ) {
if ( contains ( item ) ) {
_items . splice ( indexOf ( item ) , 1 ) ;
2014-10-12 19:07:47 +02:00
}
2014-11-03 17:32:25 +01:00
}
2014-10-12 19:07:47 +02:00
2015-02-04 16:08:32 +01:00
/ * *
2014-11-03 17:32:25 +01:00
* Get the zero - based index of the target item
* @ param item
* @ returns { * }
* /
function indexOf ( item ) {
return _items . indexOf ( item ) ;
}
2014-10-12 19:07:47 +02:00
2015-02-04 16:08:32 +01:00
/ * *
2014-11-03 17:32:25 +01:00
* Boolean existence check
* @ param item
* @ returns { boolean }
* /
function contains ( item ) {
return item && ( indexOf ( item ) > - 1 ) ;
2014-10-12 19:07:47 +02:00
}
2015-02-04 16:08:32 +01:00
/ * *
* Return first item in the list
* @ returns { * }
* /
function first ( ) {
return _items . length ? _items [ 0 ] : null ;
}
/ * *
* Return last item in the list ...
* @ returns { * }
* /
function last ( ) {
return _items . length ? _items [ _items . length - 1 ] : null ;
}
/ * *
* Find the next item . If reloop is true and at the end of the list , it will
2014-11-03 17:32:25 +01:00
* 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
2015-02-04 16:08:32 +01:00
* @ param { optional } validate Validate function
* @ param { optional } limit Recursion limit
2014-11-03 17:32:25 +01:00
* @ returns { * }
* /
2015-02-04 16:08:32 +01:00
function findSubsequentItem ( backwards , item , validate , limit ) {
2014-11-03 17:32:25 +01:00
validate = validate || trueFn ;
2014-10-12 19:07:47 +02:00
2015-02-04 16:08:32 +01:00
var curIndex = indexOf ( item ) ;
if ( ! inRange ( curIndex ) ) {
return null ;
}
2014-10-12 19:07:47 +02:00
2015-02-04 16:08:32 +01:00
var nextIndex = curIndex + ( backwards ? - 1 : 1 ) ;
var foundItem = null ;
if ( inRange ( nextIndex ) ) {
foundItem = _items [ nextIndex ] ;
} else if ( reloop ) {
foundItem = backwards ? last ( ) : first ( ) ;
nextIndex = indexOf ( foundItem ) ;
2014-11-03 17:32:25 +01:00
}
2014-10-12 19:07:47 +02:00
2015-02-04 16:08:32 +01:00
if ( ( foundItem === null ) || ( nextIndex === limit ) ) {
return null ;
}
if ( angular . isUndefined ( limit ) ) {
limit = nextIndex ;
}
return validate ( foundItem ) ? foundItem : findSubsequentItem ( backwards , foundItem , validate , limit ) ;
2014-11-03 17:32:25 +01:00
}
2015-02-04 16:08:32 +01:00
}
2014-10-12 19:07:47 +02:00
2015-02-04 16:08:32 +01:00
} ) ( ) ;
2014-10-12 19:07:47 +02:00
2015-02-04 16:08:32 +01:00
/ * !
* Angular Material Design
* https : //github.com/angular/material
* @ license MIT
* v0 . 7.1
* /
angular . module ( 'material.core' )
. factory ( '$mdMedia' , mdMediaFactory ) ;
2014-10-12 19:07:47 +02:00
2015-02-04 16:08:32 +01:00
/ * *
* Exposes a function on the '$mdMedia' service which will return true or false ,
* whether the given media query matches . Re - evaluates on resize . Allows presets
* for 'sm' , 'md' , 'lg' .
*
* @ example $mdMedia ( 'sm' ) == true if device - width <= sm
* @ example $mdMedia ( '(min-width: 1200px)' ) == true if device - width >= 1200 px
* @ example $mdMedia ( 'max-width: 300px' ) == true if device - width <= 300 px ( sanitizes input , adding parens )
* /
function mdMediaFactory ( $mdConstant , $rootScope , $window ) {
var queries = { } ;
var results = { } ;
2014-10-12 19:07:47 +02:00
2015-02-04 16:08:32 +01:00
return $mdMedia ;
2014-10-12 19:07:47 +02:00
2015-02-04 16:08:32 +01:00
function $mdMedia ( query ) {
var validated = queries [ query ] ;
if ( angular . isUndefined ( validated ) ) {
validated = queries [ query ] = validate ( query ) ;
2014-11-03 17:32:25 +01:00
}
2014-10-12 19:07:47 +02:00
2015-02-04 16:08:32 +01:00
var result = results [ validated ] ;
if ( angular . isUndefined ( result ) ) {
result = add ( validated ) ;
2014-10-12 19:07:47 +02:00
}
2015-02-04 16:08:32 +01:00
return result ;
2014-09-30 12:29:53 +02:00
}
2014-11-06 16:55:16 +01:00
2015-02-04 16:08:32 +01:00
function validate ( query ) {
return $mdConstant . MEDIA [ query ] ||
( ( query . charAt ( 0 ) !== '(' ) ? ( '(' + query + ')' ) : query ) ;
}
2014-11-06 16:55:16 +01:00
2015-02-04 16:08:32 +01:00
function add ( query ) {
var result = $window . matchMedia ( query ) ;
result . addListener ( onQueryChange ) ;
return ( results [ result . media ] = ! ! result . matches ) ;
}
2014-11-06 16:55:16 +01:00
2015-02-04 16:08:32 +01:00
function onQueryChange ( ) {
var query = this ;
$rootScope . $evalAsync ( function ( ) {
results [ query . media ] = ! ! query . matches ;
} ) ;
2014-11-06 16:55:16 +01:00
}
2015-02-04 16:08:32 +01:00
}
mdMediaFactory . $inject = [ "$mdConstant" , "$rootScope" , "$window" ] ;
/ * !
* Angular Material Design
* https : //github.com/angular/material
* @ license MIT
* v0 . 7.1
* /
( function ( ) {
'use strict' ;
/ *
* This var has to be outside the angular factory , otherwise when
* there are multiple material apps on the same page , each app
* will create its own instance of this array and the app ' s IDs
* will not be unique .
* /
var nextUniqueId = [ '0' , '0' , '0' ] ;
angular . module ( 'material.core' )
. factory ( '$mdUtil' , [ "$document" , "$timeout" , function ( $document , $timeout ) {
var Util ;
return Util = {
now : window . performance ? angular . bind ( window . performance , window . performance . now ) : Date . now ,
elementRect : function ( element , offsetParent ) {
var node = element [ 0 ] ;
offsetParent = offsetParent || node . offsetParent || document . body ;
offsetParent = offsetParent [ 0 ] || offsetParent ;
var nodeRect = node . getBoundingClientRect ( ) ;
var parentRect = offsetParent . getBoundingClientRect ( ) ;
return {
left : nodeRect . left - parentRect . left + offsetParent . scrollLeft ,
top : nodeRect . top - parentRect . top + offsetParent . scrollTop ,
width : nodeRect . width ,
height : nodeRect . height
} ;
} ,
fakeNgModel : function ( ) {
return {
$fake : true ,
$setViewValue : function ( value ) {
this . $viewValue = value ;
this . $render ( value ) ;
this . $viewChangeListeners . forEach ( function ( cb ) { cb ( ) ; } ) ;
} ,
$isEmpty : function ( value ) {
return ( '' + value ) . length === 0 ;
} ,
$parsers : [ ] ,
$formatters : [ ] ,
$viewChangeListeners : [ ] ,
$render : angular . noop
} ;
} ,
// Returns a function, that, as long as it continues to be invoked, will not
// be triggered. The function will be called after it stops being called for
// N milliseconds.
// @param wait Integer value of msecs to delay (since last debounce reset); default value 10 msecs
// @param invokeApply should the $timeout trigger $digest() dirty checking
debounce : function ( func , wait , scope , invokeApply ) {
var timer ;
return function debounced ( ) {
var context = scope ,
args = Array . prototype . slice . call ( arguments ) ;
$timeout . cancel ( timer ) ;
timer = $timeout ( function ( ) {
timer = undefined ;
func . apply ( context , args ) ;
} , wait || 10 , invokeApply ) ;
} ;
} ,
// Returns a function that can only be triggered every `delay` milliseconds.
// In other words, the function will not be called unless it has been more
// than `delay` milliseconds since the last call.
throttle : function throttle ( func , delay ) {
var recent ;
return function throttled ( ) {
var context = this ;
var args = arguments ;
var now = Util . now ( ) ;
if ( ! recent || ( now - recent > delay ) ) {
func . apply ( context , args ) ;
recent = now ;
}
} ;
} ,
/ * *
* 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 = nextUniqueId . length ;
var digit ;
while ( index ) {
index -- ;
digit = nextUniqueId [ index ] . charCodeAt ( 0 ) ;
if ( digit == 57 /*'9'*/ ) {
nextUniqueId [ index ] = 'A' ;
return nextUniqueId . join ( '' ) ;
}
if ( digit == 90 /*'Z'*/ ) {
nextUniqueId [ index ] = '0' ;
} else {
nextUniqueId [ index ] = String . fromCharCode ( digit + 1 ) ;
return nextUniqueId . join ( '' ) ;
}
}
nextUniqueId . unshift ( '0' ) ;
return nextUniqueId . 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 ;
}
} ,
/ *
* getClosest replicates jQuery . closest ( ) to walk up the DOM tree until it finds a matching nodeName
*
* @ param el Element to start walking the DOM from
* @ param tagName Tag name to find closest to el , such as 'form'
* /
getClosest : function getClosest ( el , tagName ) {
tagName = tagName . toUpperCase ( ) ;
do {
if ( el . nodeName === tagName ) {
return el ;
}
} while ( el = el . parentNode ) ;
return null ;
}
} ;
2014-11-06 16:55:16 +01:00
} ] ) ;
2014-09-30 12:29:53 +02:00
2015-02-04 16:08:32 +01:00
/ *
2014-09-30 12:29:53 +02:00
* 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 .
* /
2014-11-03 17:32:25 +01:00
2014-09-30 12:29:53 +02:00
angular . element . prototype . focus = angular . element . prototype . focus || function ( ) {
if ( this . length ) {
this [ 0 ] . focus ( ) ;
}
return this ;
} ;
2014-10-12 19:07:47 +02:00
angular . element . prototype . blur = angular . element . prototype . blur || function ( ) {
if ( this . length ) {
this [ 0 ] . blur ( ) ;
2014-09-30 12:29:53 +02:00
}
2014-10-12 19:07:47 +02:00
return this ;
2014-09-30 12:29:53 +02:00
} ;
2015-02-04 16:08:32 +01:00
2014-11-03 17:32:25 +01:00
} ) ( ) ;
2014-09-30 12:29:53 +02:00
2015-02-04 16:08:32 +01:00
/ * !
* Angular Material Design
* https : //github.com/angular/material
* @ license MIT
* v0 . 7.1
* /
2014-11-03 17:32:25 +01:00
( function ( ) {
2015-02-04 16:08:32 +01:00
'use strict' ;
2014-09-30 12:29:53 +02:00
2015-02-04 16:08:32 +01:00
angular . module ( 'material.core' )
. service ( '$mdAria' , AriaService ) ;
2014-11-03 17:32:25 +01:00
2015-02-04 16:08:32 +01:00
function AriaService ( $$rAF , $log , $window ) {
2014-11-03 17:32:25 +01:00
2015-02-04 16:08:32 +01:00
return {
expect : expect ,
expectAsync : expectAsync ,
expectWithText : expectWithText
} ;
2014-11-03 17:32:25 +01:00
2015-02-04 16:08:32 +01:00
/ * *
* Check if expected attribute has been specified on the target element or child
* @ param element
* @ param attrName
* @ param { optional } defaultValue What to set the attr to if no value is found
* /
function expect ( element , attrName , defaultValue ) {
var node = element [ 0 ] ;
2014-09-30 12:29:53 +02:00
2015-02-04 16:08:32 +01:00
if ( ! node . hasAttribute ( attrName ) && ! childHasAttribute ( node , attrName ) ) {
2014-09-30 12:29:53 +02:00
2015-02-04 16:08:32 +01:00
defaultValue = angular . isString ( defaultValue ) && defaultValue . trim ( ) || '' ;
if ( defaultValue . length ) {
element . attr ( attrName , defaultValue ) ;
} else {
$log . warn ( 'ARIA: Attribute "' , attrName , '", required for accessibility, is missing on node:' , node ) ;
}
2014-09-30 12:29:53 +02:00
}
2015-02-04 16:08:32 +01:00
}
2014-09-30 12:29:53 +02:00
2015-02-04 16:08:32 +01:00
function expectAsync ( element , attrName , defaultValueGetter ) {
// Problem: when retrieving the element's contents synchronously to find the label,
// the text may not be defined yet in the case of a binding.
// There is a higher chance that a binding will be defined if we wait one frame.
2014-09-30 12:29:53 +02:00
$$rAF ( function ( ) {
2015-02-04 16:08:32 +01:00
expect ( element , attrName , defaultValueGetter ( ) ) ;
2014-09-30 12:29:53 +02:00
} ) ;
2015-02-04 16:08:32 +01:00
}
2014-09-30 12:29:53 +02:00
2015-02-04 16:08:32 +01:00
function expectWithText ( element , attrName ) {
expectAsync ( element , attrName , function ( ) {
return element . text ( ) . trim ( ) ;
} ) ;
2014-09-30 12:29:53 +02:00
}
2015-02-04 16:08:32 +01:00
function childHasAttribute ( node , attrName ) {
var hasChildren = node . hasChildNodes ( ) ,
hasAttr = false ;
2014-09-30 12:29:53 +02:00
2015-02-04 16:08:32 +01:00
function isHidden ( el ) {
var style = el . currentStyle ? el . currentStyle : $window . getComputedStyle ( el ) ;
return ( style . display === 'none' ) ;
}
2014-09-30 12:29:53 +02:00
2015-02-04 16:08:32 +01:00
if ( hasChildren ) {
var children = node . childNodes ;
for ( var i = 0 ; i < children . length ; i ++ ) {
var child = children [ i ] ;
if ( child . nodeType === 1 && child . hasAttribute ( attrName ) ) {
if ( ! isHidden ( child ) ) {
hasAttr = true ;
}
}
}
}
return hasAttr ;
2014-09-30 12:29:53 +02:00
}
}
2015-02-04 16:08:32 +01:00
AriaService . $inject = [ "$$rAF" , "$log" , "$window" ] ;
2014-09-30 12:29:53 +02:00
} ) ( ) ;
2015-02-04 16:08:32 +01:00
/ * !
* Angular Material Design
* https : //github.com/angular/material
* @ license MIT
* v0 . 7.1
* /
2014-11-03 17:32:25 +01:00
( function ( ) {
2015-02-04 16:08:32 +01:00
'use strict' ;
2014-09-30 12:29:53 +02:00
2015-02-04 16:08:32 +01:00
angular . module ( 'material.core' )
. service ( '$mdCompiler' , mdCompilerService ) ;
2014-09-30 12:29:53 +02:00
2015-02-04 16:08:32 +01:00
function mdCompilerService ( $q , $http , $injector , $compile , $controller , $templateCache ) {
/* jshint validthis: true */
2014-09-30 12:29:53 +02:00
2015-02-04 16:08:32 +01:00
/ *
* @ ngdoc service
* @ name $mdCompiler
* @ module material . core
* @ description
* The $mdCompiler service is an abstraction of angular ' s compiler , that allows the developer
* to easily compile an element with a templateUrl , controller , and locals .
*
* @ usage
* < hljs lang = "js" >
* $mdCompiler . compile ( {
* templateUrl : 'modal.html' ,
* controller : 'ModalCtrl' ,
* locals : {
* modal : myModalInstance ;
* }
* } ) . then ( function ( compileData ) {
* compileData . element ; // modal.html's template in an element
* compileData . link ( myScope ) ; //attach controller & scope to element
* } ) ;
* < / h l j s >
* /
2014-09-30 12:29:53 +02:00
2015-02-04 16:08:32 +01:00
/ *
* @ ngdoc method
* @ name $mdCompiler # compile
* @ description A helper to compile an HTML template / templateUrl with a given controller ,
* locals , and scope .
* @ param { object } options An options object , with the following properties :
*
* - ` controller ` - ` {(string=|function()=} ` Controller fn that should be associated with
* newly created scope or the name of a registered controller if passed as a string .
* - ` controllerAs ` - ` {string=} ` A controller alias name . If present the controller will be
* published to scope under the ` controllerAs ` name .
* - ` template ` - ` {string=} ` An html template as a string .
* - ` templateUrl ` - ` {string=} ` A path to an html template .
* - ` transformTemplate ` - ` {function(template)=} ` A function which transforms the template after
* it is loaded . It will be given the template string as a parameter , and should
* return a a new string representing the transformed template .
* - ` resolve ` - ` {Object.<string, function>=} ` - An optional map of dependencies which should
* be injected into the controller . If any of these dependencies are promises , the compiler
* will wait for them all to be resolved , or if one is rejected before the controller is
* instantiated ` compile() ` will fail . .
* * ` key ` - ` {string} ` : a name of a dependency to be injected into the controller .
* * ` factory ` - ` {string|function} ` : If ` string ` then it is an alias for a service .
* Otherwise if function , then it is injected and the return value is treated as the
* dependency . If the result is a promise , it is resolved before its value is
* injected into the controller .
*
* @ returns { object = } promise A promise , which will be resolved with a ` compileData ` object .
* ` compileData ` has the following properties :
*
* - ` element ` - ` {element} ` : an uncompiled element matching the provided template .
* - ` link ` - ` {function(scope)} ` : A link function , which , when called , will compile
* the element and instantiate the provided controller ( if given ) .
* - ` locals ` - ` {object} ` : The locals which will be passed into the controller once ` link ` is
* called . If ` bindToController ` is true , they will be coppied to the ctrl instead
* - ` bindToController ` - ` bool ` : bind the locals to the controller , instead of passing them in
* /
this . compile = function ( options ) {
var templateUrl = options . templateUrl ;
var template = options . template || '' ;
var controller = options . controller ;
var controllerAs = options . controllerAs ;
var resolve = options . resolve || { } ;
var locals = options . locals || { } ;
var transformTemplate = options . transformTemplate || angular . identity ;
var bindToController = options . bindToController ;
2014-09-30 12:29:53 +02:00
2015-02-04 16:08:32 +01:00
// 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 ) ;
}
2014-09-30 12:29:53 +02:00
} ) ;
2015-02-04 16:08:32 +01:00
//Add the locals, which are just straight values to inject
//eg locals: { three: 3 }, will inject three into the controller
angular . extend ( resolve , locals ) ;
2014-09-30 12:29:53 +02:00
2015-02-04 16:08:32 +01:00
if ( templateUrl ) {
resolve . $template = $http . get ( templateUrl , { cache : $templateCache } )
. then ( function ( response ) {
return response . data ;
} ) ;
} else {
resolve . $template = $q . when ( template ) ;
}
2014-10-12 19:07:47 +02:00
2015-02-04 16:08:32 +01:00
// Wait for all the resolves to finish if they are promises
return $q . all ( resolve ) . then ( function ( locals ) {
2014-09-30 12:29:53 +02:00
2015-02-04 16:08:32 +01:00
var template = transformTemplate ( locals . $template ) ;
var element = angular . element ( '<div>' ) . html ( template . trim ( ) ) . contents ( ) ;
var linkFn = $compile ( element ) ;
2014-09-30 12:29:53 +02:00
2015-02-04 16:08:32 +01:00
//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 ;
2014-09-30 12:29:53 +02:00
2015-02-04 16:08:32 +01:00
//Instantiate controller if it exists, because we have scope
if ( controller ) {
var ctrl = $controller ( controller , locals ) ;
if ( bindToController ) {
angular . extend ( ctrl , locals ) ;
}
//See angular-route source for this logic
element . data ( '$ngControllerController' , ctrl ) ;
element . children ( ) . data ( '$ngControllerController' , ctrl ) ;
2014-10-12 19:07:47 +02:00
2015-02-04 16:08:32 +01:00
if ( controllerAs ) {
scope [ controllerAs ] = ctrl ;
}
}
2014-10-12 19:07:47 +02:00
2015-02-04 16:08:32 +01:00
return linkFn ( scope ) ;
}
} ;
} ) ;
2014-09-30 12:29:53 +02:00
2015-02-04 16:08:32 +01:00
} ;
}
mdCompilerService . $inject = [ "$q" , "$http" , "$injector" , "$compile" , "$controller" , "$templateCache" ] ;
} ) ( ) ;
2014-09-30 12:29:53 +02:00
2015-02-04 16:08:32 +01:00
/ * !
* Angular Material Design
* https : //github.com/angular/material
* @ license MIT
* v0 . 7.1
* /
( function ( ) {
'use strict' ;
2014-09-30 12:29:53 +02:00
2015-02-04 16:08:32 +01:00
/ *
* TODO : Add support for multiple fingers on the ` pointer ` object ( enables pinch gesture )
* /
2014-09-30 12:29:53 +02:00
2015-02-04 16:08:32 +01:00
var START _EVENTS = 'mousedown touchstart pointerdown' ;
var MOVE _EVENTS = 'mousemove touchmove pointermove' ;
var END _EVENTS = 'mouseup mouseleave touchend touchcancel pointerup pointercancel' ;
var HANDLERS ;
2014-09-30 12:29:53 +02:00
2015-02-04 16:08:32 +01:00
document . contains || ( document . contains = function ( node ) {
return document . body . contains ( node ) ;
} ) ;
2014-11-03 17:32:25 +01:00
2015-02-04 16:08:32 +01:00
// TODO add windows phone to this
var userAgent = navigator . userAgent || navigator . vendor || window . opera ;
var isIos = userAgent . match ( /iPad/i ) || userAgent . match ( /iPhone/i ) || userAgent . match ( /iPod/i ) ;
var isAndroid = userAgent . match ( /Android/i ) ;
var shouldHijackClicks = isIos || isAndroid ;
if ( shouldHijackClicks ) {
document . addEventListener ( 'click' , function ( ev ) {
// Space/enter on a button, and submit events, can send clicks
var isKeyClick = ev . clientX === 0 && ev . clientY === 0 ;
if ( isKeyClick || ev . $material ) return ;
// Prevent clicks unless they're sent by material
ev . preventDefault ( ) ;
ev . stopPropagation ( ) ;
} , true ) ;
}
2014-09-30 12:29:53 +02:00
2015-02-04 16:08:32 +01:00
angular . element ( document )
. on ( START _EVENTS , gestureStart )
. on ( MOVE _EVENTS , gestureMove )
. on ( END _EVENTS , gestureEnd )
// For testing
. on ( '$$mdGestureReset' , function ( ) {
lastPointer = pointer = null ;
} ) ;
2014-09-30 12:29:53 +02:00
2015-02-04 16:08:32 +01:00
// The state of the current and previous 'pointer' (user's hand)
var pointer , lastPointer ;
2014-09-30 12:29:53 +02:00
2015-02-04 16:08:32 +01:00
function runHandlers ( handlerEvent , event ) {
var handler ;
for ( var handlerName in HANDLERS ) {
handler = HANDLERS [ handlerName ] ;
if ( handlerEvent === 'start' ) {
// Run cancel to reset any handlers' state
handler . cancel ( ) ;
2014-09-30 12:29:53 +02:00
}
2015-02-04 16:08:32 +01:00
handler [ handlerEvent ] ( event , pointer ) ;
}
}
2014-10-12 19:07:47 +02:00
2015-02-04 16:08:32 +01:00
function gestureStart ( ev ) {
// If we're already touched down, abort
if ( pointer ) return ;
2014-10-12 19:07:47 +02:00
2015-02-04 16:08:32 +01:00
var now = + Date . now ( ) ;
2014-10-12 19:07:47 +02:00
2015-02-04 16:08:32 +01:00
// iOS & old android bug: after a touch event, a click event is sent 350 ms later.
// If <400ms have passed, don't allow an event of a different type than the previous event
if ( lastPointer && ! typesMatch ( ev , lastPointer ) && ( now - lastPointer . endTime < 1500 ) ) {
return ;
}
2014-10-12 19:07:47 +02:00
2015-02-04 16:08:32 +01:00
pointer = makeStartPointer ( ev ) ;
2014-11-03 17:32:25 +01:00
2015-02-04 16:08:32 +01:00
runHandlers ( 'start' , ev ) ;
}
2014-10-12 19:07:47 +02:00
2015-02-04 16:08:32 +01:00
function gestureMove ( ev ) {
if ( ! pointer || ! typesMatch ( ev , pointer ) ) return ;
updatePointerState ( ev , pointer ) ;
runHandlers ( 'move' , ev ) ;
2014-10-12 19:07:47 +02:00
}
2014-11-03 17:32:25 +01:00
2015-02-04 16:08:32 +01:00
function gestureEnd ( ev ) {
if ( ! pointer || ! typesMatch ( ev , pointer ) ) return ;
2014-11-03 17:32:25 +01:00
2015-02-04 16:08:32 +01:00
updatePointerState ( ev , pointer ) ;
pointer . endTime = + Date . now ( ) ;
2014-11-03 17:32:25 +01:00
2015-02-04 16:08:32 +01:00
runHandlers ( 'end' , ev ) ;
lastPointer = pointer ;
pointer = null ;
2014-11-03 17:32:25 +01:00
}
2014-10-12 19:07:47 +02:00
2015-02-04 16:08:32 +01:00
/******** Helpers *********/
function typesMatch ( ev , pointer ) {
return ev && pointer && ev . type . charAt ( 0 ) === pointer . type ;
}
2014-11-06 16:55:16 +01:00
2015-02-04 16:08:32 +01:00
function getEventPoint ( ev ) {
ev = ev . originalEvent || ev ; // support jQuery events
return ( ev . touches && ev . touches [ 0 ] ) ||
( ev . changedTouches && ev . changedTouches [ 0 ] ) ||
ev ;
}
2014-11-06 16:55:16 +01:00
2015-02-04 16:08:32 +01:00
function updatePointerState ( ev , pointer ) {
var point = getEventPoint ( ev ) ;
var x = pointer . x = point . pageX ;
var y = pointer . y = point . pageY ;
pointer . distanceX = x - pointer . startX ;
pointer . distanceY = y - pointer . startY ;
pointer . distance = Math . sqrt (
pointer . distanceX * pointer . distanceX + pointer . distanceY * pointer . distanceY
) ;
pointer . directionX = pointer . distanceX > 0 ? 'right' : pointer . distanceX < 0 ? 'left' : '' ;
pointer . directionY = pointer . distanceY > 0 ? 'up' : pointer . distanceY < 0 ? 'down' : '' ;
pointer . duration = + Date . now ( ) - pointer . startTime ;
pointer . velocityX = pointer . distanceX / pointer . duration ;
pointer . velocityY = pointer . distanceY / pointer . duration ;
2014-11-06 16:55:16 +01:00
}
2014-10-12 19:07:47 +02:00
2015-02-04 16:08:32 +01:00
function makeStartPointer ( ev ) {
var point = getEventPoint ( ev ) ;
var startPointer = {
startTime : + Date . now ( ) ,
target : ev . target ,
// 'p' for pointer, 'm' for mouse, 't' for touch
type : ev . type . charAt ( 0 )
2014-11-03 17:32:25 +01:00
} ;
2015-02-04 16:08:32 +01:00
startPointer . startX = startPointer . x = point . pageX ;
startPointer . startY = startPointer . y = point . pageY ;
return startPointer ;
2014-11-03 17:32:25 +01:00
}
2015-02-04 16:08:32 +01:00
angular . module ( 'material.core' )
. run ( [ "$mdGesture" , function ( $mdGesture ) { } ] ) // make sure $mdGesture is always instantiated
. factory ( '$mdGesture' , [ "$$MdGestureHandler" , "$$rAF" , "$timeout" , function ( $$MdGestureHandler , $$rAF , $timeout ) {
HANDLERS = { } ;
if ( shouldHijackClicks ) {
addHandler ( 'click' , {
options : {
maxDistance : 6
} ,
onEnd : function ( ev , pointer ) {
if ( pointer . distance < this . state . options . maxDistance ) {
this . dispatchEvent ( ev , 'click' , null , ev ) ;
}
}
} ) ;
}
2014-10-12 19:07:47 +02:00
2015-02-04 16:08:32 +01:00
addHandler ( 'press' , {
onStart : function ( ev , pointer ) {
this . dispatchEvent ( ev , '$md.pressdown' ) ;
} ,
onEnd : function ( ev , pointer ) {
this . dispatchEvent ( ev , '$md.pressup' ) ;
}
} ) ;
2014-10-12 19:07:47 +02:00
2015-02-04 16:08:32 +01:00
addHandler ( 'hold' , {
options : {
// If the user keeps his finger within the same <maxDistance> area for
// <delay> ms, dispatch a hold event.
maxDistance : 6 ,
delay : 500 ,
} ,
onCancel : function ( ) {
$timeout . cancel ( this . state . timeout ) ;
} ,
onStart : function ( ev , pointer ) {
// For hold, require a parent to be registered with $mdGesture.register()
// Because we prevent scroll events, this is necessary.
if ( ! this . state . registeredParent ) return this . cancel ( ) ;
this . state . pos = { x : pointer . x , y : pointer . y } ;
this . state . timeout = $timeout ( angular . bind ( this , function holdDelayFn ( ) {
this . dispatchEvent ( ev , '$md.hold' ) ;
this . cancel ( ) ; //we're done!
} ) , this . state . options . delay , false ) ;
} ,
onMove : function ( ev , pointer ) {
// Don't scroll while waiting for hold
ev . preventDefault ( ) ;
var dx = this . state . pos . x - pointer . x ;
var dy = this . state . pos . y - pointer . y ;
if ( Math . sqrt ( dx * dx + dy * dy ) > this . options . maxDistance ) {
this . cancel ( ) ;
}
} ,
onEnd : function ( ev , pointer ) {
this . onCancel ( ) ;
} ,
2014-11-03 17:32:25 +01:00
} ) ;
2014-10-12 19:07:47 +02:00
2015-02-04 16:08:32 +01:00
addHandler ( 'drag' , {
options : {
minDistance : 6 ,
horizontal : true ,
} ,
onStart : function ( ev ) {
// For drag, require a parent to be registered with $mdGesture.register()
if ( ! this . state . registeredParent ) this . cancel ( ) ;
} ,
onMove : function ( ev , pointer ) {
var shouldStartDrag , shouldCancel ;
// Don't allow touch events to scroll while we're dragging or
// deciding if this touchmove is a proper drag
ev . preventDefault ( ) ;
2014-10-12 19:07:47 +02:00
2015-02-04 16:08:32 +01:00
if ( ! this . state . dragPointer ) {
if ( this . state . options . horizontal ) {
shouldStartDrag = Math . abs ( pointer . distanceX ) > this . state . options . minDistance ;
shouldCancel = Math . abs ( pointer . distanceY ) > this . state . options . minDistance * 1.5 ;
} else {
shouldStartDrag = Math . abs ( pointer . distanceY ) > this . state . options . minDistance ;
shouldCancel = Math . abs ( pointer . distanceX ) > this . state . options . minDistance * 1.5 ;
}
2014-10-12 19:07:47 +02:00
2015-02-04 16:08:32 +01:00
if ( shouldStartDrag ) {
// Create a new pointer, starting at this point where the drag started.
this . state . dragPointer = makeStartPointer ( ev ) ;
updatePointerState ( ev , this . state . dragPointer ) ;
this . dispatchEvent ( ev , '$md.dragstart' , this . state . dragPointer ) ;
2014-10-12 19:07:47 +02:00
2015-02-04 16:08:32 +01:00
} else if ( shouldCancel ) {
this . cancel ( ) ;
}
} else {
this . dispatchDragMove ( ev ) ;
}
} ,
// Only dispatch these every frame; any more is unnecessray
dispatchDragMove : $$rAF . throttle ( function ( ev ) {
// Make sure the drag didn't stop while waiting for the next frame
if ( this . state . isRunning ) {
updatePointerState ( ev , this . state . dragPointer ) ;
this . dispatchEvent ( ev , '$md.drag' , this . state . dragPointer ) ;
}
} ) ,
onEnd : function ( ev , pointer ) {
if ( this . state . dragPointer ) {
updatePointerState ( ev , this . state . dragPointer ) ;
this . dispatchEvent ( ev , '$md.dragend' , this . state . dragPointer ) ;
}
}
} ) ;
2014-10-12 19:07:47 +02:00
2015-02-04 16:08:32 +01:00
addHandler ( 'swipe' , {
options : {
minVelocity : 0.65 ,
minDistance : 10 ,
} ,
onEnd : function ( ev , pointer ) {
if ( Math . abs ( pointer . velocityX ) > this . state . options . minVelocity &&
Math . abs ( pointer . distanceX ) > this . state . options . minDistance ) {
var eventType = pointer . directionX == 'left' ? '$md.swipeleft' : '$md.swiperight' ;
this . dispatchEvent ( ev , eventType ) ;
}
}
} ) ;
2014-10-12 19:07:47 +02:00
2015-02-04 16:08:32 +01:00
var self ;
return self = {
handler : addHandler ,
register : register
} ;
2014-10-12 19:07:47 +02:00
2015-02-04 16:08:32 +01:00
function addHandler ( name , definition ) {
var handler = new $$MdGestureHandler ( name ) ;
angular . extend ( handler , definition ) ;
HANDLERS [ name ] = handler ;
return self ;
2014-11-03 17:32:25 +01:00
}
2014-10-12 19:07:47 +02:00
2015-02-04 16:08:32 +01:00
function register ( element , handlerName , options ) {
var handler = HANDLERS [ handlerName . replace ( /^\$md./ , '' ) ] ;
if ( ! handler ) {
throw new Error ( 'Failed to register element with handler ' + handlerName + '. ' +
'Available handlers: ' + Object . keys ( HANDLERS ) . join ( ', ' ) ) ;
2014-10-12 19:07:47 +02:00
}
2015-02-04 16:08:32 +01:00
return handler . registerElement ( element , options ) ;
}
} ] )
. factory ( '$$MdGestureHandler' , [ "$$rAF" , function ( $$rAF ) {
2014-10-12 19:07:47 +02:00
2015-02-04 16:08:32 +01:00
function GestureHandler ( name ) {
this . name = name ;
this . state = { } ;
}
GestureHandler . prototype = {
onStart : angular . noop ,
onMove : angular . noop ,
onEnd : angular . noop ,
onCancel : angular . noop ,
options : { } ,
dispatchEvent : dispatchEvent ,
start : function ( ev , pointer ) {
if ( this . state . isRunning ) return ;
var parentTarget = this . getNearestParent ( ev . target ) ;
var parentTargetOptions = parentTarget && parentTarget . $mdGesture [ this . name ] || { } ;
this . state = {
isRunning : true ,
options : angular . extend ( { } , this . options , parentTargetOptions ) ,
registeredParent : parentTarget
} ;
this . onStart ( ev , pointer ) ;
} ,
move : function ( ev , pointer ) {
if ( ! this . state . isRunning ) return ;
this . onMove ( ev , pointer ) ;
} ,
end : function ( ev , pointer ) {
if ( ! this . state . isRunning ) return ;
this . onEnd ( ev , pointer ) ;
this . state . isRunning = false ;
} ,
cancel : function ( ev , pointer ) {
this . onCancel ( ev , pointer ) ;
this . state = { } ;
} ,
2014-10-12 19:07:47 +02:00
2015-02-04 16:08:32 +01:00
// Find and return the nearest parent element that has been registered via
// $mdGesture.register(element, 'handlerName').
getNearestParent : function ( node ) {
var current = node ;
while ( current ) {
if ( ( current . $mdGesture || { } ) [ this . name ] ) {
return current ;
2014-11-03 17:32:25 +01:00
}
2015-02-04 16:08:32 +01:00
current = current . parentNode ;
2014-10-12 19:07:47 +02:00
}
2015-02-04 16:08:32 +01:00
} ,
2014-10-12 19:07:47 +02:00
2015-02-04 16:08:32 +01:00
registerElement : function ( element , options ) {
var self = this ;
element [ 0 ] . $mdGesture = element [ 0 ] . $mdGesture || { } ;
element [ 0 ] . $mdGesture [ this . name ] = options || { } ;
element . on ( '$destroy' , onDestroy ) ;
2014-10-12 19:07:47 +02:00
2015-02-04 16:08:32 +01:00
return onDestroy ;
2014-11-03 17:32:25 +01:00
2015-02-04 16:08:32 +01:00
function onDestroy ( ) {
delete element [ 0 ] . $mdGesture [ self . name ] ;
element . off ( '$destroy' , onDestroy ) ;
2014-10-12 19:07:47 +02:00
}
2015-02-04 16:08:32 +01:00
} ,
} ;
2014-10-12 19:07:47 +02:00
2015-02-04 16:08:32 +01:00
var customEventOptions = {
bubbles : true ,
cancelable : true
} ;
/ *
* NOTE : dispatchEvent is very performance sensitive .
* /
function dispatchEvent ( srcEvent , eventType , eventPointer , /*original DOMEvent */ ev ) {
eventPointer = eventPointer || pointer ;
var eventObj ;
if ( eventType === 'click' ) {
eventObj = document . createEvent ( 'MouseEvents' ) ;
eventObj . initMouseEvent (
'click' , true , true , window , ev . detail ,
ev . screenX , ev . screenY , ev . clientX , ev . clientY ,
ev . ctrlKey , ev . altKey , ev . shiftKey , ev . metaKey ,
ev . button , ev . relatedTarget || null
) ;
2014-10-12 19:07:47 +02:00
2015-02-04 16:08:32 +01:00
} else {
eventObj = document . createEvent ( 'CustomEvent' ) ;
eventObj . initCustomEvent ( eventType , true , true , { } ) ;
2014-11-03 17:32:25 +01:00
}
2015-02-04 16:08:32 +01:00
eventObj . $material = true ;
eventObj . pointer = eventPointer ;
eventObj . srcEvent = srcEvent ;
eventPointer . target . dispatchEvent ( eventObj ) ;
2014-11-03 17:32:25 +01:00
}
2014-10-12 19:07:47 +02:00
2015-02-04 16:08:32 +01:00
return GestureHandler ;
} ] ) ;
2014-11-03 17:32:25 +01:00
} ) ( ) ;
2014-10-12 19:07:47 +02:00
2015-02-04 16:08:32 +01:00
/ * !
* Angular Material Design
* https : //github.com/angular/material
* @ license MIT
* v0 . 7.1
2014-11-03 17:32:25 +01:00
* /
2015-02-04 16:08:32 +01:00
( function ( ) {
'use strict' ;
2014-10-12 19:07:47 +02:00
2015-02-04 16:08:32 +01:00
angular . module ( 'material.core' )
. provider ( '$$interimElement' , InterimElementProvider ) ;
/ *
* @ ngdoc service
* @ name $$interimElement
* @ module material . core
2014-11-03 17:32:25 +01:00
*
* @ description
*
2015-02-04 16:08:32 +01:00
* Factory that contructs ` $ $ interimElement. $ service ` services .
* Used internally in material design for elements that appear on screen temporarily .
* The service provides a promise - like API for interacting with the temporary
* elements .
*
* ` ` ` js
* app . service ( '$mdToast' , function ( $$interimElement ) {
* var $mdToast = $$interimElement ( toastDefaultOptions ) ;
* return $mdToast ;
* } ) ;
* ` ` `
* @ param { object = } defaultOptions Options used by default for the ` show ` method on the service .
*
* @ returns { $$interimElement . $service }
2014-11-03 17:32:25 +01:00
*
* /
2014-10-12 19:07:47 +02:00
2015-02-04 16:08:32 +01:00
function InterimElementProvider ( ) {
createInterimElementProvider . $get = InterimElementFactory ;
InterimElementFactory . $inject = [ "$document" , "$q" , "$rootScope" , "$timeout" , "$rootElement" , "$animate" , "$interpolate" , "$mdCompiler" , "$mdTheming" ] ;
return createInterimElementProvider ;
2014-10-12 19:07:47 +02:00
2015-02-04 16:08:32 +01:00
/ * *
* Returns a new provider which allows configuration of a new interimElement
* service . Allows configuration of default options & methods for options ,
* as well as configuration of 'preset' methods ( eg dialog . basic ( ) : basic is a preset method )
* /
function createInterimElementProvider ( interimFactoryName ) {
var EXPOSED _METHODS = [ 'onHide' , 'onShow' , 'onRemove' ] ;
var providerConfig = {
presets : { }
} ;
var provider = {
setDefaults : setDefaults ,
addPreset : addPreset ,
$get : factory
} ;
2014-11-06 16:55:16 +01:00
2015-02-04 16:08:32 +01:00
/ * *
* all interim elements will come with the 'build' preset
* /
provider . addPreset ( 'build' , {
methods : [ 'controller' , 'controllerAs' , 'resolve' ,
'template' , 'templateUrl' , 'themable' , 'transformTemplate' , 'parent' ]
} ) ;
factory . $inject = [ "$$interimElement" , "$animate" , "$injector" ] ;
return provider ;
/ * *
* Save the configured defaults to be used when the factory is instantiated
* /
function setDefaults ( definition ) {
providerConfig . optionsFactory = definition . options ;
providerConfig . methods = ( definition . methods || [ ] ) . concat ( EXPOSED _METHODS ) ;
return provider ;
}
/ * *
* Save the configured preset to be used when the factory is instantiated
* /
function addPreset ( name , definition ) {
definition = definition || { } ;
definition . methods = definition . methods || [ ] ;
definition . options = definition . options || function ( ) { return { } ; } ;
if ( /^cancel|hide|show$/ . test ( name ) ) {
throw new Error ( "Preset '" + name + "' in " + interimFactoryName + " is reserved!" ) ;
2014-10-12 19:07:47 +02:00
}
2015-02-04 16:08:32 +01:00
if ( definition . methods . indexOf ( '_options' ) > - 1 ) {
throw new Error ( "Method '_options' in " + interimFactoryName + " is reserved!" ) ;
}
providerConfig . presets [ name ] = {
methods : definition . methods . concat ( EXPOSED _METHODS ) ,
optionsFactory : definition . options ,
argOption : definition . argOption
} ;
return provider ;
}
/ * *
* Create a factory that has the given methods & defaults implementing interimElement
* /
/* @ngInject */
function factory ( $$interimElement , $animate , $injector ) {
var defaultMethods ;
var defaultOptions ;
var interimElementService = $$interimElement ( ) ;
/ *
* publicService is what the developer will be using .
* It has methods hide ( ) , cancel ( ) , show ( ) , build ( ) , and any other
* presets which were set during the config phase .
* /
var publicService = {
hide : interimElementService . hide ,
cancel : interimElementService . cancel ,
show : showInterimElement
} ;
2014-11-03 17:32:25 +01:00
2015-02-04 16:08:32 +01:00
defaultMethods = providerConfig . methods || [ ] ;
// This must be invoked after the publicService is initialized
defaultOptions = invokeFactory ( providerConfig . optionsFactory , { } ) ;
angular . forEach ( providerConfig . presets , function ( definition , name ) {
var presetDefaults = invokeFactory ( definition . optionsFactory , { } ) ;
var presetMethods = ( definition . methods || [ ] ) . concat ( defaultMethods ) ;
// Every interimElement built with a preset has a field called `$type`,
// which matches the name of the preset.
// Eg in preset 'confirm', options.$type === 'confirm'
angular . extend ( presetDefaults , { $type : name } ) ;
// This creates a preset class which has setter methods for every
// method given in the `.addPreset()` function, as well as every
// method given in the `.setDefaults()` function.
//
// @example
// .setDefaults({
// methods: ['hasBackdrop', 'clickOutsideToClose', 'escapeToClose', 'targetEvent'],
// options: dialogDefaultOptions
// })
// .addPreset('alert', {
// methods: ['title', 'ok'],
// options: alertDialogOptions
// })
//
// Set values will be passed to the options when interimElemnt.show() is called.
function Preset ( opts ) {
this . _options = angular . extend ( { } , presetDefaults , opts ) ;
2014-11-03 17:32:25 +01:00
}
2015-02-04 16:08:32 +01:00
angular . forEach ( presetMethods , function ( name ) {
Preset . prototype [ name ] = function ( value ) {
this . _options [ name ] = value ;
return this ;
} ;
} ) ;
// Create shortcut method for one-linear methods
if ( definition . argOption ) {
var methodName = 'show' + name . charAt ( 0 ) . toUpperCase ( ) + name . slice ( 1 ) ;
publicService [ methodName ] = function ( arg ) {
var config = publicService [ name ] ( arg ) ;
return publicService . show ( config ) ;
} ;
}
// eg $mdDialog.alert() will return a new alert preset
publicService [ name ] = function ( arg ) {
// If argOption is supplied, eg `argOption: 'content'`, then we assume
// if the argument is not an options object then it is the `argOption` option.
//
// @example `$mdToast.simple('hello')` // sets options.content to hello
// // because argOption === 'content'
if ( arguments . length && definition . argOption && ! angular . isObject ( arg ) &&
! angular . isArray ( arg ) ) {
return ( new Preset ( ) ) [ definition . argOption ] ( arg ) ;
} else {
return new Preset ( arg ) ;
}
} ;
2014-11-03 17:32:25 +01:00
} ) ;
2015-02-04 16:08:32 +01:00
return publicService ;
2014-11-03 17:32:25 +01:00
2015-02-04 16:08:32 +01:00
function showInterimElement ( opts ) {
// opts is either a preset which stores its options on an _options field,
// or just an object made up of options
if ( opts && opts . _options ) opts = opts . _options ;
return interimElementService . show (
angular . extend ( { } , defaultOptions , opts )
) ;
}
/ * *
* Helper to call $injector . invoke with a local of the factory name for
* this provider .
* If an $mdDialog is providing options for a dialog and tries to inject
* $mdDialog , a circular dependency error will happen .
* We get around that by manually injecting $mdDialog as a local .
* /
function invokeFactory ( factory , defaultVal ) {
var locals = { } ;
locals [ interimFactoryName ] = publicService ;
return $injector . invoke ( factory || function ( ) { return defaultVal ; } , { } , locals ) ;
}
2014-11-03 17:32:25 +01:00
2014-10-12 19:07:47 +02:00
}
2015-02-04 16:08:32 +01:00
}
2014-10-12 19:07:47 +02:00
2015-02-04 16:08:32 +01:00
/* @ngInject */
function InterimElementFactory ( $document , $q , $rootScope , $timeout , $rootElement , $animate ,
$interpolate , $mdCompiler , $mdTheming ) {
var startSymbol = $interpolate . startSymbol ( ) ,
endSymbol = $interpolate . endSymbol ( ) ,
usesStandardSymbols = ( ( startSymbol === '{{' ) && ( endSymbol === '}}' ) ) ,
processTemplate = usesStandardSymbols ? angular . identity : replaceInterpolationSymbols ;
return function createInterimElementService ( ) {
/ *
* @ ngdoc service
* @ name $$interimElement . $service
*
* @ description
* A service used to control inserting and removing an element into the DOM .
*
* /
var stack = [ ] ;
var service ;
return service = {
show : show ,
hide : hide ,
cancel : cancel
} ;
2014-10-12 19:07:47 +02:00
2015-02-04 16:08:32 +01:00
/ *
* @ ngdoc method
* @ name $$interimElement . $service # show
* @ kind function
*
* @ description
* Adds the ` $ interimElement ` to the DOM and returns a promise that will be resolved or rejected
* with hide or cancel , respectively .
*
* @ param { * } options is hashMap of settings
* @ returns a Promise
*
* /
function show ( options ) {
if ( stack . length ) {
service . cancel ( ) ;
}
2014-10-12 19:07:47 +02:00
2015-02-04 16:08:32 +01:00
var interimElement = new InterimElement ( options ) ;
2014-10-12 19:07:47 +02:00
2015-02-04 16:08:32 +01:00
stack . push ( interimElement ) ;
return interimElement . show ( ) . then ( function ( ) {
return interimElement . deferred . promise ;
} ) ;
}
2014-10-12 19:07:47 +02:00
2015-02-04 16:08:32 +01:00
/ *
* @ ngdoc method
* @ name $$interimElement . $service # hide
* @ kind function
*
* @ description
* Removes the ` $ interimElement ` from the DOM and resolves the promise returned from ` show `
*
* @ param { * } resolveParam Data to resolve the promise with
* @ returns a Promise that will be resolved after the element has been removed .
*
* /
function hide ( response ) {
var interimElement = stack . shift ( ) ;
interimElement && interimElement . remove ( ) . then ( function ( ) {
interimElement . deferred . resolve ( response ) ;
} ) ;
2014-10-12 19:07:47 +02:00
2015-02-04 16:08:32 +01:00
return interimElement ? interimElement . deferred . promise : $q . when ( response ) ;
}
2014-10-12 19:07:47 +02:00
2015-02-04 16:08:32 +01:00
/ *
* @ ngdoc method
* @ name $$interimElement . $service # cancel
* @ kind function
*
* @ description
* Removes the ` $ interimElement ` from the DOM and rejects the promise returned from ` show `
*
* @ param { * } reason Data to reject the promise with
* @ returns Promise that will be rejected after the element has been removed .
*
* /
function cancel ( reason ) {
var interimElement = stack . shift ( ) ;
interimElement && interimElement . remove ( ) . then ( function ( ) {
interimElement . deferred . reject ( reason ) ;
} ) ;
2014-10-12 19:07:47 +02:00
2015-02-04 16:08:32 +01:00
return interimElement ? interimElement . deferred . promise : $q . reject ( reason ) ;
}
2014-10-12 19:07:47 +02:00
2015-02-04 16:08:32 +01:00
/ *
* 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 ) ,
onShow : function ( scope , element , options ) {
return $animate . enter ( element , options . parent ) ;
} ,
onRemove : function ( scope , element , options ) {
// Element could be undefined if a new element is shown before
// the old one finishes compiling.
return element && $animate . leave ( element ) || $q . when ( ) ;
}
} , options ) ;
2014-10-12 19:07:47 +02:00
2015-02-04 16:08:32 +01:00
if ( options . template ) {
options . template = processTemplate ( options . template ) ;
}
2014-10-12 19:07:47 +02:00
2015-02-04 16:08:32 +01:00
return self = {
options : options ,
deferred : $q . defer ( ) ,
show : function ( ) {
return $mdCompiler . compile ( options ) . then ( function ( compileData ) {
angular . extend ( compileData . locals , self . options ) ;
// Search for parent at insertion time, if not specified
if ( angular . isString ( options . parent ) ) {
options . parent = angular . element ( $document [ 0 ] . querySelector ( options . parent ) ) ;
} else if ( ! options . parent ) {
options . parent = $rootElement . find ( 'body' ) ;
if ( ! options . parent . length ) options . parent = $rootElement ;
}
2014-10-12 19:07:47 +02:00
2015-02-04 16:08:32 +01:00
element = compileData . link ( options . scope ) ;
if ( options . themable ) $mdTheming ( element ) ;
var ret = options . onShow ( options . scope , element , options ) ;
return $q . when ( ret )
. then ( function ( ) {
// Issue onComplete callback when the `show()` finishes
( options . onComplete || angular . noop ) ( options . scope , element , options ) ;
startHideTimeout ( ) ;
} ) ;
function startHideTimeout ( ) {
if ( options . hideDelay ) {
hideTimeout = $timeout ( service . cancel , options . hideDelay ) ;
}
}
} ) ;
} ,
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 ( ) ;
} ) ;
}
} ;
}
} ;
2014-10-12 19:07:47 +02:00
2015-02-04 16:08:32 +01:00
/ *
* Replace ` {{ ` and ` }} ` in a string ( usually a template ) with the actual start - / e n d S y m b o l s u s e d
* for interpolation . This allows pre - defined templates ( for components such as dialog , toast etc )
* to continue to work in apps that use custom interpolation start - / e n d S y m b o l s .
*
* @ param { string } text The text in which to replace ` {{ ` / ` }} `
* @ returns { string } The modified string using the actual interpolation start - / e n d S y m b o l s
* /
function replaceInterpolationSymbols ( text ) {
if ( ! text || ! angular . isString ( text ) ) return text ;
return text . replace ( /\{\{/g , startSymbol ) . replace ( /}}/g , endSymbol ) ;
}
}
2014-11-06 16:55:16 +01:00
2015-02-04 16:08:32 +01:00
}
2014-10-12 19:07:47 +02:00
2015-02-04 16:08:32 +01:00
} ) ( ) ;
2014-10-12 19:07:47 +02:00
2015-02-04 16:08:32 +01:00
/ * !
* Angular Material Design
* https : //github.com/angular/material
* @ license MIT
* v0 . 7.1
* /
( function ( ) {
'use strict' ;
/ * *
* @ ngdoc module
* @ name material . core . componentRegistry
*
* @ description
* A component instance registration service .
* Note : currently this as a private service in the SideNav component .
* /
angular . module ( 'material.core' )
. factory ( '$mdComponentRegistry' , ComponentRegistry ) ;
/ *
* @ private
* @ ngdoc factory
* @ name ComponentRegistry
* @ module material . core . componentRegistry
*
* /
function ComponentRegistry ( $log , $q ) {
var self ;
var instances = [ ] ;
var pendings = { } ;
return self = {
/ * *
* Used to print an error when an instance for a handle isn ' t found .
* /
notFoundError : function ( handle ) {
$log . error ( 'No instance found for handle' , handle ) ;
} ,
/ * *
* Return all registered instances as an array .
* /
getInstances : function ( ) {
return instances ;
} ,
/ * *
* Get a registered instance .
* @ param handle the String handle to look up for a registered instance .
* /
get : function ( handle ) {
if ( ! isValidID ( handle ) ) return null ;
var i , j , instance ;
for ( i = 0 , j = instances . length ; i < j ; i ++ ) {
instance = instances [ i ] ;
if ( instance . $$mdHandle === handle ) {
return instance ;
}
2014-10-12 19:07:47 +02:00
}
2015-02-04 16:08:32 +01:00
return null ;
} ,
2014-10-12 19:07:47 +02:00
2015-02-04 16:08:32 +01:00
/ * *
* Register an instance .
* @ param instance the instance to register
* @ param handle the handle to identify the instance under .
* /
register : function ( instance , handle ) {
if ( ! handle ) return angular . noop ;
instance . $$mdHandle = handle ;
instances . push ( instance ) ;
resolveWhen ( ) ;
return deregister ;
/ * *
* Remove registration for an instance
* /
function deregister ( ) {
var index = instances . indexOf ( instance ) ;
if ( index !== - 1 ) {
instances . splice ( index , 1 ) ;
}
}
2014-10-12 19:07:47 +02:00
2015-02-04 16:08:32 +01:00
/ * *
* Resolve any pending promises for this instance
* /
function resolveWhen ( ) {
var dfd = pendings [ handle ] ;
if ( dfd ) {
dfd . resolve ( instance ) ;
delete pendings [ handle ] ;
}
}
} ,
/ * *
* Async accessor to registered component instance
* If not available then a promise is created to notify
* all listeners when the instance is registered .
* /
when : function ( handle ) {
if ( isValidID ( handle ) ) {
var deferred = $q . defer ( ) ;
var instance = self . get ( handle ) ;
if ( instance ) {
deferred . resolve ( instance ) ;
} else {
pendings [ handle ] = deferred ;
}
return deferred . promise ;
2014-11-03 17:32:25 +01:00
}
2015-02-04 16:08:32 +01:00
return $q . reject ( "Invalid `md-component-id` value." ) ;
2014-10-12 19:07:47 +02:00
}
2015-02-04 16:08:32 +01:00
2014-11-03 17:32:25 +01:00
} ;
2015-02-04 16:08:32 +01:00
function isValidID ( handle ) {
return handle && ( handle !== "" ) ;
}
2014-10-12 19:07:47 +02:00
}
2015-02-04 16:08:32 +01:00
ComponentRegistry . $inject = [ "$log" , "$q" ] ;
2014-09-30 12:29:53 +02:00
2014-11-03 17:32:25 +01:00
} ) ( ) ;
2015-02-04 16:08:32 +01:00
/ * !
* Angular Material Design
* https : //github.com/angular/material
* @ license MIT
* v0 . 7.1
2014-09-30 12:29:53 +02:00
* /
2015-02-04 16:08:32 +01:00
( function ( ) {
'use strict' ;
2014-09-30 12:29:53 +02:00
2015-02-04 16:08:32 +01:00
angular . module ( 'material.core' )
. factory ( '$mdInkRipple' , InkRippleService )
. directive ( 'mdInkRipple' , InkRippleDirective )
. directive ( 'mdNoInk' , attrNoDirective ( ) )
. directive ( 'mdNoBar' , attrNoDirective ( ) )
. directive ( 'mdNoStretch' , attrNoDirective ( ) ) ;
function InkRippleDirective ( $mdInkRipple ) {
2014-09-30 12:29:53 +02:00
return {
2015-02-04 16:08:32 +01:00
controller : angular . noop ,
link : function ( scope , element , attr ) {
if ( attr . hasOwnProperty ( 'mdInkRippleCheckbox' ) ) {
$mdInkRipple . attachCheckboxBehavior ( scope , element ) ;
} else {
$mdInkRipple . attachButtonBehavior ( scope , element ) ;
}
2014-09-30 12:29:53 +02:00
}
} ;
}
2015-02-04 16:08:32 +01:00
InkRippleDirective . $inject = [ "$mdInkRipple" ] ;
2014-09-30 12:29:53 +02:00
2015-02-04 16:08:32 +01:00
function InkRippleService ( $window , $timeout ) {
2014-09-30 12:29:53 +02:00
2014-11-03 17:32:25 +01:00
return {
2015-02-04 16:08:32 +01:00
attachButtonBehavior : attachButtonBehavior ,
attachCheckboxBehavior : attachCheckboxBehavior ,
attachTabBehavior : attachTabBehavior ,
attach : attach
2014-11-03 17:32:25 +01:00
} ;
2014-09-30 12:29:53 +02:00
2015-02-04 16:08:32 +01:00
function attachButtonBehavior ( scope , element , options ) {
return attach ( scope , element , angular . extend ( {
isFAB : element . hasClass ( 'md-fab' ) ,
isMenuItem : element . hasClass ( 'md-menu-item' ) ,
center : false ,
dimBackground : true
} , options ) ) ;
}
2014-10-12 19:07:47 +02:00
2015-02-04 16:08:32 +01:00
function attachCheckboxBehavior ( scope , element , options ) {
return attach ( scope , element , angular . extend ( {
center : true ,
dimBackground : false ,
fitRipple : true
} , options ) ) ;
}
2014-09-30 12:29:53 +02:00
2015-02-04 16:08:32 +01:00
function attachTabBehavior ( scope , element , options ) {
return attach ( scope , element , angular . extend ( {
center : false ,
dimBackground : true ,
outline : true
} , options ) ) ;
}
2014-09-30 12:29:53 +02:00
2015-02-04 16:08:32 +01:00
function attach ( scope , element , options ) {
if ( element . controller ( 'mdNoInk' ) ) return angular . noop ;
2014-09-30 12:29:53 +02:00
2015-02-04 16:08:32 +01:00
options = angular . extend ( {
colorElement : element ,
mousedown : true ,
hover : true ,
focus : true ,
center : false ,
mousedownPauseTime : 150 ,
dimBackground : false ,
outline : false ,
isFAB : false ,
isMenuItem : false ,
fitRipple : false
} , options ) ;
var rippleSize ,
controller = element . controller ( 'mdInkRipple' ) || { } ,
counter = 0 ,
ripples = [ ] ,
states = [ ] ,
isActiveExpr = element . attr ( 'md-highlight' ) ,
isActive = false ,
isHeld = false ,
node = element [ 0 ] ,
rippleSizeSetting = element . attr ( 'md-ripple-size' ) ,
color = parseColor ( element . attr ( 'md-ink-ripple' ) ) || parseColor ( $window . getComputedStyle ( options . colorElement [ 0 ] ) . color || 'rgb(0, 0, 0)' ) ;
switch ( rippleSizeSetting ) {
case 'full' :
options . isFAB = true ;
break ;
case 'partial' :
options . isFAB = false ;
break ;
}
2014-10-12 19:07:47 +02:00
2015-02-04 16:08:32 +01:00
// expose onInput for ripple testing
if ( options . mousedown ) {
element . on ( '$md.pressdown' , onPressDown )
. on ( '$md.pressup' , onPressUp ) ;
2014-10-12 19:07:47 +02:00
}
2015-02-04 16:08:32 +01:00
controller . createRipple = createRipple ;
2014-09-30 12:29:53 +02:00
2015-02-04 16:08:32 +01:00
if ( isActiveExpr ) {
scope . $watch ( isActiveExpr , function watchActive ( newValue ) {
isActive = newValue ;
if ( isActive && ! ripples . length ) {
$timeout ( function ( ) { createRipple ( 0 , 0 ) ; } , 0 , false ) ;
}
angular . forEach ( ripples , updateElement ) ;
} ) ;
}
2014-09-30 12:29:53 +02:00
2015-02-04 16:08:32 +01:00
// Publish self-detach method if desired...
return function detach ( ) {
element . off ( '$md.pressdown' , onPressDown )
. off ( '$md.pressup' , onPressUp ) ;
getRippleContainer ( ) . remove ( ) ;
} ;
2014-09-30 12:29:53 +02:00
2015-02-04 16:08:32 +01:00
/ * *
* Gets the current ripple container
* If there is no ripple container , it creates one and returns it
*
* @ returns { angular . element } ripple container element
* /
function getRippleContainer ( ) {
var container = element . data ( '$mdRippleContainer' ) ;
if ( container ) return container ;
container = angular . element ( '<div class="md-ripple-container">' ) ;
element . append ( container ) ;
element . data ( '$mdRippleContainer' , container ) ;
return container ;
2014-10-12 19:07:47 +02:00
}
2014-09-30 12:29:53 +02:00
2015-02-04 16:08:32 +01:00
function parseColor ( color ) {
if ( ! color ) return ;
if ( color . indexOf ( 'rgba' ) === 0 ) return color . replace ( /\d?\.?\d*\s*\)\s*$/ , '0.1)' ) ;
if ( color . indexOf ( 'rgb' ) === 0 ) return rgbToRGBA ( color ) ;
if ( color . indexOf ( '#' ) === 0 ) return hexToRGBA ( color ) ;
2014-09-30 12:29:53 +02:00
2015-02-04 16:08:32 +01:00
/ * *
* Converts a hex value to an rgba string
*
* @ param { string } hex value ( 3 or 6 digits ) to be converted
*
* @ returns { string } rgba color with 0.1 alpha
* /
function hexToRGBA ( color ) {
var hex = color . charAt ( 0 ) === '#' ? color . substr ( 1 ) : color ,
dig = hex . length / 3 ,
red = hex . substr ( 0 , dig ) ,
grn = hex . substr ( dig , dig ) ,
blu = hex . substr ( dig * 2 ) ;
if ( dig === 1 ) {
red += red ;
grn += grn ;
blu += blu ;
}
return 'rgba(' + parseInt ( red , 16 ) + ',' + parseInt ( grn , 16 ) + ',' + parseInt ( blu , 16 ) + ',0.1)' ;
2014-09-30 12:29:53 +02:00
}
2015-02-04 16:08:32 +01:00
/ * *
* Converts rgb value to rgba string
*
* @ param { string } rgb color string
*
* @ returns { string } rgba color with 0.1 alpha
* /
function rgbToRGBA ( color ) {
return color . replace ( ')' , ', 0.1)' ) . replace ( '(' , 'a(' ) ;
2014-10-12 19:07:47 +02:00
}
2014-09-30 12:29:53 +02:00
2015-02-04 16:08:32 +01:00
}
2014-09-30 12:29:53 +02:00
2015-02-04 16:08:32 +01:00
function removeElement ( elem , wait ) {
ripples . splice ( ripples . indexOf ( elem ) , 1 ) ;
if ( ripples . length === 0 ) {
getRippleContainer ( ) . css ( { backgroundColor : '' } ) ;
2014-09-30 12:29:53 +02:00
}
2015-02-04 16:08:32 +01:00
$timeout ( function ( ) { elem . remove ( ) ; } , wait , false ) ;
2014-10-12 19:07:47 +02:00
}
2014-09-30 12:29:53 +02:00
2015-02-04 16:08:32 +01:00
function updateElement ( elem ) {
var index = ripples . indexOf ( elem ) ,
state = states [ index ] || { } ,
elemIsActive = ripples . length > 1 ? false : isActive ,
elemIsHeld = ripples . length > 1 ? false : isHeld ;
if ( elemIsActive || state . animating || elemIsHeld ) {
elem . addClass ( 'md-ripple-visible' ) ;
} else if ( elem ) {
elem . removeClass ( 'md-ripple-visible' ) ;
if ( options . outline ) {
elem . css ( {
width : rippleSize + 'px' ,
height : rippleSize + 'px' ,
marginLeft : ( rippleSize * - 1 ) + 'px' ,
marginTop : ( rippleSize * - 1 ) + 'px'
} ) ;
}
removeElement ( elem , options . outline ? 450 : 650 ) ;
}
}
2014-10-12 19:07:47 +02:00
2015-02-04 16:08:32 +01:00
/ * *
* Creates a ripple at the provided coordinates
*
* @ param { number } left cursor position
* @ param { number } top cursor position
*
* @ returns { angular . element } the generated ripple element
* /
function createRipple ( left , top ) {
2014-10-12 19:07:47 +02:00
2015-02-04 16:08:32 +01:00
color = parseColor ( element . attr ( 'md-ink-ripple' ) ) || parseColor ( $window . getComputedStyle ( options . colorElement [ 0 ] ) . color || 'rgb(0, 0, 0)' ) ;
var container = getRippleContainer ( ) ,
size = getRippleSize ( left , top ) ,
css = getRippleCss ( size , left , top ) ,
elem = getRippleElement ( css ) ,
index = ripples . indexOf ( elem ) ,
state = states [ index ] || { } ;
rippleSize = size ;
state . animating = true ;
$timeout ( function ( ) {
if ( options . dimBackground ) {
container . css ( { backgroundColor : color } ) ;
}
elem . addClass ( 'md-ripple-placed md-ripple-scaled' ) ;
if ( options . outline ) {
elem . css ( {
borderWidth : ( size * 0.5 ) + 'px' ,
marginLeft : ( size * - 0.5 ) + 'px' ,
marginTop : ( size * - 0.5 ) + 'px'
} ) ;
} else {
elem . css ( { left : '50%' , top : '50%' } ) ;
}
updateElement ( elem ) ;
$timeout ( function ( ) {
state . animating = false ;
updateElement ( elem ) ;
} , ( options . outline ? 450 : 225 ) , false ) ;
} , 0 , false ) ;
return elem ;
/ * *
* Creates the ripple element with the provided css
*
* @ param { object } css properties to be applied
*
* @ returns { angular . element } the generated ripple element
* /
function getRippleElement ( css ) {
var elem = angular . element ( '<div class="md-ripple" data-counter="' + counter ++ + '">' ) ;
ripples . unshift ( elem ) ;
states . unshift ( { animating : true } ) ;
container . append ( elem ) ;
css && elem . css ( css ) ;
return elem ;
}
/ * *
* Calculate the ripple size
*
* @ returns { number } calculated ripple diameter
* /
function getRippleSize ( left , top ) {
var width = container . prop ( 'offsetWidth' ) ,
height = container . prop ( 'offsetHeight' ) ,
multiplier , size , rect ;
if ( options . isMenuItem ) {
size = Math . sqrt ( Math . pow ( width , 2 ) + Math . pow ( height , 2 ) ) ;
} else if ( options . outline ) {
rect = node . getBoundingClientRect ( ) ;
left -= rect . left ;
top -= rect . top ;
width = Math . max ( left , width - left ) ;
height = Math . max ( top , height - top ) ;
size = 2 * Math . sqrt ( Math . pow ( width , 2 ) + Math . pow ( height , 2 ) ) ;
} else {
multiplier = options . isFAB ? 1.1 : 0.8 ;
size = Math . sqrt ( Math . pow ( width , 2 ) + Math . pow ( height , 2 ) ) * multiplier ;
if ( options . fitRipple ) {
size = Math . min ( height , width , size ) ;
}
}
return size ;
}
/ * *
* Generates the ripple css
*
* @ param { number } the diameter of the ripple
* @ param { number } the left cursor offset
* @ param { number } the top cursor offset
*
* @ returns { { backgroundColor : * , width : string , height : string , marginLeft : string , marginTop : string } }
* /
function getRippleCss ( size , left , top ) {
var rect ,
css = {
backgroundColor : rgbaToRGB ( color ) ,
borderColor : rgbaToRGB ( color ) ,
width : size + 'px' ,
height : size + 'px'
} ;
if ( options . outline ) {
css . width = 0 ;
css . height = 0 ;
} else {
css . marginLeft = css . marginTop = ( size * - 0.5 ) + 'px' ;
}
if ( options . center ) {
css . left = css . top = '50%' ;
} else {
rect = node . getBoundingClientRect ( ) ;
css . left = Math . round ( ( left - rect . left ) / container . prop ( 'offsetWidth' ) * 100 ) + '%' ;
css . top = Math . round ( ( top - rect . top ) / container . prop ( 'offsetHeight' ) * 100 ) + '%' ;
}
return css ;
/ * *
* Converts rgba string to rgb , removing the alpha value
*
* @ param { string } rgba color
*
* @ returns { string } rgb color
* /
function rgbaToRGB ( color ) {
return color . replace ( 'rgba' , 'rgb' ) . replace ( /,[^\)\,]+\)/ , ')' ) ;
}
}
2014-10-12 19:07:47 +02:00
}
2014-09-30 12:29:53 +02:00
2015-02-04 16:08:32 +01:00
/ * *
* Handles user input start and stop events
*
* /
function onPressDown ( ev ) {
if ( ! isRippleAllowed ( ) ) return ;
2014-09-30 12:29:53 +02:00
2015-02-04 16:08:32 +01:00
var ripple = createRipple ( ev . pointer . x , ev . pointer . y ) ;
isHeld = true ;
}
function onPressUp ( ev ) {
isHeld = false ;
var ripple = ripples [ ripples . length - 1 ] ;
$timeout ( function ( ) { updateElement ( ripple ) ; } , 0 , false ) ;
}
2014-09-30 12:29:53 +02:00
2015-02-04 16:08:32 +01:00
/ * *
* Determines if the ripple is allowed
*
* @ returns { boolean } true if the ripple is allowed , false if not
* /
function isRippleAllowed ( ) {
var parent = node . parentNode ;
var grandparent = parent && parent . parentNode ;
var ancestor = grandparent && grandparent . parentNode ;
return ! isDisabled ( node ) && ! isDisabled ( parent ) && ! isDisabled ( grandparent ) && ! isDisabled ( ancestor ) ;
function isDisabled ( elem ) {
return elem && elem . hasAttribute && elem . hasAttribute ( 'disabled' ) ;
}
2014-09-30 12:29:53 +02:00
}
2015-02-04 16:08:32 +01:00
2014-09-30 12:29:53 +02:00
}
}
2015-02-04 16:08:32 +01:00
InkRippleService . $inject = [ "$window" , "$timeout" ] ;
2014-09-30 12:29:53 +02:00
/ * *
2015-02-04 16:08:32 +01:00
* 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 .
2014-09-30 12:29:53 +02:00
*
2014-11-03 17:32:25 +01:00
* @ usage
2014-09-30 12:29:53 +02:00
* < hljs lang = "html" >
2015-02-04 16:08:32 +01:00
* < parent md - no - ink >
* < child detect - no >
* < / c h i l d >
* < / p a r e n t >
2014-09-30 12:29:53 +02:00
* < / h l j s >
2014-11-03 17:32:25 +01:00
*
2015-02-04 16:08:32 +01:00
* < hljs lang = "js" >
* myApp . directive ( 'detectNo' , function ( ) {
* return {
* require : [ '^?mdNoInk' , ^ ? mdNoBar ' ] ,
* link : function ( scope , element , attr , ctrls ) {
* var noinkCtrl = ctrls [ 0 ] ;
* var nobarCtrl = ctrls [ 1 ] ;
* if ( noInkCtrl ) {
* alert ( "the md-no-ink flag has been specified on an ancestor!" ) ;
* }
* if ( nobarCtrl ) {
* alert ( "the md-no-bar flag has been specified on an ancestor!" ) ;
* }
* }
* } ;
* } ) ;
* < / h l j s >
2014-09-30 12:29:53 +02:00
* /
2015-02-04 16:08:32 +01:00
function attrNoDirective ( ) {
return function ( ) {
return {
controller : angular . noop
} ;
2014-09-30 12:29:53 +02:00
} ;
}
2014-11-03 17:32:25 +01:00
} ) ( ) ;
2014-09-30 12:29:53 +02:00
2015-02-04 16:08:32 +01:00
/ * !
* Angular Material Design
* https : //github.com/angular/material
* @ license MIT
* v0 . 7.1
* /
2014-11-03 17:32:25 +01:00
( function ( ) {
2015-02-04 16:08:32 +01:00
'use strict' ;
angular . module ( 'material.core.theming.palette' , [ ] )
. constant ( '$mdColorPalette' , {
'red' : {
'50' : '#ffebee' ,
'100' : '#ffcdd2' ,
'200' : '#ef9a9a' ,
'300' : '#e57373' ,
'400' : '#ef5350' ,
'500' : '#f44336' ,
'600' : '#e53935' ,
'700' : '#d32f2f' ,
'800' : '#c62828' ,
'900' : '#b71c1c' ,
'A100' : '#ff8a80' ,
'A200' : '#ff5252' ,
'A400' : '#ff1744' ,
'A700' : '#d50000' ,
'contrastDefaultColor' : 'light' ,
'contrastDarkColors' : '50 100 200 300 400 A100' ,
'contrastStrongLightColors' : '500 600 700 A200 A400 A700'
} ,
'pink' : {
'50' : '#fce4ec' ,
'100' : '#f8bbd0' ,
'200' : '#f48fb1' ,
'300' : '#f06292' ,
'400' : '#ec407a' ,
'500' : '#e91e63' ,
'600' : '#d81b60' ,
'700' : '#c2185b' ,
'800' : '#ad1457' ,
'900' : '#880e4f' ,
'A100' : '#ff80ab' ,
'A200' : '#ff4081' ,
'A400' : '#f50057' ,
'A700' : '#c51162' ,
'contrastDefaultColor' : 'light' ,
'contrastDarkColors' : '50 100 200 300 400 A100' ,
'contrastStrongLightColors' : '500 600 A200 A400 A700'
} ,
'purple' : {
'50' : '#f3e5f5' ,
'100' : '#e1bee7' ,
'200' : '#ce93d8' ,
'300' : '#ba68c8' ,
'400' : '#ab47bc' ,
'500' : '#9c27b0' ,
'600' : '#8e24aa' ,
'700' : '#7b1fa2' ,
'800' : '#6a1b9a' ,
'900' : '#4a148c' ,
'A100' : '#ea80fc' ,
'A200' : '#e040fb' ,
'A400' : '#d500f9' ,
'A700' : '#aa00ff' ,
'contrastDefaultColor' : 'light' ,
'contrastDarkColors' : '50 100 200 A100' ,
'contrastStrongLightColors' : '300 400 A200 A400 A700'
} ,
'deep-purple' : {
'50' : '#ede7f6' ,
'100' : '#d1c4e9' ,
'200' : '#b39ddb' ,
'300' : '#9575cd' ,
'400' : '#7e57c2' ,
'500' : '#673ab7' ,
'600' : '#5e35b1' ,
'700' : '#512da8' ,
'800' : '#4527a0' ,
'900' : '#311b92' ,
'A100' : '#b388ff' ,
'A200' : '#7c4dff' ,
'A400' : '#651fff' ,
'A700' : '#6200ea' ,
'contrastDefaultColor' : 'light' ,
'contrastDarkColors' : '50 100 200 A100' ,
'contrastStrongLightColors' : '300 400 A200'
} ,
'indigo' : {
'50' : '#e8eaf6' ,
'100' : '#c5cae9' ,
'200' : '#9fa8da' ,
'300' : '#7986cb' ,
'400' : '#5c6bc0' ,
'500' : '#3f51b5' ,
'600' : '#3949ab' ,
'700' : '#303f9f' ,
'800' : '#283593' ,
'900' : '#1a237e' ,
'A100' : '#8c9eff' ,
'A200' : '#536dfe' ,
'A400' : '#3d5afe' ,
'A700' : '#304ffe' ,
'contrastDefaultColor' : 'light' ,
'contrastDarkColors' : '50 100 200 A100' ,
'contrastStrongLightColors' : '300 400 A200 A400'
} ,
'blue' : {
'50' : '#e3f2fd' ,
'100' : '#bbdefb' ,
'200' : '#90caf9' ,
'300' : '#64b5f6' ,
'400' : '#42a5f5' ,
'500' : '#2196f3' ,
'600' : '#1e88e5' ,
'700' : '#1976d2' ,
'800' : '#1565c0' ,
'900' : '#0d47a1' ,
'A100' : '#82b1ff' ,
'A200' : '#448aff' ,
'A400' : '#2979ff' ,
'A700' : '#2962ff' ,
'contrastDefaultColor' : 'light' ,
'contrastDarkColors' : '100 200 300 400 A100' ,
'contrastStrongLightColors' : '500 600 700 A200 A400 A700'
} ,
'light-blue' : {
'50' : '#e1f5fe' ,
'100' : '#b3e5fc' ,
'200' : '#81d4fa' ,
'300' : '#4fc3f7' ,
'400' : '#29b6f6' ,
'500' : '#03a9f4' ,
'600' : '#039be5' ,
'700' : '#0288d1' ,
'800' : '#0277bd' ,
'900' : '#01579b' ,
'A100' : '#80d8ff' ,
'A200' : '#40c4ff' ,
'A400' : '#00b0ff' ,
'A700' : '#0091ea' ,
'contrastDefaultColor' : 'dark' ,
'contrastLightColors' : '500 600 700 800 900 A700' ,
'contrastStrongLightColors' : '500 600 700 800 A700'
} ,
'cyan' : {
'50' : '#e0f7fa' ,
'100' : '#b2ebf2' ,
'200' : '#80deea' ,
'300' : '#4dd0e1' ,
'400' : '#26c6da' ,
'500' : '#00bcd4' ,
'600' : '#00acc1' ,
'700' : '#0097a7' ,
'800' : '#00838f' ,
'900' : '#006064' ,
'A100' : '#84ffff' ,
'A200' : '#18ffff' ,
'A400' : '#00e5ff' ,
'A700' : '#00b8d4' ,
'contrastDefaultColor' : 'dark' ,
'contrastLightColors' : '500 600 700 800 900' ,
'contrastStrongLightColors' : '500 600 700 800'
} ,
'teal' : {
'50' : '#e0f2f1' ,
'100' : '#b2dfdb' ,
'200' : '#80cbc4' ,
'300' : '#4db6ac' ,
'400' : '#26a69a' ,
'500' : '#009688' ,
'600' : '#00897b' ,
'700' : '#00796b' ,
'800' : '#00695c' ,
'900' : '#004d40' ,
'A100' : '#a7ffeb' ,
'A200' : '#64ffda' ,
'A400' : '#1de9b6' ,
'A700' : '#00bfa5' ,
'contrastDefaultColor' : 'dark' ,
'contrastLightColors' : '500 600 700 800 900' ,
'contrastStrongLightColors' : '500 600 700'
} ,
'green' : {
'50' : '#e8f5e9' ,
'100' : '#c8e6c9' ,
'200' : '#a5d6a7' ,
'300' : '#81c784' ,
'400' : '#66bb6a' ,
'500' : '#4caf50' ,
'600' : '#43a047' ,
'700' : '#388e3c' ,
'800' : '#2e7d32' ,
'900' : '#1b5e20' ,
'A100' : '#b9f6ca' ,
'A200' : '#69f0ae' ,
'A400' : '#00e676' ,
'A700' : '#00c853' ,
'contrastDefaultColor' : 'dark' ,
'contrastLightColors' : '500 600 700 800 900' ,
'contrastStrongLightColors' : '500 600 700'
} ,
'light-green' : {
'50' : '#f1f8e9' ,
'100' : '#dcedc8' ,
'200' : '#c5e1a5' ,
'300' : '#aed581' ,
'400' : '#9ccc65' ,
'500' : '#8bc34a' ,
'600' : '#7cb342' ,
'700' : '#689f38' ,
'800' : '#558b2f' ,
'900' : '#33691e' ,
'A100' : '#ccff90' ,
'A200' : '#b2ff59' ,
'A400' : '#76ff03' ,
'A700' : '#64dd17' ,
'contrastDefaultColor' : 'dark' ,
'contrastLightColors' : '800 900' ,
'contrastStrongLightColors' : '800 900'
} ,
'lime' : {
'50' : '#f9fbe7' ,
'100' : '#f0f4c3' ,
'200' : '#e6ee9c' ,
'300' : '#dce775' ,
'400' : '#d4e157' ,
'500' : '#cddc39' ,
'600' : '#c0ca33' ,
'700' : '#afb42b' ,
'800' : '#9e9d24' ,
'900' : '#827717' ,
'A100' : '#f4ff81' ,
'A200' : '#eeff41' ,
'A400' : '#c6ff00' ,
'A700' : '#aeea00' ,
'contrastDefaultColor' : 'dark' ,
'contrastLightColors' : '900' ,
'contrastStrongLightColors' : '900'
} ,
'yellow' : {
'50' : '#fffde7' ,
'100' : '#fff9c4' ,
'200' : '#fff59d' ,
'300' : '#fff176' ,
'400' : '#ffee58' ,
'500' : '#ffeb3b' ,
'600' : '#fdd835' ,
'700' : '#fbc02d' ,
'800' : '#f9a825' ,
'900' : '#f57f17' ,
'A100' : '#ffff8d' ,
'A200' : '#ffff00' ,
'A400' : '#ffea00' ,
'A700' : '#ffd600' ,
'contrastDefaultColor' : 'dark'
} ,
'amber' : {
'50' : '#fff8e1' ,
'100' : '#ffecb3' ,
'200' : '#ffe082' ,
'300' : '#ffd54f' ,
'400' : '#ffca28' ,
'500' : '#ffc107' ,
'600' : '#ffb300' ,
'700' : '#ffa000' ,
'800' : '#ff8f00' ,
'900' : '#ff6f00' ,
'A100' : '#ffe57f' ,
'A200' : '#ffd740' ,
'A400' : '#ffc400' ,
'A700' : '#ffab00' ,
'contrastDefaultColor' : 'dark'
} ,
'orange' : {
'50' : '#fff3e0' ,
'100' : '#ffe0b2' ,
'200' : '#ffcc80' ,
'300' : '#ffb74d' ,
'400' : '#ffa726' ,
'500' : '#ff9800' ,
'600' : '#fb8c00' ,
'700' : '#f57c00' ,
'800' : '#ef6c00' ,
'900' : '#e65100' ,
'A100' : '#ffd180' ,
'A200' : '#ffab40' ,
'A400' : '#ff9100' ,
'A700' : '#ff6d00' ,
'contrastDefaultColor' : 'dark' ,
'contrastLightColors' : '800 900' ,
'contrastStrongLightColors' : '800 900'
} ,
'deep-orange' : {
'50' : '#fbe9e7' ,
'100' : '#ffccbc' ,
'200' : '#ffab91' ,
'300' : '#ff8a65' ,
'400' : '#ff7043' ,
'500' : '#ff5722' ,
'600' : '#f4511e' ,
'700' : '#e64a19' ,
'800' : '#d84315' ,
'900' : '#bf360c' ,
'A100' : '#ff9e80' ,
'A200' : '#ff6e40' ,
'A400' : '#ff3d00' ,
'A700' : '#dd2c00' ,
'contrastDefaultColor' : 'light' ,
'contrastDarkColors' : '50 100 200 300 400 A100 A200' ,
'contrastStrongLightColors' : '500 600 700 800 900 A400 A700'
} ,
'brown' : {
'50' : '#efebe9' ,
'100' : '#d7ccc8' ,
'200' : '#bcaaa4' ,
'300' : '#a1887f' ,
'400' : '#8d6e63' ,
'500' : '#795548' ,
'600' : '#6d4c41' ,
'700' : '#5d4037' ,
'800' : '#4e342e' ,
'900' : '#3e2723' ,
'A100' : '#d7ccc8' ,
'A200' : '#bcaaa4' ,
'A400' : '#8d6e63' ,
'A700' : '#5d4037' ,
'contrastDefaultColor' : 'light' ,
'contrastDarkColors' : '50 100 200' ,
'contrastStrongLightColors' : '300 400'
} ,
'grey' : {
'0' : '#ffffff' ,
'50' : '#fafafa' ,
'100' : '#f5f5f5' ,
'200' : '#eeeeee' ,
'300' : '#e0e0e0' ,
'400' : '#bdbdbd' ,
'500' : '#9e9e9e' ,
'600' : '#757575' ,
'700' : '#616161' ,
'800' : '#424242' ,
'900' : '#212121' ,
'1000' : '#000000' ,
'A100' : '#ffffff' ,
'A200' : '#eeeeee' ,
'A400' : '#bdbdbd' ,
'A700' : '#616161' ,
'contrastDefaultColor' : 'dark' ,
'contrastLightColors' : '600 700 800 900'
} ,
'blue-grey' : {
'50' : '#eceff1' ,
'100' : '#cfd8dc' ,
'200' : '#b0bec5' ,
'300' : '#90a4ae' ,
'400' : '#78909c' ,
'500' : '#607d8b' ,
'600' : '#546e7a' ,
'700' : '#455a64' ,
'800' : '#37474f' ,
'900' : '#263238' ,
'A100' : '#cfd8dc' ,
'A200' : '#b0bec5' ,
'A400' : '#78909c' ,
'A700' : '#455a64' ,
'contrastDefaultColor' : 'light' ,
'contrastDarkColors' : '50 100 200 300' ,
'contrastStrongLightColors' : '400 500'
}
} ) ;
2014-11-03 17:32:25 +01:00
} ) ( ) ;
2014-09-30 12:29:53 +02:00
2015-02-04 16:08:32 +01:00
/ * !
* Angular Material Design
* https : //github.com/angular/material
* @ license MIT
* v0 . 7.1
2014-09-30 12:29:53 +02:00
* /
2015-02-04 16:08:32 +01:00
( function ( ) {
'use strict' ;
2014-09-30 12:29:53 +02:00
2015-02-04 16:08:32 +01:00
angular . module ( 'material.core.theming' , [ 'material.core.theming.palette' ] )
. directive ( 'mdTheme' , ThemingDirective )
. directive ( 'mdThemable' , ThemableDirective )
. provider ( '$mdTheming' , ThemingProvider )
. run ( generateThemes ) ;
2014-09-30 12:29:53 +02:00
/ * *
2015-02-04 16:08:32 +01:00
* @ ngdoc provider
* @ name $mdThemingProvider
* @ module material . core
2014-09-30 12:29:53 +02:00
*
2015-02-04 16:08:32 +01:00
* @ description Provider to configure the ` $ mdTheming ` service .
2014-09-30 12:29:53 +02:00
* /
/ * *
2015-02-04 16:08:32 +01:00
* @ ngdoc method
* @ name $mdThemingProvider # setDefaultTheme
* @ param { string } themeName Default theme name to be applied to elements . Default value is ` default ` .
2014-09-30 12:29:53 +02:00
* /
/ * *
2015-02-04 16:08:32 +01:00
* @ ngdoc method
* @ name $mdThemingProvider # alwaysWatchTheme
* @ param { boolean } watch Whether or not to always watch themes for changes and re - apply
* classes when they change . Default is ` false ` . Enabling can reduce performance .
2014-09-30 12:29:53 +02:00
* /
2014-11-03 17:32:25 +01:00
2015-02-04 16:08:32 +01:00
// In memory storage of defined themes and color palettes (both loaded by CSS, and user specified)
var PALETTES ;
var THEMES ;
var themingProvider ;
var generationIsDone ;
var DARK _FOREGROUND = {
name : 'dark' ,
'1' : 'rgba(0,0,0,0.87)' ,
'2' : 'rgba(0,0,0,0.54)' ,
'3' : 'rgba(0,0,0,0.26)' ,
'4' : 'rgba(0,0,0,0.12)'
} ;
var LIGHT _FOREGROUND = {
name : 'light' ,
'1' : 'rgba(255,255,255,1.0)' ,
'2' : 'rgba(255,255,255,0.7)' ,
'3' : 'rgba(255,255,255,0.3)' ,
'4' : 'rgba(255,255,255,0.12)'
} ;
2014-11-03 17:32:25 +01:00
2015-02-04 16:08:32 +01:00
var DARK _SHADOW = '1px 1px 0px rgba(0,0,0,0.4), -1px -1px 0px rgba(0,0,0,0.4)' ;
var LIGHT _SHADOW = '' ;
2014-09-30 12:29:53 +02:00
2015-02-04 16:08:32 +01:00
var DARK _CONTRAST _COLOR = colorToRgbaArray ( 'rgba(0,0,0,0.87)' ) ;
var LIGHT _CONTRAST _COLOR = colorToRgbaArray ( 'rgba(255,255,255,0.87' ) ;
var STRONG _LIGHT _CONTRAST _COLOR = colorToRgbaArray ( 'rgb(255,255,255)' ) ;
2014-09-30 12:29:53 +02:00
2015-02-04 16:08:32 +01:00
var THEME _COLOR _TYPES = [ 'primary' , 'accent' , 'warn' , 'background' ] ;
var DEFAULT _COLOR _TYPE = 'primary' ;
2014-09-30 12:29:53 +02:00
2015-02-04 16:08:32 +01:00
// A color in a theme will use these hues by default, if not specified by user.
var LIGHT _DEFAULT _HUES = {
'accent' : {
'default' : 'A200' ,
'hue-1' : 'A100' ,
'hue-2' : 'A400' ,
'hue-3' : 'A700'
}
} ;
var DARK _DEFAULT _HUES = {
'background' : {
'default' : '500' ,
'hue-1' : '300' ,
'hue-2' : '600' ,
'hue-3' : '800'
2014-11-03 17:32:25 +01:00
}
2015-02-04 16:08:32 +01:00
} ;
THEME _COLOR _TYPES . forEach ( function ( colorType ) {
// Color types with unspecified default hues will use these default hue values
var defaultDefaultHues = {
'default' : '500' ,
'hue-1' : '300' ,
'hue-2' : '800' ,
'hue-3' : 'A100'
} ;
if ( ! LIGHT _DEFAULT _HUES [ colorType ] ) LIGHT _DEFAULT _HUES [ colorType ] = defaultDefaultHues ;
if ( ! DARK _DEFAULT _HUES [ colorType ] ) DARK _DEFAULT _HUES [ colorType ] = defaultDefaultHues ;
} ) ;
2014-09-30 12:29:53 +02:00
2015-02-04 16:08:32 +01:00
var VALID _HUE _VALUES = [
'50' , '100' , '200' , '300' , '400' , '500' , '600' ,
'700' , '800' , '900' , 'A100' , 'A200' , 'A400' , 'A700'
] ;
2014-09-30 12:29:53 +02:00
2015-02-04 16:08:32 +01:00
function ThemingProvider ( $mdColorPalette ) {
PALETTES = { } ;
THEMES = { } ;
var defaultTheme = 'default' ;
var alwaysWatchTheme = false ;
2014-09-30 12:29:53 +02:00
2015-02-04 16:08:32 +01:00
// Load JS Defined Palettes
angular . extend ( PALETTES , $mdColorPalette ) ;
2014-09-30 12:29:53 +02:00
2015-02-04 16:08:32 +01:00
// Default theme defined in core.js
2014-11-03 17:32:25 +01:00
2015-02-04 16:08:32 +01:00
ThemingService . $inject = [ "$rootScope" , "$log" ] ;
return themingProvider = {
definePalette : definePalette ,
extendPalette : extendPalette ,
theme : registerTheme ,
2014-11-03 17:32:25 +01:00
2015-02-04 16:08:32 +01:00
setDefaultTheme : function ( theme ) {
defaultTheme = theme ;
} ,
alwaysWatchTheme : function ( alwaysWatch ) {
alwaysWatchTheme = alwaysWatch ;
} ,
$get : ThemingService ,
_LIGHT _DEFAULT _HUES : LIGHT _DEFAULT _HUES ,
_DARK _DEFAULT _HUES : DARK _DEFAULT _HUES ,
_PALETTES : PALETTES ,
_THEMES : THEMES ,
_parseRules : parseRules ,
_rgba : rgba
} ;
2014-11-03 17:32:25 +01:00
2015-02-04 16:08:32 +01:00
// Example: $mdThemingProvider.definePalette('neonRed', { 50: '#f5fafa', ... });
function definePalette ( name , map ) {
map = map || { } ;
PALETTES [ name ] = checkPaletteValid ( name , map ) ;
return themingProvider ;
2014-09-30 12:29:53 +02:00
}
2015-02-04 16:08:32 +01:00
// Returns an new object which is a copy of a given palette `name` with variables from
// `map` overwritten
// Example: var neonRedMap = $mdThemingProvider.extendPalette('red', { 50: '#f5fafafa' });
function extendPalette ( name , map ) {
return checkPaletteValid ( name , angular . extend ( { } , PALETTES [ name ] || { } , map ) ) ;
}
// Make sure that palette has all required hues
function checkPaletteValid ( name , map ) {
var missingColors = VALID _HUE _VALUES . filter ( function ( field ) {
return ! map [ field ] ;
} ) ;
if ( missingColors . length ) {
throw new Error ( "Missing colors %1 in palette %2!"
. replace ( '%1' , missingColors . join ( ', ' ) )
. replace ( '%2' , name ) ) ;
2014-09-30 12:29:53 +02:00
}
2015-02-04 16:08:32 +01:00
return map ;
}
// Register a theme (which is a collection of color palettes to use with various states
// ie. warn, accent, primary )
// Optionally inherit from an existing theme
// $mdThemingProvider.theme('custom-theme').primaryPalette('red');
function registerTheme ( name , inheritFrom ) {
inheritFrom = inheritFrom || 'default' ;
if ( THEMES [ name ] ) return THEMES [ name ] ;
var parentTheme = typeof inheritFrom === 'string' ? THEMES [ inheritFrom ] : inheritFrom ;
var theme = new Theme ( name ) ;
if ( parentTheme ) {
angular . forEach ( parentTheme . colors , function ( color , colorType ) {
theme . colors [ colorType ] = {
name : color . name ,
// Make sure a COPY of the hues is given to the child color,
// not the same reference.
hues : angular . extend ( { } , color . hues )
} ;
} ) ;
2014-11-03 17:32:25 +01:00
}
2015-02-04 16:08:32 +01:00
THEMES [ name ] = theme ;
2014-11-03 17:32:25 +01:00
2015-02-04 16:08:32 +01:00
return theme ;
2014-11-03 17:32:25 +01:00
}
2015-02-04 16:08:32 +01:00
function Theme ( name ) {
var self = this ;
self . name = name ;
self . colors = { } ;
2014-09-30 12:29:53 +02:00
2015-02-04 16:08:32 +01:00
self . dark = setDark ;
setDark ( false ) ;
2014-11-03 17:32:25 +01:00
2015-02-04 16:08:32 +01:00
function setDark ( isDark ) {
isDark = arguments . length === 0 ? true : ! ! isDark ;
2014-11-03 17:32:25 +01:00
2015-02-04 16:08:32 +01:00
// If no change, abort
if ( isDark === self . isDark ) return ;
2014-11-03 17:32:25 +01:00
2015-02-04 16:08:32 +01:00
self . isDark = isDark ;
2014-11-03 17:32:25 +01:00
2015-02-04 16:08:32 +01:00
self . foregroundPalette = self . isDark ? LIGHT _FOREGROUND : DARK _FOREGROUND ;
self . foregroundShadow = self . isDark ? DARK _SHADOW : LIGHT _SHADOW ;
// Light and dark themes have different default hues.
// Go through each existing color type for this theme, and for every
// hue value that is still the default hue value from the previous light/dark setting,
// set it to the default hue value from the new light/dark setting.
var newDefaultHues = self . isDark ? DARK _DEFAULT _HUES : LIGHT _DEFAULT _HUES ;
var oldDefaultHues = self . isDark ? LIGHT _DEFAULT _HUES : DARK _DEFAULT _HUES ;
angular . forEach ( newDefaultHues , function ( newDefaults , colorType ) {
var color = self . colors [ colorType ] ;
var oldDefaults = oldDefaultHues [ colorType ] ;
if ( color ) {
for ( var hueName in color . hues ) {
if ( color . hues [ hueName ] === oldDefaults [ hueName ] ) {
color . hues [ hueName ] = newDefaults [ hueName ] ;
}
}
}
} ) ;
2014-11-03 17:32:25 +01:00
2015-02-04 16:08:32 +01:00
return self ;
}
2014-11-03 17:32:25 +01:00
2015-02-04 16:08:32 +01:00
THEME _COLOR _TYPES . forEach ( function ( colorType ) {
var defaultHues = ( self . isDark ? DARK _DEFAULT _HUES : LIGHT _DEFAULT _HUES ) [ colorType ] ;
self [ colorType + 'Palette' ] = function setPaletteType ( paletteName , hues ) {
var color = self . colors [ colorType ] = {
name : paletteName ,
hues : angular . extend ( { } , defaultHues , hues )
} ;
Object . keys ( color . hues ) . forEach ( function ( name ) {
if ( ! defaultHues [ name ] ) {
throw new Error ( "Invalid hue name '%1' in theme %2's %3 color %4. Available hue names: %4"
. replace ( '%1' , name )
. replace ( '%2' , self . name )
. replace ( '%3' , paletteName )
. replace ( '%4' , Object . keys ( defaultHues ) . join ( ', ' ) )
) ;
}
} ) ;
Object . keys ( color . hues ) . map ( function ( key ) {
return color . hues [ key ] ;
} ) . forEach ( function ( hueValue ) {
if ( VALID _HUE _VALUES . indexOf ( hueValue ) == - 1 ) {
throw new Error ( "Invalid hue value '%1' in theme %2's %3 color %4. Available hue values: %5"
. replace ( '%1' , hueValue )
. replace ( '%2' , self . name )
. replace ( '%3' , colorType )
. replace ( '%4' , paletteName )
. replace ( '%5' , VALID _HUE _VALUES . join ( ', ' ) )
) ;
}
} ) ;
return self ;
} ;
self [ colorType + 'Color' ] = function ( ) {
var args = Array . prototype . slice . call ( arguments ) ;
console . warn ( '$mdThemingProviderTheme.' + colorType + 'Color() has been depricated. ' +
'Use $mdThemingProviderTheme.' + colorType + 'Palette() instead.' ) ;
return self [ colorType + 'Palette' ] . apply ( self , args ) ;
} ;
2014-11-03 17:32:25 +01:00
} ) ;
}
2015-02-04 16:08:32 +01:00
/ * *
* @ ngdoc service
* @ name $mdTheming
*
* @ description
*
* Service that makes an element apply theming related classes to itself .
*
* ` ` ` js
* app . directive ( 'myFancyDirective' , function ( $mdTheming ) {
* return {
* restrict : 'e' ,
* link : function ( scope , el , attrs ) {
* $mdTheming ( el ) ;
* }
* } ;
* } ) ;
* ` ` `
* @ param { el = } element to apply theming to
* /
/* @ngInject */
function ThemingService ( $rootScope , $log ) {
applyTheme . inherit = function ( el , parent ) {
var ctrl = parent . controller ( 'mdTheme' ) ;
2014-11-03 17:32:25 +01:00
2015-02-04 16:08:32 +01:00
var attrThemeValue = el . attr ( 'md-theme-watch' ) ;
if ( ( alwaysWatchTheme || angular . isDefined ( attrThemeValue ) ) && attrThemeValue != 'false' ) {
var deregisterWatch = $rootScope . $watch ( function ( ) {
return ctrl && ctrl . $mdTheme || defaultTheme ;
} , changeTheme ) ;
el . on ( '$destroy' , deregisterWatch ) ;
} else {
var theme = ctrl && ctrl . $mdTheme || defaultTheme ;
changeTheme ( theme ) ;
}
function changeTheme ( theme ) {
if ( ! registered ( theme ) ) {
$log . warn ( 'Attempted to use unregistered theme \'' + theme + '\'. ' +
'Register it with $mdThemingProvider.theme().' ) ;
}
var oldTheme = el . data ( '$mdThemeName' ) ;
if ( oldTheme ) el . removeClass ( 'md-' + oldTheme + '-theme' ) ;
el . addClass ( 'md-' + theme + '-theme' ) ;
el . data ( '$mdThemeName' , theme ) ;
}
} ;
applyTheme . registered = registered ;
return applyTheme ;
function registered ( theme ) {
if ( theme === undefined || theme === '' ) return true ;
return THEMES [ theme ] !== undefined ;
2014-11-03 17:32:25 +01:00
}
2015-02-04 16:08:32 +01:00
function applyTheme ( scope , el ) {
// Allow us to be invoked via a linking function signature.
if ( el === undefined ) {
el = scope ;
scope = undefined ;
}
if ( scope === undefined ) {
scope = $rootScope ;
}
applyTheme . inherit ( el , el ) ;
}
2014-11-03 17:32:25 +01:00
}
}
2015-02-04 16:08:32 +01:00
ThemingProvider . $inject = [ "$mdColorPalette" ] ;
2014-11-03 17:32:25 +01:00
2015-02-04 16:08:32 +01:00
function ThemingDirective ( $mdTheming , $interpolate , $log ) {
return {
priority : 100 ,
link : {
pre : function ( scope , el , attrs ) {
var ctrl = {
$setTheme : function ( theme ) {
if ( ! $mdTheming . registered ( theme ) ) {
$log . warn ( 'attempted to use unregistered theme \'' + theme + '\'' ) ;
}
ctrl . $mdTheme = theme ;
}
} ;
el . data ( '$mdThemeController' , ctrl ) ;
ctrl . $setTheme ( $interpolate ( attrs . mdTheme ) ( scope ) ) ;
attrs . $observe ( 'mdTheme' , ctrl . $setTheme ) ;
}
}
} ;
}
ThemingDirective . $inject = [ "$mdTheming" , "$interpolate" , "$log" ] ;
2014-11-03 17:32:25 +01:00
2015-02-04 16:08:32 +01:00
function ThemableDirective ( $mdTheming ) {
return $mdTheming ;
}
ThemableDirective . $inject = [ "$mdTheming" ] ;
function parseRules ( theme , colorType , rules ) {
checkValidPalette ( theme , colorType ) ;
rules = rules . replace ( /THEME_NAME/g , theme . name ) ;
var generatedRules = [ ] ;
var color = theme . colors [ colorType ] ;
var themeNameRegex = new RegExp ( '.md-' + theme . name + '-theme' , 'g' ) ;
// Matches '{{ primary-color }}', etc
var hueRegex = new RegExp ( '(\'|")?{{\\s*(' + colorType + ')-(color|contrast)-?(\\d\\.?\\d*)?\\s*}}(\"|\')?' , 'g' ) ;
var simpleVariableRegex = /'?"?\{\{\s*([a-zA-Z]+)-(A?\d+|hue\-[0-3]|shadow)-?(\d\.?\d*)?\s*\}\}'?"?/g ;
var palette = PALETTES [ color . name ] ;
// find and replace simple variables where we use a specific hue, not angentire palette
// eg. "{{primary-100}}"
//\(' + THEME_COLOR_TYPES.join('\|') + '\)'
rules = rules . replace ( simpleVariableRegex , function ( match , colorType , hue , opacity ) {
if ( colorType === 'foreground' ) {
if ( hue == 'shadow' ) {
return theme . foregroundShadow ;
} else {
return theme . foregroundPalette [ hue ] || theme . foregroundPalette [ '1' ] ;
}
}
if ( hue . indexOf ( 'hue' ) === 0 ) {
hue = theme . colors [ colorType ] . hues [ hue ] ;
}
return rgba ( ( PALETTES [ theme . colors [ colorType ] . name ] [ hue ] || '' ) . value , opacity ) ;
} ) ;
// For each type, generate rules for each hue (ie. default, md-hue-1, md-hue-2, md-hue-3)
angular . forEach ( color . hues , function ( hueValue , hueName ) {
var newRule = rules
. replace ( hueRegex , function ( match , _ , colorType , hueType , opacity ) {
return rgba ( palette [ hueValue ] [ hueType === 'color' ? 'value' : 'contrast' ] , opacity ) ;
} ) ;
if ( hueName !== 'default' ) {
newRule = newRule . replace ( themeNameRegex , '.md-' + theme . name + '-theme.md-' + hueName ) ;
}
generatedRules . push ( newRule ) ;
} ) ;
return generatedRules . join ( '' ) ;
}
// Generate our themes at run time given the state of THEMES and PALETTES
function generateThemes ( $injector ) {
var themeCss = $injector . has ( '$MD_THEME_CSS' ) ? $injector . get ( '$MD_THEME_CSS' ) : '' ;
// MD_THEME_CSS is a string generated by the build process that includes all the themable
// components as templates
// Expose contrast colors for palettes to ensure that text is always readable
angular . forEach ( PALETTES , sanitizePalette ) ;
// Break the CSS into individual rules
var rules = themeCss . split ( /\}(?!(\}|'|"|;))/ )
. filter ( function ( rule ) { return rule && rule . length ; } )
. map ( function ( rule ) { return rule . trim ( ) + '}' ; } ) ;
var rulesByType = { } ;
THEME _COLOR _TYPES . forEach ( function ( type ) {
rulesByType [ type ] = '' ;
} ) ;
var ruleMatchRegex = new RegExp ( 'md-(' + THEME _COLOR _TYPES . join ( '|' ) + ')' , 'g' ) ;
// Sort the rules based on type, allowing us to do color substitution on a per-type basis
rules . forEach ( function ( rule ) {
var match = rule . match ( ruleMatchRegex ) ;
// First: test that if the rule has '.md-accent', it goes into the accent set of rules
for ( var i = 0 , type ; type = THEME _COLOR _TYPES [ i ] ; i ++ ) {
if ( rule . indexOf ( '.md-' + type ) > - 1 ) {
return rulesByType [ type ] += rule ;
}
}
// If no eg 'md-accent' class is found, try to just find 'accent' in the rule and guess from
// there
for ( i = 0 ; type = THEME _COLOR _TYPES [ i ] ; i ++ ) {
if ( rule . indexOf ( type ) > - 1 ) {
return rulesByType [ type ] += rule ;
}
}
// Default to the primary array
return rulesByType [ DEFAULT _COLOR _TYPE ] += rule ;
} ) ;
var styleString = '' ;
// For each theme, use the color palettes specified for `primary`, `warn` and `accent`
// to generate CSS rules.
angular . forEach ( THEMES , function ( theme ) {
THEME _COLOR _TYPES . forEach ( function ( colorType ) {
styleString += parseRules ( theme , colorType , rulesByType [ colorType ] + '' ) ;
} ) ;
if ( theme . colors . primary . name == theme . colors . accent . name ) {
console . warn ( "$mdThemingProvider: Using the same palette for primary and" +
"accent. This violates the material design spec." ) ;
}
} ) ;
// Insert our newly minted styles into the DOM
if ( ! generationIsDone ) {
var style = document . createElement ( 'style' ) ;
style . innerHTML = styleString ;
var head = document . getElementsByTagName ( 'head' ) [ 0 ] ;
head . insertBefore ( style , head . firstElementChild ) ;
generationIsDone = true ;
2014-11-03 17:32:25 +01:00
}
2015-02-04 16:08:32 +01:00
// The user specifies a 'default' contrast color as either light or dark,
// then explicitly lists which hues are the opposite contrast (eg. A100 has dark, A200 has light)
function sanitizePalette ( palette ) {
var defaultContrast = palette . contrastDefaultColor ;
var lightColors = palette . contrastLightColors || [ ] ;
var strongLightColors = palette . contrastStrongLightColors || [ ] ;
var darkColors = palette . contrastDarkColors || [ ] ;
// These colors are provided as space-separated lists
if ( typeof lightColors === 'string' ) lightColors = lightColors . split ( ' ' ) ;
if ( typeof strongLightColors === 'string' ) strongLightColors = strongLightColors . split ( ' ' ) ;
if ( typeof darkColors === 'string' ) darkColors = darkColors . split ( ' ' ) ;
// Cleanup after ourselves
delete palette . contrastDefaultColor ;
delete palette . contrastLightColors ;
delete palette . contrastStrongLightColors ;
delete palette . contrastDarkColors ;
// Change { 'A100': '#fffeee' } to { 'A100': { value: '#fffeee', contrast:DARK_CONTRAST_COLOR }
angular . forEach ( palette , function ( hueValue , hueName ) {
if ( angular . isObject ( hueValue ) ) return ; // Already converted
// Map everything to rgb colors
var rgbValue = colorToRgbaArray ( hueValue ) ;
if ( ! rgbValue ) {
throw new Error ( "Color %1, in palette %2's hue %3, is invalid. Hex or rgb(a) color expected."
. replace ( '%1' , hueValue )
. replace ( '%2' , palette . name )
. replace ( '%3' , hueName ) ) ;
}
2014-11-03 17:32:25 +01:00
2015-02-04 16:08:32 +01:00
palette [ hueName ] = {
value : rgbValue ,
contrast : getContrastColor ( )
} ;
function getContrastColor ( ) {
if ( defaultContrast === 'light' ) {
if ( darkColors . indexOf ( hueName ) > - 1 ) {
return DARK _CONTRAST _COLOR ;
} else {
return strongLightColors . indexOf ( hueName ) > - 1 ? STRONG _LIGHT _CONTRAST _COLOR
: LIGHT _CONTRAST _COLOR ;
}
} else {
if ( lightColors . indexOf ( hueName ) > - 1 ) {
return strongLightColors . indexOf ( hueName ) > - 1 ? STRONG _LIGHT _CONTRAST _COLOR
: LIGHT _CONTRAST _COLOR ;
} else {
return DARK _CONTRAST _COLOR ;
}
}
}
} ) ;
2014-11-03 17:32:25 +01:00
}
2015-02-04 16:08:32 +01:00
}
generateThemes . $inject = [ "$injector" ] ;
function checkValidPalette ( theme , colorType ) {
// If theme attempts to use a palette that doesnt exist, throw error
if ( ! PALETTES [ ( theme . colors [ colorType ] || { } ) . name ] ) {
throw new Error (
"You supplied an invalid color palette for theme %1's %2 palette. Available palettes: %3"
. replace ( '%1' , theme . name )
. replace ( '%2' , colorType )
. replace ( '%3' , Object . keys ( PALETTES ) . join ( ', ' ) )
) ;
}
}
function colorToRgbaArray ( clr ) {
if ( angular . isArray ( clr ) && clr . length == 3 ) return clr ;
if ( /^rgb/ . test ( clr ) ) {
return clr . replace ( /(^\s*rgba?\(|\)\s*$)/g , '' ) . split ( ',' ) . map ( function ( value , i ) {
return i == 3 ? parseFloat ( value , 10 ) : parseInt ( value , 10 ) ;
} ) ;
}
if ( clr . charAt ( 0 ) == '#' ) clr = clr . substring ( 1 ) ;
if ( ! /^([a-fA-F0-9]{3}){1,2}$/g . test ( clr ) ) return ;
var dig = clr . length / 3 ;
var red = clr . substr ( 0 , dig ) ;
var grn = clr . substr ( dig , dig ) ;
var blu = clr . substr ( dig * 2 ) ;
if ( dig === 1 ) {
red += red ;
grn += grn ;
blu += blu ;
}
return [ parseInt ( red , 16 ) , parseInt ( grn , 16 ) , parseInt ( blu , 16 ) ] ;
}
function rgba ( rgbArray , opacity ) {
if ( rgbArray . length == 4 ) {
rgbArray = angular . copy ( rgbArray ) ;
opacity ? rgbArray . pop ( ) : opacity = rgbArray . pop ( ) ;
}
return opacity && ( typeof opacity == 'number' || ( typeof opacity == 'string' && opacity . length ) ) ?
'rgba(' + rgbArray . join ( ',' ) + ',' + opacity + ')' :
'rgb(' + rgbArray . join ( ',' ) + ')' ;
}
2014-11-03 17:32:25 +01:00
} ) ( ) ;
2015-02-04 16:08:32 +01:00
/ * !
* Angular Material Design
* https : //github.com/angular/material
* @ license MIT
* v0 . 7.1
* /
2014-11-03 17:32:25 +01:00
( function ( ) {
2015-02-04 16:08:32 +01:00
'use strict' ;
2014-11-03 17:32:25 +01:00
2015-02-04 16:08:32 +01:00
/ *
2014-11-03 17:32:25 +01:00
* @ ngdoc module
2015-02-04 16:08:32 +01:00
* @ name material . components . backdrop
* @ description Backdrop
2014-11-03 17:32:25 +01:00
* /
/ * *
* @ ngdoc directive
2015-02-04 16:08:32 +01:00
* @ name mdBackdrop
* @ module material . components . backdrop
2014-11-03 17:32:25 +01:00
*
* @ restrict E
*
* @ description
2015-02-04 16:08:32 +01:00
* ` <md-backdrop> ` is a backdrop element used by other coponents , such as dialog and bottom sheet .
* Apply class ` opaque ` to make the backdrop use the theme backdrop color .
2014-11-03 17:32:25 +01:00
*
* /
2015-02-04 16:08:32 +01:00
angular . module ( 'material.components.backdrop' , [
'material.core'
] )
. directive ( 'mdBackdrop' , BackdropDirective ) ;
2014-11-03 17:32:25 +01:00
2015-02-04 16:08:32 +01:00
function BackdropDirective ( $mdTheming ) {
return $mdTheming ;
}
BackdropDirective . $inject = [ "$mdTheming" ] ;
} ) ( ) ;
2014-11-03 17:32:25 +01:00
2015-02-04 16:08:32 +01:00
/ * !
* Angular Material Design
* https : //github.com/angular/material
* @ license MIT
* v0 . 7.1
* /
( function ( ) {
'use strict' ;
2014-11-03 17:32:25 +01:00
2015-02-04 16:08:32 +01:00
/ * *
* @ ngdoc module
* @ name material . components . bottomSheet
* @ description
* BottomSheet
* /
angular . module ( 'material.components.bottomSheet' , [
'material.core' ,
'material.components.backdrop'
] )
. directive ( 'mdBottomSheet' , MdBottomSheetDirective )
. provider ( '$mdBottomSheet' , MdBottomSheetProvider ) ;
2014-11-03 17:32:25 +01:00
2015-02-04 16:08:32 +01:00
function MdBottomSheetDirective ( ) {
return {
restrict : 'E'
} ;
}
2014-11-03 17:32:25 +01:00
/ * *
2015-02-04 16:08:32 +01:00
* @ ngdoc service
* @ name $mdBottomSheet
* @ module material . components . bottomSheet
2014-11-03 17:32:25 +01:00
*
* @ description
2015-02-04 16:08:32 +01:00
* ` $ mdBottomSheet ` opens a bottom sheet over the app and provides a simple promise API .
2014-11-03 17:32:25 +01:00
*
2015-02-04 16:08:32 +01:00
* # # # Restrictions
2014-11-03 17:32:25 +01:00
*
2015-02-04 16:08:32 +01:00
* - The bottom sheet ' s template must have an outer ` <md-bottom-sheet> ` element .
* - Add the ` md-grid ` class to the bottom sheet for a grid layout .
* - Add the ` md-list ` class to the bottom sheet for a list layout .
2014-11-03 17:32:25 +01:00
*
* @ usage
* < hljs lang = "html" >
2015-02-04 16:08:32 +01:00
* < div ng - controller = "MyController" >
* < md - button ng - click = "openBottomSheet()" >
* Open a Bottom Sheet !
* < / m d - b u t t o n >
* < / d i v >
* < / h l j s >
* < hljs lang = "js" >
* var app = angular . module ( 'app' , [ 'ngMaterial' ] ) ;
* app . controller ( 'MyController' , function ( $scope , $mdBottomSheet ) {
* $scope . openBottomSheet = function ( ) {
* $mdBottomSheet . show ( {
* template : '<md-bottom-sheet>Hello!</md-bottom-sheet>'
* } ) ;
* } ;
* } ) ;
* < / h l j s >
* /
/ * *
* @ ngdoc method
* @ name $mdBottomSheet # show
2014-11-03 17:32:25 +01:00
*
2015-02-04 16:08:32 +01:00
* @ description
* Show a bottom sheet with the specified options .
2014-11-03 17:32:25 +01:00
*
2015-02-04 16:08:32 +01:00
* @ param { object } options An options object , with the following properties :
2014-11-03 17:32:25 +01:00
*
2015-02-04 16:08:32 +01:00
* - ` templateUrl ` - ` {string=} ` : 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 ` md-bottom-sheet ` element .
* - ` template ` - ` {string=} ` : Same as templateUrl , except this is an actual
* template string .
* - ` controller ` - ` {string=} ` : The controller to associate with this bottom sheet .
* - ` locals ` - ` {string=} ` : 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.
* - ` targetEvent ` - ` {DOMClickEvent=} ` : 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 .
* - ` resolve ` - ` {object=} ` : Similar to locals , except it takes promises as values
* and the bottom sheet will not open until the promises resolve .
* - ` controllerAs ` - ` {string=} ` : An alias to assign the controller to on the scope .
* - ` parent ` - ` {element=} ` : The element to append the bottom sheet to . Defaults to appending
* to the root element of the application .
* - ` disableParentScroll ` - ` {boolean=} ` : Whether to disable scrolling while the bottom sheet is open .
* Default true .
2014-09-30 12:29:53 +02:00
*
2015-02-04 16:08:32 +01:00
* @ returns { promise } A promise that can be resolved with ` $ mdBottomSheet.hide() ` or
* rejected with ` $ mdBottomSheet.cancel() ` .
2014-09-30 12:29:53 +02:00
* /
2015-02-04 16:08:32 +01:00
/ * *
* @ ngdoc method
* @ name $mdBottomSheet # hide
*
* @ description
* Hide the existing bottom sheet and resolve the promise returned from
* ` $ mdBottomSheet.show() ` .
*
* @ param { *= } response An argument for the resolved promise .
*
* /
2014-09-30 12:29:53 +02:00
2015-02-04 16:08:32 +01:00
/ * *
* @ ngdoc method
* @ name $mdBottomSheet # cancel
*
* @ description
* Hide the existing bottom sheet and reject the promise returned from
* ` $ mdBottomSheet.show() ` .
*
* @ param { *= } response An argument for the rejected promise .
*
* /
2014-09-30 12:29:53 +02:00
2015-02-04 16:08:32 +01:00
function MdBottomSheetProvider ( $$interimElementProvider ) {
// how fast we need to flick down to close the sheet, pixels/ms
var CLOSING _VELOCITY = 0.5 ;
var PADDING = 80 ; // same as css
2014-09-30 12:29:53 +02:00
2015-02-04 16:08:32 +01:00
bottomSheetDefaults . $inject = [ "$animate" , "$mdConstant" , "$timeout" , "$$rAF" , "$compile" , "$mdTheming" , "$mdBottomSheet" , "$rootElement" , "$rootScope" , "$mdGesture" ] ;
return $$interimElementProvider ( '$mdBottomSheet' )
. setDefaults ( {
methods : [ 'disableParentScroll' , 'escapeToClose' , 'targetEvent' ] ,
options : bottomSheetDefaults
} ) ;
2014-10-12 19:07:47 +02:00
2015-02-04 16:08:32 +01:00
/* @ngInject */
function bottomSheetDefaults ( $animate , $mdConstant , $timeout , $$rAF , $compile , $mdTheming , $mdBottomSheet , $rootElement , $rootScope , $mdGesture ) {
var backdrop ;
2014-09-30 12:29:53 +02:00
2015-02-04 16:08:32 +01:00
return {
themable : true ,
targetEvent : null ,
onShow : onShow ,
onRemove : onRemove ,
escapeToClose : true ,
disableParentScroll : true
} ;
function onShow ( scope , element , options ) {
// Add a backdrop that will close on click
backdrop = $compile ( '<md-backdrop class="md-opaque md-bottom-sheet-backdrop">' ) ( scope ) ;
backdrop . on ( 'click' , function ( ) {
$timeout ( $mdBottomSheet . cancel ) ;
2014-10-12 19:07:47 +02:00
} ) ;
2014-09-30 12:29:53 +02:00
2015-02-04 16:08:32 +01:00
$mdTheming . inherit ( backdrop , options . parent ) ;
2014-09-30 12:29:53 +02:00
2015-02-04 16:08:32 +01:00
$animate . enter ( backdrop , options . parent , null ) ;
2014-09-30 12:29:53 +02:00
2015-02-04 16:08:32 +01:00
var bottomSheet = new BottomSheet ( element , options . parent ) ;
options . bottomSheet = bottomSheet ;
// Give up focus on calling item
options . targetEvent && angular . element ( options . targetEvent . target ) . blur ( ) ;
$mdTheming . inherit ( bottomSheet . element , options . parent ) ;
if ( options . disableParentScroll ) {
options . lastOverflow = options . parent . css ( 'overflow' ) ;
options . parent . css ( 'overflow' , 'hidden' ) ;
2014-09-30 12:29:53 +02:00
}
2015-02-04 16:08:32 +01:00
return $animate . enter ( bottomSheet . element , options . parent )
. then ( function ( ) {
var focusable = angular . element (
element [ 0 ] . querySelector ( 'button' ) ||
element [ 0 ] . querySelector ( 'a' ) ||
element [ 0 ] . querySelector ( '[ng-click]' )
) ;
focusable . focus ( ) ;
if ( options . escapeToClose ) {
options . rootElementKeyupCallback = function ( e ) {
if ( e . keyCode === $mdConstant . KEY _CODE . ESCAPE ) {
$timeout ( $mdBottomSheet . cancel ) ;
}
} ;
$rootElement . on ( 'keyup' , options . rootElementKeyupCallback ) ;
}
} ) ;
}
function onRemove ( scope , element , options ) {
var bottomSheet = options . bottomSheet ;
$animate . leave ( backdrop ) ;
return $animate . leave ( bottomSheet . element ) . then ( function ( ) {
if ( options . disableParentScroll ) {
options . parent . css ( 'overflow' , options . lastOverflow ) ;
delete options . lastOverflow ;
}
bottomSheet . cleanup ( ) ;
// Restore focus
options . targetEvent && angular . element ( options . targetEvent . target ) . focus ( ) ;
} ) ;
2014-09-30 12:29:53 +02:00
}
2015-02-04 16:08:32 +01:00
2014-10-12 19:07:47 +02:00
/ * *
2015-02-04 16:08:32 +01:00
* BottomSheet class to apply bottom - sheet behavior to an element
2014-10-12 19:07:47 +02:00
* /
2015-02-04 16:08:32 +01:00
function BottomSheet ( element , parent ) {
var deregister = $mdGesture . register ( parent , 'drag' , { horizontal : false } ) ;
parent . on ( '$md.dragstart' , onDragStart )
. on ( '$md.drag' , onDrag )
. on ( '$md.dragend' , onDragEnd ) ;
2014-10-12 19:07:47 +02:00
2015-02-04 16:08:32 +01:00
return {
element : element ,
cleanup : function cleanup ( ) {
deregister ( ) ;
parent . off ( '$md.dragstart' , onDragStart )
. off ( '$md.drag' , onDrag )
. off ( '$md.dragend' , onDragEnd ) ;
}
} ;
2014-10-12 19:07:47 +02:00
2015-02-04 16:08:32 +01:00
function onDragStart ( ev ) {
// Disable transitions on transform so that it feels fast
element . css ( $mdConstant . CSS . TRANSITION _DURATION , '0ms' ) ;
}
2014-10-12 19:07:47 +02:00
2015-02-04 16:08:32 +01:00
function onDrag ( ev ) {
var transform = ev . pointer . distanceY ;
if ( transform < 5 ) {
// Slow down drag when trying to drag up, and stop after PADDING
transform = Math . max ( - PADDING , transform / 2 ) ;
}
element . css ( $mdConstant . CSS . TRANSFORM , 'translate3d(0,' + ( PADDING + transform ) + 'px,0)' ) ;
}
function onDragEnd ( ev ) {
if ( ev . pointer . distanceY > 0 &&
( ev . pointer . distanceY > 20 || Math . abs ( ev . pointer . velocityY ) > CLOSING _VELOCITY ) ) {
var distanceRemaining = element . prop ( 'offsetHeight' ) - ev . pointer . distanceY ;
var transitionDuration = Math . min ( distanceRemaining / ev . pointer . velocityY * 0.75 , 500 ) ;
element . css ( $mdConstant . CSS . TRANSITION _DURATION , transitionDuration + 'ms' ) ;
$timeout ( $mdBottomSheet . cancel ) ;
} else {
element . css ( $mdConstant . CSS . TRANSITION _DURATION , '' ) ;
element . css ( $mdConstant . CSS . TRANSFORM , '' ) ;
}
2014-10-12 19:07:47 +02:00
}
}
2015-02-04 16:08:32 +01:00
2014-09-30 12:29:53 +02:00
}
2015-02-04 16:08:32 +01:00
}
MdBottomSheetProvider . $inject = [ "$$interimElementProvider" ] ;
2014-09-30 12:29:53 +02:00
2014-11-03 17:32:25 +01:00
} ) ( ) ;
2014-09-30 12:29:53 +02:00
2015-02-04 16:08:32 +01:00
/ * !
* Angular Material Design
* https : //github.com/angular/material
* @ license MIT
* v0 . 7.1
* /
2014-11-03 17:32:25 +01:00
( function ( ) {
2015-02-04 16:08:32 +01:00
'use strict' ;
2014-09-30 12:29:53 +02:00
/ * *
* @ ngdoc module
2015-02-04 16:08:32 +01:00
* @ name material . components . button
2014-09-30 12:29:53 +02:00
* @ description
*
2015-02-04 16:08:32 +01:00
* Button
2014-09-30 12:29:53 +02:00
* /
2015-02-04 16:08:32 +01:00
angular . module ( 'material.components.button' , [
'material.core'
] )
. directive ( 'mdButton' , MdButtonDirective ) ;
2014-09-30 12:29:53 +02:00
/ * *
* @ ngdoc directive
2015-02-04 16:08:32 +01:00
* @ name mdButton
* @ module material . components . button
*
2014-09-30 12:29:53 +02:00
* @ restrict E
*
* @ description
2015-02-04 16:08:32 +01:00
* ` <md-button> ` is a button directive with optional ink ripples ( default enabled ) .
2014-09-30 12:29:53 +02:00
*
2015-02-04 16:08:32 +01:00
* If you supply a ` href ` or ` ng-href ` attribute , it will become an ` <a> ` element . Otherwise , it will
* become a ` <button> ` element .
2014-09-30 12:29:53 +02:00
*
2015-02-04 16:08:32 +01:00
* As per the [ material design spec ] ( http : //www.google.com/design/spec/style/color.html#color-ui-color-application)
* the FAB button is in the accent color by default . The primary color palette may be used with
* the ` md-primary ` class .
*
* @ param { boolean = } md - no - ink If present , disable ripple ink effects .
* @ param { expression = } ng - disabled En / Disable based on the expression
* @ param { string = } md - ripple - size Overrides the default ripple size logic . Options : ` full ` , ` partial ` , ` auto `
* @ param { string = } aria - label Adds alternative text to button for accessibility , useful for icon buttons .
* If no default text is found , a warning will be logged .
2014-09-30 12:29:53 +02:00
*
* @ usage
* < hljs lang = "html" >
2015-02-04 16:08:32 +01:00
* < md - button >
* Button
* < / m d - b u t t o n >
* < md - button href = "http://google.com" class = "md-button-colored" >
* I ' m a link
* < / m d - b u t t o n >
* < md - button ng - disabled = "true" class = "md-colored" >
* I ' m a disabled button
* < / m d - b u t t o n >
2014-09-30 12:29:53 +02:00
* < / h l j s >
* /
2015-02-04 16:08:32 +01:00
function MdButtonDirective ( $mdInkRipple , $mdTheming , $mdAria ) {
2014-09-30 12:29:53 +02:00
return {
restrict : 'E' ,
2015-02-04 16:08:32 +01:00
replace : true ,
transclude : true ,
template : getTemplate ,
link : postLink
2014-10-12 19:07:47 +02:00
} ;
2014-09-30 12:29:53 +02:00
2015-02-04 16:08:32 +01:00
function isAnchor ( attr ) {
return angular . isDefined ( attr . href ) || angular . isDefined ( attr . ngHref ) ;
}
function getTemplate ( element , attr ) {
return isAnchor ( attr ) ?
'<a class="md-button" ng-transclude></a>' :
'<button class="md-button" ng-transclude></button>' ;
}
2014-09-30 12:29:53 +02:00
2015-02-04 16:08:32 +01:00
function postLink ( scope , element , attr ) {
var node = element [ 0 ] ;
$mdTheming ( element ) ;
$mdInkRipple . attachButtonBehavior ( scope , element ) ;
2014-10-12 19:07:47 +02:00
2015-02-04 16:08:32 +01:00
var elementHasText = node . textContent . trim ( ) ;
if ( ! elementHasText ) {
$mdAria . expect ( element , 'aria-label' ) ;
2014-10-12 19:07:47 +02:00
}
2015-02-04 16:08:32 +01:00
// For anchor elements, we have to set tabindex manually when the
// element is disabled
if ( isAnchor ( attr ) && angular . isDefined ( attr . ngDisabled ) ) {
scope . $watch ( attr . ngDisabled , function ( isDisabled ) {
element . attr ( 'tabindex' , isDisabled ? - 1 : 0 ) ;
2014-10-12 19:07:47 +02:00
} ) ;
2014-09-30 12:29:53 +02:00
}
2014-10-12 19:07:47 +02:00
}
2014-09-30 12:29:53 +02:00
}
2015-02-04 16:08:32 +01:00
MdButtonDirective . $inject = [ "$mdInkRipple" , "$mdTheming" , "$mdAria" ] ;
2014-11-03 17:32:25 +01:00
} ) ( ) ;
2014-09-30 12:29:53 +02:00
2015-02-04 16:08:32 +01:00
/ * !
* Angular Material Design
* https : //github.com/angular/material
* @ license MIT
* v0 . 7.1
* /
2014-11-03 17:32:25 +01:00
( function ( ) {
2015-02-04 16:08:32 +01:00
'use strict' ;
2014-09-30 12:29:53 +02:00
/ * *
* @ ngdoc module
2015-02-04 16:08:32 +01:00
* @ name material . components . card
*
* @ description
* Card components .
2014-09-30 12:29:53 +02:00
* /
2015-02-04 16:08:32 +01:00
angular . module ( 'material.components.card' , [
'material.core'
2014-09-30 12:29:53 +02:00
] )
2015-02-04 16:08:32 +01:00
. directive ( 'mdCard' , mdCardDirective ) ;
2014-09-30 12:29:53 +02:00
/ * *
* @ ngdoc directive
2015-02-04 16:08:32 +01:00
* @ name mdCard
* @ module material . components . card
*
2014-09-30 12:29:53 +02:00
* @ restrict E
*
2015-02-04 16:08:32 +01:00
* @ description
* The ` <md-card> ` directive is a container element used within ` <md-content> ` containers .
2014-09-30 12:29:53 +02:00
*
2015-02-04 16:08:32 +01:00
* 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
2014-09-30 12:29:53 +02:00
*
* @ usage
* < hljs lang = "html" >
2015-02-04 16:08:32 +01:00
* < md - card >
* < img src = "img/washedout.png" class = "md-card-image" >
* < h2 > Paracosm < / h 2 >
* < 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 >
* < / m d - c a r d >
2014-09-30 12:29:53 +02:00
* < / h l j s >
*
* /
2015-02-04 16:08:32 +01:00
function mdCardDirective ( $mdTheming ) {
2014-09-30 12:29:53 +02:00
return {
2015-02-04 16:08:32 +01:00
restrict : 'E' ,
link : function ( $scope , $element , $attr ) {
$mdTheming ( $element ) ;
}
2014-09-30 12:29:53 +02:00
} ;
}
2015-02-04 16:08:32 +01:00
mdCardDirective . $inject = [ "$mdTheming" ] ;
} ) ( ) ;
2014-09-30 12:29:53 +02:00
2015-02-04 16:08:32 +01:00
/ * !
* Angular Material Design
* https : //github.com/angular/material
* @ license MIT
* v0 . 7.1
2014-09-30 12:29:53 +02:00
* /
2015-02-04 16:08:32 +01:00
( function ( ) {
'use strict' ;
2014-09-30 12:29:53 +02:00
2015-02-04 16:08:32 +01:00
/ * *
* @ ngdoc module
* @ name material . components . checkbox
* @ description Checkbox module !
* /
angular . module ( 'material.components.checkbox' , [
'material.core'
] )
. directive ( 'mdCheckbox' , MdCheckboxDirective ) ;
2014-09-30 12:29:53 +02:00
2015-02-04 16:08:32 +01:00
/ * *
* @ ngdoc directive
* @ name mdCheckbox
* @ 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).
*
* As per the [ material design spec ] ( http : //www.google.com/design/spec/style/color.html#color-ui-color-application)
* the checkbox is in the accent color by default . The primary color palette may be used with
* the ` md-primary ` class .
*
* @ param { string } ng - model Assignable angular expression to data - bind to .
* @ param { string = } name Property name of the form under which the control is published .
* @ param { expression = } ng - true - value The value to which the expression should be set when selected .
* @ param { expression = } ng - false - value The value to which the expression should be set when not selected .
* @ param { string = } ng - change Angular expression to be executed when input changes due to user interaction with the input element .
* @ param { boolean = } md - no - ink Use of attribute indicates use of ripple ink effects
* @ param { string = } aria - label Adds label to checkbox for accessibility .
* Defaults to checkbox ' s text . If no default text is found , a warning will be logged .
*
* @ usage
* < hljs lang = "html" >
* < md - checkbox ng - model = "isChecked" aria - label = "Finished?" >
* Finished ?
* < / m d - c h e c k b o x >
*
* < md - checkbox md - no - ink ng - model = "hasInk" aria - label = "No Ink Effects" >
* No Ink Effects
* < / m d - c h e c k b o x >
*
* < md - checkbox ng - disabled = "true" ng - model = "isDisabled" aria - label = "Disabled" >
* Disabled
* < / m d - c h e c k b o x >
*
* < / h l j s >
*
* /
function MdCheckboxDirective ( inputDirective , $mdInkRipple , $mdAria , $mdConstant , $mdTheming , $mdUtil ) {
inputDirective = inputDirective [ 0 ] ;
var CHECKED _CSS = 'md-checked' ;
2014-09-30 12:29:53 +02:00
2015-02-04 16:08:32 +01:00
return {
restrict : 'E' ,
transclude : true ,
require : '?ngModel' ,
template :
'<div class="md-container" md-ink-ripple md-ink-ripple-checkbox>' +
'<div class="md-icon"></div>' +
'</div>' +
'<div ng-transclude class="md-label"></div>' ,
compile : compile
} ;
2014-11-06 16:55:16 +01:00
2015-02-04 16:08:32 +01:00
// **********************************************************
// Private Methods
// **********************************************************
2014-09-30 12:29:53 +02:00
2015-02-04 16:08:32 +01:00
function compile ( tElement , tAttrs ) {
2014-09-30 12:29:53 +02:00
2015-02-04 16:08:32 +01:00
tAttrs . type = 'checkbox' ;
tAttrs . tabIndex = 0 ;
tElement . attr ( 'role' , tAttrs . type ) ;
2014-11-03 17:32:25 +01:00
2015-02-04 16:08:32 +01:00
return function postLink ( scope , element , attr , ngModelCtrl ) {
ngModelCtrl = ngModelCtrl || $mdUtil . fakeNgModel ( ) ;
var checked = false ;
$mdTheming ( element ) ;
2014-09-30 12:29:53 +02:00
2015-02-04 16:08:32 +01:00
$mdAria . expectWithText ( element , 'aria-label' ) ;
2014-09-30 12:29:53 +02:00
2015-02-04 16:08:32 +01:00
// 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 ] ) ;
2014-09-30 12:29:53 +02:00
2015-02-04 16:08:32 +01:00
element . on ( 'click' , listener )
. on ( 'keypress' , keypressHandler ) ;
ngModelCtrl . $render = render ;
2014-09-30 12:29:53 +02:00
2015-02-04 16:08:32 +01:00
function keypressHandler ( ev ) {
if ( ev . which === $mdConstant . KEY _CODE . SPACE ) {
ev . preventDefault ( ) ;
listener ( ev ) ;
}
}
function listener ( ev ) {
if ( element [ 0 ] . hasAttribute ( 'disabled' ) ) return ;
2014-09-30 12:29:53 +02:00
2015-02-04 16:08:32 +01:00
scope . $apply ( function ( ) {
checked = ! checked ;
ngModelCtrl . $setViewValue ( checked , ev && ev . type ) ;
ngModelCtrl . $render ( ) ;
} ) ;
2014-09-30 12:29:53 +02:00
}
2015-02-04 16:08:32 +01:00
function render ( ) {
checked = ngModelCtrl . $viewValue ;
if ( checked ) {
element . addClass ( CHECKED _CSS ) ;
} else {
element . removeClass ( CHECKED _CSS ) ;
}
2014-09-30 12:29:53 +02:00
}
2015-02-04 16:08:32 +01:00
} ;
}
}
MdCheckboxDirective . $inject = [ "inputDirective" , "$mdInkRipple" , "$mdAria" , "$mdConstant" , "$mdTheming" , "$mdUtil" ] ;
2014-09-30 12:29:53 +02:00
2015-02-04 16:08:32 +01:00
} ) ( ) ;
2014-09-30 12:29:53 +02:00
2015-02-04 16:08:32 +01:00
/ * !
* Angular Material Design
* https : //github.com/angular/material
* @ license MIT
* v0 . 7.1
* /
( function ( ) {
'use strict' ;
2014-09-30 12:29:53 +02:00
2015-02-04 16:08:32 +01:00
/ * *
* @ ngdoc module
* @ name material . components . content
*
* @ description
* Scrollable content
* /
angular . module ( 'material.components.content' , [
'material.core'
] )
. directive ( 'mdContent' , mdContentDirective ) ;
2014-11-03 17:32:25 +01:00
2015-02-04 16:08:32 +01:00
/ * *
* @ ngdoc directive
* @ name mdContent
* @ module material . components . content
*
* @ restrict E
*
* @ description
* The ` <md-content> ` directive is a container element useful for scrollable content
*
* # # # Restrictions
*
* - Add the ` md-padding ` class to make the content padded .
*
* @ usage
* < hljs lang = "html" >
* < md - content class = "md-padding" >
* Lorem ipsum dolor sit amet , ne quod novum mei .
* < / m d - c o n t e n t >
* < / h l j s >
*
* /
2014-09-30 12:29:53 +02:00
2015-02-04 16:08:32 +01:00
function mdContentDirective ( $mdTheming ) {
return {
restrict : 'E' ,
controller : [ '$scope' , '$element' , ContentController ] ,
link : function ( scope , element , attr ) {
var node = element [ 0 ] ;
2014-11-06 16:55:16 +01:00
2015-02-04 16:08:32 +01:00
$mdTheming ( element ) ;
scope . $broadcast ( '$mdContentLoaded' , element ) ;
2014-11-06 16:55:16 +01:00
2015-02-04 16:08:32 +01:00
iosScrollFix ( element [ 0 ] ) ;
2014-09-30 12:29:53 +02:00
}
2015-02-04 16:08:32 +01:00
} ;
2014-09-30 12:29:53 +02:00
2015-02-04 16:08:32 +01:00
function ContentController ( $scope , $element ) {
this . $scope = $scope ;
this . $element = $element ;
}
}
mdContentDirective . $inject = [ "$mdTheming" ] ;
function iosScrollFix ( node ) {
// IOS FIX:
// If we scroll where there is no more room for the webview to scroll,
// by default the webview itself will scroll up and down, this looks really
// bad. So if we are scrolling to the very top or bottom, add/subtract one
angular . element ( node ) . on ( '$md.pressdown' , function ( ev ) {
// Only touch events
if ( ev . pointer . type !== 't' ) return ;
// Don't let a child content's touchstart ruin it for us.
if ( ev . $materialScrollFixed ) return ;
ev . $materialScrollFixed = true ;
if ( node . scrollTop === 0 ) {
node . scrollTop = 1 ;
} else if ( node . scrollHeight === node . scrollTop + node . offsetHeight ) {
node . scrollTop -= 1 ;
2014-09-30 12:29:53 +02:00
}
2015-02-04 16:08:32 +01:00
} ) ;
}
} ) ( ) ;
2014-09-30 12:29:53 +02:00
2015-02-04 16:08:32 +01:00
/ * !
* Angular Material Design
* https : //github.com/angular/material
* @ license MIT
* v0 . 7.1
* /
( function ( ) {
'use strict' ;
/ * *
* @ ngdoc module
* @ name material . components . dialog
* /
angular . module ( 'material.components.dialog' , [
'material.core' ,
'material.components.backdrop'
] )
. directive ( 'mdDialog' , MdDialogDirective )
. provider ( '$mdDialog' , MdDialogProvider ) ;
function MdDialogDirective ( $$rAF , $mdTheming ) {
return {
restrict : 'E' ,
link : function ( scope , element , attr ) {
$mdTheming ( element ) ;
$$rAF ( function ( ) {
var content = element [ 0 ] . querySelector ( 'md-content' ) ;
if ( content && content . scrollHeight > content . clientHeight ) {
element . addClass ( 'md-content-overflow' ) ;
}
} ) ;
2014-09-30 12:29:53 +02:00
}
2015-02-04 16:08:32 +01:00
} ;
}
MdDialogDirective . $inject = [ "$$rAF" , "$mdTheming" ] ;
/ * *
* @ ngdoc service
* @ name $mdDialog
* @ module material . components . dialog
*
* @ description
* ` $ mdDialog ` opens a dialog over the app and provides a simple promise API .
*
* # # # Restrictions
*
* - The dialog is always given an isolate scope .
* - The dialog ' s template must have an outer ` <md-dialog> ` element .
* Inside , use an ` <md-content> ` element for the dialog ' s content , and use
* an element with class ` md-actions ` for the dialog ' s actions .
*
* @ usage
* # # # # HTML
*
* < hljs lang = "html" >
* < div ng - app = "demoApp" ng - controller = "EmployeeController" >
* < md - button ng - click = "showAlert()" class = "md-raised md-warn" >
* Employee Alert !
* < / m d - b u t t o n >
* < md - button ng - click = "closeAlert()" ng - disabled = "!hasAlert()" class = "md-raised" >
* Close Alert
* < / m d - b u t t o n >
* < md - button ng - click = "showGreeting($event)" class = "md-raised md-primary" >
* Greet Employee
* < / m d - b u t t o n >
* < / d i v >
* < / h l j s >
*
* # # # # JavaScript
*
* < hljs lang = "js" >
* ( function ( angular , undefined ) {
* "use strict" ;
*
* angular
* . module ( 'demoApp' , [ 'ngMaterial' ] )
* . controller ( 'EmployeeController' , EmployeeEditor )
* . controller ( 'GreetingController' , GreetingController ) ;
*
* // Fictitious Employee Editor to show how to use simple and complex dialogs.
*
* function EmployeeEditor ( $scope , $mdDialog ) {
* var alert ;
*
* $scope . showAlert = showAlert ;
* $scope . closeAlert = closeAlert ;
* $scope . showGreeting = showCustomGreeting ;
*
* $scope . hasAlert = function ( ) { return ! ! alert } ;
* $scope . userName = $scope . userName || 'Bobby' ;
*
* // Dialog #1 - Show simple alert dialog and cache
* // reference to dialog instance
*
* function showAlert ( ) {
* alert = $mdDialog . alert ( )
* . title ( 'Attention, ' + $scope . userName )
* . content ( 'This is an example of how easy dialogs can be!' )
* . ok ( 'Close' ) ;
*
* $mdDialog
* . show ( alert )
* . finally ( function ( ) {
* alert = undefined ;
* } ) ;
* }
*
* // Close the specified dialog instance and resolve with 'finished' flag
* // Normally this is not needed, just use '$mdDialog.hide()' to close
* // the most recent dialog popup.
*
* function closeAlert ( ) {
* $mdDialog . hide ( alert , "finished" ) ;
* alert = undefined ;
* }
*
* // Dialog #2 - Demonstrate more complex dialogs construction and popup.
*
* function showCustomGreeting ( $event ) {
* $mdDialog . show ( {
* targetEvent : $event ,
* template :
* '<md-dialog>' +
*
* ' <md-content>Hello {{ employee }}!</md-content>' +
*
* ' <div class="md-actions">' +
* ' <md-button ng-click="closeDialog()">' +
* ' Close Greeting' +
*
* ' </md-button>' +
* ' </div>' +
* '</md-dialog>' ,
* controller : 'GreetingController' ,
* onComplete : afterShowAnimation ,
* locals : { employee : $scope . userName }
* } ) ;
*
* // When the 'enter' animation finishes...
*
* function afterShowAnimation ( scope , element , options ) {
* // post-show code here: DOM element focus, etc.
* }
* }
* }
*
* // Greeting controller used with the more complex 'showCustomGreeting()' custom dialog
*
* function GreetingController ( $scope , $mdDialog , employee ) {
* // Assigned from construction <code>locals</code> options...
* $scope . employee = employee ;
*
* $scope . closeDialog = function ( ) {
* // Easily hides most recent dialog shown...
* // no specific instance reference is needed.
* $mdDialog . hide ( ) ;
* } ;
* }
*
* } ) ( angular ) ;
* < / h l j s >
* /
2014-09-30 12:29:53 +02:00
2015-02-04 16:08:32 +01:00
/ * *
* @ ngdoc method
* @ name $mdDialog # alert
*
* @ description
* Builds a preconfigured dialog with the specified message .
*
* @ returns { obj } an ` $ mdDialogPreset ` with the chainable configuration methods :
*
* - $mdDialogPreset # title ( string ) - sets title to string
* - $mdDialogPreset # content ( string ) - sets content / message to string
* - $mdDialogPreset # ok ( string ) - sets okay button text to string
*
* /
2014-09-30 12:29:53 +02:00
2015-02-04 16:08:32 +01:00
/ * *
* @ ngdoc method
* @ name $mdDialog # confirm
*
* @ description
* Builds a preconfigured dialog with the specified message . You can call show and the promise returned
* will be resolved only if the user clicks the confirm action on the dialog .
*
* @ returns { obj } an ` $ mdDialogPreset ` with the chainable configuration methods :
*
* Additionally , it supports the following methods :
*
* - $mdDialogPreset # title ( string ) - sets title to string
* - $mdDialogPreset # content ( string ) - sets content / message to string
* - $mdDialogPreset # ok ( string ) - sets okay button text to string
* - $mdDialogPreset # cancel ( string ) - sets cancel button text to string
*
* /
/ * *
* @ ngdoc method
* @ name $mdDialog # show
*
* @ description
* Show a dialog with the specified options .
*
* @ param { object } optionsOrPreset Either provide an ` $ mdDialogPreset ` returned from ` alert() ` , and
* ` confirm() ` , or an options object with the following properties :
* - ` templateUrl ` - ` {string=} ` : The url of a template that will be used as the content
* of the dialog .
* - ` template ` - ` {string=} ` : Same as templateUrl , except this is an actual template string .
* - ` targetEvent ` - ` {DOMClickEvent=} ` : 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 .
* - ` disableParentScroll ` - ` {boolean=} ` : Whether to disable scrolling while the dialog is open .
* Default true .
* - ` hasBackdrop ` - ` {boolean=} ` : Whether there should be an opaque backdrop behind the dialog .
* Default true .
* - ` clickOutsideToClose ` - ` {boolean=} ` : Whether the user can click outside the dialog to
* close it . Default true .
* - ` escapeToClose ` - ` {boolean=} ` : Whether the user can press escape to close the dialog .
* Default true .
* - ` controller ` - ` {string=} ` : The controller to associate with the dialog . The controller
* will be injected with the local ` $ hideDialog ` , which is a function used to hide the dialog .
* - ` locals ` - ` {object=} ` : 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. If ` bindToController ` is true , they will be
* copied to the controller instead .
* - ` bindToController ` - ` bool ` : bind the locals to the controller , instead of passing them in
* - ` resolve ` - ` {object=} ` : Similar to locals , except it takes promises as values , and the
* dialog will not open until all of the promises resolve .
* - ` controllerAs ` - ` {string=} ` : An alias to assign the controller to on the scope .
* - ` parent ` - ` {element=} ` : The element to append the dialog to . Defaults to appending
* to the root element of the application .
* - ` onComplete ` ` {function=} ` : Callback function used to announce when the show ( ) action is
* finished .
*
* @ returns { promise } A promise that can be resolved with ` $ mdDialog.hide() ` or
* rejected with ` mdDialog.cancel() ` .
* /
/ * *
* @ ngdoc method
* @ name $mdDialog # hide
*
* @ description
* Hide an existing dialog and resolve the promise returned from ` $ mdDialog.show() ` .
*
* @ param { *= } response An argument for the resolved promise .
* /
/ * *
* @ ngdoc method
* @ name $mdDialog # cancel
*
* @ description
* Hide an existing dialog and reject the promise returned from ` $ mdDialog.show() ` .
*
* @ param { *= } response An argument for the rejected promise .
* /
function MdDialogProvider ( $$interimElementProvider ) {
var alertDialogMethods = [ 'title' , 'content' , 'ariaLabel' , 'ok' ] ;
advancedDialogOptions . $inject = [ "$mdDialog" ] ;
dialogDefaultOptions . $inject = [ "$timeout" , "$rootElement" , "$compile" , "$animate" , "$mdAria" , "$document" , "$mdUtil" , "$mdConstant" , "$mdTheming" , "$$rAF" , "$q" , "$mdDialog" ] ;
return $$interimElementProvider ( '$mdDialog' )
. setDefaults ( {
methods : [ 'disableParentScroll' , 'hasBackdrop' , 'clickOutsideToClose' , 'escapeToClose' , 'targetEvent' ] ,
options : dialogDefaultOptions
} )
. addPreset ( 'alert' , {
methods : [ 'title' , 'content' , 'ariaLabel' , 'ok' ] ,
options : advancedDialogOptions
} )
. addPreset ( 'confirm' , {
methods : [ 'title' , 'content' , 'ariaLabel' , 'ok' , 'cancel' ] ,
options : advancedDialogOptions
} ) ;
2014-10-12 19:07:47 +02:00
2015-02-04 16:08:32 +01:00
/* @ngInject */
function advancedDialogOptions ( $mdDialog ) {
return {
template : [
'<md-dialog aria-label="{{ dialog.ariaLabel }}">' ,
'<md-content>' ,
'<h2>{{ dialog.title }}</h2>' ,
'<p>{{ dialog.content }}</p>' ,
'</md-content>' ,
'<div class="md-actions">' ,
'<md-button ng-if="dialog.$type == \'confirm\'" ng-click="dialog.abort()">' ,
'{{ dialog.cancel }}' ,
'</md-button>' ,
'<md-button ng-click="dialog.hide()" class="md-primary">' ,
'{{ dialog.ok }}' ,
'</md-button>' ,
'</div>' ,
'</md-dialog>'
] . join ( '' ) ,
controller : function mdDialogCtrl ( ) {
this . hide = function ( ) {
$mdDialog . hide ( true ) ;
} ;
this . abort = function ( ) {
$mdDialog . cancel ( ) ;
} ;
} ,
controllerAs : 'dialog' ,
bindToController : true
} ;
}
2014-09-30 12:29:53 +02:00
2015-02-04 16:08:32 +01:00
/* @ngInject */
function dialogDefaultOptions ( $timeout , $rootElement , $compile , $animate , $mdAria , $document ,
$mdUtil , $mdConstant , $mdTheming , $$rAF , $q , $mdDialog ) {
return {
hasBackdrop : true ,
isolateScope : true ,
onShow : onShow ,
onRemove : onRemove ,
clickOutsideToClose : true ,
escapeToClose : true ,
targetEvent : null ,
disableParentScroll : true ,
transformTemplate : function ( template ) {
return '<div class="md-dialog-container">' + template + '</div>' ;
}
} ;
2014-10-12 19:07:47 +02:00
2015-02-04 16:08:32 +01:00
// On show method for dialogs
function onShow ( scope , element , options ) {
// Incase the user provides a raw dom element, always wrap it in jqLite
options . parent = angular . element ( options . parent ) ;
2014-10-12 19:07:47 +02:00
2015-02-04 16:08:32 +01:00
options . popInTarget = angular . element ( ( options . targetEvent || { } ) . target ) ;
var closeButton = findCloseButton ( ) ;
2014-09-30 12:29:53 +02:00
2015-02-04 16:08:32 +01:00
configureAria ( element . find ( 'md-dialog' ) ) ;
2014-10-12 19:07:47 +02:00
2015-02-04 16:08:32 +01:00
if ( options . hasBackdrop ) {
var parentOffset = options . parent . prop ( 'scrollTop' ) ;
options . backdrop = angular . element ( '<md-backdrop class="md-dialog-backdrop md-opaque">' ) ;
$mdTheming . inherit ( options . backdrop , options . parent ) ;
$animate . enter ( options . backdrop , options . parent ) ;
element . css ( 'top' , parentOffset + 'px' ) ;
}
2014-10-12 19:07:47 +02:00
2015-02-04 16:08:32 +01:00
if ( options . disableParentScroll ) {
options . lastOverflow = options . parent . css ( 'overflow' ) ;
options . parent . css ( 'overflow' , 'hidden' ) ;
2014-09-30 12:29:53 +02:00
}
2014-10-12 19:07:47 +02:00
2015-02-04 16:08:32 +01:00
return dialogPopIn (
element ,
options . parent ,
options . popInTarget && options . popInTarget . length && options . popInTarget
)
. then ( function ( ) {
if ( options . escapeToClose ) {
options . rootElementKeyupCallback = function ( e ) {
if ( e . keyCode === $mdConstant . KEY _CODE . ESCAPE ) {
$timeout ( $mdDialog . cancel ) ;
}
} ;
$rootElement . on ( 'keyup' , options . rootElementKeyupCallback ) ;
}
2014-10-12 19:07:47 +02:00
2015-02-04 16:08:32 +01:00
if ( options . clickOutsideToClose ) {
options . dialogClickOutsideCallback = function ( ev ) {
// Only close if we click the flex container outside the backdrop
if ( ev . target === element [ 0 ] ) {
$timeout ( $mdDialog . cancel ) ;
}
} ;
element . on ( 'click' , options . dialogClickOutsideCallback ) ;
}
closeButton . focus ( ) ;
} ) ;
2014-10-12 19:07:47 +02:00
2015-02-04 16:08:32 +01:00
function findCloseButton ( ) {
//If no element with class dialog-close, try to find the last
//button child in md-actions and assume it is a close button
var closeButton = element [ 0 ] . querySelector ( '.dialog-close' ) ;
if ( ! closeButton ) {
var actionButtons = element [ 0 ] . querySelectorAll ( '.md-actions button' ) ;
closeButton = actionButtons [ actionButtons . length - 1 ] ;
}
return angular . element ( closeButton ) ;
}
2014-10-12 19:07:47 +02:00
2015-02-04 16:08:32 +01:00
}
2014-10-12 19:07:47 +02:00
2015-02-04 16:08:32 +01:00
// On remove function for all dialogs
function onRemove ( scope , element , options ) {
2014-10-12 19:07:47 +02:00
2015-02-04 16:08:32 +01:00
if ( options . backdrop ) {
$animate . leave ( options . backdrop ) ;
}
if ( options . disableParentScroll ) {
options . parent . css ( 'overflow' , options . lastOverflow ) ;
delete options . lastOverflow ;
}
if ( options . escapeToClose ) {
$rootElement . off ( 'keyup' , options . rootElementKeyupCallback ) ;
}
if ( options . clickOutsideToClose ) {
element . off ( 'click' , options . dialogClickOutsideCallback ) ;
2014-10-12 19:07:47 +02:00
}
2015-02-04 16:08:32 +01:00
return dialogPopOut (
element ,
options . parent ,
options . popInTarget && options . popInTarget . length && options . popInTarget
) . then ( function ( ) {
options . scope . $destroy ( ) ;
element . remove ( ) ;
options . popInTarget && options . popInTarget . focus ( ) ;
} ) ;
2014-09-30 12:29:53 +02:00
}
/ * *
2015-02-04 16:08:32 +01:00
* Inject ARIA - specific attributes appropriate for Dialogs
2014-09-30 12:29:53 +02:00
* /
2015-02-04 16:08:32 +01:00
function configureAria ( element ) {
element . attr ( {
'role' : 'dialog'
} ) ;
2014-09-30 12:29:53 +02:00
2015-02-04 16:08:32 +01:00
var dialogContent = element . find ( 'md-content' ) ;
if ( dialogContent . length === 0 ) {
dialogContent = element ;
}
$mdAria . expectAsync ( element , 'aria-label' , function ( ) {
var words = dialogContent . text ( ) . split ( /\s+/ ) ;
if ( words . length > 3 ) words = words . slice ( 0 , 3 ) . concat ( '...' ) ;
return words . join ( ' ' ) ;
2014-10-12 19:07:47 +02:00
} ) ;
}
2015-02-04 16:08:32 +01:00
function dialogPopIn ( container , parentElement , clickElement ) {
var dialogEl = container . find ( 'md-dialog' ) ;
parentElement . append ( container ) ;
transformToClickElement ( dialogEl , clickElement ) ;
$$rAF ( function ( ) {
dialogEl . addClass ( 'transition-in' )
. css ( $mdConstant . CSS . TRANSFORM , '' ) ;
} ) ;
return dialogTransitionEnd ( dialogEl ) ;
2014-10-12 19:07:47 +02:00
}
2015-02-04 16:08:32 +01:00
function dialogPopOut ( container , parentElement , clickElement ) {
var dialogEl = container . find ( 'md-dialog' ) ;
dialogEl . addClass ( 'transition-out' ) . removeClass ( 'transition-in' ) ;
transformToClickElement ( dialogEl , clickElement ) ;
return dialogTransitionEnd ( dialogEl ) ;
2014-10-12 19:07:47 +02:00
}
2015-02-04 16:08:32 +01:00
function transformToClickElement ( dialogEl , clickElement ) {
if ( clickElement ) {
var clickRect = clickElement [ 0 ] . getBoundingClientRect ( ) ;
var dialogRect = dialogEl [ 0 ] . getBoundingClientRect ( ) ;
var scaleX = Math . min ( 0.5 , clickRect . width / dialogRect . width ) ;
var scaleY = Math . min ( 0.5 , clickRect . height / dialogRect . height ) ;
dialogEl . css ( $mdConstant . CSS . TRANSFORM , 'translate3d(' +
( - dialogRect . left + clickRect . left + clickRect . width / 2 - dialogRect . width / 2 ) + 'px,' +
( - dialogRect . top + clickRect . top + clickRect . height / 2 - dialogRect . height / 2 ) + 'px,' +
'0) scale(' + scaleX + ',' + scaleY + ')'
) ;
}
2014-10-12 19:07:47 +02:00
}
2015-02-04 16:08:32 +01:00
function dialogTransitionEnd ( dialogEl ) {
var deferred = $q . defer ( ) ;
dialogEl . on ( $mdConstant . CSS . TRANSITIONEND , finished ) ;
function finished ( ev ) {
//Make sure this transitionend didn't bubble up from a child
if ( ev . target === dialogEl [ 0 ] ) {
dialogEl . off ( $mdConstant . CSS . TRANSITIONEND , finished ) ;
deferred . resolve ( ) ;
}
}
return deferred . promise ;
2014-09-30 12:29:53 +02:00
}
2015-02-04 16:08:32 +01:00
}
2014-09-30 12:29:53 +02:00
}
2015-02-04 16:08:32 +01:00
MdDialogProvider . $inject = [ "$$interimElementProvider" ] ;
2014-11-03 17:32:25 +01:00
} ) ( ) ;
2014-09-30 12:29:53 +02:00
2015-02-04 16:08:32 +01:00
/ * !
* Angular Material Design
* https : //github.com/angular/material
* @ license MIT
* v0 . 7.1
2014-09-30 12:29:53 +02:00
* /
2015-02-04 16:08:32 +01:00
( function ( ) {
'use strict' ;
2014-09-30 12:29:53 +02:00
2015-02-04 16:08:32 +01:00
/ * *
* @ ngdoc module
* @ name material . components . divider
* @ description Divider module !
* /
angular . module ( 'material.components.divider' , [
'material.core'
2014-10-12 19:07:47 +02:00
] )
2015-02-04 16:08:32 +01:00
. directive ( 'mdDivider' , MdDividerDirective ) ;
2014-10-12 19:07:47 +02:00
2015-02-04 16:08:32 +01:00
function MdDividerController ( ) { }
/ * *
* @ ngdoc directive
* @ name mdDivider
* @ module material . components . divider
* @ restrict E
2014-10-12 19:07:47 +02:00
*
* @ description
2015-02-04 16:08:32 +01:00
* 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 = } md - inset Add this attribute to activate the inset divider style .
* @ usage
* < hljs lang = "html" >
* < md - divider > < / m d - d i v i d e r >
*
* < md - divider md - inset > < / m d - d i v i d e r >
* < / h l j s >
2014-10-12 19:07:47 +02:00
*
* /
2015-02-04 16:08:32 +01:00
function MdDividerDirective ( $mdTheming ) {
return {
restrict : 'E' ,
link : $mdTheming ,
controller : [ MdDividerController ]
} ;
}
MdDividerDirective . $inject = [ "$mdTheming" ] ;
} ) ( ) ;
2014-10-12 19:07:47 +02:00
2015-02-04 16:08:32 +01:00
/ * !
* Angular Material Design
* https : //github.com/angular/material
* @ license MIT
* v0 . 7.1
* /
( function ( ) {
'use strict' ;
2014-10-12 19:07:47 +02:00
2015-02-04 16:08:32 +01:00
/ *
* @ ngdoc module
* @ name material . components . icon
* @ description
* Icon
* /
angular . module ( 'material.components.icon' , [
'material.core'
] )
. directive ( 'mdIcon' , mdIconDirective ) ;
2014-10-12 19:07:47 +02:00
2015-02-04 16:08:32 +01:00
/ *
* @ ngdoc directive
* @ name mdIcon
* @ module material . components . icon
*
* @ restrict E
*
* @ description
* The ` <md-icon> ` directive is an element useful for SVG icons
*
* @ usage
* < hljs lang = "html" >
* < md - icon icon = "/img/icons/ic_access_time_24px.svg" >
* < / m d - i c o n >
* < / h l j s >
*
* /
function mdIconDirective ( ) {
return {
restrict : 'E' ,
template : '<object class="md-icon"></object>' ,
compile : function ( element , attr ) {
var object = angular . element ( element [ 0 ] . children [ 0 ] ) ;
if ( angular . isDefined ( attr . icon ) ) {
object . attr ( 'data' , attr . icon ) ;
2014-11-03 17:32:25 +01:00
}
}
} ;
2015-02-04 16:08:32 +01:00
}
} ) ( ) ;
2014-09-30 12:29:53 +02:00
2015-02-04 16:08:32 +01:00
/ * !
* Angular Material Design
* https : //github.com/angular/material
* @ license MIT
* v0 . 7.1
* /
( function ( ) {
2014-09-30 12:29:53 +02:00
2015-02-04 16:08:32 +01:00
/ * *
* @ ngdoc module
* @ name material . components . input
* /
2014-09-30 12:29:53 +02:00
2015-02-04 16:08:32 +01:00
angular . module ( 'material.components.input' , [
'material.core'
] )
. directive ( 'mdInputContainer' , mdInputContainerDirective )
. directive ( 'label' , labelDirective )
. directive ( 'input' , inputTextareaDirective )
. directive ( 'textarea' , inputTextareaDirective )
. directive ( 'mdMaxlength' , mdMaxlengthDirective )
. directive ( 'placeholder' , placeholderDirective ) ;
2014-09-30 12:29:53 +02:00
2015-02-04 16:08:32 +01:00
/ * *
* @ ngdoc directive
* @ name mdInputContainer
* @ module material . components . input
*
* @ restrict E
*
* @ description
* ` <md-input-container> ` is the parent of any input or textarea element .
*
* Input and textarea elements will not behave properly unless the md - input - container
* parent is provided .
*
* @ param md - is - error { expression = } When the given expression evaluates to true , the input container will go into error state . Defaults to erroring if the input has been touched and is invalid .
*
* @ usage
* < hljs lang = "html" >
*
* < md - input - container >
* < label > Username < / l a b e l >
* < input type = "text" ng - model = "user.name" >
* < / m d - i n p u t - c o n t a i n e r >
*
* < md - input - container >
* < label > Description < / l a b e l >
* < textarea ng - model = "user.description" > < / t e x t a r e a >
* < / m d - i n p u t - c o n t a i n e r >
*
* < / h l j s >
* /
function mdInputContainerDirective ( $mdTheming , $parse ) {
ContainerCtrl . $inject = [ "$scope" , "$element" , "$attrs" ] ;
return {
restrict : 'E' ,
link : postLink ,
controller : ContainerCtrl
} ;
2014-09-30 12:29:53 +02:00
2015-02-04 16:08:32 +01:00
function postLink ( scope , element , attr ) {
$mdTheming ( element ) ;
}
function ContainerCtrl ( $scope , $element , $attrs ) {
var self = this ;
2014-09-30 12:29:53 +02:00
2015-02-04 16:08:32 +01:00
self . isErrorGetter = $attrs . mdIsError && $parse ( $attrs . mdIsError ) ;
2014-09-30 12:29:53 +02:00
2015-02-04 16:08:32 +01:00
self . element = $element ;
self . setFocused = function ( isFocused ) {
$element . toggleClass ( 'md-input-focused' , ! ! isFocused ) ;
} ;
self . setHasValue = function ( hasValue ) {
$element . toggleClass ( 'md-input-has-value' , ! ! hasValue ) ;
} ;
self . setInvalid = function ( isInvalid ) {
$element . toggleClass ( 'md-input-invalid' , ! ! isInvalid ) ;
} ;
$scope . $watch ( function ( ) {
return self . label && self . input ;
} , function ( hasLabelAndInput ) {
if ( hasLabelAndInput && ! self . label . attr ( 'for' ) ) {
self . label . attr ( 'for' , self . input . attr ( 'id' ) ) ;
}
} ) ;
}
}
mdInputContainerDirective . $inject = [ "$mdTheming" , "$parse" ] ;
2014-09-30 12:29:53 +02:00
2015-02-04 16:08:32 +01:00
function labelDirective ( ) {
return {
restrict : 'E' ,
require : '^?mdInputContainer' ,
link : function ( scope , element , attr , containerCtrl ) {
if ( ! containerCtrl ) return ;
2014-09-30 12:29:53 +02:00
2015-02-04 16:08:32 +01:00
containerCtrl . label = element ;
scope . $on ( '$destroy' , function ( ) {
containerCtrl . label = null ;
} ) ;
2014-09-30 12:29:53 +02:00
}
2015-02-04 16:08:32 +01:00
} ;
}
2014-09-30 12:29:53 +02:00
2015-02-04 16:08:32 +01:00
/ * *
* @ ngdoc directive
* @ name input
* @ restrict E
* @ module material . components . input
*
* @ description
* Must be placed as a child of an ` <md-input-container> ` .
*
* Behaves like the [ AngularJS input directive ] ( https : //docs.angularjs.org/api/ng/directive/input).
*
* @ usage
* < hljs lang = "html" >
* < md - input - container >
* < label > Color < / l a b e l >
* < input type = "text" ng - model = "color" required md - maxlength = "10" >
* < / m d - i n p u t - c o n t a i n e r >
* < / h l j s >
* < h3 > With Errors ( uses [ ngMessages ] ( https : //docs.angularjs.org/api/ngMessages))</h3>
* < hljs lang = "html" >
* < form name = "userForm" >
* < md - input - container >
* < label > Last Name < / l a b e l >
* < input name = "lastName" ng - model = "lastName" required md - maxlength = "10" minlength = "4" >
* < div ng - messages = "userForm.lastName.$error" ng - show = "userForm.bio.$dirty" >
* < div ng - message = "required" > This is required ! < / d i v >
* < div ng - message = "md-maxlength" > That ' s too long ! < / d i v >
* < div ng - message = "minlength" > That ' s too short ! < / d i v >
* < / d i v >
* < / m d - i n p u t - c o n t a i n e r >
* < / f o r m >
* < / h l j s >
*
* @ param { number = } md - maxlength The maximum number of characters allowed in this input . If this is specified , a character counter will be shown underneath the input .
* /
/ * *
* @ ngdoc directive
* @ name textarea
* @ restrict E
* @ module material . components . input
*
* @ description
* Must be placed as a child of an ` <md-input-container> ` .
*
* Behaves like the [ AngularJS input directive ] ( https : //docs.angularjs.org/api/ng/directive/textarea).
*
* @ usage
* < hljs lang = "html" >
* < md - input - container >
* < label > Description < / l a b e l >
* < textarea ng - model = "description" required minlength = "15" md - maxlength = "20" > < / t e x t a r e a >
* < / m d - i n p u t - c o n t a i n e r >
* < / h l j s >
* < h3 > With Errors ( uses [ ngMessages ] ( https : //docs.angularjs.org/api/ngMessages))</h3>
* < hljs lang = "html" >
* < form name = "userForm" >
* < md - input - container >
* < label > Biography < / l a b e l >
* < textarea name = "bio" ng - model = "biography" required md - maxlength = "150" > < / t e x t a r e a >
* < div ng - messages = "userForm.bio.$error" ng - show = "userForm.bio.$dirty" >
* < div ng - message = "required" > This is required ! < / d i v >
* < div ng - message = "md-maxlength" > That ' s too long ! < / d i v >
* < / d i v >
* < / m d - i n p u t - c o n t a i n e r >
* < / f o r m >
* < / h l j s >
*
* @ param { number = } md - maxlength The maximum number of characters allowed in this input . If this is specified , a character counter will be shown underneath the input .
* /
function inputTextareaDirective ( $mdUtil , $window , $compile , $animate ) {
return {
restrict : 'E' ,
require : [ '^?mdInputContainer' , '?ngModel' ] ,
link : postLink
} ;
function postLink ( scope , element , attr , ctrls ) {
2014-09-30 12:29:53 +02:00
2015-02-04 16:08:32 +01:00
var containerCtrl = ctrls [ 0 ] ;
var ngModelCtrl = ctrls [ 1 ] || $mdUtil . fakeNgModel ( ) ;
var isReadonly = angular . isDefined ( attr . readonly ) ;
2014-09-30 12:29:53 +02:00
2015-02-04 16:08:32 +01:00
if ( ! containerCtrl ) return ;
if ( containerCtrl . input ) {
throw new Error ( "<md-input-container> can only have *one* <input> or <textarea> child element!" ) ;
}
containerCtrl . input = element ;
2014-09-30 12:29:53 +02:00
2015-02-04 16:08:32 +01:00
element . addClass ( 'md-input' ) ;
if ( ! element . attr ( 'id' ) ) {
element . attr ( 'id' , 'input_' + $mdUtil . nextUid ( ) ) ;
2014-09-30 12:29:53 +02:00
}
2015-02-04 16:08:32 +01:00
if ( element [ 0 ] . tagName . toLowerCase ( ) === 'textarea' ) {
setupTextarea ( ) ;
}
2014-09-30 12:29:53 +02:00
2015-02-04 16:08:32 +01:00
function ngModelPipelineCheckValue ( arg ) {
containerCtrl . setHasValue ( ! ngModelCtrl . $isEmpty ( arg ) ) ;
return arg ;
}
function inputCheckValue ( ) {
// An input's value counts if its length > 0,
// or if the input's validity state says it has bad input (eg string in a number input)
containerCtrl . setHasValue ( element . val ( ) . length > 0 || ( element [ 0 ] . validity || { } ) . badInput ) ;
}
2014-10-12 19:07:47 +02:00
2014-09-30 12:29:53 +02:00
2015-02-04 16:08:32 +01:00
var isErrorGetter = containerCtrl . isErrorGetter || function ( ) {
return ngModelCtrl . $invalid && ngModelCtrl . $touched ;
} ;
scope . $watch ( isErrorGetter , containerCtrl . setInvalid ) ;
ngModelCtrl . $parsers . push ( ngModelPipelineCheckValue ) ;
ngModelCtrl . $formatters . push ( ngModelPipelineCheckValue ) ;
2014-10-12 19:07:47 +02:00
2015-02-04 16:08:32 +01:00
element . on ( 'input' , inputCheckValue ) ;
2014-09-30 12:29:53 +02:00
2015-02-04 16:08:32 +01:00
if ( ! isReadonly ) {
element
. on ( 'focus' , function ( ev ) {
containerCtrl . setFocused ( true ) ;
} )
. on ( 'blur' , function ( ev ) {
containerCtrl . setFocused ( false ) ;
inputCheckValue ( ) ;
} ) ;
2014-11-03 17:32:25 +01:00
}
2014-09-30 12:29:53 +02:00
2015-02-04 16:08:32 +01:00
scope . $on ( '$destroy' , function ( ) {
containerCtrl . setFocused ( false ) ;
containerCtrl . setHasValue ( false ) ;
containerCtrl . input = null ;
} ) ;
2014-09-30 12:29:53 +02:00
2015-02-04 16:08:32 +01:00
function setupTextarea ( ) {
var node = element [ 0 ] ;
var onChangeTextarea = $mdUtil . debounce ( growTextarea , 1 ) ;
2014-09-30 12:29:53 +02:00
2015-02-04 16:08:32 +01:00
function pipelineListener ( value ) {
onChangeTextarea ( ) ;
return value ;
}
2014-09-30 12:29:53 +02:00
2015-02-04 16:08:32 +01:00
if ( ngModelCtrl ) {
ngModelCtrl . $formatters . push ( pipelineListener ) ;
ngModelCtrl . $viewChangeListeners . push ( pipelineListener ) ;
} else {
onChangeTextarea ( ) ;
}
element . on ( 'keydown input' , onChangeTextarea ) ;
element . on ( 'scroll' , onScroll ) ;
angular . element ( $window ) . on ( 'resize' , onChangeTextarea ) ;
2014-09-30 12:29:53 +02:00
2015-02-04 16:08:32 +01:00
scope . $on ( '$destroy' , function ( ) {
angular . element ( $window ) . off ( 'resize' , onChangeTextarea ) ;
} ) ;
2014-09-30 12:29:53 +02:00
2015-02-04 16:08:32 +01:00
function growTextarea ( ) {
node . style . height = "auto" ;
var line = node . scrollHeight - node . offsetHeight ;
node . scrollTop = 0 ;
var height = node . offsetHeight + ( line > 0 ? line : 0 ) ;
node . style . height = height + 'px' ;
}
function onScroll ( e ) {
node . scrollTop = 0 ;
// for smooth new line adding
var line = node . scrollHeight - node . offsetHeight ;
var height = node . offsetHeight + line ;
node . style . height = height + 'px' ;
2014-10-12 19:07:47 +02:00
}
}
2014-11-03 17:32:25 +01:00
}
2015-02-04 16:08:32 +01:00
}
inputTextareaDirective . $inject = [ "$mdUtil" , "$window" , "$compile" , "$animate" ] ;
2014-09-30 12:29:53 +02:00
2015-02-04 16:08:32 +01:00
function mdMaxlengthDirective ( $animate ) {
return {
restrict : 'A' ,
require : [ 'ngModel' , '^mdInputContainer' ] ,
link : postLink
} ;
function postLink ( scope , element , attr , ctrls ) {
var maxlength ;
var ngModelCtrl = ctrls [ 0 ] ;
var containerCtrl = ctrls [ 1 ] ;
var charCountEl = angular . element ( '<div class="md-char-counter">' ) ;
// Stop model from trimming. This makes it so whitespace
// over the maxlength still counts as invalid.
attr . $set ( 'ngTrim' , 'false' ) ;
containerCtrl . element . append ( charCountEl ) ;
ngModelCtrl . $formatters . push ( renderCharCount ) ;
ngModelCtrl . $viewChangeListeners . push ( renderCharCount ) ;
element . on ( 'input keydown' , function ( ) {
renderCharCount ( ) ; //make sure it's called with no args
2014-11-03 17:32:25 +01:00
} ) ;
2014-09-30 12:29:53 +02:00
2015-02-04 16:08:32 +01:00
scope . $watch ( attr . mdMaxlength , function ( value ) {
maxlength = value ;
if ( angular . isNumber ( value ) && value > 0 ) {
if ( ! charCountEl . parent ( ) . length ) {
$animate . enter ( charCountEl , containerCtrl . element ,
angular . element ( containerCtrl . element [ 0 ] . lastElementChild ) ) ;
}
renderCharCount ( ) ;
2014-11-03 17:32:25 +01:00
} else {
2015-02-04 16:08:32 +01:00
$animate . leave ( charCountEl ) ;
}
} ) ;
ngModelCtrl . $validators [ 'md-maxlength' ] = function ( modelValue , viewValue ) {
if ( ! angular . isNumber ( maxlength ) || maxlength < 0 ) {
return true ;
2014-09-30 12:29:53 +02:00
}
2015-02-04 16:08:32 +01:00
return ( modelValue || element . val ( ) || viewValue || '' ) . length <= maxlength ;
} ;
function renderCharCount ( value ) {
charCountEl . text ( ( element . val ( ) || value || '' ) . length + '/' + maxlength ) ;
return value ;
2014-10-12 19:07:47 +02:00
}
2014-11-03 17:32:25 +01:00
}
2015-02-04 16:08:32 +01:00
}
mdMaxlengthDirective . $inject = [ "$animate" ] ;
function placeholderDirective ( ) {
return {
restrict : 'A' ,
require : '^^?mdInputContainer' ,
link : postLink
} ;
function postLink ( scope , element , attr , inputContainer ) {
if ( ! inputContainer ) return ;
var placeholderText = attr . placeholder ;
element . removeAttr ( 'placeholder' ) ;
2014-09-30 12:29:53 +02:00
2015-02-04 16:08:32 +01:00
inputContainer . element . append ( '<div class="md-placeholder">' + placeholderText + '</div>' ) ;
}
2014-11-03 17:32:25 +01:00
}
2015-02-04 16:08:32 +01:00
2014-11-03 17:32:25 +01:00
} ) ( ) ;
2014-10-12 19:07:47 +02:00
2015-02-04 16:08:32 +01:00
/ * !
* Angular Material Design
* https : //github.com/angular/material
* @ license MIT
* v0 . 7.1
* /
2014-11-03 17:32:25 +01:00
( function ( ) {
2015-02-04 16:08:32 +01:00
'use strict' ;
2014-11-03 17:32:25 +01:00
/ * *
* @ ngdoc module
2015-02-04 16:08:32 +01:00
* @ name material . components . list
2014-11-03 17:32:25 +01:00
* @ description
2015-02-04 16:08:32 +01:00
* List module
2014-11-03 17:32:25 +01:00
* /
2015-02-04 16:08:32 +01:00
angular . module ( 'material.components.list' , [
'material.core'
2014-11-03 17:32:25 +01:00
] )
2015-02-04 16:08:32 +01:00
. directive ( 'mdList' , mdListDirective )
. directive ( 'mdItem' , mdItemDirective ) ;
2014-10-12 19:07:47 +02:00
2014-11-03 17:32:25 +01:00
/ * *
* @ ngdoc directive
2015-02-04 16:08:32 +01:00
* @ name mdList
* @ module material . components . list
2014-11-03 17:32:25 +01:00
*
* @ restrict E
*
* @ description
2015-02-04 16:08:32 +01:00
* The ` <md-list> ` directive is a list container for 1. . n ` <md-item> ` tags .
2014-11-03 17:32:25 +01:00
*
* @ usage
* < hljs lang = "html" >
2015-02-04 16:08:32 +01:00
* < md - list >
* < md - item ng - repeat = "item in todos" >
* < md - item - content >
* < div class = "md-tile-left" >
* < img ng - src = "{{item.face}}" class = "face" alt = "{{item.who}}" >
* < / d i v >
* < div class = "md-tile-content" >
* < h3 > { { item . what } } < / h 3 >
* < h4 > { { item . who } } < / h 4 >
* < p >
* { { item . notes } }
* < / p >
* < / d i v >
* < / m d - i t e m - c o n t e n t >
* < / m d - i t e m >
* < / m d - l i s t >
2014-11-03 17:32:25 +01:00
* < / h l j s >
2015-02-04 16:08:32 +01:00
*
2014-11-03 17:32:25 +01:00
* /
2015-02-04 16:08:32 +01:00
function mdListDirective ( ) {
2014-11-03 17:32:25 +01:00
return {
restrict : 'E' ,
2015-02-04 16:08:32 +01:00
link : function ( $scope , $element , $attr ) {
$element . attr ( {
'role' : 'list'
} ) ;
2014-11-03 17:32:25 +01:00
}
} ;
}
2014-09-30 12:29:53 +02:00
2015-02-04 16:08:32 +01:00
/ * *
* @ ngdoc directive
* @ name mdItem
* @ module material . components . list
*
* @ restrict E
*
* @ description
* The ` <md-item> ` directive is a container intended for row items in a ` <md-list> ` container .
*
* @ usage
* < hljs lang = "html" >
* < md - list >
* < md - item >
* Item content in list
* < / m d - i t e m >
* < / m d - l i s t >
* < / h l j s >
*
* /
function mdItemDirective ( ) {
return {
restrict : 'E' ,
link : function ( $scope , $element , $attr ) {
$element . attr ( {
'role' : 'listitem'
} ) ;
2014-11-03 17:32:25 +01:00
}
2015-02-04 16:08:32 +01:00
} ;
}
2014-11-03 17:32:25 +01:00
} ) ( ) ;
2014-09-30 12:29:53 +02:00
2015-02-04 16:08:32 +01:00
/ * !
* Angular Material Design
* https : //github.com/angular/material
* @ license MIT
* v0 . 7.1
* /
2014-11-03 17:32:25 +01:00
( function ( ) {
2015-02-04 16:08:32 +01:00
'use strict' ;
2014-11-03 17:32:25 +01:00
/ * *
* @ ngdoc module
2015-02-04 16:08:32 +01:00
* @ name material . components . progressCircular
* @ description Circular Progress module !
2014-11-03 17:32:25 +01:00
* /
2015-02-04 16:08:32 +01:00
angular . module ( 'material.components.progressCircular' , [
'material.core'
2014-11-03 17:32:25 +01:00
] )
2015-02-04 16:08:32 +01:00
. directive ( 'mdProgressCircular' , MdProgressCircularDirective ) ;
2014-09-30 12:29:53 +02:00
2014-11-03 17:32:25 +01:00
/ * *
* @ ngdoc directive
2015-02-04 16:08:32 +01:00
* @ name mdProgressCircular
* @ module material . components . progressCircular
2014-11-03 17:32:25 +01:00
* @ restrict E
*
2015-02-04 16:08:32 +01:00
* @ 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 .
2014-11-03 17:32:25 +01:00
*
2015-02-04 16:08:32 +01:00
* 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 } md - 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 = } md - diameter This specifies the diamter of the circular progress . Default : 48
2014-11-03 17:32:25 +01:00
*
* @ usage
* < hljs lang = "html" >
2015-02-04 16:08:32 +01:00
* < md - progress - circular md - mode = "determinate" value = "..." > < / m d - p r o g r e s s - c i r c u l a r >
2014-11-03 17:32:25 +01:00
*
2015-02-04 16:08:32 +01:00
* < md - progress - circular md - mode = "determinate" ng - value = "..." > < / m d - p r o g r e s s - c i r c u l a r >
2014-11-03 17:32:25 +01:00
*
2015-02-04 16:08:32 +01:00
* < md - progress - circular md - mode = "determinate" value = "..." md - diameter = "100" > < / m d - p r o g r e s s - c i r c u l a r >
2014-11-03 17:32:25 +01:00
*
2015-02-04 16:08:32 +01:00
* < md - progress - circular md - mode = "indeterminate" > < / m d - p r o g r e s s - c i r c u l a r >
2014-11-03 17:32:25 +01:00
* < / h l j s >
* /
2015-02-04 16:08:32 +01:00
function MdProgressCircularDirective ( $$rAF , $mdConstant , $mdTheming ) {
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)' ;
}
2014-10-12 19:07:47 +02:00
2014-11-03 17:32:25 +01:00
return {
restrict : 'E' ,
template :
2015-02-04 16:08:32 +01:00
'<div class="md-spinner-wrapper">' +
'<div class="md-inner">' +
'<div class="md-gap"></div>' +
'<div class="md-left">' +
'<div class="md-half-circle"></div>' +
'</div>' +
'<div class="md-right">' +
'<div class="md-half-circle"></div>' +
'</div>' +
'</div>' +
'</div>' ,
2014-11-03 17:32:25 +01:00
compile : compile
} ;
2014-10-12 19:07:47 +02:00
2015-02-04 16:08:32 +01:00
function compile ( tElement , tAttrs , transclude ) {
tElement . attr ( 'aria-valuemin' , 0 ) ;
tElement . attr ( 'aria-valuemax' , 100 ) ;
tElement . attr ( 'role' , 'progressbar' ) ;
2014-10-12 19:07:47 +02:00
2015-02-04 16:08:32 +01:00
return postLink ;
}
2014-09-30 12:29:53 +02:00
2015-02-04 16:08:32 +01:00
function postLink ( scope , element , attr ) {
$mdTheming ( element ) ;
var circle = element [ 0 ] ,
fill = circle . querySelectorAll ( '.md-fill, .md-mask.md-full' ) ,
fix = circle . querySelectorAll ( '.md-fill.md-fix' ) ,
i , clamped , fillRotation , fixRotation ;
var diameter = attr . mdDiameter || 48 ;
var scale = diameter / 48 ;
circle . style [ $mdConstant . CSS . 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 [ $mdConstant . CSS . TRANSFORM ] = fillRotation ;
}
for ( i = 0 ; i < fix . length ; i ++ ) {
fix [ i ] . style [ $mdConstant . CSS . TRANSFORM ] = fixRotation ;
}
} ) ;
}
function clamp ( value ) {
if ( value > 100 ) {
return 100 ;
}
if ( value < 0 ) {
return 0 ;
}
return Math . ceil ( value || 0 ) ;
2014-09-30 12:29:53 +02:00
}
2014-11-03 17:32:25 +01:00
}
2015-02-04 16:08:32 +01:00
MdProgressCircularDirective . $inject = [ "$$rAF" , "$mdConstant" , "$mdTheming" ] ;
2014-11-03 17:32:25 +01:00
} ) ( ) ;
2014-09-30 12:29:53 +02:00
2015-02-04 16:08:32 +01:00
/ * !
* Angular Material Design
* https : //github.com/angular/material
* @ license MIT
* v0 . 7.1
2014-11-03 17:32:25 +01:00
* /
( function ( ) {
2015-02-04 16:08:32 +01:00
'use strict' ;
2014-11-03 17:32:25 +01:00
/ * *
* @ ngdoc module
2015-02-04 16:08:32 +01:00
* @ name material . components . progressLinear
* @ description Linear Progress module !
2014-11-03 17:32:25 +01:00
* /
2015-02-04 16:08:32 +01:00
angular . module ( 'material.components.progressLinear' , [
'material.core'
] )
. directive ( 'mdProgressLinear' , MdProgressLinearDirective ) ;
2014-09-30 12:29:53 +02:00
/ * *
* @ ngdoc directive
2015-02-04 16:08:32 +01:00
* @ name mdProgressLinear
* @ module material . components . progressLinear
2014-09-30 12:29:53 +02:00
* @ restrict E
*
* @ description
2015-02-04 16:08:32 +01:00
* 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 .
2014-09-30 12:29:53 +02:00
*
2015-02-04 16:08:32 +01:00
* @ param { string } md - 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 = } md - buffer - value In the buffer mode , this number represents the precentage of the secondary progress bar . Default : 0
2014-09-30 12:29:53 +02:00
*
2014-11-03 17:32:25 +01:00
* @ usage
* < hljs lang = "html" >
2015-02-04 16:08:32 +01:00
* < md - progress - linear md - mode = "determinate" value = "..." > < / m d - p r o g r e s s - l i n e a r >
2014-09-30 12:29:53 +02:00
*
2015-02-04 16:08:32 +01:00
* < md - progress - linear md - mode = "determinate" ng - value = "..." > < / m d - p r o g r e s s - l i n e a r >
2014-09-30 12:29:53 +02:00
*
2015-02-04 16:08:32 +01:00
* < md - progress - linear md - mode = "indeterminate" > < / m d - p r o g r e s s - l i n e a r >
*
* < md - progress - linear md - mode = "buffer" value = "..." md - buffer - value = "..." > < / m d - p r o g r e s s - l i n e a r >
*
* < md - progress - linear md - mode = "query" > < / m d - p r o g r e s s - l i n e a r >
2014-11-03 17:32:25 +01:00
* < / h l j s >
* /
2015-02-04 16:08:32 +01:00
function MdProgressLinearDirective ( $$rAF , $mdConstant , $mdTheming ) {
2014-11-03 17:32:25 +01:00
return {
restrict : 'E' ,
2015-02-04 16:08:32 +01:00
template : '<div class="md-container">' +
'<div class="md-dashed"></div>' +
'<div class="md-bar md-bar1"></div>' +
'<div class="md-bar md-bar2"></div>' +
'</div>' ,
compile : compile
} ;
function compile ( tElement , tAttrs , transclude ) {
tElement . attr ( 'aria-valuemin' , 0 ) ;
tElement . attr ( 'aria-valuemax' , 100 ) ;
tElement . attr ( 'role' , 'progressbar' ) ;
2014-11-06 16:55:16 +01:00
2015-02-04 16:08:32 +01:00
return postLink ;
}
function postLink ( scope , element , attr ) {
$mdTheming ( element ) ;
var bar1Style = element [ 0 ] . querySelector ( '.md-bar1' ) . style ,
bar2Style = element [ 0 ] . querySelector ( '.md-bar2' ) . style ,
container = angular . element ( element [ 0 ] . querySelector ( '.md-container' ) ) ;
2014-11-06 16:55:16 +01:00
2015-02-04 16:08:32 +01:00
attr . $observe ( 'value' , function ( value ) {
if ( attr . mdMode == 'query' ) {
return ;
}
2014-11-03 17:32:25 +01:00
2015-02-04 16:08:32 +01:00
var clamped = clamp ( value ) ;
element . attr ( 'aria-valuenow' , clamped ) ;
bar2Style [ $mdConstant . CSS . TRANSFORM ] = transforms [ clamped ] ;
} ) ;
2014-09-30 12:29:53 +02:00
2015-02-04 16:08:32 +01:00
attr . $observe ( 'mdBufferValue' , function ( value ) {
bar1Style [ $mdConstant . CSS . TRANSFORM ] = transforms [ clamp ( value ) ] ;
} ) ;
2014-09-30 12:29:53 +02:00
2015-02-04 16:08:32 +01:00
$$rAF ( function ( ) {
container . addClass ( 'md-ready' ) ;
} ) ;
}
2014-11-06 16:55:16 +01:00
2015-02-04 16:08:32 +01:00
function clamp ( value ) {
if ( value > 100 ) {
return 100 ;
}
2014-11-06 16:55:16 +01:00
2015-02-04 16:08:32 +01:00
if ( value < 0 ) {
return 0 ;
2014-11-03 17:32:25 +01:00
}
2015-02-04 16:08:32 +01:00
return Math . ceil ( value || 0 ) ;
}
2014-11-03 17:32:25 +01:00
}
2015-02-04 16:08:32 +01:00
MdProgressLinearDirective . $inject = [ "$$rAF" , "$mdConstant" , "$mdTheming" ] ;
2014-09-30 12:29:53 +02:00
2015-02-04 16:08:32 +01:00
// **********************************************************
// Private Methods
// **********************************************************
var transforms = ( function ( ) {
var values = new Array ( 101 ) ;
for ( var i = 0 ; i < 101 ; i ++ ) {
values [ i ] = makeTransform ( i ) ;
}
return values ;
2014-09-30 12:29:53 +02:00
2015-02-04 16:08:32 +01:00
function makeTransform ( value ) {
var scale = value / 100 ;
var translateX = ( value - 100 ) / 2 ;
return 'translateX(' + translateX . toString ( ) + '%) scale(' + scale . toString ( ) + ', 1)' ;
}
} ) ( ) ;
2014-10-12 19:07:47 +02:00
2014-11-03 17:32:25 +01:00
} ) ( ) ;
2014-09-30 12:29:53 +02:00
2015-02-04 16:08:32 +01:00
/ * !
* Angular Material Design
* https : //github.com/angular/material
* @ license MIT
* v0 . 7.1
* /
2014-11-03 17:32:25 +01:00
( function ( ) {
2015-02-04 16:08:32 +01:00
'use strict' ;
2014-11-03 17:32:25 +01:00
/ * *
* @ ngdoc module
2015-02-04 16:08:32 +01:00
* @ name material . components . radioButton
* @ description radioButton module !
2014-11-03 17:32:25 +01:00
* /
2015-02-04 16:08:32 +01:00
angular . module ( 'material.components.radioButton' , [
'material.core'
2014-11-03 17:32:25 +01:00
] )
2015-02-04 16:08:32 +01:00
. directive ( 'mdRadioGroup' , mdRadioGroupDirective )
. directive ( 'mdRadioButton' , mdRadioButtonDirective ) ;
2014-09-30 12:29:53 +02:00
2014-11-03 17:32:25 +01:00
/ * *
2015-02-04 16:08:32 +01:00
* @ ngdoc directive
* @ module material . components . radioButton
* @ name mdRadioGroup
*
* @ restrict E
2014-11-03 17:32:25 +01:00
*
* @ description
2015-02-04 16:08:32 +01:00
* The ` <md-radio-group> ` directive identifies a grouping
* container for the 1. . n grouped radio buttons ; specified using nested
* ` <md-radio-button> ` tags .
2014-11-03 17:32:25 +01:00
*
2015-02-04 16:08:32 +01:00
* As per the [ material design spec ] ( http : //www.google.com/design/spec/style/color.html#color-ui-color-application)
* the radio button is in the accent color by default . The primary color palette may be used with
* the ` md-primary ` class .
2014-11-06 16:55:16 +01:00
*
2015-02-04 16:08:32 +01:00
* Note : ` <md-radio-group> ` and ` <md-radio-button> ` handle tabindex differently
* than the native ` <input type='radio'> ` controls . Whereas the native controls
* force the user to tab through all the radio buttons , ` <md-radio-group> `
* is focusable , and by default the ` <md-radio-button> ` s are not .
*
* @ param { string } ng - model Assignable angular expression to data - bind to .
* @ param { boolean = } md - no - ink Use of attribute indicates flag to disable ink ripple effects .
2014-11-03 17:32:25 +01:00
*
* @ usage
* < hljs lang = "html" >
2015-02-04 16:08:32 +01:00
* < md - radio - group ng - model = "selected" >
2014-11-03 17:32:25 +01:00
*
2015-02-04 16:08:32 +01:00
* < md - radio - button
* ng - repeat = "d in colorOptions"
* ng - value = "d.value" aria - label = "{{ d.label }}" >
2014-11-03 17:32:25 +01:00
*
2015-02-04 16:08:32 +01:00
* { { d . label } }
2014-11-03 17:32:25 +01:00
*
2015-02-04 16:08:32 +01:00
* < / m d - r a d i o - b u t t o n >
2014-11-03 17:32:25 +01:00
*
2015-02-04 16:08:32 +01:00
* < / m d - r a d i o - g r o u p >
* < / h l j s >
2014-11-03 17:32:25 +01:00
*
* /
2015-02-04 16:08:32 +01:00
function mdRadioGroupDirective ( $mdUtil , $mdConstant , $mdTheming ) {
RadioGroupController . prototype = createRadioGroupControllerProto ( ) ;
2014-09-30 12:29:53 +02:00
2015-02-04 16:08:32 +01:00
return {
restrict : 'E' ,
controller : [ '$element' , RadioGroupController ] ,
require : [ 'mdRadioGroup' , '?ngModel' ] ,
link : { pre : linkRadioGroup }
} ;
2014-09-30 12:29:53 +02:00
2015-02-04 16:08:32 +01:00
function linkRadioGroup ( scope , element , attr , ctrls ) {
$mdTheming ( element ) ;
var rgCtrl = ctrls [ 0 ] ;
var ngModelCtrl = ctrls [ 1 ] || $mdUtil . fakeNgModel ( ) ;
2014-09-30 12:29:53 +02:00
2015-02-04 16:08:32 +01:00
function keydownListener ( ev ) {
switch ( ev . keyCode ) {
case $mdConstant . KEY _CODE . LEFT _ARROW :
case $mdConstant . KEY _CODE . UP _ARROW :
ev . preventDefault ( ) ;
rgCtrl . selectPrevious ( ) ;
break ;
2014-09-30 12:29:53 +02:00
2015-02-04 16:08:32 +01:00
case $mdConstant . KEY _CODE . RIGHT _ARROW :
case $mdConstant . KEY _CODE . DOWN _ARROW :
ev . preventDefault ( ) ;
rgCtrl . selectNext ( ) ;
break ;
2014-09-30 12:29:53 +02:00
2015-02-04 16:08:32 +01:00
case $mdConstant . KEY _CODE . ENTER :
var form = angular . element ( $mdUtil . getClosest ( element [ 0 ] , 'form' ) ) ;
if ( form . length > 0 ) {
form . triggerHandler ( 'submit' ) ;
}
break ;
}
}
2014-09-30 12:29:53 +02:00
2015-02-04 16:08:32 +01:00
rgCtrl . init ( ngModelCtrl ) ;
element . attr ( {
'role' : 'radiogroup' ,
'tabIndex' : element . attr ( 'tabindex' ) || '0'
} )
. on ( 'keydown' , keydownListener ) ;
2014-09-30 12:29:53 +02:00
2014-11-03 17:32:25 +01:00
}
2014-09-30 12:29:53 +02:00
2015-02-04 16:08:32 +01:00
function RadioGroupController ( $element ) {
this . _radioButtonRenderFns = [ ] ;
this . $element = $element ;
2014-10-12 19:07:47 +02:00
}
2014-09-30 12:29:53 +02:00
2015-02-04 16:08:32 +01:00
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 = $mdUtil . iterator (
Array . prototype . slice . call ( parent [ 0 ] . querySelectorAll ( 'md-radio-button' ) ) ,
true
) ;
if ( buttons . count ( ) ) {
var validate = function ( button ) {
// If disabled, then NOT valid
return ! angular . element ( button ) . attr ( "disabled" ) ;
} ;
var selected = parent [ 0 ] . querySelector ( 'md-radio-button.md-checked' ) ;
var target = buttons [ increment < 0 ? 'previous' : 'next' ] ( selected , validate ) || buttons . first ( ) ;
// Activate radioButton's click listener (triggerHandler won't create a real click event)
angular . element ( target ) . triggerHandler ( 'click' ) ;
}
}
}
mdRadioGroupDirective . $inject = [ "$mdUtil" , "$mdConstant" , "$mdTheming" ] ;
2014-09-30 12:29:53 +02:00
2014-11-03 17:32:25 +01:00
/ * *
* @ ngdoc directive
2015-02-04 16:08:32 +01:00
* @ module material . components . radioButton
* @ name mdRadioButton
*
2014-11-03 17:32:25 +01:00
* @ restrict E
2015-02-04 16:08:32 +01:00
*
2014-11-03 17:32:25 +01:00
* @ description
2015-02-04 16:08:32 +01:00
* The ` <md-radio-button> ` directive is the child directive required to be used within ` <md-radio-group> ` elements .
2014-11-03 17:32:25 +01:00
*
2015-02-04 16:08:32 +01:00
* While similar to the ` <input type="radio" ng-model="" value=""> ` directive ,
* the ` <md-radio-button> ` directive provides ink effects , ARIA support , and
* supports use within named radio groups .
2014-11-03 17:32:25 +01:00
*
2015-02-04 16:08:32 +01:00
* @ 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 Adds label to radio button for accessibility .
* Defaults to radio button ' s text . If no default text is found , a warning will be logged .
2014-11-03 17:32:25 +01:00
*
* @ usage
* < hljs lang = "html" >
*
2015-02-04 16:08:32 +01:00
* < md - radio - button value = "1" aria - label = "Label 1" >
* Label 1
* < / m d - r a d i o - b u t t o n >
2014-11-03 17:32:25 +01:00
*
2015-02-04 16:08:32 +01:00
* < md - radio - button ng - model = "color" ng - value = "specialValue" aria - label = "Green" >
* Green
* < / m d - r a d i o - b u t t o n >
2014-11-03 17:32:25 +01:00
*
* < / h l j s >
*
2015-02-04 16:08:32 +01:00
* /
function mdRadioButtonDirective ( $mdAria , $mdUtil , $mdTheming ) {
var CHECKED _CSS = 'md-checked' ;
2014-11-03 17:32:25 +01:00
return {
restrict : 'E' ,
2015-02-04 16:08:32 +01:00
require : '^mdRadioGroup' ,
transclude : true ,
template : '<div class="md-container" md-ink-ripple md-ink-ripple-checkbox>' +
'<div class="md-off"></div>' +
'<div class="md-on"></div>' +
'</div>' +
'<div ng-transclude class="md-label"></div>' ,
link : link
} ;
2014-11-03 17:32:25 +01:00
2015-02-04 16:08:32 +01:00
function link ( scope , element , attr , rgCtrl ) {
var lastChecked ;
2014-09-30 12:29:53 +02:00
2015-02-04 16:08:32 +01:00
$mdTheming ( element ) ;
configureAria ( element , scope ) ;
2014-09-30 12:29:53 +02:00
2015-02-04 16:08:32 +01:00
rgCtrl . add ( render ) ;
attr . $observe ( 'value' , render ) ;
2014-09-30 12:29:53 +02:00
2015-02-04 16:08:32 +01:00
element
. on ( 'click' , listener )
. on ( '$destroy' , function ( ) {
rgCtrl . remove ( render ) ;
} ) ;
2014-09-30 12:29:53 +02:00
2015-02-04 16:08:32 +01:00
function listener ( ev ) {
if ( element [ 0 ] . hasAttribute ( 'disabled' ) ) return ;
2014-09-30 12:29:53 +02:00
2015-02-04 16:08:32 +01:00
scope . $apply ( function ( ) {
rgCtrl . setViewValue ( attr . value , ev && ev . type ) ;
} ) ;
}
2014-09-30 12:29:53 +02:00
2015-02-04 16:08:32 +01:00
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 ( ) ;
2014-09-30 12:29:53 +02:00
2015-02-04 16:08:32 +01:00
element . attr ( {
'id' : scope . ariaId ,
'role' : 'radio' ,
'aria-checked' : 'false'
} ) ;
2014-09-30 12:29:53 +02:00
2015-02-04 16:08:32 +01:00
$mdAria . expectWithText ( element , 'aria-label' ) ;
2014-09-30 12:29:53 +02:00
2015-02-04 16:08:32 +01:00
/ * *
* 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' + "_" + $mdUtil . nextUid ( ) ) ;
}
}
}
}
mdRadioButtonDirective . $inject = [ "$mdAria" , "$mdUtil" , "$mdTheming" ] ;
2014-09-30 12:29:53 +02:00
2015-02-04 16:08:32 +01:00
} ) ( ) ;
2014-09-30 12:29:53 +02:00
2015-02-04 16:08:32 +01:00
/ * !
* Angular Material Design
* https : //github.com/angular/material
* @ license MIT
* v0 . 7.1
* /
( function ( ) {
'use strict' ;
2014-09-30 12:29:53 +02:00
2015-02-04 16:08:32 +01:00
/ * *
* @ ngdoc module
* @ name material . components . sidenav
*
* @ description
* A Sidenav QP component .
* /
angular . module ( 'material.components.sidenav' , [
'material.core' ,
'material.components.backdrop'
] )
. factory ( '$mdSidenav' , SidenavService )
. directive ( 'mdSidenav' , SidenavDirective )
. controller ( '$mdSidenavController' , SidenavController ) ;
2014-09-30 12:29:53 +02:00
2015-02-04 16:08:32 +01:00
/ * *
* @ private
* @ ngdoc service
* @ name $mdSidenav
* @ module material . components . sidenav
*
* @ description
* ` $ mdSidenav ` makes it easy to interact with multiple sidenavs
* in an app .
*
* @ usage
* < hljs lang = "js" >
* // Toggle the given sidenav
* $mdSidenav ( componentId ) . toggle ( ) ;
* < / h l j s >
* < hljs lang = "js" >
* // Open the given sidenav
* $mdSidenav ( componentId ) . open ( ) ;
* < / h l j s >
* < hljs lang = "js" >
* // Close the given sidenav
* $mdSidenav ( componentId ) . close ( ) ;
* < / h l j s >
* < hljs lang = "js" >
* // Exposes whether given sidenav is set to be open
* $mdSidenav ( componentId ) . isOpen ( ) ;
* < / h l j s >
* < hljs lang = "js" >
* // Exposes whether given sidenav is locked open
* // If this is true, the sidenav will be open regardless of isOpen()
* $mdSidenav ( componentId ) . isLockedOpen ( ) ;
* < / h l j s >
* /
function SidenavService ( $mdComponentRegistry , $q ) {
return function ( handle ) {
var errorMsg = "SideNav '" + handle + "' is not available!" ;
// Lookup the controller instance for the specified sidNav instance
var instance = $mdComponentRegistry . get ( handle ) ;
if ( ! instance ) {
$mdComponentRegistry . notFoundError ( handle ) ;
}
return {
isOpen : function ( ) {
return instance && instance . isOpen ( ) ;
} ,
isLockedOpen : function ( ) {
return instance && instance . isLockedOpen ( ) ;
} ,
toggle : function ( ) {
return instance ? instance . toggle ( ) : $q . reject ( errorMsg ) ;
} ,
open : function ( ) {
return instance ? instance . open ( ) : $q . reject ( errorMsg ) ;
} ,
close : function ( ) {
return instance ? instance . close ( ) : $q . reject ( errorMsg ) ;
}
} ;
} ;
}
SidenavService . $inject = [ "$mdComponentRegistry" , "$q" ] ;
/ * *
* @ ngdoc directive
* @ name mdSidenav
* @ module material . components . sidenav
* @ restrict E
*
* @ description
*
* A Sidenav component that can be opened and closed programatically .
*
* By default , upon opening it will slide out on top of the main content area .
*
* @ usage
* < hljs lang = "html" >
* < div layout = "row" ng - controller = "MyController" >
* < md - sidenav md - component - id = "left" class = "md-sidenav-left" >
* Left Nav !
* < / m d - s i d e n a v >
*
* < md - content >
* Center Content
* < md - button ng - click = "openLeftMenu()" >
* Open Left Menu
* < / m d - b u t t o n >
* < / m d - c o n t e n t >
*
* < md - sidenav md - component - id = "right"
* md - is - locked - open = "$media('min-width: 333px')"
* class = "md-sidenav-right" >
* Right Nav !
* < / m d - s i d e n a v >
* < / d i v >
* < / h l j s >
*
* < hljs lang = "js" >
* var app = angular . module ( 'myApp' , [ 'ngMaterial' ] ) ;
* app . controller ( 'MyController' , function ( $scope , $mdSidenav ) {
* $scope . openLeftMenu = function ( ) {
* $mdSidenav ( 'left' ) . toggle ( ) ;
* } ;
* } ) ;
* < / h l j s >
*
* @ param { expression = } md - is - open A model bound to whether the sidenav is opened .
* @ param { string = } md - component - id componentId to use with $mdSidenav service .
* @ param { expression = } md - is - locked - open When this expression evalutes to true ,
* the sidenav 'locks open' : it falls into the content ' s flow instead
* of appearing over it . This overrides the ` is-open ` attribute .
*
* A $media ( ) function is exposed to the is - locked - open attribute , which
* can be given a media query or one of the ` sm ` , ` gt-sm ` , ` md ` , ` gt-md ` , ` lg ` or ` gt-lg ` presets .
* Examples :
*
* - ` <md-sidenav md-is-locked-open="shouldLockOpen"></md-sidenav> `
* - ` <md-sidenav md-is-locked-open=" $ media('min-width: 1000px')"></md-sidenav> `
* - ` <md-sidenav md-is-locked-open=" $ media('sm')"></md-sidenav> ` ( locks open on small screens )
* /
function SidenavDirective ( $timeout , $animate , $parse , $mdMedia , $mdConstant , $compile , $mdTheming , $q , $document ) {
return {
restrict : 'E' ,
scope : {
isOpen : '=?mdIsOpen'
} ,
controller : '$mdSidenavController' ,
compile : function ( element ) {
element . addClass ( 'md-closed' ) ;
element . attr ( 'tabIndex' , '-1' ) ;
return postLink ;
}
} ;
/ * *
* Directive Post Link function ...
* /
function postLink ( scope , element , attr , sidenavCtrl ) {
var triggeringElement = null ;
var promise = $q . when ( true ) ;
var isLockedOpenParsed = $parse ( attr . mdIsLockedOpen ) ;
var isLocked = function ( ) {
return isLockedOpenParsed ( scope . $parent , {
$media : $mdMedia
} ) ;
} ;
var backdrop = $compile (
'<md-backdrop class="md-sidenav-backdrop md-opaque ng-enter">'
) ( scope ) ;
element . on ( '$destroy' , sidenavCtrl . destroy ) ;
$mdTheming . inherit ( backdrop , element ) ;
scope . $watch ( isLocked , updateIsLocked ) ;
scope . $watch ( 'isOpen' , updateIsOpen ) ;
// Publish special accessor for the Controller instance
sidenavCtrl . $toggleOpen = toggleOpen ;
/ * *
* Toggle the DOM classes to indicate ` locked `
* @ param isLocked
* /
function updateIsLocked ( isLocked , oldValue ) {
scope . isLockedOpen = isLocked ;
if ( isLocked === oldValue ) {
element . toggleClass ( 'md-locked-open' , ! ! isLocked ) ;
} else {
$animate [ isLocked ? 'addClass' : 'removeClass' ] ( element , 'md-locked-open' ) ;
}
backdrop . toggleClass ( 'md-locked-open' , ! ! isLocked ) ;
}
/ * *
* Toggle the SideNav view and attach / detach listeners
* @ param isOpen
* /
function updateIsOpen ( isOpen ) {
var parent = element . parent ( ) ;
parent [ isOpen ? 'on' : 'off' ] ( 'keydown' , onKeyDown ) ;
backdrop [ isOpen ? 'on' : 'off' ] ( 'click' , close ) ;
if ( isOpen ) {
// Capture upon opening..
triggeringElement = $document [ 0 ] . activeElement ;
}
return promise = $q . all ( [
$animate [ isOpen ? 'enter' : 'leave' ] ( backdrop , parent ) ,
$animate [ isOpen ? 'removeClass' : 'addClass' ] ( element , 'md-closed' ) . then ( function ( ) {
// If we opened, and haven't closed again before the animation finished
if ( scope . isOpen ) {
element . focus ( ) ;
}
} )
] ) ;
}
/ * *
* Toggle the sideNav view and publish a promise to be resolved when
* the view animation finishes .
*
* @ param isOpen
* @ returns { * }
* /
function toggleOpen ( isOpen ) {
if ( scope . isOpen == isOpen ) {
return $q . when ( true ) ;
} else {
var deferred = $q . defer ( ) ;
// Toggle value to force an async `updateIsOpen()` to run
scope . isOpen = isOpen ;
$timeout ( function ( ) {
// When the current `updateIsOpen()` animation finishes
promise . then ( function ( result ) {
if ( ! scope . isOpen ) {
// reset focus to originating element (if available) upon close
triggeringElement && triggeringElement . focus ( ) ;
triggeringElement = null ;
}
deferred . resolve ( result ) ;
} ) ;
} , 0 , false ) ;
return deferred . promise ;
}
}
/ * *
* Auto - close sideNav when the ` escape ` key is pressed .
* @ param evt
* /
function onKeyDown ( ev ) {
var isEscape = ( ev . keyCode === $mdConstant . KEY _CODE . ESCAPE ) ;
return isEscape ? close ( ev ) : $q . when ( true ) ;
}
/ * *
* 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 ( ev ) {
ev . preventDefault ( ) ;
ev . stopPropagation ( ) ;
return sidenavCtrl . close ( ) ;
}
}
}
SidenavDirective . $inject = [ "$timeout" , "$animate" , "$parse" , "$mdMedia" , "$mdConstant" , "$compile" , "$mdTheming" , "$q" , "$document" ] ;
/ *
* @ private
* @ ngdoc controller
* @ name SidenavController
* @ module material . components . sidenav
*
* /
function SidenavController ( $scope , $element , $attrs , $mdComponentRegistry , $q ) {
var self = this ;
// Use Default internal method until overridden by directive postLink
self . $toggleOpen = function ( ) { return $q . when ( $scope . isOpen ) ; } ;
self . isOpen = function ( ) { return ! ! $scope . isOpen ; } ;
self . isLockedOpen = function ( ) { return ! ! $scope . isLockedOpen ; } ;
self . open = function ( ) { return self . $toggleOpen ( true ) ; } ;
self . close = function ( ) { return self . $toggleOpen ( false ) ; } ;
self . toggle = function ( ) { return self . $toggleOpen ( ! $scope . isOpen ) ; } ;
self . destroy = $mdComponentRegistry . register ( self , $attrs . mdComponentId ) ;
}
SidenavController . $inject = [ "$scope" , "$element" , "$attrs" , "$mdComponentRegistry" , "$q" ] ;
} ) ( ) ;
/ * !
* Angular Material Design
* https : //github.com/angular/material
* @ license MIT
* v0 . 7.1
* /
( function ( ) {
'use strict' ;
/ * *
* @ ngdoc module
* @ name material . components . slider
* /
angular . module ( 'material.components.slider' , [
'material.core'
] )
. directive ( 'mdSlider' , SliderDirective ) ;
/ * *
* @ ngdoc directive
* @ name mdSlider
* @ module material . components . slider
* @ restrict E
* @ description
* The ` <md-slider> ` component allows the user to choose from a range of
* values .
*
* As per the [ material design spec ] ( http : //www.google.com/design/spec/style/color.html#color-ui-color-application)
* the slider is in the accent color by default . The primary color palette may be used with
* the ` md-primary ` class .
*
* 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 ` md-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 < / h 4 >
* < hljs lang = "html" >
* < md - slider ng - model = "myValue" min = "5" max = "500" >
* < / m d - s l i d e r >
* < / h l j s >
* < h4 > Discrete Mode < / h 4 >
* < hljs lang = "html" >
* < md - slider md - discrete ng - model = "myDiscreteValue" step = "10" min = "10" max = "130" >
* < / m d - s l i d e r >
* < / h l j s >
*
* @ param { boolean = } md - 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 ( $$rAF , $window , $mdAria , $mdUtil , $mdConstant , $mdTheming , $mdGesture , $parse ) {
return {
scope : { } ,
require : '?ngModel' ,
template :
'<div class="md-track-container">' +
'<div class="md-track"></div>' +
'<div class="md-track md-track-fill"></div>' +
'<div class="md-track-ticks"></div>' +
'</div>' +
'<div class="md-thumb-container">' +
'<div class="md-thumb"></div>' +
'<div class="md-focus-thumb"></div>' +
'<div class="md-focus-ring"></div>' +
'<div class="md-sign">' +
'<span class="md-thumb-text"></span>' +
'</div>' +
'<div class="md-disabled-thumb"></div>' +
'</div>' ,
link : postLink
} ;
function postLink ( scope , element , attr , ngModelCtrl ) {
$mdTheming ( element ) ;
ngModelCtrl = ngModelCtrl || {
// 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 isDisabledParsed = attr . ngDisabled && $parse ( attr . ngDisabled ) ;
var isDisabledGetter = isDisabledParsed ?
function ( ) { return isDisabledParsed ( scope . $parent ) ; } :
angular . noop ;
var thumb = angular . element ( element [ 0 ] . querySelector ( '.md-thumb' ) ) ;
var thumbText = angular . element ( element [ 0 ] . querySelector ( '.md-thumb-text' ) ) ;
var thumbContainer = thumb . parent ( ) ;
var trackContainer = angular . element ( element [ 0 ] . querySelector ( '.md-track-container' ) ) ;
var activeTrack = angular . element ( element [ 0 ] . querySelector ( '.md-track-fill' ) ) ;
var tickContainer = angular . element ( element [ 0 ] . querySelector ( '.md-track-ticks' ) ) ;
var throttledRefreshDimensions = $mdUtil . throttle ( refreshSliderDimensions , 5000 ) ;
// 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 ) ;
}
$mdAria . expect ( element , 'aria-label' ) ;
$mdGesture . register ( element , 'drag' ) ;
element
. attr ( {
tabIndex : 0 ,
role : 'slider'
} )
. on ( 'keydown' , keydownListener )
. on ( '$md.pressdown' , onPressDown )
. on ( '$md.pressup' , onPressUp )
. on ( '$md.dragstart' , onDragStart )
. on ( '$md.drag' , onDrag )
. on ( '$md.dragend' , onDragEnd ) ;
// On resize, recalculate the slider's dimensions and re-render
function updateAll ( ) {
refreshSliderDimensions ( ) ;
ngModelRender ( ) ;
redrawTicks ( ) ;
}
setTimeout ( updateAll ) ;
var debouncedUpdateAll = $$rAF . throttle ( updateAll ) ;
angular . element ( $window ) . on ( 'resize' , debouncedUpdateAll ) ;
scope . $on ( '$destroy' , function ( ) {
angular . element ( $window ) . off ( 'resize' , debouncedUpdateAll ) ;
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 ) ;
updateAll ( ) ;
}
function updateMax ( value ) {
max = parseFloat ( value ) ;
element . attr ( 'aria-valuemax' , value ) ;
updateAll ( ) ;
}
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 . mdDiscrete ) ) return ;
var numSteps = Math . floor ( ( max - min ) / step ) ;
if ( ! tickCanvas ) {
var trackTicksStyle = $window . getComputedStyle ( tickContainer [ 0 ] ) ;
tickCanvas = angular . element ( '<canvas style="position:absolute;">' ) ;
tickCtx = tickCanvas [ 0 ] . getContext ( '2d' ) ;
tickCtx . fillStyle = trackTicksStyle . backgroundColor || '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 = { } ;
refreshSliderDimensions ( ) ;
function refreshSliderDimensions ( ) {
sliderDimensions = trackContainer [ 0 ] . getBoundingClientRect ( ) ;
}
function getSliderDimensions ( ) {
throttledRefreshDimensions ( ) ;
return sliderDimensions ;
}
/ * *
* left / right arrow listener
* /
function keydownListener ( ev ) {
if ( element [ 0 ] . hasAttribute ( 'disabled' ) ) {
return ;
}
var changeAmount ;
if ( ev . keyCode === $mdConstant . KEY _CODE . LEFT _ARROW ) {
changeAmount = - step ;
} else if ( ev . keyCode === $mdConstant . 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 ( ) {
if ( isNaN ( ngModelCtrl . $viewValue ) ) {
ngModelCtrl . $viewValue = ngModelCtrl . $modelValue ;
}
var percent = ( ngModelCtrl . $viewValue - min ) / ( max - min ) ;
scope . modelValue = ngModelCtrl . $viewValue ;
element . attr ( 'aria-valuenow' , ngModelCtrl . $viewValue ) ;
setSliderPercent ( percent ) ;
thumbText . text ( ngModelCtrl . $viewValue ) ;
}
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 (
$mdConstant . CSS . TRANSFORM ,
'translate3d(' + ( percent * 100 ) + '%,0,0)'
) ;
element . toggleClass ( 'md-min' , percent === 0 ) ;
}
/ * *
* Slide listeners
* /
var isDragging = false ;
var isDiscrete = angular . isDefined ( attr . mdDiscrete ) ;
function onPressDown ( ev ) {
if ( isDisabledGetter ( ) ) return ;
element . addClass ( 'active' ) ;
element [ 0 ] . focus ( ) ;
refreshSliderDimensions ( ) ;
var exactVal = percentToValue ( positionToPercent ( ev . pointer . x ) ) ;
var closestVal = minMaxValidator ( stepValidator ( exactVal ) ) ;
scope . $apply ( function ( ) {
setModelValue ( closestVal ) ;
setSliderPercent ( valueToPercent ( closestVal ) ) ;
} ) ;
}
function onPressUp ( ev ) {
if ( isDisabledGetter ( ) ) return ;
element . removeClass ( 'dragging active' ) ;
var exactVal = percentToValue ( positionToPercent ( ev . pointer . x ) ) ;
var closestVal = minMaxValidator ( stepValidator ( exactVal ) ) ;
scope . $apply ( function ( ) {
setModelValue ( closestVal ) ;
} ) ;
}
function onDragStart ( ev ) {
if ( isDisabledGetter ( ) ) return ;
isDragging = true ;
ev . stopPropagation ( ) ;
element . addClass ( 'dragging' ) ;
setSliderFromEvent ( ev ) ;
}
function onDrag ( ev ) {
if ( ! isDragging ) return ;
ev . stopPropagation ( ) ;
setSliderFromEvent ( ev ) ;
}
function onDragEnd ( ev ) {
if ( ! isDragging ) return ;
ev . stopPropagation ( ) ;
isDragging = false ;
}
function setSliderFromEvent ( ev ) {
// While panning discrete, update only the
// visual positioning but not the model value.
if ( isDiscrete ) adjustThumbPosition ( ev . pointer . x ) ;
else doSlide ( ev . pointer . x ) ;
}
/ * *
* 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 ) {
var exactVal = percentToValue ( positionToPercent ( x ) ) ;
var closestVal = minMaxValidator ( stepValidator ( exactVal ) ) ;
setSliderPercent ( positionToPercent ( x ) ) ;
thumbText . text ( closestVal ) ;
}
/ * *
* Convert horizontal position on slider to percentage value of offset from beginning ...
* @ param x
* @ returns { number }
* /
function positionToPercent ( x ) {
return Math . max ( 0 , Math . min ( 1 , ( 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 ) ;
}
}
}
SliderDirective . $inject = [ "$$rAF" , "$window" , "$mdAria" , "$mdUtil" , "$mdConstant" , "$mdTheming" , "$mdGesture" , "$parse" ] ;
} ) ( ) ;
/ * !
* Angular Material Design
* https : //github.com/angular/material
* @ license MIT
* v0 . 7.1
* /
( function ( ) {
'use strict' ;
/ *
* @ ngdoc module
* @ name material . components . sticky
* @ description
*
* Sticky effects for md
* /
angular . module ( 'material.components.sticky' , [
'material.core' ,
'material.components.content'
] )
. factory ( '$mdSticky' , MdSticky ) ;
/ *
* @ ngdoc service
* @ name $mdSticky
* @ module material . components . sticky
*
* @ description
* The ` $ mdSticky ` service provides a mixin to make elements sticky .
*
* @ returns A ` $ mdSticky ` function that takes three arguments :
* - ` scope `
* - ` element ` : The element that will be 'sticky'
* - ` elementClone ` : 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 MdSticky ( $document , $mdConstant , $compile , $$rAF , $mdUtil ) {
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 ( 'mdContent' ) ;
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 . throttle ( 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 ;
var stickyBaseoffset = contentEl . prop ( 'offsetTop' ) ;
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 ( 'md-sticky-clone' ) ;
stickyClone . css ( 'top' , stickyBaseoffset + 'px' ) ;
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 ( ) {
// 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 ? - 1 : 1 ;
} ) ;
// 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 ( $mdConstant . CSS . TRANSFORM , '' ) ;
}
} else {
item . translateY = amount ;
item . clone . css (
$mdConstant . CSS . 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 = + $mdUtil . now ( ) ;
} ) ;
function loopScrollEvent ( ) {
if ( + $mdUtil . now ( ) - lastScrollTime > SCROLL _END _DELAY ) {
isScrolling = false ;
element . triggerHandler ( '$scrollend' ) ;
} else {
element . triggerHandler ( '$scroll' ) ;
$$rAF ( loopScrollEvent ) ;
}
}
}
}
MdSticky . $inject = [ "$document" , "$mdConstant" , "$compile" , "$$rAF" , "$mdUtil" ] ;
} ) ( ) ;
/ * !
* Angular Material Design
* https : //github.com/angular/material
* @ license MIT
* v0 . 7.1
* /
( function ( ) {
'use strict' ;
/ * *
* @ ngdoc module
* @ name material . components . subheader
* @ description
* SubHeader module
*
* Subheaders are special list tiles that delineate distinct sections of a
* list or grid list and are typically related to the current filtering or
* sorting criteria . Subheader tiles are either displayed inline with tiles or
* can be associated with content , for example , in an adjacent column .
*
* Upon scrolling , subheaders remain pinned to the top of the screen and remain
* pinned until pushed on or off screen by the next subheader . @ see [ Material
* Design Specifications ] ( https : //www.google.com/design/spec/components/subheaders.html)
*
* > To improve the visual grouping of content , use the system color for your subheaders .
*
* /
angular . module ( 'material.components.subheader' , [
'material.core' ,
'material.components.sticky'
] )
. directive ( 'mdSubheader' , MdSubheaderDirective ) ;
/ * *
* @ ngdoc directive
* @ name mdSubheader
* @ module material . components . subheader
*
* @ restrict E
*
* @ description
* The ` <md-subheader> ` directive is a subheader for a section
*
* @ usage
* < hljs lang = "html" >
* < md - subheader > Online Friends < / m d - s u b h e a d e r >
* < / h l j s >
* /
function MdSubheaderDirective ( $mdSticky , $compile , $mdTheming ) {
return {
restrict : 'E' ,
replace : true ,
transclude : true ,
template :
'<h2 class="md-subheader">' +
'<span class="md-subheader-content"></span>' +
'</h2>' ,
compile : function ( element , attr , transclude ) {
var outerHTML = element [ 0 ] . outerHTML ;
return function postLink ( scope , element , attr ) {
$mdTheming ( element ) ;
function getContent ( el ) {
return angular . element ( el [ 0 ] . querySelector ( '.md-subheader-content' ) ) ;
2014-11-03 17:32:25 +01:00
}
2014-09-30 12:29:53 +02:00
2015-02-04 16:08:32 +01:00
// Transclude the user-given contents of the subheader
// the conventional way.
transclude ( scope , function ( clone ) {
getContent ( element ) . append ( clone ) ;
} ) ;
2014-09-30 12:29:53 +02:00
2015-02-04 16:08:32 +01:00
// 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 ) ;
$mdTheming ( stickyClone ) ;
getContent ( stickyClone ) . append ( clone ) ;
$mdSticky ( scope , element , stickyClone ) ;
} ) ;
} ;
2014-09-30 12:29:53 +02:00
}
2014-11-03 17:32:25 +01:00
} ;
2014-10-12 19:07:47 +02:00
}
2015-02-04 16:08:32 +01:00
MdSubheaderDirective . $inject = [ "$mdSticky" , "$compile" , "$mdTheming" ] ;
2014-11-03 17:32:25 +01:00
} ) ( ) ;
2014-09-30 12:29:53 +02:00
2015-02-04 16:08:32 +01:00
/ * !
* Angular Material Design
* https : //github.com/angular/material
* @ license MIT
* v0 . 7.1
* /
2014-11-03 17:32:25 +01:00
( function ( ) {
2015-02-04 16:08:32 +01:00
'use strict' ;
2014-11-03 17:32:25 +01:00
/ * *
* @ ngdoc module
2015-02-04 16:08:32 +01:00
* @ name material . components . swipe
* @ description Swipe module !
2014-11-03 17:32:25 +01:00
* /
2014-09-30 12:29:53 +02:00
/ * *
* @ ngdoc directive
2015-02-04 16:08:32 +01:00
* @ module material . components . swipe
* @ name mdSwipeLeft
2014-09-30 12:29:53 +02:00
*
2015-02-04 16:08:32 +01:00
* @ restrict A
2014-09-30 12:29:53 +02:00
*
2015-02-04 16:08:32 +01:00
* @ description
* The md - swipe - left directives allows you to specify custom behavior when an element is swiped
* left .
2014-09-30 12:29:53 +02:00
*
* @ usage
* < hljs lang = "html" >
2015-02-04 16:08:32 +01:00
* < div md - swipe - left = "onSwipeLeft()" > Swipe me left ! < / d i v >
2014-09-30 12:29:53 +02:00
* < / h l j s >
2015-02-04 16:08:32 +01:00
* /
/ * *
* @ ngdoc directive
* @ module material . components . swipe
* @ name mdSwipeRight
2014-09-30 12:29:53 +02:00
*
2015-02-04 16:08:32 +01:00
* @ restrict A
*
* @ description
* The md - swipe - right directives allows you to specify custom behavior when an element is swiped
* right .
*
* @ usage
* < hljs lang = "html" >
* < div md - swipe - right = "onSwipeRight()" > Swipe me right ! < / d i v >
* < / h l j s >
2014-09-30 12:29:53 +02:00
* /
2014-11-03 17:32:25 +01:00
2015-02-04 16:08:32 +01:00
var module = angular . module ( 'material.components.swipe' , [ ] ) ;
2014-09-30 12:29:53 +02:00
2015-02-04 16:08:32 +01:00
[ 'SwipeLeft' , 'SwipeRight' ] . forEach ( function ( name ) {
var directiveName = 'md' + name ;
var eventName = '$md.' + name . toLowerCase ( ) ;
2014-09-30 12:29:53 +02:00
2015-02-04 16:08:32 +01:00
module . directive ( directiveName , /*@ngInject*/ [ "$parse" , function ( $parse ) {
return {
restrict : 'A' ,
link : postLink
} ;
2014-09-30 12:29:53 +02:00
2015-02-04 16:08:32 +01:00
function postLink ( scope , element , attr ) {
var fn = $parse ( attr [ directiveName ] ) ;
2014-09-30 12:29:53 +02:00
2015-02-04 16:08:32 +01:00
element . on ( eventName , function ( ev ) {
scope . $apply ( function ( ) {
fn ( scope , {
$event : ev
} ) ;
} ) ;
} ) ;
2014-09-30 12:29:53 +02:00
2014-11-03 17:32:25 +01:00
}
2015-02-04 16:08:32 +01:00
} ] ) ;
} ) ;
2014-09-30 12:29:53 +02:00
2015-02-04 16:08:32 +01:00
} ) ( ) ;
2014-09-30 12:29:53 +02:00
2015-02-04 16:08:32 +01:00
/ * !
* Angular Material Design
* https : //github.com/angular/material
* @ license MIT
* v0 . 7.1
* /
( function ( ) {
'use strict' ;
2014-09-30 12:29:53 +02:00
2015-02-04 16:08:32 +01:00
/ * *
* @ private
* @ ngdoc module
* @ name material . components . switch
* /
2014-11-03 17:32:25 +01:00
2015-02-04 16:08:32 +01:00
angular . module ( 'material.components.switch' , [
'material.core' ,
'material.components.checkbox'
] )
. directive ( 'mdSwitch' , MdSwitch ) ;
2014-11-03 17:32:25 +01:00
2015-02-04 16:08:32 +01:00
/ * *
* @ private
* @ ngdoc directive
* @ module material . components . switch
* @ name mdSwitch
* @ restrict E
*
* The switch directive is used very much like the normal [ angular checkbox ] ( https : //docs.angularjs.org/api/ng/input/input%5Bcheckbox%5D).
*
* As per the [ material design spec ] ( http : //www.google.com/design/spec/style/color.html#color-ui-color-application)
* the switch is in the accent color by default . The primary color palette may be used with
* the ` md-primary ` class .
*
* @ param { string } ng - model Assignable angular expression to data - bind to .
* @ param { string = } name Property name of the form under which the control is published .
* @ param { expression = } ng - true - value The value to which the expression should be set when selected .
* @ param { expression = } ng - false - value The value to which the expression should be set when not selected .
* @ param { string = } ng - change Angular expression to be executed when input changes due to user interaction with the input element .
* @ param { boolean = } md - no - ink Use of attribute indicates use of ripple ink effects .
* @ param { string = } aria - label Publish the button label used by screen - readers for accessibility . Defaults to the switch ' s text .
*
* @ usage
* < hljs lang = "html" >
* < md - switch ng - model = "isActive" aria - label = "Finished?" >
* Finished ?
* < / m d - s w i t c h >
*
* < md - switch md - no - ink ng - model = "hasInk" aria - label = "No Ink Effects" >
* No Ink Effects
* < / m d - s w i t c h >
*
* < md - switch ng - disabled = "true" ng - model = "isDisabled" aria - label = "Disabled" >
* Disabled
* < / m d - s w i t c h >
*
* < / h l j s >
* /
function MdSwitch ( mdCheckboxDirective , $mdTheming , $mdUtil , $document , $mdConstant , $parse , $$rAF , $mdGesture ) {
var checkboxDirective = mdCheckboxDirective [ 0 ] ;
2014-09-30 12:29:53 +02:00
2015-02-04 16:08:32 +01:00
return {
restrict : 'E' ,
transclude : true ,
template :
'<div class="md-container">' +
'<div class="md-bar"></div>' +
'<div class="md-thumb-container">' +
'<div class="md-thumb" md-ink-ripple md-ink-ripple-checkbox></div>' +
'</div>' +
'</div>' +
'<div ng-transclude class="md-label">' +
'</div>' ,
require : '?ngModel' ,
compile : compile
} ;
2014-09-30 12:29:53 +02:00
2015-02-04 16:08:32 +01:00
function compile ( element , attr ) {
var checkboxLink = checkboxDirective . compile ( element , attr ) ;
// no transition on initial load
element . addClass ( 'md-dragging' ) ;
2014-09-30 12:29:53 +02:00
2015-02-04 16:08:32 +01:00
return function ( scope , element , attr , ngModel ) {
ngModel = ngModel || $mdUtil . fakeNgModel ( ) ;
var disabledGetter = $parse ( attr . ngDisabled ) ;
var thumbContainer = angular . element ( element [ 0 ] . querySelector ( '.md-thumb-container' ) ) ;
var switchContainer = angular . element ( element [ 0 ] . querySelector ( '.md-container' ) ) ;
2014-09-30 12:29:53 +02:00
2015-02-04 16:08:32 +01:00
// no transition on initial load
$$rAF ( function ( ) {
element . removeClass ( 'md-dragging' ) ;
2014-10-12 19:07:47 +02:00
} ) ;
2014-11-03 17:32:25 +01:00
2015-02-04 16:08:32 +01:00
checkboxLink ( scope , element , attr , ngModel ) ;
2014-11-03 17:32:25 +01:00
2015-02-04 16:08:32 +01:00
if ( angular . isDefined ( attr . ngDisabled ) ) {
scope . $watch ( disabledGetter , function ( isDisabled ) {
element . attr ( 'tabindex' , isDisabled ? - 1 : 0 ) ;
} ) ;
2014-11-03 17:32:25 +01:00
}
2015-02-04 16:08:32 +01:00
// These events are triggered by setup drag
$mdGesture . register ( switchContainer , 'drag' ) ;
switchContainer
. on ( '$md.dragstart' , onDragStart )
. on ( '$md.drag' , onDrag )
. on ( '$md.dragend' , onDragEnd ) ;
var drag ;
function onDragStart ( ev ) {
// Don't go if ng-disabled===true
if ( disabledGetter ( scope ) ) return ;
ev . stopPropagation ( ) ;
2014-11-03 17:32:25 +01:00
2015-02-04 16:08:32 +01:00
element . addClass ( 'md-dragging' ) ;
drag = {
width : thumbContainer . prop ( 'offsetWidth' )
} ;
element . removeClass ( 'transition' ) ;
}
2014-11-03 17:32:25 +01:00
2015-02-04 16:08:32 +01:00
function onDrag ( ev ) {
if ( ! drag ) return ;
ev . stopPropagation ( ) ;
ev . srcEvent && ev . srcEvent . preventDefault ( ) ;
2014-11-03 17:32:25 +01:00
2015-02-04 16:08:32 +01:00
var percent = ev . pointer . distanceX / drag . width ;
2014-09-30 12:29:53 +02:00
2015-02-04 16:08:32 +01:00
//if checked, start from right. else, start from left
var translate = ngModel . $viewValue ? 1 + percent : percent ;
// Make sure the switch stays inside its bounds, 0-1%
translate = Math . max ( 0 , Math . min ( 1 , translate ) ) ;
2014-09-30 12:29:53 +02:00
2015-02-04 16:08:32 +01:00
thumbContainer . css ( $mdConstant . CSS . TRANSFORM , 'translate3d(' + ( 100 * translate ) + '%,0,0)' ) ;
drag . translate = translate ;
}
2014-10-12 19:07:47 +02:00
2015-02-04 16:08:32 +01:00
function onDragEnd ( ev ) {
if ( ! drag ) return ;
ev . stopPropagation ( ) ;
2014-11-06 16:55:16 +01:00
2015-02-04 16:08:32 +01:00
element . removeClass ( 'md-dragging' ) ;
thumbContainer . css ( $mdConstant . CSS . TRANSFORM , '' ) ;
2014-11-06 16:55:16 +01:00
2015-02-04 16:08:32 +01:00
// We changed if there is no distance (this is a click a click),
// or if the drag distance is >50% of the total.
var isChanged = ngModel . $viewValue ? drag . translate < 0.5 : drag . translate > 0.5 ;
if ( isChanged ) {
applyModelValue ( ! ngModel . $viewValue ) ;
2014-11-06 16:55:16 +01:00
}
2015-02-04 16:08:32 +01:00
drag = null ;
}
2014-11-06 16:55:16 +01:00
2015-02-04 16:08:32 +01:00
function applyModelValue ( newValue ) {
scope . $apply ( function ( ) {
ngModel . $setViewValue ( newValue ) ;
ngModel . $render ( ) ;
} ) ;
2014-11-03 17:32:25 +01:00
}
2015-02-04 16:08:32 +01:00
} ;
2014-11-03 17:32:25 +01:00
}
}
2015-02-04 16:08:32 +01:00
MdSwitch . $inject = [ "mdCheckboxDirective" , "$mdTheming" , "$mdUtil" , "$document" , "$mdConstant" , "$parse" , "$$rAF" , "$mdGesture" ] ;
2014-11-03 17:32:25 +01:00
} ) ( ) ;
2015-02-04 16:08:32 +01:00
/ * !
* Angular Material Design
* https : //github.com/angular/material
* @ license MIT
* v0 . 7.1
* /
2014-11-03 17:32:25 +01:00
( function ( ) {
2015-02-04 16:08:32 +01:00
'use strict' ;
2014-09-30 12:29:53 +02:00
2014-10-12 19:07:47 +02:00
/ * *
2015-02-04 16:08:32 +01:00
* @ ngdoc module
* @ name material . components . tabs
* @ description
2014-10-12 19:07:47 +02:00
*
2015-02-04 16:08:32 +01:00
* Tabs , created with the ` <md-tabs> ` directive provide * tabbed * navigation with different styles .
* The Tabs component consists of clickable tabs that are aligned horizontally side - by - side .
*
* Features include support for :
*
* - static or dynamic tabs ,
* - responsive designs ,
* - accessibility support ( ARIA ) ,
* - tab pagination ,
* - external or internal tab content ,
* - focus indicators and arrow - key navigations ,
* - programmatic lookup and access to tab controllers , and
* - dynamic transitions through different tab contents .
*
* /
/ *
* @ see js folder for tabs implementation
2014-10-12 19:07:47 +02:00
* /
2015-02-04 16:08:32 +01:00
angular . module ( 'material.components.tabs' , [
'material.core'
] ) ;
} ) ( ) ;
2014-09-30 12:29:53 +02:00
2015-02-04 16:08:32 +01:00
/ * !
* Angular Material Design
* https : //github.com/angular/material
* @ license MIT
* v0 . 7.1
* /
( function ( ) {
'use strict' ;
2014-09-30 12:29:53 +02:00
2015-02-04 16:08:32 +01:00
/ * *
* @ ngdoc module
* @ name material . components . textField
* @ description
* Form
* /
angular . module ( 'material.components.textField' , [
'material.core'
] )
. directive ( 'mdInputGroup' , mdInputGroupDirective )
. directive ( 'mdInput' , mdInputDirective )
. directive ( 'mdTextFloat' , mdTextFloatDirective ) ;
2014-09-30 12:29:53 +02:00
2015-02-04 16:08:32 +01:00
function mdTextFloatDirective ( $mdTheming , $mdUtil , $parse , $log ) {
return {
restrict : 'E' ,
replace : true ,
scope : {
fid : '@?mdFid' ,
label : '@?' ,
value : '=ngModel'
} ,
compile : function ( element , attr ) {
2014-09-30 12:29:53 +02:00
2015-02-04 16:08:32 +01:00
$log . warn ( '<md-text-float> is deprecated. Please use `<md-input-container>` and `<input>`.' +
'More information at http://material.angularjs.org/#/api/material.components.input/directive/mdInputContainer' ) ;
2014-09-30 12:29:53 +02:00
2015-02-04 16:08:32 +01:00
if ( angular . isUndefined ( attr . mdFid ) ) {
attr . mdFid = $mdUtil . nextUid ( ) ;
}
2014-09-30 12:29:53 +02:00
2015-02-04 16:08:32 +01:00
return {
pre : function ( scope , element , attrs ) {
var disabledParsed = $parse ( attrs . ngDisabled ) ;
scope . isDisabled = function ( ) {
return disabledParsed ( scope . $parent ) ;
} ;
2014-09-30 12:29:53 +02:00
2015-02-04 16:08:32 +01:00
scope . inputType = attrs . type || "text" ;
} ,
post : $mdTheming
} ;
} ,
template :
'<md-input-group tabindex="-1">' +
' <label for="{{fid}}" >{{label}}</label>' +
' <md-input id="{{fid}}" ng-disabled="isDisabled()" ng-model="value" type="{{inputType}}"></md-input>' +
'</md-input-group>'
} ;
}
mdTextFloatDirective . $inject = [ "$mdTheming" , "$mdUtil" , "$parse" , "$log" ] ;
2014-09-30 12:29:53 +02:00
2015-02-04 16:08:32 +01:00
function mdInputGroupDirective ( $log ) {
return {
restrict : 'CE' ,
controller : [ '$element' , function ( $element ) {
$log . warn ( '<md-input-group> is deprecated. Please use `<md-input-container>` and `<input>`.' +
'More information at http://material.angularjs.org/#/api/material.components.input/directive/mdInputContainer' ) ;
this . setFocused = function ( isFocused ) {
$element . toggleClass ( 'md-input-focused' , ! ! isFocused ) ;
} ;
this . setHasValue = function ( hasValue ) {
$element . toggleClass ( 'md-input-has-value' , hasValue ) ;
} ;
} ]
} ;
2014-09-30 12:29:53 +02:00
2015-02-04 16:08:32 +01:00
}
mdInputGroupDirective . $inject = [ "$log" ] ;
2014-09-30 12:29:53 +02:00
2015-02-04 16:08:32 +01:00
function mdInputDirective ( $mdUtil , $log ) {
return {
restrict : 'E' ,
replace : true ,
template : '<input >' ,
require : [ '^?mdInputGroup' , '?ngModel' ] ,
link : function ( scope , element , attr , ctrls ) {
if ( ! ctrls [ 0 ] ) return ;
2014-09-30 12:29:53 +02:00
2015-02-04 16:08:32 +01:00
$log . warn ( '<md-input> is deprecated. Please use `<md-input-container>` and `<input>`.' +
'More information at http://material.angularjs.org/#/api/material.components.input/directive/mdInputContainer' ) ;
2014-09-30 12:29:53 +02:00
2015-02-04 16:08:32 +01:00
var inputGroupCtrl = ctrls [ 0 ] ;
var ngModelCtrl = ctrls [ 1 ] ;
scope . $watch ( scope . isDisabled , function ( isDisabled ) {
element . attr ( 'aria-disabled' , ! ! isDisabled ) ;
element . attr ( 'tabindex' , ! ! isDisabled ) ;
} ) ;
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 ( isNotEmpty ( value ) ) ;
return value ;
} ) ;
2014-09-30 12:29:53 +02:00
}
2015-02-04 16:08:32 +01:00
element
. on ( 'input' , function ( ) {
inputGroupCtrl . setHasValue ( isNotEmpty ( ) ) ;
} )
. on ( 'focus' , function ( e ) {
// When the input focuses, add the focused class to the group
inputGroupCtrl . setFocused ( true ) ;
} )
. on ( 'blur' , function ( e ) {
// When the input blurs, remove the focused class from the group
inputGroupCtrl . setFocused ( false ) ;
inputGroupCtrl . setHasValue ( isNotEmpty ( ) ) ;
} ) ;
scope . $on ( '$destroy' , function ( ) {
inputGroupCtrl . setFocused ( false ) ;
inputGroupCtrl . setHasValue ( false ) ;
} ) ;
function isNotEmpty ( value ) {
value = angular . isUndefined ( value ) ? element . val ( ) : value ;
return ( angular . isDefined ( value ) && ( value !== null ) &&
( value . toString ( ) . trim ( ) !== "" ) ) ;
2014-11-03 17:32:25 +01:00
}
}
} ;
}
2015-02-04 16:08:32 +01:00
mdInputDirective . $inject = [ "$mdUtil" , "$log" ] ;
2014-11-03 17:32:25 +01:00
} ) ( ) ;
2014-09-30 12:29:53 +02:00
2015-02-04 16:08:32 +01:00
/ * !
* Angular Material Design
* https : //github.com/angular/material
* @ license MIT
* v0 . 7.1
* /
2014-11-03 17:32:25 +01:00
( function ( ) {
2015-02-04 16:08:32 +01:00
'use strict' ;
/ * *
2014-11-03 17:32:25 +01:00
* @ ngdoc module
2015-02-04 16:08:32 +01:00
* @ name material . components . toast
* @ description
* Toast
2014-11-03 17:32:25 +01:00
* /
2015-02-04 16:08:32 +01:00
angular . module ( 'material.components.toast' , [
'material.core' ,
'material.components.button'
2014-11-03 17:32:25 +01:00
] )
2015-02-04 16:08:32 +01:00
. directive ( 'mdToast' , MdToastDirective )
. provider ( '$mdToast' , MdToastProvider ) ;
2014-09-30 12:29:53 +02:00
2015-02-04 16:08:32 +01:00
function MdToastDirective ( ) {
return {
restrict : 'E'
} ;
}
2014-09-30 12:29:53 +02:00
2015-02-04 16:08:32 +01:00
/ * *
* @ ngdoc service
* @ name $mdToast
* @ module material . components . toast
*
* @ description
* ` $ mdToast ` is a service to build a toast nofication on any position
* on the screen with an optional duration , and provides a simple promise API .
*
*
* # # # Restrictions on custom toasts
* - The toast ' s template must have an outer ` <md-toast> ` element .
* - For a toast action , use element with class ` md-action ` .
* - Add the class ` md-capsule ` for curved corners .
*
* @ usage
* < hljs lang = "html" >
* < div ng - controller = "MyController" >
* < md - button ng - click = "openToast()" >
* Open a Toast !
* < / m d - b u t t o n >
* < / d i v >
* < / h l j s >
*
* < hljs lang = "js" >
* var app = angular . module ( 'app' , [ 'ngMaterial' ] ) ;
* app . controller ( 'MyController' , function ( $scope , $mdToast ) {
* $scope . openToast = function ( $event ) {
* $mdToast . show ( $mdToast . simple ( ) . content ( 'Hello!' ) ) ;
* // Could also do $mdToast.showSimple('Hello');
* } ;
* } ) ;
* < / h l j s >
* /
2014-09-30 12:29:53 +02:00
2015-02-04 16:08:32 +01:00
/ * *
* @ ngdoc method
* @ name $mdToast # showSimple
*
* @ description
* Convenience method which builds and shows a simple toast .
*
* @ returns { promise } A promise that can be resolved with ` $ mdToast.hide() ` or
* rejected with ` $ mdToast.cancel() ` .
*
* /
2014-09-30 12:29:53 +02:00
2015-02-04 16:08:32 +01:00
/ * *
* @ ngdoc method
* @ name $mdToast # simple
*
* @ description
* Builds a preconfigured toast .
*
* @ returns { obj } a ` $ mdToastPreset ` with the chainable configuration methods :
*
* - $mdToastPreset # content ( string ) - sets toast content to string
* - $mdToastPreset # action ( string ) - adds an action button , which resolves the promise returned from ` show() ` if clicked .
* - $mdToastPreset # highlightAction ( boolean ) - sets action button to be highlighted
* - $mdToastPreset # capsule ( boolean ) - adds 'md-capsule' class to the toast ( curved corners )
* /
2014-09-30 12:29:53 +02:00
2015-02-04 16:08:32 +01:00
/ * *
* @ ngdoc method
* @ name $mdToast # build
*
* @ description
* Creates a custom ` $ mdToastPreset ` that you can configure .
*
* @ returns { obj } a ` $ mdToastPreset ` with the chainable configuration methods for shows ' options ( see below ) .
* /
2014-09-30 12:29:53 +02:00
2015-02-04 16:08:32 +01:00
/ * *
* @ ngdoc method
* @ name $mdToast # show
*
* @ description Shows the toast .
*
* @ param { object } optionsOrPreset Either provide an ` $ mdToastPreset ` returned from ` simple() `
* and ` build() ` , or an options object with the following properties :
*
* - ` templateUrl ` - ` {string=} ` : The url of an html template file that will
* be used as the content of the toast . Restrictions : the template must
* have an outer ` md-toast ` element .
* - ` template ` - ` {string=} ` : Same as templateUrl , except this is an actual
* template string .
* - ` hideDelay ` - ` {number=} ` : How many milliseconds the toast should stay
* active before automatically closing . Set to 0 or false to have the toast stay open until
* closed manually . Default : 3000.
* - ` position ` - ` {string=} ` : Where to place the toast . Available : any combination
* of 'bottom' , 'left' , 'top' , 'right' , 'fit' . Default : 'bottom left' .
* - ` controller ` - ` {string=} ` : The controller to associate with this toast .
* The controller will be injected the local ` $ hideToast ` , which is a function
* used to hide the toast .
* - ` locals ` - ` {string=} ` : 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.
* - ` resolve ` - ` {object=} ` : Similar to locals , except it takes promises as values
* and the toast will not open until the promises resolve .
* - ` controllerAs ` - ` {string=} ` : An alias to assign the controller to on the scope .
* - ` parent ` - ` {element=} ` : The element to append the toast to . Defaults to appending
* to the root element of the application .
*
* @ returns { promise } A promise that can be resolved with ` $ mdToast.hide() ` or
* rejected with ` $ mdToast.cancel() ` .
* /
2014-09-30 12:29:53 +02:00
2015-02-04 16:08:32 +01:00
/ * *
* @ ngdoc method
* @ name $mdToast # hide
*
* @ description
* Hide an existing toast and resolve the promise returned from ` $ mdToast.show() ` .
*
* @ param { *= } response An argument for the resolved promise .
*
* /
2014-09-30 12:29:53 +02:00
2015-02-04 16:08:32 +01:00
/ * *
* @ ngdoc method
* @ name $mdToast # cancel
*
* @ description
* Hide the existing toast and reject the promise returned from
* ` $ mdToast.show() ` .
*
* @ param { *= } response An argument for the rejected promise .
*
* /
2014-09-30 12:29:53 +02:00
2015-02-04 16:08:32 +01:00
function MdToastProvider ( $$interimElementProvider ) {
2014-09-30 12:29:53 +02:00
2015-02-04 16:08:32 +01:00
toastDefaultOptions . $inject = [ "$timeout" , "$animate" , "$mdTheming" , "$mdToast" ] ;
return $$interimElementProvider ( '$mdToast' )
. setDefaults ( {
methods : [ 'position' , 'hideDelay' , 'capsule' ] ,
options : toastDefaultOptions
} )
. addPreset ( 'simple' , {
argOption : 'content' ,
methods : [ 'content' , 'action' , 'highlightAction' ] ,
options : /* @ngInject */ [ "$mdToast" , function ( $mdToast ) {
return {
template : [
'<md-toast ng-class="{\'md-capsule\': toast.capsule}">' ,
'<span flex>{{ toast.content }}</span>' ,
'<md-button class="md-action" ng-if="toast.action" ng-click="toast.resolve()" ng-class="{\'md-highlight\': toast.highlightAction}">' ,
'{{ toast.action }}' ,
'</md-button>' ,
'</md-toast>'
] . join ( '' ) ,
controller : function mdToastCtrl ( ) {
this . resolve = function ( ) {
$mdToast . hide ( ) ;
} ;
} ,
controllerAs : 'toast' ,
bindToController : true
} ;
} ]
} ) ;
2014-09-30 12:29:53 +02:00
2015-02-04 16:08:32 +01:00
/* @ngInject */
function toastDefaultOptions ( $timeout , $animate , $mdTheming , $mdToast ) {
return {
onShow : onShow ,
onRemove : onRemove ,
position : 'bottom left' ,
themable : true ,
hideDelay : 3000
} ;
function onShow ( scope , element , options ) {
// 'top left' -> 'md-top md-left'
element . addClass ( options . position . split ( ' ' ) . map ( function ( pos ) {
return 'md-' + pos ;
} ) . join ( ' ' ) ) ;
options . parent . addClass ( toastOpenClass ( options . position ) ) ;
options . onSwipe = function ( ev , gesture ) {
//Add swipeleft/swiperight class to element so it can animate correctly
element . addClass ( 'md-' + ev . type . replace ( '$md.' , '' ) ) ;
$timeout ( $mdToast . cancel ) ;
2014-11-03 17:32:25 +01:00
} ;
2015-02-04 16:08:32 +01:00
element . on ( '$md.swipeleft $md.swiperight' , options . onSwipe ) ;
return $animate . enter ( element , options . parent ) ;
}
function onRemove ( scope , element , options ) {
element . off ( '$md.swipeleft $md.swiperight' , options . onSwipe ) ;
options . parent . removeClass ( toastOpenClass ( options . position ) ) ;
return $animate . leave ( element ) ;
}
function toastOpenClass ( position ) {
return 'md-toast-open-' +
( position . indexOf ( 'top' ) > - 1 ? 'top' : 'bottom' ) ;
}
}
2014-09-30 12:29:53 +02:00
}
2015-02-04 16:08:32 +01:00
MdToastProvider . $inject = [ "$$interimElementProvider" ] ;
2014-11-03 17:32:25 +01:00
} ) ( ) ;
2014-09-30 12:29:53 +02:00
2015-02-04 16:08:32 +01:00
/ * !
* Angular Material Design
* https : //github.com/angular/material
* @ license MIT
* v0 . 7.1
* /
2014-11-03 17:32:25 +01:00
( function ( ) {
2015-02-04 16:08:32 +01:00
'use strict' ;
/ * *
2014-10-12 19:07:47 +02:00
* @ ngdoc module
2015-02-04 16:08:32 +01:00
* @ name material . components . toolbar
2014-10-12 19:07:47 +02:00
* /
2015-02-04 16:08:32 +01:00
angular . module ( 'material.components.toolbar' , [
'material.core' ,
'material.components.content'
2014-11-03 17:32:25 +01:00
] )
2015-02-04 16:08:32 +01:00
. directive ( 'mdToolbar' , mdToolbarDirective ) ;
2014-10-12 19:07:47 +02:00
2015-02-04 16:08:32 +01:00
/ * *
* @ ngdoc directive
* @ name mdToolbar
* @ module material . components . toolbar
* @ restrict E
2014-10-12 19:07:47 +02:00
* @ description
2015-02-04 16:08:32 +01:00
* ` md-toolbar ` is used to place a toolbar in your app .
2014-10-12 19:07:47 +02:00
*
2015-02-04 16:08:32 +01:00
* Toolbars are usually used above a content area to display the title of the
* current page , and show relevant action buttons for that page .
2014-10-12 19:07:47 +02:00
*
2015-02-04 16:08:32 +01:00
* You can change the height of the toolbar by adding either the
* ` md-medium-tall ` or ` md-tall ` class to the toolbar .
2014-10-12 19:07:47 +02:00
*
2015-02-04 16:08:32 +01:00
* @ usage
* < hljs lang = "html" >
* < div layout = "column" layout - fill >
* < md - toolbar >
*
* < div class = "md-toolbar-tools" >
* < span > My App ' s Title < / s p a n >
*
* <!-- fill up the space between left and right area -- >
* < span flex > < / s p a n >
*
* < md - button >
* Right Bar Button
* < / m d - b u t t o n >
* < / d i v >
2014-10-12 19:07:47 +02:00
*
2015-02-04 16:08:32 +01:00
* < / m d - t o o l b a r >
* < md - content >
* Hello !
* < / m d - c o n t e n t >
* < / d i v >
* < / h l j s >
*
* @ param { boolean = } md - scroll - shrink 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
* ` md-content ` element , placed before it . See the scroll shrink demo .
*
*
* @ param { number = } md - shrink - speed - factor 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 .
2014-10-12 19:07:47 +02:00
* /
2015-02-04 16:08:32 +01:00
function mdToolbarDirective ( $$rAF , $mdConstant , $mdUtil , $mdTheming ) {
2014-10-12 19:07:47 +02:00
2015-02-04 16:08:32 +01:00
return {
restrict : 'E' ,
controller : angular . noop ,
link : function ( scope , element , attr ) {
$mdTheming ( element ) ;
2014-10-12 19:07:47 +02:00
2015-02-04 16:08:32 +01:00
if ( angular . isDefined ( attr . mdScrollShrink ) ) {
setupScrollShrink ( ) ;
2014-10-12 19:07:47 +02:00
}
2015-02-04 16:08:32 +01:00
function setupScrollShrink ( ) {
// Current "y" position of scroll
var y = 0 ;
// Store the last scroll top position
var prevScrollTop = 0 ;
2014-10-12 19:07:47 +02:00
2015-02-04 16:08:32 +01:00
var shrinkSpeedFactor = attr . mdShrinkSpeedFactor || 0.5 ;
2014-10-12 19:07:47 +02:00
2015-02-04 16:08:32 +01:00
var toolbarHeight ;
var contentElement ;
2014-10-12 19:07:47 +02:00
2015-02-04 16:08:32 +01:00
var debouncedContentScroll = $$rAF . throttle ( onContentScroll ) ;
var debouncedUpdateHeight = $mdUtil . debounce ( updateToolbarHeight , 5 * 1000 ) ;
2014-10-12 19:07:47 +02:00
2015-02-04 16:08:32 +01:00
// Wait for $mdContentLoaded event from mdContent directive.
// If the mdContent element is a sibling of our toolbar, hook it up
// to scroll events.
scope . $on ( '$mdContentLoaded' , onMdContentLoad ) ;
2014-10-12 19:07:47 +02:00
2015-02-04 16:08:32 +01:00
function onMdContentLoad ( $event , newContentEl ) {
// Toolbar and content must be siblings
if ( element . parent ( ) [ 0 ] === newContentEl . parent ( ) [ 0 ] ) {
// unhook old content event listener if exists
if ( contentElement ) {
contentElement . off ( 'scroll' , debouncedContentScroll ) ;
}
2014-09-30 12:29:53 +02:00
2015-02-04 16:08:32 +01:00
newContentEl . on ( 'scroll' , debouncedContentScroll ) ;
newContentEl . attr ( 'scroll-shrink' , 'true' ) ;
2014-11-06 16:55:16 +01:00
2015-02-04 16:08:32 +01:00
contentElement = newContentEl ;
$$rAF ( updateToolbarHeight ) ;
}
}
2014-11-06 16:55:16 +01:00
2015-02-04 16:08:32 +01:00
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 ( ) ;
}
2014-11-06 16:55:16 +01:00
2015-02-04 16:08:32 +01:00
function onContentScroll ( e ) {
var scrollTop = e ? e . target . scrollTop : prevScrollTop ;
2014-11-06 16:55:16 +01:00
2015-02-04 16:08:32 +01:00
debouncedUpdateHeight ( ) ;
2014-11-06 16:55:16 +01:00
2015-02-04 16:08:32 +01:00
y = Math . min (
toolbarHeight / shrinkSpeedFactor ,
Math . max ( 0 , y + scrollTop - prevScrollTop )
) ;
2014-11-06 16:55:16 +01:00
2015-02-04 16:08:32 +01:00
element . css (
$mdConstant . CSS . TRANSFORM ,
'translate3d(0,' + ( - y * shrinkSpeedFactor ) + 'px,0)'
) ;
contentElement . css (
$mdConstant . CSS . TRANSFORM ,
'translate3d(0,' + ( ( toolbarHeight - y ) * shrinkSpeedFactor ) + 'px,0)'
) ;
prevScrollTop = scrollTop ;
}
2014-11-06 16:55:16 +01:00
}
2015-02-04 16:08:32 +01:00
2014-11-06 16:55:16 +01:00
}
2015-02-04 16:08:32 +01:00
} ;
2014-11-06 16:55:16 +01:00
}
2015-02-04 16:08:32 +01:00
mdToolbarDirective . $inject = [ "$$rAF" , "$mdConstant" , "$mdUtil" , "$mdTheming" ] ;
2014-11-06 16:55:16 +01:00
} ) ( ) ;
2015-02-04 16:08:32 +01:00
/ * !
* Angular Material Design
* https : //github.com/angular/material
* @ license MIT
* v0 . 7.1
* /
2014-11-03 17:32:25 +01:00
( function ( ) {
2015-02-04 16:08:32 +01:00
'use strict' ;
/ * *
2014-09-30 12:29:53 +02:00
* @ ngdoc module
2015-02-04 16:08:32 +01:00
* @ name material . components . tooltip
2014-09-30 12:29:53 +02:00
* /
2015-02-04 16:08:32 +01:00
angular . module ( 'material.components.tooltip' , [
'material.core'
2014-09-30 12:29:53 +02:00
] )
2015-02-04 16:08:32 +01:00
. directive ( 'mdTooltip' , MdTooltipDirective ) ;
2014-09-30 12:29:53 +02:00
2015-02-04 16:08:32 +01:00
/ * *
* @ ngdoc directive
* @ name mdTooltip
* @ module material . components . tooltip
2014-09-30 12:29:53 +02:00
* @ description
2015-02-04 16:08:32 +01:00
* Tooltips are used to describe elements that are interactive and primarily graphical ( not textual ) .
*
* Place a ` <md-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" >
* < md - icon icon = "/img/icons/ic_play_arrow_24px.svg" >
* < md - tooltip >
* Play Music
* < / m d - t o o l t i p >
* < / m d - i c o n >
* < / h l j s >
*
* @ param { expression = } md - visible Boolean bound to whether the tooltip is
* currently visible .
* @ param { number = } md - delay How many milliseconds to wait to show the tooltip after the user focuses , hovers , or touches the parent . Defaults to 400 ms .
2014-09-30 12:29:53 +02:00
* /
2015-02-04 16:08:32 +01:00
function MdTooltipDirective ( $timeout , $window , $$rAF , $document , $mdUtil , $mdTheming , $rootElement ) {
var TOOLTIP _SHOW _DELAY = 400 ;
var TOOLTIP _WINDOW _EDGE _SPACE = 8 ;
2014-11-03 17:32:25 +01:00
2014-09-30 12:29:53 +02:00
return {
2015-02-04 16:08:32 +01:00
restrict : 'E' ,
transclude : true ,
template :
'<div class="md-background"></div>' +
'<div class="md-content" ng-transclude></div>' ,
scope : {
visible : '=?mdVisible' ,
delay : '=?mdDelay'
2014-11-03 17:32:25 +01:00
} ,
2015-02-04 16:08:32 +01:00
link : postLink
} ;
2014-11-03 17:32:25 +01:00
2015-02-04 16:08:32 +01:00
function postLink ( scope , element , attr , contentCtrl ) {
$mdTheming ( element ) ;
var parent = element . parent ( ) ;
2014-11-03 17:32:25 +01:00
2015-02-04 16:08:32 +01:00
// Look for the nearest parent md-content, stopping at the rootElement.
var current = element . parent ( ) [ 0 ] ;
while ( current && current !== $rootElement [ 0 ] && current !== document . body ) {
if ( current . tagName && current . tagName . toLowerCase ( ) == 'md-content' ) break ;
current = current . parentNode ;
}
var tooltipParent = angular . element ( current || document . body ) ;
2014-11-03 17:32:25 +01:00
2015-02-04 16:08:32 +01:00
if ( ! angular . isDefined ( attr . mdDelay ) ) {
scope . delay = TOOLTIP _SHOW _DELAY ;
2014-11-03 17:32:25 +01:00
}
2014-09-30 12:29:53 +02:00
2015-02-04 16:08:32 +01:00
// We will re-attach tooltip when visible
element . detach ( ) ;
element . attr ( 'role' , 'tooltip' ) ;
element . attr ( 'id' , attr . id || ( 'tooltip_' + $mdUtil . nextUid ( ) ) ) ;
2014-11-03 17:32:25 +01:00
2015-02-04 16:08:32 +01:00
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 [ 0 ] . activeElement === parent [ 0 ] ) return ;
setVisible ( false ) ;
} ) ;
2014-11-06 16:55:16 +01:00
2015-02-04 16:08:32 +01:00
scope . $watch ( 'visible' , function ( isVisible ) {
if ( isVisible ) showTooltip ( ) ;
else hideTooltip ( ) ;
} ) ;
2014-11-06 16:55:16 +01:00
2015-02-04 16:08:32 +01:00
var debouncedOnResize = $$rAF . throttle ( function windowResize ( ) {
// Reposition on resize
if ( scope . visible ) positionTooltip ( ) ;
} ) ;
angular . element ( $window ) . on ( 'resize' , debouncedOnResize ) ;
2014-11-06 16:55:16 +01:00
2015-02-04 16:08:32 +01:00
// Be sure to completely cleanup the element on destroy
scope . $on ( '$destroy' , function ( ) {
scope . visible = false ;
element . remove ( ) ;
angular . element ( $window ) . off ( 'resize' , debouncedOnResize ) ;
} ) ;
2014-11-06 16:55:16 +01:00
2015-02-04 16:08:32 +01:00
// *******
// Methods
// *******
2014-11-06 16:55:16 +01:00
2015-02-04 16:08:32 +01:00
// If setting visible to true, debounce to scope.delay ms
// If setting visible to false and no timeout is active, instantly hide the tooltip.
function setVisible ( value ) {
setVisible . value = ! ! value ;
2014-11-06 16:55:16 +01:00
2015-02-04 16:08:32 +01:00
if ( ! setVisible . queued ) {
if ( value ) {
setVisible . queued = true ;
$timeout ( function ( ) {
scope . visible = setVisible . value ;
setVisible . queued = false ;
} , scope . delay ) ;
} else {
$timeout ( function ( ) { scope . visible = false ; } ) ;
}
2014-11-06 16:55:16 +01:00
}
2015-02-04 16:08:32 +01:00
}
2014-11-06 16:55:16 +01:00
2015-02-04 16:08:32 +01:00
function showTooltip ( ) {
// Insert the element before positioning it, so we can get position
// (tooltip is hidden by default)
element . removeClass ( 'md-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 ( 'md-show' ) ;
} ) ;
} ) ;
}
function hideTooltip ( ) {
element . removeClass ( 'md-show' ) . addClass ( 'md-hide' ) ;
parent . removeAttr ( 'aria-describedby' ) ;
$timeout ( function ( ) {
if ( scope . visible ) return ;
element . detach ( ) ;
} , 200 , false ) ;
}
function positionTooltip ( ) {
var tipRect = $mdUtil . elementRect ( element , tooltipParent ) ;
var parentRect = $mdUtil . elementRect ( parent , tooltipParent ) ;
// 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 ,
tooltipParent . prop ( 'scrollWidth' ) - tipRect . width - TOOLTIP _WINDOW _EDGE _SPACE
) ;
newPosition . left = Math . max ( newPosition . left , TOOLTIP _WINDOW _EDGE _SPACE ) ;
2014-11-06 16:55:16 +01:00
2015-02-04 16:08:32 +01:00
// If element bleeds over the bottom of the window, place it above the parent.
if ( newPosition . top + tipRect . height > tooltipParent . prop ( 'scrollHeight' ) ) {
newPosition . top = parentRect . top - tipRect . height ;
tipDirection = 'top' ;
2014-11-06 16:55:16 +01:00
}
2015-02-04 16:08:32 +01:00
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 ( 'md-direction' , tipDirection ) ;
2014-11-06 16:55:16 +01:00
}
2015-02-04 16:08:32 +01:00
2014-11-06 16:55:16 +01:00
}
}
2015-02-04 16:08:32 +01:00
MdTooltipDirective . $inject = [ "$timeout" , "$window" , "$$rAF" , "$document" , "$mdUtil" , "$mdTheming" , "$rootElement" ] ;
} ) ( ) ;
2014-11-06 16:55:16 +01:00
2015-02-04 16:08:32 +01:00
/ * !
* Angular Material Design
* https : //github.com/angular/material
* @ license MIT
* v0 . 7.1
* /
( function ( ) {
'use strict' ;
/ * *
* @ ngdoc module
* @ name material . components . whiteframe
* /
angular . module ( 'material.components.whiteframe' , [ ] ) ;
2014-11-06 16:55:16 +01:00
} ) ( ) ;
2015-02-04 16:08:32 +01:00
/ * !
* Angular Material Design
* https : //github.com/angular/material
* @ license MIT
* v0 . 7.1
* /
2014-11-03 17:32:25 +01:00
( function ( ) {
2015-02-04 16:08:32 +01:00
'use strict' ;
2014-09-30 12:29:53 +02:00
/ * *
2014-11-03 17:32:25 +01:00
* Conditionally configure ink bar animations when the
2015-02-04 16:08:32 +01:00
* tab selection changes . If ` mdNoBar ` then do not show the
2014-11-03 17:32:25 +01:00
* bar nor animate .
2014-09-30 12:29:53 +02:00
* /
2014-11-03 17:32:25 +01:00
angular . module ( 'material.components.tabs' )
2015-02-04 16:08:32 +01:00
. directive ( 'mdTabsInkBar' , MdTabInkDirective ) ;
2014-11-03 17:32:25 +01:00
2015-02-04 16:08:32 +01:00
function MdTabInkDirective ( $$rAF ) {
2014-09-30 12:29:53 +02:00
2015-02-04 16:08:32 +01:00
var lastIndex = 0 ;
2014-10-12 19:07:47 +02:00
2014-09-30 12:29:53 +02:00
return {
restrict : 'E' ,
2015-02-04 16:08:32 +01:00
require : [ '^?mdNoBar' , '^mdTabs' ] ,
2014-11-03 17:32:25 +01:00
link : postLink
2014-10-12 19:07:47 +02:00
} ;
2014-11-03 17:32:25 +01:00
function postLink ( scope , element , attr , ctrls ) {
2015-02-04 16:08:32 +01:00
var mdNoBar = ! ! ctrls [ 0 ] ;
2014-10-12 19:07:47 +02:00
2015-02-04 16:08:32 +01:00
var tabsCtrl = ctrls [ 1 ] ,
debouncedUpdateBar = $$rAF . throttle ( updateBar ) ;
2014-10-12 19:07:47 +02:00
2015-02-04 16:08:32 +01:00
tabsCtrl . inkBarElement = element ;
2014-10-12 19:07:47 +02:00
2014-11-03 17:32:25 +01:00
scope . $on ( '$mdTabsPaginationChanged' , debouncedUpdateBar ) ;
2014-10-12 19:07:47 +02:00
2015-02-04 16:08:32 +01:00
function updateBar ( ) {
var selected = tabsCtrl . getSelectedItem ( ) ;
var hideInkBar = ! selected || tabsCtrl . count ( ) < 2 || mdNoBar ;
2014-10-12 19:07:47 +02:00
2015-02-04 16:08:32 +01:00
element . css ( 'display' , hideInkBar ? 'none' : 'block' ) ;
2014-10-12 19:07:47 +02:00
2015-02-04 16:08:32 +01:00
if ( hideInkBar ) return ;
2014-10-12 19:07:47 +02:00
2015-02-04 16:08:32 +01:00
if ( scope . pagination && scope . pagination . tabData ) {
var index = tabsCtrl . getSelectedIndex ( ) ;
var data = scope . pagination . tabData . tabs [ index ] || { left : 0 , right : 0 , width : 0 } ;
var right = element . parent ( ) . prop ( 'offsetWidth' ) - data . right ;
var classNames = [ 'md-transition-left' , 'md-transition-right' , 'md-no-transition' ] ;
var classIndex = lastIndex > index ? 0 : lastIndex < index ? 1 : 2 ;
2014-10-12 19:07:47 +02:00
2015-02-04 16:08:32 +01:00
element
. removeClass ( classNames . join ( ' ' ) )
. addClass ( classNames [ classIndex ] )
. css ( { left : data . left + 'px' , right : right + 'px' } ) ;
lastIndex = index ;
2014-11-03 17:32:25 +01:00
}
}
2014-10-12 19:07:47 +02:00
}
2014-11-03 17:32:25 +01:00
}
2015-02-04 16:08:32 +01:00
MdTabInkDirective . $inject = [ "$$rAF" ] ;
2014-10-12 19:07:47 +02:00
} ) ( ) ;
2015-02-04 16:08:32 +01:00
/ * !
* Angular Material Design
* https : //github.com/angular/material
* @ license MIT
* v0 . 7.1
* /
2014-11-03 17:32:25 +01:00
( function ( ) {
2015-02-04 16:08:32 +01:00
'use strict' ;
2014-10-12 19:07:47 +02:00
2014-11-03 17:32:25 +01:00
angular . module ( 'material.components.tabs' )
2015-02-04 16:08:32 +01:00
. directive ( 'mdTabsPagination' , TabPaginationDirective ) ;
2014-10-12 19:07:47 +02:00
2015-02-04 16:08:32 +01:00
function TabPaginationDirective ( $mdConstant , $window , $$rAF , $$q , $timeout , $mdMedia ) {
2014-11-03 17:32:25 +01:00
// Must match (2 * width of paginators) in scss
var PAGINATORS _WIDTH = ( 8 * 4 ) * 2 ;
2014-10-12 19:07:47 +02:00
return {
2014-11-03 17:32:25 +01:00
restrict : 'A' ,
require : '^mdTabs' ,
link : postLink
2014-10-12 19:07:47 +02:00
} ;
2014-11-03 17:32:25 +01:00
function postLink ( scope , element , attr , tabsCtrl ) {
2014-10-12 19:07:47 +02:00
2015-02-04 16:08:32 +01:00
var tabs = element [ 0 ] . getElementsByTagName ( 'md-tab' ) ;
var debouncedUpdatePagination = $$rAF . throttle ( updatePagination ) ;
2014-11-03 17:32:25 +01:00
var tabsParent = element . children ( ) ;
2015-02-04 16:08:32 +01:00
var locked = false ;
2014-11-03 17:32:25 +01:00
var state = scope . pagination = {
page : - 1 ,
active : false ,
2015-02-04 16:08:32 +01:00
clickNext : function ( ) { locked || userChangePage ( + 1 ) ; } ,
clickPrevious : function ( ) { locked || userChangePage ( - 1 ) ; }
2014-11-03 17:32:25 +01:00
} ;
2014-10-12 19:07:47 +02:00
2014-11-03 17:32:25 +01:00
scope . $on ( '$mdTabsChanged' , debouncedUpdatePagination ) ;
angular . element ( $window ) . on ( 'resize' , debouncedUpdatePagination ) ;
2014-10-12 19:07:47 +02:00
2014-11-03 17:32:25 +01:00
scope . $on ( '$destroy' , function ( ) {
angular . element ( $window ) . off ( 'resize' , debouncedUpdatePagination ) ;
} ) ;
2014-10-12 19:07:47 +02:00
2015-02-04 16:08:32 +01:00
scope . $watch ( function ( ) { return tabsCtrl . tabToFocus ; } , onTabFocus ) ;
2014-10-12 19:07:47 +02:00
2015-02-04 16:08:32 +01:00
// Make sure we don't focus an element on the next page
// before it's in view
function onTabFocus ( tab , oldTab ) {
if ( ! tab ) return ;
2014-10-12 19:07:47 +02:00
2014-11-03 17:32:25 +01:00
var pageIndex = getPageForTab ( tab ) ;
2015-02-04 16:08:32 +01:00
if ( ! state . active || pageIndex === state . page ) {
tab . element . focus ( ) ;
} else {
2014-11-03 17:32:25 +01:00
// Go to the new page, wait for the page transition to end, then focus.
2015-02-04 16:08:32 +01:00
oldTab && oldTab . element . blur ( ) ;
2014-11-03 17:32:25 +01:00
setPage ( pageIndex ) . then ( function ( ) {
2015-02-04 16:08:32 +01:00
locked = false ;
2014-11-03 17:32:25 +01:00
tab . element . focus ( ) ;
} ) ;
2014-10-12 19:07:47 +02:00
}
2014-11-03 17:32:25 +01:00
}
2014-10-12 19:07:47 +02:00
2014-11-03 17:32:25 +01:00
// Called when page is changed by a user action (click)
function userChangePage ( increment ) {
2015-02-04 16:08:32 +01:00
var sizeData = state . tabData ;
var newPage = Math . max ( 0 , Math . min ( sizeData . pages . length - 1 , state . page + increment ) ) ;
var newTabIndex = sizeData . pages [ newPage ] [ increment > 0 ? 'firstTabIndex' : 'lastTabIndex' ] ;
var newTab = tabsCtrl . itemAt ( newTabIndex ) ;
locked = true ;
onTabFocus ( newTab ) ;
2014-10-12 19:07:47 +02:00
}
2014-11-03 17:32:25 +01:00
function updatePagination ( ) {
2015-02-04 16:08:32 +01:00
if ( ! element . prop ( 'offsetParent' ) ) {
var watcher = waitForVisible ( ) ;
return ;
}
2014-11-03 17:32:25 +01:00
var tabs = element . find ( 'md-tab' ) ;
2014-10-12 19:07:47 +02:00
2015-02-04 16:08:32 +01:00
disablePagination ( ) ;
2014-10-12 19:07:47 +02:00
2015-02-04 16:08:32 +01:00
var sizeData = state . tabData = calculateTabData ( ) ;
var needPagination = state . active = sizeData . pages . length > 1 ;
2014-10-12 19:07:47 +02:00
2015-02-04 16:08:32 +01:00
if ( needPagination ) { enablePagination ( ) ; }
2014-10-12 19:07:47 +02:00
2015-02-04 16:08:32 +01:00
scope . $evalAsync ( function ( ) { scope . $broadcast ( '$mdTabsPaginationChanged' ) ; } ) ;
2014-10-12 19:07:47 +02:00
2015-02-04 16:08:32 +01:00
function enablePagination ( ) {
tabsParent . css ( 'width' , '9999px' ) ;
2014-10-12 19:07:47 +02:00
2015-02-04 16:08:32 +01:00
//-- apply filler margins
angular . forEach ( sizeData . tabs , function ( tab ) {
angular . element ( tab . element ) . css ( 'margin-left' , tab . filler + 'px' ) ;
} ) ;
2014-10-12 19:07:47 +02:00
2015-02-04 16:08:32 +01:00
setPage ( getPageForTab ( tabsCtrl . getSelectedItem ( ) ) ) ;
}
2014-10-12 19:07:47 +02:00
2015-02-04 16:08:32 +01:00
function disablePagination ( ) {
slideTabButtons ( 0 ) ;
tabsParent . css ( 'width' , '' ) ;
tabs . css ( 'width' , '' ) ;
tabs . css ( 'margin-left' , '' ) ;
state . page = null ;
state . active = false ;
}
function waitForVisible ( ) {
return watcher || scope . $watch (
function ( ) {
$timeout ( function ( ) {
if ( element [ 0 ] . offsetParent ) {
if ( angular . isFunction ( watcher ) ) {
watcher ( ) ;
}
debouncedUpdatePagination ( ) ;
watcher = null ;
}
} , 0 , false ) ;
}
) ;
2014-11-03 17:32:25 +01:00
}
}
2014-10-12 19:07:47 +02:00
2014-11-03 17:32:25 +01:00
function slideTabButtons ( x ) {
if ( tabsCtrl . pagingOffset === x ) {
// Resolve instantly if no change
return $$q . when ( ) ;
}
2014-10-12 19:07:47 +02:00
2014-11-03 17:32:25 +01:00
var deferred = $$q . defer ( ) ;
2014-10-12 19:07:47 +02:00
2014-11-03 17:32:25 +01:00
tabsCtrl . $$pagingOffset = x ;
2015-02-04 16:08:32 +01:00
tabsParent . css ( $mdConstant . CSS . TRANSFORM , 'translate3d(' + x + 'px,0,0)' ) ;
tabsParent . on ( $mdConstant . CSS . TRANSITIONEND , onTabsParentTransitionEnd ) ;
2014-10-12 19:07:47 +02:00
2014-11-03 17:32:25 +01:00
return deferred . promise ;
2014-10-12 19:07:47 +02:00
2014-11-03 17:32:25 +01:00
function onTabsParentTransitionEnd ( ev ) {
// Make sure this event didn't bubble up from an animation in a child element.
if ( ev . target === tabsParent [ 0 ] ) {
2015-02-04 16:08:32 +01:00
tabsParent . off ( $mdConstant . CSS . TRANSITIONEND , onTabsParentTransitionEnd ) ;
2014-11-03 17:32:25 +01:00
deferred . resolve ( ) ;
}
}
}
2014-10-12 19:07:47 +02:00
2015-02-04 16:08:32 +01:00
function shouldStretchTabs ( ) {
switch ( scope . stretchTabs ) {
case 'never' : return false ;
case 'always' : return true ;
default : return $mdMedia ( 'sm' ) ;
}
}
function calculateTabData ( noAdjust ) {
var clientWidth = element . parent ( ) . prop ( 'offsetWidth' ) ;
var tabsWidth = clientWidth - PAGINATORS _WIDTH - 1 ;
var $tabs = angular . element ( tabs ) ;
var totalWidth = 0 ;
var max = 0 ;
var tabData = [ ] ;
var pages = [ ] ;
var currentPage ;
$tabs . css ( 'max-width' , '' ) ;
angular . forEach ( tabs , function ( tab , index ) {
var tabWidth = Math . min ( tabsWidth , tab . offsetWidth ) ;
var data = {
element : tab ,
left : totalWidth ,
width : tabWidth ,
right : totalWidth + tabWidth ,
filler : 0
} ;
//-- This calculates the page for each tab. The first page will use the clientWidth, which
// does not factor in the pagination items. After the first page, tabsWidth is used
// because at this point, we know that the pagination buttons will be shown.
data . page = Math . ceil ( data . right / ( pages . length === 1 && index === tabs . length - 1 ? clientWidth : tabsWidth ) ) - 1 ;
if ( data . page >= pages . length ) {
data . filler = ( tabsWidth * data . page ) - data . left ;
data . right += data . filler ;
data . left += data . filler ;
currentPage = {
left : data . left ,
firstTabIndex : index ,
lastTabIndex : index ,
tabs : [ data ]
} ;
pages . push ( currentPage ) ;
} else {
currentPage . lastTabIndex = index ;
currentPage . tabs . push ( data ) ;
}
totalWidth = data . right ;
max = Math . max ( max , tabWidth ) ;
tabData . push ( data ) ;
} ) ;
$tabs . css ( 'max-width' , tabsWidth + 'px' ) ;
if ( ! noAdjust && shouldStretchTabs ( ) ) {
return adjustForStretchedTabs ( ) ;
} else {
return {
width : totalWidth ,
max : max ,
tabs : tabData ,
pages : pages ,
tabElements : tabs
} ;
}
function adjustForStretchedTabs ( ) {
var canvasWidth = pages . length === 1 ? clientWidth : tabsWidth ;
var tabsPerPage = Math . min ( Math . floor ( canvasWidth / max ) , tabs . length ) ;
var tabWidth = Math . floor ( canvasWidth / tabsPerPage ) ;
$tabs . css ( 'width' , tabWidth + 'px' ) ;
return calculateTabData ( true ) ;
}
}
2014-11-03 17:32:25 +01:00
function getPageForTab ( tab ) {
var tabIndex = tabsCtrl . indexOf ( tab ) ;
if ( tabIndex === - 1 ) return 0 ;
2014-09-30 12:29:53 +02:00
2015-02-04 16:08:32 +01:00
var sizeData = state . tabData ;
return sizeData ? sizeData . tabs [ tabIndex ] . page : 0 ;
2014-11-03 17:32:25 +01:00
}
2014-09-30 12:29:53 +02:00
2014-11-03 17:32:25 +01:00
function setPage ( page ) {
if ( page === state . page ) return ;
2014-09-30 12:29:53 +02:00
2015-02-04 16:08:32 +01:00
var lastPage = state . tabData . pages . length - 1 ;
2014-10-12 19:07:47 +02:00
2014-11-03 17:32:25 +01:00
if ( page < 0 ) page = 0 ;
if ( page > lastPage ) page = lastPage ;
2014-10-12 19:07:47 +02:00
2014-11-03 17:32:25 +01:00
state . hasPrev = page > 0 ;
2015-02-04 16:08:32 +01:00
state . hasNext = page < lastPage ;
2014-10-12 19:07:47 +02:00
2014-11-03 17:32:25 +01:00
state . page = page ;
2014-10-12 19:07:47 +02:00
2015-02-04 16:08:32 +01:00
scope . $broadcast ( '$mdTabsPaginationChanged' ) ;
2014-11-03 17:32:25 +01:00
2015-02-04 16:08:32 +01:00
return slideTabButtons ( - state . tabData . pages [ page ] . left ) ;
2014-09-30 12:29:53 +02:00
}
2014-11-03 17:32:25 +01:00
}
2014-09-30 12:29:53 +02:00
2014-11-03 17:32:25 +01:00
}
2015-02-04 16:08:32 +01:00
TabPaginationDirective . $inject = [ "$mdConstant" , "$window" , "$$rAF" , "$$q" , "$timeout" , "$mdMedia" ] ;
2014-10-12 19:07:47 +02:00
} ) ( ) ;
2015-02-04 16:08:32 +01:00
/ * !
* Angular Material Design
* https : //github.com/angular/material
* @ license MIT
* v0 . 7.1
* /
2014-11-03 17:32:25 +01:00
( function ( ) {
2015-02-04 16:08:32 +01:00
'use strict' ;
2014-09-30 12:29:53 +02:00
2015-02-04 16:08:32 +01:00
angular . module ( 'material.components.tabs' )
. controller ( '$mdTab' , TabItemController ) ;
2014-09-30 12:29:53 +02:00
2015-02-04 16:08:32 +01:00
function TabItemController ( $scope , $element , $attrs , $compile , $animate , $mdUtil , $parse , $timeout ) {
2014-11-03 17:32:25 +01:00
var self = this ;
2015-02-04 16:08:32 +01:00
var tabsCtrl = $element . controller ( 'mdTabs' ) ;
2014-09-30 12:29:53 +02:00
2014-11-03 17:32:25 +01:00
// Properties
2014-11-06 16:55:16 +01:00
self . contentContainer = angular . element ( '<div class="md-tab-content ng-hide">' ) ;
2015-02-04 16:08:32 +01:00
self . element = $element ;
2014-11-03 17:32:25 +01:00
// Methods
self . isDisabled = isDisabled ;
self . onAdd = onAdd ;
self . onRemove = onRemove ;
self . onSelect = onSelect ;
self . onDeselect = onDeselect ;
2015-02-04 16:08:32 +01:00
var disabledParsed = $parse ( $attrs . ngDisabled ) ;
2014-11-03 17:32:25 +01:00
function isDisabled ( ) {
2015-02-04 16:08:32 +01:00
return disabledParsed ( $scope . $parent ) ;
2014-09-30 12:29:53 +02:00
}
2014-11-03 17:32:25 +01:00
/ * *
* 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
* /
2015-02-04 16:08:32 +01:00
function onAdd ( contentArea , shouldDisconnectScope ) {
2014-11-03 17:32:25 +01:00
if ( self . content . length ) {
self . contentContainer . append ( self . content ) ;
2015-02-04 16:08:32 +01:00
self . contentScope = $scope . $parent . $new ( ) ;
2014-11-03 17:32:25 +01:00
contentArea . append ( self . contentContainer ) ;
2014-09-30 12:29:53 +02:00
2014-11-03 17:32:25 +01:00
$compile ( self . contentContainer ) ( self . contentScope ) ;
2015-02-04 16:08:32 +01:00
if ( shouldDisconnectScope === true ) {
$timeout ( function ( ) {
$mdUtil . disconnectScope ( self . contentScope ) ;
} , 0 , false ) ;
}
2014-11-03 17:32:25 +01:00
}
}
2014-09-30 12:29:53 +02:00
2014-11-03 17:32:25 +01:00
function onRemove ( ) {
$animate . leave ( self . contentContainer ) . then ( function ( ) {
self . contentScope && self . contentScope . $destroy ( ) ;
self . contentScope = null ;
} ) ;
}
2014-09-30 12:29:53 +02:00
2015-02-04 16:08:32 +01:00
function toggleAnimationClass ( rightToLeft ) {
self . contentContainer [ rightToLeft ? 'addClass' : 'removeClass' ] ( 'md-transition-rtl' ) ;
}
function onSelect ( rightToLeft ) {
2014-11-03 17:32:25 +01:00
// Resume watchers and events firing when tab is selected
$mdUtil . reconnectScope ( self . contentScope ) ;
2014-09-30 12:29:53 +02:00
2015-02-04 16:08:32 +01:00
$element
. addClass ( 'active' )
. attr ( {
'aria-selected' : true ,
'tabIndex' : 0
} )
. on ( '$md.swipeleft $md.swiperight' , onSwipe ) ;
toggleAnimationClass ( rightToLeft ) ;
2014-11-03 17:32:25 +01:00
$animate . removeClass ( self . contentContainer , 'ng-hide' ) ;
2015-02-04 16:08:32 +01:00
$scope . onSelect ( ) ;
2014-09-30 12:29:53 +02:00
}
2015-02-04 16:08:32 +01:00
function onDeselect ( rightToLeft ) {
2014-11-03 17:32:25 +01:00
// Stop watchers & events from firing while tab is deselected
$mdUtil . disconnectScope ( self . contentScope ) ;
2014-09-30 12:29:53 +02:00
2015-02-04 16:08:32 +01:00
$element
. removeClass ( 'active' )
. attr ( {
'aria-selected' : false ,
'tabIndex' : - 1
} )
. off ( '$md.swipeleft $md.swiperight' , onSwipe ) ;
toggleAnimationClass ( rightToLeft ) ;
2014-11-03 17:32:25 +01:00
$animate . addClass ( self . contentContainer , 'ng-hide' ) ;
2015-02-04 16:08:32 +01:00
$scope . onDeselect ( ) ;
}
///// Private functions
function onSwipe ( ev ) {
$scope . $apply ( function ( ) {
if ( /left/ . test ( ev . type ) ) {
tabsCtrl . select ( tabsCtrl . next ( ) ) ;
} else {
tabsCtrl . select ( tabsCtrl . previous ( ) ) ;
}
} ) ;
2014-09-30 12:29:53 +02:00
}
2015-02-04 16:08:32 +01:00
2014-11-03 17:32:25 +01:00
2014-09-30 12:29:53 +02:00
}
2015-02-04 16:08:32 +01:00
TabItemController . $inject = [ "$scope" , "$element" , "$attrs" , "$compile" , "$animate" , "$mdUtil" , "$parse" , "$timeout" ] ;
2014-09-30 12:29:53 +02:00
2014-11-03 17:32:25 +01:00
} ) ( ) ;
2015-02-04 16:08:32 +01:00
/ * !
* Angular Material Design
* https : //github.com/angular/material
* @ license MIT
* v0 . 7.1
* /
2014-11-03 17:32:25 +01:00
( function ( ) {
2015-02-04 16:08:32 +01:00
'use strict' ;
2014-11-03 17:32:25 +01:00
2015-02-04 16:08:32 +01:00
angular . module ( 'material.components.tabs' )
. directive ( 'mdTab' , MdTabDirective ) ;
2014-09-30 12:29:53 +02:00
/ * *
2014-11-03 17:32:25 +01:00
* @ ngdoc directive
* @ name mdTab
* @ module material . components . tabs
*
* @ restrict E
*
* @ description
* ` <md-tab> ` is the nested directive used [ within ` <md-tabs> ` ] to specify each tab with a * * label * * and optional * view content * .
*
2015-02-04 16:08:32 +01:00
* If the ` label ` attribute is not specified , then an optional ` <md-tab-label> ` tag can be used to specify more
2014-11-03 17:32:25 +01:00
* complex tab header markup . If neither the * * label * * nor the * * md - tab - label * * are specified , then the nested
* markup of the ` <md-tab> ` is used as the tab header markup .
*
* If a tab * * label * * has been identified , then any * * non - * * ` <md-tab-label> ` markup
2014-11-06 16:55:16 +01:00
* will be considered tab content and will be transcluded to the internal ` <div class="md-tabs-content"> ` container .
2014-11-03 17:32:25 +01:00
*
* 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
2015-02-04 16:08:32 +01:00
* @ param { boolean = } md - active When evaluteing to true , selects the tab .
2014-11-03 17:32:25 +01:00
* @ param { boolean = } disabled If present , disabled tab selection .
2015-02-04 16:08:32 +01:00
* @ param { expression = } md - on - deselect Expression to be evaluated after the tab has been de - selected .
* @ param { expression = } md - on - select Expression to be evaluated after the tab has been selected .
2014-11-03 17:32:25 +01:00
*
*
* @ usage
*
* < hljs lang = "html" >
2015-02-04 16:08:32 +01:00
* < md - tab label = "" disabled = "" md - on - select = "" md - on - deselect = "" >
2014-11-03 17:32:25 +01:00
* < h3 > My Tab content < / h 3 >
* < / m d - t a b >
*
* < md - tab >
* < md - tab - label >
* < h3 > My Tab content < / h 3 >
* < / m d - t a b - l a b e l >
* < 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 >
* < / m d - t a b >
* < / h l j s >
2014-09-30 12:29:53 +02:00
*
* /
2015-02-04 16:08:32 +01:00
function MdTabDirective ( $mdInkRipple , $compile , $mdUtil , $mdConstant , $timeout ) {
2014-11-03 17:32:25 +01:00
return {
restrict : 'E' ,
require : [ 'mdTab' , '^mdTabs' ] ,
controller : '$mdTab' ,
scope : {
2015-02-04 16:08:32 +01:00
onSelect : '&mdOnSelect' ,
onDeselect : '&mdOnDeselect' ,
2014-11-03 17:32:25 +01:00
label : '@'
} ,
compile : compile
} ;
2014-09-30 12:29:53 +02:00
2014-11-03 17:32:25 +01:00
function compile ( element , attr ) {
var tabLabel = element . find ( 'md-tab-label' ) ;
2014-09-30 12:29:53 +02:00
2014-11-03 17:32:25 +01:00
if ( tabLabel . length ) {
// If a tab label element is found, remove it for later re-use.
tabLabel . remove ( ) ;
2014-09-30 12:29:53 +02:00
2014-11-03 17:32:25 +01:00
} else if ( angular . isDefined ( attr . label ) ) {
// Otherwise, try to use attr.label as the label
tabLabel = angular . element ( '<md-tab-label>' ) . html ( attr . label ) ;
2014-09-30 12:29:53 +02:00
2014-11-03 17:32:25 +01:00
} else {
// If nothing is found, use the tab's content as the label
tabLabel = angular . element ( '<md-tab-label>' )
. append ( element . contents ( ) . remove ( ) ) ;
}
2014-09-30 12:29:53 +02:00
2014-11-03 17:32:25 +01:00
// Everything that's left as a child is the tab's content.
var tabContent = element . contents ( ) . remove ( ) ;
2014-09-30 12:29:53 +02:00
2014-11-03 17:32:25 +01:00
return function postLink ( scope , element , attr , ctrls ) {
2014-09-30 12:29:53 +02:00
2014-11-03 17:32:25 +01:00
var tabItemCtrl = ctrls [ 0 ] ; // Controller for THIS tabItemCtrl
var tabsCtrl = ctrls [ 1 ] ; // Controller for ALL tabs
2014-09-30 12:29:53 +02:00
2015-02-04 16:08:32 +01:00
$timeout ( element . addClass . bind ( element , 'md-tab-themed' ) , 0 , false ) ;
scope . $watch (
function ( ) { return attr . label ; } ,
function ( ) { $timeout ( function ( ) { tabsCtrl . scope . $broadcast ( '$mdTabsChanged' ) ; } , 0 , false ) ; }
) ;
2014-11-03 17:32:25 +01:00
transcludeTabContent ( ) ;
2015-02-04 16:08:32 +01:00
configureAria ( ) ;
2014-09-30 12:29:53 +02:00
2015-02-04 16:08:32 +01:00
var detachRippleFn = $mdInkRipple . attachTabBehavior ( scope , element , {
colorElement : tabsCtrl . inkBarElement
} ) ;
2014-11-03 17:32:25 +01:00
tabsCtrl . add ( tabItemCtrl ) ;
scope . $on ( '$destroy' , function ( ) {
detachRippleFn ( ) ;
tabsCtrl . remove ( tabItemCtrl ) ;
} ) ;
2015-02-04 16:08:32 +01:00
element . on ( '$destroy' , function ( ) {
//-- wait for item to be removed from the dom
$timeout ( function ( ) {
tabsCtrl . scope . $broadcast ( '$mdTabsChanged' ) ;
} , 0 , false ) ;
} ) ;
2014-09-30 12:29:53 +02:00
2015-02-04 16:08:32 +01:00
if ( ! angular . isDefined ( attr . ngClick ) ) {
element . on ( 'click' , defaultClickListener ) ;
}
2014-11-03 17:32:25 +01:00
element . on ( 'keydown' , keydownListener ) ;
2014-09-30 12:29:53 +02:00
2015-02-04 16:08:32 +01:00
if ( angular . isNumber ( scope . $parent . $index ) ) {
watchNgRepeatIndex ( ) ;
}
if ( angular . isDefined ( attr . mdActive ) ) {
watchActiveAttribute ( ) ;
}
2014-11-03 17:32:25 +01:00
watchDisabled ( ) ;
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 ( ) ;
2014-09-30 12:29:53 +02:00
}
2014-11-03 17:32:25 +01:00
//defaultClickListener isn't applied if the user provides an ngClick expression.
function defaultClickListener ( ) {
scope . $apply ( function ( ) {
tabsCtrl . select ( tabItemCtrl ) ;
2015-02-04 16:08:32 +01:00
tabsCtrl . focus ( tabItemCtrl ) ;
2014-11-03 17:32:25 +01:00
} ) ;
}
function keydownListener ( ev ) {
2015-02-04 16:08:32 +01:00
if ( ev . keyCode == $mdConstant . KEY _CODE . SPACE || ev . keyCode == $mdConstant . KEY _CODE . ENTER ) {
2014-11-03 17:32:25 +01:00
// Fire the click handler to do normal selection if space is pressed
element . triggerHandler ( 'click' ) ;
ev . preventDefault ( ) ;
2015-02-04 16:08:32 +01:00
} else if ( ev . keyCode === $mdConstant . KEY _CODE . LEFT _ARROW ) {
scope . $evalAsync ( function ( ) {
tabsCtrl . focus ( tabsCtrl . previous ( tabItemCtrl ) ) ;
} ) ;
} else if ( ev . keyCode === $mdConstant . KEY _CODE . RIGHT _ARROW ) {
scope . $evalAsync ( function ( ) {
tabsCtrl . focus ( tabsCtrl . next ( tabItemCtrl ) ) ;
} ) ;
2014-11-03 17:32:25 +01:00
}
}
2014-09-30 12:29:53 +02:00
2014-11-03 17:32:25 +01:00
// 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 ) ;
} ) ;
}
2014-09-30 12:29:53 +02:00
2014-11-03 17:32:25 +01:00
function watchActiveAttribute ( ) {
2015-02-04 16:08:32 +01:00
var unwatch = scope . $parent . $watch ( '!!(' + attr . mdActive + ')' , activeWatchAction ) ;
2014-11-03 17:32:25 +01:00
scope . $on ( '$destroy' , unwatch ) ;
2015-02-04 16:08:32 +01:00
2014-11-03 17:32:25 +01:00
function activeWatchAction ( isActive ) {
2015-02-04 16:08:32 +01:00
var isSelected = tabsCtrl . getSelectedItem ( ) === tabItemCtrl ;
2014-09-30 12:29:53 +02:00
2014-11-03 17:32:25 +01:00
if ( isActive && ! isSelected ) {
tabsCtrl . select ( tabItemCtrl ) ;
} else if ( ! isActive && isSelected ) {
tabsCtrl . deselect ( tabItemCtrl ) ;
}
}
2014-09-30 12:29:53 +02:00
}
2014-11-03 17:32:25 +01:00
function watchDisabled ( ) {
scope . $watch ( tabItemCtrl . isDisabled , disabledWatchAction ) ;
2015-02-04 16:08:32 +01:00
2014-11-03 17:32:25 +01:00
function disabledWatchAction ( isDisabled ) {
element . attr ( 'aria-disabled' , isDisabled ) ;
2014-09-30 12:29:53 +02:00
2014-11-03 17:32:25 +01:00
// Auto select `next` tab when disabled
2015-02-04 16:08:32 +01:00
var isSelected = ( tabsCtrl . getSelectedItem ( ) === tabItemCtrl ) ;
2014-11-03 17:32:25 +01:00
if ( isSelected && isDisabled ) {
tabsCtrl . select ( tabsCtrl . next ( ) || tabsCtrl . previous ( ) ) ;
}
2014-09-30 12:29:53 +02:00
2014-11-03 17:32:25 +01:00
}
}
2014-09-30 12:29:53 +02:00
2014-11-03 17:32:25 +01:00
function configureAria ( ) {
// Link together the content area and tabItemCtrl with an id
2015-02-04 16:08:32 +01:00
var tabId = attr . id || ( 'tab_' + $mdUtil . nextUid ( ) ) ;
2014-11-03 17:32:25 +01:00
element . attr ( {
id : tabId ,
2015-02-04 16:08:32 +01:00
role : 'tab' ,
tabIndex : - 1 //this is also set on select/deselect in tabItemCtrl
2014-11-03 17:32:25 +01:00
} ) ;
2014-09-30 12:29:53 +02:00
2015-02-04 16:08:32 +01:00
// Only setup the contentContainer's aria attributes if tab content is provided
if ( tabContent . length ) {
var tabContentId = 'content_' + tabId ;
if ( ! element . attr ( 'aria-controls' ) ) {
element . attr ( 'aria-controls' , tabContentId ) ;
}
tabItemCtrl . contentContainer . attr ( {
id : tabContentId ,
role : 'tabpanel' ,
'aria-labelledby' : tabId
} ) ;
}
2014-11-03 17:32:25 +01:00
}
2014-09-30 12:29:53 +02:00
2014-11-03 17:32:25 +01:00
} ;
}
2014-09-30 12:29:53 +02:00
}
2015-02-04 16:08:32 +01:00
MdTabDirective . $inject = [ "$mdInkRipple" , "$compile" , "$mdUtil" , "$mdConstant" , "$timeout" ] ;
2014-09-30 12:29:53 +02:00
2014-11-03 17:32:25 +01:00
} ) ( ) ;
2014-10-12 19:07:47 +02:00
2015-02-04 16:08:32 +01:00
/ * !
* Angular Material Design
* https : //github.com/angular/material
* @ license MIT
* v0 . 7.1
* /
2014-11-03 17:32:25 +01:00
( function ( ) {
2015-02-04 16:08:32 +01:00
'use strict' ;
2014-11-03 17:32:25 +01:00
2015-02-04 16:08:32 +01:00
angular . module ( 'material.components.tabs' )
. controller ( '$mdTabs' , MdTabsController ) ;
2014-10-12 19:07:47 +02:00
2015-02-04 16:08:32 +01:00
function MdTabsController ( $scope , $element , $mdUtil , $timeout ) {
2014-10-12 19:07:47 +02:00
2014-11-03 17:32:25 +01:00
var tabsList = $mdUtil . iterator ( [ ] , false ) ;
var self = this ;
2014-10-12 19:07:47 +02:00
2014-11-03 17:32:25 +01:00
// Properties
2015-02-04 16:08:32 +01:00
self . $element = $element ;
self . scope = $scope ;
// The section containing the tab content $elements
var contentArea = self . contentArea = angular . element ( $element [ 0 ] . querySelector ( '.md-tabs-content' ) ) ;
2014-10-12 19:07:47 +02:00
2014-11-03 17:32:25 +01:00
// Methods from iterator
2015-02-04 16:08:32 +01:00
var inRange = self . inRange = tabsList . inRange ;
var indexOf = self . indexOf = tabsList . indexOf ;
var itemAt = self . itemAt = tabsList . itemAt ;
2014-11-03 17:32:25 +01:00
self . count = tabsList . count ;
2015-02-04 16:08:32 +01:00
self . getSelectedItem = getSelectedItem ;
self . getSelectedIndex = getSelectedIndex ;
2014-11-03 17:32:25 +01:00
self . add = add ;
self . remove = remove ;
self . move = move ;
self . select = select ;
2015-02-04 16:08:32 +01:00
self . focus = focus ;
2014-11-03 17:32:25 +01:00
self . deselect = deselect ;
2014-10-12 19:07:47 +02:00
2014-11-03 17:32:25 +01:00
self . next = next ;
self . previous = previous ;
2014-10-12 19:07:47 +02:00
2015-02-04 16:08:32 +01:00
$scope . $on ( '$destroy' , function ( ) {
deselect ( getSelectedItem ( ) ) ;
2014-11-03 17:32:25 +01:00
for ( var i = tabsList . count ( ) - 1 ; i >= 0 ; i -- ) {
2015-02-04 16:08:32 +01:00
remove ( tabsList [ i ] , true ) ;
2014-11-03 17:32:25 +01:00
}
} ) ;
2014-10-12 19:07:47 +02:00
2014-11-03 17:32:25 +01:00
// Get the selected tab
2015-02-04 16:08:32 +01:00
function getSelectedItem ( ) {
return itemAt ( $scope . selectedIndex ) ;
}
function getSelectedIndex ( ) {
return $scope . selectedIndex ;
2014-11-03 17:32:25 +01:00
}
2014-10-12 19:07:47 +02:00
2014-11-03 17:32:25 +01:00
// Add a new tab.
// Returns a method to remove the tab from the list.
function add ( tab , index ) {
tabsList . add ( tab , index ) ;
2014-10-12 19:07:47 +02:00
2014-11-03 17:32:25 +01:00
// Select the new tab if we don't have a selectedIndex, or if the
// selectedIndex we've been waiting for is this tab
2015-02-04 16:08:32 +01:00
if ( ! angular . isDefined ( tab . element . attr ( 'md-active' ) ) && ( $scope . selectedIndex === - 1 || ! angular . isNumber ( $scope . selectedIndex ) ||
$scope . selectedIndex === self . indexOf ( tab ) ) ) {
tab . onAdd ( self . contentArea , false ) ;
2014-11-03 17:32:25 +01:00
self . select ( tab ) ;
2015-02-04 16:08:32 +01:00
} else {
tab . onAdd ( self . contentArea , true ) ;
2014-10-12 19:07:47 +02:00
}
2015-02-04 16:08:32 +01:00
$scope . $broadcast ( '$mdTabsChanged' ) ;
2014-11-03 17:32:25 +01:00
}
2014-10-12 19:07:47 +02:00
2014-11-03 17:32:25 +01:00
function remove ( tab , noReselect ) {
if ( ! tabsList . contains ( tab ) ) return ;
2015-02-04 16:08:32 +01:00
if ( noReselect ) return ;
var isSelectedItem = getSelectedItem ( ) === tab ,
newTab = previous ( ) || next ( ) ;
2014-11-03 17:32:25 +01:00
2015-02-04 16:08:32 +01:00
deselect ( tab ) ;
2014-11-03 17:32:25 +01:00
tabsList . remove ( tab ) ;
tab . onRemove ( ) ;
2015-02-04 16:08:32 +01:00
$scope . $broadcast ( '$mdTabsChanged' ) ;
if ( isSelectedItem ) { select ( newTab ) ; }
2014-11-03 17:32:25 +01:00
}
// Move a tab (used when ng-repeat order changes)
function move ( tab , toIndex ) {
2015-02-04 16:08:32 +01:00
var isSelected = getSelectedItem ( ) === tab ;
2014-11-03 17:32:25 +01:00
tabsList . remove ( tab ) ;
tabsList . add ( tab , toIndex ) ;
2015-02-04 16:08:32 +01:00
if ( isSelected ) select ( tab ) ;
2014-11-03 17:32:25 +01:00
2015-02-04 16:08:32 +01:00
$scope . $broadcast ( '$mdTabsChanged' ) ;
2014-11-03 17:32:25 +01:00
}
2015-02-04 16:08:32 +01:00
function select ( tab , rightToLeft ) {
2014-11-03 17:32:25 +01:00
if ( ! tab || tab . isSelected || tab . isDisabled ( ) ) return ;
if ( ! tabsList . contains ( tab ) ) return ;
2015-02-04 16:08:32 +01:00
if ( ! angular . isDefined ( rightToLeft ) ) {
rightToLeft = indexOf ( tab ) < $scope . selectedIndex ;
}
deselect ( getSelectedItem ( ) , rightToLeft ) ;
2014-10-12 19:07:47 +02:00
2015-02-04 16:08:32 +01:00
$scope . selectedIndex = indexOf ( tab ) ;
2014-11-03 17:32:25 +01:00
tab . isSelected = true ;
2015-02-04 16:08:32 +01:00
tab . onSelect ( rightToLeft ) ;
$scope . $broadcast ( '$mdTabsChanged' ) ;
}
function focus ( tab ) {
// this variable is watched by pagination
self . tabToFocus = tab ;
2014-11-03 17:32:25 +01:00
}
2015-02-04 16:08:32 +01:00
function deselect ( tab , rightToLeft ) {
2014-11-03 17:32:25 +01:00
if ( ! tab || ! tab . isSelected ) return ;
if ( ! tabsList . contains ( tab ) ) return ;
2014-10-12 19:07:47 +02:00
2015-02-04 16:08:32 +01:00
$scope . selectedIndex = - 1 ;
2014-11-03 17:32:25 +01:00
tab . isSelected = false ;
2015-02-04 16:08:32 +01:00
tab . onDeselect ( rightToLeft ) ;
2014-11-03 17:32:25 +01:00
}
2014-10-12 19:07:47 +02:00
2014-11-03 17:32:25 +01:00
function next ( tab , filterFn ) {
2015-02-04 16:08:32 +01:00
return tabsList . next ( tab || getSelectedItem ( ) , filterFn || isTabEnabled ) ;
2014-11-03 17:32:25 +01:00
}
function previous ( tab , filterFn ) {
2015-02-04 16:08:32 +01:00
return tabsList . previous ( tab || getSelectedItem ( ) , filterFn || isTabEnabled ) ;
2014-11-03 17:32:25 +01:00
}
2014-10-12 19:07:47 +02:00
2014-11-03 17:32:25 +01:00
function isTabEnabled ( tab ) {
return tab && ! tab . isDisabled ( ) ;
}
2014-10-12 19:07:47 +02:00
}
2015-02-04 16:08:32 +01:00
MdTabsController . $inject = [ "$scope" , "$element" , "$mdUtil" , "$timeout" ] ;
2014-11-03 17:32:25 +01:00
} ) ( ) ;
2014-10-12 19:07:47 +02:00
2015-02-04 16:08:32 +01:00
/ * !
* Angular Material Design
* https : //github.com/angular/material
* @ license MIT
* v0 . 7.1
* /
2014-11-03 17:32:25 +01:00
( function ( ) {
2015-02-04 16:08:32 +01:00
'use strict' ;
2014-11-03 17:32:25 +01:00
angular . module ( 'material.components.tabs' )
2015-02-04 16:08:32 +01:00
. directive ( 'mdTabs' , TabsDirective ) ;
2014-10-12 19:07:47 +02:00
2014-09-30 12:29:53 +02:00
/ * *
2014-11-03 17:32:25 +01:00
* @ ngdoc directive
* @ name mdTabs
* @ module material . components . tabs
2014-09-30 12:29:53 +02:00
*
2014-11-03 17:32:25 +01:00
* @ restrict E
2014-09-30 12:29:53 +02:00
*
* @ description
2014-11-03 17:32:25 +01:00
* The ` <md-tabs> ` directive serves as the container for 1. . n ` <md-tab> ` child directives to produces a Tabs components .
* In turn , the nested ` <md-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" >
* < md - tabs >
* < md - tab label = "Tab #1" > < / m d - t a b >
* < md - tab label = "Tab #2" > < / m d - t a b >
* < md - tab label = "Tab #3" > < / m d - t a b >
* < md - tabs >
* < / h l j s >
*
* 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 * * ` <md-tab> ` * * tags ) will be transcluded into the tab header area BEFORE the tab buttons .
*
2015-02-04 16:08:32 +01:00
* # # # Explanation of tab stretching
*
* Initially , tabs will have an inherent size . This size will either be defined by how much space is needed to accommodate their text or set by the user through CSS . Calculations will be based on this size .
*
* On mobile devices , tabs will be expanded to fill the available horizontal space . When this happens , all tabs will become the same size .
*
* On desktops , by default , stretching will never occur .
*
* This default behavior can be overridden through the ` md-stretch-tabs ` attribute . Here is a table showing when stretching will occur :
*
* ` md-stretch-tabs ` | mobile | desktop
* -- -- -- -- -- -- -- -- -- | -- -- -- -- -- - | -- -- -- --
* ` auto ` | stretched | -- -
* ` always ` | stretched | stretched
* ` never ` | -- - | -- -
*
* @ param { integer = } md - selected Index of the active / selected tab
* @ param { boolean = } md - no - ink If present , disables ink ripple effects .
* @ param { boolean = } md - no - bar If present , disables the selection ink bar .
* @ param { string = } md - align - tabs Attribute to indicate position of tab buttons : ` bottom ` or ` top ` ; default is ` top `
* @ param { string = } md - stretch - tabs Attribute to indicate whether or not to stretch tabs : ` auto ` , ` always ` , or ` never ` ; default is ` auto `
2014-11-03 17:32:25 +01:00
*
* @ usage
* < hljs lang = "html" >
2015-02-04 16:08:32 +01:00
* < md - tabs md - selected = "selectedIndex" >
* < img ng - src = "img/angular.png" class = "centered" >
2014-11-03 17:32:25 +01:00
*
* < md - tab
* ng - repeat = "tab in tabs | orderBy:predicate:reversed"
2015-02-04 16:08:32 +01:00
* md - on - select = "onTabSelected(tab)"
* md - on - deselect = "announceDeselected(tab)"
2014-11-03 17:32:25 +01:00
* disabled = "tab.disabled" >
*
* < md - tab - label >
* { { tab . title } }
2015-02-04 16:08:32 +01:00
* < img src = "img/removeTab.png"
2014-11-03 17:32:25 +01:00
* ng - click = "removeTab(tab)"
* class = "delete" >
* < / m d - t a b - l a b e l >
*
* { { tab . content } }
*
* < / m d - t a b >
*
* < / m d - t a b s >
* < / h l j s >
*
2014-09-30 12:29:53 +02:00
* /
2015-02-04 16:08:32 +01:00
function TabsDirective ( $mdTheming ) {
2014-09-30 12:29:53 +02:00
return {
2014-11-03 17:32:25 +01:00
restrict : 'E' ,
controller : '$mdTabs' ,
require : 'mdTabs' ,
transclude : true ,
scope : {
2015-02-04 16:08:32 +01:00
selectedIndex : '=?mdSelected'
2014-09-30 12:29:53 +02:00
} ,
2014-11-06 16:55:16 +01:00
template :
'<section class="md-header" ' +
'ng-class="{\'md-paginating\': pagination.active}">' +
2014-09-30 12:29:53 +02:00
2015-02-04 16:08:32 +01:00
'<button class="md-paginator md-prev" ' +
2014-11-03 17:32:25 +01:00
'ng-if="pagination.active && pagination.hasPrev" ' +
2015-02-04 16:08:32 +01:00
'ng-click="pagination.clickPrevious()" ' +
'aria-hidden="true">' +
'</button>' +
2014-09-30 12:29:53 +02:00
2014-11-03 17:32:25 +01:00
// overflow: hidden container when paginating
2014-11-06 16:55:16 +01:00
'<div class="md-header-items-container" md-tabs-pagination>' +
2014-11-03 17:32:25 +01:00
// flex container for <md-tab> elements
2015-02-04 16:08:32 +01:00
'<div class="md-header-items">' +
'<md-tabs-ink-bar></md-tabs-ink-bar>' +
'</div>' +
2014-11-03 17:32:25 +01:00
'</div>' +
2015-02-04 16:08:32 +01:00
'<button class="md-paginator md-next" ' +
2014-11-03 17:32:25 +01:00
'ng-if="pagination.active && pagination.hasNext" ' +
2015-02-04 16:08:32 +01:00
'ng-click="pagination.clickNext()" ' +
'aria-hidden="true">' +
'</button>' +
2014-11-03 17:32:25 +01:00
'</section>' +
2014-11-06 16:55:16 +01:00
'<section class="md-tabs-content"></section>' ,
2014-11-03 17:32:25 +01:00
link : postLink
} ;
2015-02-04 16:08:32 +01:00
function postLink ( scope , element , attr , tabsCtrl , transclude ) {
scope . stretchTabs = attr . hasOwnProperty ( 'mdStretchTabs' ) ? attr . mdStretchTabs || 'always' : 'auto' ;
2014-11-06 16:55:16 +01:00
$mdTheming ( element ) ;
2014-11-03 17:32:25 +01:00
configureAria ( ) ;
watchSelected ( ) ;
2015-02-04 16:08:32 +01:00
transclude ( scope . $parent , function ( clone ) {
angular . element ( element [ 0 ] . querySelector ( '.md-header-items' ) ) . append ( clone ) ;
} ) ;
2014-11-03 17:32:25 +01:00
function configureAria ( ) {
2015-02-04 16:08:32 +01:00
element . attr ( 'role' , 'tablist' ) ;
2014-11-03 17:32:25 +01:00
}
function watchSelected ( ) {
scope . $watch ( 'selectedIndex' , function watchSelectedIndex ( newIndex , oldIndex ) {
2015-02-04 16:08:32 +01:00
if ( oldIndex == newIndex ) return ;
var rightToLeft = oldIndex > newIndex ;
tabsCtrl . deselect ( tabsCtrl . itemAt ( oldIndex ) , rightToLeft ) ;
2014-11-03 17:32:25 +01:00
if ( tabsCtrl . inRange ( newIndex ) ) {
var newTab = tabsCtrl . itemAt ( newIndex ) ;
2015-02-04 16:08:32 +01:00
while ( newTab && newTab . isDisabled ( ) ) {
newTab = newIndex > oldIndex
? tabsCtrl . next ( newTab )
: tabsCtrl . previous ( newTab ) ;
2014-11-03 17:32:25 +01:00
}
2015-02-04 16:08:32 +01:00
tabsCtrl . select ( newTab , rightToLeft ) ;
2014-09-30 12:29:53 +02:00
}
2014-11-03 17:32:25 +01:00
} ) ;
2014-09-30 12:29:53 +02:00
}
}
}
2015-02-04 16:08:32 +01:00
TabsDirective . $inject = [ "$mdTheming" ] ;
2014-11-03 17:32:25 +01:00
} ) ( ) ;
2015-02-04 16:08:32 +01:00
angular . module ( "material.core" ) . constant ( "$MD_THEME_CSS" , " md - backdrop . md - opaque . md - THEME _NAME - theme { background - color : '{{foreground-4-0.5}}' ; } md - bottom - sheet . md - THEME _NAME - theme { background - color : '{{background-50}}' ; border - top - color : '{{background-300}}' ; } md - bottom - sheet . md - THEME _NAME - theme . md - list md - item { color : '{{foreground-1}}' ; } md - bottom - sheet . md - THEME _NAME - theme . md - subheader { background - color : '{{background-50}}' ; } md - bottom - sheet . md - THEME _NAME - theme . md - subheader { color : '{{foreground-1}}' ; } md - toolbar . md - button . md - THEME _NAME - theme . md - fab { background - color : white ; } . md - button . md - THEME _NAME - theme { border - radius : 3 px ; } . md - button . md - THEME _NAME - theme : not ( [ disabled ] ) : hover , . md - button . md - THEME _NAME - theme : not ( [ disabled ] ) : focus { background - color : '{{background-500-0.2}}' ; } . md - button . md - THEME _NAME - theme . md - primary { color : '{{primary-color}}' ; } . md - button . md - THEME _NAME - theme . md - primary . md - raised , . md - button . md - THEME _NAME - theme . md - primary . md - fab { color : '{{primary-contrast}}' ; background - color : '{{primary-color}}' ; } . md - button . md - THEME _NAME - theme . md - primary . md - raised : not ( [ disabled ] ) : hover , . md - button . md - THEME _NAME - theme . md - primary . md - raised : not ( [ disabled ] ) : focus , . md - button . md - THEME _NAME - theme . md - primary . md - fab : not ( [ disabled ] ) : hover , . md - button . md - THEME _NAME - theme . md - primary . md - fab : not ( [ disabled ] ) : focus { background - color : '{{primary-600}}' ; } . md - button . md - THEME _NAME - theme . md - fab { border - radius : 50 % ; background - color : '{{accent-color}}' ; color : '{{accent-contrast}}' ; } . md - button . md - THEME _NAME - theme . md - fab : not ( [ disabled ] ) : hover , . md - button . md - THEME _NAME - theme . md - fab : not ( [ disabled ] ) : focus { background - color : '{{accent-A700}}' ; } . md - button . md - THEME _NAME - theme . md - raised { color : '{{background-contrast}}' ; background - color : '{{background-50}}' ; } . md - button . md - THEME _NAME - theme . md - raised : not ( [ disabled ] ) : hover , . md - button . md - THEME _NAME - theme . md - raised : not ( [ disabled ] ) : focus { background - color : '{{background-200}}' ; } . md - button . md - THEME _NAME - theme . md - warn { color : '{{warn-color}}' ; } . md - button . md - THEME _NAME - theme . md - warn . md - raised , . md - button . md - THEME _NAME - theme . md - warn . md - fab { color : '{{warn-contrast}}' ; background - color : '{{warn-color}}' ; } . md - button . md - THEME _NAME - theme . md - warn . md - raised : not ( [ disabled ] ) : hover , . md - button . md - THEME _NAME - theme . md - warn . md - raised : not ( [ disabled ] ) : focus , . md - button . md - THEME _NAME - theme . md - warn . md - fab : not ( [ disabled ] ) : hover , . md - button . md - THEME _NAME - theme . md - warn . md - fab : not ( [ disabled ] ) : focus { background - color : '{{warn-700}}' ; } . md - button . md - THEME _NAME - theme . md - accent { color : '{{accent-color}}' ; } . md - button . md - THEME _NAME - theme . md - accent . md - raised , . md - button . md - THEME _NAME - theme . md - accent . md - fab { color : '{{accent-contrast}}' ; background - color : '{{accent-color}}' ; } . md - button . md - THEME _NAME - theme . md - accent . md - raised : not ( [ disabled ] ) : hover , . md - button . md - THEME _NAME - theme . md - accent . md - raised : not ( [ disabled ] ) : focus , . md - button . md - THEME _NAME - theme . md - accent . md - fab : not ( [ disabled ] ) : hover , . md - button . md - THEME _NAME - theme . md - accent . md - fab : not ( [ disabled ] ) : focus { background - color : '{{accent-700}}' ; } . md - button . md - THEME _NAME - theme [ disabled ] , . md - button . md - THEME _NAME - theme . md - raised [ disabled ] , . md - button . md - THEME _NAME - theme . md - fab [ disabled ] { color : '{{foreground-3}}' ; background - color : transparent ; cursor : not - allowed ; } md - card . md - THEME _NAME - theme { border - radius : 2 px ; } md - card . md - THEME _NAME - theme . md - card - image { border - radius : 2 px 2 px 0 0 ; } md - checkbox . md - THEME _NAME - theme . md - ripple { color : '{{accent-600}}' ; } md - checkbox . md - THEME _NAME - theme . md - checked . md - ripple { color : '{{background-600}}' ; } md - checkbox . md - THEME _NAME - theme . md - icon { border - color : '{{foreground-2}}' ; } md - checkbox . md - THEME _NAME - theme . md - checked . md - icon { background - color : '{{accent-color-0.87}}' ; } md - checkbox . md - THEME _NAME - theme . md - checked . md - icon : after { border - color : '{{background-200}}' ; } md - checkbox . md - THEME _NAME - theme : not ( [ disabled ] ) . md - primar