/*
 ---------------------------------------------------------------------------
 (c) 2003 Geotalk, Arne Marius Hallum
 ---------------------------------------------------------------------------
 15/05/2003
 This is the first attempt on making a javascript library to assist in
 handling the differences of various browsers.

 The following code is based on 'object detection' rather than on
 'browser detection'.
 Object detection is a better way to sniff browser capabilities, since it
 does not have to know every browsers difference.

 --
 addGTGlobalHandler will be called automatically, it sets some flags reflecting
 what the browser supports, as well as the correct gtGetElementById function.

 After that, use addGTHandlers to add event handling to the various elements.

*/

// ---------------------------------------------------------------------------
// Public interface
// ---------------------------------------------------------------------------
var gtGetElementById    = function(id)         { return null; };
var gtGetElementByNamme = function(name)       { return null; };
var gtCancelBubble      = function(evt)        {};
var gtStopContextMenu   = function(doc, stop)  { doc.oncontextmenu = function() { return !stop; }; };
var gtAddEventListener  = function(e, name, handler) {};
var gtDelEventListener  = function(e, name, handler) {};

// Methods used to set size for elements with padding (subtracts padding on W3C browsers)
var gtSetPaddedWidth;
var gtSetPaddedHeight;

// When you install GT event handlers for an element by calling addGTHandlers(element),
// the default methods below will be installed:
//
//  * To disable default handling of an event, return gtPreventDefault() from your handler.
//  * To cancel bubbling of the event call gtCancelBubble(evt) from within your handler.
//
// Not all events can be prevented from cancelling or bubbling!

var gtClick           = function(evt) { return true; };
var gtMouseDown       = function(evt) { return true; };
var gtMouseUp         = function(evt) { return true; };
var gtMouseMove       = function(evt) { return true; };
var gtMouseOver       = function(evt) { return true; };
var gtMouseOut        = function(evt) { return true; };


// ---------------------------------------------------------------------------
// Picks up what the browser supports and sets up the correct gtGetElementById
// ---------------------------------------------------------------------------

var g_supports_event_params = true;  // Event handlers receive the event object directly, false if window.event must be used
var g_supports_document_all = false; // The documents.all collection is supported
var g_needs_direct_access   = false; // Elements must be accessed directly: eval("document." + var);
var g_supports_event_2_0    = false; // Implementation supports modern event handling

function addGTGlobalHandler()
{
  // Capability- and browser-sniffing
  if (document.all)
    g_supports_document_all = true;
  if (document.layers)
    g_needs_direct_access   = true;
  if (window.navigator.userAgent.indexOf("MSIE") >= 0) // Cannot test for window.event since it isn't initialized early enough!!! (global scope)
    g_supports_event_params = false;

  // Select gtGetElementById function
  if (document.getElementById)
  {
    gtGetElementById   = intGetElementById;
    gtGetElementByName = intGetElementById;
  }
  else if (g_supports_document_all)
  {
    gtGetElementById   = intGetElementAll;
    gtGetElementByName = intGetElementAll;
  }
  else if (g_needs_direct_access)
  {
    gtGetElementById   = intGetElementDirect;
    gtGetElementByName = intGetElementDirect;
  }

  // Select gtCancelBubble
  if (g_supports_event_params)
  {
    gtCancelBubble    = intCancelBubbleOther;
    gtPreventDefault  = intPreventDefaultOther;
    gtSetPaddedWidth  = intSetPaddedWidthW3C;
    gtSetPaddedHeight = intSetPaddedHeightW3C;
  }
  else
  {
    gtCancelBubble    = intCancelBubbleIE;
    gtPreventDefault  = intPreventDefaultIE;
    gtSetPaddedWidth  = intSetPaddedWidthIE;
    gtSetPaddedHeight = intSetPaddedHeightIE;
  }

  if (document.implementation &&
      document.implementation.hasFeature &&
      document.implementation.hasFeature("Events", "2.0"))
  {
    g_supports_event_2_0 = true;
  }

  if (window.addEventListener)
  {
    gtAddEventListener = intAddEventListenerW3C;
    gtDelEventListener = intDelEventListenerW3C;
  }
  else
  {
    gtAddEventListener = intAddEventListenerIE;
    gtDelEventListener = intDelEventListenerIE;
  }
    
  addDocumentHandler(document);
} // end addGTGlobalHandler()



