ФЭНДОМ


local navbox = {};
--[[settings(?) = table -- Настройки навбокса.
{
    attributes(?) = table -- Аттрибуты навбокса
    {
        classes(?) = string, -- Классы обертки навбокса;
        styles(?) = string, -- стили обертки навбокса;
        attrs(?) = string -- HTML атрибуты обертки навбокса.
    }
}
]]--
function navbox.create( settings )
    local self = navbox;
    local obj = {};
    --
    obj._library = require( 'Модуль:Библиотека' );
    obj._elementsLibrary = require( 'Модуль:Навбокс/Элементы' );
    obj._modulesLibrary = require( 'Модуль:Навбокс/Модули' );
    --
    if settings and settings.attributes then
        self._attributes = mw.clone( settings.attributes );
    end
    obj._elementsRoot = {};
    obj._elements = {};
    obj._navbox = nil;
    obj._modulesData = {};
    --
    function checkElements()
        for name, options in pairs( obj._elementsLibrary ) do
            if not options.children then
                if options.allowableChildren then
                    error( 'Элемент "'..name..'". Для параметра "allowableChildren" требуется параметр "children".' );
                end
                if options.forbiddenChildren then
                    error( 'Элемент "'..name..'". Для параметра "forbiddenChildren" требуется параметр "children".' );
                end
                if not options.children and options.level then
                    error( 'Элемент "'..name..'". Для параметра "level" требуется параметр "children".' );
                end
            end
            if options.allowableChildren and options.forbiddenChildren then
                error( 'Элемент "'..name..'". Параметры "allowableChildren" и "forbiddenChildren" несовместимы.' );
            end
            if options.allowableParents and options.forbiddenParents then
                error( 'Элемент "'..name..'". Параметры "allowableParents" и "forbiddenParents" несовместимы.' );
            end
            if options.sample and not obj._elementsLibrary[ sample ] then
                error( 'Элемент "'..name..'". Указанный аналог элемента не существует.' );
            end
            if options.allowableChildren and not obj._library.table.check( obj._elementsLibrary, options.allowableChildren ) then
                error( 'Элемент "'..name..'". Параметр "allowableChildren" содержит несуществующие элементы.' );
            end
            if options.forbiddenChildren and not obj._library.table.check( obj._elementsLibrary, options.forbiddenChildren ) then
                error( 'Элемент "'..name..'". Параметр "forbiddenChildren" содержит несуществующие элементы.' );
            end
            if options.allowableParents and not obj._library.table.check( obj._elementsLibrary, options.allowableParents ) then
                error( 'Элемент "'..name..'". Параметр "allowableParents" содержит несуществующие элементы.' );
            end
            if options.forbiddenParents and not obj._library.table.check( obj._elementsLibrary, options.forbiddenParents ) then
                error( 'Элемент "'..name..'". Параметр "forbiddenParents" содержит несуществующие элементы.' );
            end
            if options.requiredModules and not obj._library.table.check( obj._modulesLibrary, options.requiredModules ) then
                error( 'Элемент "'..name..'". Параметр "requiredModules" содержит несуществующие модули.' );
            end
        end
    end
    --
    checkElements();
    --
    setmetatable( obj, self );
    self.__index = self;
    return obj;
end
--[[
params = table
{
    type = string -- Тип элемента;
    text = string -- текстовое содержимое элемента;
    id = string -- идентификатор элемента;
    parentId = string -- идентификатор элемента, который должен быть родительским добавляемому;
    attributes(?) = table -- Аттрибуты элемента;
    {
        classes(?) = string, -- Классы обертки навбокса;
        styles(?) = string, -- стили обертки навбокса;
        attrs(?) = string -- HTML атрибуты обертки навбокса.
    },
    modules(?) = table -- таблица модулей элемента.
}
]]--
function navbox:addElement( params )
    if self._navbox then
        error( 'Навбокс закрыт для расширения' )
    end
    self:_checkElement( params.type, params.id, params.parentId )
    if params.modules then
        self:_checkModules( params.modules, params.type, params.id )
    end
    local billet = self:_createBillet( params )
    self:_collectBillet( billet, params.parentId )
