Lompat ke isi

Modul:twoimage

Ḍâri Wikikamus

--[===[

MODULE "TWOIMAGE" (two images)

"eo.wiktionary.org/wiki/Modulo:twoimage" <!--2025-Jul-19-->
"id.wiktionary.org/wiki/Modul:twoimage"

Purpose: places one image or two images on the right side of the screen,
         with description, two images are arranged horizontally

Utilo: lokigas unu bildon aux du bildojn dekstre sur la ekrano,
       kun priskribo, du bildoj estas arangxitaj horizontale

Manfaat: meletakkan satu atau dua gambar di tepi kanan layar,
         dengan deskripsi ...

Syfte: placerar en eller tvaa bilder paa hoeger sida av skaermen,
       med beskrivning, tvaa bilder anordnas horisontellt

Used by templates / Uzata far sxablonoj /
Digunakan oleh templat / Anvaend av mallar:
* bildodek (EO) | gamkan (ID)

Required submodules / Bezonataj submoduloj / Submodul yang diperlukan:
* none

This module can accept parameters whether sent to itself (own frame) or
to the parent (parent's frame). If there is a parameter "parentframe=true"
on the own frame then that own frame is discarded in favor of the
parent's one.

Incoming: * 1...4 anonymous parameters
            * a) (1) image name
            * b) (2) image name and description
            * c) (2) two image names
            * d) (3) two image names and one description
            * e) (4) image name, description, image name, description
            Same rules apply to both image names and descriptions:
            * must be 2...160 octet:s long
            * must not contain double rectangular
              brackets [[ ]] {{ }} nor "<center>" nor "[http"
              nor strip markers resulting from some tags
          * 1 named parameter "duon=1" or "half=1" (acceptance
              depends on site language, reduces lengths by factor ca
              1.5 and areas by factor ca 2.25)
          * 3 hidden named parameters
            * "pagenameoverridetestonly="
            * "nsnumberoverridetestonly="
            * "nocat=true"  !!!FIXME!!! deprecated

Returned: * one string with a HTML table or inline error message

Note that it is indeed possible to feed in {{ }} by means of
  {<noinclude/>{
otherwise the inner template gets expanded first.

To distinguish cases b) and c) both with 2 parameters the latter
parameter is analyzed in the following way:

if (less than 7 octet:s) then
  assume b)
  done
endif
loop (walk back from the end copying into a new string writing backwards too)
  if (length of copied text is 5) then
    assume b)
    exit loop and done
  endif
  pick char
  if (dot found) then
    exit loop
  endif
  if (other value than ASCII letter found) then
    assume b)
    exit loop and done
  endif
  keep UPPERcase, change lowercase to UPPERcase
  store char into new string backwards
endloop
if (new string is equal PNG BMP GIF JPG JPEG SVG OGV WEBM) then
  assume c)
else
  assume b)
endif

The 5 input cases map onto 3 possible output cases and tables:

b) one image (two table rows each with one cell)
d) two images with a common description (two table rows, the upper one
   with two cells and the lower one with "colspan=2")
e) two images with two separate descriptions (2 table rows,
   each with 2 cells)

If no description is provided then {{PAGENAME}} is seized,
and a) maps to b) and c) maps to d).

The image syntax fed to the wiki parser used (see also string "strsizes") is
  [[File: (name) |center| (size) ]]
thus we do not use the description parameter. The reasons for doing so are:
* allow for a common description for the case d)
* avoid risk that a description like "999px" or similar breaks the layout
* reserve sufficient horizontal space for the text even if the image
  is high and very narrow

One image is showed with "size" parameter "240x240px" or reduced "160x160px",
two images with "240x160px" or reduced "160x120px".

]===]

local exporttable = {}

require('strict')

-- ***********************
-- *    CONSTANTS [O]    * ---------------------------------------------
-- ***********************

-- surrogate transcoding table (only needed for EO)

local contabtransluteo = {}
  contabtransluteo[ 67] = 0xC488 -- CX
  contabtransluteo[ 99] = 0xC489 -- cx
  contabtransluteo[ 71] = 0xC49C -- GX
  contabtransluteo[103] = 0xC49D -- gx
  contabtransluteo[ 74] = 0xC4B4 -- JX
  contabtransluteo[106] = 0xC4B5 -- jx
  contabtransluteo[ 83] = 0xC59C -- SX
  contabtransluteo[115] = 0xC59D -- sx
  contabtransluteo[ 85] = 0xC5AC -- UX breve
  contabtransluteo[117] = 0xC5AD -- ux breve

-- constant strings (error circumfixes)

local contabfel = {}
contabfel.mibg = '<span class="error">'     -- mini whining begin
contabfel.labg = '<b>' .. contabfel.mibg    -- lagom whining begin
contabfel.hubg = '<big>' .. contabfel.labg  -- huge whining begin
contabfel.mien = '</span>'                  -- mini whining end
contabfel.laen = contabfel.mien .. '</b>'   -- lagom whining end
contabfel.huen = contabfel.laen .. '</big>' -- huge whining end

  -- uncommentable EO vs ID constant table (error messages)

  -- #E02...#E98, holes permitted
  -- note that #E00 and #E01 are NOT supposed to be included here

  local contaberaroj = {}
      -- contaberaroj[ 8] = 'Erara uzo de %@, legu gxian dokumentajxon'                                                           -- EO #E08 number of params
        contaberaroj[ 8] = 'Penggunaan salah %@, bacalah dokumentasinya'                                                       -- ID #E08
      -- contaberaroj[ 9] = 'Erara uzo de %@ pro transdonita signocxeno, estu 2...160 bitokoj'                                    -- EO #E09 length of param
        contaberaroj[ 9] = 'Penggunaan salah %@ oleh karena string datang, sebaiknya 2...160 oktet'                            -- ID #E09
      -- contaberaroj[10] = 'Erara uzo de %@ pro nomspaco, estu 0 (kapvorto) aux 4 aux 102 (Aldono:)'                             -- EO #E10
        contaberaroj[10] = 'Penggunaan salah %@ oleh karena ruang nama, sebaiknya 0 (lema) atau 4 atau 102 (Lampiran:)'        -- ID #E10
      -- contaberaroj[14] = 'Erara uzo de %@ cxar en transdonita signocxeno trovita problemhava signaro'                          -- EO #E14
        contaberaroj[14] = 'Penggunaan salah %@ bahwa dalam string datang ditemukan karakter bermasalah'                       -- ID #E14
      -- contaberaroj[15] = 'Erara uzo de %@ cxar en transdonita signocxeno trovita "strip marker" pro <gallery> aux simile'      -- EO #E15
        contaberaroj[15] = 'Penggunaan salah %@ bahwa dalam string datang ditemukan "strip marker" oleh karena <gallery> dsb'  -- ID #E15

  -- constant table (HTML)

  -- note that we indeed need "clear:right", not "clear:both" !!!

