/************************************************/ /* IPB3 Javascript */ /* -------------------------------------------- */ /* ips.hoverCard.js - Delayed pop-up menu */ /* (c) IPS, Inc 2010 */ /* -------------------------------------------- */ /* Author: Matt Mecham */ /************************************************/ /* ===================================================================================================== */ /* IPB3 Hover card manager */ IPBoard.prototype.hoverCardRegister = { mainStore: $H(), initialize: function( key, options ) { var store = $H(); /* Insert into main store */ if ( ! ipb.hoverCardRegister.mainStore.get( key ) ) { ipb.hoverCardRegister.mainStore.set( key, options ); } $$('._hovertrigger').each( function( elem ) { try { _key = $(elem).readAttribute( "hovercard-ref" ); if ( key == _key ) { $(elem).addClassName( '___hover___' + key ); store.set( 'key', key ); /* remove trigger class to prevent re-iteration later */ $(elem).removeClassName('_hovertrigger'); $(elem).addClassName('_hoversetup'); } } catch( err ) { Debug.error( err ); } } ); /* Set up cards */ store.each( function( elem ) { new ipb.hoverCard( '___hover___' + elem.value, options ); } ); }, /* Post ajax update 'cos new content added via ajax won't be set up */ postAjaxInit: function() { ipb.hoverCardRegister.mainStore.each( function( elem ) { ipb.hoverCardRegister.initialize( elem.key, elem.value ); } ); } }; IPBoard.prototype.hoverCard = Class.create( { /** * options.ajaxUrl: Pass a complete ajax URL in. * options.ajaxCacheExpire: Number of seconds to store the ajax cache. 0 to not cache * options.type : Balloon* or pane * options.position : Auto, topLeft, bottomLeft*, topRight, bottomRight */ /*------------------------------*/ /* Constructor */ initialize: function( className, options ) { this.id = className; this.timer = {}, this.card = false, this.ajaxCache = {}, this.popupActive = {}, this.openId = false; this.curEvent = false; this.options = Object.extend( { type: 'balloon', position: 'bottomLeft', w: '500px', openOnClick: false, openOnHover: true, ajaxUrl: false, delay: 800, ajaxCacheExpire: 0, black: false, getId: false, setIdParam: 'id', callback: false }, arguments[1] ); this.init(); }, init: function() { this.debugWrite( "hoverCard.init()" ); /* Scoping issues #ftl */ var _hc = this; document.observe( 'mousemove', _hc.mMove.bindAsEventListener( _hc ) ); /* Set up elements */ $$('.' + this.id ).each( function( elem ) { elem.identify(); try { Event.stopObserving( $( elem.id ), 'mouseout' ); Event.stopObserving( $( elem.id ), 'mouseover' ); /* title attributes murder the pop-up effect */ $( elem.id ).writeAttribute( 'title', '' ); if ( $( elem.id ).down('a') ) { $( elem.id ).down('a').writeAttribute( 'title', '' ); } if( $( elem.id ).down('img') ) { $( elem.id ).down('img').writeAttribute( 'title', '' ); $( elem.id ).down('img').writeAttribute( 'alt', '' ); } } catch (aBall){} $( elem.id ).observe( 'contextmenu' , _hc.mContext.bindAsEventListener( _hc, elem.id ) ); $( elem.id ).observe( 'click' , _hc.mClick.bindAsEventListener( _hc, elem.id ) ); $( elem.id ).observe( 'mouseover' , _hc.mOver.bindAsEventListener( _hc, elem.id ) ); $( elem.id ).observe( 'mouseout' , _hc.mOut.bindAsEventListener( _hc, elem.id ) ); } ); }, /** * Mouse Move */ mMove: function(e) { /* IE loses reference to event, so we need to copy it instead of use original reference */ var _newEvent = {}; for( var i in e ) { _newEvent[i] = e[i]; } this.curEvent = _newEvent; }, /** * ITEM CLICK */ mClick: function(e, id) { if ( ! this.options.openOnClick ) { this.close(id); } else { if ( $(id).tagName.toLowerCase() == 'input' && $(id).type.toLowerCase() == 'checkbox' ) { /* If this is a checkbox, and we're choosing to show on click then we probably only * want to show the card if we're clicking 'true' */ if ( $(id).checked !== true ) { return true; } } this.show(id); } }, /** * ITEM Context menu */ mContext: function(e, id) { this.close(id); }, /** * ITEM MOUSEOVER */ mOver: function(e, id) { Event.stop(e); if ( this.overPopUp(id) === true ) { return false; } if ( this.options.openOnHover !== true ) { return false; } this.debugWrite( "mover - setting time OVER " + id ); if ( ! Object.isUndefined( this.timer[id + '_out'] ) ) { clearTimeout( this.timer[id + '_out'] ); } /* Set timer */ this.timer[id + '_over'] = setTimeout( this.show.bind(this, id), this.options.delay ); }, /* * ITEM MOUSEOUT */ mOut: function(e, id) { Event.stop(e); if ( this.overPopUp(id) === true ) { return false; } /* Pop up class dominates the mouseover class */ Event.stopObserving( $( id ), 'mouseover' ); $( id ).observe( 'mouseover' , this.mOver.bindAsEventListener( this, id ) ); /* Clear any time out */ if ( ! Object.isUndefined( this.timer[id + '_over'] ) ) { clearTimeout( this.timer[id + '_over'] ); } /* Set timer */ this.debugWrite( "Mout - setting time OUT " + id ); this.timer[id + '_out'] = setTimeout( this.close.bind(this, id), 800 ); }, /* * SHOW POP UP */ show: function(id) { var popup = 'pu__' + this.id + '_popup'; /* Clear any time out */ if ( ! Object.isUndefined( this.timer[id + '_out'] ) ) { clearTimeout( this.timer[id + '_out'] ); } if ( ! Object.isUndefined( this.card ) && this.card !== false ) { this.card.kill(); this.card = false; } /* If we have an old popup from a previous class method, kill it 'cos this.card won't be populated */ if ( $(popup) ) { $(popup).remove(); } /* Set this as open */ this.openId = id; var content = false; if ( this.options.ajaxUrl ) { content = "
"; } else { /* Run call back */ if ( Object.isFunction( this.options.callback ) ) { content = this.options.callback( this, id ); /* Did we decide to skip output? */ if ( content === false ) { return false; } } else { Debug.error( "No AJAX or Callback specified. Whaddayagonnado?!" ); } } this.card = new ipb.Popup( 'pu__' + this.id, { type: 'balloon', initial: content, stem: true, hideAtStart: false, hideClose: true, defer: false, black: this.options.black, attach: { target: $( id ), position: this.options.position }, w: this.options.w } ); /* Pop up class dominates the mouseover class */ Event.stopObserving( $( id ), 'mouseout' ); Event.stopObserving( $( id ), 'contextmenu' ); Event.stopObserving( $( id ), 'click' ); $( id ).observe( 'mouseout' , this.mOut.bindAsEventListener( this, id ) ); $( id ).observe( 'contextmenu' , this.mContext.bindAsEventListener( this, id ) ); $( id ).observe( 'click' , this.mClick.bindAsEventListener( this, id ) ); if ( this.options.ajaxUrl ) { this.ajax( id ); } }, /** * CLOSE POP-UP */ close: function(id) { /* Don't close if we brushed past another trigger and are now over a pop-up */ if ( this.overPopUp(id) === true ) { return false; } this.debugWrite( "Close: " + id ); /* Clear all time outs */ if ( ! Object.isUndefined( this.timer[id + '_out'] ) ) { this.debugWrite( "-- Clearing: " + id + '_out' ); clearTimeout( this.timer[id + '_out'] ); } if ( ! Object.isUndefined( this.timer[id + '_over'] ) ) { this.debugWrite( "-- Clearing: " + id + '_over' ); clearTimeout( this.timer[id + '_over'] ); } if ( ! Object.isUndefined( this.card ) && this.card !== false && id == this.openId ) { /* Hide makes card fade nicely, we kill it in open */ this.card.hide(); this.card = false; /* Set this as closed */ this.openId = false; } }, /** * Load ajax URL */ ajax: function( id ) { var now = this.unixtime(); var url = this.options.ajaxUrl; var bDims = {}; var aDims = {}; /* Before we load ajax, grab the top and height of item */ var popup = 'pu__' + this.id + '_popup'; //this.debugWrite( $(popup).innerHTML); bDims['height'] = $( popup ).getHeight(); bDims['top'] = parseInt( $( popup ).style.top ); if ( ! Object.isUndefined( this.ajaxCache[ id ] ) ) { if ( this.options.AjaxCacheExpire ) { if ( now - parseInt( this.options.AjaxCacheExpire ) < this.ajaxCache[ id ]['time'] ) { this.debugWrite( "Fetching from cache " + id ); /* Just update the card with the contents previously fetched */ this.card.update( this.ajaxCache[ id ]['content'] ); this.card.ready = true; /* Now Check after and reposition if need be */ this._rePos( bDims, popup, this.id ); return; } } else { this.debugWrite( "Fetching from cache " + id ); /* Just update the card with the contents previously fetched */ this.card.update( this.ajaxCache[ id ]['content'] ); this.card.ready = true; /* Now Check after and reposition if need be */ this._rePos( bDims, popup, this.id ); return; } } /* Fetching ID? */ if ( this.options.getId ) { var _id = $(id).readAttribute('hovercard-id'); url = url + '&' + this.options.setIdParam + '=' + _id; } this.debugWrite( "Ajax load " + id + " " + url ); new Ajax.Request( url, { method: 'get', onSuccess: function(t) { if( t.responseText != 'error' ) { if( t.responseText == 'nopermission' ) { alert( ipb.lang['no_permission'] ); return; } /* Check for log out */ if ( t.responseText.match( "__session__expired__log__out__" ) ) { this.update(''); alert( "Your session has expired, please refresh the page and log back in" ); return false; } this.debugWrite( "AJAX done!" ); this.card.update( t.responseText ); this.card.ready = true; /* Now Check after and reposition if need be */ this._rePos( bDims, popup, this.id ); this.ajaxCache[ id ] = { 'content': t.responseText, 'time': now }; } else { this.debugWrite( t.responseText ); return; } }.bind(this) }); }, _rePos: function( bDims, popup, id ) { //Debug.dir( bDims); //Debug.write('here'); //Debug.dir( aDims); //this.debugWrite(id)/; /* Now Check after and reposition if need be */ aDims = {}; aDims['height'] = $( popup ).getHeight(); aDims['top'] = parseInt( $( popup ).getStyle( 'top' ) ); if ( $( 'pu__' + id + '_stem').className.match( /top/ ) && ( aDims['height'] != bDims['height'] ) ) { var _nt = bDims['top'] - ( aDims['height'] - bDims['height'] ) - 10; $( popup ).setStyle( { 'top': _nt + 'px' } ); } }, /** * Fetch unixtime * */ unixtime: function() { var _time = new Date(); return Date.parse( _time ) / 1000; }, /** * Hovering over a pop-up? */ overPopUp: function( id ) { var myevent = this.curEvent; if( !id ){ return; } /* Moz chokes without try. */ try { if ( $(Event.findElement(myevent)) && $(Event.findElement(myevent)).descendantOf( $('pu__' + this.id + '_popup') ) ) { this.debugWrite( "*** Over Pop Up ***" ); /* Test close again */ if ( this.openId !== false ) { this.timer[id + '_out'] = setTimeout( this.close.bind(this, id), 800 ); } return true; } } catch(err){} return false; }, /** * Debug write. Allows me to turn this off quickly */ debugWrite: function( text ) { Debug.write( text ); } } );