
function getTop( node )
{
  var top = 0;
  while( node != null )
  {
    top += node.offsetTop;
    node = node.offsetParent;
  }
  return top;
}


function getLeft( node )
{
  var left = 0;
  while( node != null )
  {
    left += node.offsetLeft;
    node = node.offsetParent;
  }
  return left;
}

function parsePosition( pixels )
{
  return parseInt( pixels.substring( 0, pixels.length - 2 ) );
}


Array.prototype.indexOf = function( item )
{
  for( var i = 0; i < this.length; i++ )
  {
    if( this[i] == item )
      return i;
  }
  return -1;
};

Array.prototype.contains = function( item )
{
  return this.indexOf( item ) != -1;
};

Array.prototype.remove = function( item )
{
  var index = this.indexOf( item );
  if( index != -1 )
  {
    this.splice( index, 1 );
    return true;
  }
  return false;
};

Array.prototype._slice = function( index )
{
  var copy = [];
  for( var i = 0; i < this.length; i++ )
    copy.push( this[i] );
  return copy;
};

Array.prototype.copy = function()
{
  return this._slice( 0 );
};



function Clipboard()
{
}

Clipboard.prototype.copy = function( text )
{
  if( window.clipboardData )
  {
    window.clipboardData.setData( 'Text', text );
  }
  else
  {
    try
    {
      netscape.security.PrivilegeManager.enablePrivilege( "UniversalXPConnect" );
    }
    catch( error )
    {
      var message = 'This browser requires a configuration change to use the clipboard.\n\n' +
                    'Keep this browser open and follow the instructions below:\n\n' +
                    '1. Open a new copy of this browser.\n' +
                    '2. Type "about:config" as the url.\n' +
                    '3. Type "signed" into the filter.\n' +
                    '4. Set "signed.applets.codebase_principal_support" to true.\n' +
                    '5. Close that browser.\n' +
                    '6. Click "OK" below.\n';
      alert( message );
    }
    try
    {
      netscape.security.PrivilegeManager.enablePrivilege( "UniversalXPConnect" );
      var component = Components.classes[ "@mozilla.org/widget/clipboardhelper;1" ];
      var service = Components.interfaces.nsIClipboardHelper;
      var clipboard = component.getService( service );
      clipboard.copyString( text );
    }
    catch( error )
    {
      alert( "This browser cannot access the clipboard." );
    }
  }
};


function List()
{
  this._array = new Array();
}

List.prototype.size = function()
{
  return this._array.length;
};

List.prototype.get = function( index )
{
  return this._array[ index ];  
};

List.prototype.add = function( obj )
{
  this._array.push( obj );
};

List.prototype.indexOf = function( obj )
{
  for( var i = 0; i < this._array.length; i++ )
  {
    if( this._array[i] == obj )
      return i;
  }
  return -1;
};

List.prototype.contains = function( obj )
{
  return this.indexOf( obj ) != -1;
};

List.prototype.remove = function( obj )
{
  var index = this.indexOf( obj );
  if( index != -1 )
  {
    this._array.splice( index, 1 );
    return true;
  }
  return false;
};

List.prototype.toArray = function()
{
  return this._array;
};



function BaseEvent( e )
{
  this._event = e ? e : window.event;
}

BaseEvent.prototype.getEventType = function()
{
  return this._event.type;
};

BaseEvent.prototype.getTarget = function()
{
  return this._event.target ? this._event.target : this._event.srcElement;  
};

BaseEvent.prototype.hasTargetAncestor = function( ancestor )
{
  var node = this.getTarget();
  while( node != null && node != ancestor )
    node = node.parentNode;
  return node != null;
};

BaseEvent.prototype.isShiftDown = function()
{
  return this._event.shiftKey;  
};

BaseEvent.prototype.isCtrlDown = function()
{
  return this._event.ctrlKey;
};

BaseEvent.prototype.isAltDown = function()
{
  return this._event.altKey;  
};

BaseEvent.prototype.stop = function()
{
  if( this._event.preventDefault != undefined )
    this._event.preventDefault();
  else
    this._event.returnValue = false;
};

function MouseEvent( e )
{
  BaseEvent.prototype.constructor.call( this, e );
}

MouseEvent.prototype = new BaseEvent;

MouseEvent.prototype.getX = function()
{
  return this._event.pageX ? this._event.pageX : ( this._event.clientX + document.body.scrollLeft );
};

MouseEvent.prototype.getY = function()
{
  return this._event.pageY ? this._event.pageY : ( this._event.clientY + document.body.scrollTop );
};

MouseEvent.prototype.getButton = function()
{
  return this._event.which ? this._event.which : this._event.button;
};

function KeyEvent( e )
{
  BaseEvent.prototype.constructor.call( this, e );
}

KeyEvent.prototype = new BaseEvent;

KeyEvent.prototype.getKeyCode = function()
{
  return this._event.keyCode;
};




var KeyCode = new function()
{
  this.BACKSPACE = 8;
  this.TAB = 9;
  this.ENTER = 13;
  this.SHIFT = 16;
  this.CTRL = 17;
  this.ALT = 18;
  this.ESC = 27;
  this.PAGE_UP = 33;
  this.PAGE_DOWN = 34;
  this.END = 35;
  this.HOME = 36;
  this.LEFT = 37;
  this.UP = 38;
  this.RIGHT = 39;
  this.DOWN = 40;
  this.DELETE = 46;
};

var MouseButton = new function()
{
  this.LEFT = 1;
};

