/*
    org.berthomg.js
    version 0.1
    berthomg@gmail.com
*/
/**
 * module of namespace utility
 * this is the closure to initialize the namespace org.berthomg 
 */
//var org;
(function() {  
    if(window.org && (typeof window.org != 'object' || window.org.NAME)) 
        throw new Error("Namespace 'org' already exists");
    window.org = {};
    var org = window.org;
    //metainfo
    org.NAME = 'org';
    org.VERSION = 0.1;
    if(org.berthomg && (typeof org.berthomg != 'object' || org.berthomg.NAME)) 
        throw new Error("Namespace 'org.berthomg' already exists");
    org.berthomg = {};

/**
 * ...this is the module
 */
//some metainfo
org.berthomg.NAME = 'org.berthomg';//name
org.berthomg.VERSION = 0.1;//version of the namespace

//list of public symbols that we export from this namespace, for programmers 
//who use modules
org.berthomg.EXPORT = ["require","importSymbols"];

//the other symbols we are willing to export. They are ones normally used only
//by module authors and are not typically imported
org.berthomg.EXPORT_OK = ["createNamespace","isDefined",
                          "registerInitializationFunction",
                          "runInitializationFunctions",
                          "registerUnInitializationFunction",
                          "runUnInitializationFunctions",
                          "modules","globalNamespace"];

//adding symbol to global namespace
org.berthomg.globalNamespace = this;//so we can always refer to the global scope

// org.berthomg name->namespace map
org.berthomg.modules = {"org.berthomg": org.berthomg};

// a private property array holding initialization functions to invoque later
org.berthomg._initfuncs_ = [];

// a private property array holding uninitialization functions to invoque later
org.berthomg._uninitfuncs_ = [];

/**
 * this function creates and returns a namespace object for the specified name
 * and does useful error checking to ensure that the name does not conflict with
 * any previously loaded module. it throws an error if the namespace already
 * exists or if any of the property components of the namespace exist and are
 * not objects
 * 
 * sets a NAME property of the namespace to its name. if the version argument
 * is specified, set the VERSION property of the namespace
 * 
 * a mapping   
 */ 
org.berthomg.createNamespace = function (name, version) {
    //check name for validity. it must exist, and must not begin or end with a
    //period or contain two periods in a row
    if(!name) throw new Error("org.berthomg.createNamespace(): name required");
    if(name.charAt(0) == '.' ||
       name.charAt(name.length-1) == '.' ||
       name.indexOf("..") != -1)
       throw new Error("org.berthomg.createNamespace(): illegal name: " + name);
    
    //break the name at periods and create the object hierarchy we need
    var parts = name.split('.');
    
    //for each namespace component, iether create an object or ensure that an
    //object by that name already exists.
    var container = org.berthomg.globalNamespace;
    for(var i=0; i < parts.length; i++)  {
        var part = parts[i];
        //if there is no property of container with this name, create an 
        //empty object
        if(!container[part]) container[part] = {};
        else if(typeof container[part] != 'object') {
            //if there is already a property, make sure it is an object
            var n = parts.slice(0,i).join('.');
            throw new Error("org.berthomg.createNamespace(): " + n 
                            + " already exists and is not an object.");
        }
        container = container[part];
    }
    
    //the last container traversed above is the namespace we need.
    var namespace = container;
    //it is an error to define a namespace twice. it is okay if our namespace
    //object already exists, but it must already have a NAME property defined.
    if(namespace.NAME) 
        throw new Error("org.berthomg.createNamespace(): Namespace " 
                        + name + " is already defined.");
    
    //initialize name and version fields of the namespace
    namespace.NAME = name;
    if(version) namespace.VERSION = version
    
    //register this namespace in the map of all modules
    org.berthomg.modules[name] = namespace;
    
    //return the namespace object to the caller
    return namespace;
}
/**
 * test wether the module with the specified name has been defined.
 * return true if it is defined and false otherwise. 
 */ 
org.berthomg.isModuleDefined = function (name, version) {
    return name in org.berthomg.modules;
}
/**
 * this function throws an error if the named module is not defined or if it
 * defined but its version is less than specified. if the namespace exists and
 * has a suitable version, this function simply returns without doing anything.
 * use this function to cause a fatal error if the modules that your code 
 * requires are not present.
 */ 
org.berthomg.requireModule = function (name, version) {
    if(!(name in org.berthomg.modules)) {
        throw new Error("org.berthomg.require(): Namespace " 
                        + name + " is not defined.");
    }
    //if no version is specified nothing to check
    if(!version) return;
    
    var n = org.berthomg.modules[name];
    //if the defined version is less than therequired version or if the 
    //namespace does not declare any version, throw an error.
    if(!n.VERSION || n.VERSION < version)
        throw new Error("org.berthomg.require(): Namespace " + name 
                        + " has version " + n.VERSION + " but version " 
                        + version + " or greater is required."
                        );
}
/**
 * this function import symbols from a specified module. By default , it
 * imports them into the global namespace, but you may specify a different 
 * destination as the second argument
 * 
 * if no symbols are explicitly specified, the symbols in in the EXPORT array
 * of the module will be imported. if no such array is defined , and no 
 * EXPORT_OK is defined, all symbols from the module will be imported.
 * 
 * to import an explicitly specified set of symbols, pass their names as 
 * arguments after the module and the optional destination namespace. if the
 * modules define an EXPORT or EXPORT_OK array, symbols will be imported only 
 * if they are listed in one of those arrays.
 */ 
org.berthomg.importModuleSymbols = function (from) {
    //make sure that the module is correctly specified. we expect the module's
    //namespace object but will try with a string too
    if(typeof from == 'string') from = org.berthomg.modules[from];
    if(!from || typeof from != 'object')
        throw new Error("org.berthomg.importSymbols():"
                        + " namespace object required."
                        );
    
    //the source namespace may be followed by an optional destination namespace
    // and the names of one or more symbols to import.
    var to = org.berthomg.globalNamespace; //default dest
    var symbols = []; //no symbols by default
    var firstsymbol = 1; //index in arguments for first symbol name
    //see if a destination namespace is specified
    if(arguments.length > 1 && typeof arguments[1] == 'object') {
        if(arguments[1] != null) to = arguments[1];
        firstsymbol = 2;
    }
    
    //now get the list of specified symbols
    for(var a = firstsymbol; a < arguments.length; a++) {
        symbols.push(arguments[a]);
    }
    
    //if we were not passed any symbols to import, import a set of symbols 
    //defined by the module, or just import them all
    if(symbols.length == 0) {
        //if the module define an EXPORT array, import them.
        if(from.EXPORT) {
            for(var i = 0; i < from.EXPORT.length; i++) {
                var s = from.EXPORT[i];
                to[s] = from[s];
            }
            return;
        }
        //otherwise if the modules does not define an EXPORT_OK array, just 
        //import everything in the module's namespace
        else if(!from.EXPORT_OK) {
            for(s in from) to[s] = from[s];
            return;
        }
    }
    
    //if we get here, we have an explicitely specified array of symbols to
    //import. if the namespace defines EXPORT and/or EXPORT_OK ensure that each
    //symbol is listed before importing it. 
    //throw an error if a requested symbol does not exist or if it is not 
    //allowed for export
    var allowed;
    if(from.EXPORT || from.EXPORT_OK) {
        allowed = {};
        //copy allowed symbols from arrays to properties of an object
        //this allows us to test for an allowed symbol more efficiently
        if(from.EXPORT) {
            for(var i=0; i < from.EXPORT.length; i++) {
                allowed[from.EXPORT[i]] = true;
            }
        }
        if(from.EXPORT_OK) {
            for(var i=0; i < from.EXPORT_OK.length; i++) {
                allowed[from.EXPORT_OK[i]] = true;
            }
        }
    }
    //import the symbols
    for(var i=0; i < symbols.length; i++) {
        var s = symbols[i];//name of symbol to import
        if(!(s in from)) //make sure it exists
            throw new Error("org.berthomg.importSymbols(): symbol " + s
                            + " is not defined.");
        if(allowed && !(s in allowed))//make sur its a public symbol
            throw new Error("org.berthomg.importSymbols(): symbol " + s
                            + " is not public and cannot be imported.");
        to[s] = from[s];
    }
}

/**
 * modules use this function to register one or more initialization functions.
 */ 
org.berthomg.registerInitializationFunction = function (f) {
    //store the function in the array of initialization functions
    org.berthomg._initfuncs_.push(f);
    //if we have not yet registered an onload event handler, do so now.
    org.berthomg._registerInitEventHandler();
}

/**
 * a function to invoke all registered initialization functions. in client side 
 * javascript this will be automatically called when the document finished 
 * loading. in other contexts you must call it explicitly.
 */ 
org.berthomg.runInitializationFunctions = function () {
    //run each initialization function, catching and ignoring exceptions
    //so that a failure by one module does not prevent the others to be 
    //initialized
    for(var i=0; i < org.berthomg._initfuncs_.length; i++) {
        try { org.berthomg._initfuncs_[i](); }
        catch (e) { } //ignore exception
    }
    //erase the array so the functions are not called more than once
    org.berthomg._initfuncs_.length = 0;
}

/**
 * if we are loaded in a web browser, this private function registers an
 * onload event handler to run the initialisation functions for all loaded
 * modules. it does not allow himself to be callsed more than once. 
 */  
org.berthomg._registerInitEventHandler = function () {
    //check well known client side properties
    var clientside = "window" in org.berthomg.globalNamespace &&
        "navigator" in window;
    
    if(clientside) {
        if(window.addEventListener) {//W3C DOM standard event registration
            window.addEventListener("load"
                                    , org.berthomg.runInitializationFunctions
                                    , false
                                    );
        }
        else if(window.attachEvent) {//IE 5+
            window.attachEvent("onload", org.berthomg.runInitializationFunctions);
        }
        else {
            //IE 4 + old browser
            //if body defines an onload tag, tis event listener will be 
            //overwritten and neve get called.
            
            //j'essaie de mettre l'ancien callback kan meme dans une function
            //a tester...
            if(typeof window.onload == 'function') {
                var old = window.onload;    
            }
            window.onload = function () {
                if(old) old();
                org.berthomg.runInitializationFunctions();
            }
        }
    }
    //override itself avoiding future call
    org.berthomg._registerInitEventHandler = function () {};
}

/**
 * modules use this function to register one or more uninitialization functions.
 */ 
org.berthomg.registerUnInitializationFunction = function (f) {
    //store the function in the array of uninitialization functions
    org.berthomg._uninitfuncs_.push(f);
    //if we have not yet registered an onunload event handler, do so now.
    org.berthomg._registerUnInitEventHandler();
}

/**
 * a function to invoke all registered uninitialization functions. in client 
 * side javascript this will be automatically called when the user navigate 
 * away. in other contexts you must call it explicitly.
 */ 
org.berthomg.runUnInitializationFunctions = function () {
    //run each uninitialization function, catching and ignoring exceptions
    //so that a failure by one module does not prevent the others to be 
    //uninitialized
    for(var i=0; i < org.berthomg._uninitfuncs_.length; i++) {
        try { org.berthomg._uninitfuncs_[i](); }
        catch (e) { } //ignore exception
    }
    //erase the array so the functions are not called more than once
    org.berthomg._uninitfuncs_.length = 0;
}

/**
 * if we are loaded in a web browser, this private function registers an
 * onload event handler to run the initialisation functions for all loaded
 * modules. it does not allow himself to be callsed more than once. 
 */  
org.berthomg._registerUnInitEventHandler = function () {
    //check well known client side properties
    var clientside = "window" in org.berthomg.globalNamespace &&
        "navigator" in window;
    
    if(clientside) {
        if(window.addEventListener) {//W3C DOM standard event registration
            window.addEventListener("unload"
                                    , org.berthomg.runUnInitializationFunctions
                                    , false
                                    );
        }
        else if(window.attachEvent) {//IE 5+
            window.attachEvent("onunload", org.berthomg.runUnInitializationFunctions);
        }
        else {
            //IE 4 + old browser
            //if body defines an onload tag, tis event listener will be 
            //overwritten and neve get called.
            
            //j'essaie de mettre l'ancien callback kan meme dans une function
            //a tester...
            var old;
            if(typeof window.onunload == 'function') {
                old = window.onunload;    
            }
            window.onunload = function () {
                if(old) old();
                org.berthomg.runUnInitializationFunctions();
            }
        }
    }
    
    //override itself avoiding future call
    org.berthomg._registerUnInitEventHandler = function () {};
};

})();

/**
 * if the navigator does not handle console, 'console.log' become 'alert'.
 */
var console;
if(!console) {
	console = {};
	console.log = function() { };
	//uncomment the following if you want to use the default 'alert' function
	//console.log = alert;
}
//------------------------------------------------------------------------------

org.berthomg.createNamespace('org.berthomg.Class', 0.1);

/**
 * defineClass() -- utility function to define a class
 * 
 * this functoin expect an object as its only argument. it defines a new 
 * Javascript class based on the data in that object and return the constructor
 * function of te new class. this function handles the repetitive task of 
 * defining a class: setting up the prototype object for correct inheritance,
 * copying methods from other types, ...
 * 
 * the object passed as argument should have some or all the following 
 * properties:
 *
 * name: name of the class defined. if defined this value will be store in the
 * classname property of the prototype object.
 * 
 * extend: the constructor of the class to be extended. if omitted, the Object
 * contructor will be used. this valuewill be stored in the superclass property
 * of the prototype object.
 * 
 * construct: the constructor function for the class. if omitted, a new empty 
 * function will be used. this value becomes the return value of the function,
 * and is also stored in the constructor property of the prototype object.
 * 
 * methods: an object that specifies the instance methods and other shared 
 * properties for the class. the properties of this object are copied into the
 * prototype object of the class. if omitted an empty object is used instead.
 * properties named "classname", "superclass" and "constructor"? are reserved
 * and should not used in this object.
 * 
 * statics: an object that specifies the static methods and properties for the
 * class. the properties of this object become properties of the constructor
 * function. if omitted an empty object is used.
 * 
 * borrows: a constructor function or array of constructor functions. the 
 * instance methods of each of the specified classes are copied into the 
 * prototype object of this new class so that the new class boroows the methods
 * of each specified class. constructors are processed in the order they are 
 * specified so a class at the end of the list can override methods of one at
 * the begining. borrowed methods are copied in the prototype before the 
 * methods, so methods can override borrowed. if omitted no properties are 
 * borrowed.
 * 
 * provides: a constructor function or array of constructor functions. after
 * the prototype object is fully initialized, this function verifies that the
 * prototype includes methods whose name and number of args match the instance 
 * methods defined by each of these classes. No methods are copied; this is 
 * simply an assertion that this class "provides" the functionality of the
 * specified clases. if it fails, this method will throw an exception. if no
 * exception is thrown, any instance of this new classcan also be considered
 * to be an instanceof these other types (by "duck typing"). if omitted, no
 * verrification is performed.    
 */  
org.berthomg.Class.defineClass = function (/*object*/ data) {
    //extract the fields from arg object
    var classname = data.name;
    var superclass = data.extend || Object;
    var constructor = data.construct || function() {};
    var methods = data.methods || {};
    var statics = data.statics || {};
    var borrows;
    var provides;
    
    //borrows may be constructor or array of them
    if(!data.borrows) borrows = [];
    else if(data.borrows instanceof Array) borrows = data.borrows;
    else borrows = [ data.borrows ];
    
    //provides may be constructor or array of them
    if(!data.provides) provides = [];
    else if(data.provides instanceof Array) provides = data.provides;
    else provides = [ data.provides ];
    
    //create the object that will be the prototype of our class
    var proto = new superclass();
    
    //delete any non-herited properties of this new prototype object
    for(var p in proto)
        if(proto.hasOwnProperty(p)) delete proto[p];
    
    //borrow methods from mixin classes by copying to our new prototype
    for(var i=0; i < borrows.length ; i++) {
        var c = data.borrows[i];
        borrows[i] = c;
        //copy method properties from prototype of c to our prototype
        for(var p in c.prototype) {
            if(typeof c.prototype[p] != 'function') continue;
            proto[p] = c.prototype[p];
        }
    }
    
    //copy the instance methods to the prototype object
    //this may override methods of the mixin classes
    for(var p in methods) proto[p] = methods[p];
    
    //<GB>
    var parentize = function(previous_, current_){
		if (!previous_.apply || !current_.apply) return false;
		return function(){
			this._super_ = previous_;
			return current_.apply(this, arguments);
		};
	};
	var pr0t0typ3 = new superclass();
	for(var p in proto){
		var previous_ = pr0t0typ3[p];
        var current_ = proto[p];
		if (previous_ && previous_ != current_) {
            current_ = parentize(previous_, current_) || current_;
        }
		pr0t0typ3[p] = current_;
	}
	proto = pr0t0typ3;
	//</GB>
    
    //setup the reserved "constructor", "superclass" and "classname" properties
    //of the prototype
    proto.constructor = constructor;
    proto.superclass = superclass;
    //set classname if its specified
    if(classname) proto.classname = classname;
    
    //verrify that our prototype provides all of the methods it is supposed to.
    for(var i=0; i < provides.length; i++) {
        var c = provides[i];
        for(var p in c.prototype) {
            if(typeof c.prototype[p] != 'function') continue;
            if(p == 'constructor' || p == 'superclass') continue;
            //check the method with same name and number of args
            if( p in proto &&
                typeof proto[p] == 'function' &&
                proto[p].length == c.prototype[p].length) continue;
            //otherwise throw an exception
            throw new Error("Class " + classname + " does not provide method " +
                c.classname + "." + p);
        }
    }
    
    //associate the prototype object with the constructor function
    constructor.prototype = proto;
    
    //copy static properties to the constuctor
    for(var p in statics) constructor[p] = data.statics[p];
    
    //return the constructor function
    return constructor;
};
/*
   GB
   
   http://www.jibbering.com/faq/faq_notes/closures.html
   
   A general function that associates an object instance with an event
   handler. The returned inner function is used as the event handler.
   The object instance is passed as the - obj - parameter and the name
   of the method that is to be called on that object is passed as the -
   methodName - (string) parameter.
   
   example:
   
   //This constructor function creates objects that associates themselves
   //with DOM elements whose IDs are passed to the constructor as a
   //string. The object instances want to arrange than when the
   //corresponding element triggers onclick, onmouseover and onmouseout
   //events corresponding methods are called on their object instance.

function DhtmlObject(elementId){
    // A function is called that retrieves a reference to the DOM
      // element (or null if it cannot be found) with the ID of the
     //  required element passed as its argument. The returned value
    //   is assigned to the local variable - el -:-
    
    var el = getElementWithId(elementId);
    //The value of - el - is internally type-converted to boolean for
    //   the - if - statement so that if it refers to an object the
     //  result will be true, and if it is null the result false. So that
      // the following block is only executed if the - el - variable
      // refers to a DOM element:-
    
    if(el){
//         To assign a function as the element's event handler this
//           object calls the - associateObjWithEvent - function
//           specifying itself (with the - this - keyword) as the object
//           on which a method is to be called and providing the name of
//           the method that is to be called. The - associateObjWithEvent
//           - function will return a reference to an inner function that
//           is assigned to the event handler of the DOM element. That
//           inner function will call the required method on the
//           javascript object when it is executed in response to
//           events:-
        
        el.onclick = associateObjWithEvent(this, "doOnClick");
        el.onmouseover = associateObjWithEvent(this, "doMouseOver");
        el.onmouseout = associateObjWithEvent(this, "doMouseOut");
        ...
    }
}
DhtmlObject.prototype.doOnClick = function(event, element){
    ... // doOnClick method body.
}
DhtmlObject.prototype.doMouseOver = function(event, element){
    ... // doMouseOver method body.
}
DhtmlObject.prototype.doMouseOut = function(event, element){
    ... // doMouseOut method body.
}
*/
org.berthomg.Class.associateObjWithEvent = function (obj, methodName) {
    /* The returned inner function is intended to act as an event
       handler for a DOM element:-
    */
    return (function(e) {
        /* The event object that will have been parsed as the - e -
           parameter on DOM standard browsers is normalised to the IE
           event object if it has not been passed as an argument to the
           event handling inner function:-
        */
        e = e || window.event;
        /* The event handler calls a method of the object - obj - with
           the name held in the string - methodName - passing the now
           normalised event object and a reference to the element to
           which the event handler has been assigned using the - this -
           (which works because the inner function is executed as a
           method of that element because it has been assigned as an
           event handler):-
        */
        return obj[methodName](e, this);
    });
};
/**
 * return true if o has methods with the same name an arity as all methods 
 * in c.prototype. Otherwise return false. Throws an exception if c is a
 * built-in type with non-enumerable methods.  
 * the options is a simple object with a boolean 'arity';
 *  
 * by default this function does not test arity, for convenience of flexibility
 */
