Compare commits
152 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 | |||
| 86f528730b | |||
| fbd0845c95 | |||
| 5597e2fa99 | |||
| 6bad7592b9 | |||
| 0023d1bd97 | |||
| be0d0564c2 | |||
| 9f3e387473 | |||
| 58e0433b96 | |||
| 87a373936d | |||
| e8e4ef1070 | |||
| c61d16a933 | |||
| 6bfb4cdbdf | |||
| cac6a45345 | |||
| 8a53327bd6 | |||
| 9f06a8bfdb | |||
| 5173779f84 | |||
| 1616e9720d | |||
| b77f02cdce | |||
| 39d9b9886d | |||
| 2d831bcc2a | |||
| d846e2d788 | |||
| 409b7aa3b3 | |||
| ed3400bb68 | |||
| 1cbd73d227 | |||
| 433e536e6b | |||
| 1ec099767f | |||
| 4e0e34f47a | |||
| 7457fb0e63 | |||
| 23ef372a17 | |||
| 164811ab35 | |||
| 1de58a3e5d | |||
| 7e320e0e92 | |||
| d6fe9c7e7a | |||
| 75188211aa | |||
| 4e68778561 | |||
| a68cc20dd4 | |||
| ba113a050e | |||
| 0d8cec6e4f | |||
| a47bcedad4 | |||
| 387c6e7bbc | |||
| 9b24e1dfed | |||
| 5f4eedd0e7 | |||
| 96d52ce150 | |||
| 55cf26bf28 | |||
| 5fdcbd6b90 | |||
| e92fb7fa7c | |||
| 1a870a3bb1 | |||
| 6038403179 | |||
| fb38d0db2e | |||
| 5046bd4e56 | |||
| 2458c2863d | |||
| 3819bbe8fa | |||
| e2016fd2eb | |||
| 34a1443a85 | |||
| 35ced04445 | |||
| 90fa2f682a | |||
| 4fef0377bb | |||
| b0466d9e6e | |||
| 4b912228be | |||
| 9a606dca7c | |||
| 6d5b84a0ba | |||
| cce00970f1 | |||
| 23b91d951e | |||
| 053035234c | |||
| a075824087 | |||
| 13c14af9a2 | |||
| 280ddaf47e |
@@ -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
|
||||
|
||||
+292
-38
@@ -1,5 +1,246 @@
|
||||
<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...2.0.2) (2016-08-15)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **datepickerPopup:** correctly format to timezone ([fbd0845](https://github.com/angular-ui/bootstrap/commit/fbd0845)), closes [#6159](https://github.com/angular-ui/bootstrap/issues/6159) [#6105](https://github.com/angular-ui/bootstrap/issues/6105) [#6146](https://github.com/angular-ui/bootstrap/issues/6146) [#6147](https://github.com/angular-ui/bootstrap/issues/6147)
|
||||
* **dropdown:** fix keyboard-nav ([6bad759](https://github.com/angular-ui/bootstrap/commit/6bad759)), closes [#6102](https://github.com/angular-ui/bootstrap/issues/6102) [#6154](https://github.com/angular-ui/bootstrap/issues/6154)
|
||||
|
||||
|
||||
|
||||
<a name="2.0.1"></a>
|
||||
## [2.0.1](https://github.com/angular-ui/bootstrap/compare/2.0.0...2.0.1) (2016-08-02)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **modal:** restore broken stacked modals([c61d16a](https://github.com/angular-ui/bootstrap/commit/c61d16a)), closes [#6103](https://github.com/angular-ui/bootstrap/issues/6103) [#6104](https://github.com/angular-ui/bootstrap/issues/6104)
|
||||
|
||||
|
||||
|
||||
<a name="2.0.0"></a>
|
||||
# [2.0.0](https://github.com/angular-ui/bootstrap/compare/1.3.3...2.0.0) (2016-07-20)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **dateparser:** correctly format with literals([d846e2d](https://github.com/angular-ui/bootstrap/commit/d846e2d)), closes [#6055](https://github.com/angular-ui/bootstrap/issues/6055) [#5620](https://github.com/angular-ui/bootstrap/issues/5620) [#5802](https://github.com/angular-ui/bootstrap/issues/5802)
|
||||
* **datepickerPopup:** clear date when button is clicked([b0466d9](https://github.com/angular-ui/bootstrap/commit/b0466d9)), closes [#5945](https://github.com/angular-ui/bootstrap/issues/5945) [#5906](https://github.com/angular-ui/bootstrap/issues/5906)
|
||||
* **datepickerPopup:** specify dependency on datepicker([4fef037](https://github.com/angular-ui/bootstrap/commit/4fef037)), closes [#5954](https://github.com/angular-ui/bootstrap/issues/5954)
|
||||
* **datepickerPopup:** use value instead of viewValue([7e320e0](https://github.com/angular-ui/bootstrap/commit/7e320e0)), closes [#6007](https://github.com/angular-ui/bootstrap/issues/6007)
|
||||
* **dropdown:** align position correctly with scrollbar([2d831bc](https://github.com/angular-ui/bootstrap/commit/2d831bc)), closes [#6008](https://github.com/angular-ui/bootstrap/issues/6008) [#5942](https://github.com/angular-ui/bootstrap/issues/5942)
|
||||
* **dropdown:** bind event listener on dropdown menu([6038403](https://github.com/angular-ui/bootstrap/commit/6038403)), closes [#5982](https://github.com/angular-ui/bootstrap/issues/5982)
|
||||
* **modal:** check for overflow hidden([433e536](https://github.com/angular-ui/bootstrap/commit/433e536)), closes [#6037](https://github.com/angular-ui/bootstrap/issues/6037) [#6041](https://github.com/angular-ui/bootstrap/issues/6041)
|
||||
* **modal:** filter out non-tabbable elements([35ced04](https://github.com/angular-ui/bootstrap/commit/35ced04)), closes [#5963](https://github.com/angular-ui/bootstrap/issues/5963) [#5955](https://github.com/angular-ui/bootstrap/issues/5955)
|
||||
* **modal:** remove unused template from modal([1de58a3](https://github.com/angular-ui/bootstrap/commit/1de58a3))
|
||||
* **modal:** remove window class after animation([409b7aa](https://github.com/angular-ui/bootstrap/commit/409b7aa)), closes [#6056](https://github.com/angular-ui/bootstrap/issues/6056) [#6051](https://github.com/angular-ui/bootstrap/issues/6051)
|
||||
* **tooltip:** missed dependency for cjs([164811a](https://github.com/angular-ui/bootstrap/commit/164811a)), closes [#5999](https://github.com/angular-ui/bootstrap/issues/5999)
|
||||
* **tooltip:** reposition for placement top([34a1443](https://github.com/angular-ui/bootstrap/commit/34a1443)), closes [#5959](https://github.com/angular-ui/bootstrap/issues/5959)
|
||||
* **typeahead:** change to select class([13c14af](https://github.com/angular-ui/bootstrap/commit/13c14af)), closes [#5922](https://github.com/angular-ui/bootstrap/issues/5922) [#5848](https://github.com/angular-ui/bootstrap/issues/5848)
|
||||
* **typeahead:** clear validity in $digest([ed3400b](https://github.com/angular-ui/bootstrap/commit/ed3400b)), closes [#6033](https://github.com/angular-ui/bootstrap/issues/6033) [#6032](https://github.com/angular-ui/bootstrap/issues/6032)
|
||||
* **typeahead:** remove duplicate id attribute([6d5b84a](https://github.com/angular-ui/bootstrap/commit/6d5b84a)), closes [#5936](https://github.com/angular-ui/bootstrap/issues/5936) [#5926](https://github.com/angular-ui/bootstrap/issues/5926)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* **accordion:** add appropriate tabindex on disabled([5f4eedd](https://github.com/angular-ui/bootstrap/commit/5f4eedd)), closes [#4067](https://github.com/angular-ui/bootstrap/issues/4067) [#5990](https://github.com/angular-ui/bootstrap/issues/5990)
|
||||
* **accordion:** remove replace: true usage([3819bbe](https://github.com/angular-ui/bootstrap/commit/3819bbe)), closes [#5985](https://github.com/angular-ui/bootstrap/issues/5985)
|
||||
* **alert:** remove replace: true usage([2458c28](https://github.com/angular-ui/bootstrap/commit/2458c28)), closes [#5986](https://github.com/angular-ui/bootstrap/issues/5986)
|
||||
* **carousel:** remove replace: true usage([5046bd4](https://github.com/angular-ui/bootstrap/commit/5046bd4)), closes [#5987](https://github.com/angular-ui/bootstrap/issues/5987)
|
||||
* **collapse:** add horizontal support([1ec0997](https://github.com/angular-ui/bootstrap/commit/1ec0997)), closes [#3593](https://github.com/angular-ui/bootstrap/issues/3593) [#6010](https://github.com/angular-ui/bootstrap/issues/6010)
|
||||
* **datepicker:** add monthColumns([39d9b98](https://github.com/angular-ui/bootstrap/commit/39d9b98)), closes [#5515](https://github.com/angular-ui/bootstrap/issues/5515) [#5935](https://github.com/angular-ui/bootstrap/issues/5935)
|
||||
* **datepicker:** add ngModelOptions support([23b91d9](https://github.com/angular-ui/bootstrap/commit/23b91d9)), closes [#5933](https://github.com/angular-ui/bootstrap/issues/5933) [#5825](https://github.com/angular-ui/bootstrap/issues/5825)
|
||||
* **datepicker:** remove replace: true usage([e92fb7f](https://github.com/angular-ui/bootstrap/commit/e92fb7f)), closes [#5988](https://github.com/angular-ui/bootstrap/issues/5988)
|
||||
* **datepickerPopup:** remove replace usage([a47bced](https://github.com/angular-ui/bootstrap/commit/a47bced)), closes [#5993](https://github.com/angular-ui/bootstrap/issues/5993)
|
||||
* **dropdown:** focus toggle on close from click([cce0097](https://github.com/angular-ui/bootstrap/commit/cce0097)), closes [#5934](https://github.com/angular-ui/bootstrap/issues/5934) [#5782](https://github.com/angular-ui/bootstrap/issues/5782)
|
||||
* **dropdown:** use .value() for uibDropdownConfig([7457fb0](https://github.com/angular-ui/bootstrap/commit/7457fb0)), closes [#6004](https://github.com/angular-ui/bootstrap/issues/6004) [#6006](https://github.com/angular-ui/bootstrap/issues/6006)
|
||||
* **modal:** append using $animate([1cbd73d](https://github.com/angular-ui/bootstrap/commit/1cbd73d)), closes [#6023](https://github.com/angular-ui/bootstrap/issues/6023) [#6029](https://github.com/angular-ui/bootstrap/issues/6029)
|
||||
* **modal:** remove replace usage([96d52ce](https://github.com/angular-ui/bootstrap/commit/96d52ce)), closes [#5989](https://github.com/angular-ui/bootstrap/issues/5989)
|
||||
* **pager:** remove replace usage([9b24e1d](https://github.com/angular-ui/bootstrap/commit/9b24e1d)), closes [#5991](https://github.com/angular-ui/bootstrap/issues/5991)
|
||||
* **pager:** toggle tabindex when disabled([0d8cec6](https://github.com/angular-ui/bootstrap/commit/0d8cec6)), closes [#5996](https://github.com/angular-ui/bootstrap/issues/5996)
|
||||
* **pagination:** disable tabbing when disabled([1a870a3](https://github.com/angular-ui/bootstrap/commit/1a870a3)), closes [#5984](https://github.com/angular-ui/bootstrap/issues/5984)
|
||||
* **pagination:** remove replace usage([387c6e7](https://github.com/angular-ui/bootstrap/commit/387c6e7)), closes [#5992](https://github.com/angular-ui/bootstrap/issues/5992)
|
||||
* **rating:** remove replace usage([d6fe9c7](https://github.com/angular-ui/bootstrap/commit/d6fe9c7)), closes [#5998](https://github.com/angular-ui/bootstrap/issues/5998)
|
||||
* **stackedMap:** improve perf of removeTop([a075824](https://github.com/angular-ui/bootstrap/commit/a075824)), closes [#5925](https://github.com/angular-ui/bootstrap/issues/5925) [#5932](https://github.com/angular-ui/bootstrap/issues/5932)
|
||||
* **timepicker:** avoid allowing to tab to controls([4e68778](https://github.com/angular-ui/bootstrap/commit/4e68778)), closes [#5930](https://github.com/angular-ui/bootstrap/issues/5930)
|
||||
* **timepicker:** remove replace usage([7518821](https://github.com/angular-ui/bootstrap/commit/7518821)), closes [#5997](https://github.com/angular-ui/bootstrap/issues/5997)
|
||||
* **tooltip:** add expression support([4b91222](https://github.com/angular-ui/bootstrap/commit/4b91222)), closes [#5221](https://github.com/angular-ui/bootstrap/issues/5221) [#5938](https://github.com/angular-ui/bootstrap/issues/5938)
|
||||
* **tooltip:** remove replace usage([1616e97](https://github.com/angular-ui/bootstrap/commit/1616e97)), closes [#5994](https://github.com/angular-ui/bootstrap/issues/5994)
|
||||
|
||||
|
||||
### Reverts
|
||||
|
||||
* **dropdown:** change back to .constant()([4e0e34f](https://github.com/angular-ui/bootstrap/commit/4e0e34f))
|
||||
|
||||
|
||||
### BREAKING CHANGES
|
||||
|
||||
* tooltip: The template structure changed slightly due to the removal of `replace: true` - see documentation examples in action to see differences in practice.
|
||||
* typeahead: This changes the selector used so that it doesn't select for the `li` tag specifically, but the elements with the class `uib-typeahead-match` - if using a custom template, this class needs to be added to the `li` element in the typeahead popup template.
|
||||
* rating: Due to the removal of `replace: true`, this results in a slight change to the HTML structure - see the documentation examples to see it in action.
|
||||
* timepicker: This removes `replace: true`, which changes the HTML structure slightly - see the documentation examples to see it in action.
|
||||
* datepickerPopup: Due to the nature of `replace: true`, this has a slight structural HTML change in the popup as a result - see documentation examples for the change in action.
|
||||
* pagination: Due to the removal of `replace: true`, this affects the HTML structure slightly - see documentation examples to see new usage.
|
||||
* pager: This removes `replace: true` usage from the pager, which causes a slight usage change - see documentation examples for new usage.
|
||||
* modal: This removes `replace: true` usage, causing some structural changes to the HTML - the major part here is that there is no more backdrop template, and the top level elements for the window/backdrop elements lose a little flexibility. See documentation examples for new structure.
|
||||
* modal: This introduces a minor behavior change in when the class is removed
|
||||
* carousel: Due to the removal of `replace: true`, this causes a slight HTML structure change to the carousel and the slide elements - see documentation demos to see how it changes. This also caused removal of the ngTouch built in support - if one is using ng-touch, one needs to add the `ng-swipe-left` and `ng-swipe-right` directives to the carousel element with relevant logic.
|
||||
* alert: This removes the `replace: true` usage, so this has an effect on how one uses the directive in the template - see documentation for reference
|
||||
* accordion: This removes usage of `replace: true` in the accordion group, which results in a template change where the template no longer needs to contain the panel itself, but its contents. The accordion group will add the `panel` class by default, so the user just needs to add the appropriate classes to the accordion group element. This allows the user to use ng-class as well to fully control the panel related classes, so `panel-class` now is unnecessary
|
||||
* tooltip: This removes support for plain strings and interpolations in tooltip-trigger and popover-trigger - please change these appropriately. See test changes in this commit for reference
|
||||
* typeahead: This change removes the `id` attribute on the first `<input>`
|
||||
element placed into the DOM when the `typeahead-show-hint` attribute is used
|
||||
and there is an `id` attribute present on the original `uib-typeahead` element.
|
||||
This could affect selectors if they are being used.
|
||||
* dropdown: This changes the focus behavior of the dropdown slightly, and potentially may break code built around current usage
|
||||
* datepicker: This modifies the current behavior around the datepicker & popup's ngModelOptions, which may affect custom components that are built around both
|
||||
* datepicker: As a result of removal of `replace: true`, there is the potential that this may break some CSS layout due to the slightly different HTML. Refer to the documentation examples to see the new structure.
|
||||
|
||||
|
||||
|
||||
<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
|
||||
@@ -25,7 +266,7 @@
|
||||
|
||||
|
||||
<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
|
||||
@@ -51,7 +292,7 @@
|
||||
|
||||
|
||||
<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
|
||||
@@ -62,7 +303,7 @@
|
||||
|
||||
|
||||
<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
|
||||
@@ -105,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
|
||||
@@ -128,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
|
||||
@@ -153,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
|
||||
@@ -170,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
|
||||
@@ -180,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
|
||||
@@ -240,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
|
||||
@@ -258,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
|
||||
@@ -308,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
|
||||
@@ -318,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
|
||||
@@ -328,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
|
||||
@@ -349,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
|
||||
@@ -494,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
|
||||
@@ -519,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
|
||||
@@ -531,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
|
||||
@@ -714,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
|
||||
@@ -801,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
|
||||
@@ -847,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
|
||||
@@ -908,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
|
||||
@@ -1007,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
|
||||
|
||||
@@ -1015,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
|
||||
@@ -1068,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
|
||||
|
||||
@@ -1106,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
|
||||
|
||||
@@ -1284,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_
|
||||
|
||||
@@ -1305,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_
|
||||
|
||||
@@ -1348,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
|
||||
|
||||
@@ -1415,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
|
||||
|
||||
@@ -1463,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
|
||||
|
||||
@@ -1542,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
|
||||
|
||||
@@ -1625,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
|
||||
|
||||
@@ -1743,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
|
||||
|
||||
@@ -1785,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
|
||||
|
||||
@@ -1819,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.3',
|
||||
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)
|
||||
@@ -27,7 +28,7 @@
|
||||
|
||||
# Demo
|
||||
|
||||
Do you want to see directives in action? Visit http://angular-ui.github.io/bootstrap/!
|
||||
Do you want to see directives in action? Visit https://angular-ui.github.io/bootstrap/!
|
||||
|
||||
# Angular 2
|
||||
|
||||
@@ -73,7 +74,7 @@ PM> Install-Package Angular.UI.Bootstrap
|
||||
|
||||
#### Custom build
|
||||
|
||||
Head over to http://angular-ui.github.io/bootstrap/ and hit the *Custom build* button to create your own custom UI Bootstrap build, just the way you like it.
|
||||
Head over to https://angular-ui.github.io/bootstrap/ and hit the *Custom build* button to create your own custom UI Bootstrap build, just the way you like it.
|
||||
|
||||
#### Manual download
|
||||
|
||||
@@ -118,6 +119,7 @@ If you would prefer not to load your css through your JavaScript file loader/bun
|
||||
* datepicker
|
||||
* datepickerPopup
|
||||
* dropdown
|
||||
* modal
|
||||
* popover
|
||||
* position
|
||||
* timepicker
|
||||
@@ -130,9 +132,13 @@ 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
|
||||
|
||||
Pre-2.0.0 does not follow a particular versioning system. 2.0.0 and onwards follows [semantic versioning](http://semver.org/). All release changes can be viewed on our [changelog](CHANGELOG.md).
|
||||
|
||||
# Support
|
||||
|
||||
## FAQ
|
||||
|
||||
@@ -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 |
@@ -4,7 +4,7 @@ angular.module('plunker', [])
|
||||
|
||||
return function (ngVersion, bsVersion, version, module, content) {
|
||||
|
||||
var form = angular.element('<form style="display: none;" method="post" action="http://plnkr.co/edit/?p=preview" target="_blank"></form>');
|
||||
var form = angular.element('<form style="display: none;" method="post" action="https://plnkr.co/edit/?p=preview" target="_blank"></form>');
|
||||
var addField = function (name, value) {
|
||||
var input = angular.element('<input type="hidden" name="' + name + '">');
|
||||
input.attr('value', value);
|
||||
@@ -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/');
|
||||
|
||||
+10
-2
@@ -147,6 +147,13 @@
|
||||
<div class="page-header">
|
||||
<h1>Getting started</h1>
|
||||
</div>
|
||||
<h3>Angular 2</h3>
|
||||
<p>
|
||||
For Angular 2 support, check out
|
||||
<a href="https://ng-bootstrap.github.io" target="_blank">
|
||||
ng-bootstrap
|
||||
</a>, created by the UI Bootstrap team.
|
||||
</p>
|
||||
<h3>Dependencies</h3>
|
||||
<p>
|
||||
This repository contains a set of <strong>native AngularJS directives</strong> based on
|
||||
@@ -173,8 +180,9 @@
|
||||
<p>Alternativelly, if you are only interested in a subset of directives, you can
|
||||
<a ng-click="showBuildModal()">create your own build</a>.
|
||||
</p>
|
||||
<p>Whichever method you choose the good news that the overall size of a download is very small:
|
||||
<76kB for all directives (~20kB with gzip compression!)</p>
|
||||
<p>Whichever method you choose the good news that the overall size of a download is fairly small:
|
||||
122K minified for all directives with templates and 98K without (~31kB with gzip
|
||||
compression, with templates, and 28K gzipped without)</p>
|
||||
<h3>Installation</h3>
|
||||
<p>As soon as you've got all the files downloaded and included in your page you just need to declare
|
||||
a dependency on the <code>ui.bootstrap</code> <a href="http://docs.angularjs.org/guide/module">module</a>:<br>
|
||||
|
||||
+14
-4
@@ -1,8 +1,15 @@
|
||||
{
|
||||
"author": "https://github.com/angular-ui/bootstrap/graphs/contributors",
|
||||
"name": "angular-ui-bootstrap",
|
||||
"version": "1.3.3",
|
||||
"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.3",
|
||||
"angular-mocks": "1.5.3",
|
||||
"angular-sanitize": "1.5.3",
|
||||
"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,4 +1,4 @@
|
||||
angular.module('ui.bootstrap.accordion', ['ui.bootstrap.collapse'])
|
||||
angular.module('ui.bootstrap.accordion', ['ui.bootstrap.collapse', 'ui.bootstrap.tabindex'])
|
||||
|
||||
.constant('uibAccordionConfig', {
|
||||
closeOthers: true
|
||||
@@ -58,7 +58,7 @@ angular.module('ui.bootstrap.accordion', ['ui.bootstrap.collapse'])
|
||||
return {
|
||||
require: '^uibAccordion', // We need this directive to be inside an accordion
|
||||
transclude: true, // It transcludes the contents of the directive into the template
|
||||
replace: true, // The element containing the directive will be replaced with the template
|
||||
restrict: 'A',
|
||||
templateUrl: function(element, attrs) {
|
||||
return attrs.templateUrl || 'uib/template/accordion/accordion-group.html';
|
||||
},
|
||||
@@ -74,6 +74,7 @@ angular.module('ui.bootstrap.accordion', ['ui.bootstrap.collapse'])
|
||||
};
|
||||
},
|
||||
link: function(scope, element, attrs, accordionCtrl) {
|
||||
element.addClass('panel');
|
||||
accordionCtrl.addGroup(scope);
|
||||
|
||||
scope.openClass = attrs.openClass || 'panel-open';
|
||||
|
||||
@@ -1,18 +1,16 @@
|
||||
<div ng-controller="AccordionDemoCtrl">
|
||||
<script type="text/ng-template" id="group-template.html">
|
||||
<div class="panel {{panelClass || '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>
|
||||
|
||||
@@ -28,35 +26,35 @@
|
||||
</label>
|
||||
</div>
|
||||
<uib-accordion close-others="oneAtATime">
|
||||
<uib-accordion-group heading="Static Header, initially expanded" is-open="status.isFirstOpen" is-disabled="status.isFirstDisabled">
|
||||
<div uib-accordion-group class="panel-default" heading="Static Header, initially expanded" is-open="status.isFirstOpen" is-disabled="status.isFirstDisabled">
|
||||
This content is straight in the template.
|
||||
</uib-accordion-group>
|
||||
<uib-accordion-group heading="{{group.title}}" ng-repeat="group in groups">
|
||||
</div>
|
||||
<div uib-accordion-group class="panel-default" heading="{{group.title}}" ng-repeat="group in groups">
|
||||
{{group.content}}
|
||||
</uib-accordion-group>
|
||||
<uib-accordion-group heading="Dynamic Body Content">
|
||||
</div>
|
||||
<div uib-accordion-group class="panel-default" heading="Dynamic Body Content">
|
||||
<p>The body of the uib-accordion group grows to fit the contents</p>
|
||||
<button type="button" class="btn btn-default btn-sm" ng-click="addItem()">Add Item</button>
|
||||
<div ng-repeat="item in items">{{item}}</div>
|
||||
</uib-accordion-group>
|
||||
<uib-accordion-group heading="Custom template" template-url="group-template.html">
|
||||
</div>
|
||||
<div uib-accordion-group class="panel-default" heading="Custom template" template-url="group-template.html">
|
||||
Hello
|
||||
</uib-accordion-group>
|
||||
<uib-accordion-group is-open="status.isCustomHeaderOpen" template-url="group-template.html">
|
||||
</div>
|
||||
<div uib-accordion-group class="panel-default" is-open="status.isCustomHeaderOpen" template-url="group-template.html">
|
||||
<uib-accordion-heading>
|
||||
Custom template with custom header template <i class="pull-right glyphicon" ng-class="{'glyphicon-chevron-down': status.isCustomHeaderOpen, 'glyphicon-chevron-right': !status.isCustomHeaderOpen}"></i>
|
||||
</uib-accordion-heading>
|
||||
World
|
||||
</uib-accordion-group>
|
||||
<uib-accordion-group heading="Delete account" panel-class="panel-danger">
|
||||
</div>
|
||||
<div uib-accordion-group class="panel-danger" heading="Delete account">
|
||||
<p>Please, to delete your account, click the button below</p>
|
||||
<button class="btn btn-danger">Delete</button>
|
||||
</uib-accordion-group>
|
||||
<uib-accordion-group is-open="status.open">
|
||||
</div>
|
||||
<div uib-accordion-group class="panel-default" is-open="status.open">
|
||||
<uib-accordion-heading>
|
||||
I can have markup, too! <i class="pull-right glyphicon" ng-class="{'glyphicon-chevron-down': status.open, 'glyphicon-chevron-right': !status.open}"></i>
|
||||
</uib-accordion-heading>
|
||||
This is just some content to illustrate fancy headings.
|
||||
</uib-accordion-group>
|
||||
</div>
|
||||
</uib-accordion>
|
||||
</div>
|
||||
|
||||
@@ -32,11 +32,6 @@ The body of each accordion group is transcluded into the body of the collapsible
|
||||
_(Default: `false`)_ -
|
||||
Whether accordion group is open or closed.
|
||||
|
||||
* `panel-class`
|
||||
<i class="glyphicon glyphicon-eye-open"></i>
|
||||
_(Default: `panel-default`)_ -
|
||||
Add ability to use Bootstrap's contextual panel classes (panel-primary, panel-success, panel-info, etc...) or your own. This must be a string.
|
||||
|
||||
* `template-url`
|
||||
_(Default: `uib/template/accordion/accordion-group.html`)_ -
|
||||
Add the ability to override the template used on the component.
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
require('../collapse');
|
||||
require('../tabindex');
|
||||
require('../../template/accordion/accordion-group.html.js');
|
||||
require('../../template/accordion/accordion.html.js');
|
||||
require('./accordion');
|
||||
|
||||
@@ -176,12 +176,12 @@ describe('uib-accordion', function() {
|
||||
|
||||
var tpl =
|
||||
'<uib-accordion>' +
|
||||
'<uib-accordion-group heading="title 1" template-url="foo/bar.html"></uib-accordion-group>' +
|
||||
'<div uib-accordion-group heading="title 1" template-url="foo/bar.html"></div>' +
|
||||
'</uib-accordion>';
|
||||
|
||||
element = $compile(tpl)(scope);
|
||||
scope.$digest();
|
||||
expect(element.find('[template-url]').html()).toBe('baz');
|
||||
expect(element.find('[template-url]').html()).toBe('<div>baz</div>');
|
||||
}));
|
||||
|
||||
describe('with static panels', function() {
|
||||
@@ -189,8 +189,8 @@ describe('uib-accordion', function() {
|
||||
spyOn(Math, 'random').and.returnValue(0.1);
|
||||
var tpl =
|
||||
'<uib-accordion>' +
|
||||
'<uib-accordion-group heading="title 1">Content 1</uib-accordion-group>' +
|
||||
'<uib-accordion-group heading="title 2">Content 2</uib-accordion-group>' +
|
||||
'<div uib-accordion-group heading="title 1">Content 1</div>' +
|
||||
'<div uib-accordion-group heading="title 2">Content 2</div>' +
|
||||
'</uib-accordion>';
|
||||
element = angular.element(tpl);
|
||||
$compile(element)(scope);
|
||||
@@ -288,8 +288,8 @@ describe('uib-accordion', function() {
|
||||
beforeEach(function() {
|
||||
var tpl =
|
||||
'<uib-accordion>' +
|
||||
'<uib-accordion-group heading="title 1" open-class="custom-open-class">Content 1</uib-accordion-group>' +
|
||||
'<uib-accordion-group heading="title 2" open-class="custom-open-class">Content 2</uib-accordion-group>' +
|
||||
'<div uib-accordion-group heading="title 1" open-class="custom-open-class">Content 1</div>' +
|
||||
'<div uib-accordion-group heading="title 2" open-class="custom-open-class">Content 2</div>' +
|
||||
'</uib-accordion>';
|
||||
element = angular.element(tpl);
|
||||
$compile(element)(scope);
|
||||
@@ -318,7 +318,7 @@ describe('uib-accordion', function() {
|
||||
beforeEach(function() {
|
||||
var tpl =
|
||||
'<uib-accordion>' +
|
||||
'<uib-accordion-group ng-repeat="group in groups" heading="{{group.name}}">{{group.content}}</uib-accordion-group>' +
|
||||
'<div uib-accordion-group ng-repeat="group in groups" heading="{{group.name}}">{{group.content}}</div>' +
|
||||
'</uib-accordion>';
|
||||
element = angular.element(tpl);
|
||||
model = [
|
||||
@@ -363,8 +363,8 @@ describe('uib-accordion', function() {
|
||||
beforeEach(function() {
|
||||
var tpl =
|
||||
'<uib-accordion>' +
|
||||
'<uib-accordion-group heading="title 1" is-open="open.first">Content 1</uib-accordion-group>' +
|
||||
'<uib-accordion-group heading="title 2" is-open="open.second">Content 2</uib-accordion-group>' +
|
||||
'<div uib-accordion-group heading="title 1" is-open="open.first">Content 1</div>' +
|
||||
'<div uib-accordion-group heading="title 2" is-open="open.second">Content 2</div>' +
|
||||
'</uib-accordion>';
|
||||
element = angular.element(tpl);
|
||||
scope.open = { first: false, second: true };
|
||||
@@ -393,8 +393,8 @@ describe('uib-accordion', function() {
|
||||
beforeEach(function() {
|
||||
var tpl =
|
||||
'<uib-accordion>' +
|
||||
'<uib-accordion-group heading="title 1" is-open="open1"><div ng-repeat="item in items">{{item}}</div></uib-accordion-group>' +
|
||||
'<uib-accordion-group heading="title 2" is-open="open2">Static content</uib-accordion-group>' +
|
||||
'<div uib-accordion-group heading="title 1" is-open="open1"><div ng-repeat="item in items">{{item}}</div></div>' +
|
||||
'<div uib-accordion-group heading="title 2" is-open="open2">Static content</div>' +
|
||||
'</uib-accordion>';
|
||||
element = angular.element(tpl);
|
||||
scope.items = ['Item 1', 'Item 2', 'Item 3'];
|
||||
@@ -421,7 +421,7 @@ describe('uib-accordion', function() {
|
||||
beforeEach(function() {
|
||||
var tpl =
|
||||
'<uib-accordion>' +
|
||||
'<uib-accordion-group ng-repeat="group in groups" heading="{{group.name}}" is-open="group.open">{{group.content}}</uib-accordion-group>' +
|
||||
'<div uib-accordion-group ng-repeat="group in groups" heading="{{group.name}}" is-open="group.open">{{group.content}}</div>' +
|
||||
'</uib-accordion>';
|
||||
element = angular.element(tpl);
|
||||
scope.groups = [
|
||||
@@ -456,7 +456,7 @@ describe('uib-accordion', function() {
|
||||
beforeEach(function() {
|
||||
var tpl =
|
||||
'<uib-accordion>' +
|
||||
'<uib-accordion-group ng-repeat="group in groups" heading="{{group.name}}" is-open="group.open" class="testClass">{{group.content}}</uib-accordion-group>' +
|
||||
'<div uib-accordion-group ng-repeat="group in groups" heading="{{group.name}}" is-open="group.open" class="testClass">{{group.content}}</div>' +
|
||||
'</uib-accordion>';
|
||||
element = angular.element(tpl);
|
||||
scope.groups = [
|
||||
@@ -480,7 +480,7 @@ describe('uib-accordion', function() {
|
||||
beforeEach(function() {
|
||||
var tpl =
|
||||
'<uib-accordion>' +
|
||||
'<uib-accordion-group heading="title 1" is-disabled="disabled">Content 1</uib-accordion-group>' +
|
||||
'<div uib-accordion-group heading="title 1" is-disabled="disabled">Content 1</div>' +
|
||||
'</uib-accordion>';
|
||||
element = angular.element(tpl);
|
||||
scope.disabled = true;
|
||||
@@ -519,10 +519,10 @@ describe('uib-accordion', function() {
|
||||
function isDisabledStyleCheck() {
|
||||
var tpl =
|
||||
'<uib-accordion ng-init="a = [1,2,3]">' +
|
||||
'<uib-accordion-group heading="I get overridden" is-disabled="true">' +
|
||||
'<div uib-accordion-group heading="I get overridden" is-disabled="true">' +
|
||||
'<uib-accordion-heading>Heading Element <span ng-repeat="x in a">{{x}}</span> </uib-accordion-heading>' +
|
||||
'Body' +
|
||||
'</uib-accordion-group>' +
|
||||
'</div>' +
|
||||
'</uib-accordion>';
|
||||
scope.disabled = true;
|
||||
element = $compile(tpl)(scope);
|
||||
@@ -536,10 +536,10 @@ describe('uib-accordion', function() {
|
||||
beforeEach(function() {
|
||||
var tpl =
|
||||
'<uib-accordion ng-init="a = [1,2,3]">' +
|
||||
'<uib-accordion-group heading="I get overridden">' +
|
||||
'<div uib-accordion-group heading="I get overridden">' +
|
||||
'<uib-accordion-heading>Heading Element <span ng-repeat="x in a">{{x}}</span> </uib-accordion-heading>' +
|
||||
'Body' +
|
||||
'</uib-accordion-group>' +
|
||||
'</div>' +
|
||||
'</uib-accordion>';
|
||||
element = $compile(tpl)(scope);
|
||||
scope.$digest();
|
||||
@@ -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() {
|
||||
@@ -565,10 +565,10 @@ describe('uib-accordion', function() {
|
||||
beforeEach(function() {
|
||||
var tpl =
|
||||
'<uib-accordion ng-init="a = [1,2,3]">' +
|
||||
'<uib-accordion-group heading="I get overridden">' +
|
||||
'<div uib-accordion-group heading="I get overridden">' +
|
||||
'<div uib-accordion-heading>Heading Element <span ng-repeat="x in a">{{x}}</span> </div>' +
|
||||
'Body' +
|
||||
'</uib-accordion-group>' +
|
||||
'</div>' +
|
||||
'</uib-accordion>';
|
||||
element = $compile(tpl)(scope);
|
||||
scope.$digest();
|
||||
@@ -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);
|
||||
@@ -588,7 +588,7 @@ describe('uib-accordion', function() {
|
||||
|
||||
describe('uib-accordion-heading, with repeating uib-accordion-groups', function() {
|
||||
it('should clone the uib-accordion-heading for each group', function() {
|
||||
element = $compile('<uib-accordion><uib-accordion-group ng-repeat="x in [1,2,3]"><uib-accordion-heading>{{x}}</uib-accordion-heading></uib-accordion-group></uib-accordion>')(scope);
|
||||
element = $compile('<uib-accordion><div uib-accordion-group ng-repeat="x in [1,2,3]"><uib-accordion-heading>{{x}}</uib-accordion-heading></div></uib-accordion>')(scope);
|
||||
scope.$digest();
|
||||
groups = element.find('.panel');
|
||||
expect(groups.length).toBe(3);
|
||||
@@ -600,7 +600,7 @@ describe('uib-accordion', function() {
|
||||
|
||||
describe('uib-accordion-heading attribute, with repeating uib-accordion-groups', function() {
|
||||
it('should clone the uib-accordion-heading for each group', function() {
|
||||
element = $compile('<uib-accordion><uib-accordion-group ng-repeat="x in [1,2,3]"><div uib-accordion-heading>{{x}}</div></uib-accordion-group></uib-accordion>')(scope);
|
||||
element = $compile('<uib-accordion><div uib-accordion-group ng-repeat="x in [1,2,3]"><div uib-accordion-heading>{{x}}</div></div></uib-accordion>')(scope);
|
||||
scope.$digest();
|
||||
groups = element.find('.panel');
|
||||
expect(groups.length).toBe(3);
|
||||
@@ -614,46 +614,11 @@ describe('uib-accordion', function() {
|
||||
it('should transclude heading to a template using data-uib-accordion-header', inject(function($templateCache) {
|
||||
$templateCache.put('foo/bar.html', '<div class="panel"><a uib-accordion-transclude="heading" class="accordion-toggle"><span data-uib-accordion-header></span></a><div ng-transclude></div></div>');
|
||||
|
||||
element = $compile('<uib-accordion><uib-accordion-group template-url="foo/bar.html"><uib-accordion-heading>baz</uib-accordion-heading></uib-accordion-group></uib-accordion>')(scope);
|
||||
element = $compile('<uib-accordion><div uib-accordion-group template-url="foo/bar.html"><uib-accordion-heading>baz</uib-accordion-heading></div></uib-accordion>')(scope);
|
||||
scope.$digest();
|
||||
groups = element.find('.panel');
|
||||
expect(findGroupLink(0).text()).toBe('baz');
|
||||
}));
|
||||
});
|
||||
|
||||
describe('uib-accordion group panel class', function() {
|
||||
it('should use the default value when panel class is falsy - #3968', function() {
|
||||
element = $compile('<uib-accordion><uib-accordion-group heading="Heading">Content</uib-accordion-group></uib-accordion>')(scope);
|
||||
scope.$digest();
|
||||
groups = element.find('.panel');
|
||||
expect(groups.eq(0)).toHaveClass('panel-default');
|
||||
|
||||
element = $compile('<uib-accordion><uib-accordion-group heading="Heading" panel-class="">Content</uib-accordion-group></uib-accordion>')(scope);
|
||||
scope.$digest();
|
||||
groups = element.find('.panel');
|
||||
expect(groups.eq(0)).toHaveClass('panel-default');
|
||||
});
|
||||
|
||||
it('should use the specified value when not falsy - #3968', function() {
|
||||
element = $compile('<uib-accordion><uib-accordion-group heading="Heading" panel-class="custom-class">Content</uib-accordion-group></uib-accordion>')(scope);
|
||||
scope.$digest();
|
||||
groups = element.find('.panel');
|
||||
expect(groups.eq(0)).toHaveClass('custom-class');
|
||||
expect(groups.eq(0)).not.toHaveClass('panel-default');
|
||||
});
|
||||
|
||||
it('should change class if panel-class is changed', function() {
|
||||
element = $compile('<uib-accordion><uib-accordion-group heading="Heading" panel-class="{{panelClass}}">Content</uib-accordion-group></uib-accordion>')(scope);
|
||||
scope.panelClass = 'custom-class';
|
||||
scope.$digest();
|
||||
groups = element.find('.panel');
|
||||
expect(groups.eq(0)).toHaveClass('custom-class');
|
||||
|
||||
scope.panelClass = 'different-class';
|
||||
scope.$digest();
|
||||
expect(groups.eq(0)).toHaveClass('different-class');
|
||||
expect(groups.eq(0)).not.toHaveClass('custom-class');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
+7
-3
@@ -1,7 +1,12 @@
|
||||
angular.module('ui.bootstrap.alert', [])
|
||||
|
||||
.controller('UibAlertController', ['$scope', '$attrs', '$interpolate', '$timeout', function($scope, $attrs, $interpolate, $timeout) {
|
||||
.controller('UibAlertController', ['$scope', '$element', '$attrs', '$interpolate', '$timeout', function($scope, $element, $attrs, $interpolate, $timeout) {
|
||||
$scope.closeable = !!$attrs.close;
|
||||
$element.addClass('alert');
|
||||
$attrs.$set('role', 'alert');
|
||||
if ($scope.closeable) {
|
||||
$element.addClass('alert-dismissible');
|
||||
}
|
||||
|
||||
var dismissOnTimeout = angular.isDefined($attrs.dismissOnTimeout) ?
|
||||
$interpolate($attrs.dismissOnTimeout)($scope.$parent) : null;
|
||||
@@ -17,13 +22,12 @@ angular.module('ui.bootstrap.alert', [])
|
||||
return {
|
||||
controller: 'UibAlertController',
|
||||
controllerAs: 'alert',
|
||||
restrict: 'A',
|
||||
templateUrl: function(element, attrs) {
|
||||
return attrs.templateUrl || 'uib/template/alert/alert.html';
|
||||
},
|
||||
transclude: true,
|
||||
replace: true,
|
||||
scope: {
|
||||
type: '@',
|
||||
close: '&'
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
<div ng-controller="AlertDemoCtrl">
|
||||
<script type="text/ng-template" id="alert.html">
|
||||
<div class="alert" style="background-color:#fa39c3;color:white" role="alert">
|
||||
<div ng-transclude></div>
|
||||
</div>
|
||||
<div ng-transclude></div>
|
||||
</script>
|
||||
|
||||
<uib-alert ng-repeat="alert in alerts" type="{{alert.type}}" close="closeAlert($index)">{{alert.msg}}</uib-alert>
|
||||
<uib-alert template-url="alert.html">A happy alert!</uib-alert>
|
||||
<div uib-alert ng-repeat="alert in alerts" ng-class="'alert-' + (alert.type || 'warning')" close="closeAlert($index)">{{alert.msg}}</div>
|
||||
<div uib-alert template-url="alert.html" style="background-color:#fa39c3;color:white">A happy alert!</div>
|
||||
<button type="button" class='btn btn-default' ng-click="addAlert()">Add Alert</button>
|
||||
</div>
|
||||
|
||||
@@ -5,15 +5,11 @@ This directive can be used both to generate alerts from static and dynamic model
|
||||
* `close()`
|
||||
<small class="badge">$</small> -
|
||||
A callback function that gets fired when an `alert` is closed. If the attribute exists, a close button is displayed as well.
|
||||
|
||||
|
||||
* `dismiss-on-timeout`
|
||||
_(Default: `none`)_ -
|
||||
Takes the number of milliseconds that specify the timeout duration, after which the alert will be closed. This attribute requires the presence of the `close` attribute.
|
||||
|
||||
|
||||
* `template-url`
|
||||
_(Default: `uib/template/alert/alert.html`)_ -
|
||||
Add the ability to override the template used in the component.
|
||||
|
||||
* `type`
|
||||
_(Default: `warning`)_ -
|
||||
Defines the type of the alert. Go to [bootstrap page](http://getbootstrap.com/components/#alerts) to see the type of alerts available.
|
||||
|
||||
@@ -12,9 +12,10 @@ describe('uib-alert', function() {
|
||||
|
||||
element = angular.element(
|
||||
'<div>' +
|
||||
'<uib-alert ng-repeat="alert in alerts" type="{{alert.type}}"' +
|
||||
'<div uib-alert ng-repeat="alert in alerts" ' +
|
||||
'ng-class="\'alert-\' + (alert.type || \'warning\')" ' +
|
||||
'close="removeAlert($index)">{{alert.msg}}' +
|
||||
'</uib-alert>' +
|
||||
'</div>' +
|
||||
'</div>');
|
||||
|
||||
scope.alerts = [
|
||||
@@ -35,13 +36,13 @@ 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() {
|
||||
$templateCache.put('uib/template/alert/alert.html', '<div>{{alert.text}}</div>');
|
||||
|
||||
element = $compile('<uib-alert></uib-alert>')(scope);
|
||||
element = $compile('<div uib-alert></div>')(scope);
|
||||
scope.$digest();
|
||||
|
||||
var ctrl = element.controller('uib-alert');
|
||||
@@ -50,16 +51,16 @@ describe('uib-alert', function() {
|
||||
ctrl.text = 'foo';
|
||||
scope.$digest();
|
||||
|
||||
expect(element.html()).toBe('foo');
|
||||
expect(element.html()).toBe('<div class="ng-binding">foo</div>');
|
||||
});
|
||||
|
||||
it('should support custom templates', function() {
|
||||
$templateCache.put('foo/bar.html', '<div>baz</div>');
|
||||
|
||||
element = $compile('<uib-alert template-url="foo/bar.html"></uib-alert>')(scope);
|
||||
element = $compile('<div uib-alert template-url="foo/bar.html"></div>')(scope);
|
||||
scope.$digest();
|
||||
|
||||
expect(element.html()).toBe('baz');
|
||||
expect(element.html()).toBe('<div>baz</div>');
|
||||
});
|
||||
|
||||
it('should generate alerts using ng-repeat', function() {
|
||||
@@ -67,23 +68,6 @@ describe('uib-alert', function() {
|
||||
expect(alerts.length).toEqual(3);
|
||||
});
|
||||
|
||||
it('should use correct classes for different alert types', function() {
|
||||
var alerts = createAlerts();
|
||||
expect(alerts.eq(0)).toHaveClass('alert-success');
|
||||
expect(alerts.eq(1)).toHaveClass('alert-error');
|
||||
expect(alerts.eq(2)).toHaveClass('alert-warning');
|
||||
});
|
||||
|
||||
it('should respect alert type binding', function() {
|
||||
var alerts = createAlerts();
|
||||
expect(alerts.eq(0)).toHaveClass('alert-success');
|
||||
|
||||
scope.alerts[0].type = 'error';
|
||||
scope.$digest();
|
||||
|
||||
expect(alerts.eq(0)).toHaveClass('alert-error');
|
||||
});
|
||||
|
||||
it('should show the alert content', function() {
|
||||
var alerts = createAlerts();
|
||||
|
||||
@@ -115,22 +99,15 @@ describe('uib-alert', function() {
|
||||
});
|
||||
|
||||
it('should not show close button and have the dismissible class if no close callback specified', function() {
|
||||
element = $compile('<uib-alert>No close</uib-alert>')(scope);
|
||||
element = $compile('<div uib-alert>No close</div>')(scope);
|
||||
scope.$digest();
|
||||
expect(findCloseButton(0)).toBeHidden();
|
||||
expect(element).not.toHaveClass('alert-dismissible');
|
||||
});
|
||||
|
||||
it('should be possible to add additional classes for alert', function() {
|
||||
var element = $compile('<uib-alert class="alert-block" type="info">Default alert!</uib-alert>')(scope);
|
||||
scope.$digest();
|
||||
expect(element).toHaveClass('alert-block');
|
||||
expect(element).toHaveClass('alert-info');
|
||||
});
|
||||
|
||||
it('should close automatically if dismiss-on-timeout is defined on the element', function() {
|
||||
scope.removeAlert = jasmine.createSpy();
|
||||
$compile('<uib-alert close="removeAlert()" dismiss-on-timeout="500">Default alert!</uib-alert>')(scope);
|
||||
$compile('<div uib-alert close="removeAlert()" dismiss-on-timeout="500">Default alert!</div>')(scope);
|
||||
scope.$digest();
|
||||
|
||||
$timeout.flush();
|
||||
@@ -140,7 +117,7 @@ describe('uib-alert', function() {
|
||||
it('should not close immediately with a dynamic dismiss-on-timeout', function() {
|
||||
scope.removeAlert = jasmine.createSpy();
|
||||
scope.dismissTime = 500;
|
||||
$compile('<uib-alert close="removeAlert()" dismiss-on-timeout="{{dismissTime}}">Default alert!</uib-alert>')(scope);
|
||||
$compile('<div uib-alert close="removeAlert()" dismiss-on-timeout="{{dismissTime}}">Default alert!</div>')(scope);
|
||||
scope.$digest();
|
||||
|
||||
$timeout.flush(100);
|
||||
|
||||
+14
-28
@@ -5,9 +5,10 @@ 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');
|
||||
|
||||
self.addSlide = function(slide, element) {
|
||||
slides.push({
|
||||
@@ -66,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) {
|
||||
@@ -94,7 +90,6 @@ angular.module('ui.bootstrap.carousel', [])
|
||||
if (slides.length === 0) {
|
||||
currentIndex = null;
|
||||
$scope.active = null;
|
||||
clearBufferedTransitions();
|
||||
}
|
||||
};
|
||||
|
||||
@@ -109,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]);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -145,6 +138,9 @@ angular.module('ui.bootstrap.carousel', [])
|
||||
}
|
||||
};
|
||||
|
||||
$element.on('mouseenter', $scope.pause);
|
||||
$element.on('mouseleave', $scope.play);
|
||||
|
||||
$scope.$on('$destroy', function() {
|
||||
destroyed = true;
|
||||
resetTimer();
|
||||
@@ -176,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) {
|
||||
@@ -217,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);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -255,7 +237,6 @@ angular.module('ui.bootstrap.carousel', [])
|
||||
function resetTransition(slides) {
|
||||
if (!slides.length) {
|
||||
$scope.$currentTransition = null;
|
||||
clearBufferedTransitions();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -280,9 +261,9 @@ angular.module('ui.bootstrap.carousel', [])
|
||||
.directive('uibCarousel', function() {
|
||||
return {
|
||||
transclude: true,
|
||||
replace: true,
|
||||
controller: 'UibCarouselController',
|
||||
controllerAs: 'carousel',
|
||||
restrict: 'A',
|
||||
templateUrl: function(element, attrs) {
|
||||
return attrs.templateUrl || 'uib/template/carousel/carousel.html';
|
||||
},
|
||||
@@ -296,11 +277,11 @@ angular.module('ui.bootstrap.carousel', [])
|
||||
};
|
||||
})
|
||||
|
||||
.directive('uibSlide', function() {
|
||||
.directive('uibSlide', ['$animate', function($animate) {
|
||||
return {
|
||||
require: '^uibCarousel',
|
||||
restrict: 'A',
|
||||
transclude: true,
|
||||
replace: true,
|
||||
templateUrl: function(element, attrs) {
|
||||
return attrs.templateUrl || 'uib/template/carousel/slide.html';
|
||||
},
|
||||
@@ -309,14 +290,19 @@ angular.module('ui.bootstrap.carousel', [])
|
||||
index: '=?'
|
||||
},
|
||||
link: function (scope, element, attrs, carouselCtrl) {
|
||||
element.addClass('item');
|
||||
carouselCtrl.addSlide(scope, element);
|
||||
//when the scope is destroyed then remove the slide from the current slides array
|
||||
scope.$on('$destroy', function() {
|
||||
carouselCtrl.removeSlide(scope);
|
||||
});
|
||||
|
||||
scope.$watch('active', function(active) {
|
||||
$animate[active ? 'addClass' : 'removeClass'](element, 'active');
|
||||
});
|
||||
}
|
||||
};
|
||||
})
|
||||
}])
|
||||
|
||||
.animation('.item', ['$animateCss',
|
||||
function($animateCss) {
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
<div ng-controller="CarouselDemoCtrl">
|
||||
<div style="height: 305px">
|
||||
<uib-carousel active="active" interval="myInterval" no-wrap="noWrapSlides">
|
||||
<uib-slide ng-repeat="slide in slides track by slide.id" index="slide.id">
|
||||
<div uib-carousel active="active" interval="myInterval" no-wrap="noWrapSlides">
|
||||
<div uib-slide ng-repeat="slide in slides track by slide.id" index="slide.id">
|
||||
<img ng-src="{{slide.image}}" style="margin:auto;">
|
||||
<div class="carousel-caption">
|
||||
<h4>Slide {{slide.id}}</h4>
|
||||
<p>{{slide.text}}</p>
|
||||
</div>
|
||||
</uib-slide>
|
||||
</uib-carousel>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
|
||||
@@ -8,7 +8,7 @@ angular.module('ui.bootstrap.demo').controller('CarouselDemoCtrl', function ($sc
|
||||
$scope.addSlide = function() {
|
||||
var newWidth = 600 + slides.length + 1;
|
||||
slides.push({
|
||||
image: 'http://lorempixel.com/' + newWidth + '/300',
|
||||
image: '//unsplash.it/' + newWidth + '/300',
|
||||
text: ['Nice image','Awesome photograph','That is so cool','I love that'][slides.length % 4],
|
||||
id: currIndex++
|
||||
});
|
||||
|
||||
@@ -1,17 +1,5 @@
|
||||
describe('carousel', function() {
|
||||
beforeEach(module('ui.bootstrap.carousel', function($compileProvider, $provide) {
|
||||
angular.forEach(['ngSwipeLeft', 'ngSwipeRight'], makeMock);
|
||||
function makeMock(name) {
|
||||
$provide.value(name + 'Directive', []); //remove existing directive if it exists
|
||||
$compileProvider.directive(name, function() {
|
||||
return function(scope, element, attr) {
|
||||
element.on(name, function() {
|
||||
scope.$apply(attr[name]);
|
||||
});
|
||||
};
|
||||
});
|
||||
}
|
||||
}));
|
||||
beforeEach(module('ui.bootstrap.carousel'));
|
||||
beforeEach(module('ngAnimateMock'));
|
||||
beforeEach(module('uib/template/carousel/carousel.html', 'uib/template/carousel/slide.html'));
|
||||
|
||||
@@ -36,11 +24,11 @@ describe('carousel', function() {
|
||||
{content: 'three', index: 2}
|
||||
];
|
||||
elm = $compile(
|
||||
'<uib-carousel active="active" interval="interval" no-transition="true" no-pause="nopause">' +
|
||||
'<uib-slide ng-repeat="slide in slides track by slide.index" index="slide.index">' +
|
||||
'<div uib-carousel active="active" interval="interval" no-transition="true" no-pause="nopause">' +
|
||||
'<div uib-slide ng-repeat="slide in slides track by slide.index" index="slide.index">' +
|
||||
'{{slide.content}}' +
|
||||
'</uib-slide>' +
|
||||
'</uib-carousel>'
|
||||
'</div>' +
|
||||
'</div>'
|
||||
)(scope);
|
||||
scope.interval = 5000;
|
||||
scope.nopause = undefined;
|
||||
@@ -60,19 +48,19 @@ describe('carousel', function() {
|
||||
it('should allow overriding of the carousel template', function() {
|
||||
$templateCache.put('foo/bar.html', '<div>foo</div>');
|
||||
|
||||
elm = $compile('<uib-carousel template-url="foo/bar.html"></uib-carousel>')(scope);
|
||||
elm = $compile('<div uib-carousel template-url="foo/bar.html"></div>')(scope);
|
||||
$rootScope.$digest();
|
||||
|
||||
expect(elm.html()).toBe('foo');
|
||||
expect(elm.html()).toBe('<div>foo</div>');
|
||||
});
|
||||
|
||||
it('should allow overriding of the slide template', function() {
|
||||
$templateCache.put('foo/bar.html', '<div class="slide">bar</div>');
|
||||
|
||||
elm = $compile(
|
||||
'<uib-carousel interval="interval" no-transition="true" no-pause="nopause">' +
|
||||
'<uib-slide template-url="foo/bar.html"></uib-slide>' +
|
||||
'</uib-carousel>'
|
||||
'<div uib-carousel interval="interval" no-transition="true" no-pause="nopause">' +
|
||||
'<div uib-slide template-url="foo/bar.html"></div>' +
|
||||
'</div>'
|
||||
)(scope);
|
||||
$rootScope.$digest();
|
||||
|
||||
@@ -101,11 +89,11 @@ describe('carousel', function() {
|
||||
|
||||
it('should stop cycling slides forward when noWrap is truthy', function () {
|
||||
elm = $compile(
|
||||
'<uib-carousel active="active" interval="interval" no-wrap="noWrap">' +
|
||||
'<uib-slide ng-repeat="slide in slides track by slide.index" index="slide.index">' +
|
||||
'<div uib-carousel active="active" interval="interval" no-wrap="noWrap">' +
|
||||
'<div uib-slide ng-repeat="slide in slides track by slide.index" index="slide.index">' +
|
||||
'{{slide.content}}' +
|
||||
'</uib-slide>' +
|
||||
'</uib-carousel>'
|
||||
'</div>' +
|
||||
'</div>'
|
||||
)(scope);
|
||||
|
||||
scope.noWrap = true;
|
||||
@@ -124,11 +112,11 @@ describe('carousel', function() {
|
||||
|
||||
it('should stop cycling slides backward when noWrap is truthy', function () {
|
||||
elm = $compile(
|
||||
'<uib-carousel active="active" interval="interval" no-wrap="noWrap">' +
|
||||
'<uib-slide ng-repeat="slide in slides track by slide.index" index="slide.index">' +
|
||||
'<div uib-carousel active="active" interval="interval" no-wrap="noWrap">' +
|
||||
'<div uib-slide ng-repeat="slide in slides track by slide.index" index="slide.index">' +
|
||||
'{{slide.content}}' +
|
||||
'</uib-slide>' +
|
||||
'</uib-carousel>'
|
||||
'</div>' +
|
||||
'</div>'
|
||||
)(scope);
|
||||
|
||||
scope.noWrap = true;
|
||||
@@ -147,11 +135,11 @@ describe('carousel', function() {
|
||||
scope.slides = [{active:false,content:'one'}];
|
||||
scope.$apply();
|
||||
elm = $compile(
|
||||
'<uib-carousel active="active" interval="interval" no-transition="true">' +
|
||||
'<uib-slide ng-repeat="slide in slides" index="$index">' +
|
||||
'<div uib-carousel active="active" interval="interval" no-transition="true">' +
|
||||
'<div uib-slide ng-repeat="slide in slides" index="$index">' +
|
||||
'{{slide.content}}' +
|
||||
'</uib-slide>' +
|
||||
'</uib-carousel>'
|
||||
'</div>' +
|
||||
'</div>'
|
||||
)(scope);
|
||||
var indicators = elm.find('ol.carousel-indicators > li');
|
||||
expect(indicators.length).toBe(0);
|
||||
@@ -228,20 +216,6 @@ describe('carousel', function() {
|
||||
testSlideActive(0);
|
||||
});
|
||||
|
||||
describe('swiping', function() {
|
||||
it('should go next on swipeLeft', function() {
|
||||
testSlideActive(0);
|
||||
elm.triggerHandler('ngSwipeLeft');
|
||||
testSlideActive(1);
|
||||
});
|
||||
|
||||
it('should go prev on swipeRight', function() {
|
||||
testSlideActive(0);
|
||||
elm.triggerHandler('ngSwipeRight');
|
||||
testSlideActive(2);
|
||||
});
|
||||
});
|
||||
|
||||
it('should select a slide when clicking on slide indicators', function () {
|
||||
var indicators = elm.find('ol.carousel-indicators > li');
|
||||
indicators.eq(1).click();
|
||||
@@ -269,7 +243,7 @@ describe('carousel', function() {
|
||||
});
|
||||
|
||||
it('should bind the content to slides', function() {
|
||||
var contents = elm.find('div.item');
|
||||
var contents = elm.find('div.item [ng-transclude]');
|
||||
|
||||
expect(contents.length).toBe(3);
|
||||
expect(contents.eq(0).text()).toBe('one');
|
||||
@@ -343,7 +317,7 @@ describe('carousel', function() {
|
||||
{content:'new3', index: 6}
|
||||
];
|
||||
scope.$apply();
|
||||
var contents = elm.find('div.item');
|
||||
var contents = elm.find('div.item [ng-transclude]');
|
||||
expect(contents.length).toBe(3);
|
||||
expect(contents.eq(0).text()).toBe('new1');
|
||||
expect(contents.eq(1).text()).toBe('new2');
|
||||
@@ -441,11 +415,11 @@ describe('carousel', function() {
|
||||
{content: 'three', id: 2}
|
||||
];
|
||||
elm = $compile(
|
||||
'<uib-carousel active="active" interval="interval" no-transition="true" no-pause="nopause">' +
|
||||
'<uib-slide ng-repeat="slide in slides | orderBy: \'id\' track by slide.id" index="slide.id">' +
|
||||
'<div uib-carousel active="active" interval="interval" no-transition="true" no-pause="nopause">' +
|
||||
'<div uib-slide ng-repeat="slide in slides | orderBy: \'id\' track by slide.id" index="slide.id">' +
|
||||
'{{slide.content}}' +
|
||||
'</uib-slide>' +
|
||||
'</uib-carousel>'
|
||||
'</div>' +
|
||||
'</div>'
|
||||
)(scope);
|
||||
scope.$apply();
|
||||
});
|
||||
@@ -465,7 +439,7 @@ describe('carousel', function() {
|
||||
scope.slides[1].id = 2;
|
||||
scope.slides[2].id = 1;
|
||||
scope.$apply();
|
||||
var contents = elm.find('div.item');
|
||||
var contents = elm.find('div.item [ng-transclude]');
|
||||
expect(contents.length).toBe(3);
|
||||
expect(contents.eq(0).text()).toBe('three');
|
||||
expect(contents.eq(1).text()).toBe('two');
|
||||
@@ -491,7 +465,7 @@ describe('carousel', function() {
|
||||
scope.slides[2].id = 4;
|
||||
scope.slides.push({content:'four', id: 5});
|
||||
scope.$apply();
|
||||
var contents = elm.find('div.item');
|
||||
var contents = elm.find('div.item [ng-transclude]');
|
||||
expect(contents.length).toBe(4);
|
||||
expect(contents.eq(0).text()).toBe('two');
|
||||
expect(contents.eq(1).text()).toBe('one');
|
||||
@@ -503,7 +477,7 @@ describe('carousel', function() {
|
||||
testSlideActive(1);
|
||||
scope.slides.splice(1, 1);
|
||||
scope.$apply();
|
||||
var contents = elm.find('div.item');
|
||||
var contents = elm.find('div.item [ng-transclude]');
|
||||
expect(contents.length).toBe(2);
|
||||
expect(contents.eq(0).text()).toBe('three');
|
||||
expect(contents.eq(1).text()).toBe('one');
|
||||
@@ -583,7 +557,7 @@ describe('carousel', function() {
|
||||
$templateCache.put('uib/template/carousel/carousel.html', '<div>{{carousel.text}}</div>');
|
||||
|
||||
var scope = $rootScope.$new();
|
||||
var elm = $compile('<uib-carousel interval="bar" no-transition="false" no-pause="true"></uib-carousel>')(scope);
|
||||
var elm = $compile('<div uib-carousel interval="bar" no-transition="false" no-pause="true"></div>')(scope);
|
||||
$rootScope.$digest();
|
||||
|
||||
var ctrl = elm.controller('uibCarousel');
|
||||
@@ -593,7 +567,7 @@ describe('carousel', function() {
|
||||
ctrl.text = 'foo';
|
||||
$rootScope.$digest();
|
||||
|
||||
expect(elm.html()).toBe('foo');
|
||||
expect(elm.html()).toBe('<div class="ng-binding">foo</div>');
|
||||
}));
|
||||
});
|
||||
|
||||
@@ -605,11 +579,11 @@ describe('carousel', function() {
|
||||
{active:false,content:'three'}
|
||||
];
|
||||
var elm = $compile(
|
||||
'<uib-carousel active="active" interval="interval" no-transition="true" no-pause="nopause">' +
|
||||
'<uib-slide ng-repeat="slide in slides" index="$index" actual="slide">' +
|
||||
'<div uib-carousel active="active" interval="interval" no-transition="true" no-pause="nopause">' +
|
||||
'<div uib-slide ng-repeat="slide in slides" index="$index" actual="slide">' +
|
||||
'{{slide.content}}' +
|
||||
'</uib-slide>' +
|
||||
'</uib-carousel>'
|
||||
'</div>' +
|
||||
'</div>'
|
||||
)(scope);
|
||||
$rootScope.$digest();
|
||||
|
||||
|
||||
+53
-21
@@ -5,16 +5,42 @@ angular.module('ui.bootstrap.collapse', [])
|
||||
return {
|
||||
link: function(scope, element, attrs) {
|
||||
var expandingExpr = $parse(attrs.expanding),
|
||||
expandedExpr = $parse(attrs.expanded),
|
||||
collapsingExpr = $parse(attrs.collapsing),
|
||||
collapsedExpr = $parse(attrs.collapsed);
|
||||
expandedExpr = $parse(attrs.expanded),
|
||||
collapsingExpr = $parse(attrs.collapsing),
|
||||
collapsedExpr = $parse(attrs.collapsed),
|
||||
horizontal = false,
|
||||
css = {},
|
||||
cssTo = {};
|
||||
|
||||
if (!scope.$eval(attrs.uibCollapse)) {
|
||||
element.addClass('in')
|
||||
.addClass('collapse')
|
||||
.attr('aria-expanded', true)
|
||||
.attr('aria-hidden', false)
|
||||
.css({height: 'auto'});
|
||||
init();
|
||||
|
||||
function init() {
|
||||
horizontal = !!('horizontal' in attrs);
|
||||
if (horizontal) {
|
||||
css = {
|
||||
width: ''
|
||||
};
|
||||
cssTo = {width: '0'};
|
||||
} else {
|
||||
css = {
|
||||
height: ''
|
||||
};
|
||||
cssTo = {height: '0'};
|
||||
}
|
||||
if (!scope.$eval(attrs.uibCollapse)) {
|
||||
element.addClass('in')
|
||||
.addClass('collapse')
|
||||
.attr('aria-expanded', true)
|
||||
.attr('aria-hidden', false)
|
||||
.css(css);
|
||||
}
|
||||
}
|
||||
|
||||
function getScrollFromElement(element) {
|
||||
if (horizontal) {
|
||||
return {width: element.scrollWidth + 'px'};
|
||||
}
|
||||
return {height: element.scrollHeight + 'px'};
|
||||
}
|
||||
|
||||
function expand() {
|
||||
@@ -33,20 +59,26 @@ angular.module('ui.bootstrap.collapse', [])
|
||||
$animateCss(element, {
|
||||
addClass: 'in',
|
||||
easing: 'ease',
|
||||
to: { height: element[0].scrollHeight + 'px' }
|
||||
css: {
|
||||
overflow: 'hidden'
|
||||
},
|
||||
to: getScrollFromElement(element[0])
|
||||
}).start()['finally'](expandDone);
|
||||
} else {
|
||||
$animate.addClass(element, 'in', {
|
||||
to: { height: element[0].scrollHeight + 'px' }
|
||||
css: {
|
||||
overflow: 'hidden'
|
||||
},
|
||||
to: getScrollFromElement(element[0])
|
||||
}).then(expandDone);
|
||||
}
|
||||
});
|
||||
}, angular.noop);
|
||||
}
|
||||
|
||||
function expandDone() {
|
||||
element.removeClass('collapsing')
|
||||
.addClass('collapse')
|
||||
.css({height: 'auto'});
|
||||
.css(css);
|
||||
expandedExpr(scope);
|
||||
}
|
||||
|
||||
@@ -58,10 +90,10 @@ angular.module('ui.bootstrap.collapse', [])
|
||||
$q.resolve(collapsingExpr(scope))
|
||||
.then(function() {
|
||||
element
|
||||
// IMPORTANT: The height must be set before adding "collapsing" class.
|
||||
// Otherwise, the browser attempts to animate from height 0 (in
|
||||
// collapsing class) to the given height here.
|
||||
.css({height: element[0].scrollHeight + 'px'})
|
||||
// IMPORTANT: The width must be set before adding "collapsing" class.
|
||||
// Otherwise, the browser attempts to animate from width 0 (in
|
||||
// collapsing class) to the given width here.
|
||||
.css(getScrollFromElement(element[0]))
|
||||
// initially all panel collapse have the collapse class, this removal
|
||||
// prevents the animation from jumping to collapsed state
|
||||
.removeClass('collapse')
|
||||
@@ -72,18 +104,18 @@ angular.module('ui.bootstrap.collapse', [])
|
||||
if ($animateCss) {
|
||||
$animateCss(element, {
|
||||
removeClass: 'in',
|
||||
to: {height: '0'}
|
||||
to: cssTo
|
||||
}).start()['finally'](collapseDone);
|
||||
} else {
|
||||
$animate.removeClass(element, 'in', {
|
||||
to: {height: '0'}
|
||||
to: cssTo
|
||||
}).then(collapseDone);
|
||||
}
|
||||
});
|
||||
}, angular.noop);
|
||||
}
|
||||
|
||||
function collapseDone() {
|
||||
element.css({height: '0'}); // Required so that collapse works when animation is disabled
|
||||
element.css(cssTo); // Required so that collapse works when animation is disabled
|
||||
element.removeClass('collapsing')
|
||||
.addClass('collapse');
|
||||
collapsedExpr(scope);
|
||||
|
||||
@@ -1,7 +1,40 @@
|
||||
<style>
|
||||
.horizontal-collapse {
|
||||
height: 70px;
|
||||
}
|
||||
.navbar-collapse.in {
|
||||
overflow-y: hidden;
|
||||
}
|
||||
</style>
|
||||
<div ng-controller="CollapseDemoCtrl">
|
||||
<button type="button" class="btn btn-default" ng-click="isCollapsed = !isCollapsed">Toggle collapse</button>
|
||||
<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">
|
||||
<div class="well well-lg">Some content</div>
|
||||
</div>
|
||||
|
||||
<button type="button" class="btn btn-default" ng-click="isCollapsedHorizontal = !isCollapsedHorizontal">Toggle collapse Horizontally</button>
|
||||
<hr>
|
||||
<div class="horizontal-collapse" uib-collapse="isCollapsedHorizontal" horizontal>
|
||||
<div class="well well-lg">Some content</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
angular.module('ui.bootstrap.demo').controller('CollapseDemoCtrl', function ($scope) {
|
||||
$scope.isNavCollapsed = true;
|
||||
$scope.isCollapsed = false;
|
||||
$scope.isCollapsedHorizontal = false;
|
||||
});
|
||||
|
||||
@@ -28,3 +28,10 @@
|
||||
_(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.
|
||||
|
||||
@@ -0,0 +1,255 @@
|
||||
describe('collapse directive', function() {
|
||||
var elementH, compileFnH, scope, $compile, $animate, $q;
|
||||
|
||||
beforeEach(module('ui.bootstrap.collapse'));
|
||||
beforeEach(module('ngAnimateMock'));
|
||||
beforeEach(inject(function(_$rootScope_, _$compile_, _$animate_, _$q_) {
|
||||
scope = _$rootScope_;
|
||||
$compile = _$compile_;
|
||||
$animate = _$animate_;
|
||||
$q = _$q_;
|
||||
}));
|
||||
|
||||
beforeEach(function() {
|
||||
elementH = angular.element(
|
||||
'<div uib-collapse="isCollapsed" '
|
||||
+ 'expanding="expanding()" '
|
||||
+ 'expanded="expanded()" '
|
||||
+ 'collapsing="collapsing()" '
|
||||
+ 'collapsed="collapsed()" '
|
||||
+ 'horizontal>'
|
||||
+ 'Some Content</div>');
|
||||
compileFnH = $compile(elementH);
|
||||
angular.element(document.body).append(elementH);
|
||||
});
|
||||
|
||||
afterEach(function() {
|
||||
elementH.remove();
|
||||
});
|
||||
|
||||
function initCallbacks() {
|
||||
scope.collapsing = jasmine.createSpy('scope.collapsing');
|
||||
scope.collapsed = jasmine.createSpy('scope.collapsed');
|
||||
scope.expanding = jasmine.createSpy('scope.expanding');
|
||||
scope.expanded = jasmine.createSpy('scope.expanded');
|
||||
}
|
||||
|
||||
function assertCallbacks(expected) {
|
||||
['collapsing', 'collapsed', 'expanding', 'expanded'].forEach(function(cbName) {
|
||||
if (expected[cbName]) {
|
||||
expect(scope[cbName]).toHaveBeenCalled();
|
||||
} else {
|
||||
expect(scope[cbName]).not.toHaveBeenCalled();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
it('should be hidden on initialization if isCollapsed = true', function() {
|
||||
initCallbacks();
|
||||
scope.isCollapsed = true;
|
||||
compileFnH(scope);
|
||||
scope.$digest();
|
||||
expect(elementH.width()).toBe(0);
|
||||
assertCallbacks({ collapsed: true });
|
||||
});
|
||||
|
||||
it('should not trigger any animation on initialization if isCollapsed = true', function() {
|
||||
var wrapperFn = function() {
|
||||
$animate.flush();
|
||||
};
|
||||
|
||||
scope.isCollapsed = true;
|
||||
compileFnH(scope);
|
||||
scope.$digest();
|
||||
|
||||
expect(wrapperFn).toThrowError(/No pending animations ready to be closed or flushed/);
|
||||
});
|
||||
|
||||
it('should collapse if isCollapsed = true on subsequent use', function() {
|
||||
scope.isCollapsed = false;
|
||||
compileFnH(scope);
|
||||
scope.$digest();
|
||||
initCallbacks();
|
||||
scope.isCollapsed = true;
|
||||
scope.$digest();
|
||||
$animate.flush();
|
||||
expect(elementH.width()).toBe(0);
|
||||
assertCallbacks({ collapsing: true, collapsed: true });
|
||||
});
|
||||
|
||||
it('should show after toggled from collapsed', function() {
|
||||
initCallbacks();
|
||||
scope.isCollapsed = true;
|
||||
compileFnH(scope);
|
||||
scope.$digest();
|
||||
expect(elementH.width()).toBe(0);
|
||||
assertCallbacks({ collapsed: true });
|
||||
scope.collapsed.calls.reset();
|
||||
|
||||
scope.isCollapsed = false;
|
||||
scope.$digest();
|
||||
$animate.flush();
|
||||
expect(elementH.width()).not.toBe(0);
|
||||
assertCallbacks({ expanding: true, expanded: true });
|
||||
});
|
||||
|
||||
it('should not trigger any animation on initialization if isCollapsed = false', function() {
|
||||
var wrapperFn = function() {
|
||||
$animate.flush();
|
||||
};
|
||||
|
||||
scope.isCollapsed = false;
|
||||
compileFnH(scope);
|
||||
scope.$digest();
|
||||
|
||||
expect(wrapperFn).toThrowError(/No pending animations ready to be closed or flushed/);
|
||||
});
|
||||
|
||||
it('should expand if isCollapsed = false on subsequent use', function() {
|
||||
scope.isCollapsed = false;
|
||||
compileFnH(scope);
|
||||
scope.$digest();
|
||||
scope.isCollapsed = true;
|
||||
scope.$digest();
|
||||
$animate.flush();
|
||||
initCallbacks();
|
||||
scope.isCollapsed = false;
|
||||
scope.$digest();
|
||||
$animate.flush();
|
||||
expect(elementH.width()).not.toBe(0);
|
||||
assertCallbacks({ expanding: true, expanded: true });
|
||||
});
|
||||
|
||||
it('should collapse if isCollapsed = true on subsequent uses', function() {
|
||||
scope.isCollapsed = false;
|
||||
compileFnH(scope);
|
||||
scope.$digest();
|
||||
scope.isCollapsed = true;
|
||||
scope.$digest();
|
||||
$animate.flush();
|
||||
scope.isCollapsed = false;
|
||||
scope.$digest();
|
||||
$animate.flush();
|
||||
initCallbacks();
|
||||
scope.isCollapsed = true;
|
||||
scope.$digest();
|
||||
$animate.flush();
|
||||
expect(elementH.width()).toBe(0);
|
||||
assertCallbacks({ collapsing: true, collapsed: true });
|
||||
});
|
||||
|
||||
it('should change aria-expanded attribute', function() {
|
||||
scope.isCollapsed = false;
|
||||
compileFnH(scope);
|
||||
scope.$digest();
|
||||
expect(elementH.attr('aria-expanded')).toBe('true');
|
||||
|
||||
scope.isCollapsed = true;
|
||||
scope.$digest();
|
||||
$animate.flush();
|
||||
expect(elementH.attr('aria-expanded')).toBe('false');
|
||||
});
|
||||
|
||||
it('should change aria-hidden attribute', function() {
|
||||
scope.isCollapsed = false;
|
||||
compileFnH(scope);
|
||||
scope.$digest();
|
||||
expect(elementH.attr('aria-hidden')).toBe('false');
|
||||
|
||||
scope.isCollapsed = true;
|
||||
scope.$digest();
|
||||
$animate.flush();
|
||||
expect(elementH.attr('aria-hidden')).toBe('true');
|
||||
});
|
||||
|
||||
describe('expanding callback returning a promise', function() {
|
||||
var defer, collapsedWidth;
|
||||
|
||||
beforeEach(function() {
|
||||
defer = $q.defer();
|
||||
|
||||
scope.isCollapsed = true;
|
||||
scope.expanding = function() {
|
||||
return defer.promise;
|
||||
};
|
||||
compileFnH(scope);
|
||||
scope.$digest();
|
||||
collapsedWidth = elementH.width();
|
||||
|
||||
// set flag to expand ...
|
||||
scope.isCollapsed = false;
|
||||
scope.$digest();
|
||||
|
||||
// ... shouldn't expand yet ...
|
||||
expect(elementH.attr('aria-expanded')).not.toBe('true');
|
||||
expect(elementH.width()).toBe(collapsedWidth);
|
||||
});
|
||||
|
||||
it('should wait for it to resolve before animating', function() {
|
||||
defer.resolve();
|
||||
|
||||
// should now expand
|
||||
scope.$digest();
|
||||
$animate.flush();
|
||||
|
||||
expect(elementH.attr('aria-expanded')).toBe('true');
|
||||
expect(elementH.width()).toBeGreaterThan(collapsedWidth);
|
||||
});
|
||||
|
||||
it('should not animate if it rejects', function() {
|
||||
defer.reject();
|
||||
|
||||
// should NOT expand
|
||||
scope.$digest();
|
||||
|
||||
expect(elementH.attr('aria-expanded')).not.toBe('true');
|
||||
expect(elementH.width()).toBe(collapsedWidth);
|
||||
});
|
||||
});
|
||||
|
||||
describe('collapsing callback returning a promise', function() {
|
||||
var defer, expandedWidth;
|
||||
|
||||
beforeEach(function() {
|
||||
defer = $q.defer();
|
||||
scope.isCollapsed = false;
|
||||
scope.collapsing = function() {
|
||||
return defer.promise;
|
||||
};
|
||||
compileFnH(scope);
|
||||
scope.$digest();
|
||||
|
||||
expandedWidth = elementH.width();
|
||||
|
||||
// set flag to collapse ...
|
||||
scope.isCollapsed = true;
|
||||
scope.$digest();
|
||||
|
||||
// ... but it shouldn't collapse yet ...
|
||||
expect(elementH.attr('aria-expanded')).not.toBe('false');
|
||||
expect(elementH.width()).toBe(expandedWidth);
|
||||
});
|
||||
|
||||
it('should wait for it to resolve before animating', function() {
|
||||
defer.resolve();
|
||||
|
||||
// should now collapse
|
||||
scope.$digest();
|
||||
$animate.flush();
|
||||
|
||||
expect(elementH.attr('aria-expanded')).toBe('false');
|
||||
expect(elementH.width()).toBeLessThan(expandedWidth);
|
||||
});
|
||||
|
||||
it('should not animate if it rejects', function() {
|
||||
defer.reject();
|
||||
|
||||
// should NOT collapse
|
||||
scope.$digest();
|
||||
|
||||
expect(elementH.attr('aria-expanded')).not.toBe('false');
|
||||
expect(elementH.width()).toBe(expandedWidth);
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
@@ -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,11 +230,37 @@ 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 createParser(format, func) {
|
||||
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('');
|
||||
|
||||
// check for literal values
|
||||
@@ -283,7 +309,7 @@ angular.module('ui.bootstrap.dateparser', [])
|
||||
map.push({
|
||||
index: index,
|
||||
key: data.key,
|
||||
apply: data[func],
|
||||
apply: data.apply,
|
||||
matcher: data.regex
|
||||
});
|
||||
}
|
||||
@@ -295,6 +321,70 @@ angular.module('ui.bootstrap.dateparser', [])
|
||||
};
|
||||
}
|
||||
|
||||
function createFormatter(format) {
|
||||
var formatters = [];
|
||||
var i = 0;
|
||||
var formatter, literalIdx;
|
||||
while (i < format.length) {
|
||||
if (angular.isNumber(literalIdx)) {
|
||||
if (format.charAt(i) === '\'') {
|
||||
if (i + 1 >= format.length || format.charAt(i + 1) !== '\'') {
|
||||
formatters.push(constructLiteralFormatter(format, literalIdx, i));
|
||||
literalIdx = null;
|
||||
}
|
||||
} else if (i === format.length) {
|
||||
while (literalIdx < format.length) {
|
||||
formatter = constructFormatterFromIdx(format, literalIdx);
|
||||
formatters.push(formatter);
|
||||
literalIdx = formatter.endIdx;
|
||||
}
|
||||
}
|
||||
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (format.charAt(i) === '\'') {
|
||||
literalIdx = i;
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
|
||||
formatter = constructFormatterFromIdx(format, i);
|
||||
|
||||
formatters.push(formatter.parser);
|
||||
i = formatter.endIdx;
|
||||
}
|
||||
|
||||
return formatters;
|
||||
}
|
||||
|
||||
function constructLiteralFormatter(format, literalIdx, endIdx) {
|
||||
return function() {
|
||||
return format.substr(literalIdx + 1, endIdx - literalIdx - 1);
|
||||
};
|
||||
}
|
||||
|
||||
function constructFormatterFromIdx(format, i) {
|
||||
var currentPosStr = format.substr(i);
|
||||
for (var j = 0; j < formatCodeToRegex.length; j++) {
|
||||
if (new RegExp('^' + formatCodeToRegex[j].key).test(currentPosStr)) {
|
||||
var data = formatCodeToRegex[j];
|
||||
return {
|
||||
endIdx: i + data.key.length,
|
||||
parser: data.formatter
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
endIdx: i + 1,
|
||||
parser: function() {
|
||||
return currentPosStr.charAt(0);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
this.filter = function(date, format) {
|
||||
if (!angular.isDate(date) || isNaN(date) || !format) {
|
||||
return '';
|
||||
@@ -307,28 +397,13 @@ angular.module('ui.bootstrap.dateparser', [])
|
||||
}
|
||||
|
||||
if (!this.formatters[format]) {
|
||||
this.formatters[format] = createParser(format, 'formatter');
|
||||
this.formatters[format] = createFormatter(format);
|
||||
}
|
||||
|
||||
var parser = this.formatters[format],
|
||||
map = parser.map;
|
||||
var formatters = this.formatters[format];
|
||||
|
||||
var _format = format;
|
||||
|
||||
return map.reduce(function(str, mapper, i) {
|
||||
var match = _format.match(new RegExp('(.*)' + mapper.key));
|
||||
if (match && angular.isString(match[1])) {
|
||||
str += match[1];
|
||||
_format = _format.replace(match[1] + mapper.key, '');
|
||||
}
|
||||
|
||||
var endStr = i === map.length - 1 ? _format : '';
|
||||
|
||||
if (mapper.apply) {
|
||||
return str + mapper.apply.call(null, date) + endStr;
|
||||
}
|
||||
|
||||
return str + endStr;
|
||||
return formatters.reduce(function(str, formatter) {
|
||||
return str + formatter(date);
|
||||
}, '');
|
||||
};
|
||||
|
||||
|
||||
@@ -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');
|
||||
@@ -565,27 +575,27 @@ describe('date parser', function() {
|
||||
});
|
||||
|
||||
describe('with value literals', function() {
|
||||
// describe('filter', function() {
|
||||
// it('should work with multiple literals', function() {
|
||||
// expect(dateParser.filter(new Date(2013, 0, 29), 'd \'de\' MMMM \'de\' y')).toEqual('29 de January de 2013');
|
||||
// });
|
||||
//
|
||||
// it('should work with escaped single quote', function() {
|
||||
// expect(dateParser.filter(new Date(2015, 2, 22, 12), 'd.MMMM.yy h \'o\'\'clock\'')).toEqual('22.March.15 12 o\'clock');
|
||||
// });
|
||||
//
|
||||
// it('should work with only a single quote', function() {
|
||||
// expect(dateParser.filter(new Date(2015, 2, 22), 'd.MMMM.yy \'\'\'')).toEqual('22.March.15 \'');
|
||||
// });
|
||||
//
|
||||
// it('should work with trailing literal', function() {
|
||||
// expect(dateParser.filter(new Date(2013, 0, 1), '\'year\' y')).toEqual('year 2013');
|
||||
// });
|
||||
//
|
||||
// it('should work without whitespace', function() {
|
||||
// expect(dateParser.filter(new Date(2013, 0, 1), '\'year:\'y')).toEqual('year:2013');
|
||||
// });
|
||||
// });
|
||||
describe('filter', function() {
|
||||
it('should work with multiple literals', function() {
|
||||
expect(dateParser.filter(new Date(2013, 0, 29), 'd \'de\' MMMM \'de\' y')).toEqual('29 de January de 2013');
|
||||
});
|
||||
|
||||
it('should work with escaped single quote', function() {
|
||||
expect(dateParser.filter(new Date(2015, 2, 22, 12), 'd.MMMM.yy h \'o\'\'clock\'')).toEqual('22.March.15 12 o\'clock');
|
||||
});
|
||||
|
||||
it('should work with only a single quote', function() {
|
||||
expect(dateParser.filter(new Date(2015, 2, 22), 'd.MMMM.yy \'\'\'')).toEqual('22.March.15 \'');
|
||||
});
|
||||
|
||||
it('should work with trailing literal', function() {
|
||||
expect(dateParser.filter(new Date(2013, 0, 1), '\'year\' y')).toEqual('year 2013');
|
||||
});
|
||||
|
||||
it('should work without whitespace', function() {
|
||||
expect(dateParser.filter(new Date(2013, 0, 1), '\'year:\'y')).toEqual('year:2013');
|
||||
});
|
||||
});
|
||||
|
||||
describe('parse', function() {
|
||||
it('should work with multiple literals', function() {
|
||||
@@ -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);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -16,6 +16,7 @@ angular.module('ui.bootstrap.datepicker', ['ui.bootstrap.dateparser', 'ui.bootst
|
||||
maxMode: 'year',
|
||||
minDate: null,
|
||||
minMode: 'day',
|
||||
monthColumns: 3,
|
||||
ngModelOptions: {},
|
||||
shortcutPropagation: false,
|
||||
showWeeks: true,
|
||||
@@ -23,13 +24,15 @@ angular.module('ui.bootstrap.datepicker', ['ui.bootstrap.dateparser', 'ui.bootst
|
||||
yearRows: 4
|
||||
})
|
||||
|
||||
.controller('UibDatepickerController', ['$scope', '$attrs', '$parse', '$interpolate', '$locale', '$log', 'dateFilter', 'uibDatepickerConfig', '$datepickerLiteralWarning', '$datepickerSuppressError', 'uibDateParser',
|
||||
function($scope, $attrs, $parse, $interpolate, $locale, $log, dateFilter, datepickerConfig, $datepickerLiteralWarning, $datepickerSuppressError, dateParser) {
|
||||
.controller('UibDatepickerController', ['$scope', '$element', '$attrs', '$parse', '$interpolate', '$locale', '$log', 'dateFilter', 'uibDatepickerConfig', '$datepickerLiteralWarning', '$datepickerSuppressError', 'uibDateParser',
|
||||
function($scope, $element, $attrs, $parse, $interpolate, $locale, $log, dateFilter, datepickerConfig, $datepickerLiteralWarning, $datepickerSuppressError, dateParser) {
|
||||
var self = this,
|
||||
ngModelCtrl = { $setViewValue: angular.noop }, // nullModelCtrl;
|
||||
ngModelOptions = {},
|
||||
watchListeners = [],
|
||||
optionsUsed = !!$attrs.datepickerOptions;
|
||||
watchListeners = [];
|
||||
|
||||
$element.addClass('uib-datepicker');
|
||||
$attrs.$set('role', 'application');
|
||||
|
||||
if (!$scope.datepickerOptions) {
|
||||
$scope.datepickerOptions = {};
|
||||
@@ -52,6 +55,7 @@ angular.module('ui.bootstrap.datepicker', ['ui.bootstrap.dateparser', 'ui.bootst
|
||||
'maxMode',
|
||||
'minDate',
|
||||
'minMode',
|
||||
'monthColumns',
|
||||
'showWeeks',
|
||||
'shortcutPropagation',
|
||||
'startingDay',
|
||||
@@ -77,6 +81,7 @@ angular.module('ui.bootstrap.datepicker', ['ui.bootstrap.dateparser', 'ui.bootst
|
||||
$interpolate($scope.datepickerOptions[key])($scope.$parent) :
|
||||
datepickerConfig[key];
|
||||
break;
|
||||
case 'monthColumns':
|
||||
case 'showWeeks':
|
||||
case 'shortcutPropagation':
|
||||
case 'yearColumns':
|
||||
@@ -99,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');
|
||||
@@ -109,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;
|
||||
}
|
||||
|
||||
@@ -121,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];
|
||||
@@ -156,12 +161,13 @@ angular.module('ui.bootstrap.datepicker', ['ui.bootstrap.dateparser', 'ui.bootst
|
||||
|
||||
this.init = function(ngModelCtrl_) {
|
||||
ngModelCtrl = ngModelCtrl_;
|
||||
ngModelOptions = ngModelCtrl_.$options || 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();
|
||||
}
|
||||
});
|
||||
@@ -171,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();
|
||||
@@ -185,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');
|
||||
}
|
||||
@@ -202,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));
|
||||
}
|
||||
@@ -210,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,
|
||||
@@ -258,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 {
|
||||
@@ -328,6 +334,12 @@ angular.module('ui.bootstrap.datepicker', ['ui.bootstrap.dateparser', 'ui.bootst
|
||||
}
|
||||
};
|
||||
|
||||
$element.on('keydown', function(evt) {
|
||||
$scope.$apply(function() {
|
||||
$scope.keydown(evt);
|
||||
});
|
||||
});
|
||||
|
||||
$scope.$on('$destroy', function() {
|
||||
//Clear all watch listeners on destroy
|
||||
while (watchListeners.length) {
|
||||
@@ -339,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) {
|
||||
@@ -478,7 +521,8 @@ angular.module('ui.bootstrap.datepicker', ['ui.bootstrap.dateparser', 'ui.bootst
|
||||
}
|
||||
|
||||
scope.title = dateFilter(this.activeDate, this.formatMonthTitle);
|
||||
scope.rows = this.split(months, 3);
|
||||
scope.rows = this.split(months, this.monthColumns);
|
||||
scope.yearHeaderColspan = this.monthColumns > 3 ? this.monthColumns - 2 : 1;
|
||||
};
|
||||
|
||||
this.compare = function(date1, date2) {
|
||||
@@ -495,11 +539,11 @@ angular.module('ui.bootstrap.datepicker', ['ui.bootstrap.dateparser', 'ui.bootst
|
||||
if (key === 'left') {
|
||||
date = date - 1;
|
||||
} else if (key === 'up') {
|
||||
date = date - 3;
|
||||
date = date - this.monthColumns;
|
||||
} else if (key === 'right') {
|
||||
date = date + 1;
|
||||
} else if (key === 'down') {
|
||||
date = date + 3;
|
||||
date = date + this.monthColumns;
|
||||
} else if (key === 'pageup' || key === 'pagedown') {
|
||||
var year = this.activeDate.getFullYear() + (key === 'pageup' ? - 1 : 1);
|
||||
this.activeDate.setFullYear(year);
|
||||
@@ -570,7 +614,6 @@ angular.module('ui.bootstrap.datepicker', ['ui.bootstrap.dateparser', 'ui.bootst
|
||||
|
||||
.directive('uibDatepicker', function() {
|
||||
return {
|
||||
replace: true,
|
||||
templateUrl: function(element, attrs) {
|
||||
return attrs.templateUrl || 'uib/template/datepicker/datepicker.html';
|
||||
},
|
||||
@@ -578,6 +621,7 @@ angular.module('ui.bootstrap.datepicker', ['ui.bootstrap.dateparser', 'ui.bootst
|
||||
datepickerOptions: '=?'
|
||||
},
|
||||
require: ['uibDatepicker', '^ngModel'],
|
||||
restrict: 'A',
|
||||
controller: 'UibDatepickerController',
|
||||
controllerAs: 'datepicker',
|
||||
link: function(scope, element, attrs, ctrls) {
|
||||
@@ -590,11 +634,11 @@ angular.module('ui.bootstrap.datepicker', ['ui.bootstrap.dateparser', 'ui.bootst
|
||||
|
||||
.directive('uibDaypicker', function() {
|
||||
return {
|
||||
replace: true,
|
||||
templateUrl: function(element, attrs) {
|
||||
return attrs.templateUrl || 'uib/template/datepicker/day.html';
|
||||
},
|
||||
require: ['^uibDatepicker', 'uibDaypicker'],
|
||||
restrict: 'A',
|
||||
controller: 'UibDaypickerController',
|
||||
link: function(scope, element, attrs, ctrls) {
|
||||
var datepickerCtrl = ctrls[0],
|
||||
@@ -607,11 +651,11 @@ angular.module('ui.bootstrap.datepicker', ['ui.bootstrap.dateparser', 'ui.bootst
|
||||
|
||||
.directive('uibMonthpicker', function() {
|
||||
return {
|
||||
replace: true,
|
||||
templateUrl: function(element, attrs) {
|
||||
return attrs.templateUrl || 'uib/template/datepicker/month.html';
|
||||
},
|
||||
require: ['^uibDatepicker', 'uibMonthpicker'],
|
||||
restrict: 'A',
|
||||
controller: 'UibMonthpickerController',
|
||||
link: function(scope, element, attrs, ctrls) {
|
||||
var datepickerCtrl = ctrls[0],
|
||||
@@ -624,11 +668,11 @@ angular.module('ui.bootstrap.datepicker', ['ui.bootstrap.dateparser', 'ui.bootst
|
||||
|
||||
.directive('uibYearpicker', function() {
|
||||
return {
|
||||
replace: true,
|
||||
templateUrl: function(element, attrs) {
|
||||
return attrs.templateUrl || 'uib/template/datepicker/year.html';
|
||||
},
|
||||
require: ['^uibDatepicker', 'uibYearpicker'],
|
||||
restrict: 'A',
|
||||
controller: 'UibYearpickerController',
|
||||
link: function(scope, element, attrs, ctrls) {
|
||||
var ctrl = ctrls[0];
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
|
||||
<h4>Inline</h4>
|
||||
<div style="display:inline-block; min-height:290px;">
|
||||
<uib-datepicker ng-model="dt" class="well well-sm" datepicker-options="options"></uib-datepicker>
|
||||
<div uib-datepicker ng-model="dt" class="well well-sm" datepicker-options="options"></div>
|
||||
</div>
|
||||
|
||||
<hr />
|
||||
|
||||
@@ -104,6 +104,16 @@ Apart from the previous settings, to configure the uib-datepicker you need to cr
|
||||
_(Default: `day`)_ -
|
||||
Sets a lower limit for mode.
|
||||
|
||||
* `monthColumns`
|
||||
<small class="badge">C</small>
|
||||
_(Default: `3`)_ -
|
||||
Number of columns displayed in month selection.
|
||||
|
||||
* `ngModelOptions`
|
||||
<small class="badge">C</small>
|
||||
_(Default: `null`)_ -
|
||||
Sets `ngModelOptions` for datepicker. This can be overridden through attribute usage
|
||||
|
||||
* `shortcutPropagation`
|
||||
<small class="badge">C</small>
|
||||
_(Default: `false`)_ -
|
||||
@@ -118,7 +128,7 @@ Apart from the previous settings, to configure the uib-datepicker you need to cr
|
||||
<small class="badge">C</small>
|
||||
*(Default: `$locale.DATETIME_FORMATS.FIRSTDAYOFWEEK`)* -
|
||||
Starting day of the week from 0-6 (0=Sunday, ..., 6=Saturday).
|
||||
|
||||
|
||||
* `yearRows`
|
||||
<small class="badge">C</small>
|
||||
_(Default: `4`)_ -
|
||||
|
||||
@@ -26,8 +26,12 @@ describe('datepicker', function() {
|
||||
});
|
||||
}));
|
||||
|
||||
function getTitleCell() {
|
||||
return element.find('th').eq(1);
|
||||
}
|
||||
|
||||
function getTitleButton() {
|
||||
return element.find('th').eq(1).find('button').first();
|
||||
return getTitleCell().find('button').first();
|
||||
}
|
||||
|
||||
function getTitle() {
|
||||
@@ -155,7 +159,7 @@ describe('datepicker', function() {
|
||||
$scope.options = {
|
||||
minDate: '1984-01-01'
|
||||
};
|
||||
element = $compile('<uib-datepicker ng-model="locals.date" datepicker-options="options"></uib-datepicker>')($scope);
|
||||
element = $compile('<div uib-datepicker ng-model="locals.date" datepicker-options="options"></div>')($scope);
|
||||
$scope.$digest();
|
||||
|
||||
expect($log.warn).toHaveBeenCalledWith('Literal date support has been deprecated, please switch to date object usage');
|
||||
@@ -175,7 +179,7 @@ describe('datepicker', function() {
|
||||
$scope.options = {
|
||||
minDate: '1984-01-01'
|
||||
};
|
||||
element = $compile('<uib-datepicker ng-model="locals.date" datepicker-options="options"></uib-datepicker>')($scope);
|
||||
element = $compile('<div uib-datepicker ng-model="locals.date" datepicker-options="options"></div>')($scope);
|
||||
$scope.$digest();
|
||||
|
||||
expect($log.warn).not.toHaveBeenCalled();
|
||||
@@ -192,7 +196,7 @@ describe('datepicker', function() {
|
||||
$scope.options = {
|
||||
maxDate: '1984-01-01'
|
||||
};
|
||||
element = $compile('<uib-datepicker ng-model="locals.date" datepicker-options="options"></uib-datepicker>')($scope);
|
||||
element = $compile('<div uib-datepicker ng-model="locals.date" datepicker-options="options"></div>')($scope);
|
||||
$scope.$digest();
|
||||
|
||||
expect($log.warn).toHaveBeenCalledWith('Literal date support has been deprecated, please switch to date object usage');
|
||||
@@ -212,7 +216,7 @@ describe('datepicker', function() {
|
||||
$scope.options = {
|
||||
maxDate: '1984-01-01'
|
||||
};
|
||||
element = $compile('<uib-datepicker ng-model="locals.date" datepicker-options="options"></uib-datepicker>')($scope);
|
||||
element = $compile('<div uib-datepicker ng-model="locals.date" datepicker-options="options"></div>')($scope);
|
||||
$scope.$digest();
|
||||
|
||||
expect($log.warn).not.toHaveBeenCalled();
|
||||
@@ -232,7 +236,7 @@ describe('datepicker', function() {
|
||||
});
|
||||
|
||||
spyOn($log, 'error');
|
||||
element = $compile('<uib-datepicker ng-model="locals.date"></uib-datepicker>')($scope);
|
||||
element = $compile('<div uib-datepicker ng-model="locals.date"></div>')($scope);
|
||||
|
||||
$scope.locals = {
|
||||
date: 'lalala'
|
||||
@@ -253,7 +257,7 @@ describe('datepicker', function() {
|
||||
});
|
||||
|
||||
spyOn($log, 'error');
|
||||
element = $compile('<uib-datepicker ng-model="locals.date"></uib-datepicker>')($scope);
|
||||
element = $compile('<div uib-datepicker ng-model="locals.date"></div>')($scope);
|
||||
|
||||
$scope.locals = {
|
||||
date: 'lalala'
|
||||
@@ -274,7 +278,7 @@ describe('datepicker', function() {
|
||||
});
|
||||
spyOn($log, 'error');
|
||||
|
||||
element = $compile('<uib-datepicker ng-model="locals.date"></uib-datepicker>')($scope);
|
||||
element = $compile('<div uib-datepicker ng-model="locals.date"></div>')($scope);
|
||||
|
||||
$scope.locals = {
|
||||
date: 'lalala'
|
||||
@@ -305,7 +309,7 @@ describe('datepicker', function() {
|
||||
var baseTime = new Date(2015, 2, 23);
|
||||
jasmine.clock().mockDate(baseTime);
|
||||
|
||||
element = $compile('<uib-datepicker ng-model="fooDate"></uib-datepicker')($rootScope);
|
||||
element = $compile('<div uib-datepicker ng-model="fooDate"></div')($rootScope);
|
||||
$rootScope.$digest();
|
||||
|
||||
expect(element.controller('uibDatepicker').activeDate.getTime()).toEqual(baseTime.getTime());
|
||||
@@ -315,10 +319,10 @@ describe('datepicker', function() {
|
||||
it('should support custom templates', function() {
|
||||
$templateCache.put('foo/bar.html', '<div>baz</div>');
|
||||
|
||||
element = $compile('<uib-datepicker ng-model="date" template-url="foo/bar.html"></uib-datepicker>')($rootScope);
|
||||
element = $compile('<div uib-datepicker ng-model="date" template-url="foo/bar.html"></div>')($rootScope);
|
||||
$rootScope.$digest();
|
||||
|
||||
expect(element.html()).toBe('baz');
|
||||
expect(element.html()).toBe('<div>baz</div>');
|
||||
});
|
||||
|
||||
it('should support custom day, month and year templates', function() {
|
||||
@@ -327,15 +331,15 @@ describe('datepicker', function() {
|
||||
$templateCache.put('foo/year.html', '<div>year</div>');
|
||||
|
||||
$templateCache.put('foo/bar.html', '<div>' +
|
||||
'<uib-daypicker template-url="foo/day.html"></uib-daypicker>' +
|
||||
'<uib-monthpicker template-url="foo/month.html"></uib-monthpicker>' +
|
||||
'<uib-yearpicker template-url="foo/year.html"></uib-yearpicker>' +
|
||||
'<div uib-daypicker template-url="foo/day.html"></div>' +
|
||||
'<div uib-monthpicker template-url="foo/month.html"></div>' +
|
||||
'<div uib-yearpicker template-url="foo/year.html"></div>' +
|
||||
'</div>');
|
||||
|
||||
element = $compile('<uib-datepicker ng-model="date" template-url="foo/bar.html"></uib-datepicker>')($rootScope);
|
||||
element = $compile('<div uib-datepicker ng-model="date" template-url="foo/bar.html"></div>')($rootScope);
|
||||
$rootScope.$digest();
|
||||
|
||||
var expectedHtml = '<div template-url="foo/day.html">day</div><div template-url="foo/month.html">month</div><div template-url="foo/year.html">year</div>';
|
||||
var expectedHtml = '<div><div uib-daypicker="" template-url="foo/day.html"><div>day</div></div><div uib-monthpicker="" template-url="foo/month.html"><div>month</div></div><div uib-yearpicker="" template-url="foo/year.html"><div>year</div></div></div>';
|
||||
|
||||
expect(element.html()).toBe(expectedHtml);
|
||||
});
|
||||
@@ -343,22 +347,22 @@ describe('datepicker', function() {
|
||||
it('should expose the controller in the template', function() {
|
||||
$templateCache.put('uib/template/datepicker/datepicker.html', '<div>{{datepicker.text}}</div>');
|
||||
|
||||
element = $compile('<uib-datepicker ng-model="date"></uib-datepicker>')($rootScope);
|
||||
element = $compile('<div uib-datepicker ng-model="date"></div>')($rootScope);
|
||||
$rootScope.$digest();
|
||||
|
||||
var ctrl = element.controller('uib-datepicker');
|
||||
expect(ctrl).toBeDefined();
|
||||
expect(element.html()).toBe('');
|
||||
expect(element.html()).toBe('<div class="ng-binding"></div>');
|
||||
|
||||
ctrl.text = 'baz';
|
||||
$rootScope.$digest();
|
||||
|
||||
expect(element.html()).toBe('baz');
|
||||
expect(element.html()).toBe('<div class="ng-binding">baz</div>');
|
||||
});
|
||||
|
||||
describe('basic functionality', function() {
|
||||
beforeEach(function() {
|
||||
element = $compile('<uib-datepicker ng-model="date"></uib-datepicker>')($rootScope);
|
||||
element = $compile('<div uib-datepicker ng-model="date"></div>')($rootScope);
|
||||
$rootScope.$digest();
|
||||
});
|
||||
|
||||
@@ -953,12 +957,29 @@ describe('datepicker', function() {
|
||||
});
|
||||
|
||||
describe('attribute `datepicker-options`', function() {
|
||||
describe('ngModelOptions', function() {
|
||||
beforeEach(inject(function() {
|
||||
$rootScope.date = new Date('2005-11-07T10:00:00.000Z');
|
||||
$rootScope.options = {
|
||||
ngModelOptions: {
|
||||
timezone: '+600'
|
||||
}
|
||||
};
|
||||
element = $compile('<div uib-datepicker ng-model="date" datepicker-options="options"></div>')($rootScope);
|
||||
$rootScope.$digest();
|
||||
}));
|
||||
|
||||
it('supports ngModelOptions from options object and sets date to appropriate date', function() {
|
||||
expectSelectedElement(8);
|
||||
});
|
||||
});
|
||||
|
||||
describe('startingDay', function() {
|
||||
beforeEach(function() {
|
||||
$rootScope.options = {
|
||||
startingDay: 1
|
||||
};
|
||||
element = $compile('<uib-datepicker ng-model="date" datepicker-options="options"></uib-datepicker>')($rootScope);
|
||||
element = $compile('<div uib-datepicker ng-model="date" datepicker-options="options"></div>')($rootScope);
|
||||
$rootScope.$digest();
|
||||
});
|
||||
|
||||
@@ -987,7 +1008,7 @@ describe('datepicker', function() {
|
||||
$rootScope.options = {
|
||||
showWeeks: false
|
||||
};
|
||||
element = $compile('<uib-datepicker ng-model="date" datepicker-options="options"></uib-datepicker>')($rootScope);
|
||||
element = $compile('<div uib-datepicker ng-model="date" datepicker-options="options"></div>')($rootScope);
|
||||
$rootScope.$digest();
|
||||
});
|
||||
|
||||
@@ -1004,7 +1025,7 @@ describe('datepicker', function() {
|
||||
beforeEach(function() {
|
||||
$rootScope.options = {};
|
||||
$rootScope.date = new Date('September 10, 2010');
|
||||
element = $compile('<uib-datepicker ng-model="date" datepicker-options="options"></uib-datepicker>')($rootScope);
|
||||
element = $compile('<div uib-datepicker ng-model="date" datepicker-options="options"></div>')($rootScope);
|
||||
$rootScope.$digest();
|
||||
});
|
||||
|
||||
@@ -1029,7 +1050,7 @@ describe('datepicker', function() {
|
||||
$rootScope.options = {
|
||||
minDate: new Date('September 12, 2010')
|
||||
};
|
||||
element = $compile('<uib-datepicker ng-model="date" datepicker-options="options"></uib-datepicker>')($rootScope);
|
||||
element = $compile('<div uib-datepicker ng-model="date" datepicker-options="options"></div>')($rootScope);
|
||||
$rootScope.$digest();
|
||||
});
|
||||
|
||||
@@ -1114,7 +1135,7 @@ describe('datepicker', function() {
|
||||
|
||||
it('accepts literals, \'yyyy-MM-dd\' case', function() {
|
||||
$rootScope.options.minDate = '2010-09-05';
|
||||
element = $compile('<uib-datepicker ng-model="date" datepicker-options="options"></uib-datepicker>')($rootScope);
|
||||
element = $compile('<div uib-datepicker ng-model="date" datepicker-options="options"></div>')($rootScope);
|
||||
$rootScope.$digest();
|
||||
var buttons = getAllOptionsEl();
|
||||
angular.forEach(buttons, function(button, index) {
|
||||
@@ -1127,7 +1148,7 @@ describe('datepicker', function() {
|
||||
beforeEach(function() {
|
||||
$rootScope.options = {};
|
||||
$rootScope.date = new Date('September 10, 2010');
|
||||
element = $compile('<uib-datepicker ng-model="date" datepicker-options="options"></uib-datepicker>')($rootScope);
|
||||
element = $compile('<div uib-datepicker ng-model="date" datepicker-options="options"></div>')($rootScope);
|
||||
$rootScope.$digest();
|
||||
});
|
||||
|
||||
@@ -1152,7 +1173,7 @@ describe('datepicker', function() {
|
||||
$rootScope.options = {
|
||||
maxDate: new Date('September 25, 2010')
|
||||
};
|
||||
element = $compile('<uib-datepicker ng-model="date" datepicker-options="options"></uib-datepicker>')($rootScope);
|
||||
element = $compile('<div uib-datepicker ng-model="date" datepicker-options="options"></div>')($rootScope);
|
||||
$rootScope.$digest();
|
||||
});
|
||||
|
||||
@@ -1244,8 +1265,8 @@ describe('datepicker', function() {
|
||||
yearColumns: 4,
|
||||
yearRows: 3
|
||||
};
|
||||
element = $compile('<uib-datepicker ng-model="date"' +
|
||||
'datepicker-options="options"></uib-datepicker>')($rootScope);
|
||||
element = $compile('<div uib-datepicker ng-model="date"' +
|
||||
'datepicker-options="options"></div>')($rootScope);
|
||||
$rootScope.$digest();
|
||||
});
|
||||
|
||||
@@ -1308,8 +1329,9 @@ describe('datepicker', function() {
|
||||
uibDatepickerConfig.yearRows = 2;
|
||||
uibDatepickerConfig.yearColumns = 5;
|
||||
uibDatepickerConfig.startingDay = 6;
|
||||
uibDatepickerConfig.monthColumns = 4;
|
||||
|
||||
element = $compile('<uib-datepicker ng-model="date"></uib-datepicker>')($rootScope);
|
||||
element = $compile('<div uib-datepicker ng-model="date"></div>')($rootScope);
|
||||
$rootScope.$digest();
|
||||
}));
|
||||
afterEach(inject(function(uibDatepickerConfig) {
|
||||
@@ -1329,13 +1351,17 @@ describe('datepicker', function() {
|
||||
|
||||
expect(getTitle()).toBe('10');
|
||||
expect(getOptions()).toEqual([
|
||||
['Jan', 'Feb', 'Mar'],
|
||||
['Apr', 'May', 'Jun'],
|
||||
['Jul', 'Aug', 'Sep'],
|
||||
['Oct', 'Nov', 'Dec']
|
||||
['Jan', 'Feb', 'Mar', 'Apr'],
|
||||
['May', 'Jun', 'Jul', 'Aug'],
|
||||
['Sep', 'Oct', 'Nov', 'Dec']
|
||||
]);
|
||||
});
|
||||
|
||||
it('shows title year button to expand to fill width in `month` mode', function() {
|
||||
clickTitleButton();
|
||||
expect(getTitleCell().attr('colspan')).toBe('2');
|
||||
});
|
||||
|
||||
it('changes the title, year format & range in `year` mode', function() {
|
||||
clickTitleButton();
|
||||
clickTitleButton();
|
||||
@@ -1370,7 +1396,7 @@ describe('datepicker', function() {
|
||||
|
||||
describe('disabled', function() {
|
||||
beforeEach(function() {
|
||||
element = $compile('<uib-datepicker ng-model="date" disabled></uib-datepicker>')($rootScope);
|
||||
element = $compile('<div uib-datepicker ng-model="date" disabled></div>')($rootScope);
|
||||
$rootScope.$digest();
|
||||
});
|
||||
|
||||
@@ -1384,7 +1410,7 @@ describe('datepicker', function() {
|
||||
describe('ng-disabled', function() {
|
||||
beforeEach(function() {
|
||||
$rootScope.disabled = false;
|
||||
element = $compile('<uib-datepicker ng-model="date" ng-disabled="disabled"></uib-datepicker>')($rootScope);
|
||||
element = $compile('<div uib-datepicker ng-model="date" ng-disabled="disabled"></div>')($rootScope);
|
||||
$rootScope.$digest();
|
||||
});
|
||||
|
||||
@@ -1425,7 +1451,7 @@ describe('datepicker', function() {
|
||||
|
||||
describe('basics', function() {
|
||||
beforeEach(function() {
|
||||
element = $compile('<uib-datepicker ng-model="date"></uib-datepicker>')($rootScope);
|
||||
element = $compile('<div uib-datepicker ng-model="date"></div>')($rootScope);
|
||||
$rootScope.$digest();
|
||||
});
|
||||
|
||||
@@ -1444,7 +1470,7 @@ describe('datepicker', function() {
|
||||
initDate: new Date('2006-01-01T00:00:00.000Z')
|
||||
};
|
||||
$rootScope.date = null;
|
||||
element = $compile('<uib-datepicker ng-model="date" datepicker-options="options"><uib-datepicker>')($rootScope);
|
||||
element = $compile('<div uib-datepicker ng-model="date" datepicker-options="options"></div>')($rootScope);
|
||||
$rootScope.$digest();
|
||||
|
||||
expect(getTitle()).toEqual('January 2006');
|
||||
@@ -1454,7 +1480,7 @@ describe('datepicker', function() {
|
||||
$rootScope.options = {
|
||||
minDate: new Date('2010-10-01T00:00:00.000Z')
|
||||
};
|
||||
element = $compile('<uib-datepicker ng-model="date" datepicker-options="options"><uib-datepicker>')($rootScope);
|
||||
element = $compile('<div uib-datepicker ng-model="date" datepicker-options="options"></div>')($rootScope);
|
||||
$rootScope.$digest();
|
||||
|
||||
expect(getSelectedElement().prop('disabled')).toBe(true);
|
||||
@@ -1467,7 +1493,7 @@ describe('datepicker', function() {
|
||||
beforeEach(inject(function() {
|
||||
$rootScope.date = new Date('2005-11-07T10:00:00.000Z');
|
||||
$rootScope.ngModelOptions = { timezone: '+600'};
|
||||
element = $compile('<uib-datepicker ng-model="date" ng-model-options="ngModelOptions"></uib-datepicker>')($rootScope);
|
||||
element = $compile('<div uib-datepicker ng-model="date" ng-model-options="ngModelOptions"></div>')($rootScope);
|
||||
$rootScope.$digest();
|
||||
}));
|
||||
|
||||
@@ -1485,7 +1511,7 @@ describe('datepicker', function() {
|
||||
describe('with empty initial state', function() {
|
||||
beforeEach(inject(function() {
|
||||
$rootScope.date = null;
|
||||
element = $compile('<uib-datepicker ng-model="date"></uib-datepicker>')($rootScope);
|
||||
element = $compile('<div uib-datepicker ng-model="date"></div>')($rootScope);
|
||||
$rootScope.$digest();
|
||||
}));
|
||||
|
||||
@@ -1514,7 +1540,7 @@ describe('datepicker', function() {
|
||||
$rootScope.options = {
|
||||
initDate: new Date('November 9, 1980')
|
||||
};
|
||||
element = $compile('<uib-datepicker ng-model="date" datepicker-options="options"></uib-datepicker>')($rootScope);
|
||||
element = $compile('<div uib-datepicker ng-model="date" datepicker-options="options"></div>')($rootScope);
|
||||
$rootScope.$digest();
|
||||
}));
|
||||
|
||||
@@ -1533,7 +1559,7 @@ describe('datepicker', function() {
|
||||
$rootScope.options = {
|
||||
datepickerMode: 'month'
|
||||
};
|
||||
element = $compile('<uib-datepicker ng-model="date" datepicker-options="options"></uib-datepicker>')($rootScope);
|
||||
element = $compile('<div uib-datepicker ng-model="date" datepicker-options="options"></div>')($rootScope);
|
||||
$rootScope.$digest();
|
||||
}));
|
||||
|
||||
@@ -1554,7 +1580,7 @@ describe('datepicker', function() {
|
||||
minMode: 'month',
|
||||
datepickerMode: 'month'
|
||||
};
|
||||
element = $compile('<uib-datepicker ng-model="date" datepicker-options="options"></uib-datepicker>')($rootScope);
|
||||
element = $compile('<div uib-datepicker ng-model="date" datepicker-options="options"></div>')($rootScope);
|
||||
$rootScope.$digest();
|
||||
}));
|
||||
|
||||
@@ -1584,7 +1610,7 @@ describe('datepicker', function() {
|
||||
$rootScope.options = {
|
||||
maxMode: 'month'
|
||||
};
|
||||
element = $compile('<uib-datepicker ng-model="date" datepicker-options="options"></uib-datepicker>')($rootScope);
|
||||
element = $compile('<div uib-datepicker ng-model="date" datepicker-options="options"></div>')($rootScope);
|
||||
$rootScope.$digest();
|
||||
}));
|
||||
|
||||
@@ -1632,7 +1658,7 @@ describe('datepicker', function() {
|
||||
|
||||
// Use dateModel directive to add formatters and parsers to the
|
||||
// ngModelController that translate the custom date object.
|
||||
element = $compile('<uib-datepicker ng-model="date" date-model></uib-datepicker>')($rootScope);
|
||||
element = $compile('<div uib-datepicker ng-model="date" date-model></div>')($rootScope);
|
||||
$rootScope.$digest();
|
||||
}));
|
||||
|
||||
@@ -1657,7 +1683,7 @@ describe('datepicker', function() {
|
||||
}));
|
||||
|
||||
it('with the default starting day (sunday)', function() {
|
||||
element = $compile('<uib-datepicker ng-model="date"></uib-datepicker>')($rootScope);
|
||||
element = $compile('<div uib-datepicker ng-model="date"></div>')($rootScope);
|
||||
$rootScope.$digest();
|
||||
|
||||
expect(getWeeks()).toEqual(['23', '24', '25', '26', '27', '28']);
|
||||
@@ -1668,7 +1694,7 @@ describe('datepicker', function() {
|
||||
$rootScope.options = {
|
||||
startingDay: 1
|
||||
};
|
||||
element = $compile('<uib-datepicker ng-model="date" datepicker-options="options"></uib-datepicker>')($rootScope);
|
||||
element = $compile('<div uib-datepicker ng-model="date" datepicker-options="options"></div>')($rootScope);
|
||||
$rootScope.$digest();
|
||||
|
||||
expect(getWeeks()).toEqual(['22', '23', '24', '25', '26', '27']);
|
||||
@@ -1678,7 +1704,7 @@ describe('datepicker', function() {
|
||||
$rootScope.options = {
|
||||
startingDay: 4
|
||||
};
|
||||
element = $compile('<uib-datepicker ng-model="date" datepicker-options="options"></uib-datepicker>')($rootScope);
|
||||
element = $compile('<div uib-datepicker ng-model="date" datepicker-options="options"></div>')($rootScope);
|
||||
$rootScope.$digest();
|
||||
|
||||
expect(getWeeks()).toEqual(['22', '23', '24', '25', '26', '27']);
|
||||
@@ -1688,7 +1714,7 @@ describe('datepicker', function() {
|
||||
$rootScope.options = {
|
||||
startingDay: 6
|
||||
};
|
||||
element = $compile('<uib-datepicker ng-model="date" datepicker-options="options"></uib-datepicker>')($rootScope);
|
||||
element = $compile('<div uib-datepicker ng-model="date" datepicker-options="options"></div>')($rootScope);
|
||||
$rootScope.$digest();
|
||||
|
||||
expect(getWeeks()).toEqual(['23', '24', '25', '26', '27', '28']);
|
||||
@@ -1698,7 +1724,7 @@ describe('datepicker', function() {
|
||||
describe('first week in january', function() {
|
||||
it('in current year', function() {
|
||||
$rootScope.date = new Date('January 07, 2014');
|
||||
element = $compile('<uib-datepicker ng-model="date"></uib-datepicker>')($rootScope);
|
||||
element = $compile('<div uib-datepicker ng-model="date"></div>')($rootScope);
|
||||
$rootScope.$digest();
|
||||
|
||||
expect(getWeeks()).toEqual(['1', '2', '3', '4', '5', '6']);
|
||||
@@ -1706,7 +1732,7 @@ describe('datepicker', function() {
|
||||
|
||||
it('in last year', function() {
|
||||
$rootScope.date = new Date('January 07, 2010');
|
||||
element = $compile('<uib-datepicker ng-model="date"></uib-datepicker>')($rootScope);
|
||||
element = $compile('<div uib-datepicker ng-model="date"></div>')($rootScope);
|
||||
$rootScope.$digest();
|
||||
|
||||
expect(getWeeks()).toEqual(['53', '1', '2', '3', '4', '5']);
|
||||
@@ -1719,7 +1745,7 @@ describe('datepicker', function() {
|
||||
}));
|
||||
|
||||
it('in next year', function() {
|
||||
element = $compile('<uib-datepicker ng-model="date"></uib-datepicker>')($rootScope);
|
||||
element = $compile('<div uib-datepicker ng-model="date"></div>')($rootScope);
|
||||
$rootScope.$digest();
|
||||
|
||||
expect(getWeeks()).toEqual(['49', '50', '51', '52', '1', '2']);
|
||||
|
||||
@@ -5,6 +5,6 @@ require('./popup.js');
|
||||
|
||||
var MODULE_NAME = 'ui.bootstrap.module.datepickerPopup';
|
||||
|
||||
angular.module(MODULE_NAME, ['ui.bootstrap.datepickerPopup', 'uib/template/datepickerPopup/popup.html']);
|
||||
angular.module(MODULE_NAME, ['ui.bootstrap.datepickerPopup', 'uib/template/datepickerPopup/popup.html', 'ui.bootstrap.module.datepicker']);
|
||||
|
||||
module.exports = MODULE_NAME;
|
||||
|
||||
@@ -28,12 +28,11 @@ function($scope, $element, $attrs, $compile, $log, $parse, $window, $document, $
|
||||
isHtml5DateInput = false;
|
||||
var dateFormat, closeOnDateSelection, appendToBody, onOpenFocus,
|
||||
datepickerPopupTemplateUrl, datepickerTemplateUrl, popupEl, datepickerEl, scrollParentEl,
|
||||
ngModel, ngModelOptions, $popup, altInputFormats, watchListeners = [],
|
||||
timezone;
|
||||
ngModel, ngModelOptions, $popup, altInputFormats, watchListeners = [];
|
||||
|
||||
this.init = function(_ngModel_) {
|
||||
ngModel = _ngModel_;
|
||||
ngModelOptions = _ngModel_.$options;
|
||||
ngModelOptions = extractOptions(ngModel);
|
||||
closeOnDateSelection = angular.isDefined($attrs.closeOnDateSelection) ?
|
||||
$scope.$parent.$eval($attrs.closeOnDateSelection) :
|
||||
datepickerPopupConfig.closeOnDateSelection;
|
||||
@@ -85,19 +84,6 @@ function($scope, $element, $attrs, $compile, $log, $parse, $window, $document, $
|
||||
|
||||
// popup element used to display calendar
|
||||
popupEl = angular.element('<div uib-datepicker-popup-wrap><div uib-datepicker></div></div>');
|
||||
if (ngModelOptions) {
|
||||
timezone = ngModelOptions.timezone;
|
||||
$scope.ngModelOptions = angular.copy(ngModelOptions);
|
||||
$scope.ngModelOptions.timezone = null;
|
||||
if ($scope.ngModelOptions.updateOnDefault === true) {
|
||||
$scope.ngModelOptions.updateOn = $scope.ngModelOptions.updateOn ?
|
||||
$scope.ngModelOptions.updateOn + ' default' : 'default';
|
||||
}
|
||||
|
||||
popupEl.attr('ng-model-options', 'ngModelOptions');
|
||||
} else {
|
||||
timezone = null;
|
||||
}
|
||||
|
||||
popupEl.attr({
|
||||
'ng-model': 'date',
|
||||
@@ -137,13 +123,13 @@ function($scope, $element, $attrs, $compile, $log, $parse, $window, $document, $
|
||||
value = new Date(value);
|
||||
}
|
||||
|
||||
$scope.date = dateParser.fromTimezone(value, 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, timezone);
|
||||
$scope.date = dateParser.fromTimezone(value, ngModelOptions.getOption('timezone'));
|
||||
return value;
|
||||
});
|
||||
}
|
||||
@@ -195,7 +181,7 @@ function($scope, $element, $attrs, $compile, $log, $parse, $window, $document, $
|
||||
|
||||
$scope.isDisabled = function(date) {
|
||||
if (date === 'today') {
|
||||
date = dateParser.fromTimezone(new Date(), timezone);
|
||||
date = dateParser.fromTimezone(new Date(), ngModelOptions.getOption('timezone'));
|
||||
}
|
||||
|
||||
var dates = {};
|
||||
@@ -203,7 +189,7 @@ function($scope, $element, $attrs, $compile, $log, $parse, $window, $document, $
|
||||
if (!$scope.datepickerOptions[key]) {
|
||||
dates[key] = null;
|
||||
} else if (angular.isDate($scope.datepickerOptions[key])) {
|
||||
dates[key] = dateParser.fromTimezone(new Date($scope.datepickerOptions[key]), timezone);
|
||||
dates[key] = new Date($scope.datepickerOptions[key]);
|
||||
} else {
|
||||
if ($datepickerPopupLiteralWarning) {
|
||||
$log.warn('Literal date support has been deprecated, please switch to date object usage');
|
||||
@@ -224,9 +210,7 @@ function($scope, $element, $attrs, $compile, $log, $parse, $window, $document, $
|
||||
|
||||
// Inner change
|
||||
$scope.dateSelection = function(dt) {
|
||||
if (angular.isDefined(dt)) {
|
||||
$scope.date = dt;
|
||||
}
|
||||
$scope.date = dt;
|
||||
var date = $scope.date ? dateParser.filter($scope.date, dateFormat) : null; // Setting to NULL is necessary for form validators to function
|
||||
$element.val(date);
|
||||
ngModel.$setViewValue(date);
|
||||
@@ -254,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);
|
||||
@@ -344,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.toTimezone(date, 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) {
|
||||
@@ -371,7 +356,7 @@ function($scope, $element, $attrs, $compile, $log, $parse, $window, $document, $
|
||||
}
|
||||
|
||||
if (angular.isString(value)) {
|
||||
return !isNaN(parseDateString(viewValue));
|
||||
return !isNaN(parseDateString(value));
|
||||
}
|
||||
|
||||
return false;
|
||||
@@ -423,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);
|
||||
});
|
||||
@@ -450,7 +457,7 @@ function($scope, $element, $attrs, $compile, $log, $parse, $window, $document, $
|
||||
|
||||
.directive('uibDatepickerPopupWrap', function() {
|
||||
return {
|
||||
replace: true,
|
||||
restrict: 'A',
|
||||
transclude: true,
|
||||
templateUrl: function(element, attrs) {
|
||||
return attrs.templateUrl || 'uib/template/datepickerPopup/popup.html';
|
||||
|
||||
@@ -175,8 +175,10 @@ describe('datepicker popup', function() {
|
||||
$sniffer = _$sniffer_;
|
||||
|
||||
$rootScope.date = new Date('September 30, 2010 15:30:00');
|
||||
$rootScope.modelOptions = {allowInvalid: true};
|
||||
element = $compile('<div><input ng-model="date" ng-model-options="modelOptions" uib-datepicker-popup></div>')($rootScope);
|
||||
$rootScope.ngModelOptions = {
|
||||
allowInvalid: true
|
||||
};
|
||||
element = $compile('<div><input ng-model="date" ng-model-options="ngModelOptions" uib-datepicker-popup></div>')($rootScope);
|
||||
inputEl = element.find('input');
|
||||
$rootScope.$digest();
|
||||
}));
|
||||
@@ -395,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);
|
||||
@@ -403,7 +405,7 @@ describe('datepicker popup', function() {
|
||||
triggerKeyDown(inputEl, 'esc');
|
||||
expect(documentKey).toBe(27);
|
||||
|
||||
$document.unbind('keydown', getKey);
|
||||
$document.off('keydown', getKey);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -557,8 +559,13 @@ describe('datepicker popup', function() {
|
||||
$timeout = _$timeout_;
|
||||
$rootScope.isopen = true;
|
||||
$rootScope.date = new Date('2010-09-30T10:00:00.000Z');
|
||||
$rootScope.options = {
|
||||
ngModelOptions: {
|
||||
updateOn: 'default'
|
||||
}
|
||||
};
|
||||
wrapElement = $compile('<div><input ng-model="date" ' +
|
||||
'ng-model-options="{ updateOn: \'default\' }" ' +
|
||||
'datepicker-options="options" ' +
|
||||
'uib-datepicker-popup is-open="isopen"><div>')($rootScope);
|
||||
$rootScope.$digest();
|
||||
assignElements(wrapElement);
|
||||
@@ -1034,6 +1041,13 @@ describe('datepicker popup', function() {
|
||||
expect($rootScope.date).toBe(null);
|
||||
});
|
||||
|
||||
it('should clear the previously selected date', function() {
|
||||
$rootScope.date = new Date();
|
||||
$rootScope.$digest();
|
||||
buttons.eq(1).click();
|
||||
expect($rootScope.date).toBe(null);
|
||||
});
|
||||
|
||||
it('should have a button to close calendar', function() {
|
||||
buttons.eq(2).click();
|
||||
assignElements(wrapElement);
|
||||
@@ -1330,7 +1344,7 @@ describe('datepicker popup', function() {
|
||||
$compile(elm)($rootScope);
|
||||
$rootScope.$digest();
|
||||
|
||||
expect(elm.children().eq(1).html()).toBe('baz');
|
||||
expect(elm.children().eq(1).html()).toBe('<div>baz</div>');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1353,7 +1367,7 @@ describe('datepicker popup', function() {
|
||||
|
||||
var datepicker = elm.find('[uib-datepicker]');
|
||||
|
||||
expect(datepicker.html()).toBe('baz');
|
||||
expect(datepicker.html()).toBe('<div>baz</div>');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1598,9 +1612,13 @@ describe('datepicker popup', function() {
|
||||
|
||||
beforeEach(function() {
|
||||
$rootScope.date = new Date('2010-09-30T10:00:00.000Z');
|
||||
$rootScope.ngModelOptions = { timezone: '+600' };
|
||||
$rootScope.options = {
|
||||
ngModelOptions: {
|
||||
timezone: '+600'
|
||||
}
|
||||
};
|
||||
$rootScope.isopen = true;
|
||||
var wrapper = $compile('<div><input ng-model="date" uib-datepicker-popup="MM/dd/yyyy" ng-model-options="ngModelOptions" is-open="isopen"></div>')($rootScope);
|
||||
var wrapper = $compile('<div><input ng-model="date" uib-datepicker-popup="MM/dd/yyyy" datepicker-options="options" is-open="isopen"></div>')($rootScope);
|
||||
$rootScope.$digest();
|
||||
assignElements(wrapper);
|
||||
});
|
||||
@@ -1631,9 +1649,13 @@ describe('datepicker popup', function() {
|
||||
|
||||
beforeEach(function() {
|
||||
$rootScope.date = new Date('2010-09-30T10:00:00.000Z');
|
||||
$rootScope.ngModelOptions = { timezone: '+600' };
|
||||
$rootScope.options = {
|
||||
ngModelOptions: {
|
||||
timezone: '+600'
|
||||
}
|
||||
};
|
||||
$rootScope.isopen = true;
|
||||
var wrapper = $compile('<div><input type="date" ng-model="date" uib-datepicker-popup ng-model-options="ngModelOptions" is-open="isopen"></div>')($rootScope);
|
||||
var wrapper = $compile('<div><input type="date" ng-model="date" uib-datepicker-popup datepicker-options="options" is-open="isopen"></div>')($rootScope);
|
||||
$rootScope.$digest();
|
||||
assignElements(wrapper);
|
||||
});
|
||||
|
||||
@@ -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>
|
||||
|
||||
+136
-39
@@ -1,17 +1,35 @@
|
||||
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);
|
||||
element.on('keydown', keybindFilter);
|
||||
}
|
||||
|
||||
if (openScope && openScope !== dropdownScope) {
|
||||
@@ -19,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);
|
||||
element.off('keydown', keybindFilter);
|
||||
$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; }
|
||||
|
||||
@@ -49,6 +105,7 @@ angular.module('ui.bootstrap.dropdown', ['ui.bootstrap.position'])
|
||||
return;
|
||||
}
|
||||
|
||||
openScope.focusToggleElement();
|
||||
openScope.isOpen = false;
|
||||
|
||||
if (!$rootScope.$$phase) {
|
||||
@@ -56,12 +113,21 @@ angular.module('ui.bootstrap.dropdown', ['ui.bootstrap.position'])
|
||||
}
|
||||
};
|
||||
|
||||
var keybindFilter = function(evt) {
|
||||
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);
|
||||
var toggleElementTargeted = toggleElement && toggleElement[0].contains(evt.target);
|
||||
if (evt.which === 27) {
|
||||
evt.stopPropagation();
|
||||
openScope.focusToggleElement();
|
||||
closeDropdown();
|
||||
} else if (openScope.isKeynavEnabled() && [38, 40].indexOf(evt.which) !== -1 && openScope.isOpen) {
|
||||
} else if (openScope.isKeynavEnabled() && [38, 40].indexOf(evt.which) !== -1 && openScope.isOpen && (dropdownElementTargeted || toggleElementTargeted)) {
|
||||
evt.preventDefault();
|
||||
evt.stopPropagation();
|
||||
openScope.focusDropdownEntry(evt.which);
|
||||
@@ -78,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');
|
||||
@@ -96,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) {
|
||||
@@ -187,12 +232,48 @@ 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,
|
||||
rightalign,
|
||||
scrollbarWidth;
|
||||
scrollbarPadding,
|
||||
scrollbarWidth = 0;
|
||||
|
||||
css = {
|
||||
top: pos.top + 'px',
|
||||
@@ -205,7 +286,12 @@ angular.module('ui.bootstrap.dropdown', ['ui.bootstrap.position'])
|
||||
css.right = 'auto';
|
||||
} else {
|
||||
css.left = 'auto';
|
||||
scrollbarWidth = $position.scrollbarWidth(true);
|
||||
scrollbarPadding = $position.scrollbarPadding(appendTo);
|
||||
|
||||
if (scrollbarPadding.heightOverflow && scrollbarPadding.scrollbarWidth) {
|
||||
scrollbarWidth = scrollbarPadding.scrollbarWidth;
|
||||
}
|
||||
|
||||
css.right = window.innerWidth - scrollbarWidth -
|
||||
(pos.left + $element.prop('offsetWidth')) + 'px';
|
||||
}
|
||||
@@ -229,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 });
|
||||
}
|
||||
@@ -247,13 +341,17 @@ angular.module('ui.bootstrap.dropdown', ['ui.bootstrap.position'])
|
||||
var newEl = dropdownElement;
|
||||
self.dropdownMenu.replaceWith(newEl);
|
||||
self.dropdownMenu = newEl;
|
||||
$document.on('keydown', uibDropdownService.keybindFilter);
|
||||
});
|
||||
});
|
||||
} else {
|
||||
$document.on('keydown', uibDropdownService.keybindFilter);
|
||||
}
|
||||
|
||||
scope.focusToggleElement();
|
||||
uibDropdownService.open(scope, $element);
|
||||
uibDropdownService.open(scope, $element, appendTo);
|
||||
} else {
|
||||
uibDropdownService.close(scope, $element, appendTo);
|
||||
if (self.dropdownMenuTemplateUrl) {
|
||||
if (templateScope) {
|
||||
templateScope.$destroy();
|
||||
@@ -263,7 +361,6 @@ angular.module('ui.bootstrap.dropdown', ['ui.bootstrap.position'])
|
||||
self.dropdownMenu = newEl;
|
||||
}
|
||||
|
||||
uibDropdownService.close(scope, $element);
|
||||
self.selectedOption = null;
|
||||
}
|
||||
|
||||
@@ -327,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 });
|
||||
@@ -336,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');
|
||||
|
||||
|
||||
@@ -35,7 +35,7 @@ describe('uib-dropdown', function() {
|
||||
|
||||
describe('basic', function() {
|
||||
function dropdown() {
|
||||
return $compile('<li uib-dropdown><a href uib-dropdown-toggle></a><ul><li><a href>Hello</a></li></ul></li>')($rootScope);
|
||||
return $compile('<li uib-dropdown><a href uib-dropdown-toggle></a><ul uib-dropdown-menu><li><a href>Hello</a></li></ul></li>')($rootScope);
|
||||
}
|
||||
|
||||
beforeEach(function() {
|
||||
@@ -69,9 +69,10 @@ describe('uib-dropdown', function() {
|
||||
});
|
||||
|
||||
it('should close on escape key & focus toggle element', function() {
|
||||
var dropdownMenu = element.find('[uib-dropdown-menu]');
|
||||
$document.find('body').append(element);
|
||||
clickDropdownToggle();
|
||||
var event = triggerKeyDown(element, 27);
|
||||
var event = triggerKeyDown(dropdownMenu, 27);
|
||||
expect(element).not.toHaveClass(dropdownConfig.openClass);
|
||||
expect(element.find('a')).toHaveFocus();
|
||||
expect(event.stopPropagation).toHaveBeenCalled();
|
||||
@@ -98,17 +99,17 @@ 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);
|
||||
});
|
||||
|
||||
it('should not toggle if the element has `disabled` class', function() {
|
||||
var elm = $compile('<li uib-dropdown><a class="disabled" uib-dropdown-toggle></a><ul><li>Hello</li></ul></li>')($rootScope);
|
||||
var elm = $compile('<li uib-dropdown><a class="disabled" uib-dropdown-toggle></a><ul uib-dropdown-menu><li>Hello</li></ul></li>')($rootScope);
|
||||
clickDropdownToggle( elm );
|
||||
expect(elm).not.toHaveClass(dropdownConfig.openClass);
|
||||
});
|
||||
@@ -121,7 +122,7 @@ describe('uib-dropdown', function() {
|
||||
|
||||
it('should not toggle if the element has `ng-disabled` as true', function() {
|
||||
$rootScope.isdisabled = true;
|
||||
var elm = $compile('<li uib-dropdown><div ng-disabled="isdisabled" uib-dropdown-toggle></div><ul><li>Hello</li></ul></li>')($rootScope);
|
||||
var elm = $compile('<li uib-dropdown><div ng-disabled="isdisabled" uib-dropdown-toggle></div><ul uib-dropdown-menu><li>Hello</li></ul></li>')($rootScope);
|
||||
$rootScope.$digest();
|
||||
elm.find('div').click();
|
||||
expect(elm).not.toHaveClass(dropdownConfig.openClass);
|
||||
@@ -134,7 +135,7 @@ describe('uib-dropdown', function() {
|
||||
|
||||
it('should unbind events on scope destroy', function() {
|
||||
var $scope = $rootScope.$new();
|
||||
var elm = $compile('<li uib-dropdown><button ng-disabled="isdisabled" uib-dropdown-toggle></button><ul><li>Hello</li></ul></li>')($scope);
|
||||
var elm = $compile('<li uib-dropdown><button ng-disabled="isdisabled" uib-dropdown-toggle></button><ul uib-dropdown-menu><li>Hello</li></ul></li>')($scope);
|
||||
$scope.$digest();
|
||||
|
||||
var buttonEl = elm.find('button');
|
||||
@@ -214,27 +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();
|
||||
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]);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('adds the menu to the body', function() {
|
||||
expect($document.find('#dropdown-menu').parent()[0]).toBe($document.find('body')[0]);
|
||||
});
|
||||
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('removes the menu when the dropdown is removed', function() {
|
||||
element.remove();
|
||||
$rootScope.$digest();
|
||||
expect($document.find('#dropdown-menu').length).toEqual(0);
|
||||
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]);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
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);
|
||||
@@ -243,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);
|
||||
@@ -254,24 +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('removes the menu when the dropdown is removed', function() {
|
||||
element.remove();
|
||||
$rootScope.$digest();
|
||||
expect($document.find('#dropdown-menu').length).toEqual(0);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -279,7 +522,7 @@ describe('uib-dropdown', function() {
|
||||
describe('with uib-dropdown-toggle', function() {
|
||||
beforeEach(function() {
|
||||
$rootScope.isopen = true;
|
||||
element = $compile('<li uib-dropdown is-open="isopen"><a href uib-dropdown-toggle></a><ul><li>Hello</li></ul></li>')($rootScope);
|
||||
element = $compile('<li uib-dropdown is-open="isopen"><a href uib-dropdown-toggle></a><ul uib-dropdown-menu><li>Hello</li></ul></li>')($rootScope);
|
||||
$rootScope.$digest();
|
||||
});
|
||||
|
||||
@@ -313,7 +556,7 @@ describe('uib-dropdown', function() {
|
||||
describe('without uib-dropdown-toggle', function() {
|
||||
beforeEach(function() {
|
||||
$rootScope.isopen = true;
|
||||
element = $compile('<li uib-dropdown is-open="isopen"><ul><li>Hello</li></ul></li>')($rootScope);
|
||||
element = $compile('<li uib-dropdown is-open="isopen"><ul uib-dropdown-menu><li>Hello</li></ul></li>')($rootScope);
|
||||
$rootScope.$digest();
|
||||
});
|
||||
|
||||
@@ -334,7 +577,7 @@ describe('uib-dropdown', function() {
|
||||
beforeEach(function() {
|
||||
$rootScope.toggleHandler = jasmine.createSpy('toggleHandler');
|
||||
$rootScope.isopen = false;
|
||||
element = $compile('<li uib-dropdown on-toggle="toggleHandler(open)" is-open="isopen"><a uib-dropdown-toggle></a><ul><li>Hello</li></ul></li>')($rootScope);
|
||||
element = $compile('<li uib-dropdown on-toggle="toggleHandler(open)" is-open="isopen"><a uib-dropdown-toggle></a><ul uib-dropdown-menu><li>Hello</li></ul></li>')($rootScope);
|
||||
$rootScope.$digest();
|
||||
});
|
||||
|
||||
@@ -361,7 +604,7 @@ describe('uib-dropdown', function() {
|
||||
beforeEach(function() {
|
||||
$rootScope.toggleHandler = jasmine.createSpy('toggleHandler');
|
||||
$rootScope.isopen = true;
|
||||
element = $compile('<li uib-dropdown on-toggle="toggleHandler(open)" is-open="isopen"><a uib-dropdown-toggle></a><ul><li>Hello</li></ul></li>')($rootScope);
|
||||
element = $compile('<li uib-dropdown on-toggle="toggleHandler(open)" is-open="isopen"><a uib-dropdown-toggle></a><ul uib-dropdown-menu><li>Hello</li></ul></li>')($rootScope);
|
||||
$rootScope.$digest();
|
||||
});
|
||||
|
||||
@@ -389,7 +632,7 @@ describe('uib-dropdown', function() {
|
||||
describe('without is-open', function() {
|
||||
beforeEach(function() {
|
||||
$rootScope.toggleHandler = jasmine.createSpy('toggleHandler');
|
||||
element = $compile('<li uib-dropdown on-toggle="toggleHandler(open)"><a uib-dropdown-toggle></a><ul><li>Hello</li></ul></li>')($rootScope);
|
||||
element = $compile('<li uib-dropdown on-toggle="toggleHandler(open)"><a uib-dropdown-toggle></a><ul uib-dropdown-menu><li>Hello</li></ul></li>')($rootScope);
|
||||
$rootScope.$digest();
|
||||
});
|
||||
|
||||
@@ -417,7 +660,7 @@ describe('uib-dropdown', function() {
|
||||
function dropdown(autoClose) {
|
||||
return $compile('<li uib-dropdown ' +
|
||||
(autoClose === undefined ? '' : 'auto-close="' + autoClose + '"') +
|
||||
'><a href uib-dropdown-toggle></a><ul><li><a href>Hello</a></li></ul></li>')($rootScope);
|
||||
'><a href uib-dropdown-toggle></a><ul uib-dropdown-menu><li><a href>Hello</a></li></ul></li>')($rootScope);
|
||||
}
|
||||
|
||||
describe('always', function() {
|
||||
@@ -449,7 +692,7 @@ describe('uib-dropdown', function() {
|
||||
|
||||
it('control with is-open', function() {
|
||||
$rootScope.isopen = true;
|
||||
element = $compile('<li uib-dropdown is-open="isopen" auto-close="disabled"><a href uib-dropdown-toggle></a><ul><li>Hello</li></ul></li>')($rootScope);
|
||||
element = $compile('<li uib-dropdown is-open="isopen" auto-close="disabled"><a href uib-dropdown-toggle></a><ul uib-dropdown-menu><li>Hello</li></ul></li>')($rootScope);
|
||||
$rootScope.$digest();
|
||||
|
||||
expect(element).toHaveClass(dropdownConfig.openClass);
|
||||
@@ -472,9 +715,10 @@ describe('uib-dropdown', function() {
|
||||
|
||||
it('should close anyway if esc is pressed', function() {
|
||||
element = dropdown('disabled');
|
||||
var dropdownMenu = element.find('[uib-dropdown-menu]');
|
||||
$document.find('body').append(element);
|
||||
clickDropdownToggle();
|
||||
triggerKeyDown(element, 27);
|
||||
triggerKeyDown(dropdownMenu, 27);
|
||||
expect(element).not.toHaveClass(dropdownConfig.openClass);
|
||||
expect(element.find('a')).toHaveFocus();
|
||||
});
|
||||
@@ -519,7 +763,10 @@ describe('uib-dropdown', function() {
|
||||
|
||||
describe('using keyboard-nav', function() {
|
||||
function dropdown() {
|
||||
return $compile('<li uib-dropdown keyboard-nav><a href uib-dropdown-toggle></a><ul><li><a href>Hello</a></li><li><a href>Hello Again</a></li></ul></li>')($rootScope);
|
||||
return $compile('<li uib-dropdown keyboard-nav><a href uib-dropdown-toggle></a><ul uib-dropdown-menu><li><a href>Hello</a></li><li><a href>Hello Again</a></li></ul></li>')($rootScope);
|
||||
}
|
||||
function getFocusedElement() {
|
||||
return angular.element(document.activeElement);
|
||||
}
|
||||
beforeEach(function() {
|
||||
element = dropdown();
|
||||
@@ -528,7 +775,7 @@ describe('uib-dropdown', function() {
|
||||
it('should focus first list element when down arrow pressed', function() {
|
||||
$document.find('body').append(element);
|
||||
clickDropdownToggle();
|
||||
triggerKeyDown(element, 40);
|
||||
triggerKeyDown(getFocusedElement(), 40);
|
||||
|
||||
expect(element).toHaveClass(dropdownConfig.openClass);
|
||||
var optionEl = element.find('ul').eq(0).find('a').eq(0);
|
||||
@@ -537,7 +784,7 @@ describe('uib-dropdown', function() {
|
||||
|
||||
it('should not focus first list element when down arrow pressed if closed', function() {
|
||||
$document.find('body').append(element);
|
||||
triggerKeyDown(element, 40);
|
||||
triggerKeyDown(getFocusedElement(), 40);
|
||||
|
||||
expect(element).not.toHaveClass(dropdownConfig.openClass);
|
||||
var focusEl = element.find('ul').eq(0).find('a').eq(0);
|
||||
@@ -547,8 +794,8 @@ describe('uib-dropdown', function() {
|
||||
it('should focus second list element when down arrow pressed twice', function() {
|
||||
$document.find('body').append(element);
|
||||
clickDropdownToggle();
|
||||
triggerKeyDown(element, 40);
|
||||
triggerKeyDown(element, 40);
|
||||
triggerKeyDown(getFocusedElement(), 40);
|
||||
triggerKeyDown(getFocusedElement(), 40);
|
||||
|
||||
expect(element).toHaveClass(dropdownConfig.openClass);
|
||||
var focusEl = element.find('ul').eq(0).find('a').eq(1);
|
||||
@@ -560,7 +807,7 @@ describe('uib-dropdown', function() {
|
||||
clickDropdownToggle();
|
||||
expect(element).toHaveClass(dropdownConfig.openClass);
|
||||
|
||||
triggerKeyDown(element, 38);
|
||||
triggerKeyDown(getFocusedElement(), 38);
|
||||
var focusEl = element.find('ul').eq(0).find('a').eq(0);
|
||||
expect(focusEl).not.toHaveFocus();
|
||||
});
|
||||
@@ -568,7 +815,7 @@ describe('uib-dropdown', function() {
|
||||
it('should focus last list element when up arrow pressed after dropdown toggled', function() {
|
||||
$document.find('body').append(element);
|
||||
clickDropdownToggle();
|
||||
triggerKeyDown(element, 38);
|
||||
triggerKeyDown(getFocusedElement(), 38);
|
||||
|
||||
expect(element).toHaveClass(dropdownConfig.openClass);
|
||||
var focusEl = element.find('ul').eq(0).find('a').eq(1);
|
||||
@@ -578,7 +825,7 @@ describe('uib-dropdown', function() {
|
||||
it('should not change focus when other keys are pressed', function() {
|
||||
$document.find('body').append(element);
|
||||
clickDropdownToggle();
|
||||
triggerKeyDown(element, 37);
|
||||
triggerKeyDown(getFocusedElement(), 37);
|
||||
|
||||
expect(element).toHaveClass(dropdownConfig.openClass);
|
||||
var focusEl = element.find('ul').eq(0).find('a');
|
||||
@@ -589,10 +836,10 @@ describe('uib-dropdown', function() {
|
||||
it('should focus first list element when down arrow pressed 2x and up pressed 1x', function() {
|
||||
$document.find('body').append(element);
|
||||
clickDropdownToggle();
|
||||
triggerKeyDown(element, 40);
|
||||
triggerKeyDown(element, 40);
|
||||
triggerKeyDown(getFocusedElement(), 40);
|
||||
triggerKeyDown(getFocusedElement(), 40);
|
||||
|
||||
triggerKeyDown(element, 38);
|
||||
triggerKeyDown(getFocusedElement(), 38);
|
||||
|
||||
expect(element).toHaveClass(dropdownConfig.openClass);
|
||||
var focusEl = element.find('ul').eq(0).find('a').eq(0);
|
||||
@@ -602,8 +849,8 @@ describe('uib-dropdown', function() {
|
||||
it('should stay focused on final list element if down pressed at list end', function() {
|
||||
$document.find('body').append(element);
|
||||
clickDropdownToggle();
|
||||
triggerKeyDown(element, 40);
|
||||
triggerKeyDown(element, 40);
|
||||
triggerKeyDown(getFocusedElement(), 40);
|
||||
triggerKeyDown(getFocusedElement(), 40);
|
||||
|
||||
expect(element).toHaveClass(dropdownConfig.openClass);
|
||||
var focusEl = element.find('ul').eq(0).find('a').eq(1);
|
||||
@@ -618,19 +865,19 @@ describe('uib-dropdown', function() {
|
||||
$document.find('body').append(element);
|
||||
clickDropdownToggle();
|
||||
|
||||
triggerKeyDown(element, 40);
|
||||
triggerKeyDown(getFocusedElement(), 40);
|
||||
|
||||
expect(element).toHaveClass(dropdownConfig.openClass);
|
||||
var focusEl = element.find('ul').eq(0).find('a').eq(0);
|
||||
expect(focusEl).toHaveFocus();
|
||||
|
||||
triggerKeyDown(element, 27);
|
||||
triggerKeyDown(getFocusedElement(), 27);
|
||||
expect(element).not.toHaveClass(dropdownConfig.openClass);
|
||||
});
|
||||
|
||||
describe('with dropdown-append-to-body', function() {
|
||||
function dropdown() {
|
||||
return $compile('<li uib-dropdown dropdown-append-to-body keyboard-nav><a href uib-dropdown-toggle></a><ul uib-dropdown-menu id="dropdown-menu"><li><a href>Hello On Body</a></li><li><a href>Hello Again</a></li></ul></li>')($rootScope);
|
||||
return $compile('<li uib-dropdown dropdown-append-to-body keyboard-nav><a href uib-dropdown-toggle>foo</a><ul uib-dropdown-menu id="dropdown-menu"><li><a href>Hello On Body</a></li><li><a href>Hello Again</a></li></ul></li>')($rootScope);
|
||||
}
|
||||
|
||||
beforeEach(function() {
|
||||
@@ -638,24 +885,25 @@ describe('uib-dropdown', function() {
|
||||
});
|
||||
|
||||
it('should focus first list element when down arrow pressed', function() {
|
||||
$document.find('body').append(element);
|
||||
clickDropdownToggle();
|
||||
|
||||
triggerKeyDown(element, 40);
|
||||
|
||||
var dropdownMenu = $document.find('#dropdown-menu');
|
||||
|
||||
triggerKeyDown(getFocusedElement(), 40);
|
||||
|
||||
expect(dropdownMenu.parent()).toHaveClass(dropdownConfig.appendToOpenClass);
|
||||
var focusEl = $document.find('ul').eq(0).find('a');
|
||||
expect(focusEl).toHaveFocus();
|
||||
});
|
||||
|
||||
it('should focus second list element when down arrow pressed twice', function() {
|
||||
$document.find('body').append(element);
|
||||
clickDropdownToggle();
|
||||
triggerKeyDown(element, 40);
|
||||
triggerKeyDown(element, 40);
|
||||
triggerKeyDown(element, 40);
|
||||
|
||||
var dropdownMenu = $document.find('#dropdown-menu');
|
||||
triggerKeyDown(getFocusedElement(), 40);
|
||||
triggerKeyDown(getFocusedElement(), 40);
|
||||
triggerKeyDown(getFocusedElement(), 40);
|
||||
|
||||
expect(dropdownMenu.parent()).toHaveClass(dropdownConfig.appendToOpenClass);
|
||||
var elem1 = $document.find('ul');
|
||||
@@ -665,4 +913,34 @@ describe('uib-dropdown', function() {
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// issue #5942
|
||||
describe('using dropdown-append-to-body with dropdown-menu-right class', function() {
|
||||
function dropdown() {
|
||||
return $compile('<li style="float: right;" uib-dropdown dropdown-append-to-body><a href uib-dropdown-toggle>Toggle menu</a><ul uib-dropdown-menu class="dropdown-menu-right" id="dropdown-menu"><li><a href>Hello On Body</a></li></ul></li>')($rootScope);
|
||||
}
|
||||
|
||||
beforeEach(function() {
|
||||
element = dropdown();
|
||||
$document.find('body').append(element);
|
||||
|
||||
var menu = $document.find('#dropdown-menu');
|
||||
menu.css('position', 'absolute');
|
||||
});
|
||||
|
||||
afterEach(function() {
|
||||
element.remove();
|
||||
});
|
||||
|
||||
it('should align the menu correctly when the body has no vertical scrollbar', function() {
|
||||
var toggle = element.find('[uib-dropdown-toggle]');
|
||||
var menu = $document.find('#dropdown-menu');
|
||||
toggle.trigger('click');
|
||||
|
||||
// Get the offsets of the rightmost position of both the toggle and the menu (offset from the left of the window)
|
||||
var toggleRight = Math.round(toggle.offset().left + toggle.outerWidth());
|
||||
var menuRight = Math.round(menu.offset().left + menu.outerWidth());
|
||||
expect(menuRight).toBe(toggleRight);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
+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,11 +1,11 @@
|
||||
require('../multiMap');
|
||||
require('../position/index-nocss.js');
|
||||
require('../stackedMap');
|
||||
require('../../template/modal/backdrop.html.js');
|
||||
require('../../template/modal/window.html.js');
|
||||
require('./modal');
|
||||
|
||||
var MODULE_NAME = 'ui.bootstrap.module.modal';
|
||||
|
||||
angular.module(MODULE_NAME, ['ui.bootstrap.modal', 'uib/template/modal/backdrop.html', 'uib/template/modal/window.html']);
|
||||
angular.module(MODULE_NAME, ['ui.bootstrap.modal', 'uib/template/modal/window.html']);
|
||||
|
||||
module.exports = MODULE_NAME;
|
||||
|
||||
+189
-120
@@ -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
|
||||
@@ -106,8 +51,7 @@ angular.module('ui.bootstrap.modal', ['ui.bootstrap.stackedMap', 'ui.bootstrap.p
|
||||
.directive('uibModalBackdrop', ['$animate', '$injector', '$uibModalStack',
|
||||
function($animate, $injector, $modalStack) {
|
||||
return {
|
||||
replace: true,
|
||||
templateUrl: 'uib/template/modal/backdrop.html',
|
||||
restrict: 'A',
|
||||
compile: function(tElement, tAttrs) {
|
||||
tElement.addClass(tAttrs.backdropClass);
|
||||
return linkFn;
|
||||
@@ -136,13 +80,12 @@ angular.module('ui.bootstrap.modal', ['ui.bootstrap.stackedMap', 'ui.bootstrap.p
|
||||
scope: {
|
||||
index: '@'
|
||||
},
|
||||
replace: true,
|
||||
restrict: 'A',
|
||||
transclude: true,
|
||||
templateUrl: function(tElement, tAttrs) {
|
||||
return tAttrs.templateUrl || 'uib/template/modal/window.html';
|
||||
},
|
||||
link: function(scope, element, attrs) {
|
||||
element.addClass(attrs.windowClass || '');
|
||||
element.addClass(attrs.windowTopClass || '');
|
||||
scope.size = attrs.size;
|
||||
|
||||
@@ -165,14 +108,11 @@ 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();
|
||||
// Observe function will be called on next digest cycle after compilation, ensuring that the DOM is ready.
|
||||
// In order to use this way of finding whether DOM is ready, we need to observe a scope property used in modal's template.
|
||||
attrs.$observe('modalRender', function(value) {
|
||||
if (value === 'true') {
|
||||
modalRenderDeferObj.resolve();
|
||||
}
|
||||
// Resolve render promise post-digest
|
||||
scope.$$postDigest(function() {
|
||||
modalRenderDeferObj.resolve();
|
||||
});
|
||||
|
||||
modalRenderDeferObj.promise.then(function() {
|
||||
@@ -201,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]');
|
||||
@@ -235,16 +175,16 @@ angular.module('ui.bootstrap.modal', ['ui.bootstrap.stackedMap', 'ui.bootstrap.p
|
||||
};
|
||||
})
|
||||
|
||||
.directive('uibModalTransclude', function() {
|
||||
.directive('uibModalTransclude', ['$animate', function($animate) {
|
||||
return {
|
||||
link: function(scope, element, attrs, controller, transclude) {
|
||||
transclude(scope.$parent, function(clone) {
|
||||
element.empty();
|
||||
element.append(clone);
|
||||
$animate.enter(clone, element);
|
||||
});
|
||||
}
|
||||
};
|
||||
})
|
||||
}])
|
||||
|
||||
.factory('$uibModalStack', ['$animate', '$animateCss', '$document',
|
||||
'$compile', '$rootScope', '$q', '$$multiMap', '$$stackedMap', '$uibPosition',
|
||||
@@ -259,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 tabableSelector = 'a[href], area[href], input:not([disabled]), ' +
|
||||
'button:not([disabled]),select:not([disabled]), textarea:not([disabled]), ' +
|
||||
'iframe, object, embed, *[tabindex], *[contenteditable=true]';
|
||||
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 ||
|
||||
@@ -380,6 +330,10 @@ angular.module('ui.bootstrap.modal', ['ui.bootstrap.stackedMap', 'ui.bootstrap.p
|
||||
afterAnimating.done = true;
|
||||
|
||||
$animate.leave(domEl).then(function() {
|
||||
if (done) {
|
||||
done();
|
||||
}
|
||||
|
||||
domEl.remove();
|
||||
if (closedDeferred) {
|
||||
closedDeferred.resolve();
|
||||
@@ -387,9 +341,6 @@ angular.module('ui.bootstrap.modal', ['ui.bootstrap.stackedMap', 'ui.bootstrap.p
|
||||
});
|
||||
|
||||
scope.$destroy();
|
||||
if (done) {
|
||||
done();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -468,66 +419,152 @@ 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;
|
||||
backdropScope.index = currBackdropIndex;
|
||||
backdropDomEl = angular.element('<div uib-modal-backdrop="modal-backdrop"></div>');
|
||||
backdropDomEl.attr('backdrop-class', modal.backdropClass);
|
||||
backdropDomEl.attr({
|
||||
'class': 'modal-backdrop',
|
||||
'ng-style': '{\'z-index\': 1040 + (index && 1 || 0) + index*10}',
|
||||
'uib-modal-animation-class': 'fade',
|
||||
'modal-in-class': 'in'
|
||||
});
|
||||
if (modal.backdropClass) {
|
||||
backdropDomEl.addClass(modal.backdropClass);
|
||||
}
|
||||
|
||||
if (modal.animation) {
|
||||
backdropDomEl.attr('modal-animation', 'true');
|
||||
}
|
||||
$compile(backdropDomEl)(backdropScope);
|
||||
$animate.enter(backdropDomEl, appendToElement);
|
||||
scrollbarPadding = $uibPosition.scrollbarPadding(appendToElement);
|
||||
if (scrollbarPadding.heightOverflow && scrollbarPadding.scrollbarWidth) {
|
||||
appendToElement.css({paddingRight: scrollbarPadding.right + 'px'});
|
||||
if ($uibPosition.isScrollable(appendToElement)) {
|
||||
scrollbarPadding = $uibPosition.scrollbarPadding(appendToElement);
|
||||
if (scrollbarPadding.heightOverflow && scrollbarPadding.scrollbarWidth) {
|
||||
appendToElement.css({paddingRight: scrollbarPadding.right + 'px'});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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>');
|
||||
angularDomEl.attr({
|
||||
'class': 'modal',
|
||||
'template-url': modal.windowTemplateUrl,
|
||||
'window-class': modal.windowClass,
|
||||
'window-top-class': modal.windowTopClass,
|
||||
'role': 'dialog',
|
||||
'aria-labelledby': modal.ariaLabelledBy,
|
||||
'aria-describedby': modal.ariaDescribedBy,
|
||||
'size': modal.size,
|
||||
'index': topModalIndex,
|
||||
'animate': 'animate'
|
||||
}).html(modal.content);
|
||||
'animate': 'animate',
|
||||
'ng-style': '{\'z-index\': 1050 + $$topModalIndex*10, display: \'block\'}',
|
||||
'tabindex': -1,
|
||||
'uib-modal-animation-class': 'fade',
|
||||
'modal-in-class': 'in'
|
||||
}).append(content);
|
||||
if (modal.windowClass) {
|
||||
angularDomEl.addClass(modal.windowClass);
|
||||
}
|
||||
|
||||
if (modal.animation) {
|
||||
angularDomEl.attr('modal-animation', 'true');
|
||||
}
|
||||
|
||||
appendToElement.addClass(modalBodyClass);
|
||||
if (modal.scope) {
|
||||
// we need to explicitly add the modal index to the modal scope
|
||||
// because it is needed by ngStyle to compute the zIndex property.
|
||||
modal.scope.$$topModalIndex = topModalIndex;
|
||||
}
|
||||
$animate.enter($compile(angularDomEl)(modal.scope), appendToElement);
|
||||
|
||||
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);
|
||||
@@ -599,7 +636,7 @@ angular.module('ui.bootstrap.modal', ['ui.bootstrap.stackedMap', 'ui.bootstrap.p
|
||||
if (modalWindow) {
|
||||
var modalDomE1 = modalWindow.value.modalDomEl;
|
||||
if (modalDomE1 && modalDomE1.length) {
|
||||
var elements = modalDomE1[0].querySelectorAll(tabableSelector);
|
||||
var elements = modalDomE1[0].querySelectorAll(tabbableSelector);
|
||||
return elements ?
|
||||
Array.prototype.filter.call(elements, function(element) {
|
||||
return isVisible(element);
|
||||
@@ -658,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;
|
||||
@@ -690,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
|
||||
@@ -721,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);
|
||||
|
||||
+349
-31
@@ -46,13 +46,68 @@ describe('$uibResolve', function() {
|
||||
});
|
||||
});
|
||||
|
||||
describe('uibModalTransclude', function() {
|
||||
var uibModalTranscludeDDO,
|
||||
$animate;
|
||||
|
||||
beforeEach(module('ui.bootstrap.modal'));
|
||||
beforeEach(module(function($provide) {
|
||||
$animate = jasmine.createSpyObj('$animate', ['enter']);
|
||||
$provide.value('$animate', $animate);
|
||||
}));
|
||||
|
||||
beforeEach(inject(function(uibModalTranscludeDirective) {
|
||||
uibModalTranscludeDDO = uibModalTranscludeDirective[0];
|
||||
}));
|
||||
|
||||
describe('when initialised', function() {
|
||||
var scope,
|
||||
element,
|
||||
transcludeSpy,
|
||||
transcludeFn;
|
||||
|
||||
beforeEach(function() {
|
||||
scope = {
|
||||
$parent: 'parentScope'
|
||||
};
|
||||
|
||||
element = jasmine.createSpyObj('containerElement', ['empty']);
|
||||
transcludeSpy = jasmine.createSpy('transcludeSpy').and.callFake(function(scope, fn) {
|
||||
transcludeFn = fn;
|
||||
});
|
||||
|
||||
uibModalTranscludeDDO.link(scope, element, {}, {}, transcludeSpy);
|
||||
});
|
||||
|
||||
it('should call the transclusion function', function() {
|
||||
expect(transcludeSpy).toHaveBeenCalledWith(scope.$parent, jasmine.any(Function));
|
||||
});
|
||||
|
||||
describe('transclusion callback', function() {
|
||||
var transcludedContent;
|
||||
|
||||
beforeEach(function() {
|
||||
transcludedContent = 'my transcluded content';
|
||||
transcludeFn(transcludedContent);
|
||||
});
|
||||
|
||||
it('should empty the element', function() {
|
||||
expect(element.empty).toHaveBeenCalledWith();
|
||||
});
|
||||
|
||||
it('should append the transcluded content', function() {
|
||||
expect($animate.enter).toHaveBeenCalledWith(transcludedContent, element);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('$uibModal', function() {
|
||||
var $animate, $controllerProvider, $rootScope, $document, $compile, $templateCache, $timeout, $q;
|
||||
var $uibModal, $uibModalStack, $uibModalProvider;
|
||||
|
||||
beforeEach(module('ngAnimateMock'));
|
||||
beforeEach(module('ui.bootstrap.modal'));
|
||||
beforeEach(module('uib/template/modal/backdrop.html'));
|
||||
beforeEach(module('uib/template/modal/window.html'));
|
||||
beforeEach(module(function(_$controllerProvider_, _$uibModalProvider_, $compileProvider) {
|
||||
$controllerProvider = _$controllerProvider_;
|
||||
@@ -76,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>'
|
||||
});
|
||||
}));
|
||||
|
||||
@@ -96,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);
|
||||
|
||||
@@ -104,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};
|
||||
}
|
||||
};
|
||||
@@ -116,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);
|
||||
|
||||
@@ -127,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};
|
||||
}
|
||||
};
|
||||
@@ -215,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();
|
||||
@@ -445,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);
|
||||
@@ -575,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();
|
||||
@@ -617,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');
|
||||
|
||||
@@ -807,6 +890,52 @@ describe('$uibModal', function() {
|
||||
|
||||
initialPage.remove();
|
||||
});
|
||||
|
||||
it('should change focus to next tabbable element when tab is pressed', function() {
|
||||
var initialPage = angular.element('<a href="#" id="cannot-get-focus-from-modal">Outland link</a>');
|
||||
angular.element(document.body).append(initialPage);
|
||||
initialPage.focus();
|
||||
|
||||
open({
|
||||
template:'<button id="tab-focus-button1" tabindex="-1">Skip me!</button><a href="#" id="tab-focus-link1">a</a>' +
|
||||
'<a href="#" id="tab-focus-link2">b</a><a href="#" id="tab-focus-link3">c</a>' +
|
||||
'<button id="tab-focus-button2" tabindex="-1">Skip me!</button>',
|
||||
keyboard: false
|
||||
});
|
||||
$rootScope.$digest();
|
||||
expect($document).toHaveModalsOpen(1);
|
||||
|
||||
$('#tab-focus-link3').focus();
|
||||
expect(document.activeElement.getAttribute('id')).toBe('tab-focus-link3');
|
||||
|
||||
triggerKeyDown(angular.element(document.activeElement), 9, false);
|
||||
expect(document.activeElement.getAttribute('id')).toBe('tab-focus-link1');
|
||||
|
||||
initialPage.remove();
|
||||
});
|
||||
|
||||
it('should change focus to previous tabbable element when shift+tab is pressed', function() {
|
||||
var initialPage = angular.element('<a href="#" id="cannot-get-focus-from-modal">Outland link</a>');
|
||||
angular.element(document.body).append(initialPage);
|
||||
initialPage.focus();
|
||||
|
||||
open({
|
||||
template:'<button id="tab-focus-button1" tabindex="-1">Skip me!</button><a href="#" id="tab-focus-link1">a</a>' +
|
||||
'<a href="#" id="tab-focus-link2">b</a><a href="#" id="tab-focus-link3">c</a>' +
|
||||
'<button id="tab-focus-button2" tabindex="-1">Skip me!</button>',
|
||||
keyboard: false
|
||||
});
|
||||
$rootScope.$digest();
|
||||
expect($document).toHaveModalsOpen(1);
|
||||
|
||||
$('#tab-focus-link1').focus();
|
||||
expect(document.activeElement.getAttribute('id')).toBe('tab-focus-link1');
|
||||
|
||||
triggerKeyDown(angular.element(document.activeElement), 9, true);
|
||||
expect(document.activeElement.getAttribute('id')).toBe('tab-focus-link3');
|
||||
|
||||
initialPage.remove();
|
||||
});
|
||||
});
|
||||
|
||||
describe('default options can be changed in a provider', function() {
|
||||
@@ -829,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');
|
||||
@@ -1289,7 +1491,7 @@ describe('$uibModal', function() {
|
||||
expect(body).not.toHaveClass('modal-open');
|
||||
});
|
||||
|
||||
it('should remove the custom class on closing of modal', function() {
|
||||
it('should remove the custom class on closing of modal after animations have completed', function() {
|
||||
var modal = open({
|
||||
template: '<div>dummy modal</div>',
|
||||
openedClass: 'foo'
|
||||
@@ -1297,7 +1499,13 @@ describe('$uibModal', function() {
|
||||
|
||||
expect(body).toHaveClass('foo');
|
||||
|
||||
close(modal);
|
||||
close(modal, null, true);
|
||||
expect(body).toHaveClass('foo');
|
||||
|
||||
$animate.flush();
|
||||
$rootScope.$digest();
|
||||
$animate.flush();
|
||||
$rootScope.$digest();
|
||||
|
||||
expect(body).not.toHaveClass('foo');
|
||||
});
|
||||
@@ -1358,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() {
|
||||
@@ -1369,15 +1599,6 @@ describe('$uibModal', function() {
|
||||
expect($rootScope.foo).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should support custom CSS classes as string', function() {
|
||||
$rootScope.animate = false;
|
||||
var windowEl = $compile('<div uib-modal-window animate="animate" window-class="test foo">content</div>')($rootScope);
|
||||
$rootScope.$digest();
|
||||
|
||||
expect(windowEl).toHaveClass('test');
|
||||
expect(windowEl).toHaveClass('foo');
|
||||
});
|
||||
|
||||
it('should support window top class', function () {
|
||||
$rootScope.animate = false;
|
||||
var windowEl = $compile('<div uib-modal-window animate="animate" window-top-class="test foo">content</div>')($rootScope);
|
||||
@@ -1388,13 +1609,12 @@ describe('$uibModal', function() {
|
||||
});
|
||||
|
||||
it('should support custom template url', inject(function($templateCache) {
|
||||
$templateCache.put('window.html', '<div class="mywindow" ng-transclude></div>');
|
||||
$templateCache.put('window.html', '<div ng-transclude></div>');
|
||||
|
||||
var windowEl = $compile('<div uib-modal-window template-url="window.html" window-class="test">content</div>')($rootScope);
|
||||
var windowEl = $compile('<div uib-modal-window template-url="window.html">content</div>')($rootScope);
|
||||
$rootScope.$digest();
|
||||
|
||||
expect(windowEl).toHaveClass('mywindow');
|
||||
expect(windowEl).toHaveClass('test');
|
||||
expect(windowEl.html()).toBe('<div ng-transclude="">content</div>');
|
||||
}));
|
||||
});
|
||||
|
||||
@@ -1518,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; }
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1680,7 +1903,6 @@ describe('$uibModal', function() {
|
||||
content: '<div>Modal1</div>'
|
||||
});
|
||||
|
||||
expect($document).toHaveModalsOpen(0);
|
||||
$rootScope.$digest();
|
||||
$animate.flush();
|
||||
expect($document).toHaveModalsOpen(1);
|
||||
@@ -1700,7 +1922,6 @@ describe('$uibModal', function() {
|
||||
modal2Index = parseInt($uibModalStack.getTop().value.modalDomEl.attr('index'), 10);
|
||||
});
|
||||
|
||||
expect($document).toHaveModalsOpen(1);
|
||||
$rootScope.$digest();
|
||||
$animate.flush();
|
||||
expect($document).toHaveModalsOpen(2);
|
||||
@@ -1722,7 +1943,6 @@ describe('$uibModal', function() {
|
||||
modal3Index = parseInt($uibModalStack.getTop().value.modalDomEl.attr('index'), 10);
|
||||
});
|
||||
|
||||
expect($document).toHaveModalsOpen(1);
|
||||
$rootScope.$digest();
|
||||
$animate.flush();
|
||||
expect($document).toHaveModalsOpen(2);
|
||||
@@ -1730,6 +1950,104 @@ describe('$uibModal', function() {
|
||||
expect(modal3Index).toEqual(2);
|
||||
expect(modal2Index).toBeLessThan(modal3Index);
|
||||
});
|
||||
|
||||
it('should have top modal with highest z-index', function() {
|
||||
var modal2zIndex = null;
|
||||
var modal3zIndex = null;
|
||||
|
||||
var modal1Instance = {
|
||||
result: $q.defer(),
|
||||
opened: $q.defer(),
|
||||
closed: $q.defer(),
|
||||
rendered: $q.defer(),
|
||||
close: function(result) {
|
||||
return $uibModalStack.close(modal1Instance, result);
|
||||
},
|
||||
dismiss: function(reason) {
|
||||
return $uibModalStack.dismiss(modal1Instance, reason);
|
||||
}
|
||||
};
|
||||
var modal2Instance = {
|
||||
result: $q.defer(),
|
||||
opened: $q.defer(),
|
||||
closed: $q.defer(),
|
||||
rendered: $q.defer(),
|
||||
close: function(result) {
|
||||
return $uibModalStack.close(modal2Instance, result);
|
||||
},
|
||||
dismiss: function(reason) {
|
||||
return $uibModalStack.dismiss(modal2Instance, reason);
|
||||
}
|
||||
};
|
||||
var modal3Instance = {
|
||||
result: $q.defer(),
|
||||
opened: $q.defer(),
|
||||
closed: $q.defer(),
|
||||
rendered: $q.defer(),
|
||||
close: function(result) {
|
||||
return $uibModalStack.close(modal3Instance, result);
|
||||
},
|
||||
dismiss: function(reason) {
|
||||
return $uibModalStack.dismiss(modal3Instance, reason);
|
||||
}
|
||||
};
|
||||
|
||||
var modal1 = $uibModalStack.open(modal1Instance, {
|
||||
appendTo: angular.element(document.body),
|
||||
scope: $rootScope.$new(),
|
||||
deferred: modal1Instance.result,
|
||||
renderDeferred: modal1Instance.rendered,
|
||||
closedDeferred: modal1Instance.closed,
|
||||
content: '<div>Modal1</div>'
|
||||
});
|
||||
|
||||
$rootScope.$digest();
|
||||
$animate.flush();
|
||||
expect($document).toHaveModalsOpen(1);
|
||||
|
||||
expect(+$uibModalStack.getTop().value.modalDomEl[0].style.zIndex).toBe(1050);
|
||||
|
||||
var modal2 = $uibModalStack.open(modal2Instance, {
|
||||
appendTo: angular.element(document.body),
|
||||
scope: $rootScope.$new(),
|
||||
deferred: modal2Instance.result,
|
||||
renderDeferred: modal2Instance.rendered,
|
||||
closedDeferred: modal2Instance.closed,
|
||||
content: '<div>Modal2</div>'
|
||||
});
|
||||
|
||||
modal2Instance.rendered.promise.then(function() {
|
||||
modal2zIndex = +$uibModalStack.getTop().value.modalDomEl[0].style.zIndex;
|
||||
});
|
||||
|
||||
$rootScope.$digest();
|
||||
$animate.flush();
|
||||
expect($document).toHaveModalsOpen(2);
|
||||
|
||||
expect(modal2zIndex).toBe(1060);
|
||||
close(modal1Instance);
|
||||
expect($document).toHaveModalsOpen(1);
|
||||
|
||||
var modal3 = $uibModalStack.open(modal3Instance, {
|
||||
appendTo: angular.element(document.body),
|
||||
scope: $rootScope.$new(),
|
||||
deferred: modal3Instance.result,
|
||||
renderDeferred: modal3Instance.rendered,
|
||||
closedDeferred: modal3Instance.closed,
|
||||
content: '<div>Modal3</div>'
|
||||
});
|
||||
|
||||
modal3Instance.rendered.promise.then(function() {
|
||||
modal3zIndex = +$uibModalStack.getTop().value.modalDomEl[0].style.zIndex;
|
||||
});
|
||||
|
||||
$rootScope.$digest();
|
||||
$animate.flush();
|
||||
expect($document).toHaveModalsOpen(2);
|
||||
|
||||
expect(modal3zIndex).toBe(1070);
|
||||
expect(modal2zIndex).toBeLessThan(modal3zIndex);
|
||||
});
|
||||
});
|
||||
|
||||
describe('modal.closing event', function() {
|
||||
|
||||
@@ -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();
|
||||
}));
|
||||
@@ -1,5 +1,5 @@
|
||||
<div ng-controller="PagerDemoCtrl">
|
||||
<h4>Pager</h4>
|
||||
<pre>You are currently on page {{currentPage}}</pre>
|
||||
<uib-pager total-items="totalItems" ng-model="currentPage"></uib-pager>
|
||||
<ul uib-pager total-items="totalItems" ng-model="currentPage"></ul>
|
||||
</div>
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
require('../paging');
|
||||
require('../tabindex');
|
||||
require('../../template/pager/pager.html.js');
|
||||
require('./pager');
|
||||
|
||||
|
||||
+3
-2
@@ -1,4 +1,4 @@
|
||||
angular.module('ui.bootstrap.pager', ['ui.bootstrap.paging'])
|
||||
angular.module('ui.bootstrap.pager', ['ui.bootstrap.paging', 'ui.bootstrap.tabindex'])
|
||||
|
||||
.controller('UibPagerController', ['$scope', '$attrs', 'uibPaging', 'uibPagerConfig', function($scope, $attrs, uibPaging, uibPagerConfig) {
|
||||
$scope.align = angular.isDefined($attrs.align) ? $scope.$parent.$eval($attrs.align) : uibPagerConfig.align;
|
||||
@@ -22,13 +22,14 @@ angular.module('ui.bootstrap.pager', ['ui.bootstrap.paging'])
|
||||
ngDisabled: '='
|
||||
},
|
||||
require: ['uibPager', '?ngModel'],
|
||||
restrict: 'A',
|
||||
controller: 'UibPagerController',
|
||||
controllerAs: 'pager',
|
||||
templateUrl: function(element, attrs) {
|
||||
return attrs.templateUrl || 'uib/template/pager/pager.html';
|
||||
},
|
||||
replace: true,
|
||||
link: function(scope, element, attrs, ctrls) {
|
||||
element.addClass('pager');
|
||||
var paginationCtrl = ctrls[0], ngModelCtrl = ctrls[1];
|
||||
|
||||
if (!ngModelCtrl) {
|
||||
|
||||
@@ -10,7 +10,7 @@ describe('pager directive', function() {
|
||||
$document = _$document_;
|
||||
$templateCache = _$templateCache_;
|
||||
body = $document.find('body');
|
||||
element = $compile('<uib-pager total-items="total" ng-model="currentPage"></uib-pager>')($rootScope);
|
||||
element = $compile('<ul uib-pager total-items="total" ng-model="currentPage"></ul>')($rootScope);
|
||||
$rootScope.$digest();
|
||||
}));
|
||||
|
||||
@@ -56,7 +56,7 @@ describe('pager directive', function() {
|
||||
it('exposes the controller on the template', function() {
|
||||
$templateCache.put('uib/template/pager/pager.html', '<div>{{pager.text}}</div>');
|
||||
|
||||
element = $compile('<uib-pager></uib-pager>')($rootScope);
|
||||
element = $compile('<ul uib-pager></ul uib-pager>')($rootScope);
|
||||
$rootScope.$digest();
|
||||
|
||||
var ctrl = element.controller('uibPager');
|
||||
@@ -65,7 +65,7 @@ describe('pager directive', function() {
|
||||
ctrl.text = 'foo';
|
||||
$rootScope.$digest();
|
||||
|
||||
expect(element.html()).toBe('foo');
|
||||
expect(element.html()).toBe('<div class="ng-binding">foo</div>');
|
||||
});
|
||||
|
||||
it('disables the "previous" link if current page is 1', function() {
|
||||
@@ -102,7 +102,7 @@ describe('pager directive', function() {
|
||||
|
||||
it('executes the `ng-change` expression when an element is clicked', function() {
|
||||
$rootScope.selectPageHandler = jasmine.createSpy('selectPageHandler');
|
||||
element = $compile('<uib-pager total-items="total" ng-model="currentPage" ng-change="selectPageHandler()"></uib-pager>')($rootScope);
|
||||
element = $compile('<ul uib-pager total-items="total" ng-model="currentPage" ng-change="selectPageHandler()"></ul>')($rootScope);
|
||||
$rootScope.$digest();
|
||||
|
||||
clickPaginationEl(-1);
|
||||
@@ -147,16 +147,16 @@ describe('pager directive', function() {
|
||||
it('allows custom templates', function() {
|
||||
$templateCache.put('foo/bar.html', '<div>baz</div>');
|
||||
|
||||
element = $compile('<uib-pager template-url="foo/bar.html"></uib-pager>')($rootScope);
|
||||
element = $compile('<ul uib-pager template-url="foo/bar.html"></ul>')($rootScope);
|
||||
$rootScope.$digest();
|
||||
|
||||
expect(element.html()).toBe('baz');
|
||||
expect(element.html()).toBe('<div>baz</div>');
|
||||
});
|
||||
|
||||
describe('`items-per-page`', function() {
|
||||
beforeEach(function() {
|
||||
$rootScope.perpage = 5;
|
||||
element = $compile('<uib-pager total-items="total" items-per-page="perpage" ng-model="currentPage"></uib-pager>')($rootScope);
|
||||
element = $compile('<ul uib-pager total-items="total" items-per-page="perpage" ng-model="currentPage"></ul>')($rootScope);
|
||||
$rootScope.$digest();
|
||||
});
|
||||
|
||||
@@ -190,7 +190,7 @@ describe('pager directive', function() {
|
||||
describe('`num-pages`', function() {
|
||||
beforeEach(function() {
|
||||
$rootScope.numpg = null;
|
||||
element = $compile('<uib-pager total-items="total" ng-model="currentPage" num-pages="numpg"></uib-pager>')($rootScope);
|
||||
element = $compile('<ul uib-pager total-items="total" ng-model="currentPage" num-pages="numpg"></ul>')($rootScope);
|
||||
$rootScope.$digest();
|
||||
});
|
||||
|
||||
@@ -206,7 +206,7 @@ describe('pager directive', function() {
|
||||
uibPagerConfig.previousText = 'PR';
|
||||
uibPagerConfig.nextText = 'NE';
|
||||
uibPagerConfig.align = false;
|
||||
element = $compile('<uib-pager total-items="total" ng-model="currentPage"></uib-pager>')($rootScope);
|
||||
element = $compile('<ul uib-pager total-items="total" ng-model="currentPage"></ul>')($rootScope);
|
||||
$rootScope.$digest();
|
||||
}));
|
||||
afterEach(inject(function(uibPagerConfig) {
|
||||
@@ -227,7 +227,7 @@ describe('pager directive', function() {
|
||||
|
||||
describe('override configuration from attributes', function() {
|
||||
beforeEach(function() {
|
||||
element = $compile('<uib-pager align="false" previous-text="<" next-text=">" total-items="total" ng-model="currentPage"></uib-pager>')($rootScope);
|
||||
element = $compile('<ul uib-pager align="false" previous-text="<" next-text=">" total-items="total" ng-model="currentPage"></ul>')($rootScope);
|
||||
$rootScope.$digest();
|
||||
});
|
||||
|
||||
@@ -248,7 +248,7 @@ describe('pager directive', function() {
|
||||
it('changes "previous" & "next" text from interpolated attributes', function() {
|
||||
$rootScope.previousText = '<<';
|
||||
$rootScope.nextText = '>>';
|
||||
element = $compile('<uib-pager align="false" previous-text="{{previousText}}" next-text="{{nextText}}" total-items="total" ng-model="currentPage"></uib-pager>')($rootScope);
|
||||
element = $compile('<ul uib-pager align="false" previous-text="{{previousText}}" next-text="{{nextText}}" total-items="total" ng-model="currentPage"></ul>')($rootScope);
|
||||
$rootScope.$digest();
|
||||
|
||||
expect(getPaginationEl(0).text()).toBe('<<');
|
||||
@@ -259,7 +259,7 @@ describe('pager directive', function() {
|
||||
it('disables the component when ng-disabled is true', function() {
|
||||
$rootScope.disable = true;
|
||||
|
||||
element = $compile('<uib-pager total-items="total" ng-disabled="disable" ng-model="currentPage"></uib-pager>')($rootScope);
|
||||
element = $compile('<ul uib-pager total-items="total" ng-disabled="disable" ng-model="currentPage"></ul>')($rootScope);
|
||||
$rootScope.$digest();
|
||||
updateCurrentPage(2);
|
||||
|
||||
|
||||
@@ -1,24 +1,24 @@
|
||||
<div ng-controller="PaginationDemoCtrl">
|
||||
<h4>Default</h4>
|
||||
<uib-pagination total-items="totalItems" ng-model="currentPage" ng-change="pageChanged()"></uib-pagination>
|
||||
<uib-pagination boundary-links="true" total-items="totalItems" ng-model="currentPage" class="pagination-sm" previous-text="‹" next-text="›" first-text="«" last-text="»"></uib-pagination>
|
||||
<uib-pagination direction-links="false" boundary-links="true" total-items="totalItems" ng-model="currentPage"></uib-pagination>
|
||||
<uib-pagination direction-links="false" total-items="totalItems" ng-model="currentPage" num-pages="smallnumPages"></uib-pagination>
|
||||
<ul uib-pagination total-items="totalItems" ng-model="currentPage" ng-change="pageChanged()"></ul>
|
||||
<ul uib-pagination boundary-links="true" total-items="totalItems" ng-model="currentPage" class="pagination-sm" previous-text="‹" next-text="›" first-text="«" last-text="»"></ul>
|
||||
<ul uib-pagination direction-links="false" boundary-links="true" total-items="totalItems" ng-model="currentPage"></ul>
|
||||
<ul uib-pagination direction-links="false" total-items="totalItems" ng-model="currentPage" num-pages="smallnumPages"></ul>
|
||||
<pre>The selected page no: {{currentPage}}</pre>
|
||||
<button type="button" class="btn btn-info" ng-click="setPage(3)">Set current page to: 3</button>
|
||||
|
||||
<hr />
|
||||
<h4>Limit the maximum visible buttons</h4>
|
||||
<h6><code>rotate</code> defaulted to <code>true</code>:</h6>
|
||||
<uib-pagination total-items="bigTotalItems" ng-model="bigCurrentPage" max-size="maxSize" class="pagination-sm" boundary-links="true" num-pages="numPages"></uib-pagination>
|
||||
<ul uib-pagination total-items="bigTotalItems" ng-model="bigCurrentPage" max-size="maxSize" class="pagination-sm" boundary-links="true" num-pages="numPages"></ul>
|
||||
<h6><code>rotate</code> defaulted to <code>true</code> and <code>force-ellipses</code> set to <code>true</code>:</h6>
|
||||
<uib-pagination total-items="bigTotalItems" ng-model="bigCurrentPage" max-size="maxSize" class="pagination-sm" boundary-links="true" force-ellipses="true"></uib-pagination>
|
||||
<ul uib-pagination total-items="bigTotalItems" ng-model="bigCurrentPage" max-size="maxSize" class="pagination-sm" boundary-links="true" force-ellipses="true"></ul>
|
||||
<h6><code>rotate</code> set to <code>false</code>:</h6>
|
||||
<uib-pagination total-items="bigTotalItems" ng-model="bigCurrentPage" max-size="maxSize" class="pagination-sm" boundary-links="true" rotate="false"></uib-pagination>
|
||||
<ul uib-pagination total-items="bigTotalItems" ng-model="bigCurrentPage" max-size="maxSize" class="pagination-sm" boundary-links="true" rotate="false"></ul>
|
||||
<h6><code>boundary-link-numbers</code> set to <code>true</code> and <code>rotate</code> defaulted to <code>true</code>:</h6>
|
||||
<uib-pagination total-items="bigTotalItems" ng-model="bigCurrentPage" max-size="maxSize" class="pagination-sm" boundary-link-numbers="true"></uib-pagination>
|
||||
<ul uib-pagination total-items="bigTotalItems" ng-model="bigCurrentPage" max-size="maxSize" class="pagination-sm" boundary-link-numbers="true"></ul>
|
||||
<h6><code>boundary-link-numbers</code> set to <code>true</code> and <code>rotate</code> set to <code>false</code>:</h6>
|
||||
<uib-pagination total-items="bigTotalItems" ng-model="bigCurrentPage" max-size="maxSize" class="pagination-sm" boundary-link-numbers="true" rotate="false"></uib-pagination>
|
||||
<ul uib-pagination total-items="bigTotalItems" ng-model="bigCurrentPage" max-size="maxSize" class="pagination-sm" boundary-link-numbers="true" rotate="false"></ul>
|
||||
<pre>Page: {{bigCurrentPage}} / {{numPages}}</pre>
|
||||
|
||||
</div>
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
require('../paging');
|
||||
require('../tabindex');
|
||||
require('../../template/pagination/pagination.html.js');
|
||||
require('./pagination');
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
angular.module('ui.bootstrap.pagination', ['ui.bootstrap.paging'])
|
||||
angular.module('ui.bootstrap.pagination', ['ui.bootstrap.paging', 'ui.bootstrap.tabindex'])
|
||||
.controller('UibPaginationController', ['$scope', '$attrs', '$parse', 'uibPaging', 'uibPaginationConfig', function($scope, $attrs, $parse, uibPaging, uibPaginationConfig) {
|
||||
var ctrl = this;
|
||||
// Setup configuration parameters
|
||||
@@ -9,6 +9,7 @@ angular.module('ui.bootstrap.pagination', ['ui.bootstrap.paging'])
|
||||
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);
|
||||
|
||||
@@ -132,13 +133,14 @@ angular.module('ui.bootstrap.pagination', ['ui.bootstrap.paging'])
|
||||
ngDisabled:'='
|
||||
},
|
||||
require: ['uibPagination', '?ngModel'],
|
||||
restrict: 'A',
|
||||
controller: 'UibPaginationController',
|
||||
controllerAs: 'pagination',
|
||||
templateUrl: function(element, attrs) {
|
||||
return attrs.templateUrl || 'uib/template/pagination/pagination.html';
|
||||
},
|
||||
replace: true,
|
||||
link: function(scope, element, attrs, ctrls) {
|
||||
element.addClass('pagination');
|
||||
var paginationCtrl = ctrls[0], ngModelCtrl = ctrls[1];
|
||||
|
||||
if (!ngModelCtrl) {
|
||||
|
||||
@@ -11,7 +11,7 @@ describe('pagination directive', function() {
|
||||
$document = _$document_;
|
||||
$templateCache = _$templateCache_;
|
||||
body = $document.find('body');
|
||||
element = $compile('<uib-pagination total-items="total" ng-model="currentPage"></uib-pagination>')($rootScope);
|
||||
element = $compile('<ul uib-pagination total-items="total" ng-model="currentPage"></ul>')($rootScope);
|
||||
$rootScope.$digest();
|
||||
}));
|
||||
|
||||
@@ -54,11 +54,20 @@ 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();
|
||||
|
||||
element = $compile('<uib-pagination></uib-pagination>')(scope);
|
||||
element = $compile('<ul uib-pagination></ul>')(scope);
|
||||
$rootScope.$digest();
|
||||
|
||||
var ctrl = element.controller('uibPagination');
|
||||
@@ -68,17 +77,17 @@ describe('pagination directive', function() {
|
||||
ctrl.randomText = 'foo';
|
||||
$rootScope.$digest();
|
||||
|
||||
expect(element.html()).toBe('foo');
|
||||
expect(element.html()).toBe('<div class="ng-binding">foo</div>');
|
||||
});
|
||||
|
||||
it('allows custom templates', function() {
|
||||
$templateCache.put('foo/bar.html', '<div>baz</div>');
|
||||
var scope = $rootScope.$new();
|
||||
|
||||
element = $compile('<uib-pagination template-url="foo/bar.html"></uib-pagination>')(scope);
|
||||
element = $compile('<ul uib-pagination template-url="foo/bar.html"></ul>')(scope);
|
||||
$rootScope.$digest();
|
||||
|
||||
expect(element.html()).toBe('baz');
|
||||
expect(element.html()).toBe('<div>baz</div>');
|
||||
});
|
||||
|
||||
it('contains num-pages + 2 li elements', function() {
|
||||
@@ -213,7 +222,7 @@ describe('pagination directive', function() {
|
||||
describe('`items-per-page`', function() {
|
||||
beforeEach(function() {
|
||||
$rootScope.perpage = 5;
|
||||
element = $compile('<uib-pagination total-items="total" items-per-page="perpage" ng-model="currentPage"></uib-pagination>')($rootScope);
|
||||
element = $compile('<ul uib-pagination total-items="total" items-per-page="perpage" ng-model="currentPage"></ul>')($rootScope);
|
||||
$rootScope.$digest();
|
||||
});
|
||||
|
||||
@@ -256,7 +265,7 @@ describe('pagination directive', function() {
|
||||
describe('executes `ng-change` expression', function() {
|
||||
beforeEach(function() {
|
||||
$rootScope.selectPageHandler = jasmine.createSpy('selectPageHandler');
|
||||
element = $compile('<uib-pagination total-items="total" ng-model="currentPage" ng-change="selectPageHandler()"></uib-pagination>')($rootScope);
|
||||
element = $compile('<ul uib-pagination total-items="total" ng-model="currentPage" ng-change="selectPageHandler()"></ul>')($rootScope);
|
||||
$rootScope.$digest();
|
||||
});
|
||||
|
||||
@@ -286,7 +295,7 @@ describe('pagination directive', function() {
|
||||
$rootScope.total = 98; // 10 pages
|
||||
$rootScope.currentPage = 3;
|
||||
$rootScope.maxSize = 5;
|
||||
element = $compile('<uib-pagination total-items="total" ng-model="currentPage" max-size="maxSize"></uib-pagination>')($rootScope);
|
||||
element = $compile('<ul uib-pagination total-items="total" ng-model="currentPage" max-size="maxSize"></ul>')($rootScope);
|
||||
$rootScope.$digest();
|
||||
});
|
||||
|
||||
@@ -367,7 +376,7 @@ describe('pagination directive', function() {
|
||||
$rootScope.total = 98; // 10 pages
|
||||
$rootScope.currentPage = 3;
|
||||
$rootScope.maxSize = 5;
|
||||
element = $compile('<uib-pagination total-items="total" ng-model="currentPage" max-size="maxSize" force-ellipses="true"></uib-pagination>')($rootScope);
|
||||
element = $compile('<ul uib-pagination total-items="total" ng-model="currentPage" max-size="maxSize" force-ellipses="true"></ul>')($rootScope);
|
||||
$rootScope.$digest();
|
||||
});
|
||||
|
||||
@@ -429,7 +438,7 @@ describe('pagination directive', function() {
|
||||
$rootScope.total = 98; // 10 pages
|
||||
$rootScope.currentPage = 3;
|
||||
$rootScope.maxSize = 5;
|
||||
element = $compile('<uib-pagination total-items="total" ng-model="currentPage" max-size="maxSize" boundary-link-numbers="true"></uib-pagination>')($rootScope);
|
||||
element = $compile('<ul uib-pagination total-items="total" ng-model="currentPage" max-size="maxSize" boundary-link-numbers="true"></ul>')($rootScope);
|
||||
$rootScope.$digest();
|
||||
});
|
||||
|
||||
@@ -522,7 +531,7 @@ describe('pagination directive', function() {
|
||||
$rootScope.currentPage = 7;
|
||||
$rootScope.maxSize = 5;
|
||||
$rootScope.rotate = false;
|
||||
element = $compile('<uib-pagination total-items="total" ng-model="currentPage" max-size="maxSize" rotate="rotate"></uib-pagination>')($rootScope);
|
||||
element = $compile('<ul uib-pagination total-items="total" ng-model="currentPage" max-size="maxSize" rotate="rotate"></ul>')($rootScope);
|
||||
$rootScope.$digest();
|
||||
});
|
||||
|
||||
@@ -581,7 +590,7 @@ describe('pagination directive', function() {
|
||||
|
||||
describe('pagination directive with `boundary-links`', function() {
|
||||
beforeEach(function() {
|
||||
element = $compile('<uib-pagination boundary-links="true" total-items="total" ng-model="currentPage"></uib-pagination>')($rootScope);
|
||||
element = $compile('<ul uib-pagination boundary-links="true" total-items="total" ng-model="currentPage"></ul>')($rootScope);
|
||||
$rootScope.$digest();
|
||||
});
|
||||
|
||||
@@ -636,7 +645,7 @@ describe('pagination directive', function() {
|
||||
});
|
||||
|
||||
it('changes "first" & "last" text from attributes', function() {
|
||||
element = $compile('<uib-pagination boundary-links="true" first-text="<<<" last-text=">>>" total-items="total" ng-model="currentPage"></uib-pagination>')($rootScope);
|
||||
element = $compile('<ul uib-pagination boundary-links="true" first-text="<<<" last-text=">>>" total-items="total" ng-model="currentPage"></ul>')($rootScope);
|
||||
$rootScope.$digest();
|
||||
|
||||
expect(getPaginationEl(0).text()).toBe('<<<');
|
||||
@@ -644,7 +653,7 @@ describe('pagination directive', function() {
|
||||
});
|
||||
|
||||
it('changes "previous" & "next" text from attributes', function() {
|
||||
element = $compile('<uib-pagination boundary-links="true" previous-text="<<" next-text=">>" total-items="total" ng-model="currentPage"></uib-pagination>')($rootScope);
|
||||
element = $compile('<ul uib-pagination boundary-links="true" previous-text="<<" next-text=">>" total-items="total" ng-model="currentPage"></ul>')($rootScope);
|
||||
$rootScope.$digest();
|
||||
|
||||
expect(getPaginationEl(1).text()).toBe('<<');
|
||||
@@ -654,7 +663,7 @@ describe('pagination directive', function() {
|
||||
it('changes "first" & "last" text from interpolated attributes', function() {
|
||||
$rootScope.myfirstText = '<<<';
|
||||
$rootScope.mylastText = '>>>';
|
||||
element = $compile('<uib-pagination boundary-links="true" first-text="{{myfirstText}}" last-text="{{mylastText}}" total-items="total" ng-model="currentPage"></uib-pagination>')($rootScope);
|
||||
element = $compile('<ul uib-pagination boundary-links="true" first-text="{{myfirstText}}" last-text="{{mylastText}}" total-items="total" ng-model="currentPage"></ul>')($rootScope);
|
||||
$rootScope.$digest();
|
||||
|
||||
expect(getPaginationEl(0).text()).toBe('<<<');
|
||||
@@ -664,7 +673,7 @@ describe('pagination directive', function() {
|
||||
it('changes "previous" & "next" text from interpolated attributes', function() {
|
||||
$rootScope.previousText = '<<';
|
||||
$rootScope.nextText = '>>';
|
||||
element = $compile('<uib-pagination boundary-links="true" previous-text="{{previousText}}" next-text="{{nextText}}" total-items="total" ng-model="currentPage"></uib-pagination>')($rootScope);
|
||||
element = $compile('<ul uib-pagination boundary-links="true" previous-text="{{previousText}}" next-text="{{nextText}}" total-items="total" ng-model="currentPage"></ul>')($rootScope);
|
||||
$rootScope.$digest();
|
||||
|
||||
expect(getPaginationEl(1).text()).toBe('<<');
|
||||
@@ -700,7 +709,7 @@ describe('pagination directive', function() {
|
||||
|
||||
describe('pagination directive with just number links', function() {
|
||||
beforeEach(function() {
|
||||
element = $compile('<uib-pagination direction-links="false" total-items="total" ng-model="currentPage"></uib-pagination>')($rootScope);
|
||||
element = $compile('<ul uib-pagination direction-links="false" total-items="total" ng-model="currentPage"></ul>')($rootScope);
|
||||
$rootScope.$digest();
|
||||
});
|
||||
|
||||
@@ -752,7 +761,7 @@ describe('pagination directive', function() {
|
||||
describe('with just boundary & number links', function() {
|
||||
beforeEach(function() {
|
||||
$rootScope.directions = false;
|
||||
element = $compile('<uib-pagination boundary-links="true" direction-links="directions" total-items="total" ng-model="currentPage"></uib-pagination>')($rootScope);
|
||||
element = $compile('<ul uib-pagination boundary-links="true" direction-links="directions" total-items="total" ng-model="currentPage"></ul>')($rootScope);
|
||||
$rootScope.$digest();
|
||||
});
|
||||
|
||||
@@ -784,7 +793,7 @@ describe('pagination directive', function() {
|
||||
describe('`num-pages`', function() {
|
||||
beforeEach(function() {
|
||||
$rootScope.numpg = null;
|
||||
element = $compile('<uib-pagination total-items="total" ng-model="currentPage" num-pages="numpg"></uib-pagination>')($rootScope);
|
||||
element = $compile('<ul uib-pagination total-items="total" ng-model="currentPage" num-pages="numpg"></ul>')($rootScope);
|
||||
$rootScope.$digest();
|
||||
});
|
||||
|
||||
@@ -827,7 +836,7 @@ describe('pagination directive', function() {
|
||||
paginationConfig.previousText = 'PR';
|
||||
paginationConfig.nextText = 'NE';
|
||||
paginationConfig.lastText = 'LA';
|
||||
element = $compile('<uib-pagination total-items="total" ng-model="currentPage"></uib-pagination>')($rootScope);
|
||||
element = $compile('<ul uib-pagination total-items="total" ng-model="currentPage"></ul>')($rootScope);
|
||||
$rootScope.$digest();
|
||||
|
||||
expect(getPaginationEl(0).text()).toBe('FI');
|
||||
@@ -838,7 +847,7 @@ describe('pagination directive', function() {
|
||||
|
||||
it('contains number of pages + 2 li elements', function() {
|
||||
paginationConfig.itemsPerPage = 5;
|
||||
element = $compile('<uib-pagination total-items="total" ng-model="currentPage"></uib-pagination>')($rootScope);
|
||||
element = $compile('<ul uib-pagination total-items="total" ng-model="currentPage"></ul>')($rootScope);
|
||||
$rootScope.$digest();
|
||||
|
||||
expect(getPaginationBarSize()).toBe(12);
|
||||
@@ -846,7 +855,7 @@ describe('pagination directive', function() {
|
||||
|
||||
it('should take maxSize defaults into account', function() {
|
||||
paginationConfig.maxSize = 2;
|
||||
element = $compile('<uib-pagination total-items="total" ng-model="currentPage"></uib-pagination>')($rootScope);
|
||||
element = $compile('<ul uib-pagination total-items="total" ng-model="currentPage"></ul>')($rootScope);
|
||||
$rootScope.$digest();
|
||||
|
||||
expect(getPaginationBarSize()).toBe(4);
|
||||
@@ -854,7 +863,7 @@ describe('pagination directive', function() {
|
||||
|
||||
it('should take forceEllipses defaults into account', function () {
|
||||
paginationConfig.forceEllipses = true;
|
||||
element = $compile('<uib-pagination total-items="total" ng-model="currentPage" max-size="2"></uib-pagination>')($rootScope);
|
||||
element = $compile('<ul uib-pagination total-items="total" ng-model="currentPage" max-size="2"></ul>')($rootScope);
|
||||
$rootScope.$digest();
|
||||
|
||||
// Should contain 2 nav buttons, 2 pages, and 2 ellipsis since the currentPage defaults to 3, which is in the middle
|
||||
@@ -865,7 +874,7 @@ describe('pagination directive', function() {
|
||||
paginationConfig.boundaryLinkNumbers = true;
|
||||
$rootScope.total = 88; // 9 pages
|
||||
$rootScope.currentPage = 5;
|
||||
element = $compile('<uib-pagination total-items="total" ng-model="currentPage" max-size="3"></uib-pagination>')($rootScope);
|
||||
element = $compile('<ul uib-pagination total-items="total" ng-model="currentPage" max-size="3"></ul>')($rootScope);
|
||||
$rootScope.$digest();
|
||||
|
||||
// Should contain 2 nav buttons, 2 pages, 2 ellipsis, and 2 extra end numbers since the currentPage is in the middle
|
||||
@@ -879,7 +888,7 @@ describe('pagination directive', function() {
|
||||
$rootScope.pageLabel = function(id) {
|
||||
return 'test_'+ id;
|
||||
};
|
||||
element = $compile('<uib-pagination boundary-links="true" page-label="pageLabel($page)" first-text="<<" previous-text="<" next-text=">" last-text=">>" total-items="total" ng-model="currentPage"></uib-pagination>')($rootScope);
|
||||
element = $compile('<ul uib-pagination boundary-links="true" page-label="pageLabel($page)" first-text="<<" previous-text="<" next-text=">" last-text=">>" total-items="total" ng-model="currentPage"></ul>')($rootScope);
|
||||
$rootScope.$digest();
|
||||
});
|
||||
|
||||
@@ -904,7 +913,7 @@ describe('pagination directive', function() {
|
||||
|
||||
describe('disabled with ngDisable', function() {
|
||||
beforeEach(function() {
|
||||
element = $compile('<uib-pagination total-items="total" ng-model="currentPage" ng-disabled="disabled"></uib-pagination>')($rootScope);
|
||||
element = $compile('<ul uib-pagination total-items="total" ng-model="currentPage" ng-disabled="disabled"></ul>')($rootScope);
|
||||
$rootScope.currentPage = 3;
|
||||
$rootScope.$digest();
|
||||
});
|
||||
@@ -941,7 +950,7 @@ describe('pagination directive', function() {
|
||||
it('should retain the model value when total-items starts as undefined', function() {
|
||||
$rootScope.currentPage = 5;
|
||||
$rootScope.total = undefined;
|
||||
element = $compile('<uib-pagination total-items="total" ng-model="currentPage"></uib-pagination>')($rootScope);
|
||||
element = $compile('<ul uib-pagination total-items="total" ng-model="currentPage"></ul>')($rootScope);
|
||||
$rootScope.$digest();
|
||||
|
||||
expect($rootScope.currentPage).toBe(5);
|
||||
|
||||
@@ -34,9 +34,9 @@
|
||||
<hr />
|
||||
<h4>Triggers</h4>
|
||||
<p>
|
||||
<button uib-popover="I appeared on mouse enter!" popover-trigger="mouseenter" type="button" class="btn btn-default">Mouseenter</button>
|
||||
<button uib-popover="I appeared on mouse enter!" popover-trigger="'mouseenter'" type="button" class="btn btn-default">Mouseenter</button>
|
||||
</p>
|
||||
<input type="text" value="Click me!" uib-popover="I appeared on focus! Click away and I'll vanish..." popover-trigger="focus" class="form-control">
|
||||
<input type="text" value="Click me!" uib-popover="I appeared on focus! Click away and I'll vanish..." popover-trigger="'focus'" class="form-control">
|
||||
|
||||
<hr />
|
||||
<h4>Other</h4>
|
||||
@@ -44,4 +44,5 @@
|
||||
<button uib-popover="I have a title!" popover-title="The title." type="button" class="btn btn-default">title</button>
|
||||
<button uib-popover="I am activated manually" popover-is-open="popoverIsOpen" ng-click="popoverIsOpen = !popoverIsOpen" type="button" class="btn btn-default">Toggle popover</button>
|
||||
<button uib-popover-html="htmlPopover" class="btn btn-default">HTML Popover</button>
|
||||
<button uib-popover-html="'<b>HTML</b>, <i>inline</i>'" class="btn btn-default">HTML Popover (inline)</button>
|
||||
</div>
|
||||
|
||||
@@ -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`)_ -
|
||||
@@ -76,8 +79,9 @@ All these settings are available for the three types of popovers.
|
||||
A string to display as a fancy title.
|
||||
|
||||
* `popover-trigger`
|
||||
_(Default: `click`)_ -
|
||||
What should trigger a show of the popover? Supports a space separated list of event names (see below).
|
||||
<small class="badge">$</small>
|
||||
_(Default: `'click'`)_ -
|
||||
What should trigger a show of the popover? Supports a space separated list of event names, or objects (see below).
|
||||
|
||||
**Note:** To configure the tooltips, you need to do it on `$uibTooltipProvider` (also see below).
|
||||
|
||||
|
||||
@@ -7,9 +7,8 @@ angular.module('ui.bootstrap.popover', ['ui.bootstrap.tooltip'])
|
||||
|
||||
.directive('uibPopoverTemplatePopup', function() {
|
||||
return {
|
||||
replace: true,
|
||||
scope: { uibTitle: '@', contentExp: '&', placement: '@', popupClass: '@', animation: '&', isOpen: '&',
|
||||
originScope: '&' },
|
||||
restrict: 'A',
|
||||
scope: { uibTitle: '@', contentExp: '&', originScope: '&' },
|
||||
templateUrl: 'uib/template/popover/popover-template.html'
|
||||
};
|
||||
})
|
||||
@@ -22,8 +21,8 @@ angular.module('ui.bootstrap.popover', ['ui.bootstrap.tooltip'])
|
||||
|
||||
.directive('uibPopoverHtmlPopup', function() {
|
||||
return {
|
||||
replace: true,
|
||||
scope: { contentExp: '&', uibTitle: '@', placement: '@', popupClass: '@', animation: '&', isOpen: '&' },
|
||||
restrict: 'A',
|
||||
scope: { contentExp: '&', uibTitle: '@' },
|
||||
templateUrl: 'uib/template/popover/popover-html.html'
|
||||
};
|
||||
})
|
||||
@@ -36,8 +35,8 @@ angular.module('ui.bootstrap.popover', ['ui.bootstrap.tooltip'])
|
||||
|
||||
.directive('uibPopoverPopup', function() {
|
||||
return {
|
||||
replace: true,
|
||||
scope: { uibTitle: '@', content: '@', placement: '@', popupClass: '@', animation: '&', isOpen: '&' },
|
||||
restrict: 'A',
|
||||
scope: { uibTitle: '@', content: '@' },
|
||||
templateUrl: 'uib/template/popover/popover.html'
|
||||
};
|
||||
})
|
||||
|
||||
@@ -21,6 +21,7 @@ describe('popover', function() {
|
||||
scope.template = $sce.trustAsHtml('<span>My template</span>');
|
||||
$compile(elmBody)(scope);
|
||||
scope.$digest();
|
||||
$document.find('body').append(elmBody);
|
||||
elm = elmBody.find('span');
|
||||
elmScope = elm.scope();
|
||||
tooltipScope = elmScope.$$childTail;
|
||||
@@ -87,6 +88,7 @@ describe('popover', function() {
|
||||
it('should hide popover when template becomes empty', inject(function($timeout) {
|
||||
elm.trigger('click');
|
||||
tooltipScope.$digest();
|
||||
$timeout.flush(0);
|
||||
expect(tooltipScope.isOpen).toBe(true);
|
||||
|
||||
scope.template = '';
|
||||
|
||||
@@ -25,6 +25,7 @@ describe('popover template', function() {
|
||||
|
||||
scope = $rootScope;
|
||||
$compile(elmBody)(scope);
|
||||
$document.find('body').append(elmBody);
|
||||
scope.templateUrl = 'myUrl';
|
||||
|
||||
scope.$digest();
|
||||
@@ -35,6 +36,7 @@ describe('popover template', function() {
|
||||
|
||||
afterEach(function() {
|
||||
$document.off('keypress');
|
||||
elmBody.remove();
|
||||
});
|
||||
|
||||
it('should open on click', inject(function() {
|
||||
@@ -75,6 +77,7 @@ describe('popover template', function() {
|
||||
it('should hide popover when template becomes empty', inject(function($timeout) {
|
||||
elm.trigger('click');
|
||||
tooltipScope.$digest();
|
||||
$timeout.flush(0);
|
||||
expect(tooltipScope.isOpen).toBe(true);
|
||||
|
||||
scope.templateUrl = '';
|
||||
|
||||
@@ -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,
|
||||
@@ -529,13 +529,33 @@ angular.module('ui.bootstrap.position', [])
|
||||
},
|
||||
|
||||
/**
|
||||
* Provides a way for positioning tooltip & dropdown
|
||||
* arrows when using placement options beyond the standard
|
||||
* left, right, top, or bottom.
|
||||
*
|
||||
* @param {element} elem - The tooltip/dropdown element.
|
||||
* @param {string} placement - The placement for the elem.
|
||||
*/
|
||||
* Provides a way to adjust the top positioning after first
|
||||
* render to correctly align element to top after content
|
||||
* rendering causes resized element height
|
||||
*
|
||||
* @param {array} placementClasses - The array of strings of classes
|
||||
* element should have.
|
||||
* @param {object} containerPosition - The object with container
|
||||
* position information
|
||||
* @param {number} initialHeight - The initial height for the elem.
|
||||
* @param {number} currentHeight - The current height for the elem.
|
||||
*/
|
||||
adjustTop: function(placementClasses, containerPosition, initialHeight, currentHeight) {
|
||||
if (placementClasses.indexOf('top') !== -1 && initialHeight !== currentHeight) {
|
||||
return {
|
||||
top: containerPosition.top - currentHeight + 'px'
|
||||
};
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Provides a way for positioning tooltip & dropdown
|
||||
* arrows when using placement options beyond the standard
|
||||
* left, right, top, or bottom.
|
||||
*
|
||||
* @param {element} elem - The tooltip/dropdown element.
|
||||
* @param {string} placement - The placement for the elem.
|
||||
*/
|
||||
positionArrow: function(elem, placement) {
|
||||
elem = this.getRawNode(elem);
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<div ng-controller="RatingDemoCtrl">
|
||||
<h4>Default</h4>
|
||||
<uib-rating ng-model="rate" max="max" read-only="isReadonly" on-hover="hoveringOver(value)" on-leave="overStar = null" titles="['one','two','three']" aria-labelledby="default-rating"></uib-rating>
|
||||
<span uib-rating ng-model="rate" max="max" read-only="isReadonly" on-hover="hoveringOver(value)" on-leave="overStar = null" titles="['one','two','three']" aria-labelledby="default-rating"></span>
|
||||
<span class="label" ng-class="{'label-warning': percent<30, 'label-info': percent>=30 && percent<70, 'label-success': percent>=70}" ng-show="overStar && !isReadonly">{{percent}}%</span>
|
||||
|
||||
<pre style="margin:15px 0;">Rate: <b>{{rate}}</b> - Readonly is: <i>{{isReadonly}}</i> - Hovering over: <b>{{overStar || "none"}}</b></pre>
|
||||
@@ -10,6 +10,6 @@
|
||||
<hr />
|
||||
|
||||
<h4>Custom icons</h4>
|
||||
<div ng-init="x = 5"><uib-rating ng-model="x" max="15" state-on="'glyphicon-ok-sign'" state-off="'glyphicon-ok-circle'" aria-labelledby="custom-icons-1"></uib-rating> <b>(<i>Rate:</i> {{x}})</b></div>
|
||||
<div ng-init="y = 2"><uib-rating ng-model="y" rating-states="ratingStates" aria-labelledby="custom-icons-2"></uib-rating> <b>(<i>Rate:</i> {{y}})</b></div>
|
||||
<div ng-init="x = 5"><span uib-rating ng-model="x" max="15" state-on="'glyphicon-ok-sign'" state-off="'glyphicon-ok-circle'" aria-labelledby="custom-icons-1"></span> <b>(<i>Rate:</i> {{x}})</b></div>
|
||||
<div ng-init="y = 2"><span uib-rating ng-model="y" rating-states="ratingStates" aria-labelledby="custom-icons-2"></span> <b>(<i>Rate:</i> {{y}})</b></div>
|
||||
</div>
|
||||
|
||||
@@ -5,7 +5,7 @@ angular.module('ui.bootstrap.rating', [])
|
||||
stateOn: null,
|
||||
stateOff: null,
|
||||
enableReset: true,
|
||||
titles : ['one', 'two', 'three', 'four', 'five']
|
||||
titles: ['one', 'two', 'three', 'four', 'five']
|
||||
})
|
||||
|
||||
.controller('UibRatingController', ['$scope', '$attrs', 'uibRatingConfig', function($scope, $attrs, ratingConfig) {
|
||||
@@ -90,6 +90,7 @@ angular.module('ui.bootstrap.rating', [])
|
||||
.directive('uibRating', function() {
|
||||
return {
|
||||
require: ['uibRating', 'ngModel'],
|
||||
restrict: 'A',
|
||||
scope: {
|
||||
readonly: '=?readOnly',
|
||||
onHover: '&',
|
||||
@@ -97,7 +98,6 @@ angular.module('ui.bootstrap.rating', [])
|
||||
},
|
||||
controller: 'UibRatingController',
|
||||
templateUrl: 'uib/template/rating/rating.html',
|
||||
replace: true,
|
||||
link: function(scope, element, attrs, ctrls) {
|
||||
var ratingCtrl = ctrls[0], ngModelCtrl = ctrls[1];
|
||||
ratingCtrl.init(ngModelCtrl);
|
||||
|
||||
@@ -1,17 +1,18 @@
|
||||
describe('rating directive', function() {
|
||||
var $rootScope, $compile, element;
|
||||
var $rootScope, $compile, element, innerElem;
|
||||
beforeEach(module('ui.bootstrap.rating'));
|
||||
beforeEach(module('uib/template/rating/rating.html'));
|
||||
beforeEach(inject(function(_$compile_, _$rootScope_) {
|
||||
$compile = _$compile_;
|
||||
$rootScope = _$rootScope_;
|
||||
$rootScope.rate = 3;
|
||||
element = $compile('<uib-rating ng-model="rate"></uib-rating>')($rootScope);
|
||||
element = $compile('<span uib-rating ng-model="rate"></span>')($rootScope);
|
||||
$rootScope.$digest();
|
||||
innerElem = element.children().eq(0);
|
||||
}));
|
||||
|
||||
function getStars() {
|
||||
return element.find('i');
|
||||
return innerElem.find('i');
|
||||
}
|
||||
|
||||
function getStar(number) {
|
||||
@@ -38,17 +39,17 @@ describe('rating directive', function() {
|
||||
function triggerKeyDown(keyCode) {
|
||||
var e = $.Event('keydown');
|
||||
e.which = keyCode;
|
||||
element.trigger(e);
|
||||
innerElem.trigger(e);
|
||||
}
|
||||
|
||||
it('contains the default number of icons', function() {
|
||||
expect(getStars().length).toBe(5);
|
||||
expect(element.attr('aria-valuemax')).toBe('5');
|
||||
expect(innerElem.attr('aria-valuemax')).toBe('5');
|
||||
});
|
||||
|
||||
it('initializes the default star icons as selected', function() {
|
||||
expect(getState()).toEqual([true, true, true, false, false]);
|
||||
expect(element.attr('aria-valuenow')).toBe('3');
|
||||
expect(innerElem.attr('aria-valuenow')).toBe('3');
|
||||
});
|
||||
|
||||
it('handles correctly the click event', function() {
|
||||
@@ -56,19 +57,19 @@ describe('rating directive', function() {
|
||||
$rootScope.$digest();
|
||||
expect(getState()).toEqual([true, true, false, false, false]);
|
||||
expect($rootScope.rate).toBe(2);
|
||||
expect(element.attr('aria-valuenow')).toBe('2');
|
||||
expect(innerElem.attr('aria-valuenow')).toBe('2');
|
||||
|
||||
getStar(5).click();
|
||||
$rootScope.$digest();
|
||||
expect(getState()).toEqual([true, true, true, true, true]);
|
||||
expect($rootScope.rate).toBe(5);
|
||||
expect(element.attr('aria-valuenow')).toBe('5');
|
||||
expect(innerElem.attr('aria-valuenow')).toBe('5');
|
||||
|
||||
getStar(5).click();
|
||||
$rootScope.$digest();
|
||||
expect(getState()).toEqual([false, false, false, false, false]);
|
||||
expect($rootScope.rate).toBe(0);
|
||||
expect(element.attr('aria-valuenow')).toBe('0');
|
||||
expect(innerElem.attr('aria-valuenow')).toBe('0');
|
||||
});
|
||||
|
||||
it('handles correctly the hover event', function() {
|
||||
@@ -82,7 +83,7 @@ describe('rating directive', function() {
|
||||
expect(getState()).toEqual([true, true, true, true, true]);
|
||||
expect($rootScope.rate).toBe(3);
|
||||
|
||||
element.trigger('mouseout');
|
||||
innerElem.trigger('mouseout');
|
||||
expect(getState()).toEqual([true, true, true, false, false]);
|
||||
expect($rootScope.rate).toBe(3);
|
||||
});
|
||||
@@ -92,13 +93,13 @@ describe('rating directive', function() {
|
||||
$rootScope.$digest();
|
||||
|
||||
expect(getState()).toEqual([true, true, false, false, false]);
|
||||
expect(element.attr('aria-valuenow')).toBe('2');
|
||||
expect(innerElem.attr('aria-valuenow')).toBe('2');
|
||||
|
||||
$rootScope.rate = 2.5;
|
||||
$rootScope.$digest();
|
||||
|
||||
expect(getState()).toEqual([true, true, true, false, false]);
|
||||
expect(element.attr('aria-valuenow')).toBe('3');
|
||||
expect(innerElem.attr('aria-valuenow')).toBe('3');
|
||||
});
|
||||
|
||||
it('changes the number of selected icons when value changes', function() {
|
||||
@@ -106,30 +107,33 @@ describe('rating directive', function() {
|
||||
$rootScope.$digest();
|
||||
|
||||
expect(getState()).toEqual([true, true, false, false, false]);
|
||||
expect(element.attr('aria-valuenow')).toBe('2');
|
||||
expect(element.attr('aria-valuetext')).toBe('two');
|
||||
expect(innerElem.attr('aria-valuenow')).toBe('2');
|
||||
expect(innerElem.attr('aria-valuetext')).toBe('two');
|
||||
});
|
||||
|
||||
it('shows different number of icons when `max` attribute is set', function() {
|
||||
element = $compile('<uib-rating ng-model="rate" max="7"></uib-rating>')($rootScope);
|
||||
element = $compile('<span uib-rating ng-model="rate" max="7"></span>')($rootScope);
|
||||
$rootScope.$digest();
|
||||
innerElem = element.children().eq(0);
|
||||
|
||||
expect(getStars().length).toBe(7);
|
||||
expect(element.attr('aria-valuemax')).toBe('7');
|
||||
expect(innerElem.attr('aria-valuemax')).toBe('7');
|
||||
});
|
||||
|
||||
it('shows different number of icons when `max` attribute is from scope variable', function() {
|
||||
$rootScope.max = 15;
|
||||
element = $compile('<uib-rating ng-model="rate" max="max"></uib-rating>')($rootScope);
|
||||
element = $compile('<span uib-rating ng-model="rate" max="max"></span>')($rootScope);
|
||||
$rootScope.$digest();
|
||||
innerElem = element.children().eq(0);
|
||||
expect(getStars().length).toBe(15);
|
||||
expect(element.attr('aria-valuemax')).toBe('15');
|
||||
expect(innerElem.attr('aria-valuemax')).toBe('15');
|
||||
});
|
||||
|
||||
it('handles read-only attribute', function() {
|
||||
$rootScope.isReadonly = true;
|
||||
element = $compile('<uib-rating ng-model="rate" read-only="isReadonly"></uib-rating>')($rootScope);
|
||||
element = $compile('<span uib-rating ng-model="rate" read-only="isReadonly"></span>')($rootScope);
|
||||
$rootScope.$digest();
|
||||
innerElem = element.children().eq(0);
|
||||
|
||||
expect(getState()).toEqual([true, true, true, false, false]);
|
||||
|
||||
@@ -148,8 +152,9 @@ describe('rating directive', function() {
|
||||
|
||||
it('handles enable-reset attribute', function() {
|
||||
$rootScope.canReset = false;
|
||||
element = $compile('<uib-rating ng-model="rate" enable-reset="canReset"></uib-rating>')($rootScope);
|
||||
element = $compile('<span uib-rating ng-model="rate" enable-reset="canReset"></span>')($rootScope);
|
||||
$rootScope.$digest();
|
||||
innerElem = element.children().eq(0);
|
||||
|
||||
var star = {
|
||||
states: [true, true, true, true, true],
|
||||
@@ -162,19 +167,20 @@ describe('rating directive', function() {
|
||||
$rootScope.$digest();
|
||||
expect(getState()).toEqual(star.states);
|
||||
expect($rootScope.rate).toBe(5);
|
||||
expect(element.attr('aria-valuenow')).toBe('5');
|
||||
expect(innerElem.attr('aria-valuenow')).toBe('5');
|
||||
|
||||
selectStar.click();
|
||||
$rootScope.$digest();
|
||||
expect(getState()).toEqual(star.states);
|
||||
expect($rootScope.rate).toBe(5);
|
||||
expect(element.attr('aria-valuenow')).toBe('5');
|
||||
expect(innerElem.attr('aria-valuenow')).toBe('5');
|
||||
});
|
||||
|
||||
it('should fire onHover', function() {
|
||||
$rootScope.hoveringOver = jasmine.createSpy('hoveringOver');
|
||||
element = $compile('<uib-rating ng-model="rate" on-hover="hoveringOver(value)"></uib-rating>')($rootScope);
|
||||
element = $compile('<span uib-rating ng-model="rate" on-hover="hoveringOver(value)"></span>')($rootScope);
|
||||
$rootScope.$digest();
|
||||
innerElem = element.children().eq(0);
|
||||
|
||||
getStar(3).trigger('mouseover');
|
||||
$rootScope.$digest();
|
||||
@@ -183,10 +189,11 @@ describe('rating directive', function() {
|
||||
|
||||
it('should fire onLeave', function() {
|
||||
$rootScope.leaving = jasmine.createSpy('leaving');
|
||||
element = $compile('<uib-rating ng-model="rate" on-leave="leaving()"></uib-rating>')($rootScope);
|
||||
element = $compile('<span uib-rating ng-model="rate" on-leave="leaving()"></span>')($rootScope);
|
||||
$rootScope.$digest();
|
||||
innerElem = element.children().eq(0);
|
||||
|
||||
element.trigger('mouseleave');
|
||||
innerElem.trigger('mouseleave');
|
||||
$rootScope.$digest();
|
||||
expect($rootScope.leaving).toHaveBeenCalled();
|
||||
});
|
||||
@@ -243,8 +250,9 @@ describe('rating directive', function() {
|
||||
beforeEach(inject(function() {
|
||||
$rootScope.classOn = 'icon-ok-sign';
|
||||
$rootScope.classOff = 'icon-ok-circle';
|
||||
element = $compile('<uib-rating ng-model="rate" state-on="classOn" state-off="classOff"></uib-rating>')($rootScope);
|
||||
element = $compile('<span uib-rating ng-model="rate" state-on="classOn" state-off="classOff"></span>')($rootScope);
|
||||
$rootScope.$digest();
|
||||
innerElem = element.children().eq(0);
|
||||
}));
|
||||
|
||||
it('changes the default icons', function() {
|
||||
@@ -260,13 +268,14 @@ describe('rating directive', function() {
|
||||
{stateOn: 'heart'},
|
||||
{stateOff: 'off'}
|
||||
];
|
||||
element = $compile('<uib-rating ng-model="rate" rating-states="states"></uib-rating>')($rootScope);
|
||||
element = $compile('<span uib-rating ng-model="rate" rating-states="states"></span>')($rootScope);
|
||||
$rootScope.$digest();
|
||||
innerElem = element.children().eq(0);
|
||||
}));
|
||||
|
||||
it('should define number of icon elements', function() {
|
||||
expect(getStars().length).toBe(4);
|
||||
expect(element.attr('aria-valuemax')).toBe('4');
|
||||
expect(innerElem.attr('aria-valuemax')).toBe('4');
|
||||
});
|
||||
|
||||
it('handles each icon', function() {
|
||||
@@ -291,8 +300,9 @@ describe('rating directive', function() {
|
||||
uibRatingConfig.max = 10;
|
||||
uibRatingConfig.stateOn = 'on';
|
||||
uibRatingConfig.stateOff = 'off';
|
||||
element = $compile('<uib-rating ng-model="rate"></uib-rating>')($rootScope);
|
||||
element = $compile('<span uib-rating ng-model="rate"></span>')($rootScope);
|
||||
$rootScope.$digest();
|
||||
innerElem = element.children().eq(0);
|
||||
}));
|
||||
afterEach(inject(function(uibRatingConfig) {
|
||||
// return it to the original state
|
||||
@@ -320,8 +330,9 @@ describe('rating directive', function() {
|
||||
$rootScope.rate = 5;
|
||||
angular.extend(originalConfig, uibRatingConfig);
|
||||
uibRatingConfig.max = 10;
|
||||
element = $compile('<uib-rating ng-model="rate"></uib-rating>')($rootScope);
|
||||
element = $compile('<span uib-rating ng-model="rate"></span>')($rootScope);
|
||||
$rootScope.$digest();
|
||||
innerElem = element.children().eq(0);
|
||||
}));
|
||||
afterEach(inject(function(uibRatingConfig) {
|
||||
// return it to the original state
|
||||
@@ -336,18 +347,20 @@ describe('rating directive', function() {
|
||||
describe('shows custom titles ', function() {
|
||||
it('should return the custom title for each star', function() {
|
||||
$rootScope.titles = [44,45,46];
|
||||
element = $compile('<uib-rating ng-model="rate" titles="titles"></uib-rating>')($rootScope);
|
||||
element = $compile('<span uib-rating ng-model="rate" titles="titles"></span>')($rootScope);
|
||||
$rootScope.$digest();
|
||||
innerElem = element.children().eq(0);
|
||||
expect(getTitles()).toEqual(['44', '45', '46', '4', '5']);
|
||||
});
|
||||
it('should return the default title if the custom title is empty', function() {
|
||||
$rootScope.titles = [];
|
||||
element = $compile('<uib-rating ng-model="rate" titles="titles"></uib-rating>')($rootScope);
|
||||
element = $compile('<span uib-rating ng-model="rate" titles="titles"></span>')($rootScope);
|
||||
$rootScope.$digest();
|
||||
innerElem = element.children().eq(0);
|
||||
expect(getTitles()).toEqual(['one', 'two', 'three', 'four', 'five']);
|
||||
});
|
||||
it('should return the default title if the custom title is not an array', function() {
|
||||
element = $compile('<uib-rating ng-model="rate" titles="test"></uib-rating>')($rootScope);
|
||||
element = $compile('<span uib-rating ng-model="rate" titles="test"></span>')($rootScope);
|
||||
$rootScope.$digest();
|
||||
expect(getTitles()).toEqual(['one', 'two', 'three', 'four', 'five']);
|
||||
});
|
||||
|
||||
@@ -43,7 +43,7 @@ angular.module('ui.bootstrap.stackedMap', [])
|
||||
return stack.splice(idx, 1)[0];
|
||||
},
|
||||
removeTop: function() {
|
||||
return stack.splice(stack.length - 1, 1)[0];
|
||||
return stack.pop();
|
||||
},
|
||||
length: function() {
|
||||
return stack.length;
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
require('./tabindex');
|
||||
|
||||
var MODULE_NAME = 'ui.bootstrap.module.tabindex';
|
||||
|
||||
angular.module(MODULE_NAME, ['ui.bootstrap.tabindex']);
|
||||
|
||||
module.exports = MODULE_NAME;
|
||||
@@ -0,0 +1,12 @@
|
||||
angular.module('ui.bootstrap.tabindex', [])
|
||||
|
||||
.directive('uibTabindexToggle', function() {
|
||||
return {
|
||||
restrict: 'A',
|
||||
link: function(scope, elem, attrs) {
|
||||
attrs.$observe('disabled', function(disabled) {
|
||||
attrs.$set('tabindex', disabled ? -1 : null);
|
||||
});
|
||||
}
|
||||
};
|
||||
});
|
||||
@@ -0,0 +1,23 @@
|
||||
describe('tabindex toggle directive', function() {
|
||||
var $rootScope, element;
|
||||
beforeEach(module('ui.bootstrap.tabindex'));
|
||||
beforeEach(inject(function($compile, _$rootScope_) {
|
||||
$rootScope = _$rootScope_;
|
||||
element = $compile('<a href uib-tabindex-toggle ng-disabled="disabled">foo</a>')($rootScope);
|
||||
$rootScope.$digest();
|
||||
}));
|
||||
|
||||
it('should toggle the tabindex on disabled toggle', function() {
|
||||
expect(element.prop('tabindex')).toBe(0);
|
||||
|
||||
$rootScope.disabled = true;
|
||||
$rootScope.$digest();
|
||||
|
||||
expect(element.prop('tabindex')).toBe(-1);
|
||||
|
||||
$rootScope.disabled = false;
|
||||
$rootScope.$digest();
|
||||
|
||||
expect(element.prop('tabindex')).toBe(0);
|
||||
});
|
||||
});
|
||||
@@ -1,6 +1,6 @@
|
||||
<div ng-controller="TimepickerDemoCtrl">
|
||||
|
||||
<uib-timepicker ng-model="mytime" ng-change="changed()" hour-step="hstep" minute-step="mstep" show-meridian="ismeridian"></uib-timepicker>
|
||||
<div uib-timepicker ng-model="mytime" ng-change="changed()" hour-step="hstep" minute-step="mstep" show-meridian="ismeridian"></div>
|
||||
|
||||
<pre class="alert alert-info">Time is: {{mytime | date:'shortTime' }}</pre>
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ describe('timepicker directive', function() {
|
||||
$rootScope.time = newTime(14, 40, 25);
|
||||
$templateCache = _$templateCache_;
|
||||
|
||||
element = $compile('<uib-timepicker ng-model="time"></uib-timepicker>')($rootScope);
|
||||
element = $compile('<div uib-timepicker ng-model="time"></div>')($rootScope);
|
||||
$rootScope.$digest();
|
||||
|
||||
modelCtrl = element.controller('ngModel');
|
||||
@@ -130,7 +130,7 @@ describe('timepicker directive', function() {
|
||||
|
||||
it('has `selected` current time when model is initially cleared', function() {
|
||||
$rootScope.time = null;
|
||||
element = $compile('<uib-timepicker ng-model="time"></uib-timepicker>')($rootScope);
|
||||
element = $compile('<div uib-timepicker ng-model="time"></div>')($rootScope);
|
||||
$rootScope.$digest();
|
||||
|
||||
expect($rootScope.time).toBe(null);
|
||||
@@ -385,7 +385,7 @@ describe('timepicker directive', function() {
|
||||
});
|
||||
|
||||
it('changes only the time part when minutes change', function() {
|
||||
element = $compile('<uib-timepicker ng-model="time" minute-step="15"></uib-timepicker>')($rootScope);
|
||||
element = $compile('<div uib-timepicker ng-model="time" minute-step="15"></div>')($rootScope);
|
||||
$rootScope.time = newTime(0, 0, 0);
|
||||
$rootScope.$digest();
|
||||
|
||||
@@ -648,7 +648,7 @@ describe('timepicker directive', function() {
|
||||
$rootScope.mstep = 30;
|
||||
$rootScope.sstep = 30;
|
||||
$rootScope.time = newTime(14, 0 , 0);
|
||||
element = $compile('<uib-timepicker ng-model="time" hour-step="hstep" minute-step="mstep" second-step="sstep"></uib-timepicker>')($rootScope);
|
||||
element = $compile('<div uib-timepicker ng-model="time" hour-step="hstep" minute-step="mstep" second-step="sstep"></div>')($rootScope);
|
||||
$rootScope.$digest();
|
||||
});
|
||||
|
||||
@@ -840,7 +840,7 @@ describe('timepicker directive', function() {
|
||||
beforeEach(function(){
|
||||
$rootScope.displaysSeconds = false;
|
||||
$rootScope.time = newTime(14,40,35);
|
||||
element = $compile('<uib-timepicker ng-model="time" show-seconds="displaysSeconds"></uib-timepicker>')($rootScope);
|
||||
element = $compile('<div uib-timepicker ng-model="time" show-seconds="displaysSeconds"></div>')($rootScope);
|
||||
$rootScope.$digest();
|
||||
});
|
||||
|
||||
@@ -914,7 +914,7 @@ describe('timepicker directive', function() {
|
||||
beforeEach(function() {
|
||||
$rootScope.meridian = false;
|
||||
$rootScope.time = newTime(14, 10, 20);
|
||||
element = $compile('<uib-timepicker ng-model="time" show-meridian="meridian"></uib-timepicker>')($rootScope);
|
||||
element = $compile('<div uib-timepicker ng-model="time" show-meridian="meridian"></div>')($rootScope);
|
||||
$rootScope.$digest();
|
||||
});
|
||||
|
||||
@@ -946,7 +946,7 @@ describe('timepicker directive', function() {
|
||||
|
||||
it('handles correctly initially empty model on parent element', function() {
|
||||
$rootScope.time = null;
|
||||
element = $compile('<span ng-model="time"><uib-timepicker show-meridian="meridian"></uib-timepicker></span>')($rootScope);
|
||||
element = $compile('<span ng-model="time"><div uib-timepicker show-meridian="meridian"></div></span>')($rootScope);
|
||||
$rootScope.$digest();
|
||||
|
||||
expect($rootScope.time).toBe(null);
|
||||
@@ -956,7 +956,7 @@ describe('timepicker directive', function() {
|
||||
describe('`meridians` attribute', function() {
|
||||
beforeEach(inject(function() {
|
||||
$rootScope.meridiansArray = ['am', 'pm'];
|
||||
element = $compile('<uib-timepicker ng-model="time" meridians="meridiansArray"></uib-timepicker>')($rootScope);
|
||||
element = $compile('<div uib-timepicker ng-model="time" meridians="meridiansArray"></div>')($rootScope);
|
||||
$rootScope.$digest();
|
||||
}));
|
||||
|
||||
@@ -974,7 +974,7 @@ describe('timepicker directive', function() {
|
||||
describe('`readonly-input` attribute', function() {
|
||||
beforeEach(inject(function() {
|
||||
$rootScope.meridiansArray = ['am', 'pm'];
|
||||
element = $compile('<uib-timepicker ng-model="time" readonly-input="true"></uib-timepicker>')($rootScope);
|
||||
element = $compile('<div uib-timepicker ng-model="time" readonly-input="true"></div>')($rootScope);
|
||||
$rootScope.$digest();
|
||||
}));
|
||||
|
||||
@@ -993,7 +993,7 @@ describe('timepicker directive', function() {
|
||||
}
|
||||
|
||||
it('should pad the hours by default', function() {
|
||||
element = $compile('<uib-timepicker ng-model="time"></uib-timepicker>')($rootScope);
|
||||
element = $compile('<div uib-timepicker ng-model="time"></div>')($rootScope);
|
||||
$rootScope.$digest();
|
||||
|
||||
var inputs = element.find('input');
|
||||
@@ -1005,7 +1005,7 @@ describe('timepicker directive', function() {
|
||||
});
|
||||
|
||||
it('should not pad the hours', function() {
|
||||
element = $compile('<uib-timepicker ng-model="time" pad-hours="false"></uib-timepicker>')($rootScope);
|
||||
element = $compile('<div uib-timepicker ng-model="time" pad-hours="false"></div>')($rootScope);
|
||||
$rootScope.$digest();
|
||||
|
||||
var inputs = element.find('input');
|
||||
@@ -1025,7 +1025,7 @@ describe('timepicker directive', function() {
|
||||
uibTimepickerConfig.minuteStep = 10;
|
||||
uibTimepickerConfig.secondStep = 10;
|
||||
uibTimepickerConfig.showMeridian = false;
|
||||
element = $compile('<uib-timepicker ng-model="time"></uib-timepicker>')($rootScope);
|
||||
element = $compile('<div uib-timepicker ng-model="time"></div>')($rootScope);
|
||||
$rootScope.$digest();
|
||||
}));
|
||||
|
||||
@@ -1086,7 +1086,7 @@ describe('timepicker directive', function() {
|
||||
angular.extend(originalConfig, uibTimepickerConfig);
|
||||
uibTimepickerConfig.meridians = ['π.μ.', 'μ.μ.'];
|
||||
uibTimepickerConfig.showMeridian = true;
|
||||
element = $compile('<uib-timepicker ng-model="time"></uib-timepicker>')($rootScope);
|
||||
element = $compile('<div uib-timepicker ng-model="time"></div>')($rootScope);
|
||||
$rootScope.$digest();
|
||||
}));
|
||||
afterEach(inject(function(uibTimepickerConfig) {
|
||||
@@ -1116,7 +1116,7 @@ describe('timepicker directive', function() {
|
||||
$templateCache.put(newTemplateUrl, '<div>baz</div>');
|
||||
uibTimepickerConfig.templateUrl = newTemplateUrl;
|
||||
|
||||
element = $compile('<uib-timepicker ng-model="time"></uib-timepicker>')($rootScope);
|
||||
element = $compile('<div uib-timepicker ng-model="time"></div>')($rootScope);
|
||||
$rootScope.$digest();
|
||||
}));
|
||||
afterEach(inject(function(uibTimepickerConfig) {
|
||||
@@ -1126,7 +1126,7 @@ describe('timepicker directive', function() {
|
||||
|
||||
it('should use a custom template', function() {
|
||||
expect(element[0].tagName.toLowerCase()).toBe('div');
|
||||
expect(element.html()).toBe('baz');
|
||||
expect(element.html()).toBe('<div>baz</div>');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
|
||||
@@ -1328,7 +1334,7 @@ describe('timepicker directive', function() {
|
||||
|
||||
it('handles 12/24H mode change', function() {
|
||||
$rootScope.meridian = true;
|
||||
element = $compile('<uib-timepicker ng-model="time" show-meridian="meridian"></uib-timepicker>')($rootScope);
|
||||
element = $compile('<div uib-timepicker ng-model="time" show-meridian="meridian"></div>')($rootScope);
|
||||
$rootScope.$digest();
|
||||
|
||||
var el = getHoursInputEl();
|
||||
@@ -1346,14 +1352,14 @@ describe('timepicker directive', function() {
|
||||
});
|
||||
|
||||
it('should have a default tabindex of 0', function() {
|
||||
element = $compile('<uib-timepicker ng-model="time"></uib-timepicker>')($rootScope);
|
||||
element = $compile('<div uib-timepicker ng-model="time"></div>')($rootScope);
|
||||
$rootScope.$digest();
|
||||
|
||||
expect(element.isolateScope().tabindex).toBe(0);
|
||||
});
|
||||
|
||||
it('should have the correct tabindex', function() {
|
||||
element = $compile('<uib-timepicker ng-model="time" tabindex="5"></uib-timepicker>')($rootScope);
|
||||
element = $compile('<div uib-timepicker ng-model="time" tabindex="5"></div>')($rootScope);
|
||||
$rootScope.$digest();
|
||||
|
||||
expect(element.attr('tabindex')).toBe(undefined);
|
||||
@@ -1363,7 +1369,7 @@ describe('timepicker directive', function() {
|
||||
|
||||
describe('when model is not a Date', function() {
|
||||
beforeEach(inject(function() {
|
||||
element = $compile('<uib-timepicker ng-model="time"></uib-timepicker>')($rootScope);
|
||||
element = $compile('<div uib-timepicker ng-model="time"></div>')($rootScope);
|
||||
}));
|
||||
|
||||
it('should not be invalid when the model is null', function() {
|
||||
@@ -1415,7 +1421,7 @@ describe('timepicker directive', function() {
|
||||
describe('use with `ng-required` directive', function() {
|
||||
beforeEach(inject(function() {
|
||||
$rootScope.time = null;
|
||||
element = $compile('<uib-timepicker ng-model="time" ng-required="true"></uib-timepicker>')($rootScope);
|
||||
element = $compile('<div uib-timepicker ng-model="time" ng-required="true"></div>')($rootScope);
|
||||
$rootScope.$digest();
|
||||
}));
|
||||
|
||||
@@ -1434,7 +1440,7 @@ describe('timepicker directive', function() {
|
||||
beforeEach(inject(function() {
|
||||
$rootScope.changeHandler = jasmine.createSpy('changeHandler');
|
||||
$rootScope.time = new Date();
|
||||
element = $compile('<uib-timepicker ng-model="time" ng-change="changeHandler()"></uib-timepicker>')($rootScope);
|
||||
element = $compile('<div uib-timepicker ng-model="time" ng-change="changeHandler()"></div>')($rootScope);
|
||||
$rootScope.$digest();
|
||||
}));
|
||||
|
||||
@@ -1465,7 +1471,7 @@ describe('timepicker directive', function() {
|
||||
describe('when used with min', function() {
|
||||
var changeInputValueTo;
|
||||
beforeEach(inject(function($sniffer) {
|
||||
element = $compile('<uib-timepicker ng-model="time" min="min"></uib-timepicker>')($rootScope);
|
||||
element = $compile('<div uib-timepicker ng-model="time" min="min"></div>')($rootScope);
|
||||
$rootScope.$digest();
|
||||
changeInputValueTo = function(inputEl, value) {
|
||||
inputEl.val(value);
|
||||
@@ -1792,7 +1798,7 @@ describe('timepicker directive', function() {
|
||||
describe('when used with max', function() {
|
||||
var changeInputValueTo;
|
||||
beforeEach(inject(function($sniffer) {
|
||||
element = $compile('<uib-timepicker ng-model="time" max="max"></uib-timepicker>')($rootScope);
|
||||
element = $compile('<div uib-timepicker ng-model="time" max="max"></div>')($rootScope);
|
||||
$rootScope.$digest();
|
||||
changeInputValueTo = function(inputEl, value) {
|
||||
inputEl.val(value);
|
||||
@@ -2121,16 +2127,16 @@ describe('timepicker directive', function() {
|
||||
it('should allow custom templates', function() {
|
||||
$templateCache.put('foo/bar.html', '<div>baz</div>');
|
||||
|
||||
element = $compile('<uib-timepicker ng-model="time" template-url="foo/bar.html"></uib-timepicker>')($rootScope);
|
||||
element = $compile('<div uib-timepicker ng-model="time" template-url="foo/bar.html"></div>')($rootScope);
|
||||
$rootScope.$digest();
|
||||
expect(element[0].tagName.toLowerCase()).toBe('div');
|
||||
expect(element.html()).toBe('baz');
|
||||
expect(element.html()).toBe('<div>baz</div>');
|
||||
});
|
||||
|
||||
it('should expose the controller on the view', function() {
|
||||
$templateCache.put('uib/template/timepicker/timepicker.html', '<div><div>{{timepicker.text}}</div></div>');
|
||||
|
||||
element = $compile('<uib-timepicker ng-model="time"></uib-timepicker>')($rootScope);
|
||||
element = $compile('<div uib-timepicker ng-model="time"></div>')($rootScope);
|
||||
$rootScope.$digest();
|
||||
|
||||
var ctrl = element.controller('uibTimepicker');
|
||||
@@ -2139,14 +2145,14 @@ describe('timepicker directive', function() {
|
||||
ctrl.text = 'foo';
|
||||
$rootScope.$digest();
|
||||
|
||||
expect(element.html()).toBe('<div class="ng-binding">foo</div>');
|
||||
expect(element.html()).toBe('<div><div class="ng-binding">foo</div></div>');
|
||||
});
|
||||
});
|
||||
|
||||
describe('ngDisabled', function() {
|
||||
it('prevents modifying date via controls when true', function() {
|
||||
$rootScope.disabled = false;
|
||||
element = $compile('<uib-timepicker ng-model="time" ng-disabled="disabled"></uib-timepicker')($rootScope);
|
||||
element = $compile('<div uib-timepicker ng-model="time" ng-disabled="disabled"></div>')($rootScope);
|
||||
$rootScope.$digest();
|
||||
|
||||
var inputs = element.find('input');
|
||||
@@ -2250,7 +2256,7 @@ describe('timepicker directive', function() {
|
||||
var $scope;
|
||||
beforeEach(inject(function() {
|
||||
$scope = $rootScope.$new();
|
||||
element = $compile('<uib-timepicker ng-model="time"></uib-timepicker>')($scope);
|
||||
element = $compile('<div uib-timepicker ng-model="time"></div>')($scope);
|
||||
$rootScope.$digest();
|
||||
}));
|
||||
|
||||
|
||||
@@ -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;
|
||||
@@ -547,9 +573,9 @@ angular.module('ui.bootstrap.timepicker', [])
|
||||
.directive('uibTimepicker', ['uibTimepickerConfig', function(uibTimepickerConfig) {
|
||||
return {
|
||||
require: ['uibTimepicker', '?^ngModel'],
|
||||
restrict: 'A',
|
||||
controller: 'UibTimepickerController',
|
||||
controllerAs: 'timepicker',
|
||||
replace: true,
|
||||
scope: {},
|
||||
templateUrl: function(element, attrs) {
|
||||
return attrs.templateUrl || uibTimepickerConfig.templateUrl;
|
||||
|
||||
@@ -29,7 +29,9 @@
|
||||
</p>
|
||||
|
||||
<p>
|
||||
I can even contain HTML. <a href="#" uib-tooltip-html="htmlTooltip">Check me out!</a>
|
||||
I can even contain HTML as a
|
||||
<a href="#" uib-tooltip-html="htmlTooltip">scope variable</a> or
|
||||
<a href="#" uib-tooltip-html="'static. {{dynamicTooltipText}}. <b>bold.</b>'">inline string</a>
|
||||
</p>
|
||||
|
||||
<p>
|
||||
@@ -51,7 +53,7 @@
|
||||
|
||||
<div class="form-group">
|
||||
<label>Or use custom triggers, like focus: </label>
|
||||
<input type="text" value="Click me!" uib-tooltip="See? Now click away..." tooltip-trigger="focus" tooltip-placement="right" class="form-control" />
|
||||
<input type="text" value="Click me!" uib-tooltip="See? Now click away..." tooltip-trigger="'focus'" tooltip-placement="right" class="form-control" />
|
||||
</div>
|
||||
|
||||
<div class="form-group" ng-class="{'has-error' : !inputModel}">
|
||||
@@ -60,7 +62,7 @@
|
||||
placeholder="Hover over this for a tooltip until this is filled"
|
||||
uib-tooltip="Enter something in this input field to disable this tooltip"
|
||||
tooltip-placement="top"
|
||||
tooltip-trigger="mouseenter"
|
||||
tooltip-trigger="'mouseenter'"
|
||||
tooltip-enable="!inputModel" />
|
||||
</div>
|
||||
<div class="form-group">
|
||||
|
||||
@@ -74,8 +74,9 @@ All these settings are available for the three types of tooltips.
|
||||
Popup delay in milliseconds until it opens.
|
||||
|
||||
* `tooltip-trigger`
|
||||
_(Default: `mouseenter`)_ -
|
||||
What should trigger a show of the tooltip? Supports a space separated list of event names (see below).
|
||||
<small class="badge">$</small>
|
||||
_(Default: `'mouseenter'`)_ -
|
||||
What should trigger a show of the tooltip? Supports a space separated list of event names, or objects (see below).
|
||||
|
||||
**Note:** To configure the tooltips, you need to do it on `$uibTooltipProvider` (also see below).
|
||||
|
||||
@@ -117,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.
|
||||
|
||||
@@ -75,6 +75,7 @@ describe('tooltip template', function() {
|
||||
|
||||
it('should hide tooltip when template becomes empty', inject(function($timeout) {
|
||||
trigger(elm, 'mouseenter');
|
||||
$timeout.flush(0);
|
||||
expect(tooltipScope.isOpen).toBe(true);
|
||||
|
||||
scope.templateUrl = '';
|
||||
|
||||
@@ -27,7 +27,7 @@ describe('tooltip', function() {
|
||||
}));
|
||||
|
||||
afterEach(function() {
|
||||
$document.off('keypress');
|
||||
$document.off('keyup');
|
||||
});
|
||||
|
||||
function trigger(element, evt) {
|
||||
@@ -179,7 +179,6 @@ describe('tooltip', function() {
|
||||
expect(elm.attr('alt')).toBe(scope.alt);
|
||||
|
||||
ttScope = angular.element(elmBody.children()[1]).isolateScope();
|
||||
expect(ttScope.placement).toBe('top');
|
||||
expect(ttScope.content).toBe(scope.tooltipMsg);
|
||||
|
||||
trigger(elm, 'mouseleave');
|
||||
@@ -188,7 +187,6 @@ describe('tooltip', function() {
|
||||
trigger(elm, 'mouseenter');
|
||||
|
||||
ttScope = angular.element(elmBody.children()[1]).isolateScope();
|
||||
expect(ttScope.placement).toBe('top');
|
||||
expect(ttScope.content).toBe(scope.tooltipMsg);
|
||||
}));
|
||||
|
||||
@@ -297,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);
|
||||
});
|
||||
@@ -345,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);
|
||||
@@ -504,7 +503,7 @@ describe('tooltip', function() {
|
||||
|
||||
it('should use it to show but set the hide trigger based on the map for mapped triggers', inject(function($compile) {
|
||||
elmBody = angular.element(
|
||||
'<div><input uib-tooltip="Hello!" tooltip-trigger="focus" /></div>'
|
||||
'<div><input uib-tooltip="Hello!" tooltip-trigger="\'focus\'" /></div>'
|
||||
);
|
||||
$compile(elmBody)(scope);
|
||||
scope.$apply();
|
||||
@@ -521,7 +520,7 @@ describe('tooltip', function() {
|
||||
|
||||
it('should use it as both the show and hide triggers for unmapped triggers', inject(function($compile) {
|
||||
elmBody = angular.element(
|
||||
'<div><input uib-tooltip="Hello!" tooltip-trigger="fakeTriggerAttr" /></div>'
|
||||
'<div><input uib-tooltip="Hello!" tooltip-trigger="\'fakeTriggerAttr\'" /></div>'
|
||||
);
|
||||
$compile(elmBody)(scope);
|
||||
scope.$apply();
|
||||
@@ -540,8 +539,8 @@ describe('tooltip', function() {
|
||||
scope.test = true;
|
||||
elmBody = angular.element(
|
||||
'<div>' +
|
||||
'<input uib-tooltip="Hello!" tooltip-trigger="{{ (test && \'mouseenter\' || \'click\') }}" />' +
|
||||
'<input uib-tooltip="Hello!" tooltip-trigger="{{ (test && \'mouseenter\' || \'click\') }}" />' +
|
||||
'<input uib-tooltip="Hello!" tooltip-trigger="test && \'mouseenter\' || \'click\'" />' +
|
||||
'<input uib-tooltip="Hello!" tooltip-trigger="test && \'mouseenter\' || \'click\'" />' +
|
||||
'</div>'
|
||||
);
|
||||
|
||||
@@ -566,7 +565,7 @@ describe('tooltip', function() {
|
||||
|
||||
it('should accept multiple triggers based on the map for mapped triggers', inject(function($compile) {
|
||||
elmBody = angular.element(
|
||||
'<div><input uib-tooltip="Hello!" tooltip-trigger="focus fakeTriggerAttr" /></div>'
|
||||
'<div><input uib-tooltip="Hello!" tooltip-trigger="\'focus fakeTriggerAttr\'" /></div>'
|
||||
);
|
||||
$compile(elmBody)(scope);
|
||||
scope.$apply();
|
||||
@@ -587,7 +586,7 @@ describe('tooltip', function() {
|
||||
|
||||
it('should not show when trigger is set to "none"', inject(function($compile) {
|
||||
elmBody = angular.element(
|
||||
'<div><input uib-tooltip="Hello!" tooltip-trigger="none" /></div>'
|
||||
'<div><input uib-tooltip="Hello!" tooltip-trigger="\'none\'" /></div>'
|
||||
);
|
||||
$compile(elmBody)(scope);
|
||||
scope.$apply();
|
||||
@@ -601,7 +600,7 @@ describe('tooltip', function() {
|
||||
|
||||
it('should toggle on click and hide when anything else is clicked when trigger is set to "outsideClick"', inject(function($compile, $document) {
|
||||
elm = $compile(angular.element(
|
||||
'<span uib-tooltip="tooltip text" tooltip-trigger="outsideClick">Selector Text</span>'
|
||||
'<span uib-tooltip="tooltip text" tooltip-trigger="\'outsideClick\'">Selector Text</span>'
|
||||
))(scope);
|
||||
scope.$apply();
|
||||
elmScope = elm.scope();
|
||||
@@ -623,6 +622,23 @@ describe('tooltip', function() {
|
||||
tooltipScope.$digest();
|
||||
expect(tooltipScope.isOpen).toBeFalsy();
|
||||
}));
|
||||
|
||||
it('should support objects', inject(function($compile) {
|
||||
elmBody = angular.element(
|
||||
'<div><input uib-tooltip="Hello!" tooltip-trigger="{show: \'hide\'}" /></div>'
|
||||
);
|
||||
$compile(elmBody)(scope);
|
||||
scope.$apply();
|
||||
elm = elmBody.find('input');
|
||||
elmScope = elm.scope();
|
||||
tooltipScope = elmScope.$$childTail;
|
||||
|
||||
expect(tooltipScope.isOpen).toBeFalsy();
|
||||
trigger(elm, 'show');
|
||||
expect(tooltipScope.isOpen).toBeTruthy();
|
||||
trigger(elm, 'hide');
|
||||
expect(tooltipScope.isOpen).toBeFalsy();
|
||||
}));
|
||||
});
|
||||
|
||||
describe('with an append-to-body attribute', function() {
|
||||
@@ -673,7 +689,7 @@ describe('tooltip', function() {
|
||||
}
|
||||
|
||||
beforeEach(inject(function($compile, $rootScope) {
|
||||
elmBody = angular.element('<div><input uib-tooltip="Hello!" tooltip-trigger="fooTrigger" /></div>');
|
||||
elmBody = angular.element('<div><input uib-tooltip="Hello!" tooltip-trigger="\'fooTrigger\'" /></div>');
|
||||
|
||||
$compile(elmBody)($rootScope);
|
||||
$rootScope.$apply();
|
||||
@@ -754,7 +770,7 @@ describe('tooltipWithDifferentSymbols', function() {
|
||||
|
||||
it('should show the correct tooltip text', inject(function($compile, $rootScope) {
|
||||
elmBody = angular.element(
|
||||
'<div><input type="text" uib-tooltip="My tooltip" tooltip-trigger="focus" tooltip-placement="right" /></div>'
|
||||
'<div><input type="text" uib-tooltip="My tooltip" tooltip-trigger="\'focus\'" tooltip-placement="right" /></div>'
|
||||
);
|
||||
$compile(elmBody)($rootScope);
|
||||
$rootScope.$apply();
|
||||
@@ -1030,7 +1046,7 @@ describe('$uibTooltipProvider', function() {
|
||||
|
||||
it('should override the show and hide triggers if there is an attribute', inject(function($rootScope, $compile) {
|
||||
elmBody = angular.element(
|
||||
'<div><input uib-tooltip="tooltip text" tooltip-trigger="mouseenter"/></div>'
|
||||
'<div><input uib-tooltip="tooltip text" tooltip-trigger="\'mouseenter\'"/></div>'
|
||||
);
|
||||
|
||||
scope = $rootScope;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
describe('tooltip directive', function() {
|
||||
var $rootScope, $compile, $document, $timeout;
|
||||
var $rootScope, $compile, $document, $timeout, body, fragment;
|
||||
|
||||
beforeEach(module('ui.bootstrap.tooltip'));
|
||||
beforeEach(module('uib/template/tooltip/tooltip-popup.html'));
|
||||
@@ -10,6 +10,8 @@ describe('tooltip directive', function() {
|
||||
$compile = _$compile_;
|
||||
$document = _$document_;
|
||||
$timeout = _$timeout_;
|
||||
|
||||
body = $document.find('body');
|
||||
}));
|
||||
|
||||
beforeEach(function() {
|
||||
@@ -39,12 +41,13 @@ describe('tooltip directive', function() {
|
||||
|
||||
afterEach(function() {
|
||||
$document.off('keypress');
|
||||
fragment.remove();
|
||||
});
|
||||
|
||||
function compileTooltip(ttipMarkup) {
|
||||
var fragment = $compile('<div>' + ttipMarkup + '</div>')($rootScope);
|
||||
fragment = $compile('<div>' + ttipMarkup + '</div>')($rootScope);
|
||||
$rootScope.$digest();
|
||||
return fragment;
|
||||
body.append(fragment);
|
||||
}
|
||||
|
||||
function closeTooltip(hostEl, triggerEvt, shouldNotFlush) {
|
||||
@@ -62,7 +65,7 @@ describe('tooltip directive', function() {
|
||||
|
||||
describe('basic scenarios with default options', function() {
|
||||
it('shows default tooltip on mouse enter and closes on mouse leave', function() {
|
||||
var fragment = compileTooltip('<span uib-tooltip="tooltip text">Trigger here</span>');
|
||||
compileTooltip('<span uib-tooltip="tooltip text">Trigger here</span>');
|
||||
|
||||
trigger(fragment.find('span'), 'mouseenter');
|
||||
expect(fragment).toHaveOpenTooltips();
|
||||
@@ -72,16 +75,17 @@ describe('tooltip directive', function() {
|
||||
});
|
||||
|
||||
it('should not show a tooltip when its content is empty', function() {
|
||||
var fragment = compileTooltip('<span uib-tooltip=""></span>');
|
||||
compileTooltip('<span uib-tooltip=""></span>');
|
||||
trigger(fragment.find('span'), 'mouseenter');
|
||||
expect(fragment).not.toHaveOpenTooltips();
|
||||
});
|
||||
|
||||
it('should not show a tooltip when its content becomes empty', function() {
|
||||
$rootScope.content = 'some text';
|
||||
var fragment = compileTooltip('<span uib-tooltip="{{ content }}"></span>');
|
||||
compileTooltip('<span uib-tooltip="{{ content }}"></span>');
|
||||
|
||||
trigger(fragment.find('span'), 'mouseenter');
|
||||
$timeout.flush(0);
|
||||
expect(fragment).toHaveOpenTooltips();
|
||||
|
||||
$rootScope.content = '';
|
||||
@@ -92,7 +96,7 @@ describe('tooltip directive', function() {
|
||||
|
||||
it('should update tooltip when its content becomes empty', function() {
|
||||
$rootScope.content = 'some text';
|
||||
var fragment = compileTooltip('<span uib-tooltip="{{ content }}"></span>');
|
||||
compileTooltip('<span uib-tooltip="{{ content }}"></span>');
|
||||
|
||||
$rootScope.content = '';
|
||||
$rootScope.$digest();
|
||||
@@ -119,7 +123,7 @@ describe('tooltip directive', function() {
|
||||
describe(key, function() {
|
||||
describe('placement', function() {
|
||||
it('can specify an alternative, valid placement', function() {
|
||||
var fragment = compileTooltip('<span ' + html + ' tooltip-placement="left">Trigger here</span>');
|
||||
compileTooltip('<span ' + html + ' tooltip-placement="left">Trigger here</span>');
|
||||
trigger(fragment.find('span'), 'mouseenter');
|
||||
|
||||
var ttipElement = fragment.find('div.tooltip');
|
||||
@@ -133,7 +137,7 @@ describe('tooltip directive', function() {
|
||||
|
||||
describe('class', function() {
|
||||
it('can specify a custom class', function() {
|
||||
var fragment = compileTooltip('<span ' + html + ' tooltip-class="custom">Trigger here</span>');
|
||||
compileTooltip('<span ' + html + ' tooltip-class="custom">Trigger here</span>');
|
||||
trigger(fragment.find('span'), 'mouseenter');
|
||||
|
||||
var ttipElement = fragment.find('div.tooltip');
|
||||
@@ -149,7 +153,7 @@ describe('tooltip directive', function() {
|
||||
});
|
||||
|
||||
it('should show even after close trigger is called multiple times - issue #1847', function() {
|
||||
var fragment = compileTooltip('<span uib-tooltip="tooltip text">Trigger here</span>');
|
||||
compileTooltip('<span uib-tooltip="tooltip text">Trigger here</span>');
|
||||
|
||||
trigger(fragment.find('span'), 'mouseenter');
|
||||
expect(fragment).toHaveOpenTooltips();
|
||||
@@ -169,7 +173,7 @@ describe('tooltip directive', function() {
|
||||
});
|
||||
|
||||
it('should hide even after show trigger is called multiple times', function() {
|
||||
var fragment = compileTooltip('<span uib-tooltip="tooltip text" tooltip-popup-delay="1000">Trigger here</span>');
|
||||
compileTooltip('<span uib-tooltip="tooltip text" tooltip-popup-delay="1000">Trigger here</span>');
|
||||
|
||||
trigger(fragment.find('span'), 'mouseenter');
|
||||
trigger(fragment.find('span'), 'mouseenter');
|
||||
@@ -179,7 +183,7 @@ describe('tooltip directive', function() {
|
||||
});
|
||||
|
||||
it('should not show tooltips element is disabled (button) - issue #3167', function() {
|
||||
var fragment = compileTooltip('<button uib-tooltip="cancel!" ng-disabled="disabled" ng-click="disabled = true">Cancel</button>');
|
||||
compileTooltip('<button uib-tooltip="cancel!" ng-disabled="disabled" ng-click="disabled = true">Cancel</button>');
|
||||
|
||||
trigger(fragment.find('button'), 'mouseenter');
|
||||
expect(fragment).toHaveOpenTooltips();
|
||||
|
||||
+62
-30
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -126,12 +125,11 @@ angular.module('ui.bootstrap.tooltip', ['ui.bootstrap.position', 'ui.bootstrap.s
|
||||
(options.useContentExp ?
|
||||
'content-exp="contentExp()" ' :
|
||||
'content="' + startSym + 'content' + endSym + '" ') +
|
||||
'placement="' + startSym + 'placement' + endSym + '" ' +
|
||||
'popup-class="' + startSym + 'popupClass' + endSym + '" ' +
|
||||
'animation="animation" ' +
|
||||
'is-open="isOpen" ' +
|
||||
'origin-scope="origScope" ' +
|
||||
'class="uib-position-measure"' +
|
||||
'class="uib-position-measure ' + prefix + '" ' +
|
||||
'tooltip-animation-class="fade"' +
|
||||
'uib-tooltip-classes ' +
|
||||
'ng-class="{ in: isOpen }" ' +
|
||||
'>' +
|
||||
'</div>';
|
||||
|
||||
@@ -146,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']);
|
||||
@@ -163,11 +162,14 @@ angular.module('ui.bootstrap.tooltip', ['ui.bootstrap.position', 'ui.bootstrap.s
|
||||
if (!positionTimeout) {
|
||||
positionTimeout = $timeout(function() {
|
||||
var ttPosition = $position.positionElements(element, tooltip, ttScope.placement, appendToBody);
|
||||
var initialHeight = angular.isDefined(tooltip.offsetHeight) ? tooltip.offsetHeight : tooltip.prop('offsetHeight');
|
||||
var elementPos = appendToBody ? $position.offset(element) : $position.position(element);
|
||||
tooltip.css({ top: ttPosition.top + 'px', left: ttPosition.left + 'px' });
|
||||
var placementClasses = ttPosition.placement.split('-');
|
||||
|
||||
if (!tooltip.hasClass(ttPosition.placement.split('-')[0])) {
|
||||
if (!tooltip.hasClass(placementClasses[0])) {
|
||||
tooltip.removeClass(lastPlacement.split('-')[0]);
|
||||
tooltip.addClass(ttPosition.placement.split('-')[0]);
|
||||
tooltip.addClass(placementClasses[0]);
|
||||
}
|
||||
|
||||
if (!tooltip.hasClass(options.placementClassPrefix + ttPosition.placement)) {
|
||||
@@ -175,6 +177,15 @@ angular.module('ui.bootstrap.tooltip', ['ui.bootstrap.position', 'ui.bootstrap.s
|
||||
tooltip.addClass(options.placementClassPrefix + ttPosition.placement);
|
||||
}
|
||||
|
||||
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
|
||||
// uib-position-measure class or if the placement
|
||||
// has changed we need to position the arrow.
|
||||
@@ -197,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) {
|
||||
@@ -326,6 +334,10 @@ angular.module('ui.bootstrap.tooltip', ['ui.bootstrap.position', 'ui.bootstrap.s
|
||||
}
|
||||
});
|
||||
|
||||
openedTooltips.add(ttScope, {
|
||||
close: hide
|
||||
});
|
||||
|
||||
prepObservers();
|
||||
}
|
||||
|
||||
@@ -336,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;
|
||||
@@ -477,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') {
|
||||
@@ -485,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') {
|
||||
@@ -496,10 +523,22 @@ angular.module('ui.bootstrap.tooltip', ['ui.bootstrap.position', 'ui.bootstrap.s
|
||||
};
|
||||
|
||||
function prepTriggers() {
|
||||
var val = attrs[prefix + 'Trigger'];
|
||||
var showTriggers = [], hideTriggers = [];
|
||||
var val = scope.$eval(attrs[prefix + 'Trigger']);
|
||||
unregisterTriggers();
|
||||
|
||||
triggers = getTriggers(val);
|
||||
if (angular.isObject(val)) {
|
||||
Object.keys(val).forEach(function(key) {
|
||||
showTriggers.push(key);
|
||||
hideTriggers.push(val[key]);
|
||||
});
|
||||
triggers = {
|
||||
show: showTriggers,
|
||||
hide: hideTriggers
|
||||
};
|
||||
} else {
|
||||
triggers = getTriggers(val);
|
||||
}
|
||||
|
||||
if (triggers.show !== 'none') {
|
||||
triggers.show.forEach(function(trigger, idx) {
|
||||
@@ -512,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);
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -541,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;
|
||||
});
|
||||
};
|
||||
@@ -644,7 +677,7 @@ function ($animate, $sce, $compile, $templateRequest) {
|
||||
element.addClass(scope.popupClass);
|
||||
}
|
||||
|
||||
if (scope.animation()) {
|
||||
if (scope.animation) {
|
||||
element.addClass(attrs.tooltipAnimationClass);
|
||||
}
|
||||
}
|
||||
@@ -653,8 +686,8 @@ function ($animate, $sce, $compile, $templateRequest) {
|
||||
|
||||
.directive('uibTooltipPopup', function() {
|
||||
return {
|
||||
replace: true,
|
||||
scope: { content: '@', placement: '@', popupClass: '@', animation: '&', isOpen: '&' },
|
||||
restrict: 'A',
|
||||
scope: { content: '@' },
|
||||
templateUrl: 'uib/template/tooltip/tooltip-popup.html'
|
||||
};
|
||||
})
|
||||
@@ -665,9 +698,8 @@ function ($animate, $sce, $compile, $templateRequest) {
|
||||
|
||||
.directive('uibTooltipTemplatePopup', function() {
|
||||
return {
|
||||
replace: true,
|
||||
scope: { contentExp: '&', placement: '@', popupClass: '@', animation: '&', isOpen: '&',
|
||||
originScope: '&' },
|
||||
restrict: 'A',
|
||||
scope: { contentExp: '&', originScope: '&' },
|
||||
templateUrl: 'uib/template/tooltip/tooltip-template-popup.html'
|
||||
};
|
||||
})
|
||||
@@ -680,8 +712,8 @@ function ($animate, $sce, $compile, $templateRequest) {
|
||||
|
||||
.directive('uibTooltipHtmlPopup', function() {
|
||||
return {
|
||||
replace: true,
|
||||
scope: { contentExp: '&', placement: '@', popupClass: '@', animation: '&', isOpen: '&' },
|
||||
restrict: 'A',
|
||||
scope: { contentExp: '&' },
|
||||
templateUrl: 'uib/template/tooltip/tooltip-html-popup.html'
|
||||
};
|
||||
})
|
||||
|
||||
@@ -42,10 +42,10 @@
|
||||
<p class="message">select location from drop down.</p>
|
||||
|
||||
<ul class="dropdown-menu" role="listbox">
|
||||
<li ng-repeat="match in matches track by $index" ng-class="{active: isActive($index) }"
|
||||
ng-mouseenter="selectActive($index)" ng-click="selectMatch($index)" role="option" id="{{::match.id}}">
|
||||
<div uib-typeahead-match index="$index" match="match" query="query" template-url="templateUrl"></div>
|
||||
</li>
|
||||
<li class="uib-typeahead-match" ng-repeat="match in matches track by $index" ng-class="{active: isActive($index) }"
|
||||
ng-mouseenter="selectActive($index)" ng-click="selectMatch($index)" role="option" id="{{::match.id}}">
|
||||
<div uib-typeahead-match index="$index" match="match" query="query" template-url="templateUrl"></div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</script>
|
||||
|
||||
@@ -120,3 +120,7 @@ This directive works with promises, meaning you can retrieve matches using the `
|
||||
<small class="badge">$</small>
|
||||
<i class="glyphicon glyphicon-eye-open"></i> -
|
||||
Comprehension Angular expression (see [select directive](http://docs.angularjs.org/api/ng.directive:select)).
|
||||
|
||||
**Notes**
|
||||
|
||||
If a custom template for the popup is used, the wrapper selector used for the match items is the `uib-typeahead-match` class.
|
||||
|
||||
@@ -222,8 +222,6 @@ describe('typeahead tests', function() {
|
||||
expect(element).toBeClosed();
|
||||
});
|
||||
|
||||
|
||||
|
||||
it('should support custom model selecting function', function() {
|
||||
$scope.updaterFn = function(selectedItem) {
|
||||
return 'prefix' + selectedItem;
|
||||
@@ -315,6 +313,39 @@ describe('typeahead tests', function() {
|
||||
expect($scope.form.input.$error.parse).toBeFalsy();
|
||||
});
|
||||
|
||||
// fix for #6032
|
||||
it('should clear errors and refresh scope after blur for typeahead-editable="false"', function () {
|
||||
var element = prepareInputEl(
|
||||
'<div><form name="form" ng-class="{invalid : form.input.$invalid}">' +
|
||||
'<input name="input" ng-model="result" uib-typeahead="item for item in source | filter:$viewValue" typeahead-editable="false">' +
|
||||
'</form></div>');
|
||||
var inputEl = findInput(element);
|
||||
|
||||
// first try
|
||||
changeInputValueTo(element, 'not in matches');
|
||||
expect($scope.result).toEqual(undefined);
|
||||
expect(inputEl.val()).toEqual('not in matches');
|
||||
expect(element.find('form')).toHaveClass('invalid');
|
||||
inputEl.blur();
|
||||
|
||||
expect(inputEl.val()).toEqual(''); // <-- input is reset
|
||||
expect($scope.form.input.$error.editable).toBeFalsy();
|
||||
expect($scope.form.input.$error.parse).toBeFalsy();
|
||||
expect(element.find('form')).not.toHaveClass('invalid'); // <-- form has no error (it always works for some reason)
|
||||
|
||||
// second try
|
||||
changeInputValueTo(element, 'not in matches');
|
||||
expect($scope.result).toEqual(undefined);
|
||||
expect(inputEl.val()).toEqual('not in matches');
|
||||
expect(element.find('form')).toHaveClass('invalid');
|
||||
inputEl.blur();
|
||||
|
||||
expect(inputEl.val()).toEqual(''); // <-- input is reset
|
||||
expect($scope.form.input.$error.editable).toBeFalsy();
|
||||
expect($scope.form.input.$error.parse).toBeFalsy();
|
||||
expect(element.find('form')).not.toHaveClass('invalid'); // <-- form has no error (it didn't work prior to #6032 fix)
|
||||
});
|
||||
|
||||
it('should go through other validators after blur for typeahead-editable="false"', function () {
|
||||
var element = prepareInputEl(
|
||||
'<div><form name="form">' +
|
||||
@@ -470,6 +501,15 @@ describe('typeahead tests', function() {
|
||||
};
|
||||
expect(prepareInvalidDir).toThrow();
|
||||
});
|
||||
|
||||
it('should remove the id attribute from the original DOM element', function() {
|
||||
var element = prepareInputEl('<div><input id="typeahead-element" ng-model="result" uib-typeahead="item for item in source | filter:$viewValue" typeahead-show-hint="true"></div>');
|
||||
var inputEl = findInput(element);
|
||||
|
||||
expect(inputEl.size()).toBe(2);
|
||||
expect(inputEl.eq(0).attr('id')).toBe(undefined);
|
||||
expect(inputEl.eq(1).attr('id')).toBe('typeahead-element');
|
||||
});
|
||||
});
|
||||
|
||||
describe('shouldSelect', function() {
|
||||
|
||||
+38
-14
@@ -5,7 +5,7 @@ angular.module('ui.bootstrap.typeahead', ['ui.bootstrap.debounce', 'ui.bootstrap
|
||||
* Extracted to a separate service for ease of unit testing
|
||||
*/
|
||||
.factory('uibTypeaheadParser', ['$parse', function($parse) {
|
||||
// 00000111000000000000022200000000000000003333333333333330000000000044000
|
||||
// 000001111111100000000000002222222200000000000000003333333333333330000000000044444444000
|
||||
var TYPEAHEAD_REGEXP = /^\s*([\s\S]+?)(?:\s+as\s+([\s\S]+?))?\s+for\s+(?:([\$\w][\$\w\d]*))\s+in\s+([\s\S]+?)$/;
|
||||
return {
|
||||
parse: function(input) {
|
||||
@@ -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});
|
||||
}
|
||||
|
||||
@@ -152,6 +152,10 @@ angular.module('ui.bootstrap.typeahead', ['ui.bootstrap.debounce', 'ui.bootstrap
|
||||
'vertical-align': 'top',
|
||||
'background-color': 'transparent'
|
||||
});
|
||||
|
||||
if (hintInputElem.attr('id')) {
|
||||
hintInputElem.removeAttr('id'); // remove duplicate id if present.
|
||||
}
|
||||
inputsContainer.append(hintInputElem);
|
||||
hintInputElem.after(element);
|
||||
}
|
||||
@@ -402,13 +406,13 @@ angular.module('ui.bootstrap.typeahead', ['ui.bootstrap.debounce', 'ui.bootstrap
|
||||
case 38: // up arrow
|
||||
scope.activeIdx = (scope.activeIdx > 0 ? scope.activeIdx : scope.matches.length) - 1;
|
||||
scope.$digest();
|
||||
target = popUpEl.find('li')[scope.activeIdx];
|
||||
target = popUpEl[0].querySelectorAll('.uib-typeahead-match')[scope.activeIdx];
|
||||
target.parentNode.scrollTop = target.offsetTop;
|
||||
break;
|
||||
case 40: // down arrow
|
||||
scope.activeIdx = (scope.activeIdx + 1) % scope.matches.length;
|
||||
scope.$digest();
|
||||
target = popUpEl.find('li')[scope.activeIdx];
|
||||
target = popUpEl[0].querySelectorAll('.uib-typeahead-match')[scope.activeIdx];
|
||||
target.parentNode.scrollTop = target.offsetTop;
|
||||
break;
|
||||
default:
|
||||
@@ -426,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() {
|
||||
@@ -435,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() {
|
||||
@@ -450,9 +454,11 @@ angular.module('ui.bootstrap.typeahead', ['ui.bootstrap.debounce', 'ui.bootstrap
|
||||
}
|
||||
if (!isEditable && modelCtrl.$error.editable) {
|
||||
modelCtrl.$setViewValue();
|
||||
// Reset validity as we are clearing
|
||||
modelCtrl.$setValidity('editable', true);
|
||||
modelCtrl.$setValidity('parse', true);
|
||||
scope.$apply(function() {
|
||||
// Reset validity as we are clearing
|
||||
modelCtrl.$setValidity('editable', true);
|
||||
modelCtrl.$setValidity('parse', true);
|
||||
});
|
||||
element.val('');
|
||||
}
|
||||
hasFocus = false;
|
||||
@@ -501,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
|
||||
@@ -565,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,10 +1,8 @@
|
||||
<div class="panel" ng-class="panelClass || 'panel-default'">
|
||||
<div role="tab" id="{{::headingId}}" aria-selected="{{isOpen}}" class="panel-heading" ng-keypress="toggleOpen($event)">
|
||||
<h4 class="panel-title">
|
||||
<a role="button" data-toggle="collapse" href aria-expanded="{{isOpen}}" aria-controls="{{::panelId}}" 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 id="{{::panelId}}" aria-labelledby="{{::headingId}}" aria-hidden="{{!isOpen}}" role="tabpanel" class="panel-collapse collapse" uib-collapse="!isOpen">
|
||||
<div class="panel-body" ng-transclude></div>
|
||||
</div>
|
||||
<div role="tab" id="{{::headingId}}" aria-selected="{{isOpen}}" class="panel-heading" ng-keypress="toggleOpen($event)">
|
||||
<h4 class="panel-title">
|
||||
<a role="button" data-toggle="collapse" href aria-expanded="{{isOpen}}" aria-controls="{{::panelId}}" tabindex="0" class="accordion-toggle" ng-click="toggleOpen()" uib-accordion-transclude="heading" ng-disabled="isDisabled" uib-tabindex-toggle><span uib-accordion-header ng-class="{'text-muted': isDisabled}">{{heading}}</span></a>
|
||||
</h4>
|
||||
</div>
|
||||
<div id="{{::panelId}}" aria-labelledby="{{::headingId}}" aria-hidden="{{!isOpen}}" role="tabpanel" class="panel-collapse collapse" uib-collapse="!isOpen">
|
||||
<div class="panel-body" ng-transclude></div>
|
||||
</div>
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
<div class="alert" ng-class="['alert-' + (type || 'warning'), closeable ? 'alert-dismissible' : null]" role="alert">
|
||||
<button ng-show="closeable" type="button" class="close" ng-click="close({$event: $event})">
|
||||
<span aria-hidden="true">×</span>
|
||||
<span class="sr-only">Close</span>
|
||||
</button>
|
||||
<div ng-transclude></div>
|
||||
</div>
|
||||
<button ng-show="closeable" type="button" class="close" ng-click="close({$event: $event})">
|
||||
<span aria-hidden="true">×</span>
|
||||
<span class="sr-only">Close</span>
|
||||
</button>
|
||||
<div ng-transclude></div>
|
||||
|
||||
@@ -1,16 +1,14 @@
|
||||
<div ng-mouseenter="pause()" ng-mouseleave="play()" class="carousel" ng-swipe-right="prev()" ng-swipe-left="next()">
|
||||
<div class="carousel-inner" ng-transclude></div>
|
||||
<a role="button" href class="left carousel-control" ng-click="prev()" ng-class="{ disabled: isPrevDisabled() }" ng-show="slides.length > 1">
|
||||
<span aria-hidden="true" class="glyphicon glyphicon-chevron-left"></span>
|
||||
<span class="sr-only">previous</span>
|
||||
</a>
|
||||
<a role="button" href class="right carousel-control" ng-click="next()" ng-class="{ disabled: isNextDisabled() }" ng-show="slides.length > 1">
|
||||
<span aria-hidden="true" class="glyphicon glyphicon-chevron-right"></span>
|
||||
<span class="sr-only">next</span>
|
||||
</a>
|
||||
<ol class="carousel-indicators" ng-show="slides.length > 1">
|
||||
<li ng-repeat="slide in slides | orderBy:indexOfSlide track by $index" ng-class="{ active: isActive(slide) }" ng-click="select(slide)">
|
||||
<span class="sr-only">slide {{ $index + 1 }} of {{ slides.length }}<span ng-if="isActive(slide)">, currently active</span></span>
|
||||
</li>
|
||||
</ol>
|
||||
</div>
|
||||
<div class="carousel-inner" ng-transclude></div>
|
||||
<a role="button" href class="left carousel-control" ng-click="prev()" ng-class="{ disabled: isPrevDisabled() }" ng-show="slides.length > 1">
|
||||
<span aria-hidden="true" class="glyphicon glyphicon-chevron-left"></span>
|
||||
<span class="sr-only">previous</span>
|
||||
</a>
|
||||
<a role="button" href class="right carousel-control" ng-click="next()" ng-class="{ disabled: isNextDisabled() }" ng-show="slides.length > 1">
|
||||
<span aria-hidden="true" class="glyphicon glyphicon-chevron-right"></span>
|
||||
<span class="sr-only">next</span>
|
||||
</a>
|
||||
<ol class="carousel-indicators" ng-show="slides.length > 1">
|
||||
<li ng-repeat="slide in slides | orderBy:indexOfSlide track by $index" ng-class="{ active: isActive(slide) }" ng-click="select(slide)">
|
||||
<span class="sr-only">slide {{ $index + 1 }} of {{ slides.length }}<span ng-if="isActive(slide)">, currently active</span></span>
|
||||
</li>
|
||||
</ol>
|
||||
|
||||
@@ -1,3 +1 @@
|
||||
<div ng-class="{
|
||||
'active': active
|
||||
}" class="item text-center" ng-transclude></div>
|
||||
<div class="text-center" ng-transclude></div>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<div class="uib-datepicker" ng-switch="datepickerMode" role="application" ng-keydown="keydown($event)">
|
||||
<uib-daypicker ng-switch-when="day" tabindex="0"></uib-daypicker>
|
||||
<uib-monthpicker ng-switch-when="month" tabindex="0"></uib-monthpicker>
|
||||
<uib-yearpicker ng-switch-when="year" tabindex="0"></uib-yearpicker>
|
||||
<div ng-switch="datepickerMode">
|
||||
<div uib-daypicker ng-switch-when="day" tabindex="0" class="uib-daypicker"></div>
|
||||
<div uib-monthpicker ng-switch-when="month" tabindex="0" class="uib-monthpicker"></div>
|
||||
<div uib-yearpicker ng-switch-when="year" tabindex="0" class="uib-yearpicker"></div>
|
||||
</div>
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
<table class="uib-daypicker" role="grid" aria-labelledby="{{::uniqueId}}-title" aria-activedescendant="{{activeDateId}}">
|
||||
<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 class="uib-monthpicker" role="grid" aria-labelledby="{{::uniqueId}}-title" aria-activedescendant="{{activeDateId}}">
|
||||
<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 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-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 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 class="uib-yearpicker" role="grid" aria-labelledby="{{::uniqueId}}-title" aria-activedescendant="{{activeDateId}}">
|
||||
<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,12 +1,10 @@
|
||||
<div>
|
||||
<ul 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">
|
||||
<button type="button" class="btn btn-sm btn-info uib-datepicker-current" ng-click="select('today', $event)" ng-disabled="isDisabled('today')">{{ getText('current') }}</button>
|
||||
<button type="button" class="btn btn-sm btn-danger uib-clear" ng-click="select(null, $event)">{{ getText('clear') }}</button>
|
||||
</span>
|
||||
<button type="button" class="btn btn-sm btn-success pull-right uib-close" ng-click="close($event)">{{ getText('close') }}</button>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<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">
|
||||
<button type="button" class="btn btn-sm btn-info uib-datepicker-current" ng-click="select('today', $event)" ng-disabled="isDisabled('today')">{{ getText('current') }}</button>
|
||||
<button type="button" class="btn btn-sm btn-danger uib-clear" ng-click="select(null, $event)">{{ getText('clear') }}</button>
|
||||
</span>
|
||||
<button type="button" class="btn btn-sm btn-success pull-right uib-close" ng-click="close($event)">{{ getText('close') }}</button>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
<div class="modal-backdrop"
|
||||
uib-modal-animation-class="fade"
|
||||
modal-in-class="in"
|
||||
ng-style="{'z-index': 1040 + (index && 1 || 0) + index*10}"
|
||||
></div>
|
||||
@@ -1,6 +1 @@
|
||||
<div modal-render="{{$isRendered}}" tabindex="-1" role="dialog" class="modal"
|
||||
uib-modal-animation-class="fade"
|
||||
modal-in-class="in"
|
||||
ng-style="{'z-index': 1050 + index*10, display: 'block'}">
|
||||
<div class="modal-dialog {{size ? 'modal-' + size : ''}}"><div class="modal-content" uib-modal-transclude></div></div>
|
||||
</div>
|
||||
<div class="modal-dialog {{size ? 'modal-' + size : ''}}"><div class="modal-content" uib-modal-transclude></div></div>
|
||||
|
||||
@@ -1,4 +1,2 @@
|
||||
<ul class="pager">
|
||||
<li ng-class="{disabled: noPrevious()||ngDisabled, previous: align}"><a href ng-click="selectPage(page - 1, $event)">{{::getText('previous')}}</a></li>
|
||||
<li ng-class="{disabled: noNext()||ngDisabled, next: align}"><a href ng-click="selectPage(page + 1, $event)">{{::getText('next')}}</a></li>
|
||||
</ul>
|
||||
<li ng-class="{disabled: noPrevious()||ngDisabled, previous: align}"><a href ng-click="selectPage(page - 1, $event)" ng-disabled="noPrevious()||ngDisabled" uib-tabindex-toggle>{{::getText('previous')}}</a></li>
|
||||
<li ng-class="{disabled: noNext()||ngDisabled, next: align}"><a href ng-click="selectPage(page + 1, $event)" ng-disabled="noNext()||ngDisabled" uib-tabindex-toggle>{{::getText('next')}}</a></li>
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
<ul class="pagination">
|
||||
<li ng-if="::boundaryLinks" ng-class="{disabled: noPrevious()||ngDisabled}" class="pagination-first"><a href ng-click="selectPage(1, $event)">{{::getText('first')}}</a></li>
|
||||
<li ng-if="::directionLinks" ng-class="{disabled: noPrevious()||ngDisabled}" class="pagination-prev"><a href ng-click="selectPage(page - 1, $event)">{{::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)">{{page.text}}</a></li>
|
||||
<li ng-if="::directionLinks" ng-class="{disabled: noNext()||ngDisabled}" class="pagination-next"><a href ng-click="selectPage(page + 1, $event)">{{::getText('next')}}</a></li>
|
||||
<li ng-if="::boundaryLinks" ng-class="{disabled: noNext()||ngDisabled}" class="pagination-last"><a href ng-click="selectPage(totalPages, $event)">{{::getText('last')}}</a></li>
|
||||
</ul>
|
||||
<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>
|
||||
|
||||
@@ -1,11 +1,6 @@
|
||||
<div class="popover"
|
||||
tooltip-animation-class="fade"
|
||||
uib-tooltip-classes
|
||||
ng-class="{ in: isOpen() }">
|
||||
<div class="arrow"></div>
|
||||
<div class="arrow"></div>
|
||||
|
||||
<div class="popover-inner">
|
||||
<h3 class="popover-title" ng-bind="uibTitle" ng-if="uibTitle"></h3>
|
||||
<div class="popover-content" ng-bind-html="contentExp()"></div>
|
||||
</div>
|
||||
<div class="popover-inner">
|
||||
<h3 class="popover-title" ng-bind="uibTitle" ng-if="uibTitle"></h3>
|
||||
<div class="popover-content" ng-bind-html="contentExp()"></div>
|
||||
</div>
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user