// ---------------------------------------------------------------------------
// Platform-specific Event handlers
//
// All events (regardless of platform) are mutated to include the following
// properties:
//    int     gtX
//    int     gtY
//    element gtElement
//    int     gtKeyCode
//    bool    gtShiftKey
//    int     gtButton
//              0: left
//              1: middle
//              2: right
//
// ---------------------------------------------------------------------------


function mutateW3CEventToGT(evt)
{
  // Used information from http://www.quirksmode.org/js/events_compinfo.html
  evt.gtX        = evt.clientX;
  evt.gtY        = evt.clientY;
  evt.gtElement  = evt.currentTarget;
  evt.gtKeyCode  = evt.keyCode;
  evt.gtShiftKey = evt.shiftKey;
  evt.gtButton   = (evt.button == 2) ? 2 : 0;
} // end mutateW3CEventToGT()

function intClickParam(evt)
{
  mutateW3CEventToGT(evt);
  return evt.currentTarget.gtClick(evt);
} // end intClickParam()

function intMouseDownParam(evt)
{
  mutateW3CEventToGT(evt);
  return evt.currentTarget.gtMouseDown(evt);
} // end intMouseDownParam()

function intMouseUpParam(evt)
{
  mutateW3CEventToGT(evt);
  return evt.currentTarget.gtMouseUp(evt);
} // end intMouseUpParam()

function intMouseMoveParam(evt)
{
  mutateW3CEventToGT(evt);
  return evt.currentTarget.gtMouseMove(evt);
} // end intMouseMoveParam()

function intMouseOverParam(evt)
{
  mutateW3CEventToGT(evt);
  return evt.currentTarget.gtMouseOver(evt);
} // end intMouseOverParam()

function intMouseOutParam(evt)
{
  mutateW3CEventToGT(evt);
  return evt.currentTarget.gtMouseOut(evt);
} // end intMouseOutParam()



// ---------------------------------------------------------------------------
// Event handlers (IE versions)
// ---------------------------------------------------------------------------

function mutateIEEventToGT(evt)
{
  if (evt)
  {
    evt.gtX        = evt.clientX;
    evt.gtY        = evt.clientY;
    evt.gtElement  = evt.srcElement;
    evt.gtKeyCode  = evt.keyCode;
    evt.gtShiftKey = evt.shiftKey;
    evt.gtButton   = (evt.button == 2) ? 2 : 0;
  }
  return evt;
} // end mutateIEEventToGT()

function intClickNoParam()
{
  var evt = mutateIEEventToGT(event);
  return this.gtClick(evt);
} // end intClickNoParam()

function intMouseDownNoParam()
{
  var evt = mutateIEEventToGT(event);
  return this.gtMouseDown(evt);
} // end intMouseDownNoParam()

function intMouseUpNoParam()
{
  var evt = mutateIEEventToGT(event);
  return this.gtMouseUp(evt);
} // end intMouseUpNoParam()

function intMouseMoveNoParam()
{
  var evt = mutateIEEventToGT(event);
  return this.gtMouseMove(evt);
} // end intMouseMoveNoParam()

function intMouseOverNoParam()
{
  var evt = mutateIEEventToGT(event);
  return this.gtMouseOver(evt);
} // end intMouseOverNoParam()

function intMouseOutNoParam()
{
  var evt = mutateIEEventToGT(event);
  return this.gtMouseOut(evt);
} // end intMouseOutNoParam()



// ---------------------------------------------------------------------------
// Document handling
// ---------------------------------------------------------------------------

function addDocumentHandler(doc)
{
  if (g_supports_event_params)
  {
    doc.onmousemove = intDocGTMouseMoveParam;
    doc.onmouseup   = intDocGTMouseUpParam;
  }
  else
  {
    doc.onmousemove = intDocGTMouseMoveNoParam;
    doc.onmouseup   = intDocGTMouseUpNoParam;
  }

  doc.gtMouseMove = gtMouseMove;
  doc.gtMouseUp   = gtMouseUp;
} // end addDocumentHandler()



function intDocGTMouseMoveParam(evt)
{
  mutateW3CEventToGT(evt);
  return this.gtMouseMove(evt);
} // end intDocGTMouseMoveParam()



function intDocGTMouseMoveNoParam()
{
  var evt = mutateIEEventToGT(event);
  return this.gtMouseMove(evt);
} // end intDocGTMouseMoveNoParam()



function intDocGTMouseUpParam(evt)
{
  mutateW3CEventToGT(evt);
  return this.gtMouseUp(evt);
} // end intDocGTMouseUpParam()



