2015-03-13 09:39:35 +01:00
/ *
* angular - ui - bootstrap
* http : //angular-ui.github.io/bootstrap/
2015-05-10 05:43:32 +02:00
* Version : 0.13 . 0 - 2015 - 05 - 02
2015-03-13 09:39:35 +01:00
* License : MIT
* /
2015-05-10 05:43:32 +02:00
angular . module ( "ui.bootstrap" , [ "ui.bootstrap.tpls" , "ui.bootstrap.collapse" , "ui.bootstrap.accordion" , "ui.bootstrap.alert" , "ui.bootstrap.bindHtml" , "ui.bootstrap.buttons" , "ui.bootstrap.carousel" , "ui.bootstrap.dateparser" , "ui.bootstrap.position" , "ui.bootstrap.datepicker" , "ui.bootstrap.dropdown" , "ui.bootstrap.modal" , "ui.bootstrap.pagination" , "ui.bootstrap.tooltip" , "ui.bootstrap.popover" , "ui.bootstrap.progressbar" , "ui.bootstrap.rating" , "ui.bootstrap.tabs" , "ui.bootstrap.timepicker" , "ui.bootstrap.transition" , "ui.bootstrap.typeahead" ] ) ;
angular . module ( "ui.bootstrap.tpls" , [ "template/accordion/accordion-group.html" , "template/accordion/accordion.html" , "template/alert/alert.html" , "template/carousel/carousel.html" , "template/carousel/slide.html" , "template/datepicker/datepicker.html" , "template/datepicker/day.html" , "template/datepicker/month.html" , "template/datepicker/popup.html" , "template/datepicker/year.html" , "template/modal/backdrop.html" , "template/modal/window.html" , "template/pagination/pager.html" , "template/pagination/pagination.html" , "template/tooltip/tooltip-html-popup.html" , "template/tooltip/tooltip-html-unsafe-popup.html" , "template/tooltip/tooltip-popup.html" , "template/tooltip/tooltip-template-popup.html" , "template/popover/popover-template.html" , "template/popover/popover.html" , "template/progressbar/bar.html" , "template/progressbar/progress.html" , "template/progressbar/progressbar.html" , "template/rating/rating.html" , "template/tabs/tab.html" , "template/tabs/tabset.html" , "template/timepicker/timepicker.html" , "template/typeahead/typeahead-match.html" , "template/typeahead/typeahead-popup.html" ] ) ;
angular . module ( 'ui.bootstrap.collapse' , [ ] )
2014-02-21 02:13:31 +01:00
2015-05-10 05:43:32 +02:00
. directive ( 'collapse' , [ '$animate' , function ( $animate ) {
2015-03-13 09:39:35 +01:00
return {
link : function ( scope , element , attrs ) {
function expand ( ) {
2015-05-10 05:43:32 +02:00
element . removeClass ( 'collapse' ) . addClass ( 'collapsing' ) ;
$animate . addClass ( element , 'in' , {
to : { height : element [ 0 ] . scrollHeight + 'px' }
} ) . then ( expandDone ) ;
2015-03-13 09:39:35 +01:00
}
2014-02-21 02:13:31 +01:00
2015-03-13 09:39:35 +01:00
function expandDone ( ) {
element . removeClass ( 'collapsing' ) ;
element . css ( { height : 'auto' } ) ;
2014-02-21 02:13:31 +01:00
}
2015-03-13 09:39:35 +01:00
function collapse ( ) {
2015-05-10 05:43:32 +02:00
element
// IMPORTANT: The height must be set before adding "collapsing" class.
// Otherwise, the browser attempts to animate from height 0 (in
// collapsing class) to the given height here.
. css ( { height : element [ 0 ] . scrollHeight + 'px' } )
// initially all panel collapse have the collapse class, this removal
// prevents the animation from jumping to collapsed state
. removeClass ( 'collapse' )
. addClass ( 'collapsing' ) ;
$animate . removeClass ( element , 'in' , {
to : { height : '0' }
} ) . then ( collapseDone ) ;
2014-02-21 02:13:31 +01:00
}
2015-03-13 09:39:35 +01:00
function collapseDone ( ) {
2015-05-10 05:43:32 +02:00
element . css ( { height : '0' } ) ; // Required so that collapse works when animation is disabled
2015-03-13 09:39:35 +01:00
element . removeClass ( 'collapsing' ) ;
element . addClass ( 'collapse' ) ;
2014-02-21 02:13:31 +01:00
}
2014-05-31 11:53:44 +02:00
2015-03-13 09:39:35 +01:00
scope . $watch ( attrs . collapse , function ( shouldCollapse ) {
if ( shouldCollapse ) {
collapse ( ) ;
} else {
expand ( ) ;
}
} ) ;
}
} ;
} ] ) ;
2014-02-21 02:13:31 +01:00
angular . module ( 'ui.bootstrap.accordion' , [ 'ui.bootstrap.collapse' ] )
. constant ( 'accordionConfig' , {
closeOthers : true
} )
. controller ( 'AccordionController' , [ '$scope' , '$attrs' , 'accordionConfig' , function ( $scope , $attrs , accordionConfig ) {
// This array keeps track of the accordion groups
this . groups = [ ] ;
// Ensure that all the groups in this accordion are closed, unless close-others explicitly says not to
this . closeOthers = function ( openGroup ) {
var closeOthers = angular . isDefined ( $attrs . closeOthers ) ? $scope . $eval ( $attrs . closeOthers ) : accordionConfig . closeOthers ;
if ( closeOthers ) {
angular . forEach ( this . groups , function ( group ) {
if ( group !== openGroup ) {
group . isOpen = false ;
}
} ) ;
}
} ;
2014-05-31 11:53:44 +02:00
2014-02-21 02:13:31 +01:00
// This is called from the accordion-group directive to add itself to the accordion
this . addGroup = function ( groupScope ) {
var that = this ;
this . groups . push ( groupScope ) ;
groupScope . $on ( '$destroy' , function ( event ) {
that . removeGroup ( groupScope ) ;
} ) ;
} ;
// This is called from the accordion-group directive when to remove itself
this . removeGroup = function ( group ) {
var index = this . groups . indexOf ( group ) ;
if ( index !== - 1 ) {
2015-03-13 09:39:35 +01:00
this . groups . splice ( index , 1 ) ;
2014-02-21 02:13:31 +01:00
}
} ;
} ] )
// The accordion directive simply sets up the directive controller
// and adds an accordion CSS class to itself element.
. directive ( 'accordion' , function ( ) {
return {
restrict : 'EA' ,
controller : 'AccordionController' ,
transclude : true ,
replace : false ,
templateUrl : 'template/accordion/accordion.html'
} ;
} )
// The accordion-group directive indicates a block of html that will expand and collapse in an accordion
2015-03-13 09:39:35 +01:00
. directive ( 'accordionGroup' , function ( ) {
2014-02-21 02:13:31 +01:00
return {
require : '^accordion' , // We need this directive to be inside an accordion
restrict : 'EA' ,
transclude : true , // It transcludes the contents of the directive into the template
replace : true , // The element containing the directive will be replaced with the template
templateUrl : 'template/accordion/accordion-group.html' ,
2015-03-13 09:39:35 +01:00
scope : {
heading : '@' , // Interpolate the heading attribute onto this scope
isOpen : '=?' ,
isDisabled : '=?'
} ,
2014-02-21 02:13:31 +01:00
controller : function ( ) {
this . setHeading = function ( element ) {
this . heading = element ;
} ;
} ,
link : function ( scope , element , attrs , accordionCtrl ) {
accordionCtrl . addGroup ( scope ) ;
scope . $watch ( 'isOpen' , function ( value ) {
if ( value ) {
accordionCtrl . closeOthers ( scope ) ;
}
} ) ;
2015-03-13 09:39:35 +01:00
scope . toggleOpen = function ( ) {
if ( ! scope . isDisabled ) {
scope . isOpen = ! scope . isOpen ;
}
} ;
2014-02-21 02:13:31 +01:00
}
} ;
2015-03-13 09:39:35 +01:00
} )
2014-02-21 02:13:31 +01:00
// Use accordion-heading below an accordion-group to provide a heading containing HTML
// <accordion-group>
// <accordion-heading>Heading containing HTML - <img src="..."></accordion-heading>
// </accordion-group>
. directive ( 'accordionHeading' , function ( ) {
return {
restrict : 'EA' ,
transclude : true , // Grab the contents to be used as the heading
template : '' , // In effect remove this element!
replace : true ,
require : '^accordionGroup' ,
2015-03-13 09:39:35 +01:00
link : function ( scope , element , attr , accordionGroupCtrl , transclude ) {
// Pass the heading to the accordion-group controller
// so that it can be transcluded into the right place in the template
// [The second parameter to transclude causes the elements to be cloned so that they work in ng-repeat]
2015-05-10 05:43:32 +02:00
accordionGroupCtrl . setHeading ( transclude ( scope , angular . noop ) ) ;
2014-02-21 02:13:31 +01:00
}
} ;
} )
// Use in the accordion-group template to indicate where you want the heading to be transcluded
// You must provide the property on the accordion-group controller that will hold the transcluded element
// <div class="accordion-group">
// <div class="accordion-heading" ><a ... accordion-transclude="heading">...</a></div>
// ...
// </div>
. directive ( 'accordionTransclude' , function ( ) {
return {
require : '^accordionGroup' ,
link : function ( scope , element , attr , controller ) {
scope . $watch ( function ( ) { return controller [ attr . accordionTransclude ] ; } , function ( heading ) {
if ( heading ) {
element . html ( '' ) ;
element . append ( heading ) ;
}
} ) ;
}
} ;
2015-05-10 05:43:32 +02:00
} )
;
2014-02-21 02:13:31 +01:00
2015-03-13 09:39:35 +01:00
angular . module ( 'ui.bootstrap.alert' , [ ] )
2014-02-21 02:13:31 +01:00
. controller ( 'AlertController' , [ '$scope' , '$attrs' , function ( $scope , $attrs ) {
$scope . closeable = 'close' in $attrs ;
2015-03-13 09:39:35 +01:00
this . close = $scope . close ;
2014-02-21 02:13:31 +01:00
} ] )
. directive ( 'alert' , function ( ) {
2013-01-28 15:18:21 +01:00
return {
restrict : 'EA' ,
2014-02-21 02:13:31 +01:00
controller : 'AlertController' ,
2013-01-28 15:18:21 +01:00
templateUrl : 'template/alert/alert.html' ,
transclude : true ,
2014-02-21 02:13:31 +01:00
replace : true ,
scope : {
2015-03-13 09:39:35 +01:00
type : '@' ,
2014-02-21 02:13:31 +01:00
close : '&'
}
} ;
2015-03-13 09:39:35 +01:00
} )
. directive ( 'dismissOnTimeout' , [ '$timeout' , function ( $timeout ) {
return {
require : 'alert' ,
link : function ( scope , element , attrs , alertCtrl ) {
$timeout ( function ( ) {
alertCtrl . close ( ) ;
} , parseInt ( attrs . dismissOnTimeout , 10 ) ) ;
}
} ;
} ] ) ;
2014-02-21 02:13:31 +01:00
angular . module ( 'ui.bootstrap.bindHtml' , [ ] )
. directive ( 'bindHtmlUnsafe' , function ( ) {
return function ( scope , element , attr ) {
element . addClass ( 'ng-binding' ) . data ( '$binding' , attr . bindHtmlUnsafe ) ;
scope . $watch ( attr . bindHtmlUnsafe , function bindHtmlUnsafeWatchAction ( value ) {
element . html ( value || '' ) ;
} ) ;
} ;
} ) ;
angular . module ( 'ui.bootstrap.buttons' , [ ] )
. constant ( 'buttonConfig' , {
activeClass : 'active' ,
toggleEvent : 'click'
} )
. controller ( 'ButtonsController' , [ 'buttonConfig' , function ( buttonConfig ) {
this . activeClass = buttonConfig . activeClass || 'active' ;
this . toggleEvent = buttonConfig . toggleEvent || 'click' ;
} ] )
. directive ( 'btnRadio' , function ( ) {
return {
require : [ 'btnRadio' , 'ngModel' ] ,
controller : 'ButtonsController' ,
link : function ( scope , element , attrs , ctrls ) {
var buttonsCtrl = ctrls [ 0 ] , ngModelCtrl = ctrls [ 1 ] ;
//model -> UI
ngModelCtrl . $render = function ( ) {
element . toggleClass ( buttonsCtrl . activeClass , angular . equals ( ngModelCtrl . $modelValue , scope . $eval ( attrs . btnRadio ) ) ) ;
} ;
//ui->model
element . bind ( buttonsCtrl . toggleEvent , function ( ) {
2015-03-13 09:39:35 +01:00
var isActive = element . hasClass ( buttonsCtrl . activeClass ) ;
if ( ! isActive || angular . isDefined ( attrs . uncheckable ) ) {
2014-02-21 02:13:31 +01:00
scope . $apply ( function ( ) {
2015-03-13 09:39:35 +01:00
ngModelCtrl . $setViewValue ( isActive ? null : scope . $eval ( attrs . btnRadio ) ) ;
2014-02-21 02:13:31 +01:00
ngModelCtrl . $render ( ) ;
} ) ;
}
} ) ;
}
} ;
} )
. directive ( 'btnCheckbox' , function ( ) {
return {
require : [ 'btnCheckbox' , 'ngModel' ] ,
controller : 'ButtonsController' ,
link : function ( scope , element , attrs , ctrls ) {
var buttonsCtrl = ctrls [ 0 ] , ngModelCtrl = ctrls [ 1 ] ;
function getTrueValue ( ) {
return getCheckboxValue ( attrs . btnCheckboxTrue , true ) ;
}
function getFalseValue ( ) {
return getCheckboxValue ( attrs . btnCheckboxFalse , false ) ;
}
2014-05-31 11:53:44 +02:00
2014-02-21 02:13:31 +01:00
function getCheckboxValue ( attributeValue , defaultValue ) {
var val = scope . $eval ( attributeValue ) ;
return angular . isDefined ( val ) ? val : defaultValue ;
}
//model -> UI
ngModelCtrl . $render = function ( ) {
element . toggleClass ( buttonsCtrl . activeClass , angular . equals ( ngModelCtrl . $modelValue , getTrueValue ( ) ) ) ;
2013-01-28 15:18:21 +01:00
} ;
2014-02-21 02:13:31 +01:00
//ui->model
element . bind ( buttonsCtrl . toggleEvent , function ( ) {
scope . $apply ( function ( ) {
ngModelCtrl . $setViewValue ( element . hasClass ( buttonsCtrl . activeClass ) ? getFalseValue ( ) : getTrueValue ( ) ) ;
ngModelCtrl . $render ( ) ;
} ) ;
} ) ;
2013-01-28 15:18:21 +01:00
}
} ;
} ) ;
2014-02-21 02:13:31 +01:00
/ * *
* @ ngdoc overview
* @ name ui . bootstrap . carousel
2013-01-28 15:18:21 +01:00
*
2014-02-21 02:13:31 +01:00
* @ description
* AngularJS version of an image carousel .
2013-01-28 15:18:21 +01:00
*
* /
2015-05-10 05:43:32 +02:00
angular . module ( 'ui.bootstrap.carousel' , [ ] )
. controller ( 'CarouselController' , [ '$scope' , '$interval' , '$animate' , function ( $scope , $interval , $animate ) {
2013-01-28 15:18:21 +01:00
var self = this ,
2015-03-13 09:39:35 +01:00
slides = self . slides = $scope . slides = [ ] ,
2013-01-28 15:18:21 +01:00
currentIndex = - 1 ,
2015-03-13 09:39:35 +01:00
currentInterval , isPlaying ;
2013-01-28 15:18:21 +01:00
self . currentSlide = null ;
2014-02-21 02:13:31 +01:00
var destroyed = false ;
2013-01-28 15:18:21 +01:00
/* direction: "prev" or "next" */
2015-03-13 09:39:35 +01:00
self . select = $scope . select = function ( nextSlide , direction ) {
2015-05-10 05:43:32 +02:00
var nextIndex = self . indexOfSlide ( nextSlide ) ;
2013-01-28 15:18:21 +01:00
//Decide direction if it's not given
if ( direction === undefined ) {
2015-05-10 05:43:32 +02:00
direction = nextIndex > self . getCurrentIndex ( ) ? 'next' : 'prev' ;
2013-01-28 15:18:21 +01:00
}
if ( nextSlide && nextSlide !== self . currentSlide ) {
2015-05-10 05:43:32 +02:00
goNext ( ) ;
2013-01-28 15:18:21 +01:00
}
function goNext ( ) {
2014-02-21 02:13:31 +01:00
// Scope has been destroyed, stop here.
if ( destroyed ) { return ; }
2015-05-10 05:43:32 +02:00
angular . extend ( nextSlide , { direction : direction , active : true } ) ;
angular . extend ( self . currentSlide || { } , { direction : direction , active : false } ) ;
if ( $animate . enabled ( ) && ! $scope . noTransition && nextSlide . $element ) {
$scope . $currentTransition = true ;
nextSlide . $element . one ( '$animate:close' , function closeFn ( ) {
$scope . $currentTransition = null ;
2013-01-28 15:18:21 +01:00
} ) ;
}
2015-05-10 05:43:32 +02:00
2013-01-28 15:18:21 +01:00
self . currentSlide = nextSlide ;
currentIndex = nextIndex ;
//every time you change slides, reset the timer
restartTimer ( ) ;
}
} ;
2014-02-21 02:13:31 +01:00
$scope . $on ( '$destroy' , function ( ) {
destroyed = true ;
} ) ;
2013-01-28 15:18:21 +01:00
2015-05-10 05:43:32 +02:00
function getSlideByIndex ( index ) {
if ( angular . isUndefined ( slides [ index ] . index ) ) {
return slides [ index ] ;
}
var i , len = slides . length ;
for ( i = 0 ; i < slides . length ; ++ i ) {
if ( slides [ i ] . index == index ) {
return slides [ i ] ;
}
}
}
self . getCurrentIndex = function ( ) {
if ( self . currentSlide && angular . isDefined ( self . currentSlide . index ) ) {
return + self . currentSlide . index ;
}
return currentIndex ;
} ;
2013-01-28 15:18:21 +01:00
/* Allow outside people to call indexOf on slides array */
self . indexOfSlide = function ( slide ) {
2015-05-10 05:43:32 +02:00
return angular . isDefined ( slide . index ) ? + slide . index : slides . indexOf ( slide ) ;
2013-01-28 15:18:21 +01:00
} ;
$scope . next = function ( ) {
2015-05-10 05:43:32 +02:00
var newIndex = ( self . getCurrentIndex ( ) + 1 ) % slides . length ;
2014-02-21 02:13:31 +01:00
//Prevent this user-triggered transition from occurring if there is already one in progress
if ( ! $scope . $currentTransition ) {
2015-05-10 05:43:32 +02:00
return self . select ( getSlideByIndex ( newIndex ) , 'next' ) ;
2014-02-21 02:13:31 +01:00
}
2013-01-28 15:18:21 +01:00
} ;
$scope . prev = function ( ) {
2015-05-10 05:43:32 +02:00
var newIndex = self . getCurrentIndex ( ) - 1 < 0 ? slides . length - 1 : self . getCurrentIndex ( ) - 1 ;
2014-02-21 02:13:31 +01:00
//Prevent this user-triggered transition from occurring if there is already one in progress
if ( ! $scope . $currentTransition ) {
2015-05-10 05:43:32 +02:00
return self . select ( getSlideByIndex ( newIndex ) , 'prev' ) ;
2014-02-21 02:13:31 +01:00
}
} ;
$scope . isActive = function ( slide ) {
return self . currentSlide === slide ;
} ;
2013-01-28 15:18:21 +01:00
$scope . $watch ( 'interval' , restartTimer ) ;
2014-02-21 02:13:31 +01:00
$scope . $on ( '$destroy' , resetTimer ) ;
2013-01-28 15:18:21 +01:00
function restartTimer ( ) {
2014-02-21 02:13:31 +01:00
resetTimer ( ) ;
var interval = + $scope . interval ;
2015-03-13 09:39:35 +01:00
if ( ! isNaN ( interval ) && interval > 0 ) {
currentInterval = $interval ( timerFn , interval ) ;
2014-02-21 02:13:31 +01:00
}
}
function resetTimer ( ) {
2015-03-13 09:39:35 +01:00
if ( currentInterval ) {
$interval . cancel ( currentInterval ) ;
currentInterval = null ;
2013-01-28 15:18:21 +01:00
}
2014-02-21 02:13:31 +01:00
}
function timerFn ( ) {
2015-03-13 09:39:35 +01:00
var interval = + $scope . interval ;
if ( isPlaying && ! isNaN ( interval ) && interval > 0 ) {
2014-02-21 02:13:31 +01:00
$scope . next ( ) ;
} else {
$scope . pause ( ) ;
2013-01-28 15:18:21 +01:00
}
}
2014-02-21 02:13:31 +01:00
2013-01-28 15:18:21 +01:00
$scope . play = function ( ) {
if ( ! isPlaying ) {
isPlaying = true ;
restartTimer ( ) ;
}
} ;
$scope . pause = function ( ) {
2014-02-21 02:13:31 +01:00
if ( ! $scope . noPause ) {
isPlaying = false ;
resetTimer ( ) ;
2013-01-28 15:18:21 +01:00
}
} ;
self . addSlide = function ( slide , element ) {
slide . $element = element ;
slides . push ( slide ) ;
//if this is the first slide or the slide is set to active, select it
if ( slides . length === 1 || slide . active ) {
self . select ( slides [ slides . length - 1 ] ) ;
if ( slides . length == 1 ) {
$scope . play ( ) ;
}
} else {
slide . active = false ;
}
} ;
self . removeSlide = function ( slide ) {
2015-05-10 05:43:32 +02:00
if ( angular . isDefined ( slide . index ) ) {
slides . sort ( function ( a , b ) {
return + a . index > + b . index ;
} ) ;
}
2013-01-28 15:18:21 +01:00
//get the index of the slide inside the carousel
var index = slides . indexOf ( slide ) ;
slides . splice ( index , 1 ) ;
if ( slides . length > 0 && slide . active ) {
if ( index >= slides . length ) {
self . select ( slides [ index - 1 ] ) ;
} else {
self . select ( slides [ index ] ) ;
}
2014-02-21 02:13:31 +01:00
} else if ( currentIndex > index ) {
currentIndex -- ;
2013-01-28 15:18:21 +01:00
}
} ;
2014-02-21 02:13:31 +01:00
2013-01-28 15:18:21 +01:00
} ] )
2014-02-21 02:13:31 +01:00
/ * *
* @ ngdoc directive
* @ name ui . bootstrap . carousel . directive : carousel
* @ restrict EA
*
* @ description
* Carousel is the outer container for a set of image 'slides' to showcase .
*
* @ param { number = } interval The time , in milliseconds , that it will take the carousel to go to the next slide .
* @ param { boolean = } noTransition Whether to disable transitions on the carousel .
* @ param { boolean = } noPause Whether to disable pausing on the carousel ( by default , the carousel interval pauses on hover ) .
*
* @ example
< example module = "ui.bootstrap" >
< file name = "index.html" >
< carousel >
< slide >
< img src = "http://placekitten.com/150/150" style = "margin:auto;" >
< div class = "carousel-caption" >
< p > Beautiful ! < / p >
< / d i v >
< / s l i d e >
< slide >
< img src = "http://placekitten.com/100/150" style = "margin:auto;" >
< div class = "carousel-caption" >
< p > D ' aww ! < / p >
< / d i v >
< / s l i d e >
< / c a r o u s e l >
< / f i l e >
< file name = "demo.css" >
. carousel - indicators {
top : auto ;
bottom : 15 px ;
}
< / f i l e >
< / e x a m p l e >
* /
2013-01-28 15:18:21 +01:00
. directive ( 'carousel' , [ function ( ) {
return {
restrict : 'EA' ,
transclude : true ,
replace : true ,
controller : 'CarouselController' ,
require : 'carousel' ,
templateUrl : 'template/carousel/carousel.html' ,
scope : {
interval : '=' ,
2014-02-21 02:13:31 +01:00
noTransition : '=' ,
noPause : '='
2013-01-28 15:18:21 +01:00
}
} ;
} ] )
2014-02-21 02:13:31 +01:00
/ * *
* @ ngdoc directive
* @ name ui . bootstrap . carousel . directive : slide
* @ restrict EA
*
* @ description
* Creates a slide inside a { @ link ui . bootstrap . carousel . directive : carousel carousel } . Must be placed as a child of a carousel element .
*
* @ param { boolean = } active Model binding , whether or not this slide is currently active .
2015-05-10 05:43:32 +02:00
* @ param { number = } index The index of the slide . The slides will be sorted by this parameter .
2014-02-21 02:13:31 +01:00
*
* @ example
< example module = "ui.bootstrap" >
< file name = "index.html" >
< div ng - controller = "CarouselDemoCtrl" >
< carousel >
2015-05-10 05:43:32 +02:00
< slide ng - repeat = "slide in slides" active = "slide.active" index = "$index" >
2014-02-21 02:13:31 +01:00
< img ng - src = "{{slide.image}}" style = "margin:auto;" >
< div class = "carousel-caption" >
< h4 > Slide { { $index } } < / h 4 >
< p > { { slide . text } } < / p >
< / d i v >
< / s l i d e >
< / c a r o u s e l >
2015-03-13 09:39:35 +01:00
Interval , in milliseconds : < input type = "number" ng - model = "myInterval" >
< br / > Enter a negative number to stop the interval .
2014-02-21 02:13:31 +01:00
< / d i v >
< / f i l e >
< file name = "script.js" >
function CarouselDemoCtrl ( $scope ) {
$scope . myInterval = 5000 ;
}
< / f i l e >
< file name = "demo.css" >
. carousel - indicators {
top : auto ;
bottom : 15 px ;
}
< / f i l e >
< / e x a m p l e >
* /
2015-03-13 09:39:35 +01:00
. directive ( 'slide' , function ( ) {
2014-02-21 02:13:31 +01:00
return {
require : '^carousel' ,
restrict : 'EA' ,
transclude : true ,
2013-01-28 15:18:21 +01:00
replace : true ,
templateUrl : 'template/carousel/slide.html' ,
scope : {
2015-05-10 05:43:32 +02:00
active : '=?' ,
index : '=?'
2013-01-28 15:18:21 +01:00
} ,
link : function ( scope , element , attrs , carouselCtrl ) {
carouselCtrl . addSlide ( scope , element ) ;
//when the scope is destroyed then remove the slide from the current slides array
scope . $on ( '$destroy' , function ( ) {
carouselCtrl . removeSlide ( scope ) ;
} ) ;
scope . $watch ( 'active' , function ( active ) {
if ( active ) {
carouselCtrl . select ( scope ) ;
}
} ) ;
}
} ;
2015-05-10 05:43:32 +02:00
} )
. animation ( '.item' , [
'$animate' ,
function ( $animate ) {
return {
beforeAddClass : function ( element , className , done ) {
// Due to transclusion, noTransition property is on parent's scope
if ( className == 'active' && element . parent ( ) &&
! element . parent ( ) . scope ( ) . noTransition ) {
var stopped = false ;
var direction = element . isolateScope ( ) . direction ;
var directionClass = direction == 'next' ? 'left' : 'right' ;
element . addClass ( direction ) ;
$animate . addClass ( element , directionClass ) . then ( function ( ) {
if ( ! stopped ) {
element . removeClass ( directionClass + ' ' + direction ) ;
}
done ( ) ;
} ) ;
return function ( ) {
stopped = true ;
} ;
}
done ( ) ;
} ,
beforeRemoveClass : function ( element , className , done ) {
// Due to transclusion, noTransition property is on parent's scope
if ( className == 'active' && element . parent ( ) &&
! element . parent ( ) . scope ( ) . noTransition ) {
var stopped = false ;
var direction = element . isolateScope ( ) . direction ;
var directionClass = direction == 'next' ? 'left' : 'right' ;
$animate . addClass ( element , directionClass ) . then ( function ( ) {
if ( ! stopped ) {
element . removeClass ( directionClass ) ;
}
done ( ) ;
} ) ;
return function ( ) {
stopped = true ;
} ;
}
done ( ) ;
}
} ;
} ] )
;
2015-03-13 09:39:35 +01:00
angular . module ( 'ui.bootstrap.dateparser' , [ ] )
. service ( 'dateParser' , [ '$locale' , 'orderByFilter' , function ( $locale , orderByFilter ) {
2015-05-10 05:43:32 +02:00
// Pulled from https://github.com/mbostock/d3/blob/master/src/format/requote.js
var SPECIAL _CHARACTERS _REGEXP = /[\\\^\$\*\+\?\|\[\]\(\)\.\{\}]/g ;
2015-03-13 09:39:35 +01:00
this . parsers = { } ;
var formatCodeToRegex = {
'yyyy' : {
regex : '\\d{4}' ,
apply : function ( value ) { this . year = + value ; }
} ,
'yy' : {
regex : '\\d{2}' ,
apply : function ( value ) { this . year = + value + 2000 ; }
} ,
'y' : {
regex : '\\d{1,4}' ,
apply : function ( value ) { this . year = + value ; }
} ,
'MMMM' : {
regex : $locale . DATETIME _FORMATS . MONTH . join ( '|' ) ,
apply : function ( value ) { this . month = $locale . DATETIME _FORMATS . MONTH . indexOf ( value ) ; }
} ,
'MMM' : {
regex : $locale . DATETIME _FORMATS . SHORTMONTH . join ( '|' ) ,
apply : function ( value ) { this . month = $locale . DATETIME _FORMATS . SHORTMONTH . indexOf ( value ) ; }
} ,
'MM' : {
regex : '0[1-9]|1[0-2]' ,
apply : function ( value ) { this . month = value - 1 ; }
} ,
'M' : {
regex : '[1-9]|1[0-2]' ,
apply : function ( value ) { this . month = value - 1 ; }
} ,
'dd' : {
regex : '[0-2][0-9]{1}|3[0-1]{1}' ,
apply : function ( value ) { this . date = + value ; }
} ,
'd' : {
regex : '[1-2]?[0-9]{1}|3[0-1]{1}' ,
apply : function ( value ) { this . date = + value ; }
} ,
'EEEE' : {
regex : $locale . DATETIME _FORMATS . DAY . join ( '|' )
} ,
'EEE' : {
regex : $locale . DATETIME _FORMATS . SHORTDAY . join ( '|' )
2015-05-10 05:43:32 +02:00
} ,
'HH' : {
regex : '(?:0|1)[0-9]|2[0-3]' ,
apply : function ( value ) { this . hours = + value ; }
} ,
'H' : {
regex : '1?[0-9]|2[0-3]' ,
apply : function ( value ) { this . hours = + value ; }
} ,
'mm' : {
regex : '[0-5][0-9]' ,
apply : function ( value ) { this . minutes = + value ; }
} ,
'm' : {
regex : '[0-9]|[1-5][0-9]' ,
apply : function ( value ) { this . minutes = + value ; }
} ,
'sss' : {
regex : '[0-9][0-9][0-9]' ,
apply : function ( value ) { this . milliseconds = + value ; }
} ,
'ss' : {
regex : '[0-5][0-9]' ,
apply : function ( value ) { this . seconds = + value ; }
} ,
's' : {
regex : '[0-9]|[1-5][0-9]' ,
apply : function ( value ) { this . seconds = + value ; }
2015-03-13 09:39:35 +01:00
}
} ;
function createParser ( format ) {
var map = [ ] , regex = format . split ( '' ) ;
angular . forEach ( formatCodeToRegex , function ( data , code ) {
var index = format . indexOf ( code ) ;
if ( index > - 1 ) {
format = format . split ( '' ) ;
regex [ index ] = '(' + data . regex + ')' ;
format [ index ] = '$' ; // Custom symbol to define consumed part of format
for ( var i = index + 1 , n = index + code . length ; i < n ; i ++ ) {
regex [ i ] = '' ;
format [ i ] = '$' ;
}
format = format . join ( '' ) ;
map . push ( { index : index , apply : data . apply } ) ;
}
} ) ;
return {
regex : new RegExp ( '^' + regex . join ( '' ) + '$' ) ,
map : orderByFilter ( map , 'index' )
} ;
}
2015-05-10 05:43:32 +02:00
this . parse = function ( input , format , baseDate ) {
2015-03-13 09:39:35 +01:00
if ( ! angular . isString ( input ) || ! format ) {
return input ;
}
format = $locale . DATETIME _FORMATS [ format ] || format ;
2015-05-10 05:43:32 +02:00
format = format . replace ( SPECIAL _CHARACTERS _REGEXP , '\\$&' ) ;
2015-03-13 09:39:35 +01:00
if ( ! this . parsers [ format ] ) {
this . parsers [ format ] = createParser ( format ) ;
}
var parser = this . parsers [ format ] ,
regex = parser . regex ,
map = parser . map ,
results = input . match ( regex ) ;
if ( results && results . length ) {
2015-05-10 05:43:32 +02:00
var fields , dt ;
if ( baseDate ) {
fields = {
year : baseDate . getFullYear ( ) ,
month : baseDate . getMonth ( ) ,
date : baseDate . getDate ( ) ,
hours : baseDate . getHours ( ) ,
minutes : baseDate . getMinutes ( ) ,
seconds : baseDate . getSeconds ( ) ,
milliseconds : baseDate . getMilliseconds ( )
} ;
} else {
fields = { year : 1900 , month : 0 , date : 1 , hours : 0 , minutes : 0 , seconds : 0 , milliseconds : 0 } ;
}
2015-03-13 09:39:35 +01:00
for ( var i = 1 , n = results . length ; i < n ; i ++ ) {
var mapper = map [ i - 1 ] ;
if ( mapper . apply ) {
mapper . apply . call ( fields , results [ i ] ) ;
}
}
if ( isValid ( fields . year , fields . month , fields . date ) ) {
2015-05-10 05:43:32 +02:00
dt = new Date ( fields . year , fields . month , fields . date , fields . hours , fields . minutes , fields . seconds ,
fields . milliseconds || 0 ) ;
2015-03-13 09:39:35 +01:00
}
return dt ;
}
} ;
// Check if date is valid for specific month (and year for February).
// Month: 0 = Jan, 1 = Feb, etc
function isValid ( year , month , date ) {
2015-05-10 05:43:32 +02:00
if ( date < 1 ) {
return false ;
}
2015-03-13 09:39:35 +01:00
if ( month === 1 && date > 28 ) {
return date === 29 && ( ( year % 4 === 0 && year % 100 !== 0 ) || year % 400 === 0 ) ;
}
if ( month === 3 || month === 5 || month === 8 || month === 10 ) {
return date < 31 ;
}
return true ;
}
2013-01-28 15:18:21 +01:00
} ] ) ;
2014-02-21 02:13:31 +01:00
angular . module ( 'ui.bootstrap.position' , [ ] )
2013-01-28 15:18:21 +01:00
2014-02-21 02:13:31 +01:00
/ * *
* A set of utility methods that can be use to retrieve position of DOM elements .
* It is meant to be used where we need to absolute - position DOM elements in
* relation to other , existing elements ( this is the case for tooltips , popovers ,
* typeahead suggestions etc . ) .
* /
. factory ( '$position' , [ '$document' , '$window' , function ( $document , $window ) {
2013-01-28 15:18:21 +01:00
2014-02-21 02:13:31 +01:00
function getStyle ( el , cssprop ) {
if ( el . currentStyle ) { //IE
return el . currentStyle [ cssprop ] ;
} else if ( $window . getComputedStyle ) {
return $window . getComputedStyle ( el ) [ cssprop ] ;
}
// finally try and get inline style
return el . style [ cssprop ] ;
}
2013-01-28 15:18:21 +01:00
2014-02-21 02:13:31 +01:00
/ * *
* Checks if a given element is statically positioned
* @ param element - raw DOM element
* /
function isStaticPositioned ( element ) {
2015-03-13 09:39:35 +01:00
return ( getStyle ( element , 'position' ) || 'static' ) === 'static' ;
2014-02-21 02:13:31 +01:00
}
2013-01-28 15:18:21 +01:00
2014-02-21 02:13:31 +01:00
/ * *
* returns the closest , non - statically positioned parentOffset of a given element
* @ param element
* /
var parentOffsetEl = function ( element ) {
var docDomEl = $document [ 0 ] ;
var offsetParent = element . offsetParent || docDomEl ;
while ( offsetParent && offsetParent !== docDomEl && isStaticPositioned ( offsetParent ) ) {
offsetParent = offsetParent . offsetParent ;
}
return offsetParent || docDomEl ;
} ;
2013-01-28 15:18:21 +01:00
2014-02-21 02:13:31 +01:00
return {
/ * *
* Provides read - only equivalent of jQuery ' s position function :
* http : //api.jquery.com/position/
* /
position : function ( element ) {
var elBCR = this . offset ( element ) ;
var offsetParentBCR = { top : 0 , left : 0 } ;
var offsetParentEl = parentOffsetEl ( element [ 0 ] ) ;
if ( offsetParentEl != $document [ 0 ] ) {
offsetParentBCR = this . offset ( angular . element ( offsetParentEl ) ) ;
offsetParentBCR . top += offsetParentEl . clientTop - offsetParentEl . scrollTop ;
offsetParentBCR . left += offsetParentEl . clientLeft - offsetParentEl . scrollLeft ;
2013-01-28 15:18:21 +01:00
}
2014-02-21 02:13:31 +01:00
var boundingClientRect = element [ 0 ] . getBoundingClientRect ( ) ;
return {
width : boundingClientRect . width || element . prop ( 'offsetWidth' ) ,
height : boundingClientRect . height || element . prop ( 'offsetHeight' ) ,
top : elBCR . top - offsetParentBCR . top ,
left : elBCR . left - offsetParentBCR . left
} ;
} ,
2013-01-28 15:18:21 +01:00
2014-02-21 02:13:31 +01:00
/ * *
* Provides read - only equivalent of jQuery ' s offset function :
* http : //api.jquery.com/offset/
* /
offset : function ( element ) {
var boundingClientRect = element [ 0 ] . getBoundingClientRect ( ) ;
return {
width : boundingClientRect . width || element . prop ( 'offsetWidth' ) ,
height : boundingClientRect . height || element . prop ( 'offsetHeight' ) ,
2015-03-13 09:39:35 +01:00
top : boundingClientRect . top + ( $window . pageYOffset || $document [ 0 ] . documentElement . scrollTop ) ,
left : boundingClientRect . left + ( $window . pageXOffset || $document [ 0 ] . documentElement . scrollLeft )
} ;
} ,
/ * *
* Provides coordinates for the targetEl in relation to hostEl
* /
positionElements : function ( hostEl , targetEl , positionStr , appendToBody ) {
var positionStrParts = positionStr . split ( '-' ) ;
var pos0 = positionStrParts [ 0 ] , pos1 = positionStrParts [ 1 ] || 'center' ;
var hostElPos ,
targetElWidth ,
targetElHeight ,
targetElPos ;
hostElPos = appendToBody ? this . offset ( hostEl ) : this . position ( hostEl ) ;
targetElWidth = targetEl . prop ( 'offsetWidth' ) ;
targetElHeight = targetEl . prop ( 'offsetHeight' ) ;
var shiftWidth = {
center : function ( ) {
return hostElPos . left + hostElPos . width / 2 - targetElWidth / 2 ;
} ,
left : function ( ) {
return hostElPos . left ;
} ,
right : function ( ) {
return hostElPos . left + hostElPos . width ;
}
} ;
var shiftHeight = {
center : function ( ) {
return hostElPos . top + hostElPos . height / 2 - targetElHeight / 2 ;
} ,
top : function ( ) {
return hostElPos . top ;
} ,
bottom : function ( ) {
return hostElPos . top + hostElPos . height ;
}
2014-02-21 02:13:31 +01:00
} ;
2015-03-13 09:39:35 +01:00
switch ( pos0 ) {
case 'right' :
targetElPos = {
top : shiftHeight [ pos1 ] ( ) ,
left : shiftWidth [ pos0 ] ( )
} ;
break ;
case 'left' :
targetElPos = {
top : shiftHeight [ pos1 ] ( ) ,
left : hostElPos . left - targetElWidth
} ;
break ;
case 'bottom' :
targetElPos = {
top : shiftHeight [ pos0 ] ( ) ,
left : shiftWidth [ pos1 ] ( )
} ;
break ;
default :
targetElPos = {
top : hostElPos . top - targetElHeight ,
left : shiftWidth [ pos1 ] ( )
} ;
break ;
}
return targetElPos ;
2014-02-21 02:13:31 +01:00
}
} ;
} ] ) ;
2015-03-13 09:39:35 +01:00
angular . module ( 'ui.bootstrap.datepicker' , [ 'ui.bootstrap.dateparser' , 'ui.bootstrap.position' ] )
2014-02-21 02:13:31 +01:00
. constant ( 'datepickerConfig' , {
2015-03-13 09:39:35 +01:00
formatDay : 'dd' ,
formatMonth : 'MMMM' ,
formatYear : 'yyyy' ,
formatDayHeader : 'EEE' ,
formatDayTitle : 'MMMM yyyy' ,
formatMonthTitle : 'yyyy' ,
datepickerMode : 'day' ,
minMode : 'day' ,
maxMode : 'year' ,
2014-02-21 02:13:31 +01:00
showWeeks : true ,
startingDay : 0 ,
yearRange : 20 ,
minDate : null ,
2015-05-10 05:43:32 +02:00
maxDate : null ,
shortcutPropagation : false
2014-02-21 02:13:31 +01:00
} )
2015-03-13 09:39:35 +01:00
. controller ( 'DatepickerController' , [ '$scope' , '$attrs' , '$parse' , '$interpolate' , '$timeout' , '$log' , 'dateFilter' , 'datepickerConfig' , function ( $scope , $attrs , $parse , $interpolate , $timeout , $log , dateFilter , datepickerConfig ) {
var self = this ,
ngModelCtrl = { $setViewValue : angular . noop } ; // nullModelCtrl;
2014-02-21 02:13:31 +01:00
2015-03-13 09:39:35 +01:00
// Modes chain
this . modes = [ 'day' , 'month' , 'year' ] ;
// Configuration attributes
angular . forEach ( [ 'formatDay' , 'formatMonth' , 'formatYear' , 'formatDayHeader' , 'formatDayTitle' , 'formatMonthTitle' ,
2015-05-10 05:43:32 +02:00
'minMode' , 'maxMode' , 'showWeeks' , 'startingDay' , 'yearRange' , 'shortcutPropagation' ] , function ( key , index ) {
2015-03-13 09:39:35 +01:00
self [ key ] = angular . isDefined ( $attrs [ key ] ) ? ( index < 8 ? $interpolate ( $attrs [ key ] ) ( $scope . $parent ) : $scope . $parent . $eval ( $attrs [ key ] ) ) : datepickerConfig [ key ] ;
} ) ;
// Watchable date attributes
angular . forEach ( [ 'minDate' , 'maxDate' ] , function ( key ) {
if ( $attrs [ key ] ) {
$scope . $parent . $watch ( $parse ( $attrs [ key ] ) , function ( value ) {
self [ key ] = value ? new Date ( value ) : null ;
self . refreshView ( ) ;
} ) ;
} else {
self [ key ] = datepickerConfig [ key ] ? new Date ( datepickerConfig [ key ] ) : null ;
}
} ) ;
$scope . datepickerMode = $scope . datepickerMode || datepickerConfig . datepickerMode ;
2015-05-10 05:43:32 +02:00
$scope . maxMode = self . maxMode ;
2015-03-13 09:39:35 +01:00
$scope . uniqueId = 'datepicker-' + $scope . $id + '-' + Math . floor ( Math . random ( ) * 10000 ) ;
2015-05-10 05:43:32 +02:00
if ( angular . isDefined ( $attrs . initDate ) ) {
this . activeDate = $scope . $parent . $eval ( $attrs . initDate ) || new Date ( ) ;
$scope . $parent . $watch ( $attrs . initDate , function ( initDate ) {
if ( initDate && ( ngModelCtrl . $isEmpty ( ngModelCtrl . $modelValue ) || ngModelCtrl . $invalid ) ) {
self . activeDate = initDate ;
self . refreshView ( ) ;
}
} ) ;
} else {
this . activeDate = new Date ( ) ;
}
2014-02-21 02:13:31 +01:00
2015-03-13 09:39:35 +01:00
$scope . isActive = function ( dateObject ) {
if ( self . compare ( dateObject . date , self . activeDate ) === 0 ) {
$scope . activeDateId = dateObject . uid ;
return true ;
2013-01-28 15:18:21 +01:00
}
2015-03-13 09:39:35 +01:00
return false ;
} ;
2014-02-21 02:13:31 +01:00
2015-03-13 09:39:35 +01:00
this . init = function ( ngModelCtrl _ ) {
ngModelCtrl = ngModelCtrl _ ;
2014-02-21 02:13:31 +01:00
2015-03-13 09:39:35 +01:00
ngModelCtrl . $render = function ( ) {
self . render ( ) ;
} ;
} ;
2014-02-21 02:13:31 +01:00
2015-03-13 09:39:35 +01:00
this . render = function ( ) {
2015-05-10 05:43:32 +02:00
if ( ngModelCtrl . $viewValue ) {
var date = new Date ( ngModelCtrl . $viewValue ) ,
2015-03-13 09:39:35 +01:00
isValid = ! isNaN ( date ) ;
2014-02-21 02:13:31 +01:00
2015-03-13 09:39:35 +01:00
if ( isValid ) {
this . activeDate = date ;
} else {
$log . error ( 'Datepicker directive: "ng-model" value must be a Date object, a number of milliseconds since 01.01.1970 or a string representing an RFC2822 or ISO 8601 date.' ) ;
}
ngModelCtrl . $setValidity ( 'date' , isValid ) ;
2014-02-21 02:13:31 +01:00
}
2015-03-13 09:39:35 +01:00
this . refreshView ( ) ;
} ;
this . refreshView = function ( ) {
if ( this . element ) {
this . _refreshView ( ) ;
2015-05-10 05:43:32 +02:00
var date = ngModelCtrl . $viewValue ? new Date ( ngModelCtrl . $viewValue ) : null ;
2015-03-13 09:39:35 +01:00
ngModelCtrl . $setValidity ( 'date-disabled' , ! date || ( this . element && ! this . isDisabled ( date ) ) ) ;
}
} ;
this . createDateObject = function ( date , format ) {
2015-05-10 05:43:32 +02:00
var model = ngModelCtrl . $viewValue ? new Date ( ngModelCtrl . $viewValue ) : null ;
2015-03-13 09:39:35 +01:00
return {
date : date ,
label : dateFilter ( date , format ) ,
selected : model && this . compare ( date , model ) === 0 ,
disabled : this . isDisabled ( date ) ,
2015-05-10 05:43:32 +02:00
current : this . compare ( date , new Date ( ) ) === 0 ,
customClass : this . customClass ( date )
2015-03-13 09:39:35 +01:00
} ;
} ;
this . isDisabled = function ( date ) {
return ( ( this . minDate && this . compare ( date , this . minDate ) < 0 ) || ( this . maxDate && this . compare ( date , this . maxDate ) > 0 ) || ( $attrs . dateDisabled && $scope . dateDisabled ( { date : date , mode : $scope . datepickerMode } ) ) ) ;
} ;
2015-05-10 05:43:32 +02:00
this . customClass = function ( date ) {
return $scope . customClass ( { date : date , mode : $scope . datepickerMode } ) ;
} ;
2015-03-13 09:39:35 +01:00
// Split array into smaller arrays
this . split = function ( arr , size ) {
var arrays = [ ] ;
while ( arr . length > 0 ) {
arrays . push ( arr . splice ( 0 , size ) ) ;
}
return arrays ;
} ;
$scope . select = function ( date ) {
if ( $scope . datepickerMode === self . minMode ) {
2015-05-10 05:43:32 +02:00
var dt = ngModelCtrl . $viewValue ? new Date ( ngModelCtrl . $viewValue ) : new Date ( 0 , 0 , 0 , 0 , 0 , 0 , 0 ) ;
2015-03-13 09:39:35 +01:00
dt . setFullYear ( date . getFullYear ( ) , date . getMonth ( ) , date . getDate ( ) ) ;
ngModelCtrl . $setViewValue ( dt ) ;
ngModelCtrl . $render ( ) ;
} else {
self . activeDate = date ;
$scope . datepickerMode = self . modes [ self . modes . indexOf ( $scope . datepickerMode ) - 1 ] ;
}
} ;
$scope . move = function ( direction ) {
var year = self . activeDate . getFullYear ( ) + direction * ( self . step . years || 0 ) ,
month = self . activeDate . getMonth ( ) + direction * ( self . step . months || 0 ) ;
self . activeDate . setFullYear ( year , month , 1 ) ;
self . refreshView ( ) ;
} ;
$scope . toggleMode = function ( direction ) {
direction = direction || 1 ;
if ( ( $scope . datepickerMode === self . maxMode && direction === 1 ) || ( $scope . datepickerMode === self . minMode && direction === - 1 ) ) {
return ;
}
$scope . datepickerMode = self . modes [ self . modes . indexOf ( $scope . datepickerMode ) + direction ] ;
} ;
// Key event mapper
$scope . keys = { 13 : 'enter' , 32 : 'space' , 33 : 'pageup' , 34 : 'pagedown' , 35 : 'end' , 36 : 'home' , 37 : 'left' , 38 : 'up' , 39 : 'right' , 40 : 'down' } ;
var focusElement = function ( ) {
$timeout ( function ( ) {
self . element [ 0 ] . focus ( ) ;
} , 0 , false ) ;
} ;
// Listen for focus requests from popup directive
$scope . $on ( 'datepicker.focus' , focusElement ) ;
$scope . keydown = function ( evt ) {
var key = $scope . keys [ evt . which ] ;
2014-02-21 02:13:31 +01:00
2015-03-13 09:39:35 +01:00
if ( ! key || evt . shiftKey || evt . altKey ) {
return ;
}
evt . preventDefault ( ) ;
2015-05-10 05:43:32 +02:00
if ( ! self . shortcutPropagation ) {
evt . stopPropagation ( ) ;
}
2015-03-13 09:39:35 +01:00
if ( key === 'enter' || key === 'space' ) {
if ( self . isDisabled ( self . activeDate ) ) {
return ; // do nothing
}
$scope . select ( self . activeDate ) ;
focusElement ( ) ;
} else if ( evt . ctrlKey && ( key === 'up' || key === 'down' ) ) {
$scope . toggleMode ( key === 'up' ? 1 : - 1 ) ;
focusElement ( ) ;
} else {
self . handleKeyDown ( key , evt ) ;
self . refreshView ( ) ;
}
2013-01-28 15:18:21 +01:00
} ;
2014-02-21 02:13:31 +01:00
} ] )
2013-01-28 15:18:21 +01:00
2015-03-13 09:39:35 +01:00
. directive ( 'datepicker' , function ( ) {
2014-02-21 02:13:31 +01:00
return {
restrict : 'EA' ,
replace : true ,
templateUrl : 'template/datepicker/datepicker.html' ,
scope : {
2015-03-13 09:39:35 +01:00
datepickerMode : '=?' ,
2015-05-10 05:43:32 +02:00
dateDisabled : '&' ,
customClass : '&' ,
shortcutPropagation : '&?'
2014-02-21 02:13:31 +01:00
} ,
require : [ 'datepicker' , '?^ngModel' ] ,
controller : 'DatepickerController' ,
link : function ( scope , element , attrs , ctrls ) {
2015-03-13 09:39:35 +01:00
var datepickerCtrl = ctrls [ 0 ] , ngModelCtrl = ctrls [ 1 ] ;
2013-01-28 15:18:21 +01:00
2015-03-13 09:39:35 +01:00
if ( ngModelCtrl ) {
datepickerCtrl . init ( ngModelCtrl ) ;
2014-02-21 02:13:31 +01:00
}
2015-03-13 09:39:35 +01:00
}
} ;
} )
2013-01-28 15:18:21 +01:00
2015-03-13 09:39:35 +01:00
. directive ( 'daypicker' , [ 'dateFilter' , function ( dateFilter ) {
return {
restrict : 'EA' ,
replace : true ,
templateUrl : 'template/datepicker/day.html' ,
require : '^datepicker' ,
link : function ( scope , element , attrs , ctrl ) {
scope . showWeeks = ctrl . showWeeks ;
2013-01-28 15:18:21 +01:00
2015-03-13 09:39:35 +01:00
ctrl . step = { months : 1 } ;
ctrl . element = element ;
2013-01-28 15:18:21 +01:00
2015-03-13 09:39:35 +01:00
var DAYS _IN _MONTH = [ 31 , 28 , 31 , 30 , 31 , 30 , 31 , 31 , 30 , 31 , 30 , 31 ] ;
function getDaysInMonth ( year , month ) {
return ( ( month === 1 ) && ( year % 4 === 0 ) && ( ( year % 100 !== 0 ) || ( year % 400 === 0 ) ) ) ? 29 : DAYS _IN _MONTH [ month ] ;
2014-02-21 02:13:31 +01:00
}
2015-03-13 09:39:35 +01:00
function getDates ( startDate , n ) {
var dates = new Array ( n ) , current = new Date ( startDate ) , i = 0 ;
current . setHours ( 12 ) ; // Prevent repeated dates because of timezone bug
while ( i < n ) {
dates [ i ++ ] = new Date ( current ) ;
current . setDate ( current . getDate ( ) + 1 ) ;
2013-01-28 15:18:21 +01:00
}
2015-03-13 09:39:35 +01:00
return dates ;
2014-02-21 02:13:31 +01:00
}
2013-01-28 15:18:21 +01:00
2015-03-13 09:39:35 +01:00
ctrl . _refreshView = function ( ) {
var year = ctrl . activeDate . getFullYear ( ) ,
month = ctrl . activeDate . getMonth ( ) ,
firstDayOfMonth = new Date ( year , month , 1 ) ,
difference = ctrl . startingDay - firstDayOfMonth . getDay ( ) ,
numDisplayedFromPreviousMonth = ( difference > 0 ) ? 7 - difference : - difference ,
firstDate = new Date ( firstDayOfMonth ) ;
2014-02-21 02:13:31 +01:00
2015-03-13 09:39:35 +01:00
if ( numDisplayedFromPreviousMonth > 0 ) {
firstDate . setDate ( - numDisplayedFromPreviousMonth + 1 ) ;
2014-02-21 02:13:31 +01:00
}
2013-01-28 15:18:21 +01:00
2015-03-13 09:39:35 +01:00
// 42 is the number of days on a six-month calendar
var days = getDates ( firstDate , 42 ) ;
for ( var i = 0 ; i < 42 ; i ++ ) {
days [ i ] = angular . extend ( ctrl . createDateObject ( days [ i ] , ctrl . formatDay ) , {
secondary : days [ i ] . getMonth ( ) !== month ,
uid : scope . uniqueId + '-' + i
} ) ;
}
2013-01-28 15:18:21 +01:00
2015-03-13 09:39:35 +01:00
scope . labels = new Array ( 7 ) ;
for ( var j = 0 ; j < 7 ; j ++ ) {
scope . labels [ j ] = {
abbr : dateFilter ( days [ j ] . date , ctrl . formatDayHeader ) ,
full : dateFilter ( days [ j ] . date , 'EEEE' )
} ;
}
2013-01-28 15:18:21 +01:00
2015-03-13 09:39:35 +01:00
scope . title = dateFilter ( ctrl . activeDate , ctrl . formatDayTitle ) ;
scope . rows = ctrl . split ( days , 7 ) ;
2013-01-28 15:18:21 +01:00
2015-03-13 09:39:35 +01:00
if ( scope . showWeeks ) {
scope . weekNumbers = [ ] ;
2015-05-10 05:43:32 +02:00
var thursdayIndex = ( 4 + 7 - ctrl . startingDay ) % 7 ,
2015-03-13 09:39:35 +01:00
numWeeks = scope . rows . length ;
2015-05-10 05:43:32 +02:00
for ( var curWeek = 0 ; curWeek < numWeeks ; curWeek ++ ) {
scope . weekNumbers . push (
getISO8601WeekNumber ( scope . rows [ curWeek ] [ thursdayIndex ] . date ) ) ;
}
2013-01-28 15:18:21 +01:00
}
2014-02-21 02:13:31 +01:00
} ;
2015-03-13 09:39:35 +01:00
ctrl . compare = function ( date1 , date2 ) {
return ( new Date ( date1 . getFullYear ( ) , date1 . getMonth ( ) , date1 . getDate ( ) ) - new Date ( date2 . getFullYear ( ) , date2 . getMonth ( ) , date2 . getDate ( ) ) ) ;
2014-02-21 02:13:31 +01:00
} ;
2013-01-28 15:18:21 +01:00
2014-02-21 02:13:31 +01:00
function getISO8601WeekNumber ( date ) {
var checkDate = new Date ( date ) ;
checkDate . setDate ( checkDate . getDate ( ) + 4 - ( checkDate . getDay ( ) || 7 ) ) ; // Thursday
var time = checkDate . getTime ( ) ;
checkDate . setMonth ( 0 ) ; // Compare with Jan 1
checkDate . setDate ( 1 ) ;
return Math . floor ( Math . round ( ( time - checkDate ) / 86400000 ) / 7 ) + 1 ;
}
2015-03-13 09:39:35 +01:00
ctrl . handleKeyDown = function ( key , evt ) {
var date = ctrl . activeDate . getDate ( ) ;
if ( key === 'left' ) {
date = date - 1 ; // up
} else if ( key === 'up' ) {
date = date - 7 ; // down
} else if ( key === 'right' ) {
date = date + 1 ; // down
} else if ( key === 'down' ) {
date = date + 7 ;
} else if ( key === 'pageup' || key === 'pagedown' ) {
var month = ctrl . activeDate . getMonth ( ) + ( key === 'pageup' ? - 1 : 1 ) ;
ctrl . activeDate . setMonth ( month , 1 ) ;
date = Math . min ( getDaysInMonth ( ctrl . activeDate . getFullYear ( ) , ctrl . activeDate . getMonth ( ) ) , date ) ;
} else if ( key === 'home' ) {
date = 1 ;
} else if ( key === 'end' ) {
date = getDaysInMonth ( ctrl . activeDate . getFullYear ( ) , ctrl . activeDate . getMonth ( ) ) ;
}
ctrl . activeDate . setDate ( date ) ;
} ;
ctrl . refreshView ( ) ;
}
} ;
} ] )
. directive ( 'monthpicker' , [ 'dateFilter' , function ( dateFilter ) {
return {
restrict : 'EA' ,
replace : true ,
templateUrl : 'template/datepicker/month.html' ,
require : '^datepicker' ,
link : function ( scope , element , attrs , ctrl ) {
ctrl . step = { years : 1 } ;
ctrl . element = element ;
ctrl . _refreshView = function ( ) {
var months = new Array ( 12 ) ,
year = ctrl . activeDate . getFullYear ( ) ;
for ( var i = 0 ; i < 12 ; i ++ ) {
months [ i ] = angular . extend ( ctrl . createDateObject ( new Date ( year , i , 1 ) , ctrl . formatMonth ) , {
uid : scope . uniqueId + '-' + i
} ) ;
}
scope . title = dateFilter ( ctrl . activeDate , ctrl . formatMonthTitle ) ;
scope . rows = ctrl . split ( months , 3 ) ;
} ;
ctrl . compare = function ( date1 , date2 ) {
return new Date ( date1 . getFullYear ( ) , date1 . getMonth ( ) ) - new Date ( date2 . getFullYear ( ) , date2 . getMonth ( ) ) ;
} ;
ctrl . handleKeyDown = function ( key , evt ) {
var date = ctrl . activeDate . getMonth ( ) ;
if ( key === 'left' ) {
date = date - 1 ; // up
} else if ( key === 'up' ) {
date = date - 3 ; // down
} else if ( key === 'right' ) {
date = date + 1 ; // down
} else if ( key === 'down' ) {
date = date + 3 ;
} else if ( key === 'pageup' || key === 'pagedown' ) {
var year = ctrl . activeDate . getFullYear ( ) + ( key === 'pageup' ? - 1 : 1 ) ;
ctrl . activeDate . setFullYear ( year ) ;
} else if ( key === 'home' ) {
date = 0 ;
} else if ( key === 'end' ) {
date = 11 ;
}
ctrl . activeDate . setMonth ( date ) ;
} ;
ctrl . refreshView ( ) ;
}
} ;
} ] )
. directive ( 'yearpicker' , [ 'dateFilter' , function ( dateFilter ) {
return {
restrict : 'EA' ,
replace : true ,
templateUrl : 'template/datepicker/year.html' ,
require : '^datepicker' ,
link : function ( scope , element , attrs , ctrl ) {
var range = ctrl . yearRange ;
ctrl . step = { years : range } ;
ctrl . element = element ;
function getStartingYear ( year ) {
return parseInt ( ( year - 1 ) / range , 10 ) * range + 1 ;
}
ctrl . _refreshView = function ( ) {
var years = new Array ( range ) ;
for ( var i = 0 , start = getStartingYear ( ctrl . activeDate . getFullYear ( ) ) ; i < range ; i ++ ) {
years [ i ] = angular . extend ( ctrl . createDateObject ( new Date ( start + i , 0 , 1 ) , ctrl . formatYear ) , {
uid : scope . uniqueId + '-' + i
} ) ;
}
scope . title = [ years [ 0 ] . label , years [ range - 1 ] . label ] . join ( ' - ' ) ;
scope . rows = ctrl . split ( years , 5 ) ;
} ;
ctrl . compare = function ( date1 , date2 ) {
return date1 . getFullYear ( ) - date2 . getFullYear ( ) ;
} ;
ctrl . handleKeyDown = function ( key , evt ) {
var date = ctrl . activeDate . getFullYear ( ) ;
if ( key === 'left' ) {
date = date - 1 ; // up
} else if ( key === 'up' ) {
date = date - 5 ; // down
} else if ( key === 'right' ) {
date = date + 1 ; // down
} else if ( key === 'down' ) {
date = date + 5 ;
} else if ( key === 'pageup' || key === 'pagedown' ) {
date += ( key === 'pageup' ? - 1 : 1 ) * ctrl . step . years ;
} else if ( key === 'home' ) {
date = getStartingYear ( ctrl . activeDate . getFullYear ( ) ) ;
} else if ( key === 'end' ) {
date = getStartingYear ( ctrl . activeDate . getFullYear ( ) ) + range - 1 ;
}
ctrl . activeDate . setFullYear ( date ) ;
} ;
ctrl . refreshView ( ) ;
2014-02-21 02:13:31 +01:00
}
} ;
} ] )
2013-01-28 15:18:21 +01:00
2014-02-21 02:13:31 +01:00
. constant ( 'datepickerPopupConfig' , {
2015-03-13 09:39:35 +01:00
datepickerPopup : 'yyyy-MM-dd' ,
2015-05-10 05:43:32 +02:00
html5Types : {
date : 'yyyy-MM-dd' ,
'datetime-local' : 'yyyy-MM-ddTHH:mm:ss.sss' ,
'month' : 'yyyy-MM'
} ,
2014-02-21 02:13:31 +01:00
currentText : 'Today' ,
clearText : 'Clear' ,
closeText : 'Done' ,
closeOnDateSelection : true ,
appendToBody : false ,
showButtonBar : true
} )
2013-01-28 15:18:21 +01:00
2015-03-13 09:39:35 +01:00
. directive ( 'datepickerPopup' , [ '$compile' , '$parse' , '$document' , '$position' , 'dateFilter' , 'dateParser' , 'datepickerPopupConfig' ,
function ( $compile , $parse , $document , $position , dateFilter , dateParser , datepickerPopupConfig ) {
2014-02-21 02:13:31 +01:00
return {
restrict : 'EA' ,
require : 'ngModel' ,
2015-03-13 09:39:35 +01:00
scope : {
isOpen : '=?' ,
currentText : '@' ,
clearText : '@' ,
closeText : '@' ,
2015-05-10 05:43:32 +02:00
dateDisabled : '&' ,
customClass : '&'
2015-03-13 09:39:35 +01:00
} ,
link : function ( scope , element , attrs , ngModel ) {
var dateFormat ,
closeOnDateSelection = angular . isDefined ( attrs . closeOnDateSelection ) ? scope . $parent . $eval ( attrs . closeOnDateSelection ) : datepickerPopupConfig . closeOnDateSelection ,
appendToBody = angular . isDefined ( attrs . datepickerAppendToBody ) ? scope . $parent . $eval ( attrs . datepickerAppendToBody ) : datepickerPopupConfig . appendToBody ;
2014-02-21 02:13:31 +01:00
2015-03-13 09:39:35 +01:00
scope . showButtonBar = angular . isDefined ( attrs . showButtonBar ) ? scope . $parent . $eval ( attrs . showButtonBar ) : datepickerPopupConfig . showButtonBar ;
2013-01-28 15:18:21 +01:00
2015-03-13 09:39:35 +01:00
scope . getText = function ( key ) {
return scope [ key + 'Text' ] || datepickerPopupConfig [ key + 'Text' ] ;
} ;
2013-01-28 15:18:21 +01:00
2015-05-10 05:43:32 +02:00
var isHtml5DateInput = false ;
if ( datepickerPopupConfig . html5Types [ attrs . type ] ) {
dateFormat = datepickerPopupConfig . html5Types [ attrs . type ] ;
isHtml5DateInput = true ;
} else {
dateFormat = attrs . datepickerPopup || datepickerPopupConfig . datepickerPopup ;
attrs . $observe ( 'datepickerPopup' , function ( value , oldValue ) {
var newDateFormat = value || datepickerPopupConfig . datepickerPopup ;
// Invalidate the $modelValue to ensure that formatters re-run
// FIXME: Refactor when PR is merged: https://github.com/angular/angular.js/pull/10764
if ( newDateFormat !== dateFormat ) {
dateFormat = newDateFormat ;
ngModel . $modelValue = null ;
if ( ! dateFormat ) {
throw new Error ( 'datepickerPopup must have a date format specified.' ) ;
}
}
} ) ;
}
if ( ! dateFormat ) {
throw new Error ( 'datepickerPopup must have a date format specified.' ) ;
}
if ( isHtml5DateInput && attrs . datepickerPopup ) {
throw new Error ( 'HTML5 date input types do not support custom formats.' ) ;
}
2013-01-28 15:18:21 +01:00
2015-03-13 09:39:35 +01:00
// popup element used to display calendar
var popupEl = angular . element ( '<div datepicker-popup-wrap><div datepicker></div></div>' ) ;
popupEl . attr ( {
'ng-model' : 'date' ,
'ng-change' : 'dateSelection()'
2014-02-21 02:13:31 +01:00
} ) ;
2013-01-28 15:18:21 +01:00
2015-03-13 09:39:35 +01:00
function cameltoDash ( string ) {
return string . replace ( /([A-Z])/g , function ( $1 ) { return '-' + $1 . toLowerCase ( ) ; } ) ;
2013-01-28 15:18:21 +01:00
}
2015-03-13 09:39:35 +01:00
// datepicker element
var datepickerEl = angular . element ( popupEl . children ( ) [ 0 ] ) ;
2015-05-10 05:43:32 +02:00
if ( isHtml5DateInput ) {
if ( attrs . type == 'month' ) {
datepickerEl . attr ( 'datepicker-mode' , '"month"' ) ;
datepickerEl . attr ( 'min-mode' , 'month' ) ;
}
}
2015-03-13 09:39:35 +01:00
if ( attrs . datepickerOptions ) {
2015-05-10 05:43:32 +02:00
var options = scope . $parent . $eval ( attrs . datepickerOptions ) ;
if ( options . initDate ) {
scope . initDate = options . initDate ;
datepickerEl . attr ( 'init-date' , 'initDate' ) ;
delete options . initDate ;
}
angular . forEach ( options , function ( value , option ) {
2015-03-13 09:39:35 +01:00
datepickerEl . attr ( cameltoDash ( option ) , value ) ;
} ) ;
2013-01-28 15:18:21 +01:00
}
2015-03-13 09:39:35 +01:00
scope . watchData = { } ;
2015-05-10 05:43:32 +02:00
angular . forEach ( [ 'minDate' , 'maxDate' , 'datepickerMode' , 'initDate' , 'shortcutPropagation' ] , function ( key ) {
2015-03-13 09:39:35 +01:00
if ( attrs [ key ] ) {
var getAttribute = $parse ( attrs [ key ] ) ;
scope . $parent . $watch ( getAttribute , function ( value ) {
scope . watchData [ key ] = value ;
2014-02-21 02:13:31 +01:00
} ) ;
2015-03-13 09:39:35 +01:00
datepickerEl . attr ( cameltoDash ( key ) , 'watchData.' + key ) ;
// Propagate changes from datepicker to outside
if ( key === 'datepickerMode' ) {
var setAttribute = getAttribute . assign ;
scope . $watch ( 'watchData.' + key , function ( value , oldvalue ) {
if ( value !== oldvalue ) {
setAttribute ( scope . $parent , value ) ;
}
} ) ;
}
2014-02-21 02:13:31 +01:00
}
} ) ;
2015-03-13 09:39:35 +01:00
if ( attrs . dateDisabled ) {
datepickerEl . attr ( 'date-disabled' , 'dateDisabled({ date: date, mode: mode })' ) ;
2014-02-21 02:13:31 +01:00
}
2013-01-28 15:18:21 +01:00
2015-05-10 05:43:32 +02:00
if ( attrs . showWeeks ) {
datepickerEl . attr ( 'show-weeks' , attrs . showWeeks ) ;
}
if ( attrs . customClass ) {
datepickerEl . attr ( 'custom-class' , 'customClass({ date: date, mode: mode })' ) ;
}
2014-02-21 02:13:31 +01:00
function parseDate ( viewValue ) {
2015-05-10 05:43:32 +02:00
if ( angular . isNumber ( viewValue ) ) {
// presumably timestamp to date object
viewValue = new Date ( viewValue ) ;
}
2014-02-21 02:13:31 +01:00
if ( ! viewValue ) {
return null ;
2015-03-13 09:39:35 +01:00
} else if ( angular . isDate ( viewValue ) && ! isNaN ( viewValue ) ) {
2014-02-21 02:13:31 +01:00
return viewValue ;
} else if ( angular . isString ( viewValue ) ) {
2015-05-10 05:43:32 +02:00
var date = dateParser . parse ( viewValue , dateFormat , scope . date ) || new Date ( viewValue ) ;
2014-02-21 02:13:31 +01:00
if ( isNaN ( date ) ) {
return undefined ;
} else {
return date ;
}
} else {
return undefined ;
}
}
2015-05-10 05:43:32 +02:00
function validator ( modelValue , viewValue ) {
var value = modelValue || viewValue ;
if ( angular . isNumber ( value ) ) {
value = new Date ( value ) ;
}
if ( ! value ) {
return true ;
} else if ( angular . isDate ( value ) && ! isNaN ( value ) ) {
return true ;
} else if ( angular . isString ( value ) ) {
var date = dateParser . parse ( value , dateFormat ) || new Date ( value ) ;
return ! isNaN ( date ) ;
} else {
return false ;
}
}
if ( ! isHtml5DateInput ) {
// Internal API to maintain the correct ng-invalid-[key] class
ngModel . $$parserName = 'date' ;
ngModel . $validators . date = validator ;
ngModel . $parsers . unshift ( parseDate ) ;
ngModel . $formatters . push ( function ( value ) {
scope . date = value ;
return ngModel . $isEmpty ( value ) ? value : dateFilter ( value , dateFormat ) ;
} ) ;
}
else {
ngModel . $formatters . push ( function ( value ) {
scope . date = value ;
return value ;
} ) ;
}
2013-01-28 15:18:21 +01:00
2014-02-21 02:13:31 +01:00
// Inner change
scope . dateSelection = function ( dt ) {
if ( angular . isDefined ( dt ) ) {
scope . date = dt ;
}
2015-05-10 05:43:32 +02:00
var date = scope . date ? dateFilter ( scope . date , dateFormat ) : '' ;
element . val ( date ) ;
ngModel . $setViewValue ( date ) ;
2013-01-28 15:18:21 +01:00
2015-03-13 09:39:35 +01:00
if ( closeOnDateSelection ) {
scope . isOpen = false ;
element [ 0 ] . focus ( ) ;
2014-02-21 02:13:31 +01:00
}
} ;
2013-01-28 15:18:21 +01:00
2015-05-10 05:43:32 +02:00
// Detect changes in the view from the text box
ngModel . $viewChangeListeners . push ( function ( ) {
scope . date = dateParser . parse ( ngModel . $viewValue , dateFormat , scope . date ) || new Date ( ngModel . $viewValue ) ;
2014-02-21 02:13:31 +01:00
} ) ;
2013-01-28 15:18:21 +01:00
2015-03-13 09:39:35 +01:00
var documentClickBind = function ( event ) {
if ( scope . isOpen && event . target !== element [ 0 ] ) {
scope . $apply ( function ( ) {
scope . isOpen = false ;
2014-02-21 02:13:31 +01:00
} ) ;
}
2015-03-13 09:39:35 +01:00
} ;
2013-01-28 15:18:21 +01:00
2015-03-13 09:39:35 +01:00
var keydown = function ( evt , noApply ) {
scope . keydown ( evt ) ;
} ;
element . bind ( 'keydown' , keydown ) ;
scope . keydown = function ( evt ) {
if ( evt . which === 27 ) {
evt . preventDefault ( ) ;
2015-05-10 05:43:32 +02:00
if ( scope . isOpen ) {
evt . stopPropagation ( ) ;
}
2015-03-13 09:39:35 +01:00
scope . close ( ) ;
} else if ( evt . which === 40 && ! scope . isOpen ) {
scope . isOpen = true ;
}
} ;
2013-01-28 15:18:21 +01:00
2014-02-21 02:13:31 +01:00
scope . $watch ( 'isOpen' , function ( value ) {
if ( value ) {
2015-03-13 09:39:35 +01:00
scope . $broadcast ( 'datepicker.focus' ) ;
scope . position = appendToBody ? $position . offset ( element ) : $position . position ( element ) ;
scope . position . top = scope . position . top + element . prop ( 'offsetHeight' ) ;
2014-02-21 02:13:31 +01:00
$document . bind ( 'click' , documentClickBind ) ;
} else {
2015-03-13 09:39:35 +01:00
$document . unbind ( 'click' , documentClickBind ) ;
2014-02-21 02:13:31 +01:00
}
2013-01-28 15:18:21 +01:00
} ) ;
2015-03-13 09:39:35 +01:00
scope . select = function ( date ) {
if ( date === 'today' ) {
var today = new Date ( ) ;
2015-05-10 05:43:32 +02:00
if ( angular . isDate ( scope . date ) ) {
date = new Date ( scope . date ) ;
2015-03-13 09:39:35 +01:00
date . setFullYear ( today . getFullYear ( ) , today . getMonth ( ) , today . getDate ( ) ) ;
} else {
date = new Date ( today . setHours ( 0 , 0 , 0 , 0 ) ) ;
}
}
scope . dateSelection ( date ) ;
2014-02-21 02:13:31 +01:00
} ;
2015-03-13 09:39:35 +01:00
scope . close = function ( ) {
scope . isOpen = false ;
element [ 0 ] . focus ( ) ;
2014-02-21 02:13:31 +01:00
} ;
var $popup = $compile ( popupEl ) ( scope ) ;
2015-03-13 09:39:35 +01:00
// Prevent jQuery cache memory leak (template is now redundant after linking)
popupEl . remove ( ) ;
2014-02-21 02:13:31 +01:00
if ( appendToBody ) {
$document . find ( 'body' ) . append ( $popup ) ;
} else {
element . after ( $popup ) ;
2013-01-28 15:18:21 +01:00
}
2015-03-13 09:39:35 +01:00
scope . $on ( '$destroy' , function ( ) {
$popup . remove ( ) ;
element . unbind ( 'keydown' , keydown ) ;
$document . unbind ( 'click' , documentClickBind ) ;
} ) ;
2014-02-21 02:13:31 +01:00
}
} ;
} ] )
. directive ( 'datepickerPopupWrap' , function ( ) {
return {
restrict : 'EA' ,
replace : true ,
transclude : true ,
templateUrl : 'template/datepicker/popup.html' ,
link : function ( scope , element , attrs ) {
element . bind ( 'click' , function ( event ) {
event . preventDefault ( ) ;
event . stopPropagation ( ) ;
} ) ;
}
} ;
2013-01-28 15:18:21 +01:00
} ) ;
2014-02-21 02:13:31 +01:00
2015-05-10 05:43:32 +02:00
angular . module ( 'ui.bootstrap.dropdown' , [ 'ui.bootstrap.position' ] )
2015-03-13 09:39:35 +01:00
. constant ( 'dropdownConfig' , {
openClass : 'open'
} )
2015-05-10 05:43:32 +02:00
. service ( 'dropdownService' , [ '$document' , '$rootScope' , function ( $document , $rootScope ) {
2015-03-13 09:39:35 +01:00
var openScope = null ;
this . open = function ( dropdownScope ) {
if ( ! openScope ) {
$document . bind ( 'click' , closeDropdown ) ;
$document . bind ( 'keydown' , escapeKeyBind ) ;
}
if ( openScope && openScope !== dropdownScope ) {
openScope . isOpen = false ;
}
openScope = dropdownScope ;
} ;
2013-01-28 15:18:21 +01:00
2015-03-13 09:39:35 +01:00
this . close = function ( dropdownScope ) {
if ( openScope === dropdownScope ) {
openScope = null ;
$document . unbind ( 'click' , closeDropdown ) ;
$document . unbind ( 'keydown' , escapeKeyBind ) ;
}
} ;
var closeDropdown = function ( evt ) {
// This method may still be called during the same mouse event that
// unbound this event handler. So check openScope before proceeding.
if ( ! openScope ) { return ; }
2015-05-10 05:43:32 +02:00
if ( evt && openScope . getAutoClose ( ) === 'disabled' ) { return ; }
2015-03-13 09:39:35 +01:00
var toggleElement = openScope . getToggleElement ( ) ;
if ( evt && toggleElement && toggleElement [ 0 ] . contains ( evt . target ) ) {
return ;
}
2015-05-10 05:43:32 +02:00
var $element = openScope . getElement ( ) ;
if ( evt && openScope . getAutoClose ( ) === 'outsideClick' && $element && $element [ 0 ] . contains ( evt . target ) ) {
return ;
}
openScope . isOpen = false ;
if ( ! $rootScope . $$phase ) {
openScope . $apply ( ) ;
}
2015-03-13 09:39:35 +01:00
} ;
var escapeKeyBind = function ( evt ) {
if ( evt . which === 27 ) {
openScope . focusToggleElement ( ) ;
closeDropdown ( ) ;
}
} ;
} ] )
2015-05-10 05:43:32 +02:00
. controller ( 'DropdownController' , [ '$scope' , '$attrs' , '$parse' , 'dropdownConfig' , 'dropdownService' , '$animate' , '$position' , '$document' , function ( $scope , $attrs , $parse , dropdownConfig , dropdownService , $animate , $position , $document ) {
2015-03-13 09:39:35 +01:00
var self = this ,
scope = $scope . $new ( ) , // create a child scope so we are not polluting original one
openClass = dropdownConfig . openClass ,
getIsOpen ,
setIsOpen = angular . noop ,
2015-05-10 05:43:32 +02:00
toggleInvoker = $attrs . onToggle ? $parse ( $attrs . onToggle ) : angular . noop ,
appendToBody = false ;
2015-03-13 09:39:35 +01:00
this . init = function ( element ) {
self . $element = element ;
if ( $attrs . isOpen ) {
getIsOpen = $parse ( $attrs . isOpen ) ;
setIsOpen = getIsOpen . assign ;
$scope . $watch ( getIsOpen , function ( value ) {
scope . isOpen = ! ! value ;
} ) ;
}
2015-05-10 05:43:32 +02:00
appendToBody = angular . isDefined ( $attrs . dropdownAppendToBody ) ;
if ( appendToBody && self . dropdownMenu ) {
$document . find ( 'body' ) . append ( self . dropdownMenu ) ;
element . on ( '$destroy' , function handleDestroyEvent ( ) {
self . dropdownMenu . remove ( ) ;
} ) ;
}
2015-03-13 09:39:35 +01:00
} ;
this . toggle = function ( open ) {
return scope . isOpen = arguments . length ? ! ! open : ! scope . isOpen ;
} ;
// Allow other directives to watch status
this . isOpen = function ( ) {
return scope . isOpen ;
} ;
scope . getToggleElement = function ( ) {
return self . toggleElement ;
} ;
2015-05-10 05:43:32 +02:00
scope . getAutoClose = function ( ) {
return $attrs . autoClose || 'always' ; //or 'outsideClick' or 'disabled'
} ;
scope . getElement = function ( ) {
return self . $element ;
} ;
2015-03-13 09:39:35 +01:00
scope . focusToggleElement = function ( ) {
if ( self . toggleElement ) {
self . toggleElement [ 0 ] . focus ( ) ;
}
} ;
scope . $watch ( 'isOpen' , function ( isOpen , wasOpen ) {
2015-05-10 05:43:32 +02:00
if ( appendToBody && self . dropdownMenu ) {
var pos = $position . positionElements ( self . $element , self . dropdownMenu , 'bottom-left' , true ) ;
self . dropdownMenu . css ( {
top : pos . top + 'px' ,
left : pos . left + 'px' ,
display : isOpen ? 'block' : 'none'
} ) ;
}
2015-03-13 09:39:35 +01:00
$animate [ isOpen ? 'addClass' : 'removeClass' ] ( self . $element , openClass ) ;
if ( isOpen ) {
scope . focusToggleElement ( ) ;
dropdownService . open ( scope ) ;
} else {
dropdownService . close ( scope ) ;
}
setIsOpen ( $scope , isOpen ) ;
if ( angular . isDefined ( isOpen ) && isOpen !== wasOpen ) {
toggleInvoker ( $scope , { open : ! ! isOpen } ) ;
}
} ) ;
$scope . $on ( '$locationChangeSuccess' , function ( ) {
scope . isOpen = false ;
} ) ;
$scope . $on ( '$destroy' , function ( ) {
scope . $destroy ( ) ;
} ) ;
} ] )
. directive ( 'dropdown' , function ( ) {
2013-01-28 15:18:21 +01:00
return {
2015-03-13 09:39:35 +01:00
controller : 'DropdownController' ,
link : function ( scope , element , attrs , dropdownCtrl ) {
dropdownCtrl . init ( element ) ;
}
} ;
} )
2015-05-10 05:43:32 +02:00
. directive ( 'dropdownMenu' , function ( ) {
return {
restrict : 'AC' ,
require : '?^dropdown' ,
link : function ( scope , element , attrs , dropdownCtrl ) {
if ( ! dropdownCtrl ) {
return ;
}
dropdownCtrl . dropdownMenu = element ;
}
} ;
} )
2015-03-13 09:39:35 +01:00
. directive ( 'dropdownToggle' , function ( ) {
return {
require : '?^dropdown' ,
link : function ( scope , element , attrs , dropdownCtrl ) {
if ( ! dropdownCtrl ) {
return ;
}
2013-01-28 15:18:21 +01:00
2015-03-13 09:39:35 +01:00
dropdownCtrl . toggleElement = element ;
2013-01-28 15:18:21 +01:00
2015-03-13 09:39:35 +01:00
var toggleDropdown = function ( event ) {
2013-01-28 15:18:21 +01:00
event . preventDefault ( ) ;
2015-03-13 09:39:35 +01:00
if ( ! element . hasClass ( 'disabled' ) && ! attrs . disabled ) {
scope . $apply ( function ( ) {
dropdownCtrl . toggle ( ) ;
} ) ;
2013-01-28 15:18:21 +01:00
}
2015-03-13 09:39:35 +01:00
} ;
2013-01-28 15:18:21 +01:00
2015-03-13 09:39:35 +01:00
element . bind ( 'click' , toggleDropdown ) ;
// WAI-ARIA
element . attr ( { 'aria-haspopup' : true , 'aria-expanded' : false } ) ;
scope . $watch ( dropdownCtrl . isOpen , function ( isOpen ) {
element . attr ( 'aria-expanded' , ! ! isOpen ) ;
} ) ;
scope . $on ( '$destroy' , function ( ) {
element . unbind ( 'click' , toggleDropdown ) ;
2013-01-28 15:18:21 +01:00
} ) ;
}
} ;
2015-03-13 09:39:35 +01:00
} ) ;
2013-01-28 15:18:21 +01:00
2015-05-10 05:43:32 +02:00
angular . module ( 'ui.bootstrap.modal' , [ ] )
2013-01-28 15:18:21 +01:00
2014-02-21 02:13:31 +01:00
/ * *
* A helper , internal data structure that acts as a map but also allows getting / removing
* elements in the LIFO order
* /
. factory ( '$$stackedMap' , function ( ) {
return {
createNew : function ( ) {
var stack = [ ] ;
2013-01-28 15:18:21 +01:00
2014-02-21 02:13:31 +01:00
return {
add : function ( key , value ) {
stack . push ( {
key : key ,
value : value
} ) ;
} ,
get : function ( key ) {
for ( var i = 0 ; i < stack . length ; i ++ ) {
if ( key == stack [ i ] . key ) {
return stack [ i ] ;
}
}
} ,
keys : function ( ) {
var keys = [ ] ;
for ( var i = 0 ; i < stack . length ; i ++ ) {
keys . push ( stack [ i ] . key ) ;
}
return keys ;
} ,
top : function ( ) {
return stack [ stack . length - 1 ] ;
} ,
remove : function ( key ) {
var idx = - 1 ;
for ( var i = 0 ; i < stack . length ; i ++ ) {
if ( key == stack [ i ] . key ) {
idx = i ;
break ;
}
}
return stack . splice ( idx , 1 ) [ 0 ] ;
} ,
removeTop : function ( ) {
return stack . splice ( stack . length - 1 , 1 ) [ 0 ] ;
} ,
length : function ( ) {
return stack . length ;
}
} ;
2013-01-28 15:18:21 +01:00
}
2014-02-21 02:13:31 +01:00
} ;
} )
2013-01-28 15:18:21 +01:00
2014-02-21 02:13:31 +01:00
/ * *
* A helper directive for the $modal service . It creates a backdrop element .
* /
2015-03-13 09:39:35 +01:00
. directive ( 'modalBackdrop' , [ '$timeout' , function ( $timeout ) {
2014-02-21 02:13:31 +01:00
return {
restrict : 'EA' ,
replace : true ,
templateUrl : 'template/modal/backdrop.html' ,
2015-05-10 05:43:32 +02:00
compile : function ( tElement , tAttrs ) {
tElement . addClass ( tAttrs . backdropClass ) ;
return linkFn ;
2013-01-28 15:18:21 +01:00
}
2014-02-21 02:13:31 +01:00
} ;
2015-05-10 05:43:32 +02:00
function linkFn ( scope , element , attrs ) {
scope . animate = false ;
//trigger CSS transitions
$timeout ( function ( ) {
scope . animate = true ;
} ) ;
}
2014-02-21 02:13:31 +01:00
} ] )
2013-01-28 15:18:21 +01:00
2015-05-10 05:43:32 +02:00
. directive ( 'modalWindow' , [ '$modalStack' , '$q' , function ( $modalStack , $q ) {
2014-02-21 02:13:31 +01:00
return {
restrict : 'EA' ,
scope : {
2015-03-13 09:39:35 +01:00
index : '@' ,
animate : '='
2014-02-21 02:13:31 +01:00
} ,
replace : true ,
transclude : true ,
2015-03-13 09:39:35 +01:00
templateUrl : function ( tElement , tAttrs ) {
return tAttrs . templateUrl || 'template/modal/window.html' ;
} ,
2014-02-21 02:13:31 +01:00
link : function ( scope , element , attrs ) {
2015-03-13 09:39:35 +01:00
element . addClass ( attrs . windowClass || '' ) ;
scope . size = attrs . size ;
2014-02-21 02:13:31 +01:00
2015-05-10 05:43:32 +02:00
scope . close = function ( evt ) {
var modal = $modalStack . getTop ( ) ;
if ( modal && modal . value . backdrop && modal . value . backdrop != 'static' && ( evt . target === evt . currentTarget ) ) {
evt . preventDefault ( ) ;
evt . stopPropagation ( ) ;
$modalStack . dismiss ( modal . key , 'backdrop click' ) ;
}
} ;
// This property is only added to the scope for the purpose of detecting when this directive is rendered.
// We can detect that by using this property in the template associated with this directive and then use
// {@link Attribute#$observe} on it. For more details please see {@link TableColumnResize}.
scope . $isRendered = true ;
// Deferred object that will be resolved when this modal is render.
var modalRenderDeferObj = $q . defer ( ) ;
// Observe function will be called on next digest cycle after compilation, ensuring that the DOM is ready.
// In order to use this way of finding whether DOM is ready, we need to observe a scope property used in modal's template.
attrs . $observe ( 'modalRender' , function ( value ) {
if ( value == 'true' ) {
modalRenderDeferObj . resolve ( ) ;
}
} ) ;
modalRenderDeferObj . promise . then ( function ( ) {
2014-02-21 02:13:31 +01:00
// trigger CSS transitions
scope . animate = true ;
2015-03-13 09:39:35 +01:00
2015-05-10 05:43:32 +02:00
var inputsWithAutofocus = element [ 0 ] . querySelectorAll ( '[autofocus]' ) ;
2015-03-13 09:39:35 +01:00
/ * *
* Auto - focusing of a freshly - opened modal element causes any child elements
* with the autofocus attribute to lose focus . This is an issue on touch
* based devices which will show and then hide the onscreen keyboard .
* Attempts to refocus the autofocus element via JavaScript will not reopen
* the onscreen keyboard . Fixed by updated the focusing logic to only autofocus
* the modal element if the modal does not contain an autofocus element .
* /
2015-05-10 05:43:32 +02:00
if ( inputsWithAutofocus . length ) {
inputsWithAutofocus [ 0 ] . focus ( ) ;
} else {
2015-03-13 09:39:35 +01:00
element [ 0 ] . focus ( ) ;
}
2015-05-10 05:43:32 +02:00
// Notify {@link $modalStack} that modal is rendered.
2015-03-13 09:39:35 +01:00
var modal = $modalStack . getTop ( ) ;
2015-05-10 05:43:32 +02:00
if ( modal ) {
$modalStack . modalRendered ( modal . key ) ;
2015-03-13 09:39:35 +01:00
}
2015-05-10 05:43:32 +02:00
} ) ;
2013-01-28 15:18:21 +01:00
}
2014-02-21 02:13:31 +01:00
} ;
} ] )
2015-05-10 05:43:32 +02:00
. directive ( 'modalAnimationClass' , [
function ( ) {
return {
compile : function ( tElement , tAttrs ) {
if ( tAttrs . modalAnimation ) {
tElement . addClass ( tAttrs . modalAnimationClass ) ;
}
}
} ;
} ] )
2015-03-13 09:39:35 +01:00
. directive ( 'modalTransclude' , function ( ) {
return {
link : function ( $scope , $element , $attrs , controller , $transclude ) {
$transclude ( $scope . $parent , function ( clone ) {
$element . empty ( ) ;
$element . append ( clone ) ;
} ) ;
}
} ;
} )
2015-05-10 05:43:32 +02:00
. factory ( '$modalStack' , [ '$animate' , '$timeout' , '$document' , '$compile' , '$rootScope' , '$$stackedMap' ,
function ( $animate , $timeout , $document , $compile , $rootScope , $$stackedMap ) {
2014-02-21 02:13:31 +01:00
var OPENED _MODAL _CLASS = 'modal-open' ;
2015-03-13 09:39:35 +01:00
var backdropDomEl , backdropScope ;
2014-02-21 02:13:31 +01:00
var openedWindows = $$stackedMap . createNew ( ) ;
var $modalStack = { } ;
function backdropIndex ( ) {
var topBackdropIndex = - 1 ;
var opened = openedWindows . keys ( ) ;
for ( var i = 0 ; i < opened . length ; i ++ ) {
if ( openedWindows . get ( opened [ i ] ) . value . backdrop ) {
topBackdropIndex = i ;
2013-01-28 15:18:21 +01:00
}
}
2014-02-21 02:13:31 +01:00
return topBackdropIndex ;
2013-01-28 15:18:21 +01:00
}
2014-02-21 02:13:31 +01:00
$rootScope . $watch ( backdropIndex , function ( newBackdropIndex ) {
2015-03-13 09:39:35 +01:00
if ( backdropScope ) {
backdropScope . index = newBackdropIndex ;
}
2013-01-28 15:18:21 +01:00
} ) ;
2014-02-21 02:13:31 +01:00
function removeModalWindow ( modalInstance ) {
2013-01-28 15:18:21 +01:00
2014-02-21 02:13:31 +01:00
var body = $document . find ( 'body' ) . eq ( 0 ) ;
var modalWindow = openedWindows . get ( modalInstance ) . value ;
2013-01-28 15:18:21 +01:00
2014-02-21 02:13:31 +01:00
//clean up the stack
openedWindows . remove ( modalInstance ) ;
//remove window DOM element
2015-05-10 05:43:32 +02:00
removeAfterAnimate ( modalWindow . modalDomEl , modalWindow . modalScope , function ( ) {
2015-03-13 09:39:35 +01:00
body . toggleClass ( OPENED _MODAL _CLASS , openedWindows . length ( ) > 0 ) ;
checkRemoveBackdrop ( ) ;
} ) ;
}
function checkRemoveBackdrop ( ) {
//remove backdrop if no longer needed
if ( backdropDomEl && backdropIndex ( ) == - 1 ) {
var backdropScopeRef = backdropScope ;
2015-05-10 05:43:32 +02:00
removeAfterAnimate ( backdropDomEl , backdropScope , function ( ) {
2015-03-13 09:39:35 +01:00
backdropScopeRef = null ;
} ) ;
backdropDomEl = undefined ;
backdropScope = undefined ;
}
}
2015-05-10 05:43:32 +02:00
function removeAfterAnimate ( domEl , scope , done ) {
2015-03-13 09:39:35 +01:00
// Closing animation
scope . animate = false ;
2015-05-10 05:43:32 +02:00
if ( domEl . attr ( 'modal-animation' ) && $animate . enabled ( ) ) {
2015-03-13 09:39:35 +01:00
// transition out
2015-05-10 05:43:32 +02:00
domEl . one ( '$animate:close' , function closeFn ( ) {
$rootScope . $evalAsync ( afterAnimating ) ;
2015-03-13 09:39:35 +01:00
} ) ;
} else {
// Ensure this call is async
$timeout ( afterAnimating ) ;
2013-01-28 15:18:21 +01:00
}
2014-02-21 02:13:31 +01:00
2015-03-13 09:39:35 +01:00
function afterAnimating ( ) {
if ( afterAnimating . done ) {
return ;
}
afterAnimating . done = true ;
domEl . remove ( ) ;
2015-05-10 05:43:32 +02:00
scope . $destroy ( ) ;
2015-03-13 09:39:35 +01:00
if ( done ) {
done ( ) ;
}
}
2014-02-21 02:13:31 +01:00
}
$document . bind ( 'keydown' , function ( evt ) {
var modal ;
if ( evt . which === 27 ) {
modal = openedWindows . top ( ) ;
if ( modal && modal . value . keyboard ) {
2015-03-13 09:39:35 +01:00
evt . preventDefault ( ) ;
2014-02-21 02:13:31 +01:00
$rootScope . $apply ( function ( ) {
2015-03-13 09:39:35 +01:00
$modalStack . dismiss ( modal . key , 'escape key press' ) ;
2014-02-21 02:13:31 +01:00
} ) ;
}
2013-01-28 15:18:21 +01:00
}
} ) ;
2014-02-21 02:13:31 +01:00
$modalStack . open = function ( modalInstance , modal ) {
2015-05-10 05:43:32 +02:00
var modalOpener = $document [ 0 ] . activeElement ;
2014-02-21 02:13:31 +01:00
openedWindows . add ( modalInstance , {
deferred : modal . deferred ,
2015-05-10 05:43:32 +02:00
renderDeferred : modal . renderDeferred ,
2014-02-21 02:13:31 +01:00
modalScope : modal . scope ,
backdrop : modal . backdrop ,
keyboard : modal . keyboard
} ) ;
2015-03-13 09:39:35 +01:00
var body = $document . find ( 'body' ) . eq ( 0 ) ,
currBackdropIndex = backdropIndex ( ) ;
2014-02-21 02:13:31 +01:00
2015-03-13 09:39:35 +01:00
if ( currBackdropIndex >= 0 && ! backdropDomEl ) {
backdropScope = $rootScope . $new ( true ) ;
backdropScope . index = currBackdropIndex ;
2015-05-10 05:43:32 +02:00
var angularBackgroundDomEl = angular . element ( '<div modal-backdrop="modal-backdrop"></div>' ) ;
2015-03-13 09:39:35 +01:00
angularBackgroundDomEl . attr ( 'backdrop-class' , modal . backdropClass ) ;
2015-05-10 05:43:32 +02:00
if ( modal . animation ) {
angularBackgroundDomEl . attr ( 'modal-animation' , 'true' ) ;
}
2015-03-13 09:39:35 +01:00
backdropDomEl = $compile ( angularBackgroundDomEl ) ( backdropScope ) ;
body . append ( backdropDomEl ) ;
2013-01-28 15:18:21 +01:00
}
2014-05-31 11:53:44 +02:00
2015-05-10 05:43:32 +02:00
var angularDomEl = angular . element ( '<div modal-window="modal-window"></div>' ) ;
2015-03-13 09:39:35 +01:00
angularDomEl . attr ( {
'template-url' : modal . windowTemplateUrl ,
'window-class' : modal . windowClass ,
'size' : modal . size ,
'index' : openedWindows . length ( ) - 1 ,
'animate' : 'animate'
} ) . html ( modal . content ) ;
2015-05-10 05:43:32 +02:00
if ( modal . animation ) {
angularDomEl . attr ( 'modal-animation' , 'true' ) ;
}
2014-02-21 02:13:31 +01:00
var modalDomEl = $compile ( angularDomEl ) ( modal . scope ) ;
openedWindows . top ( ) . value . modalDomEl = modalDomEl ;
2015-05-10 05:43:32 +02:00
openedWindows . top ( ) . value . modalOpener = modalOpener ;
2014-02-21 02:13:31 +01:00
body . append ( modalDomEl ) ;
body . addClass ( OPENED _MODAL _CLASS ) ;
2013-01-28 15:18:21 +01:00
} ;
2015-05-10 05:43:32 +02:00
function broadcastClosing ( modalWindow , resultOrReason , closing ) {
return ! modalWindow . value . modalScope . $broadcast ( 'modal.closing' , resultOrReason , closing ) . defaultPrevented ;
}
2014-02-21 02:13:31 +01:00
$modalStack . close = function ( modalInstance , result ) {
2015-03-13 09:39:35 +01:00
var modalWindow = openedWindows . get ( modalInstance ) ;
2015-05-10 05:43:32 +02:00
if ( modalWindow && broadcastClosing ( modalWindow , result , true ) ) {
2015-03-13 09:39:35 +01:00
modalWindow . value . deferred . resolve ( result ) ;
2014-02-21 02:13:31 +01:00
removeModalWindow ( modalInstance ) ;
2015-05-10 05:43:32 +02:00
modalWindow . value . modalOpener . focus ( ) ;
return true ;
2013-01-28 15:18:21 +01:00
}
2015-05-10 05:43:32 +02:00
return ! modalWindow ;
2013-01-28 15:18:21 +01:00
} ;
2014-02-21 02:13:31 +01:00
$modalStack . dismiss = function ( modalInstance , reason ) {
2015-03-13 09:39:35 +01:00
var modalWindow = openedWindows . get ( modalInstance ) ;
2015-05-10 05:43:32 +02:00
if ( modalWindow && broadcastClosing ( modalWindow , reason , false ) ) {
2015-03-13 09:39:35 +01:00
modalWindow . value . deferred . reject ( reason ) ;
2014-02-21 02:13:31 +01:00
removeModalWindow ( modalInstance ) ;
2015-05-10 05:43:32 +02:00
modalWindow . value . modalOpener . focus ( ) ;
return true ;
2013-01-28 15:18:21 +01:00
}
2015-05-10 05:43:32 +02:00
return ! modalWindow ;
2013-01-28 15:18:21 +01:00
} ;
2014-02-21 02:13:31 +01:00
2015-03-13 09:39:35 +01:00
$modalStack . dismissAll = function ( reason ) {
var topModal = this . getTop ( ) ;
2015-05-10 05:43:32 +02:00
while ( topModal && this . dismiss ( topModal . key , reason ) ) {
2015-03-13 09:39:35 +01:00
topModal = this . getTop ( ) ;
}
} ;
2014-02-21 02:13:31 +01:00
$modalStack . getTop = function ( ) {
return openedWindows . top ( ) ;
} ;
2015-05-10 05:43:32 +02:00
$modalStack . modalRendered = function ( modalInstance ) {
var modalWindow = openedWindows . get ( modalInstance ) ;
if ( modalWindow ) {
modalWindow . value . renderDeferred . resolve ( ) ;
}
} ;
2014-02-21 02:13:31 +01:00
return $modalStack ;
} ] )
. provider ( '$modal' , function ( ) {
var $modalProvider = {
options : {
2015-05-10 05:43:32 +02:00
animation : true ,
backdrop : true , //can also be false or 'static'
2014-02-21 02:13:31 +01:00
keyboard : true
} ,
2015-05-10 05:43:32 +02:00
$get : [ '$injector' , '$rootScope' , '$q' , '$templateRequest' , '$controller' , '$modalStack' ,
function ( $injector , $rootScope , $q , $templateRequest , $controller , $modalStack ) {
2014-02-21 02:13:31 +01:00
var $modal = { } ;
function getTemplatePromise ( options ) {
return options . template ? $q . when ( options . template ) :
2015-05-10 05:43:32 +02:00
$templateRequest ( angular . isFunction ( options . templateUrl ) ? ( options . templateUrl ) ( ) : options . templateUrl ) ;
2014-02-21 02:13:31 +01:00
}
function getResolvePromises ( resolves ) {
var promisesArr = [ ] ;
2015-03-13 09:39:35 +01:00
angular . forEach ( resolves , function ( value ) {
2014-02-21 02:13:31 +01:00
if ( angular . isFunction ( value ) || angular . isArray ( value ) ) {
promisesArr . push ( $q . when ( $injector . invoke ( value ) ) ) ;
}
} ) ;
return promisesArr ;
}
$modal . open = function ( modalOptions ) {
var modalResultDeferred = $q . defer ( ) ;
var modalOpenedDeferred = $q . defer ( ) ;
2015-05-10 05:43:32 +02:00
var modalRenderDeferred = $q . defer ( ) ;
2014-02-21 02:13:31 +01:00
//prepare an instance of a modal to be injected into controllers and returned to a caller
var modalInstance = {
result : modalResultDeferred . promise ,
opened : modalOpenedDeferred . promise ,
2015-05-10 05:43:32 +02:00
rendered : modalRenderDeferred . promise ,
2014-02-21 02:13:31 +01:00
close : function ( result ) {
2015-05-10 05:43:32 +02:00
return $modalStack . close ( modalInstance , result ) ;
2014-02-21 02:13:31 +01:00
} ,
dismiss : function ( reason ) {
2015-05-10 05:43:32 +02:00
return $modalStack . dismiss ( modalInstance , reason ) ;
2014-02-21 02:13:31 +01:00
}
} ;
//merge and clean up options
modalOptions = angular . extend ( { } , $modalProvider . options , modalOptions ) ;
modalOptions . resolve = modalOptions . resolve || { } ;
//verify options
if ( ! modalOptions . template && ! modalOptions . templateUrl ) {
throw new Error ( 'One of template or templateUrl options is required.' ) ;
}
var templateAndResolvePromise =
$q . all ( [ getTemplatePromise ( modalOptions ) ] . concat ( getResolvePromises ( modalOptions . resolve ) ) ) ;
templateAndResolvePromise . then ( function resolveSuccess ( tplAndVars ) {
var modalScope = ( modalOptions . scope || $rootScope ) . $new ( ) ;
modalScope . $close = modalInstance . close ;
modalScope . $dismiss = modalInstance . dismiss ;
var ctrlInstance , ctrlLocals = { } ;
var resolveIter = 1 ;
//controllers
if ( modalOptions . controller ) {
ctrlLocals . $scope = modalScope ;
ctrlLocals . $modalInstance = modalInstance ;
angular . forEach ( modalOptions . resolve , function ( value , key ) {
ctrlLocals [ key ] = tplAndVars [ resolveIter ++ ] ;
} ) ;
ctrlInstance = $controller ( modalOptions . controller , ctrlLocals ) ;
2015-03-13 09:39:35 +01:00
if ( modalOptions . controllerAs ) {
modalScope [ modalOptions . controllerAs ] = ctrlInstance ;
}
2014-02-21 02:13:31 +01:00
}
$modalStack . open ( modalInstance , {
scope : modalScope ,
deferred : modalResultDeferred ,
2015-05-10 05:43:32 +02:00
renderDeferred : modalRenderDeferred ,
2014-02-21 02:13:31 +01:00
content : tplAndVars [ 0 ] ,
2015-05-10 05:43:32 +02:00
animation : modalOptions . animation ,
2014-02-21 02:13:31 +01:00
backdrop : modalOptions . backdrop ,
keyboard : modalOptions . keyboard ,
2015-03-13 09:39:35 +01:00
backdropClass : modalOptions . backdropClass ,
windowClass : modalOptions . windowClass ,
windowTemplateUrl : modalOptions . windowTemplateUrl ,
size : modalOptions . size
2014-02-21 02:13:31 +01:00
} ) ;
} , function resolveError ( reason ) {
modalResultDeferred . reject ( reason ) ;
} ) ;
templateAndResolvePromise . then ( function ( ) {
modalOpenedDeferred . resolve ( true ) ;
2015-05-10 05:43:32 +02:00
} , function ( reason ) {
modalOpenedDeferred . reject ( reason ) ;
2014-02-21 02:13:31 +01:00
} ) ;
return modalInstance ;
} ;
return $modal ;
} ]
} ;
return $modalProvider ;
} ) ;
angular . module ( 'ui.bootstrap.pagination' , [ ] )
2015-03-13 09:39:35 +01:00
. controller ( 'PaginationController' , [ '$scope' , '$attrs' , '$parse' , function ( $scope , $attrs , $parse ) {
2014-02-21 02:13:31 +01:00
var self = this ,
2015-03-13 09:39:35 +01:00
ngModelCtrl = { $setViewValue : angular . noop } , // nullModelCtrl
2014-02-21 02:13:31 +01:00
setNumPages = $attrs . numPages ? $parse ( $attrs . numPages ) . assign : angular . noop ;
2015-03-13 09:39:35 +01:00
this . init = function ( ngModelCtrl _ , config ) {
ngModelCtrl = ngModelCtrl _ ;
this . config = config ;
ngModelCtrl . $render = function ( ) {
self . render ( ) ;
} ;
2014-02-21 02:13:31 +01:00
if ( $attrs . itemsPerPage ) {
$scope . $parent . $watch ( $parse ( $attrs . itemsPerPage ) , function ( value ) {
self . itemsPerPage = parseInt ( value , 10 ) ;
$scope . totalPages = self . calculateTotalPages ( ) ;
} ) ;
} else {
2015-03-13 09:39:35 +01:00
this . itemsPerPage = config . itemsPerPage ;
2013-01-28 15:18:21 +01:00
}
2015-05-10 05:43:32 +02:00
$scope . $watch ( 'totalItems' , function ( ) {
$scope . totalPages = self . calculateTotalPages ( ) ;
} ) ;
$scope . $watch ( 'totalPages' , function ( value ) {
setNumPages ( $scope . $parent , value ) ; // Readonly variable
if ( $scope . page > value ) {
$scope . selectPage ( value ) ;
} else {
ngModelCtrl . $render ( ) ;
}
} ) ;
2013-01-28 15:18:21 +01:00
} ;
2014-02-21 02:13:31 +01:00
this . calculateTotalPages = function ( ) {
var totalPages = this . itemsPerPage < 1 ? 1 : Math . ceil ( $scope . totalItems / this . itemsPerPage ) ;
return Math . max ( totalPages || 0 , 1 ) ;
} ;
this . render = function ( ) {
2015-03-13 09:39:35 +01:00
$scope . page = parseInt ( ngModelCtrl . $viewValue , 10 ) || 1 ;
2013-01-28 15:18:21 +01:00
} ;
2015-05-10 05:43:32 +02:00
$scope . selectPage = function ( page , evt ) {
2015-03-13 09:39:35 +01:00
if ( $scope . page !== page && page > 0 && page <= $scope . totalPages ) {
2015-05-10 05:43:32 +02:00
if ( evt && evt . target ) {
evt . target . blur ( ) ;
}
2015-03-13 09:39:35 +01:00
ngModelCtrl . $setViewValue ( page ) ;
ngModelCtrl . $render ( ) ;
2013-01-28 15:18:21 +01:00
}
} ;
2014-02-21 02:13:31 +01:00
2015-03-13 09:39:35 +01:00
$scope . getText = function ( key ) {
return $scope [ key + 'Text' ] || self . config [ key + 'Text' ] ;
} ;
$scope . noPrevious = function ( ) {
return $scope . page === 1 ;
} ;
$scope . noNext = function ( ) {
return $scope . page === $scope . totalPages ;
} ;
2013-01-28 15:18:21 +01:00
} ] )
2014-02-21 02:13:31 +01:00
. constant ( 'paginationConfig' , {
itemsPerPage : 10 ,
boundaryLinks : false ,
directionLinks : true ,
firstText : 'First' ,
previousText : 'Previous' ,
nextText : 'Next' ,
lastText : 'Last' ,
rotate : true
2013-01-28 15:18:21 +01:00
} )
2014-02-21 02:13:31 +01:00
2015-03-13 09:39:35 +01:00
. directive ( 'pagination' , [ '$parse' , 'paginationConfig' , function ( $parse , paginationConfig ) {
2013-01-28 15:18:21 +01:00
return {
restrict : 'EA' ,
2014-02-21 02:13:31 +01:00
scope : {
totalItems : '=' ,
2015-03-13 09:39:35 +01:00
firstText : '@' ,
previousText : '@' ,
nextText : '@' ,
lastText : '@'
2013-01-28 15:18:21 +01:00
} ,
2015-03-13 09:39:35 +01:00
require : [ 'pagination' , '?ngModel' ] ,
2014-02-21 02:13:31 +01:00
controller : 'PaginationController' ,
templateUrl : 'template/pagination/pagination.html' ,
2013-01-28 15:18:21 +01:00
replace : true ,
2015-03-13 09:39:35 +01:00
link : function ( scope , element , attrs , ctrls ) {
var paginationCtrl = ctrls [ 0 ] , ngModelCtrl = ctrls [ 1 ] ;
if ( ! ngModelCtrl ) {
return ; // do nothing if no ng-model
}
2014-02-21 02:13:31 +01:00
// Setup configuration parameters
2015-03-13 09:39:35 +01:00
var maxSize = angular . isDefined ( attrs . maxSize ) ? scope . $parent . $eval ( attrs . maxSize ) : paginationConfig . maxSize ,
rotate = angular . isDefined ( attrs . rotate ) ? scope . $parent . $eval ( attrs . rotate ) : paginationConfig . rotate ;
scope . boundaryLinks = angular . isDefined ( attrs . boundaryLinks ) ? scope . $parent . $eval ( attrs . boundaryLinks ) : paginationConfig . boundaryLinks ;
scope . directionLinks = angular . isDefined ( attrs . directionLinks ) ? scope . $parent . $eval ( attrs . directionLinks ) : paginationConfig . directionLinks ;
2014-02-21 02:13:31 +01:00
2015-03-13 09:39:35 +01:00
paginationCtrl . init ( ngModelCtrl , paginationConfig ) ;
2014-02-21 02:13:31 +01:00
if ( attrs . maxSize ) {
scope . $parent . $watch ( $parse ( attrs . maxSize ) , function ( value ) {
maxSize = parseInt ( value , 10 ) ;
paginationCtrl . render ( ) ;
} ) ;
}
2013-01-28 15:18:21 +01:00
2014-02-21 02:13:31 +01:00
// Create page object used in template
2015-03-13 09:39:35 +01:00
function makePage ( number , text , isActive ) {
2014-02-21 02:13:31 +01:00
return {
number : number ,
text : text ,
2015-03-13 09:39:35 +01:00
active : isActive
2014-02-21 02:13:31 +01:00
} ;
}
2013-01-28 15:18:21 +01:00
2015-03-13 09:39:35 +01:00
function getPages ( currentPage , totalPages ) {
2014-02-21 02:13:31 +01:00
var pages = [ ] ;
2013-01-28 15:18:21 +01:00
2014-02-21 02:13:31 +01:00
// Default page limits
var startPage = 1 , endPage = totalPages ;
var isMaxSized = ( angular . isDefined ( maxSize ) && maxSize < totalPages ) ;
2013-01-28 15:18:21 +01:00
2014-02-21 02:13:31 +01:00
// recompute if maxSize
if ( isMaxSized ) {
if ( rotate ) {
// Current page is displayed in the middle of the visible ones
startPage = Math . max ( currentPage - Math . floor ( maxSize / 2 ) , 1 ) ;
endPage = startPage + maxSize - 1 ;
2013-01-28 15:18:21 +01:00
2014-02-21 02:13:31 +01:00
// Adjust if limit is exceeded
if ( endPage > totalPages ) {
endPage = totalPages ;
startPage = endPage - maxSize + 1 ;
}
} else {
// Visible pages are paginated with maxSize
startPage = ( ( Math . ceil ( currentPage / maxSize ) - 1 ) * maxSize ) + 1 ;
2013-01-28 15:18:21 +01:00
2014-02-21 02:13:31 +01:00
// Adjust last page if limit is exceeded
endPage = Math . min ( startPage + maxSize - 1 , totalPages ) ;
}
}
2013-01-28 15:18:21 +01:00
2014-02-21 02:13:31 +01:00
// Add page number links
for ( var number = startPage ; number <= endPage ; number ++ ) {
2015-03-13 09:39:35 +01:00
var page = makePage ( number , number , number === currentPage ) ;
2014-02-21 02:13:31 +01:00
pages . push ( page ) ;
2013-01-28 15:18:21 +01:00
}
2014-02-21 02:13:31 +01:00
// Add links to move between page sets
if ( isMaxSized && ! rotate ) {
if ( startPage > 1 ) {
2015-03-13 09:39:35 +01:00
var previousPageSet = makePage ( startPage - 1 , '...' , false ) ;
2014-02-21 02:13:31 +01:00
pages . unshift ( previousPageSet ) ;
}
2013-01-28 15:18:21 +01:00
2014-02-21 02:13:31 +01:00
if ( endPage < totalPages ) {
2015-03-13 09:39:35 +01:00
var nextPageSet = makePage ( endPage + 1 , '...' , false ) ;
2014-02-21 02:13:31 +01:00
pages . push ( nextPageSet ) ;
}
}
2013-01-28 15:18:21 +01:00
2015-03-13 09:39:35 +01:00
return pages ;
}
2014-02-21 02:13:31 +01:00
2015-03-13 09:39:35 +01:00
var originalRender = paginationCtrl . render ;
paginationCtrl . render = function ( ) {
originalRender ( ) ;
if ( scope . page > 0 && scope . page <= scope . totalPages ) {
scope . pages = getPages ( scope . page , scope . totalPages ) ;
2013-01-28 15:18:21 +01:00
}
2014-02-21 02:13:31 +01:00
} ;
}
} ;
} ] )
2013-01-28 15:18:21 +01:00
2014-02-21 02:13:31 +01:00
. constant ( 'pagerConfig' , {
itemsPerPage : 10 ,
previousText : '« Previous' ,
nextText : 'Next »' ,
align : true
} )
2013-01-28 15:18:21 +01:00
2015-03-13 09:39:35 +01:00
. directive ( 'pager' , [ 'pagerConfig' , function ( pagerConfig ) {
2014-02-21 02:13:31 +01:00
return {
restrict : 'EA' ,
scope : {
totalItems : '=' ,
2015-03-13 09:39:35 +01:00
previousText : '@' ,
nextText : '@'
2014-02-21 02:13:31 +01:00
} ,
2015-03-13 09:39:35 +01:00
require : [ 'pager' , '?ngModel' ] ,
2014-02-21 02:13:31 +01:00
controller : 'PaginationController' ,
templateUrl : 'template/pagination/pager.html' ,
replace : true ,
2015-03-13 09:39:35 +01:00
link : function ( scope , element , attrs , ctrls ) {
var paginationCtrl = ctrls [ 0 ] , ngModelCtrl = ctrls [ 1 ] ;
2014-02-21 02:13:31 +01:00
2015-03-13 09:39:35 +01:00
if ( ! ngModelCtrl ) {
return ; // do nothing if no ng-model
2013-01-28 15:18:21 +01:00
}
2015-03-13 09:39:35 +01:00
scope . align = angular . isDefined ( attrs . align ) ? scope . $parent . $eval ( attrs . align ) : pagerConfig . align ;
paginationCtrl . init ( ngModelCtrl , pagerConfig ) ;
2013-01-28 15:18:21 +01:00
}
} ;
} ] ) ;
/ * *
2014-02-21 02:13:31 +01:00
* The following features are still outstanding : animation as a
* function , placement as a function , inside , support for more triggers than
* just mouse enter / leave , html tooltips , and selector delegation .
2013-01-28 15:18:21 +01:00
* /
2014-02-21 02:13:31 +01:00
angular . module ( 'ui.bootstrap.tooltip' , [ 'ui.bootstrap.position' , 'ui.bootstrap.bindHtml' ] )
2013-01-28 15:18:21 +01:00
2014-02-21 02:13:31 +01:00
/ * *
* The $tooltip service creates tooltip - and popover - like directives as well as
* houses global options for them .
* /
. provider ( '$tooltip' , function ( ) {
// The default options tooltip and popover.
var defaultOptions = {
placement : 'top' ,
animation : true ,
2015-05-10 05:43:32 +02:00
popupDelay : 0 ,
useContentExp : false
2014-02-21 02:13:31 +01:00
} ;
2013-01-28 15:18:21 +01:00
2014-02-21 02:13:31 +01:00
// Default hide triggers for each show trigger
var triggerMap = {
'mouseenter' : 'mouseleave' ,
'click' : 'click' ,
'focus' : 'blur'
} ;
2013-01-28 15:18:21 +01:00
2014-02-21 02:13:31 +01:00
// The options specified to the provider globally.
var globalOptions = { } ;
2014-05-31 11:53:44 +02:00
2014-02-21 02:13:31 +01:00
/ * *
* ` options({}) ` allows global configuration of all tooltips in the
* application .
*
* var app = angular . module ( 'App' , [ 'ui.bootstrap.tooltip' ] , function ( $tooltipProvider ) {
* // place tooltips left instead of top by default
* $tooltipProvider . options ( { placement : 'left' } ) ;
* } ) ;
* /
2015-05-10 05:43:32 +02:00
this . options = function ( value ) {
angular . extend ( globalOptions , value ) ;
} ;
2013-01-28 15:18:21 +01:00
2014-02-21 02:13:31 +01:00
/ * *
* This allows you to extend the set of trigger mappings available . E . g . :
*
* $tooltipProvider . setTriggers ( 'openTrigger' : 'closeTrigger' ) ;
* /
this . setTriggers = function setTriggers ( triggers ) {
angular . extend ( triggerMap , triggers ) ;
} ;
2013-01-28 15:18:21 +01:00
2014-02-21 02:13:31 +01:00
/ * *
* This is a helper function for translating camel - case to snake - case .
* /
function snake _case ( name ) {
var regexp = /[A-Z]/g ;
var separator = '-' ;
return name . replace ( regexp , function ( letter , pos ) {
return ( pos ? separator : '' ) + letter . toLowerCase ( ) ;
2013-01-28 15:18:21 +01:00
} ) ;
2014-02-21 02:13:31 +01:00
}
2013-01-28 15:18:21 +01:00
2014-02-21 02:13:31 +01:00
/ * *
* Returns the actual instance of the $tooltip service .
* TODO support multiple triggers
* /
2015-03-13 09:39:35 +01:00
this . $get = [ '$window' , '$compile' , '$timeout' , '$document' , '$position' , '$interpolate' , function ( $window , $compile , $timeout , $document , $position , $interpolate ) {
2015-05-10 05:43:32 +02:00
return function $tooltip ( type , prefix , defaultTriggerShow , options ) {
options = angular . extend ( { } , defaultOptions , globalOptions , options ) ;
2014-02-21 02:13:31 +01:00
/ * *
* Returns an object of show and hide triggers .
*
* If a trigger is supplied ,
* it is used to show the tooltip ; otherwise , it will use the ` trigger `
* option passed to the ` $ tooltipProvider.options ` method ; else it will
* default to the trigger supplied to this directive factory .
*
* The hide trigger is based on the show trigger . If the ` trigger ` option
* was passed to the ` $ tooltipProvider.options ` method , it will use the
* mapped trigger from ` triggerMap ` or the passed trigger if the map is
* undefined ; otherwise , it uses the ` triggerMap ` value of the show
* trigger ; else it will just use the show trigger .
* /
function getTriggers ( trigger ) {
var show = trigger || options . trigger || defaultTriggerShow ;
var hide = triggerMap [ show ] || show ;
return {
show : show ,
hide : hide
} ;
2013-01-28 15:18:21 +01:00
}
2014-02-21 02:13:31 +01:00
var directiveName = snake _case ( type ) ;
var startSym = $interpolate . startSymbol ( ) ;
var endSym = $interpolate . endSymbol ( ) ;
2014-05-31 11:53:44 +02:00
var template =
2014-02-21 02:13:31 +01:00
'<div ' + directiveName + '-popup ' +
2015-03-13 09:39:35 +01:00
'title="' + startSym + 'title' + endSym + '" ' +
2015-05-10 05:43:32 +02:00
( options . useContentExp ?
'content-exp="contentExp()" ' :
'content="' + startSym + 'content' + endSym + '" ' ) +
2015-03-13 09:39:35 +01:00
'placement="' + startSym + 'placement' + endSym + '" ' +
2015-05-10 05:43:32 +02:00
'popup-class="' + startSym + 'popupClass' + endSym + '" ' +
2015-03-13 09:39:35 +01:00
'animation="animation" ' +
'is-open="isOpen"' +
2015-05-10 05:43:32 +02:00
'origin-scope="origScope" ' +
2014-02-21 02:13:31 +01:00
'>' +
'</div>' ;
return {
restrict : 'EA' ,
2015-03-13 09:39:35 +01:00
compile : function ( tElem , tAttrs ) {
var tooltipLinker = $compile ( template ) ;
2015-05-10 05:43:32 +02:00
return function link ( scope , element , attrs , tooltipCtrl ) {
2015-03-13 09:39:35 +01:00
var tooltip ;
var tooltipLinkedScope ;
var transitionTimeout ;
var popupTimeout ;
var appendToBody = angular . isDefined ( options . appendToBody ) ? options . appendToBody : false ;
var triggers = getTriggers ( undefined ) ;
var hasEnableExp = angular . isDefined ( attrs [ prefix + 'Enable' ] ) ;
var ttScope = scope . $new ( true ) ;
var positionTooltip = function ( ) {
2015-05-10 05:43:32 +02:00
if ( ! tooltip ) { return ; }
2015-03-13 09:39:35 +01:00
var ttPosition = $position . positionElements ( element , tooltip , ttScope . placement , appendToBody ) ;
ttPosition . top += 'px' ;
ttPosition . left += 'px' ;
// Now set the calculated positioning.
tooltip . css ( ttPosition ) ;
} ;
2014-05-31 11:53:44 +02:00
2015-05-10 05:43:32 +02:00
// Set up the correct scope to allow transclusion later
ttScope . origScope = scope ;
2015-03-13 09:39:35 +01:00
// By default, the tooltip is not open.
// TODO add ability to start tooltip opened
ttScope . isOpen = false ;
2013-01-28 15:18:21 +01:00
2015-03-13 09:39:35 +01:00
function toggleTooltipBind ( ) {
if ( ! ttScope . isOpen ) {
showTooltipBind ( ) ;
} else {
hideTooltipBind ( ) ;
}
}
2014-05-31 11:53:44 +02:00
2015-03-13 09:39:35 +01:00
// Show the tooltip with delay if specified, otherwise show it immediately
function showTooltipBind ( ) {
if ( hasEnableExp && ! scope . $eval ( attrs [ prefix + 'Enable' ] ) ) {
return ;
}
2014-02-21 02:13:31 +01:00
2015-03-13 09:39:35 +01:00
prepareTooltip ( ) ;
if ( ttScope . popupDelay ) {
// Do nothing if the tooltip was already scheduled to pop-up.
// This happens if show is triggered multiple times before any hide is triggered.
if ( ! popupTimeout ) {
popupTimeout = $timeout ( show , ttScope . popupDelay , false ) ;
popupTimeout . then ( function ( reposition ) { reposition ( ) ; } ) ;
}
} else {
show ( ) ( ) ;
}
2014-02-21 02:13:31 +01:00
}
2015-03-13 09:39:35 +01:00
function hideTooltipBind ( ) {
scope . $apply ( function ( ) {
hide ( ) ;
} ) ;
2014-02-21 02:13:31 +01:00
}
2014-05-31 11:53:44 +02:00
2015-03-13 09:39:35 +01:00
// Show the tooltip popup element.
function show ( ) {
2014-05-31 11:53:44 +02:00
2015-03-13 09:39:35 +01:00
popupTimeout = null ;
2014-02-21 02:13:31 +01:00
2015-03-13 09:39:35 +01:00
// If there is a pending remove transition, we must cancel it, lest the
// tooltip be mysteriously removed.
if ( transitionTimeout ) {
$timeout . cancel ( transitionTimeout ) ;
transitionTimeout = null ;
}
2014-02-21 02:13:31 +01:00
2015-03-13 09:39:35 +01:00
// Don't show empty tooltips.
2015-05-10 05:43:32 +02:00
if ( ! ( options . useContentExp ? ttScope . contentExp ( ) : ttScope . content ) ) {
2015-03-13 09:39:35 +01:00
return angular . noop ;
}
2014-02-21 02:13:31 +01:00
2015-03-13 09:39:35 +01:00
createTooltip ( ) ;
2014-05-31 11:53:44 +02:00
2015-03-13 09:39:35 +01:00
// Set the initial positioning.
tooltip . css ( { top : 0 , left : 0 , display : 'block' } ) ;
ttScope . $digest ( ) ;
2014-05-31 11:53:44 +02:00
2015-03-13 09:39:35 +01:00
positionTooltip ( ) ;
2014-02-21 02:13:31 +01:00
2015-03-13 09:39:35 +01:00
// And show the tooltip.
ttScope . isOpen = true ;
2015-05-10 05:43:32 +02:00
ttScope . $apply ( ) ; // digest required as $apply is not called
2014-05-31 11:53:44 +02:00
2015-03-13 09:39:35 +01:00
// Return positioning function as promise callback for correct
// positioning after draw.
return positionTooltip ;
2014-02-21 02:13:31 +01:00
}
2015-03-13 09:39:35 +01:00
// Hide the tooltip popup element.
function hide ( ) {
// First things first: we don't show it anymore.
ttScope . isOpen = false ;
//if tooltip is going to be shown after delay, we must cancel this
$timeout . cancel ( popupTimeout ) ;
popupTimeout = null ;
// And now we remove it from the DOM. However, if we have animation, we
// need to wait for it to expire beforehand.
// FIXME: this is a placeholder for a port of the transitions library.
if ( ttScope . animation ) {
if ( ! transitionTimeout ) {
transitionTimeout = $timeout ( removeTooltip , 500 ) ;
}
} else {
removeTooltip ( ) ;
}
}
2014-02-21 02:13:31 +01:00
2015-03-13 09:39:35 +01:00
function createTooltip ( ) {
// There can only be one tooltip element per directive shown at once.
if ( tooltip ) {
removeTooltip ( ) ;
}
tooltipLinkedScope = ttScope . $new ( ) ;
tooltip = tooltipLinker ( tooltipLinkedScope , function ( tooltip ) {
if ( appendToBody ) {
$document . find ( 'body' ) . append ( tooltip ) ;
} else {
element . after ( tooltip ) ;
}
} ) ;
2015-05-10 05:43:32 +02:00
tooltipLinkedScope . $watch ( function ( ) {
$timeout ( positionTooltip , 0 , false ) ;
} ) ;
if ( options . useContentExp ) {
tooltipLinkedScope . $watch ( 'contentExp()' , function ( val ) {
if ( ! val && ttScope . isOpen ) {
hide ( ) ;
}
} ) ;
}
2014-02-21 02:13:31 +01:00
}
2015-03-13 09:39:35 +01:00
function removeTooltip ( ) {
transitionTimeout = null ;
if ( tooltip ) {
tooltip . remove ( ) ;
tooltip = null ;
}
if ( tooltipLinkedScope ) {
tooltipLinkedScope . $destroy ( ) ;
tooltipLinkedScope = null ;
}
}
2014-02-21 02:13:31 +01:00
2015-03-13 09:39:35 +01:00
function prepareTooltip ( ) {
2015-05-10 05:43:32 +02:00
prepPopupClass ( ) ;
2015-03-13 09:39:35 +01:00
prepPlacement ( ) ;
prepPopupDelay ( ) ;
}
2014-02-21 02:13:31 +01:00
2015-05-10 05:43:32 +02:00
ttScope . contentExp = function ( ) {
return scope . $eval ( attrs [ type ] ) ;
} ;
2015-03-13 09:39:35 +01:00
/ * *
* Observe the relevant attributes .
* /
2015-05-10 05:43:32 +02:00
if ( ! options . useContentExp ) {
attrs . $observe ( type , function ( val ) {
ttScope . content = val ;
if ( ! val && ttScope . isOpen ) {
hide ( ) ;
}
} ) ;
}
2014-02-21 02:13:31 +01:00
2015-05-10 05:43:32 +02:00
attrs . $observe ( 'disabled' , function ( val ) {
if ( val && ttScope . isOpen ) {
2015-03-13 09:39:35 +01:00
hide ( ) ;
}
} ) ;
2014-02-21 02:13:31 +01:00
2015-03-13 09:39:35 +01:00
attrs . $observe ( prefix + 'Title' , function ( val ) {
ttScope . title = val ;
} ) ;
2014-02-21 02:13:31 +01:00
2015-05-10 05:43:32 +02:00
function prepPopupClass ( ) {
ttScope . popupClass = attrs [ prefix + 'Class' ] ;
}
2015-03-13 09:39:35 +01:00
function prepPlacement ( ) {
var val = attrs [ prefix + 'Placement' ] ;
ttScope . placement = angular . isDefined ( val ) ? val : options . placement ;
}
2014-02-21 02:13:31 +01:00
2015-03-13 09:39:35 +01:00
function prepPopupDelay ( ) {
var val = attrs [ prefix + 'PopupDelay' ] ;
var delay = parseInt ( val , 10 ) ;
ttScope . popupDelay = ! isNaN ( delay ) ? delay : options . popupDelay ;
2014-02-21 02:13:31 +01:00
}
2015-03-13 09:39:35 +01:00
var unregisterTriggers = function ( ) {
element . unbind ( triggers . show , showTooltipBind ) ;
element . unbind ( triggers . hide , hideTooltipBind ) ;
} ;
2014-02-21 02:13:31 +01:00
2015-03-13 09:39:35 +01:00
function prepTriggers ( ) {
var val = attrs [ prefix + 'Trigger' ] ;
unregisterTriggers ( ) ;
2014-02-21 02:13:31 +01:00
2015-03-13 09:39:35 +01:00
triggers = getTriggers ( val ) ;
2014-02-21 02:13:31 +01:00
2015-03-13 09:39:35 +01:00
if ( triggers . show === triggers . hide ) {
element . bind ( triggers . show , toggleTooltipBind ) ;
} else {
element . bind ( triggers . show , showTooltipBind ) ;
element . bind ( triggers . hide , hideTooltipBind ) ;
}
}
prepTriggers ( ) ;
var animation = scope . $eval ( attrs [ prefix + 'Animation' ] ) ;
ttScope . animation = angular . isDefined ( animation ) ? ! ! animation : options . animation ;
var appendToBodyVal = scope . $eval ( attrs [ prefix + 'AppendToBody' ] ) ;
appendToBody = angular . isDefined ( appendToBodyVal ) ? appendToBodyVal : appendToBody ;
// if a tooltip is attached to <body> we need to remove it on
// location change as its parent scope will probably not be destroyed
// by the change.
if ( appendToBody ) {
scope . $on ( '$locationChangeSuccess' , function closeTooltipOnLocationChangeSuccess ( ) {
if ( ttScope . isOpen ) {
hide ( ) ;
}
} ) ;
2014-02-21 02:13:31 +01:00
}
2015-03-13 09:39:35 +01:00
// Make sure tooltip is destroyed and removed.
scope . $on ( '$destroy' , function onDestroyTooltip ( ) {
$timeout . cancel ( transitionTimeout ) ;
$timeout . cancel ( popupTimeout ) ;
unregisterTriggers ( ) ;
removeTooltip ( ) ;
ttScope = null ;
} ) ;
} ;
2014-02-21 02:13:31 +01:00
}
} ;
} ;
} ] ;
} )
2015-05-10 05:43:32 +02:00
// This is mostly ngInclude code but with a custom scope
. directive ( 'tooltipTemplateTransclude' , [
'$animate' , '$sce' , '$compile' , '$templateRequest' ,
function ( $animate , $sce , $compile , $templateRequest ) {
return {
link : function ( scope , elem , attrs ) {
var origScope = scope . $eval ( attrs . tooltipTemplateTranscludeScope ) ;
var changeCounter = 0 ,
currentScope ,
previousElement ,
currentElement ;
var cleanupLastIncludeContent = function ( ) {
if ( previousElement ) {
previousElement . remove ( ) ;
previousElement = null ;
}
if ( currentScope ) {
currentScope . $destroy ( ) ;
currentScope = null ;
}
if ( currentElement ) {
$animate . leave ( currentElement ) . then ( function ( ) {
previousElement = null ;
} ) ;
previousElement = currentElement ;
currentElement = null ;
}
} ;
scope . $watch ( $sce . parseAsResourceUrl ( attrs . tooltipTemplateTransclude ) , function ( src ) {
var thisChangeId = ++ changeCounter ;
if ( src ) {
//set the 2nd param to true to ignore the template request error so that the inner
//contents and scope can be cleaned up.
$templateRequest ( src , true ) . then ( function ( response ) {
if ( thisChangeId !== changeCounter ) { return ; }
var newScope = origScope . $new ( ) ;
var template = response ;
var clone = $compile ( template ) ( newScope , function ( clone ) {
cleanupLastIncludeContent ( ) ;
$animate . enter ( clone , elem ) ;
} ) ;
currentScope = newScope ;
currentElement = clone ;
currentScope . $emit ( '$includeContentLoaded' , src ) ;
} , function ( ) {
if ( thisChangeId === changeCounter ) {
cleanupLastIncludeContent ( ) ;
scope . $emit ( '$includeContentError' , src ) ;
}
} ) ;
scope . $emit ( '$includeContentRequested' , src ) ;
} else {
cleanupLastIncludeContent ( ) ;
}
} ) ;
scope . $on ( '$destroy' , cleanupLastIncludeContent ) ;
}
} ;
} ] )
/ * *
* Note that it ' s intentional that these classes are * not * applied through $animate .
* They must not be animated as they ' re expected to be present on the tooltip on
* initialization .
* /
. directive ( 'tooltipClasses' , function ( ) {
return {
restrict : 'A' ,
link : function ( scope , element , attrs ) {
if ( scope . placement ) {
element . addClass ( scope . placement ) ;
}
if ( scope . popupClass ) {
element . addClass ( scope . popupClass ) ;
}
if ( scope . animation ( ) ) {
element . addClass ( attrs . tooltipAnimationClass ) ;
}
}
} ;
} )
2014-02-21 02:13:31 +01:00
. directive ( 'tooltipPopup' , function ( ) {
return {
restrict : 'EA' ,
replace : true ,
2015-05-10 05:43:32 +02:00
scope : { content : '@' , placement : '@' , popupClass : '@' , animation : '&' , isOpen : '&' } ,
2014-02-21 02:13:31 +01:00
templateUrl : 'template/tooltip/tooltip-popup.html'
} ;
} )
. directive ( 'tooltip' , [ '$tooltip' , function ( $tooltip ) {
return $tooltip ( 'tooltip' , 'tooltip' , 'mouseenter' ) ;
} ] )
2015-05-10 05:43:32 +02:00
. directive ( 'tooltipTemplatePopup' , function ( ) {
return {
restrict : 'EA' ,
replace : true ,
scope : { contentExp : '&' , placement : '@' , popupClass : '@' , animation : '&' , isOpen : '&' ,
originScope : '&' } ,
templateUrl : 'template/tooltip/tooltip-template-popup.html'
} ;
} )
. directive ( 'tooltipTemplate' , [ '$tooltip' , function ( $tooltip ) {
return $tooltip ( 'tooltipTemplate' , 'tooltip' , 'mouseenter' , {
useContentExp : true
} ) ;
} ] )
. directive ( 'tooltipHtmlPopup' , function ( ) {
return {
restrict : 'EA' ,
replace : true ,
scope : { contentExp : '&' , placement : '@' , popupClass : '@' , animation : '&' , isOpen : '&' } ,
templateUrl : 'template/tooltip/tooltip-html-popup.html'
} ;
} )
. directive ( 'tooltipHtml' , [ '$tooltip' , function ( $tooltip ) {
return $tooltip ( 'tooltipHtml' , 'tooltip' , 'mouseenter' , {
useContentExp : true
} ) ;
} ] )
/ *
Deprecated
* /
2014-02-21 02:13:31 +01:00
. directive ( 'tooltipHtmlUnsafePopup' , function ( ) {
return {
restrict : 'EA' ,
replace : true ,
2015-05-10 05:43:32 +02:00
scope : { content : '@' , placement : '@' , popupClass : '@' , animation : '&' , isOpen : '&' } ,
2014-02-21 02:13:31 +01:00
templateUrl : 'template/tooltip/tooltip-html-unsafe-popup.html'
} ;
} )
2015-05-10 05:43:32 +02:00
. value ( 'tooltipHtmlUnsafeSuppressDeprecated' , false )
. directive ( 'tooltipHtmlUnsafe' , [
'$tooltip' , 'tooltipHtmlUnsafeSuppressDeprecated' , '$log' ,
function ( $tooltip , tooltipHtmlUnsafeSuppressDeprecated , $log ) {
if ( ! tooltipHtmlUnsafeSuppressDeprecated ) {
$log . warn ( 'tooltip-html-unsafe is now deprecated. Use tooltip-html or tooltip-template instead.' ) ;
}
2014-02-21 02:13:31 +01:00
return $tooltip ( 'tooltipHtmlUnsafe' , 'tooltip' , 'mouseenter' ) ;
} ] ) ;
/ * *
* The following features are still outstanding : popup delay , animation as a
* function , placement as a function , inside , support for more triggers than
* just mouse enter / leave , html popovers , and selector delegatation .
* /
angular . module ( 'ui.bootstrap.popover' , [ 'ui.bootstrap.tooltip' ] )
2015-03-13 09:39:35 +01:00
2015-05-10 05:43:32 +02:00
. directive ( 'popoverTemplatePopup' , function ( ) {
return {
restrict : 'EA' ,
replace : true ,
scope : { title : '@' , contentExp : '&' , placement : '@' , popupClass : '@' , animation : '&' , isOpen : '&' ,
originScope : '&' } ,
templateUrl : 'template/popover/popover-template.html'
} ;
} )
. directive ( 'popoverTemplate' , [ '$tooltip' , function ( $tooltip ) {
return $tooltip ( 'popoverTemplate' , 'popover' , 'click' , {
useContentExp : true
} ) ;
} ] )
2014-02-21 02:13:31 +01:00
. directive ( 'popoverPopup' , function ( ) {
return {
restrict : 'EA' ,
replace : true ,
2015-05-10 05:43:32 +02:00
scope : { title : '@' , content : '@' , placement : '@' , popupClass : '@' , animation : '&' , isOpen : '&' } ,
2014-02-21 02:13:31 +01:00
templateUrl : 'template/popover/popover.html'
} ;
} )
2015-03-13 09:39:35 +01:00
. directive ( 'popover' , [ '$tooltip' , function ( $tooltip ) {
2014-02-21 02:13:31 +01:00
return $tooltip ( 'popover' , 'popover' , 'click' ) ;
} ] ) ;
2015-03-13 09:39:35 +01:00
angular . module ( 'ui.bootstrap.progressbar' , [ ] )
2014-02-21 02:13:31 +01:00
. constant ( 'progressConfig' , {
animate : true ,
max : 100
} )
2015-03-13 09:39:35 +01:00
. controller ( 'ProgressController' , [ '$scope' , '$attrs' , 'progressConfig' , function ( $scope , $attrs , progressConfig ) {
2014-02-21 02:13:31 +01:00
var self = this ,
animate = angular . isDefined ( $attrs . animate ) ? $scope . $parent . $eval ( $attrs . animate ) : progressConfig . animate ;
2015-03-13 09:39:35 +01:00
this . bars = [ ] ;
2015-05-10 05:43:32 +02:00
$scope . max = angular . isDefined ( $scope . max ) ? $scope . max : progressConfig . max ;
2015-03-13 09:39:35 +01:00
2014-02-21 02:13:31 +01:00
this . addBar = function ( bar , element ) {
2015-03-13 09:39:35 +01:00
if ( ! animate ) {
element . css ( { 'transition' : 'none' } ) ;
2014-02-21 02:13:31 +01:00
}
2015-03-13 09:39:35 +01:00
this . bars . push ( bar ) ;
2014-02-21 02:13:31 +01:00
2015-03-13 09:39:35 +01:00
bar . $watch ( 'value' , function ( value ) {
bar . percent = + ( 100 * value / $scope . max ) . toFixed ( 2 ) ;
2014-02-21 02:13:31 +01:00
} ) ;
bar . $on ( '$destroy' , function ( ) {
2015-03-13 09:39:35 +01:00
element = null ;
2014-02-21 02:13:31 +01:00
self . removeBar ( bar ) ;
} ) ;
} ;
this . removeBar = function ( bar ) {
2015-03-13 09:39:35 +01:00
this . bars . splice ( this . bars . indexOf ( bar ) , 1 ) ;
2014-02-21 02:13:31 +01:00
} ;
} ] )
. directive ( 'progress' , function ( ) {
return {
restrict : 'EA' ,
replace : true ,
transclude : true ,
controller : 'ProgressController' ,
require : 'progress' ,
scope : { } ,
2015-03-13 09:39:35 +01:00
templateUrl : 'template/progressbar/progress.html'
2014-02-21 02:13:31 +01:00
} ;
} )
. directive ( 'bar' , function ( ) {
return {
restrict : 'EA' ,
replace : true ,
transclude : true ,
require : '^progress' ,
scope : {
value : '=' ,
2015-05-10 05:43:32 +02:00
max : '=?' ,
2014-02-21 02:13:31 +01:00
type : '@'
} ,
templateUrl : 'template/progressbar/bar.html' ,
link : function ( scope , element , attrs , progressCtrl ) {
progressCtrl . addBar ( scope , element ) ;
}
} ;
} )
. directive ( 'progressbar' , function ( ) {
return {
restrict : 'EA' ,
replace : true ,
transclude : true ,
controller : 'ProgressController' ,
scope : {
value : '=' ,
2015-05-10 05:43:32 +02:00
max : '=?' ,
2014-02-21 02:13:31 +01:00
type : '@'
} ,
templateUrl : 'template/progressbar/progressbar.html' ,
link : function ( scope , element , attrs , progressCtrl ) {
progressCtrl . addBar ( scope , angular . element ( element . children ( ) [ 0 ] ) ) ;
}
} ;
} ) ;
2015-05-10 05:43:32 +02:00
2014-02-21 02:13:31 +01:00
angular . module ( 'ui.bootstrap.rating' , [ ] )
. constant ( 'ratingConfig' , {
max : 5 ,
stateOn : null ,
stateOff : null
} )
2015-03-13 09:39:35 +01:00
. controller ( 'RatingController' , [ '$scope' , '$attrs' , 'ratingConfig' , function ( $scope , $attrs , ratingConfig ) {
var ngModelCtrl = { $setViewValue : angular . noop } ;
2014-02-21 02:13:31 +01:00
2015-03-13 09:39:35 +01:00
this . init = function ( ngModelCtrl _ ) {
ngModelCtrl = ngModelCtrl _ ;
ngModelCtrl . $render = this . render ;
2014-02-21 02:13:31 +01:00
2015-05-10 05:43:32 +02:00
ngModelCtrl . $formatters . push ( function ( value ) {
if ( angular . isNumber ( value ) && value << 0 !== value ) {
value = Math . round ( value ) ;
}
return value ;
} ) ;
2015-03-13 09:39:35 +01:00
this . stateOn = angular . isDefined ( $attrs . stateOn ) ? $scope . $parent . $eval ( $attrs . stateOn ) : ratingConfig . stateOn ;
this . stateOff = angular . isDefined ( $attrs . stateOff ) ? $scope . $parent . $eval ( $attrs . stateOff ) : ratingConfig . stateOff ;
2014-02-21 02:13:31 +01:00
2015-03-13 09:39:35 +01:00
var ratingStates = angular . isDefined ( $attrs . ratingStates ) ? $scope . $parent . $eval ( $attrs . ratingStates ) :
new Array ( angular . isDefined ( $attrs . max ) ? $scope . $parent . $eval ( $attrs . max ) : ratingConfig . max ) ;
$scope . range = this . buildTemplateObjects ( ratingStates ) ;
} ;
this . buildTemplateObjects = function ( states ) {
2014-02-21 02:13:31 +01:00
for ( var i = 0 , n = states . length ; i < n ; i ++ ) {
2015-03-13 09:39:35 +01:00
states [ i ] = angular . extend ( { index : i } , { stateOn : this . stateOn , stateOff : this . stateOff } , states [ i ] ) ;
2014-02-21 02:13:31 +01:00
}
return states ;
} ;
$scope . rate = function ( value ) {
2015-03-13 09:39:35 +01:00
if ( ! $scope . readonly && value >= 0 && value <= $scope . range . length ) {
ngModelCtrl . $setViewValue ( value ) ;
ngModelCtrl . $render ( ) ;
2014-02-21 02:13:31 +01:00
}
} ;
$scope . enter = function ( value ) {
2015-03-13 09:39:35 +01:00
if ( ! $scope . readonly ) {
$scope . value = value ;
2014-02-21 02:13:31 +01:00
}
$scope . onHover ( { value : value } ) ;
} ;
$scope . reset = function ( ) {
2015-03-13 09:39:35 +01:00
$scope . value = ngModelCtrl . $viewValue ;
2014-02-21 02:13:31 +01:00
$scope . onLeave ( ) ;
} ;
2015-03-13 09:39:35 +01:00
$scope . onKeydown = function ( evt ) {
if ( /(37|38|39|40)/ . test ( evt . which ) ) {
evt . preventDefault ( ) ;
evt . stopPropagation ( ) ;
$scope . rate ( $scope . value + ( evt . which === 38 || evt . which === 39 ? 1 : - 1 ) ) ;
}
} ;
2014-02-21 02:13:31 +01:00
2015-03-13 09:39:35 +01:00
this . render = function ( ) {
$scope . value = ngModelCtrl . $viewValue ;
} ;
2014-02-21 02:13:31 +01:00
} ] )
. directive ( 'rating' , function ( ) {
return {
restrict : 'EA' ,
2015-03-13 09:39:35 +01:00
require : [ 'rating' , 'ngModel' ] ,
2014-02-21 02:13:31 +01:00
scope : {
2015-03-13 09:39:35 +01:00
readonly : '=?' ,
2014-02-21 02:13:31 +01:00
onHover : '&' ,
onLeave : '&'
} ,
controller : 'RatingController' ,
templateUrl : 'template/rating/rating.html' ,
2015-03-13 09:39:35 +01:00
replace : true ,
link : function ( scope , element , attrs , ctrls ) {
var ratingCtrl = ctrls [ 0 ] , ngModelCtrl = ctrls [ 1 ] ;
2015-05-10 05:43:32 +02:00
ratingCtrl . init ( ngModelCtrl ) ;
2015-03-13 09:39:35 +01:00
}
2014-02-21 02:13:31 +01:00
} ;
} ) ;
/ * *
* @ ngdoc overview
* @ name ui . bootstrap . tabs
*
* @ description
* AngularJS version of the tabs directive .
* /
angular . module ( 'ui.bootstrap.tabs' , [ ] )
. controller ( 'TabsetController' , [ '$scope' , function TabsetCtrl ( $scope ) {
var ctrl = this ,
tabs = ctrl . tabs = $scope . tabs = [ ] ;
2015-03-13 09:39:35 +01:00
ctrl . select = function ( selectedTab ) {
2014-02-21 02:13:31 +01:00
angular . forEach ( tabs , function ( tab ) {
2015-03-13 09:39:35 +01:00
if ( tab . active && tab !== selectedTab ) {
tab . active = false ;
tab . onDeselect ( ) ;
}
2014-02-21 02:13:31 +01:00
} ) ;
2015-03-13 09:39:35 +01:00
selectedTab . active = true ;
selectedTab . onSelect ( ) ;
2014-02-21 02:13:31 +01:00
} ;
ctrl . addTab = function addTab ( tab ) {
tabs . push ( tab ) ;
2015-03-13 09:39:35 +01:00
// we can't run the select function on the first tab
// since that would select it twice
2015-05-10 05:43:32 +02:00
if ( tabs . length === 1 && tab . active !== false ) {
2015-03-13 09:39:35 +01:00
tab . active = true ;
} else if ( tab . active ) {
2014-02-21 02:13:31 +01:00
ctrl . select ( tab ) ;
}
2015-05-10 05:43:32 +02:00
else {
tab . active = false ;
}
2014-02-21 02:13:31 +01:00
} ;
ctrl . removeTab = function removeTab ( tab ) {
var index = tabs . indexOf ( tab ) ;
2015-03-13 09:39:35 +01:00
//Select a new tab if the tab to be removed is selected and not destroyed
if ( tab . active && tabs . length > 1 && ! destroyed ) {
2014-02-21 02:13:31 +01:00
//If this is the last tab, select the previous tab. else, the next tab.
var newActiveIndex = index == tabs . length - 1 ? index - 1 : index + 1 ;
ctrl . select ( tabs [ newActiveIndex ] ) ;
}
tabs . splice ( index , 1 ) ;
} ;
2015-03-13 09:39:35 +01:00
var destroyed ;
$scope . $on ( '$destroy' , function ( ) {
destroyed = true ;
} ) ;
2014-02-21 02:13:31 +01:00
} ] )
/ * *
* @ ngdoc directive
* @ name ui . bootstrap . tabs . directive : tabset
* @ restrict EA
*
* @ description
* Tabset is the outer container for the tabs directive
*
* @ param { boolean = } vertical Whether or not to use vertical styling for the tabs .
2015-03-13 09:39:35 +01:00
* @ param { boolean = } justified Whether or not to use justified styling for the tabs .
2014-02-21 02:13:31 +01:00
*
* @ example
< example module = "ui.bootstrap" >
< file name = "index.html" >
< tabset >
2015-03-13 09:39:35 +01:00
< tab heading = "Tab 1" > < b > First < / b > C o n t e n t ! < / t a b >
< tab heading = "Tab 2" > < i > Second < / i > C o n t e n t ! < / t a b >
2014-02-21 02:13:31 +01:00
< / t a b s e t >
< hr / >
< tabset vertical = "true" >
< tab heading = "Vertical Tab 1" > < b > First < / b > V e r t i c a l C o n t e n t ! < / t a b >
< tab heading = "Vertical Tab 2" > < i > Second < / i > V e r t i c a l C o n t e n t ! < / t a b >
< / t a b s e t >
2015-03-13 09:39:35 +01:00
< tabset justified = "true" >
< tab heading = "Justified Tab 1" > < b > First < / b > J u s t i f i e d C o n t e n t ! < / t a b >
< tab heading = "Justified Tab 2" > < i > Second < / i > J u s t i f i e d C o n t e n t ! < / t a b >
< / t a b s e t >
2014-02-21 02:13:31 +01:00
< / f i l e >
< / e x a m p l e >
* /
. directive ( 'tabset' , function ( ) {
return {
restrict : 'EA' ,
transclude : true ,
replace : true ,
2015-03-13 09:39:35 +01:00
scope : {
type : '@'
} ,
2014-02-21 02:13:31 +01:00
controller : 'TabsetController' ,
templateUrl : 'template/tabs/tabset.html' ,
link : function ( scope , element , attrs ) {
scope . vertical = angular . isDefined ( attrs . vertical ) ? scope . $parent . $eval ( attrs . vertical ) : false ;
2015-03-13 09:39:35 +01:00
scope . justified = angular . isDefined ( attrs . justified ) ? scope . $parent . $eval ( attrs . justified ) : false ;
2014-02-21 02:13:31 +01:00
}
} ;
} )
/ * *
* @ ngdoc directive
* @ name ui . bootstrap . tabs . directive : tab
* @ restrict EA
*
* @ param { string = } heading The visible heading , or title , of the tab . Set HTML headings with { @ link ui . bootstrap . tabs . directive : tabHeading tabHeading } .
* @ param { string = } select An expression to evaluate when the tab is selected .
* @ param { boolean = } active A binding , telling whether or not this tab is selected .
* @ param { boolean = } disabled A binding , telling whether or not this tab is disabled .
*
* @ description
* Creates a tab with a heading and content . Must be placed within a { @ link ui . bootstrap . tabs . directive : tabset tabset } .
*
* @ example
< example module = "ui.bootstrap" >
< file name = "index.html" >
< div ng - controller = "TabsDemoCtrl" >
< button class = "btn btn-small" ng - click = "items[0].active = true" >
Select item 1 , using active binding
< / b u t t o n >
< button class = "btn btn-small" ng - click = "items[1].disabled = !items[1].disabled" >
Enable / disable item 2 , using disabled binding
< / b u t t o n >
< br / >
< tabset >
< tab heading = "Tab 1" > First Tab < / t a b >
< tab select = "alertMe()" >
< tab - heading > < i class = "icon-bell" > < / i > A l e r t m e ! < / t a b - h e a d i n g >
Second Tab , with alert callback and html heading !
< / t a b >
< tab ng - repeat = "item in items"
heading = "{{item.title}}"
disabled = "item.disabled"
active = "item.active" >
{ { item . content } }
< / t a b >
< / t a b s e t >
< / d i v >
< / f i l e >
< file name = "script.js" >
function TabsDemoCtrl ( $scope ) {
$scope . items = [
{ title : "Dynamic Title 1" , content : "Dynamic Item 0" } ,
{ title : "Dynamic Title 2" , content : "Dynamic Item 1" , disabled : true }
] ;
$scope . alertMe = function ( ) {
setTimeout ( function ( ) {
alert ( "You've selected the alert tab!" ) ;
} ) ;
} ;
} ;
< / f i l e >
< / e x a m p l e >
* /
/ * *
* @ ngdoc directive
* @ name ui . bootstrap . tabs . directive : tabHeading
* @ restrict EA
*
* @ description
* Creates an HTML heading for a { @ link ui . bootstrap . tabs . directive : tab tab } . Must be placed as a child of a tab element .
*
* @ example
< example module = "ui.bootstrap" >
< file name = "index.html" >
< tabset >
< tab >
< tab - heading > < b > HTML < / b > i n m y t i t l e s ? ! < / t a b - h e a d i n g >
And some content , too !
< / t a b >
< tab >
< tab - heading > < i class = "icon-heart" > < / i > I c o n h e a d i n g ? ! ? < / t a b - h e a d i n g >
That ' s right .
< / t a b >
< / t a b s e t >
< / f i l e >
< / e x a m p l e >
* /
2015-05-10 05:43:32 +02:00
. directive ( 'tab' , [ '$parse' , '$log' , function ( $parse , $log ) {
2014-02-21 02:13:31 +01:00
return {
require : '^tabset' ,
restrict : 'EA' ,
replace : true ,
templateUrl : 'template/tabs/tab.html' ,
transclude : true ,
scope : {
2015-03-13 09:39:35 +01:00
active : '=?' ,
2014-02-21 02:13:31 +01:00
heading : '@' ,
onSelect : '&select' , //This callback is called in contentHeadingTransclude
//once it inserts the tab's content into the dom
onDeselect : '&deselect'
} ,
controller : function ( ) {
//Empty controller so other directives can require being 'under' a tab
} ,
compile : function ( elm , attrs , transclude ) {
return function postLink ( scope , elm , attrs , tabsetCtrl ) {
scope . $watch ( 'active' , function ( active ) {
if ( active ) {
tabsetCtrl . select ( scope ) ;
}
} ) ;
scope . disabled = false ;
2015-05-10 05:43:32 +02:00
if ( attrs . disable ) {
scope . $parent . $watch ( $parse ( attrs . disable ) , function ( value ) {
scope . disabled = ! ! value ;
} ) ;
}
// Deprecation support of "disabled" parameter
// fix(tab): IE9 disabled attr renders grey text on enabled tab #2677
// This code is duplicated from the lines above to make it easy to remove once
// the feature has been completely deprecated
2014-02-21 02:13:31 +01:00
if ( attrs . disabled ) {
2015-05-10 05:43:32 +02:00
$log . warn ( 'Use of "disabled" attribute has been deprecated, please use "disable"' ) ;
2014-02-21 02:13:31 +01:00
scope . $parent . $watch ( $parse ( attrs . disabled ) , function ( value ) {
scope . disabled = ! ! value ;
} ) ;
}
scope . select = function ( ) {
2015-03-13 09:39:35 +01:00
if ( ! scope . disabled ) {
2014-02-21 02:13:31 +01:00
scope . active = true ;
}
} ;
tabsetCtrl . addTab ( scope ) ;
scope . $on ( '$destroy' , function ( ) {
tabsetCtrl . removeTab ( scope ) ;
} ) ;
//We need to transclude later, once the content container is ready.
//when this link happens, we're inside a tab heading.
scope . $transcludeFn = transclude ;
} ;
}
} ;
} ] )
. directive ( 'tabHeadingTransclude' , [ function ( ) {
return {
restrict : 'A' ,
require : '^tab' ,
link : function ( scope , elm , attrs , tabCtrl ) {
scope . $watch ( 'headingElement' , function updateHeadingElement ( heading ) {
if ( heading ) {
elm . html ( '' ) ;
elm . append ( heading ) ;
}
} ) ;
}
} ;
} ] )
. directive ( 'tabContentTransclude' , function ( ) {
return {
restrict : 'A' ,
require : '^tabset' ,
link : function ( scope , elm , attrs ) {
var tab = scope . $eval ( attrs . tabContentTransclude ) ;
//Now our tab is ready to be transcluded: both the tab heading area
//and the tab content area are loaded. Transclude 'em both.
tab . $transcludeFn ( tab . $parent , function ( contents ) {
angular . forEach ( contents , function ( node ) {
if ( isTabHeading ( node ) ) {
//Let tabHeadingTransclude know.
tab . headingElement = node ;
} else {
elm . append ( node ) ;
}
} ) ;
} ) ;
}
} ;
function isTabHeading ( node ) {
return node . tagName && (
node . hasAttribute ( 'tab-heading' ) ||
node . hasAttribute ( 'data-tab-heading' ) ||
node . tagName . toLowerCase ( ) === 'tab-heading' ||
node . tagName . toLowerCase ( ) === 'data-tab-heading'
) ;
}
} )
;
angular . module ( 'ui.bootstrap.timepicker' , [ ] )
. constant ( 'timepickerConfig' , {
hourStep : 1 ,
minuteStep : 1 ,
showMeridian : true ,
meridians : null ,
readonlyInput : false ,
2015-05-10 05:43:32 +02:00
mousewheel : true ,
arrowkeys : true
2014-02-21 02:13:31 +01:00
} )
2015-03-13 09:39:35 +01:00
. controller ( 'TimepickerController' , [ '$scope' , '$attrs' , '$parse' , '$log' , '$locale' , 'timepickerConfig' , function ( $scope , $attrs , $parse , $log , $locale , timepickerConfig ) {
var selected = new Date ( ) ,
ngModelCtrl = { $setViewValue : angular . noop } , // nullModelCtrl
meridians = angular . isDefined ( $attrs . meridians ) ? $scope . $parent . $eval ( $attrs . meridians ) : timepickerConfig . meridians || $locale . DATETIME _FORMATS . AMPMS ;
2014-02-21 02:13:31 +01:00
2015-03-13 09:39:35 +01:00
this . init = function ( ngModelCtrl _ , inputs ) {
ngModelCtrl = ngModelCtrl _ ;
ngModelCtrl . $render = this . render ;
2014-02-21 02:13:31 +01:00
2015-05-10 05:43:32 +02:00
ngModelCtrl . $formatters . unshift ( function ( modelValue ) {
return modelValue ? new Date ( modelValue ) : null ;
} ) ;
2015-03-13 09:39:35 +01:00
var hoursInputEl = inputs . eq ( 0 ) ,
minutesInputEl = inputs . eq ( 1 ) ;
2014-02-21 02:13:31 +01:00
2015-03-13 09:39:35 +01:00
var mousewheel = angular . isDefined ( $attrs . mousewheel ) ? $scope . $parent . $eval ( $attrs . mousewheel ) : timepickerConfig . mousewheel ;
if ( mousewheel ) {
this . setupMousewheelEvents ( hoursInputEl , minutesInputEl ) ;
}
2014-02-21 02:13:31 +01:00
2015-05-10 05:43:32 +02:00
var arrowkeys = angular . isDefined ( $attrs . arrowkeys ) ? $scope . $parent . $eval ( $attrs . arrowkeys ) : timepickerConfig . arrowkeys ;
if ( arrowkeys ) {
this . setupArrowkeyEvents ( hoursInputEl , minutesInputEl ) ;
}
2015-03-13 09:39:35 +01:00
$scope . readonlyInput = angular . isDefined ( $attrs . readonlyInput ) ? $scope . $parent . $eval ( $attrs . readonlyInput ) : timepickerConfig . readonlyInput ;
this . setupInputEvents ( hoursInputEl , minutesInputEl ) ;
} ;
2014-02-21 02:13:31 +01:00
2015-03-13 09:39:35 +01:00
var hourStep = timepickerConfig . hourStep ;
if ( $attrs . hourStep ) {
$scope . $parent . $watch ( $parse ( $attrs . hourStep ) , function ( value ) {
hourStep = parseInt ( value , 10 ) ;
} ) ;
}
2014-02-21 02:13:31 +01:00
2015-03-13 09:39:35 +01:00
var minuteStep = timepickerConfig . minuteStep ;
if ( $attrs . minuteStep ) {
$scope . $parent . $watch ( $parse ( $attrs . minuteStep ) , function ( value ) {
minuteStep = parseInt ( value , 10 ) ;
} ) ;
}
2014-02-21 02:13:31 +01:00
2015-03-13 09:39:35 +01:00
// 12H / 24H mode
$scope . showMeridian = timepickerConfig . showMeridian ;
if ( $attrs . showMeridian ) {
$scope . $parent . $watch ( $parse ( $attrs . showMeridian ) , function ( value ) {
$scope . showMeridian = ! ! value ;
if ( ngModelCtrl . $error . time ) {
// Evaluate from template
var hours = getHoursFromTemplate ( ) , minutes = getMinutesFromTemplate ( ) ;
if ( angular . isDefined ( hours ) && angular . isDefined ( minutes ) ) {
selected . setHours ( hours ) ;
refresh ( ) ;
2014-02-21 02:13:31 +01:00
}
2015-03-13 09:39:35 +01:00
} else {
updateTemplate ( ) ;
2014-02-21 02:13:31 +01:00
}
2015-03-13 09:39:35 +01:00
} ) ;
}
2014-02-21 02:13:31 +01:00
2015-03-13 09:39:35 +01:00
// Get $scope.hours in 24H mode if valid
function getHoursFromTemplate ( ) {
var hours = parseInt ( $scope . hours , 10 ) ;
var valid = ( $scope . showMeridian ) ? ( hours > 0 && hours < 13 ) : ( hours >= 0 && hours < 24 ) ;
if ( ! valid ) {
return undefined ;
}
2014-02-21 02:13:31 +01:00
2015-03-13 09:39:35 +01:00
if ( $scope . showMeridian ) {
if ( hours === 12 ) {
hours = 0 ;
}
if ( $scope . meridian === meridians [ 1 ] ) {
hours = hours + 12 ;
2014-02-21 02:13:31 +01:00
}
2015-03-13 09:39:35 +01:00
}
return hours ;
}
2014-02-21 02:13:31 +01:00
2015-03-13 09:39:35 +01:00
function getMinutesFromTemplate ( ) {
var minutes = parseInt ( $scope . minutes , 10 ) ;
return ( minutes >= 0 && minutes < 60 ) ? minutes : undefined ;
}
2014-02-21 02:13:31 +01:00
2015-03-13 09:39:35 +01:00
function pad ( value ) {
2015-05-10 05:43:32 +02:00
return ( angular . isDefined ( value ) && value . toString ( ) . length < 2 ) ? '0' + value : value . toString ( ) ;
2015-03-13 09:39:35 +01:00
}
2014-02-21 02:13:31 +01:00
2015-03-13 09:39:35 +01:00
// Respond on mousewheel spin
this . setupMousewheelEvents = function ( hoursInputEl , minutesInputEl ) {
var isScrollingUp = function ( e ) {
if ( e . originalEvent ) {
e = e . originalEvent ;
}
//pick correct delta variable depending on event
var delta = ( e . wheelDelta ) ? e . wheelDelta : - e . deltaY ;
return ( e . detail || delta > 0 ) ;
} ;
2014-02-21 02:13:31 +01:00
2015-03-13 09:39:35 +01:00
hoursInputEl . bind ( 'mousewheel wheel' , function ( e ) {
$scope . $apply ( ( isScrollingUp ( e ) ) ? $scope . incrementHours ( ) : $scope . decrementHours ( ) ) ;
e . preventDefault ( ) ;
} ) ;
2014-02-21 02:13:31 +01:00
2015-03-13 09:39:35 +01:00
minutesInputEl . bind ( 'mousewheel wheel' , function ( e ) {
$scope . $apply ( ( isScrollingUp ( e ) ) ? $scope . incrementMinutes ( ) : $scope . decrementMinutes ( ) ) ;
e . preventDefault ( ) ;
} ) ;
2014-02-21 02:13:31 +01:00
2015-03-13 09:39:35 +01:00
} ;
2014-02-21 02:13:31 +01:00
2015-05-10 05:43:32 +02:00
// Respond on up/down arrowkeys
this . setupArrowkeyEvents = function ( hoursInputEl , minutesInputEl ) {
hoursInputEl . bind ( 'keydown' , function ( e ) {
if ( e . which === 38 ) { // up
e . preventDefault ( ) ;
$scope . incrementHours ( ) ;
$scope . $apply ( ) ;
}
else if ( e . which === 40 ) { // down
e . preventDefault ( ) ;
$scope . decrementHours ( ) ;
$scope . $apply ( ) ;
}
} ) ;
minutesInputEl . bind ( 'keydown' , function ( e ) {
if ( e . which === 38 ) { // up
e . preventDefault ( ) ;
$scope . incrementMinutes ( ) ;
$scope . $apply ( ) ;
}
else if ( e . which === 40 ) { // down
e . preventDefault ( ) ;
$scope . decrementMinutes ( ) ;
$scope . $apply ( ) ;
}
} ) ;
} ;
2015-03-13 09:39:35 +01:00
this . setupInputEvents = function ( hoursInputEl , minutesInputEl ) {
if ( $scope . readonlyInput ) {
$scope . updateHours = angular . noop ;
$scope . updateMinutes = angular . noop ;
return ;
}
2014-02-21 02:13:31 +01:00
2015-03-13 09:39:35 +01:00
var invalidate = function ( invalidHours , invalidMinutes ) {
ngModelCtrl . $setViewValue ( null ) ;
ngModelCtrl . $setValidity ( 'time' , false ) ;
if ( angular . isDefined ( invalidHours ) ) {
$scope . invalidHours = invalidHours ;
}
if ( angular . isDefined ( invalidMinutes ) ) {
$scope . invalidMinutes = invalidMinutes ;
}
} ;
2014-02-21 02:13:31 +01:00
2015-03-13 09:39:35 +01:00
$scope . updateHours = function ( ) {
var hours = getHoursFromTemplate ( ) ;
2014-02-21 02:13:31 +01:00
2015-03-13 09:39:35 +01:00
if ( angular . isDefined ( hours ) ) {
selected . setHours ( hours ) ;
refresh ( 'h' ) ;
} else {
invalidate ( true ) ;
}
} ;
hoursInputEl . bind ( 'blur' , function ( e ) {
if ( ! $scope . invalidHours && $scope . hours < 10 ) {
$scope . $apply ( function ( ) {
$scope . hours = pad ( $scope . hours ) ;
2014-02-21 02:13:31 +01:00
} ) ;
2015-03-13 09:39:35 +01:00
}
} ) ;
2014-02-21 02:13:31 +01:00
2015-03-13 09:39:35 +01:00
$scope . updateMinutes = function ( ) {
var minutes = getMinutesFromTemplate ( ) ;
2014-02-21 02:13:31 +01:00
2015-03-13 09:39:35 +01:00
if ( angular . isDefined ( minutes ) ) {
selected . setMinutes ( minutes ) ;
refresh ( 'm' ) ;
} else {
invalidate ( undefined , true ) ;
}
} ;
2014-02-21 02:13:31 +01:00
2015-03-13 09:39:35 +01:00
minutesInputEl . bind ( 'blur' , function ( e ) {
if ( ! $scope . invalidMinutes && $scope . minutes < 10 ) {
$scope . $apply ( function ( ) {
$scope . minutes = pad ( $scope . minutes ) ;
2014-02-21 02:13:31 +01:00
} ) ;
}
2015-03-13 09:39:35 +01:00
} ) ;
2014-02-21 02:13:31 +01:00
2015-03-13 09:39:35 +01:00
} ;
2014-02-21 02:13:31 +01:00
2015-03-13 09:39:35 +01:00
this . render = function ( ) {
2015-05-10 05:43:32 +02:00
var date = ngModelCtrl . $viewValue ;
2014-02-21 02:13:31 +01:00
2015-03-13 09:39:35 +01:00
if ( isNaN ( date ) ) {
ngModelCtrl . $setValidity ( 'time' , false ) ;
$log . error ( 'Timepicker directive: "ng-model" value must be a Date object, a number of milliseconds since 01.01.1970 or a string representing an RFC2822 or ISO 8601 date.' ) ;
} else {
if ( date ) {
selected = date ;
2014-02-21 02:13:31 +01:00
}
2015-03-13 09:39:35 +01:00
makeValid ( ) ;
updateTemplate ( ) ;
}
} ;
2014-02-21 02:13:31 +01:00
2015-03-13 09:39:35 +01:00
// Call internally when we know that model is valid.
function refresh ( keyboardChange ) {
makeValid ( ) ;
ngModelCtrl . $setViewValue ( new Date ( selected ) ) ;
updateTemplate ( keyboardChange ) ;
}
2014-02-21 02:13:31 +01:00
2015-03-13 09:39:35 +01:00
function makeValid ( ) {
ngModelCtrl . $setValidity ( 'time' , true ) ;
$scope . invalidHours = false ;
$scope . invalidMinutes = false ;
}
2014-02-21 02:13:31 +01:00
2015-03-13 09:39:35 +01:00
function updateTemplate ( keyboardChange ) {
var hours = selected . getHours ( ) , minutes = selected . getMinutes ( ) ;
2014-02-21 02:13:31 +01:00
2015-03-13 09:39:35 +01:00
if ( $scope . showMeridian ) {
hours = ( hours === 0 || hours === 12 ) ? 12 : hours % 12 ; // Convert 24 to 12 hour system
}
2014-02-21 02:13:31 +01:00
2015-03-13 09:39:35 +01:00
$scope . hours = keyboardChange === 'h' ? hours : pad ( hours ) ;
2015-05-10 05:43:32 +02:00
if ( keyboardChange !== 'm' ) {
$scope . minutes = pad ( minutes ) ;
}
2015-03-13 09:39:35 +01:00
$scope . meridian = selected . getHours ( ) < 12 ? meridians [ 0 ] : meridians [ 1 ] ;
}
function addMinutes ( minutes ) {
var dt = new Date ( selected . getTime ( ) + minutes * 60000 ) ;
selected . setHours ( dt . getHours ( ) , dt . getMinutes ( ) ) ;
refresh ( ) ;
}
$scope . incrementHours = function ( ) {
addMinutes ( hourStep * 60 ) ;
} ;
$scope . decrementHours = function ( ) {
addMinutes ( - hourStep * 60 ) ;
} ;
$scope . incrementMinutes = function ( ) {
addMinutes ( minuteStep ) ;
} ;
$scope . decrementMinutes = function ( ) {
addMinutes ( - minuteStep ) ;
} ;
$scope . toggleMeridian = function ( ) {
addMinutes ( 12 * 60 * ( ( selected . getHours ( ) < 12 ) ? 1 : - 1 ) ) ;
} ;
} ] )
. directive ( 'timepicker' , function ( ) {
return {
restrict : 'EA' ,
require : [ 'timepicker' , '?^ngModel' ] ,
controller : 'TimepickerController' ,
replace : true ,
scope : { } ,
templateUrl : 'template/timepicker/timepicker.html' ,
link : function ( scope , element , attrs , ctrls ) {
var timepickerCtrl = ctrls [ 0 ] , ngModelCtrl = ctrls [ 1 ] ;
if ( ngModelCtrl ) {
timepickerCtrl . init ( ngModelCtrl , element . find ( 'input' ) ) ;
}
2014-02-21 02:13:31 +01:00
}
} ;
2015-03-13 09:39:35 +01:00
} ) ;
2014-02-21 02:13:31 +01:00
2015-05-10 05:43:32 +02:00
angular . module ( 'ui.bootstrap.transition' , [ ] )
. value ( '$transitionSuppressDeprecated' , false )
/ * *
* $transition service provides a consistent interface to trigger CSS 3 transitions and to be informed when they complete .
* @ param { DOMElement } element The DOMElement that will be animated .
* @ param { string | object | function } trigger The thing that will cause the transition to start :
* - As a string , it represents the css class to be added to the element .
* - As an object , it represents a hash of style attributes to be applied to the element .
* - As a function , it represents a function to be called that will cause the transition to occur .
* @ return { Promise } A promise that is resolved when the transition finishes .
* /
. factory ( '$transition' , [
'$q' , '$timeout' , '$rootScope' , '$log' , '$transitionSuppressDeprecated' ,
function ( $q , $timeout , $rootScope , $log , $transitionSuppressDeprecated ) {
if ( ! $transitionSuppressDeprecated ) {
$log . warn ( '$transition is now deprecated. Use $animate from ngAnimate instead.' ) ;
}
var $transition = function ( element , trigger , options ) {
options = options || { } ;
var deferred = $q . defer ( ) ;
var endEventName = $transition [ options . animation ? 'animationEndEventName' : 'transitionEndEventName' ] ;
var transitionEndHandler = function ( event ) {
$rootScope . $apply ( function ( ) {
element . unbind ( endEventName , transitionEndHandler ) ;
deferred . resolve ( element ) ;
} ) ;
} ;
if ( endEventName ) {
element . bind ( endEventName , transitionEndHandler ) ;
}
// Wrap in a timeout to allow the browser time to update the DOM before the transition is to occur
$timeout ( function ( ) {
if ( angular . isString ( trigger ) ) {
element . addClass ( trigger ) ;
} else if ( angular . isFunction ( trigger ) ) {
trigger ( element ) ;
} else if ( angular . isObject ( trigger ) ) {
element . css ( trigger ) ;
}
//If browser does not support transitions, instantly resolve
if ( ! endEventName ) {
deferred . resolve ( element ) ;
}
} ) ;
// Add our custom cancel function to the promise that is returned
// We can call this if we are about to run a new transition, which we know will prevent this transition from ending,
// i.e. it will therefore never raise a transitionEnd event for that transition
deferred . promise . cancel = function ( ) {
if ( endEventName ) {
element . unbind ( endEventName , transitionEndHandler ) ;
}
deferred . reject ( 'Transition cancelled' ) ;
} ;
return deferred . promise ;
} ;
// Work out the name of the transitionEnd event
var transElement = document . createElement ( 'trans' ) ;
var transitionEndEventNames = {
'WebkitTransition' : 'webkitTransitionEnd' ,
'MozTransition' : 'transitionend' ,
'OTransition' : 'oTransitionEnd' ,
'transition' : 'transitionend'
} ;
var animationEndEventNames = {
'WebkitTransition' : 'webkitAnimationEnd' ,
'MozTransition' : 'animationend' ,
'OTransition' : 'oAnimationEnd' ,
'transition' : 'animationend'
} ;
function findEndEventName ( endEventNames ) {
for ( var name in endEventNames ) {
if ( transElement . style [ name ] !== undefined ) {
return endEventNames [ name ] ;
}
}
}
$transition . transitionEndEventName = findEndEventName ( transitionEndEventNames ) ;
$transition . animationEndEventName = findEndEventName ( animationEndEventNames ) ;
return $transition ;
} ] ) ;
2014-02-21 02:13:31 +01:00
angular . module ( 'ui.bootstrap.typeahead' , [ 'ui.bootstrap.position' , 'ui.bootstrap.bindHtml' ] )
/ * *
* A helper service that can parse typeahead ' s syntax ( string provided by users )
* Extracted to a separate service for ease of unit testing
* /
. factory ( 'typeaheadParser' , [ '$parse' , function ( $parse ) {
// 00000111000000000000022200000000000000003333333333333330000000000044000
2015-03-13 09:39:35 +01:00
var TYPEAHEAD _REGEXP = /^\s*([\s\S]+?)(?:\s+as\s+([\s\S]+?))?\s+for\s+(?:([\$\w][\$\w\d]*))\s+in\s+([\s\S]+?)$/ ;
2014-02-21 02:13:31 +01:00
return {
parse : function ( input ) {
2015-03-13 09:39:35 +01:00
var match = input . match ( TYPEAHEAD _REGEXP ) ;
2014-02-21 02:13:31 +01:00
if ( ! match ) {
throw new Error (
2015-03-13 09:39:35 +01:00
'Expected typeahead specification in form of "_modelValue_ (as _label_)? for _item_ in _collection_"' +
' but got "' + input + '".' ) ;
2014-02-21 02:13:31 +01:00
}
return {
itemName : match [ 3 ] ,
source : $parse ( match [ 4 ] ) ,
viewMapper : $parse ( match [ 2 ] || match [ 1 ] ) ,
modelMapper : $parse ( match [ 1 ] )
} ;
}
} ;
} ] )
. directive ( 'typeahead' , [ '$compile' , '$parse' , '$q' , '$timeout' , '$document' , '$position' , 'typeaheadParser' ,
function ( $compile , $parse , $q , $timeout , $document , $position , typeaheadParser ) {
var HOT _KEYS = [ 9 , 13 , 27 , 38 , 40 ] ;
return {
require : 'ngModel' ,
link : function ( originalScope , element , attrs , modelCtrl ) {
//SUPPORTED ATTRIBUTES (OPTIONS)
//minimal no of characters that needs to be entered before typeahead kicks-in
var minSearch = originalScope . $eval ( attrs . typeaheadMinLength ) || 1 ;
2015-05-10 05:43:32 +02:00
//minimal wait time after last character typed before typeahead kicks-in
2014-02-21 02:13:31 +01:00
var waitTime = originalScope . $eval ( attrs . typeaheadWaitMs ) || 0 ;
//should it restrict model values to the ones selected from the popup only?
var isEditable = originalScope . $eval ( attrs . typeaheadEditable ) !== false ;
//binding to a variable that indicates if matches are being retrieved asynchronously
var isLoadingSetter = $parse ( attrs . typeaheadLoading ) . assign || angular . noop ;
//a callback executed when a match is selected
var onSelectCallback = $parse ( attrs . typeaheadOnSelect ) ;
var inputFormatter = attrs . typeaheadInputFormatter ? $parse ( attrs . typeaheadInputFormatter ) : undefined ;
2015-03-13 09:39:35 +01:00
var appendToBody = attrs . typeaheadAppendToBody ? originalScope . $eval ( attrs . typeaheadAppendToBody ) : false ;
var focusFirst = originalScope . $eval ( attrs . typeaheadFocusFirst ) !== false ;
2014-02-21 02:13:31 +01:00
//INTERNAL VARIABLES
//model setter executed upon match selection
var $setModelValue = $parse ( attrs . ngModel ) . assign ;
//expressions used by typeahead
var parserResult = typeaheadParser . parse ( attrs . typeahead ) ;
var hasFocus ;
2015-03-13 09:39:35 +01:00
//create a child scope for the typeahead directive so we are not polluting original scope
//with typeahead-specific data (matches, query etc.)
var scope = originalScope . $new ( ) ;
originalScope . $on ( '$destroy' , function ( ) {
scope . $destroy ( ) ;
} ) ;
// WAI-ARIA
var popupId = 'typeahead-' + scope . $id + '-' + Math . floor ( Math . random ( ) * 10000 ) ;
element . attr ( {
'aria-autocomplete' : 'list' ,
'aria-expanded' : false ,
'aria-owns' : popupId
} ) ;
2014-02-21 02:13:31 +01:00
//pop-up element used to display matches
var popUpEl = angular . element ( '<div typeahead-popup></div>' ) ;
popUpEl . attr ( {
2015-03-13 09:39:35 +01:00
id : popupId ,
2014-02-21 02:13:31 +01:00
matches : 'matches' ,
active : 'activeIdx' ,
select : 'select(activeIdx)' ,
query : 'query' ,
position : 'position'
} ) ;
//custom item template
if ( angular . isDefined ( attrs . typeaheadTemplateUrl ) ) {
popUpEl . attr ( 'template-url' , attrs . typeaheadTemplateUrl ) ;
}
var resetMatches = function ( ) {
scope . matches = [ ] ;
scope . activeIdx = - 1 ;
2015-03-13 09:39:35 +01:00
element . attr ( 'aria-expanded' , false ) ;
} ;
var getMatchId = function ( index ) {
return popupId + '-option-' + index ;
2014-02-21 02:13:31 +01:00
} ;
2015-03-13 09:39:35 +01:00
// Indicate that the specified match is the active (pre-selected) item in the list owned by this typeahead.
// This attribute is added or removed automatically when the `activeIdx` changes.
scope . $watch ( 'activeIdx' , function ( index ) {
if ( index < 0 ) {
element . removeAttr ( 'aria-activedescendant' ) ;
} else {
element . attr ( 'aria-activedescendant' , getMatchId ( index ) ) ;
}
} ) ;
2014-02-21 02:13:31 +01:00
var getMatchesAsync = function ( inputValue ) {
var locals = { $viewValue : inputValue } ;
isLoadingSetter ( originalScope , true ) ;
$q . when ( parserResult . source ( originalScope , locals ) ) . then ( function ( matches ) {
//it might happen that several async queries were in progress if a user were typing fast
//but we are interested only in responses that correspond to the current view value
2015-03-13 09:39:35 +01:00
var onCurrentRequest = ( inputValue === modelCtrl . $viewValue ) ;
if ( onCurrentRequest && hasFocus ) {
2015-05-10 05:43:32 +02:00
if ( matches && matches . length > 0 ) {
2014-02-21 02:13:31 +01:00
2015-03-13 09:39:35 +01:00
scope . activeIdx = focusFirst ? 0 : - 1 ;
2014-02-21 02:13:31 +01:00
scope . matches . length = 0 ;
//transform labels
for ( var i = 0 ; i < matches . length ; i ++ ) {
locals [ parserResult . itemName ] = matches [ i ] ;
scope . matches . push ( {
2015-03-13 09:39:35 +01:00
id : getMatchId ( i ) ,
2014-02-21 02:13:31 +01:00
label : parserResult . viewMapper ( scope , locals ) ,
model : matches [ i ]
} ) ;
}
scope . query = inputValue ;
//position pop-up with matches - we need to re-calculate its position each time we are opening a window
//with matches as a pop-up might be absolute-positioned and position of an input might have changed on a page
//due to other elements being rendered
scope . position = appendToBody ? $position . offset ( element ) : $position . position ( element ) ;
scope . position . top = scope . position . top + element . prop ( 'offsetHeight' ) ;
2015-03-13 09:39:35 +01:00
element . attr ( 'aria-expanded' , true ) ;
2014-02-21 02:13:31 +01:00
} else {
resetMatches ( ) ;
}
2015-03-13 09:39:35 +01:00
}
if ( onCurrentRequest ) {
2014-02-21 02:13:31 +01:00
isLoadingSetter ( originalScope , false ) ;
}
} , function ( ) {
resetMatches ( ) ;
isLoadingSetter ( originalScope , false ) ;
} ) ;
} ;
resetMatches ( ) ;
//we need to propagate user's query so we can higlight matches
scope . query = undefined ;
2015-05-10 05:43:32 +02:00
//Declare the timeout promise var outside the function scope so that stacked calls can be cancelled later
2014-02-21 02:13:31 +01:00
var timeoutPromise ;
2015-03-13 09:39:35 +01:00
var scheduleSearchWithTimeout = function ( inputValue ) {
timeoutPromise = $timeout ( function ( ) {
getMatchesAsync ( inputValue ) ;
} , waitTime ) ;
} ;
var cancelPreviousTimeout = function ( ) {
if ( timeoutPromise ) {
$timeout . cancel ( timeoutPromise ) ;
}
} ;
2014-02-21 02:13:31 +01:00
//plug into $parsers pipeline to open a typeahead on view changes initiated from DOM
//$parsers kick-in on all the changes coming from the view as well as manually triggered by $setViewValue
modelCtrl . $parsers . unshift ( function ( inputValue ) {
hasFocus = true ;
if ( inputValue && inputValue . length >= minSearch ) {
if ( waitTime > 0 ) {
2015-03-13 09:39:35 +01:00
cancelPreviousTimeout ( ) ;
scheduleSearchWithTimeout ( inputValue ) ;
2014-02-21 02:13:31 +01:00
} else {
getMatchesAsync ( inputValue ) ;
}
} else {
isLoadingSetter ( originalScope , false ) ;
2015-03-13 09:39:35 +01:00
cancelPreviousTimeout ( ) ;
2014-02-21 02:13:31 +01:00
resetMatches ( ) ;
}
if ( isEditable ) {
return inputValue ;
} else {
if ( ! inputValue ) {
// Reset in case user had typed something previously.
modelCtrl . $setValidity ( 'editable' , true ) ;
return inputValue ;
} else {
modelCtrl . $setValidity ( 'editable' , false ) ;
return undefined ;
}
}
} ) ;
modelCtrl . $formatters . push ( function ( modelValue ) {
var candidateViewValue , emptyViewValue ;
var locals = { } ;
2015-05-10 05:43:32 +02:00
// The validity may be set to false via $parsers (see above) if
// the model is restricted to selected values. If the model
// is set manually it is considered to be valid.
if ( ! isEditable ) {
modelCtrl . $setValidity ( 'editable' , true ) ;
}
2014-02-21 02:13:31 +01:00
if ( inputFormatter ) {
2015-03-13 09:39:35 +01:00
locals . $model = modelValue ;
2014-02-21 02:13:31 +01:00
return inputFormatter ( originalScope , locals ) ;
} else {
//it might happen that we don't have enough info to properly render input value
//we need to check for this situation and simply return model value if we can't apply custom formatting
locals [ parserResult . itemName ] = modelValue ;
candidateViewValue = parserResult . viewMapper ( originalScope , locals ) ;
locals [ parserResult . itemName ] = undefined ;
emptyViewValue = parserResult . viewMapper ( originalScope , locals ) ;
return candidateViewValue !== emptyViewValue ? candidateViewValue : modelValue ;
}
} ) ;
scope . select = function ( activeIdx ) {
//called from within the $digest() cycle
var locals = { } ;
var model , item ;
locals [ parserResult . itemName ] = item = scope . matches [ activeIdx ] . model ;
model = parserResult . modelMapper ( originalScope , locals ) ;
$setModelValue ( originalScope , model ) ;
modelCtrl . $setValidity ( 'editable' , true ) ;
2015-05-10 05:43:32 +02:00
modelCtrl . $setValidity ( 'parse' , true ) ;
2014-02-21 02:13:31 +01:00
onSelectCallback ( originalScope , {
$item : item ,
$model : model ,
$label : parserResult . viewMapper ( originalScope , locals )
} ) ;
resetMatches ( ) ;
2015-03-13 09:39:35 +01:00
//return focus to the input element if a match was selected via a mouse click event
// use timeout to avoid $rootScope:inprog error
$timeout ( function ( ) { element [ 0 ] . focus ( ) ; } , 0 , false ) ;
2014-02-21 02:13:31 +01:00
} ;
//bind keyboard events: arrows up(38) / down(40), enter(13) and tab(9), esc(27)
element . bind ( 'keydown' , function ( evt ) {
//typeahead is open and an "interesting" key was pressed
if ( scope . matches . length === 0 || HOT _KEYS . indexOf ( evt . which ) === - 1 ) {
return ;
}
2015-03-13 09:39:35 +01:00
// if there's nothing selected (i.e. focusFirst) and enter is hit, don't do anything
if ( scope . activeIdx == - 1 && ( evt . which === 13 || evt . which === 9 ) ) {
return ;
}
2014-02-21 02:13:31 +01:00
evt . preventDefault ( ) ;
if ( evt . which === 40 ) {
scope . activeIdx = ( scope . activeIdx + 1 ) % scope . matches . length ;
scope . $digest ( ) ;
} else if ( evt . which === 38 ) {
2015-03-13 09:39:35 +01:00
scope . activeIdx = ( scope . activeIdx > 0 ? scope . activeIdx : scope . matches . length ) - 1 ;
2014-02-21 02:13:31 +01:00
scope . $digest ( ) ;
} else if ( evt . which === 13 || evt . which === 9 ) {
scope . $apply ( function ( ) {
scope . select ( scope . activeIdx ) ;
} ) ;
} else if ( evt . which === 27 ) {
evt . stopPropagation ( ) ;
resetMatches ( ) ;
scope . $digest ( ) ;
}
} ) ;
element . bind ( 'blur' , function ( evt ) {
hasFocus = false ;
} ) ;
// Keep reference to click handler to unbind it.
var dismissClickHandler = function ( evt ) {
if ( element [ 0 ] !== evt . target ) {
resetMatches ( ) ;
scope . $digest ( ) ;
}
} ;
$document . bind ( 'click' , dismissClickHandler ) ;
originalScope . $on ( '$destroy' , function ( ) {
$document . unbind ( 'click' , dismissClickHandler ) ;
2015-03-13 09:39:35 +01:00
if ( appendToBody ) {
$popup . remove ( ) ;
}
2015-05-10 05:43:32 +02:00
// Prevent jQuery cache memory leak
popUpEl . remove ( ) ;
2014-02-21 02:13:31 +01:00
} ) ;
var $popup = $compile ( popUpEl ) ( scope ) ;
2015-05-10 05:43:32 +02:00
2015-03-13 09:39:35 +01:00
if ( appendToBody ) {
2014-02-21 02:13:31 +01:00
$document . find ( 'body' ) . append ( $popup ) ;
} else {
element . after ( $popup ) ;
}
}
} ;
} ] )
. directive ( 'typeaheadPopup' , function ( ) {
return {
restrict : 'EA' ,
scope : {
matches : '=' ,
query : '=' ,
active : '=' ,
position : '=' ,
select : '&'
} ,
replace : true ,
templateUrl : 'template/typeahead/typeahead-popup.html' ,
link : function ( scope , element , attrs ) {
scope . templateUrl = attrs . templateUrl ;
scope . isOpen = function ( ) {
return scope . matches . length > 0 ;
} ;
scope . isActive = function ( matchIdx ) {
return scope . active == matchIdx ;
} ;
scope . selectActive = function ( matchIdx ) {
scope . active = matchIdx ;
} ;
scope . selectMatch = function ( activeIdx ) {
scope . select ( { activeIdx : activeIdx } ) ;
} ;
}
} ;
} )
2015-05-10 05:43:32 +02:00
. directive ( 'typeaheadMatch' , [ '$templateRequest' , '$compile' , '$parse' , function ( $templateRequest , $compile , $parse ) {
2014-02-21 02:13:31 +01:00
return {
restrict : 'EA' ,
scope : {
index : '=' ,
match : '=' ,
query : '='
} ,
link : function ( scope , element , attrs ) {
var tplUrl = $parse ( attrs . templateUrl ) ( scope . $parent ) || 'template/typeahead/typeahead-match.html' ;
2015-05-10 05:43:32 +02:00
$templateRequest ( tplUrl ) . then ( function ( tplContent ) {
$compile ( tplContent . trim ( ) ) ( scope , function ( clonedElement ) {
element . replaceWith ( clonedElement ) ;
} ) ;
2014-02-21 02:13:31 +01:00
} ) ;
}
} ;
} ] )
. filter ( 'typeaheadHighlight' , function ( ) {
function escapeRegexp ( queryToEscape ) {
2015-03-13 09:39:35 +01:00
return queryToEscape . replace ( /([.?*+^$[\]\\(){}|-])/g , '\\$1' ) ;
2014-02-21 02:13:31 +01:00
}
return function ( matchItem , query ) {
2015-03-13 09:39:35 +01:00
return query ? ( '' + matchItem ) . replace ( new RegExp ( escapeRegexp ( query ) , 'gi' ) , '<strong>$&</strong>' ) : matchItem ;
2014-02-21 02:13:31 +01:00
} ;
} ) ;
2015-03-13 09:39:35 +01:00
2014-02-21 02:13:31 +01:00
angular . module ( "template/accordion/accordion-group.html" , [ ] ) . run ( [ "$templateCache" , function ( $templateCache ) {
$templateCache . put ( "template/accordion/accordion-group.html" ,
2015-03-13 09:39:35 +01:00
"<div class=\"panel panel-default\">\n" +
" <div class=\"panel-heading\">\n" +
" <h4 class=\"panel-title\">\n" +
2015-05-10 05:43:32 +02:00
" <a href=\"javascript:void(0)\" tabindex=\"0\" class=\"accordion-toggle\" ng-click=\"toggleOpen()\" accordion-transclude=\"heading\"><span ng-class=\"{'text-muted': isDisabled}\">{{heading}}</span></a>\n" +
2015-03-13 09:39:35 +01:00
" </h4>\n" +
" </div>\n" +
2015-05-10 05:43:32 +02:00
" <div class=\"panel-collapse collapse\" collapse=\"!isOpen\">\n" +
" <div class=\"panel-body\" ng-transclude></div>\n" +
2015-03-13 09:39:35 +01:00
" </div>\n" +
"</div>\n" +
"" ) ;
2014-02-21 02:13:31 +01:00
} ] ) ;
angular . module ( "template/accordion/accordion.html" , [ ] ) . run ( [ "$templateCache" , function ( $templateCache ) {
$templateCache . put ( "template/accordion/accordion.html" ,
2015-03-13 09:39:35 +01:00
"<div class=\"panel-group\" ng-transclude></div>" ) ;
2014-02-21 02:13:31 +01:00
} ] ) ;
angular . module ( "template/alert/alert.html" , [ ] ) . run ( [ "$templateCache" , function ( $templateCache ) {
$templateCache . put ( "template/alert/alert.html" ,
2015-03-13 09:39:35 +01:00
"<div class=\"alert\" ng-class=\"['alert-' + (type || 'warning'), closeable ? 'alert-dismissable' : null]\" role=\"alert\">\n" +
" <button ng-show=\"closeable\" type=\"button\" class=\"close\" ng-click=\"close()\">\n" +
" <span aria-hidden=\"true\">×</span>\n" +
" <span class=\"sr-only\">Close</span>\n" +
" </button>\n" +
2014-02-21 02:13:31 +01:00
" <div ng-transclude></div>\n" +
"</div>\n" +
"" ) ;
} ] ) ;
angular . module ( "template/carousel/carousel.html" , [ ] ) . run ( [ "$templateCache" , function ( $templateCache ) {
$templateCache . put ( "template/carousel/carousel.html" ,
2015-03-13 09:39:35 +01:00
"<div ng-mouseenter=\"pause()\" ng-mouseleave=\"play()\" class=\"carousel\" ng-swipe-right=\"prev()\" ng-swipe-left=\"next()\">\n" +
" <ol class=\"carousel-indicators\" ng-show=\"slides.length > 1\">\n" +
2015-05-10 05:43:32 +02:00
" <li ng-repeat=\"slide in slides | orderBy:'index' track by $index\" ng-class=\"{active: isActive(slide)}\" ng-click=\"select(slide)\"></li>\n" +
2014-02-21 02:13:31 +01:00
" </ol>\n" +
" <div class=\"carousel-inner\" ng-transclude></div>\n" +
2015-03-13 09:39:35 +01:00
" <a class=\"left carousel-control\" ng-click=\"prev()\" ng-show=\"slides.length > 1\"><span class=\"glyphicon glyphicon-chevron-left\"></span></a>\n" +
" <a class=\"right carousel-control\" ng-click=\"next()\" ng-show=\"slides.length > 1\"><span class=\"glyphicon glyphicon-chevron-right\"></span></a>\n" +
2014-02-21 02:13:31 +01:00
"</div>\n" +
"" ) ;
} ] ) ;
angular . module ( "template/carousel/slide.html" , [ ] ) . run ( [ "$templateCache" , function ( $templateCache ) {
$templateCache . put ( "template/carousel/slide.html" ,
"<div ng-class=\"{\n" +
2015-05-10 05:43:32 +02:00
" 'active': active\n" +
2015-03-13 09:39:35 +01:00
" }\" class=\"item text-center\" ng-transclude></div>\n" +
2014-02-21 02:13:31 +01:00
"" ) ;
} ] ) ;
angular . module ( "template/datepicker/datepicker.html" , [ ] ) . run ( [ "$templateCache" , function ( $templateCache ) {
$templateCache . put ( "template/datepicker/datepicker.html" ,
2015-03-13 09:39:35 +01:00
"<div ng-switch=\"datepickerMode\" role=\"application\" ng-keydown=\"keydown($event)\">\n" +
" <daypicker ng-switch-when=\"day\" tabindex=\"0\"></daypicker>\n" +
" <monthpicker ng-switch-when=\"month\" tabindex=\"0\"></monthpicker>\n" +
" <yearpicker ng-switch-when=\"year\" tabindex=\"0\"></yearpicker>\n" +
"</div>" ) ;
} ] ) ;
angular . module ( "template/datepicker/day.html" , [ ] ) . run ( [ "$templateCache" , function ( $templateCache ) {
$templateCache . put ( "template/datepicker/day.html" ,
"<table role=\"grid\" aria-labelledby=\"{{uniqueId}}-title\" aria-activedescendant=\"{{activeDateId}}\">\n" +
2014-02-21 02:13:31 +01:00
" <thead>\n" +
2015-03-13 09:39:35 +01:00
" <tr>\n" +
" <th><button type=\"button\" class=\"btn btn-default btn-sm pull-left\" ng-click=\"move(-1)\" tabindex=\"-1\"><i class=\"glyphicon glyphicon-chevron-left\"></i></button></th>\n" +
2015-05-10 05:43:32 +02:00
" <th colspan=\"{{5 + showWeeks}}\"><button id=\"{{uniqueId}}-title\" role=\"heading\" aria-live=\"assertive\" aria-atomic=\"true\" type=\"button\" class=\"btn btn-default btn-sm\" ng-click=\"toggleMode()\" ng-disabled=\"datepickerMode === maxMode\" tabindex=\"-1\" style=\"width:100%;\"><strong>{{title}}</strong></button></th>\n" +
2015-03-13 09:39:35 +01:00
" <th><button type=\"button\" class=\"btn btn-default btn-sm pull-right\" ng-click=\"move(1)\" tabindex=\"-1\"><i class=\"glyphicon glyphicon-chevron-right\"></i></button></th>\n" +
2014-02-21 02:13:31 +01:00
" </tr>\n" +
2015-03-13 09:39:35 +01:00
" <tr>\n" +
" <th ng-show=\"showWeeks\" class=\"text-center\"></th>\n" +
" <th ng-repeat=\"label in labels track by $index\" class=\"text-center\"><small aria-label=\"{{label.full}}\">{{label.abbr}}</small></th>\n" +
" </tr>\n" +
" </thead>\n" +
" <tbody>\n" +
" <tr ng-repeat=\"row in rows track by $index\">\n" +
" <td ng-show=\"showWeeks\" class=\"text-center h6\"><em>{{ weekNumbers[$index] }}</em></td>\n" +
2015-05-10 05:43:32 +02:00
" <td ng-repeat=\"dt in row track by dt.date\" class=\"text-center\" role=\"gridcell\" id=\"{{dt.uid}}\" aria-disabled=\"{{!!dt.disabled}}\" ng-class=\"dt.customClass\">\n" +
2015-03-13 09:39:35 +01:00
" <button type=\"button\" style=\"width:100%;\" class=\"btn btn-default btn-sm\" ng-class=\"{'btn-info': dt.selected, active: isActive(dt)}\" ng-click=\"select(dt.date)\" ng-disabled=\"dt.disabled\" tabindex=\"-1\"><span ng-class=\"{'text-muted': dt.secondary, 'text-info': dt.current}\">{{dt.label}}</span></button>\n" +
" </td>\n" +
" </tr>\n" +
" </tbody>\n" +
"</table>\n" +
"" ) ;
} ] ) ;
angular . module ( "template/datepicker/month.html" , [ ] ) . run ( [ "$templateCache" , function ( $templateCache ) {
$templateCache . put ( "template/datepicker/month.html" ,
"<table role=\"grid\" aria-labelledby=\"{{uniqueId}}-title\" aria-activedescendant=\"{{activeDateId}}\">\n" +
" <thead>\n" +
" <tr>\n" +
" <th><button type=\"button\" class=\"btn btn-default btn-sm pull-left\" ng-click=\"move(-1)\" tabindex=\"-1\"><i class=\"glyphicon glyphicon-chevron-left\"></i></button></th>\n" +
2015-05-10 05:43:32 +02:00
" <th><button id=\"{{uniqueId}}-title\" role=\"heading\" aria-live=\"assertive\" aria-atomic=\"true\" type=\"button\" class=\"btn btn-default btn-sm\" ng-click=\"toggleMode()\" ng-disabled=\"datepickerMode === maxMode\" tabindex=\"-1\" style=\"width:100%;\"><strong>{{title}}</strong></button></th>\n" +
2015-03-13 09:39:35 +01:00
" <th><button type=\"button\" class=\"btn btn-default btn-sm pull-right\" ng-click=\"move(1)\" tabindex=\"-1\"><i class=\"glyphicon glyphicon-chevron-right\"></i></button></th>\n" +
2014-02-21 02:13:31 +01:00
" </tr>\n" +
" </thead>\n" +
" <tbody>\n" +
2015-03-13 09:39:35 +01:00
" <tr ng-repeat=\"row in rows track by $index\">\n" +
" <td ng-repeat=\"dt in row track by dt.date\" class=\"text-center\" role=\"gridcell\" id=\"{{dt.uid}}\" aria-disabled=\"{{!!dt.disabled}}\">\n" +
" <button type=\"button\" style=\"width:100%;\" class=\"btn btn-default\" ng-class=\"{'btn-info': dt.selected, active: isActive(dt)}\" ng-click=\"select(dt.date)\" ng-disabled=\"dt.disabled\" tabindex=\"-1\"><span ng-class=\"{'text-info': dt.current}\">{{dt.label}}</span></button>\n" +
2014-02-21 02:13:31 +01:00
" </td>\n" +
" </tr>\n" +
" </tbody>\n" +
"</table>\n" +
"" ) ;
} ] ) ;
angular . module ( "template/datepicker/popup.html" , [ ] ) . run ( [ "$templateCache" , function ( $templateCache ) {
$templateCache . put ( "template/datepicker/popup.html" ,
2015-03-13 09:39:35 +01:00
"<ul class=\"dropdown-menu\" ng-style=\"{display: (isOpen && 'block') || 'none', top: position.top+'px', left: position.left+'px'}\" ng-keydown=\"keydown($event)\">\n" +
2015-05-10 05:43:32 +02:00
" <li ng-transclude></li>\n" +
" <li ng-if=\"showButtonBar\" style=\"padding:10px 9px 2px\">\n" +
" <span class=\"btn-group pull-left\">\n" +
" <button type=\"button\" class=\"btn btn-sm btn-info\" ng-click=\"select('today')\">{{ getText('current') }}</button>\n" +
" <button type=\"button\" class=\"btn btn-sm btn-danger\" ng-click=\"select(null)\">{{ getText('clear') }}</button>\n" +
" </span>\n" +
" <button type=\"button\" class=\"btn btn-sm btn-success pull-right\" ng-click=\"close()\">{{ getText('close') }}</button>\n" +
" </li>\n" +
2014-02-21 02:13:31 +01:00
"</ul>\n" +
"" ) ;
} ] ) ;
2015-03-13 09:39:35 +01:00
angular . module ( "template/datepicker/year.html" , [ ] ) . run ( [ "$templateCache" , function ( $templateCache ) {
$templateCache . put ( "template/datepicker/year.html" ,
"<table role=\"grid\" aria-labelledby=\"{{uniqueId}}-title\" aria-activedescendant=\"{{activeDateId}}\">\n" +
" <thead>\n" +
" <tr>\n" +
" <th><button type=\"button\" class=\"btn btn-default btn-sm pull-left\" ng-click=\"move(-1)\" tabindex=\"-1\"><i class=\"glyphicon glyphicon-chevron-left\"></i></button></th>\n" +
2015-05-10 05:43:32 +02:00
" <th colspan=\"3\"><button id=\"{{uniqueId}}-title\" role=\"heading\" aria-live=\"assertive\" aria-atomic=\"true\" type=\"button\" class=\"btn btn-default btn-sm\" ng-click=\"toggleMode()\" ng-disabled=\"datepickerMode === maxMode\" tabindex=\"-1\" style=\"width:100%;\"><strong>{{title}}</strong></button></th>\n" +
2015-03-13 09:39:35 +01:00
" <th><button type=\"button\" class=\"btn btn-default btn-sm pull-right\" ng-click=\"move(1)\" tabindex=\"-1\"><i class=\"glyphicon glyphicon-chevron-right\"></i></button></th>\n" +
" </tr>\n" +
" </thead>\n" +
" <tbody>\n" +
" <tr ng-repeat=\"row in rows track by $index\">\n" +
" <td ng-repeat=\"dt in row track by dt.date\" class=\"text-center\" role=\"gridcell\" id=\"{{dt.uid}}\" aria-disabled=\"{{!!dt.disabled}}\">\n" +
" <button type=\"button\" style=\"width:100%;\" class=\"btn btn-default\" ng-class=\"{'btn-info': dt.selected, active: isActive(dt)}\" ng-click=\"select(dt.date)\" ng-disabled=\"dt.disabled\" tabindex=\"-1\"><span ng-class=\"{'text-info': dt.current}\">{{dt.label}}</span></button>\n" +
" </td>\n" +
" </tr>\n" +
" </tbody>\n" +
"</table>\n" +
"" ) ;
} ] ) ;
2014-02-21 02:13:31 +01:00
angular . module ( "template/modal/backdrop.html" , [ ] ) . run ( [ "$templateCache" , function ( $templateCache ) {
$templateCache . put ( "template/modal/backdrop.html" ,
2015-05-10 05:43:32 +02:00
"<div class=\"modal-backdrop\"\n" +
" modal-animation-class=\"fade\"\n" +
2015-03-13 09:39:35 +01:00
" ng-class=\"{in: animate}\"\n" +
" ng-style=\"{'z-index': 1040 + (index && 1 || 0) + index*10}\"\n" +
"></div>\n" +
"" ) ;
2014-02-21 02:13:31 +01:00
} ] ) ;
angular . module ( "template/modal/window.html" , [ ] ) . run ( [ "$templateCache" , function ( $templateCache ) {
$templateCache . put ( "template/modal/window.html" ,
2015-05-10 05:43:32 +02:00
"<div modal-render=\"{{$isRendered}}\" tabindex=\"-1\" role=\"dialog\" class=\"modal\"\n" +
" modal-animation-class=\"fade\"\n" +
" ng-class=\"{in: animate}\" ng-style=\"{'z-index': 1050 + index*10, display: 'block'}\" ng-click=\"close($event)\">\n" +
" <div class=\"modal-dialog\" ng-class=\"size ? 'modal-' + size : ''\"><div class=\"modal-content\" modal-transclude></div></div>\n" +
"</div>\n" +
"" ) ;
2014-02-21 02:13:31 +01:00
} ] ) ;
angular . module ( "template/pagination/pager.html" , [ ] ) . run ( [ "$templateCache" , function ( $templateCache ) {
$templateCache . put ( "template/pagination/pager.html" ,
2015-03-13 09:39:35 +01:00
"<ul class=\"pager\">\n" +
2015-05-10 05:43:32 +02:00
" <li ng-class=\"{disabled: noPrevious(), previous: align}\"><a href ng-click=\"selectPage(page - 1, $event)\">{{getText('previous')}}</a></li>\n" +
" <li ng-class=\"{disabled: noNext(), next: align}\"><a href ng-click=\"selectPage(page + 1, $event)\">{{getText('next')}}</a></li>\n" +
2015-03-13 09:39:35 +01:00
"</ul>" ) ;
2014-02-21 02:13:31 +01:00
} ] ) ;
angular . module ( "template/pagination/pagination.html" , [ ] ) . run ( [ "$templateCache" , function ( $templateCache ) {
$templateCache . put ( "template/pagination/pagination.html" ,
2015-03-13 09:39:35 +01:00
"<ul class=\"pagination\">\n" +
2015-05-10 05:43:32 +02:00
" <li ng-if=\"boundaryLinks\" ng-class=\"{disabled: noPrevious()}\"><a href ng-click=\"selectPage(1, $event)\">{{getText('first')}}</a></li>\n" +
" <li ng-if=\"directionLinks\" ng-class=\"{disabled: noPrevious()}\"><a href ng-click=\"selectPage(page - 1, $event)\">{{getText('previous')}}</a></li>\n" +
" <li ng-repeat=\"page in pages track by $index\" ng-class=\"{active: page.active}\"><a href ng-click=\"selectPage(page.number, $event)\">{{page.text}}</a></li>\n" +
" <li ng-if=\"directionLinks\" ng-class=\"{disabled: noNext()}\"><a href ng-click=\"selectPage(page + 1, $event)\">{{getText('next')}}</a></li>\n" +
" <li ng-if=\"boundaryLinks\" ng-class=\"{disabled: noNext()}\"><a href ng-click=\"selectPage(totalPages, $event)\">{{getText('last')}}</a></li>\n" +
2015-03-13 09:39:35 +01:00
"</ul>" ) ;
2014-02-21 02:13:31 +01:00
} ] ) ;
2015-05-10 05:43:32 +02:00
angular . module ( "template/tooltip/tooltip-html-popup.html" , [ ] ) . run ( [ "$templateCache" , function ( $templateCache ) {
$templateCache . put ( "template/tooltip/tooltip-html-popup.html" ,
"<div class=\"tooltip\"\n" +
" tooltip-animation-class=\"fade\"\n" +
" tooltip-classes\n" +
" ng-class=\"{ in: isOpen() }\">\n" +
" <div class=\"tooltip-arrow\"></div>\n" +
" <div class=\"tooltip-inner\" ng-bind-html=\"contentExp()\"></div>\n" +
"</div>\n" +
"" ) ;
} ] ) ;
2014-02-21 02:13:31 +01:00
angular . module ( "template/tooltip/tooltip-html-unsafe-popup.html" , [ ] ) . run ( [ "$templateCache" , function ( $templateCache ) {
$templateCache . put ( "template/tooltip/tooltip-html-unsafe-popup.html" ,
2015-05-10 05:43:32 +02:00
"<div class=\"tooltip\"\n" +
" tooltip-animation-class=\"fade\"\n" +
" tooltip-classes\n" +
" ng-class=\"{ in: isOpen() }\">\n" +
2014-02-21 02:13:31 +01:00
" <div class=\"tooltip-arrow\"></div>\n" +
" <div class=\"tooltip-inner\" bind-html-unsafe=\"content\"></div>\n" +
"</div>\n" +
"" ) ;
} ] ) ;
angular . module ( "template/tooltip/tooltip-popup.html" , [ ] ) . run ( [ "$templateCache" , function ( $templateCache ) {
$templateCache . put ( "template/tooltip/tooltip-popup.html" ,
2015-05-10 05:43:32 +02:00
"<div class=\"tooltip\"\n" +
" tooltip-animation-class=\"fade\"\n" +
" tooltip-classes\n" +
" ng-class=\"{ in: isOpen() }\">\n" +
2014-02-21 02:13:31 +01:00
" <div class=\"tooltip-arrow\"></div>\n" +
" <div class=\"tooltip-inner\" ng-bind=\"content\"></div>\n" +
"</div>\n" +
"" ) ;
} ] ) ;
2015-05-10 05:43:32 +02:00
angular . module ( "template/tooltip/tooltip-template-popup.html" , [ ] ) . run ( [ "$templateCache" , function ( $templateCache ) {
$templateCache . put ( "template/tooltip/tooltip-template-popup.html" ,
"<div class=\"tooltip\"\n" +
" tooltip-animation-class=\"fade\"\n" +
" tooltip-classes\n" +
" ng-class=\"{ in: isOpen() }\">\n" +
" <div class=\"tooltip-arrow\"></div>\n" +
" <div class=\"tooltip-inner\"\n" +
" tooltip-template-transclude=\"contentExp()\"\n" +
" tooltip-template-transclude-scope=\"originScope()\"></div>\n" +
"</div>\n" +
"" ) ;
} ] ) ;
angular . module ( "template/popover/popover-template.html" , [ ] ) . run ( [ "$templateCache" , function ( $templateCache ) {
$templateCache . put ( "template/popover/popover-template.html" ,
"<div class=\"popover\"\n" +
" tooltip-animation-class=\"fade\"\n" +
" tooltip-classes\n" +
" ng-class=\"{ in: isOpen() }\">\n" +
" <div class=\"arrow\"></div>\n" +
"\n" +
" <div class=\"popover-inner\">\n" +
" <h3 class=\"popover-title\" ng-bind=\"title\" ng-if=\"title\"></h3>\n" +
" <div class=\"popover-content\"\n" +
" tooltip-template-transclude=\"contentExp()\"\n" +
" tooltip-template-transclude-scope=\"originScope()\"></div>\n" +
" </div>\n" +
"</div>\n" +
"" ) ;
} ] ) ;
angular . module ( "template/popover/popover-window.html" , [ ] ) . run ( [ "$templateCache" , function ( $templateCache ) {
$templateCache . put ( "template/popover/popover-window.html" ,
"<div class=\"popover {{placement}}\" ng-class=\"{ in: isOpen, fade: animation }\">\n" +
" <div class=\"arrow\"></div>\n" +
"\n" +
" <div class=\"popover-inner\">\n" +
" <h3 class=\"popover-title\" ng-bind=\"title\" ng-show=\"title\"></h3>\n" +
" <div class=\"popover-content\" tooltip-template-transclude></div>\n" +
" </div>\n" +
"</div>\n" +
"" ) ;
} ] ) ;
2014-02-21 02:13:31 +01:00
angular . module ( "template/popover/popover.html" , [ ] ) . run ( [ "$templateCache" , function ( $templateCache ) {
$templateCache . put ( "template/popover/popover.html" ,
2015-05-10 05:43:32 +02:00
"<div class=\"popover\"\n" +
" tooltip-animation-class=\"fade\"\n" +
" tooltip-classes\n" +
" ng-class=\"{ in: isOpen() }\">\n" +
2014-02-21 02:13:31 +01:00
" <div class=\"arrow\"></div>\n" +
"\n" +
" <div class=\"popover-inner\">\n" +
2015-05-10 05:43:32 +02:00
" <h3 class=\"popover-title\" ng-bind=\"title\" ng-if=\"title\"></h3>\n" +
2014-02-21 02:13:31 +01:00
" <div class=\"popover-content\" ng-bind=\"content\"></div>\n" +
" </div>\n" +
"</div>\n" +
"" ) ;
} ] ) ;
angular . module ( "template/progressbar/bar.html" , [ ] ) . run ( [ "$templateCache" , function ( $templateCache ) {
$templateCache . put ( "template/progressbar/bar.html" ,
2015-05-10 05:43:32 +02:00
"<div class=\"progress-bar\" ng-class=\"type && 'progress-bar-' + type\" role=\"progressbar\" aria-valuenow=\"{{value}}\" aria-valuemin=\"0\" aria-valuemax=\"{{max}}\" ng-style=\"{width: (percent < 100 ? percent : 100) + '%'}\" aria-valuetext=\"{{percent | number:0}}%\" ng-transclude></div>\n" +
"" ) ;
2014-02-21 02:13:31 +01:00
} ] ) ;
angular . module ( "template/progressbar/progress.html" , [ ] ) . run ( [ "$templateCache" , function ( $templateCache ) {
$templateCache . put ( "template/progressbar/progress.html" ,
"<div class=\"progress\" ng-transclude></div>" ) ;
} ] ) ;
angular . module ( "template/progressbar/progressbar.html" , [ ] ) . run ( [ "$templateCache" , function ( $templateCache ) {
$templateCache . put ( "template/progressbar/progressbar.html" ,
2015-03-13 09:39:35 +01:00
"<div class=\"progress\">\n" +
2015-05-10 05:43:32 +02:00
" <div class=\"progress-bar\" ng-class=\"type && 'progress-bar-' + type\" role=\"progressbar\" aria-valuenow=\"{{value}}\" aria-valuemin=\"0\" aria-valuemax=\"{{max}}\" ng-style=\"{width: (percent < 100 ? percent : 100) + '%'}\" aria-valuetext=\"{{percent | number:0}}%\" ng-transclude></div>\n" +
"</div>\n" +
"" ) ;
2014-02-21 02:13:31 +01:00
} ] ) ;
angular . module ( "template/rating/rating.html" , [ ] ) . run ( [ "$templateCache" , function ( $templateCache ) {
$templateCache . put ( "template/rating/rating.html" ,
2015-03-13 09:39:35 +01:00
"<span ng-mouseleave=\"reset()\" ng-keydown=\"onKeydown($event)\" tabindex=\"0\" role=\"slider\" aria-valuemin=\"0\" aria-valuemax=\"{{range.length}}\" aria-valuenow=\"{{value}}\">\n" +
" <i ng-repeat=\"r in range track by $index\" ng-mouseenter=\"enter($index + 1)\" ng-click=\"rate($index + 1)\" class=\"glyphicon\" ng-class=\"$index < value && (r.stateOn || 'glyphicon-star') || (r.stateOff || 'glyphicon-star-empty')\">\n" +
" <span class=\"sr-only\">({{ $index < value ? '*' : ' ' }})</span>\n" +
" </i>\n" +
2014-02-21 02:13:31 +01:00
"</span>" ) ;
} ] ) ;
angular . module ( "template/tabs/tab.html" , [ ] ) . run ( [ "$templateCache" , function ( $templateCache ) {
$templateCache . put ( "template/tabs/tab.html" ,
"<li ng-class=\"{active: active, disabled: disabled}\">\n" +
2015-03-13 09:39:35 +01:00
" <a href ng-click=\"select()\" tab-heading-transclude>{{heading}}</a>\n" +
2014-02-21 02:13:31 +01:00
"</li>\n" +
"" ) ;
} ] ) ;
angular . module ( "template/tabs/tabset.html" , [ ] ) . run ( [ "$templateCache" , function ( $templateCache ) {
$templateCache . put ( "template/tabs/tabset.html" ,
2015-03-13 09:39:35 +01:00
"<div>\n" +
" <ul class=\"nav nav-{{type || 'tabs'}}\" ng-class=\"{'nav-stacked': vertical, 'nav-justified': justified}\" ng-transclude></ul>\n" +
2014-02-21 02:13:31 +01:00
" <div class=\"tab-content\">\n" +
" <div class=\"tab-pane\" \n" +
" ng-repeat=\"tab in tabs\" \n" +
" ng-class=\"{active: tab.active}\"\n" +
" tab-content-transclude=\"tab\">\n" +
" </div>\n" +
" </div>\n" +
"</div>\n" +
"" ) ;
} ] ) ;
angular . module ( "template/timepicker/timepicker.html" , [ ] ) . run ( [ "$templateCache" , function ( $templateCache ) {
$templateCache . put ( "template/timepicker/timepicker.html" ,
2015-03-13 09:39:35 +01:00
"<table>\n" +
2015-05-10 05:43:32 +02:00
" <tbody>\n" +
" <tr class=\"text-center\">\n" +
" <td><a ng-click=\"incrementHours()\" class=\"btn btn-link\"><span class=\"glyphicon glyphicon-chevron-up\"></span></a></td>\n" +
" <td> </td>\n" +
" <td><a ng-click=\"incrementMinutes()\" class=\"btn btn-link\"><span class=\"glyphicon glyphicon-chevron-up\"></span></a></td>\n" +
" <td ng-show=\"showMeridian\"></td>\n" +
" </tr>\n" +
" <tr>\n" +
" <td class=\"form-group\" ng-class=\"{'has-error': invalidHours}\">\n" +
" <input style=\"width:50px;\" type=\"text\" ng-model=\"hours\" ng-change=\"updateHours()\" class=\"form-control text-center\" ng-readonly=\"readonlyInput\" maxlength=\"2\">\n" +
" </td>\n" +
" <td>:</td>\n" +
" <td class=\"form-group\" ng-class=\"{'has-error': invalidMinutes}\">\n" +
" <input style=\"width:50px;\" type=\"text\" ng-model=\"minutes\" ng-change=\"updateMinutes()\" class=\"form-control text-center\" ng-readonly=\"readonlyInput\" maxlength=\"2\">\n" +
" </td>\n" +
" <td ng-show=\"showMeridian\"><button type=\"button\" class=\"btn btn-default text-center\" ng-click=\"toggleMeridian()\">{{meridian}}</button></td>\n" +
" </tr>\n" +
" <tr class=\"text-center\">\n" +
" <td><a ng-click=\"decrementHours()\" class=\"btn btn-link\"><span class=\"glyphicon glyphicon-chevron-down\"></span></a></td>\n" +
" <td> </td>\n" +
" <td><a ng-click=\"decrementMinutes()\" class=\"btn btn-link\"><span class=\"glyphicon glyphicon-chevron-down\"></span></a></td>\n" +
" <td ng-show=\"showMeridian\"></td>\n" +
" </tr>\n" +
" </tbody>\n" +
2014-02-21 02:13:31 +01:00
"</table>\n" +
"" ) ;
} ] ) ;
angular . module ( "template/typeahead/typeahead-match.html" , [ ] ) . run ( [ "$templateCache" , function ( $templateCache ) {
$templateCache . put ( "template/typeahead/typeahead-match.html" ,
"<a tabindex=\"-1\" bind-html-unsafe=\"match.label | typeaheadHighlight:query\"></a>" ) ;
} ] ) ;
angular . module ( "template/typeahead/typeahead-popup.html" , [ ] ) . run ( [ "$templateCache" , function ( $templateCache ) {
$templateCache . put ( "template/typeahead/typeahead-popup.html" ,
2015-03-13 09:39:35 +01:00
"<ul class=\"dropdown-menu\" ng-show=\"isOpen()\" ng-style=\"{top: position.top+'px', left: position.left+'px'}\" style=\"display: block;\" role=\"listbox\" aria-hidden=\"{{!isOpen()}}\">\n" +
" <li ng-repeat=\"match in matches track by $index\" ng-class=\"{active: isActive($index) }\" ng-mouseenter=\"selectActive($index)\" ng-click=\"selectMatch($index)\" role=\"option\" id=\"{{match.id}}\">\n" +
2014-02-21 02:13:31 +01:00
" <div typeahead-match index=\"$index\" match=\"match\" query=\"query\" template-url=\"templateUrl\"></div>\n" +
" </li>\n" +
2015-03-13 09:39:35 +01:00
"</ul>\n" +
"" ) ;
2014-02-21 02:13:31 +01:00
} ] ) ;
2015-05-10 05:43:32 +02:00
! angular . $$csp ( ) && angular . element ( document ) . find ( 'head' ) . prepend ( '<style type="text/css">.ng-animate.item:not(.left):not(.right){-webkit-transition:0s ease-in-out left;transition:0s ease-in-out left}</style>' ) ;