Subversion Repositories Applications.papyrus

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
1265 jp_milcent 1
/** $Id: domLib.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 Library Core
25
 * Version: 0.70
26
 *
27
 * Summary:
28
 * A set of commonly used functions that make it easier to create javascript
29
 * applications that rely on the DOM.
30
 *
31
 * Updated: 2005/05/17
32
 *
33
 * Maintainer: Dan Allen <dan.allen@mojavelinux.com>
34
 * Maintainer: Jason Rust <jrust@rustyparts.com>
35
 *
36
 * License: Apache 2.0
37
 */
38
 
39
// }}}
40
// {{{ global constants (DO NOT EDIT)
41
 
42
// -- Browser Detection --
43
var domLib_userAgent = navigator.userAgent.toLowerCase();
44
var domLib_isMac = navigator.appVersion.indexOf('Mac') != -1;
45
var domLib_isWin = domLib_userAgent.indexOf('windows') != -1;
46
// NOTE: could use window.opera for detecting Opera
47
var domLib_isOpera = domLib_userAgent.indexOf('opera') != -1;
48
var domLib_isOpera7up = domLib_userAgent.match(/opera.(7|8)/i);
49
var domLib_isSafari = domLib_userAgent.indexOf('safari') != -1;
50
var domLib_isKonq = domLib_userAgent.indexOf('konqueror') != -1;
51
// Both konqueror and safari use the khtml rendering engine
52
var domLib_isKHTML = (domLib_isKonq || domLib_isSafari || domLib_userAgent.indexOf('khtml') != -1);
53
var domLib_isIE = (!domLib_isKHTML && !domLib_isOpera && (domLib_userAgent.indexOf('msie 5') != -1 || domLib_userAgent.indexOf('msie 6') != -1 || domLib_userAgent.indexOf('msie 7') != -1));
54
var domLib_isIE5up = domLib_isIE;
55
var domLib_isIE50 = (domLib_isIE && domLib_userAgent.indexOf('msie 5.0') != -1);
56
var domLib_isIE55 = (domLib_isIE && domLib_userAgent.indexOf('msie 5.5') != -1);
57
var domLib_isIE5 = (domLib_isIE50 || domLib_isIE55);
58
// safari and konq may use string "khtml, like gecko", so check for destinctive /
59
var domLib_isGecko = domLib_userAgent.indexOf('gecko/') != -1;
60
var domLib_isMacIE = (domLib_isIE && domLib_isMac);
61
var domLib_isIE55up = domLib_isIE5up && !domLib_isIE50 && !domLib_isMacIE;
62
var domLib_isIE6up = domLib_isIE55up && !domLib_isIE55;
63
 
64
// -- Browser Abilities --
65
var domLib_standardsMode = (document.compatMode && document.compatMode == 'CSS1Compat');
66
var domLib_useLibrary = (domLib_isOpera7up || domLib_isKHTML || domLib_isIE5up || domLib_isGecko || domLib_isMacIE || document.defaultView);
67
// fixed in Konq3.2
68
var domLib_hasBrokenTimeout = (domLib_isMacIE || (domLib_isKonq && domLib_userAgent.match(/konqueror\/3.([2-9])/) == null));
69
var domLib_canFade = (domLib_isGecko || domLib_isIE || domLib_isSafari || domLib_isOpera);
70
var domLib_canDrawOverSelect = (domLib_isMac || domLib_isOpera || domLib_isGecko);
71
var domLib_canDrawOverFlash = (domLib_isMac || domLib_isWin);
72
 
