angular-strap-rails 2.0.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 +7 -0
- data/.DS_Store +0 -0
- data/.gitignore +17 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +45 -0
- data/Rakefile +1 -0
- data/angular-strap-rails.gemspec +15 -0
- data/lib/angular-strap-rails.rb +8 -0
- data/lib/angular-strap-rails/version.rb +5 -0
- data/vendor/.DS_Store +0 -0
- data/vendor/assets/.DS_Store +0 -0
- data/vendor/assets/javascripts/.DS_Store +0 -0
- data/vendor/assets/javascripts/angular-strap.coffee +0 -0
- data/vendor/assets/javascripts/angular-strap/.DS_Store +0 -0
- data/vendor/assets/javascripts/angular-strap/datepicker.coffee +11 -0
- data/vendor/assets/javascripts/angular-strap/modal.coffee +9 -0
- data/vendor/assets/javascripts/dist/angular-strap.js +3682 -0
- data/vendor/assets/javascripts/dist/angular-strap.tpl.js +100 -0
- data/vendor/assets/javascripts/dist/modules/affix.js +191 -0
- data/vendor/assets/javascripts/dist/modules/alert.js +114 -0
- data/vendor/assets/javascripts/dist/modules/alert.tpl.js +14 -0
- data/vendor/assets/javascripts/dist/modules/aside.js +96 -0
- data/vendor/assets/javascripts/dist/modules/aside.tpl.js +14 -0
- data/vendor/assets/javascripts/dist/modules/button.js +141 -0
- data/vendor/assets/javascripts/dist/modules/date-parser.js +150 -0
- data/vendor/assets/javascripts/dist/modules/datepicker.js +583 -0
- data/vendor/assets/javascripts/dist/modules/datepicker.tpl.js +14 -0
- data/vendor/assets/javascripts/dist/modules/debounce.js +60 -0
- data/vendor/assets/javascripts/dist/modules/dimensions.js +142 -0
- data/vendor/assets/javascripts/dist/modules/dropdown.js +124 -0
- data/vendor/assets/javascripts/dist/modules/dropdown.tpl.js +14 -0
- data/vendor/assets/javascripts/dist/modules/modal.js +282 -0
- data/vendor/assets/javascripts/dist/modules/modal.tpl.js +14 -0
- data/vendor/assets/javascripts/dist/modules/navbar.js +55 -0
- data/vendor/assets/javascripts/dist/modules/parse-options.js +51 -0
- data/vendor/assets/javascripts/dist/modules/popover.js +100 -0
- data/vendor/assets/javascripts/dist/modules/popover.tpl.js +14 -0
- data/vendor/assets/javascripts/dist/modules/raf.js +45 -0
- data/vendor/assets/javascripts/dist/modules/scrollspy.js +229 -0
- data/vendor/assets/javascripts/dist/modules/select.js +281 -0
- data/vendor/assets/javascripts/dist/modules/select.tpl.js +14 -0
- data/vendor/assets/javascripts/dist/modules/tab.js +69 -0
- data/vendor/assets/javascripts/dist/modules/tab.tpl.js +14 -0
- data/vendor/assets/javascripts/dist/modules/timepicker.js +430 -0
- data/vendor/assets/javascripts/dist/modules/timepicker.tpl.js +14 -0
- data/vendor/assets/javascripts/dist/modules/tooltip.js +405 -0
- data/vendor/assets/javascripts/dist/modules/tooltip.tpl.js +14 -0
- data/vendor/assets/javascripts/dist/modules/typeahead.js +225 -0
- data/vendor/assets/javascripts/dist/modules/typeahead.tpl.js +14 -0
- data/vendor/assets/stylesheets/angular-strap.css +564 -0
- metadata +94 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA1:
|
|
3
|
+
metadata.gz: f48c1d5e6939a86e897fd4976bff5f1c3e3c2710
|
|
4
|
+
data.tar.gz: afa4a5447262e4d7eb0662b7dd43301ea7494a78
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: e5bc176a9818048aee7c3947a9e1ade2390595f26724df8303c95f23c199c15a7b93dd225470fd72dde77d5f48d1a4e339c3d484af9f439a925c91e11131eae1
|
|
7
|
+
data.tar.gz: 6f94c9436fdb28da7c7887906a9aec9a0084bfba35624095d38c3bda77293c13c65ae326b874742b8d309b9bff3f2d1400402e984f906fec63d19216e6c31099
|
data/.DS_Store
ADDED
|
Binary file
|
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
Copyright (c) 2013 Vulpi Shu
|
|
2
|
+
|
|
3
|
+
MIT License
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
|
6
|
+
a copy of this software and associated documentation files (the
|
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
|
11
|
+
the following conditions:
|
|
12
|
+
|
|
13
|
+
The above copyright notice and this permission notice shall be
|
|
14
|
+
included in all copies or substantial portions of the Software.
|
|
15
|
+
|
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# AngularStrap-Rails
|
|
2
|
+
|
|
3
|
+
angular-strap-rails adds AngularStrap
|
|
4
|
+
set of angular directives to your rails asset pipeline.
|
|
5
|
+
|
|
6
|
+
## Usage
|
|
7
|
+
|
|
8
|
+
To install put this in your Gemfile
|
|
9
|
+
|
|
10
|
+
```Gemfile
|
|
11
|
+
gem 'angular-strap-rails'
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
or
|
|
15
|
+
|
|
16
|
+
```Gemfile
|
|
17
|
+
gem 'angular-strap-rails', github: 'vulpi-shu/angular-strap-rails'
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
Then add the following to your application.js
|
|
21
|
+
```Javascript
|
|
22
|
+
//= require angular-strap
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
You may also need to include the following in application.js
|
|
26
|
+
```Javascript
|
|
27
|
+
//= require angular-strap/datepicker
|
|
28
|
+
//= require angular-strap/select
|
|
29
|
+
//= require angular-strap/datepicker
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
And this in your aplication.css.sass
|
|
33
|
+
```Sass
|
|
34
|
+
# require angular-strap
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
For further information on how to use it refer to AngularStrap
|
|
38
|
+
|
|
39
|
+
## Contributing
|
|
40
|
+
|
|
41
|
+
1. Fork it
|
|
42
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
|
43
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
|
44
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
|
45
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
require "bundler/gem_tasks"
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# coding: utf-8
|
|
2
|
+
require File.expand_path('../lib/angular-strap-rails/version', __FILE__)
|
|
3
|
+
|
|
4
|
+
Gem::Specification.new do |spec|
|
|
5
|
+
spec.name = 'angular-strap-rails'
|
|
6
|
+
spec.version = AngularStrap::Rails::VERSION
|
|
7
|
+
spec.authors = ['Vulpi Shu']
|
|
8
|
+
spec.email = ['vulpi.shu@gmail.com']
|
|
9
|
+
spec.description = %q{AngularStrap for rails.}
|
|
10
|
+
spec.summary = %q{This gem adds AngularStrap to your rails asset pipeline.}
|
|
11
|
+
spec.homepage = ''
|
|
12
|
+
spec.license = 'MIT'
|
|
13
|
+
|
|
14
|
+
spec.files = `git ls-files`.split($/)
|
|
15
|
+
end
|
data/vendor/.DS_Store
ADDED
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
File without changes
|
|
Binary file
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
#= require ../dist/modules/raf.js
|
|
2
|
+
|
|
3
|
+
#= require ../dist/modules/dimensions.js
|
|
4
|
+
|
|
5
|
+
#= require ../dist/modules/tooltip.js
|
|
6
|
+
#= require ../dist/modules/tooltip.tpl.js
|
|
7
|
+
|
|
8
|
+
#= require ../dist/modules/date-parser.js
|
|
9
|
+
|
|
10
|
+
#= require ../dist/modules/datepicker.js
|
|
11
|
+
#= require ../dist/modules/datepicker.tpl.js
|
|
@@ -0,0 +1,3682 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* angular-strap
|
|
3
|
+
* @version v2.0.1 - 2014-04-10
|
|
4
|
+
* @link http://mgcrea.github.io/angular-strap
|
|
5
|
+
* @author Olivier Louvignes (olivier@mg-crea.com)
|
|
6
|
+
* @license MIT License, http://www.opensource.org/licenses/MIT
|
|
7
|
+
*/
|
|
8
|
+
(function(window, document, undefined) {
|
|
9
|
+
'use strict';
|
|
10
|
+
// Source: module.js
|
|
11
|
+
angular.module('mgcrea.ngStrap', [
|
|
12
|
+
'mgcrea.ngStrap.modal',
|
|
13
|
+
'mgcrea.ngStrap.aside',
|
|
14
|
+
'mgcrea.ngStrap.alert',
|
|
15
|
+
'mgcrea.ngStrap.button',
|
|
16
|
+
'mgcrea.ngStrap.select',
|
|
17
|
+
'mgcrea.ngStrap.datepicker',
|
|
18
|
+
'mgcrea.ngStrap.timepicker',
|
|
19
|
+
'mgcrea.ngStrap.navbar',
|
|
20
|
+
'mgcrea.ngStrap.tooltip',
|
|
21
|
+
'mgcrea.ngStrap.popover',
|
|
22
|
+
'mgcrea.ngStrap.dropdown',
|
|
23
|
+
'mgcrea.ngStrap.typeahead',
|
|
24
|
+
'mgcrea.ngStrap.scrollspy',
|
|
25
|
+
'mgcrea.ngStrap.affix',
|
|
26
|
+
'mgcrea.ngStrap.tab'
|
|
27
|
+
]);
|
|
28
|
+
|
|
29
|
+
// Source: affix.js
|
|
30
|
+
angular.module('mgcrea.ngStrap.affix', [
|
|
31
|
+
'mgcrea.ngStrap.helpers.dimensions',
|
|
32
|
+
'mgcrea.ngStrap.helpers.debounce'
|
|
33
|
+
]).provider('$affix', function () {
|
|
34
|
+
var defaults = this.defaults = { offsetTop: 'auto' };
|
|
35
|
+
this.$get = [
|
|
36
|
+
'$window',
|
|
37
|
+
'debounce',
|
|
38
|
+
'dimensions',
|
|
39
|
+
function ($window, debounce, dimensions) {
|
|
40
|
+
var bodyEl = angular.element($window.document.body);
|
|
41
|
+
var windowEl = angular.element($window);
|
|
42
|
+
function AffixFactory(element, config) {
|
|
43
|
+
var $affix = {};
|
|
44
|
+
// Common vars
|
|
45
|
+
var options = angular.extend({}, defaults, config);
|
|
46
|
+
var targetEl = options.target;
|
|
47
|
+
// Initial private vars
|
|
48
|
+
var reset = 'affix affix-top affix-bottom', initialAffixTop = 0, initialOffsetTop = 0, offsetTop = 0, offsetBottom = 0, affixed = null, unpin = null;
|
|
49
|
+
var parent = element.parent();
|
|
50
|
+
// Options: custom parent
|
|
51
|
+
if (options.offsetParent) {
|
|
52
|
+
if (options.offsetParent.match(/^\d+$/)) {
|
|
53
|
+
for (var i = 0; i < options.offsetParent * 1 - 1; i++) {
|
|
54
|
+
parent = parent.parent();
|
|
55
|
+
}
|
|
56
|
+
} else {
|
|
57
|
+
parent = angular.element(options.offsetParent);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
$affix.init = function () {
|
|
61
|
+
$affix.$parseOffsets();
|
|
62
|
+
initialOffsetTop = dimensions.offset(element[0]).top + initialAffixTop;
|
|
63
|
+
// Bind events
|
|
64
|
+
targetEl.on('scroll', $affix.checkPosition);
|
|
65
|
+
targetEl.on('click', $affix.checkPositionWithEventLoop);
|
|
66
|
+
windowEl.on('resize', $affix.$debouncedOnResize);
|
|
67
|
+
// Both of these checkPosition() calls are necessary for the case where
|
|
68
|
+
// the user hits refresh after scrolling to the bottom of the page.
|
|
69
|
+
$affix.checkPosition();
|
|
70
|
+
$affix.checkPositionWithEventLoop();
|
|
71
|
+
};
|
|
72
|
+
$affix.destroy = function () {
|
|
73
|
+
// Unbind events
|
|
74
|
+
targetEl.off('scroll', $affix.checkPosition);
|
|
75
|
+
targetEl.off('click', $affix.checkPositionWithEventLoop);
|
|
76
|
+
windowEl.off('resize', $affix.$debouncedOnResize);
|
|
77
|
+
};
|
|
78
|
+
$affix.checkPositionWithEventLoop = function () {
|
|
79
|
+
setTimeout($affix.checkPosition, 1);
|
|
80
|
+
};
|
|
81
|
+
$affix.checkPosition = function () {
|
|
82
|
+
// if (!this.$element.is(':visible')) return
|
|
83
|
+
var scrollTop = getScrollTop();
|
|
84
|
+
var position = dimensions.offset(element[0]);
|
|
85
|
+
var elementHeight = dimensions.height(element[0]);
|
|
86
|
+
// Get required affix class according to position
|
|
87
|
+
var affix = getRequiredAffixClass(unpin, position, elementHeight);
|
|
88
|
+
// Did affix status changed this last check?
|
|
89
|
+
if (affixed === affix)
|
|
90
|
+
return;
|
|
91
|
+
affixed = affix;
|
|
92
|
+
// Add proper affix class
|
|
93
|
+
element.removeClass(reset).addClass('affix' + (affix !== 'middle' ? '-' + affix : ''));
|
|
94
|
+
if (affix === 'top') {
|
|
95
|
+
unpin = null;
|
|
96
|
+
element.css('position', options.offsetParent ? '' : 'relative');
|
|
97
|
+
element.css('top', '');
|
|
98
|
+
} else if (affix === 'bottom') {
|
|
99
|
+
if (options.offsetUnpin) {
|
|
100
|
+
unpin = -(options.offsetUnpin * 1);
|
|
101
|
+
} else {
|
|
102
|
+
// Calculate unpin threshold when affixed to bottom.
|
|
103
|
+
// Hopefully the browser scrolls pixel by pixel.
|
|
104
|
+
unpin = position.top - scrollTop;
|
|
105
|
+
}
|
|
106
|
+
element.css('position', options.offsetParent ? '' : 'relative');
|
|
107
|
+
element.css('top', options.offsetParent ? '' : bodyEl[0].offsetHeight - offsetBottom - elementHeight - initialOffsetTop + 'px');
|
|
108
|
+
} else {
|
|
109
|
+
// affix === 'middle'
|
|
110
|
+
unpin = null;
|
|
111
|
+
element.css('position', 'fixed');
|
|
112
|
+
element.css('top', initialAffixTop + 'px');
|
|
113
|
+
}
|
|
114
|
+
};
|
|
115
|
+
$affix.$onResize = function () {
|
|
116
|
+
$affix.$parseOffsets();
|
|
117
|
+
$affix.checkPosition();
|
|
118
|
+
};
|
|
119
|
+
$affix.$debouncedOnResize = debounce($affix.$onResize, 50);
|
|
120
|
+
$affix.$parseOffsets = function () {
|
|
121
|
+
// Reset position to calculate correct offsetTop
|
|
122
|
+
element.css('position', options.offsetParent ? '' : 'relative');
|
|
123
|
+
if (options.offsetTop) {
|
|
124
|
+
if (options.offsetTop === 'auto') {
|
|
125
|
+
options.offsetTop = '+0';
|
|
126
|
+
}
|
|
127
|
+
if (options.offsetTop.match(/^[-+]\d+$/)) {
|
|
128
|
+
initialAffixTop = -options.offsetTop * 1;
|
|
129
|
+
if (options.offsetParent) {
|
|
130
|
+
offsetTop = dimensions.offset(parent[0]).top + options.offsetTop * 1;
|
|
131
|
+
} else {
|
|
132
|
+
offsetTop = dimensions.offset(element[0]).top - dimensions.css(element[0], 'marginTop', true) + options.offsetTop * 1;
|
|
133
|
+
}
|
|
134
|
+
} else {
|
|
135
|
+
offsetTop = options.offsetTop * 1;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
if (options.offsetBottom) {
|
|
139
|
+
if (options.offsetParent && options.offsetBottom.match(/^[-+]\d+$/)) {
|
|
140
|
+
// add 1 pixel due to rounding problems...
|
|
141
|
+
offsetBottom = getScrollHeight() - (dimensions.offset(parent[0]).top + dimensions.height(parent[0])) + options.offsetBottom * 1 + 1;
|
|
142
|
+
} else {
|
|
143
|
+
offsetBottom = options.offsetBottom * 1;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
};
|
|
147
|
+
// Private methods
|
|
148
|
+
function getRequiredAffixClass(unpin, position, elementHeight) {
|
|
149
|
+
var scrollTop = getScrollTop();
|
|
150
|
+
var scrollHeight = getScrollHeight();
|
|
151
|
+
if (scrollTop <= offsetTop) {
|
|
152
|
+
return 'top';
|
|
153
|
+
} else if (unpin !== null && scrollTop + unpin <= position.top) {
|
|
154
|
+
return 'middle';
|
|
155
|
+
} else if (offsetBottom !== null && position.top + elementHeight + initialAffixTop >= scrollHeight - offsetBottom) {
|
|
156
|
+
return 'bottom';
|
|
157
|
+
} else {
|
|
158
|
+
return 'middle';
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
function getScrollTop() {
|
|
162
|
+
return targetEl[0] === $window ? $window.pageYOffset : targetEl[0] === $window;
|
|
163
|
+
}
|
|
164
|
+
function getScrollHeight() {
|
|
165
|
+
return targetEl[0] === $window ? $window.document.body.scrollHeight : targetEl[0].scrollHeight;
|
|
166
|
+
}
|
|
167
|
+
$affix.init();
|
|
168
|
+
return $affix;
|
|
169
|
+
}
|
|
170
|
+
return AffixFactory;
|
|
171
|
+
}
|
|
172
|
+
];
|
|
173
|
+
}).directive('bsAffix', [
|
|
174
|
+
'$affix',
|
|
175
|
+
'$window',
|
|
176
|
+
function ($affix, $window) {
|
|
177
|
+
return {
|
|
178
|
+
restrict: 'EAC',
|
|
179
|
+
require: '^?bsAffixTarget',
|
|
180
|
+
link: function postLink(scope, element, attr, affixTarget) {
|
|
181
|
+
var options = {
|
|
182
|
+
scope: scope,
|
|
183
|
+
offsetTop: 'auto',
|
|
184
|
+
target: affixTarget ? affixTarget.$element : angular.element($window)
|
|
185
|
+
};
|
|
186
|
+
angular.forEach([
|
|
187
|
+
'offsetTop',
|
|
188
|
+
'offsetBottom',
|
|
189
|
+
'offsetParent',
|
|
190
|
+
'offsetUnpin'
|
|
191
|
+
], function (key) {
|
|
192
|
+
if (angular.isDefined(attr[key]))
|
|
193
|
+
options[key] = attr[key];
|
|
194
|
+
});
|
|
195
|
+
var affix = $affix(element, options);
|
|
196
|
+
scope.$on('$destroy', function () {
|
|
197
|
+
options = null;
|
|
198
|
+
affix = null;
|
|
199
|
+
});
|
|
200
|
+
}
|
|
201
|
+
};
|
|
202
|
+
}
|
|
203
|
+
]).directive('bsAffixTarget', function () {
|
|
204
|
+
return {
|
|
205
|
+
controller: [
|
|
206
|
+
'$element',
|
|
207
|
+
function ($element) {
|
|
208
|
+
this.$element = $element;
|
|
209
|
+
}
|
|
210
|
+
]
|
|
211
|
+
};
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
// Source: alert.js
|
|
215
|
+
// @BUG: following snippet won't compile correctly
|
|
216
|
+
// @TODO: submit issue to core
|
|
217
|
+
// '<span ng-if="title"><strong ng-bind="title"></strong> </span><span ng-bind-html="content"></span>' +
|
|
218
|
+
angular.module('mgcrea.ngStrap.alert', ['mgcrea.ngStrap.modal']).provider('$alert', function () {
|
|
219
|
+
var defaults = this.defaults = {
|
|
220
|
+
animation: 'am-fade',
|
|
221
|
+
prefixClass: 'alert',
|
|
222
|
+
placement: null,
|
|
223
|
+
template: 'alert/alert.tpl.html',
|
|
224
|
+
container: false,
|
|
225
|
+
element: null,
|
|
226
|
+
backdrop: false,
|
|
227
|
+
keyboard: true,
|
|
228
|
+
show: true,
|
|
229
|
+
duration: false,
|
|
230
|
+
type: false
|
|
231
|
+
};
|
|
232
|
+
this.$get = [
|
|
233
|
+
'$modal',
|
|
234
|
+
'$timeout',
|
|
235
|
+
function ($modal, $timeout) {
|
|
236
|
+
function AlertFactory(config) {
|
|
237
|
+
var $alert = {};
|
|
238
|
+
// Common vars
|
|
239
|
+
var options = angular.extend({}, defaults, config);
|
|
240
|
+
$alert = $modal(options);
|
|
241
|
+
// Support scope as string options [/*title, content, */type]
|
|
242
|
+
if (options.type) {
|
|
243
|
+
$alert.$scope.type = options.type;
|
|
244
|
+
}
|
|
245
|
+
// Support auto-close duration
|
|
246
|
+
var show = $alert.show;
|
|
247
|
+
if (options.duration) {
|
|
248
|
+
$alert.show = function () {
|
|
249
|
+
show();
|
|
250
|
+
$timeout(function () {
|
|
251
|
+
$alert.hide();
|
|
252
|
+
}, options.duration * 1000);
|
|
253
|
+
};
|
|
254
|
+
}
|
|
255
|
+
return $alert;
|
|
256
|
+
}
|
|
257
|
+
return AlertFactory;
|
|
258
|
+
}
|
|
259
|
+
];
|
|
260
|
+
}).directive('bsAlert', [
|
|
261
|
+
'$window',
|
|
262
|
+
'$location',
|
|
263
|
+
'$sce',
|
|
264
|
+
'$alert',
|
|
265
|
+
function ($window, $location, $sce, $alert) {
|
|
266
|
+
var requestAnimationFrame = $window.requestAnimationFrame || $window.setTimeout;
|
|
267
|
+
return {
|
|
268
|
+
restrict: 'EAC',
|
|
269
|
+
scope: true,
|
|
270
|
+
link: function postLink(scope, element, attr, transclusion) {
|
|
271
|
+
// Directive options
|
|
272
|
+
var options = {
|
|
273
|
+
scope: scope,
|
|
274
|
+
element: element,
|
|
275
|
+
show: false
|
|
276
|
+
};
|
|
277
|
+
angular.forEach([
|
|
278
|
+
'template',
|
|
279
|
+
'placement',
|
|
280
|
+
'keyboard',
|
|
281
|
+
'html',
|
|
282
|
+
'container',
|
|
283
|
+
'animation',
|
|
284
|
+
'duration'
|
|
285
|
+
], function (key) {
|
|
286
|
+
if (angular.isDefined(attr[key]))
|
|
287
|
+
options[key] = attr[key];
|
|
288
|
+
});
|
|
289
|
+
// Support scope as data-attrs
|
|
290
|
+
angular.forEach([
|
|
291
|
+
'title',
|
|
292
|
+
'content',
|
|
293
|
+
'type'
|
|
294
|
+
], function (key) {
|
|
295
|
+
attr[key] && attr.$observe(key, function (newValue, oldValue) {
|
|
296
|
+
scope[key] = $sce.trustAsHtml(newValue);
|
|
297
|
+
});
|
|
298
|
+
});
|
|
299
|
+
// Support scope as an object
|
|
300
|
+
attr.bsAlert && scope.$watch(attr.bsAlert, function (newValue, oldValue) {
|
|
301
|
+
if (angular.isObject(newValue)) {
|
|
302
|
+
angular.extend(scope, newValue);
|
|
303
|
+
} else {
|
|
304
|
+
scope.content = newValue;
|
|
305
|
+
}
|
|
306
|
+
}, true);
|
|
307
|
+
// Initialize alert
|
|
308
|
+
var alert = $alert(options);
|
|
309
|
+
// Trigger
|
|
310
|
+
element.on(attr.trigger || 'click', alert.toggle);
|
|
311
|
+
// Garbage collection
|
|
312
|
+
scope.$on('$destroy', function () {
|
|
313
|
+
alert.destroy();
|
|
314
|
+
options = null;
|
|
315
|
+
alert = null;
|
|
316
|
+
});
|
|
317
|
+
}
|
|
318
|
+
};
|
|
319
|
+
}
|
|
320
|
+
]);
|
|
321
|
+
|
|
322
|
+
// Source: aside.js
|
|
323
|
+
angular.module('mgcrea.ngStrap.aside', ['mgcrea.ngStrap.modal']).provider('$aside', function () {
|
|
324
|
+
var defaults = this.defaults = {
|
|
325
|
+
animation: 'am-fade-and-slide-right',
|
|
326
|
+
prefixClass: 'aside',
|
|
327
|
+
placement: 'right',
|
|
328
|
+
template: 'aside/aside.tpl.html',
|
|
329
|
+
contentTemplate: false,
|
|
330
|
+
container: false,
|
|
331
|
+
element: null,
|
|
332
|
+
backdrop: true,
|
|
333
|
+
keyboard: true,
|
|
334
|
+
html: false,
|
|
335
|
+
show: true
|
|
336
|
+
};
|
|
337
|
+
this.$get = [
|
|
338
|
+
'$modal',
|
|
339
|
+
function ($modal) {
|
|
340
|
+
function AsideFactory(config) {
|
|
341
|
+
var $aside = {};
|
|
342
|
+
// Common vars
|
|
343
|
+
var options = angular.extend({}, defaults, config);
|
|
344
|
+
$aside = $modal(options);
|
|
345
|
+
return $aside;
|
|
346
|
+
}
|
|
347
|
+
return AsideFactory;
|
|
348
|
+
}
|
|
349
|
+
];
|
|
350
|
+
}).directive('bsAside', [
|
|
351
|
+
'$window',
|
|
352
|
+
'$location',
|
|
353
|
+
'$sce',
|
|
354
|
+
'$aside',
|
|
355
|
+
function ($window, $location, $sce, $aside) {
|
|
356
|
+
var requestAnimationFrame = $window.requestAnimationFrame || $window.setTimeout;
|
|
357
|
+
return {
|
|
358
|
+
restrict: 'EAC',
|
|
359
|
+
scope: true,
|
|
360
|
+
link: function postLink(scope, element, attr, transclusion) {
|
|
361
|
+
// Directive options
|
|
362
|
+
var options = {
|
|
363
|
+
scope: scope,
|
|
364
|
+
element: element,
|
|
365
|
+
show: false
|
|
366
|
+
};
|
|
367
|
+
angular.forEach([
|
|
368
|
+
'template',
|
|
369
|
+
'contentTemplate',
|
|
370
|
+
'placement',
|
|
371
|
+
'backdrop',
|
|
372
|
+
'keyboard',
|
|
373
|
+
'html',
|
|
374
|
+
'container',
|
|
375
|
+
'animation'
|
|
376
|
+
], function (key) {
|
|
377
|
+
if (angular.isDefined(attr[key]))
|
|
378
|
+
options[key] = attr[key];
|
|
379
|
+
});
|
|
380
|
+
// Support scope as data-attrs
|
|
381
|
+
angular.forEach([
|
|
382
|
+
'title',
|
|
383
|
+
'content'
|
|
384
|
+
], function (key) {
|
|
385
|
+
attr[key] && attr.$observe(key, function (newValue, oldValue) {
|
|
386
|
+
scope[key] = $sce.trustAsHtml(newValue);
|
|
387
|
+
});
|
|
388
|
+
});
|
|
389
|
+
// Support scope as an object
|
|
390
|
+
attr.bsAside && scope.$watch(attr.bsAside, function (newValue, oldValue) {
|
|
391
|
+
if (angular.isObject(newValue)) {
|
|
392
|
+
angular.extend(scope, newValue);
|
|
393
|
+
} else {
|
|
394
|
+
scope.content = newValue;
|
|
395
|
+
}
|
|
396
|
+
}, true);
|
|
397
|
+
// Initialize aside
|
|
398
|
+
var aside = $aside(options);
|
|
399
|
+
// Trigger
|
|
400
|
+
element.on(attr.trigger || 'click', aside.toggle);
|
|
401
|
+
// Garbage collection
|
|
402
|
+
scope.$on('$destroy', function () {
|
|
403
|
+
aside.destroy();
|
|
404
|
+
options = null;
|
|
405
|
+
aside = null;
|
|
406
|
+
});
|
|
407
|
+
}
|
|
408
|
+
};
|
|
409
|
+
}
|
|
410
|
+
]);
|
|
411
|
+
|
|
412
|
+
// Source: button.js
|
|
413
|
+
angular.module('mgcrea.ngStrap.button', []).provider('$button', function () {
|
|
414
|
+
var defaults = this.defaults = {
|
|
415
|
+
activeClass: 'active',
|
|
416
|
+
toggleEvent: 'click'
|
|
417
|
+
};
|
|
418
|
+
this.$get = function () {
|
|
419
|
+
return { defaults: defaults };
|
|
420
|
+
};
|
|
421
|
+
}).directive('bsCheckboxGroup', function () {
|
|
422
|
+
return {
|
|
423
|
+
restrict: 'A',
|
|
424
|
+
require: 'ngModel',
|
|
425
|
+
compile: function postLink(element, attr) {
|
|
426
|
+
element.attr('data-toggle', 'buttons');
|
|
427
|
+
element.removeAttr('ng-model');
|
|
428
|
+
var children = element[0].querySelectorAll('input[type="checkbox"]');
|
|
429
|
+
angular.forEach(children, function (child) {
|
|
430
|
+
var childEl = angular.element(child);
|
|
431
|
+
childEl.attr('bs-checkbox', '');
|
|
432
|
+
childEl.attr('ng-model', attr.ngModel + '.' + childEl.attr('value'));
|
|
433
|
+
});
|
|
434
|
+
}
|
|
435
|
+
};
|
|
436
|
+
}).directive('bsCheckbox', [
|
|
437
|
+
'$button',
|
|
438
|
+
'$$rAF',
|
|
439
|
+
function ($button, $$rAF) {
|
|
440
|
+
var defaults = $button.defaults;
|
|
441
|
+
var constantValueRegExp = /^(true|false|\d+)$/;
|
|
442
|
+
return {
|
|
443
|
+
restrict: 'A',
|
|
444
|
+
require: 'ngModel',
|
|
445
|
+
link: function postLink(scope, element, attr, controller) {
|
|
446
|
+
var options = defaults;
|
|
447
|
+
// Support label > input[type="checkbox"]
|
|
448
|
+
var isInput = element[0].nodeName === 'INPUT';
|
|
449
|
+
var activeElement = isInput ? element.parent() : element;
|
|
450
|
+
var trueValue = angular.isDefined(attr.trueValue) ? attr.trueValue : true;
|
|
451
|
+
if (constantValueRegExp.test(attr.trueValue)) {
|
|
452
|
+
trueValue = scope.$eval(attr.trueValue);
|
|
453
|
+
}
|
|
454
|
+
var falseValue = angular.isDefined(attr.falseValue) ? attr.falseValue : false;
|
|
455
|
+
if (constantValueRegExp.test(attr.falseValue)) {
|
|
456
|
+
falseValue = scope.$eval(attr.falseValue);
|
|
457
|
+
}
|
|
458
|
+
// Parse exotic values
|
|
459
|
+
var hasExoticValues = typeof trueValue !== 'boolean' || typeof falseValue !== 'boolean';
|
|
460
|
+
if (hasExoticValues) {
|
|
461
|
+
controller.$parsers.push(function (viewValue) {
|
|
462
|
+
// console.warn('$parser', element.attr('ng-model'), 'viewValue', viewValue);
|
|
463
|
+
return viewValue ? trueValue : falseValue;
|
|
464
|
+
});
|
|
465
|
+
// Fix rendering for exotic values
|
|
466
|
+
scope.$watch(attr.ngModel, function (newValue, oldValue) {
|
|
467
|
+
controller.$render();
|
|
468
|
+
});
|
|
469
|
+
}
|
|
470
|
+
// model -> view
|
|
471
|
+
controller.$render = function () {
|
|
472
|
+
// console.warn('$render', element.attr('ng-model'), 'controller.$modelValue', typeof controller.$modelValue, controller.$modelValue, 'controller.$viewValue', typeof controller.$viewValue, controller.$viewValue);
|
|
473
|
+
var isActive = angular.equals(controller.$modelValue, trueValue);
|
|
474
|
+
$$rAF(function () {
|
|
475
|
+
if (isInput)
|
|
476
|
+
element[0].checked = isActive;
|
|
477
|
+
activeElement.toggleClass(options.activeClass, isActive);
|
|
478
|
+
});
|
|
479
|
+
};
|
|
480
|
+
// view -> model
|
|
481
|
+
element.bind(options.toggleEvent, function () {
|
|
482
|
+
scope.$apply(function () {
|
|
483
|
+
// console.warn('!click', element.attr('ng-model'), 'controller.$viewValue', typeof controller.$viewValue, controller.$viewValue, 'controller.$modelValue', typeof controller.$modelValue, controller.$modelValue);
|
|
484
|
+
if (!isInput) {
|
|
485
|
+
controller.$setViewValue(!activeElement.hasClass('active'));
|
|
486
|
+
}
|
|
487
|
+
if (!hasExoticValues) {
|
|
488
|
+
controller.$render();
|
|
489
|
+
}
|
|
490
|
+
});
|
|
491
|
+
});
|
|
492
|
+
}
|
|
493
|
+
};
|
|
494
|
+
}
|
|
495
|
+
]).directive('bsRadioGroup', function () {
|
|
496
|
+
return {
|
|
497
|
+
restrict: 'A',
|
|
498
|
+
require: 'ngModel',
|
|
499
|
+
compile: function postLink(element, attr) {
|
|
500
|
+
element.attr('data-toggle', 'buttons');
|
|
501
|
+
element.removeAttr('ng-model');
|
|
502
|
+
var children = element[0].querySelectorAll('input[type="radio"]');
|
|
503
|
+
angular.forEach(children, function (child) {
|
|
504
|
+
angular.element(child).attr('bs-radio', '');
|
|
505
|
+
angular.element(child).attr('ng-model', attr.ngModel);
|
|
506
|
+
});
|
|
507
|
+
}
|
|
508
|
+
};
|
|
509
|
+
}).directive('bsRadio', [
|
|
510
|
+
'$button',
|
|
511
|
+
'$$rAF',
|
|
512
|
+
function ($button, $$rAF) {
|
|
513
|
+
var defaults = $button.defaults;
|
|
514
|
+
var constantValueRegExp = /^(true|false|\d+)$/;
|
|
515
|
+
return {
|
|
516
|
+
restrict: 'A',
|
|
517
|
+
require: 'ngModel',
|
|
518
|
+
link: function postLink(scope, element, attr, controller) {
|
|
519
|
+
var options = defaults;
|
|
520
|
+
// Support `label > input[type="radio"]` markup
|
|
521
|
+
var isInput = element[0].nodeName === 'INPUT';
|
|
522
|
+
var activeElement = isInput ? element.parent() : element;
|
|
523
|
+
var value = constantValueRegExp.test(attr.value) ? scope.$eval(attr.value) : attr.value;
|
|
524
|
+
// model -> view
|
|
525
|
+
controller.$render = function () {
|
|
526
|
+
// console.warn('$render', element.attr('value'), 'controller.$modelValue', typeof controller.$modelValue, controller.$modelValue, 'controller.$viewValue', typeof controller.$viewValue, controller.$viewValue);
|
|
527
|
+
var isActive = angular.equals(controller.$modelValue, value);
|
|
528
|
+
$$rAF(function () {
|
|
529
|
+
if (isInput)
|
|
530
|
+
element[0].checked = isActive;
|
|
531
|
+
activeElement.toggleClass(options.activeClass, isActive);
|
|
532
|
+
});
|
|
533
|
+
};
|
|
534
|
+
// view -> model
|
|
535
|
+
element.bind(options.toggleEvent, function () {
|
|
536
|
+
scope.$apply(function () {
|
|
537
|
+
// console.warn('!click', element.attr('value'), 'controller.$viewValue', typeof controller.$viewValue, controller.$viewValue, 'controller.$modelValue', typeof controller.$modelValue, controller.$modelValue);
|
|
538
|
+
controller.$setViewValue(value);
|
|
539
|
+
controller.$render();
|
|
540
|
+
});
|
|
541
|
+
});
|
|
542
|
+
}
|
|
543
|
+
};
|
|
544
|
+
}
|
|
545
|
+
]);
|
|
546
|
+
|
|
547
|
+
// Source: datepicker.js
|
|
548
|
+
angular.module('mgcrea.ngStrap.datepicker', [
|
|
549
|
+
'mgcrea.ngStrap.helpers.dateParser',
|
|
550
|
+
'mgcrea.ngStrap.tooltip'
|
|
551
|
+
]).provider('$datepicker', function () {
|
|
552
|
+
var defaults = this.defaults = {
|
|
553
|
+
animation: 'am-fade',
|
|
554
|
+
prefixClass: 'datepicker',
|
|
555
|
+
placement: 'bottom-left',
|
|
556
|
+
template: 'datepicker/datepicker.tpl.html',
|
|
557
|
+
trigger: 'focus',
|
|
558
|
+
container: false,
|
|
559
|
+
keyboard: true,
|
|
560
|
+
html: false,
|
|
561
|
+
delay: 0,
|
|
562
|
+
useNative: false,
|
|
563
|
+
dateType: 'date',
|
|
564
|
+
dateFormat: 'shortDate',
|
|
565
|
+
strictFormat: false,
|
|
566
|
+
autoclose: false,
|
|
567
|
+
minDate: -Infinity,
|
|
568
|
+
maxDate: +Infinity,
|
|
569
|
+
startView: 0,
|
|
570
|
+
minView: 0,
|
|
571
|
+
startWeek: 0
|
|
572
|
+
};
|
|
573
|
+
this.$get = [
|
|
574
|
+
'$window',
|
|
575
|
+
'$document',
|
|
576
|
+
'$rootScope',
|
|
577
|
+
'$sce',
|
|
578
|
+
'$locale',
|
|
579
|
+
'dateFilter',
|
|
580
|
+
'datepickerViews',
|
|
581
|
+
'$tooltip',
|
|
582
|
+
function ($window, $document, $rootScope, $sce, $locale, dateFilter, datepickerViews, $tooltip) {
|
|
583
|
+
var bodyEl = angular.element($window.document.body);
|
|
584
|
+
var isTouch = 'createTouch' in $window.document;
|
|
585
|
+
var isNative = /(ip(a|o)d|iphone|android)/gi.test($window.navigator.userAgent);
|
|
586
|
+
if (!defaults.lang)
|
|
587
|
+
defaults.lang = $locale.id;
|
|
588
|
+
function DatepickerFactory(element, controller, config) {
|
|
589
|
+
var $datepicker = $tooltip(element, angular.extend({}, defaults, config));
|
|
590
|
+
var parentScope = config.scope;
|
|
591
|
+
var options = $datepicker.$options;
|
|
592
|
+
var scope = $datepicker.$scope;
|
|
593
|
+
if (options.startView)
|
|
594
|
+
options.startView -= options.minView;
|
|
595
|
+
// View vars
|
|
596
|
+
var pickerViews = datepickerViews($datepicker);
|
|
597
|
+
$datepicker.$views = pickerViews.views;
|
|
598
|
+
var viewDate = pickerViews.viewDate;
|
|
599
|
+
scope.$mode = options.startView;
|
|
600
|
+
var $picker = $datepicker.$views[scope.$mode];
|
|
601
|
+
// Scope methods
|
|
602
|
+
scope.$select = function (date) {
|
|
603
|
+
$datepicker.select(date);
|
|
604
|
+
};
|
|
605
|
+
scope.$selectPane = function (value) {
|
|
606
|
+
$datepicker.$selectPane(value);
|
|
607
|
+
};
|
|
608
|
+
scope.$toggleMode = function () {
|
|
609
|
+
$datepicker.setMode((scope.$mode + 1) % $datepicker.$views.length);
|
|
610
|
+
};
|
|
611
|
+
// Public methods
|
|
612
|
+
$datepicker.update = function (date) {
|
|
613
|
+
// console.warn('$datepicker.update() newValue=%o', date);
|
|
614
|
+
if (angular.isDate(date) && !isNaN(date.getTime())) {
|
|
615
|
+
$datepicker.$date = date;
|
|
616
|
+
$picker.update.call($picker, date);
|
|
617
|
+
}
|
|
618
|
+
// Build only if pristine
|
|
619
|
+
$datepicker.$build(true);
|
|
620
|
+
};
|
|
621
|
+
$datepicker.select = function (date, keep) {
|
|
622
|
+
// console.warn('$datepicker.select', date, scope.$mode);
|
|
623
|
+
if (!angular.isDate(controller.$dateValue))
|
|
624
|
+
controller.$dateValue = new Date(date);
|
|
625
|
+
controller.$dateValue.setFullYear(date.getFullYear(), date.getMonth(), date.getDate());
|
|
626
|
+
if (!scope.$mode || keep) {
|
|
627
|
+
controller.$setViewValue(controller.$dateValue);
|
|
628
|
+
controller.$render();
|
|
629
|
+
if (options.autoclose && !keep) {
|
|
630
|
+
$datepicker.hide(true);
|
|
631
|
+
}
|
|
632
|
+
} else {
|
|
633
|
+
angular.extend(viewDate, {
|
|
634
|
+
year: date.getFullYear(),
|
|
635
|
+
month: date.getMonth(),
|
|
636
|
+
date: date.getDate()
|
|
637
|
+
});
|
|
638
|
+
$datepicker.setMode(scope.$mode - 1);
|
|
639
|
+
$datepicker.$build();
|
|
640
|
+
}
|
|
641
|
+
};
|
|
642
|
+
$datepicker.setMode = function (mode) {
|
|
643
|
+
// console.warn('$datepicker.setMode', mode);
|
|
644
|
+
scope.$mode = mode;
|
|
645
|
+
$picker = $datepicker.$views[scope.$mode];
|
|
646
|
+
$datepicker.$build();
|
|
647
|
+
};
|
|
648
|
+
// Protected methods
|
|
649
|
+
$datepicker.$build = function (pristine) {
|
|
650
|
+
// console.warn('$datepicker.$build() viewDate=%o', viewDate);
|
|
651
|
+
if (pristine === true && $picker.built)
|
|
652
|
+
return;
|
|
653
|
+
if (pristine === false && !$picker.built)
|
|
654
|
+
return;
|
|
655
|
+
$picker.build.call($picker);
|
|
656
|
+
};
|
|
657
|
+
$datepicker.$updateSelected = function () {
|
|
658
|
+
for (var i = 0, l = scope.rows.length; i < l; i++) {
|
|
659
|
+
angular.forEach(scope.rows[i], updateSelected);
|
|
660
|
+
}
|
|
661
|
+
};
|
|
662
|
+
$datepicker.$isSelected = function (date) {
|
|
663
|
+
return $picker.isSelected(date);
|
|
664
|
+
};
|
|
665
|
+
$datepicker.$selectPane = function (value) {
|
|
666
|
+
var steps = $picker.steps;
|
|
667
|
+
var targetDate = new Date(Date.UTC(viewDate.year + (steps.year || 0) * value, viewDate.month + (steps.month || 0) * value, viewDate.date + (steps.day || 0) * value));
|
|
668
|
+
angular.extend(viewDate, {
|
|
669
|
+
year: targetDate.getUTCFullYear(),
|
|
670
|
+
month: targetDate.getUTCMonth(),
|
|
671
|
+
date: targetDate.getUTCDate()
|
|
672
|
+
});
|
|
673
|
+
$datepicker.$build();
|
|
674
|
+
};
|
|
675
|
+
$datepicker.$onMouseDown = function (evt) {
|
|
676
|
+
// Prevent blur on mousedown on .dropdown-menu
|
|
677
|
+
evt.preventDefault();
|
|
678
|
+
evt.stopPropagation();
|
|
679
|
+
// Emulate click for mobile devices
|
|
680
|
+
if (isTouch) {
|
|
681
|
+
var targetEl = angular.element(evt.target);
|
|
682
|
+
if (targetEl[0].nodeName.toLowerCase() !== 'button') {
|
|
683
|
+
targetEl = targetEl.parent();
|
|
684
|
+
}
|
|
685
|
+
targetEl.triggerHandler('click');
|
|
686
|
+
}
|
|
687
|
+
};
|
|
688
|
+
$datepicker.$onKeyDown = function (evt) {
|
|
689
|
+
if (!/(38|37|39|40|13)/.test(evt.keyCode) || evt.shiftKey || evt.altKey)
|
|
690
|
+
return;
|
|
691
|
+
evt.preventDefault();
|
|
692
|
+
evt.stopPropagation();
|
|
693
|
+
if (evt.keyCode === 13) {
|
|
694
|
+
if (!scope.$mode) {
|
|
695
|
+
return $datepicker.hide(true);
|
|
696
|
+
} else {
|
|
697
|
+
return scope.$apply(function () {
|
|
698
|
+
$datepicker.setMode(scope.$mode - 1);
|
|
699
|
+
});
|
|
700
|
+
}
|
|
701
|
+
}
|
|
702
|
+
// Navigate with keyboard
|
|
703
|
+
$picker.onKeyDown(evt);
|
|
704
|
+
parentScope.$digest();
|
|
705
|
+
};
|
|
706
|
+
// Private
|
|
707
|
+
function updateSelected(el) {
|
|
708
|
+
el.selected = $datepicker.$isSelected(el.date);
|
|
709
|
+
}
|
|
710
|
+
function focusElement() {
|
|
711
|
+
element[0].focus();
|
|
712
|
+
}
|
|
713
|
+
// Overrides
|
|
714
|
+
var _init = $datepicker.init;
|
|
715
|
+
$datepicker.init = function () {
|
|
716
|
+
if (isNative && options.useNative) {
|
|
717
|
+
element.prop('type', 'date');
|
|
718
|
+
element.css('-webkit-appearance', 'textfield');
|
|
719
|
+
return;
|
|
720
|
+
} else if (isTouch) {
|
|
721
|
+
element.prop('type', 'text');
|
|
722
|
+
element.attr('readonly', 'true');
|
|
723
|
+
element.on('click', focusElement);
|
|
724
|
+
}
|
|
725
|
+
_init();
|
|
726
|
+
};
|
|
727
|
+
var _destroy = $datepicker.destroy;
|
|
728
|
+
$datepicker.destroy = function () {
|
|
729
|
+
if (isNative && options.useNative) {
|
|
730
|
+
element.off('click', focusElement);
|
|
731
|
+
}
|
|
732
|
+
_destroy();
|
|
733
|
+
};
|
|
734
|
+
var _show = $datepicker.show;
|
|
735
|
+
$datepicker.show = function () {
|
|
736
|
+
_show();
|
|
737
|
+
setTimeout(function () {
|
|
738
|
+
$datepicker.$element.on(isTouch ? 'touchstart' : 'mousedown', $datepicker.$onMouseDown);
|
|
739
|
+
if (options.keyboard) {
|
|
740
|
+
element.on('keydown', $datepicker.$onKeyDown);
|
|
741
|
+
}
|
|
742
|
+
});
|
|
743
|
+
};
|
|
744
|
+
var _hide = $datepicker.hide;
|
|
745
|
+
$datepicker.hide = function (blur) {
|
|
746
|
+
$datepicker.$element.off(isTouch ? 'touchstart' : 'mousedown', $datepicker.$onMouseDown);
|
|
747
|
+
if (options.keyboard) {
|
|
748
|
+
element.off('keydown', $datepicker.$onKeyDown);
|
|
749
|
+
}
|
|
750
|
+
_hide(blur);
|
|
751
|
+
};
|
|
752
|
+
return $datepicker;
|
|
753
|
+
}
|
|
754
|
+
DatepickerFactory.defaults = defaults;
|
|
755
|
+
return DatepickerFactory;
|
|
756
|
+
}
|
|
757
|
+
];
|
|
758
|
+
}).directive('bsDatepicker', [
|
|
759
|
+
'$window',
|
|
760
|
+
'$parse',
|
|
761
|
+
'$q',
|
|
762
|
+
'$locale',
|
|
763
|
+
'dateFilter',
|
|
764
|
+
'$datepicker',
|
|
765
|
+
'$dateParser',
|
|
766
|
+
'$timeout',
|
|
767
|
+
function ($window, $parse, $q, $locale, dateFilter, $datepicker, $dateParser, $timeout) {
|
|
768
|
+
var defaults = $datepicker.defaults;
|
|
769
|
+
var isNative = /(ip(a|o)d|iphone|android)/gi.test($window.navigator.userAgent);
|
|
770
|
+
var isNumeric = function (n) {
|
|
771
|
+
return !isNaN(parseFloat(n)) && isFinite(n);
|
|
772
|
+
};
|
|
773
|
+
return {
|
|
774
|
+
restrict: 'EAC',
|
|
775
|
+
require: 'ngModel',
|
|
776
|
+
link: function postLink(scope, element, attr, controller) {
|
|
777
|
+
// Directive options
|
|
778
|
+
var options = {
|
|
779
|
+
scope: scope,
|
|
780
|
+
controller: controller
|
|
781
|
+
};
|
|
782
|
+
angular.forEach([
|
|
783
|
+
'placement',
|
|
784
|
+
'container',
|
|
785
|
+
'delay',
|
|
786
|
+
'trigger',
|
|
787
|
+
'keyboard',
|
|
788
|
+
'html',
|
|
789
|
+
'animation',
|
|
790
|
+
'template',
|
|
791
|
+
'autoclose',
|
|
792
|
+
'dateType',
|
|
793
|
+
'dateFormat',
|
|
794
|
+
'strictFormat',
|
|
795
|
+
'startWeek',
|
|
796
|
+
'useNative',
|
|
797
|
+
'lang',
|
|
798
|
+
'startView',
|
|
799
|
+
'minView'
|
|
800
|
+
], function (key) {
|
|
801
|
+
if (angular.isDefined(attr[key]))
|
|
802
|
+
options[key] = attr[key];
|
|
803
|
+
});
|
|
804
|
+
// Initialize datepicker
|
|
805
|
+
if (isNative && options.useNative)
|
|
806
|
+
options.dateFormat = 'yyyy-MM-dd';
|
|
807
|
+
var datepicker = $datepicker(element, controller, options);
|
|
808
|
+
options = datepicker.$options;
|
|
809
|
+
// Observe attributes for changes
|
|
810
|
+
angular.forEach([
|
|
811
|
+
'minDate',
|
|
812
|
+
'maxDate'
|
|
813
|
+
], function (key) {
|
|
814
|
+
// console.warn('attr.$observe(%s)', key, attr[key]);
|
|
815
|
+
angular.isDefined(attr[key]) && attr.$observe(key, function (newValue) {
|
|
816
|
+
// console.warn('attr.$observe(%s)=%o', key, newValue);
|
|
817
|
+
if (newValue === 'today') {
|
|
818
|
+
var today = new Date();
|
|
819
|
+
datepicker.$options[key] = +new Date(today.getFullYear(), today.getMonth(), today.getDate() + (key === 'maxDate' ? 1 : 0), 0, 0, 0, key === 'minDate' ? 0 : -1);
|
|
820
|
+
} else if (angular.isString(newValue) && newValue.match(/^".+"$/)) {
|
|
821
|
+
// Support {{ dateObj }}
|
|
822
|
+
datepicker.$options[key] = +new Date(newValue.substr(1, newValue.length - 2));
|
|
823
|
+
} else if (isNumeric(newValue)) {
|
|
824
|
+
datepicker.$options[key] = +new Date(parseInt(newValue, 10));
|
|
825
|
+
} else {
|
|
826
|
+
datepicker.$options[key] = +new Date(newValue);
|
|
827
|
+
}
|
|
828
|
+
// Build only if dirty
|
|
829
|
+
!isNaN(datepicker.$options[key]) && datepicker.$build(false);
|
|
830
|
+
});
|
|
831
|
+
});
|
|
832
|
+
// Watch model for changes
|
|
833
|
+
scope.$watch(attr.ngModel, function (newValue, oldValue) {
|
|
834
|
+
datepicker.update(controller.$dateValue);
|
|
835
|
+
}, true);
|
|
836
|
+
var dateParser = $dateParser({
|
|
837
|
+
format: options.dateFormat,
|
|
838
|
+
lang: options.lang,
|
|
839
|
+
strict: options.strictFormat
|
|
840
|
+
});
|
|
841
|
+
// viewValue -> $parsers -> modelValue
|
|
842
|
+
controller.$parsers.unshift(function (viewValue) {
|
|
843
|
+
// console.warn('$parser("%s"): viewValue=%o', element.attr('ng-model'), viewValue);
|
|
844
|
+
// Null values should correctly reset the model value & validity
|
|
845
|
+
if (!viewValue) {
|
|
846
|
+
controller.$setValidity('date', true);
|
|
847
|
+
return;
|
|
848
|
+
}
|
|
849
|
+
var parsedDate = dateParser.parse(viewValue, controller.$dateValue);
|
|
850
|
+
if (!parsedDate || isNaN(parsedDate.getTime())) {
|
|
851
|
+
controller.$setValidity('date', false);
|
|
852
|
+
return;
|
|
853
|
+
} else {
|
|
854
|
+
var isValid = (isNaN(datepicker.$options.minDate) || parsedDate.getTime() >= datepicker.$options.minDate) && (isNaN(datepicker.$options.maxDate) || parsedDate.getTime() <= datepicker.$options.maxDate);
|
|
855
|
+
controller.$setValidity('date', isValid);
|
|
856
|
+
// Only update the model when we have a valid date
|
|
857
|
+
if (isValid)
|
|
858
|
+
controller.$dateValue = parsedDate;
|
|
859
|
+
}
|
|
860
|
+
if (options.dateType === 'string') {
|
|
861
|
+
return dateFilter(viewValue, options.dateFormat);
|
|
862
|
+
} else if (options.dateType === 'number') {
|
|
863
|
+
return controller.$dateValue.getTime();
|
|
864
|
+
} else if (options.dateType === 'iso') {
|
|
865
|
+
return controller.$dateValue.toISOString();
|
|
866
|
+
} else {
|
|
867
|
+
return new Date(controller.$dateValue);
|
|
868
|
+
}
|
|
869
|
+
});
|
|
870
|
+
// modelValue -> $formatters -> viewValue
|
|
871
|
+
controller.$formatters.push(function (modelValue) {
|
|
872
|
+
// console.warn('$formatter("%s"): modelValue=%o (%o)', element.attr('ng-model'), modelValue, typeof modelValue);
|
|
873
|
+
var date;
|
|
874
|
+
if (angular.isUndefined(modelValue) || modelValue === null) {
|
|
875
|
+
date = NaN;
|
|
876
|
+
} else if (angular.isDate(modelValue)) {
|
|
877
|
+
date = modelValue;
|
|
878
|
+
} else if (options.dateType === 'string') {
|
|
879
|
+
date = dateParser.parse(modelValue);
|
|
880
|
+
} else {
|
|
881
|
+
date = new Date(modelValue);
|
|
882
|
+
}
|
|
883
|
+
// Setup default value?
|
|
884
|
+
// if(isNaN(date.getTime())) {
|
|
885
|
+
// var today = new Date();
|
|
886
|
+
// date = new Date(today.getFullYear(), today.getMonth(), today.getDate(), 0, 0, 0, 0);
|
|
887
|
+
// }
|
|
888
|
+
controller.$dateValue = date;
|
|
889
|
+
return controller.$dateValue;
|
|
890
|
+
});
|
|
891
|
+
// viewValue -> element
|
|
892
|
+
controller.$render = function () {
|
|
893
|
+
// console.warn('$render("%s"): viewValue=%o', element.attr('ng-model'), controller.$viewValue);
|
|
894
|
+
element.val(!controller.$dateValue || isNaN(controller.$dateValue.getTime()) ? '' : dateFilter(controller.$dateValue, options.dateFormat));
|
|
895
|
+
};
|
|
896
|
+
// Garbage collection
|
|
897
|
+
scope.$on('$destroy', function () {
|
|
898
|
+
datepicker.destroy();
|
|
899
|
+
options = null;
|
|
900
|
+
datepicker = null;
|
|
901
|
+
});
|
|
902
|
+
}
|
|
903
|
+
};
|
|
904
|
+
}
|
|
905
|
+
]).provider('datepickerViews', function () {
|
|
906
|
+
var defaults = this.defaults = {
|
|
907
|
+
dayFormat: 'dd',
|
|
908
|
+
daySplit: 7
|
|
909
|
+
};
|
|
910
|
+
// Split array into smaller arrays
|
|
911
|
+
function split(arr, size) {
|
|
912
|
+
var arrays = [];
|
|
913
|
+
while (arr.length > 0) {
|
|
914
|
+
arrays.push(arr.splice(0, size));
|
|
915
|
+
}
|
|
916
|
+
return arrays;
|
|
917
|
+
}
|
|
918
|
+
// Modulus operator
|
|
919
|
+
function mod(n, m) {
|
|
920
|
+
return (n % m + m) % m;
|
|
921
|
+
}
|
|
922
|
+
this.$get = [
|
|
923
|
+
'$locale',
|
|
924
|
+
'$sce',
|
|
925
|
+
'dateFilter',
|
|
926
|
+
function ($locale, $sce, dateFilter) {
|
|
927
|
+
return function (picker) {
|
|
928
|
+
var scope = picker.$scope;
|
|
929
|
+
var options = picker.$options;
|
|
930
|
+
var weekDaysMin = $locale.DATETIME_FORMATS.SHORTDAY;
|
|
931
|
+
var weekDaysLabels = weekDaysMin.slice(options.startWeek).concat(weekDaysMin.slice(0, options.startWeek));
|
|
932
|
+
var weekDaysLabelsHtml = $sce.trustAsHtml('<th class="dow text-center">' + weekDaysLabels.join('</th><th class="dow text-center">') + '</th>');
|
|
933
|
+
var startDate = picker.$date || new Date();
|
|
934
|
+
var viewDate = {
|
|
935
|
+
year: startDate.getFullYear(),
|
|
936
|
+
month: startDate.getMonth(),
|
|
937
|
+
date: startDate.getDate()
|
|
938
|
+
};
|
|
939
|
+
var timezoneOffset = startDate.getTimezoneOffset() * 60000;
|
|
940
|
+
var views = [
|
|
941
|
+
{
|
|
942
|
+
format: 'dd',
|
|
943
|
+
split: 7,
|
|
944
|
+
steps: { month: 1 },
|
|
945
|
+
update: function (date, force) {
|
|
946
|
+
if (!this.built || force || date.getFullYear() !== viewDate.year || date.getMonth() !== viewDate.month) {
|
|
947
|
+
angular.extend(viewDate, {
|
|
948
|
+
year: picker.$date.getFullYear(),
|
|
949
|
+
month: picker.$date.getMonth(),
|
|
950
|
+
date: picker.$date.getDate()
|
|
951
|
+
});
|
|
952
|
+
picker.$build();
|
|
953
|
+
} else if (date.getDate() !== viewDate.date) {
|
|
954
|
+
viewDate.date = picker.$date.getDate();
|
|
955
|
+
picker.$updateSelected();
|
|
956
|
+
}
|
|
957
|
+
},
|
|
958
|
+
build: function () {
|
|
959
|
+
var firstDayOfMonth = new Date(viewDate.year, viewDate.month, 1), firstDayOfMonthOffset = firstDayOfMonth.getTimezoneOffset();
|
|
960
|
+
var firstDate = new Date(+firstDayOfMonth - mod(firstDayOfMonth.getDay() - options.startWeek, 6) * 86400000), firstDateOffset = firstDate.getTimezoneOffset();
|
|
961
|
+
// Handle daylight time switch
|
|
962
|
+
if (firstDateOffset !== firstDayOfMonthOffset)
|
|
963
|
+
firstDate = new Date(+firstDate + (firstDateOffset - firstDayOfMonthOffset) * 60000);
|
|
964
|
+
var days = [], day;
|
|
965
|
+
for (var i = 0; i < 42; i++) {
|
|
966
|
+
// < 7 * 6
|
|
967
|
+
day = new Date(firstDate.getFullYear(), firstDate.getMonth(), firstDate.getDate() + i);
|
|
968
|
+
days.push({
|
|
969
|
+
date: day,
|
|
970
|
+
label: dateFilter(day, this.format),
|
|
971
|
+
selected: picker.$date && this.isSelected(day),
|
|
972
|
+
muted: day.getMonth() !== viewDate.month,
|
|
973
|
+
disabled: this.isDisabled(day)
|
|
974
|
+
});
|
|
975
|
+
}
|
|
976
|
+
scope.title = dateFilter(firstDayOfMonth, 'MMMM yyyy');
|
|
977
|
+
scope.labels = weekDaysLabelsHtml;
|
|
978
|
+
scope.rows = split(days, this.split);
|
|
979
|
+
this.built = true;
|
|
980
|
+
},
|
|
981
|
+
isSelected: function (date) {
|
|
982
|
+
return picker.$date && date.getFullYear() === picker.$date.getFullYear() && date.getMonth() === picker.$date.getMonth() && date.getDate() === picker.$date.getDate();
|
|
983
|
+
},
|
|
984
|
+
isDisabled: function (date) {
|
|
985
|
+
return date.getTime() < options.minDate || date.getTime() > options.maxDate;
|
|
986
|
+
},
|
|
987
|
+
onKeyDown: function (evt) {
|
|
988
|
+
var actualTime = picker.$date.getTime();
|
|
989
|
+
if (evt.keyCode === 37)
|
|
990
|
+
picker.select(new Date(actualTime - 1 * 86400000), true);
|
|
991
|
+
else if (evt.keyCode === 38)
|
|
992
|
+
picker.select(new Date(actualTime - 7 * 86400000), true);
|
|
993
|
+
else if (evt.keyCode === 39)
|
|
994
|
+
picker.select(new Date(actualTime + 1 * 86400000), true);
|
|
995
|
+
else if (evt.keyCode === 40)
|
|
996
|
+
picker.select(new Date(actualTime + 7 * 86400000), true);
|
|
997
|
+
}
|
|
998
|
+
},
|
|
999
|
+
{
|
|
1000
|
+
name: 'month',
|
|
1001
|
+
format: 'MMM',
|
|
1002
|
+
split: 4,
|
|
1003
|
+
steps: { year: 1 },
|
|
1004
|
+
update: function (date, force) {
|
|
1005
|
+
if (!this.built || date.getFullYear() !== viewDate.year) {
|
|
1006
|
+
angular.extend(viewDate, {
|
|
1007
|
+
year: picker.$date.getFullYear(),
|
|
1008
|
+
month: picker.$date.getMonth(),
|
|
1009
|
+
date: picker.$date.getDate()
|
|
1010
|
+
});
|
|
1011
|
+
picker.$build();
|
|
1012
|
+
} else if (date.getMonth() !== viewDate.month) {
|
|
1013
|
+
angular.extend(viewDate, {
|
|
1014
|
+
month: picker.$date.getMonth(),
|
|
1015
|
+
date: picker.$date.getDate()
|
|
1016
|
+
});
|
|
1017
|
+
picker.$updateSelected();
|
|
1018
|
+
}
|
|
1019
|
+
},
|
|
1020
|
+
build: function () {
|
|
1021
|
+
var firstMonth = new Date(viewDate.year, 0, 1);
|
|
1022
|
+
var months = [], month;
|
|
1023
|
+
for (var i = 0; i < 12; i++) {
|
|
1024
|
+
month = new Date(viewDate.year, i, 1);
|
|
1025
|
+
months.push({
|
|
1026
|
+
date: month,
|
|
1027
|
+
label: dateFilter(month, this.format),
|
|
1028
|
+
selected: picker.$isSelected(month),
|
|
1029
|
+
disabled: this.isDisabled(month)
|
|
1030
|
+
});
|
|
1031
|
+
}
|
|
1032
|
+
scope.title = dateFilter(month, 'yyyy');
|
|
1033
|
+
scope.labels = false;
|
|
1034
|
+
scope.rows = split(months, this.split);
|
|
1035
|
+
this.built = true;
|
|
1036
|
+
},
|
|
1037
|
+
isSelected: function (date) {
|
|
1038
|
+
return picker.$date && date.getFullYear() === picker.$date.getFullYear() && date.getMonth() === picker.$date.getMonth();
|
|
1039
|
+
},
|
|
1040
|
+
isDisabled: function (date) {
|
|
1041
|
+
var lastDate = +new Date(date.getFullYear(), date.getMonth() + 1, 0);
|
|
1042
|
+
return lastDate < options.minDate || date.getTime() > options.maxDate;
|
|
1043
|
+
},
|
|
1044
|
+
onKeyDown: function (evt) {
|
|
1045
|
+
var actualMonth = picker.$date.getMonth();
|
|
1046
|
+
if (evt.keyCode === 37)
|
|
1047
|
+
picker.select(new Date(picker.$date.setMonth(actualMonth - 1)), true);
|
|
1048
|
+
else if (evt.keyCode === 38)
|
|
1049
|
+
picker.select(new Date(picker.$date.setMonth(actualMonth - 4)), true);
|
|
1050
|
+
else if (evt.keyCode === 39)
|
|
1051
|
+
picker.select(new Date(picker.$date.setMonth(actualMonth + 1)), true);
|
|
1052
|
+
else if (evt.keyCode === 40)
|
|
1053
|
+
picker.select(new Date(picker.$date.setMonth(actualMonth + 4)), true);
|
|
1054
|
+
}
|
|
1055
|
+
},
|
|
1056
|
+
{
|
|
1057
|
+
name: 'year',
|
|
1058
|
+
format: 'yyyy',
|
|
1059
|
+
split: 4,
|
|
1060
|
+
steps: { year: 12 },
|
|
1061
|
+
update: function (date, force) {
|
|
1062
|
+
if (!this.built || force || parseInt(date.getFullYear() / 20, 10) !== parseInt(viewDate.year / 20, 10)) {
|
|
1063
|
+
angular.extend(viewDate, {
|
|
1064
|
+
year: picker.$date.getFullYear(),
|
|
1065
|
+
month: picker.$date.getMonth(),
|
|
1066
|
+
date: picker.$date.getDate()
|
|
1067
|
+
});
|
|
1068
|
+
picker.$build();
|
|
1069
|
+
} else if (date.getFullYear() !== viewDate.year) {
|
|
1070
|
+
angular.extend(viewDate, {
|
|
1071
|
+
year: picker.$date.getFullYear(),
|
|
1072
|
+
month: picker.$date.getMonth(),
|
|
1073
|
+
date: picker.$date.getDate()
|
|
1074
|
+
});
|
|
1075
|
+
picker.$updateSelected();
|
|
1076
|
+
}
|
|
1077
|
+
},
|
|
1078
|
+
build: function () {
|
|
1079
|
+
var firstYear = viewDate.year - viewDate.year % (this.split * 3);
|
|
1080
|
+
var years = [], year;
|
|
1081
|
+
for (var i = 0; i < 12; i++) {
|
|
1082
|
+
year = new Date(firstYear + i, 0, 1);
|
|
1083
|
+
years.push({
|
|
1084
|
+
date: year,
|
|
1085
|
+
label: dateFilter(year, this.format),
|
|
1086
|
+
selected: picker.$isSelected(year),
|
|
1087
|
+
disabled: this.isDisabled(year)
|
|
1088
|
+
});
|
|
1089
|
+
}
|
|
1090
|
+
scope.title = years[0].label + '-' + years[years.length - 1].label;
|
|
1091
|
+
scope.labels = false;
|
|
1092
|
+
scope.rows = split(years, this.split);
|
|
1093
|
+
this.built = true;
|
|
1094
|
+
},
|
|
1095
|
+
isSelected: function (date) {
|
|
1096
|
+
return picker.$date && date.getFullYear() === picker.$date.getFullYear();
|
|
1097
|
+
},
|
|
1098
|
+
isDisabled: function (date) {
|
|
1099
|
+
var lastDate = +new Date(date.getFullYear() + 1, 0, 0);
|
|
1100
|
+
return lastDate < options.minDate || date.getTime() > options.maxDate;
|
|
1101
|
+
},
|
|
1102
|
+
onKeyDown: function (evt) {
|
|
1103
|
+
var actualYear = picker.$date.getFullYear();
|
|
1104
|
+
if (evt.keyCode === 37)
|
|
1105
|
+
picker.select(new Date(picker.$date.setYear(actualYear - 1)), true);
|
|
1106
|
+
else if (evt.keyCode === 38)
|
|
1107
|
+
picker.select(new Date(picker.$date.setYear(actualYear - 4)), true);
|
|
1108
|
+
else if (evt.keyCode === 39)
|
|
1109
|
+
picker.select(new Date(picker.$date.setYear(actualYear + 1)), true);
|
|
1110
|
+
else if (evt.keyCode === 40)
|
|
1111
|
+
picker.select(new Date(picker.$date.setYear(actualYear + 4)), true);
|
|
1112
|
+
}
|
|
1113
|
+
}
|
|
1114
|
+
];
|
|
1115
|
+
return {
|
|
1116
|
+
views: options.minView ? Array.prototype.slice.call(views, options.minView) : views,
|
|
1117
|
+
viewDate: viewDate
|
|
1118
|
+
};
|
|
1119
|
+
};
|
|
1120
|
+
}
|
|
1121
|
+
];
|
|
1122
|
+
});
|
|
1123
|
+
|
|
1124
|
+
// Source: dropdown.js
|
|
1125
|
+
angular.module('mgcrea.ngStrap.dropdown', ['mgcrea.ngStrap.tooltip']).provider('$dropdown', function () {
|
|
1126
|
+
var defaults = this.defaults = {
|
|
1127
|
+
animation: 'am-fade',
|
|
1128
|
+
prefixClass: 'dropdown',
|
|
1129
|
+
placement: 'bottom-left',
|
|
1130
|
+
template: 'dropdown/dropdown.tpl.html',
|
|
1131
|
+
trigger: 'click',
|
|
1132
|
+
container: false,
|
|
1133
|
+
keyboard: true,
|
|
1134
|
+
html: false,
|
|
1135
|
+
delay: 0
|
|
1136
|
+
};
|
|
1137
|
+
this.$get = [
|
|
1138
|
+
'$window',
|
|
1139
|
+
'$rootScope',
|
|
1140
|
+
'$tooltip',
|
|
1141
|
+
function ($window, $rootScope, $tooltip) {
|
|
1142
|
+
var bodyEl = angular.element($window.document.body);
|
|
1143
|
+
var matchesSelector = Element.prototype.matchesSelector || Element.prototype.webkitMatchesSelector || Element.prototype.mozMatchesSelector || Element.prototype.msMatchesSelector || Element.prototype.oMatchesSelector;
|
|
1144
|
+
function DropdownFactory(element, config) {
|
|
1145
|
+
var $dropdown = {};
|
|
1146
|
+
// Common vars
|
|
1147
|
+
var options = angular.extend({}, defaults, config);
|
|
1148
|
+
var scope = $dropdown.$scope = options.scope && options.scope.$new() || $rootScope.$new();
|
|
1149
|
+
$dropdown = $tooltip(element, options);
|
|
1150
|
+
// Protected methods
|
|
1151
|
+
$dropdown.$onKeyDown = function (evt) {
|
|
1152
|
+
if (!/(38|40)/.test(evt.keyCode))
|
|
1153
|
+
return;
|
|
1154
|
+
evt.preventDefault();
|
|
1155
|
+
evt.stopPropagation();
|
|
1156
|
+
// Retrieve focused index
|
|
1157
|
+
var items = angular.element($dropdown.$element[0].querySelectorAll('li:not(.divider) a'));
|
|
1158
|
+
if (!items.length)
|
|
1159
|
+
return;
|
|
1160
|
+
var index;
|
|
1161
|
+
angular.forEach(items, function (el, i) {
|
|
1162
|
+
if (matchesSelector && matchesSelector.call(el, ':focus'))
|
|
1163
|
+
index = i;
|
|
1164
|
+
});
|
|
1165
|
+
// Navigate with keyboard
|
|
1166
|
+
if (evt.keyCode === 38 && index > 0)
|
|
1167
|
+
index--;
|
|
1168
|
+
else if (evt.keyCode === 40 && index < items.length - 1)
|
|
1169
|
+
index++;
|
|
1170
|
+
else if (angular.isUndefined(index))
|
|
1171
|
+
index = 0;
|
|
1172
|
+
items.eq(index)[0].focus();
|
|
1173
|
+
};
|
|
1174
|
+
// Overrides
|
|
1175
|
+
var show = $dropdown.show;
|
|
1176
|
+
$dropdown.show = function () {
|
|
1177
|
+
show();
|
|
1178
|
+
setTimeout(function () {
|
|
1179
|
+
options.keyboard && $dropdown.$element.on('keydown', $dropdown.$onKeyDown);
|
|
1180
|
+
bodyEl.on('click', onBodyClick);
|
|
1181
|
+
});
|
|
1182
|
+
};
|
|
1183
|
+
var hide = $dropdown.hide;
|
|
1184
|
+
$dropdown.hide = function () {
|
|
1185
|
+
options.keyboard && $dropdown.$element.off('keydown', $dropdown.$onKeyDown);
|
|
1186
|
+
bodyEl.off('click', onBodyClick);
|
|
1187
|
+
hide();
|
|
1188
|
+
};
|
|
1189
|
+
// Private functions
|
|
1190
|
+
function onBodyClick(evt) {
|
|
1191
|
+
if (evt.target === element[0])
|
|
1192
|
+
return;
|
|
1193
|
+
return evt.target !== element[0] && $dropdown.hide();
|
|
1194
|
+
}
|
|
1195
|
+
return $dropdown;
|
|
1196
|
+
}
|
|
1197
|
+
return DropdownFactory;
|
|
1198
|
+
}
|
|
1199
|
+
];
|
|
1200
|
+
}).directive('bsDropdown', [
|
|
1201
|
+
'$window',
|
|
1202
|
+
'$location',
|
|
1203
|
+
'$sce',
|
|
1204
|
+
'$dropdown',
|
|
1205
|
+
function ($window, $location, $sce, $dropdown) {
|
|
1206
|
+
return {
|
|
1207
|
+
restrict: 'EAC',
|
|
1208
|
+
scope: true,
|
|
1209
|
+
link: function postLink(scope, element, attr, transclusion) {
|
|
1210
|
+
// Directive options
|
|
1211
|
+
var options = { scope: scope };
|
|
1212
|
+
angular.forEach([
|
|
1213
|
+
'placement',
|
|
1214
|
+
'container',
|
|
1215
|
+
'delay',
|
|
1216
|
+
'trigger',
|
|
1217
|
+
'keyboard',
|
|
1218
|
+
'html',
|
|
1219
|
+
'animation',
|
|
1220
|
+
'template'
|
|
1221
|
+
], function (key) {
|
|
1222
|
+
if (angular.isDefined(attr[key]))
|
|
1223
|
+
options[key] = attr[key];
|
|
1224
|
+
});
|
|
1225
|
+
// Support scope as an object
|
|
1226
|
+
attr.bsDropdown && scope.$watch(attr.bsDropdown, function (newValue, oldValue) {
|
|
1227
|
+
scope.content = newValue;
|
|
1228
|
+
}, true);
|
|
1229
|
+
// Initialize dropdown
|
|
1230
|
+
var dropdown = $dropdown(element, options);
|
|
1231
|
+
// Garbage collection
|
|
1232
|
+
scope.$on('$destroy', function () {
|
|
1233
|
+
dropdown.destroy();
|
|
1234
|
+
options = null;
|
|
1235
|
+
dropdown = null;
|
|
1236
|
+
});
|
|
1237
|
+
}
|
|
1238
|
+
};
|
|
1239
|
+
}
|
|
1240
|
+
]);
|
|
1241
|
+
|
|
1242
|
+
// Source: date-parser.js
|
|
1243
|
+
angular.module('mgcrea.ngStrap.helpers.dateParser', []).provider('$dateParser', [
|
|
1244
|
+
'$localeProvider',
|
|
1245
|
+
function ($localeProvider) {
|
|
1246
|
+
var proto = Date.prototype;
|
|
1247
|
+
function isNumeric(n) {
|
|
1248
|
+
return !isNaN(parseFloat(n)) && isFinite(n);
|
|
1249
|
+
}
|
|
1250
|
+
var defaults = this.defaults = {
|
|
1251
|
+
format: 'shortDate',
|
|
1252
|
+
strict: false
|
|
1253
|
+
};
|
|
1254
|
+
this.$get = [
|
|
1255
|
+
'$locale',
|
|
1256
|
+
function ($locale) {
|
|
1257
|
+
var DateParserFactory = function (config) {
|
|
1258
|
+
var options = angular.extend({}, defaults, config);
|
|
1259
|
+
var $dateParser = {};
|
|
1260
|
+
var regExpMap = {
|
|
1261
|
+
'sss': '[0-9]{3}',
|
|
1262
|
+
'ss': '[0-5][0-9]',
|
|
1263
|
+
's': options.strict ? '[1-5]?[0-9]' : '[0-9]|[0-5][0-9]',
|
|
1264
|
+
'mm': '[0-5][0-9]',
|
|
1265
|
+
'm': options.strict ? '[1-5]?[0-9]' : '[0-9]|[0-5][0-9]',
|
|
1266
|
+
'HH': '[01][0-9]|2[0-3]',
|
|
1267
|
+
'H': options.strict ? '1?[0-9]|2[0-3]' : '[01]?[0-9]|2[0-3]',
|
|
1268
|
+
'hh': '[0][1-9]|[1][012]',
|
|
1269
|
+
'h': options.strict ? '[1-9]|1[012]' : '0?[1-9]|1[012]',
|
|
1270
|
+
'a': 'AM|PM',
|
|
1271
|
+
'EEEE': $locale.DATETIME_FORMATS.DAY.join('|'),
|
|
1272
|
+
'EEE': $locale.DATETIME_FORMATS.SHORTDAY.join('|'),
|
|
1273
|
+
'dd': '0[1-9]|[12][0-9]|3[01]',
|
|
1274
|
+
'd': options.strict ? '[1-9]|[1-2][0-9]|3[01]' : '0?[1-9]|[1-2][0-9]|3[01]',
|
|
1275
|
+
'MMMM': $locale.DATETIME_FORMATS.MONTH.join('|'),
|
|
1276
|
+
'MMM': $locale.DATETIME_FORMATS.SHORTMONTH.join('|'),
|
|
1277
|
+
'MM': '0[1-9]|1[012]',
|
|
1278
|
+
'M': options.strict ? '[1-9]|1[012]' : '0?[1-9]|1[012]',
|
|
1279
|
+
'yyyy': '[1]{1}[0-9]{3}|[2]{1}[0-9]{3}',
|
|
1280
|
+
'yy': '[0-9]{2}',
|
|
1281
|
+
'y': options.strict ? '-?(0|[1-9][0-9]{0,3})' : '-?0*[0-9]{1,4}'
|
|
1282
|
+
};
|
|
1283
|
+
var setFnMap = {
|
|
1284
|
+
'sss': proto.setMilliseconds,
|
|
1285
|
+
'ss': proto.setSeconds,
|
|
1286
|
+
's': proto.setSeconds,
|
|
1287
|
+
'mm': proto.setMinutes,
|
|
1288
|
+
'm': proto.setMinutes,
|
|
1289
|
+
'HH': proto.setHours,
|
|
1290
|
+
'H': proto.setHours,
|
|
1291
|
+
'hh': proto.setHours,
|
|
1292
|
+
'h': proto.setHours,
|
|
1293
|
+
'dd': proto.setDate,
|
|
1294
|
+
'd': proto.setDate,
|
|
1295
|
+
'a': function (value) {
|
|
1296
|
+
var hours = this.getHours();
|
|
1297
|
+
return this.setHours(value.match(/pm/i) ? hours + 12 : hours);
|
|
1298
|
+
},
|
|
1299
|
+
'MMMM': function (value) {
|
|
1300
|
+
return this.setMonth($locale.DATETIME_FORMATS.MONTH.indexOf(value));
|
|
1301
|
+
},
|
|
1302
|
+
'MMM': function (value) {
|
|
1303
|
+
return this.setMonth($locale.DATETIME_FORMATS.SHORTMONTH.indexOf(value));
|
|
1304
|
+
},
|
|
1305
|
+
'MM': function (value) {
|
|
1306
|
+
return this.setMonth(1 * value - 1);
|
|
1307
|
+
},
|
|
1308
|
+
'M': function (value) {
|
|
1309
|
+
return this.setMonth(1 * value - 1);
|
|
1310
|
+
},
|
|
1311
|
+
'yyyy': proto.setFullYear,
|
|
1312
|
+
'yy': function (value) {
|
|
1313
|
+
return this.setFullYear(2000 + 1 * value);
|
|
1314
|
+
},
|
|
1315
|
+
'y': proto.setFullYear
|
|
1316
|
+
};
|
|
1317
|
+
var regex, setMap;
|
|
1318
|
+
$dateParser.init = function () {
|
|
1319
|
+
$dateParser.$format = $locale.DATETIME_FORMATS[options.format] || options.format;
|
|
1320
|
+
regex = regExpForFormat($dateParser.$format);
|
|
1321
|
+
setMap = setMapForFormat($dateParser.$format);
|
|
1322
|
+
};
|
|
1323
|
+
$dateParser.isValid = function (date) {
|
|
1324
|
+
if (angular.isDate(date))
|
|
1325
|
+
return !isNaN(date.getTime());
|
|
1326
|
+
return regex.test(date);
|
|
1327
|
+
};
|
|
1328
|
+
$dateParser.parse = function (value, baseDate) {
|
|
1329
|
+
if (angular.isDate(value))
|
|
1330
|
+
return value;
|
|
1331
|
+
var matches = regex.exec(value);
|
|
1332
|
+
if (!matches)
|
|
1333
|
+
return false;
|
|
1334
|
+
var date = baseDate || new Date(0);
|
|
1335
|
+
for (var i = 0; i < matches.length - 1; i++) {
|
|
1336
|
+
setMap[i] && setMap[i].call(date, matches[i + 1]);
|
|
1337
|
+
}
|
|
1338
|
+
return date;
|
|
1339
|
+
};
|
|
1340
|
+
// Private functions
|
|
1341
|
+
function setMapForFormat(format) {
|
|
1342
|
+
var keys = Object.keys(setFnMap), i;
|
|
1343
|
+
var map = [], sortedMap = [];
|
|
1344
|
+
// Map to setFn
|
|
1345
|
+
var clonedFormat = format;
|
|
1346
|
+
for (i = 0; i < keys.length; i++) {
|
|
1347
|
+
if (format.split(keys[i]).length > 1) {
|
|
1348
|
+
var index = clonedFormat.search(keys[i]);
|
|
1349
|
+
format = format.split(keys[i]).join('');
|
|
1350
|
+
if (setFnMap[keys[i]])
|
|
1351
|
+
map[index] = setFnMap[keys[i]];
|
|
1352
|
+
}
|
|
1353
|
+
}
|
|
1354
|
+
// Sort result map
|
|
1355
|
+
angular.forEach(map, function (v) {
|
|
1356
|
+
sortedMap.push(v);
|
|
1357
|
+
});
|
|
1358
|
+
return sortedMap;
|
|
1359
|
+
}
|
|
1360
|
+
function escapeReservedSymbols(text) {
|
|
1361
|
+
return text.replace(/\//g, '[\\/]').replace('/-/g', '[-]').replace(/\./g, '[.]').replace(/\\s/g, '[\\s]');
|
|
1362
|
+
}
|
|
1363
|
+
function regExpForFormat(format) {
|
|
1364
|
+
var keys = Object.keys(regExpMap), i;
|
|
1365
|
+
var re = format;
|
|
1366
|
+
// Abstract replaces to avoid collisions
|
|
1367
|
+
for (i = 0; i < keys.length; i++) {
|
|
1368
|
+
re = re.split(keys[i]).join('${' + i + '}');
|
|
1369
|
+
}
|
|
1370
|
+
// Replace abstracted values
|
|
1371
|
+
for (i = 0; i < keys.length; i++) {
|
|
1372
|
+
re = re.split('${' + i + '}').join('(' + regExpMap[keys[i]] + ')');
|
|
1373
|
+
}
|
|
1374
|
+
format = escapeReservedSymbols(format);
|
|
1375
|
+
return new RegExp('^' + re + '$', ['i']);
|
|
1376
|
+
}
|
|
1377
|
+
$dateParser.init();
|
|
1378
|
+
return $dateParser;
|
|
1379
|
+
};
|
|
1380
|
+
return DateParserFactory;
|
|
1381
|
+
}
|
|
1382
|
+
];
|
|
1383
|
+
}
|
|
1384
|
+
]);
|
|
1385
|
+
|
|
1386
|
+
// Source: debounce.js
|
|
1387
|
+
angular.module('mgcrea.ngStrap.helpers.debounce', []).constant('debounce', function (func, wait, immediate) {
|
|
1388
|
+
var timeout, args, context, timestamp, result;
|
|
1389
|
+
return function () {
|
|
1390
|
+
context = this;
|
|
1391
|
+
args = arguments;
|
|
1392
|
+
timestamp = new Date();
|
|
1393
|
+
var later = function () {
|
|
1394
|
+
var last = new Date() - timestamp;
|
|
1395
|
+
if (last < wait) {
|
|
1396
|
+
timeout = setTimeout(later, wait - last);
|
|
1397
|
+
} else {
|
|
1398
|
+
timeout = null;
|
|
1399
|
+
if (!immediate)
|
|
1400
|
+
result = func.apply(context, args);
|
|
1401
|
+
}
|
|
1402
|
+
};
|
|
1403
|
+
var callNow = immediate && !timeout;
|
|
1404
|
+
if (!timeout) {
|
|
1405
|
+
timeout = setTimeout(later, wait);
|
|
1406
|
+
}
|
|
1407
|
+
if (callNow)
|
|
1408
|
+
result = func.apply(context, args);
|
|
1409
|
+
return result;
|
|
1410
|
+
};
|
|
1411
|
+
}).constant('throttle', function (func, wait, options) {
|
|
1412
|
+
var context, args, result;
|
|
1413
|
+
var timeout = null;
|
|
1414
|
+
var previous = 0;
|
|
1415
|
+
options || (options = {});
|
|
1416
|
+
var later = function () {
|
|
1417
|
+
previous = options.leading === false ? 0 : new Date();
|
|
1418
|
+
timeout = null;
|
|
1419
|
+
result = func.apply(context, args);
|
|
1420
|
+
};
|
|
1421
|
+
return function () {
|
|
1422
|
+
var now = new Date();
|
|
1423
|
+
if (!previous && options.leading === false)
|
|
1424
|
+
previous = now;
|
|
1425
|
+
var remaining = wait - (now - previous);
|
|
1426
|
+
context = this;
|
|
1427
|
+
args = arguments;
|
|
1428
|
+
if (remaining <= 0) {
|
|
1429
|
+
clearTimeout(timeout);
|
|
1430
|
+
timeout = null;
|
|
1431
|
+
previous = now;
|
|
1432
|
+
result = func.apply(context, args);
|
|
1433
|
+
} else if (!timeout && options.trailing !== false) {
|
|
1434
|
+
timeout = setTimeout(later, remaining);
|
|
1435
|
+
}
|
|
1436
|
+
return result;
|
|
1437
|
+
};
|
|
1438
|
+
});
|
|
1439
|
+
|
|
1440
|
+
// Source: dimensions.js
|
|
1441
|
+
angular.module('mgcrea.ngStrap.helpers.dimensions', []).factory('dimensions', [
|
|
1442
|
+
'$document',
|
|
1443
|
+
'$window',
|
|
1444
|
+
function ($document, $window) {
|
|
1445
|
+
var jqLite = angular.element;
|
|
1446
|
+
var fn = {};
|
|
1447
|
+
/**
|
|
1448
|
+
* Test the element nodeName
|
|
1449
|
+
* @param element
|
|
1450
|
+
* @param name
|
|
1451
|
+
*/
|
|
1452
|
+
var nodeName = fn.nodeName = function (element, name) {
|
|
1453
|
+
return element.nodeName && element.nodeName.toLowerCase() === name.toLowerCase();
|
|
1454
|
+
};
|
|
1455
|
+
/**
|
|
1456
|
+
* Returns the element computed style
|
|
1457
|
+
* @param element
|
|
1458
|
+
* @param prop
|
|
1459
|
+
* @param extra
|
|
1460
|
+
*/
|
|
1461
|
+
fn.css = function (element, prop, extra) {
|
|
1462
|
+
var value;
|
|
1463
|
+
if (element.currentStyle) {
|
|
1464
|
+
//IE
|
|
1465
|
+
value = element.currentStyle[prop];
|
|
1466
|
+
} else if (window.getComputedStyle) {
|
|
1467
|
+
value = window.getComputedStyle(element)[prop];
|
|
1468
|
+
} else {
|
|
1469
|
+
value = element.style[prop];
|
|
1470
|
+
}
|
|
1471
|
+
return extra === true ? parseFloat(value) || 0 : value;
|
|
1472
|
+
};
|
|
1473
|
+
/**
|
|
1474
|
+
* Provides read-only equivalent of jQuery's offset function:
|
|
1475
|
+
* @required-by bootstrap-tooltip, bootstrap-affix
|
|
1476
|
+
* @url http://api.jquery.com/offset/
|
|
1477
|
+
* @param element
|
|
1478
|
+
*/
|
|
1479
|
+
fn.offset = function (element) {
|
|
1480
|
+
var boxRect = element.getBoundingClientRect();
|
|
1481
|
+
var docElement = element.ownerDocument;
|
|
1482
|
+
return {
|
|
1483
|
+
width: element.offsetWidth,
|
|
1484
|
+
height: element.offsetHeight,
|
|
1485
|
+
top: boxRect.top + (window.pageYOffset || docElement.documentElement.scrollTop) - (docElement.documentElement.clientTop || 0),
|
|
1486
|
+
left: boxRect.left + (window.pageXOffset || docElement.documentElement.scrollLeft) - (docElement.documentElement.clientLeft || 0)
|
|
1487
|
+
};
|
|
1488
|
+
};
|
|
1489
|
+
/**
|
|
1490
|
+
* Provides read-only equivalent of jQuery's position function
|
|
1491
|
+
* @required-by bootstrap-tooltip, bootstrap-affix
|
|
1492
|
+
* @url http://api.jquery.com/offset/
|
|
1493
|
+
* @param element
|
|
1494
|
+
*/
|
|
1495
|
+
fn.position = function (element) {
|
|
1496
|
+
var offsetParentRect = {
|
|
1497
|
+
top: 0,
|
|
1498
|
+
left: 0
|
|
1499
|
+
}, offsetParentElement, offset;
|
|
1500
|
+
// Fixed elements are offset from window (parentOffset = {top:0, left: 0}, because it is it's only offset parent
|
|
1501
|
+
if (fn.css(element, 'position') === 'fixed') {
|
|
1502
|
+
// We assume that getBoundingClientRect is available when computed position is fixed
|
|
1503
|
+
offset = element.getBoundingClientRect();
|
|
1504
|
+
} else {
|
|
1505
|
+
// Get *real* offsetParentElement
|
|
1506
|
+
offsetParentElement = offsetParent(element);
|
|
1507
|
+
offset = fn.offset(element);
|
|
1508
|
+
// Get correct offsets
|
|
1509
|
+
offset = fn.offset(element);
|
|
1510
|
+
if (!nodeName(offsetParentElement, 'html')) {
|
|
1511
|
+
offsetParentRect = fn.offset(offsetParentElement);
|
|
1512
|
+
}
|
|
1513
|
+
// Add offsetParent borders
|
|
1514
|
+
offsetParentRect.top += fn.css(offsetParentElement, 'borderTopWidth', true);
|
|
1515
|
+
offsetParentRect.left += fn.css(offsetParentElement, 'borderLeftWidth', true);
|
|
1516
|
+
}
|
|
1517
|
+
// Subtract parent offsets and element margins
|
|
1518
|
+
return {
|
|
1519
|
+
width: element.offsetWidth,
|
|
1520
|
+
height: element.offsetHeight,
|
|
1521
|
+
top: offset.top - offsetParentRect.top - fn.css(element, 'marginTop', true),
|
|
1522
|
+
left: offset.left - offsetParentRect.left - fn.css(element, 'marginLeft', true)
|
|
1523
|
+
};
|
|
1524
|
+
};
|
|
1525
|
+
/**
|
|
1526
|
+
* Returns the closest, non-statically positioned offsetParent of a given element
|
|
1527
|
+
* @required-by fn.position
|
|
1528
|
+
* @param element
|
|
1529
|
+
*/
|
|
1530
|
+
var offsetParent = function offsetParentElement(element) {
|
|
1531
|
+
var docElement = element.ownerDocument;
|
|
1532
|
+
var offsetParent = element.offsetParent || docElement;
|
|
1533
|
+
if (nodeName(offsetParent, '#document'))
|
|
1534
|
+
return docElement.documentElement;
|
|
1535
|
+
while (offsetParent && !nodeName(offsetParent, 'html') && fn.css(offsetParent, 'position') === 'static') {
|
|
1536
|
+
offsetParent = offsetParent.offsetParent;
|
|
1537
|
+
}
|
|
1538
|
+
return offsetParent || docElement.documentElement;
|
|
1539
|
+
};
|
|
1540
|
+
/**
|
|
1541
|
+
* Provides equivalent of jQuery's height function
|
|
1542
|
+
* @required-by bootstrap-affix
|
|
1543
|
+
* @url http://api.jquery.com/height/
|
|
1544
|
+
* @param element
|
|
1545
|
+
* @param outer
|
|
1546
|
+
*/
|
|
1547
|
+
fn.height = function (element, outer) {
|
|
1548
|
+
var value = element.offsetHeight;
|
|
1549
|
+
if (outer) {
|
|
1550
|
+
value += fn.css(element, 'marginTop', true) + fn.css(element, 'marginBottom', true);
|
|
1551
|
+
} else {
|
|
1552
|
+
value -= fn.css(element, 'paddingTop', true) + fn.css(element, 'paddingBottom', true) + fn.css(element, 'borderTopWidth', true) + fn.css(element, 'borderBottomWidth', true);
|
|
1553
|
+
}
|
|
1554
|
+
return value;
|
|
1555
|
+
};
|
|
1556
|
+
/**
|
|
1557
|
+
* Provides equivalent of jQuery's height function
|
|
1558
|
+
* @required-by bootstrap-affix
|
|
1559
|
+
* @url http://api.jquery.com/width/
|
|
1560
|
+
* @param element
|
|
1561
|
+
* @param outer
|
|
1562
|
+
*/
|
|
1563
|
+
fn.width = function (element, outer) {
|
|
1564
|
+
var value = element.offsetWidth;
|
|
1565
|
+
if (outer) {
|
|
1566
|
+
value += fn.css(element, 'marginLeft', true) + fn.css(element, 'marginRight', true);
|
|
1567
|
+
} else {
|
|
1568
|
+
value -= fn.css(element, 'paddingLeft', true) + fn.css(element, 'paddingRight', true) + fn.css(element, 'borderLeftWidth', true) + fn.css(element, 'borderRightWidth', true);
|
|
1569
|
+
}
|
|
1570
|
+
return value;
|
|
1571
|
+
};
|
|
1572
|
+
return fn;
|
|
1573
|
+
}
|
|
1574
|
+
]);
|
|
1575
|
+
|
|
1576
|
+
// Source: parse-options.js
|
|
1577
|
+
angular.module('mgcrea.ngStrap.helpers.parseOptions', []).provider('$parseOptions', function () {
|
|
1578
|
+
var defaults = this.defaults = { regexp: /^\s*(.*?)(?:\s+as\s+(.*?))?(?:\s+group\s+by\s+(.*))?\s+for\s+(?:([\$\w][\$\w]*)|(?:\(\s*([\$\w][\$\w]*)\s*,\s*([\$\w][\$\w]*)\s*\)))\s+in\s+(.*?)(?:\s+track\s+by\s+(.*?))?$/ };
|
|
1579
|
+
this.$get = [
|
|
1580
|
+
'$parse',
|
|
1581
|
+
'$q',
|
|
1582
|
+
function ($parse, $q) {
|
|
1583
|
+
function ParseOptionsFactory(attr, config) {
|
|
1584
|
+
var $parseOptions = {};
|
|
1585
|
+
// Common vars
|
|
1586
|
+
var options = angular.extend({}, defaults, config);
|
|
1587
|
+
$parseOptions.$values = [];
|
|
1588
|
+
// Private vars
|
|
1589
|
+
var match, displayFn, valueName, keyName, groupByFn, valueFn, valuesFn;
|
|
1590
|
+
$parseOptions.init = function () {
|
|
1591
|
+
$parseOptions.$match = match = attr.match(options.regexp);
|
|
1592
|
+
displayFn = $parse(match[2] || match[1]), valueName = match[4] || match[6], keyName = match[5], groupByFn = $parse(match[3] || ''), valueFn = $parse(match[2] ? match[1] : valueName), valuesFn = $parse(match[7]);
|
|
1593
|
+
};
|
|
1594
|
+
$parseOptions.valuesFn = function (scope, controller) {
|
|
1595
|
+
return $q.when(valuesFn(scope, controller)).then(function (values) {
|
|
1596
|
+
$parseOptions.$values = values ? parseValues(values) : {};
|
|
1597
|
+
return $parseOptions.$values;
|
|
1598
|
+
});
|
|
1599
|
+
};
|
|
1600
|
+
// Private functions
|
|
1601
|
+
function parseValues(values) {
|
|
1602
|
+
return values.map(function (match, index) {
|
|
1603
|
+
var locals = {}, label, value;
|
|
1604
|
+
locals[valueName] = match;
|
|
1605
|
+
label = displayFn(locals);
|
|
1606
|
+
value = valueFn(locals) || index;
|
|
1607
|
+
return {
|
|
1608
|
+
label: label,
|
|
1609
|
+
value: value
|
|
1610
|
+
};
|
|
1611
|
+
});
|
|
1612
|
+
}
|
|
1613
|
+
$parseOptions.init();
|
|
1614
|
+
return $parseOptions;
|
|
1615
|
+
}
|
|
1616
|
+
return ParseOptionsFactory;
|
|
1617
|
+
}
|
|
1618
|
+
];
|
|
1619
|
+
});
|
|
1620
|
+
|
|
1621
|
+
// Source: raf.js
|
|
1622
|
+
angular.version.minor < 3 && angular.version.dot < 14 && angular.module('ng').factory('$$rAF', [
|
|
1623
|
+
'$window',
|
|
1624
|
+
'$timeout',
|
|
1625
|
+
function ($window, $timeout) {
|
|
1626
|
+
var requestAnimationFrame = $window.requestAnimationFrame || $window.webkitRequestAnimationFrame || $window.mozRequestAnimationFrame;
|
|
1627
|
+
var cancelAnimationFrame = $window.cancelAnimationFrame || $window.webkitCancelAnimationFrame || $window.mozCancelAnimationFrame || $window.webkitCancelRequestAnimationFrame;
|
|
1628
|
+
var rafSupported = !!requestAnimationFrame;
|
|
1629
|
+
var raf = rafSupported ? function (fn) {
|
|
1630
|
+
var id = requestAnimationFrame(fn);
|
|
1631
|
+
return function () {
|
|
1632
|
+
cancelAnimationFrame(id);
|
|
1633
|
+
};
|
|
1634
|
+
} : function (fn) {
|
|
1635
|
+
var timer = $timeout(fn, 16.66, false);
|
|
1636
|
+
// 1000 / 60 = 16.666
|
|
1637
|
+
return function () {
|
|
1638
|
+
$timeout.cancel(timer);
|
|
1639
|
+
};
|
|
1640
|
+
};
|
|
1641
|
+
raf.supported = rafSupported;
|
|
1642
|
+
return raf;
|
|
1643
|
+
}
|
|
1644
|
+
]); // .factory('$$animateReflow', function($$rAF, $document) {
|
|
1645
|
+
// var bodyEl = $document[0].body;
|
|
1646
|
+
// return function(fn) {
|
|
1647
|
+
// //the returned function acts as the cancellation function
|
|
1648
|
+
// return $$rAF(function() {
|
|
1649
|
+
// //the line below will force the browser to perform a repaint
|
|
1650
|
+
// //so that all the animated elements within the animation frame
|
|
1651
|
+
// //will be properly updated and drawn on screen. This is
|
|
1652
|
+
// //required to perform multi-class CSS based animations with
|
|
1653
|
+
// //Firefox. DO NOT REMOVE THIS LINE.
|
|
1654
|
+
// var a = bodyEl.offsetWidth + 1;
|
|
1655
|
+
// fn();
|
|
1656
|
+
// });
|
|
1657
|
+
// };
|
|
1658
|
+
// });
|
|
1659
|
+
|
|
1660
|
+
// Source: modal.js
|
|
1661
|
+
angular.module('mgcrea.ngStrap.modal', ['mgcrea.ngStrap.helpers.dimensions']).provider('$modal', function () {
|
|
1662
|
+
var defaults = this.defaults = {
|
|
1663
|
+
animation: 'am-fade',
|
|
1664
|
+
backdropAnimation: 'am-fade',
|
|
1665
|
+
prefixClass: 'modal',
|
|
1666
|
+
prefixEvent: 'modal',
|
|
1667
|
+
placement: 'top',
|
|
1668
|
+
template: 'modal/modal.tpl.html',
|
|
1669
|
+
contentTemplate: false,
|
|
1670
|
+
container: false,
|
|
1671
|
+
element: null,
|
|
1672
|
+
backdrop: true,
|
|
1673
|
+
keyboard: true,
|
|
1674
|
+
html: false,
|
|
1675
|
+
show: true
|
|
1676
|
+
};
|
|
1677
|
+
this.$get = [
|
|
1678
|
+
'$window',
|
|
1679
|
+
'$rootScope',
|
|
1680
|
+
'$compile',
|
|
1681
|
+
'$q',
|
|
1682
|
+
'$templateCache',
|
|
1683
|
+
'$http',
|
|
1684
|
+
'$animate',
|
|
1685
|
+
'$timeout',
|
|
1686
|
+
'$sce',
|
|
1687
|
+
'dimensions',
|
|
1688
|
+
function ($window, $rootScope, $compile, $q, $templateCache, $http, $animate, $timeout, $sce, dimensions) {
|
|
1689
|
+
var forEach = angular.forEach;
|
|
1690
|
+
var trim = String.prototype.trim;
|
|
1691
|
+
var requestAnimationFrame = $window.requestAnimationFrame || $window.setTimeout;
|
|
1692
|
+
var bodyElement = angular.element($window.document.body);
|
|
1693
|
+
var htmlReplaceRegExp = /ng-bind="/gi;
|
|
1694
|
+
function ModalFactory(config) {
|
|
1695
|
+
var $modal = {};
|
|
1696
|
+
// Common vars
|
|
1697
|
+
var options = $modal.$options = angular.extend({}, defaults, config);
|
|
1698
|
+
$modal.$promise = fetchTemplate(options.template);
|
|
1699
|
+
var scope = $modal.$scope = options.scope && options.scope.$new() || $rootScope.$new();
|
|
1700
|
+
if (!options.element && !options.container) {
|
|
1701
|
+
options.container = 'body';
|
|
1702
|
+
}
|
|
1703
|
+
// Support scope as string options
|
|
1704
|
+
forEach([
|
|
1705
|
+
'title',
|
|
1706
|
+
'content'
|
|
1707
|
+
], function (key) {
|
|
1708
|
+
if (options[key])
|
|
1709
|
+
scope[key] = $sce.trustAsHtml(options[key]);
|
|
1710
|
+
});
|
|
1711
|
+
// Provide scope helpers
|
|
1712
|
+
scope.$hide = function () {
|
|
1713
|
+
scope.$$postDigest(function () {
|
|
1714
|
+
$modal.hide();
|
|
1715
|
+
});
|
|
1716
|
+
};
|
|
1717
|
+
scope.$show = function () {
|
|
1718
|
+
scope.$$postDigest(function () {
|
|
1719
|
+
$modal.show();
|
|
1720
|
+
});
|
|
1721
|
+
};
|
|
1722
|
+
scope.$toggle = function () {
|
|
1723
|
+
scope.$$postDigest(function () {
|
|
1724
|
+
$modal.toggle();
|
|
1725
|
+
});
|
|
1726
|
+
};
|
|
1727
|
+
// Support contentTemplate option
|
|
1728
|
+
if (options.contentTemplate) {
|
|
1729
|
+
$modal.$promise = $modal.$promise.then(function (template) {
|
|
1730
|
+
var templateEl = angular.element(template);
|
|
1731
|
+
return fetchTemplate(options.contentTemplate).then(function (contentTemplate) {
|
|
1732
|
+
var contentEl = findElement('[ng-bind="content"]', templateEl[0]).removeAttr('ng-bind').html(contentTemplate);
|
|
1733
|
+
// Drop the default footer as you probably don't want it if you use a custom contentTemplate
|
|
1734
|
+
if (!config.template)
|
|
1735
|
+
contentEl.next().remove();
|
|
1736
|
+
return templateEl[0].outerHTML;
|
|
1737
|
+
});
|
|
1738
|
+
});
|
|
1739
|
+
}
|
|
1740
|
+
// Fetch, compile then initialize modal
|
|
1741
|
+
var modalLinker, modalElement;
|
|
1742
|
+
var backdropElement = angular.element('<div class="' + options.prefixClass + '-backdrop"/>');
|
|
1743
|
+
$modal.$promise.then(function (template) {
|
|
1744
|
+
if (angular.isObject(template))
|
|
1745
|
+
template = template.data;
|
|
1746
|
+
if (options.html)
|
|
1747
|
+
template = template.replace(htmlReplaceRegExp, 'ng-bind-html="');
|
|
1748
|
+
template = trim.apply(template);
|
|
1749
|
+
modalLinker = $compile(template);
|
|
1750
|
+
$modal.init();
|
|
1751
|
+
});
|
|
1752
|
+
$modal.init = function () {
|
|
1753
|
+
// Options: show
|
|
1754
|
+
if (options.show) {
|
|
1755
|
+
scope.$$postDigest(function () {
|
|
1756
|
+
$modal.show();
|
|
1757
|
+
});
|
|
1758
|
+
}
|
|
1759
|
+
};
|
|
1760
|
+
$modal.destroy = function () {
|
|
1761
|
+
// Remove element
|
|
1762
|
+
if (modalElement) {
|
|
1763
|
+
modalElement.remove();
|
|
1764
|
+
modalElement = null;
|
|
1765
|
+
}
|
|
1766
|
+
if (backdropElement) {
|
|
1767
|
+
backdropElement.remove();
|
|
1768
|
+
backdropElement = null;
|
|
1769
|
+
}
|
|
1770
|
+
// Destroy scope
|
|
1771
|
+
scope.$destroy();
|
|
1772
|
+
};
|
|
1773
|
+
$modal.show = function () {
|
|
1774
|
+
scope.$emit(options.prefixEvent + '.show.before', $modal);
|
|
1775
|
+
var parent = options.container ? findElement(options.container) : null;
|
|
1776
|
+
var after = options.container ? null : options.element;
|
|
1777
|
+
// Fetch a cloned element linked from template
|
|
1778
|
+
modalElement = $modal.$element = modalLinker(scope, function (clonedElement, scope) {
|
|
1779
|
+
});
|
|
1780
|
+
// Set the initial positioning.
|
|
1781
|
+
modalElement.css({ display: 'block' }).addClass(options.placement);
|
|
1782
|
+
// Options: animation
|
|
1783
|
+
if (options.animation) {
|
|
1784
|
+
if (options.backdrop) {
|
|
1785
|
+
backdropElement.addClass(options.backdropAnimation);
|
|
1786
|
+
}
|
|
1787
|
+
modalElement.addClass(options.animation);
|
|
1788
|
+
}
|
|
1789
|
+
if (options.backdrop) {
|
|
1790
|
+
$animate.enter(backdropElement, bodyElement, null, function () {
|
|
1791
|
+
});
|
|
1792
|
+
}
|
|
1793
|
+
$animate.enter(modalElement, parent, after, function () {
|
|
1794
|
+
scope.$emit(options.prefixEvent + '.show', $modal);
|
|
1795
|
+
});
|
|
1796
|
+
scope.$isShown = true;
|
|
1797
|
+
scope.$$phase || scope.$root.$$phase || scope.$digest();
|
|
1798
|
+
// Focus once the enter-animation has started
|
|
1799
|
+
// Weird PhantomJS bug hack
|
|
1800
|
+
var el = modalElement[0];
|
|
1801
|
+
requestAnimationFrame(function () {
|
|
1802
|
+
el.focus();
|
|
1803
|
+
});
|
|
1804
|
+
bodyElement.addClass(options.prefixClass + '-open');
|
|
1805
|
+
if (options.animation) {
|
|
1806
|
+
bodyElement.addClass(options.prefixClass + '-with-' + options.animation);
|
|
1807
|
+
}
|
|
1808
|
+
// Bind events
|
|
1809
|
+
if (options.backdrop) {
|
|
1810
|
+
modalElement.on('click', hideOnBackdropClick);
|
|
1811
|
+
backdropElement.on('click', hideOnBackdropClick);
|
|
1812
|
+
}
|
|
1813
|
+
if (options.keyboard) {
|
|
1814
|
+
modalElement.on('keyup', $modal.$onKeyUp);
|
|
1815
|
+
}
|
|
1816
|
+
};
|
|
1817
|
+
$modal.hide = function () {
|
|
1818
|
+
scope.$emit(options.prefixEvent + '.hide.before', $modal);
|
|
1819
|
+
$animate.leave(modalElement, function () {
|
|
1820
|
+
scope.$emit(options.prefixEvent + '.hide', $modal);
|
|
1821
|
+
bodyElement.removeClass(options.prefixClass + '-open');
|
|
1822
|
+
if (options.animation) {
|
|
1823
|
+
bodyElement.addClass(options.prefixClass + '-with-' + options.animation);
|
|
1824
|
+
}
|
|
1825
|
+
});
|
|
1826
|
+
if (options.backdrop) {
|
|
1827
|
+
$animate.leave(backdropElement, function () {
|
|
1828
|
+
});
|
|
1829
|
+
}
|
|
1830
|
+
scope.$isShown = false;
|
|
1831
|
+
scope.$$phase || scope.$root.$$phase || scope.$digest();
|
|
1832
|
+
// Unbind events
|
|
1833
|
+
if (options.backdrop) {
|
|
1834
|
+
modalElement.off('click', hideOnBackdropClick);
|
|
1835
|
+
backdropElement.off('click', hideOnBackdropClick);
|
|
1836
|
+
}
|
|
1837
|
+
if (options.keyboard) {
|
|
1838
|
+
modalElement.off('keyup', $modal.$onKeyUp);
|
|
1839
|
+
}
|
|
1840
|
+
};
|
|
1841
|
+
$modal.toggle = function () {
|
|
1842
|
+
scope.$isShown ? $modal.hide() : $modal.show();
|
|
1843
|
+
};
|
|
1844
|
+
$modal.focus = function () {
|
|
1845
|
+
modalElement[0].focus();
|
|
1846
|
+
};
|
|
1847
|
+
// Protected methods
|
|
1848
|
+
$modal.$onKeyUp = function (evt) {
|
|
1849
|
+
evt.which === 27 && $modal.hide();
|
|
1850
|
+
};
|
|
1851
|
+
// Private methods
|
|
1852
|
+
function hideOnBackdropClick(evt) {
|
|
1853
|
+
if (evt.target !== evt.currentTarget)
|
|
1854
|
+
return;
|
|
1855
|
+
options.backdrop === 'static' ? $modal.focus() : $modal.hide();
|
|
1856
|
+
}
|
|
1857
|
+
return $modal;
|
|
1858
|
+
}
|
|
1859
|
+
// Helper functions
|
|
1860
|
+
function findElement(query, element) {
|
|
1861
|
+
return angular.element((element || document).querySelectorAll(query));
|
|
1862
|
+
}
|
|
1863
|
+
function fetchTemplate(template) {
|
|
1864
|
+
return $q.when($templateCache.get(template) || $http.get(template)).then(function (res) {
|
|
1865
|
+
if (angular.isObject(res)) {
|
|
1866
|
+
$templateCache.put(template, res.data);
|
|
1867
|
+
return res.data;
|
|
1868
|
+
}
|
|
1869
|
+
return res;
|
|
1870
|
+
});
|
|
1871
|
+
}
|
|
1872
|
+
return ModalFactory;
|
|
1873
|
+
}
|
|
1874
|
+
];
|
|
1875
|
+
}).directive('bsModal', [
|
|
1876
|
+
'$window',
|
|
1877
|
+
'$location',
|
|
1878
|
+
'$sce',
|
|
1879
|
+
'$modal',
|
|
1880
|
+
function ($window, $location, $sce, $modal) {
|
|
1881
|
+
return {
|
|
1882
|
+
restrict: 'EAC',
|
|
1883
|
+
scope: true,
|
|
1884
|
+
link: function postLink(scope, element, attr, transclusion) {
|
|
1885
|
+
// Directive options
|
|
1886
|
+
var options = {
|
|
1887
|
+
scope: scope,
|
|
1888
|
+
element: element,
|
|
1889
|
+
show: false
|
|
1890
|
+
};
|
|
1891
|
+
angular.forEach([
|
|
1892
|
+
'template',
|
|
1893
|
+
'contentTemplate',
|
|
1894
|
+
'placement',
|
|
1895
|
+
'backdrop',
|
|
1896
|
+
'keyboard',
|
|
1897
|
+
'html',
|
|
1898
|
+
'container',
|
|
1899
|
+
'animation'
|
|
1900
|
+
], function (key) {
|
|
1901
|
+
if (angular.isDefined(attr[key]))
|
|
1902
|
+
options[key] = attr[key];
|
|
1903
|
+
});
|
|
1904
|
+
// Support scope as data-attrs
|
|
1905
|
+
angular.forEach([
|
|
1906
|
+
'title',
|
|
1907
|
+
'content'
|
|
1908
|
+
], function (key) {
|
|
1909
|
+
attr[key] && attr.$observe(key, function (newValue, oldValue) {
|
|
1910
|
+
scope[key] = $sce.trustAsHtml(newValue);
|
|
1911
|
+
});
|
|
1912
|
+
});
|
|
1913
|
+
// Support scope as an object
|
|
1914
|
+
attr.bsModal && scope.$watch(attr.bsModal, function (newValue, oldValue) {
|
|
1915
|
+
if (angular.isObject(newValue)) {
|
|
1916
|
+
angular.extend(scope, newValue);
|
|
1917
|
+
} else {
|
|
1918
|
+
scope.content = newValue;
|
|
1919
|
+
}
|
|
1920
|
+
}, true);
|
|
1921
|
+
// Initialize modal
|
|
1922
|
+
var modal = $modal(options);
|
|
1923
|
+
// Trigger
|
|
1924
|
+
element.on(attr.trigger || 'click', modal.toggle);
|
|
1925
|
+
// Garbage collection
|
|
1926
|
+
scope.$on('$destroy', function () {
|
|
1927
|
+
modal.destroy();
|
|
1928
|
+
options = null;
|
|
1929
|
+
modal = null;
|
|
1930
|
+
});
|
|
1931
|
+
}
|
|
1932
|
+
};
|
|
1933
|
+
}
|
|
1934
|
+
]);
|
|
1935
|
+
|
|
1936
|
+
// Source: navbar.js
|
|
1937
|
+
angular.module('mgcrea.ngStrap.navbar', []).provider('$navbar', function () {
|
|
1938
|
+
var defaults = this.defaults = {
|
|
1939
|
+
activeClass: 'active',
|
|
1940
|
+
routeAttr: 'data-match-route',
|
|
1941
|
+
strict: false
|
|
1942
|
+
};
|
|
1943
|
+
this.$get = function () {
|
|
1944
|
+
return { defaults: defaults };
|
|
1945
|
+
};
|
|
1946
|
+
}).directive('bsNavbar', [
|
|
1947
|
+
'$window',
|
|
1948
|
+
'$location',
|
|
1949
|
+
'$navbar',
|
|
1950
|
+
function ($window, $location, $navbar) {
|
|
1951
|
+
var defaults = $navbar.defaults;
|
|
1952
|
+
return {
|
|
1953
|
+
restrict: 'A',
|
|
1954
|
+
link: function postLink(scope, element, attr, controller) {
|
|
1955
|
+
// Directive options
|
|
1956
|
+
var options = angular.copy(defaults);
|
|
1957
|
+
angular.forEach(Object.keys(defaults), function (key) {
|
|
1958
|
+
if (angular.isDefined(attr[key]))
|
|
1959
|
+
options[key] = attr[key];
|
|
1960
|
+
});
|
|
1961
|
+
// Watch for the $location
|
|
1962
|
+
scope.$watch(function () {
|
|
1963
|
+
return $location.path();
|
|
1964
|
+
}, function (newValue, oldValue) {
|
|
1965
|
+
var liElements = element[0].querySelectorAll('li[' + options.routeAttr + ']');
|
|
1966
|
+
angular.forEach(liElements, function (li) {
|
|
1967
|
+
var liElement = angular.element(li);
|
|
1968
|
+
var pattern = liElement.attr(options.routeAttr).replace('/', '\\/');
|
|
1969
|
+
if (options.strict) {
|
|
1970
|
+
pattern = '^' + pattern + '$';
|
|
1971
|
+
}
|
|
1972
|
+
var regexp = new RegExp(pattern, ['i']);
|
|
1973
|
+
if (regexp.test(newValue)) {
|
|
1974
|
+
liElement.addClass(options.activeClass);
|
|
1975
|
+
} else {
|
|
1976
|
+
liElement.removeClass(options.activeClass);
|
|
1977
|
+
}
|
|
1978
|
+
});
|
|
1979
|
+
});
|
|
1980
|
+
}
|
|
1981
|
+
};
|
|
1982
|
+
}
|
|
1983
|
+
]);
|
|
1984
|
+
|
|
1985
|
+
// Source: popover.js
|
|
1986
|
+
angular.module('mgcrea.ngStrap.popover', ['mgcrea.ngStrap.tooltip']).provider('$popover', function () {
|
|
1987
|
+
var defaults = this.defaults = {
|
|
1988
|
+
animation: 'am-fade',
|
|
1989
|
+
placement: 'right',
|
|
1990
|
+
template: 'popover/popover.tpl.html',
|
|
1991
|
+
contentTemplate: false,
|
|
1992
|
+
trigger: 'click',
|
|
1993
|
+
keyboard: true,
|
|
1994
|
+
html: false,
|
|
1995
|
+
title: '',
|
|
1996
|
+
content: '',
|
|
1997
|
+
delay: 0,
|
|
1998
|
+
container: false
|
|
1999
|
+
};
|
|
2000
|
+
this.$get = [
|
|
2001
|
+
'$tooltip',
|
|
2002
|
+
function ($tooltip) {
|
|
2003
|
+
function PopoverFactory(element, config) {
|
|
2004
|
+
// Common vars
|
|
2005
|
+
var options = angular.extend({}, defaults, config);
|
|
2006
|
+
var $popover = $tooltip(element, options);
|
|
2007
|
+
// Support scope as string options [/*title, */content]
|
|
2008
|
+
if (options.content) {
|
|
2009
|
+
$popover.$scope.content = options.content;
|
|
2010
|
+
}
|
|
2011
|
+
return $popover;
|
|
2012
|
+
}
|
|
2013
|
+
return PopoverFactory;
|
|
2014
|
+
}
|
|
2015
|
+
];
|
|
2016
|
+
}).directive('bsPopover', [
|
|
2017
|
+
'$window',
|
|
2018
|
+
'$location',
|
|
2019
|
+
'$sce',
|
|
2020
|
+
'$popover',
|
|
2021
|
+
function ($window, $location, $sce, $popover) {
|
|
2022
|
+
var requestAnimationFrame = $window.requestAnimationFrame || $window.setTimeout;
|
|
2023
|
+
return {
|
|
2024
|
+
restrict: 'EAC',
|
|
2025
|
+
scope: true,
|
|
2026
|
+
link: function postLink(scope, element, attr) {
|
|
2027
|
+
// Directive options
|
|
2028
|
+
var options = { scope: scope };
|
|
2029
|
+
angular.forEach([
|
|
2030
|
+
'template',
|
|
2031
|
+
'contentTemplate',
|
|
2032
|
+
'placement',
|
|
2033
|
+
'container',
|
|
2034
|
+
'delay',
|
|
2035
|
+
'trigger',
|
|
2036
|
+
'keyboard',
|
|
2037
|
+
'html',
|
|
2038
|
+
'animation'
|
|
2039
|
+
], function (key) {
|
|
2040
|
+
if (angular.isDefined(attr[key]))
|
|
2041
|
+
options[key] = attr[key];
|
|
2042
|
+
});
|
|
2043
|
+
// Support scope as data-attrs
|
|
2044
|
+
angular.forEach([
|
|
2045
|
+
'title',
|
|
2046
|
+
'content'
|
|
2047
|
+
], function (key) {
|
|
2048
|
+
attr[key] && attr.$observe(key, function (newValue, oldValue) {
|
|
2049
|
+
scope[key] = $sce.trustAsHtml(newValue);
|
|
2050
|
+
angular.isDefined(oldValue) && requestAnimationFrame(function () {
|
|
2051
|
+
popover && popover.$applyPlacement();
|
|
2052
|
+
});
|
|
2053
|
+
});
|
|
2054
|
+
});
|
|
2055
|
+
// Support scope as an object
|
|
2056
|
+
attr.bsPopover && scope.$watch(attr.bsPopover, function (newValue, oldValue) {
|
|
2057
|
+
if (angular.isObject(newValue)) {
|
|
2058
|
+
angular.extend(scope, newValue);
|
|
2059
|
+
} else {
|
|
2060
|
+
scope.content = newValue;
|
|
2061
|
+
}
|
|
2062
|
+
angular.isDefined(oldValue) && requestAnimationFrame(function () {
|
|
2063
|
+
popover && popover.$applyPlacement();
|
|
2064
|
+
});
|
|
2065
|
+
}, true);
|
|
2066
|
+
// Initialize popover
|
|
2067
|
+
var popover = $popover(element, options);
|
|
2068
|
+
// Garbage collection
|
|
2069
|
+
scope.$on('$destroy', function () {
|
|
2070
|
+
popover.destroy();
|
|
2071
|
+
options = null;
|
|
2072
|
+
popover = null;
|
|
2073
|
+
});
|
|
2074
|
+
}
|
|
2075
|
+
};
|
|
2076
|
+
}
|
|
2077
|
+
]);
|
|
2078
|
+
|
|
2079
|
+
// Source: scrollspy.js
|
|
2080
|
+
angular.module('mgcrea.ngStrap.scrollspy', [
|
|
2081
|
+
'mgcrea.ngStrap.helpers.debounce',
|
|
2082
|
+
'mgcrea.ngStrap.helpers.dimensions'
|
|
2083
|
+
]).provider('$scrollspy', function () {
|
|
2084
|
+
// Pool of registered spies
|
|
2085
|
+
var spies = this.$$spies = {};
|
|
2086
|
+
var defaults = this.defaults = {
|
|
2087
|
+
debounce: 150,
|
|
2088
|
+
throttle: 100,
|
|
2089
|
+
offset: 100
|
|
2090
|
+
};
|
|
2091
|
+
this.$get = [
|
|
2092
|
+
'$window',
|
|
2093
|
+
'$document',
|
|
2094
|
+
'$rootScope',
|
|
2095
|
+
'dimensions',
|
|
2096
|
+
'debounce',
|
|
2097
|
+
'throttle',
|
|
2098
|
+
function ($window, $document, $rootScope, dimensions, debounce, throttle) {
|
|
2099
|
+
var windowEl = angular.element($window);
|
|
2100
|
+
var docEl = angular.element($document.prop('documentElement'));
|
|
2101
|
+
var bodyEl = angular.element($window.document.body);
|
|
2102
|
+
// Helper functions
|
|
2103
|
+
function nodeName(element, name) {
|
|
2104
|
+
return element[0].nodeName && element[0].nodeName.toLowerCase() === name.toLowerCase();
|
|
2105
|
+
}
|
|
2106
|
+
function ScrollSpyFactory(config) {
|
|
2107
|
+
// Common vars
|
|
2108
|
+
var options = angular.extend({}, defaults, config);
|
|
2109
|
+
if (!options.element)
|
|
2110
|
+
options.element = bodyEl;
|
|
2111
|
+
var isWindowSpy = nodeName(options.element, 'body');
|
|
2112
|
+
var scrollEl = isWindowSpy ? windowEl : options.element;
|
|
2113
|
+
var scrollId = isWindowSpy ? 'window' : options.id;
|
|
2114
|
+
// Use existing spy
|
|
2115
|
+
if (spies[scrollId]) {
|
|
2116
|
+
spies[scrollId].$$count++;
|
|
2117
|
+
return spies[scrollId];
|
|
2118
|
+
}
|
|
2119
|
+
var $scrollspy = {};
|
|
2120
|
+
// Private vars
|
|
2121
|
+
var unbindViewContentLoaded, unbindIncludeContentLoaded;
|
|
2122
|
+
var trackedElements = $scrollspy.$trackedElements = [];
|
|
2123
|
+
var sortedElements = [];
|
|
2124
|
+
var activeTarget;
|
|
2125
|
+
var debouncedCheckPosition;
|
|
2126
|
+
var throttledCheckPosition;
|
|
2127
|
+
var debouncedCheckOffsets;
|
|
2128
|
+
var viewportHeight;
|
|
2129
|
+
var scrollTop;
|
|
2130
|
+
$scrollspy.init = function () {
|
|
2131
|
+
// Setup internal ref counter
|
|
2132
|
+
this.$$count = 1;
|
|
2133
|
+
// Bind events
|
|
2134
|
+
debouncedCheckPosition = debounce(this.checkPosition, options.debounce);
|
|
2135
|
+
throttledCheckPosition = throttle(this.checkPosition, options.throttle);
|
|
2136
|
+
scrollEl.on('click', this.checkPositionWithEventLoop);
|
|
2137
|
+
windowEl.on('resize', debouncedCheckPosition);
|
|
2138
|
+
scrollEl.on('scroll', throttledCheckPosition);
|
|
2139
|
+
debouncedCheckOffsets = debounce(this.checkOffsets, options.debounce);
|
|
2140
|
+
unbindViewContentLoaded = $rootScope.$on('$viewContentLoaded', debouncedCheckOffsets);
|
|
2141
|
+
unbindIncludeContentLoaded = $rootScope.$on('$includeContentLoaded', debouncedCheckOffsets);
|
|
2142
|
+
debouncedCheckOffsets();
|
|
2143
|
+
// Register spy for reuse
|
|
2144
|
+
if (scrollId) {
|
|
2145
|
+
spies[scrollId] = $scrollspy;
|
|
2146
|
+
}
|
|
2147
|
+
};
|
|
2148
|
+
$scrollspy.destroy = function () {
|
|
2149
|
+
// Check internal ref counter
|
|
2150
|
+
this.$$count--;
|
|
2151
|
+
if (this.$$count > 0) {
|
|
2152
|
+
return;
|
|
2153
|
+
}
|
|
2154
|
+
// Unbind events
|
|
2155
|
+
scrollEl.off('click', this.checkPositionWithEventLoop);
|
|
2156
|
+
windowEl.off('resize', debouncedCheckPosition);
|
|
2157
|
+
scrollEl.off('scroll', debouncedCheckPosition);
|
|
2158
|
+
unbindViewContentLoaded();
|
|
2159
|
+
unbindIncludeContentLoaded();
|
|
2160
|
+
if (scrollId) {
|
|
2161
|
+
delete spies[scrollId];
|
|
2162
|
+
}
|
|
2163
|
+
};
|
|
2164
|
+
$scrollspy.checkPosition = function () {
|
|
2165
|
+
// Not ready yet
|
|
2166
|
+
if (!sortedElements.length)
|
|
2167
|
+
return;
|
|
2168
|
+
// Calculate the scroll position
|
|
2169
|
+
scrollTop = (isWindowSpy ? $window.pageYOffset : scrollEl.prop('scrollTop')) || 0;
|
|
2170
|
+
// Calculate the viewport height for use by the components
|
|
2171
|
+
viewportHeight = Math.max($window.innerHeight, docEl.prop('clientHeight'));
|
|
2172
|
+
// Activate first element if scroll is smaller
|
|
2173
|
+
if (scrollTop < sortedElements[0].offsetTop && activeTarget !== sortedElements[0].target) {
|
|
2174
|
+
return $scrollspy.$activateElement(sortedElements[0]);
|
|
2175
|
+
}
|
|
2176
|
+
// Activate proper element
|
|
2177
|
+
for (var i = sortedElements.length; i--;) {
|
|
2178
|
+
if (angular.isUndefined(sortedElements[i].offsetTop) || sortedElements[i].offsetTop === null)
|
|
2179
|
+
continue;
|
|
2180
|
+
if (activeTarget === sortedElements[i].target)
|
|
2181
|
+
continue;
|
|
2182
|
+
if (scrollTop < sortedElements[i].offsetTop)
|
|
2183
|
+
continue;
|
|
2184
|
+
if (sortedElements[i + 1] && scrollTop > sortedElements[i + 1].offsetTop)
|
|
2185
|
+
continue;
|
|
2186
|
+
return $scrollspy.$activateElement(sortedElements[i]);
|
|
2187
|
+
}
|
|
2188
|
+
};
|
|
2189
|
+
$scrollspy.checkPositionWithEventLoop = function () {
|
|
2190
|
+
setTimeout(this.checkPosition, 1);
|
|
2191
|
+
};
|
|
2192
|
+
// Protected methods
|
|
2193
|
+
$scrollspy.$activateElement = function (element) {
|
|
2194
|
+
if (activeTarget) {
|
|
2195
|
+
var activeElement = $scrollspy.$getTrackedElement(activeTarget);
|
|
2196
|
+
if (activeElement) {
|
|
2197
|
+
activeElement.source.removeClass('active');
|
|
2198
|
+
if (nodeName(activeElement.source, 'li') && nodeName(activeElement.source.parent().parent(), 'li')) {
|
|
2199
|
+
activeElement.source.parent().parent().removeClass('active');
|
|
2200
|
+
}
|
|
2201
|
+
}
|
|
2202
|
+
}
|
|
2203
|
+
activeTarget = element.target;
|
|
2204
|
+
element.source.addClass('active');
|
|
2205
|
+
if (nodeName(element.source, 'li') && nodeName(element.source.parent().parent(), 'li')) {
|
|
2206
|
+
element.source.parent().parent().addClass('active');
|
|
2207
|
+
}
|
|
2208
|
+
};
|
|
2209
|
+
$scrollspy.$getTrackedElement = function (target) {
|
|
2210
|
+
return trackedElements.filter(function (obj) {
|
|
2211
|
+
return obj.target === target;
|
|
2212
|
+
})[0];
|
|
2213
|
+
};
|
|
2214
|
+
// Track offsets behavior
|
|
2215
|
+
$scrollspy.checkOffsets = function () {
|
|
2216
|
+
angular.forEach(trackedElements, function (trackedElement) {
|
|
2217
|
+
var targetElement = document.querySelector(trackedElement.target);
|
|
2218
|
+
trackedElement.offsetTop = targetElement ? dimensions.offset(targetElement).top : null;
|
|
2219
|
+
if (options.offset && trackedElement.offsetTop !== null)
|
|
2220
|
+
trackedElement.offsetTop -= options.offset * 1;
|
|
2221
|
+
});
|
|
2222
|
+
sortedElements = trackedElements.filter(function (el) {
|
|
2223
|
+
return el.offsetTop !== null;
|
|
2224
|
+
}).sort(function (a, b) {
|
|
2225
|
+
return a.offsetTop - b.offsetTop;
|
|
2226
|
+
});
|
|
2227
|
+
debouncedCheckPosition();
|
|
2228
|
+
};
|
|
2229
|
+
$scrollspy.trackElement = function (target, source) {
|
|
2230
|
+
trackedElements.push({
|
|
2231
|
+
target: target,
|
|
2232
|
+
source: source
|
|
2233
|
+
});
|
|
2234
|
+
};
|
|
2235
|
+
$scrollspy.untrackElement = function (target, source) {
|
|
2236
|
+
var toDelete;
|
|
2237
|
+
for (var i = trackedElements.length; i--;) {
|
|
2238
|
+
if (trackedElements[i].target === target && trackedElements[i].source === source) {
|
|
2239
|
+
toDelete = i;
|
|
2240
|
+
break;
|
|
2241
|
+
}
|
|
2242
|
+
}
|
|
2243
|
+
trackedElements = trackedElements.splice(toDelete, 1);
|
|
2244
|
+
};
|
|
2245
|
+
$scrollspy.activate = function (i) {
|
|
2246
|
+
trackedElements[i].addClass('active');
|
|
2247
|
+
};
|
|
2248
|
+
// Initialize plugin
|
|
2249
|
+
$scrollspy.init();
|
|
2250
|
+
return $scrollspy;
|
|
2251
|
+
}
|
|
2252
|
+
return ScrollSpyFactory;
|
|
2253
|
+
}
|
|
2254
|
+
];
|
|
2255
|
+
}).directive('bsScrollspy', [
|
|
2256
|
+
'$rootScope',
|
|
2257
|
+
'debounce',
|
|
2258
|
+
'dimensions',
|
|
2259
|
+
'$scrollspy',
|
|
2260
|
+
function ($rootScope, debounce, dimensions, $scrollspy) {
|
|
2261
|
+
return {
|
|
2262
|
+
restrict: 'EAC',
|
|
2263
|
+
link: function postLink(scope, element, attr) {
|
|
2264
|
+
var options = { scope: scope };
|
|
2265
|
+
angular.forEach([
|
|
2266
|
+
'offset',
|
|
2267
|
+
'target'
|
|
2268
|
+
], function (key) {
|
|
2269
|
+
if (angular.isDefined(attr[key]))
|
|
2270
|
+
options[key] = attr[key];
|
|
2271
|
+
});
|
|
2272
|
+
var scrollspy = $scrollspy(options);
|
|
2273
|
+
scrollspy.trackElement(options.target, element);
|
|
2274
|
+
scope.$on('$destroy', function () {
|
|
2275
|
+
scrollspy.untrackElement(options.target, element);
|
|
2276
|
+
scrollspy.destroy();
|
|
2277
|
+
options = null;
|
|
2278
|
+
scrollspy = null;
|
|
2279
|
+
});
|
|
2280
|
+
}
|
|
2281
|
+
};
|
|
2282
|
+
}
|
|
2283
|
+
]).directive('bsScrollspyList', [
|
|
2284
|
+
'$rootScope',
|
|
2285
|
+
'debounce',
|
|
2286
|
+
'dimensions',
|
|
2287
|
+
'$scrollspy',
|
|
2288
|
+
function ($rootScope, debounce, dimensions, $scrollspy) {
|
|
2289
|
+
return {
|
|
2290
|
+
restrict: 'A',
|
|
2291
|
+
compile: function postLink(element, attr) {
|
|
2292
|
+
var children = element[0].querySelectorAll('li > a[href]');
|
|
2293
|
+
angular.forEach(children, function (child) {
|
|
2294
|
+
var childEl = angular.element(child);
|
|
2295
|
+
childEl.parent().attr('bs-scrollspy', '').attr('data-target', childEl.attr('href'));
|
|
2296
|
+
});
|
|
2297
|
+
}
|
|
2298
|
+
};
|
|
2299
|
+
}
|
|
2300
|
+
]);
|
|
2301
|
+
|
|
2302
|
+
// Source: select.js
|
|
2303
|
+
angular.module('mgcrea.ngStrap.select', [
|
|
2304
|
+
'mgcrea.ngStrap.tooltip',
|
|
2305
|
+
'mgcrea.ngStrap.helpers.parseOptions'
|
|
2306
|
+
]).provider('$select', function () {
|
|
2307
|
+
var defaults = this.defaults = {
|
|
2308
|
+
animation: 'am-fade',
|
|
2309
|
+
prefixClass: 'select',
|
|
2310
|
+
placement: 'bottom-left',
|
|
2311
|
+
template: 'select/select.tpl.html',
|
|
2312
|
+
trigger: 'focus',
|
|
2313
|
+
container: false,
|
|
2314
|
+
keyboard: true,
|
|
2315
|
+
html: false,
|
|
2316
|
+
delay: 0,
|
|
2317
|
+
multiple: false,
|
|
2318
|
+
sort: true,
|
|
2319
|
+
caretHtml: ' <span class="caret"></span>',
|
|
2320
|
+
placeholder: 'Choose among the following...',
|
|
2321
|
+
maxLength: 3,
|
|
2322
|
+
maxLengthHtml: 'selected'
|
|
2323
|
+
};
|
|
2324
|
+
this.$get = [
|
|
2325
|
+
'$window',
|
|
2326
|
+
'$document',
|
|
2327
|
+
'$rootScope',
|
|
2328
|
+
'$tooltip',
|
|
2329
|
+
function ($window, $document, $rootScope, $tooltip) {
|
|
2330
|
+
var bodyEl = angular.element($window.document.body);
|
|
2331
|
+
var isTouch = 'createTouch' in $window.document;
|
|
2332
|
+
function SelectFactory(element, controller, config) {
|
|
2333
|
+
var $select = {};
|
|
2334
|
+
// Common vars
|
|
2335
|
+
var options = angular.extend({}, defaults, config);
|
|
2336
|
+
$select = $tooltip(element, options);
|
|
2337
|
+
var parentScope = config.scope;
|
|
2338
|
+
var scope = $select.$scope;
|
|
2339
|
+
scope.$matches = [];
|
|
2340
|
+
scope.$activeIndex = 0;
|
|
2341
|
+
scope.$isMultiple = options.multiple;
|
|
2342
|
+
scope.$activate = function (index) {
|
|
2343
|
+
scope.$$postDigest(function () {
|
|
2344
|
+
$select.activate(index);
|
|
2345
|
+
});
|
|
2346
|
+
};
|
|
2347
|
+
scope.$select = function (index, evt) {
|
|
2348
|
+
scope.$$postDigest(function () {
|
|
2349
|
+
$select.select(index);
|
|
2350
|
+
});
|
|
2351
|
+
};
|
|
2352
|
+
scope.$isVisible = function () {
|
|
2353
|
+
return $select.$isVisible();
|
|
2354
|
+
};
|
|
2355
|
+
scope.$isActive = function (index) {
|
|
2356
|
+
return $select.$isActive(index);
|
|
2357
|
+
};
|
|
2358
|
+
// Public methods
|
|
2359
|
+
$select.update = function (matches) {
|
|
2360
|
+
scope.$matches = matches;
|
|
2361
|
+
$select.$updateActiveIndex();
|
|
2362
|
+
};
|
|
2363
|
+
$select.activate = function (index) {
|
|
2364
|
+
if (options.multiple) {
|
|
2365
|
+
scope.$activeIndex.sort();
|
|
2366
|
+
$select.$isActive(index) ? scope.$activeIndex.splice(scope.$activeIndex.indexOf(index), 1) : scope.$activeIndex.push(index);
|
|
2367
|
+
if (options.sort)
|
|
2368
|
+
scope.$activeIndex.sort();
|
|
2369
|
+
} else {
|
|
2370
|
+
scope.$activeIndex = index;
|
|
2371
|
+
}
|
|
2372
|
+
return scope.$activeIndex;
|
|
2373
|
+
};
|
|
2374
|
+
$select.select = function (index) {
|
|
2375
|
+
var value = scope.$matches[index].value;
|
|
2376
|
+
$select.activate(index);
|
|
2377
|
+
if (options.multiple) {
|
|
2378
|
+
controller.$setViewValue(scope.$activeIndex.map(function (index) {
|
|
2379
|
+
return scope.$matches[index].value;
|
|
2380
|
+
}));
|
|
2381
|
+
} else {
|
|
2382
|
+
controller.$setViewValue(value);
|
|
2383
|
+
}
|
|
2384
|
+
controller.$render();
|
|
2385
|
+
if (parentScope)
|
|
2386
|
+
parentScope.$digest();
|
|
2387
|
+
// Hide if single select
|
|
2388
|
+
if (!options.multiple) {
|
|
2389
|
+
$select.hide();
|
|
2390
|
+
}
|
|
2391
|
+
// Emit event
|
|
2392
|
+
scope.$emit('$select.select', value, index);
|
|
2393
|
+
};
|
|
2394
|
+
// Protected methods
|
|
2395
|
+
$select.$updateActiveIndex = function () {
|
|
2396
|
+
if (controller.$modelValue && scope.$matches.length) {
|
|
2397
|
+
if (options.multiple && angular.isArray(controller.$modelValue)) {
|
|
2398
|
+
scope.$activeIndex = controller.$modelValue.map(function (value) {
|
|
2399
|
+
return $select.$getIndex(value);
|
|
2400
|
+
});
|
|
2401
|
+
} else {
|
|
2402
|
+
scope.$activeIndex = $select.$getIndex(controller.$modelValue);
|
|
2403
|
+
}
|
|
2404
|
+
} else if (scope.$activeIndex >= scope.$matches.length) {
|
|
2405
|
+
scope.$activeIndex = options.multiple ? [] : 0;
|
|
2406
|
+
}
|
|
2407
|
+
};
|
|
2408
|
+
$select.$isVisible = function () {
|
|
2409
|
+
if (!options.minLength || !controller) {
|
|
2410
|
+
return scope.$matches.length;
|
|
2411
|
+
}
|
|
2412
|
+
// minLength support
|
|
2413
|
+
return scope.$matches.length && controller.$viewValue.length >= options.minLength;
|
|
2414
|
+
};
|
|
2415
|
+
$select.$isActive = function (index) {
|
|
2416
|
+
if (options.multiple) {
|
|
2417
|
+
return scope.$activeIndex.indexOf(index) !== -1;
|
|
2418
|
+
} else {
|
|
2419
|
+
return scope.$activeIndex === index;
|
|
2420
|
+
}
|
|
2421
|
+
};
|
|
2422
|
+
$select.$getIndex = function (value) {
|
|
2423
|
+
var l = scope.$matches.length, i = l;
|
|
2424
|
+
if (!l)
|
|
2425
|
+
return;
|
|
2426
|
+
for (i = l; i--;) {
|
|
2427
|
+
if (scope.$matches[i].value === value)
|
|
2428
|
+
break;
|
|
2429
|
+
}
|
|
2430
|
+
if (i < 0)
|
|
2431
|
+
return;
|
|
2432
|
+
return i;
|
|
2433
|
+
};
|
|
2434
|
+
$select.$onMouseDown = function (evt) {
|
|
2435
|
+
// Prevent blur on mousedown on .dropdown-menu
|
|
2436
|
+
evt.preventDefault();
|
|
2437
|
+
evt.stopPropagation();
|
|
2438
|
+
// Emulate click for mobile devices
|
|
2439
|
+
if (isTouch) {
|
|
2440
|
+
var targetEl = angular.element(evt.target);
|
|
2441
|
+
targetEl.triggerHandler('click');
|
|
2442
|
+
}
|
|
2443
|
+
};
|
|
2444
|
+
$select.$onKeyDown = function (evt) {
|
|
2445
|
+
if (!/(9|13|38|40)/.test(evt.keyCode))
|
|
2446
|
+
return;
|
|
2447
|
+
evt.preventDefault();
|
|
2448
|
+
evt.stopPropagation();
|
|
2449
|
+
// Select with enter
|
|
2450
|
+
if (evt.keyCode === 13 || evt.keyCode === 9) {
|
|
2451
|
+
return $select.select(scope.$activeIndex);
|
|
2452
|
+
}
|
|
2453
|
+
// Navigate with keyboard
|
|
2454
|
+
if (evt.keyCode === 38 && scope.$activeIndex > 0)
|
|
2455
|
+
scope.$activeIndex--;
|
|
2456
|
+
else if (evt.keyCode === 40 && scope.$activeIndex < scope.$matches.length - 1)
|
|
2457
|
+
scope.$activeIndex++;
|
|
2458
|
+
else if (angular.isUndefined(scope.$activeIndex))
|
|
2459
|
+
scope.$activeIndex = 0;
|
|
2460
|
+
scope.$digest();
|
|
2461
|
+
};
|
|
2462
|
+
// Overrides
|
|
2463
|
+
var _show = $select.show;
|
|
2464
|
+
$select.show = function () {
|
|
2465
|
+
_show();
|
|
2466
|
+
if (options.multiple) {
|
|
2467
|
+
$select.$element.addClass('select-multiple');
|
|
2468
|
+
}
|
|
2469
|
+
setTimeout(function () {
|
|
2470
|
+
$select.$element.on(isTouch ? 'touchstart' : 'mousedown', $select.$onMouseDown);
|
|
2471
|
+
if (options.keyboard) {
|
|
2472
|
+
element.on('keydown', $select.$onKeyDown);
|
|
2473
|
+
}
|
|
2474
|
+
});
|
|
2475
|
+
};
|
|
2476
|
+
var _hide = $select.hide;
|
|
2477
|
+
$select.hide = function () {
|
|
2478
|
+
$select.$element.off(isTouch ? 'touchstart' : 'mousedown', $select.$onMouseDown);
|
|
2479
|
+
if (options.keyboard) {
|
|
2480
|
+
element.off('keydown', $select.$onKeyDown);
|
|
2481
|
+
}
|
|
2482
|
+
_hide();
|
|
2483
|
+
};
|
|
2484
|
+
return $select;
|
|
2485
|
+
}
|
|
2486
|
+
SelectFactory.defaults = defaults;
|
|
2487
|
+
return SelectFactory;
|
|
2488
|
+
}
|
|
2489
|
+
];
|
|
2490
|
+
}).directive('bsSelect', [
|
|
2491
|
+
'$window',
|
|
2492
|
+
'$parse',
|
|
2493
|
+
'$q',
|
|
2494
|
+
'$select',
|
|
2495
|
+
'$parseOptions',
|
|
2496
|
+
function ($window, $parse, $q, $select, $parseOptions) {
|
|
2497
|
+
var defaults = $select.defaults;
|
|
2498
|
+
return {
|
|
2499
|
+
restrict: 'EAC',
|
|
2500
|
+
require: 'ngModel',
|
|
2501
|
+
link: function postLink(scope, element, attr, controller) {
|
|
2502
|
+
// Directive options
|
|
2503
|
+
var options = { scope: scope };
|
|
2504
|
+
angular.forEach([
|
|
2505
|
+
'placement',
|
|
2506
|
+
'container',
|
|
2507
|
+
'delay',
|
|
2508
|
+
'trigger',
|
|
2509
|
+
'keyboard',
|
|
2510
|
+
'html',
|
|
2511
|
+
'animation',
|
|
2512
|
+
'template',
|
|
2513
|
+
'placeholder',
|
|
2514
|
+
'multiple',
|
|
2515
|
+
'maxLength',
|
|
2516
|
+
'maxLengthHtml'
|
|
2517
|
+
], function (key) {
|
|
2518
|
+
if (angular.isDefined(attr[key]))
|
|
2519
|
+
options[key] = attr[key];
|
|
2520
|
+
});
|
|
2521
|
+
// Add support for select markup
|
|
2522
|
+
if (element[0].nodeName.toLowerCase() === 'select') {
|
|
2523
|
+
var inputEl = element;
|
|
2524
|
+
inputEl.css('display', 'none');
|
|
2525
|
+
element = angular.element('<button type="button" class="btn btn-default"></button>');
|
|
2526
|
+
inputEl.after(element);
|
|
2527
|
+
}
|
|
2528
|
+
// Build proper ngOptions
|
|
2529
|
+
var parsedOptions = $parseOptions(attr.ngOptions);
|
|
2530
|
+
// Initialize select
|
|
2531
|
+
var select = $select(element, controller, options);
|
|
2532
|
+
// Watch ngOptions values before filtering for changes
|
|
2533
|
+
var watchedOptions = parsedOptions.$match[7].replace(/\|.+/, '').trim();
|
|
2534
|
+
scope.$watch(watchedOptions, function (newValue, oldValue) {
|
|
2535
|
+
// console.warn('scope.$watch(%s)', watchedOptions, newValue, oldValue);
|
|
2536
|
+
parsedOptions.valuesFn(scope, controller).then(function (values) {
|
|
2537
|
+
select.update(values);
|
|
2538
|
+
controller.$render();
|
|
2539
|
+
});
|
|
2540
|
+
}, true);
|
|
2541
|
+
// Watch model for changes
|
|
2542
|
+
scope.$watch(attr.ngModel, function (newValue, oldValue) {
|
|
2543
|
+
// console.warn('scope.$watch(%s)', attr.ngModel, newValue, oldValue);
|
|
2544
|
+
select.$updateActiveIndex();
|
|
2545
|
+
}, true);
|
|
2546
|
+
// Model rendering in view
|
|
2547
|
+
controller.$render = function () {
|
|
2548
|
+
// console.warn('$render', element.attr('ng-model'), 'controller.$modelValue', typeof controller.$modelValue, controller.$modelValue, 'controller.$viewValue', typeof controller.$viewValue, controller.$viewValue);
|
|
2549
|
+
var selected, index;
|
|
2550
|
+
if (options.multiple && angular.isArray(controller.$modelValue)) {
|
|
2551
|
+
selected = controller.$modelValue.map(function (value) {
|
|
2552
|
+
index = select.$getIndex(value);
|
|
2553
|
+
return angular.isDefined(index) ? select.$scope.$matches[index].label : false;
|
|
2554
|
+
}).filter(angular.isDefined);
|
|
2555
|
+
if (selected.length > (options.maxLength || defaults.maxLength)) {
|
|
2556
|
+
selected = selected.length + ' ' + (options.maxLengthHtml || defaults.maxLengthHtml);
|
|
2557
|
+
} else {
|
|
2558
|
+
selected = selected.join(', ');
|
|
2559
|
+
}
|
|
2560
|
+
} else {
|
|
2561
|
+
index = select.$getIndex(controller.$modelValue);
|
|
2562
|
+
selected = angular.isDefined(index) ? select.$scope.$matches[index].label : false;
|
|
2563
|
+
}
|
|
2564
|
+
element.html((selected ? selected : attr.placeholder || defaults.placeholder) + defaults.caretHtml);
|
|
2565
|
+
};
|
|
2566
|
+
// Garbage collection
|
|
2567
|
+
scope.$on('$destroy', function () {
|
|
2568
|
+
select.destroy();
|
|
2569
|
+
options = null;
|
|
2570
|
+
select = null;
|
|
2571
|
+
});
|
|
2572
|
+
}
|
|
2573
|
+
};
|
|
2574
|
+
}
|
|
2575
|
+
]);
|
|
2576
|
+
|
|
2577
|
+
// Source: tab.js
|
|
2578
|
+
angular.module('mgcrea.ngStrap.tab', []).run([
|
|
2579
|
+
'$templateCache',
|
|
2580
|
+
function ($templateCache) {
|
|
2581
|
+
$templateCache.put('$pane', '{{pane.content}}');
|
|
2582
|
+
}
|
|
2583
|
+
]).provider('$tab', function () {
|
|
2584
|
+
var defaults = this.defaults = {
|
|
2585
|
+
animation: 'am-fade',
|
|
2586
|
+
template: 'tab/tab.tpl.html'
|
|
2587
|
+
};
|
|
2588
|
+
this.$get = function () {
|
|
2589
|
+
return { defaults: defaults };
|
|
2590
|
+
};
|
|
2591
|
+
}).directive('bsTabs', [
|
|
2592
|
+
'$window',
|
|
2593
|
+
'$animate',
|
|
2594
|
+
'$tab',
|
|
2595
|
+
function ($window, $animate, $tab) {
|
|
2596
|
+
var defaults = $tab.defaults;
|
|
2597
|
+
return {
|
|
2598
|
+
restrict: 'EAC',
|
|
2599
|
+
scope: true,
|
|
2600
|
+
require: '?ngModel',
|
|
2601
|
+
templateUrl: function (element, attr) {
|
|
2602
|
+
return attr.template || defaults.template;
|
|
2603
|
+
},
|
|
2604
|
+
link: function postLink(scope, element, attr, controller) {
|
|
2605
|
+
// Directive options
|
|
2606
|
+
var options = defaults;
|
|
2607
|
+
angular.forEach(['animation'], function (key) {
|
|
2608
|
+
if (angular.isDefined(attr[key]))
|
|
2609
|
+
options[key] = attr[key];
|
|
2610
|
+
});
|
|
2611
|
+
// Require scope as an object
|
|
2612
|
+
attr.bsTabs && scope.$watch(attr.bsTabs, function (newValue, oldValue) {
|
|
2613
|
+
scope.panes = newValue;
|
|
2614
|
+
}, true);
|
|
2615
|
+
// Add base class
|
|
2616
|
+
element.addClass('tabs');
|
|
2617
|
+
// Support animations
|
|
2618
|
+
if (options.animation) {
|
|
2619
|
+
element.addClass(options.animation);
|
|
2620
|
+
}
|
|
2621
|
+
scope.active = scope.activePane = 0;
|
|
2622
|
+
// view -> model
|
|
2623
|
+
scope.setActive = function (index, ev) {
|
|
2624
|
+
scope.active = index;
|
|
2625
|
+
if (controller) {
|
|
2626
|
+
controller.$setViewValue(index);
|
|
2627
|
+
}
|
|
2628
|
+
};
|
|
2629
|
+
// model -> view
|
|
2630
|
+
if (controller) {
|
|
2631
|
+
controller.$render = function () {
|
|
2632
|
+
scope.active = controller.$modelValue * 1;
|
|
2633
|
+
};
|
|
2634
|
+
}
|
|
2635
|
+
}
|
|
2636
|
+
};
|
|
2637
|
+
}
|
|
2638
|
+
]);
|
|
2639
|
+
|
|
2640
|
+
// Source: timepicker.js
|
|
2641
|
+
angular.module('mgcrea.ngStrap.timepicker', [
|
|
2642
|
+
'mgcrea.ngStrap.helpers.dateParser',
|
|
2643
|
+
'mgcrea.ngStrap.tooltip'
|
|
2644
|
+
]).provider('$timepicker', function () {
|
|
2645
|
+
var defaults = this.defaults = {
|
|
2646
|
+
animation: 'am-fade',
|
|
2647
|
+
prefixClass: 'timepicker',
|
|
2648
|
+
placement: 'bottom-left',
|
|
2649
|
+
template: 'timepicker/timepicker.tpl.html',
|
|
2650
|
+
trigger: 'focus',
|
|
2651
|
+
container: false,
|
|
2652
|
+
keyboard: true,
|
|
2653
|
+
html: false,
|
|
2654
|
+
delay: 0,
|
|
2655
|
+
useNative: true,
|
|
2656
|
+
timeType: 'date',
|
|
2657
|
+
timeFormat: 'shortTime',
|
|
2658
|
+
autoclose: false,
|
|
2659
|
+
minTime: -Infinity,
|
|
2660
|
+
maxTime: +Infinity,
|
|
2661
|
+
length: 5,
|
|
2662
|
+
hourStep: 1,
|
|
2663
|
+
minuteStep: 5
|
|
2664
|
+
};
|
|
2665
|
+
this.$get = [
|
|
2666
|
+
'$window',
|
|
2667
|
+
'$document',
|
|
2668
|
+
'$rootScope',
|
|
2669
|
+
'$sce',
|
|
2670
|
+
'$locale',
|
|
2671
|
+
'dateFilter',
|
|
2672
|
+
'$tooltip',
|
|
2673
|
+
function ($window, $document, $rootScope, $sce, $locale, dateFilter, $tooltip) {
|
|
2674
|
+
var bodyEl = angular.element($window.document.body);
|
|
2675
|
+
var isTouch = 'createTouch' in $window.document;
|
|
2676
|
+
var isNative = /(ip(a|o)d|iphone|android)/gi.test($window.navigator.userAgent);
|
|
2677
|
+
if (!defaults.lang)
|
|
2678
|
+
defaults.lang = $locale.id;
|
|
2679
|
+
function timepickerFactory(element, controller, config) {
|
|
2680
|
+
var $timepicker = $tooltip(element, angular.extend({}, defaults, config));
|
|
2681
|
+
var parentScope = config.scope;
|
|
2682
|
+
var options = $timepicker.$options;
|
|
2683
|
+
var scope = $timepicker.$scope;
|
|
2684
|
+
// View vars
|
|
2685
|
+
var selectedIndex = 0;
|
|
2686
|
+
var startDate = controller.$dateValue || new Date();
|
|
2687
|
+
var viewDate = {
|
|
2688
|
+
hour: startDate.getHours(),
|
|
2689
|
+
meridian: startDate.getHours() < 12,
|
|
2690
|
+
minute: startDate.getMinutes(),
|
|
2691
|
+
second: startDate.getSeconds(),
|
|
2692
|
+
millisecond: startDate.getMilliseconds()
|
|
2693
|
+
};
|
|
2694
|
+
var format = $locale.DATETIME_FORMATS[options.timeFormat] || options.timeFormat;
|
|
2695
|
+
var formats = /(h+)[:]?(m+)[ ]?(a?)/i.exec(format).slice(1);
|
|
2696
|
+
// Scope methods
|
|
2697
|
+
scope.$select = function (date, index) {
|
|
2698
|
+
$timepicker.select(date, index);
|
|
2699
|
+
};
|
|
2700
|
+
scope.$moveIndex = function (value, index) {
|
|
2701
|
+
$timepicker.$moveIndex(value, index);
|
|
2702
|
+
};
|
|
2703
|
+
scope.$switchMeridian = function (date) {
|
|
2704
|
+
$timepicker.switchMeridian(date);
|
|
2705
|
+
};
|
|
2706
|
+
// Public methods
|
|
2707
|
+
$timepicker.update = function (date) {
|
|
2708
|
+
// console.warn('$timepicker.update() newValue=%o', date);
|
|
2709
|
+
if (angular.isDate(date) && !isNaN(date.getTime())) {
|
|
2710
|
+
$timepicker.$date = date;
|
|
2711
|
+
angular.extend(viewDate, {
|
|
2712
|
+
hour: date.getHours(),
|
|
2713
|
+
minute: date.getMinutes(),
|
|
2714
|
+
second: date.getSeconds(),
|
|
2715
|
+
millisecond: date.getMilliseconds()
|
|
2716
|
+
});
|
|
2717
|
+
$timepicker.$build();
|
|
2718
|
+
} else if (!$timepicker.$isBuilt) {
|
|
2719
|
+
$timepicker.$build();
|
|
2720
|
+
}
|
|
2721
|
+
};
|
|
2722
|
+
$timepicker.select = function (date, index, keep) {
|
|
2723
|
+
// console.warn('$timepicker.select', date, scope.$mode);
|
|
2724
|
+
if (!controller.$dateValue || isNaN(controller.$dateValue.getTime()))
|
|
2725
|
+
controller.$dateValue = new Date(1970, 0, 1);
|
|
2726
|
+
if (!angular.isDate(date))
|
|
2727
|
+
date = new Date(date);
|
|
2728
|
+
if (index === 0)
|
|
2729
|
+
controller.$dateValue.setHours(date.getHours());
|
|
2730
|
+
else if (index === 1)
|
|
2731
|
+
controller.$dateValue.setMinutes(date.getMinutes());
|
|
2732
|
+
controller.$setViewValue(controller.$dateValue);
|
|
2733
|
+
controller.$render();
|
|
2734
|
+
if (options.autoclose && !keep) {
|
|
2735
|
+
$timepicker.hide(true);
|
|
2736
|
+
}
|
|
2737
|
+
};
|
|
2738
|
+
$timepicker.switchMeridian = function (date) {
|
|
2739
|
+
var hours = (date || controller.$dateValue).getHours();
|
|
2740
|
+
controller.$dateValue.setHours(hours < 12 ? hours + 12 : hours - 12);
|
|
2741
|
+
controller.$render();
|
|
2742
|
+
};
|
|
2743
|
+
// Protected methods
|
|
2744
|
+
$timepicker.$build = function () {
|
|
2745
|
+
// console.warn('$timepicker.$build() viewDate=%o', viewDate);
|
|
2746
|
+
var i, midIndex = scope.midIndex = parseInt(options.length / 2, 10);
|
|
2747
|
+
var hours = [], hour;
|
|
2748
|
+
for (i = 0; i < options.length; i++) {
|
|
2749
|
+
hour = new Date(1970, 0, 1, viewDate.hour - (midIndex - i) * options.hourStep);
|
|
2750
|
+
hours.push({
|
|
2751
|
+
date: hour,
|
|
2752
|
+
label: dateFilter(hour, formats[0]),
|
|
2753
|
+
selected: $timepicker.$date && $timepicker.$isSelected(hour, 0),
|
|
2754
|
+
disabled: $timepicker.$isDisabled(hour, 0)
|
|
2755
|
+
});
|
|
2756
|
+
}
|
|
2757
|
+
var minutes = [], minute;
|
|
2758
|
+
for (i = 0; i < options.length; i++) {
|
|
2759
|
+
minute = new Date(1970, 0, 1, 0, viewDate.minute - (midIndex - i) * options.minuteStep);
|
|
2760
|
+
minutes.push({
|
|
2761
|
+
date: minute,
|
|
2762
|
+
label: dateFilter(minute, formats[1]),
|
|
2763
|
+
selected: $timepicker.$date && $timepicker.$isSelected(minute, 1),
|
|
2764
|
+
disabled: $timepicker.$isDisabled(minute, 1)
|
|
2765
|
+
});
|
|
2766
|
+
}
|
|
2767
|
+
var rows = [];
|
|
2768
|
+
for (i = 0; i < options.length; i++) {
|
|
2769
|
+
rows.push([
|
|
2770
|
+
hours[i],
|
|
2771
|
+
minutes[i]
|
|
2772
|
+
]);
|
|
2773
|
+
}
|
|
2774
|
+
scope.rows = rows;
|
|
2775
|
+
scope.showAM = !!formats[2];
|
|
2776
|
+
scope.isAM = ($timepicker.$date || hours[midIndex].date).getHours() < 12;
|
|
2777
|
+
$timepicker.$isBuilt = true;
|
|
2778
|
+
};
|
|
2779
|
+
$timepicker.$isSelected = function (date, index) {
|
|
2780
|
+
if (!$timepicker.$date)
|
|
2781
|
+
return false;
|
|
2782
|
+
else if (index === 0) {
|
|
2783
|
+
return date.getHours() === $timepicker.$date.getHours();
|
|
2784
|
+
} else if (index === 1) {
|
|
2785
|
+
return date.getMinutes() === $timepicker.$date.getMinutes();
|
|
2786
|
+
}
|
|
2787
|
+
};
|
|
2788
|
+
$timepicker.$isDisabled = function (date, index) {
|
|
2789
|
+
var selectedTime;
|
|
2790
|
+
if (index === 0) {
|
|
2791
|
+
selectedTime = date.getTime() + viewDate.minute * 60000;
|
|
2792
|
+
} else if (index === 1) {
|
|
2793
|
+
selectedTime = date.getTime() + viewDate.hour * 3600000;
|
|
2794
|
+
}
|
|
2795
|
+
return selectedTime < options.minTime || selectedTime > options.maxTime;
|
|
2796
|
+
};
|
|
2797
|
+
$timepicker.$moveIndex = function (value, index) {
|
|
2798
|
+
var targetDate;
|
|
2799
|
+
if (index === 0) {
|
|
2800
|
+
targetDate = new Date(1970, 0, 1, viewDate.hour + value * options.length, viewDate.minute);
|
|
2801
|
+
angular.extend(viewDate, { hour: targetDate.getHours() });
|
|
2802
|
+
} else if (index === 1) {
|
|
2803
|
+
targetDate = new Date(1970, 0, 1, viewDate.hour, viewDate.minute + value * options.length * options.minuteStep);
|
|
2804
|
+
angular.extend(viewDate, { minute: targetDate.getMinutes() });
|
|
2805
|
+
}
|
|
2806
|
+
$timepicker.$build();
|
|
2807
|
+
};
|
|
2808
|
+
$timepicker.$onMouseDown = function (evt) {
|
|
2809
|
+
// Prevent blur on mousedown on .dropdown-menu
|
|
2810
|
+
if (evt.target.nodeName.toLowerCase() !== 'input')
|
|
2811
|
+
evt.preventDefault();
|
|
2812
|
+
evt.stopPropagation();
|
|
2813
|
+
// Emulate click for mobile devices
|
|
2814
|
+
if (isTouch) {
|
|
2815
|
+
var targetEl = angular.element(evt.target);
|
|
2816
|
+
if (targetEl[0].nodeName.toLowerCase() !== 'button') {
|
|
2817
|
+
targetEl = targetEl.parent();
|
|
2818
|
+
}
|
|
2819
|
+
targetEl.triggerHandler('click');
|
|
2820
|
+
}
|
|
2821
|
+
};
|
|
2822
|
+
$timepicker.$onKeyDown = function (evt) {
|
|
2823
|
+
if (!/(38|37|39|40|13)/.test(evt.keyCode) || evt.shiftKey || evt.altKey)
|
|
2824
|
+
return;
|
|
2825
|
+
evt.preventDefault();
|
|
2826
|
+
evt.stopPropagation();
|
|
2827
|
+
// Close on enter
|
|
2828
|
+
if (evt.keyCode === 13)
|
|
2829
|
+
return $timepicker.hide(true);
|
|
2830
|
+
// Navigate with keyboard
|
|
2831
|
+
var newDate = new Date($timepicker.$date);
|
|
2832
|
+
var hours = newDate.getHours(), hoursLength = dateFilter(newDate, 'h').length;
|
|
2833
|
+
var minutes = newDate.getMinutes(), minutesLength = dateFilter(newDate, 'mm').length;
|
|
2834
|
+
var lateralMove = /(37|39)/.test(evt.keyCode);
|
|
2835
|
+
var count = 2 + !!formats[2] * 1;
|
|
2836
|
+
// Navigate indexes (left, right)
|
|
2837
|
+
if (lateralMove) {
|
|
2838
|
+
if (evt.keyCode === 37)
|
|
2839
|
+
selectedIndex = selectedIndex < 1 ? count - 1 : selectedIndex - 1;
|
|
2840
|
+
else if (evt.keyCode === 39)
|
|
2841
|
+
selectedIndex = selectedIndex < count - 1 ? selectedIndex + 1 : 0;
|
|
2842
|
+
}
|
|
2843
|
+
// Update values (up, down)
|
|
2844
|
+
if (selectedIndex === 0) {
|
|
2845
|
+
if (lateralMove)
|
|
2846
|
+
return createSelection(0, hoursLength);
|
|
2847
|
+
if (evt.keyCode === 38)
|
|
2848
|
+
newDate.setHours(hours - parseInt(options.hourStep, 10));
|
|
2849
|
+
else if (evt.keyCode === 40)
|
|
2850
|
+
newDate.setHours(hours + parseInt(options.hourStep, 10));
|
|
2851
|
+
} else if (selectedIndex === 1) {
|
|
2852
|
+
if (lateralMove)
|
|
2853
|
+
return createSelection(hoursLength + 1, hoursLength + 1 + minutesLength);
|
|
2854
|
+
if (evt.keyCode === 38)
|
|
2855
|
+
newDate.setMinutes(minutes - parseInt(options.minuteStep, 10));
|
|
2856
|
+
else if (evt.keyCode === 40)
|
|
2857
|
+
newDate.setMinutes(minutes + parseInt(options.minuteStep, 10));
|
|
2858
|
+
} else if (selectedIndex === 2) {
|
|
2859
|
+
if (lateralMove)
|
|
2860
|
+
return createSelection(hoursLength + 1 + minutesLength + 1, hoursLength + 1 + minutesLength + 3);
|
|
2861
|
+
$timepicker.switchMeridian();
|
|
2862
|
+
}
|
|
2863
|
+
$timepicker.select(newDate, selectedIndex, true);
|
|
2864
|
+
parentScope.$digest();
|
|
2865
|
+
};
|
|
2866
|
+
// Private
|
|
2867
|
+
function createSelection(start, end) {
|
|
2868
|
+
if (element[0].createTextRange) {
|
|
2869
|
+
var selRange = element[0].createTextRange();
|
|
2870
|
+
selRange.collapse(true);
|
|
2871
|
+
selRange.moveStart('character', start);
|
|
2872
|
+
selRange.moveEnd('character', end);
|
|
2873
|
+
selRange.select();
|
|
2874
|
+
} else if (element[0].setSelectionRange) {
|
|
2875
|
+
element[0].setSelectionRange(start, end);
|
|
2876
|
+
} else if (angular.isUndefined(element[0].selectionStart)) {
|
|
2877
|
+
element[0].selectionStart = start;
|
|
2878
|
+
element[0].selectionEnd = end;
|
|
2879
|
+
}
|
|
2880
|
+
}
|
|
2881
|
+
function focusElement() {
|
|
2882
|
+
element[0].focus();
|
|
2883
|
+
}
|
|
2884
|
+
// Overrides
|
|
2885
|
+
var _init = $timepicker.init;
|
|
2886
|
+
$timepicker.init = function () {
|
|
2887
|
+
if (isNative && options.useNative) {
|
|
2888
|
+
element.prop('type', 'time');
|
|
2889
|
+
element.css('-webkit-appearance', 'textfield');
|
|
2890
|
+
return;
|
|
2891
|
+
} else if (isTouch) {
|
|
2892
|
+
element.prop('type', 'text');
|
|
2893
|
+
element.attr('readonly', 'true');
|
|
2894
|
+
element.on('click', focusElement);
|
|
2895
|
+
}
|
|
2896
|
+
_init();
|
|
2897
|
+
};
|
|
2898
|
+
var _destroy = $timepicker.destroy;
|
|
2899
|
+
$timepicker.destroy = function () {
|
|
2900
|
+
if (isNative && options.useNative) {
|
|
2901
|
+
element.off('click', focusElement);
|
|
2902
|
+
}
|
|
2903
|
+
_destroy();
|
|
2904
|
+
};
|
|
2905
|
+
var _show = $timepicker.show;
|
|
2906
|
+
$timepicker.show = function () {
|
|
2907
|
+
_show();
|
|
2908
|
+
setTimeout(function () {
|
|
2909
|
+
$timepicker.$element.on(isTouch ? 'touchstart' : 'mousedown', $timepicker.$onMouseDown);
|
|
2910
|
+
if (options.keyboard) {
|
|
2911
|
+
element.on('keydown', $timepicker.$onKeyDown);
|
|
2912
|
+
}
|
|
2913
|
+
});
|
|
2914
|
+
};
|
|
2915
|
+
var _hide = $timepicker.hide;
|
|
2916
|
+
$timepicker.hide = function (blur) {
|
|
2917
|
+
$timepicker.$element.off(isTouch ? 'touchstart' : 'mousedown', $timepicker.$onMouseDown);
|
|
2918
|
+
if (options.keyboard) {
|
|
2919
|
+
element.off('keydown', $timepicker.$onKeyDown);
|
|
2920
|
+
}
|
|
2921
|
+
_hide(blur);
|
|
2922
|
+
};
|
|
2923
|
+
return $timepicker;
|
|
2924
|
+
}
|
|
2925
|
+
timepickerFactory.defaults = defaults;
|
|
2926
|
+
return timepickerFactory;
|
|
2927
|
+
}
|
|
2928
|
+
];
|
|
2929
|
+
}).directive('bsTimepicker', [
|
|
2930
|
+
'$window',
|
|
2931
|
+
'$parse',
|
|
2932
|
+
'$q',
|
|
2933
|
+
'$locale',
|
|
2934
|
+
'dateFilter',
|
|
2935
|
+
'$timepicker',
|
|
2936
|
+
'$dateParser',
|
|
2937
|
+
'$timeout',
|
|
2938
|
+
function ($window, $parse, $q, $locale, dateFilter, $timepicker, $dateParser, $timeout) {
|
|
2939
|
+
var defaults = $timepicker.defaults;
|
|
2940
|
+
var isNative = /(ip(a|o)d|iphone|android)/gi.test($window.navigator.userAgent);
|
|
2941
|
+
var requestAnimationFrame = $window.requestAnimationFrame || $window.setTimeout;
|
|
2942
|
+
return {
|
|
2943
|
+
restrict: 'EAC',
|
|
2944
|
+
require: 'ngModel',
|
|
2945
|
+
link: function postLink(scope, element, attr, controller) {
|
|
2946
|
+
// Directive options
|
|
2947
|
+
var options = {
|
|
2948
|
+
scope: scope,
|
|
2949
|
+
controller: controller
|
|
2950
|
+
};
|
|
2951
|
+
angular.forEach([
|
|
2952
|
+
'placement',
|
|
2953
|
+
'container',
|
|
2954
|
+
'delay',
|
|
2955
|
+
'trigger',
|
|
2956
|
+
'keyboard',
|
|
2957
|
+
'html',
|
|
2958
|
+
'animation',
|
|
2959
|
+
'template',
|
|
2960
|
+
'autoclose',
|
|
2961
|
+
'timeType',
|
|
2962
|
+
'timeFormat',
|
|
2963
|
+
'useNative',
|
|
2964
|
+
'hourStep',
|
|
2965
|
+
'minuteStep'
|
|
2966
|
+
], function (key) {
|
|
2967
|
+
if (angular.isDefined(attr[key]))
|
|
2968
|
+
options[key] = attr[key];
|
|
2969
|
+
});
|
|
2970
|
+
// Initialize timepicker
|
|
2971
|
+
if (isNative && (options.useNative || defaults.useNative))
|
|
2972
|
+
options.timeFormat = 'HH:mm';
|
|
2973
|
+
var timepicker = $timepicker(element, controller, options);
|
|
2974
|
+
options = timepicker.$options;
|
|
2975
|
+
// Initialize parser
|
|
2976
|
+
var dateParser = $dateParser({
|
|
2977
|
+
format: options.timeFormat,
|
|
2978
|
+
lang: options.lang
|
|
2979
|
+
});
|
|
2980
|
+
// Observe attributes for changes
|
|
2981
|
+
angular.forEach([
|
|
2982
|
+
'minTime',
|
|
2983
|
+
'maxTime'
|
|
2984
|
+
], function (key) {
|
|
2985
|
+
// console.warn('attr.$observe(%s)', key, attr[key]);
|
|
2986
|
+
angular.isDefined(attr[key]) && attr.$observe(key, function (newValue) {
|
|
2987
|
+
if (newValue === 'now') {
|
|
2988
|
+
timepicker.$options[key] = new Date().setFullYear(1970, 0, 1);
|
|
2989
|
+
} else if (angular.isString(newValue) && newValue.match(/^".+"$/)) {
|
|
2990
|
+
timepicker.$options[key] = +new Date(newValue.substr(1, newValue.length - 2));
|
|
2991
|
+
} else {
|
|
2992
|
+
timepicker.$options[key] = dateParser.parse(newValue);
|
|
2993
|
+
}
|
|
2994
|
+
!isNaN(timepicker.$options[key]) && timepicker.$build();
|
|
2995
|
+
});
|
|
2996
|
+
});
|
|
2997
|
+
// Watch model for changes
|
|
2998
|
+
scope.$watch(attr.ngModel, function (newValue, oldValue) {
|
|
2999
|
+
// console.warn('scope.$watch(%s)', attr.ngModel, newValue, oldValue, controller.$dateValue);
|
|
3000
|
+
timepicker.update(controller.$dateValue);
|
|
3001
|
+
}, true);
|
|
3002
|
+
// viewValue -> $parsers -> modelValue
|
|
3003
|
+
controller.$parsers.unshift(function (viewValue) {
|
|
3004
|
+
// console.warn('$parser("%s"): viewValue=%o', element.attr('ng-model'), viewValue);
|
|
3005
|
+
// Null values should correctly reset the model value & validity
|
|
3006
|
+
if (!viewValue) {
|
|
3007
|
+
controller.$setValidity('date', true);
|
|
3008
|
+
return;
|
|
3009
|
+
}
|
|
3010
|
+
var parsedTime = dateParser.parse(viewValue, controller.$dateValue);
|
|
3011
|
+
if (!parsedTime || isNaN(parsedTime.getTime())) {
|
|
3012
|
+
controller.$setValidity('date', false);
|
|
3013
|
+
} else {
|
|
3014
|
+
var isValid = parsedTime.getTime() >= options.minTime && parsedTime.getTime() <= options.maxTime;
|
|
3015
|
+
controller.$setValidity('date', isValid);
|
|
3016
|
+
// Only update the model when we have a valid date
|
|
3017
|
+
if (isValid)
|
|
3018
|
+
controller.$dateValue = parsedTime;
|
|
3019
|
+
}
|
|
3020
|
+
if (options.timeType === 'string') {
|
|
3021
|
+
return dateFilter(viewValue, options.timeFormat);
|
|
3022
|
+
} else if (options.timeType === 'number') {
|
|
3023
|
+
return controller.$dateValue.getTime();
|
|
3024
|
+
} else if (options.timeType === 'iso') {
|
|
3025
|
+
return controller.$dateValue.toISOString();
|
|
3026
|
+
} else {
|
|
3027
|
+
return new Date(controller.$dateValue);
|
|
3028
|
+
}
|
|
3029
|
+
});
|
|
3030
|
+
// modelValue -> $formatters -> viewValue
|
|
3031
|
+
controller.$formatters.push(function (modelValue) {
|
|
3032
|
+
// console.warn('$formatter("%s"): modelValue=%o (%o)', element.attr('ng-model'), modelValue, typeof modelValue);
|
|
3033
|
+
var date;
|
|
3034
|
+
if (angular.isUndefined(modelValue) || modelValue === null) {
|
|
3035
|
+
date = NaN;
|
|
3036
|
+
} else if (angular.isDate(modelValue)) {
|
|
3037
|
+
date = modelValue;
|
|
3038
|
+
} else if (options.timeType === 'string') {
|
|
3039
|
+
date = dateParser.parse(modelValue);
|
|
3040
|
+
} else {
|
|
3041
|
+
date = new Date(modelValue);
|
|
3042
|
+
}
|
|
3043
|
+
// Setup default value?
|
|
3044
|
+
// if(isNaN(date.getTime())) date = new Date(new Date().setMinutes(0) + 36e5);
|
|
3045
|
+
controller.$dateValue = date;
|
|
3046
|
+
return controller.$dateValue;
|
|
3047
|
+
});
|
|
3048
|
+
// viewValue -> element
|
|
3049
|
+
controller.$render = function () {
|
|
3050
|
+
// console.warn('$render("%s"): viewValue=%o', element.attr('ng-model'), controller.$viewValue);
|
|
3051
|
+
element.val(!controller.$dateValue || isNaN(controller.$dateValue.getTime()) ? '' : dateFilter(controller.$dateValue, options.timeFormat));
|
|
3052
|
+
};
|
|
3053
|
+
// Garbage collection
|
|
3054
|
+
scope.$on('$destroy', function () {
|
|
3055
|
+
timepicker.destroy();
|
|
3056
|
+
options = null;
|
|
3057
|
+
timepicker = null;
|
|
3058
|
+
});
|
|
3059
|
+
}
|
|
3060
|
+
};
|
|
3061
|
+
}
|
|
3062
|
+
]);
|
|
3063
|
+
|
|
3064
|
+
// Source: tooltip.js
|
|
3065
|
+
angular.module('mgcrea.ngStrap.tooltip', ['mgcrea.ngStrap.helpers.dimensions']).provider('$tooltip', function () {
|
|
3066
|
+
var defaults = this.defaults = {
|
|
3067
|
+
animation: 'am-fade',
|
|
3068
|
+
prefixClass: 'tooltip',
|
|
3069
|
+
prefixEvent: 'tooltip',
|
|
3070
|
+
container: false,
|
|
3071
|
+
placement: 'top',
|
|
3072
|
+
template: 'tooltip/tooltip.tpl.html',
|
|
3073
|
+
contentTemplate: false,
|
|
3074
|
+
trigger: 'hover focus',
|
|
3075
|
+
keyboard: false,
|
|
3076
|
+
html: false,
|
|
3077
|
+
show: false,
|
|
3078
|
+
title: '',
|
|
3079
|
+
type: '',
|
|
3080
|
+
delay: 0
|
|
3081
|
+
};
|
|
3082
|
+
this.$get = [
|
|
3083
|
+
'$window',
|
|
3084
|
+
'$rootScope',
|
|
3085
|
+
'$compile',
|
|
3086
|
+
'$q',
|
|
3087
|
+
'$templateCache',
|
|
3088
|
+
'$http',
|
|
3089
|
+
'$animate',
|
|
3090
|
+
'$timeout',
|
|
3091
|
+
'dimensions',
|
|
3092
|
+
'$$rAF',
|
|
3093
|
+
function ($window, $rootScope, $compile, $q, $templateCache, $http, $animate, $timeout, dimensions, $$rAF) {
|
|
3094
|
+
var trim = String.prototype.trim;
|
|
3095
|
+
var isTouch = 'createTouch' in $window.document;
|
|
3096
|
+
var htmlReplaceRegExp = /ng-bind="/gi;
|
|
3097
|
+
function TooltipFactory(element, config) {
|
|
3098
|
+
var $tooltip = {};
|
|
3099
|
+
// Common vars
|
|
3100
|
+
var options = $tooltip.$options = angular.extend({}, defaults, config);
|
|
3101
|
+
$tooltip.$promise = fetchTemplate(options.template);
|
|
3102
|
+
var scope = $tooltip.$scope = options.scope && options.scope.$new() || $rootScope.$new();
|
|
3103
|
+
if (options.delay && angular.isString(options.delay)) {
|
|
3104
|
+
options.delay = parseFloat(options.delay);
|
|
3105
|
+
}
|
|
3106
|
+
// Support scope as string options
|
|
3107
|
+
if (options.title) {
|
|
3108
|
+
$tooltip.$scope.title = options.title;
|
|
3109
|
+
}
|
|
3110
|
+
// Provide scope helpers
|
|
3111
|
+
scope.$hide = function () {
|
|
3112
|
+
scope.$$postDigest(function () {
|
|
3113
|
+
$tooltip.hide();
|
|
3114
|
+
});
|
|
3115
|
+
};
|
|
3116
|
+
scope.$show = function () {
|
|
3117
|
+
scope.$$postDigest(function () {
|
|
3118
|
+
$tooltip.show();
|
|
3119
|
+
});
|
|
3120
|
+
};
|
|
3121
|
+
scope.$toggle = function () {
|
|
3122
|
+
scope.$$postDigest(function () {
|
|
3123
|
+
$tooltip.toggle();
|
|
3124
|
+
});
|
|
3125
|
+
};
|
|
3126
|
+
$tooltip.$isShown = scope.$isShown = false;
|
|
3127
|
+
// Private vars
|
|
3128
|
+
var timeout, hoverState;
|
|
3129
|
+
// Support contentTemplate option
|
|
3130
|
+
if (options.contentTemplate) {
|
|
3131
|
+
$tooltip.$promise = $tooltip.$promise.then(function (template) {
|
|
3132
|
+
var templateEl = angular.element(template);
|
|
3133
|
+
return fetchTemplate(options.contentTemplate).then(function (contentTemplate) {
|
|
3134
|
+
var contentEl = findElement('[ng-bind="content"]', templateEl[0]);
|
|
3135
|
+
if (!contentEl.length)
|
|
3136
|
+
contentEl = findElement('[ng-bind="title"]', templateEl[0]);
|
|
3137
|
+
contentEl.removeAttr('ng-bind').html(contentTemplate);
|
|
3138
|
+
return templateEl[0].outerHTML;
|
|
3139
|
+
});
|
|
3140
|
+
});
|
|
3141
|
+
}
|
|
3142
|
+
// Fetch, compile then initialize tooltip
|
|
3143
|
+
var tipLinker, tipElement, tipTemplate, tipContainer;
|
|
3144
|
+
$tooltip.$promise.then(function (template) {
|
|
3145
|
+
if (angular.isObject(template))
|
|
3146
|
+
template = template.data;
|
|
3147
|
+
if (options.html)
|
|
3148
|
+
template = template.replace(htmlReplaceRegExp, 'ng-bind-html="');
|
|
3149
|
+
template = trim.apply(template);
|
|
3150
|
+
tipTemplate = template;
|
|
3151
|
+
tipLinker = $compile(template);
|
|
3152
|
+
$tooltip.init();
|
|
3153
|
+
});
|
|
3154
|
+
$tooltip.init = function () {
|
|
3155
|
+
// Options: delay
|
|
3156
|
+
if (options.delay && angular.isNumber(options.delay)) {
|
|
3157
|
+
options.delay = {
|
|
3158
|
+
show: options.delay,
|
|
3159
|
+
hide: options.delay
|
|
3160
|
+
};
|
|
3161
|
+
}
|
|
3162
|
+
// Replace trigger on touch devices ?
|
|
3163
|
+
// if(isTouch && options.trigger === defaults.trigger) {
|
|
3164
|
+
// options.trigger.replace(/hover/g, 'click');
|
|
3165
|
+
// }
|
|
3166
|
+
// Options : container
|
|
3167
|
+
if (options.container === 'self') {
|
|
3168
|
+
tipContainer = element;
|
|
3169
|
+
} else if (options.container) {
|
|
3170
|
+
tipContainer = findElement(options.container);
|
|
3171
|
+
}
|
|
3172
|
+
// Options: trigger
|
|
3173
|
+
var triggers = options.trigger.split(' ');
|
|
3174
|
+
angular.forEach(triggers, function (trigger) {
|
|
3175
|
+
if (trigger === 'click') {
|
|
3176
|
+
element.on('click', $tooltip.toggle);
|
|
3177
|
+
} else if (trigger !== 'manual') {
|
|
3178
|
+
element.on(trigger === 'hover' ? 'mouseenter' : 'focus', $tooltip.enter);
|
|
3179
|
+
element.on(trigger === 'hover' ? 'mouseleave' : 'blur', $tooltip.leave);
|
|
3180
|
+
trigger !== 'hover' && element.on(isTouch ? 'touchstart' : 'mousedown', $tooltip.$onFocusElementMouseDown);
|
|
3181
|
+
}
|
|
3182
|
+
});
|
|
3183
|
+
// Options: show
|
|
3184
|
+
if (options.show) {
|
|
3185
|
+
scope.$$postDigest(function () {
|
|
3186
|
+
options.trigger === 'focus' ? element[0].focus() : $tooltip.show();
|
|
3187
|
+
});
|
|
3188
|
+
}
|
|
3189
|
+
};
|
|
3190
|
+
$tooltip.destroy = function () {
|
|
3191
|
+
// Unbind events
|
|
3192
|
+
var triggers = options.trigger.split(' ');
|
|
3193
|
+
for (var i = triggers.length; i--;) {
|
|
3194
|
+
var trigger = triggers[i];
|
|
3195
|
+
if (trigger === 'click') {
|
|
3196
|
+
element.off('click', $tooltip.toggle);
|
|
3197
|
+
} else if (trigger !== 'manual') {
|
|
3198
|
+
element.off(trigger === 'hover' ? 'mouseenter' : 'focus', $tooltip.enter);
|
|
3199
|
+
element.off(trigger === 'hover' ? 'mouseleave' : 'blur', $tooltip.leave);
|
|
3200
|
+
trigger !== 'hover' && element.off(isTouch ? 'touchstart' : 'mousedown', $tooltip.$onFocusElementMouseDown);
|
|
3201
|
+
}
|
|
3202
|
+
}
|
|
3203
|
+
// Remove element
|
|
3204
|
+
if (tipElement) {
|
|
3205
|
+
tipElement.remove();
|
|
3206
|
+
tipElement = null;
|
|
3207
|
+
}
|
|
3208
|
+
// Destroy scope
|
|
3209
|
+
scope.$destroy();
|
|
3210
|
+
};
|
|
3211
|
+
$tooltip.enter = function () {
|
|
3212
|
+
clearTimeout(timeout);
|
|
3213
|
+
hoverState = 'in';
|
|
3214
|
+
if (!options.delay || !options.delay.show) {
|
|
3215
|
+
return $tooltip.show();
|
|
3216
|
+
}
|
|
3217
|
+
timeout = setTimeout(function () {
|
|
3218
|
+
if (hoverState === 'in')
|
|
3219
|
+
$tooltip.show();
|
|
3220
|
+
}, options.delay.show);
|
|
3221
|
+
};
|
|
3222
|
+
$tooltip.show = function () {
|
|
3223
|
+
scope.$emit(options.prefixEvent + '.show.before', $tooltip);
|
|
3224
|
+
var parent = options.container ? tipContainer : null;
|
|
3225
|
+
var after = options.container ? null : element;
|
|
3226
|
+
// Hide any existing tipElement
|
|
3227
|
+
if (tipElement)
|
|
3228
|
+
tipElement.remove();
|
|
3229
|
+
// Fetch a cloned element linked from template
|
|
3230
|
+
tipElement = $tooltip.$element = tipLinker(scope, function (clonedElement, scope) {
|
|
3231
|
+
});
|
|
3232
|
+
// Set the initial positioning.
|
|
3233
|
+
tipElement.css({
|
|
3234
|
+
top: '0px',
|
|
3235
|
+
left: '0px',
|
|
3236
|
+
display: 'block'
|
|
3237
|
+
}).addClass(options.placement);
|
|
3238
|
+
// Options: animation
|
|
3239
|
+
if (options.animation)
|
|
3240
|
+
tipElement.addClass(options.animation);
|
|
3241
|
+
// Options: type
|
|
3242
|
+
if (options.type)
|
|
3243
|
+
tipElement.addClass(options.prefixClass + '-' + options.type);
|
|
3244
|
+
$animate.enter(tipElement, parent, after, function () {
|
|
3245
|
+
scope.$emit(options.prefixEvent + '.show', $tooltip);
|
|
3246
|
+
});
|
|
3247
|
+
$tooltip.$isShown = scope.$isShown = true;
|
|
3248
|
+
scope.$$phase || scope.$root.$$phase || scope.$digest();
|
|
3249
|
+
$$rAF($tooltip.$applyPlacement);
|
|
3250
|
+
// var a = bodyEl.offsetWidth + 1; ?
|
|
3251
|
+
// Bind events
|
|
3252
|
+
if (options.keyboard) {
|
|
3253
|
+
if (options.trigger !== 'focus') {
|
|
3254
|
+
$tooltip.focus();
|
|
3255
|
+
tipElement.on('keyup', $tooltip.$onKeyUp);
|
|
3256
|
+
} else {
|
|
3257
|
+
element.on('keyup', $tooltip.$onFocusKeyUp);
|
|
3258
|
+
}
|
|
3259
|
+
}
|
|
3260
|
+
};
|
|
3261
|
+
$tooltip.leave = function () {
|
|
3262
|
+
clearTimeout(timeout);
|
|
3263
|
+
hoverState = 'out';
|
|
3264
|
+
if (!options.delay || !options.delay.hide) {
|
|
3265
|
+
return $tooltip.hide();
|
|
3266
|
+
}
|
|
3267
|
+
timeout = setTimeout(function () {
|
|
3268
|
+
if (hoverState === 'out') {
|
|
3269
|
+
$tooltip.hide();
|
|
3270
|
+
}
|
|
3271
|
+
}, options.delay.hide);
|
|
3272
|
+
};
|
|
3273
|
+
$tooltip.hide = function (blur) {
|
|
3274
|
+
if (!$tooltip.$isShown)
|
|
3275
|
+
return;
|
|
3276
|
+
scope.$emit(options.prefixEvent + '.hide.before', $tooltip);
|
|
3277
|
+
$animate.leave(tipElement, function () {
|
|
3278
|
+
scope.$emit(options.prefixEvent + '.hide', $tooltip);
|
|
3279
|
+
});
|
|
3280
|
+
$tooltip.$isShown = scope.$isShown = false;
|
|
3281
|
+
scope.$$phase || scope.$root.$$phase || scope.$digest();
|
|
3282
|
+
// Unbind events
|
|
3283
|
+
if (options.keyboard && tipElement !== null) {
|
|
3284
|
+
tipElement.off('keyup', $tooltip.$onKeyUp);
|
|
3285
|
+
}
|
|
3286
|
+
// Allow to blur the input when hidden, like when pressing enter key
|
|
3287
|
+
if (blur && options.trigger === 'focus') {
|
|
3288
|
+
return element[0].blur();
|
|
3289
|
+
}
|
|
3290
|
+
};
|
|
3291
|
+
$tooltip.toggle = function () {
|
|
3292
|
+
$tooltip.$isShown ? $tooltip.leave() : $tooltip.enter();
|
|
3293
|
+
};
|
|
3294
|
+
$tooltip.focus = function () {
|
|
3295
|
+
tipElement[0].focus();
|
|
3296
|
+
};
|
|
3297
|
+
// Protected methods
|
|
3298
|
+
$tooltip.$applyPlacement = function () {
|
|
3299
|
+
if (!tipElement)
|
|
3300
|
+
return;
|
|
3301
|
+
// Get the position of the tooltip element.
|
|
3302
|
+
var elementPosition = getPosition();
|
|
3303
|
+
// Get the height and width of the tooltip so we can center it.
|
|
3304
|
+
var tipWidth = tipElement.prop('offsetWidth'), tipHeight = tipElement.prop('offsetHeight');
|
|
3305
|
+
// Get the tooltip's top and left coordinates to center it with this directive.
|
|
3306
|
+
var tipPosition = getCalculatedOffset(options.placement, elementPosition, tipWidth, tipHeight);
|
|
3307
|
+
// Now set the calculated positioning.
|
|
3308
|
+
tipPosition.top += 'px';
|
|
3309
|
+
tipPosition.left += 'px';
|
|
3310
|
+
tipElement.css(tipPosition);
|
|
3311
|
+
};
|
|
3312
|
+
$tooltip.$onKeyUp = function (evt) {
|
|
3313
|
+
evt.which === 27 && $tooltip.hide();
|
|
3314
|
+
};
|
|
3315
|
+
$tooltip.$onFocusKeyUp = function (evt) {
|
|
3316
|
+
evt.which === 27 && element[0].blur();
|
|
3317
|
+
};
|
|
3318
|
+
$tooltip.$onFocusElementMouseDown = function (evt) {
|
|
3319
|
+
evt.preventDefault();
|
|
3320
|
+
evt.stopPropagation();
|
|
3321
|
+
// Some browsers do not auto-focus buttons (eg. Safari)
|
|
3322
|
+
$tooltip.$isShown ? element[0].blur() : element[0].focus();
|
|
3323
|
+
};
|
|
3324
|
+
// Private methods
|
|
3325
|
+
function getPosition() {
|
|
3326
|
+
if (options.container === 'body') {
|
|
3327
|
+
return dimensions.offset(element[0]);
|
|
3328
|
+
} else {
|
|
3329
|
+
return dimensions.position(element[0]);
|
|
3330
|
+
}
|
|
3331
|
+
}
|
|
3332
|
+
function getCalculatedOffset(placement, position, actualWidth, actualHeight) {
|
|
3333
|
+
var offset;
|
|
3334
|
+
var split = placement.split('-');
|
|
3335
|
+
switch (split[0]) {
|
|
3336
|
+
case 'right':
|
|
3337
|
+
offset = {
|
|
3338
|
+
top: position.top + position.height / 2 - actualHeight / 2,
|
|
3339
|
+
left: position.left + position.width
|
|
3340
|
+
};
|
|
3341
|
+
break;
|
|
3342
|
+
case 'bottom':
|
|
3343
|
+
offset = {
|
|
3344
|
+
top: position.top + position.height,
|
|
3345
|
+
left: position.left + position.width / 2 - actualWidth / 2
|
|
3346
|
+
};
|
|
3347
|
+
break;
|
|
3348
|
+
case 'left':
|
|
3349
|
+
offset = {
|
|
3350
|
+
top: position.top + position.height / 2 - actualHeight / 2,
|
|
3351
|
+
left: position.left - actualWidth
|
|
3352
|
+
};
|
|
3353
|
+
break;
|
|
3354
|
+
default:
|
|
3355
|
+
offset = {
|
|
3356
|
+
top: position.top - actualHeight,
|
|
3357
|
+
left: position.left + position.width / 2 - actualWidth / 2
|
|
3358
|
+
};
|
|
3359
|
+
break;
|
|
3360
|
+
}
|
|
3361
|
+
if (!split[1]) {
|
|
3362
|
+
return offset;
|
|
3363
|
+
}
|
|
3364
|
+
// Add support for corners @todo css
|
|
3365
|
+
if (split[0] === 'top' || split[0] === 'bottom') {
|
|
3366
|
+
switch (split[1]) {
|
|
3367
|
+
case 'left':
|
|
3368
|
+
offset.left = position.left;
|
|
3369
|
+
break;
|
|
3370
|
+
case 'right':
|
|
3371
|
+
offset.left = position.left + position.width - actualWidth;
|
|
3372
|
+
}
|
|
3373
|
+
} else if (split[0] === 'left' || split[0] === 'right') {
|
|
3374
|
+
switch (split[1]) {
|
|
3375
|
+
case 'top':
|
|
3376
|
+
offset.top = position.top - actualHeight;
|
|
3377
|
+
break;
|
|
3378
|
+
case 'bottom':
|
|
3379
|
+
offset.top = position.top + position.height;
|
|
3380
|
+
}
|
|
3381
|
+
}
|
|
3382
|
+
return offset;
|
|
3383
|
+
}
|
|
3384
|
+
return $tooltip;
|
|
3385
|
+
}
|
|
3386
|
+
// Helper functions
|
|
3387
|
+
function findElement(query, element) {
|
|
3388
|
+
return angular.element((element || document).querySelectorAll(query));
|
|
3389
|
+
}
|
|
3390
|
+
function fetchTemplate(template) {
|
|
3391
|
+
return $q.when($templateCache.get(template) || $http.get(template)).then(function (res) {
|
|
3392
|
+
if (angular.isObject(res)) {
|
|
3393
|
+
$templateCache.put(template, res.data);
|
|
3394
|
+
return res.data;
|
|
3395
|
+
}
|
|
3396
|
+
return res;
|
|
3397
|
+
});
|
|
3398
|
+
}
|
|
3399
|
+
return TooltipFactory;
|
|
3400
|
+
}
|
|
3401
|
+
];
|
|
3402
|
+
}).directive('bsTooltip', [
|
|
3403
|
+
'$window',
|
|
3404
|
+
'$location',
|
|
3405
|
+
'$sce',
|
|
3406
|
+
'$tooltip',
|
|
3407
|
+
'$$rAF',
|
|
3408
|
+
function ($window, $location, $sce, $tooltip, $$rAF) {
|
|
3409
|
+
return {
|
|
3410
|
+
restrict: 'EAC',
|
|
3411
|
+
scope: true,
|
|
3412
|
+
link: function postLink(scope, element, attr, transclusion) {
|
|
3413
|
+
// Directive options
|
|
3414
|
+
var options = { scope: scope };
|
|
3415
|
+
angular.forEach([
|
|
3416
|
+
'template',
|
|
3417
|
+
'contentTemplate',
|
|
3418
|
+
'placement',
|
|
3419
|
+
'container',
|
|
3420
|
+
'delay',
|
|
3421
|
+
'trigger',
|
|
3422
|
+
'keyboard',
|
|
3423
|
+
'html',
|
|
3424
|
+
'animation',
|
|
3425
|
+
'type'
|
|
3426
|
+
], function (key) {
|
|
3427
|
+
if (angular.isDefined(attr[key]))
|
|
3428
|
+
options[key] = attr[key];
|
|
3429
|
+
});
|
|
3430
|
+
// Observe scope attributes for change
|
|
3431
|
+
angular.forEach(['title'], function (key) {
|
|
3432
|
+
attr[key] && attr.$observe(key, function (newValue, oldValue) {
|
|
3433
|
+
scope[key] = $sce.trustAsHtml(newValue);
|
|
3434
|
+
angular.isDefined(oldValue) && $$rAF(function () {
|
|
3435
|
+
tooltip && tooltip.$applyPlacement();
|
|
3436
|
+
});
|
|
3437
|
+
});
|
|
3438
|
+
});
|
|
3439
|
+
// Support scope as an object
|
|
3440
|
+
attr.bsTooltip && scope.$watch(attr.bsTooltip, function (newValue, oldValue) {
|
|
3441
|
+
if (angular.isObject(newValue)) {
|
|
3442
|
+
angular.extend(scope, newValue);
|
|
3443
|
+
} else {
|
|
3444
|
+
scope.title = newValue;
|
|
3445
|
+
}
|
|
3446
|
+
angular.isDefined(oldValue) && $$rAF(function () {
|
|
3447
|
+
tooltip && tooltip.$applyPlacement();
|
|
3448
|
+
});
|
|
3449
|
+
}, true);
|
|
3450
|
+
// Initialize popover
|
|
3451
|
+
var tooltip = $tooltip(element, options);
|
|
3452
|
+
// Garbage collection
|
|
3453
|
+
scope.$on('$destroy', function () {
|
|
3454
|
+
tooltip.destroy();
|
|
3455
|
+
options = null;
|
|
3456
|
+
tooltip = null;
|
|
3457
|
+
});
|
|
3458
|
+
}
|
|
3459
|
+
};
|
|
3460
|
+
}
|
|
3461
|
+
]);
|
|
3462
|
+
|
|
3463
|
+
// Source: typeahead.js
|
|
3464
|
+
angular.module('mgcrea.ngStrap.typeahead', [
|
|
3465
|
+
'mgcrea.ngStrap.tooltip',
|
|
3466
|
+
'mgcrea.ngStrap.helpers.parseOptions'
|
|
3467
|
+
]).provider('$typeahead', function () {
|
|
3468
|
+
var defaults = this.defaults = {
|
|
3469
|
+
animation: 'am-fade',
|
|
3470
|
+
prefixClass: 'typeahead',
|
|
3471
|
+
placement: 'bottom-left',
|
|
3472
|
+
template: 'typeahead/typeahead.tpl.html',
|
|
3473
|
+
trigger: 'focus',
|
|
3474
|
+
container: false,
|
|
3475
|
+
keyboard: true,
|
|
3476
|
+
html: false,
|
|
3477
|
+
delay: 0,
|
|
3478
|
+
minLength: 1,
|
|
3479
|
+
filter: 'filter',
|
|
3480
|
+
limit: 6
|
|
3481
|
+
};
|
|
3482
|
+
this.$get = [
|
|
3483
|
+
'$window',
|
|
3484
|
+
'$rootScope',
|
|
3485
|
+
'$tooltip',
|
|
3486
|
+
function ($window, $rootScope, $tooltip) {
|
|
3487
|
+
var bodyEl = angular.element($window.document.body);
|
|
3488
|
+
function TypeaheadFactory(element, config) {
|
|
3489
|
+
var $typeahead = {};
|
|
3490
|
+
// Common vars
|
|
3491
|
+
var options = angular.extend({}, defaults, config);
|
|
3492
|
+
var controller = options.controller;
|
|
3493
|
+
$typeahead = $tooltip(element, options);
|
|
3494
|
+
var parentScope = config.scope;
|
|
3495
|
+
var scope = $typeahead.$scope;
|
|
3496
|
+
scope.$resetMatches = function () {
|
|
3497
|
+
scope.$matches = [];
|
|
3498
|
+
scope.$activeIndex = 0;
|
|
3499
|
+
};
|
|
3500
|
+
scope.$resetMatches();
|
|
3501
|
+
scope.$activate = function (index) {
|
|
3502
|
+
scope.$$postDigest(function () {
|
|
3503
|
+
$typeahead.activate(index);
|
|
3504
|
+
});
|
|
3505
|
+
};
|
|
3506
|
+
scope.$select = function (index, evt) {
|
|
3507
|
+
scope.$$postDigest(function () {
|
|
3508
|
+
$typeahead.select(index);
|
|
3509
|
+
});
|
|
3510
|
+
};
|
|
3511
|
+
scope.$isVisible = function () {
|
|
3512
|
+
return $typeahead.$isVisible();
|
|
3513
|
+
};
|
|
3514
|
+
// Public methods
|
|
3515
|
+
$typeahead.update = function (matches) {
|
|
3516
|
+
scope.$matches = matches;
|
|
3517
|
+
if (scope.$activeIndex >= matches.length) {
|
|
3518
|
+
scope.$activeIndex = 0;
|
|
3519
|
+
}
|
|
3520
|
+
};
|
|
3521
|
+
$typeahead.activate = function (index) {
|
|
3522
|
+
scope.$activeIndex = index;
|
|
3523
|
+
};
|
|
3524
|
+
$typeahead.select = function (index) {
|
|
3525
|
+
var value = scope.$matches[index].value;
|
|
3526
|
+
if (controller) {
|
|
3527
|
+
controller.$setViewValue(value);
|
|
3528
|
+
controller.$render();
|
|
3529
|
+
if (parentScope)
|
|
3530
|
+
parentScope.$digest();
|
|
3531
|
+
}
|
|
3532
|
+
scope.$resetMatches();
|
|
3533
|
+
// Emit event
|
|
3534
|
+
scope.$emit('$typeahead.select', value, index);
|
|
3535
|
+
};
|
|
3536
|
+
// Protected methods
|
|
3537
|
+
$typeahead.$isVisible = function () {
|
|
3538
|
+
if (!options.minLength || !controller) {
|
|
3539
|
+
return !!scope.$matches.length;
|
|
3540
|
+
}
|
|
3541
|
+
// minLength support
|
|
3542
|
+
return scope.$matches.length && angular.isString(controller.$viewValue) && controller.$viewValue.length >= options.minLength;
|
|
3543
|
+
};
|
|
3544
|
+
$typeahead.$getIndex = function (value) {
|
|
3545
|
+
var l = scope.$matches.length, i = l;
|
|
3546
|
+
if (!l)
|
|
3547
|
+
return;
|
|
3548
|
+
for (i = l; i--;) {
|
|
3549
|
+
if (scope.$matches[i].value === value)
|
|
3550
|
+
break;
|
|
3551
|
+
}
|
|
3552
|
+
if (i < 0)
|
|
3553
|
+
return;
|
|
3554
|
+
return i;
|
|
3555
|
+
};
|
|
3556
|
+
$typeahead.$onMouseDown = function (evt) {
|
|
3557
|
+
// Prevent blur on mousedown
|
|
3558
|
+
evt.preventDefault();
|
|
3559
|
+
evt.stopPropagation();
|
|
3560
|
+
};
|
|
3561
|
+
$typeahead.$onKeyDown = function (evt) {
|
|
3562
|
+
if (!/(38|40|13)/.test(evt.keyCode))
|
|
3563
|
+
return;
|
|
3564
|
+
evt.preventDefault();
|
|
3565
|
+
evt.stopPropagation();
|
|
3566
|
+
// Select with enter
|
|
3567
|
+
if (evt.keyCode === 13 && scope.$matches.length) {
|
|
3568
|
+
return $typeahead.select(scope.$activeIndex);
|
|
3569
|
+
}
|
|
3570
|
+
// Navigate with keyboard
|
|
3571
|
+
if (evt.keyCode === 38 && scope.$activeIndex > 0)
|
|
3572
|
+
scope.$activeIndex--;
|
|
3573
|
+
else if (evt.keyCode === 40 && scope.$activeIndex < scope.$matches.length - 1)
|
|
3574
|
+
scope.$activeIndex++;
|
|
3575
|
+
else if (angular.isUndefined(scope.$activeIndex))
|
|
3576
|
+
scope.$activeIndex = 0;
|
|
3577
|
+
scope.$digest();
|
|
3578
|
+
};
|
|
3579
|
+
// Overrides
|
|
3580
|
+
var show = $typeahead.show;
|
|
3581
|
+
$typeahead.show = function () {
|
|
3582
|
+
show();
|
|
3583
|
+
setTimeout(function () {
|
|
3584
|
+
$typeahead.$element.on('mousedown', $typeahead.$onMouseDown);
|
|
3585
|
+
if (options.keyboard) {
|
|
3586
|
+
element.on('keydown', $typeahead.$onKeyDown);
|
|
3587
|
+
}
|
|
3588
|
+
});
|
|
3589
|
+
};
|
|
3590
|
+
var hide = $typeahead.hide;
|
|
3591
|
+
$typeahead.hide = function () {
|
|
3592
|
+
$typeahead.$element.off('mousedown', $typeahead.$onMouseDown);
|
|
3593
|
+
if (options.keyboard) {
|
|
3594
|
+
element.off('keydown', $typeahead.$onKeyDown);
|
|
3595
|
+
}
|
|
3596
|
+
hide();
|
|
3597
|
+
};
|
|
3598
|
+
return $typeahead;
|
|
3599
|
+
}
|
|
3600
|
+
TypeaheadFactory.defaults = defaults;
|
|
3601
|
+
return TypeaheadFactory;
|
|
3602
|
+
}
|
|
3603
|
+
];
|
|
3604
|
+
}).directive('bsTypeahead', [
|
|
3605
|
+
'$window',
|
|
3606
|
+
'$parse',
|
|
3607
|
+
'$q',
|
|
3608
|
+
'$typeahead',
|
|
3609
|
+
'$parseOptions',
|
|
3610
|
+
function ($window, $parse, $q, $typeahead, $parseOptions) {
|
|
3611
|
+
var defaults = $typeahead.defaults;
|
|
3612
|
+
return {
|
|
3613
|
+
restrict: 'EAC',
|
|
3614
|
+
require: 'ngModel',
|
|
3615
|
+
link: function postLink(scope, element, attr, controller) {
|
|
3616
|
+
// Directive options
|
|
3617
|
+
var options = {
|
|
3618
|
+
scope: scope,
|
|
3619
|
+
controller: controller
|
|
3620
|
+
};
|
|
3621
|
+
angular.forEach([
|
|
3622
|
+
'placement',
|
|
3623
|
+
'container',
|
|
3624
|
+
'delay',
|
|
3625
|
+
'trigger',
|
|
3626
|
+
'keyboard',
|
|
3627
|
+
'html',
|
|
3628
|
+
'animation',
|
|
3629
|
+
'template',
|
|
3630
|
+
'filter',
|
|
3631
|
+
'limit',
|
|
3632
|
+
'minLength'
|
|
3633
|
+
], function (key) {
|
|
3634
|
+
if (angular.isDefined(attr[key]))
|
|
3635
|
+
options[key] = attr[key];
|
|
3636
|
+
});
|
|
3637
|
+
// Build proper ngOptions
|
|
3638
|
+
var filter = options.filter || defaults.filter;
|
|
3639
|
+
var limit = options.limit || defaults.limit;
|
|
3640
|
+
var ngOptions = attr.ngOptions;
|
|
3641
|
+
if (filter)
|
|
3642
|
+
ngOptions += ' | ' + filter + ':$viewValue';
|
|
3643
|
+
if (limit)
|
|
3644
|
+
ngOptions += ' | limitTo:' + limit;
|
|
3645
|
+
var parsedOptions = $parseOptions(ngOptions);
|
|
3646
|
+
// Initialize typeahead
|
|
3647
|
+
var typeahead = $typeahead(element, options);
|
|
3648
|
+
// if(!dump) var dump = console.error.bind(console);
|
|
3649
|
+
// Watch model for changes
|
|
3650
|
+
scope.$watch(attr.ngModel, function (newValue, oldValue) {
|
|
3651
|
+
scope.$modelValue = newValue;
|
|
3652
|
+
//Set model value on the scope to custom templates can use it.
|
|
3653
|
+
parsedOptions.valuesFn(scope, controller).then(function (values) {
|
|
3654
|
+
if (values.length > limit)
|
|
3655
|
+
values = values.slice(0, limit);
|
|
3656
|
+
// if(matches.length === 1 && matches[0].value === newValue) return;
|
|
3657
|
+
typeahead.update(values);
|
|
3658
|
+
// Queue a new rendering that will leverage collection loading
|
|
3659
|
+
controller.$render();
|
|
3660
|
+
});
|
|
3661
|
+
});
|
|
3662
|
+
// Model rendering in view
|
|
3663
|
+
controller.$render = function () {
|
|
3664
|
+
// console.warn('$render', element.attr('ng-model'), 'controller.$modelValue', typeof controller.$modelValue, controller.$modelValue, 'controller.$viewValue', typeof controller.$viewValue, controller.$viewValue);
|
|
3665
|
+
if (controller.$isEmpty(controller.$viewValue))
|
|
3666
|
+
return element.val('');
|
|
3667
|
+
var index = typeahead.$getIndex(controller.$modelValue);
|
|
3668
|
+
var selected = angular.isDefined(index) ? typeahead.$scope.$matches[index].label : controller.$viewValue;
|
|
3669
|
+
element.val(selected.replace(/<(?:.|\n)*?>/gm, '').trim());
|
|
3670
|
+
};
|
|
3671
|
+
// Garbage collection
|
|
3672
|
+
scope.$on('$destroy', function () {
|
|
3673
|
+
typeahead.destroy();
|
|
3674
|
+
options = null;
|
|
3675
|
+
typeahead = null;
|
|
3676
|
+
});
|
|
3677
|
+
}
|
|
3678
|
+
};
|
|
3679
|
+
}
|
|
3680
|
+
]);
|
|
3681
|
+
|
|
3682
|
+
})(window, document);
|