<script language="javascript" type="text/javascript"> function playSound(soundfile) { document.getElementById("dummy").innerHTML= "<embed src=\""+soundfile+"\" hidden=\"true\" autostart=\"true\" loop=\"false\" />"; } </script>


function filter(imagename,objectsrc){
if (document.images)
document.images[imagename].src=eval(objectsrc+".src")
}


/*
Required field(s) validation- By NavSurf
Visit NavSurf.com at http://navsurf.com
Visit http://www.dynamicdrive.com for this script
*/

function formCheck(formobj){
	//1) Enter name of mandatory fields
	var fieldRequired = Array("nom", "ville", "email");
	//2) Enter field description to appear in the dialog box
	var fieldDescription = Array("nom", "prenom", "ville", "email");
	//3) Enter dialog message
	var alertMsg = "Veuillez remplir les champs suivants :\n";
	
	var l_Msg = alertMsg.length;
	
	for (var i = 0; i < fieldRequired.length; i++){
		var obj = formobj.elements[fieldRequired[i]];
		if (obj){
			switch(obj.type){
			case "select-one":
				if (obj.selectedIndex == -1 || obj.options[obj.selectedIndex].text == ""){
					alertMsg += " - " + fieldDescription[i] + "\n";
				}
				break;
			case "select-multiple":
				if (obj.selectedIndex == -1){
					alertMsg += " - " + fieldDescription[i] + "\n";
				}
				break;
			case "text":
			case "textarea":
				if (obj.value == "" || obj.value == null){
					alertMsg += " - " + fieldDescription[i] + "\n";
				}
				break;
			default:
				if (obj.value == "" || obj.value == null){
					alertMsg += " - " + fieldDescription[i] + "\n";
				}
			}
		}
	}

	if (alertMsg.length == l_Msg){
		return true;
	}else{
		alert(alertMsg);
		return false;
	}
}
//-->



a1=new Image(80,40)
a1.src="images/spec-on.gif"
a2=new Image(80,40)
a2.src="images/spec-off.gif"

a3=new Image(80,40)
a3.src="images/crea-on.gif"
a4=new Image(80,40)
a4.src="images/crea-off.gif"

a5=new Image(80,40)
a5.src="images/stag-on.gif"
a6=new Image(80,40)
a6.src="images/stag-off.gif"

a7=new Image(80,40)
a7.src="images/cours-on.gif"
a8=new Image(80,40)
a8.src="images/cours-off.gif"




function read($fichier)
{ 
$lecture = fopen($fichier, "r");
$string = fread($lecture, filesize($fichier)); 	// reads the contents of the file
fclose($lecture);
return $string;
}


function filter(imagename,objectsrc){
if (document.images)
document.images[imagename].src=eval(objectsrc+".src")
}



function rollover(imagename, newsrc){
document.images[imagename].src=newsrc.src
}



/*
lib.js
by Caio Chassot (http://v2studio.com/k/code/)
parts by other authors, see commented version for details

version 0.5.75 
File generated on Fri Sep 24 00:35:55 E. South America Standard Time 2004

for documentation, see commented version.
get commented version at http://v2studio.com/k/code/lib/lib_c.js

Use freely. Credit is appreciated but not required.
*/

/* CHANGELOG:
2003-09-24
    - CHANGELOG is back at the lib root dir
2003-09-22
    - added Array.reject, which rejects items IN PLACE
2004-09-01
    - modified dependencies.yaml data format. (/me stills amazed it only took changing two lines of code in the actual scripts to adjust to the new format)
    - modified many functions to adapt to new reduce
    - changed reduce parameter order 
2004-08-31
    - added Array.first
2004-08-30
    - added extra properties (keyboard related) to IE_Event
2004-08-27
    - getAll now transforms the nodeList into a real Array
    - added to Array: .any
    - Added to Array: .each, .map, .filter (also .select), .reduce (also, inject). They just implement as method the original functions (actually they just call them)
    - fixed bug in __strfn that required spaces after trailing comma for last statement
    - fixed bug in addClass where mozilla would ignore the added space when adding a class 
2004-08-24
    - getElementsByClass now uses getAll
    - getAll now circumvents IE 5.x and omniweb 5's implementation of getElementsByTagName, which would fail for '*'
    - added filterElementNodes, needed for new getAll
    - modified `isRegexp` not to use `instanceof` as it fails on IE/mac
    - modified `list` to use new `isRegexp` instead of a direct instanceof
2004-08-23
    - getElementsByClass now bypasses elements which do not allow a class attribute, avoiding problems in IE
    - added //BEGIN: marks everywhere 
    - added dependencies.yaml to indicate depencendcies between script parts named by //BEGIN:
    - added scripts to check dependencies and build slimmed down, "only what you need" versions of the lib     
2004-08-22
    - New file: remedial.js
    - Changed documentation format
    - Moved `Array.push` from array.js to remedial.js
    - added `Array.merge`
    THE FOLLOWING SET OF CHANGES IS A MAJOR DEPARTUTE FROM PREVIOUS VERSIONS AND MAY BREAK YOUR SCRIPTS
    - original `Array.find` is now `Array.indexOf`, to match `String.indexOf`
    - `Array.indexOf` returns -1 instead of `false`
    - `Array.indexOf` will test against functions and regexps passed as value, instead of comparing to them (except when strict is used)
    - `Array.find` now returns the first item matching value, according to the same new rules for `Array.indexOf`
    
    - `Array.has` is now `Array.contains`, but `Array.has` still exists as alias to `Array.contains`. `Array.inlude` is also a new alias to `Array.contains`
    - Added `Array.all`
    - Added `Array.equals`

    - Added `combine` to functional.js
    - Added `each` to functional.js, which is like map, but doesn't return an array
    - replaced `String.subArgs` and `String.subDict` by new, more compatible versions
    - Addded `String.subn`
    
    - moved `isUndefined` from utils.js to remedial.js
    - added `cmp` to utils.js

    THE FOLLOWING SET OF CHANGES IS A MAJOR DEPARTUTE FROM PREVIOUS VERSIONS AND WILL BREAK YOUR DOM SCRIPTS
    - Ditched from dom.js: getElem, getHtml, getHead, getBody, listen, mlisten, loadlisten 
    - Added to dom.js: condClass, isList, isElement, getElem, getElemList, getEventModel, addEvent, remEvent, addEventDict, addLoadEvent
    - backwards compatibility for events can easily be achieved by adding the following line to the end of your lib.js:
        listen = mlisten = function(ev,el,fn){ addEvent(el,ev,fn) }
    - added getElemetsByClassName alias to getElemetsByClass to dom.js
    - due to mods in array.js hasClass now accepts a regexp or function as className, consequequently, getElementsByClass too, in this case the regexp/function will try to match against each class in the class list separately
*/

