X-Git-Url: http://git.asbjorn.biz/?a=blobdiff_plain;f=src%2Fjquery%2Fjquery.js;h=8089e4e6b11bc95065ffd490d6f91dd5531c842b;hb=b6d920cf05fae3dcfe515166013ce39411ac5e16;hp=5780ba76b38170727434ff4e135445aea9eb432e;hpb=5adf740a1522c18504b66a7d67ab23b00aad34db;p=jquery.git diff --git a/src/jquery/jquery.js b/src/jquery/jquery.js index 5780ba7..8089e4e 100644 --- a/src/jquery/jquery.js +++ b/src/jquery/jquery.js @@ -9,9 +9,6 @@ * $Rev$ */ -// Global undefined variable -window.undefined = window.undefined; - /** * Create a new jQuery Object * @@ -22,41 +19,12 @@ window.undefined = window.undefined; * @param jQuery|Element|Array c context * @cat Core */ -var jQuery = function(a,c) { +window.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 - 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 ] ); + return this.init(a,c); }; // Map over the $ in case of overwrite @@ -64,7 +32,7 @@ if ( typeof $ != "undefined" ) jQuery._$ = $; // Map the jQuery namespace to the '$' one -var $ = jQuery; +window.$ = jQuery; /** * This function accepts a string containing a CSS or @@ -170,6 +138,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 @@ -288,7 +298,7 @@ jQuery.fn = jQuery.prototype = { */ setArray: function( a ) { this.length = 0; - [].push.apply( this, a ); + Array.prototype.push.apply( this, a ); return this; }, @@ -552,7 +562,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 = ""; @@ -614,10 +624,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); @@ -795,7 +808,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. * @@ -814,9 +827,9 @@ jQuery.fn = jQuery.prototype = { * @cat DOM/Traversing */ find: function(t) { - return this.pushStack( jQuery.map( this, function(a){ - return jQuery.find(t,a); - }), t ); + var data = jQuery.map(this, function(a){ return jQuery.find(t,a); }); + return this.pushStack( /[^+>] [^+>]/.test( t ) || t.indexOf("..") > -1 ? + jQuery.unique( data ) : data ); }, /** @@ -836,11 +849,46 @@ jQuery.fn = jQuery.prototype = { * @cat DOM/Manipulation */ clone: function(deep) { - return this.pushStack( jQuery.map( this, function(a){ - var a = a.cloneNode( deep != undefined ? deep : true ); - a.$events = null; // drop $events expando to avoid firing incorrect events - return a; + 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; }, /** @@ -887,7 +935,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) ); @@ -1038,7 +1086,7 @@ 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; }, /** @@ -1109,6 +1157,10 @@ jQuery.fn = jQuery.prototype = { ( this.length ? this[0].innerHTML : null ) : this.empty().append( val ); }, + + slice: function() { + return this.pushStack( Array.prototype.slice.apply( this, arguments ) ); + }, /** * @private @@ -1121,21 +1173,29 @@ 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 && 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 ] ); }); - }); } }; @@ -1193,18 +1253,39 @@ 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, al = arguments.length, deep = false; + + // Handle a deep copy situation + if ( target.constructor == Boolean ) { + deep = target; + target = arguments[1] || {}; + } // extend jQuery itself if only one argument is passed - if ( arguments.length == 1 ) { + if ( al == 1 ) { target = this; a = 0; } + var prop; - while (prop = arguments[a++]) - // Extend the base object - for ( var i in prop ) target[i] = prop[i]; + + for ( ; a < al; a++ ) + // Only deal with non-null/undefined values + if ( (prop = arguments[a]) != null ) + // Extend the base object + for ( var i in prop ) { + // Prevent never-ending loop + if ( target == prop[i] ) + continue; + + // Recurse if we're merging object values + if ( deep && typeof prop[i] == 'object' && target[i] ) + jQuery.extend( target[i], prop[i] ); + + // Don't bring in undefined values + else if ( prop[i] != undefined ) + target[i] = prop[i]; + } // Return the modified object return target; @@ -1255,12 +1336,28 @@ jQuery.extend({ // is the only cross-browser way to do this. --John isFunction: function( fn ) { return !!fn && typeof fn != "string" && !fn.nodeName && - typeof fn[0] == "undefined" && /function/i.test( fn + "" ); + fn.constructor != Array && /function/i.test( fn + "" ); }, // check if an element is in a XML document isXMLDoc: function(elem) { - return elem.tagName && elem.ownerDocument && !elem.ownerDocument.body; + 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 ) { @@ -1297,12 +1394,24 @@ jQuery.extend({ */ // args is for internal usage only each: function( obj, fn, args ) { - if ( obj.length == undefined ) - for ( var i in obj ) - fn.apply( obj[i], args || [i, obj[i]] ); - else - for ( var i = 0, ol = obj.length; i < ol; i++ ) - if ( fn.apply( obj[i], args || [i, obj[i]] ) === false ) break; + if ( args ) { + if ( obj.length == undefined ) + for ( var i in obj ) + fn.apply( obj[i], args ); + else + for ( var i = 0, ol = obj.length; i < ol; i++ ) + if ( fn.apply( obj[i], args ) === false ) break; + + // A special, fast, case for the most common use of each + } else { + if ( obj.length == undefined ) + for ( var i in obj ) + fn.call( obj[i], i, obj[i] ); + else + for ( var i = 0, ol = obj.length, val = obj[0]; + i < ol && fn.call(val,i,val) !== false; val = obj[++i] ); + } + return obj; }, @@ -1323,7 +1432,7 @@ jQuery.extend({ 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; }); @@ -1331,7 +1440,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(" ") : ""; @@ -1339,10 +1448,7 @@ jQuery.extend({ // internal only, use is(".class") has: function( t, c ) { - t = t.className || t; - // escape regex characters - c = c.replace(/([\.\\\+\*\?\[\^\]\$\(\)\{\}\=\!\<\>\|\:])/g, "\\$1"); - return t && new RegExp("(^|\\s)" + c + "(\\s|$)").test( t ); + return jQuery.inArray( c, (t.className || t).toString().split(/\s+/) ) > -1; } }, @@ -1370,7 +1476,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 { @@ -1380,14 +1486,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); @@ -1401,47 +1507,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 = 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; a && color(a); a = a.parentNode ) + stack.unshift(a); + + // Go through and make them visible, but in reverse + // (It would be better if we knew the exact display type that they had) + for ( 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; @@ -1449,17 +1588,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("", ""] || + + // IE can't serialize and