~vcs-imports/ipfire/ipfire-2.x

« back to all changes in this revision

Viewing changes to html/html/include/domMenu.js

  • Committer: ipfire
  • Date: 2006-02-15 21:15:54 UTC
  • Revision ID: git-v1:cd1a2927226c734d96478e12bb768256fb64a06a


git-svn-id: http://svn.ipfire.org/svn/ipfire/IPFire/source@16 ea5c0bd1-69bd-2848-81d8-4f18e57aeed8

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
// {{{ docs <-- this is a VIM (text editor) text fold
 
2
 
 
3
/**
 
4
 * DOM Menu 0.3.2
 
5
 *
 
6
 * Summary: Allows developers to add dynamic drop down menus on webpages.  The
 
7
 *          menu can either be horizontal or vertical, and can open in either
 
8
 *          direction.  It has both edge detection and <select> tag detection
 
9
 *          (for browsers that cannot hide these form elements).  The styles
 
10
 *          for the menu items are controlled almost entirely through CSS and
 
11
 *          the menus are created and destroyed using the DOM.  Menu configuration
 
12
 *          is done using a custom Hash() class and is very portable from a PHP
 
13
 *          type array structure.
 
14
 *
 
15
 * Maintainer: Dan Allen <dan@mojavelinux.com>
 
16
 *
 
17
 * License: LGPL - however, if you use this library, please post to my forum where you
 
18
 *          use it so that I get a chance to see my baby in action.  If you are doing
 
19
 *          this for commercial work perhaps you could send me a few Starbucks Coffee
 
20
 *          gift dollars to encourage future developement (NOT REQUIRED).  E-mail me
 
21
 *          for and address.
 
22
 *
 
23
 * Homepage: http://www.mojavelinux.com/forum/viewtopic.php
 
24
 *
 
25
 * Freshmeat Project: http://freshmeat.net/projects/dommenu/?topic_id=92
 
26
 *
 
27
 * Updated: 2003/01/04
 
28
 *
 
29
 * Supported Browsers: Mozilla (Gecko), IE 5+, Konqueror, (not finished Opera 7), Netscape 4
 
30
 *
 
31
 * Usage: 
 
32
 *
 
33
 * Menu Options: Each option is followed by the value for that option. The options avaiable are:
 
34
 *            'contents'
 
35
 *            'rolloverContents',
 
36
 *            'uri' (may be javascript)
 
37
 *            'statusText'
 
38
 *            'target'
 
39
 *            [0-9] an index to create a submenu item
 
40
 *
 
41
 * API:
 
42
 *
 
43
 * menuElementObject {
 
44
 *     ** properties **
 
45
 *     data
 
46
 *       contents
 
47
 *       uri
 
48
 *       target
 
49
 *       statusText
 
50
 *       parentElement
 
51
 *       subMenu
 
52
 *       childElements
 
53
 *       level
 
54
 *       index (index within this level)
 
55
 *     id
 
56
 *     className
 
57
 *     style
 
58
 *     cellSpacing (Konq only)
 
59
 *     
 
60
 *     ** events **
 
61
 *     mouseover/click -> domMenu_openEvent
 
62
 *     mouseout        -> domMenu_closeEvent
 
63
 *     click           -> domMenu_resolveLink
 
64
 * }
 
65
 *
 
66
 * If there is a non-negative click open delay, then any uri of the element will be ignored
 
67
 *
 
68
 * The alternate contents for a hover element are treated by creating to <span> wrapper elements
 
69
 * and then alternating the display of them.  This avoids the need for innerHTML, which can
 
70
 * do nasty things to the browsers.  If <span> turns out to be a bad choice for tags, then a
 
71
 * non-HTML element can be used instead.
 
72
 *
 
73
**/
 
74
 
 
75
// }}}
 
76
// {{{ settings (editable)
 
77
 
 
78
var domMenu_data = new domMenu_Hash();
 
79
var domMenu_settings = new domMenu_Hash();
 
80
 
 
81
domMenu_settings.setItem('global', new domMenu_Hash(
 
82
    'menuBarClass', 'domMenu_menuBar',
 
83
    'menuElementClass', 'domMenu_menuElement',
 
84
    'menuElementHoverClass', 'domMenu_menuElementHover',
 
85
    'menuElementActiveClass', 'domMenu_menuElementHover',
 
86
    'subMenuBarClass', 'domMenu_subMenuBar',
 
87
    'subMenuElementClass', 'domMenu_subMenuElement',
 
88
    'subMenuElementHoverClass', 'domMenu_subMenuElementHover',
 
89
    'subMenuElementActiveClass', 'domMenu_subMenuElementHover',
 
90
    'subMenuElementHeadingClass', 'domMenu_subMenuElementHeading',
 
91
    'menuBarWidth', '100%',
 
92
    'subMenuMinWidth', 'inherit',
 
93
    'distributeSpace', true,
 
94
    'axis', 'horizontal',
 
95
    'verticalExpand', 'south',
 
96
    'horizontalExpand', 'east',
 
97
    'subMenuWidthCorrection', 0,
 
98
    'verticalSubMenuOffsetY', 0,
 
99
    'verticalSubMenuOffsetX', 0,
 
100
    'horizontalSubMenuOffsetX', 0,
 
101
    'horizontalSubMenuOffsetY', 0,
 
102
    'screenPadding', 0,
 
103
    'openMouseoverMenuDelay', 300,
 
104
    'openMousedownMenuDelay', -1,
 
105
    'closeMouseoutMenuDelay', 800,
 
106
    'closeClickMenuDelay', -1,
 
107
    'openMouseoverSubMenuDelay', 300,
 
108
    'openClickSubMenuDelay', -1,
 
109
    'closeMouseoutSubMenuDelay', 300,
 
110
    'closeClickSubMenuDelay', -1,
 
111
    'baseZIndex', 100
 
112
));
 
113
 
 
114
// }}}
 
115
// {{{ global variables
 
116
 
 
117
/**
 
118
 * Browser variables
 
119
 * @var domMenu_is{Browser}
 
120
 */
 
121
var domMenu_userAgent = navigator.userAgent.toLowerCase();
 
122
var domMenu_isOpera = domMenu_userAgent.indexOf('opera 7') != -1 ? 1 : 0;
 
123
var domMenu_isKonq = domMenu_userAgent.indexOf('konq') != -1 ? 1 : 0;
 
124
var domMenu_isIE = !domMenu_isKonq && !domMenu_isOpera && document.all ? 1 : 0;
 
125
var domMenu_isIE50 = domMenu_isIE && domMenu_userAgent.indexOf('msie 5.0') != -1;
 
126
var domMenu_isIE55 = domMenu_isIE && domMenu_userAgent.indexOf('msie 5.5') != -1;
 
127
var domMenu_isIE5 = domMenu_isIE50 || domMenu_isIE55;
 
128
var domMenu_isGecko = !domMenu_isKonq && domMenu_userAgent.indexOf('gecko') != -1 ? 1 : 0;
 
129
 
 
130
/**
 
131
 * Passport to use the menu system, checked before performing menu manipulation
 
132
 * @var domMenu_useLibrary
 
133
 */
 
134
var domMenu_useLibrary = domMenu_isIE || domMenu_isGecko || domMenu_isKonq || domMenu_isOpera ? 1 : 0;
 
