X-Git-Url: http://git.asbjorn.biz/?a=blobdiff_plain;f=src%2Fjquery%2Fjquery.js;h=bc4bcd1e7972de9d6faa7449fcbc9202049ee175;hb=05d401dd8439d8a2137d3fb69a70d671c0d3a69d;hp=67959697bf2472e131697a4dcc280ec878cc1af0;hpb=756183f6073a1e9fe6fae538d6f60899df47e2a1;p=jquery.git diff --git a/src/jquery/jquery.js b/src/jquery/jquery.js index 6795969..bc4bcd1 100644 --- a/src/jquery/jquery.js +++ b/src/jquery/jquery.js @@ -24,40 +24,10 @@ window.undefined = window.undefined; */ var jQuery = function(a,c) { // If the context is global, return a new object - if ( window == this ) + if ( window == this || !this.init ) return new jQuery(a,c); - - // Make sure that a selection was provided - a = a || document; - - // HANDLE: $(function) - // Shortcut for document ready - // Safari reports typeof on DOM NodeLists as a function - if ( jQuery.isFunction(a) && !a.nodeType && a[0] == undefined ) - return new jQuery(document)[ jQuery.fn.ready ? "ready" : "load" ]( a ); - - // Handle HTML strings - if ( typeof a == "string" ) { - // HANDLE: $(html) -> $(array) - var m = /^[^<]*(<.+>)[^>]*$/.exec(a); - if ( m ) - a = jQuery.clean( [ m[1] ] ); - - // HANDLE: $(expr) - else - return new jQuery( c ).find( a ); - } - return this.setArray( - // HANDLE: $(array) - a.constructor == Array && a || - - // HANDLE: $(arraylike) - // Watch for when an array-like object is passed as the selector - (a.jquery || a.length && a != window && !a.nodeType && a[0] != undefined && a[0].nodeType) && jQuery.makeArray( a ) || - - // HANDLE: $(*) - [ a ] ); + return this.init(a,c); }; // Map over the $ in case of overwrite @@ -77,8 +47,12 @@ var $ = jQuery; * (usually consisting of CSS or XPath), which then finds all matching * elements. * - * By default, $() looks for DOM elements within the context of the - * current HTML document. + * By default, if no context is specified, $() looks for DOM elements within the context of the + * current HTML document. If you do specify a context, such as a DOM + * element or jQuery object, the expression will be matched against + * the contents of that context. + * + * See [[DOM/Traversing/Selectors]] for the allowed CSS/XPath syntax for expressions. * * @example $("div > p") * @desc Finds all p elements that are children of a div element. @@ -103,10 +77,10 @@ var $ = jQuery; /** * Create DOM elements on-the-fly from the provided String of raw HTML. * - * @example $("

Hello

