2015-05-05 03:10:25 +02:00
/ *
* angular - ui - bootstrap
* http : //angular-ui.github.io/bootstrap/
2016-03-23 18:39:41 +01:00
* Version : 0.14 . 3 - 2015 - 10 - 23
2015-05-05 03:10:25 +02:00
* License : MIT
* /
2016-03-23 18:39:41 +01:00
angular . module ( "ui.bootstrap" , [ "ui.bootstrap.collapse" , "ui.bootstrap.accordion" , "ui.bootstrap.alert" , "ui.bootstrap.buttons" , "ui.bootstrap.carousel" , "ui.bootstrap.dateparser" , "ui.bootstrap.position" , "ui.bootstrap.datepicker" , "ui.bootstrap.dropdown" , "ui.bootstrap.stackedMap" , "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.typeahead" ] ) ;
angular . module ( 'ui.bootstrap.collapse' , [ ] )
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
. directive ( 'uibCollapse' , [ '$animate' , '$injector' , function ( $animate , $injector ) {
var $animateCss = $injector . has ( '$animateCss' ) ? $injector . get ( '$animateCss' ) : null ;
return {
link : function ( scope , element , attrs ) {
function expand ( ) {
element . removeClass ( 'collapse' )
. addClass ( 'collapsing' )
. attr ( 'aria-expanded' , true )
. attr ( 'aria-hidden' , false ) ;
if ( $animateCss ) {
$animateCss ( element , {
addClass : 'in' ,
easing : 'ease' ,
to : { height : element [ 0 ] . scrollHeight + 'px' }
} ) . start ( ) . finally ( expandDone ) ;
} else {
$animate . addClass ( element , 'in' , {
to : { height : element [ 0 ] . scrollHeight + 'px' }
} ) . then ( expandDone ) ;
}
}
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
function expandDone ( ) {
element . removeClass ( 'collapsing' )
. addClass ( 'collapse' )
. css ( { height : 'auto' } ) ;
}
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
function collapse ( ) {
if ( ! element . hasClass ( 'collapse' ) && ! element . hasClass ( 'in' ) ) {
return collapseDone ( ) ;
}
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01: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' )
. attr ( 'aria-expanded' , false )
. attr ( 'aria-hidden' , true ) ;
if ( $animateCss ) {
$animateCss ( element , {
removeClass : 'in' ,
to : { height : '0' }
} ) . start ( ) . finally ( collapseDone ) ;
} else {
$animate . removeClass ( element , 'in' , {
to : { height : '0' }
} ) . then ( collapseDone ) ;
}
}
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
function collapseDone ( ) {
element . css ( { height : '0' } ) ; // Required so that collapse works when animation is disabled
element . removeClass ( 'collapsing' )
. addClass ( 'collapse' ) ;
}
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
scope . $watch ( attrs . uibCollapse , function ( shouldCollapse ) {
if ( shouldCollapse ) {
collapse ( ) ;
} else {
expand ( ) ;
}
} ) ;
2015-05-05 03:10:25 +02:00
}
} ;
2016-03-23 18:39:41 +01:00
} ] ) ;
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
/* Deprecated collapse below */
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
angular . module ( 'ui.bootstrap.collapse' )
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
. value ( '$collapseSuppressWarning' , false )
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
. directive ( 'collapse' , [ '$animate' , '$injector' , '$log' , '$collapseSuppressWarning' , function ( $animate , $injector , $log , $collapseSuppressWarning ) {
var $animateCss = $injector . has ( '$animateCss' ) ? $injector . get ( '$animateCss' ) : null ;
2015-05-05 03:10:25 +02:00
return {
2016-03-23 18:39:41 +01:00
link : function ( scope , element , attrs ) {
if ( ! $collapseSuppressWarning ) {
$log . warn ( 'collapse is now deprecated. Use uib-collapse instead.' ) ;
2015-05-05 03:10:25 +02:00
}
function expand ( ) {
2016-03-23 18:39:41 +01:00
element . removeClass ( 'collapse' )
. addClass ( 'collapsing' )
. attr ( 'aria-expanded' , true )
. attr ( 'aria-hidden' , false ) ;
if ( $animateCss ) {
$animateCss ( element , {
easing : 'ease' ,
to : { height : element [ 0 ] . scrollHeight + 'px' }
} ) . start ( ) . done ( expandDone ) ;
2015-05-05 03:10:25 +02:00
} else {
2016-03-23 18:39:41 +01:00
$animate . animate ( element , { } , {
height : element [ 0 ] . scrollHeight + 'px'
} ) . then ( expandDone ) ;
2015-05-05 03:10:25 +02:00
}
}
function expandDone ( ) {
2016-03-23 18:39:41 +01:00
element . removeClass ( 'collapsing' )
. addClass ( 'collapse in' )
. css ( { height : 'auto' } ) ;
2015-05-05 03:10:25 +02:00
}
function collapse ( ) {
2016-03-23 18:39:41 +01:00
if ( ! element . hasClass ( 'collapse' ) && ! element . hasClass ( 'in' ) ) {
return collapseDone ( ) ;
}
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01: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 in' )
. addClass ( 'collapsing' )
. attr ( 'aria-expanded' , false )
. attr ( 'aria-hidden' , true ) ;
if ( $animateCss ) {
$animateCss ( element , {
to : { height : '0' }
} ) . start ( ) . done ( collapseDone ) ;
} else {
$animate . animate ( element , { } , {
height : '0'
} ) . then ( collapseDone ) ;
2015-05-05 03:10:25 +02:00
}
}
function collapseDone ( ) {
2016-03-23 18:39:41 +01:00
element . css ( { height : '0' } ) ; // Required so that collapse works when animation is disabled
element . removeClass ( 'collapsing' )
. addClass ( 'collapse' ) ;
2015-05-05 03:10:25 +02:00
}
2016-03-23 18:39:41 +01:00
scope . $watch ( attrs . collapse , function ( shouldCollapse ) {
2015-05-05 03:10:25 +02:00
if ( shouldCollapse ) {
collapse ( ) ;
} else {
expand ( ) ;
}
} ) ;
}
} ;
} ] ) ;
angular . module ( 'ui.bootstrap.accordion' , [ 'ui.bootstrap.collapse' ] )
2016-03-23 18:39:41 +01:00
. constant ( 'uibAccordionConfig' , {
2015-05-05 03:10:25 +02:00
closeOthers : true
} )
2016-03-23 18:39:41 +01:00
. controller ( 'UibAccordionController' , [ '$scope' , '$attrs' , 'uibAccordionConfig' , function ( $scope , $attrs , accordionConfig ) {
2015-05-05 03:10:25 +02:00
// 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 ) {
2016-03-23 18:39:41 +01:00
var closeOthers = angular . isDefined ( $attrs . closeOthers ) ?
$scope . $eval ( $attrs . closeOthers ) : accordionConfig . closeOthers ;
if ( closeOthers ) {
angular . forEach ( this . groups , function ( group ) {
if ( group !== openGroup ) {
2015-05-05 03:10:25 +02:00
group . isOpen = false ;
}
} ) ;
}
} ;
// 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 ) ;
2016-03-23 18:39:41 +01:00
groupScope . $on ( '$destroy' , function ( event ) {
2015-05-05 03:10:25 +02:00
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 ) ;
2016-03-23 18:39:41 +01:00
if ( index !== - 1 ) {
2015-05-05 03:10:25 +02:00
this . groups . splice ( index , 1 ) ;
}
} ;
} ] )
// The accordion directive simply sets up the directive controller
// and adds an accordion CSS class to itself element.
2016-03-23 18:39:41 +01:00
. directive ( 'uibAccordion' , function ( ) {
2015-05-05 03:10:25 +02:00
return {
2016-03-23 18:39:41 +01:00
controller : 'UibAccordionController' ,
controllerAs : 'accordion' ,
2015-05-05 03:10:25 +02:00
transclude : true ,
2016-03-23 18:39:41 +01:00
templateUrl : function ( element , attrs ) {
return attrs . templateUrl || 'template/accordion/accordion.html' ;
}
2015-05-05 03:10:25 +02:00
} ;
} )
// The accordion-group directive indicates a block of html that will expand and collapse in an accordion
2016-03-23 18:39:41 +01:00
. directive ( 'uibAccordionGroup' , function ( ) {
2015-05-05 03:10:25 +02:00
return {
2016-03-23 18:39:41 +01:00
require : '^uibAccordion' , // We need this directive to be inside an accordion
transclude : true , // It transcludes the contents of the directive into the template
2015-05-05 03:10:25 +02:00
replace : true , // The element containing the directive will be replaced with the template
2016-03-23 18:39:41 +01:00
templateUrl : function ( element , attrs ) {
return attrs . templateUrl || 'template/accordion/accordion-group.html' ;
} ,
2015-05-05 03:10:25 +02:00
scope : {
heading : '@' , // Interpolate the heading attribute onto this scope
isOpen : '=?' ,
isDisabled : '=?'
} ,
controller : function ( ) {
this . setHeading = function ( element ) {
this . heading = element ;
} ;
} ,
link : function ( scope , element , attrs , accordionCtrl ) {
accordionCtrl . addGroup ( scope ) ;
2016-03-23 18:39:41 +01:00
scope . openClass = attrs . openClass || 'panel-open' ;
scope . panelClass = attrs . panelClass ;
2015-05-05 03:10:25 +02:00
scope . $watch ( 'isOpen' , function ( value ) {
2016-03-23 18:39:41 +01:00
element . toggleClass ( scope . openClass , ! ! value ) ;
if ( value ) {
2015-05-05 03:10:25 +02:00
accordionCtrl . closeOthers ( scope ) ;
}
} ) ;
2016-03-23 18:39:41 +01:00
scope . toggleOpen = function ( $event ) {
if ( ! scope . isDisabled ) {
if ( ! $event || $event . which === 32 ) {
scope . isOpen = ! scope . isOpen ;
}
2015-05-05 03:10:25 +02:00
}
} ;
}
} ;
} )
// Use accordion-heading below an accordion-group to provide a heading containing HTML
2016-03-23 18:39:41 +01:00
. directive ( 'uibAccordionHeading' , function ( ) {
2015-05-05 03:10:25 +02:00
return {
transclude : true , // Grab the contents to be used as the heading
template : '' , // In effect remove this element!
replace : true ,
2016-03-23 18:39:41 +01:00
require : '^uibAccordionGroup' ,
link : function ( scope , element , attrs , accordionGroupCtrl , transclude ) {
2015-05-05 03:10:25 +02:00
// 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]
2016-03-23 18:39:41 +01:00
accordionGroupCtrl . setHeading ( transclude ( scope , angular . noop ) ) ;
2015-05-05 03:10:25 +02: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
2016-03-23 18:39:41 +01:00
. directive ( 'uibAccordionTransclude' , function ( ) {
return {
require : [ '?^uibAccordionGroup' , '?^accordionGroup' ] ,
link : function ( scope , element , attrs , controller ) {
controller = controller [ 0 ] ? controller [ 0 ] : controller [ 1 ] ; // Delete after we remove deprecation
scope . $watch ( function ( ) { return controller [ attrs . uibAccordionTransclude ] ; } , function ( heading ) {
if ( heading ) {
element . find ( 'span' ) . html ( '' ) ;
element . find ( 'span' ) . append ( heading ) ;
2015-05-05 03:10:25 +02:00
}
} ) ;
}
} ;
} ) ;
2016-03-23 18:39:41 +01:00
/* Deprecated accordion below */
angular . module ( 'ui.bootstrap.accordion' )
. value ( '$accordionSuppressWarning' , false )
. controller ( 'AccordionController' , [ '$scope' , '$attrs' , '$controller' , '$log' , '$accordionSuppressWarning' , function ( $scope , $attrs , $controller , $log , $accordionSuppressWarning ) {
if ( ! $accordionSuppressWarning ) {
$log . warn ( 'AccordionController is now deprecated. Use UibAccordionController instead.' ) ;
}
angular . extend ( this , $controller ( 'UibAccordionController' , {
$scope : $scope ,
$attrs : $attrs
} ) ) ;
} ] )
. directive ( 'accordion' , [ '$log' , '$accordionSuppressWarning' , function ( $log , $accordionSuppressWarning ) {
return {
restrict : 'EA' ,
controller : 'AccordionController' ,
controllerAs : 'accordion' ,
transclude : true ,
replace : false ,
templateUrl : function ( element , attrs ) {
return attrs . templateUrl || 'template/accordion/accordion.html' ;
} ,
link : function ( ) {
if ( ! $accordionSuppressWarning ) {
$log . warn ( 'accordion is now deprecated. Use uib-accordion instead.' ) ;
}
}
} ;
} ] )
. directive ( 'accordionGroup' , [ '$log' , '$accordionSuppressWarning' , function ( $log , $accordionSuppressWarning ) {
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 : function ( element , attrs ) {
return attrs . templateUrl || 'template/accordion/accordion-group.html' ;
} ,
scope : {
heading : '@' , // Interpolate the heading attribute onto this scope
isOpen : '=?' ,
isDisabled : '=?'
} ,
controller : function ( ) {
this . setHeading = function ( element ) {
this . heading = element ;
} ;
} ,
link : function ( scope , element , attrs , accordionCtrl ) {
if ( ! $accordionSuppressWarning ) {
$log . warn ( 'accordion-group is now deprecated. Use uib-accordion-group instead.' ) ;
}
accordionCtrl . addGroup ( scope ) ;
scope . openClass = attrs . openClass || 'panel-open' ;
scope . panelClass = attrs . panelClass ;
scope . $watch ( 'isOpen' , function ( value ) {
element . toggleClass ( scope . openClass , ! ! value ) ;
if ( value ) {
accordionCtrl . closeOthers ( scope ) ;
}
} ) ;
scope . toggleOpen = function ( $event ) {
if ( ! scope . isDisabled ) {
if ( ! $event || $event . which === 32 ) {
scope . isOpen = ! scope . isOpen ;
}
}
} ;
}
} ;
} ] )
. directive ( 'accordionHeading' , [ '$log' , '$accordionSuppressWarning' , function ( $log , $accordionSuppressWarning ) {
return {
restrict : 'EA' ,
transclude : true , // Grab the contents to be used as the heading
template : '' , // In effect remove this element!
replace : true ,
require : '^accordionGroup' ,
link : function ( scope , element , attr , accordionGroupCtrl , transclude ) {
if ( ! $accordionSuppressWarning ) {
$log . warn ( 'accordion-heading is now deprecated. Use uib-accordion-heading instead.' ) ;
}
// 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]
accordionGroupCtrl . setHeading ( transclude ( scope , angular . noop ) ) ;
}
} ;
} ] )
. directive ( 'accordionTransclude' , [ '$log' , '$accordionSuppressWarning' , function ( $log , $accordionSuppressWarning ) {
return {
require : '^accordionGroup' ,
link : function ( scope , element , attr , controller ) {
if ( ! $accordionSuppressWarning ) {
$log . warn ( 'accordion-transclude is now deprecated. Use uib-accordion-transclude instead.' ) ;
}
scope . $watch ( function ( ) { return controller [ attr . accordionTransclude ] ; } , function ( heading ) {
if ( heading ) {
element . find ( 'span' ) . html ( '' ) ;
element . find ( 'span' ) . append ( heading ) ;
}
} ) ;
}
} ;
} ] ) ;
2015-05-05 03:10:25 +02:00
angular . module ( 'ui.bootstrap.alert' , [ ] )
2016-03-23 18:39:41 +01:00
. controller ( 'UibAlertController' , [ '$scope' , '$attrs' , '$interpolate' , '$timeout' , function ( $scope , $attrs , $interpolate , $timeout ) {
$scope . closeable = ! ! $attrs . close ;
var dismissOnTimeout = angular . isDefined ( $attrs . dismissOnTimeout ) ?
$interpolate ( $attrs . dismissOnTimeout ) ( $scope . $parent ) : null ;
if ( dismissOnTimeout ) {
$timeout ( function ( ) {
$scope . close ( ) ;
} , parseInt ( dismissOnTimeout , 10 ) ) ;
}
2015-05-05 03:10:25 +02:00
} ] )
2016-03-23 18:39:41 +01:00
. directive ( 'uibAlert' , function ( ) {
2015-05-05 03:10:25 +02:00
return {
2016-03-23 18:39:41 +01:00
controller : 'UibAlertController' ,
controllerAs : 'alert' ,
templateUrl : function ( element , attrs ) {
return attrs . templateUrl || 'template/alert/alert.html' ;
} ,
transclude : true ,
replace : true ,
2015-05-05 03:10:25 +02:00
scope : {
type : '@' ,
close : '&'
}
} ;
2016-03-23 18:39:41 +01:00
} ) ;
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
/* Deprecated alert below */
angular . module ( 'ui.bootstrap.alert' )
. value ( '$alertSuppressWarning' , false )
. controller ( 'AlertController' , [ '$scope' , '$attrs' , '$controller' , '$log' , '$alertSuppressWarning' , function ( $scope , $attrs , $controller , $log , $alertSuppressWarning ) {
if ( ! $alertSuppressWarning ) {
$log . warn ( 'AlertController is now deprecated. Use UibAlertController instead.' ) ;
2015-05-05 03:10:25 +02:00
}
2016-03-23 18:39:41 +01:00
angular . extend ( this , $controller ( 'UibAlertController' , {
$scope : $scope ,
$attrs : $attrs
} ) ) ;
} ] )
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
. directive ( 'alert' , [ '$log' , '$alertSuppressWarning' , function ( $log , $alertSuppressWarning ) {
return {
controller : 'AlertController' ,
controllerAs : 'alert' ,
templateUrl : function ( element , attrs ) {
return attrs . templateUrl || 'template/alert/alert.html' ;
} ,
transclude : true ,
replace : true ,
scope : {
type : '@' ,
close : '&'
} ,
link : function ( ) {
if ( ! $alertSuppressWarning ) {
$log . warn ( 'alert is now deprecated. Use uib-alert instead.' ) ;
}
}
2015-05-05 03:10:25 +02:00
} ;
2016-03-23 18:39:41 +01:00
} ] ) ;
2015-05-05 03:10:25 +02:00
angular . module ( 'ui.bootstrap.buttons' , [ ] )
2016-03-23 18:39:41 +01:00
. constant ( 'uibButtonConfig' , {
2015-05-05 03:10:25 +02:00
activeClass : 'active' ,
toggleEvent : 'click'
} )
2016-03-23 18:39:41 +01:00
. controller ( 'UibButtonsController' , [ 'uibButtonConfig' , function ( buttonConfig ) {
2015-05-05 03:10:25 +02:00
this . activeClass = buttonConfig . activeClass || 'active' ;
this . toggleEvent = buttonConfig . toggleEvent || 'click' ;
} ] )
2016-03-23 18:39:41 +01:00
. directive ( 'uibBtnRadio' , function ( ) {
2015-05-05 03:10:25 +02:00
return {
2016-03-23 18:39:41 +01:00
require : [ 'uibBtnRadio' , 'ngModel' ] ,
controller : 'UibButtonsController' ,
controllerAs : 'buttons' ,
link : function ( scope , element , attrs , ctrls ) {
2015-05-05 03:10:25 +02:00
var buttonsCtrl = ctrls [ 0 ] , ngModelCtrl = ctrls [ 1 ] ;
2016-03-23 18:39:41 +01:00
element . find ( 'input' ) . css ( { display : 'none' } ) ;
2015-05-05 03:10:25 +02:00
//model -> UI
2016-03-23 18:39:41 +01:00
ngModelCtrl . $render = function ( ) {
element . toggleClass ( buttonsCtrl . activeClass , angular . equals ( ngModelCtrl . $modelValue , scope . $eval ( attrs . uibBtnRadio ) ) ) ;
2015-05-05 03:10:25 +02:00
} ;
//ui->model
2016-03-23 18:39:41 +01:00
element . on ( buttonsCtrl . toggleEvent , function ( ) {
if ( attrs . disabled ) {
return ;
}
2015-05-05 03:10:25 +02:00
var isActive = element . hasClass ( buttonsCtrl . activeClass ) ;
if ( ! isActive || angular . isDefined ( attrs . uncheckable ) ) {
2016-03-23 18:39:41 +01:00
scope . $apply ( function ( ) {
ngModelCtrl . $setViewValue ( isActive ? null : scope . $eval ( attrs . uibBtnRadio ) ) ;
2015-05-05 03:10:25 +02:00
ngModelCtrl . $render ( ) ;
} ) ;
}
} ) ;
}
} ;
} )
2016-03-23 18:39:41 +01:00
. directive ( 'uibBtnCheckbox' , function ( ) {
2015-05-05 03:10:25 +02:00
return {
2016-03-23 18:39:41 +01:00
require : [ 'uibBtnCheckbox' , 'ngModel' ] ,
controller : 'UibButtonsController' ,
controllerAs : 'button' ,
link : function ( scope , element , attrs , ctrls ) {
2015-05-05 03:10:25 +02:00
var buttonsCtrl = ctrls [ 0 ] , ngModelCtrl = ctrls [ 1 ] ;
2016-03-23 18:39:41 +01:00
element . find ( 'input' ) . css ( { display : 'none' } ) ;
2015-05-05 03:10:25 +02:00
function getTrueValue ( ) {
return getCheckboxValue ( attrs . btnCheckboxTrue , true ) ;
}
function getFalseValue ( ) {
return getCheckboxValue ( attrs . btnCheckboxFalse , false ) ;
}
2016-03-23 18:39:41 +01:00
function getCheckboxValue ( attribute , defaultValue ) {
return angular . isDefined ( attribute ) ? scope . $eval ( attribute ) : defaultValue ;
2015-05-05 03:10:25 +02:00
}
//model -> UI
2016-03-23 18:39:41 +01:00
ngModelCtrl . $render = function ( ) {
2015-05-05 03:10:25 +02:00
element . toggleClass ( buttonsCtrl . activeClass , angular . equals ( ngModelCtrl . $modelValue , getTrueValue ( ) ) ) ;
} ;
//ui->model
2016-03-23 18:39:41 +01:00
element . on ( buttonsCtrl . toggleEvent , function ( ) {
if ( attrs . disabled ) {
return ;
}
scope . $apply ( function ( ) {
2015-05-05 03:10:25 +02:00
ngModelCtrl . $setViewValue ( element . hasClass ( buttonsCtrl . activeClass ) ? getFalseValue ( ) : getTrueValue ( ) ) ;
ngModelCtrl . $render ( ) ;
} ) ;
} ) ;
}
} ;
} ) ;
2016-03-23 18:39:41 +01:00
/* Deprecated buttons below */
angular . module ( 'ui.bootstrap.buttons' )
. value ( '$buttonsSuppressWarning' , false )
. controller ( 'ButtonsController' , [ '$controller' , '$log' , '$buttonsSuppressWarning' , function ( $controller , $log , $buttonsSuppressWarning ) {
if ( ! $buttonsSuppressWarning ) {
$log . warn ( 'ButtonsController is now deprecated. Use UibButtonsController instead.' ) ;
}
angular . extend ( this , $controller ( 'UibButtonsController' ) ) ;
} ] )
. directive ( 'btnRadio' , [ '$log' , '$buttonsSuppressWarning' , function ( $log , $buttonsSuppressWarning ) {
return {
require : [ 'btnRadio' , 'ngModel' ] ,
controller : 'ButtonsController' ,
controllerAs : 'buttons' ,
link : function ( scope , element , attrs , ctrls ) {
if ( ! $buttonsSuppressWarning ) {
$log . warn ( 'btn-radio is now deprecated. Use uib-btn-radio instead.' ) ;
}
var buttonsCtrl = ctrls [ 0 ] , ngModelCtrl = ctrls [ 1 ] ;
element . find ( 'input' ) . css ( { display : 'none' } ) ;
//model -> UI
ngModelCtrl . $render = function ( ) {
element . toggleClass ( buttonsCtrl . activeClass , angular . equals ( ngModelCtrl . $modelValue , scope . $eval ( attrs . btnRadio ) ) ) ;
} ;
//ui->model
element . bind ( buttonsCtrl . toggleEvent , function ( ) {
if ( attrs . disabled ) {
return ;
}
var isActive = element . hasClass ( buttonsCtrl . activeClass ) ;
if ( ! isActive || angular . isDefined ( attrs . uncheckable ) ) {
scope . $apply ( function ( ) {
ngModelCtrl . $setViewValue ( isActive ? null : scope . $eval ( attrs . btnRadio ) ) ;
ngModelCtrl . $render ( ) ;
} ) ;
}
} ) ;
}
} ;
} ] )
. directive ( 'btnCheckbox' , [ '$document' , '$log' , '$buttonsSuppressWarning' , function ( $document , $log , $buttonsSuppressWarning ) {
return {
require : [ 'btnCheckbox' , 'ngModel' ] ,
controller : 'ButtonsController' ,
controllerAs : 'button' ,
link : function ( scope , element , attrs , ctrls ) {
if ( ! $buttonsSuppressWarning ) {
$log . warn ( 'btn-checkbox is now deprecated. Use uib-btn-checkbox instead.' ) ;
}
var buttonsCtrl = ctrls [ 0 ] , ngModelCtrl = ctrls [ 1 ] ;
element . find ( 'input' ) . css ( { display : 'none' } ) ;
function getTrueValue ( ) {
return getCheckboxValue ( attrs . btnCheckboxTrue , true ) ;
}
function getFalseValue ( ) {
return getCheckboxValue ( attrs . btnCheckboxFalse , false ) ;
}
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 ( ) ) ) ;
} ;
//ui->model
element . bind ( buttonsCtrl . toggleEvent , function ( ) {
if ( attrs . disabled ) {
return ;
}
scope . $apply ( function ( ) {
ngModelCtrl . $setViewValue ( element . hasClass ( buttonsCtrl . activeClass ) ? getFalseValue ( ) : getTrueValue ( ) ) ;
ngModelCtrl . $render ( ) ;
} ) ;
} ) ;
//accessibility
element . on ( 'keypress' , function ( e ) {
if ( attrs . disabled || e . which !== 32 || $document [ 0 ] . activeElement !== element [ 0 ] ) {
return ;
}
scope . $apply ( function ( ) {
ngModelCtrl . $setViewValue ( element . hasClass ( buttonsCtrl . activeClass ) ? getFalseValue ( ) : getTrueValue ( ) ) ;
ngModelCtrl . $render ( ) ;
} ) ;
} ) ;
}
} ;
} ] ) ;
2015-05-05 03:10:25 +02:00
/ * *
2016-03-23 18:39:41 +01:00
* @ ngdoc overview
* @ name ui . bootstrap . carousel
*
* @ description
* AngularJS version of an image carousel .
*
* /
angular . module ( 'ui.bootstrap.carousel' , [ ] )
. controller ( 'UibCarouselController' , [ '$scope' , '$element' , '$interval' , '$animate' , function ( $scope , $element , $interval , $animate ) {
2015-05-05 03:10:25 +02:00
var self = this ,
slides = self . slides = $scope . slides = [ ] ,
2016-03-23 18:39:41 +01:00
NEW _ANIMATE = angular . version . minor >= 4 ,
NO _TRANSITION = 'uib-noTransition' ,
SLIDE _DIRECTION = 'uib-slideDirection' ,
2015-05-05 03:10:25 +02:00
currentIndex = - 1 ,
currentInterval , isPlaying ;
self . currentSlide = null ;
var destroyed = false ;
/* direction: "prev" or "next" */
self . select = $scope . select = function ( nextSlide , direction ) {
2016-03-23 18:39:41 +01:00
var nextIndex = $scope . indexOfSlide ( nextSlide ) ;
2015-05-05 03:10:25 +02:00
//Decide direction if it's not given
if ( direction === undefined ) {
2016-03-23 18:39:41 +01:00
direction = nextIndex > self . getCurrentIndex ( ) ? 'next' : 'prev' ;
2015-05-05 03:10:25 +02:00
}
2016-03-23 18:39:41 +01:00
//Prevent this user-triggered transition from occurring if there is already one in progress
if ( nextSlide && nextSlide !== self . currentSlide && ! $scope . $currentTransition ) {
goNext ( nextSlide , nextIndex , direction ) ;
2015-05-05 03:10:25 +02:00
}
2016-03-23 18:39:41 +01:00
} ;
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
function goNext ( slide , index , direction ) {
// Scope has been destroyed, stop here.
if ( destroyed ) { return ; }
angular . extend ( slide , { direction : direction , active : true } ) ;
angular . extend ( self . currentSlide || { } , { direction : direction , active : false } ) ;
if ( $animate . enabled ( ) && ! $scope . noTransition && ! $scope . $currentTransition &&
slide . $element && self . slides . length > 1 ) {
slide . $element . data ( SLIDE _DIRECTION , slide . direction ) ;
if ( self . currentSlide && self . currentSlide . $element ) {
self . currentSlide . $element . data ( SLIDE _DIRECTION , slide . direction ) ;
}
$scope . $currentTransition = true ;
if ( NEW _ANIMATE ) {
$animate . on ( 'addClass' , slide . $element , function ( element , phase ) {
if ( phase === 'close' ) {
$scope . $currentTransition = null ;
$animate . off ( 'addClass' , element ) ;
}
2015-05-05 03:10:25 +02:00
} ) ;
} else {
2016-03-23 18:39:41 +01:00
slide . $element . one ( '$animate:close' , function closeFn ( ) {
$scope . $currentTransition = null ;
} ) ;
2015-05-05 03:10:25 +02:00
}
}
2016-03-23 18:39:41 +01:00
self . currentSlide = slide ;
currentIndex = index ;
//every time you change slides, reset the timer
restartTimer ( ) ;
}
$scope . $on ( '$destroy' , function ( ) {
2015-05-05 03:10:25 +02:00
destroyed = true ;
} ) ;
2016-03-23 18:39:41 +01: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 ;
} ;
2015-05-05 03:10:25 +02:00
/* Allow outside people to call indexOf on slides array */
2016-03-23 18:39:41 +01:00
$scope . indexOfSlide = function ( slide ) {
return angular . isDefined ( slide . index ) ? + slide . index : slides . indexOf ( slide ) ;
2015-05-05 03:10:25 +02:00
} ;
$scope . next = function ( ) {
2016-03-23 18:39:41 +01:00
var newIndex = ( self . getCurrentIndex ( ) + 1 ) % slides . length ;
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
if ( newIndex === 0 && $scope . noWrap ( ) ) {
$scope . pause ( ) ;
return ;
2015-05-05 03:10:25 +02:00
}
2016-03-23 18:39:41 +01:00
return self . select ( getSlideByIndex ( newIndex ) , 'next' ) ;
2015-05-05 03:10:25 +02:00
} ;
$scope . prev = function ( ) {
2016-03-23 18:39:41 +01:00
var newIndex = self . getCurrentIndex ( ) - 1 < 0 ? slides . length - 1 : self . getCurrentIndex ( ) - 1 ;
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
if ( $scope . noWrap ( ) && newIndex === slides . length - 1 ) {
$scope . pause ( ) ;
return ;
2015-05-05 03:10:25 +02:00
}
2016-03-23 18:39:41 +01:00
return self . select ( getSlideByIndex ( newIndex ) , 'prev' ) ;
2015-05-05 03:10:25 +02:00
} ;
$scope . isActive = function ( slide ) {
return self . currentSlide === slide ;
} ;
$scope . $watch ( 'interval' , restartTimer ) ;
2016-03-23 18:39:41 +01:00
$scope . $watchCollection ( 'slides' , resetTransition ) ;
2015-05-05 03:10:25 +02:00
$scope . $on ( '$destroy' , resetTimer ) ;
function restartTimer ( ) {
resetTimer ( ) ;
var interval = + $scope . interval ;
if ( ! isNaN ( interval ) && interval > 0 ) {
currentInterval = $interval ( timerFn , interval ) ;
}
}
function resetTimer ( ) {
if ( currentInterval ) {
$interval . cancel ( currentInterval ) ;
currentInterval = null ;
}
}
function timerFn ( ) {
var interval = + $scope . interval ;
2016-03-23 18:39:41 +01:00
if ( isPlaying && ! isNaN ( interval ) && interval > 0 && slides . length ) {
2015-05-05 03:10:25 +02:00
$scope . next ( ) ;
} else {
$scope . pause ( ) ;
}
}
2016-03-23 18:39:41 +01:00
function resetTransition ( slides ) {
if ( ! slides . length ) {
$scope . $currentTransition = null ;
}
}
2015-05-05 03:10:25 +02:00
$scope . play = function ( ) {
if ( ! isPlaying ) {
isPlaying = true ;
restartTimer ( ) ;
}
} ;
$scope . pause = function ( ) {
if ( ! $scope . noPause ) {
isPlaying = false ;
resetTimer ( ) ;
}
} ;
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
2016-03-23 18:39:41 +01:00
if ( slides . length === 1 || slide . active ) {
self . select ( slides [ slides . length - 1 ] ) ;
if ( slides . length === 1 ) {
2015-05-05 03:10:25 +02:00
$scope . play ( ) ;
}
} else {
slide . active = false ;
}
} ;
self . removeSlide = function ( slide ) {
2016-03-23 18:39:41 +01:00
if ( angular . isDefined ( slide . index ) ) {
slides . sort ( function ( a , b ) {
return + a . index > + b . index ;
} ) ;
}
2015-05-05 03:10:25 +02: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 ) {
2016-03-23 18:39:41 +01:00
self . select ( slides [ index - 1 ] ) ;
2015-05-05 03:10:25 +02:00
} else {
self . select ( slides [ index ] ) ;
}
} else if ( currentIndex > index ) {
currentIndex -- ;
}
2016-03-23 18:39:41 +01:00
//clean the currentSlide when no more slide
if ( slides . length === 0 ) {
self . currentSlide = null ;
}
} ;
$scope . $watch ( 'noTransition' , function ( noTransition ) {
$element . data ( NO _TRANSITION , noTransition ) ;
} ) ;
2015-05-05 03:10:25 +02: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" >
2016-03-23 18:39:41 +01:00
< uib - carousel >
< uib - slide >
2015-05-05 03:10:25 +02:00
< img src = "http://placekitten.com/150/150" style = "margin:auto;" >
< div class = "carousel-caption" >
< p > Beautiful ! < / p >
< / d i v >
2016-03-23 18:39:41 +01:00
< / u i b - s l i d e >
< uib - slide >
2015-05-05 03:10:25 +02:00
< img src = "http://placekitten.com/100/150" style = "margin:auto;" >
< div class = "carousel-caption" >
< p > D ' aww ! < / p >
< / d i v >
2016-03-23 18:39:41 +01:00
< / u i b - s l i d e >
< / u i b - c a r o u s e l >
2015-05-05 03:10:25 +02:00
< / 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 >
* /
2016-03-23 18:39:41 +01:00
. directive ( 'uibCarousel' , [ function ( ) {
2015-05-05 03:10:25 +02:00
return {
transclude : true ,
replace : true ,
2016-03-23 18:39:41 +01:00
controller : 'UibCarouselController' ,
controllerAs : 'carousel' ,
2015-05-05 03:10:25 +02:00
require : 'carousel' ,
2016-03-23 18:39:41 +01:00
templateUrl : function ( element , attrs ) {
return attrs . templateUrl || 'template/carousel/carousel.html' ;
} ,
2015-05-05 03:10:25 +02:00
scope : {
interval : '=' ,
noTransition : '=' ,
2016-03-23 18:39:41 +01:00
noPause : '=' ,
noWrap : '&'
2015-05-05 03:10:25 +02: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 .
2016-03-23 18:39:41 +01:00
* @ param { number = } index The index of the slide . The slides will be sorted by this parameter .
2015-05-05 03:10:25 +02:00
*
* @ example
< example module = "ui.bootstrap" >
< file name = "index.html" >
< div ng - controller = "CarouselDemoCtrl" >
2016-03-23 18:39:41 +01:00
< uib - carousel >
< uib - slide ng - repeat = "slide in slides" active = "slide.active" index = "$index" >
2015-05-05 03:10:25 +02: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 >
2016-03-23 18:39:41 +01:00
< / u i b - s l i d e >
< / u i b - c a r o u s e l >
2015-05-05 03:10:25 +02:00
Interval , in milliseconds : < input type = "number" ng - model = "myInterval" >
< br / > Enter a negative number to stop the interval .
< / 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 >
* /
2016-03-23 18:39:41 +01:00
. directive ( 'uibSlide' , function ( ) {
2015-05-05 03:10:25 +02:00
return {
2016-03-23 18:39:41 +01:00
require : '^uibCarousel' ,
2015-05-05 03:10:25 +02:00
restrict : 'EA' ,
transclude : true ,
replace : true ,
2016-03-23 18:39:41 +01:00
templateUrl : function ( element , attrs ) {
return attrs . templateUrl || 'template/carousel/slide.html' ;
} ,
2015-05-05 03:10:25 +02:00
scope : {
2016-03-23 18:39:41 +01:00
active : '=?' ,
actual : '=?' ,
index : '=?'
2015-05-05 03:10:25 +02: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 ) ;
}
} ) ;
}
} ;
2016-03-23 18:39:41 +01:00
} )
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
. animation ( '.item' , [
'$injector' , '$animate' ,
function ( $injector , $animate ) {
var NO _TRANSITION = 'uib-noTransition' ,
SLIDE _DIRECTION = 'uib-slideDirection' ,
$animateCss = null ;
if ( $injector . has ( '$animateCss' ) ) {
$animateCss = $injector . get ( '$animateCss' ) ;
}
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
function removeClass ( element , className , callback ) {
element . removeClass ( className ) ;
if ( callback ) {
callback ( ) ;
}
}
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
return {
beforeAddClass : function ( element , className , done ) {
// Due to transclusion, noTransition property is on parent's scope
if ( className == 'active' && element . parent ( ) && element . parent ( ) . parent ( ) &&
! element . parent ( ) . parent ( ) . data ( NO _TRANSITION ) ) {
var stopped = false ;
var direction = element . data ( SLIDE _DIRECTION ) ;
var directionClass = direction == 'next' ? 'left' : 'right' ;
var removeClassFn = removeClass . bind ( this , element ,
directionClass + ' ' + direction , done ) ;
element . addClass ( direction ) ;
if ( $animateCss ) {
$animateCss ( element , { addClass : directionClass } )
. start ( )
. done ( removeClassFn ) ;
} else {
$animate . addClass ( element , directionClass ) . then ( function ( ) {
if ( ! stopped ) {
removeClassFn ( ) ;
}
done ( ) ;
} ) ;
}
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
return function ( ) {
stopped = true ;
} ;
}
done ( ) ;
2015-05-05 03:10:25 +02:00
} ,
2016-03-23 18:39:41 +01:00
beforeRemoveClass : function ( element , className , done ) {
// Due to transclusion, noTransition property is on parent's scope
if ( className === 'active' && element . parent ( ) && element . parent ( ) . parent ( ) &&
! element . parent ( ) . parent ( ) . data ( NO _TRANSITION ) ) {
var stopped = false ;
var direction = element . data ( SLIDE _DIRECTION ) ;
var directionClass = direction == 'next' ? 'left' : 'right' ;
var removeClassFn = removeClass . bind ( this , element , directionClass , done ) ;
if ( $animateCss ) {
$animateCss ( element , { addClass : directionClass } )
. start ( )
. done ( removeClassFn ) ;
} else {
$animate . addClass ( element , directionClass ) . then ( function ( ) {
if ( ! stopped ) {
removeClassFn ( ) ;
}
done ( ) ;
} ) ;
}
return function ( ) {
stopped = true ;
} ;
}
done ( ) ;
}
} ;
} ] ) ;
/* deprecated carousel below */
angular . module ( 'ui.bootstrap.carousel' )
. value ( '$carouselSuppressWarning' , false )
. controller ( 'CarouselController' , [ '$scope' , '$element' , '$controller' , '$log' , '$carouselSuppressWarning' , function ( $scope , $element , $controller , $log , $carouselSuppressWarning ) {
if ( ! $carouselSuppressWarning ) {
$log . warn ( 'CarouselController is now deprecated. Use UibCarouselController instead.' ) ;
}
angular . extend ( this , $controller ( 'UibCarouselController' , {
$scope : $scope ,
$element : $element
} ) ) ;
} ] )
. directive ( 'carousel' , [ '$log' , '$carouselSuppressWarning' , function ( $log , $carouselSuppressWarning ) {
return {
transclude : true ,
replace : true ,
controller : 'CarouselController' ,
controllerAs : 'carousel' ,
require : 'carousel' ,
templateUrl : function ( element , attrs ) {
return attrs . templateUrl || 'template/carousel/carousel.html' ;
2015-05-05 03:10:25 +02:00
} ,
2016-03-23 18:39:41 +01:00
scope : {
interval : '=' ,
noTransition : '=' ,
noPause : '=' ,
noWrap : '&'
2015-05-05 03:10:25 +02:00
} ,
2016-03-23 18:39:41 +01:00
link : function ( ) {
if ( ! $carouselSuppressWarning ) {
$log . warn ( 'carousel is now deprecated. Use uib-carousel instead.' ) ;
}
}
} ;
} ] )
. directive ( 'slide' , [ '$log' , '$carouselSuppressWarning' , function ( $log , $carouselSuppressWarning ) {
return {
require : '^carousel' ,
transclude : true ,
replace : true ,
templateUrl : function ( element , attrs ) {
return attrs . templateUrl || 'template/carousel/slide.html' ;
2015-05-05 03:10:25 +02:00
} ,
2016-03-23 18:39:41 +01:00
scope : {
active : '=?' ,
actual : '=?' ,
index : '=?'
2015-05-05 03:10:25 +02:00
} ,
2016-03-23 18:39:41 +01:00
link : function ( scope , element , attrs , carouselCtrl ) {
if ( ! $carouselSuppressWarning ) {
$log . warn ( 'slide is now deprecated. Use uib-slide instead.' ) ;
}
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-05 03:10:25 +02:00
}
} ;
2016-03-23 18:39:41 +01:00
} ] ) ;
angular . module ( 'ui.bootstrap.dateparser' , [ ] )
. service ( 'uibDateParser' , [ '$log' , '$locale' , 'orderByFilter' , function ( $log , $locale , orderByFilter ) {
// Pulled from https://github.com/mbostock/d3/blob/master/src/format/requote.js
var SPECIAL _CHARACTERS _REGEXP = /[\\\^\$\*\+\?\|\[\]\(\)\.\{\}]/g ;
var localeId ;
var formatCodeToRegex ;
this . init = function ( ) {
localeId = $locale . id ;
this . parsers = { } ;
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 ( '|' )
} ,
'HH' : {
regex : '(?:0|1)[0-9]|2[0-3]' ,
apply : function ( value ) { this . hours = + value ; }
} ,
'hh' : {
regex : '0[0-9]|1[0-2]' ,
apply : function ( value ) { this . hours = + value ; }
} ,
'H' : {
regex : '1?[0-9]|2[0-3]' ,
apply : function ( value ) { this . hours = + value ; }
} ,
'h' : {
regex : '[0-9]|1[0-2]' ,
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 ; }
} ,
'a' : {
regex : $locale . DATETIME _FORMATS . AMPMS . join ( '|' ) ,
apply : function ( value ) {
if ( this . hours === 12 ) {
this . hours = 0 ;
}
if ( value === 'PM' ) {
this . hours += 12 ;
}
}
}
} ;
} ;
this . init ( ) ;
2015-05-05 03:10:25 +02: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' )
} ;
}
2016-03-23 18:39:41 +01:00
this . parse = function ( input , format , baseDate ) {
if ( ! angular . isString ( input ) || ! format ) {
2015-05-05 03:10:25 +02:00
return input ;
}
format = $locale . DATETIME _FORMATS [ format ] || format ;
2016-03-23 18:39:41 +01:00
format = format . replace ( SPECIAL _CHARACTERS _REGEXP , '\\$&' ) ;
if ( $locale . id !== localeId ) {
this . init ( ) ;
}
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
if ( ! this . parsers [ format ] ) {
2015-05-05 03:10:25 +02:00
this . parsers [ format ] = createParser ( format ) ;
}
var parser = this . parsers [ format ] ,
regex = parser . regex ,
map = parser . map ,
results = input . match ( regex ) ;
2016-03-23 18:39:41 +01:00
if ( results && results . length ) {
var fields , dt ;
if ( angular . isDate ( baseDate ) && ! isNaN ( baseDate . getTime ( ) ) ) {
fields = {
year : baseDate . getFullYear ( ) ,
month : baseDate . getMonth ( ) ,
date : baseDate . getDate ( ) ,
hours : baseDate . getHours ( ) ,
minutes : baseDate . getMinutes ( ) ,
seconds : baseDate . getSeconds ( ) ,
milliseconds : baseDate . getMilliseconds ( )
} ;
} else {
if ( baseDate ) {
$log . warn ( 'dateparser:' , 'baseDate is not a valid date' ) ;
}
fields = { year : 1900 , month : 0 , date : 1 , hours : 0 , minutes : 0 , seconds : 0 , milliseconds : 0 } ;
}
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
for ( var i = 1 , n = results . length ; i < n ; i ++ ) {
2015-05-05 03:10:25 +02:00
var mapper = map [ i - 1 ] ;
2016-03-23 18:39:41 +01:00
if ( mapper . apply ) {
2015-05-05 03:10:25 +02:00
mapper . apply . call ( fields , results [ i ] ) ;
}
}
2016-03-23 18:39:41 +01:00
if ( isValid ( fields . year , fields . month , fields . date ) ) {
if ( angular . isDate ( baseDate ) && ! isNaN ( baseDate . getTime ( ) ) ) {
dt = new Date ( baseDate ) ;
dt . setFullYear ( fields . year , fields . month , fields . date ,
fields . hours , fields . minutes , fields . seconds ,
fields . milliseconds || 0 ) ;
} else {
dt = new Date ( fields . year , fields . month , fields . date ,
fields . hours , fields . minutes , fields . seconds ,
fields . milliseconds || 0 ) ;
}
2015-05-05 03:10:25 +02: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 ) {
2016-03-23 18:39:41 +01:00
if ( date < 1 ) {
return false ;
}
if ( month === 1 && date > 28 ) {
return date === 29 && ( ( year % 4 === 0 && year % 100 !== 0 ) || year % 400 === 0 ) ;
2015-05-05 03:10:25 +02:00
}
2016-03-23 18:39:41 +01:00
if ( month === 3 || month === 5 || month === 8 || month === 10 ) {
return date < 31 ;
2015-05-05 03:10:25 +02:00
}
return true ;
}
} ] ) ;
2016-03-23 18:39:41 +01:00
/* Deprecated dateparser below */
angular . module ( 'ui.bootstrap.dateparser' )
. value ( '$dateParserSuppressWarning' , false )
. service ( 'dateParser' , [ '$log' , '$dateParserSuppressWarning' , 'uibDateParser' , function ( $log , $dateParserSuppressWarning , uibDateParser ) {
if ( ! $dateParserSuppressWarning ) {
$log . warn ( 'dateParser is now deprecated. Use uibDateParser instead.' ) ;
}
angular . extend ( this , uibDateParser ) ;
} ] ) ;
2015-05-05 03:10:25 +02:00
angular . module ( 'ui.bootstrap.position' , [ ] )
/ * *
* 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 . ) .
* /
2016-03-23 18:39:41 +01:00
. factory ( '$uibPosition' , [ '$document' , '$window' , function ( $document , $window ) {
2015-05-05 03:10:25 +02: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 ] ;
}
/ * *
* Checks if a given element is statically positioned
* @ param element - raw DOM element
* /
function isStaticPositioned ( element ) {
return ( getStyle ( element , 'position' ) || 'static' ) === 'static' ;
}
/ * *
* returns the closest , non - statically positioned parentOffset of a given element
* @ param element
* /
2016-03-23 18:39:41 +01:00
var parentOffsetEl = function ( element ) {
2015-05-05 03:10:25 +02:00
var docDomEl = $document [ 0 ] ;
var offsetParent = element . offsetParent || docDomEl ;
while ( offsetParent && offsetParent !== docDomEl && isStaticPositioned ( offsetParent ) ) {
offsetParent = offsetParent . offsetParent ;
}
return offsetParent || docDomEl ;
} ;
return {
/ * *
* Provides read - only equivalent of jQuery ' s position function :
* http : //api.jquery.com/position/
* /
2016-03-23 18:39:41 +01:00
position : function ( element ) {
2015-05-05 03:10:25 +02:00
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 ;
}
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
} ;
} ,
/ * *
* Provides read - only equivalent of jQuery ' s offset function :
* http : //api.jquery.com/offset/
* /
2016-03-23 18:39:41 +01:00
offset : function ( element ) {
2015-05-05 03:10:25 +02:00
var boundingClientRect = element [ 0 ] . getBoundingClientRect ( ) ;
return {
width : boundingClientRect . width || element . prop ( 'offsetWidth' ) ,
height : boundingClientRect . height || element . prop ( 'offsetHeight' ) ,
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
* /
2016-03-23 18:39:41 +01:00
positionElements : function ( hostEl , targetEl , positionStr , appendToBody ) {
2015-05-05 03:10:25 +02:00
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 = {
2016-03-23 18:39:41 +01:00
center : function ( ) {
2015-05-05 03:10:25 +02:00
return hostElPos . left + hostElPos . width / 2 - targetElWidth / 2 ;
} ,
2016-03-23 18:39:41 +01:00
left : function ( ) {
2015-05-05 03:10:25 +02:00
return hostElPos . left ;
} ,
2016-03-23 18:39:41 +01:00
right : function ( ) {
2015-05-05 03:10:25 +02:00
return hostElPos . left + hostElPos . width ;
}
} ;
var shiftHeight = {
2016-03-23 18:39:41 +01:00
center : function ( ) {
2015-05-05 03:10:25 +02:00
return hostElPos . top + hostElPos . height / 2 - targetElHeight / 2 ;
} ,
2016-03-23 18:39:41 +01:00
top : function ( ) {
2015-05-05 03:10:25 +02:00
return hostElPos . top ;
} ,
2016-03-23 18:39:41 +01:00
bottom : function ( ) {
2015-05-05 03:10:25 +02:00
return hostElPos . top + hostElPos . height ;
}
} ;
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 ;
}
} ;
} ] ) ;
2016-03-23 18:39:41 +01:00
/* Deprecated position below */
angular . module ( 'ui.bootstrap.position' )
. value ( '$positionSuppressWarning' , false )
. service ( '$position' , [ '$log' , '$positionSuppressWarning' , '$uibPosition' , function ( $log , $positionSuppressWarning , $uibPosition ) {
if ( ! $positionSuppressWarning ) {
$log . warn ( '$position is now deprecated. Use $uibPosition instead.' ) ;
}
angular . extend ( this , $uibPosition ) ;
} ] ) ;
2015-05-05 03:10:25 +02:00
angular . module ( 'ui.bootstrap.datepicker' , [ 'ui.bootstrap.dateparser' , 'ui.bootstrap.position' ] )
2016-03-23 18:39:41 +01:00
. value ( '$datepickerSuppressError' , false )
. constant ( 'uibDatepickerConfig' , {
2015-05-05 03:10:25 +02:00
formatDay : 'dd' ,
formatMonth : 'MMMM' ,
formatYear : 'yyyy' ,
formatDayHeader : 'EEE' ,
formatDayTitle : 'MMMM yyyy' ,
formatMonthTitle : 'yyyy' ,
datepickerMode : 'day' ,
minMode : 'day' ,
maxMode : 'year' ,
showWeeks : true ,
startingDay : 0 ,
yearRange : 20 ,
minDate : null ,
2016-03-23 18:39:41 +01:00
maxDate : null ,
shortcutPropagation : false
2015-05-05 03:10:25 +02:00
} )
2016-03-23 18:39:41 +01:00
. controller ( 'UibDatepickerController' , [ '$scope' , '$attrs' , '$parse' , '$interpolate' , '$log' , 'dateFilter' , 'uibDatepickerConfig' , '$datepickerSuppressError' , function ( $scope , $attrs , $parse , $interpolate , $log , dateFilter , datepickerConfig , $datepickerSuppressError ) {
2015-05-05 03:10:25 +02:00
var self = this ,
ngModelCtrl = { $setViewValue : angular . noop } ; // nullModelCtrl;
// Modes chain
this . modes = [ 'day' , 'month' , 'year' ] ;
// Configuration attributes
angular . forEach ( [ 'formatDay' , 'formatMonth' , 'formatYear' , 'formatDayHeader' , 'formatDayTitle' , 'formatMonthTitle' ,
2016-03-23 18:39:41 +01:00
'showWeeks' , 'startingDay' , 'yearRange' , 'shortcutPropagation' ] , function ( key , index ) {
self [ key ] = angular . isDefined ( $attrs [ key ] ) ? ( index < 6 ? $interpolate ( $attrs [ key ] ) ( $scope . $parent ) : $scope . $parent . $eval ( $attrs [ key ] ) ) : datepickerConfig [ key ] ;
2015-05-05 03:10:25 +02:00
} ) ;
// Watchable date attributes
2016-03-23 18:39:41 +01:00
angular . forEach ( [ 'minDate' , 'maxDate' ] , function ( key ) {
if ( $attrs [ key ] ) {
2015-05-05 03:10:25 +02:00
$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 ;
}
} ) ;
2016-03-23 18:39:41 +01:00
angular . forEach ( [ 'minMode' , 'maxMode' ] , function ( key ) {
if ( $attrs [ key ] ) {
$scope . $parent . $watch ( $parse ( $attrs [ key ] ) , function ( value ) {
self [ key ] = angular . isDefined ( value ) ? value : $attrs [ key ] ;
$scope [ key ] = self [ key ] ;
if ( ( key == 'minMode' && self . modes . indexOf ( $scope . datepickerMode ) < self . modes . indexOf ( self [ key ] ) ) || ( key == 'maxMode' && self . modes . indexOf ( $scope . datepickerMode ) > self . modes . indexOf ( self [ key ] ) ) ) {
$scope . datepickerMode = self [ key ] ;
}
} ) ;
} else {
self [ key ] = datepickerConfig [ key ] || null ;
$scope [ key ] = self [ key ] ;
}
} ) ;
2015-05-05 03:10:25 +02:00
$scope . datepickerMode = $scope . datepickerMode || datepickerConfig . datepickerMode ;
$scope . uniqueId = 'datepicker-' + $scope . $id + '-' + Math . floor ( Math . random ( ) * 10000 ) ;
2016-03-23 18:39:41 +01: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 ( ) ;
}
2015-05-05 03:10:25 +02:00
$scope . isActive = function ( dateObject ) {
if ( self . compare ( dateObject . date , self . activeDate ) === 0 ) {
$scope . activeDateId = dateObject . uid ;
return true ;
}
return false ;
} ;
2016-03-23 18:39:41 +01:00
this . init = function ( ngModelCtrl _ ) {
2015-05-05 03:10:25 +02:00
ngModelCtrl = ngModelCtrl _ ;
ngModelCtrl . $render = function ( ) {
self . render ( ) ;
} ;
} ;
this . render = function ( ) {
2016-03-23 18:39:41 +01:00
if ( ngModelCtrl . $viewValue ) {
var date = new Date ( ngModelCtrl . $viewValue ) ,
2015-05-05 03:10:25 +02:00
isValid = ! isNaN ( date ) ;
2016-03-23 18:39:41 +01:00
if ( isValid ) {
2015-05-05 03:10:25 +02:00
this . activeDate = date ;
2016-03-23 18:39:41 +01:00
} else if ( ! $datepickerSuppressError ) {
2015-05-05 03:10:25 +02:00
$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.' ) ;
}
}
this . refreshView ( ) ;
} ;
this . refreshView = function ( ) {
2016-03-23 18:39:41 +01:00
if ( this . element ) {
2015-05-05 03:10:25 +02:00
this . _refreshView ( ) ;
2016-03-23 18:39:41 +01:00
var date = ngModelCtrl . $viewValue ? new Date ( ngModelCtrl . $viewValue ) : null ;
ngModelCtrl . $setValidity ( 'dateDisabled' , ! date || ( this . element && ! this . isDisabled ( date ) ) ) ;
2015-05-05 03:10:25 +02:00
}
} ;
this . createDateObject = function ( date , format ) {
2016-03-23 18:39:41 +01:00
var model = ngModelCtrl . $viewValue ? new Date ( ngModelCtrl . $viewValue ) : null ;
2015-05-05 03:10:25 +02:00
return {
date : date ,
label : dateFilter ( date , format ) ,
selected : model && this . compare ( date , model ) === 0 ,
disabled : this . isDisabled ( date ) ,
2016-03-23 18:39:41 +01:00
current : this . compare ( date , new Date ( ) ) === 0 ,
customClass : this . customClass ( date )
2015-05-05 03:10:25 +02:00
} ;
} ;
2016-03-23 18:39:41 +01:00
this . isDisabled = function ( date ) {
2015-05-05 03:10:25 +02:00
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 } ) ) ) ;
} ;
2016-03-23 18:39:41 +01:00
this . customClass = function ( date ) {
return $scope . customClass ( { date : date , mode : $scope . datepickerMode } ) ;
} ;
2015-05-05 03:10:25 +02: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 ;
} ;
2016-03-23 18:39:41 +01:00
$scope . select = function ( date ) {
if ( $scope . datepickerMode === self . minMode ) {
var dt = ngModelCtrl . $viewValue ? new Date ( ngModelCtrl . $viewValue ) : new Date ( 0 , 0 , 0 , 0 , 0 , 0 , 0 ) ;
dt . setFullYear ( date . getFullYear ( ) , date . getMonth ( ) , date . getDate ( ) ) ;
ngModelCtrl . $setViewValue ( dt ) ;
2015-05-05 03:10:25 +02:00
ngModelCtrl . $render ( ) ;
} else {
self . activeDate = date ;
2016-03-23 18:39:41 +01:00
$scope . datepickerMode = self . modes [ self . modes . indexOf ( $scope . datepickerMode ) - 1 ] ;
2015-05-05 03:10:25 +02:00
}
} ;
2016-03-23 18:39:41 +01:00
$scope . move = function ( direction ) {
2015-05-05 03:10:25 +02:00
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 ( ) ;
} ;
2016-03-23 18:39:41 +01:00
$scope . toggleMode = function ( direction ) {
2015-05-05 03:10:25 +02:00
direction = direction || 1 ;
if ( ( $scope . datepickerMode === self . maxMode && direction === 1 ) || ( $scope . datepickerMode === self . minMode && direction === - 1 ) ) {
return ;
}
2016-03-23 18:39:41 +01:00
$scope . datepickerMode = self . modes [ self . modes . indexOf ( $scope . datepickerMode ) + direction ] ;
2015-05-05 03:10:25 +02:00
} ;
// Key event mapper
2016-03-23 18:39:41 +01:00
$scope . keys = { 13 : 'enter' , 32 : 'space' , 33 : 'pageup' , 34 : 'pagedown' , 35 : 'end' , 36 : 'home' , 37 : 'left' , 38 : 'up' , 39 : 'right' , 40 : 'down' } ;
2015-05-05 03:10:25 +02:00
var focusElement = function ( ) {
2016-03-23 18:39:41 +01:00
self . element [ 0 ] . focus ( ) ;
2015-05-05 03:10:25 +02:00
} ;
// Listen for focus requests from popup directive
2016-03-23 18:39:41 +01:00
$scope . $on ( 'uib:datepicker.focus' , focusElement ) ;
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
$scope . keydown = function ( evt ) {
2015-05-05 03:10:25 +02:00
var key = $scope . keys [ evt . which ] ;
2016-03-23 18:39:41 +01:00
if ( ! key || evt . shiftKey || evt . altKey ) {
2015-05-05 03:10:25 +02:00
return ;
}
evt . preventDefault ( ) ;
2016-03-23 18:39:41 +01:00
if ( ! self . shortcutPropagation ) {
evt . stopPropagation ( ) ;
}
2015-05-05 03:10:25 +02:00
if ( key === 'enter' || key === 'space' ) {
2016-03-23 18:39:41 +01:00
if ( self . isDisabled ( self . activeDate ) ) {
2015-05-05 03:10:25 +02:00
return ; // do nothing
}
$scope . select ( self . activeDate ) ;
} else if ( evt . ctrlKey && ( key === 'up' || key === 'down' ) ) {
$scope . toggleMode ( key === 'up' ? 1 : - 1 ) ;
} else {
self . handleKeyDown ( key , evt ) ;
self . refreshView ( ) ;
}
} ;
} ] )
2016-03-23 18:39:41 +01:00
. controller ( 'UibDaypickerController' , [ '$scope' , '$element' , 'dateFilter' , function ( scope , $element , dateFilter ) {
var DAYS _IN _MONTH = [ 31 , 28 , 31 , 30 , 31 , 30 , 31 , 31 , 30 , 31 , 30 , 31 ] ;
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
this . step = { months : 1 } ;
this . element = $element ;
function getDaysInMonth ( year , month ) {
return ( ( month === 1 ) && ( year % 4 === 0 ) && ( ( year % 100 !== 0 ) || ( year % 400 === 0 ) ) ) ? 29 : DAYS _IN _MONTH [ month ] ;
}
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
this . init = function ( ctrl ) {
angular . extend ( ctrl , this ) ;
scope . showWeeks = ctrl . showWeeks ;
ctrl . refreshView ( ) ;
} ;
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
this . getDates = function ( startDate , n ) {
var dates = new Array ( n ) , current = new Date ( startDate ) , i = 0 , date ;
while ( i < n ) {
date = new Date ( current ) ;
dates [ i ++ ] = date ;
current . setDate ( current . getDate ( ) + 1 ) ;
}
return dates ;
} ;
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
this . _refreshView = function ( ) {
var year = this . activeDate . getFullYear ( ) ,
month = this . activeDate . getMonth ( ) ,
firstDayOfMonth = new Date ( this . activeDate ) ;
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
firstDayOfMonth . setFullYear ( year , month , 1 ) ;
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
var difference = this . startingDay - firstDayOfMonth . getDay ( ) ,
numDisplayedFromPreviousMonth = ( difference > 0 ) ? 7 - difference : - difference ,
firstDate = new Date ( firstDayOfMonth ) ;
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
if ( numDisplayedFromPreviousMonth > 0 ) {
firstDate . setDate ( - numDisplayedFromPreviousMonth + 1 ) ;
}
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
// 42 is the number of days on a six-month calendar
var days = this . getDates ( firstDate , 42 ) ;
for ( var i = 0 ; i < 42 ; i ++ ) {
days [ i ] = angular . extend ( this . createDateObject ( days [ i ] , this . formatDay ) , {
secondary : days [ i ] . getMonth ( ) !== month ,
uid : scope . uniqueId + '-' + i
} ) ;
}
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
scope . labels = new Array ( 7 ) ;
for ( var j = 0 ; j < 7 ; j ++ ) {
scope . labels [ j ] = {
abbr : dateFilter ( days [ j ] . date , this . formatDayHeader ) ,
full : dateFilter ( days [ j ] . date , 'EEEE' )
} ;
}
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
scope . title = dateFilter ( this . activeDate , this . formatDayTitle ) ;
scope . rows = this . split ( days , 7 ) ;
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
if ( scope . showWeeks ) {
scope . weekNumbers = [ ] ;
var thursdayIndex = ( 4 + 7 - this . startingDay ) % 7 ,
numWeeks = scope . rows . length ;
for ( var curWeek = 0 ; curWeek < numWeeks ; curWeek ++ ) {
scope . weekNumbers . push (
getISO8601WeekNumber ( scope . rows [ curWeek ] [ thursdayIndex ] . date ) ) ;
}
}
} ;
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
this . compare = function ( date1 , date2 ) {
return ( new Date ( date1 . getFullYear ( ) , date1 . getMonth ( ) , date1 . getDate ( ) ) - new Date ( date2 . getFullYear ( ) , date2 . getMonth ( ) , date2 . getDate ( ) ) ) ;
} ;
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +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-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
this . handleKeyDown = function ( key , evt ) {
var date = this . 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 = this . activeDate . getMonth ( ) + ( key === 'pageup' ? - 1 : 1 ) ;
this . activeDate . setMonth ( month , 1 ) ;
date = Math . min ( getDaysInMonth ( this . activeDate . getFullYear ( ) , this . activeDate . getMonth ( ) ) , date ) ;
} else if ( key === 'home' ) {
date = 1 ;
} else if ( key === 'end' ) {
date = getDaysInMonth ( this . activeDate . getFullYear ( ) , this . activeDate . getMonth ( ) ) ;
2015-05-05 03:10:25 +02:00
}
2016-03-23 18:39:41 +01:00
this . activeDate . setDate ( date ) ;
2015-05-05 03:10:25 +02:00
} ;
} ] )
2016-03-23 18:39:41 +01:00
. controller ( 'UibMonthpickerController' , [ '$scope' , '$element' , 'dateFilter' , function ( scope , $element , dateFilter ) {
this . step = { years : 1 } ;
this . element = $element ;
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
this . init = function ( ctrl ) {
angular . extend ( ctrl , this ) ;
ctrl . refreshView ( ) ;
} ;
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
this . _refreshView = function ( ) {
var months = new Array ( 12 ) ,
year = this . activeDate . getFullYear ( ) ,
date ;
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
for ( var i = 0 ; i < 12 ; i ++ ) {
date = new Date ( this . activeDate ) ;
date . setFullYear ( year , i , 1 ) ;
months [ i ] = angular . extend ( this . createDateObject ( date , this . formatMonth ) , {
uid : scope . uniqueId + '-' + i
} ) ;
}
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
scope . title = dateFilter ( this . activeDate , this . formatMonthTitle ) ;
scope . rows = this . split ( months , 3 ) ;
} ;
this . compare = function ( date1 , date2 ) {
return new Date ( date1 . getFullYear ( ) , date1 . getMonth ( ) ) - new Date ( date2 . getFullYear ( ) , date2 . getMonth ( ) ) ;
} ;
this . handleKeyDown = function ( key , evt ) {
var date = this . 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 = this . activeDate . getFullYear ( ) + ( key === 'pageup' ? - 1 : 1 ) ;
this . activeDate . setFullYear ( year ) ;
} else if ( key === 'home' ) {
date = 0 ;
} else if ( key === 'end' ) {
date = 11 ;
2015-05-05 03:10:25 +02:00
}
2016-03-23 18:39:41 +01:00
this . activeDate . setMonth ( date ) ;
2015-05-05 03:10:25 +02:00
} ;
} ] )
2016-03-23 18:39:41 +01:00
. controller ( 'UibYearpickerController' , [ '$scope' , '$element' , 'dateFilter' , function ( scope , $element , dateFilter ) {
var range ;
this . element = $element ;
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
function getStartingYear ( year ) {
return parseInt ( ( year - 1 ) / range , 10 ) * range + 1 ;
}
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
this . yearpickerInit = function ( ) {
range = this . yearRange ;
this . step = { years : range } ;
} ;
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
this . _refreshView = function ( ) {
var years = new Array ( range ) , date ;
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
for ( var i = 0 , start = getStartingYear ( this . activeDate . getFullYear ( ) ) ; i < range ; i ++ ) {
date = new Date ( this . activeDate ) ;
date . setFullYear ( start + i , 0 , 1 ) ;
years [ i ] = angular . extend ( this . createDateObject ( date , this . formatYear ) , {
uid : scope . uniqueId + '-' + i
} ) ;
}
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
scope . title = [ years [ 0 ] . label , years [ range - 1 ] . label ] . join ( ' - ' ) ;
scope . rows = this . split ( years , 5 ) ;
} ;
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
this . compare = function ( date1 , date2 ) {
return date1 . getFullYear ( ) - date2 . getFullYear ( ) ;
} ;
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
this . handleKeyDown = function ( key , evt ) {
var date = this . 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 ) * this . step . years ;
} else if ( key === 'home' ) {
date = getStartingYear ( this . activeDate . getFullYear ( ) ) ;
} else if ( key === 'end' ) {
date = getStartingYear ( this . activeDate . getFullYear ( ) ) + range - 1 ;
2015-05-05 03:10:25 +02:00
}
2016-03-23 18:39:41 +01:00
this . activeDate . setFullYear ( date ) ;
2015-05-05 03:10:25 +02:00
} ;
} ] )
2016-03-23 18:39:41 +01:00
. directive ( 'uibDatepicker' , function ( ) {
2015-05-05 03:10:25 +02:00
return {
2016-03-23 18:39:41 +01:00
replace : true ,
templateUrl : function ( element , attrs ) {
return attrs . templateUrl || 'template/datepicker/datepicker.html' ;
} ,
2015-05-05 03:10:25 +02:00
scope : {
2016-03-23 18:39:41 +01:00
datepickerMode : '=?' ,
dateDisabled : '&' ,
customClass : '&' ,
shortcutPropagation : '&?'
2015-05-05 03:10:25 +02:00
} ,
2016-03-23 18:39:41 +01:00
require : [ 'uibDatepicker' , '^ngModel' ] ,
controller : 'UibDatepickerController' ,
controllerAs : 'datepicker' ,
link : function ( scope , element , attrs , ctrls ) {
var datepickerCtrl = ctrls [ 0 ] , ngModelCtrl = ctrls [ 1 ] ;
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
datepickerCtrl . init ( ngModelCtrl ) ;
}
} ;
} )
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
. directive ( 'uibDaypicker' , function ( ) {
return {
replace : true ,
templateUrl : function ( element , attrs ) {
return attrs . templateUrl || 'template/datepicker/day.html' ;
} ,
require : [ '^?uibDatepicker' , 'uibDaypicker' , '^?datepicker' ] ,
controller : 'UibDaypickerController' ,
link : function ( scope , element , attrs , ctrls ) {
var datepickerCtrl = ctrls [ 0 ] || ctrls [ 2 ] ,
daypickerCtrl = ctrls [ 1 ] ;
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
daypickerCtrl . init ( datepickerCtrl ) ;
}
} ;
} )
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
. directive ( 'uibMonthpicker' , function ( ) {
return {
replace : true ,
templateUrl : function ( element , attrs ) {
return attrs . templateUrl || 'template/datepicker/month.html' ;
} ,
require : [ '^?uibDatepicker' , 'uibMonthpicker' , '^?datepicker' ] ,
controller : 'UibMonthpickerController' ,
link : function ( scope , element , attrs , ctrls ) {
var datepickerCtrl = ctrls [ 0 ] || ctrls [ 2 ] ,
monthpickerCtrl = ctrls [ 1 ] ;
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
monthpickerCtrl . init ( datepickerCtrl ) ;
}
} ;
} )
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
. directive ( 'uibYearpicker' , function ( ) {
return {
replace : true ,
templateUrl : function ( element , attrs ) {
return attrs . templateUrl || 'template/datepicker/year.html' ;
} ,
require : [ '^?uibDatepicker' , 'uibYearpicker' , '^?datepicker' ] ,
controller : 'UibYearpickerController' ,
link : function ( scope , element , attrs , ctrls ) {
var ctrl = ctrls [ 0 ] || ctrls [ 2 ] ;
angular . extend ( ctrl , ctrls [ 1 ] ) ;
ctrl . yearpickerInit ( ) ;
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
ctrl . refreshView ( ) ;
}
} ;
} )
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
. constant ( 'uibDatepickerPopupConfig' , {
datepickerPopup : 'yyyy-MM-dd' ,
datepickerPopupTemplateUrl : 'template/datepicker/popup.html' ,
datepickerTemplateUrl : 'template/datepicker/datepicker.html' ,
html5Types : {
date : 'yyyy-MM-dd' ,
'datetime-local' : 'yyyy-MM-ddTHH:mm:ss.sss' ,
'month' : 'yyyy-MM'
} ,
currentText : 'Today' ,
clearText : 'Clear' ,
closeText : 'Done' ,
closeOnDateSelection : true ,
appendToBody : false ,
showButtonBar : true ,
onOpenFocus : true
} )
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
. controller ( 'UibDatepickerPopupController' , [ '$scope' , '$element' , '$attrs' , '$compile' , '$parse' , '$document' , '$rootScope' , '$uibPosition' , 'dateFilter' , 'uibDateParser' , 'uibDatepickerPopupConfig' , '$timeout' ,
function ( scope , element , attrs , $compile , $parse , $document , $rootScope , $position , dateFilter , dateParser , datepickerPopupConfig , $timeout ) {
var self = this ;
var cache = { } ,
isHtml5DateInput = false ;
var dateFormat , closeOnDateSelection , appendToBody , onOpenFocus ,
datepickerPopupTemplateUrl , datepickerTemplateUrl , popupEl , datepickerEl ,
ngModel , $popup ;
scope . watchData = { } ;
this . init = function ( _ngModel _ ) {
ngModel = _ngModel _ ;
closeOnDateSelection = angular . isDefined ( attrs . closeOnDateSelection ) ? scope . $parent . $eval ( attrs . closeOnDateSelection ) : datepickerPopupConfig . closeOnDateSelection ;
appendToBody = angular . isDefined ( attrs . datepickerAppendToBody ) ? scope . $parent . $eval ( attrs . datepickerAppendToBody ) : datepickerPopupConfig . appendToBody ;
onOpenFocus = angular . isDefined ( attrs . onOpenFocus ) ? scope . $parent . $eval ( attrs . onOpenFocus ) : datepickerPopupConfig . onOpenFocus ;
datepickerPopupTemplateUrl = angular . isDefined ( attrs . datepickerPopupTemplateUrl ) ? attrs . datepickerPopupTemplateUrl : datepickerPopupConfig . datepickerPopupTemplateUrl ;
datepickerTemplateUrl = angular . isDefined ( attrs . datepickerTemplateUrl ) ? attrs . datepickerTemplateUrl : datepickerPopupConfig . datepickerTemplateUrl ;
scope . showButtonBar = angular . isDefined ( attrs . showButtonBar ) ? scope . $parent . $eval ( attrs . showButtonBar ) : datepickerPopupConfig . showButtonBar ;
if ( datepickerPopupConfig . html5Types [ attrs . type ] ) {
dateFormat = datepickerPopupConfig . html5Types [ attrs . type ] ;
isHtml5DateInput = true ;
} else {
dateFormat = attrs . datepickerPopup || attrs . uibDatepickerPopup || datepickerPopupConfig . datepickerPopup ;
attrs . $observe ( 'uibDatepickerPopup' , 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 ( 'uibDatepickerPopup must have a date format specified.' ) ;
}
}
2015-05-05 03:10:25 +02:00
} ) ;
2016-03-23 18:39:41 +01:00
}
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
if ( ! dateFormat ) {
throw new Error ( 'uibDatepickerPopup must have a date format specified.' ) ;
}
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
if ( isHtml5DateInput && attrs . datepickerPopup ) {
throw new Error ( 'HTML5 date input types do not support custom formats.' ) ;
}
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
// popup element used to display calendar
popupEl = angular . element ( '<div uib-datepicker-popup-wrap><div uib-datepicker></div></div>' ) ;
popupEl . attr ( {
'ng-model' : 'date' ,
'ng-change' : 'dateSelection(date)' ,
'template-url' : datepickerPopupTemplateUrl
} ) ;
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
// datepicker element
datepickerEl = angular . element ( popupEl . children ( ) [ 0 ] ) ;
datepickerEl . attr ( 'template-url' , datepickerTemplateUrl ) ;
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
if ( isHtml5DateInput ) {
if ( attrs . type === 'month' ) {
datepickerEl . attr ( 'datepicker-mode' , '"month"' ) ;
datepickerEl . attr ( 'min-mode' , 'month' ) ;
}
}
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
if ( attrs . datepickerOptions ) {
var options = scope . $parent . $eval ( attrs . datepickerOptions ) ;
if ( options && options . initDate ) {
scope . initDate = options . initDate ;
datepickerEl . attr ( 'init-date' , 'initDate' ) ;
delete options . initDate ;
}
angular . forEach ( options , function ( value , option ) {
datepickerEl . attr ( cameltoDash ( option ) , value ) ;
2015-05-05 03:10:25 +02:00
} ) ;
2016-03-23 18:39:41 +01:00
}
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
angular . forEach ( [ 'minMode' , 'maxMode' , 'minDate' , 'maxDate' , 'datepickerMode' , 'initDate' , 'shortcutPropagation' ] , function ( key ) {
if ( attrs [ key ] ) {
var getAttribute = $parse ( attrs [ key ] ) ;
scope . $parent . $watch ( getAttribute , function ( value ) {
scope . watchData [ key ] = value ;
if ( key === 'minDate' || key === 'maxDate' ) {
cache [ key ] = new Date ( value ) ;
2015-05-05 03:10:25 +02:00
}
2016-03-23 18:39:41 +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 ( angular . isFunction ( setAttribute ) && value !== oldvalue ) {
setAttribute ( scope . $parent , value ) ;
}
} ) ;
2015-05-05 03:10:25 +02:00
}
}
2016-03-23 18:39:41 +01:00
} ) ;
if ( attrs . dateDisabled ) {
datepickerEl . attr ( 'date-disabled' , 'dateDisabled({ date: date, mode: mode })' ) ;
}
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
if ( attrs . showWeeks ) {
datepickerEl . attr ( 'show-weeks' , attrs . showWeeks ) ;
2015-05-05 03:10:25 +02:00
}
2016-03-23 18:39:41 +01:00
if ( attrs . customClass ) {
datepickerEl . attr ( 'custom-class' , 'customClass({ date: date, mode: mode })' ) ;
}
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 ;
2015-05-05 03:10:25 +02:00
} ) ;
}
2016-03-23 18:39:41 +01:00
// Detect changes in the view from the text box
ngModel . $viewChangeListeners . push ( function ( ) {
scope . date = dateParser . parse ( ngModel . $viewValue , dateFormat , scope . date ) ;
} ) ;
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
element . bind ( 'keydown' , inputKeydownBind ) ;
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
$popup = $compile ( popupEl ) ( scope ) ;
// Prevent jQuery cache memory leak (template is now redundant after linking)
popupEl . remove ( ) ;
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
if ( appendToBody ) {
$document . find ( 'body' ) . append ( $popup ) ;
} else {
element . after ( $popup ) ;
2015-05-05 03:10:25 +02:00
}
2016-03-23 18:39:41 +01:00
scope . $on ( '$destroy' , function ( ) {
if ( scope . isOpen === true ) {
if ( ! $rootScope . $$phase ) {
scope . $apply ( function ( ) {
scope . isOpen = false ;
} ) ;
}
}
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
$popup . remove ( ) ;
element . unbind ( 'keydown' , inputKeydownBind ) ;
$document . unbind ( 'click' , documentClickBind ) ;
} ) ;
2015-05-05 03:10:25 +02:00
} ;
2016-03-23 18:39:41 +01:00
scope . getText = function ( key ) {
return scope [ key + 'Text' ] || datepickerPopupConfig [ key + 'Text' ] ;
2015-05-05 03:10:25 +02:00
} ;
2016-03-23 18:39:41 +01:00
scope . isDisabled = function ( date ) {
if ( date === 'today' ) {
date = new Date ( ) ;
2015-05-05 03:10:25 +02:00
}
2016-03-23 18:39:41 +01:00
return ( ( scope . watchData . minDate && scope . compare ( date , cache . minDate ) < 0 ) ||
( scope . watchData . maxDate && scope . compare ( date , cache . maxDate ) > 0 ) ) ;
2015-05-05 03:10:25 +02:00
} ;
2016-03-23 18:39:41 +01:00
scope . compare = function ( date1 , date2 ) {
return ( new Date ( date1 . getFullYear ( ) , date1 . getMonth ( ) , date1 . getDate ( ) ) - new Date ( date2 . getFullYear ( ) , date2 . getMonth ( ) , date2 . getDate ( ) ) ) ;
2015-05-05 03:10:25 +02:00
} ;
2016-03-23 18:39:41 +01:00
// Inner change
scope . dateSelection = function ( dt ) {
if ( angular . isDefined ( dt ) ) {
scope . date = dt ;
}
var date = scope . date ? dateFilter ( scope . date , dateFormat ) : null ; // Setting to NULL is necessary for form validators to function
element . val ( date ) ;
ngModel . $setViewValue ( date ) ;
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
if ( closeOnDateSelection ) {
scope . isOpen = false ;
element [ 0 ] . focus ( ) ;
2015-05-05 03:10:25 +02:00
}
} ;
2016-03-23 18:39:41 +01:00
scope . keydown = function ( evt ) {
if ( evt . which === 27 ) {
scope . isOpen = false ;
element [ 0 ] . focus ( ) ;
}
2015-05-05 03:10:25 +02:00
} ;
2016-03-23 18:39:41 +01:00
scope . select = function ( date ) {
if ( date === 'today' ) {
var today = new Date ( ) ;
if ( angular . isDate ( scope . date ) ) {
date = new Date ( scope . date ) ;
date . setFullYear ( today . getFullYear ( ) , today . getMonth ( ) , today . getDate ( ) ) ;
} else {
date = new Date ( today . setHours ( 0 , 0 , 0 , 0 ) ) ;
}
}
scope . dateSelection ( date ) ;
2015-05-05 03:10:25 +02:00
} ;
2016-03-23 18:39:41 +01:00
scope . close = function ( ) {
scope . isOpen = false ;
element [ 0 ] . focus ( ) ;
2015-05-05 03:10:25 +02:00
} ;
2016-03-23 18:39:41 +01:00
scope . $watch ( 'isOpen' , function ( value ) {
if ( value ) {
scope . position = appendToBody ? $position . offset ( element ) : $position . position ( element ) ;
scope . position . top = scope . position . top + element . prop ( 'offsetHeight' ) ;
$timeout ( function ( ) {
if ( onOpenFocus ) {
scope . $broadcast ( 'uib:datepicker.focus' ) ;
}
$document . bind ( 'click' , documentClickBind ) ;
} , 0 , false ) ;
} else {
$document . unbind ( 'click' , documentClickBind ) ;
2015-05-05 03:10:25 +02:00
}
2016-03-23 18:39:41 +01:00
} ) ;
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
function cameltoDash ( string ) {
return string . replace ( /([A-Z])/g , function ( $1 ) { return '-' + $1 . toLowerCase ( ) ; } ) ;
}
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
function parseDate ( viewValue ) {
if ( angular . isNumber ( viewValue ) ) {
// presumably timestamp to date object
viewValue = new Date ( viewValue ) ;
}
if ( ! viewValue ) {
return null ;
} else if ( angular . isDate ( viewValue ) && ! isNaN ( viewValue ) ) {
return viewValue ;
} else if ( angular . isString ( viewValue ) ) {
var date = dateParser . parse ( viewValue , dateFormat , scope . date ) ;
if ( isNaN ( date ) ) {
return undefined ;
} else {
return date ;
}
2015-05-05 03:10:25 +02:00
} else {
2016-03-23 18:39:41 +01:00
return undefined ;
2015-05-05 03:10:25 +02:00
}
2016-03-23 18:39:41 +01:00
}
function validator ( modelValue , viewValue ) {
var value = modelValue || viewValue ;
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
if ( ! attrs . ngRequired && ! value ) {
return true ;
2015-05-05 03:10:25 +02:00
}
2016-03-23 18:39:41 +01:00
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 ) ;
return ! isNaN ( date ) ;
} else {
return false ;
}
}
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
function documentClickBind ( event ) {
var popup = $popup [ 0 ] ;
var dpContainsTarget = element [ 0 ] . contains ( event . target ) ;
// The popup node may not be an element node
// In some browsers (IE) only element nodes have the 'contains' function
var popupContainsTarget = popup . contains !== undefined && popup . contains ( event . target ) ;
if ( scope . isOpen && ! ( dpContainsTarget || popupContainsTarget ) ) {
scope . $apply ( function ( ) {
scope . isOpen = false ;
} ) ;
}
}
function inputKeydownBind ( evt ) {
if ( evt . which === 27 && scope . isOpen ) {
evt . preventDefault ( ) ;
evt . stopPropagation ( ) ;
scope . $apply ( function ( ) {
scope . isOpen = false ;
} ) ;
element [ 0 ] . focus ( ) ;
} else if ( evt . which === 40 && ! scope . isOpen ) {
evt . preventDefault ( ) ;
evt . stopPropagation ( ) ;
scope . $apply ( function ( ) {
scope . isOpen = true ;
} ) ;
}
}
2015-05-05 03:10:25 +02:00
} ] )
2016-03-23 18:39:41 +01:00
. directive ( 'uibDatepickerPopup' , function ( ) {
2015-05-05 03:10:25 +02:00
return {
2016-03-23 18:39:41 +01:00
require : [ 'ngModel' , 'uibDatepickerPopup' ] ,
controller : 'UibDatepickerPopupController' ,
scope : {
isOpen : '=?' ,
currentText : '@' ,
clearText : '@' ,
closeText : '@' ,
dateDisabled : '&' ,
customClass : '&'
} ,
link : function ( scope , element , attrs , ctrls ) {
var ngModel = ctrls [ 0 ] ,
ctrl = ctrls [ 1 ] ;
ctrl . init ( ngModel ) ;
2015-05-05 03:10:25 +02:00
}
} ;
} )
2016-03-23 18:39:41 +01:00
. directive ( 'uibDatepickerPopupWrap' , function ( ) {
2015-05-05 03:10:25 +02:00
return {
2016-03-23 18:39:41 +01:00
replace : true ,
transclude : true ,
templateUrl : function ( element , attrs ) {
return attrs . templateUrl || 'template/datepicker/popup.html' ;
}
} ;
} ) ;
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
/* Deprecated datepicker below */
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
angular . module ( 'ui.bootstrap.datepicker' )
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
. value ( '$datepickerSuppressWarning' , false )
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
. controller ( 'DatepickerController' , [ '$scope' , '$attrs' , '$parse' , '$interpolate' , '$log' , 'dateFilter' , 'uibDatepickerConfig' , '$datepickerSuppressError' , '$datepickerSuppressWarning' , function ( $scope , $attrs , $parse , $interpolate , $log , dateFilter , datepickerConfig , $datepickerSuppressError , $datepickerSuppressWarning ) {
if ( ! $datepickerSuppressWarning ) {
$log . warn ( 'DatepickerController is now deprecated. Use UibDatepickerController instead.' ) ;
}
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
var self = this ,
ngModelCtrl = { $setViewValue : angular . noop } ; // nullModelCtrl;
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
this . modes = [ 'day' , 'month' , 'year' ] ;
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
angular . forEach ( [ 'formatDay' , 'formatMonth' , 'formatYear' , 'formatDayHeader' , 'formatDayTitle' , 'formatMonthTitle' ,
'showWeeks' , 'startingDay' , 'yearRange' , 'shortcutPropagation' ] , function ( key , index ) {
self [ key ] = angular . isDefined ( $attrs [ key ] ) ? ( index < 6 ? $interpolate ( $attrs [ key ] ) ( $scope . $parent ) : $scope . $parent . $eval ( $attrs [ key ] ) ) : datepickerConfig [ key ] ;
} ) ;
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
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 ;
}
} ) ;
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
angular . forEach ( [ 'minMode' , 'maxMode' ] , function ( key ) {
if ( $attrs [ key ] ) {
$scope . $parent . $watch ( $parse ( $attrs [ key ] ) , function ( value ) {
self [ key ] = angular . isDefined ( value ) ? value : $attrs [ key ] ;
$scope [ key ] = self [ key ] ;
if ( ( key == 'minMode' && self . modes . indexOf ( $scope . datepickerMode ) < self . modes . indexOf ( self [ key ] ) ) || ( key == 'maxMode' && self . modes . indexOf ( $scope . datepickerMode ) > self . modes . indexOf ( self [ key ] ) ) ) {
$scope . datepickerMode = self [ key ] ;
}
} ) ;
} else {
self [ key ] = datepickerConfig [ key ] || null ;
$scope [ key ] = self [ key ] ;
}
} ) ;
$scope . datepickerMode = $scope . datepickerMode || datepickerConfig . datepickerMode ;
$scope . uniqueId = 'datepicker-' + $scope . $id + '-' + Math . floor ( Math . random ( ) * 10000 ) ;
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 ( ) ;
2015-05-05 03:10:25 +02:00
}
2016-03-23 18:39:41 +01:00
} ) ;
} else {
this . activeDate = new Date ( ) ;
}
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
$scope . isActive = function ( dateObject ) {
if ( self . compare ( dateObject . date , self . activeDate ) === 0 ) {
$scope . activeDateId = dateObject . uid ;
return true ;
}
return false ;
} ;
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
this . init = function ( ngModelCtrl _ ) {
ngModelCtrl = ngModelCtrl _ ;
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
ngModelCtrl . $render = function ( ) {
self . render ( ) ;
2015-05-05 03:10:25 +02:00
} ;
2016-03-23 18:39:41 +01:00
} ;
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
this . render = function ( ) {
if ( ngModelCtrl . $viewValue ) {
var date = new Date ( ngModelCtrl . $viewValue ) ,
isValid = ! isNaN ( date ) ;
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
if ( isValid ) {
this . activeDate = date ;
} else if ( ! $datepickerSuppressError ) {
$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.' ) ;
2015-05-05 03:10:25 +02:00
}
2016-03-23 18:39:41 +01:00
}
this . refreshView ( ) ;
} ;
this . refreshView = function ( ) {
if ( this . element ) {
this . _refreshView ( ) ;
var date = ngModelCtrl . $viewValue ? new Date ( ngModelCtrl . $viewValue ) : null ;
ngModelCtrl . $setValidity ( 'dateDisabled' , ! date || ( this . element && ! this . isDisabled ( date ) ) ) ;
}
} ;
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
this . createDateObject = function ( date , format ) {
var model = ngModelCtrl . $viewValue ? new Date ( ngModelCtrl . $viewValue ) : null ;
2015-05-05 03:10:25 +02:00
return {
2016-03-23 18:39:41 +01:00
date : date ,
label : dateFilter ( date , format ) ,
selected : model && this . compare ( date , model ) === 0 ,
disabled : this . isDisabled ( date ) ,
current : this . compare ( date , new Date ( ) ) === 0 ,
customClass : this . customClass ( date )
2015-05-05 03:10:25 +02:00
} ;
2016-03-23 18:39:41 +01:00
} ;
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +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-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
this . customClass = function ( date ) {
return $scope . customClass ( { date : date , mode : $scope . datepickerMode } ) ;
} ;
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +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 ;
} ;
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
this . fixTimeZone = function ( date ) {
var hours = date . getHours ( ) ;
date . setHours ( hours === 23 ? hours + 2 : 0 ) ;
} ;
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
$scope . select = function ( date ) {
if ( $scope . datepickerMode === self . minMode ) {
var dt = ngModelCtrl . $viewValue ? new Date ( ngModelCtrl . $viewValue ) : new Date ( 0 , 0 , 0 , 0 , 0 , 0 , 0 ) ;
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 ] ;
}
} ;
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
$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 ( ) ;
} ;
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
$scope . toggleMode = function ( direction ) {
direction = direction || 1 ;
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
if ( ( $scope . datepickerMode === self . maxMode && direction === 1 ) || ( $scope . datepickerMode === self . minMode && direction === - 1 ) ) {
return ;
}
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
$scope . datepickerMode = self . modes [ self . modes . indexOf ( $scope . datepickerMode ) + direction ] ;
} ;
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
// 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' } ;
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
var focusElement = function ( ) {
self . element [ 0 ] . focus ( ) ;
} ;
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
$scope . $on ( 'uib:datepicker.focus' , focusElement ) ;
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
$scope . keydown = function ( evt ) {
var key = $scope . keys [ evt . which ] ;
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
if ( ! key || evt . shiftKey || evt . altKey ) {
return ;
}
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
evt . preventDefault ( ) ;
if ( ! self . shortcutPropagation ) {
evt . stopPropagation ( ) ;
}
if ( key === 'enter' || key === 'space' ) {
if ( self . isDisabled ( self . activeDate ) ) {
return ; // do nothing
2015-05-05 03:10:25 +02:00
}
2016-03-23 18:39:41 +01:00
$scope . select ( self . activeDate ) ;
} else if ( evt . ctrlKey && ( key === 'up' || key === 'down' ) ) {
$scope . toggleMode ( key === 'up' ? 1 : - 1 ) ;
} else {
self . handleKeyDown ( key , evt ) ;
self . refreshView ( ) ;
}
} ;
} ] )
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
. directive ( 'datepicker' , [ '$log' , '$datepickerSuppressWarning' , function ( $log , $datepickerSuppressWarning ) {
return {
replace : true ,
templateUrl : function ( element , attrs ) {
return attrs . templateUrl || 'template/datepicker/datepicker.html' ;
} ,
scope : {
datepickerMode : '=?' ,
dateDisabled : '&' ,
customClass : '&' ,
shortcutPropagation : '&?'
} ,
require : [ 'datepicker' , '^ngModel' ] ,
controller : 'DatepickerController' ,
controllerAs : 'datepicker' ,
link : function ( scope , element , attrs , ctrls ) {
if ( ! $datepickerSuppressWarning ) {
$log . warn ( 'datepicker is now deprecated. Use uib-datepicker instead.' ) ;
}
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
var datepickerCtrl = ctrls [ 0 ] , ngModelCtrl = ctrls [ 1 ] ;
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
datepickerCtrl . init ( ngModelCtrl ) ;
}
} ;
} ] )
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
. directive ( 'daypicker' , [ '$log' , '$datepickerSuppressWarning' , function ( $log , $datepickerSuppressWarning ) {
return {
replace : true ,
templateUrl : 'template/datepicker/day.html' ,
require : [ '^datepicker' , 'daypicker' ] ,
controller : 'UibDaypickerController' ,
link : function ( scope , element , attrs , ctrls ) {
if ( ! $datepickerSuppressWarning ) {
$log . warn ( 'daypicker is now deprecated. Use uib-daypicker instead.' ) ;
}
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
var datepickerCtrl = ctrls [ 0 ] ,
daypickerCtrl = ctrls [ 1 ] ;
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
daypickerCtrl . init ( datepickerCtrl ) ;
}
} ;
} ] )
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
. directive ( 'monthpicker' , [ '$log' , '$datepickerSuppressWarning' , function ( $log , $datepickerSuppressWarning ) {
return {
replace : true ,
templateUrl : 'template/datepicker/month.html' ,
require : [ '^datepicker' , 'monthpicker' ] ,
controller : 'UibMonthpickerController' ,
link : function ( scope , element , attrs , ctrls ) {
if ( ! $datepickerSuppressWarning ) {
$log . warn ( 'monthpicker is now deprecated. Use uib-monthpicker instead.' ) ;
}
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
var datepickerCtrl = ctrls [ 0 ] ,
monthpickerCtrl = ctrls [ 1 ] ;
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
monthpickerCtrl . init ( datepickerCtrl ) ;
}
} ;
} ] )
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
. directive ( 'yearpicker' , [ '$log' , '$datepickerSuppressWarning' , function ( $log , $datepickerSuppressWarning ) {
return {
replace : true ,
templateUrl : 'template/datepicker/year.html' ,
require : [ '^datepicker' , 'yearpicker' ] ,
controller : 'UibYearpickerController' ,
link : function ( scope , element , attrs , ctrls ) {
if ( ! $datepickerSuppressWarning ) {
$log . warn ( 'yearpicker is now deprecated. Use uib-yearpicker instead.' ) ;
}
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
var ctrl = ctrls [ 0 ] ;
angular . extend ( ctrl , ctrls [ 1 ] ) ;
ctrl . yearpickerInit ( ) ;
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
ctrl . refreshView ( ) ;
}
} ;
} ] )
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
. directive ( 'datepickerPopup' , [ '$log' , '$datepickerSuppressWarning' , function ( $log , $datepickerSuppressWarning ) {
return {
require : [ 'ngModel' , 'datepickerPopup' ] ,
controller : 'UibDatepickerPopupController' ,
scope : {
isOpen : '=?' ,
currentText : '@' ,
clearText : '@' ,
closeText : '@' ,
dateDisabled : '&' ,
customClass : '&'
} ,
link : function ( scope , element , attrs , ctrls ) {
if ( ! $datepickerSuppressWarning ) {
$log . warn ( 'datepicker-popup is now deprecated. Use uib-datepicker-popup instead.' ) ;
}
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
var ngModel = ctrls [ 0 ] ,
ctrl = ctrls [ 1 ] ;
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
ctrl . init ( ngModel ) ;
}
} ;
} ] )
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
. directive ( 'datepickerPopupWrap' , [ '$log' , '$datepickerSuppressWarning' , function ( $log , $datepickerSuppressWarning ) {
return {
replace : true ,
transclude : true ,
templateUrl : function ( element , attrs ) {
return attrs . templateUrl || 'template/datepicker/popup.html' ;
} ,
link : function ( ) {
if ( ! $datepickerSuppressWarning ) {
$log . warn ( 'datepicker-popup-wrap is now deprecated. Use uib-datepicker-popup-wrap instead.' ) ;
}
}
} ;
} ] ) ;
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
angular . module ( 'ui.bootstrap.dropdown' , [ 'ui.bootstrap.position' ] )
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
. constant ( 'uibDropdownConfig' , {
openClass : 'open'
} )
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
. service ( 'uibDropdownService' , [ '$document' , '$rootScope' , function ( $document , $rootScope ) {
var openScope = null ;
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
this . open = function ( dropdownScope ) {
if ( ! openScope ) {
$document . bind ( 'click' , closeDropdown ) ;
$document . bind ( 'keydown' , keybindFilter ) ;
}
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
if ( openScope && openScope !== dropdownScope ) {
openScope . isOpen = false ;
}
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
openScope = dropdownScope ;
} ;
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
this . close = function ( dropdownScope ) {
if ( openScope === dropdownScope ) {
openScope = null ;
$document . unbind ( 'click' , closeDropdown ) ;
$document . unbind ( 'keydown' , keybindFilter ) ;
}
} ;
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
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-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
if ( evt && openScope . getAutoClose ( ) === 'disabled' ) { return ; }
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
var toggleElement = openScope . getToggleElement ( ) ;
if ( evt && toggleElement && toggleElement [ 0 ] . contains ( evt . target ) ) {
return ;
}
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
var dropdownElement = openScope . getDropdownElement ( ) ;
if ( evt && openScope . getAutoClose ( ) === 'outsideClick' &&
dropdownElement && dropdownElement [ 0 ] . contains ( evt . target ) ) {
return ;
}
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
openScope . isOpen = false ;
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
if ( ! $rootScope . $$phase ) {
openScope . $apply ( ) ;
}
} ;
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
var keybindFilter = function ( evt ) {
if ( evt . which === 27 ) {
openScope . focusToggleElement ( ) ;
closeDropdown ( ) ;
} else if ( openScope . isKeynavEnabled ( ) && /(38|40)/ . test ( evt . which ) && openScope . isOpen ) {
evt . preventDefault ( ) ;
evt . stopPropagation ( ) ;
openScope . focusDropdownEntry ( evt . which ) ;
}
} ;
} ] )
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
. controller ( 'UibDropdownController' , [ '$scope' , '$element' , '$attrs' , '$parse' , 'uibDropdownConfig' , 'uibDropdownService' , '$animate' , '$uibPosition' , '$document' , '$compile' , '$templateRequest' , function ( $scope , $element , $attrs , $parse , dropdownConfig , uibDropdownService , $animate , $position , $document , $compile , $templateRequest ) {
var self = this ,
scope = $scope . $new ( ) , // create a child scope so we are not polluting original one
templateScope ,
openClass = dropdownConfig . openClass ,
getIsOpen ,
setIsOpen = angular . noop ,
toggleInvoker = $attrs . onToggle ? $parse ( $attrs . onToggle ) : angular . noop ,
appendToBody = false ,
keynavEnabled = false ,
selectedOption = null ;
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
$element . addClass ( 'dropdown' ) ;
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
this . init = function ( ) {
if ( $attrs . isOpen ) {
getIsOpen = $parse ( $attrs . isOpen ) ;
setIsOpen = getIsOpen . assign ;
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
$scope . $watch ( getIsOpen , function ( value ) {
scope . isOpen = ! ! value ;
} ) ;
}
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
appendToBody = angular . isDefined ( $attrs . dropdownAppendToBody ) ;
keynavEnabled = angular . isDefined ( $attrs . uibKeyboardNav ) ;
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
if ( appendToBody && self . dropdownMenu ) {
$document . find ( 'body' ) . append ( self . dropdownMenu ) ;
$element . on ( '$destroy' , function handleDestroyEvent ( ) {
self . dropdownMenu . remove ( ) ;
2015-05-05 03:10:25 +02:00
} ) ;
}
} ;
2016-03-23 18:39:41 +01:00
this . toggle = function ( open ) {
return scope . isOpen = arguments . length ? ! ! open : ! scope . isOpen ;
2015-05-05 03:10:25 +02:00
} ;
2016-03-23 18:39:41 +01:00
// Allow other directives to watch status
this . isOpen = function ( ) {
return scope . isOpen ;
2015-05-05 03:10:25 +02:00
} ;
2016-03-23 18:39:41 +01:00
scope . getToggleElement = function ( ) {
return self . toggleElement ;
2015-05-05 03:10:25 +02:00
} ;
2016-03-23 18:39:41 +01:00
scope . getAutoClose = function ( ) {
return $attrs . autoClose || 'always' ; //or 'outsideClick' or 'disabled'
2015-05-05 03:10:25 +02:00
} ;
2016-03-23 18:39:41 +01:00
scope . getElement = function ( ) {
return $element ;
2015-05-05 03:10:25 +02:00
} ;
2016-03-23 18:39:41 +01:00
scope . isKeynavEnabled = function ( ) {
return keynavEnabled ;
} ;
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
scope . focusDropdownEntry = function ( keyCode ) {
var elems = self . dropdownMenu ? //If append to body is used.
( angular . element ( self . dropdownMenu ) . find ( 'a' ) ) :
( angular . element ( $element ) . find ( 'ul' ) . eq ( 0 ) . find ( 'a' ) ) ;
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
switch ( keyCode ) {
case ( 40 ) : {
if ( ! angular . isNumber ( self . selectedOption ) ) {
self . selectedOption = 0 ;
} else {
self . selectedOption = ( self . selectedOption === elems . length - 1 ?
self . selectedOption :
self . selectedOption + 1 ) ;
}
break ;
}
case ( 38 ) : {
if ( ! angular . isNumber ( self . selectedOption ) ) {
self . selectedOption = elems . length - 1 ;
} else {
self . selectedOption = self . selectedOption === 0 ?
0 : self . selectedOption - 1 ;
}
break ;
}
2015-05-05 03:10:25 +02:00
}
2016-03-23 18:39:41 +01:00
elems [ self . selectedOption ] . focus ( ) ;
} ;
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
scope . getDropdownElement = function ( ) {
return self . dropdownMenu ;
} ;
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
scope . focusToggleElement = function ( ) {
if ( self . toggleElement ) {
self . toggleElement [ 0 ] . focus ( ) ;
}
} ;
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
scope . $watch ( 'isOpen' , function ( isOpen , wasOpen ) {
if ( appendToBody && self . dropdownMenu ) {
var pos = $position . positionElements ( $element , self . dropdownMenu , 'bottom-left' , true ) ;
var css = {
top : pos . top + 'px' ,
display : isOpen ? 'block' : 'none'
} ;
var rightalign = self . dropdownMenu . hasClass ( 'dropdown-menu-right' ) ;
if ( ! rightalign ) {
css . left = pos . left + 'px' ;
css . right = 'auto' ;
} else {
css . left = 'auto' ;
css . right = ( window . innerWidth - ( pos . left + $element . prop ( 'offsetWidth' ) ) ) + 'px' ;
2015-05-05 03:10:25 +02:00
}
2016-03-23 18:39:41 +01:00
self . dropdownMenu . css ( css ) ;
}
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
$animate [ isOpen ? 'addClass' : 'removeClass' ] ( $element , openClass ) . then ( function ( ) {
if ( angular . isDefined ( isOpen ) && isOpen !== wasOpen ) {
toggleInvoker ( $scope , { open : ! ! isOpen } ) ;
}
} ) ;
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
if ( isOpen ) {
if ( self . dropdownMenuTemplateUrl ) {
$templateRequest ( self . dropdownMenuTemplateUrl ) . then ( function ( tplContent ) {
templateScope = scope . $new ( ) ;
$compile ( tplContent . trim ( ) ) ( templateScope , function ( dropdownElement ) {
var newEl = dropdownElement ;
self . dropdownMenu . replaceWith ( newEl ) ;
self . dropdownMenu = newEl ;
} ) ;
2015-05-05 03:10:25 +02:00
} ) ;
}
2016-03-23 18:39:41 +01:00
scope . focusToggleElement ( ) ;
uibDropdownService . open ( scope ) ;
} else {
if ( self . dropdownMenuTemplateUrl ) {
if ( templateScope ) {
templateScope . $destroy ( ) ;
}
var newEl = angular . element ( '<ul class="dropdown-menu"></ul>' ) ;
self . dropdownMenu . replaceWith ( newEl ) ;
self . dropdownMenu = newEl ;
2015-05-05 03:10:25 +02:00
}
2016-03-23 18:39:41 +01:00
uibDropdownService . close ( scope ) ;
self . selectedOption = null ;
}
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
if ( angular . isFunction ( setIsOpen ) ) {
setIsOpen ( $scope , isOpen ) ;
}
} ) ;
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
$scope . $on ( '$locationChangeSuccess' , function ( ) {
if ( scope . getAutoClose ( ) !== 'disabled' ) {
scope . isOpen = false ;
}
} ) ;
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
var offDestroy = $scope . $on ( '$destroy' , function ( ) {
scope . $destroy ( ) ;
} ) ;
scope . $on ( '$destroy' , offDestroy ) ;
} ] )
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
. directive ( 'uibDropdown' , function ( ) {
return {
controller : 'UibDropdownController' ,
link : function ( scope , element , attrs , dropdownCtrl ) {
dropdownCtrl . init ( ) ;
}
} ;
} )
. directive ( 'uibDropdownMenu' , function ( ) {
return {
restrict : 'AC' ,
require : '?^uibDropdown' ,
link : function ( scope , element , attrs , dropdownCtrl ) {
if ( ! dropdownCtrl || angular . isDefined ( attrs . dropdownNested ) ) {
return ;
}
element . addClass ( 'dropdown-menu' ) ;
var tplUrl = attrs . templateUrl ;
if ( tplUrl ) {
dropdownCtrl . dropdownMenuTemplateUrl = tplUrl ;
}
if ( ! dropdownCtrl . dropdownMenu ) {
dropdownCtrl . dropdownMenu = element ;
}
}
} ;
} )
. directive ( 'uibKeyboardNav' , function ( ) {
return {
restrict : 'A' ,
require : '?^uibDropdown' ,
link : function ( scope , element , attrs , dropdownCtrl ) {
element . bind ( 'keydown' , function ( e ) {
if ( [ 38 , 40 ] . indexOf ( e . which ) !== - 1 ) {
e . preventDefault ( ) ;
e . stopPropagation ( ) ;
var elems = dropdownCtrl . dropdownMenu . find ( 'a' ) ;
switch ( e . which ) {
case ( 40 ) : { // Down
if ( ! angular . isNumber ( dropdownCtrl . selectedOption ) ) {
dropdownCtrl . selectedOption = 0 ;
} else {
dropdownCtrl . selectedOption = dropdownCtrl . selectedOption === elems . length - 1 ?
dropdownCtrl . selectedOption : dropdownCtrl . selectedOption + 1 ;
}
break ;
}
case ( 38 ) : { // Up
if ( ! angular . isNumber ( dropdownCtrl . selectedOption ) ) {
dropdownCtrl . selectedOption = elems . length - 1 ;
} else {
dropdownCtrl . selectedOption = dropdownCtrl . selectedOption === 0 ?
0 : dropdownCtrl . selectedOption - 1 ;
}
break ;
}
2015-05-05 03:10:25 +02:00
}
2016-03-23 18:39:41 +01:00
elems [ dropdownCtrl . selectedOption ] . focus ( ) ;
2015-05-05 03:10:25 +02:00
}
2016-03-23 18:39:41 +01:00
} ) ;
}
} ;
} )
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
. directive ( 'uibDropdownToggle' , function ( ) {
return {
require : '?^uibDropdown' ,
link : function ( scope , element , attrs , dropdownCtrl ) {
if ( ! dropdownCtrl ) {
return ;
}
element . addClass ( 'dropdown-toggle' ) ;
dropdownCtrl . toggleElement = element ;
var toggleDropdown = function ( event ) {
event . preventDefault ( ) ;
if ( ! element . hasClass ( 'disabled' ) && ! attrs . disabled ) {
scope . $apply ( function ( ) {
dropdownCtrl . toggle ( ) ;
} ) ;
2015-05-05 03:10:25 +02:00
}
2016-03-23 18:39:41 +01:00
} ;
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
element . bind ( 'click' , toggleDropdown ) ;
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
// 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 ) ;
} ) ;
}
} ;
} ) ;
/* Deprecated dropdown below */
angular . module ( 'ui.bootstrap.dropdown' )
. value ( '$dropdownSuppressWarning' , false )
. service ( 'dropdownService' , [ '$log' , '$dropdownSuppressWarning' , 'uibDropdownService' , function ( $log , $dropdownSuppressWarning , uibDropdownService ) {
if ( ! $dropdownSuppressWarning ) {
$log . warn ( 'dropdownService is now deprecated. Use uibDropdownService instead.' ) ;
}
angular . extend ( this , uibDropdownService ) ;
} ] )
. controller ( 'DropdownController' , [ '$scope' , '$element' , '$attrs' , '$parse' , 'uibDropdownConfig' , 'uibDropdownService' , '$animate' , '$uibPosition' , '$document' , '$compile' , '$templateRequest' , '$log' , '$dropdownSuppressWarning' , function ( $scope , $element , $attrs , $parse , dropdownConfig , uibDropdownService , $animate , $position , $document , $compile , $templateRequest , $log , $dropdownSuppressWarning ) {
if ( ! $dropdownSuppressWarning ) {
$log . warn ( 'DropdownController is now deprecated. Use UibDropdownController instead.' ) ;
}
var self = this ,
scope = $scope . $new ( ) , // create a child scope so we are not polluting original one
templateScope ,
openClass = dropdownConfig . openClass ,
getIsOpen ,
setIsOpen = angular . noop ,
toggleInvoker = $attrs . onToggle ? $parse ( $attrs . onToggle ) : angular . noop ,
appendToBody = false ,
keynavEnabled = false ,
selectedOption = null ;
$element . addClass ( 'dropdown' ) ;
this . init = function ( ) {
if ( $attrs . isOpen ) {
getIsOpen = $parse ( $attrs . isOpen ) ;
setIsOpen = getIsOpen . assign ;
$scope . $watch ( getIsOpen , function ( value ) {
scope . isOpen = ! ! value ;
} ) ;
}
appendToBody = angular . isDefined ( $attrs . dropdownAppendToBody ) ;
keynavEnabled = angular . isDefined ( $attrs . uibKeyboardNav ) ;
if ( appendToBody && self . dropdownMenu ) {
$document . find ( 'body' ) . append ( self . dropdownMenu ) ;
$element . on ( '$destroy' , function handleDestroyEvent ( ) {
self . dropdownMenu . remove ( ) ;
} ) ;
}
} ;
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 ;
} ;
scope . getAutoClose = function ( ) {
return $attrs . autoClose || 'always' ; //or 'outsideClick' or 'disabled'
} ;
scope . getElement = function ( ) {
return $element ;
} ;
scope . isKeynavEnabled = function ( ) {
return keynavEnabled ;
} ;
scope . focusDropdownEntry = function ( keyCode ) {
var elems = self . dropdownMenu ? //If append to body is used.
( angular . element ( self . dropdownMenu ) . find ( 'a' ) ) :
( angular . element ( $element ) . find ( 'ul' ) . eq ( 0 ) . find ( 'a' ) ) ;
switch ( keyCode ) {
case ( 40 ) : {
if ( ! angular . isNumber ( self . selectedOption ) ) {
self . selectedOption = 0 ;
} else {
self . selectedOption = ( self . selectedOption === elems . length - 1 ?
self . selectedOption :
self . selectedOption + 1 ) ;
}
break ;
}
case ( 38 ) : {
if ( ! angular . isNumber ( self . selectedOption ) ) {
self . selectedOption = elems . length - 1 ;
} else {
self . selectedOption = self . selectedOption === 0 ?
0 : self . selectedOption - 1 ;
}
break ;
}
}
elems [ self . selectedOption ] . focus ( ) ;
} ;
scope . getDropdownElement = function ( ) {
return self . dropdownMenu ;
} ;
scope . focusToggleElement = function ( ) {
if ( self . toggleElement ) {
self . toggleElement [ 0 ] . focus ( ) ;
}
} ;
scope . $watch ( 'isOpen' , function ( isOpen , wasOpen ) {
if ( appendToBody && self . dropdownMenu ) {
var pos = $position . positionElements ( $element , self . dropdownMenu , 'bottom-left' , true ) ;
var css = {
top : pos . top + 'px' ,
display : isOpen ? 'block' : 'none'
} ;
var rightalign = self . dropdownMenu . hasClass ( 'dropdown-menu-right' ) ;
if ( ! rightalign ) {
css . left = pos . left + 'px' ;
css . right = 'auto' ;
} else {
css . left = 'auto' ;
css . right = ( window . innerWidth - ( pos . left + $element . prop ( 'offsetWidth' ) ) ) + 'px' ;
}
self . dropdownMenu . css ( css ) ;
}
$animate [ isOpen ? 'addClass' : 'removeClass' ] ( $element , openClass ) . then ( function ( ) {
if ( angular . isDefined ( isOpen ) && isOpen !== wasOpen ) {
toggleInvoker ( $scope , { open : ! ! isOpen } ) ;
}
} ) ;
if ( isOpen ) {
if ( self . dropdownMenuTemplateUrl ) {
$templateRequest ( self . dropdownMenuTemplateUrl ) . then ( function ( tplContent ) {
templateScope = scope . $new ( ) ;
$compile ( tplContent . trim ( ) ) ( templateScope , function ( dropdownElement ) {
var newEl = dropdownElement ;
self . dropdownMenu . replaceWith ( newEl ) ;
self . dropdownMenu = newEl ;
} ) ;
} ) ;
}
scope . focusToggleElement ( ) ;
uibDropdownService . open ( scope ) ;
} else {
if ( self . dropdownMenuTemplateUrl ) {
if ( templateScope ) {
templateScope . $destroy ( ) ;
}
var newEl = angular . element ( '<ul class="dropdown-menu"></ul>' ) ;
self . dropdownMenu . replaceWith ( newEl ) ;
self . dropdownMenu = newEl ;
}
uibDropdownService . close ( scope ) ;
self . selectedOption = null ;
}
if ( angular . isFunction ( setIsOpen ) ) {
setIsOpen ( $scope , isOpen ) ;
}
} ) ;
$scope . $on ( '$locationChangeSuccess' , function ( ) {
if ( scope . getAutoClose ( ) !== 'disabled' ) {
scope . isOpen = false ;
}
} ) ;
var offDestroy = $scope . $on ( '$destroy' , function ( ) {
scope . $destroy ( ) ;
} ) ;
scope . $on ( '$destroy' , offDestroy ) ;
} ] )
. directive ( 'dropdown' , [ '$log' , '$dropdownSuppressWarning' , function ( $log , $dropdownSuppressWarning ) {
return {
controller : 'DropdownController' ,
link : function ( scope , element , attrs , dropdownCtrl ) {
if ( ! $dropdownSuppressWarning ) {
$log . warn ( 'dropdown is now deprecated. Use uib-dropdown instead.' ) ;
}
dropdownCtrl . init ( ) ;
}
} ;
} ] )
. directive ( 'dropdownMenu' , [ '$log' , '$dropdownSuppressWarning' , function ( $log , $dropdownSuppressWarning ) {
return {
restrict : 'AC' ,
require : '?^dropdown' ,
link : function ( scope , element , attrs , dropdownCtrl ) {
if ( ! dropdownCtrl || angular . isDefined ( attrs . dropdownNested ) ) {
return ;
}
if ( ! $dropdownSuppressWarning ) {
$log . warn ( 'dropdown-menu is now deprecated. Use uib-dropdown-menu instead.' ) ;
}
element . addClass ( 'dropdown-menu' ) ;
var tplUrl = attrs . templateUrl ;
if ( tplUrl ) {
dropdownCtrl . dropdownMenuTemplateUrl = tplUrl ;
}
if ( ! dropdownCtrl . dropdownMenu ) {
dropdownCtrl . dropdownMenu = element ;
}
}
} ;
} ] )
. directive ( 'keyboardNav' , [ '$log' , '$dropdownSuppressWarning' , function ( $log , $dropdownSuppressWarning ) {
return {
restrict : 'A' ,
require : '?^dropdown' ,
link : function ( scope , element , attrs , dropdownCtrl ) {
if ( ! $dropdownSuppressWarning ) {
$log . warn ( 'keyboard-nav is now deprecated. Use uib-keyboard-nav instead.' ) ;
}
element . bind ( 'keydown' , function ( e ) {
if ( [ 38 , 40 ] . indexOf ( e . which ) !== - 1 ) {
e . preventDefault ( ) ;
e . stopPropagation ( ) ;
var elems = dropdownCtrl . dropdownMenu . find ( 'a' ) ;
switch ( e . which ) {
case ( 40 ) : { // Down
if ( ! angular . isNumber ( dropdownCtrl . selectedOption ) ) {
dropdownCtrl . selectedOption = 0 ;
} else {
dropdownCtrl . selectedOption = dropdownCtrl . selectedOption === elems . length - 1 ?
dropdownCtrl . selectedOption : dropdownCtrl . selectedOption + 1 ;
}
break ;
}
case ( 38 ) : { // Up
if ( ! angular . isNumber ( dropdownCtrl . selectedOption ) ) {
dropdownCtrl . selectedOption = elems . length - 1 ;
} else {
dropdownCtrl . selectedOption = dropdownCtrl . selectedOption === 0 ?
0 : dropdownCtrl . selectedOption - 1 ;
}
break ;
}
2015-05-05 03:10:25 +02:00
}
2016-03-23 18:39:41 +01:00
elems [ dropdownCtrl . selectedOption ] . focus ( ) ;
}
} ) ;
}
} ;
} ] )
. directive ( 'dropdownToggle' , [ '$log' , '$dropdownSuppressWarning' , function ( $log , $dropdownSuppressWarning ) {
return {
require : '?^dropdown' ,
link : function ( scope , element , attrs , dropdownCtrl ) {
if ( ! $dropdownSuppressWarning ) {
$log . warn ( 'dropdown-toggle is now deprecated. Use uib-dropdown-toggle instead.' ) ;
}
if ( ! dropdownCtrl ) {
return ;
}
element . addClass ( 'dropdown-toggle' ) ;
dropdownCtrl . toggleElement = element ;
var toggleDropdown = function ( event ) {
event . preventDefault ( ) ;
if ( ! element . hasClass ( 'disabled' ) && ! attrs . disabled ) {
scope . $apply ( function ( ) {
dropdownCtrl . toggle ( ) ;
} ) ;
}
} ;
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 ) ;
} ) ;
}
} ;
} ] ) ;
angular . module ( 'ui.bootstrap.stackedMap' , [ ] )
/ * *
* 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 = [ ] ;
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 ;
}
} ;
}
} ;
} ) ;
angular . module ( 'ui.bootstrap.modal' , [ 'ui.bootstrap.stackedMap' ] )
/ * *
* A helper , internal data structure that stores all references attached to key
* /
. factory ( '$$multiMap' , function ( ) {
return {
createNew : function ( ) {
var map = { } ;
return {
entries : function ( ) {
return Object . keys ( map ) . map ( function ( key ) {
return {
key : key ,
value : map [ key ]
} ;
} ) ;
} ,
get : function ( key ) {
return map [ key ] ;
} ,
hasKey : function ( key ) {
return ! ! map [ key ] ;
} ,
keys : function ( ) {
return Object . keys ( map ) ;
} ,
put : function ( key , value ) {
if ( ! map [ key ] ) {
map [ key ] = [ ] ;
}
map [ key ] . push ( value ) ;
} ,
remove : function ( key , value ) {
var values = map [ key ] ;
if ( ! values ) {
return ;
}
var idx = values . indexOf ( value ) ;
if ( idx !== - 1 ) {
values . splice ( idx , 1 ) ;
}
if ( ! values . length ) {
delete map [ key ] ;
}
}
} ;
}
} ;
} )
/ * *
* A helper directive for the $modal service . It creates a backdrop element .
* /
. directive ( 'uibModalBackdrop' , [
'$animate' , '$injector' , '$uibModalStack' ,
function ( $animate , $injector , $modalStack ) {
var $animateCss = null ;
if ( $injector . has ( '$animateCss' ) ) {
$animateCss = $injector . get ( '$animateCss' ) ;
}
return {
replace : true ,
templateUrl : 'template/modal/backdrop.html' ,
compile : function ( tElement , tAttrs ) {
tElement . addClass ( tAttrs . backdropClass ) ;
return linkFn ;
}
} ;
function linkFn ( scope , element , attrs ) {
// Temporary fix for prefixing
element . addClass ( 'modal-backdrop' ) ;
if ( attrs . modalInClass ) {
if ( $animateCss ) {
$animateCss ( element , {
addClass : attrs . modalInClass
} ) . start ( ) ;
} else {
$animate . addClass ( element , attrs . modalInClass ) ;
}
scope . $on ( $modalStack . NOW _CLOSING _EVENT , function ( e , setIsAsync ) {
var done = setIsAsync ( ) ;
if ( $animateCss ) {
$animateCss ( element , {
removeClass : attrs . modalInClass
} ) . start ( ) . then ( done ) ;
} else {
$animate . removeClass ( element , attrs . modalInClass ) . then ( done ) ;
}
} ) ;
}
}
} ] )
. directive ( 'uibModalWindow' , [
'$uibModalStack' , '$q' , '$animate' , '$injector' ,
function ( $modalStack , $q , $animate , $injector ) {
var $animateCss = null ;
if ( $injector . has ( '$animateCss' ) ) {
$animateCss = $injector . get ( '$animateCss' ) ;
}
return {
scope : {
index : '@'
} ,
replace : true ,
transclude : true ,
templateUrl : function ( tElement , tAttrs ) {
return tAttrs . templateUrl || 'template/modal/window.html' ;
} ,
link : function ( scope , element , attrs ) {
element . addClass ( attrs . windowClass || '' ) ;
element . addClass ( attrs . windowTopClass || '' ) ;
scope . size = attrs . size ;
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' ) ;
}
} ;
// moved from template to fix issue #2280
element . on ( 'click' , scope . close ) ;
// 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 ( ) {
var animationPromise = null ;
if ( attrs . modalInClass ) {
if ( $animateCss ) {
animationPromise = $animateCss ( element , {
addClass : attrs . modalInClass
} ) . start ( ) ;
} else {
animationPromise = $animate . addClass ( element , attrs . modalInClass ) ;
}
scope . $on ( $modalStack . NOW _CLOSING _EVENT , function ( e , setIsAsync ) {
var done = setIsAsync ( ) ;
if ( $animateCss ) {
$animateCss ( element , {
removeClass : attrs . modalInClass
} ) . start ( ) . then ( done ) ;
} else {
$animate . removeClass ( element , attrs . modalInClass ) . then ( done ) ;
}
} ) ;
}
$q . when ( animationPromise ) . then ( function ( ) {
var inputWithAutofocus = element [ 0 ] . querySelector ( '[autofocus]' ) ;
/ * *
* 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 .
* /
if ( inputWithAutofocus ) {
inputWithAutofocus . focus ( ) ;
} else {
element [ 0 ] . focus ( ) ;
}
} ) ;
// Notify {@link $modalStack} that modal is rendered.
var modal = $modalStack . getTop ( ) ;
if ( modal ) {
$modalStack . modalRendered ( modal . key ) ;
}
} ) ;
}
} ;
} ] )
. directive ( 'uibModalAnimationClass' , function ( ) {
return {
compile : function ( tElement , tAttrs ) {
if ( tAttrs . modalAnimation ) {
tElement . addClass ( tAttrs . uibModalAnimationClass ) ;
}
}
} ;
} )
. directive ( 'uibModalTransclude' , function ( ) {
return {
link : function ( $scope , $element , $attrs , controller , $transclude ) {
$transclude ( $scope . $parent , function ( clone ) {
$element . empty ( ) ;
$element . append ( clone ) ;
} ) ;
}
} ;
} )
. factory ( '$uibModalStack' , [
'$animate' , '$timeout' , '$document' , '$compile' , '$rootScope' ,
'$q' ,
'$injector' ,
'$$multiMap' ,
'$$stackedMap' ,
function ( $animate , $timeout , $document , $compile , $rootScope ,
$q ,
$injector ,
$$multiMap ,
$$stackedMap ) {
var $animateCss = null ;
if ( $injector . has ( '$animateCss' ) ) {
$animateCss = $injector . get ( '$animateCss' ) ;
}
var OPENED _MODAL _CLASS = 'modal-open' ;
var backdropDomEl , backdropScope ;
var openedWindows = $$stackedMap . createNew ( ) ;
var openedClasses = $$multiMap . createNew ( ) ;
var $modalStack = {
NOW _CLOSING _EVENT : 'modal.stack.now-closing'
} ;
//Modal focus behavior
var focusableElementList ;
var focusIndex = 0 ;
var tababbleSelector = 'a[href], area[href], input:not([disabled]), ' +
'button:not([disabled]),select:not([disabled]), textarea:not([disabled]), ' +
'iframe, object, embed, *[tabindex], *[contenteditable=true]' ;
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 ;
}
}
return topBackdropIndex ;
}
$rootScope . $watch ( backdropIndex , function ( newBackdropIndex ) {
if ( backdropScope ) {
backdropScope . index = newBackdropIndex ;
}
} ) ;
function removeModalWindow ( modalInstance , elementToReceiveFocus ) {
var body = $document . find ( 'body' ) . eq ( 0 ) ;
var modalWindow = openedWindows . get ( modalInstance ) . value ;
//clean up the stack
openedWindows . remove ( modalInstance ) ;
removeAfterAnimate ( modalWindow . modalDomEl , modalWindow . modalScope , function ( ) {
var modalBodyClass = modalWindow . openedClass || OPENED _MODAL _CLASS ;
openedClasses . remove ( modalBodyClass , modalInstance ) ;
body . toggleClass ( modalBodyClass , openedClasses . hasKey ( modalBodyClass ) ) ;
toggleTopWindowClass ( true ) ;
} ) ;
checkRemoveBackdrop ( ) ;
//move focus to specified element if available, or else to body
if ( elementToReceiveFocus && elementToReceiveFocus . focus ) {
elementToReceiveFocus . focus ( ) ;
} else {
body . focus ( ) ;
}
}
// Add or remove "windowTopClass" from the top window in the stack
function toggleTopWindowClass ( toggleSwitch ) {
var modalWindow ;
if ( openedWindows . length ( ) > 0 ) {
modalWindow = openedWindows . top ( ) . value ;
modalWindow . modalDomEl . toggleClass ( modalWindow . windowTopClass || '' , toggleSwitch ) ;
}
}
function checkRemoveBackdrop ( ) {
//remove backdrop if no longer needed
if ( backdropDomEl && backdropIndex ( ) == - 1 ) {
var backdropScopeRef = backdropScope ;
removeAfterAnimate ( backdropDomEl , backdropScope , function ( ) {
backdropScopeRef = null ;
} ) ;
backdropDomEl = undefined ;
backdropScope = undefined ;
}
}
function removeAfterAnimate ( domEl , scope , done ) {
var asyncDeferred ;
var asyncPromise = null ;
var setIsAsync = function ( ) {
if ( ! asyncDeferred ) {
asyncDeferred = $q . defer ( ) ;
asyncPromise = asyncDeferred . promise ;
}
return function asyncDone ( ) {
asyncDeferred . resolve ( ) ;
} ;
} ;
scope . $broadcast ( $modalStack . NOW _CLOSING _EVENT , setIsAsync ) ;
// Note that it's intentional that asyncPromise might be null.
// That's when setIsAsync has not been called during the
// NOW_CLOSING_EVENT broadcast.
return $q . when ( asyncPromise ) . then ( afterAnimating ) ;
function afterAnimating ( ) {
if ( afterAnimating . done ) {
return ;
}
afterAnimating . done = true ;
if ( $animateCss ) {
$animateCss ( domEl , {
event : 'leave'
} ) . start ( ) . then ( function ( ) {
domEl . remove ( ) ;
} ) ;
} else {
$animate . leave ( domEl ) ;
}
scope . $destroy ( ) ;
if ( done ) {
done ( ) ;
}
}
}
$document . bind ( 'keydown' , function ( evt ) {
if ( evt . isDefaultPrevented ( ) ) {
return evt ;
}
var modal = openedWindows . top ( ) ;
if ( modal && modal . value . keyboard ) {
switch ( evt . which ) {
case 27 : {
evt . preventDefault ( ) ;
$rootScope . $apply ( function ( ) {
$modalStack . dismiss ( modal . key , 'escape key press' ) ;
} ) ;
break ;
}
case 9 : {
$modalStack . loadFocusElementList ( modal ) ;
var focusChanged = false ;
if ( evt . shiftKey ) {
if ( $modalStack . isFocusInFirstItem ( evt ) ) {
focusChanged = $modalStack . focusLastFocusableElement ( ) ;
}
} else {
if ( $modalStack . isFocusInLastItem ( evt ) ) {
focusChanged = $modalStack . focusFirstFocusableElement ( ) ;
}
}
if ( focusChanged ) {
evt . preventDefault ( ) ;
evt . stopPropagation ( ) ;
}
break ;
}
}
}
} ) ;
$modalStack . open = function ( modalInstance , modal ) {
var modalOpener = $document [ 0 ] . activeElement ,
modalBodyClass = modal . openedClass || OPENED _MODAL _CLASS ;
toggleTopWindowClass ( false ) ;
openedWindows . add ( modalInstance , {
deferred : modal . deferred ,
renderDeferred : modal . renderDeferred ,
modalScope : modal . scope ,
backdrop : modal . backdrop ,
keyboard : modal . keyboard ,
openedClass : modal . openedClass ,
windowTopClass : modal . windowTopClass
} ) ;
openedClasses . put ( modalBodyClass , modalInstance ) ;
var body = $document . find ( 'body' ) . eq ( 0 ) ,
currBackdropIndex = backdropIndex ( ) ;
if ( currBackdropIndex >= 0 && ! backdropDomEl ) {
backdropScope = $rootScope . $new ( true ) ;
backdropScope . index = currBackdropIndex ;
var angularBackgroundDomEl = angular . element ( '<div uib-modal-backdrop="modal-backdrop"></div>' ) ;
angularBackgroundDomEl . attr ( 'backdrop-class' , modal . backdropClass ) ;
if ( modal . animation ) {
angularBackgroundDomEl . attr ( 'modal-animation' , 'true' ) ;
}
backdropDomEl = $compile ( angularBackgroundDomEl ) ( backdropScope ) ;
body . append ( backdropDomEl ) ;
}
var angularDomEl = angular . element ( '<div uib-modal-window="modal-window"></div>' ) ;
angularDomEl . attr ( {
'template-url' : modal . windowTemplateUrl ,
'window-class' : modal . windowClass ,
'window-top-class' : modal . windowTopClass ,
'size' : modal . size ,
'index' : openedWindows . length ( ) - 1 ,
'animate' : 'animate'
} ) . html ( modal . content ) ;
if ( modal . animation ) {
angularDomEl . attr ( 'modal-animation' , 'true' ) ;
}
var modalDomEl = $compile ( angularDomEl ) ( modal . scope ) ;
openedWindows . top ( ) . value . modalDomEl = modalDomEl ;
openedWindows . top ( ) . value . modalOpener = modalOpener ;
body . append ( modalDomEl ) ;
body . addClass ( modalBodyClass ) ;
$modalStack . clearFocusListCache ( ) ;
} ;
function broadcastClosing ( modalWindow , resultOrReason , closing ) {
return ! modalWindow . value . modalScope . $broadcast ( 'modal.closing' , resultOrReason , closing ) . defaultPrevented ;
}
$modalStack . close = function ( modalInstance , result ) {
var modalWindow = openedWindows . get ( modalInstance ) ;
if ( modalWindow && broadcastClosing ( modalWindow , result , true ) ) {
modalWindow . value . modalScope . $$uibDestructionScheduled = true ;
modalWindow . value . deferred . resolve ( result ) ;
removeModalWindow ( modalInstance , modalWindow . value . modalOpener ) ;
return true ;
}
return ! modalWindow ;
} ;
$modalStack . dismiss = function ( modalInstance , reason ) {
var modalWindow = openedWindows . get ( modalInstance ) ;
if ( modalWindow && broadcastClosing ( modalWindow , reason , false ) ) {
modalWindow . value . modalScope . $$uibDestructionScheduled = true ;
modalWindow . value . deferred . reject ( reason ) ;
removeModalWindow ( modalInstance , modalWindow . value . modalOpener ) ;
return true ;
}
return ! modalWindow ;
} ;
$modalStack . dismissAll = function ( reason ) {
var topModal = this . getTop ( ) ;
while ( topModal && this . dismiss ( topModal . key , reason ) ) {
topModal = this . getTop ( ) ;
}
} ;
$modalStack . getTop = function ( ) {
return openedWindows . top ( ) ;
} ;
$modalStack . modalRendered = function ( modalInstance ) {
var modalWindow = openedWindows . get ( modalInstance ) ;
if ( modalWindow ) {
modalWindow . value . renderDeferred . resolve ( ) ;
}
} ;
$modalStack . focusFirstFocusableElement = function ( ) {
if ( focusableElementList . length > 0 ) {
focusableElementList [ 0 ] . focus ( ) ;
return true ;
}
return false ;
} ;
$modalStack . focusLastFocusableElement = function ( ) {
if ( focusableElementList . length > 0 ) {
focusableElementList [ focusableElementList . length - 1 ] . focus ( ) ;
return true ;
}
return false ;
} ;
$modalStack . isFocusInFirstItem = function ( evt ) {
if ( focusableElementList . length > 0 ) {
return ( evt . target || evt . srcElement ) == focusableElementList [ 0 ] ;
}
return false ;
} ;
$modalStack . isFocusInLastItem = function ( evt ) {
if ( focusableElementList . length > 0 ) {
return ( evt . target || evt . srcElement ) == focusableElementList [ focusableElementList . length - 1 ] ;
}
return false ;
} ;
$modalStack . clearFocusListCache = function ( ) {
focusableElementList = [ ] ;
focusIndex = 0 ;
} ;
$modalStack . loadFocusElementList = function ( modalWindow ) {
if ( focusableElementList === undefined || ! focusableElementList . length ) {
if ( modalWindow ) {
var modalDomE1 = modalWindow . value . modalDomEl ;
if ( modalDomE1 && modalDomE1 . length ) {
focusableElementList = modalDomE1 [ 0 ] . querySelectorAll ( tababbleSelector ) ;
}
}
}
} ;
return $modalStack ;
} ] )
. provider ( '$uibModal' , function ( ) {
var $modalProvider = {
options : {
animation : true ,
backdrop : true , //can also be false or 'static'
keyboard : true
} ,
$get : [ '$injector' , '$rootScope' , '$q' , '$templateRequest' , '$controller' , '$uibModalStack' , '$modalSuppressWarning' , '$log' ,
function ( $injector , $rootScope , $q , $templateRequest , $controller , $modalStack , $modalSuppressWarning , $log ) {
var $modal = { } ;
function getTemplatePromise ( options ) {
return options . template ? $q . when ( options . template ) :
$templateRequest ( angular . isFunction ( options . templateUrl ) ? ( options . templateUrl ) ( ) : options . templateUrl ) ;
}
function getResolvePromises ( resolves ) {
var promisesArr = [ ] ;
angular . forEach ( resolves , function ( value ) {
if ( angular . isFunction ( value ) || angular . isArray ( value ) ) {
promisesArr . push ( $q . when ( $injector . invoke ( value ) ) ) ;
} else if ( angular . isString ( value ) ) {
promisesArr . push ( $q . when ( $injector . get ( value ) ) ) ;
} else {
promisesArr . push ( $q . when ( value ) ) ;
}
} ) ;
return promisesArr ;
}
var promiseChain = null ;
$modal . getPromiseChain = function ( ) {
return promiseChain ;
} ;
$modal . open = function ( modalOptions ) {
var modalResultDeferred = $q . defer ( ) ;
var modalOpenedDeferred = $q . defer ( ) ;
var modalRenderDeferred = $q . defer ( ) ;
//prepare an instance of a modal to be injected into controllers and returned to a caller
var modalInstance = {
result : modalResultDeferred . promise ,
opened : modalOpenedDeferred . promise ,
rendered : modalRenderDeferred . promise ,
close : function ( result ) {
return $modalStack . close ( modalInstance , result ) ;
} ,
dismiss : function ( reason ) {
return $modalStack . dismiss ( modalInstance , reason ) ;
}
} ;
//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 ) ) ) ;
function resolveWithTemplate ( ) {
return templateAndResolvePromise ;
}
// Wait for the resolution of the existing promise chain.
// Then switch to our own combined promise dependency (regardless of how the previous modal fared).
// Then add to $modalStack and resolve opened.
// Finally clean up the chain variable if no subsequent modal has overwritten it.
var samePromise ;
samePromise = promiseChain = $q . all ( [ promiseChain ] )
. then ( resolveWithTemplate , resolveWithTemplate )
. then ( function resolveSuccess ( tplAndVars ) {
var modalScope = ( modalOptions . scope || $rootScope ) . $new ( ) ;
modalScope . $close = modalInstance . close ;
modalScope . $dismiss = modalInstance . dismiss ;
modalScope . $on ( '$destroy' , function ( ) {
if ( ! modalScope . $$uibDestructionScheduled ) {
modalScope . $dismiss ( '$uibUnscheduledDestruction' ) ;
}
} ) ;
var ctrlInstance , ctrlLocals = { } ;
var resolveIter = 1 ;
//controllers
if ( modalOptions . controller ) {
ctrlLocals . $scope = modalScope ;
ctrlLocals . $uibModalInstance = modalInstance ;
Object . defineProperty ( ctrlLocals , '$modalInstance' , {
get : function ( ) {
if ( ! $modalSuppressWarning ) {
$log . warn ( '$modalInstance is now deprecated. Use $uibModalInstance instead.' ) ;
}
return modalInstance ;
}
} ) ;
angular . forEach ( modalOptions . resolve , function ( value , key ) {
ctrlLocals [ key ] = tplAndVars [ resolveIter ++ ] ;
} ) ;
ctrlInstance = $controller ( modalOptions . controller , ctrlLocals ) ;
if ( modalOptions . controllerAs ) {
if ( modalOptions . bindToController ) {
angular . extend ( ctrlInstance , modalScope ) ;
}
modalScope [ modalOptions . controllerAs ] = ctrlInstance ;
}
}
$modalStack . open ( modalInstance , {
scope : modalScope ,
deferred : modalResultDeferred ,
renderDeferred : modalRenderDeferred ,
content : tplAndVars [ 0 ] ,
animation : modalOptions . animation ,
backdrop : modalOptions . backdrop ,
keyboard : modalOptions . keyboard ,
backdropClass : modalOptions . backdropClass ,
windowTopClass : modalOptions . windowTopClass ,
windowClass : modalOptions . windowClass ,
windowTemplateUrl : modalOptions . windowTemplateUrl ,
size : modalOptions . size ,
openedClass : modalOptions . openedClass
} ) ;
modalOpenedDeferred . resolve ( true ) ;
} , function resolveError ( reason ) {
modalOpenedDeferred . reject ( reason ) ;
modalResultDeferred . reject ( reason ) ;
} )
. finally ( function ( ) {
if ( promiseChain === samePromise ) {
promiseChain = null ;
}
} ) ;
return modalInstance ;
} ;
return $modal ;
}
]
} ;
return $modalProvider ;
} ) ;
/* deprecated modal below */
angular . module ( 'ui.bootstrap.modal' )
. value ( '$modalSuppressWarning' , false )
/ * *
* A helper directive for the $modal service . It creates a backdrop element .
* /
. directive ( 'modalBackdrop' , [
'$animate' , '$injector' , '$modalStack' , '$log' , '$modalSuppressWarning' ,
function ( $animate , $injector , $modalStack , $log , $modalSuppressWarning ) {
var $animateCss = null ;
if ( $injector . has ( '$animateCss' ) ) {
$animateCss = $injector . get ( '$animateCss' ) ;
}
return {
replace : true ,
templateUrl : 'template/modal/backdrop.html' ,
compile : function ( tElement , tAttrs ) {
tElement . addClass ( tAttrs . backdropClass ) ;
return linkFn ;
}
} ;
function linkFn ( scope , element , attrs ) {
if ( ! $modalSuppressWarning ) {
$log . warn ( 'modal-backdrop is now deprecated. Use uib-modal-backdrop instead.' ) ;
}
element . addClass ( 'modal-backdrop' ) ;
if ( attrs . modalInClass ) {
if ( $animateCss ) {
$animateCss ( element , {
addClass : attrs . modalInClass
} ) . start ( ) ;
} else {
$animate . addClass ( element , attrs . modalInClass ) ;
}
scope . $on ( $modalStack . NOW _CLOSING _EVENT , function ( e , setIsAsync ) {
var done = setIsAsync ( ) ;
if ( $animateCss ) {
$animateCss ( element , {
removeClass : attrs . modalInClass
} ) . start ( ) . then ( done ) ;
} else {
$animate . removeClass ( element , attrs . modalInClass ) . then ( done ) ;
}
} ) ;
}
}
} ] )
. directive ( 'modalWindow' , [
'$modalStack' , '$q' , '$animate' , '$injector' , '$log' , '$modalSuppressWarning' ,
function ( $modalStack , $q , $animate , $injector , $log , $modalSuppressWarning ) {
var $animateCss = null ;
if ( $injector . has ( '$animateCss' ) ) {
$animateCss = $injector . get ( '$animateCss' ) ;
}
return {
scope : {
index : '@'
} ,
replace : true ,
transclude : true ,
templateUrl : function ( tElement , tAttrs ) {
return tAttrs . templateUrl || 'template/modal/window.html' ;
} ,
link : function ( scope , element , attrs ) {
if ( ! $modalSuppressWarning ) {
$log . warn ( 'modal-window is now deprecated. Use uib-modal-window instead.' ) ;
}
element . addClass ( attrs . windowClass || '' ) ;
element . addClass ( attrs . windowTopClass || '' ) ;
scope . size = attrs . size ;
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' ) ;
}
} ;
// moved from template to fix issue #2280
element . on ( 'click' , scope . close ) ;
// 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 ( ) {
var animationPromise = null ;
if ( attrs . modalInClass ) {
if ( $animateCss ) {
animationPromise = $animateCss ( element , {
addClass : attrs . modalInClass
} ) . start ( ) ;
} else {
animationPromise = $animate . addClass ( element , attrs . modalInClass ) ;
}
scope . $on ( $modalStack . NOW _CLOSING _EVENT , function ( e , setIsAsync ) {
var done = setIsAsync ( ) ;
if ( $animateCss ) {
$animateCss ( element , {
removeClass : attrs . modalInClass
} ) . start ( ) . then ( done ) ;
} else {
$animate . removeClass ( element , attrs . modalInClass ) . then ( done ) ;
}
} ) ;
}
$q . when ( animationPromise ) . then ( function ( ) {
var inputWithAutofocus = element [ 0 ] . querySelector ( '[autofocus]' ) ;
/ * *
* 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 .
* /
if ( inputWithAutofocus ) {
inputWithAutofocus . focus ( ) ;
} else {
element [ 0 ] . focus ( ) ;
}
} ) ;
// Notify {@link $modalStack} that modal is rendered.
var modal = $modalStack . getTop ( ) ;
if ( modal ) {
$modalStack . modalRendered ( modal . key ) ;
}
} ) ;
}
} ;
} ] )
. directive ( 'modalAnimationClass' , [
'$log' , '$modalSuppressWarning' ,
function ( $log , $modalSuppressWarning ) {
return {
compile : function ( tElement , tAttrs ) {
if ( ! $modalSuppressWarning ) {
$log . warn ( 'modal-animation-class is now deprecated. Use uib-modal-animation-class instead.' ) ;
}
if ( tAttrs . modalAnimation ) {
tElement . addClass ( tAttrs . modalAnimationClass ) ;
}
}
} ;
} ] )
. directive ( 'modalTransclude' , [
'$log' , '$modalSuppressWarning' ,
function ( $log , $modalSuppressWarning ) {
return {
link : function ( $scope , $element , $attrs , controller , $transclude ) {
if ( ! $modalSuppressWarning ) {
$log . warn ( 'modal-transclude is now deprecated. Use uib-modal-transclude instead.' ) ;
}
$transclude ( $scope . $parent , function ( clone ) {
$element . empty ( ) ;
$element . append ( clone ) ;
} ) ;
}
} ;
} ] )
. service ( '$modalStack' , [
'$animate' , '$timeout' , '$document' , '$compile' , '$rootScope' ,
'$q' ,
'$injector' ,
'$$multiMap' ,
'$$stackedMap' ,
'$uibModalStack' ,
'$log' ,
'$modalSuppressWarning' ,
function ( $animate , $timeout , $document , $compile , $rootScope ,
$q ,
$injector ,
$$multiMap ,
$$stackedMap ,
$uibModalStack ,
$log ,
$modalSuppressWarning ) {
if ( ! $modalSuppressWarning ) {
$log . warn ( '$modalStack is now deprecated. Use $uibModalStack instead.' ) ;
}
angular . extend ( this , $uibModalStack ) ;
} ] )
. provider ( '$modal' , [ '$uibModalProvider' , function ( $uibModalProvider ) {
angular . extend ( this , $uibModalProvider ) ;
this . $get = [ '$injector' , '$log' , '$modalSuppressWarning' ,
function ( $injector , $log , $modalSuppressWarning ) {
if ( ! $modalSuppressWarning ) {
$log . warn ( '$modal is now deprecated. Use $uibModal instead.' ) ;
}
return $injector . invoke ( $uibModalProvider . $get ) ;
} ] ;
} ] ) ;
angular . module ( 'ui.bootstrap.pagination' , [ ] )
. controller ( 'UibPaginationController' , [ '$scope' , '$attrs' , '$parse' , function ( $scope , $attrs , $parse ) {
var self = this ,
ngModelCtrl = { $setViewValue : angular . noop } , // nullModelCtrl
setNumPages = $attrs . numPages ? $parse ( $attrs . numPages ) . assign : angular . noop ;
this . init = function ( ngModelCtrl _ , config ) {
ngModelCtrl = ngModelCtrl _ ;
this . config = config ;
ngModelCtrl . $render = function ( ) {
self . render ( ) ;
} ;
if ( $attrs . itemsPerPage ) {
$scope . $parent . $watch ( $parse ( $attrs . itemsPerPage ) , function ( value ) {
self . itemsPerPage = parseInt ( value , 10 ) ;
$scope . totalPages = self . calculateTotalPages ( ) ;
} ) ;
} else {
this . itemsPerPage = config . itemsPerPage ;
}
$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 ( ) ;
}
} ) ;
} ;
this . calculateTotalPages = function ( ) {
var totalPages = this . itemsPerPage < 1 ? 1 : Math . ceil ( $scope . totalItems / this . itemsPerPage ) ;
return Math . max ( totalPages || 0 , 1 ) ;
} ;
this . render = function ( ) {
$scope . page = parseInt ( ngModelCtrl . $viewValue , 10 ) || 1 ;
} ;
$scope . selectPage = function ( page , evt ) {
if ( evt ) {
evt . preventDefault ( ) ;
}
var clickAllowed = ! $scope . ngDisabled || ! evt ;
if ( clickAllowed && $scope . page !== page && page > 0 && page <= $scope . totalPages ) {
if ( evt && evt . target ) {
evt . target . blur ( ) ;
}
ngModelCtrl . $setViewValue ( page ) ;
ngModelCtrl . $render ( ) ;
}
} ;
$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 ;
} ;
} ] )
. constant ( 'uibPaginationConfig' , {
itemsPerPage : 10 ,
boundaryLinks : false ,
directionLinks : true ,
firstText : 'First' ,
previousText : 'Previous' ,
nextText : 'Next' ,
lastText : 'Last' ,
rotate : true
} )
. directive ( 'uibPagination' , [ '$parse' , 'uibPaginationConfig' , function ( $parse , paginationConfig ) {
return {
restrict : 'EA' ,
scope : {
totalItems : '=' ,
firstText : '@' ,
previousText : '@' ,
nextText : '@' ,
lastText : '@' ,
ngDisabled : '='
} ,
require : [ 'uibPagination' , '?ngModel' ] ,
controller : 'UibPaginationController' ,
controllerAs : 'pagination' ,
templateUrl : function ( element , attrs ) {
return attrs . templateUrl || 'template/pagination/pagination.html' ;
} ,
replace : true ,
link : function ( scope , element , attrs , ctrls ) {
var paginationCtrl = ctrls [ 0 ] , ngModelCtrl = ctrls [ 1 ] ;
if ( ! ngModelCtrl ) {
return ; // do nothing if no ng-model
}
// Setup configuration parameters
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 ;
paginationCtrl . init ( ngModelCtrl , paginationConfig ) ;
if ( attrs . maxSize ) {
scope . $parent . $watch ( $parse ( attrs . maxSize ) , function ( value ) {
maxSize = parseInt ( value , 10 ) ;
paginationCtrl . render ( ) ;
} ) ;
}
// Create page object used in template
function makePage ( number , text , isActive ) {
return {
number : number ,
text : text ,
active : isActive
} ;
}
function getPages ( currentPage , totalPages ) {
var pages = [ ] ;
// Default page limits
var startPage = 1 , endPage = totalPages ;
var isMaxSized = angular . isDefined ( maxSize ) && maxSize < totalPages ;
// 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 ;
// 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 ;
// Adjust last page if limit is exceeded
endPage = Math . min ( startPage + maxSize - 1 , totalPages ) ;
}
}
// Add page number links
for ( var number = startPage ; number <= endPage ; number ++ ) {
var page = makePage ( number , number , number === currentPage ) ;
pages . push ( page ) ;
}
// Add links to move between page sets
if ( isMaxSized && ! rotate ) {
if ( startPage > 1 ) {
var previousPageSet = makePage ( startPage - 1 , '...' , false ) ;
pages . unshift ( previousPageSet ) ;
}
if ( endPage < totalPages ) {
var nextPageSet = makePage ( endPage + 1 , '...' , false ) ;
pages . push ( nextPageSet ) ;
}
}
return pages ;
}
var originalRender = paginationCtrl . render ;
paginationCtrl . render = function ( ) {
originalRender ( ) ;
if ( scope . page > 0 && scope . page <= scope . totalPages ) {
scope . pages = getPages ( scope . page , scope . totalPages ) ;
}
} ;
}
} ;
} ] )
. constant ( 'uibPagerConfig' , {
itemsPerPage : 10 ,
previousText : '« Previous' ,
nextText : 'Next »' ,
align : true
} )
. directive ( 'uibPager' , [ 'uibPagerConfig' , function ( pagerConfig ) {
return {
restrict : 'EA' ,
scope : {
totalItems : '=' ,
previousText : '@' ,
nextText : '@' ,
ngDisabled : '='
} ,
require : [ 'uibPager' , '?ngModel' ] ,
controller : 'UibPaginationController' ,
controllerAs : 'pagination' ,
templateUrl : function ( element , attrs ) {
return attrs . templateUrl || 'template/pagination/pager.html' ;
} ,
replace : true ,
link : function ( scope , element , attrs , ctrls ) {
var paginationCtrl = ctrls [ 0 ] , ngModelCtrl = ctrls [ 1 ] ;
if ( ! ngModelCtrl ) {
return ; // do nothing if no ng-model
}
scope . align = angular . isDefined ( attrs . align ) ? scope . $parent . $eval ( attrs . align ) : pagerConfig . align ;
paginationCtrl . init ( ngModelCtrl , pagerConfig ) ;
}
} ;
} ] ) ;
/* Deprecated Pagination Below */
angular . module ( 'ui.bootstrap.pagination' )
. value ( '$paginationSuppressWarning' , false )
. controller ( 'PaginationController' , [ '$scope' , '$attrs' , '$parse' , '$log' , '$paginationSuppressWarning' , function ( $scope , $attrs , $parse , $log , $paginationSuppressWarning ) {
if ( ! $paginationSuppressWarning ) {
$log . warn ( 'PaginationController is now deprecated. Use UibPaginationController instead.' ) ;
}
var self = this ,
ngModelCtrl = { $setViewValue : angular . noop } , // nullModelCtrl
setNumPages = $attrs . numPages ? $parse ( $attrs . numPages ) . assign : angular . noop ;
this . init = function ( ngModelCtrl _ , config ) {
ngModelCtrl = ngModelCtrl _ ;
this . config = config ;
ngModelCtrl . $render = function ( ) {
self . render ( ) ;
} ;
if ( $attrs . itemsPerPage ) {
$scope . $parent . $watch ( $parse ( $attrs . itemsPerPage ) , function ( value ) {
self . itemsPerPage = parseInt ( value , 10 ) ;
$scope . totalPages = self . calculateTotalPages ( ) ;
} ) ;
} else {
this . itemsPerPage = config . itemsPerPage ;
}
$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 ( ) ;
}
} ) ;
} ;
this . calculateTotalPages = function ( ) {
var totalPages = this . itemsPerPage < 1 ? 1 : Math . ceil ( $scope . totalItems / this . itemsPerPage ) ;
return Math . max ( totalPages || 0 , 1 ) ;
} ;
this . render = function ( ) {
$scope . page = parseInt ( ngModelCtrl . $viewValue , 10 ) || 1 ;
} ;
$scope . selectPage = function ( page , evt ) {
if ( evt ) {
evt . preventDefault ( ) ;
}
var clickAllowed = ! $scope . ngDisabled || ! evt ;
if ( clickAllowed && $scope . page !== page && page > 0 && page <= $scope . totalPages ) {
if ( evt && evt . target ) {
evt . target . blur ( ) ;
}
ngModelCtrl . $setViewValue ( page ) ;
ngModelCtrl . $render ( ) ;
}
} ;
$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 ;
} ;
} ] )
. directive ( 'pagination' , [ '$parse' , 'uibPaginationConfig' , '$log' , '$paginationSuppressWarning' , function ( $parse , paginationConfig , $log , $paginationSuppressWarning ) {
return {
restrict : 'EA' ,
scope : {
totalItems : '=' ,
firstText : '@' ,
previousText : '@' ,
nextText : '@' ,
lastText : '@' ,
ngDisabled : '='
} ,
require : [ 'pagination' , '?ngModel' ] ,
controller : 'PaginationController' ,
controllerAs : 'pagination' ,
templateUrl : function ( element , attrs ) {
return attrs . templateUrl || 'template/pagination/pagination.html' ;
} ,
replace : true ,
link : function ( scope , element , attrs , ctrls ) {
if ( ! $paginationSuppressWarning ) {
$log . warn ( 'pagination is now deprecated. Use uib-pagination instead.' ) ;
}
var paginationCtrl = ctrls [ 0 ] , ngModelCtrl = ctrls [ 1 ] ;
if ( ! ngModelCtrl ) {
return ; // do nothing if no ng-model
}
// Setup configuration parameters
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 ;
paginationCtrl . init ( ngModelCtrl , paginationConfig ) ;
if ( attrs . maxSize ) {
scope . $parent . $watch ( $parse ( attrs . maxSize ) , function ( value ) {
maxSize = parseInt ( value , 10 ) ;
paginationCtrl . render ( ) ;
} ) ;
}
// Create page object used in template
function makePage ( number , text , isActive ) {
return {
number : number ,
text : text ,
active : isActive
} ;
}
function getPages ( currentPage , totalPages ) {
var pages = [ ] ;
// Default page limits
var startPage = 1 , endPage = totalPages ;
var isMaxSized = angular . isDefined ( maxSize ) && maxSize < totalPages ;
// 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 ;
// 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 ;
// Adjust last page if limit is exceeded
endPage = Math . min ( startPage + maxSize - 1 , totalPages ) ;
}
}
// Add page number links
for ( var number = startPage ; number <= endPage ; number ++ ) {
var page = makePage ( number , number , number === currentPage ) ;
pages . push ( page ) ;
}
// Add links to move between page sets
if ( isMaxSized && ! rotate ) {
if ( startPage > 1 ) {
var previousPageSet = makePage ( startPage - 1 , '...' , false ) ;
pages . unshift ( previousPageSet ) ;
}
if ( endPage < totalPages ) {
var nextPageSet = makePage ( endPage + 1 , '...' , false ) ;
pages . push ( nextPageSet ) ;
}
}
return pages ;
}
var originalRender = paginationCtrl . render ;
paginationCtrl . render = function ( ) {
originalRender ( ) ;
if ( scope . page > 0 && scope . page <= scope . totalPages ) {
scope . pages = getPages ( scope . page , scope . totalPages ) ;
}
} ;
}
} ;
} ] )
. directive ( 'pager' , [ 'uibPagerConfig' , '$log' , '$paginationSuppressWarning' , function ( pagerConfig , $log , $paginationSuppressWarning ) {
return {
restrict : 'EA' ,
scope : {
totalItems : '=' ,
previousText : '@' ,
nextText : '@' ,
ngDisabled : '='
} ,
require : [ 'pager' , '?ngModel' ] ,
controller : 'PaginationController' ,
controllerAs : 'pagination' ,
templateUrl : function ( element , attrs ) {
return attrs . templateUrl || 'template/pagination/pager.html' ;
} ,
replace : true ,
link : function ( scope , element , attrs , ctrls ) {
if ( ! $paginationSuppressWarning ) {
$log . warn ( 'pager is now deprecated. Use uib-pager instead.' ) ;
}
var paginationCtrl = ctrls [ 0 ] , ngModelCtrl = ctrls [ 1 ] ;
if ( ! ngModelCtrl ) {
return ; // do nothing if no ng-model
}
scope . align = angular . isDefined ( attrs . align ) ? scope . $parent . $eval ( attrs . align ) : pagerConfig . align ;
paginationCtrl . init ( ngModelCtrl , pagerConfig ) ;
}
} ;
} ] ) ;
/ * *
* 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 .
* /
angular . module ( 'ui.bootstrap.tooltip' , [ 'ui.bootstrap.position' , 'ui.bootstrap.stackedMap' ] )
/ * *
* The $tooltip service creates tooltip - and popover - like directives as well as
* houses global options for them .
* /
. provider ( '$uibTooltip' , function ( ) {
// The default options tooltip and popover.
var defaultOptions = {
placement : 'top' ,
animation : true ,
popupDelay : 0 ,
popupCloseDelay : 0 ,
useContentExp : false
} ;
// Default hide triggers for each show trigger
var triggerMap = {
'mouseenter' : 'mouseleave' ,
'click' : 'click' ,
'focus' : 'blur' ,
'none' : ''
} ;
// The options specified to the provider globally.
var globalOptions = { } ;
/ * *
* ` 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' } ) ;
* } ) ;
* /
this . options = function ( value ) {
angular . extend ( globalOptions , value ) ;
} ;
/ * *
* 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 ) ;
} ;
/ * *
* 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 ( ) ;
} ) ;
}
/ * *
* Returns the actual instance of the $tooltip service .
* TODO support multiple triggers
* /
this . $get = [ '$window' , '$compile' , '$timeout' , '$document' , '$uibPosition' , '$interpolate' , '$rootScope' , '$parse' , '$$stackedMap' , function ( $window , $compile , $timeout , $document , $position , $interpolate , $rootScope , $parse , $$stackedMap ) {
var openedTooltips = $$stackedMap . createNew ( ) ;
$document . on ( 'keypress' , function ( e ) {
if ( e . which === 27 ) {
var last = openedTooltips . top ( ) ;
if ( last ) {
last . value . close ( ) ;
openedTooltips . removeTop ( ) ;
last = null ;
}
}
} ) ;
return function $tooltip ( ttType , prefix , defaultTriggerShow , options ) {
options = angular . extend ( { } , defaultOptions , globalOptions , options ) ;
/ * *
* 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 ) . split ( ' ' ) ;
var hide = show . map ( function ( trigger ) {
return triggerMap [ trigger ] || trigger ;
} ) ;
return {
show : show ,
hide : hide
} ;
}
var directiveName = snake _case ( ttType ) ;
var startSym = $interpolate . startSymbol ( ) ;
var endSym = $interpolate . endSymbol ( ) ;
var template =
'<div ' + directiveName + '-popup ' +
'title="' + startSym + 'title' + endSym + '" ' +
( options . useContentExp ?
'content-exp="contentExp()" ' :
'content="' + startSym + 'content' + endSym + '" ' ) +
'placement="' + startSym + 'placement' + endSym + '" ' +
'popup-class="' + startSym + 'popupClass' + endSym + '" ' +
'animation="animation" ' +
'is-open="isOpen"' +
'origin-scope="origScope" ' +
'style="visibility: hidden; display: block; top: -9999px; left: -9999px;"' +
'>' +
'</div>' ;
return {
compile : function ( tElem , tAttrs ) {
var tooltipLinker = $compile ( template ) ;
return function link ( scope , element , attrs , tooltipCtrl ) {
var tooltip ;
var tooltipLinkedScope ;
var transitionTimeout ;
var showTimeout ;
var hideTimeout ;
var positionTimeout ;
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 repositionScheduled = false ;
var isOpenParse = angular . isDefined ( attrs [ prefix + 'IsOpen' ] ) ? $parse ( attrs [ prefix + 'IsOpen' ] ) : false ;
var contentParse = options . useContentExp ? $parse ( attrs [ ttType ] ) : false ;
var observers = [ ] ;
var positionTooltip = function ( ) {
// check if tooltip exists and is not empty
if ( ! tooltip || ! tooltip . html ( ) ) { return ; }
if ( ! positionTimeout ) {
positionTimeout = $timeout ( function ( ) {
// Reset the positioning.
tooltip . css ( { top : 0 , left : 0 } ) ;
// Now set the calculated positioning.
var ttCss = $position . positionElements ( element , tooltip , ttScope . placement , appendToBody ) ;
ttCss . top += 'px' ;
ttCss . left += 'px' ;
ttCss . visibility = 'visible' ;
tooltip . css ( ttCss ) ;
positionTimeout = null ;
} , 0 , false ) ;
}
} ;
// Set up the correct scope to allow transclusion later
ttScope . origScope = scope ;
// By default, the tooltip is not open.
// TODO add ability to start tooltip opened
ttScope . isOpen = false ;
openedTooltips . add ( ttScope , {
close : hide
} ) ;
function toggleTooltipBind ( ) {
if ( ! ttScope . isOpen ) {
showTooltipBind ( ) ;
} else {
hideTooltipBind ( ) ;
}
}
// Show the tooltip with delay if specified, otherwise show it immediately
function showTooltipBind ( ) {
if ( hasEnableExp && ! scope . $eval ( attrs [ prefix + 'Enable' ] ) ) {
return ;
}
cancelHide ( ) ;
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 ( ! showTimeout ) {
showTimeout = $timeout ( show , ttScope . popupDelay , false ) ;
}
} else {
show ( ) ;
}
}
function hideTooltipBind ( ) {
cancelShow ( ) ;
if ( ttScope . popupCloseDelay ) {
if ( ! hideTimeout ) {
hideTimeout = $timeout ( hide , ttScope . popupCloseDelay , false ) ;
}
} else {
hide ( ) ;
}
}
// Show the tooltip popup element.
function show ( ) {
cancelShow ( ) ;
cancelHide ( ) ;
// Don't show empty tooltips.
if ( ! ttScope . content ) {
return angular . noop ;
}
createTooltip ( ) ;
// And show the tooltip.
ttScope . $evalAsync ( function ( ) {
ttScope . isOpen = true ;
assignIsOpen ( true ) ;
positionTooltip ( ) ;
} ) ;
}
function cancelShow ( ) {
if ( showTimeout ) {
$timeout . cancel ( showTimeout ) ;
showTimeout = null ;
}
if ( positionTimeout ) {
$timeout . cancel ( positionTimeout ) ;
positionTimeout = null ;
}
}
// Hide the tooltip popup element.
function hide ( ) {
cancelShow ( ) ;
cancelHide ( ) ;
if ( ! ttScope ) {
return ;
}
// First things first: we don't show it anymore.
ttScope . $evalAsync ( function ( ) {
ttScope . isOpen = false ;
assignIsOpen ( false ) ;
// 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.
// The fade transition in TWBS is 150ms.
if ( ttScope . animation ) {
if ( ! transitionTimeout ) {
transitionTimeout = $timeout ( removeTooltip , 150 , false ) ;
}
} else {
removeTooltip ( ) ;
}
} ) ;
}
function cancelHide ( ) {
if ( hideTimeout ) {
$timeout . cancel ( hideTimeout ) ;
hideTimeout = null ;
}
if ( transitionTimeout ) {
$timeout . cancel ( transitionTimeout ) ;
transitionTimeout = null ;
}
}
function createTooltip ( ) {
// There can only be one tooltip element per directive shown at once.
if ( tooltip ) {
return ;
}
tooltipLinkedScope = ttScope . $new ( ) ;
tooltip = tooltipLinker ( tooltipLinkedScope , function ( tooltip ) {
if ( appendToBody ) {
$document . find ( 'body' ) . append ( tooltip ) ;
} else {
element . after ( tooltip ) ;
}
} ) ;
prepObservers ( ) ;
}
function removeTooltip ( ) {
unregisterObservers ( ) ;
transitionTimeout = null ;
if ( tooltip ) {
tooltip . remove ( ) ;
tooltip = null ;
}
if ( tooltipLinkedScope ) {
tooltipLinkedScope . $destroy ( ) ;
tooltipLinkedScope = null ;
}
}
/ * *
* Set the inital scope values . Once
* the tooltip is created , the observers
* will be added to keep things in synch .
* /
function prepareTooltip ( ) {
ttScope . title = attrs [ prefix + 'Title' ] ;
if ( contentParse ) {
ttScope . content = contentParse ( scope ) ;
} else {
ttScope . content = attrs [ ttType ] ;
}
ttScope . popupClass = attrs [ prefix + 'Class' ] ;
ttScope . placement = angular . isDefined ( attrs [ prefix + 'Placement' ] ) ? attrs [ prefix + 'Placement' ] : options . placement ;
var delay = parseInt ( attrs [ prefix + 'PopupDelay' ] , 10 ) ;
var closeDelay = parseInt ( attrs [ prefix + 'PopupCloseDelay' ] , 10 ) ;
ttScope . popupDelay = ! isNaN ( delay ) ? delay : options . popupDelay ;
ttScope . popupCloseDelay = ! isNaN ( closeDelay ) ? closeDelay : options . popupCloseDelay ;
}
function assignIsOpen ( isOpen ) {
if ( isOpenParse && angular . isFunction ( isOpenParse . assign ) ) {
isOpenParse . assign ( scope , isOpen ) ;
}
}
ttScope . contentExp = function ( ) {
return ttScope . content ;
} ;
/ * *
* Observe the relevant attributes .
* /
attrs . $observe ( 'disabled' , function ( val ) {
if ( val ) {
cancelShow ( ) ;
}
if ( val && ttScope . isOpen ) {
hide ( ) ;
}
} ) ;
if ( isOpenParse ) {
scope . $watch ( isOpenParse , function ( val ) {
/*jshint -W018 */
if ( ttScope && ! val === ttScope . isOpen ) {
toggleTooltipBind ( ) ;
}
/*jshint +W018 */
} ) ;
}
function prepObservers ( ) {
observers . length = 0 ;
if ( contentParse ) {
observers . push (
scope . $watch ( contentParse , function ( val ) {
ttScope . content = val ;
if ( ! val && ttScope . isOpen ) {
hide ( ) ;
}
} )
) ;
observers . push (
tooltipLinkedScope . $watch ( function ( ) {
if ( ! repositionScheduled ) {
repositionScheduled = true ;
tooltipLinkedScope . $$postDigest ( function ( ) {
repositionScheduled = false ;
if ( ttScope && ttScope . isOpen ) {
positionTooltip ( ) ;
}
} ) ;
}
} )
) ;
} else {
observers . push (
attrs . $observe ( ttType , function ( val ) {
ttScope . content = val ;
if ( ! val && ttScope . isOpen ) {
hide ( ) ;
} else {
positionTooltip ( ) ;
}
} )
) ;
}
observers . push (
attrs . $observe ( prefix + 'Title' , function ( val ) {
ttScope . title = val ;
if ( ttScope . isOpen ) {
positionTooltip ( ) ;
}
} )
) ;
observers . push (
attrs . $observe ( prefix + 'Placement' , function ( val ) {
ttScope . placement = val ? val : options . placement ;
if ( ttScope . isOpen ) {
positionTooltip ( ) ;
}
} )
) ;
}
function unregisterObservers ( ) {
if ( observers . length ) {
angular . forEach ( observers , function ( observer ) {
observer ( ) ;
} ) ;
observers . length = 0 ;
}
}
var unregisterTriggers = function ( ) {
triggers . show . forEach ( function ( trigger ) {
element . unbind ( trigger , showTooltipBind ) ;
} ) ;
triggers . hide . forEach ( function ( trigger ) {
trigger . split ( ' ' ) . forEach ( function ( hideTrigger ) {
element [ 0 ] . removeEventListener ( hideTrigger , hideTooltipBind ) ;
} ) ;
} ) ;
} ;
function prepTriggers ( ) {
var val = attrs [ prefix + 'Trigger' ] ;
unregisterTriggers ( ) ;
triggers = getTriggers ( val ) ;
if ( triggers . show !== 'none' ) {
triggers . show . forEach ( function ( trigger , idx ) {
// Using raw addEventListener due to jqLite/jQuery bug - #4060
if ( trigger === triggers . hide [ idx ] ) {
element [ 0 ] . addEventListener ( trigger , toggleTooltipBind ) ;
} else if ( trigger ) {
element [ 0 ] . addEventListener ( trigger , showTooltipBind ) ;
triggers . hide [ idx ] . split ( ' ' ) . forEach ( function ( trigger ) {
element [ 0 ] . addEventListener ( trigger , hideTooltipBind ) ;
} ) ;
}
element . on ( 'keypress' , function ( e ) {
if ( e . which === 27 ) {
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 ( ) ;
}
} ) ;
}
// Make sure tooltip is destroyed and removed.
scope . $on ( '$destroy' , function onDestroyTooltip ( ) {
cancelShow ( ) ;
cancelHide ( ) ;
unregisterTriggers ( ) ;
removeTooltip ( ) ;
openedTooltips . remove ( ttScope ) ;
ttScope = null ;
} ) ;
} ;
}
} ;
} ;
} ] ;
} )
// This is mostly ngInclude code but with a custom scope
. directive ( 'uibTooltipTemplateTransclude' , [
'$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 . uibTooltipTemplateTransclude ) , 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 ( 'uibTooltipClasses' , 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 ) ;
}
}
} ;
} )
. directive ( 'uibTooltipPopup' , function ( ) {
return {
replace : true ,
scope : { content : '@' , placement : '@' , popupClass : '@' , animation : '&' , isOpen : '&' } ,
templateUrl : 'template/tooltip/tooltip-popup.html' ,
link : function ( scope , element ) {
element . addClass ( 'tooltip' ) ;
}
} ;
} )
. directive ( 'uibTooltip' , [ '$uibTooltip' , function ( $uibTooltip ) {
return $uibTooltip ( 'uibTooltip' , 'tooltip' , 'mouseenter' ) ;
} ] )
. directive ( 'uibTooltipTemplatePopup' , function ( ) {
return {
replace : true ,
scope : { contentExp : '&' , placement : '@' , popupClass : '@' , animation : '&' , isOpen : '&' ,
originScope : '&' } ,
templateUrl : 'template/tooltip/tooltip-template-popup.html' ,
link : function ( scope , element ) {
element . addClass ( 'tooltip' ) ;
}
} ;
} )
. directive ( 'uibTooltipTemplate' , [ '$uibTooltip' , function ( $uibTooltip ) {
return $uibTooltip ( 'uibTooltipTemplate' , 'tooltip' , 'mouseenter' , {
useContentExp : true
} ) ;
} ] )
. directive ( 'uibTooltipHtmlPopup' , function ( ) {
return {
replace : true ,
scope : { contentExp : '&' , placement : '@' , popupClass : '@' , animation : '&' , isOpen : '&' } ,
templateUrl : 'template/tooltip/tooltip-html-popup.html' ,
link : function ( scope , element ) {
element . addClass ( 'tooltip' ) ;
}
} ;
} )
. directive ( 'uibTooltipHtml' , [ '$uibTooltip' , function ( $uibTooltip ) {
return $uibTooltip ( 'uibTooltipHtml' , 'tooltip' , 'mouseenter' , {
useContentExp : true
} ) ;
} ] ) ;
/* Deprecated tooltip below */
angular . module ( 'ui.bootstrap.tooltip' )
. value ( '$tooltipSuppressWarning' , false )
. provider ( '$tooltip' , [ '$uibTooltipProvider' , function ( $uibTooltipProvider ) {
angular . extend ( this , $uibTooltipProvider ) ;
this . $get = [ '$log' , '$tooltipSuppressWarning' , '$injector' , function ( $log , $tooltipSuppressWarning , $injector ) {
if ( ! $tooltipSuppressWarning ) {
$log . warn ( '$tooltip is now deprecated. Use $uibTooltip instead.' ) ;
}
return $injector . invoke ( $uibTooltipProvider . $get ) ;
} ] ;
} ] )
// This is mostly ngInclude code but with a custom scope
. directive ( 'tooltipTemplateTransclude' , [
'$animate' , '$sce' , '$compile' , '$templateRequest' , '$log' , '$tooltipSuppressWarning' ,
function ( $animate , $sce , $compile , $templateRequest , $log , $tooltipSuppressWarning ) {
return {
link : function ( scope , elem , attrs ) {
if ( ! $tooltipSuppressWarning ) {
$log . warn ( 'tooltip-template-transclude is now deprecated. Use uib-tooltip-template-transclude instead.' ) ;
}
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 ) ;
}
} ;
} ] )
. directive ( 'tooltipClasses' , [ '$log' , '$tooltipSuppressWarning' , function ( $log , $tooltipSuppressWarning ) {
return {
restrict : 'A' ,
link : function ( scope , element , attrs ) {
if ( ! $tooltipSuppressWarning ) {
$log . warn ( 'tooltip-classes is now deprecated. Use uib-tooltip-classes instead.' ) ;
}
if ( scope . placement ) {
element . addClass ( scope . placement ) ;
}
if ( scope . popupClass ) {
element . addClass ( scope . popupClass ) ;
}
if ( scope . animation ( ) ) {
element . addClass ( attrs . tooltipAnimationClass ) ;
}
}
} ;
} ] )
. directive ( 'tooltipPopup' , [ '$log' , '$tooltipSuppressWarning' , function ( $log , $tooltipSuppressWarning ) {
return {
replace : true ,
scope : { content : '@' , placement : '@' , popupClass : '@' , animation : '&' , isOpen : '&' } ,
templateUrl : 'template/tooltip/tooltip-popup.html' ,
link : function ( scope , element ) {
if ( ! $tooltipSuppressWarning ) {
$log . warn ( 'tooltip-popup is now deprecated. Use uib-tooltip-popup instead.' ) ;
}
element . addClass ( 'tooltip' ) ;
}
} ;
} ] )
. directive ( 'tooltip' , [ '$tooltip' , function ( $tooltip ) {
return $tooltip ( 'tooltip' , 'tooltip' , 'mouseenter' ) ;
} ] )
. directive ( 'tooltipTemplatePopup' , [ '$log' , '$tooltipSuppressWarning' , function ( $log , $tooltipSuppressWarning ) {
return {
replace : true ,
scope : { contentExp : '&' , placement : '@' , popupClass : '@' , animation : '&' , isOpen : '&' ,
originScope : '&' } ,
templateUrl : 'template/tooltip/tooltip-template-popup.html' ,
link : function ( scope , element ) {
if ( ! $tooltipSuppressWarning ) {
$log . warn ( 'tooltip-template-popup is now deprecated. Use uib-tooltip-template-popup instead.' ) ;
}
element . addClass ( 'tooltip' ) ;
}
} ;
} ] )
. directive ( 'tooltipTemplate' , [ '$tooltip' , function ( $tooltip ) {
return $tooltip ( 'tooltipTemplate' , 'tooltip' , 'mouseenter' , {
useContentExp : true
} ) ;
} ] )
. directive ( 'tooltipHtmlPopup' , [ '$log' , '$tooltipSuppressWarning' , function ( $log , $tooltipSuppressWarning ) {
return {
replace : true ,
scope : { contentExp : '&' , placement : '@' , popupClass : '@' , animation : '&' , isOpen : '&' } ,
templateUrl : 'template/tooltip/tooltip-html-popup.html' ,
link : function ( scope , element ) {
if ( ! $tooltipSuppressWarning ) {
$log . warn ( 'tooltip-html-popup is now deprecated. Use uib-tooltip-html-popup instead.' ) ;
}
element . addClass ( 'tooltip' ) ;
}
} ;
} ] )
. directive ( 'tooltipHtml' , [ '$tooltip' , function ( $tooltip ) {
return $tooltip ( 'tooltipHtml' , 'tooltip' , 'mouseenter' , {
useContentExp : true
} ) ;
} ] ) ;
/ * *
* 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 , and selector delegatation .
* /
angular . module ( 'ui.bootstrap.popover' , [ 'ui.bootstrap.tooltip' ] )
. directive ( 'uibPopoverTemplatePopup' , function ( ) {
return {
replace : true ,
scope : { title : '@' , contentExp : '&' , placement : '@' , popupClass : '@' , animation : '&' , isOpen : '&' ,
originScope : '&' } ,
templateUrl : 'template/popover/popover-template.html' ,
link : function ( scope , element ) {
element . addClass ( 'popover' ) ;
}
} ;
} )
. directive ( 'uibPopoverTemplate' , [ '$uibTooltip' , function ( $uibTooltip ) {
return $uibTooltip ( 'uibPopoverTemplate' , 'popover' , 'click' , {
useContentExp : true
} ) ;
} ] )
. directive ( 'uibPopoverHtmlPopup' , function ( ) {
return {
replace : true ,
scope : { contentExp : '&' , title : '@' , placement : '@' , popupClass : '@' , animation : '&' , isOpen : '&' } ,
templateUrl : 'template/popover/popover-html.html' ,
link : function ( scope , element ) {
element . addClass ( 'popover' ) ;
}
} ;
} )
. directive ( 'uibPopoverHtml' , [ '$uibTooltip' , function ( $uibTooltip ) {
return $uibTooltip ( 'uibPopoverHtml' , 'popover' , 'click' , {
useContentExp : true
} ) ;
} ] )
. directive ( 'uibPopoverPopup' , function ( ) {
return {
replace : true ,
scope : { title : '@' , content : '@' , placement : '@' , popupClass : '@' , animation : '&' , isOpen : '&' } ,
templateUrl : 'template/popover/popover.html' ,
link : function ( scope , element ) {
element . addClass ( 'popover' ) ;
}
} ;
} )
. directive ( 'uibPopover' , [ '$uibTooltip' , function ( $uibTooltip ) {
return $uibTooltip ( 'uibPopover' , 'popover' , 'click' ) ;
} ] ) ;
/* Deprecated popover below */
angular . module ( 'ui.bootstrap.popover' )
. value ( '$popoverSuppressWarning' , false )
. directive ( 'popoverTemplatePopup' , [ '$log' , '$popoverSuppressWarning' , function ( $log , $popoverSuppressWarning ) {
return {
replace : true ,
scope : { title : '@' , contentExp : '&' , placement : '@' , popupClass : '@' , animation : '&' , isOpen : '&' ,
originScope : '&' } ,
templateUrl : 'template/popover/popover-template.html' ,
link : function ( scope , element ) {
if ( ! $popoverSuppressWarning ) {
$log . warn ( 'popover-template-popup is now deprecated. Use uib-popover-template-popup instead.' ) ;
}
element . addClass ( 'popover' ) ;
}
} ;
} ] )
. directive ( 'popoverTemplate' , [ '$tooltip' , function ( $tooltip ) {
return $tooltip ( 'popoverTemplate' , 'popover' , 'click' , {
useContentExp : true
} ) ;
} ] )
. directive ( 'popoverHtmlPopup' , [ '$log' , '$popoverSuppressWarning' , function ( $log , $popoverSuppressWarning ) {
return {
replace : true ,
scope : { contentExp : '&' , title : '@' , placement : '@' , popupClass : '@' , animation : '&' , isOpen : '&' } ,
templateUrl : 'template/popover/popover-html.html' ,
link : function ( scope , element ) {
if ( ! $popoverSuppressWarning ) {
$log . warn ( 'popover-html-popup is now deprecated. Use uib-popover-html-popup instead.' ) ;
}
element . addClass ( 'popover' ) ;
}
} ;
} ] )
. directive ( 'popoverHtml' , [ '$tooltip' , function ( $tooltip ) {
return $tooltip ( 'popoverHtml' , 'popover' , 'click' , {
useContentExp : true
} ) ;
} ] )
. directive ( 'popoverPopup' , [ '$log' , '$popoverSuppressWarning' , function ( $log , $popoverSuppressWarning ) {
return {
replace : true ,
scope : { title : '@' , content : '@' , placement : '@' , popupClass : '@' , animation : '&' , isOpen : '&' } ,
templateUrl : 'template/popover/popover.html' ,
link : function ( scope , element ) {
if ( ! $popoverSuppressWarning ) {
$log . warn ( 'popover-popup is now deprecated. Use uib-popover-popup instead.' ) ;
}
element . addClass ( 'popover' ) ;
}
} ;
} ] )
. directive ( 'popover' , [ '$tooltip' , function ( $tooltip ) {
return $tooltip ( 'popover' , 'popover' , 'click' ) ;
} ] ) ;
angular . module ( 'ui.bootstrap.progressbar' , [ ] )
. constant ( 'uibProgressConfig' , {
animate : true ,
max : 100
} )
. controller ( 'UibProgressController' , [ '$scope' , '$attrs' , 'uibProgressConfig' , function ( $scope , $attrs , progressConfig ) {
var self = this ,
animate = angular . isDefined ( $attrs . animate ) ? $scope . $parent . $eval ( $attrs . animate ) : progressConfig . animate ;
this . bars = [ ] ;
$scope . max = angular . isDefined ( $scope . max ) ? $scope . max : progressConfig . max ;
this . addBar = function ( bar , element , attrs ) {
if ( ! animate ) {
element . css ( { 'transition' : 'none' } ) ;
}
this . bars . push ( bar ) ;
bar . max = $scope . max ;
bar . title = attrs && angular . isDefined ( attrs . title ) ? attrs . title : 'progressbar' ;
bar . $watch ( 'value' , function ( value ) {
bar . recalculatePercentage ( ) ;
} ) ;
bar . recalculatePercentage = function ( ) {
var totalPercentage = self . bars . reduce ( function ( total , bar ) {
bar . percent = + ( 100 * bar . value / bar . max ) . toFixed ( 2 ) ;
return total + bar . percent ;
} , 0 ) ;
if ( totalPercentage > 100 ) {
bar . percent -= totalPercentage - 100 ;
}
} ;
bar . $on ( '$destroy' , function ( ) {
element = null ;
self . removeBar ( bar ) ;
} ) ;
} ;
this . removeBar = function ( bar ) {
this . bars . splice ( this . bars . indexOf ( bar ) , 1 ) ;
this . bars . forEach ( function ( bar ) {
bar . recalculatePercentage ( ) ;
} ) ;
} ;
$scope . $watch ( 'max' , function ( max ) {
self . bars . forEach ( function ( bar ) {
bar . max = $scope . max ;
bar . recalculatePercentage ( ) ;
} ) ;
} ) ;
} ] )
. directive ( 'uibProgress' , function ( ) {
return {
replace : true ,
transclude : true ,
controller : 'UibProgressController' ,
require : 'uibProgress' ,
scope : {
max : '=?'
} ,
templateUrl : 'template/progressbar/progress.html'
} ;
} )
. directive ( 'uibBar' , function ( ) {
return {
replace : true ,
transclude : true ,
require : '^uibProgress' ,
scope : {
value : '=' ,
type : '@'
} ,
templateUrl : 'template/progressbar/bar.html' ,
link : function ( scope , element , attrs , progressCtrl ) {
progressCtrl . addBar ( scope , element , attrs ) ;
}
} ;
} )
. directive ( 'uibProgressbar' , function ( ) {
return {
replace : true ,
transclude : true ,
controller : 'UibProgressController' ,
scope : {
value : '=' ,
max : '=?' ,
type : '@'
} ,
templateUrl : 'template/progressbar/progressbar.html' ,
link : function ( scope , element , attrs , progressCtrl ) {
progressCtrl . addBar ( scope , angular . element ( element . children ( ) [ 0 ] ) , { title : attrs . title } ) ;
}
} ;
} ) ;
/* Deprecated progressbar below */
angular . module ( 'ui.bootstrap.progressbar' )
. value ( '$progressSuppressWarning' , false )
. controller ( 'ProgressController' , [ '$scope' , '$attrs' , 'uibProgressConfig' , '$log' , '$progressSuppressWarning' , function ( $scope , $attrs , progressConfig , $log , $progressSuppressWarning ) {
if ( ! $progressSuppressWarning ) {
$log . warn ( 'ProgressController is now deprecated. Use UibProgressController instead.' ) ;
}
var self = this ,
animate = angular . isDefined ( $attrs . animate ) ? $scope . $parent . $eval ( $attrs . animate ) : progressConfig . animate ;
this . bars = [ ] ;
$scope . max = angular . isDefined ( $scope . max ) ? $scope . max : progressConfig . max ;
this . addBar = function ( bar , element , attrs ) {
if ( ! animate ) {
element . css ( { 'transition' : 'none' } ) ;
}
this . bars . push ( bar ) ;
bar . max = $scope . max ;
bar . title = attrs && angular . isDefined ( attrs . title ) ? attrs . title : 'progressbar' ;
bar . $watch ( 'value' , function ( value ) {
bar . recalculatePercentage ( ) ;
} ) ;
bar . recalculatePercentage = function ( ) {
bar . percent = + ( 100 * bar . value / bar . max ) . toFixed ( 2 ) ;
var totalPercentage = self . bars . reduce ( function ( total , bar ) {
return total + bar . percent ;
} , 0 ) ;
if ( totalPercentage > 100 ) {
bar . percent -= totalPercentage - 100 ;
}
} ;
bar . $on ( '$destroy' , function ( ) {
element = null ;
self . removeBar ( bar ) ;
} ) ;
} ;
this . removeBar = function ( bar ) {
this . bars . splice ( this . bars . indexOf ( bar ) , 1 ) ;
} ;
$scope . $watch ( 'max' , function ( max ) {
self . bars . forEach ( function ( bar ) {
bar . max = $scope . max ;
bar . recalculatePercentage ( ) ;
} ) ;
} ) ;
} ] )
. directive ( 'progress' , [ '$log' , '$progressSuppressWarning' , function ( $log , $progressSuppressWarning ) {
return {
replace : true ,
transclude : true ,
controller : 'ProgressController' ,
require : 'progress' ,
scope : {
max : '=?' ,
title : '@?'
} ,
templateUrl : 'template/progressbar/progress.html' ,
link : function ( ) {
if ( ! $progressSuppressWarning ) {
$log . warn ( 'progress is now deprecated. Use uib-progress instead.' ) ;
}
}
} ;
} ] )
. directive ( 'bar' , [ '$log' , '$progressSuppressWarning' , function ( $log , $progressSuppressWarning ) {
return {
replace : true ,
transclude : true ,
require : '^progress' ,
scope : {
value : '=' ,
type : '@'
} ,
templateUrl : 'template/progressbar/bar.html' ,
link : function ( scope , element , attrs , progressCtrl ) {
if ( ! $progressSuppressWarning ) {
$log . warn ( 'bar is now deprecated. Use uib-bar instead.' ) ;
}
progressCtrl . addBar ( scope , element ) ;
}
} ;
} ] )
. directive ( 'progressbar' , [ '$log' , '$progressSuppressWarning' , function ( $log , $progressSuppressWarning ) {
return {
replace : true ,
transclude : true ,
controller : 'ProgressController' ,
scope : {
value : '=' ,
max : '=?' ,
type : '@'
} ,
templateUrl : 'template/progressbar/progressbar.html' ,
link : function ( scope , element , attrs , progressCtrl ) {
if ( ! $progressSuppressWarning ) {
$log . warn ( 'progressbar is now deprecated. Use uib-progressbar instead.' ) ;
}
progressCtrl . addBar ( scope , angular . element ( element . children ( ) [ 0 ] ) , { title : attrs . title } ) ;
}
} ;
} ] ) ;
angular . module ( 'ui.bootstrap.rating' , [ ] )
. constant ( 'uibRatingConfig' , {
max : 5 ,
stateOn : null ,
stateOff : null ,
titles : [ 'one' , 'two' , 'three' , 'four' , 'five' ]
} )
. controller ( 'UibRatingController' , [ '$scope' , '$attrs' , 'uibRatingConfig' , function ( $scope , $attrs , ratingConfig ) {
var ngModelCtrl = { $setViewValue : angular . noop } ;
this . init = function ( ngModelCtrl _ ) {
ngModelCtrl = ngModelCtrl _ ;
ngModelCtrl . $render = this . render ;
ngModelCtrl . $formatters . push ( function ( value ) {
if ( angular . isNumber ( value ) && value << 0 !== value ) {
value = Math . round ( value ) ;
}
return value ;
} ) ;
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 ;
var tmpTitles = angular . isDefined ( $attrs . titles ) ? $scope . $parent . $eval ( $attrs . titles ) : ratingConfig . titles ;
this . titles = angular . isArray ( tmpTitles ) && tmpTitles . length > 0 ?
tmpTitles : ratingConfig . titles ;
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 ) {
for ( var i = 0 , n = states . length ; i < n ; i ++ ) {
states [ i ] = angular . extend ( { index : i } , { stateOn : this . stateOn , stateOff : this . stateOff , title : this . getTitle ( i ) } , states [ i ] ) ;
}
return states ;
} ;
this . getTitle = function ( index ) {
if ( index >= this . titles . length ) {
return index + 1 ;
} else {
return this . titles [ index ] ;
}
} ;
$scope . rate = function ( value ) {
if ( ! $scope . readonly && value >= 0 && value <= $scope . range . length ) {
ngModelCtrl . $setViewValue ( ngModelCtrl . $viewValue === value ? 0 : value ) ;
ngModelCtrl . $render ( ) ;
}
} ;
$scope . enter = function ( value ) {
if ( ! $scope . readonly ) {
$scope . value = value ;
}
$scope . onHover ( { value : value } ) ;
} ;
$scope . reset = function ( ) {
$scope . value = ngModelCtrl . $viewValue ;
$scope . onLeave ( ) ;
} ;
$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 ) ) ;
}
} ;
this . render = function ( ) {
$scope . value = ngModelCtrl . $viewValue ;
} ;
} ] )
. directive ( 'uibRating' , function ( ) {
return {
require : [ 'uibRating' , 'ngModel' ] ,
scope : {
readonly : '=?' ,
onHover : '&' ,
onLeave : '&'
} ,
controller : 'UibRatingController' ,
templateUrl : 'template/rating/rating.html' ,
replace : true ,
link : function ( scope , element , attrs , ctrls ) {
var ratingCtrl = ctrls [ 0 ] , ngModelCtrl = ctrls [ 1 ] ;
ratingCtrl . init ( ngModelCtrl ) ;
}
} ;
} ) ;
/* Deprecated rating below */
angular . module ( 'ui.bootstrap.rating' )
. value ( '$ratingSuppressWarning' , false )
. controller ( 'RatingController' , [ '$scope' , '$attrs' , '$controller' , '$log' , '$ratingSuppressWarning' , function ( $scope , $attrs , $controller , $log , $ratingSuppressWarning ) {
if ( ! $ratingSuppressWarning ) {
$log . warn ( 'RatingController is now deprecated. Use UibRatingController instead.' ) ;
}
angular . extend ( this , $controller ( 'UibRatingController' , {
$scope : $scope ,
$attrs : $attrs
} ) ) ;
} ] )
. directive ( 'rating' , [ '$log' , '$ratingSuppressWarning' , function ( $log , $ratingSuppressWarning ) {
return {
require : [ 'rating' , 'ngModel' ] ,
scope : {
readonly : '=?' ,
onHover : '&' ,
onLeave : '&'
} ,
controller : 'RatingController' ,
templateUrl : 'template/rating/rating.html' ,
replace : true ,
link : function ( scope , element , attrs , ctrls ) {
if ( ! $ratingSuppressWarning ) {
$log . warn ( 'rating is now deprecated. Use uib-rating instead.' ) ;
}
var ratingCtrl = ctrls [ 0 ] , ngModelCtrl = ctrls [ 1 ] ;
ratingCtrl . init ( ngModelCtrl ) ;
}
} ;
} ] ) ;
/ * *
* @ ngdoc overview
* @ name ui . bootstrap . tabs
*
* @ description
* AngularJS version of the tabs directive .
* /
angular . module ( 'ui.bootstrap.tabs' , [ ] )
. controller ( 'UibTabsetController' , [ '$scope' , function ( $scope ) {
var ctrl = this ,
tabs = ctrl . tabs = $scope . tabs = [ ] ;
ctrl . select = function ( selectedTab ) {
angular . forEach ( tabs , function ( tab ) {
if ( tab . active && tab !== selectedTab ) {
tab . active = false ;
tab . onDeselect ( ) ;
selectedTab . selectCalled = false ;
}
} ) ;
selectedTab . active = true ;
// only call select if it has not already been called
if ( ! selectedTab . selectCalled ) {
selectedTab . onSelect ( ) ;
selectedTab . selectCalled = true ;
}
} ;
ctrl . addTab = function addTab ( tab ) {
tabs . push ( tab ) ;
// we can't run the select function on the first tab
// since that would select it twice
if ( tabs . length === 1 && tab . active !== false ) {
tab . active = true ;
} else if ( tab . active ) {
ctrl . select ( tab ) ;
} else {
tab . active = false ;
}
} ;
ctrl . removeTab = function removeTab ( tab ) {
var index = tabs . indexOf ( tab ) ;
//Select a new tab if the tab to be removed is selected and not destroyed
if ( tab . active && tabs . length > 1 && ! destroyed ) {
//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 ) ;
} ;
var destroyed ;
$scope . $on ( '$destroy' , function ( ) {
destroyed = true ;
} ) ;
} ] )
/ * *
* @ 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 .
* @ param { boolean = } justified Whether or not to use justified styling for the tabs .
*
* @ example
< example module = "ui.bootstrap" >
< file name = "index.html" >
< uib - tabset >
< uib - tab heading = "Tab 1" > < b > First < / b > C o n t e n t ! < / u i b - t a b >
< uib - tab heading = "Tab 2" > < i > Second < / i > C o n t e n t ! < / u i b - t a b >
< / u i b - t a b s e t >
< hr / >
< uib - tabset vertical = "true" >
< uib - tab heading = "Vertical Tab 1" > < b > First < / b > V e r t i c a l C o n t e n t ! < / u i b - t a b >
< uib - tab heading = "Vertical Tab 2" > < i > Second < / i > V e r t i c a l C o n t e n t ! < / u i b - t a b >
< / u i b - t a b s e t >
< uib - tabset justified = "true" >
< uib - tab heading = "Justified Tab 1" > < b > First < / b > J u s t i f i e d C o n t e n t ! < / u i b - t a b >
< uib - tab heading = "Justified Tab 2" > < i > Second < / i > J u s t i f i e d C o n t e n t ! < / u i b - t a b >
< / u i b - t a b s e t >
< / f i l e >
< / e x a m p l e >
* /
. directive ( 'uibTabset' , function ( ) {
return {
restrict : 'EA' ,
transclude : true ,
replace : true ,
scope : {
type : '@'
} ,
controller : 'UibTabsetController' ,
templateUrl : 'template/tabs/tabset.html' ,
link : function ( scope , element , attrs ) {
scope . vertical = angular . isDefined ( attrs . vertical ) ? scope . $parent . $eval ( attrs . vertical ) : false ;
scope . justified = angular . isDefined ( attrs . justified ) ? scope . $parent . $eval ( attrs . justified ) : false ;
}
} ;
} )
/ * *
* @ 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 / >
< uib - tabset >
< uib - tab heading = "Tab 1" > First Tab < / u i b - t a b >
< uib - tab select = "alertMe()" >
< uib - 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 !
< / u i b - t a b >
< uib - tab ng - repeat = "item in items"
heading = "{{item.title}}"
disabled = "item.disabled"
active = "item.active" >
{ { item . content } }
< / u i b - t a b >
< / u i 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" >
< uib - tabset >
< uib - tab >
< uib - 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 !
< / u i b - t a b >
< uib - tab >
< uib - 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 .
< / u i b - t a b >
< / u i b - t a b s e t >
< / f i l e >
< / e x a m p l e >
* /
. directive ( 'uibTab' , [ '$parse' , function ( $parse ) {
return {
require : '^uibTabset' ,
restrict : 'EA' ,
replace : true ,
templateUrl : 'template/tabs/tab.html' ,
transclude : true ,
scope : {
active : '=?' ,
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
} ,
link : function ( scope , elm , attrs , tabsetCtrl , transclude ) {
scope . $watch ( 'active' , function ( active ) {
if ( active ) {
tabsetCtrl . select ( scope ) ;
2015-05-05 03:10:25 +02:00
}
2016-03-23 18:39:41 +01:00
} ) ;
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
scope . disabled = false ;
if ( attrs . disable ) {
scope . $parent . $watch ( $parse ( attrs . disable ) , function ( value ) {
scope . disabled = ! ! value ;
} ) ;
2015-05-05 03:10:25 +02:00
}
2016-03-23 18:39:41 +01:00
scope . select = function ( ) {
if ( ! scope . disabled ) {
scope . active = true ;
2015-05-05 03:10:25 +02:00
}
} ;
2016-03-23 18:39:41 +01:00
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 ;
2015-05-05 03:10:25 +02:00
}
} ;
} ] )
2016-03-23 18:39:41 +01:00
. directive ( 'uibTabHeadingTransclude' , function ( ) {
return {
restrict : 'A' ,
require : [ '?^uibTab' , '?^tab' ] , // TODO: change to '^uibTab' after deprecation removal
link : function ( scope , elm ) {
scope . $watch ( 'headingElement' , function updateHeadingElement ( heading ) {
if ( heading ) {
elm . html ( '' ) ;
elm . append ( heading ) ;
}
} ) ;
}
} ;
2015-05-05 03:10:25 +02:00
} )
2016-03-23 18:39:41 +01:00
. directive ( 'uibTabContentTransclude' , function ( ) {
2015-05-05 03:10:25 +02:00
return {
2016-03-23 18:39:41 +01:00
restrict : 'A' ,
require : [ '?^uibTabset' , '?^tabset' ] , // TODO: change to '^uibTabset' after deprecation removal
link : function ( scope , elm , attrs ) {
var tab = scope . $eval ( attrs . uibTabContentTransclude ) ;
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
//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 ) ;
}
} ) ;
} ) ;
2015-05-05 03:10:25 +02:00
}
} ;
2016-03-23 18:39:41 +01:00
function isTabHeading ( node ) {
return node . tagName && (
node . hasAttribute ( 'tab-heading' ) || // TODO: remove after deprecation removal
node . hasAttribute ( 'data-tab-heading' ) || // TODO: remove after deprecation removal
node . hasAttribute ( 'x-tab-heading' ) || // TODO: remove after deprecation removal
node . hasAttribute ( 'uib-tab-heading' ) ||
node . hasAttribute ( 'data-uib-tab-heading' ) ||
node . hasAttribute ( 'x-uib-tab-heading' ) ||
node . tagName . toLowerCase ( ) === 'tab-heading' || // TODO: remove after deprecation removal
node . tagName . toLowerCase ( ) === 'data-tab-heading' || // TODO: remove after deprecation removal
node . tagName . toLowerCase ( ) === 'x-tab-heading' || // TODO: remove after deprecation removal
node . tagName . toLowerCase ( ) === 'uib-tab-heading' ||
node . tagName . toLowerCase ( ) === 'data-uib-tab-heading' ||
node . tagName . toLowerCase ( ) === 'x-uib-tab-heading'
) ;
}
} ) ;
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
/* deprecated tabs below */
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
angular . module ( 'ui.bootstrap.tabs' )
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
. value ( '$tabsSuppressWarning' , false )
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
. controller ( 'TabsetController' , [ '$scope' , '$controller' , '$log' , '$tabsSuppressWarning' , function ( $scope , $controller , $log , $tabsSuppressWarning ) {
if ( ! $tabsSuppressWarning ) {
$log . warn ( 'TabsetController is now deprecated. Use UibTabsetController instead.' ) ;
}
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
angular . extend ( this , $controller ( 'UibTabsetController' , {
$scope : $scope
} ) ) ;
} ] )
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
. directive ( 'tabset' , [ '$log' , '$tabsSuppressWarning' , function ( $log , $tabsSuppressWarning ) {
return {
restrict : 'EA' ,
transclude : true ,
replace : true ,
scope : {
type : '@'
} ,
controller : 'TabsetController' ,
templateUrl : 'template/tabs/tabset.html' ,
link : function ( scope , element , attrs ) {
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
if ( ! $tabsSuppressWarning ) {
$log . warn ( 'tabset is now deprecated. Use uib-tabset instead.' ) ;
}
scope . vertical = angular . isDefined ( attrs . vertical ) ? scope . $parent . $eval ( attrs . vertical ) : false ;
scope . justified = angular . isDefined ( attrs . justified ) ? scope . $parent . $eval ( attrs . justified ) : false ;
2015-05-05 03:10:25 +02:00
}
2016-03-23 18:39:41 +01:00
} ;
} ] )
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
. directive ( 'tab' , [ '$parse' , '$log' , '$tabsSuppressWarning' , function ( $parse , $log , $tabsSuppressWarning ) {
return {
require : '^tabset' ,
restrict : 'EA' ,
replace : true ,
templateUrl : 'template/tabs/tab.html' ,
transclude : true ,
scope : {
active : '=?' ,
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
} ,
link : function ( scope , elm , attrs , tabsetCtrl , transclude ) {
if ( ! $tabsSuppressWarning ) {
$log . warn ( 'tab is now deprecated. Use uib-tab instead.' ) ;
}
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
scope . $watch ( 'active' , function ( active ) {
if ( active ) {
tabsetCtrl . select ( scope ) ;
}
} ) ;
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
scope . disabled = false ;
if ( attrs . disable ) {
scope . $parent . $watch ( $parse ( attrs . disable ) , function ( value ) {
scope . disabled = ! ! value ;
} ) ;
}
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
scope . select = function ( ) {
if ( ! scope . disabled ) {
scope . active = true ;
}
} ;
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
tabsetCtrl . addTab ( scope ) ;
scope . $on ( '$destroy' , function ( ) {
tabsetCtrl . removeTab ( scope ) ;
} ) ;
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
//We need to transclude later, once the content container is ready.
//when this link happens, we're inside a tab heading.
scope . $transcludeFn = transclude ;
}
} ;
} ] )
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
. directive ( 'tabHeadingTransclude' , [ '$log' , '$tabsSuppressWarning' , function ( $log , $tabsSuppressWarning ) {
return {
restrict : 'A' ,
require : '^tab' ,
link : function ( scope , elm ) {
if ( ! $tabsSuppressWarning ) {
$log . warn ( 'tab-heading-transclude is now deprecated. Use uib-tab-heading-transclude instead.' ) ;
}
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
scope . $watch ( 'headingElement' , function updateHeadingElement ( heading ) {
if ( heading ) {
elm . html ( '' ) ;
elm . append ( heading ) ;
}
} ) ;
}
} ;
} ] )
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
. directive ( 'tabContentTransclude' , [ '$log' , '$tabsSuppressWarning' , function ( $log , $tabsSuppressWarning ) {
return {
restrict : 'A' ,
require : '^tabset' ,
link : function ( scope , elm , attrs ) {
if ( ! $tabsSuppressWarning ) {
$log . warn ( 'tab-content-transclude is now deprecated. Use uib-tab-content-transclude instead.' ) ;
}
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
var tab = scope . $eval ( attrs . tabContentTransclude ) ;
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
//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 ;
2015-05-05 03:10:25 +02:00
}
2016-03-23 18:39:41 +01:00
else {
elm . append ( node ) ;
2015-05-05 03:10:25 +02:00
}
2016-03-23 18:39:41 +01:00
} ) ;
} ) ;
}
} ;
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
function isTabHeading ( node ) {
return node . tagName && (
node . hasAttribute ( 'tab-heading' ) ||
node . hasAttribute ( 'data-tab-heading' ) ||
node . hasAttribute ( 'x-tab-heading' ) ||
node . tagName . toLowerCase ( ) === 'tab-heading' ||
node . tagName . toLowerCase ( ) === 'data-tab-heading' ||
node . tagName . toLowerCase ( ) === 'x-tab-heading'
) ;
}
} ] ) ;
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
angular . module ( 'ui.bootstrap.timepicker' , [ ] )
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
. constant ( 'uibTimepickerConfig' , {
hourStep : 1 ,
minuteStep : 1 ,
showMeridian : true ,
meridians : null ,
readonlyInput : false ,
mousewheel : true ,
arrowkeys : true ,
showSpinners : true
} )
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
. controller ( 'UibTimepickerController' , [ '$scope' , '$element' , '$attrs' , '$parse' , '$log' , '$locale' , 'uibTimepickerConfig' , function ( $scope , $element , $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 ;
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
$scope . tabindex = angular . isDefined ( $attrs . tabindex ) ? $attrs . tabindex : 0 ;
$element . removeAttr ( 'tabindex' ) ;
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
this . init = function ( ngModelCtrl _ , inputs ) {
ngModelCtrl = ngModelCtrl _ ;
ngModelCtrl . $render = this . render ;
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
ngModelCtrl . $formatters . unshift ( function ( modelValue ) {
return modelValue ? new Date ( modelValue ) : null ;
} ) ;
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
var hoursInputEl = inputs . eq ( 0 ) ,
minutesInputEl = inputs . eq ( 1 ) ;
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
var mousewheel = angular . isDefined ( $attrs . mousewheel ) ? $scope . $parent . $eval ( $attrs . mousewheel ) : timepickerConfig . mousewheel ;
if ( mousewheel ) {
this . setupMousewheelEvents ( hoursInputEl , minutesInputEl ) ;
}
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
var arrowkeys = angular . isDefined ( $attrs . arrowkeys ) ? $scope . $parent . $eval ( $attrs . arrowkeys ) : timepickerConfig . arrowkeys ;
if ( arrowkeys ) {
this . setupArrowkeyEvents ( hoursInputEl , minutesInputEl ) ;
}
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
$scope . readonlyInput = angular . isDefined ( $attrs . readonlyInput ) ? $scope . $parent . $eval ( $attrs . readonlyInput ) : timepickerConfig . readonlyInput ;
this . setupInputEvents ( hoursInputEl , minutesInputEl ) ;
} ;
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
var hourStep = timepickerConfig . hourStep ;
if ( $attrs . hourStep ) {
$scope . $parent . $watch ( $parse ( $attrs . hourStep ) , function ( value ) {
hourStep = parseInt ( value , 10 ) ;
} ) ;
}
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
var minuteStep = timepickerConfig . minuteStep ;
if ( $attrs . minuteStep ) {
$scope . $parent . $watch ( $parse ( $attrs . minuteStep ) , function ( value ) {
minuteStep = parseInt ( value , 10 ) ;
} ) ;
}
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
var min ;
$scope . $parent . $watch ( $parse ( $attrs . min ) , function ( value ) {
var dt = new Date ( value ) ;
min = isNaN ( dt ) ? undefined : dt ;
} ) ;
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
var max ;
$scope . $parent . $watch ( $parse ( $attrs . max ) , function ( value ) {
var dt = new Date ( value ) ;
max = isNaN ( dt ) ? undefined : dt ;
} ) ;
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
$scope . noIncrementHours = function ( ) {
var incrementedSelected = addMinutes ( selected , hourStep * 60 ) ;
return incrementedSelected > max ||
( incrementedSelected < selected && incrementedSelected < min ) ;
} ;
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
$scope . noDecrementHours = function ( ) {
var decrementedSelected = addMinutes ( selected , - hourStep * 60 ) ;
return decrementedSelected < min ||
( decrementedSelected > selected && decrementedSelected > max ) ;
} ;
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
$scope . noIncrementMinutes = function ( ) {
var incrementedSelected = addMinutes ( selected , minuteStep ) ;
return incrementedSelected > max ||
( incrementedSelected < selected && incrementedSelected < min ) ;
} ;
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
$scope . noDecrementMinutes = function ( ) {
var decrementedSelected = addMinutes ( selected , - minuteStep ) ;
return decrementedSelected < min ||
( decrementedSelected > selected && decrementedSelected > max ) ;
} ;
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
$scope . noToggleMeridian = function ( ) {
if ( selected . getHours ( ) < 13 ) {
return addMinutes ( selected , 12 * 60 ) > max ;
} else {
return addMinutes ( selected , - 12 * 60 ) < min ;
}
} ;
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
// 12H / 24H mode
$scope . showMeridian = timepickerConfig . showMeridian ;
if ( $attrs . showMeridian ) {
$scope . $parent . $watch ( $parse ( $attrs . showMeridian ) , function ( value ) {
$scope . showMeridian = ! ! value ;
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
if ( ngModelCtrl . $error . time ) {
// Evaluate from template
var hours = getHoursFromTemplate ( ) , minutes = getMinutesFromTemplate ( ) ;
if ( angular . isDefined ( hours ) && angular . isDefined ( minutes ) ) {
selected . setHours ( hours ) ;
refresh ( ) ;
}
} else {
updateTemplate ( ) ;
}
} ) ;
}
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +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 ;
}
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
if ( $scope . showMeridian ) {
if ( hours === 12 ) {
hours = 0 ;
}
if ( $scope . meridian === meridians [ 1 ] ) {
hours = hours + 12 ;
}
}
return hours ;
}
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
function getMinutesFromTemplate ( ) {
var minutes = parseInt ( $scope . minutes , 10 ) ;
return ( minutes >= 0 && minutes < 60 ) ? minutes : undefined ;
}
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
function pad ( value ) {
return ( angular . isDefined ( value ) && value . toString ( ) . length < 2 ) ? '0' + value : value . toString ( ) ;
}
// 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 ) ;
2015-05-05 03:10:25 +02:00
} ;
2016-03-23 18:39:41 +01:00
hoursInputEl . bind ( 'mousewheel wheel' , function ( e ) {
$scope . $apply ( isScrollingUp ( e ) ? $scope . incrementHours ( ) : $scope . decrementHours ( ) ) ;
e . preventDefault ( ) ;
} ) ;
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
minutesInputEl . bind ( 'mousewheel wheel' , function ( e ) {
$scope . $apply ( isScrollingUp ( e ) ? $scope . incrementMinutes ( ) : $scope . decrementMinutes ( ) ) ;
e . preventDefault ( ) ;
} ) ;
2015-05-05 03:10:25 +02:00
} ;
2016-03-23 18:39:41 +01: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 ( ) ;
}
} ) ;
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
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-05-05 03:10:25 +02:00
} ;
2016-03-23 18:39:41 +01:00
this . setupInputEvents = function ( hoursInputEl , minutesInputEl ) {
if ( $scope . readonlyInput ) {
$scope . updateHours = angular . noop ;
$scope . updateMinutes = angular . noop ;
return ;
}
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +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 ;
}
} ;
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
$scope . updateHours = function ( ) {
var hours = getHoursFromTemplate ( ) ,
minutes = getMinutesFromTemplate ( ) ;
if ( angular . isDefined ( hours ) && angular . isDefined ( minutes ) ) {
selected . setHours ( hours ) ;
if ( selected < min || selected > max ) {
invalidate ( true ) ;
} else {
refresh ( 'h' ) ;
}
} else {
invalidate ( true ) ;
}
} ;
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
hoursInputEl . bind ( 'blur' , function ( e ) {
if ( ! $scope . invalidHours && $scope . hours < 10 ) {
$scope . $apply ( function ( ) {
$scope . hours = pad ( $scope . hours ) ;
} ) ;
}
} ) ;
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
$scope . updateMinutes = function ( ) {
var minutes = getMinutesFromTemplate ( ) ,
hours = getHoursFromTemplate ( ) ;
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
if ( angular . isDefined ( minutes ) && angular . isDefined ( hours ) ) {
selected . setMinutes ( minutes ) ;
if ( selected < min || selected > max ) {
invalidate ( undefined , true ) ;
} else {
refresh ( 'm' ) ;
2015-05-05 03:10:25 +02:00
}
2016-03-23 18:39:41 +01:00
} else {
invalidate ( undefined , true ) ;
}
} ;
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
minutesInputEl . bind ( 'blur' , function ( e ) {
if ( ! $scope . invalidMinutes && $scope . minutes < 10 ) {
$scope . $apply ( function ( ) {
$scope . minutes = pad ( $scope . minutes ) ;
2015-05-05 03:10:25 +02:00
} ) ;
2016-03-23 18:39:41 +01:00
}
} ) ;
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
} ;
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
this . render = function ( ) {
var date = ngModelCtrl . $viewValue ;
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +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 ;
}
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
if ( selected < min || selected > max ) {
ngModelCtrl . $setValidity ( 'time' , false ) ;
$scope . invalidHours = true ;
$scope . invalidMinutes = true ;
} else {
makeValid ( ) ;
}
updateTemplate ( ) ;
}
} ;
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
// Call internally when we know that model is valid.
function refresh ( keyboardChange ) {
makeValid ( ) ;
ngModelCtrl . $setViewValue ( new Date ( selected ) ) ;
updateTemplate ( keyboardChange ) ;
}
function makeValid ( ) {
ngModelCtrl . $setValidity ( 'time' , true ) ;
$scope . invalidHours = false ;
$scope . invalidMinutes = false ;
}
function updateTemplate ( keyboardChange ) {
var hours = selected . getHours ( ) , minutes = selected . getMinutes ( ) ;
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
if ( $scope . showMeridian ) {
hours = ( hours === 0 || hours === 12 ) ? 12 : hours % 12 ; // Convert 24 to 12 hour system
}
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
$scope . hours = keyboardChange === 'h' ? hours : pad ( hours ) ;
if ( keyboardChange !== 'm' ) {
$scope . minutes = pad ( minutes ) ;
}
$scope . meridian = selected . getHours ( ) < 12 ? meridians [ 0 ] : meridians [ 1 ] ;
}
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
function addMinutes ( date , minutes ) {
var dt = new Date ( date . getTime ( ) + minutes * 60000 ) ;
var newDate = new Date ( date ) ;
newDate . setHours ( dt . getHours ( ) , dt . getMinutes ( ) ) ;
return newDate ;
}
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
function addMinutesToSelected ( minutes ) {
selected = addMinutes ( selected , minutes ) ;
refresh ( ) ;
}
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
$scope . showSpinners = angular . isDefined ( $attrs . showSpinners ) ?
$scope . $parent . $eval ( $attrs . showSpinners ) : timepickerConfig . showSpinners ;
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
$scope . incrementHours = function ( ) {
if ( ! $scope . noIncrementHours ( ) ) {
addMinutesToSelected ( hourStep * 60 ) ;
2015-05-05 03:10:25 +02:00
}
} ;
2016-03-23 18:39:41 +01:00
$scope . decrementHours = function ( ) {
if ( ! $scope . noDecrementHours ( ) ) {
addMinutesToSelected ( - hourStep * 60 ) ;
2015-05-05 03:10:25 +02:00
}
} ;
2016-03-23 18:39:41 +01:00
$scope . incrementMinutes = function ( ) {
if ( ! $scope . noIncrementMinutes ( ) ) {
addMinutesToSelected ( minuteStep ) ;
2015-05-05 03:10:25 +02:00
}
} ;
2016-03-23 18:39:41 +01:00
$scope . decrementMinutes = function ( ) {
if ( ! $scope . noDecrementMinutes ( ) ) {
addMinutesToSelected ( - minuteStep ) ;
2015-05-05 03:10:25 +02:00
}
} ;
2016-03-23 18:39:41 +01:00
$scope . toggleMeridian = function ( ) {
if ( ! $scope . noToggleMeridian ( ) ) {
addMinutesToSelected ( 12 * 60 * ( selected . getHours ( ) < 12 ? 1 : - 1 ) ) ;
}
2015-05-05 03:10:25 +02:00
} ;
} ] )
2016-03-23 18:39:41 +01:00
. directive ( 'uibTimepicker' , function ( ) {
2015-05-05 03:10:25 +02:00
return {
restrict : 'EA' ,
2016-03-23 18:39:41 +01:00
require : [ 'uibTimepicker' , '?^ngModel' ] ,
controller : 'UibTimepickerController' ,
controllerAs : 'timepicker' ,
2015-05-05 03:10:25 +02:00
replace : true ,
2016-03-23 18:39:41 +01:00
scope : { } ,
templateUrl : function ( element , attrs ) {
return attrs . templateUrl || 'template/timepicker/timepicker.html' ;
} ,
2015-05-05 03:10:25 +02:00
link : function ( scope , element , attrs , ctrls ) {
2016-03-23 18:39:41 +01:00
var timepickerCtrl = ctrls [ 0 ] , ngModelCtrl = ctrls [ 1 ] ;
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
if ( ngModelCtrl ) {
timepickerCtrl . init ( ngModelCtrl , element . find ( 'input' ) ) ;
2015-05-05 03:10:25 +02:00
}
}
} ;
} ) ;
2016-03-23 18:39:41 +01:00
/* Deprecated timepicker below */
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
angular . module ( 'ui.bootstrap.timepicker' )
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
. value ( '$timepickerSuppressWarning' , false )
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
. controller ( 'TimepickerController' , [ '$scope' , '$element' , '$attrs' , '$controller' , '$log' , '$timepickerSuppressWarning' , function ( $scope , $element , $attrs , $controller , $log , $timepickerSuppressWarning ) {
if ( ! $timepickerSuppressWarning ) {
$log . warn ( 'TimepickerController is now deprecated. Use UibTimepickerController instead.' ) ;
}
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
angular . extend ( this , $controller ( 'UibTimepickerController' , {
$scope : $scope ,
$element : $element ,
$attrs : $attrs
} ) ) ;
2015-05-05 03:10:25 +02:00
} ] )
2016-03-23 18:39:41 +01:00
. directive ( 'timepicker' , [ '$log' , '$timepickerSuppressWarning' , function ( $log , $timepickerSuppressWarning ) {
2015-05-05 03:10:25 +02:00
return {
restrict : 'EA' ,
2016-03-23 18:39:41 +01:00
require : [ 'timepicker' , '?^ngModel' ] ,
controller : 'TimepickerController' ,
controllerAs : 'timepicker' ,
2015-05-05 03:10:25 +02:00
replace : true ,
2016-03-23 18:39:41 +01:00
scope : { } ,
templateUrl : function ( element , attrs ) {
return attrs . templateUrl || 'template/timepicker/timepicker.html' ;
2015-05-05 03:10:25 +02:00
} ,
2016-03-23 18:39:41 +01:00
link : function ( scope , element , attrs , ctrls ) {
if ( ! $timepickerSuppressWarning ) {
$log . warn ( 'timepicker is now deprecated. Use uib-timepicker instead.' ) ;
}
var timepickerCtrl = ctrls [ 0 ] , ngModelCtrl = ctrls [ 1 ] ;
if ( ngModelCtrl ) {
timepickerCtrl . init ( ngModelCtrl , element . find ( 'input' ) ) ;
}
2015-05-05 03:10:25 +02:00
}
} ;
2016-03-23 18:39:41 +01:00
} ] ) ;
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
angular . module ( 'ui.bootstrap.typeahead' , [ 'ui.bootstrap.position' ] )
2015-05-05 03:10:25 +02:00
/ * *
2016-03-23 18:39:41 +01:00
* A helper service that can parse typeahead ' s syntax ( string provided by users )
* Extracted to a separate service for ease of unit testing
2015-05-05 03:10:25 +02:00
* /
2016-03-23 18:39:41 +01:00
. factory ( 'uibTypeaheadParser' , [ '$parse' , function ( $parse ) {
// 00000111000000000000022200000000000000003333333333333330000000000044000
var TYPEAHEAD _REGEXP = /^\s*([\s\S]+?)(?:\s+as\s+([\s\S]+?))?\s+for\s+(?:([\$\w][\$\w\d]*))\s+in\s+([\s\S]+?)$/ ;
return {
parse : function ( input ) {
var match = input . match ( TYPEAHEAD _REGEXP ) ;
if ( ! match ) {
throw new Error (
'Expected typeahead specification in form of "_modelValue_ (as _label_)? for _item_ in _collection_"' +
' but got "' + input + '".' ) ;
2015-05-05 03:10:25 +02:00
}
2016-03-23 18:39:41 +01:00
return {
itemName : match [ 3 ] ,
source : $parse ( match [ 4 ] ) ,
viewMapper : $parse ( match [ 2 ] || match [ 1 ] ) ,
modelMapper : $parse ( match [ 1 ] )
2015-05-05 03:10:25 +02:00
} ;
2016-03-23 18:39:41 +01:00
}
} ;
} ] )
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
. controller ( 'UibTypeaheadController' , [ '$scope' , '$element' , '$attrs' , '$compile' , '$parse' , '$q' , '$timeout' , '$document' , '$window' , '$rootScope' , '$uibPosition' , 'uibTypeaheadParser' ,
function ( originalScope , element , attrs , $compile , $parse , $q , $timeout , $document , $window , $rootScope , $position , typeaheadParser ) {
var HOT _KEYS = [ 9 , 13 , 27 , 38 , 40 ] ;
var eventDebounceTime = 200 ;
var modelCtrl , ngModelOptions ;
//SUPPORTED ATTRIBUTES (OPTIONS)
//minimal no of characters that needs to be entered before typeahead kicks-in
var minLength = originalScope . $eval ( attrs . typeaheadMinLength ) ;
if ( ! minLength && minLength !== 0 ) {
minLength = 1 ;
2015-05-05 03:10:25 +02:00
}
2016-03-23 18:39:41 +01:00
//minimal wait time after last character typed before typeahead kicks-in
var waitTime = originalScope . $eval ( attrs . typeaheadWaitMs ) || 0 ;
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
//should it restrict model values to the ones selected from the popup only?
var isEditable = originalScope . $eval ( attrs . typeaheadEditable ) !== false ;
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
//binding to a variable that indicates if matches are being retrieved asynchronously
var isLoadingSetter = $parse ( attrs . typeaheadLoading ) . assign || angular . noop ;
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
//a callback executed when a match is selected
var onSelectCallback = $parse ( attrs . typeaheadOnSelect ) ;
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
//should it select highlighted popup value when losing focus?
var isSelectOnBlur = angular . isDefined ( attrs . typeaheadSelectOnBlur ) ? originalScope . $eval ( attrs . typeaheadSelectOnBlur ) : false ;
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
//binding to a variable that indicates if there were no results after the query is completed
var isNoResultsSetter = $parse ( attrs . typeaheadNoResults ) . assign || angular . noop ;
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
var inputFormatter = attrs . typeaheadInputFormatter ? $parse ( attrs . typeaheadInputFormatter ) : undefined ;
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
var appendToBody = attrs . typeaheadAppendToBody ? originalScope . $eval ( attrs . typeaheadAppendToBody ) : false ;
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
var appendToElementId = attrs . typeaheadAppendToElementId || false ;
var focusFirst = originalScope . $eval ( attrs . typeaheadFocusFirst ) !== false ;
//If input matches an item of the list exactly, select it automatically
var selectOnExact = attrs . typeaheadSelectOnExact ? originalScope . $eval ( attrs . typeaheadSelectOnExact ) : false ;
//INTERNAL VARIABLES
//model setter executed upon match selection
var parsedModel = $parse ( attrs . ngModel ) ;
var invokeModelSetter = $parse ( attrs . ngModel + '($$$p)' ) ;
var $setModelValue = function ( scope , newValue ) {
if ( angular . isFunction ( parsedModel ( originalScope ) ) &&
ngModelOptions && ngModelOptions . $options && ngModelOptions . $options . getterSetter ) {
return invokeModelSetter ( scope , { $$$p : newValue } ) ;
} else {
return parsedModel . assign ( scope , newValue ) ;
}
} ;
//expressions used by typeahead
var parserResult = typeaheadParser . parse ( attrs . uibTypeahead ) ;
var hasFocus ;
//Used to avoid bug in iOS webview where iOS keyboard does not fire
//mousedown & mouseup events
//Issue #3699
var selected ;
//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 ( ) ;
var offDestroy = originalScope . $on ( '$destroy' , function ( ) {
scope . $destroy ( ) ;
} ) ;
scope . $on ( '$destroy' , offDestroy ) ;
// WAI-ARIA
var popupId = 'typeahead-' + scope . $id + '-' + Math . floor ( Math . random ( ) * 10000 ) ;
element . attr ( {
'aria-autocomplete' : 'list' ,
'aria-expanded' : false ,
'aria-owns' : popupId
2015-05-05 03:10:25 +02:00
} ) ;
2016-03-23 18:39:41 +01:00
//pop-up element used to display matches
var popUpEl = angular . element ( '<div uib-typeahead-popup></div>' ) ;
popUpEl . attr ( {
id : popupId ,
matches : 'matches' ,
active : 'activeIdx' ,
select : 'select(activeIdx)' ,
'move-in-progress' : 'moveInProgress' ,
query : 'query' ,
position : 'position'
2015-05-05 03:10:25 +02:00
} ) ;
2016-03-23 18:39:41 +01:00
//custom item template
if ( angular . isDefined ( attrs . typeaheadTemplateUrl ) ) {
popUpEl . attr ( 'template-url' , attrs . typeaheadTemplateUrl ) ;
}
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
if ( angular . isDefined ( attrs . typeaheadPopupTemplateUrl ) ) {
popUpEl . attr ( 'popup-template-url' , attrs . typeaheadPopupTemplateUrl ) ;
}
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
var resetMatches = function ( ) {
scope . matches = [ ] ;
scope . activeIdx = - 1 ;
element . attr ( 'aria-expanded' , false ) ;
} ;
var getMatchId = function ( index ) {
return popupId + '-option-' + index ;
} ;
// 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' ) ;
2015-05-05 03:10:25 +02:00
} else {
2016-03-23 18:39:41 +01:00
element . attr ( 'aria-activedescendant' , getMatchId ( index ) ) ;
2015-05-05 03:10:25 +02:00
}
} ) ;
2016-03-23 18:39:41 +01:00
var inputIsExactMatch = function ( inputValue , index ) {
if ( scope . matches . length > index && inputValue ) {
return inputValue . toUpperCase ( ) === scope . matches [ index ] . label . toUpperCase ( ) ;
}
return false ;
} ;
var getMatchesAsync = function ( inputValue ) {
var locals = { $viewValue : inputValue } ;
isLoadingSetter ( originalScope , true ) ;
isNoResultsSetter ( originalScope , false ) ;
$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
var onCurrentRequest = ( inputValue === modelCtrl . $viewValue ) ;
if ( onCurrentRequest && hasFocus ) {
if ( matches && matches . length > 0 ) {
scope . activeIdx = focusFirst ? 0 : - 1 ;
isNoResultsSetter ( originalScope , false ) ;
scope . matches . length = 0 ;
//transform labels
for ( var i = 0 ; i < matches . length ; i ++ ) {
locals [ parserResult . itemName ] = matches [ i ] ;
scope . matches . push ( {
id : getMatchId ( i ) ,
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
recalculatePosition ( ) ;
element . attr ( 'aria-expanded' , true ) ;
//Select the single remaining option if user input matches
if ( selectOnExact && scope . matches . length === 1 && inputIsExactMatch ( inputValue , 0 ) ) {
scope . select ( 0 ) ;
}
} else {
resetMatches ( ) ;
isNoResultsSetter ( originalScope , true ) ;
}
}
if ( onCurrentRequest ) {
isLoadingSetter ( originalScope , false ) ;
}
} , function ( ) {
resetMatches ( ) ;
isLoadingSetter ( originalScope , false ) ;
isNoResultsSetter ( originalScope , true ) ;
} ) ;
} ;
// bind events only if appendToBody params exist - performance feature
if ( appendToBody ) {
angular . element ( $window ) . bind ( 'resize' , fireRecalculating ) ;
$document . find ( 'body' ) . bind ( 'scroll' , fireRecalculating ) ;
2015-05-05 03:10:25 +02:00
}
2016-03-23 18:39:41 +01:00
// Declare the timeout promise var outside the function scope so that stacked calls can be cancelled later
var timeoutEventPromise ;
// Default progress type
scope . moveInProgress = false ;
function fireRecalculating ( ) {
if ( ! scope . moveInProgress ) {
scope . moveInProgress = true ;
scope . $digest ( ) ;
2015-05-05 03:10:25 +02:00
}
2016-03-23 18:39:41 +01:00
// Cancel previous timeout
if ( timeoutEventPromise ) {
$timeout . cancel ( timeoutEventPromise ) ;
2015-05-05 03:10:25 +02:00
}
2016-03-23 18:39:41 +01:00
// Debounced executing recalculate after events fired
timeoutEventPromise = $timeout ( function ( ) {
// if popup is visible
if ( scope . matches . length ) {
recalculatePosition ( ) ;
}
scope . moveInProgress = false ;
} , eventDebounceTime ) ;
2015-05-05 03:10:25 +02:00
}
2016-03-23 18:39:41 +01:00
// recalculate actual position and set new values to scope
// after digest loop is popup in right position
function recalculatePosition ( ) {
scope . position = appendToBody ? $position . offset ( element ) : $position . position ( element ) ;
scope . position . top += element . prop ( 'offsetHeight' ) ;
}
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
//we need to propagate user's query so we can higlight matches
scope . query = undefined ;
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
//Declare the timeout promise var outside the function scope so that stacked calls can be cancelled later
var timeoutPromise ;
var scheduleSearchWithTimeout = function ( inputValue ) {
timeoutPromise = $timeout ( function ( ) {
getMatchesAsync ( inputValue ) ;
} , waitTime ) ;
} ;
var cancelPreviousTimeout = function ( ) {
if ( timeoutPromise ) {
$timeout . cancel ( timeoutPromise ) ;
2015-05-05 03:10:25 +02:00
}
} ;
2016-03-23 18:39:41 +01:00
resetMatches ( ) ;
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
scope . select = function ( activeIdx ) {
//called from within the $digest() cycle
var locals = { } ;
var model , item ;
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
selected = true ;
locals [ parserResult . itemName ] = item = scope . matches [ activeIdx ] . model ;
model = parserResult . modelMapper ( originalScope , locals ) ;
$setModelValue ( originalScope , model ) ;
modelCtrl . $setValidity ( 'editable' , true ) ;
modelCtrl . $setValidity ( 'parse' , true ) ;
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
onSelectCallback ( originalScope , {
$item : item ,
$model : model ,
$label : parserResult . viewMapper ( originalScope , locals )
} ) ;
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
resetMatches ( ) ;
//return focus to the input element if a match was selected via a mouse click event
// use timeout to avoid $rootScope:inprog error
if ( scope . $eval ( attrs . typeaheadFocusOnSelect ) !== false ) {
$timeout ( function ( ) { element [ 0 ] . focus ( ) ; } , 0 , false ) ;
2015-05-05 03:10:25 +02:00
}
} ;
2016-03-23 18:39:41 +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-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
// if there's nothing selected (i.e. focusFirst) and enter or tab is hit, clear the results
if ( scope . activeIdx === - 1 && ( evt . which === 9 || evt . which === 13 ) ) {
resetMatches ( ) ;
scope . $digest ( ) ;
return ;
2015-05-05 03:10:25 +02:00
}
2016-03-23 18:39:41 +01:00
evt . preventDefault ( ) ;
if ( evt . which === 40 ) {
scope . activeIdx = ( scope . activeIdx + 1 ) % scope . matches . length ;
scope . $digest ( ) ;
} else if ( evt . which === 38 ) {
scope . activeIdx = ( scope . activeIdx > 0 ? scope . activeIdx : scope . matches . length ) - 1 ;
scope . $digest ( ) ;
} else if ( evt . which === 13 || evt . which === 9 ) {
scope . $apply ( function ( ) {
scope . select ( scope . activeIdx ) ;
2015-05-05 03:10:25 +02:00
} ) ;
2016-03-23 18:39:41 +01:00
} else if ( evt . which === 27 ) {
evt . stopPropagation ( ) ;
resetMatches ( ) ;
scope . $digest ( ) ;
2015-05-05 03:10:25 +02:00
}
} ) ;
2016-03-23 18:39:41 +01:00
element . bind ( 'blur' , function ( ) {
if ( isSelectOnBlur && scope . matches . length && scope . activeIdx !== - 1 && ! selected ) {
selected = true ;
scope . $apply ( function ( ) {
scope . select ( scope . activeIdx ) ;
} ) ;
}
hasFocus = false ;
selected = false ;
} ) ;
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
// Keep reference to click handler to unbind it.
var dismissClickHandler = function ( evt ) {
// Issue #3973
// Firefox treats right click as a click on document
if ( element [ 0 ] !== evt . target && evt . which !== 3 && scope . matches . length !== 0 ) {
resetMatches ( ) ;
if ( ! $rootScope . $$phase ) {
scope . $digest ( ) ;
}
2015-05-05 03:10:25 +02:00
}
} ;
2016-03-23 18:39:41 +01:00
$document . bind ( 'click' , dismissClickHandler ) ;
originalScope . $on ( '$destroy' , function ( ) {
$document . unbind ( 'click' , dismissClickHandler ) ;
if ( appendToBody || appendToElementId ) {
$popup . remove ( ) ;
2015-05-05 03:10:25 +02:00
}
2016-03-23 18:39:41 +01:00
if ( appendToBody ) {
angular . element ( $window ) . unbind ( 'resize' , fireRecalculating ) ;
$document . find ( 'body' ) . unbind ( 'scroll' , fireRecalculating ) ;
}
// Prevent jQuery cache memory leak
popUpEl . remove ( ) ;
} ) ;
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
var $popup = $compile ( popUpEl ) ( scope ) ;
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
if ( appendToBody ) {
$document . find ( 'body' ) . append ( $popup ) ;
} else if ( appendToElementId !== false ) {
angular . element ( $document [ 0 ] . getElementById ( appendToElementId ) ) . append ( $popup ) ;
2015-05-05 03:10:25 +02:00
} else {
2016-03-23 18:39:41 +01:00
element . after ( $popup ) ;
2015-05-05 03:10:25 +02:00
}
2016-03-23 18:39:41 +01:00
this . init = function ( _modelCtrl , _ngModelOptions ) {
modelCtrl = _modelCtrl ;
ngModelOptions = _ngModelOptions ;
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +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 ;
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
if ( minLength === 0 || inputValue && inputValue . length >= minLength ) {
if ( waitTime > 0 ) {
cancelPreviousTimeout ( ) ;
scheduleSearchWithTimeout ( inputValue ) ;
} else {
getMatchesAsync ( inputValue ) ;
}
} else {
isLoadingSetter ( originalScope , false ) ;
cancelPreviousTimeout ( ) ;
resetMatches ( ) ;
}
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
if ( isEditable ) {
return inputValue ;
} else {
if ( ! inputValue ) {
// Reset in case user had typed something previously.
modelCtrl . $setValidity ( 'editable' , true ) ;
return null ;
} else {
modelCtrl . $setValidity ( 'editable' , false ) ;
return undefined ;
}
}
} ) ;
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
modelCtrl . $formatters . push ( function ( modelValue ) {
var candidateViewValue , emptyViewValue ;
var locals = { } ;
// 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 ) ;
}
if ( inputFormatter ) {
locals . $model = modelValue ;
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 ;
}
} ) ;
} ;
} ] )
. directive ( 'uibTypeahead' , function ( ) {
return {
controller : 'UibTypeaheadController' ,
require : [ 'ngModel' , '^?ngModelOptions' , 'uibTypeahead' ] ,
link : function ( originalScope , element , attrs , ctrls ) {
ctrls [ 2 ] . init ( ctrls [ 0 ] , ctrls [ 1 ] ) ;
}
} ;
} )
. directive ( 'uibTypeaheadPopup' , function ( ) {
return {
scope : {
matches : '=' ,
query : '=' ,
active : '=' ,
position : '&' ,
moveInProgress : '=' ,
select : '&'
} ,
replace : true ,
templateUrl : function ( element , attrs ) {
return attrs . popupTemplateUrl || 'template/typeahead/typeahead-popup.html' ;
} ,
link : function ( scope , element , attrs ) {
scope . templateUrl = attrs . templateUrl ;
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
scope . isOpen = function ( ) {
return scope . matches . length > 0 ;
} ;
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
scope . isActive = function ( matchIdx ) {
return scope . active == matchIdx ;
} ;
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
scope . selectActive = function ( matchIdx ) {
scope . active = matchIdx ;
} ;
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
scope . selectMatch = function ( activeIdx ) {
scope . select ( { activeIdx : activeIdx } ) ;
} ;
2015-05-05 03:10:25 +02:00
}
2016-03-23 18:39:41 +01:00
} ;
} )
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
. directive ( 'uibTypeaheadMatch' , [ '$templateRequest' , '$compile' , '$parse' , function ( $templateRequest , $compile , $parse ) {
return {
scope : {
index : '=' ,
match : '=' ,
query : '='
} ,
link : function ( scope , element , attrs ) {
var tplUrl = $parse ( attrs . templateUrl ) ( scope . $parent ) || 'template/typeahead/typeahead-match.html' ;
$templateRequest ( tplUrl ) . then ( function ( tplContent ) {
$compile ( tplContent . trim ( ) ) ( scope , function ( clonedElement ) {
element . replaceWith ( clonedElement ) ;
} ) ;
} ) ;
}
} ;
} ] )
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
. filter ( 'uibTypeaheadHighlight' , [ '$sce' , '$injector' , '$log' , function ( $sce , $injector , $log ) {
var isSanitizePresent ;
isSanitizePresent = $injector . has ( '$sanitize' ) ;
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
function escapeRegexp ( queryToEscape ) {
// Regex: capture the whole query string and replace it with the string that will be used to match
// the results, for example if the capture is "a" the result will be \a
return queryToEscape . replace ( /([.?*+^$[\]\\(){}|-])/g , '\\$1' ) ;
}
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
function containsHtml ( matchItem ) {
return /<.*>/g . test ( matchItem ) ;
}
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
return function ( matchItem , query ) {
if ( ! isSanitizePresent && containsHtml ( matchItem ) ) {
$log . warn ( 'Unsafe use of typeahead please use ngSanitize' ) ; // Warn the user about the danger
2015-05-05 03:10:25 +02:00
}
2016-03-23 18:39:41 +01:00
matchItem = query ? ( '' + matchItem ) . replace ( new RegExp ( escapeRegexp ( query ) , 'gi' ) , '<strong>$&</strong>' ) : matchItem ; // Replaces the capture string with a the same string inside of a "strong" tag
if ( ! isSanitizePresent ) {
matchItem = $sce . trustAsHtml ( matchItem ) ; // If $sanitize is not present we pack the string in a $sce object for the ng-bind-html directive
}
return matchItem ;
} ;
} ] ) ;
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
/* Deprecated typeahead below */
angular . module ( 'ui.bootstrap.typeahead' )
. value ( '$typeaheadSuppressWarning' , false )
. service ( 'typeaheadParser' , [ '$parse' , 'uibTypeaheadParser' , '$log' , '$typeaheadSuppressWarning' , function ( $parse , uibTypeaheadParser , $log , $typeaheadSuppressWarning ) {
if ( ! $typeaheadSuppressWarning ) {
$log . warn ( 'typeaheadParser is now deprecated. Use uibTypeaheadParser instead.' ) ;
2015-05-05 03:10:25 +02:00
}
2016-03-23 18:39:41 +01:00
return uibTypeaheadParser ;
} ] )
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
. directive ( 'typeahead' , [ '$compile' , '$parse' , '$q' , '$timeout' , '$document' , '$window' , '$rootScope' , '$uibPosition' , 'typeaheadParser' , '$log' , '$typeaheadSuppressWarning' ,
function ( $compile , $parse , $q , $timeout , $document , $window , $rootScope , $position , typeaheadParser , $log , $typeaheadSuppressWarning ) {
var HOT _KEYS = [ 9 , 13 , 27 , 38 , 40 ] ;
var eventDebounceTime = 200 ;
return {
require : [ 'ngModel' , '^?ngModelOptions' ] ,
link : function ( originalScope , element , attrs , ctrls ) {
if ( ! $typeaheadSuppressWarning ) {
$log . warn ( 'typeahead is now deprecated. Use uib-typeahead instead.' ) ;
}
var modelCtrl = ctrls [ 0 ] ;
var ngModelOptions = ctrls [ 1 ] ;
//SUPPORTED ATTRIBUTES (OPTIONS)
//minimal no of characters that needs to be entered before typeahead kicks-in
var minLength = originalScope . $eval ( attrs . typeaheadMinLength ) ;
if ( ! minLength && minLength !== 0 ) {
minLength = 1 ;
}
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
//minimal wait time after last character typed before typeahead kicks-in
var waitTime = originalScope . $eval ( attrs . typeaheadWaitMs ) || 0 ;
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
//should it restrict model values to the ones selected from the popup only?
var isEditable = originalScope . $eval ( attrs . typeaheadEditable ) !== false ;
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
//binding to a variable that indicates if matches are being retrieved asynchronously
var isLoadingSetter = $parse ( attrs . typeaheadLoading ) . assign || angular . noop ;
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
//a callback executed when a match is selected
var onSelectCallback = $parse ( attrs . typeaheadOnSelect ) ;
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
//should it select highlighted popup value when losing focus?
var isSelectOnBlur = angular . isDefined ( attrs . typeaheadSelectOnBlur ) ? originalScope . $eval ( attrs . typeaheadSelectOnBlur ) : false ;
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
//binding to a variable that indicates if there were no results after the query is completed
var isNoResultsSetter = $parse ( attrs . typeaheadNoResults ) . assign || angular . noop ;
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
var inputFormatter = attrs . typeaheadInputFormatter ? $parse ( attrs . typeaheadInputFormatter ) : undefined ;
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
var appendToBody = attrs . typeaheadAppendToBody ? originalScope . $eval ( attrs . typeaheadAppendToBody ) : false ;
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
var appendToElementId = attrs . typeaheadAppendToElementId || false ;
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
var focusFirst = originalScope . $eval ( attrs . typeaheadFocusFirst ) !== false ;
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
//If input matches an item of the list exactly, select it automatically
var selectOnExact = attrs . typeaheadSelectOnExact ? originalScope . $eval ( attrs . typeaheadSelectOnExact ) : false ;
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
//INTERNAL VARIABLES
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
//model setter executed upon match selection
var parsedModel = $parse ( attrs . ngModel ) ;
var invokeModelSetter = $parse ( attrs . ngModel + '($$$p)' ) ;
var $setModelValue = function ( scope , newValue ) {
if ( angular . isFunction ( parsedModel ( originalScope ) ) &&
ngModelOptions && ngModelOptions . $options && ngModelOptions . $options . getterSetter ) {
return invokeModelSetter ( scope , { $$$p : newValue } ) ;
} else {
return parsedModel . assign ( scope , newValue ) ;
}
} ;
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
//expressions used by typeahead
var parserResult = typeaheadParser . parse ( attrs . typeahead ) ;
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
var hasFocus ;
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
//Used to avoid bug in iOS webview where iOS keyboard does not fire
//mousedown & mouseup events
//Issue #3699
var selected ;
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +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 ( ) ;
var offDestroy = originalScope . $on ( '$destroy' , function ( ) {
scope . $destroy ( ) ;
} ) ;
scope . $on ( '$destroy' , offDestroy ) ;
// WAI-ARIA
var popupId = 'typeahead-' + scope . $id + '-' + Math . floor ( Math . random ( ) * 10000 ) ;
element . attr ( {
'aria-autocomplete' : 'list' ,
'aria-expanded' : false ,
'aria-owns' : popupId
} ) ;
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
//pop-up element used to display matches
var popUpEl = angular . element ( '<div typeahead-popup></div>' ) ;
popUpEl . attr ( {
id : popupId ,
matches : 'matches' ,
active : 'activeIdx' ,
select : 'select(activeIdx)' ,
'move-in-progress' : 'moveInProgress' ,
query : 'query' ,
position : 'position'
} ) ;
//custom item template
if ( angular . isDefined ( attrs . typeaheadTemplateUrl ) ) {
popUpEl . attr ( 'template-url' , attrs . typeaheadTemplateUrl ) ;
}
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
if ( angular . isDefined ( attrs . typeaheadPopupTemplateUrl ) ) {
popUpEl . attr ( 'popup-template-url' , attrs . typeaheadPopupTemplateUrl ) ;
2015-05-05 03:10:25 +02:00
}
2016-03-23 18:39:41 +01:00
var resetMatches = function ( ) {
scope . matches = [ ] ;
scope . activeIdx = - 1 ;
element . attr ( 'aria-expanded' , false ) ;
} ;
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
var getMatchId = function ( index ) {
return popupId + '-option-' + index ;
} ;
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +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 ) ) ;
}
} ) ;
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
var inputIsExactMatch = function ( inputValue , index ) {
if ( scope . matches . length > index && inputValue ) {
return inputValue . toUpperCase ( ) === scope . matches [ index ] . label . toUpperCase ( ) ;
}
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
return false ;
} ;
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
var getMatchesAsync = function ( inputValue ) {
var locals = { $viewValue : inputValue } ;
isLoadingSetter ( originalScope , true ) ;
isNoResultsSetter ( originalScope , false ) ;
$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
var onCurrentRequest = ( inputValue === modelCtrl . $viewValue ) ;
if ( onCurrentRequest && hasFocus ) {
if ( matches && matches . length > 0 ) {
scope . activeIdx = focusFirst ? 0 : - 1 ;
isNoResultsSetter ( originalScope , false ) ;
scope . matches . length = 0 ;
//transform labels
for ( var i = 0 ; i < matches . length ; i ++ ) {
locals [ parserResult . itemName ] = matches [ i ] ;
scope . matches . push ( {
id : getMatchId ( i ) ,
label : parserResult . viewMapper ( scope , locals ) ,
model : matches [ i ]
} ) ;
}
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
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
recalculatePosition ( ) ;
element . attr ( 'aria-expanded' , true ) ;
//Select the single remaining option if user input matches
if ( selectOnExact && scope . matches . length === 1 && inputIsExactMatch ( inputValue , 0 ) ) {
scope . select ( 0 ) ;
}
} else {
resetMatches ( ) ;
isNoResultsSetter ( originalScope , true ) ;
}
2015-05-05 03:10:25 +02:00
}
2016-03-23 18:39:41 +01:00
if ( onCurrentRequest ) {
isLoadingSetter ( originalScope , false ) ;
}
} , function ( ) {
resetMatches ( ) ;
2015-05-05 03:10:25 +02:00
isLoadingSetter ( originalScope , false ) ;
2016-03-23 18:39:41 +01:00
isNoResultsSetter ( originalScope , true ) ;
} ) ;
} ;
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
// bind events only if appendToBody params exist - performance feature
if ( appendToBody ) {
angular . element ( $window ) . bind ( 'resize' , fireRecalculating ) ;
$document . find ( 'body' ) . bind ( 'scroll' , fireRecalculating ) ;
}
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
// Declare the timeout promise var outside the function scope so that stacked calls can be cancelled later
var timeoutEventPromise ;
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
// Default progress type
scope . moveInProgress = false ;
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
function fireRecalculating ( ) {
if ( ! scope . moveInProgress ) {
scope . moveInProgress = true ;
scope . $digest ( ) ;
}
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
// Cancel previous timeout
if ( timeoutEventPromise ) {
$timeout . cancel ( timeoutEventPromise ) ;
}
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
// Debounced executing recalculate after events fired
timeoutEventPromise = $timeout ( function ( ) {
// if popup is visible
if ( scope . matches . length ) {
recalculatePosition ( ) ;
}
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
scope . moveInProgress = false ;
} , eventDebounceTime ) ;
2015-05-05 03:10:25 +02:00
}
2016-03-23 18:39:41 +01:00
// recalculate actual position and set new values to scope
// after digest loop is popup in right position
function recalculatePosition ( ) {
scope . position = appendToBody ? $position . offset ( element ) : $position . position ( element ) ;
scope . position . top += element . prop ( 'offsetHeight' ) ;
2015-05-05 03:10:25 +02:00
}
2016-03-23 18:39:41 +01:00
resetMatches ( ) ;
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
//we need to propagate user's query so we can higlight matches
scope . query = undefined ;
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
//Declare the timeout promise var outside the function scope so that stacked calls can be cancelled later
var timeoutPromise ;
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
var scheduleSearchWithTimeout = function ( inputValue ) {
timeoutPromise = $timeout ( function ( ) {
getMatchesAsync ( inputValue ) ;
} , waitTime ) ;
} ;
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
var cancelPreviousTimeout = function ( ) {
if ( timeoutPromise ) {
$timeout . cancel ( timeoutPromise ) ;
}
} ;
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +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 ;
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
if ( minLength === 0 || inputValue && inputValue . length >= minLength ) {
if ( waitTime > 0 ) {
cancelPreviousTimeout ( ) ;
scheduleSearchWithTimeout ( inputValue ) ;
} else {
getMatchesAsync ( inputValue ) ;
}
} else {
isLoadingSetter ( originalScope , false ) ;
cancelPreviousTimeout ( ) ;
resetMatches ( ) ;
}
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
if ( isEditable ) {
return inputValue ;
} else {
if ( ! inputValue ) {
// Reset in case user had typed something previously.
modelCtrl . $setValidity ( 'editable' , true ) ;
return null ;
} else {
modelCtrl . $setValidity ( 'editable' , false ) ;
return undefined ;
}
}
} ) ;
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
modelCtrl . $formatters . push ( function ( modelValue ) {
var candidateViewValue , emptyViewValue ;
var locals = { } ;
// 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 ) ;
}
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
if ( inputFormatter ) {
locals . $model = modelValue ;
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 ;
}
2015-05-05 03:10:25 +02:00
} ) ;
2016-03-23 18:39:41 +01:00
scope . select = function ( activeIdx ) {
//called from within the $digest() cycle
var locals = { } ;
var model , item ;
selected = true ;
locals [ parserResult . itemName ] = item = scope . matches [ activeIdx ] . model ;
model = parserResult . modelMapper ( originalScope , locals ) ;
$setModelValue ( originalScope , model ) ;
modelCtrl . $setValidity ( 'editable' , true ) ;
modelCtrl . $setValidity ( 'parse' , true ) ;
onSelectCallback ( originalScope , {
$item : item ,
$model : model ,
$label : parserResult . viewMapper ( originalScope , locals )
} ) ;
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
resetMatches ( ) ;
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +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
if ( scope . $eval ( attrs . typeaheadFocusOnSelect ) !== false ) {
$timeout ( function ( ) { element [ 0 ] . focus ( ) ; } , 0 , false ) ;
}
} ;
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +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-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
// if there's nothing selected (i.e. focusFirst) and enter or tab is hit, clear the results
if ( scope . activeIdx === - 1 && ( evt . which === 9 || evt . which === 13 ) ) {
resetMatches ( ) ;
scope . $digest ( ) ;
return ;
}
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
evt . preventDefault ( ) ;
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
if ( evt . which === 40 ) {
scope . activeIdx = ( scope . activeIdx + 1 ) % scope . matches . length ;
scope . $digest ( ) ;
} else if ( evt . which === 38 ) {
scope . activeIdx = ( scope . activeIdx > 0 ? scope . activeIdx : scope . matches . length ) - 1 ;
scope . $digest ( ) ;
} else if ( evt . which === 13 || evt . which === 9 ) {
scope . $apply ( function ( ) {
scope . select ( scope . activeIdx ) ;
} ) ;
} else if ( evt . which === 27 ) {
evt . stopPropagation ( ) ;
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
resetMatches ( ) ;
scope . $digest ( ) ;
}
} ) ;
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
element . bind ( 'blur' , function ( ) {
if ( isSelectOnBlur && scope . matches . length && scope . activeIdx !== - 1 && ! selected ) {
selected = true ;
scope . $apply ( function ( ) {
scope . select ( scope . activeIdx ) ;
} ) ;
}
hasFocus = false ;
selected = false ;
} ) ;
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
// Keep reference to click handler to unbind it.
var dismissClickHandler = function ( evt ) {
// Issue #3973
// Firefox treats right click as a click on document
if ( element [ 0 ] !== evt . target && evt . which !== 3 && scope . matches . length !== 0 ) {
resetMatches ( ) ;
if ( ! $rootScope . $$phase ) {
scope . $digest ( ) ;
}
}
} ;
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
$document . bind ( 'click' , dismissClickHandler ) ;
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
originalScope . $on ( '$destroy' , function ( ) {
$document . unbind ( 'click' , dismissClickHandler ) ;
if ( appendToBody || appendToElementId ) {
$popup . remove ( ) ;
}
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
if ( appendToBody ) {
angular . element ( $window ) . unbind ( 'resize' , fireRecalculating ) ;
$document . find ( 'body' ) . unbind ( 'scroll' , fireRecalculating ) ;
}
// Prevent jQuery cache memory leak
popUpEl . remove ( ) ;
} ) ;
2015-05-05 03:10:25 +02:00
2016-03-23 18:39:41 +01:00
var $popup = $compile ( popUpEl ) ( scope ) ;
2015-05-05 03:10:25 +02:00
if ( appendToBody ) {
2016-03-23 18:39:41 +01:00
$document . find ( 'body' ) . append ( $popup ) ;
} else if ( appendToElementId !== false ) {
angular . element ( $document [ 0 ] . getElementById ( appendToElementId ) ) . append ( $popup ) ;
} else {
element . after ( $popup ) ;
2015-05-05 03:10:25 +02:00
}
}
2016-03-23 18:39:41 +01:00
} ;
} ] )
. directive ( 'typeaheadPopup' , [ '$typeaheadSuppressWarning' , '$log' , function ( $typeaheadSuppressWarning , $log ) {
2015-05-05 03:10:25 +02:00
return {
2016-03-23 18:39:41 +01:00
scope : {
matches : '=' ,
query : '=' ,
active : '=' ,
position : '&' ,
moveInProgress : '=' ,
select : '&'
2015-05-05 03:10:25 +02:00
} ,
2016-03-23 18:39:41 +01:00
replace : true ,
templateUrl : function ( element , attrs ) {
return attrs . popupTemplateUrl || 'template/typeahead/typeahead-popup.html' ;
} ,
link : function ( scope , element , attrs ) {
if ( ! $typeaheadSuppressWarning ) {
$log . warn ( 'typeahead-popup is now deprecated. Use uib-typeahead-popup instead.' ) ;
}
2015-05-05 03:10:25 +02:00
scope . templateUrl = attrs . templateUrl ;
2016-03-23 18:39:41 +01:00
scope . isOpen = function ( ) {
2015-05-05 03:10:25 +02:00
return scope . matches . length > 0 ;
} ;
2016-03-23 18:39:41 +01:00
scope . isActive = function ( matchIdx ) {
2015-05-05 03:10:25 +02:00
return scope . active == matchIdx ;
} ;
2016-03-23 18:39:41 +01:00
scope . selectActive = function ( matchIdx ) {
2015-05-05 03:10:25 +02:00
scope . active = matchIdx ;
} ;
2016-03-23 18:39:41 +01:00
scope . selectMatch = function ( activeIdx ) {
2015-05-05 03:10:25 +02:00
scope . select ( { activeIdx : activeIdx } ) ;
} ;
}
} ;
2016-03-23 18:39:41 +01:00
} ] )
. directive ( 'typeaheadMatch' , [ '$templateRequest' , '$compile' , '$parse' , '$typeaheadSuppressWarning' , '$log' , function ( $templateRequest , $compile , $parse , $typeaheadSuppressWarning , $log ) {
2015-05-05 03:10:25 +02:00
return {
2016-03-23 18:39:41 +01:00
restrict : 'EA' ,
scope : {
index : '=' ,
match : '=' ,
query : '='
2015-05-05 03:10:25 +02:00
} ,
2016-03-23 18:39:41 +01:00
link : function ( scope , element , attrs ) {
if ( ! $typeaheadSuppressWarning ) {
$log . warn ( 'typeahead-match is now deprecated. Use uib-typeahead-match instead.' ) ;
}
2015-05-05 03:10:25 +02:00
var tplUrl = $parse ( attrs . templateUrl ) ( scope . $parent ) || 'template/typeahead/typeahead-match.html' ;
2016-03-23 18:39:41 +01:00
$templateRequest ( tplUrl ) . then ( function ( tplContent ) {
$compile ( tplContent . trim ( ) ) ( scope , function ( clonedElement ) {
element . replaceWith ( clonedElement ) ;
} ) ;
2015-05-05 03:10:25 +02:00
} ) ;
}
} ;
} ] )
2016-03-23 18:39:41 +01:00
. filter ( 'typeaheadHighlight' , [ '$sce' , '$injector' , '$log' , '$typeaheadSuppressWarning' , function ( $sce , $injector , $log , $typeaheadSuppressWarning ) {
var isSanitizePresent ;
isSanitizePresent = $injector . has ( '$sanitize' ) ;
2015-05-05 03:10:25 +02:00
function escapeRegexp ( queryToEscape ) {
2016-03-23 18:39:41 +01:00
// Regex: capture the whole query string and replace it with the string that will be used to match
// the results, for example if the capture is "a" the result will be \a
2015-05-05 03:10:25 +02:00
return queryToEscape . replace ( /([.?*+^$[\]\\(){}|-])/g , '\\$1' ) ;
}
2016-03-23 18:39:41 +01:00
function containsHtml ( matchItem ) {
return /<.*>/g . test ( matchItem ) ;
}
2015-05-05 03:10:25 +02:00
return function ( matchItem , query ) {
2016-03-23 18:39:41 +01:00
if ( ! $typeaheadSuppressWarning ) {
$log . warn ( 'typeaheadHighlight is now deprecated. Use uibTypeaheadHighlight instead.' ) ;
}
if ( ! isSanitizePresent && containsHtml ( matchItem ) ) {
$log . warn ( 'Unsafe use of typeahead please use ngSanitize' ) ; // Warn the user about the danger
}
matchItem = query ? ( '' + matchItem ) . replace ( new RegExp ( escapeRegexp ( query ) , 'gi' ) , '<strong>$&</strong>' ) : matchItem ; // Replaces the capture string with a the same string inside of a "strong" tag
if ( ! isSanitizePresent ) {
matchItem = $sce . trustAsHtml ( matchItem ) ; // If $sanitize is not present we pack the string in a $sce object for the ng-bind-html directive
}
return matchItem ;
2015-05-05 03:10:25 +02:00
} ;
2016-03-23 18:39:41 +01: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>' ) ;