/***********************************************
* Logging utility © Tim Reed  (unless otherwise noted)
* This notice must stay intact for use
* Visit http://www.i4synthesis.com for contact info
***********************************************/

/*
  This creates a logging object use to output code execution logs

  Loosely based on Log4J

  This functionality uses the LOGGER contructor.

  Name the logging object whatever you want and you have access to the LOGGER
  methods and properties

  There is some sample code below to illustrate how to stub the LOGGER object
  when you no longer want to debug the code
*/

// ::::::::::::::::::::::::: GLOBAL VARS :::::::::::::::::::::::::::::::: //

var TRACE = 1;
var DEBUG = 2;
var INFO  = 3;
var WARN  = 4;
var ERROR = 5;
var FATAL = 6;
var LOGSPCR = "                                     "; // optional to pad output when multiple rows of data output

// ::::::::::::::::::::::: HELPERS FOR WINDOW CREATION :::::::::::::::::: //

// these are optional helpers
try {
  var _pEND         = document.location.href.lastIndexOf("/");
  var _pSTART       = document.location.href.indexOf("//");
  var _PATH         = document.location.href.substring(_pSTART+2,_pEND);
  var _SERVER       = _PATH.substring(0,_PATH.indexOf("/"));
  var _SERVER_CLEAN = _SERVER.replace(/[^a-z^0-9^_]/gi,"_");
  var _FILE         = document.location.href.substring(_pEND+1,document.location.href.length);
  var _PATH_CLEAN   = _PATH.replace(/[^a-z^0-9^_]/gi,"_")
  var _FILE_CLEAN   = _FILE.replace(/[^a-z^0-9^_]/gi,"_")
} catch (e) {
  var _pEND         = "";
  var _pSTART       = "";
  var _PATH         = "path_NA";
  var _SERVER       = "server_name_NA";
  var _SERVER_CLEAN = "server_name_NA";
  var _FILE         = "file_name_NA";
  var _PATH_CLEAN   = "path_NA";
  var _FILE_CLEAN   = "file_name_NA";
}

// ::::::::::::::::::::::: OBJECT PROTOTYPE ::::::::::::::::::::::::::::: //

LOGGER.prototype.show       = false; // set this to true to enable logging
LOGGER.prototype.trace      = _EntryTrace;
LOGGER.prototype.debug      = _EntryDebug;
LOGGER.prototype.info       = _EntryInfo;
LOGGER.prototype.warn       = _EntryWarn;
LOGGER.prototype.error      = _EntryError;
LOGGER.prototype.fatal      = _EntryFatal;
LOGGER.prototype.showTrace  = _traceIsEnabled;
LOGGER.prototype.showDebug  = _debugIsEnabled;
LOGGER.prototype.showInfo   = _infoIsEnabled;
LOGGER.prototype.showWarn   = _warnIsEnabled;
LOGGER.prototype.showError  = _errorIsEnabled;
LOGGER.prototype.showFatal  = _fatalIsEnabled;
LOGGER.prototype.makeEntry  = _Write;
LOGGER.prototype.openWindow = _openWindow;
LOGGER.prototype.open       = _openLog;
LOGGER.prototype.level      = null;
LOGGER.prototype.clear      = _clearLogWindow;
LOGGER.prototype.grade      = {1:"TRACE",2:"DEBUG",3:"INFO" ,4:"WARN" ,5:"ERROR",6:"FATAL"}//{'DEBUG':0,'INFO':1,'WARN':2,'ERROR':3};
LOGGER.prototype.getStatus  = _getObjectProperties;
LOGGER.prototype.setOutputMode    = _setOutputMode;
LOGGER.prototype.getOutputMode    = _getOutputMode;
LOGGER.prototype.renderBareOutput = _renderBareOutput;

// ::::::::::::::::::::::::: CONSTRUCTOR :::::::::::::::::::::::::::::::: //

function LOGGER(str,winName,showLogging,name) {
  try {
    if (window.top.document.location.href.indexOf("LOGGER.show=true")+1) showLogging = true; //override to allow ad hoc debugging
    this.constructorArguments = { "str":str,"winName":winName,"showLogging":showLogging,"name":name }
    if (null!=showLogging) this.show = showLogging;
    if (this.show) this.open(str,winName,null);
    this.docInfo = new Object();
    this.docInfo = {
      "_pEND"        : _pEND        ,
      "_pSTART"      : _pSTART      ,
      "_PATH"        : _PATH        ,
      "_SERVER"      : _SERVER      ,
      "_SERVER_CLEAN": _SERVER_CLEAN,
      "_FILE"        : _FILE        ,
      "_PATH_CLEAN"  : _PATH_CLEAN  ,
      "_FILE_CLEAN"  : _FILE_CLEAN
    }
  } catch (e) {
    //alert("LOGGER("+str+","+winName+","+showLogging+") this.open error: " + e.message);
  }
}

