if ( !framework ) {

var framework = {};

(function() {
    var version = parseFloat(navigator.appVersion);

    framework.isOpera = (navigator.userAgent.indexOf("Opera") >= 0) ? version : 0;
    framework.isKhtml = (navigator.appVersion.indexOf("Konqueror") >= 0)||(navigator.appVersion.indexOf("Safari") >= 0) ? version : 0;
    framework.isWebKit = parseFloat(navigator.userAgent.split("WebKit/")[1]) || undefined;
    framework.isChrome = parseFloat(navigator.userAgent.split("Chrome/")[1]) || undefined;

    var index = Math.max(navigator.appVersion.indexOf("WebKit"), navigator.appVersion.indexOf("Safari"), 0);

    if ( index && !framework.isChrome ) {
        framework.isSafari = parseFloat(navigator.appVersion.split("Version/")[1]);
            if ( !framework.isSafari || parseFloat(navigator.appVersion.substr(index + 7)) <= 419.3 ) {
                framework.isSafari = 2;
            }
    }

    var geckoPos = navigator.userAgent.indexOf("Gecko");
    framework.isMozilla = framework.isMoz = ((geckoPos >= 0)&&(!framework.isKhtml)) ? version : 0;
    framework.isFF = 0;
    framework.isIE = 0;
    try {
        if(framework.isMoz){
            framework.isFF = parseFloat(navigator.userAgent.split("Firefox/")[1].split(" ")[0]);
        }
        if((document.all)&&(!framework.isOpera)){
            framework.isIE = parseFloat(navigator.appVersion.split("MSIE ")[1].split(";")[0]);
        }
    }catch(e){}
})();

framework._mixin = function(to, from) {
    var o = {};
    for ( var prop in from ) {
        if ( typeof o[prop] == "undefined" || o[prop] != from[prop] ) {
            to[prop] = from[prop];
        }
    }

    if ( framework.isIE && from ) {
        var s = from.toString;
        if ( typeof s == "function" && s != to.toString && s != o.toString &&
             s != "\nfunction toString() {\n    [native code]\n}\n" ) {
            to.toString = from.toString;
        }
    }

    return to;

}

framework._extend = function(child, parent) {
    var f = function() {};
    f.prototype = parent.prototype;
    child.prototype = new f();
    child.prototype.constructor = child;
    child.superclass = parent.prototype;
}

framework._registerNameSpace = function(fullClassName) {
    var parts = fullClassName.split(".");
    var namespace = window;
    for ( var i = 0; i < parts.length-1; i++ ) {
        namespace = namespace[parts[i]] || (namespace[parts[i]]={});
    }
    return {"namespace": namespace, "className": parts[parts.length-1]};
}

framework.isDeclared = function(fullClassName) {
    var parts = fullClassName.split("."), l = parts.length, i, o = window;
    for ( i = 0; i < l; i++ ) {
        if ( !(o = o[parts[i]]) ) {
            return false;
        }
    }
    return true;
}

framework.declareClass = function(name, parent, constructor, properties, staticProperties) {
    var n = framework._registerNameSpace(name);
    n.namespace[n.className] = constructor;
    framework._extend(constructor, parent||Object);
    constructor.prototype.fullClassName = name;

    if ( properties ) framework._mixin(constructor.prototype, properties);
    if ( staticProperties ) framework._mixin(constructor, staticProperties);
}

framework.htmlspecialchars = function(str) {
    return str.replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;');
}

framework.event = {};

framework.event.preventDefault = function(evnt) {
    if (evnt.preventDefault) {
        evnt.preventDefault();
    } else {
        evnt.returnValue = false;
    }
}

framework.event.stopPropagation = function(evnt) {
    if ( evnt.stopPropagation ) {
        evnt.stopPropagation();
    } else {
        evnt.cancelBubble = true;
    }
}

framework.event.stopEvent = function(evnt) {
    framework.event.preventDefault(evnt);
    framework.event.stopPropagation(evnt);
}

if ( framework.isIE ) {
    framework.event._wrapHandler = function(handler, sender) {
        return function(evnt) {
            var e = evnt || window.event;
            e.target = e.srcElement;
            e.currentTarget = (sender || e.srcElement); 
            var docBody = ((framework.isIE < 6) || (document["compatMode"] == "BackCompat")) ? document.body : document.documentElement;
            var body = document.body;
            var bodyStyle = framework.getComputedStyle(body);
            var de = document.documentElement;
            var offset = (framework.isIE >= 7) ?
                {x: de.getBoundingClientRect().left, y: de.getBoundingClientRect().top}
                :
                {x: bodyStyle.direction == "ltr" || window.parent == window ?
                de.clientLeft : de.offsetWidth - de.clientWidth - de.clientLeft, 
                y: de.clientTop};
            e.pageX = e.clientX - offset.x;
            e.pageY = e.clientY + (docBody.scrollTop || 0) - offset.y;
            handler(e);
        };
    }
} else {
    framework.event._wrapHandler = function(handler, sender) {
        return handler;
    }
}

framework.event.addEventListener = function(element, event, handler) {
    if ( !framework.isIE && (event=="mouseenter" || event=="mouseleave") ) {
        var oldHandler = handler;
        handler = function(evnt) {
            if ( framework.dom.isDescendant(evnt.relatedTarget, element) == false ) {
                oldHandler.call(this, evnt);
            }
        };
        event = event=="mouseenter"?"mouseover":"mouseout";
    }
    var wrapped = framework.event._wrapHandler(handler, element);
    wrapped.element = element;
    wrapped.event = event;
    if ( typeof element.addEventListener != "undefined" ){
        element.addEventListener(event, wrapped, false);
    } else if ( typeof element.attachEvent != "undefined" ) {
        element.attachEvent("on" + event, wrapped);
    }
    return wrapped;
}

framework.event.removeEventListener = function(element, event, handler) {
    if ( arguments.length == 3 ) {
        var el = element, evnt = event, h = handler;
    } else if ( arguments.length == 1 && arguments[0].element && arguments[0].event ) {
        var h = arguments[0], el = h.element, evnt = h.event;
    } else {
        throw new Error("Could't remove event listener");
    }

    if ( typeof el.removeEventListener != "undefined" ) {
        el.removeEventListener(evnt, h, false);
    } else if ( typeof el.detachEvent != "undefined" ) {
        el.detachEvent("on" + evnt, h);
    }
    return handler;
}

framework.declareClass("framework.event.Event", null,
    function() {
        this._listeners = [];
    },
    {
        add: function(listener, scope) {
            this._listeners[this._listeners.length] = [listener, scope];
            return listener;
        },
        addEventListener: function(listener, scope) {
            return this.add(listener, scope);
        },
        remove: function(listener) {
            for ( var i = 0; i < this._listeners.length; i++ ) {
                if ( this._listeners[i][0] == listener ) {
                    this._listeners = this._listeners.slice(0, i).concat(this._listeners.slice(i+1));
                    break;
                }
            }
            return listener;
        },
        removeEventListener: function(listener) {
            return this.remove(listener);
        },
        removeAll: function() {
            this._listeners = [];
        },
        fire: function() {
            for ( var i = 0; i < this._listeners.length; i++ ) {
                try {
                    this._listeners[i][0].apply(this._listeners[i][1]||window, arguments);
                } catch (e) {
                    if ( window.console && typeof console.log == "function" ) console.log(e);
                    throw e;
                }
            }
        }
    }
);

framework.runInScope = function(scope, method) {
    var args = []; for ( var i = 2; i < arguments.length; i++ ) args.push(arguments[i]);
    return function() {
        var _args = []; for ( var i = 0; i < arguments.length; i++ ) _args.push(arguments[i]); 
        return method.apply(scope, _args.concat(args));
    };
};

framework.dom = {};

framework.dom.getXY = function(node) {
    var f;
    if (document.documentElement.getBoundingClientRect) { // IE
        var getOffset = function() {
            var de = document.documentElement;
            return (framework.isIE >= 7) ?
            {x: de.getBoundingClientRect().left, y: de.getBoundingClientRect().top}
            :
            { x: (framework.getComputedStyle(document.body).direction == "ltr") || window.parent == window ?
              de.clientLeft : d.offsetWidth - de.clientWidth - de.clientLeft, 
              y: de.clientTop}
        };
        f = function(el) {
            var box = el.getBoundingClientRect();

            var offset = (framework.isIE) ? getOffset() : { x: 0, y: 0};

            return [box.left - offset.x + Math.max(document.documentElement.scrollLeft, document.body.scrollLeft),
                    box.top - offset.y + Math.max(document.documentElement.scrollTop, document.body.scrollTop)];
        };
    } else if ( document.getBoxObjectFor ) {
        f = function(el) {
            var box = document.getBoxObjectFor(el);

            var obj = el.offsetParent;
            var scroll = [0,0];
            while ( obj && obj.scrollTop && obj.scrollLeft ) {
                scroll[0] += obj.scrollLeft;
                scroll[1] += obj.scrollTop;
                obj = obj.parentNode;
            }
            var pos = [box.x - scroll[0] + document.body.scrollLeft + document.documentElement.scrollLeft,
                       box.y - scroll[1] + document.body.scrollTop + document.documentElement.scrollTop];
            return pos;
        };
    } else {
        f = function(el) { // manually calculate by crawling up offsetParents
            var pos = [el.offsetLeft, el.offsetTop];
            var parentNode = el.offsetParent;

            // safari: subtract body offsets if el is abs (or any offsetParent), unless body is offsetParent
            var accountForBody = (framework.isSafari &&
                    framework.getComputedStyle(el).position == 'absolute' &&
                    el.offsetParent == el.ownerDocument.body);

            if (parentNode != el) {
                while (parentNode) {
                    pos[0] += parentNode.offsetLeft;
                    pos[1] += parentNode.offsetTop;
                    if (!accountForBody && framework.isSafari && 
                            framework.getComputedStyle(parentNode).position == 'absolute' ) { 
                        accountForBody = true;
                    }
                    parentNode = parentNode.offsetParent;
                }
            }

            if (accountForBody) { //safari doubles in this case
                pos[0] -= el.ownerDocument.body.offsetLeft;
                pos[1] -= el.ownerDocument.body.offsetTop;
            } 
            parentNode = el.parentNode;

            // account for any scrolled ancestors
            while ( parentNode.tagName && !(/^body|html$/i).test(parentNode.tagName) ) 
            {
                if (parentNode.scrollTop || parentNode.scrollLeft) {
                    // work around opera inline/table scrollLeft/Top bug (false reports offset as scroll)
                    if (!(/^(?:inline|table-row)$/i).test(framework.getComputedStyle(parentNode).display)) { 
                        if (!framework.isOpera || framework.getComputedStyle(parentNode).overflow !== 'visible') { // opera inline-block misreports when visible
                            pos[0] -= parentNode.scrollLeft;
                            pos[1] -= parentNode.scrollTop;
                        }
                    }
                }
                
                parentNode = parentNode.parentNode; 
            }

            return pos;
        };
    }

    return f(node);
};

framework.dom.getPixelValue = function(element, value) {
    return parseFloat(value) || 0;
};

framework.dom.getMargins = function(node, computedStyle) {
    var s = computedStyle||framework.getComputedStyle(node);

    var margins = {
        t: framework.dom.getPixelValue(node, s.marginTop),
        l: framework.dom.getPixelValue(node, s.marginLeft),
        r: framework.dom.getPixelValue(node, s.marginRight),
        b: framework.dom.getPixelValue(node, s.marginBottom)
    };

    margins.w = margins.l + margins.r;
    margins.h = margins.t + margins.b;

    return margins;
};

framework.dom.getBorders = function(node, computedStyle) {
    var s = computedStyle||framework.getComputedStyle(node);

    var borders = {
        t: s.borderTopStyle!="none"?framework.dom.getPixelValue(node, s.borderTopWidth):0,
        l: s.borderLeftStyle!="none"?framework.dom.getPixelValue(node, s.borderLeftWidth):0,
        r: s.borderRightStyle!="none"?framework.dom.getPixelValue(node, s.borderRightWidth):0,
        b: s.borderBottomStyle!="none"?framework.dom.getPixelValue(node, s.borderBottomWidth):0
    };
    borders.w = borders.l + borders.r;
    borders.h = borders.t + borders.b;

    return borders;
};

framework.dom.getPaddings = function(node, computedStyle) {
    var s = computedStyle||framework.getComputedStyle(node);

    var paddings = {
        t: framework.dom.getPixelValue(node, s.paddingTop),
        l: framework.dom.getPixelValue(node, s.paddingLeft),
        r: framework.dom.getPixelValue(node, s.paddingRight),
        b: framework.dom.getPixelValue(node, s.paddingBottom)
    };

    paddings.w = paddings.l + paddings.r;
    paddings.h = paddings.t + paddings.b;

    return paddings;
};

framework.dom.getMBP = function(node, computedStyle) {
    var s = computedStyle||framework.getComputedStyle(node), d = framework.dom;

    var p = d.getPaddings(node, s), b = d.getBorders(node, s), m = d.getMargins(node, s);

    return { t: p.t+b.t+m.t, l: p.l+b.l+m.l, w: p.w+b.w+m.w, h: p.h+b.h+m.h, r: p.r+b.r+m.r, b: p.b+b.b+m.b };
};

framework.dom.getBP = function(node, computedStyle) {
    var s = computedStyle||framework.getComputedStyle(node), d = framework.dom;

    var p = d.getPaddings(node, s), b = d.getBorders(node, s);

    return { t: p.t+b.t, l: p.l+b.l, w: p.w+b.w, h: p.h+b.h, r: p.r+b.r, b: p.b+b.b };
};

/* actually border box relative to document root */
framework.dom.getBox = function(node) {
    var xy = framework.dom.getXY(node);
    return {l: xy[0], t: xy[1], w: node.offsetWidth, h: node.offsetHeight };
}

framework.dom.getContentBox = function(node, computedStyle) {
    var s = computedStyle||framework.getComputedStyle(node);

    var p = framework.dom.getPaddings(node, s), b = framework.dom.getBorders(node, s),
    box = framework.dom.getBox(node), w = node.clientWidth, h;
    if ( !w ) {
        w = node.offsetWidth, h = node.offsetHeight;
    } else {
        h = node.clientHeight, b.w = b.h = 0;
    }

    return { l:box.l + b.l + p.l, t: box.t + b.t + p.t, w: w - b.w - p.w, h: h - b.h - p.h };
}

framework.dom.getPaddingBox = function(node, computedStyle) {
    var s = computedStyle||framework.getComputedStyle(node);

    var b = framework.dom.getBorders(node, s), box = framework.dom.getBox(node);

    return { l:box.l + b.l, t: box.t + b.t, w: box.w - b.l - b.r, h: box.h - b.t - b.b };
}

framework.dom.getMarginBox = function(node, computedStyle) {
    var s = computedStyle||framework.getComputedStyle(node);

    var m = framework.dom.getMargins(node, s), box = framework.dom.getBox(node);

    return { l:box.l - m.l, t: box.t - m.t, w: box.w + m.r + m.l, h: box.h + m.t + m.b };
}

framework.dom.getViewport = function(){
    var win = window;
    var d = document;

    var w = 0, h = 0;
    var de = d.documentElement;
    var dew = de.clientWidth, deh = de.clientHeight;

    if ( framework.isMozilla ){
        var dbw = d.body.clientWidth;

        if ( dbw > dew ){
            minw = dew;
            maxw = dbw;
        } else {
            maxw = dew;
            minw = dbw;
        }
        var dbh = d.body.clientHeight;
        if ( dbh > deh ) {
            minh = deh;
            maxh = dbh;
        } else {
            maxh = deh;
            minh = dbh;
        }
        w = (maxw > win.innerWidth) ? minw : maxw;
        h = (maxh > win.innerHeight) ? minh : maxh;
    } else if ( !framework.isOpera && win.innerWidth ) {
        w = win.innerWidth;
        h = win.innerHeight;
    } else if ( framework.isIE && de && deh ){
        w = dew;
        h = deh;
    } else if ( d.body.clientWidth ){
        w = d.body.clientWidth;
        h = d.body.clientHeight;
    }

    var scroll = {
        x: (win.pageXOffset || de.scrollLeft || d.body.scrollLeft || 0),
        y: (win.pageYOffset || de.scrollTop || d.body.scrollTop || 0)
    }

    return { width: w, height: h, scrollLeft: scroll.x, scrollTop: scroll.y };
};

framework.dom.isDescendant = function(element, ancestor) {
    while ( element ) {
        if ( element === ancestor ) {
            return true;
        }
        element = element.parentNode;
    }
    return false;
};

framework.dom.removeAll = function(nodes) {
    for ( var i = 0, l = nodes.length; i < l; i++ ) {
        nodes[i].parentNode.removeChild(nodes[i]);
    }
};

framework.dom.removeExcept = function(nodes) {
    var parent = nodes[0].parentNode, i, l = nodes.length, j, k, n = [nodes[0]];
    for ( i = 1; i < l; i++ ) {
        n[i] = nodes[i];
        if ( nodes[i].parentNode != parent ) {
            throw new Error("All nodes must have same parent");
        }
    }
    for ( j = parent.childNodes.length - 1; j >= 0; j-- ) {
        for ( i = 0; i < l; i++ ) {
            if ( n[i] == parent.childNodes[j] ) {
                break;
            }
        }
        if ( i == l ) {
            parent.removeChild(parent.childNodes[j]);
        } else {/* found, we may remove from node list */
            n = n.slice(0, i).concat(n.slice(i+1));
            l--;
        }
    }
};


framework.evalXPath = function(node, xpath) {
    if ( window.XPathEvaluator ) {
        var xpe = new XPathEvaluator();
        var nsResolver = xpe.createNSResolver(node.nodeType == 9 ?
                node.documentElement : node.ownerDocument.documentElement);
        var result = xpe.evaluate(xpath, node, nsResolver, 0, null);
        switch ( result.resultType ) {
            case XPathResult.NUMBER_TYPE:
                return result.numberValue;
            case XPathResult.STRING_TYPE:
                return result.stringValue;
            case XPathResult.BOOLEAN_TYPE:
                return result.booleanValue;
            default:
                var found = [];
                var res;
                while (res = result.iterateNext()) {
                    found.push(res);
                }
                return found;
        }
    } else if ( typeof node.selectNodes != "undefined" ) {
        (node.nodeType == 9?node:node.ownerDocument).setProperty("SelectionLanguage", "XPath");
        return node.selectNodes(xpath);
    }
};

framework.getXPath = function(node) {
    var namespaceStr, localNameStr, nodes, positionStr, i;
    switch ( node.nodeType ) {
        case 9:
            return "/";
        case 1:
            namespaceStr = node.namespaceURI!=null?"namespace-uri()='" + node.namespaceURI + "' and ":"";
            localNameStr = "local-name()='" + (node.baseName||node.localName) + "'";
            nodes = framework.evalXPath(node.parentNode, "child::*["+namespaceStr+localNameStr+"]");
            if ( nodes.length == 1 ) {
                positionStr = "";
            } else {
                for ( i = 0; i < nodes.length; i++ ) {
                    if ( nodes[i] === node ) {
                        positionStr = "[" + (i+1) + "]";
                    }
                }
            }
            return framework.getXPath(node.parentNode) + (node.parentNode.nodeType==9?"":"/")
                   + "child::*["+namespaceStr+localNameStr+"]" + positionStr;
        case 2:
            namespaceStr = node.namespaceURI!=null?"namespace-uri()='" + node.namespaceURI + "' and ":"";
            localNameStr = "local-name()='" + (node.baseName||node.localName) + "'";
            return framework.getXPath(node.parentNode) + "/attribute::*[" + namespaceStr + localNameStr + "]";
    }
};

framework.declareClass("HashMap", null,
    function(hashFunction) {
        this._keys   = [];
        this._values = [];
        this._indexes = {};
        this.length = 0;
        this._hashFunction = hashFunction;
    },
    {
        _getHash: function(object) {
            if ( this._hashFunction ) {
                return this._hashFunction(object);
            } else if ( typeof object.toString != "undefined" ) {
                return object.toString();
            } else {
                return "";
            }
        },
        add: function(key, value) {
            var i = this._keys.push(key);
            this._values.push(value);
            var hash = this._getHash(key);
            if ( !this._indexes[hash] ) {
                this._indexes[hash] = [];
            }
            this._indexes[hash].push(i-1);
            this.length++;
        },
        get: function(key) {
            var index = -1, i, hash = this._getHash(key), indexes = this._indexes[hash], l = indexes?indexes.length:0;
            for ( i = 0; i < l; i++ ) {
                if ( this._keys[indexes[i]] === key ) {
                    index = indexes[i];
                    break;
                }
            }
            if ( index != -1 ) {
                return this._values[index];
            }
            return null;
        },
        containsKey: function(key) {
            var i, hash = this._getHash(key), indexes = this._indexes[hash], l = indexes?indexes.length:0;
            for ( i = 0; i < l; i++ ) {
                if ( this._keys[indexes[i]] === key ) {
                    return true;
                }
            }
            return false;
        }
    }
);

framework._onLoadListenerAdded = false;
framework._loaded = false;

framework.declareClass("framework.event.OnLoadEvent", framework.event.Event,
    function() {
        framework.event.OnLoadEvent.superclass.constructor.apply(this, arguments);
        this._loaded = false;
        this._args = [];
    },
    {
        add: function(listener, scope) {
            if ( !this._loaded ) {
                framework.event.OnLoadEvent.superclass.add.apply(this, arguments);
            } else {
                listener.apply(scope||window, this._args);
            }
        },
        fire: function() {
            if ( !this._loaded ) {
                framework.event.OnLoadEvent.superclass.fire.apply(this, arguments);
                this._loaded = true;
                for ( var i = 0, l = arguments.length; i < l; i++ ) {
                    this._args.push(arguments[i]);
                }
            }
        }
    }
);

framework.onLoad = new framework.event.OnLoadEvent();

framework._addOnLoadListener = function() {

    if ( framework._onLoadListenerAdded ) return;
    framework._onLoadListenerAdded = true;

    if ( document.addEventListener ) {
        document.addEventListener(
            "DOMContentLoaded",
            function() {
                document.removeEventListener("DOMContentLoaded", arguments.callee, false);
                framework._loaded = true;
                framework.onLoad.fire();
            },
            false
        );

    } else if ( document.attachEvent ) {
        document.attachEvent(
            "onreadystatechange",
            function() {
                if ( document.readyState === "complete" ) {
                    document.detachEvent( "onreadystatechange", arguments.callee );
                    if ( framework._loaded ) return;
                    framework._loaded = true;
                    framework.onLoad.fire();
                }
            }
        );

        if ( document.documentElement.doScroll && !window.frameElement ) (function(){
            if ( framework._loaded ) return;

            try {
                document.documentElement.doScroll("left");
            } catch( error ) {
                setTimeout( arguments.callee, 0 );
                return;
            }

            framework._loaded = true;
            framework.onLoad.fire();
        })();
    }

    framework.event.addEventListener(window, "load", function() {
        if ( framework._loaded ) return;
        framework._loaded = true;
        framework.onLoad.fire();
    });
};

framework.declareClass("framework.ElementWrapper", null,
    function(element) {
        this.element = element;
        this._classes = null;
        this._lastIndex = -1;
    },
    {
        hasClass: function(className) {
            this._classes = this._getClasses();
            var l = this._classes.length;
            for ( var i = 0; i < l; i++ ) {
                if ( this._classes[i] == className ) {
                    this._lastIndex = i;
                    return true;
                }
            }
            return false;
        },
        addClass: function(className) {
            if ( !this.hasClass(className) ) {
                this._classes.push(className);
                this.element.className = this._classes.join(" ");
            }
        },
        removeClass: function(className) {
            if ( this.hasClass(className) ) {
                this.element.className = (this._classes =
                    this._classes.slice(0, this._lastIndex).concat(this._classes.slice(this._lastIndex+1))).join(" ");
            }
        },
        _getClasses: function() {
            var className = this.element.className.replace(/^\s*(.*?)\s*$/, "$1");
            return className!=""?className.split(/\s+/):[];
        }
    }
);

framework.el = function(element) {
    return new framework.ElementWrapper(element);
};

framework._addOnLoadListener();

framework.getComputedStyle = (function() {
    if ( framework.isIE ) {
        return function(element){
            return element.currentStyle;
        };
    } else if ( framework.isSafari ) {
        return function(element) {
            var style = document.defaultView.getComputedStyle(element, null);
            if ( !style && element.style ) { 
                element.style.display = ""; 
                style = document.defaultView.getComputedStyle(element, null);
            }
            return style || {};
        }; 
    } else {
        return function(element){
            return document.defaultView.getComputedStyle(element, null);
        };
    }
})();

framework.getOpacity = (function() {
    if ( framework.isIE ) {
        return function(element){
            try{
                return (node.filters.alpha.opacity / 100);
            }catch(e){
                return 1;
            }
        };
    } else {
        return function(node) {
            return framework.getComputedStyle(node).opacity;
        };
    }
})();

framework.setOpacity = (function() {
    if ( framework.isIE ) {
        return function(element, opacity) {
            if ( opacity == 1 ) {
                element.style.cssText = element.style.cssText.replace(/FILTER:[^;]*;/i, "");
                if ( element.tagName == "TR" ) {
                    var l = element.cells.length;
                    for ( var i = 0; i < l; i++ ) {
                        element.cells[i].style.cssText = element.cells[i].style.cssText.replace(/FILTER:[^;]*;/i, "");
                    }
                }
            } else {
                var filterStr = "Alpha(Opacity=" + (opacity*100) + ")";
                element.style.filter = filterStr;

                if ( element.tagName == "TR" ) {
                    var l = element.cells.length;
                    for ( var i = 0; i < l; i++ ) {
                        element.cells[i].style.filter = filterStr;
                    }
                }
            }
            return opacity;
        };
    } else {
        return function(element, opacity){
            return element.style.opacity = opacity;
        };
    }
})();

framework.isArray = function(obj) {
    return obj && obj instanceof Array || typeof obj == "array";
}

framework.isString = function(obj) {
    return typeof obj == "string" || obj instanceof String;
}

framework.isNative = function(func) {
    var str = func + "";
    return !!str.match(/^\s*function\s+([A-z_0-9])+\(\)\s*{\s*\[native code\]\s+}\s*$/);
}

framework.isNumber = function(obj) {
    return typeof obj == "number" || obj instanceof Number;
}

framework.isNode = function(obj) {
    if ( typeof obj.appendChild != "undefined" && framework.isNative(obj.appendChild) ) {
        return true;
    } else {
        return false;
    }
}

framework.arrayIndexOf = function(arr, s) {
    for ( var i = 0, l = arr.length; i < l; i++ ) {
        if ( arr[i] == s ) {
            return i;
        }
    }
    return -1;
}


framework.declareClass("framework.Expr", null,
    function() {
    },
    {
        filter: function(context) {
        }
    }
);

framework.declareClass("framework.TagExpr", framework.Expr,
    function(tagName) {
        framework.TagExpr.superclass.constructor.apply(this, arguments);
        this.tagName = tagName;
        this.predicate = [];
        this.operator = " ";
    },
    {
        _getNodes: function(node) {
            switch(this.operator) {
                case " ":
                    return node.getElementsByTagName(this.tagName);
                    break;
                case ">":
                    var c = node.childNodes, t = this.tagName.toUpperCase(), n = [];
                    for ( var i = 0, l = c.length; i < l; i++ ) {
                        if ( c[i].tagName == t || t == "*" ) {
                            n.push(c[i]);
                        }
                    }
                    return n;
                    break;
                default:
                    throw new Error("Unknown operator '" + this.operator + "'");
                    break;
            }
        },
        filter: function(context) {
            var l = context.length, result = [], nodes, nodesArray = [], i, j, len;
            for ( i = 0; i < l; i++ ) {
                nodes = this._getNodes(context[i]);
                nodesArray = [];
                for ( j = 0, len = nodes.length; j < len; j++ ) {
                    nodesArray.push(nodes[j]);
                }
                if ( this.predicate.length > 0 ) {
                    for ( j = 0, len = this.predicate.length; j < len; j++ ) {
                        nodesArray = this.predicate[j].filter(nodesArray);
                    }
                }
                result = result.concat(nodesArray);
            }
            return result;
        }
    }
);

framework.declareClass("framework.IdExpr", framework.Expr,
    function(id) {
        framework.IdExpr.superclass.constructor.apply(this, arguments);
        this.id = id;
    },
    {
        filter: function(context) {
            var l = context.length, result = [], nodes;
            for ( var i = 0; i < l; i++ ) {
                if ( context[i].id == this.id ) {
                    result.push(context[i]);
                }
            }
            return result;
        }
    }
);

framework.declareClass("framework.ClassExpr", framework.Expr,
    function(className) {
        framework.ClassExpr.superclass.constructor.apply(this, arguments);
        this.className = className;
    },
    {
        filter: function(context) {
            var l = context.length, result = [], nodes;
            for ( var i = 0; i < l; i++ ) {
                if ( framework.el(context[i]).hasClass(this.className) ) {
                    result.push(context[i]);
                }
            }
            return result;
        }
    }
);

framework.declareClass("framework.AttrExpr", framework.Expr,
    function(name, value) {
        framework.AttrExpr.superclass.constructor.apply(this, arguments);
        this.name = name;
        this.value = value;
    },
    {
        filter: function(context) {
            var l = context.length, result = [], nodes, attr;
            for ( var i = 0; i < l; i++ ) {
                if ( (attr = context[i].getAttributeNode(this.name)) && (this.value && this.value == attr.value || !this.value) ) {
                    result.push(context[i]);
                }
            }
            return result;
        }
    }
);

framework.declareClass("framework.Selector", null,
    function(selector) {
        this._expr = this._parseSelector(selector);
    },
    {
        _parseSelector: function(selector) {
            var pos = 0;
            var char = "";
            var expr = [];
            var index = 0;
            var matches = null;
            var l = selector.length;
            var operator = null;

            for ( ; pos<l, char = selector.charAt(pos); pos++ ) {
                if ( char == " " ) {
                    while ( selector.charAt(pos+1) == " " ) {
                        pos++;
                    }
                    if ( operator == null )
                        operator = " ";
                } else if ( char == "+" ) {
                    operator = "+";
                    throw Error("Not supported");
                } else if ( char == ">" ) {
                    if ( operator == null || operator == " " ) {
                        operator = ">";
                        while ( selector.charAt(pos+1) == " " ) {
                            pos++;
                        }
                    } else {
                        throw new Error("Unexpected operator >");
                    }
                } else if ( (matches = selector.substr(pos).match(framework.Selector.TAG)) ) {
                    expr[index] = new framework.TagExpr(matches[1]);
                    expr[index].operator = operator||" ";
                    index++;
                    pos += matches[0].length-1;
                    operator = null;
                } else if ( (matches = selector.substr(pos).match(framework.Selector.ID)) ) {
                    if ( operator === null && index > 0 && expr[index-1] instanceof framework.TagExpr ) {
                        expr[index-1].predicate.push(new framework.IdExpr(matches[1]));
                    } else {
                        expr[index] = new framework.TagExpr("*");
                        expr[index].operator = operator||" ";
                        expr[index].predicate.push(new framework.IdExpr(matches[1]));
                        index++;
                    }
                    operator = null;
                    pos += matches[0].length-1;
                } else if ( (matches = selector.substr(pos).match(framework.Selector.CLASS)) ) {
                    if ( operator === null && index > 0 && expr[index-1] instanceof framework.TagExpr ) {
                        expr[index-1].predicate.push(new framework.ClassExpr(matches[1]));
                    } else {
                        expr[index] = new framework.TagExpr("*");
                        expr[index].operator = operator||" ";
                        expr[index].predicate.push(new framework.ClassExpr(matches[1]));
                        index++;
                    }
                    operator = null;
                    pos += matches[0].length-1;
                } else if ( (matches = selector.substr(pos).match(framework.Selector.ATTR)) ) {
                    if ( operator === null && index > 0 && expr[index-1] instanceof framework.TagExpr ) {
                        expr[index-1].predicate.push(new framework.AttrExpr(matches[1], matches[4]||null));
                    } else {
                        throw new Error("syntax is wrong at char " + pos + " of selector '"+selector+"' no element");
                    }
                    operator = null;
                    pos += matches[0].length-1;
                } else {
                    throw new Error("syntax is wrong at char " + pos);
                }
            }
            return expr;
        },
        execute: function(context) {
            var nodes = [context], l = this._expr.length;
            for ( var i = 0; i < l; i++ ) {
                nodes = this._expr[i].filter(nodes);
            }
            return nodes;
        }
    },
    {
        TAG: /^((?:[\w\u00c0-\uFFFF\*_-])+)/,
        ID: /^#((?:[\w\u00c0-\uFFFF_-])+)/,
        CLASS: /^\.((?:[\w\u00c0-\uFFFF_-])+)/,
        ATTR: /^\[\s*((?:[\w\u00c0-\uFFFF_-])+)\s*(?:(\S?=)\s*(['"]*)(.*?)\3|)\s*\]/
    }
);

framework.query = function(selector, context) {
    var s = new framework.Selector(selector);
    return s.execute(context||document.documentElement);
};

framework.declareClass("framework.Deferred", null,
    function(canceller) {
        this.chain = [];
        this.id = framework.Deferred._nextId();
        this.fired = -1;
        this.paused = 0;
        this.results = [null, null];
        this.canceller = canceller;
        this.silentlyCancelled = false;
    },
    {

        cancel: function() {
            var err;
            if ( this.fired == -1 ) {
                if ( this.canceller) {
                    err = this.canceller(this);
                }else{
                    this.silentlyCancelled = true;
                }
                if(this.fired == -1) {
                    if ( !(err instanceof Error) ) {
                        var res = err;
                        err = new Error("Deferred Cancelled");
                        err.dojoType = "cancel";
                        err.cancelResult = res;
                    }
                    this.errback(err);
                }
            } else if( (this.fired == 0) && (this.results[0] instanceof framework.Deferred) ) {
                this.results[0].cancel();
            }
        },

        _resback: function(res) {
            this.fired = ((res instanceof Error) ? 1 : 0);
            this.results[this.fired] = res;
            this._fire();
        },

        _check: function() {
            if ( this.fired != -1) {
                if ( !this.silentlyCancelled ) {
                    throw new Error("already called!");
                }
                this.silentlyCancelled = false;
                return;
            }
        },

        callback: function(res){
            this._check();
            this._resback(res);
        },

        errback: function(res) {
            this._check();
            if ( !(res instanceof Error) ) {
                res = new Error(res);
            }
            this._resback(res);
        },

        addBoth: function(cb, cbfn) {
            var enclosed;
            if ( !cbfn ) {
                enclosed = cb;
            } else {
                enclosed = framework.runInScope(cb, cbfn);
            }
            return this.addCallbacks(enclosed, enclosed);
        },

        addCallback: function(cb, cbfn) {
            var enclosed;
            if ( !cbfn ) {
                enclosed = cb;
            } else {
                enclosed = framework.runInScope(cb, cbfn);
            }
            return this.addCallbacks(enclosed, null);
        },

        addErrback: function(cb, cbfn) {
            var enclosed;
            if ( !cbfn ) {
                enclosed = cb;
            } else {
                enclosed = framework.runInScope(cb, cbfn);
            }
            return this.addCallbacks(null, enclosed);
        },

        addCallbacks: function(cb, eb) {
            this.chain.push([cb, eb]);
            if ( this.fired >= 0 ) {
                this._fire();
            }
            return this;
        },

        _fire: function() {
            var chain = this.chain, fired = this.fired, res = this.results[fired], self = this, cb = null;

            while ( (chain.length > 0) && (this.paused == 0) ) {
                var f = chain.shift()[fired];
                if ( !f ){ continue; }

                try {
                    res = f(res);
                    fired = ((res instanceof Error) ? 1 : 0);
                    if ( res instanceof framework.Deferred) {
                        cb = function(res) {
                            self._resback(res);
                            self.paused--;
                            if( (self.paused == 0) && (self.fired >= 0) ){
                                self._fire();
                            }
                        }

                        this.paused++;
                    }
                } catch(err) {
                    if ( window.console ) console.debug(err);
                    fired = 1;
                    res = err;
                }
            }

            this.fired = fired;
            this.results[fired] = res;

            if ( (cb) && (this.paused) ) {
                res.addBoth(cb);
            }
        }
    },
    {
        _n: 0,
        _nextId: function() {
            return framework.Deferred._n++;
        }
    }
);

framework.declareClass("framework.Xhr", null,
    function() {
    },
    {
    },
    {/* static */
        _XMLHTTP_PROGIDS: ['Msxml2.XMLHTTP', 'Microsoft.XMLHTTP', 'Msxml2.XMLHTTP.4.0'],
        _getXhrObj: function(){
            var http = null, lasError = null;

            if ( !framework.isIE ) {
                try { http = new XMLHttpRequest(); } catch(e) {}
            }

            if ( !http ) {
                for(var i=0; i<3; ++i) {
                    var progid = framework.Xhr._XMLHTTP_PROGIDS[i];
                    try {
                        http = new ActiveXObject(progid);
                    } catch(e) {
                        lastError = e;
                    }

                    if ( http ) {
                        framework.Xhr._XMLHTTP_PROGIDS = [progid];
                        break;
                    }
                }
            }

            if ( !http ) {
                throw new Error("XMLHTTP not available: "+lastError);
            }

            return http;
        },
        _isDocumentOk: function(http) {
            var stat = http.status || 0;
            return ( (stat>=200)&&(stat<300))||(stat==304)||(stat==1223)
                   ||(!stat && (location.protocol=="file:" || location.protocol=="chrome:") );
        },
        objectToQuery: function(map) {
            var enc = encodeURIComponent;
            var pairs = [];
            var backstop = {};
            for ( var name in map ) {
                var value = map[name];
                if(value != backstop[name]) {
                    var assign = enc(name) + "=";
                    if ( framework.isArray(value) ) {
                        for(var i=0; i < value.length; i++){
                            pairs.push(assign + enc(value[i]));
                        }
                    } else {
                        pairs.push(assign + enc(value));
                    }
                }
            }
            return pairs.join("&"); // String
        },
        queryToObject: function(str) {
            var ret = {};
            var qp = str.split("&");
            var dec = decodeURIComponent;
            var item;

            for ( var i = 0; i < qp.length; i++ ) {
                item = qp[i];
                if ( item.length ) {
                    var parts = item.split("=");
                    var name = dec(parts.shift());
                    var val = dec(parts.join("="));
                    if ( framework.isString(ret[name]) ) {
                        ret[name] = [ret[name]];
                    }
                    if( framework.isArray(ret[name]) ) {
                        ret[name].push(val);
                    } else {
                        ret[name] = val;
                    }
                }
            }
            return ret;
        },
        _contentHandlers: {
            "text": function(xhr) { return xhr.responseText; },
            "json": function(xhr) {
                return (xhr.status == 204) ? undefined : eval("(" + xhr.responseText + ")");
            },
            "json-comment-filtered": function(xhr) {
                var value = xhr.responseText;
                var cStartIdx = value.indexOf("\/*");
                var cEndIdx = value.lastIndexOf("*\/");

                if ( cStartIdx == -1 || cEndIdx == -1 ) {
                    throw new Error("JSON was not comment filtered");
                }
                return (xhr.status == 204) ? undefined :
                eval("(" + value.substring(cStartIdx+2, cEndIdx) + ")");
            },
            "javascript": function(xhr) {
                return eval(xhr.responseText);
            },
            "xml": function(xhr) {
                var result = xhr.responseXML;
                if ( framework.isIE && (!result || window.location.protocol == "file:") ) {
                    var prefix = ["MSXML2", "Microsoft", "MSXML", "MSXML3"];
                    for ( var i = 0; i < prefix.length; i++ ) {
                        try {
                            var dom = new ActiveXObject(prefix[i] + ".XMLDOM");
                            dom.async = false;
                            dom.loadXML(xhr.responseText);
                            result = dom;
                        } catch(e) {}
                    }
                }
                return result; // DOMDocument
            }
        },
        _ioSetArgs: function(args, canceller, okHandler, errHandler) {
            var ioArgs = {args: args, url: args.url};

            var miArgs = [{}];

            if ( args.content ) {
                miArgs.push(args.content);
            }
            if ( args.preventCache ) {
                miArgs.push({"framework.preventCache": new Date().valueOf()});
            }
            ioArgs.query = framework.Xhr.objectToQuery(framework._mixin.apply(null, miArgs));

            ioArgs.handleAs = args.handleAs || "text";
            var d = new framework.Deferred(canceller);

            d.addCallbacks(okHandler, function(error) {
                return errHandler(error, d);
            });

            var ld = args.load;
            if ( ld ) {
                d.addCallback(function(value) {
                    return ld.call(args, value, ioArgs);
                });
            }
            var err = args.error;
            if ( err ) {
                d.addErrback(function(value){
                    return err.call(args, value, ioArgs);
                });
            }
            var handle = args.handle;
            if ( handle ) {
                d.addBoth(function(value){
                    return handle.call(args, value, ioArgs);
                });
            }

            d.ioArgs = ioArgs;

            return d;
        },
        _deferredCancel: function(dfd) {
            //summary: canceller function for dojo._ioSetArgs call.

            dfd.canceled = true;
            var xhr = dfd.ioArgs.xhr;
            var _at = typeof xhr.abort;
            if ( _at == "function" || _at == "unknown" ) {
                xhr.abort();
            }
            var err = new Error("xhr cancelled");
            err.dojoType = "cancel";
            return err;
        },
        _deferredOk: function(dfd) {
            return framework.Xhr._contentHandlers[dfd.ioArgs.handleAs](dfd.ioArgs.xhr);
        },
        _deferError: function(error, dfd) {
            if ( window.console ) console.debug(error);
            return error;
        },
        _makeXhrDeferred: function(args) {
            var dfd = framework.Xhr._ioSetArgs(args, framework.Xhr._deferredCancel, framework.Xhr._deferredOk, framework.Xhr._deferError);
            dfd.ioArgs.xhr = framework.Xhr._getXhrObj(dfd.ioArgs.args);
            return dfd;
        },

        _inFlightIntvl: null,
        _inFlight: [],
        _watchInFlight: function() {
            var now = (new Date()).getTime();
            for ( var i = 0, tif; i < framework.Xhr._inFlight.length && (tif = framework.Xhr._inFlight[i]); i++) {
                var dfd = tif.dfd;
                try {
                    if ( !dfd || dfd.canceled || !tif.validCheck(dfd) ) {
                        framework.Xhr._inFlight.splice(i--, 1); 
                    } else if ( tif.ioCheck(dfd) ) {
                        framework.Xhr._inFlight.splice(i--, 1);
                        tif.resHandle(dfd);
                    } else if ( dfd.startTime ) {
                        if ( dfd.startTime + (dfd.ioArgs.args.timeout || 0) < now ) {
                            framework.Xhr._inFlight.splice(i--, 1);
                            var err = new Error("timeout exceeded");
                            dfd.errback(err);
                            dfd.cancel();
                        }
                    }
                } catch(e) {
                    dfd.errback(new Error("_watchInFlightError!"));
                }
            }

            if ( !framework.Xhr._inFlight.length ) {
                clearInterval(framework.Xhr._inFlightIntvl);
                framework.Xhr._inFlightIntvl = null;
                return;
            }

        },

        _ioCancelAll: function() {
            try {
                for ( var i = 0; i < framework.Xhr._inFlight.length; i++ ) {
                    framework.Xhr._inFlight.dfd.cancel();
                }
            } catch(e) {}
        },

        _ioWatch: function(dfd, validCheck, ioCheck, resHandle) {
            if ( dfd.ioArgs.args.timeout ) {
                dfd.startTime = (new Date()).getTime();
            }
            framework.Xhr._inFlight.push({dfd: dfd, validCheck: validCheck, ioCheck: ioCheck, resHandle: resHandle});
            if ( !framework.Xhr._inFlightIntvl ) {
                framework.Xhr._inFlightIntvl = setInterval(framework.Xhr._watchInFlight, 50);
            }
            framework.Xhr._watchInFlight(); // handle sync requests
        },

        _defaultContentType: "application/x-www-form-urlencoded",

        _validCheck: function(dfd){
            return dfd.ioArgs.xhr.readyState;
        },
        _ioCheck: function(dfd) {
            return 4 == dfd.ioArgs.xhr.readyState;
        },
        _resHandle: function(dfd) {
            var xhr = dfd.ioArgs.xhr;
            if ( framework.Xhr._isDocumentOk(xhr) ) {
                /*if ( dfd.ioArgs.handleAs != "xml" ) {*/
                    dfd.callback(dfd);
                /*} else {
                }*/
            } else {
                var err = new Error("Unable to load " + dfd.ioArgs.url + " status:" + xhr.status);
                err.status = xhr.status;
                err.responseText = xhr.responseText;
                dfd.errback(err);
            }
        },

        _doIt: function(type, dfd) {
            var ioArgs = dfd.ioArgs;
            var args = ioArgs.args;
            var xhr = ioArgs.xhr;
            xhr.open(type, ioArgs.url, args.sync !== true, args.user || undefined, args.password || undefined);
            if ( args.headers ) {
                for ( var hdr in args.headers ) {
                    if ( hdr.toLowerCase() === "content-type" && !args.contentType ) {
                        args.contentType = args.headers[hdr];
                    } else {
                        xhr.setRequestHeader(hdr, args.headers[hdr]);
                    }
                }
            }

            xhr.setRequestHeader("Content-Type", args.contentType || framework.Xhr._defaultContentType);

            if ( !args.headers || !args.headers["X-Requested-With"] ) {
                xhr.setRequestHeader("X-Requested-With", "XMLHttpRequest");
            }

            try {
                xhr.send(ioArgs.query);
            } catch(e) {
                dfd.cancel();
            }
            framework.Xhr._ioWatch(dfd, framework.Xhr._validCheck, framework.Xhr._ioCheck, framework.Xhr._resHandle);
            xhr = null;
            return dfd;
        },
        _ioAddQueryToUrl: function(ioArgs) {
            if ( ioArgs.query.length ) {
                ioArgs.url += (ioArgs.url.indexOf("?") == -1 ? "?" : "&") + ioArgs.query;
                ioArgs.query = null;
            }
        },

        /***
        //	url: String
        //		URL to server endpoint.
        //	content: Object?
        //		Contains properties with string values. These
        //		properties will be serialized as name1=value2 and
        //		passed in the request.
        //	timeout: Integer?
        //		Milliseconds to wait for the response. If this time
        //		passes, the then error callbacks are called.
        //	preventCache: Boolean?
        //		Default is false. If true, then a
        //		"dojo.preventCache" parameter is sent in the request
        //		with a value that changes with each request
        //		(timestamp). Useful only with GET-type requests.
        //	load: Function?
        //		function(response, ioArgs){}. response is an Object, ioArgs
        //		is of type dojo.__IoCallbackArgs. The load function will be
        //		called on a successful response.
        //	error: Function?
        //		function(response, ioArgs){}. response is an Object, ioArgs
        //		is of type dojo.__IoCallbackArgs. The error function will
        //		be called in an error case. 
        //	handle: Function?
        //		function(response, ioArgs){}. response is an Object, ioArgs
        //		is of type dojo.__IoCallbackArgs. The handle function will
        //		be called in either the successful or error case.  For
        //		the load, error and handle functions, the ioArgs object
        //		will contain the following properties: 
        //	handleAs: String?
        //		Acceptable values are: text (default), json, json-comment-optional,
        //		json-comment-filtered, javascript, xml
        //	sync: Boolean?
        //		false is default. Indicates whether the request should
        //		be a synchronous (blocking) request.
        //	headers: Object?
        //		Additional HTTP headers to send in the request.
        */

        send: function(method, args, hasBody) {
            var dfd = framework.Xhr._makeXhrDeferred(args);
            if ( !hasBody ) {
                framework.Xhr._ioAddQueryToUrl(dfd.ioArgs);
            }
            return framework.Xhr._doIt(method, dfd);
        },
        get: function(args){
            return framework.Xhr.send("GET", args);
        },

        post: function(args){
            return framework.Xhr.send("POST", args, true);
        }
    }
);

framework.declareClass("framework.Line", null,
    function(start, end) {
        this.start = start;
        this.end = end;
    },
    {
        getValue: function(n) {
            return ((this.end - this.start) * n) + this.start;
        }
    }
);

framework.declareClass("framework.Color", null,
    function(r, g, b, a) {
        this.r = r;
        this.g = g;
        this.b = b;
        this.a = a;
    },
    {
        setRgba: function(r, g, b, a) {
            this.r = r;
            this.g = g;
            this.b = b;
            this.a = a;
        },
        toHex: function() {
            return "#" + this.r.toString(16) + this.g.toString(16) + this.b.toString(16);
        },
        toCss: function(includeAlpha) {
            var rgb = this.r + ", " + this.g + ", " + this.b;
            return (includeAlpha?"rgba("+rgb+", "+this.a:"rgb("+rgb)+")";
        }
    },
    {
        blend: function(c1, c2, weight, obj) {
            var co = obj||new framework.Color(255,255,255,1);
            co.r = Math.round(c1.r + (c2.r - c1.r) * weight);
            co.g = Math.round(c1.g + (c2.g - c1.g) * weight);
            co.b = Math.round(c1.b + (c2.b - c1.b) * weight);
            co.a = c1.a + (c2.a - c1.a) * weight; 
            return co;
        },
        fromRgb: function(color, obj) {
            var co = obj||new framework.Color(255,255,255,1);
            var m = color.toLowerCase().match(/^rgba?\(([\s\.,0-9]+)\)/);
            return m && framework.Color.fromArray(m[1].split(/\s*,\s*/), co);
        },
        fromArray: function(a, obj) {
            var co = obj||new framework.Color(255,255,255,1);
            co.r = Number(a[0]); co.g = Number(a[1]); co.b = Number(a[2]); co.a = a[3]&&Number(a[3])||1;
            return co;
        },
        fromHex: function(color, obj) {
            var co = obj||new framework.Color(255,255,255,1);
            var bits = (color.length == 4) ? 4 : 8, mask = (1 << bits) - 1;
            color = Number("0x" + color.substr(1));
            if ( isNaN(color) ) {
                return null;
            }
            var c = [];
            for ( var i = 2; i >= 0; i-- ) {
                c[i] = color & mask;
                color >>= bits;
                c[i] = bits == 4 ? 17 * c[i] : c[i];
            }
            co.r = c[0]; co.g = c[1]; co.b = c[2];
            return co;
        },
        fromString: function(color) {
            if ( framework.Color[color] ) {
                return framework.Color[color];
            } else {
                return framework.Color.fromRgb(color)||framework.Color.fromHex(color);
            }
        },
        black:  [0,0,0],
        silver: [192,192,192],
        gray:   [128,128,128],
        white:  [255,255,255],
        maroon: [128,0,0],
        red:    [255,0,0],
        purple: [128,0,128],
        fuchsia:[255,0,255],
        green:  [0,128,0],
        lime:   [0,255,0],
        olive:  [128,128,0],
        yellow: [255,255,0],
        navy:   [0,0,128],
        blue:   [0,0,255],
        teal:   [0,128,128],
        aqua:   [0,255,255]
    }
);

(function(){
    for ( var c in framework.Color ) {
        if ( framework.isArray(framework.Color[c]) && framework.Color[c].length == 3 ) {
            framework.Color[c] = framework.Color.fromArray(framework.Color[c]);
        }
    }
})();

framework.declareClass("framework.Animator", null,

    function(config) {
        this.timer = null;

        this.duration = config.duration||1000;

        if ( framework.isArray(config.curve) ) {
            this.curve = new framework.Line(config.curve[0], config.curve[1]);
        } else {
            this.curve = config.curve||null;
        }

        this.easing = config.easing||null;
        this.repeat = config.repeat||null;
        this.delay  = config.delay||null;

        this.beforeBegin = new framework.event.Event();
        this.onBegin = new framework.event.Event();
        this.onAnimate = new framework.event.Event();
        this.onEnd = new framework.event.Event();
        this.onPlay = new framework.event.Event();
        this.onPause = new framework.event.Event();
        this.onStop = new framework.event.Event();
        this._percent = 0;
        this._startRepeatCount = 0;
        this._active = this._paused = false;
    },
    {
        play: function(delay, gotoStart) {
            if ( gotoStart ) {
                this.bindTimer();
                this._active = this._paused = false;
                this._percent = 0;
            } else if ( this._active && !this._paused ){
                return this;
            }

            this.beforeBegin.fire();

            var d = delay||this.delay;
            var p = framework.runInScope(this, this._play, gotoStart);

            if ( d > 0 ) {
                setTimeout(p, d);
                return this;
            } else {
                p();
                return this;
            }
        },
        _play: function(gotoStart) {
            this._startTime = new Date().valueOf();
            if ( this._paused ) {
                this._startTime -= this.duration * this._percent;
            }
            this._endTime = this._startTime + this.duration;

            this._active = true;
            this._paused = false;

            var value = this.curve.getValue(this._percent);

            if ( !this._percent ) {
                if ( !this._startRepeatCount ){
                    this._startRepeatCount = this.repeat;
                }
                this.onBegin.fire(value);
            }

            this.onPlay.fire(value);

            this._process();
            return this;
        },
        pause: function() {
            this.unBindTimer();
            if ( !this._active ) {
                return this;
            }
            this._paused = true;
            this.onPause.fire(this.curve.getValue(this._percent));
            return this;
        },

        gotoPercent: function(percent, andPlay) {
            this.unBindTimer();
            this._active = this._paused = true;
            this._percent = percent;

            if ( andPlay ) {
                this.play();
            }
            return this;
        },

        stop: function(gotoEnd) {
            if ( !this.timer ) {
                return this;
            }

            this.unBindTimer();

            if ( gotoEnd ) {
                this._percent = 1;
            }

            this.onStop.fire(this.curve.getValue(this._percent));
            this._active = this._paused = false;
            return this;
        },

        status: function(){
            if ( this._active ) {
                return this._paused ? "paused" : "playing";
            }
            return "stopped";
        },
        _process: function() {
            if(this._active){
                var curr = new Date().valueOf();
                var step = (curr - this._startTime) / (this._endTime - this._startTime);

                if ( step >= 1 ) {
                    step = 1;
                }
                this._percent = step;

                if ( this.easing ) {
                    step = this.easing(step);
                }

                this.onAnimate.fire(this.curve.getValue(step));

                if ( this._percent < 1 ) {
                    this.bindTimer();
                } else {
                    this._active = false;

                    if ( this.repeat > 0 ) {
                        this.repeat--;
                        this.play(null, true);
                    } else if ( this.repeat == -1 ) {
                        this.play(null, true);
                    } else {
                        if ( this._startRepeatCount ){
                            this.repeat = this._startRepeatCount;
                            this._startRepeatCount = 0;
                        }
                    }
                    this._percent = 0;
                    this.onEnd.fire();
                    this.unBindTimer();
                }
            }
            return this;
        },
        bindTimer: function() {
            if ( !this.timer ){
                this.timer = framework.Animator.onTimer.add(framework.runInScope(this, this._process));
                framework.Animator.timersCount++;
            }
            if ( !framework.Animator.timer ) {
                framework.Animator.timer = setInterval(function() { framework.Animator.onTimer.fire(); }, framework.Animator.rate);
            }
        },
        unBindTimer: function() {
            if ( this.timer ) {
                framework.Animator.onTimer.remove(this.timer);
                this.timer = null;
                framework.Animator.timersCount--;
            }
            if ( framework.Animator.timersCount <= 0 ) {
                clearInterval(framework.Animator.timer);
                framework.Animator.timer = null;
                framework.Animator.timersCount = 0;
            }
        }
    },
    {
        rate: 10,
        timersCount: 0,
        timer: null,
        onTimer: new framework.event.Event(),
        defaultEasing: function(n){ return 0.5 + ((Math.sin((n + 1.5) * Math.PI))/2); }
    }

);

framework.declareClass("framework.Properties", null,
    function(config) {
        this._properties = config;
        for ( var p in this._properties ) {
            if ( this._properties[p].start instanceof framework.Color ) {
                this._properties[p].tempColor;
            }
        }
    },
    {
        getValue: function(x) {
            values = {};
            for ( var p in this._properties ) {
                var prop = this._properties[p];
                if ( prop.start instanceof framework.Color ) {
                    values[p] = framework.Color.blend(prop.start, prop.end, x, prop.tempColor).toCss();
                } else {
                    values[p] = (((prop.end - prop.start) * x) + prop.start) + (prop.unit||0);
                }
            }
            return values;
        }
    }
);

framework.declareClass("framework.CSSStyleSheet", null,
    function() {
        this.el = document.body.appendChild(document.createElement("STYLE"));
        this.sheet = this.el.sheet||document.styleSheets[document.styleSheets.length-1];

        this.cssRules = this.sheet.rules||this.sheet.cssRules;
        this._appendMethod = this.sheet.insertRule?this._appendWithInsert:this._appendWithAdd;
        this._p = /^([^{]+){([^}]+)}\s*$/;
    },
    {
        getRules: function() {
            return this.sheet.rules||this.sheet.cssRules;
        },
        appendRule: function(rule) {
            return this._appendMethod(rule);
        },
        _appendWithInsert: function(rule) {
            var index = this.sheet.insertRule(rule, this.cssRules.length);
            return new framework.CSSStyleDeclaration(this.cssRules[index]);
        },
        _appendWithAdd: function(rule) {
            var m = rule.match(this._p);
            var s = m[1].split(/\s*,\s*/);
            var r = [];
            for ( var i = 0, l = s.length; i < l; i++ ) {
                this.sheet.addRule(s[i], m[2]);
                r.push(this.cssRules[this.cssRules.length-1]);
            }
            return new framework.CSSStyleDeclaration(r);
        }
    }
);


framework.declareClass("framework.CSSStyleDeclaration", null,
    function(rule) {
        if ( framework.isArray(rule) ) {
            this.rules = rule;
        } else {
            this.rules = [rule];
        }
    },
    {
        get: function(property) {
            return this.rules[0].style[property];
        },
        set: function(property, value) {
            for ( var i = 0, l = this.rules.length; i < l; i++ ) {
                this.rules[i].style[property] = value;
            }
        }
    }
);

framework.selection = {};

framework.selection.getPos = function(element) {
    var pos = {s:0,e:0};
    if ( typeof element.selectionStart == "number" ) {
        pos.s = element.selectionStart;
        pos.e = element.selectionEnd;
    } else if ( framework.isIE ) {
        var sel = document.selection.createRange().duplicate(),
        sel1 = sel.duplicate(),
        range = element.createTextRange();
        try {
            range.setEndPoint("EndToStart", sel);
            pos.s = String(range.text).length;
            range.setEndPoint("EndToEnd", sel);
            pos.e = String(range.text).length;
        } catch (e) {
            try {
                sel.moveToElementText(element);
                sel.setEndPoint("EndToStart", sel1);
                pos.s = String(sel.text).length;
                sel.setEndPoint("EndToEnd", sel1);
                pos.e = String(sel.text).length;
            } catch (e1) {
                console.log(e1.message);
            }
        }
    }
    return pos;
}

framework.selection.setPos = function(element, start, end) {
    element.focus();
    if ( typeof element.selectionStart == "number" ) {
        element.setSelectionRange(start, end);
        element.focus();
    } else if ( framework.isIE ) {
        var r = element.createTextRange();
        r.collapse(true);
        r.moveEnd("character", end);
        r.moveStart("character", start);
        r.select();
    }
}

framework.declareClass("org.iit.Cookie", null,
    function() {
        var vars = document.cookie.split(";"), i, l, name, pos;

        this.vars = [];

        for ( i = 0, l = vars.length; i < l; i++ ) {

            if ( (pos = vars[i].indexOf("=")) == -1 ) {
                continue;
            }

            name = vars[i].substr(0, pos);
            name = name.match(/^\s*([a-zA-Z0-9_-]+)\s*$/);

            if ( name.length == 2 && name[1] != '' ) {
                name = name[1];
            } else {
                continue;
            }

            this.vars[name] = vars[i].substr(pos + 1);
        }
    },
    {

        setVariable: function(name, value, expire) {
            if ( name.search ) {
                if ( name.search(/^[a-zA-Z0-9_-]+$/) == -1 )
                    return false;

                var str = name + "=";
                str += escape(value) + ";";
                str += "path=/;";

                if ( typeof(expire) != 'undefined' ) {
                    str += "Expires=" + expire + ";";
                }

                document.cookie = str;
                this.vars[name] = value;
            }
        },

        hasVariable: function(name) {
            return !!this.vars[name];
        },

        getVariable: function(name) {
            if ( this.vars[name] ) {
                return unescape(this.vars[name]);
            } else {
                return null;
            }
        },

        removeVariable: function(name) {
            var time;

            if ( typeof(this.vars[name]) == 'undefined' ) {
                return null;
            }

            delete this.vars[name];

            time = (new Date()).toString();

            document.cookie = name + "=;Expires=" + time + ";";
        }
    }
);

framework.cookie = new org.iit.Cookie();

framework.keys = {
    "BACKSPACE": 8,
    "LEFT": 37,
    "UP": 38,
    "RIGHT": 39,
    "DOWN": 40,
    "DELETE": 46,
    "HOME": 36,
    "END": 35,
    "TAB": 9
}

}