michael_hintbuble 1.0.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.
- data/.gitignore +2 -0
- data/.specification +122 -0
- data/MIT-LICENSE +20 -0
- data/README.rdoc +201 -0
- data/Rakefile +49 -0
- data/VERSION +1 -0
- data/generators/michael_hintbuble/michael_hintbuble_generator.rb +13 -0
- data/generators/michael_hintbuble/templates/michael_hintbuble.css +48 -0
- data/generators/michael_hintbuble/templates/michael_hintbuble.js +572 -0
- data/generators/michael_hintbuble/templates/michael_hintbuble_pointer.png +0 -0
- data/init.rb +1 -0
- data/lib/generators/michael_hintbuble/michael_hintbuble_generator.rb +22 -0
- data/lib/generators/michael_hintbuble/templates/michael_hintbuble.css +48 -0
- data/lib/generators/michael_hintbuble/templates/michael_hintbuble.js +572 -0
- data/lib/generators/michael_hintbuble/templates/michael_hintbuble_pointer.png +0 -0
- data/lib/michael_hintbuble/helpers.rb +171 -0
- data/lib/michael_hintbuble.rb +10 -0
- data/michael_hintbuble.gemspec +67 -0
- data/rails/init.rb +1 -0
- data/test/michael_hintbuble/helpers_test.rb +156 -0
- data/test/test_helper.rb +16 -0
- metadata +121 -0
@@ -0,0 +1,572 @@
|
|
1
|
+
/**
|
2
|
+
* Michael Hintbuble creates pretty hint bubbles using prototype and
|
3
|
+
* scriptaculous. These functions work with ActionView helpers
|
4
|
+
* to provide hint bubble components using the syntax defined
|
5
|
+
* for rendering rails templates.
|
6
|
+
*
|
7
|
+
*
|
8
|
+
* Brought to you by the good folks at Coroutine. Hire us!
|
9
|
+
* http://coroutine.com
|
10
|
+
*/
|
11
|
+
var MichaelHintbuble = {}
|
12
|
+
|
13
|
+
|
14
|
+
/**
|
15
|
+
* This property governs whether or not Michael bothers creating and
|
16
|
+
* managing a blocking iframe to accommodate ie6.
|
17
|
+
*
|
18
|
+
* Defaults to false, but override if you must.
|
19
|
+
*/
|
20
|
+
MichaelHintbuble.SUPPORT_IE6_BULLSHIT = false;
|
21
|
+
|
22
|
+
|
23
|
+
|
24
|
+
//-----------------------------------------------------------------------------
|
25
|
+
// Bubble class
|
26
|
+
//-----------------------------------------------------------------------------
|
27
|
+
|
28
|
+
/**
|
29
|
+
* This function lets you come fly with Michael by defining
|
30
|
+
* the hint bubble class.
|
31
|
+
*/
|
32
|
+
MichaelHintbuble.Bubble = function(target_id, content, options) {
|
33
|
+
this._target = $(target_id);
|
34
|
+
this._element = null;
|
35
|
+
this._positioner = null;
|
36
|
+
this._isShowing = null;
|
37
|
+
|
38
|
+
this._class = options["class"] || "michael_hintbuble_bubble";
|
39
|
+
this._style = options["style"] || "";
|
40
|
+
this._eventNames = options["eventNames"] || ["mouseover","resize","scroll"]
|
41
|
+
this._position = options["position"] || "right";
|
42
|
+
this._beforeShow = options["beforeShow"] || Prototype.emptyFunction
|
43
|
+
this._afterShow = options["afterShow"] || Prototype.emptyFunction
|
44
|
+
this._beforeHide = options["beforeHide"] || Prototype.emptyFunction
|
45
|
+
this._afterHide = options["afterHide"] || Prototype.emptyFunction
|
46
|
+
|
47
|
+
this._makeBubble();
|
48
|
+
this._makePositioner();
|
49
|
+
this._attachObservers();
|
50
|
+
this.setContent(content);
|
51
|
+
this.setPosition();
|
52
|
+
|
53
|
+
if (MichaelHintbuble.SUPPORT_IE6_BULLSHIT) {
|
54
|
+
this._makeFrame();
|
55
|
+
}
|
56
|
+
};
|
57
|
+
|
58
|
+
|
59
|
+
/**
|
60
|
+
* This hash maps the bubble id to the bubble object itself. It allows the Rails
|
61
|
+
* code a way to specify the js object it wishes to invoke.
|
62
|
+
*/
|
63
|
+
MichaelHintbuble.Bubble.instances = {};
|
64
|
+
|
65
|
+
|
66
|
+
/**
|
67
|
+
* This method destroys the bubble with the corresponding target id.
|
68
|
+
*
|
69
|
+
* @param {String} id The target id value of the bubble element (also the key
|
70
|
+
* in the instances hash.)
|
71
|
+
*/
|
72
|
+
MichaelHintbuble.Bubble.destroy = function(id) {
|
73
|
+
var bubble = this.instances[id];
|
74
|
+
if (bubble) {
|
75
|
+
bubble.finalize();
|
76
|
+
}
|
77
|
+
this.instances[id] = null;
|
78
|
+
};
|
79
|
+
|
80
|
+
|
81
|
+
/**
|
82
|
+
* This method hides the bubble with the corresponding target id.
|
83
|
+
*
|
84
|
+
* @param {String} id The target id value of the bubble element (also the key
|
85
|
+
* in the instances hash.)
|
86
|
+
*
|
87
|
+
* @return {Object} an instance of MichaelHintbuble.Bubble
|
88
|
+
*
|
89
|
+
*/
|
90
|
+
MichaelHintbuble.Bubble.hide = function(id) {
|
91
|
+
var bubble = this.instances[id];
|
92
|
+
if (bubble) {
|
93
|
+
bubble.hide();
|
94
|
+
}
|
95
|
+
return bubble;
|
96
|
+
};
|
97
|
+
|
98
|
+
|
99
|
+
/**
|
100
|
+
* This method returns a boolean indiciating whether or not the
|
101
|
+
* bubble with the corresponding target id is showing.
|
102
|
+
*
|
103
|
+
* @param {String} id The target id value of the bubble element (also the key
|
104
|
+
* in the instances hash.)
|
105
|
+
*
|
106
|
+
* @return {Boolean} Whether or not the bubble with the corresponding
|
107
|
+
* id is showing.
|
108
|
+
*
|
109
|
+
*/
|
110
|
+
MichaelHintbuble.Bubble.isShowing = function(id) {
|
111
|
+
var bubble = this.instances[id];
|
112
|
+
if (!bubble) {
|
113
|
+
throw "No bubble cound be found for the supplied id.";
|
114
|
+
}
|
115
|
+
return bubble.isShowing();
|
116
|
+
};
|
117
|
+
|
118
|
+
|
119
|
+
/**
|
120
|
+
* This method shows the bubble with the corresponding target id.
|
121
|
+
*
|
122
|
+
* @param {String} id The target id value of the bubble element (also the key
|
123
|
+
* in the instances hash.)
|
124
|
+
*
|
125
|
+
* @return {Object} an instance of MichaelHintbuble.Bubble
|
126
|
+
*
|
127
|
+
*/
|
128
|
+
MichaelHintbuble.Bubble.show = function(id) {
|
129
|
+
var bubble = this.instances[id];
|
130
|
+
if (bubble) {
|
131
|
+
bubble.show();
|
132
|
+
}
|
133
|
+
return bubble;
|
134
|
+
};
|
135
|
+
|
136
|
+
|
137
|
+
/**
|
138
|
+
* This function establishes all of the observations specified in the options.
|
139
|
+
*/
|
140
|
+
MichaelHintbuble.Bubble.prototype._attachObservers = function() {
|
141
|
+
if (this._eventNames.indexOf("focus") > -1) {
|
142
|
+
this._target.observe("focus", function() {
|
143
|
+
this.show();
|
144
|
+
}.bind(this));
|
145
|
+
this._target.observe("blur", function() {
|
146
|
+
this.hide();
|
147
|
+
}.bind(this));
|
148
|
+
}
|
149
|
+
if (this._eventNames.indexOf("mouseover") > -1) {
|
150
|
+
this._target.observe("mouseover", function() {
|
151
|
+
this.show();
|
152
|
+
}.bind(this));
|
153
|
+
this._target.observe("mouseout", function() {
|
154
|
+
this.hide();
|
155
|
+
}.bind(this));
|
156
|
+
}
|
157
|
+
if (this._eventNames.indexOf("resize") > -1) {
|
158
|
+
Event.observe(window, "resize", function() {
|
159
|
+
if (this.isShowing()) {
|
160
|
+
this.setPosition();
|
161
|
+
}
|
162
|
+
}.bind(this));
|
163
|
+
}
|
164
|
+
if (this._eventNames.indexOf("scroll") > -1) {
|
165
|
+
Event.observe(window, "scroll", function() {
|
166
|
+
if (this.isShowing()) {
|
167
|
+
this.setPosition();
|
168
|
+
}
|
169
|
+
}.bind(this));
|
170
|
+
}
|
171
|
+
};
|
172
|
+
|
173
|
+
|
174
|
+
/**
|
175
|
+
* This function creates the bubble element and hides it by default.
|
176
|
+
*/
|
177
|
+
MichaelHintbuble.Bubble.prototype._makeBubble = function() {
|
178
|
+
if (!this._element) {
|
179
|
+
this._container = new Element("DIV");
|
180
|
+
this._container.className = "container";
|
181
|
+
|
182
|
+
this._element = new Element("DIV");
|
183
|
+
this._element.className = this._class;
|
184
|
+
this._element.writeAttribute("style", this._style);
|
185
|
+
this._element.update(this._container);
|
186
|
+
this._element.hide();
|
187
|
+
document.body.insert(this._element);
|
188
|
+
}
|
189
|
+
};
|
190
|
+
|
191
|
+
|
192
|
+
/**
|
193
|
+
* This function creates the blocking frame element and hides it by default.
|
194
|
+
*/
|
195
|
+
MichaelHintbuble.Bubble.prototype._makeFrame = function() {
|
196
|
+
if (!this._frame) {
|
197
|
+
this._frame = new Element("IFRAME");
|
198
|
+
this._frame.className = this._class + "_frame";
|
199
|
+
this._frame.setAttribute("src", "about:blank");
|
200
|
+
this._frame.hide();
|
201
|
+
}
|
202
|
+
};
|
203
|
+
|
204
|
+
|
205
|
+
/**
|
206
|
+
* This function creates the bubble positioner object.
|
207
|
+
*/
|
208
|
+
MichaelHintbuble.Bubble.prototype._makePositioner = function() {
|
209
|
+
if (!this._positioner) {
|
210
|
+
this._positioner = new MichaelHintbuble.BubblePositioner(this._target, this._element, this._position);
|
211
|
+
}
|
212
|
+
};
|
213
|
+
|
214
|
+
|
215
|
+
/**
|
216
|
+
* This method updates the container element by applying an additional style
|
217
|
+
* class representing the relative position of the bubble to the target.
|
218
|
+
*/
|
219
|
+
MichaelHintbuble.Bubble.prototype._updateContainerClass = function() {
|
220
|
+
this._container.className = "container";
|
221
|
+
this._container.addClassName(this._positioner.styleClassForPosition());
|
222
|
+
};
|
223
|
+
|
224
|
+
|
225
|
+
/**
|
226
|
+
* This function allows the bubble object to be destroyed without
|
227
|
+
* creating memory leaks.
|
228
|
+
*/
|
229
|
+
MichaelHintbuble.Bubble.prototype.finalize = function() {
|
230
|
+
this._positioner.finalize();
|
231
|
+
this._container.remove();
|
232
|
+
this._element.remove();
|
233
|
+
|
234
|
+
this._target = null;
|
235
|
+
this._element = null;
|
236
|
+
this._container = null;
|
237
|
+
this._positioner = null;
|
238
|
+
|
239
|
+
if (MichaelHintbuble.SUPPORT_IE6_BULLSHIT) {
|
240
|
+
this._frame.remove();
|
241
|
+
this._frame = null;
|
242
|
+
}
|
243
|
+
};
|
244
|
+
|
245
|
+
|
246
|
+
/**
|
247
|
+
* This function shows the hint bubble container (and the blocking frame, if
|
248
|
+
* required).
|
249
|
+
*/
|
250
|
+
MichaelHintbuble.Bubble.prototype.hide = function() {
|
251
|
+
new Effect.Fade(this._element, {
|
252
|
+
duration: 0.2,
|
253
|
+
beforeStart: this._beforeHide,
|
254
|
+
afterFinish: function() {
|
255
|
+
this._isShowing = false;
|
256
|
+
this._afterHide();
|
257
|
+
}.bind(this)
|
258
|
+
});
|
259
|
+
|
260
|
+
if (this._frame) {
|
261
|
+
new Effect.Fade(this._frame, {
|
262
|
+
duration: 0.2
|
263
|
+
});
|
264
|
+
}
|
265
|
+
};
|
266
|
+
|
267
|
+
|
268
|
+
/**
|
269
|
+
* This function returns a boolean indicating whether or not the bubble is
|
270
|
+
* showing.
|
271
|
+
*
|
272
|
+
* @returns {Boolean} Whether or not the bubble is showing.
|
273
|
+
*/
|
274
|
+
MichaelHintbuble.Bubble.prototype.isShowing = function() {
|
275
|
+
return this._isShowing;
|
276
|
+
};
|
277
|
+
|
278
|
+
|
279
|
+
/**
|
280
|
+
* This function sets the content of the hint bubble container.
|
281
|
+
*
|
282
|
+
* @param {String} content A string representation of the content to be added
|
283
|
+
* to the hint bubble container.
|
284
|
+
*/
|
285
|
+
MichaelHintbuble.Bubble.prototype.setContent = function(content) {
|
286
|
+
var content_container = new Element("DIV");
|
287
|
+
content_container.className = "content";
|
288
|
+
content_container.update(content);
|
289
|
+
|
290
|
+
this._container.update(content_container);
|
291
|
+
};
|
292
|
+
|
293
|
+
|
294
|
+
/**
|
295
|
+
* This method sets the position of the hint bubble. It should be noted that the
|
296
|
+
* position simply states a preferred location for the bubble within the viewport.
|
297
|
+
* If the supplied position results in the bubble overrunning the viewport,
|
298
|
+
* the bubble will be repositioned to the opposite side to avoid viewport
|
299
|
+
* overrun.
|
300
|
+
*
|
301
|
+
* @param {String} position A string representation of the preferred position of
|
302
|
+
* the bubble element.
|
303
|
+
*/
|
304
|
+
MichaelHintbuble.Bubble.prototype.setPosition = function(position) {
|
305
|
+
if (position) {
|
306
|
+
this._position = position.toLowerCase();
|
307
|
+
}
|
308
|
+
this._positioner.setPosition(this._position);
|
309
|
+
this._updateContainerClass();
|
310
|
+
};
|
311
|
+
|
312
|
+
|
313
|
+
/**
|
314
|
+
* This function shows the hint bubble container (and the blocking frame, if
|
315
|
+
* required).
|
316
|
+
*/
|
317
|
+
MichaelHintbuble.Bubble.prototype.show = function() {
|
318
|
+
this.setPosition();
|
319
|
+
|
320
|
+
if (this._frame) {
|
321
|
+
var layout = new Element.Layout(this._element);
|
322
|
+
this._frame.style.top = layout.get("top") + "px";
|
323
|
+
this._frame.style.left = layout.get("left") + "px";
|
324
|
+
this._frame.style.width = layout.get("width") + "px";
|
325
|
+
this._frame.style.height = layout.get("height") + "px";
|
326
|
+
|
327
|
+
new Effect.Appear(this._frame, {
|
328
|
+
duration: 0.2
|
329
|
+
});
|
330
|
+
}
|
331
|
+
|
332
|
+
new Effect.Appear(this._element, {
|
333
|
+
duration: 0.2,
|
334
|
+
beforeStart: this._beforeShow,
|
335
|
+
afterFinish: function() {
|
336
|
+
this._isShowing = true;
|
337
|
+
this._afterShow();
|
338
|
+
}.bind(this)
|
339
|
+
});
|
340
|
+
};
|
341
|
+
|
342
|
+
|
343
|
+
|
344
|
+
|
345
|
+
//-----------------------------------------------------------------------------
|
346
|
+
// BubblePositioner class
|
347
|
+
//-----------------------------------------------------------------------------
|
348
|
+
|
349
|
+
/**
|
350
|
+
* This class encapsulates the positioning logic for bubble classes.
|
351
|
+
*
|
352
|
+
* @param {Element} target the dom element to which the bubble is anchored.
|
353
|
+
* @param {Element} element the bubble element itself.
|
354
|
+
*/
|
355
|
+
MichaelHintbuble.BubblePositioner = function(target, element, position) {
|
356
|
+
this._target = target;
|
357
|
+
this._element = element;
|
358
|
+
this._position = position;
|
359
|
+
this._axis = null
|
360
|
+
};
|
361
|
+
|
362
|
+
|
363
|
+
/**
|
364
|
+
* These properties establish numeric values for the x and y axes.
|
365
|
+
*/
|
366
|
+
MichaelHintbuble.BubblePositioner.X_AXIS = 1;
|
367
|
+
MichaelHintbuble.BubblePositioner.Y_AXIS = 2;
|
368
|
+
|
369
|
+
|
370
|
+
/**
|
371
|
+
* This property maps position values to one or the other axis.
|
372
|
+
*/
|
373
|
+
MichaelHintbuble.BubblePositioner.AXIS_MAP = {
|
374
|
+
left: MichaelHintbuble.BubblePositioner.X_AXIS,
|
375
|
+
right: MichaelHintbuble.BubblePositioner.X_AXIS,
|
376
|
+
top: MichaelHintbuble.BubblePositioner.Y_AXIS,
|
377
|
+
bottom: MichaelHintbuble.BubblePositioner.Y_AXIS
|
378
|
+
};
|
379
|
+
|
380
|
+
|
381
|
+
/**
|
382
|
+
* This property maps position values to their opposite value.
|
383
|
+
*/
|
384
|
+
MichaelHintbuble.BubblePositioner.COMPLEMENTS = {
|
385
|
+
left: "right",
|
386
|
+
right: "left",
|
387
|
+
top: "bottom",
|
388
|
+
bottom: "top"
|
389
|
+
};
|
390
|
+
|
391
|
+
|
392
|
+
/**
|
393
|
+
* This hash is a convenience that allows us to write slightly denser code when
|
394
|
+
* calculating the bubble's position.
|
395
|
+
*/
|
396
|
+
MichaelHintbuble.BubblePositioner.POSITION_FN_MAP = {
|
397
|
+
left: "getWidth",
|
398
|
+
top: "getHeight"
|
399
|
+
};
|
400
|
+
|
401
|
+
|
402
|
+
|
403
|
+
/**
|
404
|
+
* This function positions the element below the target.
|
405
|
+
*/
|
406
|
+
MichaelHintbuble.BubblePositioner.prototype._bottom = function() {
|
407
|
+
var to = this._targetAdjustedOffset();
|
408
|
+
var tl = new Element.Layout(this._target);
|
409
|
+
|
410
|
+
this._element.style.top = (to.top + tl.get("border-box-height")) + "px";
|
411
|
+
};
|
412
|
+
|
413
|
+
|
414
|
+
/**
|
415
|
+
* This function centers the positioning of the element for whichever
|
416
|
+
* axis it is on.
|
417
|
+
*/
|
418
|
+
MichaelHintbuble.BubblePositioner.prototype._center = function() {
|
419
|
+
var to = this._targetAdjustedOffset();
|
420
|
+
var tl = new Element.Layout(this._target);
|
421
|
+
var el = new Element.Layout(this._element);
|
422
|
+
|
423
|
+
if (this._axis === MichaelHintbuble.BubblePositioner.X_AXIS) {
|
424
|
+
this._element.style.top = (to.top + Math.ceil(tl.get("border-box-height")/2) - Math.ceil(el.get("padding-box-height")/2)) + "px";
|
425
|
+
}
|
426
|
+
else if (this._axis === MichaelHintbuble.BubblePositioner.Y_AXIS) {
|
427
|
+
this._element.style.left = (to.left + Math.ceil(tl.get("border-box-width")/2) - Math.ceil(el.get("padding-box-width")/2)) + "px";
|
428
|
+
}
|
429
|
+
};
|
430
|
+
|
431
|
+
|
432
|
+
/**
|
433
|
+
* This function returns a boolean indicating whether or not the element is
|
434
|
+
* contained within the viewport.
|
435
|
+
*
|
436
|
+
* @returns {Boolean} whether or not the element is contained within the viewport.
|
437
|
+
*/
|
438
|
+
MichaelHintbuble.BubblePositioner.prototype._isElementWithinViewport = function() {
|
439
|
+
var isWithinViewport = true;
|
440
|
+
var fnMap = MichaelHintbuble.BubblePositioner.POSITION_FN_MAP;
|
441
|
+
var method = null;
|
442
|
+
var viewPortMinEdge = null;
|
443
|
+
var viewPortMaxEdge = null;
|
444
|
+
var elementMinEdge = null;
|
445
|
+
var elementMaxEdge = null;
|
446
|
+
|
447
|
+
for (var prop in fnMap) {
|
448
|
+
method = fnMap[prop];
|
449
|
+
viewportMinEdge = document.viewport.getScrollOffsets()[prop];
|
450
|
+
viewportMaxEdge = viewportMinEdge + document.viewport[method]();
|
451
|
+
elementMinEdge = parseInt(this._element.style[prop] || 0);
|
452
|
+
elementMaxEdge = elementMinEdge + this._element[method]();
|
453
|
+
|
454
|
+
if ((elementMaxEdge > viewportMaxEdge) || (elementMinEdge < viewportMinEdge)) {
|
455
|
+
isWithinViewport = false;
|
456
|
+
break;
|
457
|
+
}
|
458
|
+
}
|
459
|
+
|
460
|
+
return isWithinViewport;
|
461
|
+
};
|
462
|
+
|
463
|
+
|
464
|
+
/**
|
465
|
+
* This function positions the element to the left of the target.
|
466
|
+
*/
|
467
|
+
MichaelHintbuble.BubblePositioner.prototype._left = function() {
|
468
|
+
var to = this._targetAdjustedOffset();
|
469
|
+
var el = new Element.Layout(this._element);
|
470
|
+
|
471
|
+
this._element.style.left = (to.left - el.get("padding-box-width")) + "px";
|
472
|
+
};
|
473
|
+
|
474
|
+
|
475
|
+
/**
|
476
|
+
* This function positions the element to the right of the target.
|
477
|
+
*/
|
478
|
+
MichaelHintbuble.BubblePositioner.prototype._right = function() {
|
479
|
+
var to = this._targetAdjustedOffset();
|
480
|
+
var tl = new Element.Layout(this._target);
|
481
|
+
|
482
|
+
this._element.style.left = (to.left + tl.get("border-box-width")) + "px";
|
483
|
+
};
|
484
|
+
|
485
|
+
|
486
|
+
/**
|
487
|
+
* This function positions the element relative to the target according to the
|
488
|
+
* position value supplied. Because this function is private, it assumes a
|
489
|
+
* safe position value.
|
490
|
+
*
|
491
|
+
* @param {String} position the desired relative position of the element to the
|
492
|
+
* target.
|
493
|
+
*/
|
494
|
+
MichaelHintbuble.BubblePositioner.prototype._setPosition = function(position) {
|
495
|
+
this._axis = MichaelHintbuble.BubblePositioner.AXIS_MAP[position];
|
496
|
+
this._position = position;
|
497
|
+
this["_" + position]();
|
498
|
+
this._center();
|
499
|
+
};
|
500
|
+
|
501
|
+
|
502
|
+
/**
|
503
|
+
* This function returns a hash with the adjusted offset positions for the target
|
504
|
+
* element.
|
505
|
+
*/
|
506
|
+
MichaelHintbuble.BubblePositioner.prototype._targetAdjustedOffset = function() {
|
507
|
+
var bs = $$("body").first().cumulativeScrollOffset();
|
508
|
+
var to = this._target.cumulativeOffset();
|
509
|
+
var ts = this._target.cumulativeScrollOffset();
|
510
|
+
|
511
|
+
return {
|
512
|
+
"top": to.top - ts.top + bs.top,
|
513
|
+
"left": to.left - ts.left + bs.left
|
514
|
+
}
|
515
|
+
};
|
516
|
+
|
517
|
+
|
518
|
+
/**
|
519
|
+
* This function positions the element above the target.
|
520
|
+
*/
|
521
|
+
MichaelHintbuble.BubblePositioner.prototype._top = function() {
|
522
|
+
var to = this._targetAdjustedOffset();
|
523
|
+
var el = new Element.Layout(this._element);
|
524
|
+
|
525
|
+
this._element.style.top = (to.top - el.get("padding-box-height")) + "px";
|
526
|
+
};
|
527
|
+
|
528
|
+
|
529
|
+
/**
|
530
|
+
* This function allows the bubble positioner object to be destroyed without
|
531
|
+
* creating memory leaks.
|
532
|
+
*/
|
533
|
+
MichaelHintbuble.BubblePositioner.prototype.finalize = function() {
|
534
|
+
this._target = null;
|
535
|
+
this._element = null;
|
536
|
+
this._axis = null;
|
537
|
+
this._position = null;
|
538
|
+
};
|
539
|
+
|
540
|
+
|
541
|
+
/**
|
542
|
+
* This function positions the element relative to the target according to the
|
543
|
+
* position value supplied. Invalid position values are ignored. If the new
|
544
|
+
* position runs off the viewport, the complement is tried. If that fails too,
|
545
|
+
* it gives up and does what was asked.
|
546
|
+
*
|
547
|
+
* @param {String} position the desired relative position of the element to the
|
548
|
+
* target.
|
549
|
+
*/
|
550
|
+
MichaelHintbuble.BubblePositioner.prototype.setPosition = function(position) {
|
551
|
+
var axis = MichaelHintbuble.BubblePositioner.AXIS_MAP[position];
|
552
|
+
if (axis) {
|
553
|
+
this._setPosition(position);
|
554
|
+
if (!this._isElementWithinViewport()) {
|
555
|
+
this._setPosition(MichaelHintbuble.BubblePositioner.COMPLEMENTS[position]);
|
556
|
+
if (!this._isElementWithinViewport()) {
|
557
|
+
this._setPosition(position);
|
558
|
+
}
|
559
|
+
}
|
560
|
+
}
|
561
|
+
};
|
562
|
+
|
563
|
+
|
564
|
+
/**
|
565
|
+
* This function returns a string representation of the current logical positioning that
|
566
|
+
* can be used as a stylesheet class for physical positioning.
|
567
|
+
*
|
568
|
+
* @returns {String} a styleclass name appropriate for the current position.
|
569
|
+
*/
|
570
|
+
MichaelHintbuble.BubblePositioner.prototype.styleClassForPosition = function() {
|
571
|
+
return this._position.toLowerCase();
|
572
|
+
};
|