Módulo:Droflax
Ir para navegação
Ir para pesquisar
A documentação para este módulo pode ser criada em Módulo:Droflax/doc
local p = {}
function p.main(frame)
local args = frame:getParent().args
return p._main(args)
end
function p._main(args)
-- Validaciones básicas
if not args.nome or args.nome == '' then
return '<div style="color:red;">Error: Nome es requerido</div>'
end
-- Procesamiento de habilidades
local habilidades = {}
for i = 1, 21 do
local nomeKey = 'hab' .. i .. '-nome'
if args[nomeKey] and args[nomeKey] ~= '' then
table.insert(habilidades, {
nome = args[nomeKey] or '',
icon = args['hab' .. i .. '-icon'] or '',
level = tonumber(args['hab' .. i .. '-level']) or 1,
desc = args['hab' .. i .. '-desc'] or '',
atr = args['hab' .. i .. '-atr'] or '',
video = args['hab' .. i .. '-video'] or ''
})
end
end
-- Procesamiento de skins
local skins = {}
for i = 1, 11 do
local imageKey = 'skin' .. i .. '-image'
if args[imageKey] and args[imageKey] ~= '' then
table.insert(skins, {
image = args[imageKey] or '',
banner = args['skin' .. i .. '-banner'] or '',
tooltip = args['skin' .. i .. '-tooltip'] or ''
})
end
end
-- Construcción del HTML usando mw.html (más eficiente)
local root = mw.html.create('div')
:addClass('personaje-box')
:attr('data-tier', args.tier or '')
-- Header
local header = root:tag('div'):addClass('personaje-header')
local topbar = header:tag('div'):addClass('personaje-topbar')
local nomeBox = topbar:tag('div'):addClass('personaje-nome-box')
nomeBox:tag('img')
:attr('src', '/images/6/63/Franky_ts_medal.png')
:addClass('topbar-icon')
local nomeCategory = nomeBox:tag('div'):addClass('personaje-nome-category')
nomeCategory:tag('div'):addClass('nome'):wikitext(args.nome)
nomeCategory:tag('div'):addClass('classes'):wikitext(args.classe or '')
-- Description
header:tag('div')
:addClass('topbar-description')
:wikitext((args.nome or '') .. ' (' .. (args.tier or '') .. ') é um personagem do tier ' .. string.lower(args.tier or '') .. '.')
-- Tabs
local tabs = header:tag('div'):addClass('personaje-tabs')
tabs:tag('button'):addClass('tab-btn'):attr('data-tab', 'arma'):wikitext('Arma')
tabs:tag('button'):addClass('tab-btn active'):attr('data-tab', 'habilidades'):wikitext('Habilidades')
tabs:tag('button'):addClass('tab-btn'):attr('data-tab', 'skins'):wikitext('Skins')
-- Art image
root:tag('img')
:addClass('art-personaje')
:attr('src', args.image or '')
:attr('alt', 'Arte del personaje')
-- Habilidades tab content
local habTab = root:tag('div')
:addClass('tab-content active')
:attr('id', 'habilidades')
-- Cuadros container (iconos de habilidades)
local cuadrosContainer = habTab:tag('div'):addClass('cuadros-container')
for i, hab in ipairs(habilidades) do
local cuadro = cuadrosContainer:tag('div')
:addClass('cuadro')
:attr('data-hab-index', i)
:attr('title', hab.nome)
cuadro:tag('img')
:attr('src', hab.icon)
:attr('alt', hab.nome)
:css('width', '100%')
:css('height', '100%')
:css('object-fit', 'cover')
end
-- Habilidades details container
local habContainer = habTab:tag('div'):addClass('habilidades-container')
local habDetails = habContainer:tag('div'):addClass('habilidades-details')
local descripcionContainer = habDetails:tag('div')
:addClass('descripcion-container')
:attr('id', 'descripcion-container')
local videoContainer = habContainer:tag('div')
:addClass('video-container')
:attr('id', 'video-container')
-- Skins tab content
local skinsTab = root:tag('div')
:addClass('tab-content')
:attr('id', 'skins')
local cardSkins = skinsTab:tag('div'):addClass('card-skins')
cardSkins:tag('span')
:addClass('card-skins-title')
:wikitext('SKINS & SPOTLIGHTS')
local carouselWrapper = cardSkins:tag('div'):addClass('skins-carousel-wrapper')
carouselWrapper:tag('button')
:addClass('skins-arrow left')
:wikitext('«')
local carousel = carouselWrapper:tag('div'):addClass('skins-carousel')
for _, skin in ipairs(skins) do
local skinCard = carousel:tag('div'):addClass('skin-card')
skinCard:tag('img')
:addClass('skins--imageBanner')
:attr('src', skin.banner)
:attr('alt', 'banner')
skinCard:tag('img')
:addClass('skins--imageSkin')
:attr('src', skin.image)
:attr('alt', 'skin')
end
carouselWrapper:tag('button')
:addClass('skins-arrow right')
:wikitext('»')
-- Datos JSON para JavaScript (más eficiente que data-attributes múltiples)
local dataScript = mw.html.create('script')
:attr('type', 'application/json')
:attr('id', 'persona-data-json')
:wikitext(mw.text.jsonEncode({
nome = args.nome or '',
tier = args.tier or '',
classe = args.classe or '',
image = args.image or '',
habilidades = habilidades,
skins = skins
}))
-- CSS (solo lo esencial, el resto en CSS común)
local style = mw.html.create('style'):wikitext([[
.personaje-box { padding: 16px; color: #000; font-family: 'Segoe UI', sans-serif; width: 90%; margin: auto; position: relative; user-select: none; }
.personaje-topbar { display: flex; flex-direction: column; align-items: flex-start; padding: 8px 16px; }
.personaje-nome-box { display: flex; align-items: center; gap: 8px; }
.topbar-icon { width: 90px; height: 90px; object-fit: none; background: #60dae2; }
.nome { font-size: 60px; font-family: 'Orbitron', sans-serif; font-weight: 900; }
.topbar-description { font-size: 16px; margin-top: 6px; background: #6AF3FB; width: fit-content; padding-inline: 16px; border-radius: 0 10px 10px 0; }
.personaje-header { display: flex; gap: 10px; flex-direction: column; }
.art-personaje { width: 665px; position: absolute; right: 1rem; top: 0.4rem; z-index: 9; }
/* ... resto del CSS optimizado ... */
]])
-- JavaScript optimizado
local script = mw.html.create('script'):wikitext([[
(function() {
'use strict';
// Cargar datos JSON (más rápido que procesar data-attributes)
const dataEl = document.getElementById('persona-data-json');
if (!dataEl) return console.error('Persona: no se encontró JSON data');
let data;
try {
data = JSON.parse(dataEl.textContent);
} catch (e) {
return console.error('Persona: error parsing JSON', e);
}
// Cache de elementos DOM
const elements = {
tabBtns: document.querySelectorAll('.tab-btn'),
tabContents: document.querySelectorAll('.tab-content'),
cuadros: document.querySelectorAll('.cuadro'),
descripcionContainer: document.getElementById('descripcion-container'),
videoContainer: document.getElementById('video-container'),
personajeBox: document.querySelector('.personaje-box')
};
// Aplicar tier class
const tierClass = {
'bronze': 'tier-bronze', 'bronce': 'tier-bronze',
'silver': 'tier-silver', 'prata': 'tier-silver',
'gold': 'tier-gold', 'ouro': 'tier-gold',
'diamond': 'tier-diamond', 'diamante': 'tier-diamond'
}[data.tier.toLowerCase()];
if (tierClass) elements.personajeBox.classList.add(tierClass);
// Event listeners optimizados
elements.tabBtns.forEach(btn => {
btn.addEventListener('click', handleTabClick, false);
});
elements.cuadros.forEach((cuadro, index) => {
cuadro.addEventListener('click', () => handleHabilidadClick(index), false);
});
// Funciones optimizadas
function handleTabClick(e) {
const target = e.target.dataset.tab;
elements.tabBtns.forEach(b => b.classList.remove('active'));
elements.tabContents.forEach(c => c.classList.remove('active'));
e.target.classList.add('active');
document.getElementById(target).classList.add('active');
}
function handleHabilidadClick(index) {
const hab = data.habilidades[index];
if (!hab) return;
// Generar HTML de atributos
const atributos = hab.atr.split(',').map(v => v.trim());
const labels = ['Poder PVE', 'Poder PVP', 'Energía', 'Recarga'];
const icons = [
'/images/7/7a/Icon-pve.png',
'/images/5/5f/Icon-pvp.png',
'/images/3/38/Icon-energy.png',
'/images/b/b1/Icon-cooldown.png'
];
const atributosHTML = atributos.map((v, i) => {
let f = v === '-' ? '-' : parseInt(v);
if (i === 1 && !isNaN(f)) f = (f > 0 ? '+' : '') + f;
return `
<span class="simple-tooltip">
<div class="cardAttribute">
<img src="${icons[i]}" class="cardAttribute--icon">
<h2 class="cardAttribute--value">${f}${i === 0 && f !== '-' ? ' seg' : ''}</h2>
</div>
</span>`;
}).join('');
// Actualizar descripción
elements.descripcionContainer.innerHTML = `
<div class="titulo-habilidad">
<h3>${hab.nome}</h3>
<div class="tooltip-container">
<button class="info-btn">i</button>
<span class="tooltip-text">Información adicional sobre la habilidad.</span>
</div>
</div>
<div class="attribute--cardsContainer">${atributosHTML}</div>
<div class="desc">${hab.desc.replace(/'''(.*?)'''/g, '<b>$1</b>')}</div>
`;
// Actualizar video
elements.videoContainer.innerHTML = hab.video ?
`<video width="100%" controls playsinline><source src="${hab.video}" type="video/webm"></video>` : '';
// Actualizar clases activas
elements.cuadros.forEach(c => c.classList.remove('activo'));
elements.cuadros[index].classList.add('activo');
}
// Inicializar primera habilidad
if (data.habilidades.length > 0) {
handleHabilidadClick(0);
}
// Inicializar carrusel de skins
initSkinsCarousel();
function initSkinsCarousel() {
const carousel = document.querySelector('.skins-carousel');
if (!carousel) return;
const wrapper = document.querySelector('.skins-carousel-wrapper');
const leftBtn = document.querySelector('.skins-arrow.left');
const rightBtn = document.querySelector('.skins-arrow.right');
let isScrolling = false;
function updateArrows() {
const maxScroll = carousel.scrollWidth - carousel.clientWidth;
const hasLeft = carousel.scrollLeft > 5;
const hasRight = carousel.scrollLeft < maxScroll - 5;
leftBtn.style.display = hasLeft ? 'inline-block' : 'none';
rightBtn.style.display = hasRight ? 'inline-block' : 'none';
wrapper.classList.toggle('has-left', hasLeft);
wrapper.classList.toggle('has-right', hasRight);
}
function scrollCarousel(direction) {
if (isScrolling) return;
isScrolling = true;
const scrollAmount = carousel.clientWidth * 0.6;
const currentScroll = carousel.scrollLeft;
const maxScroll = carousel.scrollWidth - carousel.clientWidth;
let nextScroll;
if (direction === 'left') {
nextScroll = Math.max(0, currentScroll - scrollAmount);
} else {
nextScroll = Math.min(maxScroll, currentScroll + scrollAmount);
}
carousel.scrollTo({ left: nextScroll, behavior: 'smooth' });
setTimeout(() => {
isScrolling = false;
updateArrows();
}, 300);
}
leftBtn.addEventListener('click', () => scrollCarousel('left'));
rightBtn.addEventListener('click', () => scrollCarousel('right'));
carousel.addEventListener('scroll', updateArrows, { passive: true });
// Observer para cambios de tamaño
new ResizeObserver(updateArrows).observe(carousel);
// Inicializar
updateArrows();
}
// Limpiar data element
dataEl.remove();
})();
]])
return tostring(root) .. tostring(dataScript) .. tostring(style) .. tostring(script)
end
return p