PrefixSearch: Avoid notice when no subpage exists
[mediawiki.git] / resources / lib / jquery / jquery.migrate.js
blob5b182366597d9e1f97cd80b5b2a41749d40c52b0
1 /*!
2  * jQuery Migrate - v1.2.1 - 2013-05-08
3  * https://github.com/jquery/jquery-migrate
4  * Copyright 2005, 2013 jQuery Foundation, Inc. and other contributors; Licensed MIT
5  *
6  * Patched for MediaWiki to add mw.track calls. --Krinkle 2014-04-14
7  */
8 (function( jQuery, window, undefined ) {
9 // See http://bugs.jquery.com/ticket/13335
10 // "use strict";
13 var warnedAbout = {};
15 // List of warnings already given; public read only
16 jQuery.migrateWarnings = [];
18 // Set to true to prevent console output; migrateWarnings still maintained
19 // jQuery.migrateMute = false;
21 // Show a message on the console so devs know we're active
22 if ( !jQuery.migrateMute && window.console && window.console.log ) {
23         window.console.log("JQMIGRATE: Logging is active");
26 // Set to false to disable traces that appear with warnings
27 if ( jQuery.migrateTrace === undefined ) {
28         jQuery.migrateTrace = true;
31 // Forget any warnings we've already given; public
32 jQuery.migrateReset = function() {
33         warnedAbout = {};
34         jQuery.migrateWarnings.length = 0;
37 function migrateWarn( msg, key ) {
38         var console = window.console;
39         /*
40                 MediaWiki patch for tracking usage.
42                 Custom keys:
43                 - andSelf
44                 - attr-pass
45                 - attr-prop
46                 - bind-error
47                 - clean
48                 - create-html
49                 - data-events
50                 - die
51                 - event-ajax
52                 - event-global
53                 - event-hover
54                 - event-handle
55                 - input-type
56                 - json-invalid
57                 - live
58                 - sub
59                 - toggle-handle
61                 Prop keys:
62                 - attrFn
63                 - browser
64         */
65         mw.track( "jquery.migrate", key || "unknown" );
67         if ( !warnedAbout[ msg ] ) {
68                 warnedAbout[ msg ] = true;
69                 jQuery.migrateWarnings.push( msg );
70                 if ( console && console.warn && !jQuery.migrateMute ) {
71                         console.warn( "JQMIGRATE: " + msg );
72                         if ( jQuery.migrateTrace && console.trace ) {
73                                 console.trace();
74                         }
75                 }
76         }
79 function migrateWarnProp( obj, prop, value, msg, key ) {
80         if ( Object.defineProperty ) {
81                 // On ES5 browsers (non-oldIE), warn if the code tries to get prop;
82                 // allow property to be overwritten in case some other plugin wants it
83                 try {
84                         Object.defineProperty( obj, prop, {
85                                 configurable: true,
86                                 enumerable: true,
87                                 get: function() {
88                                         migrateWarn( msg, key || prop );
89                                         return value;
90                                 },
91                                 set: function( newValue ) {
92                                         migrateWarn( msg, key || prop );
93                                         value = newValue;
94                                 }
95                         });
96                         return;
97                 } catch( err ) {
98                         // IE8 is a dope about Object.defineProperty, can't warn there
99                 }
100         }
102         // Non-ES5 (or broken) browser; just set the property
103         jQuery._definePropertyBroken = true;
104         obj[ prop ] = value;
107 if ( document.compatMode === "BackCompat" ) {
108         // jQuery has never supported or tested Quirks Mode
109         migrateWarn( "jQuery is not compatible with Quirks Mode" );
113 var attrFn = jQuery( "<input/>", { size: 1 } ).attr("size") && jQuery.attrFn,
114         oldAttr = jQuery.attr,
115         valueAttrGet = jQuery.attrHooks.value && jQuery.attrHooks.value.get ||
116                 function() { return null; },
117         valueAttrSet = jQuery.attrHooks.value && jQuery.attrHooks.value.set ||
118                 function() { return undefined; },
119         rnoType = /^(?:input|button)$/i,
120         rnoAttrNodeType = /^[238]$/,
121         rboolean = /^(?:autofocus|autoplay|async|checked|controls|defer|disabled|hidden|loop|multiple|open|readonly|required|scoped|selected)$/i,
122         ruseDefault = /^(?:checked|selected)$/i;
124 // jQuery.attrFn
125 migrateWarnProp( jQuery, "attrFn", attrFn || {}, "jQuery.attrFn is deprecated" );
127 jQuery.attr = function( elem, name, value, pass ) {
128         var lowerName = name.toLowerCase(),
129                 nType = elem && elem.nodeType;
131         if ( pass ) {
132                 // Since pass is used internally, we only warn for new jQuery
133                 // versions where there isn't a pass arg in the formal params
134                 if ( oldAttr.length < 4 ) {
135                         migrateWarn("jQuery.fn.attr( props, pass ) is deprecated", "attr-pass" );
136                 }
137                 if ( elem && !rnoAttrNodeType.test( nType ) &&
138                         (attrFn ? name in attrFn : jQuery.isFunction(jQuery.fn[name])) ) {
139                         return jQuery( elem )[ name ]( value );
140                 }
141         }
143         // Warn if user tries to set `type`, since it breaks on IE 6/7/8; by checking
144         // for disconnected elements we don't warn on $( "<button>", { type: "button" } ).
145         if ( name === "type" && value !== undefined && rnoType.test( elem.nodeName ) && elem.parentNode ) {
146                 migrateWarn("Can't change the 'type' of an input or button in IE 6/7/8", "input-type");
147         }
149         // Restore boolHook for boolean property/attribute synchronization
150         if ( !jQuery.attrHooks[ lowerName ] && rboolean.test( lowerName ) ) {
151                 jQuery.attrHooks[ lowerName ] = {
152                         get: function( elem, name ) {
153                                 // Align boolean attributes with corresponding properties
154                                 // Fall back to attribute presence where some booleans are not supported
155                                 var attrNode,
156                                         property = jQuery.prop( elem, name );
157                                 return property === true || typeof property !== "boolean" &&
158                                         ( attrNode = elem.getAttributeNode(name) ) && attrNode.nodeValue !== false ?
160                                         name.toLowerCase() :
161                                         undefined;
162                         },
163                         set: function( elem, value, name ) {
164                                 var propName;
165                                 if ( value === false ) {
166                                         // Remove boolean attributes when set to false
167                                         jQuery.removeAttr( elem, name );
168                                 } else {
169                                         // value is true since we know at this point it's type boolean and not false
170                                         // Set boolean attributes to the same name and set the DOM property
171                                         propName = jQuery.propFix[ name ] || name;
172                                         if ( propName in elem ) {
173                                                 // Only set the IDL specifically if it already exists on the element
174                                                 elem[ propName ] = true;
175                                         }
177                                         elem.setAttribute( name, name.toLowerCase() );
178                                 }
179                                 return name;
180                         }
181                 };
183                 // Warn only for attributes that can remain distinct from their properties post-1.9
184                 if ( ruseDefault.test( lowerName ) ) {
185                         migrateWarn( "jQuery.fn.attr('" + lowerName + "') may use property instead of attribute", "attr-prop" );
186                 }
187         }
189         return oldAttr.call( jQuery, elem, name, value );
192 // attrHooks: value
193 jQuery.attrHooks.value = {
194         get: function( elem, name ) {
195                 var nodeName = ( elem.nodeName || "" ).toLowerCase();
196                 if ( nodeName === "button" ) {
197                         return valueAttrGet.apply( this, arguments );
198                 }
199                 if ( nodeName !== "input" && nodeName !== "option" ) {
200                         migrateWarn("jQuery.fn.attr('value') no longer gets properties", "attr-prop");
201                 }
202                 return name in elem ?
203                         elem.value :
204                         null;
205         },
206         set: function( elem, value ) {
207                 var nodeName = ( elem.nodeName || "" ).toLowerCase();
208                 if ( nodeName === "button" ) {
209                         return valueAttrSet.apply( this, arguments );
210                 }
211                 if ( nodeName !== "input" && nodeName !== "option" ) {
212                         migrateWarn("jQuery.fn.attr('value', val) no longer sets properties", "attr-prop");
213                 }
214                 // Does not return so that setAttribute is also used
215                 elem.value = value;
216         }
220 var matched, browser,
221         oldInit = jQuery.fn.init,
222         oldParseJSON = jQuery.parseJSON,
223         // Note: XSS check is done below after string is trimmed
224         rquickExpr = /^([^<]*)(<[\w\W]+>)([^>]*)$/;
226 // $(html) "looks like html" rule change
227 jQuery.fn.init = function( selector, context, rootjQuery ) {
228         var match;
230         if ( selector && typeof selector === "string" && !jQuery.isPlainObject( context ) &&
231                         (match = rquickExpr.exec( jQuery.trim( selector ) )) && match[ 0 ] ) {
232                 // This is an HTML string according to the "old" rules; is it still?
233                 if ( selector.charAt( 0 ) !== "<" ) {
234                         migrateWarn("$(html) HTML strings must start with '<' character", "create-html");
235                 }
236                 if ( match[ 3 ] ) {
237                         migrateWarn("$(html) HTML text after last tag is ignored", "create-html");
238                 }
239                 // Consistently reject any HTML-like string starting with a hash (#9521)
240                 // Note that this may break jQuery 1.6.x code that otherwise would work.
241                 if ( match[ 0 ].charAt( 0 ) === "#" ) {
242                         migrateWarn("HTML string cannot start with a '#' character", "create-html");
243                         jQuery.error("JQMIGRATE: Invalid selector string (XSS)");
244                 }
245                 // Now process using loose rules; let pre-1.8 play too
246                 if ( context && context.context ) {
247                         // jQuery object as context; parseHTML expects a DOM object
248                         context = context.context;
249                 }
250                 if ( jQuery.parseHTML ) {
251                         return oldInit.call( this, jQuery.parseHTML( match[ 2 ], context, true ),
252                                         context, rootjQuery );
253                 }
254         }
255         return oldInit.apply( this, arguments );
257 jQuery.fn.init.prototype = jQuery.fn;
259 // Let $.parseJSON(falsy_value) return null
260 jQuery.parseJSON = function( json ) {
261         if ( !json && json !== null ) {
262                 migrateWarn("jQuery.parseJSON requires a valid JSON string", "json-invalid");
263                 return null;
264         }
265         return oldParseJSON.apply( this, arguments );
268 jQuery.uaMatch = function( ua ) {
269         ua = ua.toLowerCase();
271         var match = /(chrome)[ \/]([\w.]+)/.exec( ua ) ||
272                 /(webkit)[ \/]([\w.]+)/.exec( ua ) ||
273                 /(opera)(?:.*version|)[ \/]([\w.]+)/.exec( ua ) ||
274                 /(msie) ([\w.]+)/.exec( ua ) ||
275                 ua.indexOf("compatible") < 0 && /(mozilla)(?:.*? rv:([\w.]+)|)/.exec( ua ) ||
276                 [];
278         return {
279                 browser: match[ 1 ] || "",
280                 version: match[ 2 ] || "0"
281         };
284 // Don't clobber any existing jQuery.browser in case it's different
285 if ( !jQuery.browser ) {
286         matched = jQuery.uaMatch( navigator.userAgent );
287         browser = {};
289         if ( matched.browser ) {
290                 browser[ matched.browser ] = true;
291                 browser.version = matched.version;
292         }
294         // Chrome is Webkit, but Webkit is also Safari.
295         if ( browser.chrome ) {
296                 browser.webkit = true;
297         } else if ( browser.webkit ) {
298                 browser.safari = true;
299         }
301         jQuery.browser = browser;
304 // Warn if the code tries to get jQuery.browser
305 migrateWarnProp( jQuery, "browser", jQuery.browser, "jQuery.browser is deprecated" );
307 jQuery.sub = function() {
308         function jQuerySub( selector, context ) {
309                 return new jQuerySub.fn.init( selector, context );
310         }
311         jQuery.extend( true, jQuerySub, this );
312         jQuerySub.superclass = this;
313         jQuerySub.fn = jQuerySub.prototype = this();
314         jQuerySub.fn.constructor = jQuerySub;
315         jQuerySub.sub = this.sub;
316         jQuerySub.fn.init = function init( selector, context ) {
317                 if ( context && context instanceof jQuery && !(context instanceof jQuerySub) ) {
318                         context = jQuerySub( context );
319                 }
321                 return jQuery.fn.init.call( this, selector, context, rootjQuerySub );
322         };
323         jQuerySub.fn.init.prototype = jQuerySub.fn;
324         var rootjQuerySub = jQuerySub(document);
325         migrateWarn( "jQuery.sub() is deprecated", "sub" );
326         return jQuerySub;
330 // Ensure that $.ajax gets the new parseJSON defined in core.js
331 jQuery.ajaxSetup({
332         converters: {
333                 "text json": jQuery.parseJSON
334         }
338 var oldFnData = jQuery.fn.data;
340 jQuery.fn.data = function( name ) {
341         var ret, evt,
342                 elem = this[0];
344         // Handles 1.7 which has this behavior and 1.8 which doesn't
345         if ( elem && name === "events" && arguments.length === 1 ) {
346                 ret = jQuery.data( elem, name );
347                 evt = jQuery._data( elem, name );
348                 if ( ( ret === undefined || ret === evt ) && evt !== undefined ) {
349                         migrateWarn("Use of jQuery.fn.data('events') is deprecated", "data-events");
350                         return evt;
351                 }
352         }
353         return oldFnData.apply( this, arguments );
357 var rscriptType = /\/(java|ecma)script/i,
358         oldSelf = jQuery.fn.andSelf || jQuery.fn.addBack;
360 jQuery.fn.andSelf = function() {
361         migrateWarn("jQuery.fn.andSelf() replaced by jQuery.fn.addBack()", "andSelf");
362         return oldSelf.apply( this, arguments );
365 // Since jQuery.clean is used internally on older versions, we only shim if it's missing
366 if ( !jQuery.clean ) {
367         jQuery.clean = function( elems, context, fragment, scripts ) {
368                 // Set context per 1.8 logic
369                 context = context || document;
370                 context = !context.nodeType && context[0] || context;
371                 context = context.ownerDocument || context;
373                 migrateWarn("jQuery.clean() is deprecated", "clean");
375                 var i, elem, handleScript, jsTags,
376                         ret = [];
378                 jQuery.merge( ret, jQuery.buildFragment( elems, context ).childNodes );
380                 // Complex logic lifted directly from jQuery 1.8
381                 if ( fragment ) {
382                         // Special handling of each script element
383                         handleScript = function( elem ) {
384                                 // Check if we consider it executable
385                                 if ( !elem.type || rscriptType.test( elem.type ) ) {
386                                         // Detach the script and store it in the scripts array (if provided) or the fragment
387                                         // Return truthy to indicate that it has been handled
388                                         return scripts ?
389                                                 scripts.push( elem.parentNode ? elem.parentNode.removeChild( elem ) : elem ) :
390                                                 fragment.appendChild( elem );
391                                 }
392                         };
394                         for ( i = 0; (elem = ret[i]) != null; i++ ) {
395                                 // Check if we're done after handling an executable script
396                                 if ( !( jQuery.nodeName( elem, "script" ) && handleScript( elem ) ) ) {
397                                         // Append to fragment and handle embedded scripts
398                                         fragment.appendChild( elem );
399                                         if ( typeof elem.getElementsByTagName !== "undefined" ) {
400                                                 // handleScript alters the DOM, so use jQuery.merge to ensure snapshot iteration
401                                                 jsTags = jQuery.grep( jQuery.merge( [], elem.getElementsByTagName("script") ), handleScript );
403                                                 // Splice the scripts into ret after their former ancestor and advance our index beyond them
404                                                 ret.splice.apply( ret, [i + 1, 0].concat( jsTags ) );
405                                                 i += jsTags.length;
406                                         }
407                                 }
408                         }
409                 }
411                 return ret;
412         };
415 var eventAdd = jQuery.event.add,
416         eventRemove = jQuery.event.remove,
417         eventTrigger = jQuery.event.trigger,
418         oldToggle = jQuery.fn.toggle,
419         oldLive = jQuery.fn.live,
420         oldDie = jQuery.fn.die,
421         ajaxEvents = "ajaxStart|ajaxStop|ajaxSend|ajaxComplete|ajaxError|ajaxSuccess",
422         rajaxEvent = new RegExp( "\\b(?:" + ajaxEvents + ")\\b" ),
423         rhoverHack = /(?:^|\s)hover(\.\S+|)\b/,
424         hoverHack = function( events ) {
425                 if ( typeof( events ) !== "string" || jQuery.event.special.hover ) {
426                         return events;
427                 }
428                 if ( rhoverHack.test( events ) ) {
429                         migrateWarn("'hover' pseudo-event is deprecated, use 'mouseenter mouseleave'", "event-hover");
430                 }
431                 return events && events.replace( rhoverHack, "mouseenter$1 mouseleave$1" );
432         };
434 // Event props removed in 1.9, put them back if needed; no practical way to warn them
435 if ( jQuery.event.props && jQuery.event.props[ 0 ] !== "attrChange" ) {
436         jQuery.event.props.unshift( "attrChange", "attrName", "relatedNode", "srcElement" );
439 // Undocumented jQuery.event.handle was "deprecated" in jQuery 1.7
440 if ( jQuery.event.dispatch ) {
441         migrateWarnProp( jQuery.event, "handle", jQuery.event.dispatch, "jQuery.event.handle is undocumented and deprecated", "event-handle" );
444 // Support for 'hover' pseudo-event and ajax event warnings
445 jQuery.event.add = function( elem, types, handler, data, selector ){
446         if ( elem !== document && rajaxEvent.test( types ) ) {
447                 migrateWarn( "AJAX events should be attached to document: " + types, "event-ajax" );
448         }
449         eventAdd.call( this, elem, hoverHack( types || "" ), handler, data, selector );
451 jQuery.event.remove = function( elem, types, handler, selector, mappedTypes ){
452         eventRemove.call( this, elem, hoverHack( types ) || "", handler, selector, mappedTypes );
455 jQuery.fn.error = function() {
456         var args = Array.prototype.slice.call( arguments, 0);
457         migrateWarn("jQuery.fn.error() is deprecated", "bind-error");
458         args.splice( 0, 0, "error" );
459         if ( arguments.length ) {
460                 return this.bind.apply( this, args );
461         }
462         // error event should not bubble to window, although it does pre-1.7
463         this.triggerHandler.apply( this, args );
464         return this;
467 jQuery.fn.toggle = function( fn, fn2 ) {
469         // Don't mess with animation or css toggles
470         if ( !jQuery.isFunction( fn ) || !jQuery.isFunction( fn2 ) ) {
471                 return oldToggle.apply( this, arguments );
472         }
473         migrateWarn("jQuery.fn.toggle(handler, handler...) is deprecated", "toggle-handle");
475         // Save reference to arguments for access in closure
476         var args = arguments,
477                 guid = fn.guid || jQuery.guid++,
478                 i = 0,
479                 toggler = function( event ) {
480                         // Figure out which function to execute
481                         var lastToggle = ( jQuery._data( this, "lastToggle" + fn.guid ) || 0 ) % i;
482                         jQuery._data( this, "lastToggle" + fn.guid, lastToggle + 1 );
484                         // Make sure that clicks stop
485                         event.preventDefault();
487                         // and execute the function
488                         return args[ lastToggle ].apply( this, arguments ) || false;
489                 };
491         // link all the functions, so any of them can unbind this click handler
492         toggler.guid = guid;
493         while ( i < args.length ) {
494                 args[ i++ ].guid = guid;
495         }
497         return this.click( toggler );
500 jQuery.fn.live = function( types, data, fn ) {
501         migrateWarn("jQuery.fn.live() is deprecated", "live");
502         if ( oldLive ) {
503                 return oldLive.apply( this, arguments );
504         }
505         jQuery( this.context ).on( types, this.selector, data, fn );
506         return this;
509 jQuery.fn.die = function( types, fn ) {
510         migrateWarn("jQuery.fn.die() is deprecated", "die");
511         if ( oldDie ) {
512                 return oldDie.apply( this, arguments );
513         }
514         jQuery( this.context ).off( types, this.selector || "**", fn );
515         return this;
518 // Turn global events into document-triggered events
519 jQuery.event.trigger = function( event, data, elem, onlyHandlers  ){
520         if ( !elem && !rajaxEvent.test( event ) ) {
521                 migrateWarn( "Global events are undocumented and deprecated", "event-global" );
522         }
523         return eventTrigger.call( this,  event, data, elem || document, onlyHandlers  );
525 jQuery.each( ajaxEvents.split("|"),
526         function( _, name ) {
527                 jQuery.event.special[ name ] = {
528                         setup: function() {
529                                 var elem = this;
531                                 // The document needs no shimming; must be !== for oldIE
532                                 if ( elem !== document ) {
533                                         jQuery.event.add( document, name + "." + jQuery.guid, function() {
534                                                 jQuery.event.trigger( name, null, elem, true );
535                                         });
536                                         jQuery._data( this, name, jQuery.guid++ );
537                                 }
538                                 return false;
539                         },
540                         teardown: function() {
541                                 if ( this !== document ) {
542                                         jQuery.event.remove( document, name + "." + jQuery._data( this, name ) );
543                                 }
544                                 return false;
545                         }
546                 };
547         }
551 })( jQuery, window );