X-Git-Url: http://git.asbjorn.it/?a=blobdiff_plain;f=src%2Fjquery%2Fjquery.js;h=7f7ef08d1089f80fefcb767f037cddb58752886a;hb=b8d46f7b6a38d92d9e0ad0b0df12c773bcf1c634;hp=f2e9c1816e1c32e9467534ef9cf74589e257f30e;hpb=7cc550727c4e0bcd93c5d435f0799f568fc74dfa;p=jquery.git diff --git a/src/jquery/jquery.js b/src/jquery/jquery.js index f2e9c18..7f7ef08 100644 --- a/src/jquery/jquery.js +++ b/src/jquery/jquery.js @@ -22,16 +22,16 @@ window.undefined = window.undefined; */ var jQuery = function(a,c) { - // Shortcut for document ready (because $(document).each() is silly) + // Shortcut for document ready if ( a && typeof a == "function" && jQuery.fn.ready && !a.nodeType && a[0] == undefined ) // Safari reports typeof on DOM NodeLists as a function return jQuery(document).ready(a); // Make sure that a selection was provided - a = a || jQuery.context || document; + a = a || document; // Watch for when a jQuery object is passed as the selector if ( a.jquery ) - return jQuery( jQuery.merge( a, [] ) ); + return jQuery( jQuery.makeArray( a ) ); // Watch for when a jQuery object is passed at the context if ( c && c.jquery ) @@ -42,15 +42,15 @@ var jQuery = function(a,c) { return new jQuery(a,c); // Handle HTML strings - if ( a.constructor == String ) { + if ( typeof a == "string" ) { var m = /^[^<]*(<.+>)[^>]*$/.exec(a); if ( m ) a = jQuery.clean( [ m[1] ] ); } // Watch for when an array is passed in - this.get( a.constructor == Array || a.length && a != window && !a.nodeType && a[0] != undefined && a[0].nodeType ? + this.set( a.constructor == Array || a.length && a != window && !a.nodeType && a[0] != undefined && a[0].nodeType ? // Assume that it is an array of DOM Elements - jQuery.merge( a, [] ) : + jQuery.makeArray( a ) : // Find the matching elements and save them for later jQuery.find( a, c ) ); @@ -68,19 +68,19 @@ var jQuery = function(a,c) { // Map over the $ in case of overwrite if ( typeof $ != "undefined" ) jQuery._$ = $; + +// Map the jQuery namespace to the '$' one +var $ = jQuery; /** - * This function accepts a string containing a CSS selector, - * basic XPath, or raw HTML, which is then used to match a set of elements. - * The HTML string is different from the traditional selectors in that - * it creates the DOM elements representing that HTML string, on the fly, - * to be (assumedly) inserted into the document later. + * This function accepts a string containing a CSS or + * basic XPath selector which is then used to match a set of elements. * * The core functionality of jQuery centers around this function. * Everything in jQuery is based upon this, or uses this in some way. * The most basic use of this function is to pass in an expression * (usually consisting of CSS or XPath), which then finds all matching - * elements and remembers them for later use. + * elements. * * By default, $() looks for DOM elements within the context of the * current HTML document. @@ -90,29 +90,36 @@ if ( typeof $ != "undefined" ) * @before

one

two

three