135
 
 
136
/**
 
137
 * The data for the menu is stored here, loaded from an external file
 
138
 * @hash domMenu_data
 
139
 */
 
140
var domMenu_data;
 
141
 
 
142
var domMenu_selectElements;
 
143
var domMenu_scrollbarWidth = 14;
 
144
var domMenu_eventTo = domMenu_isIE ? 'toElement' : 'relatedTarget';
 
145
var domMenu_eventFrom = domMenu_isIE ? 'fromElement' : 'relatedTarget';
 
146
 
 
147
var domMenu_activeElement = new domMenu_Hash();
 
148
 
 
149
/**
 
150
 * Array of hashes listing the timouts currently running for opening/closing menus
 
151
 * @array domMenu_timeouts
 
152
 */
 
153
var domMenu_timeouts = new Array();
 
154
domMenu_timeouts['open'] = new domMenu_Hash();
 
155
domMenu_timeouts['close'] = new domMenu_Hash();
 
156
 
 
157
var domMenu_timeoutStates = new Array();
 
158
domMenu_timeoutStates['open'] = new domMenu_Hash();
 
159
domMenu_timeoutStates['close'] = new domMenu_Hash();
 
160
 
 
161
/**
 
162
 * Style to use for a link pointer, which is different between Gecko and IE
 
163
 * @var domMenu_pointerStyle
 
164
 */
 
165
var domMenu_pointerStyle = domMenu_isIE ? 'hand' : 'pointer';
 
166
 
 
167
// }}}
 
168
// {{{ domMenu_Hash()
 
169
 
 
170
function domMenu_Hash() {
 
171
    var argIndex = 0;
 
172
    this.length = 0;
 
173
    this.numericLength = 0; 
 
174
    this.items = new Array();
 
175
    while (arguments.length > argIndex) {
 
176
        this.items[arguments[argIndex]] = arguments[argIndex + 1];
 
177
        if (arguments[argIndex] == parseInt(arguments[argIndex])) {
 
178
            this.numericLength++;
 
179
        }
 
180
 
 
181
        this.length++;
 
182
        argIndex += 2;
 
183
    }
 
184
 
 
185
    this.removeItem = function(in_key)
 
186
    {
 
187
        var tmp_value;
 
188
        if (typeof(this.items[in_key]) != 'undefined') {
 
189
            this.length--;
 
190
            if (in_key == parseInt(in_key)) {
 
191
                this.numericLength--;
 
192
            }
 
193
 
 
194
            tmp_value = this.items[in_key];
 
195
            delete this.items[in_key];
 
196
        }
 
197
        
 
198
        return tmp_value;
 
199
    }
 
200
 
 
201
    this.getItem = function(in_key)
 
202
    {
 
203
        return this.items[in_key];
 
204
    }
 
205
 
 
206
    this.setItem = function(in_key, in_value)
 
207
    {
 
208
        if (typeof(this.items[in_key]) == 'undefined') {
 
209
            this.length++;
 
210
            if (in_key == parseInt(in_key)) {
 
211
                this.numericLength++;
 
212
            }
 
213
        }
 
214
        
 
215
        this.items[in_key] = in_value;
 
216
    }
 
217
 
 
218
    this.hasItem = function(in_key)
 
219
    {
 
220
        return typeof(this.items[in_key]) != 'undefined';
 
221
    }
 
222
    
 
223
    this.merge = function(in_hash)
 
224
    {
 
225
        for (var tmp_key in in_hash.items) {
 
226
            if (typeof(this.items[tmp_key]) == 'undefined') {
 
227
                this.length++;
 
228
                if (tmp_key == parseInt(tmp_key)) {
 
229
                    this.numericLength++;
 
230
                }
 
231
            }
 
232
 
 
233
            this.items[tmp_key] = in_hash.items[tmp_key];
 
234
        }
 
235
    }
 
236
 
 
237
    this.compare = function(in_hash)
 
238
    {
 
239
        if (this.length != in_hash.length) {
 
240
            return false;
 
241
        }
 
242
 
 
243
        for (var tmp_key in this.items) {
 
244
            if (this.items[tmp_key] != in_hash.items[tmp_key]) {
 
245
                return false;
 
246
            }
 
247
        }
 
248
        
 
249
        return true;
 
250
    }
 
251
}
 
252
 
 
253
// }}}
 
254
// {{{ domMenu_activate()
 
255
 
 
256
function domMenu_activate(in_containerId)
 