var EventType = new function()
{
  this.KEYDOWN = "keydown";
  this.KEYUP = "keyup";
  this.KEYPRESS = "keypress";
  this.MOUSEDOWN = "mousedown";
  this.MOUSEUP = "mouseup";
  this.MOUSEOVER = "mouseover";
  this.MOUSEOUT = "mouseout";
  this.MOUSEMOVE = "mousemove";  
  this.SELECTSTART = "selectstart";
  this.DRAGSTART = "dragstart";
  this.DRAG = "drag";
  this.SELECT = "select";
  this.LOAD = "load";
  this.UNLOAD = "unload";
  this.CLICK = "click";
  this.DOUBLECLICK = "dblclick";
  this.CONTEXTMENU = "contextmenu";
};



function addListener( element, type, listener )
{
  if( element.attachEvent != undefined )
    element.attachEvent( 'on' + type, listener );
  else if( element.addEventListener != undefined )
    element.addEventListener( type, listener, false );
  else
    alert( "Something bad happened." );
};

function removeListener( element, type, listener )
{
  if( element.detachEvent != undefined )
    element.detachEvent( 'on' + type, listener );
  else
    element.removeEventListener( type, listener, false );
};

function EventHandler( type, caller, method )
{
  this._type = type;
  this._method = method;  
  this._calledMethod = function( e ){ method.call( caller, e ); };
};

EventHandler.prototype.matches = function( type, method )
{
  return this._type == type && this._method == method;
};

EventHandler.prototype.getCalledMethod = function()
{
  return this._calledMethod;
};

function EventListener( caller )
{
  this._caller = caller;
  this._handlerList = new List();
  this._keysDown = new List();
  this._mouseButtonDown = null;
};

EventListener.prototype._getHandler = function( type, method )
{
  var handler = null;
  var i = 0;
  while( handler == null && i < this._handlerList.size() )
  {
    if( this._handlerList.get( i ).matches( type, method ) )
      handler = this._handlerList.get( i );
    i++;
  }
  return handler;
};

EventListener.prototype._add = function( element, type, method, caller )
{
  var handler = new EventHandler( type, caller, method );
  this._handlerList.add( handler );
  addListener( element, type, handler.getCalledMethod() );
};

EventListener.prototype._keydown = function( e )
{
  var keyCode = new KeyEvent( e ).getKeyCode();
  if( ! this._keysDown.contains( keyCode ) )
    this._keysDown.add( keyCode );
};

EventListener.prototype._keyup = function( e )
{
  var keyCode = new KeyEvent( e ).getKeyCode();
  this._keysDown.remove( keyCode );
};

EventListener.prototype._mousedown = function( e )
{
  this._mouseButtonDown = new MouseEvent( e ).getButton();
};

EventListener.prototype._mouseup = function( e )
{
  this._mouseButtonDown = null;
};

EventListener.prototype._stop = function( e )
{
  new BaseEvent( e ).stop();
};

EventListener.prototype.add = function( element, type, method )
{   
  this._add( element, type, method, this._caller );
};

EventListener.prototype.remove = function( element, type, method )
{
  var handler = this._getHandler( type, method );
  if( handler != null )
  {
    this._handlerList.remove( handler );
    removeListener( element, type, handler.getCalledMethod() );
  }
};

EventListener.prototype.stop = function( element, type )
{   
  this._add( element, type, this._stop, this );
};

EventListener.prototype.trackKeyDown = function()
{
  this._add( document, EventType.KEYDOWN, this._keydown, this );
  this._add( document, EventType.KEYUP, this._keyup, this );
};

EventListener.prototype.trackMouseDown = function()
{
  this._add( document, EventType.MOUSEDOWN, this._mousedown, this );
  this._add( document, EventType.MOUSEUP, this._mouseup, this );
};

EventListener.prototype.isMouseButtonDown = function( mouseButton )
{
  return this._mouseButtonDown == mouseButton;
};

EventListener.prototype.isKeyDown = function( keyCode )
{
  return this._keysDown.contains( keyCode );
};

//////////////////////////////////////////

function Point( x, y )
{
  this.x = x;
  this.y = y;
}

function Box( minx, miny, maxx, maxy )
{
  this.minx = minx;
  this.miny = miny;
  this.maxx = maxx;
  this.maxy = maxy;
}

Box.prototype.getWidth = function()
{
  return this.maxx - this.minx;
};

Box.prototype.getHeight = function()
{
  return this.maxy - this.miny;
};

function getPrecision( n )
{
  var decimal = n.toString().indexOf( "." );
  return decimal == -1 ? 0 : n.toString().length - decimal - 1;
};

function getCoordinatePrecision( coordToPixelRatio )
{
  return Math.abs( Math.floor( Math.log( Math.abs( coordToPixelRatio ) ) / Math.log( 10 ) ) );
};

function round( value, precision )
{
  var factor = Math.pow( 10, precision );
  return Math.round( factor * value ) / factor;
};

function onEnterKey( element, callback )
{
  if( element != null )
  {
    var listener = new EventListener();
    var fn = function( e )
    {
      var keyEvent = new KeyEvent( e );
      if( ! keyEvent.isCtrlDown() && keyEvent.getKeyCode() == KeyCode.ENTER )
        callback();
    };    
    listener.add( element, EventType.KEYUP, fn );
  }
};

function closeOnEsc()
{
  var listener = new EventListener();
  var fn = function( e )
  {
    var keyEvent = new KeyEvent( e );
    if( ! keyEvent.isCtrlDown() && keyEvent.getKeyCode() == KeyCode.ESC
        && keyEvent.getTarget().tagName != "SELECT" )
    {
      window.close();
    }
  };
  listener.add( document, EventType.KEYUP, fn );
};
