Subversion Repositories Applications.papyrus

Rev

Rev 1372 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
1265 jp_milcent 1
/** $Id: domTT.js,v 1.1 2007-03-16 12:39:35 jp_milcent Exp $ */
2
// {{{ license
3
 
4
/*
5
 * Copyright 2002-2005 Dan Allen, Mojavelinux.com (dan.allen@mojavelinux.com)
6
 *
7
 * Licensed under the Apache License, Version 2.0 (the "License");
8
 * you may not use this file except in compliance with the License.
9
 * You may obtain a copy of the License at
10
 *
11
 *      http://www.apache.org/licenses/LICENSE-2.0
12
 *
13
 * Unless required by applicable law or agreed to in writing, software
14
 * distributed under the License is distributed on an "AS IS" BASIS,
15
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
 * See the License for the specific language governing permissions and
17
 * limitations under the License.
18
 */
19
 
20
// }}}
21
// {{{ intro
22
 
23
/**
24
 * Title: DOM Tooltip Library
25
 * Version: 0.7.3
26
 *
27
 * Summary:
28
 * Allows developers to add custom tooltips to the webpages.  Tooltips are
29
 * generated using the domTT_activate() function and customized by setting
30
 * a handful of options.
31
 *
32
 * Maintainer: Dan Allen <dan.allen@mojavelinux.com>
33
 * Contributors:
34
 * 		Josh Gross <josh@jportalhome.com>
35
 *		Jason Rust <jason@rustyparts.com>
36
 *
37
 * License: Apache 2.0
38
 * However, if you use this library, you earn the position of official bug
39
 * reporter :) Please post questions or problem reports to the newsgroup:
40
 *
41
 *   http://groups-beta.google.com/group/dom-tooltip
42
 *
43
 * If you are doing this for commercial work, perhaps you could send me a few
44
 * Starbucks Coffee gift dollars or PayPal bucks to encourage future
45
 * developement (NOT REQUIRED).  E-mail me for my snail mail address.
46
 
47
 *
48
 * Homepage: http://www.mojavelinux.com/projects/domtooltip/
49
 *
50
 * Newsgroup: http://groups-beta.google.com/group/dom-tooltip
51
 *
52
 * Freshmeat Project: http://freshmeat.net/projects/domtt/?topic_id=92
53
 *
54
 * Updated: 2005/07/16
55
 *
56
 * Supported Browsers:
57
 * Mozilla (Gecko), IE 5.5+, IE on Mac, Safari, Konqueror, Opera 7
58
 *
59
 * Usage:
60
 * Please see the HOWTO documentation.
61
**/
62
 
63
// }}}
64
// {{{ settings (editable)
65
 
66
// IE mouse events seem to be off by 2 pixels
67
var domTT_offsetX = (domLib_isIE ? -2 : 0);
68
var domTT_offsetY = (domLib_isIE ? 4 : 2);
69
var domTT_direction = 'southeast';
70
var domTT_mouseHeight = domLib_isIE ? 13 : 19;
71
var domTT_closeLink = 'X';
72
var domTT_closeAction = 'hide';
73
var domTT_activateDelay = 500;
74
var domTT_maxWidth = false;
75
var domTT_styleClass = 'domTT';
76
var domTT_fade = 'neither';
77
var domTT_lifetime = 0;
78
var domTT_grid = 0;
79
var domTT_trailDelay = 200;
80
var domTT_useGlobalMousePosition = true;
81
var domTT_postponeActivation = false;
82
var domTT_tooltipIdPrefix = '[domTT]';
83
var domTT_screenEdgeDetection = true;
84
var domTT_screenEdgePadding = 4;
85
var domTT_oneOnly = false;
86
var domTT_cloneNodes = false;
87
var domTT_detectCollisions = true;
88
var domTT_bannedTags = ['OPTION'];
89
var domTT_draggable = false;
90
if (typeof(domTT_dragEnabled) == 'undefined')
91
{
92
	domTT_dragEnabled = false;
93
}
94
 
95
// }}}
96
// {{{ globals (DO NOT EDIT)
97
 
98
var domTT_predefined = new Hash();
99
// tooltips are keyed on both the tip id and the owner id,
100
// since events can originate on either object
101
var domTT_tooltips = new Hash();
102
var domTT_lastOpened = 0;
103
var domTT_documentLoaded = false;
104
var domTT_mousePosition = null;
105
 
106
// }}}
107
// {{{ document.onmousemove
108
 
109
if (domLib_useLibrary && domTT_useGlobalMousePosition)
110
{
111
	document.onmousemove = function(in_event)
112
	{
113
		if (typeof(in_event) == 'undefined') { in_event = window.event; }
114
 
115
		domTT_mousePosition = domLib_getEventPosition(in_event);
116
		if (domTT_dragEnabled && domTT_dragMouseDown)
117
		{
118
			domTT_dragUpdate(in_event);
119
		}
120
	}
121
}
122
 
123
// }}}
124
// {{{ domTT_activate()
125
 