73
// -- Event Variables --
74
var domLib_eventTarget = domLib_isIE ? 'srcElement' : 'currentTarget';
75
var domLib_eventButton = domLib_isIE ? 'button' : 'which';
76
var domLib_eventTo = domLib_isIE ? 'toElement' : 'relatedTarget';
77
var domLib_stylePointer = domLib_isIE ? 'hand' : 'pointer';
78
// NOTE: a bug exists in Opera that prevents maxWidth from being set to 'none', so we make it huge
79
var domLib_styleNoMaxWidth = domLib_isOpera ? '10000px' : 'none';
80
var domLib_hidePosition = '-1000px';
81
var domLib_scrollbarWidth = 14;
82
var domLib_autoId = 1;
83
var domLib_zIndex = 100;
84
 
85
// -- Detection --
86
var domLib_collisionElements;
87
var domLib_collisionsCached = false;
88
 
89
var domLib_timeoutStateId = 0;
90
var domLib_timeoutStates = new Hash();
91
 
92
// }}}
93
// {{{ DOM enhancements
94
 
95
if (!document.ELEMENT_NODE)
96
{
97
	document.ELEMENT_NODE = 1;
98
	document.ATTRIBUTE_NODE = 2;
99
	document.TEXT_NODE = 3;
100
	document.DOCUMENT_NODE = 9;
101
	document.DOCUMENT_FRAGMENT_NODE = 11;
102
}
103
 
104
function domLib_clone(obj)
105
{
106
	var copy = {};
107
	for (var i in obj)
108
	{
109
		var value = obj[i];
110
		try
111
		{
112
			if (value != null && typeof(value) == 'object' && value != window && !value.nodeType)
113
			{
114
				copy[i] = domLib_clone(value);
115
			}
116
			else
117
			{
118
				copy[i] = value;
119
			}
120
		}
121
		catch(e)
122
		{
123
			copy[i] = value;
124
		}
125
	}
126
 
127
	return copy;
128
}
129
 
130
// }}}
131
// {{{ class Hash()
132
 
133
function Hash()
134
{
135
	this.length = 0;
136
	this.numericLength = 0;
137
	this.elementData = [];
138
	for (var i = 0; i < arguments.length; i += 2)
139
	{
140
		if (typeof(arguments[i + 1]) != 'undefined')
141
		{
142
			this.elementData[arguments[i]] = arguments[i + 1];
143
			this.length++;
144
			if (arguments[i] == parseInt(arguments[i]))
145
			{
146
				this.numericLength++;
147
			}
148
		}
149
	}
150
}
151
 
152
// using prototype as opposed to inner functions saves on memory
153
Hash.prototype.get = function(in_key)
154
{
155
	if (typeof(this.elementData[in_key]) != 'undefined') {
156
		return this.elementData[in_key];
157
	}
158
 
159
	return null;
160
}
161
 
162
Hash.prototype.set = function(in_key, in_value)
163
{
164
	if (typeof(in_value) != 'undefined')
165
	{
166
		if (typeof(this.elementData[in_key]) == 'undefined')
167
		{
168
			this.length++;
169
			if (in_key == parseInt(in_key))
170
			{
171
				this.numericLength++;
172
			}
173
		}
174
 
175
		return this.elementData[in_key] = in_value;
176
	}
177
 
178
	return false;
179
}
180
 
181
Hash.prototype.remove = function(in_key)
182
{
183
	var tmp_value;
184
	if (typeof(this.elementData[in_key]) != 'undefined')
185
	{
186
		this.length--;
187
		if (in_key == parseInt(in_key))
188
		{
189
			this.numericLength--;
190
		}
191
 
192
		tmp_value = this.elementData[in_key];
193
		delete this.elementData[in_key];
194
	}
195
 
196
	return tmp_value;
197
}
198
 
199
Hash.prototype.size = function()
200
{
201
	return this.length;
202
}
203
 
204
Hash.prototype.has = function(in_key)
205
{
206
	return typeof(this.elementData[in_key]) != 'undefined';
207
}
208
 
209
Hash.prototype.find = function(in_obj)
210
{
211
	for (var tmp_key in this.elementData)
212
	{
213
		if (this.elementData[tmp_key] == in_obj)
214
		{
215
			return tmp_key;
216
		}
217
	}
218
 
219
	return null;
220
}
221
 