end
--[[
params = table
{
    type = string -- Тип элемента;
    text = string -- текстовое содержимое элемента;
    id = string -- идентификатор элемента;
    parentId = string -- идентификатор элемента, который должен быть родительским добавляемому;
    attributes(?) = table -- Аттрибуты элемента;
    {
        classes(?) = string, -- Классы обертки навбокса;
        styles(?) = string, -- стили обертки навбокса;
        attrs(?) = string -- HTML атрибуты обертки навбокса.
    },
    modules(?) = table -- таблица модулей элемента.
}
]]--
function navbox:_createBillet( params )
    local elementType = params.type
    local rawModules = params.modules
    local requiredModules = self._elementsLibrary[ elementType ].requiredModules
    local billet = {
        text = params.text,
        type = elementType,
        id = params.id
    }
    if params.attributes then
        billet.attributes = params.attributes
    end
    if rawModules or requiredModules then
        local modules = {}
        if rawModules then
            for name, setting in pairs( rawModules ) do
                local moduleSettings = mw.clone( setting )
                local dependences = self._modulesLibrary[ name ].dependences
                moduleSettings._name = name
                if dependences then
                    moduleSettings._dependences = {}
                    for i, dependence in ipairs( dependences ) do
                        if rawModules[ dependence ] then
                            dependencesData[ dependence ] = mw.clone( rawModules[dependence] )
                        end
                    end
                end
                table.insert( modules, moduleSettings )
            end
        end
        if requiredModules then
            for name, setting in pairs( requiredModules ) do
                if not rawModules[ name ] then
                    local moduleSettings = mw.clone( requiredModules[name] )
                    moduleSettings._name = name
                    table.insert( modules, moduleSettings )
                end
            end
        end
        table.sort( modules, function( mod1, mod2)
            local prior1 = self._modulesLibrary[ mod1._name ].priority
            local prior2 = self._modulesLibrary[ mod2._name ].priority
            return prior1 < prior2
        end )
        billet.modules = modules
    end
    return billet
end
--[[
elementType = string -- Тип элемента.
id = string -- Идентификатор элемента.
parentId = string -- Идентификатор элемента, который должен быть родительским добавляемому.
]]--
function navbox:_checkElement( elementType, id, parentId )
    local elementPattern = self._elementsLibrary[ elementType ]
    -- На верный тип элемента
    if not elementPattern then
        error( 'Несуществующий тип элементов' )
    end
    -- На корректный айди
    if self._elements[ id ] then
        error( 'Элемент "'..id..'", уже определён' )
    end
    -- На наличие в библиотеке требуемых модулей
    if elementPattern.requiredModules then
        for name, requiredModule in pairs( elementPattern.requiredModules ) do
            if not self._modulesLibrary[ name ] then
                error( 'Отсутствует требуемый для элемента "'..elementType..'" модуль "'..name..'"' )
            end
        end
    end
    if parentId then
        local parentElement = self._elements[ parentId ]
        -- Проверка на наличие родительского элемента
        if not parentElement then
            error( 'Блока "'..parentId..'", указанного как родительский блок для блока "'..id..'", не существует' )
        end
        local parentElementType = parentElement.type
        local parentElemetPattern = self._elementsLibrary[ parentElementType ]
        -- Проверка на возможность родительского элемента иметь дочерние элементы
        if not parentElemetPattern.children then
            error( 'Блок "'..parentId..'" типа "'..parentElementType..'" не может иметь дочерних блоков' )
        end
        -- Проверка на уровень элементов
        if elementPattern.children and elementPattern.level < parentElemetPattern.level then
            error( 'Блок "'..id..'" типа "'..elementType..'"  не может быть потомком блока "'..parentId..'" типа "'..parentElementType..'" по причине недостаточного уровня' )
        end
    end
end
--[[
    modules = table -- Таблица модулей элемента.
    elementType = string -- Тип элемента.
    id = string -- Идентификатор элемента.
]]--
function navbox:_checkModules( modules, elementType, id )
    local elementPattern = self._elementsLibrary[ elementType ]
    for name, settings in pairs( modules ) do
        local pattern = self._modulesLibrary[ name ]
        -- Проверка на существование модуля
        if not pattern then
            error( 'Элемент "'..id..'". Модуль "'..name..'" не существует' )
        end
        -- Проверка на разрешенный тип элемента
        local trueType
        for i, permittedElement in ipairs( pattern.typesElements ) do
            if permittedElement == elementType or permittedElement == elementPattern.sample then
                trueType = true
            end
        end
        if not trueType then
           error( 'Элемент "'..id..'". Элементы типа "'..elementType..'" не совместимы с модулем "..name.."' ) 
        end
        -- Проверка настроек
        local isTrueSettings, errorText = pattern.checkSettings( settings )
        if not isTrueSettings then
            if errorText then
                error( 'Элемент "'..id..'" Неверные настройки модуля"'..name..'" '..errorText )
            else
                error( 'Элемент "'..id..'" Неверные настройки модуля"'..name..'"' )
            end
        end
    end
    -- Проверка наличия требуемых модулей
    for i, requiredModule in ipairs( elementPattern.requiredModules ) do
        if not modules[ requiredModule ] then
            error( 'Элемент "'..id..'". Для модуля "'..requiredModule..'" отсутствуют требуемые модули' )
        end
    end
    -- Проверка отсутствия запрещенных модулей
    for i, incompatibleModule in ipairs( elementPattern.incompatibleModules ) do
        if modules[ incompatibleModule ] then
            error( 'Элемент "'..id..'". Присутствует запрещенный для модуля "'..requiredModule..'" модуль' )
        end
    end