126
function domTT_activate(in_this, in_event)
127
{
128
	if (!domLib_useLibrary || (domTT_postponeActivation && !domTT_documentLoaded)) { return false; }
129
 
130
	// make sure in_event is set (for IE, some cases we have to use window.event)
131
	if (typeof(in_event) == 'undefined') { in_event = window.event;	}
132
 
133
	// don't allow tooltips on banned tags (such as OPTION)
134
	if (in_event != null) {
135
		var target = in_event.srcElement ? in_event.srcElement : in_event.target;
136
		if (target != null && (',' + domTT_bannedTags.join(',') + ',').indexOf(',' + target.tagName + ',') != -1)
137
		{
138
			return false;
139
		}
140
	}
141
 
142
	var owner = document.body;
143
	// we have an active event so get the owner
144
	if (in_event != null && in_event.type.match(/key|mouse|click|contextmenu/i))
145
	{
146
		// make sure we have nothing higher than the body element
147
		if (in_this.nodeType && in_this.nodeType != document.DOCUMENT_NODE)
148
		{
149
			owner = in_this;
150
		}
151
	}
152
	// non active event (make sure we were passed a string id)
153
	else
154
	{
155
		if (typeof(in_this) != 'object' && !(owner = domTT_tooltips.get(in_this)))
156
		{
157
			// NOTE: two steps to avoid "flashing" in gecko
158
			var embryo = document.createElement('div');
159
			owner = document.body.appendChild(embryo);
160
			owner.style.display = 'none';
161
			owner.id = in_this;
162
		}
163
	}
164
 
165
	// make sure the owner has a unique id
166
	if (!owner.id)
167
	{
168
		owner.id = '__autoId' + domLib_autoId++;
169
	}
170
 
171
	// see if we should only be opening one tip at a time
172
	// NOTE: this is not "perfect" yet since it really steps on any other
173
	// tip working on fade out or delayed close, but it get's the job done
174
	if (domTT_oneOnly && domTT_lastOpened)
175
	{
176
		domTT_deactivate(domTT_lastOpened);
177
	}
178
 
179
	domTT_lastOpened = owner.id;
180
 
181
	var tooltip = domTT_tooltips.get(owner.id);
182
	if (tooltip)
183
	{
184
		if (tooltip.get('eventType') != in_event.type)
185
		{
186
			if (tooltip.get('type') == 'greasy')
187
			{
188
				tooltip.set('closeAction', 'destroy');
189
				domTT_deactivate(owner.id);
190
			}
191
			else if (tooltip.get('status') != 'inactive')
192
			{
193
				return owner.id;
194
			}
195
		}
196
		else
197
		{
198
			if (tooltip.get('status') == 'inactive')
199
			{
200
				tooltip.set('status', 'pending');
201
				tooltip.set('activateTimeout', domLib_setTimeout(domTT_runShow, tooltip.get('delay'), [owner.id, in_event]));
202
 
203
				return owner.id;
204
			}
205
			// either pending or active, let it be
206
			else
207
			{
208
				return owner.id;
209
			}
210
		}
211
	}
212
 
213
	// setup the default options hash
214
	var options = new Hash(
215
		'caption',		'',
216
		'content',		'',
217
		'clearMouse',	true,
218
		'closeAction',	domTT_closeAction,
219
		'closeLink',	domTT_closeLink,
220
		'delay',		domTT_activateDelay,
221
		'direction',	domTT_direction,
222
		'draggable',	domTT_draggable,
223
		'fade',			domTT_fade,
224
		'fadeMax',		100,
225
		'grid',			domTT_grid,
226
		'id',			domTT_tooltipIdPrefix + owner.id,
227
		'inframe',		false,
228
		'lifetime',		domTT_lifetime,
229
		'offsetX',		domTT_offsetX,
230
		'offsetY',		domTT_offsetY,
231
		'parent',		document.body,
232
		'position',		'absolute',
233
		'styleClass',	domTT_styleClass,
234
		'type',			'greasy',
235
		'trail',		false,
236
		'lazy',			false
237
	);
238
 
239
	// load in the options from the function call
240
	for (var i = 2; i < arguments.length; i += 2)
241
	{
242
		// load in predefined
243
		if (arguments[i] == 'predefined')
244
		{
245
			var predefinedOptions = domTT_predefined.get(arguments[i + 1]);
246
			for (var j in predefinedOptions.elementData)
247
			{
248
				options.set(j, predefinedOptions.get(j));
249
			}
250
		}
251
		// set option
252
		else
253
		{
254
			options.set(arguments[i], arguments[i + 1]);
255
		}
256
	}
257
 
258
	options.set('eventType', in_event != null ? in_event.type : null);
259
 
260
	// immediately set the status text if provided
261
	if (options.has('statusText'))
262
	{
263
		try { window.status = options.get('statusText'); } catch(e) {}
264
	}
265
 
266
	// if we didn't give content...assume we just wanted to change the status and return
267
	if (!options.has('content') || options.get('content') == '' || options.get('content') == null)
268
	{
269
		if (typeof(owner.onmouseout) != 'function')
270
		{
271
			owner.onmouseout = function(in_event) { domTT_mouseout(this, in_event); };
272
		}
273
 
274
		return owner.id;
275
	}
276
 
277
	options.set('owner', owner);
278
 
279
	domTT_create(options);
280
 
281
	// determine the show delay
282
	options.set('delay', (in_event != null && in_event.type.match(/click|mousedown|contextmenu/i)) ? 0 : parseInt(options.get('delay')));
283
	domTT_tooltips.set(owner.id, options);
284
	domTT_tooltips.set(options.get('id'), options);
285
	options.set('status', 'pending');
286
	options.set('activateTimeout', domLib_setTimeout(domTT_runShow, options.get('delay'), [owner.id, in_event]));
287
 
288
	return owner.id;
289
}
290
 
