이 모듈에 대한 설명문서는 모듈:노멀가이각주/설명문서에서 만들 수 있습니다
-- 노멀가이각주 2026-08-18 v7.4.1
local p = {}
local function trim(value)
if value == nil then
return ''
end
return mw.text.trim(tostring(value))
end
local function getArgs(frame)
local parent = frame:getParent()
if parent then
return parent.args
end
return frame.args
end
local function hash(value)
return mw.hash.hashValue('md5', tostring(value or '')):sub(1, 16)
end
local function makeLink(target, text)
return '[[' .. target .. '|' .. text .. ']]'
end
local function isBooleanFlag(value)
value = mw.ustring.lower(trim(value))
return value == '1'
or value == 'yes'
or value == 'true'
or value == '예'
or value == '사용'
end
local function isReuseWord(value)
value = mw.ustring.lower(trim(value))
return value == '재사용'
or value == 'reuse'
or value == '같은각주'
or value == '반복'
end
local function errorMessage(message)
local node = mw.html.create('span')
:addClass('error')
:wikitext('노멀가이각주 오류: ' .. message)
return tostring(node)
end
local function getDefinitionData(args)
local arg1 = trim(args[1])
local arg2 = trim(args[2])
local label = trim(args['표시'])
if label == '' then
label = trim(args.label)
end
if label == '' then
label = trim(args['이름표'])
end
local content = trim(args['내용'])
if content == '' then
content = trim(args.content)
end
if arg2 ~= '' then
if label == '' then
label = arg1
end
if content == '' then
content = arg2
end
elseif content == '' then
content = arg1
elseif label == '' and arg1 ~= '' then
label = arg1
end
return label, content
end
local function getReuseData(args, reuseValue)
local arg1 = trim(args[1])
local arg2 = trim(args[2])
local arg3 = trim(args[3])
local label = trim(args['표시'])
if label == '' then
label = trim(args.label)
end
if label == '' then
label = trim(args['이름표'])
end
if label == '' and arg1 ~= '' then
label = arg1
end
if label == '' and not isBooleanFlag(reuseValue) then
label = reuseValue
end
local popupContent = trim(args['팝업'])
if popupContent == '' then
popupContent = trim(args['내용'])
end
if popupContent == '' then
popupContent = trim(args.content)
end
-- {{노멀가이각주|A|재사용|원문}}에서는 세 번째 인수가
-- 재사용 팝업에 표시할 원문이다.
if popupContent == '' and arg3 ~= '' then
popupContent = arg3
elseif popupContent == '' and arg2 ~= '' and not isReuseWord(arg2) then
popupContent = arg2
end
return label, popupContent
end
function p.note(frame)
local args = getArgs(frame)
local reuseValue = trim(args['재사용'])
local reuse = reuseValue ~= '' or isReuseWord(args[2])
local label
local content
if reuse then
label, content = getReuseData(args, reuseValue)
else
label, content = getDefinitionData(args)
end
if not reuse and content == '' then
return errorMessage('각주 내용이 입력되지 않았습니다.')
end
local referenceName = trim(args['이름'])
if reuse and referenceName == '' then
if isBooleanFlag(reuseValue) then
referenceName = label
else
referenceName = reuseValue
end
end
-- 표시 이름이 있는 각주는 같은 이름끼리 자동으로 묶는다.
if referenceName == '' and label ~= '' then
referenceName = label
end
if reuse and referenceName == '' then
return errorMessage('재사용할 각주의 이름이 없습니다.')
end
local title = mw.title.getCurrentTitle().prefixedText
local key = trim(args['키'])
if referenceName ~= '' and key ~= '' then
referenceName = referenceName .. '|key=' .. key
end
local noteHash
if referenceName ~= '' then
noteHash = hash('named:' .. referenceName)
else
noteHash = hash(table.concat({
'note',
title,
content,
key
}, ':'))
end
local bodyId = 'ngfn-body-' .. noteHash
local entryId = 'ngfn-entry-' .. noteHash
-- 모든 각주에 안전한 Cite 이름을 붙여 본문 표시가 언제나
-- Cite의 실제 목록 링크와 복귀 링크를 사용하게 한다.
local citeName
if referenceName ~= '' then
citeName = 'ngfn-' .. hash(referenceName)
else
citeName = 'ngfn-' .. noteHash
end
local referenceContent = ''
if not reuse then
local entry = mw.html.create('span')
:addClass('ngfn-list-entry')
-- Cite가 만드는 목록 번호와 무관한 고유 목적지다.
-- 팝업 링크는 이 요소를 직접 가리키므로 몇 번째 각주인지
-- Lua에서 추측할 필요가 없다.
entry:tag('span')
:addClass('ngfn-list-target')
:attr('id', entryId)
:attr('tabindex', '-1')
local listLabel = entry:tag('span')
:addClass('ngfn-list-label')
if label ~= '' then
listLabel
:addClass('ngfn-list-label-custom')
:wikitext(makeLink('#' .. bodyId, '[' .. label .. ']'))
else
listLabel
:addClass('ngfn-list-label-auto')
:wikitext(makeLink('#' .. bodyId, '​'))
end
entry:tag('span')
:addClass('ngfn-list-body')
:wikitext(content)
referenceContent = tostring(entry)
end
local reference
if reuse then
-- frame:extensionTag에 nil을 넣으면 빈 <ref></ref>가 되어 오류가 날 수 있다.
-- 이름은 MD5 기반 ASCII 문자열이므로 안전하게 self-closing ref를 만든다.
reference = frame:preprocess('<ref name="' .. citeName .. '" />')
else
local refArgs = {
name = citeName
}
reference = frame:extensionTag('ref', referenceContent, refArgs)
end
local root = mw.html.create('span')
:addClass('ngfn')
:addClass(reuse and 'ngfn-reuse' or 'ngfn-definition')
:attr('data-ngfn-version', '2026-08-18-v7.4.1')
if not reuse then
root:attr('id', bodyId)
end
if label ~= '' then
root:addClass('ngfn-custom')
end
-- 팝업은 각주 표시를 기준으로 항상 가운데 정렬한다.
-- 예전에 넣은 |정렬=오른쪽 같은 인수가 남아 있어도 무시한다.
root:addClass('ngfn-align-center')
root:wikitext(reference)
if label ~= '' then
root:tag('span')
:addClass('ngfn-custom-marker')
:wikitext('[' .. label .. ']')
end
-- 모바일에서는 실제 각주 링크와 미리보기 버튼을 분리한다.
root:tag('span')
:addClass('ngfn-mobile-open')
:attr('tabindex', '0')
:attr('role', 'button')
:attr('aria-label', '각주 미리보기')
:attr('title', '각주 미리보기')
:wikitext('ⓘ')
local popup = root:tag('span')
:addClass('ngfn-popup')
:attr('tabindex', '0')
:attr('role', 'dialog')
local popupLine = popup:tag('span')
:addClass('ngfn-popup-line')
local popupLinkText
if label ~= '' then
popupLinkText = '[' .. label .. ']'
else
-- 실제 번호는 Cite가 렌더링한 뒤 CSS가 sup.reference의 ID에서 읽는다.
popupLinkText = '<span class="ngfn-popup-label-auto">[각주]</span>'
end
-- MediaWiki는 생짜 <a> 출력을 문자로 표시할 수 있으므로
-- 내부 링크 문법으로 안전한 fragment 링크를 생성한다.
popupLine:tag('span')
:addClass('ngfn-popup-jump')
:wikitext(makeLink('#' .. entryId, popupLinkText))
local popupContent = content
if reuse and popupContent == '' then
popupContent = '동일한 각주입니다. 각주 표시를 눌러 목록에서 확인할 수 있습니다.'
end
popupLine:tag('span')
:addClass('ngfn-popup-content')
:wikitext(' ' .. popupContent)
-- 모바일 팝업 하단의 전용 닫기 버튼.
-- CSS의 :has()가 이 버튼의 포커스/터치를 감지해 팝업을 닫는다.
popup:tag('span')
:addClass('ngfn-mobile-close')
:addClass('mobile-close-btn')
:attr('tabindex', '0')
:attr('role', 'button')
:attr('aria-label', '각주 팝업 닫기')
:attr('title', '각주 팝업 닫기')
:css('display', 'none')
:wikitext('[닫기]')
root:tag('span')
:addClass('ngfn-dimmer')
:attr('tabindex', '0')
:attr('role', 'button')
:attr('aria-label', '각주 닫기')
:attr('title', '각주 닫기')
return tostring(root)
end
-- 실제로 실행 중인 모듈과 스타일을 문서에서 바로 확인한다.
-- 사용: {{#invoke:노멀가이각주|version}}
function p.version()
local node = mw.html.create('span')
:addClass('ngfn-version-check')
:wikitext('Lua v7.4.1 / CSS ')
node:tag('span')
:addClass('ngfn-version-css-fallback')
:wikitext('미적용')
return tostring(node)
end
function p.list(frame)
local references = frame:extensionTag('references', '')
local container = mw.html.create('div')
:addClass('ngfn-list')
:attr('id', 'ngfn-list')
:wikitext(references)
return tostring(container)
end
p.main = p.note
return p