257
{
 
258
    var container;
 
259
    var data;
 
260
 
 
261
    // make sure we can use the menu system and this is a valid menu
 
262
    if (!domMenu_useLibrary || !(container = document.getElementById(in_containerId)) || !(data = domMenu_data.items[in_containerId])) {
 
263
        return;
 
264
    }
 
265
 
 
266
    // start with the global settings and merge in the local changes
 
267
    if (!domMenu_settings.hasItem(in_containerId)) {
 
268
        domMenu_settings.setItem(in_containerId, new domMenu_Hash());
 
269
    }
 
270
 
 
271
    var settings = domMenu_settings.items[in_containerId];
 
272
    for (var i in domMenu_settings.items['global'].items) {
 
273
        if (!settings.hasItem(i)) {
 
274
            settings.setItem(i, domMenu_settings.items['global'].items[i]);
 
275
        }
 
276
    }
 
277
 
 
278
    // populate the zero level element
 
279
    container.data = new domMenu_Hash(
 
280
        'parentElement', false,
 
281
        'numChildren', data.numericLength,
 
282
        'childElements', new domMenu_Hash(),
 
283
        'level', 0,
 
284
        'index', 1
 
285
    );
 
286
    
 
287
    // if we choose to distribute either height or width, determine ratio of each cell
 
288
    var distributeRatio = Math.round(100/container.data.items['numChildren']) + '%';
 
289
    
 
290
    // the first menu is the rootMenu, which is a child of the zero level element
 
291
    var rootMenu = document.createElement('div');
 
292
    rootMenu.id = in_containerId + '[0]';
 
293
    rootMenu.className = settings.items['menuBarClass'];
 
294
    container.data.setItem('subMenu', rootMenu);
 
295
 
 
296
    var rootMenuTable = rootMenu.appendChild(document.createElement('table'));
 
297
    if (domMenu_isKonq) {
 
298
        rootMenuTable.cellSpacing = 0;
 
299
    }
 
300
 
 
301
    rootMenuTable.style.border = 0;
 
302
    rootMenuTable.style.borderCollapse = 'collapse';
 
303
    rootMenuTable.style.width = settings.items['menuBarWidth'];
 
304
    var rootMenuTableBody = rootMenuTable.appendChild(document.createElement('tbody'));
 
305
 
 
306
    var numSiblings = container.data.items['numChildren'];
 
307
    for (var index = 1; index <= numSiblings; index++) {
 
308
        // create a row the first time if horizontal or each time if vertical
 
309
        if (index == 1 || settings.items['axis'] == 'vertical') {
 
310
            var rootMenuTableRow = rootMenuTableBody.appendChild(document.createElement('tr'));
 
311
        }
 
312
 
 
313
        // create an instance of the root level menu element
 
314
        var rootMenuTableCell = rootMenuTableRow.appendChild(document.createElement('td'));
 
315
        rootMenuTableCell.style.padding = 0;
 
316
        rootMenuTableCell.id = in_containerId + '[' + index + ']';
 
317
        // add element to list of parent children
 
318
        container.data.items['childElements'].setItem(rootMenuTableCell.id, rootMenuTableCell);
 
319
 
 
320
        // assign the settings to the root level element
 
321
        // {!} this is a problem if two menus are using the same data {!}
 
322
        rootMenuTableCell.data = data.items[index];
 
323
        rootMenuTableCell.data.merge(new domMenu_Hash(
 
324
            'basename', in_containerId,
 
325
            'parentElement', container,
 
326
            'numChildren', rootMenuTableCell.data.numericLength,
 
327
            'childElements', new domMenu_Hash(),
 
328
            'offsets', new domMenu_Hash(),
 
329
            'level', container.data.items['level'] + 1,
 
330
            'index', index
 
331
        ));
 
332
 
 
333
        // assign the styles
 
334
        rootMenuTableCell.style.cursor = 'default';
 
335
        if (settings.items['axis'] == 'horizontal') {
 
336
            if (settings.items['distributeSpace']) {
 
337
                rootMenuTableCell.style.width = distributeRatio;
 
338
            }
 
339
        }
 
340
 
 
341
        var rootElement = rootMenuTableCell.appendChild(document.createElement('div'));
 
342
        rootElement.className = settings.items['menuElementClass'];
 
343
        // fill in the menu element contents
 
344
        rootElement.innerHTML = '<span>' + rootMenuTableCell.data.items['contents'] + '</span>' + (rootMenuTableCell.data.hasItem('contentsHover') ? '<span style="display: none;">' + rootMenuTableCell.data.items['contentsHover'] + '</span>' : '');
 
345
 
 
346
        // attach the events
 
347
        rootMenuTableCell.onmouseover = function(in_event) { domMenu_openEvent(this, in_event, settings.items['openMouseoverMenuDelay']); };
 
348
        rootMenuTableCell.onmouseout = function(in_event) { domMenu_closeEvent(this, in_event); };
 
349
 
 
350
        if (settings.items['openMousedownMenuDelay'] >= 0 && rootMenuTableCell.data.items['numChildren']) {
 
351
            rootMenuTableCell.onmousedown = function(in_event) { domMenu_openEvent(this, in_event, settings.items['openMousedownMenuDelay']); };
 
352
            // cancel mouseup so that it doesn't propogate to global mouseup event
 
353
            rootMenuTableCell.onmouseup = function(in_event) { var eventObj = domMenu_isIE ? event : in_event; eventObj.cancelBubble = true; };
 
354
            if (domMenu_isIE) {
 
355
                rootMenuTableCell.ondblclick = function(in_event) { domMenu_openEvent(this, in_event, settings.items['openMousedownMenuDelay']); };
 
356
            }
 
357
        }
 
358
        else if (rootMenuTableCell.data.items['uri']) {
 
359
            rootMenuTableCell.style.cursor = domMenu_pointerStyle;
 
360
            rootMenuTableCell.onclick = function(in_event) { domMenu_resolveLink(this, in_event); };
 
361
        }
 
362
 
 
363
        // prevent highlighting of text
 
364
        if (domMenu_isIE) {
 
365
            rootMenuTableCell.onselectstart = function() { return false; };
 
366
        }
 
367
 
 
368
        rootMenuTableCell.oncontextmenu = function() { return false; };
 
369
    }
 
370
    
 
371
    // add the menu rootMenu to the zero level element
 
372
    rootMenu = container.appendChild(rootMenu);
 
373
 
 
374
    // even though most cases the top level menu does not go away, it could
 
375
    // if this menu system is used by another process
 
376
    domMenu_detectCollisions(rootMenu);
 
377
}
 
378
 
 
379
// }}}
 
380
// {{{ domMenu_activateSubMenu()
 
381
 
 
382
function domMenu_activateSubMenu(in_parentElement)
 
383
{
 
384
    // see if submenu already exists
 
385
    if (in_parentElement.data.hasItem('subMenu')) {
 
386
        domMenu_toggleSubMenu(in_parentElement, 'visible');
 
387
        return;
 
388
    }
 
389
 
 
390
    var settings = domMenu_settings.items[in_parentElement.data.items['basename']];
 
391
 
 
392
    // build the submenu
 
393
    var menu = document.createElement('div');
 
394
    menu.id = in_parentElement.id + '[0]';
 
395
    menu.className = settings.items['subMenuBarClass'];
 
396
    menu.style.zIndex = settings.items['baseZIndex'];
 
397
    menu.style.position = 'absolute';
 
398
    // position the menu in the upper left corner hidden so that we can work on it
 
399
    menu.style.visibility = 'hidden';
 
400
    menu.style.top = 0;
 
401
    menu.style.left = 0;
 
402
 
 
403
    in_parentElement.data.setItem('subMenu', menu);
 
404
 
 
405
    var menuTable = menu.appendChild(document.createElement('table'));
 
406
    // ** opera wants to make absolute tables width 100% **
 
407
    if (domMenu_isOpera) {
 
408
        menuTable.style.width = '1px';
 
409
        menuTable.style.whiteSpace = 'nowrap';
 
410
    }
 
411
 
 
412
    if (domMenu_isKonq) {
 
413
        menuTable.cellSpacing = 0;
 
414
    }
 
415
 
 
416
    menuTable.style.border = 0;
 
417
    menuTable.style.borderCollapse = 'collapse';
 
418
    var menuTableBody = menuTable.appendChild(document.createElement('tbody'));
 
419
 
 
420
    var numSiblings = in_parentElement.data.items['numChildren'];
 
421
    for (var index = 1; index <= numSiblings; index++) {
 
422
        var dataIndex = in_parentElement.data.items['level'] == 1 && settings.items['verticalExpand'] == 'north' && settings.items['axis'] == 'horizontal' ? numSiblings + 1 - index : index;
 
423
        var menuTableCell = menuTableBody.appendChild(document.createElement('tr')).appendChild(document.createElement('td'));
 
424
        menuTableCell.style.padding = 0;
 
425
        menuTableCell.id = in_parentElement.id + '[' + dataIndex + ']';
 
426
 
 
427
        // add element to list of parent children
 
428
        in_parentElement.data.items['childElements'].setItem(menuTableCell.id, menuTableCell);
 
429
 
 
430
        // assign the settings to nth level element
 
431
        menuTableCell.data = in_parentElement.data.items[dataIndex];
 
432
        menuTableCell.data.merge(new domMenu_Hash(
 
433
            'basename', in_parentElement.data.items['basename'],
 
434
            'parentElement', in_parentElement,
 
435
            'numChildren', menuTableCell.data.numericLength,
 
436
            'childElements', new domMenu_Hash(),
 
437
            'offsets', new domMenu_Hash(),
 
438
            'level', in_parentElement.data.items['level'] + 1,
 
439
            'index', index
 
440
        ));
 
441
        
 
442
        // assign the styles
 
443
        var parentStyle = in_parentElement.data.items['level'] == 1 ? in_parentElement.parentNode.style : in_parentElement.style;
 
444
        menuTableCell.style.cursor = 'default';
 
445
        
 
446
        var element = menuTableCell.appendChild(document.createElement('div')); 
 
447
        var outerElement = element;
 
448
        outerElement.className = settings.items['subMenuElementClass']; 
 
449
 
 
450
        if (menuTableCell.data.items['numChildren']) {
 
451
            element = outerElement.appendChild(document.createElement('div'));
 
452
            // {!} depends on which way we are opening {!}
 
453
            element.style.backgroundImage = 'url(arrow.gif)';
 
454
            element.style.backgroundRepeat = 'no-repeat';
 
455
            element.style.backgroundPosition = 'right center';
 
456
            // add appropriate padding to fit the arrow
 
457
            element.style.paddingRight = '12px';
 
458
        }
 
459
 
 
460
        // fill in the menu item contents
 
461
        element.innerHTML = menuTableCell.data.items['contents'];
 
462
 
 
463
        // attach the events
 
464
        menuTableCell.onmouseover = function(in_event) { domMenu_openEvent(this, in_event, settings.items['openMouseoverSubMenuDelay']); };
 
465
        menuTableCell.onmouseout = function(in_event) { domMenu_closeEvent(this, in_event); };
 
466
 
 
467
        if (settings.items['openClickSubMenuDelay'] >= 0 && menuTableCell.data.items['numChildren']) {
 
468
            menuTableCell.onmousedown = function(in_event) { domMenu_openEvent(this, in_event, settings.items['openClickSubMenuDelay']); };
 
469
            menuTableCell.onmouseup = function(in_event) { var eventObj = domMenu_isIE ? event : in_event; eventObj.cancelBubble = true; };
 
470
            if (domMenu_isIE) {
 
471
                menuTableCell.ondblclick = function(in_event) { domMenu_openEvent(this, in_event, settings.items['openClickSubMenuDelay']); };
 
472
            }
 
473
        }
 
474
        else if (menuTableCell.data.items['uri']) {
 
475
            menuTableCell.style.cursor = domMenu_pointerStyle;
 
476
            menuTableCell.onclick = function(in_event) { domMenu_resolveLink(this, in_event); };
 
477
        }
 
478
        else if (!menuTableCell.data.items['numChildren']) {
 
479
            outerElement.className += ' ' + settings.items['subMenuElementHeadingClass'];
 
480
        }
 
481
 
 
482
        // prevent highlighting of text
 
483
        if (domMenu_isIE) {
 
484
            menuTableCell.onselectstart = function() { return false; };
 
485
        }
 
486
 
 
487
        menuTableCell.oncontextmenu = function() { return false; };
 
488
    }
 
489
 
 
490
    menu = document.body.appendChild(menu);
 
491
    domMenu_toggleSubMenu(in_parentElement, 'visible');
 
492
}
 