// ::::::::::::::::::::::::::: DATA DUMP :::::::::::::::::::::::::::::::::: //

// debug method that dumps all properties of LOGGER object
function _getObjectProperties() {
  var str = "LOGGER properties:\n";
  str += _getObjectData(this);

  //format output...hard to order properties during iteration
  // this is a hack but it works
  var strArr = str.split("\n");
  var tmpFunctionArr = new Array();
  var tmpObjArr = new Array();
  var tmpStrArr = new Array();
  for (var i=0; i<strArr.length; i++) {
    // split string to format property & value
    var tmpVal = strArr[i].split("=");
    if (null!=tmpVal[1]) {
      // separate functions
      if (tmpVal[1].indexOf("function()")+1) {
        tmpFunctionArr.push(strArr[i]);
      // separate objects & object properties
      } else if ((tmpVal[0].search(/\./i)+1)||(tmpVal[1]=="[object]")) {
        tmpObjArr.push(strArr[i]);
      // store values
      } else {
        tmpStrArr.push(strArr[i]);
      }
    } else {
      tmpStrArr.push(strArr[i]);
    }
  }
  tmpFunctionArr.sort();
  str = tmpStrArr.join("\n") + tmpObjArr.join("\n") + tmpFunctionArr.join("\n");
  return str;
}

// helper for _getObjectProperties() that iterates through the LOGGER object structure
function _getObjectData(o,oName,padding) {
  try {
    if (null==padding) padding="";
    var str = "";
    oName = (null!=oName) ? oName + "." : "";
    for (var p in o) {
      if ("object"==typeof(o[p])&&(null==o[p].document)) {
        str += padding+oName+p+"=[object]\n"
            + _getObjectData(o[p],oName+p,padding+"  ");
      } else if ("function"==typeof(o[p])) {
        str += padding+oName+p+"=function()\n";
      } else {
        str += padding+oName+p+"="+o[p]+"\n";
      }
    }
    return str;
  } catch (e) {
    return "ERROR: "+e.message;
  }
}

// ::::::::::::::::::::::::::: METHODS :::::::::::::::::::::::::::::::::: //

function _setOutputMode(method) {
  this.outputMode = method;
}

function _getOutputMode() {
  return this.outputMode;
}

function _renderBareOutput() {
  return ("bare"==this.outputMode);
}

// QUICK TEST METHODS TO DETERMIN LEVEL OF LOGGING TO EXECUTE
function _traceIsEnabled() {
  return (this.show && this.level <= TRACE);
}

function _debugIsEnabled() {
  return (this.show && this.level <= DEBUG);
}

function _infoIsEnabled() {
  return (this.show && this.level <= INFO);
}

function _warnIsEnabled() {
  return (this.show && this.level <= WARN);
}

function _errorIsEnabled() {
  return (this.show && this.level <= ERROR);
}

function _fatalIsEnabled() {
  return (this.show && this.level <= FATAL);
}

function _EntryFatal(str) {
  if (this.level <= FATAL) this.makeEntry(str,FATAL)
}

function _EntryError(str) {
  if (this.level <= ERROR) this.makeEntry(str,ERROR)
}

function _EntryWarn(str) {
  if (this.level <= WARN) this.makeEntry(str,WARN)
}

function _EntryInfo(str) {
  if (this.level <= INFO) this.makeEntry(str,INFO)
}

function _EntryDebug(str) {
  if (this.level <= DEBUG) this.makeEntry(str,DEBUG)
}

function _EntryTrace(str) {
  if (this.level <= TRACE) this.makeEntry(str,TRACE)
}

function _clearLogWindow() {
  try {
    if (this.show) {
      this.logOutputWindow.document.close();
      this.open("Clearing log for " + this.constructorArguments.str,this.constructorArguments.winName,this.level);
    }
  } catch(e) {
    alert('_clearLogWindow error: '+e.message);
  }
}

function _Write(str,level) {
  if (!this.show) return;
  if (this.logOutputWindow && !this.logOutputWindow.closed) {
    levelStr = (null == level) ? "" : this.grade[level];
    if (this.renderBareOutput()) {
      this.logOutputWindow.document.write(str+"\n");
    } else {
      this.logOutputWindow.document.write(buildTimeStamp()+": ["+((null == level) ? "" : this.grade[level])+"] - "+str+"\n");
    }
  } else {
    this.show = confirm("LOGGING Window \""+this.constructorArguments.winName+"\" is not open. Do you want to start logging?");//\""+this.logOutputWindowName+"\"
    if (this.show) this.open(str,this.constructorArguments.winName,level);
  }
}

