rails-angular-strap 2.1.6 → 2.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +2 -10
- data/lib/rails-angular-strap/version.rb +1 -1
- data/vendor/assets/javascripts/angular-strap.js +1 -1
- data/vendor/assets/javascripts/angular-strap.min.js +1 -1
- data/vendor/assets/javascripts/angular-strap/angular-strap.js +1405 -1104
- data/vendor/assets/javascripts/angular-strap/angular-strap.min.js +4 -5
- data/vendor/assets/javascripts/angular-strap/angular-strap.tpl.js +15 -15
- data/vendor/assets/javascripts/angular-strap/angular-strap.tpl.min.js +2 -2
- metadata +2 -192
- data/vendor/assets/javascripts/angular-strap-unstable.js +0 -2
- data/vendor/assets/javascripts/angular-strap-unstable.min.js +0 -2
- data/vendor/assets/javascripts/angular-strap/angular-strap.min.js.map +0 -1
- data/vendor/assets/javascripts/angular-strap/modules/affix.js +0 -249
- data/vendor/assets/javascripts/angular-strap/modules/affix.min.js +0 -9
- data/vendor/assets/javascripts/angular-strap/modules/affix.min.js.map +0 -1
- data/vendor/assets/javascripts/angular-strap/modules/alert.js +0 -120
- data/vendor/assets/javascripts/angular-strap/modules/alert.min.js +0 -9
- data/vendor/assets/javascripts/angular-strap/modules/alert.min.js.map +0 -1
- data/vendor/assets/javascripts/angular-strap/modules/alert.tpl.js +0 -14
- data/vendor/assets/javascripts/angular-strap/modules/alert.tpl.min.js +0 -8
- data/vendor/assets/javascripts/angular-strap/modules/aside.js +0 -96
- data/vendor/assets/javascripts/angular-strap/modules/aside.min.js +0 -9
- data/vendor/assets/javascripts/angular-strap/modules/aside.min.js.map +0 -1
- data/vendor/assets/javascripts/angular-strap/modules/aside.tpl.js +0 -14
- data/vendor/assets/javascripts/angular-strap/modules/aside.tpl.min.js +0 -8
- data/vendor/assets/javascripts/angular-strap/modules/button.js +0 -177
- data/vendor/assets/javascripts/angular-strap/modules/button.min.js +0 -9
- data/vendor/assets/javascripts/angular-strap/modules/button.min.js.map +0 -1
- data/vendor/assets/javascripts/angular-strap/modules/collapse.js +0 -273
- data/vendor/assets/javascripts/angular-strap/modules/collapse.min.js +0 -9
- data/vendor/assets/javascripts/angular-strap/modules/collapse.min.js.map +0 -1
- data/vendor/assets/javascripts/angular-strap/modules/date-formatter.js +0 -61
- data/vendor/assets/javascripts/angular-strap/modules/date-formatter.min.js +0 -9
- data/vendor/assets/javascripts/angular-strap/modules/date-formatter.min.js.map +0 -1
- data/vendor/assets/javascripts/angular-strap/modules/date-parser.js +0 -273
- data/vendor/assets/javascripts/angular-strap/modules/date-parser.min.js +0 -9
- data/vendor/assets/javascripts/angular-strap/modules/date-parser.min.js.map +0 -1
- data/vendor/assets/javascripts/angular-strap/modules/datepicker.js +0 -640
- data/vendor/assets/javascripts/angular-strap/modules/datepicker.min.js +0 -9
- data/vendor/assets/javascripts/angular-strap/modules/datepicker.min.js.map +0 -1
- data/vendor/assets/javascripts/angular-strap/modules/datepicker.tpl.js +0 -14
- data/vendor/assets/javascripts/angular-strap/modules/datepicker.tpl.min.js +0 -8
- data/vendor/assets/javascripts/angular-strap/modules/debounce.js +0 -62
- data/vendor/assets/javascripts/angular-strap/modules/debounce.min.js +0 -9
- data/vendor/assets/javascripts/angular-strap/modules/debounce.min.js.map +0 -1
- data/vendor/assets/javascripts/angular-strap/modules/dimensions.js +0 -156
- data/vendor/assets/javascripts/angular-strap/modules/dimensions.min.js +0 -9
- data/vendor/assets/javascripts/angular-strap/modules/dimensions.min.js.map +0 -1
- data/vendor/assets/javascripts/angular-strap/modules/dropdown.js +0 -149
- data/vendor/assets/javascripts/angular-strap/modules/dropdown.min.js +0 -9
- data/vendor/assets/javascripts/angular-strap/modules/dropdown.min.js.map +0 -1
- data/vendor/assets/javascripts/angular-strap/modules/dropdown.tpl.js +0 -14
- data/vendor/assets/javascripts/angular-strap/modules/dropdown.tpl.min.js +0 -8
- data/vendor/assets/javascripts/angular-strap/modules/modal.js +0 -349
- data/vendor/assets/javascripts/angular-strap/modules/modal.min.js +0 -9
- data/vendor/assets/javascripts/angular-strap/modules/modal.min.js.map +0 -1
- data/vendor/assets/javascripts/angular-strap/modules/modal.tpl.js +0 -14
- data/vendor/assets/javascripts/angular-strap/modules/modal.tpl.min.js +0 -8
- data/vendor/assets/javascripts/angular-strap/modules/navbar.js +0 -72
- data/vendor/assets/javascripts/angular-strap/modules/navbar.min.js +0 -9
- data/vendor/assets/javascripts/angular-strap/modules/navbar.min.js.map +0 -1
- data/vendor/assets/javascripts/angular-strap/modules/parse-options.js +0 -76
- data/vendor/assets/javascripts/angular-strap/modules/parse-options.min.js +0 -9
- data/vendor/assets/javascripts/angular-strap/modules/parse-options.min.js.map +0 -1
- data/vendor/assets/javascripts/angular-strap/modules/popover.js +0 -112
- data/vendor/assets/javascripts/angular-strap/modules/popover.min.js +0 -9
- data/vendor/assets/javascripts/angular-strap/modules/popover.min.js.map +0 -1
- data/vendor/assets/javascripts/angular-strap/modules/popover.tpl.js +0 -14
- data/vendor/assets/javascripts/angular-strap/modules/popover.tpl.min.js +0 -8
- data/vendor/assets/javascripts/angular-strap/modules/raf.js +0 -61
- data/vendor/assets/javascripts/angular-strap/modules/raf.min.js +0 -9
- data/vendor/assets/javascripts/angular-strap/modules/raf.min.js.map +0 -1
- data/vendor/assets/javascripts/angular-strap/modules/scrollspy.js +0 -261
- data/vendor/assets/javascripts/angular-strap/modules/scrollspy.min.js +0 -9
- data/vendor/assets/javascripts/angular-strap/modules/scrollspy.min.js.map +0 -1
- data/vendor/assets/javascripts/angular-strap/modules/select.js +0 -325
- data/vendor/assets/javascripts/angular-strap/modules/select.min.js +0 -9
- data/vendor/assets/javascripts/angular-strap/modules/select.min.js.map +0 -1
- data/vendor/assets/javascripts/angular-strap/modules/select.tpl.js +0 -14
- data/vendor/assets/javascripts/angular-strap/modules/select.tpl.min.js +0 -8
- data/vendor/assets/javascripts/angular-strap/modules/tab.js +0 -186
- data/vendor/assets/javascripts/angular-strap/modules/tab.min.js +0 -9
- data/vendor/assets/javascripts/angular-strap/modules/tab.min.js.map +0 -1
- data/vendor/assets/javascripts/angular-strap/modules/tab.tpl.js +0 -14
- data/vendor/assets/javascripts/angular-strap/modules/tab.tpl.min.js +0 -8
- data/vendor/assets/javascripts/angular-strap/modules/timepicker.js +0 -485
- data/vendor/assets/javascripts/angular-strap/modules/timepicker.min.js +0 -9
- data/vendor/assets/javascripts/angular-strap/modules/timepicker.min.js.map +0 -1
- data/vendor/assets/javascripts/angular-strap/modules/timepicker.tpl.js +0 -14
- data/vendor/assets/javascripts/angular-strap/modules/timepicker.tpl.min.js +0 -8
- data/vendor/assets/javascripts/angular-strap/modules/tooltip.js +0 -690
- data/vendor/assets/javascripts/angular-strap/modules/tooltip.min.js +0 -9
- data/vendor/assets/javascripts/angular-strap/modules/tooltip.min.js.map +0 -1
- data/vendor/assets/javascripts/angular-strap/modules/tooltip.tpl.js +0 -14
- data/vendor/assets/javascripts/angular-strap/modules/tooltip.tpl.min.js +0 -8
- data/vendor/assets/javascripts/angular-strap/modules/typeahead.js +0 -266
- data/vendor/assets/javascripts/angular-strap/modules/typeahead.min.js +0 -9
- data/vendor/assets/javascripts/angular-strap/modules/typeahead.min.js.map +0 -1
- data/vendor/assets/javascripts/angular-strap/modules/typeahead.tpl.js +0 -14
- data/vendor/assets/javascripts/angular-strap/modules/typeahead.tpl.min.js +0 -8
- data/vendor/assets/javascripts/angular-strap/unstable/angular-strap.js +0 -5040
- data/vendor/assets/javascripts/angular-strap/unstable/angular-strap.min.js +0 -11
- data/vendor/assets/javascripts/angular-strap/unstable/angular-strap.min.js.map +0 -1
- data/vendor/assets/javascripts/angular-strap/unstable/angular-strap.tpl.js +0 -89
- data/vendor/assets/javascripts/angular-strap/unstable/angular-strap.tpl.min.js +0 -8
- data/vendor/assets/javascripts/angular-strap/unstable/modules/affix.js +0 -249
- data/vendor/assets/javascripts/angular-strap/unstable/modules/affix.min.js +0 -9
- data/vendor/assets/javascripts/angular-strap/unstable/modules/affix.min.js.map +0 -1
- data/vendor/assets/javascripts/angular-strap/unstable/modules/alert.js +0 -120
- data/vendor/assets/javascripts/angular-strap/unstable/modules/alert.min.js +0 -9
- data/vendor/assets/javascripts/angular-strap/unstable/modules/alert.min.js.map +0 -1
- data/vendor/assets/javascripts/angular-strap/unstable/modules/alert.tpl.js +0 -14
- data/vendor/assets/javascripts/angular-strap/unstable/modules/alert.tpl.min.js +0 -8
- data/vendor/assets/javascripts/angular-strap/unstable/modules/aside.js +0 -96
- data/vendor/assets/javascripts/angular-strap/unstable/modules/aside.min.js +0 -9
- data/vendor/assets/javascripts/angular-strap/unstable/modules/aside.min.js.map +0 -1
- data/vendor/assets/javascripts/angular-strap/unstable/modules/aside.tpl.js +0 -14
- data/vendor/assets/javascripts/angular-strap/unstable/modules/aside.tpl.min.js +0 -8
- data/vendor/assets/javascripts/angular-strap/unstable/modules/button.js +0 -177
- data/vendor/assets/javascripts/angular-strap/unstable/modules/button.min.js +0 -9
- data/vendor/assets/javascripts/angular-strap/unstable/modules/button.min.js.map +0 -1
- data/vendor/assets/javascripts/angular-strap/unstable/modules/collapse.js +0 -273
- data/vendor/assets/javascripts/angular-strap/unstable/modules/collapse.min.js +0 -9
- data/vendor/assets/javascripts/angular-strap/unstable/modules/collapse.min.js.map +0 -1
- data/vendor/assets/javascripts/angular-strap/unstable/modules/date-formatter.js +0 -61
- data/vendor/assets/javascripts/angular-strap/unstable/modules/date-formatter.min.js +0 -9
- data/vendor/assets/javascripts/angular-strap/unstable/modules/date-formatter.min.js.map +0 -1
- data/vendor/assets/javascripts/angular-strap/unstable/modules/date-parser.js +0 -273
- data/vendor/assets/javascripts/angular-strap/unstable/modules/date-parser.min.js +0 -9
- data/vendor/assets/javascripts/angular-strap/unstable/modules/date-parser.min.js.map +0 -1
- data/vendor/assets/javascripts/angular-strap/unstable/modules/datepicker.js +0 -640
- data/vendor/assets/javascripts/angular-strap/unstable/modules/datepicker.min.js +0 -9
- data/vendor/assets/javascripts/angular-strap/unstable/modules/datepicker.min.js.map +0 -1
- data/vendor/assets/javascripts/angular-strap/unstable/modules/datepicker.tpl.js +0 -14
- data/vendor/assets/javascripts/angular-strap/unstable/modules/datepicker.tpl.min.js +0 -8
- data/vendor/assets/javascripts/angular-strap/unstable/modules/debounce.js +0 -62
- data/vendor/assets/javascripts/angular-strap/unstable/modules/debounce.min.js +0 -9
- data/vendor/assets/javascripts/angular-strap/unstable/modules/debounce.min.js.map +0 -1
- data/vendor/assets/javascripts/angular-strap/unstable/modules/dimensions.js +0 -156
- data/vendor/assets/javascripts/angular-strap/unstable/modules/dimensions.min.js +0 -9
- data/vendor/assets/javascripts/angular-strap/unstable/modules/dimensions.min.js.map +0 -1
- data/vendor/assets/javascripts/angular-strap/unstable/modules/dropdown.js +0 -149
- data/vendor/assets/javascripts/angular-strap/unstable/modules/dropdown.min.js +0 -9
- data/vendor/assets/javascripts/angular-strap/unstable/modules/dropdown.min.js.map +0 -1
- data/vendor/assets/javascripts/angular-strap/unstable/modules/dropdown.tpl.js +0 -14
- data/vendor/assets/javascripts/angular-strap/unstable/modules/dropdown.tpl.min.js +0 -8
- data/vendor/assets/javascripts/angular-strap/unstable/modules/modal.js +0 -357
- data/vendor/assets/javascripts/angular-strap/unstable/modules/modal.min.js +0 -9
- data/vendor/assets/javascripts/angular-strap/unstable/modules/modal.min.js.map +0 -1
- data/vendor/assets/javascripts/angular-strap/unstable/modules/modal.tpl.js +0 -14
- data/vendor/assets/javascripts/angular-strap/unstable/modules/modal.tpl.min.js +0 -8
- data/vendor/assets/javascripts/angular-strap/unstable/modules/navbar.js +0 -72
- data/vendor/assets/javascripts/angular-strap/unstable/modules/navbar.min.js +0 -9
- data/vendor/assets/javascripts/angular-strap/unstable/modules/navbar.min.js.map +0 -1
- data/vendor/assets/javascripts/angular-strap/unstable/modules/parse-options.js +0 -76
- data/vendor/assets/javascripts/angular-strap/unstable/modules/parse-options.min.js +0 -9
- data/vendor/assets/javascripts/angular-strap/unstable/modules/parse-options.min.js.map +0 -1
- data/vendor/assets/javascripts/angular-strap/unstable/modules/popover.js +0 -112
- data/vendor/assets/javascripts/angular-strap/unstable/modules/popover.min.js +0 -9
- data/vendor/assets/javascripts/angular-strap/unstable/modules/popover.min.js.map +0 -1
- data/vendor/assets/javascripts/angular-strap/unstable/modules/popover.tpl.js +0 -14
- data/vendor/assets/javascripts/angular-strap/unstable/modules/popover.tpl.min.js +0 -8
- data/vendor/assets/javascripts/angular-strap/unstable/modules/raf.js +0 -61
- data/vendor/assets/javascripts/angular-strap/unstable/modules/raf.min.js +0 -9
- data/vendor/assets/javascripts/angular-strap/unstable/modules/raf.min.js.map +0 -1
- data/vendor/assets/javascripts/angular-strap/unstable/modules/scrollspy.js +0 -261
- data/vendor/assets/javascripts/angular-strap/unstable/modules/scrollspy.min.js +0 -9
- data/vendor/assets/javascripts/angular-strap/unstable/modules/scrollspy.min.js.map +0 -1
- data/vendor/assets/javascripts/angular-strap/unstable/modules/select.js +0 -328
- data/vendor/assets/javascripts/angular-strap/unstable/modules/select.min.js +0 -9
- data/vendor/assets/javascripts/angular-strap/unstable/modules/select.min.js.map +0 -1
- data/vendor/assets/javascripts/angular-strap/unstable/modules/select.tpl.js +0 -14
- data/vendor/assets/javascripts/angular-strap/unstable/modules/select.tpl.min.js +0 -8
- data/vendor/assets/javascripts/angular-strap/unstable/modules/tab.js +0 -186
- data/vendor/assets/javascripts/angular-strap/unstable/modules/tab.min.js +0 -9
- data/vendor/assets/javascripts/angular-strap/unstable/modules/tab.min.js.map +0 -1
- data/vendor/assets/javascripts/angular-strap/unstable/modules/tab.tpl.js +0 -14
- data/vendor/assets/javascripts/angular-strap/unstable/modules/tab.tpl.min.js +0 -8
- data/vendor/assets/javascripts/angular-strap/unstable/modules/timepicker.js +0 -485
- data/vendor/assets/javascripts/angular-strap/unstable/modules/timepicker.min.js +0 -9
- data/vendor/assets/javascripts/angular-strap/unstable/modules/timepicker.min.js.map +0 -1
- data/vendor/assets/javascripts/angular-strap/unstable/modules/timepicker.tpl.js +0 -14
- data/vendor/assets/javascripts/angular-strap/unstable/modules/timepicker.tpl.min.js +0 -8
- data/vendor/assets/javascripts/angular-strap/unstable/modules/tooltip.js +0 -705
- data/vendor/assets/javascripts/angular-strap/unstable/modules/tooltip.min.js +0 -9
- data/vendor/assets/javascripts/angular-strap/unstable/modules/tooltip.min.js.map +0 -1
- data/vendor/assets/javascripts/angular-strap/unstable/modules/tooltip.tpl.js +0 -14
- data/vendor/assets/javascripts/angular-strap/unstable/modules/tooltip.tpl.min.js +0 -8
- data/vendor/assets/javascripts/angular-strap/unstable/modules/typeahead.js +0 -266
- data/vendor/assets/javascripts/angular-strap/unstable/modules/typeahead.min.js +0 -9
- data/vendor/assets/javascripts/angular-strap/unstable/modules/typeahead.min.js.map +0 -1
- data/vendor/assets/javascripts/angular-strap/unstable/modules/typeahead.tpl.js +0 -14
- data/vendor/assets/javascripts/angular-strap/unstable/modules/typeahead.tpl.min.js +0 -8
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA1:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: fa51e74ad59cb6027fc917a6258aecd3f9a25edc
|
|
4
|
+
data.tar.gz: 1c12640cf47c9e1b71be35956265639aa0eb359f
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 0f8a7c918abd295b8d188415e20459a51e8be3a106ca9f78b60fb7bf3edbe32ea68db280c312853a70686742711958a65cfc0924b00e0cf05f29ed678e6f884e
|
|
7
|
+
data.tar.gz: 186f9a746746d59d2f909847fe9096f16966cf3d15ba5d6533be254fe0b8eb3a075d0a73691296ead381ac450091cf7cc830b40a621224e38f0bf00b5cb9ab5e
|
data/README.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# rails-angular-strap
|
|
1
|
+
# rails-angular-strap <a href="http://badge.fury.io/rb/rails-angular-strap"><img src="https://badge.fury.io/rb/rails-angular-strap.svg" alt="Gem Version" height="18"></a>
|
|
2
2
|
|
|
3
3
|
rails-angular-strap wraps the [AngularStrap](http://mgcrea.github.io/angular-strap/) framework for use in Rails 3.1 and above.
|
|
4
4
|
|
|
@@ -16,14 +16,6 @@ If you desire to require minified AngularStrap files, add the following:
|
|
|
16
16
|
|
|
17
17
|
//= require angular-strap.min
|
|
18
18
|
|
|
19
|
-
Also there are unstable(snapshot) version of AngularStrap, just add the following:
|
|
20
|
-
|
|
21
|
-
//= require angular-strap-unstable
|
|
22
|
-
|
|
23
|
-
or, minified version:
|
|
24
|
-
|
|
25
|
-
//= require angular-strap-unstable.min
|
|
26
|
-
|
|
27
19
|
## Versioning
|
|
28
20
|
|
|
29
|
-
Current version of AngularStrap - 2.1
|
|
21
|
+
Current version of AngularStrap - 2.2.1
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
//= require ./angular-strap/angular-strap.js
|
|
2
|
-
//= require ./angular-strap/angular-strap.tpl.js
|
|
2
|
+
//= require ./angular-strap/angular-strap.tpl.js
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
//= require ./angular-strap/angular-strap.min.js
|
|
2
|
-
//= require ./angular-strap/angular-strap.tpl.min.js
|
|
2
|
+
//= require ./angular-strap/angular-strap.tpl.min.js
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* angular-strap
|
|
3
|
-
* @version v2.1
|
|
3
|
+
* @version v2.2.1 - 2015-03-10
|
|
4
4
|
* @link http://mgcrea.github.io/angular-strap
|
|
5
5
|
* @author Olivier Louvignes (olivier@mg-crea.com)
|
|
6
6
|
* @license MIT License, http://www.opensource.org/licenses/MIT
|
|
@@ -33,7 +33,8 @@ angular.module('mgcrea.ngStrap.affix', ['mgcrea.ngStrap.helpers.dimensions', 'mg
|
|
|
33
33
|
.provider('$affix', function() {
|
|
34
34
|
|
|
35
35
|
var defaults = this.defaults = {
|
|
36
|
-
offsetTop: 'auto'
|
|
36
|
+
offsetTop: 'auto',
|
|
37
|
+
inlineStyles: true
|
|
37
38
|
};
|
|
38
39
|
|
|
39
40
|
this.$get = ["$window", "debounce", "dimensions", function($window, debounce, dimensions) {
|
|
@@ -126,11 +127,13 @@ angular.module('mgcrea.ngStrap.affix', ['mgcrea.ngStrap.helpers.dimensions', 'mg
|
|
|
126
127
|
|
|
127
128
|
if(affix === 'top') {
|
|
128
129
|
unpin = null;
|
|
129
|
-
element.css('position', (options.offsetParent) ? '' : 'relative');
|
|
130
130
|
if(setWidth) {
|
|
131
131
|
element.css('width', '');
|
|
132
132
|
}
|
|
133
|
-
|
|
133
|
+
if (options.inlineStyles) {
|
|
134
|
+
element.css('position', (options.offsetParent) ? '' : 'relative');
|
|
135
|
+
element.css('top', '');
|
|
136
|
+
}
|
|
134
137
|
} else if(affix === 'bottom') {
|
|
135
138
|
if (options.offsetUnpin) {
|
|
136
139
|
unpin = -(options.offsetUnpin * 1);
|
|
@@ -143,15 +146,19 @@ angular.module('mgcrea.ngStrap.affix', ['mgcrea.ngStrap.helpers.dimensions', 'mg
|
|
|
143
146
|
if(setWidth) {
|
|
144
147
|
element.css('width', '');
|
|
145
148
|
}
|
|
146
|
-
|
|
147
|
-
|
|
149
|
+
if (options.inlineStyles) {
|
|
150
|
+
element.css('position', (options.offsetParent) ? '' : 'relative');
|
|
151
|
+
element.css('top', (options.offsetParent) ? '' : ((bodyEl[0].offsetHeight - offsetBottom - elementHeight - initialOffsetTop) + 'px'));
|
|
152
|
+
}
|
|
148
153
|
} else { // affix === 'middle'
|
|
149
154
|
unpin = null;
|
|
150
155
|
if(setWidth) {
|
|
151
156
|
element.css('width', element[0].offsetWidth + 'px');
|
|
152
157
|
}
|
|
153
|
-
|
|
154
|
-
|
|
158
|
+
if (options.inlineStyles) {
|
|
159
|
+
element.css('position', 'fixed');
|
|
160
|
+
element.css('top', initialAffixTop + 'px');
|
|
161
|
+
}
|
|
155
162
|
}
|
|
156
163
|
|
|
157
164
|
};
|
|
@@ -165,7 +172,9 @@ angular.module('mgcrea.ngStrap.affix', ['mgcrea.ngStrap.helpers.dimensions', 'mg
|
|
|
165
172
|
$affix.$parseOffsets = function() {
|
|
166
173
|
var initialPosition = element.css('position');
|
|
167
174
|
// Reset position to calculate correct offsetTop
|
|
168
|
-
|
|
175
|
+
if (options.inlineStyles){
|
|
176
|
+
element.css('position', (options.offsetParent) ? '' : 'relative');
|
|
177
|
+
}
|
|
169
178
|
|
|
170
179
|
if(options.offsetTop) {
|
|
171
180
|
if(options.offsetTop === 'auto') {
|
|
@@ -196,7 +205,9 @@ angular.module('mgcrea.ngStrap.affix', ['mgcrea.ngStrap.helpers.dimensions', 'mg
|
|
|
196
205
|
}
|
|
197
206
|
|
|
198
207
|
// Bring back the element's position after calculations
|
|
199
|
-
|
|
208
|
+
if (options.inlineStyles){
|
|
209
|
+
element.css('position', initialPosition);
|
|
210
|
+
}
|
|
200
211
|
};
|
|
201
212
|
|
|
202
213
|
// Private methods
|
|
@@ -244,9 +255,14 @@ angular.module('mgcrea.ngStrap.affix', ['mgcrea.ngStrap.helpers.dimensions', 'mg
|
|
|
244
255
|
require: '^?bsAffixTarget',
|
|
245
256
|
link: function postLink(scope, element, attr, affixTarget) {
|
|
246
257
|
|
|
247
|
-
var options = {scope: scope,
|
|
248
|
-
angular.forEach(['offsetTop', 'offsetBottom', 'offsetParent', 'offsetUnpin'], function(key) {
|
|
249
|
-
if(angular.isDefined(attr[key]))
|
|
258
|
+
var options = {scope: scope, target: affixTarget ? affixTarget.$element : angular.element($window)};
|
|
259
|
+
angular.forEach(['offsetTop', 'offsetBottom', 'offsetParent', 'offsetUnpin', 'inlineStyles'], function(key) {
|
|
260
|
+
if(angular.isDefined(attr[key])) {
|
|
261
|
+
var option = attr[key];
|
|
262
|
+
if (/true/i.test(option)) option = true;
|
|
263
|
+
if (/false/i.test(option)) option = false;
|
|
264
|
+
options[key] = option;
|
|
265
|
+
}
|
|
250
266
|
});
|
|
251
267
|
|
|
252
268
|
var affix = $affix(element, options);
|
|
@@ -269,71 +285,48 @@ angular.module('mgcrea.ngStrap.affix', ['mgcrea.ngStrap.helpers.dimensions', 'mg
|
|
|
269
285
|
};
|
|
270
286
|
});
|
|
271
287
|
|
|
272
|
-
// Source:
|
|
273
|
-
|
|
274
|
-
// @TODO: submit issue to core
|
|
275
|
-
// '<span ng-if="title"><strong ng-bind="title"></strong> </span><span ng-bind-html="content"></span>' +
|
|
276
|
-
|
|
277
|
-
angular.module('mgcrea.ngStrap.alert', ['mgcrea.ngStrap.modal'])
|
|
288
|
+
// Source: aside.js
|
|
289
|
+
angular.module('mgcrea.ngStrap.aside', ['mgcrea.ngStrap.modal'])
|
|
278
290
|
|
|
279
|
-
.provider('$
|
|
291
|
+
.provider('$aside', function() {
|
|
280
292
|
|
|
281
293
|
var defaults = this.defaults = {
|
|
282
|
-
animation: 'am-fade',
|
|
283
|
-
prefixClass: '
|
|
284
|
-
prefixEvent: '
|
|
285
|
-
placement:
|
|
286
|
-
template: '
|
|
294
|
+
animation: 'am-fade-and-slide-right',
|
|
295
|
+
prefixClass: 'aside',
|
|
296
|
+
prefixEvent: 'aside',
|
|
297
|
+
placement: 'right',
|
|
298
|
+
template: 'aside/aside.tpl.html',
|
|
299
|
+
contentTemplate: false,
|
|
287
300
|
container: false,
|
|
288
301
|
element: null,
|
|
289
|
-
backdrop:
|
|
302
|
+
backdrop: true,
|
|
290
303
|
keyboard: true,
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
duration: false,
|
|
294
|
-
type: false,
|
|
295
|
-
dismissable: true
|
|
304
|
+
html: false,
|
|
305
|
+
show: true
|
|
296
306
|
};
|
|
297
307
|
|
|
298
|
-
this.$get = ["$modal",
|
|
308
|
+
this.$get = ["$modal", function($modal) {
|
|
299
309
|
|
|
300
|
-
function
|
|
310
|
+
function AsideFactory(config) {
|
|
301
311
|
|
|
302
|
-
var $
|
|
312
|
+
var $aside = {};
|
|
303
313
|
|
|
304
314
|
// Common vars
|
|
305
315
|
var options = angular.extend({}, defaults, config);
|
|
306
316
|
|
|
307
|
-
$
|
|
308
|
-
|
|
309
|
-
// Support scope as string options [/*title, content, */ type, dismissable]
|
|
310
|
-
$alert.$scope.dismissable = !!options.dismissable;
|
|
311
|
-
if(options.type) {
|
|
312
|
-
$alert.$scope.type = options.type;
|
|
313
|
-
}
|
|
314
|
-
|
|
315
|
-
// Support auto-close duration
|
|
316
|
-
var show = $alert.show;
|
|
317
|
-
if(options.duration) {
|
|
318
|
-
$alert.show = function() {
|
|
319
|
-
show();
|
|
320
|
-
$timeout(function() {
|
|
321
|
-
$alert.hide();
|
|
322
|
-
}, options.duration * 1000);
|
|
323
|
-
};
|
|
324
|
-
}
|
|
317
|
+
$aside = $modal(options);
|
|
325
318
|
|
|
326
|
-
return $
|
|
319
|
+
return $aside;
|
|
327
320
|
|
|
328
321
|
}
|
|
329
322
|
|
|
330
|
-
return
|
|
323
|
+
return AsideFactory;
|
|
331
324
|
|
|
332
325
|
}];
|
|
333
326
|
|
|
334
327
|
})
|
|
335
328
|
|
|
336
|
-
.directive('
|
|
329
|
+
.directive('bsAside', ["$window", "$sce", "$aside", function($window, $sce, $aside) {
|
|
337
330
|
|
|
338
331
|
var requestAnimationFrame = $window.requestAnimationFrame || $window.setTimeout;
|
|
339
332
|
|
|
@@ -341,22 +334,21 @@ angular.module('mgcrea.ngStrap.alert', ['mgcrea.ngStrap.modal'])
|
|
|
341
334
|
restrict: 'EAC',
|
|
342
335
|
scope: true,
|
|
343
336
|
link: function postLink(scope, element, attr, transclusion) {
|
|
344
|
-
|
|
345
337
|
// Directive options
|
|
346
338
|
var options = {scope: scope, element: element, show: false};
|
|
347
|
-
angular.forEach(['template', '
|
|
339
|
+
angular.forEach(['template', 'contentTemplate', 'placement', 'backdrop', 'keyboard', 'html', 'container', 'animation'], function(key) {
|
|
348
340
|
if(angular.isDefined(attr[key])) options[key] = attr[key];
|
|
349
341
|
});
|
|
350
342
|
|
|
351
343
|
// Support scope as data-attrs
|
|
352
|
-
angular.forEach(['title', 'content'
|
|
344
|
+
angular.forEach(['title', 'content'], function(key) {
|
|
353
345
|
attr[key] && attr.$observe(key, function(newValue, oldValue) {
|
|
354
346
|
scope[key] = $sce.trustAsHtml(newValue);
|
|
355
347
|
});
|
|
356
348
|
});
|
|
357
349
|
|
|
358
350
|
// Support scope as an object
|
|
359
|
-
attr.
|
|
351
|
+
attr.bsAside && scope.$watch(attr.bsAside, function(newValue, oldValue) {
|
|
360
352
|
if(angular.isObject(newValue)) {
|
|
361
353
|
angular.extend(scope, newValue);
|
|
362
354
|
} else {
|
|
@@ -364,17 +356,17 @@ angular.module('mgcrea.ngStrap.alert', ['mgcrea.ngStrap.modal'])
|
|
|
364
356
|
}
|
|
365
357
|
}, true);
|
|
366
358
|
|
|
367
|
-
// Initialize
|
|
368
|
-
var
|
|
359
|
+
// Initialize aside
|
|
360
|
+
var aside = $aside(options);
|
|
369
361
|
|
|
370
362
|
// Trigger
|
|
371
|
-
element.on(attr.trigger || 'click',
|
|
363
|
+
element.on(attr.trigger || 'click', aside.toggle);
|
|
372
364
|
|
|
373
365
|
// Garbage collection
|
|
374
366
|
scope.$on('$destroy', function() {
|
|
375
|
-
if (
|
|
367
|
+
if (aside) aside.destroy();
|
|
376
368
|
options = null;
|
|
377
|
-
|
|
369
|
+
aside = null;
|
|
378
370
|
});
|
|
379
371
|
|
|
380
372
|
}
|
|
@@ -382,48 +374,71 @@ angular.module('mgcrea.ngStrap.alert', ['mgcrea.ngStrap.modal'])
|
|
|
382
374
|
|
|
383
375
|
}]);
|
|
384
376
|
|
|
385
|
-
// Source:
|
|
386
|
-
|
|
377
|
+
// Source: alert.js
|
|
378
|
+
// @BUG: following snippet won't compile correctly
|
|
379
|
+
// @TODO: submit issue to core
|
|
380
|
+
// '<span ng-if="title"><strong ng-bind="title"></strong> </span><span ng-bind-html="content"></span>' +
|
|
387
381
|
|
|
388
|
-
|
|
382
|
+
angular.module('mgcrea.ngStrap.alert', ['mgcrea.ngStrap.modal'])
|
|
383
|
+
|
|
384
|
+
.provider('$alert', function() {
|
|
389
385
|
|
|
390
386
|
var defaults = this.defaults = {
|
|
391
|
-
animation: 'am-fade
|
|
392
|
-
prefixClass: '
|
|
393
|
-
prefixEvent: '
|
|
394
|
-
placement:
|
|
395
|
-
template: '
|
|
396
|
-
contentTemplate: false,
|
|
387
|
+
animation: 'am-fade',
|
|
388
|
+
prefixClass: 'alert',
|
|
389
|
+
prefixEvent: 'alert',
|
|
390
|
+
placement: null,
|
|
391
|
+
template: 'alert/alert.tpl.html',
|
|
397
392
|
container: false,
|
|
398
393
|
element: null,
|
|
399
|
-
backdrop:
|
|
394
|
+
backdrop: false,
|
|
400
395
|
keyboard: true,
|
|
401
|
-
|
|
402
|
-
|
|
396
|
+
show: true,
|
|
397
|
+
// Specific options
|
|
398
|
+
duration: false,
|
|
399
|
+
type: false,
|
|
400
|
+
dismissable: true
|
|
403
401
|
};
|
|
404
402
|
|
|
405
|
-
this.$get = ["$modal", function($modal) {
|
|
403
|
+
this.$get = ["$modal", "$timeout", function($modal, $timeout) {
|
|
406
404
|
|
|
407
|
-
function
|
|
405
|
+
function AlertFactory(config) {
|
|
408
406
|
|
|
409
|
-
var $
|
|
407
|
+
var $alert = {};
|
|
410
408
|
|
|
411
409
|
// Common vars
|
|
412
410
|
var options = angular.extend({}, defaults, config);
|
|
413
411
|
|
|
414
|
-
$
|
|
412
|
+
$alert = $modal(options);
|
|
415
413
|
|
|
416
|
-
|
|
414
|
+
// Support scope as string options [/*title, content, */ type, dismissable]
|
|
415
|
+
$alert.$scope.dismissable = !!options.dismissable;
|
|
416
|
+
if(options.type) {
|
|
417
|
+
$alert.$scope.type = options.type;
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
// Support auto-close duration
|
|
421
|
+
var show = $alert.show;
|
|
422
|
+
if(options.duration) {
|
|
423
|
+
$alert.show = function() {
|
|
424
|
+
show();
|
|
425
|
+
$timeout(function() {
|
|
426
|
+
$alert.hide();
|
|
427
|
+
}, options.duration * 1000);
|
|
428
|
+
};
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
return $alert;
|
|
417
432
|
|
|
418
433
|
}
|
|
419
434
|
|
|
420
|
-
return
|
|
435
|
+
return AlertFactory;
|
|
421
436
|
|
|
422
437
|
}];
|
|
423
438
|
|
|
424
439
|
})
|
|
425
440
|
|
|
426
|
-
.directive('
|
|
441
|
+
.directive('bsAlert', ["$window", "$sce", "$alert", function($window, $sce, $alert) {
|
|
427
442
|
|
|
428
443
|
var requestAnimationFrame = $window.requestAnimationFrame || $window.setTimeout;
|
|
429
444
|
|
|
@@ -431,21 +446,28 @@ angular.module('mgcrea.ngStrap.aside', ['mgcrea.ngStrap.modal'])
|
|
|
431
446
|
restrict: 'EAC',
|
|
432
447
|
scope: true,
|
|
433
448
|
link: function postLink(scope, element, attr, transclusion) {
|
|
449
|
+
|
|
434
450
|
// Directive options
|
|
435
451
|
var options = {scope: scope, element: element, show: false};
|
|
436
|
-
angular.forEach(['template', '
|
|
452
|
+
angular.forEach(['template', 'placement', 'keyboard', 'html', 'container', 'animation', 'duration', 'dismissable'], function(key) {
|
|
437
453
|
if(angular.isDefined(attr[key])) options[key] = attr[key];
|
|
438
454
|
});
|
|
439
455
|
|
|
456
|
+
// overwrite inherited title value when no value specified
|
|
457
|
+
// fix for angular 1.3.1 531a8de72c439d8ddd064874bf364c00cedabb11
|
|
458
|
+
if (!scope.hasOwnProperty('title')){
|
|
459
|
+
scope.title = '';
|
|
460
|
+
}
|
|
461
|
+
|
|
440
462
|
// Support scope as data-attrs
|
|
441
|
-
angular.forEach(['title', 'content'], function(key) {
|
|
463
|
+
angular.forEach(['title', 'content', 'type'], function(key) {
|
|
442
464
|
attr[key] && attr.$observe(key, function(newValue, oldValue) {
|
|
443
465
|
scope[key] = $sce.trustAsHtml(newValue);
|
|
444
466
|
});
|
|
445
467
|
});
|
|
446
468
|
|
|
447
469
|
// Support scope as an object
|
|
448
|
-
attr.
|
|
470
|
+
attr.bsAlert && scope.$watch(attr.bsAlert, function(newValue, oldValue) {
|
|
449
471
|
if(angular.isObject(newValue)) {
|
|
450
472
|
angular.extend(scope, newValue);
|
|
451
473
|
} else {
|
|
@@ -453,17 +475,17 @@ angular.module('mgcrea.ngStrap.aside', ['mgcrea.ngStrap.modal'])
|
|
|
453
475
|
}
|
|
454
476
|
}, true);
|
|
455
477
|
|
|
456
|
-
// Initialize
|
|
457
|
-
var
|
|
478
|
+
// Initialize alert
|
|
479
|
+
var alert = $alert(options);
|
|
458
480
|
|
|
459
481
|
// Trigger
|
|
460
|
-
element.on(attr.trigger || 'click',
|
|
482
|
+
element.on(attr.trigger || 'click', alert.toggle);
|
|
461
483
|
|
|
462
484
|
// Garbage collection
|
|
463
485
|
scope.$on('$destroy', function() {
|
|
464
|
-
if (
|
|
486
|
+
if (alert) alert.destroy();
|
|
465
487
|
options = null;
|
|
466
|
-
|
|
488
|
+
alert = null;
|
|
467
489
|
});
|
|
468
490
|
|
|
469
491
|
}
|
|
@@ -614,7 +636,11 @@ angular.module('mgcrea.ngStrap.button', [])
|
|
|
614
636
|
var isInput = element[0].nodeName === 'INPUT';
|
|
615
637
|
var activeElement = isInput ? element.parent() : element;
|
|
616
638
|
|
|
617
|
-
var value
|
|
639
|
+
var value;
|
|
640
|
+
attr.$observe('value', function(v) {
|
|
641
|
+
value = constantValueRegExp.test(v) ? scope.$eval(v) : v;
|
|
642
|
+
controller.$render();
|
|
643
|
+
});
|
|
618
644
|
|
|
619
645
|
// model -> view
|
|
620
646
|
controller.$render = function () {
|
|
@@ -929,6 +955,7 @@ angular.module('mgcrea.ngStrap.datepicker', [
|
|
|
929
955
|
useNative: false,
|
|
930
956
|
dateType: 'date',
|
|
931
957
|
dateFormat: 'shortDate',
|
|
958
|
+
timezone: null,
|
|
932
959
|
modelDateFormat: null,
|
|
933
960
|
dayFormat: 'dd',
|
|
934
961
|
monthFormat: 'MMM',
|
|
@@ -1173,7 +1200,7 @@ angular.module('mgcrea.ngStrap.datepicker', [
|
|
|
1173
1200
|
|
|
1174
1201
|
// Directive options
|
|
1175
1202
|
var options = {scope: scope, controller: controller};
|
|
1176
|
-
angular.forEach(['placement', 'container', 'delay', 'trigger', 'keyboard', 'html', 'animation', 'template', 'autoclose', 'dateType', 'dateFormat', 'modelDateFormat', 'dayFormat', 'strictFormat', 'startWeek', 'startDate', 'useNative', 'lang', 'startView', 'minView', 'iconLeft', 'iconRight', 'daysOfWeekDisabled', 'id'], function(key) {
|
|
1203
|
+
angular.forEach(['placement', 'container', 'delay', 'trigger', 'keyboard', 'html', 'animation', 'template', 'autoclose', 'dateType', 'dateFormat', 'timezone', 'modelDateFormat', 'dayFormat', 'strictFormat', 'startWeek', 'startDate', 'useNative', 'lang', 'startView', 'minView', 'iconLeft', 'iconRight', 'daysOfWeekDisabled', 'id'], function(key) {
|
|
1177
1204
|
if(angular.isDefined(attr[key])) options[key] = attr[key];
|
|
1178
1205
|
});
|
|
1179
1206
|
|
|
@@ -1248,6 +1275,7 @@ angular.module('mgcrea.ngStrap.datepicker', [
|
|
|
1248
1275
|
// viewValue -> $parsers -> modelValue
|
|
1249
1276
|
controller.$parsers.unshift(function(viewValue) {
|
|
1250
1277
|
// console.warn('$parser("%s"): viewValue=%o', element.attr('ng-model'), viewValue);
|
|
1278
|
+
var date;
|
|
1251
1279
|
// Null values should correctly reset the model value & validity
|
|
1252
1280
|
if(!viewValue) {
|
|
1253
1281
|
controller.$setValidity('date', true);
|
|
@@ -1265,16 +1293,20 @@ angular.module('mgcrea.ngStrap.datepicker', [
|
|
|
1265
1293
|
} else {
|
|
1266
1294
|
validateAgainstMinMaxDate(parsedDate);
|
|
1267
1295
|
}
|
|
1296
|
+
|
|
1268
1297
|
if(options.dateType === 'string') {
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1298
|
+
date = dateParser.timezoneOffsetAdjust(parsedDate, options.timezone, true);
|
|
1299
|
+
return formatDate(date, options.modelDateFormat || options.dateFormat);
|
|
1300
|
+
}
|
|
1301
|
+
date = dateParser.timezoneOffsetAdjust(controller.$dateValue, options.timezone, true);
|
|
1302
|
+
if(options.dateType === 'number') {
|
|
1303
|
+
return date.getTime();
|
|
1272
1304
|
} else if(options.dateType === 'unix') {
|
|
1273
|
-
return
|
|
1305
|
+
return date.getTime() / 1000;
|
|
1274
1306
|
} else if(options.dateType === 'iso') {
|
|
1275
|
-
return
|
|
1307
|
+
return date.toISOString();
|
|
1276
1308
|
} else {
|
|
1277
|
-
return new Date(
|
|
1309
|
+
return new Date(date);
|
|
1278
1310
|
}
|
|
1279
1311
|
});
|
|
1280
1312
|
|
|
@@ -1298,7 +1330,7 @@ angular.module('mgcrea.ngStrap.datepicker', [
|
|
|
1298
1330
|
// var today = new Date();
|
|
1299
1331
|
// date = new Date(today.getFullYear(), today.getMonth(), today.getDate(), 0, 0, 0, 0);
|
|
1300
1332
|
// }
|
|
1301
|
-
controller.$dateValue = date;
|
|
1333
|
+
controller.$dateValue = dateParser.timezoneOffsetAdjust(date, options.timezone);
|
|
1302
1334
|
return getDateFormattedString();
|
|
1303
1335
|
});
|
|
1304
1336
|
|
|
@@ -1364,7 +1396,6 @@ angular.module('mgcrea.ngStrap.datepicker', [
|
|
|
1364
1396
|
|
|
1365
1397
|
var startDate = picker.$date || (options.startDate ? dateParser.getDateForAttribute('startDate', options.startDate) : new Date());
|
|
1366
1398
|
var viewDate = {year: startDate.getFullYear(), month: startDate.getMonth(), date: startDate.getDate()};
|
|
1367
|
-
var timezoneOffset = startDate.getTimezoneOffset() * 6e4;
|
|
1368
1399
|
|
|
1369
1400
|
var views = [{
|
|
1370
1401
|
format: options.dayFormat,
|
|
@@ -1382,7 +1413,7 @@ angular.module('mgcrea.ngStrap.datepicker', [
|
|
|
1382
1413
|
build: function() {
|
|
1383
1414
|
var firstDayOfMonth = new Date(viewDate.year, viewDate.month, 1), firstDayOfMonthOffset = firstDayOfMonth.getTimezoneOffset();
|
|
1384
1415
|
var firstDate = new Date(+firstDayOfMonth - mod(firstDayOfMonth.getDay() - options.startWeek, 7) * 864e5), firstDateOffset = firstDate.getTimezoneOffset();
|
|
1385
|
-
var today = new Date().toDateString();
|
|
1416
|
+
var today = dateParser.timezoneOffsetAdjust(new Date(), options.timezone).toDateString();
|
|
1386
1417
|
// Handle daylight time switch
|
|
1387
1418
|
if(firstDateOffset !== firstDayOfMonthOffset) firstDate = new Date(+firstDate + (firstDateOffset - firstDayOfMonthOffset) * 60e3);
|
|
1388
1419
|
var days = [], day;
|
|
@@ -1548,6 +1579,7 @@ angular.module('mgcrea.ngStrap.dropdown', ['mgcrea.ngStrap.tooltip'])
|
|
|
1548
1579
|
var defaults = this.defaults = {
|
|
1549
1580
|
animation: 'am-fade',
|
|
1550
1581
|
prefixClass: 'dropdown',
|
|
1582
|
+
prefixEvent: 'dropdown',
|
|
1551
1583
|
placement: 'bottom-left',
|
|
1552
1584
|
template: 'dropdown/dropdown.tpl.html',
|
|
1553
1585
|
trigger: 'click',
|
|
@@ -1730,8 +1762,8 @@ angular.module('mgcrea.ngStrap.helpers.dateFormatter', [])
|
|
|
1730
1762
|
return !!splitTimeFormat(timeFormat)[3];
|
|
1731
1763
|
};
|
|
1732
1764
|
|
|
1733
|
-
this.formatDate = function(date, format, lang){
|
|
1734
|
-
return dateFilter(date, format);
|
|
1765
|
+
this.formatDate = function(date, format, lang, timezone){
|
|
1766
|
+
return dateFilter(date, format, timezone);
|
|
1735
1767
|
};
|
|
1736
1768
|
|
|
1737
1769
|
}]);
|
|
@@ -1867,10 +1899,10 @@ angular.module('mgcrea.ngStrap.helpers.dateParser', [])
|
|
|
1867
1899
|
return regex.test(date);
|
|
1868
1900
|
};
|
|
1869
1901
|
|
|
1870
|
-
$dateParser.parse = function(value, baseDate, format) {
|
|
1902
|
+
$dateParser.parse = function(value, baseDate, format, timezone) {
|
|
1871
1903
|
// check for date format special names
|
|
1872
1904
|
if(format) format = $locale.DATETIME_FORMATS[format] || format;
|
|
1873
|
-
if(angular.isDate(value)) value = dateFilter(value, format || $dateParser.$format);
|
|
1905
|
+
if(angular.isDate(value)) value = dateFilter(value, format || $dateParser.$format, timezone);
|
|
1874
1906
|
var formatRegex = format ? regExpForFormat(format) : regex;
|
|
1875
1907
|
var formatSetMap = format ? setMapForFormat(format) : setMap;
|
|
1876
1908
|
var matches = formatRegex.exec(value);
|
|
@@ -1945,6 +1977,24 @@ angular.module('mgcrea.ngStrap.helpers.dateParser', [])
|
|
|
1945
1977
|
return date;
|
|
1946
1978
|
};
|
|
1947
1979
|
|
|
1980
|
+
/* Correct the date for timezone offset.
|
|
1981
|
+
* @param date (Date) the date to adjust
|
|
1982
|
+
* @param timezone (string) the timezone to adjust for
|
|
1983
|
+
* @param undo (boolean) to add or subtract timezone offset
|
|
1984
|
+
* @return (Date) the corrected date
|
|
1985
|
+
*/
|
|
1986
|
+
$dateParser.timezoneOffsetAdjust = function(date, timezone, undo) {
|
|
1987
|
+
if (!date) {
|
|
1988
|
+
return null;
|
|
1989
|
+
}
|
|
1990
|
+
// Right now, only 'UTC' is supported.
|
|
1991
|
+
if (timezone && timezone === 'UTC') {
|
|
1992
|
+
date = new Date(date.getTime());
|
|
1993
|
+
date.setMinutes(date.getMinutes() + (undo?-1:1)*date.getTimezoneOffset());
|
|
1994
|
+
}
|
|
1995
|
+
return date;
|
|
1996
|
+
};
|
|
1997
|
+
|
|
1948
1998
|
// Private functions
|
|
1949
1999
|
|
|
1950
2000
|
function setMapForFormat(format) {
|
|
@@ -2107,6 +2157,69 @@ angular.module('mgcrea.ngStrap.helpers.dimensions', [])
|
|
|
2107
2157
|
left: boxRect.left + (window.pageXOffset || docElement.documentElement.scrollLeft) - (docElement.documentElement.clientLeft || 0)
|
|
2108
2158
|
};
|
|
2109
2159
|
};
|
|
2160
|
+
|
|
2161
|
+
/**
|
|
2162
|
+
* Provides set equivalent of jQuery's offset function:
|
|
2163
|
+
* @required-by bootstrap-tooltip
|
|
2164
|
+
* @url http://api.jquery.com/offset/
|
|
2165
|
+
* @param element
|
|
2166
|
+
* @param options
|
|
2167
|
+
* @param i
|
|
2168
|
+
*/
|
|
2169
|
+
fn.setOffset = function (element, options, i) {
|
|
2170
|
+
var curPosition,
|
|
2171
|
+
curLeft,
|
|
2172
|
+
curCSSTop,
|
|
2173
|
+
curTop,
|
|
2174
|
+
curOffset,
|
|
2175
|
+
curCSSLeft,
|
|
2176
|
+
calculatePosition,
|
|
2177
|
+
position = fn.css(element, 'position'),
|
|
2178
|
+
curElem = angular.element(element),
|
|
2179
|
+
props = {};
|
|
2180
|
+
|
|
2181
|
+
// Set position first, in-case top/left are set even on static elem
|
|
2182
|
+
if (position === 'static') {
|
|
2183
|
+
element.style.position = 'relative';
|
|
2184
|
+
}
|
|
2185
|
+
|
|
2186
|
+
curOffset = fn.offset(element);
|
|
2187
|
+
curCSSTop = fn.css(element, 'top');
|
|
2188
|
+
curCSSLeft = fn.css(element, 'left');
|
|
2189
|
+
calculatePosition = (position === 'absolute' || position === 'fixed') &&
|
|
2190
|
+
(curCSSTop + curCSSLeft).indexOf('auto') > -1;
|
|
2191
|
+
|
|
2192
|
+
// Need to be able to calculate position if either
|
|
2193
|
+
// top or left is auto and position is either absolute or fixed
|
|
2194
|
+
if (calculatePosition) {
|
|
2195
|
+
curPosition = fn.position(element);
|
|
2196
|
+
curTop = curPosition.top;
|
|
2197
|
+
curLeft = curPosition.left;
|
|
2198
|
+
} else {
|
|
2199
|
+
curTop = parseFloat(curCSSTop) || 0;
|
|
2200
|
+
curLeft = parseFloat(curCSSLeft) || 0;
|
|
2201
|
+
}
|
|
2202
|
+
|
|
2203
|
+
if (angular.isFunction(options)) {
|
|
2204
|
+
options = options.call(element, i, curOffset);
|
|
2205
|
+
}
|
|
2206
|
+
|
|
2207
|
+
if (options.top !== null ) {
|
|
2208
|
+
props.top = (options.top - curOffset.top) + curTop;
|
|
2209
|
+
}
|
|
2210
|
+
if ( options.left !== null ) {
|
|
2211
|
+
props.left = (options.left - curOffset.left) + curLeft;
|
|
2212
|
+
}
|
|
2213
|
+
|
|
2214
|
+
if ('using' in options) {
|
|
2215
|
+
options.using.call(curElem, props);
|
|
2216
|
+
} else {
|
|
2217
|
+
curElem.css({
|
|
2218
|
+
top: props.top + 'px',
|
|
2219
|
+
left: props.left + 'px'
|
|
2220
|
+
});
|
|
2221
|
+
}
|
|
2222
|
+
};
|
|
2110
2223
|
|
|
2111
2224
|
/**
|
|
2112
2225
|
* Provides read-only equivalent of jQuery's position function
|
|
@@ -2415,6 +2528,7 @@ angular.module('mgcrea.ngStrap.modal', ['mgcrea.ngStrap.helpers.dimensions'])
|
|
|
2415
2528
|
// Fetch, compile then initialize modal
|
|
2416
2529
|
var modalLinker, modalElement;
|
|
2417
2530
|
var backdropElement = angular.element('<div class="' + options.prefixClass + '-backdrop"/>');
|
|
2531
|
+
backdropElement.css({position:'fixed', top:'0px', left:'0px', bottom:'0px', right:'0px', 'z-index': 1038});
|
|
2418
2532
|
$modal.$promise.then(function(template) {
|
|
2419
2533
|
if(angular.isObject(template)) template = template.data;
|
|
2420
2534
|
if(options.html) template = template.replace(htmlReplaceRegExp, 'ng-bind-html="');
|
|
@@ -2454,9 +2568,6 @@ angular.module('mgcrea.ngStrap.modal', ['mgcrea.ngStrap.helpers.dimensions'])
|
|
|
2454
2568
|
$modal.show = function() {
|
|
2455
2569
|
if($modal.$isShown) return;
|
|
2456
2570
|
|
|
2457
|
-
if(scope.$emit(options.prefixEvent + '.show.before', $modal).defaultPrevented) {
|
|
2458
|
-
return;
|
|
2459
|
-
}
|
|
2460
2571
|
var parent, after;
|
|
2461
2572
|
if(angular.isElement(options.container)) {
|
|
2462
2573
|
parent = options.container;
|
|
@@ -2474,6 +2585,10 @@ angular.module('mgcrea.ngStrap.modal', ['mgcrea.ngStrap.helpers.dimensions'])
|
|
|
2474
2585
|
// Fetch a cloned element linked from template
|
|
2475
2586
|
modalElement = $modal.$element = modalLinker(scope, function(clonedElement, scope) {});
|
|
2476
2587
|
|
|
2588
|
+
if(scope.$emit(options.prefixEvent + '.show.before', $modal).defaultPrevented) {
|
|
2589
|
+
return;
|
|
2590
|
+
}
|
|
2591
|
+
|
|
2477
2592
|
// Set the initial positioning.
|
|
2478
2593
|
modalElement.css({display: 'block'}).addClass(options.placement);
|
|
2479
2594
|
|
|
@@ -2607,13 +2722,8 @@ angular.module('mgcrea.ngStrap.modal', ['mgcrea.ngStrap.helpers.dimensions'])
|
|
|
2607
2722
|
var fetchPromises = {};
|
|
2608
2723
|
function fetchTemplate(template) {
|
|
2609
2724
|
if(fetchPromises[template]) return fetchPromises[template];
|
|
2610
|
-
return (fetchPromises[template] = $
|
|
2611
|
-
|
|
2612
|
-
if(angular.isObject(res)) {
|
|
2613
|
-
$templateCache.put(template, res.data);
|
|
2614
|
-
return res.data;
|
|
2615
|
-
}
|
|
2616
|
-
return res;
|
|
2725
|
+
return (fetchPromises[template] = $http.get(template, {cache: $templateCache}).then(function(res) {
|
|
2726
|
+
return res.data;
|
|
2617
2727
|
}));
|
|
2618
2728
|
}
|
|
2619
2729
|
|
|
@@ -2632,10 +2742,20 @@ angular.module('mgcrea.ngStrap.modal', ['mgcrea.ngStrap.helpers.dimensions'])
|
|
|
2632
2742
|
|
|
2633
2743
|
// Directive options
|
|
2634
2744
|
var options = {scope: scope, element: element, show: false};
|
|
2635
|
-
angular.forEach(['template', 'contentTemplate', 'placement', '
|
|
2745
|
+
angular.forEach(['template', 'contentTemplate', 'placement', 'container', 'animation', 'id'], function(key) {
|
|
2636
2746
|
if(angular.isDefined(attr[key])) options[key] = attr[key];
|
|
2637
2747
|
});
|
|
2638
2748
|
|
|
2749
|
+
// use string regex match for boolean values
|
|
2750
|
+
var falseValueRegExp = /^(false|0|)$/;
|
|
2751
|
+
angular.forEach(['keyboard', 'html'], function(key) {
|
|
2752
|
+
if(angular.isDefined(attr[key])) options[key] = !falseValueRegExp.test(attr[key]);
|
|
2753
|
+
});
|
|
2754
|
+
|
|
2755
|
+
if(angular.isDefined(attr.backdrop)) {
|
|
2756
|
+
options.backdrop = falseValueRegExp.test(attr.backdrop) ? false : attr.backdrop;
|
|
2757
|
+
}
|
|
2758
|
+
|
|
2639
2759
|
// Support scope as data-attrs
|
|
2640
2760
|
angular.forEach(['title', 'content'], function(key) {
|
|
2641
2761
|
attr[key] && attr.$observe(key, function(newValue, oldValue) {
|
|
@@ -2717,7 +2837,7 @@ angular.module('mgcrea.ngStrap.navbar', [])
|
|
|
2717
2837
|
if(options.strict) {
|
|
2718
2838
|
pattern = '^' + pattern + '$';
|
|
2719
2839
|
}
|
|
2720
|
-
var regexp = new RegExp(pattern,
|
|
2840
|
+
var regexp = new RegExp(pattern, 'i');
|
|
2721
2841
|
|
|
2722
2842
|
if(regexp.test(newValue)) {
|
|
2723
2843
|
liElement.addClass(options.activeClass);
|
|
@@ -2824,6 +2944,12 @@ angular.module('mgcrea.ngStrap.popover', ['mgcrea.ngStrap.tooltip'])
|
|
|
2824
2944
|
if(angular.isString(newValue)) newValue = !!newValue.match(/true|,?(popover),?/i);
|
|
2825
2945
|
newValue === true ? popover.show() : popover.hide();
|
|
2826
2946
|
});
|
|
2947
|
+
|
|
2948
|
+
// Viewport support
|
|
2949
|
+
attr.viewport && scope.$watch(attr.viewport, function (newValue) {
|
|
2950
|
+
if(!popover || !angular.isDefined(newValue)) return;
|
|
2951
|
+
popover.setViewport(newValue);
|
|
2952
|
+
});
|
|
2827
2953
|
|
|
2828
2954
|
// Initialize popover
|
|
2829
2955
|
var popover = $popover(element, options);
|
|
@@ -2840,205 +2966,470 @@ angular.module('mgcrea.ngStrap.popover', ['mgcrea.ngStrap.tooltip'])
|
|
|
2840
2966
|
|
|
2841
2967
|
}]);
|
|
2842
2968
|
|
|
2843
|
-
// Source:
|
|
2844
|
-
angular.module('mgcrea.ngStrap.
|
|
2969
|
+
// Source: scrollspy.js
|
|
2970
|
+
angular.module('mgcrea.ngStrap.scrollspy', ['mgcrea.ngStrap.helpers.debounce', 'mgcrea.ngStrap.helpers.dimensions'])
|
|
2845
2971
|
|
|
2846
|
-
.provider('$
|
|
2972
|
+
.provider('$scrollspy', function() {
|
|
2973
|
+
|
|
2974
|
+
// Pool of registered spies
|
|
2975
|
+
var spies = this.$$spies = {};
|
|
2847
2976
|
|
|
2848
2977
|
var defaults = this.defaults = {
|
|
2849
|
-
|
|
2850
|
-
|
|
2851
|
-
|
|
2852
|
-
placement: 'bottom-left',
|
|
2853
|
-
template: 'select/select.tpl.html',
|
|
2854
|
-
trigger: 'focus',
|
|
2855
|
-
container: false,
|
|
2856
|
-
keyboard: true,
|
|
2857
|
-
html: false,
|
|
2858
|
-
delay: 0,
|
|
2859
|
-
multiple: false,
|
|
2860
|
-
allNoneButtons: false,
|
|
2861
|
-
sort: true,
|
|
2862
|
-
caretHtml: ' <span class="caret"></span>',
|
|
2863
|
-
placeholder: 'Choose among the following...',
|
|
2864
|
-
allText: 'All',
|
|
2865
|
-
noneText: 'None',
|
|
2866
|
-
maxLength: 3,
|
|
2867
|
-
maxLengthHtml: 'selected',
|
|
2868
|
-
iconCheckmark: 'glyphicon glyphicon-ok'
|
|
2978
|
+
debounce: 150,
|
|
2979
|
+
throttle: 100,
|
|
2980
|
+
offset: 100
|
|
2869
2981
|
};
|
|
2870
2982
|
|
|
2871
|
-
this.$get = ["$window", "$document", "$rootScope", "
|
|
2983
|
+
this.$get = ["$window", "$document", "$rootScope", "dimensions", "debounce", "throttle", function($window, $document, $rootScope, dimensions, debounce, throttle) {
|
|
2872
2984
|
|
|
2985
|
+
var windowEl = angular.element($window);
|
|
2986
|
+
var docEl = angular.element($document.prop('documentElement'));
|
|
2873
2987
|
var bodyEl = angular.element($window.document.body);
|
|
2874
|
-
var isNative = /(ip(a|o)d|iphone|android)/ig.test($window.navigator.userAgent);
|
|
2875
|
-
var isTouch = ('createTouch' in $window.document) && isNative;
|
|
2876
2988
|
|
|
2877
|
-
|
|
2989
|
+
// Helper functions
|
|
2878
2990
|
|
|
2879
|
-
|
|
2991
|
+
function nodeName(element, name) {
|
|
2992
|
+
return element[0].nodeName && element[0].nodeName.toLowerCase() === name.toLowerCase();
|
|
2993
|
+
}
|
|
2994
|
+
|
|
2995
|
+
function ScrollSpyFactory(config) {
|
|
2880
2996
|
|
|
2881
2997
|
// Common vars
|
|
2882
2998
|
var options = angular.extend({}, defaults, config);
|
|
2999
|
+
if(!options.element) options.element = bodyEl;
|
|
3000
|
+
var isWindowSpy = nodeName(options.element, 'body');
|
|
3001
|
+
var scrollEl = isWindowSpy ? windowEl : options.element;
|
|
3002
|
+
var scrollId = isWindowSpy ? 'window' : options.id;
|
|
2883
3003
|
|
|
2884
|
-
|
|
2885
|
-
|
|
3004
|
+
// Use existing spy
|
|
3005
|
+
if(spies[scrollId]) {
|
|
3006
|
+
spies[scrollId].$$count++;
|
|
3007
|
+
return spies[scrollId];
|
|
3008
|
+
}
|
|
2886
3009
|
|
|
2887
|
-
|
|
2888
|
-
scope.$activeIndex = 0;
|
|
2889
|
-
scope.$isMultiple = options.multiple;
|
|
2890
|
-
scope.$showAllNoneButtons = options.allNoneButtons && options.multiple;
|
|
2891
|
-
scope.$iconCheckmark = options.iconCheckmark;
|
|
2892
|
-
scope.$allText = options.allText;
|
|
2893
|
-
scope.$noneText = options.noneText;
|
|
3010
|
+
var $scrollspy = {};
|
|
2894
3011
|
|
|
2895
|
-
|
|
2896
|
-
|
|
2897
|
-
|
|
2898
|
-
|
|
2899
|
-
|
|
3012
|
+
// Private vars
|
|
3013
|
+
var unbindViewContentLoaded, unbindIncludeContentLoaded;
|
|
3014
|
+
var trackedElements = $scrollspy.$trackedElements = [];
|
|
3015
|
+
var sortedElements = [];
|
|
3016
|
+
var activeTarget;
|
|
3017
|
+
var debouncedCheckPosition;
|
|
3018
|
+
var throttledCheckPosition;
|
|
3019
|
+
var debouncedCheckOffsets;
|
|
3020
|
+
var viewportHeight;
|
|
3021
|
+
var scrollTop;
|
|
2900
3022
|
|
|
2901
|
-
|
|
2902
|
-
scope.$$postDigest(function() {
|
|
2903
|
-
$select.select(index);
|
|
2904
|
-
});
|
|
2905
|
-
};
|
|
3023
|
+
$scrollspy.init = function() {
|
|
2906
3024
|
|
|
2907
|
-
|
|
2908
|
-
|
|
2909
|
-
};
|
|
3025
|
+
// Setup internal ref counter
|
|
3026
|
+
this.$$count = 1;
|
|
2910
3027
|
|
|
2911
|
-
|
|
2912
|
-
|
|
2913
|
-
|
|
3028
|
+
// Bind events
|
|
3029
|
+
debouncedCheckPosition = debounce(this.checkPosition, options.debounce);
|
|
3030
|
+
throttledCheckPosition = throttle(this.checkPosition, options.throttle);
|
|
3031
|
+
scrollEl.on('click', this.checkPositionWithEventLoop);
|
|
3032
|
+
windowEl.on('resize', debouncedCheckPosition);
|
|
3033
|
+
scrollEl.on('scroll', throttledCheckPosition);
|
|
2914
3034
|
|
|
2915
|
-
|
|
2916
|
-
|
|
2917
|
-
|
|
2918
|
-
|
|
2919
|
-
|
|
3035
|
+
debouncedCheckOffsets = debounce(this.checkOffsets, options.debounce);
|
|
3036
|
+
unbindViewContentLoaded = $rootScope.$on('$viewContentLoaded', debouncedCheckOffsets);
|
|
3037
|
+
unbindIncludeContentLoaded = $rootScope.$on('$includeContentLoaded', debouncedCheckOffsets);
|
|
3038
|
+
debouncedCheckOffsets();
|
|
3039
|
+
|
|
3040
|
+
// Register spy for reuse
|
|
3041
|
+
if(scrollId) {
|
|
3042
|
+
spies[scrollId] = $scrollspy;
|
|
2920
3043
|
}
|
|
3044
|
+
|
|
2921
3045
|
};
|
|
2922
3046
|
|
|
2923
|
-
|
|
2924
|
-
|
|
2925
|
-
|
|
2926
|
-
|
|
2927
|
-
|
|
3047
|
+
$scrollspy.destroy = function() {
|
|
3048
|
+
|
|
3049
|
+
// Check internal ref counter
|
|
3050
|
+
this.$$count--;
|
|
3051
|
+
if(this.$$count > 0) {
|
|
3052
|
+
return;
|
|
3053
|
+
}
|
|
3054
|
+
|
|
3055
|
+
// Unbind events
|
|
3056
|
+
scrollEl.off('click', this.checkPositionWithEventLoop);
|
|
3057
|
+
windowEl.off('resize', debouncedCheckPosition);
|
|
3058
|
+
scrollEl.off('scroll', throttledCheckPosition);
|
|
3059
|
+
unbindViewContentLoaded();
|
|
3060
|
+
unbindIncludeContentLoaded();
|
|
3061
|
+
if (scrollId) {
|
|
3062
|
+
delete spies[scrollId];
|
|
2928
3063
|
}
|
|
2929
3064
|
};
|
|
2930
3065
|
|
|
2931
|
-
|
|
3066
|
+
$scrollspy.checkPosition = function() {
|
|
2932
3067
|
|
|
2933
|
-
|
|
2934
|
-
|
|
2935
|
-
$select.$updateActiveIndex();
|
|
2936
|
-
};
|
|
3068
|
+
// Not ready yet
|
|
3069
|
+
if(!sortedElements.length) return;
|
|
2937
3070
|
|
|
2938
|
-
|
|
2939
|
-
|
|
2940
|
-
|
|
2941
|
-
|
|
2942
|
-
|
|
2943
|
-
|
|
2944
|
-
|
|
3071
|
+
// Calculate the scroll position
|
|
3072
|
+
scrollTop = (isWindowSpy ? $window.pageYOffset : scrollEl.prop('scrollTop')) || 0;
|
|
3073
|
+
|
|
3074
|
+
// Calculate the viewport height for use by the components
|
|
3075
|
+
viewportHeight = Math.max($window.innerHeight, docEl.prop('clientHeight'));
|
|
3076
|
+
|
|
3077
|
+
// Activate first element if scroll is smaller
|
|
3078
|
+
if(scrollTop < sortedElements[0].offsetTop && activeTarget !== sortedElements[0].target) {
|
|
3079
|
+
return $scrollspy.$activateElement(sortedElements[0]);
|
|
2945
3080
|
}
|
|
2946
|
-
|
|
3081
|
+
|
|
3082
|
+
// Activate proper element
|
|
3083
|
+
for (var i = sortedElements.length; i--;) {
|
|
3084
|
+
if(angular.isUndefined(sortedElements[i].offsetTop) || sortedElements[i].offsetTop === null) continue;
|
|
3085
|
+
if(activeTarget === sortedElements[i].target) continue;
|
|
3086
|
+
if(scrollTop < sortedElements[i].offsetTop) continue;
|
|
3087
|
+
if(sortedElements[i + 1] && scrollTop > sortedElements[i + 1].offsetTop) continue;
|
|
3088
|
+
return $scrollspy.$activateElement(sortedElements[i]);
|
|
3089
|
+
}
|
|
3090
|
+
|
|
2947
3091
|
};
|
|
2948
3092
|
|
|
2949
|
-
$
|
|
2950
|
-
|
|
2951
|
-
|
|
2952
|
-
|
|
2953
|
-
if(options.multiple) {
|
|
2954
|
-
controller.$setViewValue(scope.$activeIndex.map(function(index) {
|
|
2955
|
-
return scope.$matches[index].value;
|
|
2956
|
-
}));
|
|
2957
|
-
} else {
|
|
2958
|
-
controller.$setViewValue(value);
|
|
2959
|
-
// Hide if single select
|
|
2960
|
-
$select.hide();
|
|
2961
|
-
}
|
|
2962
|
-
});
|
|
2963
|
-
// Emit event
|
|
2964
|
-
scope.$emit(options.prefixEvent + '.select', value, index, $select);
|
|
3093
|
+
$scrollspy.checkPositionWithEventLoop = function() {
|
|
3094
|
+
// IE 9 throws an error if we use 'this' instead of '$scrollspy'
|
|
3095
|
+
// in this setTimeout call
|
|
3096
|
+
setTimeout($scrollspy.checkPosition, 1);
|
|
2965
3097
|
};
|
|
2966
3098
|
|
|
2967
3099
|
// Protected methods
|
|
2968
3100
|
|
|
2969
|
-
$
|
|
2970
|
-
if(
|
|
2971
|
-
|
|
2972
|
-
|
|
2973
|
-
|
|
2974
|
-
|
|
2975
|
-
|
|
2976
|
-
|
|
3101
|
+
$scrollspy.$activateElement = function(element) {
|
|
3102
|
+
if(activeTarget) {
|
|
3103
|
+
var activeElement = $scrollspy.$getTrackedElement(activeTarget);
|
|
3104
|
+
if(activeElement) {
|
|
3105
|
+
activeElement.source.removeClass('active');
|
|
3106
|
+
if(nodeName(activeElement.source, 'li') && nodeName(activeElement.source.parent().parent(), 'li')) {
|
|
3107
|
+
activeElement.source.parent().parent().removeClass('active');
|
|
3108
|
+
}
|
|
2977
3109
|
}
|
|
2978
|
-
}
|
|
2979
|
-
|
|
3110
|
+
}
|
|
3111
|
+
activeTarget = element.target;
|
|
3112
|
+
element.source.addClass('active');
|
|
3113
|
+
if(nodeName(element.source, 'li') && nodeName(element.source.parent().parent(), 'li')) {
|
|
3114
|
+
element.source.parent().parent().addClass('active');
|
|
2980
3115
|
}
|
|
2981
3116
|
};
|
|
2982
3117
|
|
|
2983
|
-
$
|
|
2984
|
-
|
|
2985
|
-
return
|
|
2986
|
-
}
|
|
2987
|
-
// minLength support
|
|
2988
|
-
return scope.$matches.length && controller.$viewValue.length >= options.minLength;
|
|
3118
|
+
$scrollspy.$getTrackedElement = function(target) {
|
|
3119
|
+
return trackedElements.filter(function(obj) {
|
|
3120
|
+
return obj.target === target;
|
|
3121
|
+
})[0];
|
|
2989
3122
|
};
|
|
2990
3123
|
|
|
2991
|
-
|
|
2992
|
-
|
|
2993
|
-
|
|
2994
|
-
|
|
2995
|
-
|
|
2996
|
-
|
|
3124
|
+
// Track offsets behavior
|
|
3125
|
+
|
|
3126
|
+
$scrollspy.checkOffsets = function() {
|
|
3127
|
+
|
|
3128
|
+
angular.forEach(trackedElements, function(trackedElement) {
|
|
3129
|
+
var targetElement = document.querySelector(trackedElement.target);
|
|
3130
|
+
trackedElement.offsetTop = targetElement ? dimensions.offset(targetElement).top : null;
|
|
3131
|
+
if(options.offset && trackedElement.offsetTop !== null) trackedElement.offsetTop -= options.offset * 1;
|
|
3132
|
+
});
|
|
3133
|
+
|
|
3134
|
+
sortedElements = trackedElements
|
|
3135
|
+
.filter(function(el) {
|
|
3136
|
+
return el.offsetTop !== null;
|
|
3137
|
+
})
|
|
3138
|
+
.sort(function(a, b) {
|
|
3139
|
+
return a.offsetTop - b.offsetTop;
|
|
3140
|
+
});
|
|
3141
|
+
|
|
3142
|
+
debouncedCheckPosition();
|
|
3143
|
+
|
|
2997
3144
|
};
|
|
2998
3145
|
|
|
2999
|
-
$
|
|
3000
|
-
|
|
3001
|
-
if(!l) return;
|
|
3002
|
-
for(i = l; i--;) {
|
|
3003
|
-
if(scope.$matches[i].value === value) break;
|
|
3004
|
-
}
|
|
3005
|
-
if(i < 0) return;
|
|
3006
|
-
return i;
|
|
3146
|
+
$scrollspy.trackElement = function(target, source) {
|
|
3147
|
+
trackedElements.push({target: target, source: source});
|
|
3007
3148
|
};
|
|
3008
3149
|
|
|
3009
|
-
$
|
|
3010
|
-
|
|
3011
|
-
|
|
3012
|
-
|
|
3013
|
-
|
|
3014
|
-
|
|
3015
|
-
|
|
3016
|
-
targetEl.triggerHandler('click');
|
|
3150
|
+
$scrollspy.untrackElement = function(target, source) {
|
|
3151
|
+
var toDelete;
|
|
3152
|
+
for (var i = trackedElements.length; i--;) {
|
|
3153
|
+
if(trackedElements[i].target === target && trackedElements[i].source === source) {
|
|
3154
|
+
toDelete = i;
|
|
3155
|
+
break;
|
|
3156
|
+
}
|
|
3017
3157
|
}
|
|
3158
|
+
trackedElements = trackedElements.splice(toDelete, 1);
|
|
3018
3159
|
};
|
|
3019
3160
|
|
|
3020
|
-
$
|
|
3021
|
-
|
|
3022
|
-
|
|
3023
|
-
evt.stopPropagation();
|
|
3161
|
+
$scrollspy.activate = function(i) {
|
|
3162
|
+
trackedElements[i].addClass('active');
|
|
3163
|
+
};
|
|
3024
3164
|
|
|
3025
|
-
|
|
3026
|
-
if(!options.multiple && (evt.keyCode === 13 || evt.keyCode === 9)) {
|
|
3027
|
-
return $select.select(scope.$activeIndex);
|
|
3028
|
-
}
|
|
3165
|
+
// Initialize plugin
|
|
3029
3166
|
|
|
3030
|
-
|
|
3031
|
-
|
|
3032
|
-
else if(evt.keyCode === 40 && scope.$activeIndex < scope.$matches.length - 1) scope.$activeIndex++;
|
|
3033
|
-
else if(angular.isUndefined(scope.$activeIndex)) scope.$activeIndex = 0;
|
|
3034
|
-
scope.$digest();
|
|
3035
|
-
};
|
|
3167
|
+
$scrollspy.init();
|
|
3168
|
+
return $scrollspy;
|
|
3036
3169
|
|
|
3037
|
-
|
|
3170
|
+
}
|
|
3038
3171
|
|
|
3039
|
-
|
|
3040
|
-
|
|
3041
|
-
|
|
3172
|
+
return ScrollSpyFactory;
|
|
3173
|
+
|
|
3174
|
+
}];
|
|
3175
|
+
|
|
3176
|
+
})
|
|
3177
|
+
|
|
3178
|
+
.directive('bsScrollspy', ["$rootScope", "debounce", "dimensions", "$scrollspy", function($rootScope, debounce, dimensions, $scrollspy) {
|
|
3179
|
+
|
|
3180
|
+
return {
|
|
3181
|
+
restrict: 'EAC',
|
|
3182
|
+
link: function postLink(scope, element, attr) {
|
|
3183
|
+
|
|
3184
|
+
var options = {scope: scope};
|
|
3185
|
+
angular.forEach(['offset', 'target'], function(key) {
|
|
3186
|
+
if(angular.isDefined(attr[key])) options[key] = attr[key];
|
|
3187
|
+
});
|
|
3188
|
+
|
|
3189
|
+
var scrollspy = $scrollspy(options);
|
|
3190
|
+
scrollspy.trackElement(options.target, element);
|
|
3191
|
+
|
|
3192
|
+
scope.$on('$destroy', function() {
|
|
3193
|
+
if (scrollspy) {
|
|
3194
|
+
scrollspy.untrackElement(options.target, element);
|
|
3195
|
+
scrollspy.destroy();
|
|
3196
|
+
}
|
|
3197
|
+
options = null;
|
|
3198
|
+
scrollspy = null;
|
|
3199
|
+
});
|
|
3200
|
+
|
|
3201
|
+
}
|
|
3202
|
+
};
|
|
3203
|
+
|
|
3204
|
+
}])
|
|
3205
|
+
|
|
3206
|
+
|
|
3207
|
+
.directive('bsScrollspyList', ["$rootScope", "debounce", "dimensions", "$scrollspy", function($rootScope, debounce, dimensions, $scrollspy) {
|
|
3208
|
+
|
|
3209
|
+
return {
|
|
3210
|
+
restrict: 'A',
|
|
3211
|
+
compile: function postLink(element, attr) {
|
|
3212
|
+
var children = element[0].querySelectorAll('li > a[href]');
|
|
3213
|
+
angular.forEach(children, function(child) {
|
|
3214
|
+
var childEl = angular.element(child);
|
|
3215
|
+
childEl.parent().attr('bs-scrollspy', '').attr('data-target', childEl.attr('href'));
|
|
3216
|
+
});
|
|
3217
|
+
}
|
|
3218
|
+
|
|
3219
|
+
};
|
|
3220
|
+
|
|
3221
|
+
}]);
|
|
3222
|
+
|
|
3223
|
+
// Source: select.js
|
|
3224
|
+
angular.module('mgcrea.ngStrap.select', ['mgcrea.ngStrap.tooltip', 'mgcrea.ngStrap.helpers.parseOptions'])
|
|
3225
|
+
|
|
3226
|
+
.provider('$select', function() {
|
|
3227
|
+
|
|
3228
|
+
var defaults = this.defaults = {
|
|
3229
|
+
animation: 'am-fade',
|
|
3230
|
+
prefixClass: 'select',
|
|
3231
|
+
prefixEvent: '$select',
|
|
3232
|
+
placement: 'bottom-left',
|
|
3233
|
+
template: 'select/select.tpl.html',
|
|
3234
|
+
trigger: 'focus',
|
|
3235
|
+
container: false,
|
|
3236
|
+
keyboard: true,
|
|
3237
|
+
html: false,
|
|
3238
|
+
delay: 0,
|
|
3239
|
+
multiple: false,
|
|
3240
|
+
allNoneButtons: false,
|
|
3241
|
+
sort: true,
|
|
3242
|
+
caretHtml: ' <span class="caret"></span>',
|
|
3243
|
+
placeholder: 'Choose among the following...',
|
|
3244
|
+
allText: 'All',
|
|
3245
|
+
noneText: 'None',
|
|
3246
|
+
maxLength: 3,
|
|
3247
|
+
maxLengthHtml: 'selected',
|
|
3248
|
+
iconCheckmark: 'glyphicon glyphicon-ok'
|
|
3249
|
+
};
|
|
3250
|
+
|
|
3251
|
+
this.$get = ["$window", "$document", "$rootScope", "$tooltip", "$timeout", function($window, $document, $rootScope, $tooltip, $timeout) {
|
|
3252
|
+
|
|
3253
|
+
var bodyEl = angular.element($window.document.body);
|
|
3254
|
+
var isNative = /(ip(a|o)d|iphone|android)/ig.test($window.navigator.userAgent);
|
|
3255
|
+
var isTouch = ('createTouch' in $window.document) && isNative;
|
|
3256
|
+
|
|
3257
|
+
function SelectFactory(element, controller, config) {
|
|
3258
|
+
|
|
3259
|
+
var $select = {};
|
|
3260
|
+
|
|
3261
|
+
// Common vars
|
|
3262
|
+
var options = angular.extend({}, defaults, config);
|
|
3263
|
+
|
|
3264
|
+
// parse sort option value to support attribute as string
|
|
3265
|
+
// when binded to interpolated value
|
|
3266
|
+
options.sort = options.sort.toString().match(/true|1/i);
|
|
3267
|
+
|
|
3268
|
+
$select = $tooltip(element, options);
|
|
3269
|
+
var scope = $select.$scope;
|
|
3270
|
+
|
|
3271
|
+
scope.$matches = [];
|
|
3272
|
+
scope.$activeIndex = -1;
|
|
3273
|
+
scope.$isMultiple = options.multiple;
|
|
3274
|
+
scope.$showAllNoneButtons = options.allNoneButtons && options.multiple;
|
|
3275
|
+
scope.$iconCheckmark = options.iconCheckmark;
|
|
3276
|
+
scope.$allText = options.allText;
|
|
3277
|
+
scope.$noneText = options.noneText;
|
|
3278
|
+
|
|
3279
|
+
scope.$activate = function(index) {
|
|
3280
|
+
scope.$$postDigest(function() {
|
|
3281
|
+
$select.activate(index);
|
|
3282
|
+
});
|
|
3283
|
+
};
|
|
3284
|
+
|
|
3285
|
+
scope.$select = function(index, evt) {
|
|
3286
|
+
scope.$$postDigest(function() {
|
|
3287
|
+
$select.select(index);
|
|
3288
|
+
});
|
|
3289
|
+
};
|
|
3290
|
+
|
|
3291
|
+
scope.$isVisible = function() {
|
|
3292
|
+
return $select.$isVisible();
|
|
3293
|
+
};
|
|
3294
|
+
|
|
3295
|
+
scope.$isActive = function(index) {
|
|
3296
|
+
return $select.$isActive(index);
|
|
3297
|
+
};
|
|
3298
|
+
|
|
3299
|
+
scope.$selectAll = function () {
|
|
3300
|
+
for (var i = 0; i < scope.$matches.length; i++) {
|
|
3301
|
+
if (!scope.$isActive(i)) {
|
|
3302
|
+
scope.$select(i);
|
|
3303
|
+
}
|
|
3304
|
+
}
|
|
3305
|
+
};
|
|
3306
|
+
|
|
3307
|
+
scope.$selectNone = function () {
|
|
3308
|
+
for (var i = 0; i < scope.$matches.length; i++) {
|
|
3309
|
+
if (scope.$isActive(i)) {
|
|
3310
|
+
scope.$select(i);
|
|
3311
|
+
}
|
|
3312
|
+
}
|
|
3313
|
+
};
|
|
3314
|
+
|
|
3315
|
+
// Public methods
|
|
3316
|
+
|
|
3317
|
+
$select.update = function(matches) {
|
|
3318
|
+
scope.$matches = matches;
|
|
3319
|
+
$select.$updateActiveIndex();
|
|
3320
|
+
};
|
|
3321
|
+
|
|
3322
|
+
$select.activate = function(index) {
|
|
3323
|
+
if(options.multiple) {
|
|
3324
|
+
$select.$isActive(index) ? scope.$activeIndex.splice(scope.$activeIndex.indexOf(index), 1) : scope.$activeIndex.push(index);
|
|
3325
|
+
if(options.sort) scope.$activeIndex.sort();
|
|
3326
|
+
} else {
|
|
3327
|
+
scope.$activeIndex = index;
|
|
3328
|
+
}
|
|
3329
|
+
return scope.$activeIndex;
|
|
3330
|
+
};
|
|
3331
|
+
|
|
3332
|
+
$select.select = function(index) {
|
|
3333
|
+
var value = scope.$matches[index].value;
|
|
3334
|
+
scope.$apply(function() {
|
|
3335
|
+
$select.activate(index);
|
|
3336
|
+
if(options.multiple) {
|
|
3337
|
+
controller.$setViewValue(scope.$activeIndex.map(function(index) {
|
|
3338
|
+
return scope.$matches[index].value;
|
|
3339
|
+
}));
|
|
3340
|
+
} else {
|
|
3341
|
+
controller.$setViewValue(value);
|
|
3342
|
+
// Hide if single select
|
|
3343
|
+
$select.hide();
|
|
3344
|
+
}
|
|
3345
|
+
});
|
|
3346
|
+
// Emit event
|
|
3347
|
+
scope.$emit(options.prefixEvent + '.select', value, index, $select);
|
|
3348
|
+
};
|
|
3349
|
+
|
|
3350
|
+
// Protected methods
|
|
3351
|
+
|
|
3352
|
+
$select.$updateActiveIndex = function() {
|
|
3353
|
+
if(controller.$modelValue && scope.$matches.length) {
|
|
3354
|
+
if(options.multiple && angular.isArray(controller.$modelValue)) {
|
|
3355
|
+
scope.$activeIndex = controller.$modelValue.map(function(value) {
|
|
3356
|
+
return $select.$getIndex(value);
|
|
3357
|
+
});
|
|
3358
|
+
} else {
|
|
3359
|
+
scope.$activeIndex = $select.$getIndex(controller.$modelValue);
|
|
3360
|
+
}
|
|
3361
|
+
} else if(scope.$activeIndex >= scope.$matches.length) {
|
|
3362
|
+
scope.$activeIndex = options.multiple ? [] : 0;
|
|
3363
|
+
}
|
|
3364
|
+
};
|
|
3365
|
+
|
|
3366
|
+
$select.$isVisible = function() {
|
|
3367
|
+
if(!options.minLength || !controller) {
|
|
3368
|
+
return scope.$matches.length;
|
|
3369
|
+
}
|
|
3370
|
+
// minLength support
|
|
3371
|
+
return scope.$matches.length && controller.$viewValue.length >= options.minLength;
|
|
3372
|
+
};
|
|
3373
|
+
|
|
3374
|
+
$select.$isActive = function(index) {
|
|
3375
|
+
if(options.multiple) {
|
|
3376
|
+
return scope.$activeIndex.indexOf(index) !== -1;
|
|
3377
|
+
} else {
|
|
3378
|
+
return scope.$activeIndex === index;
|
|
3379
|
+
}
|
|
3380
|
+
};
|
|
3381
|
+
|
|
3382
|
+
$select.$getIndex = function(value) {
|
|
3383
|
+
var l = scope.$matches.length, i = l;
|
|
3384
|
+
if(!l) return;
|
|
3385
|
+
for(i = l; i--;) {
|
|
3386
|
+
if(scope.$matches[i].value === value) break;
|
|
3387
|
+
}
|
|
3388
|
+
if(i < 0) return;
|
|
3389
|
+
return i;
|
|
3390
|
+
};
|
|
3391
|
+
|
|
3392
|
+
$select.$onMouseDown = function(evt) {
|
|
3393
|
+
// Prevent blur on mousedown on .dropdown-menu
|
|
3394
|
+
evt.preventDefault();
|
|
3395
|
+
evt.stopPropagation();
|
|
3396
|
+
// Emulate click for mobile devices
|
|
3397
|
+
if(isTouch) {
|
|
3398
|
+
var targetEl = angular.element(evt.target);
|
|
3399
|
+
targetEl.triggerHandler('click');
|
|
3400
|
+
}
|
|
3401
|
+
};
|
|
3402
|
+
|
|
3403
|
+
$select.$onKeyDown = function(evt) {
|
|
3404
|
+
if (!/(9|13|38|40)/.test(evt.keyCode)) return;
|
|
3405
|
+
evt.preventDefault();
|
|
3406
|
+
evt.stopPropagation();
|
|
3407
|
+
|
|
3408
|
+
// release focus on tab
|
|
3409
|
+
if (options.multiple && evt.keyCode === 9) {
|
|
3410
|
+
return $select.hide();
|
|
3411
|
+
}
|
|
3412
|
+
|
|
3413
|
+
// Select with enter
|
|
3414
|
+
if(!options.multiple && (evt.keyCode === 13 || evt.keyCode === 9)) {
|
|
3415
|
+
return $select.select(scope.$activeIndex);
|
|
3416
|
+
}
|
|
3417
|
+
|
|
3418
|
+
if (!options.multiple) {
|
|
3419
|
+
// Navigate with keyboard
|
|
3420
|
+
if(evt.keyCode === 38 && scope.$activeIndex > 0) scope.$activeIndex--;
|
|
3421
|
+
else if(evt.keyCode === 38 && scope.$activeIndex < 0) scope.$activeIndex = scope.$matches.length - 1;
|
|
3422
|
+
else if(evt.keyCode === 40 && scope.$activeIndex < scope.$matches.length - 1) scope.$activeIndex++;
|
|
3423
|
+
else if(angular.isUndefined(scope.$activeIndex)) scope.$activeIndex = 0;
|
|
3424
|
+
scope.$digest();
|
|
3425
|
+
}
|
|
3426
|
+
};
|
|
3427
|
+
|
|
3428
|
+
// Overrides
|
|
3429
|
+
|
|
3430
|
+
var _show = $select.show;
|
|
3431
|
+
$select.show = function() {
|
|
3432
|
+
_show();
|
|
3042
3433
|
if(options.multiple) {
|
|
3043
3434
|
$select.$element.addClass('select-multiple');
|
|
3044
3435
|
}
|
|
@@ -3054,6 +3445,9 @@ angular.module('mgcrea.ngStrap.select', ['mgcrea.ngStrap.tooltip', 'mgcrea.ngStr
|
|
|
3054
3445
|
|
|
3055
3446
|
var _hide = $select.hide;
|
|
3056
3447
|
$select.hide = function() {
|
|
3448
|
+
if(!options.multiple && !controller.$modelValue) {
|
|
3449
|
+
scope.$activeIndex = -1;
|
|
3450
|
+
}
|
|
3057
3451
|
$select.$element.off(isTouch ? 'touchstart' : 'mousedown', $select.$onMouseDown);
|
|
3058
3452
|
if(options.keyboard) {
|
|
3059
3453
|
element.off('keydown', $select.$onKeyDown);
|
|
@@ -3083,7 +3477,7 @@ angular.module('mgcrea.ngStrap.select', ['mgcrea.ngStrap.tooltip', 'mgcrea.ngStr
|
|
|
3083
3477
|
|
|
3084
3478
|
// Directive options
|
|
3085
3479
|
var options = {scope: scope, placeholder: defaults.placeholder};
|
|
3086
|
-
angular.forEach(['placement', 'container', 'delay', 'trigger', 'keyboard', 'html', 'animation', 'template', 'placeholder', 'multiple', 'allNoneButtons', 'maxLength', 'maxLengthHtml', 'allText', 'noneText', 'iconCheckmark', 'autoClose', 'id'], function(key) {
|
|
3480
|
+
angular.forEach(['placement', 'container', 'delay', 'trigger', 'keyboard', 'html', 'animation', 'template', 'placeholder', 'multiple', 'allNoneButtons', 'maxLength', 'maxLengthHtml', 'allText', 'noneText', 'iconCheckmark', 'autoClose', 'id', 'sort', 'caretHtml'], function(key) {
|
|
3087
3481
|
if(angular.isDefined(attr[key])) options[key] = attr[key];
|
|
3088
3482
|
});
|
|
3089
3483
|
|
|
@@ -3095,13 +3489,13 @@ angular.module('mgcrea.ngStrap.select', ['mgcrea.ngStrap.tooltip', 'mgcrea.ngStr
|
|
|
3095
3489
|
inputEl.after(element);
|
|
3096
3490
|
}
|
|
3097
3491
|
|
|
3098
|
-
// Build proper
|
|
3099
|
-
var parsedOptions = $parseOptions(attr.
|
|
3492
|
+
// Build proper bsOptions
|
|
3493
|
+
var parsedOptions = $parseOptions(attr.bsOptions);
|
|
3100
3494
|
|
|
3101
3495
|
// Initialize select
|
|
3102
3496
|
var select = $select(element, controller, options);
|
|
3103
3497
|
|
|
3104
|
-
// Watch
|
|
3498
|
+
// Watch bsOptions values before filtering for changes
|
|
3105
3499
|
var watchedOptions = parsedOptions.$match[7].replace(/\|.+/, '').trim();
|
|
3106
3500
|
scope.$watch(watchedOptions, function(newValue, oldValue) {
|
|
3107
3501
|
// console.warn('scope.$watch(%s)', watchedOptions, newValue, oldValue);
|
|
@@ -3137,7 +3531,7 @@ angular.module('mgcrea.ngStrap.select', ['mgcrea.ngStrap.tooltip', 'mgcrea.ngStr
|
|
|
3137
3531
|
index = select.$getIndex(controller.$modelValue);
|
|
3138
3532
|
selected = angular.isDefined(index) ? select.$scope.$matches[index].label : false;
|
|
3139
3533
|
}
|
|
3140
|
-
element.html((selected ? selected : options.placeholder) + defaults.caretHtml);
|
|
3534
|
+
element.html((selected ? selected : options.placeholder) + (options.caretHtml ? options.caretHtml : defaults.caretHtml));
|
|
3141
3535
|
};
|
|
3142
3536
|
|
|
3143
3537
|
if(options.multiple){
|
|
@@ -3313,6 +3707,10 @@ angular.module('mgcrea.ngStrap.tab', [])
|
|
|
3313
3707
|
element.addClass(bsTabsCtrl.$options.animation);
|
|
3314
3708
|
}
|
|
3315
3709
|
|
|
3710
|
+
attrs.$observe('disabled', function(newValue, oldValue) {
|
|
3711
|
+
scope.disabled = scope.$eval(newValue);
|
|
3712
|
+
});
|
|
3713
|
+
|
|
3316
3714
|
// Push pane to parent bsTabs controller
|
|
3317
3715
|
bsTabsCtrl.$push(scope);
|
|
3318
3716
|
|
|
@@ -3337,599 +3735,684 @@ angular.module('mgcrea.ngStrap.tab', [])
|
|
|
3337
3735
|
|
|
3338
3736
|
}]);
|
|
3339
3737
|
|
|
3340
|
-
// Source:
|
|
3341
|
-
angular.module('mgcrea.ngStrap.
|
|
3738
|
+
// Source: timepicker.js
|
|
3739
|
+
angular.module('mgcrea.ngStrap.timepicker', [
|
|
3740
|
+
'mgcrea.ngStrap.helpers.dateParser',
|
|
3741
|
+
'mgcrea.ngStrap.helpers.dateFormatter',
|
|
3742
|
+
'mgcrea.ngStrap.tooltip'])
|
|
3342
3743
|
|
|
3343
|
-
.provider('$
|
|
3344
|
-
|
|
3345
|
-
// Pool of registered spies
|
|
3346
|
-
var spies = this.$$spies = {};
|
|
3744
|
+
.provider('$timepicker', function() {
|
|
3347
3745
|
|
|
3348
3746
|
var defaults = this.defaults = {
|
|
3349
|
-
|
|
3350
|
-
|
|
3351
|
-
|
|
3747
|
+
animation: 'am-fade',
|
|
3748
|
+
prefixClass: 'timepicker',
|
|
3749
|
+
placement: 'bottom-left',
|
|
3750
|
+
template: 'timepicker/timepicker.tpl.html',
|
|
3751
|
+
trigger: 'focus',
|
|
3752
|
+
container: false,
|
|
3753
|
+
keyboard: true,
|
|
3754
|
+
html: false,
|
|
3755
|
+
delay: 0,
|
|
3756
|
+
// lang: $locale.id,
|
|
3757
|
+
useNative: true,
|
|
3758
|
+
timeType: 'date',
|
|
3759
|
+
timeFormat: 'shortTime',
|
|
3760
|
+
timezone: null,
|
|
3761
|
+
modelTimeFormat: null,
|
|
3762
|
+
autoclose: false,
|
|
3763
|
+
minTime: -Infinity,
|
|
3764
|
+
maxTime: +Infinity,
|
|
3765
|
+
length: 5,
|
|
3766
|
+
hourStep: 1,
|
|
3767
|
+
minuteStep: 5,
|
|
3768
|
+
roundDisplay: false,
|
|
3769
|
+
iconUp: 'glyphicon glyphicon-chevron-up',
|
|
3770
|
+
iconDown: 'glyphicon glyphicon-chevron-down',
|
|
3771
|
+
arrowBehavior: 'pager'
|
|
3352
3772
|
};
|
|
3353
3773
|
|
|
3354
|
-
this.$get = ["$window", "$document", "$rootScope", "
|
|
3774
|
+
this.$get = ["$window", "$document", "$rootScope", "$sce", "$dateFormatter", "$tooltip", "$timeout", function($window, $document, $rootScope, $sce, $dateFormatter, $tooltip, $timeout) {
|
|
3355
3775
|
|
|
3356
|
-
var windowEl = angular.element($window);
|
|
3357
|
-
var docEl = angular.element($document.prop('documentElement'));
|
|
3358
3776
|
var bodyEl = angular.element($window.document.body);
|
|
3777
|
+
var isNative = /(ip(a|o)d|iphone|android)/ig.test($window.navigator.userAgent);
|
|
3778
|
+
var isTouch = ('createTouch' in $window.document) && isNative;
|
|
3779
|
+
if(!defaults.lang) defaults.lang = $dateFormatter.getDefaultLocale();
|
|
3359
3780
|
|
|
3360
|
-
|
|
3361
|
-
|
|
3362
|
-
function nodeName(element, name) {
|
|
3363
|
-
return element[0].nodeName && element[0].nodeName.toLowerCase() === name.toLowerCase();
|
|
3364
|
-
}
|
|
3781
|
+
function timepickerFactory(element, controller, config) {
|
|
3365
3782
|
|
|
3366
|
-
|
|
3783
|
+
var $timepicker = $tooltip(element, angular.extend({}, defaults, config));
|
|
3784
|
+
var parentScope = config.scope;
|
|
3785
|
+
var options = $timepicker.$options;
|
|
3786
|
+
var scope = $timepicker.$scope;
|
|
3367
3787
|
|
|
3368
|
-
|
|
3369
|
-
var
|
|
3370
|
-
|
|
3371
|
-
|
|
3372
|
-
var scrollEl = isWindowSpy ? windowEl : options.element;
|
|
3373
|
-
var scrollId = isWindowSpy ? 'window' : options.id;
|
|
3788
|
+
var lang = options.lang;
|
|
3789
|
+
var formatDate = function(date, format, timezone) {
|
|
3790
|
+
return $dateFormatter.formatDate(date, format, lang, timezone);
|
|
3791
|
+
};
|
|
3374
3792
|
|
|
3375
|
-
|
|
3376
|
-
|
|
3377
|
-
|
|
3378
|
-
|
|
3793
|
+
function floorMinutes(time)
|
|
3794
|
+
{
|
|
3795
|
+
// coeff used to floor current time to nearest minuteStep interval
|
|
3796
|
+
var coeff = 1000 * 60 * options.minuteStep;
|
|
3797
|
+
return new Date(Math.floor(time.getTime() / coeff) * coeff);
|
|
3379
3798
|
}
|
|
3380
3799
|
|
|
3381
|
-
|
|
3382
|
-
|
|
3383
|
-
// Private vars
|
|
3384
|
-
var unbindViewContentLoaded, unbindIncludeContentLoaded;
|
|
3385
|
-
var trackedElements = $scrollspy.$trackedElements = [];
|
|
3386
|
-
var sortedElements = [];
|
|
3387
|
-
var activeTarget;
|
|
3388
|
-
var debouncedCheckPosition;
|
|
3389
|
-
var throttledCheckPosition;
|
|
3390
|
-
var debouncedCheckOffsets;
|
|
3391
|
-
var viewportHeight;
|
|
3392
|
-
var scrollTop;
|
|
3800
|
+
// View vars
|
|
3393
3801
|
|
|
3394
|
-
|
|
3802
|
+
var selectedIndex = 0;
|
|
3803
|
+
var defaultDate = options.roundDisplay ? floorMinutes(new Date()) : new Date();
|
|
3804
|
+
var startDate = controller.$dateValue || defaultDate;
|
|
3805
|
+
var viewDate = {hour: startDate.getHours(), meridian: startDate.getHours() < 12, minute: startDate.getMinutes(), second: startDate.getSeconds(), millisecond: startDate.getMilliseconds()};
|
|
3395
3806
|
|
|
3396
|
-
|
|
3397
|
-
this.$$count = 1;
|
|
3807
|
+
var format = $dateFormatter.getDatetimeFormat(options.timeFormat, lang);
|
|
3398
3808
|
|
|
3399
|
-
|
|
3400
|
-
|
|
3401
|
-
|
|
3402
|
-
|
|
3403
|
-
windowEl.on('resize', debouncedCheckPosition);
|
|
3404
|
-
scrollEl.on('scroll', throttledCheckPosition);
|
|
3809
|
+
var hoursFormat = $dateFormatter.hoursFormat(format),
|
|
3810
|
+
timeSeparator = $dateFormatter.timeSeparator(format),
|
|
3811
|
+
minutesFormat = $dateFormatter.minutesFormat(format),
|
|
3812
|
+
showAM = $dateFormatter.showAM(format);
|
|
3405
3813
|
|
|
3406
|
-
|
|
3407
|
-
|
|
3408
|
-
unbindIncludeContentLoaded = $rootScope.$on('$includeContentLoaded', debouncedCheckOffsets);
|
|
3409
|
-
debouncedCheckOffsets();
|
|
3814
|
+
scope.$iconUp = options.iconUp;
|
|
3815
|
+
scope.$iconDown = options.iconDown;
|
|
3410
3816
|
|
|
3411
|
-
|
|
3412
|
-
if(scrollId) {
|
|
3413
|
-
spies[scrollId] = $scrollspy;
|
|
3414
|
-
}
|
|
3817
|
+
// Scope methods
|
|
3415
3818
|
|
|
3819
|
+
scope.$select = function(date, index) {
|
|
3820
|
+
$timepicker.select(date, index);
|
|
3821
|
+
};
|
|
3822
|
+
scope.$moveIndex = function(value, index) {
|
|
3823
|
+
$timepicker.$moveIndex(value, index);
|
|
3824
|
+
};
|
|
3825
|
+
scope.$switchMeridian = function(date) {
|
|
3826
|
+
$timepicker.switchMeridian(date);
|
|
3416
3827
|
};
|
|
3417
3828
|
|
|
3418
|
-
|
|
3829
|
+
// Public methods
|
|
3419
3830
|
|
|
3420
|
-
|
|
3421
|
-
|
|
3422
|
-
if(
|
|
3423
|
-
|
|
3831
|
+
$timepicker.update = function(date) {
|
|
3832
|
+
// console.warn('$timepicker.update() newValue=%o', date);
|
|
3833
|
+
if(angular.isDate(date) && !isNaN(date.getTime())) {
|
|
3834
|
+
$timepicker.$date = date;
|
|
3835
|
+
angular.extend(viewDate, {hour: date.getHours(), minute: date.getMinutes(), second: date.getSeconds(), millisecond: date.getMilliseconds()});
|
|
3836
|
+
$timepicker.$build();
|
|
3837
|
+
} else if(!$timepicker.$isBuilt) {
|
|
3838
|
+
$timepicker.$build();
|
|
3424
3839
|
}
|
|
3840
|
+
};
|
|
3425
3841
|
|
|
3426
|
-
|
|
3427
|
-
|
|
3428
|
-
|
|
3429
|
-
|
|
3430
|
-
|
|
3431
|
-
|
|
3432
|
-
|
|
3433
|
-
|
|
3842
|
+
$timepicker.select = function(date, index, keep) {
|
|
3843
|
+
// console.warn('$timepicker.select', date, scope.$mode);
|
|
3844
|
+
if(!controller.$dateValue || isNaN(controller.$dateValue.getTime())) controller.$dateValue = new Date(1970, 0, 1);
|
|
3845
|
+
if(!angular.isDate(date)) date = new Date(date);
|
|
3846
|
+
if(index === 0) controller.$dateValue.setHours(date.getHours());
|
|
3847
|
+
else if(index === 1) controller.$dateValue.setMinutes(date.getMinutes());
|
|
3848
|
+
controller.$setViewValue(angular.copy(controller.$dateValue));
|
|
3849
|
+
controller.$render();
|
|
3850
|
+
if(options.autoclose && !keep) {
|
|
3851
|
+
$timeout(function() { $timepicker.hide(true); });
|
|
3434
3852
|
}
|
|
3435
3853
|
};
|
|
3436
3854
|
|
|
3437
|
-
$
|
|
3438
|
-
|
|
3439
|
-
|
|
3440
|
-
|
|
3441
|
-
|
|
3442
|
-
|
|
3443
|
-
|
|
3855
|
+
$timepicker.switchMeridian = function(date) {
|
|
3856
|
+
if (!controller.$dateValue || isNaN(controller.$dateValue.getTime())) {
|
|
3857
|
+
return;
|
|
3858
|
+
}
|
|
3859
|
+
var hours = (date || controller.$dateValue).getHours();
|
|
3860
|
+
controller.$dateValue.setHours(hours < 12 ? hours + 12 : hours - 12);
|
|
3861
|
+
controller.$setViewValue(angular.copy(controller.$dateValue));
|
|
3862
|
+
controller.$render();
|
|
3863
|
+
};
|
|
3444
3864
|
|
|
3445
|
-
|
|
3446
|
-
viewportHeight = Math.max($window.innerHeight, docEl.prop('clientHeight'));
|
|
3865
|
+
// Protected methods
|
|
3447
3866
|
|
|
3448
|
-
|
|
3449
|
-
|
|
3450
|
-
|
|
3867
|
+
$timepicker.$build = function() {
|
|
3868
|
+
// console.warn('$timepicker.$build() viewDate=%o', viewDate);
|
|
3869
|
+
var i, midIndex = scope.midIndex = parseInt(options.length / 2, 10);
|
|
3870
|
+
var hours = [], hour;
|
|
3871
|
+
for(i = 0; i < options.length; i++) {
|
|
3872
|
+
hour = new Date(1970, 0, 1, viewDate.hour - (midIndex - i) * options.hourStep);
|
|
3873
|
+
hours.push({date: hour, label: formatDate(hour, hoursFormat), selected: $timepicker.$date && $timepicker.$isSelected(hour, 0), disabled: $timepicker.$isDisabled(hour, 0)});
|
|
3874
|
+
}
|
|
3875
|
+
var minutes = [], minute;
|
|
3876
|
+
for(i = 0; i < options.length; i++) {
|
|
3877
|
+
minute = new Date(1970, 0, 1, 0, viewDate.minute - (midIndex - i) * options.minuteStep);
|
|
3878
|
+
minutes.push({date: minute, label: formatDate(minute, minutesFormat), selected: $timepicker.$date && $timepicker.$isSelected(minute, 1), disabled: $timepicker.$isDisabled(minute, 1)});
|
|
3451
3879
|
}
|
|
3452
3880
|
|
|
3453
|
-
|
|
3454
|
-
for
|
|
3455
|
-
|
|
3456
|
-
if(activeTarget === sortedElements[i].target) continue;
|
|
3457
|
-
if(scrollTop < sortedElements[i].offsetTop) continue;
|
|
3458
|
-
if(sortedElements[i + 1] && scrollTop > sortedElements[i + 1].offsetTop) continue;
|
|
3459
|
-
return $scrollspy.$activateElement(sortedElements[i]);
|
|
3881
|
+
var rows = [];
|
|
3882
|
+
for(i = 0; i < options.length; i++) {
|
|
3883
|
+
rows.push([hours[i], minutes[i]]);
|
|
3460
3884
|
}
|
|
3885
|
+
scope.rows = rows;
|
|
3886
|
+
scope.showAM = showAM;
|
|
3887
|
+
scope.isAM = ($timepicker.$date || hours[midIndex].date).getHours() < 12;
|
|
3888
|
+
scope.timeSeparator = timeSeparator;
|
|
3889
|
+
$timepicker.$isBuilt = true;
|
|
3890
|
+
};
|
|
3461
3891
|
|
|
3892
|
+
$timepicker.$isSelected = function(date, index) {
|
|
3893
|
+
if(!$timepicker.$date) return false;
|
|
3894
|
+
else if(index === 0) {
|
|
3895
|
+
return date.getHours() === $timepicker.$date.getHours();
|
|
3896
|
+
} else if(index === 1) {
|
|
3897
|
+
return date.getMinutes() === $timepicker.$date.getMinutes();
|
|
3898
|
+
}
|
|
3462
3899
|
};
|
|
3463
3900
|
|
|
3464
|
-
$
|
|
3465
|
-
|
|
3466
|
-
|
|
3467
|
-
|
|
3901
|
+
$timepicker.$isDisabled = function(date, index) {
|
|
3902
|
+
var selectedTime;
|
|
3903
|
+
if(index === 0) {
|
|
3904
|
+
selectedTime = date.getTime() + viewDate.minute * 6e4;
|
|
3905
|
+
} else if(index === 1) {
|
|
3906
|
+
selectedTime = date.getTime() + viewDate.hour * 36e5;
|
|
3907
|
+
}
|
|
3908
|
+
return selectedTime < options.minTime * 1 || selectedTime > options.maxTime * 1;
|
|
3468
3909
|
};
|
|
3469
3910
|
|
|
3470
|
-
|
|
3911
|
+
scope.$arrowAction = function (value, index) {
|
|
3912
|
+
if (options.arrowBehavior === 'picker') {
|
|
3913
|
+
$timepicker.$setTimeByStep(value,index);
|
|
3914
|
+
} else {
|
|
3915
|
+
$timepicker.$moveIndex(value,index);
|
|
3916
|
+
}
|
|
3917
|
+
};
|
|
3471
3918
|
|
|
3472
|
-
$
|
|
3473
|
-
|
|
3474
|
-
|
|
3475
|
-
|
|
3476
|
-
|
|
3477
|
-
|
|
3478
|
-
activeElement.source.parent().parent().removeClass('active');
|
|
3479
|
-
}
|
|
3480
|
-
}
|
|
3919
|
+
$timepicker.$setTimeByStep = function(value, index) {
|
|
3920
|
+
var newDate = new Date($timepicker.$date);
|
|
3921
|
+
var hours = newDate.getHours(), hoursLength = formatDate(newDate, hoursFormat).length;
|
|
3922
|
+
var minutes = newDate.getMinutes(), minutesLength = formatDate(newDate, minutesFormat).length;
|
|
3923
|
+
if (index === 0) {
|
|
3924
|
+
newDate.setHours(hours - (parseInt(options.hourStep, 10) * value));
|
|
3481
3925
|
}
|
|
3482
|
-
|
|
3483
|
-
|
|
3484
|
-
if(nodeName(element.source, 'li') && nodeName(element.source.parent().parent(), 'li')) {
|
|
3485
|
-
element.source.parent().parent().addClass('active');
|
|
3926
|
+
else {
|
|
3927
|
+
newDate.setMinutes(minutes - (parseInt(options.minuteStep, 10) * value));
|
|
3486
3928
|
}
|
|
3929
|
+
$timepicker.select(newDate, index, true);
|
|
3487
3930
|
};
|
|
3488
3931
|
|
|
3489
|
-
$
|
|
3490
|
-
|
|
3491
|
-
|
|
3492
|
-
|
|
3932
|
+
$timepicker.$moveIndex = function(value, index) {
|
|
3933
|
+
var targetDate;
|
|
3934
|
+
if(index === 0) {
|
|
3935
|
+
targetDate = new Date(1970, 0, 1, viewDate.hour + (value * options.length), viewDate.minute);
|
|
3936
|
+
angular.extend(viewDate, {hour: targetDate.getHours()});
|
|
3937
|
+
} else if(index === 1) {
|
|
3938
|
+
targetDate = new Date(1970, 0, 1, viewDate.hour, viewDate.minute + (value * options.length * options.minuteStep));
|
|
3939
|
+
angular.extend(viewDate, {minute: targetDate.getMinutes()});
|
|
3940
|
+
}
|
|
3941
|
+
$timepicker.$build();
|
|
3493
3942
|
};
|
|
3494
3943
|
|
|
3495
|
-
|
|
3944
|
+
$timepicker.$onMouseDown = function(evt) {
|
|
3945
|
+
// Prevent blur on mousedown on .dropdown-menu
|
|
3946
|
+
if(evt.target.nodeName.toLowerCase() !== 'input') evt.preventDefault();
|
|
3947
|
+
evt.stopPropagation();
|
|
3948
|
+
// Emulate click for mobile devices
|
|
3949
|
+
if(isTouch) {
|
|
3950
|
+
var targetEl = angular.element(evt.target);
|
|
3951
|
+
if(targetEl[0].nodeName.toLowerCase() !== 'button') {
|
|
3952
|
+
targetEl = targetEl.parent();
|
|
3953
|
+
}
|
|
3954
|
+
targetEl.triggerHandler('click');
|
|
3955
|
+
}
|
|
3956
|
+
};
|
|
3496
3957
|
|
|
3497
|
-
$
|
|
3958
|
+
$timepicker.$onKeyDown = function(evt) {
|
|
3959
|
+
if (!/(38|37|39|40|13)/.test(evt.keyCode) || evt.shiftKey || evt.altKey) return;
|
|
3960
|
+
evt.preventDefault();
|
|
3961
|
+
evt.stopPropagation();
|
|
3498
3962
|
|
|
3499
|
-
|
|
3500
|
-
|
|
3501
|
-
trackedElement.offsetTop = targetElement ? dimensions.offset(targetElement).top : null;
|
|
3502
|
-
if(options.offset && trackedElement.offsetTop !== null) trackedElement.offsetTop -= options.offset * 1;
|
|
3503
|
-
});
|
|
3963
|
+
// Close on enter
|
|
3964
|
+
if(evt.keyCode === 13) return $timepicker.hide(true);
|
|
3504
3965
|
|
|
3505
|
-
|
|
3506
|
-
|
|
3507
|
-
|
|
3508
|
-
|
|
3509
|
-
|
|
3510
|
-
|
|
3511
|
-
});
|
|
3966
|
+
// Navigate with keyboard
|
|
3967
|
+
var newDate = new Date($timepicker.$date);
|
|
3968
|
+
var hours = newDate.getHours(), hoursLength = formatDate(newDate, hoursFormat).length;
|
|
3969
|
+
var minutes = newDate.getMinutes(), minutesLength = formatDate(newDate, minutesFormat).length;
|
|
3970
|
+
var lateralMove = /(37|39)/.test(evt.keyCode);
|
|
3971
|
+
var count = 2 + showAM * 1;
|
|
3512
3972
|
|
|
3513
|
-
|
|
3973
|
+
// Navigate indexes (left, right)
|
|
3974
|
+
if (lateralMove) {
|
|
3975
|
+
if(evt.keyCode === 37) selectedIndex = selectedIndex < 1 ? count - 1 : selectedIndex - 1;
|
|
3976
|
+
else if(evt.keyCode === 39) selectedIndex = selectedIndex < count - 1 ? selectedIndex + 1 : 0;
|
|
3977
|
+
}
|
|
3514
3978
|
|
|
3979
|
+
// Update values (up, down)
|
|
3980
|
+
var selectRange = [0, hoursLength];
|
|
3981
|
+
if(selectedIndex === 0) {
|
|
3982
|
+
if(evt.keyCode === 38) newDate.setHours(hours - parseInt(options.hourStep, 10));
|
|
3983
|
+
else if(evt.keyCode === 40) newDate.setHours(hours + parseInt(options.hourStep, 10));
|
|
3984
|
+
// re-calculate hours length because we have changed hours value
|
|
3985
|
+
hoursLength = formatDate(newDate, hoursFormat).length;
|
|
3986
|
+
selectRange = [0, hoursLength];
|
|
3987
|
+
} else if(selectedIndex === 1) {
|
|
3988
|
+
if(evt.keyCode === 38) newDate.setMinutes(minutes - parseInt(options.minuteStep, 10));
|
|
3989
|
+
else if(evt.keyCode === 40) newDate.setMinutes(minutes + parseInt(options.minuteStep, 10));
|
|
3990
|
+
// re-calculate minutes length because we have changes minutes value
|
|
3991
|
+
minutesLength = formatDate(newDate, minutesFormat).length;
|
|
3992
|
+
selectRange = [hoursLength + 1, hoursLength + 1 + minutesLength];
|
|
3993
|
+
} else if(selectedIndex === 2) {
|
|
3994
|
+
if(!lateralMove) $timepicker.switchMeridian();
|
|
3995
|
+
selectRange = [hoursLength + 1 + minutesLength + 1, hoursLength + 1 + minutesLength + 3];
|
|
3996
|
+
}
|
|
3997
|
+
$timepicker.select(newDate, selectedIndex, true);
|
|
3998
|
+
createSelection(selectRange[0], selectRange[1]);
|
|
3999
|
+
parentScope.$digest();
|
|
3515
4000
|
};
|
|
3516
4001
|
|
|
3517
|
-
|
|
3518
|
-
|
|
4002
|
+
// Private
|
|
4003
|
+
|
|
4004
|
+
function createSelection(start, end) {
|
|
4005
|
+
if(element[0].createTextRange) {
|
|
4006
|
+
var selRange = element[0].createTextRange();
|
|
4007
|
+
selRange.collapse(true);
|
|
4008
|
+
selRange.moveStart('character', start);
|
|
4009
|
+
selRange.moveEnd('character', end);
|
|
4010
|
+
selRange.select();
|
|
4011
|
+
} else if(element[0].setSelectionRange) {
|
|
4012
|
+
element[0].setSelectionRange(start, end);
|
|
4013
|
+
} else if(angular.isUndefined(element[0].selectionStart)) {
|
|
4014
|
+
element[0].selectionStart = start;
|
|
4015
|
+
element[0].selectionEnd = end;
|
|
4016
|
+
}
|
|
4017
|
+
}
|
|
4018
|
+
|
|
4019
|
+
function focusElement() {
|
|
4020
|
+
element[0].focus();
|
|
4021
|
+
}
|
|
4022
|
+
|
|
4023
|
+
// Overrides
|
|
4024
|
+
|
|
4025
|
+
var _init = $timepicker.init;
|
|
4026
|
+
$timepicker.init = function() {
|
|
4027
|
+
if(isNative && options.useNative) {
|
|
4028
|
+
element.prop('type', 'time');
|
|
4029
|
+
element.css('-webkit-appearance', 'textfield');
|
|
4030
|
+
return;
|
|
4031
|
+
} else if(isTouch) {
|
|
4032
|
+
element.prop('type', 'text');
|
|
4033
|
+
element.attr('readonly', 'true');
|
|
4034
|
+
element.on('click', focusElement);
|
|
4035
|
+
}
|
|
4036
|
+
_init();
|
|
3519
4037
|
};
|
|
3520
4038
|
|
|
3521
|
-
|
|
3522
|
-
|
|
3523
|
-
|
|
3524
|
-
|
|
3525
|
-
toDelete = i;
|
|
3526
|
-
break;
|
|
3527
|
-
}
|
|
4039
|
+
var _destroy = $timepicker.destroy;
|
|
4040
|
+
$timepicker.destroy = function() {
|
|
4041
|
+
if(isNative && options.useNative) {
|
|
4042
|
+
element.off('click', focusElement);
|
|
3528
4043
|
}
|
|
3529
|
-
|
|
4044
|
+
_destroy();
|
|
3530
4045
|
};
|
|
3531
4046
|
|
|
3532
|
-
|
|
3533
|
-
|
|
4047
|
+
var _show = $timepicker.show;
|
|
4048
|
+
$timepicker.show = function() {
|
|
4049
|
+
_show();
|
|
4050
|
+
// use timeout to hookup the events to prevent
|
|
4051
|
+
// event bubbling from being processed imediately.
|
|
4052
|
+
$timeout(function() {
|
|
4053
|
+
$timepicker.$element.on(isTouch ? 'touchstart' : 'mousedown', $timepicker.$onMouseDown);
|
|
4054
|
+
if(options.keyboard) {
|
|
4055
|
+
element.on('keydown', $timepicker.$onKeyDown);
|
|
4056
|
+
}
|
|
4057
|
+
}, 0, false);
|
|
3534
4058
|
};
|
|
3535
4059
|
|
|
3536
|
-
|
|
4060
|
+
var _hide = $timepicker.hide;
|
|
4061
|
+
$timepicker.hide = function(blur) {
|
|
4062
|
+
if(!$timepicker.$isShown) return;
|
|
4063
|
+
$timepicker.$element.off(isTouch ? 'touchstart' : 'mousedown', $timepicker.$onMouseDown);
|
|
4064
|
+
if(options.keyboard) {
|
|
4065
|
+
element.off('keydown', $timepicker.$onKeyDown);
|
|
4066
|
+
}
|
|
4067
|
+
_hide(blur);
|
|
4068
|
+
};
|
|
3537
4069
|
|
|
3538
|
-
$
|
|
3539
|
-
return $scrollspy;
|
|
4070
|
+
return $timepicker;
|
|
3540
4071
|
|
|
3541
4072
|
}
|
|
3542
4073
|
|
|
3543
|
-
|
|
4074
|
+
timepickerFactory.defaults = defaults;
|
|
4075
|
+
return timepickerFactory;
|
|
3544
4076
|
|
|
3545
4077
|
}];
|
|
3546
4078
|
|
|
3547
4079
|
})
|
|
3548
4080
|
|
|
3549
|
-
|
|
4081
|
+
|
|
4082
|
+
.directive('bsTimepicker', ["$window", "$parse", "$q", "$dateFormatter", "$dateParser", "$timepicker", function($window, $parse, $q, $dateFormatter, $dateParser, $timepicker) {
|
|
4083
|
+
|
|
4084
|
+
var defaults = $timepicker.defaults;
|
|
4085
|
+
var isNative = /(ip(a|o)d|iphone|android)/ig.test($window.navigator.userAgent);
|
|
4086
|
+
var requestAnimationFrame = $window.requestAnimationFrame || $window.setTimeout;
|
|
3550
4087
|
|
|
3551
4088
|
return {
|
|
3552
4089
|
restrict: 'EAC',
|
|
3553
|
-
|
|
4090
|
+
require: 'ngModel',
|
|
4091
|
+
link: function postLink(scope, element, attr, controller) {
|
|
3554
4092
|
|
|
3555
|
-
|
|
3556
|
-
|
|
4093
|
+
// Directive options
|
|
4094
|
+
var options = {scope: scope, controller: controller};
|
|
4095
|
+
angular.forEach(['placement', 'container', 'delay', 'trigger', 'keyboard', 'html', 'animation', 'template', 'autoclose', 'timeType', 'timeFormat', 'timezone', 'modelTimeFormat', 'useNative', 'hourStep', 'minuteStep', 'length', 'arrowBehavior', 'iconUp', 'iconDown', 'id'], function(key) {
|
|
3557
4096
|
if(angular.isDefined(attr[key])) options[key] = attr[key];
|
|
3558
4097
|
});
|
|
3559
4098
|
|
|
3560
|
-
|
|
3561
|
-
|
|
3562
|
-
|
|
3563
|
-
|
|
3564
|
-
if (scrollspy) {
|
|
3565
|
-
scrollspy.untrackElement(options.target, element);
|
|
3566
|
-
scrollspy.destroy();
|
|
3567
|
-
}
|
|
3568
|
-
options = null;
|
|
3569
|
-
scrollspy = null;
|
|
4099
|
+
// use string regex match for boolean values
|
|
4100
|
+
var falseValueRegExp = /^(false|0|)$/;
|
|
4101
|
+
angular.forEach(['roundDisplay'], function(key) {
|
|
4102
|
+
if(angular.isDefined(attr[key])) options[key] = !falseValueRegExp.test(attr[key]);
|
|
3570
4103
|
});
|
|
3571
4104
|
|
|
3572
|
-
|
|
3573
|
-
|
|
4105
|
+
// Visibility binding support
|
|
4106
|
+
attr.bsShow && scope.$watch(attr.bsShow, function(newValue, oldValue) {
|
|
4107
|
+
if(!timepicker || !angular.isDefined(newValue)) return;
|
|
4108
|
+
if(angular.isString(newValue)) newValue = !!newValue.match(/true|,?(timepicker),?/i);
|
|
4109
|
+
newValue === true ? timepicker.show() : timepicker.hide();
|
|
4110
|
+
});
|
|
3574
4111
|
|
|
3575
|
-
|
|
4112
|
+
// Initialize timepicker
|
|
4113
|
+
if(isNative && (options.useNative || defaults.useNative)) options.timeFormat = 'HH:mm';
|
|
4114
|
+
var timepicker = $timepicker(element, controller, options);
|
|
4115
|
+
options = timepicker.$options;
|
|
3576
4116
|
|
|
4117
|
+
var lang = options.lang;
|
|
4118
|
+
var formatDate = function(date, format, timezone) {
|
|
4119
|
+
return $dateFormatter.formatDate(date, format, lang, timezone);
|
|
4120
|
+
};
|
|
3577
4121
|
|
|
3578
|
-
|
|
4122
|
+
// Initialize parser
|
|
4123
|
+
var dateParser = $dateParser({format: options.timeFormat, lang: lang});
|
|
3579
4124
|
|
|
3580
|
-
|
|
3581
|
-
|
|
3582
|
-
|
|
3583
|
-
|
|
3584
|
-
|
|
3585
|
-
|
|
3586
|
-
|
|
4125
|
+
// Observe attributes for changes
|
|
4126
|
+
angular.forEach(['minTime', 'maxTime'], function(key) {
|
|
4127
|
+
// console.warn('attr.$observe(%s)', key, attr[key]);
|
|
4128
|
+
angular.isDefined(attr[key]) && attr.$observe(key, function(newValue) {
|
|
4129
|
+
timepicker.$options[key] = dateParser.getTimeForAttribute(key, newValue);
|
|
4130
|
+
!isNaN(timepicker.$options[key]) && timepicker.$build();
|
|
4131
|
+
validateAgainstMinMaxTime(controller.$dateValue);
|
|
4132
|
+
});
|
|
3587
4133
|
});
|
|
3588
|
-
}
|
|
3589
|
-
|
|
3590
|
-
};
|
|
3591
4134
|
|
|
3592
|
-
|
|
4135
|
+
// Watch model for changes
|
|
4136
|
+
scope.$watch(attr.ngModel, function(newValue, oldValue) {
|
|
4137
|
+
// console.warn('scope.$watch(%s)', attr.ngModel, newValue, oldValue, controller.$dateValue);
|
|
4138
|
+
timepicker.update(controller.$dateValue);
|
|
4139
|
+
}, true);
|
|
3593
4140
|
|
|
3594
|
-
|
|
3595
|
-
angular.
|
|
3596
|
-
|
|
3597
|
-
|
|
3598
|
-
|
|
4141
|
+
function validateAgainstMinMaxTime(parsedTime) {
|
|
4142
|
+
if (!angular.isDate(parsedTime)) return;
|
|
4143
|
+
var isMinValid = isNaN(options.minTime) || new Date(parsedTime.getTime()).setFullYear(1970, 0, 1) >= options.minTime;
|
|
4144
|
+
var isMaxValid = isNaN(options.maxTime) || new Date(parsedTime.getTime()).setFullYear(1970, 0, 1) <= options.maxTime;
|
|
4145
|
+
var isValid = isMinValid && isMaxValid;
|
|
4146
|
+
controller.$setValidity('date', isValid);
|
|
4147
|
+
controller.$setValidity('min', isMinValid);
|
|
4148
|
+
controller.$setValidity('max', isMaxValid);
|
|
4149
|
+
// Only update the model when we have a valid date
|
|
4150
|
+
if(!isValid) {
|
|
4151
|
+
return;
|
|
4152
|
+
}
|
|
4153
|
+
controller.$dateValue = parsedTime;
|
|
4154
|
+
}
|
|
3599
4155
|
|
|
3600
|
-
|
|
4156
|
+
// viewValue -> $parsers -> modelValue
|
|
4157
|
+
controller.$parsers.unshift(function(viewValue) {
|
|
4158
|
+
// console.warn('$parser("%s"): viewValue=%o', element.attr('ng-model'), viewValue);
|
|
4159
|
+
var date;
|
|
4160
|
+
// Null values should correctly reset the model value & validity
|
|
4161
|
+
if(!viewValue) {
|
|
4162
|
+
// BREAKING CHANGE:
|
|
4163
|
+
// return null (not undefined) when input value is empty, so angularjs 1.3
|
|
4164
|
+
// ngModelController can go ahead and run validators, like ngRequired
|
|
4165
|
+
controller.$setValidity('date', true);
|
|
4166
|
+
return null;
|
|
4167
|
+
}
|
|
4168
|
+
var parsedTime = angular.isDate(viewValue) ? viewValue : dateParser.parse(viewValue, controller.$dateValue);
|
|
4169
|
+
if(!parsedTime || isNaN(parsedTime.getTime())) {
|
|
4170
|
+
controller.$setValidity('date', false);
|
|
4171
|
+
// return undefined, causes ngModelController to
|
|
4172
|
+
// invalidate model value
|
|
4173
|
+
return;
|
|
4174
|
+
} else {
|
|
4175
|
+
validateAgainstMinMaxTime(parsedTime);
|
|
4176
|
+
}
|
|
3601
4177
|
|
|
3602
|
-
|
|
3603
|
-
|
|
3604
|
-
|
|
3605
|
-
|
|
3606
|
-
|
|
4178
|
+
if(options.timeType === 'string') {
|
|
4179
|
+
date = dateParser.timezoneOffsetAdjust(parsedTime, options.timezone, true);
|
|
4180
|
+
return formatDate(date, options.modelTimeFormat || options.timeFormat);
|
|
4181
|
+
}
|
|
4182
|
+
date = dateParser.timezoneOffsetAdjust(controller.$dateValue, options.timezone, true);
|
|
4183
|
+
if(options.timeType === 'number') {
|
|
4184
|
+
return date.getTime();
|
|
4185
|
+
} else if(options.timeType === 'unix') {
|
|
4186
|
+
return date.getTime() / 1000;
|
|
4187
|
+
} else if(options.timeType === 'iso') {
|
|
4188
|
+
return date.toISOString();
|
|
4189
|
+
} else {
|
|
4190
|
+
return new Date(date);
|
|
4191
|
+
}
|
|
4192
|
+
});
|
|
4193
|
+
|
|
4194
|
+
// modelValue -> $formatters -> viewValue
|
|
4195
|
+
controller.$formatters.push(function(modelValue) {
|
|
4196
|
+
// console.warn('$formatter("%s"): modelValue=%o (%o)', element.attr('ng-model'), modelValue, typeof modelValue);
|
|
4197
|
+
var date;
|
|
4198
|
+
if(angular.isUndefined(modelValue) || modelValue === null) {
|
|
4199
|
+
date = NaN;
|
|
4200
|
+
} else if(angular.isDate(modelValue)) {
|
|
4201
|
+
date = modelValue;
|
|
4202
|
+
} else if(options.timeType === 'string') {
|
|
4203
|
+
date = dateParser.parse(modelValue, null, options.modelTimeFormat);
|
|
4204
|
+
} else if(options.timeType === 'unix') {
|
|
4205
|
+
date = new Date(modelValue * 1000);
|
|
4206
|
+
} else {
|
|
4207
|
+
date = new Date(modelValue);
|
|
4208
|
+
}
|
|
4209
|
+
// Setup default value?
|
|
4210
|
+
// if(isNaN(date.getTime())) date = new Date(new Date().setMinutes(0) + 36e5);
|
|
4211
|
+
controller.$dateValue = dateParser.timezoneOffsetAdjust(date, options.timezone);
|
|
4212
|
+
return getTimeFormattedString();
|
|
4213
|
+
});
|
|
4214
|
+
|
|
4215
|
+
// viewValue -> element
|
|
4216
|
+
controller.$render = function() {
|
|
4217
|
+
// console.warn('$render("%s"): viewValue=%o', element.attr('ng-model'), controller.$viewValue);
|
|
4218
|
+
element.val(getTimeFormattedString());
|
|
4219
|
+
};
|
|
4220
|
+
|
|
4221
|
+
function getTimeFormattedString() {
|
|
4222
|
+
return !controller.$dateValue || isNaN(controller.$dateValue.getTime()) ? '' : formatDate(controller.$dateValue, options.timeFormat);
|
|
4223
|
+
}
|
|
4224
|
+
|
|
4225
|
+
// Garbage collection
|
|
4226
|
+
scope.$on('$destroy', function() {
|
|
4227
|
+
if (timepicker) timepicker.destroy();
|
|
4228
|
+
options = null;
|
|
4229
|
+
timepicker = null;
|
|
4230
|
+
});
|
|
4231
|
+
|
|
4232
|
+
}
|
|
4233
|
+
};
|
|
4234
|
+
|
|
4235
|
+
}]);
|
|
4236
|
+
|
|
4237
|
+
// Source: typeahead.js
|
|
4238
|
+
angular.module('mgcrea.ngStrap.typeahead', ['mgcrea.ngStrap.tooltip', 'mgcrea.ngStrap.helpers.parseOptions'])
|
|
4239
|
+
|
|
4240
|
+
.provider('$typeahead', function() {
|
|
4241
|
+
|
|
4242
|
+
var defaults = this.defaults = {
|
|
4243
|
+
animation: 'am-fade',
|
|
4244
|
+
prefixClass: 'typeahead',
|
|
4245
|
+
prefixEvent: '$typeahead',
|
|
4246
|
+
placement: 'bottom-left',
|
|
4247
|
+
template: 'typeahead/typeahead.tpl.html',
|
|
3607
4248
|
trigger: 'focus',
|
|
3608
4249
|
container: false,
|
|
3609
4250
|
keyboard: true,
|
|
3610
4251
|
html: false,
|
|
3611
4252
|
delay: 0,
|
|
3612
|
-
|
|
3613
|
-
|
|
3614
|
-
|
|
3615
|
-
|
|
3616
|
-
|
|
3617
|
-
autoclose: false,
|
|
3618
|
-
minTime: -Infinity,
|
|
3619
|
-
maxTime: +Infinity,
|
|
3620
|
-
length: 5,
|
|
3621
|
-
hourStep: 1,
|
|
3622
|
-
minuteStep: 5,
|
|
3623
|
-
iconUp: 'glyphicon glyphicon-chevron-up',
|
|
3624
|
-
iconDown: 'glyphicon glyphicon-chevron-down',
|
|
3625
|
-
arrowBehavior: 'pager'
|
|
4253
|
+
minLength: 1,
|
|
4254
|
+
filter: 'filter',
|
|
4255
|
+
limit: 6,
|
|
4256
|
+
autoSelect: false,
|
|
4257
|
+
comparator: ''
|
|
3626
4258
|
};
|
|
3627
4259
|
|
|
3628
|
-
this.$get = ["$window", "$
|
|
4260
|
+
this.$get = ["$window", "$rootScope", "$tooltip", "$timeout", function($window, $rootScope, $tooltip, $timeout) {
|
|
3629
4261
|
|
|
3630
4262
|
var bodyEl = angular.element($window.document.body);
|
|
3631
|
-
var isNative = /(ip(a|o)d|iphone|android)/ig.test($window.navigator.userAgent);
|
|
3632
|
-
var isTouch = ('createTouch' in $window.document) && isNative;
|
|
3633
|
-
if(!defaults.lang) defaults.lang = $dateFormatter.getDefaultLocale();
|
|
3634
4263
|
|
|
3635
|
-
function
|
|
3636
|
-
|
|
3637
|
-
var $timepicker = $tooltip(element, angular.extend({}, defaults, config));
|
|
3638
|
-
var parentScope = config.scope;
|
|
3639
|
-
var options = $timepicker.$options;
|
|
3640
|
-
var scope = $timepicker.$scope;
|
|
3641
|
-
|
|
3642
|
-
var lang = options.lang;
|
|
3643
|
-
var formatDate = function(date, format) {
|
|
3644
|
-
return $dateFormatter.formatDate(date, format, lang);
|
|
3645
|
-
};
|
|
3646
|
-
|
|
3647
|
-
// View vars
|
|
3648
|
-
|
|
3649
|
-
var selectedIndex = 0;
|
|
3650
|
-
var startDate = controller.$dateValue || new Date();
|
|
3651
|
-
var viewDate = {hour: startDate.getHours(), meridian: startDate.getHours() < 12, minute: startDate.getMinutes(), second: startDate.getSeconds(), millisecond: startDate.getMilliseconds()};
|
|
3652
|
-
|
|
3653
|
-
var format = $dateFormatter.getDatetimeFormat(options.timeFormat, lang);
|
|
4264
|
+
function TypeaheadFactory(element, controller, config) {
|
|
3654
4265
|
|
|
3655
|
-
var
|
|
3656
|
-
timeSeparator = $dateFormatter.timeSeparator(format),
|
|
3657
|
-
minutesFormat = $dateFormatter.minutesFormat(format),
|
|
3658
|
-
showAM = $dateFormatter.showAM(format);
|
|
4266
|
+
var $typeahead = {};
|
|
3659
4267
|
|
|
3660
|
-
|
|
3661
|
-
|
|
4268
|
+
// Common vars
|
|
4269
|
+
var options = angular.extend({}, defaults, config);
|
|
3662
4270
|
|
|
3663
|
-
|
|
4271
|
+
$typeahead = $tooltip(element, options);
|
|
4272
|
+
var parentScope = config.scope;
|
|
4273
|
+
var scope = $typeahead.$scope;
|
|
3664
4274
|
|
|
3665
|
-
scope.$
|
|
3666
|
-
|
|
3667
|
-
|
|
3668
|
-
scope.$moveIndex = function(value, index) {
|
|
3669
|
-
$timepicker.$moveIndex(value, index);
|
|
3670
|
-
};
|
|
3671
|
-
scope.$switchMeridian = function(date) {
|
|
3672
|
-
$timepicker.switchMeridian(date);
|
|
4275
|
+
scope.$resetMatches = function(){
|
|
4276
|
+
scope.$matches = [];
|
|
4277
|
+
scope.$activeIndex = options.autoSelect ? 0 : -1; // If set to 0, the first match will be highlighted
|
|
3673
4278
|
};
|
|
4279
|
+
scope.$resetMatches();
|
|
3674
4280
|
|
|
3675
|
-
|
|
3676
|
-
|
|
3677
|
-
|
|
3678
|
-
|
|
3679
|
-
if(angular.isDate(date) && !isNaN(date.getTime())) {
|
|
3680
|
-
$timepicker.$date = date;
|
|
3681
|
-
angular.extend(viewDate, {hour: date.getHours(), minute: date.getMinutes(), second: date.getSeconds(), millisecond: date.getMilliseconds()});
|
|
3682
|
-
$timepicker.$build();
|
|
3683
|
-
} else if(!$timepicker.$isBuilt) {
|
|
3684
|
-
$timepicker.$build();
|
|
3685
|
-
}
|
|
4281
|
+
scope.$activate = function(index) {
|
|
4282
|
+
scope.$$postDigest(function() {
|
|
4283
|
+
$typeahead.activate(index);
|
|
4284
|
+
});
|
|
3686
4285
|
};
|
|
3687
4286
|
|
|
3688
|
-
|
|
3689
|
-
|
|
3690
|
-
|
|
3691
|
-
|
|
3692
|
-
if(index === 0) controller.$dateValue.setHours(date.getHours());
|
|
3693
|
-
else if(index === 1) controller.$dateValue.setMinutes(date.getMinutes());
|
|
3694
|
-
controller.$setViewValue(angular.copy(controller.$dateValue));
|
|
3695
|
-
controller.$render();
|
|
3696
|
-
if(options.autoclose && !keep) {
|
|
3697
|
-
$timeout(function() { $timepicker.hide(true); });
|
|
3698
|
-
}
|
|
4287
|
+
scope.$select = function(index, evt) {
|
|
4288
|
+
scope.$$postDigest(function() {
|
|
4289
|
+
$typeahead.select(index);
|
|
4290
|
+
});
|
|
3699
4291
|
};
|
|
3700
4292
|
|
|
3701
|
-
|
|
3702
|
-
|
|
3703
|
-
return;
|
|
3704
|
-
}
|
|
3705
|
-
var hours = (date || controller.$dateValue).getHours();
|
|
3706
|
-
controller.$dateValue.setHours(hours < 12 ? hours + 12 : hours - 12);
|
|
3707
|
-
controller.$setViewValue(angular.copy(controller.$dateValue));
|
|
3708
|
-
controller.$render();
|
|
4293
|
+
scope.$isVisible = function() {
|
|
4294
|
+
return $typeahead.$isVisible();
|
|
3709
4295
|
};
|
|
3710
4296
|
|
|
3711
|
-
//
|
|
3712
|
-
|
|
3713
|
-
$timepicker.$build = function() {
|
|
3714
|
-
// console.warn('$timepicker.$build() viewDate=%o', viewDate);
|
|
3715
|
-
var i, midIndex = scope.midIndex = parseInt(options.length / 2, 10);
|
|
3716
|
-
var hours = [], hour;
|
|
3717
|
-
for(i = 0; i < options.length; i++) {
|
|
3718
|
-
hour = new Date(1970, 0, 1, viewDate.hour - (midIndex - i) * options.hourStep);
|
|
3719
|
-
hours.push({date: hour, label: formatDate(hour, hoursFormat), selected: $timepicker.$date && $timepicker.$isSelected(hour, 0), disabled: $timepicker.$isDisabled(hour, 0)});
|
|
3720
|
-
}
|
|
3721
|
-
var minutes = [], minute;
|
|
3722
|
-
for(i = 0; i < options.length; i++) {
|
|
3723
|
-
minute = new Date(1970, 0, 1, 0, viewDate.minute - (midIndex - i) * options.minuteStep);
|
|
3724
|
-
minutes.push({date: minute, label: formatDate(minute, minutesFormat), selected: $timepicker.$date && $timepicker.$isSelected(minute, 1), disabled: $timepicker.$isDisabled(minute, 1)});
|
|
3725
|
-
}
|
|
3726
|
-
|
|
3727
|
-
var rows = [];
|
|
3728
|
-
for(i = 0; i < options.length; i++) {
|
|
3729
|
-
rows.push([hours[i], minutes[i]]);
|
|
3730
|
-
}
|
|
3731
|
-
scope.rows = rows;
|
|
3732
|
-
scope.showAM = showAM;
|
|
3733
|
-
scope.isAM = ($timepicker.$date || hours[midIndex].date).getHours() < 12;
|
|
3734
|
-
scope.timeSeparator = timeSeparator;
|
|
3735
|
-
$timepicker.$isBuilt = true;
|
|
3736
|
-
};
|
|
4297
|
+
// Public methods
|
|
3737
4298
|
|
|
3738
|
-
$
|
|
3739
|
-
|
|
3740
|
-
|
|
3741
|
-
|
|
3742
|
-
}
|
|
3743
|
-
|
|
3744
|
-
|
|
4299
|
+
$typeahead.update = function(matches) {
|
|
4300
|
+
scope.$matches = matches;
|
|
4301
|
+
if(scope.$activeIndex >= matches.length) {
|
|
4302
|
+
scope.$activeIndex = options.autoSelect ? 0: -1;
|
|
4303
|
+
}
|
|
4304
|
+
|
|
4305
|
+
// When the placement is not one of the bottom placements, re-calc the positioning
|
|
4306
|
+
// so the results render correctly.
|
|
4307
|
+
if (/^(bottom|bottom-left|bottom-right)$/.test(options.placement)) return;
|
|
4308
|
+
|
|
4309
|
+
// wrap in a $timeout so the results are updated
|
|
4310
|
+
// before repositioning
|
|
4311
|
+
$timeout($typeahead.$applyPlacement);
|
|
3745
4312
|
};
|
|
3746
4313
|
|
|
3747
|
-
$
|
|
3748
|
-
|
|
3749
|
-
if(index === 0) {
|
|
3750
|
-
selectedTime = date.getTime() + viewDate.minute * 6e4;
|
|
3751
|
-
} else if(index === 1) {
|
|
3752
|
-
selectedTime = date.getTime() + viewDate.hour * 36e5;
|
|
3753
|
-
}
|
|
3754
|
-
return selectedTime < options.minTime * 1 || selectedTime > options.maxTime * 1;
|
|
4314
|
+
$typeahead.activate = function(index) {
|
|
4315
|
+
scope.$activeIndex = index;
|
|
3755
4316
|
};
|
|
3756
4317
|
|
|
3757
|
-
|
|
3758
|
-
|
|
3759
|
-
|
|
3760
|
-
|
|
3761
|
-
|
|
3762
|
-
|
|
4318
|
+
$typeahead.select = function(index) {
|
|
4319
|
+
var value = scope.$matches[index].value;
|
|
4320
|
+
// console.log('$setViewValue', value);
|
|
4321
|
+
controller.$setViewValue(value);
|
|
4322
|
+
controller.$render();
|
|
4323
|
+
scope.$resetMatches();
|
|
4324
|
+
if(parentScope) parentScope.$digest();
|
|
4325
|
+
// Emit event
|
|
4326
|
+
scope.$emit(options.prefixEvent + '.select', value, index, $typeahead);
|
|
3763
4327
|
};
|
|
3764
4328
|
|
|
3765
|
-
|
|
3766
|
-
var newDate = new Date($timepicker.$date);
|
|
3767
|
-
var hours = newDate.getHours(), hoursLength = formatDate(newDate, hoursFormat).length;
|
|
3768
|
-
var minutes = newDate.getMinutes(), minutesLength = formatDate(newDate, minutesFormat).length;
|
|
3769
|
-
if (index === 0) {
|
|
3770
|
-
newDate.setHours(hours - (parseInt(options.hourStep, 10) * value));
|
|
3771
|
-
}
|
|
3772
|
-
else {
|
|
3773
|
-
newDate.setMinutes(minutes - (parseInt(options.minuteStep, 10) * value));
|
|
3774
|
-
}
|
|
3775
|
-
$timepicker.select(newDate, index, true);
|
|
3776
|
-
};
|
|
4329
|
+
// Protected methods
|
|
3777
4330
|
|
|
3778
|
-
$
|
|
3779
|
-
|
|
3780
|
-
|
|
3781
|
-
targetDate = new Date(1970, 0, 1, viewDate.hour + (value * options.length), viewDate.minute);
|
|
3782
|
-
angular.extend(viewDate, {hour: targetDate.getHours()});
|
|
3783
|
-
} else if(index === 1) {
|
|
3784
|
-
targetDate = new Date(1970, 0, 1, viewDate.hour, viewDate.minute + (value * options.length * options.minuteStep));
|
|
3785
|
-
angular.extend(viewDate, {minute: targetDate.getMinutes()});
|
|
4331
|
+
$typeahead.$isVisible = function() {
|
|
4332
|
+
if(!options.minLength || !controller) {
|
|
4333
|
+
return !!scope.$matches.length;
|
|
3786
4334
|
}
|
|
3787
|
-
|
|
4335
|
+
// minLength support
|
|
4336
|
+
return scope.$matches.length && angular.isString(controller.$viewValue) && controller.$viewValue.length >= options.minLength;
|
|
3788
4337
|
};
|
|
3789
4338
|
|
|
3790
|
-
$
|
|
3791
|
-
|
|
3792
|
-
if(
|
|
3793
|
-
|
|
3794
|
-
|
|
3795
|
-
if(isTouch) {
|
|
3796
|
-
var targetEl = angular.element(evt.target);
|
|
3797
|
-
if(targetEl[0].nodeName.toLowerCase() !== 'button') {
|
|
3798
|
-
targetEl = targetEl.parent();
|
|
3799
|
-
}
|
|
3800
|
-
targetEl.triggerHandler('click');
|
|
4339
|
+
$typeahead.$getIndex = function(value) {
|
|
4340
|
+
var l = scope.$matches.length, i = l;
|
|
4341
|
+
if(!l) return;
|
|
4342
|
+
for(i = l; i--;) {
|
|
4343
|
+
if(scope.$matches[i].value === value) break;
|
|
3801
4344
|
}
|
|
4345
|
+
if(i < 0) return;
|
|
4346
|
+
return i;
|
|
3802
4347
|
};
|
|
3803
4348
|
|
|
3804
|
-
$
|
|
3805
|
-
|
|
4349
|
+
$typeahead.$onMouseDown = function(evt) {
|
|
4350
|
+
// Prevent blur on mousedown
|
|
3806
4351
|
evt.preventDefault();
|
|
3807
4352
|
evt.stopPropagation();
|
|
4353
|
+
};
|
|
3808
4354
|
|
|
3809
|
-
|
|
3810
|
-
if(evt.keyCode
|
|
3811
|
-
|
|
3812
|
-
// Navigate with keyboard
|
|
3813
|
-
var newDate = new Date($timepicker.$date);
|
|
3814
|
-
var hours = newDate.getHours(), hoursLength = formatDate(newDate, hoursFormat).length;
|
|
3815
|
-
var minutes = newDate.getMinutes(), minutesLength = formatDate(newDate, minutesFormat).length;
|
|
3816
|
-
var lateralMove = /(37|39)/.test(evt.keyCode);
|
|
3817
|
-
var count = 2 + showAM * 1;
|
|
3818
|
-
|
|
3819
|
-
// Navigate indexes (left, right)
|
|
3820
|
-
if (lateralMove) {
|
|
3821
|
-
if(evt.keyCode === 37) selectedIndex = selectedIndex < 1 ? count - 1 : selectedIndex - 1;
|
|
3822
|
-
else if(evt.keyCode === 39) selectedIndex = selectedIndex < count - 1 ? selectedIndex + 1 : 0;
|
|
3823
|
-
}
|
|
3824
|
-
|
|
3825
|
-
// Update values (up, down)
|
|
3826
|
-
var selectRange = [0, hoursLength];
|
|
3827
|
-
if(selectedIndex === 0) {
|
|
3828
|
-
if(evt.keyCode === 38) newDate.setHours(hours - parseInt(options.hourStep, 10));
|
|
3829
|
-
else if(evt.keyCode === 40) newDate.setHours(hours + parseInt(options.hourStep, 10));
|
|
3830
|
-
// re-calculate hours length because we have changed hours value
|
|
3831
|
-
hoursLength = formatDate(newDate, hoursFormat).length;
|
|
3832
|
-
selectRange = [0, hoursLength];
|
|
3833
|
-
} else if(selectedIndex === 1) {
|
|
3834
|
-
if(evt.keyCode === 38) newDate.setMinutes(minutes - parseInt(options.minuteStep, 10));
|
|
3835
|
-
else if(evt.keyCode === 40) newDate.setMinutes(minutes + parseInt(options.minuteStep, 10));
|
|
3836
|
-
// re-calculate minutes length because we have changes minutes value
|
|
3837
|
-
minutesLength = formatDate(newDate, minutesFormat).length;
|
|
3838
|
-
selectRange = [hoursLength + 1, hoursLength + 1 + minutesLength];
|
|
3839
|
-
} else if(selectedIndex === 2) {
|
|
3840
|
-
if(!lateralMove) $timepicker.switchMeridian();
|
|
3841
|
-
selectRange = [hoursLength + 1 + minutesLength + 1, hoursLength + 1 + minutesLength + 3];
|
|
3842
|
-
}
|
|
3843
|
-
$timepicker.select(newDate, selectedIndex, true);
|
|
3844
|
-
createSelection(selectRange[0], selectRange[1]);
|
|
3845
|
-
parentScope.$digest();
|
|
3846
|
-
};
|
|
3847
|
-
|
|
3848
|
-
// Private
|
|
3849
|
-
|
|
3850
|
-
function createSelection(start, end) {
|
|
3851
|
-
if(element[0].createTextRange) {
|
|
3852
|
-
var selRange = element[0].createTextRange();
|
|
3853
|
-
selRange.collapse(true);
|
|
3854
|
-
selRange.moveStart('character', start);
|
|
3855
|
-
selRange.moveEnd('character', end);
|
|
3856
|
-
selRange.select();
|
|
3857
|
-
} else if(element[0].setSelectionRange) {
|
|
3858
|
-
element[0].setSelectionRange(start, end);
|
|
3859
|
-
} else if(angular.isUndefined(element[0].selectionStart)) {
|
|
3860
|
-
element[0].selectionStart = start;
|
|
3861
|
-
element[0].selectionEnd = end;
|
|
3862
|
-
}
|
|
3863
|
-
}
|
|
3864
|
-
|
|
3865
|
-
function focusElement() {
|
|
3866
|
-
element[0].focus();
|
|
3867
|
-
}
|
|
3868
|
-
|
|
3869
|
-
// Overrides
|
|
4355
|
+
$typeahead.$onKeyDown = function(evt) {
|
|
4356
|
+
if(!/(38|40|13)/.test(evt.keyCode)) return;
|
|
3870
4357
|
|
|
3871
|
-
|
|
3872
|
-
|
|
3873
|
-
|
|
3874
|
-
|
|
3875
|
-
element.css('-webkit-appearance', 'textfield');
|
|
3876
|
-
return;
|
|
3877
|
-
} else if(isTouch) {
|
|
3878
|
-
element.prop('type', 'text');
|
|
3879
|
-
element.attr('readonly', 'true');
|
|
3880
|
-
element.on('click', focusElement);
|
|
4358
|
+
// Let ngSubmit pass if the typeahead tip is hidden
|
|
4359
|
+
if($typeahead.$isVisible()) {
|
|
4360
|
+
evt.preventDefault();
|
|
4361
|
+
evt.stopPropagation();
|
|
3881
4362
|
}
|
|
3882
|
-
_init();
|
|
3883
|
-
};
|
|
3884
4363
|
|
|
3885
|
-
|
|
3886
|
-
|
|
3887
|
-
|
|
3888
|
-
element.off('click', focusElement);
|
|
4364
|
+
// Select with enter
|
|
4365
|
+
if(evt.keyCode === 13 && scope.$matches.length) {
|
|
4366
|
+
$typeahead.select(scope.$activeIndex);
|
|
3889
4367
|
}
|
|
3890
|
-
|
|
4368
|
+
|
|
4369
|
+
// Navigate with keyboard
|
|
4370
|
+
else if(evt.keyCode === 38 && scope.$activeIndex > 0) scope.$activeIndex--;
|
|
4371
|
+
else if(evt.keyCode === 40 && scope.$activeIndex < scope.$matches.length - 1) scope.$activeIndex++;
|
|
4372
|
+
else if(angular.isUndefined(scope.$activeIndex)) scope.$activeIndex = 0;
|
|
4373
|
+
scope.$digest();
|
|
3891
4374
|
};
|
|
3892
4375
|
|
|
3893
|
-
|
|
3894
|
-
|
|
3895
|
-
|
|
4376
|
+
// Overrides
|
|
4377
|
+
|
|
4378
|
+
var show = $typeahead.show;
|
|
4379
|
+
$typeahead.show = function() {
|
|
4380
|
+
show();
|
|
3896
4381
|
// use timeout to hookup the events to prevent
|
|
3897
4382
|
// event bubbling from being processed imediately.
|
|
3898
4383
|
$timeout(function() {
|
|
3899
|
-
$
|
|
4384
|
+
$typeahead.$element.on('mousedown', $typeahead.$onMouseDown);
|
|
3900
4385
|
if(options.keyboard) {
|
|
3901
|
-
element.on('keydown', $
|
|
4386
|
+
element.on('keydown', $typeahead.$onKeyDown);
|
|
3902
4387
|
}
|
|
3903
4388
|
}, 0, false);
|
|
3904
4389
|
};
|
|
3905
4390
|
|
|
3906
|
-
var
|
|
3907
|
-
$
|
|
3908
|
-
|
|
3909
|
-
$timepicker.$element.off(isTouch ? 'touchstart' : 'mousedown', $timepicker.$onMouseDown);
|
|
4391
|
+
var hide = $typeahead.hide;
|
|
4392
|
+
$typeahead.hide = function() {
|
|
4393
|
+
$typeahead.$element.off('mousedown', $typeahead.$onMouseDown);
|
|
3910
4394
|
if(options.keyboard) {
|
|
3911
|
-
element.off('keydown', $
|
|
4395
|
+
element.off('keydown', $typeahead.$onKeyDown);
|
|
3912
4396
|
}
|
|
3913
|
-
|
|
4397
|
+
if(!options.autoSelect)
|
|
4398
|
+
$typeahead.activate(-1);
|
|
4399
|
+
hide();
|
|
3914
4400
|
};
|
|
3915
4401
|
|
|
3916
|
-
return $
|
|
4402
|
+
return $typeahead;
|
|
3917
4403
|
|
|
3918
4404
|
}
|
|
3919
4405
|
|
|
3920
|
-
|
|
3921
|
-
return
|
|
4406
|
+
TypeaheadFactory.defaults = defaults;
|
|
4407
|
+
return TypeaheadFactory;
|
|
3922
4408
|
|
|
3923
4409
|
}];
|
|
3924
4410
|
|
|
3925
4411
|
})
|
|
3926
4412
|
|
|
4413
|
+
.directive('bsTypeahead', ["$window", "$parse", "$q", "$typeahead", "$parseOptions", function($window, $parse, $q, $typeahead, $parseOptions) {
|
|
3927
4414
|
|
|
3928
|
-
|
|
3929
|
-
|
|
3930
|
-
var defaults = $timepicker.defaults;
|
|
3931
|
-
var isNative = /(ip(a|o)d|iphone|android)/ig.test($window.navigator.userAgent);
|
|
3932
|
-
var requestAnimationFrame = $window.requestAnimationFrame || $window.setTimeout;
|
|
4415
|
+
var defaults = $typeahead.defaults;
|
|
3933
4416
|
|
|
3934
4417
|
return {
|
|
3935
4418
|
restrict: 'EAC',
|
|
@@ -3937,131 +4420,86 @@ angular.module('mgcrea.ngStrap.timepicker', [
|
|
|
3937
4420
|
link: function postLink(scope, element, attr, controller) {
|
|
3938
4421
|
|
|
3939
4422
|
// Directive options
|
|
3940
|
-
var options = {scope: scope
|
|
3941
|
-
angular.forEach(['placement', 'container', 'delay', 'trigger', 'keyboard', 'html', 'animation', 'template', '
|
|
4423
|
+
var options = {scope: scope};
|
|
4424
|
+
angular.forEach(['placement', 'container', 'delay', 'trigger', 'keyboard', 'html', 'animation', 'template', 'filter', 'limit', 'minLength', 'watchOptions', 'selectMode', 'autoSelect', 'comparator', 'id'], function(key) {
|
|
3942
4425
|
if(angular.isDefined(attr[key])) options[key] = attr[key];
|
|
3943
4426
|
});
|
|
3944
4427
|
|
|
3945
|
-
//
|
|
3946
|
-
attr
|
|
3947
|
-
|
|
3948
|
-
|
|
3949
|
-
|
|
3950
|
-
|
|
3951
|
-
|
|
3952
|
-
// Initialize timepicker
|
|
3953
|
-
if(isNative && (options.useNative || defaults.useNative)) options.timeFormat = 'HH:mm';
|
|
3954
|
-
var timepicker = $timepicker(element, controller, options);
|
|
3955
|
-
options = timepicker.$options;
|
|
4428
|
+
// Disable browser autocompletion
|
|
4429
|
+
element.attr('autocomplete' ,'off');
|
|
4430
|
+
|
|
4431
|
+
// Build proper bsOptions
|
|
4432
|
+
var filter = options.filter || defaults.filter;
|
|
4433
|
+
var limit = options.limit || defaults.limit;
|
|
4434
|
+
var comparator = options.comparator || defaults.comparator;
|
|
3956
4435
|
|
|
3957
|
-
var
|
|
3958
|
-
|
|
3959
|
-
|
|
3960
|
-
|
|
4436
|
+
var bsOptions = attr.bsOptions;
|
|
4437
|
+
if(filter) bsOptions += ' | ' + filter + ':$viewValue';
|
|
4438
|
+
if (comparator) bsOptions += ':' + comparator;
|
|
4439
|
+
if(limit) bsOptions += ' | limitTo:' + limit;
|
|
4440
|
+
var parsedOptions = $parseOptions(bsOptions);
|
|
3961
4441
|
|
|
3962
|
-
// Initialize
|
|
3963
|
-
var
|
|
4442
|
+
// Initialize typeahead
|
|
4443
|
+
var typeahead = $typeahead(element, controller, options);
|
|
3964
4444
|
|
|
3965
|
-
//
|
|
3966
|
-
|
|
3967
|
-
//
|
|
3968
|
-
|
|
3969
|
-
|
|
3970
|
-
|
|
3971
|
-
|
|
3972
|
-
|
|
3973
|
-
|
|
4445
|
+
// Watch options on demand
|
|
4446
|
+
if(options.watchOptions) {
|
|
4447
|
+
// Watch bsOptions values before filtering for changes, drop function calls
|
|
4448
|
+
var watchedOptions = parsedOptions.$match[7].replace(/\|.+/, '').replace(/\(.*\)/g, '').trim();
|
|
4449
|
+
scope.$watch(watchedOptions, function (newValue, oldValue) {
|
|
4450
|
+
// console.warn('scope.$watch(%s)', watchedOptions, newValue, oldValue);
|
|
4451
|
+
parsedOptions.valuesFn(scope, controller).then(function (values) {
|
|
4452
|
+
typeahead.update(values);
|
|
4453
|
+
controller.$render();
|
|
4454
|
+
});
|
|
4455
|
+
}, true);
|
|
4456
|
+
}
|
|
3974
4457
|
|
|
3975
4458
|
// Watch model for changes
|
|
3976
4459
|
scope.$watch(attr.ngModel, function(newValue, oldValue) {
|
|
3977
|
-
// console.warn('
|
|
3978
|
-
|
|
3979
|
-
|
|
3980
|
-
|
|
3981
|
-
|
|
3982
|
-
|
|
3983
|
-
|
|
3984
|
-
|
|
3985
|
-
var isValid = isMinValid && isMaxValid;
|
|
3986
|
-
controller.$setValidity('date', isValid);
|
|
3987
|
-
controller.$setValidity('min', isMinValid);
|
|
3988
|
-
controller.$setValidity('max', isMaxValid);
|
|
3989
|
-
// Only update the model when we have a valid date
|
|
3990
|
-
if(!isValid) {
|
|
4460
|
+
// console.warn('$watch', element.attr('ng-model'), newValue);
|
|
4461
|
+
scope.$modelValue = newValue; // Publish modelValue on scope for custom templates
|
|
4462
|
+
parsedOptions.valuesFn(scope, controller)
|
|
4463
|
+
.then(function(values) {
|
|
4464
|
+
// Prevent input with no future prospect if selectMode is truthy
|
|
4465
|
+
// @TODO test selectMode
|
|
4466
|
+
if(options.selectMode && !values.length && newValue.length > 0) {
|
|
4467
|
+
controller.$setViewValue(controller.$viewValue.substring(0, controller.$viewValue.length - 1));
|
|
3991
4468
|
return;
|
|
3992
|
-
|
|
3993
|
-
|
|
3994
|
-
|
|
3995
|
-
|
|
3996
|
-
|
|
3997
|
-
|
|
3998
|
-
|
|
3999
|
-
|
|
4000
|
-
|
|
4001
|
-
|
|
4002
|
-
// return null (not undefined) when input value is empty, so angularjs 1.3
|
|
4003
|
-
// ngModelController can go ahead and run validators, like ngRequired
|
|
4004
|
-
controller.$setValidity('date', true);
|
|
4005
|
-
return null;
|
|
4006
|
-
}
|
|
4007
|
-
var parsedTime = angular.isDate(viewValue) ? viewValue : dateParser.parse(viewValue, controller.$dateValue);
|
|
4008
|
-
if(!parsedTime || isNaN(parsedTime.getTime())) {
|
|
4009
|
-
controller.$setValidity('date', false);
|
|
4010
|
-
// return undefined, causes ngModelController to
|
|
4011
|
-
// invalidate model value
|
|
4012
|
-
return;
|
|
4013
|
-
} else {
|
|
4014
|
-
validateAgainstMinMaxTime(parsedTime);
|
|
4015
|
-
}
|
|
4016
|
-
if(options.timeType === 'string') {
|
|
4017
|
-
return formatDate(parsedTime, options.modelTimeFormat || options.timeFormat);
|
|
4018
|
-
} else if(options.timeType === 'number') {
|
|
4019
|
-
return controller.$dateValue.getTime();
|
|
4020
|
-
} else if(options.timeType === 'unix') {
|
|
4021
|
-
return controller.$dateValue.getTime() / 1000;
|
|
4022
|
-
} else if(options.timeType === 'iso') {
|
|
4023
|
-
return controller.$dateValue.toISOString();
|
|
4024
|
-
} else {
|
|
4025
|
-
return new Date(controller.$dateValue);
|
|
4026
|
-
}
|
|
4469
|
+
}
|
|
4470
|
+
if(values.length > limit) values = values.slice(0, limit);
|
|
4471
|
+
var isVisible = typeahead.$isVisible();
|
|
4472
|
+
isVisible && typeahead.update(values);
|
|
4473
|
+
// Do not re-queue an update if a correct value has been selected
|
|
4474
|
+
if(values.length === 1 && values[0].value === newValue) return;
|
|
4475
|
+
!isVisible && typeahead.update(values);
|
|
4476
|
+
// Queue a new rendering that will leverage collection loading
|
|
4477
|
+
controller.$render();
|
|
4478
|
+
});
|
|
4027
4479
|
});
|
|
4028
4480
|
|
|
4029
4481
|
// modelValue -> $formatters -> viewValue
|
|
4030
4482
|
controller.$formatters.push(function(modelValue) {
|
|
4031
4483
|
// console.warn('$formatter("%s"): modelValue=%o (%o)', element.attr('ng-model'), modelValue, typeof modelValue);
|
|
4032
|
-
var
|
|
4033
|
-
|
|
4034
|
-
date = NaN;
|
|
4035
|
-
} else if(angular.isDate(modelValue)) {
|
|
4036
|
-
date = modelValue;
|
|
4037
|
-
} else if(options.timeType === 'string') {
|
|
4038
|
-
date = dateParser.parse(modelValue, null, options.modelTimeFormat);
|
|
4039
|
-
} else if(options.timeType === 'unix') {
|
|
4040
|
-
date = new Date(modelValue * 1000);
|
|
4041
|
-
} else {
|
|
4042
|
-
date = new Date(modelValue);
|
|
4043
|
-
}
|
|
4044
|
-
// Setup default value?
|
|
4045
|
-
// if(isNaN(date.getTime())) date = new Date(new Date().setMinutes(0) + 36e5);
|
|
4046
|
-
controller.$dateValue = date;
|
|
4047
|
-
return getTimeFormattedString();
|
|
4484
|
+
var displayValue = parsedOptions.displayValue(modelValue);
|
|
4485
|
+
return displayValue === undefined ? '' : displayValue;
|
|
4048
4486
|
});
|
|
4049
4487
|
|
|
4050
|
-
//
|
|
4051
|
-
controller.$render = function() {
|
|
4052
|
-
// console.warn('$render
|
|
4053
|
-
element.val(
|
|
4488
|
+
// Model rendering in view
|
|
4489
|
+
controller.$render = function () {
|
|
4490
|
+
// console.warn('$render', element.attr('ng-model'), 'controller.$modelValue', typeof controller.$modelValue, controller.$modelValue, 'controller.$viewValue', typeof controller.$viewValue, controller.$viewValue);
|
|
4491
|
+
if(controller.$isEmpty(controller.$viewValue)) return element.val('');
|
|
4492
|
+
var index = typeahead.$getIndex(controller.$modelValue);
|
|
4493
|
+
var selected = angular.isDefined(index) ? typeahead.$scope.$matches[index].label : controller.$viewValue;
|
|
4494
|
+
selected = angular.isObject(selected) ? parsedOptions.displayValue(selected) : selected;
|
|
4495
|
+
element.val(selected ? selected.toString().replace(/<(?:.|\n)*?>/gm, '').trim() : '');
|
|
4054
4496
|
};
|
|
4055
4497
|
|
|
4056
|
-
function getTimeFormattedString() {
|
|
4057
|
-
return !controller.$dateValue || isNaN(controller.$dateValue.getTime()) ? '' : formatDate(controller.$dateValue, options.timeFormat);
|
|
4058
|
-
}
|
|
4059
|
-
|
|
4060
4498
|
// Garbage collection
|
|
4061
4499
|
scope.$on('$destroy', function() {
|
|
4062
|
-
if (
|
|
4500
|
+
if (typeahead) typeahead.destroy();
|
|
4063
4501
|
options = null;
|
|
4064
|
-
|
|
4502
|
+
typeahead = null;
|
|
4065
4503
|
});
|
|
4066
4504
|
|
|
4067
4505
|
}
|
|
@@ -4092,7 +4530,11 @@ angular.module('mgcrea.ngStrap.tooltip', ['mgcrea.ngStrap.helpers.dimensions'])
|
|
|
4092
4530
|
type: '',
|
|
4093
4531
|
delay: 0,
|
|
4094
4532
|
autoClose: false,
|
|
4095
|
-
bsEnabled: true
|
|
4533
|
+
bsEnabled: true,
|
|
4534
|
+
viewport: {
|
|
4535
|
+
selector: 'body',
|
|
4536
|
+
padding: 0
|
|
4537
|
+
}
|
|
4096
4538
|
};
|
|
4097
4539
|
|
|
4098
4540
|
this.$get = ["$window", "$rootScope", "$compile", "$q", "$templateCache", "$http", "$animate", "$sce", "dimensions", "$$rAF", "$timeout", function($window, $rootScope, $compile, $q, $templateCache, $http, $animate, $sce, dimensions, $$rAF, $timeout) {
|
|
@@ -4281,19 +4723,28 @@ angular.module('mgcrea.ngStrap.tooltip', ['mgcrea.ngStrap.helpers.dimensions'])
|
|
|
4281
4723
|
// Options: custom classes
|
|
4282
4724
|
if(options.customClass) tipElement.addClass(options.customClass);
|
|
4283
4725
|
|
|
4726
|
+
// Append the element, without any animations. If we append
|
|
4727
|
+
// using $animate.enter, some of the animations cause the placement
|
|
4728
|
+
// to be off due to the transforms.
|
|
4729
|
+
after ? after.after(tipElement) : parent.prepend(tipElement);
|
|
4730
|
+
|
|
4731
|
+
$tooltip.$isShown = scope.$isShown = true;
|
|
4732
|
+
safeDigest(scope);
|
|
4733
|
+
|
|
4734
|
+
// Now, apply placement
|
|
4735
|
+
$tooltip.$applyPlacement();
|
|
4736
|
+
|
|
4737
|
+
// Once placed, animate it.
|
|
4284
4738
|
// Support v1.3+ $animate
|
|
4285
4739
|
// https://github.com/angular/angular.js/commit/bf0f5502b1bbfddc5cdd2f138efd9188b8c652a9
|
|
4286
4740
|
var promise = $animate.enter(tipElement, parent, after, enterAnimateCallback);
|
|
4287
4741
|
if(promise && promise.then) promise.then(enterAnimateCallback);
|
|
4288
|
-
|
|
4289
|
-
$tooltip.$isShown = scope.$isShown = true;
|
|
4290
4742
|
safeDigest(scope);
|
|
4291
|
-
$$rAF(function () {
|
|
4292
|
-
$tooltip.$applyPlacement();
|
|
4293
4743
|
|
|
4294
|
-
|
|
4744
|
+
$$rAF(function () {
|
|
4745
|
+
// Once the tooltip is placed and the animation starts, make the tooltip visible
|
|
4295
4746
|
if(tipElement) tipElement.css({visibility: 'visible'});
|
|
4296
|
-
});
|
|
4747
|
+
});
|
|
4297
4748
|
|
|
4298
4749
|
// Bind events
|
|
4299
4750
|
if(options.keyboard) {
|
|
@@ -4388,6 +4839,10 @@ angular.module('mgcrea.ngStrap.tooltip', ['mgcrea.ngStrap.helpers.dimensions'])
|
|
|
4388
4839
|
options.bsEnabled = isEnabled;
|
|
4389
4840
|
};
|
|
4390
4841
|
|
|
4842
|
+
$tooltip.setViewport = function(viewport) {
|
|
4843
|
+
options.viewport = viewport;
|
|
4844
|
+
};
|
|
4845
|
+
|
|
4391
4846
|
// Protected methods
|
|
4392
4847
|
|
|
4393
4848
|
$tooltip.$applyPlacement = function() {
|
|
@@ -4415,7 +4870,7 @@ angular.module('mgcrea.ngStrap.tooltip', ['mgcrea.ngStrap.helpers.dimensions'])
|
|
|
4415
4870
|
// If we're auto placing, we need to check the positioning
|
|
4416
4871
|
if (autoPlace) {
|
|
4417
4872
|
var originalPlacement = placement;
|
|
4418
|
-
var container = options.container ?
|
|
4873
|
+
var container = options.container ? findElement(options.container) : element.parent();
|
|
4419
4874
|
var containerPosition = getPosition(container);
|
|
4420
4875
|
|
|
4421
4876
|
// Determine if the vertical placement
|
|
@@ -4443,7 +4898,7 @@ angular.module('mgcrea.ngStrap.tooltip', ['mgcrea.ngStrap.helpers.dimensions'])
|
|
|
4443
4898
|
|
|
4444
4899
|
// Get the tooltip's top and left coordinates to center it with this directive.
|
|
4445
4900
|
var tipPosition = getCalculatedOffset(placement, elementPosition, tipWidth, tipHeight);
|
|
4446
|
-
|
|
4901
|
+
applyPlacement(tipPosition, placement);
|
|
4447
4902
|
};
|
|
4448
4903
|
|
|
4449
4904
|
$tooltip.$onKeyUp = function(evt) {
|
|
@@ -4543,22 +4998,28 @@ angular.module('mgcrea.ngStrap.tooltip', ['mgcrea.ngStrap.helpers.dimensions'])
|
|
|
4543
4998
|
function getPosition($element) {
|
|
4544
4999
|
$element = $element || (options.target || element);
|
|
4545
5000
|
|
|
4546
|
-
var el = $element[0]
|
|
5001
|
+
var el = $element[0],
|
|
5002
|
+
isBody = el.tagName === 'BODY';
|
|
4547
5003
|
|
|
4548
5004
|
var elRect = el.getBoundingClientRect();
|
|
4549
|
-
|
|
4550
|
-
|
|
4551
|
-
|
|
5005
|
+
var rect = {};
|
|
5006
|
+
|
|
5007
|
+
// IE8 has issues with angular.extend and using elRect directly.
|
|
5008
|
+
// By coping the values of elRect into a new object, we can continue to use extend
|
|
5009
|
+
for (var p in elRect) {
|
|
5010
|
+
// DO NOT use hasOwnProperty when inspecting the return of getBoundingClientRect.
|
|
5011
|
+
rect[p] = elRect[p];
|
|
4552
5012
|
}
|
|
4553
5013
|
|
|
4554
|
-
|
|
4555
|
-
|
|
4556
|
-
|
|
4557
|
-
} else {
|
|
4558
|
-
elPos = dimensions.position(el);
|
|
5014
|
+
if (rect.width === null) {
|
|
5015
|
+
// width and height are missing in IE8, so compute them manually; see https://github.com/twbs/bootstrap/issues/14093
|
|
5016
|
+
rect = angular.extend({}, rect, { width: elRect.right - elRect.left, height: elRect.bottom - elRect.top });
|
|
4559
5017
|
}
|
|
5018
|
+
var elOffset = isBody ? { top: 0, left: 0 } : dimensions.offset(el),
|
|
5019
|
+
scroll = { scroll: isBody ? document.documentElement.scrollTop || document.body.scrollTop : $element.prop('scrollTop') || 0 },
|
|
5020
|
+
outerDims = isBody ? { width: document.documentElement.clientWidth, height: $window.innerHeight } : null;
|
|
4560
5021
|
|
|
4561
|
-
return angular.extend({},
|
|
5022
|
+
return angular.extend({}, rect, scroll, outerDims, elOffset);
|
|
4562
5023
|
}
|
|
4563
5024
|
|
|
4564
5025
|
function getCalculatedOffset(placement, position, actualWidth, actualHeight) {
|
|
@@ -4618,8 +5079,101 @@ angular.module('mgcrea.ngStrap.tooltip', ['mgcrea.ngStrap.helpers.dimensions'])
|
|
|
4618
5079
|
return offset;
|
|
4619
5080
|
}
|
|
4620
5081
|
|
|
4621
|
-
function
|
|
4622
|
-
|
|
5082
|
+
function applyPlacement(offset, placement) {
|
|
5083
|
+
var tip = tipElement[0],
|
|
5084
|
+
width = tip.offsetWidth,
|
|
5085
|
+
height = tip.offsetHeight;
|
|
5086
|
+
|
|
5087
|
+
// manually read margins because getBoundingClientRect includes difference
|
|
5088
|
+
var marginTop = parseInt(dimensions.css(tip, 'margin-top'), 10),
|
|
5089
|
+
marginLeft = parseInt(dimensions.css(tip, 'margin-left'), 10);
|
|
5090
|
+
|
|
5091
|
+
// we must check for NaN for ie 8/9
|
|
5092
|
+
if (isNaN(marginTop)) marginTop = 0;
|
|
5093
|
+
if (isNaN(marginLeft)) marginLeft = 0;
|
|
5094
|
+
|
|
5095
|
+
offset.top = offset.top + marginTop;
|
|
5096
|
+
offset.left = offset.left + marginLeft;
|
|
5097
|
+
|
|
5098
|
+
// dimensions setOffset doesn't round pixel values
|
|
5099
|
+
// so we use setOffset directly with our own function
|
|
5100
|
+
dimensions.setOffset(tip, angular.extend({
|
|
5101
|
+
using: function (props) {
|
|
5102
|
+
tipElement.css({
|
|
5103
|
+
top: Math.round(props.top) + 'px',
|
|
5104
|
+
left: Math.round(props.left) + 'px'
|
|
5105
|
+
});
|
|
5106
|
+
}
|
|
5107
|
+
}, offset), 0);
|
|
5108
|
+
|
|
5109
|
+
// check to see if placing tip in new offset caused the tip to resize itself
|
|
5110
|
+
var actualWidth = tip.offsetWidth,
|
|
5111
|
+
actualHeight = tip.offsetHeight;
|
|
5112
|
+
|
|
5113
|
+
if (placement === 'top' && actualHeight !== height) {
|
|
5114
|
+
offset.top = offset.top + height - actualHeight;
|
|
5115
|
+
}
|
|
5116
|
+
|
|
5117
|
+
// If it's an exotic placement, exit now instead of
|
|
5118
|
+
// applying a delta and changing the arrow
|
|
5119
|
+
if (/top-left|top-right|bottom-left|bottom-right/.test(placement)) return;
|
|
5120
|
+
|
|
5121
|
+
var delta = getViewportAdjustedDelta(placement, offset, actualWidth, actualHeight);
|
|
5122
|
+
|
|
5123
|
+
if (delta.left) {
|
|
5124
|
+
offset.left += delta.left;
|
|
5125
|
+
} else {
|
|
5126
|
+
offset.top += delta.top;
|
|
5127
|
+
}
|
|
5128
|
+
|
|
5129
|
+
dimensions.setOffset(tip, offset);
|
|
5130
|
+
|
|
5131
|
+
if (/top|right|bottom|left/.test(placement)) {
|
|
5132
|
+
var isVertical = /top|bottom/.test(placement),
|
|
5133
|
+
arrowDelta = isVertical ? delta.left * 2 - width + actualWidth : delta.top * 2 - height + actualHeight,
|
|
5134
|
+
arrowOffsetPosition = isVertical ? 'offsetWidth' : 'offsetHeight';
|
|
5135
|
+
|
|
5136
|
+
replaceArrow(arrowDelta, tip[arrowOffsetPosition], isVertical);
|
|
5137
|
+
}
|
|
5138
|
+
}
|
|
5139
|
+
|
|
5140
|
+
function getViewportAdjustedDelta(placement, position, actualWidth, actualHeight) {
|
|
5141
|
+
var delta = { top: 0, left: 0 },
|
|
5142
|
+
$viewport = options.viewport && findElement(options.viewport.selector || options.viewport);
|
|
5143
|
+
|
|
5144
|
+
if (!$viewport) {
|
|
5145
|
+
return delta;
|
|
5146
|
+
}
|
|
5147
|
+
|
|
5148
|
+
var viewportPadding = options.viewport && options.viewport.padding || 0,
|
|
5149
|
+
viewportDimensions = getPosition($viewport);
|
|
5150
|
+
|
|
5151
|
+
if (/right|left/.test(placement)) {
|
|
5152
|
+
var topEdgeOffset = position.top - viewportPadding - viewportDimensions.scroll,
|
|
5153
|
+
bottomEdgeOffset = position.top + viewportPadding - viewportDimensions.scroll + actualHeight;
|
|
5154
|
+
if (topEdgeOffset < viewportDimensions.top) { // top overflow
|
|
5155
|
+
delta.top = viewportDimensions.top - topEdgeOffset;
|
|
5156
|
+
} else if (bottomEdgeOffset > viewportDimensions.top + viewportDimensions.height) { // bottom overflow
|
|
5157
|
+
delta.top = viewportDimensions.top + viewportDimensions.height - bottomEdgeOffset;
|
|
5158
|
+
}
|
|
5159
|
+
} else {
|
|
5160
|
+
var leftEdgeOffset = position.left - viewportPadding,
|
|
5161
|
+
rightEdgeOffset = position.left + viewportPadding + actualWidth;
|
|
5162
|
+
if (leftEdgeOffset < viewportDimensions.left) { // left overflow
|
|
5163
|
+
delta.left = viewportDimensions.left - leftEdgeOffset;
|
|
5164
|
+
} else if (rightEdgeOffset > viewportDimensions.width) { // right overflow
|
|
5165
|
+
delta.left = viewportDimensions.left + viewportDimensions.width - rightEdgeOffset;
|
|
5166
|
+
}
|
|
5167
|
+
}
|
|
5168
|
+
|
|
5169
|
+
return delta;
|
|
5170
|
+
}
|
|
5171
|
+
|
|
5172
|
+
function replaceArrow(delta, dimension, isHorizontal) {
|
|
5173
|
+
var $arrow = findElement('.tooltip-arrow, .arrow', tipElement[0]);
|
|
5174
|
+
|
|
5175
|
+
$arrow.css(isHorizontal ? 'left' : 'top', 50 * (1 - delta / dimension) + '%')
|
|
5176
|
+
.css(isHorizontal ? 'top' : 'left', '');
|
|
4623
5177
|
}
|
|
4624
5178
|
|
|
4625
5179
|
function destroyTipElement() {
|
|
@@ -4664,13 +5218,8 @@ angular.module('mgcrea.ngStrap.tooltip', ['mgcrea.ngStrap.helpers.dimensions'])
|
|
|
4664
5218
|
var fetchPromises = {};
|
|
4665
5219
|
function fetchTemplate(template) {
|
|
4666
5220
|
if(fetchPromises[template]) return fetchPromises[template];
|
|
4667
|
-
return (fetchPromises[template] = $
|
|
4668
|
-
|
|
4669
|
-
if(angular.isObject(res)) {
|
|
4670
|
-
$templateCache.put(template, res.data);
|
|
4671
|
-
return res.data;
|
|
4672
|
-
}
|
|
4673
|
-
return res;
|
|
5221
|
+
return (fetchPromises[template] = $http.get(template, {cache: $templateCache}).then(function(res) {
|
|
5222
|
+
return res.data;
|
|
4674
5223
|
}));
|
|
4675
5224
|
}
|
|
4676
5225
|
|
|
@@ -4689,10 +5238,15 @@ angular.module('mgcrea.ngStrap.tooltip', ['mgcrea.ngStrap.helpers.dimensions'])
|
|
|
4689
5238
|
|
|
4690
5239
|
// Directive options
|
|
4691
5240
|
var options = {scope: scope};
|
|
4692
|
-
angular.forEach(['template', 'contentTemplate', 'placement', 'container', '
|
|
5241
|
+
angular.forEach(['template', 'contentTemplate', 'placement', 'container', 'delay', 'trigger', 'keyboard', 'html', 'animation', 'backdropAnimation', 'type', 'customClass', 'id'], function(key) {
|
|
4693
5242
|
if(angular.isDefined(attr[key])) options[key] = attr[key];
|
|
4694
5243
|
});
|
|
4695
5244
|
|
|
5245
|
+
// should not parse target attribute, only data-target
|
|
5246
|
+
if(element.attr('data-target')) {
|
|
5247
|
+
options.target = element.attr('data-target');
|
|
5248
|
+
}
|
|
5249
|
+
|
|
4696
5250
|
// overwrite inherited title value when no value specified
|
|
4697
5251
|
// fix for angular 1.3.1 531a8de72c439d8ddd064874bf364c00cedabb11
|
|
4698
5252
|
if (!scope.hasOwnProperty('title')){
|
|
@@ -4737,6 +5291,12 @@ angular.module('mgcrea.ngStrap.tooltip', ['mgcrea.ngStrap.helpers.dimensions'])
|
|
|
4737
5291
|
newValue === false ? tooltip.setEnabled(false) : tooltip.setEnabled(true);
|
|
4738
5292
|
});
|
|
4739
5293
|
|
|
5294
|
+
// Viewport support
|
|
5295
|
+
attr.viewport && scope.$watch(attr.viewport, function (newValue) {
|
|
5296
|
+
if(!tooltip || !angular.isDefined(newValue)) return;
|
|
5297
|
+
tooltip.setViewport(newValue);
|
|
5298
|
+
});
|
|
5299
|
+
|
|
4740
5300
|
// Initialize popover
|
|
4741
5301
|
var tooltip = $tooltip(element, options);
|
|
4742
5302
|
|
|
@@ -4752,263 +5312,4 @@ angular.module('mgcrea.ngStrap.tooltip', ['mgcrea.ngStrap.helpers.dimensions'])
|
|
|
4752
5312
|
|
|
4753
5313
|
}]);
|
|
4754
5314
|
|
|
4755
|
-
// Source: typeahead.js
|
|
4756
|
-
angular.module('mgcrea.ngStrap.typeahead', ['mgcrea.ngStrap.tooltip', 'mgcrea.ngStrap.helpers.parseOptions'])
|
|
4757
|
-
|
|
4758
|
-
.provider('$typeahead', function() {
|
|
4759
|
-
|
|
4760
|
-
var defaults = this.defaults = {
|
|
4761
|
-
animation: 'am-fade',
|
|
4762
|
-
prefixClass: 'typeahead',
|
|
4763
|
-
prefixEvent: '$typeahead',
|
|
4764
|
-
placement: 'bottom-left',
|
|
4765
|
-
template: 'typeahead/typeahead.tpl.html',
|
|
4766
|
-
trigger: 'focus',
|
|
4767
|
-
container: false,
|
|
4768
|
-
keyboard: true,
|
|
4769
|
-
html: false,
|
|
4770
|
-
delay: 0,
|
|
4771
|
-
minLength: 1,
|
|
4772
|
-
filter: 'filter',
|
|
4773
|
-
limit: 6,
|
|
4774
|
-
comparator: ''
|
|
4775
|
-
};
|
|
4776
|
-
|
|
4777
|
-
this.$get = ["$window", "$rootScope", "$tooltip", "$timeout", function($window, $rootScope, $tooltip, $timeout) {
|
|
4778
|
-
|
|
4779
|
-
var bodyEl = angular.element($window.document.body);
|
|
4780
|
-
|
|
4781
|
-
function TypeaheadFactory(element, controller, config) {
|
|
4782
|
-
|
|
4783
|
-
var $typeahead = {};
|
|
4784
|
-
|
|
4785
|
-
// Common vars
|
|
4786
|
-
var options = angular.extend({}, defaults, config);
|
|
4787
|
-
|
|
4788
|
-
$typeahead = $tooltip(element, options);
|
|
4789
|
-
var parentScope = config.scope;
|
|
4790
|
-
var scope = $typeahead.$scope;
|
|
4791
|
-
|
|
4792
|
-
scope.$resetMatches = function(){
|
|
4793
|
-
scope.$matches = [];
|
|
4794
|
-
scope.$activeIndex = 0;
|
|
4795
|
-
};
|
|
4796
|
-
scope.$resetMatches();
|
|
4797
|
-
|
|
4798
|
-
scope.$activate = function(index) {
|
|
4799
|
-
scope.$$postDigest(function() {
|
|
4800
|
-
$typeahead.activate(index);
|
|
4801
|
-
});
|
|
4802
|
-
};
|
|
4803
|
-
|
|
4804
|
-
scope.$select = function(index, evt) {
|
|
4805
|
-
scope.$$postDigest(function() {
|
|
4806
|
-
$typeahead.select(index);
|
|
4807
|
-
});
|
|
4808
|
-
};
|
|
4809
|
-
|
|
4810
|
-
scope.$isVisible = function() {
|
|
4811
|
-
return $typeahead.$isVisible();
|
|
4812
|
-
};
|
|
4813
|
-
|
|
4814
|
-
// Public methods
|
|
4815
|
-
|
|
4816
|
-
$typeahead.update = function(matches) {
|
|
4817
|
-
scope.$matches = matches;
|
|
4818
|
-
if(scope.$activeIndex >= matches.length) {
|
|
4819
|
-
scope.$activeIndex = 0;
|
|
4820
|
-
}
|
|
4821
|
-
};
|
|
4822
|
-
|
|
4823
|
-
$typeahead.activate = function(index) {
|
|
4824
|
-
scope.$activeIndex = index;
|
|
4825
|
-
};
|
|
4826
|
-
|
|
4827
|
-
$typeahead.select = function(index) {
|
|
4828
|
-
var value = scope.$matches[index].value;
|
|
4829
|
-
// console.log('$setViewValue', value);
|
|
4830
|
-
controller.$setViewValue(value);
|
|
4831
|
-
controller.$render();
|
|
4832
|
-
scope.$resetMatches();
|
|
4833
|
-
if(parentScope) parentScope.$digest();
|
|
4834
|
-
// Emit event
|
|
4835
|
-
scope.$emit(options.prefixEvent + '.select', value, index, $typeahead);
|
|
4836
|
-
};
|
|
4837
|
-
|
|
4838
|
-
// Protected methods
|
|
4839
|
-
|
|
4840
|
-
$typeahead.$isVisible = function() {
|
|
4841
|
-
if(!options.minLength || !controller) {
|
|
4842
|
-
return !!scope.$matches.length;
|
|
4843
|
-
}
|
|
4844
|
-
// minLength support
|
|
4845
|
-
return scope.$matches.length && angular.isString(controller.$viewValue) && controller.$viewValue.length >= options.minLength;
|
|
4846
|
-
};
|
|
4847
|
-
|
|
4848
|
-
$typeahead.$getIndex = function(value) {
|
|
4849
|
-
var l = scope.$matches.length, i = l;
|
|
4850
|
-
if(!l) return;
|
|
4851
|
-
for(i = l; i--;) {
|
|
4852
|
-
if(scope.$matches[i].value === value) break;
|
|
4853
|
-
}
|
|
4854
|
-
if(i < 0) return;
|
|
4855
|
-
return i;
|
|
4856
|
-
};
|
|
4857
|
-
|
|
4858
|
-
$typeahead.$onMouseDown = function(evt) {
|
|
4859
|
-
// Prevent blur on mousedown
|
|
4860
|
-
evt.preventDefault();
|
|
4861
|
-
evt.stopPropagation();
|
|
4862
|
-
};
|
|
4863
|
-
|
|
4864
|
-
$typeahead.$onKeyDown = function(evt) {
|
|
4865
|
-
if(!/(38|40|13)/.test(evt.keyCode)) return;
|
|
4866
|
-
|
|
4867
|
-
// Let ngSubmit pass if the typeahead tip is hidden
|
|
4868
|
-
if($typeahead.$isVisible()) {
|
|
4869
|
-
evt.preventDefault();
|
|
4870
|
-
evt.stopPropagation();
|
|
4871
|
-
}
|
|
4872
|
-
|
|
4873
|
-
// Select with enter
|
|
4874
|
-
if(evt.keyCode === 13 && scope.$matches.length) {
|
|
4875
|
-
$typeahead.select(scope.$activeIndex);
|
|
4876
|
-
}
|
|
4877
|
-
|
|
4878
|
-
// Navigate with keyboard
|
|
4879
|
-
else if(evt.keyCode === 38 && scope.$activeIndex > 0) scope.$activeIndex--;
|
|
4880
|
-
else if(evt.keyCode === 40 && scope.$activeIndex < scope.$matches.length - 1) scope.$activeIndex++;
|
|
4881
|
-
else if(angular.isUndefined(scope.$activeIndex)) scope.$activeIndex = 0;
|
|
4882
|
-
scope.$digest();
|
|
4883
|
-
};
|
|
4884
|
-
|
|
4885
|
-
// Overrides
|
|
4886
|
-
|
|
4887
|
-
var show = $typeahead.show;
|
|
4888
|
-
$typeahead.show = function() {
|
|
4889
|
-
show();
|
|
4890
|
-
// use timeout to hookup the events to prevent
|
|
4891
|
-
// event bubbling from being processed imediately.
|
|
4892
|
-
$timeout(function() {
|
|
4893
|
-
$typeahead.$element.on('mousedown', $typeahead.$onMouseDown);
|
|
4894
|
-
if(options.keyboard) {
|
|
4895
|
-
element.on('keydown', $typeahead.$onKeyDown);
|
|
4896
|
-
}
|
|
4897
|
-
}, 0, false);
|
|
4898
|
-
};
|
|
4899
|
-
|
|
4900
|
-
var hide = $typeahead.hide;
|
|
4901
|
-
$typeahead.hide = function() {
|
|
4902
|
-
$typeahead.$element.off('mousedown', $typeahead.$onMouseDown);
|
|
4903
|
-
if(options.keyboard) {
|
|
4904
|
-
element.off('keydown', $typeahead.$onKeyDown);
|
|
4905
|
-
}
|
|
4906
|
-
hide();
|
|
4907
|
-
};
|
|
4908
|
-
|
|
4909
|
-
return $typeahead;
|
|
4910
|
-
|
|
4911
|
-
}
|
|
4912
|
-
|
|
4913
|
-
TypeaheadFactory.defaults = defaults;
|
|
4914
|
-
return TypeaheadFactory;
|
|
4915
|
-
|
|
4916
|
-
}];
|
|
4917
|
-
|
|
4918
|
-
})
|
|
4919
|
-
|
|
4920
|
-
.directive('bsTypeahead', ["$window", "$parse", "$q", "$typeahead", "$parseOptions", function($window, $parse, $q, $typeahead, $parseOptions) {
|
|
4921
|
-
|
|
4922
|
-
var defaults = $typeahead.defaults;
|
|
4923
|
-
|
|
4924
|
-
return {
|
|
4925
|
-
restrict: 'EAC',
|
|
4926
|
-
require: 'ngModel',
|
|
4927
|
-
link: function postLink(scope, element, attr, controller) {
|
|
4928
|
-
|
|
4929
|
-
// Directive options
|
|
4930
|
-
var options = {scope: scope};
|
|
4931
|
-
angular.forEach(['placement', 'container', 'delay', 'trigger', 'keyboard', 'html', 'animation', 'template', 'filter', 'limit', 'minLength', 'watchOptions', 'selectMode', 'comparator', 'id'], function(key) {
|
|
4932
|
-
if(angular.isDefined(attr[key])) options[key] = attr[key];
|
|
4933
|
-
});
|
|
4934
|
-
|
|
4935
|
-
// Build proper ngOptions
|
|
4936
|
-
var filter = options.filter || defaults.filter;
|
|
4937
|
-
var limit = options.limit || defaults.limit;
|
|
4938
|
-
var comparator = options.comparator || defaults.comparator;
|
|
4939
|
-
|
|
4940
|
-
var ngOptions = attr.ngOptions;
|
|
4941
|
-
if(filter) ngOptions += ' | ' + filter + ':$viewValue';
|
|
4942
|
-
if (comparator) ngOptions += ':' + comparator;
|
|
4943
|
-
if(limit) ngOptions += ' | limitTo:' + limit;
|
|
4944
|
-
var parsedOptions = $parseOptions(ngOptions);
|
|
4945
|
-
|
|
4946
|
-
// Initialize typeahead
|
|
4947
|
-
var typeahead = $typeahead(element, controller, options);
|
|
4948
|
-
|
|
4949
|
-
// Watch options on demand
|
|
4950
|
-
if(options.watchOptions) {
|
|
4951
|
-
// Watch ngOptions values before filtering for changes, drop function calls
|
|
4952
|
-
var watchedOptions = parsedOptions.$match[7].replace(/\|.+/, '').replace(/\(.*\)/g, '').trim();
|
|
4953
|
-
scope.$watch(watchedOptions, function (newValue, oldValue) {
|
|
4954
|
-
// console.warn('scope.$watch(%s)', watchedOptions, newValue, oldValue);
|
|
4955
|
-
parsedOptions.valuesFn(scope, controller).then(function (values) {
|
|
4956
|
-
typeahead.update(values);
|
|
4957
|
-
controller.$render();
|
|
4958
|
-
});
|
|
4959
|
-
}, true);
|
|
4960
|
-
}
|
|
4961
|
-
|
|
4962
|
-
// Watch model for changes
|
|
4963
|
-
scope.$watch(attr.ngModel, function(newValue, oldValue) {
|
|
4964
|
-
// console.warn('$watch', element.attr('ng-model'), newValue);
|
|
4965
|
-
scope.$modelValue = newValue; // Publish modelValue on scope for custom templates
|
|
4966
|
-
parsedOptions.valuesFn(scope, controller)
|
|
4967
|
-
.then(function(values) {
|
|
4968
|
-
// Prevent input with no future prospect if selectMode is truthy
|
|
4969
|
-
// @TODO test selectMode
|
|
4970
|
-
if(options.selectMode && !values.length && newValue.length > 0) {
|
|
4971
|
-
controller.$setViewValue(controller.$viewValue.substring(0, controller.$viewValue.length - 1));
|
|
4972
|
-
return;
|
|
4973
|
-
}
|
|
4974
|
-
if(values.length > limit) values = values.slice(0, limit);
|
|
4975
|
-
var isVisible = typeahead.$isVisible();
|
|
4976
|
-
isVisible && typeahead.update(values);
|
|
4977
|
-
// Do not re-queue an update if a correct value has been selected
|
|
4978
|
-
if(values.length === 1 && values[0].value === newValue) return;
|
|
4979
|
-
!isVisible && typeahead.update(values);
|
|
4980
|
-
// Queue a new rendering that will leverage collection loading
|
|
4981
|
-
controller.$render();
|
|
4982
|
-
});
|
|
4983
|
-
});
|
|
4984
|
-
|
|
4985
|
-
// modelValue -> $formatters -> viewValue
|
|
4986
|
-
controller.$formatters.push(function(modelValue) {
|
|
4987
|
-
// console.warn('$formatter("%s"): modelValue=%o (%o)', element.attr('ng-model'), modelValue, typeof modelValue);
|
|
4988
|
-
var displayValue = parsedOptions.displayValue(modelValue);
|
|
4989
|
-
return displayValue === undefined ? '' : displayValue;
|
|
4990
|
-
});
|
|
4991
|
-
|
|
4992
|
-
// Model rendering in view
|
|
4993
|
-
controller.$render = function () {
|
|
4994
|
-
// console.warn('$render', element.attr('ng-model'), 'controller.$modelValue', typeof controller.$modelValue, controller.$modelValue, 'controller.$viewValue', typeof controller.$viewValue, controller.$viewValue);
|
|
4995
|
-
if(controller.$isEmpty(controller.$viewValue)) return element.val('');
|
|
4996
|
-
var index = typeahead.$getIndex(controller.$modelValue);
|
|
4997
|
-
var selected = angular.isDefined(index) ? typeahead.$scope.$matches[index].label : controller.$viewValue;
|
|
4998
|
-
selected = angular.isObject(selected) ? parsedOptions.displayValue(selected) : selected;
|
|
4999
|
-
element.val(selected ? selected.toString().replace(/<(?:.|\n)*?>/gm, '').trim() : '');
|
|
5000
|
-
};
|
|
5001
|
-
|
|
5002
|
-
// Garbage collection
|
|
5003
|
-
scope.$on('$destroy', function() {
|
|
5004
|
-
if (typeahead) typeahead.destroy();
|
|
5005
|
-
options = null;
|
|
5006
|
-
typeahead = null;
|
|
5007
|
-
});
|
|
5008
|
-
|
|
5009
|
-
}
|
|
5010
|
-
};
|
|
5011
|
-
|
|
5012
|
-
}]);
|
|
5013
|
-
|
|
5014
5315
|
})(window, document);
|