// $Id: popups.js,v 1.2 2009/11/22 22:03:26 bbannon Exp $

// The following code is used to support the small popups that give the full
// description of an event when the user moves the mouse over it.
// Thanks to Klaus Knopper (www.knoppix.com) for this script.
// It has been modified to work with the existing WebCalendar
// architecture on 02/25/2005.
//
// 03/05/2005 Prevent popup from going off screen by setting maximum width,
// which is configurable.
//
// Bubblehelp infoboxes, (c) 2002 Klaus Knopper <infobox@knopper.net>
// You can copy/modify and distribute this code under the conditions
// of the GNU GENERAL PUBLIC LICENSE Version 2.

var
  ns4,              // Are we using Netscape4?
  ie4,              // Are we using Internet Explorer Version 4?
  ie5,              // Are we using Internet Explorer Version 5 and up?
  kon,              // Are we using KDE Konqueror?
  followMe = 1,     // allow popup to follow cursor...turn off for better performance
  idiv = null,      // Pointer to infodiv container
  maxwidth = 300,   // maximum width of popup window
  popupH,           // height of popup
  popupW,           // width of popup
  px = 'px',        // position suffix with "px" in some cases
  x, y, winW, winH, // Current help position and main window size
  xoffset = 8,      // popup distance from cursor x coordinate
  yoffset = 12;     // popup distance from cursor y coordinate

function nsfix() {
  setTimeout( 'window.onresize = rebrowse', 2000 );
}