local contabhtml = {}
  contabhtml.matabg = '<table style="clear:right; float:right; margin:0 0 0.3em 0.3em; padding:0.2em; border:2px solid #909090;">'
  contabhtml[1] = '<td style="padding:0.2em;">' -- cell with image
  contabhtml[2] = '<td style="padding:0.2em; text-align:center;">' -- cell with text
  contabhtml[3] = '<td style="padding:0.2em; text-align:center;" colspan="2">' -- cell with common description
  contabhtml.minend = '</table>'

  -- uncommentable EO vs ID constant table (cat:s)

local contabkatoj = {}
		contabkatoj = {"Kaca lèma bhâreng ghâmbhâr", 'Pelataran alèr komus kalabân ghâmbhâr', 'Pangangghuyân klèro templat (gamkan)'}  -- MAD
		contabkatoj[4] = 'Pangangghuyân klèro templat'                 -- MAD
		contabkatoj[7] = 'Tag manual (br) ghâbây templat (gamkan)'     -- MAD
		contabkatoj[8] = 'Dèskripsi lanjhâng ghâbây templat (gamkan)'  -- MAD

  -- constant table (3 integers for preliminary parameter check)

  local contabparam = {}
  contabparam[0] = 1   -- minimal number of anon parameters
  contabparam[1] = 4   -- maximal number of anon parameters
  contabparam[2] = 200 -- maximal length of single para (min is hardcoded ONE)

-- uncommentable (override)

-- * name of table MUST always be defined, OTOH elements are usually NOT
-- * for testing only, values automatically peeked otherwise

local contabovrd = {}
  -- contabovrd.sitelang = 'eo'                                     -- "en"
  -- contabovrd.sitelang = 'mad'
  -- contabovrd.katprefi = 'Kategorio'                              -- "Category"
  -- contabovrd.katprefi = 'Bhângsa'
  -- contabovrd.apxprefi = 'Aldono'                                 -- "Appendix"
  -- contabovrd.apxprefi = 'Alèr'  -- the namespace currently does NOT exist om MAD wiktionary https://phabricator.wikimedia.org/T218796

-- ****************************************
-- *    SPECIAL STUFF OUTSIDE MAIN [B]    * ----------------------------
-- ****************************************

  -- SPECIAL VAR:S

local qbooguard = false  -- only for the guard test, pass to other var ASAP

------------------------------------------------------------------------

---- MATH PROCEDURES [E] ----

------------------------------------------------------------------------

local function mathisintrange (numzjinput, numzjmin, numzjmax)
  local booisclean = false -- preASSume guilt
  if (type(numzjinput)=='number') then -- no non-numbers, thanks
    if (numzjinput==math.floor(numzjinput)) then -- no transcendental
      booisclean = ((numzjinput>=numzjmin) and (numzjinput<=numzjmax)) -- rang
    end--if
  end--if
  return booisclean
end--function mathisintrange