org.berthomg.Class.provides = function (/*object*/ o, /*object*/ c, /*object*/ options) {
    options = options || {arity: false};
    if(o instanceof c) return true;
    //if a contructor was passed instead of an object, we use its prototype
    if(typeof o == 'function') o = o.prototype;
    //see borrows above
    if(c == Array || c == Boolean || c == Date || c == Error || c == Function ||
       c == Number || c == RegExp || c == String)
       return undefined;
    var proto = c.prototype;
    for(var p in proto) {
        //ignore other than function
        if(typeof proto[p] != 'function') continue;
        //if o does not have a property by the same name return false
        if(!(p in o)) return false;
        //if that property is not a function return false
        if(typeof o[p] != 'function') return false;
        //if the number of args declared is different, return false
        if(options.arity)
            if(o[p].length != proto[p].length) return false;
    } 
    return true;
};
/**
 * testing for array-like objects
 */ 
org.berthomg.Class.isArrayLike = function (/*object*/ x) {
    if(x instanceof Array) return true;
    if(!('length' in x)) return false;//must have a length property
    if(typeof x.length != 'number') return false;//length must be a number
    if(x.length < 0) return false;//not negative
    if(x.length > 0) {
        //if not empty, it must at a minimum have a property definedwhose name 
        //is the number length-1
        if(!((x.length-1) in x)) return false;
    }
    return true;
};
/**
 * return true if each of the method properties in c.prototype have been 
 * borrowed by o. If o is a function rather than an object, we test the 
 * prototype of o rather than itself. Note that this function requires methods
 * to be copied, not implemented. If a class  borrows a method and then 
 * overrides it, this method will return false.
 * this function is more strict than provides, see after. 
 */  
org.berthomg.Class.borrows = function (/*object*/ o, /*object*/ c) {
    if(o instanceof c) return true;
    //it is impossible to test wether the methods of a buit-in type have been
    //borrowed, since the methods of buit-in types are not enumerable.
    //return undefined in this case and can be distinguished from false if 
    //necessary.
    if(c == Array || c == Boolean || c == Date || c == Error || c == Function ||
       c == Number || c == RegExp || c == String)
       return undefined;
    //if a contructor was passed instead of an object, we use its prototype
    if(typeof o == 'function') o = o.prototype;
    var proto = c.prototype;
    for(var p in proto) {
        //ignore other than function
        if(typeof proto[p] != 'function') continue;
        if(o[p] != proto[p]) return false;
    } 
    return true;
};
/**
 * this function adds property accessor methods for a property with
 * the specified name to the object o. The methods are named get<name> and
 * set<name>. If a predicate function is supplied, the setter method uses it 
 * to test its argument for validity before storing it. If the predicate 
 * returns false, the setter method throws an exception.
 * 
 * The unusual thing about this function is thet the property value that is
 * manipulated by the getter and setter methods is not stored in the object o
 * Instead the value is stored only in a local variable in this function and
 * therefore have access to this local variable. Note that the value is private
 * to the two accessor methods, and it cannot be set or modified except through
 * the setter.
 */
org.berthomg.Class.makeProperty = function (/*object*/ o, /*string*/ name_, /*boolean function*/ predicate) {
    var value_; //this is the property value.
    //getter simply return the value.
    o['get' + name_] = function() { return value_; };
    //setter store the value or throws an Exception if the predicate reject
    //the value.
    o['set' + name_] = function(v) {
        if(predicate && !predicate(v))
            throw new Error("set" + name_ + ": invalid value " + v);
        else
            value_ = v;
    };
};
/**
 * function utility function
 * return a standalone function that invokes the function f as a method of
 * the object o. this is useful when you need to pass a method to a function.
 * if you don't bind it to its object, the association will be lost and the
 * method you passed will be invoked as a regular function. 
 */  
org.berthomg.Class.bindMethod = function (/*object*/ o, /*function*/ f) {
    return function() { return f.apply(o, arguments) }
};
/**
 * object utility function
 * return an array that holds the names of the enumerable properties of o.
 */  
org.berthomg.Class.getPropertyNames = function (/*Object*/ o) {
    var r = [];
    for(p in o) r.push(p);
    return r;
};
/**
 * object utility function
 * copy the enumerable properties of the object from to the object to
 * if to is null, a new object is created. The function returns to or
 * the newly created object. 
 */
org.berthomg.Class.copyProperties = function (/*Object*/ from, /*optional object*/ to) {
    if(!to) to = {};
    for(p in from) to[p] = from[p];
    return to;
};
/**
 * object utility function
 * Copy the enumerable properties of the object from to the object to,
 * but only the ones that are not defined by to. 
 */ 
org.berthomg.Class.copyUndefinedProperties = function (/*Object*/ from, /*object*/ to) {
    for(p in from) {
        if(!p in to) to[p] = from[p];
    }
};
/**
 ** object utility function
 * normally get the classname 
 **/
org.berthomg.Class.isA = function (obj, classname) {
    if (typeof obj != "object" || obj === null) return false;
    return (typeof obj == typeof eval(classname));
};
//------------------------------------------------------------------------------
// ===================================================================
// Author: Matt Kruse <matt@mattkruse.com>
// WWW: http://www.mattkruse.com/
//
// NOTICE: You may use this code for any purpose, commercial or
// private, without any further permission from the author. You may
// remove this notice from your final code if you wish, however it is
// appreciated by the author if at least my web site address is kept.
//
// You may *NOT* re-distribute this code in any way except through its
// use. That means, you can include it in your product, or your web
// site, or any other form where the code is actually being used. You
// may not put the plain javascript up on your site for download or
// include it in your javascript libraries for download. 
// If you wish to share this code with others, please just point them
// to the URL instead.
// Please DO NOT link directly to my .js files from your site. Copy
// the files to your server and use them there. Thank you.
// ===================================================================
org.berthomg.createNamespace('org.berthomg.Color', 0.1);

/* SOURCE FILE: AnchorPosition.js */

/* 
AnchorPosition.js
Author: Matt Kruse
Last modified: 10/11/02

DESCRIPTION: These functions find the position of an <A> tag in a document,
so other elements can be positioned relative to it.

COMPATABILITY: Netscape 4.x,6.x,Mozilla, IE 5.x,6.x on Windows. Some small
positioning errors - usually with Window positioning - occur on the 
Macintosh platform.

FUNCTIONS:
org.berthomg.Color.getAnchorPosition(anchorname)
  Returns an Object() having .x and .y properties of the pixel coordinates
  of the upper-left corner of the anchor. Position is relative to the PAGE.

org.berthomg.Color.getAnchorWindowPosition(anchorname)
  Returns an Object() having .x and .y properties of the pixel coordinates
  of the upper-left corner of the anchor, relative to the WHOLE SCREEN.

NOTES:

1) For popping up separate browser windows, use org.berthomg.Color.getAnchorWindowPosition. 
   Otherwise, use org.berthomg.Color.getAnchorPosition

2) Your anchor tag MUST contain both NAME and ID attributes which are the 
   same. For example:
   <A NAME="test" ID="test"> </A>

3) There must be at least a space between <A> </A> for IE5.5 to see the 
   anchor tag correctly. Do not do <A></A> with no space.
*/ 

//   This function returns an object having .x and .y properties which are the coordinates
//   of the named anchor, relative to the page.
org.berthomg.Color.getAnchorPosition = function(anchorname) {
	// This function will return an Object with x and y properties
	var useWindow=false;
	var coordinates=new Object();
	var x=0,y=0;
	// Browser capability sniffing
	var use_gebi=false, use_css=false, use_layers=false;
	if (document.getElementById) { use_gebi=true; }
	else if (document.all) { use_css=true; }
	else if (document.layers) { use_layers=true; }
	// Logic to find position
 	/*
    if (use_gebi && document.all) {
		x=org.berthomg.Color.AnchorPosition_getPageOffsetLeft(document.all[anchorname]);
		y=org.berthomg.Color.AnchorPosition_getPageOffsetTop(document.all[anchorname]);
	} else 
    */
    if (use_gebi) {
		var o=document.getElementById(anchorname);
		x=org.berthomg.Color.AnchorPosition_getPageOffsetLeft(o);
		y=org.berthomg.Color.AnchorPosition_getPageOffsetTop(o);
	} else if (use_css) {
		x=org.berthomg.Color.AnchorPosition_getPageOffsetLeft(document.all[anchorname]);
		y=org.berthomg.Color.AnchorPosition_getPageOffsetTop(document.all[anchorname]);
	} else if (use_layers) {
		var found=0;
		for (var i=0; i<document.anchors.length; i++) {
			if (document.anchors[i].name==anchorname) { found=1; break; }
			}
		if (found==0) {
			coordinates.x=0; coordinates.y=0; return coordinates;
			}
		x=document.anchors[i].x;
		y=document.anchors[i].y;
	} else {
		coordinates.x=0; coordinates.y=0; return coordinates;
	}
	coordinates.x=x;
	coordinates.y=y;
	return coordinates;
};

// org.berthomg.Color.getAnchorWindowPosition(anchorname)
//   This function returns an object having .x and .y properties which are the coordinates
//   of the named anchor, relative to the window
org.berthomg.Color.getAnchorWindowPosition = function(anchorname) {
	var coordinates=org.berthomg.Color.getAnchorPosition(anchorname);
	var x=0;
	var y=0;
	if (document.getElementById) {
		if (isNaN(window.screenX)) {
			x=coordinates.x-document.body.scrollLeft+window.screenLeft;
			y=coordinates.y-document.body.scrollTop+window.screenTop;
			}
		else {
			x=coordinates.x+window.screenX+(window.outerWidth-window.innerWidth)-window.pageXOffset;
			y=coordinates.y+window.screenY+(window.outerHeight-24-window.innerHeight)-window.pageYOffset;
			}
		}
	else if (document.all) {
		x=coordinates.x-document.body.scrollLeft+window.screenLeft;
		y=coordinates.y-document.body.scrollTop+window.screenTop;
		}
	else if (document.layers) {
		x=coordinates.x+window.screenX+(window.outerWidth-window.innerWidth)-window.pageXOffset;
		y=coordinates.y+window.screenY+(window.outerHeight-24-window.innerHeight)-window.pageYOffset;
		}
	coordinates.x=x;
	coordinates.y=y;
	return coordinates;
	}

// Functions for IE to get position of an object
org.berthomg.Color.AnchorPosition_getPageOffsetLeft = function(el) {
    var ol=el.offsetLeft;
	while ((el=el.offsetParent) != null) { ol += el.offsetLeft; }
	return ol;
};

org.berthomg.Color.AnchorPosition_getWindowOffsetLeft = function(el) {
	return org.berthomg.Color.AnchorPosition_getPageOffsetLeft(el)-document.body.scrollLeft;
};
	
org.berthomg.Color.AnchorPosition_getPageOffsetTop = function(el) {
	var ot=el.offsetTop;
	while((el=el.offsetParent) != null) { ot += el.offsetTop; }
	return ot;
};

org.berthomg.Color.AnchorPosition_getWindowOffsetTop = function(el) {
	return org.berthomg.Color.AnchorPosition_getPageOffsetTop(el)-document.body.scrollTop;
};

/* SOURCE FILE: PopupWindow.js */

/* 
PopupWindow.js
Author: Matt Kruse
Last modified: 02/16/04

DESCRIPTION: This object allows you to easily and quickly popup a window
in a certain place. The window can either be a DIV or a separate browser
window.

COMPATABILITY: Works with Netscape 4.x, 6.x, IE 5.x on Windows. Some small
positioning errors - usually with Window positioning - occur on the 
Macintosh platform. Due to bugs in Netscape 4.x, populating the popup 
window with <STYLE> tags may cause errors.

USAGE:
// Create an object for a WINDOW popup
var win = new PopupWindow(); 

// Create an object for a DIV window using the DIV named 'mydiv'
var win = new PopupWindow('mydiv'); 

// Set the window to automatically hide itself when the user clicks 
// anywhere else on the page except the popup
win.autoHide(); 

// Show the window relative to the anchor name passed in
win.showPopup(anchorname);

// Hide the popup
win.hidePopup();

// Set the size of the popup window (only applies to WINDOW popups
win.setSize(width,height);

// Populate the contents of the popup window that will be shown. If you 
// change the contents while it is displayed, you will need to refresh()
win.populate(string);

// set the URL of the window, rather than populating its contents
// manually
win.setUrl("http://www.site.com/");

// Refresh the contents of the popup
win.refresh();

// Specify how many pixels to the right of the anchor the popup will appear
win.offsetX = 50;

// Specify how many pixels below the anchor the popup will appear
win.offsetY = 100;

NOTES:
1) Requires the functions in AnchorPosition.js

2) Your anchor tag MUST contain both NAME and ID attributes which are the 
   same. For example:
   <A NAME="test" ID="test"> </A>

3) There must be at least a space between <A> </A> for IE5.5 to see the 
   anchor tag correctly. Do not do <A></A> with no space.

4) When a PopupWindow object is created, a handler for 'onmouseup' is
   attached to any event handler you may have already defined. Do NOT define
   an event handler for 'onmouseup' after you define a PopupWindow object or
   the autoHide() will not work correctly.
*/ 

// Set the position of the popup window based on the anchor
org.berthomg.Color.PopupWindow_getXYPosition = function(anchorname) {
	var coordinates;
	if (this.type == "WINDOW") {
		coordinates = org.berthomg.Color.getAnchorWindowPosition(anchorname);
	} else {
		coordinates = org.berthomg.Color.getAnchorPosition(anchorname);
	}
	this.x = coordinates.x;
	this.y = coordinates.y;
};
// Set width/height of DIV/popup window
org.berthomg.Color.PopupWindow_setSize = function(width,height) {
	this.width = width;
	this.height = height;
	}
// Fill the window with contents
org.berthomg.Color.PopupWindow_populate = function(contents) {
	this.contents = contents;
	this.populated = false;
	}
// Set the URL to go to
org.berthomg.Color.PopupWindow_setUrl = function(url) {
	this.url = url;
	}
// Set the window popup properties
org.berthomg.Color.PopupWindow_setWindowProperties = function(props) {
	this.windowProperties = props;
	}
// Refresh the displayed contents of the popup
org.berthomg.Color.PopupWindow_refresh = function() {
	if (this.divName != null) {
		// refresh the DIV object
		if (this.use_gebi) {
			document.getElementById(this.divName).innerHTML = this.contents;
		} else if (this.use_css) { 
			document.all[this.divName].innerHTML = this.contents;
		} else if (this.use_layers) { 
			var d = document.layers[this.divName]; 
			d.document.open();
			d.document.writeln(this.contents);
			d.document.close();
		}
	} else {
		if (this.popupWindow != null && !this.popupWindow.closed) {
			if (this.url!="") {
				this.popupWindow.location.href=this.url;
			} else {
				this.popupWindow.document.open();
				this.popupWindow.document.writeln(this.contents);
				this.popupWindow.document.close();
			}
			this.popupWindow.focus();
		}
	}
};
// Position and show the popup, relative to an anchor object
org.berthomg.Color.PopupWindow_showPopup = function(anchorname) {
	this.getXYPosition(anchorname);
	this.x += this.offsetX;
	this.y += this.offsetY;
	if (!this.populated && (this.contents != "")) {
		this.populated = true;
		this.refresh();
	}
	if (this.divName != null) {
		// Show the DIV object
		if (this.use_gebi) {
		    var o = document.getElementById(this.divName);
			o.style.left = this.x + "px";
			o.style.top = this.y + "px";
			o.style.visibility = "visible";
			o.style.zIndex = 10;
		} else if (this.use_css) {
			document.all[this.divName].style.left = this.x;
			document.all[this.divName].style.top = this.y;
			document.all[this.divName].style.visibility = "visible";
		} else if (this.use_layers) {
			document.layers[this.divName].left = this.x;
			document.layers[this.divName].top = this.y;
			document.layers[this.divName].visibility = "visible";
		}
	} else {
		if (this.popupWindow == null || this.popupWindow.closed) {
			// If the popup window will go off-screen, move it so it doesn't
			if (this.x<0) { this.x=0; }
			if (this.y<0) { this.y=0; }
			if (screen && screen.availHeight) {
				if ((this.y + this.height) > screen.availHeight) {
					this.y = screen.availHeight - this.height;
				}
			}
			if (screen && screen.availWidth) {
				if ((this.x + this.width) > screen.availWidth) {
					this.x = screen.availWidth - this.width;
				}
			}
			var avoidAboutBlank = window.opera || ( document.layers && !navigator.mimeTypes['*'] ) || navigator.vendor == 'KDE' || ( document.childNodes && !document.all && !navigator.taintEnabled );
			this.popupWindow = window.open(avoidAboutBlank?"":"about:blank","window_"+anchorname,this.windowProperties+",width="+this.width+",height="+this.height+",screenX="+this.x+",left="+this.x+",screenY="+this.y+",top="+this.y+"");
		}
		this.refresh();
	}
};
// Hide the popup
org.berthomg.Color.PopupWindow_hidePopup = function() {
	if (this.divName != null) {
		if (this.use_gebi) {
			document.getElementById(this.divName).style.visibility = "hidden";
		} else if (this.use_css) {
			document.all[this.divName].style.visibility = "hidden";
		} else if (this.use_layers) {
			document.layers[this.divName].visibility = "hidden";
		}
	} else {
		if (this.popupWindow && !this.popupWindow.closed) {
			this.popupWindow.close();
			this.popupWindow = null;
		}
	}
};
// Pass an event and return whether or not it was the popup DIV that was clicked
org.berthomg.Color.PopupWindow_isClicked = function(e) {
	if (this.divName != null) {
		if (this.use_layers) {
			var clickX = e.pageX;
			var clickY = e.pageY;
			var t = document.layers[this.divName];
			if ((clickX > t.left) && (clickX < t.left+t.clip.width) && (clickY > t.top) && (clickY < t.top+t.clip.height)) {
				return true;
				}
			else { return false; }
			}
		else if (document.all) { // Need to hard-code this to trap IE for error-handling
			var t = window.event.srcElement;
			while (t.parentElement != null) {
				if (t.id==this.divName) {
					return true;
					}
				t = t.parentElement;
				}
			return false;
			}
		else if (this.use_gebi && e) {
			var t = e.originalTarget;
			while (t.parentNode != null) {
				if (t.id==this.divName) {
					return true;
					}
				t = t.parentNode;
				}
			return false;
			}
		return false;
		}
	return false;
	}

