User:PerfektesChaos/js/WikiSyntaxTextMod/dO.js

Note: After saving, you have to bypass your browser's cache to see the changes. Google Chrome, Firefox, Microsoft Edge and Safari: Hold down the ⇧ Shift key and click the Reload toolbar button. For details and instructions about other browsers, see Wikipedia:Bypass your cache.
/// PerfektesChaos/js/WikiSyntaxTextMod/dO.js
//  Objects for wiki syntax specific code
/// 2019-08-01 PerfektesChaos@de.wikipedia
/// Fingerprint: #0#0#
/// @license: CC-by-sa/4.0 GPLv3
/// <nowiki>
/* global mw:true, mediaWiki:false                                     */
/* jshint forin:false, nocomma:false,
          bitwise:true, curly:true, eqeqeq:true, latedef:true,
          laxbreak:true,
          strict:true, undef:true, unused:true                         */


if ( typeof mediaWiki  !==  "object" ) {   // disconnected
   mw  =  { libs:   { WikiSyntaxTextMod:  { }
                    },
            log:    function () {"use strict";}
          };
}
( function ( mw ) {
   "use strict";
   var version  =  -7.21,
       sign     =  "WikiSyntaxTextMod",
       sub      =  "O",
       rls, self, WSTM;
   if ( typeof mw.loader  ===  "object" ) {
      rls   =  { };
      self  =  "user:PerfektesChaos/" + sign + "/" + sub;
      rls[ self ] = "loading";
      mw.loader.state( rls );
   }
   if ( typeof mw.libs[ sign ]  !==  "object" ) {   // isolated
      mw.libs[ sign ]  =  { };
   }
   WSTM  =  mw.libs[ sign ];
   if ( typeof WSTM.o  !==  "object" ) {
      WSTM.o  =  { };
   }
   WSTM.o.vsn   =  version;
   WSTM.o.self  =  self;
   if ( typeof WSTM.bb  !==  "object" ) {
      WSTM.bb  =  { };
   }
   if ( typeof WSTM.debugging  !==  "object" ) {
      WSTM.debugging  =  { };
   }
} ( mw ) );



// Provides:
//    .o.URL2wiki
//    .o.Weblink
//    .o.Wikilink
//    .o.WikiTom
/*
Requires: JavaScript 1.3
          (String.charCodeAt String.fromCharCode String.replace)
 */



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



mw.libs.WikiSyntaxTextMod.bb.URL2wiki  =  function (WSTM) {
   // Administration of wikilinks
   // 2013-03-15 PerfektesChaos@de.wikipedia
   // Class:
   //    Public:
   //       set()
   //    Private:
   //       init()
   // .law      true: https mandatory
   // .index    number: position of "://" in .source
   // .multiple number: of leading brackets '['  (0, 1, 2)
   // .newFrom  number: of chars in .source for replacement region start
   // .newTo    number: of chars in .source for replacement region end
   // .scheme   URL protocol, may be false or terminated by ':'
   // .shift    replacement string (total)        , beginning at .newFrom
   // .show     non-empty title string
   // .sister   wikilink sister project
   // .site     domain (including port)
   // .slashed  path etc.
   // .source   wikitext
   // .start    wikilink prefix string terminated with ':', or false
   // .storage  target of wikilink
   // .story    title of wikilink, or false
   // .suite    string: URL   false: wikilink
   // .suffix   aftermath, after "]"
   "use strict";
   var U2W;



   WSTM.o.URL2wiki  =  function () {
      // .constructor for new
      // Postcondition:
      //    Returns .o.URL2wiki object
      // Uses:
      //    >  .o.URL2wiki::
      //    .o.Wikilink.init()
      // 2013-03-15 PerfektesChaos@de.wikipedia
      var p;
      this.init();
      for (p in WSTM.o.URL2wiki) {
         this[ p ]  =  WSTM.o.URL2wiki[ p ];
      }   // for p in
      return this;
   };   // .o.URL2wiki() .constructor
   U2W  =  WSTM.o.URL2wiki;



   U2W.prototype.init  =  function () {
      // Initialize existing URL2wiki object
      // Postcondition:
      //    .o.URL2wiki object has been reset
      // Uses:
      //    >  .o.URL2wiki::
      //       >  .***
      // 2013-03-15 PerfektesChaos@de.wikipedia
      this.index     =  -1;
      this.mode      =  0;
      this.multiple  =  -1;
      this.newFrom   =  false;
      this.newTo     =  false;
      this.scheme    =  false;
      this.shift     =  false;
      this.show      =  false;
      this.sister    =  false;
      this.site      =  false;
      this.slashed   =  false;
      this.source    =  false;
      this.start     =  false;
      this.storage   =  false;
      this.story     =  false;
      this.suite     =  false;
      this.suffix    =  false;
   };   // .o.URL2wiki.init()



   U2W.prototype.fetch  =  function () {
      // Retrieve URL segments from wikitext
      // Uses:
      //    >  .o.URL2wiki::
      //       >  .source
      //       >  .index
      //       >< .move
      //        < .multiple
      //        < .newFrom
      //        < .site
      //        < .slashed
      //    .w.link.web.fetch()
      // 2013-03-15 PerfektesChaos@de.wikipedia
      var i, s;
      this.multiple  =  0;
      WSTM.w.link.web.fetch(this, true);   // cave!
      s  =  this.source.substr(this.index + this.move);
      i  =  s.indexOf(" ");
      if (i > 0) {
         s          =  s.substr(0, i);
         this.move  =  i + 1;
      } else {
         this.move  =  s.length;
      }
      i  =  s.indexOf("/");
      if (i > 0) {
         this.site  =  s.substr(0, i);
         if (i < s.length - 1) {
            this.slashed  =  s.substr(i);
         }
      } else {
         this.site  =  s;
      }
   };   // .o.URL2wiki.fetch()



   U2W.prototype.finish  =  function () {
      // Modify source string if URL improved
      // Postcondition:
      //    .o.URL2wiki object has been reset
      // Uses:
      //    >  .o.URL2wiki::
      //       >  .source
      //        < .shift
      // 2013-03-15 PerfektesChaos@de.wikipedia
   };   // .o.URL2wiki.finish()



   U2W.prototype.fire  =  function () {
      // Perform analysis
      // Postcondition:
      //    .o.URL2wiki object has been evaluated
      // Uses:
      //    >  .o.URL2wiki::
      //       >  .scheme
      //       >  .site
      //       >  .slashed
      //       >  .show
      //        < .mode
      //        < .sister
      //        < .storage
      //        < .story
      //    .w.link.wiki.url()
      // 2013-03-15 PerfektesChaos@de.wikipedia
      var i, g, s;
      switch (this.scheme) {
         case "http:" :
            s  =  "http://";
            i  =  7;
            break;
         case "https:" :
            s  =  "https://";
            i  =  8;
            break;
         default:
            s  =  "//";
            i  =  2;
      }   // switch .scheme
      s  =  s + this.site;
      g  =  WSTM.w.link.wiki.url(i, s, this.slashed, this.show);
      switch (typeof(g)) {
         case "string" :   // reformatted URL (later: object parts)
            this.mode   =  1;
            this.shift  =  g;
            break;
         case "object" :   // wikilink
            this.mode  =  2;
            if (g[0]) {
               this.sister  =  g[0];
            }
            this.storage  =  g[1];
            this.story    =  g[2];
            s             =  (this.sister  ?  this.sister + this.storage
                                           :  this.storage);
            if (this.show || this.story) {
               this.story  =  (this.show ? this.show : this.story);
               if (! this.sister  &&  this.story === s) {
                  this.story  =  false;
               }
            }
            break;
      }   // switch typeof .url()
   };   // .o.URL2wiki.fire()



   U2W.prototype.getMode  =  function () {
      // Return state (1: URL refinement; 2: wikilink) false: unchanged
      // Uses:
      //    >  .o.URL2wiki::
      //       >  .mode
      // 2013-03-15 PerfektesChaos@de.wikipedia
      return this.mode;
   };   // .o.URL2wiki.getMode()



   U2W.prototype.getRemoveFrom  =  function () {
      // Return number of target chars since which to remove, or false
      // Uses:
      //    >  .o.URL2wiki::
      //       >  .newFrom
      // 2013-03-15 PerfektesChaos@de.wikipedia
      return this.newFrom;
   };   // .o.URL2wiki.getRemoveFrom()



   U2W.prototype.getRemoveTo  =  function () {
      // Return number of target chars to be removed, or false
      // Uses:
      //    >  .o.URL2wiki::
      //       >  .newTo
      // 2013-03-15 PerfektesChaos@de.wikipedia
      return this.newTo;
   };   // .o.URL2wiki.getRemoveTo()



   U2W.prototype.getSuffix  =  function () {
      // Return suffix to be integrated, or false
      // Uses:
      //    >  .o.URL2wiki::
      //       >  .story
      //       >  .suffix
      //    .w.link.wiki.fore()
      // 2013-03-15 PerfektesChaos@de.wikipedia
      var r  =  false;
      if (this.mode === 2) {   // wikilink
         if (this.story && this.suffix) {
            r  =  WSTM.w.link.wiki.fore(this.suffix, 0);
            if (r) {
               r  =  this.suffix.substr(0, r);
            }
         }
      }
      return r;
   };   // .o.URL2wiki.getSuffix()



   U2W.prototype.getTarget  =  function () {
      // Returns wikilink target, or URL
      // Uses:
      //    >  .o.URL2wiki::
      //       >  .mode
      //       >  .sister
      //       >  .storage
      //    .getURL()
      // 2013-03-15 PerfektesChaos@de.wikipedia
      var r;
      if (this.mode === 2) {   // wikilink
         if (this.sister) {
            r  =  this.sister + this.storage;
         } else {
            r  =  this.storage;
         }
      } else {   // reformatted URL
         r  =  this.getURL();
      }
      return r;
   };   // .o.URL2wiki.getTarget()



   U2W.prototype.getTextReplace  =  function () {
      // Returns replacement string, maybe false if nothing to do
      // Uses:
      //    >  .o.URL2wiki::
      //       >  .mode
      //       >  .shift
      //       >  .sister
      //       >  .storage
      //       >  .story
      //       >  .multiple
      //       .getTarget()
      // 2013-03-15 PerfektesChaos@de.wikipedia
      var r;
      switch (this.mode) {
         case 1 :   // reformatted URL
            r  =  this.shift;
            break;
         case 2 :   // wikilink
            r  =  this.getTarget();
            if (this.story) {
               r  =  r + "|" + this.story;
            }
            r  =  "[" + r + "]";
            if (! this.multiple) {
               r  =  "[" + r + "]";
            }
            break;
         default :
            r  =  false;
      }   // switch .mode
      return r;
   };   // .o.URL2wiki.getTextReplace()



   U2W.prototype.getTitle  =  function () {
      // Returns link title, maybe "" if not necessary
      // Uses:
      //    >  .o.URL2wiki::
      //       >  .story
      // 2013-03-15 PerfektesChaos@de.wikipedia
      return (this.story ? this.story : "");
   };   // .o.URL2wiki.getTitle()



   U2W.prototype.getURL  =  function () {
      // Returns URL, maybe reformatted
      // Uses:
      //    >  .o.URL2wiki::
      //       >  .law
      //       >  .scheme
      //       >  .site
      //       >  .slashed
      // 2013-03-15 PerfektesChaos@de.wikipedia
      var r;
      if (this.law) {
         r  =  "https://";
      } else {
         r  =  (this.scheme  ?  this.scheme + "//"  :  "//");
      }
      r  =  r + this.site + "/"
            +  (this.slashed ? this.slashed : "");
      return r;
   };   // .o.URL2wiki.getURL()



   U2W.prototype.set  =  function (assign, access) {
      // Initialize existing weblink object
      // Precondition:
      //    assign  -- Weblink object, or string with URL
      //               object:    >  .scheme
      //                          >  .site
      //                          >  .slashed
      //                          >  .show
      //                          >  .suffix
      //    access  -- position of "://" in assign string
      // Postcondition:
      //    .o.URL2wiki object has been evaluated
      // Uses:
      //    >  .o.URL2wiki::
      //        < .index
      //        < .source
      //        < .multiple
      //        < .scheme
      //        < .site
      //        < .slashed
      //        < .show
      //        < .suffix
      //       .init()
      //       .fetch()
      //       .fire()
      //       .finish()
      //    .str.substrEnd()
      // 2013-03-15 PerfektesChaos@de.wikipedia
      this.init();
      switch (typeof(assign)) {
         case "string" :   // string with URL
            this.index   =  access;
            this.source  =  assign;
            this.fetch();
            break;
         case "object" :   // Weblink
            this.multiple  =  assign.multiple;
            this.scheme    =  assign.scheme;
            this.site      =  assign.site;
            this.slashed   =  assign.slashed;
            this.show      =  assign.show;
            this.suffix    =  assign.suffix;
            break;
      }   // switch .scheme
      if (this.site) {
         if (! this.scheme  ||  this.scheme === "http:"
                            ||  this.scheme === "https:") {
            if (WSTM.str.substrEnd(this.site, 4)  ===  ".org") {
               this.fire();
               if (this.source) {
                  this.finish();
               }
            }
         }
      }
   };   // .o.URL2wiki.set()



};   // .bb.URL2wiki()
mw.libs.WikiSyntaxTextMod.bb.URL2wiki(mw.libs.WikiSyntaxTextMod);
delete mw.libs.WikiSyntaxTextMod.bb.URL2wiki;



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



