Commit 7f94a5cc authored by Rick Waldron's avatar Rick Waldron

2.0: Rewrite data.js (Incl. event, manipulation, tests)

parent ed0e2d1e
This diff is collapsed.
...@@ -24,7 +24,7 @@ jQuery.event = { ...@@ -24,7 +24,7 @@ jQuery.event = {
var handleObjIn, eventHandle, tmp, var handleObjIn, eventHandle, tmp,
events, t, handleObj, events, t, handleObj,
special, handlers, type, namespaces, origType, special, handlers, type, namespaces, origType,
elemData = jQuery._data( elem ); elemData = data_priv.get( elem );
// Don't attach events to noData or text/comment nodes (but allow plain objects) // Don't attach events to noData or text/comment nodes (but allow plain objects)
if ( !elemData ) { if ( !elemData ) {
...@@ -130,7 +130,7 @@ jQuery.event = { ...@@ -130,7 +130,7 @@ jQuery.event = {
var j, origCount, tmp, var j, origCount, tmp,
events, t, handleObj, events, t, handleObj,
special, handlers, type, namespaces, origType, special, handlers, type, namespaces, origType,
elemData = jQuery.hasData( elem ) && jQuery._data( elem ); elemData = data_priv.hasData( elem ) && data_priv.get( elem );
if ( !elemData || !(events = elemData.events) ) { if ( !elemData || !(events = elemData.events) ) {
return; return;
......
...@@ -328,7 +328,7 @@ jQuery.fn.extend({ ...@@ -328,7 +328,7 @@ jQuery.fn.extend({
for ( i = 0; i < hasScripts; i++ ) { for ( i = 0; i < hasScripts; i++ ) {
node = scripts[ i ]; node = scripts[ i ];
if ( rscriptType.test( node.type || "" ) && if ( rscriptType.test( node.type || "" ) &&
!jQuery._data( node, "globalEval" ) && jQuery.contains( doc, node ) ) { !data_priv.access( node, "globalEval" ) && jQuery.contains( doc, node ) ) {
if ( node.src ) { if ( node.src ) {
// Hope ajax is available... // Hope ajax is available...
...@@ -511,11 +511,9 @@ jQuery.extend({ ...@@ -511,11 +511,9 @@ jQuery.extend({
}, },
cleanData: function( elems, /* internal */ acceptData ) { cleanData: function( elems, /* internal */ acceptData ) {
var id, data, elem, type, var data, elem, type,
l = elems.length, l = elems.length,
i = 0, i = 0,
internalKey = jQuery.expando,
cache = jQuery.cache,
special = jQuery.event.special; special = jQuery.event.special;
for ( ; i < l; i++ ) { for ( ; i < l; i++ ) {
...@@ -523,8 +521,7 @@ jQuery.extend({ ...@@ -523,8 +521,7 @@ jQuery.extend({
if ( acceptData || jQuery.acceptData( elem ) ) { if ( acceptData || jQuery.acceptData( elem ) ) {
id = elem[ internalKey ]; data = data_priv.access( elem );
data = id && cache[ id ];
if ( data ) { if ( data ) {
for ( type in data.events ) { for ( type in data.events ) {
...@@ -536,14 +533,10 @@ jQuery.extend({ ...@@ -536,14 +533,10 @@ jQuery.extend({
jQuery.removeEvent( elem, type, data.handle ); jQuery.removeEvent( elem, type, data.handle );
} }
} }
// Remove cache only if it was not already removed by jQuery.event.remove
if ( cache[ id ] ) {
delete cache[ id ];
delete elem[ internalKey ];
}
} }
} }
// Discard any remaining `private` and `user` data
data_discard( elem );
} }
} }
}); });
...@@ -576,38 +569,49 @@ function setGlobalEval( elems, refElements ) { ...@@ -576,38 +569,49 @@ function setGlobalEval( elems, refElements ) {
i = 0; i = 0;
for ( ; i < l; i++ ) { for ( ; i < l; i++ ) {
jQuery._data( elems[ i ], "globalEval", !refElements || jQuery._data( refElements[ i ], "globalEval" ) ); data_priv.set(
elems[ i ], "globalEval", !refElements || data_priv.get( refElements[ i ], "globalEval" )
);
} }
} }
function cloneCopyEvent( src, dest ) { function cloneCopyEvent( src, dest ) {
var i, l, type, pdataOld, pdataCur, udataOld, udataCur, events;
if ( dest.nodeType !== 1 || !jQuery.hasData( src ) ) { if ( dest.nodeType !== 1 ) {
return; return;
} }
var i, l, type, // 1. Copy private data: events, handlers, etc.
oldData = jQuery._data( src ), if ( data_priv.hasData( src ) ) {
curData = jQuery._data( dest, oldData ), pdataOld = data_priv.access( src );
events = oldData.events; pdataCur = jQuery.extend( {}, pdataOld );
events = pdataOld.events;
data_priv.set( dest, pdataCur );
if ( events ) { if ( events ) {
delete curData.handle; delete pdataCur.handle;
curData.events = {}; pdataCur.events = {};
for ( type in events ) { for ( type in events ) {
for ( i = 0, l = events[ type ].length; i < l; i++ ) { for ( i = 0, l = events[ type ].length; i < l; i++ ) {
jQuery.event.add( dest, type, events[ type ][ i ] ); jQuery.event.add( dest, type, events[ type ][ i ] );
}
} }
} }
} }
// make the cloned public data object a copy from the original // 2. Copy user data
if ( curData.data ) { if ( data_user.hasData( src ) ) {
curData.data = jQuery.extend( {}, curData.data ); udataOld = data_user.access( src );
udataCur = jQuery.extend( {}, udataOld );
data_user.set( dest, udataCur );
} }
} }
function getAll( context, tag ) { function getAll( context, tag ) {
var ret = context.getElementsByTagName ? context.getElementsByTagName( tag || "*" ) : var ret = context.getElementsByTagName ? context.getElementsByTagName( tag || "*" ) :
context.querySelectorAll ? context.querySelectorAll( tag || "*" ) : context.querySelectorAll ? context.querySelectorAll( tag || "*" ) :
......
<!doctype html>
<html>
<script src="/dist/jquery.js"></script>
<script>
function report( event ) {
var payload = {
event: event.type
};
return top.postMessage( JSON.stringify(payload), "*" );
}
jQuery( window ).on( "beforeunload", function( event ) {
report( event );
}).on( "load", function( event ) {
setTimeout(function() {
window.location.reload();
}, 50);
});
</script>
</html>
...@@ -200,47 +200,53 @@ var Globals = (function() { ...@@ -200,47 +200,53 @@ var Globals = (function() {
*/ */
QUnit.expectJqData = function( elems, key ) { QUnit.expectJqData = function( elems, key ) {
var i, elem, expando; var i, elem, expando;
QUnit.current_testEnvironment.checkJqData = true;
if ( elems.jquery && elems.toArray ) { // As of jQuery 2.0, there will be no "cache"-data is
elems = elems.toArray(); // stored and managed completely below the API surface
} if ( jQuery.cache ) {
if ( !jQuery.isArray( elems ) ) { QUnit.current_testEnvironment.checkJqData = true;
elems = [ elems ];
}
for ( i = 0; i < elems.length; i++ ) { if ( elems.jquery && elems.toArray ) {
elem = elems[i]; elems = elems.toArray();
}
// jQuery.data only stores data for nodes in jQuery.cache, if ( !jQuery.isArray( elems ) ) {
// for other data targets the data is stored in the object itself, elems = [ elems ];
// in that case we can't test that target for memory leaks.
// But we don't have to since in that case the data will/must will
// be available as long as the object is not garbage collected by
// the js engine, and when it is, the data will be removed with it.
if ( !elem.nodeType ) {
// Fixes false positives for dataTests(window), dataTests({}).
continue;
} }
expando = elem[ jQuery.expando ]; for ( i = 0; i < elems.length; i++ ) {
elem = elems[i];
if ( expando === undefined ) {
// In this case the element exists fine, but // jQuery.data only stores data for nodes in jQuery.cache,
// jQuery.data (or internal data) was never (in)directly // for other data targets the data is stored in the object itself,
// called. // in that case we can't test that target for memory leaks.
// Since this method was called it means some data was // But we don't have to since in that case the data will/must will
// expected to be found, but since there is nothing, fail early // be available as long as the object is not garbage collected by
// (instead of in teardown). // the js engine, and when it is, the data will be removed with it.
notStrictEqual( expando, undefined, "Target for expectJqData must have an expando, for else there can be no data to expect." ); if ( !elem.nodeType ) {
} else { // Fixes false positives for dataTests(window), dataTests({}).
if ( expectedDataKeys[expando] ) { continue;
expectedDataKeys[expando].push( key ); }
expando = elem[ jQuery.expando ];
if ( expando === undefined ) {
// In this case the element exists fine, but
// jQuery.data (or internal data) was never (in)directly
// called.
// Since this method was called it means some data was
// expected to be found, but since there is nothing, fail early
// (instead of in teardown).
notStrictEqual( expando, undefined, "Target for expectJqData must have an expando, for else there can be no data to expect." );
} else { } else {
expectedDataKeys[expando] = [ key ]; if ( expectedDataKeys[expando] ) {
expectedDataKeys[expando].push( key );
} else {
expectedDataKeys[expando] = [ key ];
}
} }
} }
} }
}; };
QUnit.config.urlConfig.push( { QUnit.config.urlConfig.push( {
id: "jqdata", id: "jqdata",
...@@ -334,7 +340,7 @@ var Globals = (function() { ...@@ -334,7 +340,7 @@ var Globals = (function() {
} else { } else {
delete jQuery.ajaxSettings; delete jQuery.ajaxSettings;
} }
// Cleanup globals // Cleanup globals
Globals.cleanup(); Globals.cleanup();
......
...@@ -6,6 +6,33 @@ test("expando", function(){ ...@@ -6,6 +6,33 @@ test("expando", function(){
equal(jQuery.expando !== undefined, true, "jQuery is exposing the expando"); equal(jQuery.expando !== undefined, true, "jQuery is exposing the expando");
}); });
test( "jQuery.data & removeData, expected returns", function() {
expect(2);
equal(
jQuery.data( document.body, "hello", "world" ), "world",
"jQuery.data( elem, key, value ) returns value"
);
equal(
jQuery.removeData( document.body, "hello" ), undefined,
"jQuery.removeData( elem, key, value ) returns undefined"
);
});
test( "jQuery._data & _removeData, expected returns", function() {
expect(2);
equal(
jQuery._data( document.body, "hello", "world" ), "world",
"jQuery.data( elem, key, value ) returns value"
);
equal(
jQuery._removeData( document.body, "hello" ), undefined,
"jQuery.removeData( elem, key, value ) returns undefined"
);
});
function dataTests (elem) { function dataTests (elem) {
var oldCacheLength, dataObj, internalDataObj, expected, actual; var oldCacheLength, dataObj, internalDataObj, expected, actual;
...@@ -106,6 +133,10 @@ test("Data is not being set on comment and text nodes", function() { ...@@ -106,6 +133,10 @@ test("Data is not being set on comment and text nodes", function() {
ok( !jQuery.hasData( jQuery("<span>text</span>").contents().data("foo", 0) ) ); ok( !jQuery.hasData( jQuery("<span>text</span>").contents().data("foo", 0) ) );
}); });
/*
// Since the new data system does not rely on exandos, limiting the type of
// nodes that can have data is no longer necessary. jQuery.acceptData is now irrelevant
// and should be removed from the library.
test("jQuery.acceptData", function() { test("jQuery.acceptData", function() {
expect(9); expect(9);
...@@ -127,7 +158,7 @@ test("jQuery.acceptData", function() { ...@@ -127,7 +158,7 @@ test("jQuery.acceptData", function() {
ok( !jQuery.acceptData( document.createComment("") ), "comment" ); ok( !jQuery.acceptData( document.createComment("") ), "comment" );
ok( !jQuery.acceptData( document.createTextNode("") ), "text" ); ok( !jQuery.acceptData( document.createTextNode("") ), "text" );
}); });
*/
test(".data()", function() { test(".data()", function() {
expect(5); expect(5);
...@@ -424,7 +455,7 @@ if (window.JSON && window.JSON.stringify) { ...@@ -424,7 +455,7 @@ if (window.JSON && window.JSON.stringify) {
} }
test("jQuery.data should follow html5 specification regarding camel casing", function() { test("jQuery.data should follow html5 specification regarding camel casing", function() {
expect(10); expect(12);
var div = jQuery("<div id='myObject' data-w-t-f='ftw' data-big-a-little-a='bouncing-b' data-foo='a' data-foo-bar='b' data-foo-bar-baz='c'></div>") var div = jQuery("<div id='myObject' data-w-t-f='ftw' data-big-a-little-a='bouncing-b' data-foo='a' data-foo-bar='b' data-foo-bar-baz='c'></div>")
.prependTo("body"); .prependTo("body");
...@@ -445,6 +476,9 @@ test("jQuery.data should follow html5 specification regarding camel casing", fun ...@@ -445,6 +476,9 @@ test("jQuery.data should follow html5 specification regarding camel casing", fun
equal( div.data("fooBar"), "d", "Verify updated data-* key" ); equal( div.data("fooBar"), "d", "Verify updated data-* key" );
equal( div.data("foo-bar"), "d", "Verify updated data-* key" ); equal( div.data("foo-bar"), "d", "Verify updated data-* key" );
equal( div.data("fooBar"), "d", "Verify updated data-* key (fooBar)" );
equal( div.data("foo-bar"), "d", "Verify updated data-* key (foo-bar)" );
div.remove(); div.remove();
}); });
......
...@@ -1371,75 +1371,20 @@ test("Submit event can be stopped (#11049)", function() { ...@@ -1371,75 +1371,20 @@ test("Submit event can be stopped (#11049)", function() {
// Test beforeunload event only if it supported (i.e. not Opera) // Test beforeunload event only if it supported (i.e. not Opera)
if ( window.onbeforeunload === null ) { if ( window.onbeforeunload === null ) {
asyncTest("on(beforeunload)", 4, function() { asyncTest("on(beforeunload)", 1, function() {
var win, var iframe = jQuery(jQuery.parseHTML("<iframe src='data/onbeforeunload.html'><iframe>"));
forIE6 = 0,
fired = false,
iframe = jQuery("<iframe src='data/iframe.html' />");
iframe.appendTo("#qunit-fixture").one( "load", function() {
win = this.contentWindow || this.contentDocument;
jQuery( win ).on( "beforeunload", function() {
fired = true;
ok( true, "beforeunload event is fired" );
});
strictEqual( win.onbeforeunload, null, "onbeforeunload property on window object still equals null" );
// In old Safari beforeunload event will not fire on iframes
jQuery( win ).on( "unload", function() {
if ( !fired ) {
ok( true, "This is suppose to be true only in old Safari" );
checker();
}
});
jQuery( win ).on( "beforeunload", function() {
// On iframe in IE6 beforeunload event will not fire if event is binded through window object,
// nevertheless, test should continue
window.setTimeout(function() {
if ( !forIE6 ) {
checker();
}
});
});
win.onbeforeunload = function() {
if ( !forIE6 ) {
forIE6++;
checker();
}
};
function checker() { window.onmessage = function( event ) {
ok( true, "window.onbeforeunload handler is called" ); var payload = JSON.parse( event.data );
iframe = jQuery("<iframe src='data/iframe.html' />");
iframe.appendTo("#qunit-fixture").one( "load", function() { ok( payload.event, "beforeunload", "beforeunload event" );
win = iframe[ 0 ].contentWindow || iframe[ 0 ].contentDocument;
jQuery( win ).on( "beforeunload", function() { iframe.remove();
strictEqual( win.onbeforeunload, null, "Event handler is fired, even when onbeforeunload property on window is nulled" ); window.onmessage = null;
start();
start(); };
});
jQuery( win ).on( "unload", function() {
if ( !fired ) {
jQuery( win ).trigger("beforeunload");
}
});
win.onbeforeunload = null;
win.location.reload();
});
}
win.location.reload(); iframe.appendTo("#qunit-fixture");
});
}); });
} }
...@@ -2567,7 +2512,7 @@ test( "Namespace preserved when passed an Event (#12739)", function() { ...@@ -2567,7 +2512,7 @@ test( "Namespace preserved when passed an Event (#12739)", function() {
e.handled = true; e.handled = true;
equal( e.namespace, "bar", "namespace is bar" ); equal( e.namespace, "bar", "namespace is bar" );
jQuery( e.target ).find("div").each(function() { jQuery( e.target ).find("div").each(function() {
jQuery( this ).triggerHandler( e ); jQuery( this ).triggerHandler( e );
}); });
} }
}) })
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment