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));