mw.libs.WikiSyntaxTextMod.bb.Weblink  =  function (WSTM) {
   // Administration of weblinks
   // 2015-12-25 PerfektesChaos@de.wikipedia
   // Class:
   //    Public:
   //       set()
   //       getIncrement()
   //    Private:
   //       fault()
   //       fenced()
   //       fiat()
   //       filled()
   //       filter()
   //       fire()
   //       first()
   //       fix()
   //       flat()
   //       friend()
   //       fruit()
   //       further()
   //       format()
   //       init()
   // .lazy       false: any modification happened
   // .limited    true: enclosed in brackets (leading bracket '[')
   // .lackScheme true: relative URL protocol was set to https
   // .lowScheme  true: URL protocol has been downcased
   // .leaf       true: template parameter; terminate suffix by '|'
   // .index      number: position of URL begin in .source
   // .join       number: start of interesting region in .node
   // .keep       number: length of URL
   // .maxTitle   number: limiting length for suspicious titles
   // .maxURL     number: limiting URL length
   // .mode       number: WikiTom mode
   // .move       number: string incrementation
   // .met        number: end of prolog
   // .moving     number: node incrementation on global search
   // .multiple   number: of leading brackets '['  (0, 1, 2)
   // .node       number: of current within .off
   // .off        parent WikiTom node
   // .points     Array of Arrays with found and replacement information
   // .scheme     URL protocol, may be false or terminated by ':'
   // .shift      replacement string for link target
   // .shower     non-empty title string (continued in fuerher node)
   // .source     wikitext including prolog
   // .url2w      URL2wiki object
   "use strict";
   var WebLK;



   WSTM.o.Weblink  =  function (available, allow) {
      // .constructor for new
      // Precondition:
      //    available  -- limiting URL length, or false
      //    allow      -- limiting length for suspicious titles, or false
      // Postcondition:
      //    Returns .o.Weblink object
      // Uses:
      //    >  .o.Weblink::
      //       >  .joinSuffix
      //       >< .re.schemes
      //        < .maxURL
      //        < .maxTitle
      //        < .points
      //    >  .w.link.protocol.weblinks
      // 2013-03-15 PerfektesChaos@de.wikipedia
      var p;
      for (p in WSTM.o.Weblink) {
         this[ p ]  =  WSTM.o.Weblink[ p ];
      }   // for p in
      if (! WebLK.re.schemes) {
         WebLK.re.schemes  =  WSTM.w.link.protocol.weblinks;
      }
      if (typeof(available) === "number") {
         this.maxURL  =  available;
      }
      if (typeof(allow) === "number") {
         this.maxTitle  =  allow;
      }
      this.points   =  new Array(WebLK.joinSuffix + 1);
      return this;
   };   // .o.Weblink() .constructor
   WebLK  =  WSTM.o.Weblink;



   // Definition of constants
   // 2016-01-01 PerfektesChaos@de.wikipedia
   WebLK.joinProlog  =  0;
   WebLK.joinLBrack  =  1;
   WebLK.joinScheme  =  2;
   WebLK.joinSite    =  3;
   WebLK.joinSlash   =  4;
   WebLK.joinSelect  =  5;
   WebLK.joinShow    =  6;
   WebLK.joinRBrack  =  7;
   WebLK.joinSuffix  =  8;
   WebLK.maxTitle    =  300;
   WebLK.maxURL      =  511;
   WebLK.re          =  { schemes: "|ftp|git|http|https|mms|svn|",
                          split:   new RegExp("[\"{}]"),
                          stop:    "({,:.;?!\"\\"
                        };




/*
   WebLK.prototype.fruit  =  function (about) {
      // Debug info
      // Precondition:
      //    about  -- informative key, or nothing.
      // Debug
      // 2013-03-15 PerfektesChaos@de.wikipedia
var e, i,
    o  =  { },
    s  =  "|joinLBrack|joinProlog|joinRBrack|joinScheme|joinSelect|"
          + "joinShow|joinSite|joinSlash|joinSuffix|"
          + "lowScheme|maxTitle|maxURL|off|points|"
          + "re|source|url2w|";
for (e in this) {
   if (typeof(this[e]) !== "function") {
      if (s.indexOf("|"+e+"|") < 0) {
         o[e]  =  this[e];
      }
   }
}   // for e in this
s  =  (about  ?  " " + about  :  "" )   +   "  points";
for (i = 0;  i <= WebLK.joinSuffix;  i++) {
   e  =  this.points[ i ];
   s  =  s + "\n" + e[0] + "    >>";
   if (typeof(e[1]) === "string") {
      s  =  s + e[1].substr(0,350);
   } else {
      s  =  s + e[1];
   }
   s  =  s + "<<    " + e[2];
}   // for i
mw.log(WSTM.debugging,".o.Weblink.fruit()"+s,0,o);
   };   // .o.Weblink.fruit()
*/



   WebLK.prototype.facet  =  function (about, adjacent) {
      // Retrieve string from range
      // Precondition:
      //    about      -- level to begin
      //                  .joinSite
      //                  .joinSlash
      //                  .joinShow
      //                  .joinRBrack
      //    adjacent   -- optional: number of last level to include
      // Postcondition:
      //    Returns string including range
      // Uses:
      //    >  .o.Weblink::
      //       >  .points
      //       >  .joinSite
      //       >  .joinSlash
      //       >  .joinShow
      // 2013-03-15 PerfektesChaos@de.wikipedia
      var e, i,
          k  =  (typeof(adjacent) === "number"  ?  adjacent  :  about),
          r  =  "";
      for (i = about;  i <= k;  i++) {
         e  =  this.points[ i ][1];
         switch (i) {
            case WebLK.joinSite :
               if (i > about) {
                  r  =  r + "//";
               }
               break;
            case WebLK.joinSlash :
               e  =  "/";
               break;
            case WebLK.joinShow :
               if (i > about) {
                  e  =  " " + e;
               }
               break;
         }   // switch i
         if (e) {
            r  =  r + e;
         }
      }   //   // for i
      return r;
   };   // .o.Weblink.facet()



   WebLK.prototype.fair  =  function () {
      // Perform user defined modifications
      // Uses:
      //    >  .o.Weblink::
      //       >  .multiple
      //       >  .joinProlog
      //       >  .joinLBrack
      //       >  .joinShow
      //       >  .joinSuffix
      //       >  .joinScheme
      //       >  .joinSelect
      //       >  .joinRBrack
      //        < .mode
      //       .facet()
      //       .feed()
      //       .fit()
      //       .fill()
      //    >  .mod.url
      //    >  .o.WikiTom.TextOnly
      //     < .mod.lazy
      //    .w.link.replace.flip()
      //    .str.substrEnd()
      // 2013-03-15 PerfektesChaos@de.wikipedia
      var i, sB, sE, x;
      if (this.multiple) {
         sB  =  this.facet(WebLK.joinProlog, WebLK.joinLBrack);
         if (this.showing) {
            sE  =  "]";
         } else {
            sE  =  this.facet(WebLK.joinShow, WebLK.joinSuffix);
         }
      } else {
         this.fit(WebLK.joinSuffix, this.feed(), false);
         sB  =  this.facet(WebLK.joinProlog);
         sE  =  this.facet(WebLK.joinSuffix);
      }
      x  =  WSTM.w.link.replace.flip(WSTM.mod.url,
                                     this.facet(WebLK.joinScheme,
                                                WebLK.joinSelect),
                                     sB,
                                     sE,
                                     "url");
      if (x) {
         if (typeof(x) === "object") {
            if (typeof(x[0]) === "string") {
               sB  =  x[0];
               if (sB) {
                  if (WSTM.str.substrEnd(sB, 2)  ===  "[[") {
                     this.mode  =  WSTM.o.WikiTom.LinkWiki;
                  }
               }
               this.fit(WebLK.joinProlog, sB, true);
               this.fit(WebLK.joinLBrack, false, true);
            }
            if (x[1]) {
               this.fill(x[1]);
            }
            if (typeof(x[2]) === "string") {
               sE  =  x[2];
               this.fit(WebLK.joinShow, false, true);
               this.fit(WebLK.joinRBrack, false, true);
               if (sE) {
                  i  =  sE.indexOf("]");
                  if (i >= 0) {
                     this.fit(WebLK.joinShow,  sE.substr(0, i),  true);
                     this.fit(WebLK.joinRBrack, "]", true);
                     sE  =  sE.substr(i + 1);
                  }
               }
               this.fit(WebLK.joinSuffix, sE, true);
            }
         } else {
            this.fill(x);
         }
         WSTM.mod.lazy  =  false;
      }
   };   // .o.Weblink.fair()



   WebLK.prototype.fault  =  function (adapt, abolish, align, around) {
      // Convert URL formatted by titled wikilink syntax
      // Precondition:
      //    adapt    -- string segment beginning with "//"
      //    abolish  -- position of Pipe in adapt
      //    align    -- position of "/" in adapt
      //    around   -- position of "]" in adapt
      // Uses:
      //    >  .o.Weblink::
      //       >  .joinProlog
      //       >  .joinShow
      //       >  .joinSelect
      //        < .mod.lazy
      //       .facet()
      //       .fit()
      //       .fetch()
      //       .fenced()
      //    .errors.found()
      // 2013-03-15 PerfektesChaos@de.wikipedia
      var i  =  abolish,
          j  =  adapt.indexOf(" "),
          s  =  this.facet(WebLK.joinProlog);
      this.fit(WebLK.joinProlog,
               s.substr(0,  s.length - 1),
               true);
      s  =  adapt.substr(0,  around + 2);
      if (j > 0  &&  j < abolish) {
         i  =  j;
      }
      this.fetch(s.substr(0, i),  false);
      s  =  adapt.substr(i + 1,  around - i);
      this.fit(WebLK.joinShow, s, false);
      if (i === j) {
         this.fit(WebLK.joinShow,  s.replace(/\|/g, " "),  true);
      }
      this.fenced(this.facet(WebLK.joinSelect), true);
      WSTM.mod.lazy  =  false;
      WSTM.errors.found("weblinkWikilink",  true,  "[[" + s);
   };   // .o.Weblink.fault()



   WebLK.prototype.feed  =  function () {
      // Retrieve not yet processed source string
      // Postcondition:
      //    Returns string
      // Uses:
      //    >  .o.Weblink::
      //       >  .source
      //       >  .move
      // 2013-03-15 PerfektesChaos@de.wikipedia
      return  this.source.substr(this.move);
   };   // .o.Weblink.feed()



   WebLK.prototype.fenced  =  function (adjust, already) {
      // Escape bracket pairs within URL path and store
      // Precondition:
      //    adjust   -- string with potential bracket pairs
      //    already  -- Not initial scan
      // Postcondition:
      //    Returns adjust, or false if escaped and stored
      // Uses:
      //    >  .o.Weblink::
      //       >  .joinSelect
      //       >  .joinRBrack
      //       >  .joinScheme
      //        < .mod.lazy
      //       .fit()
      //       .facet()
      //    .errors.found()
      // Requires: JavaScript 1.3   charCodeAt()
      // 2013-08-09 PerfektesChaos@de.wikipedia
      var i, k, s,
          n      =  0,
          slash  =  adjust,
          r      =  adjust;
      do {
         k  =  slash.indexOf("]");
         if (k > 0) {
            i  =  slash.indexOf("[");
            if (i < 0) {
               break;   // while
            } else if (i  <  k - 1) {
               if (slash.charCodeAt(k + 1)  ===  93) {
                  break;   // while
               } else {
                  s  =  slash.slice(i + 1,  k);
                  if (s.length) {
                     if (! /^[-a-z.A-Z_0-9]+$/.test(s)) {
                        break;   // while
                     }
                  }
                  if (k > 0) {
                     slash   =  slash.substr(0, i)
                                +  "&#91;" + s + "&#93;"
                                +  slash.substr(k + 1);
                     n      +=  8;
                  }
               }
            } else {
               break;   // while
            }
         }
      } while (k > 0);   // do
      if (n) {
         if (! already) {
            this.fit(WebLK.joinSelect,
                     adjust.substr(0,  slash.length - n),
                     false);
         }
         this.fit(WebLK.joinSelect, slash, true);
         this.fit(WebLK.joinRBrack, "", true);
         WSTM.mod.lazy  =  false;
         WSTM.errors.found("weblinkBrackets",
                           true,
                           this.facet(WebLK.joinScheme,
                                      WebLK.joinSelect));
         r  =  false;
      }
      return r;
   };   // .o.Weblink.fenced()



   WebLK.prototype.fetch  =  function (apply, already) {
      // Split URL string into components
      // Precondition:
      //    apply    -- string segment beginning with URL
      //    already  -- Not initial scan
      // Postcondition:
      //    object components are initialized or updated
      // Uses:
      //    >  .o.Weblink::
      //       >  .joinScheme
      //       >  .joinSite
      //       >  .joinSelect
      //       >  .joinShow
      //       >  .joinSuffix
      //       .fit()
      //       .facet()
      // Requires: JavaScript 1.3   charCodeAt()
      // 2013-03-15 PerfektesChaos@de.wikipedia
      var s       =  false,
          show    =  false,
          site    =  false,
          slash   =  false,
          suffix  =  false,
          i       =  -1;
      if (apply) {
         i  =  apply.indexOf("//");
      }
      if (i >= 0) {
         site  =  apply.substr(i + 2);
         if (i > 3) {
            if (apply.charCodeAt(i - 1)  ===  58) {   // ':'
               s  =  apply.substr(0, i);
            } else {
               site  =  false;
            }
         } else if (i) {
            site  =  false;
         }
      }
      this.fit(WebLK.joinScheme, s, already);
      if (site) {
         i  =  site.indexOf(" ");
         if (i > 0) {
            site  =  site.substr(0, i);
            show  =  site.substr(i + 1);
            i     =  show.indexOf("]");
            s     =  this.facet(WebLK.joinShow);
            if (s) {
               show  =  show  +  s;
            }
            if (i > 0) {
               suffix  =  this.facet(WebLK.joinSuffix);
               suffix  =  show.substr(i)  +  (suffix ? suffix : "");
               show    =  show.substr(0, i);
           }
         }
         i  =  site.indexOf("/");
         if (i > 0) {
            this.fit(WebLK.joinSlash, "/", already);
            slash  =  site.substr(i + 1);
            site   =  site.substr(0, i);
         }
         i  =  site.indexOf("?");
         if (i > 0) {
            slash  =  site.substr(i + 1)
                      +   (slash  ?  "/" + slash  :  "");
            site   =  site.substr(0, i);
         }
         if (site.length < 5) {   // ix.de
            this.fit(WebLK.joinScheme, false, true);
         } else {
            if (show) {
               this.fit(WebLK.joinShow, show, true);
            }
            if (suffix) {
               this.fit(WebLK.joinSuffix, suffix, true);
            }
         }
      }
      this.fit(WebLK.joinSite, site, already);
      this.fit(WebLK.joinSelect, slash, already);
   };   // .o.Weblink.fetch()



   WebLK.prototype.fiat  =  function () {
      // Analyze and adjust URL and embedding within brackets
      // Uses:
      //    >  .o.Weblink::
      //       >  .move
      //       >  .multiple
      //       >  .joinSite
      //       >  .joinScheme
      //       >  .joinSelect
      //       >  .leaf
      //       >  .joinSuffix
      //       >< .source
      //       .feed()
      //       .fault()
      //       .fit()
      //       .facet()
      //       .fenced()
      //       .filled()
      //       .frozen()
      //     < .mod.lazy
      //    .errors.found()
      //    .str.terminated()
      // Requires: JavaScript 1.3   charCodeAt()
      // 2013-09-06 PerfektesChaos@de.wikipedia
      var i, j, k, less, limit, n, s, slashed, suffix,
          m    =  -2;
      s     =  this.feed();
      i     =  s.indexOf("\n\n");
      less  =  (i > 0);
      if (less) {
         s            =  s.substr(0, i);
         this.source  =  this.source.substr(0,  this.move + i);
      }
      i      =  s.indexOf("</ref>");
      limit  =  (i > 0);
      if (limit) {
         less  =  true;
         s     =  s.substr(0, i);
      }
      i  =  s.indexOf("/");
      k  =  s.indexOf("]");
      n  =  s.indexOf("\n");
      if (this.multiple > 1  &&  k > 0) {
         m  =  s.indexOf("|");
         if (m > 0) {
            if (m < k
                &&   s.charCodeAt(k + 1)  ===  93
                &&   (n < 0  ||  n > k)) {   // ']'
               this.fault(s, m, i, k);
            } else {
               m  =  -3;
            }
         }
         less  =  true;
      }
      if (m < 0) {
         j  =  s.indexOf(" ");
         if (j < 0) {
            m  =  s.length;
         } else {
            m  =  j;
         }
         if (n >= 0  &&  n < m) {
            m     =  n;
            less  =  true;
         }
         if (k >= 0  &&  k < m) {
            m  =  k;
         }
         if (i > 0  &&  i < m) {
            this.fit(WebLK.joinSite,  s.substr(0, i),  false);
            this.fit(WebLK.joinSlash, "/", false);
            slashed  =  this.feed();
            if (limit) {
               i  =  slashed.indexOf("</ref>");
               if (i > 0) {
                  slashed  =  slashed.substr(0, i);
               }
            }
            if (j > 0) {
               j  =  slashed.indexOf(" ");
               if (j > 0) {
                  slashed  =  slashed.substr(0, j);
               } else {
                  slashed  =  false;
               }
            }
         } else {
            i  =  s.indexOf("?", m);
            if (i > 0  &&  i < m) {
               slashed  =  s.substr(i);
               this.fit(WebLK.joinSite,  s.substr(0, i),  false);
               this.fit(WebLK.joinSlash, "/", true);
               WSTM.mod.lazy  =  false;
               WSTM.errors.found("weblinkStrange",
                                 true,
                                 this.facet(WebLK.joinScheme,
                                            WebLK.joinSlash)
                                 + slashed);
            } else {
               this.fit(WebLK.joinSite,  s.substr(0, m),  false);
               this.fit(WebLK.joinSlash, "/", true);
               slashed  =  false;
            }
         }
         if (slashed) {
            if (n > 0) {
               n  =  slashed.indexOf("\n");
               if (n > 0) {
                  slashed  =  slashed.substr(0, n);
               }
            }
            if (k > 0) {
               k  =  slashed.indexOf("]");
               if (k >= 0) {
                  s  =  slashed.substr(0, k);
                  i  =  s.indexOf("[");
                  if (i < 0) {
                     this.fit(WebLK.joinSelect, s, false);
                     this.fit(WebLK.joinRBrack, "]", false);
                     slashed  =  false;
                     suffix   =  this.feed();
                  } else {
                     slashed  =  this.fenced(slashed, false);
                     if (slashed) {
                        i  =  slashed.indexOf("]");
                        if (i < 0) {
                           suffix  =  false;
                        } else {
                           suffix   =  slashed.substr(i + 1);
                           slashed  =  slashed.substr(0, i);
                           this.fit(WebLK.joinRBrack, "]", false);
                        }
                     }
                  }
                  if (suffix) {
                     if (this.leaf) {
                        suffix  =  WSTM.str.terminated(suffix, "|");
                     }
                     this.fit(WebLK.joinSuffix, suffix, false);
                  }
               }
            } else if (this.limited) {
               this.fit(WebLK.joinSelect, slashed, false);
               i  =  s.indexOf(" ");
               if (i > 0) {
                  this.fit(WebLK.joinShow,  s.substr(i + 1),  false);
               }
               if (! less) {
                  if (this.frozen()) {
                     slashed  =  false;
                  }
               }
               if (slashed) {
                  this.fit(WebLK.joinRBrack, "]", true);
                  WSTM.errors.found("weblinkBracketRight",
                                    true,
                                    this.facet(WebLK.joinScheme,
                                               WebLK.joinSelect));
                  slashed  =  false;
               }
            }
            if (slashed) {
               if (this.multiple === 0) {
                  slashed  =  WSTM.str.terminated(slashed, " ");
                  slashed  =  WSTM.str.terminated(slashed, "\n");
                  slashed  =  WSTM.str.terminated(slashed, "</ref>");
               }
               this.fit(WebLK.joinSelect, slashed, false);
            }
         }
         if (this.limited) {
            if (! this.facet(WebLK.joinRBrack)) {
               this.filled();
            }
         }
      }
   };   // .o.Weblink.fiat()



   WebLK.prototype.fill  =  function (apply) {
      // Process URL replacement
      // Precondition:
      //    apply  -- URL, or replaced string
      // Uses:
      //    >  .o.Weblink::
      //        < .shift
      //        < .lazy
      //        < .mode
      //       .fetch()
      //       .facet()
      //    >  .o.WikiTom.TextOnly
      // 2013-03-15 PerfektesChaos@de.wikipedia
      this.fetch(apply, true);
      if (! this.facet(WebLK.joinSite)) {
         this.shift  =  apply;
         this.lazy   =  false;
         this.mode   =  WSTM.o.WikiTom.TextOnly;
      }
   };   // .o.Weblink.fill()



   WebLK.prototype.filled  =  function () {
      // Analyze and adjust link title and suffix
      // Uses:
      //    >  .o.Weblink::
      //       >  .source
      //       >  .maxTitle
      //       >  .joinScheme
      //       >  .joinSelect
      //       >  .joinShow
      //       >  .joinRBrack
      //       >  .leaf
      //       >  .joinSuffix
      //       >  .limited
      //       .feed()
      //       .further()
      //       .fit()
      //       .facet()
      //       .flat()
      //    .str.terminated()
      //    .str.trimR()
      //    .errors.found()
      // Requires: JavaScript 1.3   charCodeAt()
      // 2013-03-15 PerfektesChaos@de.wikipedia
      var i, j, k, s, show;
      s  =  this.feed();
      k  =  s.charCodeAt(0);
      if (k === 32) {   // ' '
         s  =  s.substr(1);
         k  =  s.indexOf("]");
         if (k < 0) {
            if (s.length < this.maxTitle) {
               k  =  this.further();   // return
            }
         } else if (k < this.maxTitle) {
            i  =  s.indexOf("[[");
            if (i >= 0  &&  i < k) {
               show  =  s.substr(0, i);
               this.fit(WebLK.joinShow, show, false);
               this.fit(WebLK.joinRBrack, "]", false);
               s  =  s.substr(i);
               if (this.leaf) {
                  s  =  WSTM.str.terminated(s, "|");
               }
               this.fit(WebLK.joinSuffix, s, false);
               if (i) {
                  k     =  show.length;
                  show  =  WSTM.str.trimR(show);
                  if (show.length < k) {
                     s  =  " " + s;
                     this.fit(WebLK.joinShow, show, true);
                     this.fit(WebLK.joinSuffix, s, true);
                  }
               }
               i  =  2;
               do {
                  k  =  s.indexOf("]]", i);
                  if (k > 0) {
                     i  =  k + 2;
                     k  =  s.indexOf("]", i);
                     j  =  s.indexOf("[[", i);
                     if (j > 0  &&  k > 0  &&  k < j) {
                        i  =  -2;
                     }
                  } else {
                     k  =  s.indexOf("]", i);
                     i  =  -3;
                  }
                  if (k > 0  &&  i < 0) {
                     s  =  s.substr(0, k)  +  s.substr(k + 1);
                     this.fit(WebLK.joinSuffix, s, true);
                     break;   // while
                  }
               } while (k > 0);
               k  =  0;
            } else {
               this.fit(WebLK.joinShow,  s.substr(0, k),  false);
               this.fit(WebLK.joinRBrack, "]", false);
               s  =  s.substr(k + 1);
               if (this.leaf) {
                  s  =  WSTM.str.terminated(s, "|");
               }
               this.fit(WebLK.joinSuffix, s, false);
            }
            this.flat();
         } else {
            k  =  -2;
         }
      } else  if (k === 93) {   // ']'
         this.fit(WebLK.joinRBrack, "]", false);
         s  =  s.substr(1);
         if (this.leaf) {
            s  =  WSTM.str.terminated(s, "|");
         }
         this.fit(WebLK.joinSuffix, s, false);
      }
      if (this.limited) {
         if (k < 0) {
            WSTM.errors.found("weblinkBracketRight",
                              false,
                              "[" + this.facet(WebLK.joinScheme,
                                               WebLK.joinSelect));
         }
      }
   };   // .o.Weblink.filled()



   WebLK.prototype.filter  =  function () {
      // Test URL for user warning request
      // Uses:
      //    >  .o.Weblink::
      //       >  .shift
      //       >  .joinScheme
      //       >  .joinSelect
      //       .facet()
      //    .warn.filter()
      // 2013-03-15 PerfektesChaos@de.wikipedia
      var s;
      if (this.shift) {
         s  =  this.shift;
      } else {
         s  =  this.facet(WebLK.joinScheme, WebLK.joinSelect);
      }
      WSTM.warn.filter(s, "url");
   };   // .o.Weblink.filter()



   WebLK.prototype.finalize  =  function () {
      // Terminate analysis
      // Uses:
      //    >  .o.Weblink::
      //       >  .mode
      //       >  .joinProlog
      //       >  .joinLBrack
      //       >  .joinScheme
      //       >  .joinSelect
      //        < .index
      //        < .keep
      //       .facet()
      //       .forward()
      //    >  .o.WikiTom.LinkWeb
      // 2013-03-15 PerfektesChaos@de.wikipedia
      var s;
      if (this.lazy) {
         s           =  this.facet(WebLK.joinProlog, WebLK.joinLBrack);
         this.index  =  s.length;
         s           =  this.facet(WebLK.joinScheme, WebLK.joinSelect);
         this.keep   =  s.length;
      } else {
         this.forward();
      }
   };   // .o.Weblink.finalize()



   WebLK.prototype.fire  =  function () {
      // Start analysis
      // Uses:
      //    >  .o.Weblink::
      //       >  .node
      //       >  .join
      //       >  .off
      //       >  .leaf
      //       >  .limited
      //       >  .index
      //       >  .joinProlog
      //       >  .multiple
      //       >  .met
      //       >  .joinLBrack
      //       >  .mark
      //       >  .joinScheme
      //       >  .scheme
      //       >  .lackScheme
      //       >  .lowScheme
      //       >  .joinSite
      //        < .source
      //       .fit()
      //       .fiat()
      //       .fix()
      //       .facet()
      //       .friend()
      //       .fair()
      //       .filter()
      //       .finalize()
      //    >  .o.WikiTom.LinkWeb
      //    >  .mod.url
      //    >  .warn.url
      //    .o.WikiTom().fetch()
      //    .w.link.web.fetch()
      //    .str.substrEnd()
      // 2015-12-25 PerfektesChaos@de.wikipedia
      var i, s;
      this.source  =  this.off.fetch(this.node, this.join, false);
      if (this.leaf  &&  ! this.limited) {
         i  =  this.source.indexOf("|", this.index);
         if (i > 0) {
            this.source  =  this.source.substr(0, i);
         }
      }
      WSTM.w.link.web.fetch(this, this.limited);
      if ((this.multiple > 0)  ===  this.limited) {
         this.fit(WebLK.joinProlog,
                  this.source.substr(0, this.met),
                  false);
         if (this.multiple) {
            s  =  this.feed();
            this.fit(WebLK.joinLBrack,  s.substr(0, this.mark),  false);
            if (this.mark > 1) {
               this.fit(WebLK.joinLBrack, "[", true);
            }
         }
         this.fit(WebLK.joinScheme, this.scheme, false);
         if (this.lackScheme || this.lowScheme) {
            this.fit(WebLK.joinScheme, this.scheme, true);
         }
         this.fiat();
         this.fix();
         s  =  this.facet(WebLK.joinSite);
         if (s) {
            if (WSTM.str.substrEnd(s, 4)  ===  ".org") {
               this.friend();
            }
            if (this.mode === WSTM.o.WikiTom.LinkWeb) {
               if (WSTM.mod.url) {
                  this.fair();
               }
               if (WSTM.warn.url) {
                  if (this.mode === WSTM.o.WikiTom.LinkWeb) {
                     this.filter();
                  }
               }
            }
         }
         this.finalize();
      }
   };   // .o.Weblink.fire()



   WebLK.prototype.fit  =  function (about, apply, alter) {
      // Extend modified area
      // Precondition:
      //    about  -- level
      //    apply  -- string segment
      //    alter  -- string is modified
      // Uses:
      //    >  .o.Weblink::
      //       >  .joinLBrack
      //       >  .joinScheme
      //       >  .joinSite
      //       >  .joinSelect
      //       >  .joinSuffix
      //       >< .points
      //       >< .move
      //        < .lazy
      //    .w.link.filter()
      // 2015-09-12 PerfektesChaos@de.wikipedia
      var swap,
          e  =  this.points[ about ],
          s  =  (apply ? apply : "");
      if (alter) {
         if (s !== (e[1] ? e[1] : "")) {
            e[2]  =  true;
         }
      } else if (apply) {
         e[0]        =  apply.length;
         this.move  +=  e[0];
      }
      e[1]  =  s;
      switch (about) {
         case WebLK.joinScheme:
            swap  =  s.toLowerCase();
            if (swap !== s) {
               e[1]  =  swap;
               e[2]  =  true;
            }
            if (alter) {
               e[2]  =  apply;
            } else {
               e[0]       +=  2;
               this.move  +=  2;
            }
            break;
         case WebLK.joinSite:
            if (s) {
               swap  =  WSTM.w.link.filter(s, false);
               swap  =  (swap ? swap : s).toLowerCase();
               if (swap !== s) {
                  e[1]  =  swap;
                  e[2]  =  true;
               }
            }
            break;
         case WebLK.joinSelect:
            if (s) {
               swap  =  WSTM.w.link.filter(s, false);
               if (swap  &&  swap !== s) {
                  e[1]  =  swap;
                  e[2]  =  true;
               }
            }
            break;
         case WebLK.joinShow:
            if (! alter) {
               e[0]  +=  1;
               this.move++;
            }
            break;
         case WebLK.joinSuffix:
            if (! alter) {
               this.move  -=  e[0];
            }
            break;
      }   // switch about
      if (e[2]) {
         this.lazy  =  false;
      }
   };   // .o.Weblink.fit()



   WebLK.prototype.fix  =  function () {
      // Inspect resulting URL for suspicious content
      // Uses:
      //    >  .o.Weblink::
      //       >  .re.schemes
      //       >  .joinScheme
      //       >  .joinSite
      //       >  .joinSelect
      //       >  .multiple
      //       >  .re.split
      //       >  .re.stop
      //       >  .maxURL
      //       .facet()
      //    .str.substrExcept()
      //    .util.isURL()
      //    .str.substrEnd()
      //    .errors.found()
      // 2016-01-28 PerfektesChaos@de.wikipedia
      var s     =  this.facet(WebLK.joinScheme),
          site  =  this.facet(WebLK.joinSite),
          scan;
      if (s) {
         scan  =  "|"  +  WSTM.str.substrExcept(s, 1)  +  "|";
         if (WebLK.re.schemes.indexOf(scan) < 0) {
            WSTM.errors.found("weblinkScheme",
                              false,
                              this.facet(WebLK.joinScheme,
                                         WebLK.joinSelect));
         }
         s  =  this.facet(WebLK.joinScheme, WebLK.joinSite);
      } else {
         s  =  "//" + site;
      }
      if (site  &&  WSTM.util.isURL(s)) {
         WSTM.errors.found("weblinkDomain", false, site);
      }
      scan  =  this.facet(WebLK.joinSelect);
      if (scan) {
         s  =  s + "/" + scan;
         if (scan.indexOf("''") > 3) {
            WSTM.errors.found("weblinkApostrophs", false, s);
         }
         if (this.multiple === 1) {
            if (scan.indexOf("|") >= 0) {
               WSTM.errors.found("weblinkPipe", false, s);
            }
         }
         if (WebLK.re.split.test(scan)) {
            WSTM.errors.found("weblinkSpecial", false, s);
         }
         if (WebLK.re.stop.indexOf(WSTM.str.substrEnd(scan, 1))
             >=  0) {
            WSTM.errors.found("weblinkPunctuation", false, s);
         }
         //  /<\/[a-z]+>/.test(s)
      }
      if (s.length > this.maxURL) {
         WSTM.errors.found("weblinkURLlength", false, s);
      }
  };   // .o.Weblink.fix()



   WebLK.prototype.flat  =  function () {
      // Standardize link title spacing
      // Postcondition:
      //    Returns true if shrinked
      // Uses:
      //    >  .o.Weblink::
      //       >< .re.spaces
      //        < .mod.lazy
      //       .facet()
      //    .str.trim()
      // 2013-03-15 PerfektesChaos@de.wikipedia
      var s  =  this.facet(WebLK.joinShow),
          n  =  s.length,
          r  =  false;
      if (s.indexOf("\n") >= 0) {
         WSTM.mod.lazy  =  false;
      }
      s  =  WSTM.str.trim(s, true);
      if (s) {
         if (! this.re.spaces) {
            this.re.spaces  =  new RegExp("  +", "g");
         }
         s  =  s.replace(this.re.spaces, " ");
         r  =  (s.length < n);
      } else {
         s  =  false;
         r  =  true;
      }
      this.fit(WebLK.joinShow, s, r);
      return r;
   };   // .o.Weblink.flat()



   WebLK.prototype.focus  =  function (at, advance) {
      // Aggregate splitted information
      // Precondition:
      //    at       -- code of first element to be collected
      //    advance  -- code of last element to be collected
      // Postcondition:
      //    Returns  Array [old length,
      //                    replacement string,
      //                    replace it]
      // Uses:
      //    >  .o.Weblink::
      //       >  .mode
      //       >  .lazy
      //       >  .points
      //       >  .joinShow
      //       .facet()
      //    >  .o.WikiTom.LinkWeb
      // 2013-05-26 PerfektesChaos@de.wikipedia
      var e, i, s,
          l  =  (this.mode !== WSTM.o.WikiTom.LinkWeb),
          m  =  l,
          n  =  0;
      if (this.lazy) {
         for (i = at;  i <= advance;  i++) {
            e   =  this.points[ i ];
            n  +=  e[0];
         }   // for i
         s  =  false;
      } else {
         s  =  this.facet(at, advance);
         for (i = at;  i <= advance;  i++) {
            e   =  this.points[ i ];
            n  +=  e[0];
            l   =  (l || e[2]);
         }   // for i
         if (at === WebLK.joinShow  &&  this.points[ at ][1]  &&  ! m) {
            s  =  " " + s;
         }
      }
      return  [n, s, l];
   };   // .o.Weblink.focus()



   WebLK.prototype.format  =  function (anode, access, around, arg) {
      // Initialize existing weblink object with node
      // Precondition:
      //    anode   -- parent node WikiTom object
      //    access  -- location object
      //               .i  start of "://" or "[" in .j
      //               .j  start of interesting region in .k
      //               .k  sibling number of location
      //    around  -- number of known leading brackets  '[' (0, 1)
      //    arg     -- template parameter on unbracket link
      // Postcondition:
      //    Returns position object to continue search
      //            .i  string position in entire node
      //            .k  node number
      //    .o.Weblink object was set, has been analyzed and reformatted
      //    WikiTom nodes in basic text have been updated
      // Uses:
      //    WikiTom().toString()
      //    >  .o.Weblink::
      //       >  .joinSuffix
      //       >  .joinScheme
      //       >< .move
      //        < .off
      //        < .node
      //        < .index
      //        < .join
      //        < .multiple
      //        < .limited
      //        < .leaf
      //        < .mode
      //        < .keep
      //        < .lazy
      //        < .moving
      //        < .shift
      //        < .shower
      //        < .points
      //       .fire()
      //       .facet()
      //    >  .o.WikiTom.LinkWeb
      // 2013-05-02 PerfektesChaos@de.wikipedia
      var i, r;
      this.move      =  0;
      this.off       =  anode;
      this.node      =  access.k;
      this.index     =  access.i;
      this.join      =  access.j;
      this.multiple  =  around;
      this.limited   =  (around > 0);
      this.leaf      =  arg;
      this.mode      =  WSTM.o.WikiTom.LinkWeb;
      this.keep      =  -1;
      this.lazy      =  true;
      this.moving    =  0;
      this.shift     =  false;
      this.shower    =  false;
      for (i = 0;  i  <=  WebLK.joinSuffix;  i++) {
         this.points[ i ]  =  [0, false, false];
      }   // for i
      this.fire();
      r  =  { k:  this.node + this.moving };
      if (this.moving) {
         r.i  =  0;
      } else {
         r.i         =  this.join + this.index;
         this.index  =  r.i;
         if (this.keep > 0) {
            r.i  +=  this.keep - 1;
         } else if (this.move < 0) {
            r.i  +=  this.move;
         } else if (this.limited) {
            r.i  +=  9;
         } else {
            r.i  +=  2  +  this.facet(WebLK.joinScheme).length;
         }
      }
      return r;
   };   // .o.Weblink.format()



   WebLK.prototype.forward  =  function () {
      // Modify source text
      // Uses:
      //    >  .o.Weblink::
      //       >  .joinProlog
      //       >  .joinLBrack
      //       >  .off
      //       >  .node
      //       >  .join
      //       >  .shift
      //       >  .joinScheme
      //       >  .joinSelect
      //       >  .mode
      //       >  .joinShow
      //       >  .joinSuffix
      //        < .index
      //        < .keep
      //       .focus()
      //    >  .o.WikiTom()::
      //       .flip()
      // 2013-05-26 PerfektesChaos@de.wikipedia
      var got  =  this.focus(WebLK.joinProlog, WebLK.joinLBrack);
      this.index  =  got[0];
      if (got[2]) {
         this.off.flip(this.node, this.join, this.index, got[1]);
         this.index  =  got[1].length;
      }
      got  =  this.focus(WebLK.joinScheme, WebLK.joinSelect);
      if (typeof(this.shift) === "string") {
         got[1]  =  this.shift;
         got[2]  =  true;
      }
      if (got[2]) {
         this.keep  =  got[1].length;
         this.off.flip(this.node,
                       this.join + this.index,
                       got[0],
                       got[1]);
      } else {
         this.keep  =  got[0];
      }
      got  =  this.focus(WebLK.joinShow, WebLK.joinSuffix);
      if (got[2]) {
         this.off.flip(this.node,
                       this.join + this.index + this.keep,
                       got[0],
                       got[1]);
      }
      if (this.mode !== WSTM.o.WikiTom.LinkWeb) {
         this.keep  =  -1;
      }
   };   // .o.Weblink.forward()



   WebLK.prototype.freeze  =  function (advanced) {
      // Freeze link target as recently formatted
      // Uses:
      //    >  .o.Weblink::
      //       >  .keep
      //       >  .node
      //       >  .index
      //       >  .off
      //        < .mode
      //        < .lookup
      //    >  .o.WikiTom.folder()
      // 2013-03-15 PerfektesChaos@de.wikipedia
      var url,
          r  =  advanced;
      if (this.keep > 0) {
         url  =  this.off.folder(this.index,
                                 this.node,
                                 this.index + this.keep,
                                 this.node);
         if (url) {
            url.mode    =  WSTM.o.WikiTom.LinkWeb;
            url.lookup  =  false;
            r           =  { i: 0,
                             k: this.node  +  (this.index ? 2 : 1) };
         }
      }
      return r;
   };   // .o.Weblink.freeze()



   WebLK.prototype.frozen  =  function () {
      // Skip nowiki/comment WTOM within link title
      // Postcondition:
      //    Returns true if closing bracket found after WTOM
      // Uses:
      //    >  .o.Weblink::
      //       >  .moving
      //       >  .node
      //       >  .off
      //       >  .maxTitle
      //    >  .o.WikiTom.TextOnly
      //    .o.WikiTom().focus()
      //    .o.WikiTom().fetch()
      // 2013-09-12 PerfektesChaos@de.wikipedia
      var i, other, s,
          r  =  false;
      if (! this.moving) {
         other  =  this.off.focus(this.node + 1);
         if (other) {
            if (other.mode > WSTM.o.WikiTom.TextOnly) {
               other  =  this.off.focus(this.node + 2);
               if (other) {
                  if (other.mode <= WSTM.o.WikiTom.TextOnly) {
                     s  =  this.off.fetch(this.node + 2,  0,  false);
                     i  =  s.indexOf("\n");
                     if (i >= 0) {
                        s  =  s.substr(0, i);
                     }
                     i  =  s.indexOf("]");
                     if (i >= 0) {
                        if (i < WebLK.maxTitle) {
                           s  =  s.substr(0, i);
                           r  =  (s.indexOf("[") < 0);
                        }
                     }
                  }
               }
            }
         }
      }
      return r;
   };   // .o.Weblink.frozen()



   WebLK.prototype.friend  =  function () {
      // Check whether detected URL can be converted into wikilink
      // Uses:
      //    >  .o.Weblink::
      //       >  .joinScheme
      //       >  .joinSite
      //       >  .joinSelect
      //       >  .joinShow
      //       >  .joinSuffix
      //       >  .limited
      //       >  .index
      //       >< .url2w
      //       >< .move
      //        < .mode
      //        < .shift
      //       .facet()
      //       .fetch()
      //       .fit()
      //    >< .o.URL2wiki()
      //       .set()
      //       .getMode()
      //       .getRemoveTo()
      //       .getTextReplace()
      //       .getTarget()
      //       .getTitle()
      //       .getURL()
      //    >  .mod.wikilink
      //    >  .o.WikiTom.LinkWiki
      //    .errors.found()
      // 2013-12-14 PerfektesChaos@de.wikipedia
      var t, u2w, x;
      if (! this.url2w) {
         this.url2w  =  new WSTM.o.URL2wiki();
      }
      u2w  =  this.url2w;
      this.url2w.set({ multiple: this.multiple,
                       scheme:   this.facet(WebLK.joinScheme),
                       site:     this.facet(WebLK.joinSite),
                       slashed:  this.facet(WebLK.joinSelect),
                       show:     this.facet(WebLK.joinShow),
                       suffix:   this.facet(WebLK.joinSuffix)
                     },
                     false);
      switch (this.url2w.getMode()) {
         case 1 :
            this.fetch(this.url2w.getTextReplace(), true);
            break;
         case 2 :
            if (this.limited) {
               this.mode  =  WSTM.o.WikiTom.LinkWiki;
               this.fetch(false, true);
               x  =  this.url2w.getTarget();
               this.fit(WebLK.joinSelect, x, true);
               t  =  u2w.getTitle();
               if (t  &&  t !== x) {
                  this.fit(WebLK.joinShow,  t + "]",  true);
                  x  =  this.url2w.getSuffix();
                  if (x) {
                     this.fit(WebLK.joinSuffix, x, true);
                  }
                  this.shift  =  "|";
               } else if (t === "") {
                  this.shift  =  true;
               } else {
                  this.shift  =  "]";
               }
               if (this.shift === true) {
                  this.shift  =  "[" + x + "]";
                  this.fit(WebLK.joinShow,   "", true);
                  this.move  =  (this.index ? -1 : 0);
               } else {
                  this.shift  =  "[" + this.facet(WebLK.joinSelect)
                                     + this.shift;
                  this.move   =  (this.index ? -2 : 0);
               }
            } else {
               WSTM.errors.found("wikilinkURL",
                                 false,
                                 u2w.getURL()
                                 + String.fromCharCode(10, 8658, 10)
                                 + "[[" + u2w.getTarget() + "]]");
            }
            break;
      }   // switch .url2w.get()
   };   // .o.Weblink.friend()



   WebLK.prototype.further  =  function () {
      // Skip over node
      // Postcondition:
      //    Returns  number < 0  iff ']' not found
      // Uses:
      //    >  .o.Weblink::
      //       >  .node
      //       >  .off
      //       >  .maxTitle
      //        < .moving
      //        < .shower
      //       .facet()
      //    >  .o.WikiTom.TextOnly
      //    .o.WikiTom().focus()
      //    .o.WikiTom().fetch()
      //    .errors.found()
      // 2013-03-15 PerfektesChaos@de.wikipedia
      var got, m, s,
          past  =  this.off.focus(this.node + 1),
          r     =  -2;
      if (past) {
         m  =  past.mode;
         if (m > WSTM.o.WikiTom.TextOnly) {
            past         =  this.off.focus(this.node + 2);
            this.moving  =  2;
            if (past) {
               m  =  past.mode;
               if (m > WSTM.o.WikiTom.TextOnly) {
                  this.moving  =  3;
               }
            }
            if (m < WSTM.o.WikiTom.TextOnly) {
               s  =  this.off.fetch(this.node + this.moving,  0,  false);
               r  =  s.indexOf("]");
               if (r >= 0  &&  r < this.maxTitle) {
                  if (r) {
                     this.shower  =  s.substr(0, r);
                     s            =  "\f" + this.shower + "\f";
                     got          =  /\f([^<\n]*)\f/.exec(s);
                     if (got) {
                        if (this.shower.indexOf("[") >= 0) {
                           this.shower  =  false;
                        }
                     } else {
                        this.shower  =  false;
                     }
                  } else {
                     this.shower  =  "";
                  }
               }
            }
         }
      }
      if (this.shower === false) {
         WSTM.errors.found("weblinkBracketRight",
                           false,
                           "["
                           + this.facet(this.joinScheme,
                                        this.joinSelect));
      }
      return r;
   };   // .o.Weblink.further()



};   // .bb.Weblink()
mw.libs.WikiSyntaxTextMod.bb.Weblink(mw.libs.WikiSyntaxTextMod);
delete mw.libs.WikiSyntaxTextMod.bb.Weblink;



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



