define( [

	"../core",

	"../core/camelCase",

	"../var/rnothtmlwhite",

	"./var/acceptData"

], function( jQuery, camelCase, rnothtmlwhite, acceptData ) {



"use strict";



function Data() {

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

}



Data.uid = 1;



Data.prototype = {



	cache: function( owner ) {



		// 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

		// Always use camelCase key (gh-2257)

		if ( typeof data === "string" ) {

			cache[ camelCase( data ) ] = value;



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

		} else {



			// Copy the properties one-by-one to the cache object

			for ( prop in data ) {

				cache[ camelCase( prop ) ] = data[ prop ];

			}

		}

		return cache;

	},

	get: function( owner, key ) {

		return key === undefined ?

			this.cache( owner ) :



			// Always use camelCase key (gh-2257)

			owner[ this.expando ] && owner[ this.expando ][ camelCase( key ) ];

	},

	access: function( owner, key, value ) {



		// 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 ) ) {



			return this.get( owner, 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,

			cache = owner[ this.expando ];



		if ( cache === undefined ) {

			return;

		}



		if ( key !== undefined ) {



			// Support array or space separated string of keys

			if ( Array.isArray( key ) ) {



				// If key is an array of keys...

				// We always set camelCase keys, so remove that.

				key = key.map( camelCase );

			} else {

				key = camelCase( key );



				// If a key with the spaces exists, use it.

				// Otherwise, create an array by matching non-whitespace

				key = key in cache ?

					[ key ] :

					( key.match( rnothtmlwhite ) || [] );

			}



			i = key.length;



			while ( i-- ) {

				delete cache[ key[ 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://bugs.chromium.org/p/chromium/issues/detail?id=378607 (bug restricted)

			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;

} );

