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