mw.libs.WikiSyntaxTextMod.bb.Wikilink  =  function (WSTM) {
   // Administration of wikilinks
   // 2012-10-01 PerfektesChaos@de.wikipedia
   // Class:
   //    Public:
   //       getChange()
   //       getError()
   //       getIncrement()
   //       getRemoveFrom()
   //       getRemoveTo()
   //       getSortkey()
   //       getTargetLength()
   //       getTextReplace()
   //       getType()
   //       getUserModified()
   //       set()
   //       setTarget()
   //    Private:
   //       adjust()
   //       analyze()
   //       capitalize()
   //       category()
   //       closingBracket()
   //       extend()
   //       file()
   //       language()
   //       lineFeedAhead()
   //       magic()
   //       magic_ISBN()
   //       namespace()
   //       newline()
   //       old2New()
   //       pipeSymbols()
   //       project()
   //       specials()  //  -> special()
   //       target()
   //       title()
   //       unlink()
   //       url()
   //       init()
   // .label    true: pipe trick completion
   // .lapsus   true: syntax error detected
   // .last     true: pipe at end of parameter list
   // .lead     true: target starts with ":"
   // .leader   true: first item in category or interlanguage sequence
   // .leap     true: leading whitespace
   // .learn    true: link modified by user replacement
   // .lfAhead  true: line feed present before link
   // .lift     true: left brackets shifted
   // .limited  true: enclosed in brackets (single bracket termination)
   // .lack     true: single  "[" heading
   // .lock     true: freeze wikilink targets
   // .index    number: position of "[[" in .source
   // .inPipe   number: of chars in analyze until pipe symbol, or false
   // .inTerm   number: of chars in analyze until terminating bracket
   // .join     number: relative position of "[["
   // .justify  number: default lang for project link
   // .keySort  number: of chars to be unconditionally locked as sort key
   // .mode     number: interwiki, category, media (file) etc.
   // .move     number: incrementation on global search
   // .newFrom  number: of chars in .source for replacement region start
   // .newTo    number: of chars in .source for replacement region end
   // .next     number: of chars in .suffix to be added, or all
   // .node     number: of .onto within .off
   // .nucleus  number: of target chars to be locked
   // .off      WikiTom with <includeonly> spans, or false
   // .onto     current WikiTom node
   // .score    wikilink target
   // .sequence media parameters
   // .sister   'project:', if any
   // .shift    replacement string (total)
   // .show     non-empty title string
   // .slang    'language:', if any
   // .source   wikitext including "[[" at .index
   // .special  category or interwiki content    // -> schedule
   // .start    replacement string (preceeding)
   // .subcase  downcased wgTitle
   // .suffix   aftermath
   "use strict";
   var WikiLK;



   WSTM.o.Wikilink  =  function () {
      // .constructor for new
      // Postcondition:
      //    Returns .o.Wikilink object
      // Uses:
      //    >  .g.wTitle
      //     < .o.Wikilink.subcase
      //    .o.Wikilink.init()
      //    .hooks.fire()
      //    .str.deCapitalize()
      // 2016-08-17 PerfektesChaos@de.wikipedia
      var p;
      this.init();
      this.index    =  false;
      this.lock     =  false;
      this.move     =  false;
      this.newFrom  =  false;
      this.node     =  false;
      this.off      =  false;
      this.onto     =  false;
      this.source   =  false;
   // this.lfAhead  =  false;
      if (WSTM.hooks.fire("capitalize1")) {
         this.subcase  =  WSTM.str.deCapitalize(WSTM.g.wTitle);
      } else {
         this.subcase  =  WSTM.g.wTitle;
      }
      for (p in WSTM.o.Wikilink) {
         this[ p ]  =  WSTM.o.Wikilink[ p ];
      }   // for p in
      return this;
   };   // .o.Wikilink() .constructor
   WikiLK  =  WSTM.o.Wikilink;



   // Definition of constants
   // 2012-10-24 PerfektesChaos@de.wikipedia
   WikiLK.ModeNull  =  -9;
   WikiLK.ModeWeb   =  -7;
   WikiLK.ModeWiki  =  91;
   WikiLK.ModeIw    =  93;
   WikiLK.ModeFile  =   6;
   WikiLK.ModeCat   =  14;
   WikiLK.ModeMap   =  -5;  //   special weblink ([[DOI:...]] etc.)
   //                   6   special interwikilink ([[bugzilla:...]] etc.)
   //                   7   page



   WikiLK.prototype.init  =  function () {
      // Initialize existing wikilink object
      // Postcondition:
      //    .o.Wikilink object has been reset
      // Uses:
      //    >  .o.Wikilink::
      //       >  .***
      // 2015-12-20 PerfektesChaos@de.wikipedia
      this.label     =  false;
      this.lapsus    =  false;
      this.last      =  false;
      this.lead      =  false;
      this.leader    =  false;
      this.leap      =  false;
      this.learn     =  false;
      this.lift      =  false;
      this.limited   =  false;
      this.lock      =  false;
      this.inPipe    =  false;
      this.inTerm    =  false;
      this.join      =  false;
      this.justify   =  false;
      this.keySort   =  false;
      this.mode      =  false;
      this.move      =  false;
      this.newFrom   =  false;
      this.newTo     =  false;
      this.next      =  false;
      this.node      =  false;
      this.nucleus   =  false;
      this.off       =  false;
      this.onto      =  false;
      this.score     =  false;
      this.sequence  =  false;
      this.shift     =  false;
      this.show      =  false;
      this.sister    =  false;
      this.slang     =  false;
      this.special   =  false;
      this.start     =  false;
      this.suffix    =  false;
   };   // .o.Wikilink.init()



   WikiLK.prototype.adjust  =  function (analyze) {
      // Analyze entire link syntax, divide into major parts
      // Precondition:
      //    analyze  -- string with link content beginning with '[['
      // Uses:
      //    >  .w.link.namespace.nsFile
      //    >  .o.Wikilink::
      //       >  .ModeFile
      //       >  .ModeMap
      //       >  .show
      //        < .lack
      //        < .mode
      //        < .limited
      //        < .join
      //        < .inTerm
      //        < .inPipe
      //        < .lapsus
      //        < .move
      //        < .leap
      //        < .suffix
      //        < .score
      //        < .lift
      //       .file()
      //       .extend()
      //       .newline()
      //       .url()
      //       .closingBracket()
      //       .pipeSymbols()
      //       .analyze()
      //     < .mod.lazy
      //    .w.link.namespace.furnish()
      //    .w.link.wiki.iwMap()
      //    .str.trim()
      //    .errors.found()
      //    .str.isBlank()
      // Requires: JavaScript 1.3   charCodeAt()
      // 2016-02-05 PerfektesChaos@de.wikipedia
      var s       =  analyze.substr(2),
          inside  =  2,
          join    =  s.indexOf("\n\n"),
          lack    =  (analyze.substr(0, 2)  !==  "[["),
          later   =  false,
          c,
          e,
          newl,
          mid,
          shift,
          start,
          swap;
      if (join > 0) {
         s  =  s.substr(0, join);
      }   // paragraph found
      join  =  s.indexOf(":");
      if (join > 1) {
         if (WSTM.w.link.namespace.furnish(s.substr(0, join),
                                           false,
                                           false)  ===
             WSTM.w.link.namespace.nsFile) {
            this.mode  =  this.ModeFile;   // (not Media:)
            if (this.file(analyze.substr(2),  join + 1)) {
               s  =  "";
            }
            join  =  s.indexOf("\n");
            if (join > 0) {
               s  =  s.substr(0, join);
            }   // EOL -- image description might contain strange things
         } else {
            start  =  s.substr(0, join);
            if ( start.indexOf("[[") < 0 ) {
               swap  =  WSTM.w.link.wiki.iwMap(start);
               if (swap) {
                  this.mode  =  this.ModeMap;
                  later      =  (swap !== start);
                  if (later) {
                     start  =  swap;
                     s      =  start + s.substr(join);
                  }   // adjust
                  swap  =  s.substr(join + 1);
                  mid   =  swap.indexOf("]]");
                  if (mid > 0) {
                     swap   =  swap.substr(0, mid);
                     shift  =  WSTM.str.trim(swap, true);
                     if (swap !== shift) {
                        later  =  swap.length - shift.length;
                        s      =  start  +  ":"  +  shift
                                  +  s.substr(start.length + 1 + mid);
                     }
                     if (typeof(WSTM.util[ start ]) === "object") {
                        e  =  WSTM.util[ start ];
                        if (typeof(e.failure) === "function") {
                           swap  =  s.substr(start.length + 1);
                           swap  =  swap.substr(0,  swap.indexOf("]]"));
                           e     =  e.failure(swap);
                           switch (typeof e) {
                              case "string" :   // simple correction
                                 mid  =  start.length + 1 + swap.length;
                                 s    =  WSTM.str.makeString(32,
                                                  swap.length - e.length)
                                         + start + ":" + e
                                         + s.substr(mid);
                                 break;
                              case "object" :   // severe
                                 WSTM.errors.found("badURI", true, e[1]);
                                 break;
                           }   // switch typeof e
                        }
                     }
                     if (typeof(later) === "number") {
                        s  =  WSTM.str.makeString(32, later)  +  s;
                     }
                  }
               }   // interwiki mapping
            }   // interwiki mapping
         }
      }   // may be prefixed
      join          =  s.indexOf("]") + 2;
      this.limited  =  (join > 1);
      if (this.limited) {   // first terminating bracket found
         inside  =  s.indexOf("[") + 2;
         if (inside > 1) {
            if (inside < join) {
               if (this.mode !== this.ModeFile   &&
                   inside === 2) {   // triple bracket, or even more
                  c           =  s.charCodeAt(0);
                  this.join   =  0;
                  this.newTo  =  join;
                  this.extend(32);
                  while (WSTM.str.isBlank(c, false)
                         ||  c === 91) {   // [
                     s  =  s.substr(1);
                     c  =  s.charCodeAt(0);
                     this.join++;
                     join--;
                  }   // while
                  WSTM.mod.lazy  =  false;
                  WSTM.errors.found("tooManyLeftBrackets",
                                    true,
                                    analyze.substr(0, 100));
               } else {   // got opening bracket inside, could be File:
                  join          =  inside;
                  s             =  s.substr(0,  join - 2);
                  this.limited  =  false;
               }
            }
         }
         this.inTerm  =  join;
      }
      if (s) {
         newl  =  s.indexOf("\n") + 2;
         mid   =  s.indexOf("|") + 2;
         if (! this.limited) {
            if (this.mode === this.ModeFile) {
               join  =  s.length;   // "]]"
            } else {
               this.extend(-1);
               if (newl > 1) {
                  s  =  s.substr(0,  newl - 1);
               }
               if (mid > 1) {
                  s  =  s.substr(0, mid);
               }
               if (s.length  &&  mid < 1) {   // not folded
                  WSTM.errors.found("wikilinkBracketsAhead",
                                    false,
                                    mid +
                                    "[[" + s.substr(0, 100));
               }
               s  =  false;
            }
         }
      }
      if (s) {
         if (mid > 1) {
            if (mid < join) {
               this.inPipe  =  mid;
            }
         }
         if (newl > 1) {
            if (this.limited) {
               if (newl < this.inTerm) {
                  s     =  this.newline(s, newl);
                  newl  =  false;
               }   // newline within bracket pair
            }
            if (newl) {
               s  =  s.substr(0,  newl - 2);
               if (mid > newl) {
                  this.inPipe  =  false;
               }
            }
         }
      }
      if (s) {
         while (WSTM.str.isBlank(s.charCodeAt(0), false)) {
            s  =  s.substr(1);
            join--;
            if (this.inPipe) {
               mid--;
            }
            this.extend(1);
            this.leap  =  true;
         }   // while ltrim
         if (join === 2) {   // "[[\n"
            WSTM.errors.found("linkTargetMissing",
                              true,
                              analyze.substr(0, 100));
            this.extend(-1);
            s  =  false;
         } else if (s.charCodeAt(0) === 93) {   // ']'   // [[]
            this.extend(-1);
            s  =  false;
         } else if (this.limited) {   // at least single brackets
            if (s.length >= join) {
               this.suffix  =  s.substr(join - 1);
               s            =  s.substr(0,  join - 2);
            }
            if (this.url(s)) {
               s  =  false;
            }
         }   // not empty
      }
      if (s) {   // neither URL nor empty
         if (later) {
            this.extend(1);
         }
         this.closingBracket(s);
         if (this.score) {
            if (this.inPipe) {
               this.pipeSymbols(mid - 2);
               if (lack || this.lack) {
                  s  =  analyze.substr(0, 1);
                  if (this.start) {
                     this.start  =  this.start + s;
                  } else {
                     this.start  =  s;
                  }
                  this.extend(32, 0);
                  this.join++;
                  this.lift  =  true;
               }
            }
            if (this.score || this.show) {
               this.analyze();
            } else if (this.score) {   // ??? impossible     ! this.score
               this.extend(-1);
            }
         }   // local
      }
   };   // .o.Wikilink.adjust()



   WikiLK.prototype.analyze  =  function () {
      // Analyze bracket content fractions and aftermath
      // Precondition:
      //    Fractions .score, .show, .suffix are defined.
      // Uses:
      //    >  .o.Wikilink::
      //       >  .ModeMap
      //       >  .mode
      //       >  .lead
      //       >  .inPipe
      //       >  .newFrom
      //       >  .newTo
      //       >  .start
      //       >  .index
      //       >  .ModeWiki
      //       >  .lack
      //       >  .next
      //       >< .score
      //       >< .show
      //       >< .suffix
      //       >< .nucleus
      //       >< .leap
      //        < .label
      //        < .lock
      //        < .shift
      //        < .move
      //       .target()
      //       .title()
      //       .unlink()
      //       .context()
      //       .magic()
      //       .extend()
      //       .lineFeedAhead()
      //    .str.substrEnd()
      //    .w.link.wiki.fore()
      //    .str.isLetter()
      // Requires: JavaScript 1.3   charCodeAt()
      // 2013-03-06 PerfektesChaos@de.wikipedia
      var c,
          linking,
          n,
          s;
      if (this.score) {
         if (this.mode !== this.ModeMap) {   // not Interwiki mapping
            this.target();
         }
         if (! this.mode) {   // not Category:
            if (! this.lead) {
               this.unlink();
            }
            this.label  =  (this.inPipe  &&  ! this.show);
            if (this.label) {
               this.show  =  this.context();
               this.extend(2);
            }   // context style   [[title (Abc)|]]
         }
      }
      if (! this.mode) {   // magic?
         this.magic();
         if (this.show) {
            this.title();
         }
      }
      if (this.lock) {
         if (this.newFrom === 2  &&  ! this.nucleus) {
            n  =  this.score.length;
            if (n > 0) {
               this.nucleus  =  n;
            }
         }
      }
      if (this.leap) {
         if (this.start) {
            c  =  WSTM.str.substrEnd(this.start, 1);
         } else if (this.index > 0) {
            c  =  this.source.substr(this.index - 1,  1);
         } else {
            c  =  false;
            n  =  10;
         }
         if (c) {
            n  =  c.charCodeAt(0);
         }
         if (n === 10  ||  n === 32) {
            this.leap  =  false;
         } else {
            this.extend(32);
            this.move++;
         }
      }
      if (! this.mode) {
         if (this.show) {   // titled
            if (WSTM.str.isLetter(WSTM.str.substrEnd(this.show, 1))) {
               // c'td?
               n  =  WSTM.w.link.wiki.fore(this.suffix, 0);
               if (n) {
                  this.show  =  this.show  +  this.suffix.substr(0, n);
                  this.extend(16, n);
                  if (n < this.suffix.length) {
// TODO  2013-03-06  (n > this.suffix.length)     IMPOSSIBLE. Bug!
                     this.suffix  =  this.suffix.substr(n);
                  } else {
                     this.suffix  =  false;
                  }
               }
            }   // isLetter
            if (typeof(this.newTo) === "number") {
               if (this.newTo >= this.inTerm) {
                  if (typeof(this.score) === "string") {
                     this.score  =  this.score + "|" + this.show;
                     this.show   =  false;
                  }
               }
            }
         }   // titled
         if (this.newFrom === 2) {
            this.mode  =  this.ModeWiki;
         }
      }
      this.lineFeedAhead();
      if (typeof(this.newTo) === "number") {
         this.join  =  (typeof(this.join) === "number"  ?  this.join
                                                        :  0);
         if (this.newTo > this.join) {
            if (typeof(this.shift) === "boolean") {
               this.shift  =  (this.score ? this.score : "");
            }
         }
         if (this.newFrom <= 0) {
            linking  =  (typeof(this.score) === "string")
                        &&  this.newTo > this.join;
            if (linking) {
               if (typeof(this.start) === "string") {
                  if (WSTM.str.substrEnd(this.start, 2) === "[[") {
                     linking  =  this.leap;
                  }
               }
            }
            this.shift  =  (this.leap ? " " : "")
                           +  (linking ? "[[" : "")
                           +  (this.shift ? this.shift : "");
         }
         if (this.start  &&  this.newFrom <= 0) {
            this.shift  =  this.start  +  (this.shift ? this.shift : "");
         }
         n  =  this.join  +  this.inTerm  +  (this.lack ? 1 : 2);
         if (this.newTo  >=  n) {
            if (this.score  &&  this.mode !== this.ModeFile) {
               this.shift  =  (this.shift ? this.shift : "")  +  "]]";
            }
            if (this.suffix && this.next) {
               s  =  this.suffix;
               if (typeof(this.next) === "number") {
                  s  =  s.substr(0, this.next);
               }
               this.shift  =  (this.shift ? this.shift : "")  +  s;
            }   // suffix
         }   // newTo
      }
      if (this.score) {
         n  =  this.score.length;
         if (n > this.move) {
            this.move  =  n;
         }
      }
   };   // .o.Wikilink.analyze()



   WikiLK.prototype.capitalize  =  function () {
      // Capitalize wikilink if appropriate
      // Precondition:
      //    address  -- string with link target
      // Uses:
      //    >  .o.Wikilink::
      //       >  .sister
      //       >  .show
      //       >< .score
      //       .extend()
      //    .hooks.fire()
      // 2012-09-27 PerfektesChaos@de.wikipedia
      var low2up,
          s,
          start   =  this.score.substr(0, 1),
          swap    =  start.toUpperCase();
      if (start !== swap) {   // 1st letter downcased
         low2up  =  true;
         if (this.sister) {
            s       =  this.sister.substr(0, 5) + ":";
            low2up  =  (s.substr(0, 5)  !==  "wikt:");
         }   // no upcasing in Wiktionary
         if (low2up) {
            low2up  =  WSTM.hooks.fire("wikilink_lower1",
                                       [ this.score, this.show ]);
         }   // upcasing appropriate
         if (low2up) {
            this.score  =  swap + this.score.substr(1);
            this.extend(1);
         }   // upcase
      }   // 1st letter downcased
   };   // .o.Wikilink.capitalize()



   WikiLK.prototype.category  =  function () {
      // Handle category of page
      // Uses:
      //    >  .w.encountered.DEFAULTSORT
      //    >  .g.wTitle
      //    >  .g.wNsNumber
      //    >  .o.WikiTom.LinkCategory
      //    >  .o.Wikilink::
      //       >  .sister
      //       >  .label
      //       >  .ModeCat
      //       >< .score
      //       >< .show
      //       >< .inPipe
      //        < .lock
      //        < .keySort
      //        < .mode
      //        < .special  //  -> schedule
      //        < .leader
      //       .extend()
      //    .w.elem.sortkey()
      //    .hooks()
      //    .w.link.wiki.further()
      // 2016-01-23 PerfektesChaos@de.wikipedia
      var s  =  this.score.substr(0, 1),
          leave;
      if (s.toUpperCase() !== s) {
         this.score  =  s.toUpperCase() + this.score.substr(1);
         this.extend(1);
      }
      if (! this.sister) {   // [[Category:  ]]
         if (this.inPipe) {   // [[Category:  |   ]]
            if (this.show) {
               s  =  WSTM.w.elem.sortkey(this.show);
               if (typeof(s) === "string") {
                  if (! s.length) {
                     this.show  =  false;
                  } else {
                     this.show  =  s;
                  }
                  this.extend(2);
               }
               if (this.label) {
                  this.show  =  false;
               } else {
                  if (WSTM.w.encountered.DEFAULTSORT) {
                     if (WSTM.hooks.fire("sortkey_ignorecase")) {
                        leave  =  (this.show.toUpperCase() ===
                                   WSTM.w.encountered.DEFAULTSORT
                                       .toUpperCase());
                     } else {
                        leave  =  (this.show ===
                                   WSTM.w.encountered.DEFAULTSORT);
                     }
                  } else if (WSTM.g.wNsNumber === 0) {
                     if (WSTM.hooks.fire("sortkey_ignorecase")) {
                        leave  =  (this.show.toUpperCase() ===
                                   WSTM.g.wTitle.toUpperCase());
                     } else {
                        leave  =  (this.show === WSTM.g.wTitle);
                     }
                  }
                  if (leave) {
                     this.show  =  false;
                     this.extend(2);
                  }
               }   // not possibly erroneous "|]]"
            }   // non-empty sortkey
            if (this.show) {   // non-empty sortkey
               if (WSTM.str.substrEnd(this.show, 1)  ===  "|") {
                  this.show  =  this.show.substr(0,
                                                 this.show.length - 1);
               }
               if (this.lock) {
                  this.keySort  =  this.show.length;
               }
               this.score  =  this.score + "|" + this.show;
               this.show   =  false;
               this.extend(2);
               this.inPipe  =  false;
            } else {   // empty/emptied sortkey
               this.extend(1);
            }   // sortkey
         }   // pipe symbol
         this.mode     =  this.ModeCat;
         this.special  =  this.score;
         this.leader   =  WSTM.w.link.wiki.further(
                                   { mode:   WSTM.o.WikiTom.LinkCategory,
                                     source: this.score } );
         if ( ! this.lead
              &&   typeof WSTM.lang.write === "object" ) {
            this.lead  =  WSTM.lang.write.lead;
         }
      }   // ! sister
   };   // .o.Wikilink.category()



   WikiLK.prototype.closingBracket  =  function (adjust) {
      // Check or complete link content for second closing bracket
      // Precondition:
      //    adjust  -- string with link inner content
      //    Neither URL nor empty
      // Postcondition:
      //    this.score defined if appropriate
      // Uses:
      //    >  .o.Wikilink::
      //       >  .ModeFile
      //       >< .suffix
      //       >< .mode
      //       >< .lapsus
      //        < .score
      //       .extend()
      //    .errors.found()
      // Requires: JavaScript 1.3   charCodeAt()
      // 2013-01-22 PerfektesChaos@de.wikipedia
      var less  =  true,
          n;
      if (this.suffix) {
         if (this.suffix.charCodeAt(0) === 93) {   // ']'
            this.score  =  adjust;
            if (this.suffix.length < 2) {
               this.suffix  =  false;
            } else {
               this.suffix  =  this.suffix.substr(1);
               if (this.suffix.charCodeAt(0) === 93
                   &&false) {   // ']'   ////////////////////////////////
                  n            =  this.suffix.length;
                  this.lapsus  =  true;
                  this.suffix  =  this.suffix.substr(1);
                  this.extend(16, 3);
                  if (this.suffix.charCodeAt(0) === 93) {   // ']'
                     this.suffix  =  this.suffix.substr(1);
                     this.extend(16, 4);
                  }
                  WSTM.errors.found("tooManyRightBrackets",
                                    true,
                                    "[[" + this.score + "]]]");
               }
            }
            less  =  false;
         }
      }
      if (less) {
         if (this.mode === this.ModeFile) {
            this.score  =  adjust;
         } else {
            WSTM.errors.found("secondClosingBracket", true, adjust);
            if (this.lapsus) {
               this.extend(-1);
            } else {   // append ']'
               this.score  =  adjust + "]";
               this.extend(9);
               this.lapsus  =  true;
            }
         }
      }
   };   // .o.Wikilink.closingBracket()



   WikiLK.prototype.context  =  function () {
      // Retrieve context title for [[fool:bar (Abc)|]]
      // Postcondition:
      //    Returns link title
      //    RegExp was used.
      // Uses:
      //    >  .o.Wikilink::
      //       >  .score
      //       >  .sister
      //       >< .rePipeTrick
      //       >< .reSpace
      //    >  .g.wNsIds
      //    .g.fetch()
      //    .str.trimL()
      // Requires: JavaScript 1.3   fromCharCode()
      // Info: "Pipe trick"
      //       * Strip off and use as title
      //       ** anything looking like a namespace -- [[CSI: Miami|]]
      //       ** bracketed suffix.
      //       ** comma ',' does not matter.
      // 2013-03-08 PerfektesChaos@de.wikipedia
      var got,
          r,
          sB   =  "(" + String.fromCharCode(65288),
          sE   =  ")" + String.fromCharCode(65289),
                        // FF08 FF09  FULLWIDTH PARENTHESIS
          re   =  sB + sE;
      re   =  "([^" + re + "]*[^ " + re + "]) *"
              + "[" + sB + "].+[" + sE + "]";
      re   =  new RegExp(re, "");
      if (! this.rePipeTrick) {
         sB                =  "(" + String.fromCharCode(65288);
         sE                =  ")" + String.fromCharCode(65289);
                                    // FF08 FF09  FULLWIDTH PARENTHESIS
         this.rePipeTrick  =  sB + sE;
         this.rePipeTrick  =     "([^" + this.rePipeTrick + "]*"
                              +   "[^ " + this.rePipeTrick + "]) *"
                              +  "([" + sB + "].+[" + sE + "])$";
         this.rePipeTrick  =  new RegExp(this.rePipeTrick, "");
      }
      got  =  this.rePipeTrick.exec(this.score);
      r    =  (got ? got[1] : this.score);
      got  =  r.indexOf(":", 1);
      if (got > 0) {
         if (this.sister) {
            r    =  r.substr(this.sister.length);
            got  =  r.indexOf(":");
         }
      }
      if (got) {
         if (WSTM.g.fetch(WSTM.g.wNsIds, "wgNamespaceIds")) {
            if (! this.reSpace) {
               this.reSpace  =  new RegExp(" +", "g");
            }
            sB  =  r.substr(0, got).toLowerCase();
            sB  =  sB.replace(this.reSpace, "_");
            if (! WSTM.g.wNsIds[ sB ]) {
               got  =  0;
            }
         }
         if (got) {
            r  =  WSTM.str.trimL(r.substr(got + 1),  true,  false);
         }
      }
      return  r;
   };   // .o.Wikilink.context()



   WikiLK.prototype.extend  =  function (assign, around) {
      // Extend replacement region
      // Precondition:
      //    assign  -- kind of extension
      //               -1  -- no extension
      //                1  -- target region (beginning at 2)
      //                2  -- title region
      //                3  -- end of both target and title
      //                4  -- begin at 0, not at 2
      //                8  -- end after first terminating bracket
      //                9  -- second terminating bracket appended
      //               16  -- append afterward characters
      //               32  -- begin and terminate at least at 0
      //    around  -- position extending outside brackets
      //               assign=16   after second bracket
      //               assign=32   before first bracket
      // Uses:
      //    >  .o.Wikilink::
      //       >  .inPipe
      //       >  .inTerm
      //       >< .newFrom
      //       >< .newTo
      // 2013-01-22 PerfektesChaos@de.wikipedia
      var n;
      switch (assign) {
         case -1 :
            this.newFrom  =  2;
            this.newTo    =  false;
            break;
         case  1 :
         case  2 :
         case  3 :
         case  8 :
         case  9 :
         case 16 :
            switch (assign) {
               case  1 :
                  n  =  (this.inPipe ? this.inPipe : this.inTerm);
                  break;
               case  2 :
               case  3 :
                  n  =  this.inTerm;
                  break;
               case  8 :
                  n  =  this.inTerm + 1;
                  break;
               case  9 :
                  n  =  this.inTerm;
                  break;
               case 16 :
                  n  =  this.inTerm + 2;
                  if (around) {
                     n  +=  around;
                  }
                  break;
            }   // switch assign
            if (typeof(this.newTo) === "number") {
               if (n > this.newTo) {
                  this.newTo  =  n;
               }
            } else {
               this.newTo  =  n;
            }
            break;
         case  4 :
            if (this.newFrom > 0) {
               this.newFrom  =  0;
            }
            break;
         case 32 :
            if (this.newFrom > 0) {
               this.newFrom  =  0;
            }
            if (around) {
               this.newFrom  -=  around;
            }
            if (typeof(this.newTo) !== "number") {
               this.newTo  =  0;
            }
            break;
      }   // switch assign
   };   // .o.Wikilink.extend()



   WikiLK.prototype.file  =  function (analyze, ahead) {
      // Analyze Media embedding in File: namespace
      // Precondition:
      //    analyze  -- string with link content beginning after '[['
      //    ahead    -- position after ':' of namespace
      // Postcondition:
      //    Returns found object, or false
      // Uses:
      //    >  .o.Wikilink::
      //       >  .off
      //       >  .index
      //       >  .node
      //       >  .ModeFile
      //        < .mode
      //    .o.WikiTom().find()
      // 2015-11-02 PerfektesChaos@de.wikipedia
      var r  =  this.off.find("]]",
                              this.index + 2,
                              this.node,
                              true,
                              false,
                              false);
      if (r) {
         if (r.k === this.node) {
            this.filer(analyze, ahead, r.i, true);
         } else {
            this.filer(analyze,
                       ahead,
                       analyze.length + 1,
                       false);
         }
      }
      return r;
   };   // .o.Wikilink.file()



   WikiLK.prototype.filer  =  function (analyze, ahead, after, all) {
      // Analyze leading section of Media embedding in File: namespace
      // Precondition:
      //    analyze  -- string with embedding start beginning after '[['
      //                truncated if links, templates, comments inside
      //    ahead    -- position after ':' of namespace
      //    after    -- position of termination in analyze (before ']]')
      //    all      -- full caption seems to be present
      // Uses:
      //    >  .o.Wikilink::
      //       >  .index
      //       >  .node
      //       >  .ModeFile
      //       >< .newTo
      //        < .limited
      //        < .mode
      //        < .inTerm
      //        < .inPipe
      //        < .score
      //        < .last
      //        < .sequence
      //       .filing()
      //       .str.trim()
      //       .target()
      //       .extend()
      //    >  .w.link.namespace.nsFile
      //    .str.trim()
      //    .w.link.namespace.fetch()
      //    .mod.wikilink()
      //    .w.img.format()
      //    .str.substrEnd()
      // 2017-09-13 PerfektesChaos@de.wikipedia
      var inside  =  false,
          leap    =  false,
          loose   =  false,
          s       =  analyze.substr(ahead),
          slice   =  false,
          join    =  s.indexOf("|"),
          n;
      this.limited  =  true;
      this.mode     =  this.ModeFile;   // (not Media: nor :File:)
      this.inTerm   =  (all  ?  after - this.index - 2  :  after);
      if (join > 2) {
         if (s.substr(0,join).indexOf("]]") < 0) {
            if ((/\|\s*$/.test(s))   &&   s.indexOf("|") < 0) {
               this.last  =  true;
            }
            this.inPipe  =  join;
         } else {
            join         =  -2;
            this.inPipe  =  false;
            n            =  s.indexOf("\n{|");
            if (n >= 0) {
               s  =  s.substr(0, n);
            }
         }
      }
      if (join > 2) {
         if (join + ahead  <  after) {
            this.inPipe  =  join + ahead;
            this.score   =  s.substr(0, join);
            slice        =  s.substr(join + 1);
            n            =  slice.indexOf("\n{|");   // /\n:*{|/
            if (n >= 0) {
               slice  =  slice.substr(0, n);
            }
            inside  =  slice.indexOf("[[");
            loose   =  (slice.indexOf("]]") < 0  &&  /\s$/.test(slice));
            slice  =  slice.substr(0,  this.inTerm - this.inPipe - 1);
            if (inside < 0  &&  n >= 0) {
               inside  =  n;
            }
            if (inside >= 0   &&
                ahead + join + inside  <=  this.inTerm) {
               slice  =  this.filing(ahead + join + 3);
               if (slice) {
                  this.inTerm  =  this.inPipe + slice.length + 2;
                  if (/\s$/.test(slice)) {
                     loose  =  true;
                  }
                  leap  =  true;
               } else {
                  this.inPipe  =  false;
               }
            } else if (all) {
               this.last  =  true;
            }
         }
      }
      if ( ! this.inPipe) {
         this.score  =  s.substr(0,  this.inTerm - ahead);
      }
      this.target();// TODO Ensure capitalization  CamelCase  Media title
      n           =  this.score.length;
      this.score  =  WSTM.str.trim(this.score);
      if (n !== this.score.length) {
         n  =  this.score.length - n;
         if ( this.inPipe) {
            this.inPipe  +=  n;
         }
         this.inTerm  +=  n;
      }
      this.score  =  WSTM.w.link.namespace.fetch(
                                            WSTM.w.link.namespace.nsFile,
                                            false,
                                            false)
                     + ":" +  this.score;
      if (WSTM.mod.wikilink) {// TODO
      // WSTM.w.link.replace.flip(WSTM.mod.wikilink,
      //                          this.score,
      //                          false,
      //                          false,
      //                          "File:");
      }
      n  =  this.score.length;
      if (this.score  !==  analyze.substr(0, n)) {
         this.shift  =  this.score;
      }
      if (this.inPipe) {
         this.sequence  =  slice;
         slice  =  WSTM.w.img.format(this);
         if (typeof(slice) === "string") {
            if ( ! this.shift) {
               this.shift  =  this.score;
            }
            if (slice.length) {
               this.shift   =  this.shift + "|" + slice
                               +  (leap ? "|" : "");
               this.newTo  +=  ahead  -  (leap ? 0 : 1);
            } else if (this.last) {
               this.shift  =  this.shift + "|";
            }
            if (loose) {
               n  =  s.length + this.inTerm - this.inPipe + 1;
               if (n > this.newTo) {
                  this.newTo  =  n;
               }
               this.shift  =  this.shift
                              + s.substr(this.inTerm - ahead)
                              + " ";
            }
            this.inPipe  =  false;
         }
      } else {
         if (this.shift) {
            this.newTo  =  this.inTerm + 2;
            loose       =  true;
         }
         this.inTerm  =  n;
         this.move    =  n;
      }
      if (this.shift) {
         if (loose) {
            this.extend(3);
         } else {
            this.extend(1);
            this.newTo  +=  2;
         }
      }
   };   // .o.Wikilink.filer()



   WikiLK.prototype.filing  =  function (after) {
      // Shift Media title with links inside to the end of transclusion
      // Precondition:
      //    after  -- position of pipe in transclusion
      // Postcondition:
      //    Returns string with parameters, or false if nothing to do
      // Uses:
      //    >  .o.Wikilink::
      //       >  .source
      //       >  .off
      //       >  .index
      //       >  .node
      //        < .inPipe
      //     < .o.WikiTom().learnt
      //    .o.WikiTom().find()
      //    .o.WikiTom().flip()
      // 2015-10-12 PerfektesChaos@de.wikipedia
      var gotE   =  false,
          i      =  this.index + after,
          j      =  i,
          k      =  this.node,
          n      =  0,
          loop   =  true,
          r      =  false,
          tom    =  this.off,
      //  table  =  tom.find([/\n:*{|/, 0],
      //                     i, k, true, false, false);
          table  =  tom.find("\n{|", i, k, true, false, false),
          gotB,
          m,
          s;
//if (table) {
//mw.log(WSTM.debugging,".o.Wikilink.filing() table",0,table);
//}
      do {
         gotB  =  tom.find("[[", j, k, true, false, false);
         gotE  =  tom.find("]]", j, k, true, false, false);
         if (table) {
            if (table.k < gotE.k  ||
                (table.k === gotE.k  &&  table.i < gotE.i)) {
               gotB  =  false;
            }
         }
         loop  =  (gotB && gotE);
         if (loop) {
//mw.log(WSTM.debugging,".o.Wikilink.filing() gotB && gotE",0);
            if (! n) {
               s  =  tom.fetch(this.node);
               if (gotB.k === this.node) {
                  s  =  s.substr(0, gotB.i);
               }
               n  =  s.lastIndexOf("|");
            }
            if (gotB.k === gotE.k) {
               loop  =  (gotB.i < gotE.i);
            } else {
               loop  =  (gotB.k < gotE.k);
            }
            if (loop) {
               j  =  gotE.i + 2;
               k  =  gotE.k;
            } else {
               r  =  s.substr(i,  n - i);
               gotE  =  false;
            }
         }
      } while (loop);   // do
//mw.log(WSTM.debugging,".o.Wikilink.filing() +loop",0);
      if (gotE) {
//mw.log(WSTM.debugging,".o.Wikilink.filing() gotE",0);
         s  =  tom.fetch(gotE.k);
         s  =  s.substr(0, gotE.i);
         m  =  s.lastIndexOf("|");
         if (s.substr(m + 1, 1)  ===  "}") {
            m  =  -1;
         }
         if (m >= j) {
            s  =  s.substr(m);
            j  =  s.indexOf("[[");
            if (j > 0  &&  gotE.k === this.node) {
               n  =  j;
            } else {
               tom.flip(gotE.k, m, s.length, "");
               tom.flip(this.node,  i - 1,  0,  s);
               n  +=  s.length;
            }
         }
         r  =  tom.fetch(this.node).substr(i,  n - i);
      }
      return r;
   };   // .o.Wikilink.filing()



   WikiLK.prototype.language  =  function (ahead) {
      // Analyze wikilink beginning whether it starts with language
      // Check language identification, remove wgContentLanguage
      // Precondition:
      //    ahead     -- position of ':' in this.score (>0)
      //    .justify  -- behaviour if no explicit "lang" identified
      //                 1        adjust with wgContentLanguage
      //                 2        adjust with "en"
      //                 false (else)
      //                          do nothing than trimming and downcasing
      // Postcondition:
      //    Returns true iff language stripped off from this.score
      //    RegExp was used.
      // Uses:
      //    >  .g.projLang
      //    >  .g.projLone
      //    >  .lang.write.linklang
      //    >  .lang.write.lead
      //    >  .o.Wikilink::
      //       >  .mode
      //       >  .ModeFile
      //       >< .justify
      //       >< .score
      //        < .lead
      //        < .slang
      //       .extend()
      //    .str.trimL()
      //    .lang.flop()
      //    .str.trimR()
      // 2014-10-10 PerfektesChaos@de.wikipedia
      var learnt  =  false,
          left    =  false,
          r       =  false,
          s       =  this.score.substr(0, ahead),
          slang   =  false,   // language specification
          story;              // heading part of article title
      s  =  WSTM.str.trimL(s, false);
      if (s.length < ahead) {
         learnt  =  true;
      }   // trimmed
      story  =  WSTM.lang.flop(s);
      if (! story) {
         slang  =  WSTM.str.trimR(s, false).toLowerCase();
         if (slang !== s) {
            learnt  =  true;
         }
      }   // matching lang?
      if (! slang) {
         if (this.justify) {
            switch (this.justify) {
               case  1 :
                  slang   =  false;
                  learnt  =  story;
                  break;
               case  2 :
                  slang   =  "en";
                  learnt  =  true;
                  break;
            }   // switch this.justify
            left  =  true;
         } else if ( typeof WSTM.lang.write === "object" ) {
            if (this.mode !==  this.ModeFile) {
               slang      =  WSTM.lang.write.linklang;
               this.lead  =  WSTM.lang.write.lead;
               learnt     =  true;
            }
         }
      }
      if (slang === WSTM.g.projLang) {
         if (! WSTM.g.projLone) {
            slang   =  false;
            learnt  =  true;
         }
      }
      if (learnt || slang || story) {   // language or similar identified
         this.score  =  this.score.substr(ahead + 1);
         if (story) {
            this.score  =  story + ":" + this.score;
            slang       =  false;
         } else {
            this.score  =  WSTM.str.trimL(this.score, false);
            if (left) {
               this.score  =  s + ":" + this.score;
            }
         }
         if (slang) {
            this.slang  =  slang + ":";
            r           =  true;
         }
         this.extend(1);
         this.justify  =  false;
      } else {
         if (this.justify) {
            if (this.lead) {
               this.lead  =  false;
               this.extend(1);
            }
         }
      }
      return r;
   };   // .o.Wikilink.language()



   WikiLK.prototype.lineFeedAhead  =  function () {
      // Ensure that a category or interlanguage starts on a new line
      // Uses:
      //    >  .o.Wikilink::
      //       >  .mode
      //       >  .ModeIw
      //       >  .ModeWiki
      //       >  .ModeCat
      //       >  .slang
      //       >  .show
      //       >  .source
      //       >  .index
      //       >< .start
      //       >< .move
      //       .extend()
      //    .util.isO_639_1()      .lang.flop()
      //    .errors.found()
      //    .str.isBlank()
      // Requires: JavaScript 1.3   charCodeAt()
      // 2017-01-30 PerfektesChaos@de.wikipedia
      var c, k, s, m;
      if (this.mode === this.ModeIw) {
         if (this.slang.length === 3) {   // like "bd:"
            if (WSTM.lang.flop(this.slang.substr(0, 2))) {
               this.mode  =  this.ModeWiki;   // downgrade   "bd"/"wd"
            }   // not a language code
         }   // A2 language code
         if (this.mode === this.ModeIw) {   // still interlanguage
            if (this.show) {
               WSTM.errors.found("interlanguageTitled",
                                 false,
                                 this.score + "|" + this.show);
               this.show  =  false;
            }   // titled
         }
      }   // interlanguage supposed
      if (this.mode === this.ModeCat  ||  this.mode === this.ModeIw) {
         if (this.start) {
            if (this.start >= 3) {
               s  =  this.start;
               k  =  s.length - 3;
               c  =  s.charCodeAt(k);
            }
         } else {
            if (this.index > 0) {
               s  =  this.source;
               k  =  this.index - 1;
               c  =  s.charCodeAt(k);
            } else {
               c  =  false;
            }
         }
         if (c  &&  c !== 10) {
            m  =  0;
            while (WSTM.str.isBlank(c, true)) {
               m++;
               if (! k) {
                  break;   // while
               }
               k--;
               c  =  s.charCodeAt(k);
            }
            if (c === 10) {
               if (this.start) {
                  this.start  =  s.substr(0,  k - 2);
               } else {
                  this.start  =  "";
               }
               this.extend(32, m);
               this.move  -=  m + 2;
            } else {
// TODO  2016-06
               if (this.start) {
                  k           =  this.start.length - 2;
                  this.start  =  this.start.substr(0, k)  +  "\n";
               } else {
                  this.start  =  "\n";
               }
               this.extend(32);   // 2017-01-30 (32,1) -> (32)
               this.move++;
            }
         }   // no break
      }   // category or interlanguage
   };   // .o.Wikilink.lineFeedAhead()



   WikiLK.prototype.magic  =  function () {
      // Analyze wikilink whether it is actually a magic word
      // Precondition:
      //    Neither File nor Category
      // Uses:
      //    >  .o.Wikilink::
      //       >  .show
      //       >  .score
      //       .magic_ISBN()
      // 2010-10-01 PerfektesChaos@de.wikipedia
      var s  =  (this.show  ?  this.score + "|" + this.show
                            :  this.score);
      if (s.indexOf("ISBN") >= 0) {
         this.magic_ISBN();
      }
      /*
         PMID Nummer   min.6
              Richtig: PMID 4957203
              Falsch:  PMID:4957203
              Falsch:  PubMed 4957203
         RFC ISSN DOI gleichrangig Linkziele schützen
      */
   };   // .o.Wikilink.magic()



   WikiLK.prototype.magic_ISBN  =  function () {
      // Analyze wikilink and context whether it is actually magic ISBN
      // Precondition:
      //    Wikilink contains magic word "ISBN" in some way
      // Uses:
      //    >  .g.re.ISBN
      //    >  .o.Wikilink::
      //       >  .suffix
      //       >  .ModeNull
      //       >< .show
      //       >< .score
      //        < .inPipe
      //        < .mode
      //        < .shift
      //      .extend()
      //    .w.elem.isbn.format()
      //    .str.substrEnd()
      // Requires: JavaScript 1.3   charCodeAt()
      // 2015-07-29 PerfektesChaos@de.wikipedia
      var found  =  false,
          k,
          later,
          n,
          narrow,
          swap;
      if (this.show) {
         narrow  =  this.show.length;
         swap    =  ">"  +  this.show;
         if (narrow > 7) {
            if (narrow >= 14) {
               swap  =  swap  +  this.suffix;
            } else {
               swap  =  false;
            }
         }
         if (swap) {
            swap   =  swap + "\t";
            found  =  WSTM.g.re.ISBN.exec(swap);
         }
         if (swap && found && found.index) {
            found  =  false;
         }
         if (found) {
            k     =  0;
            n     =  swap.length;
            swap  =  WSTM.w.elem.isbn.format(found);
            if (! swap) {
               swap  =  found[0];
            }   // unchanged
            if (WSTM.str.substrEnd(swap, 1)  ===  "\t") {
               swap  =  swap.substr(0,  n - 1);
               if (narrow <= 7) {
                  k--;
               }
            }   // end of source
            this.shift   =  swap.substr(1);
            if (narrow <= 7) {
               k  +=  found[0].length - 5;
            } else {
               k  +=  2;
            }
            this.extend(16, k);
         }   // |ISBN]] + number
      }
      if (!  (found || this.show)) {
         later  =  (this.score === "ISBN");
         if (later) {
            swap  =  "ISBN " + this.suffix;
         } else {
            swap  =  this.score;
         }
         swap   =  ">" + swap + "\t";
         found  =  WSTM.g.re.ISBN.exec(swap);
         if (found && found.index) {
            found  =  false;
         }
         if (found) {
            k     =  (later  ?  found[0].length - 6  :  0);
            swap  =  WSTM.w.elem.isbn.format(found);
            if (! swap) {
               swap  =  found[0];
            }   // unchanged
            n  =  swap.length - 1;
            if (swap.charCodeAt(n) === 9) {   //  \t
               swap  =  swap.substr(0, n);
               if (later) {
                  k--;
               }
            }
            this.extend(16, k);
            this.shift  =  swap.substr(1);
         }   // [[ISBN + number
      }
      if (found) {
         this.extend(4);
         this.show    =  false;
         this.score   =  false;
         this.inPipe  =  false;
         this.mode    =  this.ModeNull;
      }
   };   // .o.Wikilink.magic_ISBN()



   WikiLK.prototype.namespace  =  function (ahead) {
      // Analyze wikilink beginning whether it starts with namespace
      // Precondition:
      //    ahead  -- position of ':' in this.score (>0)
      // Uses:
      //    >  .o.Wikilink::
      //       >  .sister
      //       >  .slang
      //       >  .lead
      //       >  .inPipe
      //       >< .score
      //       >< .show
      //        < .nucleus
      //        < .lock
      //       .extend()
      //       .category()
      //       .specials()
      //       .capitalize()
      //    >  .w.link.namespace.nsCategory
      //    >  .w.link.namespace.nsSpecial
      //    >  .w.link.namespace.nsPage
      //    .w.link.namespace.furnish()
      //    .w.link.namespace.fetch()
      //    .w.link.wiki.decode()
      // 2012-10-01 PerfektesChaos@de.wikipedia
      var space  =  this.score.substr(0, ahead),
          key    =  WSTM.w.link.namespace.furnish(space,
                                                  this.slang,
                                                  this.sister),
          s,
          swap;
      if (key) {   // relevant namespace?
         s           =  (this.sister || this.slang  ?  "en"  :  false);
         this.score  =  this.score.substr(ahead + 1);
         swap        =  WSTM.w.link.namespace.fetch(key, s, this.score);
         if (swap) {
            if (space !== swap) {
               this.extend(1);
            }
            space  =  swap;
         }
         if (this.lead  &&  ! this.sister) {   // :File:  or  :Category:
            this.sister  =  ":";
            this.extend(1);
         }
         s  =  WSTM.w.link.wiki.decode(this.score,
                                       true, false, true, true);
         if (s) {
            this.score  =  s;
            this.extend(1);
         }
         if (this.lock  &&  ! this.sister  &&  this.show) {
            this.nucleus  =  space.length + 1 + this.score.length;
            if (this.nucleus + 2  !==  this.inPipe) {
               this.extend(1);
            }
         }
         switch (key) {
            case WSTM.w.link.namespace.nsCategory :
               this.category();
               break;
            case WSTM.w.link.namespace.nsSpecial :
               this.specials();
               break;
         }   // switch key
         if (this.score) {
            this.capitalize();
            this.score  =  space + ":" + this.score;
         }
      }   // key
   };   // .o.Wikilink.namespace()



   WikiLK.prototype.newline  =  function (analyze, address) {
      // Repair line break within wikilink, if appropriate
      // Precondition:
      //    analyze  -- string with link inner content
      //    address  -- position of '\n' in environment (>1)
      // Postcondition:
      //    Returns repaired analyzed string, or false
      // Uses:
      //    >  .o.Wikilink::
      //       >  .inTerm
      //       >  .inPipe
      //       >  .join
      //       >< .score
      //        < .lapsus
      //       .extend()
      //     < .mod.lazy
      //    .str.setChar()
      // 2012-05-09 PerfektesChaos@de.wikipedia
      var maxd  =  50,
          newl,
          r     =  analyze,
          leak  =  (address < maxd
                    ||   this.inTerm - address  <  maxd);
      if (leak) {
         this.extend(3);
      } else if (this.inPipe) {
         if (address < maxd) {   // left of pipe
            leak  =  (this.inPipe - address  <  maxd);
            this.extend(1);
         } else {   // right of pipe
            leak  =  (address - this.inPipe  <  maxd);
            this.extend(2);
         }
      }
      // TODO   maybe  [[File:
      if (leak) {   // error detected and repairing
         r     =  WSTM.str.setChar(analyze,  32,  address - 2);   // ' '
         newl  =  r.indexOf("\n") + 2;
         if (newl > 1  &&  newl < this.join) {   // newline(s) left
            this.extend(-1);
            r  =  false;
         }
         this.lapsus     =  true;
         WSTM.mod.lazy   =  false;
      } else {
         this.extend(-1);
         r  =  false;
      }
      return r;
   };   // .o.Wikilink.newline()



   WikiLK.prototype.old2New  =  function () {
      // Perform user defined replacements
      // Precondition:
      //    .mod.wikilink is defined.
      // Uses:
      //    >  .o.Wikilink::
      //       >  .source
      //       >  .index
      //       >  .ModeNull
      //       >< .show
      //       >< .suffix
      //       >< .score
      //       >< .learn
      //       >< .start
      //       >< .mode
      //        < .next
      //       .extend()
      //    >  .mod.wikilink
      //    .w.link.replace.flip()
      //    .str.trimR()
      //    .str.substrEnd()
      // Requires: JavaScript 1.3   charCodeAt()
      // 2013-12-11 PerfektesChaos@de.wikipedia
      var i,
          j,
          n,
          s,
          sB  =  this.source.substr(0,  this.index + 2),
          sE  =  (this.show  ?  "|" + this.show  :  "")   +   "]]"   +
                 (this.suffix ? this.suffix : ""),
          sO,
          x   =  WSTM.w.link.replace.flip(WSTM.mod.wikilink,
                                          this.score,
                                          sB, sE, "[[");
      if (x) {
         this.learn  =  true;
         this.extend(1);
         if (typeof(x) === "string") {
            this.score  =  x;
         } else {
            if (x[0]) {
               if (x[0] !== sB) {
                  for (j = 0;  j < sB.length;  j++) {
                     if (x[0].charCodeAt(j) !== sB.charCodeAt(j)) {
                        break;   // for j
                     }
                  }   // for j
               }
            }
            if (x[1]) {
               j  =  x[1].indexOf("]]");
               if (j >= 0) {
                  mw.log(WSTM.debugging,
                         ".o.Wikilink.old2New() TODO ]] in replaced",
                         2);
               }
               j  =  x[1].indexOf("|");
               if (j >= 0) {
                  mw.log(WSTM.debugging,
                         ".o.Wikilink.old2New() TODO pipe in replaced",
                         2);
               }
               if (this.score !== x[1]) {
                  this.score  =  x[1];
                  this.extend(3);
               }
            }
            if (x[0]) {
               sO  =  x[0];
               n   =  sB.length;
               for (j = 0;  j < n;  j++) {
                  if (sO.charCodeAt(j) !== sB.charCodeAt(j)) {
                     break;   // for j
                  }
               }   // for j
               if (j >= 0) {
                  this.start  =  sO.substr(j);
               }
               j   =  n - j - 2;
               sB  =  WSTM.str.substrEnd(this.start, 2);
               if (sB === "[[") {
                  n           =  this.start.length - 2;
                  this.start  =  this.start.substr(0, n);
               } else {   // unlink
                  this.extend(4);
                  this.shift  =  this.start + this.score;
                  this.mode   =  this.ModeNull;
               }
               if (j > 0) {
                  this.extend(32, j);
               }
            }
            if (x[2] === sE) {
               x[2]  =  false;
            }
            if (x[2]) {
               sO  =  x[2];
               if ( ! this.mode) {
                  this.mode  =  0;
               }
               if (x[3]) {
                  this.shift   =  this.start + this.score + sO;
                  this.mode    =  this.ModeNull;
                  this.show    =  false;
                  this.suffix  =  "";
                  this.extend(4);
                  this.extend(16, sO.length);   // 2013-12-11
//  -1  -- no extension
//   1  -- target region (beginning at 2)
//   2  -- title region
//   3  -- end of both target and title
//   4  -- begin at 0, not at 2
//   8  -- end after first terminating bracket
//   9  -- second terminating bracket appended
//  16  -- append afterward characters
//  32  -- begin and terminate at least at 0
mw.log(WSTM.debugging,"WikiLK.old2New() sO="+sO,0);   //2013-12-01
               } else {
                  j  =  sO.indexOf("]]");
                  if (this.mode <= 0  ||  ! j) {
                     if (j >= 0) {
                           n  =  this.suffix.length;
                        for (i = sO.length;  i && n;  i--, n--) {
                           if (this.suffix.charCodeAt(n)
                               !== sO.charCodeAt(i)) {
                              break;   // for i
                           }
                        }   // for i
                        if (i || n) {
                           this.extend(16, n);
                           this.next  =  i;
                        }
                        if (sO.charCodeAt(0) === 124) {   // '|'
                           sO  =  sO.substr(1);
                           j--;
                        }
                        this.show    =  (j  ?  sO.substr(0, j)
                                            :  false);
                        this.suffix  =  sO.substr(j + 2);
                     }
                     j  =  -1;
                     if (x[0]) {
                        this.extend(3);
                     }
                  } else {
                     this.show  =  false;
                  }
               }
               if (j > 0) {
                  s  =  sO.substr(0, j);
                  if (s.indexOf("[") < 0) {
                     this.show   =  WSTM.str.trimR(s, true);
                     j          +=  2;
                  } else {
                     j  =  -2;
                  }
               } else if (j === 0) {
                  j  =  2;
               }
               if (this.show) {
                  i  =  this.show.indexOf("|");
                  if (i > 0) {
                     this.score   =  this.score
                                     +  this.show.substr(0, i);
                     this.show    =  this.show.substr(i + 1);
                  }
               }
               if (this.suffix) {
                  n  =  this.suffix.length;
               } else {
                  n  =  0;
               }
               this.extend(2);
            }
            if (this.mode < 0) {
               this.start   =  false;
               this.show    =  false;
               this.score   =  false;
               this.inPipe  =  false;
            }
         }   // modified
      }   // user defined replacements
   };   // .o.Wikilink.old2New()



   WikiLK.prototype.pipeSymbols  =  function (align) {
      // Analyze pipe symbol(s) within wikilink
      // Precondition:
      //    align  -- position of '|' in this.score  (>=0)
      // Uses:
      //    >  .o.Wikilink::
      //       >  .ModeFile
      //       >< .score
      //       >< .mode
      //        < .lapsus
      //        < .show
      //       .extend()
      //    .str.trimL()
      //    .errors.found()
      // 2012-10-18 PerfektesChaos@de.wikipedia
      var lead,
          less,
          mid,
          n,
          s,
          shine  =  this.score.substr(align + 1);
      if (align > 0) {
         this.score  =  this.score.substr(0, align);
      } else {
         this.score  =  false;
      }
      s  =  WSTM.str.trimL(shine, false);
      n  =  s.length;
      if (n < shine.length) {
         this.extend(2);
      }   // ltrim
      if (n === 0) {
         if (! this.score) {   // [[|]]
            WSTM.errors.found("meaninglessLinkTarget", true, "");
            this.extend(-1);
            shine  =  false;
         }
      }
      if (shine) {
         mid  =  s.indexOf("|");
         if (mid < 0) {
            this.show  =  shine;
         } else {   // second pipe
            if (mid === 0) {   // directly following
               s    =  s.substr(1);
               mid  =  s.indexOf("|");
               this.extend(2);
               this.lapsus  =  true;
               if (mid < 0) {
                  s  =  WSTM.str.trimL(s, false);
                  if (! s.length) {
                     s  =  false;
                  }
               }
            } else {
               less  =  true;
               if (this.score) {
                  lead  =  false;
                  less  =  (this.mode !== this.ModeFile);
               } else {   // empty target, something like   [[|a|b]]
                  lead        =  true;
                  this.score  =  WSTM.str.trimR(s.substr(0, mid),
                                                false);
                  s           =  WSTM.str.trimL(s.substr(mid + 1),
                                                false);
                  mid         =  s.length;
                  this.extend(2);
               }
               if (less && s) {
                  if (s.substr(0, mid).indexOf("{{")  <  0) {
                     WSTM.errors.found("multiplePipeSymbols",
                                       lead,
                                       "[["  +  (lead ? "|" : "")
                                       +  this.score  +  "|"
                                       +  s.substr(0, 50));
                                        // this.source.substr(
                     if (this.lapsus) {
                        this.extend(-1);
                        s  =  false;
                     } else {
                        this.lapsus  =  true;
                     }
                  }
               }   // less
            }
            this.show  =  s;
         }   // second pipe
      }
   };   // .o.Wikilink.pipeSymbols()



   WikiLK.prototype.project  =  function (ahead) {
      // Analyze wikilink beginning whether it starts with project
      // Precondition:
      //    ahead  -- position of ':' in this.score (>0)
      // Postcondition:
      //    Returns true iff project stripped off from this.score
      //    RegExp was used.
      // Uses:
      //    >  .o.Wikilink::
      //       >< .score
      //        < .lead
      //        < .sister
      //        < .justify
      //       .extend()
      //    .str.trimL()
      //    .str.camelCasing()
      //    .w.link.projects.friend()
      // 2012-12-06 PerfektesChaos@de.wikipedia
      var join,
          kind,
          sole  =  "|commons|mediawiki|meta|",
          swap,
          r     =  false,
          s     =  this.score.substr(0, ahead);
      if (ahead === 1) {
         if (s === s.toUpperCase()) {
            s  =  false;
            // ":sv:S:t Eriksplan (tunnelbanestation)"
         }
      }
      if (s) {
         r  =  WSTM.w.link.projects.friend(s, true);
      }
      if (r) {   // project identified
         kind  =  r[0];
         swap  =  r[1];
         this.score  =  WSTM.str.trimL(this.score.substr(ahead + 1),
                                       false);
         if (this.score.length  <  ahead + 1) {
            this.extend(1);
         }   // trimmed
         if (kind === 3) {
            if (sole.indexOf("|" + swap + "|")  >  0) {
               this.sister  =  swap + ":";
               if (this.score.charCodeAt(4) === 58) {   // ':'
                  join  =  4;
               } else if (this.score.charCodeAt(5) === 58) {   // ':'
                  join  =  5;
               } else {
                  join  =  0;
               }
               if (join) {
                  s  =  this.score.substr(0, join).toLowerCase();
                  if (s ===  "file"  ||  s ===  "image") {
                     swap  =  this.score;
                     join++;
                     this.score   =  this.score.substr(join);
                     this.score   =  WSTM.str.trimL(this.score, false);
                     this.score   =  WSTM.str.camelCasing(this.score);
                     this.lead    =  true;
                     this.sister  =  this.sister + "File:";
                     if (swap  !==  this.sister + this.score) {
                        this.extend(1);
                     }
                     kind  =  false;
                     swap  =  false;
                  }
               }
            }
         }
         if (kind) {
            if (this.lead) {   // superfluous heading ':'
               this.extend(1);
            }
            this.lead  =  true;
            if (swap) {
               this.sister   =  swap + ":";
               this.justify  =  kind;
               if (swap !== s) {
                  this.extend(1);
               }
            } else {   // itself
               this.justify  =  2;
               this.extend(1);
            }
         } else if (swap) {   // project major namespace
            this.score  =  swap + ":" + this.score;
            this.extend(1);
         }   // mode
      }   // affiliated project
      return r;
   };   // .o.Wikilink.project()



   WikiLK.prototype.specials  =  function () {
      // Handle link into special namespace
      // Precondition:
      //    .score is defined, special namespace already stripped off.
      // Uses:
      //    >  .o.Wikilink::
      //       >  .ModeNull
      //       >< .RE_ISBN
      //       >< .score
      //        < .inPipe
      //        < .show
      //        < .shift
      //        < .mode
      //       .extend()
      //    .hooks.fire()
      //    .util.code.isbn()
      // 2017-01-01 PerfektesChaos@de.wikipedia
      var got,
          s    =  this.score.substr(0, 1);
      if (! WikiLK.RE_ISBN) {
         WikiLK.RE_ISBN  =  "^([^/]+)"
                            + "/(ISBN *)?"
                            + "([-0-9]{9,}[0-9X]|[-0-9]{13,})$";
         WikiLK.RE_ISBN  =  new RegExp(WikiLK.RE_ISBN, "i");
      }
      if (s.toUpperCase() !== s) {
         this.score  =  s.toUpperCase() + this.score.substr(1);
         this.extend(1);
      }
      got  =  WikiLK.RE_ISBN.exec(this.score);
      if (got) {
         if (WSTM.hooks.fire("booksources", got[1])) {
            s    =  got[3];
            got  =  WSTM.util.code.isbn(s);
            if (got[0]) {
               if (got[1]) {
                  s  =  got[1];
               }
            }
            this.extend(4);
            this.shift   =  "ISBN " + s;
            this.extend(16, 0);
            this.inPipe  =  false;
            this.show    =  false;
            this.score   =  false;
            this.mode    =  this.ModeNull;
         }
      }
   };   // .o.Wikilink.specials()



   WikiLK.prototype.target  =  function () {
      // Analyze wikilink target
      // Precondition:
      //    .score is defined.
      // Uses:
      //    >  .o.Wikilink::
      //       >  .ModeFile
      //       >  .justify
      //       >  .ModeIw
      //       >< .score
      //       >< .show
      //       >< .lead
      //       >< .slang
      //       >< .sister
      //        < .mode
      //        < .leap
      //        < .special
      //        < .leader
      //       .extend()
      //       .project()
      //       .language()
      //       .namespace()
      //       .capitalize()
      //       .old2New()
      //    >  .lang.chr.zwsp
      //    >  .lang.chr.zwnj
      //    >  .lang.chr.zwj
      //    >  .o.WikiTom.LinkInterWiki
      //    >  .mod.wikilink
      //    .w.link.filter()
      //    .str.isBlank()
      //    .str.trimL()
      //    .w.link.wiki.target()
      //    .w.link.wiki.further()
      // Requires: JavaScript 1.3   charCodeAt()   fromCharCode()
      // 2012-11-06 PerfektesChaos@de.wikipedia
      var s     =  WSTM.w.link.filter(this.score,
                                      this.show
                                      ||  (this.mode === this.ModeFile)),
          join,
          label,
          lang,
          re;
      if (s) {
         this.score  =  s;
         this.extend(1);
      }   // undesired character removed
      if (WSTM.str.isBlank(this.score.charCodeAt(0), false)) {
         this.score  =  WSTM.str.trimL(this.score.substr(1), true);
         if (! this.show) {
            this.leap   =  true;
         }
      }
      while (this.score.charCodeAt(0) === 58) {   // ':'
         this.lead   =  true;
         this.score  =  WSTM.str.trimL(this.score.substr(1), true);
      }   // while leading ':'
      join  =  this.score.indexOf(":");
      if (join > 0) {
         label  =  this.project(join);
         lang   =  false;
         if (! label) {
            lang  =  this.language(join);
         }
         if (label  ||  (lang && this.lead)) {   // one already in effect
            join  =  this.score.indexOf(":");
            if (join > 0) {
               if (label) {   //   (:)project:lang:Lemma ??
                  this.language(join);
               } else if (this.lead) {   //   :lang:project:Lemma ??
                  this.project(join);
               }
            }   // ':'  ':'
         }
         if (! this.slang
             &&  this.justify === 2) {  // no lang, but needed
            this.slang  =  "en:";
            this.extend(1);
            this.lead  =  true;
         }
         if (this.slang) {   // lang detected
            // 200A   8203  ZERO WIDTH SPACE
            // 200B   8204  ZERO WIDTH NON-JOINER
            // 200C   8205  ZERO WIDTH JOINER
            if (WSTM.lang.chr.zwsp.indexOf(":" + this.slang)  >=  0) {
               if (this.score.indexOf("&#x200A;") >= 0) {
                  re  =  new RegExp("&#x200A;", "g");
                  s   =  String.fromCharCode(8203);
                  if (re.test(this.score)) {
                     this.score  =  this.score.replace(re, s);
                  }
               }
            }
            if (this.score.indexOf("&zw") >= 0) {
               if (WSTM.lang.chr.zwnj.indexOf(":" + this.slang)  >=  0) {
                  re  =  new RegExp("&zwnj;", "g");
                  if (re.test(this.score)) {
                     s           =  String.fromCharCode(8204);
                     this.score  =  this.score.replace(re, s);
                  }
               }
               if (WSTM.lang.chr.zwj.indexOf(":" + this.slang)  >=  0) {
                  re  =  new RegExp("&zwj;", "g");
                  if (re.test(this.score)) {
                     s           =  String.fromCharCode(8205);
                     this.score  =  this.score.replace(re, s);
                  }
               }
            }
            if (this.sister) {
               this.sister  =  this.sister + this.slang;
               this.lead    =  true;
            } else if (this.lead) {
               this.sister  =  ":" + this.slang;
            } else {   // (! this.lead) -- interlanguage
               this.mode     =  this.ModeIw;   // .interlanguage()
               this.special  =  this.slang + this.score;
               this.sister   =  this.slang;
               s             =  this.slang.substr(0,
                                                  this.slang.length - 1);
               this.leader   =  WSTM.w.link.wiki.further(
                                  { mode:   WSTM.o.WikiTom.LinkInterWiki,
                                    source: s } );
            }
         }
         join  =  this.score.indexOf(":");
      }   // ':'
      s  =  WSTM.w.link.wiki.target(this.score,  ! this.show);
      if (s) {
         this.score  =  s;
         this.extend(1);
         if (join > 0) {   // adjust
            join  =  this.score.indexOf(":");
         }
      }
      if (join > 0) {   // namespace?
         this.namespace(join);
      }   // namespace?
      if (this.show) {
         if (WSTM.str.isBlank(this.show.charCodeAt(0), false)) {
            this.show  =  WSTM.str.trimL(this.show.substr(1), true);
            this.leap  =  true;
         }
         this.capitalize();
      }   // titled?
      if (this.sister) {
         this.score  =  this.sister + this.score;
      }
      if (this.lead  &&  ! this.sister) {
         if (this.score.charCodeAt(0) !== 47) {   // '/'
            this.lead  =  false;
         }
      }
      if (WSTM.mod.wikilink && this.score) {
         this.old2New();
      }   // user defined replacements
   };   // .o.Wikilink.target()



   WikiLK.prototype.title  =  function () {
      // Analyze wikilink title and representation
      // Precondition:
      //    this.show not a Category
      // Uses:
      //    >  .o.Wikilink::
      //       >< .show
      //       >< .suffix
      //       >< .next
      //        < .leap
      //       .extend()
      //    .str.trimR()
      //    .str.isBlank()
      //    .str.trimL()
      // Requires: JavaScript 1.3   charCodeAt()
      // 2012-12-27 PerfektesChaos@de.wikipedia
      var k,
          leap,
          m  =  this.show.length,
          n;
      this.show  =  WSTM.str.trimR(this.show, false);
      n  =  this.show.length;
      if (n < m) {
         leap  =  false;
         if (this.suffix) {
            k  =  this.suffix.charCodeAt(0);
            if (k === 10) {
               leap  =  true;
            } else {
               leap  =  WSTM.str.isBlank(k, true);
            }
         }
         if (leap) {   // whitespace follows
            this.extend(2);
         } else {
            this.suffix  =  " "  +  (this.suffix ? this.suffix : "");
            this.extend(16, 0);
            this.next  =  (this.next ? this.next++ : 1);
         }
      }   // rtrim
      if (WSTM.str.isBlank(this.show.charCodeAt(0), false)) {
         this.show  =  WSTM.str.trimL(this.show.substr(1), false);
         this.extend(32);
         this.leap  =  true;
      }   // ltrim
   };   // .o.Wikilink.title()



   WikiLK.prototype.unlink  =  function () {
      // Analyze link whether it can be unlinked or merged with title
      // Precondition:
      //    Wikilink without leading colon ':'
      // Uses:
      //    >  .g.wTitle
      //    >  .g.wNsNumber
      //    >  .o.Wikilink::
      //       >  .sister
      //       >  .subcase
      //       >  .off
      //       >  .onto
      //       >  .ModeNull
      //       >  .lapsus
      //       >< .score
      //       >< .show
      //       >< .suffix
      //       >< .next
      //        < .inPipe
      //        < .shift
      //        < .mode
      //       .extend()
      //    .hooks.fire()
      //    .str.deCapitalize()
      //    .errors.found()
      //    .w.link.wiki.fore()
      //    .str.isLetter()
      // 2016-08-17 PerfektesChaos@de.wikipedia
      var lower  =  WSTM.hooks.fire("capitalize1"),
          sw     =  (lower ? WSTM.str.deCapitalize(this.score)
                           : this.score),
          io,
          j,
          n,
          sh;
      if (! this.sister  &&  WSTM.g.wNsNumber === 0) {
         io  =  0;
         if (sw === this.subcase) {   // unlink
            io  =  1;
            WSTM.errors.found("wikilinkSelf", false, false);
         } else if (sw.indexOf(this.subcase + "#")  ===  0) {   // intern
            io  =  2;
         }
         if (io > 0) {
            if (this.off && this.onto) {
               if (this.off.isNodeInSpan(this.onto)) {
                  io  =  -1;
               }
            }
         }
         if (io === 1) {   // unlink
            this.inPipe  =  false;
            if (this.show) {
               this.shift  =  this.show;
               this.show   =  false;
            } else {
               this.shift  =  this.score;
            }
            this.score  =  false;
            this.extend(4);
            this.extend(16, 0);
            this.mode  =  this.ModeNull;
         } else if (io === 2) {   // internal
            this.score  =  this.score.substr(WSTM.g.wTitle.length);
            this.extend(1);
         }
      }   // same project
      if (this.show) {
         sh  =  (lower ? WSTM.str.deCapitalize(this.show)
                       : this.show);
         if (sw === sh  &&  ! this.sister) {
            this.score  =  this.show;
            this.show   =  false;
            this.extend(3);
            this.inPipe  =  false;
         } else if (n  &&  ! this.lapsus) {
            n  =  sw.length;
            if (sw  ===  sh.substr(0, n)
                &&   WSTM.str.isLetter(sh.substr(n - 1,  1))
                // [[Ferrari F2001|Ferrari F2001B]]
                //     !==       [[Ferrari F2001]]B
                &&   WSTM.str.isLetter(sh.substr(n, 1))) {
               j  =  WSTM.w.link.wiki.fore(sh, n);
               if (j) {
                  if (j + n  ===  sh.length) {
                     this.score  =  this.show.substr(0, n);
                     this.show   =  this.show.substr(n);
                     j           =  this.show.length;
                     if (j > 0) {
                        if (this.suffix) {
                           this.suffix  =  this.show + this.suffix;
                           if (! this.next) {
                              this.next  =  j;
                           }
                        } else {
                           this.suffix  =  this.show;
                           if (! this.next) {
                              this.next  =  true;
                           }
                        }
                     }
                     this.show  =  false;
                     this.extend(16, 0);
                     this.inPipe  =  false;
                  }   // link range unchanged
               }   // do not merge sophisticated link titles
            } else if (this.suffix) {
               if (sh  ===  sw.substr(0, sh.length)) {
                  j  =  WSTM.w.link.wiki.fore(this.suffix, 0);
                  if (j) {
                     if (sw  ===  sh + this.suffix.substr(0, j)) {
                        this.suffix  =  this.suffix.substr(j);
                        this.show  =  false;
                        this.extend(16, j);
                        this.inPipe  =  false;
                     }
                  }
               }
            }
         }   // title === target, or part
      }   // titled
   };   // .o.Wikilink.unlink()



   WikiLK.prototype.url  =  function (adjust) {
      // Analyze bracket content whether it starts with an URL
      // Precondition:
      //    adjust  -- string with possible link target, left trimmed
      // Postcondition:
      //    Returns true iff adjust starts with an URL
      // Uses:
      //    >  .o.Wikilink::
      //       >  .suffix
      //       >  .ModeWeb
      //        < .lapsus
      //        < .mode
      //        < .move
      //        < .shift
      //       .extend()
      //    .util.isURL()
      // Requires: JavaScript 1.3   charCodeAt()
      // 2016-01-20 PerfektesChaos@de.wikipedia
      var r  =  false,
          s;
      switch (adjust.charCodeAt(1)) {
         case  47 :   //  '/'
            r  =  (adjust.charCodeAt(0) === 47);
            break;
         case 116 :   //  't'
            s  =  adjust.substr(0, 4);
            r  =  (s === "http"  ||  s === "ftp:");
            break;
      }   // switch charCodeAt(1)
      if (r  &&  WSTM.util.isURL(adjust, true, true)) {
         r  =  true;
         this.extend(3);
         this.extend(4);
         if (this.suffix) {
            if (this.suffix.charCodeAt(0) === 93) {   // ']'
               this.extend(8);   // post first bracket
            }   // terminating double brackets
         }   // follow
         this.lapsus  =  true;
         this.mode    =  this.ModeWeb;
         this.move    =  adjust.length + 2;
         this.shift   =  "[" + adjust;
      }
      return r;
   };   // .o.Wikilink.url()



   WikiLK.prototype.getBracketShift  =  function () {
      // Returns true if leading bracket has been shifted to the right
      // Uses:
      //    >  .o.Wikilink::
      //       >  .leap
      //       >  .lift
      // 2012-10-10 PerfektesChaos@de.wikipedia
      return  (this.leap || this.lift);
   };   // .o.Wikilink.getBracketShift()



   WikiLK.prototype.getChange  =  function () {
      // Returns true if any need for changes
      // Uses:
      //    >  .o.Wikilink::
      //       >  .shift
      //       >  .newTo
      //       >  .newFrom
      //       >  .source
      //       >  .index
      // 2012-09-19 PerfektesChaos@de.wikipedia
      var r  =  (typeof(this.shift) === "string");
      if (r) {
         r  =  (typeof(this.newTo) === "number");
         if (r) {
            if (this.newFrom === 2) {
               r  =  (this.source.substr(this.index + 2,  this.newTo - 2)
                      !==   this.shift);
            } else {
               r  =  (typeof(this.newFrom) === "number");
            }
         }
      }
      return r;
   };   // .o.Wikilink.getChange()


   WikiLK.prototype.getError  =  function () {
      // Return true if severe syntax error detected (repaired if change)
      // Uses:
      //    >  .o.Wikilink::
      //       >  .lapsus
      // 2010-10-01 PerfektesChaos@de.wikipedia
      return  this.lapsus;
   };   // .o.Wikilink.getError()


   WikiLK.prototype.getIncrement  =  function () {
      // Returns number of chars to advance in basic text
      // Uses:
      //    >  .o.Wikilink::
      //       >  .move
      // 2010-10-01 PerfektesChaos@de.wikipedia
      return  this.move;
   };   // .o.Wikilink.getIncrement()


   WikiLK.prototype.getLeader  =  function () {
      // Returns true if category/interlanguage has been first occurrence
      // Uses:
      //    >  .o.Wikilink::
      //       >  .leader
      // 2012-04-26 PerfektesChaos@de.wikipedia
      return  this.leader;
   };   // .o.Wikilink.getLeader()


   WikiLK.prototype.getRemoveFrom  =  function () {
      // Returns number of target chars since which to remove, or false
      // Uses:
      //    >  .o.Wikilink::
      //       >  .newFrom
      //       >  .newTo
      // 2011-05-03 PerfektesChaos@de.wikipedia
      var k  =  this.newFrom;
      if (k) {
         if (typeof(this.newTo) !== "number") {
            k  =  false;
         }
      }
      return  k;
   };   // .o.Wikilink.getRemoveFrom()


   WikiLK.prototype.getRemoveTo  =  function () {
      // Returns number of target chars to be removed, or false
      // Uses:
      //    >  .o.Wikilink::
      //       >  .newTo
      // 2010-10-01 PerfektesChaos@de.wikipedia
      return  this.newTo;
   };   // .o.Wikilink.getRemoveTo()


   WikiLK.prototype.getSortkey  =  function () {
      // Returns number of chars to be unconditionally locked, or false
      // Uses:
      //    >  .o.Wikilink::
      //       >  .keySort
      // 2010-10-01 PerfektesChaos@de.wikipedia
      return  this.keySort;
   };   // .o.Wikilink.getSortkey()


   WikiLK.prototype.getSpecial  =  function () {
      // Returns category or interlanguage content
      // Uses:
      //    >  .o.Wikilink::
      //       >  .special  //  -> schedule
      // 2010-10-01 PerfektesChaos@de.wikipedia
      return  this.special;
   };   // .o.Wikilink.getSpecial()


   WikiLK.prototype.getTargetLength  =  function () {
      // Returns number of target chars to lock, or false if not adhere
      // Uses:
      //    >  .o.Wikilink::
      //       >  .lock
      //       >  .score
      //       >  .nucleus
      // 2010-10-01 PerfektesChaos@de.wikipedia
      //    [1]
      var r  =  false;
      if (this.lock && this.score) {
         r  =  (this.nucleus ? this.nucleus : this.score.length);
      }
      return  r;
   };   // .o.Wikilink.getTargetLength()


   WikiLK.prototype.getTextReplace  =  function () {
      // Returns replacement string, maybe false if nothing to do
      // Uses:
      //    >  .o.Wikilink::
      //       >  .shift
      // 2010-10-01 PerfektesChaos@de.wikipedia
      return  this.shift;
   };   // .o.Wikilink.getTextReplace()


   WikiLK.prototype.getTitle  =  function () {
      // Returns title string, maybe false
      // Uses:
      //    >  .o.Wikilink::
      //       >  .show
      // 2018-11-19 PerfektesChaos@de.wikipedia
      return  this.show;
   };   // .o.Wikilink.getTitle()


   WikiLK.prototype.getType  =  function () {
      // Returns special link type, or false if not a link
      //    .ModeNull    unlinked (magic, self-reference, or replaced)
      //    .ModeWeb     weblink
      //    .ModeWiki    normal wikilink
      //    .ModeFile    file (not Media:)
      //    .ModeCat     category
      //    .ModeIw      interlanguage
      //                 special interwikilink ([[bugzilla:...]] etc.)
      //                 page
      //    .ModeMap     special weblink ([[DOI:...]] etc.)
      // Uses:
      //    >  .o.Wikilink::
      //       >  .mode
      // 2010-10-22 PerfektesChaos@de.wikipedia
      return  this.mode;
   };   // .o.Wikilink.getType()


   WikiLK.prototype.getUserModified  =  function () {
      // Returns true if user defined modification performed
      // Uses:
      //    >  .o.Wikilink::
      //       >  .learn
      // 2010-10-01 PerfektesChaos@de.wikipedia
      return  this.learn;
   };   // .o.Wikilink.getUserModified()



   WikiLK.prototype.set  =  function (anode, access, adhere) {
      // Initialize existing wikilink object with node
      // Precondition:
      //    anode    -- parent node object
      //    access   -- anode position {i,k} where '[[' starts
      //    adhere   -- true: freeze link targets
      // Postcondition:
      //    .o.Wikilink object was set and has been analyzed
      //    RegExp was used.
      // Uses:
      //    WikiTom().toString()
      //    >  .o.Wikilink::
      //        < .onto
      //        < .node
      //        < .index
      //        < .lack
      //        < .lock
      //        < .off
      //        < .move
      //        < .newFrom
      //        < .source
      //       .init()
      //       .adjust()
      // 2013-02-15 PerfektesChaos@de.wikipedia
      this.init();
      this.off      =  anode;
      this.node     =  access.k;
      this.onto     =  anode.focus(this.node);
      this.index    =  access.i;
      this.lack     =  access.lack;
      this.lock     =  adhere;
      this.move     =  2;
      this.newFrom  =  2;
      this.source   =  this.onto.toString();
      this.adjust(this.source.substr(this.index));
   };   // .o.Wikilink.set()



/*
   WikiLK.prototype.setTarget  =  function (adjust) {
      // Initialize existing wikilink object with target string
      // Precondition:
      //    adjust   -- target string
      // Postcondition:
      //    .o.Wikilink object was set and has been analyzed
      //    RegExp was used.
      // Uses:
      //    WikiTom().toString()
      //    >  .o.Wikilink::
      //        < .source
      //       .init()
      //       .adjust()
      // 2010-11-06 PerfektesChaos@de.wikipedia
      this.init();
      this.source   =  adjust;
      this.adjust(this.source);
   };   // .o.Wikilink.setTarget()
*/



};   // .bb.Wikilink()
mw.libs.WikiSyntaxTextMod.bb.Wikilink(mw.libs.WikiSyntaxTextMod);
delete mw.libs.WikiSyntaxTextMod.bb.Wikilink;



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