local function mathdiv (xdividens, xdivisero)
  local resultdiv = 0 -- DIV operator lacks in LUA :-(
  resultdiv = math.floor (xdividens / xdivisero)
  return resultdiv
end--function mathdiv

local function mathmod (xdividendo, xdivisoro)
  local resultmod = 0 -- MOD operator is "%" and bitwise AND operator lack too
  resultmod = xdividendo % xdivisoro
  return resultmod
end--function mathmod

------------------------------------------------------------------------

---- NUMBER CONVERSION PROCEDURES [N] ----

------------------------------------------------------------------------

local function prnnumto2digit (numzerotoninetynine)

-- Convert integer 0...99 to decimal ASCII string always 2 digits "00"..."99".

-- Depends on procedures :
-- [E] mathisintrange mathdiv mathmod

  local strtwodig = '??' -- always 2 digits
  if (mathisintrange(numzerotoninetynine,0,99)) then
    strtwodig = tostring(mathdiv(numzerotoninetynine,10)) .. tostring(mathmod(numzerotoninetynine,10))
  end--if
  return strtwodig
end--function prnnumto2digit

-- *****************************************
-- *    LOW LEVEL STRING PROCEDURES [G]    * ---------------------------
-- *****************************************

-- test whether char is an ASCII uppercase letter, return boolean

local function lfgtestuc (numkode)
  local booupperc = false
  booupperc = ((numkode>=65) and (numkode<=90))
  return booupperc
end--function lfgtestuc

------------------------------------------------------------------------

-- test whether char is an ASCII lowercase letter, return boolean

local function lfgtestlc (numcode)
  local boolowerc = false
  boolowerc = ((numcode>=97) and (numcode<=122))
  return boolowerc
end--function lfgtestlc

------------------------------------------------------------------------

local function prgstringrange (varvictim, nummini, nummaxi)

  local nummylengthofstr = 0
  local booveryvalid = false -- preASSume guilt
  if (type(varvictim)=='string') then
    nummylengthofstr = string.len(varvictim)
    booveryvalid = ((nummylengthofstr>=nummini) and (nummylengthofstr<=nummaxi))
  end--if
  return booveryvalid
end--function prgstringrange

------------------------------------------------------------------------

---- UTF8 PROCEDURES [U] ----

------------------------------------------------------------------------

-- Local function LFULNUTF8CHAR

-- Evaluate length of a single UTF8 char in octet:s.

-- Input  : * numbgoctet  -- beginning octet of a UTF8 char

-- Output : * numlen1234x

-- Does NOT thoroughly check the validity, looks at ONE octet only.

local function lfulnutf8char (numbgoctet)
  local numlen1234x = 0
    if (numbgoctet<128) then
      numlen1234x = 1 -- $00...$7F -- ANSI/ASCII
    end--if
    if ((numbgoctet>=194) and (numbgoctet<=223)) then
      numlen1234x = 2 -- $C2 to $DF
    end--if
    if ((numbgoctet>=224) and (numbgoctet<=239)) then
      numlen1234x = 3 -- $E0 to $EF
    end--if
    if ((numbgoctet>=240) and (numbgoctet<=244)) then
      numlen1234x = 4 -- $F0 to $F4
    end--if
  return numlen1234x
end--function lfulnutf8char

------------------------------------------------------------------------

-- Local function LFULNUTF8STRING

-- Evaluate length of a string in UTF8 char:s.

-- Input  : * strutf8masu6k -- empty is useless but cannot cause major harm

-- Output : * numpanjan6g -- unit UTF8 char, -1 if invalid

-- Depends on procedures :
-- [U] lfulnutf8char

-- Does NOT thoroughly check the validity, looks at ONE octet
-- of every char only.

local function lfulnutf8string (strutf8masu6k)

  local numpanjan6g = 0 -- unit UTF8 char, becomes length of string
  local numdepoin = 0 -- unit octet, ZERO-based index
  local numoktle6n = 0
  local numbeg6val = 0
  local numchrlen = 0

  numoktle6n = string.len(strutf8masu6k) -- limit

  while true do
    if (numdepoin>numoktle6n) then
      numpanjan6g = -1 -- truncated -> invalid
    end--if
    if (numdepoin>=numoktle6n) then
      break -- good or bad
    end--if
    numbeg6val = string.byte(strutf8masu6k,(numdepoin+1),(numdepoin+1))
    numchrlen = lfulnutf8char (numbeg6val)
    if (numchrlen==0) then
      numpanjan6g = -1
      break -- broken char -> invalid
    end--if
    numdepoin = numdepoin + numchrlen -- step 1...4 -- can overflow
    numpanjan6g = numpanjan6g + 1 -- unit octet, step ONE
  end--while

  return numpanjan6g

end--function lfulnutf8string

------------------------------------------------------------------------

-- Local function LFUELEKTU

-- Called only from lfucututf8byspc.

local function lfuelektu (numpozzia, numdiista, numpozzib, numdiistb)
  local nummynresult = -1 -- preASSume guilt
  if ((numpozzia~=-1) and (numpozzib==-1)) then
    nummynresult = numpozzia
  end--if
  if ((numpozzia==-1) and (numpozzib~=-1)) then
    nummynresult = numpozzib
  end--if
  if ((numpozzia~=-1) and (numpozzib~=-1)) then
    if (numdiista<numdiistb) then
      nummynresult = numpozzia
    else
      nummynresult = numpozzib
    end--if
  end--if
  return nummynresult
end--function lfuelektu

------------------------------------------------------------------------

-- Local function LFUCUTUTF8BYSPC

-- Find position for very clean UTF8 cutting between words (NOT in the middle
-- of a word, even less middle of char) by incoming UTF8 char position.

-- Input  : * strutf8masu9k -- at least 10 octet:s, shorter rejected
--          * numtheopos -- unit UTF8 char, theoretical position (usually a
--                          fraction of value obtained from lfulnutf8string,
--                          at least 2 char:s away from begin and 2 char:s
--                          away from end)
--          * nummaxdist -- unit UTF8 char, maximal distance from theoretical
--                          position to found boundary (min 2, max 20, and
--                          at most 1/4 of the length of the incoming string
--                          measured in UTF8 char:s)

-- Output : * numcutpos -- best position, unit octet, -1 if failed

-- Depends on procedures :
-- [U] lfulnutf8char
-- [U] lfuelektu (strictly internal, reserved for us)

-- Does NOT thoroughly check the validity, looks at ONE octet
-- of every char only.

-- Cutting priorities:
-- * on a space (returned position AT the space, caller probably will want
--   to suppress the space if replaced by EOL)
-- * on tuation "+,-./:;" (returned position AFTER the found char, caller
--   probably will NOT want to suppress it)
-- * anywhere between 2 UTF8 char:s
-- Thus we can happen to suggest cutting in the middle of a word in
-- worst case, but never in the middle of a UTF8 char.

-- We care about different lengths of UTF8 char:s, but not about
-- more. For useful results, the incoming string MUST NOT contain:
-- * combining decorations
-- * invisible ZWSP-spaces, RTL-marks, BOM-marks
-- * wikilinks
-- * HTML tags or HTML comments (for <i> and <b> see below)
-- * repetitive spaces
-- We do not care about bold and italic either, but limited use (say up to one
-- occurrence per 20 UTF8 char:s) will not have a remarkable negative impact.

-- There are some peculiarities when registering promising boundaries.
-- * BEFORE the theoretical position we register EVERY HIT, overwriting
--   a possible previous one, and resetting distance to ZERO
-- * AFTER the theoretical position we register the EARLIEST HIT ONLY, and
--   skip possible later hits, and writing in the only final distance
--   from a separate variable
-- * BEFORE the theoretical position we increase the distance for
--   entries we have ALREADY found, but if we exceed "nummaxdist" then
--   we reinvalidate the position
-- * AFTER the theoretical position we increase a separate variable that
--   we maybe write in on hit

local function lfucututf8byspc (strutf8masu9k, numtheopos, nummaxdist)

  local tabposisi = { [0]= -1,-1,-1,-1} -- unit octet
  local tabdistan = { [0]=  0, 0, 0, 0} -- unit UTF8 char

  local numcutpos = -1 -- preASSume guilt, unit octet, the final result
  local numpanjan9g = 0 -- unit UTF8 char, grow to numtheopos plus tiny margin
  local numpo9in = 0 -- unit octet, ZERO-based index
  local numoktle9n = 0
  local numbeg9val = 0
  local numchr9len = 0
  local numoffset = 0 -- ZERO for preceding | 2 for passed
  local numdistafter = 0 -- unit UTF8 char, BEFORE: keep ZERO | AFTER: grow

  local boospejs = false
  local bootuation = false
  local boopassed = false -- set to true after theoretical position passed
  local boodoone = false -- true if analyzing loop failed or fully succeeded

  numoktle9n = string.len(strutf8masu9k) -- limit

  while true do

    if ((numoktle9n<10) or (numtheopos<2) or (nummaxdist<2) or (nummaxdist>20) or (numpo9in>numoktle9n)) then
      numcutpos = -1
      boodoone = true -- too short string or ... or trunc in middle of char
      break
    end--if
    if (numpo9in==numoktle9n) then
      if ((numpanjan9g<(numtheopos+2)) or (numpanjan9g<(nummaxdist*4))) then
        numcutpos = -1
        boodoone = true -- too close to end of string or "nummaxdist" too high
      end--if
      break -- done or not
    end--if

    numbeg9val = string.byte(strutf8masu9k,(numpo9in+1),(numpo9in+1))
    numchr9len = lfulnutf8char (numbeg9val)
    if (numchr9len==0) then
      numcutpos = -1
      boodoone = true -- broken char -> invalid
      break
    end--if

    boospejs = (numbeg9val==32)
    bootuation = ((numbeg9val>=43) and (numbeg9val<=47)) or (numbeg9val==58) or (numbeg9val==59)

    if (numpanjan9g==numtheopos) then
      numcutpos = numpo9in -- unit octet, maybe will get replaced later
      if (boospejs) then
        boodoone = true
        break -- yeah the best imaginable outcome
      end--if
    end--if

    if ((not boopassed) and (tabposisi[0]~=-1)) then
      tabdistan[0] = tabdistan[0] + 1 -- unit UTF8 char
      if (tabdistan[0]>nummaxdist) then -- reinvalidate the position
        tabdistan[0] = 0
        tabposisi[0] = -1
      end--if
    end--if
    if ((not boopassed) and (tabposisi[1]~=-1)) then
      tabdistan[1] = tabdistan[1] + 1 -- unit UTF8 char
      if (tabdistan[1]>nummaxdist) then -- reinvalidate the position
        tabdistan[1] = 0
        tabposisi[1] = -1
      end--if
    end--if

    if (boospejs and ((not boopassed) or (tabposisi[numoffset]==-1))) then
      tabposisi[numoffset] = numpo9in -- unit octet
      tabdistan[numoffset] = numdistafter -- unit UTF8 char
    end--if
    if (bootuation and ((not boopassed) or (tabposisi[1+numoffset]==-1))) then
      tabposisi[1+numoffset] = numpo9in -- unit octet
      tabdistan[1+numoffset] = numdistafter -- unit UTF8 char
    end--if

    if ((numdistafter>nummaxdist) or ((tabposisi[2]~=-1) and (tabposisi[3]~=-1))) then
      break -- nothing to be gained anymore or got all
    end--if

    numpo9in = numpo9in + numchr9len -- step 1...4 -- can overflow
    numpanjan9g = numpanjan9g + 1 -- unit octet, step ONE

    boopassed = (numpanjan9g>=numtheopos)
    if (boopassed) then
      numoffset = 2
      numdistafter = numdistafter + 1
    end--if

  end--while

  if (not boodoone) then -- try to buy space
    numoffset = lfuelektu (tabposisi[0],tabdistan[0],tabposisi[2],tabdistan[2])
    if (numoffset~=-1) then
      numcutpos = numoffset -- succeeded to buy space
      boodoone = true -- prefer space over tuation
    end--if
  end--if

  if (not boodoone) then -- try to buy tuation
    numoffset = lfuelektu (tabposisi[1],tabdistan[1],tabposisi[3],tabdistan[3])
    if (numoffset~=-1) then
      numcutpos = numoffset + 1 -- succeeded to buy tuation
    end--if
  end--if

  return numcutpos

end--function lfucututf8byspc

------------------------------------------------------------------------

---- HIGH LEVEL STRING PROCEDURES [I] ----

------------------------------------------------------------------------

local function pricheckforbabr (strinputstring,numminlen,nummaxlen)

-- Check whether a parameter is illegal.

-- Output : * numerarko -- non-ZERO on error

-- Note that we tolerate "<br>" as well as double and triple apos:s here.

  local numlongoo     = 0
  local numerarko     = 0 -- ZERO OK | 9 len | 14 bad generic | 15 strip
  local boobrottsligt = false

  while true do -- fake loop

    if ((type(numminlen)=='number') and (type(nummaxlen)=='number')) then
      if (numminlen<nummaxlen) then
        numlongoo = string.len (strinputstring)
        if ((numlongoo<numminlen) or (numlongoo>nummaxlen)) then -- !!!FIXME!!! use prgstringrange and rework length checks
          numerarko = 9
          break -- to join mark
        end--if
      end--if
    end--if

    boobrottsligt = (string.find(strinputstring,"  ",1,true)~=nil) -- multiple spaces
    boobrottsligt = boobrottsligt or (string.find(strinputstring,"[[",1,true)~=nil) -- wikilinks
    boobrottsligt = boobrottsligt or (string.find(strinputstring,"]]",1,true)~=nil) -- wikilinks
    boobrottsligt = boobrottsligt or (string.find(strinputstring,"{{",1,true)~=nil) -- possible
    boobrottsligt = boobrottsligt or (string.find(strinputstring,"}}",1,true)~=nil) -- possible
    boobrottsligt = boobrottsligt or (string.find(strinputstring,"<center>",1,true)~=nil) -- !!!FIXME!!! this is weak
    boobrottsligt = boobrottsligt or (string.find(strinputstring,"<centre>",1,true)~=nil)
    boobrottsligt = boobrottsligt or (string.find(strinputstring,"<BR>",1,true)~=nil) -- !!!FIXME!!! this is weak
    boobrottsligt = boobrottsligt or (string.find(strinputstring,"</br>",1,true)~=nil)
    boobrottsligt = boobrottsligt or (string.find(strinputstring,"</ br>",1,true)~=nil)
    boobrottsligt = boobrottsligt or (string.find(strinputstring,"<br/>",1,true)~=nil)
    boobrottsligt = boobrottsligt or (string.find(strinputstring,"<br />",1,true)~=nil)
    boobrottsligt = boobrottsligt or (string.find(strinputstring,"[http",1,true)~=nil)

    if (boobrottsligt) then
      numerarko = 14
      break -- to join mark
    end--if

    if (string.find(strinputstring,(string.char(127,39,34,96)..'UNIQ'),1,true)) then -- striptease markers
      numerarko = 15
    end--if

    break -- finally to join mark
  end--while -- fake loop -- join mark

  return numerarko

end--function pricheckforbabr

-- *******************************************
-- *    HIGH LEVEL STRING PROCEDURES [I7]    * -- placeholderism
-- *******************************************

local function pripl2altwre (strbeforfill, numaskikodo, varsupstitu)

-- Process all anon and fixed placeholders "%@" or "%~".

-- Input  : * strbeforfill -- request string with placeholders to be filled
--                            in, no placeholders or empty input is useless
--                            but cannot cause major harm
--          * numaskikodo  -- ASCII code of placeholder type, 64 for "%@" or
--                            126 for "%~"
--          * varsupstitu  -- substitute, either string (same content reused
--                            if multiple placeholders), or ZERO-based table
--                            (with one element per placeholder such as
--                            {[0]="none","neniu"}), length 1...80, further
--                            sanitization must be done elsewhere

-- Output : * strafterfill

-- Depends on procedures :
-- [G] prgstringrange

local varpfiller    = 0  -- risky picking
local strufiller    = '' -- final validated filler
local strafterfill  = ''
local numlenbigtext = 0  -- len of strbeforfill
local numsfrcindex  = 0  -- char index ZERO-based
local numinsrtinde  = 0  -- index in table ZERO-based
local numtecken0d   = 0
local numtecken1d   = 0

  numlenbigtext = string.len (strbeforfill)

  while true do
    if (numsfrcindex>=numlenbigtext) then
      break -- empty input is useless but cannot cause major harm
    end--if
    numtecken0d = string.byte(strbeforfill,(numsfrcindex+1),(numsfrcindex+1))
    numsfrcindex = numsfrcindex + 1 -- INC here
    numtecken1d = 0 -- preASSume none
    if (numsfrcindex<numlenbigtext) then -- pick but do NOT INC
      numtecken1d = string.byte(strbeforfill,(numsfrcindex+1),(numsfrcindex+1))
    end--if
    if ((numtecken0d==37) and (numtecken1d==numaskikodo)) then -- "%@" "%~"
      numsfrcindex = numsfrcindex + 1 -- INC more, now totally + 2
      varpfiller = 0 -- preASSume nothing available
      strufiller = '??' -- preASSume nothing available
      if (type(varsupstitu)=='string') then
        varpfiller = varsupstitu -- take it as-is (length check below)
      end--if
      if (type(varsupstitu)=='table') then
        varpfiller = varsupstitu [numinsrtinde] -- risk of type "nil"
        numinsrtinde = numinsrtinde + 1 -- INC tab index on every placeholder
      end--if
      if (prgstringrange(varpfiller,1,80)) then -- restrict
        strufiller = varpfiller -- now the substitute is finally accepted
      end--if
    else
      strufiller = string.char (numtecken0d) -- no placeholder -> copy octet
    end--if
    strafterfill = strafterfill .. strufiller -- add one of 4 possible cases
  end--while

return strafterfill

end--function pripl2altwre

------------------------------------------------------------------------

---- HIGH LEVEL PROCEDURES [H] ----

------------------------------------------------------------------------

local function prhconstructerar (numerar3code, boopeek3it)

-- Construct partial error message maybe peeking description.

-- Input  : * numerar3code -- 1 ... 98 or 2 ... 98 (resistant against
--                            invalid data type, giving "??" on such)
--          * boopeek3it   -- do peek description #E02...#E98 from table

-- Depends on procedures :
-- [N] prnnumto2digit
-- [E] mathisintrange mathdiv mathmod

-- Depends on constants :
-- * maybe table contaberaroj TWO-based (holes permitted)

-- To be called ONLY from PRHBREWERR4HUNP PRHBREWERR5HUPA
-- PRHBREWERR6SLNP PRHBREWERR7SLPA PRHBREWERR8SUBM PRHBREWERR9DETA.

local vardes3krip = 0
local numbottom3limit = 1
local stryt3sux = '#E'

  if (boopeek3it) then
    numbottom3limit = 2 -- #E01 is a valid code for submodule only
  end--if
  if (mathisintrange(numerar3code,numbottom3limit,98)) then
    stryt3sux = stryt3sux .. prnnumto2digit(numerar3code)
    if (boopeek3it) then
      vardes3krip = contaberaroj[numerar3code] -- risk of type "nil"
      if (type(vardes3krip)=='string') then
        stryt3sux = stryt3sux .. ' ' .. vardes3krip
      else
        stryt3sux = stryt3sux .. ' ??' -- no text found
      end--if
    end--if (boopeek3it) then
  else
    stryt3sux = stryt3sux .. '??' -- no valid error code
  end--if

return stryt3sux

end--function prhconstructerar

------------------------------------------------------------------------

local function prhbrewerr5hupa (numeror5code, strlkller5nm)

-- Brew error sev huge, one line, insertable parent.

-- Input  : * numeror5code -- TWO-based error code 2 ... 98 (resistant
--                            against invalid data type, giving "??" on such)
--          * strlkller5nm -- name of parent (not used and should be type
--                            "nil" if message does NOT contain "%@")

-- Depends on procedures :
-- [H] prhconstructerar
-- [I] pripl2altwre
-- [G] prgstringrange
-- [N] prnnumto2digit
-- [E] mathisintrange mathdiv mathmod

-- Depends on constants :
-- * table contabfel with 2 elements .hubg .huen
-- * table contaberaroj TWO-based (holes permitted)

-- #E02...#E98, note that #E00 #E01 #E99 are NOT supposed to be included here.

local stryt5sux = ''

  stryt5sux = contabfel.hubg .. pripl2altwre(prhconstructerar(numeror5code,true),64,strlkller5nm) .. contabfel.huen

return stryt5sux

end--function prhbrewerr5hupa

------------------------------------------------------------------------

-- Local function LFHSPLITMANAUTO                                               !!!FIXME!!! trim spaces

-- Depends on procedures :
-- [U] lfulnutf8char lfulnutf8string lfucututf8byspc
-- [E] mathdiv

local function lfhsplitmanauto (strdescriiption)
  local strbeginpart = ''
  local numleninutf8chars = 0 -- unit UTF8 char
  local numcuthere = 0 -- unit octet
  local boomanualsplit = false
  boomanualsplit = (string.find(strdescriiption,'<br>',1,true)~=nil)
  if (not boomanualsplit) then
    numleninutf8chars = lfulnutf8string (strdescriiption)
    if (numleninutf8chars>80) then
      numcuthere = lfucututf8byspc(strdescriiption,mathdiv(numleninutf8chars,3),10)
      strbeginpart = string.sub(strdescriiption,1,numcuthere) .. '<br>'
      strdescriiption = string.sub(strdescriiption,(numcuthere+1),-1)
    end--if
    numleninutf8chars = lfulnutf8string (strdescriiption) -- reevaluate !!!
    if ((numleninutf8chars>40) or (strbeginpart~='')) then
      numcuthere = lfucututf8byspc(strdescriiption,mathdiv(numleninutf8chars,2),10)
      strdescriiption = string.sub(strdescriiption,1,numcuthere) .. '<br>' .. string.sub(strdescriiption,(numcuthere+1),-1)
    end--if
    strdescriiption = strbeginpart .. strdescriiption
  end--if
  return strdescriiption
end--function lfhsplitmanauto

------------------------------------------------------------------------

-- Local function LFHTESTFILE

-- Check whether parameter can be a filename ie name of a picture or video.

-- Example:
-- "blackhole.png" len = 13 (positions 1...13 ONE-based)
--  1234567890123  last dot at position 10 (ONE-based)
-- difference 3 (4 would be OK too)
-- seized substring comes from positions 11...13 (difference 2, length 3)

local function lfhtestfile (strparanometer)

  local strsubu = ''
  local numlen = 0
  local numchhar = 0
  local numiindeks = 0
  local boogotcee = false -- "false" is b) and "true" is c)

  while true do -- fake loop
    numlen = string.len (strparanometer)
    if (numlen<7) then
      break -- no way, minimal is "Saw.png" or like that
    end--if
    numiindeks = numlen -- ONE-based counts down
    while true do -- inner loop
      if ((numiindeks+5)==numlen) then
        numiindeks = 0 -- F**K
        break -- inner loop -- no dot found, this is NOT a filename extension
      end--if
      numchhar = string.byte (strparanometer,numiindeks,numiindeks)
      if (numchhar==46) then
        break -- inner loop -- dot is here, do NOT store it
      end--if
      if ((lfgtestuc(numchhar)==false) and (lfgtestlc(numchhar)==false)) then
        numiindeks = 0 -- F**K
        break -- inner loop -- faulty value, this is NOT a filename extension
      end--if
      if (lfgtestlc(numchhar)) then
        numchhar = numchhar - 32 -- make UPPERcase
      end--if
      strsubu = string.char (numchhar) .. strsubu -- ueglstr
      numiindeks = numiindeks - 1 -- ONE-based counts down
    end--while -- inner loop
    if (numiindeks==0) then
      break -- this is NOT a filename extension
    end--if
    boogotcee = ((strsubu=='PNG') or (strsubu=='BMP') or (strsubu=='GIF') or (strsubu=='JPG') or (strsubu=='JPEG') or (strsubu=='SVG') or (strsubu=='OGV') or (strsubu=='WEBM'))
    break -- finally
  end--while -- fake loop

  return boogotcee

