Jump to content

User:PerfektesChaos/js/tableXpander/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.
/// tableXpander.js
/// 2023-04-19 PerfektesChaos@de.wikipedia
/// Documentation:  [[w:en:User:PerfektesChaos/js/tableXpander]]
/// Fingerprint:    #0#0#
/// @license: CC-by-sa/4.0 GPLv3
/// <nowiki>
/* global window:false                                                 */
/* jshint forin:false,
          bitwise:true, curly:true, eqeqeq:true, latedef:true,
          laxbreak:true,
          nocomma:true, strict:true, undef:true, unused:true           */
( function ( mw, $ ) {
   "use strict";
   var Version   = -2.2,
       Signature = "tableXpander",
       API       = { Api: false },
       PAGE      = { show:      Signature + "@PerfektesChaos",
                     sign:      "|}===={|",
                     support:   "https://en.wikipedia.org/wiki/"
                                + "Special:MyLanguage/"
                                + "User:PerfektesChaos/js/"
                                + Signature,
                     $textarea: false },
       CSS       = { background: "FFF8DC",
                     color:      "006400" },
       NUM       = { structure: "dot;comma" },
       TABLE     = { },
       REPO      = { };



   CSS.design = { "background": "#" + CSS.background,
                  "color":      "#" + CSS.color };
   CSS.def    = { "active":
                          { "background-color": CSS.design.background
                                                        + "!important" },
                  "box":  { "background-color": CSS.design.background,
                            "border-color":     CSS.design.color,
                            "border-radius":    "6px",
                            "border-style":     "solid",
                            "border-width":     "3px",
                            "clear":            "both",
                            "margin-bottom":    "2em",
                            "margin-left":      "1%",
                            "margin-right":     "1%",
                            "margin-top":       "1em",
                            "padding":          "0.5em",
                            "width":            "95%" },
                  "caption":
                          {  },
                  "cell": { "display":          "table-cell",
                            "vertical-align":   "middle" },
                  "chosen":
                          { "background-color": CSS.design.color,
                            "color":            CSS.design.background,
                            "font-weight":      "bold",
                            "padding":          "0.1em" },
                  "clickable":
                          { "color":            CSS.design.color,
                            "text-decoration":  "none" },
                  "clickable::link":
                          { "color":            CSS.design.color },
                  "doclink":
                          { "font-size":        "120%",
                            "font-weight":      "bolder" },
                  "grouping":
                          { "background-color": CSS.design.color,
                            "color":            CSS.design.background,
                            "font-weight":      "bold",
                            "padding":          "0.3em" },
                  "handle":
                          { "background-color": CSS.design.background,
                            "border-color":     CSS.design.color,
                            "border-radius":    "3px",
                            "border-style":     "solid",
                            "border-width":     "2px",
                            "color":            CSS.design.color,
                            "font-size":        "100%",
                            "font-style":       "normal",
                            "font-weight":      "normal",
                            "padding":          "0.2em" },
                  "icon": { "border-radius":    "0.4em",
                            "border-style":     "solid",
                            "border-width":     "0.15em",
                            "display":          "inline-block",
                            "font-size":        "150%",
                            "font-weight":      "bolder",
                            "margin":           "0.5em",
                            "padding":          "0.3em" },
                  "include":
                          { "background-color": CSS.design.background },
                  "info": { "border-color":     CSS.design.color,
                            "border-radius":    "3px",
                            "border-style":     "solid",
                            "border-width":     "1px",
                            "font-family":      "monospace",
                            "font-size":        "100%",
                            "font-style":       "normal",
                            "font-weight":      "normal",
                            "padding-left":     "0.1em",
                            "padding-right":    "0.1em"
                             },
                  "quit": { "color":            "#FF0000",
                            "font-size":        "150%",
                            "font-weight":      "700",
                            "text-align":       "center",
                            "vertical-align":   "middle"},
                  "td":   { "background-color": CSS.design.background,
                            "color":            CSS.design.color,
                            "font-size":        "100%",
                            "font-style":       "normal",
                            "font-weight":      "normal",
                            "text-align":       "center" },
                  "textarea":
                          { "background-color": CSS.design.background,
                            "margin-bottom":    "2em",
                            "margin-top":       "1em",
                            "width":            "95%" },
                  "toggle":
                          { "display":          "inline-block",
                            "font-size":        "80%",
                            "font-style":       "normal",
                            "font-weight":      "normal",
                            "margin":           "0.2em" },
                  "ul":   { "list-style-image": "none",
                            "list-style-position": "outside",
                            "list-style-type":  "none",
                            "margin-bottom":    "0.2em",
                            "margin-left":      "0",
                            "margin-right":     "0",
                            "margin-top":       "0.2em",
                            "padding":          "0" },
                  "ul > li":
                          { "display":          "inline",
                            "margin-right":     "0.7em",
                            "padding":          "0" },
                  "ul > li::after":
                          { "content":          " \n" },
                  "ul > li:last-child":
                          { "margin-right":     "0" },
                  "version":
                          { "font-size":        "80%" },
                  "widget":
                          { "margin-left":      "0.8em",
                            "margin-right":     "0.8em" }
                };
   NUM.call      = { "#": "forward",
                     "+": "full",
                     "~": "fair",
                     "%": "fraction",
                     "?": "fruits" };
   NUM.codes     = { apos:  0x27,
                     comma: 0x2C,
                     dot:   0x2E,
                     nbsp:  0xA0,
                     nnbsp: 0x202F };
   NUM.entities  = { apos:  0x27,
                     nbsp:  0xA0,
                     lrm:   false,
                     rlm:   false,
                     zwj:   false };
   NUM.min       = 0.0001;
   NUM.offer     = [ "dot;comma",
                     "comma;dot",
                     "comma;apos",
                     "comma;nbsp",
                     "dot;nbsp",
                     "comma;nnbsp",
                     "dot;nnbsp" ];
   NUM.patterns  = { "dot;comma":   "0,000.00",
                     "dot;nbsp":    "0&#x25AF;000.00",
                     "dot;nnbsp":   "0&#x25A1;000.00",
                     "comma;apos":  "0'000,00",
                     "comma;dot":   "0.000,00",
                     "comma;nbsp":  "0&#x25AF;000,00",
                     "comma;nnbsp": "0&#x25A1;000,00" };
   NUM.translate = { 0x2D:     45,  // -
                     0x2212:   45,  // typographic -
                     0x2B:     43,  // +
                     0xFF0B:   43,
                     0xFF0D:   45 };
   NUM.seek      = NUM.structure;
   NUM.sep       = ".";
   NUM.spaces    = [ 0xA0,
                     0x1680,
                     0x2000,
                     0x2001,
                     0x2002,
                     0x2003,
                     0x2004,
                     0x2005,
                     0x2006,
                     0x2007,
                     0x2008,
                     0x2009,
                     0x200A,
                     0x200B,
                     0x200C,
                     0x200D,
                     0x200E,
                     0x200F,
                     0x202F,
                     0x205F,
                     0x3000,
                     0x303F ];
   NUM.starts    = "#+~%?";
   NUM.variants  = { apos:  [ 0x2019,
                              0xFF07 ],
                     comma: [ 0xFF0C ],
                     dot:   [ 0xFF0E ] };
   NUM.zero      = [    0x30,
                      0x0660,
                      0x06F0,
                      0x07C0,
                      0x0966,
                      0x09E6,
                      0x0A66,
                      0x0AE6,
                      0x0B66,
                      0x0BE6,
                      0x0C66,
                      0x0CE6,
                      0x0D66,
                      0x0DE6,
                      0x0E50,
                      0x0ED0,
                      0x0F20,
                      0x102E,
                      0x1040,
                      0x1369,
                      0x17E0,
                      0x1810,
                      0x1946,
                      0x19D0,
                      0x1A80,
                      0x1A90,
                      0x1B50,
                      0x1BB0,
                      0x1C40,
                      0x1C50,
                      0xA620,
                      0xA8D0,
                      0xA900,
                      0xA9D0,
                      0xA9F0,
                      0xAA50,
                      0xABF0,
                      0xFF10,
                     0x1104A,
                     0x110D3,
                     0x110E6,
                     0x11106,
                     0x1110F,
                     0x11113,
                     0x1111D,
                     0x1112F,
                     0x11145,
                     0x1114D,
                     0x11165,
                     0x1116C,
                     0x11173,
                     0x1118E,
                     0x111C5,
                     0x111D5,
                     0x111DA,
                     0x116A6,
                     0x116B5,
                     0x116E8,
                     0x11E14,
                     0x11E2F,
                     0x11E8C,
                     0x11E95,
                     0x1D7F6,
                     0xE0030 ];
   TABLE.title   = { "#": "#",
                     "+": String.fromCharCode( 0x03A3 ),   // Sigma
                     "~": String.fromCharCode( 0x00F8 ),   // Oslash
                     "%": "%",
                     "?": "^",
                     "h": String.fromCharCode( 0x2194 ),   // <-->
                     "v": String.fromCharCode( 0x2195 ),   // ^
                     "X": "X"
                   };



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



   function fire() {
      // Start processing
      // Uses:
      //    >  $
      //    (PAGE.fire)
      // 2017-11-30 PerfektesChaos@de.wikipedia
      if ( $ ) {
         if ( typeof $  ===  "function" ) {
            $( PAGE.fire );
         }
      }
   }   // fire()



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



   API.formatnum = function () {
      // Launch API query for number formatting
      // Uses:
      //    >< API.Api
      //    (API.formattednum)
      //    (API.fail)
      // 2017-05-31 PerfektesChaos@de.wikipedia
      var o = { action: "expandtemplates",
                prop:   "wikitext",
                text:   "{{formatnum:1234567.89}}" };
      if ( typeof API.Api  !==  "object" ) {
         API.Api = new mw.Api();
      }
      API.Api.get( o ).done( API.formattednum )
                      .fail( API.fail );
   };   // API.formatnum()



   API.formattednum = function ( arrived ) {
      // API success on parsed number formatting
      // Precondition:
      //    arrived  -- JSON result of ajax query
      // Uses:
      //    NUM.formatting()
      // 2017-05-31 PerfektesChaos@de.wikipedia
      if ( typeof arrived  ===  "object"
           &&     arrived    &&
           typeof arrived.expandtemplates  ===  "object"    &&
           typeof arrived.expandtemplates.wikitext  ===  "string" ) {
         NUM.formatting( arrived.expandtemplates.wikitext );
      }
   };   // API.formattednum()



   NUM.faculty = function ( attempt, array ) {
      // Precondition:
      //    attempt  -- character code to be checked
      //    access   -- Array to be checked against, or not
      // Postcondition:
      //    Returns true if character might be ignored
      // 2017-11-30 PerfektesChaos@de.wikipedia
      var i, r;
      if ( array ) {
         for ( i = 0;  i < array.length;  i++ ) {
            if ( array[ i ] === attempt ) {
               r = true;
               break;   // for i
            }
         }   // for i
      }
      return r;
   };   // NUM.faculty()



   NUM.fair = function ( $all, $actives, $about ) {
      // Average of all figures
      // Precondition:
      //    $all      -- <table>
      //    $actives  -- active cells
      //    $about    -- <code> for result
      // Uses:
      //    >  Signature
      //    NUM.full()
      // 2020-08-17 PerfektesChaos@de.wikipedia
      var n = $actives.length,
          g;
      if ( n ) {
         g = NUM.full( $all, $actives, $about );
         if ( g[ 0 ]  &&  g[ 1 ] ) {
            n = g[ 0 ]  /  g[ 1 ];
         } else {
            n = 0;
         }
      }
      $about.text( n );
   };   // NUM.fair()



   NUM.fetch = function ( $at ) {
      // Retrieve number
      // Precondition:
      //    $at  -- <td> / <th>
      // Uses:
      //    >  Signature
      //    >  NUM.reInteger
      //    NUM.fill()
      // 2020-08-25 PerfektesChaos@de.wikipedia
      var s       = $at.data( Signature + "-parsed" ),
          skipped = ":hidden,[style*='visibility']",
          r, store, $dup;
      if ( s ) {
         if ( s !== "!" ) {
            r = parseInt( s, 10 );
            s = false;
         }
      } else {
         s = $at.data( Signature + "-raw" );
         if ( ! s ) {
            s = $at.data( "sort-value" );
            if ( s ) {
               $at.attr( { title: s } );
            } else {
               if ( $at.find( skipped ).length > 1 ) {
                  $dup = $at.clone();
                  $dup.find( ":hidden" ).remove();
                  s = $dup.text();
               } else {
                  s = $at.text();
               }
            }
            if ( s ) {
               s = NUM.fill( s );
               if ( s ) {
                  if ( NUM.reInteger.test( s ) ) {
                     store = "-parsed";
                  } else {
                     store = "-raw";
                  }
                  $at.data( Signature + store,  s );
               }
            }
         }
         if ( s ) {
            s = NUM.filter( s );
            if ( s ) {
               r = parseFloat( s );
               if ( isNaN( r) ) {
                  r = false;
               }
            }
         } else {
            $at.data( Signature + "-parsed",  "!" );
         }
      }
      return r;
   };   // NUM.fetch()



   NUM.figure = function ( analyze ) {
      // Identify digit in scripting systems
      // Precondition:
      //    analyze  -- character code
      // Postcondition:
      //    Returns code of zero, or not
      // Uses:
      //    >  NUM.zero
      // 2017-11-30 PerfektesChaos@de.wikipedia
      var i, j, r;
      for ( i = 0;  i < NUM.zero.length;  i++ ) {
         j = NUM.zero[ i ];
         if ( analyze < j ) {
            break;   // for i
         } else {
            if ( analyze  <=  j + 9 ) {
               r = j;
               break;   // for i
            }
         }
      }   // for i
      return r;
   };   // NUM.figure()



   NUM.fill = function ( analyze ) {
      // Normalize number string
      // Precondition:
      //    analyze  -- string, with number to be parsed
      // Postcondition:
      //    Returns normalized string
      // Uses:
      //    >  Signature
      //    >  NUM.reInteger
      //    >  NUM.translate
      //    NUM.flat()
      //    PAGE.flat()
      //    NUM.foreign()
      // 2020-08-17 PerfektesChaos@de.wikipedia
      var i = analyze.indexOf( "[-]" ),
          s = analyze,
          j, k, less, r;
      if ( i >= 0 ) {
         s = s.substr( 0, i );
      }
      s = PAGE.flat( s );
      s = NUM.flat( s );
      k = NUM.translate[ s.charCodeAt( 0 ) ];
      if ( k === 45  ||  k === 43 ) {
         less = ( k === 45 );
         s    = NUM.flat( s.substr( 1 ) );
      }
      j = NUM.figure( s.charCodeAt( 0 ) );
      if ( ! j ) {
         j = NUM.figure( s.charCodeAt( 1 ) );
      }
      if ( j ) {
         if ( j === 0x30 ) {
            r = s;
         } else {
            r = NUM.foreign( s, j );
         }
         for ( i = 1;  i < r.length;  i++ ) {
            if ( r.charCodeAt( i )  <=  32 ) {
               r = r.substr( 0, i );
               break;   // for i
            }
         }   // for i
         if ( less ) {
            r = "-" + r;
         }
      }
      return r;
   };   // NUM.fill()



   NUM.filter = function ( attempt ) {
      // Parse number according to current formatting
      // Precondition:
      //    attempt  -- string
      // Uses:
      //    >  NUM.seek
      //    >  NUM.codes
      //    >  NUM.variants
      //    >< NUM.dec
      //     < NUM.omit
      //    NUM.faculty()
      // 2020-07-25 PerfektesChaos@de.wikipedia
      var fill, i, k, large, left, less, m, r, seg, sep;
      if ( ! NUM.dec  &&
           typeof NUM.seek  ===  "string" ) {
         i = NUM.seek.indexOf( ";" );
         if ( i > 0 ) {
            sep = NUM.seek.substr( 0, i );
            seg = NUM.seek.substr( i + 1 );
         } else {
            sep = NUM.seek;
            seg = false;
         }
         NUM.omit = false;
         if ( sep   &&
              typeof NUM.codes[ sep ]  ===  "number" ) {
            fill = function ( array, assigned ) {
               var i, v;
               NUM[ array ] = [ NUM.codes[ assigned ] ];
               if ( typeof NUM.variants[ assigned ]  ===  "object" ) {
                  v = NUM.variants[ assigned ];
                  for ( i = 0;  i < v.length;  i++ ) {
                     NUM[ array ].push( v[ i ] );
                  }   // for i
               }
            };   // fill()
            fill( "dec", sep );
            if ( seg   &&
                 typeof NUM.codes[ seg ]  ===  "number" ) {
               fill( "omit", seg );
            }
         }
      }
      if ( NUM.dec ) {
         i = 0;
         if ( attempt.charCodeAt( 0 ) === 0x2D ) {
            less = true;
            i    = 1;
         }
         do {   // while k
            k = attempt.charCodeAt( i );
            if ( k >= 0x30  &&  k <= 0x39 ) {
               r = ( r || "" )  +  String.fromCharCode( k );
            } else if ( NUM.faculty( k, NUM.omit ) ) {
               k = attempt.charCodeAt( i + 1 );
               if ( ! r  ||  k < 0x30  ||  k > 0x39 ) {
                  break;   // do
               }
            } else if ( NUM.faculty( k, NUM.dec ) ) {
               if ( left ) {
                  break;   // do
               } else {
                  if ( r ) {
                     r = r + ".";
                  } else {
                     k = attempt.charCodeAt( i + 1 );
                     if ( k === 0x45  ||  k === 0x65 ) {
                        r = "0.";
                     } else {
                        break;   // do
                     }
                  }
                  left = true;
               }
            } else if ( k === 0x45  ||  k === 0x65 ) {
               if ( large ) {
                  break;   // do
               } else {
                  k = attempt.charCodeAt( i + 1 );
                  if ( k >= 0x30  &&  k <= 0x39 ) {
                     large = true;
                     r     = r + "e";
                  } else if ( k === 0x2B  ||
                              k === 0x2D  ||
                              k === 0x2E  ||
                              k === NUM.dec[ 0 ] ) {
                     m = k;
                     k = attempt.charCodeAt( i + 2 );
                     if ( k >= 0x30  &&  k <= 0x39 ) {
                        large = true;
                        switch ( m ) {
                           case 0x2D:
                              r = r + "e-";
                              break;
                           case 0x2E:
                              r = r + "e0.";
                              break;
                           default:
                              r = r + "e";
                        }   // switch m
                        i++;
                     }
                  }
                  if ( large ) {
                     left = false;
                  } else {
                     break;   // do
                  }
               }
            } else {
               break;   // do
            }
            i++;
         } while ( k );
         if ( r && less ) {
            r = "-" + r;
         }
      }
      return r;
   };   // NUM.filter()



   NUM.find = function ( assign ) {
      // Retrieve character name for formatting character
      // Precondition:
      //    assign  -- string with formatting character
      // Postcondition:
      //    Returns name of character
      // Uses:
      //    >  NUM.codes
      // 2017-11-30 PerfektesChaos@de.wikipedia
      var k, r, s;
      if ( assign.length === 1 ) {
         k = assign.charCodeAt( 0 );
         for ( s in NUM.codes ) {
            if ( NUM.codes[ s ] === k ) {
               r = s;
               break;   // for s in NUM.codes
            }
         }   // for s in NUM.codes
      }
      return r;
   };   // NUM.find()



   NUM.fire = function ( $all ) {
      // Requery
      // Precondition:
      //    $all  -- <table>
      // Uses:
      //    >  Signature
      //    NUM.first()
      // 2020-08-21 PerfektesChaos@de.wikipedia
      var $div = $all.find( "." + Signature + "-result" ),
          s    = $div.data( "query" );
      NUM.first( $all, s, $div );
   };   // NUM.fire()



   NUM.first = function ( $all, activity, $at ) {
      // Perform activity processing
      // Precondition:
      //    $all      -- <table>
      //    activity  -- code of task, # + ~ % ?
      //    $at       -- <div>
      // Uses:
      //    >  Signature
      //    >  NUM.call
      //    NUM.fore()
      // 2020-08-21 PerfektesChaos@de.wikipedia
      var s     = NUM.call[ activity ],
          $code = $at.find( "code" ),
          $active;
      $code.empty();
      if ( activity === "?" ) {
         $code.hide();
      } else {
         $code.text( "0" ).show();
      }
      NUM.fore( $all );
      if ( s ) {
         $active = $all.find( "tbody" )
                       .find( "." + Signature + "-active" )
                       .not( "." + Signature + "-td" );
         if ( $active.length ) {
            NUM[ s ]( $all, $active, $code );
         }
      }
   };   // NUM.first()



   NUM.flat = function ( adjust ) {
      // Trim left
      // Precondition:
      //    adjust  -- string
      // Postcondition:
      //    Returns trimmed string
      // Uses:
      //    >  NUM.spaces
      // 2020-08-17 PerfektesChaos@de.wikipedia
      var i = 0,
          j, k;
      do {   // while k <= 32  &&  i < adjust.length
         k = adjust.charCodeAt( i );
         if ( k > 32 ) {
            for ( j = 0;  j < NUM.spaces.length;  j++ ) {
               if ( NUM.spaces[ j ]  ===  k ) {
                  k = 32;
                  break;   // for j
               }
            }   // for j
         }
         i++;
      } while ( k <= 32  &&  i < adjust.length );
      return ( i > 1  ?  adjust.substr( i - 1 )  :  adjust  );
   };   // NUM.flat()



   NUM.flip = function ( action, $at ) {
      // Number format selection
      // Precondition:
      //    action  -- event data object, or string with identifier
      //               .data
      //               [0]  -- identifier of pattern
      //               [1]  -- <li> or not
      // Uses:
      //    >  NUM.$group
      //    >  Signature
      //     < NUM.seek
      //     < NUM.dec
      // 2017-11-30 PerfektesChaos@de.wikipedia
      var sign = Signature + "-grouping",
          i, seek, $li, $ul;
      NUM.$group.find( "." + sign ).removeClass( sign );
      if ( typeof action  ===  "object"
           &&     action   &&
           typeof action.data  ===  "object"
           &&     action.data ) {
         seek = action.data[ 0 ];
         if ( typeof action.data[ 1 ]  ===  "object" ) {
            $li = action.data[ 1 ];
         }
      } else if ( typeof action  ===  "string" ) {
         seek = action;
         $li  = $at;
      }
      if ( seek ) {
         NUM.seek = seek;
         NUM.dec  = false;
         if ( ! $li ) {
            $ul = NUM.$group.children( "li" );
            for ( i = NUM.offer.length - 1;  i >= 0;  i-- ) {
               $li = $ul.eq( i );
               if ( NUM.offer[ i ]  ===  NUM.seek ) {
                  break;   // for i--
               }
            }   // for i--
         }
         $li.addClass( Signature + "-grouping" );
      }
   };   // NUM.flip()



   NUM.fore = function ( $all, $active, assign ) {
      // Equip cell(s) with indicating box and display value, or clear
      // Precondition:
      //    $all     -- <table> or active cells / clear them all
      //    $active  -- active cell
      //    assign   -- text shown in active cell, or nil
      // Postcondition:
      //    Returns $active content, if not assign
      // Uses:
      //    >  Signature
      // 2020-08-27 PerfektesChaos@de.wikipedia
      var sign = Signature + "-info",
          r, $e;
      if ( $active ) {
         $e = $active.find( "." + sign );
         if ( $e.length ) {
            $e.show();
         } else {
            $e = $( "<div>" );
            $e.addClass( sign );
            $active.prepend( $e );
         }
         if ( assign ) {
            $e.empty()
              .text( assign );
         } else {
            r = $e.text();
         }
      } else {
         $all.find( "." + sign ).hide().empty();
      }
      return r;
   };   // NUM.fore()



   NUM.foreign = function ( adjust, align ) {
      // Translate non-ASCII digits etc.
      // Precondition:
      //    adjust  -- string with non-ASCII digits
      //    align   -- local zero character code
      // Postcondition:
      //    Returns name of character
      // 2017-11-30 PerfektesChaos@de.wikipedia
      var r = "",
          m = align - 0x30,
          n = align + 9,
          i, k;
      for ( i = 0;  i < adjust.length;  i++ ) {
         k = adjust.charCodeAt( i );
         if ( k >= align  &&  k <= n ) {
            k -= m;
         } else if ( align === 0xFF10  &&
                     ( k === 0xFF0E  ||  k === 0xFF0C ) ) {
            k -= 0xFEE0;
         }
         r = r + String.fromCharCode( k );
      }   // for i
      return r;
   };   // NUM.foreign()



   NUM.formatting = function ( analyze ) {
      // Guess project formatting from project query
      // Precondition:
      //    analyze  -- string with project number formatting 1234567.89
      // Uses:
      //    >< NUM.reSeparator
      //    >< NUM.reSegment
      //     < NUM.sep
      //    NUM.find()
      //    NUM.flip()
      //    REPO.feed()
      // 2020-08-21 PerfektesChaos@de.wikipedia
      var got, s, start, struct;
      if ( typeof NUM.reSeparator  !==  "object" ) {
         NUM.reSeparator = new RegExp( "^(.+)67(.+)89$" );
      }
      got = NUM.reSeparator.exec( analyze );
      if ( got ) {
         start  = got[ 1 ];
         struct = NUM.find( got[ 2 ] );
         if ( struct ) {
            if ( typeof NUM.codes[ struct ]  ===  "number" ) {
               NUM.sep = String.fromCharCode( NUM.codes[ struct ] );
            }
            if ( typeof NUM.reSegment  !==  "object" ) {
               NUM.reSegment = new RegExp( "[1-4]([^2-4&]+)[2-5]" );
            }
            got = NUM.reSegment.exec( start );
            if ( got ) {
               s = NUM.find( got[ 1 ] );
               if ( s ) {
                  struct = struct + ";" + s;
               }
            }
            NUM.flip( struct );
            REPO.feed( "seek", struct );
         }
      }
   };   // NUM.formatting()



   NUM.forward = function ( $all, $actives, $about ) {
      // Renumber cells
      // Precondition:
      //    $all      -- <table>
      //    $actives  -- active cells
      //    $about    -- <code> for result
      // Uses:
      //    NUM.fore()
      //    TABLE.factory()
      // 2020-08-27 PerfektesChaos@de.wikipedia
      var n = $actives.length,
          s = $about.parent().parent().data( "row" ),
          i, m, row, table, $t;
      $about.text( n );
      if ( s ) {
         table = TABLE.factory( $all );
         row   = table[ parseInt( s, 10 ) ];
         m     = 0;
         for ( i = 0;  i < row.length;  i++ ) {
            $t = row[ i ];
            if ( $t  &&  $t.is( $actives ) ) {
               s = NUM.fore( false, $t );
               if ( ! s ) {
                  m++;
                  NUM.fore( false, $t, m );
               }
            }
         }   // for i
      } else {
         for ( i = 0;  i < n;  i++ ) {
            NUM.fore( false,  $actives.eq( i ),  i + 1 );
         }   // for i
      }
   };   // NUM.forward()



   NUM.fraction = function ( $all, $actives, $about ) {
      // Percentage of all figures
      // Precondition:
      //    $all      -- <table>
      //    $actives  -- active cells
      //    $about    -- <code> for result
      // Uses:
      //    >  Signature
      //    >  NUM.min
      //    >  NUM.sep
      //    NUM.full()
      //    NUM.fore()
      // 2020-08-25 PerfektesChaos@de.wikipedia
      var n = $actives.length,
          i, k, less, lesser, m, q, total, $td;
      if ( n ) {
         total = NUM.full( $all, $actives, $about );
         n     = total[ 1 ];
      }
      if ( n ) {
         total  = total[ 0 ];
         lesser = ( total < NUM.min);
         if ( lesser ) {
            if ( total  >  -1 * NUM.min ) {
               total = false;
            } else {
               total = -1 * total;
            }
         }
         if ( total ) {
            q = 1 / total;
            for ( i = 0;  i < $actives.length;  i++ ) {
               $td  = $actives.eq( i );
               k    = NUM.fetch( $td );
               if ( typeof k  ===  "number" ) {
                  less = ( k < 0 );
                  if ( less ) {
                     k = -1 * k;
                  }
                  k = Math.round( 10000 * k * q );
                  if ( k ) {
                     m = k % 100;
                     k = Math.floor( k * 0.01 );
                     if ( less !== lesser ) {
                        k = -1 * k;
                     }
                     if ( m ) {
                        k = k + NUM.sep;
                        if ( m < 10 ) {
                           k = k + "0";
                        }
                        k = k + m;
                     }
                  }
                  NUM.fore( false,  $td,  k + "%" );
               }
            }   // for i
         }
      }
   };   // NUM.fraction()



   NUM.fruits = function ( $all, $actives, $about ) {
      // Renumber cells
      // Precondition:
      //    $all      -- <table>
      //    $actives  -- active cells
      //    $about    -- <code> for result
      // Uses:
      //    >  Signature
      //    >< NUM.reWhite
      //    >< NUM.reSpaces
      //    NUM.fore()
      //    PAGE.fruit()
      // 2020-08-21 PerfektesChaos@de.wikipedia
      var send = "",
          i, k, s, $t;
      $about.empty();
      if ( typeof NUM.reWhite  !==  "object" ) {
         NUM.reWhite    = new RegExp( "[\t\n\r]", "g" );
         NUM.reSpaces = new RegExp( " +", "g" );
      }
      for ( i = 0;  i < $actives.length;  i++ ) {
         $t = $actives.eq( i );
         NUM.fore( $t );
         s = $t.text();
         k = s.indexOf( "[-][+]" );
         if ( k >= 0   &&
              k  ===  s.length - 6 ) {
            s = s.substr( 0, k );
         }
         s = s.replace( NUM.reWhite, " " )
              .replace( NUM.reSpaces, " " );
         if ( s === " " ) {
            s = "";
         }
         if ( s.substr( 0, 1 ) === " " ) {
            s = s.substr( 1 );
         }
         if ( s.substr( s.length - 1 ) === " " ) {
            s = s.substr( 0,  s.length - 1 );
         }
         send = send  +  ( send ? "\n" : "" )  +  s;
      }   // for i
      if ( send ) {
         PAGE.fruit( send );
      }
   };   // NUM.fruits()



   NUM.full = function ( $all, $actives, $about ) {
      // Sum of all figures
      // Precondition:
      //    $all      -- <table>
      //    $actives  -- active cells
      //    $about    -- <code> for result
      // Postcondition:
      //    Returns Array:   [ 0 ]   -- sum
      //                     [ 1 ]   -- number of summands
      // Uses:
      //    >< NUM.reInteger
      //    NUM.fetch()
      // 2020-08-27 PerfektesChaos@de.wikipedia
      var n = $actives.length,
          m = n,
          v = 0,
          got, i, k, $t;
      if ( typeof NUM.reEntitiy  !==  "object" ) {
         NUM.reInteger = new RegExp( "^-?[0-9]+$" );
         NUM.re0000000 = new RegExp( "^(.*\\..*)00000000[1-9]$" );
         NUM.re9999999 = new RegExp( "\\.(.*99999999)([1-9])$" );
      }
      for ( i = 0;  i < n;  i++ ) {
         $t = $actives.eq( i );
         k  = NUM.fetch( $t );
         if ( typeof k  ===  "number" ) {
            v += k;
         } else {
            TABLE.flipper( { data:  [ $all, $t, false ] },  true );
            m--;
         }
      }   // for i
      if ( v !== Math.floor( v ) ) {
         k = "" + v;
         got = NUM.re0000000.exec( k );
         if ( got ) {
            v = parseFloat( got[ 1 ] );
         } else {
            got = NUM.re9999999.exec( k );
            if ( got ) {
               k = "0.";
               n = got[ 1 ].length;
               for ( i = 0;  i < n;  i++ ) {
                  k = k + "0";
               }   // for i
               k   = k   +   ( 12  -  parseInt( got[ 2 ], 10 ) );
               v   = v + parseFloat( k );
               got = NUM.re0000000.exec( "" + v );
               if ( got ) {
                  v = parseFloat( got[ 1 ] );
               }
            }
         }
      }
      $about.text( v );
      return  [ v, m ];
   };   // NUM.full()



   NUM.furnish = function () {
      // Initialize number formatting scheme
      // Postcondition:
      //    Selection has been set on project default
      // Uses:
      //    >  NUM.codes
      //     < NUM.sep
      //    REPO.fetch()
      //    NUM.flip()
      //    API.formatnum ()
      // 2020-08-21 PerfektesChaos@de.wikipedia
      var s = REPO.fetch( "seek" ),
          i;
      if ( s ) {
         NUM.flip( s );
         i = s.indexOf( ";" );
         if ( i > 0 ) {
            s = s.substr( 0, i );
         }
         if ( typeof NUM.codes[ s ]  ===  "number" ) {
            NUM.sep = String.fromCharCode( NUM.codes[ s ] );
         }
      } else if ( mw ) {
         API.formatnum();
      }
   };   // NUM.furnish()



   PAGE.fire = function () {
      // Start real processing, if not yet done
      // Precondition:
      //    DOM ready
      // Uses:
      //    >  Signature
      //    >  PAGE.support
      //    >< mw
      //     < PAGE.$box
      //    PAGE.first()
      // 2020-08-07 PerfektesChaos@de.wikipedia
      var $content;
      if ( ! $( "#" + Signature ).length ) {
         PAGE.$box = $( "<div>" );
         PAGE.$box.attr( { id: Signature } );
         $content = $( "#content" );
         if ( ! $content.length ) {
            $content = $( "body" );
         }
         $content.prepend( PAGE.$box );
         if ( ! mw   ||   typeof mw  !==  "object" ) {
            mw = false;
         }
         PAGE.first();
         if ( mw   &&   typeof mw.hook  ===  "function" ) {
            mw.hook( Signature + ".ready" ).fire( PAGE.support );
         }
      }
   };   // PAGE.fire()



   PAGE.first = function () {
      // Start processing by inserting box at top of page
      // Uses:
      //    >  mw
      //    >  Signature
      //    >  PAGE.support
      //    >  PAGE.sign
      //    >  PAGE.show
      //    >  CSS.def
      //    >  PAGE.$box
      //    >  Version
      //     < PAGE.dir
      //     < PAGE.ltr
      //     < NUM.$group
      //     < PAGE.id
      //     < PAGE.details
      //    NUM.furnish()
      //    (REPO.format)
      //    (NUM.flip)
      // 2020-08-22 PerfektesChaos@de.wikipedia
      var def, i, s, side, sided, single, style,
          $a, $icon, $li, $style, $survey, $tables, $text, $version;
      if ( mw && mw.loader ) {
         mw.loader.using( [ "mediawiki.storage" ],
                          REPO.format );
      }
      PAGE.dir = $( "html" ).attr( "dir" )  ||  "ltr";
      PAGE.ltr = ( PAGE.dir === "ltr" );
      side     = ( PAGE.ltr ? "left" : "right" );
      sided    = ( PAGE.ltr ? "right" : "left" );
      CSS.def.caption["text-align"]   = side;
      CSS.def.handle["float"]         = side;
      CSS.def.icon["margin-"+sided]   = "1em";
      CSS.def.info["float"]           = side;
      CSS.def.info["margin-"+sided]   = "1em";
      CSS.def.toggle["float"]         = sided;
      CSS.def.toggle["margin-"+side]  = "1em";
      CSS.def.version["margin-"+side] = "4em";
      $style = $( "<style>" );
      style  = "";
      for ( s in CSS.def ) {
         def   = CSS.def[ s ];
         style = style + "." + Signature + "-" + s + "{\n";
         for ( single in def ) {
            style = style + single + ":" + def[ single ] + ";\n";
         }   // for single in def
         style = style + "}\n";
      }   // for s in CSS.def
      $( "head" ).after( $style.text( style ) );
      $a = $( "<a>" );
      $a.addClass( Signature + "-icon " + Signature + "-clickable" )
        .attr( { href:   PAGE.support,
                 target: "_blank" } )
        .text( PAGE.sign );
      $icon = $( "<div>" );
      $icon.append( $a )
           .addClass( Signature + "-cell" )
           .attr( { "aria-hidden": "true" } );
      PAGE.$box.addClass( Signature + "-box" );
      PAGE.$box.append( $icon );
      $text = $( "<div>" );
      $a    = $( "<a>" );
      $a.addClass( Signature + "-doclink " + Signature + "-clickable" )
        .attr( { href:   PAGE.support,
                 target: "_blank" } )
        .text( PAGE.show );
      $text.append( $a );
      $version = $( "<span>" );
      $version.addClass( Signature + "-version" )
              .text( Version );
      $text.append( $version );
      $tables = $( "table" ).not( ":hidden" );
      if ( $tables.length ) {
         PAGE.$details = $( "<ul>" );
         PAGE.id       = 0;
         $tables.each( PAGE.furnish );
         if ( PAGE.id ) {
            PAGE.$details.addClass( Signature + "-ul" );
            $survey = $( "<div>" );
            $survey.addClass( Signature + "-cell" );
            $survey.append( $text );
            NUM.$group = $( "<ul>" );
            NUM.$group.addClass( Signature + "-ul" );
            for ( i = 0;  i < NUM.offer.length;  i++ ) {
               s   = NUM.offer[ i ];
               $li = $( "<li>" );
               $li.addClass( Signature + "-ul" )
                  .on( "click",  [ s, $li ],  NUM.flip )
                  .text( PAGE.flat( NUM.patterns[ s ] ) );
               if ( i ) {
                  $li.prepend( $( "<wbr>" ) );
               }
               NUM.$group.append( $li );
               if ( ! i ) {
                  NUM.flip( s, $li );
               }
            }   // for i
            NUM.furnish();
            $survey.append( NUM.$group );
            $survey.append( PAGE.$details );
            PAGE.$box.append( $survey );
         } else {
            $text.addClass( Signature + "-cell" );
            PAGE.$box.append( $text );
         }
      } else {
         $text.addClass( Signature + "-cell" );
         PAGE.$box.append( $text );
      }
   };   // PAGE.first()



   PAGE.flat = function ( adjust ) {
      // Replace entities by characters
      // Precondition:
      //    adjust  -- string expected
      // Uses:
      //    >  NUM.entities
      //    >< PAGE.reEntitiy
      //     < PAGE.reEntitiyX
      // 2017-11-30 PerfektesChaos@de.wikipedia
      var r = ( adjust || "ERROR" ),
          fun10, fun16, s, seek, sign, shift;
      if ( r.indexOf( "&#" )  >=  0 ) {
         fun10 = function ( all, adapt ) {
                    return String.fromCharCode( parseInt( adapt, 10 ) );
                 };
         fun16 = function ( all, adapt ) {
                    return String.fromCharCode( parseInt( adapt, 16 ) );
                 };
         if ( typeof PAGE.reEntitiy  !==  "object" ) {
            PAGE.reEntitiy  = new RegExp( "&#([0-9]+);", "g" );
            PAGE.reEntitiyX = new RegExp( "&#x([0-9A-F]+);", "gi" );
         }
         r = r.replace( PAGE.reEntitiy, fun10 );
         if ( r.indexOf( "&#x" )  >=  0 ) {
            r = r.replace( PAGE.reEntitiyX, fun16 );
         }
      }
      if ( r.indexOf( "&" )  >=  0 ) {
         for ( s in NUM.entities ) {
            seek = "&" + s + ";";
            if ( r.indexOf( seek )  >=  0 ) {
               shift = NUM.entities[ s ];
               sign  = "re_" + s;
               if ( typeof PAGE[ sign ]  !==  "object" ) {
                  PAGE[ sign ] = new RegExp( seek, "g" );
               }
               if ( shift ) {
                  shift = String.fromCharCode( shift );
               } else {
                  shift = "";
               }
               r = r.replace( PAGE[ sign ], shift );
               break;   // for s in NUM.entities
            }
         }   // for s in NUM.entities
      }
      return r;
   };   // PAGE.flat()



   PAGE.fruit = function ( assign ) {
      // Make selected data available for copy&paste
      // Precondition:
      //    assign  -- string, to be copied to clipboard textarea
      // Uses:
      //    >  Signature
      //    >  PAGE.$box
      //    >< PAGE.$textarea
      // 2020-07-25 PerfektesChaos@de.wikipedia
      if ( ! PAGE.$textarea ) {
         PAGE.$textarea = $( "<textarea>" );
         PAGE.$textarea.addClass( Signature + "-textarea" )
                       .attr( { id:        Signature + "-copy",
                                readonly: "readonly",
                                rows:     "5" } );
         PAGE.$box.after( PAGE.$textarea );
      }
      PAGE.$textarea.val( assign );
   };   // PAGE.fruit()



   PAGE.furnish = function ( i, e ) {
      // Initialize data table
      // Precondition:
      //    i  -- index number
      //    e  -- DOM element
      // Uses:
      //    >  Signature
      //    >  PAGE.sign
      //    >  PAGE.details
      //    >< PAGE.id
      //    TABLE.fiat()
      //    (TABLE.furnish)
      // 2020-08-21 PerfektesChaos@de.wikipedia
      var $table   = $( e ).eq( 0 ),
          s, $a, $caption, $handle, $li;
      if ( $table.find( "thead" ).length  ||
           $table.find( "th" ).length ) {
         $a  = $( "<a>" );
         $li = $( "<li>" );
         if ( PAGE.id ) {
            $li.append( $( "<wbr>" ) );
         }
         ++PAGE.id;
         s = Signature + "-" + PAGE.id;
         $a.attr( { href: "#" + s } )
           .text( PAGE.id );
         $li.append( $a );
         PAGE.$details.append( $li );
         TABLE.fiat( $table, PAGE.id );
         $caption = $table.find( "caption" ).eq( 0 );
         $handle  = $( "<div>" );
         if ( ! $caption.length ) {
             $caption = $( "<caption>" );
             $table.prepend( $caption );
         }
         $handle.addClass( Signature + "-handle" )
                .attr( { id:    s,
                         title: Signature } )
                .text( PAGE.sign )
                .on( "click", $table, TABLE.furnish );
         $caption.addClass( Signature + "-caption" );
         $caption.append( $handle );
      }
      return true;
   };   // PAGE.furnish()



   REPO.feed = function ( sign, val ) {
      // Store value at sign
      // Precondition:
      //    sign   -- string, with group ID
      //    val    -- any value
      // Uses:
      //    >< REPO.o
      //    REPO.fetch()
      // 2020-08-21 PerfektesChaos@de.wikipedia
      var webSs;
      if ( typeof REPO.o  !==  "object" ) {
         REPO.fetch( "" );
      }
      REPO.o[ sign ] = val;
      if ( typeof window.localStorage  ===  "object" ) {
         webSs = window.localStorage;
         if ( webSs   &&   typeof webSs.setItem  ===  "function" ) {
            try {
               webSs.setItem( REPO.full(),
                              JSON.stringify( REPO.o ) );
            } catch (e) {
            }
         }
      }
   };   // REPO.feed()



   REPO.fetch = function ( sign ) {
      // Retrieve value at sign
      // Precondition:
      //    sign   -- string, with group ID
      // Uses:
      //     < REPO.o
      //    REPO.full()
      // 2020-08-21 PerfektesChaos@de.wikipedia
      var got, r, webSs;
      if ( typeof REPO.o  !==  "object"   &&
           typeof window.localStorage  ===  "object" ) {
         webSs = window.localStorage;
         if ( webSs   &&   typeof webSs.getItem  ===  "function" ) {
            try {
               got = JSON.parse( webSs.getItem( REPO.full() ) );
               if ( typeof got  ===  "object"
                    &&     got ) {
                  REPO.o = got;
               }
            } catch (e) {
            }
         }
      }
      if ( typeof REPO.o  !==  "object" ) {
         REPO.o = { };
      }
      if ( typeof REPO.o[ sign ]  !==  "undefined" ) {
         r = REPO.o[ sign ];
      }
      return r;
   };   // REPO.fetch()



   REPO.first = function () {
      // Initialize memory
      // Uses:
      //    >  Signature
      //    >  JSON
      //     < BACKUP.repo
      //    mw.storage.get()
      // 2020-01-05 PerfektesChaos@de.wikipedia
      var stored;
      if ( typeof REPO.repo  ===  "undefined"   &&
           typeof mw  ===  "object"
           &&     mw   &&
           typeof mw.storage  ===  "object"
           &&     mw.storage   &&
           typeof mw.storage.get  ===  "function" ) {
         REPO.repo = false;
         stored    = mw.storage.get( Signature );
         if ( stored ) {
            try {
               REPO.repo = JSON.parse( stored );
               if ( typeof REPO.repo  !==  "object"
                    ||   ! REPO.repo ) {
                  REPO.repo = false;
               }
            } catch( ex ) {
            }
         }
      }
   };   // REPO.first()



   REPO.full = function () {
      // Provide project selector
      // Uses:
      //    >  Signature
      //    >< REPO.sign
      //    mw.config.get()
      // 2017-10-01 PerfektesChaos@de.wikipedia
      if ( typeof REPO.sign  !==  "string" ) {
         REPO.sign = Signature + "." + mw.config.get( "wgDBname" );
      }
      return REPO.sign;
   };   // REPO.full()



   TABLE.facilitate = function ( $all, $at ) {
      // Cell equipping, if not yet done
      // Precondition:
      //    $all  -- <table>
      //    $at   -- <td> / <th>
      // Uses:
      //    >  Signature
      //    (TABLE.flipper)
      // 2017-11-30 PerfektesChaos@de.wikipedia
      var s  = "." + Signature,
          $w = $at.find( s + "-exclude" );
      if ( $w.length ) {
         $w.show();
         $at.find( s + "-include" ).hide();
      } else {
         $w = $( "<div>" );
         $w.addClass( Signature + "-exclude " + Signature + "-toggle" )
           .on( "click",
                [ $all, $at, false ],
                TABLE.flipper )
           .text( "[-]" );
         $at.append( $w );
         $w = $( "<div>" );
         $w.addClass( Signature + "-include " + Signature + "-toggle" )
           .hide()
           .on( "click",
                [ $all, $at, true ],
                TABLE.flipper )
           .text( "[+]" );
         $at.append( $w );
      }
   };   // TABLE.facilitate()



   TABLE.factory = function ( $all ) {
      // Retrieve completed regular mirror table
      // Precondition:
      //    $all  -- <table>
      //    assigned mirror table has been prepared
      //                                   .nC
      //                                   .nR
      //                                   .$rows
      //                                   .d
      // Postcondition:
      //    Returns complete assigned mirror table
      // Uses:
      //    >< TABLE.reInteger
      //    TABLE.fiat()
      // 2020-08-25 PerfektesChaos@de.wikipedia
      var r = TABLE.fiat( $all ),
          e, i, j, k, m, n, s, tmp, ts, $row, $rows, $t;
      if ( ! r.d ) {
         if ( typeof TABLE.reInteger  !==  "object" ) {
            TABLE.reInteger = new RegExp( "^[0-9]+$" );
         }
         $rows = r[ "$rows" ];
         for ( i = 0;  i < r.nR;  i++ ) {
            n = $rows.eq( i ).children().length  -  1;
            if ( n > r.nC ) {
               r.nC = n;
            }
         }   // for i
         r.d = [ ];
         tmp = [ ];
         for ( i = 0;  i < r.nR;  i++ ) {
            ts = [ ];
            r.d.push( new Array( r.nC ) );
            $row = $rows.eq( i ).children();
            for ( k = 1;  k < $row.length;  k++ ) {
               ts.push( $row.eq( k ) );
            }   // for k
            tmp.push( ts );
         }   // for i
         delete r[ "$rows" ];
         do {   // while  $t
            i  = 0;
            $t = false;
            do {   // while  i < r.nR
               ts = tmp[ i ];
               if ( ts ) {
                  $t = ts[ 0 ];
                  if ( ts.length <= 1 ) {
                     tmp[ i ] = false;
                  } else {
                     tmp[ i ].splice( 0, 1 );
                  }
                  s  = $t.attr( "colspan" );
                  if ( s   &&   TABLE.reInteger.test( s ) ) {
                     j = ( parseInt( s, 10 )  ||  1 );
                  } else {
                     j = 1;
                  }
                  s  = $t.attr( "rowspan" );
                  if ( s   &&   TABLE.reInteger.test( s ) ) {
                     n = ( parseInt( s, 10 )  ||  1 );
                  } else {
                     n = 1;
                  }
                  n = Math.min( i + n,  r.nR );
                  for ( ;  i < n;  i++ ) {
                     e = r.d[ i ];
                     m = 0;
                     do {   // while  m < r.nC
                        if ( ! e[ m ] ) {
                           k = Math.min( m + j,  r.nC );
                           for ( ;  m < k;  m++ ) {
                              r.d[ i ][ m ] = $t;
                           }   // for m
                           break;   // while  m < r.nC
                        }
                        m++;
                     } while ( m < r.nC );
                  }   // for i
               } else {
                  i++;
               }
            } while ( i < r.nR );
         } while ( $t );
         TABLE.fiat( $all, false, r );
      }
      return r.d;
   };   // TABLE.factory()



   TABLE.fiat = function ( $all, assign, apply ) {
      // Create or assign regular mirror table
      // Precondition:
      //    $all    -- <table>
      //    assign  -- number, with ID, to be assigned to $all
      //    apply   -- regular mirror table to be stored
      // Postcondition:
      //    Returns any assigned mirror table, if not assigning
      // Uses:
      //    >  Signature
      // 2020-08-21 PerfektesChaos@de.wikipedia
      var r;
      if ( typeof assign  ===  "number" ) {
         $all.data( Signature,  "" + assign );
      } else {
         r = $all.data( Signature );
         if ( apply ) {
            TABLE.t[ r ] = apply;
            r            = apply;
         } else {
            if ( typeof TABLE.t  !==  "object" ) {
               TABLE.t = { };
            }
            if ( typeof TABLE.t[ r ]  !==  "object" ) {
               TABLE.t[ r ] = { d: false };
            }
            r = TABLE.t[ r ];
         }
      }
      return r;
   };   // TABLE.fiat()



   TABLE.flat = function ( $assembly ) {
      // Normalize single row against colspan etc.
      // Precondition:
      //    $assembly  -- <tr>
      // Postcondition:
      //    $assembly is split into single cells.
      //    Returns number of columns or rows
      // Uses:
      //    >  Signature
      //    >< TABLE.reInteger
      // 2017-11-30 PerfektesChaos@de.wikipedia
      var $row = $assembly.children(),
          r    = $row.length,
          fine = function () {
                           if ( typeof TABLE.reInteger  !==  "object" ) {
                              TABLE.reInteger = new RegExp( "^[0-9]+$" );
                           }
                 },
          i, m, s, $t;
      for ( i = r - 1;  i >= 0;  i-- ) {
         $t = $row.eq( i );
         s  = $t.attr( "rowspan" );
         if ( s ) {
            fine();
            if ( TABLE.reInteger.test( s ) ) {
               m = parseInt( s, 10 );
            } else {
               m = 0;
            }
            if ( m < 2 ) {
               $t.attr( { rowspan: null } );
            }
         }
         s  = $t.attr( "colspan" );
         if ( s ) {
            fine();
            if ( TABLE.reInteger.test( s ) ) {
               m = parseInt( s, 10 );
               if ( m > 1 ) {
                  r = r + m - 1;
               }
            } else {
               m = 0;
            }
            if ( m < 2 ) {
               $t.attr( { colspan: null } );
            }
         }
      }   // for i--
      return r;
   };   // TABLE.flat()



   TABLE.flatten = function ( $all ) {
      // Remove sortability etc.
      // Precondition:
      //    $all  -- <table>
      // Postcondition:
      //    Sortability removed
      // 2020-08-28 PerfektesChaos@de.wikipedia
      var $clickable;
      if ( $all.hasClass( "sortable" ) ) {
         $all.removeClass( "sortable jquery-tablesorter" );
         $clickable = $all.children( "thead" ).find( "th" );
         $clickable.off( "click" )
                   .off( "keypress" )
                   .off( "mousedown" );
      }
      $all.removeClass( "mw-datatable" );
   };   // TABLE.flatten()



   TABLE.flip = function ( action ) {
      // Widget usage
      // Precondition:
      //    action  -- event data
      //               .data
      //               [0]  -- <table>
      //               [1]  -- true: horizontal, false: column
      //               [2]  -- number of row or column
      //               [3]  -- activity code
      // Postcondition:
      //    Performed.
      // Uses:
      //    >  Signature
      //    >  NUM.call
      //    NUM.fore()
      //    TABLE.focus()
      //    NUM.first()
      // 2020-08-27 PerfektesChaos@de.wikipedia
      var k, line, query, s, s1, s2,
          $active, $table, $tbody, $td, $thead, $tr;
      if ( typeof action  ===  "object"
           &&     action   &&
           typeof action.data ===  "object"
           &&     action.data ) {
         s = "." + Signature;
         $table = action.data[ 0 ];
         line   = action.data[ 1 ];
         k      = action.data[ 2 ];
         query  = action.data[ 3 ];
         $tbody = $table.find( "tbody" );
         if ( line ) {
            $tr    = $tbody.children( "tr" ).eq( k );
            $tr    = $tr.children();
            $td    = $tr.eq( 0 );
         } else {
            $thead = $table.find( "thead" );
            $tr    = $thead.children( "tr" ).eq( 0 );
            $td    = $tr.children( "td" ).eq( k );
         }
         $table.find( s + "-result" ).remove();
         NUM.fore( $tbody );
         s1 = Signature + "-chosen";
         $table.find( "." + s1 ).removeClass( s1 );
         if ( typeof query  ===  "boolean" ) {
            s1      = Signature + "-active";
            $active = $tbody.find( "." + s1 );
            $active.removeClass( s1 );
            $tbody.find( s + "-exclude" ).hide();
            $tbody.find( s + "-include" ).hide();
            s1 = s + "-menu";
            s2 = s + "-widget";
            $table.find( s1 ).hide();
            $table.find( s2 ).css( { visibility: "visible" } );
            $active = $table.find( s + "-quit" ).children();
            if ( query ) {
               $td.find( s2 ).css( { visibility: "hidden" } );
               $td.find( s1 ).show();
               TABLE.focus( $table, $tbody, line, k );
               $active.show();
            } else {
               $active.hide();
            }
         } else {
            $td.find( "." + Signature + "-" + NUM.call[ query ] )
                                                         .addClass( s1 );
            $active = $( "<div>" );
            $active.addClass( Signature + "-result" )
                   .data( "query", query )
                   .append( $( "<code>" ) );
            $td.append( $active );
            NUM.first( $table, query, $active );
         }
      }
   };   // TABLE.flip()



   TABLE.flipper = function ( action, abort ) {
      // Cell toggle
      // Precondition:
      //    action  -- event data
      //               .data
      //               [0]  -- <table>
      //               [1]  -- <td> / <th>
      //               [2]  -- true: +, false: -
      //    abort   -- true: no propagation to result
      // Postcondition:
      //    Toggled.
      // Uses:
      //    >  Signature
      //    NUM.fore()
      //    NUM.fire()
      // 2020-08-21 PerfektesChaos@de.wikipedia
      var less, s, sub, $ex, $in, $t, $table;
      if ( typeof action  ===  "object"
           &&     action   &&
           typeof action.data  ===  "object"
           &&     action.data ) {
         $table = action.data[ 0 ];
         $t     = action.data[ 1 ];
         less   = action.data[ 2 ];
         s      = "." + Signature;
         sub    = Signature + "-active";
         $ex    = $t.find( s + "-exclude" );
         $in    = $t.find( s + "-include" );
         if ( less ) {
            $t.addClass( sub );
            $in.hide();
            $ex.show();
         } else {
            $t.removeClass( sub );
            $ex.hide();
            $in.show();
            NUM.fore( $t );
         }
         if ( ! abort ) {
            NUM.fire( $table );
         }
      }
   };   // TABLE.flipper()



   TABLE.focus = function ( $all, $area, align, at ) {
      // Prepare single table for expansion
      // Precondition:
      //    $all    -- <table>
      //    $area   -- <tbody>
      //    align   -- true: horizontal, false: column
      //    at      -- number of row or column
      // Postcondition:
      // Uses:
      //    >  Signature
      //    TABLE.factory()
      //    TABLE.facilitate()
      // 2020-08-27 PerfektesChaos@de.wikipedia
      var s     = Signature + "-active",
          table = TABLE.factory( $all ),
          i, k, row, $cells, $t;
      if ( align ) {
         row  = table[ at ];
         for ( i = 0;  i < row.length;  i++ ) {
            $t = row[ i ];
            if ( $t ) {
               $t.addClass( s );
            }
         }   // for k
      } else {
         k = at - 1;
         for ( i = 0;  i < table.length;  i++ ) {
            $t = table[ i ][ k ];
            if ( $t ) {
               $t.addClass( s );
            }
         }   // for i
      }
      $cells = $area.find( "." + s );
      for ( k = 0;  k < $cells.length;  k++ ) {
         TABLE.facilitate( $all, $cells.eq( k ) );
      }   // for k
   };   // TABLE.focus()



   TABLE.$fold = function ( $all, align, at ) {
      // Create control header cell
      // Precondition:
      //    $all   -- <table>
      //    align  -- true: horizontal, false: column
      //    at     -- number of row or column
      // Postcondition:
      //    Returns <td>
      // Uses:
      //    >  Signature
      //    >  NUM.starts
      //    >  NUM.call
      //    >  PAGE.sign
      //    (TABLE.flip)
      // 2020-08-27 PerfektesChaos@de.wikipedia
      var $r       = $( "<td>" ),
          $widget  = $( "<div>" ),
          $menu    = $( "<div>" ),
          $space   = $( "<span>" ).text( " " ),
          i, s, $e;
      $menu.addClass( Signature + "-menu" )
           .hide();
      for ( i = 0;  i < NUM.starts.length;  i++ ) {
         s  = NUM.starts.substr( i, 1 );
         $e = $( "<span>" );
         $e.addClass( Signature + "-" + NUM.call[ s ] )
            .on( "click",
                 [ $all, align, at, s ],
                 TABLE.flip );
         if ( s === "?" ) {
            s = ( align ? "h" : "v" );
         }
         $e.text( TABLE.title[ s ] );
         $menu.append( $e )
              .append( $space.clone() );
      }   // for i
      if ( align ) {
         $r.data( "row",  "" + at );
      }
      $widget.addClass( Signature + "-widget" )
             .on( "click",
                  [ $all, align, at, true ],
                  TABLE.flip )
             .text( PAGE.sign );
      $r.append( $widget )
        .append( $menu )
        .addClass( Signature + "-td" );
      return $r;
   };   // TABLE.$fold()



   TABLE.furnish = function ( action ) {
      // Prepare single table for expansion
      // Precondition:
      //    action  -- event data
      //               .target
      // Uses:
      //    >  Signature
      //    >  PAGE.$details
      //    TABLE.flatten()
      //    TABLE.fiat()
      //    TABLE.$fold()
      // 2023-04-19 PerfektesChaos@de.wikipedia
      var e, i, k, n, sign, table, $caption, $handle,
          $rows, $table, $tbody, $td, $tfoot, $thead, $tr, $x;
      if ( typeof action  ===  "object"
           &&     action   &&
           typeof action.target  ===  "object"
           &&     action.target ) {
         $handle  = $( action.target );
         sign     = $handle.attr( "id" );
         $caption = $handle.parent();
         $table   = $caption.parent();
         $handle.remove();
      }
      if ( $table ) {
         TABLE.flatten( $table );
         $thead = $table.find( "thead" );
         if ( ! $thead.length ) {
            $thead = $( "<thead>" );
            $rows  = $table.find( "tr" );
            for ( i = 0;  i < $rows.length;  i++ ) {
               $tr = $rows.eq( i );
               $x  = $tr.children();
               for ( k = 0;  k < $x.length;  k++ ) {
                  e = $x.eq( k ).get( 0 );
                  if ( e.tagName.toUpperCase() !== "TH" ) {
                     $tr = false;
                     break;   // for k
                  }
               }   // for k
               if ( $tr ) {
                  $tr.detach();
                  $thead.append( $tr );
               } else {
                  break;   // for i
               }
            }   // for i
            $caption.after( $thead );
         }
         $tfoot = $table.find( "tfoot" );
         if ( $tfoot.length ) {
            $rows = $tfoot.find( "tr" );
            $td   = $( "<td>" );
            $td.addClass( Signature + "-active" );
            if ( $rows.length > 1 ) {
               $td.attr( { rowspan: $rows.length } );
            }
            $rows.eq( 0 ).prepend( $td );
         }
         $tbody = $table.find( "tbody" );
         if ( ! $tbody.length ) {
            $tbody = $( "<tbody>" );
            $rows  = $table.children( "tr" );
            for ( i = 0;  i < $rows.length;  i++ ) {
               $tr = $rows.eq( i );
               $tr.detach();
               $tbody.append( $tr );
            }   // for i
            $thead.after( $tbody );
         }
         $rows = $tbody.children();
         n     = $rows.length;
         if ( n ) {
            table            = TABLE.fiat( $table );
            table[ "$rows" ] = $rows;
            table.nR         = n;
            $rows            = $thead.children();
            k                = TABLE.flat( $rows.eq( 0 ) );
            table.nC         = k;
            k++;
            $td = $( "<td>" );
            $x  = $( "<span>" );
            $x.hide()
              .on( "click",
                   [ $table, true, 0, false ],
                   TABLE.flip )
              .text( TABLE.title.X );
            $td.addClass( Signature + "-active " + Signature + "-quit" )
               .attr( { id:       sign,
                        rowspan:  $rows.length + 1 } )
               .append( $x );
            $tr = $( "<tr>" );
            $tr.append( $td );
            for ( i = 1;  i < k;  i++ ) {
               $tr.append( TABLE.$fold( $table, false, i ) );
            }   // for i
            $thead.prepend( $tr );
            for ( i = 0;  i < n;  i++ ) {
               $tr = table[ "$rows" ].eq( i );
               $tr.prepend( TABLE.$fold( $table, true, i ) );
            }   // for i
         } else {
            PAGE.$details.find( "a[href$=\"#" + sign + "\"]" )
                         .parent()
                         .remove();
         }
      }
   };   // TABLE.furnish()


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



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

/// EOF </nowiki>   tableXpander.js