/************************************************/ /* IPB3 Javascript */ /* -------------------------------------------- */ /* ips.menu.js - Me n you class <3 */ /* (c) IPS, Inc 2008 */ /* -------------------------------------------- */ /* Author: Rikki Tissier */ /************************************************/ /* ipb.menus is a menu manager; ipb.Menu is a menu object */ var _menu = window.IPBoard; _menu.prototype.menus = { registered: $H(), readyForMoving: false, init: function() { Debug.write("Initializing acp.menu.js"); document.observe("dom:loaded", function(){ ipb.menus.readyForMoving = true; ipb.menus.initEvents(); }); }, initEvents: function() { // Set document event Event.observe( document, 'click', ipb.menus.docCloseAll ); // Auto-find menus $$('.ipbmenu').each( function(menu){ id = menu.identify(); if( $( id + "_menucontent" ) ) { //$$('body')[0].insert( $( id + "_menucontent" ) ); new ipb.Menu( menu, $( id + "_menucontent" ) ); } }); }, register: function( source, obj ) { ipb.menus.registered.set( source, obj ); }, docCloseAll: function( e ) { try { if ( Event.element(e).up('.ipbmenu_content') ) { return false; } } catch (e) { return false; } if( ( !Event.isLeftClick(e) || e.ctrlKey == true || e.keyCode == 91 ) && !Prototype.Browser.IE ) // IE handles this fine anyway { // This line caused the IPB3 preview to break spectacularly. // Left here for the memories. Dont uncomment. Fair warning. //Event.stop(e); } else { ipb.menus.closeAll( e ); } }, closeAll: function( except ) { ipb.menus.registered.each( function(menu, force){ if( typeof( except ) == 'undefined' || ( except && menu.key != except ) ) { try{ menu.value.doClose(); } catch(err) { // Assume this menu gone byebye } } }); } }; ipb.menus.init(); _menu.prototype.Menu = Class.create({ initialize: function( source, target, options, callbacks ){ if( !$( source ) || !$( target ) ){ return; } if( !$( source ).id ){ $( source ).identify(); } this.id = $( source ).id + '_menu'; this.source = $( source ); this.target = $( target ); this.callbacks = callbacks || {}; this.options = Object.extend( { eventType: 'click', stopClose: false, offsetX: 0, offsetY: 0 }, arguments[2] || {}); // Set up events $( source ).observe( 'click', this.eventClick.bindAsEventListener( this ) ); $( source ).observe( 'mouseover', this.eventOver.bindAsEventListener( this ) ); $( target ).observe( 'click', this.targetClick.bindAsEventListener( this ) ); if( this.options.callback ) { this.options.callback.each( function(c){ $( source ).observe( c.key, c.value ); }); } if( ipb.menus.readyForMoving ){ $$('body')[0].insert( { bottom: $( this.target ) } ); } else { // Move into body document.observe("dom:loaded", function(){ $$('body')[0].insert( { bottom: $( this.target ) } ); }.bind(this)); } // Set up target $( this.target ).setStyle( 'position: absolute;' ).hide().setStyle( { zIndex: 20000 } ); $( this.target ).descendants().each( function( elem ){ $( elem ).setStyle( { zIndex: 20000 } ); }); ipb.menus.register( $( source ).id, this ); if( Object.isFunction( this.callbacks['afterInit'] ) ) { this.callbacks['afterInit']( this ); } }, doOpen: function() { Debug.write("Menu open"); var pos = {}; _source = ( this.options.positionSource ) ? this.options.positionSource : this.source; // This is the positioned offset of the source element var sourcePos = $( _source ).positionedOffset(); // Cumulative offset (actual position on the page, e.g. if you scrolled down it could be higher than max resolution height) var _sourcePos = $( _source ).cumulativeOffset(); // Cumulative offset of your scrolling (how much you have scrolled) var _offset = $( _source ).cumulativeScrollOffset(); // Real source position: Actual position on page, minus scroll offset (provides position on page within viewport) var realSourcePos = { top: _sourcePos.top - _offset.top, left: _sourcePos.left - _offset.left }; // Dimensions of source object var sourceDim = $( _source ).getDimensions(); // Viewport dimensions (e.g. 1280x1024) var screenDim = document.viewport.getDimensions(); // Target dimensions var menuDim = $( this.target ).getDimensions(); // Some logging Debug.write( "realSourcePos: " + realSourcePos.top + " x " + realSourcePos.left ); Debug.write( "sourcePos: " + sourcePos.top + " x " + sourcePos.left ); Debug.write( "scrollOffset: " + _offset.top + " x " + _offset.left ); Debug.write( "_sourcePos: " + _sourcePos.top + " x " + _sourcePos.left ); Debug.write( "sourceDim: " + sourceDim.width + " x " + sourceDim.height); Debug.write( "menuDim: " + menuDim.height + " x " + menuDim.width ); Debug.write( "screenDim: " + screenDim.height ); Debug.write( "manual ofset: " + this.options.offsetX + " x " + this.options.offsetY ); // Ok, if it's a relative parent, do one thing, else be normal _a = $(_source).getOffsetParent(); _b = $(this.target).getOffsetParent(); if( _a != _b ) { Debug.write( "a unequal to b" ); // Left if( ( realSourcePos.left + menuDim.width ) > screenDim.width ){ diff = menuDim.width - sourceDim.width; if( Prototype.Browser.IE7 ) { pos.left = (sourcePos.left - diff) + this.options.offsetX; } else { pos.left = (_sourcePos.left - diff) + this.options.offsetX; } } else { pos.left = _sourcePos.left + this.options.offsetX; } // Top /* If there's no space to open downwards, open upwards *unless* /* it would go off the top of the screen (i.e. < 0px ) Bug #18270 */ if( ( ( ( realSourcePos.top + sourceDim.height ) + menuDim.height ) > screenDim.height ) && ( _sourcePos.top - menuDim.height + this.options.offsetY ) > 0 ) { pos.top = _sourcePos.top - menuDim.height + this.options.offsetY; } else { pos.top = _sourcePos.top + sourceDim.height + this.options.offsetY; } Debug.write("Top: " + pos.top ); } else { Debug.write( "a equal to b" ); // Left if( ( realSourcePos.left + menuDim.width ) > screenDim.width ){ diff = menuDim.width - sourceDim.width; pos.left = sourcePos.left - diff + this.options.offsetX; } else { pos.left = sourcePos.left + this.options.offsetX; } // Top /* If there's no space to open downwards, open upwards *unless* /* it would go off the top of the screen (i.e. < 0px ) Bug #18270 */ if( ( ( ( realSourcePos.top + sourceDim.height ) + menuDim.height ) > screenDim.height ) && ( _sourcePos.top - menuDim.height + this.options.offsetY ) > 0 ) { pos.top = sourcePos.top - menuDim.height + this.options.offsetY; } else { pos.top = sourcePos.top + sourceDim.height + this.options.offsetY; } Debug.write("Top: " + pos.top ); } // Now set pos Debug.write("Menu position: " + pos.top + " x " + pos.left ); $( this.target ).setStyle( 'top: ' + (pos.top-1) + 'px; left: ' + pos.left + 'px;' ); /*finalPos['left'] = sourcePos[0] - delta[0]; finalPos['top'] = sourcePos[1] - delta[1] + screenScroll.top;*/ // Now try and keep it on screen /*if( ( finalPos['left'] + menuDim.width ) > screenDim.width ){ finalPos['left'] = finalPos['left'] - ( menuDim.width - sourceDim.width ); } if( ( ( finalPos['top'] + sourceDim.height ) + menuDim.height ) > ( screenDim.height + screenScroll.top ) ){ finalPos['top'] = finalPos['top'] - menuDim.height; } else { finalPos['top'] = finalPos['top'] + sourceDim.height; } finalPos['left'] += this.options.offsetX; finalPos['top'] += this.options.offsetY;*/ // Now position //$( this.target ).setStyle( 'top: ' + (finalPos.top-1) + 'px; left: ' + finalPos.left + 'px;' ); // Left /*if( ( sourcePos.left + menuDim.width ) > ( screenDim.width ) ){ diff = menuDim.width - sourceDim.width; pos.left = sourcePos.left - diff + this.options.offsetX; } else { pos.left = sourcePos.left + this.options.offsetX; } // Top if( ( ( sourcePos.top + sourceDim.height ) + menuDim.height ) > ( screenDim.height + screenScroll.top ) ){ pos.top = sourcePos.top - menuDim.height + this.options.offsetY; } else { pos.top = sourcePos.top + sourceDim.height + this.options.offsetY; } // Now set pos $( this.target ).setStyle( 'top: ' + (pos.top-1) + 'px; left: ' + pos.left + 'px;' );*/ // And show new Effect.Appear( $( this.target ), { duration: 0.2, afterFinish: function(e){ if( Object.isFunction( this.callbacks['afterOpen'] ) ) { this.callbacks['afterOpen']( this ); } }.bind(this) } ); }, doClose: function() { new Effect.Fade( $( this.target ), { duration: 0.3, afterFinish: function(e){ if( Object.isFunction( this.callbacks['afterClose'] ) ) { this.callbacks['afterClose']( this ); } }.bind( this ) } ); }, targetClick: function(e) { if( this.options.stopClose ) { Event.stop(e); } }, eventClick: function(e) { Event.stop(e); if( $( this.target ).visible() ){ if( Object.isFunction( this.callbacks['beforeClose'] ) ) { this.callbacks['beforeClose']( this ); } this.doClose(); } else { ipb.menus.closeAll( $(this.source).id ); if( Object.isFunction( this.callbacks['beforeOpen'] ) ) { this.callbacks['beforeOpen']( this ); } this.doOpen(); } }, eventOver: function() { } });