end--function lfhtestfile

------------------------------------------------------------------------

local function prikodeosg (streosurr)  -- !!!FIXME!!! move to [I5] and replace

-- Transcode eo X-surrogates to cxapeloj in a single string (eo only).

-- Input  : * streosurr -- ANSI string

-- Output : * strutf8eo -- UTF8 string

-- Depends on procedures :
-- [E] mathdiv mathmod

-- Depends on constants :
-- * table "contabtransluteo" inherently holy

-- To be called ONLY from "prhrecusurrstrtab".

-- * the "x" in a surr pair is case insensitive,
--   for example both "kacxo" and "kacXo" give same result
-- * avoid "\", thus for example "ka\cxo" would get converted but the "\" kept
-- * double "x" (both case insensitive) prevents conversion and becomes
--   reduced to single "x", for example "kacxxo" becomes "kacxo"

  local vareopeek = 0
  local strutf8eo = ''
  local numeoinplen = 0
  local numinpinx = 0 -- ZERO-based source index
  local numknar0k = 0 -- current char
  local numknaf1x = 0 -- next char (ZERO is NOT valid)
  local numknaf2x = 0 -- post next char (ZERO is NOT valid)
  local boonext1x = false
  local boonext2x = false
  local boosudahdone = false

  numeoinplen = string.len(streosurr)

  while true do

    if (numinpinx>=numeoinplen) then
      break
    end--if

    numknar0k = string.byte(streosurr,(numinpinx+1),(numinpinx+1))
    numknaf1x = 0 -- preASSume no char
    numknaf2x = 0 -- preASSume no char
    if ((numinpinx+1)<numeoinplen) then
      numknaf1x = string.byte(streosurr,(numinpinx+2),(numinpinx+2))
    end--if
    if ((numinpinx+2)<numeoinplen) then
      numknaf2x = string.byte(streosurr,(numinpinx+3),(numinpinx+3))
    end--if

    boonext1x = ((numknaf1x==88) or (numknaf1x==120)) -- case insensitive
    boonext2x = ((numknaf2x==88) or (numknaf2x==120)) -- case insensitive
    boosudahdone = false
    if (boonext1x and boonext2x) then -- got "xx"
      strutf8eo = strutf8eo .. string.char(numknar0k,numknaf1x) -- keep one "x" only
      numinpinx = numinpinx + 3 -- eaten 3 written 2
      boosudahdone = true
    end--if
    if (boonext1x and (not boonext2x)) then -- got yes-"x" and no-"x"
      vareopeek = contabtransluteo[numknar0k] -- UINT16 or type "nil"
      if (type(vareopeek)=='number') then
        strutf8eo = strutf8eo .. string.char(mathdiv(vareopeek,256),mathmod(vareopeek,256)) -- add UTF8 char
        numinpinx = numinpinx + 2 -- eaten 2 written 2
        boosudahdone = true
      end--if
    end--if
    if (not boosudahdone) then
      strutf8eo = strutf8eo .. string.char(numknar0k) -- copy char
      numinpinx = numinpinx + 1 -- eaten 1 written 1
    end--if

  end--while

  return strutf8eo