291
// }}}
292
// {{{ domTT_create()
293
 
294
function domTT_create(in_options)
295
{
296
	var tipOwner = in_options.get('owner');
297
	var parentObj = in_options.get('parent');
298
	var parentDoc = parentObj.ownerDocument || parentObj.document;
299
 
300
	// create the tooltip and hide it
301
	// NOTE: two steps to avoid "flashing" in gecko
302
	var embryo = parentDoc.createElement('div');
303
	var tipObj = parentObj.appendChild(embryo);
304
	tipObj.style.position = 'absolute';
305
	tipObj.style.left = '0px';
306
	tipObj.style.top = '0px';
307
	tipObj.style.visibility = 'hidden';
308
	tipObj.id = in_options.get('id');
309
	tipObj.className = in_options.get('styleClass');
310
 
311
	var contentBlock;
312
	var tableLayout = false;
313
 
314
	if (in_options.get('caption') || (in_options.get('type') == 'sticky' && in_options.get('caption') !== false))
315
	{
316
		tableLayout = true;
317
		// layout the tip with a hidden formatting table
318
		var tipLayoutTable = tipObj.appendChild(parentDoc.createElement('table'));
319
		tipLayoutTable.style.borderCollapse = 'collapse';
320
		if (domLib_isKHTML)
321
		{
322
			tipLayoutTable.cellSpacing = 0;
323
		}
324
 
325
		var tipLayoutTbody = tipLayoutTable.appendChild(parentDoc.createElement('tbody'));
326
 
327
		var numCaptionCells = 0;
328
		var captionRow = tipLayoutTbody.appendChild(parentDoc.createElement('tr'));
329
		var captionCell = captionRow.appendChild(parentDoc.createElement('td'));
330
		captionCell.style.padding = '0px';
331
		var caption = captionCell.appendChild(parentDoc.createElement('div'));
332
		caption.className = 'caption';
333
		if (domLib_isIE50)
334
		{
335
			caption.style.height = '100%';
336
		}
337
 
338
		if (in_options.get('caption').nodeType)
339
		{
340
			caption.appendChild(domTT_cloneNodes ? in_options.get('caption').cloneNode(1) : in_options.get('caption'));
341
		}
342
		else
343
		{
344
			caption.innerHTML = in_options.get('caption');
345
		}
346
 
347
		if (in_options.get('type') == 'sticky')
348
		{
349
			var numCaptionCells = 2;
350
			var closeLinkCell = captionRow.appendChild(parentDoc.createElement('td'));
351
			closeLinkCell.style.padding = '0px';
352
			var closeLink = closeLinkCell.appendChild(parentDoc.createElement('div'));
353
			closeLink.className = 'caption';
354
			if (domLib_isIE50)
355
			{
356
				closeLink.style.height = '100%';
357
			}
358
 
359
			closeLink.style.textAlign = 'right';
360
			closeLink.style.cursor = domLib_stylePointer;
361
			// merge the styles of the two cells
362
			closeLink.style.borderLeftWidth = caption.style.borderRightWidth = '0px';
363
			closeLink.style.paddingLeft = caption.style.paddingRight = '0px';
364
			closeLink.style.marginLeft = caption.style.marginRight = '0px';
365
			if (in_options.get('closeLink').nodeType)
366
			{
367
				closeLink.appendChild(in_options.get('closeLink').cloneNode(1));
368
			}
369
			else
370
			{
371
				closeLink.innerHTML = in_options.get('closeLink');
372
			}
373
 
374
			closeLink.onclick = function()
375
			{
376
				domTT_deactivate(tipOwner.id);
377
			};
378
			closeLink.onmousedown = function(in_event)
379
			{
380
				if (typeof(in_event) == 'undefined') { in_event = window.event; }
381
				in_event.cancelBubble = true;
382
			};
383
			// MacIE has to have a newline at the end and must be made with createTextNode()
384
			if (domLib_isMacIE)
385
			{
386
				closeLinkCell.appendChild(parentDoc.createTextNode("\n"));
387
			}
388
		}
389
 
390
		// MacIE has to have a newline at the end and must be made with createTextNode()
391
		if (domLib_isMacIE)
392
		{
393
			captionCell.appendChild(parentDoc.createTextNode("\n"));
394
		}
395
 
396
		var contentRow = tipLayoutTbody.appendChild(parentDoc.createElement('tr'));
397
		var contentCell = contentRow.appendChild(parentDoc.createElement('td'));
398
		contentCell.style.padding = '0px';
399
		if (numCaptionCells)
400
		{
401
			if (domLib_isIE || domLib_isOpera)
402
			{
403
				contentCell.colSpan = numCaptionCells;
404
			}
405
			else
406
			{
407
				contentCell.setAttribute('colspan', numCaptionCells);
408
			}
409
		}
410
 
411
		contentBlock = contentCell.appendChild(parentDoc.createElement('div'));
412
		if (domLib_isIE50)
413
		{
414
			contentBlock.style.height = '100%';
415
		}
416
	}
417
	else
418
	{
419
		contentBlock = tipObj.appendChild(parentDoc.createElement('div'));
420
	}
421
 
422
	contentBlock.className = 'contents';
423
 
424
	var content = in_options.get('content');
425
	// allow content has a function to return the actual content
426
	if (typeof(content) == 'function') {
427
		content = content(in_options.get('id'));
428
	}
429
 
430
	if (content != null && content.nodeType)
431
	{
432
		contentBlock.appendChild(domTT_cloneNodes ? content.cloneNode(1) : content);
433
	}
434
	else
435
	{
436
		contentBlock.innerHTML = content;
437
	}
438
 
439
	// adjust the width if specified
440
	if (in_options.has('width'))
441
	{
442
		tipObj.style.width = parseInt(in_options.get('width')) + 'px';
443
	}
444
 
445
	// check if we are overridding the maxWidth
446
	// if the browser supports maxWidth, the global setting will be ignored (assume stylesheet)
447
	var maxWidth = domTT_maxWidth;
448
	if (in_options.has('maxWidth'))
449
	{
450
		if ((maxWidth = in_options.get('maxWidth')) === false)
451
		{
452
			tipObj.style.maxWidth = domLib_styleNoMaxWidth;
453
		}
454
		else
455
		{
456
			maxWidth = parseInt(in_options.get('maxWidth'));
457
			tipObj.style.maxWidth = maxWidth + 'px';
458
		}
459
	}
460
 
461
	// HACK: fix lack of maxWidth in CSS for KHTML and IE
462
	if (maxWidth !== false && (domLib_isIE || domLib_isKHTML) && tipObj.offsetWidth > maxWidth)
463
	{
464
		tipObj.style.width = maxWidth + 'px';
465
	}
466
 
467
	in_options.set('offsetWidth', tipObj.offsetWidth);
468
	in_options.set('offsetHeight', tipObj.offsetHeight);
469
 
470
	// konqueror miscalcuates the width of the containing div when using the layout table based on the
471
	// border size of the containing div
472
	if (domLib_isKonq && tableLayout && !tipObj.style.width)
473
	{
474
		var left = document.defaultView.getComputedStyle(tipObj, '').getPropertyValue('border-left-width');
475
		var right = document.defaultView.getComputedStyle(tipObj, '').getPropertyValue('border-right-width');
476
 
477
		left = left.substring(left.indexOf(':') + 2, left.indexOf(';'));
478
		right = right.substring(right.indexOf(':') + 2, right.indexOf(';'));
479
		var correction = 2 * ((left ? parseInt(left) : 0) + (right ? parseInt(right) : 0));
480
		tipObj.style.width = (tipObj.offsetWidth - correction) + 'px';
481
	}
482
 
483
	// if a width is not set on an absolutely positioned object, both IE and Opera
484
	// will attempt to wrap when it spills outside of body...we cannot have that
485
	if (domLib_isIE || domLib_isOpera)
486
	{
487
		if (!tipObj.style.width)
488
		{
489
			// HACK: the correction here is for a border
490
			tipObj.style.width = (tipObj.offsetWidth - 2) + 'px';
491
		}
492
 
493
		// HACK: the correction here is for a border
494
		tipObj.style.height = (tipObj.offsetHeight - 2) + 'px';
495
	}
496
 
497
	// store placement offsets from event position
498
	var offsetX, offsetY;
499
 
500
	// tooltip floats
501
	if (in_options.get('position') == 'absolute' && !(in_options.has('x') && in_options.has('y')))
502
	{
503
		// determine the offset relative to the pointer
504
		switch (in_options.get('direction'))
505
		{
506
			case 'northeast':
507
				offsetX = in_options.get('offsetX');
508
				offsetY = 0 - tipObj.offsetHeight - in_options.get('offsetY');
509
			break;
510
			case 'northwest':
511
				offsetX = 0 - tipObj.offsetWidth - in_options.get('offsetX');
512
				offsetY = 0 - tipObj.offsetHeight - in_options.get('offsetY');
513
			break;
514
			case 'north':
515
				offsetX = 0 - parseInt(tipObj.offsetWidth/2);
516
				offsetY = 0 - tipObj.offsetHeight - in_options.get('offsetY');
517
			break;
518
			case 'southwest':
519
				offsetX = 0 - tipObj.offsetWidth - in_options.get('offsetX');
520
				offsetY = in_options.get('offsetY');
521
			break;
522
			case 'southeast':
523
				offsetX = in_options.get('offsetX');
524
				offsetY = in_options.get('offsetY');
525
			break;
526
			case 'south':
527
				offsetX = 0 - parseInt(tipObj.offsetWidth/2);
528
				offsetY = in_options.get('offsetY');
529
			break;
530
		}
531
 
532
		// if we are in an iframe, get the offsets of the iframe in the parent document
533
		if (in_options.get('inframe'))
534
		{
535
			var iframeObj = domLib_getIFrameReference(window);
536
			if (iframeObj)
537
			{
538
				var frameOffsets = domLib_getOffsets(iframeObj);
539
				offsetX += frameOffsets.get('left');
540
				offsetY += frameOffsets.get('top');
541
			}
542
		}
543
	}
544
	// tooltip is fixed
545
	else
546
	{
547
		offsetX = 0;
548
		offsetY = 0;
549
		in_options.set('trail', false);
550
	}
551
 
552
	// set the direction-specific offsetX/Y
553
	in_options.set('offsetX', offsetX);
554
	in_options.set('offsetY', offsetY);
555
	if (in_options.get('clearMouse') && in_options.get('direction').indexOf('south') != -1)
556
	{
557
		in_options.set('mouseOffset', domTT_mouseHeight);
558
	}
559
	else
560
	{
561
		in_options.set('mouseOffset', 0);
562
	}
563
 
564
	if (domLib_canFade && typeof(Fadomatic) == 'function')
565
	{
566
		if (in_options.get('fade') != 'neither')
567
		{
568
			var fadeHandler = new Fadomatic(tipObj, 10, 0, 0, in_options.get('fadeMax'));
569
			in_options.set('fadeHandler', fadeHandler);
570
		}
571
	}
572
	else
573
	{
574
		in_options.set('fade', 'neither');
575
	}
576
 
577
	// setup mouse events
578
	if (in_options.get('trail') && typeof(tipOwner.onmousemove) != 'function')
579
	{
580
		tipOwner.onmousemove = function(in_event) { domTT_mousemove(this, in_event); };
581
	}
582
 
583
	if (typeof(tipOwner.onmouseout) != 'function')
584
	{
585
		tipOwner.onmouseout = function(in_event) { domTT_mouseout(this, in_event); };
586
	}
587
 
588
	if (in_options.get('type') == 'sticky')
589
	{
590
		if (in_options.get('position') == 'absolute' && domTT_dragEnabled && in_options.get('draggable'))
591
		{
592
			if (domLib_isIE)
593
			{
594
				captionRow.onselectstart = function() { return false; };
595
			}
596
 
597
			// setup drag
598
			captionRow.onmousedown = function(in_event) { domTT_dragStart(tipObj, in_event);  };
599
			captionRow.onmousemove = function(in_event) { domTT_dragUpdate(in_event); };
600
			captionRow.onmouseup = function() { domTT_dragStop(); };
601
		}
602
	}
603
	else if (in_options.get('type') == 'velcro')
604
	{
605
		/* can use once we have deactivateDelay
606
		tipObj.onmouseover = function(in_event)
607
		{
608
			if (typeof(in_event) == 'undefined') { in_event = window.event; }
609
			var tooltip = domTT_tooltips.get(tipObj.id);
610
			if (in_options.get('lifetime')) {
611
				domLib_clearTimeout(in_options.get('lifetimeTimeout');
612
			}
613
		};
614
		*/
615
		tipObj.onmouseout = function(in_event)
616
		{
617
			if (typeof(in_event) == 'undefined') { in_event = window.event; }
618
			if (!domLib_isDescendantOf(in_event[domLib_eventTo], tipObj, domTT_bannedTags)) {
619
				domTT_deactivate(tipOwner.id);
620
			}
621
		};
622
		// NOTE: this might interfere with links in the tip
623
		tipObj.onclick = function(in_event)
624
		{
625
			domTT_deactivate(tipOwner.id);
626
		};
627
	}
628
 
629
	if (in_options.get('position') == 'relative')
630
	{
631
		tipObj.style.position = 'relative';
632
	}
633
 
634
	in_options.set('node', tipObj);
635
	in_options.set('status', 'inactive');
636
}
637
 