222
Hash.prototype.merge = function(in_hash)
223
{
224
	for (var tmp_key in in_hash.elementData)
225
	{
226
		if (typeof(this.elementData[tmp_key]) == 'undefined')
227
		{
228
			this.length++;
229
			if (tmp_key == parseInt(tmp_key))
230
			{
231
				this.numericLength++;
232
			}
233
		}
234
 
235
		this.elementData[tmp_key] = in_hash.elementData[tmp_key];
236
	}
237
}
238
 
239
Hash.prototype.compare = function(in_hash)
240
{
241
	if (this.length != in_hash.length)
242
	{
243
		return false;
244
	}
245
 
246
	for (var tmp_key in this.elementData)
247
	{
248
		if (this.elementData[tmp_key] != in_hash.elementData[tmp_key])
249
		{
250
			return false;
251
		}
252
	}
253
 
254
	return true;
255
}
256
 
257
// }}}
258
// {{{ domLib_isDescendantOf()
259
 
260
function domLib_isDescendantOf(in_object, in_ancestor, in_bannedTags)
261
{
262
	if (in_object == null)
263
	{
264
		return false;
265
	}
266
 
267
	if (in_object == in_ancestor)
268
	{
269
		return true;
270
	}
271
 
272
	if (typeof(in_bannedTags) != 'undefined' &&
273
		(',' + in_bannedTags.join(',') + ',').indexOf(',' + in_object.tagName + ',') != -1)
274
	{
275
		return false;
276
	}
277
 
278
	while (in_object != document.documentElement)
279
	{
280
		try
281
		{
282
			if ((tmp_object = in_object.offsetParent) && tmp_object == in_ancestor)
283
			{
284
				return true;
285
			}
286
			else if ((tmp_object = in_object.parentNode) == in_ancestor)
287
			{
288
				return true;
289
			}
290
			else
291
			{
292
				in_object = tmp_object;
293
			}
294
		}
295
		// in case we get some wierd error, assume we left the building
296
		catch(e)
297
		{
298
			return false;
299
		}
300
	}
301
 
302
	return false;
303
}
304
 
305
// }}}
306
// {{{ domLib_detectCollisions()
307
 
308
/**
309
 * For any given target element, determine if elements on the page
310
 * are colliding with it that do not obey the rules of z-index.
311
 */