493
 
 
494
// }}}
 
495
// {{{ domMenu_changeActivePath()
 
496
 
 
497
/**
 
498
 * Close the old active path up to the new active element
 
499
 * and return the value of the new active element (or the same if unchanged)
 
500
 * If the new active element is not set, the top level is assumed
 
501
 *
 
502
 * @return mixed new active element or false if not set
 
503
 */
 
504
function domMenu_changeActivePath(in_newActiveElement, in_oldActiveElement, in_closeDelay)
 
505
{
 
506
    // protect against crap
 
507
    if (!in_oldActiveElement && !in_newActiveElement) {
 
508
        return false;
 
509
    }
 
510
 
 
511
    // cancel open timeouts since we know we are opening something different now
 
512
    for (var i in domMenu_timeouts['open'].items) {
 
513
        domMenu_cancelTimeout(i, 'open');
 
514
    }
 
515
 
 
516
    // grab some info about this menu system
 
517
    var basename = in_oldActiveElement ? in_oldActiveElement.data.items['basename'] : in_newActiveElement.data.items['basename'];
 
518
    var settings = domMenu_settings.items[basename];
 
519
 
 
520
    // build the old and new paths
 
521
    var oldActivePath = new domMenu_Hash();
 
522
    if (in_oldActiveElement) {
 
523
        var tmp_oldActivePathElement = in_oldActiveElement;
 
524
        do {
 
525
            oldActivePath.setItem(tmp_oldActivePathElement.id, tmp_oldActivePathElement); 
 
526
        } while ((tmp_oldActivePathElement = tmp_oldActivePathElement.data.items['parentElement']) && tmp_oldActivePathElement.id != basename);
 
527
 
 
528
        // unhighlight the old active element if it doesn't have children open
 
529
        if (!in_oldActiveElement.data.items['subMenu'] || in_oldActiveElement.data.items['subMenu'].style.visibility == 'hidden') {
 
530
            domMenu_toggleHighlight(in_oldActiveElement, false);
 
531
        }
 
532
    }
 
533
 
 
534
    var newActivePath = new domMenu_Hash();
 
535
    var intersectPoint;
 
536
    if (in_newActiveElement) {
 
537
        var actualActiveElement = in_newActiveElement;
 
538
        window.status = in_newActiveElement.data.items['statusText'] + ' ';
 
539
 
 
540
        // in the event we have no old active element, just highlight new one and return
 
541
        // without setting the new active element (handled later)
 
542
        if (!in_oldActiveElement) {
 
543
            domMenu_cancelTimeout(in_newActiveElement.id, 'close'); 
 
544
            domMenu_toggleHighlight(in_newActiveElement, true);
 
545
            return false;
 
546
        }
 
547
        // if the new element is in the path of the old element, then pretend event is
 
548
        // on the old active element
 
549
        else if (oldActivePath.hasItem(in_newActiveElement.id)) {
 
550
            in_newActiveElement = in_oldActiveElement;
 
551
        }
 
552
 
 
553
        var tmp_newActivePathElement = in_newActiveElement;
 
554
        do {
 
555
            // if we have met up with the old active path, then record merge point
 
556
            if (!intersectPoint && oldActivePath.hasItem(tmp_newActivePathElement.id)) {
 
557
                intersectPoint = tmp_newActivePathElement;
 
558
            }
 
559
 
 
560
            newActivePath.setItem(tmp_newActivePathElement.id, tmp_newActivePathElement); 
 
561
            domMenu_cancelTimeout(tmp_newActivePathElement.id, 'close'); 
 
562
            // {!} this is ugly {!}
 
563
            if (tmp_newActivePathElement != in_oldActiveElement || actualActiveElement == in_oldActiveElement) {
 
564
                domMenu_toggleHighlight(tmp_newActivePathElement, true);
 
565
            }
 
566
        } while ((tmp_newActivePathElement = tmp_newActivePathElement.data.items['parentElement']) && tmp_newActivePathElement.id != basename);
 
567
 
 
568
        // if we move to the child of the old active element
 
569
        if (in_newActiveElement.data.items['parentElement'] == in_oldActiveElement) {
 
570
            return in_newActiveElement;
 
571
        }
 
572
        // if the new active element is in the old active path
 
573
        else if (in_newActiveElement == in_oldActiveElement) {
 
574
            return in_newActiveElement;
 
575
        }
 
576
 
 
577
        // find the sibling element
 
578
        var intersectSibling;
 
579
        if (intersectPoint) {
 
580
            for (var i in oldActivePath.items) {
 
581
                if (oldActivePath.items[i].data.items['parentElement'] == intersectPoint) {
 
582
                    intersectSibling = oldActivePath.items[i];
 
583
                    break;
 
584
                }
 
585
            }
 
586
        }
 
587
 
 
588
        var isRootLevel = in_newActiveElement.data.items['level'] == 1 ? true : false;
 
589
        var closeDelay = isRootLevel ? settings.items['closeMouseoutMenuDelay'] : settings.items['closeMouseoutSubMenuDelay'];
 
590
    }
 
591
    else {
 
592
        var isRootLevel = false;
 
593
        var closeDelay = settings.items['closeMouseoutMenuDelay'];
 
594
        window.status = window.defaultStatus;
 
595
    }
 
596
 
 
597
    // override the close delay with that passed in
 
598
    if (typeof(in_closeDelay) != 'undefined') {
 
599
        closeDelay = in_closeDelay;
 
600
    }
 
601
 
 
602
    // if there is an intersect sibling, then we need to work from there up to 
 
603
    // preserve the active path
 
604
    if (intersectSibling) {
 
605
        // only if this is not the root level to we allow the scheduled close
 
606
        // events to persist...otherwise we close immediately
 
607
        if (!isRootLevel) {
 
608
            // toggle the sibling highlight (only one sibling highlighted at a time)
 
609
            domMenu_toggleHighlight(intersectSibling, false);
 
610
        }
 
611
        // we are moving to another top level menu
 
612
        // {!} clean this up {!}
 
613
        else {
 
614
            // add lingering menus outside of old active path to active path
 
615
            for (var i in domMenu_timeouts['close'].items) {
 
616
                if (!oldActivePath.hasItem(i)) {
 
617
                    var tmp_element = document.getElementById(i);
 
618
                    if (tmp_element.data.items['basename'] == basename) {
 
619
                        oldActivePath.setItem(i, tmp_element);
 
620
                    }
 
621
                }
 
622
            }
 
623
        }
 
624
    }
 
625
 
 
626
    // schedule the old active path to be closed
 
627
    for (var i in oldActivePath.items) {
 
628
        if (newActivePath.hasItem(i)) {
 
629
            continue;
 
630
        }
 
631
 
 
632
        // make sure we don't double schedule here
 
633
        domMenu_cancelTimeout(i, 'close');
 
634
 
 
635
        if (isRootLevel) {
 
636
            domMenu_toggleHighlight(oldActivePath.items[i], false); 
 
637
            domMenu_toggleSubMenu(oldActivePath.items[i], 'hidden');
 
638
        }
 
639
        else {
 
640
            var tmp_args = new Array();
 
641
            tmp_args[0] = oldActivePath.items[i];
 
642
            var tmp_function = 'domMenu_toggleHighlight(argv[0], false); domMenu_toggleSubMenu(argv[0], ' + domMenu_quote('hidden') + ');';
 
643
            // if this is the top level, then the menu is being deactivated
 
644
            if (oldActivePath.items[i].data.items['level'] == 1) {
 
645
                tmp_function += ' domMenu_activeElement.setItem(' + domMenu_quote(basename) + ', false);';
 
646
            }
 
647
 
 
648
            domMenu_callTimeout(tmp_function, closeDelay, tmp_args, i, 'close');
 
649
        }
 
650
    }
 
651
    
 
652
    return in_newActiveElement;
 
653
}
 
