익명
×
새 문서 만들기
여기에 문서 제목을 쓰세요:
We currently have 900 articles on 루리위키. Type your article name above or click on one of the titles below and start writing!



900Articles

모듈:푸른 저편의 포리듬/ 캐릭터 프로필: 두 판 사이의 차이

편집 요약 없음
편집 요약 없음
 
(같은 사용자의 중간 판 9개는 보이지 않습니다)
9번째 줄: 9번째 줄:
end
end


-- 시그니처 기술 수집 (시그니처기술1..10)
-- 안전한 HTML 인코딩 (데이터 출력 시 특수 문자 오류 방지)
local function safe_html_encode(s)
    if not s or s == '' then return '' end
    -- mw.text.html_encode 사용 시 오류 발생 가능성이 낮으므로 우선 사용
    if mw.text and mw.text.html_encode then
        return mw.text.html_encode(tostring(s))
    end
    -- 대체 인코딩 (mw.text 라이브러리 자체가 불안정할 경우 대비)
    s = tostring(s)
    s = s:gsub('&', '&')
    s = s:gsub('<', '&lt;')
    s = s:gsub('>', '&gt;')
    return s
end
 
-- 시그니처 기술 수집 및 HTML 리스트 반환
local function gather_techs(args)
local function gather_techs(args)
     local techs = {}
     local techs = {}
     for i = 1, 10 do
     for i = 1, 10 do
         local k = '시그니처기술' .. i
         local k = '시그니처기술' .. i
         if args[k] and args[k] ~= '' then
         local val = trim(args[k])
             table.insert(techs, trim(args[k]))
        if val ~= '' then
             table.insert(techs, val)
         end
         end
     end
     end
     return techs
    if #techs > 0 then
        local tstr = '<ul style="list-style:none; margin:0; padding:0 0 0 10px; font-size:95%;">'
        for i, tname in ipairs(techs) do
            local marker = '①'
            if i == 2 then marker = '②' end
            if i == 3 then marker = '③' end
            if i > 3 then marker = '•' end
            tstr = tstr .. string.format('<li style="margin-top:2px;">%s %s</li>', marker, safe_html_encode(tname))
        end
        tstr = tstr .. '</ul>'
        return tstr
    end
     return ''
end
end


-- 이미지 블록 (VN 먼저, 애니 다음). 파일명은 그대로 사용.
-- 이미지 블록 (위키텍스트 반환)
local function build_image_block(args)
local function build_image_block(args)
     local images = {}
     local main_file = trim(args['이미지_VN'] or args['이미지_애니'] or 'NoImage.png')
    if args['이미지_VN'] and args['이미지_VN'] ~= '' then
      
        table.insert(images, { title = "비주얼 노벨", file = trim(args['이미지_VN']) })
     local output = {}
     end
    table.insert(output, string.format('[[파일:%s|250px|center]]', mw.text.encode(main_file)))
     if args['이미지_애니'] and args['이미지_애니'] ~= '' then
    table.insert(output, '<div style="text-align:center; font-size:85%; margin-top:5px;">(애니 / VN 스위치)</div>')
        table.insert(images, { title = "애니메이션", file = trim(args['이미지_애니']) })
    end
    if args['이미지_추가'] and args['이미지_추가'] ~= '' then
        for img in string.gmatch(args['이미지_추가'], '([^,]+)') do
            img = trim(img)
            if img ~= '' then
                table.insert(images, { title = "추가", file = img })
            end
        end
    end
      
      
     if #images == 0 then
     return table.concat(output, '\n')
        return '[[파일:NoImage.png|200px]]'
    elseif #images == 1 then
        return string.format('[[파일:%s|200px]]', mw.text.encode(images[1].file))
    else
        local t = {}
        table.insert(t, '<tabber>')
        for i, im in ipairs(images) do
            local title = mw.text.escape(im.title)
            local file = mw.text.encode(im.file)
            table.insert(t, string.format('%s= [[파일:%s|200px]]', title, file))
            if i < #images then table.insert(t, '|-|') end
        end
        table.insert(t, '</tabber>')
        return table.concat(t, '\n')
    end