312
function domLib_detectCollisions(in_object, in_recover, in_useCache)
313
{
314
	// the reason for the cache is that if the root menu is built before
315
	// the page is done loading, then it might not find all the elements.
316
	// so really the only time you don't use cache is when building the
317
	// menu as part of the page load
318
	if (!domLib_collisionsCached)
319
	{
320
		var tags = [];
321
 
322
		if (!domLib_canDrawOverFlash)
323
		{
324
			tags[tags.length] = 'object';
325
		}
326
 
327
		if (!domLib_canDrawOverSelect)
328
		{
329
			tags[tags.length] = 'select';
330
		}
331
 
332
		domLib_collisionElements = domLib_getElementsByTagNames(tags, true);
333
		domLib_collisionsCached = in_useCache;
334
	}
335
 
336
	// if we don't have a tip, then unhide selects
337
	if (in_recover)
338
	{
339
		for (var cnt = 0; cnt < domLib_collisionElements.length; cnt++)
340
		{
341
			var thisElement = domLib_collisionElements[cnt];
342
 
343
			if (!thisElement.hideList)
344
			{
345
				thisElement.hideList = new Hash();
346
			}
347
 
348
			thisElement.hideList.remove(in_object.id);
349
			if (!thisElement.hideList.length)
350
			{
351
				domLib_collisionElements[cnt].style.visibility = 'visible';
352
				if (domLib_isKonq)
353
				{
354
					domLib_collisionElements[cnt].style.display = '';
355
				}
356
			}
357
		}
358
 
359
		return;
360
	}
361
	else if (domLib_collisionElements.length == 0)
362
	{
363
		return;
364
	}
365
 
366
	// okay, we have a tip, so hunt and destroy
367
	var objectOffsets = domLib_getOffsets(in_object);
368
 
369
	for (var cnt = 0; cnt < domLib_collisionElements.length; cnt++)
370
	{
371
		var thisElement = domLib_collisionElements[cnt];
372
 
373
		// if collision element is in active element, move on
374
		// WARNING: is this too costly?
375
		if (domLib_isDescendantOf(thisElement, in_object))
376
		{
377
			continue;
378
		}
379
 
380
		// konqueror only has trouble with multirow selects
381
		if (domLib_isKonq &&
382
			thisElement.tagName == 'SELECT' &&
383
			(thisElement.size <= 1 && !thisElement.multiple))
384
		{
385
			continue;
386
		}
387
 
388
		if (!thisElement.hideList)
389
		{
390
			thisElement.hideList = new Hash();
391
		}
392
 
393
		var selectOffsets = domLib_getOffsets(thisElement);
394
		var center2centerDistance = Math.sqrt(Math.pow(selectOffsets.get('leftCenter') - objectOffsets.get('leftCenter'), 2) + Math.pow(selectOffsets.get('topCenter') - objectOffsets.get('topCenter'), 2));
395
		var radiusSum = selectOffsets.get('radius') + objectOffsets.get('radius');
396
		// the encompassing circles are overlapping, get in for a closer look
397
		if (center2centerDistance < radiusSum)
398
		{
399
			// tip is left of select
400
			if ((objectOffsets.get('leftCenter') <= selectOffsets.get('leftCenter') && objectOffsets.get('right') < selectOffsets.get('left')) ||
401
			// tip is right of select
402
				(objectOffsets.get('leftCenter') > selectOffsets.get('leftCenter') && objectOffsets.get('left') > selectOffsets.get('right')) ||
403
			// tip is above select
404
				(objectOffsets.get('topCenter') <= selectOffsets.get('topCenter') && objectOffsets.get('bottom') < selectOffsets.get('top')) ||
405
			// tip is below select
406
				(objectOffsets.get('topCenter') > selectOffsets.get('topCenter') && objectOffsets.get('top') > selectOffsets.get('bottom')))
407
			{
408
				thisElement.hideList.remove(in_object.id);
409
				if (!thisElement.hideList.length)
410
				{
411
					thisElement.style.visibility = 'visible';
412
					if (domLib_isKonq)
413
					{
414
						thisElement.style.display = '';
415
					}
416
				}
417
			}
418
			else
419
			{
420
				thisElement.hideList.set(in_object.id, true);
421
				thisElement.style.visibility = 'hidden';
422
				if (domLib_isKonq)
423
				{
424
					thisElement.style.display = 'none';
425
				}
426
			}
427
		}
428
	}
429
}
430
 
431
// }}}
432
// {{{ domLib_getOffsets()
433
 
434
function domLib_getOffsets(in_object, in_preserveScroll)
435
{
436
	if (typeof(in_preserveScroll) == 'undefined') {
437
		in_preserveScroll = false;
438
	}
439
 
440
	var originalObject = in_object;
441
	var originalWidth = in_object.offsetWidth;
442
	var originalHeight = in_object.offsetHeight;
443
	var offsetLeft = 0;
444
	var offsetTop = 0;
445
 
446
	while (in_object)
447
	{
448
		offsetLeft += in_object.offsetLeft;
449
		offsetTop += in_object.offsetTop;
450
		in_object = in_object.offsetParent;
451
		// consider scroll offset of parent elements
452
		if (in_object && !in_preserveScroll)
453
		{
454
			offsetLeft -= in_object.scrollLeft;
455
			offsetTop -= in_object.scrollTop;
456
		}
457
	}
458
 
459
	// MacIE misreports the offsets (even with margin: 0 in body{}), still not perfect
460
	if (domLib_isMacIE) {
461
		offsetLeft += 10;
462
		offsetTop += 10;
463
	}
464
 
465
	return new Hash(
466
		'left',		offsetLeft,
467
		'top',		offsetTop,
468
		'right',	offsetLeft + originalWidth,
469
		'bottom',	offsetTop + originalHeight,
470
		'leftCenter',	offsetLeft + originalWidth/2,
471
		'topCenter',	offsetTop + originalHeight/2,
472
		'radius',	Math.max(originalWidth, originalHeight)
473
	);
474
}
475
 