638
// }}}
639
// {{{ domTT_show()
640
 
641
// in_id is either tip id or the owner id
642
function domTT_show(in_id, in_event)
643
{
644
 
645
	// should always find one since this call would be cancelled if tip was killed
646
	var tooltip = domTT_tooltips.get(in_id);
647
	var status = tooltip.get('status');
648
	var tipObj = tooltip.get('node');
649
 
650
	if (tooltip.get('position') == 'absolute')
651
	{
652
		var mouseX, mouseY;
653
 
654
		if (tooltip.has('x') && tooltip.has('y'))
655
		{
656
			mouseX = tooltip.get('x');
657
			mouseY = tooltip.get('y');
658
		}
659
		else if (!domTT_useGlobalMousePosition || domTT_mousePosition == null || status == 'active' || tooltip.get('delay') == 0)
660
		{
661
			var eventPosition = domLib_getEventPosition(in_event);
662
			var eventX = eventPosition.get('x');
663
			var eventY = eventPosition.get('y');
664
			if (tooltip.get('inframe'))
665
			{
666
				eventX -= eventPosition.get('scrollX');
667
				eventY -= eventPosition.get('scrollY');
668
			}
669
 
670
			// only move tip along requested trail axis when updating position
671
			if (status == 'active' && tooltip.get('trail') !== true)
672
			{
673
				var trail = tooltip.get('trail');
674
				if (trail == 'x')
675
				{
676
					mouseX = eventX;
677
					mouseY = tooltip.get('mouseY');
678
				}
679
				else if (trail == 'y')
680
				{
681
					mouseX = tooltip.get('mouseX');
682
					mouseY = eventY;
683
				}
684
			}
685
			else
686
			{
687
				mouseX = eventX;
688
				mouseY = eventY;
689
			}
690
		}
691
		else
692
		{
693
			mouseX = domTT_mousePosition.get('x');
694
			mouseY = domTT_mousePosition.get('y');
695
			if (tooltip.get('inframe'))
696
			{
697
				mouseX -= domTT_mousePosition.get('scrollX');
698
				mouseY -= domTT_mousePosition.get('scrollY');
699
			}
700
		}
701
 
702
		// we are using a grid for updates
703
		if (tooltip.get('grid'))
704
		{
705
			// if this is not a mousemove event or it is a mousemove event on an active tip and
706
			// the movement is bigger than the grid
707
			if (in_event.type != 'mousemove' || (status == 'active' && (Math.abs(tooltip.get('lastX') - mouseX) > tooltip.get('grid') || Math.abs(tooltip.get('lastY') - mouseY) > tooltip.get('grid'))))
708
			{
709
				tooltip.set('lastX', mouseX);
710
				tooltip.set('lastY', mouseY);
711
			}
712
			// did not satisfy the grid movement requirement
713
			else
714
			{
715
				return false;
716
			}
717
		}
718
 
719
		// mouseX and mouseY store the last acknowleged mouse position,
720
		// good for trailing on one axis
721
		tooltip.set('mouseX', mouseX);
722
		tooltip.set('mouseY', mouseY);
723
 
724
		var coordinates;
725
		if (domTT_screenEdgeDetection)
726
		{
727
			coordinates = domTT_correctEdgeBleed(
728
				tooltip.get('offsetWidth'),
729
				tooltip.get('offsetHeight'),
730
				mouseX,
731
				mouseY,
732
				tooltip.get('offsetX'),
733
				tooltip.get('offsetY'),
734
				tooltip.get('mouseOffset'),
735
				tooltip.get('inframe') ? window.parent : window
736
			);
737
		}
738
		else
739
		{
740
			coordinates = {
741
				'x' : mouseX + tooltip.get('offsetX'),
742
				'y' : mouseY + tooltip.get('offsetY') + tooltip.get('mouseOffset')
743
			};
744
		}
745
 
746
		// update the position
747
		tipObj.style.left = coordinates.x + 'px';
748
		tipObj.style.top = coordinates.y + 'px';
749
 
750
		// increase the tip zIndex so it goes over previously shown tips
751
		tipObj.style.zIndex = domLib_zIndex++;
752
	}
753
 
754
	// if tip is not active, active it now and check for a fade in
755
	if (status == 'pending')
756
	{
757
		// unhide the tooltip
758
		tooltip.set('status', 'active');
759
		tipObj.style.display = '';
760
		tipObj.style.visibility = 'visible';
761
 
762
		var fade = tooltip.get('fade');
763
		if (fade != 'neither')
764
		{
765
			var fadeHandler = tooltip.get('fadeHandler');
766
			if (fade == 'out' || fade == 'both')
767
			{
768
				fadeHandler.haltFade();
769
				if (fade == 'out')
770
				{
771
					fadeHandler.halt();
772
				}
773
			}
774
 
775
			if (fade == 'in' || fade == 'both')
776
			{
777
				fadeHandler.fadeIn();
778
			}
779
		}
780
 
781
		if (tooltip.get('type') == 'greasy' && tooltip.get('lifetime') != 0)
782
		{
783
			tooltip.set('lifetimeTimeout', domLib_setTimeout(domTT_runDeactivate, tooltip.get('lifetime'), [tipObj.id]));
784
		}
785
	}
786
 
787
	if (tooltip.get('position') == 'absolute' && domTT_detectCollisions)
788
	{
789
		// utilize original collision element cache
790
		domLib_detectCollisions(tipObj, false, true);
791
	}
792
}
793
 