end
end


-- 이름 블록: 한글(첫줄) / 일어 → 영어(둘째줄, 일어 우선)
-- 이름 블록 (HTML 반환)
local function build_name_block(args)
local function build_name_block(args)
     local kr = trim(args['이름'] or '')
     local kr = trim(args['이름'] or '')
63번째 줄: 66번째 줄:
     local en = trim(args['이름_영어'] or '')
     local en = trim(args['이름_영어'] or '')
     local parts = {}
     local parts = {}
     if kr ~= '' then table.insert(parts, string.format('<div class="prf-name-kr">%s</div>', mw.text.escape(kr))) end
   
     if jp ~= '' or en ~= '' then
     if kr ~= '' then
        local second = ''
        table.insert(parts, string.format('<strong style="font-size:1.1em;">이름: %s</strong>', safe_html_encode(kr)))
        if jp ~= '' and en ~= '' then
    end
            second = mw.text.escape(jp) .. ' / ' .. mw.text.escape(en)
      
        else
    local sub_parts = {}
            second = mw.text.escape((jp ~= '' and jp) or en)
    if jp ~= '' then table.insert(sub_parts, safe_html_encode(jp)) end
        end
    if en ~= '' then table.insert(sub_parts, safe_html_encode(en)) end
         table.insert(parts, string.format('<div class="prf-name-sub">%s</div>', second))
   
    if #sub_parts > 0 then
         table.insert(parts, string.format('<div style="font-size:90%%; margin-top:2px;">%s</div>', table.concat(sub_parts, ' / ')))
     end
     end
     return table.concat(parts, '\n')
     return table.concat(parts, '\n')
77번째 줄: 82번째 줄:


-- 라벨+값 행 생성 (값 없으면 빈 문자열)
-- 라벨+값 행 생성 (값 없으면 빈 문자열)
local function row(label, value)
local function info_row(label, value)
     if not value or value == '' then return '' end
    -- value가 nil이거나 빈 문자열이면 출력하지 않음
     return string.format('<div class="prf-row"><strong>%s</strong> %s</div>', mw.text.escape(label), mw.text.escape(value))
    local val = trim(value)
     if val == '' then return '' end
   
    -- 목록 형태로 반환 ('- 라벨: 값' 형태)
     return string.format('<div style="margin: 3px 0; padding-left: 10px;">- <strong>%s</strong>: %s</div>', safe_html_encode(label), safe_html_encode(val))
end
end


-- 나이 출력 규칙: VN 먼저, 애니 다음
-- 나이 출력 규칙
local function build_age_row(args)
local function build_age_row(args)
     local nv = trim(args['나이_VN'])
     local nv = trim(args['나이_VN'])
     local na = trim(args['나이_애니'])
     local na = trim(args['나이_애니'])
   
    local age_str = ''
     if nv ~= '' and na ~= '' then
     if nv ~= '' and na ~= '' then
         return string.format('<div class="prf-row"><strong>나이</strong> VN %s / 애니 %s</div>', mw.text.escape(nv), mw.text.escape(na))
         age_str = string.format('VN %s / Anime %s', safe_html_encode(nv), safe_html_encode(na))
     elseif nv ~= '' then
     elseif nv ~= '' then
         return string.format('<div class="prf-row"><strong>나이</strong> VN %s</div>', mw.text.escape(nv))
         age_str = string.format('VN %s', safe_html_encode(nv))
     elseif na ~= '' then
     elseif na ~= '' then
         return string.format('<div class="prf-row"><strong>나이</strong> 애니 %s</div>', mw.text.escape(na))
         age_str = string.format('Anime %s', safe_html_encode(na))
     else
     else
         return ''
         return ''
     end
     end
    -- 나이 필드만 예외적으로 info_row 대신 직접 출력
    if age_str ~= '' then
        return string.format('<div style="margin: 3px 0; padding-left: 10px;">- <strong>나이</strong>: %s</div>', age_str)
    end
    return ''
end
end