end
--[[
billet = table -- заготовка элемента
{
    text = string, -- текстовое содержимое элемента
    type = string, -- тип элемента
    id = string, -- идентификатор элемента,
    attributes = table --(?) аттрибуты элемента
    {
        classes = string, --(?) css классы обертки навбокса,
        styles = string, --(?) css стили обертки навбокса,
        attrs = string --(?) HTML атрибуты обертки навбокса,
    },
    modules = table --(?) таблица модулей элемента
}
parentId = string -- идентификатор элемента, который должен быть родительским добавляемому
]]--
function navbox:_collectBillet( billet, parentId )
    local type = billet.type
    local parentElement
    if parentId then
        parentElement = self._elements[ parentId ]
    end
    if self._elementsLibrary[ type ].children then
        billet.children = {}
        if parentId and type == parentElement.type then
            billet.nesting = parentElement.nesting + 1
        else
            billet.nesting = 1
        end
    end
    if not parentId then
        billet.level = 1
        table.insert( self._elementsRoot, billet )
    else
        billet.parent =  parentElement
        billet.level = parentElement.level + 1
        table.insert( parentElement.children, billet )
    end
    self._elements[ billet.id ] = billet
end
--
function navbox:assembly()
    if self._navbox then
        error( 'Навбокс закрыт для редактирования' )
    end
    local wrapper = mw.html.create( 'div' )
    if self._attributes then
        self:_applyAttrs( wrapper, self._attributes )
    end
    wrapper:addClass( 'navbox_wrapper' )
    for i, child in ipairs( self._elementsRoot ) do
        local block = self:_assembly( child )
        wrapper:node( block )
    end
    self._navbox = wrapper
    return wrapper
end
--[[
billet = table -- заготовка элемента
{
    text = string, -- текстовое содержимое элемента
    type = string, -- тип элемента
    id = string, -- идентификатор элемента
    attributes = table --(?) аттрибуты элемента
    {
        classes = string, --(?) css классы обертки навбокса,
        styles = string --(?), css стили обертки навбокса,
        attrs = HTML атрибуты обертки навбокса,
    },
    modules = table, --(?) таблица модулей элемента
    children = table, --(?) заготовки дочерних элементов
    nesting = number, --(?) глубина вложенности в однотипные элементы
    level = number, --(?) глобальный уровень вложенности
    parent = table --(?) заготовка родительского элемента
}
]]--
function navbox:_assembly( billet )
    local text = billet.text
    local attributes = billet.attributes
    if billet.modules then
        for i, modul in ipairs( billet.modules ) do
            local attrs
            text, attrs = self._modulesLibrary[ modul._name ].modify( text, modul )
            --self:_applyAttrs( element, attributes )
        end
    end
    local childBlocks = {}
    if billet.children then
        for i, child in ipairs( billet.children ) do
            local childBlock = self:_assembly( child )
            table.insert( childBlocks, childBlock ) 
        end
    end
    element = self._elementsLibrary[ billet.type ].create( billet.text, childBlocks )
    if attributes then
        self:_applyAttrs( element, attributes )
    end
    if billet.nesting then
        element:addClass( 'navbox_'..billet.type..'_n'..billet.nesting )
    end
    return element
end
--[[
elements = table -- таблица добавляемых элементов
replace = boolean --(?) заменять ли имеющиеся элементы
]]--
function navbox:addElements( elements, replace )
    if replace and #self._elementsRoot ~= 0 then
        error( 'Замена элементов невозможна. Навбокс уже отредактирован' )
    end
    local library = self._elementsLibrary
    for name, element in pairs( elements ) do
        if replace or not library[ name ] then
            library[ name ] = element
        end
    end
end
--[[
elem = htmlObject -- элемент, к которому применяются аттрибуды
attrs = table -- применяемые аттрибуты
]]--
function navbox:_applyAttrs( elem, attrs )
    if attrs.classes then
        for i, class in ipairs( attrs.classes ) do
            elem:addClass( class )
        end 
    end
    if attrs.styles then
        elem:css( attrs.styles )
    end
    if attrs.attrs then
        elem:attr( attrs.attrs )
    end
end
--
function navbox:getBlockInfo( blockId )
    if not blockId or not self._elems[ blockId ] then
        return false
    end
    local block = self._elems[ blockId ]
    local info = {}
    info.nesting = block.nesting
    info.level = block.level
    info.blockType = block.blockType
    info.id = blockId
    if block.parent then
        info.parentId = block.parent.id
    end
    info.children = {}
    for i, child in ipairs( block.children ) do
        info.children[ #info.children + 1 ] = child.id
    end
    return info
end
--
function navbox:checkBlock( blockId )
    if blockId and self._elems[ blockId ] then
        return true
    end
    return false
end
--
function navbox:getNavbox()
    if not self._navbox then
        error( 'Навбокс не готов' )
    end
    return self._navbox
end
--
function navbox:createSection( text, id, parentId, settings )
    self:addElement( {type='section', text=text, id=id, parentId=parentId, attributes=settings} )
end
--
function navbox:createPartition( text, id, parentId, settings )
    self:addElement( {type='partition', text=text, id=id, parentId=parentId, attributes=settings} )
end
--
function navbox:createContent( text, id, parentId, settings )
    self:addElement( {type='content', text=text, id=id, parentId=parentId, attributes=settings} )
end
 --
return navbox
Материалы сообщества доступны в соответствии с условиями лицензии CC-BY-SA , если не указано иное.