654
 
 
655
// }}}
 
656
// {{{ domMenu_deactivate()
 
657
 
 
658
function domMenu_deactivate(in_basename, in_delay)
 
659
{
 
660
    if (!in_delay) {
 
661
        in_delay = 0;
 
662
    }
 
663
 
 
664
    domMenu_changeActivePath(false, domMenu_activeElement.items[in_basename], in_delay);
 
665
}
 
666
 
 
667
// }}}
 
668
// {{{ domMenu_openEvent()
 
669
 
 
670
/**
 
671
 * Handle the mouse event to open a menu
 
672
 *
 
673
 * When an event is received to open the menu, this function is
 
674
 * called, handles reinitialization of the menu state and sets
 
675
 * a timeout interval for opening the submenu (if one exists)
 
676
 */
 
677
function domMenu_openEvent(in_this, in_event, in_openDelay)
 
678
{
 
679
    if (domMenu_isGecko) {
 
680
        try {
 
681
            window.getSelection().removeAllRanges();
 
682
        } catch (e) {}
 
683
    }
 
684
 
 
685
    // setup the cross-browser event object and target
 
686
    var eventObj = domMenu_isIE ? event : in_event;
 
687
    var currentTarget = domMenu_isIE ? in_this : eventObj.currentTarget;
 
688
    var basename = currentTarget.data.items['basename'];
 
689
 
 
690
    // if we are moving amoungst children of the same element, just ignore event
 
691
    if (eventObj.type != 'mousedown' && domMenu_getElement(eventObj[domMenu_eventFrom], basename) == currentTarget) {
 
692
        return;
 
693
    }
 
694
 
 
695
    // if we click on an open menu, close it
 
696
    if (eventObj.type == 'mousedown' && domMenu_activeElement.items[basename]) {
 
697
        var settings = domMenu_settings.items[basename];
 
698
        domMenu_changeActivePath(false, domMenu_activeElement.items[basename], currentTarget.data.items['level'] == 1 ? settings.items['closeClickMenuDelay'] : settings.items['closeClickSubMenuDelay']);
 
699
        return;
 
700
    }
 
701
 
 
702
    // if this element has children, popup the child menu
 
703
    if (currentTarget.data.items['numChildren']) {
 
704
        // the top level menus have no delay when moving between them
 
705
        // so activate submenu immediately
 
706
        if (currentTarget.data.items['level'] == 1 && domMenu_activeElement.items[basename]) {
 
707
            // ** I place changeActivePath() call here so the hiding of selects does not flicker **
 
708
            // {!} instead I could tell changeActivePath to clear select ownership but not
 
709
            // toggle visibility....hmmm....{!}
 
710
            domMenu_activateSubMenu(currentTarget);
 
711
            // clear the active path and initialize the new one
 
712
            domMenu_activeElement.setItem(basename, domMenu_changeActivePath(currentTarget, domMenu_activeElement.items[basename]));
 
713
        }
 
714
        else {
 
715
            // clear the active path and initialize the new one
 
716
            domMenu_activeElement.setItem(basename, domMenu_changeActivePath(currentTarget, domMenu_activeElement.items[basename]));
 
717
            var tmp_args = new Array();
 
718
            tmp_args[0] = currentTarget;
 
719
            var tmp_function = 'if (!domMenu_activeElement.items[' + domMenu_quote(basename) + ']) { domMenu_activeElement.setItem(' + domMenu_quote(basename) + ', argv[0]); } domMenu_activateSubMenu(argv[0]);';
 
720
            domMenu_callTimeout(tmp_function, in_openDelay, tmp_args, currentTarget.id, 'open');
 
721
        }
 
722
    }
 
723
    else {
 
724
        // clear the active path and initialize the new one
 
725
        domMenu_activeElement.setItem(basename, domMenu_changeActivePath(currentTarget, domMenu_activeElement.items[basename]));
 
726
    }
 
727
}
 
728
 
 
729
// }}}
 
730
// {{{ domMenu_closeEvent()
 
