Модуль:biblio

Материал из Викисловаря
Документация

Модуль предназначен для формирования библиографического описания согласно ГОСТ 7.1—2003 «Библиографическая запись. Библиографическое описание: Общие требования и правила составления».

Использование

{{#invoke:biblio|ref}}
--[[
    Модуль предназначен для форматирования библиографических ссылок на книжные издания
]]

local p = {}

--[[
  Список параметров

  Имена параметров должны быть в нижнем регистре.
]]
p.config = {
    param_autor             = 'автор',
    param_part_link         = 'ссылка часть',
    param_part_title        = 'часть',
    param_part_material     = 'часть материал',
    param_part_alt          = 'часть оригинал',
    param_part_ext          = 'часть сведения',
    param_part_resp         = 'часть ответственный',
    param_wikisource        = 'викитека',
    param_url               = 'ссылка',
    param_title             = {'название', 'заглавие'},
    param_material          = 'материал',
    param_alt_title         = 'оригинал',
    param_ext_title         = 'сведения',
    param_responsible       = 'ответственный',
    param_edition           = 'издание',
    param_alt_edition       = 'издание оригинал',
    param_edition_resp      = 'издание ответственный',
    param_edition_info      = 'об издании',
    param_edition_info_resp = 'об издании ответственный',
    param_place             = {'город', 'место'},
    param_publisher         = 'издательство',
    param_year              = 'год',
    param_phys              = 'объем',
    param_ext_phys          = 'объем доп',
    param_size              = 'размеры',
    param_addition          = 'комплект',
    param_volumes           = 'томов',
    param_volume            = {'том как есть', ru = 'том', en = 'volume', de = 'band'},
    param_volume_subtitle   = 'подзаголовок тома',
    param_number            = 'номер',
    param_issue             = {ru = 'выпуск', en  = 'issue', de = 'heft'},
    param_issue_subtitle    = 'подзаголовок выпуска',
    param_columns           = {ru = 'столбцы', en = 'columns', de = 'kolonnen'},
    param_pages             = {'страницы как есть', ru = 'страницы', en = 'pages', de = 'seite'},
    param_all_pages         = {'страниц как есть', ru ='страниц', en = 'allpages', de = 'alleseiten'},
    param_series            = 'серия', 
    param_alt_series        = 'серия оригинал',
    param_ext_series        = 'серия сведения',
    param_resp_series       = 'серия ответственный',
    param_series_issn       = 'серия issn',
    param_series_issue      = 'серия выпуск',
    param_comment           = 'примечание',
    param_copies            = 'тираж', 
    param_issn              = 'issn',
    param_isbn              = 'isbn',
    param_doi               = 'doi'
}

-- Подстрочная библиографическая ссылка ГОСТ 7.1—2003
function p._ref(args)
    local param_handlers = {} -- таблица обработчиков параметров
    -- формат значения
    local function format_string(str, fmt)
        str = mw.ustring.gsub( str, "%%", "%%%%" ); -- маркируем знак процента
        return mw.ustring.gsub(fmt, '$1', str); -- заменяем $1
    end
    -- получить параметр по внутреннему имени и префиксу
    local function getparam (x, suffix)
        local param_names  = p.config['param_' .. x];
        if param_names == nil then
            return '';
        end
        if suffix == nil then
            suffix = '';
        end
        local formater = param_handlers[x];
        local function readparam(key, name)
            local param_name = name .. suffix;
            local value = args[param_name] or '';
            -- mw.log('readparam(\'' .. key .. '\', \'' .. param_name .. '\') = \'' .. value .. '\'')
            if formater ~= nil then
                if type(formater) == 'function' then
                    value = formater(value, suffix, key)
                elseif type(formater) == 'table' and value ~= '' then
                    local fmt = formater[key] or '';
                    if fmt ~= '' then
                       value = format_string(value, fmt)
                    end
                elseif type(formater) == 'string' and value ~= '' then
                    value = format_string(value, formater)
                end
            end
            return  value;
        end
        local result ='';
        if type(param_names) == 'table' then
            for key, param_name in pairs(param_names) do
                result = readparam(key, param_name);
                if result ~= '' then
                    break
                end
            end
        elseif type(param_names) == 'string' then
            result = readparam('', param_names);
        end;
        return result;
    end
    -- обход блоков вглубь с объединением результатов в строку  
    local function process(f, level, suffix)
        -- обработка повторяющихся блоков
        local function iterate(f)
            local result = '';
            if f == nil then
                return result;
            end
            local nextlevel = level;
            if f.block ~= nil and f.beforenext ~= nil then
                nextlevel = nextlevel +1;
            end
            local i = 1;
            while true do
                local sfx = suffix;
                if i > 1 then
                    if level > 0 then
                        sfx = sfx .. '_';
                    end
                    sfx = sfx .. tostring(i);
                end
                local str = process(f, nextlevel, sfx);
                if str == nil or str == '' then
                    break; -- пустой блок
                end
                if f.vformat ~= nil and  f.vformat ~= '' then
                    str = format_string(str, f.vformat);
                end
                if f.beforenext == nil then
                    -- неповторяющийся блок
                    result = str;
                    break; 
                else
                    if result ~= '' and f.beforenext ~= nil and f.beforenext ~= '' then
                        result = result .. f.beforenext; -- добавляем разделитель до
                    end
                    result = result .. str;
                end
                i = i + 1;
            end
            return result;
        end
        local result = '';
        if level == nil then
            level = 0; 
        end
        if suffix == nil then
            suffix = ''; -- префикс параметра по умолчанию
        end
        if f.block ~= nil and type(f.block) == 'table' then
            local after = '';
            for i=1, #f.block do
                local str = iterate(f.block[i]);
                if str ~= '' then
                    if result ~= '' then -- не первый непустой блок 
                        result = result .. after; -- разделитель после, указанный в предыдущем блоке
                        if f.block[i].before ~= nil and f.block[i].before ~= '' then
                            result = result .. f.block[i].before; 
                        end
                    end
                    result = result .. str;
                    after = f.block[i].after or ''; 
                end
            end
        elseif f.param ~= nil and type(f.param) == 'string' then
            result = getparam(f.param, suffix) or '';
        end 
        return result;
    end
    --[[
        Далее идут обработчики значений
        Функция обработчика принимает три параметра:
        1. внутреннее имя параметра из p.config без 'param_'
        2. префикс (название блока, если параметр есть в разных блоках)
        3. суффикс (порядковое значение, если имя тоже)
    ]]
    -- Автор
    param_handlers['autor'] = function (x, suffix)
        if x == '' then
            return x;
        end
        x, i = mw.ustring.gsub( x, '^(%[*)(.-[^%.%]])(%]*)%.?$', '%1%2%3.'); -- добавляем точку если её нет
        mw.logObject(i, 'i');
        return '<i>' .. x .. '</i>'; 
    end
    -- Название части
    param_handlers['part_title'] = function (x, suffix) 
        if x == '' then
            return x;
        end
        local l = getparam('part_link', suffix) or ''
        if l ~= '' then 
            return '[' .. l .. ' ' .. x .. ']' 
        else
            return x
        end
    end
    -- Основное заглавие
    param_handlers['title'] = function (x, suffix)
        if x == '' then
            return x;
        end
        x = mw.ustring.gsub( x, '^(.-)%.$', '%1'); -- всегда убираем точку в конце названия
        local ws =  getparam('wikisource', suffix) or ''
        if ws ~= '' then
           x = '[[:s:' .. ws .. '|' .. x .. ']]';
        else
            local l = getparam('url', suffix) or ''
            if  l ~= '' then
                x = '[' .. l .. ' ' .. x .. ']';
            end
        end
        return x
    end
    -- Общее обозначение материала
    function format_material(x, suffix) 
        if x == '' then
            return x;
        end
        local materialtype_ru = { 
            ['видеозапись']=1, ['звукозапись']=2, ['изоматериал']=3, ['карты']=4,
            ['комплект']=5, ['кинофильм']=6, ['микроформа']=7, ['мультимедиа']=8,      
            ['ноты']=9, ['предмет']=10, ['рукопись']=11, ['текст']=12, 
            ['шрифт Брайля']=13, ['электронный ресурс']=14 
        }
        local materialtype_en = {
            ['videorecording']=1, ['sound recording']=2, ['graphic']=3, 
            ['cartographic material']=4, ['kit']=5, ['motion picture']=6, ['microform']=7,
            ['multimedia']=8, ['music']=9, ['object']=10, ['manuscript']=11, ['text']=12,
            ['braille']=13, ['electronic resource']=14
        }
        local lmt = mw.ustring.lower(x);
        if (materialtype_ru[lmt] ~= nil or materialtype_en[lmt] ~= nil) then
            return '[' .. x .. ']';
        else
            return ''; -- игнорируем
        end
    end
    param_handlers['material'] = format_material;
    param_handlers['part_material'] = format_material;
    -- формат места издание
    param_handlers['place'] = function (x, suffix) 
        if x == '' then
            return x;
        end
        local knownPlaces = { ['L.'] = 'London', ['N. Y.'] = 'New York', ['P.'] = 'Paris', ['Б.'] = 'Баку',
            ['Б. м.'] = 'без указания места', ['Ер.'] = 'Ереван', ['Иер.'] = 'Иерусалим', ['К.'] = 'Киев', 
            ['Каз.'] = 'Казань',  ['Л.'] = 'Ленинград',  ['М.'] = 'Москва', ['Мн.'] = 'Минск', 
            ['Н. Н.'] = 'Нижний Новгород', ['Н. Новгород'] = 'Нижний Новгород', ['Пг.'] = 'Петроград', 
            ['Ростов н/Д'] = 'Ростов-на-Дону', ['СПб.'] = 'Санкт-Петербург', ['Тб.'] = 'Тбилиси', 
            ['Тф.'] = 'Тифлис', ['Яр.'] = 'Ярославль'
        }
        local kp = knownPlaces[x];
        if kp ~= nil then
            if kp == 'без указания места' then
                x = '[' .. x .. ']';
            end
            return '<span style="border-bottom:1px dotted gray; cursor:default;" title="'.. kp .. '">' .. x .. '</span>';
        else
            return x;
        end
    end
    param_handlers['volumes']   = '$1&nbsp;т.';
    param_handlers['number'] = '№&nbsp;$1';
    param_handlers['volume']    = { '', ru = 'Т.&nbsp;$1',      en = 'Vol.&nbsp;$1',    de = 'B.&nbsp;$1' };
    param_handlers['issue']     = {     ru = 'Вып.&nbsp;$1',    en = 'Iss.&nbsp;$1',    de = 'H.&nbsp;$1' };
    param_handlers['columns']   = {     ru = '(стб.&nbsp;$1)',  en = '(col.&nbsp;$1)',  de = '(Kol.&nbsp;$1)' };
    param_handlers['all_pages'] = { '', ru = '$1&nbsp;с.',      en = '$1&nbsp;p.',      de = '$1&nbsp;s.' };
    param_handlers['pages'] =  function (x, suffix, name) 
        if x == '' then
            return x;
        end
        local fmts = { '',  ru = 'С.&nbsp;$1', en = 'P.&nbsp;$1', de = 'S.&nbsp;$1' }
        local fmt = fmts[name] or '';
        if fmt ~= '' then
            local col = getparam('columns', suffix) or '';
            if col ~= '' then
                x = '['.. x .. '] ' .. col;
            end
           x = format_string(x, fmt)
        end
        return x;
    end
    param_handlers['phys'] = function (x, suffix, name) 
        if x == '' then
            x = getparam('volumes', prefix, suffix) or ''; 
            local ap = getparam('all_pages', suffix) or ''; 
            if ap ~= '' then
                if x ~= '' then    
                    x = x .. ', ' .. ap;
                else
                    x = ap;
                end
            end
        end
        return x
    end
    param_handlers['copies']        = '$1 экз.';
    param_handlers['isbn']          = 'ISBN $1';
    param_handlers['series_issn']   = 'ISSN $1';
    param_handlers['issn']          = 'ISSN $1';
    param_handlers['doi']           = 'DOI $1';

    local sep = '. — ' --  точка и тире
    -- Область заглавия и сведений об ответственности 
    local title_area = { 
        { param = 'title'                                           }, -- Основное заглавие
        { param = 'material',  before = ' '                         }, -- Общее обозначение материала
        { param = 'alt_title', before = ' = ', beforenext = ' = '   }, -- Параллельное заглавие
        { param = 'ext_title', before = ' : ', beforenext = ' : '   }, -- Сведения, относящиеся к заглавию
        { param = 'responsible', before = ' / ', beforenext = '; '  }  -- Cведения об ответственности
    }
    -- Область издания 
    local edition_area = {
        { param = 'edition'                                         }, -- Сведения об издании
        { param = 'alt_edition', before = ' = '                     }, -- Параллельные сведения об издании
        { param = 'edition_resp', before = ' / ', beforenext = '; ' }, -- Сведения об ответственности, относящиеся к изданию
        { param = 'edition_info', before = ', ', beforenext = '; '  }, -- Дополнительные сведения об издании
        { param = 'edition_info_resp', before = ' / ', beforenext = '; ' }  -- Сведения об ответственности, относящиеся к дополнительным сведениям об издании
    }
    -- Область нумерации 
    local number_area = {
        { param = 'volume'                                          }, -- Том
        { param = 'issue', before =', '                             }, -- Номер выпуска
        { param = 'number', before =', '                            }, -- Номер
        { param = 'volume_subtitle', before =' : '                  }  -- Частное заглавие тома или выпуска
    }
    --  Сведения о местоположении объекта ссылки в документе (если ссылка на часть документа);
    local ref_area = {
        { block = number_area, name='number_area'                   }, -- Область нумерации
        { param = 'pages', before = sep, beforenext = sep           }  -- Номера страниц и столбцов в цитируемом источнике
    }
    -- Область выходных данных
    local output_data_area = {
        { param = 'place', beforenext= ' ; '                        }, -- Место издания, распространения
        { param = 'publisher', before = ' : ', beforenext = ' : '   }, -- Имя (наименование) издателя, распространителя и т. п.
        -- Сведения о функции издателя, распространителя и т. п. пропущены
        { param = 'year', before = ', '                             }, -- Дата издания, распространения и т. п.
        -- Место изготовления, имя изготовителя и дата изготовления пропущены
        { block = ref_area, before = sep, beforenext = ' ; '        }  -- Сведения о местоположении объекта ссылки в документе (если ссылка на часть документа);
    }
    -- Сведения об объеме документа (если ссылка на весь документ)
    local phys_area = {
        { param = 'phys'                                            }, -- Специфическое обозначение материала и объем
        { param = 'ext_phys', before = ' : '                        }, -- Другие сведения о физической характеристике
        { param = 'size', before = ' ; '                            }, -- Размеры
        { param = 'addition', before = ' + ', beforenext = ' + '    }  -- Сведения о сопроводительном материале
    }
    -- Область серии
    local series_area = {
        { param = 'series'                                          }, -- Основное заглавие серии или подсерии
        { param = 'alt_series', before = ' = ', beforenext = ' = '  }, -- Параллельное заглавие серии или подсерии
        { param = 'ext_series', before = ' : ', beforenext = ' : '  }, -- Сведения, относящиеся к заглавию серии или подсерии
        { param = 'resp_series', before = ' / ', beforenext = '; '  }, -- Сведения об ответственности, относящиеся к серии или подсерии
        { param = 'series_issn', before = ' , '                     }, -- Международный стандартный номер сериального издания (ISSN), присвоенный данной серии или подсерии
        { param = 'series_issue', before = ' ; '                    }  -- Номер выпуска серии или подсерии
    }
    -- Сведения об идентифицирующем документе
    local document_area = {
        { block = title_area, beforenext='. '                       }, -- Область заглавия и сведений об ответственности
        { block = edition_area, before = sep                        }, -- Область издания
        { block = output_data_area, before = sep                    }, -- Область выходных данных
        { block = phys_area, before = sep                           }, -- Сведения об объеме документа (если ссылка на весь документ)
        { block = series_area, before = sep, beforenext = ' ',
                    vformat = '($1)'                                }, -- Область серии
    }
    -- Сведения о составной части документа
    local part_area = {
        { param = 'part_title'                                      }, -- Заглавие части
        { param = 'part_material',  before = ' '                    }, -- Общее обозначение материала части
        { param = 'part_alt', before = ' = ', beforenext = ' = '    }, -- Параллельное заглавие части
        { param = 'part_ext', before = ' : ', beforenext = ' : '    }, -- Сведения, относящиеся к заглавию части
        { param = 'part_resp', before = ' / ', beforenext = '; '    }  -- Cведения об ответственности части
    }
    -- Аналитическое библиографическое описание
    local description = {
        { param = 'autor', after=' '                                }, -- Автор 
        { block = part_area, after=' // ', beforenext='. '          }, -- Сведения о составной части документа
        { block = document_area, after=sep                          }, -- Сведения об идентифицирующем документе
        { param = 'comment', after = sep, beforenext= ' — '         }, -- Область примечания
        { param = 'copies',  after = sep                            }, -- Тираж
        { param = 'issn', after = sep                               }, -- Международный стандартный издания
        { param = 'isbn',  after = sep, beforenext=sep              }, -- Международный стандартный номер книги 
        { param = 'doi'                                             }  -- Идентификатор цифрового объекта
    }
    -- удаление точки перед разделителем
    local function removeDotBefore(s, sep)
        -- разделитель без лидирующей точки
        local nodotsep = mw.ustring.sub(sep, 2); 
        -- маскируем спецсимволы в разделителе
        local esep = mw.ustring.gsub( sep, "([%(%)%.%%%+%-%*%?%[%^%$%]])", "%%%1" ); 
        -- убираем точку в разделителе, если перед ним восклицательный, вопросительный знак или многоточие
        local result = mw.ustring.gsub(s, '([!%?…]%]?%]?)'.. esep, '%1' .. nodotsep);
        -- убираем точку перед разделителем
        return mw.ustring.gsub(result, '%.'.. esep,  sep);
    end
    -- добавление точки в конце
    local function addDotAfter(s)
        local fs = mw.ustring.gsub( s, '%b<>', ''); -- убираем теги в строке
        -- проверяем есть ли в конце строки точка
        if mw.ustring.match(fs, '^.-[^!%?.…]%]?%]?$' ) then
            s = s .. '.';
        end
        return s;
    end
    -- добавления якоря ссылки
    local function AddRef(s)
        if s ~= '' then
            local id = args['ref'] or '';
            if id ~= '' then
                local y = args['год'] or '';
                local b = args['буква'] or '';
                id = 'CITEREF' .. id .. y .. b;
                id = ' id="' .. mw.uri.anchorEncode( id ) .. '"';
            end
            s = '<span class="citation"' .. id ..  '>' .. s .. '</span>';
        end
        return s;
    end
    local result = process({block = description, name='description' });
    result = removeDotBefore(result, sep); -- удаление лишней точки перед разделителем
    result = addDotAfter(result); -- описание заканчивается точкой
    result = AddRef(result); 
    return result;
end

function p.ref(frame)
    local pframe = frame:getParent()
    local args = {};
    for key, value in pairs(pframe.args) do
        if type(key) == 'string' and key ~= '' then
            args[mw.ustring.lower(key)] = value;
        end
    end
    return p._ref(args);
end;
return p