794
// }}}
795
// {{{ domTT_close()
796
 
797
// in_handle can either be an child object of the tip, the tip id or the owner id
798
function domTT_close(in_handle)
799
{
800
	var id;
801
	if (typeof(in_handle) == 'object' && in_handle.nodeType)
802
	{
803
		var obj = in_handle;
804
		while (!obj.id || !domTT_tooltips.get(obj.id))
805
		{
806
			obj = obj.parentNode;
807
 
808
			if (obj.nodeType != document.ELEMENT_NODE) { return; }
809
		}
810
 
811
		id = obj.id;
812
	}
813
	else
814
	{
815
		id = in_handle;
816
	}
817
 
818
	domTT_deactivate(id);
819
}
820
 
821
// }}}
822
// {{{ domTT_closeAll()
823
 
824
// run through the tooltips and close them all
825
function domTT_closeAll()
826
{
827
	// NOTE: this will iterate 2x # of tooltips
828
	for (var id in domTT_tooltips.elementData) {
829
		domTT_close(id);
830
	}
831
}
832
 
833
// }}}
834
// {{{ domTT_deactivate()
835
 
836
// in_id is either the tip id or the owner id
837
function domTT_deactivate(in_id)
838
{
839
	var tooltip = domTT_tooltips.get(in_id);
840
	if (tooltip)
841
	{
842
		var status = tooltip.get('status');
843
		if (status == 'pending')
844
		{
845
			// cancel the creation of this tip if it is still pending
846
			domLib_clearTimeout(tooltip.get('activateTimeout'));
847
			tooltip.set('status', 'inactive');
848
		}
849
		else if (status == 'active')
850
		{
851
			if (tooltip.get('lifetime'))
852
			{
853
				domLib_clearTimeout(tooltip.get('lifetimeTimeout'));
854
			}
855
 
856
			var tipObj = tooltip.get('node');
857
			if (tooltip.get('closeAction') == 'hide')
858
			{
859
				var fade = tooltip.get('fade');
860
				if (fade != 'neither')
861
				{
862
					var fadeHandler = tooltip.get('fadeHandler');
863
					if (fade == 'out' || fade == 'both')
864
					{
865
						fadeHandler.fadeOut();
866
					}
867
					else
868
					{
869
						fadeHandler.hide();
870
					}
871
				}
872
				else
873
				{
874
					tipObj.style.display = 'none';
875
				}
876
			}
877
			else
878
			{
879
				tooltip.get('parent').removeChild(tipObj);
880
				domTT_tooltips.remove(tooltip.get('owner').id);
881
				domTT_tooltips.remove(tooltip.get('id'));
882
			}
883
 
884
			tooltip.set('status', 'inactive');
885
			if (domTT_detectCollisions) {
886
				// unhide all of the selects that are owned by this object
887
				// utilize original collision element cache
888
				domLib_detectCollisions(tipObj, true, true);
889
			}
890
		}
891
	}
892
}
893
 