").appendTo("#body") + * @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. + * and appends it to the body element. Internally, an + * element is created and its innerHTML property set to the given markup. * It is therefore both quite flexible and limited. * * @name $ @@ -122,7 +96,7 @@ var $ = jQuery; * This function also accepts XML Documents and Window objects * as valid arguments (even though they are not DOM Elements). * - * @example $(document.body).background( "black" ); + * @example $(document.body).css( "background", "black" ); * @desc Sets the background color of the page to black. * * @example $( myForm.elements ).hide() @@ -138,8 +112,10 @@ var $ = jQuery; * A shorthand for $(document).ready(), allowing you to bind a function * to be executed when the DOM document has finished loading. This function * behaves just like $(document).ready(), in that it should be used to wrap - * all of the other $() operations on your page. While this function is, - * technically, chainable - there really isn't much use for chaining against it. + * other $() operations on your page that depend on the DOM being ready to be + * operated on. While this function is, 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. @@ -165,6 +141,48 @@ var $ = jQuery; jQuery.fn = jQuery.prototype = { /** + * Initialize a new jQuery object + * + * @private + * @name init + * @param String|Function|Element|Array|jQuery a selector + * @param jQuery|Element|Array c context + * @cat Core + */ + init: function(a,c) { + // Make sure that a selection was provided + a = a || document; + + // HANDLE: $(function) + // Shortcut for document ready + if ( jQuery.isFunction(a) ) + return new jQuery(document)[ jQuery.fn.ready ? "ready" : "load" ]( a ); + + // Handle HTML strings + if ( typeof a == "string" ) { + // HANDLE: $(html) -> $(array) + var m = /^[^<]*(<(.|\s)+>)[^>]*$/.exec(a); + if ( m ) + a = jQuery.clean( [ m[1] ] ); + + // HANDLE: $(expr) + else + return new jQuery( c ).find( a ); + } + + return this.setArray( + // HANDLE: $(array) + a.constructor == Array && a || + + // HANDLE: $(arraylike) + // Watch for when an array-like object is passed as the selector + (a.jquery || a.length && a != window && !a.nodeType && a[0] != undefined && a[0].nodeType) && jQuery.makeArray( a ) || + + // HANDLE: $(*) + [ a ] ); + }, + + /** * The current version of jQuery. * * @private @@ -176,7 +194,7 @@ jQuery.fn = jQuery.prototype = { jquery: "@VERSION", /** - * The number of elements currently matched. + * The number of elements currently matched. The size function will return the same value. * * @example $("img").length; * @before @@ -189,7 +207,8 @@ jQuery.fn = jQuery.prototype = { */ /** - * The number of elements currently matched. + * Get the number of elements currently matched. This returns the same + * number as the 'length' property of the jQuery object. * * @example $("img").size(); * @before @@ -206,10 +225,12 @@ jQuery.fn = jQuery.prototype = { length: 0, /** - * Access all matched elements. This serves as a backwards-compatible + * Access all matched DOM elements. This serves as a backwards-compatible * way of accessing all matched elements (other than the jQuery object * itself, which is, in fact, an array of elements). * + * It is useful if you need to operate on the DOM elements themselves instead of using built-in jQuery functions. + * * @example $("img").get(); * @before * @result [ ] @@ -221,12 +242,13 @@ jQuery.fn = jQuery.prototype = { */ /** - * Access a single matched element. num is used to access the - * Nth element matched. + * Access a single matched DOM element at a specified index in the matched set. + * This allows you to extract the actual DOM element and operate on it + * directly without necessarily using jQuery functionality on it. * * @example $("img").get(0); * @before - * @result [ ] + * @result * @desc Selects all images in the document and returns the first one * * @name get @@ -258,9 +280,9 @@ jQuery.fn = jQuery.prototype = { * @cat Core */ pushStack: function( a ) { - var ret = jQuery(this); + var ret = jQuery(a); ret.prevObject = this; - return ret.setArray( a ); + return ret; }, /** @@ -287,11 +309,11 @@ jQuery.fn = jQuery.prototype = { * Execute a function within the context of every matched element. * This means that every time the passed-in function is executed * (which is once for every element matched) the 'this' keyword - * points to the specific element. + * points to the specific DOM element. * * Additionally, the function, when executed, is passed a single * argument representing the position of the element in the matched - * set. + * set (integer, zero-index). * * @example $("img").each(function(i){ * this.src = "test" + i + ".jpg"; @@ -347,6 +369,9 @@ jQuery.fn = jQuery.prototype = { * This method makes it easy to retrieve a property value * from the first matched element. * + * If the element does not have an attribute with such a + * name, undefined is returned. + * * @example $("img").attr("src"); * @before * @result test.jpg @@ -378,8 +403,6 @@ jQuery.fn = jQuery.prototype = { /** * Set a single property to a value, on all matched elements. * - * Can compute values provided as ${formula}, see second example. - * * 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. @@ -389,11 +412,6 @@ jQuery.fn = jQuery.prototype = { * @result * @desc Sets src attribute to all images. * - * @example $("img").attr("title", "${this.src}"); - * @before - * @result - * @desc Sets title attribute from src attribute, a shortcut for attr(String,Function) - * * @name attr * @type jQuery * @param String key The name of the property to set. @@ -404,7 +422,9 @@ jQuery.fn = jQuery.prototype = { /** * Set a single property to a computed value, on all matched elements. * - * Instead of a value, a function is provided, that computes the value. + * Instead of supplying a string value as described + * [[DOM/Attributes#attr.28_key.2C_value_.29|above]], + * a function is provided that computes the value. * * @example $("img").attr("title", function() { return this.src }); * @before @@ -429,7 +449,7 @@ jQuery.fn = jQuery.prototype = { // Look for the case where we're accessing a style value if ( key.constructor == String ) if ( value == undefined ) - return jQuery[ type || "attr" ]( this[0], key ); + return this.length && jQuery[ type || "attr" ]( this[0], key ) || undefined; else { obj = {}; obj[ key ] = value; @@ -545,7 +565,7 @@ jQuery.fn = jQuery.prototype = { * @cat DOM/Attributes */ text: function(e) { - if ( typeof e == "string" ) + if ( typeof e != "object" && e != null ) return this.empty().append( document.createTextNode( e ) ); var t = ""; @@ -607,10 +627,13 @@ jQuery.fn = jQuery.prototype = { */ wrap: function() { // The elements to wrap the target around - var a = jQuery.clean(arguments); + var a, args = arguments; // Wrap each of the matched elements individually return this.each(function(){ + if ( !a ) + a = jQuery.clean(args, this.ownerDocument); + // Clone the structure that we're using to wrap var b = a[0].cloneNode(true); @@ -761,12 +784,17 @@ jQuery.fn = jQuery.prototype = { }, /** - * End the most recent 'destructive' operation, reverting the list of matched elements - * back to its previous state. After an end operation, the list of matched elements will - * revert to the last state of matched elements. + * Revert the most recent 'destructive' operation, changing the set of matched elements + * to its previous state (right before the destructive operation). * * If there was no destructive operation before, an empty set is returned. * + * A 'destructive' operation is any operation that changes the set of + * matched jQuery elements. These functions are: add, + * children, clone, filter, + * find, not, next, + * parent, parents, prev and siblings. + * * @example $("p").find("span").end(); * @before

