Module:GHS_phrases
This module is subject to page protection. It is a highly visible module in use by a very large number of pages, or is substituted very frequently. Because vandalism or mistakes would affect many pages, and even trivial editing might cause substantial load on the servers, it is protected from editing. |
This module depends on the following other modules: |
- Implements templates {{GHS phrases}} (talk) (tests), {{H-phrases}} and {{P-phrases}}.
List all phrasesList all phrases
List of H-phrasesList of H-phrases
List of P-phrasesList of P-phrases
List formatsList formats
|listtype=abbr
(default)- P201, P302+P334, P502
Full abbr listFull abbr list
FeaturesFeatures
Hide DoublesHide Doubles
Repeated phrase codes are hidden:
{{GHS phrases|H400|H401|H401|H401}}
→ H400, H401
Omit RulesOmit Rules
|omit=true
(default)
Omit Rules: when the keep ID is present, do not show the omit ID phrase
|
{{GHS phrases|H314|H318}}
→ H314
All module functionsAll module functions
TrackingTracking
- First Help page: Template:GHS phrases (including documentation)
- Category:GHS errors (0) (includes: "unknown parameter used")
- Category:GHS warnings (1)
See alsoSee also
- Main article: Globally Harmonized System of Classification and Labelling of Chemicals (GHS);
- {{Chembox}}, {{Chembox Hazards}}
- Source: "Globally Harmonized System of Classification and Labelling of Chemicals" (pdf). 2021. Annex 3: Codification of Statements and Pictograms (pp 268–385).
- "Globally Harmonized System of Classification and Labelling of Chemicals" (pdf). 2021. Annex 3: Codification of Statements and Pictograms (pp 268–385).
--------------------------------------------------------------------------------
-- Module:GHS phrases
--
-- main: reads GHS parameters (arguments like "H301", "P401")
-- and returns for each (listtype='abbr'):
-- phraseID visible; formal phrase text as <abbr title="...">
-- setID = "H" or "P"
-- phraseID = e.g. "H201", "P231+P234"
-- phrase text read from array tables in [[Module:GHS phrases/data]]
--
-- Implements: [[Template:GHS phrases]]
-- Helppage: [[Template:GHS phrases]]
-- Error category: [[Category:GHS errors]], [[Category:GHS warnings]] (mainspace pages only)
--
-- Also:
-- listAll(), numberOfPhrases(), listOmitRules(),
-- listtype, omit
--------------------------------------------------------------------------------
require('strict')
local r = {} -- "r" for return, so no confusion with setID P
local GHSdata = mw.loadData('Module:GHS phrases/data')
local getArgs = require('Module:Arguments').getArgs
local tTools = require('Module:TableTools')
local yesno = require('Module:Yesno')
local tArgName = {} -- named parameters (setid, omit, listtype)
local tMessagesToShow = {} -- the tail: Preview, Categories
--------------------------------------------------------------------------------
-- wlHelpPage
--
-- Formats page as [[Helppage#Section|Label]]
-- by default, sLabel == sSection
--------------------------------------------------------------------------------
local function wlHelpPage(sSection, sLabel)
local sHelpPage = 'Template:GHS phrases'
if sLabel == nil then sLabel = sSection end
if (sLabel or '') == '' then
sLabel = ''
else
sLabel = '|' .. sLabel
end
if (sSection or '') == '' then
sSection = ''
else
sSection = '#' .. sSection
end
return '[[' .. sHelpPage .. sSection .. sLabel .. ']]'
end
--------------------------------------------------------------------------------
-- addErrorCategory
--
-- Formats as [[Category:GHS errors|catsort]]
-- or '' when in other namespace.
-- sCatsort option using: H, P, _
--------------------------------------------------------------------------------
local function addErrorCategory(sCatsort)
local pagetype = require('Module:Pagetype').main
local wlErrCat = ''
if pagetype() == 'article' then -- mainspace only
if sCatsort == nil then sCatsort = tArgName['setID'] end
if sCatsort == '' then
wlErrCat = '[[Category:GHS errors]]'
else
wlErrCat = '[[Category:GHS errors|' .. sCatsort .. ']]'
end
else
return ''
end
table.insert(tMessagesToShow, wlErrCat)
return
end
--------------------------------------------------------------------------------
-- addWarningCategory
--
-- Formats as [[Category:GHS warnings|catsort]]
-- mainspace only, or '' when in other namespace.
-- sCatsort option using: H, P, U, ?, D, O
--------------------------------------------------------------------------------
local function addWarningCategory(sCatsort)
local pagetype = require('Module:Pagetype').main
if sCatsort == nil then sCatsort = tArgName['setID'] end
local wlWarnCat = ''
if pagetype() == 'article' then -- mainspace only
if sCatsort == '' then
wlWarnCat = '[[Category:GHS warnings]]'
else
wlWarnCat = '[[Category:GHS warnings|' .. sCatsort .. ']]'
end
else
return
end
table.insert(tMessagesToShow, wlWarnCat)
return
end
--------------------------------------------------------------------------------
-- addPreviewMsg
--------------------------------------------------------------------------------
local function addPreviewMsg(sMsg)
local previewWarn = require('Module:If preview')._warning
table.insert(tMessagesToShow, previewWarn({sMsg}))
return
end
--------------------------------------------------------------------------------
-- showPreviewMsg
--
-- show table tMessagesToShow
-- preview-messages and errorcat
-- all namespaces
--------------------------------------------------------------------------------
local function showPreviewMsg()
if tTools.size(tMessagesToShow) > 0 then
return table.concat(tMessagesToShow, '')
else
return ''
end
end
--------------------------------------------------------------------------------
-- applyRemoveDuplicates
--
-- returns edited table, with double Codes removed
-- adds warning with codes.
-- base table tArgs is walked through by a iwalker that reads a singel code,
-- then a ikiller checks the upward part of the same table to delete all copies
-- ikiller starts at end of table, walks towards iwalker; then tArgs is compressed
-- iwalker steps 1 up in the freshly compressed table
-- Used: iArgs is sorted, and order stays same. compress does not change that.
--------------------------------------------------------------------------------
local function applyRemoveDuplicates(tArgs)
local iR, iK -- iR = reader, iK = killer
local hit = false
iR = 1
while iR < #tArgs do
iK = #tArgs -- will be counting downwards
while iK > iR do
if tArgs[iK] == tArgs[iR] then
hit = true
addPreviewMsg('Duplicate removed: ' .. tArgs[iR])
table.remove(tArgs, iK)
tTools.compressSparseArray(tArgs)
end
iK = iK - 1
end
tTools.compressSparseArray(tArgs)
iR = iR + 1
end
if hit then
addWarningCategory('D')
end
return tArgs
end
--------------------------------------------------------------------------------
-- applyOmitRules
--
-- returns edited table, with Omit phraseID's removed
-- Omit rule is per GHS_Rev9E_0.pdf (2021)
--------------------------------------------------------------------------------
local function applyOmitRules(tArgs)
local tRules = GHSdata['tOmitRules']
local hit = false
for keep, omit in pairs(tRules) do
if tTools.inArray(tArgs, omit) then
if tTools.inArray(tArgs, keep) then
hit = true
for i, k in pairs(tArgs) do
if k == omit then
table.remove(tArgs, i)
end
end
addPreviewMsg(wlHelpPage('Omit Rules') .. ': keep ' .. keep .. ', omit ' .. omit)
end
end
end
if hit then
tTools.compressSparseArray(tArgs)
addWarningCategory('O')
end
return tArgs
end
--------------------------------------------------------------------------------
-- label H-phrases or P-phrases
--------------------------------------------------------------------------------
local function PHlabel()
if tArgName['setID'] == 'GHS' then
return 'GHS phrases'
else
return tArgName['setID'] .. '-phrases'
end
end
--------------------------------------------------------------------------------
-- inMono
--
-- Use mono font-family (from: Template:Mono)
--------------------------------------------------------------------------------
local function inMono(s)
if s == nil then s = '' end
return '<span class="monospaced" style="font-family: monospace;">' .. s .. '</span>'
end
--------------------------------------------------------------------------------
-- wlInlineTag
--
-- Returns <sup>[?]</sup> with wikilink to [[helppage#section|errormessage]]
--------------------------------------------------------------------------------
local function wlInlineTag(phraseID)
local sMsg
sMsg = '<sup><span class="noprint Inline-Template">[<i>'
.. wlHelpPage(PHlabel(), '<span title="'
.. PHlabel() .. ': '
.. phraseID
.. ' not found'
.. '">?</span>')
.. '</i>]</span></sup>'
return sMsg
end
--------------------------------------------------------------------------------
-- errorPhraseIDnotFound
--
-- Returns single value when error (not found in list):
-- plain value + inline warning [?] (linked) + error cat (mainsp) + preview warning
--------------------------------------------------------------------------------
local function errorPhraseIDnotFound(phraseID)
if phraseID == nil then phraseID = '' end
local inlineTag = wlInlineTag(phraseID)
local previewMsg = wlHelpPage(PHlabel()) .. ': \"' .. phraseID .. '\" not found'
addPreviewMsg(previewMsg)
addErrorCategory()
return phraseID .. inlineTag
end
--------------------------------------------------------------------------------
-- errorHPsetIDnotFound
--
-- setID H or P could not be found
--------------------------------------------------------------------------------
local function errorHPsetIDnotFound()
local sMsg
sMsg = wlHelpPage('', PHlabel())
.. ': "H" or "P" set id not found'
.. ' (please use form like "|H200" or "|P300+P301")'
addPreviewMsg(sMsg)
addErrorCategory('?')
return showPreviewMsg()
end
--------------------------------------------------------------------------------
-- errorHPsetIDmissing
--
-- parameter |setid= to be used
--------------------------------------------------------------------------------
local function errorHPsetIDmissing()
local sMsg
sMsg = wlHelpPage( '', PHlabel())
.. ': "H" or "P" set id not found,'
.. ' please use |setid=... (H or P)'
addPreviewMsg(sMsg)
return
end
--------------------------------------------------------------------------------
-- formatPhraseAbbr
--
-- format phraseID and text, for abbr-form (infobox list form)
--------------------------------------------------------------------------------
local function formatPhraseAbbr(phraseID, sPhrase)
return '<abbr class="abbr" title=" ' .. phraseID .. ': ' .. sPhrase .. '">'
.. phraseID
.. '</abbr>'
end
--------------------------------------------------------------------------------
-- formatPhraseInline
--
-- format phraseID and text, for inline form (in sentence)
-- adds "quotes"
--------------------------------------------------------------------------------
local function formatPhraseInline(phraseID, sPhrase)
return inMono(phraseID) .. ': \"' .. sPhrase .. '\"'
end
--------------------------------------------------------------------------------
-- formatPhraseList
--
-- as inline, but no "quotes" added.
--------------------------------------------------------------------------------
local function formatPhraseList(phraseID, sPhrase)
return inMono(phraseID) .. ': ' .. sPhrase
end
--------------------------------------------------------------------------------
-- getSetID
--
-- Determines setID (expected either 'H' or 'P')
-- First route is: read |setid=
-- When |setid= is not set,
-- it looks for a first parameter that has an H of P prefix (in |P201|P202|...)
-- when not found, 'GHS' is retured
-- In one call, P and H numbers can *not* be mixed
-- so "|H201|P202|" will cause error "P202 not found" (... in H-list)
--------------------------------------------------------------------------------
local function getSetID(tArgs)
local setIDfound = 'GHS'
local paramsetID = tArgs['setid'] or nil
if (paramsetID ~= nil) and (paramsetID == 'P' or paramsetID == 'H') then
setIDfound = paramsetID
else
local initial = nil
for i, v in ipairs(tArgs) do
initial = mw.ustring.match(v, '^[PH]')
if initial ~=nil then
setIDfound = initial
break
end
end
end
return setIDfound
end
--------------------------------------------------------------------------------
-- getListType
--
-- Checks list format, including those from Module:List
--------------------------------------------------------------------------------
local function getListType(tArgs)
local listTypes = {
['abbr'] = true,
['bulleted'] = true,
['unbulleted'] = true,
['horizontal'] = true,
['ordered'] = true,
['horizontal_ordered'] = true,
['horizontal ordered'] = true,
['inline'] = true
}
local sListType = tArgs['listtype'] or 'abbr'
if sListType == '' or sListType == 'abbr' then
return 'abbr'
elseif listTypes[sListType] == true then
if sListType == 'horizontal ordered' then
sListType = 'horizontal_ordered'
end
return sListType
else
sListType = 'abbr'
end
return sListType
end
--------------------------------------------------------------------------------
-- getDoOmitRules
--------------------------------------------------------------------------------
local function getDoOmitRules(tArgs)
local b = yesno(tArgs['omit'], true)
if b == nil then b = true end
return yesno(b, true)
end
--------------------------------------------------------------------------------
-- prepareArgs
--
-- First: determine setID (from |setID= OR from prefixes in parameters)
-- Then: clean up & format phrase IDs (=unnamed parameters)
-- remove bad characters, create H/P pattern "H201", "P310+P302"
-- straight array, no nil's, sorted
--------------------------------------------------------------------------------
local function prepareArgs(tArgs)
tArgName['setID'] = getSetID(tArgs)
tArgName['listtype'] = getListType(tArgs)
tArgName['omit'] = getDoOmitRules(tArgs)
tArgs = tTools.compressSparseArray(tArgs) -- removes all named args
if string.len(tArgName['setID']) == 1 and #tArgs > 0 then
for i, v in ipairs(tArgs) do
v = mw.text.decode(v)
v = mw.ustring.gsub(v, '[^%d%+A-Za-z]', '')
v = mw.ustring.gsub(v, '^(%d)', tArgName['setID'] .. '%1')
v = mw.ustring.gsub(v, '%+(%d)', '+' .. tArgName['setID'] .. '%1')
tArgs[i] = v
end
table.sort(tArgs)
end
return tArgs
end
--------------------------------------------------------------------------------
-- listAll
--
-- Returns wikitable rows for each phrase id.
-- requires |setID=P/H
-- returns full list, all phrases, for a setID
-- 2-columns wikitable, sorted, sortable, anchor like "H201" for each
--------------------------------------------------------------------------------
function r.listAll(frame)
local newArgs = getArgs(frame)
local tL = {}
prepareArgs(newArgs)
local tRead
if tArgName['setID'] == 'H' then
tRead = GHSdata['Hphrases']
elseif tArgName['setID'] == 'P' then
tRead = GHSdata['Pphrases']
else
errorHPsetIDmissing()
return showPreviewMsg()
end
-- Intermediate table t2 to maintain order; read from original table (/data)
local t2 = {}
local iPh
for s, v in pairs(tRead) do
iPh = tonumber(mw.ustring.match(s, '[PH](%d%d%d)'))
if string.len(s) > 4 then
iPh = tTools.size(t2) + 1
end
table.insert(t2, iPh, s)
end
t2 = tTools.compressSparseArray(t2)
table.sort(t2)
local sTR, v, sAnchor
-- i = array index, s = phraseID, v = phrase text
for i, s in ipairs(t2) do
v = tRead[s]
sAnchor = '<span class="anchor" id="' .. s .. '"></span>'
sTR = '|- ' .. sAnchor .. '\n| datasortvalue="' .. i .. '" | <span style="font-family: monospace;">' .. s .. '</span> || ' .. v
table.insert(tL, sTR)
end
return table.concat(tL, '\n')
end
--------------------------------------------------------------------------------
-- numberOfPhrases
--
-- Documentation
-- requires |setID=H/P
-- Returns number of phrases, in format
-- "GHS H-phrases (123)"
--------------------------------------------------------------------------------
function r.numberOfPhrases(frame)
local newArgs = getArgs(frame)
prepareArgs(newArgs)
local iT
if tArgName['setID'] == 'H' then
iT = tTools.size(GHSdata['Hphrases'])
elseif tArgName['setID'] == 'P' then
iT = tTools.size(GHSdata['Pphrases'])
else
errorHPsetIDmissing()
return showPreviewMsg()
end
return 'GHS ' .. PHlabel() .. ' <span style="font-weight: normal;">(' .. tostring(iT) .. ')</span>'
end
--------------------------------------------------------------------------------
-- listOmitRules
--
-- self-documentation
--------------------------------------------------------------------------------
function r.listOmitRules()
local tRules = GHSdata['tOmitRules']
local tL = {}
local s
s = wlHelpPage('Omit Rules')
.. ': when the <i>keep</i> ID is present, do not show the <i>omit</i> ID phrase'
table.insert(tL, s)
for keep, omit in pairs (tRules) do
s = '• keep ' .. inMono(keep) .. ', omit ' .. inMono(omit)
table.insert(tL, s)
end
return table.concat(tL, '<br/>')
end
--------------------------------------------------------------------------------
-- _main
--
-- processes setID (H, P) and phrase codes
-- error: setID not P, H
-- code not found
-- cannot mix H and P phrases
-- reads phrases from /data H or P phrases tables
-- formats phrase (abbreviation, abbr-title, phraseID)
--------------------------------------------------------------------------------
function r._main(tArgs)
tArgs = prepareArgs(tArgs)
if #tArgs == 0 then
return showPreviewMsg() -- no content
elseif tArgName['setID'] == 'GHS' then
return errorHPsetIDnotFound()
end
tArgs = applyRemoveDuplicates(tArgs)
if tArgName['omit'] then
tArgs = applyOmitRules(tArgs)
end
local formatterF
if tArgName['listtype'] == 'abbr' then
formatterF = formatPhraseAbbr
elseif tArgName['listtype'] == 'inline' then
formatterF = formatPhraseInline
else --- Module:List options
formatterF = formatPhraseList
end
local tReadD = {}
if tArgName['setID'] == 'H' then
tReadD = GHSdata['Hphrases']
elseif tArgName['setID'] == 'P' then
tReadD = GHSdata['Pphrases']
else
return showPreviewMsg()
end
local sPhrase
local tR = {}
for i, v in ipairs(tArgs) do
sPhrase = tReadD[v]
if sPhrase == nil then
table.insert(tR, errorPhraseIDnotFound(tostring(v)))
else
table.insert(tR, formatterF(v, sPhrase))
end
end
if tArgName['listtype'] == 'abbr' then
return table.concat(tR, ', ') .. showPreviewMsg()
elseif tArgName['listtype'] == 'inline' then
return table.concat(tR, ', ') .. showPreviewMsg()
else
local mList = require('Module:List')
return mList[tArgName['listtype']](tR) .. showPreviewMsg()
end
end
--------------------------------------------------------------------------------
-- main
--
-- handles template input frame, then calls generic _main() function
-- To be invoked from {{template}}
--------------------------------------------------------------------------------
function r.main(frame)
local newArgs = getArgs(frame)
return r._main(newArgs)
end
return r