end--function prikodeosg

------------------------------------------------------------------------

local function prhrecusurrstrtab (varinkommen, strlingkod, bookeys)

-- Process (transcode) either a single string, or all string items in a
-- table (even nested) using any type of keys/indexes (such as a holey
-- number sequence and non-numeric ones). Items with a non-string non-table
-- value are kept unchanged. Optional transcoding of eo and NOPE sv surrogates
-- (via 2 separate procedures). Optionally string keys/indexes are transcoded
-- as well.

-- Input  : * varinkommen -- type "string" or "table"
--          * strlingkod -- "eo" or NOPE "sv" to transcode surrogates,
--                          anything else (preferably type "nil") to skip this
--          * bookeys -- transcode keys too (preferably either "true"
--                       or type "nil")

-- Depends on procedures :
-- [I] prikodeosg (only if trans of eo X-surrogates desired)
-- [I] NOPE prikodsvsg
-- [E] mathdiv mathmod (via "prikodeosg" and NOPE "prikodsvsg")

-- Depends on constants :
-- * table "contabtransluteo" inherently holey (via "prikodeosg")
-- * NOPE table "contabtranslutsv"

-- We always fully rebrew tables from scratch, thus do NOT replace
-- single elements (doing so would break "in pairs").

  local varnky = 0 -- variable without type
  local varutmatning = 0
  local boodone = false

  if (type(varinkommen)=='string') then
    if (strlingkod=='eo') then
      varinkommen = prikodeosg (varinkommen) -- surr
    end--if
    -- if (strlingkod=='sv') then
      -- varinkommen = prikodsvsg (varinkommen) -- surr
    -- end--if
    varutmatning = varinkommen -- copy, change or no change
    boodone = true
  end--if

  if (type(varinkommen)=='table') then
    varutmatning = {} -- brew new table from scratch
    for k4k,v4v in pairs(varinkommen) do -- nothing done if table empty
      if ((bookeys==true) and (type(k4k)=='string')) then
        varnky = prhrecusurrstrtab (k4k, strlingkod, nil) -- RECURSION
      else
        varnky = k4k
      end--if
      if ((type(v4v)=='string') or (type(v4v)=='table')) then
        v4v = prhrecusurrstrtab (v4v, strlingkod, bookeys) -- RECURSION
      end--if
      varutmatning[varnky] = v4v -- write same or diff place in dest table
    end--for
    boodone = true
  end--if

  if (not boodone) then
    varutmatning = varinkommen -- copy as-is whatever it is, useless
  end--if

