Документация

Модуль обеспечивает работу шаблона {{cite web}}.

Подробнее об использовании шаблона см. его документацию.

local p = {};

require('strict');
local listRef = require('Module:Languages').list_ref;
local boxDate = require('Module:Calendar').bxDate;

local error_cats = {
    ['noname_param'] = 'К:Википедия:Cite web (некорректное использование: непустой неименованный параметр)',
    ['empty_title'] = 'К:Википедия:Cite web (некорректное использование: не указан title)',
    ['empty_url'] = 'К:Википедия:Cite web (некорректное использование: не указан url)',
    ['bad_archive'] = 'К:Википедия:Cite web (некорректное использование: параметры архивации)',
    ['deadlink'] = 'К:Википедия:Cite web (недоступные ссылки без архивной копии)',
    -- ['bad_lang'] = 'К:Википедия:Cite web (неверный код языка)',
    -- ['deadlink_old'] = 'К:Википедия:Cite web (устаревшие параметры мёртвых ссылок)',
    ['empty_lang'] = 'К:Википедия:Cite web (не указан язык)',
    ['bad_url'] = 'К:Википедия:Cite web (некорректный url)',
    ['webcitation_no'] = 'К:Википедия:Cite web (заменить webcitation-архив: deadlink no)',
    ['webcitation_yes'] = 'К:Википедия:Cite web (заменить webcitation-архив: deadlink yes)',
}
local errors = {
    ['noname_param'] = '{{error|Все параметры шаблона {{tl|cite web}} должны [[T:cite web#Неименованные_параметры|иметь имя]].}}',
    ['empty_title'] = '{{error|Необходимо задать параметр {{code|title{{=}}}} в шаблоне {{tl|cite web}}.}}',
    ['empty_url'] = '{{error|Необходимо задать параметр {{code|url{{=}}}} в шаблоне {{tl|cite web}}.}}',
    ['bad_archive'] = '{{error|Если в шаблоне {{tl|cite web}} задаётся параметр {{code|archive-url{{=}}}}, должен задаваться и параметр {{code|archive-date{{=}}}}, и наоборот.}}',
}
local replace_params = {
    ['accessdate'] = 'access-date',
    ['archivedate'] = 'archive-date',
    ['archiveurl'] = 'archive-url',
    ['authorlink'] = 'author-link',
    ['first1'] = 'first',
    ['last1'] = 'last',
    ['deadurl'] = 'deadlink',
    ['dead-url'] = 'deadlink',
    ['language'] = 'lang',
    ['datepublished'] = 'date',
    ['work'] = 'website',
}

-- проверка существования переменной. возврат её, или nil если пустая
local function is(var)
    if (var == '' or var == nil) then return nil else return var end
end

-- замена устаревших аргументов на их аналоги
local function prepareArgs(args)
    local bad_args = {};
    local new_args = {};
    for param, value in pairs(args) do
        if is(replace_params[param]) and not is(args[replace_params[param]]) then
            param = replace_params[param];
        end
        new_args[param] = value;
    end
    return new_args, bad_args
end

-- добавление скрытого языка
local function hiddenRef(code)
    return '<span class="hidden-ref" style="display:none;">&nbsp;' .. code .. '</span>'
end

local function insertDot(str, insert)
    if insert == false then
        return
    end
    table.insert(str, '.')
end

local function needDot(source_str)
    if mw.ustring.find(source_str, '[.?!:…]»?$') ~= nil then
        return false
    else
        return true
    end
end

local function nowiki(text)
    local frame = mw.getCurrentFrame();
    return frame:callParserFunction('#tag', { 'nowiki', text })
end

local function replace(source_str, pattern, replace)
    return mw.ustring.gsub(source_str, pattern, replace)
end

-- форматирование даты; в случае ошибки - возврат переданного значения без изменений
local function formatDate(strFormat, txtDateIn, params)
    local txtDateOut, date, status = boxDate(txtDateIn, strFormat, params)
    if status.brk then
        return txtDateIn
    else
        return txtDateOut
    end
end

-- отрисовка ошибки по коду
local function expandError(code)
    return mw.getCurrentFrame():preprocess(errors[code]) .. ' '
end

-- отрисовка всех категорий по их кодам
local function expandCats(cats)
    local str = {};
    local frame = mw.getCurrentFrame();
    for _, cat in pairs(cats) do
        table.insert(str, '[[' .. error_cats[cat] .. ']]')
    end
    return frame:preprocess(table.concat(str))
end

-- разделение их
local function splitBySlash(string)
    local args = {};
    local iterator = mw.ustring.gmatch(string, "[^/]+");

    for w in iterator do
        table.insert(args, w)
    end
    return args
end

-- оборачиваем главную ссылку в span с указанным языком (может быть полезно для rtl)
local function wrapLang(link, langs)
    local lang_code = 'und';

    if is(langs) then
        local args = splitBySlash(langs);
        if #args ~= 0 then
            lang_code = args[1];
        end
    end

    return '<span lang="' .. lang_code .. '">' .. link .. '</span>'