Hello, how are you?

* @result [

...

] @@ -783,7 +811,7 @@ jQuery.fn = jQuery.prototype = { /** * Searches for all elements that match the specified expression. - + * * This method is a good way to find additional descendant * elements with which to process. * @@ -802,9 +830,9 @@ jQuery.fn = jQuery.prototype = { * @cat DOM/Traversing */ find: function(t) { - return this.pushStack( jQuery.map( this, function(a){ - return jQuery.find(t,a); - }) ); + var data = jQuery.map(this, function(a){ return jQuery.find(t,a); }); + return this.pushStack( /[^+>] [^+>]/.test( t ) || t.indexOf("..") > -1 ? + jQuery.unique( data ) : data ); }, /** @@ -824,9 +852,46 @@ jQuery.fn = jQuery.prototype = { * @cat DOM/Manipulation */ clone: function(deep) { - return this.pushStack( jQuery.map( this, function(a){ - return a.cloneNode( deep != undefined ? deep : true ); + deep = deep != undefined ? deep : true; + var $this = this.add(this.find("*")); + if (jQuery.browser.msie) { + // Need to remove events on the element and its descendants + $this.each(function() { + this._$events = {}; + for (var type in this.$events) + this._$events[type] = jQuery.extend({},this.$events[type]); + }).unbind(); + } + + // Do the clone + var r = this.pushStack( jQuery.map( this, function(a){ + return a.cloneNode( deep ); }) ); + + if (jQuery.browser.msie) { + $this.each(function() { + // Add the events back to the original and its descendants + var events = this._$events; + for (var type in events) + for (var handler in events[type]) + jQuery.event.add(this, type, events[type][handler], events[type][handler].data); + this._$events = null; + }); + } + + // copy form values over + if (deep) { + var inputs = r.add(r.find('*')).filter('select,input[@type=checkbox]'); + $this.filter('select,input[@type=checkbox]').each(function(i) { + if (this.selectedIndex) + inputs[i].selectedIndex = this.selectedIndex; + if (this.checked) + inputs[i].checked = true; + }); + } + + // Return the cloned set + return r; }, /** @@ -873,7 +938,7 @@ jQuery.fn = jQuery.prototype = { return this.pushStack( jQuery.isFunction( t ) && jQuery.grep(this, function(el, index){ - return t.apply(el, [index]) + return t.apply(el, [index]); }) || jQuery.multiFilter(t,this) ); @@ -915,6 +980,9 @@ jQuery.fn = jQuery.prototype = { * of matched elements. This method is used to remove one or more * elements from a jQuery object. * + * Please note: the expression cannot use a reference to the + * element name. See the two examples below. + * * @example $("p").not( $("div p.selected") ) * @before

Hello

Hello Again

* @result [

Hello

] @@ -928,14 +996,14 @@ jQuery.fn = jQuery.prototype = { not: function(t) { return this.pushStack( t.constructor == String && - jQuery.multiFilter(t,this,true) || + jQuery.multiFilter(t, this, true) || - jQuery.grep(this,function(a){ - if ( t.constructor == Array || t.jquery ) - return jQuery.inArray( t, a ) < 0; - else - return a != t; - }) ); + jQuery.grep(this, function(a) { + return ( t.constructor == Array || t.jquery ) + ? jQuery.inArray( a, t ) < 0 + : a != t; + }) + ); }, /** @@ -943,8 +1011,12 @@ jQuery.fn = jQuery.prototype = { * to the set of matched elements. * * @example $("p").add("span") - * @before

Hello

Hello Again - * @result [

Hello

, Hello Again ] + * @before (HTML)

Hello

Hello Again + * @result (jQuery object matching 2 elements) [

Hello

, Hello Again ] + * @desc Compare the above result to the result of $('p'), + * which would just result in [

Hello

]
. + * Using add(), matched elements of $('span') are simply + * added to the returned jQuery-object. * * @name add * @type jQuery @@ -985,7 +1057,10 @@ jQuery.fn = jQuery.prototype = { add: function(t) { return this.pushStack( jQuery.merge( this.get(), - typeof t == "string" ? jQuery(t).get() : t.length ? t : [t] ) + t.constructor == String ? + jQuery(t).get() : + t.length != undefined && (!t.nodeName || t.nodeName == "FORM") ? + t : [t] ) ); }, @@ -1014,11 +1089,18 @@ jQuery.fn = jQuery.prototype = { * @cat DOM/Traversing */ is: function(expr) { - return expr ? jQuery.filter(expr,this).r.length > 0 : false; + return expr ? jQuery.multiFilter(expr,this).length > 0 : false; }, /** - * Get the current value of the first matched element. + * Get the content of the value attribute of the first matched element. + * + * Use caution when relying on this function to check the value of + * multiple-select elements and checkboxes in a form. While it will + * still work as intended, it may not accurately represent the value + * the server will receive because these elements may send an array + * of values. For more robust handling of field values, see the + * [http://www.malsup.com/jquery/form/#fields fieldValue function of the Form Plugin]. * * @example $("input").val(); * @before @@ -1030,7 +1112,7 @@ jQuery.fn = jQuery.prototype = { */ /** - * Set the value of every matched element. + * Set the value attribute of every matched element. * * @example $("input").val("test"); * @before @@ -1090,28 +1172,36 @@ jQuery.fn = jQuery.prototype = { * @cat Core */ domManip: function(args, table, dir, fn){ - var clone = this.length > 1; - var a = jQuery.clean(args); - if ( dir < 0 ) - a.reverse(); + var clone = this.length > 1, a; return this.each(function(){ + if ( !a ) { + a = jQuery.clean(args, this.ownerDocument); + if ( dir < 0 ) + a.reverse(); + } + var obj = this; - if ( table && this.nodeName.toUpperCase() == "TABLE" && a[0].nodeName.toUpperCase() == "TR" ) + if ( table && jQuery.nodeName(this, "table") && jQuery.nodeName(a[0], "tr") ) obj = this.getElementsByTagName("tbody")[0] || this.appendChild(document.createElement("tbody")); jQuery.each( a, function(){ - fn.apply( obj, [ clone ? this.cloneNode(true) : this ] ); + if ( jQuery.nodeName(this, "script") ) { + if ( this.src ) + jQuery.ajax({ url: this.src, async: false, dataType: "script" }); + else + jQuery.globalEval( this.text || this.textContent || this.innerHTML || "" ); + } else + fn.apply( obj, [ clone ? this.cloneNode(true) : this ] ); }); - }); } }; /** * Extends the jQuery object itself. Can be used to add functions into - * the jQuery namespace and to add plugin methods (plugins). + * the jQuery namespace and to [[Plugins/Authoring|add plugin methods]] (plugins). * * @example jQuery.fn.extend({ * check: function() { @@ -1162,8 +1252,7 @@ jQuery.fn = jQuery.prototype = { */ jQuery.extend = jQuery.fn.extend = function() { // copy reference to target object - var target = arguments[0], - a = 1; + var target = arguments[0], a = 1; // extend jQuery itself if only one argument is passed if ( arguments.length == 1 ) { @@ -1171,7 +1260,7 @@ jQuery.extend = jQuery.fn.extend = function() { a = 0; } var prop; - while (prop = arguments[a++]) + while ( (prop = arguments[a++]) != null ) // Extend the base object for ( var i in prop ) target[i] = prop[i]; @@ -1217,14 +1306,43 @@ jQuery.extend({ noConflict: function() { if ( jQuery._$ ) $ = jQuery._$; + return jQuery; }, + // This may seem like some crazy code, but trust me when I say that this + // is the only cross-browser way to do this. --John isFunction: function( fn ) { - return fn && typeof fn == "function"; + return !!fn && typeof fn != "string" && !fn.nodeName && + fn.constructor != Array && /function/i.test( fn + "" ); + }, + + // check if an element is in a XML document + isXMLDoc: function(elem) { + return elem.documentElement && !elem.body || + elem.tagName && elem.ownerDocument && !elem.ownerDocument.body; + }, + + // Evalulates a script in a global context + // Evaluates Async. in Safari 2 :-( + globalEval: function( data ) { + data = jQuery.trim( data ); + if ( data ) { + if ( window.execScript ) + window.execScript( data ); + else if ( jQuery.browser.safari ) + // safari doesn't provide a synchronous global eval + window.setTimeout( data, 0 ); + else + eval.call( window, data ); + } + }, + + nodeName: function( elem, name ) { + return elem.nodeName && elem.nodeName.toUpperCase() == name.toUpperCase(); }, /** - * A generic iterator function, which can be used to seemlessly + * A generic iterator function, which can be used to seamlessly * iterate over both objects and arrays. This function is not the same * as $().each() - which is used to iterate, exclusively, over a jQuery * object. This function can be used to iterate over anything. @@ -1265,22 +1383,21 @@ jQuery.extend({ prop: function(elem, value, type, index, prop){ // Handle executable functions if ( jQuery.isFunction( value ) ) - return value.call( elem, [index] ); + value = value.call( elem, [index] ); // exclude the following css properties to add px - var exclude = /z-?index|font-?weight|opacity|zoom/i; + var exclude = /z-?index|font-?weight|opacity|zoom|line-?height/i; // Handle passing in a number to a CSS property - if ( value.constructor == Number && type == "curCSS" && !exclude.test(prop) ) - return value + "px"; - - return value; + return value && value.constructor == Number && type == "curCSS" && !exclude.test(prop) ? + value + "px" : + value; }, className: { // internal only, use addClass("class") add: function( elem, c ){ - jQuery.each( c.split(/\s+/), function(i, cur){ + jQuery.each( (c || "").split(/\s+/), function(i, cur){ if ( !jQuery.className.has( elem.className, cur ) ) elem.className += ( elem.className ? " " : "" ) + cur; }); @@ -1288,7 +1405,7 @@ jQuery.extend({ // internal only, use removeClass("class") remove: function( elem, c ){ - elem.className = c ? + elem.className = c != undefined ? jQuery.grep( elem.className.split(/\s+/), function(cur){ return !jQuery.className.has( c, cur ); }).join(" ") : ""; @@ -1296,8 +1413,7 @@ jQuery.extend({ // internal only, use is(".class") has: function( t, c ) { - t = t.className || t; - return t && new RegExp("(^|\\s)" + c + "(\\s|$)").test( t ); + return jQuery.inArray( c, (t.className || t).toString().split(/\s+/) ) > -1; } }, @@ -1325,7 +1441,7 @@ jQuery.extend({ }); jQuery.swap( e, old, function() { - if (jQuery.css(e,"display") != "none") { + if ( jQuery(e).is(':visible') ) { oHeight = e.offsetHeight; oWidth = e.offsetWidth; } else { @@ -1335,14 +1451,14 @@ jQuery.extend({ visibility: "hidden", position: "absolute", display: "block", right: "0", left: "0" }).appendTo(e.parentNode)[0]; - var parPos = jQuery.css(e.parentNode,"position"); - if ( parPos == "" || parPos == "static" ) + var parPos = jQuery.css(e.parentNode,"position") || "static"; + if ( parPos == "static" ) e.parentNode.style.position = "relative"; oHeight = e.clientHeight; oWidth = e.clientWidth; - if ( parPos == "" || parPos == "static" ) + if ( parPos == "static" ) e.parentNode.style.position = "static"; e.parentNode.removeChild(e); @@ -1356,47 +1472,80 @@ jQuery.extend({ }, curCSS: function(elem, prop, force) { - var ret; + var ret, stack = [], swap = []; + + // A helper method for determining if an element's values are broken + function color(a){ + if ( !jQuery.browser.safari ) + return false; + + var ret = document.defaultView.getComputedStyle(a,null); + return !ret || ret.getPropertyValue("color") == ""; + } + + if (prop == "opacity" && jQuery.browser.msie) { + ret = jQuery.attr(elem.style, "opacity"); + return ret == "" ? "1" : ret; + } - if (prop == "opacity" && jQuery.browser.msie) - return jQuery.attr(elem.style, "opacity"); - - if (prop == "float" || prop == "cssFloat") - prop = jQuery.browser.msie ? "styleFloat" : "cssFloat"; + if (prop.match(/float/i)) + prop = jQuery.styleFloat; if (!force && elem.style[prop]) ret = elem.style[prop]; else if (document.defaultView && document.defaultView.getComputedStyle) { - if (prop == "cssFloat" || prop == "styleFloat") + if (prop.match(/float/i)) prop = "float"; prop = prop.replace(/([A-Z])/g,"-$1").toLowerCase(); var cur = document.defaultView.getComputedStyle(elem, null); - if ( cur ) + if ( cur && !color(elem) ) ret = cur.getPropertyValue(prop); - else if ( prop == "display" ) - ret = "none"; - else - jQuery.swap(elem, { display: "block" }, function() { - var c = document.defaultView.getComputedStyle(this, ""); - ret = c && c.getPropertyValue(prop) || ""; - }); - } else if (elem.currentStyle) { + // If the element isn't reporting its values properly in Safari + // then some display: none elements are involved + else { + // Locate all of the parent display: none elements + for ( var a = elem; color(a); a = a.parentNode ) + stack.unshift(a); + + // Go through and make them visible, but in reverse + // (It would be better if we knew the exact display type that they had) + for ( a = 0; a < stack.length; a++ ) + if ( color(stack[a]) ) { + swap[a] = stack[a].style.display; + stack[a].style.display = "block"; + } + + // Since we flip the display style, we have to handle that + // one special, otherwise get the value + ret = prop == "display" && swap[stack.length-1] != null ? + "none" : + document.defaultView.getComputedStyle(elem,null).getPropertyValue(prop) || ""; + + // Finally, revert the display styles back + for ( a = 0; a < swap.length; a++ ) + if ( swap[a] != null ) + stack[a].style.display = swap[a]; + } + + if ( prop == "opacity" && ret == "" ) + ret = "1"; + } 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; }, - clean: function(a) { + clean: function(a, doc) { var r = []; + doc = doc || document; jQuery.each( a, function(i,arg){ if ( !arg ) return; @@ -1404,17 +1553,20 @@ jQuery.extend({ if ( arg.constructor == Number ) arg = arg.toString(); - // Convert html string into DOM nodes + // Convert html string into DOM nodes if ( typeof arg == "string" ) { // Trim whitespace, otherwise indexOf won't work as expected - var s = jQuery.trim(arg), div = document.createElement("div"), tb = []; + var s = jQuery.trim(arg).toLowerCase(), div = doc.createElement("div"), tb = []; var wrap = - // option or optgroup + // option or optgroup !s.indexOf("", ""] || - (!s.indexOf("", ""] || + + s.match(/^<(thead|tbody|tfoot|colg|cap)/) && [1, "", "
"] || !s.indexOf("", ""] || + !s.indexOf("", ""] || + [0,"",""]; // Go to html and back, then peel off extra wrappers - div.innerHTML = wrap[1] + s + wrap[2]; + div.innerHTML = wrap[1] + arg + wrap[2]; // Move to the right depth while ( wrap[0]-- ) @@ -1445,18 +1600,22 @@ jQuery.extend({ tb = div.childNodes; for ( var n = tb.length-1; n >= 0 ; --n ) - if ( tb[n].nodeName.toUpperCase() == "TBODY" && !tb[n].childNodes.length ) + if ( jQuery.nodeName(tb[n], "tbody") && !tb[n].childNodes.length ) tb[n].parentNode.removeChild(tb[n]); - + + // IE completely kills leading whitespace when innerHTML is used + if ( /^\s/.test(arg) ) + div.insertBefore( doc.createTextNode( arg.match(/^\s*/)[0] ), div.firstChild ); + } - arg = div.childNodes; + arg = jQuery.makeArray( div.childNodes ); } - if ( arg.length === 0 ) + if ( 0 === arg.length && (!jQuery.nodeName(arg, "form") && !jQuery.nodeName(arg, "select")) ) return; - - if ( arg[0] == undefined ) + + if ( arg[0] == undefined || jQuery.nodeName(arg, "form") || arg.options ) r.push( arg ); else r = jQuery.merge( r, arg ); @@ -1467,52 +1626,48 @@ jQuery.extend({ }, attr: function(elem, name, value){ - var fix = { - "for": "htmlFor", - "class": "className", - "float": jQuery.browser.msie ? "styleFloat" : "cssFloat", - cssFloat: jQuery.browser.msie ? "styleFloat" : "cssFloat", - innerHTML: "innerHTML", - className: "className", - value: "value", - disabled: "disabled", - checked: "checked", - readonly: "readOnly", - selected: "selected" - }; - - // IE actually uses filters for opacity ... elem is actually elem.style - if ( name == "opacity" && jQuery.browser.msie && value != undefined ) { - // IE has trouble with opacity if it does not have layout - // 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; + var fix = jQuery.isXMLDoc(elem) ? {} : jQuery.props; + // Safari mis-reports the default selected property of a hidden option + // Accessing the parent's selectedIndex property fixes it + if ( name == "selected" && jQuery.browser.safari ) + elem.parentNode.selectedIndex; + // 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 ( jQuery.browser.msie && name == "style" ) + return jQuery.attr( elem.style, "cssText", value ); - } else if ( value == undefined && jQuery.browser.msie && elem.nodeName && elem.nodeName.toUpperCase() == "FORM" && (name == "action" || name == "method") ) + else if ( value == undefined && jQuery.browser.msie && jQuery.nodeName(elem, "form") && (name == "action" || name == "method") ) return elem.getAttributeNode(name).nodeValue; // IE elem.getAttribute passes even for style else if ( elem.tagName ) { + if ( value != undefined ) elem.setAttribute( name, value ); + if ( jQuery.browser.msie && /href|src/.test(name) && !jQuery.isXMLDoc(elem) ) + return elem.getAttribute( name, 2 ); return elem.getAttribute( name ); + // elem is actually elem.style ... set the style } else { + // IE actually uses filters for opacity + if ( name == "opacity" && jQuery.browser.msie ) { + if ( value != undefined ) { + // IE has trouble with opacity if it does not have layout + // Force it by setting the zoom level + elem.zoom = 1; + + // Set the alpha filter to set the opacity + elem.filter = (elem.filter || "").replace(/alpha\([^)]*\)/,"") + + (parseFloat(value).toString() == "NaN" ? "" : "alpha(opacity=" + value * 100 + ")"); + } + + return elem.filter ? + (parseFloat( elem.filter.match(/opacity=([^)]*)/)[1] ) / 100).toString() : ""; + } name = name.replace(/-([a-z])/ig,function(z,b){return b.toUpperCase();}); if ( value != undefined ) elem[name] = value; return elem[name]; @@ -1531,13 +1686,14 @@ jQuery.extend({ * @cat JavaScript */ trim: function(t){ - return t.replace(/^\s+|\s+$/g, ""); + return (t||"").replace(/^\s+|\s+$/g, ""); }, makeArray: function( a ) { var r = []; - if ( a.constructor != Array ) + // Need to use typeof to fight Safari childNodes crashes + if ( typeof a != "array" ) for ( var i = 0, al = a.length; i < al; i++ ) r.push( a[i] ); else @@ -1554,40 +1710,56 @@ jQuery.extend({ }, /** - * Merge two arrays together, removing all duplicates. - * - * The result is the altered first argument with - * the unique elements from the second array added. + * Merge two arrays together by concatenating them. * * @example $.merge( [0,1,2], [2,3,4] ) - * @result [0,1,2,3,4] - * @desc Merges two arrays, removing the duplicate 2 - * - * @example var array = [3,2,1]; - * $.merge( array, [4,3,2] ) - * @result array == [3,2,1,4] - * @desc Merges two arrays, removing the duplicates 3 and 2 + * @result [0,1,2,2,3,4] + * @desc Merges two arrays. * * @name $.merge * @type Array - * @param Array first The first array to merge, the unique elements of second added. - * @param Array second The second array to merge into the first, unaltered. + * @param Array first The first array to merge, the elements of second are added. + * @param Array second The second array to append to the first, unaltered. * @cat JavaScript */ merge: function(first, second) { - var r = [].slice.call( first, 0 ); + // We have to loop this way because IE & Opera overwrite the length + // expando of getElementsByTagName + for ( var i = 0; second[i]; i++ ) + first.push(second[i]); + return first; + }, - // Now check for duplicates between the two arrays - // and only add the unique items - for ( var i = 0, sl = second.length; i < sl; i++ ) - // Check for duplicates - if ( jQuery.inArray( second[i], r ) == -1 ) - // The item is unique, add it - first.push( second[i] ); + /** + * Reduce an array (of jQuery objects only) to its unique elements. + * + * @example $.unique( [x1, x2, x3, x2, x3] ) + * @result [x1, x2, x3] + * @desc Reduces the arrays of jQuery objects to unique elements by removing the duplicates of x2 and x3 + * + * @name $.unique + * @type Array + * @param Array array The array to reduce to its unique jQuery objects. + * @cat JavaScript + */ + unique: function(first) { + var r = [], num = jQuery.mergeNum++; + + try { + for ( var i = 0, fl = first.length; i < fl; i++ ) + if ( num != first[i].mergeNum ) { + first[i].mergeNum = num; + r.push(first[i]); + } + } catch(e) { + r = first; + } - return first; + return r; }, + mergeNum: 0, + /** * Filter items out of an array, by using a filter function. * @@ -1668,7 +1840,7 @@ jQuery.extend({ if ( typeof fn == "string" ) fn = new Function("a","return " + fn); - var result = [], r = []; + var result = []; // Go through the array, translating each of the items to their // new value (or values). @@ -1681,17 +1853,7 @@ jQuery.extend({ } } - var r = result.length ? [ result[0] ] : []; - - check: for ( var i = 1, rl = result.length; i < rl; i++ ) { - for ( var j = 0; j < i; j++ ) - if ( result[i] == r[j] ) - continue check; - - r.push( result[i] ); - } - - return r; + return result; } }); @@ -1732,6 +1894,7 @@ new function() { // Figure out what browser is being used jQuery.browser = { + version: (b.match(/.+(?:rv|it|ra|ie)[\/: ]([\d.]+)/) || [])[1], safari: /webkit/.test(b), opera: /opera/.test(b), msie: /msie/.test(b) && !/opera/.test(b), @@ -1740,13 +1903,32 @@ new function() { // Check to see if the W3C box model is being used jQuery.boxModel = !jQuery.browser.msie || document.compatMode == "CSS1Compat"; + + jQuery.styleFloat = jQuery.browser.msie ? "styleFloat" : "cssFloat"; + + jQuery.props = { + "for": "htmlFor", + "class": "className", + "float": jQuery.styleFloat, + cssFloat: jQuery.styleFloat, + styleFloat: jQuery.styleFloat, + innerHTML: "innerHTML", + className: "className", + value: "value", + disabled: "disabled", + checked: "checked", + readonly: "readOnly", + selected: "selected", + maxlength: "maxLength" + }; + }; /** * Get a set of elements containing the unique parents of the matched * set of elements. * - * Can be filtered with an optional expressions. + * You may use an optional expression to filter the set of parent elements that will match. * * @example $("p").parent() * @before

Hello

Hello

@@ -1768,7 +1950,7 @@ new function() { * Get a set of elements containing the unique ancestors of the matched * set of elements (except for the root element). * - * Can be filtered with an optional expressions. + * The matched elements can be filtered with an optional expression. * * @example $("span").parents() * @before

Hello

Hello Again
@@ -1790,9 +1972,10 @@ new function() { * Get a set of elements containing the unique next siblings of each of the * matched set of elements. * - * It only returns the very next sibling, not all next siblings. + * It only returns the very next sibling for each element, not all + * next siblings. * - * Can be filtered with an optional expressions. + * You may provide an optional expression to filter the match. * * @example $("p").next() * @before

Hello

Hello Again

And Again
@@ -1814,9 +1997,9 @@ new function() { * Get a set of elements containing the unique previous siblings of each of the * matched set of elements. * - * Can be filtered with an optional expressions. + * Use an optional expression to filter the matched set. * - * It only returns the immediately previous sibling, not all previous siblings. + * Only the immediately previous sibling is returned, not all previous siblings. * * @example $("p").prev() * @before

Hello

Hello Again

And Again

@@ -1860,7 +2043,8 @@ new function() { * Get a set of elements containing all of the unique children of each of the * matched set of elements. * - * Can be filtered with an optional expressions. + * This set can be filtered with an optional expression that will cause + * only elements matching the selector to be collected. * * @example $("div").children() * @before

Hello

Hello Again

And Again

@@ -1889,7 +2073,7 @@ jQuery.each({ var ret = jQuery.map(this,n); if ( a && typeof a == "string" ) ret = jQuery.multiFilter(a,ret); - return this.pushStack( ret ); + return this.pushStack( jQuery.unique(ret) ); }; }); @@ -2213,7 +2397,7 @@ jQuery.each( [ "eq", "lt", "gt", "contains" ], function(i,n){ */ /** - * Set the CSS width of every matched element. If no explicit unit + * Set the CSS height of every matched element. If no explicit unit * was specified (like 'em' or '%') then "px" is added to the width. * * @example $("p").height(20);