require("/o2www/js/util/string.js"); // Need to have the "split" function available
require("/o2www/js/CBEvent.js");
require("/o2www/js/O2Exception.js");

var o2 = {};

function addLoadEvent(functionRef) {
  addEvent(window, 'load', functionRef); // XXX Document or window??
}

/* Appends a className to the class-attribute of an element */
function addClassName(elm, className) {
  if (elm == null || hasClassName(elm, className)) {
    return;
  }
  if (!elm.className) {
    elm.className = className;
  }
  else {
    var newClassName = elm.className;
    newClassName    += " ";
    newClassName    += className;
    elm.className = newClassName;
  }
}

/* Removes a className from the class-attribute of an element */
function removeClassName(elm, className) {
  if (!elm) {
    return alert("hasClassName: No element specified");
  }
  if (!elm.className) {
    return false;
  }
  var newClassName = "";
  var classNames = split(/ /, elm.className);
  for (var i = 0; i < classNames.length; i++) {
    if (classNames[i] != className) {
      newClassName += classNames[i] + " ";
    }
  }
  if (className) {
    newClassName = newClassName.substring( 0, newClassName.length-1 );
  }
  elm.className = newClassName;
}

/* Checks if an element has the given class */
function hasClassName(elm, className) {
  if (!elm) {
    return alert("hasClassName: No element specified");
  }
  if (!elm.className) {
    return false;
  }
  var classNames = split(/ /, elm.className);
  for (var i = 0; i < classNames.length; i++) {
    if (classNames[i] == className) {
      return true;
    }
  }
  return false;
}

/* Goes through an object and returns the elements with the specified
   class name and eventuel tag. 

   Caveats: if you want to use another tag than '*', you
   have to specify the element (0 or document works).

   Based on, but simplyfied: 
   
   http://muffinresearch.co.uk/archives/2006/04/29/getelementsbyclassname-deluxe-edition/

   thomasez 2008-03-03
*/
function getElementsByClassName(className, elm, tag) {
  if (!className) {
    o2Debug.warn("getElementsByClassName called without className parameter");
    return new Array();
  }
  elm = elm || document;
  tag = tag || "*";

  var tagList = elm.getElementsByTagName(tag);
  var elements = new Array();

  // Speed.
  var tLl = tagList.length;
  for (var i = 0; i < tLl; i++) {
    var classNames = split(/ /, tagList[i].className);
    for (var j = 0; j < classNames.length; j++) {
      if (classNames[j] == className) {
        elements.push(tagList[i]);
      }
    }
  }
  return elements;
}

/* Event handler stuff. Experimental */
function EventHandler() {
  this.listeners            = new Array();
  this.registeredEventTypes = new Array();
}

EventHandler.prototype.addEventListenerById = function(eventType, id, functionObject) {
  var elm = document.getElementById(id);
  if (!elm) {
    return alert("addEventListenerById: No element with id '" + id + "'");
  }
  addEvent(elm, eventType, functionObject);
}

EventHandler.prototype.addEventListenerByClass = function(eventType, className, functionObject) {
  var elements = getElementsByClassName(className);
  for (var i = 0; i < elements.length; i++) {
    addEvent(elements[i], eventType, functionObject);
  }
}

EventHandler.prototype.addEventListener = function(params) {
  if (!params["eventType"] || !params["functionObject"]) {
    alert("addEventListener: Missing eventType or functionObject attribute");
    return;
  }
  if (params["id"]) {
    addEvent(document.getElementById(params["id"]), params["eventType"], params["functionObject"]);
  }
  else if (params["className"]) {
    var elements = getElementsByClassName(params["className"], params["element"], params["tagName"]);
    for (var i = 0; i < elements.length; i++) {
      addEvent(elements[i], params["eventType"], params["functionObject"]);
    }
  }
  else {
    alert("addEventListener: Either id or className must be supplied");
  }
}

var o2EventHandler = new EventHandler();


/* Using the traditional event model. The event object sent to the function is a cross-browser event (see CBEvent.js) */
function addEvent(obj, type, fn, currentThis) {
    if(!_addEvent(obj, type, fn, currentThis)) {
        return false;
    }
    
  
  if (!obj.registeredEvents) {
    obj.registeredEvents = new Array();
  }
  if (!obj.registeredEvents[type]) {
    obj.registeredEvents[type] = new Array();
  }
  obj.registeredEvents[type].push({
    "code"           : fn.toString(),
    "functionObject" : fn,
    "currentThis"    : currentThis
  });
  return true;
}

/* Only adds the event if it's not added already */
function replaceEvent(obj, type, fn, currentThis) {
  if (!obj.registeredEvents || !obj.registeredEvents[type]) {
    return addEvent(obj, type, fn, currentThis);
  }
  for (var i = 0; i < obj.registeredEvents[type].length; i++) {
    var func = obj.registeredEvents[type][i];
    if (func.code === fn.toString()) {
      return;
    }
  }
  addEvent(obj, type, fn, currentThis);
}

function removeEvent(obj, type, fn) {
  if (!obj || !obj.registeredEvents || !obj.registeredEvents[type]) {
    return false;
  }
  var functions = new Array();
  for (var i = 0; i < obj.registeredEvents[type].length; i++) {
    var func = obj.registeredEvents[type][i];
    if (func.code !== fn.toString()) {
      functions.push(func);
    }
  }
  obj.registeredEvents[type] = null;
  obj["on"+type] = null;
  for (var i = 0; i < functions.length; i++) {
    addEvent(obj, type, functions[i].functionObject, functions[i].currentThis);
  }
}

function _addEvent(obj, type, fn, currentThis) {
  currentThis = currentThis || this;
  var doc = null;
  
  try { 
    doc = obj.ownerDocument || obj.document || obj;
  }
  catch (error) {
    if (window.console) {
      console.warn( getExceptionMessage(error) );
    }
    return false;
  }
  var win = doc.defaultView   || doc.parentWindow;
  var oldFn = obj["on"+type];
  obj["on"+type] = function(e) {
    e = e || win.event;
    e = new CBEvent(null, e);
    if (oldFn) {
      oldFn.call(this, e);
    }
    return fn.call(currentThis, e);
  }
  return true;
}

/*
Possible methods
o2Debug.log
o2Debug.debug
o2Debug.info
o2Debug.warn
o2Debug.error
o2Debug.assert
*/
var o2Debug = {

    setDebugMode: function(bool) {
        
    },
    _getConsole: function() {
        if(navigator.userAgent.match("Firefox/2.")) {
          loadFirebugConsole();
        }       
        if(window.console) {
            return window.console;
        }
        else {
            return {
                log   : function() {},
                debug : function() {},
                warn  : function() {},
                error : function() {},
                assert: function() {}
            };
        }
    },
    log    : function() { try { o2Debug._getConsole().log.apply(this,arguments);    } catch(e) {} },
    debug  : function() { try { o2Debug._getConsole().debug.apply(this,arguments);  } catch(e) {} },
    info   : function() { try { o2Debug._getConsole().info.apply(this,arguments);   } catch(e) {} },
    warn   : function() { try { o2Debug._getConsole().warn.apply(this,arguments);   } catch(e) {} },
    error  : function() { try { o2Debug._getConsole().error.apply(this,arguments);  } catch(e) {} },
    assert : function() { try { o2Debug._getConsole().assert.apply(this,arguments); } catch(e) {} }
};

