User:PerfektesChaos/js/refNames/core/d.js

From Wikipedia, the free encyclopedia
Note: After saving, you have to bypass your browser's cache to see the changes. Google Chrome, Firefox, Microsoft Edge and Safari: Hold down the ⇧ Shift key and click the Reload toolbar button. For details and instructions about other browsers, see Wikipedia:Bypass your cache.
/// User:PerfektesChaos/js/refNames/core/d.js
/// 2023-05-01 PerfektesChaos@de.wikipedia
//  <ref name="..."> major work
//  ResourceLoader:  compatible;
//    dependencies: user.options,
//                  mediawiki.API, mediawiki.storage, mediawiki.util
/// Fingerprint: #0#0#
/// License: CC-by-sa/4.0
//  Documentation:  [[w:en:User:PerfektesChaos/js/refNames]]
/// <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   = -1.5,
       Signature = "refNames",
       THIS      = { lapsus:   false,
                     launch:   false,
                     reUscore: false,
                     show:     "&lt;ref&gt;",
                     sub:      "core",
                     using:    [ "mediawiki.api",
                                 "mediawiki.storage" ],
                     warn:     [ "^:?\\d+$",
                                 "^:$",
                                 "^Reference[A-Z]$",
                                 "^auto(?:generated)?\\d*$",
                                 String.fromCharCode( 0x5B,
                                                      0x09,     // TAB
                                                      0x0A,     // LF
                                                      0x22,     // "
                                                      0x23,     // #
                                                      0x25,     // %
                                                      0x26,     // &
                                                      0x27,     // '
                                                      0x3C,     // <
                                                      0x3E,	    // >
                                                      0x5B,	    // [
                                                      0x5C,	    // \
                                                      0x5D,	    // ]
                                                      0x60,	    // `
                                                      0xA0,	    // nbsp
                                                      0xAD,     // shy
                                                      0xB4,	    // ´
                                                      0x02C6,   // ˆ
                                                      0x02DC,   // ˜
                                                      0x2002,   // ensp
                                                      0x2003,   // emsp
                                                      0x2009,   // thinsp
                                                      0x200A,   // HAIRsp
                                                      0x200B,   // ZEROsp
                                                      0x200C,   // zwnj
                                                      0x200D,   // zwj
                                                      0x200E,   // lrm
                                                      0x200F,   // rlm
                                                      0x2018,   // lsquo
                                                      0x2019,   // rsquo
                                                      0x201A,   // sbquo
                                                      0x201C,   // ldquo
                                                      0x201D,   // rdquo
                                                      0x201E,   // bdquo
                                                      0x202F,   // NNBSP
                                                      0x2032,   // prime
                                                      0x2033,   // Prime
                                                      0x2039,   // ‹
                                                      0x203A,   // ›
                                                      0x5D )
                                  ],
                     $widget:  false
                   },
       PARSER    = { reQuot:    false,
                     reWS:      false,
                     pending:   false,
                     $textarea: false },
       SOURCE    = { before:   [ ".wikibase-entity-usage",
                                 ".templatesUsed",
                                 ".hiddencats",
                                 ".limitreport" ],
                     locked:   true,
                     pics:     { drag: [ "de", "OOjs_UI_icon_trash"
                                                  + "-destructive.svg" ],
                                 drop: [ "9b", "OOjs_UI_icon_trash"
                                                  + "-invert.svg" ],
                                 exch: [ "e9", "OOjs_UI_icon_reload"
                                                  + "-invert.svg" ],
                                 edit: [ "8a", "OOjs_UI_icon_edit-ltr"
                                                  + "-progressive.svg" ],
                                 wait: [ "de", "Ajax-loader.gif" ]
                               },
                     section:  "section",
                     $box:     false,
                     $editor:  false,
                     $wrapper: false },
       VIEW      = { };



   function failure( apply ) {
      // Analyze name syntax
      // Precondition:
      //    apply  -- string, to be analyzed
      // Postcondition:
      //    Returns true if undesired
      // Uses:
      //    >  THIS.warn
      // 2022-07-01 PerfektesChaos@de.wikipedia
      var r = false,
          i;
      for ( i = 0;  i < THIS.warn.length;  i++ ) {
         if ( THIS.warn[ i ].test( apply ) ) {
            r = true;
            break;   // for i
         }
      }   // for i
      return r;
   }   // failure()



   function fair( apply ) {
      // Decode name
      // Precondition:
      //    apply  -- string
      // Postcondition:
      //    Returns decoded string
      // Uses:
      //    >< THIS.reUscore
      // 2022-07-01 PerfektesChaos@de.wikipedia
      var r = apply;
      if ( r.indexOf( "%" )  >=  0 ) {
         try {
            r = decodeURIComponent( r );
         } catch ( e ) {
            r = unescape( r );
         }
      }
      if ( r.indexOf( "_" )  >=  0 ) {
         if ( ! THIS.reUscore ) {
            THIS.reUscore = new RegExp( "_", "g" );
         }
         r = r.replace( THIS.reUscore, " " );
      }
      return r;
   }   // fair()



   function $fault( apply ) {
      // Invisible error element
      // Postcondition:
      //    Returns <span> object
      // Uses:
      //    >  Signature
      //     < THIS.lapsus
      // 2022-07-01 PerfektesChaos@de.wikipedia
      var $r = $( "<span>" ),
          s  = Signature;
      if ( apply ) {
         s = s + " " + apply;
      }
      $r.addClass( [ "mw-error", "error",
                     "remindErrorMessages-invisible" ] )
        .hide()
        .text( s );
      THIS.lapsus = true;
      return $r;
   }   // $fault()



   function fiat() {
      // Portlet request
      // Uses:
      //    >  PARSER.$textarea
      //    >  THIS.$widget
      //    >  THIS.selector
      //    >< SOURCE.section
      //    VIEW.fire()
      //    SOURCE.first()
      // 2022-08-08 PerfektesChaos@de.wikipedia
      var learn = ( PARSER.$textarea ? true : false );
      if ( THIS.$widget ) {
         THIS.$widget.empty();
      }
      if ( learn ) {
         SOURCE.section = THIS.selector + SOURCE.section;
      } else {
         SOURCE.section = false;
      }
      VIEW.fire();
      if ( learn ) {
         SOURCE.first( true );
      }
   }   // fiat()



   function $file( address, appearance ) {
      // Create <img>
      // Precondition:
      //    address  -- Array, with image location
      //                       [ 0 ]  -- string, with 2 hex storage path
      //                       [ 1 ]  -- string, with signifcant ID
      //    appearance  -- number, with pixel size
      // 2022-07-01 PerfektesChaos@de.wikipedia
      var $r = $( "<img>" );
      $r.attr( { "src": "https://upload.wikimedia.org/"
                        + "wikipedia/commons/thumb/"
                        + address[ 0 ].substr( 0, 1 ) + "/"
                        + address[ 0 ] + "/"
                        + address[ 1 ]
                        + "/" + appearance + "px-"
                        + address[ 1 ] + ".png" } );
      return $r;
   }   // $file()



   function fire( $area ) {
      // DOM ready
      // Precondition:
      //    $area  -- mw-content-text
      // Uses:
      //    >  THIS.launch
      //    >  THIS.serial
      //    >  THIS.show
      //    >< THIS.warn
      //     < THIS.$content
      //     < THIS.$references
      //     < THIS.ltr
      //     < PARSER.$textarea
      //     < THIS.selector
      //     < THIS.selItem
      //     < THIS.selProblem
      //     < THIS.selWarn
      //     < THIS.$widget
      //    fiat()
      //    (fiat)
      // 2022-07-01 PerfektesChaos@de.wikipedia
      var dom, i, sel, $btn, $ta;
      THIS.$content    = $area;
      THIS.$references = THIS.$content.find( ".references" );
      if ( THIS.$references.length ) {
         THIS.ltr        = ( $( "html" ).attr( "dir" )  !==  "rtl" );
         THIS.selector   = "gadget-" + Signature.toLowerCase() + "-";
         THIS.selItem    = THIS.selector + "item";
         THIS.selProblem = THIS.selector + "problem";
         THIS.selWarn    = THIS.selector + "warn";
         for ( i = 0;  i < THIS.warn.length;  i++ ) {
            THIS.warn[ i ] = new RegExp( THIS.warn[ i ] );
         }   // for i
         $ta = THIS.$content.find( "#wpTextbox1" );
         if ( $ta.length === 1 ) {
            PARSER.$textarea = $ta;
         }
         if ( THIS.launch && PARSER.$textarea ) {
            fiat();
         } else {
            sel  = THIS.selector + "portlet";
            $btn = $( "<span>" );
            $btn.addClass( THIS.selItem )
                .attr( { "id":    sel,
                         "title": THIS.serial } )
                .click( fiat )
                .html( THIS.show );
            dom = mw.util.addPortletLink( "p-tb",
                                          "#",
                                          THIS.show,
                                          sel,
                                          THIS.serial );
            THIS.$widget = $( dom );
            if ( dom.nodeName.toLowerCase() !== "li" ) {
               THIS.$widget = THIS.$widget.parent();
            }
            THIS.$widget.empty()
                        .append( $btn );
         }
      }
   }   // fire()



   function fired( above ) {
      // Initialize on scout request
      // Precondition:
      //    above  -- object, with scout details
      //    mediawiki.util has been loaded
      // Uses:
      //    >  Version
      //     < THIS.doc
      //     < THIS.serial
      //     < THIS.launch
      //    mw.hook()
      //    (fire)
      // 2022-07-01 PerfektesChaos@de.wikipedia
      var i;
      if ( typeof above  ===  "object"
           &&     above ) {
         if ( typeof above.doc  ===  "object"
              &&     above.doc ) {
            THIS.doc = above.doc;
         }
         if ( typeof above.serial  ===  "string"
              &&     above.serial ) {
            THIS.serial = above.serial;
         } else {
            THIS.serial = "?";
         }
         if ( typeof above.launch  ===  "boolean"
              &&     above.launch ) {
            THIS.launch = true;
         }
         THIS.serial = THIS.serial + " / " + Version;
         if ( typeof above.warn  ===  "object"
              &&     above.warn   &&
              typeof above.warn.length  ===  "number" ) {
            for ( i = 0;  i < above.warn.length;  i++ ) {
               THIS.warn.push( above.warn[ i ] );
            }   // for i
         }
         mw.hook( "wikipage.content" ).add( fire );
      }
   }   // fired()



   function first() {
      // Autorun on loading
      // Uses:
      //    >  Signature
      //    >  THIS.sub
      //    mw.loader.getState()
      //    mw.loader.state()
      //    mw.hook()
      //    (fired)
      // 2022-07-01 PerfektesChaos@de.wikipedia
      var signature = "ext.gadget." + Signature + "." + THIS.sub,
          rls;
      if ( mw.loader.getState( signature )  !==  "ready" ) {
         rls = { };
         rls[ signature ] = "ready";
         mw.loader.state( rls );
         mw.hook( Signature + "." + THIS.sub ).add( fired );
      }
   }   // first()



   function $flag( apply ) {
      // Show decorated name item
      // Precondition:
      //    apply  -- string, to be displayed
      // Postcondition:
      //    Returns <span> object
      // Uses:
      //    >  THIS.selItem
      //    >  THIS.selWarn
      //    >  SOURCE.section
      //    failure()
      //    $fault()
      //    (SOURCE.forward)
      // 2022-08-08 PerfektesChaos@de.wikipedia
      var $r = $( "<span>" ),
          $swap;
      $r.addClass( THIS.selItem )
        .text( " " + apply + " " );
      if ( failure( apply ) ) {
         $r.addClass( THIS.selWarn );
         $swap = $r;
         $r = $( "<span>" );
         $r.append( $fault( apply ) )
           .append( $swap );
      }
      if ( SOURCE.section ) {
         $r.click( SOURCE.forward );
      }
      return $r;
   }   // $flag()



   function flat( all, at, allow ) {
      // Retrieve normalized character code
      // Precondition:
      //    all    -- string, with entire story
      //    at     -- number, position in all
      //    allow  -- true, if bidi permitted
      // Postcondition:
      //    Returns character code
      // 2022-07-01 PerfektesChaos@de.wikipedia
      var r = all.charCodeAt( at );
      switch ( r ) {
         case  0x0009: // CHARACTER TABULATION
         case  0x000A: // LINE FEED
         case  0x005F: // _
         case  0x00A0: // nbsp
         case  0x1680: // OGHAM SPACE MARK
         case  0x180E: // MONGOLIAN VOWEL SEPARATOR
         case  0x2000: // EN QUAD
         case  0x2001: // EM QUAD
         case  0x2002: // N-SPACE
         case  0x2003: // M-SPACE
         case  0x2004: // THREE-PER-EM SPACE
         case  0x2005: // FOUR-PER-EM SPACE
         case  0x2006: // SIX-PER-EM SPACE
         case  0x2007: // FIGURE SPACE
         case  0x2008: // PUNCTUATION SPACE
         case  0x2009: // thinsp
         case  0x200A: // HAIR SPACE (english typography)
         case  0x200B: // ZERO WIDTH SPACE
         case  0x200C: // zwnj
         case  0x200D: // zwj
         case  0x2028: // LINE SEPARATOR
         case  0x2029: // PARAGRAPH SEPARATOR
         case  0x202F: // NARROW NO-BREAK SPACE
         case  0x205F: // MEDIUM MATHEMATICAL SPACE
         case  0x2060: // WORD JOINER
         case  0x2061: // FUNCTION APPLICATION
         case  0x2062: // INVISIBLE TIMES
         case  0x2063: // INVISIBLE SEPARATOR
         case  0x2064: // INVISIBLE PLUS
         case  0x3000: // IDEOGRAPHIC SPACE
         case  0x303F: // IDEOGRAPHIC HALF FILL SPACE
         case  0xFEFF: // ZERO WIDTH NO-BREAK SPACE
         case 0xE0001: // LANGUAGE TAG
         case 0xE0020: // TAG SPACE
            r = 0x20;
            break;
         case  0x200E: // lrm
         case  0x200F: // rlm
         case  0x202A: // LEFT-TO-RIGHT EMBEDDING
         case  0x202B: // RIGHT-TO-LEFT EMBEDDING
         case  0x202C: // POP DIRECTIONAL FORMATTING
         case  0x202D: // LEFT-TO-RIGHT OVERRIDE
         case  0x202E: // RIGHT-TO-LEFT OVERRIDE
            if ( ! allow ) {
               r = 0x20;
            }
            break;
      }   // switch r
      return r;
   }   // flat()



   function fruit( all ) {
      // Retrieve simplified string
      // Precondition:
      //    all  -- string, with entire story
      // Postcondition:
      //    Returns string, with cleaned characters
      // 2022-07-01 PerfektesChaos@de.wikipedia
      var k = 0x20,
          r = "",
          i, m;
      for ( i = 0;  i < all.length;  i++ ) {
         m = flat( all, i, true );
         if ( m !== 0x20  ||  m !== k ) {
            r = r + String.fromCharCode( m );
            k = m;
         }
      }   // for i
      return r;
   }   // fruit()



   PARSER.fault = function ( all, at, alert, about ) {
      // Communicate error details (browser console)
      // Precondition:
      //    all    -- string, with entire story
      //    at     -- number, position in wikitext
      //    alert  -- string, with message
      //    about  -- string, with detail, or not
      // Uses:
      //    >  Signature
      //    >< PARSER.reWS
      // 2022-08-25 PerfektesChaos@de.wikipedia
      var i, n, s;
      if ( typeof window.console  ===  "object"   &&
           typeof window.console.warn  ===  "function" ) {
         i = -1;
         n = 0;
         do {
            i = all.indexOf( "\n",  i + 1 );
            n++;
         } while ( i >= 0  &&  i < at );
         s = Signature + " #" + at + " @" + n + " " + alert;
         if ( about ) {
            s = s + " -- " + about;
         }
         if ( ! PARSER.reWS ) {
            PARSER.reWS = new RegExp( "\\s+" );
         }
         i = at;
         while ( all.charCodeAt( i ) !== 0x3C  &&  i >= 0 ) {
            i--;
         }   // while
         n = all.indexOf( ">",  i + 1 );
         if ( n < 0 ) {
            n = i + 100;
         }
         s = s + "\n"
               + all.substring( i, n + 1).replace( PARSER.reWS, " " );
         window.console.warn( s );
      }
   };   // PARSER.fault()



   PARSER.feature = function ( all, at, after, about ) {
      // Retrieve attribute value
      // Precondition:
      //    all     -- string, with entire story
      //    at      -- number, first possible position in all
      //    after   -- number, position of '>' in all
      //    about   -- string, with attribute name
      // Postcondition:
      //    Returns object
      //            .set   -- string, value
      //            .lock  -- boolean, on error
      //            .more  -- number, position after value
      // Uses:
      //    PARSER.first()
      //    PARSER.fault()
      //    PARSER.focus()
      //    flat()
      // 2022-08-24 PerfektesChaos@de.wikipedia
      var r = { "set":  "",
                "lock": false,
                "more": after },
          i = PARSER.first( all, at, after ),
          k = all.charCodeAt( i ),
          s;
      if ( k === 0x22  ||  k === 0x27 ) {          // " '
         s = String.fromCharCode( k );
         k = all.indexOf( s,  i + 1 );
         if ( k < 0  ||  k > after ) {
            r.lock = true;
            PARSER.fault( all,
                          at,
                          "Unbalanced delimiter",
                          about + "=" + s );
         } else if ( k  ===  i + 1 ) {
            r.lock = true;
            PARSER.fault( all,
                          at,
                          "Missing identifier",
                          about + "=" + s + s );
         } else {
            r.set  = PARSER.focus( all.substring( i + 1,  k ) );
            r.more = k + 1;
         }
      } else if ( k === 0x2F  ||  k === 0x3E ) {   // / >
         PARSER.fault( all, at,  "Identifier missing",  about + "=" );
         r.lock = true;
      } else {
         for ( k = i;  k <= after;  k++ ) {
            switch ( flat( all, k ) ) {
               case 0x2F:   // /
               case 0x3E:   // >
                  if ( k === i ) {
                     r.lock = true;
                     PARSER.fault( all,
                                   at,
                                   "Missing identifier",
                                   about + "=" );
                     k = after + 1;
                     break;
                  }   // fall through
               case 0x20:
                  r.set  = all.substring( i,  k );
                  r.more = k;
                  k      = after + 1;
                  break;
            }   // switch flat()
         }   // for k
      }
      return r;
   };   // PARSER.feature()



   PARSER.features = function ( all, at, after, alone ) {
      // Retrieve <ref> or </references> tag attributes
      // Precondition:
      //    all    -- string, with entire story
      //    at     -- number, first possible position in all
      //    after  -- number, position of '>' in all
      //    alone  -- boolean, for </ref>
      // Postcondition:
      //    Returns object with name="", or not
      //            .name     -- string, ID, or not
      //            .group    -- string, ID, or not
      //            .extends  -- string, text, or not
      //            .content  -- string, content until </ref>, or not
      //            .lone     -- boolean, unary (/)
      //            .next     -- number, position of '>' in all
      // Uses:
      //     < PARSER.lock
      //    PARSER.focus()
      //    PARSER.fault()
      //    PARSER.feature()
      //    PARSER.first()
      // 2022-08-24 PerfektesChaos@de.wikipedia
      var i = at,
          j, lock, r, s, v;
      do {
         j = all.indexOf( "=", i );
         if ( j < 0  ||  j > after ) {
            break;   // while ! r
         } else {
            s = PARSER.focus( all.substring( i, j ) ).toLowerCase();
            switch ( s ) {
               case "name":
               case "group":
               case "extends":
                  if ( ! r ) {
                     r = { "name":     false,
                           "group":    false,
                           "extends":  false,
                           "content":  false,
                           "lone":     false,
                           "multi":    0,
                           "next":     after,
                           "wikitext": false };
                  }
                  if ( typeof r[ s ] === "string" ) {
                     PARSER.fault( all, at, "Duplicated keyword", s );
                     lock = true;
                  } else {
                     v      = PARSER.feature( all,  j + 1,  after,  s );
                     r[ s ] = v.set;
                     lock   = v.lock;
                     i      = v.more;
                  }
                  break;
               default:
                  PARSER.fault( all, at, "Unknown keyword", s );
                  lock = true;
            }   // switch
         }
      } while ( ! lock );
      if ( lock ) {
         PARSER.lock = true;
      } else if ( r ) {
         i = PARSER.first( all, i, after );
         if ( all.charCodeAt( i ) === 0x2F ) {   // /
            r.lone = true;
            i      = PARSER.first( all, ++i, after );
         }
         if ( alone || r.lone ) {
            i = PARSER.first( all, i, after );
            if ( i === after ) {
               if ( r  &&  ! r.name  &&  alone ) {
                  r = false;
               }
            } else {
               PARSER.lock = true;
               PARSER.fault( all, i, "Trailing garbage" );
            }
         }
      }
      return r;
   };   // PARSER.features()



   PARSER.fiat = function ( all, at, after, about, adjust ) {
      // Replace name="" identifier
      // Precondition:
      //    all     -- string, with entire story
      //    at      -- number, position after '<' in all
      //    after   -- number, position of '>' in all
      //    about   -- object
      //               .group    -- string, ID, or not
      //               .name     -- string, ID
      //               .extends  -- string, text, or not
      //               .lone     -- boolean, unary (/)
      //               .content  -- string, until </ref>, or not
      //    adjust  -- object, with name-group to be replaced
      //               .group  -- string, ID, or not
      //               .shift  -- string, replacing ID
      // Postcondition:
      //    Returns about object with modified entries
      //            .wikitext  -- string, modified all
      //            .next      -- number, modified after position
      // Uses:
      //    PARSER.fix()
      // 2022-07-01 PerfektesChaos@de.wikipedia
      var p = [ "group", "name", "extends" ],
          r = about,
          s = "ref",
          i, set, sign;
      for ( i = 0;  i < 3;  i++ ) {
         sign = p[ i ];
         set  = ( i === 1  ?  adjust.shift  :  about[ sign ] );
         if ( set ) {
            s = s + " " + sign + "=\"" + PARSER.fix( set ) + "\"";
         }
      }   // for i
      if ( about.lone ) {
         s = s + " /";
      }
      r.name     = adjust.shift;
      r.wikitext = all.substr( 0, at )  +  s  +  all.substr( after );
      r.next     = r.next + s.length + at - after;
      return r;
   };   // PARSER.fiat()



   PARSER.finalize = function ( array ) {
      // Build sorted structures
      // Precondition:
      //    array  -- Array, with raw objects of parsing
      // Postcondition:
      //    Returns object
      //            groups, with Array of group IDs
      //            grouped, with group IDs assigned to sorted Arrays
      //                     of objects
      // 2022-07-01 PerfektesChaos@de.wikipedia
      var grouped = { },
          groups  = [ ],
          c, e, g, i, k, o, p, s, v;
      for ( i = 0;  i < array.length;  i++ ) {
         e = array[ i ];
         s = e.group;
         if ( typeof grouped[ s ]  !==  "object" ) {
            groups.push( s );
            grouped[ s ] = [ ];
         }
         grouped[ s ].push( e );
      }   // for i
      groups.sort();
      for ( i = 0;  i < groups.length;  i++ ) {
         p = groups[ i ];
         g = grouped[ p ];
         c = { };
         o = [ ];
         for ( k = 0;  k < g.length;  k++ ) {
            v = g[ k ];
            s = v.name;
            if ( typeof c[ s ]  ===  "object" ) {
               e = c[ s ];
               e.linked = true;
               if ( ! v.lone ) {
                  e.lone = false;
                  if ( e.content && v.content ) {
                     e.multi++;
                  }
               }
               c[ s ] = e;
            } else {
               c[ s ] = { name:    s,
                          content: v.content,
                          lone:    v.lone,
                          linked:  v.linked || v.lone,
                          multi:   v.multi };
               o.push( s );
            }
            g[ k ] = null;
         }   // for k
         o.sort();
         for ( k = 0;  k < o.length;  k++ ) {
            o[ k ] = c[ o[ k ] ];
         }   // for k
         grouped[ p ] = o;
         c = null;
         g = null;
         o = null;
      }   // for i
      return { groups:  groups,
               grouped: grouped };
   };   // PARSER.finalize()



   PARSER.finish = function ( all, at, alone ) {
      // Find </ref> or </references> tag
      // Precondition:
      //    all    -- string, with entire story
      //    at     -- number, first possible position in all
      //    alone  -- boolean, for </ref>
      // Postcondition:
      //    Returns Array   [ begin, end ]   of  < >
      //            or      false
      // Uses:
      //    PARSER.first()
      // 2022-07-28 PerfektesChaos@de.wikipedia
      var i = at,
          j, kR, kE, kF, n, r, s;
      do {
         i = all.indexOf( "<", i );
         if ( i < 0 ) {
            break;   // while ! r
         } else {
            i++;
            n = all.indexOf( ">", i );
            if ( n < 0 ) {
               break;   // while ! r
            } else if ( n  >  i + 3 ) {
               j = PARSER.first( all, i, n );
               if ( n  >=  j + 3   &&
                    all.charCodeAt( j )  ===  0x2F ) {
                  j  = PARSER.first( all, j, n );
                  kR = all.charCodeAt( j + 1 );
                  kE = all.charCodeAt( j + 2 );
                  kF = all.charCodeAt( j + 3 );
                  if ( ( kR === 0x52  ||  kR === 0x72 )   &&
                       ( kE === 0x45  ||  kE === 0x65 )   &&
                       ( kF === 0x46  ||  kF === 0x66 ) ) {
                     if ( alone) {
                        if ( n  ===  j + 4   ||
                             n  ===  PARSER.first( all,  j + 4,  n ) ) {
                           r = [ i - 1,  n ];
                        }
                     } else if ( n  >=  j + 10 ) {
                        kE = all.charCodeAt( j + 4 );
                        if ( kE === 0x45  ||  kE === 0x65 ) {
                           s = all.substring( j + 1,  j + 11 )
                                   .toLowerCase();
                           if ( s === "references" ) {
                              r = [ i - 1,  n ];
                           }
                        }
                     }
                  }
               }
            }
         }
      } while ( ! r );
      return r;
   };   // PARSER.finish()



   PARSER.fire = function ( always, adjust ) {
      // Retrieve all <ref> specifications
      // Precondition:
      //    always  -- boolean, if text availability does not matter
      //    adjust  -- object, with name-group to be replaced, or not
      // Postcondition:
      //    Returns object, or not
      //            .groups    Array of group names (or false)
      //            .grouped   object, mapping group names to Array
      //                       Each Array will contain objects
      //                             .name      -- string, ID
      //                             .extends   -- string, text, or false
      //                             .content   -- string, until </ref>
      //                             .lone      -- boolean, unary (/)
      // Uses:
      //    >  PARSER.$textarea
      //     < PARSER.lock
      //    PARSER.fired()
      //    PARSER.finalize()
      // 2022-07-01 PerfektesChaos@de.wikipedia
      var e, k, r, s, x;
      if ( PARSER.$textarea.attr( "disabled" ) ) {
         if ( always ) {
            r = PARSER.fired( PARSER.$textarea.val() );
         }
      } else {
         PARSER.lock = false;
         s           = PARSER.$textarea.val();
         if ( adjust ) {
            PARSER.$textarea.attr( "disabled", true );
            r = PARSER.fired( s );
            if ( ! PARSER.lock ) {
               x = adjust;
               for ( k = 0;  k < r.length;  k++ ) {
                  e = r[ k ];
                  if ( e.name === x.name  &&
                       e.group === x.group ) {
                     x = false;
                     break;   // for k
                  }
               }   // for k
               if ( x ) {
                  r = PARSER.fired( s, x, PARSER.$textarea );
               }
            }
            PARSER.$textarea.attr( "disabled", false );
         } else {
            r = PARSER.fired( s );
         }
      }
      if ( r ) {
         r = PARSER.finalize( r );
      }
      return r;
   };   // PARSER.fire()



   PARSER.fired = function ( all, adjust, $assign ) {
      // Retrieve all <ref> specifications
      // Precondition:
      //    all      -- string, with entire story
      //    adjust   -- object, with name-group to be replaced, or not
      //    $assign  -- jquery, to be updated, or not
      // Postcondition:
      //    Returns Array of objects, or not
      //                     .name      -- string, ID
      //                     .group     -- string, ID, or false
      //                     .extends   -- string, text, or false
      //                     .content   -- string, until </ref>, or false
      //                     .linked    -- boolean, repeated
      //                     .lone      -- boolean, unary (/)
      // Uses:
      //    PARSER.front()
      // 2022-08-01  PerfektesChaos@de.wikipedia
      var i = 0,
          s = all,
          e, k, p, r, v;
      do {
         v = PARSER.front( s, i, adjust );
         if ( v ) {
            i = v.next;
            if ( v.wikitext ) {
               s = v.wikitext;
               delete v.wikitext;
            }
            delete v.next;
            if ( v.lone ) {
               if ( r ) {
                  for ( k = 0;  k < r.length;  k++ ) {
                     e = r[ k ];
                     if ( e.name === v.name  &&
                          e.group === v.group ) {
                        r[ k ].linked = true;
                        v             = false;
                        break;   // for k
                     }
                  }   // for k
               }
               if ( v ) {
                  if ( p ) {
                     for ( k = 0;  k < p.length;  k++ ) {
                        e = p[ k ];
                        if ( e.name === v.name  &&
                             e.group === v.group ) {
                           p[ k ].linked = true;
                           v             = false;
                           break;   // for k
                        }
                     }   // for k
                  } else {
                     p = [ ];
                  }
                  if ( v ) {
                     v.linked = true;
                     p.push( v );
                  }
               }
            } else if ( r ) {
               v.linked = false;
               for ( k = 0;  k < r.length;  k++ ) {
                  e = r[ k ];
                  if ( e.name === v.name  &&
                       e.group === v.group ) {
                     if ( v.content  &&
                          e.content === v.content ) {
                        r[ k ].multi++;
                     }
                     r[ k ].content = e.content || v.content;
                     if ( r[ k ].content ) {
                        r[ k ].lone = false;
                     }
                     r[ k ].linked = true;
                     v             = false;
                     break;   // for k
                  }
               }   // for k
            }
            if ( v ) {
               r = r  ||  [ ];
               r.push( v );
            } else {
               v = true;
            }
         }
      } while ( v );
      if ( $assign ) {
         $assign.val( s );
      }
      if ( p ) {
         for ( k = 0;  k < p.length;  k++ ) {
            v = p[ k ];
            if ( r ) {
               for ( i = 0;  i < r.length;  i++ ) {
                  e = r[ i ];
                  if ( e === v ) {
                     v = false;
                  } else if ( e.name === v.name  &&
                              e.group === v.group ) {
                     r[ i ].linked = true;
                     if ( e.content  &&
                          e.content === v.content ) {
                        r[ i ].multi++;
                     }
                     v = false;
                  }
               }   // for i
            }
            if ( v ) {
               r = r  ||  [ ];
               r.push( v );
            }
         }   // for k
      }
      return r;
   };   // PARSER.fired()



   PARSER.first = function ( all, at, after ) {
      // Find first non-space character
      // Precondition:
      //    all    -- string, with entire story
      //    at     -- number, first position in all
      //    after  -- number, beyond position in all
      // Postcondition:
      //    Returns position in all, even after
      // Uses:
      //    flat()
      // 2022-07-01 PerfektesChaos@de.wikipedia
      var r = at,
          i;
      for ( i = at;  i <= after;  i++ ) {
         if ( flat( all, i )  !==  0x20 ) {
            r = i;
            break;   // for i
         }
      }   // for i
      return r;
   };   // PARSER.first()



   PARSER.fix = function ( ask ) {
      // Escape " in a string
      // Precondition:
      //    ask  -- string
      // Postcondition:
      //    Returns safe string
      // Uses:
      //    >< PARSER.reQuot
      // 2022-07-01 PerfektesChaos@de.wikipedia
      var r = ask;
      if ( ask.indexOf( "\"" )  >=  0 ) {
         if ( ! PARSER.reQuot ) {
            PARSER.reQuot = new RegExp( "\"", "g" );
         }
         r = r.replace( PARSER.reQuot, "&quot;" );
      }
      return r;
   };   // PARSER.fix()



   PARSER.focus = function ( ask ) {
      // Trim a string
      // Precondition:
      //    ask  -- string
      // Postcondition:
      //    Returns trimmed string
      // Uses:
      //    PARSER.first()
      //    flat()
      // 2022-07-01 PerfektesChaos@de.wikipedia
      var r = ask.trim(),
          n = r.length,
          i = PARSER.first( r, 0, n );
      if ( i ) {
         r = r.substr( i );
      }
      n = r.length;
      if ( n ) {
         for ( i = n - 1;  i >= 0;  i-- ) {
            if ( flat( r, i )  ===  0x20 ) {
               r = r.substr( 0, i );
            } else {
               break;   // for i--
            }
         }   // for i--
      }
      return r;
   };   // PARSER.focus()



   PARSER.front = function ( all, at, adjust ) {
      // Check opening <ref> tag
      // Precondition:
      //    all     -- string, with entire story
      //    at      -- number, first possible position in all
      //    adjust  -- object, with name-group to be replaced, or not
      //               .group  -- string, ID, or not
      //               .seek   -- string, ID to be replaced
      // Postcondition:
      //    Returns object, or not
      //            .name      -- string, ID, or not
      //            .group     -- string, ID, or not
      //            .extends   -- string, text, or not
      //            .content   -- string, content until </ref>, or not
      //            .lone      -- boolean, unary (/)
      //            .multi     -- number, 0
      //            .next      -- number, position of closing '>' in all
      //            .wikitext  -- string, modified all
      // Uses:
      //    >< PARSER.pending
      //     < PARSER.lock
      //    PARSER.first()
      //    flat()
      //    PARSER.features()
      //    PARSER.fiat()
      //    PARSER.frontal()
      // 2022-07-28 PerfektesChaos@de.wikipedia
      var i = at,
          j, kR, kE, kF, n, r, v;
      do {
         i = all.indexOf( "<", i );
         if ( i < 0 ) {
            break;   // while ! r
         } else {
            i++;
            n = all.indexOf( ">", i );
            if ( n < 0 ) {
               break;   // while ! r
            } else if ( n  >=  i + 10 ) {
               j = PARSER.first( all, i, n );
               if ( n  >=  j + 10 ) {
                  kR   = all.charCodeAt( j );
                  kE   = all.charCodeAt( j + 1 );
                  kF   = all.charCodeAt( j + 2 );
                  if ( ( kR === 0x52  ||  kR === 0x72 )   &&
                       ( kE === 0x45  ||  kE === 0x65 )   &&
                       ( kF === 0x46  ||  kF === 0x66 ) ) {
                     if ( flat( all,  j + 3 )   ===   0x20 ) {
                        r = PARSER.features( all,  j + 4,  n,  true );
                        if ( r ) {
                           if ( ! r.lone ) {
                              v = PARSER.finish( all,  n + 1,  true );
                              if ( v ) {
                                 r.content = all.substring( n + 1,
                                                            v[ 0 ] )
                                                .trim();
                                 r.next    = v[ 1 ] + 1;
                              } else {
                                 PARSER.lock = true;
                                 PARSER.fault( all,
                                               i,
                                               "Missing </ref>" );
                              }
                           }
                           if ( PARSER.pending ) {
                              if ( r.next < PARSER.pending.max ) {
                                 r.group = PARSER.pending.shared;
                              } else {
                                 PARSER.pending = false;
                              }
                           }
                           if ( r.name    &&
                                adjust    &&
                                adjust.seek === r.name    &&
                                ( ( ! adjust.group  &&  ! r.group )   ||
                                  adjust.group === r.group )    &&
                                ! PARSER.lock ) {
                              r = PARSER.fiat( all, i, n, r, adjust );
                           }
                        }
                     } else {
                        kE = all.charCodeAt( j + 3 );
                        if ( kE === 0x45  ||  kE === 0x65 ) {
                           PARSER.frontal( all, j, n );
                        }
                     }
                  }
               }
            }
         }
      } while ( ! r );
      return r;
   };   // PARSER.front()



   PARSER.frontal = function ( all, at, after ) {
      // Check opening <references group=""> tag
      // Precondition:
      //    all    -- string, with entire story
      //    at     -- number, position of "ref" in all
      //    after  -- number, position of ">" in all
      // Uses:
      //    >< PARSER.pending
      //     < PARSER.lock
      //    flat()
      //    PARSER.features()
      //    PARSER.finish()
      // 2022-08-09 PerfektesChaos@de.wikipedia
      var s = all.substring( at,  at + 10 ).toLowerCase(),
          k, q;
      if ( s === "references" ) {
         k = flat( all,  at + 10 );
         if ( k === 0x20 ) {
            q = PARSER.features( all,  at + 10,  after,  false );
            if ( q  &&  q.group  &&  ! q.lone ) {
               PARSER.pending = { shared: q.group };
               q = PARSER.finish( all,  at + 10,  false );
               if ( q  ) {
                  PARSER.pending.max = q[ 0 ];
               } else {
                  PARSER.pending.max = all.length;
                  PARSER.lock        = true;
               }
            }
         } else if ( PARSER.pending ) {
            PARSER.pending = false;
         }
      }
   };   // PARSER.frontal()



   SOURCE.favour = function () {
      // Open new browser tab for doc page
      // Uses:
      //    >  THIS.doc
      //    >  Signature
      // 2022-07-01 PerfektesChaos@de.wikipedia
      if ( typeof THIS.doc  ===  "object"
           &&     THIS.doc   &&
           typeof THIS.doc.server  ===  "string"
           &&     THIS.doc.server   &&
           typeof THIS.doc.support  ===  "string"
           &&     THIS.doc.support ) {
         window.open( THIS.doc.server + "Special:MyLanguage/"
                                      + THIS.doc.support,
                      Signature );
         }
   };   // SOURCE.favour()



   SOURCE.fiat = function () {
      // Execute name exchange
      // Uses:
      //    >  SOURCE.$doit
      //    >  SOURCE.$wait
      //    >  SOURCE.exec
      //    >  SOURCE.$editor
      //    >  SOURCE.$input
      //    ><  VIEW.offered
      //     < SOURCE.grouped
      //     < SOURCE.groups
      //    PARSER.fire()
      //    SOURCE.fill()
      //    SOURCE.focus()
      // 2022-07-01 PerfektesChaos@de.wikipedia
      var o;
      SOURCE.$doit.hide();
      SOURCE.$wait.show();
      o = PARSER.fire( false, SOURCE.exec );
      if ( o ) {
         SOURCE.$editor.hide();
         SOURCE.$wait.hide();
         SOURCE.groups  = o.groups;
         SOURCE.grouped = o.grouped;
         if ( typeof VIEW.offered[ SOURCE.exec.seek ]  ===  "object" ) {
            delete VIEW.offered[ SOURCE.exec.seek ];
         }
         SOURCE.fill( false );
      } else {
         SOURCE.$input.css( { "border-color": "#FF0000" } );
         SOURCE.focus();
      }
   };   // SOURCE.fiat()



   SOURCE.field = function () {
      // Create edit field
      // Uses:
      //    >  THIS.selector
      //    >  THIS.ltr
      //    >  SOURCE.pics.exch
      //    >  SOURCE.pics.wait
      //     < SOURCE.$editor
      //     < SOURCE.$input
      //     < SOURCE.$deny
      //     < SOURCE.$doit
      //     < SOURCE.$wait
      //     < SOURCE.$abort
      //     < SOURCE.$story
      //    SOURCE.fresh()
      //    $file()
      // 2022-07-01 PerfektesChaos@de.wikipedia
      var $buttons = $( "<div>" ),
          $ruler   = $( "<div>" ),
          m        = 22,
          $img;
      SOURCE.$editor = $( "<div>" );
      SOURCE.$editor.addClass( THIS.selector + "editor" )
                    .css( { "position":         "absolute",
                            "text-align":       ( THIS.ltr ? "left"
                                                           : "right" ),
                            "z-index":          "1" } );
      SOURCE.$input = $( "<input>" );
      SOURCE.$input.addClass( [ THIS.selector + "input",
                                "noime" ] )
                   .attr( { "maxlength": "100",
                            "size":      "42",
                            "type":      "text" } )
                   .css( { "border-color":  "transparent",
                           "border-radius": "2px",
                           "border-style":  "solid",
                           "border-width":  "2px",
                           "margin-left":   ( THIS.ltr ? "0"
                                                       : "auto" ),
                           "margin-right":  ( THIS.ltr ? "auto"
                                                       : "0" ) } );
      SOURCE.fresh();
      $img = $file( SOURCE.pics.exch, m );
      SOURCE.$deny = $( "<div>" );
      SOURCE.$deny.append( $img )
                  .css( { "background-color": "#808080",
                          "border-color":     "transparent",
                          "border-radius":    "4px",
                          "border-style":     "solid",
                          "border-width":     "2px",
                          "float":            ( THIS.ltr ? "left"
                                                         : "right" ) } );
      SOURCE.$doit = $( "<div>" );
      SOURCE.$doit.addClass( THIS.selector + "button" )
                  .append( $img.clone() )
                  .attr( { "lang":  "en",
                           "role":  "button",
                           "title": "exchange!" } )
                  .css( { //         "background-color": "#3366CC",
                          "border-color":  "transparent",
                          "border-radius": "4px",
                          "border-style":  "solid",
                          "border-width":  "4px",
                          "cursor":        "pointer",
                          "float":         ( THIS.ltr ? "left"
                                                      : "right" ) } )
                  .hide();
      $img = $file( SOURCE.pics.wait, m );
      SOURCE.$wait = $( "<div>" );
      SOURCE.$wait.append( $img )
                  .attr( { "title": "..." } )
                  .css( { "border-color":  "transparent",
                          "border-radius": "4px",
                          "border-style":  "solid",
                          "border-width":  "4px",
                          "float":         ( THIS.ltr ? "left"
                                                      : "right" ) } )
                  .hide();
      SOURCE.$abort = $( "<div>" );
      SOURCE.$abort.addClass( THIS.selector + "abort" )
                   .attr( { "lang":  "en",
                            "role":  "button",
                            "title": "abort" } )
                   .css( { "border-color":     "transparent",
                           "border-radius":    "4px",
                           "border-style":     "solid",
                           "border-width":     "2px",
                           "color":            "#FFFFFF",
                           "cursor":           "pointer",
                           "float":            ( THIS.ltr ? "right"
                                                          : "left" ),
                           "font-family":      "sans-serif",
                           "font-size":        ( m - 4 )  +  "px",
                           "font-weight":      "bold",
                           "min-width":        m + "px",
                           "text-align":       "center",
                           "vertical-align":   "middle" } )
                   .text( "×" );
      $buttons.append( SOURCE.$deny )
              .append( SOURCE.$doit )
              .append( SOURCE.$wait )
              .append( SOURCE.$abort )
              .css( { "margin-top": "1em",
                      "width":      "100%" } );
      $ruler.css( { "clear": "both" } );
      SOURCE.$story = $( "<textarea>" );
      SOURCE.$story.addClass( THIS.selector + "content" )
                   .attr( { "readonly": "readonly",
                            "rows":     "5" } )
                   .css( { "margin-top": "1em",
                           "max-width":  "100%",
                           "min-width":  "100%",
                           "overflow-y": "auto" } );
      SOURCE.$editor.append( SOURCE.$input )
                    .append( $buttons )
                    .append( $ruler )
                    .append( SOURCE.$story );
   };   // SOURCE.field()



   SOURCE.fill = function ( adjust ) {
      // Show list of groups
      // Precondition:
      //    adjust    -- boolean, if assembly needs initialization
      // Uses:
      //    >  SOURCE.$box
      //    >  SOURCE.locked
      //    >  PARSER.lock
      //    >  SOURCE.grouped
      //    >  VIEW.offered
      //    >  THIS.selItem
      //    >  THIS.selProblem
      //    >  SOURCE.pics.edit
      //    >  SOURCE.pics.drag
      //    >< SOURCE.groups
      //     < SOURCE.lock
      //    fruit()
      //    $flag()
      //    $fault()
      //    $file()
      //    (SOURCE.further)
      //    (SOURCE.flip)
      //    (SOURCE.flopping)
      // 2022-07-25 PerfektesChaos@de.wikipedia
      var css = { "display":      "inline-block",
                  "margin-left":  "1em",
                  "margin-right": "1em" },
          n   = SOURCE.groups.length,
          e, group, i, k, o, s, $head, $img, $item, $li, $ul;
      if ( ! adjust ) {
         SOURCE.$box.empty();
      }
      if ( !  ( SOURCE.locked || PARSER.lock ) ) {
         SOURCE.lock = false;
      }
      if ( n > 1 ) {
         SOURCE.groups.sort();
         if ( ! SOURCE.groups[ n - 1 ] ) {
            SOURCE.groups.pop();
            SOURCE.groups.unshift( false );
         }
      }
      for ( i = 0;  i < n;  i++ ) {
         s     = SOURCE.groups[ i ];
         group = SOURCE.grouped[ s ];
         for ( k = 0;  k < group.length;  k++ ) {
            e = group[ k ];
            if ( e.lone ) {
               PARSER.lock = true;
            }
         }   // for k
      }   // for i
      for ( i = 0;  i < n;  i++ ) {
         s     = SOURCE.groups[ i ];
         group = SOURCE.grouped[ s ];
         if ( s ) {
            $head = $( "<div>" );
            $head.addClass( THIS.selItem )
                 .text( "<ref group=\"" + s + "\">" );
            SOURCE.$box.append( $head );
         }
         $ul = $( "<ul>" );
         for ( k = 0;  k < group.length;  k++ ) {
            e = group[ k ];
            if ( typeof VIEW.offered[ e.name ]  ===  "object" ) {
               o = VIEW.offered[ e.name ];
               if ( o.lone && o.content ) {
                  e.content = o.content;
               }
            } else {
               o = false;
            }
            e.content = fruit( e.content );
            $item = $flag( e.name );
            $item.attr( { "title": e.content } );
            $li = $( "<li>" );
            $li.append( $item )
               .css( { "position": "relative" } );
            if ( e.lone ) {
               $li.append( $fault( "<ref />" ) );
               $item = $( "<span>" );
               $item.addClass( THIS.selProblem )
                    .css( css )
                    .html( "&lt;ref&#160;/&gt;" );
               $li.append( $item );
            } else if ( e.multi ) {
               s = ( e.multi + 1 ) + "×";
               $li.append( $fault() );
               $item = $( "<span>" );
               $item.addClass( THIS.selProblem )
                    .css( css )
                    .text( s );
               $li.append( $item );
            } else if ( o  &&  o.sign ) {
               $item.attr( { "role": "link" } )
                    .click( o.sign, SOURCE.fragment )
                    .css( { "cursor": "pointer" } );
            }
            if ( ! PARSER.lock ) {
               $img = $file( SOURCE.pics.edit, 16 );
               $item = $( "<span>" );
               $item.append( $img )
                    .attr( { "lang":  "en",
                             "role":  "button",
                             "title": "edit" } )
                    .click( [ $li, e, s ],  SOURCE.flip )
                    .css( css )
                    .css( { "cursor": "pointer" } );
               $li.append( $item );
               if ( ! e.linked ) {
                  $img = $file( SOURCE.pics.drag, 16 );
                  $item = $( "<span>" );
                  $item.append( $img )
                       .attr( { "lang":  "en",
                                "role":  "button",
                                "title": "discard" } )
                       .click( [ $li, e, s, $item, css ],
                               SOURCE.flopping )
                       .css( css )
                       .css( { "cursor": "pointer" } );
                  $li.append( $item );
               }
            }
            $ul.append( $li );
         }   // for k
         SOURCE.$box.append( $ul );
      }   // for i
   };   // SOURCE.fill()



   SOURCE.first = function ( always ) {
      // Initialize source evaluation
      // Precondition:
      //    always  -- boolean, if text availability does not matter
      //    mediawiki.util available
      // Uses:
      //    >  SOURCE.before
      //    >  THIS.selItem
      //    >  THIS.selector
      //    >  SOURCE.section
      //    >  THIS.show
      //     < SOURCE.grouped
      //     < SOURCE.groups
      //     < SOURCE.$wrapper
      //     < SOURCE.$head
      //     < SOURCE.$box
      //     < SOURCE.locked
      //    PARSER.fire()
      //    mw.util.getParamValue()
      //    SOURCE.fill()
      //    (SOURCE.favour)
      // 2022-08-08 PerfektesChaos@de.wikipedia
      var collection = PARSER.fire( always ),
          i, $before, $editform, $head, $header;
      if ( collection ) {
         SOURCE.grouped = collection.grouped;
         SOURCE.groups  = collection.groups;
         $editform = THIS.$content.find( ".mw-editform" );
         for ( i = 0;  i < SOURCE.before.length;  i++ ) {
            $before = $editform.find( SOURCE.before[ i ] );
            if ( $before.length === 1 ) {
               SOURCE.$wrapper = $( "<div>" );
               $header = $( "<div>" );
               $head   = $( "<span>" );
               $head.addClass( THIS.selItem )
                    .attr( { "id":    SOURCE.section,
                             "lang":  "en",
                             "role":  "link",
                             "title": "help" } )
                    .click( SOURCE.favour )
                    .css( { "cursor":    "pointer",
                            "font-size": "130%" } )
                    .html( THIS.show );
               $header.append( $head );
               SOURCE.$box = $( "<div>" );
               SOURCE.$wrapper.addClass( THIS.selector + "wrapper" )
                              .append( $header )
                              .append( SOURCE.$box );
               $before.before( SOURCE.$wrapper );
               SOURCE.locked = mw.util.getParamValue( "section" );
               SOURCE.fill( true );
               break;   // for i
            }
         }   // for i
      }
   };   // SOURCE.first()



   SOURCE.flash = function ( about ) {
      // Removing requested
      // Precondition:
      //    about  -- Event object, with Array .data[ 6 ]
      // Uses:
      //    >  VIEW.offered
      //     < SOURCE.lock
      //    PARSER.fire()
      // 2022-07-29 PerfektesChaos@de.wikipedia
      var $li   = about.data[ 0 ],
          pre   = about.data[ 1 ],
          group = about.data[ 2 ],
          $drag = about.data[ 3 ],
          $drop = about.data[ 5 ];
         if ( $drop ) {
            $drop.remove();
         }
      if ( PARSER.fire( false,
                        { group: group,
                          seek:  pre.name,
                          shift: false } ) ) {
         if ( typeof VIEW.offered[ pre.name ]  ===  "object" ) {
            delete VIEW.offered[ pre.name ];
         }
         pre.name = false;
         $li.remove();
      } else if ( $drag ) {
         $drag.show();
      }
      SOURCE.lock = false;
   };   // SOURCE.flash()



   SOURCE.flip = function ( about ) {
      // Editing requested
      // Precondition:
      //    about  -- Event object, with Array .data[ 3 ]
      // Uses:
      //    >  SOURCE.$input
      //    >  SOURCE.$story
      //    >  SOURCE.$doit
      //    >  SOURCE.$abort
      //    >< SOURCE.lock
      //    >< SOURCE.$editor
      //     < SOURCE.pre
      //     < SOURCE.start
      //    SOURCE.focus()
      //    SOURCE.field()
      //    (SOURCE.formal)
      //    (SOURCE.focus)
      //    (SOURCE.fresh)
      //    (SOURCE.fiat)
      //    (SOURCE.flush)
      // 2022-07-01 PerfektesChaos@de.wikipedia
      var $li;
      if ( ! SOURCE.lock ) {
         SOURCE.lock      = true;
         $li              = about.data[ 0 ];
         SOURCE.pre       = about.data[ 1 ];
         SOURCE.pre.group = about.data[ 2 ];
         if ( SOURCE.$editor ) {
            SOURCE.$input.attr( { "readonly": null } );
            SOURCE.focus();
            SOURCE.$editor.show();
         } else {
            SOURCE.field();
         }
         SOURCE.start = SOURCE.pre.name;
         SOURCE.$input.val( SOURCE.start );
         SOURCE.$story.val( SOURCE.pre.content );
         $li.append( SOURCE.$editor );
         SOURCE.$input.blur( SOURCE.formal )
                      .focus( SOURCE.focus )
                      .keyup( SOURCE.fresh )
                      .mousedown( SOURCE.fresh )
                      .mouseout( SOURCE.formal )
                      .focus();
         SOURCE.$doit.click( SOURCE.fiat );
         SOURCE.$abort.click( SOURCE.flush );
      }
   };   // SOURCE.flip()



   SOURCE.flop = function ( about ) {
      // Offer removal
      // Precondition:
      //    about  -- Event object, with Array[ 6 ]
      // Uses:
      //    >  SOURCE.lock
      //    (SOURCE.flash)
      // 2022-07-01 PerfektesChaos@de.wikipedia
      var $drop;
      if ( SOURCE.lock ) {
         $drop = about[ 5 ];
         $drop.click( about, SOURCE.flash )
              .css( { "background-color": "#FF0000",
                      "cursor":           "pointer" } );
      }
   };   // SOURCE.flop()



   SOURCE.flopped = function ( about ) {
      // Abort removal
      // Precondition:
      //    about  -- Event object, with Array .data[ 6 ]
      // Uses:
      //    >< SOURCE.lock
      // 2022-07-29 PerfektesChaos@de.wikipedia
      var $drag, $drop;
      if ( SOURCE.lock ) {
         $drag = about.data[ 3 ];
         $drop = about.data[ 5 ];
         if ( $drop ) {
            $drop.remove();
         }
         if ( $drag ) {
            $drag.show();
         }
         SOURCE.lock = false;
      }
   };   // SOURCE.flopped()



   SOURCE.flopping = function ( about ) {
      // Preparing removal
      // Precondition:
      //    about  -- Event object, with Array .data[ 5 ]
      // Uses:
      //    >  SOURCE.pics.drop
      //    >  THIS.selector
      //    >< SOURCE.lock
      //    (SOURCE.flop)
      //    (SOURCE.flopped)
      // 2022-07-01 PerfektesChaos@de.wikipedia
      var css, discard, e, s, $drag, $img, $drop, $li;
      if ( ! SOURCE.lock ) {
         SOURCE.lock = true;
         $li         = about.data[ 0 ];
         e           = about.data[ 1 ];
         s           = about.data[ 2 ];
         $drag       = about.data[ 3 ];
         css         = about.data[ 4 ];
         $drag.focus()
              .hide();
         $img    = $file( SOURCE.pics.drop, 16 );
         $drop   = $( "<span>" );
         discard = [ $li, e, s, $drag, css, $drop ];
         $drop.addClass( THIS.selector + "discard" )
              .append( $img )
              .attr( { "lang":  "en",
                       "role":  "button",
                       "title": "discard" } )
              .css( css )
              .css( { "background-color": "#FFB0B0",
                      "border-color":     "transparent",
                      "border-radius":    "2px",
                      "border-style":     "solid",
                      "border-width":     "2px",
                      "cursor":           "none",
                      "transition":       "background-color 2s" } )
              .focusout( discard, SOURCE.flopped )
              .mouseout( discard, SOURCE.flopped );
         $li.append( $drop );
         $drop.focus();
         window.setTimeout( SOURCE.flop, 1500, discard );
      }
   };   // SOURCE.flopping()



   SOURCE.flush = function () {
      // Terminate editing
      // Postcondition:
      //    $editor is invisible, other items ready to edit
      // Uses:
      //    >  SOURCE.$editor
      //     < SOURCE.lock
      // 2022-07-01 PerfektesChaos@de.wikipedia
      SOURCE.$editor.detach()
                    .hide();
      SOURCE.lock = false;
   };   // SOURCE.flush()



   SOURCE.focus = function () {
      // Input field got focus
      // Uses:
      //    >  SOURCE.$doit
      //    >  SOURCE.$wait
      //    >  SOURCE.$deny
      // 2022-07-01 PerfektesChaos@de.wikipedia
      SOURCE.$doit.hide();
      SOURCE.$wait.hide();
      SOURCE.$deny.show();
   };   // SOURCE.focus()



   SOURCE.formal = function () {
      // Input field lost focus, check data
      // Uses:
      //    >  SOURCE.$input
      //    >  SOURCE.start
      //    >  SOURCE.groups
      //    >  SOURCE.grouped
      //    >  SOURCE.pre
      //    >  SOURCE.start
      //    >  SOURCE.$deny
      //    >  SOURCE.$doit
      //     < SOURCE.exec
      //    SOURCE.fresh()
      //    SOURCE.focus()
      // 2023-05-01 PerfektesChaos@de.wikipedia
      var s      = fruit( PARSER.focus( SOURCE.$input.val() ) ),
          group, i, lapsus;
      SOURCE.$input.val( s );
      if ( s ) {
         lapsus = failure( s );
      } else {
         lapsus = false;
      }
      if ( s === SOURCE.start ) {
         SOURCE.focus();
      } else if ( ! lapsus ) {
         SOURCE.fresh();
         group = SOURCE.grouped[ SOURCE.pre.group ];
         if ( group ) {
            for ( i = 0;  i < group.length;  i++ ) {
               if ( s === group[ i ].name ) {
                  lapsus = true;
                  break;   // for i
               }
            }   // for i
            if ( ! lapsus ) {
               SOURCE.exec = { group: SOURCE.pre.group,
                               seek:  SOURCE.start,
                               shift: s };
               SOURCE.$deny.hide();
               SOURCE.$doit.show();
            }
         }
      }
      if ( lapsus ) {
         SOURCE.$input.css( { "border-color": "#FF0000" } );
         SOURCE.focus();
      } else {
         SOURCE.fresh();
      }
   };   // SOURCE.formal()



   SOURCE.forward = function () {
      // Jump to section
      // Uses:
      //    >  SOURCE.section
      // 2022-08-08 PerfektesChaos@de.wikipedia
      var url = window.location;
      url.hash = "#" + SOURCE.section;
      window.location = url;
   };   // SOURCE.forward()



   SOURCE.fragment = function ( about ) {
      // Jump to fragment
      // Precondition:
      //    about  -- Event object, with string of fragment
      // 2022-07-01 PerfektesChaos@de.wikipedia
      var url = window.location;
      url.hash = "#" + about.data;
      window.location = url;
   };   // SOURCE.fragment()



   SOURCE.fresh = function () {
      // Input event on field
      // Uses:
      //    >  SOURCE.$input
      // 2022-07-01 PerfektesChaos@de.wikipedia
      SOURCE.$input.css( { "border-color":  "#3366CC" } );
   };   // SOURCE.fresh()



   VIEW.field = function ( at, apply ) {
      // Equip references list item -- $.each()
      // Precondition:
      //    at     -- number, with sequence index
      //    apply  -- object, with <li> DOM
      // Postcondition:
      //    Returns true -- continue $.each()
      // Uses:
      //    >  VIEW.reNamed
      //    >< VIEW.offered
      //    fair()
      //    $flag()
      // 2022-07-01 PerfektesChaos@de.wikipedia
      var $li = $( apply ),
          sel = $li.attr( "id" ),
          got, s;
      if ( sel ) {
         got = VIEW.reNamed.exec( sel );
         if ( got ) {
            s = fair( got[ 1 ] );
            $li.prepend( $flag( s ) );
            if ( typeof VIEW.offered[ s ]  ===  "object" ) {
               VIEW.offered[ s ] = { lone: false };
            } else {
               VIEW.offered[ s ] = {
                  content: $li.find( ".reference-text" )
                              .text()
                              .trim(),
                  lone:    true,
                  sign:    sel
               };
            }
         }
      }
      return true;
   };   // VIEW.field()



   VIEW.fill = function ( at, apply ) {
      // Equip references list -- $.each()
      // Precondition:
      //    at     -- number, with sequence index
      //    apply  -- object, with <ol> DOM
      // Postcondition:
      //    Returns true -- continue $.each()
      // Uses:
      //    (VIEW.field)
      // 2022-07-01 PerfektesChaos@de.wikipedia
      var $ol = $( apply );
      $ol.children( "li" ).each( VIEW.field );
      return true;
   };   // VIEW.fill()



   VIEW.fire = function () {
      // Equip all references lists
      // Uses:
      //    >  THIS.$content
      //    >  THIS.lapsus
      //     < THIS.$references
      //     < VIEW.reNamed
      //     < VIEW.offered
      //    mw.hook()
      //    (VIEW.fill)
      // 2022-07-01 PerfektesChaos@de.wikipedia
      THIS.$references = THIS.$content.find( ".references" );
      if ( THIS.$references.length ) {
         VIEW.reNamed = new RegExp( "^cite_note-(.+)-\\d+$" );
         VIEW.offered = { };
         THIS.$references.each( VIEW.fill );
         if ( THIS.lapsus ) {
            mw.hook( "remindErrorMessages.refresh" )
                                               .fire( THIS.$references );
         }
      }
   };   // VIEW.fire()



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



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

/// EOF </nowiki>   refNames/core/d.js