-/*\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 "e;d"e;)\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é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, "&") + "\" 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(/\]\]>/, "]]>]]><![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 "e;d"e;)
+ * @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é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, "&") + "\" 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(/\]\]>/, "]]>]]><![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
-<?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
-<?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);
+ }
+
+}