/*  A.indexOf(value [, start [, strict]])

    searches Array for `value`.
    returns the index of the first item which matches `value`, or -1 if not found.
    searching starts at index 0, or at `start`, if specified.

    Here are the rules for an item to match `value`
    if strict is false or not specified (default):
        if `value` is a:
            `function`    -> `value(item)` must be true
            `RegExp`      -> `value.test(item)` must be true
            anything else -> `item == value` must be true
    if `strict` is specified and true:
        use strict comparison (===) for everything
*/
Array.prototype.indexOf = function(value, start, strict) {
    start = start || 0;
    for (var i=start; i<this.length; i++) {
        var item = this[i];
        if (strict            ? item === value   :
            isRegexp(value)   ? value.test(item) :
            isFunction(value) ? value(item)      :
            item == value)
            return i;
    }
    return -1;
}



/*  A.find(value [, start [, strict]])
    searches Array for `value`
    returns the first matched item, or null if not found
    Parameters work the same as indexOf
*/
Array.prototype.find = function(value, start, strict) {
    var i = this.indexOf(value, start, strict);
    if (i != -1) return this[i];
    return null
}



/*  A.contains(value [, strict])
    aliases: has, include

    returns true if `value` is found in Array, otherwise false;
    relies on indexOf, see its doc for details on `value` and `strict`
*/
Array.prototype.contains = function(value,strict) {
    return this.indexOf(value,0,strict) !== -1;
}



Array.prototype.has     = Array.prototype.contains;



Array.prototype.include = Array.prototype.contains;



/*  A.count(value, [, strict])
    counts occurences of `value` in Array
    relies on indexOf, see its doc for details on `value` and `strict`
*/
Array.prototype.count = function(value, strict) {
    var pos, start = 0, count = 0;
    while ((pos = this.indexOf(value, start, strict)) !== -1) {
        start = pos + 1;
        count++;
    }
    return count;
}



/*  A.remove(value [, all [, strict]])
    if `all` is false or not provied:
        removes first occurence of `value` from Array
    if `all` is provided and true:
        removes all occurences of `value` from Array
    returns the array
    relies on indexOf, see its doc for details on `value` and `strict`
*/
Array.prototype.remove = function(value,all,strict) {
    while (this.contains(value,strict)) {
        this.splice(this.indexOf(value,0,strict),1);
        if (!all) break
    }
    return this;
}



/*  A.merge(a [, a]*)
    Append the contents of provided arrays into the current
    takes: one or more arrays
    returns: current array (modified)
*/
Array.prototype.merge = function() {
    var a = [];
    for (var i=0; i<arguments.length; i++)
        for (var j=0; j<arguments[i].length; j++)
            a.push(arguments[i][j]);
    for (var i=0; i<a.length; i++) this.push(a[i]);
    return this
}



/*  A.min()
    returns the smallest item in array by comparing them with >
*/
Array.prototype.min = function() {
    if (!this.length) return;
    var n = this[0];
    for (var i=1; i<this.length; i++) if (n>this[i]) n=this[i];
    return n;
}



/*  A.min()
    returns the graetest item in array by comparing them with <
*/
Array.prototype.max = function() {
    if (!this.length) return;
    var n = this[0];
    for (var i=1; i<this.length; i++) if (n<this[i]) n=this[i];
    return n;
}



