138 lines
3.9 KiB
JavaScript
138 lines
3.9 KiB
JavaScript
/**
|
|
* Handles CSS-drive menu bar
|
|
*
|
|
* Copyright (C) 2012 Mike Gerwitz
|
|
*
|
|
* This program is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU Affero General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU Affero General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Affero General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
|
|
/**
|
|
* Basic MenuBar
|
|
*
|
|
* This class provides basic scripting for a menu bar; it expected the majority
|
|
* of the menu bar to be handled via CSS.
|
|
*/
|
|
ltjs.ui.MenuBar = Class( 'MenuBar',
|
|
{
|
|
/**
|
|
* DOM element representing the menu bar
|
|
* @type {Element}
|
|
*/
|
|
'private _bar': null,
|
|
|
|
|
|
/**
|
|
* Initialize menu bar with a DOM element
|
|
*
|
|
* The provided DOM element should be a UL element with each LI element
|
|
* containing an A element, containing the title, and a sub-UL element
|
|
* containing the sub-menu.
|
|
*
|
|
* @param {Element} element DOM element representing menu bar
|
|
*/
|
|
__construct: function( element )
|
|
{
|
|
// we'll need an id set for this element
|
|
if ( !element.id )
|
|
{
|
|
element.id = '__menubar';
|
|
}
|
|
|
|
this._bar = element;
|
|
this._initMenuActivation();
|
|
},
|
|
|
|
|
|
/**
|
|
* Initialize menu activation
|
|
*
|
|
* When any menu item is clicked, the menu bar will be considered active
|
|
* (focus), which will apply a CSS class to the menu bar. On mouse out the
|
|
* active state will be cleared.
|
|
*
|
|
* @return {undefined}
|
|
*/
|
|
'private _initMenuActivation': function()
|
|
{
|
|
var _self = this,
|
|
id = this._bar.id,
|
|
menus = this._bar.parentNode.querySelectorAll( '#'+id+' > li > a' ),
|
|
i = menus.length,
|
|
|
|
click = function( event )
|
|
{
|
|
event.target.parentNode.parentNode.className += ' focus';
|
|
return false;
|
|
};
|
|
|
|
// on menu click, apply focus class (this allows the menu to be opened
|
|
// properly on click rather than a simple CSS hover menu)
|
|
while ( i-- )
|
|
{
|
|
// ultimately we'll want to use onmousedown, but we'll leave it as
|
|
// onclick for now since we don't offer mouseup
|
|
menus[ i ].addEventListener( 'click', click );
|
|
}
|
|
|
|
this._hookMenuMouseOut();
|
|
},
|
|
|
|
|
|
/**
|
|
* Clear active (focus) on mouse out
|
|
*
|
|
* To determine if the mouse has left the menu bar, we need to ensure that
|
|
* the mouse has not been moved over any child element (as we do not want to
|
|
* close the menu).
|
|
*
|
|
* @return {undefined}
|
|
*/
|
|
'private _hookMenuMouseOut': function()
|
|
{
|
|
var _self = this,
|
|
bar = this._bar;
|
|
|
|
this._bar.addEventListener( 'mouseout', function( event )
|
|
{
|
|
// do not close the menu if our new target is a menu child element
|
|
if ( _self._isNodeOrChildOf( bar, event.relatedTarget ) )
|
|
{
|
|
return;
|
|
}
|
|
|
|
bar.className = bar.className.replace( ' focus', '' );
|
|
} );
|
|
},
|
|
|
|
|
|
/**
|
|
* Determines if the given node is a child of, or is, the given parent node
|
|
*
|
|
* @param {Element} parent parent node
|
|
* @param {Element} node node to check
|
|
*
|
|
* @return {boolean} TRUE if child or node itself, otherwise FALSE
|
|
*/
|
|
'private _isNodeOrChildOf': function( parent, node )
|
|
{
|
|
if ( !node || !node.parentNode ) return false;
|
|
|
|
return ( node === parent )
|
|
? true
|
|
: this._isNodeOrChildOf( parent, node.parentNode );
|
|
}
|
|
} );
|
|
|