731
 
 
732
/**
 
733
 * Handle the mouse event to close a menu
 
734
 *
 
735
 * When an mouseout event is received to close the menu, this function is
 
736
 * called, sets a timeout interval for closing the menu.
 
737
 */
 
738
function domMenu_closeEvent(in_this, in_event)
 
739
{
 
740
    // setup the cross-browser event object and target
 
741
    var eventObj = domMenu_isIE ? event : in_event;
 
742
    var currentTarget = domMenu_isIE ? in_this : eventObj.currentTarget;
 
743
    var basename = currentTarget.data.items['basename'];
 
744
    var relatedTarget = domMenu_getElement(eventObj[domMenu_eventTo], basename);
 
745
 
 
746
    // if the related target is not a menu element then we left the menu system
 
747
    // at this point (or cannot discern where we are in the menu)
 
748
    if (domMenu_activeElement.items[basename]) {
 
749
        if (!relatedTarget) {
 
750
            domMenu_changeActivePath(false, domMenu_activeElement.items[basename]);
 
751
        }
 
752
    }
 
753
    // we are highlighting the top level, but menu is not yet 'active'
 
754
    else {
 
755
        if (currentTarget != relatedTarget) {
 
756
            domMenu_cancelTimeout(currentTarget.id, 'open');
 
757
            domMenu_toggleHighlight(currentTarget, false);
 
758
        }
 
759
    }
 
760
}    
 
761
 
 
762
// }}}
 
763
// {{{ domMenu_getElement()
 
764
 
 
765
function domMenu_getElement(in_object, in_basename)
 
766
{
 
767
    while (in_object) {
 
768
        try {
 
769
            if (in_object.id && in_object.id.search(new RegExp('^' + in_basename + '(\\[[0-9]\\])*\\[[0-9]\\]$')) == 0) {
 
770
                return in_object;
 
771
            }
 
772
            else {
 
773
                in_object = in_object.parentNode;
 
774
            }
 
775
        }
 
776
        catch(e) {
 
777
            return false;
 
778
        }
 
779
    }
 
780
    
 
781
    return false;
 
782
}
 
783
 
 
784
// }}}
 
785
// {{{ domMenu_detectCollisions()
 
786
 
 
787
function domMenu_detectCollisions(in_menuObj, in_recover)
 
788
{
 
789
    // no need to do anything for opera
 
790
    if (domMenu_isOpera) {
 
791
        return;
 
792
    }
 
793
 
 
794
    if (typeof(domMenu_selectElements) == 'undefined') {
 
795
        domMenu_selectElements = document.getElementsByTagName('select');
 
796
    }
 
797
    
 
798
    // if we don't have a menu, then unhide selects
 
799
    if (in_recover) {
 
800
        for (var cnt = 0; cnt < domMenu_selectElements.length; cnt++) {
 
801
            if (domMenu_isGecko && domMenu_selectElements[cnt].size <= 1 && !domMenu_selectElements[cnt].multiple) {
 
802
                continue;
 
803
            }
 
804
 
 
805
            var thisSelect = domMenu_selectElements[cnt];
 
806
            thisSelect.hideList.removeItem(in_menuObj.id);
 
807
            if (!thisSelect.hideList.length) {
 
808
                domMenu_selectElements[cnt].style.visibility = 'visible';
 
809
            }
 
810
        }
 
811
 
 
812
        return;
 
813
    }
 
814
 
 
815
    // okay, in_menu exists, let's hunt and destroy
 
816
    var menuOffsets = domMenu_getOffsets(in_menuObj);
 
817
 
 
818
    for (var cnt = 0; cnt < domMenu_selectElements.length; cnt++) {
 
819
        var thisSelect = domMenu_selectElements[cnt];
 
820
 
 
821
        // mozilla doesn't have a problem with regular selects
 
822
        if (domMenu_isGecko && thisSelect.size <= 1 && !thisSelect.multiple) {
 
823
            continue;
 
824
        }
 
825
 
 
826
        // {!} make sure this hash is congruent with domTT hash {!}
 
827
        if (!thisSelect.hideList) {
 
828
            thisSelect.hideList = new domMenu_Hash();
 
829
        }
 
830
 
 
831
        var selectOffsets = domMenu_getOffsets(thisSelect); 
 
832
        // for mozilla we only have to worry about the scrollbar itself
 
833
        if (domMenu_isGecko) {
 
834
            selectOffsets.setItem('left', selectOffsets.items['left'] + thisSelect.offsetWidth - domMenu_scrollbarWidth);
 
835
            selectOffsets.setItem('leftCenter', selectOffsets.items['left'] + domMenu_scrollbarWidth/2);
 
836
            selectOffsets.setItem('radius', Math.max(thisSelect.offsetHeight, domMenu_scrollbarWidth/2));
 
837
        }
 
838
 
 
839
        var center2centerDistance = Math.sqrt(Math.pow(selectOffsets.items['leftCenter'] - menuOffsets.items['leftCenter'], 2) + Math.pow(selectOffsets.items['topCenter'] - menuOffsets.items['topCenter'], 2));
 
840
        var radiusSum = selectOffsets.items['radius'] + menuOffsets.items['radius'];
 
841
        // the encompassing circles are overlapping, get in for a closer look
 
842
        if (center2centerDistance < radiusSum) {
 
843
            // tip is left of select
 
844
            if ((menuOffsets.items['leftCenter'] <= selectOffsets.items['leftCenter'] && menuOffsets.items['right'] < selectOffsets.items['left']) ||
 
845
            // tip is right of select
 
846
                (menuOffsets.items['leftCenter'] > selectOffsets.items['leftCenter'] && menuOffsets.items['left'] > selectOffsets.items['right']) ||
 
847
            // tip is above select
 
848
                (menuOffsets.items['topCenter'] <= selectOffsets.items['topCenter'] && menuOffsets.items['bottom'] < selectOffsets.items['top']) ||
 
849
            // tip is below select
 
850
                (menuOffsets.items['topCenter'] > selectOffsets.items['topCenter'] && menuOffsets.items['top'] > selectOffsets.items['bottom'])) {
 
851
                thisSelect.hideList.removeItem(in_menuObj.id);
 
852
                if (!thisSelect.hideList.length) {
 
853
                    thisSelect.style.visibility = 'visible';
 
854
                }
 
855
            }
 
856
            else {
 
857
                thisSelect.hideList.setItem(in_menuObj.id, true);
 
858
                thisSelect.style.visibility = 'hidden';
 
859
            }
 
860
        }
 
861
    }
 
862
}
 
863
 
 
864
// }}}
 
865
// {{{ domMenu_getOffsets()
 
866
 
 
867
function domMenu_getOffsets(in_object)
 
868
{
 
869
    var originalObject = in_object;
 
870
    var originalWidth = in_object.offsetWidth;
 
871
    var originalHeight = in_object.offsetHeight;
 
872
    var offsetLeft = 0;
 
873
    var offsetTop = 0;
 
874
 
 
875
    while (in_object) {
 
876
        offsetLeft += in_object.offsetLeft;
 
877
        offsetTop += in_object.offsetTop;
 
878
        in_object = in_object.offsetParent;
 
879
    }
 
880
    
 
881
    return new domMenu_Hash(
 
882
        'left',       offsetLeft,
 
883
        'top',        offsetTop,
 
884
        'right',      offsetLeft + originalWidth,
 
885
        'bottom',     offsetTop + originalHeight,
 
886
        'leftCenter', offsetLeft + originalWidth/2,
 
887
        'topCenter',  offsetTop + originalHeight/2,
 
888
        'radius',     Math.max(originalWidth, originalHeight) 
 
889
    );
 
890
}
 
