local HelpVisualEditor = { suite = "HelpVisualEditor",
serial = "2019-05-16",
item = 63383133 }
--[=[
Unterstützung für Vorlagen/Hilfeseiten Hilfe:VisualEditor/***
]=]
local lucky, Data = pcall( mw.loadData,
"Module:Hilfe:VisualEditor/config" )
if type( Data ) ~= "table" then
error( "[[Module:Hilfe:VisualEditor/config]] fehlt" )
end
local Config = Data.config
local Widgets = { }
local Frame
local Title
local Tree
local function facet( activate )
return activate:gsub( "%.svg$", "-progressive.svg" )
end -- facet()
local function factory( ancestor, array, all )
-- Clone a table
-- Parameter:
-- ancestor -- table, to be copied
-- array -- true, if sequence; number: copy first elements
-- all -- true, if deep elements to be copied also
-- Returns:
-- new table
local r = { }
if array then
local n
if type( array ) == "number" then
n = array
else
n = table.maxn( ancestor )
if n == 0 then
for k, v in pairs( ancestor ) do
n = n + 1
end -- for k, v
end
end
for i = 1, n do
table.insert( r, ancestor[ i ] )
end -- for i
if all then
local e
for i = 1, n do
e = r[ i ]
if type( e ) == "table" then
r[ i ] = factory( e, false, true )
end
end -- for i
end
else
for k, v in pairs( ancestor ) do
r[ k ] = v
end -- for k, v
if all then
local e
for k, v in pairs( r ) do
e = r[ k ]
if type( e ) == "table" then
r[ k ] = factory( e, false, true )
end
end -- for k, v
end
end
return r
end -- factory()
local function faculty( adjust )
-- Test template arg for boolean
-- adjust -- string or nil
-- Returns boolean
local s = type( adjust )
local r
if s == "string" then
r = mw.text.trim( adjust )
r = ( r ~= "" and r ~= "0" )
elseif s == "boolean" then
r = adjust
else
r = false
end
return r
end -- faculty()
local function fallback( ask )
-- Create similar item
-- ask -- string, with ID
-- Returns table
local insertFew = 4
local kurzSchrift = 4
local r
if ask:sub( 1, 10 ) == "CodeMirror" and #ask > 10 then
r = { menu = 3,
show = Tree.CodeMirror.show,
slot = Tree.CodeMirror.slot,
icon = Tree.CodeMirror.icon,
start = Tree.CodeMirror.start,
lowered = false }
if ask == "CodeMirrorAktiv" then
r.icon = facet( r.icon )
end
elseif ask == "InsertFew" then
r = { menu = -2,
show = Tree.InsertAll.show,
slot = Tree.InsertAll.slot,
icon = Tree.InsertAll.icon,
start = Tree.InsertAll.start,
entries = factory( Tree.InsertAll.entries, insertFew ) }
r.entries[ insertFew ] = "Mehr"
elseif ask:sub( 1, 7 ) == "Schrift" then
local entries, k
if ask:sub( 1, 11 ) == "SchriftKurz" then
k = kurzSchrift
else
k = true
end
entries = factory( Tree.SchriftAlle.entries, k )
r = { menu = -2,
icon = Tree.SchriftAlle.icon,
slot = Tree.SchriftAlle.slot,
entries = entries }
if k == kurzSchrift then
r.entries[ kurzSchrift ] = "Mehr"
if ask == "SchriftKurz" then
r.entries[ kurzSchrift - 1 ] = "Gestaltlos"
else
r.entries[ kurzSchrift - 1 ] = "GestaltlosNix"
end
else
r.entries[ #r.entries - 1 ] = "GestaltlosNix"
end
elseif ask:sub( 1, 14 ) == "Seitenoptionen" and #ask > 14 then
local sub
r = factory( Tree.Seitenoptionen, false, true )
for i = 2, #r.entries do
sub = r.entries[ i ]
if i <= 5 then
Tree[ sub ] = factory( Tree[ sub ] )
Tree[ sub ].lowered = true
elseif sub == "CodeMirror" then
if ask:sub( 20 ) == "" then
r.entries[ i ] = "CodeMirrorInaktiv"
else
r.entries[ i ] = sub .. ask:sub( 20 )
end
break -- for i
end
end -- for i
end
return r
end -- fallback()
local function fault( a )
-- Formatiere Fehler mit class=error
-- Parameter:
-- a -- string, mit Fehlermeldung
-- Rückgabewert:
-- string, mit HTML-Element
local e = mw.html.create( "span" )
e:addClass( "error" )
:wikitext( "FEHLER * " .. a )
return tostring( e )
end -- fault()
local function fetch( access )
-- Retrieve static tree entry; else on the fly
-- Parameter:
-- access -- string, mit ID
-- Rückgabewert:
-- tree entry
local r = Tree[ access ]
if not r then
r = fallback( access )
end
return r
end -- fetch()
local function fill( apply, assume )
-- Beschriftung ermitteln
-- Parameter:
-- apply -- table
-- assume -- string, mit Rückfallwert
-- Rückgabewert:
-- string
local r
if apply.slot then
r = mw.message.new( apply.slot:gsub( "^@", "visualeditor-" ) )
if not r:exists() then
r = false
end
end
if r then
r = r:plain()
elseif apply.show then
r = apply.show
else
r = assume
end
return r
end -- fill()
local function flat( a )
-- Eingabewert trimmen; leeren Wert ignorieren
-- Parameter:
-- a -- string oder nil
-- Rückgabewert:
-- string oder nil oder false
local r
if a then
r = mw.text.trim( a )
if r == "" then
r = false
end
end
return r
end -- flat()
Widgets.dropdown2 = function ( all, accessed, aim, above, active, align )
local r
if all.entries then
local cssT = { "background-color:#FFFFFF",
"border-collapse:collapse",
"margin-left:.5em",
"margin-bottom:.5em" }
local high = { }
local low = ( all.menu < 0 )
local e, entry, launch, show, short, sign, space, strong, style
if aim then
e = mw.text.split( aim, "+", true )
for k, v in pairs( e ) do
high[ v ] = true
end -- for k, v
end
if above then
if type( above ) == "string" then
table.insert( cssT, "min-width:" .. above )
end
else
table.insert( cssT, "width:100%" )
end
style = table.concat( cssT, ";" )
r = "\n{|"
if above then
local pars = { background = "#" .. Config.bgMainSel,
div = true }
local sep = "style='border%%s: #%s 1px solid;%%s'"
sep = string.format( sep, Config.borderDd )
style = style .. ";box-shadow: 0 2px 2px 0 rgba(0,0,0,0.25)"
r = string.format( "%s %s\n|%s|%s",
r,
string.format( sep, "", style ),
string.format( sep, "-bottom", "" ),
Widgets.toolItem( accessed, pars ) )
else
cssT = { style,
"font-size:90%" }
r = string.format( "%s style='%s'",
r,
table.concat( cssT, ";" ) )
end
space = false
for k, v in pairs( all.entries ) do
entry = fetch( v )
launch = high[ v ]
if not entry then
return fault( "dropdown: Bad entry " .. v )
end
if launch then
if low or Config.itemSel == 1 then
strong = "background-color: #" .. Config.bgItemSel
else
strong = "border: 2px solid #" .. Config.borderSel
end
high[ v ] = false
elseif entry.lowered then
strong = "opacity:0.5"
else
strong = ""
end
if strong ~= "" then
strong = string.format( "style='%s'|", strong )
end
if entry.icon then
sign = entry.icon
if launch and not entry.lock and
sign:match( "^OOjs UI .+%.svg$" ) then
sign = facet( sign )
end
elseif launch and all.leader then
sign = "OOjs UI icon check-progressive.svg"
else
sign = false
end
if sign then
sign = string.format( "[[File:%s|%dpx|icon]]",
sign,
entry.px or Config.icon )
else
if not space then
space = string.format( "%dpx", Config.icon )
e = mw.html.create( "span" )
e:css( { ["display"] = "inline-block",
["width"] = space } )
:wikitext( " " )
space = tostring( e )
end
sign = space
end
show = fill( entry, v )
if active and entry.smart then
local s
e = mw.html.create( "span" )
if launch then
s = "#3366CC"
else
s = "#444444"
end
e:css( "color", s )
:wikitext( show )
show = string.format( "[[#%s|%s]]",
entry.smart,
tostring( e ) )
launch = false
end
if entry.style or launch then
e = mw.html.create( "span" )
if entry.style then
e:cssText( entry.style )
end
if launch then
e:css( "color", "#3366CC" )
end
e:wikitext( show )
show = tostring( e )
end
e = mw.html.create( "div" )
e:css( { ["float"] = "left",
["padding-right"] = "3px",
["white-space"] = "pre" } )
:wikitext( string.format( "%s %s", sign, show ) )
show = tostring( e )
if entry.shortcut then
if not Title then
Title = mw.title.getCurrentTitle()
end
if Title.text == Config.single then
short = "#VEshortcuts"
else
short = string.format( "%s:%s",
mw.site.namespaces[12].name,
Config.shortcut )
end
e = mw.html.create( "div" )
e:css( { ["float"] = "right",
["opacity"] = "0.5",
["margin-left"] = "0.8em",
["margin-right"] = "0.3em",
["white-space"] = "nowrap" } )
:wikitext( string.format( "[[%s|%s]]",
short,
entry.shortcut ) )
short = tostring( e )
elseif entry.iconRight then
e = mw.html.create( "div" )
e:css( { ["float"] = "right",
["margin-left"] = "0.8em",
["margin-right"] = "0.3em" } )
:wikitext( string.format( "[[File:%s|%dpx]]",
entry.iconRight,
entry.pxR or Config.icon ) )
short = tostring( e )
else
short = ""
end
r = string.format( "%s\n|-\n|%s %s %s",
r, strong, show, short )
end -- for k, v
r = r .. "\n|}"
if aim then
show = false
for k, v in pairs( e ) do
if high[ v ] then
if show then
show = string.format( "%s+%s", show, k )
else
show = k
end
end
end -- for k, v
if show then
r = r .. fault( "dropdown: Auswahl fehlt: " .. show )
end
end
else
r = fault( "dropdown: Keine .entries" )
end
return r
end -- Widgets.dropdown2()
Widgets.progressive = function ( about, adjust )
local e = mw.html.create( "span" )
local size = "1em"
local r
if adjust.div or adjust.mini then
size = "0.8em"
e:css( { ["border-radius"] = "0",
["font-size"] = "75%" } )
end
e:addClass( "mw-ui-button mw-ui-progressive" )
:css( { ["color"] = "#FFFFFF",
["line-height"] = size,
["min-width"] = "none" } )
:wikitext( about )
r = tostring( e )
if Config.tstyleUI then
r = Frame:extensionTag( "templatestyles",
nil,
{ src = Config.tstyleUI } )
.. r
end
return r
end -- Widgets.progressive()
Widgets.toolbar = function ( ahead, after )
local div = { div = true,
["line-height"] = "1em" }
local element = mw.html.create( "div" )
local entries = factory( Tree.TOOLBAR.entries, true )
local n = #entries
local surround = "1px solid #" .. Config.borderBar
local entry, s
element:css( { ["background"] = "#FFFFFF",
["border"] = surround,
["float"] = "left",
["margin"] = "0" } )
if ahead >= 1 and after >= 1 then
element:css( { ["width"] = "100%" } )
end
if after > 0 then
local rechts = mw.html.create( "div" )
rechts:css( { ["float"] = "right",
["margin-left"] = "1em",
["white-space"] = "nowrap" } )
if ahead > 0 then
rechts:css( { ["border-left"] = surround } )
end
for i = 5, n do
s = entries[ i ]
entry = Tree[ s ]
if entry.rechts then
rechts:wikitext( Widgets.toolItem( s, div ) )
end
end -- for i
element:wikitext( tostring( rechts ) )
end
if ahead > 0 then
for i = 1, n do
s = entries[ i ]
entry = Tree[ s ]
if entry.links and ahead >= entry.links then
element:wikitext( Widgets.toolItem( s, div ) )
end
end -- for i
end
entry = mw.html.create( "div" )
entry:css( { ["clear"] = "both" } )
element:wikitext( tostring( entry ) )
return tostring( element )
end -- Widgets.toolbar()
Widgets.toolItem = function ( access, adjust )
local details = factory( fetch( access ), false )
local element, low, r, shift, show, smart
if adjust then
for k, v in pairs( adjust ) do
details[ k ] = v
end -- for k, v
end
low = ( details.border or details.menu ~= 2 )
and not details.div
if low then
element = "span"
else
element = "div"
end
element = mw.html.create( element )
if details.border and details.icon then
show = ""
else
show = fill( details, access )
if not low then
local height = adjust["line-height"]
element:css( { ["float"] = "left" } )
if height then
element:css( { ["line-height"] = height } )
end
end
if not Title then
Title = mw.title.getCurrentTitle()
end
if Title.text == Config.single then
if details.swift then
shift = "#" .. details.swift
end
elseif details.start then
shift = string.format( "%s:%s",
mw.site.namespaces[12].name,
details.start )
if shift == Title.prefixedText then
shift = false
end
end
if details.progressive then
show = Widgets.progressive( show, details )
end
if shift then
show = string.format( "[[%s|%s]]", shift, show )
end
end
if details.smart then
smart = details.smart
if details.shortcut then
smart = string.format( "%s %s %s",
smart,
mw.ustring.char( 8211 ),
details.shortcut )
end
elseif details.shortcut then
smart = details.shortcut
end
element:css( { ["white-space"] = "nowrap" } )
if not details.progressive then
local graphics = details.icon or details.before
if details.border then
element:css( { ["border"] = "1px solid #"
.. Config.borderBar,
["padding"] = "2px 5px" } )
else
element:css( { ["padding"] = "0 5px" } )
if type( details.borderR ) ~= "boolean" then
details.borderR = true
end
if details.background then
element:css( { ["background"] = details.background } )
end
if details.borderR then
element:css( { ["border-right"] = "1px solid #"
.. Config.borderBar } )
end
end
if graphics then
local k, s
if type( graphics ) == "table" then
details.px = graphics.px
details.top = graphics.top
graphics = graphics.img or "example.svg"
else
show = ""
end
if type( details.px ) == "number" then
k = details.px
else
k = Config.mainPX
end
-- if details.align == "right" then
-- s = "|right"
-- end
if details.top then
s = "|top"
end
show = string.format( "[[File:%s|%dpx%s|link=%s|%s]]%s",
graphics,
k,
s or "",
shift or "",
smart or "icon",
show )
end
if details.div and not details.icon then
element:css( { ["padding-bottom"] = "3px",
["padding-top"] = "2px" } )
end
if details.entries and not details.less then
show = string.format( "%s [[File:%s|%dpx|link=|%s]]",
show,
Config.openDown.src,
Config.openDown.px,
smart or "Dropdown-Menü" )
end
end
if smart then
element:attr( "title", smart )
end
element:wikitext( show )
if details.name then
show = fill( details, access )
if math.abs( details.name ) > 1 then
show = string.format( "'''%s'''", show )
end
if details.name > 0 then
r = string.format( "%s %s", tostring( element ), show )
else
r = string.format( "%s %s", show, tostring( element ) )
end
else
r = tostring( element )
end
return r
end -- Widgets.toolItem()
Tree = factory( Data.tree )
HelpVisualEditor.failsafe = function ( atleast )
-- Retrieve versioning and check for compliance
-- Precondition:
-- atleast -- string, with required version or "wikidata",
-- or false
-- Postcondition:
-- Returns string with appropriate version, or false
local since = atleast
local r
if since == "wikidata" then
local item = HelpVisualEditor.item
since = false
if type( item ) == "number" and item > 0 then
local entity = mw.wikibase.getEntity( string.format( "Q%d",
item ) )
if type( entity ) == "table" then
local vsn = entity:formatPropertyValues( "P348" )
if type( vsn ) == "table" and
type( vsn.value ) == "string" and
vsn.value ~= "" then
r = vsn.value
end
end
end
end
if not r then
if not since or since <= HelpVisualEditor.serial then
r = HelpVisualEditor.serial
else
r = false
end
end
return r
end -- HelpVisualEditor.failsafe()
-- Export
local p = { }
p.dropdown = function ( frame )
-- Tropfrunter
local params = frame:getParent().args
local scope = flat( params[ 1 ] )
local r
if scope then
local path = fetch( scope )
if path then
local leader = not faculty( params.nohead )
or faculty( params.head )
local link = faculty( params.link )
local widget = string.format( "dropdown%d",
math.abs( path.menu ) )
Frame = frame
if leader and params.head and
params.head:match( "^%d+%l+$" ) then
leader = params.head
end
r = Widgets[ widget ]( path,
scope,
flat( params[ 2 ] ),
leader,
link,
flat( params.float ) )
else
r = fault( "dropdown * Menü unbekannt: " .. scope )
end
else
r = fault( "dropdown: Kein Menü" )
end
return r
end -- p.dropdown()
p.headline = function ( frame )
-- Dynamische Überschrift
local pages = { }
local params = frame:getParent().args
local parts = { }
local e, m, s, show
Title = mw.title.getCurrentTitle()
for k, v in pairs( params ) do
s = type( k )
if s == "number" then
if k == 1 then
if v:match( "^%s*[1-6]%s*$" ) then
m = tonumber( v )
else
show = fault( "h-Zahl ungültig:" .. v )
end
elseif k == 2 and not show then
v = mw.text.trim( v )
if v ~= "" then
show = v
end
end
elseif s == "string" then
if k == "Gesamt" then
k = Config.single
end
if k:find( "/", 2, true ) then
pages[ k:gsub( "^%./", "" ) ] = v
elseif k:sub( 1, 5 ) == "Anker" then
if v ~= "" then
table.insert( parts, v )
end
end
end
end -- for k, v
if not m then
m = 2
end
if not pages[ Config.single ] then
pages[ Config.single ] = tostring( m + 1 )
end
if Title.namespace == 12 then
s = pages[ Title.text ]
if s then
if s:match( "^[1-6]$" ) then
m = tonumber( s )
else
show = fault( "h-Zahl ungültig:" .. s )
end
end
end
if not show then
show = fault( "Überschrift fehlt" )
end
e = mw.html.create( string.format( "h%d", m ) )
m = table.maxn( parts )
if m > 0 then
e:attr( "id", mw.uri.anchorEncode( parts[ 1 ] ) )
if m > 1 then
local el
for i = 2, m do
el = mw.html.create( "span" )
el:attr( "id", mw.uri.anchorEncode( parts[ i ] ) )
show = tostring( el ) .. show
end -- for i
end
end
e:wikitext( show )
return tostring( e )
end -- p.headline()
p.icon = function ( frame )
-- Icon per ID
local params = frame:getParent().args
local sketch = flat( params[ 1 ] )
local r
if sketch then
local entry = Tree[ sketch ]
if entry then
r = entry.icon
if r then
local icon = flat( params[ 2 ] )
if icon then
icon = tonumber( icon ) or 1
else
icon = Config.icon
end
if icon > 7 then
r = string.format( "[[File:%s|%dpx|icon]]",
r, icon )
else
r = fault( "icon * zu klein: " .. tostring( icon ) )
end
else
r = fault( "icon * unbebildert: " .. sketch )
end
else
r = fault( "icon * unbekannt: " .. sketch )
end
else
r = fault( "icon: Keine ID" )
end
return r
end -- p.icon()
p.progressive = function ( frame )
-- Button auf blau
local params = frame:getParent().args
Frame = frame
return Widgets.progressive( params[ 1 ] or "??????????????",
{ mini = faculty( params.mini ) } )
end -- p.progressive()
p.toolbar = function ( frame )
-- Hauptmenü
local params = frame:getParent().args
local links = flat( params.links ) or 1
local rechts = flat( params.rechts ) or 1
local r
if links == "½" then
links = 0.5
elseif links == "¾" then
links = 0.75
elseif links == mw.ustring.char( 8540 ) then -- 3/8
links = 0.375
else
links = tonumber( links ) or 0
end
rechts = tonumber( rechts ) or 0
if links > 0 or rechts > 0 then
Frame = frame
r = Widgets.toolbar( links, rechts )
else
r = fault( "toolbar: links und rechts 0" )
end
return r
end -- p.toolbar()
p.toolitem = function ( frame )
-- Hauptmenü-Element
local params = frame:getParent().args
local scope = flat( params[ 1 ] )
local r
if scope then
local branch = Tree[ scope ]
if branch then
local name = params.name
local pars = { }
if branch.menu == 2 then
pars.border = true
else
pars.borderR = false
end
if name and name:match( "^[-+]?[12]$" ) then
pars.name = tonumber( name )
end
Frame = frame
r = Widgets.toolItem( scope, pars )
else
r = fault( "toolitem * Item unbekannt: " .. scope )
end
else
r = fault( "toolitem: Kein Item" )
end
return r
end -- p.toolitem()
p.failsafe = function ( frame )
-- Check or retrieve version information
-- Precondition:
-- frame -- object; #invoke environment
-- Postcondition:
-- Return string with error message or ""
-- Uses:
-- HelpVisualEditor.failsafe()
local s = type( frame )
local since
if s == "table" then
since = frame.args[ 1 ]
elseif s == "string" then
since = frame
end
if since then
since = mw.text.trim( since )
if since == "" then
since = false
end
end
return HelpVisualEditor.failsafe( since ) or ""
end -- p.failsafe()
p.HelpVisualEditor = function ()
-- Module interface
return HelpVisualEditor
end
return p