476
// }}}
477
// {{{ domLib_setTimeout()
478
 
479
function domLib_setTimeout(in_function, in_timeout, in_args)
480
{
481
	if (typeof(in_args) == 'undefined')
482
	{
483
		in_args = [];
484
	}
485
 
486
	if (in_timeout == -1)
487
	{
488
		// timeout event is disabled
489
		return 0;
490
	}
491
	else if (in_timeout == 0)
492
	{
493
		in_function(in_args);
494
		return 0;
495
	}
496
 
497
	// must make a copy of the arguments so that we release the reference
498
	var args = domLib_clone(in_args);
499
 
500
	if (!domLib_hasBrokenTimeout)
501
	{
502
		return setTimeout(function() { in_function(args); }, in_timeout);
503
	}
504
	else
505
	{
506
		var id = domLib_timeoutStateId++;
507
		var data = new Hash();
508
		data.set('function', in_function);
509
		data.set('args', args);
510
		domLib_timeoutStates.set(id, data);
511
 
512
		data.set('timeoutId', setTimeout('domLib_timeoutStates.get(' + id + ').get(\'function\')(domLib_timeoutStates.get(' + id + ').get(\'args\')); domLib_timeoutStates.remove(' + id + ');', in_timeout));
513
		return id;
514
	}
515
}
516
 
517
// }}}
518
// {{{ domLib_clearTimeout()
519
 
520
function domLib_clearTimeout(in_id)
521
{
522
	if (!domLib_hasBrokenTimeout)
523
	{
524
		if (in_id > 0) {
525
			clearTimeout(in_id);
526
		}
527
	}
528
	else
529
	{
530
		if (domLib_timeoutStates.has(in_id))
531
		{
532
			clearTimeout(domLib_timeoutStates.get(in_id).get('timeoutId'))
533
			domLib_timeoutStates.remove(in_id);
534
		}
535
	}
536
}
537
 
538
// }}}
539
// {{{ domLib_getEventPosition()
540
 
541
function domLib_getEventPosition(in_eventObj)
542
{
543
	var eventPosition = new Hash('x', 0, 'y', 0, 'scrollX', 0, 'scrollY', 0);
544
 
545
	// IE varies depending on standard compliance mode
546
	if (domLib_isIE)
547
	{
548
		var doc = (domLib_standardsMode ? document.documentElement : document.body);
549
		// NOTE: events may fire before the body has been loaded
550
		if (doc)
551
		{
552
			eventPosition.set('x', in_eventObj.clientX + doc.scrollLeft);
553
			eventPosition.set('y', in_eventObj.clientY + doc.scrollTop);
554
			eventPosition.set('scrollX', doc.scrollLeft);
555
			eventPosition.set('scrollY', doc.scrollTop);
556
		}
557
	}
558
	else
559
	{
560
		eventPosition.set('x', in_eventObj.pageX);
561
		eventPosition.set('y', in_eventObj.pageY);
562
		eventPosition.set('scrollX', in_eventObj.pageX - in_eventObj.clientX);
563
		eventPosition.set('scrollY', in_eventObj.pageY - in_eventObj.clientY);
564
	}
565
 
566
	return eventPosition;
567
}
568
 
569
// }}}
570
// {{{ domLib_cancelBubble()
571
 
