]> _ Git - cubeextranet.git/commitdiff
(no commit message)
authorvincent@cubedesigners.com <vincent@cubedesigners.com@f5622870-0f3c-0410-866d-9cb505b7a8ef>
Mon, 12 Jan 2015 12:51:55 +0000 (12:51 +0000)
committervincent@cubedesigners.com <vincent@cubedesigners.com@f5622870-0f3c-0410-866d-9cb505b7a8ef>
Mon, 12 Jan 2015 12:51:55 +0000 (12:51 +0000)
12 files changed:
fluidbook/compile/_js/log4js.js
fluidbook/compile/index.html
inc/ws/Controlleur/class.ws.flash.php
inc/ws/Controlleur/class.ws.maintenance.php
inc/ws/DAO/class.ws.dao.book.php
inc/ws/DAO/class.ws.dao.document.php
inc/ws/Metier/class.ws.document.php
inc/ws/Util/class.ws.pdf.fontextractor.php
inc/ws/Util/class.ws.pdf.fontextractor.php.php
inc/ws/Util/fontextractor/class.ws.pdf.fontextractor.php
inc/ws/Util/packager/class.ws.packager.html.php
inc/ws/Util/packager/class.ws.packager.v1.php

index d2c9dd6fe9e8c6d7ce7d7a4543d3e92b80ddabd2..a1154f1d8ffe1bb2bd0dd4a183bec0c8339e4cd9 100644 (file)
-/*\r
- * Licensed under the Apache License, Version 2.0 (the "License");\r
- * you may not use this file except in compliance with the License.\r
- * You may obtain a copy of the License at\r
- *\r
- *      http://www.apache.org/licenses/LICENSE-2.0\r
- *\r
- * Unless required by applicable law or agreed to in writing, software\r
- * distributed under the License is distributed on an "AS IS" BASIS,\r
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
- * See the License for the specific language governing permissions and\r
- * limitations under the License.\r
- */\r
-\r
-/*jsl:option explicit*/\r
-\r
-/**\r
- * @fileoverview log4js is a library to log in JavaScript in similar manner \r
- * than in log4j for Java. The API should be nearly the same.\r
- * \r
- * This file contains all log4js code and is the only file required for logging.\r
- * \r
- * <h3>Example:</h3>\r
- * <pre>\r
- *  var log = new Log4js.getLogger("some-category-name"); //create logger instance\r
- *  log.setLevel(Log4js.Level.TRACE); //set the Level\r
- *  log.addAppender(new ConsoleAppender(log, false)); // console that launches in new window\r
\r
- *  // if multiple appenders are set all will log\r
- *  log.addAppender(new ConsoleAppender(log, true)); // console that is in-line in the page\r
- *  log.addAppender(new FileAppender("C:\\somefile.log")); // file appender logs to C:\\somefile.log\r
- * \r
- *  ...\r
- * \r
- *  //call the log\r
- *  log.trace("trace me" );\r
- * </pre>\r
- *\r
- * @version 0.3\r
- * @author Stephan Strittmatter - http://jroller.com/page/stritti\r
- * @author Seth Chisamore - http://www.chisamore.com\r
- * @since 2005-05-20\r
- * Website: http://log4js.berlios.de\r
- */\r
-var Log4js = {\r
-       \r
-       /** \r
-        * Current version of log4js. \r
-        * @static\r
-        * @final\r
-        */\r
-       version: "1.0",\r
-\r
-       /**  \r
-        * Date of logger initialized.\r
-        * @static\r
-        * @final\r
-        */\r
-       applicationStartDate: new Date(),\r
-               \r
-       /**  \r
-        * Hashtable of loggers.\r
-        * @static\r
-        * @final\r
-        * @private  \r
-        */\r
-       loggers: {},\r
-       \r
-       /**\r
-        * Get a logger instance. Instance is cached on categoryName level.\r
-        * @param  {String} categoryName name of category to log to.\r
-        * @return {Logger} instance of logger for the category\r
-        * @static\r
-        */\r
-       getLogger: function(categoryName) {\r
-               \r
-               // Use default logger if categoryName is not specified or invalid\r
-               if (!(typeof categoryName == "string")) {\r
-                       categoryName = "[default]";\r
-               }\r
-\r
-               if (!Log4js.loggers[categoryName]) {\r
-                       // Create the logger for this name if it doesn't already exist\r
-                       Log4js.loggers[categoryName] = new Log4js.Logger(categoryName);\r
-               }\r
-               \r
-               return Log4js.loggers[categoryName];\r
-       },\r
-       \r
-       /**\r
-        * Get the default logger instance.\r
-        * @return {Logger} instance of default logger\r
-        * @static\r
-        */\r
-       getDefaultLogger: function() {\r
-               return Log4js.getLogger("[default]"); \r
-       },\r
-       \r
-       /**\r
-        * Atatch an observer function to an elements event browser independent.\r
-        * \r
-        * @param element element to attach event\r
-        * @param name name of event\r
-        * @param observer observer method to be called\r
-        * @private\r
-        */\r
-       attachEvent: function (element, name, observer) {\r
-               if (element.addEventListener) { //DOM event model\r
-                       element.addEventListener(name, observer, false);\r
-       } else if (element.attachEvent) { //M$ event model\r
-                       element.attachEvent('on' + name, observer);\r
-       }\r
-       }\r
-       \r
-       /**\r
-        * Load a JS-script dynamically.\r
-        * @param {String} src\r
-        */\r
-/*     $import: function (src) {\r
-               var documentScripts = document.getElementsByTagName("script");\r
-       \r
-               for (index = 0; index < documentScripts.length; ++index)\r
-               {\r
-                       var documentScript = documentScripts[index];\r
-                       if (documentScript.src == src) {\r
-                               return false;\r
-                       }\r
-               }\r
-               \r
-               var script = document.createElement('script');\r
-               script.type = 'text/javascript';\r
-               script.src = src;\r
-               document.getElementsByTagName('head')[0].appendChild(script); \r
-               \r
-               return true;\r
-       }\r
-       */\r
-};\r
-\r
-/**\r
- * Internal object extension (OO) methods.\r
- * \r
- * @private\r
- * @ignore\r
- */\r
-Log4js.extend = function(destination, source) {\r
-  for (property in source) {\r
-    destination[property] = source[property];\r
-  }\r
-  return destination;\r
-}\r
-    \r
-/**\r
- * Functions taken from Prototype library,  \r
- * didn't want to require for just few functions.\r
- * More info at {@link http://prototype.conio.net/}\r
- * @private\r
- */    \r
-Log4js.bind = function(fn, object) {\r
-  return function() {\r
-        return fn.apply(object, arguments);\r
-  };\r
-};\r
-\r
-/**\r
- * Log4js.Level Enumeration. Do not use directly. Use static objects instead.\r
- * @constructor\r
- * @param {Number} level number of level\r
- * @param {String} levelString String representation of level\r
- * @private\r
- */\r
-Log4js.Level = function(level, levelStr) {\r
-       this.level = level;\r
-       this.levelStr = levelStr;\r
-};\r
-\r
-Log4js.Level.prototype =  {\r
-       /** \r
-        * converts given String to corresponding Level\r
-        * @param {String} sArg String value of Level\r
-        * @param {Log4js.Level} defaultLevel default Level, if no String representation\r
-        * @return Level object\r
-        * @type Log4js.Level\r
-        */\r
-       toLevel: function(sArg, defaultLevel) {                  \r
-                               \r
-               if(sArg === null) {\r
-                       return defaultLevel;\r
-               }\r
-               \r
-               if(typeof sArg == "string") { \r
-                       var s = sArg.toUpperCase();\r
-                       if(s == "ALL") {return Log4js.Level.ALL;}\r
-                       if(s == "DEBUG") {return Log4js.Level.DEBUG;}\r
-                       if(s == "INFO") {return Log4js.Level.INFO;}\r
-                       if(s == "WARN") {return Log4js.Level.WARN;}\r
-                       if(s == "ERROR") {return Log4js.Level.ERROR;}\r
-                       if(s == "FATAL") {return Log4js.Level.FATAL;}\r
-                       if(s == "OFF") {return Log4js.Level.OFF;}\r
-                       if(s == "TRACE") {return Log4js.Level.TRACE;}\r
-                       return defaultLevel;\r
-               } else if(typeof sArg == "number") {\r
-                       switch(sArg) {\r
-                               case ALL_INT: return Log4js.Level.ALL;\r
-                               case DEBUG_INT: return Log4js.Level.DEBUG;\r
-                               case INFO_INT: return Log4js.Level.INFO;\r
-                               case WARN_INT: return Log4js.Level.WARN;\r
-                               case ERROR_INT: return Log4js.Level.ERROR;\r
-                               case FATAL_INT: return Log4js.Level.FATAL;\r
-                               case OFF_INT: return Log4js.Level.OFF;\r
-                               case TRACE_INT: return Log4js.Level.TRACE;\r
-                               default: return defaultLevel;\r
-                       }\r
-               } else {\r
-                       return defaultLevel;    \r
-               }\r
-       },      \r
-       /** \r
-        * @return  converted Level to String\r
-        * @type String\r
-        */             \r
-       toString: function() {\r
-               return this.levelStr;   \r
-       },\r
-       /** \r
-        * @return internal Number value of Level\r
-        * @type Number\r
-        */                     \r
-       valueOf: function() {\r
-               return this.level;\r
-       }\r
-};\r
-\r
-// Static variables\r
-/** \r
- * @private\r
- */\r
-Log4js.Level.OFF_INT = Number.MAX_VALUE;\r
-/** \r
- * @private\r
- */\r
-Log4js.Level.FATAL_INT = 50000;\r
-/** \r
- * @private\r
- */\r
-Log4js.Level.ERROR_INT = 40000;\r
-/** \r
- * @private\r
- */\r
-Log4js.Level.WARN_INT = 30000;\r
-/** \r
- * @private\r
- */\r
-Log4js.Level.INFO_INT = 20000;\r
-/** \r
- * @private\r
- */\r
-Log4js.Level.DEBUG_INT = 10000;\r
-/** \r
- * @private\r
- */\r
-Log4js.Level.TRACE_INT = 5000;\r
-/** \r
- * @private\r
- */\r
-Log4js.Level.ALL_INT = Number.MIN_VALUE;\r
-\r
-/** \r
- * Logging Level OFF - all disabled\r
- * @type Log4js.Level\r
- * @static\r
- */\r
-Log4js.Level.OFF = new Log4js.Level(Log4js.Level.OFF_INT, "OFF");\r
-/** \r
- * Logging Level Fatal\r
- * @type Log4js.Level\r
- * @static\r
- */\r
-Log4js.Level.FATAL = new Log4js.Level(Log4js.Level.FATAL_INT, "FATAL");\r
-/** \r
- * Logging Level Error\r
- * @type Log4js.Level\r
- * @static\r
- */\r
-Log4js.Level.ERROR = new Log4js.Level(Log4js.Level.ERROR_INT, "ERROR"); \r
-/** \r
- * Logging Level Warn\r
- * @type Log4js.Level\r
- * @static\r
- */\r
-Log4js.Level.WARN = new Log4js.Level(Log4js.Level.WARN_INT, "WARN"); \r
-/** \r
- * Logging Level Info\r
- * @type Log4js.Level\r
- * @static\r
- */\r
-Log4js.Level.INFO = new Log4js.Level(Log4js.Level.INFO_INT, "INFO");     \r
-/** \r
- * Logging Level Debug\r
- * @type Log4js.Level\r
- * @static\r
- */\r
-Log4js.Level.DEBUG = new Log4js.Level(Log4js.Level.DEBUG_INT, "DEBUG");  \r
-/** \r
- * Logging Level Trace\r
- * @type Log4js.Level\r
- * @static\r
- */\r
-Log4js.Level.TRACE = new Log4js.Level(Log4js.Level.TRACE_INT, "TRACE");  \r
-/** \r
- * Logging Level All - All traces are enabled\r
- * @type Log4js.Level\r
- * @static\r
- */\r
-Log4js.Level.ALL = new Log4js.Level(Log4js.Level.ALL_INT, "ALL"); \r
-\r
-/**\r
- * Log4js CustomEvent\r
- * @constructor\r
- * @author Corey Johnson - original code in Lumberjack (http://gleepglop.com/javascripts/logger/)\r
- * @author Seth Chisamore - adapted for Log4js\r
- * @private\r
- */\r
-Log4js.CustomEvent = function() {\r
-       this.listeners = [];\r
-};\r
-\r
-Log4js.CustomEvent.prototype = {\r
\r
-       /**\r
-        * @param method method to be added\r
-        */ \r
-       addListener : function(method) {\r
-               this.listeners.push(method);\r
-       },\r
-\r
-       /**\r
-        * @param method method to be removed\r
-        */ \r
-       removeListener : function(method) {\r
-               var foundIndexes = this.findListenerIndexes(method);\r
-\r
-               for(var i = 0; i < foundIndexes.length; i++) {\r
-                       this.listeners.splice(foundIndexes[i], 1);\r
-               }\r
-       },\r
-\r
-       /**\r
-        * @param handler\r
-        */ \r
-       dispatch : function(handler) {\r
-               for(var i = 0; i < this.listeners.length; i++) {\r
-                       try {\r
-                               this.listeners[i](handler);\r
-                       }\r
-                       catch (e) {\r
-                               log4jsLogger.warn("Could not run the listener " + this.listeners[i] + ". \n" + e);\r
-                       }\r
-               }\r
-       },\r
-\r
-       /**\r
-        * @private\r
-        * @param method\r
-        */\r
-       findListenerIndexes : function(method) {\r
-               var indexes = [];\r
-               for(var i = 0; i < this.listeners.length; i++) {                        \r
-                       if (this.listeners[i] == method) {\r
-                               indexes.push(i);\r
-                       }\r
-               }\r
-\r
-               return indexes;\r
-       }\r
-};\r
-\r
-/**\r
- * Models a logging event.\r
- * @constructor\r
- * @param {String} categoryName name of category\r
- * @param {Log4js.Level} level level of message\r
- * @param {String} message message to log\r
- * @param {Log4js.Logger} logger the associated logger\r
- * @author Seth Chisamore\r
- */\r
-Log4js.LoggingEvent = function(categoryName, level, message, exception, logger) {\r
-       /**\r
-        * the timestamp of the Logging Event\r
-        * @type Date\r
-        * @private\r
-        */\r
-       this.startTime = new Date();\r
-       /**\r
-        * category of event\r
-        * @type String\r
-        * @private\r
-        */\r
-       this.categoryName = categoryName;\r
-       /**\r
-        * the logging message\r
-        * @type String\r
-        * @private\r
-        */\r
-       this.message = message;\r
-       /**\r
-        * the logging exception\r
-        * @type Exception\r
-        * @private\r
-        */\r
-       this.exception = exception;\r
-       /**\r
-        * level of log\r
-        * @type Log4js.Level\r
-        * @private\r
-        */\r
-       this.level = level;\r
-       /**\r
-        * reference to logger\r
-        * @type Log4js.Logger\r
-        * @private\r
-        */\r
-       this.logger = logger;\r
-};\r
-\r
-Log4js.LoggingEvent.prototype = {      \r
-       /**\r
-        * get the timestamp formatted as String.\r
-        * @return {String} formatted timestamp\r
-        * @see Log4js#setDateFormat()\r
-        */\r
-       getFormattedTimestamp: function() {\r
-               if(this.logger) {\r
-                       return this.logger.getFormattedTimestamp(this.startTime);\r
-               } else {\r
-                       return this.startTime.toGMTString();\r
-               }\r
-       }\r
-};\r
-\r
-/**\r
- * Logger to log messages to the defined appender.</p>\r
- * Default appender is Appender, which is ignoring all messages. Please\r
- * use setAppender() to set a specific appender (e.g. WindowAppender).\r
- * use {@see Log4js#getLogger(String)} to get an instance.\r
- * @constructor\r
- * @param name name of category to log to\r
- * @author Stephan Strittmatter\r
- */\r
-Log4js.Logger = function(name) {\r
-       this.loggingEvents = [];\r
-       this.appenders = [];\r
-       /** category of logger */\r
-       this.category = name || "";\r
-       /** level to be logged */\r
-       this.level = Log4js.Level.FATAL;\r
-       \r
-       this.dateformat = Log4js.DateFormatter.DEFAULT_DATE_FORMAT;\r
-       this.dateformatter = new Log4js.DateFormatter();\r
-       \r
-       this.onlog = new Log4js.CustomEvent();\r
-       this.onclear = new Log4js.CustomEvent();\r
-       \r
-       /** appender to write in */\r
-       this.appenders.push(new Log4js.Appender(this));\r
-       \r
-       // if multiple log objects are instantiated this will only log to the log \r
-       // object that is declared last can't seem to get the attachEvent method to \r
-       // work correctly\r
-       try {\r
-               window.onerror = this.windowError.bind(this);\r
-       } catch (e) {\r
-               //log4jsLogger.fatal(e);\r
-       }\r
-};\r
-\r
-Log4js.Logger.prototype = {\r
-\r
-       /**\r
-        * add additional appender. DefaultAppender always is there.\r
-        * @param appender additional wanted appender\r
-        */\r
-       addAppender: function(appender) {\r
-               if (appender instanceof Log4js.Appender) {\r
-                       appender.setLogger(this);\r
-                       this.appenders.push(appender);                  \r
-               } else {\r
-                       throw "Not instance of an Appender: " + appender;\r
-               }\r
-       },\r
-\r
-       /**\r
-        * set Array of appenders. Previous Appenders are cleared and removed.\r
-        * @param {Array} appenders Array of Appenders\r
-        */\r
-       setAppenders: function(appenders) {\r
-               //clear first all existing appenders\r
-               for(var i = 0; i < this.appenders.length; i++) {\r
-                       this.appenders[i].doClear();\r
-               }\r
-               \r
-               this.appenders = appenders;\r
-               \r
-               for(var j = 0; j < this.appenders.length; j++) {\r
-                       this.appenders[j].setLogger(this);\r
-               }\r
-       },\r
-       \r
-       /**\r
-        * Set the Loglevel default is LogLEvel.TRACE\r
-        * @param level wanted logging level\r
-        */\r
-       setLevel: function(level) {\r
-               this.level = level;\r
-       },\r
-       \r
-       /** \r
-        * main log method logging to all available appenders \r
-        * @private\r
-        */\r
-       log: function(logLevel, message, exception) {\r
-               var loggingEvent = new Log4js.LoggingEvent(this.category, logLevel, \r
-                       message, exception, this);\r
-               this.loggingEvents.push(loggingEvent);\r
-               this.onlog.dispatch(loggingEvent);\r
-       },\r
-       \r
-       /** clear logging */\r
-       clear : function () {\r
-               try{\r
-                       this.loggingEvents = [];\r
-                       this.onclear.dispatch();\r
-               } catch(e){}\r
-       },\r
-       /** checks if Level Trace is enabled */\r
-       isTraceEnabled: function() {\r
-               if (this.level.valueOf() <= Log4js.Level.TRACE.valueOf()) {\r
-                       return true;\r
-               }\r
-               return false;\r
-       },\r
-       /** \r
-        * Trace messages \r
-        * @param message {Object} message to be logged\r
-        */\r
-       trace: function(message) {\r
-               if (this.isTraceEnabled()) {\r
-                       this.log(Log4js.Level.TRACE, message, null);\r
-               }\r
-       },\r
-       /** checks if Level Debug is enabled */\r
-       isDebugEnabled: function() {\r
-               if (this.level.valueOf() <= Log4js.Level.DEBUG.valueOf()) {\r
-                       return true;\r
-               }\r
-               return false;\r
-       },\r
-       /** \r
-        * Debug messages \r
-        * @param message {Object} message to be logged\r
-        */\r
-       debug: function(message) {\r
-               if (this.isDebugEnabled()) {\r
-                       this.log(Log4js.Level.DEBUG, message, null);\r
-               }\r
-       },\r
-       /**\r
-        * Debug messages \r
-        * @param {Object} message  message to be logged\r
-        * @param {Throwable} throwable \r
-        */\r
-       debug: function(message, throwable) {\r
-               if (this.isDebugEnabled()) {\r
-                       this.log(Log4js.Level.DEBUG, message, throwable);\r
-               }\r
-       },      \r
-       /** checks if Level Info is enabled */\r
-       isInfoEnabled: function() {\r
-               if (this.level.valueOf() <= Log4js.Level.INFO.valueOf()) {\r
-                       return true;\r
-               }\r
-               return false;\r
-       },\r
-       /** \r
-        * logging info messages \r
-        * @param {Object} message  message to be logged\r
-        */\r
-       info: function(message) {\r
-               if (this.isInfoEnabled()) {\r
-                       this.log(Log4js.Level.INFO, message, null);\r
-               }\r
-       },\r
-       /** \r
-        * logging info messages \r
-        * @param {Object} message  message to be logged\r
-        * @param {Throwable} throwable  \r
-        */\r
-       info: function(message, throwable) {\r
-               if (this.isInfoEnabled()) {\r
-                       this.log(Log4js.Level.INFO, message, throwable);\r
-               }\r
-       },\r
-       /** checks if Level Warn is enabled */\r
-       isWarnEnabled: function() {\r
-               if (this.level.valueOf() <= Log4js.Level.WARN.valueOf()) {\r
-                       return true;\r
-               }\r
-               return false;\r
-       },\r
-\r
-       /** logging warn messages */\r
-       warn: function(message) {\r
-               if (this.isWarnEnabled()) {\r
-                       this.log(Log4js.Level.WARN, message, null);\r
-               }\r
-       },\r
-       /** logging warn messages */\r
-       warn: function(message, throwable) {\r
-               if (this.isWarnEnabled()) {\r
-                       this.log(Log4js.Level.WARN, message, throwable);\r
-               }\r
-       },\r
-       /** checks if Level Error is enabled */\r
-       isErrorEnabled: function() {\r
-               if (this.level.valueOf() <= Log4js.Level.ERROR.valueOf()) {\r
-                       return true;\r
-               }\r
-               return false;\r
-       },\r
-       /** logging error messages */\r
-       error: function(message) {\r
-               if (this.isErrorEnabled()) {\r
-                       this.log(Log4js.Level.ERROR, message, null);\r
-               }\r
-       },\r
-       /** logging error messages */\r
-       error: function(message, throwable) {\r
-               if (this.isErrorEnabled()) {\r
-                       this.log(Log4js.Level.ERROR, message, throwable);\r
-               }\r
-       },\r
-       /** checks if Level Fatal is enabled */\r
-       isFatalEnabled: function() {\r
-               if (this.level.valueOf() <= Log4js.Level.FATAL.valueOf()) {\r
-                       return true;\r
-               }\r
-               return false;\r
-       },\r
-       /** logging fatal messages */\r
-       fatal: function(message) {\r
-               if (this.isFatalEnabled()) {\r
-                       this.log(Log4js.Level.FATAL, message, null);\r
-               }\r
-       },\r
-       /** logging fatal messages */\r
-       fatal: function(message, throwable) {\r
-               if (this.isFatalEnabled()) {\r
-                       this.log(Log4js.Level.FATAL, message, throwable);\r
-               }\r
-       },      \r
-       /** \r
-        * Capture main window errors and log as fatal.\r
-        * @private\r
-        */\r
-       windowError: function(msg, url, line){\r
-               var message = "Error in (" + (url || window.location) + ") on line "+ line +" with message (" + msg + ")";\r
-               this.log(Log4js.Level.FATAL, message, null);    \r
-       },\r
-       \r
-       /**\r
-        * Set the date format of logger. Following switches are supported:\r
-        * <ul>\r
-        * <li>yyyy - The year</li>\r
-        * <li>MM - the month</li>\r
-        * <li>dd - the day of month<li>\r
-        * <li>hh - the hour<li>\r
-        * <li>mm - minutes</li>\r
-        * <li>O - timezone offset</li>\r
-        * </ul>\r
-        * @param {String} format format String for the date\r
-        * @see #getTimestamp\r
-        */\r
-       setDateFormat: function(format) {\r
-               this.dateformat = format;\r
-       },\r
-        \r
-       /**\r
-        * Generates a timestamp using the format set in {Log4js.setDateFormat}.\r
-        * @param {Date} date the date to format\r
-        * @see #setDateFormat\r
-        * @return A formatted timestamp with the current date and time.\r
-        */\r
-       getFormattedTimestamp: function(date) {\r
-         return this.dateformatter.formatDate(date, this.dateformat);\r
-       }\r
-};\r
-\r
-/**\r
- * Abstract base class for other appenders. \r
- * It is doing nothing.\r
- *\r
- * @constructor\r
- * @param {Log4js.Logger} logger log4js instance this appender is attached to\r
- * @author Stephan Strittmatter\r
- */\r
-Log4js.Appender = function () {\r
-       /**\r
-        * Reference to calling logger\r
-        * @type Log4js.Logger\r
-        * @private\r
-        */\r
-        this.logger = null;\r
-};\r
-\r
-Log4js.Appender.prototype = {\r
-       /** \r
-        * appends the given loggingEvent appender specific\r
-        * @param {Log4js.LoggingEvent} loggingEvent loggingEvent to append\r
-        */\r
-       doAppend: function(loggingEvent) {\r
-               return;\r
-       },\r
-       /** \r
-        * clears the Appender\r
-        */\r
-       doClear: function() {\r
-               return;\r
-       },\r
-       \r
-       /**\r
-        * Set the Layout for this appender.\r
-        * @param {Log4js.Layout} layout Layout for formatting loggingEvent\r
-        */\r
-       setLayout: function(layout){\r
-               this.layout = layout;\r
-       },\r
-       /**\r
-        * Set reference to the logger.\r
-        * @param {Log4js.Logger} the invoking logger\r
-        */\r
-       setLogger: function(logger){\r
-               // add listener to the logger methods\r
-               logger.onlog.addListener(Log4js.bind(this.doAppend, this));\r
-               logger.onclear.addListener(Log4js.bind(this.doClear, this));\r
-       \r
-               this.logger = logger;\r
-       }\r
-};\r
-\r
-/**\r
- * Interface for Layouts.\r
- * Use this Layout as "interface" for other Layouts. It is doing nothing.\r
- *\r
- * @constructor\r
- * @author Stephan Strittmatter\r
- */\r
-Log4js.Layout = function(){return;};\r
-Log4js.Layout.prototype = {\r
-       /** \r
-        * Implement this method to create your own layout format.\r
-        * @param {Log4js.LoggingEvent} loggingEvent loggingEvent to format\r
-        * @return formatted String\r
-        * @type String\r
-        */\r
-       format: function(loggingEvent) {\r
-               return "";\r
-       },\r
-       /** \r
-        * Returns the content type output by this layout. \r
-        * @return The base class returns "text/plain".\r
-        * @type String\r
-        */\r
-       getContentType: function() {\r
-               return "text/plain";\r
-       },\r
-       /** \r
-        * @return Returns the header for the layout format. The base class returns null.\r
-        * @type String\r
-        */\r
-       getHeader: function() {\r
-               return null;\r
-       },\r
-       /** \r
-        * @return Returns the footer for the layout format. The base class returns null.\r
-        * @type String\r
-        */\r
-       getFooter: function() {\r
-               return null;\r
-       },\r
-       \r
-       /**\r
-        * @return Separator between events\r
-        * @type String\r
-        */\r
-       getSeparator: function() {\r
-               return "";\r
-       }\r
-};\r
-\r
-/**\r
- * Console Appender writes the logs to a console.  If "inline" is\r
- * set to "false" the console launches in another window otherwise\r
- * the window is inline on the page and toggled on and off with "Alt-D".\r
- * Note: At FireFox &gb; 2.0 the keystroke is little different now: "SHIFT+ALT+D".\r
- *\r
- * @constructor\r
- * @extends Log4js.Appender\r
- * @param {boolean} isInline boolean value that indicates whether the console be placed inline, default is to launch in new window\r
- *\r
- * @author Corey Johnson - original console code in Lumberjack (http://gleepglop.com/javascripts/logger/)\r
- * @author Seth Chisamore - adapted for use as a log4js appender\r
- */\r
-Log4js.ConsoleAppender = function(isInline) {\r
-       \r
-       /**\r
-        * @type Log4js.Layout\r
-        * @private\r
-        */\r
-       this.layout = new Log4js.PatternLayout(Log4js.PatternLayout.TTCC_CONVERSION_PATTERN);\r
-       /**\r
-        * @type boolean\r
-        * @private\r
-        */\r
-       this.inline = isInline;\r
-\r
-       /**\r
-        * @type String\r
-        * @private\r
-        */\r
-       this.accesskey = "d";\r
-       \r
-       /**\r
-        * @private\r
-        */\r
-       this.tagPattern = null;\r
-       \r
-       this.commandHistory = [];\r
-       this.commandIndex = 0;\r
-       \r
-       /**\r
-        * true if popup is blocked.\r
-        */\r
-       this.popupBlocker = false;\r
-       \r
-       /**\r
-        * current output div-element.\r
-        */\r
-       this.outputElement = null;\r
-       \r
-       this.docReference = null;\r
-       this.winReference = null;               \r
-               \r
-       if(this.inline) {\r
-               Log4js.attachEvent(window, 'load', Log4js.bind(this.initialize, this));\r
-       }\r
-};\r
-\r
-Log4js.ConsoleAppender.prototype = Log4js.extend(new Log4js.Appender(), {  \r
-\r
-       /**\r
-        * Set the access key to show/hide the inline console (default &quote;d&quote;)\r
-        * @param key access key to show/hide the inline console\r
-        */     \r
-       setAccessKey : function(key) {\r
-               this.accesskey = key;\r
-       },\r
-       \r
-       /**\r
-        * @private\r
-        */\r
-       initialize : function() {\r
-               \r
-               if(!this.inline) {\r
-                       var doc = null; \r
-                       var win = null;\r
-                       window.top.consoleWindow = window.open("", this.logger.category, \r
-                               "left=0,top=0,width=700,height=700,scrollbars=no,status=no,resizable=yes;toolbar=no");\r
-                       window.top.consoleWindow.opener = self;\r
-                       win = window.top.consoleWindow;\r
-                                                               \r
-                       if (!win) { \r
-                               this.popupBlocker=true; \r
-                               alert("Popup window manager blocking the Log4js popup window to bedisplayed.\n\n" \r
-                                       + "Please disabled this to properly see logged events.");  \r
-                       } else {        \r
-\r
-                               doc = win.document;\r
-                               doc.open();\r
-                               doc.write("<!DOCTYPE html PUBLIC -//W3C//DTD XHTML 1.0 Transitional//EN ");\r
-                               doc.write("  http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd>\n\n");\r
-                               doc.write("<html><head><title>Log4js - " + this.logger.category + "</title>\n");\r
-                               doc.write("</head><body style=\"background-color:darkgray\"></body>\n");\r
-                               win.blur();\r
-                               win.focus();\r
-                       }\r
-                       \r
-                       this.docReference = doc;\r
-                       this.winReference = win;\r
-               } else {\r
-                       this.docReference = document;\r
-                       this.winReference = window;                     \r
-               }\r
-                               \r
-               this.outputCount = 0;\r
-               this.tagPattern = ".*";\r
-         \r
-               // I hate writing javascript in HTML... but what's a better alternative\r
-               this.logElement = this.docReference.createElement('div');\r
-               this.docReference.body.appendChild(this.logElement);\r
-               this.logElement.style.display = 'none';\r
-               \r
-               this.logElement.style.position = "absolute";\r
-               this.logElement.style.left = '0px';\r
-               this.logElement.style.width = '100%';\r
-       \r
-               this.logElement.style.textAlign = "left";\r
-               this.logElement.style.fontFamily = "lucida console";\r
-               this.logElement.style.fontSize = "100%";\r
-               this.logElement.style.backgroundColor = 'darkgray';      \r
-               this.logElement.style.opacity = 0.9;\r
-               this.logElement.style.zIndex = 2000; \r
-       \r
-               // Add toolbarElement\r
-               this.toolbarElement = this.docReference.createElement('div');\r
-               this.logElement.appendChild(this.toolbarElement);     \r
-               this.toolbarElement.style.padding = "0 0 0 2px";\r
-       \r
-               // Add buttons        \r
-               this.buttonsContainerElement = this.docReference.createElement('span');\r
-               this.toolbarElement.appendChild(this.buttonsContainerElement); \r
-       \r
-               if(this.inline) {\r
-                       var closeButton = this.docReference.createElement('button');\r
-                       closeButton.style.cssFloat = "right";\r
-                       closeButton.style.styleFloat = "right"; // IE dom bug...doesn't understand cssFloat\r
-                       closeButton.style.color = "black";\r
-                       closeButton.innerHTML = "close";\r
-                       closeButton.onclick = Log4js.bind(this.toggle, this);\r
-                       this.buttonsContainerElement.appendChild(closeButton);\r
-               }\r
-               \r
-               var clearButton = this.docReference.createElement('button');\r
-               clearButton.style.cssFloat = "right";\r
-               clearButton.style.styleFloat = "right"; // IE dom bug...doesn't understand cssFloat\r
-               clearButton.style.color = "black";\r
-               clearButton.innerHTML = "clear";\r
-               clearButton.onclick = Log4js.bind(this.logger.clear, this.logger);\r
-               this.buttonsContainerElement.appendChild(clearButton);\r
-       \r
-\r
-               //Add CategoryName and  Level Filter\r
-               this.tagFilterContainerElement = this.docReference.createElement('span');\r
-               this.toolbarElement.appendChild(this.tagFilterContainerElement);\r
-               this.tagFilterContainerElement.style.cssFloat = 'left';\r
-               \r
-               this.tagFilterContainerElement.appendChild(this.docReference.createTextNode("Log4js - " + this.logger.category));\r
-               this.tagFilterContainerElement.appendChild(this.docReference.createTextNode(" | Level Filter: "));\r
-               \r
-               this.tagFilterElement = this.docReference.createElement('input');\r
-               this.tagFilterContainerElement.appendChild(this.tagFilterElement);\r
-               this.tagFilterElement.style.width = '200px';                    \r
-               this.tagFilterElement.value = this.tagPattern;    \r
-               this.tagFilterElement.setAttribute('autocomplete', 'off'); // So Firefox doesn't flip out\r
-               \r
-               Log4js.attachEvent(this.tagFilterElement, 'keyup', Log4js.bind(this.updateTags, this));\r
-               Log4js.attachEvent(this.tagFilterElement, 'click', Log4js.bind( function() {this.tagFilterElement.select();}, this));\r
-               \r
-               // Add outputElement\r
-               this.outputElement = this.docReference.createElement('div');\r
-               this.logElement.appendChild(this.outputElement);  \r
-               this.outputElement.style.overflow = "auto";              \r
-               this.outputElement.style.clear = "both";\r
-               this.outputElement.style.height = (this.inline) ? ("200px"):("650px");\r
-               this.outputElement.style.width = "100%";\r
-               this.outputElement.style.backgroundColor = 'black'; \r
-                         \r
-               this.inputContainerElement = this.docReference.createElement('div');\r
-               this.inputContainerElement.style.width = "100%";\r
-               this.logElement.appendChild(this.inputContainerElement);      \r
-               \r
-               this.inputElement = this.docReference.createElement('input');\r
-               this.inputContainerElement.appendChild(this.inputElement);  \r
-               this.inputElement.style.width = '100%';\r
-               this.inputElement.style.borderWidth = '0px'; // Inputs with 100% width always seem to be too large (I HATE THEM) they only work if the border, margin and padding are 0\r
-               this.inputElement.style.margin = '0px';\r
-               this.inputElement.style.padding = '0px';\r
-               this.inputElement.value = 'Type command here'; \r
-               this.inputElement.setAttribute('autocomplete', 'off'); // So Firefox doesn't flip out\r
-       \r
-               Log4js.attachEvent(this.inputElement, 'keyup', Log4js.bind(this.handleInput, this));\r
-               Log4js.attachEvent(this.inputElement, 'click', Log4js.bind( function() {this.inputElement.select();}, this));\r
-               \r
-               if(this.inline){\r
-                       window.setInterval(Log4js.bind(this.repositionWindow, this), 500);\r
-                       this.repositionWindow();        \r
-                       // Allow acess key link          \r
-                       var accessElement = this.docReference.createElement('button');\r
-                       accessElement.style.position = "absolute";\r
-                       accessElement.style.top = "-100px";\r
-                       accessElement.accessKey = this.accesskey;\r
-                       accessElement.onclick = Log4js.bind(this.toggle, this);\r
-                       this.docReference.body.appendChild(accessElement);\r
-               } else {\r
-                       this.show();\r
-               }\r
-       },\r
-       /**\r
-        * shows/hide an element\r
-        * @private\r
-        * @return true if shown\r
-        */\r
-       toggle : function() {\r
-               if (this.logElement.style.display == 'none') {\r
-                       this.show();\r
-                       return true;\r
-               } else {\r
-                       this.hide();\r
-                       return false;\r
-               }\r
-       }, \r
-       /**\r
-        * @private\r
-        */\r
-       show : function() {\r
-               this.logElement.style.display = '';\r
-               this.outputElement.scrollTop = this.outputElement.scrollHeight; // Scroll to bottom when toggled\r
-               this.inputElement.select();\r
-       }, \r
-       /**\r
-        * @private\r
-        */     \r
-       hide : function() {\r
-               this.logElement.style.display = 'none';\r
-       },  \r
-       /**\r
-        * @private\r
-        * @param message\r
-        * @style\r
-        */     \r
-       output : function(message, style) {\r
-\r
-               // If we are at the bottom of the window, then keep scrolling with the output                   \r
-               var shouldScroll = (this.outputElement.scrollTop + (2 * this.outputElement.clientHeight)) >= this.outputElement.scrollHeight;\r
-               \r
-               this.outputCount++;\r
-               style = (style ? style += ';' : '');            \r
-               style += 'padding:1px;margin:0 0 5px 0';             \r
-                 \r
-               if (this.outputCount % 2 === 0) {\r
-                       style += ";background-color:#101010";\r
-               }\r
-               \r
-               message = message || "undefined";\r
-               message = message.toString();\r
-               \r
-               this.outputElement.innerHTML += "<pre style='" + style + "'>" + message + "</pre>";\r
-               \r
-               if (shouldScroll) {                             \r
-                       this.outputElement.scrollTop = this.outputElement.scrollHeight;\r
-               }\r
-       },\r
-       \r
-       /**\r
-        * @private\r
-        */\r
-       updateTags : function() {\r
-               \r
-               var pattern = this.tagFilterElement.value;\r
-       \r
-               if (this.tagPattern == pattern) {\r
-                       return;\r
-               }\r
-               \r
-               try {\r
-                       new RegExp(pattern);\r
-               } catch (e) {\r
-                       return;\r
-               }\r
-               \r
-               this.tagPattern = pattern;\r
-\r
-               this.outputElement.innerHTML = "";\r
-               \r
-               // Go through each log entry again\r
-               this.outputCount = 0;\r
-               for (var i = 0; i < this.logger.loggingEvents.length; i++) {\r
-                       this.doAppend(this.logger.loggingEvents[i]);\r
-               }  \r
-       },\r
-\r
-       /**\r
-        * @private\r
-        */     \r
-       repositionWindow : function() {\r
-               var offset = window.pageYOffset || this.docReference.documentElement.scrollTop || this.docReference.body.scrollTop;\r
-               var pageHeight = self.innerHeight || this.docReference.documentElement.clientHeight || this.docReference.body.clientHeight;\r
-               this.logElement.style.top = (offset + pageHeight - this.logElement.offsetHeight) + "px";\r
-       },\r
-\r
-       /**\r
-        * @param loggingEvent event to be logged\r
-        * @see Log4js.Appender#doAppend\r
-        */\r
-       doAppend : function(loggingEvent) {\r
-               \r
-               if(this.popupBlocker) {\r
-                       //popup blocked, we return in this case\r
-                       return;\r
-               }\r
-               \r
-               if ((!this.inline) && (!this.winReference || this.winReference.closed)) {\r
-                       this.initialize();\r
-               }\r
-               \r
-               if (this.tagPattern !== null && \r
-                       loggingEvent.level.toString().search(new RegExp(this.tagPattern, 'igm')) == -1) {\r
-                       return;\r
-               }\r
-               \r
-               var style = '';\r
-               \r
-               if (loggingEvent.level.toString().search(/ERROR/) != -1) { \r
-                       style += 'color:red';\r
-               } else if (loggingEvent.level.toString().search(/FATAL/) != -1) { \r
-                       style += 'color:red';\r
-               } else if (loggingEvent.level.toString().search(/WARN/) != -1) { \r
-                       style += 'color:orange';\r
-               } else if (loggingEvent.level.toString().search(/DEBUG/) != -1) {\r
-                       style += 'color:green';\r
-               } else if (loggingEvent.level.toString().search(/INFO/) != -1) {\r
-                       style += 'color:white';\r
-               } else {\r
-                       style += 'color:yellow';\r
-               }\r
-       \r
-               this.output(this.layout.format(loggingEvent), style);   \r
-       },\r
-\r
-       /**\r
-        * @see Log4js.Appender#doClear\r
-        */\r
-       doClear : function() {\r
-               this.outputElement.innerHTML = "";\r
-       },\r
-       /**\r
-        * @private\r
-        * @param e\r
-        */\r
-       handleInput : function(e) {\r
-               if (e.keyCode == 13 ) {      \r
-                       var command = this.inputElement.value;\r
-                       \r
-                       switch(command) {\r
-                               case "clear":\r
-                                       this.logger.clear();  \r
-                                       break;\r
-                                       \r
-                               default:        \r
-                                       var consoleOutput = "";\r
-                               \r
-                                       try {\r
-                                               consoleOutput = eval(this.inputElement.value);\r
-                                       } catch (e) {  \r
-                                               this.logger.error("Problem parsing input <" + command + ">" + e.message);\r
-                                               break;\r
-                                       }\r
-                                               \r
-                                       this.logger.trace(consoleOutput);\r
-                                       break;\r
-                       }        \r
-               \r
-                       if (this.inputElement.value !== "" && this.inputElement.value !== this.commandHistory[0]) {\r
-                               this.commandHistory.unshift(this.inputElement.value);\r
-                       }\r
-                 \r
-                       this.commandIndex = 0;\r
-                       this.inputElement.value = "";                                                     \r
-               } else if (e.keyCode == 38 && this.commandHistory.length > 0) {\r
-               this.inputElement.value = this.commandHistory[this.commandIndex];\r
-\r
-                       if (this.commandIndex < this.commandHistory.length - 1) {\r
-                       this.commandIndex += 1;\r
-               }\r
-       } else if (e.keyCode == 40 && this.commandHistory.length > 0) {\r
-               if (this.commandIndex > 0) {                                      \r
-                       this.commandIndex -= 1;\r
-               }                       \r
-\r
-                       this.inputElement.value = this.commandHistory[this.commandIndex];\r
-               } else {\r
-               this.commandIndex = 0;\r
-       }\r
-       },\r
-       \r
-       /** \r
-        * toString\r
-        */\r
-        toString: function() {\r
-               return "Log4js.ConsoleAppender[inline=" + this.inline + "]"; \r
-        }\r
-}); \r
-\r
-/**\r
- * Metatag Appender writing the logs to meta tags\r
- *\r
- * @extends Log4js.Appender\r
- * @constructor\r
- * @param logger log4js instance this appender is attached to\r
- * @author Stephan Strittmatter\r
- */\r
-Log4js.MetatagAppender = function() {\r
-       this.currentLine = 0;\r
-};\r
-Log4js.MetatagAppender.prototype = Log4js.extend(new Log4js.Appender(), {  \r
-       /**\r
-        * @param loggingEvent event to be logged\r
-        * @see Log4js.Appender#doAppend\r
-        */\r
-       doAppend: function(loggingEvent) {\r
-               var now = new Date();\r
-               var lines = loggingEvent.message.split("\n");\r
-               var headTag = document.getElementsByTagName("head")[0];\r
-\r
-               for (var i = 1; i <= lines.length; i++) {\r
-                       var value = lines[i - 1];\r
-                       if (i == 1) {\r
-                               value = loggingEvent.level.toString() + ": " + value;\r
-                       } else {\r
-                               value = "> " + value;\r
-                       }\r
-\r
-                       var metaTag = document.createElement("meta");\r
-                       metaTag.setAttribute("name", "X-log4js:" + this.currentLine);\r
-                       metaTag.setAttribute("content", value);\r
-                       headTag.appendChild(metaTag);\r
-                       this.currentLine += 1;\r
-               }\r
-       },\r
-\r
-       /** \r
-        * toString\r
-        */\r
-        toString: function() {\r
-               return "Log4js.MetatagAppender"; \r
-        }\r
-});\r
-\r
-/**\r
- * AJAX Appender sending {@link Log4js.LoggingEvent}s asynchron via \r
- * <code>XMLHttpRequest</code> to server.<br />\r
- * The {@link Log4js.LoggingEvent} is POSTed as response content and is \r
- * formatted by the accociated layout. Default layout is {@link Log4js.XMLLayout}. \r
- * The <code>threshold</code> defines when the logs \r
- * should be send to the server. By default every event is sent on its\r
- * own (threshold=1). If it is set to 10, then the events are send in groups of\r
- * 10 events.\r
- *\r
- * @extends Log4js.Appender \r
- * @constructor\r
- * @param {Log4js.Logger} logger log4js instance this appender is attached to\r
- * @param {String} loggingUrl url where appender will post log messages to\r
- * @author Stephan Strittmatter\r
- */\r
-Log4js.AjaxAppender = function(loggingUrl) {\r
-\r
-       /**\r
-        * is still esnding data to server\r
-        * @type boolean\r
-        * @private\r
-        */\r
-       this.isInProgress = false;\r
-       \r
-       /**\r
-        * @type String\r
-        * @private\r
-        */\r
-       this.loggingUrl = loggingUrl || "logging.log4js";\r
-       \r
-       /**\r
-        * @type Integer\r
-        * @private\r
-        */\r
-       this.threshold = 1;\r
-       \r
-       /**\r
-        * timeout when request is aborted.\r
-        * @private\r
-        */\r
-       this.timeout = 2000;\r
-       \r
-       /**\r
-        * List of LoggingEvents which should be send after threshold is reached.\r
-        * @type Map\r
-        * @private\r
-        */\r
-       this.loggingEventMap = new Log4js.FifoBuffer();\r
-\r
-       /**\r
-        * @type Log4js.Layout\r
-        * @private\r
-        */\r
-       this.layout = new Log4js.XMLLayout();\r
-       /**\r
-        * @type XMLHttpRequest\r
-        * @private\r
-        */     \r
-       this.httpRequest = null;\r
-};\r
-\r
-Log4js.AjaxAppender.prototype = Log4js.extend(new Log4js.Appender(), {  \r
-       /**\r
-        * sends the logs to the server\r
-        * @param loggingEvent event to be logged\r
-        * @see Log4js.Appender#doAppend\r
-        */\r
-       doAppend: function(loggingEvent) {\r
-               log4jsLogger.trace("> AjaxAppender.append");\r
-       \r
-               if (this.loggingEventMap.length() <= this.threshold || this.isInProgress === true) {\r
-                       this.loggingEventMap.push(loggingEvent);\r
-               }\r
-               \r
-               if(this.loggingEventMap.length() >= this.threshold && this.isInProgress === false) {\r
-                       //if threshold is reached send the events and reset current threshold\r
-                       this.send();\r
-               }\r
-               \r
-               log4jsLogger.trace("< AjaxAppender.append");\r
-       },\r
-       \r
-       /** @see Appender#doClear */\r
-       doClear: function() {\r
-               log4jsLogger.trace("> AjaxAppender.doClear" );\r
-               if(this.loggingEventMap.length() > 0) {\r
-                       this.send();\r
-               }\r
-               log4jsLogger.trace("< AjaxAppender.doClear" );\r
-       },\r
-       \r
-       /**\r
-        * Set the threshold when logs have to be send. Default threshold is 1.\r
-        * @praram {int} threshold new threshold\r
-        */\r
-       setThreshold: function(threshold) {\r
-               log4jsLogger.trace("> AjaxAppender.setThreshold: " + threshold );\r
-               this.threshold = threshold;\r
-               log4jsLogger.trace("< AjaxAppender.setThreshold" );\r
-       },\r
-       \r
-       /**\r
-        * Set the timeout in milli seconds until sending request is aborted.\r
-        * Default is 2000 ms.\r
-        * @param {int} milliseconds the new timeout\r
-        */\r
-       setTimeout: function(milliseconds) {\r
-               this.timeout = milliseconds;\r
-       },\r
-       \r
-       /**\r
-        * send the request.\r
-        */\r
-       send: function() {\r
-               if(this.loggingEventMap.length() >0) {\r
-                       \r
-                       log4jsLogger.trace("> AjaxAppender.send");\r
-                       \r
-                       \r
-                       this.isInProgress = true;\r
-                       var a = [];\r
-       \r
-                       for(var i = 0; i < this.loggingEventMap.length() && i < this.threshold; i++) {\r
-                               a.push(this.layout.format(this.loggingEventMap.pull()));\r
-                       } \r
-                                       \r
-                       var content = this.layout.getHeader();  \r
-                       content += a.join(this.layout.getSeparator());\r
-                       content += this.layout.getFooter();\r
-                       \r
-                       var appender = this;\r
-                       if(this.httpRequest === null){\r
-                               this.httpRequest = this.getXmlHttpRequest();\r
-                       }\r
-                       this.httpRequest.onreadystatechange = function() {\r
-                               appender.onReadyStateChanged.call(appender);\r
-                       };\r
-                       \r
-                       this.httpRequest.open("POST", this.loggingUrl, true);\r
-                       // set the request headers.\r
-                       //this.httpRequest.setRequestHeader("Content-type", "application/x-www-form-urlencoded");\r
-                       this.httpRequest.setRequestHeader("Content-type", this.layout.getContentType());\r
-                       //REFERER will be the top-level\r
-                       // URI which may differ from the location of the error if\r
-                       // it occurs in an included .js file\r
-                       this.httpRequest.setRequestHeader("REFERER", location.href);\r
-                       this.httpRequest.setRequestHeader("Content-length", content.length);\r
-                       this.httpRequest.setRequestHeader("Connection", "close");\r
-                       this.httpRequest.send( content );\r
-                       \r
-                       appender = this;\r
-                       \r
-                       try {\r
-                               window.setTimeout(function(){\r
-                                       log4jsLogger.trace("> AjaxAppender.timeout");\r
-                                       appender.httpRequest.onreadystatechange = function(){return;};\r
-                                       appender.httpRequest.abort();\r
-                                       //this.httpRequest = null;\r
-                                       appender.isInProgress = false;\r
-               \r
-                                       if(appender.loggingEventMap.length() > 0) {\r
-                                               appender.send();\r
-                                       }\r
-                                       log4jsLogger.trace("< AjaxAppender.timeout");\r
-                               }, this.timeout);\r
-                       } catch (e) {\r
-                               log4jsLogger.fatal(e);\r
-                       }\r
-                       log4jsLogger.trace("> AjaxAppender.send");\r
-               }\r
-       },\r
-       \r
-       /**\r
-        * @private\r
-        */\r
-       onReadyStateChanged: function() {\r
-               log4jsLogger.trace("> AjaxAppender.onReadyStateChanged");\r
-               var req = this.httpRequest;\r
-               if (this.httpRequest.readyState != 4) { \r
-                       log4jsLogger.trace("< AjaxAppender.onReadyStateChanged: readyState " + req.readyState + " != 4");\r
-                       return; \r
-               }\r
-               \r
-               var success = ((typeof req.status === "undefined") || req.status === 0 || (req.status >= 200 && req.status < 300));\r
-               \r
-               if (success) {\r
-                       log4jsLogger.trace("  AjaxAppender.onReadyStateChanged: success");\r
-\r
-                       //ready sending data\r
-                       this.isInProgress = false;\r
-\r
-               } else {\r
-                       var msg = "  AjaxAppender.onReadyStateChanged: XMLHttpRequest request to URL " + this.loggingUrl + " returned status code " + this.httpRequest.status;\r
-                       log4jsLogger.error(msg);\r
-               }\r
-               \r
-               log4jsLogger.trace("< AjaxAppender.onReadyStateChanged: readyState == 4");              \r
-       },\r
-       /**\r
-        * Get the XMLHttpRequest object independent of browser.\r
-        * @private\r
-        */\r
-       getXmlHttpRequest: function() {\r
-               log4jsLogger.trace("> AjaxAppender.getXmlHttpRequest");\r
-               \r
-               var httpRequest = false;\r
-\r
-               try {           \r
-                       if (window.XMLHttpRequest) { // Mozilla, Safari, IE7...\r
-                                       httpRequest = new XMLHttpRequest();\r
-                               if (httpRequest.overrideMimeType) {\r
-                                       httpRequest.overrideMimeType(this.layout.getContentType());\r
-                               }\r
-                       } else if (window.ActiveXObject) { // IE\r
-                               try {\r
-                                       httpRequest = new ActiveXObject("Msxml2.XMLHTTP");\r
-                               } catch (e) {\r
-                                       httpRequest = new ActiveXObject("Microsoft.XMLHTTP");\r
-                               }\r
-                       }\r
-               } catch (e) {\r
-                       httpRequest = false;\r
-               }\r
-               \r
-               if (!httpRequest) {\r
-                       log4jsLogger.fatal("Unfortunatelly your browser does not support AjaxAppender for log4js!");\r
-               }\r
-               \r
-               log4jsLogger.trace("< AjaxAppender.getXmlHttpRequest");\r
-               return httpRequest;\r
-       },\r
-       \r
-       /** \r
-        * toString\r
-        */\r
-        toString: function() {\r
-               return "Log4js.AjaxAppender[loggingUrl=" + this.loggingUrl + ", threshold=" + this.threshold + "]"; \r
-        }\r
-});\r
-\r
-/**\r
- * File Appender writing the logs to a text file.\r
- * PLEASE NOTE - Only works in IE and Mozilla \r
- * use ActiveX to write file on IE\r
- * use XPCom components  to write file on Mozilla\r
- * \r
- * @extends Log4js.Appender \r
- * @constructor\r
- * @param logger log4js instance this appender is attached to\r
- * @param file file log messages will be written to\r
- * @author Seth Chisamore\r
- * @author Nicolas Justin njustin@idealx.com\r
- * @author Gregory Kokanosky gkokanosky@idealx.com\r
- */\r
-Log4js.FileAppender = function(file) {\r
-\r
-       this.layout = new Log4js.SimpleLayout();\r
-       this.isIE = 'undefined';\r
-       \r
-       this.file = file || "log4js.log";       \r
-       \r
-       try{\r
-               this.fso = new ActiveXObject("Scripting.FileSystemObject");\r
-               this.isIE = true;\r
-       } catch(e){\r
-               try {\r
-                       netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");\r
-                       this.fso =  Components.classes["@mozilla.org/file/local;1"].createInstance(Components.interfaces.nsILocalFile);\r
-                       this.isIE = false; //mozilla & co\r
-               } catch (e) {\r
-                       log4jsLogger.error(e);\r
-               }\r
-       }\r
-};\r
-\r
-Log4js.FileAppender.prototype = Log4js.extend(new Log4js.Appender(), {  \r
-       /**\r
-        * @param loggingEvent event to be logged\r
-        * @see Log4js.Appender#doAppend\r
-        */\r
-       doAppend: function(loggingEvent) {\r
-               try {\r
-                       var fileHandle = null;\r
-                       \r
-                       if( this.isIE === 'undefined') {\r
-                               log4jsLogger.error("Unsupported ")\r
-                       }\r
-                       else if( this.isIE ){\r
-                               // try opening existing file, create if needed\r
-                               fileHandle = this.fso.OpenTextFile(this.file, 8, true);        \r
-                               // write out our data\r
-                               fileHandle.WriteLine(this.layout.format(loggingEvent));\r
-                               fileHandle.close();   \r
-                       } else {\r
-                               netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");\r
-                               this.fso.initWithPath(this.file);\r
-                       if(!this.fso.exists()) {\r
-                               //create file if needed\r
-                       this.fso.create(0x00, 0600);\r
-                       }\r
-                               \r
-                               fileHandle = Components.classes["@mozilla.org/network/file-output-stream;1"].createInstance(Components.interfaces.nsIFileOutputStream);\r
-                       fileHandle.init( this.fso, 0x04 | 0x08 | 0x10, 064, 0);\r
-                               var line = this.layout.format(loggingEvent);\r
-                       fileHandle.write(line, line.length); //write data\r
-                       fileHandle.close();\r
-                       }\r
-               } catch (e) {\r
-                       log4jsLogger.error(e);\r
-               }\r
-       },\r
-       /*\r
-        * @see Log4js.Appender#doClear\r
-        */\r
-       doClear: function() {\r
-               try {\r
-                       if( this.isIE ){\r
-                               var fileHandle = this.fso.GetFile(this.file);\r
-                               fileHandle.Delete();\r
-                       } else {\r
-                               netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");\r
-                               this.fso.initWithPath(this.file);\r
-                               if(this.fso.exists()) {\r
-                                       this.fso.remove(false);\r
-                               }\r
-                       }\r
-               } catch (e) {\r
-                       log4jsLogger.error(e);\r
-               }\r
-       },\r
-       \r
-       /** \r
-        * toString\r
-        */\r
-        toString: function() {\r
-               return "Log4js.FileAppender[file=" + this.file + "]"; \r
-        }\r
-});\r
-\r
-/**\r
- * Windows Event Appender writes the logs to the Windows Event log.\r
- * PLEASE NOTE - Only works in IE..uses ActiveX to write to Windows Event log\r
- *\r
- * @extends Log4js.Appender \r
- * @constructor\r
- * @param logger log4js instance this appender is attached to\r
- * @author Seth Chisamore\r
- */\r
-Log4js.WindowsEventAppender = function() {\r
-       \r
-       this.layout = new Log4js.SimpleLayout();\r
-       \r
-       try {\r
-               this.shell = new ActiveXObject("WScript.Shell");\r
-       } catch(e) {\r
-               log4jsLogger.error(e);\r
-       }\r
-};\r
-\r
-Log4js.WindowsEventAppender.prototype = Log4js.extend(new Log4js.Appender(), {  \r
-       /**\r
-        * @param loggingEvent event to be logged\r
-        * @see Log4js.Appender#doAppend\r
-        */\r
-       doAppend: function(loggingEvent) {\r
-               var winLevel = 4;\r
-               \r
-               // Map log level to windows event log level.\r
-               // Windows events: - SUCCESS: 0, ERROR: 1, WARNING: 2, INFORMATION: 4, AUDIT_SUCCESS: 8, AUDIT_FAILURE: 16\r
-               switch (loggingEvent.level) {   \r
-                       case Log4js.Level.FATAL:\r
-                               winLevel = 1;\r
-                               break;\r
-                       case Log4js.Level.ERROR:\r
-                               winLevel = 1;\r
-                               break;\r
-                       case Log4js.Level.WARN:\r
-                               winLevel = 2;\r
-                               break;\r
-                       default:\r
-                               winLevel = 4;\r
-                               break;\r
-               }\r
-               \r
-               try {\r
-                       this.shell.LogEvent(winLevel, this.level.format(loggingEvent));\r
-               } catch(e) {\r
-                       log4jsLogger.error(e);\r
-               }\r
-       },\r
-       \r
-       /** \r
-        * toString\r
-        */\r
-        toString: function() {\r
-               return "Log4js.WindowsEventAppender"; \r
-        } \r
-});\r
-\r
-/**\r
- * JS Alert Appender writes the logs to the JavaScript alert dialog box\r
- * @constructor\r
- * @extends Log4js.Appender  \r
- * @param logger log4js instance this appender is attached to\r
- * @author S&eacute;bastien LECACHEUR\r
- */\r
-Log4js.JSAlertAppender = function() {\r
-\r
-       this.layout = new Log4js.SimpleLayout();\r
-};\r
-\r
-Log4js.JSAlertAppender.prototype = Log4js.extend(new Log4js.Appender(), {  \r
-       /** \r
-        * @see Log4js.Appender#doAppend\r
-        */\r
-       doAppend: function(loggingEvent) {\r
-               alert(this.layout.getHeader() + this.layout.format(loggingEvent) + this.layout.getFooter());\r
-       },\r
-       \r
-       /** \r
-        * toString\r
-        */\r
-        toString: function() {\r
-               return "Log4js.JSAlertAppender"; \r
-        }      \r
-});\r
-\r
-/**\r
- * Appender writes the logs to the JavaScript console of Mozilla browser\r
- * More infos: http://kb.mozillazine.org/index.php?title=JavaScript_Console&redirect=no\r
- * PLEASE NOTE - Only works in Mozilla browser\r
- * @constructor\r
- * @extends Log4js.Appender  \r
- * @param logger log4js instance this appender is attached to\r
- * @author Stephan Strittmatter\r
- */\r
-Log4js.MozillaJSConsoleAppender = function() {\r
-       this.layout = new Log4js.SimpleLayout();\r
-       try {\r
-               netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");\r
-               this.jsConsole = Components.classes["@mozilla.org/consoleservice;1"].getService(Components.interfaces.nsIConsoleService);\r
-               this.scriptError = Components.classes["@mozilla.org/scripterror;1"].createInstance(Components.interfaces.nsIScriptError);\r
-       } catch (e) {\r
-               log4jsLogger.error(e);\r
-       }\r
-};\r
-\r
-Log4js.MozillaJSConsoleAppender.prototype = Log4js.extend(new Log4js.Appender(), {  \r
-       /** \r
-        * @see Log4js.Appender#doAppend\r
-        */\r
-       doAppend: function(loggingEvent) {\r
-               try {\r
-                       netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");\r
-                       this.scriptError.init(this.layout.format(loggingEvent), null, null, null, null, this.getFlag(loggingEvent), loggingEvent.categoryName);\r
-                       this.jsConsole.logMessage(this.scriptError);\r
-               } catch (e) {\r
-                       log4jsLogger.error(e);\r
-               }\r
-       },\r
-       \r
-       /** \r
-        * toString\r
-        */\r
-        toString: function() {\r
-               return "Log4js.MozillaJSConsoleAppender"; \r
-        },\r
-        \r
-       /**\r
-        * Map Log4js.Level to jsConsole Flags:\r
-        * <ul>\r
-        * <li>nsIScriptError.errorFlag (0) = Level.Error</li>\r
-        * <li>nsIScriptError.warningFlag (1)= Log4js.Level.WARN</li>\r
-        * <li>nsIScriptError.exceptionFlag (2) = Log4js.Level.FATAL</li>\r
-        * <li>nsIScriptError.strictFlag (4) = unused</li>\r
-        * </ul>\r
-        * @private\r
-        */     \r
-       getFlag: function(loggingEvent)\r
-       {\r
-               var retval;\r
-               switch (loggingEvent.level) {   \r
-                       case Log4js.Level.FATAL:\r
-                               retval = 2;//nsIScriptError.exceptionFlag = 2\r
-                               break;\r
-                       case Log4js.Level.ERROR:\r
-                               retval = 0;//nsIScriptError.errorFlag\r
-                               break;\r
-                       case Log4js.Level.WARN:\r
-                               retval = 1;//nsIScriptError.warningFlag = 1\r
-                               break;\r
-                       default:\r
-                               retval = 1;//nsIScriptError.warningFlag = 1\r
-                               break;\r
-               }\r
-               \r
-               return retval;          \r
-       }\r
-});\r
-\r
-/**\r
- * Appender writes the logs to the JavaScript console of Opera browser\r
- * PLEASE NOTE - Only works in Opera browser\r
- * @constructor\r
- * @extends Log4js.Appender  \r
- * @param logger log4js instance this appender is attached to\r
- * @author Stephan Strittmatter\r
- */\r
-Log4js.OperaJSConsoleAppender = function() {\r
-       this.layout = new Log4js.SimpleLayout();\r
-};\r
-\r
-Log4js.OperaJSConsoleAppender.prototype = Log4js.extend(new Log4js.Appender(), {  \r
-       /** \r
-        * @see Log4js.Appender#doAppend\r
-        */\r
-       doAppend: function(loggingEvent) {\r
-               opera.postError(this.layout.format(loggingEvent));\r
-       },\r
-       \r
-       /** \r
-        * toString\r
-        */\r
-        toString: function() {\r
-               return "Log4js.OperaJSConsoleAppender"; \r
-        }\r
-});\r
-\r
-/**\r
- * Appender writes the logs to the JavaScript console of Safari browser\r
- * PLEASE NOTE - Only works in Safari browser\r
- * @constructor\r
- * @extends Log4js.Appender  \r
- * @param logger log4js instance this appender is attached to\r
- * @author Stephan Strittmatter\r
- */\r
-Log4js.SafariJSConsoleAppender = function() {\r
-       this.layout = new Log4js.SimpleLayout();\r
-};\r
-\r
-Log4js.SafariJSConsoleAppender.prototype = Log4js.extend(new Log4js.Appender(), {  \r
-       /** \r
-        * @see Log4js.Appender#doAppend\r
-        */\r
-       doAppend: function(loggingEvent) {\r
-               window.console.log(this.layout.format(loggingEvent));\r
-       },\r
-       \r
-       /** \r
-        * toString\r
-        */\r
-        toString: function() {\r
-               return "Log4js.SafariJSConsoleAppender"; \r
-        }\r
-});\r
-\r
-/**\r
- * JavaScript Console Appender which is browser independent.\r
- * It checks internally for the current browser and adds delegate to\r
- * specific JavaScript Console Appender of the browser.\r
- * \r
- * @author Stephan Strittmatter\r
- * @since 1.0\r
- */\r
-Log4js.BrowserConsoleAppender = function() {\r
-       /**\r
-        * Delegate for browser specific implementation\r
-        * @type Log4js.Appender\r
-        * @private\r
-        */\r
-       this.consoleDelegate = null;\r
-       \r
-       if (window.console) {\r
-               this.consoleDelegate = new Log4js.SafariJSConsoleAppender(); \r
-       }\r
-    else if (window.opera) {\r
-               this.consoleDelegate = new Log4js.OperaJSConsoleAppender(); \r
-       }\r
-       else if(netscape) {\r
-               this.consoleDelegate = new Log4js.MozJSConsoleAppender(); \r
-       }\r
-    else {\r
-       //@todo\r
-       log4jsLogger.error("Unsupported Browser");\r
-    }\r
-};\r
-\r
-Log4js.BrowserConsoleAppender.prototype = Log4js.extend(new Log4js.Appender(), {  \r
-       /** \r
-        * @see Log4js.Appender#doAppend\r
-        */\r
-       doAppend: function(loggingEvent) {\r
-               this.consoleDelegate.doAppend(loggingEvent);\r
-       },\r
-       /** \r
-        * @see Log4js.Appender#doClear\r
-        */\r
-       doClear: function() {\r
-               this.consoleDelegate.doClear();\r
-       },\r
-       /**\r
-        * @see Log4js.Appender#setLayout\r
-        */\r
-       setLayout: function(layout){\r
-               this.consoleDelegate.setLayout(layout);\r
-       },\r
-       \r
-       /** \r
-        * toString\r
-        */\r
-        toString: function() {\r
-               return "Log4js.BrowserConsoleAppender: " + this.consoleDelegate.toString(); \r
-        }\r
-});\r
-\r
-/**\r
- * SimpleLayout consists of the level of the log statement, followed by " - " \r
- * and then the log message itself. For example,\r
- * <code>DEBUG - Hello world</code>\r
- *\r
- * @constructor\r
- * @extends Log4js.Layout\r
- * @extends Layout\r
- * @author Stephan Strittmatter\r
- */\r
-Log4js.SimpleLayout = function() {\r
-       this.LINE_SEP  = "\n";\r
-       this.LINE_SEP_LEN = 1;\r
-};\r
-\r
-Log4js.SimpleLayout.prototype = Log4js.extend(new Log4js.Layout(), {\r
-       /** \r
-        * Implement this method to create your own layout format.\r
-        * @param {Log4js.LoggingEvent} loggingEvent loggingEvent to format\r
-        * @return formatted String\r
-        * @type String\r
-        */\r
-       format: function(loggingEvent) {\r
-               return loggingEvent.level.toString() + " - " + loggingEvent.message + this.LINE_SEP;\r
-       },\r
-       /** \r
-        * Returns the content type output by this layout. \r
-        * @return The base class returns "text/plain".\r
-        * @type String\r
-        */\r
-       getContentType: function() {\r
-               return "text/plain";\r
-       },\r
-       /** \r
-        * @return Returns the header for the layout format. The base class returns null.\r
-        * @type String\r
-        */\r
-       getHeader: function() {\r
-               return "";\r
-       },\r
-       /** \r
-        * @return Returns the footer for the layout format. The base class returns null.\r
-        * @type String\r
-        */\r
-       getFooter: function() {\r
-               return "";\r
-       }\r
-});\r
-       \r
-/**\r
- * BasicLayout is a simple layout for storing the loggs. The loggs are stored\r
- * in following format:\r
- * <pre>\r
- * categoryName~startTime [logLevel] message\n\r
- * </pre>\r
- *\r
- * @constructor\r
- * @extends Log4js.Layout\r
- * @author Stephan Strittmatter\r
- */\r
-Log4js.BasicLayout = function() {\r
-       this.LINE_SEP  = "\n";\r
-};\r
-\r
-Log4js.BasicLayout.prototype = Log4js.extend(new Log4js.Layout(), {\r
-       /** \r
-        * Implement this method to create your own layout format.\r
-        * @param {Log4js.LoggingEvent} loggingEvent loggingEvent to format\r
-        * @return formatted String\r
-        * @type String\r
-        */\r
-       format: function(loggingEvent) {\r
-               return loggingEvent.categoryName + "~" + loggingEvent.startTime.toLocaleString() + " [" + loggingEvent.level.toString() + "] " + loggingEvent.message + this.LINE_SEP;\r
-       },\r
-       /** \r
-        * Returns the content type output by this layout. \r
-        * @return The base class returns "text/plain".\r
-        * @type String\r
-        */\r
-       getContentType: function() {\r
-               return "text/plain";\r
-       },\r
-       /** \r
-        * @return Returns the header for the layout format. The base class returns null.\r
-        * @type String\r
-        */\r
-       getHeader: function() {\r
-               return "";\r
-       },\r
-       /** \r
-        * @return Returns the footer for the layout format. The base class returns null.\r
-        * @type String\r
-        */\r
-       getFooter: function() {\r
-               return "";\r
-       }\r
-});\r
-\r
-/**\r
- * HtmlLayout write the logs in Html format.\r
- *\r
- * @constructor\r
- * @extends Log4js.Layout\r
- * @author Stephan Strittmatter\r
- */\r
-Log4js.HtmlLayout = function() {return;};\r
-\r
-Log4js.HtmlLayout.prototype = Log4js.extend(new Log4js.Layout(), {\r
-       /** \r
-        * Implement this method to create your own layout format.\r
-        * @param {Log4js.LoggingEvent} loggingEvent loggingEvent to format\r
-        * @return formatted String\r
-        * @type String\r
-        */\r
-       format: function(loggingEvent) {\r
-               return "<div style=\"" + this.getStyle(loggingEvent) + "\">" + loggingEvent.getFormattedTimestamp() + " - " + loggingEvent.level.toString() + " - " + loggingEvent.message + "</div>\n";\r
-       },\r
-       /** \r
-        * Returns the content type output by this layout. \r
-        * @return The base class returns "text/html".\r
-        * @type String\r
-        */\r
-       getContentType: function() {\r
-               return "text/html";\r
-       },\r
-       /** \r
-        * @return Returns the header for the layout format. The base class returns null.\r
-        * @type String\r
-        */\r
-       getHeader: function() {\r
-               return "<html><head><title>log4js</head><body>";\r
-       },\r
-       /** \r
-        * @return Returns the footer for the layout format. The base class returns null.\r
-        * @type String\r
-        */\r
-       getFooter: function() {\r
-               return "</body></html>";\r
-       },\r
-       \r
-       getStyle: function(loggingEvent)\r
-       {\r
-               var style;\r
-               if (loggingEvent.level.toString().search(/ERROR/) != -1) { \r
-                       style = 'color:red';\r
-               } else if (loggingEvent.level.toString().search(/FATAL/) != -1) { \r
-                       style = 'color:red';\r
-               } else if (loggingEvent.level.toString().search(/WARN/) != -1) { \r
-                       style = 'color:orange';\r
-               } else if (loggingEvent.level.toString().search(/DEBUG/) != -1) {\r
-                       style = 'color:green';\r
-               } else if (loggingEvent.level.toString().search(/INFO/) != -1) {\r
-                       style = 'color:white';\r
-               } else {\r
-                       style = 'color:yellow';\r
-               }       \r
-               return style;\r
-       }\r
-});\r
-\r
-/**\r
- * XMLLayout write the logs in XML format.\r
- * Layout is simmilar to log4j's XMLLayout:\r
- * <pre>\r
- * <log4js:event category="category" level="Level" client="Client" referer="ref" timestam="Date">\r
- * <log4js:message>Logged message</log4js:message>\r
- * </log4js:event>\r
- * </pre>\r
- * @constructor\r
- * @extends Layout\r
- * @author Stephan Strittmatter\r
- */\r
-Log4js.XMLLayout = function(){return;};\r
-Log4js.XMLLayout.prototype = Log4js.extend(new Log4js.Layout(), {\r
-       /** \r
-        * Implement this method to create your own layout format.\r
-        * @param {Log4js.LoggingEvent} loggingEvent loggingEvent to format\r
-        * @return formatted String\r
-        * @type String\r
-        */\r
-       format: function(loggingEvent) {\r
-               var useragent = "unknown";\r
-               try {\r
-                       useragent = navigator.userAgent;\r
-               } catch(e){\r
-                       useragent = "unknown";\r
-               }\r
-               \r
-               var referer = "unknown";\r
-               try {\r
-                       referer = location.href;\r
-               } catch(e){\r
-                       referer = "unknown";\r
-               }\r
-                               \r
-               var content = "<log4js:event logger=\"";\r
-               content += loggingEvent.categoryName + "\" level=\"";\r
-               content += loggingEvent.level.toString() + "\" useragent=\"";\r
-               content += useragent + "\" referer=\"";\r
-               content += referer.replace(/&/g, "&amp;") + "\" timestamp=\"";\r
-               content += loggingEvent.getFormattedTimestamp() + "\">\n";\r
-               content += "\t<log4js:message><![CDATA[" + this.escapeCdata(loggingEvent.message) + "]]></log4js:message>\n";   \r
-               \r
-               if (loggingEvent.exception) {\r
-                       content += this.formatException(loggingEvent.exception) ;\r
-               }\r
-               content += "</log4js:event>\n";\r
-        \r
-      return content;\r
-       },\r
-       /** \r
-        * Returns the content type output by this layout. \r
-        * @return The base class returns "text/xml".\r
-        * @type String\r
-        */\r
-       getContentType: function() {\r
-               return "text/xml";\r
-       },\r
-       /** \r
-        * @return Returns the header for the layout format. The base class returns null.\r
-        * @type String\r
-        */\r
-       getHeader: function() {\r
-               return "<log4js:eventSet version=\"" + Log4js.version + \r
-                       "\" xmlns:log4js=\"http://log4js.berlios.de/2007/log4js/\">\n";\r
-       },\r
-       /** \r
-        * @return Returns the footer for the layout format. The base class returns null.\r
-        * @type String\r
-        */\r
-       getFooter: function() {\r
-               return "</log4js:eventSet>\n";\r
-       },\r
-       \r
-       getSeparator: function() {\r
-               return "\n";\r
-       },\r
-       \r
-       /**\r
-        * better readable formatted Exceptions.\r
-        * @param ex {Exception} the exception to be formatted.\r
-        * @return {String} the formatted String representation of the exception.\r
-        * @private\r
-        */\r
-       formatException: function(ex) {\r
-               if (ex) {\r
-                       var exStr = "\t<log4js:throwable>"; \r
-                       if (ex.message) {\r
-                               exStr +=  "\t\t<log4js:message><![CDATA[" + this.escapeCdata(ex.message) + "]]></log4js:message>\n";    \r
-                       } \r
-                       if (ex.description) {\r
-                               exStr +=  "\t\t<log4js:description><![CDATA[" + this.escapeCdata(ex.description) + "]]></log4js:description>\n";        \r
-                       }\r
-                       \r
-                       exStr +=  "\t\t<log4js:stacktrace>";\r
-                       exStr +=  "\t\t\t<log4js:location fileName=\""+ex.fileName+"\" lineNumber=\""+ex.lineNumber+"\" />";\r
-                       exStr +=  "\t\t</log4js:stacktrace>";\r
-                       exStr = "\t</log4js:throwable>";\r
-                       return exStr;\r
-               }\r
-               return null;\r
-       },\r
-       /**\r
-        * Escape Cdata messages\r
-        * @param str {String} message to escape\r
-        * @return {String} the escaped message\r
-        * @private\r
-        */\r
-       escapeCdata: function(str) {\r
-               return str.replace(/\]\]>/, "]]>]]&gt;<![CDATA[");\r
-       }\r
-});\r
-\r
-/**\r
- * JSONLayout write the logs in JSON format.\r
- * JSON library is required to use this Layout. See also {@link http://www.json.org}\r
- * @constructor\r
- * @extends Log4js.Layout\r
- * @author Stephan Strittmatter\r
- */\r
-Log4js.JSONLayout = function() {\r
-       this.df = new Log4js.DateFormatter();\r
-};\r
-Log4js.JSONLayout.prototype = Log4js.extend(new Log4js.Layout(), {\r
-       /** \r
-        * Implement this method to create your own layout format.\r
-        * @param {Log4js.LoggingEvent} loggingEvent loggingEvent to format\r
-        * @return formatted String\r
-        * @type String\r
-        */\r
-       format: function(loggingEvent) {\r
-               \r
-                               var useragent = "unknown";\r
-               try {\r
-                       useragent = navigator.userAgent;\r
-               } catch(e){\r
-                       useragent = "unknown";\r
-               }\r
-               \r
-               var referer = "unknown";\r
-               try {\r
-                       referer = location.href;\r
-               } catch(e){\r
-                       referer = "unknown";\r
-               }\r
-               \r
-               var jsonString = "{\n \"LoggingEvent\": {\n";\r
-               \r
-               jsonString += "\t\"logger\": \"" +  loggingEvent.categoryName + "\",\n";\r
-               jsonString += "\t\"level\": \"" +  loggingEvent.level.toString() + "\",\n";\r
-               jsonString += "\t\"message\": \"" +  loggingEvent.message + "\",\n"; \r
-               jsonString += "\t\"referer\": \"" + referer + "\",\n"; \r
-               jsonString += "\t\"useragent\": \"" + useragent + "\",\n"; \r
-               jsonString += "\t\"timestamp\": \"" +  this.df.formatDate(loggingEvent.startTime, "yyyy-MM-ddThh:mm:ssZ") + "\",\n";\r
-               jsonString += "\t\"exception\": \"" +  loggingEvent.exception + "\"\n"; \r
-               jsonString += "}}";      \r
-        \r
-        return jsonString;\r
-       },\r
-       /** \r
-        * Returns the content type output by this layout. \r
-        * @return The base class returns "text/xml".\r
-        * @type String\r
-        */\r
-       getContentType: function() {\r
-               return "text/json";\r
-       },\r
-       /** \r
-        * @return Returns the header for the layout format. The base class returns null.\r
-        * @type String\r
-        */\r
-       getHeader: function() {\r
-               return "{\"Log4js\": [\n";\r
-       },\r
-       /** \r
-        * @return Returns the footer for the layout format. The base class returns null.\r
-        * @type String\r
-        */\r
-       getFooter: function() {\r
-               return "\n]}";\r
-       },\r
-       \r
-       getSeparator: function() {\r
-               return ",\n";\r
-       }\r
-});\r
-\r
-/** \r
- * PatternLayout \r
- */\r
-Log4js.PatternLayout = function(pattern) {\r
-       if (pattern) {\r
-               this.pattern = pattern;\r
-       } else {\r
-               this.pattern = Log4js.PatternLayout.DEFAULT_CONVERSION_PATTERN;\r
-       }\r
-};\r
-\r
-Log4js.PatternLayout.TTCC_CONVERSION_PATTERN = "%r %p %c - %m%n";\r
-Log4js.PatternLayout.DEFAULT_CONVERSION_PATTERN = "%m%n";\r
-Log4js.PatternLayout.ISO8601_DATEFORMAT = "yyyy-MM-dd HH:mm:ss,SSS";\r
-Log4js.PatternLayout.DATETIME_DATEFORMAT = "dd MMM YYYY HH:mm:ss,SSS";\r
-Log4js.PatternLayout.ABSOLUTETIME_DATEFORMAT = "HH:mm:ss,SSS";\r
-\r
-Log4js.PatternLayout.prototype = Log4js.extend(new Log4js.Layout(), {\r
-       /** \r
-        * Returns the content type output by this layout. \r
-        * @return "text/plain".\r
-        * @type String\r
-        */\r
-       getContentType: function() {\r
-               return "text/plain";\r
-       },\r
-       /** \r
-        * @return Returns the header for the layout format.\r
-        * @type String\r
-        */\r
-       getHeader: function() {\r
-               return null;\r
-       },\r
-       /** \r
-        * @return Returns the footer for the layout format.\r
-        * @type String\r
-        */\r
-       getFooter: function() {\r
-               return null;\r
-       },\r
-       \r
-       format: function(loggingEvent) {\r
-               var regex = /%(-?[0-9]+)?(\.?[0-9]+)?([cdmnpr%])(\{([^\}]+)\})?|([^%]+)/;\r
-               var formattedString = "";\r
-               var result;\r
-               var searchString = this.pattern;\r
-\r
-               // Cannot use regex global flag since it doesn't work in IE5\r
-               while ((result = regex.exec(searchString))) {\r
-                       var matchedString = result[0];\r
-                       var padding = result[1];\r
-                       var truncation = result[2];\r
-                       var conversionCharacter = result[3];\r
-                       var specifier = result[5];\r
-                       var text = result[6];\r
-\r
-                       // Check if the pattern matched was just normal text\r
-                       if (text) {\r
-                               formattedString += "" + text;\r
-                       } else {\r
-                               // Create a raw replacement string based on the conversion\r
-                               // character and specifier\r
-                               var replacement = "";\r
-                               switch(conversionCharacter) {\r
-                                       case "c":\r
-                                               var loggerName = loggingEvent.categoryName;\r
-                                               if (specifier) {\r
-                                                       var precision = parseInt(specifier, 10);\r
-                                                       var loggerNameBits = loggingEvent.categoryName.split(".");\r
-                                                       if (precision >= loggerNameBits.length) {\r
-                                                               replacement = loggerName;\r
-                                                       } else {\r
-                                                               replacement = loggerNameBits.slice(loggerNameBits.length - precision).join(".");\r
-                                                       }\r
-                                               } else {\r
-                                                       replacement = loggerName;\r
-                                               }\r
-                                               break;\r
-                                       case "d":\r
-                                               var dateFormat = Log4js.PatternLayout.ISO8601_DATEFORMAT;\r
-                                               if (specifier) {\r
-                                                       dateFormat = specifier;\r
-                                                       // Pick up special cases\r
-                                                       if (dateFormat == "ISO8601") {\r
-                                                               dateFormat = Log4js.PatternLayout.ISO8601_DATEFORMAT;\r
-                                                       } else if (dateFormat == "ABSOLUTE") {\r
-                                                               dateFormat = Log4js.PatternLayout.ABSOLUTETIME_DATEFORMAT;\r
-                                                       } else if (dateFormat == "DATE") {\r
-                                                               dateFormat = Log4js.PatternLayout.DATETIME_DATEFORMAT;\r
-                                                       }\r
-                                               }\r
-                                               // Format the date\r
-                                               replacement = (new Log4js.SimpleDateFormat(dateFormat)).format(loggingEvent.startTime);\r
-                                               break;\r
-                                       case "m":\r
-                                               replacement = loggingEvent.message;\r
-                                               break;\r
-                                       case "n":\r
-                                               replacement = "\n";\r
-                                               break;\r
-                                       case "p":\r
-                                               replacement = loggingEvent.level.toString();\r
-                                               break;\r
-                                       case "r":\r
-                                               replacement = "" + loggingEvent.startTime.toLocaleTimeString(); //TODO: .getDifference(Log4js.applicationStartDate);\r
-                                               break;\r
-                                       case "%":\r
-                                               replacement = "%";\r
-                                               break;\r
-                                       default:\r
-                                               replacement = matchedString;\r
-                                               break;\r
-                               }\r
-                               // Format the replacement according to any padding or\r
-                               // truncation specified\r
-\r
-                               var len;\r
-\r
-                               // First, truncation\r
-                               if (truncation) {\r
-                                       len = parseInt(truncation.substr(1), 10);\r
-                                       replacement = replacement.substring(0, len);\r
-                               }\r
-                               // Next, padding\r
-                               if (padding) {\r
-                                       if (padding.charAt(0) == "-") {\r
-                                               len = parseInt(padding.substr(1), 10);\r
-                                               // Right pad with spaces\r
-                                               while (replacement.length < len) {\r
-                                                       replacement += " ";\r
-                                               }\r
-                                       } else {\r
-                                               len = parseInt(padding, 10);\r
-                                               // Left pad with spaces\r
-                                               while (replacement.length < len) {\r
-                                                       replacement = " " + replacement;\r
-                                               }\r
-                                       }\r
-                               }\r
-                               formattedString += replacement;\r
-                       }\r
-                       searchString = searchString.substr(result.index + result[0].length);\r
-               }\r
-               return formattedString;\r
-       }\r
-});\r
-\r
-/**\r
- * @private\r
- * @ignore\r
- */\r
-if (!Array.prototype.push) {\r
-       /**\r
-        * Functions taken from Prototype library, didn't want to require for just few \r
-        * functions.\r
-        * More info at {@link http://\r
-        * prototype.conio.net/}\r
-        * @private\r
-        */\r
-       Array.prototype.push = function() {\r
-               var startLength = this.length;\r
-               for (var i = 0; i < arguments.length; i++) {\r
-                       this[startLength + i] = arguments[i];\r
-               }\r
-               return this.length;\r
-       };\r
-}\r
-\r
-/**\r
- * FIFO buffer\r
- * @private\r
- */\r
-Log4js.FifoBuffer = function()\r
-{\r
-  this.array = new Array();\r
-};\r
-\r
-Log4js.FifoBuffer.prototype = {\r
-\r
-       /**\r
-        * @param {Object} obj any object added to buffer\r
-        */\r
-       push : function(obj) {\r
-        this.array[this.array.length] = obj;\r
-        return this.array.length;\r
-       },\r
-       \r
-       /**\r
-        * @return first putted in Object\r
-        */\r
-       pull : function() {\r
-               if (this.array.length > 0) {\r
-                       var firstItem = this.array[0];\r
-                       for (var i = 0; i < this.array.length - 1; i++) {\r
-                               this.array[i] = this.array[i + 1];\r
-                       }\r
-                       this.array.length = this.array.length - 1;\r
-                       return firstItem;\r
-               }\r
-               return null;\r
-       },\r
-       \r
-       length : function() {\r
-               return this.array.length;\r
-       }\r
-};\r
-\r
-\r
-\r
-/**\r
- * Date Formatter\r
- * addZero() and formatDate() are courtesy of Mike Golding:\r
- * http://www.mikezilla.com/exp0015.html\r
- * @private\r
- */ \r
-Log4js.DateFormatter = function() {\r
-       return;\r
-};\r
-/**\r
- * default format of date (ISO-8601)\r
- * @static\r
- * @final\r
- */\r
-Log4js.DateFormatter.DEFAULT_DATE_FORMAT = "yyyy-MM-ddThh:mm:ssO";\r
-\r
-\r
-Log4js.DateFormatter.prototype = {\r
-       /**\r
-        * Formats the given date by the given pattern.<br />\r
-        * Following switches are supported:\r
-        * <ul>\r
-        * <li>yyyy: The year</li>\r
-        * <li>MM: the month</li>\r
-        * <li>dd: the day of month<li>\r
-        * <li>hh: the hour<li>\r
-        * <li>mm: minutes</li>\r
-        * <li>O: timezone offset</li>\r
-        * </ul>\r
-        * @param {Date} vDate the date to format\r
-        * @param {String} vFormat the format pattern\r
-        * @return {String} formatted date string\r
-        * @static\r
-        */\r
-       formatDate : function(vDate, vFormat) {\r
-         var vDay = this.addZero(vDate.getDate());\r
-         var vMonth = this.addZero(vDate.getMonth()+1);\r
-         var vYearLong = this.addZero(vDate.getFullYear());\r
-         var vYearShort = this.addZero(vDate.getFullYear().toString().substring(3,4));\r
-         var vYear = (vFormat.indexOf("yyyy")>-1?vYearLong:vYearShort);\r
-         var vHour  = this.addZero(vDate.getHours());\r
-         var vMinute = this.addZero(vDate.getMinutes());\r
-         var vSecond = this.addZero(vDate.getSeconds());\r
-         var vTimeZone = this.O(vDate);\r
-         var vDateString = vFormat.replace(/dd/g, vDay).replace(/MM/g, vMonth).replace(/y{1,4}/g, vYear);\r
-         vDateString = vDateString.replace(/hh/g, vHour).replace(/mm/g, vMinute).replace(/ss/g, vSecond);\r
-         vDateString = vDateString.replace(/O/g, vTimeZone);\r
-         return vDateString;\r
-       },\r
-               \r
-       /**\r
-        * @private\r
-        * @static\r
-        */\r
-       addZero : function(vNumber) {\r
-         return ((vNumber < 10) ? "0" : "") + vNumber;\r
-       },\r
-       \r
-       /**\r
-        * Formates the TimeOffest\r
-        * Thanks to http://www.svendtofte.com/code/date_format/\r
-        * @private\r
-        */\r
-       O : function (date) {\r
-               // Difference to Greenwich time (GMT) in hours\r
-               var os = Math.abs(date.getTimezoneOffset());\r
-               var h = String(Math.floor(os/60));\r
-               var m = String(os%60);\r
-               h.length == 1? h = "0"+h:1;\r
-               m.length == 1? m = "0"+m:1;\r
-               return date.getTimezoneOffset() < 0 ? "+"+h+m : "-"+h+m;\r
-       }\r
-};\r
-\r
-\r
-/**\r
- * internal Logger to be used\r
- * @private\r
- */\r
-var log4jsLogger = Log4js.getLogger("Log4js");\r
-log4jsLogger.addAppender(new Log4js.ConsoleAppender());\r
-log4jsLogger.setLevel(Log4js.Level.ALL);
\ No newline at end of file
+/*
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*jsl:option explicit*/
+
+/**
+ * @fileoverview log4js is a library to log in JavaScript in similar manner
+ * than in log4j for Java. The API should be nearly the same.
+ *
+ * This file contains all log4js code and is the only file required for logging.
+ *
+ * <h3>Example:</h3>
+ * <pre>
+ *  var log = new Log4js.getLogger("some-category-name"); //create logger instance
+ *  log.setLevel(Log4js.Level.TRACE); //set the Level
+ *  log.addAppender(new ConsoleAppender(log, false)); // console that launches in new window
+
+ *  // if multiple appenders are set all will log
+ *  log.addAppender(new ConsoleAppender(log, true)); // console that is in-line in the page
+ *  log.addAppender(new FileAppender("C:\\somefile.log")); // file appender logs to C:\\somefile.log
+ *
+ *  ...
+ *
+ *  //call the log
+ *  log.trace("trace me" );
+ * </pre>
+ *
+ * @version 0.3
+ * @author Stephan Strittmatter - http://jroller.com/page/stritti
+ * @author Seth Chisamore - http://www.chisamore.com
+ * @since 2005-05-20
+ * Website: http://log4js.berlios.de
+ */
+var Log4js = {
+       /**
+        * Current version of log4js.
+        * @static
+        * @final
+        */
+       version: "1.0",
+       /**
+        * Date of logger initialized.
+        * @static
+        * @final
+        */
+       applicationStartDate: new Date(),
+       /**
+        * Hashtable of loggers.
+        * @static
+        * @final
+        * @private
+        */
+       loggers: {},
+       /**
+        * Get a logger instance. Instance is cached on categoryName level.
+        * @param  {String} categoryName name of category to log to.
+        * @return {Logger} instance of logger for the category
+        * @static
+        */
+       getLogger: function (categoryName) {
+
+               // Use default logger if categoryName is not specified or invalid
+               if (!(typeof categoryName == "string")) {
+                       categoryName = "[default]";
+               }
+
+               if (!Log4js.loggers[categoryName]) {
+                       // Create the logger for this name if it doesn't already exist
+                       Log4js.loggers[categoryName] = new Log4js.Logger(categoryName);
+               }
+
+               return Log4js.loggers[categoryName];
+       },
+       /**
+        * Get the default logger instance.
+        * @return {Logger} instance of default logger
+        * @static
+        */
+       getDefaultLogger: function () {
+               return Log4js.getLogger("[default]");
+       },
+       /**
+        * Atatch an observer function to an elements event browser independent.
+        *
+        * @param element element to attach event
+        * @param name name of event
+        * @param observer observer method to be called
+        * @private
+        */
+       attachEvent: function (element, name, observer) {
+               if (element.addEventListener) { //DOM event model
+                       element.addEventListener(name, observer, false);
+               } else if (element.attachEvent) { //M$ event model
+                       element.attachEvent('on' + name, observer);
+               }
+       }
+
+       /**
+        * Load a JS-script dynamically.
+        * @param {String} src
+        */
+       /*      $import: function (src) {
+        var documentScripts = document.getElementsByTagName("script");
+
+        for (index = 0; index < documentScripts.length; ++index)
+        {
+        var documentScript = documentScripts[index];
+        if (documentScript.src == src) {
+        return false;
+        }
+        }
+
+        var script = document.createElement('script');
+        script.type = 'text/javascript';
+        script.src = src;
+        document.getElementsByTagName('head')[0].appendChild(script);
+
+        return true;
+        }
+        */
+};
+
+/**
+ * Internal object extension (OO) methods.
+ *
+ * @private
+ * @ignore
+ */
+Log4js.extend = function (destination, source) {
+       for (property in source) {
+               destination[property] = source[property];
+       }
+       return destination;
+}
+
+/**
+ * Functions taken from Prototype library,
+ * didn't want to require for just few functions.
+ * More info at {@link http://prototype.conio.net/}
+ * @private
+ */
+Log4js.bind = function (fn, object) {
+       return function () {
+               return fn.apply(object, arguments);
+       };
+};
+
+/**
+ * Log4js.Level Enumeration. Do not use directly. Use static objects instead.
+ * @constructor
+ * @param {Number} level number of level
+ * @param {String} levelString String representation of level
+ * @private
+ */
+Log4js.Level = function (level, levelStr) {
+       this.level = level;
+       this.levelStr = levelStr;
+};
+
+Log4js.Level.prototype = {
+       /**
+        * converts given String to corresponding Level
+        * @param {String} sArg String value of Level
+        * @param {Log4js.Level} defaultLevel default Level, if no String representation
+        * @return Level object
+        * @type Log4js.Level
+        */
+       toLevel: function (sArg, defaultLevel) {
+
+               if (sArg === null) {
+                       return defaultLevel;
+               }
+
+               if (typeof sArg == "string") {
+                       var s = sArg.toUpperCase();
+                       if (s == "ALL") {
+                               return Log4js.Level.ALL;
+                       }
+                       if (s == "DEBUG") {
+                               return Log4js.Level.DEBUG;
+                       }
+                       if (s == "INFO") {
+                               return Log4js.Level.INFO;
+                       }
+                       if (s == "WARN") {
+                               return Log4js.Level.WARN;
+                       }
+                       if (s == "ERROR") {
+                               return Log4js.Level.ERROR;
+                       }
+                       if (s == "FATAL") {
+                               return Log4js.Level.FATAL;
+                       }
+                       if (s == "OFF") {
+                               return Log4js.Level.OFF;
+                       }
+                       if (s == "TRACE") {
+                               return Log4js.Level.TRACE;
+                       }
+                       return defaultLevel;
+               } else if (typeof sArg == "number") {
+                       switch (sArg) {
+                               case ALL_INT:
+                                       return Log4js.Level.ALL;
+                               case DEBUG_INT:
+                                       return Log4js.Level.DEBUG;
+                               case INFO_INT:
+                                       return Log4js.Level.INFO;
+                               case WARN_INT:
+                                       return Log4js.Level.WARN;
+                               case ERROR_INT:
+                                       return Log4js.Level.ERROR;
+                               case FATAL_INT:
+                                       return Log4js.Level.FATAL;
+                               case OFF_INT:
+                                       return Log4js.Level.OFF;
+                               case TRACE_INT:
+                                       return Log4js.Level.TRACE;
+                               default:
+                                       return defaultLevel;
+                       }
+               } else {
+                       return defaultLevel;
+               }
+       },
+       /**
+        * @return  converted Level to String
+        * @type String
+        */
+       toString: function () {
+               return this.levelStr;
+       },
+       /**
+        * @return internal Number value of Level
+        * @type Number
+        */
+       valueOf: function () {
+               return this.level;
+       }
+};
+
+// Static variables
+/**
+ * @private
+ */
+Log4js.Level.OFF_INT = Number.MAX_VALUE;
+/**
+ * @private
+ */
+Log4js.Level.FATAL_INT = 50000;
+/**
+ * @private
+ */
+Log4js.Level.ERROR_INT = 40000;
+/**
+ * @private
+ */
+Log4js.Level.WARN_INT = 30000;
+/**
+ * @private
+ */
+Log4js.Level.INFO_INT = 20000;
+/**
+ * @private
+ */
+Log4js.Level.DEBUG_INT = 10000;
+/**
+ * @private
+ */
+Log4js.Level.TRACE_INT = 5000;
+/**
+ * @private
+ */
+Log4js.Level.ALL_INT = Number.MIN_VALUE;
+
+/**
+ * Logging Level OFF - all disabled
+ * @type Log4js.Level
+ * @static
+ */
+Log4js.Level.OFF = new Log4js.Level(Log4js.Level.OFF_INT, "OFF");
+/**
+ * Logging Level Fatal
+ * @type Log4js.Level
+ * @static
+ */
+Log4js.Level.FATAL = new Log4js.Level(Log4js.Level.FATAL_INT, "FATAL");
+/**
+ * Logging Level Error
+ * @type Log4js.Level
+ * @static
+ */
+Log4js.Level.ERROR = new Log4js.Level(Log4js.Level.ERROR_INT, "ERROR");
+/**
+ * Logging Level Warn
+ * @type Log4js.Level
+ * @static
+ */
+Log4js.Level.WARN = new Log4js.Level(Log4js.Level.WARN_INT, "WARN");
+/**
+ * Logging Level Info
+ * @type Log4js.Level
+ * @static
+ */
+Log4js.Level.INFO = new Log4js.Level(Log4js.Level.INFO_INT, "INFO");
+/**
+ * Logging Level Debug
+ * @type Log4js.Level
+ * @static
+ */
+Log4js.Level.DEBUG = new Log4js.Level(Log4js.Level.DEBUG_INT, "DEBUG");
+/**
+ * Logging Level Trace
+ * @type Log4js.Level
+ * @static
+ */
+Log4js.Level.TRACE = new Log4js.Level(Log4js.Level.TRACE_INT, "TRACE");
+/**
+ * Logging Level All - All traces are enabled
+ * @type Log4js.Level
+ * @static
+ */
+Log4js.Level.ALL = new Log4js.Level(Log4js.Level.ALL_INT, "ALL");
+
+/**
+ * Log4js CustomEvent
+ * @constructor
+ * @author Corey Johnson - original code in Lumberjack (http://gleepglop.com/javascripts/logger/)
+ * @author Seth Chisamore - adapted for Log4js
+ * @private
+ */
+Log4js.CustomEvent = function () {
+       this.listeners = [];
+};
+
+Log4js.CustomEvent.prototype = {
+       /**
+        * @param method method to be added
+        */
+       addListener: function (method) {
+               this.listeners.push(method);
+       },
+       /**
+        * @param method method to be removed
+        */
+       removeListener: function (method) {
+               var foundIndexes = this.findListenerIndexes(method);
+
+               for (var i = 0; i < foundIndexes.length; i++) {
+                       this.listeners.splice(foundIndexes[i], 1);
+               }
+       },
+       /**
+        * @param handler
+        */
+       dispatch: function (handler) {
+               for (var i = 0; i < this.listeners.length; i++) {
+                       try {
+                               this.listeners[i](handler);
+                       }
+                       catch (e) {
+                               log4jsLogger.warn("Could not run the listener " + this.listeners[i] + ". \n" + e);
+                       }
+               }
+       },
+       /**
+        * @private
+        * @param method
+        */
+       findListenerIndexes: function (method) {
+               var indexes = [];
+               for (var i = 0; i < this.listeners.length; i++) {
+                       if (this.listeners[i] == method) {
+                               indexes.push(i);
+                       }
+               }
+
+               return indexes;
+       }
+};
+
+/**
+ * Models a logging event.
+ * @constructor
+ * @param {String} categoryName name of category
+ * @param {Log4js.Level} level level of message
+ * @param {String} message message to log
+ * @param {Log4js.Logger} logger the associated logger
+ * @author Seth Chisamore
+ */
+Log4js.LoggingEvent = function (categoryName, level, message, exception, logger) {
+       /**
+        * the timestamp of the Logging Event
+        * @type Date
+        * @private
+        */
+       this.startTime = new Date();
+       /**
+        * category of event
+        * @type String
+        * @private
+        */
+       this.categoryName = categoryName;
+       /**
+        * the logging message
+        * @type String
+        * @private
+        */
+       this.message = message;
+       /**
+        * the logging exception
+        * @type Exception
+        * @private
+        */
+       this.exception = exception;
+       /**
+        * level of log
+        * @type Log4js.Level
+        * @private
+        */
+       this.level = level;
+       /**
+        * reference to logger
+        * @type Log4js.Logger
+        * @private
+        */
+       this.logger = logger;
+};
+
+Log4js.LoggingEvent.prototype = {
+       /**
+        * get the timestamp formatted as String.
+        * @return {String} formatted timestamp
+        * @see Log4js#setDateFormat()
+        */
+       getFormattedTimestamp: function () {
+               if (this.logger) {
+                       return this.logger.getFormattedTimestamp(this.startTime);
+               } else {
+                       return this.startTime.toGMTString();
+               }
+       }
+};
+
+/**
+ * Logger to log messages to the defined appender.</p>
+ * Default appender is Appender, which is ignoring all messages. Please
+ * use setAppender() to set a specific appender (e.g. WindowAppender).
+ * use {@see Log4js#getLogger(String)} to get an instance.
+ * @constructor
+ * @param name name of category to log to
+ * @author Stephan Strittmatter
+ */
+Log4js.Logger = function (name) {
+       this.loggingEvents = [];
+       this.appenders = [];
+       /** category of logger */
+       this.category = name || "";
+       /** level to be logged */
+       this.level = Log4js.Level.FATAL;
+
+       this.dateformat = Log4js.DateFormatter.DEFAULT_DATE_FORMAT;
+       this.dateformatter = new Log4js.DateFormatter();
+
+       this.onlog = new Log4js.CustomEvent();
+       this.onclear = new Log4js.CustomEvent();
+
+       /** appender to write in */
+       this.appenders.push(new Log4js.Appender(this));
+
+       // if multiple log objects are instantiated this will only log to the log
+       // object that is declared last can't seem to get the attachEvent method to
+       // work correctly
+       try {
+               window.onerror = this.windowError.bind(this);
+       } catch (e) {
+               //log4jsLogger.fatal(e);
+       }
+};
+
+Log4js.Logger.prototype = {
+       /**
+        * add additional appender. DefaultAppender always is there.
+        * @param appender additional wanted appender
+        */
+       addAppender: function (appender) {
+               if (appender instanceof Log4js.Appender) {
+                       appender.setLogger(this);
+                       this.appenders.push(appender);
+               } else {
+                       throw "Not instance of an Appender: " + appender;
+               }
+       },
+       /**
+        * set Array of appenders. Previous Appenders are cleared and removed.
+        * @param {Array} appenders Array of Appenders
+        */
+       setAppenders: function (appenders) {
+               //clear first all existing appenders
+               for (var i = 0; i < this.appenders.length; i++) {
+                       this.appenders[i].doClear();
+               }
+
+               this.appenders = appenders;
+
+               for (var j = 0; j < this.appenders.length; j++) {
+                       this.appenders[j].setLogger(this);
+               }
+       },
+       /**
+        * Set the Loglevel default is LogLEvel.TRACE
+        * @param level wanted logging level
+        */
+       setLevel: function (level) {
+               this.level = level;
+       },
+       /**
+        * main log method logging to all available appenders
+        * @private
+        */
+       log: function (logLevel, message, exception) {
+               var loggingEvent = new Log4js.LoggingEvent(this.category, logLevel,
+                               message, exception, this);
+               this.loggingEvents.push(loggingEvent);
+               this.onlog.dispatch(loggingEvent);
+       },
+       /** clear logging */
+       clear: function () {
+               try {
+                       this.loggingEvents = [];
+                       this.onclear.dispatch();
+               } catch (e) {
+               }
+       },
+       /** checks if Level Trace is enabled */
+       isTraceEnabled: function () {
+               if (this.level.valueOf() <= Log4js.Level.TRACE.valueOf()) {
+                       return true;
+               }
+               return false;
+       },
+       /**
+        * Trace messages
+        * @param message {Object} message to be logged
+        */
+       trace: function (message) {
+               if (this.isTraceEnabled()) {
+                       this.log(Log4js.Level.TRACE, message, null);
+               }
+       },
+       /** checks if Level Debug is enabled */
+       isDebugEnabled: function () {
+               if (this.level.valueOf() <= Log4js.Level.DEBUG.valueOf()) {
+                       return true;
+               }
+               return false;
+       },
+       /**
+        * Debug messages
+        * @param message {Object} message to be logged
+        */
+       debug: function (message) {
+               if (this.isDebugEnabled()) {
+                       this.log(Log4js.Level.DEBUG, message, null);
+               }
+       },
+       /**
+        * Debug messages
+        * @param {Object} message  message to be logged
+        * @param {Throwable} throwable
+        */
+       debug: function (message, throwable) {
+               if (this.isDebugEnabled()) {
+                       this.log(Log4js.Level.DEBUG, message, throwable);
+               }
+       },
+                       /** checks if Level Info is enabled */
+                       isInfoEnabled: function () {
+                               if (this.level.valueOf() <= Log4js.Level.INFO.valueOf()) {
+                                       return true;
+                               }
+                               return false;
+                       },
+       /**
+        * logging info messages
+        * @param {Object} message  message to be logged
+        */
+       info: function (message) {
+               if (this.isInfoEnabled()) {
+                       this.log(Log4js.Level.INFO, message, null);
+               }
+       },
+       /**
+        * logging info messages
+        * @param {Object} message  message to be logged
+        * @param {Throwable} throwable
+        */
+       info: function (message, throwable) {
+               if (this.isInfoEnabled()) {
+                       this.log(Log4js.Level.INFO, message, throwable);
+               }
+       },
+                       /** checks if Level Warn is enabled */
+                       isWarnEnabled: function () {
+                               if (this.level.valueOf() <= Log4js.Level.WARN.valueOf()) {
+                                       return true;
+                               }
+                               return false;
+                       },
+       /** logging warn messages */
+       warn: function (message) {
+               if (this.isWarnEnabled()) {
+                       this.log(Log4js.Level.WARN, message, null);
+               }
+       },
+       /** logging warn messages */
+       warn: function (message, throwable) {
+               if (this.isWarnEnabled()) {
+                       this.log(Log4js.Level.WARN, message, throwable);
+               }
+       },
+                       /** checks if Level Error is enabled */
+                       isErrorEnabled: function () {
+                               if (this.level.valueOf() <= Log4js.Level.ERROR.valueOf()) {
+                                       return true;
+                               }
+                               return false;
+                       },
+       /** logging error messages */
+       error: function (message) {
+               if (this.isErrorEnabled()) {
+                       this.log(Log4js.Level.ERROR, message, null);
+               }
+       },
+       /** logging error messages */
+       error: function (message, throwable) {
+               if (this.isErrorEnabled()) {
+                       this.log(Log4js.Level.ERROR, message, throwable);
+               }
+       },
+                       /** checks if Level Fatal is enabled */
+                       isFatalEnabled: function () {
+                               if (this.level.valueOf() <= Log4js.Level.FATAL.valueOf()) {
+                                       return true;
+                               }
+                               return false;
+                       },
+       /** logging fatal messages */
+       fatal: function (message) {
+               if (this.isFatalEnabled()) {
+                       this.log(Log4js.Level.FATAL, message, null);
+               }
+       },
+       /** logging fatal messages */
+       fatal: function (message, throwable) {
+               if (this.isFatalEnabled()) {
+                       this.log(Log4js.Level.FATAL, message, throwable);
+               }
+       },
+                       /**
+                        * Capture main window errors and log as fatal.
+                        * @private
+                        */
+                       windowError: function (msg, url, line) {
+                               var message = "Error in (" + (url || window.location) + ") on line " + line + " with message (" + msg + ")";
+                               this.log(Log4js.Level.FATAL, message, null);
+                       },
+       /**
+        * Set the date format of logger. Following switches are supported:
+        * <ul>
+        * <li>yyyy - The year</li>
+        * <li>MM - the month</li>
+        * <li>dd - the day of month<li>
+        * <li>hh - the hour<li>
+        * <li>mm - minutes</li>
+        * <li>O - timezone offset</li>
+        * </ul>
+        * @param {String} format format String for the date
+        * @see #getTimestamp
+        */
+       setDateFormat: function (format) {
+               this.dateformat = format;
+       },
+       /**
+        * Generates a timestamp using the format set in {Log4js.setDateFormat}.
+        * @param {Date} date the date to format
+        * @see #setDateFormat
+        * @return A formatted timestamp with the current date and time.
+        */
+       getFormattedTimestamp: function (date) {
+               return this.dateformatter.formatDate(date, this.dateformat);
+       }
+};
+
+/**
+ * Abstract base class for other appenders.
+ * It is doing nothing.
+ *
+ * @constructor
+ * @param {Log4js.Logger} logger log4js instance this appender is attached to
+ * @author Stephan Strittmatter
+ */
+Log4js.Appender = function () {
+       /**
+        * Reference to calling logger
+        * @type Log4js.Logger
+        * @private
+        */
+       this.logger = null;
+};
+
+Log4js.Appender.prototype = {
+       /**
+        * appends the given loggingEvent appender specific
+        * @param {Log4js.LoggingEvent} loggingEvent loggingEvent to append
+        */
+       doAppend: function (loggingEvent) {
+               return;
+       },
+       /**
+        * clears the Appender
+        */
+       doClear: function () {
+               return;
+       },
+       /**
+        * Set the Layout for this appender.
+        * @param {Log4js.Layout} layout Layout for formatting loggingEvent
+        */
+       setLayout: function (layout) {
+               this.layout = layout;
+       },
+       /**
+        * Set reference to the logger.
+        * @param {Log4js.Logger} the invoking logger
+        */
+       setLogger: function (logger) {
+               // add listener to the logger methods
+               logger.onlog.addListener(Log4js.bind(this.doAppend, this));
+               logger.onclear.addListener(Log4js.bind(this.doClear, this));
+
+               this.logger = logger;
+       }
+};
+
+/**
+ * Interface for Layouts.
+ * Use this Layout as "interface" for other Layouts. It is doing nothing.
+ *
+ * @constructor
+ * @author Stephan Strittmatter
+ */
+Log4js.Layout = function () {
+       return;
+};
+Log4js.Layout.prototype = {
+       /**
+        * Implement this method to create your own layout format.
+        * @param {Log4js.LoggingEvent} loggingEvent loggingEvent to format
+        * @return formatted String
+        * @type String
+        */
+       format: function (loggingEvent) {
+               return "";
+       },
+       /**
+        * Returns the content type output by this layout.
+        * @return The base class returns "text/plain".
+        * @type String
+        */
+       getContentType: function () {
+               return "text/plain";
+       },
+       /**
+        * @return Returns the header for the layout format. The base class returns null.
+        * @type String
+        */
+       getHeader: function () {
+               return null;
+       },
+       /**
+        * @return Returns the footer for the layout format. The base class returns null.
+        * @type String
+        */
+       getFooter: function () {
+               return null;
+       },
+       /**
+        * @return Separator between events
+        * @type String
+        */
+       getSeparator: function () {
+               return "";
+       }
+};
+
+/**
+ * Console Appender writes the logs to a console.  If "inline" is
+ * set to "false" the console launches in another window otherwise
+ * the window is inline on the page and toggled on and off with "Alt-D".
+ * Note: At FireFox &gb; 2.0 the keystroke is little different now: "SHIFT+ALT+D".
+ *
+ * @constructor
+ * @extends Log4js.Appender
+ * @param {boolean} isInline boolean value that indicates whether the console be placed inline, default is to launch in new window
+ *
+ * @author Corey Johnson - original console code in Lumberjack (http://gleepglop.com/javascripts/logger/)
+ * @author Seth Chisamore - adapted for use as a log4js appender
+ */
+Log4js.ConsoleAppender = function (isInline) {
+
+       /**
+        * @type Log4js.Layout
+        * @private
+        */
+       this.layout = new Log4js.PatternLayout(Log4js.PatternLayout.TTCC_CONVERSION_PATTERN);
+       /**
+        * @type boolean
+        * @private
+        */
+       this.inline = isInline;
+
+       /**
+        * @type String
+        * @private
+        */
+       this.accesskey = "d";
+
+       /**
+        * @private
+        */
+       this.tagPattern = null;
+
+       this.commandHistory = [];
+       this.commandIndex = 0;
+
+       /**
+        * true if popup is blocked.
+        */
+       this.popupBlocker = false;
+
+       /**
+        * current output div-element.
+        */
+       this.outputElement = null;
+
+       this.docReference = null;
+       this.winReference = null;
+
+       if (this.inline) {
+               Log4js.attachEvent(window, 'load', Log4js.bind(this.initialize, this));
+       }
+};
+
+Log4js.ConsoleAppender.prototype = Log4js.extend(new Log4js.Appender(), {
+       /**
+        * Set the access key to show/hide the inline console (default &quote;d&quote;)
+        * @param key access key to show/hide the inline console
+        */
+       setAccessKey: function (key) {
+               this.accesskey = key;
+       },
+       /**
+        * @private
+        */
+       initialize: function () {
+
+               if (!this.inline) {
+                       var doc = null;
+                       var win = null;
+                       window.top.consoleWindow = window.open("", this.logger.category,
+                                       "left=0,top=0,width=700,height=700,scrollbars=no,status=no,resizable=yes;toolbar=no");
+                       window.top.consoleWindow.opener = self;
+                       win = window.top.consoleWindow;
+
+                       if (!win) {
+                               this.popupBlocker = true;
+                               alert("Popup window manager blocking the Log4js popup window to bedisplayed.\n\n"
+                                               + "Please disabled this to properly see logged events.");
+                       } else {
+
+                               doc = win.document;
+                               doc.open();
+                               doc.write("<!DOCTYPE html PUBLIC -//W3C//DTD XHTML 1.0 Transitional//EN ");
+                               doc.write("  http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd>\n\n");
+                               doc.write("<html><head><title>Log4js - " + this.logger.category + "</title>\n");
+                               doc.write("</head><body style=\"background-color:darkgray\"></body>\n");
+                               win.blur();
+                               win.focus();
+                       }
+
+                       this.docReference = doc;
+                       this.winReference = win;
+               } else {
+                       this.docReference = document;
+                       this.winReference = window;
+               }
+
+               this.outputCount = 0;
+               this.tagPattern = ".*";
+
+               // I hate writing javascript in HTML... but what's a better alternative
+               this.logElement = this.docReference.createElement('div');
+               this.docReference.body.appendChild(this.logElement);
+               this.logElement.style.display = 'none';
+
+               this.logElement.style.position = "absolute";
+               this.logElement.style.left = '0px';
+               this.logElement.style.width = '100%';
+
+               this.logElement.style.textAlign = "left";
+               this.logElement.style.fontFamily = "lucida console";
+               this.logElement.style.fontSize = "100%";
+               this.logElement.style.backgroundColor = 'darkgray';
+               this.logElement.style.opacity = 0.9;
+               this.logElement.style.zIndex = 2000;
+
+               // Add toolbarElement
+               this.toolbarElement = this.docReference.createElement('div');
+               this.logElement.appendChild(this.toolbarElement);
+               this.toolbarElement.style.padding = "0 0 0 2px";
+
+               // Add buttons
+               this.buttonsContainerElement = this.docReference.createElement('span');
+               this.toolbarElement.appendChild(this.buttonsContainerElement);
+
+               if (this.inline) {
+                       var closeButton = this.docReference.createElement('button');
+                       closeButton.style.cssFloat = "right";
+                       closeButton.style.styleFloat = "right"; // IE dom bug...doesn't understand cssFloat
+                       closeButton.style.color = "black";
+                       closeButton.innerHTML = "close";
+                       closeButton.onclick = Log4js.bind(this.toggle, this);
+                       this.buttonsContainerElement.appendChild(closeButton);
+               }
+
+               var clearButton = this.docReference.createElement('button');
+               clearButton.style.cssFloat = "right";
+               clearButton.style.styleFloat = "right"; // IE dom bug...doesn't understand cssFloat
+               clearButton.style.color = "black";
+               clearButton.innerHTML = "clear";
+               clearButton.onclick = Log4js.bind(this.logger.clear, this.logger);
+               this.buttonsContainerElement.appendChild(clearButton);
+
+
+               //Add CategoryName and  Level Filter
+               this.tagFilterContainerElement = this.docReference.createElement('span');
+               this.toolbarElement.appendChild(this.tagFilterContainerElement);
+               this.tagFilterContainerElement.style.cssFloat = 'left';
+
+               this.tagFilterContainerElement.appendChild(this.docReference.createTextNode("Log4js - " + this.logger.category));
+               this.tagFilterContainerElement.appendChild(this.docReference.createTextNode(" | Level Filter: "));
+
+               this.tagFilterElement = this.docReference.createElement('input');
+               this.tagFilterContainerElement.appendChild(this.tagFilterElement);
+               this.tagFilterElement.style.width = '200px';
+               this.tagFilterElement.value = this.tagPattern;
+               this.tagFilterElement.setAttribute('autocomplete', 'off'); // So Firefox doesn't flip out
+
+               Log4js.attachEvent(this.tagFilterElement, 'keyup', Log4js.bind(this.updateTags, this));
+               Log4js.attachEvent(this.tagFilterElement, 'click', Log4js.bind(function () {
+                       this.tagFilterElement.select();
+               }, this));
+
+               // Add outputElement
+               this.outputElement = this.docReference.createElement('div');
+               this.logElement.appendChild(this.outputElement);
+               this.outputElement.style.overflow = "auto";
+               this.outputElement.style.clear = "both";
+               this.outputElement.style.height = (this.inline) ? ("200px") : ("650px");
+               this.outputElement.style.width = "100%";
+               this.outputElement.style.backgroundColor = 'black';
+
+               this.inputContainerElement = this.docReference.createElement('div');
+               this.inputContainerElement.style.width = "100%";
+               this.logElement.appendChild(this.inputContainerElement);
+
+               this.inputElement = this.docReference.createElement('input');
+               this.inputContainerElement.appendChild(this.inputElement);
+               this.inputElement.style.width = '100%';
+               this.inputElement.style.borderWidth = '0px'; // Inputs with 100% width always seem to be too large (I HATE THEM) they only work if the border, margin and padding are 0
+               this.inputElement.style.margin = '0px';
+               this.inputElement.style.padding = '0px';
+               this.inputElement.value = 'Type command here';
+               this.inputElement.setAttribute('autocomplete', 'off'); // So Firefox doesn't flip out
+
+               Log4js.attachEvent(this.inputElement, 'keyup', Log4js.bind(this.handleInput, this));
+               Log4js.attachEvent(this.inputElement, 'click', Log4js.bind(function () {
+                       this.inputElement.select();
+               }, this));
+
+               if (this.inline) {
+                       window.setInterval(Log4js.bind(this.repositionWindow, this), 500);
+                       this.repositionWindow();
+                       // Allow acess key link
+                       var accessElement = this.docReference.createElement('button');
+                       accessElement.style.position = "absolute";
+                       accessElement.style.top = "-100px";
+                       accessElement.accessKey = this.accesskey;
+                       accessElement.onclick = Log4js.bind(this.toggle, this);
+                       this.docReference.body.appendChild(accessElement);
+               } else {
+                       this.show();
+               }
+       },
+       /**
+        * shows/hide an element
+        * @private
+        * @return true if shown
+        */
+       toggle: function () {
+               if (this.logElement.style.display == 'none') {
+                       this.show();
+                       return true;
+               } else {
+                       this.hide();
+                       return false;
+               }
+       },
+       /**
+        * @private
+        */
+       show: function () {
+               this.logElement.style.display = '';
+               this.outputElement.scrollTop = this.outputElement.scrollHeight; // Scroll to bottom when toggled
+               this.inputElement.select();
+       },
+       /**
+        * @private
+        */
+       hide: function () {
+               this.logElement.style.display = 'none';
+       },
+       /**
+        * @private
+        * @param message
+        * @style
+        */
+       output: function (message, style) {
+
+               // If we are at the bottom of the window, then keep scrolling with the output
+               var shouldScroll = (this.outputElement.scrollTop + (2 * this.outputElement.clientHeight)) >= this.outputElement.scrollHeight;
+
+               this.outputCount++;
+               style = (style ? style += ';' : '');
+               style += 'padding:1px;margin:0 0 5px 0';
+
+               if (this.outputCount % 2 === 0) {
+                       style += ";background-color:#101010";
+               }
+
+               message = message || "undefined";
+               message = message.toString();
+
+               this.outputElement.innerHTML += "<pre style='" + style + "'>" + message + "</pre>";
+
+               if (shouldScroll) {
+                       this.outputElement.scrollTop = this.outputElement.scrollHeight;
+               }
+       },
+       /**
+        * @private
+        */
+       updateTags: function () {
+
+               var pattern = this.tagFilterElement.value;
+
+               if (this.tagPattern == pattern) {
+                       return;
+               }
+
+               try {
+                       new RegExp(pattern);
+               } catch (e) {
+                       return;
+               }
+
+               this.tagPattern = pattern;
+
+               this.outputElement.innerHTML = "";
+
+               // Go through each log entry again
+               this.outputCount = 0;
+               for (var i = 0; i < this.logger.loggingEvents.length; i++) {
+                       this.doAppend(this.logger.loggingEvents[i]);
+               }
+       },
+       /**
+        * @private
+        */
+       repositionWindow: function () {
+               var offset = window.pageYOffset || this.docReference.documentElement.scrollTop || this.docReference.body.scrollTop;
+               var pageHeight = self.innerHeight || this.docReference.documentElement.clientHeight || this.docReference.body.clientHeight;
+               this.logElement.style.top = (offset + pageHeight - this.logElement.offsetHeight) + "px";
+       },
+       /**
+        * @param loggingEvent event to be logged
+        * @see Log4js.Appender#doAppend
+        */
+       doAppend: function (loggingEvent) {
+
+               if (this.popupBlocker) {
+                       //popup blocked, we return in this case
+                       return;
+               }
+
+               if ((!this.inline) && (!this.winReference || this.winReference.closed)) {
+                       this.initialize();
+               }
+
+               if (this.tagPattern !== null &&
+                               loggingEvent.level.toString().search(new RegExp(this.tagPattern, 'igm')) == -1) {
+                       return;
+               }
+
+               var style = '';
+
+               if (loggingEvent.level.toString().search(/ERROR/) != -1) {
+                       style += 'color:red';
+               } else if (loggingEvent.level.toString().search(/FATAL/) != -1) {
+                       style += 'color:red';
+               } else if (loggingEvent.level.toString().search(/WARN/) != -1) {
+                       style += 'color:orange';
+               } else if (loggingEvent.level.toString().search(/DEBUG/) != -1) {
+                       style += 'color:green';
+               } else if (loggingEvent.level.toString().search(/INFO/) != -1) {
+                       style += 'color:white';
+               } else {
+                       style += 'color:yellow';
+               }
+
+               this.output(this.layout.format(loggingEvent), style);
+       },
+       /**
+        * @see Log4js.Appender#doClear
+        */
+       doClear: function () {
+               this.outputElement.innerHTML = "";
+       },
+       /**
+        * @private
+        * @param e
+        */
+       handleInput: function (e) {
+               if (e.keyCode == 13) {
+                       var command = this.inputElement.value;
+
+                       switch (command) {
+                               case "clear":
+                                       this.logger.clear();
+                                       break;
+
+                               default:
+                                       var consoleOutput = "";
+
+                                       try {
+                                               consoleOutput = eval(this.inputElement.value);
+                                       } catch (e) {
+                                               this.logger.error("Problem parsing input <" + command + ">" + e.message);
+                                               break;
+                                       }
+
+                                       this.logger.trace(consoleOutput);
+                                       break;
+                       }
+
+                       if (this.inputElement.value !== "" && this.inputElement.value !== this.commandHistory[0]) {
+                               this.commandHistory.unshift(this.inputElement.value);
+                       }
+
+                       this.commandIndex = 0;
+                       this.inputElement.value = "";
+               } else if (e.keyCode == 38 && this.commandHistory.length > 0) {
+                       this.inputElement.value = this.commandHistory[this.commandIndex];
+
+                       if (this.commandIndex < this.commandHistory.length - 1) {
+                               this.commandIndex += 1;
+                       }
+               } else if (e.keyCode == 40 && this.commandHistory.length > 0) {
+                       if (this.commandIndex > 0) {
+                               this.commandIndex -= 1;
+                       }
+
+                       this.inputElement.value = this.commandHistory[this.commandIndex];
+               } else {
+                       this.commandIndex = 0;
+               }
+       },
+       /**
+        * toString
+        */
+       toString: function () {
+               return "Log4js.ConsoleAppender[inline=" + this.inline + "]";
+       }
+});
+
+/**
+ * Metatag Appender writing the logs to meta tags
+ *
+ * @extends Log4js.Appender
+ * @constructor
+ * @param logger log4js instance this appender is attached to
+ * @author Stephan Strittmatter
+ */
+Log4js.MetatagAppender = function () {
+       this.currentLine = 0;
+};
+Log4js.MetatagAppender.prototype = Log4js.extend(new Log4js.Appender(), {
+       /**
+        * @param loggingEvent event to be logged
+        * @see Log4js.Appender#doAppend
+        */
+       doAppend: function (loggingEvent) {
+               var now = new Date();
+               var lines = loggingEvent.message.split("\n");
+               var headTag = document.getElementsByTagName("head")[0];
+
+               for (var i = 1; i <= lines.length; i++) {
+                       var value = lines[i - 1];
+                       if (i == 1) {
+                               value = loggingEvent.level.toString() + ": " + value;
+                       } else {
+                               value = "> " + value;
+                       }
+
+                       var metaTag = document.createElement("meta");
+                       metaTag.setAttribute("name", "X-log4js:" + this.currentLine);
+                       metaTag.setAttribute("content", value);
+                       headTag.appendChild(metaTag);
+                       this.currentLine += 1;
+               }
+       },
+       /**
+        * toString
+        */
+       toString: function () {
+               return "Log4js.MetatagAppender";
+       }
+});
+
+/**
+ * AJAX Appender sending {@link Log4js.LoggingEvent}s asynchron via
+ * <code>XMLHttpRequest</code> to server.<br />
+ * The {@link Log4js.LoggingEvent} is POSTed as response content and is
+ * formatted by the accociated layout. Default layout is {@link Log4js.XMLLayout}.
+ * The <code>threshold</code> defines when the logs
+ * should be send to the server. By default every event is sent on its
+ * own (threshold=1). If it is set to 10, then the events are send in groups of
+ * 10 events.
+ *
+ * @extends Log4js.Appender
+ * @constructor
+ * @param {Log4js.Logger} logger log4js instance this appender is attached to
+ * @param {String} loggingUrl url where appender will post log messages to
+ * @author Stephan Strittmatter
+ */
+Log4js.AjaxAppender = function (loggingUrl) {
+
+       /**
+        * is still esnding data to server
+        * @type boolean
+        * @private
+        */
+       this.isInProgress = false;
+
+       /**
+        * @type String
+        * @private
+        */
+       this.loggingUrl = loggingUrl || "logging.log4js";
+
+       /**
+        * @type Integer
+        * @private
+        */
+       this.threshold = 1;
+
+       /**
+        * timeout when request is aborted.
+        * @private
+        */
+       this.timeout = 2000;
+
+       /**
+        * List of LoggingEvents which should be send after threshold is reached.
+        * @type Map
+        * @private
+        */
+       this.loggingEventMap = new Log4js.FifoBuffer();
+
+       /**
+        * @type Log4js.Layout
+        * @private
+        */
+       this.layout = new Log4js.XMLLayout();
+       /**
+        * @type XMLHttpRequest
+        * @private
+        */
+       this.httpRequest = null;
+};
+
+Log4js.AjaxAppender.prototype = Log4js.extend(new Log4js.Appender(), {
+       /**
+        * sends the logs to the server
+        * @param loggingEvent event to be logged
+        * @see Log4js.Appender#doAppend
+        */
+       doAppend: function (loggingEvent) {
+               log4jsLogger.trace("> AjaxAppender.append");
+
+               if (this.loggingEventMap.length() <= this.threshold || this.isInProgress === true) {
+                       this.loggingEventMap.push(loggingEvent);
+               }
+
+               if (this.loggingEventMap.length() >= this.threshold && this.isInProgress === false) {
+                       //if threshold is reached send the events and reset current threshold
+                       this.send();
+               }
+
+               log4jsLogger.trace("< AjaxAppender.append");
+       },
+       /** @see Appender#doClear */
+       doClear: function () {
+               log4jsLogger.trace("> AjaxAppender.doClear");
+               if (this.loggingEventMap.length() > 0) {
+                       this.send();
+               }
+               log4jsLogger.trace("< AjaxAppender.doClear");
+       },
+       /**
+        * Set the threshold when logs have to be send. Default threshold is 1.
+        * @praram {int} threshold new threshold
+        */
+       setThreshold: function (threshold) {
+               log4jsLogger.trace("> AjaxAppender.setThreshold: " + threshold);
+               this.threshold = threshold;
+               log4jsLogger.trace("< AjaxAppender.setThreshold");
+       },
+       /**
+        * Set the timeout in milli seconds until sending request is aborted.
+        * Default is 2000 ms.
+        * @param {int} milliseconds the new timeout
+        */
+       setTimeout: function (milliseconds) {
+               this.timeout = milliseconds;
+       },
+       /**
+        * send the request.
+        */
+       send: function () {
+               if (this.loggingEventMap.length() > 0) {
+
+                       log4jsLogger.trace("> AjaxAppender.send");
+
+
+                       this.isInProgress = true;
+                       var a = [];
+
+                       for (var i = 0; i < this.loggingEventMap.length() && i < this.threshold; i++) {
+                               a.push(this.layout.format(this.loggingEventMap.pull()));
+                       }
+
+                       var content = this.layout.getHeader();
+                       content += a.join(this.layout.getSeparator());
+                       content += this.layout.getFooter();
+
+                       var appender = this;
+                       if (this.httpRequest === null) {
+                               this.httpRequest = this.getXmlHttpRequest();
+                       }
+                       this.httpRequest.onreadystatechange = function () {
+                               appender.onReadyStateChanged.call(appender);
+                       };
+
+                       this.httpRequest.open("POST", this.loggingUrl, true);
+                       // set the request headers.
+                       //this.httpRequest.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
+                       this.httpRequest.setRequestHeader("Content-type", this.layout.getContentType());
+                       //REFERER will be the top-level
+                       // URI which may differ from the location of the error if
+                       // it occurs in an included .js file
+                       this.httpRequest.setRequestHeader("REFERER", location.href);
+                       this.httpRequest.setRequestHeader("Content-length", content.length);
+                       this.httpRequest.setRequestHeader("Connection", "close");
+                       this.httpRequest.send(content);
+
+                       appender = this;
+
+                       try {
+                               window.setTimeout(function () {
+                                       log4jsLogger.trace("> AjaxAppender.timeout");
+                                       appender.httpRequest.onreadystatechange = function () {
+                                               return;
+                                       };
+                                       appender.httpRequest.abort();
+                                       //this.httpRequest = null;
+                                       appender.isInProgress = false;
+
+                                       if (appender.loggingEventMap.length() > 0) {
+                                               appender.send();
+                                       }
+                                       log4jsLogger.trace("< AjaxAppender.timeout");
+                               }, this.timeout);
+                       } catch (e) {
+                               log4jsLogger.fatal(e);
+                       }
+                       log4jsLogger.trace("> AjaxAppender.send");
+               }
+       },
+       /**
+        * @private
+        */
+       onReadyStateChanged: function () {
+               log4jsLogger.trace("> AjaxAppender.onReadyStateChanged");
+               var req = this.httpRequest;
+               if (this.httpRequest.readyState != 4) {
+                       log4jsLogger.trace("< AjaxAppender.onReadyStateChanged: readyState " + req.readyState + " != 4");
+                       return;
+               }
+
+               var success = ((typeof req.status === "undefined") || req.status === 0 || (req.status >= 200 && req.status < 300));
+
+               if (success) {
+                       log4jsLogger.trace("  AjaxAppender.onReadyStateChanged: success");
+
+                       //ready sending data
+                       this.isInProgress = false;
+
+               } else {
+                       var msg = "  AjaxAppender.onReadyStateChanged: XMLHttpRequest request to URL " + this.loggingUrl + " returned status code " + this.httpRequest.status;
+                       log4jsLogger.error(msg);
+               }
+
+               log4jsLogger.trace("< AjaxAppender.onReadyStateChanged: readyState == 4");
+       },
+       /**
+        * Get the XMLHttpRequest object independent of browser.
+        * @private
+        */
+       getXmlHttpRequest: function () {
+               log4jsLogger.trace("> AjaxAppender.getXmlHttpRequest");
+
+               var httpRequest = false;
+
+               try {
+                       if (window.XMLHttpRequest) { // Mozilla, Safari, IE7...
+                               httpRequest = new XMLHttpRequest();
+                               if (httpRequest.overrideMimeType) {
+                                       httpRequest.overrideMimeType(this.layout.getContentType());
+                               }
+                       } else if (window.ActiveXObject) { // IE
+                               try {
+                                       httpRequest = new ActiveXObject("Msxml2.XMLHTTP");
+                               } catch (e) {
+                                       httpRequest = new ActiveXObject("Microsoft.XMLHTTP");
+                               }
+                       }
+               } catch (e) {
+                       httpRequest = false;
+               }
+
+               if (!httpRequest) {
+                       log4jsLogger.fatal("Unfortunatelly your browser does not support AjaxAppender for log4js!");
+               }
+
+               log4jsLogger.trace("< AjaxAppender.getXmlHttpRequest");
+               return httpRequest;
+       },
+       /**
+        * toString
+        */
+       toString: function () {
+               return "Log4js.AjaxAppender[loggingUrl=" + this.loggingUrl + ", threshold=" + this.threshold + "]";
+       }
+});
+
+/**
+ * File Appender writing the logs to a text file.
+ * PLEASE NOTE - Only works in IE and Mozilla
+ * use ActiveX to write file on IE
+ * use XPCom components  to write file on Mozilla
+ *
+ * @extends Log4js.Appender
+ * @constructor
+ * @param logger log4js instance this appender is attached to
+ * @param file file log messages will be written to
+ * @author Seth Chisamore
+ * @author Nicolas Justin njustin@idealx.com
+ * @author Gregory Kokanosky gkokanosky@idealx.com
+ */
+Log4js.FileAppender = function (file) {
+
+       this.layout = new Log4js.SimpleLayout();
+       this.isIE = 'undefined';
+
+       this.file = file || "log4js.log";
+
+       try {
+               this.fso = new ActiveXObject("Scripting.FileSystemObject");
+               this.isIE = true;
+       } catch (e) {
+               try {
+                       netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
+                       this.fso = Components.classes["@mozilla.org/file/local;1"].createInstance(Components.interfaces.nsILocalFile);
+                       this.isIE = false; //mozilla & co
+               } catch (e) {
+                       log4jsLogger.error(e);
+               }
+       }
+};
+
+Log4js.FileAppender.prototype = Log4js.extend(new Log4js.Appender(), {
+       /**
+        * @param loggingEvent event to be logged
+        * @see Log4js.Appender#doAppend
+        */
+       doAppend: function (loggingEvent) {
+               try {
+                       var fileHandle = null;
+
+                       if (this.isIE === 'undefined') {
+                               log4jsLogger.error("Unsupported ")
+                       }
+                       else if (this.isIE) {
+                               // try opening existing file, create if needed
+                               fileHandle = this.fso.OpenTextFile(this.file, 8, true);
+                               // write out our data
+                               fileHandle.WriteLine(this.layout.format(loggingEvent));
+                               fileHandle.close();
+                       } else {
+                               netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
+                               this.fso.initWithPath(this.file);
+                               if (!this.fso.exists()) {
+                                       //create file if needed
+                                       this.fso.create(0x00, 0600);
+                               }
+
+                               fileHandle = Components.classes["@mozilla.org/network/file-output-stream;1"].createInstance(Components.interfaces.nsIFileOutputStream);
+                               fileHandle.init(this.fso, 0x04 | 0x08 | 0x10, 064, 0);
+                               var line = this.layout.format(loggingEvent);
+                               fileHandle.write(line, line.length); //write data
+                               fileHandle.close();
+                       }
+               } catch (e) {
+                       log4jsLogger.error(e);
+               }
+       },
+       /*
+        * @see Log4js.Appender#doClear
+        */
+       doClear: function () {
+               try {
+                       if (this.isIE) {
+                               var fileHandle = this.fso.GetFile(this.file);
+                               fileHandle.Delete();
+                       } else {
+                               netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
+                               this.fso.initWithPath(this.file);
+                               if (this.fso.exists()) {
+                                       this.fso.remove(false);
+                               }
+                       }
+               } catch (e) {
+                       log4jsLogger.error(e);
+               }
+       },
+       /**
+        * toString
+        */
+       toString: function () {
+               return "Log4js.FileAppender[file=" + this.file + "]";
+       }
+});
+
+/**
+ * Windows Event Appender writes the logs to the Windows Event log.
+ * PLEASE NOTE - Only works in IE..uses ActiveX to write to Windows Event log
+ *
+ * @extends Log4js.Appender
+ * @constructor
+ * @param logger log4js instance this appender is attached to
+ * @author Seth Chisamore
+ */
+Log4js.WindowsEventAppender = function () {
+
+       this.layout = new Log4js.SimpleLayout();
+
+       try {
+               this.shell = new ActiveXObject("WScript.Shell");
+       } catch (e) {
+               log4jsLogger.error(e);
+       }
+};
+
+Log4js.WindowsEventAppender.prototype = Log4js.extend(new Log4js.Appender(), {
+       /**
+        * @param loggingEvent event to be logged
+        * @see Log4js.Appender#doAppend
+        */
+       doAppend: function (loggingEvent) {
+               var winLevel = 4;
+
+               // Map log level to windows event log level.
+               // Windows events: - SUCCESS: 0, ERROR: 1, WARNING: 2, INFORMATION: 4, AUDIT_SUCCESS: 8, AUDIT_FAILURE: 16
+               switch (loggingEvent.level) {
+                       case Log4js.Level.FATAL:
+                               winLevel = 1;
+                               break;
+                       case Log4js.Level.ERROR:
+                               winLevel = 1;
+                               break;
+                       case Log4js.Level.WARN:
+                               winLevel = 2;
+                               break;
+                       default:
+                               winLevel = 4;
+                               break;
+               }
+
+               try {
+                       this.shell.LogEvent(winLevel, this.level.format(loggingEvent));
+               } catch (e) {
+                       log4jsLogger.error(e);
+               }
+       },
+       /**
+        * toString
+        */
+       toString: function () {
+               return "Log4js.WindowsEventAppender";
+       }
+});
+
+/**
+ * JS Alert Appender writes the logs to the JavaScript alert dialog box
+ * @constructor
+ * @extends Log4js.Appender
+ * @param logger log4js instance this appender is attached to
+ * @author S&eacute;bastien LECACHEUR
+ */
+Log4js.JSAlertAppender = function () {
+
+       this.layout = new Log4js.SimpleLayout();
+};
+
+Log4js.JSAlertAppender.prototype = Log4js.extend(new Log4js.Appender(), {
+       /**
+        * @see Log4js.Appender#doAppend
+        */
+       doAppend: function (loggingEvent) {
+               alert(this.layout.getHeader() + this.layout.format(loggingEvent) + this.layout.getFooter());
+       },
+       /**
+        * toString
+        */
+       toString: function () {
+               return "Log4js.JSAlertAppender";
+       }
+});
+
+/**
+ * Appender writes the logs to the JavaScript console of Mozilla browser
+ * More infos: http://kb.mozillazine.org/index.php?title=JavaScript_Console&redirect=no
+ * PLEASE NOTE - Only works in Mozilla browser
+ * @constructor
+ * @extends Log4js.Appender
+ * @param logger log4js instance this appender is attached to
+ * @author Stephan Strittmatter
+ */
+Log4js.MozillaJSConsoleAppender = function () {
+       this.layout = new Log4js.SimpleLayout();
+       try {
+               netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
+               this.jsConsole = Components.classes["@mozilla.org/consoleservice;1"].getService(Components.interfaces.nsIConsoleService);
+               this.scriptError = Components.classes["@mozilla.org/scripterror;1"].createInstance(Components.interfaces.nsIScriptError);
+       } catch (e) {
+               log4jsLogger.error(e);
+       }
+};
+
+Log4js.MozillaJSConsoleAppender.prototype = Log4js.extend(new Log4js.Appender(), {
+       /**
+        * @see Log4js.Appender#doAppend
+        */
+       doAppend: function (loggingEvent) {
+               try {
+                       netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
+                       this.scriptError.init(this.layout.format(loggingEvent), null, null, null, null, this.getFlag(loggingEvent), loggingEvent.categoryName);
+                       this.jsConsole.logMessage(this.scriptError);
+               } catch (e) {
+                       log4jsLogger.error(e);
+               }
+       },
+       /**
+        * toString
+        */
+       toString: function () {
+               return "Log4js.MozillaJSConsoleAppender";
+       },
+       /**
+        * Map Log4js.Level to jsConsole Flags:
+        * <ul>
+        * <li>nsIScriptError.errorFlag (0) = Level.Error</li>
+        * <li>nsIScriptError.warningFlag (1)= Log4js.Level.WARN</li>
+        * <li>nsIScriptError.exceptionFlag (2) = Log4js.Level.FATAL</li>
+        * <li>nsIScriptError.strictFlag (4) = unused</li>
+        * </ul>
+        * @private
+        */
+       getFlag: function (loggingEvent)
+       {
+               var retval;
+               switch (loggingEvent.level) {
+                       case Log4js.Level.FATAL:
+                               retval = 2;//nsIScriptError.exceptionFlag = 2
+                               break;
+                       case Log4js.Level.ERROR:
+                               retval = 0;//nsIScriptError.errorFlag
+                               break;
+                       case Log4js.Level.WARN:
+                               retval = 1;//nsIScriptError.warningFlag = 1
+                               break;
+                       default:
+                               retval = 1;//nsIScriptError.warningFlag = 1
+                               break;
+               }
+
+               return retval;
+       }
+});
+
+/**
+ * Appender writes the logs to the JavaScript console of Opera browser
+ * PLEASE NOTE - Only works in Opera browser
+ * @constructor
+ * @extends Log4js.Appender
+ * @param logger log4js instance this appender is attached to
+ * @author Stephan Strittmatter
+ */
+Log4js.OperaJSConsoleAppender = function () {
+       this.layout = new Log4js.SimpleLayout();
+};
+
+Log4js.OperaJSConsoleAppender.prototype = Log4js.extend(new Log4js.Appender(), {
+       /**
+        * @see Log4js.Appender#doAppend
+        */
+       doAppend: function (loggingEvent) {
+               opera.postError(this.layout.format(loggingEvent));
+       },
+       /**
+        * toString
+        */
+       toString: function () {
+               return "Log4js.OperaJSConsoleAppender";
+       }
+});
+
+/**
+ * Appender writes the logs to the JavaScript console of Safari browser
+ * PLEASE NOTE - Only works in Safari browser
+ * @constructor
+ * @extends Log4js.Appender
+ * @param logger log4js instance this appender is attached to
+ * @author Stephan Strittmatter
+ */
+Log4js.SafariJSConsoleAppender = function () {
+       this.layout = new Log4js.SimpleLayout();
+};
+
+Log4js.SafariJSConsoleAppender.prototype = Log4js.extend(new Log4js.Appender(), {
+       /**
+        * @see Log4js.Appender#doAppend
+        */
+       doAppend: function (loggingEvent) {
+               window.console.log(this.layout.format(loggingEvent));
+       },
+       /**
+        * toString
+        */
+       toString: function () {
+               return "Log4js.SafariJSConsoleAppender";
+       }
+});
+
+/**
+ * JavaScript Console Appender which is browser independent.
+ * It checks internally for the current browser and adds delegate to
+ * specific JavaScript Console Appender of the browser.
+ *
+ * @author Stephan Strittmatter
+ * @since 1.0
+ */
+Log4js.BrowserConsoleAppender = function () {
+       /**
+        * Delegate for browser specific implementation
+        * @type Log4js.Appender
+        * @private
+        */
+       this.consoleDelegate = null;
+
+       if (window.console) {
+               this.consoleDelegate = new Log4js.SafariJSConsoleAppender();
+       }
+       else if (window.opera) {
+               this.consoleDelegate = new Log4js.OperaJSConsoleAppender();
+       }
+       else if (netscape) {
+               this.consoleDelegate = new Log4js.MozJSConsoleAppender();
+       }
+       else {
+               //@todo
+               log4jsLogger.error("Unsupported Browser");
+       }
+};
+
+Log4js.BrowserConsoleAppender.prototype = Log4js.extend(new Log4js.Appender(), {
+       /**
+        * @see Log4js.Appender#doAppend
+        */
+       doAppend: function (loggingEvent) {
+               this.consoleDelegate.doAppend(loggingEvent);
+       },
+       /**
+        * @see Log4js.Appender#doClear
+        */
+       doClear: function () {
+               this.consoleDelegate.doClear();
+       },
+       /**
+        * @see Log4js.Appender#setLayout
+        */
+       setLayout: function (layout) {
+               this.consoleDelegate.setLayout(layout);
+       },
+       /**
+        * toString
+        */
+       toString: function () {
+               return "Log4js.BrowserConsoleAppender: " + this.consoleDelegate.toString();
+       }
+});
+
+/**
+ * SimpleLayout consists of the level of the log statement, followed by " - "
+ * and then the log message itself. For example,
+ * <code>DEBUG - Hello world</code>
+ *
+ * @constructor
+ * @extends Log4js.Layout
+ * @extends Layout
+ * @author Stephan Strittmatter
+ */
+Log4js.SimpleLayout = function () {
+       this.LINE_SEP = "\n";
+       this.LINE_SEP_LEN = 1;
+};
+
+Log4js.SimpleLayout.prototype = Log4js.extend(new Log4js.Layout(), {
+       /**
+        * Implement this method to create your own layout format.
+        * @param {Log4js.LoggingEvent} loggingEvent loggingEvent to format
+        * @return formatted String
+        * @type String
+        */
+       format: function (loggingEvent) {
+               return loggingEvent.level.toString() + " - " + loggingEvent.message + this.LINE_SEP;
+       },
+       /**
+        * Returns the content type output by this layout.
+        * @return The base class returns "text/plain".
+        * @type String
+        */
+       getContentType: function () {
+               return "text/plain";
+       },
+       /**
+        * @return Returns the header for the layout format. The base class returns null.
+        * @type String
+        */
+       getHeader: function () {
+               return "";
+       },
+       /**
+        * @return Returns the footer for the layout format. The base class returns null.
+        * @type String
+        */
+       getFooter: function () {
+               return "";
+       }
+});
+
+/**
+ * BasicLayout is a simple layout for storing the loggs. The loggs are stored
+ * in following format:
+ * <pre>
+ * categoryName~startTime [logLevel] message\n
+ * </pre>
+ *
+ * @constructor
+ * @extends Log4js.Layout
+ * @author Stephan Strittmatter
+ */
+Log4js.BasicLayout = function () {
+       this.LINE_SEP = "\n";
+};
+
+Log4js.BasicLayout.prototype = Log4js.extend(new Log4js.Layout(), {
+       /**
+        * Implement this method to create your own layout format.
+        * @param {Log4js.LoggingEvent} loggingEvent loggingEvent to format
+        * @return formatted String
+        * @type String
+        */
+       format: function (loggingEvent) {
+               return loggingEvent.categoryName + "~" + loggingEvent.startTime.toLocaleString() + " [" + loggingEvent.level.toString() + "] " + loggingEvent.message + this.LINE_SEP;
+       },
+       /**
+        * Returns the content type output by this layout.
+        * @return The base class returns "text/plain".
+        * @type String
+        */
+       getContentType: function () {
+               return "text/plain";
+       },
+       /**
+        * @return Returns the header for the layout format. The base class returns null.
+        * @type String
+        */
+       getHeader: function () {
+               return "";
+       },
+       /**
+        * @return Returns the footer for the layout format. The base class returns null.
+        * @type String
+        */
+       getFooter: function () {
+               return "";
+       }
+});
+
+/**
+ * HtmlLayout write the logs in Html format.
+ *
+ * @constructor
+ * @extends Log4js.Layout
+ * @author Stephan Strittmatter
+ */
+Log4js.HtmlLayout = function () {
+       return;
+};
+
+Log4js.HtmlLayout.prototype = Log4js.extend(new Log4js.Layout(), {
+       /**
+        * Implement this method to create your own layout format.
+        * @param {Log4js.LoggingEvent} loggingEvent loggingEvent to format
+        * @return formatted String
+        * @type String
+        */
+       format: function (loggingEvent) {
+               return "<div style=\"" + this.getStyle(loggingEvent) + "\">" + loggingEvent.getFormattedTimestamp() + " - " + loggingEvent.level.toString() + " - " + loggingEvent.message + "</div>\n";
+       },
+       /**
+        * Returns the content type output by this layout.
+        * @return The base class returns "text/html".
+        * @type String
+        */
+       getContentType: function () {
+               return "text/html";
+       },
+       /**
+        * @return Returns the header for the layout format. The base class returns null.
+        * @type String
+        */
+       getHeader: function () {
+               return "<html><head><title>log4js</head><body>";
+       },
+       /**
+        * @return Returns the footer for the layout format. The base class returns null.
+        * @type String
+        */
+       getFooter: function () {
+               return "</body></html>";
+       },
+       getStyle: function (loggingEvent)
+       {
+               var style;
+               if (loggingEvent.level.toString().search(/ERROR/) != -1) {
+                       style = 'color:red';
+               } else if (loggingEvent.level.toString().search(/FATAL/) != -1) {
+                       style = 'color:red';
+               } else if (loggingEvent.level.toString().search(/WARN/) != -1) {
+                       style = 'color:orange';
+               } else if (loggingEvent.level.toString().search(/DEBUG/) != -1) {
+                       style = 'color:green';
+               } else if (loggingEvent.level.toString().search(/INFO/) != -1) {
+                       style = 'color:white';
+               } else {
+                       style = 'color:yellow';
+               }
+               return style;
+       }
+});
+
+/**
+ * XMLLayout write the logs in XML format.
+ * Layout is simmilar to log4j's XMLLayout:
+ * <pre>
+ * <log4js:event category="category" level="Level" client="Client" referer="ref" timestam="Date">
+ * <log4js:message>Logged message</log4js:message>
+ * </log4js:event>
+ * </pre>
+ * @constructor
+ * @extends Layout
+ * @author Stephan Strittmatter
+ */
+Log4js.XMLLayout = function () {
+       return;
+};
+Log4js.XMLLayout.prototype = Log4js.extend(new Log4js.Layout(), {
+       /**
+        * Implement this method to create your own layout format.
+        * @param {Log4js.LoggingEvent} loggingEvent loggingEvent to format
+        * @return formatted String
+        * @type String
+        */
+       format: function (loggingEvent) {
+               var useragent = "unknown";
+               try {
+                       useragent = navigator.userAgent;
+               } catch (e) {
+                       useragent = "unknown";
+               }
+
+               var referer = "unknown";
+               try {
+                       referer = location.href;
+               } catch (e) {
+                       referer = "unknown";
+               }
+
+               var content = "<log4js:event logger=\"";
+               content += loggingEvent.categoryName + "\" level=\"";
+               content += loggingEvent.level.toString() + "\" useragent=\"";
+               content += useragent + "\" referer=\"";
+               content += referer.replace(/&/g, "&amp;") + "\" timestamp=\"";
+               content += loggingEvent.getFormattedTimestamp() + "\">\n";
+               content += "\t<log4js:message><![CDATA[" + this.escapeCdata(loggingEvent.message) + "]]></log4js:message>\n";
+
+               if (loggingEvent.exception) {
+                       content += this.formatException(loggingEvent.exception);
+               }
+               content += "</log4js:event>\n";
+
+               return content;
+       },
+       /**
+        * Returns the content type output by this layout.
+        * @return The base class returns "text/xml".
+        * @type String
+        */
+       getContentType: function () {
+               return "text/xml";
+       },
+       /**
+        * @return Returns the header for the layout format. The base class returns null.
+        * @type String
+        */
+       getHeader: function () {
+               return "<log4js:eventSet version=\"" + Log4js.version +
+                               "\" xmlns:log4js=\"http://log4js.berlios.de/2007/log4js/\">\n";
+       },
+       /**
+        * @return Returns the footer for the layout format. The base class returns null.
+        * @type String
+        */
+       getFooter: function () {
+               return "</log4js:eventSet>\n";
+       },
+       getSeparator: function () {
+               return "\n";
+       },
+       /**
+        * better readable formatted Exceptions.
+        * @param ex {Exception} the exception to be formatted.
+        * @return {String} the formatted String representation of the exception.
+        * @private
+        */
+       formatException: function (ex) {
+               if (ex) {
+                       var exStr = "\t<log4js:throwable>";
+                       if (ex.message) {
+                               exStr += "\t\t<log4js:message><![CDATA[" + this.escapeCdata(ex.message) + "]]></log4js:message>\n";
+                       }
+                       if (ex.description) {
+                               exStr += "\t\t<log4js:description><![CDATA[" + this.escapeCdata(ex.description) + "]]></log4js:description>\n";
+                       }
+
+                       exStr += "\t\t<log4js:stacktrace>";
+                       exStr += "\t\t\t<log4js:location fileName=\"" + ex.fileName + "\" lineNumber=\"" + ex.lineNumber + "\" />";
+                       exStr += "\t\t</log4js:stacktrace>";
+                       exStr = "\t</log4js:throwable>";
+                       return exStr;
+               }
+               return null;
+       },
+       /**
+        * Escape Cdata messages
+        * @param str {String} message to escape
+        * @return {String} the escaped message
+        * @private
+        */
+       escapeCdata: function (str) {
+               return str.replace(/\]\]>/, "]]>]]&gt;<![CDATA[");
+       }
+});
+
+/**
+ * JSONLayout write the logs in JSON format.
+ * JSON library is required to use this Layout. See also {@link http://www.json.org}
+ * @constructor
+ * @extends Log4js.Layout
+ * @author Stephan Strittmatter
+ */
+Log4js.JSONLayout = function () {
+       this.df = new Log4js.DateFormatter();
+};
+Log4js.JSONLayout.prototype = Log4js.extend(new Log4js.Layout(), {
+       /**
+        * Implement this method to create your own layout format.
+        * @param {Log4js.LoggingEvent} loggingEvent loggingEvent to format
+        * @return formatted String
+        * @type String
+        */
+       format: function (loggingEvent) {
+
+               var useragent = "unknown";
+               try {
+                       useragent = navigator.userAgent;
+               } catch (e) {
+                       useragent = "unknown";
+               }
+
+               var referer = "unknown";
+               try {
+                       referer = location.href;
+               } catch (e) {
+                       referer = "unknown";
+               }
+
+               var jsonString = "{\n \"LoggingEvent\": {\n";
+
+               jsonString += "\t\"logger\": \"" + loggingEvent.categoryName + "\",\n";
+               jsonString += "\t\"level\": \"" + loggingEvent.level.toString() + "\",\n";
+               jsonString += "\t\"message\": \"" + loggingEvent.message + "\",\n";
+               jsonString += "\t\"referer\": \"" + referer + "\",\n";
+               jsonString += "\t\"useragent\": \"" + useragent + "\",\n";
+               jsonString += "\t\"timestamp\": \"" + this.df.formatDate(loggingEvent.startTime, "yyyy-MM-ddThh:mm:ssZ") + "\",\n";
+               jsonString += "\t\"exception\": \"" + loggingEvent.exception + "\"\n";
+               jsonString += "}}";
+
+               return jsonString;
+       },
+       /**
+        * Returns the content type output by this layout.
+        * @return The base class returns "text/xml".
+        * @type String
+        */
+       getContentType: function () {
+               return "text/json";
+       },
+       /**
+        * @return Returns the header for the layout format. The base class returns null.
+        * @type String
+        */
+       getHeader: function () {
+               return "{\"Log4js\": [\n";
+       },
+       /**
+        * @return Returns the footer for the layout format. The base class returns null.
+        * @type String
+        */
+       getFooter: function () {
+               return "\n]}";
+       },
+       getSeparator: function () {
+               return ",\n";
+       }
+});
+
+/**
+ * PatternLayout
+ */
+Log4js.PatternLayout = function (pattern) {
+       if (pattern) {
+               this.pattern = pattern;
+       } else {
+               this.pattern = Log4js.PatternLayout.DEFAULT_CONVERSION_PATTERN;
+       }
+};
+
+Log4js.PatternLayout.TTCC_CONVERSION_PATTERN = "%r %p %c - %m%n";
+Log4js.PatternLayout.DEFAULT_CONVERSION_PATTERN = "%m%n";
+Log4js.PatternLayout.ISO8601_DATEFORMAT = "yyyy-MM-dd HH:mm:ss,SSS";
+Log4js.PatternLayout.DATETIME_DATEFORMAT = "dd MMM YYYY HH:mm:ss,SSS";
+Log4js.PatternLayout.ABSOLUTETIME_DATEFORMAT = "HH:mm:ss,SSS";
+
+Log4js.PatternLayout.prototype = Log4js.extend(new Log4js.Layout(), {
+       /**
+        * Returns the content type output by this layout.
+        * @return "text/plain".
+        * @type String
+        */
+       getContentType: function () {
+               return "text/plain";
+       },
+       /**
+        * @return Returns the header for the layout format.
+        * @type String
+        */
+       getHeader: function () {
+               return null;
+       },
+       /**
+        * @return Returns the footer for the layout format.
+        * @type String
+        */
+       getFooter: function () {
+               return null;
+       },
+       format: function (loggingEvent) {
+               var regex = /%(-?[0-9]+)?(\.?[0-9]+)?([cdmnpr%])(\{([^\}]+)\})?|([^%]+)/;
+               var formattedString = "";
+               var result;
+               var searchString = this.pattern;
+
+               // Cannot use regex global flag since it doesn't work in IE5
+               while ((result = regex.exec(searchString))) {
+                       var matchedString = result[0];
+                       var padding = result[1];
+                       var truncation = result[2];
+                       var conversionCharacter = result[3];
+                       var specifier = result[5];
+                       var text = result[6];
+
+                       // Check if the pattern matched was just normal text
+                       if (text) {
+                               formattedString += "" + text;
+                       } else {
+                               // Create a raw replacement string based on the conversion
+                               // character and specifier
+                               var replacement = "";
+                               switch (conversionCharacter) {
+                                       case "c":
+                                               var loggerName = loggingEvent.categoryName;
+                                               if (specifier) {
+                                                       var precision = parseInt(specifier, 10);
+                                                       var loggerNameBits = loggingEvent.categoryName.split(".");
+                                                       if (precision >= loggerNameBits.length) {
+                                                               replacement = loggerName;
+                                                       } else {
+                                                               replacement = loggerNameBits.slice(loggerNameBits.length - precision).join(".");
+                                                       }
+                                               } else {
+                                                       replacement = loggerName;
+                                               }
+                                               break;
+                                       case "d":
+                                               var dateFormat = Log4js.PatternLayout.ISO8601_DATEFORMAT;
+                                               if (specifier) {
+                                                       dateFormat = specifier;
+                                                       // Pick up special cases
+                                                       if (dateFormat == "ISO8601") {
+                                                               dateFormat = Log4js.PatternLayout.ISO8601_DATEFORMAT;
+                                                       } else if (dateFormat == "ABSOLUTE") {
+                                                               dateFormat = Log4js.PatternLayout.ABSOLUTETIME_DATEFORMAT;
+                                                       } else if (dateFormat == "DATE") {
+                                                               dateFormat = Log4js.PatternLayout.DATETIME_DATEFORMAT;
+                                                       }
+                                               }
+                                               // Format the date
+                                               replacement = (new Log4js.SimpleDateFormat(dateFormat)).format(loggingEvent.startTime);
+                                               break;
+                                       case "m":
+                                               replacement = loggingEvent.message;
+                                               break;
+                                       case "n":
+                                               replacement = "\n";
+                                               break;
+                                       case "p":
+                                               replacement = loggingEvent.level.toString();
+                                               break;
+                                       case "r":
+                                               replacement = "" + loggingEvent.startTime.toLocaleTimeString(); //TODO: .getDifference(Log4js.applicationStartDate);
+                                               break;
+                                       case "%":
+                                               replacement = "%";
+                                               break;
+                                       default:
+                                               replacement = matchedString;
+                                               break;
+                               }
+                               // Format the replacement according to any padding or
+                               // truncation specified
+
+                               var len;
+
+                               // First, truncation
+                               if (truncation) {
+                                       len = parseInt(truncation.substr(1), 10);
+                                       replacement = replacement.substring(0, len);
+                               }
+                               // Next, padding
+                               if (padding) {
+                                       if (padding.charAt(0) == "-") {
+                                               len = parseInt(padding.substr(1), 10);
+                                               // Right pad with spaces
+                                               while (replacement.length < len) {
+                                                       replacement += " ";
+                                               }
+                                       } else {
+                                               len = parseInt(padding, 10);
+                                               // Left pad with spaces
+                                               while (replacement.length < len) {
+                                                       replacement = " " + replacement;
+                                               }
+                                       }
+                               }
+                               formattedString += replacement;
+                       }
+                       searchString = searchString.substr(result.index + result[0].length);
+               }
+               return formattedString;
+       }
+});
+
+/**
+ * @private
+ * @ignore
+ */
+if (!Array.prototype.push) {
+       /**
+        * Functions taken from Prototype library, didn't want to require for just few
+        * functions.
+        * More info at {@link http://
+        * prototype.conio.net/}
+        * @private
+        */
+       Array.prototype.push = function () {
+               var startLength = this.length;
+               for (var i = 0; i < arguments.length; i++) {
+                       this[startLength + i] = arguments[i];
+               }
+               return this.length;
+       };
+}
+
+/**
+ * FIFO buffer
+ * @private
+ */
+Log4js.FifoBuffer = function ()
+{
+       this.array = new Array();
+};
+
+Log4js.FifoBuffer.prototype = {
+       /**
+        * @param {Object} obj any object added to buffer
+        */
+       push: function (obj) {
+               this.array[this.array.length] = obj;
+               return this.array.length;
+       },
+       /**
+        * @return first putted in Object
+        */
+       pull: function () {
+               if (this.array.length > 0) {
+                       var firstItem = this.array[0];
+                       for (var i = 0; i < this.array.length - 1; i++) {
+                               this.array[i] = this.array[i + 1];
+                       }
+                       this.array.length = this.array.length - 1;
+                       return firstItem;
+               }
+               return null;
+       },
+       length: function () {
+               return this.array.length;
+       }
+};
+
+
+
+/**
+ * Date Formatter
+ * addZero() and formatDate() are courtesy of Mike Golding:
+ * http://www.mikezilla.com/exp0015.html
+ * @private
+ */
+Log4js.DateFormatter = function () {
+       return;
+};
+/**
+ * default format of date (ISO-8601)
+ * @static
+ * @final
+ */
+Log4js.DateFormatter.DEFAULT_DATE_FORMAT = "yyyy-MM-ddThh:mm:ssO";
+
+
+Log4js.DateFormatter.prototype = {
+       /**
+        * Formats the given date by the given pattern.<br />
+        * Following switches are supported:
+        * <ul>
+        * <li>yyyy: The year</li>
+        * <li>MM: the month</li>
+        * <li>dd: the day of month<li>
+        * <li>hh: the hour<li>
+        * <li>mm: minutes</li>
+        * <li>O: timezone offset</li>
+        * </ul>
+        * @param {Date} vDate the date to format
+        * @param {String} vFormat the format pattern
+        * @return {String} formatted date string
+        * @static
+        */
+       formatDate: function (vDate, vFormat) {
+               var vDay = this.addZero(vDate.getDate());
+               var vMonth = this.addZero(vDate.getMonth() + 1);
+               var vYearLong = this.addZero(vDate.getFullYear());
+               var vYearShort = this.addZero(vDate.getFullYear().toString().substring(3, 4));
+               var vYear = (vFormat.indexOf("yyyy") > -1 ? vYearLong : vYearShort);
+               var vHour = this.addZero(vDate.getHours());
+               var vMinute = this.addZero(vDate.getMinutes());
+               var vSecond = this.addZero(vDate.getSeconds());
+               var vTimeZone = this.O(vDate);
+               var vDateString = vFormat.replace(/dd/g, vDay).replace(/MM/g, vMonth).replace(/y{1,4}/g, vYear);
+               vDateString = vDateString.replace(/hh/g, vHour).replace(/mm/g, vMinute).replace(/ss/g, vSecond);
+               vDateString = vDateString.replace(/O/g, vTimeZone);
+               return vDateString;
+       },
+       /**
+        * @private
+        * @static
+        */
+       addZero: function (vNumber) {
+               return ((vNumber < 10) ? "0" : "") + vNumber;
+       },
+       /**
+        * Formates the TimeOffest
+        * Thanks to http://www.svendtofte.com/code/date_format/
+        * @private
+        */
+       O: function (date) {
+               // Difference to Greenwich time (GMT) in hours
+               var os = Math.abs(date.getTimezoneOffset());
+               var h = String(Math.floor(os / 60));
+               var m = String(os % 60);
+               h.length == 1 ? h = "0" + h : 1;
+               m.length == 1 ? m = "0" + m : 1;
+               return date.getTimezoneOffset() < 0 ? "+" + h + m : "-" + h + m;
+       }
+};
+
+
+/**
+ * internal Logger to be used
+ * @private
+ */
+var log4jsLogger = Log4js.getLogger("Log4js");
+log4jsLogger.setLevel(Log4js.Level.OFF);
\ No newline at end of file
index 63481e207a24067e975d29f8308e89ae338d2cc0..450d8a6fb6d4acc6dcedc394c7dcb26f4a9da8c3 100644 (file)
@@ -17,7 +17,6 @@
                <title>$title</title>
                <link href="$spstyle.css?junk=$junk" rel="stylesheet" type="text/css">
                <style type="text/css" media="print">*{ visibility: hidden; display: none }</style>