891
 
 
892
// }}}
 
893
// {{{ domMenu_callTimeout()
 
894
 
 
895
function domMenu_callTimeout(in_function, in_timeout, in_args, in_basename, in_type)
 
896
{
 
897
    if (in_timeout == 0) {
 
898
        var tmp_function = new Function('argv', in_function);
 
899
        tmp_function(in_args);
 
900
    }
 
901
    else if (in_timeout > 0) {
 
902
        // after we complete the timeout call, we want to remove the reference, so always add that
 
903
        var tmp_function = new Function('argv', in_function + ' domMenu_timeouts[' + domMenu_quote(in_type) + '].removeItem(' + domMenu_quote(in_basename) + ');');
 
904
 
 
905
        var tmp_args = new Array();
 
906
        for (var i = 0; i < in_args.length; i++) {
 
907
            tmp_args[i] = in_args[i];
 
908
        }
 
909
 
 
910
        if (!domMenu_isKonq && !domMenu_isIE50) {
 
911
            domMenu_timeouts[in_type].setItem(in_basename, setTimeout(function() { tmp_function(tmp_args); }, in_timeout));
 
912
        }
 
913
        else {
 
914
            var tmp_data = new Array();
 
915
            tmp_data['function'] = tmp_function;
 
916
            tmp_data['args'] = tmp_args;
 
917
            domMenu_timeoutStates[in_type].setItem(in_basename, tmp_data);
 
918
            var tmp_type = domMenu_quote(in_type);
 
919
            var tmp_basename = domMenu_quote(in_basename);
 
920
 
 
921
            domMenu_timeouts[in_type].setItem(in_basename, setTimeout('domMenu_timeoutStates[' + tmp_type + '].items[' + tmp_basename + '][' + domMenu_quote('function') + '](domMenu_timeoutStates[' + tmp_type + '].items[' + tmp_basename + '][' + domMenu_quote('args') + ']); domMenu_timeoutStates[' + tmp_type + '].removeItem(' + tmp_basename + ');', in_timeout));
 
922
        }
 
923
    }
 
924
}
 
925
 
 
926
// }}}
 
927
// {{{ domMenu_cancelTimeout()
 
928
 
 
929
function domMenu_cancelTimeout(in_basename, in_type)
 
930
{
 
931
    // take advantage of browsers which use the anonymous function
 
932
    if (!domMenu_isKonq && !domMenu_isIE50) {
 
933
        clearTimeout(domMenu_timeouts[in_type].removeItem(in_basename));
 
934
    }
 
935
    else {
 
936
        // if konqueror, we only want to clearTimeout if it is still running
 
937
        if (domMenu_timeoutStates[in_type].hasItem(in_basename)) {
 
938
            clearTimeout(domMenu_timeouts[in_type].removeItem(in_basename));
 
939
            domMenu_timeoutStates[in_type].removeItem(in_basename);
 
940
        }
 
941
    }
 
942
}
 
943
 
 
944
// }}}
 
945
// {{{ domMenu_correctEdgeBleed()
 
946
 
 
947
function domMenu_correctEdgeBleed(in_width, in_height, in_x, in_y, in_padding, in_axis)
 
948
{
 
949
    if (domMenu_isIE && !domMenu_isIE5) {
 
950
        var pageHeight = document.documentElement.clientHeight;
 
951
    }
 
952
    else if (!domMenu_isKonq) {
 
953
        var pageHeight = document.body.clientHeight;
 
954
    }
 
955
    else {
 
956
        var pageHeight = window.innerHeight;
 
957
    }
 
958
 
 
959
    var pageYOffset = domMenu_isIE ? document.body.scrollTop : window.pageYOffset;
 
960
    var pageXOffset = domMenu_isIE ? document.body.scrollLeft : window.pageXOffset;
 
961
    
 
962
 
 
963
    if (in_axis == 'horizontal') {
 
964
        var bleedRight = (in_x - pageXOffset) + in_width - (document.body.clientWidth - in_padding);
 
965
        var bleedLeft = (in_x - pageXOffset) - in_padding;
 
966
 
 
967
        // we are bleeding off the right, move menu to stay on page
 
968
        if (bleedRight > 0) {
 
969
            in_x -= bleedRight;
 
970
        }
 
971
 
 
972
        // we are bleeding to the left, move menu over to stay on page
 
973
        // we don't want an 'else if' here, because if it doesn't fit we will bleed off the right
 
974
        if (bleedLeft < 0) {
 
975
            in_x += bleedLeft;
 
976
        }
 
977
    }
 
978
    else {
 
979
        var bleedTop = (in_y - pageYOffset) - in_padding;
 
980
        var bleedBottom = (in_y - pageYOffset) + in_height - (pageHeight - in_padding);
 
981
        
 
982
        // if we are bleeding off the bottom, move menu to stay on page
 
983
        if (bleedBottom > 0) {
 
984
            in_y -= bleedBottom;
 
985
        }
 
986
 
 
987
        // if we are bleeding off the top, move menu down
 
988
        // we don't want an 'else if' here, because if we just can't fit it, bleed off the bottom
 
989
        if (bleedTop < 0) {
 
990
            in_y += bleedTop;
 
991
        }
 
992
    }
 
993
    
 
994
    return new Array(in_x, in_y);
 
995
}
 
996
 
 
997
// }}}
 
998
// {{{ domMenu_toggleSubMenu()
 
999
 
 
1000
function domMenu_toggleSubMenu(in_parentElement, in_style)
 
