+ escapeIdForLink: function ( str ) {
+ return escapeIdInternal( str, config.FragmentMode[ 0 ] );
+ },
+
+ /**
+ * Return a wrapper function that is debounced for the given duration.
+ *
+ * When it is first called, a timeout is scheduled. If before the timer
+ * is reached the wrapper is called again, it gets rescheduled for the
+ * same duration from now until it stops being called. The original function
+ * is called from the "tail" of such chain, with the last set of arguments.
+ *
+ * @since 1.34
+ * @param {number} delay Time in milliseconds
+ * @param {Function} callback
+ * @return {Function}
+ */
+ debounce: function ( delay, callback ) {
+ var timeout;
+ return function () {
+ clearTimeout( timeout );
+ timeout = setTimeout( Function.prototype.apply.bind( callback, this, arguments ), delay );
+ };
+ },
+
+ /**
+ * Encode page titles for use in a URL
+ *
+ * We want / and : to be included as literal characters in our title URLs
+ * as they otherwise fatally break the title.
+ *
+ * The others are decoded because we can, it's prettier and matches behaviour
+ * of `wfUrlencode` in PHP.
+ *
+ * @param {string} str String to be encoded.
+ * @return {string} Encoded string
+ */
+ wikiUrlencode: function ( str ) {
+ return util.rawurlencode( str )
+ .replace( /%20/g, '_' )
+ // wfUrlencode replacements
+ .replace( /%3B/g, ';' )
+ .replace( /%40/g, '@' )
+ .replace( /%24/g, '$' )
+ .replace( /%21/g, '!' )
+ .replace( /%2A/g, '*' )
+ .replace( /%28/g, '(' )
+ .replace( /%29/g, ')' )
+ .replace( /%2C/g, ',' )
+ .replace( /%2F/g, '/' )
+ .replace( /%7E/g, '~' )
+ .replace( /%3A/g, ':' );
+ },
+
+ /**
+ * Get the link to a page name (relative to `wgServer`),
+ *
+ * @param {string|null} [pageName=wgPageName] Page name
+ * @param {Object} [params] A mapping of query parameter names to values,
+ * e.g. `{ action: 'edit' }`
+ * @return {string} Url of the page with name of `pageName`
+ */
+ getUrl: function ( pageName, params ) {
+ var fragmentIdx, url, query, fragment,
+ title = typeof pageName === 'string' ? pageName : mw.config.get( 'wgPageName' );
+
+ // Find any fragment
+ fragmentIdx = title.indexOf( '#' );
+ if ( fragmentIdx !== -1 ) {
+ fragment = title.slice( fragmentIdx + 1 );
+ // Exclude the fragment from the page name
+ title = title.slice( 0, fragmentIdx );
+ }
+
+ // Produce query string
+ if ( params ) {
+ query = $.param( params );
+ }
+ if ( query ) {
+ url = title ?
+ util.wikiScript() + '?title=' + util.wikiUrlencode( title ) + '&' + query :
+ util.wikiScript() + '?' + query;
+ } else {
+ url = mw.config.get( 'wgArticlePath' )
+ .replace( '$1', util.wikiUrlencode( title ).replace( /\$/g, '$$$$' ) );
+ }
+
+ // Append the encoded fragment
+ if ( fragment && fragment.length ) {
+ url += '#' + util.escapeIdForLink( fragment );