* @result [

two

] * - * @example $("

Hello

").appendTo("#body") - * @desc Creates a div element (and all of its contents) dynamically, - * and appends it to the element with the ID of body. Internally, an - * element is created and it's innerHTML property set to the given markup. - * It is therefore both quite flexible and limited. + * @example $("input:radio", document.forms[0]) + * @desc Searches for all inputs of type radio within the first form in the document + * + * @example $("div", xml.responseXML) + * @desc This finds all div elements within the specified XML document. * * @name $ - * @param String expr An expression to search with, or a string of HTML to create on the fly. + * @param String expr An expression to search with + * @param Element context (optional) A DOM Element, or Document, representing the base context. * @cat Core * @type jQuery + * @see $(Element) + * @see $(Element) */ - + /** - * This function accepts a string containing a CSS selector, or - * basic XPath, which is then used to match a set of elements with the - * context of the specified DOM element, or document + * This function accepts a string of raw HTML. * - * @example $("div", xml.responseXML) - * @desc This finds all div elements within the specified XML document. + * The HTML string is different from the traditional selectors in that + * it creates the DOM elements representing that HTML string, on the fly, + * to be (assumedly) inserted into the document later. + * + * @example $("

Hello

").appendTo("#body") + * @desc Creates a div element (and all of its contents) dynamically, + * and appends it to the element with the ID of body. Internally, an + * element is created and it's innerHTML property set to the given markup. + * It is therefore both quite flexible and limited. * * @name $ - * @param String expr An expression to search with. - * @param Element context A DOM Element, or Document, representing the base context. + * @param String html A string of HTML to create on the fly. * @cat Core * @type jQuery */ @@ -155,6 +162,8 @@ if ( typeof $ != "undefined" ) * technically, chainable - there really isn't much use for chaining against it. * You can have as many $(document).ready events on your page as you like. * + * See ready(Function) for details about the ready event. + * * @example $(function(){ * // Document is ready * }); @@ -181,9 +190,6 @@ if ( typeof $ != "undefined" ) * @type jQuery */ -// Map the jQuery namespace to the '$' one -var $ = jQuery; - jQuery.fn = jQuery.prototype = { /** * The current version of jQuery. @@ -251,37 +257,34 @@ jQuery.fn = jQuery.prototype = { * @param Number num Access the element in the Nth position. * @cat Core */ + get: function( num ) { + return num == undefined ? + + // Return a 'clean' array + jQuery.makeArray( this ) : + // Return just the object + this[num]; + }, + /** * Set the jQuery object to an array of elements. * - * @example $("img").get([ document.body ]); - * @result $("img").get() == [ document.body ] + * @example $("img").set([ document.body ]); + * @result $("img").set() == [ document.body ] * * @private - * @name get + * @name set * @type jQuery * @param Elements elems An array of elements * @cat Core */ - get: function( num ) { - // Watch for when an array (of elements) is passed in - if ( num && num.constructor == Array ) { - - // Use a tricky hack to make the jQuery object - // look and feel like an array - this.length = 0; - [].push.apply( this, num ); - - return this; - } else - return num == undefined ? - - // Return a 'clean' array - jQuery.merge( this, [] ) : - - // Return just the object - this[num]; + set: function( array ) { + // Use a tricky hack to make the jQuery object + // look and feel like an array + this.length = 0; + [].push.apply( this, array ); + return this; }, /** @@ -294,17 +297,12 @@ jQuery.fn = jQuery.prototype = { * argument representing the position of the element in the matched * set. * - * @example $("img").each(function(){ - * this.src = "test.jpg"; - * }); - * @before - * @result - * * @example $("img").each(function(i){ - * alert( "Image #" + i + " is " + this ); + * this.src = "test" + i + ".jpg"; * }); * @before - * @result + * @result + * @desc Iterates over two images and sets their src property * * @name each * @type jQuery @@ -378,6 +376,10 @@ jQuery.fn = jQuery.prototype = { /** * Set a single property to a value, on all matched elements. * + * Note that you can't set the name property of input elements in IE. + * Use $(html) or $().append(html) or $().html(html) to create elements + * on the fly including the name property. + * * @example $("img").attr("src","test.jpg"); * @before * @result @@ -390,7 +392,7 @@ jQuery.fn = jQuery.prototype = { */ attr: function( key, value, type ) { // Check to see if we're setting style values - return key.constructor != String || value != undefined ? + return typeof key != "string" || value != undefined ? this.each(function(){ // See if we're setting a hash of styles if ( value == undefined ) @@ -485,7 +487,27 @@ jQuery.fn = jQuery.prototype = { * @type String * @cat DOM */ + + /** + * Set the text contents of all matched elements. This has the same + * effect as calling .html() with your specified string. + * + * @example $("p").text("Some new text."); + * @before

Test Paragraph.

+ * @result

Some new text.

+ * + * @param String val The text value to set the contents of the element to. + * + * @name text + * @type String + * @cat DOM + */ text: function(e) { + // A surprisingly high number of people expect the + // .text() method to do this, so lets do it! + if ( typeof e == "string" ) + return this.html( e ); + e = e || this; var t = ""; for ( var j = 0; j < e.length; j++ ) { @@ -773,7 +795,7 @@ jQuery.fn = jQuery.prototype = { end: function() { if( !(this.stack && this.stack.length) ) return this; - return this.get( this.stack.pop() ); + return this.set( this.stack.pop() ); }, /** @@ -863,7 +885,7 @@ jQuery.fn = jQuery.prototype = { for ( var i = 0; i < t.length; i++ ) if ( jQuery.filter(t[i],[a]).r.length ) return a; - return false; + return null; }) || t.constructor == Boolean && @@ -904,8 +926,8 @@ jQuery.fn = jQuery.prototype = { * @cat DOM/Traversing */ not: function(t) { - return this.pushStack( t.constructor == String ? - jQuery.filter(t,this,false).r : + return this.pushStack( typeof t == "string" ? + jQuery.filter(t,this,true).r : jQuery.grep(this,function(a){ return a != t; }), arguments ); }, @@ -951,14 +973,16 @@ jQuery.fn = jQuery.prototype = { * @cat DOM/Traversing */ add: function(t) { - return this.pushStack( jQuery.merge( this, t.constructor == String ? - jQuery.find(t) : t.constructor == Array ? t : [t] ), arguments ); + return this.pushStack( jQuery.merge( + this.get(), typeof t == "string" ? + jQuery.find(t) : + t.constructor == Array ? t : [t] ), arguments ); }, /** * Checks the current selection against an expression and returns true, - * if the selection fits the given expression. Does return false, if the - * selection does not fit or the expression is not valid. + * if at least one element of the selection fits the given expression. + * Does return false, if no element fits or the expression is not valid. * * @example $("input[@type='checkbox']").parent().is("form") * @before
@@ -985,38 +1009,30 @@ jQuery.fn = jQuery.prototype = { }, /** - * - * * @private * @name domManip * @param Array args - * @param Boolean table - * @param Number int + * @param Boolean table Insert TBODY in TABLEs if one is not found. + * @param Number dir If dir<0, process args in reverse order. * @param Function fn The function doing the DOM manipulation. * @type jQuery * @cat Core */ domManip: function(args, table, dir, fn){ - var clone = this.size() > 1; + var clone = this.length > 1; var a = jQuery.clean(args); + if ( dir < 0 ) + a.reverse(); return this.each(function(){ var obj = this; - if ( table && this.nodeName.toUpperCase() == "TABLE" && a[0].nodeName.toUpperCase() != "THEAD" ) { - var tbody = this.getElementsByTagName("tbody"); + if ( table && this.nodeName.toUpperCase() == "TABLE" && a[0].nodeName.toUpperCase() == "TR" ) + obj = this.getElementsByTagName("tbody")[0] || this.appendChild(document.createElement("tbody")); - if ( !tbody.length ) { - obj = document.createElement("tbody"); - this.appendChild( obj ); - } else - obj = tbody[0]; - } + for ( var i=0; i < a.length; i++ ) + fn.apply( obj, [ clone ? a[i].cloneNode(true) : a[i] ] ); - for ( var i = ( dir < 0 ? a.length - 1 : 0 ); - i != ( dir < 0 ? dir : a.length ); i += dir ) { - fn.apply( obj, [ clone ? a[i].cloneNode(true) : a[i] ] ); - } }); }, @@ -1031,8 +1047,8 @@ jQuery.fn = jQuery.prototype = { * @cat Core */ pushStack: function(a,args) { - var fn = args && args[args.length-1]; - var fn2 = args && args[args.length-2]; + var fn = args && args.length > 1 && args[args.length-1]; + var fn2 = args && args.length > 2 && args[args.length-2]; if ( fn && fn.constructor != Function ) fn = null; if ( fn2 && fn2.constructor != Function ) fn2 = null; @@ -1040,15 +1056,15 @@ jQuery.fn = jQuery.prototype = { if ( !fn ) { if ( !this.stack ) this.stack = []; this.stack.push( this.get() ); - this.get( a ); + this.set( a ); } else { var old = this.get(); - this.get( a ); + this.set( a ); if ( fn2 && a.length || !fn2 ) - this.each( fn2 || fn ).get( old ); + this.each( fn2 || fn ).set( old ); else - this.get( old ).each( fn ); + this.set( old ).each( fn ); } return this; @@ -1056,56 +1072,73 @@ jQuery.fn = jQuery.prototype = { }; /** - * Extends the jQuery object itself. Can be used to add both static - * functions and plugin methods. + * Extends the jQuery object itself. Can be used to add functions into + * the jQuery namespace and to add plugin methods (plugins). * - * @example $.fn.extend({ + * @example jQuery.fn.extend({ * check: function() { - * this.each(function() { this.checked = true; }); + * return this.each(function() { this.checked = true; }); * ), * uncheck: function() { - * this.each(function() { this.checked = false; }); + * return this.each(function() { this.checked = false; }); * } * }); * $("input[@type=checkbox]").check(); * $("input[@type=radio]").uncheck(); * @desc Adds two plugin methods. * - * @private - * @name extend - * @param Object obj + * @example jQuery.extend({ + * min: function(a, b) { return a < b ? a : b; }, + * max: function(a, b) { return a > b ? a : b; } + * }); + * @desc Adds two functions into the jQuery namespace + * + * @name $.extend + * @param Object prop The object that will be merged into the jQuery object * @type Object * @cat Core */ /** - * Extend one object with another, returning the original, + * Extend one object with one or more others, returning the original, * modified, object. This is a great utility for simple inheritance. * * @example var settings = { validate: false, limit: 5, name: "foo" }; * var options = { validate: true, name: "bar" }; * jQuery.extend(settings, options); * @result settings == { validate: true, limit: 5, name: "bar" } + * @desc Merge settings and options, modifying settings + * + * @example var defaults = { validate: false, limit: 5, name: "foo" }; + * var options = { validate: true, name: "bar" }; + * var settings = jQuery.extend({}, defaults, options); + * @result settings == { validate: true, limit: 5, name: "bar" } + * @desc Merge defaults and options, without modifying the defaults * * @name $.extend - * @param Object obj The object to extend - * @param Object prop The object that will be merged into the first. + * @param Object target The object to extend + * @param Object prop1 The object that will be merged into the first. + * @param Object propN (optional) More objects to merge into the first * @type Object * @cat Javascript */ -jQuery.extend = jQuery.fn.extend = function(obj,prop) { - // Watch for the case where null or undefined gets passed in by accident - if ( arguments.length > 1 && (prop === null || prop == undefined) ) - return obj; - - // If no property object was provided, then we're extending jQuery - if ( !prop ) { prop = obj; obj = this; } - - // Extend the base object - for ( var i in prop ) obj[i] = prop[i]; +jQuery.extend = jQuery.fn.extend = function() { + // copy reference to target object + var target = arguments[0], + a = 1; + + // extend jQuery itself if only one argument is passed + if ( arguments.length == 1 ) { + target = this; + a = 0; + } + var prop; + while (prop = arguments[a++]) + // Extend the base object + for ( var i in prop ) target[i] = prop[i]; // Return the modified object - return obj; + return target; }; jQuery.extend({ @@ -1121,7 +1154,7 @@ jQuery.extend({ jQuery.each( jQuery.macros.axis, function(i,n){ jQuery.fn[ i ] = function(a) { var ret = jQuery.map(this,n); - if ( a && a.constructor == String ) + if ( a && typeof a == "string" ) ret = jQuery.filter(a,ret).r; return this.pushStack( ret, arguments ); }; @@ -1190,6 +1223,7 @@ jQuery.extend({ * @type Object * @cat Javascript */ + // args is for internal usage only each: function( obj, fn, args ) { if ( obj.length == undefined ) for ( var i in obj ) @@ -1293,11 +1327,6 @@ jQuery.extend({ ret = elem.style[prop]; - } else if (elem.currentStyle) { - - var newProp = prop.replace(/\-(\w)/g,function(m,c){return c.toUpperCase();}); - ret = elem.currentStyle[prop] || elem.currentStyle[newProp]; - } else if (document.defaultView && document.defaultView.getComputedStyle) { if (prop == "cssFloat" || prop == "styleFloat") @@ -1312,9 +1341,15 @@ jQuery.extend({ ret = 'none'; else jQuery.swap(elem, { display: 'block' }, function() { - ret = document.defaultView.getComputedStyle(this,null).getPropertyValue(prop); + var c = document.defaultView.getComputedStyle(this, ''); + ret = c && c.getPropertyValue(prop) || ''; }); + } else if (elem.currentStyle) { + + var newProp = prop.replace(/\-(\w)/g,function(m,c){return c.toUpperCase();}); + ret = elem.currentStyle[prop] || elem.currentStyle[newProp]; + } return ret; @@ -1324,22 +1359,40 @@ jQuery.extend({ var r = []; for ( var i = 0; i < a.length; i++ ) { var arg = a[i]; - if ( arg.constructor == String ) { // Convert html string into DOM nodes + if ( typeof arg == "string" ) { // Convert html string into DOM nodes // Trim whitespace, otherwise indexOf won't work as expected - var s = jQuery.trim(arg), div = document.createElement("div"), wrap = [0,"",""]; + var s = jQuery.trim(arg), s3 = s.substring(0,3), s6 = s.substring(0,6), + div = document.createElement("div"), wrap = [0,"",""]; - if ( !s.indexOf("", ""]; - else if ( !s.indexOf("", ""]; - else if ( !s.indexOf("", ""]; // tbody auto-inserted - else if ( !s.indexOf("", ""]; + else if ( s3 == " matched above wrap = [3, "", "
"]; // Go to html and back, then peel off extra wrappers div.innerHTML = wrap[1] + s + wrap[2]; while ( wrap[0]-- ) div = div.firstChild; + + // Remove IE's autoinserted from table fragments + if ( jQuery.browser.msie ) { + var tb = null; + // String was a , *may* have spurious + if ( s6 == " or + else if ( wrap[1] == "
" && s.indexOf("= 0 ; --n ) + if ( tb[n].nodeName.toUpperCase() == "TBODY" && !tb[n].childNodes.length ) + tb[n].parentNode.removeChild(tb[n]); + } + } + arg = div.childNodes; } @@ -1354,9 +1407,19 @@ jQuery.extend({ return r; }, + nth: function(cur,result,dir){ + result = result || 1; + var num = 0; + for ( ; cur; cur = cur[dir] ) { + if ( cur.nodeType == 1 ) num++; + if ( num == result || result == "even" && num % 2 == 0 && num > 1 || + result == "odd" && num % 2 == 1 ) return cur; + } + }, + expr: { "": "m[2]== '*'||a.nodeName.toUpperCase()==m[2].toUpperCase()", - "#": "a.getAttribute('id')&&a.getAttribute('id')==m[2]", + "#": "a.getAttribute('id')==m[2]", ":": { // Position Checks lt: "i=0", - "": "z" + "": "z", + _resort: function(m){ + return ["", m[1], m[3], m[2], m[5]]; + }, + _prefix: "z=jQuery.attr(a,m[3]);" }, "[": "jQuery.find(m[2],a).length" }, + /** + * All elements on a specified axis. + * + * @private + * @name $.sibling + * @type Array + * @param Element elem The element to find all the siblings of (including itself). + * @cat DOM/Traversing + */ + sibling: function( n, elem ) { + var r = []; + + for ( ; n; n = n.nextSibling ) { + if ( n.nodeType == 1 && (!elem || n != elem) ) + r.push( n ); + } + + return r; + }, + token: [ "\\.\\.|/\\.\\.", "a.parentNode", ">|/", "jQuery.sibling(a.firstChild)", - "\\+", "jQuery.sibling(a).next", + "\\+", "jQuery.nth(a,2,'nextSibling')", "~", function(a){ - var s = jQuery.sibling(a); - return s.n >= 0 ? s.slice(s.n+1) : []; + var s = jQuery.sibling(a.parentNode.firstChild) + return s.slice(0, jQuery.inArray(a,s)); } ], @@ -1432,31 +1519,36 @@ jQuery.extend({ * @cat Core */ find: function( t, context ) { + // Quickly handle non-string expressions + if ( typeof t != "string" ) + return [ t ]; + // Make sure that the context is a DOM Element if ( context && context.nodeType == undefined ) context = null; // Set the correct context (if none is provided) - context = context || jQuery.context || document; - - if ( t.constructor != String ) return [t]; + context = context || document; + // Handle the common XPath // expression if ( !t.indexOf("//") ) { context = context.documentElement; t = t.substr(2,t.length); + + // And the / root expression } else if ( !t.indexOf("/") ) { context = context.documentElement; t = t.substr(1,t.length); - // FIX Assume the root element is right :( if ( t.indexOf("/") >= 1 ) t = t.substr(t.indexOf("/"),t.length); } - var ret = [context]; - var done = []; - var last = null; + // Initialize the search + var ret = [context], done = [], last = null; - while ( t.length > 0 && last != t ) { + // Continue while a selector expression exists, and while + // we're no longer looping upon ourselves + while ( t && last != t ) { var r = []; last = t; @@ -1464,69 +1556,171 @@ jQuery.extend({ var foundToken = false; - for ( var i = 0; i < jQuery.token.length; i += 2 ) { - if ( foundToken ) continue; - - var re = new RegExp("^(" + jQuery.token[i] + ")"); - var m = re.exec(t); - - if ( m ) { - r = ret = jQuery.map( ret, jQuery.token[i+1] ); - t = jQuery.trim( t.replace( re, "" ) ); - foundToken = true; + // An attempt at speeding up child selectors that + // point to a specific element tag + var re = /^[\/>]\s*([a-z0-9*-]+)/i; + var m = re.exec(t); + + if ( m ) { + // Perform our own iteration and filter + for ( var i = 0; i < ret.length; i++ ) + for ( var c = ret[i].firstChild; c; c = c.nextSibling ) + if ( c.nodeType == 1 && ( c.nodeName == m[1].toUpperCase() || m[1] == "*" ) ) + r.push( c ); + + ret = r; + t = jQuery.trim( t.replace( re, "" ) ); + foundToken = true; + } else { + // Look for pre-defined expression tokens + for ( var i = 0; i < jQuery.token.length; i += 2 ) { + // Attempt to match each, individual, token in + // the specified order + var re = new RegExp("^(" + jQuery.token[i] + ")"); + var m = re.exec(t); + + // If the token match was found + if ( m ) { + // Map it against the token's handler + r = ret = jQuery.map( ret, jQuery.token[i+1].constructor == Function ? + jQuery.token[i+1] : + function(a){ return eval(jQuery.token[i+1]); }); + + // And remove the token + t = jQuery.trim( t.replace( re, "" ) ); + foundToken = true; + break; + } } } - if ( !foundToken ) { + // See if there's still an expression, and that we haven't already + // matched a token + if ( t && !foundToken ) { + // Handle multiple expressions if ( !t.indexOf(",") || !t.indexOf("|") ) { + // Clean teh result set if ( ret[0] == context ) ret.shift(); - done = jQuery.merge( done, ret ); + + // Merge the result sets + jQuery.merge( done, ret ); + + // Reset the context r = ret = [context]; + + // Touch up the selector string t = " " + t.substr(1,t.length); + } else { - var re2 = /^([#.]?)([a-z0-9\\*_-]*)/i; + // Optomize for the case nodeName#idName + var re2 = /^([a-z0-9_-]+)(#)([a-z0-9\\*_-]*)/i; var m = re2.exec(t); + + // Re-organize the results, so that they're consistent + if ( m ) { + m = [ 0, m[2], m[3], m[1] ]; - if ( m[1] == "#" ) { - // Ummm, should make this work in all XML docs - var oid = document.getElementById(m[2]); - r = ret = oid ? [oid] : []; - t = t.replace( re2, "" ); } else { - if ( !m[2] || m[1] == "." ) m[2] = "*"; + // Otherwise, do a traditional filter check for + // ID, class, and element selectors + re2 = /^([#.]?)([a-z0-9\\*_-]*)/i; + m = re2.exec(t); + } + + // Try to do a global search by ID, where we can + if ( m[1] == "#" && ret[ret.length-1].getElementById ) { + // Optimization for HTML document case + var oid = ret[ret.length-1].getElementById(m[2]); + + // Do a quick check for node name (where applicable) so + // that div#foo searches will be really fast + ret = r = oid && + (!m[3] || oid.nodeName == m[3].toUpperCase()) ? [oid] : []; + + // Use the DOM 0 shortcut for the body element + } else if ( m[1] == "" && m[2] == "body" ) { + ret = r = [ document.body ]; + } else { + // Pre-compile a regular expression to handle class searches + if ( m[1] == "." ) + var rec = new RegExp("(^|\\s)" + m[2] + "(\\s|$)"); + + // We need to find all descendant elements, it is more + // efficient to use getAll() when we are already further down + // the tree - we try to recognize that here for ( var i = 0; i < ret.length; i++ ) - r = jQuery.merge( r, - m[2] == "*" ? - jQuery.getAll(ret[i]) : - ret[i].getElementsByTagName(m[2]) + jQuery.merge( r, + m[1] != "" && ret.length != 1 ? + jQuery.getAll( ret[i], [], m[1], m[2], rec ) : + ret[i].getElementsByTagName( m[1] != "" || m[0] == "" ? "*" : m[2] ) ); + + // It's faster to filter by class and be done with it + if ( m[1] == "." && ret.length == 1 ) + r = jQuery.grep( r, function(e) { + return rec.test(e.className); + }); + + // Same with ID filtering + if ( m[1] == "#" && ret.length == 1 ) { + // Remember, then wipe out, the result set + var tmp = r; + r = []; + + // Then try to find the element with the ID + for ( var i = 0; i < tmp.length; i++ ) + if ( tmp[i].getAttribute("id") == m[2] ) { + r = [ tmp[i] ]; + break; + } + } + + ret = r; } + + t = t.replace( re2, "" ); } } + // If a selector string still exists if ( t ) { + // Attempt to filter it var val = jQuery.filter(t,r); ret = r = val.r; t = jQuery.trim(val.t); } } + // Remove the root context if ( ret && ret[0] == context ) ret.shift(); - done = jQuery.merge( done, ret ); + + // And combine the results + jQuery.merge( done, ret ); return done; }, - getAll: function(o,r) { - r = r || []; - var s = o.childNodes; - for ( var i = 0; i < s.length; i++ ) - if ( s[i].nodeType == 1 ) { - r.push( s[i] ); - jQuery.getAll( s[i], r ); + getAll: function( o, r, token, name, re ) { + for ( var s = o.firstChild; s; s = s.nextSibling ) + if ( s.nodeType == 1 ) { + var add = true; + + if ( token == "." ) + add = s.className && re.test(s.className); + else if ( token == "#" ) + add = s.getAttribute('id') == name; + + if ( add ) + r.push( s ); + + if ( token == "#" && r.length ) break; + + if ( s.firstChild ) + jQuery.getAll( s, r, token, name, re ); } + return r; }, @@ -1541,33 +1735,42 @@ jQuery.extend({ value: "value", disabled: "disabled", checked: "checked", - readonly: "readOnly" + readonly: "readOnly", + selected: "selected" }; // IE actually uses filters for opacity ... elem is actually elem.style - if (name == "opacity" && jQuery.browser.msie && value != undefined) { + if ( name == "opacity" && jQuery.browser.msie && value != undefined ) { // IE has trouble with opacity if it does not have layout - // Would prefer to check element.hasLayout first but don't have access to the element here - elem['zoom'] = 1; - if (value == 1) // Remove filter to avoid more IE weirdness - return elem["filter"] = elem["filter"].replace(/alpha\([^\)]*\)/gi,""); - else - return elem["filter"] = elem["filter"].replace(/alpha\([^\)]*\)/gi,"") + "alpha(opacity=" + value * 100 + ")"; - } else if (name == "opacity" && jQuery.browser.msie) { - return elem["filter"] ? parseFloat( elem["filter"].match(/alpha\(opacity=(.*)\)/)[1] )/100 : 1; + // Force it by setting the zoom level + elem.zoom = 1; + + // Set the alpha filter to set the opacity + return elem.filter = elem.filter.replace(/alpha\([^\)]*\)/gi,"") + + ( value == 1 ? "" : "alpha(opacity=" + value * 100 + ")" ); + + } else if ( name == "opacity" && jQuery.browser.msie ) { + return elem.filter ? + parseFloat( elem.filter.match(/alpha\(opacity=(.*)\)/)[1] ) / 100 : 1; } // Mozilla doesn't play well with opacity 1 - if (name == "opacity" && jQuery.browser.mozilla && value == 1) value = 0.9999; + if ( name == "opacity" && jQuery.browser.mozilla && value == 1 ) + value = 0.9999; + // Certain attributes only work when accessed via the old DOM 0 way if ( fix[name] ) { if ( value != undefined ) elem[fix[name]] = value; return elem[fix[name]]; - } else if( value == undefined && jQuery.browser.msie && elem.nodeName && elem.nodeName.toUpperCase() == 'FORM' && (name == 'action' || name == 'method') ) { + + } else if ( value == undefined && jQuery.browser.msie && elem.nodeName && elem.nodeName.toUpperCase() == 'FORM' && (name == 'action' || name == 'method') ) { return elem.getAttributeNode(name).nodeValue; - } else if ( elem.tagName ) { // IE elem.getAttribute passes even for style + + // IE elem.getAttribute passes even for style + } else if ( elem.tagName ) { if ( value != undefined ) elem.setAttribute( name, value ); return elem.getAttribute( name ); + } else { name = name.replace(/-([a-z])/ig,function(z,b){return b.toUpperCase();}); if ( value != undefined ) elem[name] = value; @@ -1581,7 +1784,7 @@ jQuery.extend({ "\\[ *(@)S *([!*$^=]*) *('?\"?)(.*?)\\4 *\\]", // Match: [div], [div p] - "(\\[)\s*(.*?)\s*\\]", + "(\\[)\\s*(.*?)\\s*\\]", // Match: :contains('foo') "(:)S\\(\"?'?([^\\)]*?)\"?'?\\)", @@ -1591,10 +1794,7 @@ jQuery.extend({ ], filter: function(t,r,not) { - // Figure out if we're doing regular, or inverse, filtering - var g = not !== false ? jQuery.grep : - function(a,f) {return jQuery.grep(a,f,true);}; - + // Look for common filter expressions while ( t && /^[a-z[({<*:.#]/i.test(t) ) { var p = jQuery.parse; @@ -1610,8 +1810,8 @@ jQuery.extend({ if ( m ) { // Re-organize the first match - if ( !i ) - m = ["",m[1], m[3], m[2], m[5]]; + if ( jQuery.expr[ m[1] ]._resort ) + m = jQuery.expr[ m[1] ]._resort( m ); // Remove what we just matched t = t.replace( re, "" ); @@ -1623,21 +1823,30 @@ jQuery.extend({ // :not() is a special case that can be optimized by // keeping it out of the expression list if ( m[1] == ":" && m[2] == "not" ) - r = jQuery.filter(m[3],r,false).r; + r = jQuery.filter(m[3], r, true).r; + + // Handle classes as a special case (this will help to + // improve the speed, as the regexp will only be compiled once) + else if ( m[1] == "." ) { + + var re = new RegExp("(^|\\s)" + m[2] + "(\\s|$)"); + r = jQuery.grep( r, function(e){ + return re.test(e.className || ''); + }, not); // Otherwise, find the expression to execute - else { + } else { var f = jQuery.expr[m[1]]; - if ( f.constructor != String ) + if ( typeof f != "string" ) f = jQuery.expr[m[1]][m[2]]; // Build a custom macro to enclose it eval("f = function(a,i){" + - ( m[1] == "@" ? "z=jQuery.attr(a,m[3]);" : "" ) + + ( jQuery.expr[ m[1] ]._prefix || "" ) + "return " + f + "}"); // Execute it against the current filter - r = g( r, f ); + r = jQuery.grep( r, f, not ); } } @@ -1680,36 +1889,23 @@ jQuery.extend({ return matched; }, - /** - * All elements on a specified axis. - * - * @private - * @name $.sibling - * @type Array - * @param Element elem The element to find all the siblings of (including itself). - * @cat DOM/Traversing - */ - sibling: function(elem, pos, not) { - var elems = []; - - if(elem) { - var siblings = elem.parentNode.childNodes; - for ( var i = 0; i < siblings.length; i++ ) { - if ( not === true && siblings[i] == elem ) continue; - - if ( siblings[i].nodeType == 1 ) - elems.push( siblings[i] ); - if ( siblings[i] == elem ) - elems.n = elems.length - 1; - } - } + makeArray: function( a ) { + var r = []; - return jQuery.extend( elems, { - last: elems.n == elems.length - 1, - cur: pos == "even" && elems.n % 2 == 0 || pos == "odd" && elems.n % 2 || elems[pos] == elem, - prev: elems[elems.n - 1], - next: elems[elems.n + 1] - }); + if ( a.constructor != Array ) { + for ( var i = 0; i < a.length; i++ ) + r.push( a[i] ); + } else + r = a.slice( 0 ); + + return r; + }, + + inArray: function( b, a ) { + for ( var i = 0; i < a.length; i++ ) + if ( a[i] == b ) + return i; + return -1; }, /** @@ -1730,29 +1926,18 @@ jQuery.extend({ * @cat Javascript */ merge: function(first, second) { - var result = []; - - // Move b over to the new array (this helps to avoid - // StaticNodeList instances) - for ( var k = 0; k < first.length; k++ ) - result[k] = first[k]; + var r = [].slice.call( first, 0 ); - // Now check for duplicates between a and b and only - // add the unique items + // Now check for duplicates between the two arrays + // and only add the unique items for ( var i = 0; i < second.length; i++ ) { - var noCollision = true; - - // The collision-checking process - for ( var j = 0; j < first.length; j++ ) - if ( second[i] == first[j] ) - noCollision = false; - - // If the item is unique, add it - if ( noCollision ) - result.push( second[i] ); + // Check for duplicates + if ( jQuery.inArray( second[i], r ) == -1 ) + // The item is unique, add it + first.push( second[i] ); } - return result; + return first; }, /** @@ -1777,7 +1962,7 @@ jQuery.extend({ grep: function(elems, fn, inv) { // If a string is passed in for the function, make a function // for it (a handy shortcut) - if ( fn.constructor == String ) + if ( typeof fn == "string" ) fn = new Function("a","i","return " + fn); var result = []; @@ -1823,10 +2008,10 @@ jQuery.extend({ map: function(elems, fn) { // If a string is passed in for the function, make a function // for it (a handy shortcut) - if ( fn.constructor == String ) + if ( typeof fn == "string" ) fn = new Function("a","return " + fn); - var result = []; + var result = [], r = []; // Go through the array, translating each of the items to their // new value (or values). @@ -1835,11 +2020,21 @@ jQuery.extend({ if ( val !== null && val != undefined ) { if ( val.constructor != Array ) val = [val]; - result = jQuery.merge( result, val ); + result = result.concat( val ); } } - return result; + var r = [ result[0] ]; + + check: for ( var i = 1; i < result.length; i++ ) { + for ( var j = 0; j < i; j++ ) + if ( result[i] == r[j] ) + continue check; + + r.push( result[i] ); + } + + return r; }, /* @@ -1850,12 +2045,16 @@ jQuery.extend({ // Bind an event to an element // Original by Dean Edwards - add: function(element, type, handler) { + add: function(element, type, handler, data) { // For whatever reason, IE has trouble passing the window object // around, causing it to be cloned in the process if ( jQuery.browser.msie && element.setInterval != undefined ) element = window; + // if data is passed, bind to handler + if( data ) + handler.data = data; + // Make sure that the function being executed has a unique ID if ( !handler.guid ) handler.guid = this.guid++; @@ -1895,7 +2094,9 @@ jQuery.extend({ // Detach an event or set of events from an element remove: function(element, type, handler) { if (element.events) - if (type && element.events[type]) + if ( type && type.type ) + delete element.events[ type.type ][ type.handler.guid ]; + else if (type && element.events[type]) if ( handler ) delete element.events[type][handler.guid]; else @@ -1908,7 +2109,7 @@ jQuery.extend({ trigger: function(type,data,element) { // Clone the incoming data, if any - data = $.merge([], data || []); + data = jQuery.makeArray(data || []); // Handle a global trigger if ( !element ) { @@ -1932,9 +2133,6 @@ jQuery.extend({ event = jQuery.event.fix( event || window.event || {} ); // Empty object is for triggered events with no data - // If no correct event was found, fail - if ( !event ) return false; - var returnValue = true; var c = this.events[event.type]; @@ -1943,6 +2141,11 @@ jQuery.extend({ args.unshift( event ); for ( var j in c ) { + // Pass in a reference to the handler function itself + // So that we can later remove it + args[0].handler = c[j]; + args[0].data = c[j].data; + if ( c[j].apply( this, args ) === false ) { event.preventDefault(); event.stopPropagation(); @@ -1951,19 +2154,25 @@ jQuery.extend({ } // Clean up added properties in IE to prevent memory leak - if (jQuery.browser.msie) event.target = event.preventDefault = event.stopPropagation = null; + if (jQuery.browser.msie) event.target = event.preventDefault = event.stopPropagation = event.handler = event.data = null; return returnValue; }, fix: function(event) { - // check IE - if(jQuery.browser.msie) { - // fix target property + // Fix target property, if necessary + if ( !event.target && event.srcElement ) event.target = event.srcElement; - - // check safari and if target is a textnode - } else if(jQuery.browser.safari && event.target.nodeType == 3) { + + // Calculate pageX/Y if missing and clientX/Y available + if ( typeof event.pageX == "undefined" && typeof event.clientX != "undefined" ) { + var e = document.documentElement, b = document.body; + event.pageX = event.clientX + (e.scrollLeft || b.scrollLeft); + event.pageY = event.clientY + (e.scrollTop || b.scrollTop); + } + + // Check safari and if target is a textnode + if ( jQuery.browser.safari && event.target.nodeType == 3 ) { // target is readonly, clone the event object event = jQuery.extend({}, event); // get parentnode from textnode @@ -1971,16 +2180,18 @@ jQuery.extend({ } // fix preventDefault and stopPropagation - if (!event.preventDefault) + if (!event.preventDefault) { event.preventDefault = function() { this.returnValue = false; }; + } - if (!event.stopPropagation) + if (!event.stopPropagation) { event.stopPropagation = function() { this.cancelBubble = true; }; - + } + return event; } } @@ -1992,19 +2203,31 @@ jQuery.extend({ * This property is available before the DOM is ready, therefore you can * use it to add ready events only for certain browsers. * - * See - * jQBrowser plugin for advanced browser detection: + * There are situations where object detections is not reliable enough, in that + * cases it makes sense to use browser detection. Simply try to avoid both! + * + * A combination of browser and object detection yields quite reliable results. * * @example $.browser.msie - * @desc returns true if the current useragent is some version of microsoft's internet explorer + * @desc Returns true if the current useragent is some version of microsoft's internet explorer * * @example if($.browser.safari) { $( function() { alert("this is safari!"); } ); } * @desc Alerts "this is safari!" only for safari browsers * + * @property * @name $.browser * @type Boolean * @cat Javascript */ + +/* + * Wheather the W3C compliant box model is being used. + * + * @property + * @name $.boxModel + * @type Boolean + * @cat Javascript + */ new function() { var b = navigator.userAgent.toLowerCase(); @@ -2415,6 +2638,7 @@ jQuery.macros = { /** * Get the html contents of the first matched element. + * This property is not available on XML documents. * * @example $("div").html(); * @before
@@ -2427,6 +2651,7 @@ jQuery.macros = { /** * Set the html contents of every matched element. + * This property is not available on XML documents. * * @example $("div").html("new stuff"); * @before
@@ -2629,35 +2854,7 @@ jQuery.macros = { * Get a set of elements containing the unique ancestors of the matched * set of elements (except for the root element). * - * @example $("span").ancestors() - * @before

Hello

Hello Again
- * @result [ ...,
...
,

Hello

] - * - * @name ancestors - * @type jQuery - * @cat DOM/Traversing - */ - - /** - * Get a set of elements containing the unique ancestors of the matched - * set of elements, and filtered by an expression. - * - * @example $("span").ancestors("p") - * @before

Hello

Hello Again
- * @result [

Hello

] - * - * @name ancestors - * @type jQuery - * @param String expr An expression to filter the ancestors with - * @cat DOM/Traversing - */ - ancestors: jQuery.parents, - - /** - * Get a set of elements containing the unique ancestors of the matched - * set of elements (except for the root element). - * - * @example $("span").ancestors() + * @example $("span").parents() * @before

Hello

Hello Again
* @result [ ...,
...
,

Hello

] * @@ -2670,7 +2867,7 @@ jQuery.macros = { * Get a set of elements containing the unique ancestors of the matched * set of elements, and filtered by an expression. * - * @example $("span").ancestors("p") + * @example $("span").parents("p") * @before

Hello

Hello Again
* @result [

Hello

] * @@ -2711,7 +2908,7 @@ jQuery.macros = { * @param String expr An expression to filter the next Elements with * @cat DOM/Traversing */ - next: "jQuery.sibling(a).next", + next: "jQuery.nth(a,1,'nextSibling')", /** * Get a set of elements containing the unique previous siblings of each of the @@ -2743,7 +2940,7 @@ jQuery.macros = { * @param String expr An expression to filter the previous Elements with * @cat DOM/Traversing */ - prev: "jQuery.sibling(a).prev", + prev: "jQuery.nth(a,1,'previousSibling')", /** * Get a set of elements containing all of the unique siblings of each of the @@ -2771,8 +2968,7 @@ jQuery.macros = { * @param String expr An expression to filter the sibling Elements with * @cat DOM/Traversing */ - siblings: "jQuery.sibling(a, null, true)", - + siblings: "jQuery.sibling(a.parentNode.firstChild,a)", /** * Get a set of elements containing all of the unique children of each of the @@ -2818,6 +3014,7 @@ jQuery.macros = { * @cat DOM */ removeAttr: function( key ) { + jQuery.attr( this, key, "" ); this.removeAttribute( key ); }, @@ -2925,7 +3122,7 @@ jQuery.macros = { * @cat DOM */ toggleClass: function( c ){ - jQuery.className[ jQuery.className.has(this,c) ? "remove" : "add" ](this,c); + jQuery.className[ jQuery.className.has(this,c) ? "remove" : "add" ](this, c); }, /** @@ -2982,12 +3179,24 @@ jQuery.macros = { * default behaviour. To stop both default action and event bubbling, your handler * has to return false. * + * In most cases, you can define your event handlers as anonymous functions + * (see first example). In cases where that is not possible, you can pass additional + * data as the second paramter (and the handler function as the third), see + * second example. + * * @example $("p").bind( "click", function() { * alert( $(this).text() ); * } ) * @before

Hello

* @result alert("Hello") * + * @example var handler = function(event) { + * alert(event.data.foo); + * }; + * $("p").bind( "click", {foo: "bar"}, handler) + * @result alert("bar") + * @desc Pass some additional data to the event handler. + * * @example $("form").bind( "submit", function() { return false; } ) * @desc Cancel a default action and prevent it from bubbling by returning false * from your function. @@ -3006,13 +3215,12 @@ jQuery.macros = { * @name bind * @type jQuery * @param String type An event type + * @param Object data (optional) Additional data passed to the event handler as event.data * @param Function fn A function to bind to the event on each of the set of matched elements * @cat Events */ - bind: function( type, fn ) { - if ( fn.constructor == String ) - fn = new Function("e", ( !fn.indexOf(".") ? "jQuery(this)" : "return " ) + fn); - jQuery.event.add( this, type, fn ); + bind: function( type, data, fn ) { + jQuery.event.add( this, type, fn || data, data ); }, /**