define( [

"../core",
"../var/rnotwhite",
"./var/acceptData"

], function( jQuery, rnotwhite, acceptData ) {

function Data() {

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

}

Data.uid = 1;

Data.prototype = {

register: function( owner, initial ) {
        var value = initial || {};

        // If it is a node unlikely to be stringify-ed or looped over
        // use plain assignment
        if ( owner.nodeType ) {
                owner[ this.expando ] = value;

        // Otherwise secure it in a non-enumerable, non-writable property
        // configurability must be true to allow the property to be
        // deleted with the delete operator
        } else {
                Object.defineProperty( owner, this.expando, {
                        value: value,
                        writable: true,
                        configurable: true
                } );
        }
        return owner[ this.expando ];
},
cache: function( owner ) {

        // We can accept data for non-element nodes in modern browsers,
        // but we should not, see #8335.
        // Always return an empty object.
        if ( !acceptData( owner ) ) {
                return {};
        }

        // Check if the owner object already has a cache
        var value = owner[ this.expando ];

        // If not, create one
        if ( !value ) {
                value = {};

                // We can accept data for non-element nodes in modern browsers,
                // but we should not, see #8335.
                // Always return an empty object.
                if ( acceptData( owner ) ) {

                        // If it is a node unlikely to be stringify-ed or looped over
                        // use plain assignment
                        if ( owner.nodeType ) {
                                owner[ this.expando ] = value;

                        // Otherwise secure it in a non-enumerable property
                        // configurable must be true to allow the property to be
                        // deleted when data is removed
                        } else {
                                Object.defineProperty( owner, this.expando, {
                                        value: value,
                                        configurable: true
                                } );
                        }
                }
        }

        return value;
},
set: function( owner, data, value ) {
        var prop,
                cache = this.cache( owner );

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

        // Handle: [ owner, { properties } ] args
        } else {

                // Copy the properties one-by-one to the cache object
                for ( prop in data ) {
                        cache[ prop ] = data[ prop ];
                }
        }
        return cache;
},
get: function( owner, key ) {
        return key === undefined ?
                this.cache( owner ) :
                owner[ this.expando ] && owner[ this.expando ][ 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,
                cache = owner[ this.expando ];

        if ( cache === undefined ) {
                return;
        }

        if ( key === undefined ) {
                this.register( owner );

        } 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 ] ];
                }
        }

        // Remove the expando if there's no more data
        if ( key === undefined || jQuery.isEmptyObject( cache ) ) {

                // Support: Chrome <= 35-45+
                // Webkit & Blink performance suffers when deleting properties
                // from DOM nodes, so set to undefined instead
                // https://code.google.com/p/chromium/issues/detail?id=378607
                if ( owner.nodeType ) {
                        owner[ this.expando ] = undefined;
                } else {
                        delete owner[ this.expando ];
                }
        }
},
hasData: function( owner ) {
        var cache = owner[ this.expando ];
        return cache !== undefined && !jQuery.isEmptyObject( cache );
}

};

return Data; } );