diff options
-rw-r--r-- | README.md | 2 | ||||
-rw-r--r-- | build/lib/parse-js.js | 40 | ||||
-rw-r--r-- | build/lib/process.js | 96 | ||||
-rw-r--r-- | build/post-compile.js | 2 | ||||
-rw-r--r-- | build/release-notes.js | 4 | ||||
-rw-r--r-- | build/release.js | 2 | ||||
-rw-r--r-- | build/uglify.js | 176 | ||||
-rw-r--r-- | src/attributes.js | 188 | ||||
-rw-r--r-- | src/core.js | 7 | ||||
-rw-r--r-- | src/data.js | 2 | ||||
-rw-r--r-- | src/deferred.js | 2 | ||||
-rw-r--r-- | src/effects.js | 37 | ||||
-rw-r--r-- | src/event.js | 4 | ||||
-rw-r--r-- | src/manipulation.js | 8 | ||||
-rw-r--r-- | src/queue.js | 3 | ||||
-rw-r--r-- | src/support.js | 12 | ||||
-rw-r--r-- | test/data/support/boxModelIE.html | 27 | ||||
-rw-r--r-- | test/data/support/hiddenIFrameFF.html | 25 | ||||
-rw-r--r-- | test/data/testrunner.js | 11 | ||||
-rw-r--r-- | test/data/versioncheck.js | 8 | ||||
-rw-r--r-- | test/index.html | 7 | ||||
-rw-r--r-- | test/unit/attributes.js | 230 | ||||
-rw-r--r-- | test/unit/core.js | 21 | ||||
-rw-r--r-- | test/unit/css.js | 10 | ||||
-rw-r--r-- | test/unit/deferred.js | 12 | ||||
-rw-r--r-- | test/unit/effects.js | 80 | ||||
-rw-r--r-- | test/unit/event.js | 16 | ||||
-rw-r--r-- | test/unit/manipulation.js | 13 | ||||
-rw-r--r-- | test/unit/support.js | 32 | ||||
-rw-r--r-- | version.txt | 2 |
30 files changed, 778 insertions, 301 deletions
@@ -1,4 +1,4 @@ -[jQuery](http://jquery.com/) - New Wave Javascript +[jQuery](http://jquery.com/) - New Wave JavaScript ================================================== What you need to build your own jQuery diff --git a/build/lib/parse-js.js b/build/lib/parse-js.js index 9f90dfeec..8edecb733 100644 --- a/build/lib/parse-js.js +++ b/build/lib/parse-js.js @@ -751,14 +751,17 @@ function parse($TEXT, exigent_mode, embed_tokens) { return str instanceof NodeWithToken ? str : new NodeWithToken(str, start, end); }; - var statement = embed_tokens ? function() { - var start = S.token; - var ast = $statement.apply(this, arguments); - ast[0] = add_tokens(ast[0], start, prev()); - return ast; - } : $statement; - - function $statement() { + function maybe_embed_tokens(parser) { + if (embed_tokens) return function() { + var start = S.token; + var ast = parser.apply(this, arguments); + ast[0] = add_tokens(ast[0], start, prev()); + return ast; + }; + else return parser; + }; + + var statement = maybe_embed_tokens(function() { if (is("operator", "/")) { S.peeked = null; S.token = S.input(true); // force regexp @@ -852,7 +855,7 @@ function parse($TEXT, exigent_mode, embed_tokens) { unexpected(); } } - }; + }); function labeled_statement(label) { S.labels.push(label); @@ -910,14 +913,7 @@ function parse($TEXT, exigent_mode, embed_tokens) { return as("for-in", init, lhs, obj, in_loop(statement)); }; - var function_ = embed_tokens ? function() { - var start = prev(); - var ast = $function_.apply(this, arguments); - ast[0] = add_tokens(ast[0], start, prev()); - return ast; - } : $function_; - - function $function_(in_statement) { + var function_ = maybe_embed_tokens(function(in_statement) { var name = is("name") ? prog1(S.token.value, next) : null; if (in_statement && !name) unexpected(); @@ -945,7 +941,7 @@ function parse($TEXT, exigent_mode, embed_tokens) { S.in_loop = loop; return a; })()); - }; + }); function if_() { var cond = parenthesised(), body = statement(), belse; @@ -1053,7 +1049,7 @@ function parse($TEXT, exigent_mode, embed_tokens) { return subscripts(as("new", newexp, args), true); }; - function expr_atom(allow_calls) { + var expr_atom = maybe_embed_tokens(function(allow_calls) { if (is("operator", "new")) { next(); return new_(); @@ -1088,7 +1084,7 @@ function parse($TEXT, exigent_mode, embed_tokens) { return subscripts(prog1(atom, next), allow_calls); } unexpected(); - }; + }); function expr_list(closing, allow_trailing_comma, allow_empty) { var first = true, a = []; @@ -1228,7 +1224,7 @@ function parse($TEXT, exigent_mode, embed_tokens) { return left; }; - function expression(commas, no_in) { + var expression = maybe_embed_tokens(function(commas, no_in) { if (arguments.length == 0) commas = true; var expr = maybe_assign(no_in); @@ -1237,7 +1233,7 @@ function parse($TEXT, exigent_mode, embed_tokens) { return as("seq", expr, expression(true, no_in)); } return expr; - }; + }); function in_loop(cont) { try { diff --git a/build/lib/process.js b/build/lib/process.js index 09cbc2ad8..3878c8d62 100644 --- a/build/lib/process.js +++ b/build/lib/process.js @@ -75,6 +75,12 @@ function ast_walker(ast) { return a; }) ]; }; + function _block(statements) { + var out = [ this[0] ]; + if (statements != null) + out.push(MAP(statements, walk)); + return out; + }; var walkers = { "string": function(str) { return [ this[0], str ]; @@ -88,12 +94,8 @@ function ast_walker(ast) { "toplevel": function(statements) { return [ this[0], MAP(statements, walk) ]; }, - "block": function(statements) { - var out = [ this[0] ]; - if (statements != null) - out.push(MAP(statements, walk)); - return out; - }, + "block": _block, + "splice": _block, "var": _vardefs, "const": _vardefs, "try": function(t, c, f) { @@ -377,7 +379,9 @@ function ast_add_scope(ast) { }; function _lambda(name, args, body) { - return [ this[0], this[0] == "defun" ? define(name) : name, args, with_new_scope(function(){ + var is_defun = this[0] == "defun"; + return [ this[0], is_defun ? define(name) : name, args, with_new_scope(function(){ + if (!is_defun) define(name); MAP(args, define); return MAP(body, walk); })]; @@ -463,9 +467,22 @@ function ast_mangle(ast, options) { return scope.get_mangled(name, newMangle); }; + function get_define(name) { + // we always lookup a defined symbol for the current scope FIRST, so declared + // vars trump a DEFINE symbol, but if no such var is found, then match a DEFINE value + if (!scope.has(name)) { + if (HOP(options.defines, name)) { + return options.defines[name]; + } + } + return null; + }; + function _lambda(name, args, body) { - if (name) name = get_mangled(name); + var is_defun = this[0] == "defun"; + if (is_defun && name) name = get_mangled(name); body = with_scope(body.scope, function(){ + if (!is_defun && name) name = get_mangled(name); args = MAP(args, function(name){ return get_mangled(name) }); return MAP(body, walk); }); @@ -507,7 +524,7 @@ function ast_mangle(ast, options) { "var": _vardefs, "const": _vardefs, "name": function(name) { - return [ this[0], get_mangled(name) ]; + return get_define(name) || [ this[0], get_mangled(name) ]; }, "try": function(t, c, f) { return [ this[0], @@ -583,11 +600,18 @@ function boolean_expr(expr) { }; function make_conditional(c, t, e) { + var make_real_conditional = function() { if (c[0] == "unary-prefix" && c[1] == "!") { - return e ? [ "conditional", c[2], e, t ] : [ "binary", "||", c[2], t ]; + return e ? [ "conditional", c[2], e, t ] : [ "binary", "||", c[2], t ]; } else { - return e ? [ "conditional", c, t, e ] : [ "binary", "&&", c, t ]; + return e ? [ "conditional", c, t, e ] : [ "binary", "&&", c, t ]; } + }; + // shortcut the conditional if the expression has a constant value + return when_constant(c, function(ast, val){ + warn_unreachable(val ? e : t); + return (val ? t : e); + }, make_real_conditional); }; function empty(b) { @@ -676,6 +700,18 @@ var when_constant = (function(){ || (boolean_expr(expr[2]) && boolean_expr(expr[3])))) { expr[1] = expr[1].substr(0, 2); } + else if (no && expr[0] == "binary" + && (expr[1] == "||" || expr[1] == "&&")) { + // the whole expression is not constant but the lval may be... + try { + var lval = evaluate(expr[2]); + expr = ((expr[1] == "&&" && (lval ? expr[3] : lval)) || + (expr[1] == "||" && (lval ? lval : expr[3])) || + expr); + } catch(ex2) { + // IGNORE... lval is not constant + } + } return no ? no.call(expr, expr) : null; } else throw ex; @@ -751,9 +787,14 @@ function ast_squeeze(ast, options) { }; function _lambda(name, args, body) { - return [ this[0], name, args, with_scope(body.scope, function(){ - return tighten(MAP(body, walk), "lambda"); - }) ]; + var is_defun = this[0] == "defun"; + body = with_scope(body.scope, function(){ + var ret = tighten(MAP(body, walk), "lambda"); + if (!is_defun && name && !HOP(scope.refs, name)) + name = null; + return ret; + }); + return [ this[0], name, args, body ]; }; // we get here for blocks that have been already transformed. @@ -959,13 +1000,7 @@ function ast_squeeze(ast, options) { return [ branch[0] ? walk(branch[0]) : null, block ]; }) ]; }, - "function": function() { - var ret = _lambda.apply(this, arguments); - if (ret[1] && !HOP(scope.refs, ret[1])) { - ret[1] = null; - } - return ret; - }, + "function": _lambda, "defun": _lambda, "block": function(body) { if (body) return rmblock([ "block", tighten(MAP(body, walk)) ]); @@ -1067,6 +1102,8 @@ function to_ascii(str) { }); }; +var SPLICE_NEEDS_BRACKETS = jsp.array_to_hash([ "if", "while", "do", "for", "for-in", "with" ]); + function gen_code(ast, options) { options = defaults(options, { indent_start : 0, @@ -1197,6 +1234,19 @@ function gen_code(ast, options) { return make_block_statements(statements) .join(newline + newline); }, + "splice": function(statements) { + var parent = $stack[$stack.length - 2][0]; + if (HOP(SPLICE_NEEDS_BRACKETS, parent)) { + // we need block brackets in this case + return make_block.apply(this, arguments); + } else { + return MAP(make_block_statements(statements, true), + function(line, i) { + // the first line is already indented + return i > 0 ? indent(line) : line; + }).join(newline); + } + }, "block": make_block, "var": function(defs) { return "var " + add_commas(MAP(defs, make_1vardef)) + ";"; @@ -1437,7 +1487,7 @@ function gen_code(ast, options) { return add_spaces([ out, make_block(body) ]); }; - function make_block_statements(statements) { + function make_block_statements(statements, noindent) { for (var a = [], last = statements.length - 1, i = 0; i <= last; ++i) { var stat = statements[i]; var code = make(stat); @@ -1455,7 +1505,7 @@ function gen_code(ast, options) { a.push(code); } } - return MAP(a, indent); + return noindent ? a : MAP(a, indent); }; function make_switch_block(body) { diff --git a/build/post-compile.js b/build/post-compile.js index 4bcafe814..1bbeaa6f4 100644 --- a/build/post-compile.js +++ b/build/post-compile.js @@ -4,4 +4,4 @@ var print = require("sys").print, src = require("fs").readFileSync(process.argv[2], "utf8"); // Previously done in sed but reimplemented here due to portability issues -print(src.replace(/^(\s*\*\/)(.+)/m, "$1\n$2;")); +print( src.replace( /^(\s*\*\/)(.+)/m, "$1\n$2" ) + ";" ); diff --git a/build/release-notes.js b/build/release-notes.js index c81060a8d..37b9d3897 100644 --- a/build/release-notes.js +++ b/build/release-notes.js @@ -9,8 +9,8 @@ var fs = require("fs"), extract = /<a href="\/ticket\/(\d+)" title="View ticket">(.*?)<[^"]+"component">\s*(\S+)/g; var opts = { - version: "1.6 RC 1", - short_version: "1.6rc1", + version: "1.6", + short_version: "1.6", final_version: "1.6", categories: [] }; diff --git a/build/release.js b/build/release.js index 09f917b48..7a42f998b 100644 --- a/build/release.js +++ b/build/release.js @@ -86,7 +86,7 @@ function setNextVersion( newVersion ) { } else if ( /^\d+\.\d+\.?(\d*)$/.test( newVersion ) ) { newVersion = newVersion.replace( /^(\d+\.\d+\.?)(\d*)$/, function( all, pre, num ) { - return pre + (num ? parseFloat( num ) + 1 : 1) + "pre"; + return pre + (pre.charAt( pre.length - 1 ) !== "." ? "." : "") + (num ? parseFloat( num ) + 1 : 1) + "pre"; }); isFinal = true; diff --git a/build/uglify.js b/build/uglify.js index 943ddd806..aad18e8ca 100644 --- a/build/uglify.js +++ b/build/uglify.js @@ -1,14 +1,10 @@ #! /usr/bin/env node -// -*- js2 -*- +// -*- js -*- global.sys = require(/^v0\.[012]/.test(process.version) ? "sys" : "util"); -var fs = require("fs"), - jsp = require("./lib/parse-js"), - pro = require("./lib/process"); - -pro.set_logger(function(msg){ - sys.debug(msg); -}); +var fs = require("fs"); +var jsp = require("./lib/parse-js"), + pro = require("./lib/process"); var options = { ast: false, @@ -17,13 +13,16 @@ var options = { squeeze: true, make_seqs: true, dead_code: true, - beautify: false, verbose: false, show_copyright: true, out_same_file: false, - extra: false, - unsafe: false, // XXX: extra & unsafe? but maybe we don't want both, so.... - beautify_options: { + max_line_length: 32 * 1024, + unsafe: false, + reserved_names: null, + defines: { }, + codegen_options: { + ascii_only: false, + beautify: false, indent_level: 4, indent_start: 0, quote_keys: false, @@ -40,15 +39,15 @@ out: while (args.length > 0) { switch (v) { case "-b": case "--beautify": - options.beautify = true; + options.codegen_options.beautify = true; break; case "-i": case "--indent": - options.beautify_options.indent_level = args.shift(); + options.codegen_options.indent_level = args.shift(); break; case "-q": case "--quote-keys": - options.beautify_options.quote_keys = true; + options.codegen_options.quote_keys = true; break; case "-mt": case "--mangle-toplevel": @@ -86,23 +85,109 @@ out: while (args.length > 0) { case "--ast": options.ast = true; break; - case "--extra": - options.extra = true; - break; case "--unsafe": options.unsafe = true; break; + case "--max-line-len": + options.max_line_length = parseInt(args.shift(), 10); + break; + case "--reserved-names": + options.reserved_names = args.shift().split(","); + break; + case "-d": + case "--define": + var defarg = args.shift(); + try { + var defsym = function(sym) { + // KEYWORDS_ATOM doesn't include NaN or Infinity - should we check + // for them too ?? We don't check reserved words and the like as the + // define values are only substituted AFTER parsing + if (jsp.KEYWORDS_ATOM.hasOwnProperty(sym)) { + throw "Don't define values for inbuilt constant '"+sym+"'"; + } + return sym; + }, + defval = function(v) { + if (v.match(/^"(.*)"$/) || v.match(/^'(.*)'$/)) { + return [ "string", RegExp.$1 ]; + } + else if (!isNaN(parseFloat(v))) { + return [ "num", parseFloat(v) ]; + } + else if (v.match(/^[a-z\$_][a-z\$_0-9]*$/i)) { + return [ "name", v ]; + } + else if (!v.match(/"/)) { + return [ "string", v ]; + } + else if (!v.match(/'/)) { + return [ "string", v ]; + } + throw "Can't understand the specified value: "+v; + }; + if (defarg.match(/^([a-z_\$][a-z_\$0-9]*)(=(.*))?$/i)) { + var sym = defsym(RegExp.$1), + val = RegExp.$2 ? defval(RegExp.$2.substr(1)) : [ 'name', 'true' ]; + options.defines[sym] = val; + } + else { + throw "The --define option expects SYMBOL[=value]"; + } + } catch(ex) { + sys.print("ERROR: In option --define "+defarg+"\n"+ex+"\n"); + process.exit(1); + } + break; + case "--define-from-module": + var defmodarg = args.shift(), + defmodule = require(defmodarg), + sym, + val; + for (sym in defmodule) { + if (defmodule.hasOwnProperty(sym)) { + options.defines[sym] = function(val) { + if (typeof val == "string") + return [ "string", val ]; + if (typeof val == "number") + return [ "num", val ]; + if (val === true) + return [ 'name', 'true' ]; + if (val === false) + return [ 'name', 'false' ]; + if (val === null) + return [ 'name', 'null' ]; + if (val === undefined) + return [ 'name', 'undefined' ]; + sys.print("ERROR: In option --define-from-module "+defmodarg+"\n"); + sys.print("ERROR: Unknown object type for: "+sym+"="+val+"\n"); + process.exit(1); + return null; + }(defmodule[sym]); + } + } + break; + case "--ascii": + options.codegen_options.ascii_only = true; + break; default: filename = v; break out; } } +if (options.verbose) { + pro.set_logger(function(msg){ + sys.debug(msg); + }); +} + +jsp.set_logger(function(msg){ + sys.debug(msg); +}); + if (filename) { fs.readFile(filename, "utf8", function(err, text){ - if (err) { - throw err; - } + if (err) throw err; output(squeeze_it(text)); }); } else { @@ -131,7 +216,9 @@ function output(text) { }); } out.write(text); - out.end(); + if (options.output !== true) { + out.end(); + } }; // --------- main ends here. @@ -152,36 +239,35 @@ function show_copyright(comments) { function squeeze_it(code) { var result = ""; if (options.show_copyright) { - var initial_comments = []; - // keep first comment - var tok = jsp.tokenizer(code, false), c; + var tok = jsp.tokenizer(code), c; c = tok(); - var prev = null; - while (/^comment/.test(c.type) && (!prev || prev == c.type)) { - initial_comments.push(c); - prev = c.type; - c = tok(); - } - result += show_copyright(initial_comments); + result += show_copyright(c.comments_before); } try { var ast = time_it("parse", function(){ return jsp.parse(code); }); - if (options.mangle) - ast = time_it("mangle", function(){ return pro.ast_mangle(ast, options.mangle_toplevel); }); - if (options.squeeze) - ast = time_it("squeeze", function(){ - ast = pro.ast_squeeze(ast, { - make_seqs : options.make_seqs, - dead_code : options.dead_code, - extra : options.extra - }); - if (options.unsafe) - ast = pro.ast_squeeze_more(ast); - return ast; + if (options.mangle) ast = time_it("mangle", function(){ + return pro.ast_mangle(ast, { + toplevel: options.mangle_toplevel, + defines: options.defines, + except: options.reserved_names + }); + }); + if (options.squeeze) ast = time_it("squeeze", function(){ + ast = pro.ast_squeeze(ast, { + make_seqs : options.make_seqs, + dead_code : options.dead_code, + keep_comps : !options.unsafe }); + if (options.unsafe) + ast = pro.ast_squeeze_more(ast); + return ast; + }); if (options.ast) return sys.inspect(ast, null, null); - result += time_it("generate", function(){ return pro.gen_code(ast, options.beautify && options.beautify_options) }); + result += time_it("generate", function(){ return pro.gen_code(ast, options.codegen_options) }); + if (!options.codegen_options.beautify && options.max_line_length) { + result = time_it("split", function(){ return pro.split_lines(result, options.max_line_length) }); + } return result; } catch(ex) { sys.debug(ex.stack); diff --git a/src/attributes.js b/src/attributes.js index 43d72ae9b..bc26d735a 100644 --- a/src/attributes.js +++ b/src/attributes.js @@ -1,13 +1,14 @@ (function( jQuery ) { var rclass = /[\n\t\r]/g, - rspaces = /\s+/, + rspace = /\s+/, rreturn = /\r/g, rtype = /^(?:button|input)$/i, rfocusable = /^(?:button|input|object|select|textarea)$/i, rclickable = /^a(?:rea)?$/i, - rspecial = /^(?:data-|aria-)/, - formHook; + rboolean = /^(?:autofocus|autoplay|async|checked|controls|defer|disabled|hidden|loop|multiple|open|readonly|required|scoped|selected)$/i, + rinvalidChar = /\:/, + formHook, boolHook; jQuery.fn.extend({ attr: function( name, value ) { @@ -25,6 +26,7 @@ jQuery.fn.extend({ }, removeProp: function( name ) { + name = jQuery.propFix[ name ] || name; return this.each(function() { // try/catch handles cases where IE balks (such as removing a property on window) try { @@ -43,7 +45,7 @@ jQuery.fn.extend({ } if ( value && typeof value === "string" ) { - var classNames = (value || "").split( rspaces ); + var classNames = (value || "").split( rspace ); for ( var i = 0, l = this.length; i < l; i++ ) { var elem = this[i]; @@ -79,7 +81,7 @@ jQuery.fn.extend({ } if ( (value && typeof value === "string") || value === undefined ) { - var classNames = (value || "").split( rspaces ); + var classNames = (value || "").split( rspace ); for ( var i = 0, l = this.length; i < l; i++ ) { var elem = this[i]; @@ -120,7 +122,7 @@ jQuery.fn.extend({ i = 0, self = jQuery( this ), state = stateVal, - classNames = value.split( rspaces ); + classNames = value.split( rspace ); while ( (className = classNames[ i++ ]) ) { // check each className given, space seperated list @@ -198,7 +200,7 @@ jQuery.fn.extend({ hooks = jQuery.valHooks[ this.nodeName.toLowerCase() ] || jQuery.valHooks[ this.type ]; // If set returns undefined, fall back to normal setting - if ( !hooks || ("set" in hooks && hooks.set( this, val, "value" ) === undefined) ) { + if ( !hooks || !("set" in hooks) || hooks.set( this, val, "value" ) === undefined ) { this.value = val; } }); @@ -217,7 +219,8 @@ jQuery.extend({ }, select: { get: function( elem ) { - var index = elem.selectedIndex, + var value, + index = elem.selectedIndex, values = [], options = elem.options, one = elem.type === "select-one"; @@ -284,8 +287,7 @@ jQuery.extend({ attrFix: { // Always normalize to ensure hook usage - tabindex: "tabIndex", - readonly: "readOnly" + tabindex: "tabIndex" }, attr: function( elem, name, value, pass ) { @@ -299,20 +301,36 @@ jQuery.extend({ if ( pass && name in jQuery.attrFn ) { return jQuery( elem )[ name ]( value ); } - + + // Fallback to prop when attributes are not supported + if ( !("getAttribute" in elem) ) { + return jQuery.prop( elem, name, value ); + } + var ret, hooks, notxml = nType !== 1 || !jQuery.isXMLDoc( elem ); - + // Normalize the name if needed name = notxml && jQuery.attrFix[ name ] || name; - // Get the appropriate hook, or the formHook - // if getSetAttribute is not supported and we have form objects in IE6/7 - hooks = jQuery.attrHooks[ name ] || ( jQuery.nodeName( elem, "form" ) && formHook ); + hooks = jQuery.attrHooks[ name ]; + + if ( !hooks ) { + // Use boolHook for boolean attributes + if ( rboolean.test( name ) && + (typeof value === "boolean" || value === undefined || value.toLowerCase() === name.toLowerCase()) ) { + + hooks = boolHook; + + // Use formHook for forms and if the name contains certain characters + } else if ( formHook && (jQuery.nodeName( elem, "form" ) || rinvalidChar.test( name )) ) { + hooks = formHook; + } + } if ( value !== undefined ) { - if ( value === null || (value === false && !rspecial.test( name )) ) { + if ( value === null ) { jQuery.removeAttr( elem, name ); return undefined; @@ -320,34 +338,26 @@ jQuery.extend({ return ret; } else { - - // Set boolean attributes to the same name - if ( value === true && !rspecial.test( name ) ) { - value = name; - } - elem.setAttribute( name, "" + value ); return value; } - } else { - - if ( hooks && "get" in hooks && notxml ) { - return hooks.get( elem, name ); + } else if ( hooks && "get" in hooks && notxml ) { + return hooks.get( elem, name ); - } else { + } else { - ret = elem.getAttribute( name ); + ret = elem.getAttribute( name ); - // Non-existent attributes return null, we normalize to undefined - return ret === null ? - undefined : - ret; - } + // Non-existent attributes return null, we normalize to undefined + return ret === null ? + undefined : + ret; } }, - + removeAttr: function( elem, name ) { + var propName; if ( elem.nodeType === 1 ) { name = jQuery.attrFix[ name ] || name; @@ -358,6 +368,11 @@ jQuery.extend({ jQuery.attr( elem, name, "" ); elem.removeAttributeNode( elem.getAttributeNode( name ) ); } + + // Set corresponding property to false for boolean attributes + if ( rboolean.test( name ) && (propName = jQuery.propFix[ name ] || name) in elem ) { + elem[ propName ] = false; + } } }, @@ -371,7 +386,7 @@ jQuery.extend({ // Setting the type on a radio button after the value resets the value in IE6-9 // Reset value to it's default in case type is set after value // This is for element creation - var val = elem.getAttribute("value"); + var val = elem.value; elem.setAttribute( "type", value ); if ( val ) { elem.value = val; @@ -394,37 +409,50 @@ jQuery.extend({ } } }, - - propFix: {}, + + propFix: { + tabindex: "tabIndex", + readonly: "readOnly", + "for": "htmlFor", + "class": "className", + maxlength: "maxLength", + cellspacing: "cellSpacing", + cellpadding: "cellPadding", + rowspan: "rowSpan", + colspan: "colSpan", + usemap: "useMap", + frameborder: "frameBorder", + contenteditable: "contentEditable" + }, prop: function( elem, name, value ) { var nType = elem.nodeType; - + // don't get/set properties on text, comment and attribute nodes if ( !elem || nType === 3 || nType === 8 || nType === 2 ) { return undefined; } - + var ret, hooks, notxml = nType !== 1 || !jQuery.isXMLDoc( elem ); - + // Try to normalize/fix the name name = notxml && jQuery.propFix[ name ] || name; hooks = jQuery.propHooks[ name ]; - + if ( value !== undefined ) { if ( hooks && "set" in hooks && (ret = hooks.set( elem, value, name )) !== undefined ) { return ret; - + } else { return (elem[ name ] = value); } - + } else { if ( hooks && "get" in hooks && (ret = hooks.get( elem, name )) !== undefined ) { return ret; - + } else { return elem[ name ]; } @@ -434,29 +462,65 @@ jQuery.extend({ propHooks: {} }); +// Hook for boolean attributes +boolHook = { + get: function( elem, name ) { + // Align boolean attributes with corresponding properties + return elem[ jQuery.propFix[ name ] || name ] ? + name.toLowerCase() : + undefined; + }, + set: function( elem, value, name ) { + var propName; + if ( value === false ) { + // Remove boolean attributes when set to false + jQuery.removeAttr( elem, name ); + } else { + // value is true since we know at this point it's type boolean and not false + // Set boolean attributes to the same name and set the DOM property + propName = jQuery.propFix[ name ] || name; + if ( propName in elem ) { + // Only set the IDL specifically if it already exists on the element + elem[ propName ] = value; + } + + elem.setAttribute( name, name.toLowerCase() ); + } + return name; + } +}; + +// Use the value property for back compat +// Use the formHook for button elements in IE6/7 (#1954) +jQuery.attrHooks.value = { + get: function( elem, name ) { + if ( formHook && jQuery.nodeName( elem, "button" ) ) { + return formHook.get( elem, name ); + } + return elem.value; + }, + set: function( elem, value, name ) { + if ( formHook && jQuery.nodeName( elem, "button" ) ) { + return formHook.set( elem, value, name ); + } + // Does not return so that setAttribute is also used + elem.value = value; + } +}; + // IE6/7 do not support getting/setting some attributes with get/setAttribute if ( !jQuery.support.getSetAttribute ) { - jQuery.attrFix = jQuery.extend( jQuery.attrFix, { - "for": "htmlFor", - "class": "className", - maxlength: "maxLength", - cellspacing: "cellSpacing", - cellpadding: "cellPadding", - rowspan: "rowSpan", - colspan: "colSpan", - usemap: "useMap", - frameborder: "frameBorder" - }); + + // propFix is more comprehensive and contains all fixes + jQuery.attrFix = jQuery.propFix; // Use this for any attribute on a form in IE6/7 - formHook = jQuery.attrHooks.name = jQuery.attrHooks.value = jQuery.valHooks.button = { + formHook = jQuery.attrHooks.name = jQuery.valHooks.button = { get: function( elem, name ) { - if ( name === "value" && !jQuery.nodeName( elem, "button" ) ) { - return elem.getAttribute( name ); - } - var ret = elem.getAttributeNode( name ); - // Return undefined if not specified instead of empty string - return ret && ret.specified ? + var ret; + ret = elem.getAttributeNode( name ); + // Return undefined if nodeValue is empty string + return ret && ret.nodeValue !== "" ? ret.nodeValue : undefined; }, diff --git a/src/core.js b/src/core.js index 8d812e384..056fb88fb 100644 --- a/src/core.js +++ b/src/core.js @@ -96,9 +96,10 @@ jQuery.fn = jQuery.prototype = { // Handle HTML strings if ( typeof selector === "string" ) { // Are we dealing with HTML string or an ID? - if ( selector.length > 1024 ) { - // Assume very large strings are HTML and skip the regex check + if ( selector.charAt(0) === "<" && selector.charAt( selector.length - 1 ) === ">" && selector.length >= 3 ) { + // Assume that strings that start and end with <> are HTML and skip the regex check match = [ null, selector, null ]; + } else { match = quickExpr.exec( selector ); } @@ -719,7 +720,7 @@ jQuery.extend({ i = 0, length = elems.length, // jquery objects are treated as arrays - isArray = elems instanceof jQuery || length !== undefined && typeof length === "number" && ( ( length > 0 && elems[ 0 ] && elems[ length -1 ] ) || jQuery.isArray( elems ) ) ; + isArray = elems instanceof jQuery || length !== undefined && typeof length === "number" && ( ( length > 0 && elems[ 0 ] && elems[ length -1 ] ) || length === 0 || jQuery.isArray( elems ) ) ; // Go through the array, translating each of the items to their if ( isArray ) { diff --git a/src/data.js b/src/data.js index c2fd558f0..9e5d1ab0b 100644 --- a/src/data.js +++ b/src/data.js @@ -284,7 +284,7 @@ function dataAttr( elem, key, data ) { // If nothing was found internally, try to fetch any // data from the HTML5 data-* attribute if ( data === undefined && elem.nodeType === 1 ) { - name = "data-" + key.replace( rmultiDash, "$1-$2" ).toLowerCase(); + var name = "data-" + key.replace( rmultiDash, "$1-$2" ).toLowerCase(); data = elem.getAttribute( name ); diff --git a/src/deferred.js b/src/deferred.js index 02f92b26c..5cc5fb5be 100644 --- a/src/deferred.js +++ b/src/deferred.js @@ -119,7 +119,7 @@ jQuery.extend({ if ( jQuery.isFunction( fn ) ) { deferred[ handler ](function() { returned = fn.apply( this, arguments ); - if ( jQuery.isFunction( returned.promise ) ) { + if ( returned && jQuery.isFunction( returned.promise ) ) { returned.promise().then( newDefer.resolve, newDefer.reject ); } else { newDefer[ action ]( returned ); diff --git a/src/effects.js b/src/effects.js index 956005089..38d5a0119 100644 --- a/src/effects.js +++ b/src/effects.js @@ -126,6 +126,9 @@ jQuery.fn.extend({ return this.each( optall.complete, [ false ] ); } + // Do not change referenced properties as per-property easing will be lost + prop = jQuery.extend( {}, prop ); + return this[ optall.queue === false ? "each" : "queue" ](function() { // XXX 'this' does not always have a nodeName when running the // test suite @@ -134,7 +137,7 @@ jQuery.fn.extend({ jQuery._mark( this ); } - var opt = jQuery.extend({}, optall), + var opt = jQuery.extend( {}, optall ), isElement = this.nodeType === 1, hidden = isElement && jQuery(this).is(":hidden"), name, val, p, @@ -153,10 +156,18 @@ jQuery.fn.extend({ delete prop[ p ]; } - val = prop[name]; + val = prop[ name ]; + + // easing resolution: per property > opt.specialEasing > opt.easing > 'swing' (default) + if ( jQuery.isArray( val ) ) { + opt.animatedProperties[ name ] = val[ 1 ]; + val = prop[ name ] = val[ 0 ]; + } else { + opt.animatedProperties[ name ] = opt.specialEasing && opt.specialEasing[ name ] || opt.easing || 'swing'; + } if ( val === "hide" && hidden || val === "show" && !hidden ) { - return opt.complete.call(this); + return opt.complete.call( this ); } if ( isElement && ( name === "height" || name === "width" ) ) { @@ -175,7 +186,7 @@ jQuery.fn.extend({ this.style.display = "inline-block"; } else { - display = defaultDisplay(this.nodeName); + display = defaultDisplay( this.nodeName ); // inline-level elements accept inline-block; // block-level elements need to be inline with layout @@ -189,11 +200,6 @@ jQuery.fn.extend({ } } } - - // easing resolution: per property > opt.specialEasing > opt.easing > 'swing' (default) - opt.animatedProperties[name] = jQuery.isArray( val ) ? - val[1]: - opt.specialEasing && opt.specialEasing[name] || opt.easing || 'swing'; } if ( opt.overflow != null ) { @@ -202,19 +208,18 @@ jQuery.fn.extend({ for ( p in prop ) { e = new jQuery.fx( this, opt, p ); - - val = prop[p]; + val = prop[ p ]; if ( rfxtypes.test(val) ) { e[ val === "toggle" ? hidden ? "show" : "hide" : val ](); } else { - parts = rfxnum.exec(val); + parts = rfxnum.exec( val ); start = e.cur(); if ( parts ) { end = parseFloat( parts[2] ); - unit = parts[3] || ( jQuery.cssNumber[ name ] ? "" : "px" ); + unit = parts[3] || ( jQuery.cssNumber[ p ] ? "" : "px" ); // We need to compute starting value if ( unit !== "px" ) { @@ -225,7 +230,7 @@ jQuery.fn.extend({ // If a +=/-= token was provided, we're doing a relative animation if ( parts[1] ) { - end = ((parts[1] === "-=" ? -1 : 1) * end) + start; + end = ( (parts[ 1 ] === "-=" ? -1 : 1) * end ) + start; } e.custom( start, end, unit ); @@ -500,10 +505,10 @@ jQuery.fx.prototype = { this.now = t; } else { n = t - this.startTime; - this.state = n / options.duration; + // Perform the easing function, defaults to swing - this.pos = jQuery.easing[options.animatedProperties[this.prop]](this.state, n, 0, 1, options.duration); + this.pos = jQuery.easing[ options.animatedProperties[ this.prop ] ]( this.state, n, 0, 1, options.duration ); this.now = this.start + ((this.end - this.start) * this.pos); } // Perform the next step of the animation diff --git a/src/event.js b/src/event.js index 0f09241c9..05e79d358 100644 --- a/src/event.js +++ b/src/event.js @@ -4,7 +4,7 @@ var hasOwn = Object.prototype.hasOwnProperty, rnamespaces = /\.(.*)$/, rformElems = /^(?:textarea|input|select)$/i, rperiod = /\./g, - rspace = / /g, + rspaces = / /g, rescape = /[^\w\s.|`]/g, fcleanup = function( nm ) { return nm.replace(rescape, "\\$&"); @@ -1177,7 +1177,7 @@ function liveHandler( event ) { } function liveConvert( type, selector ) { - return (type && type !== "*" ? type + "." : "") + selector.replace(rperiod, "`").replace(rspace, "&"); + return (type && type !== "*" ? type + "." : "") + selector.replace(rperiod, "`").replace(rspaces, "&"); } jQuery.each( ("blur focus focusin focusout load resize scroll unload click dblclick " + diff --git a/src/manipulation.js b/src/manipulation.js index 610d19b7a..e9b1ee538 100644 --- a/src/manipulation.js +++ b/src/manipulation.js @@ -582,7 +582,7 @@ jQuery.extend({ context = context.ownerDocument || context[0] && context[0].ownerDocument || document; } - var ret = []; + var ret = [], j; for ( var i = 0, elem; (elem = elems[i]) != null; i++ ) { if ( typeof elem === "number" ) { @@ -628,7 +628,7 @@ jQuery.extend({ div.childNodes : []; - for ( var j = tbody.length - 1; j >= 0 ; --j ) { + for ( j = tbody.length - 1; j >= 0 ; --j ) { if ( jQuery.nodeName( tbody[ j ], "tbody" ) && !tbody[ j ].childNodes.length ) { tbody[ j ].parentNode.removeChild( tbody[ j ] ); } @@ -649,8 +649,8 @@ jQuery.extend({ var len; if ( !jQuery.support.appendChecked ) { if ( elem[0] && typeof (len = elem.length) === "number" ) { - for ( i = 0; i < len; i++ ) { - findInputs( elem[i] ); + for ( j = 0; j < len; j++ ) { + findInputs( elem[j] ); } } else { findInputs( elem ); diff --git a/src/queue.js b/src/queue.js index ce2305426..66383c190 100644 --- a/src/queue.js +++ b/src/queue.js @@ -148,7 +148,8 @@ jQuery.fn.extend({ count = 1, deferDataKey = type + "defer", queueDataKey = type + "queue", - markDataKey = type + "mark"; + markDataKey = type + "mark", + tmp; function resolve() { if ( !( --count ) ) { defer.resolveWith( elements, [ elements ] ); diff --git a/src/support.js b/src/support.js index ea14bac63..9ffad2803 100644 --- a/src/support.js +++ b/src/support.js @@ -3,6 +3,7 @@ jQuery.support = (function() { var div = document.createElement( "div" ), + documentElement = document.documentElement, all, a, select, @@ -142,13 +143,15 @@ jQuery.support = (function() { width: 0, height: 0, border: 0, - margin: 0 + margin: 0, + // Set background to avoid IE crashes when removing (#9028) + background: "none" }; for ( i in bodyStyle ) { body.style[ i ] = bodyStyle[ i ]; } body.appendChild( div ); - document.documentElement.appendChild( body ); + documentElement.insertBefore( body, documentElement.firstChild ); // Check if a disconnected checkbox will retain its checked // value of true after appended to the DOM (IE6/7) @@ -203,11 +206,12 @@ jQuery.support = (function() { marginDiv.style.marginRight = "0"; div.appendChild( marginDiv ); support.reliableMarginRight = - ( parseInt( document.defaultView.getComputedStyle( marginDiv, null ).marginRight, 10 ) || 0 ) === 0; + ( parseInt( ( document.defaultView.getComputedStyle( marginDiv, null ) || { marginRight: 0 } ).marginRight, 10 ) || 0 ) === 0; } // Remove the body element we added - document.documentElement.removeChild( body ); + body.innerHTML = ""; + documentElement.removeChild( body ); // Technique from Juriy Zaytsev // http://thinkweb2.com/projects/prototype/detecting-event-support-without-browser-sniffing/ diff --git a/test/data/support/boxModelIE.html b/test/data/support/boxModelIE.html new file mode 100644 index 000000000..1b11d2a59 --- /dev/null +++ b/test/data/support/boxModelIE.html @@ -0,0 +1,27 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> +<html> +<body> + <script src="../../../src/core.js"></script> + <script src="../../../src/deferred.js"></script> + <script src="../../../src/support.js"></script> + <script src="../../../src/data.js"></script> + <script src="../../../src/queue.js"></script> + <script src="../../../src/attributes.js"></script> + <script src="../../../src/event.js"></script> + <script src="../../../src/sizzle/sizzle.js"></script> + <script src="../../../src/sizzle-jquery.js"></script> + <script src="../../../src/traversing.js"></script> + <script src="../../../src/manipulation.js"></script> + <script src="../../../src/css.js"></script> + <script src="../../../src/ajax.js"></script> + <script src="../../../src/ajax/jsonp.js"></script> + <script src="../../../src/ajax/script.js"></script> + <script src="../../../src/ajax/xhr.js"></script> + <script src="../../../src/effects.js"></script> + <script src="../../../src/offset.js"></script> + <script src="../../../src/dimensions.js"></script> + <script> + window.top.supportCallback( document.compatMode, jQuery.support.boxModel ); + </script> +</body> +</html> diff --git a/test/data/support/hiddenIFrameFF.html b/test/data/support/hiddenIFrameFF.html new file mode 100644 index 000000000..000ac851a --- /dev/null +++ b/test/data/support/hiddenIFrameFF.html @@ -0,0 +1,25 @@ +<html> + <head> + <script src="../../../src/core.js"></script> + <script src="../../../src/deferred.js"></script> + <script src="../../../src/support.js"></script> + <script src="../../../src/data.js"></script> + <script src="../../../src/queue.js"></script> + <script src="../../../src/attributes.js"></script> + <script src="../../../src/event.js"></script> + <script src="../../../src/sizzle/sizzle.js"></script> + <script src="../../../src/sizzle-jquery.js"></script> + <script src="../../../src/traversing.js"></script> + <script src="../../../src/manipulation.js"></script> + <script src="../../../src/css.js"></script> + <script src="../../../src/ajax.js"></script> + <script src="../../../src/ajax/jsonp.js"></script> + <script src="../../../src/ajax/script.js"></script> + <script src="../../../src/ajax/xhr.js"></script> + <script src="../../../src/effects.js"></script> + <script src="../../../src/offset.js"></script> + <script src="../../../src/dimensions.js"></script> + </head> + <body> + </body> +</html> diff --git a/test/data/testrunner.js b/test/data/testrunner.js index beb0fe2ab..6d44b460c 100644 --- a/test/data/testrunner.js +++ b/test/data/testrunner.js @@ -2,8 +2,9 @@ jQuery.noConflict(); // Allow the test to run with other libs or jQuery's. // jQuery-specific QUnit.reset (function() { - var reset = QUnit.reset; - var ajaxSettings = jQuery.ajaxSettings + var reset = QUnit.reset, + ajaxSettings = jQuery.ajaxSettings; + QUnit.reset = function() { reset.apply(this, arguments); jQuery.event.global = {}; @@ -24,3 +25,9 @@ jQuery.noConflict(); // Allow the test to run with other libs or jQuery's. document.write("<scr" + "ipt src='http://swarm.jquery.org/js/inject.js?" + (new Date).getTime() + "'></scr" + "ipt>"); })(); + +// QUnit Aliases +(function() { + window.equals = window.equal; + window.same = window.deepEqual; +})(); diff --git a/test/data/versioncheck.js b/test/data/versioncheck.js new file mode 100644 index 000000000..f4b7790da --- /dev/null +++ b/test/data/versioncheck.js @@ -0,0 +1,8 @@ +// Run minified source from dist (do make first) +// Should be loaded before QUnit but after src +(function() { + if ( /jquery\=min/.test( window.location.search ) ) { + jQuery.noConflict( true ); + document.write(unescape("%3Cscript%20src%3D%27../dist/jquery.min.js%27%3E%3C/script%3E")); + } +})();
\ No newline at end of file diff --git a/test/index.html b/test/index.html index b19673d99..4b4c98552 100644 --- a/test/index.html +++ b/test/index.html @@ -28,10 +28,13 @@ <script src="../src/offset.js"></script> <script src="../src/dimensions.js"></script> + <script src="data/versioncheck.js"></script> + <script src="qunit/qunit/qunit.js"></script> <script src="data/testrunner.js"></script> <script src="unit/core.js"></script> + <script src="unit/support.js"></script> <script src="unit/deferred.js"></script> <script src="unit/data.js"></script> <script src="unit/queue.js"></script> @@ -48,7 +51,9 @@ </head> <body id="body"> - <h1 id="qunit-header">jQuery Test Suite</h1> + <h1 id="qunit-header"><a href="/jquery/test/index.html">jQuery Test Suite</a> + <a href="?jquery=min">(minified)</a> + </h1> <h2 id="qunit-banner"></h2> <div id="qunit-testrunner-toolbar"></div> <h2 id="qunit-userAgent"></h2> diff --git a/test/unit/attributes.js b/test/unit/attributes.js index cfe676a63..d2d43475d 100644 --- a/test/unit/attributes.js +++ b/test/unit/attributes.js @@ -4,16 +4,14 @@ var bareObj = function(value) { return value; }; var functionReturningObj = function(value) { return (function() { return value; }); }; -test("jQuery.attrFix integrity test", function() { - expect(1); +test("jQuery.attrFix/jQuery.propFix integrity test", function() { + expect(2); // This must be maintained and equal jQuery.attrFix when appropriate // Ensure that accidental or erroneous property // overwrites don't occur // This is simply for better code coverage and future proofing. - var propsShouldBe; - if ( !jQuery.support.getSetAttribute ) { - propsShouldBe = { + var props = { tabindex: "tabIndex", readonly: "readOnly", "for": "htmlFor", @@ -24,60 +22,25 @@ test("jQuery.attrFix integrity test", function() { rowspan: "rowSpan", colspan: "colSpan", usemap: "useMap", - frameborder: "frameBorder" - }; + frameborder: "frameBorder", + contenteditable: "contentEditable" + }, + propsShouldBe; + + if ( !jQuery.support.getSetAttribute ) { + propsShouldBe = props; } else { propsShouldBe = { - tabindex: "tabIndex", - readonly: "readOnly" + tabindex: "tabIndex" }; } - same(propsShouldBe, jQuery.attrFix, "jQuery.attrFix passes integrity check"); -}); - -test("prop(String, Object)", function() { - expect(19); - equals( jQuery("#text1").prop("value"), "Test", "Check for value attribute" ); - equals( jQuery("#text1").prop("value", "Test2").prop("defaultValue"), "Test", "Check for defaultValue attribute" ); - equals( jQuery("#select2").prop("selectedIndex"), 3, "Check for selectedIndex attribute" ); - equals( jQuery("#foo").prop("nodeName").toUpperCase(), "DIV", "Check for nodeName attribute" ); - equals( jQuery("#foo").prop("tagName").toUpperCase(), "DIV", "Check for tagName attribute" ); - equals( jQuery("<option/>").prop("selected"), false, "Check selected attribute on disconnected element." ); - - var body = document.body, $body = jQuery( body ); - ok( $body.prop("nextSibling") === null, "Make sure a null expando returns null" ); - body.foo = "bar"; - equals( $body.prop("foo"), "bar", "Make sure the expando is preferred over the dom attribute" ); - body.foo = undefined; - ok( $body.prop("foo") === undefined, "Make sure the expando is preferred over the dom attribute, even if undefined" ); - - var select = document.createElement("select"), optgroup = document.createElement("optgroup"), option = document.createElement("option"); - optgroup.appendChild( option ); - select.appendChild( optgroup ); - - equals( jQuery(option).prop("selected"), true, "Make sure that a single option is selected, even when in an optgroup." ); - equals( jQuery(document).prop("nodeName"), "#document", "prop works correctly on document nodes (bug #7451)." ); - - var attributeNode = document.createAttribute("irrelevant"), - commentNode = document.createComment("some comment"), - textNode = document.createTextNode("some text"), - obj = {}; - jQuery.each( [document, attributeNode, commentNode, textNode, obj, "#firstp"], function( i, ele ) { - strictEqual( jQuery(ele).prop("nonexisting"), undefined, "prop works correctly for non existing attributes (bug #7500)." ); - }); - - var obj = {}; - jQuery.each( [document, obj], function( i, ele ) { - var $ele = jQuery( ele ); - $ele.prop( "nonexisting", "foo" ); - equal( $ele.prop("nonexisting"), "foo", "prop(name, value) works correctly for non existing attributes (bug #7500)." ); - }); - jQuery( document ).removeProp("nonexisting"); + deepEqual(propsShouldBe, jQuery.attrFix, "jQuery.attrFix passes integrity check"); + deepEqual(props, jQuery.propFix, "jQuery.propFix passes integrity check"); }); test("attr(String)", function() { - expect(35); + expect(37); equals( jQuery("#text1").attr("type"), "text", "Check for type attribute" ); equals( jQuery("#radio1").attr("type"), "radio", "Check for type attribute" ); @@ -142,6 +105,10 @@ test("attr(String)", function() { equals( $button.attr("value"), "foobar", "Value retrieval on a button does not return innerHTML" ); equals( $button.attr("value", "baz").html(), "text", "Setting the value does not change innerHTML" ); + // Attributes with a colon on a table element (#1591) + equals( jQuery("#table").attr("test:attrib"), undefined, "Retrieving a non-existent attribute on a table with a colon does not throw an error." ); + equals( jQuery("#table").attr("test:attrib", "foobar").attr("test:attrib"), "foobar", "Setting an attribute on a table with a colon does not throw an error." ); + ok( jQuery("<div/>").attr("doesntexist") === undefined, "Make sure undefined is returned when no attribute is found." ); ok( jQuery().attr("doesntexist") === undefined, "Make sure undefined is returned when no element is there." ); }); @@ -150,9 +117,9 @@ if ( !isLocal ) { test("attr(String) in XML Files", function() { expect(2); stop(); - jQuery.get("data/dashboard.xml", function(xml) { - equals( jQuery("locations", xml).attr("class"), "foo", "Check class attribute in XML document" ); - equals( jQuery("location", xml).attr("for"), "bar", "Check for attribute in XML document" ); + jQuery.get("data/dashboard.xml", function( xml ) { + equals( jQuery( "locations", xml ).attr("class"), "foo", "Check class attribute in XML document" ); + equals( jQuery( "location", xml ).attr("for"), "bar", "Check for attribute in XML document" ); start(); }); }); @@ -171,13 +138,13 @@ test("attr(Hash)", function() { if ( this.getAttribute("foo") != "baz" && this.getAttribute("zoo") != "ping" ) pass = false; }); ok( pass, "Set Multiple Attributes" ); - equals( jQuery("#text1").attr({value: function() { return this.id; }})[0].value, "text1", "Set attribute to computed value #1" ); - equals( jQuery("#text1").attr({title: function(i) { return i; }}).attr("title"), "0", "Set attribute to computed value #2"); + equals( jQuery("#text1").attr({value: function() { return this.id; }})[0].value, "text1", "Set attribute to computed value #1" ); + equals( jQuery("#text1").attr({title: function(i) { return i; }}).attr("title"), "0", "Set attribute to computed value #2"); }); test("attr(String, Object)", function() { - expect(35); + expect(66); var div = jQuery("div").attr("foo", "bar"), fail = false; @@ -191,45 +158,85 @@ test("attr(String, Object)", function() { equals( fail, false, "Set Attribute, the #" + fail + " element didn't get the attribute 'foo'" ); - // Fails on IE since recent changes to .attr() - // ok( jQuery("#foo").attr({"width": null}), "Try to set an attribute to nothing" ); + ok( jQuery("#foo").attr({ "width": null }), "Try to set an attribute to nothing" ); jQuery("#name").attr("name", "something"); equals( jQuery("#name").attr("name"), "something", "Set name attribute" ); jQuery("#name").attr("name", null); equals( jQuery("#name").attr("name"), undefined, "Remove name attribute" ); - jQuery("#check2").attr("checked", true); + var $input = jQuery("<input>", { name: "something" }); + equals( $input.attr("name"), "something", "Check element creation gets/sets the name attribute." ); + + jQuery("#check2").prop("checked", true).prop("checked", false).attr("checked", true); equals( document.getElementById("check2").checked, true, "Set checked attribute" ); + equals( jQuery("#check2").prop("checked"), true, "Set checked attribute" ); + equals( jQuery("#check2").attr("checked"), "checked", "Set checked attribute" ); jQuery("#check2").attr("checked", false); equals( document.getElementById("check2").checked, false, "Set checked attribute" ); + equals( jQuery("#check2").prop("checked"), false, "Set checked attribute" ); + equals( jQuery("#check2").attr("checked"), undefined, "Set checked attribute" ); jQuery("#text1").attr("readonly", true); equals( document.getElementById("text1").readOnly, true, "Set readonly attribute" ); + equals( jQuery("#text1").prop("readOnly"), true, "Set readonly attribute" ); + equals( jQuery("#text1").attr("readonly"), "readonly", "Set readonly attribute" ); jQuery("#text1").attr("readonly", false); equals( document.getElementById("text1").readOnly, false, "Set readonly attribute" ); + equals( jQuery("#text1").prop("readOnly"), false, "Set readonly attribute" ); + equals( jQuery("#text1").attr("readonly"), undefined, "Set readonly attribute" ); + + jQuery("#check2").prop("checked", true); + equals( document.getElementById("check2").checked, true, "Set checked attribute" ); + equals( jQuery("#check2").prop("checked"), true, "Set checked attribute" ); + equals( jQuery("#check2").attr("checked"), "checked", "Set checked attribute" ); + jQuery("#check2").prop("checked", false); + equals( document.getElementById("check2").checked, false, "Set checked attribute" ); + equals( jQuery("#check2").prop("checked"), false, "Set checked attribute" ); + equals( jQuery("#check2").attr("checked"), undefined, "Set checked attribute" ); + + jQuery("#text1").prop("readOnly", true); + equals( document.getElementById("text1").readOnly, true, "Set readonly attribute" ); + equals( jQuery("#text1").prop("readOnly"), true, "Set readonly attribute" ); + equals( jQuery("#text1").attr("readonly"), "readonly", "Set readonly attribute" ); + jQuery("#text1").prop("readOnly", false); + equals( document.getElementById("text1").readOnly, false, "Set readonly attribute" ); + equals( jQuery("#text1").prop("readOnly"), false, "Set readonly attribute" ); + equals( jQuery("#text1").attr("readonly"), undefined, "Set readonly attribute" ); + jQuery("#name").attr("maxlength", "5"); equals( document.getElementById("name").maxLength, 5, "Set maxlength attribute" ); jQuery("#name").attr("maxLength", "10"); equals( document.getElementById("name").maxLength, 10, "Set maxlength attribute" ); - var $p = jQuery("#firstp").attr("nonexisting", "foo"); - equals( $p.attr("nonexisting"), "foo", "attr(name, value) works correctly for non existing attributes (bug #7500)."); - $p.removeAttr("nonexisting"); var $text = jQuery("#text1").attr("autofocus", true); - equals( $text.attr("autofocus"), "autofocus", "Set boolean attributes to the same name"); + if ( "autofocus" in $text[0] ) { + equals( $text.attr("autofocus"), "autofocus", "Set boolean attributes to the same name"); + } else { + equals( $text.attr("autofocus"), undefined, "autofocus stays undefined in browsers that do not support it(F<4)"); + } equals( $text.attr("autofocus", false).attr("autofocus"), undefined, "Setting autofocus attribute to false removes it"); equals( $text.attr("data-something", true).data("something"), true, "Setting data attributes are not affected by boolean settings"); equals( $text.attr("data-another", false).data("another"), false, "Setting data attributes are not affected by boolean settings" ); equals( $text.attr("aria-disabled", false).attr("aria-disabled"), "false", "Setting aria attributes are not affected by boolean settings"); $text.removeData("something").removeData("another").removeAttr("aria-disabled"); + jQuery("#foo").attr("contenteditable", true); + equals( jQuery("#foo").attr("contenteditable"), "true", "Enumerated attributes are set properly" ); + var attributeNode = document.createAttribute("irrelevant"), commentNode = document.createComment("some comment"), - textNode = document.createTextNode("some text"); + textNode = document.createTextNode("some text"), + obj = {}; - jQuery.each( [commentNode, textNode, attributeNode], function( i, ele ) { - var $ele = jQuery( ele ); - $ele.attr( "nonexisting", "foo" ); - strictEqual( $ele.attr("nonexisting"), undefined, "attr(name, value) works correctly on comment and text nodes (bug #7500)." ); + jQuery.each( [commentNode, textNode, attributeNode], function( i, elem ) { + var $elem = jQuery( elem ); + $elem.attr( "nonexisting", "foo" ); + strictEqual( $elem.attr("nonexisting"), undefined, "attr(name, value) works correctly on comment and text nodes (bug #7500)." ); + }); + + jQuery.each( [window, document, obj, "#firstp"], function( i, elem ) { + var $elem = jQuery( elem ); + strictEqual( $elem.attr("nonexisting"), undefined, "attr works correctly for non existing attributes (bug #7500)." ); + equal( $elem.attr("something", "foo" ).attr("something"), "foo", "attr falls back to prop on unsupported arguments" ); }); var table = jQuery("#table").append("<tr><td>cell</td></tr><tr><td>cell</td><td>cell</td></tr><tr><td>cell</td><td>cell</td></tr>"), @@ -241,6 +248,8 @@ test("attr(String, Object)", function() { table.attr("cellspacing", "2"); equals( table[0].cellSpacing, "2", "Check cellspacing is correctly set" ); + equals( jQuery("#area1").attr("value"), "foobar", "Value attribute retrieves the property for backwards compatibility." ); + // for #1070 jQuery("#name").attr("someAttr", "0"); equals( jQuery("#name").attr("someAttr"), "0", "Set attribute to a string of \"0\"" ); @@ -345,10 +354,10 @@ if ( !isLocal ) { test("attr(String, Object) - Loaded via XML document", function() { expect(2); stop(); - jQuery.get("data/dashboard.xml", function(xml) { + jQuery.get("data/dashboard.xml", function( xml ) { var titles = []; - jQuery("tab", xml).each(function() { - titles.push(jQuery(this).attr("title")); + jQuery( "tab", xml ).each(function() { + titles.push( jQuery(this).attr("title") ); }); equals( titles[0], "Location", "attr() in XML context: Check first title" ); equals( titles[1], "Users", "attr() in XML context: Check second title" ); @@ -413,12 +422,79 @@ test("attr('tabindex', value)", function() { }); test("removeAttr(String)", function() { - expect(5); + expect(7); equals( jQuery("#mark").removeAttr( "class" )[0].className, "", "remove class" ); equals( jQuery("#form").removeAttr("id").attr("id"), undefined, "Remove id" ); equals( jQuery("#foo").attr("style", "position:absolute;").removeAttr("style").attr("style"), undefined, "Check removing style attribute" ); equals( jQuery("#form").attr("style", "position:absolute;").removeAttr("style").attr("style"), undefined, "Check removing style attribute on a form" ); equals( jQuery("#fx-test-group").attr("height", "3px").removeAttr("height").css("height"), "1px", "Removing height attribute has no effect on height set with style attribute" ); + + jQuery("#check1").removeAttr("checked").prop("checked", true).removeAttr("checked"); + equals( document.getElementById("check1").checked, false, "removeAttr sets boolean properties to false" ); + jQuery("#text1").prop("readOnly", true).removeAttr("readonly"); + equals( document.getElementById("text1").readOnly, false, "removeAttr sets boolean properties to false" ); +}); + +test("prop(String, Object)", function() { + expect(30); + + equals( jQuery("#text1").prop("value"), "Test", "Check for value attribute" ); + equals( jQuery("#text1").prop("value", "Test2").prop("defaultValue"), "Test", "Check for defaultValue attribute" ); + equals( jQuery("#select2").prop("selectedIndex"), 3, "Check for selectedIndex attribute" ); + equals( jQuery("#foo").prop("nodeName").toUpperCase(), "DIV", "Check for nodeName attribute" ); + equals( jQuery("#foo").prop("tagName").toUpperCase(), "DIV", "Check for tagName attribute" ); + equals( jQuery("<option/>").prop("selected"), false, "Check selected attribute on disconnected element." ); + + equals( jQuery("#listWithTabIndex").prop("tabindex"), 5, "Check retrieving tabindex" ); + jQuery("#text1").prop("readonly", true); + equals( document.getElementById("text1").readOnly, true, "Check setting readOnly property with 'readonly'" ); + equals( jQuery("#label-for").prop("for"), "action", "Check retrieving htmlFor" ); + jQuery("#text1").prop("class", "test"); + equals( document.getElementById("text1").className, "test", "Check setting className with 'class'" ); + equals( jQuery("#text1").prop("maxlength"), 30, "Check retrieving maxLength" ); + jQuery("#table").prop("cellspacing", 1); + equals( jQuery("#table").prop("cellSpacing"), "1", "Check setting and retrieving cellSpacing" ); + jQuery("#table").prop("cellpadding", 1); + equals( jQuery("#table").prop("cellPadding"), "1", "Check setting and retrieving cellPadding" ); + jQuery("#table").prop("rowspan", 1); + equals( jQuery("#table").prop("rowSpan"), 1, "Check setting and retrieving rowSpan" ); + jQuery("#table").prop("colspan", 1); + equals( jQuery("#table").prop("colSpan"), 1, "Check setting and retrieving colSpan" ); + jQuery("#table").prop("usemap", 1); + equals( jQuery("#table").prop("useMap"), 1, "Check setting and retrieving useMap" ); + jQuery("#table").prop("frameborder", 1); + equals( jQuery("#table").prop("frameBorder"), 1, "Check setting and retrieving frameBorder" ); + QUnit.reset(); + + var body = document.body, $body = jQuery( body ); + ok( $body.prop("nextSibling") === null, "Make sure a null expando returns null" ); + body.foo = "bar"; + equals( $body.prop("foo"), "bar", "Make sure the expando is preferred over the dom attribute" ); + body.foo = undefined; + ok( $body.prop("foo") === undefined, "Make sure the expando is preferred over the dom attribute, even if undefined" ); + + var select = document.createElement("select"), optgroup = document.createElement("optgroup"), option = document.createElement("option"); + optgroup.appendChild( option ); + select.appendChild( optgroup ); + + equals( jQuery(option).prop("selected"), true, "Make sure that a single option is selected, even when in an optgroup." ); + equals( jQuery(document).prop("nodeName"), "#document", "prop works correctly on document nodes (bug #7451)." ); + + var attributeNode = document.createAttribute("irrelevant"), + commentNode = document.createComment("some comment"), + textNode = document.createTextNode("some text"), + obj = {}; + jQuery.each( [document, attributeNode, commentNode, textNode, obj, "#firstp"], function( i, ele ) { + strictEqual( jQuery(ele).prop("nonexisting"), undefined, "prop works correctly for non existing attributes (bug #7500)." ); + }); + + var obj = {}; + jQuery.each( [document, obj], function( i, ele ) { + var $ele = jQuery( ele ); + $ele.prop( "nonexisting", "foo" ); + equal( $ele.prop("nonexisting"), "foo", "prop(name, value) works correctly for non existing attributes (bug #7500)." ); + }); + jQuery( document ).removeProp("nonexisting"); }); test("removeProp(String)", function() { @@ -436,14 +512,14 @@ test("removeProp(String)", function() { strictEqual( ele.nonexisting, undefined, "removeProp works correctly on non DOM element nodes (bug #7500)." ); }); jQuery.each( [commentNode, textNode, attributeNode], function( i, ele ) { - $ele = jQuery( ele ); + var $ele = jQuery( ele ); $ele.prop( "nonexisting", "foo" ).removeProp( "nonexisting" ); strictEqual( ele.nonexisting, undefined, "removeProp works correctly on non DOM element nodes (bug #7500)." ); }); }); test("val()", function() { - expect(25); + expect(26); document.getElementById("text1").value = "bla"; equals( jQuery("#text1").val(), "bla", "Check for modified value of input element" ); @@ -509,6 +585,8 @@ test("val()", function() { var $button = jQuery("<button value='foobar'>text</button>").insertAfter("#button"); equals( $button.val(), "foobar", "Value retrieval on a button does not return innerHTML" ); equals( $button.val("baz").html(), "text", "Setting the value does not change innerHTML" ); + + equals( jQuery("<option/>").val("test").attr("value"), "test", "Setting value sets the value attribute" ); }); var testVal = function(valueObj) { @@ -913,4 +991,4 @@ test("addClass, removeClass, hasClass", function() { ok( jq.hasClass("cla.ss3")==false, "Check the dotted class has been removed" ); jq.removeClass("class4"); ok( jq.hasClass("class4")==false, "Check the class has been properly removed" ); -});
\ No newline at end of file +}); diff --git a/test/unit/core.js b/test/unit/core.js index f0bf24cd5..75d3e0e2c 100644 --- a/test/unit/core.js +++ b/test/unit/core.js @@ -12,7 +12,7 @@ test("Basic requirements", function() { }); test("jQuery()", function() { - expect(25); + expect(29); // Basic constructor's behavior @@ -96,6 +96,17 @@ test("jQuery()", function() { // manually clean up detached elements elem.remove(); + + equals( jQuery(" <div/> ").length, 1, "Make sure whitespace is trimmed." ); + equals( jQuery(" a<div/>b ").length, 1, "Make sure whitespace and other characters are trimmed." ); + + var long = ""; + for ( var i = 0; i < 128; i++ ) { + long += "12345678"; + } + + equals( jQuery(" <div>" + long + "</div> ").length, 1, "Make sure whitespace is trimmed on long strings." ); + equals( jQuery(" a<div>" + long + "</div>b ").length, 1, "Make sure whitespace and other characters are trimmed on long strings." ); }); test("selector state", function() { @@ -642,7 +653,7 @@ test("first()/last()", function() { }); test("map()", function() { - expect(7); + expect(8); same( jQuery("#ap").map(function(){ @@ -683,6 +694,12 @@ test("map()", function() { }); equals( mapped.length, scripts.length, "Map an array(-like) to a hash" ); + var nonsense = document.getElementsByTagName("asdf"); + var mapped = jQuery.map( nonsense, function( v, k ){ + return v; + }); + equals( mapped.length, nonsense.length, "Map an empty array(-like) to a hash" ); + var flat = jQuery.map( Array(4), function( v, k ){ return k % 2 ? k : [k,k,k];//try mixing array and regular returns }); diff --git a/test/unit/css.js b/test/unit/css.js index ba0fab9f3..380709d12 100644 --- a/test/unit/css.js +++ b/test/unit/css.js @@ -397,7 +397,7 @@ test("jQuery.cssProps behavior, (bug #8402)", function() { test("widows & orphans #8936", function () { - var $p = jQuery("<p>").appendTo("#main").end(); + var $p = jQuery("<p>").appendTo("#qunit-fixture"); if ( "widows" in $p[0].style ) { expect(4); @@ -406,16 +406,16 @@ test("widows & orphans #8936", function () { orphans: 0 }); - equal( $p.css("widows"), 0, "widows correctly start with value 0"); - equal( $p.css("orphans"), 0, "orphans correctly start with value 0"); + equal( $p.css("widows") || jQuery.style( $p[0], "widows" ), 0, "widows correctly start with value 0"); + equal( $p.css("orphans") || jQuery.style( $p[0], "orphans" ), 0, "orphans correctly start with value 0"); $p.css({ widows: 3, orphans: 3 }); - equal( $p.css("widows"), 3, "widows correctly set to 3"); - equal( $p.css("orphans"), 3, "orphans correctly set to 3"); + equal( $p.css("widows") || jQuery.style( $p[0], "widows" ), 3, "widows correctly set to 3"); + equal( $p.css("orphans") || jQuery.style( $p[0], "orphans" ), 3, "orphans correctly set to 3"); } else { expect(1); diff --git a/test/unit/deferred.js b/test/unit/deferred.js index c71fbdbe7..89c9c612d 100644 --- a/test/unit/deferred.js +++ b/test/unit/deferred.js @@ -145,7 +145,7 @@ jQuery.each( [ "", " - new operator" ], function( _, withNew ) { test( "jQuery.Deferred.pipe - filtering (done)", function() { - expect(3); + expect(4); var defer = jQuery.Deferred(), piped = defer.pipe(function( a, b ) { @@ -173,11 +173,15 @@ test( "jQuery.Deferred.pipe - filtering (done)", function() { jQuery.Deferred().reject().pipe(function() { ok( false, "pipe should not be called on reject" ); }); + + jQuery.Deferred().resolve().pipe( jQuery.noop ).done(function( value ) { + strictEqual( value, undefined, "pipe done callback can return undefined/null" ); + }); }); test( "jQuery.Deferred.pipe - filtering (fail)", function() { - expect(3); + expect(4); var defer = jQuery.Deferred(), piped = defer.pipe( null, function( a, b ) { @@ -205,6 +209,10 @@ test( "jQuery.Deferred.pipe - filtering (fail)", function() { jQuery.Deferred().resolve().pipe( null, function() { ok( false, "pipe should not be called on resolve" ); } ); + + jQuery.Deferred().reject().pipe( null, jQuery.noop ).fail(function( value ) { + strictEqual( value, undefined, "pipe fail callback can return undefined/null" ); + }); }); test( "jQuery.Deferred.pipe - deferred (done)", function() { diff --git a/test/unit/effects.js b/test/unit/effects.js index 6fbbbfe39..864c4a400 100644 --- a/test/unit/effects.js +++ b/test/unit/effects.js @@ -32,7 +32,8 @@ test("show()", function() { hiddendiv.css("display",""); - var pass = true, div = jQuery("#qunit-fixture div"); + var pass = true; + div = jQuery("#qunit-fixture div"); div.show().each(function(){ if ( this.style.display == "none" ) pass = false; }); @@ -582,7 +583,7 @@ jQuery.checkOverflowDisplay = function(){ equals(jQuery.css( this, "display" ), "inline", "Display shouldn't be tampered with."); start(); -} +}; test( "jQuery.fx.prototype.cur()", 6, function() { var div = jQuery( "<div></div>" ).appendTo( "#qunit-fixture" ).css({ @@ -694,8 +695,8 @@ jQuery.each( { jQuery(elem).css(prop,prop == "opacity" ? 0 : "0px"); return 0; } -}, function(fn, f){ - jQuery.each( { +}, function( fn, f ) { + jQuery.each({ "show": function(elem,prop){ jQuery(elem).hide().addClass("wide"+prop); return "show"; @@ -901,7 +902,7 @@ jQuery.makeTest = function( text ){ .after( elem ); return elem; -} +}; jQuery.makeTest.id = 1; @@ -922,34 +923,42 @@ test("jQuery.show('fast') doesn't clear radio buttons (bug #1095)", function () test("animate with per-property easing", function(){ - expect(3); + expect(5); stop(); - var _test1_called = false; - var _test2_called = false; - var _default_test_called = false; - - jQuery.easing["_test1"] = function() { + var data = { a:0, b:0, c:0 }, + _test1_called = false, + _test2_called = false, + _default_test_called = false, + props = { + a: [ 100, "_test1" ], + b: [ 100, "_test2" ], + c: 100 + }; + + jQuery.easing["_test1"] = function(p) { _test1_called = true; + return p; }; - jQuery.easing["_test2"] = function() { + jQuery.easing["_test2"] = function(p) { _test2_called = true; + return p; }; - jQuery.easing["_default_test"] = function() { + jQuery.easing["_default_test"] = function(p) { _default_test_called = true; + return p; }; - jQuery({a:0,b:0,c:0}).animate({ - a: [100, "_test1"], - b: [100, "_test2"], - c: 100 - }, 400, "_default_test", function(){ + jQuery(data).animate( props, 400, "_default_test", function(){ start(); - ok(_test1_called, "Easing function (1) called"); - ok(_test2_called, "Easing function (2) called"); - ok(_default_test_called, "Easing function (_default) called"); + + ok( _test1_called, "Easing function (_test1) called" ); + ok( _test2_called, "Easing function (_test2) called" ); + ok( _default_test_called, "Easing function (_default) called" ); + equal( props.a[ 1 ], "_test1", "animate does not change original props (per-property easing would be lost)"); + equal( props.b[ 1 ], "_test2", "animate does not change original props (per-property easing would be lost)"); }); }); @@ -994,6 +1003,33 @@ test("animate unit-less properties (#4966)", 2, function() { }); }); +test( "animate properties missing px w/ opacity as last (#9074)", 2, function() { + expect( 6 ); + stop(); + var div = jQuery( "<div style='position: absolute; margin-left: 0; left: 0px;'></div>" ) + .appendTo( "#qunit-fixture" ); + function cssInt( prop ) { + return parseInt( div.css( prop ), 10 ); + } + equal( cssInt( "marginLeft" ), 0, "Margin left is 0" ); + equal( cssInt( "left" ), 0, "Left is 0" ); + div.animate({ + left: 200, + marginLeft: 200, + opacity: 0 + }, 1000); + setTimeout(function() { + var ml = cssInt( "marginLeft" ), + l = cssInt( "left" ); + notEqual( ml, 0, "Margin left is not 0 after partial animate" ); + notEqual( ml, 200, "Margin left is not 200 after partial animate" ); + notEqual( l, 0, "Left is not 0 after partial animate" ); + notEqual( l, 200, "Left is not 200 after partial animate" ); + div.stop().remove(); + start(); + }, 100); +}); + test("callbacks should fire in correct order (#9100)", function() { stop(); var a = 1, @@ -1008,4 +1044,4 @@ test("callbacks should fire in correct order (#9100)", function() { start(); } }); -});
\ No newline at end of file +}); diff --git a/test/unit/event.js b/test/unit/event.js index 0424538d6..13877e019 100644 --- a/test/unit/event.js +++ b/test/unit/event.js @@ -68,6 +68,22 @@ test("bind(), multiple events at once", function() { equals( mouseoverCounter, 1, "bind() with multiple events at once" ); }); +test("bind(), five events at once", function() { + expect(1); + + var count = 0, + handler = function(event) { + count++; + }; + + jQuery("#firstp").bind("click mouseover foo bar baz", handler) + .trigger("click").trigger("mouseover") + .trigger("foo").trigger("bar") + .trigger("baz"); + + equals( count, 5, "bind() five events at once" ); +}); + test("bind(), multiple events at once and namespaces", function() { expect(7); diff --git a/test/unit/manipulation.js b/test/unit/manipulation.js index b71b6962e..972cfaf91 100644 --- a/test/unit/manipulation.js +++ b/test/unit/manipulation.js @@ -227,7 +227,7 @@ test("unwrap()", function() { }); var testAppend = function(valueObj) { - expect(40); + expect(41); var defaultText = "Try them out:" var result = jQuery("#first").append(valueObj("<b>buga</b>")); equals( result.text(), defaultText + "buga", "Check if text appending works" ); @@ -344,6 +344,17 @@ var testAppend = function(valueObj) { equals( $radio[0].checked, true, "Reappending radios uphold which radio is checked" ); equals( $radioNot[0].checked, false, "Reappending radios uphold not being checked" ); QUnit.reset(); + + var prev = jQuery("#sap").children().length; + + jQuery("#sap").append( + "<span></span>", + "<span></span>", + "<span></span>" + ); + + equals( jQuery("#sap").children().length, prev + 3, "Make sure that multiple arguments works." ); + QUnit.reset(); } test("append(String|Element|Array<Element>|jQuery)", function() { diff --git a/test/unit/support.js b/test/unit/support.js new file mode 100644 index 000000000..9d99529a1 --- /dev/null +++ b/test/unit/support.js @@ -0,0 +1,32 @@ +module("support", { teardown: moduleTeardown }); + +function supportIFrameTest( title, url, noDisplay, func ) { + + if ( noDisplay !== true ) { + func = noDisplay; + noDisplay = false; + } + + test( title, function() { + var iframe; + + stop(); + window.supportCallback = function() { + var self = this, + args = arguments; + setTimeout( function() { + window.supportCallback = undefined; + iframe.remove(); + func.apply( self, args ); + start(); + }, 0 ); + }; + iframe = jQuery( "<div/>" ).css( "display", noDisplay ? "none" : "block" ).append( + jQuery( "<iframe/>" ).attr( "src", "data/support/" + url + ".html" ) + ).appendTo( "body" ); + }); +} + +supportIFrameTest( "proper boxModel in compatMode CSS1Compat (IE6 and IE7)", "boxModelIE", function( compatMode, boxModel ) { + ok( compatMode !== "CSS1Compat" || boxModel, "boxModel properly detected" ); +}); diff --git a/version.txt b/version.txt index 0a0196e21..29349dd55 100644 --- a/version.txt +++ b/version.txt @@ -1 +1 @@ -1.6pre
\ No newline at end of file +1.6.1pre
\ No newline at end of file |