Split out the fragment-building code from domManip. Switched core.js to using that...
[jquery.git] / src / core.js
1 // Define a local copy of jQuery
2 var jQuery = function( selector, context ) {
3                 // The jQuery object is actually just the init constructor 'enhanced'
4                 return arguments.length === 0 ?
5                         rootjQuery :
6                         new jQuery.fn.init( selector, context );
7         },
8
9         // Map over jQuery in case of overwrite
10         _jQuery = window.jQuery,
11
12         // Map over the $ in case of overwrite
13         _$ = window.$,
14
15         // Use the correct document accordingly with window argument (sandbox)
16         document = window.document,
17
18         // A central reference to the root jQuery(document)
19         rootjQuery,
20
21         // A simple way to check for HTML strings or ID strings
22         // (both of which we optimize for)
23         quickExpr = /^[^<]*(<[\w\W]+>)[^>]*$|^#([\w-]+)$/,
24
25         // Is it a simple selector
26         isSimple = /^.[^:#\[\.,]*$/,
27
28         // Check if a string has a non-whitespace character in it
29         rnotwhite = /\S/,
30
31         // Used for trimming whitespace
32         rtrim = /^\s+|\s+$/g,
33
34         // Match a standalone tag
35         rsingleTag = /^<(\w+)\s*\/?>$/,
36
37         // Keep a UserAgent string for use with jQuery.browser
38         userAgent = navigator.userAgent.toLowerCase(),
39
40         // Save a reference to some core methods
41         toString = Object.prototype.toString,
42         push = Array.prototype.push,
43         slice = Array.prototype.slice;
44
45 jQuery.fn = jQuery.prototype = {
46         init: function( selector, context ) {
47                 var match, elem, ret, doc;
48
49                 // Handle $(""), $(null), or $(undefined)
50                 if ( !selector ) {
51                         return this;
52                 }
53
54                 // $("body"): Shortcut for quickly finding the body element
55                 if ( selector === "body" && !context && document.body ) {
56                         selector = document.body;
57                 }
58
59                 // Handle $(DOMElement)
60                 if ( selector.nodeType ) {
61                         this.context = this[0] = selector;
62                         this.length++;
63                         return this;
64                 }
65
66                 // Handle HTML strings
67                 if ( typeof selector === "string" ) {
68                         // Are we dealing with HTML string or an ID?
69                         match = quickExpr.exec( selector );
70
71                         // Verify a match, and that no context was specified for #id
72                         if ( match && (match[1] || !context) ) {
73
74                                 // HANDLE: $(html) -> $(array)
75                                 if ( match[1] ) {
76                                         doc = (context ? context.ownerDocument || context : document);
77
78                                         // If a single string is passed in and it's a single tag
79                                         // just do a createElement and skip the rest
80                                         ret = rsingleTag.exec( selector );
81
82                                         if ( ret ) {
83                                                 selector = [ doc.createElement( ret[1] ) ];
84
85                                         } else {
86                                                 ret = buildFragment( [ match[1] ], [ doc ] );
87                                                 selector = (ret.cacheable ? ret.fragment.cloneNode(true) : ret.fragment).childNodes;
88                                         }
89
90                                 // HANDLE: $("#id")
91                                 } else {
92                                         elem = document.getElementById( match[2] );
93
94                                         if ( elem ) {
95                                                 // Handle the case where IE and Opera return items
96                                                 // by name instead of ID
97                                                 if ( elem.id !== match[2] ) {
98                                                         return rootjQuery.find( selector );
99                                                 }
100
101                                                 // Otherwise, we inject the element directly into the jQuery object
102                                                 this.length++;
103                                                 this[0] = elem;
104                                         }
105
106                                         this.context = document;
107                                         this.selector = selector;
108                                         return this;
109                                 }
110
111                         // HANDLE: $(expr, $(...))
112                         } else if ( !context || context.jquery ) {
113                                 return (context || rootjQuery).find( selector );
114
115                         // HANDLE: $(expr, context)
116                         // (which is just equivalent to: $(context).find(expr)
117                         } else {
118                                 return jQuery( context ).find( selector );
119                         }
120
121                 // HANDLE: $(function)
122                 // Shortcut for document ready
123                 } else if ( jQuery.isFunction( selector ) ) {
124                         return rootjQuery.ready( selector );
125                 }
126
127                 // Make sure that old selector state is passed along
128                 if ( selector.selector && selector.context ) {
129                         this.selector = selector.selector;
130                         this.context = selector.context;
131                 }
132
133                 return this.setArray(jQuery.isArray( selector ) ?
134                         selector :
135                         jQuery.makeArray(selector));
136         },
137
138         // Start with an empty selector
139         selector: "",
140
141         // The current version of jQuery being used
142         jquery: "@VERSION",
143
144         // The default length of a jQuery object is 0
145         length: 0,
146
147         // The number of elements contained in the matched element set
148         size: function() {
149                 return this.length;
150         },
151
152         toArray: function(){
153                 return slice.call( this, 0 );
154         },
155
156         // Get the Nth element in the matched element set OR
157         // Get the whole matched element set as a clean array
158         get: function( num ) {
159                 return num == null ?
160
161                         // Return a 'clean' array
162                         this.toArray() :
163
164                         // Return just the object
165                         ( num < 0 ? this.slice(num)[ 0 ] : this[ num ] );
166         },
167
168         // Take an array of elements and push it onto the stack
169         // (returning the new matched element set)
170         pushStack: function( elems, name, selector ) {
171                 // Build a new jQuery matched element set
172                 var ret = jQuery( elems || null );
173
174                 // Add the old object onto the stack (as a reference)
175                 ret.prevObject = this;
176
177                 ret.context = this.context;
178
179                 if ( name === "find" ) {
180                         ret.selector = this.selector + (this.selector ? " " : "") + selector;
181                 } else if ( name ) {
182                         ret.selector = this.selector + "." + name + "(" + selector + ")";
183                 }
184
185                 // Return the newly-formed element set
186                 return ret;
187         },
188
189         // Force the current matched set of elements to become
190         // the specified array of elements (destroying the stack in the process)
191         // You should use pushStack() in order to do this, but maintain the stack
192         setArray: function( elems ) {
193                 // Resetting the length to 0, then using the native Array push
194                 // is a super-fast way to populate an object with array-like properties
195                 this.length = 0;
196                 push.apply( this, elems );
197
198                 return this;
199         },
200
201         // Execute a callback for every element in the matched set.
202         // (You can seed the arguments with an array of args, but this is
203         // only used internally.)
204         each: function( callback, args ) {
205                 return jQuery.each( this, callback, args );
206         },
207
208         // Determine the position of an element within
209         // the matched set of elements
210         index: function( elem ) {
211                 if ( !elem || typeof elem === "string" ) {
212                         return jQuery.inArray( this[0],
213                                 // If it receives a string, the selector is used
214                                 // If it receives nothing, the siblings are used
215                                 elem ? jQuery( elem ) : this.parent().children() );
216                 }
217                 // Locate the position of the desired element
218                 return jQuery.inArray(
219                         // If it receives a jQuery object, the first element is used
220                         elem.jquery ? elem[0] : elem, this );
221         },
222
223         is: function( selector ) {
224                 return !!selector && jQuery.multiFilter( selector, this ).length > 0;
225         },
226
227         // For internal use only.
228         // Behaves like an Array's method, not like a jQuery method.
229         push: push,
230         sort: [].sort,
231         splice: [].splice
232 };
233
234 // Give the init function the jQuery prototype for later instantiation
235 jQuery.fn.init.prototype = jQuery.fn;
236
237 jQuery.extend = jQuery.fn.extend = function() {
238         // copy reference to target object
239         var target = arguments[0] || {}, i = 1, length = arguments.length, deep = false, options, name, src, copy;
240
241         // Handle a deep copy situation
242         if ( typeof target === "boolean" ) {
243                 deep = target;
244                 target = arguments[1] || {};
245                 // skip the boolean and the target
246                 i = 2;
247         }
248
249         // Handle case when target is a string or something (possible in deep copy)
250         if ( typeof target !== "object" && !jQuery.isFunction(target) ) {
251                 target = {};
252         }
253
254         // extend jQuery itself if only one argument is passed
255         if ( length === i ) {
256                 target = this;
257                 --i;
258         }
259
260         for ( ; i < length; i++ ) {
261                 // Only deal with non-null/undefined values
262                 if ( (options = arguments[ i ]) != null ) {
263                         // Extend the base object
264                         for ( name in options ) {
265                                 src = target[ name ];
266                                 copy = options[ name ];
267
268                                 // Prevent never-ending loop
269                                 if ( target === copy ) {
270                                         continue;
271                                 }
272
273                                 // Recurse if we're merging object values
274                                 if ( deep && copy && typeof copy === "object" && !copy.nodeType ) {
275                                         var clone;
276
277                                         if ( src ) {
278                                                 clone = src;
279                                         } else if ( jQuery.isArray(copy) ) {
280                                                 clone = [];
281                                         } else if ( jQuery.isObject(copy) ) {
282                                                 clone = {};
283                                         } else {
284                                                 clone = copy;
285                                         }
286
287                                         // Never move original objects, clone them
288                                         target[ name ] = jQuery.extend( deep, clone, copy );
289
290                                 // Don't bring in undefined values
291                                 } else if ( copy !== undefined ) {
292                                         target[ name ] = copy;
293                                 }
294                         }
295                 }
296         }
297
298         // Return the modified object
299         return target;
300 };
301
302 jQuery.extend({
303         noConflict: function( deep ) {
304                 window.$ = _$;
305
306                 if ( deep ) {
307                         window.jQuery = _jQuery;
308                 }
309
310                 return jQuery;
311         },
312
313         // See test/unit/core.js for details concerning isFunction.
314         // Since version 1.3, DOM methods and functions like alert
315         // aren't supported. They return false on IE (#2968).
316         isFunction: function( obj ) {
317                 return toString.call(obj) === "[object Function]";
318         },
319
320         isArray: function( obj ) {
321                 return toString.call(obj) === "[object Array]";
322         },
323
324         isObject: function( obj ) {
325                 return this.constructor.call(obj) === Object;
326         },
327
328         isEmptyObject: function( obj ) {
329                 for ( var name in obj ) {
330                         return false;
331                 }
332                 return true;
333         },
334
335         // check if an element is in a (or is an) XML document
336         isXMLDoc: function( elem ) {
337                 // documentElement is verified for cases where it doesn't yet exist
338                 // (such as loading iframes in IE - #4833)
339                 var documentElement = (elem ? elem.ownerDocument || elem : 0).documentElement;
340                 return documentElement ? documentElement.nodeName !== "HTML" : false;
341         },
342
343         // Evalulates a script in a global context
344         globalEval: function( data ) {
345                 if ( data && rnotwhite.test(data) ) {
346                         // Inspired by code by Andrea Giammarchi
347                         // http://webreflection.blogspot.com/2007/08/global-scope-evaluation-and-dom.html
348                         var head = document.getElementsByTagName("head")[0] || document.documentElement,
349                                 script = document.createElement("script");
350
351                         script.type = "text/javascript";
352
353                         if ( jQuery.support.scriptEval ) {
354                                 script.appendChild( document.createTextNode( data ) );
355                         } else {
356                                 script.text = data;
357                         }
358
359                         // Use insertBefore instead of appendChild  to circumvent an IE6 bug.
360                         // This arises when a base node is used (#2709).
361                         head.insertBefore( script, head.firstChild );
362                         head.removeChild( script );
363                 }
364         },
365
366         nodeName: function( elem, name ) {
367                 return elem.nodeName && elem.nodeName.toUpperCase() === name.toUpperCase();
368         },
369
370         // args is for internal usage only
371         each: function( object, callback, args ) {
372                 var name, i = 0,
373                         length = object.length,
374                         isObj = length === undefined || jQuery.isFunction(object);
375
376                 if ( args ) {
377                         if ( isObj ) {
378                                 for ( name in object ) {
379                                         if ( callback.apply( object[ name ], args ) === false ) {
380                                                 break;
381                                         }
382                                 }
383                         } else {
384                                 for ( ; i < length; ) {
385                                         if ( callback.apply( object[ i++ ], args ) === false ) {
386                                                 break;
387                                         }
388                                 }
389                         }
390
391                 // A special, fast, case for the most common use of each
392                 } else {
393                         if ( isObj ) {
394                                 for ( name in object ) {
395                                         if ( callback.call( object[ name ], name, object[ name ] ) === false ) {
396                                                 break;
397                                         }
398                                 }
399                         } else {
400                                 for ( var value = object[0];
401                                         i < length && callback.call( value, i, value ) !== false; value = object[++i] ) {}
402                         }
403                 }
404
405                 return object;
406         },
407
408         trim: function( text ) {
409                 return (text || "").replace( rtrim, "" );
410         },
411
412         makeArray: function( array ) {
413                 var ret = [], i;
414
415                 if ( array != null ) {
416                         i = array.length;
417
418                         // The window, strings (and functions) also have 'length'
419                         if ( i == null || typeof array === "string" || jQuery.isFunction(array) || array.setInterval ) {
420                                 ret[0] = array;
421                         } else {
422                                 while ( i ) {
423                                         ret[--i] = array[i];
424                                 }
425                         }
426                 }
427
428                 return ret;
429         },
430
431         inArray: function( elem, array ) {
432                 for ( var i = 0, length = array.length; i < length; i++ ) {
433                         if ( array[ i ] === elem ) {
434                                 return i;
435                         }
436                 }
437
438                 return -1;
439         },
440
441         merge: function( first, second ) {
442                 // We have to loop this way because IE & Opera overwrite the length
443                 // expando of getElementsByTagName
444                 var i = 0, elem, pos = first.length;
445
446                 // Also, we need to make sure that the correct elements are being returned
447                 // (IE returns comment nodes in a '*' query)
448                 if ( !jQuery.support.getAll ) {
449                         while ( (elem = second[ i++ ]) != null ) {
450                                 if ( elem.nodeType !== 8 ) {
451                                         first[ pos++ ] = elem;
452                                 }
453                         }
454
455                 } else {
456                         while ( (elem = second[ i++ ]) != null ) {
457                                 first[ pos++ ] = elem;
458                         }
459                 }
460
461                 return first;
462         },
463
464         unique: function( array ) {
465                 var ret = [], done = {}, id;
466
467                 try {
468                         for ( var i = 0, length = array.length; i < length; i++ ) {
469                                 id = jQuery.data( array[ i ] );
470
471                                 if ( !done[ id ] ) {
472                                         done[ id ] = true;
473                                         ret.push( array[ i ] );
474                                 }
475                         }
476                 } catch( e ) {
477                         ret = array;
478                 }
479
480                 return ret;
481         },
482
483         grep: function( elems, callback, inv ) {
484                 var ret = [];
485
486                 // Go through the array, only saving the items
487                 // that pass the validator function
488                 for ( var i = 0, length = elems.length; i < length; i++ ) {
489                         if ( !inv !== !callback( elems[ i ], i ) ) {
490                                 ret.push( elems[ i ] );
491                         }
492                 }
493
494                 return ret;
495         },
496
497         map: function( elems, callback ) {
498                 var ret = [], value;
499
500                 // Go through the array, translating each of the items to their
501                 // new value (or values).
502                 for ( var i = 0, length = elems.length; i < length; i++ ) {
503                         value = callback( elems[ i ], i );
504
505                         if ( value != null ) {
506                                 ret[ ret.length ] = value;
507                         }
508                 }
509
510                 return ret.concat.apply( [], ret );
511         },
512
513         // Use of jQuery.browser is deprecated.
514         // It's included for backwards compatibility and plugins,
515         // although they should work to migrate away.
516         browser: {
517                 version: (/.+(?:rv|it|ra|ie)[\/: ]([\d.]+)/.exec(userAgent) || [0,'0'])[1],
518                 safari: /webkit/.test( userAgent ),
519                 opera: /opera/.test( userAgent ),
520                 msie: /msie/.test( userAgent ) && !/opera/.test( userAgent ),
521                 mozilla: /mozilla/.test( userAgent ) && !/(compatible|webkit)/.test( userAgent )
522         }
523 });
524
525 // All jQuery objects should point back to these
526 rootjQuery = jQuery(document);
527
528 function evalScript( i, elem ) {
529         if ( elem.src ) {
530                 jQuery.ajax({
531                         url: elem.src,
532                         async: false,
533                         dataType: "script"
534                 });
535         } else {
536                 jQuery.globalEval( elem.text || elem.textContent || elem.innerHTML || "" );
537         }
538
539         if ( elem.parentNode ) {
540                 elem.parentNode.removeChild( elem );
541         }
542 }
543
544 function now() {
545         return (new Date).getTime();
546 }