-- 메인 생성 함수 (ASCII 식별자 사용: "main")
 
function p.main(frame)
function p.main(frame)
     local parent = frame:getParent() or frame
     local parent = frame:getParent() or frame
     local args = parent.args or {}
     local args = parent.args or {}
    for k, v in pairs(args) do args[k] = trim(v) end
      
      
     local theme = args['색상'] and args['색상'] ~= '' and args['색상'] or '#4cc9ff'
    -- 모든 인수를 문자열로 트림 (nil 방지)
    local image_block = build_image_block(args)
     local clean_args = {}
    local name_block = build_name_block(args)
    for k, v in pairs(args) do
        clean_args[k] = trim(v)
    end
   
    local theme = clean_args['색상'] ~= '' and clean_args['색상'] or '#007bff'
      
      
    -- 기본 정보
     local image_block = build_image_block(clean_args)
     local basic = ''
     local name_block = build_name_block(clean_args)
    basic = basic .. row('성별', args['성별'])
     basic = basic .. build_age_row(args)
    basic = basic .. row('생일', args['생일'])
    basic = basic .. row('신장', args['신장'])
    basic = basic .. row('체중', args['체중'])
    basic = basic .. row('머리색', args['머리색'])
    basic = basic .. row('눈색', args['눈색'])
      
      
     -- FC 프로필
     -- 섹션별 정보 구성
     local fc = ''
     local basic = table.concat({
    fc = fc .. row('포지션', args['포지션'])
        info_row('성별', clean_args['성별']),
     fc = fc .. row('소속 팀', args['소속_팀'])
        build_age_row(clean_args),
     local techs = gather_techs(args)
        info_row('생일', clean_args['생일']),
     if #techs > 0 then
        info_row('신장', clean_args['신장']),
         local tstr = '<div class="prf-row"><strong>시그니처 기술</strong><ol class="sig-list">'
        info_row('체중', clean_args['체중']),
        for _, tname in ipairs(techs) do
        info_row('머리색', clean_args['머리색']),
            tstr = tstr .. string.format('<li>%s</li>', mw.text.escape(tname))
        info_row('눈색', clean_args['눈색']),
        end
    }, '\n')
        tstr = tstr .. '</ol></div>'
 
        fc = fc .. tstr
     local fc_techs = gather_techs(clean_args)
    local fc = table.concat({
        info_row('포지션', clean_args['포지션']),
     }, '\n')  
   
     if fc_techs ~= '' then
         fc = fc .. '<div style="margin: 3px 0; padding-left: 10px;">- <strong>시그니처 기술</strong>:</div>' .. fc_techs
     end
     end
    local personal = table.concat({
        info_row('학교', clean_args['학교']),
        info_row('거주지', clean_args['거주지']),
        info_row('취미', clean_args['취미']),
        info_row('좋아하는 음식', clean_args['좋아하는음식']),
    }, '\n')
      
      
    -- 개인정보
     local voice = table.concat({
     local personal = ''
        info_row('일본어', clean_args['성우_JP']),
    personal = personal .. row('학교', args['학교'])
        info_row('영어', clean_args['성우_EN']),
    personal = personal .. row('거주지', args['거주지'])
     }, '\n')
    personal = personal .. row('취미', args['취미'])
     personal = personal .. row('좋아하는 음식', args['좋아하는음식'])
      
      
    -- 성우
     local debut = table.concat({
     local voice = ''
        info_row('애니메이션', clean_args['애니_데뷔']),
    voice = voice .. row('일본어', args['성우_JP'])
        info_row('게임(VN)', clean_args['게임_데뷔']),
    voice = voice .. row('영어', args['성우_EN'])
        info_row('만화', clean_args['만화_데뷔']),
    }, '\n')
      
      
    -- 데뷔
    local debut = ''
    debut = debut .. row('애니메이션', args['애니_데뷔'])
    debut = debut .. row('게임(VN)', args['게임_데뷔'])
    debut = debut .. row('만화', args['만화_데뷔'])
      
      
    -- ********** 위키 테이블 문자열 조립 **********
    local wiki_table = {}
    -- 1. 전체 컨테이너 테이블 시작 (2열: 이미지 / 정보)
    table.insert(wiki_table, string.format('{| class="wikitable" style="width: 100%%; max-width: 650px; border: 2px solid %s; border-collapse: collapse; margin: 1em auto; font-size: 95%%;"', safe_html_encode(theme)))
    table.insert(wiki_table, '|-')
      
      
     -- 최종 HTML 문자열 구성 (mw.html 사용 대신 순수한 문자열 조립)
     -- 2. 좌측 열 (이미지 영역)
    local html = {}
     table.insert(wiki_table, string.format('| style="width: 35%%; vertical-align: top; padding: 10px; background-color: #f8f8f8;" | %s', image_block))
     table.insert(html, '<div class="prf-card" style="display:flex;gap:14px;border-radius:10px;padding:12px;align-items:flex-start;">')
   
    -- 좌측 이미지
    table.insert(html, '<div class="prf-left" style="flex:0 0 36%;min-width:200px;">')
    table.insert(html, image_block) -- 위키텍스트 이미지 삽입
    table.insert(html, '</div>')
      
      
     -- 우측 정보
     -- 3. 우측 열 (정보 영역)
     table.insert(html, '<div class="prf-right" style="flex:1 1 auto;">')
     table.insert(wiki_table, '| style="width: 65%%; vertical-align: top; padding: 10px;" |')
      
      
     -- 이름 블록
     -- 우측 영역 내부 내용 (이름, 섹션 헤더, 정보 리스트)
     table.insert(html, name_block)
    local right_content = {}
     table.insert(right_content, name_block)
    table.insert(right_content, '<hr style="border: 0; border-top: 1px solid #ccc; margin: 8px 0;">')
      
      
    -- 섹션별 정보 (내용이 있을 경우에만 출력)
     local function add_section(title, content)
     local function add_section(title, content)
         if content ~= '' then
         if trim(content) ~= '' then
             table.insert(html, string.format('<div class="prf-section"><h4>%s</h4>%s</div>', title, content))
             table.insert(right_content, string.format('<h4 style="color:%s; margin:8px 0 4px 0; padding:0; font-size:1em; border-bottom:1px solid %s;">&lt;%s&gt;</h4>', safe_html_encode(theme), safe_html_encode(theme), safe_html_encode(title)))
            table.insert(right_content, content)
         end
         end
     end
     end
