1 (function (win) {
  2   'use strict';
  3   /**
  4    * Hermes is the class that allows to log messages to different appenders depending of level to show.
  5    * Highly extensible. You can create your own Appenders and add it to Hermes.
  6    * The idea was to create an ErrorHandler in the same way as Log4JS but being more extensible.
  7    * @author Tomás Corral Casas
  8    * @version 1.0
  9    */
 10   /**
 11    * Hermes is the private declaration of the Hermes object.
 12    * Hermes is declared null by default.
 13    * @private
 14    * @type Hermes
 15    */
 16   var Hermes = null,
 17   	Trace = null,
 18   	Tracer = null,
 19   	TraceMessage = null,
 20     oTracer = null,
 21     /**
 22      * oHermes is the instance of Hermes object.
 23      * oHermes is declared null by default.
 24      * @private
 25      * @type Hermes
 26      */
 27     oHermes = null,
 28     ErrorExt = null,
 29 
 30     Appender = null,
 31     ConsoleAppender = null,
 32     Layout = null,
 33     ConsoleLayout = null,
 34     Level = null,
 35     DateFormatter = null,
 36     /**
 37      * nTimeoutToLogMilliseconds is the default value for nTimeout in Hermes to defer the log of message
 38      * If we need to log messages to one DB it's important not to launch so much Ajax calls
 39      * nTimeoutToLogMilliseconds is declared 10000 by default.
 40      * @private
 41      * @type Number
 42      */
 43     nTimeoutToLogMilliseconds = 5000;
 44   /**
 45    * isFunction checks if fpCallback is function or not
 46    * @private
 47    * @return {Boolean}
 48    */
 49   function isFunction(fpCallback) {
 50     return Object.prototype.toString.call(fpCallback) === '[object Function]';
 51   }
 52   /**
 53    * existObjectAndMethod checks if sMethod exist on oObject
 54    * @private
 55    * @return {Boolean}
 56    */
 57   function existObjectAndMethod(oObject, sMethod) {
 58     return oObject && isFunction(oObject[sMethod]);
 59   }
 60   /**
 61    * times count the executions and add this as indentation
 62    * @private
 63    * @return {Number}
 64    */
 65   function times(nCount)
 66   {
 67   	return nCount < 1 ? '' : new Array(nCount + 1).join(" ");
 68   }
 69   /**
 70    * getTypeFromMessage returns the type of error extracted from message
 71    * @private
 72    * @returns {String}
 73    */
 74   function getTypeFromMessage(sMessage) {
 75     var nIndexDoubleColon = sMessage.indexOf(":");
 76     return sMessage.substr(0, nIndexDoubleColon);
 77   }
 78   /**
 79    * removeTypeFromMessage returns the message removing the error type
 80    * @private
 81    * @returns {String}
 82    */
 83   function removeTypeFromMessage(sMessage) {
 84     var sType = getTypeFromMessage(sMessage);
 85     return sMessage.replace(sType + ":", "");
 86   }
 87   /**
 88    * Appender is the class that will log or clear messages
 89    * @abstract
 90    * @private
 91    * @class Appender
 92    * @constructor
 93    * @param oLayout
 94    * @type {Layout}
 95    */
 96   Appender = function (oLayout) {
 97     /**
 98      * sName is the name of the appender
 99      * @member Appender.prototype
100      * @type {String}
101      */
102     this.sName = '[object Appender]';
103     /**
104      * oLayout is the Layout instance to be used to format the error before append it
105      * @member Appender.prototype
106      * @type {Layout}
107      */
108     this.oLayout = oLayout || null;
109   };
110   /**
111    * setName is the method to set the name of Appender
112    * @member Appender.prototype
113    * @param sName
114    * @type {String}
115    * @return Appender instance
116    */
117   Appender.prototype.setName = function (sName) {
118     this.sName = sName;
119     return this;
120   };
121   /**
122    * setLayout is the method to set the layout for Appender
123    * @member Appender.prototype
124    * @param oLayout
125    * @type {Layout}
126    * @return Appender instance
127    */
128   Appender.prototype.setLayout = function (oLayout) {
129     this.oLayout = oLayout;
130     return this;
131   };
132   /**
133    * toString is the method to overwrite the Object.prototype.toString
134    * @member Appender.prototype
135    * @return {String}
136    */
137   Appender.prototype.toString = function () {
138     return this.sName;
139   };
140   /**
141    * log is an abstract method that needs to be overwritten on extended classes to make it work.
142    * @member Appender.prototype
143    * @param oError
144    * @type {ErroExt}
145    */
146   Appender.prototype.log = function (oError) {
147     throw new Error("This method must be overwritten!");
148   };
149   /**
150    * clear is an abstract method that needs to be overwritten on extended classes to make it work.
151    * @member Appender.prototype
152    */
153   Appender.prototype.clear = function () {
154     throw new Error("This method must be overwritten!");
155   };
156   /**
157    * Layout is the class that will format the errors before send it to the Appender class
158    * @abstract
159    * @private
160    * @class Layout
161    * @constructor
162    */
163   Layout = function () {
164 
165   };
166   /**
167    * formatError is an abstract method that needs to be overwritten on extended classes to make it work.
168    * @member Layout.prototype
169    * @param oError
170    * @type {ErroExt}
171    */
172   Layout.prototype.formatError = function (oError) {
173     throw new Error("This method must be overwritten!");
174   };
175   /**
176    * ConsoleLayout is a class to format log messages.
177    * @extends Layout
178    * @private
179    * @class ConsoleLayout
180    * @constructor
181    */
182   ConsoleLayout = function () {
183     Layout.apply(this);
184   };
185   ConsoleLayout.prototype = new Layout();
186   /**
187    * formatError is a method to format the error.
188    * @member ConsoleLayout.prototype
189    * @param oError
190    * @type {ErrorExt}
191    * @return sError
192    * @type {String}
193    */
194   ConsoleLayout.prototype.formatError = function (oError) {
195     var sError = '';
196     if (oError instanceof ErrorExt) {
197       sError = "Error level: " + oError.oLevel.toString() +
198         ", Time: " + oError.getFormattedDate();
199 		if(oError.sCategory !== null)
200 		{
201 			sError += ", Category: " + oError.sCategory;
202 		}
203 		if(oError.sMessage !== null)
204 		{
205 			sError += ", Message: " + oError.sMessage
206 		}
207 		if(oError.sFilenameUrl !== null)
208 		{
209 			sError += ", FilenameUrl: " + oError.sFilenameUrl
210 		}
211 		if(oError.nLineNumber !== null)
212 		{
213 			sError += ", LineNumber: " + oError.nLineNumber
214 		}
215 		sError += '.';
216     }
217     return sError;
218   };
219   /**
220    * ConsoleAppender is a class to append log messages in the console.
221    * @extends Appender
222    * @private
223    * @class ConsoleAppender
224    * @constructor
225    */
226   ConsoleAppender = function (oLayout) {
227     Appender.apply(this, arguments);
228     /**
229      * sName is the name of the appender
230      * @member ConsoleAppender.prototype
231      * @type {String}
232      */
233     this.sName = '[object ConsoleAppender]';
234     /**
235      * oLayout is the Layout instance to be used to format the error before append it
236      * @member ConsoleAppender.prototype
237      * @type {ConsoleLayout}
238      */
239     this.oLayout = new ConsoleLayout();
240   };
241   ConsoleAppender.prototype = new Appender();
242   /**
243    * log is the method that logs the message to the console.
244    * Before log it's needed to check if console exist.
245    * @member ConsoleAppender.prototype
246    * @param oError
247    * @type {ErrorExt}
248    */
249   ConsoleAppender.prototype.log = function (oError) {
250     if (existObjectAndMethod(win.console, "log")) {
251       win.console.log(this.oLayout.formatError(oError));
252     }
253   };
254   /**
255    * clear is the method that clear all the logged messages in the console.
256    * Before clear it's needed to check if console exist.
257    * @member ConsoleAppender.prototype
258    */
259   ConsoleAppender.prototype.clear = function () {
260     if (existObjectAndMethod(win.console, "clear")) {
261       win.console.clear();
262     }
263   };
264   /**
265    * Hermes is the class that manages Appenders, Error, Deferred calls and Levels of Error to show
266    * We can add Errors but if the Error is not of the same level the error will not be showed.
267    * If the Hermes Error Level is ALL all the logs will be added
268    * If the Hermes Error Level is OFF no log will be added
269    * The add of logs can be deferred to avoid multiple calls that block browser
270    * @private
271    * @class Hermes
272    * @constructor
273    * @param oLevel
274    * @type {Level}
275    * @param nImmediate
276    * @type {Number}
277    * @param nTimeout
278    * @type {Number}
279    */
280   Hermes = function (oLevel, nImmediate, nTimeout) {
281     /**
282      * oAppenders is the object that will contain the Appenders to append messages
283      * @member Hermes.prototype
284      * @type {Object}
285      */
286     this.oAppenders = {};
287     /**
288      * aErrors is the array that will contain the Errors
289      * @member Hermes.prototype
290      * @type {Array}
291      */
292     this.aErrors = [];
293     /**
294      * oLevel is the Level instance to manage the Error Level to show.
295      * @member Hermes.prototype
296      * @type {Level}
297      */
298     this.oLevel = oLevel || Level.ALL;
299     /**
300      * nTimeLastSent is the number of milliseconds of the last sent
301      * now date by default
302      * @member Hermes.prototype
303      * @type {Number}
304      */
305     this.nTimeLastSent = this.now();
306     /**
307      * nImmediate is the config number to defer or not the append of logs
308      * @member Hermes.prototype
309      * @type {Number}
310      */
311     this.nImmediate = nImmediate || Hermes.DEFERRED;
312     /**
313      * nTimeout is the number of milliseconds to defer the append of logs
314      * @member Hermes.prototype
315      * @type {Number}
316      */
317     this.nTimeout = nTimeout || nTimeoutToLogMilliseconds;
318   };
319   /**
320    * @static
321    */
322   Hermes.DEFERRED = 0;
323   /**
324    * @static
325    */
326   Hermes.IMMEDIATE = 1;
327   /**
328    * deferLog change the method to append logs to be deferred
329    * @member Hermes.prototype
330    * @return Hermes instance
331    */
332   Hermes.prototype.deferLog = function () {
333     this.nImmediate = Hermes.DEFERRED;
334     return this;
335   };
336   /**
337    * immediateLog change the method to append logs to be immediate
338    * @member Hermes.prototype
339    * @return Hermes instance
340    */
341   Hermes.prototype.immediateLog = function () {
342     this.nImmediate = Hermes.IMMEDIATE;
343     return this;
344   };
345   /**
346    * now returns the number of milliseconds at the moment of the execution of this method
347    * @member Hermes.prototype
348    * @return {Number} Milliseconds
349    */
350   Hermes.prototype.now = function () {
351     return +new Date();
352   };
353   /**
354    * setLevel set a new Level for the Hermes
355    * Before change the level it's checked if oLevel is instance of Level to be assigned or not.
356    * @member Hermes.prototype
357    * @param oLevel
358    * @type {Level}
359    * @return Hermes instance
360    */
361   Hermes.prototype.setLevel = function (oLevel) {
362   	if (oLevel instanceof Level) {
363       this.oLevel = oLevel;
364     }
365     return this;
366   };
367   /**
368    * addAppender add a new Appender to Hermes. When the Appender is added starts to log messages
369    * Before add a new Appender it's checked if oAppender is instance of Appender to be added or not.
370    * @member Hermes.prototype
371    * @param oAppender
372    * @type {Appender}
373    * @return Hermes instance
374    */
375   Hermes.prototype.addAppender = function (oAppender) {
376     if (oAppender instanceof Appender) {
377       this.oAppenders[oAppender.toString()] = oAppender;
378     }
379     return this;
380   };
381   /**
382    * removeAppender removes the Appender. When the Appender is removed stops to log messages
383    * @member Hermes.prototype
384    * @param oAppender
385    * @type {Appender}
386    * @return Hermes instance
387    */
388   Hermes.prototype.removeAppender = function (oAppender) {
389     delete this.oAppenders[oAppender.toString()];
390     return this;
391   };
392   /**
393    * setAppenders add an array of Appenders to be added in one step.
394    * @member Hermes.prototype
395    * @param aAppenders
396    * @type {Array}
397    * @return Hermes instance
398    */
399   Hermes.prototype.setAppenders = function (aAppenders) {
400     var nAppender = 0,
401       nLenAppenders = aAppenders.length,
402       oAppender = null;
403 
404     for (; nAppender < nLenAppenders;) {
405       oAppender = aAppenders[nAppender];
406       this.addAppender(oAppender);
407       nAppender = nAppender + 1;
408     }
409     try {
410       return this;
411     } finally {
412       nAppender = nLenAppenders = oAppender = null;
413     }
414   };
415   /**
416    * isSameLevel checks if the Error Level is the same that in oError
417    * The method returns true if Level is ALL to allow log all the logs
418    * The method returns false if Level is OFF to avoid log any log
419    * @member Hermes.prototype
420    * @param oError
421    * @type {ErrorExt}
422    * @return {Boolean}
423    */
424   Hermes.prototype.isSameLevel = function (oError) {
425     if (this.oLevel.valueOf() === Level.nALL) {
426       return true;
427     } else if (this.oLevel.valueOf() === Level.OFF) {
428       return false;
429     } else {
430       return this.oLevel.valueOf() === oError.oLevel.valueOf();
431     }
432   };
433   /**
434    * sendMessage executes the action in Appender for each Error.
435    * @member Hermes.prototype
436    * @param oAppender
437    * @type {Appender}
438    * @param sAction
439    * @type {String}
440    */
441   Hermes.prototype.sendMessage = function (oAppender, sAction) {
442     if (typeof oAppender[sAction] === "undefined") {
443       return;
444     }
445     var nError = 0,
446       nLenError = this.aErrors.length,
447       oError = null;
448 
449     for (; nError < nLenError;) {
450       oError = this.aErrors[nError];
451       if (this.isSameLevel(oError)) {
452         oAppender[sAction](oError);
453       }
454       nError = nError + 1;
455     }
456     this.nTimeLastSent = this.now();
457   };
458   /**
459    * isImmediate checks if nImmediate is equals to Hermes.IMMEDIATE
460    * @member Hermes.prototype
461    * @return {Boolean}
462    */
463   Hermes.prototype.isImmediate = function () {
464     return this.nImmediate === Hermes.IMMEDIATE;
465   };
466   /**
467    * isTimeToSent checks if now is time to sent new logs to the appender
468    * @member Hermes.prototype
469    * @return {Boolean}
470    */
471   Hermes.prototype.isTimeToSent = function () {
472     return this.now() >= this.nextTimeToSent();
473   };
474   /**
475    * notifyAppenders send the message log for all the Appenders if the execution is Deferred and is time to send new logs or if the execution is Immediate.
476    * after execute notifyAppenders the errors are reset to allow new Errors to log.
477    * @member Hermes.prototype
478    * @param sAction
479    * @type {String}
480    * @return Hermes instance
481    */
482   Hermes.prototype.notifyAppenders = function (sAction) {
483     var sKey = '',
484       oAppender = null;
485     sAction = sAction.toLowerCase();
486 
487     if (this.isImmediate())
488     {
489     	for (sKey in this.oAppenders) {
490     		if (this.oAppenders.hasOwnProperty(sKey)) {
491     			oAppender = this.oAppenders[sKey];
492     			this.sendMessage(oAppender, sAction);
493     		}
494     	}
495     	this.resetErrors();
496     } else if (this.isTimeToSent()){
497 		for (sKey in this.oAppenders) {
498     		if (this.oAppenders.hasOwnProperty(sKey)) {
499     			oAppender = this.oAppenders[sKey];
500     			this.sendMessage(oAppender, sAction);
501     		}
502     	}
503     	this.resetErrors();
504     }
505     return this;
506   };
507   /**
508    * addError check if oError is instance of ErrorExt and if it's the error is added and the notifyAppenders is called
509    * notifyAppenders is executed to defer or log the message.
510    * @member Hermes.prototype
511    * @param oError
512    * @type {ErrorExt}
513    * @return Hermes instance
514    */
515   Hermes.prototype.addError = function (oError) {
516     if (oError instanceof ErrorExt) {
517       this.aErrors.push(oError);
518       this.notifyAppenders('log');
519     }
520     return this;
521   };
522   /**
523    * resetErrors clean all the erros in the aErrors Array
524    * @member Hermes.prototype
525    */
526   Hermes.prototype.resetErrors = function () {
527     this.aErrors = [];
528   };
529   /**
530    * nextTimeToSent returns the time to know, if the method of log is Deferred, if it's possible to log messages.
531    * @member Hermes.prototype
532    * @return {Number}
533    */
534   Hermes.prototype.nextTimeToSent = function () {
535     return this.nTimeLastSent + this.nTimeout;
536   };
537   /**
538    * forceLog will log messages even if the method of log is Deferred.
539    * @member Hermes.prototype
540    * @return Hermes instance
541    */
542   Hermes.prototype.forceLog = function () {
543     this.immediateLog();
544     this.log();
545     this.deferLog();
546     return this;
547   };
548   /**
549    * log is the method that will log messages
550    * @member Hermes.prototype
551    */
552   Hermes.prototype.log = function () {
553     this.notifyAppenders("log");
554   };
555   /**
556    * clear is the method that will clear messages
557    * @member Hermes.prototype
558    */
559   Hermes.prototype.clear = function () {
560     this.notifyAppenders("clear");
561   };
562   /**
563    * DateFormatter is the class to format dates
564    * @private
565    * @class DateFormatter
566    * @constructor
567    * @param sFormat
568    * @type {String}
569    */
570   DateFormatter = function (sFormat) {
571     this.sFormat = sFormat || DateFormatter.DEFAULT;
572   };
573   /**
574    * @static
575    */
576   DateFormatter.DEFAULT = "yyyy-MM-dd  hh:mm:ss TZ";
577   /**
578    * setDefaultFormat change the format to format the date
579    * @member DateFormatter.prototype
580    * @param sFormat
581    * @type {String}
582    * @return DateFormatter instance
583    */
584   DateFormatter.prototype.setDefaultFormat = function (sFormat) {
585     this.sFormat = sFormat || DateFormatter.DEFAULT;
586     return this;
587   };
588   /**
589    * formatDate returns the string with the formatted date.
590    * @member DateFormatter.prototype
591    * @param oDate
592    * @type {Date}
593    */
594   DateFormatter.prototype.formatDate = function (oDate) {
595     var nDay = this.addZero(oDate.getDate()),
596       nMonth = this.addZero(oDate.getMonth() + 1),
597       nYearLong = oDate.getFullYear(),
598       nYearShort = String(nYearLong).substring(3, 4),
599       nYear = this.sFormat.indexOf("yyyy") > -1 ? nYearLong : nYearShort,
600       nHours = this.addZero(oDate.getHours()),
601       nMinutes = this.addZero(oDate.getMinutes()),
602       nSeconds = this.addZero(oDate.getSeconds()),
603       nTimeZone = this.getTimeZoneOffset(oDate),
604       sDate = this.sFormat.replace(/dd/g, nDay).replace(/MM/g, nMonth).replace(/y{1,4}/g, nYear);
605     sDate = sDate.replace(/hh/g, nHours).replace(/mm/g, nMinutes).replace(/ss/g, nSeconds);
606     sDate = sDate.replace(/TZ/g, nTimeZone);
607     try {
608       return sDate;
609     } finally {
610       nDay = nMonth = nYearLong = nYearShort = nYear = nHours = nMinutes = nSeconds = nTimeZone = sDate = null;
611     }
612   };
613   /**
614    * addZero is a method that adds a Zero to all the number that are less than 10
615    * @member DateFormatter.prototype
616    * @param nNumber
617    * @type {Number}
618    * @return {String}
619    */
620   DateFormatter.prototype.addZero = function (nNumber) {
621     return ((nNumber < 10) ? "0" : "") + nNumber;
622   };
623   /**
624    * getTimeZoneOffset set the timezone and returns the difference
625    * @member DateFormatter.prototype
626    * @param oDate
627    * @type {Date}
628    * @return {String}
629    */
630   DateFormatter.prototype.getTimeZoneOffset = function (oDate) {
631     var nOffset = Math.abs(oDate.getTimezoneOffset()),
632       nHour = this.addZero(Math.floor(nOffset / 60)),
633       nMinutes = this.addZero((nOffset % 60));
634     try {
635       return oDate.getTimezoneOffset() < 0 ? "+" + nHour + ":" + nMinutes : "-" + nHour + ":" + nMinutes;
636     } finally {
637       nOffset = nHour = nMinutes = null;
638     }
639   };
640   /**
641    * ErrorExt is the class that represents the Error
642    * @private
643    * @class ErrorExt
644    * @constructor
645    * @param oLevel
646    * @type {Level}
647    * @param sCategory
648    * @type {String}
649    * @param sMessage
650    * @type {String}
651    * @param sFilenameUrl
652    * @type {String}
653    * @param nLineNumber
654    * @type {Number}
655    * @param sDateFormat
656    * @type {String}
657    */
658   ErrorExt = function (oLevel, sCategory, sMessage, sFilenameUrl, nLineNumber, sDateFormat) {
659     /**
660      * oLevel is the Error Level to know if it can be logged or not.
661      * @member ErrorExt.prototype
662      * @type {Level}
663      */
664     this.oLevel =  oLevel || Level.ALL;
665     /**
666      * sCategory is the error category
667      * @member ErrorExt.prototype
668      * @type {String}
669      */
670     this.sCategory = sCategory || 'GENERAL';
671     /**
672      * sMessage is the error message
673      * @member ErrorExt.prototype
674      * @type {String}
675      */
676     this.sMessage = sMessage || 'Error Undefined';
677     /**
678      * dInitialize sets the instanciation date
679      * @member ErrorExt.prototype
680      * @type {Date}
681      */
682     this.dInitialize = new Date();
683     /**
684      * oDateFormatter is the instance of DateFormatter after set the default format
685      * @member ErrorExt.prototype
686      * @type {DateFormatter}
687      */
688     this.oDateFormatter = new DateFormatter().setDefaultFormat(sDateFormat);
689     /**
690      * sFilenameUrl is the url or filename to the file where the error is launched
691      * @member ErrorExt.prototype
692      * @type {String}
693      */
694     this.sFilenameUrl = sFilenameUrl || null;
695     /**
696      * nFileNumber is the number of line where the error is launched
697      */
698     this.nLineNumber = nLineNumber || null;
699   };
700   /**
701    * setFilenameUrl change the sFilenameUrl
702    * @member ErrorExt.prototype
703    * @param sFilenameUrl
704    * @type {String}
705    * @return ErrorExt instance
706    */
707   ErrorExt.prototype.setFilenameUrl = function (sFilenameUrl) {
708     this.sFilenameUrl = sFilenameUrl;
709     return this;
710   };
711   /**
712    * setLineNumber change the nLineNumber
713    * @member ErrorExt.prototype
714    * @param nLineNumber
715    * @type {Number}
716    * @return ErrorExt instance
717    */
718   ErrorExt.prototype.setLineNumber = function (nLineNumber) {
719     this.nLineNumber = nLineNumber;
720     return this;
721   };
722   /**
723    * setCategory change the sCategory
724    * @member ErrorExt.prototype
725    * @param sCategory
726    * @type {String}
727    * @return ErrorExt instance
728    */
729   ErrorExt.prototype.setCategory = function (sCategory) {
730     this.sCategory = sCategory;
731     return this;
732   };
733   /**
734    * setLevel change the level if oLevel is instance of Level
735    * @member ErrorExt.prototype
736    * @param oLevel
737    * @type {Level}
738    * @return ErrorExt instance
739    */
740   ErrorExt.prototype.setLevel = function (oLevel) {
741     if (oLevel instanceof Level) {
742       this.oLevel = oLevel;
743     }
744     return this;
745   };
746   /**
747    * setMessage change the sMessage
748    * @member ErrorExt.prototype
749    * @param sMessage
750    * @type {String}
751    * @return ErrorExt instance
752    */
753   ErrorExt.prototype.setMessage = function (sMessage) {
754     this.sMessage = sMessage;
755     return this;
756   };
757   /**
758    * setDateFormat change the sDateFormat
759    * @member ErrorExt.prototype
760    * @param sDateFormat
761    * @type {String}
762    * @return ErrorExt instance
763    */
764   ErrorExt.prototype.setDateFormat = function (sDateFormat) {
765     this.sDateFormat = sDateFormat;
766     return this;
767   };
768   /**
769    * getFormattedDate returns the error Date afte format it
770    * @member ErrorExt.prototype
771    * @return {String}
772    */
773   ErrorExt.prototype.getFormattedDate = function () {
774     return this.oDateFormatter.formatDate(this.dInitialize);
775   };
776   /**
777    * Level is the class that sets the Error level
778    * @private
779    * @class Level
780    * @constructor
781    * @param nLevel
782    * @type {Level}
783    * @param sLevel
784    * @type {String}
785    */
786   Level = function (nLevel, sLevel) {
787     this.nLevel = nLevel;
788     this.sLevel = sLevel;
789   };
790   /**
791    * getLevel return the Level from the sLevel Name
792    * @param sLevel
793    * @type {String}
794    * @return oNewLevel
795    * @type Level
796    */
797   Level.prototype.getLevel = function (sLevel) {
798     var oNewLevel = Level[sLevel];
799     if (typeof oNewLevel !== "undefined") {
800       return oNewLevel;
801     }
802     oNewLevel = null;
803   };
804   /**
805    * toString overwrittes the Object.prototype.toString to return the name of the Level
806    * @return sLevel
807    * @type {String}
808    */
809   Level.prototype.toString = function () {
810     return this.sLevel;
811   };
812   /**
813    * valueOf returns the number of Level
814    * @return nLevel
815    * @type {Number}
816    */
817   Level.prototype.valueOf = function () {
818     return this.nLevel;
819   };
820   /**
821    * @static
822    * @type {Number}
823    */
824   Level.nOFF = Number.MAX_VALUE;
825    /**
826    * @static
827    * @type {Number}
828    */
829   Level.nFATAL = 50000;
830    /**
831    * @static
832    * @type {Number}
833    */
834   Level.nERROR = 40000;
835    /**
836    * @static
837    * @type {Number}
838    */
839   Level.nWARNING = 30000;
840    /**
841    * @static
842    * @type {Number}
843    */
844   Level.nINFO = 20000;
845    /**
846    * @static
847    * @type {Number}
848    */
849   Level.nDEBUG = 10000;
850    /**
851    * @static
852    * @type {Number}
853    */
854   Level.nTRACE = 5000;
855    /**
856    * @static
857    * @type {Number}
858    */
859   Level.nALL = Number.MIN_VALUE;
860    /**
861    * @static
862    * @type {Level}
863    */
864   Level.OFF = new Level(Level.nOFF, "OFF");
865    /**
866    * @static
867    * @type {Level}
868    */
869   Level.FATAL = new Level(Level.nFATAL, "FATAL");
870    /**
871    * @static
872    * @type {Level}
873    */
874   Level.ERROR = new Level(Level.nERROR, "ERROR");
875    /**
876    * @static
877    * @type {Level}
878    */
879   Level.WARNING = new Level(Level.nWARNING, "WARN");
880    /**
881    * @static
882    * @type {Level}
883    */
884   Level.INFO = new Level(Level.nINFO, "INFO");
885    /**
886    * @static
887    * @type {Level}
888    */
889   Level.DEBUG = new Level(Level.nDEBUG, "DEBUG");
890    /**
891    * @static
892    * @type {Level}
893    */
894   Level.TRACE = new Level(Level.nTRACE, "TRACE");
895    /**
896    * @static
897    * @type {Level}
898    */
899   Level.ALL = new Level(Level.nALL, "ALL");
900   /**
901    * TraceMessage is the message class for automatic Trace
902    * @private
903    * @extends ErrorExt
904    * @class TraceMessage
905    * @constructor
906    * @param sMessage
907    * @type {String}
908    */
909   TraceMessage = function(sMessage)
910   {
911 	ErrorExt.apply(this, [Level.TRACE, 'Trace Message', sMessage]);
912   };
913   TraceMessage.prototype = new ErrorExt();
914   /**
915    * Trace is the class that manage all the info to be used in Tracer.
916    * @private
917    * @class Trace
918    * @constructor
919    * @param oConstructor
920    * @type {Function}
921    * @param sMethodName
922    * @type {String}
923    * @param oTracer
924    * @type {Tracer}
925    */
926   Trace = function (oConstructor, sMethodName, oTracer) {
927   	/**
928   	 * sIndentString is the number of spaces to indent functions.
929   	 * Missing modifycation - maybe in the next version -
930   	 * @member Trace.prototype
931   	 * @type {String}
932   	 */
933 	this.sIndentString = '';
934 	/**
935 	 * oConstructor is the oConstructor to trace
936   	 * @member Trace.prototype
937 	 * @type {Function}
938 	 */
939 	this.oConstructor = oConstructor;
940 	/**
941 	 * sMethodName is the name of the oConstructor
942   	 * @member Trace.prototype
943 	 * @type {String}
944 	 */
945 	this.sMethodName = sMethodName;
946 	/**
947 	 * oTracer is the reference to the Tracer
948   	 * @member Trace.prototype
949 	 * @type {Tracer}
950 	 */
951 	this.oTracer = oTracer;
952   };
953   /**
954    * formatArguments returns the arguments passed to the function as a string
955    * @member Trace.prototype
956    * @param aArgs
957    * @type {Array}
958    * @return {String}
959    */
960   Trace.prototype.formatArguments = function (aArgs)
961   {
962 	return '(' + aArgs.join(", ") + ')';
963   };
964   /**
965    * getIndentation returns the indentation
966    * @member Trace.prototype
967    * @return {String}
968    */
969   Trace.prototype.getIndentation = function () {
970   	return this.sIndentString;
971   };
972   /**
973    * getMethodNameIndentedAndParams return the string with method name and formatted arguments.
974    * @member Trace.prototype
975    * @return {String}
976    */
977   Trace.prototype.getMethodNameIndentedAndParams = function(aArgs)
978   {
979   	return (this.getIndentation() + this.sMethodName + this.formatArguments(aArgs));
980   };
981   /**
982    * getProfile return the string with method name, result and time that tooks the execution
983    * @member Trace.prototype
984    * @return {String}
985    */
986   Trace.prototype.getProfile = function(nStart, oResult)
987   {
988 	return this.getIndentation(true) + this.sMethodName + ' -> result: ' + oResult + '. (' + (+new Date() - nStart) +'ms)';
989   };
990   /**
991    * wrap is the method to wrap all the methods to trace
992    * @member Trace.prototype
993    */
994   Trace.prototype.wrap = function()
995   {
996   	var sKey = '';
997   	for(sKey in this.oConstructor)
998   	{
999 		this[sKey] = this.oConstructor[sKey];
1000   	}
1001   };
1002   /**
1003    * Tracer is the class that start the automatic tracer for the element that you want to trace
1004    * Is possible to trace more than one object at the same time.
1005    * @private
1006    * @class Tracer
1007    * @constructor
1008    */
1009   Tracer = function () {
1010   	/**
1011   	 * rNativeCode is the regular expression that returns if the content is native code or not.
1012   	 * @member Tracer.prototype
1013   	 * @type {RegExp}
1014   	 */
1015 	this.rNativeCode = /\[native code\]/;
1016 	/**
1017 	 * nIndentCount is the number of indents.
1018 	 * Missing modifycation - maybe in the next version -
1019 	 * @member Tracer.prototype
1020 	 * @type {Number}
1021 	 */
1022 	this.nIndentCount = -4;
1023 	/**
1024 	 * aTracing is the array where all the trace elements will be saved to remove it if needed
1025 	 * @member Tracer.prototype
1026 	 * @type {Array}
1027 	 */
1028 	this.aTracing = [];
1029   };
1030   /**
1031    * trace gets the method to trace and wraps his content
1032    * @member Tracer.prototype
1033    * @param oConstructor
1034    * @type {Function}
1035    * @param sMethodName
1036    * @type {String}
1037    * @return {mixed} Depends of the execution
1038    */
1039   Tracer.prototype.trace = function (oConstructor, sMethodName) {
1040 	var oTrace = new Trace(oConstructor, sMethodName, this);
1041 	oTrace.wrap();
1042 	oHermes.addError(new TraceMessage("Tracing: " + sMethodName));
1043 	return function () {
1044 		var oResult = null,
1045   			nStart = +new Date(),
1046   			aArgs = Array.prototype.slice.call(arguments);
1047   		oHermes.addError(new TraceMessage(oTrace.getMethodNameIndentedAndParams(aArgs)));
1048 		oResult = oConstructor.apply(oTrace, arguments);
1049 		oHermes.addError(new TraceMessage(oTrace.getProfile(nStart, oResult)));
1050 		return oResult;
1051 	}
1052   };
1053   /**
1054    * addTracing adds a new element to trace
1055    * @member Tracer.prototype
1056    * @param oTracing
1057    * @type {Trace}
1058    */
1059   Tracer.prototype.addTracing = function(oTracing)
1060   {
1061   	this.aTracing.push(oTracing);
1062   };
1063   /**
1064    * traceAll trace all the methods in oRoot to be tracable
1065    * @member Tracer.prototype
1066    * @param oRoot
1067    * @type {Object}
1068    * @param bRecurse
1069    * @type {Boolean}
1070    */
1071   Tracer.prototype.traceAll = function (oRoot, bRecurse)
1072   {
1073 	var sKey = '';
1074 	var oThat = null;
1075 	if ((oRoot === win) || !((typeof oRoot === 'object')) || (typeof oRoot === 'function')) {
1076 		return;
1077 	}
1078 	for(sKey in oRoot) {
1079 		if (oRoot[sKey] !== oRoot)
1080 		{
1081 			oThat = oRoot[sKey];
1082 			if (typeof oThat === 'function') {
1083 				if ((this !== oRoot) && !oThat.oConstructor && !this.rNativeCode.test(oThat)) {
1084 					oRoot[sKey] = this.trace(oRoot[sKey], sKey);
1085 					this.addTracing({
1086 						oObj: oRoot,
1087 						sMethodName: sKey
1088 					});
1089 				}
1090 			}
1091 			bRecurse && this.traceAll(oThat, true);
1092 		}
1093 	}
1094   };
1095   /**
1096    * resetTracing removes all the tracable elements
1097    * @member Tracer.prototype
1098    */
1099   Tracer.prototype.resetTracing = function () {
1100   	this.aTracking = [];
1101   };
1102   /**
1103    * untraceAll removes all the tracable elements and restore the original object
1104    * @member Tracer.prototype
1105    */
1106   Tracer.prototype.untraceAll = function () {
1107   	var nTrace = 0;
1108 	var aTracing = this.aTracing;
1109 	var nLenTrace = aTracing.length;
1110 	var oTrace = null;
1111 	for (; nTrace < nLenTrace; nTrace++) {
1112 		oTrace = aTracing[nTrace];
1113 		oTrace.oObj[oTrace.sMethodName] = oTrace.oObj[oTrace.sMethodName].oConstructor;
1114 	}
1115 	oHermes.addError(new TraceMessage("Tracing disabled"));
1116 	this.resetTracing();
1117   };
1118   /*
1119    * oHermes is the instance of Hermes.
1120    */
1121   oHermes = new Hermes(Level.ALL, Hermes.IMMEDIATE).addAppender(new ConsoleAppender());
1122   /*
1123    * fpErrorTrap is the method that will be called when uncaught errors are launched
1124    * Returns false to avoid default behaviour.
1125    * @return {Boolean}
1126    */
1127   win.onerror = function fpErrorTrap(oErrorMsg, sFileNameUrl, nLineNumber) {
1128   	oHermes.addError(new ErrorExt(oHermes.oLevel, getTypeFromMessage(oErrorMsg), removeTypeFromMessage(oErrorMsg), sFileNameUrl, nLineNumber));
1129     return false;
1130   };
1131   /*
1132    * This object expose the private classes as global to use it from outside of the module.
1133    */
1134   win.Hermes = {
1135     logger: oHermes,
1136     appender: Appender,
1137     level: Level,
1138     error: ErrorExt,
1139     layout: Layout,
1140     tracer: new Tracer()
1141   };
1142 }(window));
1143