-               <script type="text/javascript" src="$spfluidbook.js?junk=$junk"></script>
                <script type="text/javascript">
                        FB_DEFAULT_LANG = '$lang';
                        var backgroundColor = "$bgcolor";
@@ -32,6 +31,7 @@
                                }
                        }
                </script>
+               <script type="text/javascript" src="$spfluidbook.js?junk=$junk"></script>
                $ga
                $redirectScript
        </head>
index d2313760f00e4f7f1f64539257b5f24a74ca6cd4..afac29574f0f4483c6435a89e7de149d088808cf 100644 (file)
-<?php\r
-\r
-class wsFlash extends cubeFlashGateway {\r
-\r
-       const CNAME = __CLASS__;\r
-\r
-       public static function in($args) {\r
-               global $core;\r
-               $args = cubePage::getArgs($args);\r
-               $n = self::CNAME;\r
-               $gateway = new $n($core->con, $args);\r
-\r
-               $droits = wsDroits::getDroits();\r
-               $gateway->addAttribute('creation', $droits->creation ? '1' : '0');\r
-               $gateway->addAttribute('revendeur', $droits->revendeur ? '1' : '0');\r
-               $gateway->addAttribute('admin', $droits->admin ? '1' : '0');\r
-               $gateway->addAttribute('grade', $core->user->ws_grade);\r
-       }\r
-\r
-       public function saveConversionSettings() {\r
-               global $core;\r
-               $dao = new commonDAOUtilisateur($core->con);\r
-               $datas = array('resolution' => $this->args['resolution'],\r
-                       'objects' => $this->args['objects'],\r
-                       'method' => $this->args['method'],\r
-                       'quality' => $this->args['quality'],\r
-                       'version' => $this->args['version']);\r
-               $dao->sauveWSSettings($core->user->utilisateur_id, $datas);\r
-       }\r
-\r
-       public function getConversionSettings() {\r
-               global $core;\r
-               $this->xml->addChild('resolution', $core->user->ws_settings['resolution']);\r
-               $this->xml->addChild('quality', $core->user->ws_settings['quality']);\r
-               $this->xml->addChild('method', $core->user->ws_settings['method']);\r
-               $this->xml->addChild('objects', $core->user->ws_settings['objects']);\r
-               $this->xml->addChild('version', $core->user->ws_settings['version']);\r
-       }\r
-\r
-       public function uploadDocument() {\r
-               cubePHP::neverStop();\r
-               global $core;\r
-               if (!isset($_SESSION['conversionSession'])) {\r
-                       $_SESSION['conversionSession'] = array();\r
-               }\r
-               $dao = new wsDAODocument($core->con);\r
-               foreach ($_FILES as $varname => $infos) {\r
-                       if ($infos['error']) {\r
-                               continue;\r
-                       }\r
-                       // Initialisation du document\r
-                       $data = array();\r
-                       $data['proprietaire'] = $core->user->utilisateur_id;\r
-                       $data['pages'] = 0;\r
-                       $data['version'] = 2;\r
-                       $data['file'] = cubeFiles::tidyName($infos['name']);\r
-                       $li = new wsDocumentLocalInfos($this);\r
-                       $li->fileName = $this->args['fileName'];\r
-                       $li->fileSize = $this->args['fileSize'];\r
-                       $li->creationDate = $this->args['creationDate'];\r
-                       $li->modificationDate = $this->args['modificationDate'];\r
-                       $data['localInfos'] = $li;\r
-                       $data['conversionInfos'] = new wsDocumentConversionInfos($this->args['resolution'], $this->args['method'], $this->args['quality'], $this->args['objects'], $this->args['version']);\r
-                       $document = $dao->sauve($data);\r
-                       $document->copyOriginalFromUpload($infos['tmp_name']);\r
-                       $this->xml->addChild('document_id', $document->document_id);\r
-                       $_SESSION['conversionSession'][$document->document_id] = 'all';\r
-                       $_SESSION['conversionSessionReload'] = false;\r
-               }\r
-       }\r
-\r
-       public function addDocument() {\r
-               $_SESSION['conversionSession'] = array();\r
-               $_SESSION['conversionSession'][$this->args['document_id']] = 'all';\r
-               $_SESSION['conversionSessionReload'] = false;\r
-       }\r
-\r
-       public function reload() {\r
-               global $core;\r
-\r
-               if ($this->args['reloadDocs'] == '1') {\r
-                       foreach ($this->args['documents'] as $document_id => $pages) {\r
-                               $this->args['documents'][$document_id] = 'all';\r
-                       }\r
-               }\r
-\r
-               $dao = new wsDAODocument($core->con);\r
-               $_SESSION['conversionSession'] = $this->args['documents'];\r
-               $_SESSION['conversionSessionReload'] = true;\r
-               foreach ($this->args['documents'] as $id => $pages) {\r
-                       $doc = $dao->selectById($id);\r
-                       $doc->conversionInfos->updatePages($pages, $this->args['resolution'], $this->args['quality'], $this->args['method'], $this->args['objects'], $this->args['version']);\r
-                       $dao->updateFromObject($doc);\r
-               }\r
-       }\r
-\r
-       public function processConversionSession() {\r
-               $_SESSION['sessionConversionGUID'] = $sessionConversionGUID = uniqid('conversion_', true);\r
-               $session = new wsConversionSession($sessionConversionGUID);\r
-               $session->reload = $_SESSION['conversionSessionReload'];\r
-               $session->setDocuments($_SESSION['conversionSession']);\r
-               $session->setBook($this->args['book_id']);\r
-               unset($_SESSION['conversionSession']);\r
-               unset($_SESSION['conversionSessionReload']);\r
-               $session->serialize();\r
-\r
-               $php = new cubeCommandLine('php');\r
-               $php->setPath(CONVERTER_PATH);\r
-               $php->setArg('f', ROOT . '/index.php');\r
-               $php->setManualArg('--');\r
-               $php->setArg('user_email', $_SESSION['user_email']);\r
-               $php->setArg('user_password', $_SESSION['user_password']);\r
-               $php->setArg('sessionConversionGUID', $sessionConversionGUID);\r
-               $php->setArg('PATH_INFO', '/flash/processConversionSessionAsync');\r
-               $php->setArg('HTTP_HOST', $_SERVER['HTTP_HOST']);\r
-               $php->setNohup(true);\r
-               $php->execute('exec');\r
-\r
-               fb($php->commande);\r
-               fb($php->output);\r
-\r
-               $this->xml->addChild('command', html::escapeHTML($php->command));\r
-               exit;\r
-       }\r
-\r
-       public function processConversionSessionAsync() {\r
-               cubePHP::neverStop();\r
-               try {\r
-                       $conversion = wsConversionSession::openFromGUID($_REQUEST['sessionConversionGUID']);\r
-                       $conversion->process();\r
-               } catch (Exception $e) {\r
-                       file_put_contents(CACHE . '/conversionExceptions.txt', print_r($e, true), FILE_APPEND);\r
-               }\r
-               $conversion->destroy();\r
-       }\r
-\r
-       public function getConversionSessionProgress() {\r
-               set_time_limit(1);\r
-               $session = wsConversionSession::openFromGUID($_SESSION['sessionConversionGUID']);\r
-               $p = $session->getProgress();\r
-               $this->xml->addChild('progress', $p['progress']);\r
-               $this->xml->addChild('processedDoc', $p['processedDoc']);\r
-               $this->xml->addChild('totalDoc', $p['totalDoc']);\r
-               $this->xml->addChild('currentDoc', $p['currentDoc']);\r
-               $this->xml->addChild('currentDocPage', $p['currentDocPage']);\r
-               $this->xml->addChild('totalDocPage', $p['totalDocPage']);\r
-       }\r
-\r
-       public function uploadThemeFile() {\r
-               foreach ($_FILES as $varname => $infos) {\r
-                       if ($infos['error']) {\r
-                               continue;\r
-                       }\r
-                       $fname = cubeFiles::tidyName($infos['name']);\r
-                       $dir = WS_THEMES . '/' . $this->args['theme_id'] . '/';\r
-                       $dest = $dir . $fname;\r
-\r
-\r
-                       if (!file_exists(WS_THEMES . '/' . $this->args['theme_id'])) {\r
-                               mkdir(WS_THEMES . '/' . $this->args['theme_id'], 0755, true);\r
-                       }\r
-\r
-                       move_uploaded_file($infos['tmp_name'], $dest);\r
-\r
-                       if (isset($this->args['type']) && $this->args['type'] == 'favicon') {\r
-                               $icotool = new cubeCommandLine('icotool');\r
-                               $icotool->setArg('c');\r
-                               $icotool->setArg('o', $dir . '/fluidbook.ico');\r
-\r
-                               $sizes = array(256, 128, 64, 32, 16);\r
-\r
-                               foreach ($sizes as $s) {\r
-                                       $r = $dir . '/ico-' . $s . '.png';\r
-                                       $it = new cubeImageTools();\r
-                                       $it->loadImage($dest);\r
-                                       $it->resize($s, $s, 'crop', true, 'C', 'M', 'transparent');\r
-                                       $it->output('png', $r);\r
-                                       $icotool->setArg(null, $r);\r
-                               }\r
-\r
-                               $icotool->execute();\r
-                       }\r
-\r
-                       $this->xml->addChild('file', $fname);\r
-                       return;\r
-               }\r
-       }\r
-\r
-       public function uploadLinkContent() {\r
-               foreach ($_FILES as $varname => $infos) {\r
-                       if ($infos['error']) {\r
-                               continue;\r
-                       }\r
-\r
-                       if (isset($_POST['dir'])) {\r
-                               $dir = trim($_POST['dir'], '/\\ ');\r
-                       } else {\r
-                               $dir = '';\r
-                       }\r
-\r
-                       if ($dir != '') {\r
-                               $dir = $dir . '/';\r
-                       }\r
-\r
-                       $fname = cubeFiles::tidyName($infos['name']);\r
-                       $dir = WS_BOOKS . '/working/' . $this->args['book_id'] . '/' . $dir;\r
-                       if (!file_exists($dir)) {\r
-                               mkdir($dir, 0777, true);\r
-                       }\r
-                       $dest = $dir . $fname;\r
-                       move_uploaded_file($infos['tmp_name'], $dest);\r
-                       if (in_array(strtolower(files::getExtension($dest)), array('flv', 'f4v', 'mp4'))) {\r
-                               // convert uploaded file as webvideo (ogv and mp4)\r
-                               wsTools::encodeWebVideos($dest, null, true, false);\r
-                       }\r
-                       $this->xml->addChild('file', $fname);\r
-                       return;\r
-               }\r
-       }\r
-\r
-       public function testDocuments() {\r
-               global $core;\r
-\r
-               $toload = $this->xml->addChild('toLoad');\r
-               $alreadyLoaded = $this->xml->addChild('alreadyLoaded');\r
-\r
-               $hash = array();\r
-\r
-               foreach ($this->args['fileName'] as $k => $name) {\r
-                       $o = new wsDocumentLocalInfos();\r
-                       $o->fileName = $name;\r
-                       $o->fileSize = intval($this->args['fileSize'][$k]);\r
-                       $o->modificationDate = intval($this->args['modificationDate'][$k]);\r
-                       $o->creationDate = intval($this->args['creationDate'][$k]);\r
-\r
-                       $hash[$k] = md5(serialize($o));\r
-               }\r
-\r
-               $in = array();\r
-               foreach ($hash as $h) {\r
-                       $in[] = "'" . $h . "'";\r
-               }\r
-\r
-               $sql = 'SELECT document_id,localHash FROM documents WHERE localHash';\r
-               if (count($in) == 1) {\r
-                       $sql .= ' = ' . implode(',', $in);\r
-               } else {\r
-                       $sql .= ' IN (' . implode(',', $in) . ')';\r
-               }\r
-\r
-               $al = array();\r
-               $r = $core->con->select($sql);\r
-               while ($r->fetch()) {\r
-                       $k = array_search($r->localHash, $hash);\r
-\r
-                       if (!file_exists(WS_DOCS . '/' . $r->document_id . '/original.pdf')) {\r
-                               continue;\r
-                       }\r
-\r
-                       $al[$k] = true;\r
-                       $f = $alreadyLoaded->addChild('file', $this->args['fileName'][$k]);\r
-                       $f->addAttribute('document_id', $r->document_id);\r
-               }\r
-\r
-               foreach ($this->args['fileName'] as $k => $name) {\r
-                       if (!isset($al[$k])) {\r
-                               $toload->addChild('file', $name);\r
-                       }\r
-               }\r
-       }\r
-\r
-       public function getPagesOfBook() {\r
-               global $core;\r
-\r
-               $this->json = true;\r
-\r
-               $dao = new wsDAOBook($core->con);\r
-               $book = $dao->selectById($this->args['book_id']);\r
-               $pages = $dao->getPagesOfBook($this->args['book_id'], false);\r
-               $this->jsonDatas['pages'] = array();\r
-               $docs = array();\r
-               $defaultNum = array();\r
-               foreach ($pages as $page => $info) {\r
-                       $file = WS_DOCS . '/' . $info['document_id'] . '/p' . $info['document_page'] . '.jpg';\r
-                       if (!file_exists($file)) {\r
-                               $info['resolution'] = 150;\r
-                               $info['method'] = 1;\r
-                               $info['objects'] = 1800;\r
-                               $info['quality'] = 85;\r
-                               $info['version'] = 'stable';\r
-                       } elseif (!isset($this->jsonDatas['thumbWidth'])) {\r
-                               $dim = getimagesize($file);\r
-                               $this->jsonDatas['thumbWidth'] = $dim[0];\r
-                               $this->jsonDatas['thumbHeight'] = $dim[1];\r
-                       }\r
-\r
-                       $docs[] = $info['document_id'];\r
-                       $defaultNum[] = $info['defaultNum'];\r
-\r
-                       $this->jsonDatas['pages'][] = array($info['document_id'], $info['document_page']);\r
-               }\r
-               $docs = array_unique($docs);\r
-               $this->jsonDatas['documents'] = array_unique($docs);\r
-\r
-               if (is_null($book->numerotation) || !$book->numerotation || $book->numerotation == 'null') {\r
-                       $this->jsonDatas['numerotation'] = $defaultNum;\r
-               } else {\r
-                       $this->jsonDatas['numerotation'] = explode(',', $book->numerotation);\r
-               }\r
-       }\r
-\r
-       public function getTheme() {\r
-               global $core;\r
-               $dao = new wsDAOTheme($core->con);\r
-               if (isset($this->args['theme_id'])) {\r
-                       $theme = $dao->selectById($this->args['theme_id']);\r
-               } else {\r
-                       $theme = $dao->getThemeOfBook($this->args['book_id']);\r
-               }\r
-               $this->_themeToXML($theme);\r
-       }\r
-\r
-       public function saveTheme() {\r
-               global $core;\r
-               $dao = new wsDAOTheme($core->con);\r
-               $dao->sauve($this->args);\r
-       }\r
-\r
-       public function getAllThemes() {\r
-               global $core;\r
-\r
-               if (isset($this->args['book_id'])) {\r
-                       $dao = new wsDAOBook($core->con);\r
-                       $book = $dao->selectById($this->args['book_id']);\r
-               } else {\r
-                       $this->args['book_id'] = 0;\r
-               }\r
-\r
-               $demos = array(1161, 1160);\r
-               $readOnly = array(1);\r
-\r
-               $dao = new wsDAOTheme($core->con);\r
-               $themes = $dao->getAllThemes($core->user, 'ORDER BY FIELD (theme_id,' . $this->args['book_id'] . ',' . implode(',', $demos) . ') DESC, theme_id DESC', $demos);\r
-               foreach ($themes as $theme) {\r
-                       $t = $this->xml->addChild('theme');\r
-                       $t->addAttribute('id', $theme->theme_id);\r
-                       $t->addAttribute('nom', $theme->nom);\r
-                       if (isset($book) && $theme->theme_id == $book->theme) {\r
-                               $t->addAttribute('selected', '1');\r
-                               $mine = true;\r
-                       } else {\r
-                               $mine = false;\r
-                       }\r
-                       if (in_array($theme->theme_id, $demos)) {\r
-                               $right = 'r';\r
-                       } elseif (wsDroits::admin()) {\r
-                               $right = 'w';\r
-                       } elseif (in_array($theme->theme_id, $readOnly)) {\r
-                               $right = 'r';\r
-                       } elseif (wsDroits::revendeur() && wsDroits::creation()) {\r
-                               $right = 'w';\r
-                       } else {\r
-                               $right = 'r';\r
-                       }\r
-\r
-\r
-\r
-                       $t->addAttribute('right', $right);\r
-                       $t->addAttribute('proprietaire', $theme->proprietaire_nom);\r
-                       $t->addAttribute('books', max(($theme->nbBooks) - ($mine ? 1 : 0), 0));\r
-               }\r
-       }\r
-\r
-       public function deleteTheme() {\r
-               global $core;\r
-\r
-               $dao = new wsDAOTheme($core->con);\r
-               $dao->delete($this->args['theme_id']);\r
-       }\r
-\r
-       public function renameTheme() {\r
-               global $core;\r
-\r
-               $dao = new wsDAOTheme($core->con);\r
-               $dao->rename($this->args['theme_id'], $this->args['text']);\r
-       }\r
-\r
-       public function duplicateTheme() {\r
-               global $core;\r
-               $dao = new wsDAOTheme($core->con);\r
-               $theme = $dao->duplicate($this->args['theme_id'], $core->user->utilisateur_id);\r
-               $this->xml->addChild('theme_id', $theme->theme_id);\r
-               if (isset($this->args['book_id'])) {\r
-                       $dao = new wsDAOBook($core->con);\r
-                       $data = array('book_id' => $this->args['book_id'], 'theme' => $theme->theme_id);\r
-                       $dao->sauve($core->user->utilisateur_id, $data);\r
-               }\r
-       }\r
-\r
-       public function setTheme() {\r
-               global $core;\r
-               $dao = new wsDAOBook($core->con);\r
-               $dao->setTheme($this->args['book_id'], $this->args['theme']);\r
-       }\r
-\r
-       public function postThemeShot() {\r
-               file_put_contents(WS_THEMES . '/' . $this->args['theme_id'] . '.jpg', base64_decode($this->args['data']));\r
-       }\r
-\r
-       public function getAllIcones() {\r
-               global $core;\r
-               $dao = new wsDAOIcone($core->con);\r
-               $icones = $dao->selectAll('ORDER BY icone_id DESC');\r
-               foreach ($icones as $icone) {\r
-                       $i = $this->xml->addChild('icone');\r
-                       $i->addAttribute('id', $icone->icone_id);\r
-                       $i->addAttribute('nom', $icone->nom);\r
-                       $i->addAttribute('path', ICONS . $icone->icone_id . '.png');\r
-               }\r
-       }\r
-\r
-       protected function _themeToXML($theme) {\r
-               $t = $this->xml->addChild('theme');\r
-               $t->addAttribute('theme_id', $theme->theme_id);\r
-               $t->addAttribute('icones_id', $theme->icones);\r
-               foreach ($theme->parametres as $k => $v) {\r
-                       $t->addChild($k, $v);\r
-               }\r
-       }\r
-\r
-       public function getThemeForms() {\r
-               global $core;\r
-               $dao = new wsDAOTheme($core->con);\r
-               $theme = $dao->selectById($this->args['theme_id']);\r
-\r
-               foreach ($theme->parametres->getForms() as $name) {\r
-                       $f = $this->xml->addChild('form', json_encode($theme->parametres->getForm($name)));\r
-                       $f->addAttribute('name', $name);\r
-               }\r
-       }\r
-\r
-       public function getFluidbookForms() {\r
-               global $core;\r
-               $dao = new wsDAOBook($core->con);\r
-               $book = $dao->selectById($this->args['book_id']);\r
-               foreach ($book->parametres->getForms() as $name) {\r
-                       $f = $this->xml->addChild('form', html::escapeHTML(json_encode($book->parametres->getForm($name))));\r
-                       $f->addAttribute('name', $name);\r
-               }\r
-       }\r
-\r
-       public function saveSettings() {\r
-               global $core;\r
-               $dao = new wsDAOBook($core->con);\r
-\r
-               $pages = $dao->getPagesOfBook($this->args['book_id']);\r
-               $nb_pages = count($pages);\r
-\r
-               $daoDoc = new wsDAODocument($core->con);\r
-               $firstDoc = $daoDoc->selectById($pages[1]['document_id']);\r
-               $size = $firstDoc->generalInfos['size'];\r
-\r
-               $settings = json_decode($this->args['settings'], false);\r
-               $settings->width=$size[0];\r
-               $settings->height=$size[1];\r
-\r
-               $dao->setSettings($this->args['book_id'], $settings);\r
-       }\r
-\r
-       public function setChapters() {\r
-               global $core;\r
-               $dao = new wsDAOBook($core->con);\r
-               $dao->setChapters($this->args['book_id'], $this->args['chapters']);\r
-       }\r
-\r
-       public function getChapters() {\r
-               global $core;\r
-               $dao = new wsDAOBook($core->con);\r
-               $book = $dao->selectById($this->args['book_id']);\r
-               $this->xml->addChild('chapters', html::escapeHTML(json_encode($book->chapters)));\r
-       }\r
-\r
-       public function getLinks() {\r
-               global $core;\r
-\r
-               set_time_limit(0);\r
-\r
-               $dao = new wsDAOBook($core->con);\r
-               $book = $dao->selectById($this->args['book_id']);\r
-\r
-\r
-               $pages = $dao->getPagesOfBook($this->args['book_id']);\r
-               $nb_pages = count($pages);\r
-\r
-               $daoDoc = new wsDAODocument($core->con);\r
-               $firstDoc = $daoDoc->selectById($pages[1]['document_id']);\r
-               $size = $firstDoc->generalInfos['size'];\r
-               $daoDoc->getLinksAndRulers($this->args['book_id'], $links, $rulers);\r
-\r
-               $this->json = false;\r
-               $this->json = true;\r
-\r
-               $types = array(1, 2, 3, 4, 5, 6, 7, 11, 13, 14, 15, 16, 17);\r
-               if (wsDroits::revendeur()) {\r
-                       $types = array(1, 2, 3, 4, 5, 6, 7, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21);\r
-               }\r
-               if (wsDroits::admin()) {\r
-                       $types = range(1, 30);\r
-               }\r
-\r
-               if ($this->json) {\r
-                       $this->jsonDatas['types'] = $types;\r
-                       $this->jsonDatas['pages'] = $nb_pages;\r
-                       $this->jsonDatas['numerotation'] = $book->numerotation;\r
-                       $this->jsonDatas['width'] = $size[0];\r
-                       $this->jsonDatas['height'] = $size[1];\r
-                       $this->jsonDatas['links'] = $links;\r
-                       $this->jsonDatas['rulers'] = $rulers;\r
-               } else {\r
-                       $l = json_encode($links);\r
-                       $l = html::escapeHTML($l);\r
-                       $this->xml->addChild('width', $size[0]);\r
-                       $this->xml->addChild('height', $size[1]);\r
-                       $this->xml->addChild('pages', $nb_pages);\r
-                       $this->xml->addChild('numerotation', $book->numerotation);\r
-                       $this->xml->addChild('links', $l);\r
-                       $this->xml->addChild('rulers', json_encode($rulers));\r
-               }\r
-\r
-               $daoTheme = new wsDAOTheme($this->con);\r
-               $theme = $daoTheme->getThemeOfBook($this->args['book_id'], true);\r
-\r
-               $specials = array('backgroundImage' => 'background', 'topBar' => 'topbar', 'afterSearch' => 'aftersearch', 'externalArchives' => 'archives');\r
-\r
-               foreach ($specials as $tparam => $sname) {\r
-                       $this->_addSpecialInfos($book, $theme, $tparam, $sname);\r
-               }\r
-       }\r
-\r
-       protected function _addSpecialInfos($book, $theme, $param, $specialName) {\r
-               if ($specialName == 'archives') {\r
-                       $p = $book->parametres;\r
-               } else {\r
-                       $p = $theme->parametres;\r
-               }\r
-\r
-\r
-               if ($p->$param != '') {\r
-                       if ($specialName != 'archives') {\r
-                               $themeRoot = WS_THEMES . '/' . $theme->theme_id . '/';\r
-                               $dim = getimagesize($themeRoot . $p->$param);\r
-                               $url = 'http://' . $_SERVER['HTTP_HOST'] . WEBROOT . '/fluidbook/themes/' . $theme->theme_id . '/' . $p->$param;\r
-                       } else {\r
-                               $bookRoot = WS_BOOKS . '/working/' . $book->book_id . '/';\r
-                               $dim = getimagesize($bookRoot . $p->$param);\r
-                               $url = 'http://' . $_SERVER['HTTP_HOST'] . WEBROOT . '/fluidbook/books/working/' . $book->book_id . '/' . $p->$param;\r
-                       }\r
-\r
-                       if ($this->json) {\r
-                               $p = array('width' => $dim[0], 'height' => $dim[1], 'url' => $url);\r
-                               $this->jsonDatas[$specialName] = $p;\r
-                       } else {\r
-                               $b = $this->xml->addChild($specialName);\r
-                               $b->addChild('width', $dim[0]);\r
-                               $b->addChild('height', $dim[1]);\r
-                               $b->addChild('url', $url);\r
-                       }\r
-               }\r
-       }\r
-\r
-       public function saveLinks() {\r
-               global $core;\r
-\r
-               $comments = isset($this->args['comments']) ? $this->args['comments'] : 'Saved from editor';\r
-\r
-               $dao = new wsDAODocument($core->con);\r
-               $dao->setLinksAndRulers($this->args['book_id'], $this->args['links'], $this->args['rulers'], $comments, $core->user->utilisateur_id);\r
-\r
-               $daoBook = new wsDAOBook($core->con);\r
-               $daoBook->setSpecialLinksAndRulers($this->args['book_id'], $this->args['specialLinks'], $this->args['specialRulers']);\r
-\r
-               if (isset($this->args['getLinks'])) {\r
-                       $this->getLinks();\r
-               }\r
-       }\r
-\r
-       public function formatSize($val) {\r
-               $str = files::size($val);\r
-               $str = str_replace('.', __(','), $str);\r
-               $str = str_replace('B', __('o'), $str);\r
-               return $str;\r
-       }\r
-\r
-       public function getExtras() {\r
-               global $core;\r
-               $dao = new wsDAOBook($core->con);\r
-               $book = $dao->selectById($this->args['book_id']);\r
-               if ($book->extras != '') {\r
-                       $tidy = cubeXML::tidy('<extras>' . $book->extras . '</extras>');\r
-                       $tidy = str_replace('<extras>', '', $tidy);\r
-                       $tidy = str_replace('</extras>', '', $tidy);\r
-                       $tidy = str_replace('<?xml version="1.0"?>', '', $tidy);\r
-                       $tidy = trim($tidy);\r
-                       $e = explode("\n", $tidy);\r
-                       foreach ($e as $k => $v) {\r
-                               if (substr($v, 0, 2) == '  ') {\r
-                                       $v = substr($v, 2);\r
-                               }\r
-                               $e[$k] = $v;\r
-                       }\r
-\r
-                       $extras = implode("\n", $e);\r
-               } else {\r
-                       $extras = '';\r
-               }\r
-\r
-               $this->xml->addChild('extras', $extras);\r
-       }\r
-\r
-       public function saveExtras() {\r
-               global $core;\r
-               $dao = new wsDAOBook($core->con);\r
-               $res = $dao->setExtras($this->args['book_id'], $this->args['extras']);\r
-               $this->xml->addChild('ok', $res ? '1' : '0');\r
-       }\r
-\r
-       public function getBookInfos() {\r
-               global $core;\r
-               $dao = new wsDAOBook($core->con);\r
-               $book = $dao->selectById($this->args['book_id']);\r
-               $pages = $dao->getPagesOfBook($this->args['book_id'], false);\r
-               $daoDoc = new wsDAODocument($core->con);\r
-               $nb_pages = count($pages);\r
-               $this->xml->addChild('pages', $nb_pages);\r
-               $total_size = 0;\r
-               foreach ($pages as $page => $info) {\r
-                       $file = WS_DOCS . '/' . $info['document_id'] . '/p' . $info['document_page'] . '.swf';\r
-                       $total_size += filesize($file);\r
-               }\r
-               $average_size = $total_size / $nb_pages;\r
-               $total = self::formatSize($total_size);\r
-               $average = self::formatSize($average_size);\r
-               $firstDoc = $daoDoc->selectById($pages[1]['document_id']);\r
-               $size = $firstDoc->generalInfos['size'];\r
-               $this->xml->addChild('width', $size[0]);\r
-               $this->xml->addChild('height', $size[1]);\r
-               $res = '<b>' . __('Nombre de pages') . '</b> : ';\r
-               $res .= $nb_pages . ' ' . __('pages') . "\n";\r
-               $res .= "\n";\r
-               $res .= '<b>' . __('Dimensions') . '</b> : ' . "\n";\r
-               $res .= round($size[0], 3) . ' x ' . round($size[1], 3) . ' pts' . "\n";\r
-               $res .= "\n";\r
-               $res .= '<b>' . __('Taille totale des pages') . '</b> : ' . "\n";\r
-               $res .= $total . "\n";\r
-               $res .= "\n";\r
-               $res .= '<b>' . __('Taille moyenne des pages') . '</b> : ' . "\n";\r
-               $res .= $average . "\n";\r
-               $res .= "\n";\r
-               $this->xml->addChild('infos', $res);\r
-       }\r
-\r
-       public function getLangs() {\r
-               global $core;\r
-               if (isset($this->args['book_id'])) {\r
-                       $dao = new wsDAOBook($core->con);\r
-                       $book = $dao->selectById($this->args['book_id']);\r
-                       $book->traductions = wsLang::checkTranslations($book->traductions);\r
-                       if ($book->traductions != array()) {\r
-                               //$bookLang = $this->xml->addChild('book_lang', json_encode($book->traductions));\r
-                               $bookLang = (array) $book->traductions;\r
-                       }\r
-               }\r
-\r
-               if (isset($bookLang)) {\r
-                       if (wsLang::compare($bookLang, $book->lang)) {\r
-                               unset($bookLang);\r
-                       }\r
-               }\r
-\r
-               $dao = new wsDAOLang($core->con);\r
-               $langs = $dao->selectAll();\r
-               foreach ($langs as $lang) {\r
-                       $nom = cubeLang::getNameByCode($lang->lang_id, $core->user->lang);\r
-                       $l = $this->xml->addChild('lang', json_encode($lang->traductions));\r
-                       $l->addAttribute('id', $lang->lang_id);\r
-                       $l->addAttribute('nom', $nom);\r
-                       if (isset($book) && $book->lang == $lang->lang_id) {\r
-                               if (!isset($bookLang)) {\r
-                                       $l->addAttribute('selected', '1');\r
-                               } else {\r
-                                       $trad = array_merge($lang->traductions, $bookLang);\r
-                                       $bl = $this->xml->addChild('book_lang', json_encode($trad));\r
-                                       $bl->addAttribute('id', $lang->lang_id);\r
-                                       $bl->addAttribute('nom', $nom . ' (' . __('modifié') . ')');\r
-                                       $bl->addAttribute('selected', '1');\r
-                               }\r
-                       }\r
-               }\r
-       }\r
-\r
-       public function saveLang() {\r
-               global $core;\r
-               $dao = new wsDAOBook($core->con);\r
-               $dao->setLang($this->args['book_id'], $this->args['lang_id'], $this->args['traductions']);\r
-       }\r
-\r
-       public function saveComposition() {\r
-               global $core;\r
-               $dao = new wsDAOBook($core->con);\r
-               $dao->setComposition($this->args['book_id'], json_decode($this->args['pages']));\r
-       }\r
-\r
-       public function getTexts() {\r
-               $this->xml->addChild('texts', json_encode($GLOBALS['__l10n']));\r
-       }\r
-\r
-       public function copyLinks() {\r
-               global $core;\r
-               $daoDocument = new wsDAODocument($core->con);\r
-               $daoDocument->copyLinks($this->args['fromDoc'], $this->args['toDoc']);\r
-       }\r
-\r
-       public function compile() {\r
-               global $core;\r
-               wsSecureSWF::checkProtectedSWF();\r
-               $dao = new wsDAOBook($core->con);\r
-\r
-               $log = $dao->compile($this->args['book_id'], '2');\r
-\r
-\r
-               $book = $dao->selectById($this->args['book_id']);\r
-               $viewer = 'viewer';\r
-               if (wsDroits::admin()) {\r
-                       $viewer = 'vieweru';\r
-               }\r
-\r
-               $absoluteURL = 'http://' . $_SERVER['HTTP_HOST'] . '/' . $viewer . '/' . $book->book_id . '_' . $book->hash . '_' . TIME . '/';\r
-               $this->xml->addChild('compiledBook', $absoluteURL . 'index.swf?base=' . $absoluteURL);\r
-       }\r
-\r
-}\r
-\r
+<?php
+
+class wsFlash extends cubeFlashGateway {
+
+       const CNAME = __CLASS__;
+
+       public static function in($args) {
+               global $core;
+               $args = cubePage::getArgs($args);
+               $n = self::CNAME;
+               $gateway = new $n($core->con, $args);
+
+               $droits = wsDroits::getDroits();
+               $gateway->addAttribute('creation', $droits->creation ? '1' : '0');
+               $gateway->addAttribute('revendeur', $droits->revendeur ? '1' : '0');
+               $gateway->addAttribute('admin', $droits->admin ? '1' : '0');
+               $gateway->addAttribute('grade', $core->user->ws_grade);
+       }
+
+       public function saveConversionSettings() {
+               global $core;
+               $dao = new commonDAOUtilisateur($core->con);
+               $datas = array('resolution' => $this->args['resolution'],
+                       'objects' => $this->args['objects'],
+                       'method' => $this->args['method'],
+                       'quality' => $this->args['quality'],
+                       'version' => $this->args['version']);
+               $dao->sauveWSSettings($core->user->utilisateur_id, $datas);
+       }
+
+       public function getConversionSettings() {
+               global $core;
+               $this->xml->addChild('resolution', $core->user->ws_settings['resolution']);
+               $this->xml->addChild('quality', $core->user->ws_settings['quality']);
+               $this->xml->addChild('method', $core->user->ws_settings['method']);
+               $this->xml->addChild('objects', $core->user->ws_settings['objects']);
+               $this->xml->addChild('version', $core->user->ws_settings['version']);
+       }
+
+       public function uploadDocument() {
+               cubePHP::neverStop();
+               global $core;
+               if (!isset($_SESSION['conversionSession'])) {
+                       $_SESSION['conversionSession'] = array();
+               }
+               $dao = new wsDAODocument($core->con);
+               foreach ($_FILES as $varname => $infos) {
+                       if ($infos['error']) {
+                               continue;
+                       }
+                       // Initialisation du document
+                       $data = array();
+                       $data['proprietaire'] = $core->user->utilisateur_id;
+                       $data['pages'] = 0;
+                       $data['version'] = 2;
+                       $data['file'] = cubeFiles::tidyName($infos['name']);
+                       $li = new wsDocumentLocalInfos($this);
+                       $li->fileName = $this->args['fileName'];
+                       $li->fileSize = $this->args['fileSize'];
+                       $li->creationDate = $this->args['creationDate'];
+                       $li->modificationDate = $this->args['modificationDate'];
+                       $data['localInfos'] = $li;
+                       $data['conversionInfos'] = new wsDocumentConversionInfos($this->args['resolution'], $this->args['method'], $this->args['quality'], $this->args['objects'], $this->args['version']);
+                       $document = $dao->sauve($data);
+                       $document->copyOriginalFromUpload($infos['tmp_name']);
+                       $this->xml->addChild('document_id', $document->document_id);
+                       $_SESSION['conversionSession'][$document->document_id] = 'all';
+                       $_SESSION['conversionSessionReload'] = false;
+               }
+       }
+
+       public function addDocument() {
+               $_SESSION['conversionSession'] = array();
+               $_SESSION['conversionSession'][$this->args['document_id']] = 'all';
+               $_SESSION['conversionSessionReload'] = false;
+       }
+
+       public function reload() {
+               global $core;
+
+               if ($this->args['reloadDocs'] == '1') {
+                       foreach ($this->args['documents'] as $document_id => $pages) {
+                               $this->args['documents'][$document_id] = 'all';
+                       }
+               }
+
+               $dao = new wsDAODocument($core->con);
+               $_SESSION['conversionSession'] = $this->args['documents'];
+               $_SESSION['conversionSessionReload'] = true;
+               foreach ($this->args['documents'] as $id => $pages) {
+                       $doc = $dao->selectById($id);
+                       $doc->conversionInfos->updatePages($pages, $this->args['resolution'], $this->args['quality'], $this->args['method'], $this->args['objects'], $this->args['version']);
+                       $dao->updateFromObject($doc);
+               }
+       }
+
+       public function processConversionSession() {
+               $_SESSION['sessionConversionGUID'] = $sessionConversionGUID = uniqid('conversion_', true);
+               $session = new wsConversionSession($sessionConversionGUID);
+               $session->reload = $_SESSION['conversionSessionReload'];
+               $session->setDocuments($_SESSION['conversionSession']);
+               $session->setBook($this->args['book_id']);
+               unset($_SESSION['conversionSession']);
+               unset($_SESSION['conversionSessionReload']);
+               $session->serialize();
+
+               $php = new cubeCommandLine('php');
+               $php->setPath(CONVERTER_PATH);
+               $php->setArg('f', ROOT . '/index.php');
+               $php->setManualArg('--');
+               $php->setArg('user_email', $_SESSION['user_email']);
+               $php->setArg('user_password', $_SESSION['user_password']);
+               $php->setArg('sessionConversionGUID', $sessionConversionGUID);
+               $php->setArg('PATH_INFO', '/flash/processConversionSessionAsync');
+               $php->setArg('HTTP_HOST', $_SERVER['HTTP_HOST']);
+               $php->setNohup(true);
+               $php->execute('exec');
+
+               fb($php->commande);
+               fb($php->output);
+
+               $this->xml->addChild('command', html::escapeHTML($php->command));
+               exit;
+       }
+
+       public function processConversionSessionAsync() {
+               cubePHP::neverStop();
+               try {
+                       $conversion = wsConversionSession::openFromGUID($_REQUEST['sessionConversionGUID']);
+                       $conversion->process();
+               } catch (Exception $e) {
+                       file_put_contents(CACHE . '/conversionExceptions.txt', print_r($e, true), FILE_APPEND);
+               }
+               $conversion->destroy();
+       }
+
+       public function getConversionSessionProgress() {
+               set_time_limit(1);
+               $session = wsConversionSession::openFromGUID($_SESSION['sessionConversionGUID']);
+               $p = $session->getProgress();
+               $this->xml->addChild('progress', $p['progress']);
+               $this->xml->addChild('processedDoc', $p['processedDoc']);
+               $this->xml->addChild('totalDoc', $p['totalDoc']);
+               $this->xml->addChild('currentDoc', $p['currentDoc']);
+               $this->xml->addChild('currentDocPage', $p['currentDocPage']);
+               $this->xml->addChild('totalDocPage', $p['totalDocPage']);
+       }
+
+       public function uploadThemeFile() {
+               foreach ($_FILES as $varname => $infos) {
+                       if ($infos['error']) {
+                               continue;
+                       }
+                       $fname = cubeFiles::tidyName($infos['name']);
+                       $dir = WS_THEMES . '/' . $this->args['theme_id'] . '/';
+                       $dest = $dir . $fname;
+
+
+                       if (!file_exists(WS_THEMES . '/' . $this->args['theme_id'])) {
+                               mkdir(WS_THEMES . '/' . $this->args['theme_id'], 0755, true);
+                       }
+
+                       move_uploaded_file($infos['tmp_name'], $dest);
+
+                       if (isset($this->args['type']) && $this->args['type'] == 'favicon') {
+                               $icotool = new cubeCommandLine('icotool');
+                               $icotool->setArg('c');
+                               $icotool->setArg('o', $dir . '/fluidbook.ico');
+
+                               $sizes = array(256, 128, 64, 32, 16);
+
+                               foreach ($sizes as $s) {
+                                       $r = $dir . '/ico-' . $s . '.png';
+                                       $it = new cubeImageTools();
+                                       $it->loadImage($dest);
+                                       $it->resize($s, $s, 'crop', true, 'C', 'M', 'transparent');
+                                       $it->output('png', $r);
+                                       $icotool->setArg(null, $r);
+                               }
+
+                               $icotool->execute();
+                       }
+
+                       $this->xml->addChild('file', $fname);
+                       return;
+               }
+       }
+
+       public function uploadLinkContent() {
+               foreach ($_FILES as $varname => $infos) {
+                       if ($infos['error']) {
+                               continue;
+                       }
+
+                       if (isset($_POST['dir'])) {
+                               $dir = trim($_POST['dir'], '/\\ ');
+                       } else {
+                               $dir = '';
+                       }
+
+                       if ($dir != '') {
+                               $dir = $dir . '/';
+                       }
+
+                       $fname = cubeFiles::tidyName($infos['name']);
+                       $dir = WS_BOOKS . '/working/' . $this->args['book_id'] . '/' . $dir;
+                       if (!file_exists($dir)) {
+                               mkdir($dir, 0777, true);
+                       }
+                       $dest = $dir . $fname;
+                       move_uploaded_file($infos['tmp_name'], $dest);
+                       if (in_array(strtolower(files::getExtension($dest)), array('flv', 'f4v', 'mp4'))) {
+                               // convert uploaded file as webvideo (ogv and mp4)
+                               wsTools::encodeWebVideos($dest, null, true, false);
+                       }
+                       $this->xml->addChild('file', $fname);
+                       return;
+               }
+       }
+
+       public function testDocuments() {
+               global $core;
+
+               $toload = $this->xml->addChild('toLoad');
+               $alreadyLoaded = $this->xml->addChild('alreadyLoaded');
+
+               $hash = array();
+
+               foreach ($this->args['fileName'] as $k => $name) {
+                       $o = new wsDocumentLocalInfos();
+                       $o->fileName = $name;
+                       $o->fileSize = intval($this->args['fileSize'][$k]);
+                       $o->modificationDate = intval($this->args['modificationDate'][$k]);
+                       $o->creationDate = intval($this->args['creationDate'][$k]);
+
+                       $hash[$k] = md5(serialize($o));
+               }
+
+               $in = array();
+               foreach ($hash as $h) {
+                       $in[] = "'" . $h . "'";
+               }
+
+               $sql = 'SELECT document_id,localHash FROM documents WHERE localHash';
+               if (count($in) == 1) {
+                       $sql .= ' = ' . implode(',', $in);
+               } else {
+                       $sql .= ' IN (' . implode(',', $in) . ')';
+               }
+
+               $al = array();
+               $r = $core->con->select($sql);
+               while ($r->fetch()) {
+                       $k = array_search($r->localHash, $hash);
+
+                       if (!file_exists(WS_DOCS . '/' . $r->document_id . '/crop.pdf')) {
+                               continue;
+                       }
+
+                       $al[$k] = true;
+                       $f = $alreadyLoaded->addChild('file', $this->args['fileName'][$k]);
+                       $f->addAttribute('document_id', $r->document_id);
+               }
+
+               foreach ($this->args['fileName'] as $k => $name) {
+                       if (!isset($al[$k])) {
+                               $toload->addChild('file', $name);
+                       }
+               }
+       }
+
+       public function getPagesOfBook() {
+               global $core;
+
+               $this->json = true;
+
+               $dao = new wsDAOBook($core->con);
+               $book = $dao->selectById($this->args['book_id']);
+               $pages = $dao->getPagesOfBook($this->args['book_id'], false);
+               $this->jsonDatas['pages'] = array();
+               $docs = array();
+               $defaultNum = array();
+               foreach ($pages as $page => $info) {
+                       $file = WS_DOCS . '/' . $info['document_id'] . '/p' . $info['document_page'] . '.jpg';
+                       if (!file_exists($file)) {
+                               $info['resolution'] = 150;
+                               $info['method'] = 1;
+                               $info['objects'] = 1800;
+                               $info['quality'] = 85;
+                               $info['version'] = 'stable';
+                       } elseif (!isset($this->jsonDatas['thumbWidth'])) {
+                               $dim = getimagesize($file);
+                               $this->jsonDatas['thumbWidth'] = $dim[0];
+                               $this->jsonDatas['thumbHeight'] = $dim[1];
+                       }
+
+                       $docs[] = $info['document_id'];
+                       $defaultNum[] = $info['defaultNum'];
+
+                       $this->jsonDatas['pages'][] = array($info['document_id'], $info['document_page']);
+               }
+               $docs = array_unique($docs);
+               $this->jsonDatas['documents'] = array_unique($docs);
+
+               if (is_null($book->numerotation) || !$book->numerotation || $book->numerotation == 'null') {
+                       $this->jsonDatas['numerotation'] = $defaultNum;
+               } else {
+                       $this->jsonDatas['numerotation'] = explode(',', $book->numerotation);
+               }
+       }
+
+       public function getTheme() {
+               global $core;
+               $dao = new wsDAOTheme($core->con);
+               if (isset($this->args['theme_id'])) {
+                       $theme = $dao->selectById($this->args['theme_id']);
+               } else {
+                       $theme = $dao->getThemeOfBook($this->args['book_id']);
+               }
+               $this->_themeToXML($theme);
+       }
+
+       public function saveTheme() {
+               global $core;
+               $dao = new wsDAOTheme($core->con);
+               $dao->sauve($this->args);
+       }
+
+       public function getAllThemes() {
+               global $core;
+
+               if (isset($this->args['book_id'])) {
+                       $dao = new wsDAOBook($core->con);
+                       $book = $dao->selectById($this->args['book_id']);
+               } else {
+                       $this->args['book_id'] = 0;
+               }
+
+               $demos = array(1161, 1160);
+               $readOnly = array(1);
+
+               $dao = new wsDAOTheme($core->con);
+               $themes = $dao->getAllThemes($core->user, 'ORDER BY FIELD (theme_id,' . $this->args['book_id'] . ',' . implode(',', $demos) . ') DESC, theme_id DESC', $demos);
+               foreach ($themes as $theme) {
+                       $t = $this->xml->addChild('theme');
+                       $t->addAttribute('id', $theme->theme_id);
+                       $t->addAttribute('nom', $theme->nom);
+                       if (isset($book) && $theme->theme_id == $book->theme) {
+                               $t->addAttribute('selected', '1');
+                               $mine = true;
+                       } else {
+                               $mine = false;
+                       }
+                       if (in_array($theme->theme_id, $demos)) {
+                               $right = 'r';
+                       } elseif (wsDroits::admin()) {
+                               $right = 'w';
+                       } elseif (in_array($theme->theme_id, $readOnly)) {
+                               $right = 'r';
+                       } elseif (wsDroits::revendeur() && wsDroits::creation()) {
+                               $right = 'w';
+                       } else {
+                               $right = 'r';
+                       }
+
+
+
+                       $t->addAttribute('right', $right);
+                       $t->addAttribute('proprietaire', $theme->proprietaire_nom);
+                       $t->addAttribute('books', max(($theme->nbBooks) - ($mine ? 1 : 0), 0));
+               }
+       }
+
+       public function deleteTheme() {
+               global $core;
+
+               $dao = new wsDAOTheme($core->con);
+               $dao->delete($this->args['theme_id']);
+       }
+
+       public function renameTheme() {
+               global $core;
+
+               $dao = new wsDAOTheme($core->con);
+               $dao->rename($this->args['theme_id'], $this->args['text']);
+       }
+
+       public function duplicateTheme() {
+               global $core;
+               $dao = new wsDAOTheme($core->con);
+               $theme = $dao->duplicate($this->args['theme_id'], $core->user->utilisateur_id);
+               $this->xml->addChild('theme_id', $theme->theme_id);
+               if (isset($this->args['book_id'])) {
+                       $dao = new wsDAOBook($core->con);
+                       $data = array('book_id' => $this->args['book_id'], 'theme' => $theme->theme_id);
+                       $dao->sauve($core->user->utilisateur_id, $data);
+               }
+       }
+
+       public function setTheme() {
+               global $core;
+               $dao = new wsDAOBook($core->con);
+               $dao->setTheme($this->args['book_id'], $this->args['theme']);
+       }
+
+       public function postThemeShot() {
+               file_put_contents(WS_THEMES . '/' . $this->args['theme_id'] . '.jpg', base64_decode($this->args['data']));
+       }
+
+       public function getAllIcones() {
+               global $core;
+               $dao = new wsDAOIcone($core->con);
+               $icones = $dao->selectAll('ORDER BY icone_id DESC');
+               foreach ($icones as $icone) {
+                       $i = $this->xml->addChild('icone');
+                       $i->addAttribute('id', $icone->icone_id);
+                       $i->addAttribute('nom', $icone->nom);
+                       $i->addAttribute('path', ICONS . $icone->icone_id . '.png');
+               }
+       }
+
+       protected function _themeToXML($theme) {
+               $t = $this->xml->addChild('theme');
+               $t->addAttribute('theme_id', $theme->theme_id);
+               $t->addAttribute('icones_id', $theme->icones);
+               foreach ($theme->parametres as $k => $v) {
+                       $t->addChild($k, $v);
+               }
+       }
+
+       public function getThemeForms() {
+               global $core;
+               $dao = new wsDAOTheme($core->con);
+               $theme = $dao->selectById($this->args['theme_id']);
+
+               foreach ($theme->parametres->getForms() as $name) {
+                       $f = $this->xml->addChild('form', json_encode($theme->parametres->getForm($name)));
+                       $f->addAttribute('name', $name);
+               }
+       }
+
+       public function getFluidbookForms() {
+               global $core;
+               $dao = new wsDAOBook($core->con);
+               $book = $dao->selectById($this->args['book_id']);
+               foreach ($book->parametres->getForms() as $name) {
+                       $f = $this->xml->addChild('form', html::escapeHTML(json_encode($book->parametres->getForm($name))));
+                       $f->addAttribute('name', $name);
+               }
+       }
+
+       public function saveSettings() {
+               global $core;
+               $dao = new wsDAOBook($core->con);
+
+               $pages = $dao->getPagesOfBook($this->args['book_id']);
+               $nb_pages = count($pages);
+
+               $daoDoc = new wsDAODocument($core->con);
+               $firstDoc = $daoDoc->selectById($pages[1]['document_id']);
+               $size = $firstDoc->generalInfos['size'];
+
+               $settings = json_decode($this->args['settings'], false);
+               $settings->width = $size[0];
+               $settings->height = $size[1];
+
+               $dao->setSettings($this->args['book_id'], $settings);
+       }
+
+       public function setChapters() {
+               global $core;
+               $dao = new wsDAOBook($core->con);
+               $dao->setChapters($this->args['book_id'], $this->args['chapters']);
+       }
+
+       public function getChapters() {
+               global $core;
+               $dao = new wsDAOBook($core->con);
+               $book = $dao->selectById($this->args['book_id']);
+               $this->xml->addChild('chapters', html::escapeHTML(json_encode($book->chapters)));
+       }
+
+       public function getLinks() {
+               global $core;
+
+               set_time_limit(0);
+
+               $dao = new wsDAOBook($core->con);
+               $book = $dao->selectById($this->args['book_id']);
+
+
+               $pages = $dao->getPagesOfBook($this->args['book_id']);
+               $nb_pages = count($pages);
+
+               $daoDoc = new wsDAODocument($core->con);
+               $firstDoc = $daoDoc->selectById($pages[1]['document_id']);
+               $size = $firstDoc->generalInfos['size'];
+               $daoDoc->getLinksAndRulers($this->args['book_id'], $links, $rulers);
+
+               $this->json = false;
+               $this->json = true;
+
+               $types = array(1, 2, 3, 4, 5, 6, 7, 11, 13, 14, 15, 16, 17);
+               if (wsDroits::revendeur()) {
+                       $types = array(1, 2, 3, 4, 5, 6, 7, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21);
+               }
+               if (wsDroits::admin()) {
+                       $types = range(1, 30);
+               }
+
+               if ($this->json) {
+                       $this->jsonDatas['types'] = $types;
+                       $this->jsonDatas['pages'] = $nb_pages;
+                       $this->jsonDatas['numerotation'] = $book->numerotation;
+                       $this->jsonDatas['width'] = $size[0];
+                       $this->jsonDatas['height'] = $size[1];
+                       $this->jsonDatas['links'] = $links;
+                       $this->jsonDatas['rulers'] = $rulers;
+               } else {
+                       $l = json_encode($links);
+                       $l = html::escapeHTML($l);
+                       $this->xml->addChild('width', $size[0]);
+                       $this->xml->addChild('height', $size[1]);
+                       $this->xml->addChild('pages', $nb_pages);
+                       $this->xml->addChild('numerotation', $book->numerotation);
+                       $this->xml->addChild('links', $l);
+                       $this->xml->addChild('rulers', json_encode($rulers));
+               }
+
+               $daoTheme = new wsDAOTheme($this->con);
+               $theme = $daoTheme->getThemeOfBook($this->args['book_id'], true);
+
+               $specials = array('backgroundImage' => 'background', 'topBar' => 'topbar', 'afterSearch' => 'aftersearch', 'externalArchives' => 'archives');
+
+               foreach ($specials as $tparam => $sname) {
+                       $this->_addSpecialInfos($book, $theme, $tparam, $sname);
+               }
+       }
+
+       protected function _addSpecialInfos($book, $theme, $param, $specialName) {
+               if ($specialName == 'archives') {
+                       $p = $book->parametres;
+               } else {
+                       $p = $theme->parametres;
+               }
+
+
+               if ($p->$param != '') {
+                       if ($specialName != 'archives') {
+                               $themeRoot = WS_THEMES . '/' . $theme->theme_id . '/';
+                               $dim = getimagesize($themeRoot . $p->$param);
+                               $url = 'http://' . $_SERVER['HTTP_HOST'] . WEBROOT . '/fluidbook/themes/' . $theme->theme_id . '/' . $p->$param;
+                       } else {
+                               $bookRoot = WS_BOOKS . '/working/' . $book->book_id . '/';
+                               $dim = getimagesize($bookRoot . $p->$param);
+                               $url = 'http://' . $_SERVER['HTTP_HOST'] . WEBROOT . '/fluidbook/books/working/' . $book->book_id . '/' . $p->$param;
+                       }
+
+                       if ($this->json) {
+                               $p = array('width' => $dim[0], 'height' => $dim[1], 'url' => $url);
+                               $this->jsonDatas[$specialName] = $p;
+                       } else {
+                               $b = $this->xml->addChild($specialName);
+                               $b->addChild('width', $dim[0]);
+                               $b->addChild('height', $dim[1]);
+                               $b->addChild('url', $url);
+                       }
+               }
+       }
+
+       public function saveLinks() {
+               global $core;
+
+               $comments = isset($this->args['comments']) ? $this->args['comments'] : 'Saved from editor';
+
+               $dao = new wsDAODocument($core->con);
+               $dao->setLinksAndRulers($this->args['book_id'], $this->args['links'], $this->args['rulers'], $comments, $core->user->utilisateur_id);
+
+               $daoBook = new wsDAOBook($core->con);
+               $daoBook->setSpecialLinksAndRulers($this->args['book_id'], $this->args['specialLinks'], $this->args['specialRulers']);
+
+               if (isset($this->args['getLinks'])) {
+                       $this->getLinks();
+               }
+       }
+
+       public function formatSize($val) {
+               $str = files::size($val);
+               $str = str_replace('.', __(','), $str);
+               $str = str_replace('B', __('o'), $str);
+               return $str;
+       }
+
+       public function getExtras() {
+               global $core;
+               $dao = new wsDAOBook($core->con);
+               $book = $dao->selectById($this->args['book_id']);
+               if ($book->extras != '') {
+                       $tidy = cubeXML::tidy('<extras>' . $book->extras . '</extras>');
+                       $tidy = str_replace('<extras>', '', $tidy);
+                       $tidy = str_replace('</extras>', '', $tidy);
+                       $tidy = str_replace('<?xml version="1.0"?>', '', $tidy);
+                       $tidy = trim($tidy);
+                       $e = explode("\n", $tidy);
+                       foreach ($e as $k => $v) {
+                               if (substr($v, 0, 2) == '  ') {
+                                       $v = substr($v, 2);
+                               }
+                               $e[$k] = $v;
+                       }
+
+                       $extras = implode("\n", $e);
+               } else {
+                       $extras = '';
+               }
+
+               $this->xml->addChild('extras', $extras);
+       }
+
+       public function saveExtras() {
+               global $core;
+               $dao = new wsDAOBook($core->con);
+               $res = $dao->setExtras($this->args['book_id'], $this->args['extras']);
+               $this->xml->addChild('ok', $res ? '1' : '0');
+       }
+
+       public function getBookInfos() {
+               global $core;
+               $dao = new wsDAOBook($core->con);
+               $book = $dao->selectById($this->args['book_id']);
+               $pages = $dao->getPagesOfBook($this->args['book_id'], false);
+               $daoDoc = new wsDAODocument($core->con);
+               $nb_pages = count($pages);
+               $this->xml->addChild('pages', $nb_pages);
+               $total_size = 0;
+               foreach ($pages as $page => $info) {
+                       $file = WS_DOCS . '/' . $info['document_id'] . '/p' . $info['document_page'] . '.swf';
+                       $total_size += filesize($file);
+               }
+               $average_size = $total_size / $nb_pages;
+               $total = self::formatSize($total_size);
+               $average = self::formatSize($average_size);
+               $firstDoc = $daoDoc->selectById($pages[1]['document_id']);
+               $size = $firstDoc->generalInfos['size'];
+               $this->xml->addChild('width', $size[0]);
+               $this->xml->addChild('height', $size[1]);
+               $res = '<b>' . __('Nombre de pages') . '</b> : ';
+               $res .= $nb_pages . ' ' . __('pages') . "\n";
+               $res .= "\n";
+               $res .= '<b>' . __('Dimensions') . '</b> : ' . "\n";
+               $res .= round($size[0], 3) . ' x ' . round($size[1], 3) . ' pts' . "\n";
+               $res .= "\n";
+               $res .= '<b>' . __('Taille totale des pages') . '</b> : ' . "\n";
+               $res .= $total . "\n";
+               $res .= "\n";
+               $res .= '<b>' . __('Taille moyenne des pages') . '</b> : ' . "\n";
+               $res .= $average . "\n";
+               $res .= "\n";
+               $this->xml->addChild('infos', $res);
+       }
+
+       public function getLangs() {
+               global $core;
+               if (isset($this->args['book_id'])) {
+                       $dao = new wsDAOBook($core->con);
+                       $book = $dao->selectById($this->args['book_id']);
+                       $book->traductions = wsLang::checkTranslations($book->traductions);
+                       if ($book->traductions != array()) {
+                               //$bookLang = $this->xml->addChild('book_lang', json_encode($book->traductions));
+                               $bookLang = (array) $book->traductions;
+                       }
+               }
+
+               if (isset($bookLang)) {
+                       if (wsLang::compare($bookLang, $book->lang)) {
+                               unset($bookLang);
+                       }
+               }
+
+               $dao = new wsDAOLang($core->con);
+               $langs = $dao->selectAll();
+               foreach ($langs as $lang) {
+                       $nom = cubeLang::getNameByCode($lang->lang_id, $core->user->lang);
+                       $l = $this->xml->addChild('lang', json_encode($lang->traductions));
+                       $l->addAttribute('id', $lang->lang_id);
+                       $l->addAttribute('nom', $nom);
+                       if (isset($book) && $book->lang == $lang->lang_id) {
+                               if (!isset($bookLang)) {
+                                       $l->addAttribute('selected', '1');
+                               } else {
+                                       $trad = array_merge($lang->traductions, $bookLang);
+                                       $bl = $this->xml->addChild('book_lang', json_encode($trad));
+                                       $bl->addAttribute('id', $lang->lang_id);
+                                       $bl->addAttribute('nom', $nom . ' (' . __('modifié') . ')');
+                                       $bl->addAttribute('selected', '1');
+                               }
+                       }
+               }
+       }
+
+       public function saveLang() {
+               global $core;
+               $dao = new wsDAOBook($core->con);
+               $dao->setLang($this->args['book_id'], $this->args['lang_id'], $this->args['traductions']);
+       }
+
+       public function saveComposition() {
+               global $core;
+               $dao = new wsDAOBook($core->con);
+               $dao->setComposition($this->args['book_id'], json_decode($this->args['pages']));
+       }
+
+       public function getTexts() {
+               $this->xml->addChild('texts', json_encode($GLOBALS['__l10n']));
+       }
+
+       public function copyLinks() {
+               global $core;
+               $daoDocument = new wsDAODocument($core->con);
+               $daoDocument->copyLinks($this->args['fromDoc'], $this->args['toDoc']);
+       }
+
+       public function compile() {
+               global $core;
+               wsSecureSWF::checkProtectedSWF();
+               $dao = new wsDAOBook($core->con);
+
+               $log = $dao->compile($this->args['book_id'], '2');
+
+
+               $book = $dao->selectById($this->args['book_id']);
+               $viewer = 'viewer';
+               if (wsDroits::admin()) {
+                       $viewer = 'vieweru';
+               }
+
+               $absoluteURL = 'http://' . $_SERVER['HTTP_HOST'] . '/' . $viewer . '/' . $book->book_id . '_' . $book->hash . '_' . TIME . '/';
+               $this->xml->addChild('compiledBook', $absoluteURL . 'index.swf?base=' . $absoluteURL);
+       }
+
+}
+
 ?>
