Module:Interlinear/sandbox2/gcl

local p = {}

local data = mw.loadData("Module:Interlinear/data")

local gloss_override = {} -- for custom gloss abbreviations

local getArgs = require("Module:Arguments").getArgs

local yesno = require("Module:Yesno")

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

-- Almost-global variables

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

local frame, glossing_type, displaying_messages, msg

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

-- General settings

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

local conf = {

-- glossing abbreviations matching this pattern

-- will not be rendered in small caps

GlossSmallCapsExclude = "^[AOPS]$",

-- if set to "label" gloss abbreviations are formatted as an with the "label" appearing in a tooltip

-- if set to "wikilink" the abbreviation is formatted as a wikilink to the relevant wikipedia article

-- if set to "none" abbreviations aren't formatted at all

GlossingType = "label",

ErrorCategory = "Category:Pages with errors in interlinear text",

AmbiguousGlossCategory = "Category:Articles with ambiguous glossing abbreviations",

MessageGlossingError = "Error(s) in interlinear glossing",

--e.g. G4 '4th gender' or CL7 'class 7'

combining_gender_numbers = "[0-9][0-9]?$",

combining_gender_prefixes = {G = "gender", CL = "class"},

combining_person = {

["1"] = "first person",

["2"] = "second person",

["3"] = "third person"

},

combining_number = {

S = "singular", SG = "singular",

P = "plural", PL = "plural",

D = "dual", DU = "dual",

TRI = "trial"

},

combining_gender = {F = "feminine", M = "masculine", N = "neuter"},

-- these are the non-all-upper-case strings

-- that will be recognised as glossing abbreviations

LowerCaseGlosses = {

["1sg"] = true, ["2sg"] = true, ["3sg"] = true,

["1du"] = true, ["2du"] = true, ["3du"] = true,

["1pl"] = true, ["2pl"] = true, ["3pl"] = true,

["Fsg"] = true, ["Fpl"] = true,

["Msg"] = true, ["Mpl"] = true,

},

ErrorHelpLocation = "Template:Interlinear"

}

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

-- CSS styles and classes

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

conf.class = {

GlossAbbr = "gloss-abbr",

GlossAbbrAmb = "gloss-abbr-ambiguous",

GlossAbbrError = "gloss-abbr-error",

ErrorMessage = "error"

}

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

-- Sundry small functions

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

local function tidyCss(str)

-- trims quotation marks

str = mw.ustring.gsub(str, '^["\']*(.-)["\']*$', "%1")

-- appends ";" if missing

if mw.ustring.sub(str, -1) ~= ";" then

str = str .. ";"

end

return str

end

local function highlight(text)

if text then

return '' .. text .. ""

else

return ""

end

end

local function help_link(anchor)

if anchor then

return " (help)"

else

return ""

end

end

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

-- The following two functions update the glossing settings based on the received

-- template arguments. set_global_glossing_settings() updates the global settings

-- that are valid for all gloss abbreviations. set_glossing_type()

-- returns the glossing type, which can vary between the different lines.

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

function set_global_glossing_settings(a)

local style = ""

if a.style then

style = tidyCss(a.style)

end

if a.underline == "no" then

style = style .. "text-decoration: none;"

a.class = a.class .. " no_underline"

end

if a.small_caps == "no" then

style = style .. "font-variant:normal; text-transform: none;"

a.class = a.class .. " no_smallcaps"

end

if style ~= "" then

conf.style.GlossAbbr = conf.style.GlossAbbr .. style

end

end

function set_glossing_type(glossing)

if glossing then

local GlossingType

glossing = mw.ustring.lower(mw.text.trim(glossing))

if mw.ustring.find(glossing, "link") then

GlossingType = "wikilink"

elseif mw.ustring.find(glossing, "label")

or mw.ustring.find(glossing, "no link") then

GlossingType = "label"

elseif mw.ustring.find(glossing, "no abbr") then

GlossingType = "no abbr"

elseif yesno(glossing) == false then

GlossingType = nil

elseif yesno(glossing) then

GlossingType = conf.GlossingType

else

msg:add("error", 'Glossing type "' .. glossing .. '" not recognised')

end

return GlossingType

else

error("set_glossing_type: 'glossing' is nil or false", 2)

end

end

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

-- The UserMessages object contains and processes error messages and warnings

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

local UserMessages = {errors = {}, warnings = {}, gloss_messages = {}}

function UserMessages:add(msgtype, text, gloss)

if msgtype == "gloss_message" then

self.gloss_messages[gloss] = text

elseif msgtype == "warning" then

table.insert(self.warnings, text)

elseif msgtype == "non-repeating error" then

self.errors.nre = text

elseif msgtype == "ambiguous gloss" then

self.if_ambiguous_glosses = true

elseif msgtype == "error" then

table.insert(self.errors, text)

else

return error("UserMessages:add(): unknown message type", 2)