/*  A.first()
    returns first element of Array
*/
Array.prototype.first = function() { return this[0] }



/*  A.last()
    returns last element of Array
*/
Array.prototype.last = function() { return this[this.length-1] }



/*  A.sjoin()
    Shorthand for A.join(' ')
*/
Array.prototype.sjoin = function() { return this.join(' ') }



/*  A.njoin()
    Shorthand for A.join('\n')
*/
Array.prototype.njoin = function() { return this.join('\n') }



/*  A.cjoin()
    Shorthand for A.join(', ')
*/
Array.prototype.cjoin = function() { return this.join(', ') }



/*  A.equals(a [, strict])
    true if all elements of array are equal to all elements of `a` in the same
    order. if strict is specified and true, all elements must be equal and of
    the same type.
*/
Array.prototype.equals = function(a, strict){
    if (this==a) return true;
    if (a.length != this.length) return false;
    return this.map(function(item,idx){
        return strict? item === a[idx] : item == a[idx]
    }).all();
}



/*  A.all([fn])
    Returns true if fn returns true for all elements in array
    if fn is not specified, returns true if all elements in array evaluate to
    true
*/
Array.prototype.all = function(fn) {
    return filter(this, fn).length == this.length;
}



/*  A.any([fn])
    Returns true if fn returns true for any elements in array
    if fn is not specified, returns true if at least one element in array 
    evaluates to true
*/
Array.prototype.any = function(fn) {
    return filter(this, fn).length > 0;
}



/*  A.each(fn)
    method form of each function
*/
Array.prototype.each = function(fn) { return each(this, fn) }



/*  A.map([fn])
    method form of map function
*/
Array.prototype.map = function(fn) { return map(this, fn) }



/*  A.filter([fn])
    method form of filter function
*/
Array.prototype.filter = function(fn) { return filter(this, fn) }


Array.prototype.select = Array.prototype.filter



/*  A.reduce([initial,] fn)
    method form of filter function
*/
Array.prototype.reduce = function() {
    var args = map(arguments);
    fn = args.pop();
    d  = args.pop();
    return reduce(this, d, fn); 
}



Array.prototype.inject = Array.prototype.reduce



/*  A.reject(fn)
    deletes items in A *in place* for which fn(item) is true
    returns a
*/
Array.prototype.reject = function(fn) {
    if (typeof(fn)=='string') fn = __strfn('item,idx,list', fn);
    var self = this;
    var itemsToRemove = [];
    fn = fn || function(v) {return v};
    map(self, function(item,idx,list) { if (fn(item,idx,list)) itemsToRemove.push(idx) } );
    itemsToRemove.reverse().each(function(idx) { self.splice(idx,1) });
    return self;
}