// Check an onMouseDown event to see if we should hide
org.berthomg.Color.PopupWindow_hideIfNotClicked = function(e) {
	if (this.autoHideEnabled && !this.isClicked(e)) {
		this.hidePopup();
		}
	}
// Call this to make the DIV disable automatically when mouse is clicked outside it
org.berthomg.Color.PopupWindow_autoHide = function() {
	this.autoHideEnabled = true;
	}
// This global function checks all PopupWindow objects onmouseup to see if they should be hidden
org.berthomg.Color.PopupWindow_hidePopupWindows = function(e) {
	for (var i=0; i<org.berthomg.Color.popupWindowObjects.length; i++) {
		if (org.berthomg.Color.popupWindowObjects[i] != null) {
			var p = org.berthomg.Color.popupWindowObjects[i];
			p.hideIfNotClicked(e);
			}
		}
	}
// Run this immediately to attach the event listener
org.berthomg.Color.PopupWindow_attachListener = function() {
	if (document.layers) {
		document.captureEvents(Event.MOUSEUP);
		}
	org.berthomg.Color.popupWindowOldEventListener = document.onmouseup;
	if (org.berthomg.Color.popupWindowOldEventListener != null) {
		document.onmouseup = new Function("org.berthomg.Color.popupWindowOldEventListener(); org.berthomg.Color.PopupWindow_hidePopupWindows();");
		}
	else {
		document.onmouseup = org.berthomg.Color.PopupWindow_hidePopupWindows;
		}
	}
// CONSTRUCTOR for the PopupWindow object
// Pass it a DIV name to use a DHTML popup, otherwise will default to window popup
org.berthomg.Color.PopupWindow = function() {
	if (!org.berthomg.Color.popupWindowIndex) { org.berthomg.Color.popupWindowIndex = 0; }
	if (!org.berthomg.Color.popupWindowObjects) { org.berthomg.Color.popupWindowObjects = new Array(); }
	if (!org.berthomg.Color.listenerAttached) {
		org.berthomg.Color.listenerAttached = true;
		org.berthomg.Color.PopupWindow_attachListener();
		}
	this.index = org.berthomg.Color.popupWindowIndex++;
	org.berthomg.Color.popupWindowObjects[this.index] = this;
	this.divName = null;
	this.popupWindow = null;
	this.width=0;
	this.height=0;
	this.populated = false;
	this.visible = false;
	this.autoHideEnabled = false;
	
	this.contents = "";
	this.url="";
	this.windowProperties="toolbar=no,location=no,status=no,menubar=no,scrollbars=auto,resizable,alwaysRaised,dependent,titlebar=no";
	if (arguments.length>0) {
		this.type="DIV";
		this.divName = arguments[0];
		}
	else {
		this.type="WINDOW";
		}
	this.use_gebi = false;
	this.use_css = false;
	this.use_layers = false;
	if (document.getElementById) { this.use_gebi = true; }
	else if (document.all) { this.use_css = true; }
	else if (document.layers) { this.use_layers = true; }
	else { this.type = "WINDOW"; }
	this.offsetX = 0;
	this.offsetY = 0;
	// Method mappings
	this.getXYPosition         = org.berthomg.Color.PopupWindow_getXYPosition;
	this.populate              = org.berthomg.Color.PopupWindow_populate;
	this.setUrl                = org.berthomg.Color.PopupWindow_setUrl;
	this.setWindowProperties   = org.berthomg.Color.PopupWindow_setWindowProperties;
	this.refresh               = org.berthomg.Color.PopupWindow_refresh;
	this.showPopup             = org.berthomg.Color.PopupWindow_showPopup;
	this.hidePopup             = org.berthomg.Color.PopupWindow_hidePopup;
	this.setSize               = org.berthomg.Color.PopupWindow_setSize;
	this.isClicked             = org.berthomg.Color.PopupWindow_isClicked;
	this.autoHide              = org.berthomg.Color.PopupWindow_autoHide;
	this.hideIfNotClicked      = org.berthomg.Color.PopupWindow_hideIfNotClicked;
	}

/* SOURCE FILE: ColorPicker2.js */

/* 
Last modified: 02/24/2003

DESCRIPTION: This widget is used to select a color, in hexadecimal #RRGGBB 
form. It uses a color "swatch" to display the standard 216-color web-safe 
palette. The user can then click on a color to select it.

COMPATABILITY: See notes in AnchorPosition.js and PopupWindow.js.
Only the latest DHTML-capable browsers will show the color and hex values
at the bottom as your mouse goes over them.

USAGE:
// Create a new ColorPicker object using DHTML popup
var cp = new ColorPicker();

// Create a new ColorPicker object using Window Popup
var cp = new ColorPicker('window');

// Add a link in your page to trigger the popup. For example:
<A HREF="#" onClick="cp.show('pick');return false;" NAME="pick" ID="pick">Pick</A>

// Or use the built-in "select" function to do the dirty work for you:
<A HREF="#" onClick="cp.select(document.forms[0].color,'pick');return false;" NAME="pick" ID="pick">Pick</A>

// If using DHTML popup, write out the required DIV tag near the bottom
// of your page.
<SCRIPT LANGUAGE="JavaScript">cp.writeDiv()</SCRIPT>

// Write the 'pickColor' function that will be called when the user clicks
// a color and do something with the value. This is only required if you
// want to do something other than simply populate a form field, which is 
// what the 'select' function will give you.
function pickColor(color) {
	field.value = color;
	}

NOTES:
1) Requires the functions in AnchorPosition.js and PopupWindow.js

2) Your anchor tag MUST contain both NAME and ID attributes which are the 
   same. For example:
   <A NAME="test" ID="test"> </A>

3) There must be at least a space between <A> </A> for IE5.5 to see the 
   anchor tag correctly. Do not do <A></A> with no space.

4) When a ColorPicker object is created, a handler for 'onmouseup' is
   attached to any event handler you may have already defined. Do NOT define
   an event handler for 'onmouseup' after you define a ColorPicker object or
   the color picker will not hide itself correctly.
*/ 
org.berthomg.Color.ColorPicker_targetInput = null;

org.berthomg.Color.ColorPicker_writeDiv = function() {
	document.writeln("<DIV ID=\"colorPickerDiv\" STYLE=\"position:absolute;visibility:hidden;\"> </DIV>");
};

org.berthomg.Color.ColorPicker_show = function(anchorname) {
	this.showPopup(anchorname);
};

org.berthomg.Color.ColorPicker_pickColor = function(color,obj) {
	obj.hidePopup();
	org.berthomg.Color.pickColor(color);
};

// A Default "pickColor" org.berthomg.Color.to accept the color passed back from popup.
// User can over-ride this with their own function.
org.berthomg.Color.pickColor = function(color) {
	if (org.berthomg.Color.ColorPicker_targetInput==null) {
		alert("Target Input is null, which means you either didn't use the 'select' function or you have no defined your own 'pickColor' function to handle the picked color!");
		return;
	}
	org.berthomg.Color.ColorPicker_targetInput.value = color;
};

// This function is the easiest way to popup the window, select a color, and
// have the value populate a form field, which is what most people want to do.
org.berthomg.Color.ColorPicker_select = function(inputobj,linkname) {
	if (inputobj.type!="text" && inputobj.type!="hidden" && inputobj.type!="textarea") { 
		alert("colorpicker.select: Input object passed is not a valid form input object"); 
		org.berthomg.Color.ColorPicker_targetInput=null;
		return;
		}
	org.berthomg.Color.ColorPicker_targetInput = inputobj;
	this.show(linkname);
	}
	
// This function runs when you move your mouse over a color block, if you have a newer browser
org.berthomg.Color.ColorPicker_highlightColor = function(c) {
	var thedoc = (arguments.length>1)?arguments[1]:window.document;
	var d = thedoc.getElementById("colorPickerSelectedColor");
	d.style.backgroundColor = c;
	d = thedoc.getElementById("colorPickerSelectedColorValue");
	d.innerHTML = c;
	}

org.berthomg.Color.ColorPicker = function() {
	var windowMode = false;
	// Create a new PopupWindow object
	if (arguments.length==0) {
		var divname = "colorPickerDiv";
	} else if (arguments[0] == "window") {
		var divname = '';
		windowMode = true;
	} else {
		var divname = arguments[0];
	}
	
	if (divname != "") {
		var cp = new org.berthomg.Color.PopupWindow(divname);
	} else {
		var cp = new org.berthomg.Color.PopupWindow();
		cp.setSize(225,250);
	}

	// Object variables
	cp.currentValue = "#FFFFFF";
	
	// Method Mappings
	cp.writeDiv        = org.berthomg.Color.ColorPicker_writeDiv;
	cp.highlightColor  = org.berthomg.Color.ColorPicker_highlightColor;
	cp.show            = org.berthomg.Color.ColorPicker_show;
	cp.select          = org.berthomg.Color.ColorPicker_select; 

	// Code to populate color picker window
	var colors = new Array("#000000","#000033","#000066","#000099","#0000CC","#0000FF","#330000","#330033","#330066","#330099","#3300CC",
							"#3300FF","#660000","#660033","#660066","#660099","#6600CC","#6600FF","#990000","#990033","#990066","#990099",
							"#9900CC","#9900FF","#CC0000","#CC0033","#CC0066","#CC0099","#CC00CC","#CC00FF","#FF0000","#FF0033","#FF0066",
							"#FF0099","#FF00CC","#FF00FF","#003300","#003333","#003366","#003399","#0033CC","#0033FF","#333300","#333333",
							"#333366","#333399","#3333CC","#3333FF","#663300","#663333","#663366","#663399","#6633CC","#6633FF","#993300",
							"#993333","#993366","#993399","#9933CC","#9933FF","#CC3300","#CC3333","#CC3366","#CC3399","#CC33CC","#CC33FF",
							"#FF3300","#FF3333","#FF3366","#FF3399","#FF33CC","#FF33FF","#006600","#006633","#006666","#006699","#0066CC",
							"#0066FF","#336600","#336633","#336666","#336699","#3366CC","#3366FF","#666600","#666633","#666666","#666699",
							"#6666CC","#6666FF","#996600","#996633","#996666","#996699","#9966CC","#9966FF","#CC6600","#CC6633","#CC6666",
							"#CC6699","#CC66CC","#CC66FF","#FF6600","#FF6633","#FF6666","#FF6699","#FF66CC","#FF66FF","#009900","#009933",
							"#009966","#009999","#0099CC","#0099FF","#339900","#339933","#339966","#339999","#3399CC","#3399FF","#669900",
							"#669933","#669966","#669999","#6699CC","#6699FF","#999900","#999933","#999966","#999999","#9999CC","#9999FF",
							"#CC9900","#CC9933","#CC9966","#CC9999","#CC99CC","#CC99FF","#FF9900","#FF9933","#FF9966","#FF9999","#FF99CC",
							"#FF99FF","#00CC00","#00CC33","#00CC66","#00CC99","#00CCCC","#00CCFF","#33CC00","#33CC33","#33CC66","#33CC99",
							"#33CCCC","#33CCFF","#66CC00","#66CC33","#66CC66","#66CC99","#66CCCC","#66CCFF","#99CC00","#99CC33","#99CC66",
							"#99CC99","#99CCCC","#99CCFF","#CCCC00","#CCCC33","#CCCC66","#CCCC99","#CCCCCC","#CCCCFF","#FFCC00","#FFCC33",
							"#FFCC66","#FFCC99","#FFCCCC","#FFCCFF","#00FF00","#00FF33","#00FF66","#00FF99","#00FFCC","#00FFFF","#33FF00",
							"#33FF33","#33FF66","#33FF99","#33FFCC","#33FFFF","#66FF00","#66FF33","#66FF66","#66FF99","#66FFCC","#66FFFF",
							"#99FF00","#99FF33","#99FF66","#99FF99","#99FFCC","#99FFFF","#CCFF00","#CCFF33","#CCFF66","#CCFF99","#CCFFCC",
							"#CCFFFF","#FFFF00","#FFFF33","#FFFF66","#FFFF99","#FFFFCC","#FFFFFF");
	var total = colors.length;
	var width = 18;
	var cp_contents = "";
	var windowRef = (windowMode)?"window.opener.":"";
	if (windowMode) {
		cp_contents += "<HTML><HEAD><TITLE>Select Color</TITLE></HEAD>";
		cp_contents += "<BODY MARGINWIDTH=0 MARGINHEIGHT=0 LEFTMARGIN=0 TOPMARGIN=0><CENTER>";
	}
	cp_contents += "<TABLE BORDER=1 CELLSPACING=1 CELLPADDING=0>";
	var use_highlight = (document.getElementById || document.all)?true:false;
	for (var i=0; i<total; i++) {
		if ((i % width) == 0) { 
            cp_contents += "<TR>"; 
        }
		if (use_highlight) { 
            var mo = 'onMouseOver="'+windowRef+'org.berthomg.Color.ColorPicker_highlightColor(\''+colors[i]+'\',window.document)"'; 
        }
		else { 
            mo = ""; 
        }
		//cp_contents += '<TD BGCOLOR="'+colors[i]+'"><FONT SIZE="-3"><A HREF="#" onClick="'+windowRef+'org.berthomg.Color.ColorPicker_pickColor(\''+colors[i]+'\','+windowRef+'org.berthomg.Color.popupWindowObjects['+cp.index+']);return false;" '+mo+' STYLE="text-decoration:none;">&nbsp;&nbsp;&nbsp;</A></FONT></TD>';
		cp_contents += '<TD BGCOLOR="'+colors[i]+'"><FONT SIZE="-3"><SPAN onClick="'+windowRef+'org.berthomg.Color.ColorPicker_pickColor(\''+colors[i]+'\','+windowRef+'org.berthomg.Color.popupWindowObjects['+cp.index+']);return false;" '+mo+' STYLE="cursor: pointer;cursor: hand;">&nbsp;&nbsp;&nbsp;</A></FONT></TD>';
		if ( ((i+1)>=total) || (((i+1) % width) == 0)) { 
			cp_contents += "</TR>";
		}
	}
	// If the browser supports dynamically changing TD cells, add the fancy stuff
	if (document.getElementById) {
		var width1 = Math.floor(width/2);
		var width2 = width = width1;
		cp_contents += "<TR><TD COLSPAN='"+width1+"' BGCOLOR='#ffffff' ID='colorPickerSelectedColor'>&nbsp;</TD><TD COLSPAN='"+width2+"' ALIGN='CENTER' ID='colorPickerSelectedColorValue'>#FFFFFF</TD></TR>";
	}
	cp_contents += "</TABLE>";
	if (windowMode) {
		cp_contents += "</CENTER></BODY></HTML>";
	}
	// end populate code

	// Write the contents to the popup object
	cp.populate(cp_contents+"\n");
	// Move the table down a bit so you can see it
	cp.offsetY = 25;
	cp.autoHide();
	return cp;
};
//------------------------------------------------------------------------------
org.berthomg.createNamespace('org.berthomg.CSS', 0.1);
/**
 * the module declare a single global class named Shadows
 *
 * add(element, shadows) :
 * add the specified shadows to the element.the first argument is string_id or
 * element.must have a single text node child. child will be shadowed. see syntax
 * below
 * 
 * addAll(root, tagname) :
 * find all descendants of root that have tagname. if one of these eleemnts has
 * an attribute named shadow, then call the add method for the element  and the 
 * value of his shadow attribute if tagname not specidied check all elements.
 * if root omitted, document is used. 
 * should be called once after dom ready.
 * 
 * shadows are specified by strings:
 * [x y color]+
 * one or more triplets in legal css format
 * if more than one, the first shadow is on the bottom, overlapped by subsequent
 * shadows.    
 * example: "4px 4px #ccc 2px 2px #aaa"
 */  
org.berthomg.CSS.Shadows = new Class({
    initialize: function(options) {
        this.setOptions({
            test:'test'
		}, options);
    }
    ,
    add: function (element, shadows) {
        if(typeof element == 'string')
            element = document.getElementById(element);
        //split shdows string at white spaces with trimming
        shadows = shadows.replace(/^\s+/, '').replace(/\s+$/, '');
        var args = shadows.split(/\s+/);
        //for simplicity we only take the first text node
        var textnode = element.firstChild;
        
        //give the container element relative positioning, so that shadows
        //can be relatively positioned to it
        element.style.position = 'relative';
        
        //create shadows
        var numshadows = args.length/3;
        for(var i=0;i<numshadows;i++) {
            var shX = args[i*3];
            var shY = args[i*3 + 1];
            var shColor = args[i*3 + 2];
            
            //create a span to hold the shadow
            var shadow = document.createElement('span');
            shadow.setAttribute('style', 'position:absolute; '
                                + 'left:'+shX+';'
                                + 'top:'+shY+';'
                                + 'color:'+shColor+';'
            );
            
            //ad a copy of the textnode to this shadow span
            shadow.appendChild(textnode.cloneNode(false));
            //and add the span to conatiner
            element.appendChild(shadow);
        }
        
        //now put the text at top of the shadow
        var text = document.createElement('span');
        text.setAttribute('style', 'position:relative;')
        text.appendChild(textnode);
        element.appendChild(text);
    }
    ,
    addAll: function (root, tagname) {
        if(!root) root = document;
        if(!tagname) tagname = '*';//all tags
        
        var eles = root.getElementsByTagName(tagname);
        for(var i=0;i<eles.length;i++) {
            var shadow = eles[i].getAttribute('shadow');
            if(shadow) this.add(eles[i], shadow);
        }
    }
});
org.berthomg.CSS.Shadows.implement(new Options);
/**
 * create a tooltip object with the tooltip constructor. then make it visible 
 * with the show()/hide() methods.
 * 
 * .tooltipShadow {
 *  background: url(shadow.png);
 * }
 *
 * .tooltipContent {
 *  left: -4px; top: -4px; background-color: #ff0; border: solid black 1px;
 *  padding: 5px; font: bold 10pt sans-serif;
 *	} 
 *	
 *   
 */
