Module:FFSchema
Summary
Allows you to use JSON schemas to create forms.
Uses
local p = {}
local mFFInput = require('Module:FFInput')
--[[
Separate handler for base props (currently)
{{#invoke:FFSchema|class|id=
|page= // mandatory
|class= // mandatory
|slot= // defaults to ws-base-props
|fields= // defaults to Class
|template= // defaults to Base properties
|mwfollow=true (default)/false
}}
]]--
p.class = function(frame)
-- params:
local newPage = frame.args.page or ""
local class = frame.args.class or ""
if newPage == "" or class == "" then return "" end
--
local id = frame.args.id or "create-class-instance"
local slot = frame.args.slot or "ws-base-props"
-- currently one val only
local fieldStr = frame.args.fields or "Class"
local template = frame.args.template or "Base properties"
local mwfollow = frame.args.mwfollow or "true"
-- e.g. <_create id="wscreate-base" mwwrite="Titel/{{#tool:randomint|1000000|9999999}}" mwslot="ws-base-props" mwtemplate="Base properties" mwfields="Class" mwfollow="true" />
local createAttrbs = {
id = id,
mwwrite = newPage,
mwslot = slot,
mwtemplate = template,
mwfields = fieldStr,
mwfollow = mwfollow
}
local createTag = mw.text.tag( "_create", createAttrbs, false )
-- e.g. <_input type="hidden" name="Class" value="Titles" />
local classAttrbs = {
["type"] = "hidden",
name = fieldStr,
value = class
}
local classInput = mw.text.tag( "_input", classAttrbs, false )
return frame:preprocess( createTag .. classInput )
end
--[[
main entry point - @todo rename wiki
@frame table
- schema (olim json) - pagename of JSON schema
- page - title of existing page, or title of new page to be created
- slot
- template - wiki template if any. Without it, we're assuming the content is JSON (detect content model with WSSlots)? @todo: what if we are in interested in the raw content?
create
{{#invoke:FFSchema |wiki
|schema=Json:FFSchema23
|mode=create
|page=Titel/{{#tool:randomint|1000000|9999999}}
|slot=ws-page-props
|template=Text
|defaults=...
}}
edit
{{#invoke:FFSchema |wiki
|schema=Json:FFSchema23
|mode=edit
|page={{FULLPAGENAME}}
...etc
}}
mode (optional)
The schema should define a couple of possible 'modes', currently 'create' and 'edit'. They can be used to add the necessary _create or _edit elements. For each of these modes, the corresponding 'actions' are defined.
@todo Consider custom actions other than edit and create
Field definitions accept two types of parameters:
- those that are native to FlexForm
- additional parameters beginning with an underscore (_) that can assist in some tedious tasks, including _type, _label, _default,_fields _options, _optionsep, _fields, _instance, _note (before), _tooltip, _itemLayout (@todo), _itemClass, _htmlElement and _content, _filtertemplate, _show-on-select-trigger (or _data-wssos-value), _hasVar
with select/token:
- with _options you can set 'arrays' of either single values or pairs of 'label' and 'value'
At the time of writing, FlexForm is not stable with regard to its JavaScript implementation of instances
]]--
p.wiki = function(frame)
-- 'wiki' = legacy name
return p.page(frame)
end
p.page = function(frame)
local schema = frame.args.schema or frame.args.json or ""
-- read-only!
-- @todo check that schema exists and is valid
local schemaTbl = mw.loadJsonData( schema )
-- cannot use getSlotContent because regular user may not have 'read' rights for the JSON page
--local schemaTbl = p.getSlotContent( schema, "main" )
if schemaTbl == nil then return "" end
local mode = frame.args.mode or "fields"
local page = frame.args.page or ""
local slot = frame.args.slot or "main"
local template = frame.args.template or nil
local sourceFormat = "json"
if ( template ~= nil and template ~= "" ) then
sourceFormat = "wiki"
end
-- default values in the format name=value ### name=value ### etc.
-- allows for custom delimiter/sep
local defaults = frame.args.defaults or ""
local defaultsSep = frame.args.defaultssep or "###"
-- vars for query
local varStr = frame.args.vars or ""
local varsTbl = p.createVarsTable( varStr, "###" )
-- get content from page/slot for autofill
local slotContent = nil
local title = nil
if page ~= "" then
title = mw.title.new( page )
end
if title ~= nil and title.exists then
if sourceFormat == "wiki" then
slotContent = p.getSlotContent( page, slot, template )
else
slotContent = p.getSlotContent( page, slot, nil )
end
end
-- for now, attempt nothing if page does not exist, except for defaults
local prefillTbl = p.createPrefillTable( sourceFormat, slotContent, defaults, defaultsSep )
-- fields = form field definitions
local fieldDefinitions = nil
if schemaTbl.fields ~= nil then
fieldDefinitions = schemaTbl.fields
else
return ""
end
-- edit/create actions or ""
local actionDefs = p.getSelectedActionDefs( page, mode, schemaTbl )
local actionTags = p.setupActionTags( page, actionDefs, fieldDefinitions )
-- note: turns fieldDefinitions into proxy
fieldDefinitions = p.maybeSubtractFromFieldDefinitions( actionDefs, fieldDefinitions )
-- fields
local fieldStr = p.buildFromFieldDefinitions( fieldDefinitions, sourceFormat, prefillTbl, varsTbl, false )
-- outputformat=debug
local outputFormat = frame.args["outputformat"] or nil
if outputFormat == "debug" then return "<pre>" .. actionTags .. fieldStr .. "</pre>" end
return frame:preprocess( actionTags .. fieldStr )
end
--[[
@fieldDefinitions table
@sourceFormat string
@prefillTbl table
@varsTbl table
@isNested bool
]]--
p.buildFromFieldDefinitions = function( fieldDefinitions, sourceFormat, prefillTbl, varsTbl, isNested )
--frame = mw.getCurrentFrame()
--str = ""
local res = {}
local fieldDefinitions = fieldDefinitions or {}
local varsTbl = varsTbl or {}
for _,def in ipairs(fieldDefinitions) do
local f
if def["_type"] == "fieldset" and def["_fields"] ~= nil then
-- ! needs to be tested -- inputType = def["_type"] or nil
-- @todo allow other custom definitions
f = p.createPopulatedFieldset(
def,
p.buildFromFieldDefinitions( def["_fields"], sourceFormat, prefillTbl, varsTbl, true )
)
-- str = str .. fieldset
--table.insert(res, fieldset)
else
f = p.buildFromFieldDefinition( def, sourceFormat, prefillTbl, varsTbl )
end
table.insert(res, f )
end
return table.concat(res,"")
--return str
end
--[[
@def table
@sourceFormat string
@prefillTbl
]]--
p.buildFromFieldDefinition = function( definition, sourceFormat, prefillTbl, varsTbl )
local def = p.createProxyPairs( definition )
local nameWithoutBrackets = ""
if def.name ~= nil then
nameWithoutBrackets = string.gsub( def.name, "%[%]", "" )
end
local val = nil
if nameWithoutBrackets == "" then
--do nothing
elseif prefillTbl ~= nil then
val = prefillTbl[nameWithoutBrackets] or nil
if val == "" or val == nil then val = def["_default"] or nil end
else
val = def["_default"] or nil
end
-- replace variables, currently 'query' only
local varsTbl = varsTbl or {}
if def["_hasVar"] ~= nil and varsTbl ~= {} then
-- e.g. "_hasVar": [ "query" ]
for _,keyName in ipairs( def["_hasVar"] ) do
-- keyName, usually "query" - does not work with arrays/ tables
if def[keyName] ~= nil then
def[keyName] = p.replaceVariables( def[keyName], varsTbl )
end
end
end
--[[
if sourceFormat == "wiki" then
-- todo could more efficient
--mw.log( "wiki content " )
if ( slotContent ~= nil and slotContent[1] ~= nil and slotContent[1][nameWithoutBrackets] ~= nil ) then
--[param]["_text"]
val = slotContent[1][nameWithoutBrackets]["_text"] or nil
if val == "" or val == nil then
val = def["_default"] or nil end
else
val = def["_default"] or nil
end
elseif sourceFormat == "json" and slotContent ~= nil then
--mw.log( "JSON content " )
--mw.log( "type: " .. type(slotContent[nameWithoutBrackets]) .. "<br>" )
--val may be string, table or nil
--mw.log( slotContent[nameWithoutBrackets] )
val = slotContent[nameWithoutBrackets] or nil
if val == "" or val == nil then
val = def["_default"] or nil
end
else
mw.log( "No content detected " )
end
if type(val) ~= "table" then
mw.log( "val: " .. ( val or "(none)" ) )
end
]]--
return p.parseFieldDefinition( def, val )
end
--[[
@def
@selectedVal string, table (JSON instance only) or nil
@boolean
]]--
p.parseFieldDefinition = function( def, selectedVal )
--name = def.name or ""
local nameWithoutBrackets = string.gsub( def.name or "", "%[%]", "" )
local inputType = def["_type"] or nil
selectedVal = selectedVal or nil
-- @todo revise 'for'
local labelText = def['_label'] or ""
if def["required"] ~= nil and def["required"] == "required" then
-- @todo localisation, make choice configurable, not dependent on Bootstrap
labelText = labelText .. ' <sup class="fa fa-asterisk text-danger" title="verplicht"></sup>'
end
local labelTag = mw.text.tag( "label", { ["for"] = nameWithoutBrackets }, labelText )
local tooltipText = def['_tooltip'] or nil
local itemLayout = "default"
local defaultItemClass = "ff-item"
if def["_itemLayout"] ~= nil then
itemLayout = def["_itemLayout"]
defaultItemClass = ""
end
local itemClass = def["_itemClass"] or defaultItemClass
if def["_type"] == "instance" then
itemClass = itemClass .. " ff-item-instances"
end
-- _show-on-select-trigger or _data-wssos-value (with underscore) is for adding to the item not the input
local itemShowOnSelectTrigger = def["_show-on-select-trigger"] or def["_data-wssos-value"] or nil
-- note before input
local beforeInputField = ""
local note = def['_note'] or nil
if note ~= nil and note ~= "" then
local currentFrame = mw.getCurrentFrame()
beforeInputField = "<div class='ff-note'>" .. currentFrame:preprocess(note) .. "</div>"
end
-- whether to ignore item layout:
local standalone = false
local attributes = {}
for k,v in pairs( def ) do
attributes[k] = v or ""
end
-- optional pre-filter for selected values (string only)
if attributes["_filtertemplate"] ~= nil and selectedVal ~= nil then
local currentFrame = mw.getCurrentFrame()
selectedVal = currentFrame:expandTemplate{
title = attributes["_filtertemplate"],
args = { selectedVal }
}
end
local inputField = ""
if inputType == "instance" or inputType == "instances" then
local instanceLabelTag = labelTag
if attributes.template ~= nil then
-- wiki template version
if selectedVal ~= nil then
attributes["default-content"] = mw.text.nowiki( selectedVal )
end
inputField = p.createTemplateInstance( attributes )
else
-- JSON version
selectedVal = selectedVal or nil
if type(selectedVal) == "table" then
attributes["default-content"] = mw.text.nowiki( mw.text.jsonEncode( selectedVal ) )
elseif type(selectedVal) == "string" then
-- shouldn't happen but..
attributes["default-content"] = mw.text.nowiki( selectedVal )
end
inputField = p.createJsonInstance( attributes )
end
-- todo
-- attributes.fields
--??
labelTag = instanceLabelTag
elseif inputType == "text" then
-- @todo default class
if ( attributes.class == nil ) then
attributes.class = "form-control"
end
selectedVal = selectedVal or nil
local attributes = p.removeNonFFAttributes( attributes )
inputField = mFFInput.createTextInput( nameWithoutBrackets, attributes, selectedVal )
elseif inputType == "number" then
-- @todo default class
if ( attributes.class == nil ) then
attributes.class = "form-control"
end
selectedVal = selectedVal or nil
local attributes = p.removeNonFFAttributes( attributes )
inputField = mFFInput.createNumberInput( nameWithoutBrackets, attributes, selectedVal )
elseif inputType == "date" then
if ( attributes.class == nil ) then
attributes.class = "form-control"
end
selectedVal = selectedVal or nil
local attributes = p.removeNonFFAttributes( attributes )
inputField = mFFInput.createDateInput( nameWithoutBrackets, attributes, selectedVal )
elseif inputType == "hidden" then
--todo - temporary solutipn
itemClass = "d-none"
selectedVal = selectedVal or nil
local attributes = p.removeNonFFAttributes( attributes )
inputField = mFFInput.createTextInput( nameWithoutBrackets, attributes, selectedVal )
elseif inputType == "textarea" then
-- @todo default class
if attributes.class == nil then
attributes.class = "form-control"
end
local attributes = p.removeNonFFAttributes( attributes )
local selectedVal = selectedVal or ""
inputField = mFFInput.createTextarea( nameWithoutBrackets, attributes, selectedVal)
elseif ( inputType == "token" or inputType == "tokens" ) then
-- @todo just an example
-- @todo remove stuff from attributes that does not belong
-- run attributes through a validator
local options = {}
if ( attributes['_options'] ~= nil and attributes['_options'] ~= "" ) then
options = attributes['_options']
if type(options) == "string" then
-- expand wikitext
local currentFrame = mw.getCurrentFrame()
local optionText = currentFrame:preprocess( options )
-- @todo what's a good for default for _optionsep (semi-colon?)
local optionSep = attributes['_optionsep'] or "\n"
options = mw.text.split( optionText, optionSep )
end
end
local attributes = p.removeNonFFAttributes( attributes )
local selectedVal = selectedVal or nil
inputField = mFFInput.createTokenField( nameWithoutBrackets, attributes, selectedVal, options )
elseif inputType == "select" then
if ( attributes['_options'] ~= nil and attributes['_options'] ~= "" ) then
options = attributes['_options']
end
local attributes = p.removeNonFFAttributes( attributes )
local selectedVal = selectedVal or nil
inputField = mFFInput.createSelectField( nameWithoutBrackets, attributes, selectedVal, options )
elseif inputType == "checkboxes" or inputType == "checkbox" then
if ( attributes["_options"] ~= nil and attributes["_options"] ~= "" ) then
options = attributes["_options"]
end
local attributes = p.removeNonFFAttributes( attributes )
local selectedVal = selectedVal or nil
inputField = mFFInput.createCheckInputField( "checkbox", nameWithoutBrackets, attributes, selectedVal, options )
elseif inputType == "radio" then
if ( attributes["_options"] ~= nil and attributes["_options"] ~= "" ) then
options = attributes["_options"]
end
local attributes = p.removeNonFFAttributes( attributes )
local selectedVal = selectedVal or nil
inputField = mFFInput.createCheckInputField( "radio", nameWithoutBrackets, attributes, selectedVal, options )
elseif inputType == "rating" then
local steps = nil
if attributes["_options"] ~= nil then
-- semi-colon separated list from 0.5;
steps = attributes["_options"]
end
-- @todo make configurable - starLabel, starsLabel, isCancellable, cancelTitle, static
inputField = mFFInput.createRatingField( nameWithoutBrackets, selectedVal, steps, "ster", "sterren", "true", "Annuleren" )
elseif attributes["_htmlElement"] ~= nil then
-- method closest to FlexForm's JSON schema
local el = attributes["_htmlElement"]
local content = attributes["_content"] or ""
local sosTrigger = attributes["show-on-select-trigger"] or nil
if sosTrigger ~= nil then
-- because "show-on-select-trigger" can end up being thrown away (by Lua?)
-- data attributes are safer anyway
attributes["data-wssos-value"] = sosTrigger
end
local attributes = p.removeNonFFAttributes( attributes )
inputField = mw.text.tag( el, attributes, content )
--[[
inputFieldTbl = mw.html.create( el )
:attr( attributes )
:wikitext( content )
inputField = tostring(inputFieldTbl)
]]--
standalone = true
end
-- item
-- @todo id and show-on-select id on
if standalone then
local res = inputField
return res
elseif itemLayout == "compact" then
labelTag = nil
return p.buildCompactItemLayout( inputField, itemClass )
else
local res = p.buildItemLayout( labelTag, tooltipText, inputField, beforeInputField, itemClass, itemShowOnSelectTrigger )
-- reset shouldn't be necessary any more:
labelTag = nil
return res
end
end
--[[
@labelTag
@tooltipText string|nil
@inputField
@beforeInputField - optional short message to be inserted just before the input
@itemClass string
@itemShowOnSelectTrigger nil|string
]]--
p.buildItemLayout = function( labelTag, tooltipText, inputField, beforeInputField, itemClass, itemShowOnSelectTrigger )
local tooltip = ""
if tooltipText ~= nil then
-- smw tooltip
local frame = mw.getCurrentFrame()
-- @todo create tooltip which does not depend on SMW's info
tooltip = frame:callParserFunction( '#info', { tooltipText } )
end
local labelDiv = mw.html.create( "div" )
:attr( "class", "item-label" )
:wikitext( labelTag .. tooltip )
--labelItem = "<div class='item-label'>" .. labelTag .. "</div>"
local labelItem = tostring(labelDiv)
local fieldDiv = mw.html.create( "div" )
:attr( "class", "item-field" )
:wikitext( beforeInputField .. inputField )
local fieldItem = tostring(fieldDiv)
local resDiv = mw.html.create( "div" )
:attr( "class", itemClass )
:wikitext( labelItem .. fieldItem )
if itemShowOnSelectTrigger ~= nil then
-- = show-on-select-trigger
resDiv:attr( "data-wssos-value", itemShowOnSelectTrigger )
end
return tostring(resDiv)
end
--[[
Sober, compact layout - no labels, tooltips, notes, etc.
especially for avoiding clutter in multiple-instance setups
(where some of the info doesn't need repeating)
]]--
p.buildCompactItemLayout = function( inputField, itemClass )
if itemClass == nil then itemClass = "ff-layout-compact" end
local resDiv = mw.html.create( "div" )
:attr( "class", itemClass )
:wikitext( inputField )
return tostring(resDiv)
end
p.createTemplateInstance = function( attributes )
local itemStr = ""
--frame = mw.getCurrentFrame()
for _,def in ipairs( attributes['_fields'] or {} ) do
-- @todo defaults?
--val = def["_default"] or nil
--don't use 'val' arg because the instance takes care of this
itemStr = itemStr .. p.parseFieldDefinition( def, nil )
end
local attributes = p.removeNonFFAttributes( attributes )
--content = "<div class='item-instance'>" .. itemStr .. "</div>"
local contentDiv = mw.html.create("div")
:attr( "class", "item-instance" )
:wikitext( itemStr )
local content = tostring(contentDiv)
return mw.text.tag( "_instance", attributes, content )
end
-- Identical to createTemplateInstance, currently
p.createJsonInstance = function( attributes )
local itemStr = ""
--name="Items" format="json"
for _,def in ipairs( attributes['_fields'] or {} ) do
itemStr = itemStr .. p.parseFieldDefinition( def, nil )
end
local attributes = p.removeNonFFAttributes( attributes )
local contentDiv = mw.html.create("div")
:attr( "class", "item-instance" )
:wikitext( itemStr )
local content = tostring(contentDiv)
return mw.text.tag( "_instance", attributes, content )
end
--[[
For JSON content, use this not mw.loadJsonData()
@page string - full name of page
@slot - name of slot
@template - name of the template (without NS prefix)
@return table - note differences between JSON and wikitext templates
because the latter uses the slotData format
]]--
p.getSlotContent = function( page, slot, template )
local template = template or nil
if ( template == nil or template == "" ) then
-- Assuming JSON
local str = mw.slots.slotContent( slot, page )
if type(str) == "table" then
-- @todo VERY unlikely
return str
end
if str ~= nil and str ~= "" then
return mw.text.jsonDecode( str )
else
return {}
end
end
-- template
local slotData = mw.slots.slotData( slot, page )
if ( slotData == nil ) then
return {}
end
return slotData[template]
end
p.showSlotContent = function( frame )
local page = frame.args.page
local slot = frame.args.slot or "main"
local template = frame.args.template or nil
local content = p.getSlotContent( page, slot, template )
local str = mw.text.jsonEncode( content )
return str
end
--[[
@def table - readonly
@fieldsetContent
if ( content == nil ) then
str = mw.slots.slotContent( slot, page )
return "content seems to be nil . " .. st
end
return "<pre>" .. mw.text.jsonEncode( content ) .. "</pre>"
]]--
p.createPopulatedFieldset = function( def, fieldsetContent )
-- create proxy for def
-- or; local attrbs = p.createProxyPairs( def )
local attrbs = {}
for k,v in pairs( def ) do attrbs[k] = v end
local fieldsetContent = fieldsetContent or ""
local fieldDefs = attrbs["_fields"] or {}
-- show-on-select-trigger does not appear to work in Lua
local itemShowOnSelectTrigger = attrbs["show-on-select-trigger"] or nil
if itemShowOnSelectTrigger ~= nil then attrbs["data-wssos-value"] = itemShowOnSelectTrigger end
return mw.text.tag(
"fieldset",
p.removeNonFFAttributes( attrbs ),
fieldsetContent or ""
)
end
--[[
Get the action definition(s) selected by 'mode'
@return table
]]--
p.getSelectedActionDefs = function( page, mode, schemaTbl )
if mode == nil or mode == "" then return {} end
-- modes define which action tag must be used
local modes = schemaTbl.modes or nil
local actions = schemaTbl.actions or nil
if modes == nil or actions == nil then return {} end
local actionIds = modes[mode] or nil
if actionIds == nil then return {} end
local actionDefs = {}
for _,id in ipairs( actionIds ) do
table.insert(actionDefs, actions[id] )
end
return actionDefs
end
--local actionTags = p.setupActionTagsNew( page, actionDefs, fieldDefinitions )
p.setupActionTags = function( page, actionDefs, fieldDefinitions )
local actionTags = ""
for _,def in ipairs( actionDefs ) do
actionTags = actionTags .. p.createActionTag( page, def, fieldDefinitions )
end
return actionTags
end
--[[
DEPRECATED - set up _create or _edit elements
@page
@mode
@schemaTbl
@fieldDefinitions
]]--
p.setupActionTagsOld = function( page, mode, schemaTbl, fieldDefinitions )
if mode == nil or mode == "" then return "" end
--if mode ~= "create" and mode ~= "edit" then return "" end
-- modes define which action tag must be used
local modes = schemaTbl.modes or nil
local actions = schemaTbl.actions or nil
if modes == nil or actions == nil then return "" end
local actionIds = modes[mode] or nil
if actionIds == nil then return "" end
local actionTags = ""
for _,id in ipairs( actionIds ) do
local def = actions[id]
actionTags = actionTags .. p.createActionTag( page, def, fieldDefinitions )
end
return actionTags
--[[ older approach
if mode == "new" then
local actionDefs = schemaTbl.new or {}
for _,def in ipairs(actionDefs) do
actionTags = actionTags .. p.createActionTag( page, def, fieldDefinitions )
end
end
]]--
end
--[[
@actionDefs table
@fieldDefinitions table
@todo table
]]--
p.maybeSubtractFromFieldDefinitions = function( actionDefs, fieldDefs )
-- first get excludable fields from action defs
local excludableFields = {}
for _,def in ipairs( actionDefs ) do
if def["_excludeIds"] ~= nil then
for i,field in ipairs( def["_excludeIds"] ) do
table.insert( excludableFields, field )
end
end
--[[
if def["_excludeFields"] ~= nil then
for i,field in ipairs( def["_excludeFields"] ) do
table.insert( excludableFields, field )
end
end
--]]
end
-- no change
if excludableFields == {} then return fieldDefs end
local newFieldDefs = {}
for _,def in ipairs( fieldDefs ) do
local id = def["_id"] or ""
-- ocal name = def["name"] or ""
--local fieldName = string.gsub( mw.text.trim( name ), "%[%]", "" )
if p.isValueInSequentialTable( id, excludableFields ) then
-- do nothing
else
table.insert( newFieldDefs, def )
end
end
return newFieldDefs
end
--[[
Set up a _create or _edit element
@page string
@attrs table (read-only, assoc)
@fieldDefs table (read-only)
]]--
p.createActionTag = function( page, attributes, fieldDefs )
-- create proxy for read-only attributes
local attrbs = {}
for k,v in pairs( attributes ) do attrbs[k] = v end
if attrbs["mwwrite"] == nil then
attrbs["mwwrite"] = page
end
if attrbs["mwfields"] == nil then
local fieldsTbl = p.getFieldNames( fieldDefs )
-- new: _excludeFields => _excludeIds
if attrbs["_excludeIds"] ~= nil then
-- when using multiple _creates you may want to exclude specific fields
-- local invalidFields = mw.text.split( attrbs["_excludeIds"], "," )
fieldsTbl = p.removeValuesFromSequentialTable( fieldsTbl, attrbs["_excludeIds"] )
end
attrbs["mwfields"] = table.concat( fieldsTbl, "," )
end
-- default tag is _create
local tag = "_create"
if attrbs["_type"] ~= nil then
tag = attrbs["_type"]
end
if attrbs["mwformat"] == "json" and attrbs["mwtemplate"] == nil then
attrbs["mwtemplate"] = "wsnone"
end
local attrbs = p.removeNonFFAttributes(attrbs)
return mw.text.tag( tag, attrbs, false )
end
--[[
Get the field names (as a table) so that they can be added to 'mwfields'
includes field names nested in a fieldset
@fields table|nil
@return table
]]--
p.getFieldNames = function( fields )
if fields == nil then return {} end
local fieldNames = {}
for _,field in ipairs(fields) do
if field["_type"] == "fieldset" then
--recursive
local fieldsetNames = p.getFieldNames( field["_fields"] )
for _,fieldsetName in ipairs( fieldsetNames ) do
table.insert(fieldNames, fieldsetName)
end
elseif field.name ~= nil then
-- remove [] if any
local fieldName = string.gsub( mw.text.trim(field.name), "%[%]", "" )
table.insert(fieldNames, fieldName)
end
end
return fieldNames
end
--[[
Remove parameters that are not FlexForm attributes
i.e. removes each parameter whose key begins with an underscore
]]--
p.removeNonFFAttributes = function( attributes )
local newAttributes = {}
for k,v in pairs(attributes) do
if ( string.sub( k, 1, 1 ) ~= "_" ) then
newAttributes[k] = v
end
end
return newAttributes
end
--[[
revise!
]]
p.removeValuesFromSequentialTable = function( tbl, invalidFields )
--[[ DO NOT DO THIS:
for _,nonValid in ipairs(invalidFields) do
for i,v in ipairs(tbl) do
if v == nonValid then
table.remove( tbl, i )
end
end
end
return tbl
]]--
local newTable = {}
for _,field in ipairs( tbl ) do
if p.isValueInSequentialTable( field, invalidFields ) then
--do nothing
else
table.insert( newTable, field )
end
end
return newTable
--[[
-- compare: remove select fieldDefinitions in reverse order
for i = #fieldDefs, 1, -1 do
local name = fieldDefs[i]["name"] or ""
local fieldName = string.gsub( mw.text.trim( name ), "%[%]", "" )
mw.log( "fieldName: " .. fieldName )
for _,field in ipairs( excludableFields ) do
if fieldName == field then
mw.log( "equal to excludable field: " .. field )
fieldDefs[i] = nil
end
end
end
]]--
end
p.isValueInSequentialTable = function( val, tbl )
for _,v in ipairs( tbl ) do
if v == val then return true end
end
return false
end
p.printAssocTable = function( tbl )
local str = ""
for k,v in pairs(tbl) do
str = str .. k .. " = " .. v .. ". "
end
return str
end
--[[
Create standardised prefill table as k/v pairs from
either pre-existing content (JSON, wikitext) when editing
or defaults when creating a page
For structural differences between JSON/wiki template, see getSlotContent()
@sourceFormat
@slotContent table|nil
@defaultStr
@defaultSep
@return table
]]--
p.createPrefillTable = function( sourceFormat, slotContent, defaultStr, defaultsSep )
--mw.log( "createPrefillTable. Type of slotContent = " .. type(slotContent) )
local slotContent = slotContent or nil
local defaults = defaults or nil
local prefillTbl = {}
if slotContent ~= nil and sourceFormat == "json" then
prefillTbl = slotContent
elseif slotContent ~= nil and slotContent[1] ~= nil and sourceFormat == "wiki" then
-- note ["_text"]
for k,v in pairs( slotContent[1] ) do
-- slotData format
prefillTbl[k] = v["_text"] or nil
end
elseif defaultStr ~= nil and defaultStr ~= "" then
-- new page
mw.log( "defaults: " .. defaultStr )
local defaults = mw.text.split( defaultStr, defaultsSep )
for _,v in ipairs( defaults ) do
local pair = mw.text.split( v, "=" )
if pair[2] ~= nil then
prefillTbl[ mw.text.trim( pair[1] ) ] = mw.text.trim( pair[2] )
end
end
end
return prefillTbl
end
--[[
Transform user-provided string into a table (k/v)
@varStr string
@sep string
@return table
]]--
p.createVarsTable = function( varStr, sep )
if varStr == "" then return {} end
local sep = sep or "###"
local vars = mw.text.split( varStr, sep )
local varTbl = {}
for _,v in ipairs( vars ) do
local pair = mw.text.split( v, "=" )
if pair[2] ~= nil then
varTbl[ mw.text.trim( pair[1] ) ] = mw.text.trim( pair[2] )
end
end
return varTbl
end
p.replaceVariables = function( str, varsTbl )
if str == "" or varsTbl == {} then return str end
--def["query"]
-- vars def["_vars"]
-- string.gsub( def.name, "%[%]", "" )
local newStr = str
for k,v in pairs(varsTbl) do
newStr = string.gsub( newStr, k, v )
end
mw.log( "variables replaced: " .. newStr )
return newStr
end
--[[ Create proxy for read-only indexed table ]]--
p.createProxyIpairs = function( tbl )
local newTbl = {}
for _,v in ipairs( tbl ) do table.insert( newTbl, v ) end
return newTbl
end
--[[ Create proxy for read-only associative table ]]--
p.createProxyPairs = function( tbl )
local newTbl = {}
for k,v in pairs( tbl ) do newTbl[k] = v end
return newTbl
end
return p