894
// }}}
895
// {{{ domTT_mouseout()
896
 
897
function domTT_mouseout(in_owner, in_event)
898
{
899
	if (!domLib_useLibrary) { return false; }
900
 
901
	if (typeof(in_event) == 'undefined') { in_event = window.event;	}
902
 
903
	var toChild = domLib_isDescendantOf(in_event[domLib_eventTo], in_owner, domTT_bannedTags);
904
	var tooltip = domTT_tooltips.get(in_owner.id);
905
	if (tooltip && (tooltip.get('type') == 'greasy' || tooltip.get('status') != 'active'))
906
	{
907
		// deactivate tip if exists and we moved away from the owner
908
		if (!toChild)
909
		{
910
			domTT_deactivate(in_owner.id);
911
			try { window.status = window.defaultStatus; } catch(e) {}
912
		}
913
	}
914
	else if (!toChild)
915
	{
916
		try { window.status = window.defaultStatus; } catch(e) {}
917
	}
918
}
919
 
920
// }}}
921
// {{{ domTT_mousemove()
922
 
923
function domTT_mousemove(in_owner, in_event)
924
{
925
	if (!domLib_useLibrary) { return false; }
926
 
927
	if (typeof(in_event) == 'undefined') { in_event = window.event;	}
928
 
929
	var tooltip = domTT_tooltips.get(in_owner.id);
930
	if (tooltip && tooltip.get('trail') && tooltip.get('status') == 'active')
931
	{
932
		// see if we are trailing lazy
933
		if (tooltip.get('lazy'))
934
		{
935
			domLib_setTimeout(domTT_runShow, domTT_trailDelay, [in_owner.id, in_event]);
936
		}
937
		else
938
		{
939
			domTT_show(in_owner.id, in_event);
940
		}
941
	}
942
}
943
 