org.berthomg.CSS.Tooltip = new Class({
	initialize: function() {
		this.tt = new Element('div');
		this.tt.style.position = 'absolute';
		this.tt.style.visibility = 'hidden';
		this.tt.style.className = 'tooltipShadow';
		
		this.content = new Element('div');
		this.content.style.position = 'relative';
		this.content.className = 'tooltipContent';
		
		this.tt.appendChild(this.content);
	}
	,
	show: function (text_, x, y) {
		this.content.innerHTML = text_;
		this.tt.style.left = x + 'px';
		this.tt.style.top = y + 'px';
		this.tt.style.visibility = 'visible';
		if(this.tt.parentNode != document.body)
			document.body.appendChild(this.tt);
	}
	,
	hide: function () {
		this.tt.style.visibility = 'hidden';
	}
	,
	schedule: function (target, e) {
        var text = target.getAttribute("tooltip");
        if(!text) return;
        
        var x = e.clientX + org.berthomg.Geometry.getHorizontalScroll();
        var y = e.clientY + org.berthomg.Geometry.getVerticalScroll();
        
        x += org.berthomg.CSS.Tooltip.X_OFFSET;
        y += org.berthomg.CSS.Tooltip.Y_OFFSET;
        
        var self = this;
        var timer = window.setTimeout(function() { self.show(text, x, y); }
            , org.berthomg.CSS.Tooltip.DELAY);
        if(target.addEventListener) target.addEventListener("mouseout",mouseout,false);
        else if (target.attachEvent) target.attachEvent("onmouseout", mouseout);
        else target.onmouseout = mouseout;
        
        function mouseout() {
            self.hide();
            window.clearTimeout(timer);
            if(target.removeEventListener)
                target.removeEventListener("mouseout", mouseout, false);
            else if (target.attachEvent) target.detachEvent("onmouseout", mouseout);
            else target.onmouseout = null;
        };
    }
});
org.berthomg.CSS.Tooltip.X_OFFSET = 25;//pixel at right of the mouse pointer
org.berthomg.CSS.Tooltip.Y_OFFSET = 15;//below
org.berthomg.CSS.Tooltip.DELAY = 500;//ms after mouseover
org.berthomg.CSS.Tooltip.tooltip = new org.berthomg.CSS.Tooltip();


/**
* framework for css-based animation.
*
* element: html element to be animated
* numFrames: total number of frames in the animation
* timePerFrame: the number of millisecs to display each frame
* animation: an object that defines an animation, see below
* whendone: an optional function to call when the animation finishes
*	    if specified, this function is passed element as its argument
*
* it is the properties of the animation object that specify the animation
* each property should have the same name as a CSS property. the value of each
* property must be a function that return value for this style property. each
* function is passed the frame number and the total amount of elapsed time, and 
* it can use these to compute the style value it slides in from the upper left, you might
* invoque animateCSS as follow:
*
* animateCSS(image, 25, 30  //animate image for 25 frames of 50ms each
* { //set top and left attributes for each frames as follow
* top: function (frame, time) { return frame*8 + 'px'; },
* left: function (frame, time) { return frame*8 + 'px'; }
* }
* );
*/
org.berthomg.CSS.animateCSS = function(element, numFrames, timePerFrame, animation, whendone) {
	var frame = 0; //current frame number
	var time = 0;//total elapsed time

	//arrange to call displayNextFRame() every timePerFRame ms.
	//this will display each of the frames of the animation
	var intervalId = setInterval(displayNextFrame, timePerFrame);
	
	//the call to animateCSS() returns now, but the previous line ensures
	//that the following nested function will be invoked once for each frame
	//of the animation
	function displayNextFrame() {
		if(frame >= numFrames) {//first see if we're done
			clearInterval(intervalId);//if so stop calling ourselves
			if(whendone) whendone(element);//invoke whendone function
			return;//we're finished
		}
		//now loop through all properties defined in the animation object
		for(var cssprop in animation) {
			//for each property, call its animation function, passing the
			//frame number and the elapsed time. Use the return value of the
			//function as the new value of the corresponding style property
			//of the specified element. Use try/catch to ignore any
			//exceptions caused by bad return values.
			try {
				element.style[cssprop] = animation[cssprop](frame, time);
			} catch(e) {
			}
		}
		
		frame++;
		time += timePerFrame;
	}
}; 

/**
* get the computed style of an element
* quirky, does not always provide the information you want
* 
* prop: is the CSS attribute in camelcase format (ie: 'fontFamily')
*/
org.berthomg.CSS.getComputedStyle = function (element, prop) {
	if(typeof element == 'string')
		element = document.getElementById(element);
	var cstyle;
	if(element.currentStyle)
		cstyle = element.currentStyle;
	else if (window.getComputedStyle)
		cstyle = window.getComputedStyle(element, null);
	return cstyle[prop];
};

/**
* utility to manipulate css class of an element
* this object contains utility funcs. all funciton take 2 args:
* the element and the classname to be tested, added or removed.
* if element is a string, we get element by id.
*/
org.berthomg.CSS.CSSClass = {};
//return true if e is a member of the class c; false otherwise
org.berthomg.CSS.CSSClass.is = function (e, c) {
	if(typeof e == 'string') e = document.getElementById(e);
	//optimize a couple of common cases
	var classes = e.className;
	if(!classes) return false;//not any class
	if(classes == c) return true;//just this class
	//otherwise use a regexp to search for c as word by itself
	// \b in a regular expression requires a match at a word boundary
	return e.className.search("\\b" + c + "\\b") != -1;
};
//add class c to the className of element e if it is not already there
org.berthomg.CSS.CSSClass.add = function (e, c) {
	if(typeof e == 'string') e = document.getElementById(e);
	if(org.berthomg.CSS.CSSClass.is(e,c)) return;//already have it
	if(e.className) c = " " + c;//space if needed
	e.className += c;//add it	
};
//remove all occurences if any
org.berthomg.CSS.CSSClass.remove = function (e, c) {
	if(typeof e == 'string') e = document.getElementById(e);
	//search the className for all occurences of c and replace it with ''
	// \s* matches any number of whitespaces chars.
	// 'g' makes the regexp match any number of occurences
	e.className = e.className.replace(new RegExp("\\b" + c + "\\b\\s*", "g"), "");
};
/**
 * utility class to enable CSS stylesheets
 * works for <link> and <style> 
 */
org.berthomg.CSS.enableCSS = function(sheetid, enabled) {
    document.getElementById(sheetid).disabled = !enabled;
};
/**
 * utility methods to script CSS stylesheets
 * 
 * this module defines a Stylesheet    class that is a simple wrapper
 * around an element of the document.styleSheets[] array. it defines useful
 * cross platform methods for querying and modify the stylesheet.
 */ 
/**
 * construct a new Stylesheet object that wraps the specified CSSStylesheet
 * if ss is a number, look up in the styleSheet array. 
 */ 
org.berthomg.CSS.Stylesheet = function (ss) {
    if(typeof ss == 'number') ss = document.styleSheets[ss];
    this.ss = ss;
}
//return the rules array of this stylesheet
org.berthomg.CSS.Stylesheet.prototype.getRules = function () {
    //w3c or IE property
    return this.ss.cssRules?this.ss.cssRules:this.ss.rules;
}
/**
 * return a rule of the stylesheet. if s is a number, we return the rule at
 * that index. otherwise we assume s as a selector and look for the rule
 * that matches that selector.  
 */ 
org.berthomg.CSS.Stylesheet.prototype.getRule = function (s) {
    var rules = this.getRules();
    if(!rules) return null;
    if(typeof s == 'number') return rules[s];
    //assume s is a selector. look backwards if more than one rule match s we 
    //find the one with the most precedence.
    s = s.toLowerCase();
    for(var i=rules.length-1 ; i >= 0 ; i--) {
        if(rules[i].selectorText.toLowerCase() == s) return rules[i];
    }
    return null;
}
/**
 * return CSS2Properties object for the specified rule. rules can be specified
 * by number or selector. 
 */ 
org.berthomg.CSS.Stylesheet.prototype.getStyles = function (s) {
    var rule = this.getRule(s);
    if(rule && rule.style) return rule.style;
    return null;
}
/**
 * return the syle text for the specified rule.
 */ 
org.berthomg.CSS.Stylesheet.prototype.getStyleText = function (s) {
    var rule = this.getRule(s);
    if(rule && rule.style && rule.style.cssText) return rule.style.cssText;
    return "";
}
/**
 * insert a rule into the stylesheet: selector and style string. inserted at
 * index n, if omitted at the end. 
 */ 
org.berthomg.CSS.Stylesheet.prototype.insertRule = function (selector, styles, n) {
    if(n == undefined) {
        var rules = this.getRules();
        n = rules.length;
    }
    if(this.ss.insertRule) //w3c
        this.ss.insertRule(selector + "{" + styles + "}", n);
    else if(this.ss.addRule) //IE
        this.ss.addRule(selector, styles, n);
}
/**
 * remove a rule at the specified position in the stylesheet.
 * if s is a number: delete at that position.
 * if s is a string: delete rule with that selector
 * if s not specified, delete the last rule in the stylesheet   
 */ 
org.berthomg.CSS.Stylesheet.prototype.deleteRule = function (s) {
    //if s undefined, make it the index of the last rule
    if(s == undefined) {
        var rules = this.getRules();
        s = rules.length - 1;
    }
    //if s not a number, look for matching rule and get the index
    if(typeof s != 'number') {
        s = s.toLowerCase();
        var rules = this.getRules();
        for(var i=rules.length-1 ; i >= 0 ; i--) {
            if(rules[i].selectorText.toLowerCase() == s) {
                s = i; //remember the index of the rule to delete and stop
                break;
            }
        }
        //if we didnot find a match give up
        if(i == -1) return;
    }
    
    //at this point s is anumber. try w3c first then IE
    if(this.ss.deleteRule) this.ss.deleteRule(s);
    else if(this.ss.removeRule) this.ss.removeRule(s);
}
//------------------------------------------------------------------------------
org.berthomg.createNamespace('org.berthomg.Dom', 0.1);
/**
 * GB
 * wrap a Element e around a Element wrapper
 */ 
org.berthomg.Dom.wrapElement = function(e, wrapper) {
    if(typeof e == 'string') e = document.getElementById(e);
    if(typeof wrapper == 'string') wrapper = document.getElementById(wrapper);
    
    //if both are node element and e is text or element
    if( (e.nodeType == 1 || e.nodeType == 3) && wrapper.nodeType == 1) {
        var parent = e.parentNode;//get the parent of the node
        parent.replaceChild(wrapper, e);//replace the node with the wrapper in the parent
        wrapper.appendChild(e);//make the node a child of the wrapper
    }
};
/**
 * GB
 * reverse the order of the children of node Element e
 */ 
org.berthomg.Dom.reverse = function(e) {
    if(typeof e == 'string') e = document.getElementById(e);
    //create an empty document fragment as container
    var f = document.createDocumentFragment();
    //loop backward to the children, move each one to the fragment
    //appending a child to f automatically remove it from n
    while(e.lastChild) f.appendChild(e.lastChild);
    //move all the children of f at once back to n.
    e.appendChild(f);
};
//------------------------------------------------------------------------------
org.berthomg.createNamespace('org.berthomg.Element', 0.1);
/**
 * create an html element with specified tagname, attributes and children
 * 
 * the attributes argument is a javascript object
 * if attributes is null, and children is an array or a string, the attribute 
 * can be omitted and the children parsed as second argument
 * 
 * the children args is normally an array of children. if no children, this args 
 * can be omitted. if only a single child, it can be parsed directly. but if
 * the child is not a string and no attributes  are specified, an array must be
 * used.
 * 
 * example:
 * make('p', [ 'this is a', 
 *              make( 'b' , 'bold' ),
 *              ' word.' 
 *           ]
 *      );    
 * 
 * inspired by mochikit library  
 */ 
org.berthomg.Element.make = function (tagname, attributes, children) {
    //if we were invoked with 2 args, the attribute argument is an array of 
    ///string; it should be the children arguments
    if(arguments.length == 2 &&
       (attributes instanceof Array || typeof attributes == 'string')) {
       children = attributes;
       attributes = null;   
    }
    
    //create the element
    var e = document.createElement(tagname);
    
    //set attributes
    if(attributes) {
        for(var p in attributes) {
            if(p == 'class') p = 'className';
            if(e.setAttribute) {
                e.setAttribute(p, attributes[p]);
            } else {
                e[p] = attributes[p];
            }
        }
    }
    
    //add children
    if(children != null) {
        if(children instanceof Array) {//if it's an array
            for(var i=0; i<children.length; i++) {
                var child = children[i];
                if(typeof child == 'string') {//handle text nodes
                    child = document.createTextNode(child);
                }
                e.appendChild(child);//assume anything else is a node
            }
        } else if(typeof children == 'string') {//handle single text child
            e.appendChild(document.createTextNode(children));
        } else {//handle any other single child
            e.appendChild(children);
        }
    }
    
    //return the element
    return e;
};
/**
 * return a function that call make() for the specified tag.
 * 
 * example:
 * var table = maker('table'), tr = maker('tr'), td = maker('td');   
 * 
 * var mytable = table({border:1}, tr( [ th('Name') , th('Firstname') ] ));  
 */ 
org.berthomg.Element.maker = function (tag) {
    return function(attrs, kids) {
        var make = org.berthomg.Element.make;
        if(arguments.length == 1) return make(tag, attrs);
        else return make(tag, attrs, kids);
    }
};
//------------------------------------------------------------------------------
// JavaScript Document
org.berthomg.createNamespace('org.berthomg.Error', 0.1);

/**
 * browser default error handler
 */ 
org.berthomg.Error.BROWSER_ERROR_HANDLER = function() { return false; };
/**
 * silent error handler
 */ 
org.berthomg.Error.SILENT_ERROR_HANDLER = function()  { return true; };
/**
 * default error handler function. alerts each error but not more than
 * org.berthomg.Error.ERROR_MAX 
 */ 
org.berthomg.Error.DEFAULT_ERROR_HANDLER = function (desc,page,line) {
    alert(
        'JavaScript error occurred! \n'
        +'The error was handled by '
        +'a customized error handler.\n'
        +'\nError description: \t'+desc
        +'\nPage address:\t'+page
        +'\nLine number:\t'+line
    )
    return true;
};


/**
 * set window error handler to the passed function
 * if the param is a function , set it to the window.onerror, otherwise
 * set to the default handler function  
 * the passed function  
 */ 
org.berthomg.Error.setErrorHandler = function ( func ) {
    //if it's set and it's a function
    if(func && typeof func == 'function') {
        window.onerror = func;
    } else {
        window.onerror = org.berthomg.Error.DEFAULT_ERROR_HANDLER;
    }
};
/**
 * set window error handler to the passed function
 * if the param is a function , set it to the window.onerror, otherwise
 * set to the default handler function  
 * the passed function  
 */ 
org.berthomg.Error.restoreErrorHandler = function ( func ) {
    window.onerror = org.berthomg.Error.BROWSER_ERROR_HANDLER;
};
//------------------------------------------------------------------------------
// JavaScript Document
org.berthomg.createNamespace('org.berthomg.Events', 0.1);
/**
 * http://www.quirksmode.org/blog/archives/2005/09/addevent_recodi.html
 * http://www.quirksmode.org/blog/archives/2005/10/_and_the_winner_1.html
 * function org.berthomg.addLoadEvent
 *
 * ajoute une fonction a executer dans le onload.
 */
org.berthomg.Events.addLoadEvent = function (func) {
  var oldonload = window.onload;
  if (typeof window.onload != 'function') {
    window.onload = func;
  } else {
    window.onload = function() {
      oldonload();
      func();
    }
  }
}
/**
 * add an event function "fn" with "type" to the "obj".
 *
 */  
org.berthomg.Events.addEvent = function( obj, type, fn ) {
    if ( obj.attachEvent ) {
        obj['e'+type+fn] = fn;
        obj[type+fn] = function(){obj['e'+type+fn]( window.event );}
        obj.attachEvent( 'on'+type, obj[type+fn] );
    } else {
        obj.addEventListener( type, fn, false );
    }
};
/**
 * remove an event function "fn" with "type" to the "obj".
 */ 
org.berthomg.Events.removeEvent = function( obj, type, fn ) {
    if ( obj.detachEvent ) {
        obj.detachEvent( 'on'+type, obj[type+fn] );
        obj[type+fn] = null;
    } else {
        obj.removeEventListener( type, fn, false );
    }
};
  
/**
 * to use the method of an object for event do:
 *  
 * button.onclick = function () { o.mymethod(); }; 
 */ 
org.berthomg.Events.portableEventHandler = function (e) {
    if(!e) e = window.event;
    //body of the vent goes here;
};
/**
 * Handler
 * 
 * portable event registartion functions
 * Handler.add() et Handler.remove().
 * both function take 3 args:
 * 
 * element: DOM element, document or window      
 * 
 * eventType: "click" => onclick
 * 
 * handler: function to be invoked . the this will refer to the element
 * 
 * no return value      
 * 
 * add() ignores duplicate  for the same element and event type. remove() does
 * nothing if called to remove handler not registered.
 * 
 * if browser implements addEventListener => invokde these functions passing
 * false as 3rd args: means that event handler are nerver registered as capturing
 * event handlers.
 * 
 * if version of IE that supports attachEvent() compliqué!! add property named
 * _allHandlers on window object and create a property _handlers on any element
 * on which a handler is registered  
 */ 
org.berthomg.createNamespace('org.berthomg.Events.Handler', 0.1);
(function() {
    if(document.addEventListener) {
        org.berthomg.Events.Handler.add = function (element, eventType, handler ) {
            element.addEventListener(eventType, handler, false);
        };
        org.berthomg.Events.Handler.remove = function (element, eventType, handler ) {
            element.removeEventListener(eventType, handler, false);
        };
    }
    else if(document.attachEvent) {
        org.berthomg.Events.Handler.add = function (element, eventType, handler ) {
            if(org.berthomg.Events.Handler._find(element, eventType, handler) != -1) return;
            
            var wrappedHandler = function(e) {
                if(!e) e = window.event;
                
                var event = {
                    _event: e,
                    type: e.type,
                    target: e.srcElement,
                    currentTarget: element,
                    relatedTarget: e.fromElement?e.fromElement:e.toElement,
                    eventPhase: (e.srcElement==element)?2:3,
                    
                    clientX: e.clientX, clientY: e.clientY,
                    screenX: e.screenX, screenY: e.screenY,
                    
                    altKey: e.altKey, ctrlKey: e.ctrlKey, 
                    shiftKey: e.shiftKey, charCode: e.keyCode,
                    
                    stopPropagation: function() {this._event.cancelBubble = true;}, 
                    preventDefault: function() {this._event.returnValue = false;}
                    
                };
                
                if(Function.prototype.call)
                handler.call(element, event);
                else {
                    element._currentHandler = handler;
                    element._currentHandler(event);
                    element._currentHandler = null;
                }
            };
            
            element.attachEvent("on" + eventType, wrappedHandler);
            
            //record keeping
            var h = {
                element: element,
                eventType: eventType,
                handler: handler,
                wrappedHandler: wrappedHandler
            };
            
            var d = element.document || element;
            var w = d.parentWindow;
            var id = Handler._uid();
            if(!w._allHandlers) w._allHandlers = {};
            w._allHandlers[id] = h;
            
            if(!element._handlers) element._handlers = [];
            element._handlers.push(id);
            
            if(!w._onunloadHandlerRegistered) {
                w._onunloadHandlerRegistered = true;
                w.attachEvent("onunload", org.berthomg.Events.Handler._removeAllHandlers);
            }
        };
        
        org.berthomg.Events.Handler.remove = function (element, eventType, handler ) {
            var i = org.berthomg.Events.Handler._find(element, eventType, handler);
            if(i == -1) return;
            
            var d = element.document || element;
            var w = d.parentWindow;
            
            var handlerId = element._handlers[i];
            var h = w._allHandlers[handlerId];
            element.detachEvent("on" + eventType, h.wrappedHandler);
            element._handlers.splice(i,1);
            delete w._allHandlers[handlerId]
        };
        
        org.berthomg.Events.Handler._find = function (element, eventType, handler ) {
            var handlers = element._handlers;
            if(!handlers) return -1;
            
            var d = element.document || element;
            var w = d.parentWindow;
            
            for(var i = handlers.length-1; i >= 0; i--) {
                var handlerId = handlers[i];
                var h = w._allHandlers[handlerId];
                if(h.eventType == eventType && h.handler == handler)
                    return i;    
            }
            return -1;
        };
        
        org.berthomg.Events.Handler._removeAllHandlers = function() {
            var w = this;
            for(id in w._allHandlers) {
                var h = w._allHandlers[id];
                h.element.detachEvent("on" + h.eventType, h.wrappedHandler);
                delete w._allHandlers[id];
            }
        };
        
        org.berthomg.Events.Handler._counter = 0;
        org.berthomg.Events.Handler._uid = function () {return "h"+org.berthomg.Events.Handler._counter++; };
    }
})();

