      1 (function($, rf) {
      2     var logLevels = ['debug', 'info', 'warn', 'error'];
      3     var logLevelsPadded = {'debug': 'debug', 'info': 'info ', 'warn': 'warn ', 'error': 'error'};
      4     var logLevelValues = {'debug': 1, 'info': 2, 'warn': 3, 'error': 4};
      5
      6     var logClassMethods = {
      7
      8         __import: function(doc, node) {
      9             if (doc === document) {
     10                 return node;
     11             }
     12
     13             var result = $();
     14             for (var i = 0; i < node.length; i++) {
     15                 if (doc.importNode) {
     16                     result = result.add(doc.importNode(node[i], true));
     17                 } else {
     18                     var container = doc.createElement("div");
     19                     container.innerHTML = node[i].outerHTML;
     20                     for (var child = container.firstChild; child; child = child.nextSibling) {
     21                         result = result.add(child);
     22                     }
     23                 }
     24             }
     25
     26             return result;
     27         },
     28
     29         __getStyles: function() {
     30             var head = $("head");
     31
     32             if (head.length == 0) {
     33                 return "";
     34             }
     35
     36             try {
     37                 //TODO - BASE element support?
     38                 var clonedHead = head.clone();
     39                 if (clonedHead.children().length == head.children().length) {
     40                     return clonedHead.children(":not(style):not(link[rel='stylesheet'])").remove().end().html();
     41                 } else {
     42                     var result = new Array();
     43                     head.children("style, link[rel='stylesheet']").each(function() {
     44                         result.push(this.outerHTML);
     45                     });
     46
     47                     return result.join('');
     48                 }
     49             } catch (e) {
     50                 return "";
     51             }
     52         },
     53
     54         __openPopup: function() {
     55             if (!this.__popupWindow || this.__popupWindow.closed) {
     56                 this.__popupWindow = open("", "_richfaces_logWindow", "height=400, width=600, resizable = yes, status=no, " +
     57                     "scrollbars = yes, statusbar=no, toolbar=no, menubar=no, location=no");
     58
     59                 var doc = this.__popupWindow.document;
     60
     61                 doc.write("<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">" +
     62                     "<html xmlns=\"http://www.w3.org/1999/xhtml\"><head>" + this.__getStyles() + "</head>" +
     63                     "<body onunload='window.close()'><div id='richfaces.log' clas='rf-log rf-log-popup'></div></body></html>");
     64                 doc.close();
     65                 this.__initializeControls(doc);
     66             } else {
     67                 this.__popupWindow.focus();
     68             }
     69         },
     70
     71         __hotkeyHandler: function(event) {
     72             if (event.ctrlKey && event.shiftKey) {
     73                 if ((this.hotkey || 'l').toLowerCase() == String.fromCharCode(event.keyCode).toLowerCase()) {
     74                     this.__openPopup();
     75                 }
     76             }
     77         },
     78
     79         __getTimeAsString: function() {
     80             var date = new Date();
     81
     82             var timeString = this.__lzpad(date.getHours(), 2) + ':' + this.__lzpad(date.getMinutes(), 2) + ':' +
     83                 this.__lzpad(date.getSeconds(), 2) + '.' + this.__lzpad(date.getMilliseconds(), 3);
     84
     85             return timeString;
     86         },
     87
     88         __lzpad: function(s, length) {
     89             s = s.toString();
     90             var a = new Array();
     91             for (var i = 0; i < length - s.length; i++) {
     92                 a.push('0');
     93             }
     94             a.push(s);
     95             return a.join('');
     96         },
     97
     98         __getMessagePrefix: function(level) {
     99             return logLevelsPadded[level] + '[' + this.__getTimeAsString() + ']: ';
    100         },
    101
    102         __setLevelFromSelect: function(event) {
    103             this.setLevel(event.target.value);
    104         },
    105
    106         __initializeControls : function(doc) {
    107             var console = $("#richfaces\\.log", doc);
    108
    109             var clearBtn = console.children("button.rf-log-element");
    110             if (clearBtn.length == 0) {
    111                 clearBtn = $("<button type='button' name='clear' class='rf-log-element'>Clear</button>", doc).appendTo(console);
    112             }
    113
    114             clearBtn.click($.proxy(this.clear, this));
    115
    116             var levelSelect = console.children("select.rf-log-element");
    117             if (levelSelect.length == 0) {
    118                 levelSelect = $("<select class='rf-log-element' name='richfaces.log' />", doc).appendTo(console);
    119             }
    120
    121             if (levelSelect.children().length == 0) {
    122                 for (var l = 0; l < logLevels.length; l++) {
    123                     $("<option value='" + logLevels[l] + "'>" + logLevels[l] + "</option>", doc).appendTo(levelSelect);
    124                 }
    125             }
    126
    127             levelSelect.val(this.getLevel());
    128             levelSelect.change($.proxy(this.__setLevelFromSelect, this));
    129
    130             var consoleEntries = console.children(".rf-log-contents");
    131             if (consoleEntries.length == 0) {
    132                 consoleEntries = $("<div class='rf-log-contents'></div>", doc).appendTo(console);
    133             }
    134             this.__contentsElement = consoleEntries;
    135         },
    136
    137         __append: function(element) {
    138             var target = this.__contentsElement;
    139             if (this.mode == "popup") {
    140                 var doc = this.__popupWindow.document;
    141                 $(doc.createElement("div")).appendTo(target).append(this.__import(doc, element));
    142             } else {
    143                 $(document.createElement("div")).appendTo(target).append(element);
    144             }
    145         },
    146
    147         __log: function(level, message) {
    148             var configuredLevel = this.getLevel();
    149
    150             if (!logLevelValues[configuredLevel]) {
    151                 // unknown log level
    152                 if (console.log) {
    153                   console.log('Warning: unknown log level "' + this.getLevel() + '" - using log level "debug"');
    154                 }
    155                 configuredLevel = 'debug';
    156             }
    157             if (logLevelValues[level] < logLevelValues[configuredLevel]) {
    158                 // message is not loggable due to its level
    159                 return;
    160             }
    161
    162             if (this.mode == 'console') {
    163                 var logMsg = 'RichFaces: ' + message;
    164                 if (console[level]) {
    165                     console[level](logMsg);
    166                 } else if (console.log) {
    167                     console.log(logMsg);
    168                 }
    169                 return;
    170             }
    171
    172             if (!this.__contentsElement) {
    173                 return;
    174             }
    175
    176             var newEntry = $();
    177             newEntry = newEntry.add($("<span class='rf-log-entry-lbl rf-log-entry-lbl-" + level + "'></span>").text(this.__getMessagePrefix(level)));
    178
    179             var entrySpan = $("<span class='rf-log-entry-msg rf-log-entry-msg-" + level + "'></span>");
    180             if (typeof message != 'object' || !message.appendTo) {
    181                 entrySpan.text(message);
    182             } else {
    183                 message.appendTo(entrySpan);
    184             }
    185
    186             newEntry = newEntry.add(entrySpan);
    187
    188             this.__append(newEntry);
    189         },
    190
    191         init: function(options) {
    192             $super.constructor.call(this, 'richfaces.log');
    193             this.attachToDom();
    194             rf.setLog(this);
    195
    196             options = options || {};
    197
    198             this.level = (options.level || 'info').toLowerCase();
    199             this.hotkey = options.hotkey;
    200             this.mode = (options.mode || 'inline');
    201
    202             if (this.mode == 'console') {
    203                 // do nothing
    204             } else if (this.mode == 'popup') {
    205                 this.__boundHotkeyHandler = $.proxy(this.__hotkeyHandler, this);
    206                 $(document).bind('keydown', this.__boundHotkeyHandler);
    207             } else {
    208                 this.__initializeControls(document);
    209             }
    210         },
    211
    212         destroy: function() {
    213             rf.setLog(null);
    214
    215             //TODO test this method
    216             if (this.__popupWindow) {
    217                 this.__popupWindow.close();
    218             }
    219             this.__popupWindow = null;
    220
    221             if (this.__boundHotkeyHandler) {
    222                 $(document).unbind('keydown', this.__boundHotkeyHandler);
    223                 this.__boundHotkeyHandler = null;
    224             }
    225
    226             this.__contentsElement = null;
    227             $super.destroy.call(this);
    228         },
    229
    230         setLevel: function(level) {
    231             this.level = level;
    232             this.clear();
    233         },
    234
    235         getLevel: function() {
    236             return this.level || 'info';
    237         },
    238
    239         clear: function() {
    240             if (this.__contentsElement) {
    241                 this.__contentsElement.children().remove();
    242             }
    243         }
    244     };
    245
    246     for (var i = 0; i < logLevels.length; i++) {
    247         logClassMethods[logLevels[i]] = (function() {
    248             var level = logLevels[i];
    249             return function(message) {
    250                 this.__log(level, message);
    251             }
    252         }());
    253     }
    254
    255     rf.HtmlLog = rf.BaseComponent.extendClass(logClassMethods);
    256     // define super class link
    257     var $super = rf.HtmlLog.$super;
    258
    259     $(document).ready(function() {
    260         if (typeof jsf != 'undefined') {
    261             (function($, rf, jsf) {
    262
    263                 //JSF log adapter
    264                 var identifyElement = function(elt) {
    265                     var identifier = '<' + elt.tagName.toLowerCase();
    266                     var e = $(elt);
    267                     if (e.attr('id')) {
    268                         identifier += (' id=' + e.attr('id'));
    269                     }
    270                     if (e.attr('class')) {
    271                         identifier += (' class=' + e.attr('class'));
    272                     }
    273
    274                     identifier += ' ...>';
    275
    276                     return identifier;
    277                 }
    278
    279                 var formatPartialResponseElement = function(logElement, responseElement) {
    280                     var change = $(responseElement);
    281
    282                     logElement.append("Element <b>" + responseElement.nodeName + "</b>");
    283                     if (change.attr("id")) {
    284                         logElement.append(document.createTextNode(" for id=" + change.attr("id")));
    285                     }
    286
    287                     $(document.createElement("br")).appendTo(logElement);
    288                     $("<span class='rf-log-entry-msg-xml'></span>").appendTo(logElement).text(change.toXML());
    289                     $(document.createElement("br")).appendTo(logElement);
    290                 }
    291
    292                 var formatPartialResponse = function(partialResponse) {
    293                     var logElement = $(document.createElement("span"));
    294
    295                     partialResponse.children().each(function() {
    296                         var responseElement = $(this);
    297                         if (responseElement.is('changes')) {
    298                             logElement.append("Listing content of response <b>changes</b> element:<br />");
    299                             responseElement.children().each(function() {
    300                                 formatPartialResponseElement(logElement, this);
    301                             });
    302                         } else {
    303                             formatPartialResponseElement(logElement, this);
    304                         }
    305                     });
    306
    307                     return logElement;
    308                 }
    309
    310                 var jsfAjaxLogAdapter = function(data) {
    311                     try {
    312                         var log = rf.log;
    313
    314                         var source = data.source;
    315                         var type = data.type;
    316
    317                         var responseCode = data.responseCode;
    318                         var responseXML = data.responseXML;
    319                         var responseText = data.responseText;
    320
    321                         if (type != 'error') {
    322                             log.info("Received '" + type + "' event from " + identifyElement(source));
    323
    324                             if (type == 'beforedomupdate') {
    325                                 var partialResponse;
    326
    327                                 if (responseXML) {
    328                                     partialResponse = $(responseXML).children("partial-response");
    329                                 }
    330
    331                                 var responseTextEntry = $("<span>Server returned responseText: </span><span class='rf-log-entry-msg-xml'></span>").eq(1).text(responseText).end();
    332
    333                                 if (partialResponse && partialResponse.length) {
    334                                     log.debug(responseTextEntry);
    335                                     log.info(formatPartialResponse(partialResponse));
    336                                 } else {
    337                                     log.info(responseTextEntry);
    338                                 }
    339                             }
    340                         } else {
    341                             var status = data.status;
    342                             log.error("Received '" + type + '@' + status + "' event from " + identifyElement(source));
    343
    344                             var message = "[status=" + data.responseCode + "] ";
    345                             if (data.errorName && data.errorMessage) {
    346                                 message += " " + data.errorName + ": " + data.errorMessage;
    347                             } else if (data.description) {
    348                                 message += " " + data.description;
    349                             } else {
    350                                 message += " no error details";
    351                             }
    352                             log.error(message);
    353                         }
    354                     } catch (e) {
    355                         //ignore logging errors
    356                     }
    357                 };
    358
    359                 var eventsListener = rf.createJSFEventsAdapter({
    360                         begin: jsfAjaxLogAdapter,
    361                         beforedomupdate: jsfAjaxLogAdapter,
    362                         success: jsfAjaxLogAdapter,
    363                         complete: jsfAjaxLogAdapter,
    364                         error: jsfAjaxLogAdapter
    365                     });
    366
    367                 jsf.ajax.addOnEvent(eventsListener);
    368                 jsf.ajax.addOnError(eventsListener);
    369                 //
    370             }($, rf, jsf));
    371         }
    372     });
    373
    374 }(RichFaces.jQuery, RichFaces));