From d7d908b05a442745c1a90728805b74db0a7921cf Mon Sep 17 00:00:00 2001 From: Brandon Aaron Date: Sun, 22 Mar 2009 23:25:03 +0000 Subject: [PATCH] decoupling styles retrieval from the attr method --- Makefile | 1 + build.xml | 1 + speed/benchmarker.css | 65 + speed/benchmarker.js | 181 ++ speed/index.html | 72 + speed/jquery-basis.js | 3549 +++++++++++++++++++++++++++++++++++++++ src/attributes.js | 179 +- src/css.js | 205 +++ src/fx.js | 8 +- src/support.js | 5 - test/index.html | 1 + test/unit/attributes.js | 83 - test/unit/css.js | 82 + test/unit/fx.js | 2 +- 14 files changed, 4177 insertions(+), 257 deletions(-) create mode 100755 speed/benchmarker.css create mode 100755 speed/benchmarker.js create mode 100755 speed/index.html create mode 100644 speed/jquery-basis.js create mode 100644 src/css.js create mode 100644 test/unit/css.js diff --git a/Makefile b/Makefile index 8b873e78..c2361205 100644 --- a/Makefile +++ b/Makefile @@ -16,6 +16,7 @@ BASE_FILES = ${SRC_DIR}/core.js\ ${SRC_DIR}/manipulation.js\ ${SRC_DIR}/event.js\ ${SRC_DIR}/support.js\ + ${SRC_DIR}/css.js\ ${SRC_DIR}/ajax.js\ ${SRC_DIR}/fx.js\ ${SRC_DIR}/offset.js\ diff --git a/build.xml b/build.xml index ae8b8df3..095c52eb 100644 --- a/build.xml +++ b/build.xml @@ -60,6 +60,7 @@ + diff --git a/speed/benchmarker.css b/speed/benchmarker.css new file mode 100755 index 00000000..6fc8154c --- /dev/null +++ b/speed/benchmarker.css @@ -0,0 +1,65 @@ + + .dialog { + margin-bottom: 1em; + } + a.expand { + background: #e3e3e3; + } + + div#time-test { + font-family: Arial, Helvetica, sans-serif; + font-size: 62.5%; + } + + td.test button { + float: right; + } + + table { + border: 1px solid #000; + } + + table td, table th { + border: 1px solid #000; + padding: 10px; + } + + td.winner { + background-color: #cfc; + } + + td.tie { + background-color: #ffc; + } + + td.fail { + background-color: #f99; + font-weight: bold; + text-align: center; + } + + tfoot td { + text-align: center; + } + + #time-test { + margin: 1em 0; + padding: .5em; + background: #e3e3e3; + } + #time-taken { + font-weight: bold; + } + + span.wins { + color: #330; + } + + span.fails { + color: #900; + } + + div.buttons { + margin-top: 10px; + margin-bottom: 10px; + } \ No newline at end of file diff --git a/speed/benchmarker.js b/speed/benchmarker.js new file mode 100755 index 00000000..bfcc16ed --- /dev/null +++ b/speed/benchmarker.js @@ -0,0 +1,181 @@ + jQuery.benchmarker.tests = [ + // Selectors from: + // http://ejohn.org/blog/selectors-that-people-actually-use/ + /* + // For Amazon.com + "#navAmazonLogo", "#navSwmSkedPop", + ".navbar", ".navGreeting", + "div", "table", + "img.navCrossshopTabCap", "span.navGreeting", + "#navbar table", "#navidWelcomeMsg span", + "div#navbar", "ul#navAmazonLogo", + "#navAmazonLogo .navAmazonLogoGatewayPanel", "#navidWelcomeMsg .navGreeting", + ".navbar .navAmazonLogoGatewayPanel", ".navbar .navGreeting", + "*", + "#navAmazonLogo li.navAmazonLogoGatewayPanel", "#navidWelcomeMsg span.navGreeting", + "a[name=top]", "form[name=site-search]", + ".navbar li", ".navbar span", + "[name=top]", "[name=site-search]", + "ul li", "a img", + "#navbar #navidWelcomeMsg", "#navbar #navSwmDWPop", + "#navbar ul li", "#navbar a img" + */ + // For Yahoo.com + "#page", "#masthead", "#mastheadhd", + ".mastheadbd", ".first", ".on", + "div", "li", "a", + "div.mastheadbd", "li.first", "li.on", + "#page div", "#dtba span", + "div#page", "div#masthead", + "#page .mastheadbd", "#page .first", + ".outer_search_container .search_container", ".searchbox_container .inputtext", + "*", + "#page div.mastheadbd", "#page li.first", + "input[name=p]", "a[name=marketplace]", + ".outer_search_container div", ".searchbox_container span", + "[name=p]", "[name=marketplace]", + "ul li", "form input", + "#page #e2econtent", "#page #e2e" + ]; + + jQuery.fn.benchmark = function() { + this.each(function() { + try { + jQuery(this).parent().children("*:gt(1)").remove(); + } catch(e) { } + }) + // set # times to run the test in index.html + var times = parseInt(jQuery("#times").val()); + jQuery.benchmarker.startingList = this.get(); + benchmark(this.get(), times, jQuery.benchmarker.libraries); + } + + jQuery(function() { + for(i = 0; i < jQuery.benchmarker.tests.length; i++) { + jQuery("tbody").append("" + jQuery.benchmarker.tests[i] + ""); + } + jQuery("tbody tr:first-child").remove(); + jQuery("td.test").before(""); + jQuery("button.runTests").bind("click", function() { + jQuery('td:has(input:checked) + td.test').benchmark(); + }); + + jQuery("button.retryTies").bind("click", function() { jQuery("tr:has(td.tie) td.test").benchmark() }) + + jQuery("button.selectAll").bind("click", function() { jQuery("input[type=checkbox]").each(function() { this.checked = true }) }) + jQuery("button.deselectAll").bind("click", function() { jQuery("input[type=checkbox]").each(function() { this.checked = false }) }) + + jQuery("#addTest").bind("click", function() { + jQuery("table").append(""); + jQuery("div#time-test > button").each(function() { this.disabled = true; }) + jQuery("tbody tr:last button").bind("click", function() { + var td = jQuery(this).parent(); + td.html("" + jQuery(this).prev().val()).addClass("test"); + jQuery("div#time-test > button").each(function() { this.disabled = false; }) + jQuery("button", td).bind("click", function() { jQuery(this).parents("tr").remove(); }) + }) + }) + + var headers = jQuery.map(jQuery.benchmarker.libraries, function(i,n) { + var extra = n == 0 ? "basis - " : ""; + return "" + extra + i + "" + }).join(""); + + jQuery("thead tr").append(headers); + + var footers = ""; + for(i = 0; i < jQuery.benchmarker.libraries.length; i++) + footers += "" + + var wlfooters = ""; + for(i = 0; i < jQuery.benchmarker.libraries.length; i++) + wlfooters += "W / F" + + jQuery("tfoot tr:first").append(footers); + jQuery("tfoot tr:last").append(wlfooters); + + }); + + benchmark = function(list, times, libraries) { + if(list[0]) { + var times = times || 50; + var el = list[0]; + var code = jQuery(el).text().replace(/^-/, ""); + var timeArr = [] + for(i = 0; i < times + 2; i++) { + var time = new Date() + try { + window[libraries[0]](code); + } catch(e) { } + timeArr.push(new Date() - time); + } + var diff = Math.sum(timeArr) - Math.max.apply( Math, timeArr ) + - Math.min.apply( Math, timeArr ); + try { + var libRes = window[libraries[0]](code); + var jqRes = jQuery(code); + if(((jqRes.length == 0) && (libRes.length != 0)) || + (libRes.length > 0 && (jqRes.length == libRes.length)) || + ((libraries[0] == "cssQuery" || libraries[0] == "jQuery") && code.match(/nth\-child/) && (libRes.length > 0)) || + ((libraries[0] == "jQold") && jqRes.length > 0)) { + jQuery(el).parent().append("" + Math.round(diff / times * 100) / 100 + "ms"); + } else { + jQuery(el).parent().append("FAIL"); + } + } catch(e) { + jQuery(el).parent().append("FAIL"); + } + setTimeout(benchmarkList(list, times, libraries), 100); + } else if(libraries[1]) { + benchmark(jQuery.benchmarker.startingList, times, libraries.slice(1)); + } else { + jQuery("tbody tr").each(function() { + var winners = jQuery("td:gt(1)", this).min(2); + if(winners.length == 1) winners.addClass("winner"); + else winners.addClass("tie"); + }); + setTimeout(count, 100); + } + } + + function benchmarkList(list, times, libraries) { + return function() { + benchmark(list.slice(1), times, libraries); + } + } + + function count() { + for(i = 3; i <= jQuery.benchmarker.libraries.length + 2 ; i++) { + var fails = jQuery("td:nth-child(" + i + ").fail").length; + var wins = jQuery("td:nth-child(" + i + ").winner").length; + jQuery("tfoot tr:first th:eq(" + (i - 1) + ")") + .html("" + wins + " / " + fails + ""); + } + } + + + jQuery.fn.maxmin = function(tolerance, maxmin, percentage) { + tolerance = tolerance || 0; + var target = Math[maxmin].apply(Math, jQuery.map(this, function(i) { + var parsedNum = parseFloat(i.innerHTML.replace(/[^\.\d]/g, "")); + if(parsedNum || (parsedNum == 0)) return parsedNum; + })); + return this.filter(function() { + if( withinTolerance(parseFloat(this.innerHTML.replace(/[^\.\d]/g, "")), target, tolerance, percentage) ) return true; + }) + } + + jQuery.fn.max = function(tolerance, percentage) { return this.maxmin(tolerance, "max", percentage) } + jQuery.fn.min = function(tolerance, percentage) { return this.maxmin(tolerance, "min", percentage) } + + function withinTolerance(number, target, tolerance, percentage) { + if(percentage) { var high = target + ((tolerance / 100) * target); var low = target - ((tolerance / 100) * target); } + else { var high = target + tolerance; var low = target - tolerance; } + if(number >= low && number <= high) return true; + } + + Math.sum = function(arr) { + var sum = 0; + for(i = 0; i < arr.length; i++) sum += arr[i]; + return sum; + } diff --git a/speed/index.html b/speed/index.html new file mode 100755 index 00000000..717ca731 --- /dev/null +++ b/speed/index.html @@ -0,0 +1,72 @@ + + + + + + Speed Test + + + + + + + + +

Speed Test

+
+
+

Using the following selector expressions ( times each):

+

NOTE: Number shown is an average.

+
+ + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Run?Test
+ +
+ + + + + + +
+
+
+ + + + + diff --git a/speed/jquery-basis.js b/speed/jquery-basis.js new file mode 100644 index 00000000..88e661ee --- /dev/null +++ b/speed/jquery-basis.js @@ -0,0 +1,3549 @@ +(function(){ +/* + * jQuery 1.2.6 - New Wave Javascript + * + * Copyright (c) 2008 John Resig (jquery.com) + * Dual licensed under the MIT (MIT-LICENSE.txt) + * and GPL (GPL-LICENSE.txt) licenses. + * + * $Date: 2008-05-24 14:22:17 -0400 (Sat, 24 May 2008) $ + * $Rev: 5685 $ + */ + +// Map over jQuery in case of overwrite +var _jQuery = window.jQuery, +// Map over the $ in case of overwrite + _$ = window.$; + +var jQuery = window.jQuery = window.$ = function( selector, context ) { + // The jQuery object is actually just the init constructor 'enhanced' + return new jQuery.fn.init( selector, context ); +}; + +// A simple way to check for HTML strings or ID strings +// (both of which we optimize for) +var quickExpr = /^[^<]*(<(.|\s)+>)[^>]*$|^#(\w+)$/, + +// Is it a simple selector + isSimple = /^.[^:#\[\.]*$/, + +// Will speed up references to undefined, and allows munging its name. + undefined; + +jQuery.fn = jQuery.prototype = { + init: function( selector, context ) { + // Make sure that a selection was provided + selector = selector || document; + + // Handle $(DOMElement) + if ( selector.nodeType ) { + this[0] = selector; + this.length = 1; + return this; + } + // Handle HTML strings + if ( typeof selector == "string" ) { + // Are we dealing with HTML string or an ID? + var match = quickExpr.exec( selector ); + + // Verify a match, and that no context was specified for #id + if ( match && (match[1] || !context) ) { + + // HANDLE: $(html) -> $(array) + if ( match[1] ) + selector = jQuery.clean( [ match[1] ], context ); + + // HANDLE: $("#id") + else { + var elem = document.getElementById( match[3] ); + + // Make sure an element was located + if ( elem ){ + // Handle the case where IE and Opera return items + // by name instead of ID + if ( elem.id != match[3] ) + return jQuery().find( selector ); + + // Otherwise, we inject the element directly into the jQuery object + return jQuery( elem ); + } + selector = []; + } + + // HANDLE: $(expr, [context]) + // (which is just equivalent to: $(content).find(expr) + } else + return jQuery( context ).find( selector ); + + // HANDLE: $(function) + // Shortcut for document ready + } else if ( jQuery.isFunction( selector ) ) + return jQuery( document )[ jQuery.fn.ready ? "ready" : "load" ]( selector ); + + return this.setArray(jQuery.makeArray(selector)); + }, + + // The current version of jQuery being used + jquery: "1.2.6", + + // The number of elements contained in the matched element set + size: function() { + return this.length; + }, + + // The number of elements contained in the matched element set + length: 0, + + // Get the Nth element in the matched element set OR + // Get the whole matched element set as a clean array + get: function( num ) { + return num == undefined ? + + // Return a 'clean' array + jQuery.makeArray( this ) : + + // Return just the object + this[ num ]; + }, + + // Take an array of elements and push it onto the stack + // (returning the new matched element set) + pushStack: function( elems ) { + // Build a new jQuery matched element set + var ret = jQuery( elems ); + + // Add the old object onto the stack (as a reference) + ret.prevObject = this; + + // Return the newly-formed element set + return ret; + }, + + // Force the current matched set of elements to become + // the specified array of elements (destroying the stack in the process) + // You should use pushStack() in order to do this, but maintain the stack + setArray: function( elems ) { + // Resetting the length to 0, then using the native Array push + // is a super-fast way to populate an object with array-like properties + this.length = 0; + Array.prototype.push.apply( this, elems ); + + return this; + }, + + // Execute a callback for every element in the matched set. + // (You can seed the arguments with an array of args, but this is + // only used internally.) + each: function( callback, args ) { + return jQuery.each( this, callback, args ); + }, + + // Determine the position of an element within + // the matched set of elements + index: function( elem ) { + var ret = -1; + + // Locate the position of the desired element + return jQuery.inArray( + // If it receives a jQuery object, the first element is used + elem && elem.jquery ? elem[0] : elem + , this ); + }, + + attr: function( name, value, type ) { + var options = name; + + // Look for the case where we're accessing a style value + if ( name.constructor == String ) + if ( value === undefined ) + return this[0] && jQuery[ type || "attr" ]( this[0], name ); + + else { + options = {}; + options[ name ] = value; + } + + // Check to see if we're setting style values + return this.each(function(i){ + // Set all the styles + for ( name in options ) + jQuery.attr( + type ? + this.style : + this, + name, jQuery.prop( this, options[ name ], type, i, name ) + ); + }); + }, + + css: function( key, value ) { + // ignore negative width and height values + if ( (key == 'width' || key == 'height') && parseFloat(value) < 0 ) + value = undefined; + return this.attr( key, value, "curCSS" ); + }, + + text: function( text ) { + if ( typeof text != "object" && text != null ) + return this.empty().append( (this[0] && this[0].ownerDocument || document).createTextNode( text ) ); + + var ret = ""; + + jQuery.each( text || this, function(){ + jQuery.each( this.childNodes, function(){ + if ( this.nodeType != 8 ) + ret += this.nodeType != 1 ? + this.nodeValue : + jQuery.fn.text( [ this ] ); + }); + }); + + return ret; + }, + + wrapAll: function( html ) { + if ( this[0] ) + // The elements to wrap the target around + jQuery( html, this[0].ownerDocument ) + .clone() + .insertBefore( this[0] ) + .map(function(){ + var elem = this; + + while ( elem.firstChild ) + elem = elem.firstChild; + + return elem; + }) + .append(this); + + return this; + }, + + wrapInner: function( html ) { + return this.each(function(){ + jQuery( this ).contents().wrapAll( html ); + }); + }, + + wrap: function( html ) { + return this.each(function(){ + jQuery( this ).wrapAll( html ); + }); + }, + + append: function() { + return this.domManip(arguments, true, false, function(elem){ + if (this.nodeType == 1) + this.appendChild( elem ); + }); + }, + + prepend: function() { + return this.domManip(arguments, true, true, function(elem){ + if (this.nodeType == 1) + this.insertBefore( elem, this.firstChild ); + }); + }, + + before: function() { + return this.domManip(arguments, false, false, function(elem){ + this.parentNode.insertBefore( elem, this ); + }); + }, + + after: function() { + return this.domManip(arguments, false, true, function(elem){ + this.parentNode.insertBefore( elem, this.nextSibling ); + }); + }, + + end: function() { + return this.prevObject || jQuery( [] ); + }, + + find: function( selector ) { + var elems = jQuery.map(this, function(elem){ + return jQuery.find( selector, elem ); + }); + + return this.pushStack( /[^+>] [^+>]/.test( selector ) || selector.indexOf("..") > -1 ? + jQuery.unique( elems ) : + elems ); + }, + + clone: function( events ) { + // Do the clone + var ret = this.map(function(){ + if ( jQuery.browser.msie && !jQuery.isXMLDoc(this) ) { + // IE copies events bound via attachEvent when + // using cloneNode. Calling detachEvent on the + // clone will also remove the events from the orignal + // In order to get around this, we use innerHTML. + // Unfortunately, this means some modifications to + // attributes in IE that are actually only stored + // as properties will not be copied (such as the + // the name attribute on an input). + var clone = this.cloneNode(true), + container = document.createElement("div"); + container.appendChild(clone); + return jQuery.clean([container.innerHTML])[0]; + } else + return this.cloneNode(true); + }); + + // Need to set the expando to null on the cloned set if it exists + // removeData doesn't work here, IE removes it from the original as well + // this is primarily for IE but the data expando shouldn't be copied over in any browser + var clone = ret.find("*").andSelf().each(function(){ + if ( this[ expando ] != undefined ) + this[ expando ] = null; + }); + + // Copy the events from the original to the clone + if ( events === true ) + this.find("*").andSelf().each(function(i){ + if (this.nodeType == 3) + return; + var events = jQuery.data( this, "events" ); + + for ( var type in events ) + for ( var handler in events[ type ] ) + jQuery.event.add( clone[ i ], type, events[ type ][ handler ], events[ type ][ handler ].data ); + }); + + // Return the cloned set + return ret; + }, + + filter: function( selector ) { + return this.pushStack( + jQuery.isFunction( selector ) && + jQuery.grep(this, function(elem, i){ + return selector.call( elem, i ); + }) || + + jQuery.multiFilter( selector, this ) ); + }, + + not: function( selector ) { + if ( selector.constructor == String ) + // test special case where just one selector is passed in + if ( isSimple.test( selector ) ) + return this.pushStack( jQuery.multiFilter( selector, this, true ) ); + else + selector = jQuery.multiFilter( selector, this ); + + var isArrayLike = selector.length && selector[selector.length - 1] !== undefined && !selector.nodeType; + return this.filter(function() { + return isArrayLike ? jQuery.inArray( this, selector ) < 0 : this != selector; + }); + }, + + add: function( selector ) { + return this.pushStack( jQuery.unique( jQuery.merge( + this.get(), + typeof selector == 'string' ? + jQuery( selector ) : + jQuery.makeArray( selector ) + ))); + }, + + is: function( selector ) { + return !!selector && jQuery.multiFilter( selector, this ).length > 0; + }, + + hasClass: function( selector ) { + return this.is( "." + selector ); + }, + + val: function( value ) { + if ( value == undefined ) { + + if ( this.length ) { + var elem = this[0]; + + // We need to handle select boxes special + if ( jQuery.nodeName( elem, "select" ) ) { + var index = elem.selectedIndex, + values = [], + options = elem.options, + one = elem.type == "select-one"; + + // Nothing was selected + if ( index < 0 ) + return null; + + // Loop through all the selected options + for ( var i = one ? index : 0, max = one ? index + 1 : options.length; i < max; i++ ) { + var option = options[ i ]; + + if ( option.selected ) { + // Get the specifc value for the option + value = jQuery.browser.msie && !option.attributes.value.specified ? option.text : option.value; + + // We don't need an array for one selects + if ( one ) + return value; + + // Multi-Selects return an array + values.push( value ); + } + } + + return values; + + // Everything else, we just grab the value + } else + return (this[0].value || "").replace(/\r/g, ""); + + } + + return undefined; + } + + if( value.constructor == Number ) + value += ''; + + return this.each(function(){ + if ( this.nodeType != 1 ) + return; + + if ( value.constructor == Array && /radio|checkbox/.test( this.type ) ) + this.checked = (jQuery.inArray(this.value, value) >= 0 || + jQuery.inArray(this.name, value) >= 0); + + else if ( jQuery.nodeName( this, "select" ) ) { + var values = jQuery.makeArray(value); + + jQuery( "option", this ).each(function(){ + this.selected = (jQuery.inArray( this.value, values ) >= 0 || + jQuery.inArray( this.text, values ) >= 0); + }); + + if ( !values.length ) + this.selectedIndex = -1; + + } else + this.value = value; + }); + }, + + html: function( value ) { + return value == undefined ? + (this[0] ? + this[0].innerHTML : + null) : + this.empty().append( value ); + }, + + replaceWith: function( value ) { + return this.after( value ).remove(); + }, + + eq: function( i ) { + return this.slice( i, i + 1 ); + }, + + slice: function() { + return this.pushStack( Array.prototype.slice.apply( this, arguments ) ); + }, + + map: function( callback ) { + return this.pushStack( jQuery.map(this, function(elem, i){ + return callback.call( elem, i, elem ); + })); + }, + + andSelf: function() { + return this.add( this.prevObject ); + }, + + data: function( key, value ){ + var parts = key.split("."); + parts[1] = parts[1] ? "." + parts[1] : ""; + + if ( value === undefined ) { + var data = this.triggerHandler("getData" + parts[1] + "!", [parts[0]]); + + if ( data === undefined && this.length ) + data = jQuery.data( this[0], key ); + + return data === undefined && parts[1] ? + this.data( parts[0] ) : + data; + } else + return this.trigger("setData" + parts[1] + "!", [parts[0], value]).each(function(){ + jQuery.data( this, key, value ); + }); + }, + + removeData: function( key ){ + return this.each(function(){ + jQuery.removeData( this, key ); + }); + }, + + domManip: function( args, table, reverse, callback ) { + var clone = this.length > 1, elems; + + return this.each(function(){ + if ( !elems ) { + elems = jQuery.clean( args, this.ownerDocument ); + + if ( reverse ) + elems.reverse(); + } + + var obj = this; + + if ( table && jQuery.nodeName( this, "table" ) && jQuery.nodeName( elems[0], "tr" ) ) + obj = this.getElementsByTagName("tbody")[0] || this.appendChild( this.ownerDocument.createElement("tbody") ); + + var scripts = jQuery( [] ); + + jQuery.each(elems, function(){ + var elem = clone ? + jQuery( this ).clone( true )[0] : + this; + + // execute all scripts after the elements have been injected + if ( jQuery.nodeName( elem, "script" ) ) + scripts = scripts.add( elem ); + else { + // Remove any inner scripts for later evaluation + if ( elem.nodeType == 1 ) + scripts = scripts.add( jQuery( "script", elem ).remove() ); + + // Inject the elements into the document + callback.call( obj, elem ); + } + }); + + scripts.each( evalScript ); + }); + } +}; + +// Give the init function the jQuery prototype for later instantiation +jQuery.fn.init.prototype = jQuery.fn; + +function evalScript( i, elem ) { + if ( elem.src ) + jQuery.ajax({ + url: elem.src, + async: false, + dataType: "script" + }); + + else + jQuery.globalEval( elem.text || elem.textContent || elem.innerHTML || "" ); + + if ( elem.parentNode ) + elem.parentNode.removeChild( elem ); +} + +function now(){ + return +new Date; +} + +jQuery.extend = jQuery.fn.extend = function() { + // copy reference to target object + var target = arguments[0] || {}, i = 1, length = arguments.length, deep = false, options; + + // Handle a deep copy situation + if ( target.constructor == Boolean ) { + deep = target; + target = arguments[1] || {}; + // skip the boolean and the target + i = 2; + } + + // Handle case when target is a string or something (possible in deep copy) + if ( typeof target != "object" && typeof target != "function" ) + target = {}; + + // extend jQuery itself if only one argument is passed + if ( length == i ) { + target = this; + --i; + } + + for ( ; i < length; i++ ) + // Only deal with non-null/undefined values + if ( (options = arguments[ i ]) != null ) + // Extend the base object + for ( var name in options ) { + var src = target[ name ], copy = options[ name ]; + + // Prevent never-ending loop + if ( target === copy ) + continue; + + // Recurse if we're merging object values + if ( deep && copy && typeof copy == "object" && !copy.nodeType ) + target[ name ] = jQuery.extend( deep, + // Never move original objects, clone them + src || ( copy.length != null ? [ ] : { } ) + , copy ); + + // Don't bring in undefined values + else if ( copy !== undefined ) + target[ name ] = copy; + + } + + // Return the modified object + return target; +}; + +var expando = "jQuery" + now(), uuid = 0, windowData = {}, + // exclude the following css properties to add px + exclude = /z-?index|font-?weight|opacity|zoom|line-?height/i, + // cache defaultView + defaultView = document.defaultView || {}; + +jQuery.extend({ + noConflict: function( deep ) { + window.$ = _$; + + if ( deep ) + window.jQuery = _jQuery; + + return jQuery; + }, + + // See test/unit/core.js for details concerning this function. + isFunction: function( fn ) { + return !!fn && typeof fn != "string" && !fn.nodeName && + fn.constructor != Array && /^[\s[]?function/.test( fn + "" ); + }, + + // check if an element is in a (or is an) XML document + isXMLDoc: function( elem ) { + return elem.documentElement && !elem.body || + elem.tagName && elem.ownerDocument && !elem.ownerDocument.body; + }, + + // Evalulates a script in a global context + globalEval: function( data ) { + data = jQuery.trim( data ); + + if ( data ) { + // Inspired by code by Andrea Giammarchi + // http://webreflection.blogspot.com/2007/08/global-scope-evaluation-and-dom.html + var head = document.getElementsByTagName("head")[0] || document.documentElement, + script = document.createElement("script"); + + script.type = "text/javascript"; + if ( jQuery.browser.msie ) + script.text = data; + else + script.appendChild( document.createTextNode( data ) ); + + // Use insertBefore instead of appendChild to circumvent an IE6 bug. + // This arises when a base node is used (#2709). + head.insertBefore( script, head.firstChild ); + head.removeChild( script ); + } + }, + + nodeName: function( elem, name ) { + return elem.nodeName && elem.nodeName.toUpperCase() == name.toUpperCase(); + }, + + cache: {}, + + data: function( elem, name, data ) { + elem = elem == window ? + windowData : + elem; + + var id = elem[ expando ]; + + // Compute a unique ID for the element + if ( !id ) + id = elem[ expando ] = ++uuid; + + // Only generate the data cache if we're + // trying to access or manipulate it + if ( name && !jQuery.cache[ id ] ) + jQuery.cache[ id ] = {}; + + // Prevent overriding the named cache with undefined values + if ( data !== undefined ) + jQuery.cache[ id ][ name ] = data; + + // Return the named cache data, or the ID for the element + return name ? + jQuery.cache[ id ][ name ] : + id; + }, + + removeData: function( elem, name ) { + elem = elem == window ? + windowData : + elem; + + var id = elem[ expando ]; + + // If we want to remove a specific section of the element's data + if ( name ) { + if ( jQuery.cache[ id ] ) { + // Remove the section of cache data + delete jQuery.cache[ id ][ name ]; + + // If we've removed all the data, remove the element's cache + name = ""; + + for ( name in jQuery.cache[ id ] ) + break; + + if ( !name ) + jQuery.removeData( elem ); + } + + // Otherwise, we want to remove all of the element's data + } else { + // Clean up the element expando + try { + delete elem[ expando ]; + } catch(e){ + // IE has trouble directly removing the expando + // but it's ok with using removeAttribute + if ( elem.removeAttribute ) + elem.removeAttribute( expando ); + } + + // Completely remove the data cache + delete jQuery.cache[ id ]; + } + }, + + // args is for internal usage only + each: function( object, callback, args ) { + var name, i = 0, length = object.length; + + if ( args ) { + if ( length == undefined ) { + for ( name in object ) + if ( callback.apply( object[ name ], args ) === false ) + break; + } else + for ( ; i < length; ) + if ( callback.apply( object[ i++ ], args ) === false ) + break; + + // A special, fast, case for the most common use of each + } else { + if ( length == undefined ) { + for ( name in object ) + if ( callback.call( object[ name ], name, object[ name ] ) === false ) + break; + } else + for ( var value = object[0]; + i < length && callback.call( value, i, value ) !== false; value = object[++i] ){} + } + + return object; + }, + + prop: function( elem, value, type, i, name ) { + // Handle executable functions + if ( jQuery.isFunction( value ) ) + value = value.call( elem, i ); + + // Handle passing in a number to a CSS property + return value && value.constructor == Number && type == "curCSS" && !exclude.test( name ) ? + value + "px" : + value; + }, + + className: { + // internal only, use addClass("class") + add: function( elem, classNames ) { + jQuery.each((classNames || "").split(/\s+/), function(i, className){ + if ( elem.nodeType == 1 && !jQuery.className.has( elem.className, className ) ) + elem.className += (elem.className ? " " : "") + className; + }); + }, + + // internal only, use removeClass("class") + remove: function( elem, classNames ) { + if (elem.nodeType == 1) + elem.className = classNames != undefined ? + jQuery.grep(elem.className.split(/\s+/), function(className){ + return !jQuery.className.has( classNames, className ); + }).join(" ") : + ""; + }, + + // internal only, use hasClass("class") + has: function( elem, className ) { + return jQuery.inArray( className, (elem.className || elem).toString().split(/\s+/) ) > -1; + } + }, + + // A method for quickly swapping in/out CSS properties to get correct calculations + swap: function( elem, options, callback ) { + var old = {}; + // Remember the old values, and insert the new ones + for ( var name in options ) { + old[ name ] = elem.style[ name ]; + elem.style[ name ] = options[ name ]; + } + + callback.call( elem ); + + // Revert the old values + for ( var name in options ) + elem.style[ name ] = old[ name ]; + }, + + css: function( elem, name, force ) { + if ( name == "width" || name == "height" ) { + var val, props = { position: "absolute", visibility: "hidden", display:"block" }, which = name == "width" ? [ "Left", "Right" ] : [ "Top", "Bottom" ]; + + function getWH() { + val = name == "width" ? elem.offsetWidth : elem.offsetHeight; + var padding = 0, border = 0; + jQuery.each( which, function() { + padding += parseFloat(jQuery.curCSS( elem, "padding" + this, true)) || 0; + border += parseFloat(jQuery.curCSS( elem, "border" + this + "Width", true)) || 0; + }); + val -= Math.round(padding + border); + } + + if ( jQuery(elem).is(":visible") ) + getWH(); + else + jQuery.swap( elem, props, getWH ); + + return Math.max(0, val); + } + + return jQuery.curCSS( elem, name, force ); + }, + + curCSS: function( elem, name, force ) { + var ret, style = elem.style; + + // A helper method for determining if an element's values are broken + function color( elem ) { + if ( !jQuery.browser.safari ) + return false; + + // defaultView is cached + var ret = defaultView.getComputedStyle( elem, null ); + return !ret || ret.getPropertyValue("color") == ""; + } + + // We need to handle opacity special in IE + if ( name == "opacity" && jQuery.browser.msie ) { + ret = jQuery.attr( style, "opacity" ); + + return ret == "" ? + "1" : + ret; + } + // Opera sometimes will give the wrong display answer, this fixes it, see #2037 + if ( jQuery.browser.opera && name == "display" ) { + var save = style.outline; + style.outline = "0 solid black"; + style.outline = save; + } + + // Make sure we're using the right name for getting the float value + if ( name.match( /float/i ) ) + name = styleFloat; + + if ( !force && style && style[ name ] ) + ret = style[ name ]; + + else if ( defaultView.getComputedStyle ) { + + // Only "float" is needed here + if ( name.match( /float/i ) ) + name = "float"; + + name = name.replace( /([A-Z])/g, "-$1" ).toLowerCase(); + + var computedStyle = defaultView.getComputedStyle( elem, null ); + + if ( computedStyle && !color( elem ) ) + ret = computedStyle.getPropertyValue( name ); + + // If the element isn't reporting its values properly in Safari + // then some display: none elements are involved + else { + var swap = [], stack = [], a = elem, i = 0; + + // Locate all of the parent display: none elements + for ( ; a && color(a); a = a.parentNode ) + stack.unshift(a); + + // Go through and make them visible, but in reverse + // (It would be better if we knew the exact display type that they had) + for ( ; i < stack.length; i++ ) + if ( color( stack[ i ] ) ) { + swap[ i ] = stack[ i ].style.display; + stack[ i ].style.display = "block"; + } + + // Since we flip the display style, we have to handle that + // one special, otherwise get the value + ret = name == "display" && swap[ stack.length - 1 ] != null ? + "none" : + ( computedStyle && computedStyle.getPropertyValue( name ) ) || ""; + + // Finally, revert the display styles back + for ( i = 0; i < swap.length; i++ ) + if ( swap[ i ] != null ) + stack[ i ].style.display = swap[ i ]; + } + + // We should always get a number back from opacity + if ( name == "opacity" && ret == "" ) + ret = "1"; + + } else if ( elem.currentStyle ) { + var camelCase = name.replace(/\-(\w)/g, function(all, letter){ + return letter.toUpperCase(); + }); + + ret = elem.currentStyle[ name ] || elem.currentStyle[ camelCase ]; + + // From the awesome hack by Dean Edwards + // http://erik.eae.net/archives/2007/07/27/18.54.15/#comment-102291 + + // If we're not dealing with a regular pixel number + // but a number that has a weird ending, we need to convert it to pixels + if ( !/^\d+(px)?$/i.test( ret ) && /^\d/.test( ret ) ) { + // Remember the original values + var left = style.left, rsLeft = elem.runtimeStyle.left; + + // Put in the new values to get a computed value out + elem.runtimeStyle.left = elem.currentStyle.left; + style.left = ret || 0; + ret = style.pixelLeft + "px"; + + // Revert the changed values + style.left = left; + elem.runtimeStyle.left = rsLeft; + } + } + + return ret; + }, + + clean: function( elems, context ) { + var ret = []; + context = context || document; + // !context.createElement fails in IE with an error but returns typeof 'object' + if (typeof context.createElement == 'undefined') + context = context.ownerDocument || context[0] && context[0].ownerDocument || document; + + jQuery.each(elems, function(i, elem){ + if ( !elem ) + return; + + if ( elem.constructor == Number ) + elem += ''; + + // Convert html string into DOM nodes + if ( typeof elem == "string" ) { + // Fix "XHTML"-style tags in all browsers + elem = elem.replace(/(<(\w+)[^>]*?)\/>/g, function(all, front, tag){ + return tag.match(/^(abbr|br|col|img|input|link|meta|param|hr|area|embed)$/i) ? + all : + front + ">"; + }); + + // Trim whitespace, otherwise indexOf won't work as expected + var tags = jQuery.trim( elem ).toLowerCase(), div = context.createElement("div"); + + var wrap = + // option or optgroup + !tags.indexOf("", "" ] || + + !tags.indexOf("", "" ] || + + tags.match(/^<(thead|tbody|tfoot|colg|cap)/) && + [ 1, "", "
" ] || + + !tags.indexOf("", "" ] || + + // matched above + (!tags.indexOf("", "" ] || + + !tags.indexOf("", "" ] || + + // IE can't serialize and + diff --git a/test/unit/attributes.js b/test/unit/attributes.js index e6a30a1b..0c73f3ad 100644 --- a/test/unit/attributes.js +++ b/test/unit/attributes.js @@ -232,89 +232,6 @@ test("attr('tabindex', value)", function() { equals(element.attr('tabindex'), -1, 'set negative tabindex'); }); -test("css(String|Hash)", function() { - expect(19); - - equals( jQuery('#main').css("display"), 'none', 'Check for css property "display"'); - - ok( jQuery('#nothiddendiv').is(':visible'), 'Modifying CSS display: Assert element is visible'); - jQuery('#nothiddendiv').css({display: 'none'}); - ok( !jQuery('#nothiddendiv').is(':visible'), 'Modified CSS display: Assert element is hidden'); - jQuery('#nothiddendiv').css({display: 'block'}); - ok( jQuery('#nothiddendiv').is(':visible'), 'Modified CSS display: Assert element is visible'); - - jQuery('#floatTest').css({styleFloat: 'right'}); - equals( jQuery('#floatTest').css('styleFloat'), 'right', 'Modified CSS float using "styleFloat": Assert float is right'); - jQuery('#floatTest').css({cssFloat: 'left'}); - equals( jQuery('#floatTest').css('cssFloat'), 'left', 'Modified CSS float using "cssFloat": Assert float is left'); - jQuery('#floatTest').css({'float': 'right'}); - equals( jQuery('#floatTest').css('float'), 'right', 'Modified CSS float using "float": Assert float is right'); - jQuery('#floatTest').css({'font-size': '30px'}); - equals( jQuery('#floatTest').css('font-size'), '30px', 'Modified CSS font-size: Assert font-size is 30px'); - - jQuery.each("0,0.25,0.5,0.75,1".split(','), function(i, n) { - jQuery('#foo').css({opacity: n}); - equals( jQuery('#foo').css('opacity'), parseFloat(n), "Assert opacity is " + parseFloat(n) + " as a String" ); - jQuery('#foo').css({opacity: parseFloat(n)}); - equals( jQuery('#foo').css('opacity'), parseFloat(n), "Assert opacity is " + parseFloat(n) + " as a Number" ); - }); - jQuery('#foo').css({opacity: ''}); - equals( jQuery('#foo').css('opacity'), '1', "Assert opacity is 1 when set to an empty String" ); -}); - -test("css(String, Object)", function() { - expect(21); - ok( jQuery('#nothiddendiv').is(':visible'), 'Modifying CSS display: Assert element is visible'); - jQuery('#nothiddendiv').css("display", 'none'); - ok( !jQuery('#nothiddendiv').is(':visible'), 'Modified CSS display: Assert element is hidden'); - jQuery('#nothiddendiv').css("display", 'block'); - ok( jQuery('#nothiddendiv').is(':visible'), 'Modified CSS display: Assert element is visible'); - - jQuery('#floatTest').css('styleFloat', 'left'); - equals( jQuery('#floatTest').css('styleFloat'), 'left', 'Modified CSS float using "styleFloat": Assert float is left'); - jQuery('#floatTest').css('cssFloat', 'right'); - equals( jQuery('#floatTest').css('cssFloat'), 'right', 'Modified CSS float using "cssFloat": Assert float is right'); - jQuery('#floatTest').css('float', 'left'); - equals( jQuery('#floatTest').css('float'), 'left', 'Modified CSS float using "float": Assert float is left'); - jQuery('#floatTest').css('font-size', '20px'); - equals( jQuery('#floatTest').css('font-size'), '20px', 'Modified CSS font-size: Assert font-size is 20px'); - - jQuery.each("0,0.25,0.5,0.75,1".split(','), function(i, n) { - jQuery('#foo').css('opacity', n); - equals( jQuery('#foo').css('opacity'), parseFloat(n), "Assert opacity is " + parseFloat(n) + " as a String" ); - jQuery('#foo').css('opacity', parseFloat(n)); - equals( jQuery('#foo').css('opacity'), parseFloat(n), "Assert opacity is " + parseFloat(n) + " as a Number" ); - }); - jQuery('#foo').css('opacity', ''); - equals( jQuery('#foo').css('opacity'), '1', "Assert opacity is 1 when set to an empty String" ); - // for #1438, IE throws JS error when filter exists but doesn't have opacity in it - if (jQuery.browser.msie) { - jQuery('#foo').css("filter", "progid:DXImageTransform.Microsoft.Chroma(color='red');"); - } - equals( jQuery('#foo').css('opacity'), '1', "Assert opacity is 1 when a different filter is set in IE, #1438" ); - - // using contents will get comments regular, text, and comment nodes - var j = jQuery("#nonnodes").contents(); - j.css("padding-left", "1px"); - equals( j.css("padding-left"), "1px", "Check node,textnode,comment css works" ); - - // opera sometimes doesn't update 'display' correctly, see #2037 - jQuery("#t2037")[0].innerHTML = jQuery("#t2037")[0].innerHTML - equals( jQuery("#t2037 .hidden").css("display"), "none", "Make sure browser thinks it is hidden" ); -}); - -test("jQuery.css(elem, 'height') doesn't clear radio buttons (bug #1095)", function () { - expect(4); - - var $checkedtest = jQuery("#checkedtest"); - // IE6 was clearing "checked" in jQuery.css(elem, "height"); - jQuery.css($checkedtest[0], "height"); - ok( !! jQuery(":radio:first", $checkedtest).attr("checked"), "Check first radio still checked." ); - ok( ! jQuery(":radio:last", $checkedtest).attr("checked"), "Check last radio still NOT checked." ); - ok( !! jQuery(":checkbox:first", $checkedtest).attr("checked"), "Check first checkbox still checked." ); - ok( ! jQuery(":checkbox:last", $checkedtest).attr("checked"), "Check last checkbox still NOT checked." ); -}); - test("width()", function() { expect(6); diff --git a/test/unit/css.js b/test/unit/css.js new file mode 100644 index 00000000..97e529f0 --- /dev/null +++ b/test/unit/css.js @@ -0,0 +1,82 @@ +test("css(String|Hash)", function() { + expect(19); + + equals( jQuery('#main').css("display"), 'none', 'Check for css property "display"'); + + ok( jQuery('#nothiddendiv').is(':visible'), 'Modifying CSS display: Assert element is visible'); + jQuery('#nothiddendiv').css({display: 'none'}); + ok( !jQuery('#nothiddendiv').is(':visible'), 'Modified CSS display: Assert element is hidden'); + jQuery('#nothiddendiv').css({display: 'block'}); + ok( jQuery('#nothiddendiv').is(':visible'), 'Modified CSS display: Assert element is visible'); + + jQuery('#floatTest').css({styleFloat: 'right'}); + equals( jQuery('#floatTest').css('styleFloat'), 'right', 'Modified CSS float using "styleFloat": Assert float is right'); + jQuery('#floatTest').css({cssFloat: 'left'}); + equals( jQuery('#floatTest').css('cssFloat'), 'left', 'Modified CSS float using "cssFloat": Assert float is left'); + jQuery('#floatTest').css({'float': 'right'}); + equals( jQuery('#floatTest').css('float'), 'right', 'Modified CSS float using "float": Assert float is right'); + jQuery('#floatTest').css({'font-size': '30px'}); + equals( jQuery('#floatTest').css('font-size'), '30px', 'Modified CSS font-size: Assert font-size is 30px'); + + jQuery.each("0,0.25,0.5,0.75,1".split(','), function(i, n) { + jQuery('#foo').css({opacity: n}); + equals( jQuery('#foo').css('opacity'), parseFloat(n), "Assert opacity is " + parseFloat(n) + " as a String" ); + jQuery('#foo').css({opacity: parseFloat(n)}); + equals( jQuery('#foo').css('opacity'), parseFloat(n), "Assert opacity is " + parseFloat(n) + " as a Number" ); + }); + jQuery('#foo').css({opacity: ''}); + equals( jQuery('#foo').css('opacity'), '1', "Assert opacity is 1 when set to an empty String" ); +}); + +test("css(String, Object)", function() { + expect(21); + ok( jQuery('#nothiddendiv').is(':visible'), 'Modifying CSS display: Assert element is visible'); + jQuery('#nothiddendiv').css("display", 'none'); + ok( !jQuery('#nothiddendiv').is(':visible'), 'Modified CSS display: Assert element is hidden'); + jQuery('#nothiddendiv').css("display", 'block'); + ok( jQuery('#nothiddendiv').is(':visible'), 'Modified CSS display: Assert element is visible'); + + jQuery('#floatTest').css('styleFloat', 'left'); + equals( jQuery('#floatTest').css('styleFloat'), 'left', 'Modified CSS float using "styleFloat": Assert float is left'); + jQuery('#floatTest').css('cssFloat', 'right'); + equals( jQuery('#floatTest').css('cssFloat'), 'right', 'Modified CSS float using "cssFloat": Assert float is right'); + jQuery('#floatTest').css('float', 'left'); + equals( jQuery('#floatTest').css('float'), 'left', 'Modified CSS float using "float": Assert float is left'); + jQuery('#floatTest').css('font-size', '20px'); + equals( jQuery('#floatTest').css('font-size'), '20px', 'Modified CSS font-size: Assert font-size is 20px'); + + jQuery.each("0,0.25,0.5,0.75,1".split(','), function(i, n) { + jQuery('#foo').css('opacity', n); + equals( jQuery('#foo').css('opacity'), parseFloat(n), "Assert opacity is " + parseFloat(n) + " as a String" ); + jQuery('#foo').css('opacity', parseFloat(n)); + equals( jQuery('#foo').css('opacity'), parseFloat(n), "Assert opacity is " + parseFloat(n) + " as a Number" ); + }); + jQuery('#foo').css('opacity', ''); + equals( jQuery('#foo').css('opacity'), '1', "Assert opacity is 1 when set to an empty String" ); + // for #1438, IE throws JS error when filter exists but doesn't have opacity in it + if (jQuery.browser.msie) { + jQuery('#foo').css("filter", "progid:DXImageTransform.Microsoft.Chroma(color='red');"); + } + equals( jQuery('#foo').css('opacity'), '1', "Assert opacity is 1 when a different filter is set in IE, #1438" ); + + // using contents will get comments regular, text, and comment nodes + var j = jQuery("#nonnodes").contents(); + j.css("padding-left", "1px"); + equals( j.css("padding-left"), "1px", "Check node,textnode,comment css works" ); + + // opera sometimes doesn't update 'display' correctly, see #2037 + jQuery("#t2037")[0].innerHTML = jQuery("#t2037")[0].innerHTML + equals( jQuery("#t2037 .hidden").css("display"), "none", "Make sure browser thinks it is hidden" ); +}); + +test("jQuery.css(elem, 'height') doesn't clear radio buttons (bug #1095)", function () { + expect(4); + + var $checkedtest = jQuery("#checkedtest"); + // IE6 was clearing "checked" in jQuery.css(elem, "height"); + jQuery.css($checkedtest[0], "height"); + ok( !! jQuery(":radio:first", $checkedtest).attr("checked"), "Check first radio still checked." ); + ok( ! jQuery(":radio:last", $checkedtest).attr("checked"), "Check last radio still NOT checked." ); + ok( !! jQuery(":checkbox:first", $checkedtest).attr("checked"), "Check first checkbox still checked." ); + ok( ! jQuery(":checkbox:last", $checkedtest).attr("checked"), "Check last checkbox still NOT checked." ); +}); \ No newline at end of file diff --git a/test/unit/fx.js b/test/unit/fx.js index 63a3de7a..978ef717 100644 --- a/test/unit/fx.js +++ b/test/unit/fx.js @@ -365,7 +365,7 @@ jQuery.each( { if ( t_h == "hide"||t_h == "show" ) equals(this.style.height.indexOf(f_h), 0, "Height must be reset to " + f_h + ": " + this.style.height); - var cur_o = jQuery.attr(this.style, "opacity"); + var cur_o = jQuery.style(this, "opacity"); if ( cur_o !== "" ) cur_o = parseFloat( cur_o ); if ( t_o == "hide"||t_o == "show" )