Jump to content

User:PerfektesChaos/js/pageLinkHelper/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.
/// Gadget-pageLinkHelper.js
//  Some minor link helpers
/// 2021-05-21 PerfektesChaos@de.wikipedia
//  ResourceLoader: compatible;
//    dependencies: user, user.options,
//                  mediawiki.api, mediawiki.user, mediawiki.util
/// Fingerprint:    #0#0#
//  Documentation:  [[w:en:User:PerfektesChaos/js/pageLinkHelper]]
/// <nowiki>
/* global window: false                                                */
/* jshint bitwise:true, curly:true, eqeqeq:true, latedef:true,
          laxbreak:true,
          nocomma:true, strict:true, undef:true, unused:true           */
( function ( mw, $ ) {
   "use strict";
   var Version   = -2.6,
       Sign      = "pageLinkHelper",
       Signature = "ext.gadget." + Sign,
       Shop      = "w:en:User:PerfektesChaos/js/",
       Diff      = { },
       Hist      = { },
       OPTS      = { },
       Perm      = { },
       Purge     = { },
       QQmsg     = { },
       Redir     = { },
       URL2i     = { },
       Config, Env, PLH;



   Config = { PermanentLink: { "*": "PermaLink" },
              Redirect:      { de: "Weiterleitung" },
              permTitle:     { "*": "PermaLink" },
              purgeLUtip:    { en: "PURGE this page"
                                   + " and force link update",
                               de: "PURGE mit rekursivem Linkupdate" },
              purgeLUtitle:  { en: "PURGE/linkupdate",
                               de: "PURGE/linkupdate" },
              purgeLURtip:   { en: "PURGE this page"
                                   + " and force recursive link update",
                               de: "PURGE mit rekursivem Linkupdate" },
              purgeLURtitle: { en: "PURGE/recursive",
                               de: "PURGE/rekursiv" },
              purgeLURGtip:  { en: "PURGE related pages"
                                   + " and force recursive link update",
                               de: "PURGE bezogener Seiten"
                                   + " mit rekursivem Linkupdate" },
              purgeLURGtitle:{ en: "PURGE/recursive related pages",
                               de: "PURGE/rekursiv bezogene Seiten" },
              purgeTip:      { en: "PURGE this page from server cache",
                               de: "PURGE:"
                                   + " Diese Seite"
                                   + " im Server-Cache aktualisieren" },
              purgeTitle:    { en: "Purge cache",
                               de: "Seite purgen" },
              qqxTip:        { en: "Reload"
                                   + " and show interface element IDs",
                               de: "Neu laden"
                                   + " und MediaWiki-IDs zeigen" },
              qqxTitle:      { en: "lang=qqx" },
              translate:     { "als" :       "de",
                               "bar" :       "de",
                               "dsb" :       "de",
                               "frr" :       "de",
                               "gsw" :       "de",
                               "hsb" :       "de",
                               "ksh" :       "de",
                               "lb" :        "de",
                               "nds" :       "de",
                               "pdc" :       "de",
                               "pdt" :       "de",
                               "pfl" :       "de",
                               "sli" :       "de",
                               "stq" :       "de",
                               "vmf" :       "de" }
            };   // 2015-01-06 PerfektesChaos@de.wikipedia



   function $factory( access ) {
      // Create <div> for upper right corner
      // Precondition:
      //    access  -- distinguishing selector ID
      // Uses:
      //    >  Env.ltr
      //    >  Sign
      // 2015-01-06 PerfektesChaos@de.wikipedia
      var space = "4px",
          style = "#B0C4DE 1px solid",
          $r    = $( "<div>" );
      $r.attr( { "class": Sign,
                 "id":    access } );
      $r.css( { "border-bottom":    style,
                "background-color": "#FFFFFF",
                "background":       "rgba(255, 255, 255, 0.5)",
                "font-family":      "monospace",
                "margin-bottom":    "1em",
                "margin-top":       space,
                "padding-bottom":   space,
                "padding-right":    space,
                "padding-left":     space } );
      if ( Env.ltr ) {
         $r.css( { "border-left":   style,
                   "float":         "right",
                   "margin-left":   "1em" } );
      } else {
         $r.css( { "border-right":  style,
                   "float":         "left",
                   "margin-right":  "1em" } );
      }
      return $r;
   }   // $factory()



   function $float( $apply, anchor ) {
      // Insert/exchange upper right corner element
      // Precondition:
      //    $apply  -- jQuery element to insert
      //               false to check availability
      //    anchor  -- string, with user defined selector, or undefined
      // Postcondition:
      //    Returns true, if insertion possible
      // Uses:
      //    >  Sign
      //    >< Env
      // 2021-05-21 PerfektesChaos@de.wikipedia
      var r,   // = undefined
          sequence, suitable, $anchor, $clear;
      if ( typeof Env.$corner  ===  "undefined" ) {
         Env.ltr     = ( !  $( "body.sitedir-rtl" ).length );
         Env.$corner = $( "<div>" );
         Env.$corner.attr( { "class": Sign,
                             "id":    Sign + "Corner" } );
         if ( anchor ) {
            $anchor = $( anchor );
            if ( $anchor.length > 1 ) {
               $anchor = $anchor.eq( 0 );
            }
         } else {
            if ( ! Env.skin ) {
               Env.skin = mw.config.get( "skin" );
            }
            switch ( Env.skin ) {
               case "cologneblue" :
                  suitable = "#firstHeading";
                  sequence = "after";
                  break;
               case "modern" :
                  suitable = "#contentSub";
                  sequence = "before";
                  break;
               case "monobook" :
               case "vector" :
                  suitable = ".mw-body-content";
                  sequence = "prepend";
                  break;
            }   // switch Env.skin
            $anchor = $( suitable ).eq( 0 );
         }
         if ( ! $anchor.length ) {
            $anchor  = $( "#firstHeading" );
            sequence = "after";
         }
         if ( $anchor.length ) {
            $anchor[ sequence ]( Env.$corner );
            $clear = $( "<div>" );
            $clear.css( { "clear":  ( Env.ltr ? "left" : "right" )  } );
            Env.$corner.after( $clear );
         } else {
            Env.$corner = false;
         }
      }
      if ( Env.$corner ) {
         if ( $apply ) {
            Env.$corner.empty();
            Env.$corner.prepend( $apply );
         }
         r = true;
      }
      return r;
   }   // $float()



   function facility( available ) {
      // Localize in user language, GUI only
      // Precondition:
      //    available  -- translation object
      // Postcondition:
      //    Returns string, or undefined
      // Uses:
      //    >  Config
      //    >< Env
      //    mw.config.get()
      // 2014-12-16 PerfektesChaos@de.wikipedia
      var i, r, slang;
      if ( ! Env.userLang ) {
         Env.userLang = mw.config.get( "wgUserLanguage" ).toLowerCase();
      }
      slang = Env.userLang;
      if ( typeof available[ slang ]  ===  "string" ) {
         r = available[ slang ];
      } else {
         i = slang.indexOf( "-", 2 );
         if ( i > 0 ) {
            slang = slang.substr( 0, i );
            if ( typeof available[ slang ]  ===  "string" ) {
               r = available[ slang ];
            }
         }
      }
      if ( ! r ) {
         if ( typeof Config.translate[ slang ]  ===  "string" ) {
            slang = Config.translate[ slang ];
            if ( typeof available[ slang ]  ===  "string" ) {
               r = available[ slang ];
            }
         }
         if ( ! r ) {
            r = available.en;
         }
      }
      return r;
   }   // facility()



   function fair( access, adapt, alter ) {
      // Localization
      // Precondition:
      //    access  -- configuration keyword
      //    adapt   -- false, if project (language) dependant
      //               else user language dependant
      //    alter   -- default string (en)
      // Postcondition:
      //    Returns value, or keyword 'access'
      // Uses:
      //    >  PLH
      //    >  Config
      //    >< Env
      //    facility()
      //    mw.config.get()
      // 2017-01-20 PerfektesChaos@de.wikipedia
      var el, r, say;
      if ( alter ) {
         r = alter;
      } else {
         r = access;
      }
      if ( typeof PLH.config  ===  "object"   &&
           PLH.config   &&
           typeof PLH.config[ access ]  ===  "object" ) {
         el = PLH.config[ access ];
      }
      if ( ! el ) {
         el = Config[ access ];
      }
      if ( el ) {
         if ( adapt ) {
            say = facility( el );
            if ( say ) {
               r = say;
            } else if ( typeof alter  !==  "string" ) {
               r = "???" + access + "???";
            }
         } else {
            if ( ! Env.wgDBname ) {
               Env.wgDBname = mw.config.get( "wgDBname" );
            }
            if ( typeof el[ Env.wgDBname ]  ===  "string" ) {
               r = el[ Env.wgDBname ];
            } else {
               if ( ! Env.wgContentLanguage ) {
                  Env.wgContentLanguage
                                  = mw.config.get( "wgContentLanguage" );
               }
               if ( typeof el[ Env.wgContentLanguage ]  ===  "string" ) {
                  r = el[ Env.wgContentLanguage ];
               }
            }
         }
      } else if ( adapt ) {
         r = "???" + access + "???";
      }
      return r;
   }   // fair()



   function favorite( apply ) {
      // Insert portlet link, if appropriate
      // Precondition:
      //    apply  -- object
      //              .signature  -- user option keyword
      //              .skins      -- default skins, if any
      //                             true; or "|vector|" etc.
      //              .fun        -- callback for link generation
      //              .forward    -- callback for click
      //              .show       -- link title keyword
      //              .support    -- tooltip keyword
      //              OPTIONAL:
      //              .suffix     -- selector creation
      //    DOM.ready
      //    mediawiki.util loaded
      //    user resources loaded
      // Postcondition:
      //    Returns DOM of portlet, or undefined
      // Uses:
      //    >< Env
      //    OPTS.facet()
      //    mw.config.get()
      //    fair()
      //    mw.util.addPortletLink()
      //    (apply.fun)
      // 2016-08-18 PerfektesChaos@de.wikipedia
      var opt   = OPTS.facet( apply.signature ),
          seed  = null,
          slot  = "p-cactions",
          start = "ca",
          got, r, re, skins, suffix, swap;
      switch ( typeof opt ) {
         case "boolean" :
            if ( opt ) {
               seed  = false;
               skins = true;
            } else {
               slot = false;
            }
            break;
         case "string" :
            seed = opt;
            re   = /^([a-z]+)-(.+)$/;
            got  = re.exec( seed );
            if ( got ) {
               switch ( got[ 1 ] ) {
                  case "p" :
                     slot = seed;
                     seed = false;
                     switch ( got[ 2 ] ) {
                        case "navigation" :
                           start = "n";
                           break;
                        case "tb" :
                           start = "t";
                           break;
                     }   // switch got.2
                     break;
                  case "ca" :
                     break;
                  case "n" :
                     slot = "p-navigation";
                     break;
                  case "t" :
                     slot = "p-tb";
                     break;
               }   // switch got.1
            }
            break;
      }   // switch typeof opt
      if ( slot  &&  seed === null ) {
         if ( ! skins ) {
            switch ( typeof apply.skins ) {
               case "boolean" :
               case "string" :
                  skins = apply.skins;
                  break;
            }   // switch typeof apply.skins
         }
         if ( skins === true ) {
            skins = "|vector|";
         }
         if ( skins ) {
            if ( ! Env.skin ) {
               Env.skin = mw.config.get( "skin" );
            }
            if ( skins.indexOf( Env.skin )  <  0 ) {
               if ( opt ) {
                  slot  = "p-tb";
                  start = "t";
               } else {
                  slot = false;
               }
            }
         } else {
            slot = false;
         }
      }
      if ( slot ) {
         if ( typeof apply.fun  ===  "function" ) {
            swap = apply.fun();
         } else {
            swap = "#";
         }
         if ( typeof apply.suffix  ===  "string" ) {
            suffix = apply.suffix;
         } else {
            suffix = apply.signature;
         }
         if ( seed ) {
            seed = "#" + seed;
         }
         r = mw.util.addPortletLink( slot,
                                     swap,
                                     fair( apply.show, true ),
                                     start + "-" + suffix,
                                     fair( apply.support, true ),
                                     null,
                                     seed );
         if ( typeof apply.forward  ===  "function" ) {
            $( r ).find( "a" ).click( apply.forward );
         }
      }
      return r;
   }   // favorite()



   function feed( about, alter, assign ) {
      // Execute URL replacement after some delay
      // Precondition:
      //    about   -- object, with reason
      //    alter   -- string, with title
      //    assign  -- string, with new URL
      // Uses:
      //    history.replaceState()
      // 2015-01-03 PerfektesChaos@de.wikipedia
      window.history.replaceState( about, alter, assign );
   }   // feed()



   function fire() {
      // Autorun on load
      // Uses:
      //    >  Signature
      //    mw.loader.getState()
      //    mw.loader.state()
      //    mw.loader.using()
      //    (OPTS.fire)
      // 2018-08-24 PerfektesChaos@de.wikipedia
      var rls;
      if ( mw.loader.getState( Signature )  !==  "ready" ) {
         rls = { };
         rls[ Signature ] = "ready";
         mw.loader.state( rls );
         mw.loader.using( [ "mediawiki.api",
                            "mediawiki.user",
                            "mediawiki.util",
                            "user",
                            "user.options" ],
                          OPTS.fire );
      }
   }   // fire()



   function first() {
      // Initialize
      // Precondition:
      //    mediawiki.user mediawiki.util loaded
      // Uses:
      //    >  Sign
      //    >  PLH
      //     < Env
      //    mw.config.get()
      //    mw.util.getParamValue()
      //    URL2i.forward()
      //    Diff.flip()
      //    mw.user.options.get()
      //    Purge.freshissimo()
      //    Hist.further()
      //    Perm.furnish()
      //    Perm.flip()
      //    Purge.fresh()
      //    Redir.from()
      //    OPTS.facet()
      //    Purge.fresher()
      //    Purge.freshest()
      //    QQmsg.flat()
      //    mw.hook()
      // 2020-01-05 PerfektesChaos@de.wikipedia
      var embed, i, linkUpdate, s;
      Env = { skin:                       false,
              wgCanonicalSpecialPageName:
                           mw.config.get( "wgCanonicalSpecialPageName" ),
              wgFormattedNamespaces:      false,
              wgNamespaceNumber:          false,
              wgPageName:                 false
          };
      if ( Env.wgCanonicalSpecialPageName ) {
         switch ( Env.wgCanonicalSpecialPageName ) {
            case "Contributions" :
               URL2i.forward( 2 );
               break;
            case "Diff" :
               Diff.flip();
               break;
            case "Mobile" :
               break;
            case "Whatlinkshere" :
               Env.wgNamespaceNumber = -1;
               if ( OPTS.facet( "forcerecursivelinkupdate" ) ) {
                  Env.wgFormattedNamespaces =
                                mw.config.get( "wgFormattedNamespaces" );
                  Env.wgRelevantPageName =
                                   mw.config.get( "wgRelevantPageName" );
                  embed = [ 10, 828 ];
                  for ( i = 0;  i < embed.length;  i++ ) {
                     s = embed[ i ] + "";
                     s = Env.wgFormattedNamespaces[ s ] + ":";
                     if ( Env.wgRelevantPageName
                             .substr( 0, s.length )  ===  s ) {
                        Purge.freshissimo();
                        break;   // for i
                     }
                  }   // for i
               }
               break;
            case "Recentchanges" :
            case "Watchlist" :
               if ( !
                    mw.user.options.get( "gadget-navigation-popups" ) ) {
                  URL2i.forward( 1 );
               }
               break;
            case "Gadgets" :
               // form();
               break;
         }   // switch wgCanonicalSpecialPageName
      } else {
         Env.diff  = mw.util.getParamValue( "diff" );
         Env.oldid = mw.util.getParamValue( "oldid" );
         if ( Env.diff ) {
            Diff.flip();
         } else {
            linkUpdate   = true;
            Env.wgAction = mw.config.get( "wgAction" );
            switch ( Env.wgAction ) {
               case "history" :
                  Hist.further();
                  break;
               case "view" :
                  Env.curid = mw.util.getParamValue( "curid" );
                  if ( Env.curid || Env.oldid ) {
                     Perm.furnish( true );
                  } else {
                     Perm.flip( true );
                  }
                  Purge.fresh( true );
                  Env.wgRedirectedFrom
                                   = mw.config.get( "wgRedirectedFrom" );
                  if ( Env.wgRedirectedFrom ) {
                     Redir.from();
                  }
                  Env.wgNamespaceNumber
                                  = mw.config.get( "wgNamespaceNumber" );
                  if ( Env.wgNamespaceNumber === 14  &&
                       OPTS.facet( "forcerecursivelinkupdate" ) ) {
                     Purge.freshissimo();
                  }
                  break;
               case "info" :
                  Purge.fresh( true );
                  break;
               case "edit" :
               case "submit" :
                  linkUpdate = false;
                  // fall through
               default:
                  Perm.flip( true );
            }   // switch wgAction
            if ( linkUpdate ) {
               if ( OPTS.facet( "forcelinkupdate" ) ) {
                  Purge.fresher( true );
               }
               if ( OPTS.facet( "forcerecursivelinkupdate" ) ) {
                  Purge.freshest( true );
               }
            }
         }
      }
      if ( OPTS.facet( "slangQQX" ) ) {
         QQmsg.flat( true );
      }
      mw.hook( Sign + ".ready" ).fire( PLH );
   }   // first()



   OPTS.facet = function ( applied ) {
      // Check for user option
      // Precondition:
      //    applied  -- option keyword
      //    module 'user' loaded
      // Postcondition:
      //    Returns value, or undefined
      // Uses:
      //    >  PLH
      //    >  OPTS.user
      // 2017-01-20 PerfektesChaos@de.wikipedia
      var r;   // = undefined
      if ( typeof PLH[ applied ]  !==  "undefined" ) {
         r = PLH[ applied ];
      }
      if ( OPTS.user   &&
           typeof OPTS.user[ applied ]  !==  "undefined" ) {
         r              = OPTS.user[ applied ];
         PLH[ applied ] = r;
      }
      return r;
   };   // OPTS.facet()



   OPTS.features = function ( apply ) {
      // Receive user options
      // Precondition:
      //    apply  -- options object
      //    module 'user' loaded
      // 2017-01-20 PerfektesChaos@de.wikipedia
      if ( typeof OPTS.user  ===  "undefined" ) {
         if ( typeof apply  ===  "object"
              &&     apply ) {
            OPTS.user = apply;
         } else {
            OPTS.user = false;
         }
      }
   };   // OPTS.features()



   OPTS.fire = function () {
      // Consider user options by mw.libs, and run
      // Precondition:
      //    user user.options loaded
      // Uses:
      //    >  Sign
      //    >  Version
      //    >  mw.libs
      //    >  Shop
      //     < PLH
      //    mw.user.options.set()
      //    mw.hook()
      //    (OPTS.features)
      //    (Purge.foreign)
      //    (first)
      // 2017-01-21 PerfektesChaos@de.wikipedia
      mw.user.options.set( "gadget-" + Sign, Version );
      if ( typeof mw.libs[ Sign ]  ===  "object"   &&
           mw.libs[ Sign ] ) {
         PLH = mw.libs[ Sign ];
      } else {
         PLH = { };
      }
      PLH.type = Sign;
      PLH.vsn  = Version;
      PLH.doc  = "[[" + Shop + Sign + "]]";
      mw.hook( Sign + ".before"  ).add( OPTS.features );
      PLH.fresh = Purge.foreign;
      $( first );   // don't hurry
   };   // OPTS.fire()



   Diff.flip = function () {
      // Equip diffpage with wikilink for c&p
      // Precondition:
      //    DOM.ready
      //    mediawiki.util loaded
      // Uses:
      //    >  Sign
      //    >  location
      //    >  history
      //    >  document
      //    >< Env
      //    OPTS.facet()
      //    $float()
      //    $factory()
      //    Perm.fiat()
      //    mw.config.get()
      //    mw.util.wikiUrlencode()
      //    setTimeout()
      //    history.replaceState()
      //    Perm.flip()
      //    (feed)
      // 2015-01-07 PerfektesChaos@de.wikipedia
      var link = false,
          msec = 500,
          opt  = OPTS.facet( "diff" ),
          sign = Sign + "Difflink",
          caused, got, i, id1, id2, j,
          re, selector, shift, show, story, swap,
          $a, $div, $span, $spanB, $spanE;
      switch ( typeof opt ) {
         case "boolean" :
            break;
         case "object" :
            if ( opt ) {
               if ( typeof opt.link  ===  "boolean" ) {
                  link = opt.link;
               }
               if ( typeof opt.msec  ===  "number" ) {
                  msec = opt.msec;
               }
               if ( typeof opt.selector  ===  "string" ) {
                  selector = opt.selector;
               }
            } else {
               opt = true;
            }
            break;
         default :
            opt = true;
      }   // switch typeof opt
      if ( opt ) {
         $div = $( "#" + sign );
         if ( ! $div.length ) {
            opt = $float( false, selector );
         }
      }
      if ( opt ) {
         if ( Env.wgCanonicalSpecialPageName ) {
            // 2014 redirected to &diff=
            re  = /\/wiki\/[^:]+:[^\/]+\/(\d+)(?:\/(\.+))?$/;
            got = re.exec( window.location.pathname );
            if ( got ) {
               id1 = got[ 1 ];
               id2 = got[ 2 ];
            }
         } else {
            id1 = Env.diff;
            id2 = Env.oldid;
         }
         if ( id1 ) {
            i = parseInt( id1, 10 );
            if ( id2 ) {
               j = parseInt( id2, 10 );
               if ( isNaN( i ) ) {
                  if ( isNaN( j ) ) {
                     id2 = false;
                  } else {
                     id2 = id1;
                     id1 = j;
                  }
               } else {
                  id1 = i;
                  if ( ! isNaN( j )  &&  i > j ) {
                     id2 = i;
                     id1 = j;
                  }
               }
               if ( id2 === "prev" ) {
                  id2 = false;
               }
            } else if ( isNaN( i ) ) {
               id1 = false;
            } else {
               id1 = i;
            }
         }
         if ( id1 ) {
            Env.spcNS = mw.config.get( "wgFormattedNamespaces" )[ "-1" ];
            if ( ! Env.wgPageName ) {
               Env.wgPageName = mw.config.get( "wgPageName" );
            }
            story = ":Diff/" + id1
                        + ( id2  ?  "/" + id2  :  "" );
            shift = "/wiki/"
                    + mw.util.wikiUrlencode( Env.spcNS ) + story
                    + "?title="
                    + mw.util.wikiUrlencode( Env.wgPageName );
            story = Env.spcNS + story;
            $span = $( "<span>" );
            if ( link ) {
               $spanB = $( "<span>" );
               $spanB.text( "[[" );
               $a = $( "<a>" );
               $a.attr( { "href": shift } );
               $a.text( story );
               $spanE = $( "<span>" );
               $spanE.text( "]]" );
               $span.append( $spanB );
               $span.append( $a );
               $span.append( $spanE );
            } else {
               $span.text( "[[" + story + "]]" );
            }
            $div = $factory( sign );
            $div.append( $span );
            $float( $div );
            if ( id2 ) {
               Perm.fiat( id1, "o" );
               Perm.fiat( id2, "n" );
            } else {
               Perm.fiat( false, "o" );
               Perm.fiat( false, "n" );
            }
            if ( ! link   &&
                 typeof window.history.replaceState  ===  "function" ) {
               caused = { caused: Sign };
               show   = window.document.title;
               if ( msec > 0 ) {
                  if ( id2 ) {
                     swap = shift + "&diff=" + id2 + "&oldid=" + id1;
                  } else {
                     swap = shift + "&diff=" + id1;
                  }
                  window.history.replaceState( caused, show, swap );
                  window.setTimeout( feed, msec, caused, show, shift );
               } else {
                  window.history.replaceState( caused, show, shift );
               }
            }
         }
      }
      Perm.flip( true );
   };   // Diff.flip()



   Hist.further = function () {
      // Deal with history page
      // Precondition:
      //     loaded user.options mediawiki.user
      // Uses:
      //    OPTS.facet()
      //    mw.user.options.get()
      // 2015-01-03 PerfektesChaos@de.wikipedia
      var n   = 3,
          opt = OPTS.facet( "history" ),
          scan;
      switch ( typeof opt ) {
         case "boolean" :
            if ( opt === false ) {
               n = 0;
            }
            break;
         case "string" :
            if ( opt === "diff" ) {
               scan = "diff";
            }
            break;
      }   // switch opt
      if ( n ) {
         if ( scan === "diff" ) {
            n = 2;
         }
         if ( mw.user.options.get( "gadget-navigation-popups" ) ) {
            n = 0;
         }
         if ( n >= 1 ) {
            URL2i.forward( n, scan );
         }
      }
   };   // Hist.further()



   Perm.fiat = function ( apply, at ) {
      // Equip diffpage revision with wikilink for permalink c&p
      // Precondition:
      //    apply  -- revision ID, or some keyword
      //    at     -- revision column; "n" or "o"
      //    DOM.ready
      //    Diffpage
      // Uses:
      //    >  Sign
      //    >  Env.spcNS
      //    mw.config.get()
      //    mw.util.getParamValue()
      // 2014-12-16 PerfektesChaos@de.wikipedia
      var $div  = $( "#mw-diff-" + at + "title1" ),
          id, s, $a, $span;
      if ( $div.length ) {
         if ( apply ) {
            id = apply;
            if ( apply === "cur" ) {
               id = mw.config.get( "wgCurRevisionId" );
            } else if ( typeof apply  === "string"   &&
                        ! /^\d+$/.test( apply ) ) {   // "next"
               id = false;
            }
         }
         if ( ! id ) {
            $a = $div.find( "a" ).eq( 0 );
            s  = $a.attr( "href" );
            id = mw.util.getParamValue( "oldid", s );
         }
         if ( id ) {
            s     = "[[" + Env.spcNS + ":PermanentLink/" + id + "]]";
            $span = $( "<span>" );
            $span.attr( { "class":  Sign + " " + Sign + "DiffRev" } );
            $span.css( { "font-family": "monospace",
                         "margin-left": "1em" } );
            $span.text( s );
            $div.append( $span );
         }
      }
   };   // Perm.fiat()



   Perm.flip = function ( assign ) {
      // Change portlet for permalink
      // Precondition:
      //    assign  -- true if link to be diverted, false to gray out
      //    DOM.ready
      // Uses:
      //    >  Sign
      //    >  Version
      //    fair()
      //    (Perm.furnish)
      // 2014-12-19 PerfektesChaos@de.wikipedia
      var $portlet = $( "#t-permalink" ),
          show, support, $a, $span;
      if ( $portlet.length ) {
         support = Sign + " " + Version;
         if ( assign ) {
            $a = $( "<a>" );
            $a.attr( { "title": support } );
            $a.click( Perm.furnish );
            $a.text( fair( "permTitle", false, "PermaLink" ) );
            $portlet.empty();
            $portlet.append( $a );
         } else {
            $a   = $portlet.find( "a" );
            show = $a.text();
            if ( show ) {
               $span = $( "<span>" );
               $span.css( { "color": "#808080" } );
               $span.text( show );
               $span.attr( { "title": support } );
               $portlet.empty();
               $portlet.append( $span );
            }
         }
      }
   };   // Perm.flip()



   Perm.furnish = function () {
      // Equip page with wikilink to perma for c&p
      // Precondition:
      //    DOM.ready
      //    mediawiki.util loaded
      // Uses:
      //    >  Sign
      //    >< Env
      //    mw.config.get()
      //    $float()
      //    $factory()
      //    mw.util.wikiUrlencode()
      //    mw.util.getUrl()
      //    fair()
      //    Perm.flip()
      // 2016-08-30 PerfektesChaos@de.wikipedia
      var sign = Sign + "Permalink",
          $div = $( "#" + sign ),
          show, special, swift, $a, $br, $spanBeg, $spanEnd;
      if ( ! $div.length  &&  $float() ) {
         if ( ! Env.wgPageName ) {
            Env.wgPageName = mw.config.get( "wgPageName" );
         }
         if ( Env.curid ) {
            Env.curid = parseInt( Env.curid, 10 );
         } else {
            Env.curid = mw.config.get( "wgArticleId" );
         }
         if ( Env.oldid ) {
            Env.oldid = parseInt( Env.oldid, 10 );
         } else {
            Env.oldid = mw.config.get( "wgCurRevisionId" );
         }
         $div = $factory( sign );
         if ( ! Env.wgFormattedNamespaces ) {
            Env.wgFormattedNamespaces
                              = mw.config.get( "wgFormattedNamespaces" );
         }
         special  = Env.wgFormattedNamespaces[ "-1" ]  +  ":";
         swift    = "?title="
                    + mw.util.wikiUrlencode( Env.wgPageName );
         $spanBeg = $( "<span>" );
         $spanBeg.text( "[[" );
         $spanEnd = $( "<span>" );
         $spanEnd.text( "]]" );
         $a = $( "<a>" );
         if ( Env.oldid ) {
            $div.append( $spanBeg.clone() );
            show = special
                   + fair( "PermanentLink", false, "PermaLink" )
                   + "/" + Env.oldid;
            $a.attr( { "href":  mw.util.getUrl( show ) + swift } );
            $a.text( show );
            $div.append( $a );
            $div.append( $spanEnd.clone() );
            $br = $( "<br />" );
            $div.append( $br );
            $a = $( "<a>" );
         }
         $div.append( $spanBeg );
         show = special
                + fair( "Redirect", false )
                + "/page/" + Env.curid;
         $a.attr( { "href":  mw.util.getUrl( show ) + swift } );
         $a.text( show );
         $div.append( $a );
         $div.append( $spanEnd );
         $float( $div );
         Perm.flip( false );
      }
   };   // Perm.furnish()



   Purge.fail = function ( arrived, add ) {
      // API purging failed
      // Precondition:
      //    arrived  -- string with basic message
      //    add      -- object with additional information
      // Uses:
      //    window.console
      // 2016-08-30 PerfektesChaos@de.wikipedia
      if ( typeof window.console  ===  "object"   &&
           typeof arrived  ===  "string" ) {
         if ( typeof window.console.log  ===  "function" ) {
            window.console.log( arrived );
         }
         if ( typeof add  ===  "object"   &&   add   &&
              typeof window.console.dir  ===  "function" ) {
            window.console.dir( add );
         }
      }
   };   // Purge.fail()



   Purge.feed = function () {
      // Launch API generator query
      // Uses:
      //    >  Purge.Api
      //    >  Purge.request
      //    (Purge.further)
      //    (Purge.fail)
      // 2016-08-30 PerfektesChaos@de.wikipedia
      Purge.Api.post( Purge.request )
               .done( Purge.further )
               .fail( Purge.fail );
   };   // Purge.feed()



   Purge.fiat = function ( additional, already ) {
      // Start API purging of a single page
      // Precondition:
      //    additional  -- some keyword, or not
      //    already     -- request parameters, or not
      // Uses:
      //    mw.Api()   .post()
      // 2017-01-20 PerfektesChaos@de.wikipedia
      var o = new mw.Api( { parameters: { action: "purge" } } ),
          p;
      if ( already ) {
         p = already;
      } else {
         p = { pageids: Env.curid };
      }
      if ( additional ) {
         p[ additional ] = true;
      }
      o.post( p )
       .done( function () { window.location.reload( true ); } );
   };   // Purge.fiat()



   Purge.first = function () {
      // Ensure curid=pageid and purgeable revision
      // Postcondition:
      //    Returns curid=pageid, or undefined
      // Uses:
      //    >< Env.curid
      //    mw.config.get()
      // 2016-08-18 PerfektesChaos@de.wikipedia
      if ( typeof Env.curid  !==  "number" ) {
         Env.curid = mw.config.get( "wgArticleId" );
      }
      return Env.curid;
   };   // Purge.first()



   Purge.foreign = function ( additional, array, allow ) {
      // External access to purge function
      // Precondition:
      //    additional  -- string, number, or false
      //    array       -- Attay of page titles, or page IDs, or not
      //    allow       -- number of permitted array elements, or not
      // Uses:
      //    Purge.fiat()
      //    Purge.full()
      // 2017-01-21 PerfektesChaos@de.wikipedia
      var many = 50,
          e, i, m, p, q, s, stage;
      switch ( additional ) {
         case 1 :
            stage = "forcelinkupdate";
            break;
         case 2 :
            stage = "forcerecursivelinkupdate";
            break;
         case "forcelinkupdate" :
         case "forcerecursivelinkupdate" :
            stage = additional;
            break;
         default:
            stage = "";
      }   // switch additional
      if ( typeof allow  ===  "number"
           &&     allow > many ) {
         many = allow;
      }
      if ( typeof array  ===  "object"
           &&     array   &&
           typeof array.length  ===  "number"
           &&     array.length ) {
         e = array[ 0 ];
         s = typeof e;
         switch ( s ) {
            case "number" :
               p = { pageids: e };
               q = p.pageids;
               break;
            case "" :
               p = { titles: e };
               q = p.titles;
               break;
         }   // typeof array[ 0 ]
         if ( p ) {
            m = many - 1;
            for ( i = 1;  i < array.length;  i++ ) {
               if ( m ) {
                  e = array[ i ];
                  if ( typeof e  ===  s ) {
                     if ( s === "number"  &&  e < 0 ) {
                        e = 0;
                     }
                     if ( e ) {
                        q = q + "|" + e;
                        m--;
                     }
                  }
               } else {
                  break;   // for i
               }
            }   // for i
         }
      }
      if ( ! p ) {
         switch ( Env.wgNamespaceNumber ) {
            case -1 :
               switch ( Env.wgCanonicalSpecialPageName ) {
                  case "Whatlinkshere" :
                     p = false;
                     break;
               }   // switch wgCanonicalSpecialPageName
               break;
            case 14 :
               p = false;
               break;
         }   // switch wgNamespaceNumber
      }
      if ( p === false ) {
         Purge.full( stage );
      } else {
         Purge.fiat( stage, p );
      }
   };   // Purge.foreign()



   Purge.fresh = function ( ahead ) {
      // Provide purge link
      // Precondition:
      //    ahead  -- true if initializing, false to link
      //    DOM.ready
      //    user resources loaded
      // Uses:
      //    Purge.first()
      //    favorite()
      //    Purge.fiat()
      //    (Purge.fresh)
      // 2016-08-18 PerfektesChaos@de.wikipedia
      if ( ahead === true ) {
         if ( Purge.first() ) {
            favorite( { signature: "purge",
                        skins:     true,
                        forward:   Purge.fresh,
                        show:      "purgeTitle",
                        support:   "purgeTip",
                        suffix:    "purge"
                      } );
         }
      } else {
         Purge.fiat();
      }
   };   // Purge.fresh()



   Purge.fresher = function ( ahead ) {
      // Provide forcelinkupdate purge link
      // Precondition:
      //    ahead  -- true if initializing, false to link
      //    DOM.ready
      //    user resources loaded
      // Postcondition:
      //    Returns link, or undefined
      // Uses:
      //    Purge.first()
      //    favorite()
      //    Purge.fiat()
      //    (Purge.fresher)
      // 2016-08-18 PerfektesChaos@de.wikipedia
      if ( ahead === true ) {
         if ( Purge.first() ) {
            favorite( { signature: "forcelinkupdate",
                        skins:     false,
                        forward:   Purge.fresher,
                        show:      "purgeLUtitle",
                        support:   "purgeLUtip",
                        suffix:    "purgeLinkUpd"
                      } );
         }
      } else {
         Purge.fiat( "forcelinkupdate" );
      }
   };   // Purge.fresher()



   Purge.freshest = function ( ahead ) {
      // Provide forcerecursivelinkupdate purge link
      // Precondition:
      //    ahead  -- true if initializing, false to link
      //    DOM.ready
      //    user resources loaded
      // Uses:
      //    Purge.first()
      //    favorite()
      //    Purge.fiat()
      //    (Purge.fresher)
      // 2016-08-18 PerfektesChaos@de.wikipedia
      if ( ahead === true ) {
         if ( Purge.first() ) {
            favorite( { signature: "forcerecursivelinkupdate",
                        skins:     false,
                        forward:   Purge.freshest,
                        show:      "purgeLURtitle",
                        support:   "purgeLURtip",
                        suffix:    "purgeLinkUpdRec"
                      } );
         }
      } else {
         Purge.fiat( "forcerecursivelinkupdate" );
      }
   };   // Purge.freshest()



   Purge.freshissimo = function () {
      // Provide generator forcerecursivelinkupdate purge link
      // Precondition:
      //    DOM.ready
      //    user resources loaded
      // Uses:
      //     < Purge.portlet
      //    favorite()
      //    (Purge.full)
      // 2016-08-30 PerfektesChaos@de.wikipedia
      Purge.portlet = favorite( { signature: "forcerecursivelinkupdate",
                                  skins:     false,
                                  forward:   Purge.full,
                                  show:      "purgeLURGtitle",
                                  support:   "purgeLURGtip",
                                  suffix:    "purgeGenLinkUpdRec"
                                } );
   };   // Purge.freshissimo()



   Purge.full = function ( additional ) {
      // Start API purging of a generator
      // Precondition:
      //    additional  -- some keyword, or not
      // Uses:
      //    >  Env
      //     < Purge.Api
      //     < Purge.request
      //    mw.Api()   .post()
      //    Purge.feed()
      // 2017-01-21 PerfektesChaos@de.wikipedia
      var many = 20,
          p;
      switch ( Env.wgNamespaceNumber ) {
         case -1 :
            switch ( Env.wgCanonicalSpecialPageName ) {
               case "Whatlinkshere" :
                  p = { generator: "embeddedin",
                        geititle:  Env.wgRelevantPageName,
                        geilimit:  many };
                  break;
            }   // switch wgCanonicalSpecialPageName
            break;
         case 14 :
            p = { generator: "categorymembers",
                  gcmpageid: Env.curid,
                  gcmtype:   "page|file",
                  gcmlimit:  many };
            break;
      }   // switch wgNamespaceNumber
      if ( p ) {
         if ( typeof additional  ===  "string" ) {
            if ( additional ) {
               p[ additional ] = true;
            }
         } else {
           p.forcerecursivelinkupdate = true;
         }
         Purge.Api = new mw.Api( { parameters: { action: "purge" } } );
         Purge.request = p;
         Purge.feed();
      }
   };   // Purge.full()



   Purge.further = function ( arrived ) {
      // Start API purging of a generator
      // Precondition:
      //    arrived  -- object with answers
      // Uses:
      //    >< Purge.portlet
      //    >< Purge.request
      //    (Purge.feed)
      // 2016-08-30 PerfektesChaos@de.wikipedia
      var msec = 61000;
      if ( typeof Purge.portlet  ===  "object" ) {
         $( Purge.portlet ).remove();
         Purge.portlet = false;
      }
      if ( typeof arrived[ "continue" ]  ===  "object"   &&
           typeof arrived.warnings  !==  "object" ) {
         $.extend( Purge.request, arrived[ "continue" ] );
         window.setTimeout( Purge.feed, msec );
      }
   };   // Purge.further()



   QQmsg.flat = function ( ahead ) {
      // Provide "uselang=qqx" link
      // Precondition:
      //    ahead  -- true if initializing, false to link
      //    DOM.ready
      //    mediawiki.util loaded
      //    user resources loaded
      // Postcondition:
      //    Returns link, or undefined
      // Uses:
      //    >  location
      //    favorite()
      //    (QQmsg.flat)
      // 2014-12-19 PerfektesChaos@de.wikipedia
      var r;
      if ( ahead ) {
         favorite( { signature: "slangQQX",
                     skins:     false,
                     fun:       QQmsg.flat,
                     show:      "qqxTitle",
                     support:   "qqxTip",
                     suffix:    "langQQX"
                   } );
      } else {
         r = window.location.href;
         r = r.replace( /^(.+)#.+$/, "$1" )
              .replace( /([?&])uselang=[^&]*(&.*)?$/, "$1$2" );
         if ( ! /[?&]$/.test( r ) ) {
            if ( r.indexOf( "?" )  >  0 ) {
               r = r + "&";
            } else {
               r = r + "?";
            }
         }
         r = r + "uselang=qqx";
      }
      return r;
   };   // QQmsg.flat()



   Redir.from = function () {
      // Redirected from somewhere
      // Precondition:
      //    mediawiki.util loaded
      // Uses:
      //    >  history
      //    >  location
      //    >  document
      //    >  Sign
      //    >< Env
      //    mw.util.getUrl()
      //    OPTS.facet()
      //    history.pushState()
      //    history.replaceState()
      // 2014-12-07 PerfektesChaos@de.wikipedia
      var caused, opt, last, legacy, show, showed, state, swap;
      if ( typeof window.history.pushState  ===  "function"  &&
           typeof window.history.replaceState  ===  "function" ) {
         swap = mw.util.getUrl( Env.wgRedirectedFrom,
                                { redirect: "no" } );
         if ( window.history.previous ) {
            // should actually not cause wgRedirectedFrom, but avoid
            last = ( window.history.previous.indexOf( swap )  >=  0 );
         }
         if ( ! last ) {
            opt = OPTS.facet( "redirect" );
            if ( opt   &&
                 typeof opt  ===  "object"   &&
                 typeof opt.legacy  ===  "boolean" ) {
               legacy = opt.legacy;
            }
            showed = window.document.title;
            caused = { caused: Sign };
            if ( legacy ) {
               state = mw.util.getUrl( Env.wgRedirectedFrom );
            } else {
               state = window.location.href;
            }
            show = "#REDIRECT " + Env.wgRedirectedFrom;
            window.history.replaceState( caused, show, swap );
            window.history.pushState( caused, showed, state );
         }
      }
   };   // Redir.from()



   URL2i.forward = function ( amount, alike ) {
      // Convert links on page into Special: format
      // Precondition:
      //    amount  -- number of links per item to inspect
      //    alike   -- if string "diff", required to occur
      //    DOM.ready
      //    mediawiki.util loaded
      // Uses:
      //    mw.util.getParamValue()
      // 2015-01-11 PerfektesChaos@de.wikipedia
      var $results = $( "#mw-content-text li" ),
          n        = $results.length,
          i, j, shift, special, start, strip, $a, $li;
      if ( n ) {
         special  = "/wiki/"
                    + mw.config.get( "wgFormattedNamespaces" )[ "-1" ]
                    + ":";
         for ( i = 0;  i < n;  i++ ) {
            $li = $results.eq( i ).find( "a" );
            for ( j = 0;  j < amount;  j++ ) {
               $a    = $li.eq( j );
               strip = $a.attr( "href" );
               if ( strip  &&
                    strip.substr( 0, 3 )  ===  "/w/" ) {   // wikidata
                  start = mw.util.getParamValue( "oldid", strip );
                  if ( start ) {
                     strip = strip.replace( /&oldid=\d+\b/, "" )
                                  .replace( /&curid=\d+\b/, "" );
                     shift = mw.util.getParamValue( "diff", strip );
                     if ( shift ) {
                        strip = strip.replace( /&diff=\d+\b/, "" );
                        shift = "Diff/" + start + "/" + shift;
                     } else if ( alike ) {
                        shift = false;
                     } else {
                        shift = "PermaLink/" + start;
                     }
                     if ( shift ) {
                        shift = special + shift + "$1";
                        strip = strip.replace( /^.+(\?title=.+)$/,
                                               shift );
                        $a.attr( "href", strip );
                     }
                  }
               }
            }   // for j
         }   // for i
      }
   };   // URL2i.forward()



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

/// EOF </nowiki>   pageLinkHelper/d.js