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