177번째 줄: 198번째 줄:
     add_section('성우', voice)
     add_section('성우', voice)
     add_section('데뷔', debut)
     add_section('데뷔', debut)
    table.insert(html, '</div>')
    table.insert(html, '</div>')
      
      
     -- 인라인 CSS (반응형 포함) - 테마 색상 적용
     table.insert(wiki_table, table.concat(right_content, '\n'))
    local style = string.format([[<style>.prf-card { border:2px solid %s; background:#ffffff; }.prf-name-kr { font-size:1.25em; font-weight:700; color:%s; margin-bottom:4px; }.prf-name-sub { font-size:0.95em; color:#444; margin-bottom:8px; }.prf-row { margin:4px 0; }.prf-section h4 { margin:8px 0 6px 0; color:%s; font-size:0.95em; }.sig-list { margin:4px 0 0 18px; padding:0; }.tabber { margin-bottom:8px; }@media screen and (max-width:768px) {  .prf-card { flex-direction:column; }  .prf-left { width:100%%; min-width:0; }}</style>]], theme, theme, theme)
      
      
     -- 최종 출력: 스타일 + HTML 문자열
     -- 4. 테이블 닫기
     return style .. table.concat(html, '\n')
    table.insert(wiki_table, '|}')
 
     return table.concat(wiki_table, '\n')
end
end


return style .. table.concat(html, '\n')
return p

2025년 12월 5일 (금) 03:27 기준 최신판

이 모듈에 대한 설명문서는 모듈:푸른 저편의 포리듬/ 캐릭터 프로필/설명문서에서 만들 수 있습니다

local p = {}
local mw = mw
local tt = mw.text

-- 안전 문자열 트림
local function trim(s)
    if not s then return '' end
    return tt.trim(tostring(s))
end

-- 안전한 HTML 인코딩 (데이터 출력 시 특수 문자 오류 방지)
local function safe_html_encode(s)
    if not s or s == '' then return '' end
    -- mw.text.html_encode 사용 시 오류 발생 가능성이 낮으므로 우선 사용
    if mw.text and mw.text.html_encode then
        return mw.text.html_encode(tostring(s))
    end
    -- 대체 인코딩 (mw.text 라이브러리 자체가 불안정할 경우 대비)
    s = tostring(s)
    s = s:gsub('&', '&amp;')
    s = s:gsub('<', '&lt;')
    s = s:gsub('>', '&gt;')
    return s
end

-- 시그니처 기술 수집 및 HTML 리스트 반환
local function gather_techs(args)
    local techs = {}
    for i = 1, 10 do
        local k = '시그니처기술' .. i
        local val = trim(args[k])
        if val ~= '' then
            table.insert(techs, val)
        end
    end
    if #techs > 0 then
        local tstr = '<ul style="list-style:none; margin:0; padding:0 0 0 10px; font-size:95%;">'
        for i, tname in ipairs(techs) do
            local marker = '①'
            if i == 2 then marker = '②' end
            if i == 3 then marker = '③' end
            if i > 3 then marker = '•' end
            tstr = tstr .. string.format('<li style="margin-top:2px;">%s %s</li>', marker, safe_html_encode(tname))
        end
        tstr = tstr .. '</ul>'
        return tstr
    end
    return ''
end

-- 이미지 블록 (위키텍스트 반환)
local function build_image_block(args)
    local main_file = trim(args['이미지_VN'] or args['이미지_애니'] or 'NoImage.png')
    
    local output = {}
    table.insert(output, string.format('[[파일:%s|250px|center]]', mw.text.encode(main_file)))
    table.insert(output, '<div style="text-align:center; font-size:85%; margin-top:5px;">(애니 / VN 스위치)</div>')
    
    return table.concat(output, '\n')
end

-- 이름 블록 (HTML 반환)
local function build_name_block(args)
    local kr = trim(args['이름'] or '')
    local jp = trim(args['이름_일어'] or '')
    local en = trim(args['이름_영어'] or '')
    local parts = {}
    
    if kr ~= '' then
        table.insert(parts, string.format('<strong style="font-size:1.1em;">이름: %s</strong>', safe_html_encode(kr)))
    end
    
    local sub_parts = {}
    if jp ~= '' then table.insert(sub_parts, safe_html_encode(jp)) end
    if en ~= '' then table.insert(sub_parts, safe_html_encode(en)) end
    
    if #sub_parts > 0 then
        table.insert(parts, string.format('<div style="font-size:90%%; margin-top:2px;">%s</div>', table.concat(sub_parts, ' / ')))
    end
    return table.concat(parts, '\n')
end

-- 라벨+값 행 생성 (값 없으면 빈 문자열)
local function info_row(label, value)
    -- value가 nil이거나 빈 문자열이면 출력하지 않음
    local val = trim(value)
    if val == '' then return '' end
    
    -- 목록 형태로 반환 ('- 라벨: 값' 형태)
    return string.format('<div style="margin: 3px 0; padding-left: 10px;">- <strong>%s</strong>: %s</div>', safe_html_encode(label), safe_html_encode(val))
end

-- 나이 출력 규칙
local function build_age_row(args)
    local nv = trim(args['나이_VN'])
    local na = trim(args['나이_애니'])
    
    local age_str = ''
    if nv ~= '' and na ~= '' then
        age_str = string.format('VN %s / Anime %s', safe_html_encode(nv), safe_html_encode(na))
    elseif nv ~= '' then
        age_str = string.format('VN %s', safe_html_encode(nv))
    elseif na ~= '' then
        age_str = string.format('Anime %s', safe_html_encode(na))
    else
        return ''
    end
    -- 나이 필드만 예외적으로 info_row 대신 직접 출력
    if age_str ~= '' then
        return string.format('<div style="margin: 3px 0; padding-left: 10px;">- <strong>나이</strong>: %s</div>', age_str)
    end
    return ''
end


function p.main(frame)
    local parent = frame:getParent() or frame
    local args = parent.args or {}
    
    -- 모든 인수를 문자열로 트림 (nil 방지)
    local clean_args = {}
    for k, v in pairs(args) do 
        clean_args[k] = trim(v) 
    end
    
    local theme = clean_args['색상'] ~= '' and clean_args['색상'] or '#007bff'
    
    local image_block = build_image_block(clean_args)
    local name_block = build_name_block(clean_args)
    
    -- 섹션별 정보 구성
    local basic = table.concat({
        info_row('성별', clean_args['성별']),
        build_age_row(clean_args),
        info_row('생일', clean_args['생일']),
        info_row('신장', clean_args['신장']),
        info_row('체중', clean_args['체중']),
        info_row('머리색', clean_args['머리색']),
        info_row('눈색', clean_args['눈색']),
    }, '\n')

    local fc_techs = gather_techs(clean_args)
    local fc = table.concat({
        info_row('포지션', clean_args['포지션']),
    }, '\n') 
    
    if fc_techs ~= '' then
        fc = fc .. '<div style="margin: 3px 0; padding-left: 10px;">- <strong>시그니처 기술</strong>:</div>' .. fc_techs
    end

    local personal = table.concat({
        info_row('학교', clean_args['학교']),
        info_row('거주지', clean_args['거주지']),
        info_row('취미', clean_args['취미']),
        info_row('좋아하는 음식', clean_args['좋아하는음식']),
    }, '\n')
    
    local voice = table.concat({
        info_row('일본어', clean_args['성우_JP']),
        info_row('영어', clean_args['성우_EN']),
    }, '\n')
    
    local debut = table.concat({
        info_row('애니메이션', clean_args['애니_데뷔']),
        info_row('게임(VN)', clean_args['게임_데뷔']),
        info_row('만화', clean_args['만화_데뷔']),
    }, '\n')
    
    
    -- ********** 위키 테이블 문자열 조립 **********
    local wiki_table = {}

    -- 1. 전체 컨테이너 테이블 시작 (2열: 이미지 / 정보)
    table.insert(wiki_table, string.format('{| class="wikitable" style="width: 100%%; max-width: 650px; border: 2px solid %s; border-collapse: collapse; margin: 1em auto; font-size: 95%%;"', safe_html_encode(theme)))
    table.insert(wiki_table, '|-')
    
    -- 2. 좌측 열 (이미지 영역)
    table.insert(wiki_table, string.format('| style="width: 35%%; vertical-align: top; padding: 10px; background-color: #f8f8f8;" | %s', image_block))
    
    -- 3. 우측 열 (정보 영역)
    table.insert(wiki_table, '| style="width: 65%%; vertical-align: top; padding: 10px;" |')
    
    -- 우측 영역 내부 내용 (이름, 섹션 헤더, 정보 리스트)
    local right_content = {}
    table.insert(right_content, name_block)
    table.insert(right_content, '<hr style="border: 0; border-top: 1px solid #ccc; margin: 8px 0;">')
    
    local function add_section(title, content)
        if trim(content) ~= '' then
            table.insert(right_content, string.format('<h4 style="color:%s; margin:8px 0 4px 0; padding:0; font-size:1em; border-bottom:1px solid %s;">&lt;%s&gt;</h4>', safe_html_encode(theme), safe_html_encode(theme), safe_html_encode(title)))
            table.insert(right_content, content)
        end
    end

    add_section('기본 정보', basic)
    add_section('FC 프로필', fc)
    add_section('개인 정보', personal)
    add_section('성우', voice)
    add_section('데뷔', debut)
    
    table.insert(wiki_table, table.concat(right_content, '\n'))
    
    -- 4. 테이블 닫기
    table.insert(wiki_table, '|}')

    return table.concat(wiki_table, '\n')
end

return p