if( typeof document.getElementsBySelector == 'undefined' ) {
  /* document.getElementsBySelector( selector )
     - returns an array of element objects from the current document
       matching the CSS selector. Selectors can contain element names,
       class names and ids and can be nested. For example:

         elements = document.getElementsBySelect('div#main p a.external')

       Will return an array of all 'a' elements with 'external' in their
       class attribute that are contained inside 'p' elements that are
       contained inside the 'div' element which has id="main".

     New in version 0.4: Support for CSS2 and CSS3 attribute selectors:
     See http://www.w3.org/TR/css3-selectors/#attribute-selectors

     Version 0.4 - Simon Willison, March 25th 2003
     -- Works in Phoenix 0.5, Mozilla 1.3, Opera 7, Internet Explorer 6,
        Internet Explorer 5 on Windows.
     -- Opera 7 fails.
  */

  function getAllChildren( e ) {
    // Returns all children of element. Workaround required for IE5/Windows. Ugh.
    return ( e.all ? e.all : e.getElementsByTagName( '*' ) );
  }

  document.getElementsBySelector = function( selector ) {
    // Attempt to fail gracefully in lesser browsers.
    if( ! document.getElementsByTagName )
      return new Array();

    // Split selector in to tokens.
    var
      tokens = selector.split( ' ' ),
      currentContext = new Array( document );

    for( var i = 0; i < tokens.length; i++ ) {
      token = tokens[i].replace( /^\s+/,'' ).replace( /\s+$/,'' );

      if( token.indexOf( '#' ) > -1 ) {
        // Token is an ID selector.
        var
          bits = token.split( '#' ),
          tagName = bits[0],
          id = bits[1],
          element = document.getElementById( id );

        if( tagName && element.nodeName.toLowerCase() != tagName ) {
          // Tag with that ID not found, return false.
          return new Array();
        }
        // Set currentContext to contain just this element.
        currentContext = new Array( element );
        continue; // Skip to next token.
      }
      if( token.indexOf( '.' ) > -1 ) {
      // Token contains a class selector.
        var
          bits = token.split( '.' ),
          tagName = bits[0],
          className = bits[1];

        if( ! tagName )
          tagName = '*';

        // Get elements matching tag, filter them for class selector.
        var
          found = new Array,
          foundCount = 0;

        for( var h = 0; h < currentContext.length; h++ ) {
          var elements;
          elements = ( tagName == '*'
            ? getAllChildren( currentContext[h] )
            : currentContext[h].getElementsByTagName( tagName ) );

          for( var j = 0; j < elements.length; j++ ) {
            found[foundCount++] = elements[j];
          }
        }
        currentContext = new Array;
        var currentContextIndex = 0;
        for( var k = 0; k < found.length; k++ ) {
          if( found[k].className && found[k].className.match( new RegExp( '\\b'+className+'\\b' ) ) )
            currentContext[currentContextIndex++] = found[k];
        }
        continue; // Skip to next token.
      }
      // Code to deal with attribute selectors.
      if( token.match( /^(\w*)\[(\w+)([=~\|\^\$\*]?)=?"?([^\]"]*)"?\]$/ ) ) {
        var
          tagName = RegExp.$1,
          attrName = RegExp.$2,
          attrOperator = RegExp.$3,
          attrValue = RegExp.$4;

        if( ! tagName )
          tagName = '*';

        // Grab all of the tagName elements within current context.
        var
          found = new Array,
          foundCount = 0;

        for( var h = 0; h < currentContext.length; h++ ) {
          var elements;
            elements = ( tagName == '*'
              ? getAllChildren( currentContext[h] )
              : currentContext[h].getElementsByTagName( tagName ) );

          for( var j = 0; j < elements.length; j++ ) {
            found[foundCount++] = elements[j];
          }
        }
        currentContext = new Array;

        var
          currentContextIndex = 0,
          checkFunction; // This function will be used to filter the elements.

        switch( attrOperator ) {
          case '=': // Equality
            checkFunction = function( e ) {
              return ( e.getAttribute( attrName ) == attrValue );
            };
            break;
          case '~': // Match one of space seperated words.
            checkFunction = function( e ) {
              return ( e.getAttribute( attrName ).match( new RegExp( '\\b'+attrValue+'\\b' ) ) );
            };
            break;
          case '|': // Match start with value followed by optional hyphen.
            checkFunction = function( e ) {
              return ( e.getAttribute( attrName ).match( new RegExp( '^'+attrValue+'-?' ) ) );
            };
            break;
          case '^': // Match starts with value.
            checkFunction = function( e ) {
              return ( e.getAttribute(attrName ).indexOf( attrValue ) == 0);
            };
            break;
          case '$': // Match ends with value - fails with "Warning" in Opera 7.
            checkFunction = function( e ) {
              return ( e.getAttribute( attrName ).lastIndexOf( attrValue ) == e.getAttribute( attrName ).length - attrValue.length );
            };
            break;
          case '*': // Match ends with value.
            checkFunction = function( e ) {
              return ( e.getAttribute( attrName ).indexOf( attrValue ) > -1 );
            };
            break;
          default: // Just test for existence of attribute.
            checkFunction = function( e ) {
              return e.getAttribute( attrName );
            };
        }
        currentContext = new Array;

        var currentContextIndex = 0;

        for( var k = 0; k < found.length; k++ ) {
          if( checkFunction( found[k] ) )
            currentContext[currentContextIndex++] = found[k];
        }
        continue; // Skip to next token.
      }
      // If we get here, token is JUST an element (not a class or ID selector).
      tagName = token;

      var
        found = new Array,
        foundCount = 0;

      for( var h = 0; h < currentContext.length; h++ ) {
        var elements = currentContext[h].getElementsByTagName( tagName );

        for( var j = 0; j < elements.length; j++ ) {
          found[foundCount++] = elements[j];
        }
      }
      currentContext = found;
    }
    return currentContext;
  }

  /* That revolting regular expression explained.
  /^(\w+)\[(\w+)([=~\|\^\$\*]?)=?"?([^\]"]*)"?\]$/
    \---/  \---/\-------------/    \-------/
      |      |         |               |
      |      |         |           The value
      |      |    ~,|,^,$,* or =
      |   Attribute
     Tag
  */
}

function rebrowse() {
  window.location.reload();
}

function infoinit() {
  ns4 = ( document.layers );
  ie4 = ( document.all );
  ie5 = ( ie4
    && ( navigator.userAgent.indexOf( 'MSIE 5' ) > 0
      || navigator.userAgent.indexOf( 'MSIE 6' ) > 0
      || navigator.userAgent.indexOf( 'MSIE 7' ) > 0 ) );
  kon = ( navigator.userAgent.indexOf( 'konqueror' ) > 0 );
  idiv = null;
  winW = 800;
  winH = 600;
  x = y = 0;

  if( followMe ) {
    document.onmousemove = mousemove;

    if( ns4 && document.captureEvents )
      document.captureEvents( Event.MOUSEMOVE );
  }
  // Workaround for another Netscape bug: Fix browser confusion on resize.
  // Obviously Konqueror has a similar problem. :-(
  if( ns4 || kon )
    nsfix();

  if( ns4 )
    px = '';

  var entries = document.getElementsBySelector( 'a.entry' );

  entries = entries.concat( document.getElementsBySelector( 'a.layerentry' ) );
  entries = entries.concat( document.getElementsBySelector( 'a.unapprovedentry' ) );
  entries = entries.concat( document.getElementsBySelector( 'tr.task' ) );
  for( var i = 0; i < entries.length; i++ ) {
    entries[i].onmouseover = function( event ) {
      showPopUp( event, 'eventinfo-' + this.id );
      return true;
    }
    entries[i].onmouseout = function() {
      hidePopUp( 'eventinfo-' + this.id );
      return true;
    }
  }
}

