/**  Reflow : Digitas Javascript Object, version 1
 *--------------------------------------------------------------------------*/
/**
 *  Create the Reflow namespace, on which framework's elements will be based 
 *  @type {function}
 *  @author Digitas Front End
 *  @version 1
 *  @constructor
 */
var Reflow = (function() {
    function Reflow(params) {
        //to avoid error if the new word has been forgotten when creating instance
        if (! (this instanceof arguments.callee)) {
            return new Reflow(params);
        }
        this.browser = {          
          iTruc :(/iPhone|iPad|iPod|android/gi.test(navigator.userAgent))
        };
        this.data = (function() {
            var loaderImg = "/media/img/loader.gif";
            var version = {
                major: 1,
                minor: 0,
                patch: 1,
                flag: ""
            };
            var id = "Reflow Javascript Object";
            var flag = (version.flag == "") ? "": ":";
            // manage rendering and calculation time to see if something's going too long 
            var t = new Date().getTime();
            var calcTime = "Calculation time: " + ((new Date().getTime() - t) / 1000.0) + "s";

            return {
                id: id,
                version: "Reflow node v" + version.major + "." + version.minor + "." + version.patch + flag + version.flag,
                loaderImg: loaderImg,
                calcTime: calcTime
            };
        })();
        /**
     * array for all local variables associated to the instance
     */
        this.elmts = [];
        // ajax
        this.ajax = (function() {
            var Caching = false;
            function Create() {
                var request = false;
                try {
                    request = new ActiveXObject('Msxml2.XMLHTTP');
                } catch(err2) {
                    try {
                        request = new ActiveXObject('Microsoft.XMLHTTP');
                    } catch(err3) {
                        try {
                            request = new XMLHttpRequest();
                        } catch(err1) {
                            request = false;
                        }
                    }
                }
                return request;
            }

            /**
           Read
          Load a file, text or XML
        */

            function Read(url, callAfterRequest, element) {
                var xhr = Create();
                var ext = url.substr(url.length - 3);
                var isXML = (ext == "xml");

                xhr.onreadystatechange = function() {
                    if (xhr.readyState == 4) {
                        if (xhr.status == 200) {
                            var content;
                            if (isXML) {
                                content = xhr.responseXML;
                            } else {
                                content = xhr.responseText;
                            }
                            callAfterRequest(content, element);
                        } else if (xhr.status == 500 || xhr.status == 404) {
                            callAfterRequest("there was a problem retrieving the content, please try again later", element);
                        }
                    }
                };

                if (Caching == false) {
                    url = url + "&nocache=" + Math.random();
                }
                xhr.open("GET", url, true);
                xhr.send(null);
            }

            /**
          Read an XML file with any extension
        */

            function LoadXML(url, callAfterInsert, element) {
                var xhr = Create();
                xhr.onreadystatechange = function() {
                    if (xhr.readyState == 4) {
                        callAfterInsert(xhr.responseXML, element);
                    }
                };
                xhr.open("GET", url, true);
                xhr.send(null);
            }

            /**
          Write
          url:  the script
          data: the string to pass to the script
            it is a list of pairs x=y separated by &
        */

            function Write(url, data, callAfterInsert) {
                var xhr = Create();

                xhr.onreadystatechange = function() {
                    if (xhr.readyState == 4) {
                        if (callAfterInsert != null) {
                            callAfterInsert(xhr.responseText);
                        }
                    }
                };
                xhr.open("POST", url, true);
                xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
                xhr.send(data);
            }

            function GetBody(content) {
                var x = content.indexOf("<body");
                if (x == -1) {
                    return "";
                }

                x = content.indexOf(">", x);
                if (x == -1) {
                    return "";
                }

                var y = content.lastIndexOf("</body>");
                if (y == -1) {
                    return "";
                }

                return content.slice(x + 1, y);
            }

            function PutHTML(content, target) {
                target.innerHTML = GetBody(content);
            }

            /**
          Loads a HTML page
          Put the content of the body tag into the current page.
          Arguments:
            url of the other HTML page to load
            id of the tag that has to hold the content
        */

            function LoadHTML(url, callAfterInsert, whereToInsert, param) {
                var xhr = Create();
                xhr.onreadystatechange = function() {
                    if (xhr.readyState == 4) {
                        if (xhr.status == 200) {
                            whereToInsert = document.getElementById(whereToInsert);
                            whereToInsert.innerHTML = GetBody(xhr.responseText);
                            callAfterInsert(whereToInsert, param);
                        }
                    }
                };

                if (Caching == false) {
                    url = url + "?nocache=" + Math.random();
                }
                xhr.open("GET", url, true);
                xhr.send(null);

            }

            /**
          Send a HEAD request,
          call a function with the value 
        */

            function Head(url, key, callAfterInsert, element) {
                var xhr = Create();

                xhr.onreadystatechange = function() {
                    if (xhr.readyState == 4) {
                        var value;
                        if (xhr.status == 200) {
                            value = xhr.getResponseHeader(key);
                        } else {
                            if (xhr.status == 404) {
                                value = url + " doesn't exist!";
                            } else {
                                value = "Error, status is " + xhr.status;
                            }
                        }
                        callAfterInsert(value, element);
                    }
                };
                xhr.open("HEAD", url, true);
                xhr.send(null);
            }
            return {
                getExternalFile: Read,
                loadHTMLBody: LoadHTML,
                setExternal: Write,
                loadXML: LoadXML
            };
        })();

        // event manager
        this.events = (function() {
            var guid = 1;
            // manage events
            // written by Dean Edwards, 2005
            // http://dean.edwards.name/
            var addEvent = (function() {
                function addEvent(element, type, handler) {
                    // assign each event handler a unique ID
                    if (!handler.$$guid) {
                        handler.$$guid = guid++;
                    }
                    // create a hash table of event types for the element
                    if (!element.events) {
                        element.events = {};
                    }
                    // create a hash table of event handlers for each element/event pair
                    var handlers = element.events[type];
                    if (!handlers) {
                        handlers = element.events[type] = {};
                        // store the existing event handler (if there is one)
                        if (element["on" + type]) {
                            handlers[0] = element["on" + type];
                        }
                    }
                    // store the event handler in the hash table
                    handlers[handler.$$guid] = handler;
                    // assign a global event handler to do all the work
                    element["on" + type] = handleEvent;
                }
                function handleEvent(event) {
                    // grab the event object (IE uses a global event object)
                    event = event || window.event;
                    // get a reference to the hash table of event handlers
                    var handlers = this.events[event.type];
                    // execute each event handler
                    for (var i in handlers) {
                        if (typeof handlers[i] == "function") {
                            this.$$handleEvent = handlers[i];

                            this.$$handleEvent(event);
                        }
                    }
                }
                return addEvent;
            })();
            var removeEvent = function removeEvent(element, type, handler) {
                // delete the event handler from the hash table
                if (element.events && element.events[type]) {
                    delete element.events[type][handler.$$guid];
                }
            };
            /*
       * (c)2006 Jesse Skinner/Dean Edwards/Matthias Miller/John Resig
       * Special thanks to Dan Webb's domready.js Prototype extension
       * and Simon Willison's addLoadEvent
       *
       * For more info, see:
       * http://www.thefutureoftheweb.com/blog/adddomloadevent
       * http://dean.edwards.name/weblog/2006/06/again/
       * http://www.vivabit.com/bollocks/2006/06/21/a-dom-ready-extension-for-prototype
       * http://simon.incutio.com/archive/2004/05/26/addLoadEvent
       * 
       * adapted to reFlow by LL
       * 
       */
            var addDOMLoadEvent = (function() {
                // create event function stack
                var load_events = [],
                load_timer,
                script,
                done,
                exec,
                old_onload,
                init = function() {
                    done = true;

                    // kill the timer
                    clearInterval(load_timer);

                    // execute each function in the stack in the order they were added
                    while (exec = load_events.shift()) {
                        exec();
                    }
                    if (script) {
                        script.onreadystatechange = '';
                    }
                };

                return function addDOMLoadEvent(func) {
                    // if the init function was already ran, just run this function now and stop
                    if (done) {
                        return func();
                    }

                    if (!load_events[0]) {
                        // for Mozilla/Opera9
                        if (document.addEventListener) {
                            document.addEventListener("DOMContentLoaded", init, false);
                        }

                        // for Internet Explorer
                        if (document.all) {
                            var s = document.createElement("script");
                            s.type = "text/javascript";
                            s.id = "__ie_onload";
                            s.src = "//0";
                            s.defer;
                            s.onreadystatechange = function() {
                                if (this.readyState == "complete") {
                                    init();
                                    // call the onload handler
                                }
                            };
                            document.getElementsByTagName("head")[0].appendChild(s);
                        }

                        // for Safari
                        if (/WebKit/i.test(navigator.userAgent)) {
                            // sniff
                            load_timer = setInterval(function() {
                                if (/loaded|complete/.test(document.readyState)) {
                                    init();
                                    // call the onload handler
                                }
                            },
                            10);
                        }

                        // for other browsers set the window.onload, but also execute the old window.onload
                        old_onload = window.onload;
                        window.onload = function() {
                            init();
                            if (old_onload) {
                                old_onload();
                            }
                        };
                    }
                    load_events.push(func);
                    return;
                };
            })();
            return {
                guid: guid,
                addEvent: addEvent,
                addEditable: function addEditable(id, action, callback) {
                    if (typeof id == 'string') {
                        addEvent(document.getElementById(id), action,
                        function() {
                            document.getElementById(id).contentEditable = true;
                            if (typeof callback == "function") {
                                callback(this);
                            }
                        });
                    } else if (typeof id == 'object') {
                        for (var i = 0; i < id.length; i++) {
                            addEvent(document.getElementById(id[i]), action,
                            function() {
                                this.contentEditable = true;
                                if (typeof callback == "function") {
                                    callback(this);
                                }
                            });
                        }
                    }

                },
                removeEditable: function removeEditable(id, action, callback) {
                    if (typeof id == "string") {
                        addEvent(document.getElementById(id), action,
                        function() {
                            document.getElementById(id).contentEditable = false;
                            if (typeof callback == "function") {
                                callback(this);
                            }
                        });
                    } else if (typeof id == 'object') {
                        for (var i = 0; i < id.length; i++) {
                            addEvent(document.getElementById(id[i]), action,
                            function() {
                                this.contentEditable = false;
                                if (typeof callback == "function") {
                                    callback(this);
                                }
                            });
                        }
                    }

                },
                removeEvent: removeEvent,
                addDOMLoadEvent: addDOMLoadEvent
            };

        })();
        this.DOM = {
            up: function up(ele, childObj) {
                childObj = (childObj) ? childObj: this;

                var testObj = childObj.parentNode;
                var isClass,
                isId,
                isTag;
                if (ele.indexOf(".") != -1) {
                    ele = ele.replace(".", "");
                    isClass = true;
                } else if (ele.indexOf("#") != -1) {
                    ele = ele.replace("#", "");
                    isId = true;
                } else {
                    isTag = true;
                }
                var eleTesting = function(obj) {
                    if (isClass) {
                        return obj.className;
                    } else if (isId) {
                        return obj.id;
                    } else {
                        return obj.tagName.toLowerCase();
                    }
                };

                var count = 1;
                while (eleTesting(testObj) != ele) {
                    testObj = testObj.parentNode;
                    count++;
                }
                // now you have the object you are looking for - do something with it
                window.Reflow.core.extend(testObj, window.Reflow.DOM);
                return testObj;
            },
            down: function down(ele, obj) {
                obj = (obj) ? obj: this;
                var isClass,
                isId,
                isTag;
                if (ele.indexOf(".") != -1) {
                    ele = ele.replace(".", "");
                    isClass = true;
                } else if (ele.indexOf("#") != -1) {
                    ele = ele.replace("#", "");
                    isId = true;
                } else {
                    isTag = true;
                }
                var eleTesting = function(obj) {
                    if (isClass) {
                        return obj.className;
                    } else if (isId) {
                        return obj.id;
                    } else if (typeof obj.tagName != "undefined") {
                        return obj.tagName.toLowerCase();
                    } else {
                        return null;
                    }
                };
                for (var i = 0; i < obj.childNodes.length; i++) {
                    if (eleTesting(obj.childNodes[i]) == ele) {
                        return obj.childNodes[i];
                    }
                }
                return null;

            },
            $: (typeof $ != "undefined") ? $: this.get,
            /**
       * check if an id exists or not
       * @param {String} id of the searched element
       * @return {boolean} false or true
       */
            test: function test(ele) {
                element = document.getElementById(ele);
                if (element == null) {
                    return false;
                } else {
                    return true;
                }
                return false;
            },
            /**
       * Get a dom node
       * @param {String} id The id of the element to get or the element itself
       * @param {String} context The root to start searching the element
       * @return {Element} The element found
       */
            get: function get(ele, context) {
                element = (typeof ele == "string") ? (typeof context != "undefined") ? context.getElementById(ele) : document.getElementById(ele) : ele;
                if (element == null) {
                    element = context || document;
                }
                window.Reflow.core.extend(element, window.Reflow.DOM);
                return element;
            },
            each: function each(block, context, object) {
                // globally resolve forEach enumeration
                object = (object) ? object: this;
                if (object) {
                    var resolve = Object; // default
                    if (object instanceof Function) {
                        // functions have a "length" property
                        resolve = Function;
                    } else if (object.forEach instanceof Function) {
                        // the object implements a custom forEach method so use that
                        object.forEach(block, context);
                        return;
                    } else if (typeof object == "string") {
                        // the object is a string
                        resolve = String;
                    } else if (typeof object.length == "number") {
                        // the object is array-like
                        resolve = Array;
                    }
                    resolve.forEach(object, block, context);
                }
            },
            // getElementsByClassName
            getElementsByClassName: function getElementsByClassName(className, tag, elm) {
                if (document.getElementsByClassName) {
                    elm = elm || document;
                    var elements = elm.getElementsByClassName(className),
                    nodeName = (tag) ? new RegExp("\\b" + tag + "\\b", "i") : null,
                    returnElements = [],
                    current;
                    for (var i = 0, il = elements.length; i < il; i += 1) {
                        current = elements[i];
                        if (!nodeName || nodeName.test(current.nodeName)) {
                            window.Reflow.core.extend(current, window.Reflow.DOM);
                            returnElements.push(current);
                        }
                    }
                    window.Reflow.core.extend(returnElements, window.Reflow.DOM);
                    return returnElements;
                } else if (document.evaluate) {
                    tag = tag || "*";
                    elm = elm || document;
                    var classes = className.split(" "),
                    classesToCheck = "",
                    xhtmlNamespace = "http://www.w3.org/1999/xhtml",
                    namespaceResolver = (document.documentElement.namespaceURI === xhtmlNamespace) ? xhtmlNamespace: null,
                    returnElements = [],
                    elements,
                    node;
                    for (var j = 0, jl = classes.length; j < jl; j += 1) {
                        classesToCheck += "[contains(concat(' ', @class, ' '), ' " + classes[j] + " ')]";
                    }
                    try {
                        elements = document.evaluate(".//" + tag + classesToCheck, elm, namespaceResolver, 0, null);
                    } catch(e) {
                        elements = document.evaluate(".//" + tag + classesToCheck, elm, null, 0, null);
                    }
                    while ((node = elements.iterateNext())) {
                        window.Reflow.core.extend(node, window.Reflow.DOM);
                        returnElements.push(node);
                    }
                    window.Reflow.core.extend(returnElements, window.Reflow.DOM);
                    return returnElements;
                } else {
                    tag = tag || "*";
                    elm = elm || document;
                    var classes = className.split(" "),
                    classesToCheck = [],
                    elements = (tag === "*" && elm.all) ? elm.all: elm.getElementsByTagName(tag),
                    current,
                    returnElements = [],
                    match;
                    for (var k = 0, kl = classes.length; k < kl; k += 1) {
                        classesToCheck.push(new RegExp("(^|\\s)" + classes[k] + "(\\s|$)"));
                    }
                    for (var l = 0, ll = elements.length; l < ll; l += 1) {
                        current = elements[l];
                        match = false;
                        for (var m = 0, ml = classesToCheck.length; m < ml; m += 1) {
                            match = classesToCheck[m].test(current.className);
                            if (!match) {
                                break;
                            }
                        }
                        if (match) {
                            window.Reflow.core.extend(current, window.Reflow.DOM);
                            returnElements.push(current);
                        }
                    }
                    window.Reflow.core.extend(returnElements, window.Reflow.DOM);
                    return returnElements;
                }
                return getElementsByClassName(className, tag, elm);
            },
            //manage classes for elements
            hasClass: function(cls, ele) {
                if (!ele) {
                    ele = this;
                }
                return ele.className.match(new RegExp('(\\s|^)' + cls + '(\\s|$)'));
            },
            addClass: function addClass(cls, ele) {
                if (!ele) {
                    ele = this;
                }
                if (!this.hasClass(cls, ele)) {
                    ele.className += " " + cls;
                }

            },
            removeClass: function removeClass(cls, ele) {
                if (!ele) {
                    ele = this;
                }
                if (this.hasClass(cls, ele)) {
                    var reg = new RegExp('(\\s|^)' + cls + '(\\s|$)');
                    ele.className = ele.className.replace(reg, ' ');
                }
            }
        };
        this.widgets = {

            };
        this.core = (function() {
            var extend = function(destination, source) {
                for (var property in source) {
                    if (source[property] && source[property].constructor && source[property].constructor === Object) {
                        destination[property] = destination[property] || {};
                        arguments.callee(destination[property], source[property]);
                    } else {
                        destination[property] = source[property];
                    }
                }
                return destination;
            };
            var pluginUrl = function(obj, name) {
                return obj.settings.jsPluginPath + obj.settings.pluginList[name];
            };
            /**
      * log
      *
      * @classDescription This function dispatch error messages through console or alert
      * @type {function}
      * @param {string} message : the message to be logged 
      */
            // log feature that will display the console.log or an alert if non available
            var log = function log(message, error) {
                if (Reflow() && Reflow().settings.debugMode) {
                    try {
                        console.log.apply(console, arguments);
                    } catch(e) {
                        try {
                            opera.postError.apply(opera, arguments);
                        } catch(e) {
                            nitobi.Debug.log(Array.prototype.join.call(arguments, " "));

                        }
                    }
                }
            };
            /**
      * debug
      *
      * @classDescription This function is a debugging tool to trap errors
      * @type {function}
      * @param {string} object : error in itself
      * @param {string} message : the file in which error is located 
      * @param {string} number :line number 
      */
            var debug = function debug(object, message, number) {
                //use globale variable because we lost track for the current instance with the onerror
                Reflow().core.log("Reflow.debug(): Error \n -> " + object + "\n -> line " + number + " \n -> in file " + message, "error");
                return true;
            };
            /**
       * initLogMode
       * 
       * @classDescription  This function will check if the current browser has an internal debugger and will load firebug lite if it doesn't
       * @type {function}
       * @param {Object} obj will receive the current instance of Reflow class
       */
            var initLogMode = function initLogMode(obj) {
                if (!window.console) {
                    //if no window.console, let inactivate potential errors if a window.console is left in the code
                    (function() {
                        var names = ["log", "debug", "info", "warn", "error", "assert", "dir", "dirxml", "group", "groupEnd", "time", "timeEnd", "count", "trace", "profile", "profileEnd"];

                        window.console = {};
                        for (var i = 0; i < names.length; ++i) {
                            window.console[names[i]] = function() {};
                        }
                    })();
                }
                if (obj.settings.debugMode && document.all) {

                    // then load an external debugger if needed
                    obj.core.jsLoader(obj.settings.ieFirebugLiteUrl,
                    function() {
                        //firebug.env.height = 150;
                    },
                    'nitobibug');
                }
            };
            /**
      * external feature loader
      * 
      * @classDescription This function loads a couple css file + js file
      * @type {function}
      * @param {string} json object, with .js and .css elements, and a callback function
      */
            var featureLoader = function featureLoader(obj) {
                obj.parent.core.jsLoader(obj.js,
                function() {
                    obj.parent.core.cssLoader(obj.css,
                    function() {
                        obj.callback();
                    });
                },
                obj.name);
            };
            var cssLoader = function cssLoader(file, callback, id) {
                var s = document.createElement("link");
                s.type = "text/css";
                s.rel = "stylesheet";
                if (id) {
                    s.id = "css-" + id;
                }
                if (s.readyState) {
                    // IE
                    s.onreadystatechange = function() {
                        if (s.readyState == "loaded" || s.readyState == "complete") {
                            s.onreadystatechange = null;
                            if (callback) {
                                callback();
                            }
                        }
                    };
                } else {
                    s.onload = function() {
                        if (callback) {
                            callback();
                        }
                    };
                }
                s.href = file;
                for (i = 0; i < document.getElementsByTagName("head")[0].getElementsByTagName("link").length; i++) {
                    if (document.getElementsByTagName("head")[0].getElementsByTagName("link")[i].href == s.href) {
                        document.getElementsByTagName("head")[0].removeChild(document.getElementsByTagName("head")[0].getElementsByTagName("link")[i]);
                    }
                }
                jQuery("head link:eq(0)").after(s);
            };
            /**
      * external js loader
      *
      * @classDescription This function acts as an external js loader.
      * @type {function}
      * @param {string} file : url of the javascript file to be loaded
      * @param {function} callback : name of the function to executed after the external load
      * @param {String} id : non mandatory, will give an id for the script tag to be write
      */
            var jsLoader = function jsLoader(file, callback, id) {
                var s = document.createElement("script");
                s.type = "text/javascript";
                if (id) {
                    s.id = "script-" + id;
                }
                if (s.readyState) {
                    // IE         
                    s.onreadystatechange = function() {
                        if (s.readyState == "loaded" || s.readyState == "complete") {
                            s.onreadystatechange = null;
                            if (callback) {
                                callback();
                            }
                        }
                    };
                } else {
                    s.onload = function() {
                        if (callback) {
                            callback();
                        }
                    };
                }
                s.src = file;
                for (i = 0; i < document.getElementsByTagName("head")[0].getElementsByTagName("script").length; i++) {
                    if (document.getElementsByTagName("head")[0].getElementsByTagName("script")[i].src == s.src) {
                        document.getElementsByTagName("head")[0].removeChild(document.getElementsByTagName("head")[0].getElementsByTagName("script")[i]);
                    }
                }
                document.getElementsByTagName("head")[0].appendChild(s);
            };
            var image = function image(imgUrl, id, obj) {
                obj.elmts[id] = new Image();
                obj.elmts[id].src = imgUrl;

            };
            /** 
       * history manager
       * @name Hash
       * @type function
       * 
       */
            var Hash = (function() {
                var
                // Import globals
                window = this,
                documentMode = document.documentMode,
                history = window.history,
                location = window.location,
                // Plugin variables
                callback,
                hash,
                // IE-specific
                iframe,

                getHash = function() {
                    // Internet Explorer 6 (and possibly other browsers) extracts the query
                    // string out of the location.hash property into the location.search
                    // property, so we can't rely on it. The location.search property can't be
                    // relied on either, since if the URL contains a real query string, that's
                    // what it will be set to. The only way to get the whole hash is to parse
                    // it from the location.href property.
                    //
                    // Another thing to note is that in Internet Explorer 6 and 7 (and possibly
                    // other browsers), subsequent hashes are removed from the location.href
                    // (and location.hash) property if the location.search property is set.
                    //
                    // Via Aaron: Firefox 3.5 (and below?) always unescape location.hash which
                    // causes poll to fire the hashchange event twice on escaped hashes. This is
                    // because the hash variable (escaped) will not match location.hash
                    // (unescaped.) The only consistent option is to rely completely on
                    // location.href.
                    var index = window.location.href.indexOf('#');
                    return (index == -1 ? '': window.location.href.substr(index + 1));
                },

                // Used by all browsers except Internet Explorer 7 and below.
                poll = function() {
                    var curHash = getHash();
                    if (curHash != hash) {
                        hash = curHash;
                        callback(curHash, false);
                    }
                },

                // Used to create a history entry with a value in the iframe.
                setIframe = function(newHash) {
                    try {
                        var doc = iframe.contentWindow.document;
                        doc.open();
                        doc.write('<html><body>' + newHash + '</body></html>');
                        doc.close();
                        hash = newHash;
                    } catch(e) {
                        setTimeout(function() {
                            setIframe(newHash);
                        },
                        10);
                    }
                },

                // Used by Internet Explorer 7 and below to set up an iframe that keeps track
                // of history changes.
                setUpIframe = function() {
                    // Don't run until access to the iframe is allowed.
                    try {
                        iframe.contentWindow.document;
                    } catch(e) {
                        setTimeout(setUpIframe, 10);
                        return;
                    }

                    // Create a history entry for the initial state.
                    setIframe(hash);
                    var data = hash;

                    setInterval(function() {
                        var curData,
                        curHash;

                        try {
                            curData = iframe.contentWindow.document.body.innerText;
                            if (curData != data) {
                                data = curData;
                                location.hash = hash = curData;
                                callback(curData, true);
                            } else {
                                curHash = getHash();
                                if (curHash != hash) {
                                    setIframe(curHash);
                                }
                            }
                        } catch(e) {}
                    },
                    50);
                };

                return {
                    init: function init(cb, ifr) {
                        // init can only be called once.
                        if (callback) {
                            return;
                        }

                        callback = cb;

                        // Keep track of the hash value.
                        hash = getHash();
                        cb(hash, true);

                        // Run specific code for Internet Explorer.
                        if (window.ActiveXObject) {
                            if (!documentMode || documentMode < 8) {
                                // Internet Explorer 5.5/6/7 need an iframe for history
                                // support.
                                iframe = ifr;
                                setUpIframe();
                            } else {
                                // Internet Explorer 8 has onhashchange event.
                                window.attachEvent('onhashchange', poll);
                            }
                        } else {
                            // Change Opera navigation mode to improve history support.
                            if (history.navigationMode) {
                                history.navigationMode = 'compatible';
                            }
                            setInterval(poll, 50);
                        }
                    },
                    getHash: getHash,
                    go: function go(newHash) {
                        // Cancel if the new hash is the same as the current one, since there
                        // is no cross-browser way to keep track of navigation to the exact
                        // same hash multiple times in a row. A wrapper can handle this by
                        // adding an incrementing counter to the end of the hash.
                        if (newHash == hash) {
                            return;
                        }
                        if (iframe) {
                            setIframe(newHash);
                        } else {
                            location.hash = hash = newHash;
                            if (callback) {
                                callback(newHash, false);
                            }
                        }
                    },
                    // add call back functions to the list
                    // each function will be launched when hash change
                    // new hash value will be passed to these
                    add2Queue: function add2Queue(obj, func) {
                        obj.core.hashQueue.push(func);
                        //run the handler to manage newly added funcs with the current hash in url
                        obj.core.hashHandler(obj.core.Hash.getHash());
                    }
                };
            })();
            // queue for functions to be runned at hash change
            var hashQueue = [];
            // handler to manage each hash change
            var hashHandler = function hashHandler(newHash, initial) {
                //log("hash change : " + newHash);
                //log("nb of actions in hash queue : " + hashQueue.length);
                for (i in hashQueue) {
                    if (typeof hashQueue[i] == "function") {
                        hashQueue[i](newHash, initial);
                    }
                }
            };

            return {
                extend: extend,
                hashQueue: hashQueue,
                hashHandler: hashHandler,
                Hash: Hash,
                jsLoader: jsLoader,
                cssLoader: cssLoader,
                initLogMode: initLogMode,
                log: log,
                debug: debug,
                image: image,
                featureLoader: featureLoader,
                pluginUrl: pluginUrl
            };
        })();
        // create a few global framework settings
        // choose using google.load or not
        // if using it, choose loading a specific js library from google CDN or not
        /**
     *  settings object
     *  @name defaults
     *  @type {object}
     */
        /*
     *  @param {boolean} settings.useGoogleLoad
     *  @param  {string} settings.jsLib  name of js library to load through google load
     *  @param  {string} settings.jsLibVersion version of js library to load through google load
     *  @param  {boolean} settings.jsLibIsloaded true/false flag to indicate if the js library has been loaded
     *  @param  {boolean} settings.useJsLibLocalLoad  true/false to use the js local library or not
     *  @param  {string} settings.jsLibLocalUrl local url for js library
     *  @param  {string} settings.jsPluginPath path for all Reflow plugins
     *  @param  {string} settings.loadedJsListing will contain a list of external js file loaded through the Reflow loading module
     *  @param  {boolean} settings.debugMode true/false to activate the debug mode
     *  @param  {Object} pluginList contains a listing for all Reflow plugins available, so that they can be loaded easily
     *  @param  {string} settings.ieFirebugLiteUrl url for firebug lite debugging tool
     *  
     */
        this.settings = (function(obj) {
            var that = obj;
            var defaults = {
                rspace: /\s+/,
                rtrim: /^(\s|\u00A0)+|(\s|\u00A0)+$/g,
                useGoogleLoad: false,
                // true/false to use google load for js library
                jsLib: "jquery",
                // name of js library to load through google load
                jsLibVersion: "1.3.2",
                // version of js library to load through google load
                jsLibIsloaded: false,
                // true/false flag to indicate if the js library has been loaded    
                useJsLibLocalLoad: false,
                // true/false to use the js local library or not
                jsLibLocalUrl: "/scripts/lib/Jquery/jquery-1.3.2.min.js",
                // local url for js library
                jsPluginPath: "scripts/lib/",
                // path for all Reflow plugins
                loadedJsListing: '',
                // will contain a list of external js file loaded through the Reflow loading module
                debugMode: true,
                // true/false to activate the debug mode
                /** 
         * {object} list Reflow plugin with external javascript files associated : "settings.jsPluginPath" will be used as path to find these plugins
         * @property {object} 
         */
                pluginList: {
                    selector: 'selector.js',
                    // add a getElementsBySelector method
                    thumbnails: 'thumbnails.js',
                    // in a product page, manage thumbnails and big image behaviors
                    modimodo: 'modimodo.js',
                    // test for a popin/modal box manager (still in development for several features)
                    browser: 'browser.js',
                    // manage tests on browser name, version and os
                    tabs: 'tabs.js',
                    // manage tabs menus
                    cookies: 'cookie.js',
                    // will manage cookies (create, read, delete) : to be implemented
                    gmap: 'gmap.js',
                    // manage google map and points mapping + behavior : implementation in progress
                    //rounded   : 'rounded-shadows.js',   // create rounded boxes, with shadows and borders (optionals) : implementation
                    //roundedLight: 'rounded-simple.js',  // create rounded boxes only : implementation and tests in progressand tests in progress
                    select: 'select.js',
                    // will generate an ul+li list with behavior to replace unobtrusively a select menu : not implemented yet
                    carousel: 'carousel.js',
                    // will manage carousel features : not implemented yet
                    accordians: 'accordians.js',
                    // manage horizontal and vertical accordians, click or mouseover based : implementation in progress
                    toggles: 'toggles.js',
                    // toggle things...
                    modals: 'modals.js'
                    // Modal things...
                },
                ieFirebugLiteUrl: '/scripts/lib/Reflow/nitobi.bug.compressed.js'
                // url for firebug lite debugging tool
            };
            return that.core.extend(defaults, params);
        })(this);

        this.customer = {
            lang: "en"
        };
        // for elements selected through DOM Reflow methods to be handled by others DOM methods simply
        this.prototype = this.DOM.prototype = Object.prototype;
        /**
    * init all the library and its error handling
    *
    * @classDescription This function inits all the library and its error handling
    * @type {function}
    * @param {object} obj : the current instance to be initialized
    */

        (function init(obj) {
            /*
        forEach, version 1.0
        Copyright 2006, Dean Edwards
        License: http://www.opensource.org/licenses/mit-license.php
      */

            // array-like enumeration
            if (!Array.forEach) { // mozilla already supports this
                Array.forEach = function(array, block, context) {
                    for (var i = 0; i < array.length; i++) {
                        block.call(context, array[i], i, array);
                    }
                };
            }

            // generic enumeration
            Function.prototype.forEach = function(object, block, context) {
                for (var key in object) {
                    if (typeof this.prototype[key] == "undefined" && context) {
                        block.call(context, object[key], key, object);
                    }
                }
            };

            // character enumeration
            String.forEach = function(string, block, context) {
                Array.forEach(string.split(""),
                function(chr, index) {
                    block.call(context, chr, index, string);
                });
            };

            // debug mode + non compatible browser (IE), load firefug lite to allow console
            obj.core.initLogMode(obj);

            // error catching
            //window.onerror = obj.core.debug;
        })(this);
    }
    return new Reflow({});
})();
