Fixed typo in commit #6461.
[jquery.git] / src / manipulation.js
1 var rinlinejQuery = / jQuery\d+="(?:\d+|null)"/g,
2         rleadingWhitespace = /^\s+/,
3         rsingleTag = /^<(\w+)\s*\/?>$/,
4         rxhtmlTag = /(<(\w+)[^>]*?)\/>/g,
5         rselfClosing = /^(?:abbr|br|col|img|input|link|meta|param|hr|area|embed)$/i,
6         rinsideTable = /^<(thead|tbody|tfoot|colg|cap)/,
7         rtbody = /<tbody/i;
8
9 jQuery.fn.extend({
10         text: function( text ) {
11                 if ( typeof text !== "object" && text !== undefined )
12                         return this.empty().append( (this[0] && this[0].ownerDocument || document).createTextNode( text ) );
13
14                 var ret = "";
15
16                 jQuery.each( text || this, function(){
17                         jQuery.each( this.childNodes, function(){
18                                 if ( this.nodeType !== 8 ) {
19                                         ret += this.nodeType !== 1 ?
20                                                 this.nodeValue :
21                                                 jQuery.fn.text( [ this ] );
22                                 }
23                         });
24                 });
25
26                 return ret;
27         },
28
29         wrapAll: function( html ) {
30                 if ( jQuery.isFunction( html ) ) {
31                         return this.each(function() {
32                                 jQuery(this).wrapAll( html.apply(this, arguments) );
33                         });
34                 }
35                 
36                 if ( this[0] ) {
37                         // The elements to wrap the target around
38                         var wrap = jQuery( html, this[0].ownerDocument ).eq(0).clone();
39
40                         if ( this[0].parentNode ) {
41                                 wrap.insertBefore( this[0] );
42                         }
43
44                         wrap.map(function(){
45                                 var elem = this;
46
47                                 while ( elem.firstChild && elem.firstChild.nodeType === 1 ) {
48                                         elem = elem.firstChild;
49                                 }
50
51                                 return elem;
52                         }).append(this);
53                 }
54
55                 return this;
56         },
57
58         wrapInner: function( html ) {
59                 return this.each(function(){
60                         jQuery( this ).contents().wrapAll( html );
61                 });
62         },
63
64         wrap: function( html ) {
65                 return this.each(function(){
66                         jQuery( this ).wrapAll( html );
67                 });
68         },
69
70         append: function() {
71                 return this.domManip(arguments, true, function(elem){
72                         if ( this.nodeType === 1 ) {
73                                 this.appendChild( elem );
74                         }
75                 });
76         },
77
78         prepend: function() {
79                 return this.domManip(arguments, true, function(elem){
80                         if ( this.nodeType === 1 ) {
81                                 this.insertBefore( elem, this.firstChild );
82                         }
83                 });
84         },
85
86         before: function() {
87                 return this.domManip(arguments, false, function(elem){
88                         this.parentNode.insertBefore( elem, this );
89                 });
90         },
91
92         after: function() {
93                 return this.domManip(arguments, false, function(elem){
94                         this.parentNode.insertBefore( elem, this.nextSibling );
95                 });
96         },
97
98         clone: function( events ) {
99                 // Do the clone
100                 var ret = this.map(function(){
101                         if ( !jQuery.support.noCloneEvent && !jQuery.isXMLDoc(this) ) {
102                                 // IE copies events bound via attachEvent when
103                                 // using cloneNode. Calling detachEvent on the
104                                 // clone will also remove the events from the orignal
105                                 // In order to get around this, we use innerHTML.
106                                 // Unfortunately, this means some modifications to
107                                 // attributes in IE that are actually only stored
108                                 // as properties will not be copied (such as the
109                                 // the name attribute on an input).
110                                 var html = this.outerHTML, ownerDocument = this.ownerDocument;
111                                 if ( !html ) {
112                                         var div = ownerDocument.createElement("div");
113                                         div.appendChild( this.cloneNode(true) );
114                                         html = div.innerHTML;
115                                 }
116
117                                 return jQuery.clean([html.replace(rinlinejQuery, "")
118                                         .replace(rleadingWhitespace, "")], ownerDocument)[0];
119                         } else {
120                                 return this.cloneNode(true);
121                         }
122                 });
123
124                 // Copy the events from the original to the clone
125                 if ( events === true ) {
126                         var orig = this.find("*").andSelf(), i = 0;
127
128                         ret.find("*").andSelf().each(function(){
129                                 if ( this.nodeName !== orig[i].nodeName ) { return; }
130
131                                 var events = jQuery.data( orig[i], "events" );
132
133                                 for ( var type in events ) {
134                                         for ( var handler in events[ type ] ) {
135                                                 jQuery.event.add( this, type, events[ type ][ handler ], events[ type ][ handler ].data );
136                                         }
137                                 }
138
139                                 i++;
140                         });
141                 }
142
143                 // Return the cloned set
144                 return ret;
145         },
146
147         html: function( value ) {
148                 return value === undefined ?
149                         (this[0] ?
150                                 this[0].innerHTML.replace(rinlinejQuery, "") :
151                                 null) :
152                         this.empty().append( value );
153         },
154
155         replaceWith: function( value ) {
156                 return this.after( value ).remove();
157         },
158
159         domManip: function( args, table, callback ) {
160                 var fragment, scripts, cacheable, cached, cacheresults, first,
161                         value = args[0];
162
163                 if ( jQuery.isFunction(value) ) {
164                         return this.each(function() {
165                                 args[0] = value.call(this);
166                                 return jQuery(this).domManip( args, table, callback );
167                         });
168                 }
169
170                 if ( this[0] ) {
171                         if ( args.length === 1 && typeof args[0] === "string" && args[0].length < 512 && args[0].indexOf("<option") < 0 ) {
172                                 cacheable = true;
173                                 cacheresults = jQuery.fragments[ args[0] ];
174                                 if ( cacheresults ) {
175                                         if ( cacheresults !== 1 ) {
176                                                 fragment = cacheresults;
177                                         }
178                                         cached = true;
179                                 }
180                         }
181                         
182                         if ( !fragment ) {
183                                 fragment = (this[0].ownerDocument || this[0]).createDocumentFragment();
184                                 scripts = jQuery.clean( args, (this[0].ownerDocument || this[0]), fragment );
185                         }
186
187                         first = fragment.firstChild;
188
189                         if ( first ) {
190                                 table = table && jQuery.nodeName( first, "tr" );
191
192                                 for ( var i = 0, l = this.length; i < l; i++ ) {
193                                         callback.call(
194                                                 table ?
195                                                         root(this[i], first) :
196                                                         this[i],
197                                                 cacheable || this.length > 1 || i > 0 ?
198                                                         fragment.cloneNode(true) :
199                                                         fragment
200                                         );
201                                 }
202                         }
203
204                         if ( scripts ) {
205                                 jQuery.each( scripts, evalScript );
206                         }
207
208                         if ( cacheable ) {
209                                 jQuery.fragments[ args[0] ] = cacheresults ? fragment : 1;
210                         }
211                 }
212
213                 return this;
214
215                 function root( elem, cur ) {
216                         return jQuery.nodeName(elem, "table") ?
217                                 (elem.getElementsByTagName("tbody")[0] ||
218                                 elem.appendChild(elem.ownerDocument.createElement("tbody"))) :
219                                 elem;
220                 }
221         }
222 });
223
224 jQuery.fragments = {};
225
226 jQuery.each({
227         appendTo: "append",
228         prependTo: "prepend",
229         insertBefore: "before",
230         insertAfter: "after",
231         replaceAll: "replaceWith"
232 }, function(name, original){
233         jQuery.fn[ name ] = function( selector ) {
234                 var ret = [], insert = jQuery( selector );
235
236                 for ( var i = 0, l = insert.length; i < l; i++ ) {
237                         var elems = (i > 0 ? this.clone(true) : this).get();
238                         jQuery.fn[ original ].apply( jQuery(insert[i]), elems );
239                         ret = ret.concat( elems );
240                 }
241
242                 return this.pushStack( ret, name, selector );
243         };
244 });
245
246 jQuery.each({
247         remove: function( selector ) {
248                 if ( !selector || jQuery.multiFilter( selector, [ this ] ).length ) {
249                         if ( this.nodeType === 1 ) {
250                                 cleanData( jQuery("*", this).add(this) );
251                         }
252
253                         if ( this.parentNode ) {
254                                 this.parentNode.removeChild( this );
255                         }
256                 }
257         },
258
259         empty: function() {
260                 // Remove element nodes and prevent memory leaks
261                 if ( this.nodeType === 1 ) {
262                         cleanData( jQuery("*", this) );
263                 }
264
265                 // Remove any remaining nodes
266                 while ( this.firstChild ) {
267                         this.removeChild( this.firstChild );
268                 }
269         }
270 }, function(name, fn){
271         jQuery.fn[ name ] = function(){
272                 return this.each( fn, arguments );
273         };
274 });
275
276 jQuery.extend({
277         clean: function( elems, context, fragment ) {
278                 context = context || document;
279
280                 // !context.createElement fails in IE with an error but returns typeof 'object'
281                 if ( typeof context.createElement === "undefined" ) {
282                         context = context.ownerDocument || context[0] && context[0].ownerDocument || document;
283                 }
284
285                 // If a single string is passed in and it's a single tag
286                 // just do a createElement and skip the rest
287                 if ( !fragment && elems.length === 1 && typeof elems[0] === "string" ) {
288                         var match = rsingleTag.exec(elems[0]);
289                         if ( match ) {
290                                 return [ context.createElement( match[1] ) ];
291                         }
292                 }
293
294                 var ret = [], scripts = [], div = context.createElement("div");
295
296                 jQuery.each(elems, function(i, elem){
297                         if ( typeof elem === "number" ) {
298                                 elem += '';
299                         }
300
301                         if ( !elem ) { return; }
302
303                         // Convert html string into DOM nodes
304                         if ( typeof elem === "string" ) {
305                                 // Fix "XHTML"-style tags in all browsers
306                                 elem = elem.replace(rxhtmlTag, function(all, front, tag){
307                                         return rselfClosing.test(tag) ?
308                                                 all :
309                                                 front + "></" + tag + ">";
310                                 });
311
312                                 // Trim whitespace, otherwise indexOf won't work as expected
313                                 var tags = elem.replace(rleadingWhitespace, "")
314                                         .substring(0, 10).toLowerCase();
315
316                                 var wrap =
317                                         // option or optgroup
318                                         !tags.indexOf("<opt") &&
319                                         [ 1, "<select multiple='multiple'>", "</select>" ] ||
320
321                                         !tags.indexOf("<leg") &&
322                                         [ 1, "<fieldset>", "</fieldset>" ] ||
323
324                                         rinsideTable.test(tags) &&
325                                         [ 1, "<table>", "</table>" ] ||
326
327                                         !tags.indexOf("<tr") &&
328                                         [ 2, "<table><tbody>", "</tbody></table>" ] ||
329
330                                         // <thead> matched above
331                                         (!tags.indexOf("<td") || !tags.indexOf("<th")) &&
332                                         [ 3, "<table><tbody><tr>", "</tr></tbody></table>" ] ||
333
334                                         !tags.indexOf("<col") &&
335                                         [ 2, "<table><tbody></tbody><colgroup>", "</colgroup></table>" ] ||
336
337                                         // IE can't serialize <link> and <script> tags normally
338                                         !jQuery.support.htmlSerialize &&
339                                         [ 1, "div<div>", "</div>" ] ||
340
341                                         [ 0, "", "" ];
342
343                                 // Go to html and back, then peel off extra wrappers
344                                 div.innerHTML = wrap[1] + elem + wrap[2];
345
346                                 // Move to the right depth
347                                 while ( wrap[0]-- ) {
348                                         div = div.lastChild;
349                                 }
350
351                                 // Remove IE's autoinserted <tbody> from table fragments
352                                 if ( !jQuery.support.tbody ) {
353
354                                         // String was a <table>, *may* have spurious <tbody>
355                                         var hasBody = rtbody.test(elem),
356                                                 tbody = !tags.indexOf("<table") && !hasBody ?
357                                                         div.firstChild && div.firstChild.childNodes :
358
359                                                         // String was a bare <thead> or <tfoot>
360                                                         wrap[1] == "<table>" && !hasBody ?
361                                                                 div.childNodes :
362                                                                 [];
363
364                                         for ( var j = tbody.length - 1; j >= 0 ; --j ) {
365                                                 if ( jQuery.nodeName( tbody[ j ], "tbody" ) && !tbody[ j ].childNodes.length ) {
366                                                         tbody[ j ].parentNode.removeChild( tbody[ j ] );
367                                                 }
368                                         }
369
370                                 }
371
372                                 // IE completely kills leading whitespace when innerHTML is used
373                                 if ( !jQuery.support.leadingWhitespace && rleadingWhitespace.test( elem ) ) {
374                                         div.insertBefore( context.createTextNode( rleadingWhitespace.exec(elem)[0] ), div.firstChild );
375                                 }
376
377                                 elem = jQuery.makeArray( div.childNodes );
378                         }
379
380                         if ( elem.nodeType ) {
381                                 ret.push( elem );
382                         } else {
383                                 ret = jQuery.merge( ret, elem );
384                         }
385
386                 });
387
388                 if ( fragment ) {
389                         for ( var i = 0; ret[i]; i++ ) {
390                                 if ( jQuery.nodeName( ret[i], "script" ) && (!ret[i].type || ret[i].type.toLowerCase() === "text/javascript") ) {
391                                         scripts.push( ret[i].parentNode ? ret[i].parentNode.removeChild( ret[i] ) : ret[i] );
392                                 } else {
393                                         if ( ret[i].nodeType === 1 ) {
394                                                 ret.splice.apply( ret, [i + 1, 0].concat(jQuery.makeArray(ret[i].getElementsByTagName("script"))) );
395                                         }
396                                         fragment.appendChild( ret[i] );
397                                 }
398                         }
399
400                         return scripts;
401                 }
402
403                 return ret;
404         }
405 });
406
407 function cleanData( elems ) {
408         for ( var i = 0, l = elems.length; i < l; i++ ) {
409                 var id = elems[i][expando];
410                 if ( id ) {
411                         delete jQuery.cache[ id ];
412                 }
413         }
414 }