function intDocGTMouseUpNoParam()
{
  var evt = mutateIEEventToGT(event);
  return this.gtMouseUp(evt);
} // end intDocGTMouseUpNoParam()



function intAddEventListenerIE(e, name, handler)
{
  e.attachEvent("on" + name, handler);
} // end intAddEventListenerIE()



function intDelEventListenerIE(e, name, handler)
{
  e.detachEvent("on" + name, handler)
} // end intDelEventListenerIE()



function intAddEventListenerW3C(e, name, handler)
{
  e.addEventListener(name, handler, false);
} // end intAddEventListenerW3C()



function intDelEventListenerW3C(e, name, handler)
{
  e.removeEventListener(name, handler, false);
} // end intDelEventListenerW3C()



function intSetPaddedWidthW3C (e, v)
{
  var p = parseInt(e.style.paddingLeft) + parseInt(e.style.paddingRight);
  p += parseInt(e.style.borderLeftWidth) + parseInt(e.style.borderRightWidth);
  e.style.width  = Math.max(v - p, 0) + "px";
} // end intSetPaddedWidthW3C()

function intSetPaddedHeightW3C(e, v)
{
  var p = parseInt(e.style.paddingTop) + parseInt(e.style.paddingBottom);
  p += parseInt(e.style.borderTopWidth) + parseInt(e.style.borderBottomWidth);
  e.style.height = Math.max(v - p, 0) + "px";
} // end intSetPaddedHeightW3C()



function intSetPaddedWidthIE (e, v)
{
  var p = 0; //parseInt(e.style.borderLeftWidth) + parseInt(e.style.borderRightWidth);
//  var p = parseInt(e.style.paddingLeft) + parseInt(e.style.paddingRight);
  e.style.width  = Math.max(v - p, 0) + "px";
} // end intSetPaddedWidthIE()

function intSetPaddedHeightIE(e, v)
{
  var p = 0; //parseInt(e.style.borderTopWidth) + parseInt(e.style.borderBottomWidth);
//  var p = parseInt(e.style.paddingTop) + parseInt(e.style.paddingBottom);
  e.style.height = Math.max(v - p, 0) + "px";
} // end intSetPaddedHeightIE()



// ---------------------------------------------------------------------------
// Element event handling
// ---------------------------------------------------------------------------

function addGTHandlers(element)
{
  // Set default generic event handlers for the element
  element.gtClick     = gtClick;
  element.gtMouseDown = gtMouseDown;
  element.gtMouseUp   = gtMouseUp;
  element.gtMouseMove = gtMouseMove;
  element.gtMouseOver = gtMouseOver;
  element.gtMouseOut  = gtMouseOut;

  // Hook up the built-in events to a browser-dependent handler that will route the event
  // on to the generic event handler
  if (g_supports_event_params)
  {
    // most other browsers
    element.onclick       = intClickParam;
    element.onmousedown   = intMouseDownParam;
    element.onmouseup     = intMouseUpParam;
    element.onmousemove   = intMouseMoveParam;
    element.onmouseover   = intMouseOverParam;
    element.onmouseout    = intMouseOutParam;
  }
  else
  {
    // mainly IE
    element.onclick       = intClickNoParam;
    element.onmousedown   = intMouseDownNoParam;
    element.onmouseup     = intMouseUpNoParam;
    element.onmousemove   = intMouseMoveNoParam;
    element.onmouseover   = intMouseOverNoParam;
    element.onmouseout    = intMouseOutNoParam;
  }
} // end addGTHandlers()


function delGTHandlers(element)
{
  element.onclick       = null;
  element.onmousedown   = null;
  element.onmouseup     = null;
  element.onmousemove   = null;
  element.onmouseover   = null;
  element.onmouseout    = null;
  element.gtClick       = undefined;
  element.gtMouseDown   = undefined;
  element.gtMouseUp     = undefined;
  element.gtMouseMove   = undefined;
  element.gtMouseOver   = undefined;
  element.gtMouseOut    = undefined;
} // end delGTHandlers()

// NB for all get-functions: Opera returns propertyvalues in uppercase.
// Use .toLowerCase() to fix it. (only found this in a newsgroup, not verified).

//gtGetElementById-functions
//IE4 does not implement getElementById()


// ---------------------------------------------------------------------------
// Browser-dependent access functions
// ---------------------------------------------------------------------------

