angular-ui-bootstrap
Advanced tools
Comparing version 1.0.3 to 1.1.0
{ | ||
"author": "https://github.com/angular-ui/bootstrap/graphs/contributors", | ||
"name": "angular-ui-bootstrap", | ||
"version": "1.0.3", | ||
"version": "1.1.0", | ||
"homepage": "http://angular-ui.github.io/bootstrap/", | ||
@@ -6,0 +6,0 @@ "dependencies": {}, |
@@ -40,4 +40,11 @@ ### UI Bootstrap - [AngularJS](http://angularjs.org/) directives specific to [Bootstrap](http://getbootstrap.com) | ||
Additionally, it is strongly recommended that for UI Bootstrap 0.13.3 and higher you use Angular 1.3.18 or higher due to animation fixes. | ||
## Angular Requirements | ||
* UI Bootstrap 1.0 and higher _requires_ Angular 1.4.x or higher and it has been tested with Angular 1.4.8. | ||
* UI Bootstrap 0.14.3 is the _last_ version that supports Angular 1.3.x. | ||
* UI Bootstrap 0.12.0 is the _last_ version that supports Angular 1.2.x. | ||
## Bootstrap Requirements | ||
* UI Bootstrap requires Bootstrap CSS version 3.x or higher and it has been tested with Bootstrap CSS 3.3.6. | ||
* UI Bootstrap 0.8 is the _last_ version that supports Bootstrap CSS 2.3.x. | ||
#### Install with NPM | ||
@@ -44,0 +51,0 @@ |
@@ -12,3 +12,3 @@ The **accordion directive** builds on top of the collapse directive to provide a list of items, with collapsible bodies that are collapsed or expanded by clicking on the item's header. | ||
Control whether expanding an item will cause the other items to close. | ||
* `template-url` | ||
@@ -36,3 +36,3 @@ _(Default: `template/accordion/accordion.html`)_ - | ||
* `panel-class` | ||
* `panel-class` | ||
_(Default: `panel-default`)_ - | ||
@@ -48,1 +48,5 @@ Add ability to use Bootstrap's contextual panel classes (panel-primary, panel-success, panel-info, etc...) or your own. This must be a string. | ||
Instead of the `heading` attribute on the `uib-accordion-group`, you can use an `uib-accordion-heading` element inside a group that will be used as the group's header. | ||
### Known issues | ||
To use clickable elements within the accordion, you have override the accordion-group template to use div elements instead of anchor elements, and add `cursor: pointer` in your CSS. This is due to browsers interpreting anchor elements as the target of any click event, which triggers routing when certain elements such as buttons are nested inside the anchor element. |
@@ -5,5 +5,4 @@ This directive can be used both to generate alerts from static and dynamic model data (using the `ng-repeat` directive). | ||
* `close` | ||
<small class="badge">$</small> | ||
_(Default: `none`)_ - | ||
* `close()` | ||
<small class="badge">$</small> - | ||
A callback function that gets fired when an `alert` is closed. If the attribute exists, a close button is displayed as well. | ||
@@ -10,0 +9,0 @@ |
angular.module('ui.bootstrap.collapse', []) | ||
.directive('uibCollapse', ['$animate', '$injector', function($animate, $injector) { | ||
.directive('uibCollapse', ['$animate', '$q', '$parse', '$injector', function($animate, $q, $parse, $injector) { | ||
var $animateCss = $injector.has('$animateCss') ? $injector.get('$animateCss') : null; | ||
return { | ||
link: function(scope, element, attrs) { | ||
var expandingExpr = $parse(attrs.expanding), | ||
expandedExpr = $parse(attrs.expanded), | ||
collapsingExpr = $parse(attrs.collapsing), | ||
collapsedExpr = $parse(attrs.collapsed); | ||
if (!scope.$eval(attrs.uibCollapse)) { | ||
element.addClass('in') | ||
.addClass('collapse') | ||
.attr('aria-expanded', true) | ||
.attr('aria-hidden', false) | ||
.css({height: 'auto'}); | ||
@@ -14,18 +21,25 @@ } | ||
function expand() { | ||
element.removeClass('collapse') | ||
.addClass('collapsing') | ||
.attr('aria-expanded', true) | ||
.attr('aria-hidden', false); | ||
if (element.hasClass('collapse') && element.hasClass('in')) { | ||
return; | ||
} | ||
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); | ||
} | ||
$q.resolve(expandingExpr(scope)) | ||
.then(function() { | ||
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); | ||
} | ||
}); | ||
} | ||
@@ -37,2 +51,3 @@ | ||
.css({height: 'auto'}); | ||
expandedExpr(scope); | ||
} | ||
@@ -45,24 +60,27 @@ | ||
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); | ||
$q.resolve(collapsingExpr(scope)) | ||
.then(function() { | ||
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); | ||
} | ||
if ($animateCss) { | ||
$animateCss(element, { | ||
removeClass: 'in', | ||
to: {height: '0'} | ||
}).start()['finally'](collapseDone); | ||
} else { | ||
$animate.removeClass(element, 'in', { | ||
to: {height: '0'} | ||
}).then(collapseDone); | ||
} | ||
}); | ||
} | ||
@@ -74,2 +92,3 @@ | ||
.addClass('collapse'); | ||
collapsedExpr(scope); | ||
} | ||
@@ -76,0 +95,0 @@ |
@@ -5,2 +5,22 @@ **uib-collapse** provides a simple way to hide and show an element with a css transition | ||
* `collapsed()` | ||
<small class="badge">$</small> - | ||
An optional expression called after the element finished collapsing. | ||
* `collapsing()` | ||
<small class="badge">$</small> - | ||
An optional expression called before the element begins collapsing. | ||
If the expression returns a promise, animation won't start until the promise resolves. | ||
If the returned promise is rejected, collapsing will be cancelled. | ||
* `expanded()` | ||
<small class="badge">$</small> - | ||
An optional expression called after the element finished expanding. | ||
* `expanding()` | ||
<small class="badge">$</small> - | ||
An optional expression called before the element begins expanding. | ||
If the expression returns a promise, animation won't start until the promise resolves. | ||
If the returned promise is rejected, expanding will be cancelled. | ||
* `uib-collapse` | ||
@@ -11,1 +31,2 @@ <small class="badge">$</small> | ||
Whether the element should be collapsed or not. | ||
describe('collapse directive', function() { | ||
var element, compileFn, scope, $compile, $animate; | ||
var element, compileFn, scope, $compile, $animate, $q; | ||
beforeEach(module('ui.bootstrap.collapse')); | ||
beforeEach(module('ngAnimateMock')); | ||
beforeEach(inject(function(_$rootScope_, _$compile_, _$animate_) { | ||
beforeEach(inject(function(_$rootScope_, _$compile_, _$animate_, _$q_) { | ||
scope = _$rootScope_; | ||
$compile = _$compile_; | ||
$animate = _$animate_; | ||
$q = _$q_; | ||
})); | ||
beforeEach(function() { | ||
element = angular.element('<div uib-collapse="isCollapsed">Some Content</div>'); | ||
element = angular.element( | ||
'<div uib-collapse="isCollapsed" ' | ||
+ 'expanding="expanding()" ' | ||
+ 'expanded="expanded()" ' | ||
+ 'collapsing="collapsing()" ' | ||
+ 'collapsed="collapsed()">' | ||
+ 'Some Content</div>'); | ||
compileFn = $compile(element); | ||
@@ -22,3 +29,21 @@ angular.element(document.body).append(element); | ||
function initCallbacks() { | ||
scope.collapsing = jasmine.createSpy('scope.collapsing'); | ||
scope.collapsed = jasmine.createSpy('scope.collapsed'); | ||
scope.expanding = jasmine.createSpy('scope.expanding'); | ||
scope.expanded = jasmine.createSpy('scope.expanded'); | ||
} | ||
function assertCallbacks(expected) { | ||
['collapsing', 'collapsed', 'expanding', 'expanded'].forEach(function(cbName) { | ||
if (expected[cbName]) { | ||
expect(scope[cbName]).toHaveBeenCalled(); | ||
} else { | ||
expect(scope[cbName]).not.toHaveBeenCalled(); | ||
} | ||
}); | ||
} | ||
it('should be hidden on initialization if isCollapsed = true', function() { | ||
initCallbacks(); | ||
scope.isCollapsed = true; | ||
@@ -28,4 +53,17 @@ compileFn(scope); | ||
expect(element.height()).toBe(0); | ||
assertCallbacks({ collapsed: true }); | ||
}); | ||
it('should not trigger any animation on initialization if isCollapsed = true', function() { | ||
var wrapperFn = function() { | ||
$animate.flush(); | ||
}; | ||
scope.isCollapsed = true; | ||
compileFn(scope); | ||
scope.$digest(); | ||
expect(wrapperFn).toThrowError(/No pending animations ready to be closed or flushed/); | ||
}); | ||
it('should collapse if isCollapsed = true on subsequent use', function() { | ||
@@ -35,3 +73,3 @@ scope.isCollapsed = false; | ||
scope.$digest(); | ||
$animate.flush(); | ||
initCallbacks(); | ||
scope.isCollapsed = true; | ||
@@ -41,12 +79,33 @@ scope.$digest(); | ||
expect(element.height()).toBe(0); | ||
assertCallbacks({ collapsing: true, collapsed: true }); | ||
}); | ||
it('should be shown on initialization if isCollapsed = false', function() { | ||
scope.isCollapsed = false; | ||
it('should show after toggled from collapsed', function() { | ||
initCallbacks(); | ||
scope.isCollapsed = true; | ||
compileFn(scope); | ||
scope.$digest(); | ||
expect(element.height()).toBe(0); | ||
assertCallbacks({ collapsed: true }); | ||
scope.collapsed.calls.reset(); | ||
scope.isCollapsed = false; | ||
scope.$digest(); | ||
$animate.flush(); | ||
expect(element.height()).not.toBe(0); | ||
assertCallbacks({ expanding: true, expanded: true }); | ||
}); | ||
it('should not trigger any animation on initialization if isCollapsed = false', function() { | ||
var wrapperFn = function() { | ||
$animate.flush(); | ||
}; | ||
scope.isCollapsed = false; | ||
compileFn(scope); | ||
scope.$digest(); | ||
expect(wrapperFn).toThrowError(/No pending animations ready to be closed or flushed/); | ||
}); | ||
it('should expand if isCollapsed = false on subsequent use', function() { | ||
@@ -56,6 +115,6 @@ scope.isCollapsed = false; | ||
scope.$digest(); | ||
$animate.flush(); | ||
scope.isCollapsed = true; | ||
scope.$digest(); | ||
$animate.flush(); | ||
initCallbacks(); | ||
scope.isCollapsed = false; | ||
@@ -65,9 +124,9 @@ scope.$digest(); | ||
expect(element.height()).not.toBe(0); | ||
assertCallbacks({ expanding: true, expanded: true }); | ||
}); | ||
it('should expand if isCollapsed = true on subsequent uses', function() { | ||
it('should collapse if isCollapsed = true on subsequent uses', function() { | ||
scope.isCollapsed = false; | ||
compileFn(scope); | ||
scope.$digest(); | ||
$animate.flush(); | ||
scope.isCollapsed = true; | ||
@@ -79,2 +138,3 @@ scope.$digest(); | ||
$animate.flush(); | ||
initCallbacks(); | ||
scope.isCollapsed = true; | ||
@@ -84,2 +144,3 @@ scope.$digest(); | ||
expect(element.height()).toBe(0); | ||
assertCallbacks({ collapsing: true, collapsed: true }); | ||
}); | ||
@@ -91,3 +152,2 @@ | ||
scope.$digest(); | ||
$animate.flush(); | ||
expect(element.attr('aria-expanded')).toBe('true'); | ||
@@ -105,3 +165,2 @@ | ||
scope.$digest(); | ||
$animate.flush(); | ||
expect(element.attr('aria-hidden')).toBe('false'); | ||
@@ -132,3 +191,2 @@ | ||
scope.$digest(); | ||
$animate.flush(); | ||
var collapseHeight = element.height(); | ||
@@ -144,3 +202,2 @@ scope.exp = true; | ||
scope.$digest(); | ||
$animate.flush(); | ||
var collapseHeight = element.height(); | ||
@@ -152,2 +209,93 @@ scope.exp = false; | ||
}); | ||
describe('expanding callback returning a promise', function() { | ||
var defer, collapsedHeight; | ||
beforeEach(function() { | ||
defer = $q.defer(); | ||
scope.isCollapsed = true; | ||
scope.expanding = function() { | ||
return defer.promise; | ||
}; | ||
compileFn(scope); | ||
scope.$digest(); | ||
collapsedHeight = element.height(); | ||
// set flag to expand ... | ||
scope.isCollapsed = false; | ||
scope.$digest(); | ||
// ... shouldn't expand yet ... | ||
expect(element.attr('aria-expanded')).not.toBe('true'); | ||
expect(element.height()).toBe(collapsedHeight); | ||
}); | ||
it('should wait for it to resolve before animating', function() { | ||
defer.resolve(); | ||
// should now expand | ||
scope.$digest(); | ||
$animate.flush(); | ||
expect(element.attr('aria-expanded')).toBe('true'); | ||
expect(element.height()).toBeGreaterThan(collapsedHeight); | ||
}); | ||
it('should not animate if it rejects', function() { | ||
defer.reject(); | ||
// should NOT expand | ||
scope.$digest(); | ||
expect(element.attr('aria-expanded')).not.toBe('true'); | ||
expect(element.height()).toBe(collapsedHeight); | ||
}); | ||
}); | ||
describe('collapsing callback returning a promise', function() { | ||
var defer, expandedHeight; | ||
beforeEach(function() { | ||
defer = $q.defer(); | ||
scope.isCollapsed = false; | ||
scope.collapsing = function() { | ||
return defer.promise; | ||
}; | ||
compileFn(scope); | ||
scope.$digest(); | ||
expandedHeight = element.height(); | ||
// set flag to collapse ... | ||
scope.isCollapsed = true; | ||
scope.$digest(); | ||
// ... but it shouldn't collapse yet ... | ||
expect(element.attr('aria-expanded')).not.toBe('false'); | ||
expect(element.height()).toBe(expandedHeight); | ||
}); | ||
it('should wait for it to resolve before animating', function() { | ||
defer.resolve(); | ||
// should now collapse | ||
scope.$digest(); | ||
$animate.flush(); | ||
expect(element.attr('aria-expanded')).toBe('false'); | ||
expect(element.height()).toBeLessThan(expandedHeight); | ||
}); | ||
it('should not animate if it rejects', function() { | ||
defer.reject(); | ||
// should NOT collapse | ||
scope.$digest(); | ||
expect(element.attr('aria-expanded')).not.toBe('false'); | ||
expect(element.height()).toBe(expandedHeight); | ||
}); | ||
}); | ||
}); |
@@ -6,2 +6,3 @@ angular.module('ui.bootstrap.datepicker', ['ui.bootstrap.dateparser', 'ui.bootstrap.isClass', 'ui.bootstrap.position']) | ||
.constant('uibDatepickerConfig', { | ||
datepickerMode: 'day', | ||
formatDay: 'dd', | ||
@@ -13,20 +14,19 @@ formatMonth: 'MMMM', | ||
formatMonthTitle: 'yyyy', | ||
datepickerMode: 'day', | ||
maxDate: null, | ||
maxMode: 'year', | ||
minDate: null, | ||
minMode: 'day', | ||
maxMode: 'year', | ||
ngModelOptions: {}, | ||
shortcutPropagation: false, | ||
showWeeks: true, | ||
startingDay: 0, | ||
yearRows: 4, | ||
yearColumns: 5, | ||
minDate: null, | ||
maxDate: null, | ||
shortcutPropagation: false, | ||
ngModelOptions: {} | ||
yearRows: 4 | ||
}) | ||
.controller('UibDatepickerController', ['$scope', '$attrs', '$parse', '$interpolate', '$log', 'dateFilter', 'uibDatepickerConfig', '$datepickerSuppressError', 'uibDateParser', | ||
function($scope, $attrs, $parse, $interpolate, $log, dateFilter, datepickerConfig, $datepickerSuppressError, dateParser) { | ||
.controller('UibDatepickerController', ['$scope', '$attrs', '$parse', '$interpolate', '$locale', '$log', 'dateFilter', 'uibDatepickerConfig', '$datepickerSuppressError', 'uibDateParser', | ||
function($scope, $attrs, $parse, $interpolate, $locale, $log, dateFilter, datepickerConfig, $datepickerSuppressError, dateParser) { | ||
var self = this, | ||
ngModelCtrl = { $setViewValue: angular.noop }, // nullModelCtrl; | ||
ngModelOptions = {}; | ||
ngModelOptions = {}, | ||
watchListeners = []; | ||
@@ -42,13 +42,22 @@ // Modes chain | ||
// Evaled configuration attributes | ||
angular.forEach(['showWeeks', 'startingDay', 'yearRows', 'yearColumns', 'shortcutPropagation'], function(key) { | ||
self[key] = angular.isDefined($attrs[key]) ? $scope.$parent.$eval($attrs[key]) : datepickerConfig[key]; | ||
angular.forEach(['showWeeks', 'yearRows', 'yearColumns', 'shortcutPropagation'], function(key) { | ||
self[key] = angular.isDefined($attrs[key]) ? | ||
$scope.$parent.$eval($attrs[key]) : datepickerConfig[key]; | ||
}); | ||
if (angular.isDefined($attrs.startingDay)) { | ||
self.startingDay = $scope.$parent.$eval($attrs.startingDay); | ||
} else if (angular.isNumber(datepickerConfig.startingDay)) { | ||
self.startingDay = datepickerConfig.startingDay; | ||
} else { | ||
self.startingDay = ($locale.DATETIME_FORMATS.FIRSTDAYOFWEEK + 8) % 7; | ||
} | ||
// Watchable date attributes | ||
angular.forEach(['minDate', 'maxDate'], function(key) { | ||
if ($attrs[key]) { | ||
$scope.$parent.$watch($attrs[key], function(value) { | ||
watchListeners.push($scope.$parent.$watch($attrs[key], function(value) { | ||
self[key] = value ? angular.isDate(value) ? dateParser.fromTimezone(new Date(value), ngModelOptions.timezone) : new Date(dateFilter(value, 'medium')) : null; | ||
self.refreshView(); | ||
}); | ||
})); | ||
} else { | ||
@@ -61,3 +70,3 @@ self[key] = datepickerConfig[key] ? dateParser.fromTimezone(new Date(datepickerConfig[key]), ngModelOptions.timezone) : null; | ||
if ($attrs[key]) { | ||
$scope.$parent.$watch($attrs[key], function(value) { | ||
watchListeners.push($scope.$parent.$watch($attrs[key], function(value) { | ||
self[key] = $scope[key] = angular.isDefined(value) ? value : $attrs[key]; | ||
@@ -68,3 +77,3 @@ if (key === 'minMode' && self.modes.indexOf($scope.datepickerMode) < self.modes.indexOf(self[key]) || | ||
} | ||
}); | ||
})); | ||
} else { | ||
@@ -80,3 +89,3 @@ self[key] = $scope[key] = datepickerConfig[key] || null; | ||
this.activeDate = dateParser.fromTimezone($scope.$parent.$eval($attrs.initDate), ngModelOptions.timezone) || new Date(); | ||
$scope.$parent.$watch($attrs.initDate, function(initDate) { | ||
watchListeners.push($scope.$parent.$watch($attrs.initDate, function(initDate) { | ||
if (initDate && (ngModelCtrl.$isEmpty(ngModelCtrl.$modelValue) || ngModelCtrl.$invalid)) { | ||
@@ -86,3 +95,3 @@ self.activeDate = dateParser.fromTimezone(initDate, ngModelOptions.timezone); | ||
} | ||
}); | ||
})); | ||
} else { | ||
@@ -94,6 +103,6 @@ this.activeDate = new Date(); | ||
if (angular.isDefined($attrs.ngDisabled)) { | ||
$scope.$parent.$watch($attrs.ngDisabled, function(disabled) { | ||
watchListeners.push($scope.$parent.$watch($attrs.ngDisabled, function(disabled) { | ||
$scope.disabled = disabled; | ||
self.refreshView(); | ||
}); | ||
})); | ||
} | ||
@@ -259,2 +268,9 @@ | ||
}; | ||
$scope.$on("$destroy", function() { | ||
//Clear all watch listeners on destroy | ||
while (watchListeners.length) { | ||
watchListeners.shift()(); | ||
} | ||
}); | ||
}]) | ||
@@ -565,2 +581,8 @@ | ||
.constant('uibDatepickerPopupConfig', { | ||
altInputFormats: [], | ||
appendToBody: false, | ||
clearText: 'Clear', | ||
closeOnDateSelection: true, | ||
closeText: 'Done', | ||
currentText: 'Today', | ||
datepickerPopup: 'yyyy-MM-dd', | ||
@@ -574,10 +596,4 @@ datepickerPopupTemplateUrl: 'uib/template/datepicker/popup.html', | ||
}, | ||
currentText: 'Today', | ||
clearText: 'Clear', | ||
closeText: 'Done', | ||
closeOnDateSelection: true, | ||
appendToBody: false, | ||
showButtonBar: true, | ||
onOpenFocus: true, | ||
altInputFormats: [] | ||
showButtonBar: true | ||
}) | ||
@@ -587,3 +603,2 @@ | ||
function(scope, element, attrs, $compile, $parse, $document, $rootScope, $position, dateFilter, dateParser, datepickerPopupConfig, $timeout, datepickerConfig) { | ||
var self = this; | ||
var cache = {}, | ||
@@ -593,3 +608,3 @@ isHtml5DateInput = false; | ||
datepickerPopupTemplateUrl, datepickerTemplateUrl, popupEl, datepickerEl, | ||
ngModel, ngModelOptions, $popup, altInputFormats; | ||
ngModel, ngModelOptions, $popup, altInputFormats, watchListeners = []; | ||
@@ -616,13 +631,13 @@ scope.watchData = {}; | ||
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; | ||
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.'); | ||
} | ||
if (!dateFormat) { | ||
throw new Error('uibDatepickerPopup must have a date format specified.'); | ||
} | ||
} | ||
}); | ||
@@ -673,13 +688,4 @@ } | ||
angular.forEach(['minMode', 'maxMode'], function(key) { | ||
angular.forEach(['minMode', 'maxMode', 'datepickerMode', 'shortcutPropagation'], function(key) { | ||
if (attrs[key]) { | ||
scope.$parent.$watch(function() { return attrs[key]; }, function(value) { | ||
scope.watchData[key] = value; | ||
}); | ||
datepickerEl.attr(cameltoDash(key), 'watchData.' + key); | ||
} | ||
}); | ||
angular.forEach(['datepickerMode', 'shortcutPropagation'], function(key) { | ||
if (attrs[key]) { | ||
var getAttribute = $parse(attrs[key]); | ||
@@ -710,10 +716,18 @@ var propConfig = { | ||
scope.$parent.$watch(getAttribute, function(value) { | ||
watchListeners.push(scope.$parent.$watch(getAttribute, function(value) { | ||
if (key === 'minDate' || key === 'maxDate') { | ||
cache[key] = angular.isDate(value) ? dateParser.fromTimezone(new Date(value), ngModelOptions.timezone) : new Date(dateFilter(value, 'medium')); | ||
if (value === null) { | ||
cache[key] = null; | ||
} else if (angular.isDate(value)) { | ||
cache[key] = dateParser.fromTimezone(new Date(value), ngModelOptions.timezone); | ||
} else { | ||
cache[key] = new Date(dateFilter(value, 'medium')); | ||
} | ||
scope.watchData[key] = value === null ? null : cache[key]; | ||
} else { | ||
scope.watchData[key] = dateParser.fromTimezone(new Date(value), ngModelOptions.timezone); | ||
} | ||
})); | ||
scope.watchData[key] = cache[key] || dateParser.fromTimezone(new Date(value), ngModelOptions.timezone); | ||
}); | ||
datepickerEl.attr(cameltoDash(key), 'watchData.' + key); | ||
@@ -749,3 +763,3 @@ } | ||
dateFormat = dateFormat.replace(/M!/, 'MM') | ||
.replace(/d!/, 'dd'); | ||
.replace(/d!/, 'dd'); | ||
@@ -790,2 +804,7 @@ return dateFilter(scope.date, dateFormat); | ||
$document.unbind('click', documentClickBind); | ||
//Clear all watch listeners on destroy | ||
while (watchListeners.length) { | ||
watchListeners.shift()(); | ||
} | ||
}); | ||
@@ -804,3 +823,3 @@ }; | ||
return scope.watchData.minDate && scope.compare(date, cache.minDate) < 0 || | ||
scope.watchData.maxDate && scope.compare(date, cache.maxDate) > 0; | ||
scope.watchData.maxDate && scope.compare(date, cache.maxDate) > 0; | ||
}; | ||
@@ -855,5 +874,5 @@ | ||
if (attrs.ngDisabled) { | ||
scope.$parent.$watch($parse(attrs.ngDisabled), function(disabled) { | ||
watchListeners.push(scope.$parent.$watch($parse(attrs.ngDisabled), function(disabled) { | ||
scope.disabled = disabled; | ||
}); | ||
})); | ||
} | ||
@@ -860,0 +879,0 @@ |
@@ -16,9 +16,7 @@ Our datepicker is flexible and fully customizable. | ||
* `custom-class (date, mode)` | ||
<small class="badge">$</small> | ||
_(Default: `null`)_ - | ||
<small class="badge">$</small> - | ||
An optional expression to add classes based on passing a date and current mode. | ||
* `date-disabled (date, mode)` | ||
<small class="badge">$</small> | ||
_(Default: `null`)_ - | ||
<small class="badge">$</small> - | ||
An optional expression to disable visible options based on passing a date and current mode. | ||
@@ -101,3 +99,3 @@ | ||
The date object. Needs to be a Javascript Date object. | ||
* `ng-model-options` | ||
@@ -126,3 +124,3 @@ <small class="badge">$</small> | ||
<small class="badge">C</small> | ||
_(Default: `0`)_ - | ||
_(Default: `$locale.DATETIME_FORMATS.FIRSTDAYOFWEEK`)_ - | ||
Starting day of the week from 0-6 (0=Sunday, ..., 6=Saturday). | ||
@@ -148,3 +146,3 @@ | ||
Options for the uib-datepicker must be passed as JSON using the `datepicker-options` attribute. This list is only for popup settings. | ||
The popup is a wrapper that you can use in an input to toggle a datepicker. To configure the datepicker, use `datepicker-options`. | ||
@@ -184,2 +182,6 @@ * `alt-input-formats` | ||
* `datepicker-options` | ||
<small class="badge">$</small> - | ||
An object with any combination of the datepicker settings (in camelCase) used to configure the wrapped datepicker. | ||
* `datepicker-popup-template-url` | ||
@@ -212,3 +214,3 @@ <small class="badge">C</small> | ||
Whether or not to display a button bar underneath the uib-datepicker. | ||
* `type` | ||
@@ -222,4 +224,7 @@ <small class="badge">C</small> | ||
_(Default: `yyyy-MM-dd`, Config: `datepickerConfig`)_ - | ||
The format for displayed dates. This string can take string literals by surrounding the value with backticks, i.e. ``yyyy-MM-dd h `o'clock` ``. | ||
The format for displayed dates. This string can take string literals by surrounding the value with single quotes, i.e. `yyyy-MM-dd h 'o\'clock'`. | ||
**Note:** With the exception of `ng-model[-options]` and `templateUrl`, you can configure the wrapped datepicker using its attributes in the popup as well. | ||
But beware **this possibility may be deprecated on the near future.** | ||
### Keyboard support | ||
@@ -245,1 +250,3 @@ | ||
If the date a user enters falls outside of the min-/max-date range, a `dateDisabled` validation error will show on the form. | ||
If using this directive on input type date, a native browser datepicker could also appear. |
@@ -41,2 +41,6 @@ Dropdown is a simple directive which will toggle a dropdown menu on click or programmatically. | ||
* `on-toggle(open)` | ||
<small class="badge">$</small> - | ||
An optional expression called when the dropdown menu is opened or closed. | ||
### uib-dropdown-menu settings | ||
@@ -43,0 +47,0 @@ |
@@ -480,5 +480,4 @@ angular.module('ui.bootstrap.modal', ['ui.bootstrap.stackedMap']) | ||
$animate.enter(angularDomEl, appendToElement) | ||
$animate.enter($compile(angularDomEl)(modal.scope), appendToElement) | ||
.then(function() { | ||
$compile(angularDomEl)(modal.scope); | ||
$animate.addClass(appendToElement, modalBodyClass); | ||
@@ -485,0 +484,0 @@ }); |
@@ -49,3 +49,3 @@ describe('$uibResolve', function() { | ||
describe('$uibModal', function () { | ||
describe('$uibModal', function() { | ||
var $animate, $controllerProvider, $rootScope, $document, $compile, $templateCache, $timeout, $q; | ||
@@ -217,8 +217,12 @@ var $uibModal, $uibModalStack, $uibModalProvider; | ||
function open(modalOptions, noFlush) { | ||
function open(modalOptions, noFlush, noDigest) { | ||
var modal = $uibModal.open(modalOptions); | ||
$rootScope.$digest(); | ||
if (!noFlush) { | ||
$animate.flush(); | ||
if (!noDigest) { | ||
$rootScope.$digest(); | ||
if (!noFlush) { | ||
$animate.flush(); | ||
} | ||
} | ||
return modal; | ||
@@ -266,2 +270,42 @@ } | ||
it('should compile modal before inserting into DOM', function() { | ||
var topModal; | ||
var modalInstance = { | ||
result: $q.defer(), | ||
opened: $q.defer(), | ||
closed: $q.defer(), | ||
rendered: $q.defer(), | ||
close: function (result) { | ||
return $uibModalStack.close(modalInstance, result); | ||
}, | ||
dismiss: function (reason) { | ||
return $uibModalStack.dismiss(modalInstance, reason); | ||
} | ||
}; | ||
var expectedText = 'test'; | ||
$uibModalStack.open(modalInstance, { | ||
appendTo: angular.element(document.body), | ||
scope: $rootScope.$new(), | ||
deferred: modalInstance.result, | ||
renderDeferred: modalInstance.rendered, | ||
closedDeferred: modalInstance.closed, | ||
content: '<div id="test">{{\'' + expectedText + '\'}}</div>' | ||
}); | ||
topModal = $uibModalStack.getTop(); | ||
expect(topModal.value.modalDomEl.find('#test').length).toEqual(0); | ||
expect(angular.element('#test').length).toEqual(0); | ||
$rootScope.$digest(); | ||
expect(topModal.value.modalDomEl.find('#test').text()).toEqual(expectedText); | ||
expect(angular.element('#test').text()).toEqual(expectedText); | ||
$animate.flush(); | ||
close(modalInstance, 'closing in test', true); | ||
}); | ||
it('should not throw an exception on a second dismiss', function() { | ||
@@ -370,3 +414,2 @@ var modal = open({template: '<div>Content</div>'}); | ||
var modal = open({template: '<div>Content<button>inside modal</button></div>'}); | ||
$animate.flush(); | ||
$rootScope.$digest(); | ||
@@ -396,3 +439,2 @@ expect(document.activeElement.tagName).toBe('DIV'); | ||
var modal = open({template: '<div>Content</div>'}); | ||
$animate.flush(); | ||
$rootScope.$digest(); | ||
@@ -476,3 +518,2 @@ expect(document.activeElement.tagName).toBe('DIV'); | ||
var modal = open({template: '<div><input type="text" id="auto-focus-element" autofocus></div>'}); | ||
$animate.flush(); | ||
$rootScope.$digest(); | ||
@@ -494,3 +535,2 @@ expect(angular.element('#auto-focus-element')).toHaveFocus(); | ||
var modal = open({template: '<div><input type="text" id="auto-focus-element" autofocus><input type="text" id="pre-focus-element" focus-me></div>'}); | ||
$animate.flush(); | ||
$rootScope.$digest(); | ||
@@ -511,7 +551,7 @@ expect(angular.element('#auto-focus-element')).not.toHaveFocus(); | ||
function openAndCloseModalWithAutofocusElement() { | ||
var modal = open({template: '<div><input type="text" id="auto-focus-element" autofocus></div>'}); | ||
var modal = open({template: '<div><input type="text" id="auto-focus-element" autofocus></div>'}, true, true); | ||
expect(angular.element('#auto-focus-element')).not.toHaveFocus(); | ||
$rootScope.$digest(); | ||
$animate.flush(); | ||
$rootScope.$digest(); | ||
@@ -532,7 +572,7 @@ expect(angular.element('#auto-focus-element')).toHaveFocus(); | ||
var modal = open({template: '<div><input type="text"></div>'}); | ||
var modal = open({template: '<div><input type="text"></div>'}, true, true); | ||
expect(document.activeElement.tagName).toBe('A'); | ||
$rootScope.$digest(); | ||
$animate.flush(); | ||
$rootScope.$digest(); | ||
@@ -800,3 +840,2 @@ expect(document.activeElement.tagName).toBe('DIV'); | ||
$timeout.flush(); | ||
$animate.flush(); | ||
expect($document).toHaveModalOpenWithContent('Promise', 'div'); | ||
@@ -899,3 +938,2 @@ }); | ||
var modal = open({ template: '<div>With backdrop</div>' }); | ||
$animate.flush(); | ||
var backdropEl = $document.find('body > div.modal-backdrop'); | ||
@@ -907,3 +945,2 @@ expect(backdropEl).toHaveClass('in'); | ||
modal = open({ template: '<div>With backdrop</div>' }); | ||
$animate.flush(); | ||
backdropEl = $document.find('body > div.modal-backdrop'); | ||
@@ -1046,3 +1083,2 @@ expect(backdropEl).toHaveClass('in'); | ||
open({template: '<div child-directive>{{text}}</div>', appendTo: element}); | ||
$animate.flush(); | ||
expect($document.find('[child-directive]').text()).toBe('foo'); | ||
@@ -1066,4 +1102,2 @@ | ||
$animate.flush(); | ||
expect(body).toHaveClass('modal-open'); | ||
@@ -1078,4 +1112,2 @@ }); | ||
$animate.flush(); | ||
expect(body).toHaveClass('foo'); | ||
@@ -1091,4 +1123,2 @@ expect(body).not.toHaveClass('modal-open'); | ||
$animate.flush(); | ||
expect(body).toHaveClass('foo'); | ||
@@ -1107,4 +1137,2 @@ | ||
$animate.flush(); | ||
expect(body).toHaveClass('foo'); | ||
@@ -1118,4 +1146,2 @@ expect(body).not.toHaveClass('modal-open'); | ||
$animate.flush(); | ||
expect(body).toHaveClass('foo'); | ||
@@ -1130,4 +1156,2 @@ expect(body).toHaveClass('bar'); | ||
$animate.flush(); | ||
expect(body).toHaveClass('foo'); | ||
@@ -1254,7 +1278,5 @@ expect(body).toHaveClass('bar'); | ||
var modal1 = open({template: '<div>Content1</div>'}); | ||
$animate.flush(); | ||
expect(body).toHaveClass('modal-open'); | ||
var modal2 = open({template: '<div>Content1</div>'}); | ||
$animate.flush(); | ||
expect(body).toHaveClass('modal-open'); | ||
@@ -1277,3 +1299,2 @@ | ||
var modal1 = open({template: '<div>Modal1<button id="focus">inside modal1</button></div>'}); | ||
$animate.flush(); | ||
$rootScope.$digest(); | ||
@@ -1285,3 +1306,2 @@ document.getElementById('focus').focus(); | ||
var modal2 = open({template: '<div>Modal2</div>'}); | ||
$animate.flush(); | ||
$rootScope.$digest(); | ||
@@ -1503,2 +1523,1 @@ expect(document.activeElement.tagName).toBe('DIV'); | ||
}); | ||
@@ -15,6 +15,6 @@ angular.module('ui.bootstrap.pagination', ['ui.bootstrap.paging']) | ||
if ($attrs.maxSize) { | ||
$scope.$parent.$watch($parse($attrs.maxSize), function(value) { | ||
ctrl._watchers.push($scope.$parent.$watch($parse($attrs.maxSize), function(value) { | ||
maxSize = parseInt(value, 10); | ||
ctrl.render(); | ||
}); | ||
})); | ||
} | ||
@@ -21,0 +21,0 @@ |
@@ -11,2 +11,3 @@ angular.module('ui.bootstrap.paging', []) | ||
ctrl.ngModelCtrl = { $setViewValue: angular.noop }; // nullModelCtrl | ||
ctrl._watchers = []; | ||
@@ -22,7 +23,7 @@ ctrl.init = function(ngModelCtrl, config) { | ||
if ($attrs.itemsPerPage) { | ||
$scope.$parent.$watch($parse($attrs.itemsPerPage), function(value) { | ||
ctrl._watchers.push($scope.$parent.$watch($parse($attrs.itemsPerPage), function(value) { | ||
ctrl.itemsPerPage = parseInt(value, 10); | ||
$scope.totalPages = ctrl.calculateTotalPages(); | ||
ctrl.updatePage(); | ||
}); | ||
})); | ||
} else { | ||
@@ -85,4 +86,10 @@ ctrl.itemsPerPage = config.itemsPerPage; | ||
}; | ||
$scope.$on('$destroy', function() { | ||
while (ctrl._watchers.length) { | ||
ctrl._watchers.shift()(); | ||
} | ||
}); | ||
} | ||
}; | ||
}]); |
@@ -256,2 +256,16 @@ describe('paging factory', function() { | ||
}); | ||
describe('gc', function() { | ||
it('should clear watchers', function() { | ||
var watcher1 = jasmine.createSpy('watcher1'), | ||
watcher2 = jasmine.createSpy('watcher2'); | ||
ctrl._watchers = [watcher1, watcher2]; | ||
$scope.$destroy(); | ||
expect(ctrl._watchers.length).toBe(0); | ||
expect(watcher1).toHaveBeenCalled(); | ||
expect(watcher2).toHaveBeenCalled(); | ||
}); | ||
}); | ||
}); |
@@ -467,13 +467,9 @@ angular.module('ui.bootstrap.position', []) | ||
var isTooltip = true; | ||
var innerElem = elem.querySelector('.tooltip-inner'); | ||
var innerElem = elem.querySelector('.tooltip-inner, .popover-inner'); | ||
if (!innerElem) { | ||
isTooltip = false; | ||
innerElem = elem.querySelector('.popover-inner'); | ||
} | ||
if (!innerElem) { | ||
return; | ||
} | ||
var isTooltip = angular.element(innerElem).hasClass('tooltip-inner'); | ||
var arrowElem = isTooltip ? elem.querySelector('.tooltip-arrow') : elem.querySelector('.arrow'); | ||
@@ -480,0 +476,0 @@ if (!arrowElem) { |
@@ -26,6 +26,5 @@ AngularJS version of the tabs directive. | ||
Whether tab is currently selected. | ||
* `deselect()` | ||
<small class="badge">$</small> | ||
_(Default: `null`)_ - | ||
<small class="badge">$</small> - | ||
An optional expression called when tab is deactivated. | ||
@@ -43,4 +42,3 @@ | ||
* `select()` | ||
<small class="badge">$</small> | ||
_(Default: `null`)_ - | ||
<small class="badge">$</small> - | ||
An optional expression called when tab is activated. | ||
@@ -51,1 +49,5 @@ | ||
Instead of the `heading` attribute on the `uib-tabset`, you can use an `uib-tab-heading` element inside a tabset that will be used as the tabset's header. There you can use HTML as well. | ||
### Known issues | ||
To use clickable elements within the tab, you have override the tab template to use div elements instead of anchor elements, and replicate the desired styles from Bootstrap's CSS. This is due to browsers interpreting anchor elements as the target of any click event, which triggers routing when certain elements such as buttons are nested inside the anchor element. |
@@ -64,6 +64,6 @@ describe('tabs', function() { | ||
expect(t.length).toBe(2); | ||
expect(t.find('> div').eq(0).text()).toBe('First Tab 1'); | ||
expect(t.find('> a').eq(0).text()).toBe('First Tab 1'); | ||
//It should put the uib-tab-heading element into the 'a' title | ||
expect(t.find('> div').eq(1).children().is('uib-tab-heading')).toBe(true); | ||
expect(t.find('> div').eq(1).children().html()).toBe('<b>Second</b> Tab 2'); | ||
expect(t.find('> a').eq(1).children().is('uib-tab-heading')).toBe(true); | ||
expect(t.find('> a').eq(1).children().html()).toBe('<b>Second</b> Tab 2'); | ||
}); | ||
@@ -80,3 +80,3 @@ | ||
it('should change active on click', function() { | ||
titles().eq(1).find('> div').click(); | ||
titles().eq(1).find('> a').click(); | ||
expect(contents().eq(1)).toHaveClass('active'); | ||
@@ -90,5 +90,5 @@ expect(titles().eq(0)).not.toHaveClass('active'); | ||
it('should call select callback on select', function() { | ||
titles().eq(1).find('> div').click(); | ||
titles().eq(1).find('> a').click(); | ||
expect(scope.selectSecond).toHaveBeenCalled(); | ||
titles().eq(0).find('> div').click(); | ||
titles().eq(0).find('> a').click(); | ||
expect(scope.selectFirst).toHaveBeenCalled(); | ||
@@ -98,6 +98,6 @@ }); | ||
it('should call deselect callback on deselect', function() { | ||
titles().eq(1).find('> div').click(); | ||
titles().eq(0).find('> div').click(); | ||
titles().eq(1).find('> a').click(); | ||
titles().eq(0).find('> a').click(); | ||
expect(scope.deselectSecond).toHaveBeenCalled(); | ||
titles().eq(1).find('> div').click(); | ||
titles().eq(1).find('> a').click(); | ||
expect(scope.deselectFirst).toHaveBeenCalled(); | ||
@@ -188,3 +188,3 @@ }); | ||
// Select second tab | ||
titles().eq(1).find('> div').click(); | ||
titles().eq(1).find('> a').click(); | ||
expect(execOrder).toEqual([ 'deselect1', 'select2' ]); | ||
@@ -195,3 +195,3 @@ | ||
// Select again first tab | ||
titles().eq(0).find('> div').click(); | ||
titles().eq(0).find('> a').click(); | ||
expect(execOrder).toEqual([ 'deselect2', 'select1' ]); | ||
@@ -286,3 +286,3 @@ }); | ||
it('should switch active when clicking', function() { | ||
titles().eq(3).find('> div').click(); | ||
titles().eq(3).find('> a').click(); | ||
expectTabActive(scope.tabs[3]); | ||
@@ -354,3 +354,3 @@ }); | ||
function heading() { | ||
return elm.find('ul li > div').children(); | ||
return elm.find('ul li > a').children(); | ||
} | ||
@@ -417,3 +417,3 @@ | ||
function titles() { | ||
return elm.find('ul.nav-tabs li > div'); | ||
return elm.find('ul.nav-tabs li > a'); | ||
} | ||
@@ -561,3 +561,3 @@ scope.$apply(); | ||
// Select last tab | ||
titles().find('> div').eq(3).click(); | ||
titles().find('> a').eq(3).click(); | ||
expect(contents().eq(3)).toHaveClass('active'); | ||
@@ -576,3 +576,3 @@ expect(titles().eq(3)).toHaveClass('active'); | ||
// Select 2nd tab ("tab 1") | ||
titles().find('> div').eq(1).click(); | ||
titles().find('> a').eq(1).click(); | ||
expect(titles().eq(1)).toHaveClass('active'); | ||
@@ -673,6 +673,6 @@ expect(contents().eq(1)).toHaveClass('active'); | ||
it('should not switch active when clicking on title', function() { | ||
titles().eq(2).find('> div').click(); | ||
titles().eq(2).find('> a').click(); | ||
expectTabActive(scope.tabs[2]); | ||
titles().eq(3).find('> div').click(); | ||
titles().eq(3).find('> a').click(); | ||
expectTabActive(scope.tabs[2]); | ||
@@ -679,0 +679,0 @@ }); |
@@ -17,3 +17,3 @@ A lightweight & configurable timepicker directive. | ||
Number of hours to increase or decrease when using a button. | ||
* `max` | ||
@@ -43,3 +43,3 @@ <small class="badge">$</small> | ||
Number of minutes to increase or decrease when using a button. | ||
* `mousewheel` | ||
@@ -103,1 +103,5 @@ <small class="badge">$</small> | ||
Add the ability to override the template used on the component. | ||
**Notes** | ||
If the model value is updated (i.e. via `Date.prototype.setDate`), you must update the model value by breaking the reference by `modelValue = new Date(modelValue)` in order to have the timepicker update. |
@@ -19,4 +19,5 @@ angular.module('ui.bootstrap.timepicker', []) | ||
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; | ||
watchers = [], | ||
ngModelCtrl = { $setViewValue: angular.noop }, // nullModelCtrl | ||
meridians = angular.isDefined($attrs.meridians) ? $scope.$parent.$eval($attrs.meridians) : timepickerConfig.meridians || $locale.DATETIME_FORMATS.AMPMS; | ||
@@ -55,5 +56,5 @@ $scope.tabindex = angular.isDefined($attrs.tabindex) ? $attrs.tabindex : 0; | ||
if ($attrs.hourStep) { | ||
$scope.$parent.$watch($parse($attrs.hourStep), function(value) { | ||
watchers.push($scope.$parent.$watch($parse($attrs.hourStep), function(value) { | ||
hourStep = +value; | ||
}); | ||
})); | ||
} | ||
@@ -63,24 +64,24 @@ | ||
if ($attrs.minuteStep) { | ||
$scope.$parent.$watch($parse($attrs.minuteStep), function(value) { | ||
watchers.push($scope.$parent.$watch($parse($attrs.minuteStep), function(value) { | ||
minuteStep = +value; | ||
}); | ||
})); | ||
} | ||
var min; | ||
$scope.$parent.$watch($parse($attrs.min), function(value) { | ||
watchers.push($scope.$parent.$watch($parse($attrs.min), function(value) { | ||
var dt = new Date(value); | ||
min = isNaN(dt) ? undefined : dt; | ||
}); | ||
})); | ||
var max; | ||
$scope.$parent.$watch($parse($attrs.max), function(value) { | ||
watchers.push($scope.$parent.$watch($parse($attrs.max), function(value) { | ||
var dt = new Date(value); | ||
max = isNaN(dt) ? undefined : dt; | ||
}); | ||
})); | ||
var disabled = false; | ||
if ($attrs.ngDisabled) { | ||
$scope.$parent.$watch($parse($attrs.ngDisabled), function(value) { | ||
watchers.push($scope.$parent.$watch($parse($attrs.ngDisabled), function(value) { | ||
disabled = value; | ||
}); | ||
})); | ||
} | ||
@@ -134,5 +135,5 @@ | ||
if ($attrs.secondStep) { | ||
$scope.$parent.$watch($parse($attrs.secondStep), function(value) { | ||
watchers.push($scope.$parent.$watch($parse($attrs.secondStep), function(value) { | ||
secondStep = +value; | ||
}); | ||
})); | ||
} | ||
@@ -142,5 +143,5 @@ | ||
if ($attrs.showSeconds) { | ||
$scope.$parent.$watch($parse($attrs.showSeconds), function(value) { | ||
watchers.push($scope.$parent.$watch($parse($attrs.showSeconds), function(value) { | ||
$scope.showSeconds = !!value; | ||
}); | ||
})); | ||
} | ||
@@ -151,3 +152,3 @@ | ||
if ($attrs.showMeridian) { | ||
$scope.$parent.$watch($parse($attrs.showMeridian), function(value) { | ||
watchers.push($scope.$parent.$watch($parse($attrs.showMeridian), function(value) { | ||
$scope.showMeridian = !!value; | ||
@@ -165,3 +166,3 @@ | ||
} | ||
}); | ||
})); | ||
} | ||
@@ -528,2 +529,8 @@ | ||
}; | ||
$scope.$on('$destroy', function() { | ||
while (watchers.length) { | ||
watchers.shift()(); | ||
} | ||
}); | ||
}]) | ||
@@ -530,0 +537,0 @@ |
@@ -1131,2 +1131,33 @@ describe('tooltip', function() { | ||
}); | ||
describe('placementClassPrefix', function() { | ||
beforeEach(module('ui.bootstrap.tooltip', function($uibTooltipProvider) { | ||
$uibTooltipProvider.options({placementClassPrefix: 'uib-'}); | ||
})); | ||
// load the template | ||
beforeEach(module('uib/template/tooltip/tooltip-popup.html')); | ||
it('should add the classes', inject(function($rootScope, $compile, $timeout) { | ||
elmBody = angular.element( | ||
'<div><span uib-tooltip="tooltip text" tooltip-placement="top-right"></span></div>' | ||
); | ||
scope = $rootScope; | ||
$compile(elmBody)(scope); | ||
scope.$digest(); | ||
elm = elmBody.find('span'); | ||
elmScope = elm.scope(); | ||
tooltipScope = elmScope.$$childTail; | ||
expect(elmBody.children().length).toBe(1); | ||
trigger(elm, 'mouseenter'); | ||
$timeout.flush(); | ||
var tooltipElm = elmBody.find('.tooltip'); | ||
expect(tooltipElm.hasClass('top')).toBe(true); | ||
expect(tooltipElm.hasClass('uib-top-right')).toBe(true); | ||
})); | ||
}); | ||
}); |
@@ -192,3 +192,3 @@ /** | ||
var placement = ttPosition.placement.split('-'); | ||
tooltip.addClass(placement[0], options.placementClassPrefix + ttPosition.placement); | ||
tooltip.addClass(placement[0] + ' ' + options.placementClassPrefix + ttPosition.placement); | ||
$position.positionArrow(tooltip, ttPosition.placement); | ||
@@ -291,14 +291,16 @@ | ||
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); | ||
if(ttScope){ | ||
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(); | ||
} | ||
} else { | ||
removeTooltip(); | ||
} | ||
@@ -305,0 +307,0 @@ }); |
@@ -6,3 +6,3 @@ angular.module("uib/template/accordion/accordion-group.html", []).run(["$templateCache", function($templateCache) { | ||
" <h4 class=\"panel-title\">\n" + | ||
" <div tabindex=\"0\" class=\"accordion-toggle\" ng-click=\"toggleOpen()\" uib-accordion-transclude=\"heading\"><span ng-class=\"{'text-muted': isDisabled}\">{{heading}}</span></div>\n" + | ||
" <a href tabindex=\"0\" class=\"accordion-toggle\" ng-click=\"toggleOpen()\" uib-accordion-transclude=\"heading\"><span ng-class=\"{'text-muted': isDisabled}\">{{heading}}</span></a>\n" + | ||
" </h4>\n" + | ||
@@ -9,0 +9,0 @@ " </div>\n" + |
@@ -7,3 +7,3 @@ angular.module("uib/template/datepicker/day.html", []).run(["$templateCache", function($templateCache) { | ||
" <th><button type=\"button\" class=\"btn btn-default btn-sm pull-left uib-left\" ng-click=\"move(-1)\" tabindex=\"-1\"><i class=\"glyphicon glyphicon-chevron-left\"></i></button></th>\n" + | ||
" <th colspan=\"{{::5 + showWeeks}}\"><button id=\"{{::uniqueId}}-title\" role=\"heading\" aria-live=\"assertive\" aria-atomic=\"true\" type=\"button\" class=\"btn btn-default btn-sm uib-title\" ng-click=\"toggleMode()\" ng-disabled=\"datepickerMode === maxMode\" tabindex=\"-1\" style=\"width:100%;\"><strong>{{title}}</strong></button></th>\n" + | ||
" <th colspan=\"{{::5 + showWeeks}}\"><button id=\"{{::uniqueId}}-title\" role=\"heading\" aria-live=\"assertive\" aria-atomic=\"true\" type=\"button\" class=\"btn btn-default btn-sm uib-title\" ng-click=\"toggleMode()\" ng-disabled=\"datepickerMode === maxMode\" tabindex=\"-1\"><strong>{{title}}</strong></button></th>\n" + | ||
" <th><button type=\"button\" class=\"btn btn-default btn-sm pull-right uib-right\" ng-click=\"move(1)\" tabindex=\"-1\"><i class=\"glyphicon glyphicon-chevron-right\"></i></button></th>\n" + | ||
@@ -22,3 +22,3 @@ " </tr>\n" + | ||
" ng-class=\"::dt.customClass\">\n" + | ||
" <button type=\"button\" style=\"min-width:100%;\" class=\"btn btn-default btn-sm\"\n" + | ||
" <button type=\"button\" class=\"btn btn-default btn-sm\"\n" + | ||
" uib-is-class=\"\n" + | ||
@@ -25,0 +25,0 @@ " 'btn-info' for selectedDt,\n" + |
@@ -7,3 +7,3 @@ angular.module("uib/template/datepicker/month.html", []).run(["$templateCache", function($templateCache) { | ||
" <th><button type=\"button\" class=\"btn btn-default btn-sm pull-left uib-left\" ng-click=\"move(-1)\" tabindex=\"-1\"><i class=\"glyphicon glyphicon-chevron-left\"></i></button></th>\n" + | ||
" <th><button id=\"{{::uniqueId}}-title\" role=\"heading\" aria-live=\"assertive\" aria-atomic=\"true\" type=\"button\" class=\"btn btn-default btn-sm uib-title\" ng-click=\"toggleMode()\" ng-disabled=\"datepickerMode === maxMode\" tabindex=\"-1\" style=\"width:100%;\"><strong>{{title}}</strong></button></th>\n" + | ||
" <th><button id=\"{{::uniqueId}}-title\" role=\"heading\" aria-live=\"assertive\" aria-atomic=\"true\" type=\"button\" class=\"btn btn-default btn-sm uib-title\" ng-click=\"toggleMode()\" ng-disabled=\"datepickerMode === maxMode\" tabindex=\"-1\"><strong>{{title}}</strong></button></th>\n" + | ||
" <th><button type=\"button\" class=\"btn btn-default btn-sm pull-right uib-right\" ng-click=\"move(1)\" tabindex=\"-1\"><i class=\"glyphicon glyphicon-chevron-right\"></i></button></th>\n" + | ||
@@ -17,3 +17,3 @@ " </tr>\n" + | ||
" ng-class=\"::dt.customClass\">\n" + | ||
" <button type=\"button\" style=\"min-width:100%;\" class=\"btn btn-default\"\n" + | ||
" <button type=\"button\" class=\"btn btn-default\"\n" + | ||
" uib-is-class=\"\n" + | ||
@@ -20,0 +20,0 @@ " 'btn-info' for selectedDt,\n" + |
angular.module("uib/template/datepicker/popup.html", []).run(["$templateCache", function($templateCache) { | ||
$templateCache.put("uib/template/datepicker/popup.html", | ||
"<ul class=\"uib-datepicker-popup dropdown-menu\" dropdown-nested ng-if=\"isOpen\" style=\"display: block\" ng-style=\"{top: position.top+'px', left: position.left+'px'}\" ng-keydown=\"keydown($event)\" ng-click=\"$event.stopPropagation()\">\n" + | ||
" <li ng-transclude></li>\n" + | ||
" <li ng-if=\"showButtonBar\" style=\"padding:10px 9px 2px\" class=\"uib-button-bar\">\n" + | ||
" <span class=\"btn-group pull-left\">\n" + | ||
" <button type=\"button\" class=\"btn btn-sm btn-info uib-datepicker-current\" ng-click=\"select('today')\" ng-disabled=\"isDisabled('today')\">{{ getText('current') }}</button>\n" + | ||
" <button type=\"button\" class=\"btn btn-sm btn-danger uib-clear\" ng-click=\"select(null)\">{{ getText('clear') }}</button>\n" + | ||
" </span>\n" + | ||
" <button type=\"button\" class=\"btn btn-sm btn-success pull-right uib-close\" ng-click=\"close()\">{{ getText('close') }}</button>\n" + | ||
" </li>\n" + | ||
"</ul>\n" + | ||
"<div>\n" + | ||
" <ul class=\"uib-datepicker-popup dropdown-menu\" dropdown-nested ng-if=\"isOpen\" ng-style=\"{top: position.top+'px', left: position.left+'px'}\" ng-keydown=\"keydown($event)\" ng-click=\"$event.stopPropagation()\">\n" + | ||
" <li ng-transclude></li>\n" + | ||
" <li ng-if=\"showButtonBar\" class=\"uib-button-bar\">\n" + | ||
" <span class=\"btn-group pull-left\">\n" + | ||
" <button type=\"button\" class=\"btn btn-sm btn-info uib-datepicker-current\" ng-click=\"select('today')\" ng-disabled=\"isDisabled('today')\">{{ getText('current') }}</button>\n" + | ||
" <button type=\"button\" class=\"btn btn-sm btn-danger uib-clear\" ng-click=\"select(null)\">{{ getText('clear') }}</button>\n" + | ||
" </span>\n" + | ||
" <button type=\"button\" class=\"btn btn-sm btn-success pull-right uib-close\" ng-click=\"close()\">{{ getText('close') }}</button>\n" + | ||
" </li>\n" + | ||
" </ul>\n" + | ||
"</div>\n" + | ||
""); | ||
}]); |
@@ -7,3 +7,3 @@ angular.module("uib/template/datepicker/year.html", []).run(["$templateCache", function($templateCache) { | ||
" <th><button type=\"button\" class=\"btn btn-default btn-sm pull-left uib-left\" ng-click=\"move(-1)\" tabindex=\"-1\"><i class=\"glyphicon glyphicon-chevron-left\"></i></button></th>\n" + | ||
" <th colspan=\"{{::columns - 2}}\"><button id=\"{{::uniqueId}}-title\" role=\"heading\" aria-live=\"assertive\" aria-atomic=\"true\" type=\"button\" class=\"btn btn-default btn-sm uib-title\" ng-click=\"toggleMode()\" ng-disabled=\"datepickerMode === maxMode\" tabindex=\"-1\" style=\"width:100%;\"><strong>{{title}}</strong></button></th>\n" + | ||
" <th colspan=\"{{::columns - 2}}\"><button id=\"{{::uniqueId}}-title\" role=\"heading\" aria-live=\"assertive\" aria-atomic=\"true\" type=\"button\" class=\"btn btn-default btn-sm uib-title\" ng-click=\"toggleMode()\" ng-disabled=\"datepickerMode === maxMode\" tabindex=\"-1\"><strong>{{title}}</strong></button></th>\n" + | ||
" <th><button type=\"button\" class=\"btn btn-default btn-sm pull-right uib-right\" ng-click=\"move(1)\" tabindex=\"-1\"><i class=\"glyphicon glyphicon-chevron-right\"></i></button></th>\n" + | ||
@@ -17,3 +17,3 @@ " </tr>\n" + | ||
" ng-class=\"::dt.customClass\">\n" + | ||
" <button type=\"button\" style=\"min-width:100%;\" class=\"btn btn-default\"\n" + | ||
" <button type=\"button\" class=\"btn btn-default\"\n" + | ||
" uib-is-class=\"\n" + | ||
@@ -20,0 +20,0 @@ " 'btn-info' for selectedDt,\n" + |
@@ -7,5 +7,5 @@ angular.module("uib/template/modal/window.html", []).run(["$templateCache", function($templateCache) { | ||
" ng-style=\"{'z-index': 1050 + index*10, display: 'block'}\">\n" + | ||
" <div class=\"modal-dialog\" ng-class=\"size ? 'modal-' + size : ''\"><div class=\"modal-content\" uib-modal-transclude></div></div>\n" + | ||
" <div class=\"modal-dialog {{size ? 'modal-' + size : ''}}\"><div class=\"modal-content\" uib-modal-transclude></div></div>\n" + | ||
"</div>\n" + | ||
""); | ||
}]); |
angular.module("uib/template/tabs/tab.html", []).run(["$templateCache", function($templateCache) { | ||
$templateCache.put("uib/template/tabs/tab.html", | ||
"<li ng-class=\"{active: active, disabled: disabled}\" class=\"uib-tab\">\n" + | ||
" <div ng-click=\"select()\" uib-tab-heading-transclude>{{heading}}</div>\n" + | ||
" <a href ng-click=\"select()\" uib-tab-heading-transclude>{{heading}}</a>\n" + | ||
"</li>\n" + | ||
""); | ||
}]); |
@@ -23,3 +23,3 @@ angular.module("uib/template/timepicker/timepicker.html", []).run(["$templateCache", function($templateCache) { | ||
" <td class=\"form-group uib-time seconds\" ng-class=\"{'has-error': invalidSeconds}\" ng-show=\"showSeconds\">\n" + | ||
" <input style=\"width:50px;\" type=\"text\" ng-model=\"seconds\" ng-change=\"updateSeconds()\" class=\"form-control text-center\" ng-readonly=\"readonlyInput\" maxlength=\"2\" tabindex=\"{{::tabindex}}\" ng-disabled=\"noIncrementSeconds()\" ng-blur=\"blur()\">\n" + | ||
" <input style=\"width:50px;\" type=\"text\" placeholder=\"SS\" ng-model=\"seconds\" ng-change=\"updateSeconds()\" class=\"form-control text-center\" ng-readonly=\"readonlyInput\" maxlength=\"2\" tabindex=\"{{::tabindex}}\" ng-disabled=\"noIncrementSeconds()\" ng-blur=\"blur()\">\n" + | ||
" </td>\n" + | ||
@@ -26,0 +26,0 @@ " <td ng-show=\"showMeridian\" class=\"uib-time am-pm\"><button type=\"button\" ng-class=\"{disabled: noToggleMeridian()}\" class=\"btn btn-default text-center\" ng-click=\"toggleMeridian()\" ng-disabled=\"noToggleMeridian()\" tabindex=\"{{::tabindex}}\">{{meridian}}</button></td>\n" + |
angular.module("uib/template/typeahead/typeahead-match.html", []).run(["$templateCache", function($templateCache) { | ||
$templateCache.put("uib/template/typeahead/typeahead-match.html", | ||
"<a href tabindex=\"-1\" ng-bind-html=\"match.label | uibTypeaheadHighlight:query\"></a>\n" + | ||
"<a href\n" + | ||
" tabindex=\"-1\"\n" + | ||
" ng-bind-html=\"match.label | uibTypeaheadHighlight:query\"\n" + | ||
" ng-attr-title=\"{{match.label}}\"></a>\n" + | ||
""); | ||
}]); |
angular.module("uib/template/typeahead/typeahead-popup.html", []).run(["$templateCache", function($templateCache) { | ||
$templateCache.put("uib/template/typeahead/typeahead-popup.html", | ||
"<ul class=\"dropdown-menu\" ng-show=\"isOpen() && !moveInProgress\" ng-style=\"{top: position().top+'px', left: position().left+'px'}\" style=\"display: block;\" role=\"listbox\" aria-hidden=\"{{!isOpen()}}\">\n" + | ||
"<ul class=\"dropdown-menu\" ng-show=\"isOpen() && !moveInProgress\" ng-style=\"{top: position().top+'px', left: position().left+'px'}\" role=\"listbox\" aria-hidden=\"{{!isOpen()}}\">\n" + | ||
" <li ng-repeat=\"match in matches track by $index\" ng-class=\"{active: isActive($index) }\" ng-mouseenter=\"selectActive($index)\" ng-click=\"selectMatch($index, $event)\" role=\"option\" id=\"{{::match.id}}\">\n" + | ||
@@ -5,0 +5,0 @@ " <div uib-typeahead-match index=\"$index\" match=\"match\" query=\"query\" template-url=\"templateUrl\"></div>\n" + |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
1589413
184
32208
144