end

end

function UserMessages:print_errors()

local out = ""

local namespace = mw.title.getCurrentTitle().namespace

if next(self.errors) or self.warnings[1] then

local err_span = mw.html.create("span")

err_span:addClass(conf.class.ErrorMessage)

for _, v in pairs(self.errors) do

err_span:wikitext(" " .. v .. ";")

end

-- non-talk namespaces, excluding user pages;

-- if modifying please update the description on the category page

if namespace % 2 == 0 and namespace ~= 2 then

err_span:wikitext(conf.ErrorCategory)

end

out = tostring(err_span)

mw.addWarning(conf.MessageGlossingError)

end

if self.if_ambiguous_glosses then

if namespace == 0 then -- article namespace

-- this category will only track articles

out = out .. conf.AmbiguousGlossCategory

end

end

return out

end

function UserMessages:print_warnings()

local out = ""

-- Messages and warnings get displayed only if the page is being viewed in "preview" mode:

if displaying_messages

and (next(self.gloss_messages) or next(self.warnings)) then

local div = mw.html.create("div")

div:addClass("interlinear-preview-warning")

:cssText("border: 1px solid #a2a9b1; background-color: #f8f9fa; width: 80%; padding: 0.2em;")

:wikitext("This message box is shown only in preview:"):newline()

for _, v in ipairs(self.warnings) do

local p = div:tag("p")

:addClass(conf.class.ErrorMessage)

:wikitext(v)

end

if self.gloss_messages then

div:wikitext(

"

To change any of the following default expansions, see the template's documentation:

"

)

end

for _, v in pairs(self.gloss_messages) do

