rails-angularstrap 2.2.0
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/README.md +89 -0
- data/Rakefile +2 -0
- data/lib/rails/angularstrap.rb +8 -0
- data/lib/rails/angularstrap/version.rb +5 -0
- data/vendor/assets/javascripts/angular-strap/LICENSE.md +21 -0
- data/vendor/assets/javascripts/angular-strap/README.md +112 -0
- data/vendor/assets/javascripts/angular-strap/angular-strap.nuspec +23 -0
- data/vendor/assets/javascripts/angular-strap/bower.json +53 -0
- data/vendor/assets/javascripts/angular-strap/dist/angular-strap.js +5014 -0
- data/vendor/assets/javascripts/angular-strap/dist/angular-strap.min.js +11 -0
- data/vendor/assets/javascripts/angular-strap/dist/angular-strap.min.js.map +1 -0
- data/vendor/assets/javascripts/angular-strap/dist/angular-strap.tpl.js +89 -0
- data/vendor/assets/javascripts/angular-strap/dist/angular-strap.tpl.min.js +8 -0
- data/vendor/assets/javascripts/angular-strap/dist/modules/affix.js +249 -0
- data/vendor/assets/javascripts/angular-strap/dist/modules/affix.min.js +9 -0
- data/vendor/assets/javascripts/angular-strap/dist/modules/affix.min.js.map +1 -0
- data/vendor/assets/javascripts/angular-strap/dist/modules/alert.js +120 -0
- data/vendor/assets/javascripts/angular-strap/dist/modules/alert.min.js +9 -0
- data/vendor/assets/javascripts/angular-strap/dist/modules/alert.min.js.map +1 -0
- data/vendor/assets/javascripts/angular-strap/dist/modules/alert.tpl.js +14 -0
- data/vendor/assets/javascripts/angular-strap/dist/modules/alert.tpl.min.js +8 -0
- data/vendor/assets/javascripts/angular-strap/dist/modules/aside.js +96 -0
- data/vendor/assets/javascripts/angular-strap/dist/modules/aside.min.js +9 -0
- data/vendor/assets/javascripts/angular-strap/dist/modules/aside.min.js.map +1 -0
- data/vendor/assets/javascripts/angular-strap/dist/modules/aside.tpl.js +14 -0
- data/vendor/assets/javascripts/angular-strap/dist/modules/aside.tpl.min.js +8 -0
- data/vendor/assets/javascripts/angular-strap/dist/modules/button.js +177 -0
- data/vendor/assets/javascripts/angular-strap/dist/modules/button.min.js +9 -0
- data/vendor/assets/javascripts/angular-strap/dist/modules/button.min.js.map +1 -0
- data/vendor/assets/javascripts/angular-strap/dist/modules/collapse.js +273 -0
- data/vendor/assets/javascripts/angular-strap/dist/modules/collapse.min.js +9 -0
- data/vendor/assets/javascripts/angular-strap/dist/modules/collapse.min.js.map +1 -0
- data/vendor/assets/javascripts/angular-strap/dist/modules/date-formatter.js +61 -0
- data/vendor/assets/javascripts/angular-strap/dist/modules/date-formatter.min.js +9 -0
- data/vendor/assets/javascripts/angular-strap/dist/modules/date-formatter.min.js.map +1 -0
- data/vendor/assets/javascripts/angular-strap/dist/modules/date-parser.js +273 -0
- data/vendor/assets/javascripts/angular-strap/dist/modules/date-parser.min.js +9 -0
- data/vendor/assets/javascripts/angular-strap/dist/modules/date-parser.min.js.map +1 -0
- data/vendor/assets/javascripts/angular-strap/dist/modules/datepicker.js +640 -0
- data/vendor/assets/javascripts/angular-strap/dist/modules/datepicker.min.js +9 -0
- data/vendor/assets/javascripts/angular-strap/dist/modules/datepicker.min.js.map +1 -0
- data/vendor/assets/javascripts/angular-strap/dist/modules/datepicker.tpl.js +14 -0
- data/vendor/assets/javascripts/angular-strap/dist/modules/datepicker.tpl.min.js +8 -0
- data/vendor/assets/javascripts/angular-strap/dist/modules/debounce.js +62 -0
- data/vendor/assets/javascripts/angular-strap/dist/modules/debounce.min.js +9 -0
- data/vendor/assets/javascripts/angular-strap/dist/modules/debounce.min.js.map +1 -0
- data/vendor/assets/javascripts/angular-strap/dist/modules/dimensions.js +156 -0
- data/vendor/assets/javascripts/angular-strap/dist/modules/dimensions.min.js +9 -0
- data/vendor/assets/javascripts/angular-strap/dist/modules/dimensions.min.js.map +1 -0
- data/vendor/assets/javascripts/angular-strap/dist/modules/dropdown.js +149 -0
- data/vendor/assets/javascripts/angular-strap/dist/modules/dropdown.min.js +9 -0
- data/vendor/assets/javascripts/angular-strap/dist/modules/dropdown.min.js.map +1 -0
- data/vendor/assets/javascripts/angular-strap/dist/modules/dropdown.tpl.js +14 -0
- data/vendor/assets/javascripts/angular-strap/dist/modules/dropdown.tpl.min.js +8 -0
- data/vendor/assets/javascripts/angular-strap/dist/modules/modal.js +349 -0
- data/vendor/assets/javascripts/angular-strap/dist/modules/modal.min.js +9 -0
- data/vendor/assets/javascripts/angular-strap/dist/modules/modal.min.js.map +1 -0
- data/vendor/assets/javascripts/angular-strap/dist/modules/modal.tpl.js +14 -0
- data/vendor/assets/javascripts/angular-strap/dist/modules/modal.tpl.min.js +8 -0
- data/vendor/assets/javascripts/angular-strap/dist/modules/navbar.js +72 -0
- data/vendor/assets/javascripts/angular-strap/dist/modules/navbar.min.js +9 -0
- data/vendor/assets/javascripts/angular-strap/dist/modules/navbar.min.js.map +1 -0
- data/vendor/assets/javascripts/angular-strap/dist/modules/parse-options.js +76 -0
- data/vendor/assets/javascripts/angular-strap/dist/modules/parse-options.min.js +9 -0
- data/vendor/assets/javascripts/angular-strap/dist/modules/parse-options.min.js.map +1 -0
- data/vendor/assets/javascripts/angular-strap/dist/modules/popover.js +112 -0
- data/vendor/assets/javascripts/angular-strap/dist/modules/popover.min.js +9 -0
- data/vendor/assets/javascripts/angular-strap/dist/modules/popover.min.js.map +1 -0
- data/vendor/assets/javascripts/angular-strap/dist/modules/popover.tpl.js +14 -0
- data/vendor/assets/javascripts/angular-strap/dist/modules/popover.tpl.min.js +8 -0
- data/vendor/assets/javascripts/angular-strap/dist/modules/raf.js +61 -0
- data/vendor/assets/javascripts/angular-strap/dist/modules/raf.min.js +9 -0
- data/vendor/assets/javascripts/angular-strap/dist/modules/raf.min.js.map +1 -0
- data/vendor/assets/javascripts/angular-strap/dist/modules/scrollspy.js +261 -0
- data/vendor/assets/javascripts/angular-strap/dist/modules/scrollspy.min.js +9 -0
- data/vendor/assets/javascripts/angular-strap/dist/modules/scrollspy.min.js.map +1 -0
- data/vendor/assets/javascripts/angular-strap/dist/modules/select.js +325 -0
- data/vendor/assets/javascripts/angular-strap/dist/modules/select.min.js +9 -0
- data/vendor/assets/javascripts/angular-strap/dist/modules/select.min.js.map +1 -0
- data/vendor/assets/javascripts/angular-strap/dist/modules/select.tpl.js +14 -0
- data/vendor/assets/javascripts/angular-strap/dist/modules/select.tpl.min.js +8 -0
- data/vendor/assets/javascripts/angular-strap/dist/modules/tab.js +186 -0
- data/vendor/assets/javascripts/angular-strap/dist/modules/tab.min.js +9 -0
- data/vendor/assets/javascripts/angular-strap/dist/modules/tab.min.js.map +1 -0
- data/vendor/assets/javascripts/angular-strap/dist/modules/tab.tpl.js +14 -0
- data/vendor/assets/javascripts/angular-strap/dist/modules/tab.tpl.min.js +8 -0
- data/vendor/assets/javascripts/angular-strap/dist/modules/timepicker.js +485 -0
- data/vendor/assets/javascripts/angular-strap/dist/modules/timepicker.min.js +9 -0
- data/vendor/assets/javascripts/angular-strap/dist/modules/timepicker.min.js.map +1 -0
- data/vendor/assets/javascripts/angular-strap/dist/modules/timepicker.tpl.js +14 -0
- data/vendor/assets/javascripts/angular-strap/dist/modules/timepicker.tpl.min.js +8 -0
- data/vendor/assets/javascripts/angular-strap/dist/modules/tooltip.js +690 -0
- data/vendor/assets/javascripts/angular-strap/dist/modules/tooltip.min.js +9 -0
- data/vendor/assets/javascripts/angular-strap/dist/modules/tooltip.min.js.map +1 -0
- data/vendor/assets/javascripts/angular-strap/dist/modules/tooltip.tpl.js +14 -0
- data/vendor/assets/javascripts/angular-strap/dist/modules/tooltip.tpl.min.js +8 -0
- data/vendor/assets/javascripts/angular-strap/dist/modules/typeahead.js +266 -0
- data/vendor/assets/javascripts/angular-strap/dist/modules/typeahead.min.js +9 -0
- data/vendor/assets/javascripts/angular-strap/dist/modules/typeahead.min.js.map +1 -0
- data/vendor/assets/javascripts/angular-strap/dist/modules/typeahead.tpl.js +14 -0
- data/vendor/assets/javascripts/angular-strap/dist/modules/typeahead.tpl.min.js +8 -0
- data/vendor/assets/javascripts/angular-strap/gulpfile.js +489 -0
- data/vendor/assets/javascripts/angular-strap/package.json +73 -0
- data/vendor/assets/javascripts/angular-strap/src/affix/affix.js +258 -0
- data/vendor/assets/javascripts/angular-strap/src/alert/alert.js +113 -0
- data/vendor/assets/javascripts/angular-strap/src/alert/alert.tpl.html +4 -0
- data/vendor/assets/javascripts/angular-strap/src/aside/aside.js +89 -0
- data/vendor/assets/javascripts/angular-strap/src/aside/aside.tpl.html +14 -0
- data/vendor/assets/javascripts/angular-strap/src/button/button.js +174 -0
- data/vendor/assets/javascripts/angular-strap/src/collapse/collapse.js +266 -0
- data/vendor/assets/javascripts/angular-strap/src/datepicker/datepicker.js +633 -0
- data/vendor/assets/javascripts/angular-strap/src/datepicker/datepicker.tpl.html +33 -0
- data/vendor/assets/javascripts/angular-strap/src/dropdown/dropdown.js +143 -0
- data/vendor/assets/javascripts/angular-strap/src/dropdown/dropdown.tpl.html +6 -0
- data/vendor/assets/javascripts/angular-strap/src/helpers/date-formatter.js +54 -0
- data/vendor/assets/javascripts/angular-strap/src/helpers/date-parser.js +266 -0
- data/vendor/assets/javascripts/angular-strap/src/helpers/debounce.js +55 -0
- data/vendor/assets/javascripts/angular-strap/src/helpers/dimensions.js +212 -0
- data/vendor/assets/javascripts/angular-strap/src/helpers/parse-options.js +69 -0
- data/vendor/assets/javascripts/angular-strap/src/helpers/raf.js +54 -0
- data/vendor/assets/javascripts/angular-strap/src/modal/modal.js +348 -0
- data/vendor/assets/javascripts/angular-strap/src/modal/modal.tpl.html +14 -0
- data/vendor/assets/javascripts/angular-strap/src/module.js +19 -0
- data/vendor/assets/javascripts/angular-strap/src/navbar/navbar.js +65 -0
- data/vendor/assets/javascripts/angular-strap/src/popover/popover.js +111 -0
- data/vendor/assets/javascripts/angular-strap/src/popover/popover.tpl.html +5 -0
- data/vendor/assets/javascripts/angular-strap/src/scrollspy/scrollspy.js +254 -0
- data/vendor/assets/javascripts/angular-strap/src/select/select.js +321 -0
- data/vendor/assets/javascripts/angular-strap/src/select/select.tpl.html +14 -0
- data/vendor/assets/javascripts/angular-strap/src/tab/tab.js +183 -0
- data/vendor/assets/javascripts/angular-strap/src/tab/tab.tpl.html +7 -0
- data/vendor/assets/javascripts/angular-strap/src/timepicker/timepicker.js +493 -0
- data/vendor/assets/javascripts/angular-strap/src/timepicker/timepicker.tpl.html +62 -0
- data/vendor/assets/javascripts/angular-strap/src/tooltip/tooltip.js +806 -0
- data/vendor/assets/javascripts/angular-strap/src/tooltip/tooltip.tpl.html +4 -0
- data/vendor/assets/javascripts/angular-strap/src/typeahead/typeahead.js +262 -0
- data/vendor/assets/javascripts/angular-strap/src/typeahead/typeahead.tpl.html +5 -0
- data/vendor/assets/javascripts/angular/README.md +64 -0
- data/vendor/assets/javascripts/angular/angular-csp.css +13 -0
- data/vendor/assets/javascripts/angular/angular.js +26181 -0
- data/vendor/assets/javascripts/angular/angular.min.js +250 -0
- data/vendor/assets/javascripts/angular/angular.min.js.gzip +0 -0
- data/vendor/assets/javascripts/angular/angular.min.js.map +8 -0
- data/vendor/assets/javascripts/angular/bower.json +8 -0
- data/vendor/assets/javascripts/angular/index.js +2 -0
- data/vendor/assets/javascripts/angular/package.json +25 -0
- metadata +237 -0
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
<div class="dropdown-menu timepicker" style="min-width: 0px;width: auto;">
|
|
2
|
+
<table height="100%">
|
|
3
|
+
<thead>
|
|
4
|
+
<tr class="text-center">
|
|
5
|
+
<th>
|
|
6
|
+
<button tabindex="-1" type="button" class="btn btn-default pull-left" ng-click="$arrowAction(-1, 0)">
|
|
7
|
+
<i class="{{ $iconUp }}"></i>
|
|
8
|
+
</button>
|
|
9
|
+
</th>
|
|
10
|
+
<th>
|
|
11
|
+
|
|
12
|
+
</th>
|
|
13
|
+
<th>
|
|
14
|
+
<button tabindex="-1" type="button" class="btn btn-default pull-left" ng-click="$arrowAction(-1, 1)">
|
|
15
|
+
<i class="{{ $iconUp }}"></i>
|
|
16
|
+
</button>
|
|
17
|
+
</th>
|
|
18
|
+
</tr>
|
|
19
|
+
</thead>
|
|
20
|
+
<tbody>
|
|
21
|
+
<tr ng-repeat="(i, row) in rows">
|
|
22
|
+
<td class="text-center">
|
|
23
|
+
<button tabindex="-1" style="width: 100%" type="button" class="btn btn-default" ng-class="{'btn-primary': row[0].selected}" ng-click="$select(row[0].date, 0)" ng-disabled="row[0].disabled">
|
|
24
|
+
<span ng-class="{'text-muted': row[0].muted}" ng-bind="row[0].label"></span>
|
|
25
|
+
</button>
|
|
26
|
+
</td>
|
|
27
|
+
<td>
|
|
28
|
+
<span ng-bind="i == midIndex ? timeSeparator : ' '"></span>
|
|
29
|
+
</td>
|
|
30
|
+
<td class="text-center">
|
|
31
|
+
<button tabindex="-1" ng-if="row[1].date" style="width: 100%" type="button" class="btn btn-default" ng-class="{'btn-primary': row[1].selected}" ng-click="$select(row[1].date, 1)" ng-disabled="row[1].disabled">
|
|
32
|
+
<span ng-class="{'text-muted': row[1].muted}" ng-bind="row[1].label"></span>
|
|
33
|
+
</button>
|
|
34
|
+
</td>
|
|
35
|
+
<td ng-if="showAM">
|
|
36
|
+
|
|
37
|
+
</td>
|
|
38
|
+
<td ng-if="showAM">
|
|
39
|
+
<button tabindex="-1" ng-show="i == midIndex - !isAM * 1" style="width: 100%" type="button" ng-class="{'btn-primary': !!isAM}" class="btn btn-default" ng-click="$switchMeridian()" ng-disabled="el.disabled">AM</button>
|
|
40
|
+
<button tabindex="-1" ng-show="i == midIndex + 1 - !isAM * 1" style="width: 100%" type="button" ng-class="{'btn-primary': !isAM}" class="btn btn-default" ng-click="$switchMeridian()" ng-disabled="el.disabled">PM</button>
|
|
41
|
+
</td>
|
|
42
|
+
</tr>
|
|
43
|
+
</tbody>
|
|
44
|
+
<tfoot>
|
|
45
|
+
<tr class="text-center">
|
|
46
|
+
<th>
|
|
47
|
+
<button tabindex="-1" type="button" class="btn btn-default pull-left" ng-click="$arrowAction(1, 0)">
|
|
48
|
+
<i class="{{ $iconDown }}"></i>
|
|
49
|
+
</button>
|
|
50
|
+
</th>
|
|
51
|
+
<th>
|
|
52
|
+
|
|
53
|
+
</th>
|
|
54
|
+
<th>
|
|
55
|
+
<button tabindex="-1" type="button" class="btn btn-default pull-left" ng-click="$arrowAction(1, 1)">
|
|
56
|
+
<i class="{{ $iconDown }}"></i>
|
|
57
|
+
</button>
|
|
58
|
+
</th>
|
|
59
|
+
</tr>
|
|
60
|
+
</tfoot>
|
|
61
|
+
</table>
|
|
62
|
+
</div>
|
|
@@ -0,0 +1,806 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
angular.module('mgcrea.ngStrap.tooltip', ['mgcrea.ngStrap.helpers.dimensions'])
|
|
4
|
+
|
|
5
|
+
.provider('$tooltip', function() {
|
|
6
|
+
|
|
7
|
+
var defaults = this.defaults = {
|
|
8
|
+
animation: 'am-fade',
|
|
9
|
+
customClass: '',
|
|
10
|
+
prefixClass: 'tooltip',
|
|
11
|
+
prefixEvent: 'tooltip',
|
|
12
|
+
container: false,
|
|
13
|
+
target: false,
|
|
14
|
+
placement: 'top',
|
|
15
|
+
template: 'tooltip/tooltip.tpl.html',
|
|
16
|
+
contentTemplate: false,
|
|
17
|
+
trigger: 'hover focus',
|
|
18
|
+
keyboard: false,
|
|
19
|
+
html: false,
|
|
20
|
+
show: false,
|
|
21
|
+
title: '',
|
|
22
|
+
type: '',
|
|
23
|
+
delay: 0,
|
|
24
|
+
autoClose: false,
|
|
25
|
+
bsEnabled: true,
|
|
26
|
+
viewport: {
|
|
27
|
+
selector: 'body',
|
|
28
|
+
padding: 0
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
this.$get = function($window, $rootScope, $compile, $q, $templateCache, $http, $animate, $sce, dimensions, $$rAF, $timeout) {
|
|
33
|
+
|
|
34
|
+
var trim = String.prototype.trim;
|
|
35
|
+
var isTouch = 'createTouch' in $window.document;
|
|
36
|
+
var htmlReplaceRegExp = /ng-bind="/ig;
|
|
37
|
+
var $body = angular.element($window.document);
|
|
38
|
+
|
|
39
|
+
function TooltipFactory(element, config) {
|
|
40
|
+
|
|
41
|
+
var $tooltip = {};
|
|
42
|
+
|
|
43
|
+
// Common vars
|
|
44
|
+
var nodeName = element[0].nodeName.toLowerCase();
|
|
45
|
+
var options = $tooltip.$options = angular.extend({}, defaults, config);
|
|
46
|
+
$tooltip.$promise = fetchTemplate(options.template);
|
|
47
|
+
var scope = $tooltip.$scope = options.scope && options.scope.$new() || $rootScope.$new();
|
|
48
|
+
if(options.delay && angular.isString(options.delay)) {
|
|
49
|
+
var split = options.delay.split(',').map(parseFloat);
|
|
50
|
+
options.delay = split.length > 1 ? {show: split[0], hide: split[1]} : split[0];
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// store $id to identify the triggering element in events
|
|
54
|
+
// give priority to options.id, otherwise, try to use
|
|
55
|
+
// element id if defined
|
|
56
|
+
$tooltip.$id = options.id || element.attr('id') || '';
|
|
57
|
+
|
|
58
|
+
// Support scope as string options
|
|
59
|
+
if(options.title) {
|
|
60
|
+
scope.title = $sce.trustAsHtml(options.title);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Provide scope helpers
|
|
64
|
+
scope.$setEnabled = function(isEnabled) {
|
|
65
|
+
scope.$$postDigest(function() {
|
|
66
|
+
$tooltip.setEnabled(isEnabled);
|
|
67
|
+
});
|
|
68
|
+
};
|
|
69
|
+
scope.$hide = function() {
|
|
70
|
+
scope.$$postDigest(function() {
|
|
71
|
+
$tooltip.hide();
|
|
72
|
+
});
|
|
73
|
+
};
|
|
74
|
+
scope.$show = function() {
|
|
75
|
+
scope.$$postDigest(function() {
|
|
76
|
+
$tooltip.show();
|
|
77
|
+
});
|
|
78
|
+
};
|
|
79
|
+
scope.$toggle = function() {
|
|
80
|
+
scope.$$postDigest(function() {
|
|
81
|
+
$tooltip.toggle();
|
|
82
|
+
});
|
|
83
|
+
};
|
|
84
|
+
// Publish isShown as a protected var on scope
|
|
85
|
+
$tooltip.$isShown = scope.$isShown = false;
|
|
86
|
+
|
|
87
|
+
// Private vars
|
|
88
|
+
var timeout, hoverState;
|
|
89
|
+
|
|
90
|
+
// Support contentTemplate option
|
|
91
|
+
if(options.contentTemplate) {
|
|
92
|
+
$tooltip.$promise = $tooltip.$promise.then(function(template) {
|
|
93
|
+
var templateEl = angular.element(template);
|
|
94
|
+
return fetchTemplate(options.contentTemplate)
|
|
95
|
+
.then(function(contentTemplate) {
|
|
96
|
+
var contentEl = findElement('[ng-bind="content"]', templateEl[0]);
|
|
97
|
+
if(!contentEl.length) contentEl = findElement('[ng-bind="title"]', templateEl[0]);
|
|
98
|
+
contentEl.removeAttr('ng-bind').html(contentTemplate);
|
|
99
|
+
return templateEl[0].outerHTML;
|
|
100
|
+
});
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// Fetch, compile then initialize tooltip
|
|
105
|
+
var tipLinker, tipElement, tipTemplate, tipContainer, tipScope;
|
|
106
|
+
$tooltip.$promise.then(function(template) {
|
|
107
|
+
if(angular.isObject(template)) template = template.data;
|
|
108
|
+
if(options.html) template = template.replace(htmlReplaceRegExp, 'ng-bind-html="');
|
|
109
|
+
template = trim.apply(template);
|
|
110
|
+
tipTemplate = template;
|
|
111
|
+
tipLinker = $compile(template);
|
|
112
|
+
$tooltip.init();
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
$tooltip.init = function() {
|
|
116
|
+
|
|
117
|
+
// Options: delay
|
|
118
|
+
if (options.delay && angular.isNumber(options.delay)) {
|
|
119
|
+
options.delay = {
|
|
120
|
+
show: options.delay,
|
|
121
|
+
hide: options.delay
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// Replace trigger on touch devices ?
|
|
126
|
+
// if(isTouch && options.trigger === defaults.trigger) {
|
|
127
|
+
// options.trigger.replace(/hover/g, 'click');
|
|
128
|
+
// }
|
|
129
|
+
|
|
130
|
+
// Options : container
|
|
131
|
+
if(options.container === 'self') {
|
|
132
|
+
tipContainer = element;
|
|
133
|
+
} else if(angular.isElement(options.container)) {
|
|
134
|
+
tipContainer = options.container;
|
|
135
|
+
} else if(options.container) {
|
|
136
|
+
tipContainer = findElement(options.container);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// Options: trigger
|
|
140
|
+
bindTriggerEvents();
|
|
141
|
+
|
|
142
|
+
// Options: target
|
|
143
|
+
if(options.target) {
|
|
144
|
+
options.target = angular.isElement(options.target) ? options.target : findElement(options.target);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// Options: show
|
|
148
|
+
if(options.show) {
|
|
149
|
+
scope.$$postDigest(function() {
|
|
150
|
+
options.trigger === 'focus' ? element[0].focus() : $tooltip.show();
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
};
|
|
155
|
+
|
|
156
|
+
$tooltip.destroy = function() {
|
|
157
|
+
|
|
158
|
+
// Unbind events
|
|
159
|
+
unbindTriggerEvents();
|
|
160
|
+
|
|
161
|
+
// Remove element
|
|
162
|
+
destroyTipElement();
|
|
163
|
+
|
|
164
|
+
// Destroy scope
|
|
165
|
+
scope.$destroy();
|
|
166
|
+
|
|
167
|
+
};
|
|
168
|
+
|
|
169
|
+
$tooltip.enter = function() {
|
|
170
|
+
|
|
171
|
+
clearTimeout(timeout);
|
|
172
|
+
hoverState = 'in';
|
|
173
|
+
if (!options.delay || !options.delay.show) {
|
|
174
|
+
return $tooltip.show();
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
timeout = setTimeout(function() {
|
|
178
|
+
if (hoverState ==='in') $tooltip.show();
|
|
179
|
+
}, options.delay.show);
|
|
180
|
+
|
|
181
|
+
};
|
|
182
|
+
|
|
183
|
+
$tooltip.show = function() {
|
|
184
|
+
if (!options.bsEnabled || $tooltip.$isShown) return;
|
|
185
|
+
|
|
186
|
+
scope.$emit(options.prefixEvent + '.show.before', $tooltip);
|
|
187
|
+
var parent, after;
|
|
188
|
+
if (options.container) {
|
|
189
|
+
parent = tipContainer;
|
|
190
|
+
if (tipContainer[0].lastChild) {
|
|
191
|
+
after = angular.element(tipContainer[0].lastChild);
|
|
192
|
+
} else {
|
|
193
|
+
after = null;
|
|
194
|
+
}
|
|
195
|
+
} else {
|
|
196
|
+
parent = null;
|
|
197
|
+
after = element;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
|
|
201
|
+
// Hide any existing tipElement
|
|
202
|
+
if(tipElement) destroyTipElement();
|
|
203
|
+
// Fetch a cloned element linked from template
|
|
204
|
+
tipScope = $tooltip.$scope.$new();
|
|
205
|
+
tipElement = $tooltip.$element = tipLinker(tipScope, function(clonedElement, scope) {});
|
|
206
|
+
|
|
207
|
+
// Set the initial positioning. Make the tooltip invisible
|
|
208
|
+
// so IE doesn't try to focus on it off screen.
|
|
209
|
+
tipElement.css({top: '-9999px', left: '-9999px', display: 'block', visibility: 'hidden'});
|
|
210
|
+
|
|
211
|
+
// Options: animation
|
|
212
|
+
if(options.animation) tipElement.addClass(options.animation);
|
|
213
|
+
// Options: type
|
|
214
|
+
if(options.type) tipElement.addClass(options.prefixClass + '-' + options.type);
|
|
215
|
+
// Options: custom classes
|
|
216
|
+
if(options.customClass) tipElement.addClass(options.customClass);
|
|
217
|
+
|
|
218
|
+
// Append the element, without any animations. If we append
|
|
219
|
+
// using $animate.enter, some of the animations cause the placement
|
|
220
|
+
// to be off due to the transforms.
|
|
221
|
+
after ? after.after(tipElement) : parent.prepend(tipElement);
|
|
222
|
+
|
|
223
|
+
$tooltip.$isShown = scope.$isShown = true;
|
|
224
|
+
safeDigest(scope);
|
|
225
|
+
|
|
226
|
+
// Now, apply placement
|
|
227
|
+
$tooltip.$applyPlacement();
|
|
228
|
+
|
|
229
|
+
// Once placed, animate it.
|
|
230
|
+
// Support v1.3+ $animate
|
|
231
|
+
// https://github.com/angular/angular.js/commit/bf0f5502b1bbfddc5cdd2f138efd9188b8c652a9
|
|
232
|
+
var promise = $animate.enter(tipElement, parent, after, enterAnimateCallback);
|
|
233
|
+
if(promise && promise.then) promise.then(enterAnimateCallback);
|
|
234
|
+
safeDigest(scope);
|
|
235
|
+
|
|
236
|
+
$$rAF(function () {
|
|
237
|
+
// Once the tooltip is placed and the animation starts, make the tooltip visible
|
|
238
|
+
if(tipElement) tipElement.css({visibility: 'visible'});
|
|
239
|
+
});
|
|
240
|
+
|
|
241
|
+
// Bind events
|
|
242
|
+
if(options.keyboard) {
|
|
243
|
+
if(options.trigger !== 'focus') {
|
|
244
|
+
$tooltip.focus();
|
|
245
|
+
}
|
|
246
|
+
bindKeyboardEvents();
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
if(options.autoClose) {
|
|
250
|
+
bindAutoCloseEvents();
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
};
|
|
254
|
+
|
|
255
|
+
function enterAnimateCallback() {
|
|
256
|
+
scope.$emit(options.prefixEvent + '.show', $tooltip);
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
$tooltip.leave = function() {
|
|
260
|
+
|
|
261
|
+
clearTimeout(timeout);
|
|
262
|
+
hoverState = 'out';
|
|
263
|
+
if (!options.delay || !options.delay.hide) {
|
|
264
|
+
return $tooltip.hide();
|
|
265
|
+
}
|
|
266
|
+
timeout = setTimeout(function () {
|
|
267
|
+
if (hoverState === 'out') {
|
|
268
|
+
$tooltip.hide();
|
|
269
|
+
}
|
|
270
|
+
}, options.delay.hide);
|
|
271
|
+
|
|
272
|
+
};
|
|
273
|
+
|
|
274
|
+
var _blur;
|
|
275
|
+
var _tipToHide;
|
|
276
|
+
$tooltip.hide = function(blur) {
|
|
277
|
+
|
|
278
|
+
if(!$tooltip.$isShown) return;
|
|
279
|
+
scope.$emit(options.prefixEvent + '.hide.before', $tooltip);
|
|
280
|
+
|
|
281
|
+
// store blur value for leaveAnimateCallback to use
|
|
282
|
+
_blur = blur;
|
|
283
|
+
|
|
284
|
+
// store current tipElement reference to use
|
|
285
|
+
// in leaveAnimateCallback
|
|
286
|
+
_tipToHide = tipElement;
|
|
287
|
+
|
|
288
|
+
// Support v1.3+ $animate
|
|
289
|
+
// https://github.com/angular/angular.js/commit/bf0f5502b1bbfddc5cdd2f138efd9188b8c652a9
|
|
290
|
+
var promise = $animate.leave(tipElement, leaveAnimateCallback);
|
|
291
|
+
if(promise && promise.then) promise.then(leaveAnimateCallback);
|
|
292
|
+
|
|
293
|
+
$tooltip.$isShown = scope.$isShown = false;
|
|
294
|
+
safeDigest(scope);
|
|
295
|
+
|
|
296
|
+
// Unbind events
|
|
297
|
+
if(options.keyboard && tipElement !== null) {
|
|
298
|
+
unbindKeyboardEvents();
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
if(options.autoClose && tipElement !== null) {
|
|
302
|
+
unbindAutoCloseEvents();
|
|
303
|
+
}
|
|
304
|
+
};
|
|
305
|
+
|
|
306
|
+
function leaveAnimateCallback() {
|
|
307
|
+
scope.$emit(options.prefixEvent + '.hide', $tooltip);
|
|
308
|
+
|
|
309
|
+
// check if current tipElement still references
|
|
310
|
+
// the same element when hide was called
|
|
311
|
+
if (tipElement === _tipToHide) {
|
|
312
|
+
// Allow to blur the input when hidden, like when pressing enter key
|
|
313
|
+
if(_blur && options.trigger === 'focus') {
|
|
314
|
+
return element[0].blur();
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
// clean up child scopes
|
|
318
|
+
destroyTipElement();
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
$tooltip.toggle = function() {
|
|
323
|
+
$tooltip.$isShown ? $tooltip.leave() : $tooltip.enter();
|
|
324
|
+
};
|
|
325
|
+
|
|
326
|
+
$tooltip.focus = function() {
|
|
327
|
+
tipElement[0].focus();
|
|
328
|
+
};
|
|
329
|
+
|
|
330
|
+
$tooltip.setEnabled = function(isEnabled) {
|
|
331
|
+
options.bsEnabled = isEnabled;
|
|
332
|
+
};
|
|
333
|
+
|
|
334
|
+
$tooltip.setViewport = function(viewport) {
|
|
335
|
+
options.viewport = viewport;
|
|
336
|
+
};
|
|
337
|
+
|
|
338
|
+
// Protected methods
|
|
339
|
+
|
|
340
|
+
$tooltip.$applyPlacement = function() {
|
|
341
|
+
if(!tipElement) return;
|
|
342
|
+
|
|
343
|
+
// Determine if we're doing an auto or normal placement
|
|
344
|
+
var placement = options.placement,
|
|
345
|
+
autoToken = /\s?auto?\s?/i,
|
|
346
|
+
autoPlace = autoToken.test(placement);
|
|
347
|
+
|
|
348
|
+
if (autoPlace) {
|
|
349
|
+
placement = placement.replace(autoToken, '') || defaults.placement;
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
// Need to add the position class before we get
|
|
353
|
+
// the offsets
|
|
354
|
+
tipElement.addClass(options.placement);
|
|
355
|
+
|
|
356
|
+
// Get the position of the target element
|
|
357
|
+
// and the height and width of the tooltip so we can center it.
|
|
358
|
+
var elementPosition = getPosition(),
|
|
359
|
+
tipWidth = tipElement.prop('offsetWidth'),
|
|
360
|
+
tipHeight = tipElement.prop('offsetHeight');
|
|
361
|
+
|
|
362
|
+
// If we're auto placing, we need to check the positioning
|
|
363
|
+
if (autoPlace) {
|
|
364
|
+
var originalPlacement = placement;
|
|
365
|
+
var container = options.container ? findElement(options.container) : element.parent();
|
|
366
|
+
var containerPosition = getPosition(container);
|
|
367
|
+
|
|
368
|
+
// Determine if the vertical placement
|
|
369
|
+
if (originalPlacement.indexOf('bottom') >= 0 && elementPosition.bottom + tipHeight > containerPosition.bottom) {
|
|
370
|
+
placement = originalPlacement.replace('bottom', 'top');
|
|
371
|
+
} else if (originalPlacement.indexOf('top') >= 0 && elementPosition.top - tipHeight < containerPosition.top) {
|
|
372
|
+
placement = originalPlacement.replace('top', 'bottom');
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
// Determine the horizontal placement
|
|
376
|
+
// The exotic placements of left and right are opposite of the standard placements. Their arrows are put on the left/right
|
|
377
|
+
// and flow in the opposite direction of their placement.
|
|
378
|
+
if ((originalPlacement === 'right' || originalPlacement === 'bottom-left' || originalPlacement === 'top-left') &&
|
|
379
|
+
elementPosition.right + tipWidth > containerPosition.width) {
|
|
380
|
+
|
|
381
|
+
placement = originalPlacement === 'right' ? 'left' : placement.replace('left', 'right');
|
|
382
|
+
} else if ((originalPlacement === 'left' || originalPlacement === 'bottom-right' || originalPlacement === 'top-right') &&
|
|
383
|
+
elementPosition.left - tipWidth < containerPosition.left) {
|
|
384
|
+
|
|
385
|
+
placement = originalPlacement === 'left' ? 'right' : placement.replace('right', 'left');
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
tipElement.removeClass(originalPlacement).addClass(placement);
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
// Get the tooltip's top and left coordinates to center it with this directive.
|
|
392
|
+
var tipPosition = getCalculatedOffset(placement, elementPosition, tipWidth, tipHeight);
|
|
393
|
+
applyPlacement(tipPosition, placement);
|
|
394
|
+
};
|
|
395
|
+
|
|
396
|
+
$tooltip.$onKeyUp = function(evt) {
|
|
397
|
+
if (evt.which === 27 && $tooltip.$isShown) {
|
|
398
|
+
$tooltip.hide();
|
|
399
|
+
evt.stopPropagation();
|
|
400
|
+
}
|
|
401
|
+
};
|
|
402
|
+
|
|
403
|
+
$tooltip.$onFocusKeyUp = function(evt) {
|
|
404
|
+
if (evt.which === 27) {
|
|
405
|
+
element[0].blur();
|
|
406
|
+
evt.stopPropagation();
|
|
407
|
+
}
|
|
408
|
+
};
|
|
409
|
+
|
|
410
|
+
$tooltip.$onFocusElementMouseDown = function(evt) {
|
|
411
|
+
evt.preventDefault();
|
|
412
|
+
evt.stopPropagation();
|
|
413
|
+
// Some browsers do not auto-focus buttons (eg. Safari)
|
|
414
|
+
$tooltip.$isShown ? element[0].blur() : element[0].focus();
|
|
415
|
+
};
|
|
416
|
+
|
|
417
|
+
// bind/unbind events
|
|
418
|
+
function bindTriggerEvents() {
|
|
419
|
+
var triggers = options.trigger.split(' ');
|
|
420
|
+
angular.forEach(triggers, function(trigger) {
|
|
421
|
+
if(trigger === 'click') {
|
|
422
|
+
element.on('click', $tooltip.toggle);
|
|
423
|
+
} else if(trigger !== 'manual') {
|
|
424
|
+
element.on(trigger === 'hover' ? 'mouseenter' : 'focus', $tooltip.enter);
|
|
425
|
+
element.on(trigger === 'hover' ? 'mouseleave' : 'blur', $tooltip.leave);
|
|
426
|
+
nodeName === 'button' && trigger !== 'hover' && element.on(isTouch ? 'touchstart' : 'mousedown', $tooltip.$onFocusElementMouseDown);
|
|
427
|
+
}
|
|
428
|
+
});
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
function unbindTriggerEvents() {
|
|
432
|
+
var triggers = options.trigger.split(' ');
|
|
433
|
+
for (var i = triggers.length; i--;) {
|
|
434
|
+
var trigger = triggers[i];
|
|
435
|
+
if(trigger === 'click') {
|
|
436
|
+
element.off('click', $tooltip.toggle);
|
|
437
|
+
} else if(trigger !== 'manual') {
|
|
438
|
+
element.off(trigger === 'hover' ? 'mouseenter' : 'focus', $tooltip.enter);
|
|
439
|
+
element.off(trigger === 'hover' ? 'mouseleave' : 'blur', $tooltip.leave);
|
|
440
|
+
nodeName === 'button' && trigger !== 'hover' && element.off(isTouch ? 'touchstart' : 'mousedown', $tooltip.$onFocusElementMouseDown);
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
function bindKeyboardEvents() {
|
|
446
|
+
if(options.trigger !== 'focus') {
|
|
447
|
+
tipElement.on('keyup', $tooltip.$onKeyUp);
|
|
448
|
+
} else {
|
|
449
|
+
element.on('keyup', $tooltip.$onFocusKeyUp);
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
function unbindKeyboardEvents() {
|
|
454
|
+
if(options.trigger !== 'focus') {
|
|
455
|
+
tipElement.off('keyup', $tooltip.$onKeyUp);
|
|
456
|
+
} else {
|
|
457
|
+
element.off('keyup', $tooltip.$onFocusKeyUp);
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
var _autoCloseEventsBinded = false;
|
|
462
|
+
function bindAutoCloseEvents() {
|
|
463
|
+
// use timeout to hookup the events to prevent
|
|
464
|
+
// event bubbling from being processed imediately.
|
|
465
|
+
$timeout(function() {
|
|
466
|
+
// Stop propagation when clicking inside tooltip
|
|
467
|
+
tipElement.on('click', stopEventPropagation);
|
|
468
|
+
|
|
469
|
+
// Hide when clicking outside tooltip
|
|
470
|
+
$body.on('click', $tooltip.hide);
|
|
471
|
+
|
|
472
|
+
_autoCloseEventsBinded = true;
|
|
473
|
+
}, 0, false);
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
function unbindAutoCloseEvents() {
|
|
477
|
+
if (_autoCloseEventsBinded) {
|
|
478
|
+
tipElement.off('click', stopEventPropagation);
|
|
479
|
+
$body.off('click', $tooltip.hide);
|
|
480
|
+
_autoCloseEventsBinded = false;
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
function stopEventPropagation(event) {
|
|
485
|
+
event.stopPropagation();
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
// Private methods
|
|
489
|
+
|
|
490
|
+
function getPosition($element) {
|
|
491
|
+
$element = $element || (options.target || element);
|
|
492
|
+
|
|
493
|
+
var el = $element[0],
|
|
494
|
+
isBody = el.tagName === 'BODY';
|
|
495
|
+
|
|
496
|
+
var elRect = el.getBoundingClientRect();
|
|
497
|
+
var rect = {};
|
|
498
|
+
|
|
499
|
+
// IE8 has issues with angular.extend and using elRect directly.
|
|
500
|
+
// By coping the values of elRect into a new object, we can continue to use extend
|
|
501
|
+
for (var p in elRect) {
|
|
502
|
+
if (elRect.hasOwnProperty(p)) {
|
|
503
|
+
rect[p] = elRect[p];
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
if (rect.width === null) {
|
|
508
|
+
// width and height are missing in IE8, so compute them manually; see https://github.com/twbs/bootstrap/issues/14093
|
|
509
|
+
rect = angular.extend({}, rect, { width: elRect.right - elRect.left, height: elRect.bottom - elRect.top });
|
|
510
|
+
}
|
|
511
|
+
var elOffset = isBody ? { top: 0, left: 0 } : dimensions.offset(el),
|
|
512
|
+
scroll = { scroll: isBody ? document.documentElement.scrollTop || document.body.scrollTop : $element.prop('scrollTop') || 0 },
|
|
513
|
+
outerDims = isBody ? { width: document.documentElement.clientWidth, height: $window.innerHeight } : null;
|
|
514
|
+
|
|
515
|
+
return angular.extend({}, rect, scroll, outerDims, elOffset);
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
function getCalculatedOffset(placement, position, actualWidth, actualHeight) {
|
|
519
|
+
var offset;
|
|
520
|
+
var split = placement.split('-');
|
|
521
|
+
|
|
522
|
+
switch (split[0]) {
|
|
523
|
+
case 'right':
|
|
524
|
+
offset = {
|
|
525
|
+
top: position.top + position.height / 2 - actualHeight / 2,
|
|
526
|
+
left: position.left + position.width
|
|
527
|
+
};
|
|
528
|
+
break;
|
|
529
|
+
case 'bottom':
|
|
530
|
+
offset = {
|
|
531
|
+
top: position.top + position.height,
|
|
532
|
+
left: position.left + position.width / 2 - actualWidth / 2
|
|
533
|
+
};
|
|
534
|
+
break;
|
|
535
|
+
case 'left':
|
|
536
|
+
offset = {
|
|
537
|
+
top: position.top + position.height / 2 - actualHeight / 2,
|
|
538
|
+
left: position.left - actualWidth
|
|
539
|
+
};
|
|
540
|
+
break;
|
|
541
|
+
default:
|
|
542
|
+
offset = {
|
|
543
|
+
top: position.top - actualHeight,
|
|
544
|
+
left: position.left + position.width / 2 - actualWidth / 2
|
|
545
|
+
};
|
|
546
|
+
break;
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
if(!split[1]) {
|
|
550
|
+
return offset;
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
// Add support for corners @todo css
|
|
554
|
+
if(split[0] === 'top' || split[0] === 'bottom') {
|
|
555
|
+
switch (split[1]) {
|
|
556
|
+
case 'left':
|
|
557
|
+
offset.left = position.left;
|
|
558
|
+
break;
|
|
559
|
+
case 'right':
|
|
560
|
+
offset.left = position.left + position.width - actualWidth;
|
|
561
|
+
}
|
|
562
|
+
} else if(split[0] === 'left' || split[0] === 'right') {
|
|
563
|
+
switch (split[1]) {
|
|
564
|
+
case 'top':
|
|
565
|
+
offset.top = position.top - actualHeight;
|
|
566
|
+
break;
|
|
567
|
+
case 'bottom':
|
|
568
|
+
offset.top = position.top + position.height;
|
|
569
|
+
}
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
return offset;
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
function applyPlacement(offset, placement) {
|
|
576
|
+
var tip = tipElement[0],
|
|
577
|
+
width = tip.offsetWidth,
|
|
578
|
+
height = tip.offsetHeight;
|
|
579
|
+
|
|
580
|
+
// manually read margins because getBoundingClientRect includes difference
|
|
581
|
+
var marginTop = parseInt(dimensions.css(tip, 'margin-top'), 10),
|
|
582
|
+
marginLeft = parseInt(dimensions.css(tip, 'margin-left'), 10);
|
|
583
|
+
|
|
584
|
+
// we must check for NaN for ie 8/9
|
|
585
|
+
if (isNaN(marginTop)) marginTop = 0;
|
|
586
|
+
if (isNaN(marginLeft)) marginLeft = 0;
|
|
587
|
+
|
|
588
|
+
offset.top = offset.top + marginTop;
|
|
589
|
+
offset.left = offset.left + marginLeft;
|
|
590
|
+
|
|
591
|
+
// dimensions setOffset doesn't round pixel values
|
|
592
|
+
// so we use setOffset directly with our own function
|
|
593
|
+
dimensions.setOffset(tip, angular.extend({
|
|
594
|
+
using: function (props) {
|
|
595
|
+
tipElement.css({
|
|
596
|
+
top: Math.round(props.top) + 'px',
|
|
597
|
+
left: Math.round(props.left) + 'px'
|
|
598
|
+
});
|
|
599
|
+
}
|
|
600
|
+
}, offset), 0);
|
|
601
|
+
|
|
602
|
+
// check to see if placing tip in new offset caused the tip to resize itself
|
|
603
|
+
var actualWidth = tip.offsetWidth,
|
|
604
|
+
actualHeight = tip.offsetHeight;
|
|
605
|
+
|
|
606
|
+
if (placement === 'top' && actualHeight !== height) {
|
|
607
|
+
offset.top = offset.top + height - actualHeight;
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
// If it's an exotic placement, exit now instead of
|
|
611
|
+
// applying a delta and changing the arrow
|
|
612
|
+
if (/top-left|top-right|bottom-left|bottom-right/.test(placement)) return;
|
|
613
|
+
|
|
614
|
+
var delta = getViewportAdjustedDelta(placement, offset, actualWidth, actualHeight);
|
|
615
|
+
|
|
616
|
+
if (delta.left) {
|
|
617
|
+
offset.left += delta.left;
|
|
618
|
+
} else {
|
|
619
|
+
offset.top += delta.top;
|
|
620
|
+
}
|
|
621
|
+
|
|
622
|
+
dimensions.setOffset(tip, offset);
|
|
623
|
+
|
|
624
|
+
if (/top|right|bottom|left/.test(placement)) {
|
|
625
|
+
var isVertical = /top|bottom/.test(placement),
|
|
626
|
+
arrowDelta = isVertical ? delta.left * 2 - width + actualWidth : delta.top * 2 - height + actualHeight,
|
|
627
|
+
arrowOffsetPosition = isVertical ? 'offsetWidth' : 'offsetHeight';
|
|
628
|
+
|
|
629
|
+
replaceArrow(arrowDelta, tip[arrowOffsetPosition], isVertical);
|
|
630
|
+
}
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
function getViewportAdjustedDelta(placement, position, actualWidth, actualHeight) {
|
|
634
|
+
var delta = { top: 0, left: 0 },
|
|
635
|
+
$viewport = options.viewport && findElement(options.viewport.selector || options.viewport);
|
|
636
|
+
|
|
637
|
+
if (!$viewport) {
|
|
638
|
+
return delta;
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
var viewportPadding = options.viewport && options.viewport.padding || 0,
|
|
642
|
+
viewportDimensions = getPosition($viewport);
|
|
643
|
+
|
|
644
|
+
if (/right|left/.test(placement)) {
|
|
645
|
+
var topEdgeOffset = position.top - viewportPadding - viewportDimensions.scroll,
|
|
646
|
+
bottomEdgeOffset = position.top + viewportPadding - viewportDimensions.scroll + actualHeight;
|
|
647
|
+
if (topEdgeOffset < viewportDimensions.top) { // top overflow
|
|
648
|
+
delta.top = viewportDimensions.top - topEdgeOffset;
|
|
649
|
+
} else if (bottomEdgeOffset > viewportDimensions.top + viewportDimensions.height) { // bottom overflow
|
|
650
|
+
delta.top = viewportDimensions.top + viewportDimensions.height - bottomEdgeOffset;
|
|
651
|
+
}
|
|
652
|
+
} else {
|
|
653
|
+
var leftEdgeOffset = position.left - viewportPadding,
|
|
654
|
+
rightEdgeOffset = position.left + viewportPadding + actualWidth;
|
|
655
|
+
if (leftEdgeOffset < viewportDimensions.left) { // left overflow
|
|
656
|
+
delta.left = viewportDimensions.left - leftEdgeOffset;
|
|
657
|
+
} else if (rightEdgeOffset > viewportDimensions.width) { // right overflow
|
|
658
|
+
delta.left = viewportDimensions.left + viewportDimensions.width - rightEdgeOffset;
|
|
659
|
+
}
|
|
660
|
+
}
|
|
661
|
+
|
|
662
|
+
return delta;
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
function replaceArrow(delta, dimension, isHorizontal) {
|
|
666
|
+
var $arrow = findElement('.tooltip-arrow, .arrow', tipElement[0]);
|
|
667
|
+
|
|
668
|
+
$arrow.css(isHorizontal ? 'left' : 'top', 50 * (1 - delta / dimension) + '%')
|
|
669
|
+
.css(isHorizontal ? 'top' : 'left', '');
|
|
670
|
+
}
|
|
671
|
+
|
|
672
|
+
function destroyTipElement() {
|
|
673
|
+
// Cancel pending callbacks
|
|
674
|
+
clearTimeout(timeout);
|
|
675
|
+
|
|
676
|
+
if($tooltip.$isShown && tipElement !== null) {
|
|
677
|
+
if(options.autoClose) {
|
|
678
|
+
unbindAutoCloseEvents();
|
|
679
|
+
}
|
|
680
|
+
|
|
681
|
+
if(options.keyboard) {
|
|
682
|
+
unbindKeyboardEvents();
|
|
683
|
+
}
|
|
684
|
+
}
|
|
685
|
+
|
|
686
|
+
if(tipScope) {
|
|
687
|
+
tipScope.$destroy();
|
|
688
|
+
tipScope = null;
|
|
689
|
+
}
|
|
690
|
+
|
|
691
|
+
if(tipElement) {
|
|
692
|
+
tipElement.remove();
|
|
693
|
+
tipElement = $tooltip.$element = null;
|
|
694
|
+
}
|
|
695
|
+
}
|
|
696
|
+
|
|
697
|
+
return $tooltip;
|
|
698
|
+
|
|
699
|
+
}
|
|
700
|
+
|
|
701
|
+
// Helper functions
|
|
702
|
+
|
|
703
|
+
function safeDigest(scope) {
|
|
704
|
+
scope.$$phase || (scope.$root && scope.$root.$$phase) || scope.$digest();
|
|
705
|
+
}
|
|
706
|
+
|
|
707
|
+
function findElement(query, element) {
|
|
708
|
+
return angular.element((element || document).querySelectorAll(query));
|
|
709
|
+
}
|
|
710
|
+
|
|
711
|
+
var fetchPromises = {};
|
|
712
|
+
function fetchTemplate(template) {
|
|
713
|
+
if(fetchPromises[template]) return fetchPromises[template];
|
|
714
|
+
return (fetchPromises[template] = $http.get(template, {cache: $templateCache}).then(function(res) {
|
|
715
|
+
return res.data;
|
|
716
|
+
}));
|
|
717
|
+
}
|
|
718
|
+
|
|
719
|
+
return TooltipFactory;
|
|
720
|
+
|
|
721
|
+
};
|
|
722
|
+
|
|
723
|
+
})
|
|
724
|
+
|
|
725
|
+
.directive('bsTooltip', function($window, $location, $sce, $tooltip, $$rAF) {
|
|
726
|
+
|
|
727
|
+
return {
|
|
728
|
+
restrict: 'EAC',
|
|
729
|
+
scope: true,
|
|
730
|
+
link: function postLink(scope, element, attr, transclusion) {
|
|
731
|
+
|
|
732
|
+
// Directive options
|
|
733
|
+
var options = {scope: scope};
|
|
734
|
+
angular.forEach(['template', 'contentTemplate', 'placement', 'container', 'delay', 'trigger', 'keyboard', 'html', 'animation', 'backdropAnimation', 'type', 'customClass', 'id'], function(key) {
|
|
735
|
+
if(angular.isDefined(attr[key])) options[key] = attr[key];
|
|
736
|
+
});
|
|
737
|
+
|
|
738
|
+
// should not parse target attribute, only data-target
|
|
739
|
+
if(element.attr('data-target')) {
|
|
740
|
+
options.target = element.attr('data-target');
|
|
741
|
+
}
|
|
742
|
+
|
|
743
|
+
// overwrite inherited title value when no value specified
|
|
744
|
+
// fix for angular 1.3.1 531a8de72c439d8ddd064874bf364c00cedabb11
|
|
745
|
+
if (!scope.hasOwnProperty('title')){
|
|
746
|
+
scope.title = '';
|
|
747
|
+
}
|
|
748
|
+
|
|
749
|
+
// Observe scope attributes for change
|
|
750
|
+
attr.$observe('title', function(newValue) {
|
|
751
|
+
if (angular.isDefined(newValue) || !scope.hasOwnProperty('title')) {
|
|
752
|
+
var oldValue = scope.title;
|
|
753
|
+
scope.title = $sce.trustAsHtml(newValue);
|
|
754
|
+
angular.isDefined(oldValue) && $$rAF(function() {
|
|
755
|
+
tooltip && tooltip.$applyPlacement();
|
|
756
|
+
});
|
|
757
|
+
}
|
|
758
|
+
});
|
|
759
|
+
|
|
760
|
+
// Support scope as an object
|
|
761
|
+
attr.bsTooltip && scope.$watch(attr.bsTooltip, function(newValue, oldValue) {
|
|
762
|
+
if(angular.isObject(newValue)) {
|
|
763
|
+
angular.extend(scope, newValue);
|
|
764
|
+
} else {
|
|
765
|
+
scope.title = newValue;
|
|
766
|
+
}
|
|
767
|
+
angular.isDefined(oldValue) && $$rAF(function() {
|
|
768
|
+
tooltip && tooltip.$applyPlacement();
|
|
769
|
+
});
|
|
770
|
+
}, true);
|
|
771
|
+
|
|
772
|
+
// Visibility binding support
|
|
773
|
+
attr.bsShow && scope.$watch(attr.bsShow, function(newValue, oldValue) {
|
|
774
|
+
if(!tooltip || !angular.isDefined(newValue)) return;
|
|
775
|
+
if(angular.isString(newValue)) newValue = !!newValue.match(/true|,?(tooltip),?/i);
|
|
776
|
+
newValue === true ? tooltip.show() : tooltip.hide();
|
|
777
|
+
});
|
|
778
|
+
|
|
779
|
+
// Enabled binding support
|
|
780
|
+
attr.bsEnabled && scope.$watch(attr.bsEnabled, function(newValue, oldValue) {
|
|
781
|
+
// console.warn('scope.$watch(%s)', attr.bsEnabled, newValue, oldValue);
|
|
782
|
+
if(!tooltip || !angular.isDefined(newValue)) return;
|
|
783
|
+
if(angular.isString(newValue)) newValue = !!newValue.match(/true|1|,?(tooltip),?/i);
|
|
784
|
+
newValue === false ? tooltip.setEnabled(false) : tooltip.setEnabled(true);
|
|
785
|
+
});
|
|
786
|
+
|
|
787
|
+
// Viewport support
|
|
788
|
+
attr.viewport && scope.$watch(attr.viewport, function (newValue) {
|
|
789
|
+
if(!tooltip || !angular.isDefined(newValue)) return;
|
|
790
|
+
tooltip.setViewport(newValue);
|
|
791
|
+
});
|
|
792
|
+
|
|
793
|
+
// Initialize popover
|
|
794
|
+
var tooltip = $tooltip(element, options);
|
|
795
|
+
|
|
796
|
+
// Garbage collection
|
|
797
|
+
scope.$on('$destroy', function() {
|
|
798
|
+
if(tooltip) tooltip.destroy();
|
|
799
|
+
options = null;
|
|
800
|
+
tooltip = null;
|
|
801
|
+
});
|
|
802
|
+
|
|
803
|
+
}
|
|
804
|
+
};
|
|
805
|
+
|
|
806
|
+
});
|