572
function domLib_cancelBubble(in_event)
573
{
574
	var eventObj = in_event ? in_event : window.event;
575
	eventObj.cancelBubble = true;
576
}
577
 
578
// }}}
579
// {{{ domLib_getIFrameReference()
580
 
581
function domLib_getIFrameReference(in_frame)
582
{
583
	if (domLib_isGecko || domLib_isIE)
584
	{
585
		return in_frame.frameElement;
586
	}
587
	else
588
	{
589
		// we could either do it this way or require an id on the frame
590
		// equivalent to the name
591
		var name = in_frame.name;
592
		if (!name || !in_frame.parent)
593
		{
594
			return null;
595
		}
596
 
597
		var candidates = in_frame.parent.document.getElementsByTagName('iframe');
598
		for (var i = 0; i < candidates.length; i++)
599
		{
600
			if (candidates[i].name == name)
601
			{
602
				return candidates[i];
603
			}
604
		}
605
 
606
		return null;
607
	}
608
}
609
 
610
// }}}
611
// {{{ domLib_getElementsByClass()
612
 
613
function domLib_getElementsByClass(in_class)
614
{
615
	var elements = domLib_isIE5 ? document.all : document.getElementsByTagName('*');
616
	var matches = [];
617
	var cnt = 0;
618
	for (var i = 0; i < elements.length; i++)
619
	{
620
		if ((" " + elements[i].className + " ").indexOf(" " + in_class + " ") != -1)
621
		{
622
			matches[cnt++] = elements[i];
623
		}
624
	}
625
 
626
	return matches;
627
}
628
 
629
// }}}
630
// {{{ domLib_getElementsByTagNames()
631
 
632
function domLib_getElementsByTagNames(in_list, in_excludeHidden)
633
{
634
	var elements = [];
635
	for (var i = 0; i < in_list.length; i++)
636
	{
637
		var matches = document.getElementsByTagName(in_list[i]);
638
		for (var j = 0; j < matches.length; j++)
639
		{
640
			// skip objects that have nested embeds, or else we get "flashing"
641
			if (matches[j].tagName == 'OBJECT' && domLib_isGecko)
642
			{
643
				var kids = matches[j].childNodes;
644
				var skip = false;
645
				for (var k = 0; k < kids.length; k++)
646
				{
647
					if (kids[k].tagName == 'EMBED')
648
					{
649
						skip = true;
650
						break;
651
					}
652
				}
653
				if (skip) continue;
654
			}
655
 
656
			if (in_excludeHidden && domLib_getComputedStyle(matches[j], 'visibility') == 'hidden')
657
			{
658
				continue;
659
			}
660
 
661
			elements[elements.length] = matches[j];
662
		}
663
	}
664
 
665
	return elements;
666
}
667
 
668
// }}}
669
// {{{ domLib_getComputedStyle()
670
 
671
function domLib_getComputedStyle(in_obj, in_property)
672
{
673
	if (domLib_isIE)
674
	{
675
		var humpBackProp = in_property.replace(/-(.)/, function (a, b) { return b.toUpperCase(); });
676
		return eval('in_obj.currentStyle.' + humpBackProp);
677
	}
678
	// getComputedStyle() is broken in konqueror, so let's go for the style object
679
	else if (domLib_isKonq)
680
	{
681
		//var humpBackProp = in_property.replace(/-(.)/, function (a, b) { return b.toUpperCase(); });
682
		return eval('in_obj.style.' + in_property);
683
	}
684
	else
685
	{
686
		return document.defaultView.getComputedStyle(in_obj, null).getPropertyValue(in_property);
687
	}
688
}
689
 
690
// }}}
691
// {{{ makeTrue()
692
 
693
function makeTrue()
694
{
695
	return true;
696
}
697
 
698
// }}}
699
// {{{ makeFalse()
700
 
701
function makeFalse()
702
{
703
	return false;
704
}
705
 
706
// }}}