function hidePopUp( name ) {
  idiv.style.visibility = ( ns4 ? 'hide' : 'hidden' );
  idiv = null;
}

function gettip( name ) {
  return ( document.layers && document.layers[name]
    ? document.layers[name]
    : ( document.all && document.all[name]
      ? document.all[name]
      : ( document[name]
        ? document[name]
        : ( document.getElementById( name )
          ? document.getElementById( name )
          : 0 ) ) ) );
}

function showPopUp( evt, name ) {
  if( idiv )
    hide( name );

  idiv = gettip( name );

  if( idiv ) {
    scrollX = scrollY = 0;

    scrollX = ( typeof window.pageXOffset == 'number'
      ? window.pageXOffset
      : ( document.documentElement && document.documentElement.scrollLeft
        ? document.documentElement.scrollLeft
        : ( document.body && document.body.scrollLeft
          ? document.body.scrollLeft
          : window.scrollX ) ) );
    scrollY = ( typeof window.pageYOffset == 'number'
      ? window.pageYOffset
      : ( document.documentElement && document.documentElement.scrollTop
        ? document.documentElement.scrollTop
        : ( document.body && document.body.scrollTop
          ? document.body.scrollTop
          : window.scrollY ) ) );
    winW = ( window.innerWidth
      ? window.innerWidth + window.pageXOffset - 16
      : document.body.offsetWidth - 20 );
    winH = ( window.innerHeight
      ? window.innerHeight
      : ( ie5
        ? 500
        : document.body.offsetHeight ) ) + scrollY;

    popupW = idiv.offsetWidth;
    popupH = idiv.offsetHeight;

    showtip( evt );
  }
}

function recursive_resize( ele, width, height ) {
  if( ele.nodeType != 1 )
    return;

  if( width != null && ele.offsetWidth > width )
    ele.style.width = width + px;

  if( height != null && ele.offsetHeight > height )
    ele.style.height = height + px;

  for( var i = 0; i < ele.childNodes.length; i++ ) {
    recursive_resize( ele.childNodes[i],
      width - ele.childNodes[i].offsetLeft,
      height - ele.childNodes[i].offsetTop );
  }
}

function showtip( e ) {
  e = ( e ? e : window.event );

  if( idiv ) {
    if( e ) {
      x = ( e.pageX
        ? e.pageX
        : ( e.clientX
          ? e.clientX + scrollX
          : 0 ) );
      y = ( e.pageY
        ? e.pageY
        : ( e.clientY
          ? e.clientY + scrollY
          : 0 ) );
    } else {
      x = y = 0;
    }
    // Make sure we don't go off screen.
    recursive_resize( idiv, maxwidth );
    popupW = idiv.offsetWidth;
    popupH = idiv.offsetHeight;
    idiv.style.top = ( y + popupH + yoffset > winH - yoffset
      ? ( winH - popupH - yoffset < 0 ? 0 : winH - popupH - yoffset )
      : y + yoffset ) + px ;
    idiv.style.left = ( x + popupW + xoffset > winW - xoffset
      ? x - popupW - xoffset : x + xoffset ) + px;
    idiv.style.visibility = ( ns4 ? 'show' : 'visible' );
  }
}

function mousemove( e ) {
  showtip( e );
}
// Initialize after loading the page.
if( typeof addLoadHandler == 'undefined' ) {
  function addLoadHandler( handler ) {
    if( window.addEventListener ) {
      window.addEventListener( 'load',handler,false);
    } else if( window.attachEvent ) {
      window.attachEvent( 'onload',handler );
    } else if( window.onload ) {
      var oldHandler = window.onload;

      window.onload = function piggyback() {
        oldHandler();
        handler();
      };
    } else {
      window.onload = handler;
    }
  }
}

addLoadHandler( infoinit );