/**
 * DRAG
 */ 
org.berthomg.createNamespace('org.berthomg.Events.Drag', 0.1);
/**
 * drag absolutely positioned elements
 * 
 * a function to be called from an onmousedown event handler.
 * mousemove will move the element. 
 * mouseup will terminate the drag
 * 
 * if dragged off screen, window does not scroll.
 * 
 *  works fo DOM level2 and IE model.  
 *  
 * elementToDrag: element that received the mousedown event   or some container.
 * must be absolutely positioned.
 * style.left and .top wil be changed
 * 
 * event: Event object for mousedown event.    
 */ 
org.berthomg.Events.Drag.drag = function (elementToDrag, event) {
    var startX = event.clientX;
    var startY = event.clientY;
    
    var origX = elementToDrag.offsetLeft;
    var origY = elementToDrag.offsetTop;
    
    var deltaX = startX - origX;
    var deltaY = startY - origY;
    
    var moveHandler = function (e) {
        if(!e) e = window.event;//IE
        
        elementToDrag.style.left = (e.clientX - deltaX) + "px";
        elementToDrag.style.top = (e.clientY - deltaY) + "px";
        
        if(e.stopPropagation) e.stopPropagation(); //dom2
        else e.cancelBubble = true;
    };
    
    var upHandler = function (e) {
        if(!e) e = window.event;//IE
        
        if(document.removeEventListener) {
            document.removeEventListener("mouseup", upHandler, true);
            document.removeEventListener("mousemove", moveHandler, true);
        } else if(document.detachEvent) {//IE5+
            elementToDrag.detachEvent("onlosecapture", upHandler);
            elementToDrag.detachEvent("onmouseup", upHandler);
            elementToDrag.detachEvent("onmousemove", moveHandler);
        } else {//IE4
            document.onmouseup = olduphandler;
            document.onmousemove = oldmovehandler;
        }
        
        if(e.stopPropagation) e.stopPropagation(); //dom2
        else e.cancelBubble = true;//ie
    };
    
    if(document.addEventListener) {//DOM l2
        document.addEventListener("mousemove", moveHandler, true);
        document.addEventListener("mouseup", upHandler, true);
    } else if(document.attachEvent) {//IE5+
        elementToDrag.setCapture();
        elementToDrag.attachEvent("onmousemove", moveHandler);
        elementToDrag.attachEvent("onmouseup", upHandler);
        elementToDrag.attachEvent("onlosecapture", upHandler);
    } else {//IE4
        var oldmovehandler = document.onmousemove;
        var olduphandler = document.onmouseup;
        document.onmousemove = moveHandler;
        document.onmouseup = upHandler;
    }
    
    if(event.stopPropagation) event.stopPropagation(); //dom2
    else event.cancelBubble = true;
    
    if(event.preventDefault) event.preventDefault();
    else event.returnValue = false;  
};
/**
 * InputFilter: unobstrusive filtering of keystrokes for input tag
 * 
 * modified from the original!!!  
 * 
 * define a filter function wich can be an event handler for "onkeypress";  
 */ 
/**
 * inputEle: the input element on wich we apply the filter
 *  
 *  allowed: a string of allowed chars
 *   
 *  actionFun: executed when it doesnot pass the test. takes the event as arg
 *  must return false or true to allow character? 
 *  
 * //ne marche pas bien sous firefox, les caracteres s'affichent kan meem  
 */ 
org.berthomg.Events.InputFilter = function(inputEle, allowed, actionFunc) {
    this.inputEle = inputEle;
    this.allowed = allowed;
    this.actionFunc = actionFunc;
    if(inputEle.type == "text") {
        if(inputEle.addEventListener) {
            inputEle.addEventListener("keypress", this.handle.bind(this), false);
        } else {
            inputEle.onkeypress = this.handle.bind(this);
        }
    }
};
org.berthomg.Events.InputFilter.prototype.handle = function(e) {
    var ev = e || window.event;
    var test = this.filter(ev);
    if(!test) {
        this.actionFunc(ev);
    }
    return test;
};
org.berthomg.Events.InputFilter.prototype.filter = function (event) {
    var e = event || window.event;
    var code = e.charCode || e.keyCode;
    if(e.charCode == 0) return true;//firefox
    if(e.ctrlKey || e.altKey) return true;
    if(code < 32) return true;//ascii control chars
    
    var c = String.fromCharCode(code);
    
    if(this.allowed.indexOf(c) != -1) {
        return true;
    } else {
        //reject keypress event
        if(e.preventDefault) e.preventDefault();
        if(e.returnValue) e.returnValue = false;
        return false;
    }
};

//------------------------------------------------------------------------------
// create org.berthomg.Geometry namespace
org.berthomg.createNamespace('org.berthomg.Geometry', 0.1);

/**
 * portable functions for querying window and document geometry.
 * 
 * - getWindowX/Y(): return the position of the window on the screen.
 * - getViewportWidth/Height(): return the size of the browser viewport area.
 * - getDocumentWidth/Height(): return the size of the document.
 * - getHorizontalScroll(): return the position of the horizontal scrollbar. 
 * - getVerticalScroll(): return the position of the vertical scrollbar.
 * 
 * note that there is no portable way to query the overall size of the browser 
 * window, so there are no getWindowWidth/Height functions.
 * 
 * IMPORTANT: this module must be included in the <BODY> of the document instead
 * of the <HEAD> !   
 */ 
(function () {
    //getWindowX/Y
    if(window.screenLeft) {//IE and others
        org.berthomg.Geometry.getWindowX = function() { return window.screenLeft; };
        org.berthomg.Geometry.getWindowY = function() { return window.screenTop; };
    }
    else if(window.screenX) {// firefox and others
        org.berthomg.Geometry.getWindowX = function() { return window.screenX; };
        org.berthomg.Geometry.getWindowY = function() { return window.screenY; };
    }
    
    //getViewportWidth/Height
    var viewportwidth, viewportheight;
    if (self.innerHeight) {// all except Explorer
    	viewportwidth = self.innerWidth;
    	viewportheight = self.innerHeight;
    }
    else if (document.documentElement 
             && document.documentElement.clientHeight) {// Explorer 6 Strict Mode
    	viewportwidth = document.documentElement.clientWidth;
    	viewportheight = document.documentElement.clientHeight;
    }
    else if (document.body) { // other Explorers
    	viewportwidth = document.body.clientWidth;
    	viewportheight = document.body.clientHeight;
    }
    org.berthomg.Geometry.getViewportWidth = function () {
        return viewportwidth;
    };
    org.berthomg.Geometry.getViewportHeight = function () {
        return viewportheight;
    };
    
    //getHorizontalScroll/getVerticalScroll
    var hscroll, vscroll;
    if (self.pageYOffset) {// all except Explorer
    	hscroll = self.pageXOffset;
    	vscroll = self.pageYOffset;
    }
    else if (document.documentElement && document.documentElement.scrollTop) {
    // Explorer 6 Strict
    	hscroll = document.documentElement.scrollLeft;
    	vscroll = document.documentElement.scrollTop;
    }
    else if (document.body) {// all other Explorers
    	hscroll = document.body.scrollLeft;
    	vscroll = document.body.scrollTop;
    }
    org.berthomg.Geometry.getHorizontalScroll = function () {
        return hscroll;
    };
    org.berthomg.Geometry.getVerticalScroll = function () {
        return vscroll;
    };
    
    //getDocumentWidth/Height
    var dwidth, dheight;
    if (document.body) {
        var test1 = document.body.scrollHeight;
        var test2 = document.body.offsetHeight;
        if (test1 > test2) {// all but Explorer Mac
        	dwidth = document.body.scrollWidth;
        	dheight = document.body.scrollHeight;
        }
        else {// Explorer Mac;
             //would also work in Explorer 6 Strict, Mozilla and Safari
        	dwidth = document.body.offsetWidth;
        	dheight = document.body.offsetHeight;
        }
    }
    org.berthomg.Geometry.getDocumentWidth = function () {
        return dwidth;
    };
    org.berthomg.Geometry.getDocumentHeight = function () {
        return dheight;
    };
    /**
	 * get the x position of an element
	 */         
	org.berthomg.Geometry.getX = function (e) {
	    var x=0;
	    while(e) {
	        x+=e.offsetLeft;
	        e = e.offsetParent//move up to the offsetParent
	    }
	    return x;
	};
	/**
	 * get the y position of an element
	 */         
	org.berthomg.Geometry.getY = function (e) {
	    var y=0;
	    while(e) {
	        y+=e.offsetTop;
	        e = e.offsetParent//move up to the offsetParent
	    }
	    return y;
	};
})();
//------------------------------------------------------------------------------
org.berthomg.createNamespace('org.berthomg.Log', 0.1);
/**
 * log a message with 2 or 3 arguments
 * 
 * category: type of the message. required. messages with different types can 
 *  be enabled/disabled and styled independently
 *  
 * message: text, may be empty if object
 * 
 * object: object to be logged, optional. properties in the form of a table.
 * recursive.
 * 
 * utility functions:
 * 
 * log.debug() and log.warn(). hardcoded category of "debug" and "warning".
 * 
 * enabling logging:
 * 
 * not displayed by default. to enables the display of a category you have 
 *  2 choices:
 *
 *  1) i.e. "debug" => rajouter <div id="debug_log"></div>  , everything 
 *  is appened to this container.
 *  
 * 2) "debug" => log.options.debugEnabled = true; a <div class="log"> 
 *  is created.  
 *  to disable: log.options.debugDisabled = true;
 *  
 *
 * styling with CSS:
 * 
 * example: debug messages created in divs with class "debug_message"    
 * 
 * 
 * log options:
 * log.options.timestamp : boolean, if true => add date to message
 * 
 * log.options.maxRecursion : integer, for nested tables, 0 if you never want a table
 * within a table.
 * 
 * log.options.filter : function, that filter properties out when logging an 
 *  object. a filter function is passed the name and value of a property and
 *  return true if the property should appear in the object table; false otherwise 
 * 
 * 
 */
org.berthomg.Log.log = function(category, message, object) {
    var l = org.berthomg.Log.log;

    //if this category is disabled explicitely, do nothing
    if(l.options[category + "Disabled"]) return;
    
    //find the container
    var id = category + "_log";
    var c = document.getElementById(id);
    
    //if there is no container , but logging iin this category is enabled
    //create the container.
    if(!c && l.options[category + "Enabled"]) {
        c = document.createElement('div');
        c.id = id;
        c.className = "log";
        document.body.appendChild(c);
    }
    
    //if still no container, ignore
    if(!c) return;
    
    //if timestamp
    if(l.options.timestamp) {
        message = new Date() + ": " + (message?message:"");
    }
    
    //create div element for the message
    var entry = document.createElement('div');
    entry.className = category + "_message";
    
    if(message) {
        entry.appendChild(document.createTextNode(message));
    }
    
    if(object && typeof object == "object") {
        entry.appendChild(l.makeTable(object,0));
    }
    
    //add the entry to the logging container
    c.appendChild(entry);
};

/**
 * empty object for options
 */
org.berthomg.Log.log.options = {}; 

/**
 * utility functions for debug and warn
 */ 
org.berthomg.Log.log.debug = function(message, object) {
    org.berthomg.Log.log('debug', message, object);
};
org.berthomg.Log.log.warn = function(message, object) {
    org.berthomg.Log.log('warning', message, object);
};

/**
 * create a table to display the properties of the object
 */ 
org.berthomg.Log.log.makeTable = function (object, level) {
    var l = org.berthomg.Log.log;
    //if we reached the max recursion, return a text node instead
    if(level > l.options.maxRecursion) {
        if(object && !object.toString) {
            object.toString = function() {return 'object?';};
        }
        return document.createTextNode(object.toString());
    }
        
        
    //craete the table
    var table = document.createElement('table');
    table.border = 1;
    //ad name|type|value to the table
    var header = document.createElement('tr');
    
    var headerName = document.createElement('th');
    headerName.appendChild(document.createTextNode("Name"));
    
    var headerType = document.createElement('th');
    headerType.appendChild(document.createTextNode("Type"));
    
    var headerValue = document.createElement('th');
    headerValue.appendChild(document.createTextNode("Value"));
    
    header.appendChild(headerName);
    header.appendChild(headerType);
    header.appendChild(headerValue);
    
    table.appendChild(header);
    
    //get property names of object and order them
    var names = [];
    for(var name in object) names.push(name);
    names.sort();
    
    //loop through properties
    for(var i=0; i < names.length; i++) {
        var name, value, type;
        name = names[i];
        try {
            value = object[name];
            type = typeof value;
        } catch(e) {//can happend in firefox
            value = "<unknown value>";
            type = "unknown";
        }
        
        //skip property if rejected by filter
        if(l.options.filter && !l.options.filter(name, value)) continue;
        
        //never display function source code
        if(type == 'function') value = "{/*source code suppressed*/}";
        
        //craete table row to display property name type value
        var row = document.createElement('tr');
        row.vAlign = "top";
        var rowName = document.createElement('td');
        var rowType = document.createElement('td');
        var rowValue = document.createElement('td');
        rowName.appendChild(document.createTextNode(name));
        rowType.appendChild(document.createTextNode(type));
        
        //for object, recursion
        if(type == 'object')
            rowValue.appendChild(l.makeTable(value, level+1));
        else
            rowValue.appendChild(document.createTextNode(value));
            
        row.appendChild(rowName);
        row.appendChild(rowType);
        row.appendChild(rowValue);
        
        table.appendChild(row);
        
    }
    
    //finally return the table
    return table;
};

//uncomment the following line to conver alert into log messages
// function alert(msg) { org.berthomg.Log.log("alert", msg); }
//------------------------------------------------------------------------------
// create org.berthomg.Utils namespace
org.berthomg.createNamespace('org.berthomg.Utils', 0.1);
/*
GB

http://www.jibbering.com/faq/faq_notes/closures.html

A common use for a closure is to provide parameters for the execution of a 
function prior to the execution of that function. For example, when a function 
is to be provided as the first argument to the setTimout function that is 
common in web browser environments.
setTimeout schedules the execution of a function (or a string of javascript 
source code, but not in this context), provided as its first argument, after an 
interval expressed in milliseconds (as its second argument). If a piece of code 
wants to use setTimeout it calls the setTimeout function and passes a reference 
to a function object as the first argument and the millisecond interval as the 
second, but a reference to a function object cannot provide parameters for the 
scheduled execution of that function.
However, code could call another function that returned a reference to an inner 
function object, with that inner function object being passed by reference to 
the setTimeout function. The parameters to be used for the execution of the 
inner function are passed with the call to the function that returns it. 
setTimout executes the inner function without passing arguments but that inner 
function can still access the parameters provided by the call to the outer 
function that returned it:-

example :

Call the function that will return a reference to the inner function
   object created in its execution context. Passing the parameters that
   the inner function will use when it is eventually executed as
   arguments to the outer function. The returned reference to the inner
   function object is assigned to a local variable:-

>>> var functRef = callLater(elStyle, "display", "none");
 
 Call the setTimeout function, passing the reference to the inner
   function assigned to the - functRef - variable as the first argument:-

>>> hideMenu=setTimeout(functRef, 500);

*/
org.berthomg.Utils.callLater = function (func, options) {
    //Return a reference to an anonymous inner function created
    //with a function expression:-
    return (function(){
        //This inner function is to be executed with - setTimeout
        // - and when it is executed it can read, and act upon, the
        // parameters passed to the outer function:-
        if(options && typeof options == 'object') {
            for(p in options) func[p] = options[p];
        }
    });
};
/**
 * array utility function
 * pass each element of the array a to the specified predicate funciton.
 * return an array that holds the elements for which the predicate returned
 * true. 
 */  
org.berthomg.Utils.filterArray = function (/*array*/ a, /*boolean function*/ predicate) {
    var r = []; //to hold the results
    var l = a.length; //in case predicate change the length!
    for(var i=0; i < l; i++) {
        var e = a[i];
        if(predicate(e)) r.push(e);
    }
    return r;
};
/**
 * array utility function
 * return the array of values that result when each of the elements
 * of the array a are passed to the function f. 
 */  
org.berthomg.Utils.mapArray = function (/*array*/ a, /*function*/ f) {
    var r = []; //to hold the results
    var l = a.length; //in case predicate change the length!
    for(var i=0; i < l; i++) r[i] = f(a[i]);
    return r;
};

/**
 * function utility function
 * return a function that invokes the function f with the specified arguments
 * and also any additional arguments that are passed to the return function.
 * this is sometimes called "currying" .
 */  
org.berthomg.Utils.bindArguments = function (/*function*/ f /*, initial arguments...*/) {
    var boundArgs = arguments;
    return function() {
        //build up an array of arguments. it starts with the previously
        //bound arguments and is extended with the arguments passed now.
        var args = [];
        for(var i=1; i < boundArgs.length; i++) args.push(boundArgs[i]);
        for(var i=0; i < arguments.length; i++) args.push(arguments[i]);
        //now invoke the function with these arguments
        return f.apply(this, args);
    }
};
/**
 * returns unique id through a closure function
 * the call object of this function holds our value. 
 */ 
org.berthomg.Utils.uniqueID = (function () {
    var id = 0;//private persistent value
    //the outer function returns a nested function that has access to the
    //persistent value. it is this nested function we are storing in uniqueID.
    return function() { return id++; };//return and increment.
})();//invoque the outer function after defining it.
  
/**
 * breakpoints using closures
 * this function implement a breakpoint. it repeatedly prompts the user for
 * an expression, evaluates it with the the supplied self-inspecting closure,
 * and display the result. it is the closure that provides acces to the scope 
 * to be inspected, so each function must supply its own closure.
 * 
 * inspired by steve yen breakpoint() function  
 */ 
