User:PerfektesChaos/js/externalLinkProblem/gui/d.js

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.
/// User:PerfektesChaos/js/externalLinkProblem/gui/d.js
/// 2019-01-23 PerfektesChaos@de.wikipedia
// Sub-module: Build and maintain GUI
// ResourceLoader:  compatible;
//                  dependencies: mediawiki.util,
//                                oojs, oojs-ui-core, oojs-ui-widgets
/// Fingerprint: #0#0#
/// @license GPL [//www.mediawiki.org/w/COPYING] (+GFDL, LGPL, CC-BY-SA)
/// <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           */



( function ( mw, $ ) {
   "use strict";
   var Version   =  -2.9,
       ELP       =  "externalLinkProblem",
       Sub       =  "gui",
       Supersed  =  "defekterWeblinkBotVorlage",
       Box       =  { },
       Content   =  { },
       Pop       =  { $body: false },
       OO;
   if ( typeof mw.libs[ ELP ]  !==  "object"   ||   ! mw.libs[ ELP ] ) {
      mw.libs[ ELP ]  =  { };
   }
   mw.libs[ ELP ].type  =  ELP;
   ELP                  =  mw.libs[ ELP ];



   /*
    * This program is free software; you can redistribute it and/or
    * modify it under the terms of the GNU General Public License as
    * published by the Free Software Foundation; either version 2 of the
    * License, or (at your option) any later version.
    *
    * This program is distributed in the hope that it will be useful,
    * but WITHOUT ANY WARRANTY; without even the implied warranty of
    * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    * GNU General Public License for more details.
    *
    * You should have received a copy of the GNU General Public License
    * along with this program;
    * if not, write to the Free Software Foundation, Inc.,
    * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
    * http://www.gnu.org/copyleft/gpl.html
    */



   if ( typeof ELP[ Sub ]  !==  "object" ) {
      ELP[ Sub ]  =  { };
   }
   ELP[ Sub ].vsn  =  Version;
   if ( typeof ELP.config  !==  "object" ) {
      ELP.config  =  { };
   }
   if ( typeof ELP.config.css  !==  "object" ) {
      ELP.config.css       =  { };
      ELP.config.css.box   =  { "border":        "#808080 2px solid",
                                "clear":         "both",
                                "margin-top":    "1em",
                                "margin-bottom": "1em",
                                "padding":       "0.8em",
                                "padding-right": "0" };
      ELP.config.css.href  =  { "background-color": "#FFD0D0",
                                "border":           "#FF0080 2px solid",
                                "padding-bottom":   "2px",
                                "padding-left":     "4px",
                                "padding-right":    "16px",
                                "padding-top":      "2px" };
      ELP.config.css.ref   =  { "color": "#FF00FF" };



      ELP.config.css.factory  =  function () {
         // Merge styles with user styles
         // Uses:
         //    >  .css
         //    >  .config.css
         // 2014-02-13 PerfektesChaos@de.wikipedia
         var o, s, u, v;
         if ( typeof ELP.css  ===  "object"   &&
              typeof ELP.css ) {
            for ( s in ELP.config.css ) {
               if ( typeof ELP.config.css[ s ]  ===  "object"   &&
                    typeof ELP.css[ s ]  ===  "object"   &&
                    typeof ELP.css[ s ] ) {
                  o  =  ELP.config.css[ s ];
                  u  =  ELP.css[ s ];
                  for ( s in u ) {
                     v  =  u[ s ];
                     switch ( typeof v ) {
                        case "string":
                           o[ s ]  =  v;
                           break;
                        case "boolean":
                           if ( ! v   &&
                                typeof o[ s ]  ===  "string" ) {
                              delete o[ s ];
                           }
                           break;
                     }   // switch typeof v
                  }   // for s in u
               }
            }   // for o in ELP.config.css
         }
      };   // .config.css.factory()
   }   // .config.css
   if ( typeof ELP.lang  !==  "object" ) {
      ELP.lang  =  { slang: "en" };
   }
   if ( typeof ELP[ Sub ].texts  !==  "object" ) {
      ELP[ Sub ].texts  =  {
         // 2016-04-18 PerfektesChaos@de.wikipedia
         "blocked": {"en": "Blocked.",
                     "de": "Blockiert."},
         "blocking":{"en": "(malware, spam [blacklist], mailto:)",
                     "de": "(Malware, Spam [blacklist], mailto:)"},
         "bracket": {"en": "Opening bracket '[' in URL,"
                           + " might be unescaped ']'",
                     "de": "Öffnende Klammer '[' in der URL,"
                           + " womöglich ']' nicht kodiert"},
         "curl":    {"en": "CURL: network problem",
                     "de": "CURL: Netzwerkproblem"},
         "current": {"en": "current",
                     "de": "aktuell"},
         "discard": {"en": "Perhaps already resolved",
                     "de": "Möglicherweise bereits nicht mehr wirksam"},
         "editSrc": {"en": "edit section source",
                     "de": "Abschnittsquelltext bearbeiten"},
         "empty":   {"en": "(empty)",
                     "de": "(leer)"},
         "help":    {"en": "details",
                     "de": "Details"},
         "hint":    {"en": "Show external links",
                     "de": "Zeige Links"},
         "hintTalk":{"en": "Show external links with assumed problem"
                           + " on talk page"
                           + " (will open in new window)",
                     "de": "Zeige Links mit vermutetem Problem"
                           + " auf der Diskussionsseite"
                           + " (wird in neuem Fenster geöffnet)"},
         "mode":    {"en": "HTTP status",
                     "de": "HTTP-Status"},
         "mode3":   {"en": "Page has been moved",
                     "de": "Seite wurde verschoben"},
         "mode5":   {"en": "Entire domain or server unavailable",
                     "de": "Ganze Domain / Server unerreichbar"},
         "more":    {"en": "More pages",
                     "de": "Weitere Seiten"},
         "notdone": {"en": "Error: marked as done",
                     "de": "Fehler: als erledigt markiert"},
         "other":   {"en": "Other page with same URL:",
                     "de": "Andere Seite mit gleicher URL:"},
         "others":  {"en": "Other pages with same URL"
                           + " (research might be used here):",
                     "de": "Andere Seiten mit gleicher URL"
                           + " (Erfolg könnte wiederverwendet werden):"},
         "pipe":    {"en": "Pipe '|' within URL,"
                           + " might be wikilink syntax",
                     "de": "Pipe '|' in der URL,"
                           + " womöglich Wikilink-Syntax"},
         "earlier": {"en": "Section from earlier bot run detected",
                     "de": "Abschnitt aus früherem Botlauf gefunden"},
         "since":   {"en": "Already unavailable:",
                     "de": "Bereits unerreichbar:"},
         "special": {"en": "Special character at URL end,"
                           + " might have caused problem",
                     "de": "Sonderzeichen am Ende der URL,"
                           + " womöglich Ursache"},
         "sub":     {"en": "Truncated URL appropriate?",
                     "de": "Gekürzte URL inhaltlich richtig?"},
         "success": {"en": "success",
                     "de": "erfolgreich"},
         "syntax":  {"en": "Syntax error",
                     "de": "Syntaxfehler"},
         "try":     {"en": "try",
                     "de": "Ausprobieren"},
         "wayback": {"en": "A version in"
                           + " wayback.archive.org "
                           + " might be appropriate:",
                     "de": "Vielleicht ist eine Version auf"
                           + " wayback.archive.org "
                           + " geeignet:"},
         "wayback*":{"en": "all",
                     "de": "alle"},
         "wayback!":{"en": "most recent",
                     "de": "jüngste"},
         "webcite": {"en": "A version in"
                           + " webcitation.org "
                           + " might be appropriate:",
                     "de": "Vielleicht ist eine Version auf"
                           + " webcitation.org "
                            + " geeignet:"},
         "wikilink":{"en": "Internal link"
                           + " should be rewritten as [[...]]",
                     "de": "Wikilink"
                           + " sollte als [[...]] geschrieben werden"}
      };   // .gui.texts
   }



   // .problems
   //    .live      true: effective on subject page
   //    .url       mandatory; string
   //    .mode      numerical;  HTTP other than 404
   //    .since     string; recent years
   //    .less      suitable without trailing
   //    .link      protocol secure
   //    .listed    Spam etc.
   //    .wba       true / id
   //    .cit       id
   //    .sub       string; suitable partial URL
   //    .other     Array[ numbers ]
   //    .learnt    true: should be resolved



   function facilitated() {
      // Mark sub-module as ready
      // Uses:
      //    >  .signature
      //    >  Sub
      //    >  .type
      //    mw.loader.getState()
      //    mw.loader.state()
      //    mw.hook()
      // 2018-08-24 PerfektesChaos@de.wikipedia
      var rls, signature, sub;
      if ( typeof ELP.signature  ===  "string" ) {
         sub        =  "/" + Sub;
         signature  =  ELP.signature + sub;
         if ( mw.loader.getState( signature )  !==  "ready" ) {
            rls = { };
            rls[ signature ] = "ready";
            mw.loader.state( rls );
            mw.hook( ELP.type + sub + ".ready" ).fire();
         }
      }
   }   // facilitated()



   function fair( access ) {
      // Normalize URL
      // Precondition:
      //    access  -- URL
      // Postcondition:
      //    Returns URL in standard formatting
      // 2016-04-18 PerfektesChaos@de.wikipedia
      var r  =  access.replace( /#.*$/, "" ),
          k  =  r.indexOf( "\\" );
      if ( k >= 0 ) {
         if ( k > 6 ) {
            r  =  r.replace( /\\/g, "%5C" );
         } else {
            k  =  r.indexOf( "\\\\", k );
            if ( k >= 0  &&  k <= 6 ) {
               r  =  r.substr( 0, k )  +  "//"  +  r.substr( k + 2 );
            }
         }
      }
      r  =  r.replace( /{/g, "%7B" )
             .replace( /}/g, "%7D" )
             .replace( /%28/g, "(" )
             .replace( /%29/g, ")" );
      k  =  r.indexOf( "//" );
      if ( k >= 0  &&  k <= 6 ) {
         if ( k ) {
            r  =  r.substr( 0, k ).toLowerCase()  +  r.substr( k );
         } else {
            r  =  "https:" + r;
         }
         if ( r.indexOf( "/", 11 )  <  0 ) {
            r  =  r + "/";
         }
      }
      return r;
   }   // fair()



   function far( access ) {
      // Percent-encode 8-bit characters in path
      // Precondition:
      //    access  -- URL
      // Postcondition:
      //    Returns URL in standard formatting
      // 2015-11-27 PerfektesChaos@de.wikipedia
      var r  =  access,
          i  =  r.indexOf( "//" ),
          k, start, strip;
      if ( i >= 0  &&  i <= 6 ) {
         i  =  r.indexOf( "/",  i + 6 );
         if ( i > 0 ) {
            start  =  r.substr( 0, i );
            strip  =  r.substr( i + 1 );
            try {
               start  =  decodeURI( start );
            } catch (e) {
            }
            if ( strip ) {
               for ( i = strip.length - 1;  i >= 0;  i-- ) {
                  k  =  strip.charCodeAt(i);
                  if ( k > 127 ) {
                     strip  =  strip.substr( 0, i )
                               + String.fromCharCode(k)
                               + strip.substr( i + 1 );
                  }
               }   // for i--
            }
            r  =  start + "/" + strip;
         }
      }
      return r;
   }   // far()



   function furnish() {
      // Equip page with box
      // Uses:
      //    >  .gui.$wrapper
      //    >  .gui.$content
      //     < OO
      // Box.factory()
      // .gui.fresh()
      // 2016-09-18 PerfektesChaos@de.wikipedia
      $( ".cn-fundraiser-banner, #fundraising" ).remove();
      OO  =  window.OO;
      Box.factory();
      ELP.gui.$content.prepend( ELP.gui.$wrapper );
      ELP.gui.fresh();
   }   // furnish()



   Box.factory  =  function () {
      // Create box
      // Uses:
      //    >  .type
      //    >  .config.css.box
      //    >  .prego
      //    >  .prego.supply
      //    >  Supersed
      //    >  .support
      //    >  .project.say
      //     < .prego.lib
      //     < .gui.$wrapper
      //     < .gui.$buttons
      //    .config.css.factory()
      //    mw.util.getUrl()
      //    .lang.find()
      //    .talk.furnish()
      // 2019-01-23 PerfektesChaos@de.wikipedia
      var s, $bl, $el;
      ELP.config.css.factory();
      ELP.gui.$wrapper  =  $( "<div>" );
      ELP.gui.$wrapper.addClass( "noprint" )
                      .attr( "id", ELP.type )
                      .css( ELP.config.css.box );
      ELP.gui.$buttons  =  $( "<div>" );
      ELP.gui.$buttons.css( { "float":          "right",
                              "vertical-align": "top" } );
      if ( typeof ELP.prego  ===  "object" ) {
         if ( typeof ELP.prego.supply  ===  "string" ) {
            ELP.prego.lib  =  mw.libs[ ELP.prego.supply ];
         }
         if ( typeof ELP.prego.lib  ===  "object"   &&
              typeof ELP.prego.lib.$button  ===  "function" ) {
            $el  =  ELP.prego.lib.$button( ELP.type );
            $el.css( { "float":          "right",
                       "vertical-align": "top" } );
            ELP.gui.$buttons.append( $el );
         }
      }
      ELP.gui.$wrapper.append( ELP.gui.$buttons );
      if ( typeof ELP[ Supersed ]  ===  "boolean"  &&  false ) {
         s  =  "Du verwendest noch das Skript '" + Supersed + "'.<br />"
               + " Der Nachfolger ist"
               + " <a href='"
               + mw.util.getUrl( ELP.support + "#" + Supersed )
               + "' target='_blank'>" + ELP.type + "</a>.<br />"
               + "Bitte ändere den Eintrag ";
         if ( typeof ELP.fliegelflagel  ===  "boolean"
              &&     ELP.fliegelflagel ) {
            s  =  s + "auf"
                    + " <a href='"
                    + mw.util.getUrl( "Spezial:Fliegelflagel" )
                    + "' target='_blank'>Fliegelflagel</a>.";
         } else {
            s  =  s + "in deiner"
                    + " <a href='"
                    + mw.util.getUrl( "Special:MyPage/common.js" )
                    + "' target='_blank'>common.js</a>"
                    + " usw.";
         }
         $bl  =  $( "<div>" );
         $bl.css( { "color":         "#FF00FF",
                    "font-weight":   "bold",
                    "margin-bottom": "1em",
                    "width":         "70%" } );
         $el  =  $( "<span>" + s + "</span>" );
         $bl.append( $el );
         ELP.gui.$wrapper.append( $bl );
      }
      if ( typeof ELP.talk  ===  "object" ) {
         $el  =  ELP.talk.furnish( ELP.lang.find( "hintTalk" ) );
         $el.attr( { "target": "ExtLPtalk" } );
      } else {   // tools
         $el  =  $( "<span>" );
      }
      $el.css( { "font-size":   "1.8em",
                 "white-space": "nowrap" } )
         .text( ELP.project.say );
      $bl  =  $( "<div>" );
      $bl.css( { "width": "70%" } )
         .append( $el );
      ELP.gui.$wrapper.append( $bl );
   };   // Box.factory()



   Box.fiat  =  function ( access ) {
      // Create gui element
      // Precondition:
      //    access  -- string with code
      //               "throbber"
      //               "spacer"
      //               "show"  -- [+]
      //               "hide"  -- [X]
      // Postcondition:
      //    Returns jQuery element
      // Uses:
      //    >  .type
      //    >< Box.$throbber
      //    >< Box.$hide
      //    >< Box.$show
      //    (Box.flip)
      // 2015-10-22 PerfektesChaos@de.wikipedia
      var s   =  "$" + access,
          $r  =  null,
          $el;
      if ( typeof this[ s ]  ===  "object" ) {
         $r  =  this[ s ];
      } else {
         if ( access === "throbber" ) {
            $r  =  $( "<img />" );
            $r.attr( "src",
                     "//upload.wikimedia.org/"
                     + "wikipedia/commons"
                     + "/d/de/Ajax-loader.gif" )
              .attr( "id",  ELP.type + "-clock" )
              .css( { "height": "20px" } );
         } else if ( access === "spacer" ) {
            $r  =  $( "<span>" );
            $r.text( String.fromCharCode( 160, 32, 160 ) );
         } else {
            $r  =  $( "<button />" );
            $r.attr( { "id":   ELP.type + "-" + access,
                       "type": "button" } )
              .css( { "border-color":     "#E0E0E0"
                                       + " #E0E0E0"
                                       + " #707070"
                                       + " #707070",
                      "float": "left" } );
            $el  =  $( "<span>" );
            $el.css( { "font-weight":  "bolder" } );
            if ( access === "show" ) {
               $el.css( { "color":       "#00A000",
                          "font-size":   "120%",
                          "font-weight": "bold",
                          "padding":     "2px" } )
                  .text( "+" );
            } else {
               $el.css( { "color":   "#FF0000",
                          "padding": "2px" } )
                  .text( "X" );
            }
            $r.append( $el );
            $r.click( this.flip );
         }
         this[ s ]  =  $r;
      }
      return $r;
   };   // Box.fiat()



   Box.fill  =  function () {
      // Create/update list of URL
      // Uses:
      //    >  Box.$ol
      //    >  Pop.complain
      //    >  .scope
      //    >  .type
      //    >  .gui.$wrapper
      //    >< Pop.$ol
      //    >< Pop.$list
      // 2014-01-20 PerfektesChaos@de.wikipedia
      var listed  =  ( typeof Box.$ol  ===  "object" ),
          i, n;
      if ( listed ) {
         Pop.$ol.empty();
      }
      if ( typeof Pop.complain  ===  "object" ) {
         if ( ! listed ) {
            Pop.$ol  =  $( "<ol>" );
         }
         n  =  Pop.complain.length;
         for ( i = 0;  i < n;  i++ ) {
            Pop.$ol.append( Pop.complain[ i ] );
         }   // for i
         if ( ! Pop.$list ) {
            Pop.$list  =  $( "<div>" );
            Pop.$list.attr( "id",  ELP.type + "_list" )
                     .css( { "clear": "both" } );
            ELP.gui.$wrapper.append( Pop.$list );
         }
         if ( ! listed ) {
            Pop.$list.append( Pop.$ol );
         }
      }
   };   // Box.fill()



   Box.flip  =  function ( after, assign ) {
      // Toggle list
      // Precondition:
      //    after   -- true iff explicit, else toggle
      //    assign  -- true iff to be opened
      // Uses:
      //    >  Pop.$list
      //    >  .config.mode
      //    >  Box.$action
      //    >< Box.large
      //    Box.fill()
      //    Box.flop()
      //    Box.fiat()
      //    Box.future()
      // Remark: May be used as event handler -- 'this' is not accessed
      // 2014-01-20 PerfektesChaos@de.wikipedia
      var live  =  ( typeof Pop.$list  ===  "object" ),
          list, s, submit, suspend;
      if ( after === true ) {
         list  =  assign;
      } else if ( typeof Box.large  ===  "boolean" ) {
         list  =  ( ! Box.large );
      } else {
         list  =  true;
      }
      submit   =  ( list ? "hide" : "show" );
      suspend  =  ( list ? "show" : "hide" );
      s   =  "$" + suspend;
      if ( typeof Box[ s ]  ===  "object" ) {
         Box[ s ].hide();
      }
      if ( list ) {
         if ( live ) {
            Pop.$list.show();
         } else {
            Box.fill();
         }
      } else if ( live ) {
         Pop.$list.hide();
      }
      Box.flop( true );
      s   =  "$" + submit;
      if ( typeof Box[ s ]  ===  "object" ) {
         Box[ s ].show();
      } else if ( typeof Box.$action  ===  "object" ) {
         Box.$action.append(  Box.fiat( submit )  );
      }
      if ( ELP.config.mode === 1 ) {
         Box.future( true, list );
      }
      Box.large  =  list;
   };   // Box.flip()



   Box.flop  =  function ( align ) {
      // Remove clock/throbber, add spacer
      // Precondition:
      //    align  -- true:add spacer
      // Uses:
      //    >  Box.$spacer
      //    >  Box.$action
      //    >< Box.$throbber
      //    Box.fiat()
      // 2014-01-20 PerfektesChaos@de.wikipedia
      if ( typeof this.$throbber  ===  "object" ) {
         this.$throbber.remove();
         delete this.$throbber;
      }
      if ( align    &&
           typeof this.$spacer  !==  "object" ) {
         Box.$action.append(  Box.fiat( "spacer" )  );
      }
   };   // Box.flop()



   Box.future  =  function ( after, assign ) {
      // Memorize toggle state
      // Precondition:
      //    after   -- true iff storing, else retrieving
      //    assign  -- true iff open
      // Postcondition:
      //    Returns previous state on retrieving
      // Uses:
      //    window.sessionStorage
      //    >  .type
      // 2014-01-20 PerfektesChaos@de.wikipedia
      var r  =  false,
          s;
      if ( typeof window.sessionStorage  ===  "object" ) {
         s  =  window.sessionStorage.getItem( ELP.type );
         if ( ! s ) {
            s  =  "";
         }
         if ( after ) {
            s  =  s.replace( /~details=.~/g, "" )
                  + "~details="
                  + ( assign ? 1 : 0 )
                  + "~";
            window.sessionStorage.setItem( ELP.type, s );
         } else {
            r  =  ( s.indexOf( "~details=1~" )  >=  0 );
         }
      }
      return r;
   };   // Box.future()



   Box.google  =  { de: "de",
                    fr: "fr"
                  };   // 2015-11-25



   Content.fetch  =  function () {
      // Retrieve list of URL appearing in subject page
      // Uses:
      //    this
      //    >  .problems
      //    >  .scope
      //    >  .gui.$content
      //    >< Content.pointer
      //     < Content.reWiki
      // fair()
      // Content.found()
      // Box.fill()
      // 2016-11-16 PerfektesChaos@de.wikipedia
      var i, n, swift, $external;
      if ( typeof ELP.problems  ===  "object"  &&
           typeof this.pointer  !==  "object" ) {
         swift  =  "(?:ipedia"
                   + "|ibooks"
                   + "|imediafoundation"
                   + "|inews"
                   + "|iquote"
                   + "|isource"
                   + "|iversity"
                   + "|ivoyage"
                   + "|tionary";
         if ( ELP.scope !== "info" ) {
            swift  =  swift + "|idata";
         }
         swift  =  "\\/\\/[^/#?]*\\.?wik" + swift + ")\.org/wiki/";
         swift  =  "^(?:[hft]+tps?:)?" + swift;
         this.reWiki   =  new RegExp( swift );
         n             =  ELP.problems.length;
         this.pointer  =  { };
         for ( i = 0;  i < n;  i++ ) {
            this.pointer[ fair( ELP.problems[ i ].url ) ]  =  i;
         }   // for i
         $external  =  ELP.gui.$content.find( ".external" );
         if ( $external.length ) {
            $external.each( Content.found );
         }
      }
   };   // Content.fetch()



   Content.fill  =  function () {
      // Create/update list of URL
      // Postcondition:
      //    Returns true if any list item present
      // Uses:
      //    >  Content.signature
      //    >  .gui.$content
      //    >  .scope
      //    >  .problems
      //    >  Box.wikilinks
      //    >  .gui.$wrapper
      //    >< Pop.$ol
      //    >< Pop.$list
      //     < Pop.complain
      //     < Pop.limited
      // Pop.factory()
      // Pop.friend()
      // mw.util.addCSS()
      // 2016-09-18 PerfektesChaos@de.wikipedia
      var i, n, r, $e;
      ELP.gui.$content.find( "." + Content.signature ).remove();
      Pop.complain  =  [ ];
      Pop.limited   =  ( ELP.scope !== "view" );
      if ( typeof ELP.problems  ===  "object" ) {
         n  =  ELP.problems.length;
         if ( n ) {
            for ( i = 0;  i < n;  i++ ) {
               $e  =  Pop.factory( ELP.problems[ i ],
                                   Pop.complain.length,
                                   -1 );
               if ( $e ) {
                  $e.attr( { "data-problem": i } );
                  Pop.complain.push( $e );
               }
            }   // for i
         }
      }
      if ( typeof Box.wikilinks  ===  "object" ) {
         n  =  Box.wikilinks.length;
         if ( n ) {
            for ( i = 0;  i < n;  i++ ) {
               Pop.friend( Box.wikilinks[ i ] );
            }   // for i
         }
      }
      if ( Pop.complain.length ) {
         r  =  true;
      } else {
         delete Pop.complain;
         r  =  false;
      }
      return r;
   };   // Content.fill()



   Content.flip  =  function ( about ) {
      // Toggle click on link button
      // Uses:
      //    this  -- clicked element
      //    >  Pop.complain
      //    >  .problems
      //    >  Content.signature
      //    >< Content.$body
      //     < Content.$window
      //    Pop.factory()
      //    .lang.find()
      // 2016-09-18 PerfektesChaos@de.wikipedia
      var illusive = 0.9,
          margin   = 10,
          j, k, offset, options, sign, $c;
      if ( about.popup ) {
         about.live  =  ! about.live;
      } else {
         $c  =  Pop.complain[ about.id ];
         if ( $c ) {
            if ( about.sequence ) {
               j  =  about.sequence.charCodeAt( 0 )  -  65;
            } else {
               j  =  0;
            }
            sign  =  $c.attr( "data-problem" );
            if ( sign ) {
               k   =  parseInt( sign, 10 );
               $c  =  Pop.factory( ELP.problems[ k ],
                                   about.id,
                                   j,
                                   about.section );
            } else if ( about.syntax ) {
               $c  =  $( "<span>" );
               $c.text( ELP.lang.find( "wikilink" ) );
            } else {
               $c  =  false;
            }
         }
         if ( $c ) {
            if ( typeof this.$body  !==  "object" ) {
               this.$body    =  $( "body" );
               this.$window  =  $( window );
            }
            sign  =  Content.signature + "-" + about.id;
            if ( about.sequence ) {
               sign  =  sign + about.sequence;
            }
            $c.attr( { "id": sign } )
              .addClass( Content.signature + "-popup" )
              .css( { "display":        "table",
                      "font-family":    "sans-serif",
                      "font-size":      "medium",
                      "font-style":     "normal",
                      "font-variant":   "normal",
                      "padding-top":    "3px",
                      "visibility":     "hidden" } );
            this.$body.append( $c );
            $c       =  $( "#" + sign );
            options  =  { align:    "center",
                          anchor:   false,
                          height:   $c.outerHeight() + margin,
                          padded:   false,
                          width:    $c.outerWidth() + margin };
            $c.detach();
            options.$content  =  $c;
            options.$content.css( { "display":    "block",
                                    "visibility": "visible" } );
            about.popup  =  new OO.ui.PopupWidget( options );
            about.popup.$element.attr( { "role": "tooltip" } )
                                .css( { "opacity": illusive } );
            this.$body.append( about.popup.$element );
            this.$window.resize( function() {
                                    if ( about.live ) {
                                       about.live  =  false;
                                       about.popup.toggle( false );
                                    }
                                 } );
            about.live  =  1;
         }
      }
      if ( about.popup ) {
         if ( about.live ) {
            offset        =  about.$parent.offset();
            offset.left  +=  about.$parent.outerWidth() / 2;
            offset.top   -=  about.popup.height + 5;
            if ( offset.top < 0 ) {
               offset.top  =  about.$parent.offset().top
                              + about.$parent.outerHeight()
                              + 3;
            }
            j  =  $( window.document ).scrollLeft();
            k  =  this.$window.width()  +  j;
            if ( offset.left  +  about.popup.width / 2   >   k ) {
               offset.left  =  k  -  about.popup.width / 2 - 2;
               if ( offset.left < 0 ) {
                  offset.left  =  j + 3;
               }
            }
            if ( about.live === true ) {
               about.popup.toggle( true );
            }
            about.popup.$element.offset( offset );
            if ( about.live !== true ) {
               about.popup.toggle( true );
            }
         } else {
            about.popup.toggle( false );
         }
      }
   };   // Content.flip()



   Content.found  =  function () {
      // External link has been found in content area
      // Precondition:
      //    index    -- index among all external links in content area
      //    element  -- matching element
      // Postcondition:
      //    Returns true for search continuaton
      // Uses:
      //    this  -- DOM element
      //    >  Content.pointer
      //    >  Content.reWiki
      //    >< .problems.effective
      //    >< Box.wikilinks
      //    far()
      //    fair()
      // 2016-09-18 PerfektesChaos@de.wikipedia
      var $el  =  $( this ),
          k, p, s;
      s  =  $el.attr( "href" );
      if ( s === "#" ) {
         s  =  $el.attr( "data-href" );
      }
      if ( s ) {
         s  =  fair( far( s ) );
         k  =  Content.pointer[ s ];
         if ( typeof k  ===  "number" ) {
            p  =  ELP.problems[ k ];
            if ( p.effective ) {
               p.effective.push( $el );
            } else {
               p.effective  =  [ $el ];
            }
         } else if ( Content.reWiki.test( s ) ) {
            p  =  [ s, $el ];
            if ( Box.wikilinks ) {
               Box.wikilinks.push( p );
            } else {
               Box.wikilinks  =  [ p ];
            }
         }
      }
      return true;
   };   // Content.found()



   Content.frame  =  function ( $a, allow ) {
      // Detect most recent editsection link
      // Precondition:
      //    $a     -- element on tree level
      //    allow  -- number, of permitted recursion
      // Postcondition:
      //    Returns editsection link, or false
      // Uses:
      //    Content.frame()   -- self, recursive
      // 2014-02-20 PerfektesChaos@de.wikipedia
      var $el  =  $a.prevAll( ":header" ),
          r    =  false;
      if ( $el.length ) {
         $el  =  $el.first().children( ".mw-editsection" );
         if ( $el.length ) {
            $el  =  $el.children( "a" );
            r    =  $el.attr( "href" );
         }
      }
      if ( ! r ) {
         $el  =  $a.parent( "#mw-content-text" );
         if ( ! $el.length  &&  allow ) {
            r  =  Content.frame( $a.parent(),  allow - 1 );
         }
      }
      return r;
   };   // Content.frame()



   Content.furnish  =  function ( $a, assign, another ) {
      // Equip element with anchor, marker, editsection
      // Precondition:
      //    $a       -- element with <a href=URL>
      //    assign   -- number in complain (zero-based)
      //    another  -- string or false
      // Uses:
      //    >  .type
      //    >  Content.signature
      //    >  .config.css.href
      //    >  Pop.limited
      //    >  .config.css.ref
      //    >< Content.start
      //    Content.frame()
      //    mw.util.wikiUrlencode()
      //    (Content.flip)
      // 2016-09-30 PerfektesChaos@de.wikipedia
      var sign  =  "data-" + ELP.type.toLowerCase(),
          id,
          max,
          section,
          styleNE,
          styleSW,
          syntax,
          widget,
          $el,
          $id,
          $span;
      if ( ! $a.attr( sign ) ) {
         $a.attr( sign, assign );
         max      =  20;
         section  =  false;
         id       =  assign + 1;
         sign     =  id  +  ( another ? another : "" );
         styleSW  =  "#707070";
         styleNE  =  "#D8D8D8";
         $id      =  $( "<span>" );
         $id.attr( { "class": Content.signature,
                     "id":    ELP.type + "_" + sign } );
         $a.attr( { "target": Pop.forward( "ExternalL" ) } )
           .before( $id )
           .css( ELP.config.css.href );
         if ( ! Pop.limited ) {
            $el  =  $a.closest( ".references" );
            if ( ! $el.length ) {
               section  =  this.frame( $a, max );
               if ( ! section ) {
                  if ( typeof this.start  !==  "string" ) {
                     section     =  mw.config.get( "wgPageName" );
                     this.start  =  mw.config.get( "wgScript" )
                                    + "?action=edit&section=0&title="
                                    + mw.util.wikiUrlencode( section );
                  }
                  section  =  this.start;
               }
            }
         }
         $span  =  $( "<span>" );
         $el    =  $( "<span>" );
         $el.css( { "font-size": "25%" } )
            .text( " " );
         $span.append( $el );
         if ( another ) {
            syntax  =  false;
         } else {
            syntax  =  $a.attr( "data-syntax" );
         }
         $el  =  $( "<a>" );
         widget  =  { $parent:  $el,
                      id:       assign,
                      section:  section,
                      sequence: another,
                      syntax:   syntax,
                      popup:    false };
         $el.attr( { "data-sign":  "#" + assign,
                     "href":       "#",
                     "title":      ELP.type } )
            .click( function () {
                       Content.flip( widget );
                       // Returns always false, stop the bubbling
                       return false;
                    } )
            .css( { "background":          "#D0D0D0",
                    "border-top-color":    styleNE,
                    "border-right-color":  styleNE,
                    "border-left-color":   styleSW,
                    "border-bottom-color": styleSW,
                    "border-style":        "solid",
                    "border-width":        "2px",
                    "color":               "#000000",
                    "display":             "inline-block",
                    "padding-left":        "2px",
                    "padding-right":       "2px",
                    "padding-top":         "1px",
                    "padding-bottom":      "1px",
                    "text-decoration":     "none" } )
             .text( sign );
         $span.append( $el );
         $el  =  $( "<span>" );
         $el.css( { "font-size": "25%" } )
            .text( " " );
         $span.append( $el )
              .attr( { "class": Content.signature + "-button" } );
         $a.after( $span );
      }
   };   // Content.furnish()



   Content.signature  =  ELP.type + "_el-id";



   Pop.facetter  =  function ( access ) {
      // Split URL into segmentation URL links
      // Precondition:
      //    access  -- string with URL
      // Postcondition:
      //    Returns $(<li>), or false
      // Uses:
      //    Pop.forward()
      // 2016-09-01 PerfektesChaos@de.wikipedia
      var join  =  access.indexOf( "//" ),
          $r    =  false,
          got, i, n, s, shift, spacer, start, sub, $el;
      if ( join >= 0 ) {
         join  +=  2;
         i      =  access.indexOf( "/", join );
         if ( i >= 0 ) {
            s      =  access.substr( join,  i - join );
            got    =  /\.([^.]+\.[a-z]+)$/.exec( s );
            shift  =  Pop.forward( "ExternalL" );
            if ( got ) {
               sub  =  got[ 1 ];
               $el  =  $( "<a>" );
               $el.attr( { "href":   access.substr( 0, join ) + sub,
                           "target": shift } );
               $el.text( sub );
               $r  =  $( "<li>" );
               $r.append( $el );
            }
            join  =  i + 1;
            i     =  access.indexOf( "?", join );
            if ( i < 0 ) {
               sub  =  access;
            } else {
               sub  =  access.substr( 0, i );
            }
            sub  =  sub.substr( join );
            if ( sub !== ""  ||  i >= 0 ) {
               if ( $r ) {
                  spacer  =  String.fromCharCode( 160, 183, 32 );
                  $el     =  $( "<span>" );
                  $el.text( spacer );
                  $r.append( $el );
               } else {
                  $r  =  $( "<li>" );
               }
               start  =  access.substr( 0,  join - 1 );
               $el    =  $( "<a>" );
               $el.attr( { "href":   start + "/" ,
                           "target": shift } )
                  .text( start );
               $r.append( $el );
               if ( sub !== "" ) {
                  got  =  sub.split( "/" );
                  n    =  got.length;
                  for ( i = 0;  i < n;  i++ ) {
                     $el  =  $( "<span>" );
                     $el.text( " / " );
                     $r.append( $el );
                     s      =  got[ i ];
                     start  =  start + "/" + s;
                     $el    =  $( "<a>" );
                     $el.attr( { "href":   start,
                                 "target": shift } )
                        .text( s );
                     $r.append( $el );
                  }   // for i
               }
            }
         }
      }
      return $r;
   };   // Pop.facetter()



   Pop.factory  =  function ( about, assigned, another, access ) {
      // Create list item on problem entry
      // Precondition:
      //    about     -- object with analysis
      //                 .effective   Array of objects, .$el = <a>jQuery
      //                 .url         mandatory; string
      //                 .mode        numerical;  HTTP other than 404
      //                 .since       string; recent years
      //                 .listed      Spam etc.
      //                 .link        protocol secure
      //                 .less        suitable without trailing
      //                 .wba         true / id
      //                 .cit         id
      //                 .sub         string; suitable partial URL
      //                 .other       Array[ numbers ] with other pageids
      //                 .learnt      true: should be resolved
      //    assigned  -- id of complaint
      //    another   -- id of particular occurrence, -1 for all
      //    access    -- section edit URL, or false
      // Postcondition:
      //    Returns $(<li>), $(<div>),  or false
      // Uses:
      //    >  .type
      //    >  Pop.limited
      //    >  Pop.complain
      //    Content.furnish()
      //    .lang.find()
      //    Pop.fatal()
      //    Pop.forbidden()
      //    Pop.former()
      //    Pop.figure()
      //    Pop.webcite()
      //    Pop.wayback()
      //    Pop.flop()
      //    Pop.further()
      //    Pop.facetter()
      //    Pop.google()
      // 2017-01-30 PerfektesChaos@de.wikipedia
      var legal  =  true,
          $r     =  $( ( another < 0  ?  "<li>"  :  "<div>" )  ),
          $ul    =  $( "<ul>" ),
          list, lock, i, j, n, s, spacer, $el, $uli;
      if ( typeof about.effective  ===  "object" ) {
         spacer  =  String.fromCharCode( 160, 183, 32 );
         s       =  about.url;
         list    =  ( another < 0 );
         if ( typeof about.listed  ===  "boolean"   &&
              about.listed ) {
            lock  =  true;
            if ( list ) {
               $el  =  this.forbidden( s );
            } else {
               $el  =  $( "<span>" );
               $el.css( { "font-weight": "bolder" } )
                  .text( "SPAM" );
            }
         } else if ( list ) {
            $el  =  $( "<a>" );
            $el.attr( { "href":   s,
                        "target": Pop.forward( "ExternalL" ) } )
               .text( s );
         }
         if ( $el ) {
            $r.append( $el );
         }
         n  =  about.effective.length;
         if ( list  ||  n > 1 ) {
            $uli   =  $( "<li>" );
            for ( i = 0;  i < n;  i++ ) {
               if ( i !== another ) {
                  if ( i ) {
                     $el  =  $( "<span>" );
                     $el.text( spacer );
                     $uli.append( $el );
                  }
                  j    =  assigned + 1;
                  s    =  String.fromCharCode( i + 65 );
                  $el  =  $( "<a>" );
                  $el.text( s )
                     .attr( "href",  "#" + ELP.type + "_" + j + s );
                  $uli.append( $el );
                  Content.furnish( about.effective[ i ],
                                   assigned,
                                   s );
               }
            }   // for i
            $ul.append( $uli );
         }
         if ( ! lock ) {
            $uli  =  this.fatal( s );
            if ( $uli ) {
               $ul.append( $uli );
               legal  =  false;
            }
            if ( typeof about.link  ===  "boolean"
                 &&     about.link ) {
               $el  =  this.flow( s );
               if ( $el ) {
                  $ul.append( $el );
               }
            }
            if ( typeof about.since  ===  "string" ) {
               $ul.append(  this.former( about.since )  );
            }
            if ( typeof about.mode  ===  "number" ) {
               $ul.append(  this.figure( about.mode, s )  );
            }
            if ( typeof about.cit === "string" ) {
               $ul.append(  this.webcite( s, about.cit )  );
            }
            s  =  typeof about.wba;
            if ( s === "string"  ||  s === "boolean" ) {
               $ul.append(  this.wayback( about.url, about.wba )  );
            }
            $uli  =  this.flop( about.url, about.sub, about.less );
            if ( $uli ) {
               $ul.append( $uli );
            }
            if ( typeof about.other  ===  "object" ) {
               $uli  =  this.further( about.other, about.url );
               if ( $uli ) {
                  $ul.append( $uli );
               }
            }
            if ( legal ) {
               if ( ELP.config.levels ) {
                  $uli  =  this.facetter( about.url );
                  if ( $uli ) {
                     $ul.append( $uli );
                  }
               }
               if ( ELP.config.lookup ) {
                  $uli  =  this.google( about.url, about.effective );
                  if ( $uli ) {
                     $ul.append( $uli );
                  }
               }
            }
         }
         if ( typeof about.learnt  ===  "boolean"  &&
              about.learnt ) {
            $uli  =  $( "<li>" );
            $el   =  $( "<span>" );
            $el.css( { "font-weight": "bold" } )
               .text( ELP.lang.find( "notdone" ) );
            $uli.append( $el );
            $ul.append( $uli );
         } else if ( access ) {
            $uli  =  $( "<li>" );
            $el   =  $( "<span>" );
            $el.text( "[" );
            $uli.append( $el );
            $el  =  $( "<a>" );
            $el.attr( "href", access )
               .text( ELP.lang.find( "editSrc" ) );
            $uli.append( $el );
            $el  =  $( "<span>" );
            $el.text( "]" );
            $uli.append( $el );
            $ul.append( $uli );
         }
      } else if ( this.limited ) {
         $r  =  false;
      } else if ( about.learnt ) {
         $el  =  $( "<span>" );
         $el.css( { "text-decoration": "line-through" } )
            .text( about.url );
         $r.append( $el );
         $el  =  $( "<span>" );
         $el.text( ELP.lang.find( "discard" ) );
         $uli  =  $( "<li>" );
         $uli.append( $el );
         $ul.append( $uli );
      } else {
         $r  =  false;
      }
      if ( $r ) {
         if ( $ul.children().length ) {
            $r.append( $ul );
         } else if ( ! list ) {
            $el  =  $( "<span>" );
            $el.text( "404" );
            $r.append( $el )
              .css( { "text-align": "center" } );
         }
      }
      return $r;
   };   // Pop.factory()



   Pop.fatal  =  function ( access ) {
      // Create list item on syntactically invalid URL
      // Precondition:
      //    access  -- string; URL
      // Postcondition:
      //    Returns $(<li>), or false
      // Uses:
      //    >< Pop.rePort
      //    >< Pop.reIPv4
      //    >< Pop.reIPv4s
      //    .lang.find()
      // 2014-03-19 PerfektesChaos@de.wikipedia
      var i   =  access.indexOf( "//" ),
          $r  =  false,
          got, k, s, $el;
      if ( i >= 0 ) {
         s  =  access.substr( i + 2 );
         i  =  s.indexOf( "/" );
         if ( i >= 0 ) {
            if ( typeof this.rePort  !==  "object" ) {
               this.rePort   =  new RegExp( "^(.+):[0-9]+$" );
               this.reIPv4   =  new RegExp( "^[0-9.]+$" );
               this.reIPv4s  =  new RegExp( "^"
                                            + "([0-9]+)\\."
                                            + "([0-9]+)\\."
                                            + "([0-9]+)\\."
                                            + "([0-9]+)"
                                            + "$" );
               this.reIPv6   =  new RegExp( "^\\[[0-9.a-f:]+\\]$" );
               this.TLD2     =  new RegExp( "^[0-9a-z]"
                                            + "[-0-9.a-z]*"
                                            + "[0-9a-z]"
                                            + "\\.[a-z]{2,}"
                                            + "$" );
            }
            s    =  s.substr( 0, i ).toLowerCase();
            got  =  this.rePort.exec( s );
            if ( got ) {
               s  =  got[ 1 ];
            }
            if ( this.reIPv4.test( s ) ) {
               got  =  this.reIPv4s.exec( s );
               for ( i = 1;  i <= 4;  i++ ) {
                  k   =  parseInt( got[ i ], 10 );
                  if ( k > 255 ) {
                     $r  =  true;
                     break;   // for i
                  }
               }   // for i
            } else if ( ! this.reIPv6.test( s )  &&
                        ! this.TLD2.test( s ) ) {
               $r  =  true;
            }
            if ( $r ) {
               $r   =  $( "<li>" );
               $el  =  $( "<span>" );
               $el.text( ELP.lang.find( "syntax" ) )
                  .css( { "font-weight": "bold" } );
               $r.append( $el );
            }
         }
      }
      return $r;
   };   // Pop.fatal()


   Pop.favour  =  function ( access, about ) {
      // Create help link element
      // Precondition:
      //    access  -- string; ID of page ID
      //    about   -- string with title; or not
      // Postcondition:
      //    Returns jQuery object, or undefined
      // Uses:
      //    >  .project
      //    .lang.find()
      //    mw.util.getUrl()
      //    Pop.forward()
      // 2015-12-02 PerfektesChaos@de.wikipedia
      var show  =  about,
          id, $a, $e, $r;
      if ( typeof ELP.project[ access ]  ===  "number" ) {
         id  =  ELP.project[ access ];
         if ( ! show ) {
            show  =  ELP.lang.find( "help" );
         }
         $a  =  $( "<a>" );
         $a.attr( { "href":   mw.util.getUrl( "Special:Redirect" )
                              + "/page/" + id,
                    "target": Pop.forward( "help" ) } )
            .text( show );
         if ( about ) {
            $r  =  $a;
         } else {
            $r  =  $( "<span>" );
            $e  =  $( "<span>" );
            $e.text( "(" );
            $r.append( $e )
              .append( $a );
            $e  =  $( "<span>" );
            $e.text( ")" );
            $r.append( $e )
              .css( { "margin-left": "1em" } );
         }
      }
      return $r;
   };   // Pop.favour()



   Pop.figure  =  function ( answer, access ) {
      // Create item wrt HTTP status code
      // Precondition:
      //    answer  -- number with HTTP status code
      //    access  -- string with URL
      // Postcondition:
      //    Returns $(<li>)
      // Uses:
      //    >  .project.idHTTPstat
      //    .lang.find()
      //    Pop.forward()
      //    mw.util.getUrl()
      // 2015-12-02 PerfektesChaos@de.wikipedia
      var s    =  ELP.lang.find( "mode" ),
          $r   =  $( "<li>" ),
          $el  =  Pop.favour( "idHTTPstat", s ),
          i;
      if ( $el ) {
         $r.append( $el );
         s  =  "";
      }
      $el  =  $( "<span>" );
      s    =  s + " = " + answer;
      if ( answer >= 500  &&  answer <= 599 ) {
         s  =  s  +  " ("  +  ELP.lang.find( "mode5" )  +  ")";
         i  =  access.indexOf( "//" );
         if ( i > 0 ) {
            $el.text( s );
            $r.append( $el );
            $el  =  $( "<span>" );
            $el.text( String.fromCharCode( 160, 183, 32 ) );
            $r.append( $el );
            $el  =  $( "<a>" );
            i    =  access.indexOf( "/", i + 2 );
            if ( i > 0 ) {
               s  =  access.substr( 0, i + 1 );
            } else {
               s  =  access + "/";
            }
            $el.attr( { "href":   mw.util.getUrl( "Special:Linksearch" )
                                  + "/" + s,
                        "target": Pop.forward( "Linksearch" ) } );
            s  =  ELP.lang.find( "more" );
         }
      } else if ( answer >= 300  &&  answer <= 399 ) {
         s  =  s + " (" +  ELP.lang.find( "mode3" )  +  ")";
      } else if ( answer < 100 ) {
         s  =  s + " (" +  ELP.lang.find( "curl" )  +  ")";
      }
      $el.text( s );
      $r.append( $el );
      return $r;
   };   // Pop.figure()



   Pop.flop  =  function ( access, attempt, after ) {
      // Create list item on partial URL
      // Precondition:
      //    access   -- string; URL as provided
      //    attempt  -- string; suitable partial URL, or nil
      //    after    -- true, if working without trailing special
      // Uses:
      //    this
      //    >< Pop.reEnd
      //    >< Pop.reFile
      //    >< Pop.rePipe
      //    >< Pop.reSlash
      //    >< Pop.reURL
      //    .lang.find()
      //    Pop.forward()
      // 2015-12-02 PerfektesChaos@de.wikipedia
      var s      =  access,
          sub    =  attempt,
          i      =  s.lastIndexOf( "|" ),
          j      =  s.lastIndexOf( "%7C" ),
          like   =  false,
          story  =  false,
          $r     =  false,
          suffix, $el, $help, $show;
      if ( typeof this.reEnd  !==  "object" ) {
         this.reEnd    =  new RegExp( "[(,.;?|]$" );
         // charCodeAt  44 46 59 63
         this.reFile   =  new RegExp( "[a-zA-Z0-9]\\.[a-zA-Z]" );
         this.rePipe   =  new RegExp( "%7C$", "i" );
         this.reSlash  =  new RegExp( "//.+/.+/$" );
         this.reURL    =  new RegExp( "[&#]" );
      }
      if ( i > 0  ||  j > 0 ) {
         if ( j > i ) {
            s       =  s.replace( this.rePipe, "|" );
            suffix  =  s.substr( j + 3 );
            i       =  j;
         } else {
            suffix  =  s.substr( i + 1 );
         }
         if ( suffix ) {
            if ( this.reURL.test( suffix ) ) {
               suffix  =  false;
            } else if ( this.reFile.test( suffix ) ) {
               suffix  =  false;
            }
            if ( suffix ) {
               story  =  ELP.lang.find( "pipe" );
               $help  =  Pop.favour( "idPipe" );
               $show  =  $( "<span>" );
               $show.css( { "font-weight": "bold",
                            "margin-left": "1em" } )
                    .text( "|" + suffix + "]" );
               if ( ! sub ) {
                  sub   =  s.substr( 0, i );
                  like  =  true;
               }
            }
         }
         /*   won't happen
      } else if ( access.indexOf( "[" ) > 0  ||
                  access.indexOf( "%5B" ) > 0 ) {
         s  =  ELP.lang.find( "bracket" );
         */
      }
      if ( after  ||
           this.reEnd.test( s )  ||
           this.reSlash.test( s ) ) {
         story  =  ( story  ?  story + ". "  :  "" )
                   + ELP.lang.find( "special" );
         if ( ! sub ) {
            sub   =  s.substr( 0,  s.length - 1 );
            like  =  true;
         }
      }
      if ( story ) {
         $r   =  $( "<li>" );
         $el  =  $( "<span>" );
         $el.text( story );
         $r.append( $el );
      }
      if ( typeof sub  ===  "string" ) {
         if ( ! $r ) {
            $r  =  $( "<li>" );
         }
         story  =  ( story  ?  ". "  :  "" )
                   + ELP.lang.find( "sub" );
         $el    =  $( "<span>" );
         $el.text( story );
         $r.append( $el );
         if ( $help ) {
            $r.append( $help );
         }
         if ( $show ) {
            $r.append( $show );
         }
         $el  =  $( "<a>" );
         $el.attr( { "href":   sub,
                     "target": Pop.forward( "ExternalL" ) } )
            .css( { "margin-left": "1em" } )
            .text( ELP.lang.find( ( like ? "try" : "success" ) ) );
         $r.append( $el );
      }
      return $r;
   };   // Pop.flop()



   Pop.flow  =  function ( access ) {
      // Change https/http etc.
      // Precondition:
      //    access  -- string with URL
      // Postcondition:
      //    Returns $(<li>), or false
      // Uses:
      //    >< Pop.reScheme
      //     < Pop.reWiki
      //    Pop.forward()
      // 2015-10-22 PerfektesChaos@de.wikipedia
      var $r  =  false,
          parts, scheme, $a;
      if ( typeof Pop.reScheme  !==  "object" ) {
         Pop.reWiki  =  new RegExp( "^((?:ht|f)tp)(s?)(//:.+)$",  "i" );
      }
      parts  =  Pop.reWiki.exec( access );
      if ( parts ) {
         scheme  =  parts[ 1 ]   +   ( parts[ 2 ]  ?  ""  :  "s"  );
         $a      =  $( "<a>" );
         $r      =  $( "<li>" );
         $a.attr( { "href":   scheme + parts[ 3 ],
                    "target": Pop.forward( "ExternalL" ) } )
           .text( scheme + "^//:" );
         $r.append( $a );
      }
      return $r;
   };   // Pop.flow()



   Pop.forbidden  =  function ( access ) {
      // Create list item on spam black list
      // Precondition:
      //    access  -- string with escaped URL
      // Postcondition:
      //    Returns $(<li>)
      // Uses:
      //    fair()
      //    .lang.find()
      //    Pop.forward()
      // 2015-12-06 PerfektesChaos@de.wikipedia
      var s    =  access + "/",
          j    =  s.indexOf( "\\\\" ),
          k    =  s.indexOf( "/" ),
          $a   =  $( "<a>" ),
          $el  =  $( "<span>" ),
          $r   =  $( "<span>" );
      $a.attr( { "href":   fair( access ),
                 "target": Pop.forward( "ExternalL" ) } )
        .css( { "font-weight": "bold" } )
        .text( ELP.lang.find( "blocked" ) );
      $r.append( $a );
      $el.text( ELP.lang.find( "blocking" ) )
         .css( { "margin-left": "1em" } );
      $r.append( $el );
      $el  =  $( "<span>" );
      $el.text( s.substring( j + 2, k ) )
         .css( { "font-style": "italic",
                 "margin-left": "1em" } );
      $r.append( $el );
      return $r;
   };   // Pop.forbidden()



   Pop.former  =  function ( already ) {
      // Create list item on previous years
      // Precondition:
      //    already  -- string; previous year or run
      // Postcondition:
      //    Returns $(<li>)
      // Uses:
      //    .lang.find()
      // 2016-04-18 PerfektesChaos@de.wikipedia
      var $el  =  $( "<span>" ),
          $r   =  $( "<li>" );
      $el.text( ELP.lang.find( "since" )  +  already );
      $r.append( $el );
      return $r;
   };   // Pop.former()



   Pop.forward  =  function ( assume ) {
      // Determine new tab/window for linked page target
      // Precondition:
      //    assume  -- string; window ID
      // Postcondition:
      //    Returns string; window ID
      // Uses:
      //    >  .config.loose
      // 2015-10-22 PerfektesChaos@de.wikipedia
      return ( ELP.config.loose ? "_blank" : assume );
   };   // Pop.forward()



   Pop.friend  =  function ( about ) {
      // Create list item on wikilink URL
      // Precondition:
      //    about  -- Array  [0]: URL   [1]: $(<a>)
      // Uses:
      //    >  Pop.wikiClasses
      //    >  .type
      //    Pop.forward()
      //    .lang.find()
      //    Content.furnish()
      // 2016-04-26 PerfektesChaos@de.wikipedia
      var src   =  about[ 0 ],
          $got  =  about[ 1 ],
          k, n, $a, $li, $ul, $uli;
      if ( ! $got.closest( Pop.wikiClasses ).length ) {
         k   =  this.complain.length;
         $got.attr( { "data-syntax": "wikilink" } );
         $a  =  $( "<a>" );
         $a.attr( { "href":   src,
                    "target": Pop.forward( "ExternalL" ) } )
           .text( src );
         $li  =  $( "<li>" );
         $li.append( $a );
         $a  =  $( "<a>" );
         n   =  k + 1;
         $a.attr( "href",  "#" + ELP.type + "_" + n )
           .text( ELP.lang.find( "wikilink" ) );
         $uli  =  $( "<li>" );
         $uli.append( $a );
         $ul  =  $( "<ul>" );
         $ul.append( $uli );
         $li.append( $ul );
         Content.furnish( $got, k );
         this.complain.push( $li );
      }
   };   // Pop.friend()



   Pop.further  =  function ( array, access ) {
      // Create item wrt other pages of same URL
      // Precondition:
      //    array    -- Array with numbers of pages
      //    access   -- string with URL
      // Postcondition:
      //    Returns $(<li>), or false
      // Uses:
      //    .lang.find()
      //    mw.util.getUrl()
      //    Pop.forward()
      // 2016-01-01 PerfektesChaos@de.wikipedia
      var i, s, spacer, $el,
          n   =  array.length,
          $r  =  false;
      if ( n ) {
         $r      =  $( "<li>" );
         $el     =  $( "<span>" );
         s       =  ( n === 1  ?  "other"  :  "others" );
         spacer  =  String.fromCharCode( 160, 183, 32 );
         $el.text( ELP.lang.find( s ) );
         $r.append( $el );
         for ( i = 0;  i < n;  i++ ) {
            $el  =  $( "<span>" );
            $el.text( ( i  ?  spacer  :  " " ) );
            $r.append( $el );
            s    =  array[ i ];
            $el  =  $( "<a>" );
            $el.attr( { "href":   mw.util.getUrl( "Special:Redirect" )
                                  + "/page/" + s,
                        "target": Pop.forward( "_blank" ) } );
            $el.text( s );
            $r.append( $el );
         }   // for i
         $el  =  $( "<span>" );
         $el.text( spacer );
         $r.append( $el );
         $el  =  $( "<a>" );
         $el.attr( { "href":   mw.util.getUrl( "Special:Linksearch" )
                               + "/" + access,
                     "target": Pop.forward( "_blank" ) } )
            .text( ELP.lang.find( "current" ) );
         $r.append( $el );
      }
      return $r;
   };   // Pop.further()



   Pop.google  =  function ( access, a ) {
      // Provide link to google.de/search
      // Precondition:
      //    access  -- string with URL
      //    a       -- Array of objects with .$parent = <a> jQuery
      // Postcondition:
      //    Returns $(<li>), or false
      // Uses:
      //    >  ELP.lang.search
      //    Pop.forward()
      // 2016-09-18 PerfektesChaos@de.wikipedia
      var i   =  access.indexOf( "//" ),
          $r  =  false,
          j, n, parts, s, seek, words, $el;
      if ( i >= 0 ) {
         seek   =  "https://www.google." + ELP.lang.search
                   + "/search?q=site:";
         i     +=  2;
         n      =  access.indexOf( "/", i );
         words  =  { };
         if ( n > 0 ) {
            seek  =  seek + access.substring( i, n );
            s     =  access.substr( n ).replace( /[#?].*$/g, "")
                                       .replace( /\.[a-z0-9]+$/g, "");
            parts  =  s.split( "/" );
            n      =  parts.length;
            for ( i = 0;  i < n;  i++ ) {
               s  =  parts[ i ];
               if ( s ) {
                  words[ s ]  =  true;
               }
            }   // for i
         }
         for ( i = 0;  i < a.length;  i++ ) {
            $el    =  a[ i ];
            s      =  $el.text()
                         .replace( /[-_|:,;.?!&(){}+*=~'"\xA0\u202F]/g,
                                   " " )
                         .replace( /\s+/g, " " );
            parts  =  s.split( " " );
            for ( j = 0;  j < parts.length;  j++ ) {
               words[ encodeURIComponent( parts[ j ] ) ]  =  true;
            }   // for j
         }   // for i
         for ( s in words ) {
            seek  =  seek + "+" + s;
         }   // for s
         $el  =  $( "<a>" );
         $el.attr( { "href":   seek,
                     "target": Pop.forward( "GoogleSearch" ) } );
         $el.text( "Google" );
         $r  =  $( "<li>" );
         $r.append( $el );
      }
      return $r;
   };   // Pop.google()



   Pop.wayback  =  function ( access, almost ) {
      // Provide links to wayback.archive.org
      // Precondition:
      //    access  -- string with URL
      //    almost  -- string with version ID, or true
      // Postcondition:
      //    Returns $(<li>)
      // Uses:
      //    .lang.find()
      //    Pop.forward()
      // 2016-06-04 PerfektesChaos@de.wikipedia
      var $el  =  $( "<span>" ),
          $r   =  $( "<li>" );
      $el.text( ELP.lang.find( "wayback" )  +  " " );
      $r.append( $el );
      $el  =  $( "<a>" );
      $el.attr( { "href":   "http://wayback.archive.org/web/*/"
                            + access.replace( /</g, "%3C" ),
                  "target": Pop.forward( "wwwarchive" ) } )
         .text( ELP.lang.find( "wayback*" ) );
      $r.append( $el );
      $el  =  $( "<a>" );
      if ( typeof almost === "string"  &&  almost.length >= 10 ) {
         $el  =  $( "<span>" );
         $el.text( String.fromCharCode( 160, 183, 32 ) );
         $r.append( $el );
         $el  =  $( "<a>" );
         $el.attr( { "href":   "http://wayback.archive.org/web/"
                               + almost + "/" + access,
                     "target": Pop.forward( "wwwarchive" ) } )
            .text( ELP.lang.find( "wayback!" ) );
         $r.append( $el );
      }
      return $r;
   };   // Pop.wayback()



   Pop.webcite  =  function ( access, almost ) {
      // Provide links to webcitation.org
      // Precondition:
      //    access  -- string with URL
      //    almost  -- string with version ID, or true
      // Postcondition:
      //    Returns $(<li>)
      // Uses:
      //    .lang.find()
      //    Pop.forward()
      // 2015-10-22 PerfektesChaos@de.wikipedia
      var $el  =  $( "<span>" ),
          $r   =  $( "<li>" );
      $el.text( ELP.lang.find( "webcite" )  +  " " );
      $r.append( $el );
      $el  =  $( "<a>" );
      $el.attr( { "href":   "http://www.webcitation.org/" + almost,
                  "target": Pop.forward( "wwwarchive" ) } )
         .text( "webcitation.org" );
      $r.append( $el );
      return $r;
   };   // Pop.webcite()



   Pop.wikiClasses  =  ".mw-warning-with-logexcerpt," +
                       ".noprint," +
                       ".plainlinks," +
                       ".wikibase-entity-usage";
                       // 2016-11-03 PerfektesChaos@de.wikipedia



   ELP.lang.fallback  =  function ( access ) {
      // Simple language selection function
      // Precondition:
      //    access  -- string ID
      // Postcondition:
      //    Returns string with default language code
      // Uses:
      //    >  .gui.texts
      // 2015-10-22 PerfektesChaos@de.wikipedia
      var r;
      if ( typeof ELP.gui.texts[ access ]  ===  "object" ) {
         r  =  ELP.gui.texts[ access ].en;
      } else {
         r  =  "***????* " + access + " *????***";
      }
      return r;
   };   // .lang.fallback()



   ELP.lang.fine  =  function ( access ) {
      // Best translation function
      // Precondition:
      //    access  -- string ID
      // Postcondition:
      //    Returns string with best matching language code
      // Uses:
      //    >  .gui.texts
      //    .prego.lib.translation()
      // 2015-10-22 PerfektesChaos@de.wikipedia
      var r;
      if ( typeof ELP.gui.texts[ access ]  ===  "object" ) {
         r  =  ELP.prego.lib.translation( ELP.gui.texts[ access ] );
      } else {
         r  =  "***??* " + access + " *??***";
      }
      return r;
   };   // .lang.fine()



   ELP.gui.fresh  =  function () {
      // Update box details, if box already present
      // Uses:
      //    >  .gui.$wrapper
      //    >  .problems
      //    >  .talk.leader
      //    >  .config.mode
      //    >  .config.lazy
      //    >  .gui.$buttons
      //    >< .talk.last
      //     < Box.$action
      //    Box.fiat()
      //    Content.fetch()
      //    Content.fill()
      //    Box.future()
      //    Box.flip()
      //    Box.flop()
      //    .talk.former()
      //    .lang.find()
      //    .follow()
      // Remark: May be used as event handler -- 'this' is not accessed
      // 2015-11-23 PerfektesChaos@de.wikipedia
      var last, list, $bl, $span, $ul;
      if ( typeof ELP.gui.$wrapper  ===  "object"   &&
           typeof ELP.problems  ===  "object"
           &&     ELP.problems ) {
         $bl  =  $( "<div>" );
         $bl.css( { "float":          "left",
                    "vertical-align": "top" } );
         if ( typeof ELP.talk  ===  "object"
              &&     ELP.talk ) {
            list  =  ELP.talk.leader;
            if ( typeof ELP.talk.last  ===  "boolean" ) {
               last  =  ELP.talk.last;
            }
         } else {
            list  =  true;
         }
         if ( ELP.problems ) {
            if ( list ) {
               Box.$action  =  $bl;
               Box.$action.append(  Box.fiat( "throbber" )  );
               ELP.gui.$buttons.prepend( Box.$action );
               Content.fetch();
               if ( Content.fill() ) {
                  switch ( ELP.config.mode ) {
                     case 1:
                        list  =  Box.future( false );
                        break;
                     case 2:
                        list  =  true;
                        break;
                     default:
                        list  =  false;
                  }   // switch .config.mode
                  Box.flip( true, list );
               } else {
                  Box.flop( false );
                  // TODO   no [+] button   here ???
               }
            } else if ( ! ELP.config.lazy    &&
                        typeof ELP.talk.former  ===  "function" ) {
               $ul  =  ELP.talk.former();
               if ( $ul ) {
                  $bl  =  $( "<div>" );
                  $bl.css( { "padding": "1em" } );
                  $span  =  $( "<span>" );
                  $span.css( { "font-weight": "bold" } )
                       .text( ELP.lang.find( "discard" ) + ":" );
                  $bl.append( $span )
                     .append( $ul );
                  ELP.gui.$wrapper.append( $bl );
               }
            }
         } else {   // null: empty
            $bl.css( { "color":       "#FF0000",
                       "font-weight": "bold" } );
            $bl.text( ELP.lang.find( "empty" ) );
            ELP.gui.$buttons.prepend( $bl );
         }
         if ( ! list  &&  last ) {
            ELP.talk.last  =  false;
            $bl  =  $( "<div>" );
            $bl.css( { "background-color": "#FFD0D0",
                       "margin-right":     "1em",
                       "padding":          "1em" } )
               .text( ELP.lang.find( "earlier" ) );
            ELP.gui.$wrapper.append( $bl );
         }
         if ( typeof ELP.follow  ===  "function" ) {
            ELP.follow();
         }
      }
   };   // .gui.fresh()



   function fire( $area ) {
      // Start possible actions on current content
      // Precondition:
      //    Document is ready.
      //    All resources have been loaded.
      //    $area  -- jQuery object of $content
      // Uses:
      //    >  .gui.$wrapper
      //     < .gui.$content
      //    furnish()
      //    .gui.fresh()
      //    mw.loader.using()
      // 2016-09-18 PerfektesChaos@de.wikipedia
      ELP.gui.$content  =  $area;
      if ( typeof ELP.gui.$wrapper  !==  "object" ) {
         if ( ELP.config.less ) {
            furnish();
         } else {
            mw.loader.using( [ "oojs",
                               "oojs-ui-core",
                               "oojs-ui-widgets" ],
                             furnish );
         }
      }
   }   // fire()



   ELP[ Sub ].fire  =  function () {
      // Start possible actions on current page
      // Precondition:
      //    Current page is meaningful
      // Uses:
      //    >  .project
      //    >  Sub
      //    >  .prego.lib.translation
      //    >  .lang.fine
      //    >  .lang.fallback
      //    >  Box.google
      //    >< .request
      //    >< .config.less
      //    >< .config.levels
      //    >< .config.loose
      //    >< .config.low
      //    >< .config.lookup
      //     < .lang.find
      //     < .lang.standard
      //     < .lang.search
      //    mw.config.get()
      //    mw.hook()
      //    (fire)
      // Remark: May be used as event handler -- 'this' is not accessed
      // 2015-11-27 PerfektesChaos@de.wikipedia
      facilitated();
      if ( typeof ELP.request  ===  "string"
           &&     ELP.request === Sub   &&
           typeof ELP.project  ===  "object"
           &&     ELP.project   &&
           typeof ELP.config  ===  "object"
           &&     ELP.config ) {
         ELP.request  =  false;
         if ( typeof ELP.config.less  !==  "boolean" ) {
            ELP.config.less  =  false;
         }
         if ( typeof ELP.config.levels  !==  "boolean" ) {
            ELP.config.levels  =  false;
         }
         if ( typeof ELP.prego.lib  ===  "object"   &&
              typeof ELP.prego.lib.translation  ===  "function" ) {
            ELP.lang.find  =  ELP.lang.fine;
         } else {
            ELP.lang.find  =  ELP.lang.fallback;
         }
         if ( typeof ELP.config.loose  !==  "boolean" ) {
            ELP.config.loose  =  false;
         }
         if ( typeof ELP.config.low  !==  "boolean" ) {
            ELP.config.low  =  false;
         }
         if ( ! ELP.config.low   &&
              typeof ELP.talk  ===  "object"
              &&     ELP.talk   &&
              typeof ELP.talk.leader  ===  "boolean"
              &&     ELP.talk.leader ) {
            ELP.config.low  =  false;
         }
         if ( typeof ELP.config.lookup  ===  "boolean"
              &&     ELP.config.lookup ) {
            ELP.lang.standard  =  mw.config.get( "wgContentLanguage" );
            if ( typeof Box.google[ ELP.lang.standard ] ) {
               ELP.lang.search  =  Box.google[ ELP.lang.standard ];
            } else {
               ELP.lang.search  =  "com";
            }
         } else {
            ELP.config.lookup  =  false;
         }
         if ( ! ELP.config.low ) {
            mw.hook( "wikipage.content" ).add( fire );
         }
      }
   };   // .gui.fire()



   function first() {
      // Initialize sub-module
      // Uses:
      //    >  Sub
      //    facilitated()
      //    .featuring()
      //    (.prego.fire)
      // 2015-10-22 PerfektesChaos@de.wikipedia
      facilitated();
      if ( typeof ELP.featuring  ===  "function" ) {
         ELP.featuring( ELP[ Sub ].fire );
      }
   }   // first()
   first();   // autorun
}( window.mediaWiki, window.jQuery ) );



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

/// EOF </nowiki>   externalLinkProblem/gui/d.js