Skip to content
Permalink
Browse files

Rewrite of the support module. We use a temporary body element in ord…

…er not to have to wait for the document to be ready for boxModel-related support tests.
  • Loading branch information...
jaubourg committed Apr 7, 2011
1 parent f3c6077 commit c1dcad69427b78f3e70628ab1444f42033ee593a
Showing with 142 additions and 151 deletions.
  1. +142 −151 src/support.js
@@ -1,44 +1,58 @@
(function( jQuery ) {

(function() {

jQuery.support = {};

var div = document.createElement("div");

div.style.display = "none";
jQuery.support = (function() {

var div = document.createElement( "div" ),
all,
a,
select,
opt,
input,
support,
fragment,
body,
bodyStyle,
tds,
events,
eventName,
i,
isSupported;

// Preliminary tests
div.innerHTML = " <link/><table></table><a href='/a' style='color:red;float:left;opacity:.55;'>a</a><input type='checkbox'/>";

var all = div.getElementsByTagName("*"),
a = div.getElementsByTagName("a")[0],
select = document.createElement("select"),
opt = select.appendChild( document.createElement("option") ),
input = div.getElementsByTagName("input")[0];
all = div.getElementsByTagName( "*" );
a = div.getElementsByTagName( "a" )[ 0 ];

// Can't get basic test support
if ( !all || !all.length || !a ) {
return;
return {};
}

jQuery.support = {
// First batch of supports tests
select = document.createElement( "select" );
opt = select.appendChild( document.createElement("option") );
input = div.getElementsByTagName( "input" )[ 0 ];

support = {
// IE strips leading whitespace when .innerHTML is used
leadingWhitespace: div.firstChild.nodeType === 3,
leadingWhitespace: ( div.firstChild.nodeType === 3 ),

// Make sure that tbody elements aren't automatically inserted
// IE will insert them into empty tables
tbody: !div.getElementsByTagName("tbody").length,
tbody: !div.getElementsByTagName( "tbody" ).length,

// Make sure that link elements get serialized correctly by innerHTML
// This requires a wrapper element in IE
htmlSerialize: !!div.getElementsByTagName("link").length,
htmlSerialize: !!div.getElementsByTagName( "link" ).length,

// Get the style information from getAttribute
// (IE uses .cssText insted)
// (IE uses .cssText instead)
style: /red/.test( a.getAttribute("style") ),

// Make sure that URLs aren't manipulated
// (IE normalizes it by default)
hrefNormalized: a.getAttribute("href") === "/a",
hrefNormalized: ( a.getAttribute( "href" ) === "/a" ),

// Make sure that element opacity exists
// (IE uses filter instead)
@@ -59,175 +73,152 @@
optSelected: opt.selected,

// Will be defined later
submitBubbles: true,
changeBubbles: true,
deleteExpando: true,
optDisabled: false,
checkClone: false,
noCloneEvent: true,
noCloneChecked: true,
boxModel: null,
inlineBlockNeedsLayout: false,
shrinkWrapBlocks: false,
reliableHiddenOffsets: true,
reliableMarginRight: true
};

// Make sure checked status is properly cloned
input.checked = true;
jQuery.support.noCloneChecked = input.cloneNode( true ).checked;
support.noCloneChecked = input.cloneNode( true ).checked;

// Make sure that the options inside disabled selects aren't marked as disabled
// (WebKit marks them as diabled)
// (WebKit marks them as disabled)
select.disabled = true;
jQuery.support.optDisabled = !opt.disabled;

var _scriptEval = null;
jQuery.support.scriptEval = function() {
if ( _scriptEval === null ) {
var root = document.documentElement,
script = document.createElement("script"),
id = "script" + jQuery.now();

// Make sure that the execution of code works by injecting a script
// tag with appendChild/createTextNode
// (IE doesn't support this, fails, and uses .text instead)
try {
script.appendChild( document.createTextNode( "window." + id + "=1;" ) );
} catch(e) {}

root.insertBefore( script, root.firstChild );

if ( window[ id ] ) {
_scriptEval = true;
delete window[ id ];
} else {
_scriptEval = false;
}

root.removeChild( script );
}

return _scriptEval;
};

This comment has been minimized.

Copy link
@markstory

markstory Aug 11, 2011

Was scriptEval removed intentionally? Its still part of the documented API.

This comment has been minimized.

Copy link
@danheberden

danheberden Aug 11, 2011

Member

There's nothing in the change-logs about it being removed (though _scriptEval was made private in 1.5.1) - docs page: http://api.jquery.com/jQuery.support/

This comment has been minimized.

Copy link
@jaubourg

jaubourg Aug 11, 2011

Author Member

scriptEval was removed since it is no longer required in the lib (globalEval was the only place where it was used in the lib). Doc should be amended.

This comment has been minimized.

Copy link
@markstory

markstory Aug 12, 2011

I can understand that its not used internally anymore. But what about people using it in their applications/sites? Is there a similar way to do this feature test currently? Its a useful test to have I think.

This comment has been minimized.

Copy link
@jaubourg

jaubourg Aug 12, 2011

Author Member

It's actually quite easy to extract the test from an older version of support.js. I understand the inconvenience, but it wouldn't make sense to keep this in when jQuery never actually uses it.

This comment has been minimized.

Copy link
@dmethvin

dmethvin Aug 12, 2011

Member

@jaubourg, true that's the criteria we use for deciding whether something gets into jQuery.support. The problem is that we documented it, so people thought it was fair game to use. Given the amount of code involved and the fact that we had changed it from a property to a method in 1.5.1 anyway, I don't think we should keep it if we don't need it ourselves. With that in mind I've updated the jQuery.support docs to note its disappearance.

Perhaps we shouldn't document most of what's in jQuery.support anyway, especially if we think we might need/want to take it out later. I don't think there's any dictate that we have to document every property there.

This comment has been minimized.

Copy link
@rwaldron

rwaldron Aug 12, 2011

Member

Or, we could maintain the docs with the added caveat that these are property of jQuery internal (not public) and subject to change without advance notice, use at own risk.

This comment has been minimized.

Copy link
@danheberden

danheberden Aug 12, 2011

Member

Moving forward, I think we need to be sure to not remove things that are documented as available without more communication so that docs are updated and changelog reflects the alterations. Moving forward, I think it's fine to mark individual things as deprecated (even when we add them initially) to as to say they aren't to be relied upon in future versions.

This comment has been minimized.

Copy link
@jaubourg

jaubourg Aug 13, 2011

Author Member

I hadn't realized at the time this was documented, sorry for the mess I caused. :(

support.optDisabled = !opt.disabled;

// Test to see if it's possible to delete an expando from an element
// Fails in Internet Explorer
try {
delete div.test;

} catch(e) {
jQuery.support.deleteExpando = false;
} catch( e ) {
support.deleteExpando = false;
}

if ( !div.addEventListener && div.attachEvent && div.fireEvent ) {
div.attachEvent("onclick", function click() {
div.attachEvent( "onclick", function click() {
// Cloning a node shouldn't copy over any
// bound event handlers (IE does this)
jQuery.support.noCloneEvent = false;
div.detachEvent("onclick", click);
});
div.cloneNode(true).fireEvent("onclick");
support.noCloneEvent = false;
div.detachEvent( "onclick", click );
} );

This comment has been minimized.

Copy link
@rwaldron

rwaldron Apr 8, 2011

Member

Extra space between } and )

This comment has been minimized.

Copy link
@jaubourg

jaubourg Apr 8, 2011

Author Member

Which is in accordance with the guidelines given the function is not the only parameter in attachEvent... though everybody seems to make an exception when the function and/or object is the last param.

This comment has been minimized.

Copy link
@rdworth

rdworth Apr 8, 2011

Contributor

The exception is in the guidelines (emphasis added)

"
Exception: Functions, object literals, and array literals go snug to front and back of the parentheses - but ONLY when it's the only argument.

foo(function() { });
foo([   ]);
foo({   });

Exception: Multi-line function/object/array literals go snug at end (and at start ONLY if the only argument).

WRONG: foo( true, { blah: "baz" });
RIGHT: foo( true, {
  blah: "baz"
});

"

The significance here (at the end) is not whether it's the only argument but that it's multi-line.

This comment has been minimized.

Copy link
@jaubourg

jaubourg Apr 8, 2011

Author Member

Damnit, I always miss those damn exceptions: 2594a93

div.cloneNode( true ).fireEvent( "onclick" );
}

div = document.createElement("div");
div.innerHTML = "<input type='radio' name='radiotest' checked='checked'/>";

var fragment = document.createDocumentFragment();
fragment = document.createDocumentFragment();
fragment.appendChild( div.firstChild );

// WebKit doesn't clone checked state correctly in fragments
jQuery.support.checkClone = fragment.cloneNode(true).cloneNode(true).lastChild.checked;
support.checkClone = fragment.cloneNode( true ).cloneNode( true ).lastChild.checked;

// Figure out if the W3C box model works as expected
// document.body must exist before we can do this
jQuery(function() {
var div = document.createElement("div"),
body = document.getElementsByTagName("body")[0];

// Frameset documents with no body should not run this code
if ( !body ) {
return;
}
div.innerHTML = "";

div.style.width = div.style.paddingLeft = "1px";
body.appendChild( div );
jQuery.boxModel = jQuery.support.boxModel = div.offsetWidth === 2;

if ( "zoom" in div.style ) {
// Check if natively block-level elements act like inline-block
// elements when setting their display to 'inline' and giving
// them layout
// (IE < 8 does this)
div.style.display = "inline";
div.style.zoom = 1;
jQuery.support.inlineBlockNeedsLayout = div.offsetWidth === 2;

// Check if elements with layout shrink-wrap their children
// (IE 6 does this)
div.style.display = "";
div.innerHTML = "<div style='width:4px;'></div>";
jQuery.support.shrinkWrapBlocks = div.offsetWidth !== 2;
}
// Figure out if the W3C box model works as expected
div.style.width = div.style.paddingLeft = "1px";

// We use our own, invisible, body
body = document.createElement( "body" );
bodyStyle = {
visibility: "hidden",
width: 0,
height: 0,
border: 0,
margin: 0
};
for ( i in bodyStyle ) {
body.style[ i ] = bodyStyle[ i ];
}
body.appendChild( div );
document.documentElement.appendChild( body );

support.boxModel = div.offsetWidth === 2;

if ( "zoom" in div.style ) {
// Check if natively block-level elements act like inline-block
// elements when setting their display to 'inline' and giving
// them layout
// (IE < 8 does this)
div.style.display = "inline";
div.style.zoom = 1;
support.inlineBlockNeedsLayout = ( div.offsetWidth === 2 );

// Check if elements with layout shrink-wrap their children
// (IE 6 does this)
div.style.display = "";
div.innerHTML = "<div style='width:4px;'></div>";
support.shrinkWrapBlocks = ( div.offsetWidth !== 2 );
}

div.innerHTML = "<table><tr><td style='padding:0;border:0;display:none'></td><td>t</td></tr></table>";
var tds = div.getElementsByTagName("td");

// Check if table cells still have offsetWidth/Height when they are set
// to display:none and there are still other visible table cells in a
// table row; if so, offsetWidth/Height are not reliable for use when
// determining if an element has been hidden directly using
// display:none (it is still safe to use offsets if a parent element is
// hidden; don safety goggles and see bug #4512 for more information).
// (only IE 8 fails this test)
jQuery.support.reliableHiddenOffsets = tds[0].offsetHeight === 0;

tds[0].style.display = "";
tds[1].style.display = "none";

// Check if empty table cells still have offsetWidth/Height
// (IE < 8 fail this test)
jQuery.support.reliableHiddenOffsets = jQuery.support.reliableHiddenOffsets && tds[0].offsetHeight === 0;
div.innerHTML = "";

// Check if div with explicit width and no margin-right incorrectly
// gets computed margin-right based on width of container. For more
// info see bug #3333
// Fails in WebKit before Feb 2011 nightlies
// WebKit Bug 13343 - getComputedStyle returns wrong value for margin-right
if ( document.defaultView && document.defaultView.getComputedStyle ) {
div.style.width = "1px";
div.style.marginRight = "0";
jQuery.support.reliableMarginRight = ( parseInt(document.defaultView.getComputedStyle(div, null).marginRight, 10) || 0 ) === 0;
}
div.innerHTML = "<table><tr><td style='padding:0;border:0;display:none'></td><td>t</td></tr></table>";
tds = div.getElementsByTagName( "td" );

// Check if table cells still have offsetWidth/Height when they are set
// to display:none and there are still other visible table cells in a
// table row; if so, offsetWidth/Height are not reliable for use when
// determining if an element has been hidden directly using
// display:none (it is still safe to use offsets if a parent element is
// hidden; don safety goggles and see bug #4512 for more information).
// (only IE 8 fails this test)
isSupported = ( tds[ 0 ].offsetHeight === 0 );

tds[ 0 ].style.display = "";
tds[ 1 ].style.display = "none";

// Check if empty table cells still have offsetWidth/Height
// (IE < 8 fail this test)
support.reliableHiddenOffsets = isSupported && ( tds[ 0 ].offsetHeight === 0 );
div.innerHTML = "";

// Check if div with explicit width and no margin-right incorrectly
// gets computed margin-right based on width of container. For more
// info see bug #3333
// Fails in WebKit before Feb 2011 nightlies
// WebKit Bug 13343 - getComputedStyle returns wrong value for margin-right
if ( document.defaultView && document.defaultView.getComputedStyle ) {
div.style.width = "1px";
div.style.marginRight = "0";
support.reliableMarginRight =
( parseInt( document.defaultView.getComputedStyle(div).marginRight, 10 ) || 0 ) === 0;

This comment has been minimized.

Copy link
@ShaneMcC

ShaneMcC Apr 12, 2011

This ends up reverting ef0157d which fixed Bug #8635 (Firefox uncaught exception due to missing 2nd parameter in getComputedStyle)

}

body.removeChild( div ).style.display = "none";
div = tds = null;
});
// Remove the body element we added
document.documentElement.removeChild( body );

// Technique from Juriy Zaytsev
// http://thinkweb2.com/projects/prototype/detecting-event-support-without-browser-sniffing/
var eventSupported = function( eventName ) {
var el = document.createElement("div");
eventName = "on" + eventName;

// We only care about the case where non-standard event systems
// are used, namely in IE. Short-circuiting here helps us to
// avoid an eval call (in setAttribute) which can cause CSP
// to go haywire. See: https://developer.mozilla.org/en/Security/CSP
if ( !el.attachEvent ) {
return true;
}

var isSupported = (eventName in el);
if ( !isSupported ) {
el.setAttribute(eventName, "return;");
isSupported = typeof el[eventName] === "function";
// We only care about the case where non-standard event systems
// are used, namely in IE. Short-circuiting here helps us to
// avoid an eval call (in setAttribute) which can cause CSP
// to go haywire. See: https://developer.mozilla.org/en/Security/CSP
if ( div.attachEvent ) {
for( i in {
submit: 1,
change: 1,
focusin: 1

This comment has been minimized.

Copy link
@rwaldron

rwaldron Apr 8, 2011

Member

In the other "hash tables": attrFn, noData, guaranteedUnique and cssNumber, the standard is to set the value to true

This comment has been minimized.

Copy link
@jaubourg

jaubourg Apr 8, 2011

Author Member

Well, if we don't mind 3 more bytes (uglify turning true into !0)... Is the "standard" really that good? I don't mind changing, just asking.

This comment has been minimized.

Copy link
@rwaldron

rwaldron Apr 8, 2011

Member

Perhaps then, we should change them all to 1 and save some bytes? I'm not saying yours is wrong, I'm just saying lets pick the best one, whatever it is, and do that with them all :)

This comment has been minimized.

Copy link
@jaubourg

jaubourg Apr 8, 2011

Author Member

I actually already use 1 in all of ajax maps, though I understand the rational behind having true... it's just that the value is really just a marker of sort.

} ) {
eventName = "on" + i;
isSupported = ( eventName in div );
if ( !isSupported ) {
div.setAttribute( eventName, "return;" );
isSupported = ( typeof div[ eventName ] === "function" );
}
support[ i + "Bubbles" ] = isSupported;
}
return isSupported;
};

jQuery.support.submitBubbles = eventSupported("submit");
jQuery.support.changeBubbles = eventSupported("change");
}

// release memory in IE
div = all = a = null;
body = div = all = a = tds = undefined;

return support;
})();

// Keep track of boxModel
jQuery.boxModel = jQuery.support.boxModel;

})( jQuery );

0 comments on commit c1dcad6

Please sign in to comment.
You can’t perform that action at this time.