944
// }}}
945
// {{{ domTT_addPredefined()
946
 
947
function domTT_addPredefined(in_id)
948
{
949
	var options = new Hash();
950
	for (var i = 1; i < arguments.length; i += 2)
951
	{
952
		options.set(arguments[i], arguments[i + 1]);
953
	}
954
 
955
	domTT_predefined.set(in_id, options);
956
}
957
 
958
// }}}
959
// {{{ domTT_correctEdgeBleed()
960
 
961
function domTT_correctEdgeBleed(in_width, in_height, in_x, in_y, in_offsetX, in_offsetY, in_mouseOffset, in_window)
962
{
963
	var win, doc;
964
	var bleedRight, bleedBottom;
965
	var pageHeight, pageWidth, pageYOffset, pageXOffset;
966
 
967
	var x = in_x + in_offsetX;
968
	var y = in_y + in_offsetY + in_mouseOffset;
969
 
970
	win = (typeof(in_window) == 'undefined' ? window : in_window);
971
 
972
	// Gecko and IE swaps values of clientHeight, clientWidth properties when
973
	// in standards compliance mode from documentElement to document.body
974
	doc = ((domLib_standardsMode && (domLib_isIE || domLib_isGecko)) ? win.document.documentElement : win.document.body);
975
 
976
	// for IE in compliance mode
977
	if (domLib_isIE)
978
	{
979
		pageHeight = doc.clientHeight;
980
		pageWidth = doc.clientWidth;
981
		pageYOffset = doc.scrollTop;
982
		pageXOffset = doc.scrollLeft;
983
	}
984
	else
985
	{
986
		pageHeight = doc.clientHeight;
987
		pageWidth = doc.clientWidth;
988
 
989
		if (domLib_isKHTML)
990
		{
991
			pageHeight = win.innerHeight;
992
		}
993
 
994
		pageYOffset = win.pageYOffset;
995
		pageXOffset = win.pageXOffset;
996
	}
997
 
998
	// we are bleeding off the right, move tip over to stay on page
999
	// logic: take x position, add width and subtract from effective page width
1000
	if ((bleedRight = (x - pageXOffset) + in_width - (pageWidth - domTT_screenEdgePadding)) > 0)
1001
	{
1002
		x -= bleedRight;
1003
	}
1004
 
1005
	// we are bleeding to the left, move tip over to stay on page
1006
	// if tip doesn't fit, we will go back to bleeding off the right
1007
	// logic: take x position and check if less than edge padding
1008
	if ((x - pageXOffset) < domTT_screenEdgePadding)
1009
	{
1010
		x = domTT_screenEdgePadding + pageXOffset;
1011
	}
1012
 
1013
	// if we are bleeding off the bottom, flip to north
1014
	// logic: take y position, add height and subtract from effective page height
1015
	if ((bleedBottom = (y - pageYOffset) + in_height - (pageHeight - domTT_screenEdgePadding)) > 0)
1016
	{
1017
		y = in_y - in_height - in_offsetY;
1018
	}
1019
 
1020
	// if we are bleeding off the top, flip to south
1021
	// if tip doesn't fit, we will go back to bleeding off the bottom
1022
	// logic: take y position and check if less than edge padding
1023
	if ((y - pageYOffset) < domTT_screenEdgePadding)
1024
	{
1025
		y = in_y + domTT_mouseHeight + in_offsetY;
1026
	}
1027
 
1028
	return {'x' : x, 'y' : y};
1029
}
1030
 