org.berthomg.Utils.inspect = function (/**/ inspector, /*string*/ title_) {
    var expression, result;
    
    // you can use a breakpoint to turn off subsequent breakpoints by creating
    // a property named ignore on this function.
    if('ignore' in arguments.callee) return;
    
    while(true) {
        //figure out how to prompt the user
        var message = "";
        //if title given, display that first
        if(title_) message = title_ + "\n";
        //if we already evaluated an expression, display it and its value
        if(expression) 
            message += "\n" + expression + " ==> " + result + "\n";
        else
            expression = "";
        //we display at least a basic prompt
        message += "Enter an expression to evaluate:";
        
        //get the user input, displaying our prompt an using the last expression
        //as the default value this time
        expression = prompt(message, expression);
        
        //if the user didn't enter anything (or clicked cancel), they're done,
        //and so we return, ending the breakpoint.
        if(!expression) return;
        
        //otherwise, use the supplied closure to evaluate the expression
        //in the scope that is being inspected. the result will be dsiplayed 
        //on the next iteration.
        result = inspector(expression);
        
    }
};
/**
 * create the inspector for using above
 * see example below:

function factorial(n) {
    //create a closure for this function
    var inspector = org.berthomg.Utils.inspector;
    var inspect = org.berthomg.Utils.inspect;
    inspect(inspector, "entering factorial()");
    var result = 1;
    while (n>1) {
        result = result * n;
        n--;
        inspect(inspector, "factorial() loop")
    }
    inspect(inspector, "exiting factorial()")
    return result;
}
  
 */ 
org.berthomg.Utils.inspector = function (_$_$_$_$_$_) {
    return eval(_$_$_$_$_$_);
};
/**
 * enhanced typeof testing
 */  
org.berthomg.Utils.getType = function (x) {
    if(x == null) return "null";
    var t = typeof x;
    if(t != "object") return t;
    var c = Object.prototype.toString.apply(x);
    c = c.substring(8, c.length - 1);
    if(c != "Object") return c;
    if(x.constructor == Object) return c;
    if("classname" in x.constructor.prototype &&
    typeof x.constructor.prototype.classname == "string")
        return x.constructor.prototype.classname;
    return "<unknown type>";
};





/**
 * essai de import de fichier js chope sur le net
 * usage: org.berthomg.Utils.dhtmlImport("dhtml_way.js");
 */
org.berthomg.Utils.dhtmlImport = function(/*string*/ url) {
   var e = document.createElement("script");
   e.src = url;
   e.type = "text/javascript";
   document.getElementsByTagName("head")[0].appendChild(e); 
}; 

/**
 * essai de import de fichier js chope sur le net
 * usage: org.berthomg.Utils.staticImport("dhtml_way.js"); 
 */  
org.berthomg.Utils.staticImport = function(/*string*/ url) {
   document.write('<script src="', url, '" type="text/javascript"><\/script>');
};

/**
 * get navigator infos as string
 * @return string 
 */ 
org.berthomg.Utils.getNavigatorInfos = function() {
    var browser = [];
    browser.push("BROWSER INFORMATION:");
    for(var p in navigator) {
        browser.push( p + ": " + navigator[p] );
    }
    return browser.join("\n");
};
/**
 * very simple client sniffer
 * ne pas utilier 
 */ 
org.berthomg.Utils.getBrowser = function() {
    var browser = {
        version: parseInt(navigator.appVersion),
        name: navigator.appName,
        isNetscape: navigator.appName.indexOf("Netscape") != -1,
        isMicrosoft: navigator.appName.indexOf("Microsoft") != -1
    };
    return browser;
};
/**
 * this function parses ampersand-separated name=value argument pairs from the
 * query string of the URL. It stores the name=value pairs in properties of an
 * object and returns that object.
 * 
 * example:
 * var args = org.berthomg.getURLArgs(); // parse URL
 * var q = args.q || ""; //use argument if defined or default value 
 * var n = args.n ? parseInt(args.n) : 10; 
 */ 
org.berthomg.Utils.getURLArgs = function () {
    var args = {};
    var query = location.search.substring(1); //get query string
    var pairs = query.split('&');
    for(var i=0; i < pairs.length; i++) {
        var pos = pairs[i].indexOf('='); // look for name=value
        if(pos == -1) continue;
        var argname = pairs[i].substring(0, pos);//extract the name
        var valeur = pairs[i].substring(pos + 1);//extract the value
        valeur = decodeURIComponent(valeur);//decode it if needed
        args[argname] = valeur;
    }
    return args;
};
/**
 * querying the currently selected text
 */ 
org.berthomg.Utils.getSelectedText = function () {
    if(window.getSelection) {
        //this technioque is the most like ly to be standardized.
        //getSelection() returns a Selection of object, which we do not document
        return window.getSelection().toString();
    } else if (document.getSelection) {
        //this is an older simpler technique that returns a string.
        return document.getSelection();
    } else if (document.selection) {
        //this is the IE specific technique. not documented here.
        return document.selection.createRange().text;
    }
};
//------------------------------------------------------------------------------
/**
 * here come the xtended libraries that are useful in the framework
 *
 *
 */   
org.berthomg.createNamespace('org.lib', 0.1);
org.berthomg.createNamespace('org.lib.javascripttoolbox', 0.1);
/*===================================================================
 Author: Matt Kruse
 
 View documentation, examples, and source code at:
     http://www.JavascriptToolbox.com/

 NOTICE: You may use this code for any purpose, commercial or
 private, without any further permission from the author. You may
 remove this notice from your final code if you wish, however it is
 appreciated by the author if at least the web site address is kept.

 This code may NOT be distributed for download from script sites, 
 open source CDs or sites, or any other distribution method. If you
 wish you share this code with others, please direct them to the 
 web site above.
 
 Pleae do not link directly to the .js files on the server above. Copy
 the files to your own server for use with your site or webapp.
 ===================================================================*/