/*  __strfn(args, fn)
    this is used internally by each, map, combine, filter and reduce to accept
    strings as functions.

    takes:
        `args` -> a string of comma separated names of the function arguments
        `fn`   -> the function body

    if `fn` does not contain a return statement, a return keyword will be added
    before the last statement. the last statement is determined by removing the
    trailing semicolon (';') (if it exists) and then searching for the last
    semicolon, hence, caveats may apply (i.e. if the last statement has a
    string or regex containing the ';' character things will go wrong)
*/
function __strfn(args, fn) {
    function quote(s) { return '"' + s.replace(/"/g,'\\"') + '"' }
    if (!/\breturn\b/.test(fn)) {
        fn = fn.replace(/;\s*$/, '');
        fn = fn.insert(fn.lastIndexOf(';')+1, ' return ');
    }
    return eval('new Function('
        + map(args.split(/\s*,\s*/), quote).join()
        + ','
        + quote(fn)
        + ')'
        );
}



/*  each(list, fn)
    traverses `list`, applying `fn` to each item of `list`
    takes:
        `list` -> anything that can be indexed and has a `length` property.
                  usually an array.
        `fn`   -> either a function, or  a string containing a function body,
                  in which case the name of the paremeters passed to it will be
                  'item', 'idx' and 'list'.
                  se doc for `__strfn` for peculiarities about passing strings
                  for `fn`

    `each` provides a safe way for traversing only an array's indexed items,
    ignoring its other properties. (as opposed to how for-in works)
*/
function each(list, fn) {
    if (typeof(fn)=='string') return each(list, __strfn('item,idx,list', fn));
    for (var i=0; i < list.length; i++) fn(list[i], i, list);
}




/*  map(list [, fn])
    traverses `list`, applying `fn` to each item of `list`, returning an array
    of values returned by `fn`

    parameters work the same as for `each`, same `__strfn` caveats apply

    if `fn` is not provided, the list item is returned itself. this is an easy
    way to transform fake arrays (e.g. the arguments object of a function or
    nodeList objects) into real javascript arrays.
    e.g.: args = map(arguments)

    If you don't care about map's return value, you should use `each`

    this is a simplified version of python's map. parameter order is different,
    only a single list (array) is accepted, and the parameters passed to [fn]
    are different:
    [fn] takes the current item, then, optionally, the current index and a
    reference to the list (so that [fn] can modify list)
    see `combine` if you want to pass multiple lists
*/
function map(list, fn) {
    if (typeof(fn)=='string') return map(list, __strfn('item,idx,list', fn));

    var result = [];
    fn = fn || function(v) {return v};
    for (var i=0; i < list.length; i++) result.push(fn(list[i], i, list));
    return result;
}




/*  combine(list [, list]* [, fn])

    takes:
        `list`s -> one or more lists (see `each` for definition of a list)
        `fn`    -> Similar s `each` or `map`, a function or a string containing
                   a function body.
                   if a string is used, the name of parameters passed to the
                   created function will be the lowercase alphabet letters, in
                   order: a,b,c...
                   same `__strfn` caveats apply

    combine will traverse all lists concurrently, passing each row if items as
    parameters to `fn`
    if `fn` is not provided, a function that returns a list containing each
    item in the row is used.
    if a list is smaller than the other, `null` is used in place of its missing
    items

    returns:
        an array of the values returned by calling `fn` for each row of items
*/
function combine() {
    var args   = map(arguments);
    var lists  = map(args.slice(0,-1),'map(item)');
    var fn     = args.last();
    var toplen = map(lists, "item.length").max();
    var vals   = [];

    if (!fn) fn = function(){return map(arguments)};
    if (typeof fn == 'string') {
        if (lists.length > 26) throw 'string functions can take at most 26 lists';
        var a = 'a'.charCodeAt(0);
        fn = __strfn(map(range(a, a+lists.length),'String.fromCharCode(item)').join(','), fn);
    }

    map(lists, function(li) {
        while (li.length < toplen) li.push(null);
        map(li, function(item,ix){
            if (ix < vals.length) vals[ix].push(item);
            else vals.push([item]);
        });
    });

    return map(vals, function(val) { return fn.apply(fn, val) });
}



/*  filter(list [, fn])
    returns an array of items in `list` for which `fn(item)` is true

    parameters work the same as for `each`, same `__strfn` caveats apply

    if `fn` is not specified the items are evaluated themselves, that is,
    filter will return an array of the items in `list` which evaluate to true

    this is a similar to python's filter, but parameter order is inverted
*/
function filter(list, fn) {
    if (typeof(fn)=='string') return filter(list, __strfn('item,idx,list', fn));

    var result = [];
    fn = fn || function(v) {return v};
    map(list, function(item,idx,list) { if (fn(item,idx,list)) result.push(item) } );
    return result;
}



/*  reduce(list [, initial], fn)
    similar to python's reduce. paremeter onder inverted...

    TODO: document this properly

    takes:
        `list`   -> see doc for `each` to learn more about it
        `inirial -> TODO: doc`
        `fn`     -> similar to `each` too, but in the case where it's a string,
                    the name of the paremeters passed to it will be 'a' and 'b'
                    same `__strfn` caveats apply

*/
function reduce(list, initial, fn) {
    if (undef(fn)) {
        fn      = initial;
        initial = window.undefined; // explicit `window` object so browsers that do not have an `undefined` keyword will evaluate to the (hopefully) undefined parameter `undefined` of `window` 
    }
    if (typeof(fn)=='string') return reduce(list, initial, __strfn('a,b', fn));
    if (isdef(initial)) list.splice(0,0,initial);
    if (list.length===0) return false;
    if (list.length===1) return list[0];
    var result = list[0];
    var i = 1;
    while(i<list.length) result = fn(result,list[i++]);
    return result;
}




function isAlien(a)     { return isObject(a) && typeof a.constructor != 'function' }


function isArray(a)     { return isObject(a) && a.constructor == Array }


function isBoolean(a)   { return typeof a == 'boolean' }


function isFunction(a)  { return typeof a == 'function' }


function isNull(a)      { return typeof a == 'object' && !a }


function isNumber(a)    { return typeof a == 'number' && isFinite(a) }


function isObject(a)    { return (a && typeof a == 'object') || isFunction(a) }


// we would prefer to use instanceof, but IE/mac is crippled and will choke at it
function isRegexp(a)    { return a && a.constructor == RegExp }


function isString(a)    { return typeof a == 'string' }


function isUndefined(a) { return typeof a == 'undefined' }


function isEmpty(o)     {
    var i, v;
    if (isObject(o)) {
        for (i in o) {
            v = o[i];
            if (isUndefined(v) && isFunction(v)) {
                return false;
            }
        }
    }
    return true;
}


// array method reimplementations: adding only when missing



if (!Function.prototype.apply) Function.prototype.apply = function (o, a) {
    var r, x = '____apply';
    if (!isObject(o)) {
        o = {};
    }
    o[x] = this;
    switch ((a && a.length) || 0) {
    case 0:
        r = o[x]();
        break;
    case 1:
        r = o[x](a[0]);
        break;
    case 2:
        r = o[x](a[0], a[1]);
        break;
    case 3:
        r = o[x](a[0], a[1], a[2]);
        break;
    case 4:
        r = o[x](a[0], a[1], a[2], a[3]);
        break;
    case 5:
        r = o[x](a[0], a[1], a[2], a[3], a[4]);
        break;
    case 6:
        r = o[x](a[0], a[1], a[2], a[3], a[4], a[5]);
        break;
    default:
        alert('Too many arguments to apply.');
    }
    delete o[x];
    return r;
}




if (!Array.prototype.pop) Array.prototype.pop = function () {
    return this.splice(this.length - 1, 1)[0];
}



if (!Array.prototype.push) Array.prototype.push = function() {
    for (var i=0; i<arguments.length; i++) this[this.length] = arguments[i];
    return this.length;
}



if (!Array.prototype.shift) Array.prototype.shift = function () {
    return this.splice(0, 1)[0];
}



if (!Array.prototype.splice) Array.prototype.splice = function (s, d) {
    var max = Math.max,
        min = Math.min,
        a = [], // The return value array
        e,  // element
        i = max(arguments.length - 2, 0),   // insert count
        k = 0,
        l = this.length,
        n,  // new length
        v,  // delta
        x;  // shift count

    s = s || 0;
    if (s < 0) {
        s += l;
    }
    s = max(min(s, l), 0);  // start point
    d = max(min(isNumber(d) ? d : l, l - s), 0);    // delete count
    v = i - d;
    n = l + v;
    while (k < d) {
        e = this[s + k];
        if (!isUndefined(e)) {
            a[k] = e;
        }
        k += 1;
    }
    x = l - s - d;
    if (v < 0) {
        k = s + i;
        while (x) {
            this[k] = this[k - v];
            k += 1;
            x -= 1;
        }
        this.length = n;
    } else if (v > 0) {
        k = 1;
        while (x) {
            this[n - k] = this[l - k];
            k += 1;
            x -= 1;
        }
    }
    for (k = 0; k < i; ++k) {
        this[s + k] = arguments[k + 2];
    }
    return a;
}



if (!Array.prototype.unshift) Array.prototype.unshift = function () {
    this.splice.apply(this,
        [0, 0].concat(Array.prototype.slice.apply(arguments)));
    return this.length;
}


// string method reimplementations: adding only when missing or wrong



/*
    if browser doesn't support a function as replacement string, replace replace
    notice this is for safari, it still won't work in ie5.0/win because it has
    no notion of a global regexp. in the future i might provide a workaround for it
    but it'll not be transparent (ie: explicitly passing a global flag to this function)
*/
if ( /^function\s*\(\s*\)\s*\{\s*\}$/.test('a'.replace(/a/,function(){})) ) {
    String.prototype.replace_ = String.prototype.replace;
    String.prototype.replace = function(rx, rf) { //regexp, replacement function
        if (isFunction(rf)) {
            var s = this,
                r,   // rx result
                rfv, // rf returned value
                replaceList = [];
            while ((r=rx.exec(s))!==null) {
                rfv = rf.apply(rf, map(r).concat([r.index, s]));
                replaceList.push({ start: r.index, length: r[0].length, value: rfv });
                if (!rx.global) break;
            }
            for (var i=replaceList.length-1; i>=0; i--) {
                var rli = replaceList[i];
                s = s.splice(rli.start, rli.length, rli.value);
            }
            return s;
        } else return this.replace_(rx, rf);
    }
}




/*  S.trimLeft()
    returns the string with leading whitespace removed
*/
String.prototype.trimLeft = function() { return this.replace(/^\s+/,'') }



/*  S.trimRight()
    returns the string with trailing whitespace removed
*/
String.prototype.trimRight = function() { return this.replace(/\s+$/,'') }



/*  S.trim()
    returns the string with leading and trailing whitespace removed
*/
String.prototype.trim = function() { return this.replace(/^\s+|\s+$/g,'') }



/*  S.insert(idx, value)
    returns the string with `value` inserted at position `idx`
*/
String.prototype.insert = function(idx,value) { return this.slice(0,idx) + value + this.slice(idx) }



/*  S.strip(idx1, idx2)
    returns the string with characters from `idx1` up to but not including
    `idx2` removed
*/
String.prototype.strip = function(idx1,idx2) {
    if (arguments.length==1) idx2 = this.length;
    return this.slice(0,idx1) + this.slice(idx2);
}



/*  S.splice(idx, count, value)
    returns the string with characters from `idx` up to but not including
    `idx + count` removed, and `value` inserted at `idx`

    the original string is not modified (as opposed to Array.splice)
*/
String.prototype.splice = function(idx,count,value) { return this.strip(idx, idx+count).insert(idx, value) }



/*  S.subArgs(value_0 [,...,value_n])
    returns string with occurrences of {n} replaced with [value_n]
    e.g.:
        >> 'a{0}c{1}'.subArgs('b','d')
        'abcd'
*/
String.prototype.subArgs = function() { return this.subDict(map(arguments)) }



/*  S.subDict(dict)
    takes: dict => an object containing key/value pairs
    returns: string with occurences of {key} replaced by dict['key']
    undefined keys are not changed
    (my original version relied on String.replace with a function as
     replacement, which doesn't work in IE 5.0 (nor Safari), so now
     we have this ugly version)
*/
String.prototype.subDict = function(dict) {
    var
        parts = this.split('{'),
        r = parts[0],
        part, ci, k, v;
    for (var i = 1; i < parts.length; i++) {
        part = parts[i];
        ci   = part.indexOf('}');
        k    = part.substring(0,ci);
        v    = dict[k];
        r   += isdef(v) ? v + part.substring(ci+1) : '{' + part;
    }
    return r;
}




/*  S.subn(dict) or S.subn(value [, value]*)
    Shorthand for subArgs and subDict, decides automatically which to use
*/
String.prototype.subn = function(d) {
    if (arguments.length > 1 || !isObject(arguments[0])) d = map(arguments);
    return this.subDict(d);
}

// If you don't use the original sub, you can overwrite it with the more useful
// subn by uncommenting this line:
// String.prototype.sub = String.prototype.subn;



/*  S.wrap(left [, right]);
    returns string wrapped in `left` and `right`; right = left if not provided;
*/
String.prototype.wrap = function(left,right) {
    if (undef(left)) throw 'S.wrap takes 1 argument (none given)';
    if (undef(right)) right = left;
    return left + this + right;
}



/*  S.quote()
    returns string quoted in double quotes
*/
String.prototype.quote = function() { return this.wrap('"') }



/*  S.squote()
    returns string quoted in single quotes
*/
String.prototype.squote = function() { return this.wrap("'") }



/*  S.pad(side, len, chr)
    takes:
        `side` -> A string. 'left' will add padding to the left of the string,
                  any other string will add pad to the right.
        `len`  -> the length of the padding to add
        `chr`  -> the char to be used for padding. if not provided, a space is
                  used
    returns:
        s padded with `chr` by `len` on the `side` side
*/
String.prototype.pad = function(side, len, chr) {
    if (undef(chr)) chr = ' ';
    var s = this;
    var left = side.toLowerCase()=='left';
    while (s.length<len) s = left? chr + s : s + chr;
    return s;
}



/*  S.padLeft(len, chr)
    Returns string paddded by `len` in the left using `chr` (space if not
    provided)
*/
String.prototype.padLeft = function(len, chr) { return this.pad('left',len,chr) }



/*  S.padRight(len, chr)
    Returns string paddded by `len` in the right using `chr` (space if not
    provided)
*/
String.prototype.padRight = function(len, chr) { return this.pad('right',len,chr) }



/*  S.zerofill(len)
    return string left filled with 0s to match length `len`
    a leftmost + or - sign is kept the leftmost
*/
String.prototype.zerofill = function(len) {
    var s = this;
    var ix = /^[+-]/.test(s) ? 1 : 0;
    while (s.length<len) s = s.insert(ix, '0');
    return s;
}



/*  S.isEmpty([donttrim])
    true if string contains no characters.
    unless `donttrim` is specified and true, the trimmed string is tested
*/
String.prototype.isEmpty = function(donttrim) { return !(donttrim? this : this.trim()).length }



/* alias for isUndefined  */
function undef(v) { return  isUndefined(v) }


/* alias for !isUndefined */
function isdef(v) { return !isUndefined(v) }



/*  cmp(a,b)
    generic comparison, useful for creating sorting functions
*/
function cmp(a,b) { return a<b?-1:a==b?0:1 }



/*  list(s [, sep])
    takes a string containing `sep`-separated values
    returns an array of `s` split at `sep`
    sep is optional. the default separator is a comma surrounded by optional
    whitespace.
    if `sep` is true, the separator becomes a simple comma
    if `sep` is any string or regex, then sep is the separator
*/
function list(s, sep) {
    if (!isString(sep) && !isRegexp(sep))
        sep = sep? ',' : /\s*,\s*/;
    return s.split(sep);
}


// PYTHONIC



/*  range(start, stop, step)
    identical to python's range.
    range(stop)
    range(start,stop)
    range(start,stop,step)

    Return a list containing an arithmetic progression of integers.
    range(i, j) returns [i, i+1, i+2, ..., j-1]; start (!) defaults to 0.
    When step is given, it specifies the increment (or decrement).
    For example, range(4) returns [0, 1, 2, 3].  The end point is omitted!
    [from python's range's docstring]
*/
function range(start,stop,step) {
    if (isUndefined(stop)) return range(0,start,step);
    if (isUndefined(step)) step = 1;
    var ss = (step/Math.abs(step)); // step sign
    var r = [];
    for (i=start; i*ss<stop*ss; i=i+step) r.push(i);
    return r;
}



/*  maprange(stop, fn)
    maprange(start, stop, fn)

    return map(range(start,stop), fn)

    this is pure laziness.
    */
function maprange(start, stop, fn) {
    if (arguments.length==2) return maprange(0, start, stop);
    if (arguments.length!=3) throw "maprange takes 2 or 3 arguments";
    return map(range(start,stop), fn);
}



/*  isList(o)
    true if o is an Array or a NodeList, (NodeList in Opera returns a type of function)
*/
function isList(o) { return o && isObject(o) && (isArray(o) || o.item) }



/*  isElement(o [, strict])
    true if o is an Element Node or document or window. The last two because it's used for onload events
    if you specify strict as true, return false for document or window
*/
function isElement(o, strict) {
    return o && isObject(o) && ((!strict && (o==window || o==document)) || o.nodeType == 1)
}



/*  getElem(el)
    takes:   `el` -> element or element id string
    returns: element
*/
function getElem(el) {
    var ge = (document.getElementById && function(id){return document.getElementById(id)} ) ||
             (document.all && function(id){return document.all[id]} ) ||
             function(){return null};
    return isElement(el)? el : isString(el) ? ge(el) : null;
}



/*  getElemList(el)
    takes:
        `el` -> an element id string / an element /
                a whitespace-separated string of ids / a list of elements /
                a list of ids / a mixed list
    returns:
        a list of elements, null if supplied argument is invalid
*/
function getElemList(el) {
    if      (isElement(el)) return [el];
    else if (isString(el) ) return getElemList(el.split(/\s+/g)); // it doesn't matter whether it's a list or not, we split it anyway
    else if (isList(el)   ) {
        var r = map(el, getElem);
        return filter(r, isElement).length==r.length? r : null;
    }
    else return null;
}



/*  filterElementNodes(nodeList [, tagName])
    returns a list with only nodes from the list `nodeList` which are elements,
    and optionally, only those which are the element `tagName`
*/
function filterElementNodes(nodeList, tagName) {
    return filter(nodeList, function(n){
        // the weird check against '!' is because IE will list the doctype in 
        // the nodeList with a time of ELEMENT_NODE, and it'll have a nodeName of '!'
        // 1 is the value of node.ELEMENT_NODE, but the constant is not supported in IE
        // so we use the literal value
        return n.nodeType==1 && n.nodeName!='!' && 
               (undef(tagName) || tagName == '*' || n.nodeName.toUpperCase()==tagName.toUpperCase())
    })
}




/*  getAll([tagName [, parent]])
    shorthand for `parent.getElementsByTagName(tagName)`, but turns the result
    into a real array.
    `tagName` is optional and defaults to '*'
    `parent` is optional and defaults to document
    This is important to use when tagName == '*' and support for IE 5.x and/or
    omniweb 5 is required, as getElementsByTagName('*') fails on them.
*/
function getAll(tagName, parent) {
    parent = isdef(parent)? getElem(parent) : document;
    if (undef(tagName)) tagName = '*';
    var r = parent.getElementsByTagName(tagName);
    /*  IE 5.x/win and some other browers (like omniweb 5, and probably older 
        safari) do not support getElementsByTagName with a value of '*' as 
        parameter, so we have to work around it.
        If we get an empty match for a '*' tag, we try again with workaround
        below
    */
    return r.length || tagName != '*'?  map(r) :
        reduce(filterElementNodes(parent.childNodes), [], function(l,c){
            return l.merge([c], getAll(tagName, c))
        })
}



/*  getElementsByClass(className [, tagName [, parentNode]])
    Returns elements having class `className`, optionally being a tag `tagName`
    (otherwise any tag), optionally being a descendant of `parentNode`
    (otherwise the whole document is searched)
    if `className` is a function or regexp, each class is tested against it
*/
function getElementsByClass(className, tagName, parentNode) {
    // IE will fail if it tries to access classNode on an element on which classNode is not allowed
    // Also, IE 6 seems to list #comment elements for getElementsByTagName('*'), so we add it to the
    // ignore list too
    var noClassTags = list('#comment,BASE,BASEFONT,HEAD,HTML,META,PARAM,SCRIPT,STYLE,TITLE');
    return filter(getAll(tagName,parentNode),
        function(elem) {
            return !noClassTags.include(elem.nodeName) && hasClass(elem, className) 
        });
}



getElementsByClassName = getElementsByClass;


// CLASS MANIPULATION



/*  hasClass(elem, className)
    return how many times `className` occurs in the class list of getElem(elem)

    The tested element can have multiple space-separated classes. className must
    be a single class (i.e. can't be a list), but can be a regexp or function,
    in which case each class in the element class list is tested against it
    */
function hasClass(elem, className) {
    return getElem(elem).className.split(' ').count(className);
}



/*  remClass(elem, className [, all])
    removes first occurence of `className` in getElem(elem)'s class list
    if `all` is provided and true, removes all occurences.
*/
function remClass(elem, className, all) {
    elem = getElem(elem);
    elem.className = elem.className.split(' ').remove(className,all).join(' ');
}



/*  addClass(elem, className [, allowDuplicates])
    adds [className] to getElem(elem)'s class list.

    `allowDuplicates` determintes whether or not a class should be inserted if
    it already exists in the element's class list.
    `allowDuplicates` is optional and defaults to false.
    */
function addClass(elem, className, allowDuplicates) {
    elem = getElem(elem);
    if (!allowDuplicates && elem.className.split(' ').contains(className)) return;
    elem.className += (elem.className.length?' ':'') + className;
}



/*  swapClass(el,className)
    takes:
        `el`        -> element (in getElem fashion),
        `className` -> class name (string only)
    adds `className` to `el` class list if it's not present, removes it if it is.
    returns true is class was added, false if removed.
*/
function swapClass(e,c) {
    if (hasClass(e,c)) remClass(e,c); else addClass(e,c);
    return !!hasClass(e,c);
}



/*  condClass(el, className, cond)
    adds class `className` to `el`'s class list if cond is true, removes it if
    cond is false
*/
function condClass(e,c,cond) { (cond?addClass:remClass)(e,c) }


// SHORTHANDS


/* NODOC */


function insertBefore(newChild, refChild) {
    return refChild.parentNode.insertBefore(newChild, refChild);
}

/* NODOC */


function insertAfter(newChild, refChild) {
    if (refChild.nextSibling) insertBefore(newChild, refChild.nextSibling);
    else refChild.parentNode.appendChild(newChild);
    return newChild;
}


// DOM EVENTS

/*
 controls whether events can be added or not to old browsers which are unable to
 pass a reliable event object to the event handler
 notice that IE/win's event model is wrapped in a fake event object, and thus
 events will be added in IE even if this is false. This is for really old browsers
 that don't have *any* rubust event model (like, erm, IE/mac)
*/



var ALLOW_LEGACY_EVENTS = true;



/*  getEventModel()
    tells us which event model is present
*/
function getEventModel() {
    var d = document;
    return d.addEventListener? 'DOM' :
           d.attachEvent     ? 'IE'  :
                               'legacy';
}



/*  wrapper for IE proprietary event model to fake a w3c dom event
    should not be called manually
*/
function IE_Event(currentTarget) {
    this.currentTarget   = currentTarget;
    this.preventDefault  = function() { window.event.returnValue  = false }
    this.stopPropagation = function() { window.event.cancelBubble = true }
    this.target  = window.event.srcElement;
    var self = this;
    // direct equivalence properties
    list('altKey,ctrlKey,shiftKey,clientX,clientY').map(function(p){ self[p] = event[p] });
    return this;
}



/*  simplified w3c dom event for really old browsers
    should not be called manually
*/
function Legacy_Event(currentTarget) {
    this.currentTarget   = currentTarget;
    return this;
}



/*  addEvent(el, ev, fn [, capture])
    adds the `fn` handler to handle event `ev` on element or elements `el`
    takes:
        el -> an element or list of elements (getElemList style, see its doc for detailed options)
        ev -> event name, 'on' prefix should be ommited
        fn -> the function that'll handle the event
        capture -> whether to use capture or not. optional, true if not specified
*/
function addEvent(els, ev, fn, capture) {
    if (!ALLOW_LEGACY_EVENTS && getEventModel()=='legacy') return false;
    if (undef(capture)) capture = true;
    function DOM_addEvent   (el, ev, fn, capture) { el.addEventListener(ev, fn, capture) }
    function legacy_addEvent(el, ev, fn) {
        var evn = 'on'+ev;
        if (!el[evn] || undef(el[evn].handlers)) {
            el[evn] = function() {
                map(el[evn].handlers, function(h){  h( new (el.attachEvent?IE_Event:Legacy_Event)(el) ) });
            }
            el[evn].handlers = [];
        }
        el[evn].handlers.push(fn);
    }
    var addEventFn = getEventModel()=='DOM'? DOM_addEvent : legacy_addEvent;
    map(getElemList(els), function(el) { addEventFn(el, ev, fn, capture) });
}



/*  remEvent(els, ev, fn [, capture])
    removes the `fn` handler to handle event `ev` from element or elements `el`
    see addEvent for explanation on parameters
*/
function remEvent(els, ev, fn, capture) {
    if (!ALLOW_LEGACY_EVENTS && getEventModel()=='legacy') return false;
    if (undef(capture)) capture = true;
    map(getElemList(els), function(el) {
        if(getEventModel()=='DOM') el.removeEventListener(ev, fn, capture);
        else el['on'+ev].handlers.remove(fn);
    });
}



/*  addEventDict(els, evDict [, capture])
    adds event handlers in `evDict` to handle events in `evDict` on element(s) `els`
    takes
        els, capture -> see addEvent doc
        evDict -> a data stucture mapping event names to functions:
                  e.g.:
                  { click     : function(e) { ...},
                    mouseover : function_reference,
                    mouseout  : function(e) { ...},
                  }
*/
function addEventDict(els, evDict, capture) {
    for (ev in evDict) addEvent(els, ev, evDict[ev], capture);
}



/*  addLoadEvent(fn)
    adds a function to be executed onload
*/
function addLoadEvent(fn) {
    // opera onload is in document, not window
    var w = getEventModel()=="DOM" && !window.addEventListener ? document : window;
    return addEvent(w, 'load', fn, true)
}