return varutmatning

end--function prhrecusurrstrtab

------------------------------------------------------------------------

---- VARIABLES [R] ----

------------------------------------------------------------------------

function exporttable.ek (arxframent)

  -- general unknown type

  local vartymp = 0 -- variable without type multipurpose

  -- special type "args" AKA "arx"

  local arxsomons = 0 -- metaized "args" from our own or parent's "frame"

  -- peeked stuff

  local strpiklangcode = '' -- "en" privileged site language
  local strpikkatns    = '' -- "Category"
  local strpikparent   = '' -- "Template:nope" FULLPAGENAME

  -- misc str

  local strpagenam  = '' -- from "{{PAGENAME}}" o "pagenameoverridetestonly"
  local strruangna  = '' -- "{{NAMESPACENUMBER}}" o "nsnumberoverridetestonly"
  local strsizes    = '' -- actually more than x and y
  local strtomp     = '' -- temp

  local strimagkk   = '' -- name left or one
  local strdesckk   = '' -- desc left or one
  local strimagll   = '' -- name right
  local strdescll   = '' -- desc right

  local strviserr   = '' -- visible error
  local strvisgud   = '' -- visible good output ie table
  local strinvkat   = '' -- invisible category part
  local strret      = '' -- final result string

  -- general num (no var for namespace here)

  local numerr    = 0  -- 0 OK 1 inter 2 param 3 deskripsi 11 ns
  local numpindex = 0  -- number of anon params
  local numcase   = 0  -- decimal 97...101 for a) to e)
  local numsizxx  = 0
  local numsizyy  = 0

  -- misc boo

  local boonocat    = false  -- from "nocat=true"
  local booreduced  = false  -- from "duon=1" "half=1"
  local booprojekto = false  -- "true" if ns = 4 (no num var exists)
  local booaldono   = false  -- "true" if ns = 102 (no num var exists)

