define([

"../core",
"../var/rnotwhite",
"./accepts"

], function( jQuery, rnotwhite ) {

function Data() {

// Support: Android<4,
// Old WebKit does not have Object.preventExtensions/freeze method,
// return new empty object instead with no [[set]] accessor
Object.defineProperty( this.cache = {}, 0, {
        get: function() {
                return {};
        }
});

this.expando = jQuery.expando + Data.uid++;

}

Data.uid = 1; Data.accepts = jQuery.acceptData;

Data.prototype = {

key: function( owner ) {
        // We can accept data for non-element nodes in modern browsers,
        // but we should not, see #8335.
        // Always return the key for a frozen object.
        if ( !Data.accepts( owner ) ) {
                return 0;
        }

        var descriptor = {},
                // Check if the owner object already has a cache key
                unlock = owner[ this.expando ];

        // If not, create one
        if ( !unlock ) {
                unlock = Data.uid++;

                // Secure it in a non-enumerable, non-writable property
                try {
                        descriptor[ this.expando ] = { value: unlock };
                        Object.defineProperties( owner, descriptor );

                // Support: Android<4
                // Fallback to a less secure definition
                } catch ( e ) {
                        descriptor[ this.expando ] = unlock;
                        jQuery.extend( owner, descriptor );
                }
        }

        // Ensure the cache object
        if ( !this.cache[ unlock ] ) {
                this.cache[ unlock ] = {};
        }

        return unlock;
},
set: function( owner, data, value ) {
        var prop,
                // There may be an unlock assigned to this node,
                // if there is no entry for this "owner", create one inline
                // and set the unlock as though an owner entry had always existed
                unlock = this.key( owner ),
                cache = this.cache[ unlock ];

        // Handle: [ owner, key, value ] args
        if ( typeof data === "string" ) {
                cache[ data ] = value;

        // Handle: [ owner, { properties } ] args
        } else {
                // Fresh assignments by object are shallow copied
                if ( jQuery.isEmptyObject( cache ) ) {
                        jQuery.extend( this.cache[ unlock ], data );
                // Otherwise, copy the properties one-by-one to the cache object
                } else {
                        for ( prop in data ) {
                                cache[ prop ] = data[ prop ];
                        }
                }
        }
        return cache;
},
get: function( owner, key ) {
        // Either a valid cache is found, or will be created.
        // New caches will be created and the unlock returned,
        // allowing direct access to the newly created
        // empty data object. A valid owner object must be provided.
        var cache = this.cache[ this.key( owner ) ];

        return key === undefined ?
                cache : cache[ key ];
},
access: function( owner, key, value ) {
        var stored;
        // In cases where either:
        //
        //   1. No key was specified
        //   2. A string key was specified, but no value provided
        //
        // Take the "read" path and allow the get method to determine
        // which value to return, respectively either:
        //
        //   1. The entire cache object
        //   2. The data stored at the key
        //
        if ( key === undefined ||
                        ((key && typeof key === "string") && value === undefined) ) {

                stored = this.get( owner, key );

                return stored !== undefined ?
                        stored : this.get( owner, jQuery.camelCase(key) );
        }

        // [*]When the key is not a string, or both a key and value
        // are specified, set or extend (existing objects) with either:
        //
        //   1. An object of properties
        //   2. A key and value
        //
        this.set( owner, key, value );

        // Since the "set" path can have two possible entry points
        // return the expected data based on which path was taken[*]
        return value !== undefined ? value : key;
},
remove: function( owner, key ) {
        var i, name, camel,
                unlock = this.key( owner ),
                cache = this.cache[ unlock ];

        if ( key === undefined ) {
                this.cache[ unlock ] = {};

        } else {
                // Support array or space separated string of keys
                if ( jQuery.isArray( key ) ) {
                        // If "name" is an array of keys...
                        // When data is initially created, via ("key", "val") signature,
                        // keys will be converted to camelCase.
                        // Since there is no way to tell _how_ a key was added, remove
                        // both plain key and camelCase key. #12786
                        // This will only penalize the array argument path.
                        name = key.concat( key.map( jQuery.camelCase ) );
                } else {
                        camel = jQuery.camelCase( key );
                        // Try the string as a key before any manipulation
                        if ( key in cache ) {
                                name = [ key, camel ];
                        } else {
                                // If a key with the spaces exists, use it.
                                // Otherwise, create an array by matching non-whitespace
                                name = camel;
                                name = name in cache ?
                                        [ name ] : ( name.match( rnotwhite ) || [] );
                        }
                }

                i = name.length;
                while ( i-- ) {
                        delete cache[ name[ i ] ];
                }
        }
},
hasData: function( owner ) {
        return !jQuery.isEmptyObject(
                this.cache[ owner[ this.expando ] ] || {}
        );
},
discard: function( owner ) {
        if ( owner[ this.expando ] ) {
                delete this.cache[ owner[ this.expando ] ];
        }
}

};

return Data; });