Compare commits
85 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| a2dee1bdeb | |||
| 409eec4019 | |||
| b90485b2a4 | |||
| 0d79005f8d | |||
| e538d2f71f | |||
| f5b357fd11 | |||
| 761db7b4b7 | |||
| 86ee770834 | |||
| 47c4544e23 | |||
| e8479514cd | |||
| cf3f870735 | |||
| 7d3a7502fa | |||
| 71dc691323 | |||
| 42f3cc847a | |||
| b755559ec9 | |||
| 9e6d2a0a14 | |||
| 2596b9805f | |||
| 5a3e44a146 | |||
| 4872c05a32 | |||
| fc686bb7a3 | |||
| 7f664f9f45 | |||
| 955848c3b1 | |||
| 7a1d54c8c3 | |||
| 0ed1a59aef | |||
| f2722b59a5 | |||
| 57ed7e4f7f | |||
| 90848144e8 | |||
| a4d7076c8e | |||
| c824731ae8 | |||
| 1653afa210 | |||
| 7e2f2c1bad | |||
| 61f365abfd | |||
| ec2d9ad605 | |||
| 08b50ccb1c | |||
| 44ab0a8106 | |||
| 1962485504 | |||
| 8a4f625ef6 | |||
| 7be665399f | |||
| 2edb5d38cb | |||
| 4e06553f7c | |||
| 25ff206767 | |||
| 997813f0eb | |||
| 25db68e903 | |||
| 78559761dc | |||
| 14384fc40f | |||
| 58f1813aca | |||
| 9666c64f4a | |||
| f9f7e02d15 | |||
| a4bea6f229 | |||
| c00851a18b | |||
| c687acd489 | |||
| 3f70d76327 | |||
| 08ee30a91e | |||
| 7671bd6ed4 | |||
| 1a132dd757 | |||
| 9881a27397 | |||
| 294b151342 | |||
| 969eb9c74d | |||
| 048d85a2ad | |||
| 876e72a0df | |||
| 6734908108 | |||
| 515bcf2933 | |||
| 148371fb6d | |||
| 8047c06258 | |||
| 3e8ecfffe0 | |||
| ddcacb7a83 | |||
| 915eda0540 | |||
| f9eede7555 | |||
| fb5fabf580 | |||
| aef24cde4b | |||
| b893a93f82 | |||
| 6352f13e85 | |||
| 76cd65ef1a | |||
| 84cc2cf5cc | |||
| 2ade0545a9 | |||
| f5ff12c0a4 | |||
| f147d22f5c | |||
| f798a47d48 | |||
| 43a1f75e7c | |||
| 4a5e6a7418 | |||
| af6c2aaca0 | |||
| 61415e1968 | |||
| 5123e38a49 | |||
| ca20be4667 | |||
| d9dd5803ae |
@@ -6,6 +6,8 @@ existing issues (both open and closed) prior to opening any new issue and ensure
|
||||
|
||||
### Link to minimally-working plunker that reproduces the issue:
|
||||
|
||||
### Steps to reproduce the issue:
|
||||
|
||||
### Version of Angular, UIBS, and Bootstrap
|
||||
|
||||
Angular:
|
||||
|
||||
+1
-1
@@ -13,7 +13,7 @@ addons:
|
||||
before_install:
|
||||
- export DISPLAY=:99.0
|
||||
- sh -e /etc/init.d/xvfb start
|
||||
- npm install --quiet -g grunt-cli karma
|
||||
- npm install --quiet -g karma
|
||||
|
||||
script: grunt
|
||||
sudo: false
|
||||
|
||||
+194
-41
@@ -1,5 +1,145 @@
|
||||
<a name="2.5.0"></a>
|
||||
# [2.5.0](https://github.com/angular-ui/bootstrap/compare/2.4.0...v2.5.0) (2017-01-28)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **angular:** add compatibility with Angular 1.6([0d79005](https://github.com/angular-ui/bootstrap/commit/0d79005)), closes [#6427](https://github.com/angular-ui/bootstrap/issues/6427) [#6360](https://github.com/angular-ui/bootstrap/issues/6360)
|
||||
* **carousel:** remove transition buffering([86ee770](https://github.com/angular-ui/bootstrap/commit/86ee770)), closes [#6367](https://github.com/angular-ui/bootstrap/issues/6367) [#5967](https://github.com/angular-ui/bootstrap/issues/5967)
|
||||
* **dropdown:** do nothing if not open when clicking([761db7b](https://github.com/angular-ui/bootstrap/commit/761db7b)), closes [#6414](https://github.com/angular-ui/bootstrap/issues/6414)
|
||||
* **tooltip:** unbind keypress listener on hide([f5b357f](https://github.com/angular-ui/bootstrap/commit/f5b357f)), closes [#6423](https://github.com/angular-ui/bootstrap/issues/6423) [#6405](https://github.com/angular-ui/bootstrap/issues/6405)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **dropdown:** make dropdown-append-to-body configurable ([#6356](https://github.com/angular-ui/bootstrap/issues/6356))([7d3a750](https://github.com/angular-ui/bootstrap/commit/7d3a750))
|
||||
* **pagination:** Added menu and menuitem roles (closes [#6383](https://github.com/angular-ui/bootstrap/issues/6383)) ([#6386](https://github.com/angular-ui/bootstrap/issues/6386))([71dc691](https://github.com/angular-ui/bootstrap/commit/71dc691)), closes [#6383](https://github.com/angular-ui/bootstrap/issues/6383) [(#6386](https://github.com/(/issues/6386)
|
||||
|
||||
|
||||
|
||||
<a name="2.4.0"></a>
|
||||
# [2.4.0](https://github.com/angular-ui/bootstrap/compare/2.3.2...v2.4.0) (2016-12-30)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **dateparser:** allow overriding of parsers([5a3e44a](https://github.com/angular-ui/bootstrap/commit/5a3e44a)), closes [#6370](https://github.com/angular-ui/bootstrap/issues/6370) [#6373](https://github.com/angular-ui/bootstrap/issues/6373)
|
||||
|
||||
|
||||
|
||||
<a name="2.3.2"></a>
|
||||
## [2.3.2](https://github.com/angular-ui/bootstrap/compare/2.3.1...v2.3.2) (2016-12-27)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **dropdown:** re-add close([955848c](https://github.com/angular-ui/bootstrap/commit/955848c)), closes [#6382](https://github.com/angular-ui/bootstrap/issues/6382) [#6321](https://github.com/angular-ui/bootstrap/issues/6321) [#6357](https://github.com/angular-ui/bootstrap/issues/6357) [#6364](https://github.com/angular-ui/bootstrap/issues/6364)
|
||||
|
||||
|
||||
|
||||
<a name="2.3.1"></a>
|
||||
## [2.3.1](https://github.com/angular-ui/bootstrap/compare/2.3.0...v2.3.1) (2016-12-10)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **dateparser:** add new date format for angular 1.5+ only([f2722b5](https://github.com/angular-ui/bootstrap/commit/f2722b5)), closes [#6349](https://github.com/angular-ui/bootstrap/issues/6349)
|
||||
|
||||
* **datepickerPopup:** change to toTimezone only([1962485](https://github.com/angular-ui/bootstrap/commit/1962485)), fixes [#6235](https://github.com/angular-ui/bootstrap/issues/6235)
|
||||
|
||||
* **modal:** revert focus behavior on open([8a4f625](https://github.com/angular-ui/bootstrap/commit/8a4f625)), closes [#6295](https://github.com/angular-ui/bootstrap/issues/6295)
|
||||
|
||||
<a name="2.3.0"></a>
|
||||
# [2.3.0](https://github.com/angular-ui/bootstrap/compare/2.2.0...2.3.0) (2016-11-26)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **dateparser:** add LLLL support([25ff206](https://github.com/angular-ui/bootstrap/commit/25ff206)), closes [#6281](https://github.com/angular-ui/bootstrap/issues/6281)
|
||||
|
||||
|
||||
|
||||
<a name="2.2.0"></a>
|
||||
# [2.2.0](https://github.com/angular-ui/bootstrap/compare/2.1.4...2.2.0) (2016-10-10)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **dropdown:** exit keybind is not open ([14384fc](https://github.com/angular-ui/bootstrap/commit/14384fc)), closes [#6278](https://github.com/angular-ui/bootstrap/issues/6278) [#6208](https://github.com/angular-ui/bootstrap/issues/6208)
|
||||
* **modal:** improve ARIA support. ([f9f7e02](https://github.com/angular-ui/bootstrap/commit/f9f7e02)), closes [#6203](https://github.com/angular-ui/bootstrap/issues/6203)
|
||||
* **position:** correct scrollbar width calculation ([58f1813](https://github.com/angular-ui/bootstrap/commit/58f1813)), closes [#6273](https://github.com/angular-ui/bootstrap/issues/6273)
|
||||
* **tooltip:** cancel timeout when hidden ([7855976](https://github.com/angular-ui/bootstrap/commit/7855976)), closes [#6226](https://github.com/angular-ui/bootstrap/issues/6226) [#6221](https://github.com/angular-ui/bootstrap/issues/6221)
|
||||
|
||||
### Features
|
||||
|
||||
* **timepicker:** add validation information ([9666c64](https://github.com/angular-ui/bootstrap/commit/9666c64)), closes [#6230](https://github.com/angular-ui/bootstrap/issues/6230) [#6259](https://github.com/angular-ui/bootstrap/issues/6259)
|
||||
|
||||
|
||||
|
||||
<a name="2.1.4"></a>
|
||||
## [2.1.4](https://github.com/angular-ui/bootstrap/compare/2.1.3...2.1.4) (2016-09-24)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **datepicker:** improve accessibility ([3f70d76](https://github.com/angular-ui/bootstrap/commit/3f70d76)), closes [#6247](https://github.com/angular-ui/bootstrap/issues/6247)
|
||||
* **dropdown:** prevent premature scope removal ([08ee30a](https://github.com/angular-ui/bootstrap/commit/08ee30a)), closes [#6238](https://github.com/angular-ui/bootstrap/issues/6238) [#6225](https://github.com/angular-ui/bootstrap/issues/6225)
|
||||
|
||||
|
||||
|
||||
<a name="2.1.3"></a>
|
||||
## [2.1.3](https://github.com/angular-ui/bootstrap/compare/2.1.2...2.1.3) (2016-08-25)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **modal:** compile only once with component ([969eb9c](https://github.com/angular-ui/bootstrap/commit/969eb9c)), closes [#6202](https://github.com/angular-ui/bootstrap/issues/6202) [#6201](https://github.com/angular-ui/bootstrap/issues/6201)
|
||||
|
||||
|
||||
|
||||
<a name="2.1.2"></a>
|
||||
## [2.1.2](https://github.com/angular-ui/bootstrap/compare/2.1.1...2.1.2) (2016-08-22)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **collapse:** revert change to transition css ([515bcf2](https://github.com/angular-ui/bootstrap/commit/515bcf2)), closes [#6196](https://github.com/angular-ui/bootstrap/issues/6196) [#6194](https://github.com/angular-ui/bootstrap/issues/6194)
|
||||
* **datepicker:** fix accidental global ([ddcacb7](https://github.com/angular-ui/bootstrap/commit/ddcacb7)), closes [#6188](https://github.com/angular-ui/bootstrap/issues/6188)
|
||||
* **modal:** close and dismiss bindings on component ([3e8ecff](https://github.com/angular-ui/bootstrap/commit/3e8ecff)), closes [#6192](https://github.com/angular-ui/bootstrap/issues/6192) [#6191](https://github.com/angular-ui/bootstrap/issues/6191)
|
||||
|
||||
|
||||
|
||||
<a name="2.1.1"></a>
|
||||
## [2.1.1](https://github.com/angular-ui/bootstrap/compare/2.1.0...2.1.1) (2016-08-21)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **collapse:** default to css([aef24cd](https://github.com/angular-ui/bootstrap/commit/aef24cd)), closes [#6182](https://github.com/angular-ui/bootstrap/issues/6182) [#6045](https://github.com/angular-ui/bootstrap/issues/6045)
|
||||
* **modal:** switch to .append([fb5fabf](https://github.com/angular-ui/bootstrap/commit/fb5fabf)), closes [#6187](https://github.com/angular-ui/bootstrap/issues/6187) [#6186](https://github.com/angular-ui/bootstrap/issues/6186)
|
||||
|
||||
|
||||
|
||||
<a name="2.1.0"></a>
|
||||
# [2.1.0](https://github.com/angular-ui/bootstrap/compare/2.0.2...2.1.0) (2016-08-19)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **collapse:** remove unnecessary inherit ([ca20be4](https://github.com/angular-ui/bootstrap/commit/ca20be4)), closes [#6164](https://github.com/angular-ui/bootstrap/issues/6164) [#6163](https://github.com/angular-ui/bootstrap/issues/6163)
|
||||
* **collapse:** set overflow to hidden on transition ([84cc2cf](https://github.com/angular-ui/bootstrap/commit/84cc2cf)), closes [#6180](https://github.com/angular-ui/bootstrap/issues/6180) [#5474](https://github.com/angular-ui/bootstrap/issues/5474)
|
||||
* **datepickerPopup:** apply timezone conversion ([f147d22](https://github.com/angular-ui/bootstrap/commit/f147d22)), closes [#6173](https://github.com/angular-ui/bootstrap/issues/6173) [#6147](https://github.com/angular-ui/bootstrap/issues/6147)
|
||||
* **modal:** improve ARIA support ([4a5e6a7](https://github.com/angular-ui/bootstrap/commit/4a5e6a7)), closes [#4772](https://github.com/angular-ui/bootstrap/issues/4772)
|
||||
* **tooltip:** close tooltip on esc ([f5ff12c](https://github.com/angular-ui/bootstrap/commit/f5ff12c)), closes [#6177](https://github.com/angular-ui/bootstrap/issues/6177) [#6108](https://github.com/angular-ui/bootstrap/issues/6108)
|
||||
|
||||
### Features
|
||||
|
||||
* **modal:** add component support ([2ade054](https://github.com/angular-ui/bootstrap/commit/2ade054)), closes [#5683](https://github.com/angular-ui/bootstrap/issues/5683) [#6179](https://github.com/angular-ui/bootstrap/issues/6179)
|
||||
|
||||
|
||||
|
||||
<a name="2.0.2"></a>
|
||||
## [2.0.2](https://github.com/angular-ui/bootstrap/compare/2.0.1...v2.0.2) (2016-08-15)
|
||||
## [2.0.2](https://github.com/angular-ui/bootstrap/compare/2.0.1...2.0.2) (2016-08-15)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
@@ -10,7 +150,7 @@
|
||||
|
||||
|
||||
<a name="2.0.1"></a>
|
||||
## [2.0.1](https://github.com/angular-ui/bootstrap/compare/2.0.0...v2.0.1) (2016-08-02)
|
||||
## [2.0.1](https://github.com/angular-ui/bootstrap/compare/2.0.0...2.0.1) (2016-08-02)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
@@ -20,7 +160,7 @@
|
||||
|
||||
|
||||
<a name="2.0.0"></a>
|
||||
# [2.0.0](https://github.com/angular-ui/bootstrap/compare/1.3.3...v2.0.0) (2016-07-20)
|
||||
# [2.0.0](https://github.com/angular-ui/bootstrap/compare/1.3.3...2.0.0) (2016-07-20)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
@@ -100,7 +240,7 @@ This could affect selectors if they are being used.
|
||||
|
||||
|
||||
<a name="1.3.3"></a>
|
||||
## [1.3.3](https://github.com/angular-ui/bootstrap/compare/1.3.2...v1.3.3) (2016-05-23)
|
||||
## [1.3.3](https://github.com/angular-ui/bootstrap/compare/1.3.2...1.3.3) (2016-05-23)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
@@ -126,7 +266,7 @@ This could affect selectors if they are being used.
|
||||
|
||||
|
||||
<a name="1.3.2"></a>
|
||||
## [1.3.2](https://github.com/angular-ui/bootstrap/compare/1.3.1...v1.3.2) (2016-04-14)
|
||||
## [1.3.2](https://github.com/angular-ui/bootstrap/compare/1.3.1...1.3.2) (2016-04-14)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
@@ -152,7 +292,7 @@ This could affect selectors if they are being used.
|
||||
|
||||
|
||||
<a name="1.3.1"></a>
|
||||
## [1.3.1](https://github.com/angular-ui/bootstrap/compare/1.3.0...v1.3.1) (2016-04-05)
|
||||
## [1.3.1](https://github.com/angular-ui/bootstrap/compare/1.3.0...1.3.1) (2016-04-05)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
@@ -163,7 +303,7 @@ This could affect selectors if they are being used.
|
||||
|
||||
|
||||
<a name="1.3.0"></a>
|
||||
# [1.3.0](https://github.com/angular-ui/bootstrap/compare/1.2.5...v1.3.0) (2016-04-05)
|
||||
# [1.3.0](https://github.com/angular-ui/bootstrap/compare/1.2.5...1.3.0) (2016-04-05)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
@@ -206,7 +346,7 @@ attribute pass-throughs in the popup
|
||||
|
||||
|
||||
<a name="1.2.5"></a>
|
||||
## [1.2.5](https://github.com/angular-ui/bootstrap/compare/1.2.4...v1.2.5) (2016-03-20)
|
||||
## [1.2.5](https://github.com/angular-ui/bootstrap/compare/1.2.4...1.2.5) (2016-03-20)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
@@ -229,13 +369,13 @@ attribute pass-throughs in the popup
|
||||
|
||||
|
||||
<a name="1.2.4"></a>
|
||||
## [1.2.4](https://github.com/angular-ui/bootstrap/compare/1.2.3...v1.2.4) (2016-03-06)
|
||||
## [1.2.4](https://github.com/angular-ui/bootstrap/compare/1.2.3...1.2.4) (2016-03-06)
|
||||
|
||||
|
||||
|
||||
|
||||
<a name="1.2.3"></a>
|
||||
## [1.2.3](https://github.com/angular-ui/bootstrap/compare/1.2.2...v1.2.3) (2016-03-06)
|
||||
## [1.2.3](https://github.com/angular-ui/bootstrap/compare/1.2.2...1.2.3) (2016-03-06)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
@@ -254,7 +394,7 @@ attribute pass-throughs in the popup
|
||||
|
||||
|
||||
<a name="1.2.2"></a>
|
||||
## [1.2.2](https://github.com/angular-ui/bootstrap/compare/1.2.1...v1.2.2) (2016-03-03)
|
||||
## [1.2.2](https://github.com/angular-ui/bootstrap/compare/1.2.1...1.2.2) (2016-03-03)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
@@ -271,7 +411,7 @@ attribute pass-throughs in the popup
|
||||
|
||||
|
||||
<a name="1.2.1"></a>
|
||||
## [1.2.1](https://github.com/angular-ui/bootstrap/compare/1.2.0...v1.2.1) (2016-02-27)
|
||||
## [1.2.1](https://github.com/angular-ui/bootstrap/compare/1.2.0...1.2.1) (2016-02-27)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
@@ -281,7 +421,7 @@ attribute pass-throughs in the popup
|
||||
|
||||
|
||||
<a name="1.2.0"></a>
|
||||
# [1.2.0](https://github.com/angular-ui/bootstrap/compare/1.1.2...v1.2.0) (2016-02-26)
|
||||
# [1.2.0](https://github.com/angular-ui/bootstrap/compare/1.1.2...1.2.0) (2016-02-26)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
@@ -341,7 +481,7 @@ template
|
||||
|
||||
|
||||
<a name="1.1.2"></a>
|
||||
## [1.1.2](https://github.com/angular-ui/bootstrap/compare/1.1.1...v1.1.2) (2016-02-01)
|
||||
## [1.1.2](https://github.com/angular-ui/bootstrap/compare/1.1.1...1.1.2) (2016-02-01)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
@@ -359,7 +499,7 @@ template
|
||||
|
||||
|
||||
<a name="1.1.1"></a>
|
||||
## [1.1.1](https://github.com/angular-ui/bootstrap/compare/v1.1.0...v1.1.1) (2016-01-25)
|
||||
## [1.1.1](https://github.com/angular-ui/bootstrap/compare/1.1.0...1.1.1) (2016-01-25)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
@@ -409,7 +549,7 @@ template in one's app and provide the necessary CSS
|
||||
|
||||
|
||||
<a name="1.0.3"></a>
|
||||
# [1.0.3](https://github.com/angular-ui/bootstrap/compare/v1.0.2...v1.0.3) (2016-01-12)
|
||||
# [1.0.3](https://github.com/angular-ui/bootstrap/compare/1.0.2...1.0.3) (2016-01-12)
|
||||
|
||||
|
||||
## Bug Fixes
|
||||
@@ -419,7 +559,7 @@ template in one's app and provide the necessary CSS
|
||||
|
||||
|
||||
<a name="1.0.2"></a>
|
||||
# [1.0.2](https://github.com/angular-ui/bootstrap/compare/v1.0.1...v1.0.2) (2016-01-12)
|
||||
# [1.0.2](https://github.com/angular-ui/bootstrap/compare/1.0.1...1.0.2) (2016-01-12)
|
||||
|
||||
|
||||
## Bug Fixes
|
||||
@@ -429,7 +569,7 @@ template in one's app and provide the necessary CSS
|
||||
|
||||
|
||||
<a name="1.0.1"></a>
|
||||
# [1.0.1](https://github.com/angular-ui/bootstrap/compare/1.0.0...v1.0.1) (2016-01-12)
|
||||
# [1.0.1](https://github.com/angular-ui/bootstrap/compare/1.0.0...1.0.1) (2016-01-12)
|
||||
|
||||
|
||||
## Bug Fixes
|
||||
@@ -450,7 +590,7 @@ template in one's app and provide the necessary CSS
|
||||
|
||||
|
||||
<a name="1.0.0"></a>
|
||||
# [1.0.0](https://github.com/angular-ui/bootstrap/compare/0.14.3...v1.0.0) (2016-01-08)
|
||||
# [1.0.0](https://github.com/angular-ui/bootstrap/compare/0.14.3...1.0.0) (2016-01-08)
|
||||
|
||||
|
||||
## Bug Fixes
|
||||
@@ -595,7 +735,7 @@ $scope.typeaheadContainer = angular.element(document.querySelector('#typeaheadCo
|
||||
|
||||
|
||||
<a name="0.14.3"></a>
|
||||
# [0.14.3](https://github.com/angular-ui/bootstrap/compare/0.14.2...v0.14.3) (2015-10-23)
|
||||
# [0.14.3](https://github.com/angular-ui/bootstrap/compare/0.14.2...0.14.3) (2015-10-23)
|
||||
|
||||
|
||||
## Bug Fixes
|
||||
@@ -620,7 +760,7 @@ $scope.typeaheadContainer = angular.element(document.querySelector('#typeaheadCo
|
||||
|
||||
|
||||
<a name="0.14.2"></a>
|
||||
# [0.14.2](https://github.com/angular-ui/bootstrap/compare/0.14.1...v0.14.2) (2015-10-14)
|
||||
# [0.14.2](https://github.com/angular-ui/bootstrap/compare/0.14.1...0.14.2) (2015-10-14)
|
||||
|
||||
|
||||
## Bug Fixes
|
||||
@@ -632,7 +772,7 @@ $scope.typeaheadContainer = angular.element(document.querySelector('#typeaheadCo
|
||||
|
||||
|
||||
<a name="0.14.1"></a>
|
||||
# [0.14.1](https://github.com/angular-ui/bootstrap/compare/0.14.0...v0.14.1) (2015-10-11)
|
||||
# [0.14.1](https://github.com/angular-ui/bootstrap/compare/0.14.0...0.14.1) (2015-10-11)
|
||||
|
||||
|
||||
## Bug Fixes
|
||||
@@ -815,7 +955,7 @@ $scope.typeaheadContainer = angular.element(document.querySelector('#typeaheadCo
|
||||
|
||||
|
||||
<a name"0.13.3"></a>
|
||||
# 0.13.3 (2015-08-09)
|
||||
# [0.13.3](https://github.com/angular-ui/bootstrap/compare/0.13.2...0.13.3) (2015-08-09)
|
||||
|
||||
|
||||
## Bug Fixes
|
||||
@@ -902,7 +1042,7 @@ Closes #4080
|
||||
|
||||
|
||||
<a name"0.13.2"></a>
|
||||
# 0.13.2 (2015-08-02)
|
||||
# [0.13.2](https://github.com/angular-ui/bootstrap/compare/0.13.1...0.13.2) (2015-08-02)
|
||||
|
||||
|
||||
## Bug Fixes
|
||||
@@ -948,7 +1088,7 @@ Closes #4080
|
||||
|
||||
|
||||
<a name"0.13.1"></a>
|
||||
# 0.13.1 (2015-07-23)
|
||||
# [0.13.1](https://github.com/angular-ui/bootstrap/compare/0.13.0...0.13.1) (2015-07-23)
|
||||
|
||||
|
||||
## Bug Fixes
|
||||
@@ -1009,7 +1149,7 @@ Closes #4080
|
||||
|
||||
|
||||
<a name="0.13.0"></a>
|
||||
# 0.13.0 (2015-05-02)
|
||||
# [0.13.0](https://github.com/angular-ui/bootstrap/compare/0.12.1...0.13.0) (2015-05-02)
|
||||
|
||||
|
||||
## Bug Fixes
|
||||
@@ -1108,7 +1248,8 @@ Closes #4080
|
||||
* **transition:** deprecate transition module ([8a552443](https://github.com/angular-ui/bootstrap/commit/8a552443741d1e5b4b29d9da9c7e9990fa37886c), closes [#3497](https://github.com/angular-ui/bootstrap/issues/3497))
|
||||
|
||||
|
||||
# 0.12.1 (2015-02-20)
|
||||
<a name="0.12.1"></a>
|
||||
# [0.12.1](https://github.com/angular-ui/bootstrap/compare/0.12.0...0.12.1) (2015-02-20)
|
||||
|
||||
## Bug Fixes
|
||||
|
||||
@@ -1116,7 +1257,7 @@ Closes #4080
|
||||
- incorrect position when text wraps ([5726e3ef](http://github.com/angular-ui/bootstrap/commit/5726e3ef))
|
||||
|
||||
<a name="0.12.0"></a>
|
||||
# 0.12.0 (2014-11-16)
|
||||
# [0.12.0](https://github.com/angular-ui/bootstrap/compare/0.11.2...0.12.0) (2014-11-16)
|
||||
|
||||
|
||||
## Bug Fixes
|
||||
@@ -1169,11 +1310,13 @@ once* and can no longer be changed after initialization.
|
||||
```
|
||||
|
||||
|
||||
# 0.11.2 (2014-09-26)
|
||||
<a name="0.11.2"></a>
|
||||
# [0.11.2](https://github.com/angular-ui/bootstrap/compare/0.11.1...0.11.2) (2014-09-26)
|
||||
|
||||
Revert breaking change in **dropdown** ([1a998c4](http://github.com/angular-ui/bootstrap/commit/1a998c4))
|
||||
|
||||
# 0.11.1 (2014-09-26)
|
||||
<a name="0.11.1"></a>
|
||||
# [0.11.1](https://github.com/angular-ui/bootstrap/compare/0.11.0...0.11.1) (2014-09-26)
|
||||
|
||||
## Features
|
||||
|
||||
@@ -1207,7 +1350,8 @@ Revert breaking change in **dropdown** ([1a998c4](http://github.com/angular-ui/b
|
||||
- allow multiple line expression ([c7db0df4](http://github.com/angular-ui/bootstrap/commit/c7db0df4))
|
||||
- replace ng-if with ng-show in matches popup ([a0be450d](http://github.com/angular-ui/bootstrap/commit/a0be450d))
|
||||
|
||||
# 0.11.0 (2014-05-01)
|
||||
<a name="0.11.0"></a>
|
||||
# [0.11.0](https://github.com/angular-ui/bootstrap/compare/0.10.0...0.11.0) (2014-05-01)
|
||||
|
||||
## Features
|
||||
|
||||
@@ -1385,7 +1529,8 @@ Revert breaking change in **dropdown** ([1a998c4](http://github.com/angular-ui/b
|
||||
<tabset type="{{navtype}}" ...></tabset>
|
||||
```
|
||||
|
||||
# 0.10.0 (2014-01-13)
|
||||
<a name="0.10.0"></a>
|
||||
# [0.10.0](https://github.com/angular-ui/bootstrap/compare/0.9.0...0.10.0) (2014-01-13)
|
||||
|
||||
_This release adds AngularJS 1.2 support_
|
||||
|
||||
@@ -1406,7 +1551,8 @@ _This release adds AngularJS 1.2 support_
|
||||
- **tooltip:**
|
||||
- performance and scope fixes ([c0df3201](http://github.com/angular-ui/bootstrap/commit/c0df3201))
|
||||
|
||||
# 0.9.0 (2013-12-28)
|
||||
<a name="0.9.0"></a>
|
||||
# [0.9.0](https://github.com/angular-ui/bootstrap/compare/0.8.0...0.9.0) (2013-12-28)
|
||||
|
||||
_This release adds Bootstrap3 support_
|
||||
|
||||
@@ -1449,7 +1595,8 @@ _This release adds Bootstrap3 support_
|
||||
- **tooltip:**
|
||||
- re-position tooltip after draw ([a99b3608](http://github.com/angular-ui/bootstrap/commit/a99b3608))
|
||||
|
||||
# 0.8.0 (2013-12-28)
|
||||
<a name="0.8.0"></a>
|
||||
# [0.8.0](https://github.com/angular-ui/bootstrap/compare/0.7.0...0.8.0) (2013-12-28)
|
||||
|
||||
## Features
|
||||
|
||||
@@ -1516,7 +1663,8 @@ _This release adds Bootstrap3 support_
|
||||
<progress><bar ng-repeat="obj in objs" value="obj.var" type="{{obj.type}}"></bar></progress>
|
||||
```
|
||||
|
||||
# 0.7.0 (2013-11-22)
|
||||
<a name="0.7.0"></a>
|
||||
# [0.7.0](https://github.com/angular-ui/bootstrap/compare/0.6.0...0.7.0) (2013-11-22)
|
||||
|
||||
## Features
|
||||
|
||||
@@ -1564,7 +1712,8 @@ _This release adds Bootstrap3 support_
|
||||
- evaluate matches source against a correct scope ([fd21214d](http://github.com/angular-ui/bootstrap/commit/fd21214d))
|
||||
- support IE8 ([0e9f9980](http://github.com/angular-ui/bootstrap/commit/0e9f9980))
|
||||
|
||||
# 0.6.0 (2013-09-08)
|
||||
<a name="0.6.0"></a>
|
||||
# [0.6.0](https://github.com/angular-ui/bootstrap/compare/0.6.0...0.7.0) (2013-09-08)
|
||||
|
||||
## Features
|
||||
|
||||
@@ -1643,7 +1792,8 @@ Check the documentation for the `$modal` service to migrate from `$dialog`
|
||||
|
||||
The placment='mouse' is gone with no equivalent
|
||||
|
||||
# 0.5.0 (2013-08-04)
|
||||
<a name="0.5.0"></a>
|
||||
# [0.5.0](https://github.com/angular-ui/bootstrap/compare/0.4.0...0.5.0) (2013-08-04)
|
||||
|
||||
## Features
|
||||
|
||||
@@ -1726,7 +1876,8 @@ The placment='mouse' is gone with no equivalent
|
||||
<pagination first-text="{{var1}}" ...></pagination>
|
||||
```
|
||||
|
||||
# 0.4.0 (2013-06-24)
|
||||
<a name="0.4.0"></a>
|
||||
# [0.4.0](https://github.com/angular-ui/bootstrap/compare/0.3.0...0.4.0) (2013-06-24)
|
||||
|
||||
## Features
|
||||
|
||||
@@ -1844,8 +1995,8 @@ The placment='mouse' is gone with no equivalent
|
||||
</tabset>
|
||||
```
|
||||
|
||||
|
||||
# 0.3.0 (2013-04-30)
|
||||
<a name="0.3.0"></a>
|
||||
# [0.3.0](https://github.com/angular-ui/bootstrap/compare/0.2.0...0.3.0) (2013-04-30)
|
||||
|
||||
## Features
|
||||
|
||||
@@ -1886,7 +2037,8 @@ The placment='mouse' is gone with no equivalent
|
||||
- correctly higlight matches if query contains regexp-special chars ([467afcd6](https://github.com/angular-ui/bootstrap/commit/467afcd6))
|
||||
- fix matches pop-up positioning issues ([74beecdb](https://github.com/angular-ui/bootstrap/commit/74beecdb))
|
||||
|
||||
# 0.2.0 (2013-03-03)
|
||||
<a name="0.2.0"></a>
|
||||
# [0.2.0](https://github.com/angular-ui/bootstrap/compare/0.1.0...0.2.0) (2013-03-03)
|
||||
|
||||
## Features
|
||||
|
||||
@@ -1920,6 +2072,7 @@ The placment='mouse' is gone with no equivalent
|
||||
- **typeahead:**
|
||||
- update inputs value on mapping where label is not derived from the model ([a5f64de](https://github.com/angular-ui/bootstrap/commit/a5f64de))
|
||||
|
||||
<a name="0.1.0"></a>
|
||||
# 0.1.0 (2013-02-02)
|
||||
|
||||
_Very first, initial release_.
|
||||
|
||||
+2
-2
@@ -9,8 +9,8 @@ module.exports = function(grunt) {
|
||||
grunt.util.linefeed = '\n';
|
||||
|
||||
grunt.initConfig({
|
||||
ngversion: '1.5.5',
|
||||
bsversion: '3.3.6',
|
||||
ngversion: '1.6.1',
|
||||
bsversion: '3.3.7',
|
||||
modules: [],//to be filled in by build task
|
||||
pkg: grunt.file.readJSON('package.json'),
|
||||
dist: 'dist',
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
The MIT License
|
||||
|
||||
Copyright (c) 2012-2016 the AngularUI Team, https://github.com/organizations/angular-ui/teams/291112
|
||||
Copyright (c) 2012-2017 the AngularUI Team, https://github.com/organizations/angular-ui/teams/291112
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
[](https://gitter.im/angular-ui/bootstrap?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
||||
[](http://travis-ci.org/angular-ui/bootstrap)
|
||||
[](https://david-dm.org/angular-ui/bootstrap#info=devDependencies)
|
||||
[](https://cdnjs.com/libraries/angular-ui-bootstrap/)
|
||||
|
||||
### Quick links
|
||||
- [Demo](#demo)
|
||||
@@ -131,7 +132,7 @@ The other modules, such as `accordion` in the example below, do not have CSS res
|
||||
import accordion from 'angular-ui-bootstrap/src/accordion';
|
||||
import typeahead from 'angular-ui-bootstrap/src/typeahead/index-nocss.js';
|
||||
|
||||
angular.module('myModule', [accordion, datepicker]);
|
||||
angular.module('myModule', [accordion, typeahead]);
|
||||
```
|
||||
|
||||
# Versioning
|
||||
|
||||
@@ -73,7 +73,7 @@ section {
|
||||
|
||||
}
|
||||
|
||||
.navbar .collapse {
|
||||
.navbar-fixed-top .collapse {
|
||||
border-top: 1px solid #e7e7e7;
|
||||
margin-left: -15px;
|
||||
margin-right: -15px;
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 8.2 KiB |
@@ -17,6 +17,7 @@ angular.module('plunker', [])
|
||||
' <head>\n' +
|
||||
' <script src="//ajax.googleapis.com/ajax/libs/angularjs/'+ngVersion+'/angular.js"></script>\n' +
|
||||
' <script src="//ajax.googleapis.com/ajax/libs/angularjs/'+ngVersion+'/angular-animate.js"></script>\n' +
|
||||
' <script src="//ajax.googleapis.com/ajax/libs/angularjs/'+ngVersion+'/angular-sanitize.js"></script>\n' +
|
||||
' <script src="//angular-ui.github.io/bootstrap/ui-bootstrap-tpls-'+version+'.js"></script>\n' +
|
||||
' <script src="example.js"></script>\n' +
|
||||
' <link href="//netdna.bootstrapcdn.com/bootstrap/'+bsVersion+'/css/bootstrap.min.css" rel="stylesheet">\n' +
|
||||
@@ -28,7 +29,7 @@ angular.module('plunker', [])
|
||||
};
|
||||
|
||||
var scriptContent = function(content) {
|
||||
return "angular.module('ui.bootstrap.demo', ['ngAnimate', 'ui.bootstrap']);" + "\n" + content;
|
||||
return "angular.module('ui.bootstrap.demo', ['ngAnimate', 'ngSanitize', 'ui.bootstrap']);" + "\n" + content;
|
||||
};
|
||||
|
||||
addField('description', 'http://angular-ui.github.io/bootstrap/');
|
||||
|
||||
+14
-4
@@ -1,8 +1,15 @@
|
||||
{
|
||||
"author": "https://github.com/angular-ui/bootstrap/graphs/contributors",
|
||||
"name": "angular-ui-bootstrap",
|
||||
"version": "2.0.2",
|
||||
"version": "2.5.1-SNAPSHOT",
|
||||
"description": "Native AngularJS (Angular) directives for Bootstrap",
|
||||
"homepage": "http://angular-ui.github.io/bootstrap/",
|
||||
"keywords": [
|
||||
"angularjs",
|
||||
"angular",
|
||||
"bootstrap",
|
||||
"ui"
|
||||
],
|
||||
"dependencies": {},
|
||||
"directories": {
|
||||
"lib": "src/"
|
||||
@@ -15,6 +22,7 @@
|
||||
],
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"demo": "grunt after-test && static dist -a 0.0.0.0 -H '{\"Cache-Control\": \"no-cache, must-revalidate\"}'",
|
||||
"test": "grunt"
|
||||
},
|
||||
"repository": {
|
||||
@@ -22,10 +30,11 @@
|
||||
"url": "https://github.com/angular-ui/bootstrap.git"
|
||||
},
|
||||
"devDependencies": {
|
||||
"angular": "1.5.5",
|
||||
"angular-mocks": "1.5.5",
|
||||
"angular-sanitize": "1.5.5",
|
||||
"angular": "1.6.1",
|
||||
"angular-mocks": "1.6.1",
|
||||
"angular-sanitize": "1.6.1",
|
||||
"grunt": "^0.4.5",
|
||||
"grunt-cli": "^1.2.0",
|
||||
"grunt-contrib-concat": "^1.0.0",
|
||||
"grunt-contrib-copy": "^1.0.0",
|
||||
"grunt-contrib-uglify": "^1.0.1",
|
||||
@@ -44,6 +53,7 @@
|
||||
"load-grunt-tasks": "^3.3.0",
|
||||
"lodash": "^4.1.0",
|
||||
"marked": "^0.3.5",
|
||||
"node-static": "^0.7.8",
|
||||
"semver": "^5.0.1",
|
||||
"shelljs": "^0.6.0"
|
||||
},
|
||||
|
||||
@@ -1,18 +1,16 @@
|
||||
<div ng-controller="AccordionDemoCtrl">
|
||||
<script type="text/ng-template" id="group-template.html">
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<h4 class="panel-title" style="color:#fa39c3">
|
||||
<a href tabindex="0" class="accordion-toggle" ng-click="toggleOpen()" uib-accordion-transclude="heading">
|
||||
<span uib-accordion-header ng-class="{'text-muted': isDisabled}">
|
||||
{{heading}}
|
||||
</span>
|
||||
</a>
|
||||
</h4>
|
||||
</div>
|
||||
<div class="panel-collapse collapse" uib-collapse="!isOpen">
|
||||
<div class="panel-body" style="text-align: right" ng-transclude></div>
|
||||
</div>
|
||||
<div class="panel-heading">
|
||||
<h4 class="panel-title" style="color:#fa39c3">
|
||||
<a href tabindex="0" class="accordion-toggle" ng-click="toggleOpen()" uib-accordion-transclude="heading">
|
||||
<span uib-accordion-header ng-class="{'text-muted': isDisabled}">
|
||||
{{heading}}
|
||||
</span>
|
||||
</a>
|
||||
</h4>
|
||||
</div>
|
||||
<div class="panel-collapse collapse" uib-collapse="!isOpen">
|
||||
<div class="panel-body" style="text-align: right" ng-transclude></div>
|
||||
</div>
|
||||
</script>
|
||||
|
||||
|
||||
@@ -551,7 +551,7 @@ describe('uib-accordion', function() {
|
||||
});
|
||||
|
||||
it('attaches the same scope to the transcluded heading and body', function() {
|
||||
expect(findGroupLink(0).find('span.ng-scope').scope().$id).toBe(findGroupBody(0).find('span').scope().$id);
|
||||
expect(findGroupLink(0).scope().$id).toBe(findGroupBody(0).scope().$id);
|
||||
});
|
||||
|
||||
it('should wrap the transcluded content in a span', function() {
|
||||
@@ -580,7 +580,7 @@ describe('uib-accordion', function() {
|
||||
});
|
||||
|
||||
it('attaches the same scope to the transcluded heading and body', function() {
|
||||
expect(findGroupLink(0).find('span.ng-scope').scope().$id).toBe(findGroupBody(0).find('span').scope().$id);
|
||||
expect(findGroupLink(0).scope().$id).toBe(findGroupBody(0).scope().$id);
|
||||
});
|
||||
|
||||
it('should have disabled styling when is-disabled is true', isDisabledStyleCheck);
|
||||
|
||||
@@ -36,7 +36,7 @@ describe('uib-alert', function() {
|
||||
}
|
||||
|
||||
function findContent(index) {
|
||||
return element.find('div[ng-transclude] span').eq(index);
|
||||
return element.find('div[ng-transclude]').eq(index);
|
||||
}
|
||||
|
||||
it('should expose the controller to the view', function() {
|
||||
|
||||
@@ -5,7 +5,7 @@ angular.module('ui.bootstrap.carousel', [])
|
||||
slides = self.slides = $scope.slides = [],
|
||||
SLIDE_DIRECTION = 'uib-slideDirection',
|
||||
currentIndex = $scope.active,
|
||||
currentInterval, isPlaying, bufferedTransitions = [];
|
||||
currentInterval, isPlaying;
|
||||
|
||||
var destroyed = false;
|
||||
$element.addClass('carousel');
|
||||
@@ -67,11 +67,6 @@ angular.module('ui.bootstrap.carousel', [])
|
||||
self.removeSlide = function(slide) {
|
||||
var index = findSlideIndex(slide);
|
||||
|
||||
var bufferedIndex = bufferedTransitions.indexOf(slides[index]);
|
||||
if (bufferedIndex !== -1) {
|
||||
bufferedTransitions.splice(bufferedIndex, 1);
|
||||
}
|
||||
|
||||
//get the index of the slide inside the carousel
|
||||
slides.splice(index, 1);
|
||||
if (slides.length > 0 && currentIndex === index) {
|
||||
@@ -95,7 +90,6 @@ angular.module('ui.bootstrap.carousel', [])
|
||||
if (slides.length === 0) {
|
||||
currentIndex = null;
|
||||
$scope.active = null;
|
||||
clearBufferedTransitions();
|
||||
}
|
||||
};
|
||||
|
||||
@@ -110,8 +104,6 @@ angular.module('ui.bootstrap.carousel', [])
|
||||
if (nextSlide.slide.index !== currentIndex &&
|
||||
!$scope.$currentTransition) {
|
||||
goNext(nextSlide.slide, nextIndex, direction);
|
||||
} else if (nextSlide && nextSlide.slide.index !== currentIndex && $scope.$currentTransition) {
|
||||
bufferedTransitions.push(slides[nextIndex]);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -180,12 +172,6 @@ angular.module('ui.bootstrap.carousel', [])
|
||||
}
|
||||
});
|
||||
|
||||
function clearBufferedTransitions() {
|
||||
while (bufferedTransitions.length) {
|
||||
bufferedTransitions.shift();
|
||||
}
|
||||
}
|
||||
|
||||
function getSlideByIndex(index) {
|
||||
for (var i = 0, l = slides.length; i < l; ++i) {
|
||||
if (slides[i].index === index) {
|
||||
@@ -221,14 +207,6 @@ angular.module('ui.bootstrap.carousel', [])
|
||||
if (phase === 'close') {
|
||||
$scope.$currentTransition = null;
|
||||
$animate.off('addClass', element);
|
||||
if (bufferedTransitions.length) {
|
||||
var nextSlide = bufferedTransitions.pop().slide;
|
||||
var nextIndex = nextSlide.index;
|
||||
var nextDirection = nextIndex > self.getCurrentIndex() ? 'next' : 'prev';
|
||||
clearBufferedTransitions();
|
||||
|
||||
goNext(nextSlide, nextIndex, nextDirection);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -259,7 +237,6 @@ angular.module('ui.bootstrap.carousel', [])
|
||||
function resetTransition(slides) {
|
||||
if (!slides.length) {
|
||||
$scope.$currentTransition = null;
|
||||
clearBufferedTransitions();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -18,14 +18,12 @@ angular.module('ui.bootstrap.collapse', [])
|
||||
horizontal = !!('horizontal' in attrs);
|
||||
if (horizontal) {
|
||||
css = {
|
||||
width: 'auto',
|
||||
height: 'inherit'
|
||||
width: ''
|
||||
};
|
||||
cssTo = {width: '0'};
|
||||
} else {
|
||||
css = {
|
||||
width: 'inherit',
|
||||
height: 'auto'
|
||||
height: ''
|
||||
};
|
||||
cssTo = {height: '0'};
|
||||
}
|
||||
@@ -61,14 +59,20 @@ angular.module('ui.bootstrap.collapse', [])
|
||||
$animateCss(element, {
|
||||
addClass: 'in',
|
||||
easing: 'ease',
|
||||
css: {
|
||||
overflow: 'hidden'
|
||||
},
|
||||
to: getScrollFromElement(element[0])
|
||||
}).start()['finally'](expandDone);
|
||||
} else {
|
||||
$animate.addClass(element, 'in', {
|
||||
css: {
|
||||
overflow: 'hidden'
|
||||
},
|
||||
to: getScrollFromElement(element[0])
|
||||
}).then(expandDone);
|
||||
}
|
||||
});
|
||||
}, angular.noop);
|
||||
}
|
||||
|
||||
function expandDone() {
|
||||
@@ -107,7 +111,7 @@ angular.module('ui.bootstrap.collapse', [])
|
||||
to: cssTo
|
||||
}).then(collapseDone);
|
||||
}
|
||||
});
|
||||
}, angular.noop);
|
||||
}
|
||||
|
||||
function collapseDone() {
|
||||
|
||||
@@ -1,4 +1,31 @@
|
||||
<style>
|
||||
.horizontal-collapse {
|
||||
height: 70px;
|
||||
}
|
||||
.navbar-collapse.in {
|
||||
overflow-y: hidden;
|
||||
}
|
||||
</style>
|
||||
<div ng-controller="CollapseDemoCtrl">
|
||||
<p>Resize window to less than 768 pixels to display mobile menu toggle button.</p>
|
||||
<nav class="navbar navbar-default" role="navigation">
|
||||
<div class="navbar-header">
|
||||
<button type="button" class="navbar-toggle" ng-click="isNavCollapsed = !isNavCollapsed">
|
||||
<span class="sr-only">Toggle navigation</span>
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
</button>
|
||||
<a class="navbar-brand" href="#">A menu</a>
|
||||
</div>
|
||||
<div class="collapse navbar-collapse" uib-collapse="isNavCollapsed">
|
||||
<ul class="nav navbar-nav">
|
||||
<li><a href="#">Link 1</a></li>
|
||||
<li><a href="#">Link 2</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</nav>
|
||||
<hr>
|
||||
<button type="button" class="btn btn-default" ng-click="isCollapsed = !isCollapsed">Toggle collapse Vertically</button>
|
||||
<hr>
|
||||
<div uib-collapse="isCollapsed">
|
||||
@@ -7,7 +34,7 @@
|
||||
|
||||
<button type="button" class="btn btn-default" ng-click="isCollapsedHorizontal = !isCollapsedHorizontal">Toggle collapse Horizontally</button>
|
||||
<hr>
|
||||
<div uib-collapse="isCollapsedHorizontal" horizontal>
|
||||
<div class="horizontal-collapse" uib-collapse="isCollapsedHorizontal" horizontal>
|
||||
<div class="well well-lg">Some content</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
angular.module('ui.bootstrap.demo').controller('CollapseDemoCtrl', function ($scope) {
|
||||
$scope.isNavCollapsed = true;
|
||||
$scope.isCollapsed = false;
|
||||
$scope.isCollapsedHorizontal = false;
|
||||
});
|
||||
|
||||
@@ -27,8 +27,11 @@
|
||||
<i class="glyphicon glyphicon-eye-open"></i>
|
||||
_(Default: `false`)_ -
|
||||
Whether the element should be collapsed or not.
|
||||
|
||||
|
||||
* `horizontal`
|
||||
<small class="badge">$</small> -
|
||||
An optional attribute that permit to collapse horizontally.
|
||||
|
||||
### Known Issues
|
||||
|
||||
When using the `horizontal` attribute with this directive, CSS can reflow as the collapse element goes from `0px` to its desired end width, which can result in height changes. This can cause animations to not appear to run. The best way around this is to set a fixed height via CSS on the horizontal collapse element so that this situation does not occur, and so the animation can run as expected.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
angular.module('ui.bootstrap.dateparser', [])
|
||||
|
||||
.service('uibDateParser', ['$log', '$locale', 'dateFilter', 'orderByFilter', function($log, $locale, dateFilter, orderByFilter) {
|
||||
.service('uibDateParser', ['$log', '$locale', 'dateFilter', 'orderByFilter', 'filterFilter', function($log, $locale, dateFilter, orderByFilter, filterFilter) {
|
||||
// Pulled from https://github.com/mbostock/d3/blob/master/src/format/requote.js
|
||||
var SPECIAL_CHARACTERS_REGEXP = /[\\\^\$\*\+\?\|\[\]\(\)\.\{\}]/g;
|
||||
|
||||
@@ -230,10 +230,36 @@ angular.module('ui.bootstrap.dateparser', [])
|
||||
formatter: function(date) { return dateFilter(date, 'G'); }
|
||||
}
|
||||
];
|
||||
|
||||
if (angular.version.major >= 1 && angular.version.minor > 4) {
|
||||
formatCodeToRegex.push({
|
||||
key: 'LLLL',
|
||||
regex: $locale.DATETIME_FORMATS.STANDALONEMONTH.join('|'),
|
||||
apply: function(value) { this.month = $locale.DATETIME_FORMATS.STANDALONEMONTH.indexOf(value); },
|
||||
formatter: function(date) { return dateFilter(date, 'LLLL'); }
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
this.init();
|
||||
|
||||
function getFormatCodeToRegex(key) {
|
||||
return filterFilter(formatCodeToRegex, {key: key}, true)[0];
|
||||
}
|
||||
|
||||
this.getParser = function (key) {
|
||||
var f = getFormatCodeToRegex(key);
|
||||
return f && f.apply || null;
|
||||
};
|
||||
|
||||
this.overrideParser = function (key, parser) {
|
||||
var f = getFormatCodeToRegex(key);
|
||||
if (f && angular.isFunction(parser)) {
|
||||
this.parsers = {};
|
||||
f.apply = parser;
|
||||
}
|
||||
}.bind(this);
|
||||
|
||||
function createParser(format) {
|
||||
var map = [], regex = format.split('');
|
||||
|
||||
|
||||
@@ -58,6 +58,9 @@ Certain format codes support i18n. Check this [guide](https://docs.angularjs.org
|
||||
_(Example: `3` or `03`)_ -
|
||||
Parses a numeric month, but allowing an optional leading zero
|
||||
|
||||
* `LLLL`
|
||||
_(Example: `February`, i18n support)_ - Stand-alone month in year (January-December). Requires Angular version 1.5.1 or higher.
|
||||
|
||||
* `dd`
|
||||
_(Example: `05`, Leading 0)_ -
|
||||
Parses a numeric day.
|
||||
|
||||
@@ -84,6 +84,16 @@ describe('date parser', function() {
|
||||
expectFilter(new Date(2011, 4, 2, 0), 'dd-M!-yy', '02-05-11');
|
||||
expectFilter(oldDate, 'yyyy/M!/dd', '0001/03/06');
|
||||
});
|
||||
|
||||
it('should work correctly for `LLLL`', function() {
|
||||
expectFilter(new Date(2013, 7, 24, 0), 'LLLL/dd/yyyy', 'August/24/2013');
|
||||
expectFilter(new Date(2004, 10, 7, 0), 'dd.LLLL.yy', '07.November.04');
|
||||
expectFilter(new Date(2011, 4, 18, 0), 'dd-LLLL-yy', '18-May-11');
|
||||
expectFilter(new Date(1980, 1, 5, 0), 'LLLL/dd/yyyy', 'February/05/1980');
|
||||
expectFilter(new Date(1955, 2, 5, 0), 'yyyy/LLLL/dd', '1955/March/05');
|
||||
expectFilter(new Date(2011, 5, 2, 0), 'dd-LLLL-yy', '02-June-11');
|
||||
expectFilter(oldDate, 'yyyy/LLLL/dd', '0001/March/06');
|
||||
});
|
||||
|
||||
it('should work correctly for `d`', function() {
|
||||
expectFilter(new Date(2013, 10, 17, 0), 'd.MMMM.yy', '17.November.13');
|
||||
@@ -781,4 +791,28 @@ describe('date parser', function() {
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('overrideParser', function() {
|
||||
var twoDigitYearParser = function (value) {
|
||||
this.year = +value + (+value > 30 ? 1900 : 2000);
|
||||
};
|
||||
|
||||
it('should get the current parser', function() {
|
||||
expect(dateParser.getParser('yy')).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should override the parser', function() {
|
||||
dateParser.overrideParser('yy', twoDigitYearParser);
|
||||
expect(dateParser.parse('68', 'yy').getFullYear()).toEqual(1968);
|
||||
expect(dateParser.parse('67', 'yy').getFullYear()).toEqual(1967);
|
||||
expect(dateParser.parse('31', 'yy').getFullYear()).toEqual(1931);
|
||||
expect(dateParser.parse('30', 'yy').getFullYear()).toEqual(2030);
|
||||
});
|
||||
|
||||
it('should clear cached parsers', function() {
|
||||
expect(dateParser.parse('68', 'yy').getFullYear()).toEqual(2068);
|
||||
dateParser.overrideParser('yy', twoDigitYearParser);
|
||||
expect(dateParser.parse('68', 'yy').getFullYear()).toEqual(1968);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -104,7 +104,7 @@ angular.module('ui.bootstrap.datepicker', ['ui.bootstrap.dateparser', 'ui.bootst
|
||||
$scope.$watch('datepickerOptions.' + key, function(value) {
|
||||
if (value) {
|
||||
if (angular.isDate(value)) {
|
||||
self[key] = dateParser.fromTimezone(new Date(value), ngModelOptions.timezone);
|
||||
self[key] = dateParser.fromTimezone(new Date(value), ngModelOptions.getOption('timezone'));
|
||||
} else {
|
||||
if ($datepickerLiteralWarning) {
|
||||
$log.warn('Literal date support has been deprecated, please switch to date object usage');
|
||||
@@ -114,7 +114,7 @@ angular.module('ui.bootstrap.datepicker', ['ui.bootstrap.dateparser', 'ui.bootst
|
||||
}
|
||||
} else {
|
||||
self[key] = datepickerConfig[key] ?
|
||||
dateParser.fromTimezone(new Date(datepickerConfig[key]), ngModelOptions.timezone) :
|
||||
dateParser.fromTimezone(new Date(datepickerConfig[key]), ngModelOptions.getOption('timezone')) :
|
||||
null;
|
||||
}
|
||||
|
||||
@@ -126,7 +126,7 @@ angular.module('ui.bootstrap.datepicker', ['ui.bootstrap.dateparser', 'ui.bootst
|
||||
case 'minMode':
|
||||
if ($scope.datepickerOptions[key]) {
|
||||
$scope.$watch(function() { return $scope.datepickerOptions[key]; }, function(value) {
|
||||
self[key] = $scope[key] = angular.isDefined(value) ? value : datepickerOptions[key];
|
||||
self[key] = $scope[key] = angular.isDefined(value) ? value : $scope.datepickerOptions[key];
|
||||
if (key === 'minMode' && self.modes.indexOf($scope.datepickerOptions.datepickerMode) < self.modes.indexOf(self[key]) ||
|
||||
key === 'maxMode' && self.modes.indexOf($scope.datepickerOptions.datepickerMode) > self.modes.indexOf(self[key])) {
|
||||
$scope.datepickerMode = self[key];
|
||||
@@ -161,14 +161,13 @@ angular.module('ui.bootstrap.datepicker', ['ui.bootstrap.dateparser', 'ui.bootst
|
||||
|
||||
this.init = function(ngModelCtrl_) {
|
||||
ngModelCtrl = ngModelCtrl_;
|
||||
ngModelOptions = ngModelCtrl_.$options ||
|
||||
$scope.datepickerOptions.ngModelOptions ||
|
||||
datepickerConfig.ngModelOptions;
|
||||
ngModelOptions = extractOptions(ngModelCtrl);
|
||||
|
||||
if ($scope.datepickerOptions.initDate) {
|
||||
self.activeDate = dateParser.fromTimezone($scope.datepickerOptions.initDate, ngModelOptions.timezone) || new Date();
|
||||
self.activeDate = dateParser.fromTimezone($scope.datepickerOptions.initDate, ngModelOptions.getOption('timezone')) || new Date();
|
||||
$scope.$watch('datepickerOptions.initDate', function(initDate) {
|
||||
if (initDate && (ngModelCtrl.$isEmpty(ngModelCtrl.$modelValue) || ngModelCtrl.$invalid)) {
|
||||
self.activeDate = dateParser.fromTimezone(initDate, ngModelOptions.timezone);
|
||||
self.activeDate = dateParser.fromTimezone(initDate, ngModelOptions.getOption('timezone'));
|
||||
self.refreshView();
|
||||
}
|
||||
});
|
||||
@@ -178,8 +177,8 @@ angular.module('ui.bootstrap.datepicker', ['ui.bootstrap.dateparser', 'ui.bootst
|
||||
|
||||
var date = ngModelCtrl.$modelValue ? new Date(ngModelCtrl.$modelValue) : new Date();
|
||||
this.activeDate = !isNaN(date) ?
|
||||
dateParser.fromTimezone(date, ngModelOptions.timezone) :
|
||||
dateParser.fromTimezone(new Date(), ngModelOptions.timezone);
|
||||
dateParser.fromTimezone(date, ngModelOptions.getOption('timezone')) :
|
||||
dateParser.fromTimezone(new Date(), ngModelOptions.getOption('timezone'));
|
||||
|
||||
ngModelCtrl.$render = function() {
|
||||
self.render();
|
||||
@@ -192,7 +191,7 @@ angular.module('ui.bootstrap.datepicker', ['ui.bootstrap.dateparser', 'ui.bootst
|
||||
isValid = !isNaN(date);
|
||||
|
||||
if (isValid) {
|
||||
this.activeDate = dateParser.fromTimezone(date, ngModelOptions.timezone);
|
||||
this.activeDate = dateParser.fromTimezone(date, ngModelOptions.getOption('timezone'));
|
||||
} else if (!$datepickerSuppressError) {
|
||||
$log.error('Datepicker directive: "ng-model" value must be a Date object');
|
||||
}
|
||||
@@ -209,7 +208,7 @@ angular.module('ui.bootstrap.datepicker', ['ui.bootstrap.dateparser', 'ui.bootst
|
||||
}
|
||||
|
||||
var date = ngModelCtrl.$viewValue ? new Date(ngModelCtrl.$viewValue) : null;
|
||||
date = dateParser.fromTimezone(date, ngModelOptions.timezone);
|
||||
date = dateParser.fromTimezone(date, ngModelOptions.getOption('timezone'));
|
||||
ngModelCtrl.$setValidity('dateDisabled', !date ||
|
||||
this.element && !this.isDisabled(date));
|
||||
}
|
||||
@@ -217,9 +216,9 @@ angular.module('ui.bootstrap.datepicker', ['ui.bootstrap.dateparser', 'ui.bootst
|
||||
|
||||
this.createDateObject = function(date, format) {
|
||||
var model = ngModelCtrl.$viewValue ? new Date(ngModelCtrl.$viewValue) : null;
|
||||
model = dateParser.fromTimezone(model, ngModelOptions.timezone);
|
||||
model = dateParser.fromTimezone(model, ngModelOptions.getOption('timezone'));
|
||||
var today = new Date();
|
||||
today = dateParser.fromTimezone(today, ngModelOptions.timezone);
|
||||
today = dateParser.fromTimezone(today, ngModelOptions.getOption('timezone'));
|
||||
var time = this.compare(date, today);
|
||||
var dt = {
|
||||
date: date,
|
||||
@@ -265,9 +264,9 @@ angular.module('ui.bootstrap.datepicker', ['ui.bootstrap.dateparser', 'ui.bootst
|
||||
|
||||
$scope.select = function(date) {
|
||||
if ($scope.datepickerMode === self.minMode) {
|
||||
var dt = ngModelCtrl.$viewValue ? dateParser.fromTimezone(new Date(ngModelCtrl.$viewValue), ngModelOptions.timezone) : new Date(0, 0, 0, 0, 0, 0, 0);
|
||||
var dt = ngModelCtrl.$viewValue ? dateParser.fromTimezone(new Date(ngModelCtrl.$viewValue), ngModelOptions.getOption('timezone')) : new Date(0, 0, 0, 0, 0, 0, 0);
|
||||
dt.setFullYear(date.getFullYear(), date.getMonth(), date.getDate());
|
||||
dt = dateParser.toTimezone(dt, ngModelOptions.timezone);
|
||||
dt = dateParser.toTimezone(dt, ngModelOptions.getOption('timezone'));
|
||||
ngModelCtrl.$setViewValue(dt);
|
||||
ngModelCtrl.$render();
|
||||
} else {
|
||||
@@ -352,6 +351,37 @@ angular.module('ui.bootstrap.datepicker', ['ui.bootstrap.dateparser', 'ui.bootst
|
||||
$scope.datepickerMode = mode;
|
||||
$scope.datepickerOptions.datepickerMode = mode;
|
||||
}
|
||||
|
||||
function extractOptions(ngModelCtrl) {
|
||||
var ngModelOptions;
|
||||
|
||||
if (angular.version.minor < 6) { // in angular < 1.6 $options could be missing
|
||||
// guarantee a value
|
||||
ngModelOptions = ngModelCtrl.$options ||
|
||||
$scope.datepickerOptions.ngModelOptions ||
|
||||
datepickerConfig.ngModelOptions ||
|
||||
{};
|
||||
|
||||
// mimic 1.6+ api
|
||||
ngModelOptions.getOption = function (key) {
|
||||
return ngModelOptions[key];
|
||||
};
|
||||
} else { // in angular >=1.6 $options is always present
|
||||
// ng-model-options defaults timezone to null; don't let its precedence squash a non-null value
|
||||
var timezone = ngModelCtrl.$options.getOption('timezone') ||
|
||||
($scope.datepickerOptions.ngModelOptions ? $scope.datepickerOptions.ngModelOptions.timezone : null) ||
|
||||
(datepickerConfig.ngModelOptions ? datepickerConfig.ngModelOptions.timezone : null);
|
||||
|
||||
// values passed to createChild override existing values
|
||||
ngModelOptions = ngModelCtrl.$options // start with a ModelOptions instance
|
||||
.createChild(datepickerConfig.ngModelOptions) // lowest precedence
|
||||
.createChild($scope.datepickerOptions.ngModelOptions)
|
||||
.createChild(ngModelCtrl.$options) // highest precedence
|
||||
.createChild({timezone: timezone}); // to keep from squashing a non-null value
|
||||
}
|
||||
|
||||
return ngModelOptions;
|
||||
}
|
||||
}])
|
||||
|
||||
.controller('UibDaypickerController', ['$scope', '$element', 'dateFilter', function(scope, $element, dateFilter) {
|
||||
|
||||
@@ -32,11 +32,7 @@ function($scope, $element, $attrs, $compile, $log, $parse, $window, $document, $
|
||||
|
||||
this.init = function(_ngModel_) {
|
||||
ngModel = _ngModel_;
|
||||
ngModelOptions = angular.isObject(_ngModel_.$options) ?
|
||||
_ngModel_.$options :
|
||||
{
|
||||
timezone: null
|
||||
};
|
||||
ngModelOptions = extractOptions(ngModel);
|
||||
closeOnDateSelection = angular.isDefined($attrs.closeOnDateSelection) ?
|
||||
$scope.$parent.$eval($attrs.closeOnDateSelection) :
|
||||
datepickerPopupConfig.closeOnDateSelection;
|
||||
@@ -127,13 +123,13 @@ function($scope, $element, $attrs, $compile, $log, $parse, $window, $document, $
|
||||
value = new Date(value);
|
||||
}
|
||||
|
||||
$scope.date = dateParser.fromTimezone(value, ngModelOptions.timezone);
|
||||
$scope.date = dateParser.fromTimezone(value, ngModelOptions.getOption('timezone'));
|
||||
|
||||
return dateParser.filter($scope.date, dateFormat);
|
||||
});
|
||||
} else {
|
||||
ngModel.$formatters.push(function(value) {
|
||||
$scope.date = dateParser.fromTimezone(value, ngModelOptions.timezone);
|
||||
$scope.date = dateParser.fromTimezone(value, ngModelOptions.getOption('timezone'));
|
||||
return value;
|
||||
});
|
||||
}
|
||||
@@ -185,7 +181,7 @@ function($scope, $element, $attrs, $compile, $log, $parse, $window, $document, $
|
||||
|
||||
$scope.isDisabled = function(date) {
|
||||
if (date === 'today') {
|
||||
date = dateParser.fromTimezone(new Date(), ngModelOptions.timezone);
|
||||
date = dateParser.fromTimezone(new Date(), ngModelOptions.getOption('timezone'));
|
||||
}
|
||||
|
||||
var dates = {};
|
||||
@@ -242,7 +238,8 @@ function($scope, $element, $attrs, $compile, $log, $parse, $window, $document, $
|
||||
date = new Date($scope.date);
|
||||
date.setFullYear(today.getFullYear(), today.getMonth(), today.getDate());
|
||||
} else {
|
||||
date = new Date(today.setHours(0, 0, 0, 0));
|
||||
date = dateParser.fromTimezone(today, ngModelOptions.getOption('timezone'));
|
||||
date.setHours(0, 0, 0, 0);
|
||||
}
|
||||
}
|
||||
$scope.dateSelection(date);
|
||||
@@ -332,11 +329,11 @@ function($scope, $element, $attrs, $compile, $log, $parse, $window, $document, $
|
||||
if (angular.isString(viewValue)) {
|
||||
var date = parseDateString(viewValue);
|
||||
if (!isNaN(date)) {
|
||||
return dateParser.fromTimezone(date, ngModelOptions.timezone);
|
||||
return dateParser.toTimezone(date, ngModelOptions.getOption('timezone'));
|
||||
}
|
||||
}
|
||||
|
||||
return ngModel.$options && ngModel.$options.allowInvalid ? viewValue : undefined;
|
||||
return ngModelOptions.getOption('allowInvalid') ? viewValue : undefined;
|
||||
}
|
||||
|
||||
function validator(modelValue, viewValue) {
|
||||
@@ -411,6 +408,28 @@ function($scope, $element, $attrs, $compile, $log, $parse, $window, $document, $
|
||||
}
|
||||
}
|
||||
|
||||
function extractOptions(ngModelCtrl) {
|
||||
var ngModelOptions;
|
||||
|
||||
if (angular.version.minor < 6) { // in angular < 1.6 $options could be missing
|
||||
// guarantee a value
|
||||
ngModelOptions = angular.isObject(ngModelCtrl.$options) ?
|
||||
ngModelCtrl.$options :
|
||||
{
|
||||
timezone: null
|
||||
};
|
||||
|
||||
// mimic 1.6+ api
|
||||
ngModelOptions.getOption = function (key) {
|
||||
return ngModelOptions[key];
|
||||
};
|
||||
} else { // in angular >=1.6 $options is always present
|
||||
ngModelOptions = ngModelCtrl.$options;
|
||||
}
|
||||
|
||||
return ngModelOptions;
|
||||
}
|
||||
|
||||
$scope.$on('uib:datepicker.mode', function() {
|
||||
$timeout(positionPopup, 0, false);
|
||||
});
|
||||
|
||||
@@ -397,7 +397,7 @@ describe('datepicker popup', function() {
|
||||
it('stops the ESC key from propagating if the dropdown is open, but not when closed', function() {
|
||||
var documentKey = -1;
|
||||
var getKey = function(evt) { documentKey = evt.which; };
|
||||
$document.bind('keydown', getKey);
|
||||
$document.on('keydown', getKey);
|
||||
|
||||
triggerKeyDown(inputEl, 'esc');
|
||||
expect(documentKey).toBe(-1);
|
||||
@@ -405,7 +405,7 @@ describe('datepicker popup', function() {
|
||||
triggerKeyDown(inputEl, 'esc');
|
||||
expect(documentKey).toBe(27);
|
||||
|
||||
$document.unbind('keydown', getKey);
|
||||
$document.off('keydown', getKey);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@ Each of these parts need to be used as attribute directives.
|
||||
* `dropdown-append-to-body`
|
||||
<small class="badge">B</small>
|
||||
_(Default: `false`)_ -
|
||||
Appends the inner dropdown-menu to the body element.
|
||||
Appends the inner dropdown-menu to the body element if the attribute is present without a value, or with a non `false` value.
|
||||
|
||||
* `is-open`
|
||||
<small class="badge">$</small>
|
||||
|
||||
+118
-34
@@ -1,14 +1,33 @@
|
||||
angular.module('ui.bootstrap.dropdown', ['ui.bootstrap.position'])
|
||||
angular.module('ui.bootstrap.dropdown', ['ui.bootstrap.multiMap', 'ui.bootstrap.position'])
|
||||
|
||||
.constant('uibDropdownConfig', {
|
||||
appendToOpenClass: 'uib-dropdown-open',
|
||||
openClass: 'open'
|
||||
})
|
||||
|
||||
.service('uibDropdownService', ['$document', '$rootScope', function($document, $rootScope) {
|
||||
.service('uibDropdownService', ['$document', '$rootScope', '$$multiMap', function($document, $rootScope, $$multiMap) {
|
||||
var openScope = null;
|
||||
var openedContainers = $$multiMap.createNew();
|
||||
|
||||
this.open = function(dropdownScope, element) {
|
||||
this.isOnlyOpen = function(dropdownScope, appendTo) {
|
||||
var openedDropdowns = openedContainers.get(appendTo);
|
||||
if (openedDropdowns) {
|
||||
var openDropdown = openedDropdowns.reduce(function(toClose, dropdown) {
|
||||
if (dropdown.scope === dropdownScope) {
|
||||
return dropdown;
|
||||
}
|
||||
|
||||
return toClose;
|
||||
}, {});
|
||||
if (openDropdown) {
|
||||
return openedDropdowns.length === 1;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
this.open = function(dropdownScope, element, appendTo) {
|
||||
if (!openScope) {
|
||||
$document.on('click', closeDropdown);
|
||||
}
|
||||
@@ -18,20 +37,58 @@ angular.module('ui.bootstrap.dropdown', ['ui.bootstrap.position'])
|
||||
}
|
||||
|
||||
openScope = dropdownScope;
|
||||
|
||||
if (!appendTo) {
|
||||
return;
|
||||
}
|
||||
|
||||
var openedDropdowns = openedContainers.get(appendTo);
|
||||
if (openedDropdowns) {
|
||||
var openedScopes = openedDropdowns.map(function(dropdown) {
|
||||
return dropdown.scope;
|
||||
});
|
||||
if (openedScopes.indexOf(dropdownScope) === -1) {
|
||||
openedContainers.put(appendTo, {
|
||||
scope: dropdownScope
|
||||
});
|
||||
}
|
||||
} else {
|
||||
openedContainers.put(appendTo, {
|
||||
scope: dropdownScope
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
this.close = function(dropdownScope, element) {
|
||||
this.close = function(dropdownScope, element, appendTo) {
|
||||
if (openScope === dropdownScope) {
|
||||
openScope = null;
|
||||
$document.off('click', closeDropdown);
|
||||
$document.off('keydown', this.keybindFilter);
|
||||
openScope = null;
|
||||
}
|
||||
|
||||
if (!appendTo) {
|
||||
return;
|
||||
}
|
||||
|
||||
var openedDropdowns = openedContainers.get(appendTo);
|
||||
if (openedDropdowns) {
|
||||
var dropdownToClose = openedDropdowns.reduce(function(toClose, dropdown) {
|
||||
if (dropdown.scope === dropdownScope) {
|
||||
return dropdown;
|
||||
}
|
||||
|
||||
return toClose;
|
||||
}, {});
|
||||
if (dropdownToClose) {
|
||||
openedContainers.remove(appendTo, dropdownToClose);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
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; }
|
||||
if (!openScope || !openScope.isOpen) { return; }
|
||||
|
||||
if (evt && openScope.getAutoClose() === 'disabled') { return; }
|
||||
|
||||
@@ -48,8 +105,8 @@ angular.module('ui.bootstrap.dropdown', ['ui.bootstrap.position'])
|
||||
return;
|
||||
}
|
||||
|
||||
openScope.isOpen = false;
|
||||
openScope.focusToggleElement();
|
||||
openScope.isOpen = false;
|
||||
|
||||
if (!$rootScope.$$phase) {
|
||||
openScope.$apply();
|
||||
@@ -57,6 +114,11 @@ angular.module('ui.bootstrap.dropdown', ['ui.bootstrap.position'])
|
||||
};
|
||||
|
||||
this.keybindFilter = function(evt) {
|
||||
if (!openScope) {
|
||||
// see this.close as ESC could have been pressed which kills the scope so we can not proceed
|
||||
return;
|
||||
}
|
||||
|
||||
var dropdownElement = openScope.getDropdownElement();
|
||||
var toggleElement = openScope.getToggleElement();
|
||||
var dropdownElementTargeted = dropdownElement && dropdownElement[0].contains(evt.target);
|
||||
@@ -82,8 +144,6 @@ angular.module('ui.bootstrap.dropdown', ['ui.bootstrap.position'])
|
||||
getIsOpen,
|
||||
setIsOpen = angular.noop,
|
||||
toggleInvoker = $attrs.onToggle ? $parse($attrs.onToggle) : angular.noop,
|
||||
appendToBody = false,
|
||||
appendTo = null,
|
||||
keynavEnabled = false,
|
||||
selectedOption = null,
|
||||
body = $document.find('body');
|
||||
@@ -100,26 +160,7 @@ angular.module('ui.bootstrap.dropdown', ['ui.bootstrap.position'])
|
||||
});
|
||||
}
|
||||
|
||||
if (angular.isDefined($attrs.dropdownAppendTo)) {
|
||||
var appendToEl = $parse($attrs.dropdownAppendTo)(scope);
|
||||
if (appendToEl) {
|
||||
appendTo = angular.element(appendToEl);
|
||||
}
|
||||
}
|
||||
|
||||
appendToBody = angular.isDefined($attrs.dropdownAppendToBody);
|
||||
keynavEnabled = angular.isDefined($attrs.keyboardNav);
|
||||
|
||||
if (appendToBody && !appendTo) {
|
||||
appendTo = body;
|
||||
}
|
||||
|
||||
if (appendTo && self.dropdownMenu) {
|
||||
appendTo.append(self.dropdownMenu);
|
||||
$element.on('$destroy', function handleDestroyEvent() {
|
||||
self.dropdownMenu.remove();
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
this.toggle = function(open) {
|
||||
@@ -191,7 +232,42 @@ angular.module('ui.bootstrap.dropdown', ['ui.bootstrap.position'])
|
||||
}
|
||||
};
|
||||
|
||||
function removeDropdownMenu() {
|
||||
$element.append(self.dropdownMenu);
|
||||
}
|
||||
|
||||
scope.$watch('isOpen', function(isOpen, wasOpen) {
|
||||
var appendTo = null,
|
||||
appendToBody = false;
|
||||
|
||||
if (angular.isDefined($attrs.dropdownAppendTo)) {
|
||||
var appendToEl = $parse($attrs.dropdownAppendTo)(scope);
|
||||
if (appendToEl) {
|
||||
appendTo = angular.element(appendToEl);
|
||||
}
|
||||
}
|
||||
|
||||
if (angular.isDefined($attrs.dropdownAppendToBody)) {
|
||||
var appendToBodyValue = $parse($attrs.dropdownAppendToBody)(scope);
|
||||
if (appendToBodyValue !== false) {
|
||||
appendToBody = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (appendToBody && !appendTo) {
|
||||
appendTo = body;
|
||||
}
|
||||
|
||||
if (appendTo && self.dropdownMenu) {
|
||||
if (isOpen) {
|
||||
appendTo.append(self.dropdownMenu);
|
||||
$element.on('$destroy', removeDropdownMenu);
|
||||
} else {
|
||||
$element.off('$destroy', removeDropdownMenu);
|
||||
removeDropdownMenu();
|
||||
}
|
||||
}
|
||||
|
||||
if (appendTo && self.dropdownMenu) {
|
||||
var pos = $position.positionElements($element, self.dropdownMenu, 'bottom-left', true),
|
||||
css,
|
||||
@@ -239,10 +315,18 @@ angular.module('ui.bootstrap.dropdown', ['ui.bootstrap.position'])
|
||||
}
|
||||
|
||||
var openContainer = appendTo ? appendTo : $element;
|
||||
var hasOpenClass = openContainer.hasClass(appendTo ? appendToOpenClass : openClass);
|
||||
var dropdownOpenClass = appendTo ? appendToOpenClass : openClass;
|
||||
var hasOpenClass = openContainer.hasClass(dropdownOpenClass);
|
||||
var isOnlyOpen = uibDropdownService.isOnlyOpen($scope, appendTo);
|
||||
|
||||
if (hasOpenClass === !isOpen) {
|
||||
$animate[isOpen ? 'addClass' : 'removeClass'](openContainer, appendTo ? appendToOpenClass : openClass).then(function() {
|
||||
var toggleClass;
|
||||
if (appendTo) {
|
||||
toggleClass = !isOnlyOpen ? 'addClass' : 'removeClass';
|
||||
} else {
|
||||
toggleClass = isOpen ? 'addClass' : 'removeClass';
|
||||
}
|
||||
$animate[toggleClass](openContainer, dropdownOpenClass).then(function() {
|
||||
if (angular.isDefined(isOpen) && isOpen !== wasOpen) {
|
||||
toggleInvoker($scope, { open: !!isOpen });
|
||||
}
|
||||
@@ -265,9 +349,9 @@ angular.module('ui.bootstrap.dropdown', ['ui.bootstrap.position'])
|
||||
}
|
||||
|
||||
scope.focusToggleElement();
|
||||
uibDropdownService.open(scope, $element);
|
||||
uibDropdownService.open(scope, $element, appendTo);
|
||||
} else {
|
||||
uibDropdownService.close(scope, $element);
|
||||
uibDropdownService.close(scope, $element, appendTo);
|
||||
if (self.dropdownMenuTemplateUrl) {
|
||||
if (templateScope) {
|
||||
templateScope.$destroy();
|
||||
@@ -340,7 +424,7 @@ angular.module('ui.bootstrap.dropdown', ['ui.bootstrap.position'])
|
||||
}
|
||||
};
|
||||
|
||||
element.bind('click', toggleDropdown);
|
||||
element.on('click', toggleDropdown);
|
||||
|
||||
// WAI-ARIA
|
||||
element.attr({ 'aria-haspopup': true, 'aria-expanded': false });
|
||||
@@ -349,7 +433,7 @@ angular.module('ui.bootstrap.dropdown', ['ui.bootstrap.position'])
|
||||
});
|
||||
|
||||
scope.$on('$destroy', function() {
|
||||
element.unbind('click', toggleDropdown);
|
||||
element.off('click', toggleDropdown);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
require('../multiMap');
|
||||
require('../position/index-nocss.js');
|
||||
require('./dropdown');
|
||||
|
||||
|
||||
@@ -99,11 +99,11 @@ describe('uib-dropdown', function() {
|
||||
expect(elm1).not.toHaveClass(dropdownConfig.openClass);
|
||||
expect(elm2).not.toHaveClass(dropdownConfig.openClass);
|
||||
|
||||
clickDropdownToggle( elm1 );
|
||||
clickDropdownToggle(elm1);
|
||||
expect(elm1).toHaveClass(dropdownConfig.openClass);
|
||||
expect(elm2).not.toHaveClass(dropdownConfig.openClass);
|
||||
|
||||
clickDropdownToggle( elm2 );
|
||||
clickDropdownToggle(elm2);
|
||||
expect(elm1).not.toHaveClass(dropdownConfig.openClass);
|
||||
expect(elm2).toHaveClass(dropdownConfig.openClass);
|
||||
});
|
||||
@@ -215,43 +215,198 @@ describe('uib-dropdown', function() {
|
||||
});
|
||||
|
||||
describe('using dropdown-append-to-body', function() {
|
||||
function dropdown() {
|
||||
return $compile('<li uib-dropdown dropdown-append-to-body><a href uib-dropdown-toggle></a><ul uib-dropdown-menu id="dropdown-menu"><li><a href>Hello On Body</a></li></ul></li>')($rootScope);
|
||||
}
|
||||
describe('with no value', function() {
|
||||
function dropdown() {
|
||||
return $compile('<li uib-dropdown dropdown-append-to-body><a href uib-dropdown-toggle></a><ul uib-dropdown-menu id="dropdown-menu"><li><a href>Hello On Body</a></li></ul></li>')($rootScope);
|
||||
}
|
||||
|
||||
beforeEach(function() {
|
||||
element = dropdown();
|
||||
$document.find('body').append(element);
|
||||
beforeEach(function() {
|
||||
element = dropdown();
|
||||
$document.find('body').append(element);
|
||||
});
|
||||
|
||||
afterEach(function() {
|
||||
element.remove();
|
||||
});
|
||||
|
||||
it('does not add the menu to the body', function() {
|
||||
expect($document.find('#dropdown-menu').parent()[0]).not.toBe($document.find('body')[0]);
|
||||
});
|
||||
|
||||
describe('when toggled open', function() {
|
||||
var toggle;
|
||||
beforeEach(function() {
|
||||
toggle = element.find('[uib-dropdown-toggle]');
|
||||
toggle.trigger('click');
|
||||
});
|
||||
it('adds the menu to the body', function() {
|
||||
expect($document.find('#dropdown-menu').parent()[0]).toBe($document.find('body')[0]);
|
||||
});
|
||||
|
||||
describe('when toggled closed', function() {
|
||||
beforeEach(function() {
|
||||
toggle.trigger('click');
|
||||
});
|
||||
it('removes the menu from body', function() {
|
||||
expect($document.find('#dropdown-menu').parent()[0]).not.toBe($document.find('body')[0]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when closed by clicking on menu', function() {
|
||||
var menu;
|
||||
beforeEach(function() {
|
||||
menu = $document.find('#dropdown-menu a');
|
||||
menu.focus();
|
||||
menu.trigger('click');
|
||||
});
|
||||
it('focuses the dropdown element on close', function() {
|
||||
expect(document.activeElement).toBe(toggle[0]);
|
||||
});
|
||||
it('removes the menu from body', function() {
|
||||
expect($document.find('#dropdown-menu').parent()[0]).not.toBe($document.find('body')[0]);
|
||||
});
|
||||
});
|
||||
describe('when the dropdown is removed', function() {
|
||||
beforeEach(function() {
|
||||
element.remove();
|
||||
$rootScope.$digest();
|
||||
});
|
||||
it('removes the menu from body', function() {
|
||||
expect($document.find('#dropdown-menu').parent()[0]).not.toBe($document.find('body')[0]);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(function() {
|
||||
element.remove();
|
||||
});
|
||||
describe('with a value', function() {
|
||||
function dropdown() {
|
||||
return $compile('<li uib-dropdown dropdown-append-to-body="appendToBody"><a href uib-dropdown-toggle></a><ul uib-dropdown-menu id="dropdown-menu"><li><a href>Hello On Body</a></li></ul></li>')($rootScope);
|
||||
}
|
||||
describe('that is not false', function() {
|
||||
beforeEach(function() {
|
||||
$rootScope.appendToBody = 'sure';
|
||||
|
||||
it('adds the menu to the body', function() {
|
||||
expect($document.find('#dropdown-menu').parent()[0]).toBe($document.find('body')[0]);
|
||||
});
|
||||
element = dropdown();
|
||||
$document.find('body').append(element);
|
||||
});
|
||||
|
||||
it('focuses the dropdown element on close', function() {
|
||||
var toggle = element.find('[uib-dropdown-toggle]');
|
||||
var menu = $document.find('#dropdown-menu a');
|
||||
toggle.trigger('click');
|
||||
menu.focus();
|
||||
afterEach(function() {
|
||||
element.remove();
|
||||
});
|
||||
it('does not add the menu to the body', function() {
|
||||
expect($document.find('#dropdown-menu').parent()[0]).not.toBe($document.find('body')[0]);
|
||||
});
|
||||
|
||||
menu.trigger('click');
|
||||
describe('when toggled open', function() {
|
||||
var toggle;
|
||||
beforeEach(function() {
|
||||
toggle = element.find('[uib-dropdown-toggle]');
|
||||
toggle.trigger('click');
|
||||
});
|
||||
it('adds the menu to the body', function() {
|
||||
expect($document.find('#dropdown-menu').parent()[0]).toBe($document.find('body')[0]);
|
||||
});
|
||||
|
||||
expect(document.activeElement).toBe(toggle[0]);
|
||||
});
|
||||
describe('when toggled closed', function() {
|
||||
beforeEach(function() {
|
||||
toggle.trigger('click');
|
||||
});
|
||||
it('removes the menu from body', function() {
|
||||
expect($document.find('#dropdown-menu').parent()[0]).not.toBe($document.find('body')[0]);
|
||||
});
|
||||
});
|
||||
|
||||
it('removes the menu when the dropdown is removed', function() {
|
||||
element.remove();
|
||||
$rootScope.$digest();
|
||||
expect($document.find('#dropdown-menu').length).toEqual(0);
|
||||
describe('when closed by clicking on menu', function() {
|
||||
var menu;
|
||||
beforeEach(function() {
|
||||
menu = $document.find('#dropdown-menu a');
|
||||
menu.focus();
|
||||
menu.trigger('click');
|
||||
});
|
||||
it('focuses the dropdown element on close', function() {
|
||||
expect(document.activeElement).toBe(toggle[0]);
|
||||
});
|
||||
it('removes the menu from body', function() {
|
||||
expect($document.find('#dropdown-menu').parent()[0]).not.toBe($document.find('body')[0]);
|
||||
});
|
||||
});
|
||||
describe('when the dropdown is removed', function() {
|
||||
beforeEach(function() {
|
||||
element.remove();
|
||||
$rootScope.$digest();
|
||||
});
|
||||
it('removes the menu from body', function() {
|
||||
expect($document.find('#dropdown-menu').parent()[0]).not.toBe($document.find('body')[0]);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('that is false', function() {
|
||||
beforeEach(function() {
|
||||
$rootScope.appendToBody = false;
|
||||
|
||||
element = dropdown();
|
||||
$document.find('body').append(element);
|
||||
});
|
||||
|
||||
afterEach(function() {
|
||||
element.remove();
|
||||
});
|
||||
|
||||
it('does not add the menu to the body', function() {
|
||||
expect($document.find('#dropdown-menu').parent()[0]).not.toBe($document.find('body')[0]);
|
||||
});
|
||||
|
||||
describe('when toggled open', function() {
|
||||
var toggle;
|
||||
beforeEach(function() {
|
||||
toggle = element.find('[uib-dropdown-toggle]');
|
||||
toggle.trigger('click');
|
||||
});
|
||||
it('does not add the menu to the body', function() {
|
||||
expect($document.find('#dropdown-menu').parent()[0]).not.toBe($document.find('body')[0]);
|
||||
});
|
||||
|
||||
describe('when toggled closed', function() {
|
||||
beforeEach(function() {
|
||||
toggle.trigger('click');
|
||||
});
|
||||
it('does not remove the menu', function() {
|
||||
expect($document.find('#dropdown-menu').length).not.toEqual(0);
|
||||
});
|
||||
});
|
||||
|
||||
describe('when closed by clicking on menu', function() {
|
||||
var menu;
|
||||
beforeEach(function() {
|
||||
menu = $document.find('#dropdown-menu a');
|
||||
menu.focus();
|
||||
menu.trigger('click');
|
||||
});
|
||||
it('focuses the dropdown element on close', function() {
|
||||
expect(document.activeElement).toBe(toggle[0]);
|
||||
});
|
||||
it('does not removes the menu from body', function() {
|
||||
expect($document.find('#dropdown-menu').parent()[0]).not.toBe($document.find('body')[0]);
|
||||
});
|
||||
});
|
||||
describe('when the dropdown is removed', function() {
|
||||
beforeEach(function() {
|
||||
element.remove();
|
||||
$rootScope.$digest();
|
||||
});
|
||||
it('removes the menu from body', function() {
|
||||
expect($document.find('#dropdown-menu').parent()[0]).not.toBe($document.find('body')[0]);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('using dropdown-append-to', function() {
|
||||
var initialPage;
|
||||
var initialPage, container;
|
||||
|
||||
function dropdown() {
|
||||
return $compile('<li uib-dropdown dropdown-append-to="appendTo"><a href uib-dropdown-toggle></a><ul class="dropdown-menu" uib-dropdown-menu id="dropdown-menu"><li><a href>Hello On Container</a></li></ul></li>')($rootScope);
|
||||
@@ -260,7 +415,7 @@ describe('uib-dropdown', function() {
|
||||
beforeEach(function() {
|
||||
$document.find('body').append(angular.element('<div id="dropdown-container"></div>'));
|
||||
|
||||
$rootScope.appendTo = $document.find('#dropdown-container');
|
||||
$rootScope.appendTo = container = $document.find('#dropdown-container');
|
||||
|
||||
element = dropdown();
|
||||
$document.find('body').append(element);
|
||||
@@ -271,35 +426,95 @@ describe('uib-dropdown', function() {
|
||||
$document.find('#dropdown-container').remove();
|
||||
});
|
||||
|
||||
it('appends to container', function() {
|
||||
expect($document.find('#dropdown-menu').parent()[0].id).toBe('dropdown-container');
|
||||
it('does not add the menu to the container', function() {
|
||||
expect($document.find('#dropdown-menu').parent()[0]).not.toBe(container[0]);
|
||||
});
|
||||
it('does not add open class on container', function() {
|
||||
expect(container).not.toHaveClass('uib-dropdown-open');
|
||||
});
|
||||
|
||||
it('toggles open class on container', function() {
|
||||
describe('when toggled open', function() {
|
||||
var toggle;
|
||||
beforeEach(function() {
|
||||
toggle = element.find('[uib-dropdown-toggle]');
|
||||
toggle.trigger('click');
|
||||
});
|
||||
it('adds the menu to the container', function() {
|
||||
expect($document.find('#dropdown-menu').parent()[0]).toBe(container[0]);
|
||||
});
|
||||
it('adds open class on container', function() {
|
||||
expect(container).toHaveClass('uib-dropdown-open');
|
||||
});
|
||||
|
||||
describe('when toggled closed', function() {
|
||||
beforeEach(function() {
|
||||
toggle.trigger('click');
|
||||
});
|
||||
it('removes the menu from the container', function() {
|
||||
expect($document.find('#dropdown-menu').parent()[0]).not.toBe($document.find('body')[0]);
|
||||
});
|
||||
it('removes open class from container', function() {
|
||||
expect(container).not.toHaveClass('uib-dropdown-open');
|
||||
});
|
||||
});
|
||||
|
||||
describe('when closed by clicking on menu', function() {
|
||||
var menu;
|
||||
beforeEach(function() {
|
||||
menu = $document.find('#dropdown-menu a');
|
||||
menu.focus();
|
||||
menu.trigger('click');
|
||||
});
|
||||
it('focuses the dropdown element on close', function() {
|
||||
expect(document.activeElement).toBe(toggle[0]);
|
||||
});
|
||||
it('removes the menu from the container', function() {
|
||||
expect($document.find('#dropdown-menu').parent()[0]).not.toBe($document.find('body')[0]);
|
||||
});
|
||||
it('removes open class from container', function() {
|
||||
expect(container).not.toHaveClass('uib-dropdown-open');
|
||||
});
|
||||
});
|
||||
describe('when the dropdown is removed', function() {
|
||||
beforeEach(function() {
|
||||
element.remove();
|
||||
$rootScope.$digest();
|
||||
});
|
||||
it('removes the menu from the container', function() {
|
||||
expect($document.find('#dropdown-menu').parent()[0]).not.toBe($document.find('body')[0]);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('using dropdown-append-to with two dropdowns', function() {
|
||||
function dropdown() {
|
||||
return $compile('<div><div class="dropdown1" uib-dropdown dropdown-append-to="appendTo" on-toggle="log(1, open)"><a href uib-dropdown-toggle></a><ul class="dropdown-menu" uib-dropdown-menu id="dropdown-menu"><li><a href>Hello On Container</a></li></ul></div><div class="dropdown2" uib-dropdown dropdown-append-to="appendTo" on-toggle="log(2, open)"><a href uib-dropdown-toggle></a><ul class="dropdown-menu" uib-dropdown-menu id="dropdown-menu"><li><a href>Hello On Container</a></li></ul></div></div>')($rootScope);
|
||||
}
|
||||
|
||||
beforeEach(function() {
|
||||
$document.find('body').append(angular.element('<div id="dropdown-container"></div>'));
|
||||
|
||||
$rootScope.appendTo = $document.find('#dropdown-container');
|
||||
$rootScope.log = jasmine.createSpy('log');
|
||||
|
||||
element = dropdown();
|
||||
$document.find('body').append(element);
|
||||
});
|
||||
|
||||
afterEach(function() {
|
||||
// Cleanup the extra elements we appended
|
||||
$document.find('#dropdown-container').remove();
|
||||
});
|
||||
|
||||
it('should keep the class when toggling from one dropdown to another with the same container', function() {
|
||||
var container = $document.find('#dropdown-container');
|
||||
|
||||
expect(container).not.toHaveClass('uib-dropdown-open');
|
||||
element.find('[uib-dropdown-toggle]').click();
|
||||
element.find('.dropdown1 [uib-dropdown-toggle]').click();
|
||||
expect(container).toHaveClass('uib-dropdown-open');
|
||||
element.find('.dropdown2 [uib-dropdown-toggle]').click();
|
||||
expect(container).toHaveClass('uib-dropdown-open');
|
||||
element.find('[uib-dropdown-toggle]').click();
|
||||
expect(container).not.toHaveClass('uib-dropdown-open');
|
||||
});
|
||||
|
||||
it('focuses the dropdown element on close', function() {
|
||||
var toggle = element.find('[uib-dropdown-toggle]');
|
||||
var menu = $document.find('#dropdown-menu a');
|
||||
toggle.trigger('click');
|
||||
menu.focus();
|
||||
|
||||
menu.trigger('click');
|
||||
|
||||
expect(document.activeElement).toBe(toggle[0]);
|
||||
});
|
||||
|
||||
it('removes the menu when the dropdown is removed', function() {
|
||||
element.remove();
|
||||
$rootScope.$digest();
|
||||
expect($document.find('#dropdown-menu').length).toEqual(0);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
+33
-14
@@ -1,25 +1,44 @@
|
||||
<div ng-controller="ModalDemoCtrl">
|
||||
<div ng-controller="ModalDemoCtrl as $ctrl" class="modal-demo">
|
||||
<script type="text/ng-template" id="myModalContent.html">
|
||||
<div class="modal-header">
|
||||
<h3 class="modal-title">I'm a modal!</h3>
|
||||
<h3 class="modal-title" id="modal-title">I'm a modal!</h3>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="modal-body" id="modal-body">
|
||||
<ul>
|
||||
<li ng-repeat="item in items">
|
||||
<a href="#" ng-click="$event.preventDefault(); selected.item = item">{{ item }}</a>
|
||||
<li ng-repeat="item in $ctrl.items">
|
||||
<a href="#" ng-click="$event.preventDefault(); $ctrl.selected.item = item">{{ item }}</a>
|
||||
</li>
|
||||
</ul>
|
||||
Selected: <b>{{ selected.item }}</b>
|
||||
Selected: <b>{{ $ctrl.selected.item }}</b>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button class="btn btn-primary" type="button" ng-click="ok()">OK</button>
|
||||
<button class="btn btn-warning" type="button" ng-click="cancel()">Cancel</button>
|
||||
<button class="btn btn-primary" type="button" ng-click="$ctrl.ok()">OK</button>
|
||||
<button class="btn btn-warning" type="button" ng-click="$ctrl.cancel()">Cancel</button>
|
||||
</div>
|
||||
</script>
|
||||
<script type="text/ng-template" id="stackedModal.html">
|
||||
<div class="modal-header">
|
||||
<h3 class="modal-title" id="modal-title-{{name}}">The {{name}} modal!</h3>
|
||||
</div>
|
||||
<div class="modal-body" id="modal-body-{{name}}">
|
||||
Having multiple modals open at once is probably bad UX but it's technically possible.
|
||||
</div>
|
||||
</script>
|
||||
|
||||
<button type="button" class="btn btn-default" ng-click="open()">Open me!</button>
|
||||
<button type="button" class="btn btn-default" ng-click="open('lg')">Large modal</button>
|
||||
<button type="button" class="btn btn-default" ng-click="open('sm')">Small modal</button>
|
||||
<button type="button" class="btn btn-default" ng-click="toggleAnimation()">Toggle Animation ({{ animationsEnabled }})</button>
|
||||
<div ng-show="selected">Selection from a modal: {{ selected }}</div>
|
||||
</div>
|
||||
<button type="button" class="btn btn-default" ng-click="$ctrl.open()">Open me!</button>
|
||||
<button type="button" class="btn btn-default" ng-click="$ctrl.open('lg')">Large modal</button>
|
||||
<button type="button" class="btn btn-default" ng-click="$ctrl.open('sm')">Small modal</button>
|
||||
<button type="button"
|
||||
class="btn btn-default"
|
||||
ng-click="$ctrl.open('sm', '.modal-parent')">
|
||||
Modal appended to a custom parent
|
||||
</button>
|
||||
<button type="button" class="btn btn-default" ng-click="$ctrl.toggleAnimation()">Toggle Animation ({{ $ctrl.animationsEnabled }})</button>
|
||||
<button type="button" class="btn btn-default" ng-click="$ctrl.openComponentModal()">Open a component modal!</button>
|
||||
<button type="button" class="btn btn-default" ng-click="$ctrl.openMultipleModals()">
|
||||
Open multiple modals at once
|
||||
</button>
|
||||
<div ng-show="$ctrl.selected">Selection from a modal: {{ $ctrl.selected }}</div>
|
||||
<div class="modal-parent">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
+94
-19
@@ -1,51 +1,126 @@
|
||||
angular.module('ui.bootstrap.demo').controller('ModalDemoCtrl', function ($scope, $uibModal, $log) {
|
||||
angular.module('ui.bootstrap.demo').controller('ModalDemoCtrl', function ($uibModal, $log, $document) {
|
||||
var $ctrl = this;
|
||||
$ctrl.items = ['item1', 'item2', 'item3'];
|
||||
|
||||
$scope.items = ['item1', 'item2', 'item3'];
|
||||
|
||||
$scope.animationsEnabled = true;
|
||||
|
||||
$scope.open = function (size) {
|
||||
$ctrl.animationsEnabled = true;
|
||||
|
||||
$ctrl.open = function (size, parentSelector) {
|
||||
var parentElem = parentSelector ?
|
||||
angular.element($document[0].querySelector('.modal-demo ' + parentSelector)) : undefined;
|
||||
var modalInstance = $uibModal.open({
|
||||
animation: $scope.animationsEnabled,
|
||||
animation: $ctrl.animationsEnabled,
|
||||
ariaLabelledBy: 'modal-title',
|
||||
ariaDescribedBy: 'modal-body',
|
||||
templateUrl: 'myModalContent.html',
|
||||
controller: 'ModalInstanceCtrl',
|
||||
controllerAs: '$ctrl',
|
||||
size: size,
|
||||
appendTo: parentElem,
|
||||
resolve: {
|
||||
items: function () {
|
||||
return $scope.items;
|
||||
return $ctrl.items;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
modalInstance.result.then(function (selectedItem) {
|
||||
$scope.selected = selectedItem;
|
||||
$ctrl.selected = selectedItem;
|
||||
}, function () {
|
||||
$log.info('Modal dismissed at: ' + new Date());
|
||||
});
|
||||
};
|
||||
|
||||
$scope.toggleAnimation = function () {
|
||||
$scope.animationsEnabled = !$scope.animationsEnabled;
|
||||
$ctrl.openComponentModal = function () {
|
||||
var modalInstance = $uibModal.open({
|
||||
animation: $ctrl.animationsEnabled,
|
||||
component: 'modalComponent',
|
||||
resolve: {
|
||||
items: function () {
|
||||
return $ctrl.items;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
modalInstance.result.then(function (selectedItem) {
|
||||
$ctrl.selected = selectedItem;
|
||||
}, function () {
|
||||
$log.info('modal-component dismissed at: ' + new Date());
|
||||
});
|
||||
};
|
||||
|
||||
$ctrl.openMultipleModals = function () {
|
||||
$uibModal.open({
|
||||
animation: $ctrl.animationsEnabled,
|
||||
ariaLabelledBy: 'modal-title-bottom',
|
||||
ariaDescribedBy: 'modal-body-bottom',
|
||||
templateUrl: 'stackedModal.html',
|
||||
size: 'sm',
|
||||
controller: function($scope) {
|
||||
$scope.name = 'bottom';
|
||||
}
|
||||
});
|
||||
|
||||
$uibModal.open({
|
||||
animation: $ctrl.animationsEnabled,
|
||||
ariaLabelledBy: 'modal-title-top',
|
||||
ariaDescribedBy: 'modal-body-top',
|
||||
templateUrl: 'stackedModal.html',
|
||||
size: 'sm',
|
||||
controller: function($scope) {
|
||||
$scope.name = 'top';
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
$ctrl.toggleAnimation = function () {
|
||||
$ctrl.animationsEnabled = !$ctrl.animationsEnabled;
|
||||
};
|
||||
});
|
||||
|
||||
// Please note that $uibModalInstance represents a modal window (instance) dependency.
|
||||
// It is not the same as the $uibModal service used above.
|
||||
|
||||
angular.module('ui.bootstrap.demo').controller('ModalInstanceCtrl', function ($scope, $uibModalInstance, items) {
|
||||
|
||||
$scope.items = items;
|
||||
$scope.selected = {
|
||||
item: $scope.items[0]
|
||||
angular.module('ui.bootstrap.demo').controller('ModalInstanceCtrl', function ($uibModalInstance, items) {
|
||||
var $ctrl = this;
|
||||
$ctrl.items = items;
|
||||
$ctrl.selected = {
|
||||
item: $ctrl.items[0]
|
||||
};
|
||||
|
||||
$scope.ok = function () {
|
||||
$uibModalInstance.close($scope.selected.item);
|
||||
$ctrl.ok = function () {
|
||||
$uibModalInstance.close($ctrl.selected.item);
|
||||
};
|
||||
|
||||
$scope.cancel = function () {
|
||||
$ctrl.cancel = function () {
|
||||
$uibModalInstance.dismiss('cancel');
|
||||
};
|
||||
});
|
||||
|
||||
// Please note that the close and dismiss bindings are from $uibModalInstance.
|
||||
|
||||
angular.module('ui.bootstrap.demo').component('modalComponent', {
|
||||
templateUrl: 'myModalContent.html',
|
||||
bindings: {
|
||||
resolve: '<',
|
||||
close: '&',
|
||||
dismiss: '&'
|
||||
},
|
||||
controller: function () {
|
||||
var $ctrl = this;
|
||||
|
||||
$ctrl.$onInit = function () {
|
||||
$ctrl.items = $ctrl.resolve.items;
|
||||
$ctrl.selected = {
|
||||
item: $ctrl.items[0]
|
||||
};
|
||||
};
|
||||
|
||||
$ctrl.ok = function () {
|
||||
$ctrl.close({$value: $ctrl.selected.item});
|
||||
};
|
||||
|
||||
$ctrl.cancel = function () {
|
||||
$ctrl.dismiss({$value: 'cancel'});
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
+35
-13
@@ -1,5 +1,5 @@
|
||||
`$uibModal` is a service to create modal windows.
|
||||
Creating modals is straightforward: create a template, a controller and reference them when using `$uibModal`.
|
||||
Creating modals is straightforward: create a template and controller, and reference them when using `$uibModal`.
|
||||
|
||||
The `$uibModal` service has only one method: `open(options)`.
|
||||
|
||||
@@ -10,15 +10,23 @@ The `$uibModal` service has only one method: `open(options)`.
|
||||
* `animation`
|
||||
_(Type: `boolean`, Default: `true`)_ -
|
||||
Set to false to disable animations on new modal/backdrop. Does not toggle animations for modals/backdrops that are already displayed.
|
||||
|
||||
* `appendTo`
|
||||
|
||||
* `appendTo`
|
||||
_(Type: `angular.element`, Default: `body`: Example: `$document.find('aside').eq(0)`)_ -
|
||||
Appends the modal to a specific element.
|
||||
|
||||
|
||||
* `ariaDescribedBy`
|
||||
_(Type: `string`, `my-modal-description`)_ -
|
||||
Sets the [`aria-describedby`](https://www.w3.org/TR/wai-aria/states_and_properties#aria-describedby) property on the modal. The value should be an id (without the leading `#`) pointing to the element that describes your modal. Typically, this will be the text on your modal, but does not include something the user would interact with, like buttons or a form. Omitting this option will not impact sighted users but will weaken your accessibility support.
|
||||
|
||||
* `ariaLabelledBy`
|
||||
_(Type: `string`, `my-modal-title`)_ -
|
||||
Sets the [`aria-labelledby`](https://www.w3.org/TR/wai-aria/states_and_properties#aria-labelledby) property on the modal. The value should be an id (without the leading `#`) pointing to the element that labels your modal. Typically, this will be a header element. Omitting this option will not impact sighted users but will weaken your accessibility support.
|
||||
|
||||
* `backdrop`
|
||||
_(Type: `boolean|string`, Default: `true`)_ -
|
||||
Controls presence of a backdrop. Allowed values: `true` (default), `false` (no backdrop), `'static'` (disables modal closing by click on the backdrop).
|
||||
|
||||
|
||||
* `backdropClass`
|
||||
_(Type: `string`)_ -
|
||||
Additional CSS class(es) to be added to a modal backdrop template.
|
||||
@@ -27,15 +35,29 @@ The `$uibModal` service has only one method: `open(options)`.
|
||||
_(Type: `boolean`, Default: `false`)_ -
|
||||
When used with `controllerAs` & set to `true`, it will bind the $scope properties onto the controller.
|
||||
|
||||
* `component`
|
||||
_(Type: `string`, Example: `myComponent`)_ -
|
||||
A string reference to the component to be rendered that is registered with Angular's compiler. If using a directive, the directive must have `restrict: 'E'` and a template or templateUrl set.
|
||||
|
||||
It supports these bindings:
|
||||
|
||||
* `close` - A method that can be used to close a modal, passing a result. The result must be passed in this format: `{$value: myResult}`
|
||||
|
||||
* `dismiss` - A method that can be used to dismiss a modal, passing a result. The result must be passed in this format: `{$value: myRejectedResult}`
|
||||
|
||||
* `modalInstance` - The modal instance. This is the same `$uibModalInstance` injectable found when using `controller`.
|
||||
|
||||
* `resolve` - An object of the modal resolve values. See [UI Router resolves](#ui-router-resolves) for details.
|
||||
|
||||
* `controller`
|
||||
_(Type: `function|string|array`, Example: `MyModalController`)_ -
|
||||
A controller for the modal instance, either a controller name as a string, or an inline controller function, optionally wrapped in array notation for dependency injection. Allows the controller-as syntax. Has a special `$uibModalInstance` injectable to access the modal instance.
|
||||
|
||||
* `controllerAs`
|
||||
_(Type: `string`, Example: `ctrl`)_ -
|
||||
_(Type: `string`, Example: `ctrl`)_ -
|
||||
An alternative to the controller-as syntax. Requires the `controller` option to be provided as well.
|
||||
|
||||
* `keyboard` -
|
||||
* `keyboard` -
|
||||
_(Type: `boolean`, Default: `true`)_ -
|
||||
Indicates whether the dialog should be closable by hitting the ESC key.
|
||||
|
||||
@@ -76,7 +98,7 @@ The `$uibModal` service has only one method: `open(options)`.
|
||||
CSS class(es) to be added to the top modal window.
|
||||
|
||||
Global defaults may be set for `$uibModal` via `$uibModalProvider.options`.
|
||||
|
||||
|
||||
#### return
|
||||
|
||||
The `open` method returns a modal instance, an object with the following properties:
|
||||
@@ -103,8 +125,8 @@ The `open` method returns a modal instance, an object with the following propert
|
||||
|
||||
* `rendered`
|
||||
_(Type: `promise`)_ -
|
||||
Is resolved when a modal is rendered.
|
||||
|
||||
Is resolved when a modal is rendered.
|
||||
|
||||
---
|
||||
|
||||
The scope associated with modal's content is augmented with:
|
||||
@@ -125,9 +147,9 @@ Also, when using `bindToController`, you can define an `$onInit` method in the c
|
||||
|
||||
Events fired:
|
||||
|
||||
* `$uibUnscheduledDestruction` -
|
||||
* `$uibUnscheduledDestruction` -
|
||||
This event is fired if the $scope is destroyed via unexpected mechanism, such as it being passed in the modal options and a $route/$state transition occurs. The modal will also be dismissed.
|
||||
|
||||
|
||||
* `modal.closing` -
|
||||
This event is broadcast to the modal scope before the modal closes. If the listener calls preventDefault() on the event, then the modal will remain open.
|
||||
Also, the `$close` and `$dismiss` methods returns true if the event was executed. This event also includes a parameter for the result/reason and a boolean that indicates whether the modal is being closed (true) or dismissed.
|
||||
@@ -136,4 +158,4 @@ Events fired:
|
||||
|
||||
If one wants to have the modal resolve using [UI Router's](https://github.com/angular-ui/ui-router) pre-1.0 resolve mechanism, one can call `$uibResolve.setResolver('$resolve')` in the configuration phase of the application. One can also provide a custom resolver as well, as long as the signature conforms to UI Router's [$resolve](http://angular-ui.github.io/ui-router/site/#/api/ui.router.util.$resolve).
|
||||
|
||||
When the modal is opened with a controller, a `$resolve` object is exposed on the template with the resolved values from the resolve object.
|
||||
When the modal is opened with a controller, a `$resolve` object is exposed on the template with the resolved values from the resolve object. If using the component option, see details on how to access this object in component section of the modal documentation.
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
require('../multiMap');
|
||||
require('../position/index-nocss.js');
|
||||
require('../stackedMap');
|
||||
require('../../template/modal/window.html.js');
|
||||
|
||||
+142
-94
@@ -1,59 +1,4 @@
|
||||
angular.module('ui.bootstrap.modal', ['ui.bootstrap.stackedMap', 'ui.bootstrap.position'])
|
||||
/**
|
||||
* 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];
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
})
|
||||
|
||||
angular.module('ui.bootstrap.modal', ['ui.bootstrap.multiMap', 'ui.bootstrap.stackedMap', 'ui.bootstrap.position'])
|
||||
/**
|
||||
* Pluggable resolve mechanism for the modal resolve resolution
|
||||
* Supports UI Router's $resolve service
|
||||
@@ -163,7 +108,7 @@ angular.module('ui.bootstrap.modal', ['ui.bootstrap.stackedMap', 'ui.bootstrap.p
|
||||
// {@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.
|
||||
// Deferred object that will be resolved when this modal is rendered.
|
||||
var modalRenderDeferObj = $q.defer();
|
||||
// Resolve render promise post-digest
|
||||
scope.$$postDigest(function() {
|
||||
@@ -196,7 +141,7 @@ angular.module('ui.bootstrap.modal', ['ui.bootstrap.stackedMap', 'ui.bootstrap.p
|
||||
|
||||
/**
|
||||
* If something within the freshly-opened modal already has focus (perhaps via a
|
||||
* directive that causes focus). then no need to try and focus anything.
|
||||
* directive that causes focus) then there's no need to try to focus anything.
|
||||
*/
|
||||
if (!($document[0].activeElement && element[0].contains($document[0].activeElement))) {
|
||||
var inputWithAutofocus = element[0].querySelector('[autofocus]');
|
||||
@@ -254,12 +199,22 @@ angular.module('ui.bootstrap.modal', ['ui.bootstrap.stackedMap', 'ui.bootstrap.p
|
||||
};
|
||||
var topModalIndex = 0;
|
||||
var previousTopOpenedModal = null;
|
||||
var ARIA_HIDDEN_ATTRIBUTE_NAME = 'data-bootstrap-modal-aria-hidden-count';
|
||||
|
||||
//Modal focus behavior
|
||||
var tabbableSelector = 'a[href], area[href], input:not([disabled]):not([tabindex=\'-1\']), ' +
|
||||
'button:not([disabled]):not([tabindex=\'-1\']),select:not([disabled]):not([tabindex=\'-1\']), textarea:not([disabled]):not([tabindex=\'-1\']), ' +
|
||||
'iframe, object, embed, *[tabindex]:not([tabindex=\'-1\']), *[contenteditable=true]';
|
||||
var scrollbarPadding;
|
||||
var SNAKE_CASE_REGEXP = /[A-Z]/g;
|
||||
|
||||
// TODO: extract into common dependency with tooltip
|
||||
function snake_case(name) {
|
||||
var separator = '-';
|
||||
return name.replace(SNAKE_CASE_REGEXP, function(letter, pos) {
|
||||
return (pos ? separator : '') + letter.toLowerCase();
|
||||
});
|
||||
}
|
||||
|
||||
function isVisible(element) {
|
||||
return !!(element.offsetWidth ||
|
||||
@@ -464,10 +419,6 @@ angular.module('ui.bootstrap.modal', ['ui.bootstrap.stackedMap', 'ui.bootstrap.p
|
||||
var appendToElement = modal.appendTo,
|
||||
currBackdropIndex = backdropIndex();
|
||||
|
||||
if (!appendToElement.length) {
|
||||
throw new Error('appendTo element not found. Make sure that the element passed is in DOM.');
|
||||
}
|
||||
|
||||
if (currBackdropIndex >= 0 && !backdropDomEl) {
|
||||
backdropScope = $rootScope.$new(true);
|
||||
backdropScope.modalOptions = modal;
|
||||
@@ -496,6 +447,20 @@ angular.module('ui.bootstrap.modal', ['ui.bootstrap.stackedMap', 'ui.bootstrap.p
|
||||
}
|
||||
}
|
||||
|
||||
var content;
|
||||
if (modal.component) {
|
||||
content = document.createElement(snake_case(modal.component.name));
|
||||
content = angular.element(content);
|
||||
content.attr({
|
||||
resolve: '$resolve',
|
||||
'modal-instance': '$uibModalInstance',
|
||||
close: '$close($value)',
|
||||
dismiss: '$dismiss($value)'
|
||||
});
|
||||
} else {
|
||||
content = modal.content;
|
||||
}
|
||||
|
||||
// Set the top modal index based on the index of the previous top modal
|
||||
topModalIndex = previousTopOpenedModal ? parseInt(previousTopOpenedModal.value.modalDomEl.attr('index'), 10) + 1 : 0;
|
||||
var angularDomEl = angular.element('<div uib-modal-window="modal-window"></div>');
|
||||
@@ -504,6 +469,8 @@ angular.module('ui.bootstrap.modal', ['ui.bootstrap.stackedMap', 'ui.bootstrap.p
|
||||
'template-url': modal.windowTemplateUrl,
|
||||
'window-top-class': modal.windowTopClass,
|
||||
'role': 'dialog',
|
||||
'aria-labelledby': modal.ariaLabelledBy,
|
||||
'aria-describedby': modal.ariaDescribedBy,
|
||||
'size': modal.size,
|
||||
'index': topModalIndex,
|
||||
'animate': 'animate',
|
||||
@@ -511,7 +478,7 @@ angular.module('ui.bootstrap.modal', ['ui.bootstrap.stackedMap', 'ui.bootstrap.p
|
||||
'tabindex': -1,
|
||||
'uib-modal-animation-class': 'fade',
|
||||
'modal-in-class': 'in'
|
||||
}).html(modal.content);
|
||||
}).append(content);
|
||||
if (modal.windowClass) {
|
||||
angularDomEl.addClass(modal.windowClass);
|
||||
}
|
||||
@@ -530,25 +497,74 @@ angular.module('ui.bootstrap.modal', ['ui.bootstrap.stackedMap', 'ui.bootstrap.p
|
||||
|
||||
openedWindows.top().value.modalDomEl = angularDomEl;
|
||||
openedWindows.top().value.modalOpener = modalOpener;
|
||||
|
||||
applyAriaHidden(angularDomEl);
|
||||
|
||||
function applyAriaHidden(el) {
|
||||
if (!el || el[0].tagName === 'BODY') {
|
||||
return;
|
||||
}
|
||||
|
||||
getSiblings(el).forEach(function(sibling) {
|
||||
var elemIsAlreadyHidden = sibling.getAttribute('aria-hidden') === 'true',
|
||||
ariaHiddenCount = parseInt(sibling.getAttribute(ARIA_HIDDEN_ATTRIBUTE_NAME), 10);
|
||||
|
||||
if (!ariaHiddenCount) {
|
||||
ariaHiddenCount = elemIsAlreadyHidden ? 1 : 0;
|
||||
}
|
||||
|
||||
sibling.setAttribute(ARIA_HIDDEN_ATTRIBUTE_NAME, ariaHiddenCount + 1);
|
||||
sibling.setAttribute('aria-hidden', 'true');
|
||||
});
|
||||
|
||||
return applyAriaHidden(el.parent());
|
||||
|
||||
function getSiblings(el) {
|
||||
var children = el.parent() ? el.parent().children() : [];
|
||||
|
||||
return Array.prototype.filter.call(children, function(child) {
|
||||
return child !== el[0];
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
function broadcastClosing(modalWindow, resultOrReason, closing) {
|
||||
return !modalWindow.value.modalScope.$broadcast('modal.closing', resultOrReason, closing).defaultPrevented;
|
||||
}
|
||||
|
||||
function unhideBackgroundElements() {
|
||||
Array.prototype.forEach.call(
|
||||
document.querySelectorAll('[' + ARIA_HIDDEN_ATTRIBUTE_NAME + ']'),
|
||||
function(hiddenEl) {
|
||||
var ariaHiddenCount = parseInt(hiddenEl.getAttribute(ARIA_HIDDEN_ATTRIBUTE_NAME), 10),
|
||||
newHiddenCount = ariaHiddenCount - 1;
|
||||
hiddenEl.setAttribute(ARIA_HIDDEN_ATTRIBUTE_NAME, newHiddenCount);
|
||||
|
||||
if (!newHiddenCount) {
|
||||
hiddenEl.removeAttribute(ARIA_HIDDEN_ATTRIBUTE_NAME);
|
||||
hiddenEl.removeAttribute('aria-hidden');
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
$modalStack.close = function(modalInstance, result) {
|
||||
var modalWindow = openedWindows.get(modalInstance);
|
||||
unhideBackgroundElements();
|
||||
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);
|
||||
unhideBackgroundElements();
|
||||
if (modalWindow && broadcastClosing(modalWindow, reason, false)) {
|
||||
modalWindow.value.modalScope.$$uibDestructionScheduled = true;
|
||||
modalWindow.value.deferred.reject(reason);
|
||||
@@ -679,13 +695,22 @@ angular.module('ui.bootstrap.modal', ['ui.bootstrap.stackedMap', 'ui.bootstrap.p
|
||||
modalOptions.resolve = modalOptions.resolve || {};
|
||||
modalOptions.appendTo = modalOptions.appendTo || $document.find('body').eq(0);
|
||||
|
||||
//verify options
|
||||
if (!modalOptions.template && !modalOptions.templateUrl) {
|
||||
throw new Error('One of template or templateUrl options is required.');
|
||||
if (!modalOptions.appendTo.length) {
|
||||
throw new Error('appendTo element not found. Make sure that the element passed is in DOM.');
|
||||
}
|
||||
|
||||
var templateAndResolvePromise =
|
||||
$q.all([getTemplatePromise(modalOptions), $uibResolve.resolve(modalOptions.resolve, {}, null, null)]);
|
||||
//verify options
|
||||
if (!modalOptions.component && !modalOptions.template && !modalOptions.templateUrl) {
|
||||
throw new Error('One of component or template or templateUrl options is required.');
|
||||
}
|
||||
|
||||
var templateAndResolvePromise;
|
||||
if (modalOptions.component) {
|
||||
templateAndResolvePromise = $q.when($uibResolve.resolve(modalOptions.resolve, {}, null, null));
|
||||
} else {
|
||||
templateAndResolvePromise =
|
||||
$q.all([getTemplatePromise(modalOptions), $uibResolve.resolve(modalOptions.resolve, {}, null, null)]);
|
||||
}
|
||||
|
||||
function resolveWithTemplate() {
|
||||
return templateAndResolvePromise;
|
||||
@@ -711,17 +736,34 @@ angular.module('ui.bootstrap.modal', ['ui.bootstrap.stackedMap', 'ui.bootstrap.p
|
||||
}
|
||||
});
|
||||
|
||||
var modal = {
|
||||
scope: modalScope,
|
||||
deferred: modalResultDeferred,
|
||||
renderDeferred: modalRenderDeferred,
|
||||
closedDeferred: modalClosedDeferred,
|
||||
animation: modalOptions.animation,
|
||||
backdrop: modalOptions.backdrop,
|
||||
keyboard: modalOptions.keyboard,
|
||||
backdropClass: modalOptions.backdropClass,
|
||||
windowTopClass: modalOptions.windowTopClass,
|
||||
windowClass: modalOptions.windowClass,
|
||||
windowTemplateUrl: modalOptions.windowTemplateUrl,
|
||||
ariaLabelledBy: modalOptions.ariaLabelledBy,
|
||||
ariaDescribedBy: modalOptions.ariaDescribedBy,
|
||||
size: modalOptions.size,
|
||||
openedClass: modalOptions.openedClass,
|
||||
appendTo: modalOptions.appendTo
|
||||
};
|
||||
|
||||
var component = {};
|
||||
var ctrlInstance, ctrlInstantiate, ctrlLocals = {};
|
||||
|
||||
//controllers
|
||||
if (modalOptions.controller) {
|
||||
ctrlLocals.$scope = modalScope;
|
||||
ctrlLocals.$scope.$resolve = {};
|
||||
ctrlLocals.$uibModalInstance = modalInstance;
|
||||
angular.forEach(tplAndVars[1], function(value, key) {
|
||||
ctrlLocals[key] = value;
|
||||
ctrlLocals.$scope.$resolve[key] = value;
|
||||
});
|
||||
if (modalOptions.component) {
|
||||
constructLocals(component, false, true, false);
|
||||
component.name = modalOptions.component;
|
||||
modal.component = component;
|
||||
} else if (modalOptions.controller) {
|
||||
constructLocals(ctrlLocals, true, false, true);
|
||||
|
||||
// the third param will make the controller instantiate later,private api
|
||||
// @see https://github.com/angular/angular.js/blob/master/src/ng/controller.js#L126
|
||||
@@ -742,25 +784,31 @@ angular.module('ui.bootstrap.modal', ['ui.bootstrap.stackedMap', 'ui.bootstrap.p
|
||||
}
|
||||
}
|
||||
|
||||
$modalStack.open(modalInstance, {
|
||||
scope: modalScope,
|
||||
deferred: modalResultDeferred,
|
||||
renderDeferred: modalRenderDeferred,
|
||||
closedDeferred: modalClosedDeferred,
|
||||
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,
|
||||
appendTo: modalOptions.appendTo
|
||||
});
|
||||
if (!modalOptions.component) {
|
||||
modal.content = tplAndVars[0];
|
||||
}
|
||||
|
||||
$modalStack.open(modalInstance, modal);
|
||||
modalOpenedDeferred.resolve(true);
|
||||
|
||||
function constructLocals(obj, template, instanceOnScope, injectable) {
|
||||
obj.$scope = modalScope;
|
||||
obj.$scope.$resolve = {};
|
||||
if (instanceOnScope) {
|
||||
obj.$scope.$uibModalInstance = modalInstance;
|
||||
} else {
|
||||
obj.$uibModalInstance = modalInstance;
|
||||
}
|
||||
|
||||
var resolves = template ? tplAndVars[1] : tplAndVars;
|
||||
angular.forEach(resolves, function(value, key) {
|
||||
if (injectable) {
|
||||
obj[key] = value;
|
||||
}
|
||||
|
||||
obj.$scope.$resolve[key] = value;
|
||||
});
|
||||
}
|
||||
}, function resolveError(reason) {
|
||||
modalOpenedDeferred.reject(reason);
|
||||
modalResultDeferred.reject(reason);
|
||||
|
||||
+139
-13
@@ -131,6 +131,16 @@ describe('$uibModal', function() {
|
||||
elem.focus();
|
||||
}
|
||||
};
|
||||
}).component('fooBar', {
|
||||
bindings: {
|
||||
resolve: '<',
|
||||
modalInstance: '<',
|
||||
close: '&',
|
||||
dismiss: '&'
|
||||
},
|
||||
controller: angular.noop,
|
||||
controllerAs: 'foobar',
|
||||
template: '<div>Foo Bar</div>'
|
||||
});
|
||||
}));
|
||||
|
||||
@@ -151,6 +161,7 @@ describe('$uibModal', function() {
|
||||
toBeResolvedWith: function(util, customEqualityTesters) {
|
||||
return {
|
||||
compare: function(promise, expected) {
|
||||
var called = false;
|
||||
promise.then(function(result) {
|
||||
expect(result).toEqual(expected);
|
||||
|
||||
@@ -159,10 +170,18 @@ describe('$uibModal', function() {
|
||||
} else {
|
||||
result.message = 'Expected "' + angular.mock.dump(result) + '" to be resolved with "' + expected + '".';
|
||||
}
|
||||
}, function(result) {
|
||||
fail('Expected "' + angular.mock.dump(result) + '" to be resolved with "' + expected + '".');
|
||||
})['finally'](function() {
|
||||
called = true;
|
||||
});
|
||||
|
||||
$rootScope.$digest();
|
||||
|
||||
if (!called) {
|
||||
fail('Expected "' + angular.mock.dump(result) + '" to be resolved with "' + expected + '".');
|
||||
}
|
||||
|
||||
return {pass: true};
|
||||
}
|
||||
};
|
||||
@@ -171,9 +190,10 @@ describe('$uibModal', function() {
|
||||
return {
|
||||
compare: function(promise, expected) {
|
||||
var result = {};
|
||||
var called = false;
|
||||
|
||||
promise.then(function() {
|
||||
|
||||
promise.then(function(result) {
|
||||
fail('Expected "' + angular.mock.dump(result) + '" to be rejected with "' + expected + '".');
|
||||
}, function(result) {
|
||||
expect(result).toEqual(expected);
|
||||
|
||||
@@ -182,10 +202,16 @@ describe('$uibModal', function() {
|
||||
} else {
|
||||
result.message = 'Expected "' + angular.mock.dump(result) + '" to be rejected with "' + expected + '".';
|
||||
}
|
||||
})['finally'](function() {
|
||||
called = true;
|
||||
});
|
||||
|
||||
$rootScope.$digest();
|
||||
|
||||
if (!called) {
|
||||
fail('Expected "' + angular.mock.dump(result) + '" to be rejected with "' + expected + '".');
|
||||
}
|
||||
|
||||
return {pass: true};
|
||||
}
|
||||
};
|
||||
@@ -270,6 +296,8 @@ describe('$uibModal', function() {
|
||||
|
||||
function open(modalOptions, noFlush, noDigest) {
|
||||
var modal = $uibModal.open(modalOptions);
|
||||
modal.opened['catch'](angular.noop);
|
||||
modal.result['catch'](angular.noop);
|
||||
|
||||
if (!noDigest) {
|
||||
$rootScope.$digest();
|
||||
@@ -500,7 +528,7 @@ describe('$uibModal', function() {
|
||||
|
||||
var modal = open({template: '<div>Content<button>inside modal</button></div>'});
|
||||
$rootScope.$digest();
|
||||
expect(document.activeElement.tagName).toBe('DIV');
|
||||
expect(document.activeElement.className.split(' ')).toContain('modal');
|
||||
expect($document).toHaveModalsOpen(1);
|
||||
|
||||
triggerKeyDown($document, 27);
|
||||
@@ -630,7 +658,7 @@ describe('$uibModal', function() {
|
||||
it('should not focus on the element that has autofocus attribute when the modal is opened and something in the modal already has focus and the animations have finished', function() {
|
||||
function openAndCloseModalWithAutofocusElement() {
|
||||
|
||||
var modal = open({template: '<div><input type="text" id="auto-focus-element" autofocus><input type="text" id="pre-focus-element" focus-me></div>'});
|
||||
var modal = open({template: '<div><input type="text" id="pre-focus-element" focus-me><input type="text" id="auto-focus-element" autofocus></div>'});
|
||||
$rootScope.$digest();
|
||||
expect(angular.element('#auto-focus-element')).not.toHaveFocus();
|
||||
expect(angular.element('#pre-focus-element')).toHaveFocus();
|
||||
@@ -672,7 +700,7 @@ describe('$uibModal', function() {
|
||||
$rootScope.$digest();
|
||||
$animate.flush();
|
||||
|
||||
expect(document.activeElement.tagName).toBe('DIV');
|
||||
expect(document.activeElement.className.split(' ')).toContain('modal');
|
||||
|
||||
close(modal, 'closed ok');
|
||||
|
||||
@@ -930,16 +958,89 @@ describe('$uibModal', function() {
|
||||
});
|
||||
});
|
||||
|
||||
describe('option by option', function () {
|
||||
describe('template and templateUrl', function () {
|
||||
it('should throw an error if none of template and templateUrl are provided', function() {
|
||||
describe('option by option', function() {
|
||||
describe('component', function() {
|
||||
function getModalComponent($document) {
|
||||
return $document.find('body > div.modal > div.modal-dialog > div.modal-content foo-bar');
|
||||
}
|
||||
|
||||
it('should use as modal content', function() {
|
||||
open({
|
||||
component: 'fooBar'
|
||||
});
|
||||
|
||||
var component = getModalComponent($document);
|
||||
expect(component.html()).toBe('<div>Foo Bar</div>');
|
||||
});
|
||||
|
||||
it('should bind expected values', function() {
|
||||
var modal = open({
|
||||
component: 'fooBar',
|
||||
resolve: {
|
||||
foo: function() {
|
||||
return 'bar';
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
var component = getModalComponent($document);
|
||||
var componentScope = component.isolateScope();
|
||||
|
||||
expect(componentScope.foobar.resolve.foo).toBe('bar');
|
||||
expect(componentScope.foobar.modalInstance).toBe(modal);
|
||||
expect(componentScope.foobar.close).toEqual(jasmine.any(Function));
|
||||
expect(componentScope.foobar.dismiss).toEqual(jasmine.any(Function));
|
||||
});
|
||||
|
||||
it('should close the modal', function() {
|
||||
var modal = open({
|
||||
component: 'fooBar',
|
||||
resolve: {
|
||||
foo: function() {
|
||||
return 'bar';
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
var component = getModalComponent($document);
|
||||
var componentScope = component.isolateScope();
|
||||
|
||||
componentScope.foobar.close({
|
||||
$value: 'baz'
|
||||
});
|
||||
|
||||
expect(modal.result).toBeResolvedWith('baz');
|
||||
});
|
||||
|
||||
it('should dismiss the modal', function() {
|
||||
var modal = open({
|
||||
component: 'fooBar',
|
||||
resolve: {
|
||||
foo: function() {
|
||||
return 'bar';
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
var component = getModalComponent($document);
|
||||
var componentScope = component.isolateScope();
|
||||
|
||||
componentScope.foobar.dismiss({
|
||||
$value: 'baz'
|
||||
});
|
||||
|
||||
expect(modal.result).toBeRejectedWith('baz');
|
||||
});
|
||||
});
|
||||
|
||||
describe('template and templateUrl', function() {
|
||||
it('should throw an error if none of component, template and templateUrl are provided', function() {
|
||||
expect(function(){
|
||||
var modal = open({});
|
||||
}).toThrow(new Error('One of template or templateUrl options is required.'));
|
||||
}).toThrow(new Error('One of component or template or templateUrl options is required.'));
|
||||
});
|
||||
|
||||
it('should not fail if a templateUrl contains leading / trailing white spaces', function() {
|
||||
|
||||
$templateCache.put('whitespace.html', ' <div>Whitespaces</div> ');
|
||||
open({templateUrl: 'whitespace.html'});
|
||||
expect($document).toHaveModalOpenWithContent('Whitespaces', 'div');
|
||||
@@ -1465,6 +1566,28 @@ describe('$uibModal', function() {
|
||||
expect(body).not.toHaveClass('modal-open');
|
||||
});
|
||||
});
|
||||
|
||||
describe('ariaLabelledBy', function() {
|
||||
it('should add the aria-labelledby property to the modal', function() {
|
||||
open({
|
||||
template: '<div><h3 id="modal-label">Modal Label</h3><p id="modal-description">Modal description</p></div>',
|
||||
ariaLabelledBy: 'modal-label'
|
||||
});
|
||||
|
||||
expect($document.find('.modal').attr('aria-labelledby')).toEqual('modal-label');
|
||||
});
|
||||
});
|
||||
|
||||
describe('ariaDescribedBy', function() {
|
||||
it('should add the aria-describedby property to the modal', function() {
|
||||
open({
|
||||
template: '<div><h3 id="modal-label">Modal Label</h3><p id="modal-description">Modal description</p></div>',
|
||||
ariaDescribedBy: 'modal-description'
|
||||
});
|
||||
|
||||
expect($document.find('.modal').attr('aria-describedby')).toEqual('modal-description');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('modal window', function() {
|
||||
@@ -1491,7 +1614,7 @@ describe('$uibModal', function() {
|
||||
var windowEl = $compile('<div uib-modal-window template-url="window.html">content</div>')($rootScope);
|
||||
$rootScope.$digest();
|
||||
|
||||
expect(windowEl.html()).toBe('<div ng-transclude=""><span class="ng-scope">content</span></div>');
|
||||
expect(windowEl.html()).toBe('<div ng-transclude="">content</div>');
|
||||
}));
|
||||
});
|
||||
|
||||
@@ -1615,16 +1738,19 @@ describe('$uibModal', function() {
|
||||
ds[x] = {index: i, deferred: $q.defer(), reject: reject};
|
||||
|
||||
var scope = $rootScope.$new();
|
||||
var failed = false;
|
||||
scope.index = i;
|
||||
open({
|
||||
template: '<div>' + i + '</div>',
|
||||
scope: scope,
|
||||
resolve: {
|
||||
x: function() { return ds[x].deferred.promise; }
|
||||
x: function() { return ds[x].deferred.promise['catch'](function () {
|
||||
failed = true;
|
||||
}); }
|
||||
}
|
||||
}, true).opened.then(function() {
|
||||
expect($uibModalStack.getTop().value.modalScope.index).toEqual(i);
|
||||
actual += i;
|
||||
if (!failed) { actual += i; }
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
require('./multiMap.js');
|
||||
@@ -0,0 +1,55 @@
|
||||
angular.module('ui.bootstrap.multiMap', [])
|
||||
/**
|
||||
* 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];
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
});
|
||||
@@ -1,7 +1,7 @@
|
||||
describe('multi map', function() {
|
||||
var multiMap;
|
||||
|
||||
beforeEach(module('ui.bootstrap.modal'));
|
||||
beforeEach(module('ui.bootstrap.multiMap'));
|
||||
beforeEach(inject(function($$multiMap) {
|
||||
multiMap = $$multiMap.createNew();
|
||||
}));
|
||||
@@ -9,6 +9,7 @@ angular.module('ui.bootstrap.pagination', ['ui.bootstrap.paging', 'ui.bootstrap.
|
||||
pageLabel = angular.isDefined($attrs.pageLabel) ? function(idx) { return $scope.$parent.$eval($attrs.pageLabel, {$page: idx}); } : angular.identity;
|
||||
$scope.boundaryLinks = angular.isDefined($attrs.boundaryLinks) ? $scope.$parent.$eval($attrs.boundaryLinks) : uibPaginationConfig.boundaryLinks;
|
||||
$scope.directionLinks = angular.isDefined($attrs.directionLinks) ? $scope.$parent.$eval($attrs.directionLinks) : uibPaginationConfig.directionLinks;
|
||||
$attrs.$set('role', 'menu');
|
||||
|
||||
uibPaging.create(this, $scope, $attrs);
|
||||
|
||||
|
||||
@@ -54,6 +54,15 @@ describe('pagination directive', function() {
|
||||
expect(element.hasClass('pagination')).toBe(true);
|
||||
});
|
||||
|
||||
it('has accessibility attributes', function() {
|
||||
expect(element.attr('role')).toEqual('menu');
|
||||
|
||||
var li = element.find('li');
|
||||
for (var i = 0; i < li.length; i++) {
|
||||
expect(li.eq(i).attr('role')).toEqual('menuitem');
|
||||
}
|
||||
});
|
||||
|
||||
it('exposes the controller to the template', function() {
|
||||
$templateCache.put('uib/template/pagination/pagination.html', '<div>{{pagination.randomText}}</div>');
|
||||
var scope = $rootScope.$new();
|
||||
|
||||
@@ -34,6 +34,9 @@ All these settings are available for the three types of popovers.
|
||||
_(Default: `false`, Config: `appendToBody`)_ -
|
||||
Should the popover be appended to '$body' instead of the parent element?
|
||||
|
||||
* `popover-class` -
|
||||
Custom class to be applied to the popover.
|
||||
|
||||
* `popover-enable`
|
||||
<small class="badge">$</small>
|
||||
_(Default: `true`)_ -
|
||||
|
||||
@@ -130,7 +130,7 @@ angular.module('ui.bootstrap.position', [])
|
||||
var paddingRight = this.parseStyle(elemStyle.paddingRight);
|
||||
var paddingBottom = this.parseStyle(elemStyle.paddingBottom);
|
||||
var scrollParent = this.scrollParent(elem, false, true);
|
||||
var scrollbarWidth = this.scrollbarWidth(scrollParent, BODY_REGEX.test(scrollParent.tagName));
|
||||
var scrollbarWidth = this.scrollbarWidth(BODY_REGEX.test(scrollParent.tagName));
|
||||
|
||||
return {
|
||||
scrollbarWidth: scrollbarWidth,
|
||||
|
||||
@@ -1230,6 +1230,7 @@ describe('timepicker directive', function() {
|
||||
changeInputValueTo(el, 'pizza');
|
||||
expect($rootScope.time).toBe(null);
|
||||
expect(el.parent().hasClass('has-error')).toBe(true);
|
||||
expect(el.hasClass('ng-invalid-hours'));
|
||||
expect(element.hasClass('ng-invalid-time')).toBe(true);
|
||||
|
||||
changeInputValueTo(el, 8);
|
||||
@@ -1247,6 +1248,7 @@ describe('timepicker directive', function() {
|
||||
changeInputValueTo(el, '8a');
|
||||
expect($rootScope.time).toBe(null);
|
||||
expect(el.parent().hasClass('has-error')).toBe(true);
|
||||
expect(el.hasClass('ng-invalid-minutes'));
|
||||
expect(element.hasClass('ng-invalid-time')).toBe(true);
|
||||
|
||||
changeInputValueTo(el, 22);
|
||||
@@ -1262,6 +1264,7 @@ describe('timepicker directive', function() {
|
||||
changeInputValueTo(el, 'pizza');
|
||||
expect($rootScope.time).toBe(null);
|
||||
expect(el.parent().hasClass('has-error')).toBe(true);
|
||||
expect(el.hasClass('ng-invalid-seconds'));
|
||||
expect(element.hasClass('ng-invalid-time')).toBe(true);
|
||||
|
||||
changeInputValueTo(el, 13);
|
||||
@@ -1291,6 +1294,9 @@ describe('timepicker directive', function() {
|
||||
elS.blur();
|
||||
$rootScope.$digest();
|
||||
|
||||
expect(elH.hasClass('ng-valid'));
|
||||
expect(elM.hasClass('ng-valid'));
|
||||
expect(elS.hasClass('ng-valid'));
|
||||
expect(element.hasClass('ng-invalid-time')).toBe(false);
|
||||
});
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@ angular.module('ui.bootstrap.timepicker', [])
|
||||
})
|
||||
|
||||
.controller('UibTimepickerController', ['$scope', '$element', '$attrs', '$parse', '$log', '$locale', 'uibTimepickerConfig', function($scope, $element, $attrs, $parse, $log, $locale, timepickerConfig) {
|
||||
var hoursModelCtrl, minutesModelCtrl, secondsModelCtrl;
|
||||
var selected = new Date(),
|
||||
watchers = [],
|
||||
ngModelCtrl = { $setViewValue: angular.noop }, // nullModelCtrl
|
||||
@@ -36,6 +37,10 @@ angular.module('ui.bootstrap.timepicker', [])
|
||||
minutesInputEl = inputs.eq(1),
|
||||
secondsInputEl = inputs.eq(2);
|
||||
|
||||
hoursModelCtrl = hoursInputEl.controller('ngModel');
|
||||
minutesModelCtrl = minutesInputEl.controller('ngModel');
|
||||
secondsModelCtrl = secondsInputEl.controller('ngModel');
|
||||
|
||||
var mousewheel = angular.isDefined($attrs.mousewheel) ? $scope.$parent.$eval($attrs.mousewheel) : timepickerConfig.mousewheel;
|
||||
|
||||
if (mousewheel) {
|
||||
@@ -215,21 +220,21 @@ angular.module('ui.bootstrap.timepicker', [])
|
||||
return e.detail || delta > 0;
|
||||
};
|
||||
|
||||
hoursInputEl.bind('mousewheel wheel', function(e) {
|
||||
hoursInputEl.on('mousewheel wheel', function(e) {
|
||||
if (!disabled) {
|
||||
$scope.$apply(isScrollingUp(e) ? $scope.incrementHours() : $scope.decrementHours());
|
||||
}
|
||||
e.preventDefault();
|
||||
});
|
||||
|
||||
minutesInputEl.bind('mousewheel wheel', function(e) {
|
||||
minutesInputEl.on('mousewheel wheel', function(e) {
|
||||
if (!disabled) {
|
||||
$scope.$apply(isScrollingUp(e) ? $scope.incrementMinutes() : $scope.decrementMinutes());
|
||||
}
|
||||
e.preventDefault();
|
||||
});
|
||||
|
||||
secondsInputEl.bind('mousewheel wheel', function(e) {
|
||||
secondsInputEl.on('mousewheel wheel', function(e) {
|
||||
if (!disabled) {
|
||||
$scope.$apply(isScrollingUp(e) ? $scope.incrementSeconds() : $scope.decrementSeconds());
|
||||
}
|
||||
@@ -239,7 +244,7 @@ angular.module('ui.bootstrap.timepicker', [])
|
||||
|
||||
// Respond on up/down arrowkeys
|
||||
this.setupArrowkeyEvents = function(hoursInputEl, minutesInputEl, secondsInputEl) {
|
||||
hoursInputEl.bind('keydown', function(e) {
|
||||
hoursInputEl.on('keydown', function(e) {
|
||||
if (!disabled) {
|
||||
if (e.which === 38) { // up
|
||||
e.preventDefault();
|
||||
@@ -253,7 +258,7 @@ angular.module('ui.bootstrap.timepicker', [])
|
||||
}
|
||||
});
|
||||
|
||||
minutesInputEl.bind('keydown', function(e) {
|
||||
minutesInputEl.on('keydown', function(e) {
|
||||
if (!disabled) {
|
||||
if (e.which === 38) { // up
|
||||
e.preventDefault();
|
||||
@@ -267,7 +272,7 @@ angular.module('ui.bootstrap.timepicker', [])
|
||||
}
|
||||
});
|
||||
|
||||
secondsInputEl.bind('keydown', function(e) {
|
||||
secondsInputEl.on('keydown', function(e) {
|
||||
if (!disabled) {
|
||||
if (e.which === 38) { // up
|
||||
e.preventDefault();
|
||||
@@ -295,14 +300,23 @@ angular.module('ui.bootstrap.timepicker', [])
|
||||
ngModelCtrl.$setValidity('time', false);
|
||||
if (angular.isDefined(invalidHours)) {
|
||||
$scope.invalidHours = invalidHours;
|
||||
if (hoursModelCtrl) {
|
||||
hoursModelCtrl.$setValidity('hours', false);
|
||||
}
|
||||
}
|
||||
|
||||
if (angular.isDefined(invalidMinutes)) {
|
||||
$scope.invalidMinutes = invalidMinutes;
|
||||
if (minutesModelCtrl) {
|
||||
minutesModelCtrl.$setValidity('minutes', false);
|
||||
}
|
||||
}
|
||||
|
||||
if (angular.isDefined(invalidSeconds)) {
|
||||
$scope.invalidSeconds = invalidSeconds;
|
||||
if (secondsModelCtrl) {
|
||||
secondsModelCtrl.$setValidity('seconds', false);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -325,7 +339,7 @@ angular.module('ui.bootstrap.timepicker', [])
|
||||
}
|
||||
};
|
||||
|
||||
hoursInputEl.bind('blur', function(e) {
|
||||
hoursInputEl.on('blur', function(e) {
|
||||
ngModelCtrl.$setTouched();
|
||||
if (modelIsEmpty()) {
|
||||
makeValid();
|
||||
@@ -357,7 +371,7 @@ angular.module('ui.bootstrap.timepicker', [])
|
||||
}
|
||||
};
|
||||
|
||||
minutesInputEl.bind('blur', function(e) {
|
||||
minutesInputEl.on('blur', function(e) {
|
||||
ngModelCtrl.$setTouched();
|
||||
if (modelIsEmpty()) {
|
||||
makeValid();
|
||||
@@ -383,7 +397,7 @@ angular.module('ui.bootstrap.timepicker', [])
|
||||
}
|
||||
};
|
||||
|
||||
secondsInputEl.bind('blur', function(e) {
|
||||
secondsInputEl.on('blur', function(e) {
|
||||
if (modelIsEmpty()) {
|
||||
makeValid();
|
||||
} else if (!$scope.invalidSeconds && $scope.seconds < 10) {
|
||||
@@ -425,6 +439,18 @@ angular.module('ui.bootstrap.timepicker', [])
|
||||
}
|
||||
|
||||
function makeValid() {
|
||||
if (hoursModelCtrl) {
|
||||
hoursModelCtrl.$setValidity('hours', true);
|
||||
}
|
||||
|
||||
if (minutesModelCtrl) {
|
||||
minutesModelCtrl.$setValidity('minutes', true);
|
||||
}
|
||||
|
||||
if (secondsModelCtrl) {
|
||||
secondsModelCtrl.$setValidity('seconds', true);
|
||||
}
|
||||
|
||||
ngModelCtrl.$setValidity('time', true);
|
||||
$scope.invalidHours = false;
|
||||
$scope.invalidMinutes = false;
|
||||
|
||||
@@ -118,3 +118,5 @@ For Safari 7+ support, if you want to use the **focus** `tooltip-trigger`, you n
|
||||
Click Me
|
||||
</a>
|
||||
```
|
||||
|
||||
For Safari (potentially all versions up to 9), there is an issue with the hover CSS selector when using multiple elements grouped close to each other that are using the tooltip - it is possible for multiple elements to gain the hover state when mousing between the elements quickly and exiting the container at the right time. See [issue #5445](https://github.com/angular-ui/bootstrap/issues/5445) for more details.
|
||||
|
||||
@@ -27,7 +27,7 @@ describe('tooltip', function() {
|
||||
}));
|
||||
|
||||
afterEach(function() {
|
||||
$document.off('keypress');
|
||||
$document.off('keyup');
|
||||
});
|
||||
|
||||
function trigger(element, evt) {
|
||||
@@ -295,10 +295,9 @@ describe('tooltip', function() {
|
||||
trigger(elm, 'mouseenter');
|
||||
expect(tooltipScope.isOpen).toBe(false);
|
||||
|
||||
$timeout.flush(500);
|
||||
expect(tooltipScope.isOpen).toBe(false);
|
||||
elmScope.disabled = true;
|
||||
elmScope.$digest();
|
||||
$timeout.flush(500);
|
||||
|
||||
expect(tooltipScope.isOpen).toBe(false);
|
||||
});
|
||||
@@ -343,22 +342,24 @@ describe('tooltip', function() {
|
||||
expect(tooltipScope.isOpen).toBe(true);
|
||||
expect(tooltipScope2.isOpen).toBe(true);
|
||||
|
||||
var evt = $.Event('keypress');
|
||||
var evt = $.Event('keyup');
|
||||
evt.which = 27;
|
||||
|
||||
$document.trigger(evt);
|
||||
tooltipScope.$digest();
|
||||
tooltipScope2.$digest();
|
||||
$timeout.flush();
|
||||
|
||||
expect(tooltipScope.isOpen).toBe(true);
|
||||
expect(tooltipScope2.isOpen).toBe(false);
|
||||
|
||||
var evt2 = $.Event('keypress');
|
||||
var evt2 = $.Event('keyup');
|
||||
evt2.which = 27;
|
||||
|
||||
$document.trigger(evt2);
|
||||
tooltipScope.$digest();
|
||||
tooltipScope2.$digest();
|
||||
$timeout.flush(500);
|
||||
|
||||
expect(tooltipScope.isOpen).toBe(false);
|
||||
expect(tooltipScope2.isOpen).toBe(false);
|
||||
|
||||
+25
-14
@@ -71,10 +71,10 @@ angular.module('ui.bootstrap.tooltip', ['ui.bootstrap.position', 'ui.bootstrap.s
|
||||
*/
|
||||
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', keypressListener);
|
||||
$document.on('keyup', keypressListener);
|
||||
|
||||
$rootScope.$on('$destroy', function() {
|
||||
$document.off('keypress', keypressListener);
|
||||
$document.off('keyup', keypressListener);
|
||||
});
|
||||
|
||||
function keypressListener(e) {
|
||||
@@ -82,7 +82,6 @@ angular.module('ui.bootstrap.tooltip', ['ui.bootstrap.position', 'ui.bootstrap.s
|
||||
var last = openedTooltips.top();
|
||||
if (last) {
|
||||
last.value.close();
|
||||
openedTooltips.removeTop();
|
||||
last = null;
|
||||
}
|
||||
}
|
||||
@@ -145,6 +144,7 @@ angular.module('ui.bootstrap.tooltip', ['ui.bootstrap.position', 'ui.bootstrap.s
|
||||
var showTimeout;
|
||||
var hideTimeout;
|
||||
var positionTimeout;
|
||||
var adjustmentTimeout;
|
||||
var appendToBody = angular.isDefined(options.appendToBody) ? options.appendToBody : false;
|
||||
var triggers = getTriggers(undefined);
|
||||
var hasEnableExp = angular.isDefined(attrs[prefix + 'Enable']);
|
||||
@@ -177,12 +177,13 @@ angular.module('ui.bootstrap.tooltip', ['ui.bootstrap.position', 'ui.bootstrap.s
|
||||
tooltip.addClass(options.placementClassPrefix + ttPosition.placement);
|
||||
}
|
||||
|
||||
$timeout(function() {
|
||||
adjustmentTimeout = $timeout(function() {
|
||||
var currentHeight = angular.isDefined(tooltip.offsetHeight) ? tooltip.offsetHeight : tooltip.prop('offsetHeight');
|
||||
var adjustment = $position.adjustTop(placementClasses, elementPos, initialHeight, currentHeight);
|
||||
if (adjustment) {
|
||||
tooltip.css(adjustment);
|
||||
}
|
||||
adjustmentTimeout = null;
|
||||
}, 0, false);
|
||||
|
||||
// first time through tt element will have the
|
||||
@@ -207,9 +208,6 @@ angular.module('ui.bootstrap.tooltip', ['ui.bootstrap.position', 'ui.bootstrap.s
|
||||
// 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) {
|
||||
@@ -336,6 +334,10 @@ angular.module('ui.bootstrap.tooltip', ['ui.bootstrap.position', 'ui.bootstrap.s
|
||||
}
|
||||
});
|
||||
|
||||
openedTooltips.add(ttScope, {
|
||||
close: hide
|
||||
});
|
||||
|
||||
prepObservers();
|
||||
}
|
||||
|
||||
@@ -346,8 +348,15 @@ angular.module('ui.bootstrap.tooltip', ['ui.bootstrap.position', 'ui.bootstrap.s
|
||||
|
||||
if (tooltip) {
|
||||
tooltip.remove();
|
||||
|
||||
tooltip = null;
|
||||
if (adjustmentTimeout) {
|
||||
$timeout.cancel(adjustmentTimeout);
|
||||
}
|
||||
}
|
||||
|
||||
openedTooltips.remove(ttScope);
|
||||
|
||||
if (tooltipLinkedScope) {
|
||||
tooltipLinkedScope.$destroy();
|
||||
tooltipLinkedScope = null;
|
||||
@@ -487,6 +496,13 @@ angular.module('ui.bootstrap.tooltip', ['ui.bootstrap.position', 'ui.bootstrap.s
|
||||
}
|
||||
}
|
||||
|
||||
// KeyboardEvent handler to hide the tooltip on Escape key press
|
||||
function hideOnEscapeKey(e) {
|
||||
if (e.which === 27) {
|
||||
hideTooltipBind();
|
||||
}
|
||||
}
|
||||
|
||||
var unregisterTriggers = function() {
|
||||
triggers.show.forEach(function(trigger) {
|
||||
if (trigger === 'outsideClick') {
|
||||
@@ -495,6 +511,7 @@ angular.module('ui.bootstrap.tooltip', ['ui.bootstrap.position', 'ui.bootstrap.s
|
||||
element.off(trigger, showTooltipBind);
|
||||
element.off(trigger, toggleTooltipBind);
|
||||
}
|
||||
element.off('keypress', hideOnEscapeKey);
|
||||
});
|
||||
triggers.hide.forEach(function(trigger) {
|
||||
if (trigger === 'outsideClick') {
|
||||
@@ -534,12 +551,7 @@ angular.module('ui.bootstrap.tooltip', ['ui.bootstrap.position', 'ui.bootstrap.s
|
||||
element.on(trigger, showTooltipBind);
|
||||
element.on(triggers.hide[idx], hideTooltipBind);
|
||||
}
|
||||
|
||||
element.on('keypress', function(e) {
|
||||
if (e.which === 27) {
|
||||
hideTooltipBind();
|
||||
}
|
||||
});
|
||||
element.on('keypress', hideOnEscapeKey);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -563,7 +575,6 @@ angular.module('ui.bootstrap.tooltip', ['ui.bootstrap.position', 'ui.bootstrap.s
|
||||
scope.$on('$destroy', function onDestroyTooltip() {
|
||||
unregisterTriggers();
|
||||
removeTooltip();
|
||||
openedTooltips.remove(ttScope);
|
||||
ttScope = null;
|
||||
});
|
||||
};
|
||||
|
||||
@@ -94,7 +94,7 @@ angular.module('ui.bootstrap.typeahead', ['ui.bootstrap.debounce', 'ui.bootstrap
|
||||
var invokeModelSetter = $parse(attrs.ngModel + '($$$p)');
|
||||
var $setModelValue = function(scope, newValue) {
|
||||
if (angular.isFunction(parsedModel(originalScope)) &&
|
||||
ngModelOptions && ngModelOptions.$options && ngModelOptions.$options.getterSetter) {
|
||||
ngModelOptions.getOption('getterSetter')) {
|
||||
return invokeModelSetter(scope, {$$$p: newValue});
|
||||
}
|
||||
|
||||
@@ -430,7 +430,7 @@ angular.module('ui.bootstrap.typeahead', ['ui.bootstrap.debounce', 'ui.bootstrap
|
||||
}
|
||||
});
|
||||
|
||||
element.bind('focus', function (evt) {
|
||||
element.on('focus', function (evt) {
|
||||
hasFocus = true;
|
||||
if (minLength === 0 && !modelCtrl.$viewValue) {
|
||||
$timeout(function() {
|
||||
@@ -439,7 +439,7 @@ angular.module('ui.bootstrap.typeahead', ['ui.bootstrap.debounce', 'ui.bootstrap
|
||||
}
|
||||
});
|
||||
|
||||
element.bind('blur', function(evt) {
|
||||
element.on('blur', function(evt) {
|
||||
if (isSelectOnBlur && scope.matches.length && scope.activeIdx !== -1 && !selected) {
|
||||
selected = true;
|
||||
scope.$apply(function() {
|
||||
@@ -507,11 +507,11 @@ angular.module('ui.bootstrap.typeahead', ['ui.bootstrap.debounce', 'ui.bootstrap
|
||||
element.after($popup);
|
||||
}
|
||||
|
||||
this.init = function(_modelCtrl, _ngModelOptions) {
|
||||
this.init = function(_modelCtrl) {
|
||||
modelCtrl = _modelCtrl;
|
||||
ngModelOptions = _ngModelOptions;
|
||||
ngModelOptions = extractOptions(modelCtrl);
|
||||
|
||||
scope.debounceUpdate = modelCtrl.$options && $parse(modelCtrl.$options.debounce)(originalScope);
|
||||
scope.debounceUpdate = $parse(ngModelOptions.getOption('debounce'))(originalScope);
|
||||
|
||||
//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
|
||||
@@ -571,14 +571,32 @@ angular.module('ui.bootstrap.typeahead', ['ui.bootstrap.debounce', 'ui.bootstrap
|
||||
return candidateViewValue !== emptyViewValue ? candidateViewValue : modelValue;
|
||||
});
|
||||
};
|
||||
|
||||
function extractOptions(ngModelCtrl) {
|
||||
var ngModelOptions;
|
||||
|
||||
if (angular.version.minor < 6) { // in angular < 1.6 $options could be missing
|
||||
// guarantee a value
|
||||
ngModelOptions = ngModelCtrl.$options || {};
|
||||
|
||||
// mimic 1.6+ api
|
||||
ngModelOptions.getOption = function (key) {
|
||||
return ngModelOptions[key];
|
||||
};
|
||||
} else { // in angular >=1.6 $options is always present
|
||||
ngModelOptions = ngModelCtrl.$options;
|
||||
}
|
||||
|
||||
return ngModelOptions;
|
||||
}
|
||||
}])
|
||||
|
||||
.directive('uibTypeahead', function() {
|
||||
return {
|
||||
controller: 'UibTypeaheadController',
|
||||
require: ['ngModel', '^?ngModelOptions', 'uibTypeahead'],
|
||||
require: ['ngModel', 'uibTypeahead'],
|
||||
link: function(originalScope, element, attrs, ctrls) {
|
||||
ctrls[2].init(ctrls[0], ctrls[1]);
|
||||
ctrls[1].init(ctrls[0]);
|
||||
}
|
||||
};
|
||||
})
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
<table role="grid" aria-labelledby="{{::uniqueId}}-title" aria-activedescendant="{{activeDateId}}">
|
||||
<thead>
|
||||
<tr>
|
||||
<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>
|
||||
<th><button type="button" class="btn btn-default btn-sm pull-left uib-left" ng-click="move(-1)" tabindex="-1"><i aria-hidden="true" class="glyphicon glyphicon-chevron-left"></i><span class="sr-only">previous</span></button></th>
|
||||
<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>
|
||||
<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>
|
||||
<th><button type="button" class="btn btn-default btn-sm pull-right uib-right" ng-click="move(1)" tabindex="-1"><i aria-hidden="true" class="glyphicon glyphicon-chevron-right"></i><span class="sr-only">next</span></button></th>
|
||||
</tr>
|
||||
<tr>
|
||||
<th ng-if="showWeeks" class="text-center"></th>
|
||||
@@ -11,7 +11,7 @@
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr class="uib-weeks" ng-repeat="row in rows track by $index">
|
||||
<tr class="uib-weeks" ng-repeat="row in rows track by $index" role="row">
|
||||
<td ng-if="showWeeks" class="text-center h6"><em>{{ weekNumbers[$index] }}</em></td>
|
||||
<td ng-repeat="dt in row" class="uib-day text-center" role="gridcell"
|
||||
id="{{::dt.uid}}"
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
<table role="grid" aria-labelledby="{{::uniqueId}}-title" aria-activedescendant="{{activeDateId}}">
|
||||
<thead>
|
||||
<tr>
|
||||
<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>
|
||||
<th><button type="button" class="btn btn-default btn-sm pull-left uib-left" ng-click="move(-1)" tabindex="-1"><i aria-hidden="true" class="glyphicon glyphicon-chevron-left"></i><span class="sr-only">previous</span></button></th>
|
||||
<th colspan="{{::yearHeaderColspan}}"><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>
|
||||
<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>
|
||||
<th><button type="button" class="btn btn-default btn-sm pull-right uib-right" ng-click="move(1)" tabindex="-1"><i aria-hidden="true" class="glyphicon glyphicon-chevron-right"></i><span class="sr-only">next</span></i></button></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr class="uib-months" ng-repeat="row in rows track by $index">
|
||||
<tr class="uib-months" ng-repeat="row in rows track by $index" role="row">
|
||||
<td ng-repeat="dt in row" class="uib-month text-center" role="gridcell"
|
||||
id="{{::dt.uid}}"
|
||||
ng-class="::dt.customClass">
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
<table role="grid" aria-labelledby="{{::uniqueId}}-title" aria-activedescendant="{{activeDateId}}">
|
||||
<thead>
|
||||
<tr>
|
||||
<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>
|
||||
<th><button type="button" class="btn btn-default btn-sm pull-left uib-left" ng-click="move(-1)" tabindex="-1"><i aria-hidden="true" class="glyphicon glyphicon-chevron-left"></i><span class="sr-only">previous</span></button></th>
|
||||
<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>
|
||||
<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>
|
||||
<th><button type="button" class="btn btn-default btn-sm pull-right uib-right" ng-click="move(1)" tabindex="-1"><i aria-hidden="true" class="glyphicon glyphicon-chevron-right"></i><span class="sr-only">next</span></button></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr class="uib-years" ng-repeat="row in rows track by $index">
|
||||
<tr class="uib-years" ng-repeat="row in rows track by $index" role="row">
|
||||
<td ng-repeat="dt in row" class="uib-year text-center" role="gridcell"
|
||||
id="{{::dt.uid}}"
|
||||
ng-class="::dt.customClass">
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<ul class="uib-datepicker-popup dropdown-menu uib-position-measure" dropdown-nested ng-if="isOpen" ng-keydown="keydown($event)" ng-click="$event.stopPropagation()">
|
||||
<ul role="presentation" class="uib-datepicker-popup dropdown-menu uib-position-measure" dropdown-nested ng-if="isOpen" ng-keydown="keydown($event)" ng-click="$event.stopPropagation()">
|
||||
<li ng-transclude></li>
|
||||
<li ng-if="showButtonBar" class="uib-button-bar">
|
||||
<span class="btn-group pull-left">
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<li ng-if="::boundaryLinks" ng-class="{disabled: noPrevious()||ngDisabled}" class="pagination-first"><a href ng-click="selectPage(1, $event)" ng-disabled="noPrevious()||ngDisabled" uib-tabindex-toggle>{{::getText('first')}}</a></li>
|
||||
<li ng-if="::directionLinks" ng-class="{disabled: noPrevious()||ngDisabled}" class="pagination-prev"><a href ng-click="selectPage(page - 1, $event)" ng-disabled="noPrevious()||ngDisabled" uib-tabindex-toggle>{{::getText('previous')}}</a></li>
|
||||
<li ng-repeat="page in pages track by $index" ng-class="{active: page.active,disabled: ngDisabled&&!page.active}" class="pagination-page"><a href ng-click="selectPage(page.number, $event)" ng-disabled="ngDisabled&&!page.active" uib-tabindex-toggle>{{page.text}}</a></li>
|
||||
<li ng-if="::directionLinks" ng-class="{disabled: noNext()||ngDisabled}" class="pagination-next"><a href ng-click="selectPage(page + 1, $event)" ng-disabled="noNext()||ngDisabled" uib-tabindex-toggle>{{::getText('next')}}</a></li>
|
||||
<li ng-if="::boundaryLinks" ng-class="{disabled: noNext()||ngDisabled}" class="pagination-last"><a href ng-click="selectPage(totalPages, $event)" ng-disabled="noNext()||ngDisabled" uib-tabindex-toggle>{{::getText('last')}}</a></li>
|
||||
<li role="menuitem" ng-if="::boundaryLinks" ng-class="{disabled: noPrevious()||ngDisabled}" class="pagination-first"><a href ng-click="selectPage(1, $event)" ng-disabled="noPrevious()||ngDisabled" uib-tabindex-toggle>{{::getText('first')}}</a></li>
|
||||
<li role="menuitem" ng-if="::directionLinks" ng-class="{disabled: noPrevious()||ngDisabled}" class="pagination-prev"><a href ng-click="selectPage(page - 1, $event)" ng-disabled="noPrevious()||ngDisabled" uib-tabindex-toggle>{{::getText('previous')}}</a></li>
|
||||
<li role="menuitem" ng-repeat="page in pages track by $index" ng-class="{active: page.active,disabled: ngDisabled&&!page.active}" class="pagination-page"><a href ng-click="selectPage(page.number, $event)" ng-disabled="ngDisabled&&!page.active" uib-tabindex-toggle>{{page.text}}</a></li>
|
||||
<li role="menuitem" ng-if="::directionLinks" ng-class="{disabled: noNext()||ngDisabled}" class="pagination-next"><a href ng-click="selectPage(page + 1, $event)" ng-disabled="noNext()||ngDisabled" uib-tabindex-toggle>{{::getText('next')}}</a></li>
|
||||
<li role="menuitem" ng-if="::boundaryLinks" ng-class="{disabled: noNext()||ngDisabled}" class="pagination-last"><a href ng-click="selectPage(totalPages, $event)" ng-disabled="noNext()||ngDisabled" uib-tabindex-toggle>{{::getText('last')}}</a></li>
|
||||
|
||||
Reference in New Issue
Block a user