User:PerfektesChaos/js/listPageOptions/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/listPageOptions/d.js
/// 2019-02-04 PerfektesChaos@de.wikipedia
//  Add specific functionality on watchlists and 'recent changes'
//  ResourceLoader: compatible;
//    dependencies: user, user.options,
//                  mediawiki.api, mediawiki.api.watch,
//                  mediawiki.user, mediawiki.util
//    namespaces:   -1
/// Documentation:  [[w:en:User:PerfektesChaos/js/listPageOptions]]
/// Fingerprint:    #0#0#
/// @license GPL [//www.mediawiki.org/w/COPYING] (+GFDL, LGPL, CC-BY-SA)
/// <nowiki>
/* global window:false, unescape: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  =  -6.2,
       POpt     =  "listPageOptions";
   if ( typeof mw.libs[ POpt ]  !==  "object"   ||
        ! mw.libs[ POpt ] ) {
      mw.libs[ POpt ]  =  { };
   }
   mw.libs[ POpt ].type  =  POpt;
   POpt                  =  mw.libs[ POpt ];



   // Requires: JavaScript  1.3
   //           MediaWiki   1.23            (mw .libs .hook, jQuery core)



   if ( typeof POpt.loaded  !==  "boolean" ) {
      // 2018-08-24
      POpt.loaded        =  true;
      POpt.config        =  { "wikidata.bgc": "AFEEEE" };
      POpt.disguise      =  { };
      POpt.edit          =  { };
      POpt.group         =  { "sign": POpt.type + "_groupPg",
                              "show": "groupie",
                              "soft": "enhanced" };
      POpt.increm        =  { more: 30 };
      POpt.multi         =  { "sign": POpt.type + "_multiPg",
                              "show": "multiple",
                              "soft": "extended" };
      POpt.notice        =  { };
      POpt.picked        =  { "sign": POpt.type + "_visitedPg",
                              "show": "visited",
                              "soft": "visited" };
      POpt.reduce        =  { };
      POpt.storage       =  { };
      POpt.unwatch       =  { };
      POpt.values        =  { };
      POpt.config.texts  =  {
         // 2015-10-11
         "edit":      {"en": "edit",
                       "de": "Bearbeiten"},
         "groupie":   {"en": "Group by pages",
                       "de": "Seitenbezogene\xA0Gruppierung"},
         "hide":      {"en": "Hide",
                       "de": "ausblenden"},
         "multiple":  {"en": "Multiple entries",
                       "de": "Mehrere\xA0Einträge"},
         "notice":    {"en": "+\xA0Visited",
                       "de": "+\xA0Gesehen"},
         "notice?":   {"en": "Add link for [visited] to every entry",
                       "de": "Link zu 'Gesehen'"
                             + " bei jedem Eintrag"},
         "notice!":   {"en": "Mark as visited",
                       "de": "Markiere als 'Gesehen'"},
/*
         "ignore":    {"en": "Ignore",
                       "de": "Ignorieren"},
         "ignored":   {"en": "Unwatched",
                       "de": "Unbeobachtet"},
*/
         "options":   {"en": "Options and notices",
                       "de": "Anzeigeoptionen"},
         "show":      {"en": "Show",
                       "de": "einblenden"},
         "toggleOff": {"en": "off",
                       "de": "aus"},   // abschalten
         "toggleOn":  {"en": "on",
                       "de": "ein"},   // einschalten
         "update":    {"en": "meanwhile",
                       "de": "Zwischenzeitlich"},
         "unwatch":   {"en": "+\xA0[Unwatch]",
                       "de": "+\xA0Nicht-Beobachten"},
         "unwatch?":  {"en": "Add link for [unwatch] to every entry",
                       "de": "Link zu 'Nicht beobachten'"
                             + " bei jedem Eintrag"},
         "visited":   {"en": "Visited\xA0Pages",
                       "de": "Besuchte\xA0Seiten:"},
         "watchPgRE": {"en": "^\\s*([1-9][0-9,]*[^.\n]+[.\n])(.|\n)*$",
                       "de": "^\\s*([1-9][0-9.]*[^.\n]+[.\n])(.|\n)*$"},
         "wlnoteRE":  {"en": "as of(.+,.+)\\.",
                       "de": "Stand:(.+,.+)Uhr\\."},
         "^show":     {"en": "List-Page-Options",
                       "de": "List-Page-Options"},
         "^suffix":   {"en": "&#8211;"
                             + " equip watchlist and recent changes"
                             + " with Options",
                       "de": "&#8211;"
                             + " Beobachtungsliste und Letzte Änderungen"
                             + " mit Optionen ausstatten"},
         "^support":  {"en": "/wiki/"
                             + "User:PerfektesChaos/js/listPageOptions",
                       "de": "/wiki/"
                             + "User:PerfektesChaos/js/listPageOptions"},
         "^^lean-show":
                      {"en": "Compress option block" ,
                       "de": "Komprimierter Optionsblock"},
         "^^learnt-show":
                      {"en": "Enable hiding of visited changes"
                             + " on watchlist",
                       "de": "Möglichkeit zum Verbergen"
                              + " besuchter Änderungen"
                              + " auf der Beobachtungsliste"},
         "^^learnt-null":
                      {"en": "not enabled",
                       "de": "keine Möglichkeit"},
         "^^learnt-false":
                      {"en": "additional button; preset: show all",
                       "de": "Zusatz-Button; Vorgabe: alle anzeigen"},
         "^^learnt-true":
                      {"en": "additional button; preset: hide visited",
                       "de": "Zusatz-Button;"
                             + " Vorgabe: besuchte ausblenden"},
         "^^leyo-show":
                      {"en": "Enable hiding of "
                             + " last-edit-your-own on watchlist",
                       "de": "Möglichkeit zum Verbergen"
                             + " eigener letzter Beiträge"
                             + " auf der Beobachtungsliste"},
         "^^link-show":
                      {"en": "Button to access this option page",
                       "de": "Schaltknopf,"
                             + " um auf diese Optionsseite zu kommen"},
         "^^looked-show":
                      {"en": "Enable entries to be marked as 'visited'"
                             + " on watchlist",
                       "de": "Möglichkeit zum Markieren"
                              + " einzelner Änderungen als 'Gesehen'"
                              + " auf der Beobachtungsliste"},
         "^^looked-null":
                      {"en": "not enabled",
                       "de": "keine Möglichkeit"},
         "^^looked-false":
                      {"en": "on demand",
                       "de": "auf Anforderung"},
         "^^looked-true":
                      {"en": "always",
                       "de": "immer"},
         "^^lower-show":
                      {"en": "Additional buttons:"
                             + " hide and show option block",
                       "de": "Zusätzliche Knöpfe:"
                             + " Ein- und Ausblenden"
                             + " des Optionsblocks"},
         "^^lower-null":
                      {"en": "no additional button",
                       "de": "kein zusätzlicher Knopf"},
         "^^lower-false":
                      {"en": "button for hiding",
                       "de": "Knopf zum Ausblenden"},
         "^^lower-true":
                      {"en": "hide: button for showing",
                       "de": "Ausblenden; Knopf zum Einblenden"},
         "^^luxury-show":
                      {"en": "Additional checkboxes for hide and show",
                       "de": "Zusätzliche Checkboxen"
                             + " zum Ein- und Ausblenden"},
         "^^more-show":
                      {"en": "Overlap seconds for incremental watchlist",
                       "de": "Zeitüberschneidung"
                             + " für inkrementelle Beobachtungsliste"
                             + " in Sekunden"},
         "^^self":    {"en": "Mark own edit",
                       "de": "Eigene Bearbeitung markieren"}
      };   // POpt.texts
      POpt.spooling  =  "https://upload.wikimedia.org/wikipedia/commons"
                        + "/d/de/Ajax-loader.gif";   // throbber URL
   }   // ! .loaded



   function fullstop( activity ) {
      // Stop immediately event propagation
      // Precondition:
      //    activity  -- event
      // 2015-05-12 PerfektesChaos@de.wikipedia
      if ( typeof activity  ===  "object"   &&   activity ) {
         if ( typeof activity.preventDefault  ===  "function" ) {
            activity.preventDefault();
         }
         if ( typeof activity.stopPropagation  ===  "function" ) {
            activity.stopPropagation();
         }
      }
   }   // fullstop()



   POpt.config.feature  =  function ( apply ) {
      // Wrap message text access for user or project language
      // Precondition:
      //    apply   -- text keyword
      // Postcondition:
      //    Return text closest to user language
      // Uses:
      //    this
      //    >  .config.supply
      //    >  .config.texts
      //    >< .config.language
      //    >< .prego
      //    mw.libs.preferencesGadgetOptions.translation()
      // Remark: To be replaced
      //         if one day ResourceLoader3 gives access to
      //         gadget@translatewiki
      // 2015-01-11 PerfektesChaos@de.wikipedia
      var e, r;
      if ( typeof this.language  !==  "boolean" ) {
         if ( typeof POpt.prego  !==  "object" ) {
            if ( typeof mw.libs[ this.supply ]  ===  "object" ) {
               POpt.prego  =  mw.libs[ this.supply ];
            }
         }
         if ( typeof POpt.prego  ===  "object" ) {
            if ( typeof POpt.prego.translation  ===  "function" ) {
               this.language  =
                      ( typeof POpt.prego.translation  ===  "function" );
            }
         } else {
            this.language  =  false;
         }
      }
      e  =  this.texts[ apply ];
      if ( e ) {
         if ( this.language ) {
            r  =  POpt.prego.translation( e );
         } else {
            r  =  e.en;
         }
      } else {
         r  =  "***" + apply + "***";
      }
      return r;
   };   // .config.feature()



   POpt.config.fetch  =  function () {
      // Retrieve preferences and overwrite presets
      // Uses:
      //    >  .prego
      //    >  .type
      //     < .lean
      //     < .learn
      //     < .leyo
      //     < .lower
      //     < .luxury
      //     < .more
      //     < .self
      //    mw.libs.preferencesGadgetOptions.fetch()
      // 2016-05-07 PerfektesChaos@de.wikipedia
      var scope  =  " lean learnt leyo link looked lower luxury"
                    + " more self ",
          vals   =  POpt.prego.fetch( POpt.type ),
          s;
      for ( s in vals ) {
         if ( scope.indexOf( " " + s + " " )  >  0 ) {
            POpt[ s ]  =  vals[ s ];
         }
      }   // for s in vals
   };   // .config.fetch()



   POpt.config.fire  =  function () {
      // Prepare ResourceLoader availability
      // Postcondition:
      //    preferencesGadgetOptions requested, if not yet defined
      // Uses:
      //    this
      //     <  .config.supply
      //    mw.loader.getState()
      //    mw.loader.state()
      //    mw.loader.load()
      // 2018-08-24 PerfektesChaos@de.wikipedia
      var rls, signature;
      this.supply  =  "preferencesGadgetOptions";
      signature    =  "ext.gadget." + this.supply;
      if ( ! mw.loader.getState( signature ) ) {
         rls  =  { };
         rls[ signature ]  =  "loading";
         mw.loader.state( rls );
         mw.loader.load( "https://en.wikipedia.org"
                         + "/w/index.php?title="
                         + "User:PerfektesChaos/js/" + this.supply
                         + "/r.js"
                         + "&action=raw&bcache=1&maxage=604802"
                         + "&ctype=text/javascript",
                         "text/javascript" );
      }
   };   // .config.fire()



   POpt.config.$flow  =  function () {
      // Create throbber
      // Postcondition:
      //    Return jQuery throbber image
      // Uses:
      //    >  .spooling
      //    jQuery()
      // 2015-10-11 PerfektesChaos@de.wikipedia
      var $r  =  $( "<img />" );
      $r.attr( { src:    POpt.spooling,
                 height: "16",
                 alt:    "Ajax" } );
      return $r;
   };   // .config.$flow()



   POpt.config.form  =  function () {
      // Equip Special:Gadgets page with entry and form
      // Uses:
      //    >  .config.texts
      //    >  .type
      //    >  .increm.more
      //    >  .prego
      //    mw.libs.preferencesGadgetOptions.form()
      // 2016-05-07 PerfektesChaos@de.wikipedia
      var txt     =  this.texts,
          dialog  =
         { script:  POpt.type,
           show:    txt[ "^show" ],
           support: txt[ "^support" ],
           suffix:  txt[ "^suffix" ],
           opts:    [ { signature: "lean",
                        type:      "checkbox",
                        show:      txt[ "^^lean-show" ],
                        val:       false
                      },
                      { signature: "learnt",
                        type:      "radio",
                        show:      txt[ "^^learnt-show" ],
                        poly:      [ { val:  null,
                                       show: txt[ "^^learnt-null" ] },
                                     { val:  false,
                                       show: txt[ "^^learnt-false" ] },
                                     { val:  true,
                                       show: txt[ "^^learnt-true" ] }
                                    ] },
                      { signature: "looked",
                        type:      "radio",
                        show:      txt[ "^^looked-show" ],
                        poly:      [ { val:  null,
                                       show: txt[ "^^looked-null" ] },
                                     { val:  false,
                                       show: txt[ "^^looked-false" ] },
                                     { val:  true,
                                       show: txt[ "^^looked-true" ] }
                                    ] },
                      { signature: "leyo",
                        type:      "checkbox",
                        show:      txt[ "^^leyo-show" ],
                        val:       false
                      },
                      { signature: "lower",
                        type:      "radio",
                        show:      txt[ "^^lower-show" ],
                        val:       null,
                        poly:      [ { val:  null,
                                       show: txt[ "^^lower-null" ] },
                                     { val:  false,
                                       show: txt[ "^^lower-false" ] },
                                     { val:  true,
                                       show: txt[ "^^lower-true" ] }
                                   ] },
                      { signature: "luxury",
                        type:      "checkbox",
                        show:      txt[ "^^luxury-show" ],
                        val:       false
                      },
                      { signature: "self",
                        type:      "text",
                        show:      txt[ "^^self" ],
                        val:       false
                      },
                      { signature: "more",
                        type:      "text",
                        show:      txt[ "^^more-show" ],
                        minimum:   4,
                        maxlength: 4,
                        val:       POpt.increm.more
                      },
                      { signature: "link",
                        type:      "checkbox",
                        show:      txt[ "^^link-show" ],
                        val:       false
                      }
                    ] };
      POpt.prego.form( dialog );
   };   // .config.form()



   POpt.disguise.fetch  =  function () {
      // Initialize Array with leading item links
      // Postcondition:
      //    Hidden if matched
      //    Return true for continuation
      // Uses:
      //    this
      //    >  mw.util.$content
      //    >  .picked.learn
      //    >  .letal
      //    >  .self
      //    >  .reLeyo
      //     < .picked.leap
      //     < .disguise.got
      //            Array of Arrays
      //                  [0]  $li  jQuery entry, or $table
      //                  [1]  page URL
      //                  [2]  user URL
      //                  [3]  true: Minor
      //                  [4]  true: unseen
      //                  [5]  comment
      //     < .disguise.list
      //    jQuery.find()
      //    jQuery.eq()
      //    jQuery.children()
      //    jQuery.attr()
      //    jQuery().css()
      // 2015-01-23 PerfektesChaos@de.wikipedia
      var leap   =  false,
          learn  =  false,
          $list  =  mw.util.$content.find( "table.mw-enhanced-rc" ),
          n      =  $list.length,
          seen   =  "mw-changeslist-line-not-watched",   // visited
          e, i, low, staff, story, stuff, $e, $li, $trs, $user;
      POpt.picked.leap  =  false;
      this.got          =  [ ];
      this.list         =  ( n === 0 );
      if ( this.list ) {   // standard
         $e  =  mw.util.$content.find( "#resultListSort" );
         if ( $e.length ) {
            $e  =  $e.find( "ol" );
         } else {
            $e  =  mw.util.$content.find( "UL.special" );
         }
         if ( $e.length ) {
            $list  =  $e.children( "li" );
            n      =  $list.length;
            for ( i = 0;  i < n;  i++ ) {
               $li =  $list.eq( i );
               if ( POpt.picked.learn ) {
                  learn  =  $li.hasClass( seen );
                  if ( learn ) {
                     POpt.picked.leap  =  true;
                  } else {
                     leap  =  true;
                  }
               }
               $e     =  $li.children( "a" ).eq( 0 );
               stuff  =  $e.attr( "href" );
               stuff  =  $li.find( "A.mw-changeslist-title"  )
                            .attr( "href" );
               $user  =  $li.find( "A.mw-userlink" );
               if ( $user.length ) {
                  staff  =  $user.attr( "href" );
               } else {
                  staff  =  false;
               }
               low  =  ( $li.children( ".minoredit" ).length  >  0 );
               if ( POpt.letal ) {
                  story  =  false;
               }
               this.got.push( [ $li, stuff, staff, low, learn, story ] );
            }   // for i
            if ( POpt.mode === 1  &&  POpt.self ) {
               story  =  ".mw-changeslist-separator";
               $e     =  $( "<span>" );
               $e.css(  { "font-weight": "bold" }  );
               $e.text( POpt.self );
               for ( i = 0;  i < n;  i++ ) {
                  e      =  this.got[ i ];
                  staff  =  e[ 2 ];
                  if ( staff ) {
                     if ( this.reLeyo.test( staff ) ) {
                        e[ 0 ].find( story ).after( $e.clone() );
                     }
                  }
               }   // for i
            }
         }
      } else {   // enhanced (grouped)
         staff  =  false;
         for ( i = 0;  i < n;  i++ ) {
            $li  =  $list.eq( i );
            if ( POpt.picked.learn ) {
               learn  =  $li.hasClass( seen );
               if ( learn ) {
                  POpt.picked.leap  =  true;
               } else {
                  leap  =  true;
               }
            }
            $trs   =  $li.children( "tbody" ).children( "tr" );
            stuff  =  $trs.find( "SPAN.mw-title" ).children( "a" )
                                                  .attr( "href" );
            $e     =  $trs.find( "A.mw-userlink" );
            staff  =  false;
            switch ( $e.length ) {
               case 0 :
                  break;
               case 1 :
                  staff  =  $e.attr( "href" );
                  break;
               default :
                  $e  =  $trs.find( ".changedby" )
                             .children( "A.mw-userlink" );
                  if ( $e.length === 1 ) {
                     staff  =  $e.attr( "href" );
                  } else if ( this.reLeyo ) {
                     $e     =  $trs.eq( 1 ).find( "A.mw-userlink" );
                     staff  =  $e.attr( "href" );
                  }
            }   // switch $e.length
            if ( this.reLeyo && staff ) {
               if ( this.reLeyo.test( staff ) ) {
                  $li.hide();
               }
            }
            if ( $li ) {
               low  =  ( $trs.children( ".minoredit" ).length  >  0 );
//             if ( POpt.letal  &&  $trs.length == 2 ) {
//             }
               this.got.push( [ $li, stuff, staff, low, learn, story ] );
            }
         }   // for i
      }
      POpt.picked.leap  =  ( POpt.picked.leap && leap );
   };   // .disguise.fetch()



   POpt.disguise.fiat  =  function ( array, abolish ) {
      // Hiding or show entries
      // Precondition:
      //    array    -- filter condition
      //    abolish  -- true: hide;  false: show again
      // Uses:
      //    this
      //    >  .mode
      //    .disguise.filter()
      // jQuery.isArray()
      // 2015-11-01 PerfektesChaos@de.wikipedia
      var i, k, m, n, q, q3, q4;
      if ( $.isArray( array ) ) {
         n  =  array.length;
         for ( i = 0;  i < n;  i++ ) {
            q  =  array[ i ];
            if ( $.isArray( q ) ) {
               m  =  q.length;
               if ( m > 1 ) {
                  k  =  q[ 2 ];
                  if ( m < 4 ) {
                     q4  =  false;
                     if ( m < 3 ) {
                        q3  =  false;
                     } else {
                        q3  =  q[ 3 ];
                     }
                  } else {
                     q3  =  q[ 3 ];
                     q4  =  q[ 4 ];
                  }
                  if ( ! k  ||  k === POpt.mode ) {
                     if ( this.filter( q[ 0 ],
                                       q[ 1 ],
                                       q3,
                                       q4,
                                       abolish ) ) {
                        break;   // for i
                     }
                  }
               }
            }
         }   // for i
      }
   };   // .disguise.fiat()



   POpt.disguise.filter  =  function ( alter, abandon, account, ant, abolish ) {
      // Hide or show for one entry pattern rule
      // Precondition:
      //    alter    -- special page
      //    abandon  -- namespace page pattern, special page name pattern
      //    account  -- user pattern
      //    ant      -- limit to minor edit
      //    abolish  -- true: hide;  false: show again
      // Postcondition:
      //    Returns true if to be aborted, no elements any more
      //    Elements hidden if matched
      // Uses:
      //    this
      //    >  .picked.learn
      //    >  .picked.leap
      //    >  .disguise.list
      //    >< .disguise.got
      //    >< .disguise.special
      //    .format()
      //    .disguise.format()
      //  mw.config.get()
      //  jQuery.remove()
      // 2015-01-26 PerfektesChaos@de.wikipedia
      var r  =  false,
          i, l, n, q, rep, reu, s;
      if ( alter  ||  abandon  ||  account
           ||  ( POpt.picked.learn && POpt.picked.leap ) ) {
         n  =  this.got.length;
         if ( n ) {
            if ( alter  ||  ! this.list ) {
               rep  =  "/wiki/";
               if ( alter ) {
                  if ( ! this.special ) {
                     q  =  mw.config.get( "wgFormattedNamespaces" );
                     if ( q ) {
                        rep  =  q[ -1 ];
                     }
                     this.special  =  rep + ":";
                  }
                  rep  =  this.special;
               }
            } else {
               rep  =  "/w(iki/|/index\\.php\\?title=)";
            }
            if ( abandon ) {
               s  =  POpt.format( abandon );
            } else {
               s  =  "";
            }
            rep  =  new RegExp( rep + s );
            reu  =  this.format( account );
            r  =  true;
            for ( i = 0;  i < n;  i++ ) {
               q  =  this.got[ i ];
               if ( q ) {
                  l  =  false;
                  if ( rep.test( q[ 1 ] ) ) {
                     l  =  alter;
                     if ( reu ) {
                        l  =  reu.test( q[ 2 ] );
                     }
                     if ( l && ant ) {
                        l  =  q[ 3 ];
                     }
                  }
                  if ( l ) {
                     if ( abolish ) {
                        q[ 0 ].hide();
                     } else {
                        q[ 0 ].show();
                     }
                     this.got[ i ]  =  false;
                  } else {
                     r  =  false;
                  }
               }
            }   // for i
         }
      }
      return r;
   };   // .disguise.filter()



   POpt.disguise.fire  =  function () {
      // Start hiding entries
      // Uses:
      //    this
      //    >  .leyo
      //    >  .self
      //    >  .picked.learn
      //    >  .disguise.hide
      //    >  .disguise.show
      //    >  .hide
      //    >  .show
      //    .disguise.fresh()
      //    .disguise.fetch()
      //    .disguise.fiat()
      // 2015-01-26 PerfektesChaos@de.wikipedia
      if ( POpt.leyo || POpt.self ) {
         this.fresh();
      } else if ( POpt.picked.learn || this.hide || this.show ) {
         this.fetch();
      }
      if ( this.hide ) {
         this.fiat( POpt.hide, true );
      }
      if ( this.show ) {
         this.fiat( POpt.show, false );
      }
   };   // .disguise.fire()



   POpt.disguise.format  =  function ( account ) {
      // Retrieve RegExp for User
      // Precondition:
      //    account  -- user pattern, or false
      // Postcondition:
      //    Returns RegExp, or false
      // Uses:
      //    .format()
      // 2013-04-18 PerfektesChaos@de.wikipedia
      var r;
      if ( account ) {
         r  =  new RegExp( "/w(iki/|/index.php?title=)[^:]+:"
                           +  POpt.format( account ) );
      } else {
         r  =  false;
      }
      return r;
   };   // .disguise.format()



   POpt.disguise.fresh  =  function () {
      // Prepare "last edit your own" mode
      // Uses:
      //    this
      //     < .reLeyo
      //    .disguise.format()
      //    .disguise.fetch()
      // mw.config.get()
      // 2013-04-18 PerfektesChaos@de.wikipedia
      this.reLeyo  =  this.format( mw.config.get( "wgUserName" ) );
      this.fetch();
   };   // .disguise.fresh()



   POpt.edit.fire  =  function () {
      // Equip page with link for editing pages
      // Precondition:
      //    document ready
      // Remark: Used as event handler -- 'this' is not POpt
      // 2018-05-13 PerfektesChaos@de.wikipedia
      var $entries  =  $( ".mw-htmlform-flatlist-item,"   // OBSOLETING
                          + ".oo-ui-fieldLayout-header" );
      $entries.each( POpt.edit.furnish );
   };   // .edit.fire()



   POpt.edit.furnish  =  function ( address, achieve ) {
      // Add page edit link to watchlist edit page entry
      // Precondition:
      //    address  -- number in superset
      //    achieve  -- DOM element of list entry
      // Uses:
      //    jQuery().find()
      //    jQuery().eq()
      //    jQuery().attr()
      //    jQuery().last()
      //    jQuery().insertAfter()append
      //    .config.feature()
      // Remark: Used as $.each() result  -- 'this' is not POpt
      // 2015-03-21 PerfektesChaos@de.wikipedia
      var $entry  =  $( achieve ),
          $links  =  $entry.find( "a" ),
          $a      =  $links.eq( 2 ),
          swift   =  $a.attr( "href" ),
          $end, $span;
      $links.attr(  { target: "_blank" }  );
      if ( swift ) {
         $end   =  $links.last();
         swift  =  swift.replace( /&action=history/, "&action=edit" );
         $a     =  $( "<a>" );
         $a.attr(  { href:   swift,
                     target: "_blank" }  );
         $a.text( POpt.config.feature( "edit" ) );
         $end.after( $a );
         $span  =  $( "<span>" );
         $span.text( " | " );
         $end.after( $span );
      }
   };   // .edit.furnish()



   POpt.group.find  =  function ( assigned ) {
      // Analyze URL and page by grabbing; less items than all?
      // Precondition:
      //    assigned  -- found URL parameter value  "0", "1", null
      // Postcondition:
      //    Initial state of page grouping has been explored
      // Uses:
      //    this
      //    >  mw.util.$content
      //    >  .group.sign
      //    >  .group.leaves
      //    >< .values.ctl2url
      //    jQuery().find()
      //    mw.user.options.get()
      // 2013-02-27 PerfektesChaos@de.wikipedia
      var less   =  false,
          $grab;
      if ( assigned ) {
         less  =  ( assigned === "1" );
      } else {
         $grab  =  mw.util.$content.find( ".mw-enhanced-rc" );
         if ( $grab.length ) {
            less  =  true;
         } else if ( ! this.leaves ) {
            if ( mw.user.options.get( "usenewrc" ) ) {
               less  =  true;
            }
         }
      }
      POpt.values.ctl2url[ this.sign ].val  =  ( less ? "1" : "0" );
   };   // .group.find()



   POpt.increm.fire  =  function () {
      // Incremental page refresh
      // Uses:
      //    >  .increm.retrieved
      //    >  .increm.more
      //    >  .values.script
      //    >  .special
      //    >< .more
      //    >< location
      //     < .values.ctl2url.days
      //    mw.config.get()
      //    .values.factory()
      // Remark: Used as event handler -- 'this' is not POpt.increm
      // 2016-05-09 PerfektesChaos@de.wikipedia
      var date  =  new Date(),
          incr  =  ( date - POpt.increm.retrieved )  /  1000,
          more  =  POpt.increm.more,
          site  =  mw.config.get( "wgServer" );
      if ( typeof POpt.more  === "string"   &&   POpt.more ) {
         POpt.more  =  parseInt( POpt.more, 10 );
      }
      if ( typeof POpt.more  === "number"   &&   POpt.more > more ) {
         more  =  POpt.more;
      }
      incr  +=  more;
      POpt.values.ctl2url.days  =  false;
      if ( site.indexOf( "://" )  <  0 ) {
         site  =  window.location.protocol + site;
      }
      window.location  =  site + POpt.values.script
                          + "?title=Special:" + POpt.special
                          + POpt.values.factory()
                          + "&days="  +  ( incr / 86400 );
   };   // .increm.fire()



   POpt.increm.furnish  =  function ( $around ) {
      // Precondition:
      //    $around  -- options block, or false
      // Postcondition:
      //    Return jQuery 'update' button, or false
      // Uses:
      //    this
      //    >  mw.util.$content
      //    >  .type
      //     < .increm.retrieved
      //    jQuery()
      //    jQuery().find()
      //    jQuery().attr()
      //    jQuery().click()
      //    jQuery().css()
      //    .config.feature()
      //    jQuery().text()
      //    jQuery().before()
      //    (.increm.fire)
      // 2015-01-26 PerfektesChaos@de.wikipedia
      var $r  =  false,
          $box, $e;
      if ( $around ) {
         $box  =  $around;
      } else {
         $box  =  mw.util.$content.find( "#mw-watchlist-options" );
      }
      if ( $box.length ) {
         POpt.increm.retrieved  =  new Date();
         $e  =  $box.find( "br" ).eq( 0 );
         if ( $e.length ) {
            $r  =  $( "<button />" );
            $r.attr(  { id:   POpt.type + "_incrNormal",
                        type: "button" }  )
              .click( this.fire )
              .css(  { "margin-left":  "1em",
                       "margin-right": "1em" }  )
              .text( POpt.config.feature( "update" ) );
            $e.before( $r );
         }
      }
      return $r;
   };   // .increm.furnish()



   POpt.multi.find  =  function ( assigned ) {
      // Analyze URL; less items than all?
      // Precondition:
      //    assigned  -- found URL parameter value  "0", "1", null
      // Postcondition:
      //    Initial state of page has been explored
      // Uses:
      //    this
      //    >  .multi.sign
      //    >< .values.ctl2url
      //    jQuery().find()
      //    mw.user.options.get()
      // 2013-02-27 PerfektesChaos@de.wikipedia
      var less  =  false;
      if ( assigned ) {
         less  =  ( assigned === "1" );
      } else {
         if ( mw.user.options.get( "extendwatchlist" ) ) {
            less  =  true;
         }
      }
      POpt.values.ctl2url[ this.sign ].val  =  ( less ? "1" : "0" );
   };   // .multi.find()



   POpt.notice.factory  =  function () {
      // Equip one entry with a [visited] link
      // Postcondition:
      //    Return true -- continue $.each()
      // Uses:
      //    >  .unwatch.less
      //    jQuery()
      //    jQuery().parent()
      //    jQuery().attr()
      //    jQuery().find()
      //    jQuery().each()
      //    (.notice.feed)
      // Remark: Used as jQuery handler -- 'this' is DOM element
      // 2015-10-11 PerfektesChaos@de.wikipedia
      var $e  =  $( this ),
          s;
      if ( POpt.unwatch.less ) {   // enhanced
         $e  =  $e.parent();
      } else {   // standard
         s  =  $e.attr( "class" );
         if ( s ) {
            if ( s.indexOf( "mw-changeslist-line-" )  <  0 ) {
               $e  =  false;
            }
         } else {
            $e  =  false;
         }
      }
      if ( $e ) {
         $e.find( "a" ).each( POpt.notice.feed );
      }
      return true;
   };   // .notice.factory()



   POpt.notice.feed  =  function () {
      // Insert [unwatch] link after history link
      // Postcondition:
      //    Return true or false -- continue or break $.each()
      // Uses:
      //    jQuery()
      //    jQuery().attr()
      //    jQuery().text()
      //    jQuery().click()
      //    jQuery().css()
      //    jQuery().data()
      //    jQuery().append()
      //    jQuery().after()
      //    (POpt.notice.fire)
      // Remark: Used as jQuery handler -- 'this' is DOM element
      // 2015-10-11 PerfektesChaos@de.wikipedia
      var loop  =  true,
          $e    =  $( this ),
          s     =  $e.attr( "href" ),
          g, id, $btn, $span;
      if ( s   &&   s.indexOf( "&action=history" )  >  0 ) {
         g  =  /[?&]title=([^&]+)(&.+)*&curid=([0-9]+)(&.+)?$/.exec( s );
         if ( g ) {
            s      =  g[ 1 ];
            id     =  g[ 3 ];
            $btn  =  $( "<button />" );
            $btn.attr( { type:  "button",
                         title: POpt.config.feature( "notice!" ) } )
                .click( POpt.notice.fire )
                .css(  { "color":         "#006000",
                         "font-size":     "85%",
                         "font-weight":   "bold",
                         "padding-left":  "0",
                         "padding-right": "0" }  )
                .data(  { curid: id,
                          title: s }  )
                .text( "+" );
            $span  =  $( "<span>" );
            $span.attr(  { "class": "curid" + id } )
                 .text( " | " )
                 .append( $btn );
            $e.after( $span );
            loop  =  false;
         }
      }
      return loop;
   };   // .notice.feed()



   POpt.notice.fiat  =  function () {
      // Provide link(s) for visited page
      // Precondition:
      //    Watchlist
      // Uses:
      //    this
      //     < .notice.$portlet
      //    .config.feature()
      //    mw.util.addPortletLink()
      //    (.notice.furnish)
      // 2015-10-11 PerfektesChaos@de.wikipedia
      this.$portlet  =
             $( mw.util.addPortletLink( "p-tb",
                                        "#",
                                        POpt.config.feature( "notice" ),
                                        "t-mark-visited",
                                        POpt.config.feature( "notice?" ),
                                        "v",
                                        null ) );
      this.$portlet.click( this.furnish );
   };   // .notice.fiat()



   POpt.notice.fine  =  function ( arrived, $apply ) {
      // Normalize entry after successful [visited]
      // Precondition:
      //    arrived  -- JSON info of ajax query
      //    $apply   -- current set of throbber element containers
      // Uses:
      //    jQuery().children()
      //    jQuery().each()
      //    (.notice.fix)
      // Remark: Used as jQuery handler -- 'this' is not POpt.notice
      // 2015-10-11 PerfektesChaos@de.wikipedia
      if ( typeof arrived  ===  "object"   &&
           typeof arrived.invalid  ===  "undefined" ) {
         $apply.children( "img" ).remove();
         $apply.each( POpt.notice.fix );
      } else {
         POpt.fault( "notice", arrived, $apply );
      }
   };   // .notice.fine()



   POpt.notice.fire  =  function () {
      // Perform visited action
      // Uses:
      //    this
      //    >  mw.util.$content
      //    >< .notice.api
      //    jQuery().find()
      //    jQuery().children()
      //    jQuery().hide()
      //    .config.$flow()
      //    jQuery().append()
      //    mw.Api()
      //    mw.Api().postWithToken()
      //    (.notice.fine)
      //    (.fault)
      // Remark: Used as jQuery handler -- 'this' is DOM element
      // 2015-10-11 PerfektesChaos@de.wikipedia
      var $g  =  $( this ),
          id  =  $g.data( "curid" ),
          $e  =  mw.util.$content.find( ".curid" + id ),
          defaults, promise, s;
      if ( $e.length ) {
         $e.children( "button" ).hide();
         $e.append( POpt.config.$flow() );
         if ( typeof POpt.notice.api  !==  "object" ) {
            defaults         =  { parameters:
                                   { action: "setnotificationtimestamp" }
                                };
            POpt.notice.api  =  new mw.Api( defaults );
         }
         s        =  unescape( decodeURI( $g.data( "title" ) ) );
         promise  =  POpt.notice.api.postWithToken( "edit",
                                                    { titles: s } );
         promise.done( function ( arrived ) {
                          POpt.notice.fine( arrived, $e );
                       } )
                .fail( function ( arrived ) {
                          POpt.fault( "notice", arrived, $e );
                       } );
      }
   };   // .notice.fire()



   POpt.notice.first  =  function () {
      // Provide link(s) for visited page
      // Precondition:
      //    Watchlist
      // Uses:
      //    this
      //    >  .looked
      //     < .notice.$unvisiteds
      //     < .notice.$portlet
      //    .notice.furnish()
      //    .notice.fiat()
      // 2015-10-11 PerfektesChaos@de.wikipedia
      if ( typeof POpt.looked  ===  "boolean" ) {
         this.$unvisiteds  =  $( ".mw-changeslist-line-watched" );
         if ( this.$unvisiteds.length ) {
            if ( POpt.looked ) {
               this.$portlet  =  false;
               this.furnish();
            } else {
               this.fiat();
            }
         }
      }
   };   // .notice.first()



   POpt.notice.fix  =  function () {
      // Turn one entry into visited and remove throbber
      // Postcondition:
      //    Return true -- continue $.each()
      // Uses:
      //    jQuery().attr()
      // Remark: Used as jQuery handler -- 'this' is DOM element
      // 2015-10-11 PerfektesChaos@de.wikipedia
      var $g     =  $( this ).parent(),
          state  =  $g.attr( "class" )
                      .replace( /\bmw-changeslist-line-watched\b/,
                                "mw-changeslist-line-not-watched" );
      $g.attr( "class", state );
   };   // .notice.fix()



   POpt.notice.furnish  =  function ( activity ) {
      // Equip all entries with an [unwatch] link
      // Precondition:
      //    activity  -- event
      //    Current page is watchlist
      // Uses:
      //    >  .$unvisiteds
      //    >  .$portlet
      //    fullstop()
      //    jQuery().each()
      //    jQuery().remove()
      //    (.notice.factory)
      // Remark: Used as event handler -- 'this' is not POpt.notice
      // 2016-11-11 PerfektesChaos@de.wikipedia
      fullstop( activity );
      POpt.notice.$unvisiteds.each( POpt.notice.factory );
      if ( POpt.notice.$portlet ) {
         POpt.notice.$portlet.remove();
      }
   };   // .notice.furnish()



   POpt.picked.$factory  =  function () {
      // Create toggle for visited
      // Postcondition:
      //    Return toggle text
      // Uses:
      //    this
      //    >  .picked.leap
      //    >  .type
      //    >  .values.$POpt
      //    >  .picked.learning
      //    >< .picked.$toggle
      //    jQuery()
      //    jQuery().attr()
      //    jQuery().click()
      //    jQuery().css()
      //    jQuery().append()
      //    .config.feature()
      //    jQuery().text()
      //    (.picked.fire)
      // 2015-01-11 PerfektesChaos@de.wikipedia
      var s;
      if ( this.leap ) {
         if ( typeof this.$toggle  !==  "object" ) {
            this.$toggle  =  $( "<button />" );
            this.$toggle.attr(  { id:   POpt.type + "_visited",
                                  type: "button" }  );
            this.$toggle.click( this.fire );
            this.$toggle.css( { "margin-left":  "0.5em",
                                "margin-right": "0.5em" } );
            POpt.values.$POpt.append( this.$toggle );
         }
         s  =  ( this.learning ? "hide" : "show" );
         this.$toggle.text( POpt.config.feature( s ) );
      }
   };   // .picked.$factory()



   POpt.picked.fire  =  function () {
      // Execute visited toggling
      // Uses:
      //    this
      //    >  .picked.leap
      //    >< .picked.learning
      //    >< .disguise.got
      //    jQuery().hide()
      //    jQuery().show()
      //    .storage.flush()
      //    .picked.$factory()
      // Remark: Used as event handler -- 'this' is not POpt.reduce
      // 2015-01-26 PerfektesChaos@de.wikipedia
      var less  =  ( POpt.picked.learning && POpt.picked.leap ),
          n, i, q;
      n  =  POpt.disguise.got.length;
      for ( i = 0;  i < n;  i++ ) {
         q  =  POpt.disguise.got[ i ];
         if ( q[ 4 ] ) {
            if ( less ) {
               q[ 0 ].hide();
            } else {
               q[ 0 ].show();
            }
         }
      }   // for i
      POpt.storage.flush( "learnt", POpt.picked.learning );
      POpt.picked.learning  =  ! POpt.picked.learning;
      POpt.picked.$factory();
   };   // .picked.fire()



   POpt.picked.furnish  =  function () {
      // Equip page with link for visited pages
      // Uses:
      //    this
      //    >  .values.$form
      //    >  .learnt
      //    >  .picked.show
      //    >  .picked.leap
      //    >  .lean
      //    >  .values.$POpt
      //     < .picked.learning
      //     < .picked.$toggle
      //    .storage.find()
      //    .storage.flush()
      //    jQuery()
      //    jQuery().text()
      //    jQuery().append()
      //    .config.feature()
      //    jQuery().css()
      //    .picked.fire()
      // 2015-01-11 PerfektesChaos@de.wikipedia
      var s, $span;
      if ( POpt.values.$form ) {
         s  =  POpt.storage.find( "learnt" );
         if ( s ) {
            this.learning  =  ( s === "true" );
         } else {
            this.learning  =  POpt.learnt;
            POpt.storage.flush( "learnt", this.learning );
         }
         $span  =  $( "<span>" );
         $span.text( "\xA0| " );
         POpt.values.$POpt.append( $span );
         $span  =  $( "<span>" );
         $span.text( POpt.config.feature( this.show ) );
         POpt.values.$POpt.append( $span );
         if ( ! this.leap ) {
            $span  =  $( "<span>" );
            $span.css(  { "color": "#B0B0B0" }  );
            $span.text( " ./." );
            POpt.values.$POpt.append( $span );
            if ( POpt.mode === 2 ) {   // should be excluded anyway
               this.learning  =  false;
            }
         }
         if ( POpt.mode === 2  &&  ! POpt.lean ) {
            POpt.values.$POpt.append( $( "<hr />" ) );
         }
         this.fire();
      }
   };   // .picked.furnish()



   POpt.reduce.facility  =  function () {
      // Make options and notices visible
      // Uses:
      //    >  .reduce.$box
      //    >  .reduce.$resuscitate
      //     < .reduce.lower
      //    jQuery().show()
      //    jQuery().hide()
      //    .storage.flush()
      // Remark: Used as event handler -- 'this' is not POpt.reduce
      // 2015-01-11 PerfektesChaos@de.wikipedia
      POpt.reduce.$box.show();
      POpt.reduce.$resuscitate.hide();
      POpt.reduce.lower  =  false;
      POpt.storage.flush( "lower", POpt.reduce.lower );
   };   // .picked.facility()



   POpt.reduce.fade  =  function () {
      // Hide options and notices
      // Uses:
      //    >  .reduce.$resuscitate
      //    >  .reduce.$box
      //     < .reduce.lower
      //    jQuery().css()
      //    jQuery().show()
      //    jQuery().hide()
      // Remark: Used as event handler -- 'this' is not POpt.reduce
      // 2015-01-11 PerfektesChaos@de.wikipedia
      POpt.reduce.$resuscitate.css(  { "margin-top": "10px" }  );
      POpt.reduce.$resuscitate.show();
      POpt.reduce.$box.hide();
      POpt.reduce.lower  =  true;
      POpt.storage.flush( "lower", POpt.reduce.lower );
   };   // .picked.fade()



   POpt.reduce.furnish  =  function () {
      // Equip options area with collapsible handle and prego button
      // Uses:
      //    this
      //    >  .type
      //    >  .lower
      //    >  mw.util.$content
      //    >  .mode
      //    >  .prego
      //    >  .link
      //    >< .$options
      //     < .reduce.lower
      //     < .reduce.$box
      //     < .reduce.stamp
      //     < .reduce.$resuscitate
      //     < .reduce.$vanish
      //    .storage.find()
      //    .storage.flush()
      //    jQuery().find()
      //    .config.feature()
      //    jQuery()
      //    jQuery().attr()
      //    jQuery().css()
      //    jQuery().text()
      //    jQuery().append()
      //    .increm.furnish
      //    jQuery().clone()
      //    jQuery().click()
      //    jQuery().after()
      //    jQuery().before()
      //    .reduce.fade()
      //    .reduce.facility()
      //    .prego.$button()
      //    (.increm.fire)
      //    (.reduce.facility)
      //    (.reduce.fade)
      // 2015-10-11 PerfektesChaos@de.wikipedia
      var s  =  POpt.storage.find( "lower" ),
          btnR, got, re, $btn, $div, $span;
      if ( s ) {
         this.lower  =  ( s === "true" );
      } else {
         this.lower  =  POpt.lower;
         POpt.storage.flush( "lower", this.lower );
      }
      s          =  ( POpt.mode === 1  ?  "#mw-watchlist-options"
                                       :  ".rcoptions" );
      this.$box  =  mw.util.$content.find( s );
      if ( this.$box.length ) {
         btnR  =  { "float":  ( POpt.ltr ? "right" : "left " ) };
         btnR[ ( POpt.ltr ? "margin-left" : "margin-right" ) ]  =  "8px";
         btnR[ ( POpt.ltr ? "margin-right" : "margin-left" ) ]  =  "0";
         this.$resuscitate  =  $( "<div>" );
         this.$resuscitate.attr(  { id:  POpt.type + "_resuscitate" }  );
         this.$resuscitate.css(  { "margin-top": "10px" }  );
         if ( POpt.mode === 1 ) {
            s  =  this.$box.text();
            if ( s ) {
               re   =  new RegExp( POpt.config.feature( "wlnoteRE" ) );
               got  =  re.exec( s );
               if ( got ) {
                  $span  =  $( "<span>" );
                  $span.text( got[ 1 ] );
                  this.$resuscitate.append( $span );
                  $btn  =  POpt.increm.furnish( this.$box );
                  if ( $btn ) {
                     $btn  =  $btn.clone();
                     $btn.attr(  { id:  POpt.type + "_incrShrinked" }  );
                     $btn.click( POpt.increm.fire );
                     this.$resuscitate.append( $btn );
                  }
               }
            }
         }
         if ( ! POpt.$options ) {
            POpt.$options  =  this.$box.find( "legend" );
         }
         if ( POpt.link && POpt.prego ) {
            $btn  =  POpt.prego.$button( POpt.type );
            $btn.attr(  { id:  POpt.type + "_optPage" }  );
            $btn.css( btnR );
            $btn.css(  { "margin-top": "20px" }  );
            POpt.$options.after( $btn );
            $div  =  $( "<div>" );
            $div.css(  { "clear": "right" }  );
            POpt.$options.after( $div );
         }
         $btn  =  $( "<button />" );
         $btn.attr(  { id:   POpt.type + "_optShow",
                       type: "button" }  );
         $btn.click( POpt.reduce.facility );
         $btn.css( ( POpt.ltr ? "margin-left" : "margin-right" ),
                   "10px" );
         $btn.text( POpt.config.feature( "options" ) );
         this.$resuscitate.append( $btn );
         this.$box.before( this.$resuscitate );
         $btn  =  $( "<button />" );
         $btn.attr( { id:   POpt.type + "_optHide",
                      type: "button" } );
         $btn.click( POpt.reduce.fade );
         $btn.css( btnR );
         $span  =  $( "<span>" );
         $span.css( { "color":       "#FF0000",
                      "font-weight": "bold" } );
         $span.text( "X" );
         $btn.append( $span );
         POpt.$options.after( $btn );
         if ( this.lower ) {
            this.fade();
         } else {
            this.facility();
         }
      }
   };   // .reduce.furnish()



   POpt.storage.find  =  function ( access ) {
      // Retrieve volatile configuration from session storage
      // Precondition:
      //    access  -- key
      // Postcondition:
      //    Return value string, or false
      // Uses:
      //    this
      //    sessionStorage
      //    >  .type
      //    >< .stored
      // 2013-09-06 PerfektesChaos@de.wikipedia
      var r  =  false,
          s  =  "~" + access + "_",
          i, j;
      if ( ! this.stored ) {
         if ( window.sessionStorage ) {
            this.stored  =  window.sessionStorage.getItem( POpt.type );
         }
         if ( ! this.stored ) {
            this.stored  =  "~";
         }
      }
      i  =  this.stored.indexOf( s );
      if ( i >= 0 ) {
         j  =  this.stored.indexOf( "~",  i + 2 );
         if ( j > 0 ) {
            r  =  this.stored.substring( i + s.length,  j );
         }
      }
      return r;
   };   // .storage.find()



   POpt.storage.flush  =  function ( access, assign ) {
      // Remember volatile configuration in session storage
      // Precondition:
      //    access  -- key
      //    assign  -- value
      // Uses:
      //    this
      //    >  .type
      //    >< .stored
      // 2013-09-06 PerfektesChaos@de.wikipedia
      var s  =  "~" + access + "_",
          i, j;
      if ( ! this.stored ) {
         this.stored  =  "~";
      }
      i  =  this.stored.indexOf( s );
      s  =  s + assign;
      if ( i < 0 ) {
         this.stored  =  s + this.stored;
      } else {
         j  =  this.stored.indexOf( "~",  i + 2 );
         if ( j < 0 ) {
            this.stored  =  s + "~";
         } else {
            this.stored  =  this.stored.substr( 0, i )
                            +  s
                            +  this.stored.substr( j );
         }
      }
      if ( window.sessionStorage ) {
         window.sessionStorage.setItem( POpt.type, this.stored );
      }
   };   // .storage.flush()



   POpt.unwatch.factory  =  function () {
      // Equip one entry with an [unwatch] link, if appropriate
      // Postcondition:
      //    Return true -- continue $.each()
      // Uses:
      //    >  .unwatch.less
      //    jQuery()
      //    jQuery().parent()
      //    jQuery().attr()
      //    jQuery().find()
      //    jQuery().each()
      //    (.unwatch.feed)
      // Remark: Used as jQuery handler -- 'this' is DOM element
      // 2013-02-08 PerfektesChaos@de.wikipedia
      var $e  =  $( this ),
          s;
      if ( POpt.unwatch.less ) {   // enhanced
         $e  =  $e.parent();
      } else {   // standard
         s  =  $e.attr( "class" );
         if ( s ) {
            if ( s.indexOf( "mw-changeslist-line-" )  <  0 ) {
               $e  =  false;
            }
         } else {
            $e  =  false;
         }
      }
      if ( $e ) {
         $e.find( "a" ).each( POpt.unwatch.feed );
      }
      return true;
   };   // .unwatch.factory()



   POpt.unwatch.feed  =  function () {
      // Insert [unwatch] link after history link
      // Postcondition:
      //    Return true or false -- continue or break $.each()
      // Uses:
      //    jQuery()
      //    jQuery().attr()
      //    jQuery().text()
      //    jQuery().click()
      //    jQuery().css()
      //    jQuery().data()
      //    .unwatch.flag()
      //    jQuery().append()
      //    jQuery().after()
      //    (POpt.unwatch.fire)
      // Remark: Used as jQuery handler -- 'this' is DOM element
      // 2015-01-11 PerfektesChaos@de.wikipedia
      var loop  =  true,
          $e    =  $( this ),
          s     =  $e.attr( "href" ),
          g, id, $btn, $span;
      if ( s   &&   s.indexOf( "&action=history" )  >  0 ) {
         g  =  /[?&]title=([^&]+)(&.+)*&curid=([0-9]+)(&.+)?$/.exec( s );
         if ( g ) {
            s      =  g[ 1 ];
            id     =  g[ 3 ];
            $btn  =  $( "<button />" );
            $btn.attr( { type: "button" } )
                .click( POpt.unwatch.fire )
                .css(  { "padding-left":  "0",
                         "padding-right": "0" }  )
                .data(  { curid: id,
                          leave: true,
                          title: s }  )
                .text( POpt.unwatch.flag( false ) );
            $span  =  $( "<span>" );
            $span.attr(  { "class": "curid" + id } )
                 .text( " | " )
                 .append( $btn );
            $e.after( $span );
            loop  =  false;
         }
      }
      return loop;
   };   // .unwatch.feed()



   POpt.unwatch.fiat  =  function () {
      // Equip watchlist toolbox with link to [unwatch]
      // Precondition:
      //    watchlist has been loaded
      // Uses:
      //    this
      //     < .unwatch.$portlet
      //    .config.feature()
      //    mw.util.addPortletLink()
      //    (POpt.unwatch.furnish)
      // 2015-10-11 PerfektesChaos@de.wikipedia
      this.$portlet  =
            $( mw.util.addPortletLink( "p-tb",
                                       "#",
                                       POpt.config.feature( "unwatch" ),
                                       "t-unwatch",
                                       POpt.config.feature( "unwatch?" ),
                                       "w",
                                       null ) );
      this.$portlet.click( POpt.unwatch.furnish );
   };   // .unwatch.fiat()



   POpt.unwatch.fine  =  function ( arrived, $apply ) {
      // Insert [watch again] link after successful unwatch
      // Precondition:
      //    arrived  -- JSON info of ajax query
      //    $apply   -- current set of throbber element containers
      // Uses:
      //    jQuery().children()
      //    jQuery().remove()
      //    jQuery().each()
      //    .fault()
      //    (.unwatch.fix)
      // Remark: Used as event handler -- 'this' is not POpt.unwatch
      // 2015-10-11 PerfektesChaos@de.wikipedia
      if ( typeof arrived  ===  "object"   &&
           typeof arrived.invalid  ===  "undefined" ) {
         $apply.children( "img" ).remove();
         $apply.each( POpt.unwatch.fix );
      } else {
         POpt.fault( "unwatch", arrived, $apply );
      }
   };   // .unwatch.fine()



   POpt.unwatch.fire  =  function () {
      // Perform unwatch action, or undo this
      // Uses:
      //    this
      //    >  mw.util.$content
      //    >< .unwatch.api
      //    jQuery().find()
      //    .config.$flow()
      //    mw.Api()
      //    mw.Api().unwatch()
      //    mw.Api().watch()
      //    jQuery().children()
      //    jQuery().hide()
      //    jQuery().append()
      //    (.unwatch.fine)
      //    (.fault)
      // Remark: Used as jQuery handler -- 'this' is DOM element
      // 2016-11-11 PerfektesChaos@de.wikipedia
      var $g  =  $( this ),
          id  =  $g.data( "curid" ),
          $e  =  mw.util.$content.find( ".curid" + id ),
          promise, subject, submit;
      if ( $e.length ) {
         $e.children( "button" ).hide();
         $e.append( POpt.config.$flow() );
         subject  =  unescape( decodeURI( $g.data( "title" ) ) );
         submit   =  ( $g.data( "leave" )  ?  "unwatch"  :  "watch" );
         if ( typeof this.api  !==  "object" ) {
            this.api  =  new mw.Api();
         }
         promise  =  this.api[ submit ]( subject );
         promise.done( function ( arrived ) {
                          POpt.unwatch.fine( arrived, $e );
                       } )
                .fail( function ( arrived ) {
                          POpt.fault( "unwatch", arrived, $e );
                       } );
      }
   };   // .unwatch.fire()



   POpt.unwatch.fix  =  function () {
      // Turn [unwatch] link into [watch again]
      // Uses:
      //    jQuery()
      //    jQuery().children()
      //    jQuery().text()
      //    jQuery().data()
      //    .unwatch.flag()
      //    jQuery().show()
      // Remark: Used as event handler -- 'this' is DOM element
      // 2015-01-11 PerfektesChaos@de.wikipedia
      var $e    =  $( this ),
          $btn  =  $e.children( "button" ),
          leave;
      if ( $btn.length ) {
         leave  =  $btn.data( "leave" );
         $btn.css(  { "background-color":  ( leave ? "#FFB0B0"
                                                   : "#B0B0FF" ),
                      "padding-left":  "5px",
                      "padding-right": "5px" }  );
         $btn.data(  { leave:  ! leave }  );
         $btn.text( POpt.unwatch.flag( leave ) );
         $btn.show();
      }
   };   // .unwatch.fix()



   POpt.unwatch.flag  =  function ( again ) {
      // Retrieve [unwatch] link title, or [watch again]
      // Precondition:
      //    again  -- true iff [watch]
      // Postcondition:
      //    Return string with [unwatch] link title
      // 2015-01-11 PerfektesChaos@de.wikipedia
      return  ( again  ?  "/////"  :  String.fromCharCode( 8212 ) );
   };   // .unwatch.flag()



   POpt.unwatch.furnish  =  function () {
      // Equip all entries with an [unwatch] link
      // Precondition:
      //    Current page is watchlist
      // Uses:
      //    >  mw.util.$content
      //    >  .group.sign
      //    >  .values.ctl2url
      //    >  .unwatch.$portlet
      //     < .unwatch.less
      //    jQuery().find()
      //    jQuery().each()
      //    jQuery().remove()
      //    (.unwatch.factory)
      // Remark: Used as event handler -- 'this' is not POpt.unwatch
      // 2015-10-11 PerfektesChaos@de.wikipedia
      var $list  =  mw.util.$content.find( "#mw-content-text" ),
          ctl, seek;
      if ( $list.length ) {
         ctl                =  POpt.values.ctl2url[ POpt.group.sign ];
         POpt.unwatch.less  =  ctl.val;
         if ( POpt.unwatch.less === "0" ) {
            POpt.unwatch.less  =  false;
         }
         seek  =  ( POpt.unwatch.less  ?  ".mw-title"  :  "li" );
         $list.find( seek ).each( POpt.unwatch.factory );
      }
      POpt.unwatch.$portlet.remove();
   };   // .unwatch.furnish()



   POpt.values.factory  =  function () {
      // Make URL from .ctl2url
      // Postcondition:
      //    Return query part of URL
      // Uses:
      //    this
      //    >  .values.ctl2url
      //    .values.fiat()
      // 2013-02-08 PerfektesChaos@de.wikipedia
      var r  =  "",
          s;
      for ( s in this.ctl2url ) {
         r  =  r  +  this.fiat( s );
      }   // for s in .ctl2url
      return r;
   };   // .values.factory()



   POpt.values.fiat  =  function ( access ) {
      // Make URL parameter assignment from '&' until value
      // Precondition:
      //    access  -- identifier in .ctl2url
      // Postcondition:
      //    Return assignment
      // Uses:
      //    this
      //    >  .values.ctl2url
      // 2013-02-06 PerfektesChaos@de.wikipedia
      var par  =  this.ctl2url[ access ],
          r    =  "",
          s    =  access;
      if ( par ) {
         if ( par.val !== undefined ) {
            if ( par.url ) {
               s  =  par.url;
            }
            if ( par.type === "checkbox" ) {
               if ( typeof par.val  !==  "string" ) {
                  par.val  = ( par.val ? "1" : "0" );
               }
            }
            r  =  "&" + s + "=" + par.val;
         }
      }
      return r;
   };   // .values.fiat()



   POpt.values.find  =  function ( around, adapt ) {
      // Initialize form and .values fields with hidden input
      // Precondition:
      //    around  -- identification of wrapping element
      //    adapt   -- identification of form
      // Uses:
      //    this
      //    >  mw.util.$content
      //    >  .lean
      //    >  .type
      //     < .values.$wrap
      //     < .values.$form
      //     < .values.$POpt
      //    .values.first()
      //    jQuery().find()
      //    jQuery().each()
      //    jQuery().append()
      //    jQuery().attr()
      //    (.values.found)
      // 2015-01-11 PerfektesChaos@de.wikipedia
      var $input, $sep;
      this.first();
      this.$wrap  =  mw.util.$content.find( around );
      if ( this.$wrap.length ) {
         this.$form  =  mw.util.$content.find( adapt );
         if ( this.$form.length ) {
            $input  =  this.$form.find( "input" );
            $input.each( this.found );
         }
         if ( POpt.lean ) {
            $sep  =  $( "<span>" );
            $sep.text( "\xA0| " );
            this.$wrap.append( $sep );
            this.$POpt  =  $( "<span>" );
         } else {
            this.$POpt  =  $( "<div>" );
         }
         this.$POpt.attr(  { id:  POpt.type + "_extra" }  );
         this.$wrap.append( this.$POpt );
      } else {
         this.$form  =  false;
         this.$wrap  =  false;
         this.$POpt  =  false;
      }
   };   // .values.find()



   POpt.values.first  =  function () {
      // Initialize .values component for this application
      // Uses:
      //    this
      //    >  .group.soft
      //    >  .group.sign
      //    >  .multi.soft
      //    >  .multi.sign
      //    >  .mode
      //    >< .values.ctl2url
      // 2013-03-12 PerfektesChaos@de.wikipedia
      if ( ! this.ctl2url ) {
         this.ctl2url  =  { "associated":      { type: "checkbox" },
                            "days":            { name: "days",
                                                 type: false },
                        //  enhanced...      Bugzilla:51901, Gerrit:75579
                            "hideAnons":       { type: "checkbox" },
                            "hideBots":        { type: "checkbox" },
                            "hideLiu":         { type: "checkbox" },
                            "hideMinor":       { type: "checkbox" },
                            "hideOwn":         { type: "checkbox" },
                            "hideReviewed":    { type: "checkbox" },
                            "hideWikibase":    { type: "checkbox" },
                                                 // #wb-toggle
                            "nsinvert":        { type: "checkbox",
                                                 url:  "invert" },
                            "namespace":       { type: "select" }
                          };
         this.ctl2url[ POpt.group.sign ]   =  { type: "checkbox",
                                                url:  POpt.group.soft };
         this.ctl2url[ POpt.multi.sign ]   =  { type: "checkbox",
                                                url:  POpt.multi.soft };
         if ( POpt.mode === 2 ) {
            this.ctl2url.from       =  { type: false };
            this.ctl2url.limit      =  { type: false };
            this.ctl2url.tagfilter  =  { type: "text" };
         }
      }
   };   // .values.first()



   POpt.values.fix  =  function ( access ) {
      // Adapt URLs of option links to current state
      // Precondition:
      //    access  -- identifier in .ctl2url
      // Uses:
      //    this
      //    >  .values.$wrap
      //    >< .values.$hrefs
      //    >< .values.ctl2url
      //     < .values.current
      //    jQuery().find()
      //    jQuery().each()
      //    (.values.fixing)
      // 2019-02-04 PerfektesChaos@de.wikipedia
      if ( ! this.$hrefs ) {
         if ( this.$wrap ) {
            this.$hrefs  =  this.$wrap.find( "a" );
         } else {
            this.$hrefs  =  true;
         }
      }
      if ( typeof this.$hrefs  !==  "boolean" ) {
         this.current  =  this.ctl2url[ access ];
         if ( ! this.current.url ) {
            this.current.url  =  access;
         }
         this.$hrefs.each( this.fixing );
      }
   };   // .values.fix()



   POpt.values.fixing  =  function ( at, access ) {
      // Adapt URL to current state of a control
      // Precondition:
      //    at      -- index of element in $.each() collection
      //    access  -- DOM element
      // Postcondition:
      //    Return true -- continue $.each()
      // Uses:
      //    >  .values.current
      //    >  .values.ctl2url
      //    >< .values.script
      //    >< .values.reURL
      //    mw.config.get()
      //    jQuery().attr()
      // Remark: Used as jQuery handler -- 'this' is DOM element
      // 2016-10-16 PerfektesChaos@de.wikipedia
      var got, par, swap, $e;
      if ( at < 0 ) {
         $e  =  access;
      } else {
         $e  =  $( access );
      }
      swap  =  $e.attr( "href" );
      if ( swap ) {
         if ( ! POpt.values.script ) {
            POpt.values.script  =  mw.config.get( "wgScript" );
         }
         if ( swap.indexOf( POpt.values.script )  <  0 ) {
            swap  =  false;
         }
         if ( swap ) {
            par  =  POpt.values.current;
            if ( par.val !== undefined ) {
               if ( ! par.reURL ) {
                  par.reURL  =    "([?&]" + par.url + "=)"
                                + "(?:[^&]*)"
                                + "(&?.+)?$";
                  par.reURL  =  new RegExp( par.reURL );
               }
               if ( par.type === "checkbox" ) {
                  if ( typeof par.val  !==  "string" ) {
                     par.val  = ( par.val ? "1" : "0" );
                  }
               }
               got  =  par.reURL.exec( swap );
               if ( got ) {
                  swap  =  swap.replace( par.reURL,
                                         "$1" + par.val + "$2" );
               } else {
                  swap  =  swap + "&" + par.url + "=" + par.val;
               }
               $e.attr(  { href: swap }  );
            }
         }
      }
      return true;
   };   // .values.fixing()



   POpt.values.flat  =  function () {
      // Compress options by removing line breaks and lines
      // Uses:
      //    this
      //    >  .values.$wrap
      //    jQuery().find()
      //    jQuery().before()
      //    jQuery().text()
      //    jQuery().remove()
      //    jQuery().show()
      // 2015-01-11 PerfektesChaos@de.wikipedia
      var $e    =  this.$wrap.find( "br, hr" ),
          $sep  =  $( "<span>" );
      $sep.text( " | " );
      $e.before( $sep.clone() );
      $e.remove();
      this.$wrap.find( "p, form, ul" ).show();
   };   // .values.flat()



   POpt.values.flip  =  function ( access ) {
      // Value of a checkbox changed
      // Precondition:
      //    access  -- identifier in .ctl2url
      // Uses:
      //    this
      //    >  .values.$form
      //    >  .mode
      //    >  .group.sign
      //    >  .multi.sign
      //    >< .values.ctl2url
      //    jQuery().find()
      //    jQuery().attr()
      //    jQuery().prop()
      //    jQuery().val()
      //    jQuery().removeAttr()
      //    .flip()
      //    .values.fix()
      // Remark: Used as event handler -- 'this' is not POpt.group
      // 2013-02-28 PerfektesChaos@de.wikipedia
      var par  =  this.ctl2url[ access ],
          s, $e;
      if ( par ) {
         if ( ! par.$ctl ) {
            par.$ctl  =  this.$form.find( "#" + access );
         }
         if ( par.$ctl && par.$ctl.length ) {
            if ( par.type === "checkbox" ) {
               s  =  par.$ctl.attr( "value" );
               if ( s ) {
                  s  =  ( s === "0"  ?  "1"  :  "0" );
               } else {
                  s  =  ( par.$ctl.prop( "checked" )  ?  "1"  :  "0" );
               }
               par.$ctl.val( s );
               if ( s === "0" ) {
                  par.$ctl.removeAttr( "checked" );
               }
               if ( POpt.mode === 1 ) {   // watchlist only
                  switch ( access ) {
                     case POpt.group.sign :
                        POpt.flip( s, "1", POpt.multi );
                        break;
                     case POpt.multi.sign :
                        POpt.flip( s, "0", POpt.group );
                        break;
                  }   // switch access
               }
            } else {
               s  =  par.$ctl.val();
            }
            par.val  =  s;
            $e  =  this.$form.find( "#hidden_" + access );
            if ( $e.length ) {
               $e.val( s );
            }
            this.fix( access );
         }
      }
   };   // .values.flip()



   POpt.values.found  =  function () {
      // Initialize .values field with hidden input
      // Precondition:
      //    this is an input field
      // Postcondition:
      //    Return true -- continue $.each()
      // Uses:
      //    >  .values.ctl2url
      //    jQuery().attr()
      //    jQuery().val()
      // Remark: Used as jQuery handler -- 'this' is DOM element
      // 2013-02-06 PerfektesChaos@de.wikipedia
      var $input  =  $( this ),
          par, sign;
      if ( $input.attr( "type" ) === "hidden" ) {
         sign  =  $input.attr( "name" );
         par   =  POpt.values.ctl2url[ sign ];
         if ( par ) {
            par.val  =  $input.val();
         }
      }
      return true;
   };   // .values.found()



   POpt.values.furnish  =  function ( access ) {
      // Equip visible control with onChange
      // Precondition:
      //    access  -- identifier in .ctl2url and of control
      // Uses:
      //    this
      //    >  .values.ctl2url
      //    >  .values.$form
      //    >  .slip
      //    jQuery().find()
      //    jQuery().val()
      //    jQuery().attr()
      // 2015-01-11 PerfektesChaos@de.wikipedia
      var par  =  this.ctl2url[ access ],
          s;
      if ( par ) {
         if ( ! par.$ctl ) {
            if ( this.$form ) {   // catch SQL failure
               par.$ctl  =  this.$form.find( "#" + access );
            }
         }
         if ( par.$ctl ) {
            if ( par.type === "checkbox" ) {
               s  =  ( par.$ctl.prop( "checked" )  ?  "1"  :  "0" );
            } else {
               s  =  par.$ctl.val();
            }
            par.val  =  s;
            s        =  POpt.slip + ".values.flip('" + access + "');";
            par.$ctl.attr(  { onchange: s }  );
         }
      }
   };   // .values.furnish()



   POpt.facilitate  =  function () {
      // Equip options with checkbox for every plain link
      // Uses:
      //    this
      //    >  .mode
      //    >  .slip
      //    >  .values.$wrap
      //    >  .values.ctl2url
      //    >  .values.$form
      //    jQuery().find()
      //    jQuery()
      //    jQuery().eq()
      //    jQuery().attr()
      //    jQuery().before()
      //    jQuery().append()
      // 2015-01-11 PerfektesChaos@de.wikipedia
      var bools  =  [ "hideMinor",
                      "hideBots",
                      "hideAnons",
                      "hideLiu",
                      "hideOwn",
                      "hideReviewed"
                  //  "hidePatrolled"
                    ],
          j       =  ( this.mode === 1  ?  8  :  9 ),
          n       =  j + bools.length,
          k       =  0,
          $set    =  POpt.values.$wrap.find( "a" ),
          i, par, sign, $e;
      for ( i = j;  i < n;  i++ ) {
         sign      =  bools[ k ];
         par       =  POpt.values.ctl2url[ sign ];
         par.$ctl  =  $( "<input />" );
         par.$ctl.attr(  { id:        sign,
                           onchange:  POpt.slip
                                      + ".values.flip(\"" + sign,
                           type:      "checkbox" }  );
         if ( par.val ) {
            par.$ctl.attr(  { checked: "true" }  );
         }
         par.$ctl.val( ( par.val ? "1" : "0" ) );
         $e  =  $set.eq( i );
         $e.attr(  { id:  "link_" + sign }  );
         $e.before( par.$ctl );
         $e  =  $( "<input />" );
         $e.attr(  { id:     "hidden_" + sign,
                     name:   sign,
                     type:   "hidden",
                     value:  ( par.val ? "1'" : "0'" )  }  );
         $e.val( ( par.val ? "1" : "0" ) );
         POpt.values.$form.append( $e );
         k++;
      }   // for i
   };   // .facilitate()



   POpt.factory  =  function ( assign, ahead ) {
      // Equip page with 'extended'/'enhanced' toggle checkbox and label
      // Precondition:
      //    assign  -- object ( .group. or .multi. )
      //               >  .sign
      //               >  .show
      //               >  .soft
      //    ahead   -- true iff leading
      // Uses:
      //    this
      //    >  .values.$form
      //    >  .values.ctl2url
      //    >  .special
      //    >  .lean
      //    >  .slip
      //    >  .values.$POpt
      //    >< .values.script
      //    mw.config.get()
      //    .config.feature()
      //    .values.factory()
      //    jQuery()
      //    jQuery().prepend()
      //    (.values.flip)
      // 2015-01-11 PerfektesChaos@de.wikipedia
      var less, shift, sub, $a, $input, $label, $sep, $span;
      if ( this.values.$form ) {
         if ( ! this.values.script ) {
            this.values.script  =  mw.config.get( "wgScript" );
         }
         less  =  this.values.ctl2url[ assign.sign ].val;
         if ( less === "0" ) {
            less  =  false;
         }
         shift  =  this.values.script + "?title=Special:" + this.special
                   + this.values.factory();
         sub    =  "&" + assign.soft + "=";
         shift  =  shift.replace( new RegExp( sub + "?[01]" ),
                                  sub  +  ( less ? "0" : "1" )
                                );
         if ( this.mode === 1 ) {
            if ( shift.indexOf( "&extended=0" )  >  0 ) {
               shift  =  shift.replace( /&enhanced=1/,
                                        "&enhanced=0" );
            }
            if ( shift.indexOf( "&enhanced=1" )  >  0 ) {
               if ( shift.indexOf( "&extended=" )  >  0 ) {
                  shift  =  shift.replace( /&extended=0/,
                                           "&extended=1" );
               } else {
                  shift  =  shift + "&extended=1";
               }
            }
         }
         $label  =  $( "<label />" );
         $span   =  $( "<span>" );
         $span.text( this.config.feature( assign.show )  +  " (" );
         $label.append( $span );
         $a  =  $( "<a>" );
         $a.attr(  { href: shift }  );
         $a.text( this.config.feature( ( less ? "toggleOff"
                                              : "toggleOn" ) ) );
         $label.append( $a );
         $span  =  $( "<span>" );
         $span.text( ")" );
         $label.append( $span );
         this.values.$POpt.prepend( $label );
         $input  =  $( "<input />" );
         $input.attr(  { id:        assign.sign,
                         name:      assign.soft,
                         onchange:  this.slip
                                    + ".values.flip('" + assign.sign
                                                                  + "')",
                         type:      "checkbox",
                         value:     ( less ? "1" : "0" ) }  );
         if ( less ) {
            $input.attr(  { checked: true }  );
         }
         this.values.$POpt.prepend( $input );
         if ( this.mode === 1 ) {
            if ( ! this.lean  &&  ahead ) {
               $sep  =  $( "<hr />" );
            } else if ( ! ahead ) {
               $sep  =  $( "<span>" );
               $sep.text( "\xA0| " );
            }
            this.values.$POpt.prepend( $sep );
         }
      }
   };   // .factory()



   POpt.fault  =  function ( action, arrived, $apply ) {
      // Show API failure on any action
      // Precondition:
      //    action   -- keyword of request
      //    arrived  -- JSON info of ajax query
      //    $apply   -- current set of throbber element containers
      // Uses:
      //    >  mw.util.$content
      //    >  .type
      //    jQuery()()
      //    jQuery().css()
      //    jQuery().text()
      //    jQuery().empty()
      //    jQuery().append()
      //    mw.log()
      // Remark: Used as jQuery handler -- 'this' is not POpt.notice
      // 2015-10-11 PerfektesChaos@de.wikipedia
      var $e  =  $( "<strong>" );
      $e.css( { "border": "#FF0000 2px solid",
                "color":  "#FF0000",
                "margin": "5px" } );
      $e.text( "ERROR" );
      $apply.empty();
      $apply.append( $e );
      mw.log( {loud:true},
              "." + POpt.type + "." + action + ".fault()",
              2,
              arrived );
   };   // .fault()



   POpt.fiat  =  function () {
      // Equip changes page with controls, analyze by grabbing
      // Precondition:
      //    document has been loaded
      // Uses:
      //    >  .mode
      //    >  mw.util.$content
      //    >  .store
      //    >  .luxury
      //    >  .learnt
      //    >  .lower
      //    >  .lean
      //    >  .hide
      //    >  .letal
      //    >  .leyo
      //    >  .self
      //    >< .$options
      //     < .ltr
      //     < .group.leaves
      //     < .disguise.hide
      //     < .disguise.show
      //     < .picked.learn
      //    jQuery().find()
      //    mw.util.addCSS()
      //    jQuery().hide()
      //    .unwatch.fiat()
      //    .flat()
      //    .values.find()
      //    .facilitate()
      //    .values.furnish()
      //    .disguise.fire()
      //    .picked.furnish()
      //    .reduce.furnish()
      //    .increm.furnish()
      //    .values.flat()
      //    .furnish()
      //    .notice.first()
      // Remark: Used as event handler -- 'this' is not POpt
      // 2016-05-07 PerfektesChaos@de.wikipedia
      var sep, store, story, style, suite, $grab, $wd;
      mw.util.$content.find( ".mw-changeslist-legend" ).hide();
      POpt.ltr  =  ( $( window.document ).find( "html" ).attr( "dir" )
                     !==  "rtl" );
      switch ( POpt.mode ) {
         case 1 :   // watchlist
            sep                =  ".mw-changeslist-separator";
            $grab              =  mw.util.$content.find( sep );
            POpt.group.leaves  =  ( $grab.length > 0 );
            if ( POpt.group.leaves ) {
               POpt.unwatch.fiat();
            }
            story  =  "#mw-watchlist-options";
            suite  =  "#mw-watchlist-form";
            POpt.flat();
            store  =  ".wikibase-edit";
            $wd    =  mw.util.$content.find( store );
            if ( $wd.length ) {
               switch ( typeof POpt.store ) {
                  case "boolean":
                     style  =  POpt.store;
                     break;
                  case "string":
                     if ( /[0-9A-F]{6}/.test( POpt.store ) ) {
                        style  =  POpt.store;
                        break;
                     }   // fall through
                  default:
                     style  =  true;
               }   // switch typeof POpt.store
               if ( style  ===  true ) {
                  style  =  POpt.config[ "wikidata.bgc" ];
               }
               if ( style ) {
                  style  =  "{background-color:#" + style + "}";
                  mw.util.addCSS( store + style );
               }
            }
            break;
         case 2 :   // rc
            POpt.group.leaves  =  true;
            story              =  ".rcoptions";
            suite              =  "form";
            break;
      }   // switch .mode
      POpt.values.find( story, suite );
      if ( POpt.luxury ) {
         POpt.facilitate();
      }
      POpt.values.furnish( "associated" );
      POpt.values.furnish( "nsinvert" );
      POpt.values.furnish( "namespace" );
      POpt.disguise.hide  =  ( typeof POpt.hide  ===  "object"
                               &&     POpt.hide );
      POpt.disguise.show  =  ( typeof POpt.show  ===  "object"
                               &&     POpt.show );
      POpt.picked.learn   =  ( typeof POpt.learnt  ===  "boolean" );
      if ( POpt.disguise.hide ||
           POpt.disguise.show ||
           POpt.picked.learn ||
           POpt.letal ||
           POpt.leyo ||
           POpt.self ) {
         POpt.disguise.fire();
      }
      if ( POpt.picked.learn  &&  POpt.mode === 1 ) {
         POpt.picked.furnish();   // watchlist & visited
      }
      if ( typeof POpt.lower  ===  "boolean" ) {
         POpt.reduce.furnish();
      } else if ( POpt.mode === 1 ) {   // watchlist
         POpt.increm.furnish();
      }
      if ( POpt.lean ) {
         POpt.values.flat();
      }
      POpt.furnish( POpt.group, false );
      if ( POpt.mode === 1 ) {   // watchlist
         POpt.furnish( POpt.multi, true );
         if ( POpt.values.$wrap && POpt.values.$score ) {
            if ( ! POpt.$options ) {
               POpt.$options  =  POpt.values.$wrap.find( "legend" );
            }
            POpt.$options.after( POpt.values.$score );
         }
         POpt.notice.first();
      }
   };   // .fiat()



   POpt.fire  =  function () {
      // Start processing, if appropriate
      // Precondition:
      //    Special page
      // Uses:
      //    this
      //    >  Version
      //     < .signature
      //     < .vsn
      //     < .doc
      //     < .special
      //     < .mode
      //     < .rLoading
      //    mw.loader.getState()
      //    mw.loader.state()
      //    mw.config.get()
      //    .config.fire()
      //    mw.loader.using()
      //    (.firing)
      // 2018-08-24 PerfektesChaos@de.wikipedia
      var env, pages, require, rls;
      POpt.signature  =  "ext.gadget." + POpt.type;
      POpt.vsn        =  Version;
      if ( ! mw.loader.getState( POpt.signature ) ) {
         env       =  mw.config.get( [ "wgCanonicalSpecialPageName",
                                       "wgNamespaceNumber" ] );
         pages     =  { "Blankpage":     -1,
                        "Gadgets":       -1,
                        "Watchlist":      1,
                        "Recentchanges":  2,
                        "EditWatchlist":  3 };
         rls       =  { };
         POpt.doc  =  "[[w:en:User:PerfektesChaos/js/" + POpt.type
                      + "]]";
         if ( env.wgNamespaceNumber  ===  -1 ) {
            this.special  =  env.wgCanonicalSpecialPageName;
            this.mode     =  pages[ this.special ];
            if ( this.mode ) {
               rls[ this.signature ]  =  "loading";
               mw.loader.state( rls );
               rls[ this.signature ]  =  "ready";
               this.rLoading  =  rls;
               rls            =  false;
               this.config.fire();
               require  =  [ "user",
                             "user.options",
                             "mediawiki.user",
                             "mediawiki.util" ];
               if ( this.mode === 1 ) {
                  require  =  require.concat( [ "mediawiki.api" ] );
               }
               mw.loader.using( require, this.firing );
            }
         }
         if ( rls ) {
            rls[ this.signature ]  =  "ready";
            mw.loader.state( rls );
         }
      }
   };   // .fire()



   POpt.firing  =  function () {
      // Intermediate loading process step
      // Precondition:
      //    MW ressources are available
      // Uses:
      //    >  .config.supply
      //    mw.hook()
      //    (.first)
      // Remark: Used as event handler -- 'this' is not POpt
      // 2015-09-12 PerfektesChaos@de.wikipedia
      mw.hook( POpt.config.supply + ".ready" ).add( POpt.first );
   };   // .firing()



   POpt.first  =  function ( attributes ) {
      // Start processing
      // Precondition:
      //    attributes  -- preferencesGadgetOptions application object
      //    All ressources are available
      // Uses:
      //    >  .type
      //    >  .mode
      //    >  .rLoading
      //     < .prego
      //     < .slip
      //    .config.fetch()
      //    jQuery().ready()
      //    .config.form()
      //    mw.loader.state()
      //    mw.hook()
      //    (.fiat)
      //    (.edit.fire)
      // Remark: Used as event handler -- 'this' is not POpt
      // 2018-08-26 PerfektesChaos@de.wikipedia
      POpt.prego  =  attributes;
      POpt.slip   =  "mw.libs." + POpt.type;
      if ( POpt.mode > 0 ) {
         if ( POpt.prego ) {
            POpt.config.fetch();
         }
         if ( POpt.mode < 3 ) {
            $( POpt.fiat );
         } else {
            $( POpt.edit.fire );
         }
      } else if ( POpt.prego ) {
         POpt.config.form();
      }
      mw.loader.state( POpt.rLoading );
      mw.hook( POpt.type + ".ready" ).fire( POpt );
   };   // .first()



   POpt.flat  =  function () {
      // Remove watchlist information
      // Uses:
      //    >  mw.util.$content
      //     < .values.$score
      //    jQuery().find()
      //    jQuery().children()
      //    .config.feature()
      //    jQuery().text()
      //    jQuery().remove()
      // 2015-01-11 PerfektesChaos@de.wikipedia
      var $e  =  mw.util.$content.find( "#mw-content-text" ),
          re, s;
      if ( $e.length ) {
         $e  =  $e.children( "p" );
         if ( $e.length ) {
            re  =  new RegExp( POpt.config.feature( "watchPgRE",
                                                    false ) );
            s   =  $e.text().replace( re, "$1" );
            if ( s ) {
               $e.remove();
               POpt.values.$score  =  $( "<span>" );
               POpt.values.$score.text( s + "\xA0| " );
               // update
            }
         }
      }
   };   // .flat()



   POpt.flip  =  function ( alter, allow, assign ) {
      // Coordinate multiple and grouped watchlist entries
      // Precondition:
      //    alter   -- just set parameter value,  "0" or "1"
      //    allow   -- required parameter value,  "0" or "1"
      //    assign  -- corresponding object to be updated
      // Uses:
      //    this
      //    >  .values.$form
      //    >< .values.ctl2url
      //    jQuery().find()
      //    jQuery().click()
      // 2013-02-28 PerfektesChaos@de.wikipedia
      var co, s;
      if ( alter === allow ) {
         s   =  assign.sign;
         co  =  this.values.ctl2url[ s ];
         if ( co ) {
            if ( co.val !== allow ) {
               if ( ! co.$ctl ) {
                  if ( this.values.$form ) {
                     co.$ctl  =  this.values.$form.find( "#" + s );
                  }
               }
               if ( co.$ctl ) {
                  co.$ctl.click();
               }
            }
         }
      }
   };   // .flip()



   POpt.format  =  function ( access ) {
      // Encode URL part string regexp conditionally for Wiki
      // Precondition:
      //    access  -- string, part of URL
      // Postcondition:
      //    Return string, ready for URL
      // Uses:
      //    this
      //    >< .sp2_
      // 2013-03-08 PerfektesChaos@de.wikipedia
      var r  =  access,
          i, k, n, s;
      if ( r ) {
         if ( access.indexOf( "%" )  <  0 ) {
            n  =  access.length;
            r  =  "";
            for ( i = n - 1;  i >= 0;  i-- ) {
               k  =  access.charCodeAt( i );
               s  =  String.fromCharCode( k );
               if ( k > 127 ) {
                  s  =  encodeURI( s );
               }
               r  =  s + r;
            }   // for i--
            s  =  mw.util.wikiUrlencode( r );
         } else {
            if ( ! this.sp2_ ) {
               this.sp2_  =  new RegExp( " ", "g" );
            }
            r  =  r.replace( this.sp2_, "_" );
         }
      }
      return r;
   };   // .format()



   POpt.furnish  =  function ( assign, ahead ) {
      // Equip page with additional labeled control
      // Precondition:
      //    assign  -- object ( .group. or .multi. )
      //               >  .soft
      //               >  .sign
      //               .find()
      //    ahead  -- true iff leading
      //    document has been loaded
      // Uses:
      //    this
      //    mw.util.getParamValue()
      //    .values.fix()
      //    .factory()
      // 2013-02-27 PerfektesChaos@de.wikipedia
      var set  =  mw.util.getParamValue( assign.soft );
      assign.find( set );
      POpt.values.fix( assign.sign );
      this.factory( assign, ahead );
   };   // .furnish()



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



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

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