User:PerfektesChaos/js/redirectResolver/d.js

From Wikipedia, the free encyclopedia
Note: After saving, you have to bypass your browser's cache to see the changes. Google Chrome, Firefox, Microsoft Edge and Safari: Hold down the ⇧ Shift key and click the Reload toolbar button. For details and instructions about other browsers, see Wikipedia:Bypass your cache.
// PerfektesChaos/js/redirectResolver.js
/// Resolve redirects
/// 2023-02-15 PerfektesChaos@de.wikipedia
/// Documentation:  [[w:en:User:PerfektesChaos/js/redirectResolver]]
/// Fingerprint:    #0#0#
/// @license: CC-by-sa/4.0 GPLv3
/// <nowiki>
/* global window:false                                                 */
/* jshint forin:false,
          bitwise:true, curly:true, eqeqeq:true, latedef:true,
          laxbreak:true,
          nocomma:true, strict:true, undef:true, unused:true           */
/* String.fromCharCode()                                               */



( function ( mw, $ ) {
   "use strict";
   var Version   = -1.2,
       Signature = "redirectResolver",
       THIS      = { api:      { Api:     false,
                                 errors:  false,
                                 got:     { } },
                     body:     { exclude: [ // ".wikibase-entity-usage",
                                               ".templatesUsed" // ,
                                            // ".hiddencats",
                                            // ".limitreport"
                                           ],
                                 id:       0,
                                 load:     false,
                                 plus:     [ ],
                                 redirs:   [ ],
                                 reURL:    false,
                                 remove:   false,
                                 selector: "mw-redirect" },
                     change:   { rePerc:  false,
                                 reSpec:  false,
                                 reUS:    false,
                                 rooms:   false,
                                 spot:    "#wpTextbox1" },
                     doc:      { site:    "w:en",
                                 support: "User:PerfektesChaos/js/"
                                          + Signature },
                     edit:     { learn: false },
                     onlink:   { css:    { "background-color": "#F0E68C",
                                           "color":            "#8B0000",
                                           "display":          "inline",
                                           "font-size":        "87%",
                                           "padding-left":     "0.3em",
                                           "padding-right":    "0.3em",
                                           "vertical-align":   "super" },
                                 show:     false,
                                 texts:    { ltr: 0x21AA,
                                             rtl: 0x21A9 },
                                 $button:  false },
                     popup:    { change:   0x21AF,
                                 css:
                        { box:    { "background-color": "#F0E68C",
                                    "border-color":     "#8B0000",
                                    "border-radius":    "11px",
                                    "border-style":     "solid",
                                    "border-width":     "4px",
                                    "display":          "inline-block",
                                    "font-family":      "sans-serif",
                                    "font-size":        "1rem",
                                    "font-style":       "normal",
                                    "font-variant":     "normal",
                                    "font-weight":      "normal",
                                    "padding":          "0.6em",
                                    "position":         "absolute",
                                    "text-align":       "center",
                                    "text-decoration":  "none",
                                    "visibility":       "hidden",
                                    "white-space":      "nowrap",
                                    "z-index":          "11" },
                          change: { "background-color": "#3366CC",
                                    "border-color":     "transparent",
                                    "border-radius":    "4px",
                                    "border-style":     "solid",
                                    "border-width":     "2px",
                                    "color":            "#FFFFFF",
                                    "cursor":           "pointer",
                                    "min-width":        "3em",
                                    "text-align":       "center" },
                          count:  { "border-color":     "#C0C0C0",
                                    "border-radius":    "4px",
                                    "border-style":     "solid",
                                    "border-width":     "1px",
                                    "font-size":        "90%",
                                    "padding":          "0.2em",
                                    "text-align":       "center" },
                          count0: { "background-color": "#FF0000",
                                    "color":            "#FFFFFF" },
                          countN: { "background-color": "#228B22",
                                    "color":            "#FFFF00" },
                          err:    { "color":            "#FF0000" },
                          exit:   { "background-color": "#FF0000",
                                    "border-color":     "transparent",
                                    "border-radius":    "4px",
                                    "border-style":     "solid",
                                    "border-width":     "2px",
                                    "color":            "#FFFFFF",
                                    "cursor":           "pointer",
                                    "font-weight":      "bold",
                                    "line-height":      "1.1em",
                                    "margin-top":       "0.2em",
                                    "padding-left":     "0.3em",
                                    "padding-right":    "0.3em",
                                    "text-align":       "center",
                                    "vertical-align":   "middle" },
                          going:  { "background-color": "#FFFFFF",
                                    "border-color":     "#3366CC",
                                    "border-radius":    "4px",
                                    "border-style":     "solid",
                                    "border-width":     "2px",
                                    "color":            "#000000",
                                    "min-width":        "3em",
                                    "text-align":       "center" },
                          help:   { "font-size":        "70%" },
                          t1:     { "font-family":      "sans-serif",
                                    "font-size":        "87%" },
                          t2:     { "color":            "#8B0000",
                                    "font-family":      "serif",
                                    "font-size":        "92%" }
                        },
                                 live:     false,
                                 margin:   20,
                                 millisec: 500,
                                 next:     10,
                                 timer:    false,
                                 $box:     false },
                     using:    [ "mediawiki.Title",
                                 "mediawiki.util" ]
                   },
       REPOS     = { };



//-----------------------------------------------------------------------



   function factory( apply ) {
      // Find text in direction from string or number
      // Precondition:
      //    apply  -- something
      // Postcondition:
      //    Returns string, or not
      // Uses:
      //    >  .body.ltr
      // 2022-12-27 PerfektesChaos@de.wikipedia
      var v = apply,
          r;
      if ( typeof v  ===  "object"
           &&     v ) {
         v = v[ ( THIS.body.ltr ? "ltr" : "rtl" ) ];
      }
      switch ( typeof v ) {
         case "number":
            r = String.fromCharCode( v );
            break;
         case "string":
            r = v;
            break;
      }   // switch  typeof v
      return  r;
   }   // factory()



   function fire() {
      // Resources ready, wait for DOM now
      // Uses:
      //    (.body.fire)
      // 2023-01-18 PerfektesChaos@de.wikipedia
      mw.hook( "wikipage.content" ).add( THIS.body.fire );
   }   // fire()



   function first() {
      // Autorun on loading
      // Uses:
      //    >  Signature
      //    >  Version
      //    >  .using
      //    >  .doc.site
      //    >  .doc.support
      //     < .signature
      //     < .edit.learn
      //     < .pub
      //    (fire)
      // 2022-12-27 PerfektesChaos@de.wikipedia
      var env, live, rls;
      THIS.signature = "ext.gadget." + Signature;
      if ( mw.loader.getState( THIS.signature )  !==  "ready" ) {
         rls = { };
         rls[ THIS.signature ] = "ready";
         mw.loader.state( rls );
         env = mw.config.get( [ "wgAction",
                                "wgNamespaceNumber" ] );
         switch ( env.wgAction ) {
            case "edit":
            case "submit":
               THIS.edit.learn = true;   // fall through
            case "view":
               live = ( env.wgNamespaceNumber >= 0 );
               break;
         }   // switch wgAction
         if ( live ) {
            live = ( mw.config.get( "wgPageContentModel" )
                                                       ===  "wikitext" );
         }
         if ( live ) {
            mw.loader.using( THIS.using, fire );
         }
         THIS.pub = { doc:  "[[" + THIS.doc.site + ":"
                                 + THIS.doc.support + "]]",
                      type: Signature,
                      vsn:  Version };
         mw.hook( Signature + ".ready" ).fire( THIS.pub );
      }
   }   // first()



//-----------------------------------------------------------------------



   THIS.api.fetch = function () {
      // Retrieve target via API
      // Uses:
      //    >  .api.seek
      //    (.api.fine)
      //    (.api.flop)
      // 2022-12-27 PerfektesChaos@de.wikipedia
      var api = THIS.api,
          q   = { action:    "query",
                  redirects: "1",
                  titles:    api.seek };
      api.Api.get( q ).done( api.fine )
                      .fail( api.flop );
   };   // .api.fetch()



   THIS.api.fine = function ( arrived ) {
      // Answer on query arrived
      // Precondition:
      //    arrived  -- result of upload
      //    assign   -- task object
      // Uses:
      //    >  .api.got
      //    >  .api.seek
      //    >  .popup.$box
      //    >< .popup.live
      //    .popup.found()
      // 2023-01-07 PerfektesChaos@de.wikipedia
      var got, s1, s2;
      if ( THIS.popup.live   &&
           typeof arrived  ===  "object"
           &&     arrived   &&
           typeof arrived.query  ===  "object"
           &&     arrived.query   &&
           typeof arrived.query.redirects  ===  "object"
           &&     arrived.query.redirects   &&
           typeof arrived.query.redirects.length  ===  "number" ) {
         got = arrived.query.redirects[ 0 ];
         if ( typeof got.to  ===  "string"
              &&     got.to   &&
              typeof got.from  ===  "string"
              &&     got.from ) {
            s1 = got.from;
            s2 = got.to;
            if ( s1 === THIS.api.seek ) {
               if ( typeof got.tofragment  ===  "string"
                    &&     got.tofragment ) {
                  s2 = s2 + " #" + got.tofragment;
               }
               THIS.popup.found( s2 );
            }
            THIS.api.got[ s1 ] = s2;
         }
      } else {
         THIS.popup.$box.hide();
         THIS.popup.live = false;
      }
   };   // .api.fine()



   THIS.api.fire = function ( ask ) {
      // Request target
      // Precondition:
      //    ask  -- redirect page name
      // Uses:
      //    >  .api.got
      //    >< .api.Api
      //     < .api.seek
      //    .popup.found()
      //    .api.fetch()
      //    (.api.fetch)
      // 2022-12-27 PerfektesChaos@de.wikipedia
      var api = THIS.api,
          i   = ask.indexOf( "#" ),
          s   = ask;
      if ( i > 0 ) {
         s = s.substr( 0, i );
      }
      if ( typeof api.got[ s ]  ===  "string" ) {
         THIS.popup.found( api.got[ s ] );
      } else {
         api.seek = s;
         if ( api.Api ) {
            api.fetch();
         } else {
            api.Api = new mw.Api();
            mw.loader.using( [ "mediawiki.api" ],
                             api.fetch
                           );
         }
      }
   };   // .api.fire()



   THIS.api.flop = function ( jqXHR, textStatus, errorThrown ) {
      // Uses:
      //    >  .popup.$t2
      //    >  .popup.$error
      // 2023-02-15 PerfektesChaos@de.wikipedia
      THIS.popup.$t2.hide();
      THIS.popup.$error.show()
                       .text( errorThrown + " " + textStatus );
      if ( typeof window.console  ===  "object"   &&
           typeof window.console.log  ===  "function" ) {
         window.console.log( jqXHR );
      }
   };   // .api.flop()



//-----------------------------------------------------------------------



   THIS.body.$factory = function () {
      // Create detached postfix button
      // Postcondition:
      //    Returns string, or not
      // Uses:
      //    >  .body.selector
      //    >  .body.selected
      //    >  Signature
      //    >  .onlink.css
      //    >  .onlink.show
      //    >< .onlink.$button
      // 2022-12-27 PerfektesChaos@de.wikipedia
      if ( typeof THIS.onlink.$button  !==  "object" ) {
         THIS.onlink.$button = $( "<div>" )
                                 .addClass( THIS.body.selector )
                                 .addClass( THIS.body.selected + "-bgc" )
                                 .attr( { "lang":  "en",
                                          "role":  "button",
                                          "title": Signature } )
                                 .css( THIS.onlink.css )
                                 .text( THIS.onlink.show );
      }
      return  THIS.onlink.$button.clone();
   };   // .body.$factory()



   THIS.body.fiat = function () {
      // Handle click on document button
      // Uses:
      //    >  this
      //    >  .popup.live
      //    >  .body.selected
      //    >  .redirs
      //    >< .body.id
      //    .popup.finish()
      //    .popup.fire()
      // 2022-12-27 PerfektesChaos@de.wikipedia
      /* jshint validthis:true                                         */
      var $btn = $( this ),
          id   = $btn.data( THIS.body.selected );
      if ( typeof id  ===  "string" ) {
         id = parseInt( id, 10 );
      }
      if ( typeof id  ===  "number" ) {
         if ( THIS.popup.live  &&  id === THIS.body.id ) {
            THIS.popup.finish();
         } else {
            THIS.body.id = id;
            id--;
            if ( THIS.body.redirs[ id ] ) {
               THIS.popup.fire( id );
            }
         }
      }
      // Returns always false, stop the bubbling
      return false;
   };   // .body.fiat()



   THIS.body.fill = function () {
      // Handle particular <a.mw-redirect> element
      // Uses:
      //    >  this
      //    >  .body.remove
      //    >  .body.reURL
      //    >  .body.selected
      //    >  .body.redirs
      //    >  .body.plus
      //    >< .body.reURL
      //    >< .body.id
      //    .body.$factory()
      //    (.body.fiat)
      // 2023-02-15 PerfektesChaos@de.wikipedia
      /* jshint validthis:true                                         */
      var body = THIS.body,
          $a   = $( this ),
          k, $btn;
      $a.addClass( body.selected );
      if ( body.remove ) {
         for ( k = 0;  k < body.remove.length;  k++ ) {
            if ( $a.parents( body.remove[ k ] ).length ) {
               $a = false;
               break;   // for k
            }
         }   // for k
      }
      if ( $a ) {
         if ( ! body.reURL ) {
            body.reURL = new RegExp( "^(?:(?:https?:)//[^/]+)?/wiki/" );
         }
         if ( ! body.reURL.test( $a.attr( "href" )) ) {
            $a = false;
         }
      }
      if ( $a ) {
         $btn = body.$factory();
         body.id++;
         $btn.click( body.fiat )
             .data( body.selected, body.id );
         $a.after( $btn );
         body.redirs.push( $a );
         body.plus.push( $btn );
      }
   };   // .body.fill()



   THIS.body.fire = function ( $area ) {
      // Resources and DOM ready
      // Precondition:
      //    $area  -- region of interest
      // Uses:
      //    >  .body.selector
      //    >  .onlink.texts
      //    >  Signature
      //    >  .body.exclude
      //    >< .body.load
      //     < .body.ltr
      //     < .onlink.show
      //     < .body.selected
      //     < .body.$area
      //     < .body.remove
      //     < .body.id
      //    factory()
      //    (.popup.fit)
      //    (.body.fill)
      // 2023-01-18 PerfektesChaos@de.wikipedia
      var body = THIS.body,
          sel  = "a." + body.selector,
          k, o, $redirs;
      if ( ! body.load ) {
         body.load = true;
         body.ltr  = ( $( "html" ).attr( "dir" )  !==  "rtl" );
         o = THIS.onlink;
         o.show = factory( o.texts );
         if ( ! o.show ) {
            k      = ( body.ltr ? 0x21B2 : 0x21B3 );
            o.show = String.fromCharCode( k );
         }
         body.selected = "gadget-" + Signature.toLowerCase();
         $( window ).resize( THIS.popup.fit );
      }
      body.$area = $area;
      $redirs    = body.$area.find( sel );
      if ( $redirs.length ) {
         if ( typeof body.exclude  ===  "object"
              &&     body.exclude   &&
              typeof body.exclude.length  ===  "number" ) {
            for ( k = 0;  k < body.exclude.length;  k++ ) {
               o = body.exclude[ k ];
               if ( body.$area.find( o + " " + sel ).length ) {
                  body.remove = body.remove  ||  [ ];
                  body.remove.push( o );
               }
            }   // for k
         }
         $redirs.not( "." + body.selected ).each( body.fill );
         body.id = -1;
      }
   };   // .body.fire()



//-----------------------------------------------------------------------



   THIS.change.fire = function ( assign ) {
      // Exchange link targets
      // Precondition:
      //    assign  -- redirect target page name, perhaps also fragment
      // Postcondition:
      //    Returns number of replacements
      // Uses:
      //    >  .change.spot
      //    >  .edit.origin
      //     < .edit.learn
      //    .change.fold()
      //    .change.fit()
      //    .change.flip()
      // 2023-01-18 PerfektesChaos@de.wikipedia
      var ch = THIS.change,
          r  = 0,
          $t = $( ch.spot ),
          i, j, p, s, shift, story, t;
      if ( ! $t.length  ||
           $t.attr( "readonly" ) ) {
         THIS.edit.learn = false;
      }
      if ( THIS.edit.learn ) {
         p = ch.fold( assign );
         if ( p ) {
            story = $t.val();
            i     = 0;
            do {
               i = story.indexOf( "[[", i );
               if ( i >= 0 ) {
                  i += 2;
                  j  = story.indexOf( "]]", i );
                  if ( j >= 0 ) {
                     s = story.substring( i, j );
                     t = ch.fit( s, THIS.edit.origin );
                     if ( t  &&  t[ 1 ] !== 2  &&  t[ 1 ] !== 3 ) {
                        shift = ch.flip( s, t, p );
                        if ( shift ) {
                           story = story.substr( 0, i )
                                   + shift
                                   + story.substr( j );
                           r++;
                           s = shift;
                        }
                     }
                     i += s.length + 2;
                  }
               }
            } while ( i >= 0 );   // do
            if ( r ) {
               $t.val( story );
            }
         }
      }
      return r;
   };   // .change.fire()



   THIS.change.fit = function ( ask, alike ) {
      // Test whether a page is like another one
      // Precondition:
      //    ask    -- string, with inner link content
      //    alike  -- Array with [1]: namespace number, [2]: page title
      // Postcondition:
      //    Returns true if the same page
      // Uses:
      //    .change.fold()
      // 2023-01-02 PerfektesChaos@de.wikipedia
      var s = ask,
          i = s.indexOf( "|" ),
          r;
      if ( i > 0 ) {
         s = s.substr( 0, i );
      }
      r = THIS.change.fold( s );
      if ( ! r   ||
           r[ 1 ] !== alike[ 1 ]  ||
           r[ 2 ] !== alike[ 2 ] ) {
         r = false;
      }
      return r;
   };   // .change.fit()



   THIS.change.flip = function ( at, alter, assign ) {
      // Exchange link
      // Precondition:
      //    at      -- string, with inner source text
      //    alter   -- Array, source link page
      //    assign  -- Array, redirect target page and fragment
      // Postcondition:
      //    Returns modified string, or not
      // Uses:
      //    >< .change.reSpec
      //    >< .change.rooms
      //    .change.fold()
      // 2023-01-02 PerfektesChaos@de.wikipedia
      var ch = THIS.change,
          i, n, r, s, t;
      if ( at.indexOf( "[" )  <  0   &&
           at.indexOf( "]" )  <  0 ) {
         if ( ! ch.reSpec ) {
            ch.reSpec = new RegExp( "[<>{}]" );
         }
         if ( ! ch.reSpec.test( at ) ) {
            i = at.indexOf( "|" );
            if ( i > 0 ) {
               s = at.substr( i + 1 );
               t = ch.fold( at.substr( 0, i ) );
            }
            if ( t   &&
                 t[ 1 ] === assign[ 1 ]   &&
                 t[ 2 ] === assign[ 2 ] ) {
               r = s;
            } else {
               n = assign[ 1 ];
               if ( n ) {
                  if ( ! ch.rooms ) {
                     ch.rooms = mw.config.get( "wgFormattedNamespaces" );
                  }
                  r = ch.rooms[ n ] + ":";
                  if ( n === 6  || n === 14 ) {
                     r = ":" + r;
                  }
               } else {
                  r = "";
               }
               r = r + assign[ 2 ];
               if ( alter[ 3 ] ) {
                  r = r + "#" + alter[ 3 ];
               } else if ( assign[ 3 ] ) {
                  r = r + "#" + assign[ 3 ];
               }
               r = r  +  "|"  +  ( s ? s : at );
            }
         }
      }
      return r;
   };   // .change.flip()



   THIS.change.fold = function ( apply ) {
      // Split into page title and perhaps fragment or namespace
      // Precondition:
      //    apply  -- string
      // Postcondition:
      //    Returns Array, or not
      //                   with  [0]  -- Title object
      //                         [1]  -- namespace number
      //                         [2]  -- page title
      //                         [3]  -- fragment, or not
      // Uses:
      //    >< .change.rePerc
      // 2023-01-02 PerfektesChaos@de.wikipedia
      var ch = THIS.change,
          s  = apply,
          r, t;
      if ( s.indexOf( "%" ) >= 0 ) {
         if ( ! ch.rePerc ) {
            ch.rePerc = new RegExp( "%[0-9A-Fa-f][0-9A-Fa-f]" );
         }
         if ( ch.rePerc.test( s ) ) {
            s = decodeURIComponent( s );
         }
      }
      t = mw.Title.newFromText( s );
      if ( t ) {
         r = [ t, t.getNamespaceId(), t.getMainText(), t.getFragment() ];
      }
      return r;
   };   // .change.fold()



//-----------------------------------------------------------------------



   THIS.edit.fill = function ( apply ) {
      // Register redirect link target now
      // Precondition:
      //    apply  -- target page name, perhaps also fragment
      // Uses:
      //    >  .edit.learn
      //    >  .edit.seek
      //    >  .popup.$change
      //     < .edit.origin
      //     < .edit.shift
      //    .change.fold()
      // 2023-01-18 PerfektesChaos@de.wikipedia
      var e = THIS.edit,
          n;
      if ( e.learn ) {
         e.origin = THIS.change.fold( e.seek );
         if ( e.origin ) {
            n = e.origin[ 1 ];
            if ( ( n === 2  ||  n === 3 )   &&
                 e.origin[ 2 ].indexOf( "/" )  <  0 ) {
               e.origin = false;
            }
         }
         if ( e.origin ) {
            e.shift = apply;
            THIS.popup.$change.show();
         }
      }
   };   // .edit.fill()



   THIS.edit.fire = function () {
      // Exchange link targets now
      // Uses:
      //    >  .popup.$change
      //    >  .popup.$changing
      //    >  .edit.shift
      //    >  .popup.$count
      //    .change.fire()
      // 2023-01-18 PerfektesChaos@de.wikipedia
      var p = THIS.popup,
          n;
      p.$change.hide();
      p.$changing.show();
      n = THIS.change.fire( THIS.edit.shift );
      p.$changing.hide();
      p.$count.css( p.css[ ( n ? "countN" : "count0" ) ] )
              .show()
              .text( n );
      // Returns always false, stop the bubbling
      return false;
   };   // .edit.fire()



   THIS.edit.first = function ( assigned ) {
      // Register redirect link
      // Precondition:
      //    assigned  -- redirect page name, perhaps also fragment
      // Uses:
      //    >  .edit.learn
      //     < .edit.seek
      // 2022-12-27 PerfektesChaos@de.wikipedia
      if ( THIS.edit.learn ) {
         THIS.edit.seek = assigned;
      }
   };   // .edit.first()



   THIS.edit.flat = function () {
      // Reset edit elements
      // Uses:
      //    >  .edit.learn
      //    >  .$change
      //    >  .$changing
      //    >  .$count
      // 2022-12-27 PerfektesChaos@de.wikipedia
      var p = THIS.popup;
      if ( THIS.edit.learn ) {
         p.$change.hide();
         p.$changing.hide();
         p.$count.empty()
                 .hide();
      }
   };   // .edit.flat()



//-----------------------------------------------------------------------



   THIS.popup.factory = function () {
      // Create popup
      // Uses:
      //    >  .popup.css.*
      //    >  .edit.learn
      //    >  Version
      //    >  .doc.*
      //    >  .popup.$a
      //    >  .body.$area
      //     < .popup.$box
      //     < .popup.$t1
      //     < .popup.$t2
      //     < .popup.$error
      //     < .popup.$change
      //     < .popup.$changing
      //     < .popup.$count
      //    factory()
      //    REPOS.foundation()
      //    (.edit.fire)
      //    (.popup.finish)
      // 2023-01-11 PerfektesChaos@de.wikipedia
      var p = THIS.popup,
          t = { "margin":              "0",
                "list-style-image":    "none",
                "list-style-position": "outside",
                "list-style-type":     "none" },
          u = { "display":       "inline-block",
                "padding-left":  "1em",
                "padding-right": "1em" },
          $ol = $( "<ol>" ).css( t ),
          $ul = $( "<ul>" ).css( t ),
          s, service, $exit, $help, $li;
      p.$box = $( "<div>" );
      p.$t1 = $( "<a>" ).attr( { "target": "_blank",
                                 "title":  "redirect=no" } )
                        .css( p.css.t1 );
      p.$t2 = $( "<li>" ).css( p.css.t2 );
      p.$error = $( "<li>" ).addClass( "error" )
                            .css( p.css.err )
                            .hide();
      $li = $( "<li>" ).append( p.$t1 )
                       .css( u );
      p.$error.addClass( "error" )
              .css( p.css.err )
              .hide();
      $ol.append( $li, p.$t2, p.$error );
      if ( THIS.edit.learn ) {
         p.$change = $( "<span>" ).attr( { "lang":  "en",
                                           "role":  "button",
                                           "title": "Exchange" } )
                                  .click( THIS.edit.fire )
                                  .css( p.css.change )
                                  .hide()
                                  .text( factory( p.change )  ||
                                         "!" );
         p.$changing = $( "<span>" ).attr( { "lang":  "en",
                                             "title": "Changing" } )
                                    .css( p.css.going )
                                    .hide()
                                    .text( "..." );
         p.$count = $( "<span>" ).css( p.css.count )
                                 .hide();
         $li = $( "<li>" ).append( p.$change, p.$changing, p.$count )
                          .css( u );
         $ul.append( $li );
      }
      $li = $( "<li>" ).css( u );
      if ( typeof THIS.doc  ===  "object"
           &&     THIS.doc   &&
           typeof THIS.doc.site  ===  "string"
           &&     THIS.doc.site   &&
           typeof THIS.doc.support  ===  "string"
           &&     THIS.doc.support ) {
         service = REPOS.foundation( THIS.doc.site, "" )
                   + "Special:MyLanguage/" + THIS.doc.support;
         $help   = $( "<a>" ).attr( { "href":   service,
                                      "lang":   "en",
                                      "target": "_blank",
                                      "title":  "Help" } )
                             .css( p.css.help )
                             .text( Version );
         $li.append( $help );
      } else {
         $li.css( p.css.help )
            .text( s );
      }
      $exit = $( "<li>" ).attr( { "lang":  "en",
                                  "role":  "button",
                                  "title": "Exit" } )
                         .click( p.finish )
                         .css( p.css.exit )
                         .css( u )
                         .text( "X" );
      $ul.append( $li, $exit );
      p.$box.addClass( THIS.body.selected + "-popup" )
            .addClass( THIS.body.selected + "-bgc" )
            .attr( { "role": "tooltip" } )
            .append( $ol, $ul )
            .css( p.css.box );
      THIS.body.$area.prepend( p.$box );
   };   // .popup.factory()



   THIS.popup.finish = function () {
      // Close popup
      // Uses:
      //    >  .popup.$box
      //     < .popup.live
      // 2023-01-11 PerfektesChaos@de.wikipedia
      THIS.popup.$box.css( "visibility", "hidden" );
      THIS.popup.live = false;
      // Returns always false, stop the bubbling
      return false;
   };   // .popup.finish()



   THIS.popup.fire = function ( at ) {
      // Handle request for popup
      // Precondition:
      //    at  -- index of redirect
      // Uses:
      //    >  .body.redirs
      //    >  .popup.$box
      //    >  .popup.$t2
      //    >  .popup.$error
      //    >  .body.plus
      //    >  .popup.millisec
      //    >< .popup.live
      //    >< .change.reUS
      //     < .popup.$a
      //     < .popup.$p
      //     < .popup.timer
      //   .edit.flat()
      //   .popup.factory()
      //   .edit.first()
      //   .popup.fit()
      //   .api.fire()
      //   (.popup.flush)
      // 2023-01-12 PerfektesChaos@de.wikipedia
      var p = THIS.popup,
          s, s1;
      p.$a = THIS.body.redirs[ at ];
      if ( p.$box ) {
         if ( p.live ) {
            p.$box.css( "visibility", "hidden" );
         }
         p.$t2.empty()
              .show();
         p.$error.hide();
         THIS.edit.flat();
      } else {
         p.factory();
      }
      s = p.$a.attr( "href" );
      if ( s.substr( 0, 6 ) === "/wiki/" ) {
         p.live = true;
         s  = s.substr( 6 );
         s  = decodeURIComponent( s );
         s1 = mw.util.getUrl( s,
                              { redirect: "no" } );
         if ( ! THIS.change.reUS ) {
            THIS.change.reUS = new RegExp( "_", "g" );
         }
         s = s.replace( THIS.change.reUS, " " );
         p.$t1.attr( "href", s1 )
              .text( s );
         THIS.edit.first( s );
         p.$box.detach();
         p.$p = THIS.body.plus[ at ];
         p.$p.after( p.$box );
         p.fit();
         THIS.api.fire( s );
         p.timer = window.setTimeout( p.flush, p.millisec );
      }
   };   // .popup.fire()



   THIS.popup.fit = function () {
      // Handle resize event, or fit updated $box into text
      // Uses:
      //    >  .popup.live
      //    >  .popup.margin
      //    >  .popup.$p
      //    >  .popup.$t1
      //    >  .popup.$t2
      //    >  .popup.$box
      //    >  .popup.$p
      //    >  .popup.next
      // 2023-02-15 PerfektesChaos@de.wikipedia
      var jP = 0,
          j, k, m, max, o, off, p, $o, $up;
      if ( THIS.popup.live ) {
         p = THIS.popup;
         p.$box.css( "white-space", "nowrap" );
         $up = p.$p;
         p.$t1.text( p.$t1.text() );
         p.$t2.text( p.$t2.text() );
         max = THIS.body.$area.width()  -  2 * p.margin;
         m   = p.$box.width();
         o   = $up.offset();
         if ( m > max ) {
            p.$box.css( "white-space", "normal" )
                  .width( max );
            j = 0;
         } else {
            k = o.left  +  0.5 * $up.width();
            if ( k  +  0.5 * m   >   max ) {
               j = max - m + p.margin;
            } else {
               j = k  -  0.5 * m;
               if ( j < 0 ) {
                  j = 0;
               }
            }
         }
         $o  = $up.offsetParent();
         off = $o.offset();
         if ( off && off.left ) {
            jP = off.left;
            $o = $o.offsetParent();
            if ( $o.length && $o.offset() ) {
               jP = $o.offset().left;
            }
         }
         o.top += $up.outerHeight() + p.next;
         if ( jP ) {
            o.left = jP  +  0.5 * p.margin;
         } else {
            o.left = j + p.margin;
         }
         p.$box.offset( o );
      }
   };   // .popup.fit()



   THIS.popup.flush = function () {
      // Handle delay of initial presentation
      // Uses:
      //    >  .popup.live
      //    >  .popup.$box
      //    >< .popup.timer
      // 2023-01-11 PerfektesChaos@de.wikipedia
      var p = THIS.popup;
      if ( p.live ) {
         p.$box.css( "visibility", "visible" );
      }
      if ( p.timer ) {
         window.clearTimeout( p.timer );
         p.timer = false;
      }
      // Returns always false, stop the bubbling
      return false;
   };   // .popup.flush()



   THIS.popup.found = function ( apply ) {
      // Communicate target page name
      // Precondition:
      //    apply  -- string with target page name, perhaps also fragment
      // Postcondition:
      //    target is visible, and editing mode is ready
      // Uses:
      //    >  .popup.$t2
      //    .edit.fill()
      //    .popup.fit()
      //    .popup.flush()
      // 2023-01-11 PerfektesChaos@de.wikipedia
      var p = THIS.popup;
      p.$t2.text( apply );
      THIS.edit.fill( apply );
      p.fit();
      p.flush();
   };   // .popup.found()



//-----------------------------------------------------------------------



   REPOS.foundation = function ( at, access, alter ) {
      // Create URL within Wikimedia Foundation
      // Precondition:
      //    at      -- site code, or not
      //    access  -- string with page name
      //    alter   -- parameter object, or not
      // Postcondition:
      //    Returns full URL
      // 2018-03-21 PerfektesChaos@de.wikipedia
      var s = access,
          r = encodeURI( s );
      if ( typeof alter  ===  "object"
           &&     alter ) {
         r = "/w/index.php?title=" + r;
         if ( access.substr( -3 ) === ".js" ) {
            alter.ctype = "text/javascript";
         } else if ( access.substr( -4 ) === ".css" ) {
            alter.ctype = "text/css";
         }
         alter.action = "raw";
         for ( s in alter ) {
            r = r + "&" + s + "=" + encodeURI( alter[ s ] );
         }   // for s in alter
      } else {
         r = "/wiki/" + r;
      }
      if ( typeof at  ===  "string"
           &&     at ) {
         switch ( at ) {
            case "meta":
               r = "meta.wikimedia.org" + r;
               break;
            case "mw":
               r = "www.mediawiki.org" + r;
               break;
            case "w:en":
               r = "en.wikipedia.org" + r;
               break;
            default:
               r = window.location.host + r;
         }   // switch at
         r = "https://" + r;
      }
      return r;
   };   // REPOS.foundation()



//-----------------------------------------------------------------------



   first();
}( window.mediaWiki, window.jQuery ) );



// Emacs
// Local Variables:
// coding: utf-8-unix
// fill-column: 80
// End:

/// EOF </nowiki>   redirectResolver.js