forked from juggy/jquery-popover
-
Notifications
You must be signed in to change notification settings - Fork 8
/
jquery.popover.js
324 lines (269 loc) · 9.77 KB
/
jquery.popover.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
// Modified from version taken -> https://github.com/juggy/jquery-popover
(function($) {
$.fn.popover = function(options) {
var KEY_ESC = 27;
// settings stored options and state
var settings = $.extend({
id: '', // id for created popover
openEvent: null, // callback function to be called when popup opened
closeEvent: null, // callback function to be called when popup closed
offsetX: 0, // fixed offset to correct popup X position
offsetY: 0, // fixed offset to correct popup Y position
zindex: 100000, // default z-index value
padding: 18, // default settings.padding around popover from document edges
closeOnEsc: true, // change to false to disable ESC
preventLeft: false, // pass true to prevent left popover
preventRight: false, // pass true to prevent right popover
preventTop: false, // pass true to prevent top popover
preventBottom: false, // pass true to prevent bottom popover
live: false // popover created on live selector
}, options || {});
// functions to claculate popover direction and position
function calcPopoverDirPossible(button, coord) {
var possibleDir = {
left: false,
right: false,
top: false,
bottom: false
}
if (coord.buttonOffset.top + coord.buttonHeight + coord.triangleSize + coord.popoverHeight <=
coord.docHeight - settings.padding) {
possibleDir.bottom = true;
}
if (coord.buttonOffset.top - coord.triangleSize - coord.popoverHeight >= settings.padding) {
possibleDir.top = true;
}
if (coord.buttonOffset.left + coord.buttonWidth + coord.triangleSize + coord.popoverWidth <=
coord.docWidth - settings.padding) {
possibleDir.right = true;
}
if (coord.buttonOffset.left - coord.triangleSize - coord.popoverWidth >= settings.padding) {
possibleDir.left = true;
}
return possibleDir;
}
function chooseDir(possibleDir) {
// remove directions prevented by settings
if (settings.preventBottom)
possibleDir.bottom = false;
if (settings.preventTop)
possibleDir.top = false;
if (settings.preventLeft)
possibleDir.left = false;
if (settings.preventRight)
possibleDir.right = false;
// determine default direction if nothing works out
// make sure it is not one of the prevented directions
var dir = 'right';
if (settings.preventRight)
dir = 'bottom';
if (settings.preventBottom)
dir = 'top';
if (settings.preventTop)
dir = 'left';
if (possibleDir.right)
dir = 'right';
else if (possibleDir.bottom)
dir = 'bottom';
else if (possibleDir.left)
dir = 'left';
else if (possibleDir.top)
dir = 'top';
return dir;
}
function calcPopoverPos(button) {
// Set this first for the layout calculations to work.
settings.popover$.css('display', 'block');
var coord = {
popoverDir: 'bottom',
popoverX: 0,
popoverY: 0,
deltaX: 0,
deltaY: 0,
triangleX: 0,
triangleY: 0,
triangleSize: 20, // needs to be updated if triangle changed in css
docWidth: $(window).width(),
docHeight: $(window).height(),
popoverWidth: settings.popover$.outerWidth(),
popoverHeight: settings.popover$.outerHeight(),
buttonWidth: button.outerWidth(),
buttonHeight: button.outerHeight(),
buttonOffset: button.offset()
}
// calculate the possible directions based on popover size and button position
var possibleDir = calcPopoverDirPossible(button, coord);
// choose selected direction
coord.popoverDir = chooseDir(possibleDir);
// Calculate popover top
if (coord.popoverDir == 'bottom')
coord.popoverY = coord.buttonOffset.top + coord.buttonHeight + coord.triangleSize;
else if (coord.popoverDir == 'top')
coord.popoverY = coord.buttonOffset.top - coord.triangleSize - coord.popoverHeight;
else // same Y for left & right
coord.popoverY = coord.buttonOffset.top + (coord.buttonHeight - coord.popoverHeight)/2;
// Calculate popover left
if ((coord.popoverDir == 'bottom') || (coord.popoverDir == 'top')) {
coord.popoverX = coord.buttonOffset.left + (coord.buttonWidth - coord.popoverWidth)/2;
if (coord.popoverX < settings.padding) {
// out of the document at left
coord.deltaX = coord.popoverX - settings.padding;
} else if (coord.popoverX + coord.popoverWidth > coord.docWidth - settings.padding) {
// out of the document right
coord.deltaX = coord.popoverX + coord.popoverWidth - coord.docWidth + settings.padding;
}
// calc triangle pos
coord.triangleX = coord.popoverWidth/2 - coord.triangleSize + coord.deltaX;
coord.triangleY = 0;
}
else { // left or right direction
if (coord.popoverDir == 'right')
coord.popoverX = coord.buttonOffset.left + coord.buttonWidth + coord.triangleSize;
else // left
coord.popoverX = coord.buttonOffset.left - coord.triangleSize - coord.popoverWidth;
if (coord.popoverY < settings.padding) {
// out of the document at top
coord.deltaY = coord.popoverY - settings.padding;
} else if (coord.popoverY + coord.popoverHeight > coord.docHeight - settings.padding) {
// out of the document bottom
coord.deltaY = coord.popoverY + coord.popoverHeight - coord.docHeight + settings.padding;
}
// calc triangle pos
coord.triangleX = 0;
coord.triangleY = coord.popoverHeight/2 - coord.triangleSize + coord.deltaY;
}
return coord;
}
function positionPopover(coord) {
// set the triangle class for it's direction
settings.triangle$.removeClass("left top right bottom");
settings.triangle$.addClass(coord.popoverDir);
if (coord.triangleX > 0) {
settings.triangle$.css('left', coord.triangleX);
}
if (coord.triangleY > 0) {
settings.triangle$.css('top', coord.triangleY);
}
// position popover
settings.popover$.offset({
top: coord.popoverY - coord.deltaY + settings.offsetY,
left: coord.popoverX - coord.deltaX + settings.offsetX
});
// set popover css and show it
settings.popover$.css('z-index', settings.zindex).show();
}
// toggle a button popover. If show set to true do not toggle - always show
function togglePopover(button, show) {
// if this button popover is already open close it an return
if ($.fn.popover.openedPopup &&
($.fn.popover.openedPopup.get(0) === button.get(0))) {
if (!show)
hideOpenPopover();
return;
}
// hide any open popover
hideOpenPopover();
// clicking triangle will also close the popover
settings.triangle$.click(function() {
button.trigger('hidePopover')
});
// reset triangle
settings.triangle$.attr("style", "");
// calculate all the coordinates needed for positioning the popover and position it
positionPopover(calcPopoverPos(button));
//Timeout for webkit transitions to take effect
setTimeout(function() {
settings.popover$.addClass("active");
}, 0);
if ($.isFunction(settings.openEvent)) {
settings.openEvent(button);
}
$.fn.popover.openedPopup = button;
button.addClass('popover-on');
$(document).trigger('popoverOpened');
}
// hide a button popover
function hidePopover(button) {
button.removeClass('popover-on');
$(document).trigger('popoverClosed');
settings.popover$.removeClass("active").attr("style", "").hide();
if ($.isFunction(settings.closeEvent)) {
settings.closeEvent(button);
}
$.fn.popover.openedPopup = null;
}
// hide the currently open popover if there is one
function hideOpenPopover() {
if ($.fn.popover.openedPopup != null)
$.fn.popover.openedPopup.trigger('hidePopover');
}
// build HTML popover
settings.popover$ = $('<div class="popover" id="' + settings.id + '">'
+ '<div class="triangle"></div>'
+ '<div class="header"></div>'
+ '<div class="content"></div>'
+ '</div>').appendTo('body');
$('.header', settings.popover$).append($(settings.header).detach());
$('.content', settings.popover$).append($(settings.content).detach());
settings.triangle$ = $('.triangle', settings.popover$);
// remember last opened popup
$.fn.popover.openedPopup = null;
// setup global document bindings
$(document).unbind(".popover");
// document click closes active popover
$(document).bind("click.popover", function(event) {
if (($(event.target).parents(".popover").length == 0)
&& (!$(event.target).hasClass('popover-button'))) {
hideOpenPopover();
}
});
// document hidePopover event causes active popover to close
$(document).bind("hideOpenPopover.popover", hideOpenPopover);
// document Esc key listener
var selector = this;
if (settings.closeOnEsc) {
$(document).bind('keydown', function(event) {
if (!event.altKey && !event.ctrlKey && !event.shiftKey
&& (event.keyCode == KEY_ESC)) {
selector.trigger('hidePopover');
}
});
}
// setup callbacks for each popover button in wrapped set & return wrapped set
if (!settings.live) {
return this.each(function() {
var button = $(this);
button.addClass("popover-button");
button.bind('click', function() {
togglePopover(button);
return false;
});
button.bind('showPopover', function() {
togglePopover(button, true);
return false;
});
button.bind('hidePopover', function() {
hidePopover(button);
return false;
});
});
}
else { // live popover
this.live('click', function(event) {
$(event.target).addClass("popover-button");
togglePopover($(event.target));
return false;
});
this.live('showPopover', function(event) {
$(event.target).addClass("popover-button");
togglePopover($(event.target), true);
return false;
});
this.live('hidePopover', function(event) {
hidePopover($(event.target));
return false;
});
return this;
}
};
})(jQuery);