/*
 * Copyright (c) 2008, Culnou. All rights reserved.
 * http://www.culnou.com/license.txt
 * version: 0.1
*/
/**
 * The MUMU object is the single global object used by Mumu Library.  
 * @module mumu
 * @title  MUMU Global
 */

/**
 * Forces the use of the supplied locale where applicable in the library
 * @property locale
 * @type string
 * @static
 * @default undefined
 */

if (typeof MUMU == "undefined" || !MUMU) {
    /**
     * The MUMU global namespace object.  If MUMU is already defined, the
     * existing MUMU object will not be overwritten so that defined
     * namespaces are preserved.
     * @class MUMU
     * @static
     */
    MUMU = {};
}

/**
 * Returns the namespace specified and creates it if it doesn't exist
 * <pre>
 * MUMU.namespace("property.package");
 * MUMU.namespace("MUMU.property.package");
 * </pre>
 * Either of the above would create MUMU.property, then
 * MUMU.property.package
 *
 * Be careful when naming packages. Reserved words may work in some browsers
 * and not others. For instance, the following will fail in Safari:
 * <pre>
 * MUMU.namespace("really.long.nested.namespace");
 * </pre>
 * This fails because "long" is a future reserved word in ECMAScript
 *
 * @method namespace
 * @static
 * @param  {String*} arguments 1-n namespaces to create 
 * @return {Object}  A reference to the last namespace object created
 */
MUMU.namespace = function() {
    var a=arguments, o=null, i, j, d;
    for (i=0; i<a.length; i=i+1) {
        d=a[i].split(".");
        o=MUMU;

        // MUMU is implied, so it is ignored if it is included
        for (j=(d[0] == "MUMU") ? 1 : 0; j<d.length; j=j+1) {
            o[d[j]]=o[d[j]] || {};
            o=o[d[j]];
        }
    }

    return o;
};

MUMU.namespace("MUMU", "MUMU.lib");

/**
 * Provides the language utilites and extensions used by the library
 * @class MUMU.lang
 */
MUMU.lang = MUMU.lang || {
    /**
     * Determines whether or not the provided object is an array.
     * Testing typeof/instanceof/constructor of arrays across frame 
     * boundaries isn't possible in Safari unless you have a reference
     * to the other frame to test against its Array prototype.  To
     * handle this case, we test well-known array properties instead.
     * properties.
     * @method isArray
     * @param {any} o The object being testing
     * @return Boolean
     */

	idCounter: 0,

    isArray: function(o) { 

        if (o) {
           var l = MUMU.lang;
           return l.isNumber(o.length) && l.isFunction(o.splice);
        }
        return false;
    },

    /**
     * Determines whether or not the provided object is a boolean
     * @method isBoolean
     * @param {any} o The object being testing
     * @return Boolean
     */
    isBoolean: function(o) {
        return typeof o === 'boolean';
    },
    
    /**
     * Determines whether or not the provided object is a function
     * @method isFunction
     * @param {any} o The object being testing
     * @return Boolean
     */
    isFunction: function(o) {
        return typeof o === 'function';
    },
        
    /**
     * Determines whether or not the provided object is null
     * @method isNull
     * @param {any} o The object being testing
     * @return Boolean
     */
    isNull: function(o) {
        return o === null;
    },
        
    /**
     * Determines whether or not the provided object is a legal number
     * @method isNumber
     * @param {any} o The object being testing
     * @return Boolean
     */
    isNumber: function(o) {
        return typeof o === 'number' && isFinite(o);
    },
      
    /**
     * Determines whether or not the provided object is of type object
     * or function
     * @method isObject
     * @param {any} o The object being testing
     * @return Boolean
     */  
    isObject: function(o) {
return (o && (typeof o === 'object' || MUMU.lang.isFunction(o))) || false;
    },
        
    /**
     * Determines whether or not the provided object is a string
     * @method isString
     * @param {any} o The object being testing
     * @return Boolean
     */
    isString: function(o) {
        return typeof o === 'string';
    },
        
    /**
     * Determines whether or not the provided object is undefined
     * @method isUndefined
     * @param {any} o The object being testing
     * @return Boolean
     */
    isUndefined: function(o) {
        return typeof o === 'undefined';
    },
    
    /**
     * Determines whether or not the property was added
     * to the object instance.  Returns false if the property is not present
     * in the object, or was inherited from the prototype.
     * This abstraction is provided to enable hasOwnProperty for Safari 1.3.x.
     * There is a discrepancy between MUMU.lang.hasOwnProperty and
     * Object.prototype.hasOwnProperty when the property is a primitive added to
     * both the instance AND prototype with the same value:
     * <pre>
     * var A = function() {};
     * A.prototype.foo = 'foo';
     * var a = new A();
     * a.foo = 'foo';
     * alert(a.hasOwnProperty('foo')); // true
     * alert(MUMU.lang.hasOwnProperty(a, 'foo')); // false when using fallback
     * </pre>
     * @method hasOwnProperty
     * @param {any} o The object being testing
     * @return Boolean
     */
    hasOwnProperty: function(o, prop) {
        if (Object.prototype.hasOwnProperty) {
            return o.hasOwnProperty(prop);
        }
        
        return !MUMU.lang.isUndefined(o[prop]) && 
                o.constructor.prototype[prop] !== o[prop];
    },

    /**
     * IE will not enumerate native functions in a derived object even if the
     * function was overridden.  This is a workaround for specific functions 
     * we care about on the Object prototype. 
     * @property _IEEnumFix
     * @param {Function} r  the object to receive the augmentation
     * @param {Function} s  the object that supplies the properties to augment
     * @static
     * @private
     */
    _IEEnumFix: function(r, s) {
        if (navigator.appName.toUpperCase() == 'MICROSOFT INTERNET EXPLORER') {
            var add=["toString", "valueOf"], i;
            for (i=0;i<add.length;i=i+1) {
                var fname=add[i],f=s[fname];
                if (MUMU.lang.isFunction(f) && f!=Object.prototype[fname]) {
                    r[fname]=f;
                }
            }
        }
    },

    /**
     * Utility to set up the prototype, constructor and superclass properties to
     * support an inheritance strategy that can chain constructors and methods.
     * Static members will not be inherited.
     *
     * @method extend
     * @static
     * @param {Function} subc   the object to modify
     * @param {Function} superc the object to inherit
     * @param {Object} overrides  additional properties/methods to add to the
     *                              subclass prototype.  These will override the
     *                              matching items obtained from the superclass 
     *                              if present.
     */
    extend: function(subc, superc, overrides) {
        if (!superc||!subc) {
            window.alert("MUMU.lang.extend failed");
        }
        var F = function() {};
        F.prototype=superc.prototype;
        subc.prototype=new F();
        subc.prototype.constructor=subc;
        subc.superclass=superc.prototype;
        if (superc.prototype.constructor == Object.prototype.constructor) {
            superc.prototype.constructor=superc;
        }
    
        if (overrides) {
            for (var i in overrides) {
                subc.prototype[i]=overrides[i];
            }

            MUMU.lang._IEEnumFix(subc.prototype, overrides);
        }
    },

    /**
	 * Clones the specified object.
     * @param {Object} obj  source object.
     */
    clone: function(obj) {
		var clone = null;
		if (obj != null && typeof(obj.constructor) == 'function') {
			clone = new obj.constructor();
		    for (var i in obj) {
		    	if (typeof(obj[i]) == 'object') {
		            clone[i] = this.clone(obj[i]);
		        } else {
		            clone[i] = obj[i];
		        }
		    }
		}
	    return clone;
	},

	/**
	 * Copies all the properties of config to obj.
	 * @param {Object} obj The receiver of the properties
	 * @param {Object} config The source of the properties
	 * @param {Object} defaults A different object that will also be applied for default values
	 * @return {Object} returns obj
	 */
	apply : function(o, c, defaults){
		if(defaults){
			// no "this" reference for friendly out of scope calls
			this.apply(o, defaults);
		}
		if(o && c && typeof c == 'object'){
			for(var p in c){
				o[p] = c[p];
			}
		}
		return o;
	},

    // internal
    callback : function(cb, scope, args, delay){
        if(typeof cb == "function"){
            if(delay){
                cb.defer(delay, scope, args || []);
            }else{
                cb.apply(scope, args || []);
            }
        }
    },

    /**
     * Generates unique ids. If the element already has an id, it is unchanged
     * @param {String/HTMLElement/Element} el (optional) The element to generate an id for
     * @param {String} prefix (optional) Id prefix (defaults "ext-gen")
     */
     generateId : function(prefix){
         prefix = prefix || "gen";
         var id = prefix + (++MUMU.lang.idCounter);
         return id;
     }

};
/**
 * Provides the language utilites and extensions used by the library
 * @class MUMU.display
 */