function _openLog(str,winName,level) {
  if (!this.show) return;
  this.logOutputWindow = this.openWindow(winName);
  if (null != this.logOutputWindow) {
    // close document first, so that it redraws when logging starts
    // without this, Firefox appends current document
      this.logOutputWindow.document.close();
      this.logOutputWindow.document.open("text/plain");
    if (this.windowOpenError) {
      // If there was a problem and logging window was opened in default mode
      // So far this has only occurred when the window name has invalid chars
      this.makeEntry("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!",ERROR);
      this.makeEntry("---------------------------------------------------------",ERROR);
      this.makeEntry(" ERROR OPENING LOGGING WINDOW!",ERROR);
      this.makeEntry("   "+this.windowOpenError,ERROR);
      this.makeEntry("   "+this.windowOpenArgments,ERROR);
      this.makeEntry("---------------------------------------------------------",ERROR);
      this.makeEntry("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n",ERROR);
    }
    if (this.windowNameInvalid) {
      this.makeEntry("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!",ERROR);
      this.makeEntry("---------------------------------------------------------",ERROR);
      this.makeEntry("INVALID WINDOW NAME:",ERROR);
      this.makeEntry("    "+winName+"",ERROR);
      this.makeEntry("    Use alphanumeric & underscore (a-zA-Z0-9_) ONLY",ERROR);
      this.makeEntry("---------------------------------------------------------",ERROR);
      this.makeEntry("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n",ERROR);
    }
    this.makeEntry("*************** LOGGING STARTED ****************",((level)?level:INFO));
    if (null != str) this.makeEntry(str,((level)?level:INFO));
    this.makeEntry("Window Name is \"" + this.logOutputWindow.name + "\"\n",((level)?level:INFO));
  } else {
    this.show=false;
  }
}

// ::::::::::: GENERIC FUNCTIONS USED BY THE LOGGER OBJECT :::::::::::::: //

function _openWindow(name) {
  try {
    var tmpStr = "";
    var winName = (null==name) ? "dataWindowPopUp" : name.replace(/[^(a-zA-Z0-9_)]|[\(]|[\)]/gi,"_"); // substitute any invalid characters with "_"
    if ((winName!=name) && (null!=name))  {
      this.windowNameInvalid = true; // check to see if the name param that was passed had to be changed
      this.constructorArguments.winName = name;
    }
    var mimeType = "text/plain";
    var popWidth=720;
    var popHeight=400;
    var winLeft = (screen.availWidth - popWidth) / 2;
    var winTop = (screen.availHeight - popHeight) / 2;
    //parameters = "toolbar,location,directories,status,menubar,scrollbars,resizable,";
    var parameters = "resizable,scrollbars,menubar,";
    try {
      var dataWindow = window.open("", winName, parameters + "width=" + popWidth + ",height=" + popHeight + ",left=" + winLeft + ",top=" + winTop);
      dataWindow.document.title = name;
    } catch (e) {
      // catch failure to open window as specified and open in "safe" default mode
      var dataWindow = window.open("","dataWindowPopUp");
      this.windowOpenError = e.message;
      this.windowOpenArgments = 'arguments=window.open("",'+winName+','+parameters+'width='+popWidth+',height='+popHeight+',left='+winLeft+',top='+winTop+')';
    }
    //dataWindow.focus();
    return dataWindow;
  } catch (e) {
    //alert("_openWindow("+name+") error: " + e.message);
    return null;
  }
}

function buildTimeStamp(oDate,getUTC) {
  if ((null == oDate) || isNaN(oDate)) oDate = new Date();
  var y = (getUTC) ? oDate.getUTCFullYear()     : oDate.getFullYear();
  var M = (getUTC) ? oDate.getUTCMonth()+1      : oDate.getMonth()+1;
  var d = (getUTC) ? oDate.getUTCDate()         : oDate.getDate();
  var h = (getUTC) ? oDate.getUTCHours()        : oDate.getHours();
  var m = (getUTC) ? oDate.getUTCMinutes()      : oDate.getMinutes();
  var s = (getUTC) ? oDate.getUTCSeconds()      : oDate.getSeconds();
  var S = (getUTC) ? oDate.getUTCMilliseconds() : oDate.getMilliseconds();

  if (M<10) M = "0" + M;
  if (d<10) d = "0" + d;
  if (h<10) h = "0" + h;
  if (m<10) m = "0" + m;
  if (s<10) s = "0" + s;
  //S = Math.pow(10,3-String(S).length)*S;
  if (S<10) {
    S="00"+S;
  } else if (S<100) {
    S="0"+S;
  }

  var ts = y+"-"+M+"-"+d+" "+h+":"+m+":"+s+"."+S;
  return ts;
}

