Compare commits

..

85 Commits

Author SHA1 Message Date
Adam Gordon a2dee1bdeb docs: add steps to reproduce section to template (#6443)
* add new steps to reproduce section to github issue template to ask
issue filers to provide explicit steps to reproduce the issue so people
aren't left to guess or parse poorly written ones.
2017-01-30 13:13:38 -08:00
Wesley Cho 409eec4019 chore(release): starting 2.5.1 2017-01-28 05:34:16 -08:00
Wesley Cho b90485b2a4 chore(release): 2.5.0 2017-01-28 05:30:11 -08:00
Daniel Smith 0d79005f8d fix(angular): add compatibility with Angular 1.6
- Change how ngModelOptions is used to conform to Angular 1.6
- Catch rejections as per change to $q

Closes #6427
Fixes #6360
2017-01-28 05:26:34 -08:00
mattcollier e538d2f71f docs(collapse): fix typo
Closes #6398
Closes #6421
2017-01-23 11:31:37 -08:00
Matthias Dailey f5b357fd11 fix(tooltip): unbind keypress listener on hide
- Unbind listener on hide to fix memory leak

Closes #6423
Fixes #6405
2017-01-23 11:30:23 -08:00
Alex (Jinghao) Yan 761db7b4b7 fix(dropdown): do nothing if not open when clicking
- When clicking, do nothing if dropdown is closed on document listener

Closes #6414
2017-01-23 03:07:20 -08:00
tariqporter 86ee770834 fix(carousel): remove transition buffering
- Remove transition buffering

Closes #6367
Fixes #5967
2017-01-23 02:27:05 -08:00
david.humphrey@senecacollege.ca 47c4544e23 chore(npm): add fields to package.json
Closes #6418
2017-01-21 07:07:47 -08:00
Wesley Cho e8479514cd chore(release): bump to 2.5.0 2017-01-21 07:06:41 -08:00
Sherry Chang cf3f870735 Add CDNJS version badge in README.md (#6297)
This will add the badge to show its version on CDNJS and also link to its page on CDNJS!
2017-01-20 13:16:27 -08:00
Umer Farooq 7d3a7502fa feat(dropdown): make dropdown-append-to-body configurable (#6356)
* feat(dropdown): make dropdown-append-to-body configurable

Make dropdown-append-to-body accept a value which will be evaluated to
determine if the menu should be appended to body. If no value is
specified, or a non-false value is specified then the menu will be
appended to body. If the value is `false` then the menu will not be
appended to body.

* feat(dropdown): append and remove menu when menu is opened or closed

Only append and remove append-to and append-to-body menus when the
menu is opened or closed. This allows for the values of append-to and
append-to-body to be evaluated when the menu is toggled open, and also
prevents littering of the DOM.

* fix(dropdown): don't remove the dropdown-menu on close

Instead of removing the dropdown-menu on close, append it back to the
original element.
2017-01-20 13:14:29 -08:00
Jeff Carey 71dc691323 feat(pagination): Added menu and menuitem roles (closes #6383) (#6386)
feat(pagination): Added test
2017-01-20 13:13:54 -08:00
Scott Kao 42f3cc847a chore(license): update year to 2017 2017-01-10 05:23:17 -08:00
Wesley Cho b755559ec9 chore(release): starting 2.4.1 2016-12-29 23:19:51 -08:00
Wesley Cho 9e6d2a0a14 chore(changelog): update for 2.4.0 2016-12-29 23:17:21 -08:00
Wesley Cho 2596b9805f chore(release): 2.4.0 2016-12-29 23:15:22 -08:00
Douglas Ludlow 5a3e44a146 feat(dateparser): allow overriding of parsers
- Add ability to override parser

Closes #6370
Closes #6373
2016-12-29 23:12:17 -08:00
Wesley Cho 4872c05a32 docs(accordion): remove unnecessary div
- Remove unnecessary div creating a doubled border

Closes #6378
2016-12-27 03:01:04 -08:00
Wesley Cho fc686bb7a3 chore(release): starting 2.3.3 2016-12-27 01:59:02 -08:00
Wesley Cho 7f664f9f45 chore(release): 2.3.2 2016-12-27 01:54:51 -08:00
khlevon98 955848c3b1 fix(dropdown): re-add close
- Re-add closing via service

Closes #6382
Fixes #6321
Fixes #6357
Fixes #6364
2016-12-27 01:47:15 -08:00
Wesley Cho 7a1d54c8c3 chore(release): starting 2.3.2 2016-12-10 03:18:18 -08:00
Wesley Cho 0ed1a59aef chore(release): 2.3.1 2016-12-10 03:14:22 -08:00
Wesley Cho f2722b59a5 fix(dateparser): add new date format for angular 1.5+ only
- Only add parsing ability for new date format for angular 1.5+

Fixes #6349
2016-12-10 03:06:50 -08:00
béla 57ed7e4f7f chore(readme): fix variable used
Closes #6344
2016-12-02 06:23:16 -08:00
Steven Yeh 90848144e8 chore(changelog): fix generated links
- Fix links to use proper tags

Closes #6340
2016-12-01 22:35:18 -08:00
Wesley Cho a4d7076c8e chore(dropdown): fix accidental deletion 2016-11-29 23:25:21 -08:00
Wesley Cho c824731ae8 revert(dropdown): undo addition of unbind 2016-11-29 23:19:43 -08:00
Wesley Cho 1653afa210 refactor(dropdown): use container for class toggle detection 2016-11-29 01:47:23 -08:00
Wesley Cho 7e2f2c1bad chore(datepickerPopup): change to on/off 2016-11-29 00:19:19 -08:00
Wesley Cho 61f365abfd chore(timepicker): change to on/off 2016-11-29 00:18:36 -08:00
Wesley Cho ec2d9ad605 chore(typeahead): change to on/off 2016-11-29 00:17:13 -08:00
Wesley Cho 08b50ccb1c chore(dropdown): change to on/off 2016-11-29 00:14:33 -08:00
patricksmms 44ab0a8106 fix(dropDown): remove dropdown on close
- Unbind keybind on close

Closes #6326
Fixes #6314
2016-11-29 00:09:08 -08:00
Wesley Cho 1962485504 fix(datepickerPopup): change to toTimezone
- Change conversion to use `toTimezone`, as the date is going from UI date with timezone applied to timezone set by `ngModelOptions`

Fixes #6235
2016-11-27 02:26:52 -08:00
Jonathan Perez 8a4f625ef6 fix(modal): revert focus behavior on open
- On open, do not focus the first focusable element

Closes #6295
2016-11-27 02:18:17 -08:00
Wesley Cho 7be665399f chore(release): starting 2.3.1 2016-11-26 07:43:50 -08:00
Wesley Cho 2edb5d38cb chore(release): update changelog 2016-11-26 07:41:20 -08:00
Wesley Cho 4e06553f7c chore(release): 2.3.0 2016-11-26 07:38:21 -08:00
Janne 25ff206767 feat(dateparser): add LLLL support
- Add LLLL support. This requires Angular 1.5+ to take advantage of this.

Closes #6281
2016-11-26 07:35:37 -08:00
Wesley Cho 997813f0eb chore(release): starting 2.2.1 2016-10-10 08:30:03 -07:00
Wesley Cho 25db68e903 chore(release): 2.2.0 2016-10-10 08:26:52 -07:00
Mark Wheeldon 78559761dc fix(tooltip): cancel timeout when hidden
- Cancel timeout when hidden so that positioning does not incorrectly occur when not visible

Closes #6226
Fixes #6221
2016-10-10 07:47:56 -07:00
nmccready 14384fc40f fix(dropdown): exit keybind is not open
- Exit keybindFilter if the $scope is not present

Closes #6278
Fixes #6208
2016-10-10 07:44:34 -07:00
JuliaUsanova 58f1813aca fix(position): correct scrollbar width calculation
Closes #6273
2016-10-10 07:42:36 -07:00
Eric Schneider 9666c64f4a feat(timepicker): add validation information
- Add validation information for specific violations, allowing users to style classes generated by Angular

Closes #6230
Closes #6259
2016-09-24 21:14:55 -04:00
Nicholas Heiner f9f7e02d15 fix(modal): improve ARIA support.
- Add accessibility with multiple modals

Closes #6203
2016-09-23 23:21:25 -05:00
Wesley Cho a4bea6f229 chore(release): starting 2.2.0 2016-09-23 23:19:16 -05:00
Wesley Cho c00851a18b chore(release): 2.1.4 2016-09-23 23:15:28 -05:00
Sergey Lysenko c687acd489 docs: create retina-ready favicon
- Update favicon with retina-friendly icon

Closes #6255
2016-09-23 23:09:24 -05:00
Eric Schneider 3f70d76327 fix(datepicker): improve accessibility
- Improve accessibility with forward and previous buttons with screen readers

Closes #6247
2016-09-23 23:06:17 -05:00
Nathan Herzing 08ee30a91e fix(dropdown): prevent premature scope removal
- Toggle false after focus executes

Closes #6238
Fixes #6225
2016-09-23 23:03:16 -05:00
ravi-123 7671bd6ed4 docs(popover): add popover-class 2016-09-23 23:01:31 -05:00
Evan Theurer 1a132dd757 docs(collapse): add navigation collapse example
- Add an example for using collapse with the bootstrap navbar

Closes #3481
Closes #6217
2016-09-01 20:18:59 -07:00
Wesley Cho 9881a27397 chore(release): starting 2.1.4 2016-08-25 08:38:42 -07:00
Wesley Cho 294b151342 chore(release): 2.1.3 2016-08-25 08:35:57 -07:00
Axel Navarro 969eb9c74d fix(modal): compile only once with component
- When using the component option, do not compile content in order to take advantage of later compilation

Closes #6202
Fixes #6201
2016-08-24 20:26:33 -07:00
Wesley Cho 048d85a2ad chore(release): starting 2.1.3 2016-08-22 10:04:12 -07:00
Wesley Cho 876e72a0df chore(release): 2.1.2 2016-08-22 10:01:02 -07:00
Wesley Cho 6734908108 test(modal): fix custom matchers
- Trigger a test failure if resolved on rejected promise and rejected on resolved promise
- Trigger a test failure if neither resolved or rejected when expected resolved/rejected promise

Closes #6195
2016-08-22 09:57:20 -07:00
Wesley Cho 515bcf2933 fix(collapse): revert change to transition css
- Revert change to `getScrollFromElement` function

Closes #6196
Fixes #6194
2016-08-22 09:54:30 -07:00
Wesley Cho 148371fb6d docs(tooltip): add info on Safari bug
- Add information on Safari bug, affecting all versions up to 9 at the least

Closes #5445
2016-08-22 09:53:11 -07:00
Axel Navarro 8047c06258 docs(modal): added a simple component modal demo
Closes #6193
2016-08-22 08:03:06 -07:00
Axel Navarro 3e8ecfffe0 fix(modal): close and dismiss bindings on component
- Correctly bind close and dismiss handlers on component usage

Closes #6192
Fixes #6191
2016-08-21 12:12:44 -07:00
Georgii Dolzhykov ddcacb7a83 fix(datepicker): fix accidental global
- Fix accidental global usage

Closes #6188
2016-08-21 04:42:13 -07:00
Wesley Cho 915eda0540 chore(release): starting 2.1.2 2016-08-20 22:54:15 -07:00
Wesley Cho f9eede7555 chore(release): 2.1.1 2016-08-20 22:49:30 -07:00
Axel Navarro fb5fabf580 fix(modal): switch to .append
- Switch to `append` since `html` is jQuery specific

Closes #6187
Fixes #6186
2016-08-20 22:46:35 -07:00
Wesley Cho aef24cde4b fix(collapse): default to css
- Default to value set from CSS for height/width

Closes #6182
Fixes #6045
2016-08-20 05:58:59 -07:00
Wesley Cho b893a93f82 docs(collapse): fix height on horizontal
- Fix height on horizontal collapse usage

Closes #6050
Closes #6183
2016-08-20 05:58:03 -07:00
Wesley Cho 6352f13e85 chore(release): starting 2.1.1 2016-08-19 14:34:37 -07:00
Wesley Cho 76cd65ef1a chore(release): 2.1.0 2016-08-19 14:31:32 -07:00
Wesley Cho 84cc2cf5cc fix(collapse): set overflow to hidden on transition
- When collapse is transitioning, set `overflow: hidden` to avoid possibility of scrollbars changing size

Closes #6180
Fixes #5474
2016-08-19 10:37:06 -07:00
Wesley Cho 2ade0545a9 feat(modal): add component support
- Add support for `component` option

Closes #5683
Closes #6179
2016-08-19 09:54:05 -07:00
Stuart Forster f5ff12c0a4 fix(tooltip): close tooltip on esc
- On keypress of escape, close tooltip/popover

Closes #6177
Fixes #6108
2016-08-18 14:10:17 -07:00
Wesley Cho f147d22f5c fix(datepickerPopup): apply timezone conversion
- Apply timezone conversion to selecting today

Closes #6173
Fixes #6147
2016-08-18 13:21:15 -07:00
Wesley Cho f798a47d48 chore: bump angular version 2016-08-18 10:12:21 -07:00
Wesley Cho 43a1f75e7c chore(demo): bump versions 2016-08-18 10:11:55 -07:00
Nick Heiner 4a5e6a7418 fix(modal): improve ARIA support
- Add aria-labelledby and aria-described by support

Closes #4772
2016-08-17 22:09:30 -07:00
Wesley Cho af6c2aaca0 chore(release): starting 2.1.0 2016-08-17 22:09:18 -07:00
Kirkland 61415e1968 Adding roles to the datepicker templates to resolve an ADA violation. (#6170)
Closes #6170
2016-08-17 17:45:34 -07:00
Wesley Cho 5123e38a49 docs(tooltip): add ngSanitize to Plunker
- Add ngSanitize to Plunkers for uib-tooltip-html and uib-popover-html Plunkers

Closes #6165
Fixes #6157
2016-08-16 08:36:00 -07:00
Wesley Cho ca20be4667 fix(collapse): remove unnecessary inherit
- Remove unnecessary `inherit` property, which causes issues when custom widths are used, including percentage based widths

Closes #6164
Fixes #6163
2016-08-16 08:35:09 -07:00
Wesley Cho d9dd5803ae chore(release): starting 2.0.3 2016-08-15 13:45:53 -07:00
52 changed files with 1435 additions and 422 deletions
+2
View File
@@ -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
View File
@@ -13,7 +13,7 @@ addons:
before_install:
- export DISPLAY=:99.0
- sh -e /etc/init.d/xvfb start
- npm install --quiet -g grunt-cli karma
- npm install --quiet -g karma
script: grunt
sudo: false
+194 -41
View File
@@ -1,5 +1,145 @@
<a name="2.5.0"></a>
# [2.5.0](https://github.com/angular-ui/bootstrap/compare/2.4.0...v2.5.0) (2017-01-28)
### Bug Fixes
* **angular:** add compatibility with Angular 1.6([0d79005](https://github.com/angular-ui/bootstrap/commit/0d79005)), closes [#6427](https://github.com/angular-ui/bootstrap/issues/6427) [#6360](https://github.com/angular-ui/bootstrap/issues/6360)
* **carousel:** remove transition buffering([86ee770](https://github.com/angular-ui/bootstrap/commit/86ee770)), closes [#6367](https://github.com/angular-ui/bootstrap/issues/6367) [#5967](https://github.com/angular-ui/bootstrap/issues/5967)
* **dropdown:** do nothing if not open when clicking([761db7b](https://github.com/angular-ui/bootstrap/commit/761db7b)), closes [#6414](https://github.com/angular-ui/bootstrap/issues/6414)
* **tooltip:** unbind keypress listener on hide([f5b357f](https://github.com/angular-ui/bootstrap/commit/f5b357f)), closes [#6423](https://github.com/angular-ui/bootstrap/issues/6423) [#6405](https://github.com/angular-ui/bootstrap/issues/6405)
### Features
* **dropdown:** make dropdown-append-to-body configurable ([#6356](https://github.com/angular-ui/bootstrap/issues/6356))([7d3a750](https://github.com/angular-ui/bootstrap/commit/7d3a750))
* **pagination:** Added menu and menuitem roles (closes [#6383](https://github.com/angular-ui/bootstrap/issues/6383)) ([#6386](https://github.com/angular-ui/bootstrap/issues/6386))([71dc691](https://github.com/angular-ui/bootstrap/commit/71dc691)), closes [#6383](https://github.com/angular-ui/bootstrap/issues/6383) [(#6386](https://github.com/(/issues/6386)
<a name="2.4.0"></a>
# [2.4.0](https://github.com/angular-ui/bootstrap/compare/2.3.2...v2.4.0) (2016-12-30)
### Features
* **dateparser:** allow overriding of parsers([5a3e44a](https://github.com/angular-ui/bootstrap/commit/5a3e44a)), closes [#6370](https://github.com/angular-ui/bootstrap/issues/6370) [#6373](https://github.com/angular-ui/bootstrap/issues/6373)
<a name="2.3.2"></a>
## [2.3.2](https://github.com/angular-ui/bootstrap/compare/2.3.1...v2.3.2) (2016-12-27)
### Bug Fixes
* **dropdown:** re-add close([955848c](https://github.com/angular-ui/bootstrap/commit/955848c)), closes [#6382](https://github.com/angular-ui/bootstrap/issues/6382) [#6321](https://github.com/angular-ui/bootstrap/issues/6321) [#6357](https://github.com/angular-ui/bootstrap/issues/6357) [#6364](https://github.com/angular-ui/bootstrap/issues/6364)
<a name="2.3.1"></a>
## [2.3.1](https://github.com/angular-ui/bootstrap/compare/2.3.0...v2.3.1) (2016-12-10)
### Bug Fixes
* **dateparser:** add new date format for angular 1.5+ only([f2722b5](https://github.com/angular-ui/bootstrap/commit/f2722b5)), closes [#6349](https://github.com/angular-ui/bootstrap/issues/6349)
* **datepickerPopup:** change to toTimezone only([1962485](https://github.com/angular-ui/bootstrap/commit/1962485)), fixes [#6235](https://github.com/angular-ui/bootstrap/issues/6235)
* **modal:** revert focus behavior on open([8a4f625](https://github.com/angular-ui/bootstrap/commit/8a4f625)), closes [#6295](https://github.com/angular-ui/bootstrap/issues/6295)
<a name="2.3.0"></a>
# [2.3.0](https://github.com/angular-ui/bootstrap/compare/2.2.0...2.3.0) (2016-11-26)
### Features
* **dateparser:** add LLLL support([25ff206](https://github.com/angular-ui/bootstrap/commit/25ff206)), closes [#6281](https://github.com/angular-ui/bootstrap/issues/6281)
<a name="2.2.0"></a>
# [2.2.0](https://github.com/angular-ui/bootstrap/compare/2.1.4...2.2.0) (2016-10-10)
### Bug Fixes
* **dropdown:** exit keybind is not open ([14384fc](https://github.com/angular-ui/bootstrap/commit/14384fc)), closes [#6278](https://github.com/angular-ui/bootstrap/issues/6278) [#6208](https://github.com/angular-ui/bootstrap/issues/6208)
* **modal:** improve ARIA support. ([f9f7e02](https://github.com/angular-ui/bootstrap/commit/f9f7e02)), closes [#6203](https://github.com/angular-ui/bootstrap/issues/6203)
* **position:** correct scrollbar width calculation ([58f1813](https://github.com/angular-ui/bootstrap/commit/58f1813)), closes [#6273](https://github.com/angular-ui/bootstrap/issues/6273)
* **tooltip:** cancel timeout when hidden ([7855976](https://github.com/angular-ui/bootstrap/commit/7855976)), closes [#6226](https://github.com/angular-ui/bootstrap/issues/6226) [#6221](https://github.com/angular-ui/bootstrap/issues/6221)
### Features
* **timepicker:** add validation information ([9666c64](https://github.com/angular-ui/bootstrap/commit/9666c64)), closes [#6230](https://github.com/angular-ui/bootstrap/issues/6230) [#6259](https://github.com/angular-ui/bootstrap/issues/6259)
<a name="2.1.4"></a>
## [2.1.4](https://github.com/angular-ui/bootstrap/compare/2.1.3...2.1.4) (2016-09-24)
### Bug Fixes
* **datepicker:** improve accessibility ([3f70d76](https://github.com/angular-ui/bootstrap/commit/3f70d76)), closes [#6247](https://github.com/angular-ui/bootstrap/issues/6247)
* **dropdown:** prevent premature scope removal ([08ee30a](https://github.com/angular-ui/bootstrap/commit/08ee30a)), closes [#6238](https://github.com/angular-ui/bootstrap/issues/6238) [#6225](https://github.com/angular-ui/bootstrap/issues/6225)
<a name="2.1.3"></a>
## [2.1.3](https://github.com/angular-ui/bootstrap/compare/2.1.2...2.1.3) (2016-08-25)
### Bug Fixes
* **modal:** compile only once with component ([969eb9c](https://github.com/angular-ui/bootstrap/commit/969eb9c)), closes [#6202](https://github.com/angular-ui/bootstrap/issues/6202) [#6201](https://github.com/angular-ui/bootstrap/issues/6201)
<a name="2.1.2"></a>
## [2.1.2](https://github.com/angular-ui/bootstrap/compare/2.1.1...2.1.2) (2016-08-22)
### Bug Fixes
* **collapse:** revert change to transition css ([515bcf2](https://github.com/angular-ui/bootstrap/commit/515bcf2)), closes [#6196](https://github.com/angular-ui/bootstrap/issues/6196) [#6194](https://github.com/angular-ui/bootstrap/issues/6194)
* **datepicker:** fix accidental global ([ddcacb7](https://github.com/angular-ui/bootstrap/commit/ddcacb7)), closes [#6188](https://github.com/angular-ui/bootstrap/issues/6188)
* **modal:** close and dismiss bindings on component ([3e8ecff](https://github.com/angular-ui/bootstrap/commit/3e8ecff)), closes [#6192](https://github.com/angular-ui/bootstrap/issues/6192) [#6191](https://github.com/angular-ui/bootstrap/issues/6191)
<a name="2.1.1"></a>
## [2.1.1](https://github.com/angular-ui/bootstrap/compare/2.1.0...2.1.1) (2016-08-21)
### Bug Fixes
* **collapse:** default to css([aef24cd](https://github.com/angular-ui/bootstrap/commit/aef24cd)), closes [#6182](https://github.com/angular-ui/bootstrap/issues/6182) [#6045](https://github.com/angular-ui/bootstrap/issues/6045)
* **modal:** switch to .append([fb5fabf](https://github.com/angular-ui/bootstrap/commit/fb5fabf)), closes [#6187](https://github.com/angular-ui/bootstrap/issues/6187) [#6186](https://github.com/angular-ui/bootstrap/issues/6186)
<a name="2.1.0"></a>
# [2.1.0](https://github.com/angular-ui/bootstrap/compare/2.0.2...2.1.0) (2016-08-19)
### Bug Fixes
* **collapse:** remove unnecessary inherit ([ca20be4](https://github.com/angular-ui/bootstrap/commit/ca20be4)), closes [#6164](https://github.com/angular-ui/bootstrap/issues/6164) [#6163](https://github.com/angular-ui/bootstrap/issues/6163)
* **collapse:** set overflow to hidden on transition ([84cc2cf](https://github.com/angular-ui/bootstrap/commit/84cc2cf)), closes [#6180](https://github.com/angular-ui/bootstrap/issues/6180) [#5474](https://github.com/angular-ui/bootstrap/issues/5474)
* **datepickerPopup:** apply timezone conversion ([f147d22](https://github.com/angular-ui/bootstrap/commit/f147d22)), closes [#6173](https://github.com/angular-ui/bootstrap/issues/6173) [#6147](https://github.com/angular-ui/bootstrap/issues/6147)
* **modal:** improve ARIA support ([4a5e6a7](https://github.com/angular-ui/bootstrap/commit/4a5e6a7)), closes [#4772](https://github.com/angular-ui/bootstrap/issues/4772)
* **tooltip:** close tooltip on esc ([f5ff12c](https://github.com/angular-ui/bootstrap/commit/f5ff12c)), closes [#6177](https://github.com/angular-ui/bootstrap/issues/6177) [#6108](https://github.com/angular-ui/bootstrap/issues/6108)
### Features
* **modal:** add component support ([2ade054](https://github.com/angular-ui/bootstrap/commit/2ade054)), closes [#5683](https://github.com/angular-ui/bootstrap/issues/5683) [#6179](https://github.com/angular-ui/bootstrap/issues/6179)
<a name="2.0.2"></a>
## [2.0.2](https://github.com/angular-ui/bootstrap/compare/2.0.1...v2.0.2) (2016-08-15)
## [2.0.2](https://github.com/angular-ui/bootstrap/compare/2.0.1...2.0.2) (2016-08-15)
### Bug Fixes
@@ -10,7 +150,7 @@
<a name="2.0.1"></a>
## [2.0.1](https://github.com/angular-ui/bootstrap/compare/2.0.0...v2.0.1) (2016-08-02)
## [2.0.1](https://github.com/angular-ui/bootstrap/compare/2.0.0...2.0.1) (2016-08-02)
### Bug Fixes
@@ -20,7 +160,7 @@
<a name="2.0.0"></a>
# [2.0.0](https://github.com/angular-ui/bootstrap/compare/1.3.3...v2.0.0) (2016-07-20)
# [2.0.0](https://github.com/angular-ui/bootstrap/compare/1.3.3...2.0.0) (2016-07-20)
### Bug Fixes
@@ -100,7 +240,7 @@ This could affect selectors if they are being used.
<a name="1.3.3"></a>
## [1.3.3](https://github.com/angular-ui/bootstrap/compare/1.3.2...v1.3.3) (2016-05-23)
## [1.3.3](https://github.com/angular-ui/bootstrap/compare/1.3.2...1.3.3) (2016-05-23)
### Bug Fixes
@@ -126,7 +266,7 @@ This could affect selectors if they are being used.
<a name="1.3.2"></a>
## [1.3.2](https://github.com/angular-ui/bootstrap/compare/1.3.1...v1.3.2) (2016-04-14)
## [1.3.2](https://github.com/angular-ui/bootstrap/compare/1.3.1...1.3.2) (2016-04-14)
### Bug Fixes
@@ -152,7 +292,7 @@ This could affect selectors if they are being used.
<a name="1.3.1"></a>
## [1.3.1](https://github.com/angular-ui/bootstrap/compare/1.3.0...v1.3.1) (2016-04-05)
## [1.3.1](https://github.com/angular-ui/bootstrap/compare/1.3.0...1.3.1) (2016-04-05)
### Bug Fixes
@@ -163,7 +303,7 @@ This could affect selectors if they are being used.
<a name="1.3.0"></a>
# [1.3.0](https://github.com/angular-ui/bootstrap/compare/1.2.5...v1.3.0) (2016-04-05)
# [1.3.0](https://github.com/angular-ui/bootstrap/compare/1.2.5...1.3.0) (2016-04-05)
### Bug Fixes
@@ -206,7 +346,7 @@ attribute pass-throughs in the popup
<a name="1.2.5"></a>
## [1.2.5](https://github.com/angular-ui/bootstrap/compare/1.2.4...v1.2.5) (2016-03-20)
## [1.2.5](https://github.com/angular-ui/bootstrap/compare/1.2.4...1.2.5) (2016-03-20)
### Bug Fixes
@@ -229,13 +369,13 @@ attribute pass-throughs in the popup
<a name="1.2.4"></a>
## [1.2.4](https://github.com/angular-ui/bootstrap/compare/1.2.3...v1.2.4) (2016-03-06)
## [1.2.4](https://github.com/angular-ui/bootstrap/compare/1.2.3...1.2.4) (2016-03-06)
<a name="1.2.3"></a>
## [1.2.3](https://github.com/angular-ui/bootstrap/compare/1.2.2...v1.2.3) (2016-03-06)
## [1.2.3](https://github.com/angular-ui/bootstrap/compare/1.2.2...1.2.3) (2016-03-06)
### Bug Fixes
@@ -254,7 +394,7 @@ attribute pass-throughs in the popup
<a name="1.2.2"></a>
## [1.2.2](https://github.com/angular-ui/bootstrap/compare/1.2.1...v1.2.2) (2016-03-03)
## [1.2.2](https://github.com/angular-ui/bootstrap/compare/1.2.1...1.2.2) (2016-03-03)
### Bug Fixes
@@ -271,7 +411,7 @@ attribute pass-throughs in the popup
<a name="1.2.1"></a>
## [1.2.1](https://github.com/angular-ui/bootstrap/compare/1.2.0...v1.2.1) (2016-02-27)
## [1.2.1](https://github.com/angular-ui/bootstrap/compare/1.2.0...1.2.1) (2016-02-27)
### Bug Fixes
@@ -281,7 +421,7 @@ attribute pass-throughs in the popup
<a name="1.2.0"></a>
# [1.2.0](https://github.com/angular-ui/bootstrap/compare/1.1.2...v1.2.0) (2016-02-26)
# [1.2.0](https://github.com/angular-ui/bootstrap/compare/1.1.2...1.2.0) (2016-02-26)
### Bug Fixes
@@ -341,7 +481,7 @@ template
<a name="1.1.2"></a>
## [1.1.2](https://github.com/angular-ui/bootstrap/compare/1.1.1...v1.1.2) (2016-02-01)
## [1.1.2](https://github.com/angular-ui/bootstrap/compare/1.1.1...1.1.2) (2016-02-01)
### Bug Fixes
@@ -359,7 +499,7 @@ template
<a name="1.1.1"></a>
## [1.1.1](https://github.com/angular-ui/bootstrap/compare/v1.1.0...v1.1.1) (2016-01-25)
## [1.1.1](https://github.com/angular-ui/bootstrap/compare/1.1.0...1.1.1) (2016-01-25)
### Bug Fixes
@@ -409,7 +549,7 @@ template in one's app and provide the necessary CSS
<a name="1.0.3"></a>
# [1.0.3](https://github.com/angular-ui/bootstrap/compare/v1.0.2...v1.0.3) (2016-01-12)
# [1.0.3](https://github.com/angular-ui/bootstrap/compare/1.0.2...1.0.3) (2016-01-12)
## Bug Fixes
@@ -419,7 +559,7 @@ template in one's app and provide the necessary CSS
<a name="1.0.2"></a>
# [1.0.2](https://github.com/angular-ui/bootstrap/compare/v1.0.1...v1.0.2) (2016-01-12)
# [1.0.2](https://github.com/angular-ui/bootstrap/compare/1.0.1...1.0.2) (2016-01-12)
## Bug Fixes
@@ -429,7 +569,7 @@ template in one's app and provide the necessary CSS
<a name="1.0.1"></a>
# [1.0.1](https://github.com/angular-ui/bootstrap/compare/1.0.0...v1.0.1) (2016-01-12)
# [1.0.1](https://github.com/angular-ui/bootstrap/compare/1.0.0...1.0.1) (2016-01-12)
## Bug Fixes
@@ -450,7 +590,7 @@ template in one's app and provide the necessary CSS
<a name="1.0.0"></a>
# [1.0.0](https://github.com/angular-ui/bootstrap/compare/0.14.3...v1.0.0) (2016-01-08)
# [1.0.0](https://github.com/angular-ui/bootstrap/compare/0.14.3...1.0.0) (2016-01-08)
## Bug Fixes
@@ -595,7 +735,7 @@ $scope.typeaheadContainer = angular.element(document.querySelector('#typeaheadCo
<a name="0.14.3"></a>
# [0.14.3](https://github.com/angular-ui/bootstrap/compare/0.14.2...v0.14.3) (2015-10-23)
# [0.14.3](https://github.com/angular-ui/bootstrap/compare/0.14.2...0.14.3) (2015-10-23)
## Bug Fixes
@@ -620,7 +760,7 @@ $scope.typeaheadContainer = angular.element(document.querySelector('#typeaheadCo
<a name="0.14.2"></a>
# [0.14.2](https://github.com/angular-ui/bootstrap/compare/0.14.1...v0.14.2) (2015-10-14)
# [0.14.2](https://github.com/angular-ui/bootstrap/compare/0.14.1...0.14.2) (2015-10-14)
## Bug Fixes
@@ -632,7 +772,7 @@ $scope.typeaheadContainer = angular.element(document.querySelector('#typeaheadCo
<a name="0.14.1"></a>
# [0.14.1](https://github.com/angular-ui/bootstrap/compare/0.14.0...v0.14.1) (2015-10-11)
# [0.14.1](https://github.com/angular-ui/bootstrap/compare/0.14.0...0.14.1) (2015-10-11)
## Bug Fixes
@@ -815,7 +955,7 @@ $scope.typeaheadContainer = angular.element(document.querySelector('#typeaheadCo
<a name"0.13.3"></a>
# 0.13.3 (2015-08-09)
# [0.13.3](https://github.com/angular-ui/bootstrap/compare/0.13.2...0.13.3) (2015-08-09)
## Bug Fixes
@@ -902,7 +1042,7 @@ Closes #4080
<a name"0.13.2"></a>
# 0.13.2 (2015-08-02)
# [0.13.2](https://github.com/angular-ui/bootstrap/compare/0.13.1...0.13.2) (2015-08-02)
## Bug Fixes
@@ -948,7 +1088,7 @@ Closes #4080
<a name"0.13.1"></a>
# 0.13.1 (2015-07-23)
# [0.13.1](https://github.com/angular-ui/bootstrap/compare/0.13.0...0.13.1) (2015-07-23)
## Bug Fixes
@@ -1009,7 +1149,7 @@ Closes #4080
<a name="0.13.0"></a>
# 0.13.0 (2015-05-02)
# [0.13.0](https://github.com/angular-ui/bootstrap/compare/0.12.1...0.13.0) (2015-05-02)
## Bug Fixes
@@ -1108,7 +1248,8 @@ Closes #4080
* **transition:** deprecate transition module ([8a552443](https://github.com/angular-ui/bootstrap/commit/8a552443741d1e5b4b29d9da9c7e9990fa37886c), closes [#3497](https://github.com/angular-ui/bootstrap/issues/3497))
# 0.12.1 (2015-02-20)
<a name="0.12.1"></a>
# [0.12.1](https://github.com/angular-ui/bootstrap/compare/0.12.0...0.12.1) (2015-02-20)
## Bug Fixes
@@ -1116,7 +1257,7 @@ Closes #4080
- incorrect position when text wraps ([5726e3ef](http://github.com/angular-ui/bootstrap/commit/5726e3ef))
<a name="0.12.0"></a>
# 0.12.0 (2014-11-16)
# [0.12.0](https://github.com/angular-ui/bootstrap/compare/0.11.2...0.12.0) (2014-11-16)
## Bug Fixes
@@ -1169,11 +1310,13 @@ once* and can no longer be changed after initialization.
```
# 0.11.2 (2014-09-26)
<a name="0.11.2"></a>
# [0.11.2](https://github.com/angular-ui/bootstrap/compare/0.11.1...0.11.2) (2014-09-26)
Revert breaking change in **dropdown** ([1a998c4](http://github.com/angular-ui/bootstrap/commit/1a998c4))
# 0.11.1 (2014-09-26)
<a name="0.11.1"></a>
# [0.11.1](https://github.com/angular-ui/bootstrap/compare/0.11.0...0.11.1) (2014-09-26)
## Features
@@ -1207,7 +1350,8 @@ Revert breaking change in **dropdown** ([1a998c4](http://github.com/angular-ui/b
- allow multiple line expression ([c7db0df4](http://github.com/angular-ui/bootstrap/commit/c7db0df4))
- replace ng-if with ng-show in matches popup ([a0be450d](http://github.com/angular-ui/bootstrap/commit/a0be450d))
# 0.11.0 (2014-05-01)
<a name="0.11.0"></a>
# [0.11.0](https://github.com/angular-ui/bootstrap/compare/0.10.0...0.11.0) (2014-05-01)
## Features
@@ -1385,7 +1529,8 @@ Revert breaking change in **dropdown** ([1a998c4](http://github.com/angular-ui/b
<tabset type="{{navtype}}" ...></tabset>
```
# 0.10.0 (2014-01-13)
<a name="0.10.0"></a>
# [0.10.0](https://github.com/angular-ui/bootstrap/compare/0.9.0...0.10.0) (2014-01-13)
_This release adds AngularJS 1.2 support_
@@ -1406,7 +1551,8 @@ _This release adds AngularJS 1.2 support_
- **tooltip:**
- performance and scope fixes ([c0df3201](http://github.com/angular-ui/bootstrap/commit/c0df3201))
# 0.9.0 (2013-12-28)
<a name="0.9.0"></a>
# [0.9.0](https://github.com/angular-ui/bootstrap/compare/0.8.0...0.9.0) (2013-12-28)
_This release adds Bootstrap3 support_
@@ -1449,7 +1595,8 @@ _This release adds Bootstrap3 support_
- **tooltip:**
- re-position tooltip after draw ([a99b3608](http://github.com/angular-ui/bootstrap/commit/a99b3608))
# 0.8.0 (2013-12-28)
<a name="0.8.0"></a>
# [0.8.0](https://github.com/angular-ui/bootstrap/compare/0.7.0...0.8.0) (2013-12-28)
## Features
@@ -1516,7 +1663,8 @@ _This release adds Bootstrap3 support_
<progress><bar ng-repeat="obj in objs" value="obj.var" type="{{obj.type}}"></bar></progress>
```
# 0.7.0 (2013-11-22)
<a name="0.7.0"></a>
# [0.7.0](https://github.com/angular-ui/bootstrap/compare/0.6.0...0.7.0) (2013-11-22)
## Features
@@ -1564,7 +1712,8 @@ _This release adds Bootstrap3 support_
- evaluate matches source against a correct scope ([fd21214d](http://github.com/angular-ui/bootstrap/commit/fd21214d))
- support IE8 ([0e9f9980](http://github.com/angular-ui/bootstrap/commit/0e9f9980))
# 0.6.0 (2013-09-08)
<a name="0.6.0"></a>
# [0.6.0](https://github.com/angular-ui/bootstrap/compare/0.6.0...0.7.0) (2013-09-08)
## Features
@@ -1643,7 +1792,8 @@ Check the documentation for the `$modal` service to migrate from `$dialog`
The placment='mouse' is gone with no equivalent
# 0.5.0 (2013-08-04)
<a name="0.5.0"></a>
# [0.5.0](https://github.com/angular-ui/bootstrap/compare/0.4.0...0.5.0) (2013-08-04)
## Features
@@ -1726,7 +1876,8 @@ The placment='mouse' is gone with no equivalent
<pagination first-text="{{var1}}" ...></pagination>
```
# 0.4.0 (2013-06-24)
<a name="0.4.0"></a>
# [0.4.0](https://github.com/angular-ui/bootstrap/compare/0.3.0...0.4.0) (2013-06-24)
## Features
@@ -1844,8 +1995,8 @@ The placment='mouse' is gone with no equivalent
</tabset>
```
# 0.3.0 (2013-04-30)
<a name="0.3.0"></a>
# [0.3.0](https://github.com/angular-ui/bootstrap/compare/0.2.0...0.3.0) (2013-04-30)
## Features
@@ -1886,7 +2037,8 @@ The placment='mouse' is gone with no equivalent
- correctly higlight matches if query contains regexp-special chars ([467afcd6](https://github.com/angular-ui/bootstrap/commit/467afcd6))
- fix matches pop-up positioning issues ([74beecdb](https://github.com/angular-ui/bootstrap/commit/74beecdb))
# 0.2.0 (2013-03-03)
<a name="0.2.0"></a>
# [0.2.0](https://github.com/angular-ui/bootstrap/compare/0.1.0...0.2.0) (2013-03-03)
## Features
@@ -1920,6 +2072,7 @@ The placment='mouse' is gone with no equivalent
- **typeahead:**
- update inputs value on mapping where label is not derived from the model ([a5f64de](https://github.com/angular-ui/bootstrap/commit/a5f64de))
<a name="0.1.0"></a>
# 0.1.0 (2013-02-02)
_Very first, initial release_.
+2 -2
View File
@@ -9,8 +9,8 @@ module.exports = function(grunt) {
grunt.util.linefeed = '\n';
grunt.initConfig({
ngversion: '1.5.5',
bsversion: '3.3.6',
ngversion: '1.6.1',
bsversion: '3.3.7',
modules: [],//to be filled in by build task
pkg: grunt.file.readJSON('package.json'),
dist: 'dist',
+1 -1
View File
@@ -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
+2 -1
View File
@@ -3,6 +3,7 @@
[![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/angular-ui/bootstrap?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
[![Build Status](https://secure.travis-ci.org/angular-ui/bootstrap.svg)](http://travis-ci.org/angular-ui/bootstrap)
[![devDependency Status](https://david-dm.org/angular-ui/bootstrap/dev-status.svg?branch=master)](https://david-dm.org/angular-ui/bootstrap#info=devDependencies)
[![CDNJS](https://img.shields.io/cdnjs/v/angular-ui-bootstrap.svg)](https://cdnjs.com/libraries/angular-ui-bootstrap/)
### Quick links
- [Demo](#demo)
@@ -131,7 +132,7 @@ The other modules, such as `accordion` in the example below, do not have CSS res
import accordion from 'angular-ui-bootstrap/src/accordion';
import typeahead from 'angular-ui-bootstrap/src/typeahead/index-nocss.js';
angular.module('myModule', [accordion, datepicker]);
angular.module('myModule', [accordion, typeahead]);
```
# Versioning
+1 -1
View File
@@ -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

+2 -1
View File
@@ -17,6 +17,7 @@ angular.module('plunker', [])
' <head>\n' +
' <script src="//ajax.googleapis.com/ajax/libs/angularjs/'+ngVersion+'/angular.js"></script>\n' +
' <script src="//ajax.googleapis.com/ajax/libs/angularjs/'+ngVersion+'/angular-animate.js"></script>\n' +
' <script src="//ajax.googleapis.com/ajax/libs/angularjs/'+ngVersion+'/angular-sanitize.js"></script>\n' +
' <script src="//angular-ui.github.io/bootstrap/ui-bootstrap-tpls-'+version+'.js"></script>\n' +
' <script src="example.js"></script>\n' +
' <link href="//netdna.bootstrapcdn.com/bootstrap/'+bsVersion+'/css/bootstrap.min.css" rel="stylesheet">\n' +
@@ -28,7 +29,7 @@ angular.module('plunker', [])
};
var scriptContent = function(content) {
return "angular.module('ui.bootstrap.demo', ['ngAnimate', 'ui.bootstrap']);" + "\n" + content;
return "angular.module('ui.bootstrap.demo', ['ngAnimate', 'ngSanitize', 'ui.bootstrap']);" + "\n" + content;
};
addField('description', 'http://angular-ui.github.io/bootstrap/');
+14 -4
View File
@@ -1,8 +1,15 @@
{
"author": "https://github.com/angular-ui/bootstrap/graphs/contributors",
"name": "angular-ui-bootstrap",
"version": "2.0.2",
"version": "2.5.1-SNAPSHOT",
"description": "Native AngularJS (Angular) directives for Bootstrap",
"homepage": "http://angular-ui.github.io/bootstrap/",
"keywords": [
"angularjs",
"angular",
"bootstrap",
"ui"
],
"dependencies": {},
"directories": {
"lib": "src/"
@@ -15,6 +22,7 @@
],
"main": "index.js",
"scripts": {
"demo": "grunt after-test && static dist -a 0.0.0.0 -H '{\"Cache-Control\": \"no-cache, must-revalidate\"}'",
"test": "grunt"
},
"repository": {
@@ -22,10 +30,11 @@
"url": "https://github.com/angular-ui/bootstrap.git"
},
"devDependencies": {
"angular": "1.5.5",
"angular-mocks": "1.5.5",
"angular-sanitize": "1.5.5",
"angular": "1.6.1",
"angular-mocks": "1.6.1",
"angular-sanitize": "1.6.1",
"grunt": "^0.4.5",
"grunt-cli": "^1.2.0",
"grunt-contrib-concat": "^1.0.0",
"grunt-contrib-copy": "^1.0.0",
"grunt-contrib-uglify": "^1.0.1",
@@ -44,6 +53,7 @@
"load-grunt-tasks": "^3.3.0",
"lodash": "^4.1.0",
"marked": "^0.3.5",
"node-static": "^0.7.8",
"semver": "^5.0.1",
"shelljs": "^0.6.0"
},
+11 -13
View File
@@ -1,18 +1,16 @@
<div ng-controller="AccordionDemoCtrl">
<script type="text/ng-template" id="group-template.html">
<div class="panel panel-default">
<div class="panel-heading">
<h4 class="panel-title" style="color:#fa39c3">
<a href tabindex="0" class="accordion-toggle" ng-click="toggleOpen()" uib-accordion-transclude="heading">
<span uib-accordion-header ng-class="{'text-muted': isDisabled}">
{{heading}}
</span>
</a>
</h4>
</div>
<div class="panel-collapse collapse" uib-collapse="!isOpen">
<div class="panel-body" style="text-align: right" ng-transclude></div>
</div>
<div class="panel-heading">
<h4 class="panel-title" style="color:#fa39c3">
<a href tabindex="0" class="accordion-toggle" ng-click="toggleOpen()" uib-accordion-transclude="heading">
<span uib-accordion-header ng-class="{'text-muted': isDisabled}">
{{heading}}
</span>
</a>
</h4>
</div>
<div class="panel-collapse collapse" uib-collapse="!isOpen">
<div class="panel-body" style="text-align: right" ng-transclude></div>
</div>
</script>
+2 -2
View File
@@ -551,7 +551,7 @@ describe('uib-accordion', function() {
});
it('attaches the same scope to the transcluded heading and body', function() {
expect(findGroupLink(0).find('span.ng-scope').scope().$id).toBe(findGroupBody(0).find('span').scope().$id);
expect(findGroupLink(0).scope().$id).toBe(findGroupBody(0).scope().$id);
});
it('should wrap the transcluded content in a span', function() {
@@ -580,7 +580,7 @@ describe('uib-accordion', function() {
});
it('attaches the same scope to the transcluded heading and body', function() {
expect(findGroupLink(0).find('span.ng-scope').scope().$id).toBe(findGroupBody(0).find('span').scope().$id);
expect(findGroupLink(0).scope().$id).toBe(findGroupBody(0).scope().$id);
});
it('should have disabled styling when is-disabled is true', isDisabledStyleCheck);
+1 -1
View File
@@ -36,7 +36,7 @@ describe('uib-alert', function() {
}
function findContent(index) {
return element.find('div[ng-transclude] span').eq(index);
return element.find('div[ng-transclude]').eq(index);
}
it('should expose the controller to the view', function() {
+1 -24
View File
@@ -5,7 +5,7 @@ angular.module('ui.bootstrap.carousel', [])
slides = self.slides = $scope.slides = [],
SLIDE_DIRECTION = 'uib-slideDirection',
currentIndex = $scope.active,
currentInterval, isPlaying, bufferedTransitions = [];
currentInterval, isPlaying;
var destroyed = false;
$element.addClass('carousel');
@@ -67,11 +67,6 @@ angular.module('ui.bootstrap.carousel', [])
self.removeSlide = function(slide) {
var index = findSlideIndex(slide);
var bufferedIndex = bufferedTransitions.indexOf(slides[index]);
if (bufferedIndex !== -1) {
bufferedTransitions.splice(bufferedIndex, 1);
}
//get the index of the slide inside the carousel
slides.splice(index, 1);
if (slides.length > 0 && currentIndex === index) {
@@ -95,7 +90,6 @@ angular.module('ui.bootstrap.carousel', [])
if (slides.length === 0) {
currentIndex = null;
$scope.active = null;
clearBufferedTransitions();
}
};
@@ -110,8 +104,6 @@ angular.module('ui.bootstrap.carousel', [])
if (nextSlide.slide.index !== currentIndex &&
!$scope.$currentTransition) {
goNext(nextSlide.slide, nextIndex, direction);
} else if (nextSlide && nextSlide.slide.index !== currentIndex && $scope.$currentTransition) {
bufferedTransitions.push(slides[nextIndex]);
}
};
@@ -180,12 +172,6 @@ angular.module('ui.bootstrap.carousel', [])
}
});
function clearBufferedTransitions() {
while (bufferedTransitions.length) {
bufferedTransitions.shift();
}
}
function getSlideByIndex(index) {
for (var i = 0, l = slides.length; i < l; ++i) {
if (slides[i].index === index) {
@@ -221,14 +207,6 @@ angular.module('ui.bootstrap.carousel', [])
if (phase === 'close') {
$scope.$currentTransition = null;
$animate.off('addClass', element);
if (bufferedTransitions.length) {
var nextSlide = bufferedTransitions.pop().slide;
var nextIndex = nextSlide.index;
var nextDirection = nextIndex > self.getCurrentIndex() ? 'next' : 'prev';
clearBufferedTransitions();
goNext(nextSlide, nextIndex, nextDirection);
}
}
});
}
@@ -259,7 +237,6 @@ angular.module('ui.bootstrap.carousel', [])
function resetTransition(slides) {
if (!slides.length) {
$scope.$currentTransition = null;
clearBufferedTransitions();
}
}
+10 -6
View File
@@ -18,14 +18,12 @@ angular.module('ui.bootstrap.collapse', [])
horizontal = !!('horizontal' in attrs);
if (horizontal) {
css = {
width: 'auto',
height: 'inherit'
width: ''
};
cssTo = {width: '0'};
} else {
css = {
width: 'inherit',
height: 'auto'
height: ''
};
cssTo = {height: '0'};
}
@@ -61,14 +59,20 @@ angular.module('ui.bootstrap.collapse', [])
$animateCss(element, {
addClass: 'in',
easing: 'ease',
css: {
overflow: 'hidden'
},
to: getScrollFromElement(element[0])
}).start()['finally'](expandDone);
} else {
$animate.addClass(element, 'in', {
css: {
overflow: 'hidden'
},
to: getScrollFromElement(element[0])
}).then(expandDone);
}
});
}, angular.noop);
}
function expandDone() {
@@ -107,7 +111,7 @@ angular.module('ui.bootstrap.collapse', [])
to: cssTo
}).then(collapseDone);
}
});
}, angular.noop);
}
function collapseDone() {
+28 -1
View File
@@ -1,4 +1,31 @@
<style>
.horizontal-collapse {
height: 70px;
}
.navbar-collapse.in {
overflow-y: hidden;
}
</style>
<div ng-controller="CollapseDemoCtrl">
<p>Resize window to less than 768 pixels to display mobile menu toggle button.</p>
<nav class="navbar navbar-default" role="navigation">
<div class="navbar-header">
<button type="button" class="navbar-toggle" ng-click="isNavCollapsed = !isNavCollapsed">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand" href="#">A menu</a>
</div>
<div class="collapse navbar-collapse" uib-collapse="isNavCollapsed">
<ul class="nav navbar-nav">
<li><a href="#">Link 1</a></li>
<li><a href="#">Link 2</a></li>
</ul>
</div>
</nav>
<hr>
<button type="button" class="btn btn-default" ng-click="isCollapsed = !isCollapsed">Toggle collapse Vertically</button>
<hr>
<div uib-collapse="isCollapsed">
@@ -7,7 +34,7 @@
<button type="button" class="btn btn-default" ng-click="isCollapsedHorizontal = !isCollapsedHorizontal">Toggle collapse Horizontally</button>
<hr>
<div uib-collapse="isCollapsedHorizontal" horizontal>
<div class="horizontal-collapse" uib-collapse="isCollapsedHorizontal" horizontal>
<div class="well well-lg">Some content</div>
</div>
</div>
+1
View File
@@ -1,4 +1,5 @@
angular.module('ui.bootstrap.demo').controller('CollapseDemoCtrl', function ($scope) {
$scope.isNavCollapsed = true;
$scope.isCollapsed = false;
$scope.isCollapsedHorizontal = false;
});
+4 -1
View File
@@ -27,8 +27,11 @@
<i class="glyphicon glyphicon-eye-open"></i>
_(Default: `false`)_ -
Whether the element should be collapsed or not.
* `horizontal`
<small class="badge">$</small> -
An optional attribute that permit to collapse horizontally.
### Known Issues
When using the `horizontal` attribute with this directive, CSS can reflow as the collapse element goes from `0px` to its desired end width, which can result in height changes. This can cause animations to not appear to run. The best way around this is to set a fixed height via CSS on the horizontal collapse element so that this situation does not occur, and so the animation can run as expected.
+27 -1
View File
@@ -1,6 +1,6 @@
angular.module('ui.bootstrap.dateparser', [])
.service('uibDateParser', ['$log', '$locale', 'dateFilter', 'orderByFilter', function($log, $locale, dateFilter, orderByFilter) {
.service('uibDateParser', ['$log', '$locale', 'dateFilter', 'orderByFilter', 'filterFilter', function($log, $locale, dateFilter, orderByFilter, filterFilter) {
// Pulled from https://github.com/mbostock/d3/blob/master/src/format/requote.js
var SPECIAL_CHARACTERS_REGEXP = /[\\\^\$\*\+\?\|\[\]\(\)\.\{\}]/g;
@@ -230,10 +230,36 @@ angular.module('ui.bootstrap.dateparser', [])
formatter: function(date) { return dateFilter(date, 'G'); }
}
];
if (angular.version.major >= 1 && angular.version.minor > 4) {
formatCodeToRegex.push({
key: 'LLLL',
regex: $locale.DATETIME_FORMATS.STANDALONEMONTH.join('|'),
apply: function(value) { this.month = $locale.DATETIME_FORMATS.STANDALONEMONTH.indexOf(value); },
formatter: function(date) { return dateFilter(date, 'LLLL'); }
});
}
};
this.init();
function getFormatCodeToRegex(key) {
return filterFilter(formatCodeToRegex, {key: key}, true)[0];
}
this.getParser = function (key) {
var f = getFormatCodeToRegex(key);
return f && f.apply || null;
};
this.overrideParser = function (key, parser) {
var f = getFormatCodeToRegex(key);
if (f && angular.isFunction(parser)) {
this.parsers = {};
f.apply = parser;
}
}.bind(this);
function createParser(format) {
var map = [], regex = format.split('');
+3
View File
@@ -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.
+34
View File
@@ -84,6 +84,16 @@ describe('date parser', function() {
expectFilter(new Date(2011, 4, 2, 0), 'dd-M!-yy', '02-05-11');
expectFilter(oldDate, 'yyyy/M!/dd', '0001/03/06');
});
it('should work correctly for `LLLL`', function() {
expectFilter(new Date(2013, 7, 24, 0), 'LLLL/dd/yyyy', 'August/24/2013');
expectFilter(new Date(2004, 10, 7, 0), 'dd.LLLL.yy', '07.November.04');
expectFilter(new Date(2011, 4, 18, 0), 'dd-LLLL-yy', '18-May-11');
expectFilter(new Date(1980, 1, 5, 0), 'LLLL/dd/yyyy', 'February/05/1980');
expectFilter(new Date(1955, 2, 5, 0), 'yyyy/LLLL/dd', '1955/March/05');
expectFilter(new Date(2011, 5, 2, 0), 'dd-LLLL-yy', '02-June-11');
expectFilter(oldDate, 'yyyy/LLLL/dd', '0001/March/06');
});
it('should work correctly for `d`', function() {
expectFilter(new Date(2013, 10, 17, 0), 'd.MMMM.yy', '17.November.13');
@@ -781,4 +791,28 @@ describe('date parser', function() {
});
});
});
describe('overrideParser', function() {
var twoDigitYearParser = function (value) {
this.year = +value + (+value > 30 ? 1900 : 2000);
};
it('should get the current parser', function() {
expect(dateParser.getParser('yy')).toBeTruthy();
});
it('should override the parser', function() {
dateParser.overrideParser('yy', twoDigitYearParser);
expect(dateParser.parse('68', 'yy').getFullYear()).toEqual(1968);
expect(dateParser.parse('67', 'yy').getFullYear()).toEqual(1967);
expect(dateParser.parse('31', 'yy').getFullYear()).toEqual(1931);
expect(dateParser.parse('30', 'yy').getFullYear()).toEqual(2030);
});
it('should clear cached parsers', function() {
expect(dateParser.parse('68', 'yy').getFullYear()).toEqual(2068);
dateParser.overrideParser('yy', twoDigitYearParser);
expect(dateParser.parse('68', 'yy').getFullYear()).toEqual(1968);
});
});
});
+46 -16
View File
@@ -104,7 +104,7 @@ angular.module('ui.bootstrap.datepicker', ['ui.bootstrap.dateparser', 'ui.bootst
$scope.$watch('datepickerOptions.' + key, function(value) {
if (value) {
if (angular.isDate(value)) {
self[key] = dateParser.fromTimezone(new Date(value), ngModelOptions.timezone);
self[key] = dateParser.fromTimezone(new Date(value), ngModelOptions.getOption('timezone'));
} else {
if ($datepickerLiteralWarning) {
$log.warn('Literal date support has been deprecated, please switch to date object usage');
@@ -114,7 +114,7 @@ angular.module('ui.bootstrap.datepicker', ['ui.bootstrap.dateparser', 'ui.bootst
}
} else {
self[key] = datepickerConfig[key] ?
dateParser.fromTimezone(new Date(datepickerConfig[key]), ngModelOptions.timezone) :
dateParser.fromTimezone(new Date(datepickerConfig[key]), ngModelOptions.getOption('timezone')) :
null;
}
@@ -126,7 +126,7 @@ angular.module('ui.bootstrap.datepicker', ['ui.bootstrap.dateparser', 'ui.bootst
case 'minMode':
if ($scope.datepickerOptions[key]) {
$scope.$watch(function() { return $scope.datepickerOptions[key]; }, function(value) {
self[key] = $scope[key] = angular.isDefined(value) ? value : datepickerOptions[key];
self[key] = $scope[key] = angular.isDefined(value) ? value : $scope.datepickerOptions[key];
if (key === 'minMode' && self.modes.indexOf($scope.datepickerOptions.datepickerMode) < self.modes.indexOf(self[key]) ||
key === 'maxMode' && self.modes.indexOf($scope.datepickerOptions.datepickerMode) > self.modes.indexOf(self[key])) {
$scope.datepickerMode = self[key];
@@ -161,14 +161,13 @@ angular.module('ui.bootstrap.datepicker', ['ui.bootstrap.dateparser', 'ui.bootst
this.init = function(ngModelCtrl_) {
ngModelCtrl = ngModelCtrl_;
ngModelOptions = ngModelCtrl_.$options ||
$scope.datepickerOptions.ngModelOptions ||
datepickerConfig.ngModelOptions;
ngModelOptions = extractOptions(ngModelCtrl);
if ($scope.datepickerOptions.initDate) {
self.activeDate = dateParser.fromTimezone($scope.datepickerOptions.initDate, ngModelOptions.timezone) || new Date();
self.activeDate = dateParser.fromTimezone($scope.datepickerOptions.initDate, ngModelOptions.getOption('timezone')) || new Date();
$scope.$watch('datepickerOptions.initDate', function(initDate) {
if (initDate && (ngModelCtrl.$isEmpty(ngModelCtrl.$modelValue) || ngModelCtrl.$invalid)) {
self.activeDate = dateParser.fromTimezone(initDate, ngModelOptions.timezone);
self.activeDate = dateParser.fromTimezone(initDate, ngModelOptions.getOption('timezone'));
self.refreshView();
}
});
@@ -178,8 +177,8 @@ angular.module('ui.bootstrap.datepicker', ['ui.bootstrap.dateparser', 'ui.bootst
var date = ngModelCtrl.$modelValue ? new Date(ngModelCtrl.$modelValue) : new Date();
this.activeDate = !isNaN(date) ?
dateParser.fromTimezone(date, ngModelOptions.timezone) :
dateParser.fromTimezone(new Date(), ngModelOptions.timezone);
dateParser.fromTimezone(date, ngModelOptions.getOption('timezone')) :
dateParser.fromTimezone(new Date(), ngModelOptions.getOption('timezone'));
ngModelCtrl.$render = function() {
self.render();
@@ -192,7 +191,7 @@ angular.module('ui.bootstrap.datepicker', ['ui.bootstrap.dateparser', 'ui.bootst
isValid = !isNaN(date);
if (isValid) {
this.activeDate = dateParser.fromTimezone(date, ngModelOptions.timezone);
this.activeDate = dateParser.fromTimezone(date, ngModelOptions.getOption('timezone'));
} else if (!$datepickerSuppressError) {
$log.error('Datepicker directive: "ng-model" value must be a Date object');
}
@@ -209,7 +208,7 @@ angular.module('ui.bootstrap.datepicker', ['ui.bootstrap.dateparser', 'ui.bootst
}
var date = ngModelCtrl.$viewValue ? new Date(ngModelCtrl.$viewValue) : null;
date = dateParser.fromTimezone(date, ngModelOptions.timezone);
date = dateParser.fromTimezone(date, ngModelOptions.getOption('timezone'));
ngModelCtrl.$setValidity('dateDisabled', !date ||
this.element && !this.isDisabled(date));
}
@@ -217,9 +216,9 @@ angular.module('ui.bootstrap.datepicker', ['ui.bootstrap.dateparser', 'ui.bootst
this.createDateObject = function(date, format) {
var model = ngModelCtrl.$viewValue ? new Date(ngModelCtrl.$viewValue) : null;
model = dateParser.fromTimezone(model, ngModelOptions.timezone);
model = dateParser.fromTimezone(model, ngModelOptions.getOption('timezone'));
var today = new Date();
today = dateParser.fromTimezone(today, ngModelOptions.timezone);
today = dateParser.fromTimezone(today, ngModelOptions.getOption('timezone'));
var time = this.compare(date, today);
var dt = {
date: date,
@@ -265,9 +264,9 @@ angular.module('ui.bootstrap.datepicker', ['ui.bootstrap.dateparser', 'ui.bootst
$scope.select = function(date) {
if ($scope.datepickerMode === self.minMode) {
var dt = ngModelCtrl.$viewValue ? dateParser.fromTimezone(new Date(ngModelCtrl.$viewValue), ngModelOptions.timezone) : new Date(0, 0, 0, 0, 0, 0, 0);
var dt = ngModelCtrl.$viewValue ? dateParser.fromTimezone(new Date(ngModelCtrl.$viewValue), ngModelOptions.getOption('timezone')) : new Date(0, 0, 0, 0, 0, 0, 0);
dt.setFullYear(date.getFullYear(), date.getMonth(), date.getDate());
dt = dateParser.toTimezone(dt, ngModelOptions.timezone);
dt = dateParser.toTimezone(dt, ngModelOptions.getOption('timezone'));
ngModelCtrl.$setViewValue(dt);
ngModelCtrl.$render();
} else {
@@ -352,6 +351,37 @@ angular.module('ui.bootstrap.datepicker', ['ui.bootstrap.dateparser', 'ui.bootst
$scope.datepickerMode = mode;
$scope.datepickerOptions.datepickerMode = mode;
}
function extractOptions(ngModelCtrl) {
var ngModelOptions;
if (angular.version.minor < 6) { // in angular < 1.6 $options could be missing
// guarantee a value
ngModelOptions = ngModelCtrl.$options ||
$scope.datepickerOptions.ngModelOptions ||
datepickerConfig.ngModelOptions ||
{};
// mimic 1.6+ api
ngModelOptions.getOption = function (key) {
return ngModelOptions[key];
};
} else { // in angular >=1.6 $options is always present
// ng-model-options defaults timezone to null; don't let its precedence squash a non-null value
var timezone = ngModelCtrl.$options.getOption('timezone') ||
($scope.datepickerOptions.ngModelOptions ? $scope.datepickerOptions.ngModelOptions.timezone : null) ||
(datepickerConfig.ngModelOptions ? datepickerConfig.ngModelOptions.timezone : null);
// values passed to createChild override existing values
ngModelOptions = ngModelCtrl.$options // start with a ModelOptions instance
.createChild(datepickerConfig.ngModelOptions) // lowest precedence
.createChild($scope.datepickerOptions.ngModelOptions)
.createChild(ngModelCtrl.$options) // highest precedence
.createChild({timezone: timezone}); // to keep from squashing a non-null value
}
return ngModelOptions;
}
}])
.controller('UibDaypickerController', ['$scope', '$element', 'dateFilter', function(scope, $element, dateFilter) {
+30 -11
View File
@@ -32,11 +32,7 @@ function($scope, $element, $attrs, $compile, $log, $parse, $window, $document, $
this.init = function(_ngModel_) {
ngModel = _ngModel_;
ngModelOptions = angular.isObject(_ngModel_.$options) ?
_ngModel_.$options :
{
timezone: null
};
ngModelOptions = extractOptions(ngModel);
closeOnDateSelection = angular.isDefined($attrs.closeOnDateSelection) ?
$scope.$parent.$eval($attrs.closeOnDateSelection) :
datepickerPopupConfig.closeOnDateSelection;
@@ -127,13 +123,13 @@ function($scope, $element, $attrs, $compile, $log, $parse, $window, $document, $
value = new Date(value);
}
$scope.date = dateParser.fromTimezone(value, ngModelOptions.timezone);
$scope.date = dateParser.fromTimezone(value, ngModelOptions.getOption('timezone'));
return dateParser.filter($scope.date, dateFormat);
});
} else {
ngModel.$formatters.push(function(value) {
$scope.date = dateParser.fromTimezone(value, ngModelOptions.timezone);
$scope.date = dateParser.fromTimezone(value, ngModelOptions.getOption('timezone'));
return value;
});
}
@@ -185,7 +181,7 @@ function($scope, $element, $attrs, $compile, $log, $parse, $window, $document, $
$scope.isDisabled = function(date) {
if (date === 'today') {
date = dateParser.fromTimezone(new Date(), ngModelOptions.timezone);
date = dateParser.fromTimezone(new Date(), ngModelOptions.getOption('timezone'));
}
var dates = {};
@@ -242,7 +238,8 @@ function($scope, $element, $attrs, $compile, $log, $parse, $window, $document, $
date = new Date($scope.date);
date.setFullYear(today.getFullYear(), today.getMonth(), today.getDate());
} else {
date = new Date(today.setHours(0, 0, 0, 0));
date = dateParser.fromTimezone(today, ngModelOptions.getOption('timezone'));
date.setHours(0, 0, 0, 0);
}
}
$scope.dateSelection(date);
@@ -332,11 +329,11 @@ function($scope, $element, $attrs, $compile, $log, $parse, $window, $document, $
if (angular.isString(viewValue)) {
var date = parseDateString(viewValue);
if (!isNaN(date)) {
return dateParser.fromTimezone(date, ngModelOptions.timezone);
return dateParser.toTimezone(date, ngModelOptions.getOption('timezone'));
}
}
return ngModel.$options && ngModel.$options.allowInvalid ? viewValue : undefined;
return ngModelOptions.getOption('allowInvalid') ? viewValue : undefined;
}
function validator(modelValue, viewValue) {
@@ -411,6 +408,28 @@ function($scope, $element, $attrs, $compile, $log, $parse, $window, $document, $
}
}
function extractOptions(ngModelCtrl) {
var ngModelOptions;
if (angular.version.minor < 6) { // in angular < 1.6 $options could be missing
// guarantee a value
ngModelOptions = angular.isObject(ngModelCtrl.$options) ?
ngModelCtrl.$options :
{
timezone: null
};
// mimic 1.6+ api
ngModelOptions.getOption = function (key) {
return ngModelOptions[key];
};
} else { // in angular >=1.6 $options is always present
ngModelOptions = ngModelCtrl.$options;
}
return ngModelOptions;
}
$scope.$on('uib:datepicker.mode', function() {
$timeout(positionPopup, 0, false);
});
+2 -2
View File
@@ -397,7 +397,7 @@ describe('datepicker popup', function() {
it('stops the ESC key from propagating if the dropdown is open, but not when closed', function() {
var documentKey = -1;
var getKey = function(evt) { documentKey = evt.which; };
$document.bind('keydown', getKey);
$document.on('keydown', getKey);
triggerKeyDown(inputEl, 'esc');
expect(documentKey).toBe(-1);
@@ -405,7 +405,7 @@ describe('datepicker popup', function() {
triggerKeyDown(inputEl, 'esc');
expect(documentKey).toBe(27);
$document.unbind('keydown', getKey);
$document.off('keydown', getKey);
});
});
+1 -1
View File
@@ -25,7 +25,7 @@ Each of these parts need to be used as attribute directives.
* `dropdown-append-to-body`
<small class="badge">B</small>
_(Default: `false`)_ -
Appends the inner dropdown-menu to the body element.
Appends the inner dropdown-menu to the body element if the attribute is present without a value, or with a non `false` value.
* `is-open`
<small class="badge">$</small>
+118 -34
View File
@@ -1,14 +1,33 @@
angular.module('ui.bootstrap.dropdown', ['ui.bootstrap.position'])
angular.module('ui.bootstrap.dropdown', ['ui.bootstrap.multiMap', 'ui.bootstrap.position'])
.constant('uibDropdownConfig', {
appendToOpenClass: 'uib-dropdown-open',
openClass: 'open'
})
.service('uibDropdownService', ['$document', '$rootScope', function($document, $rootScope) {
.service('uibDropdownService', ['$document', '$rootScope', '$$multiMap', function($document, $rootScope, $$multiMap) {
var openScope = null;
var openedContainers = $$multiMap.createNew();
this.open = function(dropdownScope, element) {
this.isOnlyOpen = function(dropdownScope, appendTo) {
var openedDropdowns = openedContainers.get(appendTo);
if (openedDropdowns) {
var openDropdown = openedDropdowns.reduce(function(toClose, dropdown) {
if (dropdown.scope === dropdownScope) {
return dropdown;
}
return toClose;
}, {});
if (openDropdown) {
return openedDropdowns.length === 1;
}
}
return false;
};
this.open = function(dropdownScope, element, appendTo) {
if (!openScope) {
$document.on('click', closeDropdown);
}
@@ -18,20 +37,58 @@ angular.module('ui.bootstrap.dropdown', ['ui.bootstrap.position'])
}
openScope = dropdownScope;
if (!appendTo) {
return;
}
var openedDropdowns = openedContainers.get(appendTo);
if (openedDropdowns) {
var openedScopes = openedDropdowns.map(function(dropdown) {
return dropdown.scope;
});
if (openedScopes.indexOf(dropdownScope) === -1) {
openedContainers.put(appendTo, {
scope: dropdownScope
});
}
} else {
openedContainers.put(appendTo, {
scope: dropdownScope
});
}
};
this.close = function(dropdownScope, element) {
this.close = function(dropdownScope, element, appendTo) {
if (openScope === dropdownScope) {
openScope = null;
$document.off('click', closeDropdown);
$document.off('keydown', this.keybindFilter);
openScope = null;
}
if (!appendTo) {
return;
}
var openedDropdowns = openedContainers.get(appendTo);
if (openedDropdowns) {
var dropdownToClose = openedDropdowns.reduce(function(toClose, dropdown) {
if (dropdown.scope === dropdownScope) {
return dropdown;
}
return toClose;
}, {});
if (dropdownToClose) {
openedContainers.remove(appendTo, dropdownToClose);
}
}
};
var closeDropdown = function(evt) {
// This method may still be called during the same mouse event that
// unbound this event handler. So check openScope before proceeding.
if (!openScope) { return; }
if (!openScope || !openScope.isOpen) { return; }
if (evt && openScope.getAutoClose() === 'disabled') { return; }
@@ -48,8 +105,8 @@ angular.module('ui.bootstrap.dropdown', ['ui.bootstrap.position'])
return;
}
openScope.isOpen = false;
openScope.focusToggleElement();
openScope.isOpen = false;
if (!$rootScope.$$phase) {
openScope.$apply();
@@ -57,6 +114,11 @@ angular.module('ui.bootstrap.dropdown', ['ui.bootstrap.position'])
};
this.keybindFilter = function(evt) {
if (!openScope) {
// see this.close as ESC could have been pressed which kills the scope so we can not proceed
return;
}
var dropdownElement = openScope.getDropdownElement();
var toggleElement = openScope.getToggleElement();
var dropdownElementTargeted = dropdownElement && dropdownElement[0].contains(evt.target);
@@ -82,8 +144,6 @@ angular.module('ui.bootstrap.dropdown', ['ui.bootstrap.position'])
getIsOpen,
setIsOpen = angular.noop,
toggleInvoker = $attrs.onToggle ? $parse($attrs.onToggle) : angular.noop,
appendToBody = false,
appendTo = null,
keynavEnabled = false,
selectedOption = null,
body = $document.find('body');
@@ -100,26 +160,7 @@ angular.module('ui.bootstrap.dropdown', ['ui.bootstrap.position'])
});
}
if (angular.isDefined($attrs.dropdownAppendTo)) {
var appendToEl = $parse($attrs.dropdownAppendTo)(scope);
if (appendToEl) {
appendTo = angular.element(appendToEl);
}
}
appendToBody = angular.isDefined($attrs.dropdownAppendToBody);
keynavEnabled = angular.isDefined($attrs.keyboardNav);
if (appendToBody && !appendTo) {
appendTo = body;
}
if (appendTo && self.dropdownMenu) {
appendTo.append(self.dropdownMenu);
$element.on('$destroy', function handleDestroyEvent() {
self.dropdownMenu.remove();
});
}
};
this.toggle = function(open) {
@@ -191,7 +232,42 @@ angular.module('ui.bootstrap.dropdown', ['ui.bootstrap.position'])
}
};
function removeDropdownMenu() {
$element.append(self.dropdownMenu);
}
scope.$watch('isOpen', function(isOpen, wasOpen) {
var appendTo = null,
appendToBody = false;
if (angular.isDefined($attrs.dropdownAppendTo)) {
var appendToEl = $parse($attrs.dropdownAppendTo)(scope);
if (appendToEl) {
appendTo = angular.element(appendToEl);
}
}
if (angular.isDefined($attrs.dropdownAppendToBody)) {
var appendToBodyValue = $parse($attrs.dropdownAppendToBody)(scope);
if (appendToBodyValue !== false) {
appendToBody = true;
}
}
if (appendToBody && !appendTo) {
appendTo = body;
}
if (appendTo && self.dropdownMenu) {
if (isOpen) {
appendTo.append(self.dropdownMenu);
$element.on('$destroy', removeDropdownMenu);
} else {
$element.off('$destroy', removeDropdownMenu);
removeDropdownMenu();
}
}
if (appendTo && self.dropdownMenu) {
var pos = $position.positionElements($element, self.dropdownMenu, 'bottom-left', true),
css,
@@ -239,10 +315,18 @@ angular.module('ui.bootstrap.dropdown', ['ui.bootstrap.position'])
}
var openContainer = appendTo ? appendTo : $element;
var hasOpenClass = openContainer.hasClass(appendTo ? appendToOpenClass : openClass);
var dropdownOpenClass = appendTo ? appendToOpenClass : openClass;
var hasOpenClass = openContainer.hasClass(dropdownOpenClass);
var isOnlyOpen = uibDropdownService.isOnlyOpen($scope, appendTo);
if (hasOpenClass === !isOpen) {
$animate[isOpen ? 'addClass' : 'removeClass'](openContainer, appendTo ? appendToOpenClass : openClass).then(function() {
var toggleClass;
if (appendTo) {
toggleClass = !isOnlyOpen ? 'addClass' : 'removeClass';
} else {
toggleClass = isOpen ? 'addClass' : 'removeClass';
}
$animate[toggleClass](openContainer, dropdownOpenClass).then(function() {
if (angular.isDefined(isOpen) && isOpen !== wasOpen) {
toggleInvoker($scope, { open: !!isOpen });
}
@@ -265,9 +349,9 @@ angular.module('ui.bootstrap.dropdown', ['ui.bootstrap.position'])
}
scope.focusToggleElement();
uibDropdownService.open(scope, $element);
uibDropdownService.open(scope, $element, appendTo);
} else {
uibDropdownService.close(scope, $element);
uibDropdownService.close(scope, $element, appendTo);
if (self.dropdownMenuTemplateUrl) {
if (templateScope) {
templateScope.$destroy();
@@ -340,7 +424,7 @@ angular.module('ui.bootstrap.dropdown', ['ui.bootstrap.position'])
}
};
element.bind('click', toggleDropdown);
element.on('click', toggleDropdown);
// WAI-ARIA
element.attr({ 'aria-haspopup': true, 'aria-expanded': false });
@@ -349,7 +433,7 @@ angular.module('ui.bootstrap.dropdown', ['ui.bootstrap.position'])
});
scope.$on('$destroy', function() {
element.unbind('click', toggleDropdown);
element.off('click', toggleDropdown);
});
}
};
+1
View File
@@ -1,3 +1,4 @@
require('../multiMap');
require('../position/index-nocss.js');
require('./dropdown');
+266 -51
View File
@@ -99,11 +99,11 @@ describe('uib-dropdown', function() {
expect(elm1).not.toHaveClass(dropdownConfig.openClass);
expect(elm2).not.toHaveClass(dropdownConfig.openClass);
clickDropdownToggle( elm1 );
clickDropdownToggle(elm1);
expect(elm1).toHaveClass(dropdownConfig.openClass);
expect(elm2).not.toHaveClass(dropdownConfig.openClass);
clickDropdownToggle( elm2 );
clickDropdownToggle(elm2);
expect(elm1).not.toHaveClass(dropdownConfig.openClass);
expect(elm2).toHaveClass(dropdownConfig.openClass);
});
@@ -215,43 +215,198 @@ describe('uib-dropdown', function() {
});
describe('using dropdown-append-to-body', function() {
function dropdown() {
return $compile('<li uib-dropdown dropdown-append-to-body><a href uib-dropdown-toggle></a><ul uib-dropdown-menu id="dropdown-menu"><li><a href>Hello On Body</a></li></ul></li>')($rootScope);
}
describe('with no value', function() {
function dropdown() {
return $compile('<li uib-dropdown dropdown-append-to-body><a href uib-dropdown-toggle></a><ul uib-dropdown-menu id="dropdown-menu"><li><a href>Hello On Body</a></li></ul></li>')($rootScope);
}
beforeEach(function() {
element = dropdown();
$document.find('body').append(element);
beforeEach(function() {
element = dropdown();
$document.find('body').append(element);
});
afterEach(function() {
element.remove();
});
it('does not add the menu to the body', function() {
expect($document.find('#dropdown-menu').parent()[0]).not.toBe($document.find('body')[0]);
});
describe('when toggled open', function() {
var toggle;
beforeEach(function() {
toggle = element.find('[uib-dropdown-toggle]');
toggle.trigger('click');
});
it('adds the menu to the body', function() {
expect($document.find('#dropdown-menu').parent()[0]).toBe($document.find('body')[0]);
});
describe('when toggled closed', function() {
beforeEach(function() {
toggle.trigger('click');
});
it('removes the menu from body', function() {
expect($document.find('#dropdown-menu').parent()[0]).not.toBe($document.find('body')[0]);
});
});
describe('when closed by clicking on menu', function() {
var menu;
beforeEach(function() {
menu = $document.find('#dropdown-menu a');
menu.focus();
menu.trigger('click');
});
it('focuses the dropdown element on close', function() {
expect(document.activeElement).toBe(toggle[0]);
});
it('removes the menu from body', function() {
expect($document.find('#dropdown-menu').parent()[0]).not.toBe($document.find('body')[0]);
});
});
describe('when the dropdown is removed', function() {
beforeEach(function() {
element.remove();
$rootScope.$digest();
});
it('removes the menu from body', function() {
expect($document.find('#dropdown-menu').parent()[0]).not.toBe($document.find('body')[0]);
});
});
});
});
afterEach(function() {
element.remove();
});
describe('with a value', function() {
function dropdown() {
return $compile('<li uib-dropdown dropdown-append-to-body="appendToBody"><a href uib-dropdown-toggle></a><ul uib-dropdown-menu id="dropdown-menu"><li><a href>Hello On Body</a></li></ul></li>')($rootScope);
}
describe('that is not false', function() {
beforeEach(function() {
$rootScope.appendToBody = 'sure';
it('adds the menu to the body', function() {
expect($document.find('#dropdown-menu').parent()[0]).toBe($document.find('body')[0]);
});
element = dropdown();
$document.find('body').append(element);
});
it('focuses the dropdown element on close', function() {
var toggle = element.find('[uib-dropdown-toggle]');
var menu = $document.find('#dropdown-menu a');
toggle.trigger('click');
menu.focus();
afterEach(function() {
element.remove();
});
it('does not add the menu to the body', function() {
expect($document.find('#dropdown-menu').parent()[0]).not.toBe($document.find('body')[0]);
});
menu.trigger('click');
describe('when toggled open', function() {
var toggle;
beforeEach(function() {
toggle = element.find('[uib-dropdown-toggle]');
toggle.trigger('click');
});
it('adds the menu to the body', function() {
expect($document.find('#dropdown-menu').parent()[0]).toBe($document.find('body')[0]);
});
expect(document.activeElement).toBe(toggle[0]);
});
describe('when toggled closed', function() {
beforeEach(function() {
toggle.trigger('click');
});
it('removes the menu from body', function() {
expect($document.find('#dropdown-menu').parent()[0]).not.toBe($document.find('body')[0]);
});
});
it('removes the menu when the dropdown is removed', function() {
element.remove();
$rootScope.$digest();
expect($document.find('#dropdown-menu').length).toEqual(0);
describe('when closed by clicking on menu', function() {
var menu;
beforeEach(function() {
menu = $document.find('#dropdown-menu a');
menu.focus();
menu.trigger('click');
});
it('focuses the dropdown element on close', function() {
expect(document.activeElement).toBe(toggle[0]);
});
it('removes the menu from body', function() {
expect($document.find('#dropdown-menu').parent()[0]).not.toBe($document.find('body')[0]);
});
});
describe('when the dropdown is removed', function() {
beforeEach(function() {
element.remove();
$rootScope.$digest();
});
it('removes the menu from body', function() {
expect($document.find('#dropdown-menu').parent()[0]).not.toBe($document.find('body')[0]);
});
});
});
});
describe('that is false', function() {
beforeEach(function() {
$rootScope.appendToBody = false;
element = dropdown();
$document.find('body').append(element);
});
afterEach(function() {
element.remove();
});
it('does not add the menu to the body', function() {
expect($document.find('#dropdown-menu').parent()[0]).not.toBe($document.find('body')[0]);
});
describe('when toggled open', function() {
var toggle;
beforeEach(function() {
toggle = element.find('[uib-dropdown-toggle]');
toggle.trigger('click');
});
it('does not add the menu to the body', function() {
expect($document.find('#dropdown-menu').parent()[0]).not.toBe($document.find('body')[0]);
});
describe('when toggled closed', function() {
beforeEach(function() {
toggle.trigger('click');
});
it('does not remove the menu', function() {
expect($document.find('#dropdown-menu').length).not.toEqual(0);
});
});
describe('when closed by clicking on menu', function() {
var menu;
beforeEach(function() {
menu = $document.find('#dropdown-menu a');
menu.focus();
menu.trigger('click');
});
it('focuses the dropdown element on close', function() {
expect(document.activeElement).toBe(toggle[0]);
});
it('does not removes the menu from body', function() {
expect($document.find('#dropdown-menu').parent()[0]).not.toBe($document.find('body')[0]);
});
});
describe('when the dropdown is removed', function() {
beforeEach(function() {
element.remove();
$rootScope.$digest();
});
it('removes the menu from body', function() {
expect($document.find('#dropdown-menu').parent()[0]).not.toBe($document.find('body')[0]);
});
});
});
});
});
});
describe('using dropdown-append-to', function() {
var initialPage;
var initialPage, container;
function dropdown() {
return $compile('<li uib-dropdown dropdown-append-to="appendTo"><a href uib-dropdown-toggle></a><ul class="dropdown-menu" uib-dropdown-menu id="dropdown-menu"><li><a href>Hello On Container</a></li></ul></li>')($rootScope);
@@ -260,7 +415,7 @@ describe('uib-dropdown', function() {
beforeEach(function() {
$document.find('body').append(angular.element('<div id="dropdown-container"></div>'));
$rootScope.appendTo = $document.find('#dropdown-container');
$rootScope.appendTo = container = $document.find('#dropdown-container');
element = dropdown();
$document.find('body').append(element);
@@ -271,35 +426,95 @@ describe('uib-dropdown', function() {
$document.find('#dropdown-container').remove();
});
it('appends to container', function() {
expect($document.find('#dropdown-menu').parent()[0].id).toBe('dropdown-container');
it('does not add the menu to the container', function() {
expect($document.find('#dropdown-menu').parent()[0]).not.toBe(container[0]);
});
it('does not add open class on container', function() {
expect(container).not.toHaveClass('uib-dropdown-open');
});
it('toggles open class on container', function() {
describe('when toggled open', function() {
var toggle;
beforeEach(function() {
toggle = element.find('[uib-dropdown-toggle]');
toggle.trigger('click');
});
it('adds the menu to the container', function() {
expect($document.find('#dropdown-menu').parent()[0]).toBe(container[0]);
});
it('adds open class on container', function() {
expect(container).toHaveClass('uib-dropdown-open');
});
describe('when toggled closed', function() {
beforeEach(function() {
toggle.trigger('click');
});
it('removes the menu from the container', function() {
expect($document.find('#dropdown-menu').parent()[0]).not.toBe($document.find('body')[0]);
});
it('removes open class from container', function() {
expect(container).not.toHaveClass('uib-dropdown-open');
});
});
describe('when closed by clicking on menu', function() {
var menu;
beforeEach(function() {
menu = $document.find('#dropdown-menu a');
menu.focus();
menu.trigger('click');
});
it('focuses the dropdown element on close', function() {
expect(document.activeElement).toBe(toggle[0]);
});
it('removes the menu from the container', function() {
expect($document.find('#dropdown-menu').parent()[0]).not.toBe($document.find('body')[0]);
});
it('removes open class from container', function() {
expect(container).not.toHaveClass('uib-dropdown-open');
});
});
describe('when the dropdown is removed', function() {
beforeEach(function() {
element.remove();
$rootScope.$digest();
});
it('removes the menu from the container', function() {
expect($document.find('#dropdown-menu').parent()[0]).not.toBe($document.find('body')[0]);
});
});
});
});
describe('using dropdown-append-to with two dropdowns', function() {
function dropdown() {
return $compile('<div><div class="dropdown1" uib-dropdown dropdown-append-to="appendTo" on-toggle="log(1, open)"><a href uib-dropdown-toggle></a><ul class="dropdown-menu" uib-dropdown-menu id="dropdown-menu"><li><a href>Hello On Container</a></li></ul></div><div class="dropdown2" uib-dropdown dropdown-append-to="appendTo" on-toggle="log(2, open)"><a href uib-dropdown-toggle></a><ul class="dropdown-menu" uib-dropdown-menu id="dropdown-menu"><li><a href>Hello On Container</a></li></ul></div></div>')($rootScope);
}
beforeEach(function() {
$document.find('body').append(angular.element('<div id="dropdown-container"></div>'));
$rootScope.appendTo = $document.find('#dropdown-container');
$rootScope.log = jasmine.createSpy('log');
element = dropdown();
$document.find('body').append(element);
});
afterEach(function() {
// Cleanup the extra elements we appended
$document.find('#dropdown-container').remove();
});
it('should keep the class when toggling from one dropdown to another with the same container', function() {
var container = $document.find('#dropdown-container');
expect(container).not.toHaveClass('uib-dropdown-open');
element.find('[uib-dropdown-toggle]').click();
element.find('.dropdown1 [uib-dropdown-toggle]').click();
expect(container).toHaveClass('uib-dropdown-open');
element.find('.dropdown2 [uib-dropdown-toggle]').click();
expect(container).toHaveClass('uib-dropdown-open');
element.find('[uib-dropdown-toggle]').click();
expect(container).not.toHaveClass('uib-dropdown-open');
});
it('focuses the dropdown element on close', function() {
var toggle = element.find('[uib-dropdown-toggle]');
var menu = $document.find('#dropdown-menu a');
toggle.trigger('click');
menu.focus();
menu.trigger('click');
expect(document.activeElement).toBe(toggle[0]);
});
it('removes the menu when the dropdown is removed', function() {
element.remove();
$rootScope.$digest();
expect($document.find('#dropdown-menu').length).toEqual(0);
});
});
+33 -14
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -1,3 +1,4 @@
require('../multiMap');
require('../position/index-nocss.js');
require('../stackedMap');
require('../../template/modal/window.html.js');
+142 -94
View File
@@ -1,59 +1,4 @@
angular.module('ui.bootstrap.modal', ['ui.bootstrap.stackedMap', 'ui.bootstrap.position'])
/**
* A helper, internal data structure that stores all references attached to key
*/
.factory('$$multiMap', function() {
return {
createNew: function() {
var map = {};
return {
entries: function() {
return Object.keys(map).map(function(key) {
return {
key: key,
value: map[key]
};
});
},
get: function(key) {
return map[key];
},
hasKey: function(key) {
return !!map[key];
},
keys: function() {
return Object.keys(map);
},
put: function(key, value) {
if (!map[key]) {
map[key] = [];
}
map[key].push(value);
},
remove: function(key, value) {
var values = map[key];
if (!values) {
return;
}
var idx = values.indexOf(value);
if (idx !== -1) {
values.splice(idx, 1);
}
if (!values.length) {
delete map[key];
}
}
};
}
};
})
angular.module('ui.bootstrap.modal', ['ui.bootstrap.multiMap', 'ui.bootstrap.stackedMap', 'ui.bootstrap.position'])
/**
* Pluggable resolve mechanism for the modal resolve resolution
* Supports UI Router's $resolve service
@@ -163,7 +108,7 @@ angular.module('ui.bootstrap.modal', ['ui.bootstrap.stackedMap', 'ui.bootstrap.p
// {@link Attribute#$observe} on it. For more details please see {@link TableColumnResize}.
scope.$isRendered = true;
// Deferred object that will be resolved when this modal is render.
// Deferred object that will be resolved when this modal is rendered.
var modalRenderDeferObj = $q.defer();
// Resolve render promise post-digest
scope.$$postDigest(function() {
@@ -196,7 +141,7 @@ angular.module('ui.bootstrap.modal', ['ui.bootstrap.stackedMap', 'ui.bootstrap.p
/**
* If something within the freshly-opened modal already has focus (perhaps via a
* directive that causes focus). then no need to try and focus anything.
* directive that causes focus) then there's no need to try to focus anything.
*/
if (!($document[0].activeElement && element[0].contains($document[0].activeElement))) {
var inputWithAutofocus = element[0].querySelector('[autofocus]');
@@ -254,12 +199,22 @@ angular.module('ui.bootstrap.modal', ['ui.bootstrap.stackedMap', 'ui.bootstrap.p
};
var topModalIndex = 0;
var previousTopOpenedModal = null;
var ARIA_HIDDEN_ATTRIBUTE_NAME = 'data-bootstrap-modal-aria-hidden-count';
//Modal focus behavior
var tabbableSelector = 'a[href], area[href], input:not([disabled]):not([tabindex=\'-1\']), ' +
'button:not([disabled]):not([tabindex=\'-1\']),select:not([disabled]):not([tabindex=\'-1\']), textarea:not([disabled]):not([tabindex=\'-1\']), ' +
'iframe, object, embed, *[tabindex]:not([tabindex=\'-1\']), *[contenteditable=true]';
var scrollbarPadding;
var SNAKE_CASE_REGEXP = /[A-Z]/g;
// TODO: extract into common dependency with tooltip
function snake_case(name) {
var separator = '-';
return name.replace(SNAKE_CASE_REGEXP, function(letter, pos) {
return (pos ? separator : '') + letter.toLowerCase();
});
}
function isVisible(element) {
return !!(element.offsetWidth ||
@@ -464,10 +419,6 @@ angular.module('ui.bootstrap.modal', ['ui.bootstrap.stackedMap', 'ui.bootstrap.p
var appendToElement = modal.appendTo,
currBackdropIndex = backdropIndex();
if (!appendToElement.length) {
throw new Error('appendTo element not found. Make sure that the element passed is in DOM.');
}
if (currBackdropIndex >= 0 && !backdropDomEl) {
backdropScope = $rootScope.$new(true);
backdropScope.modalOptions = modal;
@@ -496,6 +447,20 @@ angular.module('ui.bootstrap.modal', ['ui.bootstrap.stackedMap', 'ui.bootstrap.p
}
}
var content;
if (modal.component) {
content = document.createElement(snake_case(modal.component.name));
content = angular.element(content);
content.attr({
resolve: '$resolve',
'modal-instance': '$uibModalInstance',
close: '$close($value)',
dismiss: '$dismiss($value)'
});
} else {
content = modal.content;
}
// Set the top modal index based on the index of the previous top modal
topModalIndex = previousTopOpenedModal ? parseInt(previousTopOpenedModal.value.modalDomEl.attr('index'), 10) + 1 : 0;
var angularDomEl = angular.element('<div uib-modal-window="modal-window"></div>');
@@ -504,6 +469,8 @@ angular.module('ui.bootstrap.modal', ['ui.bootstrap.stackedMap', 'ui.bootstrap.p
'template-url': modal.windowTemplateUrl,
'window-top-class': modal.windowTopClass,
'role': 'dialog',
'aria-labelledby': modal.ariaLabelledBy,
'aria-describedby': modal.ariaDescribedBy,
'size': modal.size,
'index': topModalIndex,
'animate': 'animate',
@@ -511,7 +478,7 @@ angular.module('ui.bootstrap.modal', ['ui.bootstrap.stackedMap', 'ui.bootstrap.p
'tabindex': -1,
'uib-modal-animation-class': 'fade',
'modal-in-class': 'in'
}).html(modal.content);
}).append(content);
if (modal.windowClass) {
angularDomEl.addClass(modal.windowClass);
}
@@ -530,25 +497,74 @@ angular.module('ui.bootstrap.modal', ['ui.bootstrap.stackedMap', 'ui.bootstrap.p
openedWindows.top().value.modalDomEl = angularDomEl;
openedWindows.top().value.modalOpener = modalOpener;
applyAriaHidden(angularDomEl);
function applyAriaHidden(el) {
if (!el || el[0].tagName === 'BODY') {
return;
}
getSiblings(el).forEach(function(sibling) {
var elemIsAlreadyHidden = sibling.getAttribute('aria-hidden') === 'true',
ariaHiddenCount = parseInt(sibling.getAttribute(ARIA_HIDDEN_ATTRIBUTE_NAME), 10);
if (!ariaHiddenCount) {
ariaHiddenCount = elemIsAlreadyHidden ? 1 : 0;
}
sibling.setAttribute(ARIA_HIDDEN_ATTRIBUTE_NAME, ariaHiddenCount + 1);
sibling.setAttribute('aria-hidden', 'true');
});
return applyAriaHidden(el.parent());
function getSiblings(el) {
var children = el.parent() ? el.parent().children() : [];
return Array.prototype.filter.call(children, function(child) {
return child !== el[0];
});
}
}
};
function broadcastClosing(modalWindow, resultOrReason, closing) {
return !modalWindow.value.modalScope.$broadcast('modal.closing', resultOrReason, closing).defaultPrevented;
}
function unhideBackgroundElements() {
Array.prototype.forEach.call(
document.querySelectorAll('[' + ARIA_HIDDEN_ATTRIBUTE_NAME + ']'),
function(hiddenEl) {
var ariaHiddenCount = parseInt(hiddenEl.getAttribute(ARIA_HIDDEN_ATTRIBUTE_NAME), 10),
newHiddenCount = ariaHiddenCount - 1;
hiddenEl.setAttribute(ARIA_HIDDEN_ATTRIBUTE_NAME, newHiddenCount);
if (!newHiddenCount) {
hiddenEl.removeAttribute(ARIA_HIDDEN_ATTRIBUTE_NAME);
hiddenEl.removeAttribute('aria-hidden');
}
}
);
}
$modalStack.close = function(modalInstance, result) {
var modalWindow = openedWindows.get(modalInstance);
unhideBackgroundElements();
if (modalWindow && broadcastClosing(modalWindow, result, true)) {
modalWindow.value.modalScope.$$uibDestructionScheduled = true;
modalWindow.value.deferred.resolve(result);
removeModalWindow(modalInstance, modalWindow.value.modalOpener);
return true;
}
return !modalWindow;
};
$modalStack.dismiss = function(modalInstance, reason) {
var modalWindow = openedWindows.get(modalInstance);
unhideBackgroundElements();
if (modalWindow && broadcastClosing(modalWindow, reason, false)) {
modalWindow.value.modalScope.$$uibDestructionScheduled = true;
modalWindow.value.deferred.reject(reason);
@@ -679,13 +695,22 @@ angular.module('ui.bootstrap.modal', ['ui.bootstrap.stackedMap', 'ui.bootstrap.p
modalOptions.resolve = modalOptions.resolve || {};
modalOptions.appendTo = modalOptions.appendTo || $document.find('body').eq(0);
//verify options
if (!modalOptions.template && !modalOptions.templateUrl) {
throw new Error('One of template or templateUrl options is required.');
if (!modalOptions.appendTo.length) {
throw new Error('appendTo element not found. Make sure that the element passed is in DOM.');
}
var templateAndResolvePromise =
$q.all([getTemplatePromise(modalOptions), $uibResolve.resolve(modalOptions.resolve, {}, null, null)]);
//verify options
if (!modalOptions.component && !modalOptions.template && !modalOptions.templateUrl) {
throw new Error('One of component or template or templateUrl options is required.');
}
var templateAndResolvePromise;
if (modalOptions.component) {
templateAndResolvePromise = $q.when($uibResolve.resolve(modalOptions.resolve, {}, null, null));
} else {
templateAndResolvePromise =
$q.all([getTemplatePromise(modalOptions), $uibResolve.resolve(modalOptions.resolve, {}, null, null)]);
}
function resolveWithTemplate() {
return templateAndResolvePromise;
@@ -711,17 +736,34 @@ angular.module('ui.bootstrap.modal', ['ui.bootstrap.stackedMap', 'ui.bootstrap.p
}
});
var modal = {
scope: modalScope,
deferred: modalResultDeferred,
renderDeferred: modalRenderDeferred,
closedDeferred: modalClosedDeferred,
animation: modalOptions.animation,
backdrop: modalOptions.backdrop,
keyboard: modalOptions.keyboard,
backdropClass: modalOptions.backdropClass,
windowTopClass: modalOptions.windowTopClass,
windowClass: modalOptions.windowClass,
windowTemplateUrl: modalOptions.windowTemplateUrl,
ariaLabelledBy: modalOptions.ariaLabelledBy,
ariaDescribedBy: modalOptions.ariaDescribedBy,
size: modalOptions.size,
openedClass: modalOptions.openedClass,
appendTo: modalOptions.appendTo
};
var component = {};
var ctrlInstance, ctrlInstantiate, ctrlLocals = {};
//controllers
if (modalOptions.controller) {
ctrlLocals.$scope = modalScope;
ctrlLocals.$scope.$resolve = {};
ctrlLocals.$uibModalInstance = modalInstance;
angular.forEach(tplAndVars[1], function(value, key) {
ctrlLocals[key] = value;
ctrlLocals.$scope.$resolve[key] = value;
});
if (modalOptions.component) {
constructLocals(component, false, true, false);
component.name = modalOptions.component;
modal.component = component;
} else if (modalOptions.controller) {
constructLocals(ctrlLocals, true, false, true);
// the third param will make the controller instantiate later,private api
// @see https://github.com/angular/angular.js/blob/master/src/ng/controller.js#L126
@@ -742,25 +784,31 @@ angular.module('ui.bootstrap.modal', ['ui.bootstrap.stackedMap', 'ui.bootstrap.p
}
}
$modalStack.open(modalInstance, {
scope: modalScope,
deferred: modalResultDeferred,
renderDeferred: modalRenderDeferred,
closedDeferred: modalClosedDeferred,
content: tplAndVars[0],
animation: modalOptions.animation,
backdrop: modalOptions.backdrop,
keyboard: modalOptions.keyboard,
backdropClass: modalOptions.backdropClass,
windowTopClass: modalOptions.windowTopClass,
windowClass: modalOptions.windowClass,
windowTemplateUrl: modalOptions.windowTemplateUrl,
size: modalOptions.size,
openedClass: modalOptions.openedClass,
appendTo: modalOptions.appendTo
});
if (!modalOptions.component) {
modal.content = tplAndVars[0];
}
$modalStack.open(modalInstance, modal);
modalOpenedDeferred.resolve(true);
function constructLocals(obj, template, instanceOnScope, injectable) {
obj.$scope = modalScope;
obj.$scope.$resolve = {};
if (instanceOnScope) {
obj.$scope.$uibModalInstance = modalInstance;
} else {
obj.$uibModalInstance = modalInstance;
}
var resolves = template ? tplAndVars[1] : tplAndVars;
angular.forEach(resolves, function(value, key) {
if (injectable) {
obj[key] = value;
}
obj.$scope.$resolve[key] = value;
});
}
}, function resolveError(reason) {
modalOpenedDeferred.reject(reason);
modalResultDeferred.reject(reason);
+139 -13
View File
@@ -131,6 +131,16 @@ describe('$uibModal', function() {
elem.focus();
}
};
}).component('fooBar', {
bindings: {
resolve: '<',
modalInstance: '<',
close: '&',
dismiss: '&'
},
controller: angular.noop,
controllerAs: 'foobar',
template: '<div>Foo Bar</div>'
});
}));
@@ -151,6 +161,7 @@ describe('$uibModal', function() {
toBeResolvedWith: function(util, customEqualityTesters) {
return {
compare: function(promise, expected) {
var called = false;
promise.then(function(result) {
expect(result).toEqual(expected);
@@ -159,10 +170,18 @@ describe('$uibModal', function() {
} else {
result.message = 'Expected "' + angular.mock.dump(result) + '" to be resolved with "' + expected + '".';
}
}, function(result) {
fail('Expected "' + angular.mock.dump(result) + '" to be resolved with "' + expected + '".');
})['finally'](function() {
called = true;
});
$rootScope.$digest();
if (!called) {
fail('Expected "' + angular.mock.dump(result) + '" to be resolved with "' + expected + '".');
}
return {pass: true};
}
};
@@ -171,9 +190,10 @@ describe('$uibModal', function() {
return {
compare: function(promise, expected) {
var result = {};
var called = false;
promise.then(function() {
promise.then(function(result) {
fail('Expected "' + angular.mock.dump(result) + '" to be rejected with "' + expected + '".');
}, function(result) {
expect(result).toEqual(expected);
@@ -182,10 +202,16 @@ describe('$uibModal', function() {
} else {
result.message = 'Expected "' + angular.mock.dump(result) + '" to be rejected with "' + expected + '".';
}
})['finally'](function() {
called = true;
});
$rootScope.$digest();
if (!called) {
fail('Expected "' + angular.mock.dump(result) + '" to be rejected with "' + expected + '".');
}
return {pass: true};
}
};
@@ -270,6 +296,8 @@ describe('$uibModal', function() {
function open(modalOptions, noFlush, noDigest) {
var modal = $uibModal.open(modalOptions);
modal.opened['catch'](angular.noop);
modal.result['catch'](angular.noop);
if (!noDigest) {
$rootScope.$digest();
@@ -500,7 +528,7 @@ describe('$uibModal', function() {
var modal = open({template: '<div>Content<button>inside modal</button></div>'});
$rootScope.$digest();
expect(document.activeElement.tagName).toBe('DIV');
expect(document.activeElement.className.split(' ')).toContain('modal');
expect($document).toHaveModalsOpen(1);
triggerKeyDown($document, 27);
@@ -630,7 +658,7 @@ describe('$uibModal', function() {
it('should not focus on the element that has autofocus attribute when the modal is opened and something in the modal already has focus and the animations have finished', function() {
function openAndCloseModalWithAutofocusElement() {
var modal = open({template: '<div><input type="text" id="auto-focus-element" autofocus><input type="text" id="pre-focus-element" focus-me></div>'});
var modal = open({template: '<div><input type="text" id="pre-focus-element" focus-me><input type="text" id="auto-focus-element" autofocus></div>'});
$rootScope.$digest();
expect(angular.element('#auto-focus-element')).not.toHaveFocus();
expect(angular.element('#pre-focus-element')).toHaveFocus();
@@ -672,7 +700,7 @@ describe('$uibModal', function() {
$rootScope.$digest();
$animate.flush();
expect(document.activeElement.tagName).toBe('DIV');
expect(document.activeElement.className.split(' ')).toContain('modal');
close(modal, 'closed ok');
@@ -930,16 +958,89 @@ describe('$uibModal', function() {
});
});
describe('option by option', function () {
describe('template and templateUrl', function () {
it('should throw an error if none of template and templateUrl are provided', function() {
describe('option by option', function() {
describe('component', function() {
function getModalComponent($document) {
return $document.find('body > div.modal > div.modal-dialog > div.modal-content foo-bar');
}
it('should use as modal content', function() {
open({
component: 'fooBar'
});
var component = getModalComponent($document);
expect(component.html()).toBe('<div>Foo Bar</div>');
});
it('should bind expected values', function() {
var modal = open({
component: 'fooBar',
resolve: {
foo: function() {
return 'bar';
}
}
});
var component = getModalComponent($document);
var componentScope = component.isolateScope();
expect(componentScope.foobar.resolve.foo).toBe('bar');
expect(componentScope.foobar.modalInstance).toBe(modal);
expect(componentScope.foobar.close).toEqual(jasmine.any(Function));
expect(componentScope.foobar.dismiss).toEqual(jasmine.any(Function));
});
it('should close the modal', function() {
var modal = open({
component: 'fooBar',
resolve: {
foo: function() {
return 'bar';
}
}
});
var component = getModalComponent($document);
var componentScope = component.isolateScope();
componentScope.foobar.close({
$value: 'baz'
});
expect(modal.result).toBeResolvedWith('baz');
});
it('should dismiss the modal', function() {
var modal = open({
component: 'fooBar',
resolve: {
foo: function() {
return 'bar';
}
}
});
var component = getModalComponent($document);
var componentScope = component.isolateScope();
componentScope.foobar.dismiss({
$value: 'baz'
});
expect(modal.result).toBeRejectedWith('baz');
});
});
describe('template and templateUrl', function() {
it('should throw an error if none of component, template and templateUrl are provided', function() {
expect(function(){
var modal = open({});
}).toThrow(new Error('One of template or templateUrl options is required.'));
}).toThrow(new Error('One of component or template or templateUrl options is required.'));
});
it('should not fail if a templateUrl contains leading / trailing white spaces', function() {
$templateCache.put('whitespace.html', ' <div>Whitespaces</div> ');
open({templateUrl: 'whitespace.html'});
expect($document).toHaveModalOpenWithContent('Whitespaces', 'div');
@@ -1465,6 +1566,28 @@ describe('$uibModal', function() {
expect(body).not.toHaveClass('modal-open');
});
});
describe('ariaLabelledBy', function() {
it('should add the aria-labelledby property to the modal', function() {
open({
template: '<div><h3 id="modal-label">Modal Label</h3><p id="modal-description">Modal description</p></div>',
ariaLabelledBy: 'modal-label'
});
expect($document.find('.modal').attr('aria-labelledby')).toEqual('modal-label');
});
});
describe('ariaDescribedBy', function() {
it('should add the aria-describedby property to the modal', function() {
open({
template: '<div><h3 id="modal-label">Modal Label</h3><p id="modal-description">Modal description</p></div>',
ariaDescribedBy: 'modal-description'
});
expect($document.find('.modal').attr('aria-describedby')).toEqual('modal-description');
});
});
});
describe('modal window', function() {
@@ -1491,7 +1614,7 @@ describe('$uibModal', function() {
var windowEl = $compile('<div uib-modal-window template-url="window.html">content</div>')($rootScope);
$rootScope.$digest();
expect(windowEl.html()).toBe('<div ng-transclude=""><span class="ng-scope">content</span></div>');
expect(windowEl.html()).toBe('<div ng-transclude="">content</div>');
}));
});
@@ -1615,16 +1738,19 @@ describe('$uibModal', function() {
ds[x] = {index: i, deferred: $q.defer(), reject: reject};
var scope = $rootScope.$new();
var failed = false;
scope.index = i;
open({
template: '<div>' + i + '</div>',
scope: scope,
resolve: {
x: function() { return ds[x].deferred.promise; }
x: function() { return ds[x].deferred.promise['catch'](function () {
failed = true;
}); }
}
}, true).opened.then(function() {
expect($uibModalStack.getTop().value.modalScope.index).toEqual(i);
actual += i;
if (!failed) { actual += i; }
});
});
+1
View File
@@ -0,0 +1 @@
require('./multiMap.js');
+55
View File
@@ -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
View File
@@ -9,6 +9,7 @@ angular.module('ui.bootstrap.pagination', ['ui.bootstrap.paging', 'ui.bootstrap.
pageLabel = angular.isDefined($attrs.pageLabel) ? function(idx) { return $scope.$parent.$eval($attrs.pageLabel, {$page: idx}); } : angular.identity;
$scope.boundaryLinks = angular.isDefined($attrs.boundaryLinks) ? $scope.$parent.$eval($attrs.boundaryLinks) : uibPaginationConfig.boundaryLinks;
$scope.directionLinks = angular.isDefined($attrs.directionLinks) ? $scope.$parent.$eval($attrs.directionLinks) : uibPaginationConfig.directionLinks;
$attrs.$set('role', 'menu');
uibPaging.create(this, $scope, $attrs);
+9
View File
@@ -54,6 +54,15 @@ describe('pagination directive', function() {
expect(element.hasClass('pagination')).toBe(true);
});
it('has accessibility attributes', function() {
expect(element.attr('role')).toEqual('menu');
var li = element.find('li');
for (var i = 0; i < li.length; i++) {
expect(li.eq(i).attr('role')).toEqual('menuitem');
}
});
it('exposes the controller to the template', function() {
$templateCache.put('uib/template/pagination/pagination.html', '<div>{{pagination.randomText}}</div>');
var scope = $rootScope.$new();
+3
View File
@@ -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`)_ -
+1 -1
View File
@@ -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,
+6
View File
@@ -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);
});
+35 -9
View File
@@ -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;
+2
View File
@@ -118,3 +118,5 @@ For Safari 7+ support, if you want to use the **focus** `tooltip-trigger`, you n
Click Me
</a>
```
For Safari (potentially all versions up to 9), there is an issue with the hover CSS selector when using multiple elements grouped close to each other that are using the tooltip - it is possible for multiple elements to gain the hover state when mousing between the elements quickly and exiting the container at the right time. See [issue #5445](https://github.com/angular-ui/bootstrap/issues/5445) for more details.
+6 -5
View File
@@ -27,7 +27,7 @@ describe('tooltip', function() {
}));
afterEach(function() {
$document.off('keypress');
$document.off('keyup');
});
function trigger(element, evt) {
@@ -295,10 +295,9 @@ describe('tooltip', function() {
trigger(elm, 'mouseenter');
expect(tooltipScope.isOpen).toBe(false);
$timeout.flush(500);
expect(tooltipScope.isOpen).toBe(false);
elmScope.disabled = true;
elmScope.$digest();
$timeout.flush(500);
expect(tooltipScope.isOpen).toBe(false);
});
@@ -343,22 +342,24 @@ describe('tooltip', function() {
expect(tooltipScope.isOpen).toBe(true);
expect(tooltipScope2.isOpen).toBe(true);
var evt = $.Event('keypress');
var evt = $.Event('keyup');
evt.which = 27;
$document.trigger(evt);
tooltipScope.$digest();
tooltipScope2.$digest();
$timeout.flush();
expect(tooltipScope.isOpen).toBe(true);
expect(tooltipScope2.isOpen).toBe(false);
var evt2 = $.Event('keypress');
var evt2 = $.Event('keyup');
evt2.which = 27;
$document.trigger(evt2);
tooltipScope.$digest();
tooltipScope2.$digest();
$timeout.flush(500);
expect(tooltipScope.isOpen).toBe(false);
expect(tooltipScope2.isOpen).toBe(false);
+25 -14
View File
@@ -71,10 +71,10 @@ angular.module('ui.bootstrap.tooltip', ['ui.bootstrap.position', 'ui.bootstrap.s
*/
this.$get = ['$window', '$compile', '$timeout', '$document', '$uibPosition', '$interpolate', '$rootScope', '$parse', '$$stackedMap', function($window, $compile, $timeout, $document, $position, $interpolate, $rootScope, $parse, $$stackedMap) {
var openedTooltips = $$stackedMap.createNew();
$document.on('keypress', keypressListener);
$document.on('keyup', keypressListener);
$rootScope.$on('$destroy', function() {
$document.off('keypress', keypressListener);
$document.off('keyup', keypressListener);
});
function keypressListener(e) {
@@ -82,7 +82,6 @@ angular.module('ui.bootstrap.tooltip', ['ui.bootstrap.position', 'ui.bootstrap.s
var last = openedTooltips.top();
if (last) {
last.value.close();
openedTooltips.removeTop();
last = null;
}
}
@@ -145,6 +144,7 @@ angular.module('ui.bootstrap.tooltip', ['ui.bootstrap.position', 'ui.bootstrap.s
var showTimeout;
var hideTimeout;
var positionTimeout;
var adjustmentTimeout;
var appendToBody = angular.isDefined(options.appendToBody) ? options.appendToBody : false;
var triggers = getTriggers(undefined);
var hasEnableExp = angular.isDefined(attrs[prefix + 'Enable']);
@@ -177,12 +177,13 @@ angular.module('ui.bootstrap.tooltip', ['ui.bootstrap.position', 'ui.bootstrap.s
tooltip.addClass(options.placementClassPrefix + ttPosition.placement);
}
$timeout(function() {
adjustmentTimeout = $timeout(function() {
var currentHeight = angular.isDefined(tooltip.offsetHeight) ? tooltip.offsetHeight : tooltip.prop('offsetHeight');
var adjustment = $position.adjustTop(placementClasses, elementPos, initialHeight, currentHeight);
if (adjustment) {
tooltip.css(adjustment);
}
adjustmentTimeout = null;
}, 0, false);
// first time through tt element will have the
@@ -207,9 +208,6 @@ angular.module('ui.bootstrap.tooltip', ['ui.bootstrap.position', 'ui.bootstrap.s
// By default, the tooltip is not open.
// TODO add ability to start tooltip opened
ttScope.isOpen = false;
openedTooltips.add(ttScope, {
close: hide
});
function toggleTooltipBind() {
if (!ttScope.isOpen) {
@@ -336,6 +334,10 @@ angular.module('ui.bootstrap.tooltip', ['ui.bootstrap.position', 'ui.bootstrap.s
}
});
openedTooltips.add(ttScope, {
close: hide
});
prepObservers();
}
@@ -346,8 +348,15 @@ angular.module('ui.bootstrap.tooltip', ['ui.bootstrap.position', 'ui.bootstrap.s
if (tooltip) {
tooltip.remove();
tooltip = null;
if (adjustmentTimeout) {
$timeout.cancel(adjustmentTimeout);
}
}
openedTooltips.remove(ttScope);
if (tooltipLinkedScope) {
tooltipLinkedScope.$destroy();
tooltipLinkedScope = null;
@@ -487,6 +496,13 @@ angular.module('ui.bootstrap.tooltip', ['ui.bootstrap.position', 'ui.bootstrap.s
}
}
// KeyboardEvent handler to hide the tooltip on Escape key press
function hideOnEscapeKey(e) {
if (e.which === 27) {
hideTooltipBind();
}
}
var unregisterTriggers = function() {
triggers.show.forEach(function(trigger) {
if (trigger === 'outsideClick') {
@@ -495,6 +511,7 @@ angular.module('ui.bootstrap.tooltip', ['ui.bootstrap.position', 'ui.bootstrap.s
element.off(trigger, showTooltipBind);
element.off(trigger, toggleTooltipBind);
}
element.off('keypress', hideOnEscapeKey);
});
triggers.hide.forEach(function(trigger) {
if (trigger === 'outsideClick') {
@@ -534,12 +551,7 @@ angular.module('ui.bootstrap.tooltip', ['ui.bootstrap.position', 'ui.bootstrap.s
element.on(trigger, showTooltipBind);
element.on(triggers.hide[idx], hideTooltipBind);
}
element.on('keypress', function(e) {
if (e.which === 27) {
hideTooltipBind();
}
});
element.on('keypress', hideOnEscapeKey);
});
}
}
@@ -563,7 +575,6 @@ angular.module('ui.bootstrap.tooltip', ['ui.bootstrap.position', 'ui.bootstrap.s
scope.$on('$destroy', function onDestroyTooltip() {
unregisterTriggers();
removeTooltip();
openedTooltips.remove(ttScope);
ttScope = null;
});
};
+26 -8
View File
@@ -94,7 +94,7 @@ angular.module('ui.bootstrap.typeahead', ['ui.bootstrap.debounce', 'ui.bootstrap
var invokeModelSetter = $parse(attrs.ngModel + '($$$p)');
var $setModelValue = function(scope, newValue) {
if (angular.isFunction(parsedModel(originalScope)) &&
ngModelOptions && ngModelOptions.$options && ngModelOptions.$options.getterSetter) {
ngModelOptions.getOption('getterSetter')) {
return invokeModelSetter(scope, {$$$p: newValue});
}
@@ -430,7 +430,7 @@ angular.module('ui.bootstrap.typeahead', ['ui.bootstrap.debounce', 'ui.bootstrap
}
});
element.bind('focus', function (evt) {
element.on('focus', function (evt) {
hasFocus = true;
if (minLength === 0 && !modelCtrl.$viewValue) {
$timeout(function() {
@@ -439,7 +439,7 @@ angular.module('ui.bootstrap.typeahead', ['ui.bootstrap.debounce', 'ui.bootstrap
}
});
element.bind('blur', function(evt) {
element.on('blur', function(evt) {
if (isSelectOnBlur && scope.matches.length && scope.activeIdx !== -1 && !selected) {
selected = true;
scope.$apply(function() {
@@ -507,11 +507,11 @@ angular.module('ui.bootstrap.typeahead', ['ui.bootstrap.debounce', 'ui.bootstrap
element.after($popup);
}
this.init = function(_modelCtrl, _ngModelOptions) {
this.init = function(_modelCtrl) {
modelCtrl = _modelCtrl;
ngModelOptions = _ngModelOptions;
ngModelOptions = extractOptions(modelCtrl);
scope.debounceUpdate = modelCtrl.$options && $parse(modelCtrl.$options.debounce)(originalScope);
scope.debounceUpdate = $parse(ngModelOptions.getOption('debounce'))(originalScope);
//plug into $parsers pipeline to open a typeahead on view changes initiated from DOM
//$parsers kick-in on all the changes coming from the view as well as manually triggered by $setViewValue
@@ -571,14 +571,32 @@ angular.module('ui.bootstrap.typeahead', ['ui.bootstrap.debounce', 'ui.bootstrap
return candidateViewValue !== emptyViewValue ? candidateViewValue : modelValue;
});
};
function extractOptions(ngModelCtrl) {
var ngModelOptions;
if (angular.version.minor < 6) { // in angular < 1.6 $options could be missing
// guarantee a value
ngModelOptions = ngModelCtrl.$options || {};
// mimic 1.6+ api
ngModelOptions.getOption = function (key) {
return ngModelOptions[key];
};
} else { // in angular >=1.6 $options is always present
ngModelOptions = ngModelCtrl.$options;
}
return ngModelOptions;
}
}])
.directive('uibTypeahead', function() {
return {
controller: 'UibTypeaheadController',
require: ['ngModel', '^?ngModelOptions', 'uibTypeahead'],
require: ['ngModel', 'uibTypeahead'],
link: function(originalScope, element, attrs, ctrls) {
ctrls[2].init(ctrls[0], ctrls[1]);
ctrls[1].init(ctrls[0]);
}
};
})
+3 -3
View File
@@ -1,9 +1,9 @@
<table role="grid" aria-labelledby="{{::uniqueId}}-title" aria-activedescendant="{{activeDateId}}">
<thead>
<tr>
<th><button type="button" class="btn btn-default btn-sm pull-left uib-left" ng-click="move(-1)" tabindex="-1"><i class="glyphicon glyphicon-chevron-left"></i></button></th>
<th><button type="button" class="btn btn-default btn-sm pull-left uib-left" ng-click="move(-1)" tabindex="-1"><i aria-hidden="true" class="glyphicon glyphicon-chevron-left"></i><span class="sr-only">previous</span></button></th>
<th colspan="{{::5 + showWeeks}}"><button id="{{::uniqueId}}-title" role="heading" aria-live="assertive" aria-atomic="true" type="button" class="btn btn-default btn-sm uib-title" ng-click="toggleMode()" ng-disabled="datepickerMode === maxMode" tabindex="-1"><strong>{{title}}</strong></button></th>
<th><button type="button" class="btn btn-default btn-sm pull-right uib-right" ng-click="move(1)" tabindex="-1"><i class="glyphicon glyphicon-chevron-right"></i></button></th>
<th><button type="button" class="btn btn-default btn-sm pull-right uib-right" ng-click="move(1)" tabindex="-1"><i aria-hidden="true" class="glyphicon glyphicon-chevron-right"></i><span class="sr-only">next</span></button></th>
</tr>
<tr>
<th ng-if="showWeeks" class="text-center"></th>
@@ -11,7 +11,7 @@
</tr>
</thead>
<tbody>
<tr class="uib-weeks" ng-repeat="row in rows track by $index">
<tr class="uib-weeks" ng-repeat="row in rows track by $index" role="row">
<td ng-if="showWeeks" class="text-center h6"><em>{{ weekNumbers[$index] }}</em></td>
<td ng-repeat="dt in row" class="uib-day text-center" role="gridcell"
id="{{::dt.uid}}"
+3 -3
View File
@@ -1,13 +1,13 @@
<table role="grid" aria-labelledby="{{::uniqueId}}-title" aria-activedescendant="{{activeDateId}}">
<thead>
<tr>
<th><button type="button" class="btn btn-default btn-sm pull-left uib-left" ng-click="move(-1)" tabindex="-1"><i class="glyphicon glyphicon-chevron-left"></i></button></th>
<th><button type="button" class="btn btn-default btn-sm pull-left uib-left" ng-click="move(-1)" tabindex="-1"><i aria-hidden="true" class="glyphicon glyphicon-chevron-left"></i><span class="sr-only">previous</span></button></th>
<th colspan="{{::yearHeaderColspan}}"><button id="{{::uniqueId}}-title" role="heading" aria-live="assertive" aria-atomic="true" type="button" class="btn btn-default btn-sm uib-title" ng-click="toggleMode()" ng-disabled="datepickerMode === maxMode" tabindex="-1"><strong>{{title}}</strong></button></th>
<th><button type="button" class="btn btn-default btn-sm pull-right uib-right" ng-click="move(1)" tabindex="-1"><i class="glyphicon glyphicon-chevron-right"></i></button></th>
<th><button type="button" class="btn btn-default btn-sm pull-right uib-right" ng-click="move(1)" tabindex="-1"><i aria-hidden="true" class="glyphicon glyphicon-chevron-right"></i><span class="sr-only">next</span></i></button></th>
</tr>
</thead>
<tbody>
<tr class="uib-months" ng-repeat="row in rows track by $index">
<tr class="uib-months" ng-repeat="row in rows track by $index" role="row">
<td ng-repeat="dt in row" class="uib-month text-center" role="gridcell"
id="{{::dt.uid}}"
ng-class="::dt.customClass">
+3 -3
View File
@@ -1,13 +1,13 @@
<table role="grid" aria-labelledby="{{::uniqueId}}-title" aria-activedescendant="{{activeDateId}}">
<thead>
<tr>
<th><button type="button" class="btn btn-default btn-sm pull-left uib-left" ng-click="move(-1)" tabindex="-1"><i class="glyphicon glyphicon-chevron-left"></i></button></th>
<th><button type="button" class="btn btn-default btn-sm pull-left uib-left" ng-click="move(-1)" tabindex="-1"><i aria-hidden="true" class="glyphicon glyphicon-chevron-left"></i><span class="sr-only">previous</span></button></th>
<th colspan="{{::columns - 2}}"><button id="{{::uniqueId}}-title" role="heading" aria-live="assertive" aria-atomic="true" type="button" class="btn btn-default btn-sm uib-title" ng-click="toggleMode()" ng-disabled="datepickerMode === maxMode" tabindex="-1"><strong>{{title}}</strong></button></th>
<th><button type="button" class="btn btn-default btn-sm pull-right uib-right" ng-click="move(1)" tabindex="-1"><i class="glyphicon glyphicon-chevron-right"></i></button></th>
<th><button type="button" class="btn btn-default btn-sm pull-right uib-right" ng-click="move(1)" tabindex="-1"><i aria-hidden="true" class="glyphicon glyphicon-chevron-right"></i><span class="sr-only">next</span></button></th>
</tr>
</thead>
<tbody>
<tr class="uib-years" ng-repeat="row in rows track by $index">
<tr class="uib-years" ng-repeat="row in rows track by $index" role="row">
<td ng-repeat="dt in row" class="uib-year text-center" role="gridcell"
id="{{::dt.uid}}"
ng-class="::dt.customClass">
+1 -1
View File
@@ -1,4 +1,4 @@
<ul class="uib-datepicker-popup dropdown-menu uib-position-measure" dropdown-nested ng-if="isOpen" ng-keydown="keydown($event)" ng-click="$event.stopPropagation()">
<ul role="presentation" class="uib-datepicker-popup dropdown-menu uib-position-measure" dropdown-nested ng-if="isOpen" ng-keydown="keydown($event)" ng-click="$event.stopPropagation()">
<li ng-transclude></li>
<li ng-if="showButtonBar" class="uib-button-bar">
<span class="btn-group pull-left">
+5 -5
View File
@@ -1,5 +1,5 @@
<li ng-if="::boundaryLinks" ng-class="{disabled: noPrevious()||ngDisabled}" class="pagination-first"><a href ng-click="selectPage(1, $event)" ng-disabled="noPrevious()||ngDisabled" uib-tabindex-toggle>{{::getText('first')}}</a></li>
<li ng-if="::directionLinks" ng-class="{disabled: noPrevious()||ngDisabled}" class="pagination-prev"><a href ng-click="selectPage(page - 1, $event)" ng-disabled="noPrevious()||ngDisabled" uib-tabindex-toggle>{{::getText('previous')}}</a></li>
<li ng-repeat="page in pages track by $index" ng-class="{active: page.active,disabled: ngDisabled&&!page.active}" class="pagination-page"><a href ng-click="selectPage(page.number, $event)" ng-disabled="ngDisabled&&!page.active" uib-tabindex-toggle>{{page.text}}</a></li>
<li ng-if="::directionLinks" ng-class="{disabled: noNext()||ngDisabled}" class="pagination-next"><a href ng-click="selectPage(page + 1, $event)" ng-disabled="noNext()||ngDisabled" uib-tabindex-toggle>{{::getText('next')}}</a></li>
<li ng-if="::boundaryLinks" ng-class="{disabled: noNext()||ngDisabled}" class="pagination-last"><a href ng-click="selectPage(totalPages, $event)" ng-disabled="noNext()||ngDisabled" uib-tabindex-toggle>{{::getText('last')}}</a></li>
<li role="menuitem" ng-if="::boundaryLinks" ng-class="{disabled: noPrevious()||ngDisabled}" class="pagination-first"><a href ng-click="selectPage(1, $event)" ng-disabled="noPrevious()||ngDisabled" uib-tabindex-toggle>{{::getText('first')}}</a></li>
<li role="menuitem" ng-if="::directionLinks" ng-class="{disabled: noPrevious()||ngDisabled}" class="pagination-prev"><a href ng-click="selectPage(page - 1, $event)" ng-disabled="noPrevious()||ngDisabled" uib-tabindex-toggle>{{::getText('previous')}}</a></li>
<li role="menuitem" ng-repeat="page in pages track by $index" ng-class="{active: page.active,disabled: ngDisabled&&!page.active}" class="pagination-page"><a href ng-click="selectPage(page.number, $event)" ng-disabled="ngDisabled&&!page.active" uib-tabindex-toggle>{{page.text}}</a></li>
<li role="menuitem" ng-if="::directionLinks" ng-class="{disabled: noNext()||ngDisabled}" class="pagination-next"><a href ng-click="selectPage(page + 1, $event)" ng-disabled="noNext()||ngDisabled" uib-tabindex-toggle>{{::getText('next')}}</a></li>
<li role="menuitem" ng-if="::boundaryLinks" ng-class="{disabled: noNext()||ngDisabled}" class="pagination-last"><a href ng-click="selectPage(totalPages, $event)" ng-disabled="noNext()||ngDisabled" uib-tabindex-toggle>{{::getText('last')}}</a></li>