MUMU.display = MUMU.display || {

	/**
	 * Returns true if the specified point (x, y) is
	 * contained in the specified bounds.
	 */
	contains : function(bounds, x, y) {
		return (bounds.x <= x &&
				bounds.x + bounds.width >= x &&
				bounds.y <= y &&
				bounds.y + bounds.height >= y);
	},
	
	/**
	 * Returns true if the two rectangles intersect.
	 */
	intersects : function(first, second) {
		return MUMU.display.contains(first,
			second.x, second.y) ||
			MUMU.display.contains(first,
				second.x+second.width,
				second.y+second.height);
	},
	
	/**
	 * Returns the offset for the specified container as
	 * a point with x and y coordinates.
	 */
	getOffset : function(container) {
		// TODO: Take scrollbar into account
		var offsetLeft = 0;
		var offsetTop = 0;
		if (container.offsetParent)	{
			while (container.offsetParent)	{
				offsetLeft += container.offsetLeft
				offsetTop += container.offsetTop
				container = container.offsetParent;
			}
		}
		return {x: offsetLeft, y: offsetTop};
	},
	
	/**
	 * Converts the specified point (x, y) to the offset
	 * of the specified container and returns a new point
	 * with x and y coordinates.
	 */
	convertPoint : function(dom, x, y) {
		var offset = MUMU.display.getOffset(dom);
		offset.x -= document.body.scrollLeft;
		offset.y -= document.body.scrollTop;
		return {x: x-offset.x, y: y-offset.y};
	},
	
	/**
	 * Creates a new rectangle object using the specified
	 * fields.
	 */
	createBounds : function(x, y, width, height) {
		var bounds = {x: x, y: y};
		bounds.width = width;
		bounds.height = height;
		return bounds;
	},
	
	/**
	 * Creates a new rectangle object using the specified
	 * fields.
	 */
	getAbsolutePoint : function(evt, dom) {
		var absoluteY = evt.clientY + dom.parentNode.scrollTop;
		var absoluteX = evt.clientX + dom.parentNode.scrollLeft;
		var pt = this.convertPoint(dom, absoluteX, absoluteY);
		return pt;
	}
};

/**
 * An alias for <a href="MUMU.lang.html#extend">MUMU.lang.extend</a>
 * @method extend
 * @static
 * @param {Function} subc   the object to modify
 * @param {Function} superc the object to inherit
 * @param {Object} overrides  additional properties/methods to add to the
 *        subclass prototype.  These will override the
 *        matching items obtained from the superclass if present.
 */
MUMU.extend = MUMU.lang.extend;
MUMU.clone = MUMU.lang.clone;