1031
// }}}
1032
// {{{ domTT_isActive()
1033
 
1034
// in_id is either the tip id or the owner id
1035
function domTT_isActive(in_id)
1036
{
1037
	var tooltip = domTT_tooltips.get(in_id);
1038
	if (!tooltip || tooltip.get('status') != 'active')
1039
	{
1040
		return false;
1041
	}
1042
	else
1043
	{
1044
		return true;
1045
	}
1046
}
1047
 
1048
// }}}
1049
// {{{ domTT_runXXX()
1050
 
1051
// All of these domMenu_runXXX() methods are used by the event handling sections to
1052
// avoid the circular memory leaks caused by inner functions
1053
function domTT_runDeactivate(args) { domTT_deactivate(args[0]); }
1054
function domTT_runShow(args) { domTT_show(args[0], args[1]); }
1055
 
1056
// }}}
1057
// {{{ domTT_replaceTitles()
1058
 
1059
function domTT_replaceTitles(in_decorator)
1060
{
1061
	var elements = domLib_getElementsByClass('tooltip');
1062
	for (var i = 0; i < elements.length; i++)
1063
	{
1064
		if (elements[i].title)
1065
		{
1066
			var content;
1067
			if (typeof(in_decorator) == 'function')
1068
			{
1069
				content = in_decorator(elements[i]);
1070
			}
1071
			else
1072
			{
1073
				content = elements[i].title;
1074
			}
1075
 
1076
			content = content.replace(new RegExp('\'', 'g'), '\\\'');
1077
			elements[i].onmouseover = new Function('in_event', "domTT_activate(this, in_event, 'content', '" + content + "')");
1078
			elements[i].title = '';
1079
		}
1080
	}
1081
}
1082
 
1083
// }}}
1084
// {{{ domTT_update()
1085
 
1086
// Allow authors to update the contents of existing tips using the DOM
1087
// Unfortunately, the tip must already exist, or else no work is done.
1088
// TODO: make getting at content or caption cleaner
1089
function domTT_update(handle, content, type)
1090
{
1091
	// type defaults to 'content', can also be 'caption'
1092
	if (typeof(type) == 'undefined')
1093
	{
1094
		type = 'content';
1095
	}
1096
 
1097
	var tip = domTT_tooltips.get(handle);
1098
	if (!tip)
1099
	{
1100
		return;
1101
	}
1102
 
1103
	var tipObj = tip.get('node');
1104
	var updateNode;
1105
	if (type == 'content')
1106
	{
1107
		// <div class="contents">...
1108
		updateNode = tipObj.firstChild;
1109
		if (updateNode.className != 'contents')
1110
		{
1111
			// <table><tbody><tr>...</tr><tr><td><div class="contents">...
1112
			updateNode = updateNode.firstChild.firstChild.nextSibling.firstChild.firstChild;
1113
		}
1114
	}
1115
	else
1116
	{
1117
		updateNode = tipObj.firstChild;
1118
		if (updateNode.className == 'contents')
1119
		{
1120
			// missing caption
1121
			return;
1122
		}
1123
 
1124
		// <table><tbody><tr><td><div class="caption">...
1125
		updateNode = updateNode.firstChild.firstChild.firstChild.firstChild;
1126
	}
1127
 
1128
	// TODO: allow for a DOM node as content
1129
	updateNode.innerHTML = content;
1130
}
1131
 
1132
// }}}