1001
{
 
1002
    var subMenu = in_parentElement.data.items['subMenu'];
 
1003
    if (subMenu && subMenu.style.visibility != in_style) {
 
1004
        var settings = domMenu_settings.items[in_parentElement.data.items['basename']];
 
1005
        var prefix = in_parentElement.data.items['level'] == 1 ? 'menu' : 'subMenu';
 
1006
        var className = settings.items[prefix + 'ElementClass'];
 
1007
                // :BUG: this is a problem if submenus click to open, then it won't
 
1008
                // have the right class when you click to close
 
1009
                if (in_style == 'visible') {
 
1010
            className += ' ' + settings.items[prefix + 'Element' + (in_style == 'visible' ? 'Active' : 'Hover') + 'Class'];
 
1011
                }
 
1012
 
 
1013
        in_parentElement.firstChild.className = className;
 
1014
        
 
1015
        // position our submenu
 
1016
        if (in_style == 'visible') {
 
1017
            var tmp_offsets = domMenu_getOffsets(in_parentElement);
 
1018
            if (in_parentElement.data.items['level'] == 1) {
 
1019
                tmp_offsets.items['top'] += settings.items['verticalSubMenuOffsetY'];
 
1020
                tmp_offsets.items['bottom'] += settings.items['verticalSubMenuOffsetY'];
 
1021
                tmp_offsets.items['left'] += settings.items['verticalSubMenuOffsetX'];
 
1022
                tmp_offsets.items['right'] += settings.items['verticalSubMenuOffsetX'];
 
1023
            }
 
1024
 
 
1025
            // reposition if there was a change in the parent position/size
 
1026
            if (!in_parentElement.data.items['offsets'].compare(tmp_offsets)) {
 
1027
                in_parentElement.data.items['offsets'] = tmp_offsets;
 
1028
 
 
1029
                if (settings.items['axis'] == 'horizontal' && in_parentElement.data.items['level'] == 1) {
 
1030
                    var xCoor = tmp_offsets.items['left'];
 
1031
                    if (settings.items['verticalExpand'] == 'north') {
 
1032
                        var yCoor = tmp_offsets.items['top'] - subMenu.offsetHeight - settings.items['verticalSubMenuOffsetY'];
 
1033
                    }
 
1034
                    else {
 
1035
                        var yCoor = tmp_offsets.items['bottom'];
 
1036
                    }
 
1037
                }
 
1038
                else {
 
1039
                    var xCoor = tmp_offsets.items['right'] + settings.items['horizontalSubMenuOffsetX'];
 
1040
                    var yCoor = tmp_offsets.items['top'] + settings.items['horizontalSubMenuOffsetY'];
 
1041
                }
 
1042
 
 
1043
                var minWidth = settings.items['subMenuMinWidth'];
 
1044
                var renderedWidth = subMenu.offsetWidth;
 
1045
                if (minWidth == 'inherit') {
 
1046
                    minWidth = in_parentElement.offsetWidth + settings.items['subMenuWidthCorrection'];
 
1047
                }
 
1048
                else if (minWidth == 'auto') {
 
1049
                    minWidth = renderedWidth;
 
1050
                }
 
1051
 
 
1052
                if (domMenu_isKonq) {
 
1053
                    // change with width of the first cell
 
1054
                    subMenu.firstChild.firstChild.firstChild.firstChild.style.width = Math.max(minWidth, renderedWidth) + 'px';
 
1055
                }
 
1056
                else {
 
1057
                    // change the width of the table
 
1058
                    subMenu.firstChild.style.width = Math.max(minWidth, renderedWidth) + 'px';
 
1059
                }
 
1060
                
 
1061
                var coordinates = domMenu_correctEdgeBleed(subMenu.offsetWidth, subMenu.offsetHeight, xCoor, yCoor, settings.items['screenPadding'], settings.items['axis']);
 
1062
                subMenu.style.left = coordinates[0] + 'px';
 
1063
                subMenu.style.top = coordinates[1] + 'px';
 
1064
 
 
1065
                // ** if we inherit, it is necessary to check the parent element width again **
 
1066
                if (settings.items['axis'] == 'horizontal' && settings.items['subMenuMinWidth'] == 'inherit') {
 
1067
                    subMenu.firstChild.style.width = Math.max(in_parentElement.offsetWidth + settings.items['subMenuWidthCorrection'], renderedWidth) + 'px';
 
1068
                }
 
1069
            }
 
1070
        }
 
1071
 
 
1072
        // force konqueror to change the styles
 
1073
        if (domMenu_isKonq) {
 
1074
            in_parentElement.firstChild.style.display = 'none';
 
1075
            in_parentElement.firstChild.style.display = '';
 
1076
        }
 
1077
 
 
1078
        subMenu.style.visibility = in_style;
 
1079
        domMenu_detectCollisions(subMenu, (in_style == 'hidden'));
 
1080
    }
 
1081
}
 
1082
 
 
1083
// }}}
 
1084
// {{{ domMenu_toggleHighlight()
 
1085
 
 
1086
function domMenu_toggleHighlight(in_element, in_status)
 
1087
{
 
1088
    // if this is a heading, don't change the style
 
1089
    if (!in_element.data.items['numChildren'] && !in_element.data.items['uri']) {
 
1090
        return;
 
1091
    }
 
1092
 
 
1093
    var settings = domMenu_settings.items[in_element.data.items['basename']];
 
1094
    var prefix = in_element.data.items['level'] == 1 ? 'menu' : 'subMenu';
 
1095
    var className = settings.items[prefix + 'ElementClass'];
 
1096
    var highlightElement = in_element.firstChild;
 
1097
 
 
1098
    var pseudoClass;
 
1099
    if (in_status) {
 
1100
        if (in_element.data.hasItem('subMenu') && in_element.data.items['subMenu'].style.visibility == 'visible') {
 
1101
            pseudoClass = 'Active';
 
1102
        }
 
1103
        else if (in_element.data.items['numChildren'] || in_element.data.items['uri']) {
 
1104
            pseudoClass = 'Hover';
 
1105
        }
 
1106
    }
 
1107
 
 
1108
    if (pseudoClass) {
 
1109
        className += ' ' + settings.items[prefix + 'Element' + pseudoClass + 'Class'];
 
1110
        // if we are changing to hover, change the alt contents (only change if needs it)
 
1111
        if (highlightElement.childNodes.length == 2 && highlightElement.lastChild.style.display == 'none') {
 
1112
            highlightElement.firstChild.style.display = 'none';
 
1113
            highlightElement.lastChild.style.display = '';
 
1114
        }
 
1115
    }
 
1116
    else {
 
1117
        // if we are changing to non-hover, change the alt contents (only change if needs it)
 
1118
        if (highlightElement.childNodes.length == 2 && highlightElement.firstChild.style.display == 'none') {
 
1119
            highlightElement.lastChild.style.display = 'none';
 
1120
            highlightElement.firstChild.style.display = '';
 
1121
        }
 
1122
    }
 
1123
 
 
1124
    highlightElement.className = className;
 
1125
 
 
1126
    // force konqueror to change the styles
 
1127
    if (domMenu_isKonq) {
 
1128
        highlightElement.style.display = 'none';
 
1129
        highlightElement.style.display = '';
 
1130
    }
 
1131
}
 
1132
 
 
1133
// }}}
 
1134
// {{{ domMenu_resolveLink()
 
1135
 
 
1136
function domMenu_resolveLink(in_this, in_event)
 
1137
{
 
1138
    var eventObj = domMenu_isIE ? event : in_event;
 
1139
    var currentTarget = domMenu_isIE ? in_this : eventObj.currentTarget;
 
1140
    var basename = currentTarget.data.items['basename'];
 
1141
 
 
1142
    // close the menu system immediately when we resolve the uri
 
1143
    domMenu_changeActivePath(false, domMenu_activeElement.items[basename], 0);
 
1144
 
 
1145
    if (currentTarget.data.items['uri']) {
 
1146
        window.status = 'Resolving Link...';
 
1147
 
 
1148
        // open in current window
 
1149
        if (!currentTarget.data.items['target'] || currentTarget.data.items['target'] == '_self') {
 
1150
            window.location = currentTarget.data.items['uri'];
 
1151
        }
 
1152
        // open in new window
 
1153
        else {
 
1154
            window.open(currentTarget.data.items['uri'], currentTarget.data.items['target']);
 
1155
        }
 
1156
    }
 
1157
}
 
1158
 
 
1159
// }}}
 
1160
// {{{ domMenu_quote()
 
1161
 
 
1162
function domMenu_quote(in_string)
 
1163
{
 
1164
    return "'" + in_string.replace(new RegExp("'", 'g'), "\\'") + "'";
 
1165
}
 
1166
 
 
1167
// }}}