function intGetElementAll(name, owner_doc)
{
  if (!owner_doc)
    owner_doc = document;
  return owner_doc.all(name);
} // end intGetElementAll()

function intGetElementById(name, owner_doc)
{
  if (!owner_doc)
    owner_doc = document;
  return owner_doc.getElementById(name);
} // end intGetElementById()

function intGetElementDirect(name, owner_doc)
{
  if (!owner_doc)
    return eval("document." + name);
  else
    return eval(owner_doc.id + "." + name);
} // end intGetElementDirect()

function intCancelBubbleOther(evt)
{
  evt.stopPropagation();
} // end intCancelBubbleOther()

function intCancelBubbleIE(evt)
{
  evt.cancelBubble = true;
} // end intCancelBubbleIE()

function intPreventDefaultOther(evt)
{
  evt.preventDefault();
  return false;
} // end intPreventDefaultOther()

function intPreventDefaultIE(evt)
{
  evt.returnValue = false;
  return false;
} // end intPreventDefaultIE()


// ---------------------------------------------------------------------------
// Mouse capture
// ---------------------------------------------------------------------------

function gtSetMouseCapture(element, on)
{
  if (g_supports_event_2_0)
  {
    w3cSetMouseCapture(element, on);
  }
  else
  {
    if (on)
      element.setCapture();
    else
      top.document.releaseCapture();
  }
} // end gtSetMouseCapture()



//var capture = ["click", "mousedown", "mouseup", "mousemove", "mouseover", "mouseout" ]; 
var capture = ["mouseup", "mousemove" ]; 

function w3cSetMouseCapture(element, on)
{ 
  if (on)
  { 
    var flag = false;
     
    element._capture = function w3c_capture(e)
    { 
      if (flag)
        return;
      flag = true; 
      var evt = document.createEvent("MouseEvents"); 
      evt.initMouseEvent(e.type, 
        e.bubbles, e.cancelable, e.view, e.detail, 
        e.screenX, e.screenY, e.clientX, e.clientY, 
        e.ctrlKey, e.altKey, e.shiftKey, e.metaKey, 
        e.button, e.relatedTarget); 
      element.dispatchEvent(evt); 
      flag = false; 
    }; 
    
    for (var i = 0; i < capture.length; i++)
      window.addEventListener(capture[i], element._capture, true); 
  }
  else
  {
    for (var i = 0; i < capture.length; i++)
      window.removeEventListener(capture[i], element._capture, true); 
    element._capture = null; 
  } 
} // end w3cSetMouseCapture()

 

// ---------------------------------------------------------------------------
// Some Public access functions
// ---------------------------------------------------------------------------

// Should try to find a way to determine this on load, but this test is really quite cheap...
function gtGetFrameDocument(frame)
{
  if (frame.contentDocument)
    return frame.contentDocument;
  else if(frame.contentWindow)
    return frame.contentWindow.document;
  else
    return frame.document;
} // end gtGetFrameDocument()


function gtGetFrameWindow(frame)
{
  if (frame.contentDocument)
    return frame.contentDocument.parentWindow;
  else
    return frame.contentWindow;
} // end gtGetFrameWindow()


function gtGetFrameWindowById(id)
{
  return gtGetFrameWindow(gtGetElementById(id));
} // end gtGetFrameWindowById()


function gtGetWindowHeight()
{
//  alert("innerHeight=" + window.innerHeight +
//    ", offsetHeight=" + document.documentElement.offsetHeight +
//    ", clientHeight=" + document.documentElement.clientHeight);
  if (self.innerHeight) // all except Explorer
    return self.innerHeight;
  else if (document.documentElement && document.documentElement.clientHeight) // Explorer 6 Strict Mode
    return document.documentElement.clientHeight;
  else if (document.body) // other Explorers
    return document.body.clientHeight;
} // end gtGetWindowHeight()


function gtGetWindowWidth()
{
//  alert("body.clientWidth: " + document.body.clientWidth + ", innerWidth: " + window.innerWidth + ", offsetWidth; " + document.documentElement.offsetWidth + ", clientWidth: " + document.documentElement.clientWidth + ", scrollWidth: " + document.documentElement.scrollWidth);
//  alert("body.clientHeight: " + document.body.clientHeight + ", innerHeight: " + window.innerHeight + ", offsetHeight; " + document.documentElement.offsetHeight + ", clientHeight: " + document.documentElement.clientHeight + ", scrollHeight: " + document.documentElement.scrollHeight);
  if (self.innerWidth) // all except Explorer
    return self.innerWidth;
  else if (document.documentElement && document.documentElement.clientWidth) // Explorer 6 Strict Mode
    return document.documentElement.clientWidth;
  else if (document.body) // other Explorers
    return document.body.clientWidth;
} // end gtGetWindowWidth()


