var RORTICUS = {
    /**
     * Cross browser event handling
     *
     * @param element {HTMLElement} The element to attach the event to
     * @param event {string} The name of the event (without an 'on'), i.e., click
     * @param callback {function} The function to call when the event is fired
    */
    addEvent : function (element, event, callback) {
        if('addEventListener' in element)
        {
            element.addEventListener(event, callback, false);
        }
        else
        {
            element.attachEvent('on' + event, callback);
        }
    },

    /**
     * Cross browser event removal
     *
     * @param element {HTMLElement} The element to detach from
     * @param event {string} The name of the event (without the 'on')
     * @param callback {function} The callback function to remove
    */
    removeEvent : function (element, event, callback) {
        if('addEventListener' in element)
        {
            element.removeEventListener(event, callback, false);
        }
        else
        {
            element.detachEvent('on' + event, callback);
        }
    },

    /**
     * Return the bounding rectangle of an element in document space
     *
     * @param element {HTMLElement} The element to get the coordinates for
     * @returns {object} An object containing the coordinates of the element
    */
    elementFrame : function (element) {
        var left = function (e) { return (e)?(((e.offsetLeft)?e.offsetLeft:0) + arguments.callee(e.offsetParent)):0; }(element);
        var top = function (e) { return (e)?(((e.offsetTop)?e.offsetTop:0) + arguments.callee(e.offsetParent)):0; }(element);

         return {
            left: left,
            top: top,
            right: left + element.offsetWidth,
            bottom: top + element.offsetHeight
        };
    },

    /**
     * Apply a CSS-like set of styles to an element
     *
     * @param element {HTMLElement} The target element
     * @param style {string} The CSS-like style, i.e., "background-color: black;"
    */
    stylize : function (element, style) {
        // split by CSS key/value pairs, separated by ;
        var parts = style.split(";");

        for(var i = 0; i < parts.length; i++)
        {
            // split by CSS key/value, separated by :
            var colon = parts[i].indexOf(':');
            if(colon > 0)
            {
                var key = parts[i].substr(0, colon);
                var value = parts[i].substr(colon + 1).replace(/^\s*(.*?)\s*$/, "$1");

                // convert the key
                var cssKey = "";
                var validChars = "abcdefghijklmnopqrstuvwxyz0123456789-";
                var upperCase = false;

                for(var j = 0; j < key.length; j++)
                {
                    var c = key.substr(j, 1);

                    if(validChars.indexOf(c.toLowerCase()) >= 0)
                    {
                        if(c == "-")
                        {
                            upperCase = true;
                        }
                        else
                        {
                            if(upperCase)
                            {
                                c = c.toUpperCase();
                                upperCase = false;
                            }

                            cssKey += c;
                        }
                    }
                }

                // set the property
                try
                {
                    element.style[cssKey] = value;
                }
                catch (e) { }
            }
        }
    },

    /**
     * Helper function to create an initialize a DOM element
    */
    initElement : function (tagName, style, initFunction) {
        var e = document.createElement(tagName);
        RORTICUS.stylize(e, style);

        initFunction.call(e);

        return e;
    },

    attachDrag : function (element, dragEvent) {
        var f = function (event) {
            if(!event)
            {
                event = window.event;
            }

            event.cancelBubble = true;

            // get the current mouse cursor position
            var cursor = {
                x: event.clientX + document.body.scrollLeft,
                y: event.clientY + document.body.scrollTop
            };

            var bounds = CROBBIES.elementFrame(element);

            var pos = {
                x: Math.min(Math.max(0, cursor.x - bounds.left), bounds.right - bounds.left),
                y: Math.min(Math.max(0, cursor.y - bounds.top), bounds.bottom - bounds.top)
            };

            dragEvent(pos, bounds);

            if ('preventDefault' in event)
            {
                event.preventDefault();
            }
            else
            {
                event.returnValue = false;
            }

            return false;
        };

        RORTICUS.addEvent(element, 'mousedown', function (event) {
            RORTICUS.addEvent(document, 'mousemove', f);
            RORTICUS.addEvent(document, 'mouseup', function (event) {
                if(!event)
                {
                    event = window.event;
                }

                event.cancelBubble = true;

                RORTICUS.removeEvent(document, 'mousemove', f);
                RORTICUS.removeEvent(document, 'mouseup', arguments.callee);

                if ('preventDefault' in event)
                {
                    event.preventDefault();
                }
                else
                {
                    event.returnValue = false;
                }
                return false;
            });

            return f(event);
        });
    },

    ajax: function (config) {
        if('XMLHttpRequest' in window)
        {
            // create one!
            var xml = new XMLHttpRequest();
        }
        else
        {
            var xml = new ActiveXObject("Microsoft.XMLHTTP");
        }

        var url = ('url' in config)?config.url:"";
        var method = ('method' in config)?config.method:"POST";
        var params = ('params' in config)?config.params:{};

        // assemble something that looks like a get string from this stuffs
        var parts = [];
        for(var key in params)
        {
            parts.push(key + "=" + encodeURIComponent(RORTICUS.jsonify(params[key])));
        }

        var data = null;

        if(method == "GET")
        {
            if(url.indexOf("?") >= 0)
            {
                url += "&" + parts.join("&");
            }
            else
            {
                url += "?" + parts.join("&");
            }
        }
        else if(method == "POST")
        {
            data = parts.join("&");
        }

        xml.open(method, url);
        xml.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
        xml.send(data);

        xml.onreadystatechange = function () {
            if(xml.readyState == 4)
            {
                if('success' in config)
                {
                    config.success(xml.responseText, xml);
                }
            }
        };
    },

    jsonify: function (obj) {
        if(obj instanceof Array)
        {
            var str = "[";
            for(var i = 0; i < obj.length; i++)
            {
                str += RORTICUS.jsonify(obj[i]);

                if(i < obj.length - 1)
                {
                    str += ",";
                }
            }
            str += "]";

            return str;
        }
        else if (obj instanceof Object)
        {
            var str = "{";
            var parts = [];

            for(var key in obj)
            {
                parts.push("\"" + key + "\":" + RORTICUS.jsonify(obj[key]));
            }
            str += parts.join(",");
            str += "}";

            return str;
        }
        else if (obj instanceof String)
        {
            return "\"" + obj + "\"";
        }

        return obj;
    }
};