end

-- обёртка в тег цитирования
local function wrapCite(str, args)
    if is(args['ref']) then
        table.insert(str, 1, '<span class="citation" id="CITEREF' .. mw.uri.anchorEncode(args['ref']) .. '">');
        if is(args['date']) then
            table.insert(str, 2,
                '<span class="citation" id="CITEREF' ..
                mw.uri.anchorEncode(args['ref'] .. formatDate('Y', args['date'])) .. '">');
            table.insert(str, '</span>');
        end
    else
        table.insert(str, 1, '<span class="citation">');
    end

    table.insert(str, '</span>');
end

-- генерирум список языков (аргумент - список языков через слеш)
local function refLang(lang)
    local args = splitBySlash(lang);

    local frame = mw.getCurrentFrame();
    frame.args = args

    local lang_result = listRef(frame)
    local hidden = false;

    if #args == 1 and args[1] == 'ru' then
        hidden = true;
    end

    return lang_result, hidden
end

function p.render(frame)
    local str = {}
    local cats = {}

    local pFrame = frame:getParent();
    local args = mw.clone(pFrame.args);
    setmetatable(args, nil);

    args = prepareArgs(args);

    -- Проверки
    -- Проверка отсутствия неименованных параметров
    if is(args[1]) or is(args[2]) or is(args[3]) or is(args[4]) or is(args[5]) or is(args[6]) then
        table.insert(str, expandError('noname_param'));
        table.insert(cats, 'noname_param');
    end

    -- Проверка корректности заполнения параметров archiveurl и archivedate
    if (is(args['archive-date']) ~= nil and is(args['archive-url']) == nil) or
        (is(args['archive-date']) == nil and is(args['archive-url']) ~= nil) then
        table.insert(str, expandError('bad_archive'));
        table.insert(cats, 'bad_archive');
    end

    -- Проверка устаревших параметров мёртвых ссылок
    -- if is(args['dead-url']) or is(args['deadurl']) then
    --     table.insert(cats, error_cats['deadlink_old'])
    -- end

    local urlstatus = 'live';
    if args['url-status'] == 'live' or args['url-status'] == 'dead' or args['url-status'] == 'unfit' then
        urlstatus = args['url-status']
    elseif args['deadlink'] == 'yes' then
        urlstatus = 'dead'
    elseif args['deadlink'] == 'unfit' then
        urlstatus = 'unfit'
    end

    if is(args['archive-url']) then
        if mw.ustring.find(args['archive-url'], 'webcitation.org', 1, true) then
            if urlstatus == 'dead' or urlstatus == 'unfit' then
                table.insert(cats, 'webcitation_yes')
            else
                table.insert(cats, 'webcitation_no')
            end
        end
    elseif urlstatus == 'dead' then
        table.insert(cats, 'deadlink')
    end

    -- Проверка заполнения параметра url
    if is(args['url']) then
        if mw.ustring.find(args['url'], '^https?://', 1, false) ~= 1 and
            mw.ustring.find(args['url'], '^ftp://', 1, false) ~= 1 or mw.ustring.find(args['url'], ' ', 1, true) ~= nil then
            table.insert(cats, 'bad_url')
        end
    else
        table.insert(str, expandError('empty_url'));
        table.insert(cats, 'empty_url')
    end

    -- Проверка заполнения параметра title
    if not is(args['title']) then
        table.insert(str, expandError('empty_title'));
        table.insert(cats, 'empty_title')
    elseif not is(args['lang']) and mw.ustring.find(args['title'], '^[0-9А-яЁё«»:;,…!? \194\160%(%)%.—№%/%|%&%#+-]+$') == nil then
        table.insert(cats, 'empty_lang')
    end

    -- Формирование вывода
    -- Автор
    if is(args['author']) or is(args['last']) then
        table.insert(str, '<i>')

        local author = args['author'];
        if is(args['last']) then
            author = args['last']
            if is(args['first']) then
                author = author .. ', ' .. args['first']
            end
        elseif not is(args['last2']) and not is(args['coauthors']) then
            author = replace(author, '^(%[*)(.-[^%.%]])(%]*)$', '%1%2%3.')
        end

        if is(args['author-link']) then
            table.insert(str, '[[' .. args['author-link'] .. '|' .. author .. "]]")
        else
            table.insert(str, author)
        end

        -- Дополнительные сведения об авторах
        for i = 2, 5 do
            if is(args['last' .. i]) then
                local author = nowiki(';') .. '&#32;' .. args['last' .. i];
                if is(args['first' .. i]) then
                    author = author .. ', ' .. args['first' .. i]
                end
                table.insert(str, author)
            end
        end

        if is(args['coauthors']) then
            table.insert(str, nowiki(';') .. '&#32;' .. replace(args['coauthors'], '^(.-)%.?$', '%1.') .. ':&#32;')
        end

        table.insert(str, '</i>&#32;')
    end

    -- Редактор
    if is(args['editor']) then
        table.insert(str, ' ' .. args['editor'] .. ':&#32;')
    end

    -- URL, заголовок
    local link;
    local title = replace(replace(args['title'] or '', '%[', '&#91;'), '%]', '&#93;');
    local dot = needDot(title);

    if (urlstatus == 'dead' or urlstatus == 'unfit') and is(args['archive-url']) then
        link = '[' .. (args['archive-url'] or '') .. ' ' .. title .. ']'
    elseif urlstatus == 'unfit' then
        link = title
    else
        link = '[' .. (args['url'] or '') .. ' ' .. title .. ']'
    end
    table.insert(str, wrapLang(link, args['lang']))

    -- Подзаголовок
    if is(args['subtitle']) then
        insertDot(str, dot)
        table.insert(str, '&#32;' .. args['subtitle'])
        dot = needDot(args['subtitle']);
    end

    -- Отображение названия языка источника
    local langs;
    local hidden = false;
    if is(args['lang']) then
        langs, hidden = refLang(args['lang']);
    else
        langs = frame:expandTemplate { title = 'ref-und' };
        hidden = true;
    end
    if hidden == true then
        langs = hiddenRef(langs);
    else
        dot = true;
    end
    table.insert(str, langs)

    -- Формат источника
    if is(args['format']) then
        dot = true;
        table.insert(str, '&#32;(' .. args['format'] .. ')')
    end

    -- Пометка о недоступности
    if (urlstatus == 'dead' or urlstatus == 'unfit') and not is(args['archive-url']) then
        dot = true;
        table.insert(str,
            frame:expandTemplate { title = 'ref-info',
                args = { 'недоступная ссылка&nbsp;— [//web.archive.org/web/*/' ..
                args['url'] .. ' <i>история</i>]' } })
    end

    -- Название сайта или проекта
    if is(args['website']) then
        insertDot(str, dot)
        dot = true;
        table.insert(str, '&#32;<i>' .. args['website'] .. '</i>')
    end

    -- Страницы
    if is(args['pages']) then
        dot = true;
        table.insert(str, '&#32;' .. args['pages'])
    end

    -- Страница
    if is(args['page']) then
        dot = true;
        table.insert(str, '&#32;' .. args['page'])
    end

    -- Место, издательство
    if is(args['publisher']) then
        insertDot(str, dot)
        dot = true;
        table.insert(str, '&#32;');
        if is(args['location']) then
            table.insert(str, args['location'] .. ':&#32;')
        end
        table.insert(str, args['publisher'])
    end

    -- Дата
    if is(args['date']) then
        dot = true;
        table.insert(str, '&#32;(' .. formatDate('j xg Y', args['date']) .. ')')
    elseif is(args['year']) then
        dot = true;
        if is(args['month']) then
            table.insert(str, '&#32;(' .. args['month'] .. ' ' .. args['year'] .. ')')
        else
            table.insert(str, '&#32;(' .. args['year'] .. ')')
        end
    end

    -- Точка
    insertDot(str, dot)

    -- DOI
    if is(args['doi']) then
        table.insert(str,
            '&#32;[[Идентификатор цифрового объекта|doi]]:[http://dx.doi.org/' ..
            args['doi'] .. ' ' .. args['doi'] .. '].')
    end

    -- Описание
    if is(args['description']) then
        table.insert(str, '&nbsp;— ' .. args['description'])
        insertDot(str, needDot(args['description']))
    end

    -- Цитата
    if is(args['quote']) then
        table.insert(str, '&nbsp;— «' .. args['quote'] .. '»')
        insertDot(str, needDot(args['quote']))
    end

    -- Дата обращения
    if is(args['access-date']) then
        table.insert(str, '&#32;Дата обращения: ' ..
            formatDate('j xg Y', args['access-date']) .. '.')
    end

    -- Дата архивирования
    if is(args['archive-date']) and is(args['archive-url']) then
        if urlstatus == 'live' then
            table.insert(str,
                '&#32;[' ..
                args['archive-url'] ..
                ' Архивировано] ' ..
                formatDate('j xg Y', args['archive-date']) .. ' года.')
        elseif urlstatus == 'unfit' then
            table.insert(str,
                '&#32;Архивировано ' ..
                formatDate('j xg Y', args['archive-date']) .. ' года.')
        else
            table.insert(str,
                '&#32;Архивировано из [' ..
                args['url'] ..
                ' оригинала] ' ..
                formatDate('j xg Y', args['archive-date']) .. ' года.')
        end
    end

    wrapCite(str, args);

    if #cats ~= 0 and mw.title.getCurrentTitle():inNamespace(0) then
        table.insert(str, expandCats(cats));
    end

    return table.concat(str)
end

return p;