function gtGetComputedStyle(e)
{
  if (e.currentStyle)
    return e.currentStyle;
  else if (document.defaultView.getComputedStyle)
    return document.defaultView.getComputedStyle(e, null);
  else
    return null;
} // end gtGetComputedStyle()


// ----------------------------------------------------------------------------
//  Positioning-functions
// ----------------------------------------------------------------------------

function gtGetLeft(e)
{
  var offset = 0;
  try
  {
    while (e)
    {
      if (e.offsetLeft)
        offset += e.offsetLeft;
      e = e.offsetParent;
    }
  }
  catch (e) { }
  return offset;
} // end gtGetLeft()

function gtGetTop(e)
{
  var offset = 0;
  try
  {
    while (e)
    {
      if (e.offsetTop)
        offset += e.offsetTop;
      e = e.offsetParent;
    }
  }
  catch (e) { }
  return offset;
} // end gtGetTop()


function gtGetWidth       (e)    { return e.offsetWidth; }
function gtGetHeight      (e)    { return e.offsetHeight; }
function gtGetVisibility  (e)    { return e.style.visibility.toLowerCase(); }

function gtSetLeft        (e, v) { e.style.left    = v + "px"; }
function gtSetTop         (e, v) { e.style.top     = v + "px"; }
function gtSetWidth       (e, v) { e.style.width   = v + "px"; }
function gtSetHeight      (e, v) { e.style.height  = v + "px"; }
function gtSetVisibility  (e, v) { e.style.visibility = v; }


function findFilter(e, name)
{
  for (var f in e.filters)
  {
    if (f == name)
      return e.filters[f];
  }
  return null;
} // end findFilter()


function gtSetTransparent(e, t)
{
  var style = e.style;
  try { var has_filters = (e.filters != null); } catch (e) {}
  if (has_filters)
  {
    var val = 100.0 - t * 100.0;
    var a = findFilter(e, "DXImageTransform.Microsoft.Alpha");
    if (t == 0)
    {
      style.filter = "";
    }
    else
    {
      if (!a)
        style.filter += "progid:DXImageTransform.Microsoft.Alpha(opacity=" + val + ")";
      else
        a.Opacity = val; 
    }
  }
  else if (style.MozOpacity)
    style.MozOpacity = 1.0 - t;
  else
    style.opacity = 1.0 - t;
} // end gtSetTransparent()


function findIndex(array, text)
{
  for (var i = 0; i < array.length; i++)
  {
    if (array[i] == text)
      return i;
  }
  return -1;
} // end findIndex()


function gtAddClassName(e, class_name)
{
  var class_names = e.className.split(" ");
  var pos = findIndex(class_names, class_name);
  if (pos < 0)
    e.className = class_names.join(" ") + " " + class_name;
} // end gtAddClassName()


function gtDelClassName(e, class_name)
{
  var class_names = e.className.split(" ");
  var pos = findIndex(class_names, class_name);
  if (pos >= 0)
  {
    class_names.splice(pos, 1); // Removes it!
    e.className = class_names.join(" ");
    return true;
  }
  return false;
} // end gtDelClassName()



// Helper-function to create class inheritance
function gtSubclass(super_constructor, subclass_constructor)
{
  // Create a new prototype with all the methods and members of super
  var PROTOTYPE = new super_constructor();
  
  // Delete any noninherited properties of this new prototype object
  for (var p in PROTOTYPE)
    if (PROTOTYPE.hasOwnProperty(p))
      delete PROTOTYPE[p];
  
  // Set the prototype's superclass an constructor to point to the given methods
  PROTOTYPE.superclass  = super_constructor;
  PROTOTYPE.constructor = subclass_constructor;
  
  // Set the prototype of the subclass to the newly created prototype and return it
  subclass_constructor.prototype = PROTOTYPE;
  return PROTOTYPE;
} // end gtSubclass()




// ---------------------------------------------------------------------------
// Initialize library when included!
// ---------------------------------------------------------------------------

addGTGlobalHandler();

// ---------------------------------------------------------------------------