/* ******************************************************************* */
/*   UTIL FUNCTIONS                                                    */
/* ******************************************************************* */
org.lib.javascripttoolbox.table = (function () {

var Util = (function () {

    var util = {'$VERSION':1.06};
    
    // Util functions - these are GLOBAL so they
    // look like built-in functions.
    
    // Determine if an object is an array
    util.isArray = function (o) {
    	return (o!=null && typeof(o)=="object" && typeof(o.length)=="number" && (o.length==0 || defined(o[0])));
    };
    
    // Determine if an object is an Object
    util.isObject = function (o) {
    	return (o!=null && typeof(o)=="object" && defined(o.constructor) && o.constructor==Object && !defined(o.nodeName));
    };
    
    // Determine if a reference is defined
    util.defined = function (o) {
    	return (typeof(o)!="undefined");
    };
    
    // Iterate over an array, object, or list of items and run code against each item
    // Similar functionality to Perl's map() function
    util.map = function (func) {
    	var i,j,o;
    	var results = [];
    	if (typeof(func)=="string") {
    		func = new Function('$_',func);
    	}
    	for (i=1; i<arguments.length; i++) {
    		o = arguments[i];
    		if (Util.isArray(o)) {
    			for (j=0; j<o.length; j++) {
    				results[results.length] = func(o[j]);
    			}
    		}
    		else if (Util.isObject(o)) {
    			for (j in o) {
    				results[results.length] = func(o[j]);
    			}
    		}
    		else {
    			results[results.length] = func(o);
    		}
    	}
    	return results;
    };
    
    // Set default values in an object if they are undefined
    util.setDefaultValues = function (o,values) {
    	if (!util.defined(o) || o==null) {
    		o = {};
    	}
    	if (!util.defined(values) || values==null) {
    		return o;
    	}
    	for (var val in values) {
    		if (!util.defined(o[val])) {
    			o[val] = values[val];
    		}
    	}
    	return o;
    };

    return util;
    
})();
/* ******************************************************************* */
/*   DEFAULT OBJECT PROTOTYPE ENHANCEMENTS                             */
/* ******************************************************************* */
// These functions add useful functionality to built-in objects
Array.prototype.contains = function(o) {
	var i,l;
	if (!(l = this.length)) { return false; }
	for (i=0; i<l; i++) {
		if (o==this[i]) {
			return true;
		}
	}
}

/* ******************************************************************* */
/*   DOM FUNCTIONS                                                     */
/* ******************************************************************* */
var DOM = (function() { 
	var dom = {};
	
	// Get a parent tag with a given nodename
	dom.getParentByTagName = function(o,tagNames) {
		if(o==null) { return null; }
		if (Util.isArray(tagNames)) {
			tagNames = Util.map("return $_.toUpperCase()",tagNames);
			while (o=o.parentNode) {
				if (o.nodeName && tagNames.contains(o.nodeName)) {
					return o;
				}
			}
		}
		else {
			tagNames = tagNames.toUpperCase();
			while (o=o.parentNode) {
				if (o.nodeName && tagNames==o.nodeName) {
					return o;
				}
			}
		}
		return null;
	};
	
	// Remove a node from its parent
	dom.removeNode = function(o) {
		if (o!=null && o.parentNode && o.parentNode.removeChild) {
			// First remove all attributes which are func references, to avoid memory leaks
			for (var i in o) {
				if (typeof(o[i])=="function") {
					o[i] = null;
				}
			}
			o.parentNode.removeChild(o);
			return true;
		}
		return false;
	};

	// Get the outer width in pixels of an object, including borders, padding, and margin
	dom.getOuterWidth = function(o) {
		if (Util.defined(o.offsetWidth)) {
			return o.offsetWidth;
		}
		return null;
	};

	// Get the outer height in pixels of an object, including borders, padding, and margin
	dom.getOuterHeight = function(o) {
		if (Util.defined(o.offsetHeight)) {
			return o.offsetHeight;
		}
		return null;
	};

	// Resolve an item, an array of items, or an object of items
	dom.resolve = function() {
		var results = new Array();
		var i,j,o;
		for (var i=0; i<arguments.length; i++) {
			var o = arguments[i];
			if (o==null) {
				if (arguments.length==1) {
					return null;
				}
				results[results.length] = null;
			}
			else if (typeof(o)=='string') {
				if (document.getElementById) {
					o = document.getElementById(o);
				}
				else if (document.all) {
					o = document.all[o];
				}
				if (arguments.length==1) {
					return o;
				}
				results[results.length] = o;
			}
			else if (Util.isArray(o)) {
				for (j=0; j<o.length; j++) {
					results[results.length] = o[j];
				}
			}
			else if (Util.isObject(o)) {
				for (j in o) {
					results[results.length] = o[j];
				}
			}
			else if (arguments.length==1) {
				return o;
			}
			else {
				results[results.length] = o;
			}
	  }
	  return results;
	};
	dom.$ = dom.resolve;
	
	return dom;
})();

/* ******************************************************************* */
/*   CSS FUNCTIONS                                                     */
/* ******************************************************************* */
var CSS = (function(){
	var css = {};

	// Convert an RGB string in the form "rgb (255, 255, 255)" to "#ffffff"
	css.rgb2hex = function(rgbString) {
		if (typeof(rgbString)!="string" || !Util.defined(rgbString.match)) { return null; }
		var result = rgbString.match(/^\s*rgb\s*\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*/);
		if (result==null) { return rgbString; }
		var rgb = +result[1] << 16 | +result[2] << 8 | +result[3]
		var hex = "";
		var digits = "0123456789abcdef";
		while(rgb!=0) { 
			hex = digits.charAt(rgb&0xf)+hex; 
			rgb>>>=4; 
		} 
		while(hex.length<6) { hex='0'+hex; }
		return "#" + hex;
	};

	// Convert hyphen style names like border-width to camel case like borderWidth
	css.hyphen2camel = function(property) {
		if (!Util.defined(property) || property==null) { return null; }
		if (property.indexOf("-")<0) { return property; }
		var str = "";
		var c = null;
		var l = property.length;
		for (var i=0; i<l; i++) {
			c = property.charAt(i);
			str += (c!="-")?c:property.charAt(++i).toUpperCase();
		}
		return str;
	};
	
	// Determine if an object or class string contains a given class.
	css.hasClass = function(obj,className) {
		if (!Util.defined(obj) || obj==null || !RegExp) { return false; }
		var re = new RegExp("(^|\\s)" + className + "(\\s|$)");
		if (typeof(obj)=="string") {
			return re.test(obj);
		}
		else if (typeof(obj)=="object" && obj.className) {
			return re.test(obj.className);
		}
		return false;
	};
	
	// Add a class to an object
	css.addClass = function(obj,className) {
		if (typeof(obj)!="object" || obj==null || !Util.defined(obj.className)) { return false; }
		if (obj.className==null || obj.className=='') { 
			obj.className = className; 
			return true; 
		}
		if (css.hasClass(obj,className)) { return true; }
		obj.className = obj.className + " " + className;
		return true;
	};
	
	// Remove a class from an object
	css.removeClass = function(obj,className) {
		if (typeof(obj)!="object" || obj==null || !Util.defined(obj.className) || obj.className==null) { return false; }
		if (!css.hasClass(obj,className)) { return false; }
		var re = new RegExp("(^|\\s+)" + className + "(\\s+|$)");
		obj.className = obj.className.replace(re,' ');
		return true;
	};
	
	// Fully replace a class with a new one
	css.replaceClass = function(obj,className,newClassName) {
		if (typeof(obj)!="object" || obj==null || !Util.defined(obj.className) || obj.className==null) { return false; }
		css.removeClass(obj,className);
		css.addClass(obj,newClassName);
		return true;
	};
	
	// Get the currently-applied style of an object
	css.getStyle = function(o, property) {
		if (o==null) { return null; }
		var val = null;
		var camelProperty = css.hyphen2camel(property);
		// Handle "float" property as a special case
		if (property=="float") {
			val = css.getStyle(o,"cssFloat");
			if (val==null) { 
				val = css.getStyle(o,"styleFloat"); 
			}
		}
		else if (o.currentStyle && Util.defined(o.currentStyle[camelProperty])) {
			val = o.currentStyle[camelProperty];
		}
		else if (window.getComputedStyle) {
			val = window.getComputedStyle(o,null).getPropertyValue(property);
		}
		else if (o.style && Util.defined(o.style[camelProperty])) {
			val = o.style[camelProperty];
		}
		// For color values, make the value consistent across browsers
		// Convert rgb() colors back to hex for consistency
		if (/^\s*rgb\s*\(/.test(val)) {
			val = css.rgb2hex(val);
		}
		// Lowercase all #hex values
		if (/^#/.test(val)) {
			val = val.toLowerCase();
		}
		return val;
	};
	css.get = css.getStyle;

	// Set a style on an object
	css.setStyle = function(o, property, value) {
		if (o==null || !Util.defined(o.style) || !Util.defined(property) || property==null || !Util.defined(value)) { return false; }
		if (property=="float") {
			o.style["cssFloat"] = value;
			o.style["styleFloat"] = value;
		}
		else if (property=="opacity") {
			o.style['-moz-opacity'] = value;
			o.style['-khtml-opacity'] = value;
			o.style.opacity = value;
			if (Util.defined(o.style.filter)) {
				o.style.filter = "alpha(opacity=" + value*100 + ")";
			}
		}
		else {
			o.style[css.hyphen2camel(property)] = value;
		}
		return true;
	};
	css.set = css.setStyle;
	
	// Get a unique ID which doesn't already exist on the page
	css.uniqueIdNumber=1000;
	css.createId = function(o) {
		if (Util.defined(o) && o!=null && Util.defined(o.id) && o.id!=null && o.id!="") { 
			return o.id;
		}
		var id = null;
		while (id==null || document.getElementById(id)!=null) {
			id = "ID_"+(css.uniqueIdNumber++);
		}
		if (Util.defined(o) && o!=null && (!Util.defined(o.id)||o.id=="")) {
			o.id = id;
		}
		return id;
	};
	
	return css;
})();

/* ******************************************************************* */
/*   EVENT FUNCTIONS                                                   */
/* ******************************************************************* */

var Event = (function(){
	var ev = {};
	
	// Resolve an event using IE's window.event if necessary
	// --------------------------------------------------------------------
	ev.resolve = function(e) {
		if (!Util.defined(e) && Util.defined(window.event)) {
			e = window.event;
		}
		return e;
	};
	
	// Add an event handler to a function
	// Note: Don't use 'this' within functions added using this method, since
	// the attachEvent and addEventListener models differ.
	// --------------------------------------------------------------------
	ev.add = function( obj, type, fn, capture ) {
		if (obj.addEventListener) {
			obj.addEventListener( type, fn, capture );
			return true;
		}
		else if (obj.attachEvent) {
			obj.attachEvent( "on"+type, fn );
			return true;
		}
		return false;
	};

	// Get the mouse position of an event
	// --------------------------------------------------------------------
	// PageX/Y, where they exist, are more reliable than ClientX/Y because 
	// of some browser bugs in Opera/Safari
	ev.getMouseX = function(e) {
		e = ev.resolve(e);
		if (Util.defined(e.pageX)) {
			return e.pageX;
		}
		if (Util.defined(e.clientX)) {
			return e.clientX+Screen.getScrollLeft();
		}
		return null;
	};
	ev.getMouseY = function(e) {
		e = ev.resolve(e);
		if (Util.defined(e.pageY)) {
			return e.pageY;
		}
		if (Util.defined(e.clientY)) {
			return e.clientY+Screen.getScrollTop();
		}
		return null;
	};

	// Stop the event from bubbling up to parent elements.
	// Two method names map to the same function
	// --------------------------------------------------------------------
	ev.cancelBubble = function(e) {
		e = ev.resolve(e);
		if (typeof(e.stopPropagation)=="function") { e.stopPropagation(); } 
		if (Util.defined(e.cancelBubble)) { e.cancelBubble = true; }
	};
	ev.stopPropagation = ev.cancelBubble;

	// Prevent the default handling of the event to occur
	// --------------------------------------------------------------------
	ev.preventDefault = function(e) {
		e = ev.resolve(e);
		if (typeof(e.preventDefault)=="function") { e.preventDefault(); } 
		if (Util.defined(e.returnValue)) { e.returnValue = false; }
	};
	
	return ev;
})();

/* ******************************************************************* */
/*   SCREEN FUNCTIONS                                                  */
/* ******************************************************************* */
var Screen = (function() {
	var screen = {};

	// Get a reference to the body
	// --------------------------------------------------------------------
	screen.getBody = function() {
		if (document.body) {
			return document.body;
		}
		if (document.getElementsByTagName) {
			var bodies = document.getElementsByTagName("BODY");
			if (bodies!=null && bodies.length>0) {
				return bodies[0];
			}
		}
		return null;
	};

	// Get the amount that the main document has scrolled from top
	// --------------------------------------------------------------------
	screen.getScrollTop = function() {
		if (document.documentElement && Util.defined(document.documentElement.scrollTop) && document.documentElement.scrollTop>0) {
			return document.documentElement.scrollTop;
		}
		if (document.body && Util.defined(document.body.scrollTop)) {
			return document.body.scrollTop;
		}
		return null;
	};
	
	// Get the amount that the main document has scrolled from left
	// --------------------------------------------------------------------
	screen.getScrollLeft = function() {
		if (document.documentElement && Util.defined(document.documentElement.scrollLeft) && document.documentElement.scrollLeft>0) {
			return document.documentElement.scrollLeft;
		}
		if (document.body && Util.defined(document.body.scrollLeft)) {
			return document.body.scrollLeft;
		}
		return null;
	};
	
	// Util function to default a bad number to 0
	// --------------------------------------------------------------------
	screen.zero = function(n) {
		return (!Util.defined(n) || isNaN(n))?0:n;
	};

	// Get the width of the entire document
	// --------------------------------------------------------------------
	screen.getDocumentWidth = function() {
		var width = 0;
		var body = screen.getBody();
		if (document.documentElement && (!document.compatMode || document.compatMode=="CSS1Compat")) {
		    var rightMargin = parseInt(CSS.get(body,'marginRight'),10) || 0;
		    var leftMargin = parseInt(CSS.get(body,'marginLeft'), 10) || 0;
			width = Math.max(body.offsetWidth + leftMargin + rightMargin, document.documentElement.clientWidth);
		}
		else {
			width =  Math.max(body.clientWidth, body.scrollWidth);
		}
		if (isNaN(width) || width==0) {
			width = screen.zero(self.innerWidth);
		}
		return width;
	};
	
	// Get the height of the entire document
	// --------------------------------------------------------------------
	screen.getDocumentHeight = function() {
		var body = screen.getBody();
		var innerHeight = (Util.defined(self.innerHeight)&&!isNaN(self.innerHeight))?self.innerHeight:0;
		if (document.documentElement && (!document.compatMode || document.compatMode=="CSS1Compat")) {
		    var topMargin = parseInt(CSS.get(body,'marginTop'),10) || 0;
		    var bottomMargin = parseInt(CSS.get(body,'marginBottom'), 10) || 0;
			return Math.max(body.offsetHeight + topMargin + bottomMargin, document.documentElement.clientHeight, document.documentElement.scrollHeight, screen.zero(self.innerHeight));
		}
		return Math.max(body.scrollHeight, body.clientHeight, screen.zero(self.innerHeight));
	};
	
	// Get the width of the viewport (viewable area) in the browser window
	// --------------------------------------------------------------------
	screen.getViewportWidth = function() {
		if (document.documentElement && (!document.compatMode || document.compatMode=="CSS1Compat")) {
			return document.documentElement.clientWidth;
		}
		else if (document.compatMode && document.body) {
			return document.body.clientWidth;
		}
		return screen.zero(self.innerWidth);
	};
	
	// Get the height of the viewport (viewable area) in the browser window
	// --------------------------------------------------------------------
	screen.getViewportHeight = function() {
		if (!window.opera && document.documentElement && (!document.compatMode || document.compatMode=="CSS1Compat")) {
			return document.documentElement.clientHeight;
		}
		else if (document.compatMode && !window.opera && document.body) {
			return document.body.clientHeight;
		}
		return screen.zero(self.innerHeight);
	};

	return screen;
})();
var Sort = (function(){
	var sort = {};
	sort.AlphaNumeric = function(a,b) {
		if (a==b) { return 0; }
		if (a<b) { return -1; }
		return 1;
	};

	sort.Default = sort.AlphaNumeric;
	
	sort.NumericConversion = function(val) {
		if (typeof(val)!="number") {
			if (typeof(val)=="string") {
				val = parseFloat(val.replace(/,/g,''));
				if (isNaN(val) || val==null) { val=0; }
			}
			else {
				val = 0;
			}
		}
		return val;
	};
	
	sort.Numeric = function(a,b) {
		return sort.NumericConversion(a)-sort.NumericConversion(b);
	};

	sort.IgnoreCaseConversion = function(val) {
		if (val==null) { val=""; }
		return (""+val).toLowerCase();
	};

	sort.IgnoreCase = function(a,b) {
		return sort.AlphaNumeric(sort.IgnoreCaseConversion(a),sort.IgnoreCaseConversion(b));
	};

	sort.CurrencyConversion = function(val) {
		if (typeof(val)=="string") {
			val = val.trim().replace(/^[^\d\.]/,'');
		}
		return sort.NumericConversion(val);
	};
	
	sort.Currency = function(a,b) {
		return sort.Numeric(sort.CurrencyConversion(a),sort.CurrencyConversion(b));
	};
	
	sort.DateConversion = function(val) {
		// inner util function to parse date formats
		function getdate(str) {
			// inner util function to convert 2-digit years to 4
			function fixYear(yr) {
				yr = +yr;
				if (yr<50) { yr += 2000; }
				else if (yr<100) { yr += 1900; }
				return yr;
			};
			var ret;
			// YYYY-MM-DD
			if (ret=str.match(/(\d{2,4})-(\d{1,2})-(\d{1,2})/)) {
				return (fixYear(ret[1])*10000) + (ret[2]*100) + (+ret[3]);
			}
			// MM/DD/YY[YY] or MM-DD-YY[YY]
			if (ret=str.match(/(\d{1,2})[\/-](\d{1,2})[\/-](\d{2,4})/)) {
				return (fixYear(ret[3])*10000) + (ret[1]*100) + (+ret[2]);
			}
			return 99999999; // So non-parsed dates will be last, not first
		};
		return getdate(val);
	};

	sort.Date = function(a,b) {
		return sort.Numeric(sort.DateConversion(a),sort.DateConversion(b));
	};

	return sort;
})();// Functions for interacting with Tables
// =====================================
var table = (function () {

    var Table = {};
    
    Table.VERSION = .955;
    
    Table.Sort = Sort;
    
    // Get a parent TABLE object reference from any element within it
    // --------------------------------------------------------------
    Table.getTable = function(o) {
    	if (o==null) { 
    		return o; 
    	}
    	return DOM.getParentByTagName(o,"TABLE");
    };
    
    // Resolve a table given an element reference, and make sure it has an ID
    // ----------------------------------------------------------------------
    Table.resolve = function(o) {
    	if (o==null) { return null; }
    	if (o.nodeName && o.nodeName!="TABLE") {
    		o = this.getTable(o);
    	}
    	CSS.createId(o);
    	return o;
    };
    
    // Find the table cell containing any passed-in element
    // ----------------------------------------------------
    Table.getCell = function(o) {
    	if (o==null) { return null; }
    	if (o.nodeName && o.nodeName!="TH" && o.nodeName!="TD") {
    		o = DOM.getParentByTagName(o,["TD","TH"]);
    	}
    	return o;
    };
    
    // Expand all hidden TBODY tags	
    // ----------------------------
    Table.expandBodies = function(t) {
    	var bodies = this.getBodies(t);
    	if (bodies==null) { 
    		return bodies; 
    	}
    	for (var i=0; i<bodies.length; i++) {
    		if (CSS.get(bodies[i],"display")=="none") {
    			CSS.set(bodies[i],"display","block");
    		}
    	}
    };
    
    // Get all the tbody elements in a table
    // -------------------------------------
    Table.getBodies = function(t) {
    	if (t==null) { 
    		return t; 
    	}
    	if (t.getElementsByTagName) {
    		return t.getElementsByTagName("TBODY");
    	}
    	return null;
    };
    
    // Get all the thead elements in a table
    // -------------------------------------
    Table.getHeads = function(t) {
    	if (t==null) { 
    		return t; 
    	}
    	if (t.getElementsByTagName) {
    		return t.getElementsByTagName("THEAD");
    	}
    	return null;
    };
    
    // Expand all table rows and hide the row containing the onclick action
    // --------------------------------------------------------------------
    Table.expandRowClicked = function(o) {
    	var t, tr;
    	if ((tr = DOM.getParentByTagName(o,"TR"))==null) { 
    		return; 
    	}
    	if ((t = this.getTable(tr))==null) { 
    		return; 
    	}
    	this.expandBodies(t);
    	CSS.setStyle(tr,"display","none");
    };
    
    // Run a function against each cell in a table header, usually to add
    // or remove css classes based on sorting, filtering, etc.
    // ------------------------------------------------------------------
    Table.processHeaderCells = function(t, func) {
    	t = this.resolve(t);
    	if (t==null) { return; }
    	var theads = this.getHeads(t);
    	for (var i=0; i<theads.length; i++) {
    		var th = theads[i];
    		if (th.rows && th.rows.length && th.rows.length>0) { 
    			var rows = th.rows;
    			var len = rows.length;
    			for (var j=0; j<len; j++) { 
    				var row = rows[j];
    				if (row.cells && row.cells.length && row.cells.length>0) {
    					var cells = row.cells;
    					var len2 = cells.length;
    					for (var k=0; k<len2; k++) {
    						var cellsK = cells[k];
    						func(cellsK);
    					}
    				}
    			}
    		}
    	}
    };
    
    // Get the cellIndex value for a cell. This is only needed because of a Safari
    // bug that causes cellIndex to exist but always be 0.
    // Rather than feature-detecting each time it is called, the function will
    // re-write itself the first time it is called.
    // ---------------------------------------------------------------------------
    Table.getCellIndex = function(td) {
    	var tr = td.parentNode;
    	var cells = tr.cells;
    	if (cells && cells.length) {
    		if (cells.length>1 && cells[cells.length-1].cellIndex && cells[cells.length-1].cellIndex>0) {
    			this.getCellIndex = function(td) {
    				return td.cellIndex;
    			};
    			return td.cellIndex;
    		}
    		for (var i=0; i<cells.length; i++) {
    			if (tr.cells[i]==td) {
    				return i;
    			}
    		}
    	}
    	return 0;
    };
    
    // Get the text value of a cell. Only use innerText if explicitly told to, because 
    // otherwise we want to be able to handle sorting on inputs and other types
    // -------------------------------------------------------------------------------
    Table.getCellValue = function(td,useInnerText) {
    	if (td==null) { 
    		return null; 
    	}
    	if (useInnerText && Util.defined(td.innerText)) {
    		return td.innerText;
    	}
    	if (!td.childNodes) { 
    		return ""; 
    	}
    	var childNodes = td.childNodes;
    	var childNodesLength = childNodes.length;
    	var node = null;
    	var ret = "";
    	for (var i=0; i<childNodesLength; i++) {
    		node = childNodes[i];
    		if (node.nodeType && node.nodeType==1) {
    			if (node.nodeName=="INPUT" && Util.defined(node.value)) {
    				if (node.type && (node.type!="checkbox" || node.checked)) {
    					ret += node.value;
    				}
    			}
    			else if (node.nodeName=="SELECT" && node.selectedIndex>=0 && node.options) {
    				// Sort select elements by the visible text
    				ret += node.options[node.selectedIndex].text;
    			}
    			else if (node.nodeName=="IMG" && node.name) {
    				// Sort image elements by their NAME attribute
    				ret += node.name;
    			}
    			else {
    				ret += this.getCellValue(node);
    			}
    		}
    		else {
    			if (node.nodeType && node.nodeType==3) {
    				if (Util.defined(node.innerText)) {
    					ret += node.innerText;
    				}
    				else if (Util.defined(node.nodeValue)) {
    					ret += node.nodeValue;
    				}
    			}
    		}
    	}
    	return ret;
    };
    
    // Consider colspan and rowspan values in table header cells to calculate the actual cellIndex
    // of a given cell
    // -------------------------------------------------------------------------------------------
    Table.tableHeaderIndexes = {};
    Table.getActualCellIndex = function(tableCellObj) {
    	var tableObj = this.getTable(tableCellObj);
    	var cellCoordinates = tableCellObj.parentNode.rowIndex+"-"+this.getCellIndex(tableCellObj);
    
    	// If it has already been computed, return the answer from the lookup table
    	if (typeof(this.tableHeaderIndexes[tableObj.id])!='undefined') {
    		return this.tableHeaderIndexes[tableObj.id][cellCoordinates];			
    	} 
    
    	var matrix = [];
    	this.tableHeaderIndexes[tableObj.id] = {};
    	var thead = tableObj.getElementsByTagName('THEAD')[0];
    	var trs = thead.getElementsByTagName('TR');
    
    	// Loop thru every tr and every cell in the tr, building up a 2-d array "grid" that gets
    	// populated with an "x" for each space that a cell takes up. If the first cell is colspan
    	// 2, it will fill in values [0] and [1] in the first array, so that the second cell will
    	// find the first empty cell in the first row (which will be [2]) and know that this is
    	// where it sits, rather than its internal .cellIndex value of [1].
    	for (var i=0; i<trs.length; i++) {
    		var cells = trs[i].cells;
    		for (var j=0; j<cells.length; j++) {
    			var c = cells[j];
    			var rowIndex = c.parentNode.rowIndex;
    			var cellId = rowIndex+"-"+this.getCellIndex(c);
    			var rowSpan = c.rowSpan || 1;
    			var colSpan = c.colSpan || 1
    			var firstAvailCol;
    			if(typeof(matrix[rowIndex])=="undefined") { 
    				matrix[rowIndex] = []; 
    			}
    			var m = matrix[rowIndex];
    			// Find first available column in the first row
    			for (var k=0; k<m.length+1; k++) {
    				if (typeof(m[k])=="undefined") {
    					firstAvailCol = k;
    					break;
    				}
    			}
    			this.tableHeaderIndexes[tableObj.id][cellId] = firstAvailCol;
    			for (var k=rowIndex; k<rowIndex+rowSpan; k++) {
    				if(typeof(matrix[k])=="undefined") { 
    					matrix[k] = []; 
    				}
    				var matrixrow = matrix[k];
    				for (var l=firstAvailCol; l<firstAvailCol+colSpan; l++) {
    					matrixrow[l] = "x";
    				}
    			}
    		}
    	}
    	// Store the map so future lookups are fast.
    	return this.tableHeaderIndexes[tableObj.id][cellCoordinates];
    };
    
    
    // Sort all rows in each TBODY (tbodies are sorted independent of each other)
    // --------------------------------------------------------------------------
    Table.lastSortedColumn = {};
    Table.SortedAscendingClassName = "TableSortedAscending";
    Table.SortedDescendingClassName = "TableSortedDescending";
    Table.SortableClassName = "sortable";
    
    Table.sort = function(t,args) {
    	
        var colIndex, sortType, descending, rowShade, ignoreHiddenRows;
    	if (!Util.defined(args)) { 
    		args = {}; 
    	}
    
    	// Save a ref to the original object passed in
    	var origT = t;
    
    	if (t==null) { 
    		return; 
    	}
    	// Resolve the table
    	t = this.resolve(t);
    
    	// Resolve actual colIndex
    	if (Util.defined(args['colIndex'])) {
    		colIndex = args['colIndex'];
    	} else if (Util.defined(origT) && Util.defined(origT.cellIndex)) {
    		colIndex = this.getActualCellIndex(origT);
    	} else {
    		colIndex = 0;
    	}
    
    	// Resolve sortType
    	sortType = ((!Util.defined(args['sortType'])) || (typeof(args['sortType'])!="function")) ? Sort.Default : args['sortType'];
    
    	// Resolve descending
    	if (Util.defined(this.lastSortedColumn[t.id]) && this.lastSortedColumn[t.id]['index']==colIndex) {
    		descending = !(this.lastSortedColumn[t.id]['descending']);
    	} else if (Util.defined(args['descending']) && typeof(args['descending'])=="boolean") { 
    		descending = args['descending'];
    	} else {
    		descending = false;
    	}
    
    	// Resolve whether or not to consider hidden rows when shading alternate rows
    	ignoreHiddenRows = (Util.defined(args['ignoreHiddenRows'])) ? args['ignoreHiddenRows'] : false;
    
    	// Class name corresponding to the sort order
    	var sortedAscendingClassName = this.SortedAscendingClassName;
    	var sortedDescendingClassName = this.SortedDescendingClassName;
    	var sortableClassName = this.SortableClassName;
    	var sortedClassName = descending?sortedDescendingClassName:sortedAscendingClassName;
    
    	// If standard sorting functions are used, convert each cell value in advance using a conversion
    	// function, then sort by alphanumeric so sorting is much faster
    	var sortConversion = false;
    	if (sortType==Sort.Default || sortType==Sort.AlphaNumeric) {
    		sortType=Sort.AlphaNumeric;
    	} else if (sortType==Sort.IgnoreCase) {
    		sortConversion = Sort.IgnoreCaseConversion;
    		sortType=Sort.AlphaNumeric;
    	} else if (sortType==Sort.Numeric) {
    		sortConversion = Sort.NumericConversion;
    		sortType=Sort.AlphaNumeric;
    	} else if (sortType==Sort.Currency) {
    		sortConversion = Sort.CurrencyConversion;
    		sortType=Sort.AlphaNumeric;
    	} else if (sortType==Sort.Date) {
    		sortConversion = Sort.DateConversion;
    		sortType=Sort.AlphaNumeric;
    	}
    
    	// Store the last sorted column so clicking again will reverse the sort order
    	this.lastSortedColumn[t.id] = {'index':colIndex, 'descending':descending};
    
    	// Loop through all THEADs and remove sorted class names, then re-add them for the colIndex
    	// that is being sorted
    	var self = this;
    	this.processHeaderCells(t,
    		function(cell) {
    			if (CSS.hasClass(cell,sortableClassName)) {
    				CSS.removeClass(cell,sortedAscendingClassName);
    				CSS.removeClass(cell,sortedDescendingClassName);
    				// If the computed colIndex of the cell equals the sorted colIndex, flag it as sorted
    				if (colIndex==self.getActualCellIndex(cell)) {
    					CSS.addClass(cell,sortedClassName);
    				}
    			}
    		}
    	);
    
    	// Sort each tbody independently
    	var bodies = this.getBodies(t);
    	if (bodies==null || bodies.length==0) { return; }
    	for (var i=0; i<bodies.length; i++) {
    		var tb = bodies[i];
    		var tbrows = tb.rows;
    		var tbrowslength = tbrows.length;
    		var rows = [];
    
    		// If there are no inputs in the tbody, feel free to use innerText which is faster
    		var useInnerText = (tb.getElementsByTagName('INPUT').length==0);
    
    		// Create a separate array which will store the converted values and refs to the
    		// actual tows. This is the array that will be sorted.
    		var cRow;
    		var cRowIndex=0;
    		if (cRow=tbrows[cRowIndex]){
    			// Funky loop style because it's faster in IE
    			do {
    				if (rowCells = cRow.cells) {
    					var cellValue = (rowCells&&colIndex<rowCells.length)?this.getCellValue(rowCells[colIndex],useInnerText):null;
    					if (sortConversion) cellValue = sortConversion(cellValue);
    					rows[cRowIndex] = [cellValue,tbrows[cRowIndex]];
    				}
    			} while (cRow=tbrows[++cRowIndex])
    		}
    
    		// Do the actual sorting
    		var newSortFunc = function(a,b) {
    			return (descending)?sortType(b[0],a[0]):sortType(a[0],b[0]);
    		};
    		rows.sort(newSortFunc);
    
    		// Move the rows to the correctly sorted order. Appending an existing DOM object
    		// just moves it!
    		var cRow;
    		var cRowIndex=0;
    		if (cRow=rows[cRowIndex]){
    			do { tb.appendChild(cRow[1]); } while (cRow=rows[++cRowIndex])
    		}
    	}
    
    	// Re-shade alternate rows if a class name was supplied
    	if (Util.defined(args['rowShade'])) {
    		this.shadeOddRows(t,args['rowShade'],ignoreHiddenRows);
    	}
    };
    
    // Filter all rows in each TBODY
    // -----------------------------
    // TODO: Finish up and Test table filtering!
    
    Table.FilteredClassName = "TableFiltered";
    Table.FilterableClassName = "filterable";
    Table.Filters = {};
    
    Table.filter = function(t,filters,args) {
    	var colIndex, rowShade, filter, allFilters;
    	var reset = false; // If null is passed in, reset the whole table
    	if (!Util.defined(args)) { args = {}; }
    	// Filters can either be sent in one at a time or as a group
    	if (!Util.defined(filters)) { return; }
    	// Allow for passing a select list in as the filter, since this is common design
    	if (filters && filters.nodeName && filters.nodeName=="SELECT" && filters.type) {
    		if (filters.type=="select-one" && Util.defined(filters.selectedIndex) && filters.selectedIndex>-1) {
    			var sel = filters;
    			filters = {};
    			filters.filter = sel.options[sel.selectedIndex].value;
    		}
    	}
    	if (Util.isObject(filters) && !Util.isArray(filters)) {
    		filters = [filters];
    	}
    	else if (filters==null) {
    		reset = true;
    		filters = [];
    	}
    	else { return; }
    
    	// Find the cell
    	t = this.getCell(t);
    	
    	// Resolve colIndex for each filter
    	for (var i=0; i<filters.length; i++) {
    		colIndex = filters[i].colIndex;
    		if (!Util.defined(colIndex) && Util.defined(t) && Util.defined(t.cellIndex)) {
    			filters[i].colIndex = this.getCellIndex(t);
    		}
    	}
    
    	if (t==null) { return; }
    	// Resolve the table
    	t = this.resolve(t);
    
    	// Update the list of all active filters for this table
    	if (!Util.defined(this.Filters[t.id]) || reset) {
    		this.Filters[t.id] = {};
    	}
    	var allFilters = this.Filters[t.id];
    	for (var i=0; i<filters.length; i++) {
    		filter = filters[i];
    		if (filter.filter==null || filter.filter=="") {
    			delete allFilters[filter.colIndex];
    		}
    		else {
    			allFilters[filter.colIndex] = filter.filter;
    		}
    	}
    
    	// Filter all tbodies of the table
    	var bodies = this.getBodies(t);
    	if (bodies==null || bodies.length==0) { return; }
    	for (var i=0; i<bodies.length; i++) {
    		var tb = bodies[i];
    		for (var j=0; j<tb.rows.length; j++) {
    			var row = tb.rows[j];
    			if (reset) {
    				row.style.display="";
    			}
    			else if (row.cells) {
    				var cells = row.cells;
    				var cellsLength = cells.length;
    				// Test each filter
    				var hide = false;
    				for (colIndex in allFilters) {
    					if (!hide) {
    						filter = allFilters[colIndex];
    						if (colIndex < cellsLength) {
    							var val = this.getCellValue(cells[colIndex]);
    							if (filter.charAt(0)=="/" && val.search) {
    								hide = (val.search(new RegExp(filter.substring(1,filter.length-1)))<0);
    							}
    							else if (val!=filter) {
    								hide = true;
    							}
    						}
    					}
    				}
    				if (hide) {
    					row.style.display = "none";
    				}
    				else {
    					row.style.display="";
    				}
    			}
    		}
    	}
    
    	// Loop through all THEADs and add filtered class names
    	var self = this;
    	this.processHeaderCells(t,
    		function(cell) {
    			if (Util.defined(allFilters[self.getCellIndex(cell)]) && CSS.hasClass(cell,self.FilterableClassName)) {
    				CSS.addClass(cell,self.FilteredClassName);
    			}
    			else {
    				CSS.removeClass(cell,self.FilteredClassName);
    			}
    		}
    	);
    
    	// Shade rows if a class name was supplied
    	if (Util.defined(args['rowShade'])) {
    		this.shadeOddRows(t,args['rowShade']);
    	}
    };
    
    // Shade alternate rows
    // --------------------
    Table.shadeOddRows = function(t,className,ignoreHiddenRows) { 
    	if (t==null) { 
    		return;
    	}
    	ignoreHiddenRows = (Util.defined(ignoreHiddenRows) && typeof(ignoreHiddenRows)=="boolean") ? ignoreHiddenRows : false;
    	t = this.resolve(t);
    	var bodies = this.getBodies(t);
    	if (bodies==null || bodies.length==0) { 
    		return; 
    	}
    	for (var i=0; i<bodies.length; i++) {
    		var tb = bodies[i];
    		var tbrows = tb.rows;
    		var cRowIndex=0;
    		var cRow;
    		var displayedCount=0;
    		if (cRow=tbrows[cRowIndex]){
    			do {
    				if (ignoreHiddenRows || CSS.getStyle(cRow,"display")!="none") {
    					if (displayedCount++%2==0) { 
    						CSS.removeClass(cRow,className); 
    					}
    					else { 
    						CSS.addClass(cRow,className); 
    					}
    				}
    			} while (cRow=tbrows[++cRowIndex])
    		}
    	}
    };
    
    // "Page" a table by showing only a subset of the rows
    // ---------------------------------------------------
    Table.pages = {};
    Table.page = function(t,pageIndex,pageSize,args) {
    	if (!Util.defined(args)) { args = {}; }
    	if (!Util.defined(pageSize) || typeof(pageSize)!="number" || pageSize==0) {
    		pageSize = 25; // arbitrary default
    	}
    	if (!Util.defined(pageIndex) || typeof(pageIndex)!="number") {
    		pageIndex = 0;
    	}
    
    	var startRow = pageIndex*pageSize;
    	var endRow = startRow + pageSize - 1;
    	
    	if (t==null) { return; }
    	// Resolve the table
    	t = this.resolve(t);
    
    	// Assumption: only one tbody!
    	var bodies = this.getBodies(t);
    	if (bodies==null || bodies.length==0) { return; }
    	var tb = bodies[0];
    
    	// Don't let the page go past the beginning
    	if (startRow<0) {
    		pageIndex = 0;
    		startRow = 0;
    		endRow = startRow + pageSize - 1;
    	}
    	// Don't let the page go past the end
    	if (startRow > tb.rows.length) {
    		pageIndex = Math.floor(tb.rows.length/pageSize);
    		if (pageIndex==tb.rows.length/pageSize) {
    			pageIndex--;
    		}
    		startRow = pageIndex * pageSize;
    		endRow = startRow + pageSize;
    	}
    
    	// Store the table's current state	
    	this.pages[t.id] = { 'pageIndex':pageIndex, 'pageSize':pageSize };
    
    	for (var i=0; i<tb.rows.length; i++) {
    		var row = tb.rows[i];
    		if (i<startRow || i>endRow) {
    			row.style.display="none";
    		}
    		else {
    			row.style.display="";
    		}
    	}
    
    	// Shade rows if a class name was supplied
    	if (Util.defined(args['rowShade'])) {
    		this.shadeOddRows(t,args['rowShade']);
    	}
    }
    
    Table.pageNext = function(t,pageSize,args) {
    	t = this.resolve(t);
    	if (Util.defined(Table.pages[t.id])) {
    		var pages = Table.pages[t.id];
    		var newPage = pages.pageIndex+1;
    		this.page(t,newPage,pageSize || pages.pageSize,args);
    		return newPage;
    	}
    	else {
    		this.page(t,1,pageSize,args);
    		return 1;
    	}
    	return -1;
    };
    
    Table.pagePrevious = function(t,pageSize,args) {
    	t = this.resolve(t);
    	if (Util.defined(Table.pages[t.id])) {
    		var pages = Table.pages[t.id];
    		var newPage = pages.pageIndex-1;
    		this.page(t,newPage,pageSize || pages.pageSize,args);
    		return newPage;
    	}
    	else {
    		this.page(t,0,pageSize,args);
    		return 0;
    	}
    	return -1;
    };
    
    return Table;
})();

return table;
})();
//------------------------------------------------------------------------------

// ===================================================================
// Author: Matt Kruse <matt@mattkruse.com>
// WWW: http://www.mattkruse.com/
//
// NOTICE: You may use this code for any purpose, commercial or
// private, without any further permission from the author. You may
// remove this notice from your final code if you wish, however it is
// appreciated by the author if at least my web site address is kept.
//
// You may *NOT* re-distribute this code in any way except through its
// use. That means, you can include it in your product, or your web
// site, or any other form where the code is actually being used. You
// may not put the plain javascript up on your site for download or
// include it in your javascript libraries for download. 
// If you wish to share this code with others, please just point them
// to the URL instead.
// Please DO NOT link directly to my .js files from your site. Copy
// the files to your server and use them there. Thank you.
// ===================================================================

// HISTORY
// ------------------------------------------------------------------
// December 9, 2003: Added script to the Javascript Toolbox
// December 10, 2003: Added the preProcessTrees variable to allow user
//      to turn off automatic conversion of UL's onLoad
// March 1, 2004: Changed it so if a <li> has a class already attached
//      to it, that class won't be erased when initialized. This allows
//      you to set the state of the tree when painting the page simply
//      by setting some <li>'s class name as being "liOpen" (see example)
/*
This code is inspired by and extended from Stuart Langridge's aqlist code:
		http://www.kryogenix.org/code/browser/aqlists/
		Stuart Langridge, November 2002
		sil@kryogenix.org
		Inspired by Aaron's labels.js (http://youngpup.net/demos/labels/) 
		and Dave Lindquist's menuDropDown.js (http://www.gazingus.org/dhtml/?id=109)
*/
org.lib.mktree = new Class({
// Automatically attach a listener to the window onload, to convert the trees
initialize: function() {
    //this.addEvent(window,"load",convertTrees);
}
,
// Utility function to add an event listener
addEvent: function (o,e,f){
	if (o.addEventListener){ o.addEventListener(e,f,true); return true; }
	else if (o.attachEvent){ return o.attachEvent("on"+e,f); }
	else { return false; }
}
,
// utility function to set a global variable if it is not already set
setDefault: function (name,val) {
	if (typeof(this[name])=="undefined" || this[name]==null) {
		this[name]=val;
	}
}
,
// Full expands a tree with a given ID
expandTree: function (treeId) {
	var ul = document.getElementById(treeId);
	if (ul == null) { return false; }
	this.expandCollapseList(ul,this.nodeOpenClass);
}
,
// Fully collapses a tree with a given ID
collapseTree: function (treeId) {
	var ul = document.getElementById(treeId);
	if (ul == null) { return false; }
	this.expandCollapseList(ul,this.nodeClosedClass);
}
,
// Expands enough nodes to expose an LI with a given ID
expandToItem: function (treeId,itemId) {
	var ul = document.getElementById(treeId);
	if (ul == null) { return false; }
	var ret = this.expandCollapseList(ul,this.nodeOpenClass,itemId);
	if (ret) {
		var o = document.getElementById(itemId);
		if (o.scrollIntoView) {
			o.scrollIntoView(false);
		}
	}
}
,
// Performs 3 functions:
// a) Expand all nodes
// b) Collapse all nodes
// c) Expand all nodes to reach a certain ID
expandCollapseList: function (ul,cName,itemId) {
	if (!ul.childNodes || ul.childNodes.length==0) { return false; }
	// Iterate LIs
	for (var itemi=0;itemi<ul.childNodes.length;itemi++) {
		var item = ul.childNodes[itemi];
		if (itemId!=null && item.id==itemId) { return true; }
		if (item.nodeName == "LI") {
			// Iterate things in this LI
			var subLists = false;
			for (var sitemi=0;sitemi<item.childNodes.length;sitemi++) {
				var sitem = item.childNodes[sitemi];
				if (sitem.nodeName=="UL") {
					subLists = true;
					var ret = this.expandCollapseList(sitem,cName,itemId);
					if (itemId!=null && ret) {
						item.className=cName;
						return true;
					}
				}
			}
			if (subLists && itemId==null) {
				item.className = cName;
			}
		}
	}
}
,
// Search the document for UL elements with the correct CLASS name, then process them
convertTrees: function () {
	this.setDefault("treeClass","mktree");
	this.setDefault("nodeClosedClass","liClosed");
	this.setDefault("nodeOpenClass","liOpen");
	this.setDefault("nodeBulletClass","liBullet");
	this.setDefault("nodeLinkClass","bullet");
	this.setDefault("preProcessTrees",true);
	if (this.preProcessTrees) {
		if (!document.createElement) { return; } // Without createElement, we can't do anything
		uls = document.getElementsByTagName("ul");
		for (var uli=0;uli<uls.length;uli++) {
			var ul=uls[uli];
			if (ul.nodeName=="UL" && ul.className==this.treeClass) {
				this.processList(ul);
			}
		}
	}
}
,
// Process a UL tag and all its children, to convert to a tree
processList: function (ul) {
	if (!ul.childNodes || ul.childNodes.length==0) { return; }
	// Iterate LIs
	for (var itemi=0;itemi<ul.childNodes.length;itemi++) {
		var item = ul.childNodes[itemi];
		if (item.nodeName == "LI") {
			// Iterate things in this LI
			var subLists = false;
			for (var sitemi=0;sitemi<item.childNodes.length;sitemi++) {
				var sitem = item.childNodes[sitemi];
				if (sitem.nodeName=="UL") {
					subLists = true;
					this.processList(sitem);
				}
			}
			var s= document.createElement("SPAN");
			var t= '\u00A0'; // &nbsp;
			s.className = this.nodeLinkClass;
			if (subLists) {
				// This LI has UL's in it, so it's a +/- node
				if (item.className==null || item.className=="") {
					item.className = nodeClosedClass;
				}
				// If it's just text, make the text work as the link also
				if (item.firstChild.nodeName=="#text") {
					t = t+item.firstChild.nodeValue;
					item.removeChild(item.firstChild);
				}
				var nodeOpenClass = this.nodeOpenClass;
				var nodeClosedClass = this.nodeClosedClass;
				s.onclick = function () {
					this.parentNode.className = (this.parentNode.className==nodeOpenClass) ? nodeClosedClass : nodeOpenClass;
					return false;
				}
			}
			else {
				// No sublists, so it's just a bullet node
				item.className = this.nodeBulletClass;
				s.onclick = function () { return false; }
			}
			s.appendChild(document.createTextNode(t));
			item.insertBefore(s,item.firstChild);
		}
	}
}

});
//------------------------------------------------------------------------------
// JavaScript Document
org.berthomg.createNamespace('org.lib.mootools', 0.1);
//org.berthomg.createNamespace('org.lib.mootools.BehaviourBaseClass', 0.1);
//org.berthomg.createNamespace('org.lib.mootools.Behaviour', 0.1);
/**
 * the mootools version of Behaviour.js
 */ 
org.lib.mootools.BehaviourBaseClass = new Class({
    initialize: function(){
        this.behaviours = [];
        var bhvr = this;
        Window.onDomReady(function(){bhvr.apply();});
    },
    register: function(actions){
        if(! this.behaviours.test(actions))
            this.behaviours.push(actions);
    },
    apply: function(actions) {
        if ($type(actions)!='array') {
            actions = this.behaviours;
        }
        actions.each(function(bhvrs){
            for (bhvr in bhvrs){
                try {
                    if($type(bhvrs[bhvr])=='function') {
                        $ES(bhvr).each(function(el){
                            bhvrs[bhvr](el);
                        });
                    }
                } catch(e){alert(e.message);}
            }
        });
    }
});
/**
 * the mootools version of Behaviour.js
 */ 
org.lib.mootools.Behaviour = new org.lib.mootools.BehaviourBaseClass();
//------------------------------------------------------------------------------
org.berthomg.createNamespace('org.berthomg.DesignPattern', 0.1);

org.berthomg.DesignPattern.Observer = new Class({
    initialize: function() {
    },
    update: function() {
        return;
    }
});

org.berthomg.DesignPattern.Observable = new Class({
    initialize: function() {
        this.observers = [];
    },
	notify: function(context) {
        var m_count = this.observers.length;
		for( var i = 0; i < m_count; i++ )
			this.observers[i].update(context);
    },
    addObserver: function(observer) {
        if( !observer.update ) throw 'Wrong parameter';
		this.observers.push(observer);
    },
    removeObserver: function(observer) {
        if( !observer.update ) throw 'Wrong parameter';
        if(this.observers.contains(observer))
            this.observers.remove(observer);
    }	
});

org.berthomg.DesignPattern.makeObservable = function(observable) {
    org.berthomg.Class.copyProperties(new org.berthomg.DesignPattern.Observable(), observable);
};

org.berthomg.DesignPattern.makeObserver = function(observer) {
    org.berthomg.Class.copyProperties(new org.berthomg.DesignPattern.Observer(), observer);
};
//------------------------------------------------------------------------------
//org.berthomg.createNamespace('org.berthomg.Loader', 0.1);
org.berthomg.Loader = new Class({
    _image: null,
    _e: null,
    _inBody: false,
    
    initialize: function(imageSrc) {
        this._image = document.createElement('img');
        if(typeof(imageSrc) == 'string') {
            this._image.src = imageSrc;
        } else {
            this._image.src = '/genoret/Project/images/gvee.gif';
        }
        this._e = document.createElement('div');
        if(window.ie) {
            this._e.style.position   = 'absolute';//absolute pour IE
        } else {
            this._e.style.position   = 'fixed';//fixed sur les autres
        }
        this._e.style.top        = '50%';
        this._e.style.left       = '50%';
        this._e.style.padding    = '0px 5px 0px 5px';
        this._e.style.margin     = '-50px 0 0 -150px';
        this._e.style.textAlign  = 'center';
        this._e.style.color      = '#000000';
        this._e.style.border     = '1px solid #FF0000';
        this._e.style.background = '#FFFFFF';
        this._e.style.display    = 'none';
        this._e.style.zIndex     = '9999';
        
        var table = document.createElement("TABLE");
        var tbody = document.createElement("TBODY");
        var tr1 = document.createElement("TR");
        var td1 = document.createElement("TD");
        var tr2 = document.createElement("TR");
        var td2 = document.createElement("TD");
        var text = document.createTextNode('Loading, please wait\u2026');
        
        td1.appendChild(text);
        td2.appendChild(this._image);
        tr1.appendChild(td1);
        tr2.appendChild(td2);
        tbody.appendChild(tr1);
        tbody.appendChild(tr2);
        table.appendChild(tbody);
        this._e.appendChild(table);
    },

	_add2Body: function() {
		if(!this._inBody) {
			window.document.body.appendChild(this._e);
			this._inBody = true;
		}
	},
	
    load: function() {
    	this._add2Body();
        this._e.style.display = 'block';    
    },
    
    kill: function() {
    	this._add2Body();
        this._e.style.display = 'none';    
    }
});
//------------------------------------------------------------------------------
//org.berthomg.createNamespace('org.berthomg.Selection', 0.1);
/*
HOW TO USE:
var sel = new org.berthomg.Selection();
alert(sel.getSelectedText());
alert(sel.getRangeObject());
*/
org.berthomg.Selection = new Class({
    selection: null,
    
    initialize: function() {    
    },
    
    _buildSelection: function() {
        if (window.getSelection) { //Mozilla, Safari and Opera
        	this.selection = window.getSelection();
        } else if (document.selection) { // Microsoft should come last; Opera!
        	this.selection = document.selection.createRange();
        }
    },
    
    getSelectedText: function() {
        this._buildSelection();        
        if(this.selection.text) {
            selectedText = this.selection.text;
        } else {
            selectedText = this.selection;
        }
        return selectedText;
    },
    
    getRangeObject: function() {
        this._buildSelection();
        if(window.ie) { //IE
            return this.selection;
        } else if (this.selection.getRangeAt) { //Mozilla, etc.
    		return this.selection.getRangeAt(0);
    	} else { // Safari!
    		var range = document.createRange();
    		range.setStart(this.selection.anchorNode,this.selection.anchorOffset);
    		range.setEnd(this.selection.focusNode,this.selection.focusOffset);
    		return range;
    	}
    }
});
//------------------------------------------------------------------------------