\ No newline at end of file
index dd09eaf1360b012bf5ca63afb013d9c0730a5954..884fa78c2d645faed199fe69b0b35948010e1f55 100644 (file)
@@ -11,6 +11,22 @@ class wsMaintenance {
                }
        }
 
+       public static function cleanOriginauxPDF() {
+               global $core;
+               cubePHP::neverStop();
+               $r = $core->con->select('SELECT document_id FROM documents');
+               while ($r->fetch()) {
+                       $root = WS_DOCS . '/' . $r->document_id . '/';
+                       $original = $root . 'original.pdf';
+                       $crop = $root . 'crop.pdf';
+                       if (file_exists($original) && file_exists($crop) && filesize($original) == filesize($crop)) {
+                               `rm $crop`;
+                               `cd $root;ln -s original.pdf crop.pdf`;
+                               echo $root . '<br />';
+                       }
+               }
+       }
+
        public static function initCompositionVersions($args) {
                global $core;
                $dao = new wsDAOBook($core->con);
@@ -882,6 +898,11 @@ class wsMaintenance {
                $dao->setLinksAndRulers($book_id, json_encode($newlinks), json_encode($rulers), 'Autobookmark links correction', $core->user->utilisateur_id);
        }
 
+       public static function compress() {
+               `gzip /home/extranet/www/fluidbook/docs/*/p*.csv`;
+               `gzip /home/extranet/www/fluidbook/docs/*/*.txt`;
+       }
+
        public static function offsetLinksPos($args) {
                global $core;
                $book_id = $args[0];
@@ -951,9 +972,7 @@ class wsMaintenance {
                        $files = array($ifilec, $tfilec);
 
                        foreach ($files as $f) {
-                               if (file_exists($f)) {
-                                       unlink($f);
-                               }
+                               CubeIT_Util_Gzip::unlink($f . '.gz');
                        }
                }
 
index 5367d69272dd01be6840d2077589f7eb3ed8db08..e68f8ec5e5812517d68b83b3fe9d4bc0e79b493c 100644 (file)
@@ -821,9 +821,9 @@ class wsDAOBook extends commonDAO {
                }
                $tfilec = $dir . '/' . $prefix . 'textes.json';
 
-               if (file_exists($ifilec) && file_exists($tfilec) && (min(filemtime($ifilec), filemtime($tfilec)) >= $book->composition_update)) {
-                       $index = file_get_contents($ifilec);
-                       $textes = file_get_contents($tfilec);
+               if (CubeIT_Util_Gzip::file_exists($ifilec) && CubeIT_Util_Gzip::file_exists($tfilec) && (min(CubeIT_Util_Gzip::filemtime($ifilec), CubeIT_Util_Gzip::filemtime($tfilec)) >= $book->composition_update)) {
+                       $index = CubeIT_Util_Gzip::file_get_contents($ifilec);
+                       $textes = CubeIT_Util_Gzip::file_get_contents($tfilec);
                        return;
                }
 
@@ -852,8 +852,10 @@ class wsDAOBook extends commonDAO {
                foreach ($pages as $book_page => $infos) {
                        $tfile = WS_DOCS . '/' . $infos['document_id'] . '/' . $prefix . 'p' . $infos['document_page'] . '.txt';
                        $ifile = WS_DOCS . '/' . $infos['document_id'] . '/' . $prefix . 'i' . $infos['document_page'] . '.txt';
-                       $text = file_get_contents($tfile);
-                       $ipage = file_get_contents($ifile);
+                       CubeIT_Util_Gzip::compressIfNotCompressed($tfile);
+                       CubeIT_Util_Gzip::compressIfNotCompressed($ifile);
+                       $text = CubeIT_Util_Gzip::file_get_contents($tfile);
+                       $ipage = CubeIT_Util_Gzip::file_get_contents($ifile);
 
                        if ($simple) {
                                $this->fillIndexWithWordsSimple($index, $book_page, $ipage);
@@ -867,8 +869,8 @@ class wsDAOBook extends commonDAO {
                $textes = json_encode($textes);
                $index = json_encode($index);
 
-               file_put_contents($tfilec, $textes);
-               file_put_contents($ifilec, $index);
+               CubeIT_Util_Gzip::file_put_contents($tfilec, $textes);
+               CubeIT_Util_Gzip::file_put_contents($ifilec, $index);
        }
 
        protected function _escapeIndex($str) {
@@ -1490,27 +1492,27 @@ class wsDAOBook extends commonDAO {
                $htmlCompiler->compile();
        }
 
-       public function indexPDF($book, $pages) {
-               $indexPath = WS_BOOKS . '/search/' . $book->book_id;
+       /* public function indexPDF($book, $pages) {
+         $indexPath = WS_BOOKS . '/search/' . $book->book_id;
 
-               Zend_Search_Lucene_Analysis_Analyzer::setDefault(new Zend_Search_Lucene_Analysis_Analyzer_Common_Utf8Num_CaseInsensitive());
+         Zend_Search_Lucene_Analysis_Analyzer::setDefault(new Zend_Search_Lucene_Analysis_Analyzer_Common_Utf8Num_CaseInsensitive());
 
-               if (file_exists($indexPath)) {
-                       files::deltree($indexPath);
-               }
-               $index = Zend_Search_Lucene::create($indexPath);
+         if (file_exists($indexPath)) {
+         files::deltree($indexPath);
+         }
+         $index = Zend_Search_Lucene::create($indexPath);
 
-               foreach ($pages as $i => $infos) {
-                       $doc = new Zend_Search_Lucene_Document();
-                       $doc->addField(Zend_Search_Lucene_Field::Text('url', '#' . $i));
-                       $doc->addField(Zend_Search_Lucene_Field::UnStored('contents', file_get_contents(WS_DOCS . '/' . $infos['document_id'] . '/p' . $infos['document_page'] . '.txt')));
-                       $index->addDocument($doc);
-               }
+         foreach ($pages as $i => $infos) {
+         $doc = new Zend_Search_Lucene_Document();
+         $doc->addField(Zend_Search_Lucene_Field::Text('url', '#' . $i));
+         $doc->addField(Zend_Search_Lucene_Field::UnStored('contents', file_get_contents(WS_DOCS . '/' . $infos['document_id'] . '/p' . $infos['document_page'] . '.txt')));
+         $index->addDocument($doc);
+         }
 
-               $c = $this->con->openCursor('books');
-               $c->lucene_time = TIME;
-               $c->update('WHERE book_id=' . $book->book_id);
-       }
+         $c = $this->con->openCursor('books');
+         $c->lucene_time = TIME;
+         $c->update('WHERE book_id=' . $book->book_id);
+         } */
 
        public function compilePDF($book, $pages) {
                if (substr($book->parametres->pdfName, 0, 4) == 'http') {
index 9119db4483c2e885c99c2a971ec90b38c2e6feff..9120eaf9022cbc498583dffca0cf2bc37f517491 100644 (file)
-<?php\r
-\r
-class wsDAODocument extends commonDAO {\r
-\r
-       public static $normal = array('document_id', 'file', 'proprietaire', 'pages', 'date', 'numberSections', 'version');\r
-       public static $complex = array('localInfos', 'bookmarks', 'generalInfos', 'conversionInfos', 'trim');\r
-\r
-       public function singleton($r) {\r
-               $document = new wsDocument();\r
-               foreach (self::$normal as $k) {\r
-                       $document->$k = $r->$k;\r
-               }\r
-               foreach (self::$complex as $k) {\r
-                       if ($r->$k != '') {\r
-                               $document->$k = unserialize($r->$k);\r
-                       }\r
-               }\r
-               $document->init();\r
-               return $document;\r
-       }\r
-\r
-       public function setFluidbookLinksFromCSV($book_id) {\r
-               $daoBook = new wsDAOBook($this->con);\r
-               $pages = $daoBook->getPagesOfBook($book_id);\r
-\r
-               foreach ($pages as $page => $infos) {\r
-                       $docs[] = $infos['document_id'];\r
-               }\r
-\r
-               $docs = array_unique($docs);\r
-               foreach ($docs as $document_id) {\r
-                       $this->putLinksFromCSV($document_id);\r
-               }\r
-       }\r
-\r
-       public function restoreLinksVersion($book_id, $update, $user = 0) {\r
-\r
-               $daoBook = new wsDAOBook($this->con);\r
-               $pages = $daoBook->getPagesOfBookAt($book_id, $update);\r
-\r
-               $docs = array();\r
-               foreach ($pages as $infos) {\r
-                       $docs[] = $infos['document_id'];\r
-               }\r
-\r
-               $r = $this->con->select('SELECT document_id,links,rulers FROM document_links_versions WHERE `update`=' . $update . ' AND document_id IN (' . implode(',', $docs) . ')');\r
-\r
-               while ($r->fetch()) {\r
-                       wsLinks::setDocumentLinks($r->document_id, $r->links, $r->rulers, $user, 'Links restored from ' . date('Y-m-d H:i:s', $update) . ' version', TIME);\r
-               }\r
-\r
-               $r = $this->con->select('SELECT * FROM special_links_versions WHERE `update`=' . $update . ' AND book_id=\'' . $this->con->escape($book_id) . '\'');\r
-               if ($r->count()) {\r
-                       $c = $this->con->openCursor('books');\r
-                       $c->specialLinks = $r->links;\r
-                       $c->specialRulers = $r->rulers;\r
-                       $c->update('WHERE book_id=\'' . $this->con->escape($book_id) . '\'');\r
-               }\r
-       }\r
-\r
-       public function putLinksFromCSV($document_id) {\r
-               $log = array('Copie des liens du PDF vers la base de données');\r
-               $document = $this->selectById($document_id);\r
-\r
-               $booleans = array('video_loop', 'video_auto_start', 'video_controls', 'video_sound_on');\r
-\r
-               $links = array();\r
-\r
-               for ($page = 1; $page <= $document->generalInfos['pages']; $page++) {\r
-                       $links[$page] = array();\r
-\r
-                       $csv = WS_DOCS . '/' . $document->document_id . '/p' . $page . '.csv';\r
-                       if (!file_exists($csv)) {\r
-                               $log[] = 'Links file of page ' . $page . ' not found';\r
-                               continue;\r
-                       }\r
-\r
-                       $newformat = (filemtime($csv) > 1363685416);\r
-\r
-                       $log[] = 'Links file of page ' . $page . ' found';\r
-\r
-\r
-                       $fp = fopen($csv, 'rb');\r
-\r
-                       while (true) {\r
-                               $line = fgetcsv($fp, 512, ';', '"');\r
-                               // End of file\r
-                               if (!$line) {\r
-                                       break;\r
-                               }\r
-                               // Commentaire || ligne vide\r
-                               if (substr($line[0], 0, 1) == '#' || is_null($line[0])) {\r
-                                       continue;\r
-                               }\r
-                               $link = array();\r
-                               if ($newformat) {\r
-                                       $cols = array('page' => '', 'left' => '', 'top' => '', 'width' => '', 'height' => '', 'type' => '', 'to' => '', 'target' => '_blank', 'video_loop' => true, 'video_auto_start' => true, 'video_controls' => true, 'video_sound_on' => true, 'infobulle' => '', 'numerotation' => 'physical');\r
-                               } else {\r
-                                       $cols = array('page' => '', 'type' => '', 'to' => '', 'left' => '', 'top' => '', 'width' => '', 'height' => '', 'target' => '_blank', 'video_loop' => true, 'video_auto_start' => true, 'video_controls' => true, 'video_sound_on' => true, 'infobulle' => '', 'numerotation' => 'physical');\r
-                               }\r
-                               $k = 0;\r
-                               foreach ($cols as $col => $default) {\r
-                                       if (isset($line[$k])) {\r
-                                               if (in_array($k, $booleans)) {\r
-                                                       $link[$col] = ($line[$k] == '1');\r
-                                               } else {\r
-                                                       $link[$col] = utf8_encode($line[$k]);\r
-                                               }\r
-                                       } else {\r
-                                               $link[$col] = $default;\r
-                                       }\r
-                                       $k++;\r
-                               }\r
-                               $link['page'] = $page;\r
-                               $links[$page][] = $link;\r
-                       }\r
-\r
-                       $log[] = 'Added ' . count($links) . ' links';\r
-               }\r
-\r
-               wsLinks::setDocumentLinks($document_id, json_encode($links), json_encode(array()), 0, 'Links imported from PDF', TIME);\r
-\r
-               return implode("\n", $log);\r
-       }\r
-\r
-       public function setLinksAndRulers($book_id, $links, $rulers, $comments = '', $user = 0) {\r
-               $daoBook = new wsDAOBook($this->con);\r
-               $pages = $daoBook->getPagesOfBook($book_id, false);\r
-\r
-               if (is_array($links)) {\r
-                       $links = json_encode($links);\r
-               }\r
-               if (is_array($rulers)) {\r
-                       $rulers = json_encode($rulers);\r
-               }\r
-               $links = json_decode($links, false);\r
-               $rulers = json_decode($rulers, false);\r
-\r
-               $t = array();\r
-\r
-               foreach ($links as $link) {\r
-                       if (!isset($t[$link->page])) {\r
-                               $t[$link->page] = array('links' => array(), 'rulers' => array());\r
-                       }\r
-                       $t[$link->page]['links'][] = $link;\r
-               }\r
-               foreach ($rulers as $ruler) {\r
-                       if (!isset($t[$ruler->page])) {\r
-                               $t[$ruler->page] = array('links' => array(), 'rulers' => array());\r
-                       }\r
-                       $t[$ruler->page]['rulers'][] = $ruler;\r
-               }\r
-\r
-               $links = array();\r
-               $rulers = array();\r
-\r
-               foreach ($pages as $p) {\r
-                       if (!isset($links[$p['document_id']])) {\r
-                               $links[$p['document_id']] = array();\r
-                               $rulers[$p['document_id']] = array();\r
-                       }\r
-\r
-                       if (!isset($links[$p['document_id']][$p['document_page']])) {\r
-                               $links[$p['document_id']][$p['document_page']] = array();\r
-                               $rulers[$p['document_id']][$p['document_page']] = array();\r
-                       }\r
-               }\r
-\r
-               $specialLinks = array();\r
-               $specialRulers = array();\r
-\r
-               foreach ($t as $page => $tt) {\r
-                       if (!is_int($page)) {\r
-                               if (isset($tt['links'])) {\r
-                                       $specialLinks = array_merge($specialLinks, $tt['links']);\r
-                               }\r
-                               if (isset($tt['rulers'])) {\r
-                                       $specialRulers = array_merge($specialRulers, $tt['rulers']);\r
-                               }\r
-                               continue;\r
-                       }\r
-                       $infos = $pages[$page];\r
-                       $doc_id = $infos['document_id'];\r
-                       $doc_page = $infos['document_page'];\r
-\r
-                       if (!isset($links[$doc_id])) {\r
-                               $links[$doc_id] = array();\r
-                               $rulers[$doc_id] = array();\r
-                       }\r
-                       if (!isset($links[$doc_id][$doc_page])) {\r
-                               $links[$doc_id][$doc_page] = array();\r
-                               $rulers[$doc_id][$doc_page] = array();\r
-                       }\r
-\r
-                       foreach ($tt['links'] as $link) {\r
-                               $links[$doc_id][$doc_page][] = $link;\r
-                       }\r
-\r
-                       foreach ($tt['rulers'] as $ruler) {\r
-                               $rulers[$doc_id][$doc_page][] = $ruler;\r
-                       }\r
-               }\r
-\r
-               foreach ($links as $document_id => $dummy) {\r
-                       wsLinks::setDocumentLinks($document_id, json_encode($links[$document_id]), json_encode($rulers[$document_id]), $user, $comments, TIME);\r
-               }\r
-               \r
-               $daoBook->setSpecialLinksAndRulers($book_id, $specialLinks, $specialRulers);\r
-\r
-               $c = $this->con->openCursor('books');\r
-               $c->changedate = TIME;\r
-               $c->update('WHERE book_id=\'' . $this->con->escape($book_id) . '\'');\r
-       }\r
-\r
-       public function copyLinks($from, $to) {\r
-               $sql = 'REPLACE INTO document_links_versions (`document_id`,`links`,`rulers`,`update`) '\r
-                               . 'SELECT \'' . $to . '\',`links`,`rulers`,`update` FROM document_links_versions WHERE document_id=\'' . $from . '\'';\r
-\r
-               $this->con->execute($sql);\r
-       }\r
-\r
-       public function setLinksFromOldFluidbook($book_id) {\r
-               $daoBook = new wsDAOBook($this->con);\r
-               $pages = $daoBook->getPagesOfBook($book_id);\r
-               fb($pages);\r
-               $book = $daoBook->selectById($book_id);\r
-               $width = $book->parametres->width;\r
-\r
-               $xml = simplexml_load_file('http://ws.fluidbook.com/books/' . $book_id . '/data/links.xml');\r
-               echo $xml;\r
-               $links = $xml->xpath('//oneLink');\r
-\r
-               $res = array();\r
-               $lpages = array();\r
-               foreach ($links as $l) {\r
-                       if ((string) $l->page == '') {\r
-                               continue;\r
-                       }\r
-\r
-                       $left = floatval((string) $l->startX);\r
-                       $right = floatval((string) $l->endX);\r
-                       $top = floatval((string) $l->startY);\r
-                       $bottom = floatval((string) $l->endY);\r
-\r
-                       $lwidth = abs($right - $left);\r
-                       $lheight = abs($bottom - $top);\r
-\r
-                       $left = min($left, $right);\r
-                       $top = min($top, $bottom);\r
-\r
-                       $link = array();\r
-                       $link['page'] = (string) $l->page;\r
-                       $link['numerotation'] = false;\r
-                       $link['left'] = $left;\r
-                       $link['width'] = $lwidth;\r
-                       if ($link['left'] > $width) {\r
-                               $link['page']++;\r
-                               $link['left'] -= $width;\r
-                       }\r
-                       $page = $link['page'];\r
-                       $link['top'] = $top;\r
-                       $link['height'] = $lheight;\r
-                       $link['infobulle'] = '';\r
-                       $link['target'] = '_blank';\r
-                       $link['to'] = (string) $l->linkTo;\r
-                       $link['type'] = intval((string) $l->linkType);\r
-                       $link['video_loop'] = ((string) $l->loopFlag == '1');\r
-                       $link['video_auto_start'] = true;\r
-                       $link['video_controls'] = ((string) $l->playerActive == '1');\r
-                       $link['video_sound_on'] = ((string) $l->soundOn == '1');\r
-\r
-                       $dir = WS_BOOKS . '/working/' . $book_id . '/';\r
-\r
-                       if ($link['type'] == '4' || $link['type'] == '6') {\r
-                               if (!stristr($link['to'], ':/')) {\r
-                                       if (!file_exists($dir)) {\r
-                                               mkdir($dir, 0755, true);\r
-                                       }\r
-                                       copy('http://ws.fluidbook.com/books/' . $book_id . '/data/' . $link['to'], $dir . '/' . $link['to']);\r
-                               }\r
-                       }\r
-\r
-                       if (!isset($lpages[$page])) {\r
-                               $lpages[$page] = array();\r
-                       }\r
-                       $lpages[$page][] = $link;\r
-               }\r
-\r
-\r
-               $links = array();\r
-               foreach ($pages as $p => $infos) {\r
-                       if (!isset($links[$infos['document_id']])) {\r
-                               $links[$infos['document_id']] = array();\r
-                       }\r
-                       if (isset($lpages[$p])) {\r
-                               $links[$infos['document_id']][$infos['document_page']] = $lpages[$p];\r
-                       }\r
-               }\r
-\r
-               krsort($links);\r
-\r
-               foreach ($links as $document_id => $l) {\r
-                       wsLinks::setDocumentLinks($document_id, json_encode($l), json_encode(array()), 0, 'Import from Fluidbook V1', TIME);\r
-               }\r
-\r
-               $c = $this->con->openCursor('books');\r
-               $c->changedate = TIME;\r
-               $c->update('WHERE book_id=\'' . $this->con->escape($book_id) . '\'');\r
-       }\r
-\r
-       public function getLinksAndRulers($book_id, &$links, &$rulers, $time = null) {\r
-               return wsLinks::getLinksAndRulers($book_id, $links, $rulers, $time);\r
-       }\r
-\r
-       public function getLinksVersions($book_id) {\r
-               $r = $this->con->select('SELECT * FROM special_links_versions WHERE book_id=\'' . $this->con->escape($book_id) . '\'');\r
-               $specials = array();\r
-               while ($r->fetch()) {\r
-                       $ll = json_decode($r->links);\r
-                       $lr = json_decode($r->rulers);\r
-                       $specials[$r->update] = array('links' => count($ll), 'rulers' => count($lr));\r
-               }\r
-\r
-\r
-               // Get composition versions\r
-               $r = $this->con->select('SELECT * FROM book_pages_versions WHERE book_id=\'' . $this->con->escape($book_id) . '\'');\r
-               $docs = array();\r
-               while ($r->fetch()) {\r
-                       $c = unserialize($r->composition);\r
-                       foreach ($c as $infos) {\r
-                               $docs[] = $infos['document_id'];\r
-                       }\r
-               }\r
-\r
-               $res = array();\r
-               $r = $this->con->select('SELECT l.*,u.prenom,u.nom FROM document_links_versions l LEFT JOIN utilisateurs u ON l.user=u.utilisateur_id WHERE document_id IN(' . implode(',', $docs) . ')');\r
-               while ($r->fetch()) {\r
-                       if (!isset($res[$r->update])) {\r
-                               if (isset($specials[$r->update])) {\r
-                                       $res[$r->update] = $specials[$r->update];\r
-                               } else {\r
-                                       $res[$r->update] = array('links' => 0, 'rulers' => 0);\r
-                               }\r
-                       }\r
-\r
-\r
-                       $ll = json_decode($r->links);\r
-                       foreach ($ll as $lll) {\r
-                               $res[$r->update]['links'] += count($lll);\r
-                       }\r
-                       $rr = json_decode($r->rulers);\r
-                       foreach ($rr as $rrr) {\r
-                               $res[$r->update]['rulers'] += count($rrr);\r
-                       }\r
-                       $res[$r->update]['comments'] = $r->comments;\r
-                       if ($r->user > 0) {\r
-                               $user = $r->prenom . ' ' . $r->nom;\r
-                       } else {\r
-                               $user = '';\r
-                       }\r
-                       $res[$r->update]['user'] = $user;\r
-               }\r
-               return $res;\r
-       }\r
-\r
-       public function getNextId() {\r
-               $r = $this->con->select('SELECT MAX(document_id) AS document_id FROM documents');\r
-               if ($r->document_id < 100000) {\r
-                       return 100000;\r
-               }\r
-               return $r->document_id + 1;\r
-       }\r
-\r
-       public function selectById($document_id) {\r
-               $r = $this->con->select('SELECT * FROM documents WHERE document_id=\'' . $this->con->escape($document_id) . '\'');\r
-               return $this->singleton($r);\r
-       }\r
-\r
-       public function selectByBookId($book_id) {\r
-               $r = $this->con->select('SELECT * FROM documents WHERE document_id IN(SELECT document_id FROM book_pages WHERE book_id=\'' . $this->con->escape($book_id) . '\')');\r
-               return $this->factory($r);\r
-       }\r
-\r
-       public function selectInList($documents_id) {\r
-               $r = $this->con->select('SELECT * FROM documents WHERE document_id IN(' . implode(',', $documents_id) . ')');\r
-               return $this->factory($r);\r
-       }\r
-\r
-       public function cree() {\r
-               $document = new wsDocument();\r
-               $document->document_id = 'new';\r
-               $document->file = '';\r
-               $document->proprietaire = 0;\r
-               $document->pages = 0;\r
-               $document->trim = array();\r
-               $document->date = TIME;\r
-               $document->localInfos = new wsDocumentLocalInfos();\r
-               return $document;\r
-       }\r
-\r
-       public function sauve($data) {\r
-               if (!isset($data['document_id'])) {\r
-                       $data['document_id'] = 'new';\r
-               }\r
-               $c = $this->con->openCursor('documents');\r
-\r
-               foreach (self::$normal as $k) {\r
-                       if (isset($data[$k])) {\r
-                               $c->$k = $data[$k];\r
-                       }\r
-               }\r
-               foreach (self::$complex as $k) {\r
-                       if (isset($data[$k])) {\r
-                               $c->$k = serialize($data[$k]);\r
-                       }\r
-               }\r
-               if (isset($data['localInfos'])) {\r
-                       $c->localHash = md5($c->localInfos);\r
-               }\r
-\r
-               if ($data['document_id'] == 'new') {\r
-                       $document_id = $c->document_id = $this->getNextId();\r
-                       $c->date = TIME;\r
-                       $c->insert();\r
-               } else {\r
-                       $document_id = $data['document_id'];\r
-                       $c->update('WHERE document_id=\'' . $this->con->escape($data['document_id']) . '\'');\r
-               }\r
-               $doc = $this->selectById($document_id);\r
-               return $doc;\r
-       }\r
-\r
-       public function updateField($document_id, $fieldName, $value) {\r
-               $c = $this->con->openCursor('documents');\r
-               $c->$fieldName = $value;\r
-               $c->update('WHERE document_id=\'' . $this->con->escape($document_id) . '\'');\r
-       }\r
-\r
-       public function updateFromObject($document) {\r
-               $data = array();\r
-               foreach (self::$normal as $k) {\r
-                       $data[$k] = $document->$k;\r
-               }\r
-               foreach (self::$complex as $k) {\r
-                       $data[$k] = $document->$k;\r
-               }\r
-               return $this->sauve($data);\r
-       }\r
-\r
-       public function getLinksAsExcel($links, $rulers) {\r
-               return wsLinks::linksToExcel($links, $rulers);\r
-       }\r
-\r
-}\r
-\r
-?>
\ No newline at end of file
+<?php
+
+class wsDAODocument extends commonDAO {
+
+       public static $normal = array('document_id', 'file', 'proprietaire', 'pages', 'date', 'numberSections', 'version');
+       public static $complex = array('localInfos', 'bookmarks', 'generalInfos', 'conversionInfos', 'trim');
+
+       public function singleton($r) {
+               $document = new wsDocument();
+               foreach (self::$normal as $k) {
+                       $document->$k = $r->$k;
+               }
+               foreach (self::$complex as $k) {
+                       if ($r->$k != '') {
+                               $document->$k = unserialize($r->$k);
+                       }
+               }
+               $document->init();
+               return $document;
+       }
+
+       public function setFluidbookLinksFromCSV($book_id) {
+               $daoBook = new wsDAOBook($this->con);
+               $pages = $daoBook->getPagesOfBook($book_id);
+
+               foreach ($pages as $page => $infos) {
+                       $docs[] = $infos['document_id'];
+               }
+
+               $docs = array_unique($docs);
+               foreach ($docs as $document_id) {
+                       $this->putLinksFromCSV($document_id);
+               }
+       }
+
+       public function restoreLinksVersion($book_id, $update, $user = 0) {
+
+               $daoBook = new wsDAOBook($this->con);
+               $pages = $daoBook->getPagesOfBookAt($book_id, $update);
+
+               $docs = array();
+               foreach ($pages as $infos) {
+                       $docs[] = $infos['document_id'];
+               }
+
+               $r = $this->con->select('SELECT document_id,links,rulers FROM document_links_versions WHERE `update`=' . $update . ' AND document_id IN (' . implode(',', $docs) . ')');
+
+               while ($r->fetch()) {
+                       wsLinks::setDocumentLinks($r->document_id, $r->links, $r->rulers, $user, 'Links restored from ' . date('Y-m-d H:i:s', $update) . ' version', TIME);
+               }
+
+               $r = $this->con->select('SELECT * FROM special_links_versions WHERE `update`=' . $update . ' AND book_id=\'' . $this->con->escape($book_id) . '\'');
+               if ($r->count()) {
+                       $c = $this->con->openCursor('books');
+                       $c->specialLinks = $r->links;
+                       $c->specialRulers = $r->rulers;
+                       $c->update('WHERE book_id=\'' . $this->con->escape($book_id) . '\'');
+               }
+       }
+
+       public function putLinksFromCSV($document_id) {
+               $log = array('Copie des liens du PDF vers la base de données');
+               $document = $this->selectById($document_id);
+
+               $booleans = array('video_loop', 'video_auto_start', 'video_controls', 'video_sound_on');
+
+               $links = array();
+
+               for ($page = 1; $page <= $document->generalInfos['pages']; $page++) {
+                       $links[$page] = array();
+
+                       $csv = WS_DOCS . '/' . $document->document_id . '/p' . $page . '.csv';
+                       if (!file_exists($csv) && file_exists($csv . '.gz')) {
+                               $csv = 'compress.zlib://' . $csv . '.gz';
+                       } elseif (!file_exists($csv)) {
+                               $log[] = 'Links file of page ' . $page . ' not found';
+                               continue;
+                       }
+
+                       $newformat = (filemtime($csv) > 1363685416);
+
+                       $log[] = 'Links file of page ' . $page . ' found';
+
+                       $fp = fopen($csv, 'rb');
+
+                       while (true) {
+                               $line = fgetcsv($fp, 512, ';', '"');
+                               // End of file
+                               if (!$line) {
+                                       break;
+                               }
+                               // Commentaire || ligne vide
+                               if (substr($line[0], 0, 1) == '#' || is_null($line[0])) {
+                                       continue;
+                               }
+                               $link = array();
+                               if ($newformat) {
+                                       $cols = array('page' => '', 'left' => '', 'top' => '', 'width' => '', 'height' => '', 'type' => '', 'to' => '', 'target' => '_blank', 'video_loop' => true, 'video_auto_start' => true, 'video_controls' => true, 'video_sound_on' => true, 'infobulle' => '', 'numerotation' => 'physical');
+                               } else {
+                                       $cols = array('page' => '', 'type' => '', 'to' => '', 'left' => '', 'top' => '', 'width' => '', 'height' => '', 'target' => '_blank', 'video_loop' => true, 'video_auto_start' => true, 'video_controls' => true, 'video_sound_on' => true, 'infobulle' => '', 'numerotation' => 'physical');
+                               }
+                               $k = 0;
+                               foreach ($cols as $col => $default) {
+                                       if (isset($line[$k])) {
+                                               if (in_array($k, $booleans)) {
+                                                       $link[$col] = ($line[$k] == '1');
+                                               } else {
+                                                       $link[$col] = utf8_encode($line[$k]);
+                                               }
+                                       } else {
+                                               $link[$col] = $default;
+                                       }
+                                       $k++;
+                               }
+                               $link['page'] = $page;
+                               $links[$page][] = $link;
+                       }
+
+                       $log[] = 'Added ' . count($links) . ' links';
+               }
+
+               wsLinks::setDocumentLinks($document_id, json_encode($links), json_encode(array()), 0, 'Links imported from PDF', TIME);
+
+               return implode("\n", $log);
+       }
+
+       public function setLinksAndRulers($book_id, $links, $rulers, $comments = '', $user = 0) {
+               $daoBook = new wsDAOBook($this->con);
+               $pages = $daoBook->getPagesOfBook($book_id, false);
+
+               if (is_array($links)) {
+                       $links = json_encode($links);
+               }
+               if (is_array($rulers)) {
+                       $rulers = json_encode($rulers);
+               }
+               $links = json_decode($links, false);
+               $rulers = json_decode($rulers, false);
+
+               $t = array();
+
+               foreach ($links as $link) {
+                       if (!isset($t[$link->page])) {
+                               $t[$link->page] = array('links' => array(), 'rulers' => array());
+                       }
+                       $t[$link->page]['links'][] = $link;
+               }
+               foreach ($rulers as $ruler) {
+                       if (!isset($t[$ruler->page])) {
+                               $t[$ruler->page] = array('links' => array(), 'rulers' => array());
+                       }
+                       $t[$ruler->page]['rulers'][] = $ruler;
+               }
+
+               $links = array();
+               $rulers = array();
+
+               foreach ($pages as $p) {
+                       if (!isset($links[$p['document_id']])) {
+                               $links[$p['document_id']] = array();
+                               $rulers[$p['document_id']] = array();
+                       }
+
+                       if (!isset($links[$p['document_id']][$p['document_page']])) {
+                               $links[$p['document_id']][$p['document_page']] = array();
+                               $rulers[$p['document_id']][$p['document_page']] = array();
+                       }
+               }
+
+               $specialLinks = array();
+               $specialRulers = array();
+
+               foreach ($t as $page => $tt) {
+                       if (!is_int($page)) {
+                               if (isset($tt['links'])) {
+                                       $specialLinks = array_merge($specialLinks, $tt['links']);
+                               }
+                               if (isset($tt['rulers'])) {
+                                       $specialRulers = array_merge($specialRulers, $tt['rulers']);
+                               }
+                               continue;
+                       }
+                       $infos = $pages[$page];
+                       $doc_id = $infos['document_id'];
+                       $doc_page = $infos['document_page'];
+
+                       if (!isset($links[$doc_id])) {
+                               $links[$doc_id] = array();
+                               $rulers[$doc_id] = array();
+                       }
+                       if (!isset($links[$doc_id][$doc_page])) {
+                               $links[$doc_id][$doc_page] = array();
+                               $rulers[$doc_id][$doc_page] = array();
+                       }
+
+                       foreach ($tt['links'] as $link) {
+                               $links[$doc_id][$doc_page][] = $link;
+                       }
+
+                       foreach ($tt['rulers'] as $ruler) {
+                               $rulers[$doc_id][$doc_page][] = $ruler;
+                       }
+               }
+
+               foreach ($links as $document_id => $dummy) {
+                       wsLinks::setDocumentLinks($document_id, json_encode($links[$document_id]), json_encode($rulers[$document_id]), $user, $comments, TIME);
+               }
+
+               $daoBook->setSpecialLinksAndRulers($book_id, $specialLinks, $specialRulers);
+
+               $c = $this->con->openCursor('books');
+               $c->changedate = TIME;
+               $c->update('WHERE book_id=\'' . $this->con->escape($book_id) . '\'');
+       }
+
+       public function copyLinks($from, $to) {
+               $sql = 'REPLACE INTO document_links_versions (`document_id`,`links`,`rulers`,`update`) '
+                               . 'SELECT \'' . $to . '\',`links`,`rulers`,`update` FROM document_links_versions WHERE document_id=\'' . $from . '\'';
+
+               $this->con->execute($sql);
+       }
+
+       public function setLinksFromOldFluidbook($book_id) {
+               $daoBook = new wsDAOBook($this->con);
+               $pages = $daoBook->getPagesOfBook($book_id);
+               fb($pages);
+               $book = $daoBook->selectById($book_id);
+               $width = $book->parametres->width;
+
+               $xml = simplexml_load_file('http://ws.fluidbook.com/books/' . $book_id . '/data/links.xml');
+               echo $xml;
+               $links = $xml->xpath('//oneLink');
+
+               $res = array();
+               $lpages = array();
+               foreach ($links as $l) {
+                       if ((string) $l->page == '') {
+                               continue;
+                       }
+
+                       $left = floatval((string) $l->startX);
+                       $right = floatval((string) $l->endX);
+                       $top = floatval((string) $l->startY);
+                       $bottom = floatval((string) $l->endY);
+
+                       $lwidth = abs($right - $left);
+                       $lheight = abs($bottom - $top);
+
+                       $left = min($left, $right);
+                       $top = min($top, $bottom);
+
+                       $link = array();
+                       $link['page'] = (string) $l->page;
+                       $link['numerotation'] = false;
+                       $link['left'] = $left;
+                       $link['width'] = $lwidth;
+                       if ($link['left'] > $width) {
+                               $link['page'] ++;
+                               $link['left'] -= $width;
+                       }
+                       $page = $link['page'];
+                       $link['top'] = $top;
+                       $link['height'] = $lheight;
+                       $link['infobulle'] = '';
+                       $link['target'] = '_blank';
+                       $link['to'] = (string) $l->linkTo;
+                       $link['type'] = intval((string) $l->linkType);
+                       $link['video_loop'] = ((string) $l->loopFlag == '1');
+                       $link['video_auto_start'] = true;
+                       $link['video_controls'] = ((string) $l->playerActive == '1');
+                       $link['video_sound_on'] = ((string) $l->soundOn == '1');
+
+                       $dir = WS_BOOKS . '/working/' . $book_id . '/';
+
+                       if ($link['type'] == '4' || $link['type'] == '6') {
+                               if (!stristr($link['to'], ':/')) {
+                                       if (!file_exists($dir)) {
+                                               mkdir($dir, 0755, true);
+                                       }
+                                       copy('http://ws.fluidbook.com/books/' . $book_id . '/data/' . $link['to'], $dir . '/' . $link['to']);
+                               }
+                       }
+
+                       if (!isset($lpages[$page])) {
+                               $lpages[$page] = array();
+                       }
+                       $lpages[$page][] = $link;
+               }
+
+
+               $links = array();
+               foreach ($pages as $p => $infos) {
+                       if (!isset($links[$infos['document_id']])) {
+                               $links[$infos['document_id']] = array();
+                       }
+                       if (isset($lpages[$p])) {
+                               $links[$infos['document_id']][$infos['document_page']] = $lpages[$p];
+                       }
+               }
+
+               krsort($links);
+
+               foreach ($links as $document_id => $l) {
+                       wsLinks::setDocumentLinks($document_id, json_encode($l), json_encode(array()), 0, 'Import from Fluidbook V1', TIME);
+               }
+
+               $c = $this->con->openCursor('books');
+               $c->changedate = TIME;
+               $c->update('WHERE book_id=\'' . $this->con->escape($book_id) . '\'');
+       }
+
+       public function getLinksAndRulers($book_id, &$links, &$rulers, $time = null) {
+               return wsLinks::getLinksAndRulers($book_id, $links, $rulers, $time);
+       }
+
+       public function getLinksVersions($book_id) {
+               $r = $this->con->select('SELECT * FROM special_links_versions WHERE book_id=\'' . $this->con->escape($book_id) . '\'');
+               $specials = array();
+               while ($r->fetch()) {
+                       $ll = json_decode($r->links);
+                       $lr = json_decode($r->rulers);
+                       $specials[$r->update] = array('links' => count($ll), 'rulers' => count($lr));
+               }
+
+
+               // Get composition versions
+               $r = $this->con->select('SELECT * FROM book_pages_versions WHERE book_id=\'' . $this->con->escape($book_id) . '\'');
+               $docs = array();
+               while ($r->fetch()) {
+                       $c = unserialize($r->composition);
+                       foreach ($c as $infos) {
+                               $docs[] = $infos['document_id'];
+                       }
+               }
+
+               $res = array();
+               $r = $this->con->select('SELECT l.*,u.prenom,u.nom FROM document_links_versions l LEFT JOIN utilisateurs u ON l.user=u.utilisateur_id WHERE document_id IN(' . implode(',', $docs) . ')');
+               while ($r->fetch()) {
+                       if (!isset($res[$r->update])) {
+                               if (isset($specials[$r->update])) {
+                                       $res[$r->update] = $specials[$r->update];
+                               } else {
+                                       $res[$r->update] = array('links' => 0, 'rulers' => 0);
+                               }
+                       }
+
+
+                       $ll = json_decode($r->links);
+                       foreach ($ll as $lll) {
+                               $res[$r->update]['links'] += count($lll);
+                       }
+                       $rr = json_decode($r->rulers);
+                       foreach ($rr as $rrr) {
+                               $res[$r->update]['rulers'] += count($rrr);
+                       }
+                       $res[$r->update]['comments'] = $r->comments;
+                       if ($r->user > 0) {
+                               $user = $r->prenom . ' ' . $r->nom;
+                       } else {
+                               $user = '';
+                       }
+                       $res[$r->update]['user'] = $user;
+               }
+               return $res;
+       }
+
+       public function getNextId() {
+               $r = $this->con->select('SELECT MAX(document_id) AS document_id FROM documents');
+               if ($r->document_id < 100000) {
+                       return 100000;
+               }
+               return $r->document_id + 1;
+       }
+
+       public function selectById($document_id) {
+               $r = $this->con->select('SELECT * FROM documents WHERE document_id=\'' . $this->con->escape($document_id) . '\'');
+               return $this->singleton($r);
+       }
+
+       public function selectByBookId($book_id) {
+               $r = $this->con->select('SELECT * FROM documents WHERE document_id IN(SELECT document_id FROM book_pages WHERE book_id=\'' . $this->con->escape($book_id) . '\')');
+               return $this->factory($r);
+       }
+
+       public function selectInList($documents_id) {
+               $r = $this->con->select('SELECT * FROM documents WHERE document_id IN(' . implode(',', $documents_id) . ')');
+               return $this->factory($r);
+       }
+
+       public function cree() {
+               $document = new wsDocument();
+               $document->document_id = 'new';
+               $document->file = '';
+               $document->proprietaire = 0;
+               $document->pages = 0;
+               $document->trim = array();
+               $document->date = TIME;
+               $document->localInfos = new wsDocumentLocalInfos();
+               return $document;
+       }
+
+       public function sauve($data) {
+               if (!isset($data['document_id'])) {
+                       $data['document_id'] = 'new';
+               }
+               $c = $this->con->openCursor('documents');
+
+               foreach (self::$normal as $k) {
+                       if (isset($data[$k])) {
+                               $c->$k = $data[$k];
+                       }
+               }
+               foreach (self::$complex as $k) {
+                       if (isset($data[$k])) {
+                               $c->$k = serialize($data[$k]);
+                       }
+               }
+               if (isset($data['localInfos'])) {
+                       $c->localHash = md5($c->localInfos);
+               }
+
+               if ($data['document_id'] == 'new') {
+                       $document_id = $c->document_id = $this->getNextId();
+                       $c->date = TIME;
+                       $c->insert();
+               } else {
+                       $document_id = $data['document_id'];
+                       $c->update('WHERE document_id=\'' . $this->con->escape($data['document_id']) . '\'');
+               }
+               $doc = $this->selectById($document_id);
+               return $doc;
+       }
+
+       public function updateField($document_id, $fieldName, $value) {
+               $c = $this->con->openCursor('documents');
+               $c->$fieldName = $value;
+               $c->update('WHERE document_id=\'' . $this->con->escape($document_id) . '\'');
+       }
+
+       public function updateFromObject($document) {
+               $data = array();
+               foreach (self::$normal as $k) {
+                       $data[$k] = $document->$k;
+               }
+               foreach (self::$complex as $k) {
+                       $data[$k] = $document->$k;
+               }
+               return $this->sauve($data);
+       }
+
+       public function getLinksAsExcel($links, $rulers) {
+               return wsLinks::linksToExcel($links, $rulers);
+       }
+
+}
index a951cdc24457ebd0e1b767ee801a3272f603d5cb..5db466c3b2eeebde629850d2dcffbd58889460dc 100644 (file)
@@ -213,6 +213,11 @@ class wsDocument extends cubeMetier {
                return cubeMath::compare($x[0], $y[0], $tolerance) && cubeMath::compare($x[1], $y[1], $tolerance);
        }
 
+       public function lnCrop() {
+               $root = dirname($this->cropped);
+               `cd $root;ln -s original.pdf crop.pdf`;
+       }
+
        public function parseInfos($data) {
                // This function get general infos (pages sizes, boxes, number sections and
                // bookmarks
@@ -277,13 +282,13 @@ class wsDocument extends cubeMetier {
 
        public function CropAndCut() {
                if (!$this->isCropped()) {
-                       copy($this->in, $this->cropped);
+                       $this->lnCrop();
                        return false;
                }
                if ($this->autocrop == 'trim') {
                        $this->trimDocument();
                } else {
-                       copy($this->in, $this->cropped);
+                       $this->lnCrop();
                }
 
                if ($this->autocut) {
@@ -330,9 +335,6 @@ class wsDocument extends cubeMetier {
        }
 
        public function optimizeSVG() {
-
-
-
                $scour = new cubeCommandLine('scour.php');
                $scour->setPath(CONVERTER_PATH);
                $scour->setNohup(true);
@@ -344,7 +346,7 @@ class wsDocument extends cubeMetier {
                foreach ($pages as $i) {
                        $this->processOnePage($i);
                }
-               $this->optimizeSVG();
+               //$this->optimizeSVG();
        }
 
        public function getLinksAndTexts() {
@@ -572,7 +574,7 @@ class wsDocument extends cubeMetier {
 
        public function makeHTML5Files($page) {
                // Then make HD background shots
-               $resolutions = array(300 => 85, 150 => 85, 36 => 85);
+               $resolutions = array(300 => 85, 150 => 85);
                foreach ($resolutions as $r => $q) {
                        $this->makeShotPNM($page, 'html/h' . $r . '-', $r, $q, 4, null, false);
                        $this->makeShotPNM($page, 'html/t' . $r . '-', $r, $q, 4, null, true);
index bc76aedd0105990f54418a241ad450ed64c5ebfe..fcfcefaf604dc6175d03cd4198c73fd0d0664d49 100644 (file)
@@ -11,9 +11,9 @@ class wsPDFFontExtractor {
        protected $descendantNames = array();
        protected $doc;
 
-       public function __construct($in, $doc=null) {
+       public function __construct($in, $doc = null) {
                $this->doc = $doc;
-               $this->in = $in . '/original.pdf';
+               $this->in = $in . '/crop.pdf';
                $this->outpdf = $in . '/fonts/pdf';
                $this->outweb = $in . '/fonts/web';
        }
@@ -162,7 +162,7 @@ class wsPDFFontExtractor {
                  0 740 0 0 0 0 0 0 0 0 536 ]
                  >>
                  endobj
-                * 
+                *
                  10 0 obj
                  <<
                  /BaseFont /FuturaLT-Bold--Identity-H
index dcb4cfb4d5e0f50df9566445504627c25989ad71..ef683728393799c52ad2995f837aa5dc35d2eef1 100644 (file)
@@ -12,7 +12,7 @@ class wsPDFFontExtractor {
 
        public function __construct($in, $doc) {
                $this->doc = $doc;
-               $this->in = $in . '/original.pdf';
+               $this->in = $in . '/crop.pdf';
                $this->outpdf = $in . '/fonts/pdf';
                $this->outweb = $in . '/fonts/web';
        }
index 3b53184b665720fc171806ada409fe9b074457c2..9c8cb86b9c0dcb60f138dd4fb59ff404248e7b5d 100644 (file)
@@ -11,9 +11,9 @@ class wsPDFFontExtractor {
        protected $descendantNames = array();
        protected $doc;
 
-       public function __construct($in, $doc=null) {
+       public function __construct($in, $doc = null) {
                $this->doc = $doc;
-               $this->in = $in . '/original.pdf';
+               $this->in = $in . '/crop.pdf';
                $this->outpdf = $in . '/fonts/pdf';
                $this->outweb = $in . '/fonts/web';
        }
@@ -115,7 +115,7 @@ class wsPDFFontExtractor {
                return $res;
        }
 
-       public function showObject($object, $binary=null) {
+       public function showObject($object, $binary = null) {
                $pdfshow = $this->getCommandLine('pdfshow');
                $pdfshow->setArg(null, $this->in);
                $pdfshow->setArg(null, $object);
index cbac22f025457f4ad759c9a5b8e89180f2b1f71c..06e28fb606d09179a05ce0502d34f02e37c1be8c 100644 (file)
@@ -153,8 +153,8 @@ class wsPackagerHTML extends wsPackager {
 
                        $htmlfile = WS_DOCS . '/' . $infos['document_id'] . '/h' . $infos['document_page'] . '.txt';
 
-                       if ($seoVersion && file_exists($htmlfile)) {
-                               $html = file_get_contents($htmlfile);
+                       if ($seoVersion && CubeIT_Util_Gzip::file_exists($htmlfile)) {
+                               $html = CubeIT_Util_Gzip::file_get_contents($htmlfile);
                                $alt .= "\n" . $html . "\n";
 
 
@@ -371,5 +371,3 @@ class wsPackagerHTML extends wsPackager {
        }
 
 }
-
-?>
\ No newline at end of file
index e6b88d9ab552e5124e04747ad3f52e2cc9bbb189..39af64c92eebda0103af303d607eb6bfe1355ba9 100644 (file)
@@ -24,7 +24,7 @@ class wsPackagerV1 extends wsPackagerHTML {
        }
 
        protected function compile() {
-               
+
        }
 
        protected function copyFluidbookFiles() {
@@ -52,7 +52,7 @@ class wsPackagerV1 extends wsPackagerHTML {
                foreach ($this->pages as $book_page => $infos) {
                        $ftext = WS_DOCS . '/' . $infos['document_id'] . '/p' . $infos['document_page'] . '.txt';
                        if (file_exists($ftext)) {
-                               $xml->addChild('pageText', htmlspecialchars(file_get_contents($ftext)));
+                               $xml->addChild('pageText', htmlspecialchars(CubeIT_Util_Gzip::file_get_contents($ftext)));
                        } else {
                                $xml->addChild('pageText', ' ');
                        }
@@ -90,7 +90,7 @@ class wsPackagerV1 extends wsPackagerHTML {
                        $link['type'] = intval($link['type']);
 
                        if ($link['page'] % 2 == 1) {
-                               $link['page']--;
+                               $link['page'] --;
                                $link['left'] = $link['left'] + $this->size[0];
                        }