div:wikitext("

" .. v .. "

")

end

out = out .. "\n\n" .. tostring(div)

end

return out

end

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

-- gloss_lookup() receives a gloss abbreviation and tries to uncover its meaning.

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

function gloss_lookup(a, label, wikilink)

local _label, _wikilink, _lookup, source = nil, nil, nil, nil

if gloss_override[a] then

_lookup = gloss_override[a]

source = "local"

elseif data.abbreviations[a] then

_lookup = data.abbreviations[a]

end

if _lookup and _lookup.expansion ~= "" then

_label, _wikilink = _lookup.expansion, _lookup.wikipage

else

local prefix = mw.ustring.sub(a, 1, 1)

local suffix = mw.ustring.sub(a, 2)

if conf.combining_person[prefix] then -- is it of the form 1PL or 3FS?

_label = conf.combining_person[prefix]

local _suffix = conf.combining_number[suffix]

or conf.combining_gender[suffix]

if _suffix then

_label = _label .. ", " .. _suffix

else

local suffix1 = mw.ustring.sub(suffix, 1, 1)

local suffix2 = mw.ustring.sub(suffix, 2)

if conf.combining_gender[suffix1]

and conf.combining_number[suffix2] then

_label = _label .. ", " .. conf.combining_gender[suffix1] .. ", " .. conf.combining_number[suffix2]

else

_label = nil

end

end

elseif mw.ustring.match(suffix, conf.combining_gender_numbers) then -- cases like G4 = gender 4

local _i, _j = mw.ustring.find(a, conf.combining_gender_numbers)

local _pre = mw.ustring.sub(a, 1, _i - 1)

local _suff = mw.ustring.sub(a, _i)

if conf.combining_gender_prefixes[_pre] then

_label = conf.combining_gender_prefixes[_pre] .. " " .. _suff

end

elseif prefix == "N" then -- dealing with cases like NPST = non-past

local s = gloss_override[suffix] or data.abbreviations[suffix]

if s ~= nil and not s.ExcludeNegation then

_label = "non-" .. s.expansion

_wikilink = s.wikipage

end

s = nil

end

end

if _label == "" then _label = nil end

if _wikilink == "" then _wikilink = nil end

if not label then label = _label end

if not wikilink then wikilink = _wikilink end

return label, wikilink, source

end

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

-- Section transclusion

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

local page_content = nil -- lazy initilization

local function get_section(section_name)

if page_content == nil then

local current_title = mw.title.getCurrentTitle()

page_content = current_title:getContent()

end

if page_content then

if mw.ustring.find(page_content, section_name, 1, true) then

return frame:preprocess('{{#section:{{FULLPAGENAME}}|' .. section_name .. '}}')

end

end

return ''

end

local function set_custom_glosses(list)

local abbs = mw.text.split(list, '[;\n\t]')

for _,v in pairs(abbs) do

local gloss = mw.text.split(v, ':')

local a = mw.text.trim(gloss[1])

if a and a ~= "" then

gloss_override[a] = {}

gloss_override[a].expansion = gloss[2]

gloss_override[a].wikipage = gloss[3]

end

end

end

--this looks for a list of glossing abbreviations on the page

-- that transcludes the template:

function view_abbreviations_list()

local _ablist_section = get_section('list-of-glossing-abbreviations')

if _ablist_section and _ablist_section ~= "" then

local _a = mw.ustring.gsub(_ablist_section, '', '') -- strips off the div tags

set_custom_glosses(_a)

end

end

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

-- format_gloss() calls gloss_lookup() to find the meaning of a gloss

-- abbreviation, which it then proceeds to format

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

function format_gloss(gloss, label, wikilink)

if string.sub(gloss, 1, 3) == "000" then -- checks for a common component of exposed strip markers (see :mw:Strip marker)

return gloss

end

local gloss2 = mw.ustring.gsub(gloss, "<.->", "") -- remove any html fluff

gloss2 = mw.ustring.gsub(gloss2, "%'%'+", "") -- remove wiki bold/italic formatting

gloss2 = mw.text.trim(mw.ustring.upper(gloss2))

if not (label or wikilink)

or (not label and glossing_type == "label")

or (not wikilink and glossing_type == "wikilink") then

if glossing_type ~= "no abbr" then

label, wikilink, source = gloss_lookup(gloss2, label, wikilink)

end

end

local gloss_node

if glossing_type == "no abbr" then

gloss_node = mw.html.create("span")

else

gloss_node = mw.html.create("abbr")

end

gloss_node:addClass(conf.class.GlossAbbr)

if label or wikilink then

if mw.ustring.match(gloss, conf.GlossSmallCapsExclude) then

gloss_node:addClass("gloss-not-smallcaps")

end

-- if not mw.ustring.match(gloss, "%l")

-- and not mw.ustring.match(gloss, conf.GlossSmallCapsExclude) -- excluding glosses that contain lower-case characters

-- then -- and also excluding A, O etc. from rendering in small caps

--gloss_node:attr("style", conf.style.GlossAbbr)

-- end

local abbr_label = label or wikilink

-- if label then

-- abbr_label = label

-- else

-- abbr_label = wikilink

-- end

gloss_node:attr("title", abbr_label)

if source ~= "local" and data.abbreviations[gloss2] then

if data.abbreviations[gloss2].ambiguous then

gloss_node:addClass(conf.class.GlossAbbrAmb)

msg:add("ambiguous gloss")

end

end

if glossing_type == "wikilink" and wikilink then

gloss_node:wikitext("", gloss, "")

else

gloss_node:wikitext(gloss)

end

if source ~= "local" and displaying_messages then -- logging gloss lookups:

local message = ""

if label then

message = "assuming " .. gloss2 .. ' means "' .. abbr_label .. '";'

end

if glossing_type == "wikilink" and wikilink then

message = message .. " linking to " .. wikilink .. ";"

end

msg:add("gloss_message", message, gloss)

end

elseif glossing_type == "no abbr" then

gloss_node:wikitext(gloss)

else

if displaying_messages then

msg:add(

"warning",

"Gloss abbreviation " .. highlight(gloss2) .. " not recognised" .. help_link("gloss abbr")

)

end

msg:add("non-repeating error", "Unknown glossing abbreviation(s)" .. help_link("gloss abbr"))

gloss_node

:addClass(conf.class.GlossAbbrError)

:addClass("error")

:attr("title", gloss2 .. ": glossing abbreviation not found")

:wikitext(gloss)

end

return tostring(gloss_node)

end

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

-- The following function is called by Template:gcl and is used for formatting an individual glossing abbreviation

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

function gcl(gloss, label, wikilink)

if not gloss then

UserMessages:add("error", "No gloss supplied")

return UserMessages:print()

end

-- if a wikilink is supplied and glossing isn't set to 'label'...

if wikilink and not args.glossing then

glossing_type = "wikilink"

end -- .. then the wikilink will be formatted as such

if label == "" then label = nil end

if wikilink == "" then wikilink = nil end

return format_gloss(gloss, label, wikilink)

end

function p.gcl(f)

frame = f

local args = getArgs(frame, {

trim = true,

removeBlanks = false,

parentOnly = true,

wrappers = {"Template:Gcl"}

})

msg = UserMessages

set_global_glossing_settings {

style = args.style,

underline = args.underline,

small_caps = args["small-caps"]

}

view_abbreviations_list()

if not args.glossing then

glossing_type = conf.GlossingType -- a global variable

else

glossing_type = set_glossing_type(args.glossing)

end

local gloss, label, wikilink = args[1], args[2], args[3]

return gcl(gloss, label, wikilink)

-- if not gloss then

-- UserMessages:add("error", "No gloss supplied")

-- return UserMessages:print()

-- end

-- -- if a wikilink is supplied and glossing isn't set to 'label'...

-- if wikilink and not args.glossing then

-- glossing_type = "wikilink"

-- end -- .. then the wikilink will be formatted as such

-- if label == "" then label = nil end

-- if wikilink == "" then wikilink = nil end

-- local result = format_gloss(gloss, label, wikilink)

-- return result

end

return p