mw.libs.WikiSyntaxTextMod.bb.WikiTom  =  function (WSTM) {
// Text and wikisyntax node
// 2012-07-07 PerfektesChaos@de.wikipedia
   "use strict";
   var WTOM;


WSTM.o.WikiTom  =  function (assign, above) {
   // .constructor for new
   // Precondition:
   //    assign  -- string
   //    above   -- parent Node   root: null
   // Postcondition:
   //    Returns new WikiTom
   // 2012-03-12 PerfektesChaos@de.wikipedia
   this.parent    =  above;    // Object
   this.source    =  assign;   // String   basic or no change in children
   this.learnt    =  false;    // boolean  any (minor) modification
   this.lookup    =  true;     // boolean  may be searched
   this.limited   =  false;    // boolean  end of block
   this.mode      =  0;        // Number   kind of node
// this.children               // Array    of WikiTom nodes
// this.scope                  // String   group description
   return this;
};   // .o.WikiTom() .constructor
WTOM  =  WSTM.o.WikiTom;



// Definition of constants
// 2016-01-12 PerfektesChaos@de.wikipedia
WTOM.type            =  "WikiTom";
WTOM.WSTMinternal    =  -9;
WTOM.TextOnly        =   1;
WTOM.Nowiki          =  10;
WTOM.Inline          =  11;
WTOM.CodedBlock      =  13;
WTOM.CodedInline     =  14;
WTOM.CodeBlock       =  15;
WTOM.Code            =  16;
WTOM.Comment         =  18;
WTOM.CommentOld      =  19;
WTOM.Tag             =  20;
WTOM.TagUnary        =  21;
WTOM.TagBegin        =  22;
WTOM.TagEnd          =  23;
WTOM.TagBinary       =  29;
WTOM.LinkWiki        =  31;
WTOM.LinkFile        =  32;   // link to File: even without NS
WTOM.LinkTemplate    =  33;   // link to Template: even in {{
WTOM.LinkCategory    =  34;
WTOM.LinkExtWiki     =  35;
WTOM.LinkInterWiki   =  36;
WTOM.LinkWikiTotal   =  38;   // link to entire Link including [[]]
WTOM.LinkWikiPipe    =  38;
WTOM.LinkWeb         =  39;
WTOM.MagicWord       =  41;
WTOM.ParserFun       =  42;
WTOM.Template        =  43;
WTOM.TmplBrackets    =  44;
WTOM.TmplParName     =  45;
WTOM.TmplParAssign   =  46;
WTOM.TmplParValue    =  47;
WTOM.TmplParAccess   =  48;
WTOM.FileParam       =  48;
WTOM.Sortkey         =  50;
WTOM.Table           =  60;
WTOM.TableRow        =  61;
WTOM.TableAttr       =  62;



WTOM.prototype.fade  =  function () {
   // Deconstruct WikiTom node
   // Postcondition:
   //    Allocated members are destroyed.
   // Uses:
   //    >- this.children
   //    >- this.source
   //    this.children.fade()
   // 2012-03-07 PerfektesChaos@de.wikipedia
   var i;
   if (this.source) {
      delete this.source;
   }
   if (this.children) {
      for (i = 0;  i < this.children.length;  i++) {
         this.children[i].fade();
      }   // for i
      delete this.children;
   }
};   // .o.WikiTom().fade()



WTOM.prototype.fetch  =  function (assigned, ahead, alone) {
   // Access string in WikiTom
   // Precondition:
   //    assigned  -- sibling number to be accessed
   //    ahead     -- string position to start within assigned
   //                 ahead < 0  count from end
   //    alone     -- retrieve charCodeAt only
   // Postcondition:
   //    Returns  string,  or false if assigned not found
   // Uses:
   //    >  this.children
   //    >  this.source
   //    this.children.toString()
   //    mw.log()
   // 2012-07-17 PerfektesChaos@de.wikipedia
   var j,
       r  =  false;
   if (this.children) {
      if (assigned < this.children.length) {
         r  =  this.children[assigned].toString();
      } else {
         mw.log(WSTM.debugging,
                ".WikiTom().fetch() node beyond length " + assigned,
                3,
                this);
         r  =  "";
      }
   } else {   // plain string
      if (assigned) {
         mw.log(WSTM.debugging,
                ".WikiTom().fetch() bad node for string " + assigned,
                3,
                this);
      } else if (this.source) {
         r  =  this.source;
      } else {   // empty page
         r  =  "";
      }
   }
   if (r && ahead) {
      if (ahead < 0) {
         j  =  r.length + ahead;
         if (j > 0) {
            r  =  r.substr(j);
         }
      } else {
         r  =  r.substr(ahead);
      }
   }
   if (r && alone) {
      r  =  r.charCodeAt(0);
   }
   return r;
};   // .o.WikiTom().fetch()



WTOM.prototype.find  =  function (achieve, already, assigned, allow, alone, attached) {
   // Find term in WikiTom
   // Precondition:
   //    achieve   -- string or regexp info to be searched
   //                 regexp info:
   //                 [0]  RegExp object
   //                 [1]  bracket number
   //    already   -- string position to start
   //    assigned  -- sibling number containing already
   //    allow     -- permit inspection of children
   //    alone     -- skip after element limitation since assigned
   //    attached  -- achieve is deeper than application request
   // Postcondition:
   //    Returns  false, if achieve not found,  or found info object
   //             .i      string position of beginning
   //             .k      sibling number
   //             .m      regexp match for bracket number, if regexp
   //             .r      regexp result array, if regexp
   //             .child  object if allow and attached
   //                     .i  string position of beginning in child
   //                     .k  child number
   //                     .o  WikiTom child itself
   //    RegExp was used.
   // Uses:
   //    >  this.lookup
   //    >  this.source
   //    >  this.children
   //    >  this.limited
   //    this.children.find()   -- recursive
   // 2012-06-16 PerfektesChaos@de.wikipedia
   var join,
       node,
       e,
       i,
       j,
       n,
       s,
       r     =  false;
   if (this.lookup) {
      join  =  (typeof(already) === "number"  ?  already  :  0);
      node  =  (typeof(assigned) === "number"  ?  assigned  :  0);
      if (typeof(this.children) === "object") {
         if (allow) {
            n  =  this.children.length;
            j  =  join;
            for (i = node;  i < n;  i++) {
               e  =  this.children[i];
               r  =  e.find(achieve, j, 0, attached, false, attached);
               if (r) {
                  if (attached) {
                     r.child  =  { i: r.i,
                                   k: r.k,
                                   o: this };
                  }
                  r.k  =  i;
                  break;   // for i
               }
               if (alone && e.limited) {
                  break;   // for i
               }
               j  =  0;
            }   // for i
         }
      } else if (! node) {   // plain string
         s  =  this.source;
         n  =  s.length;
         if (n) {
            if (join) {
               if (n > join) {
                  s  =  s.substr(join);
               } else {
                  n  =  0;
               }
            }
            if (n) {
               if (typeof(achieve) === "string") {
                  j  =  s.indexOf(achieve);
                  if (j >= 0) {
                     r  =  { i:  join + j,
                             k:  0 };
                  }
               } else {
                  j  =  s.search(achieve[0]);
                  if (j >= 0) {
                     r  =  s.match(achieve[0]);
                     r  =  { i:  join + j,
                             k:  0,
                             m:  r[ achieve[1] ],
                             r:  r };
                  }
               }
            }
         }
      }
   }
   return r;
};   // .o.WikiTom().find()



WTOM.prototype.fixTab  =  function (adjust) {
   // Remove tab chars from WikiTom, if any
   // Precondition:
   //    adjust  -- WikiTom to be changed, if necessary
   //               >  .mode
   //               >  .children
   //               >< .source
   // Postcondition:
   //    adjust is updated
   // Uses:
   //    >  this.CodedBlock
   //    this.fixTab()   -- recursive
   //    .w.chr.fixTab()
   //    this.fresh()
   // 2012-03-23 PerfektesChaos@de.wikipedia
   var j,
       s;
   if (adjust.mode !== WTOM.CodedBlock) {
      if (adjust.children) {
         for (j = 0;  j < adjust.children.length;  j++) {
            this.fixTab(adjust.children[j]);
         }   // for j
      } else if (adjust.source) {
         s  =  WSTM.w.chr.fixTab(adjust.source);
         if (s) {
            adjust.fresh(s);
         }
      }
   }
};   // .o.WikiTom().fixTab()



WTOM.prototype.flip  =  function (assigned, ahead, amount, apply) {
   // Exchange string in WikiTom
   // Precondition:
   //    assigned  -- sibling number containing ahead
   //    ahead     -- string position to start exchange within assigned
   //    amount    -- number of characters to remove within assigned
   //    apply     -- string to be inserted
   // Postcondition:
   //    Returns entire node string, or false
   // Uses:
   //    >  this.children
   //    >  this.limited
   //    >  this.lookup
   //    >  this.mode
   //    >< this.source
   //    this.children.toString()
   //    this.fade()
   //    this.fresh()
   //    .str.setString()
   //    mw.log()
   // 2012-04-30 PerfektesChaos@de.wikipedia
   var s  =  false,
       r  =  false,
       p;
   if (this.children) {
      if (assigned < this.children.length) {
         r  =  this.children[assigned].toString();
      } else {
         s  =  "node beyond length";
      }
   } else {   // plain string
      if (assigned) {
         s  =  "bad node for string";
      } else {
         r  =  this.source;
      }
   }
   if (s) {
      mw.log(WSTM.debugging,
             ".WikiTom().flip() " + s + " " + assigned,
             3,
             this);
   } else {
      r  =  WSTM.str.setString(r, ahead, amount, apply);
      if (this.children) {
         s  =  new WSTM.o.WikiTom(r, this);
         p  =  this.children[assigned];
         s.limited  =  p.limited;
         s.lookup   =  p.lookup;
         s.mode     =  p.mode;
         this.children[assigned]  =  s;
         p.fade();
         s.fresh(false);
      } else {
         this.fresh(r);
      }
   }
   return r;
};   // .o.WikiTom().flip()



WTOM.prototype.flush  =  function (avoid) {
   // Remove one child
   // Precondition:
   //    avoid  -- child number
   // Postcondition:
   //    this has been updated
   // Uses:
   //    >  this.children
   //    mw.log()
   // 2012-04-23 PerfektesChaos@de.wikipedia
   var q  =  this.children;
   if (q) {
      if (avoid < q.length) {
         q.splice(avoid, 1);
      } else {
         mw.log(WSTM.debugging,
                ".WikiTom().flush() bad node for discard; r=" + avoid
                + " / " + q.length,
                3,
                this);
      }
   } else {
      mw.log(WSTM.debugging,
             ".WikiTom().flush() no child to discard; r=" + avoid,
             3,
             this);
   }
};   // .o.WikiTom().flush()



WTOM.prototype.focus  =  function (assign) {
   // Access particular child
   // Precondition:
   //    assign  -- child number
   // Postcondition:
   //    Returns WikiTom;  or  false if failed
   // Uses:
   //    >  this.children
   // 2012-03-25 PerfektesChaos@de.wikipedia
   var r  =  false;
   if (this.children) {
      if (assign < this.children.length) {
         r  =  this.children[assign];
      }
   } else if (assign) {
      mw.log(WSTM.debugging,
             ".WikiTom().focus()  has no children",
             3,
             this);
   } else {
      r  =  this;
   }
   return r;
};   // .o.WikiTom().focus()



WTOM.prototype.fold  =  function (assign, align, append, allow) {
   // Split child with searchable string content
   // Precondition:
   //    assign  -- child number
   //    align   -- position where to split
   //    append  -- true: insert after;  false: insert before
   //    allow   -- true: permit initial string separation
   // Postcondition:
   //    Returns inserted WikiTom, even if string is empty;
   //            or  false if failed
   // Uses:
   //    >  this.source
   //    >  this.children
   //    mw.log()
   // 2012-07-07 PerfektesChaos@de.wikipedia
   var r  =  false,
       s;
   if (this.children) {
      if (assign < this.children.length) {
         if (align) {
            r  =  this.children[assign];
            if (r.children) {
               r  =  false;
               mw.log(WSTM.debugging,
                      ".WikiTom().fold() no string node: " + assign,
                      3,
                      this);
            } else {
               s  =  r.source;
               if (align <= s.length) {
                  if (append) {
                     r.source  =  s.substr(0, align);
                     s         =  s.substr(align);
                  } else {
                     this.children[assign].source  =  s.substr(align);
                     s         =  s.substr(0, align);
                  }
               } else {
                  r  =  false;
                  mw.log(WSTM.debugging,
                         ".WikiTom().fold() > string end: " + align,
                         3,
                         this);
               }
            }
         } else {
            s  =  "";
         }
         if (r) {
            r  =  new WSTM.o.WikiTom(s, this);
            this.children.splice((append ? assign+1 : assign),  0,  r);
            r.learnt  =  this.learnt;
         }
      } else {
         mw.log(WSTM.debugging,
                ".WikiTom().fold() invalid child number: " + assign,
                3,
                this);
      }
   } else if (allow) {
      this.children  =  [ new WSTM.o.WikiTom(this.source, this) ];
   } else {
      mw.log(WSTM.debugging,
             ".WikiTom().fold()  has no children",
             3,
             this);
   }
   return r;
};   // .o.WikiTom().fold()



WTOM.prototype.folder  =  function (ahead, assign, after, adjacent) {
   // Subdivide separated section, even over multiple children
   // Precondition:
   //    ahead     -- begin position within assign
   //    assign    -- child number of begin
   //    after     -- end position within adjacent
   //    adjacent  -- child number of end
   // Postcondition:
   //    Returns inserted WikiTom
   // Uses:
   //    >  this.source
   //    >  this.children
   //    this.fold()
   //    mw.log()
   // 2012-03-18 PerfektesChaos@de.wikipedia
   var beg,
       end,
       r      =  false,
       i,
       later,
       n,
       s;
   if (this.children) {
      if (adjacent < assign  ||  adjacent > this.children.length) {
         mw.log(WSTM.debugging,
                ".WikiTom().folder() bad nodes for string; b="
                + assign + " e=" + adjacent
                + " / " + this.children.length,
                3,
                this);
      } else {
         r  =  true;
         for (i = assign;  i <= adjacent;  i++) {
            if (this.children[i].children) {
               mw.log(WSTM.debugging,
                      ".WikiTom().folder() no string: b=" + assign,
                      3,
                      this);
               r  =  false;
               break;   // for i
            }
         }   // for i
         if (r) {
            beg  =  false;
            end  =  false;
            n    =  adjacent;
            if (after) {
               r  =  this.fold(adjacent, after, false);
               if (r) {
                  end  =  r;
                  n++;
               }
            }
         }
         if (r) {
            i  =  assign;
            if (ahead) {
               r  =  this.fold(assign, ahead, true);
               if (r) {
                  beg  =  r;
                  end  =  false;
                  i++;
                  n++;
               }
            }
         }
         if (r) {
            s  =  "";
            if (end) {
               s  =  end.source;
            }
            if (i < n) {
               for (n = n-1;  n > i;  n--) {
                  s  =  this.children[i].toString() + s;
               }   // for i
               this.children.splice(i,  n - i - 1);
            }
            if (beg) {
               s  =  beg.source + s;
            }
            this.children[i].source  =  s;
            r                        =  this.children[i];
         }
      }
   } else {
      if (assign || adjacent) {
         mw.log(WSTM.debugging,
                ".WikiTom().folder() bad nodes for string b="
                + assign + " e=" + adjacent,
                3,
                this);
      } else {
         later  =  (after < this.source.length);
         n      =  (ahead ? 2 : 1)  +  (later ? 1 : 0);
         this.children  =  new Array(n);
         i              =  0;
         if (ahead) {
            s                 =  this.source.substr(0, ahead);
            this.children[0]  =  new WSTM.o.WikiTom(s, this);
            i                 =  1;
         }
         s                 =  this.source.substr(ahead,  after - ahead);
         this.children[i]  =  new WSTM.o.WikiTom(s, this);
         r                 =  this.children[i];
         if (later) {
            i++;
            s                 =  this.source.substr(after);
            this.children[i]  =  new WSTM.o.WikiTom(s, this);
         }
         if (this.learnt) {
            for (i = 0;  i < n;  i++) {
               this.children[i].learnt  =  true;
            }   // for i
         }
      }
   }
   return r;
};   // .o.WikiTom().folder()



WTOM.prototype.fork  =  function (at, advance, ancestor, assign, attach) {
   // Introduce deeper level
   // Precondition:
   //    at        -- child number of begin
   //    advance   -- child number of end;  advance >= at
   //    ancestor  -- name of family (tag etc.)
   //    assign    -- family mode
   //    attach    -- true: 'include' tag
   // Postcondition:
   //    Nodes from at until advance (including) created at parent at.
   //    Returns parent, if succeeded, else false
   // Uses:
   //    >< this.children
   //    >< this.parent
   //     < this.mode
   //     < this.scope
   //     < this.source
   //    mw.log()
   // 2012-04-14 PerfektesChaos@de.wikipedia
   var i,
       r  =  false;
   if (this.children) {
      r           =  new WSTM.o.WikiTom(false, this);
      r.children  =  this.children.slice(at,  advance + 1);
      r.mode      =  assign;
      r.scope     =  ancestor;
      for (i = 0;  i < r.children.length;  i++) {
         r.children[i].parent  =  r;
         if (attach) {
            r.children[i].include  =  true;
         } else {
            r.children[i][ancestor]  =  true;
         }
      }   // for i
      this.children[at]  =  r;
      if (advance > at) {
         this.children.splice(at + 1,  advance - at);
      }
      if (at) {
         delete r.source;
      }
      r.parent  =  this;
   } else {
      mw.log(WSTM.debugging, ".WikiTom().fork() no children", 3, this);
   }
   return r;
};   // .o.WikiTom().fork()



WTOM.prototype.free  =  function () {
   // Replace parents of children by this parent
   // Uses:
   //    >  this.children
   //    >  this.parent
   // 2012-04-22 PerfektesChaos@de.wikipedia
   var e,
       i,
       n;
   if (this.children) {
      n  =  this.children.length;
      for (i = 0;  i < n;  i++) {
         e  =  this.children[i];
         e.free();
         e.parent  =  this;
      }   // for i
   }
};   // .o.WikiTom().free()



WTOM.prototype.fresh  =  function (apply) {
   // Mark this and above nodes as modified
   // Precondition:
   //    apply  -- optional string to be stored
   // Postcondition:
   //    nodes are marked.
   // Uses:
   //    >  this.children
   //    >  this.parent
   //     < this.source
   //     < this.learnt
   //    this.fresh()   -- recursive
   // 2012-03-23 PerfektesChaos@de.wikipedia
   this.learnt  =  true;
   if (this.children) {
      this.source  =  false;
   } else if (apply) {
      this.source  =  apply;
   } else if (typeof(apply) === "string") {
      this.source  =  "";
   }
   if (this.parent) {
      this.parent.fresh(false);
   }
};   // .o.WikiTom().fresh()



WTOM.prototype.getCount  =  function () {
   // How many nodes?
   // Postcondition:
   //     Return current node count.
   // Uses:
   //    >  this.children
   // 2012-03-19 PerfektesChaos@de.wikipedia
   var r  =  0;
   if (this.children) {
      r  =  this.children.length;
   }
   return r;
};   // .o.WikiTom().getCount()



WTOM.prototype.isNodeInSpan  =  function () {
   // Is this node within <include>...</include> span?
   // Postcondition:
   //    Returns true, if anode within span, else false
   // Uses:
   //    >  .w.encountered.include
   // 2012-04-06 PerfektesChaos@de.wikipedia
   var r  =  false;
   if (WSTM.w.encountered.include) {
      r  =  (this.include ? true : false);
   }
   return r;
};   // .o.WikiTom().isNodeInSpan()



WTOM.prototype.replace  =  function (apply, assign) {
   // Precondition:
   //    apply   -- RegExp object,  or Array of replacement pairs
   //    assign  -- single replacement,  or false if replacement pairs
   // Postcondition:
   //    Nodes are modified
   // Uses:
   //    >  this.children
   //    >  this.lookup
   //    >  this.mode
   //    >  .o.WikiTom.TextOnly
   //    >< this.source
   //    this.replace()   -- recursive
   //    .util.translate.flip()
   //    this.fresh()
   // 2019-08-01 PerfektesChaos@de.wikipedia
   var i,
       n,
       s;
   if (this.lookup) {
      if (this.children) {
         n  =  this.children.length;
         for (i = 0;  i < n;  i++) {
            this.children[i].replace(apply, assign);
         }   // for i
      } else {
         if (this.mode <= WTOM.TextOnly) {
            s  =  this.source;
            if (s) {
               if (typeof(assign) === "string") {
                  n  =  s.replace(apply, assign);
               } else {
                  n  =  WSTM.util.translate.flip(s, apply);
               }
               if (n !== s) {
                  this.fresh(n);
               }
            }
         }
      }
   }
};   // .o.WikiTom().replace()



WTOM.prototype.toString  =  function () {
   // Postcondition:
   //    Returns string
   // Uses:
   //    >  this.source
   //    >  this.children
   //    >  this.learnt
   //    this.toString()
   // 2012-05-16 PerfektesChaos@de.wikipedia
   var i,
       n,
       p,
       r  =  this.source;
   if (this.children) {
      if (this.learnt  ||  ! r) {
         n  =  this.children.length;
         r  =  "";
         for (i = 0;  i < this.children.length;  i++) {
            p  =  this.children[i];
            if (p) {
//             if (p.hasOwnProperty("toString")) {   /// failed
               if (p.toString) {
                  r  =  r + p.toString();
               }
            }
         }   // for i
      }
   } else if (! r) {
      r  =  "";
   }
   return r;
};   // .o.WikiTom().toString()



WTOM.prototype.trimL  =  function (any) {
   // Trim leftmost node from heading spacing charcodes of any kind
   // Precondition:
   //    any  -- true: remove also zero width and direction marks
   // Postcondition:
   //    Returns true, if whitespace removed
   //    leftmost node modified, if whitespace
   // Uses:
   //    >  this.source
   //    >  this.children
   //    >  .o.WikiTom().lookup
   //    >  .o.WikiTom().mode
   //    >  .o.WikiTom.TextOnly
   //    .str.trimL()
   //    this.fresh()
   // 2013-04-29 PerfektesChaos@de.wikipedia
   var n,
       r  =  false,
       s  =  false;
   if (this.children) {
      n  =  this.children[0];
      if (n.lookup) {
         if (n.mode <= WTOM.TextOnly) {
            if (n.source) {
               s  =  n.source;
            }
         }
      }
   } else if (this.source) {
      s  =  this.source;
   }
   if (s) {
      n  =  s.length;
      s  =  WSTM.str.trimL(s, any);
      r  =  (s.length < n);
      if (r) {
         if (this.children) {
            this.children[0].fresh(s);
         } else {
            this.fresh(s);
         }
      }
   }
   return r;
};   // .o.WikiTom().trimL()



WTOM.prototype.trimR  =  function (any, also) {
   // Trim rightmost node from trailing spacing charcodes of any kind
   // Precondition:
   //    any   -- true: include zero width and direction marks
   //    also  -- true: remove also line break
   // Postcondition:
   //    Returns true, if whitespace removed
   //    rightmost node modified, if whitespace
   // Uses:
   //    >  this.source
   //    >  this.children
   //    >  .o.WikiTom().lookup
   //    >  .o.WikiTom().mode
   //    >  .o.WikiTom.TextOnly
   //    .str.trimR()
   //    .str.substrEnd()
   //    this.fresh()
   // 2012-05-28 PerfektesChaos@de.wikipedia
   var n,
       p,
       r  =  false,
       s  =  false;
   if (this.children) {
      p  =  this.children[this.children.length - 1];
      if (p.lookup) {
         if (p.mode <= WTOM.TextOnly) {
            if (p.source) {
               s  =  p.source;
            }
         }
      }
   } else if (this.source) {
      s  =  this.source;
   }
   if (s) {
      n  =  s.length;
      s  =  WSTM.str.trimR(s, any, also);
      r  =  (s.length < n);
      if (r) {
         if (this.children) {
            p.fresh(s);
         } else {
            this.fresh(s);
         }
      }
   }
   return r;
};   // .o.WikiTom().trimR()



WTOM.prototype.DEBUGparent  =  function () {
   // Replace parent objects by brief string for debugging report
   // Uses:
   //    >  this.parent
   //    >  this.children
   //    this.toString()
   // 2012-04-22 PerfektesChaos@de.wikipedia
   var e,
       i,
       n;
   if (this.parent) {
      this.parent  =  this.parent.toString().substr(0, 100);
   }
   if (this.children) {
      n  =  this.children.length;
      for (i = 0;  i < n;  i++) {
         e  =  this.children[i].DEBUGparent();
      }   // for i
   }
};   // .o.WikiTom().DEBUGparent()



};   // .bb.WikiTom()
mw.libs.WikiSyntaxTextMod.bb.WikiTom(mw.libs.WikiSyntaxTextMod);
delete mw.libs.WikiSyntaxTextMod.bb.WikiTom;



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



( function ( WSTM ) {
   "use strict";
   var sub      =  "O",
       self     =  WSTM.o.self,
       version  =  WSTM.o.vsn,
       rls;
   if ( typeof WSTM.main  ===  "object"
        &&     WSTM.main   &&
        typeof WSTM.main.wait  ===  "function" ) {
      // Start on import: callback to waiting ...
      WSTM.main.wait( sub, version );
   } else if ( typeof mw.loader  ===  "object"   &&
               typeof mw.hook  !==  "undefined" ) {
      rls = { };
      rls[ self ] = "ready";
      mw.loader.state( rls );
      mw.hook( "WikiSyntaxTextMod/" + sub + ".ready" )
        .fire( [ sub, version ] );
   }
} ( mw.libs.WikiSyntaxTextMod ) );



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

/// EOF </nowiki>   WikiSyntaxTextMod/dO.js