-- ******************
-- *    MAIN [Z]    * --------------------------------------------------
-- ******************

  ---- GUARD AGAINST INTERNAL ERROR AGAIN ----

  -- later reporting of #E01 may NOT depend on uncommentable strings

  if (qbooguard) then
    numerr = 1 -- #E01 internal
  end--if

  ---- PEEK STUFF THAT IS NOT OVERRIDDEN MINIMAL ----

  -- this depends on "arxframent" (only if parent requested) but NOT on "arx"

  -- "strpikkatns" and "strpikindns" and "strpikapxns" do NOT
  -- include a trailing ":" colon, and are for "lfykattlaenk"
  -- and "lfyapxindlaenk" and "lfikatpaldigu"

  if (numerr==0) then
    strpiklangcode = contabovrd.sitelang or mw.getContentLanguage():getCode() or 'en'              -- privileged site language
    strpikkatns    = contabovrd.katprefi or (mw.site.namespaces[ 14] or {})['name'] or 'Category'  -- standard namespace
    strpikparent   = contabovrd.parentfn or arxframent:getParent():getTitle() or 'Template:nope'   -- fullpagename
    if ((type(strpiklangcode)~='string') or (type(strpikkatns)~='string') or (type(strpikparent)~='string')) then
      numerr = 1 -- #E01 internal (unlikely)
    end--if
  end--if (numerr==0) then

  ---- PROCESS MESSAGES ----

  if (numerr==0) then
    contaberaroj = prhrecusurrstrtab (contaberaroj, strpiklangcode, nil)
    contabkatoj  = prhrecusurrstrtab (contabkatoj,  strpiklangcode, nil)
  end--if

  ---- GET THE ARX (ONE OF TWO) ----

  -- must be seized independently on "numerr" even if we already suck

  -- give a f**k in possible anon params for now (would cause #E08)

  arxsomons = arxframent.args -- "args" from our own "frame"
  if (type(arxsomons)~='table') then
    arxsomons = {} -- guard against indexing error
    numerr = 1 -- #E01 internal
  end--if
  if (arxsomons['parentframe']=='true') then
    arxsomons = arxframent:getParent().args -- "args" from parent's "frame"
  end--if
  if (type(arxsomons)~="table") then
    arxsomons = {} -- guard against indexing error again
    numerr = 1 -- #E01 internal
  end--if

  ---- PROCESS 3 HIDDEN NAMED PARAMS ----

  -- this may override "mw.title.getCurrentTitle().text" and
  -- stipulate content in "strpagenam", missing is OK, empty is NOT valid

  -- bad "pagenameoverridetestonly=" can give #E01

  -- give a f**k in possible anon params for now (could otherwise cause #E08)

  strpagenam = '' -- using vartymp here
  if (numerr==0) then -- get pagename (error if bad, silent if absent)
    vartymp = arxsomons['pagenameoverridetestonly']
    if (type(vartymp)=='string') then -- do NOT merge if:s
      if (prgstringrange(vartymp,1,200)) then -- empty or too long NOT legal
        strpagenam = vartymp
      else
        numerr = 1 -- #E01 internal
      end--if
    end--if
  end--if

  strruangna = '' -- using vartymp here
  if (numerr==0) then -- get namespace (silent if bad, silent if absent)
    vartymp = arxsomons['nsnumberoverridetestonly']
    if (prgstringrange(vartymp,1,4)) then -- empty or too long NOT legal
      strruangna = vartymp
    end--if
  end--if

  boonocat = (arxsomons['nocat']=='true') -- "arxsomons" must be a table !!!

  ---- SEIZE THE PAGENAME FROM MW AFTER OVERRIDE AND PROHIBIT EMPTY ----

  -- later reporting of #E01 may NOT depend on uncommentable strings

  -- must be 1...160 octet:s keep consistent with "pagenameoverridetestonly="

  if ((numerr==0) and (strpagenam=='')) then -- get pagename (error if bad)
    vartymp = mw.title.getCurrentTitle().text -- without namespace prefix
    if (prgstringrange(vartymp,1,200)) then -- empty or too long NOT legal
      strpagenam = vartymp -- cannot be left empty
    else
      numerr = 1 -- #E01 internal
    end--if
  end--if

  if (strpagenam=='') then
    numerr = 1 -- #E01 internal -- no other error number is possible so far
  end--if

  ---- WHINE IF YOU MUST #E01 ----

  -- reporting of this error #E01 must NOT depend on uncommentable
  -- or pickabe stuff such as "contaberaroj" or "strpikparent"

  -- do NOT use "prhbrewerr5hupa", report our name (NOT of template), in EN

  if (numerr==1) then
    strtomp = '#E01 Internal error in "Module:twoimage".'
    strviserr = contabfel.hubg .. strtomp .. contabfel.huen
  end--if

  ---- SEIZE THE NAMESPACE FROM MW AND CHECK IT ----

  -- assign "booprojekto" "booaldono" on success or brew #E10 on failure

  if ((numerr==0) and (strruangna=='')) then -- get namespace (silent if bad)
    vartymp = mw.title.getCurrentTitle().namespace -- type is "number"
    if (mathisintrange(vartymp,0,9999)) then -- negative NOT legal but silent
      strruangna = tostring(vartymp) -- can be left empty, check below required
    end--if
  end--if

  if (numerr==0) then
    booprojekto = (strruangna=='4')
    booaldono = (strruangna=='102')
    if ((strruangna~='0') and (not booprojekto) and (not booaldono)) then
      numerr = 10 -- #E10 wrong ns (overrides #E08 and #E09)
    end--if
  end--if

  ---- PRELIMINARILY ANALYZE ANONYMOUS PARAMETERS ----

  -- this will catch holes, empty parameters, too long parameters,
  -- and wrong number of parameters

  -- below on exit var "numpindex" will contain number of
  -- prevalidated anonymous params

  -- this depends on 3 constants:
  -- * contabparam[0] minimal number
  -- * contabparam[1] maximal number
  -- * contabparam[2] maximal length (default 200)

  if (numerr==0) then -- using vartymp calling prgstringrange
    numpindex = 0 -- ZERO-based
    while true do
      vartymp = arxsomons [numpindex+1] -- can be type "nil"
      if ((type(vartymp)~='string') or (numpindex>contabparam[1])) then
        break -- good or bad
      end--if
      if (not prgstringrange (vartymp,1,contabparam[2])) then
        numerr = 9 -- #E09 anon param empty or too long
        break -- only bad here
      end--if
      numpindex = numpindex + 1 -- on exit has number of valid parameters
    end--while
    if ((numerr==0) and ((numpindex<contabparam[0]) or (numpindex>contabparam[1]))) then
      numerr = 8 -- #E08 number of anon params
    end--if
  end--if

  ---- SEIZE 1...4 ANONYMOUS PARAMETERS ----

  -- here we assign "numcase" to "a)" to "e)" ie 97 to 101

  -- "pricheckforbabr" returns non-ZERO if illegal giving #E09 #E14 #E15

  if (numerr==0) then
    while true do -- fake loop
      strimagkk = arxsomons [1] -- assign in any valid case
      numerr = pricheckforbabr(strimagkk,2,160)
      if (numerr~=0) then
        break -- #E09 #E14 #E15
      end--if
      if (numpindex==1) then
        numcase = 97 -- a) (1) image name
        break
      end--if
      if (numpindex==2) then -- b) or c)
        strtomp = arxsomons [2]
        numerr = pricheckforbabr(strtomp,2,160)
        if (numerr~=0) then
          break -- #E09 #E14 #E15
        end--if
        if (lfhtestfile(strtomp)) then -- c)
          numcase = 99 -- c) (2) two image names
          strimagll = strtomp
        else
          numcase = 98 -- b) (2) image name and description
          strdesckk = strtomp
        end--if
        break
      end--if (numpindex==2) then
      if (numpindex==3) then -- d)
        numcase = 100 -- d) (3) two image names and one description
        strimagll = arxsomons [2]
        strdesckk = arxsomons [3]
        numerr = pricheckforbabr(strimagll,2,160)
        if (numerr~=0) then
          break -- #E09 #E14 #E15
        end--if
        numerr = pricheckforbabr(strdesckk,2,160)
        if (numerr~=0) then
          break -- #E09 #E14 #E15
        end--if
      else
        numcase = 101 -- e) (4) image name, description, image name, description
        strdesckk = arxsomons [2]
        strimagll = arxsomons [3]
        strdescll = arxsomons [4]
        numerr = pricheckforbabr(strdesckk,2,160)
        if (numerr~=0) then
          break -- #E09 #E14 #E15
        end--if
        numerr = pricheckforbabr(strimagll,2,160)
        if (numerr~=0) then
          break -- #E09 #E14 #E15
        end--if
        numerr = pricheckforbabr(strdescll,2,160)
        if (numerr~=0) then
          break -- #E09 #E14 #E15
        end--if
      end--if (numpindex==3) else
      break -- finally
    end--while -- fake loop
  end--if (numerr==0) then

  ---- WHINE IF YOU MUST #E02...#E98 SIMPLE THERE IS A PARENT ----

  -- reporting of errors #E02...#E98 depends on uncommentable
  -- stuff and on pickable "strpikparent"

  if ((numerr>=2) and (numerr<=98)) then
    strviserr = prhbrewerr5hupa(numerr,('"'..strpikparent..'"'))
  end--if

  ---- SEIZE ONE NAMED PARAMETER ----

  -- this cannot cause any error but "arxsomons" must be a table !!!

  booreduced = false
  if (strpiklangcode=='eo') then
    booreduced = (arxsomons['duon']=='1')
  end--if
  if (strpiklangcode=='id') then
    booreduced = (arxsomons['half']=='1')
  end--if

  ---- REDUCE 5 CASES TO 3 BY MAGIC MAPPING ----

  if (numerr==0) then
    if (numcase==97) then
      strdesckk = strpagenam
      numcase = 98 -- a) becomes b) -- one image one description
    end--if
    if (numcase==99) then
      strdesckk = strpagenam
      numcase = 100 -- c) becomes d) -- two images one common description
    end--if
  end--if

  ---- RANDOMGENERATE THE SIZES ----

  if (numerr==0) then
    if (numcase==98) then
      if (booreduced) then
        numsizxx  = 160 -- one reduced, case b)
        numsizyy  = 160
      else
        numsizxx  = 240 -- one full, case b)
        numsizyy  = 240
      end--if
    else
      if (booreduced) then
        numsizxx  = 160 -- two reduced, cases d) e)
        numsizyy  = 120
      else
        numsizxx  = 240 -- two full, cases d) e)
        numsizyy  = 160
      end--if
    end--if
    strsizes = "|center|" .. tostring (numsizxx) .. "x" .. tostring (numsizyy) .. "px]]"
  end--if

  ---- SPLIT THE DESCRIPTIONS ----

  strdesckk = lfhsplitmanauto (strdesckk)
  if (numcase==101) then -- e)
    strdescll = lfhsplitmanauto (strdescll)
  end--if

  ---- BREW THE TABLE ----

  -- this depends on "contabhtml"

  -- [[File: (name) |center| (size) ]]
  --  "strsizes" begins with the left wall "|" and includes the final "]]"

  -- b) one image (two table rows each with one cell)
  -- d) two images with a common description (two table rows, the upper one
  --    with two cells and the lower one with "colspan=2")
  -- e) two images with two separate descriptions (2 table rows,
  --    each with 2 cells)

  if (numerr==0) then
    strvisgud = contabhtml.matabg -- begin table
    if (numcase==98) then -- b)
      strvisgud = strvisgud .. '<tr>' .. contabhtml[1] .. '[[File:' .. strimagkk .. strsizes .. '</td></tr>'
      strvisgud = strvisgud .. '<tr>' .. contabhtml[2] .. strdesckk .. '</td></tr>'
    end--if
    if (numcase==100) then -- d)
      strvisgud = strvisgud .. '<tr>' .. contabhtml[1] .. '[[File:' .. strimagkk .. strsizes .. '</td>' .. contabhtml[1] .. '[[File:' .. strimagll .. strsizes .. '</td></tr>'
      strvisgud = strvisgud .. '<tr>' .. contabhtml[3] .. strdesckk .. '</td></tr>'
    end--if
    if (numcase==101) then -- e)
      strvisgud = strvisgud .. '<tr>' .. contabhtml[1] .. '[[File:' .. strimagkk .. strsizes .. '</td>' .. contabhtml[1] .. '[[File:' .. strimagll .. strsizes .. '</td></tr>'
      strvisgud = strvisgud .. '<tr>' .. contabhtml[2] .. strdesckk .. '</td>' .. contabhtml[2] .. strdescll .. '</td></tr>'
    end--if
    strvisgud = strvisgud .. contabhtml.minend -- close table
  end--if

  ---- BREW THE CAT PART ----  !!!FIXME!!! more tracking cat:s

  -- one cat for numerr=0, two cats for numerr>=2, no cats for numerr=1

  -- "strpikkatns" is the cat prefix NOT including the colon ":"

  if (not boonocat) then
    if (numerr==0) then
      if (booprojekto or booaldono) then
        strinvkat = contabkatoj[2] -- aldo (alt project/internal)
      else
        strinvkat = contabkatoj[1] -- lemma
      end--if
      strinvkat = '[[' .. strpikkatns .. ':' .. strinvkat .. ']]'
    end--if
    if (numerr>=2) then
      strinvkat = '[[' .. strpikkatns .. ':' .. contabkatoj[3] .. ']][['.. strpikkatns .. ':' .. contabkatoj[4] .. ']]' -- both
    end--if
  end--if

  ---- RETURN THE JUNK STRING ----

  strret = strviserr .. strvisgud .. strinvkat
  return strret

end--function exporttable.ek

  ---- RETURN THE JUNK LUA TABLE ----

return exporttable