/*
	fvlogger v1.0
	(c) 2005 davidfmiller
	http://www.fivevoltlogic.com/code/fvlogger/
	see readme.txt for documentation

  modifications by Jonathan Simms
    - changed style to emulate prototype.js-type code
    - put all methods under Log "namespace"
*/

var Log = function() {};

// version number
Log.VERSION = '1.0';

// turn logging on or off;
Log.LOG_ON = true;

// at one point i wanted to put the logging stuff in a different frame
// i still might one day
Log._console = window;

// constants for logging levels
Log.DEBUG = 0;
Log.INFO = 1;
Log.WARN = 2;
Log.ERROR = 3;
Log.FATAL = 4;

// all logging statements that whose level is greater than or equal to Log.DEFAULT_LOG_LEVEL will be processed;
// all others will be ignored
Log.DEFAULT_LOG_LEVEL = Log.DEBUG;

// the id of the node that will have the logging statements appended to it
Log.LOG_ID = 'fvlogger';

// the element that should be wrapped around the log messages
Log.LOG_ELEMENT = 'pre';

/* the code that follows is */

// the css classes that will be applied to the logging elements
Log.LOG_CLASSES = new Array("debug","info","warn","error","fatal");

/* since we're using a preformatted tag, this letter will be placed
 * in front of each log entry to discern its level (as opposed to the 
 * fruit-colored-and-flowy formatting the original author preferred).
*/
Log.LOG_LEVEL_PREFIX = new Array("D", "I", "W", "E", "F");

Log.ClassMethods = {
  // retrieves the element whose id is equal to Log.LOG_ID
  getLogger: function (id) {
    if (arguments.length === 0) { id = Log.LOG_ID; }
    if (!Log._console) { return false; }
    return Log._console.document.getElementById(id);
  },

  showDebug: function() { Log.showMessages(Log.DEBUG); },
  showInfo: function() { Log.showMessages(Log.INFO); },
  showWarn: function() { Log.showMessages(Log.WARN); },
  showError: function() { Log.showMessages(Log.ERROR); },
  showFatal: function() { Log.showMessages(Log.FATAL); },
  showAll: function() { Log.showMessages(); },

  // removes all logging information from the logging element
  eraseLog: function (ask) {
    var debug = Log.getLogger();
    if (! debug) { return false; }

    if (ask && ! confirm("Are you sure you wish to erase the log?")) {
      return false;
    }

    var ps = debug.getElementsByTagName(Log.LOG_ELEMENT);
    var length = ps.length;
    for (var i = 0; i < length; i++) { debug.removeChild(ps[length - i - 1]); }
    return true;
  },

  debug: function(message) { Log.log("" + message, Log.DEBUG); },
  warn: function(message) { Log.log("" + message, Log.WARN); },
  info: function(message) { Log.log("" + message, Log.INFO); },
  error: function(message) { Log.log("" + message, Log.ERROR);},
//  fatal: function(message) { Log.log("" + message, Log.FATAL);},

  windowError: function (message, url, line) {
    Log.log('Error on line ' + line + ' of document ' + url + ': ' + message, Log.FATAL);
    return true; //
  },


  showMessages: function(level, hideOthers) {

    // alert('showing ' + level);
    var showAll = false;
    // if no level has been specified, use the default
    if (arguments.length === 0) { level = Log.DEFAULT_LOG_LEVEL; showAll = true; }
    if (arguments.length < 2) { hideOthers = true; }

    // retrieve the element and current statements
    var debug = Log.getLogger();
    if (! debug) { 
      return false; 
    }
    var ps = debug.getElementsByTagName("p");
    if (ps.length === 0) { return true; }

    // get the number of nodes in the list
    var l = ps.length; 

    // get the class name for the specified level
    var lookup = Log.LOG_CLASSES[level]; 

    // loop through all logging statements/<p> elements...
    for (var i = l - 1; i >= 0; i--) {
      // hide all elements by default, if specified
      if (hideOthers) { 
        Log.hide(ps[i]); 
      }

      // get the class name for this <p>
      var c = Log.getNodeClass(ps[i]);
      if (c && c.indexOf(lookup) > -1 || showAll) { 
        Log.show(ps[i]); 
      }
    }
    return true; // quiet ff-js-console
  },

  prettyPrint: function(obj) {
    if (isArray(obj)) {
      var strArray = new Array();

      for(var i=0; i < obj.length; i++) {
        strArray.push(Log.prettyPrint(obj[i]));
      }

      return "[" + strArray.join(", ") + "]";
    }
    else if (isString(obj)) {
      return '"' + obj + '"';
    }
    else if (isNumber(obj)) {
      return obj.toString();
    }
    else if (isBoolean(obj)) {
      return obj ? "true" : "false";
    }
    else if (isUndefined(obj)) {
      return "undefined";
    }
    else if (isNull(obj)) {
      return "null";
    }
    else if (isFunction(obj)) {
      return  "function";
    }
    else if (isAlien(obj)) {
      return "alien!";
    }
    else {
      var tmp = "";
      var val, str;
      for (x in obj) {
        str = Log.prettyPrint(obj[x])
        try { 
          tmp += "  " + x + ": " + str + "\n";
        }
        catch(e) {
          tmp += "caught '" + e + "' when trying toString() on property '" + x + "'";
        }
      }
      return tmp;
    }
  },

  dump: function(obj) {
    Log.debug(Log.prettyPrint(obj));
  },

  // appends a statement to the logging element if the threshold level is exceeded
  log: function(message, level) {
    // check to make sure logging is turned on
    if (! Log.LOG_ON) { return false; } 

    // retrieve the infrastructure
    if (arguments.length == 1) { level = Log.INFO;}
    if (level < Log.DEFAULT_LOG_LEVEL) { 

      return false; 
    }
    var div = Log.getLogger();
    if (! div) { 
      return false; 
    }

    // append the statement
    var p = document.createElement(Log.LOG_ELEMENT);

    // this is a hack work around a bug in ie
    if (p.getAttributeNode("class")) {
      for (var i = 0; i < p.attributes.length; i++) {
        if (p.attributes[i].name.toUpperCase() == 'CLASS') {
          p.attributes[i].value = Log.LOG_CLASSES[level];
        }
      }
    } else {
      p.setAttribute("class", Log.LOG_CLASSES[level]);
    }
    var text = document.createTextNode(Log.LOG_LEVEL_PREFIX[level] + ": " + message);
    p.appendChild(text);
    div.appendChild(p);
    return true;
  },

  // show a node
  show: function(target) {
    target.style.display = "";
    return true;
  },

  // hide a node
  hide: function(target) {
    target.style.display = "none";
    return true;
  },

  // returns the class attribute of a node
  getNodeClass: function(obj) {
    var result = false;

    if (obj.getAttributeNode("class")) {
      result = obj.attributes.getNamedItem("class").value;
    }
    return result;
  }
}; // ClassMethods

/* because Object.extend may not have been defined yet
*/
for (var property in Log.ClassMethods) {
  Log[property] = Log.ClassMethods[property];
}


