..
- Подпись автора

♥️
поле чудес х rich bitch x капсула х капсула'23
«good morning, reasons why I drink.»











dust ultimate two |
Привет, Гость! Войдите или зарегистрируйтесь.
Вы здесь » dust ultimate two » Организация » для сбора табличке 2
..
|
<!--HTML-->
<style>
.buildertd-body {
font-family: -apple-system, Segoe UI, Roboto, sans-serif;
margin: 0; padding: 20px;
background: #f5f5f7;
color: #222;
}
.buildertd-title { font-size: 20px; margin: 0 0 16px; }
.buildertd-panel {
background: #fff;
border: 1px solid #ddd;
border-radius: 8px;
padding: 12px;
margin-bottom: 16px;
}
.buildertd-panel-title {
font-size: 14px;
margin: 0 0 8px;
color: #555;
font-weight: 600;
}
.buildertd-hint {
font-size: 12px;
color: #888;
margin: 6px 0 0;
}
.buildertd-hint code {
background: #eef1f5;
padding: 1px 5px;
border-radius: 4px;
font-family: ui-monospace, Consolas, monospace;
}
.buildertd-textarea {
width: 100%;
resize: vertical;
font-family: ui-monospace, Consolas, monospace;
font-size: 13px;
padding: 8px;
border: 1px solid #ccc;
border-radius: 6px;
outline: none;
}
.buildertd-textarea:focus { border-color: #4a90e2; }
.buildertd-template { height: 160px; }
.buildertd-list { height: 180px; }
.buildertd-list-head {
display: flex;
align-items: center;
gap: 10px;
margin-bottom: 8px;
}
.buildertd-list-num {
font-weight: 700;
font-size: 14px;
color: #333;
}
.buildertd-list-count {
font-size: 12px;
color: #888;
margin-left: auto;
}
.buildertd-remove {
background: #fdecea;
color: #c0392b;
border: 1px solid #f5c6c0;
border-radius: 6px;
padding: 4px 10px;
font-size: 12px;
cursor: pointer;
}
.buildertd-remove:hover { background: #fbdcd7; }
.buildertd-group-row {
display: flex;
align-items: center;
gap: 10px;
margin-bottom: 8px;
flex-wrap: wrap;
}
.buildertd-group-label {
font-size: 14px;
color: #333;
}
.buildertd-group-input {
width: 100px;
padding: 8px 10px;
font-family: ui-monospace, Consolas, monospace;
font-size: 14px;
border: 1px solid #ccc;
border-radius: 6px;
outline: none;
}
.buildertd-group-input:focus { border-color: #4a90e2; }
.buildertd-group-input:disabled {
background: #f2f2f4;
color: #999;
cursor: not-allowed;
}
.buildertd-checkbox-row {
display: flex;
align-items: center;
gap: 8px;
font-size: 13px;
color: #444;
}
.buildertd-checkbox-row input[type="checkbox"] {
width: 16px; height: 16px;
cursor: pointer;
}
.buildertd-checkbox-row.disabled { color: #aaa; }
.buildertd-checkbox-row.disabled input[type="checkbox"] { cursor: not-allowed; }
.buildertd-actions {
display: flex;
flex-wrap: wrap;
gap: 10px;
align-items: center;
margin: 16px 0;
}
.buildertd-button {
padding: 10px 20px;
font-size: 14px;
border: none;
border-radius: 6px;
cursor: pointer;
background: #4a90e2;
color: #fff;
font-weight: 600;
}
.buildertd-button:hover { background: #3a7bc8; }
.buildertd-button-secondary {
background: #e5e5ea;
color: #333;
}
.buildertd-button-secondary:hover { background: #d5d5da; }
.buildertd-warning {
color: #c0392b;
font-weight: 600;
padding: 8px 12px;
background: #fdecea;
border-radius: 6px;
display: none;
width: 100%;
box-sizing: border-box;
}
.buildertd-warning-show { display: block; }
.buildertd-output {
width: 100%;
height: 340px;
font-family: ui-monospace, Consolas, monospace;
font-size: 13px;
padding: 8px;
border: 1px solid #ccc;
border-radius: 6px;
background: #fff;
white-space: pre;
overflow: auto;
}
</style>
</head>
<body class="buildertd-body">
<h1 class="buildertd-title">Билдер табличек с призами</h1>
<!-- Шаблон -->
<div class="buildertd-panel">
<h2 class="buildertd-panel-title">Шаблон</h2>
<textarea id="buildertdTemplate" class="buildertd-textarea buildertd-template"
placeholder="[td][align=center][img]$ССЫЛКА$[/img]
[size=10]$НАЗВАНИЕ$[/size][/align][/td]"></textarea>
<p class="buildertd-hint">
Переменные указываются как <code>$НАЗВАНИЕ_ПЕРЕМЕННОЙ$</code>.
Значение подставляется из списка, у которого первой строкой указана эта же переменная.
</p>
</div>
<!-- Списки -->
<div id="buildertdLists"></div>
<!-- Группировка -->
<div class="buildertd-panel">
<h2 class="buildertd-panel-title">Разбить на группы по</h2>
<div class="buildertd-group-row">
<label for="buildertdGroupSize" class="buildertd-group-label">Количество в группе:</label>
<input id="buildertdGroupSize" class="buildertd-group-input" type="number" min="1" step="1" placeholder="N">
</div>
<div id="buildertdPadRow" class="buildertd-checkbox-row disabled">
<input id="buildertdPadLast" type="checkbox" disabled>
<label for="buildertdPadLast">добавить пустые строки до полной группы</label>
</div>
</div>
<div class="buildertd-actions">
<button id="buildertdAddList" class="buildertd-button buildertd-button-secondary">+ Добавить список</button>
<button id="buildertdGenerate" class="buildertd-button">Сгенерировать</button>
<button id="buildertdCopy" class="buildertd-button buildertd-button-secondary">Копировать</button>
<div id="buildertdWarning" class="buildertd-warning"></div>
</div>
<!-- Вывод -->
<div class="buildertd-panel">
<h2 class="buildertd-panel-title">Результат</h2>
<textarea id="buildertdOutput" class="buildertd-output" readonly></textarea>
</div>
<script>
(function () {
const templateEl = document.getElementById('buildertdTemplate');
const listsWrap = document.getElementById('buildertdLists');
const outEl = document.getElementById('buildertdOutput');
const warnEl = document.getElementById('buildertdWarning');
const groupSizeEl = document.getElementById('buildertdGroupSize');
const padLastEl = document.getElementById('buildertdPadLast');
const padRowEl = document.getElementById('buildertdPadRow');
let nextListId = 1;
// ---- Утилиты ----
function parseLines(text) {
return text.split(/\r?\n/).map(s => s.trim());
}
function parseListBlock(text) {
const lines = parseLines(text);
while (lines.length && lines[0] === '') lines.shift();
if (lines.length === 0) {
return { error: 'Пустой список — нет строки с названием переменной' };
}
const m = lines[0].match(/^\$([^$]+)\$$/);
if (!m) {
return { error: 'Первая строка должна содержать $НАЗВАНИЕ_ПЕРЕМЕННОЙ$' };
}
const varName = m[1].trim();
if (!varName) {
return { error: 'Пустое имя переменной' };
}
const values = lines.slice(1).map(s => s.trim()).filter(s => s.length > 0);
return { varName, values };
}
function escapeRegex(s) {
return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
}
// ---- Управление списками ----
function createListBlock() {
const id = nextListId++;
const panel = document.createElement('div');
panel.className = 'buildertd-panel';
panel.dataset.listId = String(id);
panel.innerHTML = `
<div class="buildertd-list-head">
<span class="buildertd-list-num">Список ${id}</span>
<span class="buildertd-list-count" data-role="count">строк: 0</span>
<button class="buildertd-remove" data-role="remove">Удалить</button>
</div>
<textarea class="buildertd-textarea buildertd-list"
placeholder="$НАЗВАНИЕ_ПЕРЕМЕННОЙ$
значение 1
значение 2"></textarea>
<p class="buildertd-hint">
Первой строкой укажите <code>$НАЗВАНИЕ_ПЕРЕМЕННОЙ$</code>. Последующие строки — значения.
</p>
`;
const ta = panel.querySelector('textarea');
const countEl = panel.querySelector('[data-role="count"]');
function updateCount() {
const parsed = parseListBlock(ta.value);
countEl.textContent = parsed.error ? 'строк: —' : `строк: ${parsed.values.length}`;
}
ta.addEventListener('input', updateCount);
panel.querySelector('[data-role="remove"]').addEventListener('click', () => {
panel.remove();
renumberLists();
});
listsWrap.appendChild(panel);
updateCount();
return panel;
}
function renumberLists() {
const panels = listsWrap.querySelectorAll('[data-list-id]');
panels.forEach((p, idx) => {
const num = p.querySelector('.buildertd-list-num');
if (num) num.textContent = `Список ${idx + 1}`;
});
}
// ---- Логика чекбокса ----
function updatePadState() {
const val = groupSizeEl.value.trim();
const n = Number(val);
const enabled = val !== '' && Number.isFinite(n) && n >= 1 && Number.isInteger(n);
if (enabled) {
padRowEl.classList.remove('disabled');
padLastEl.disabled = false;
} else {
padRowEl.classList.add('disabled');
padLastEl.disabled = true;
padLastEl.checked = false;
}
}
groupSizeEl.addEventListener('input', updatePadState);
// ---- Предупреждения ----
function showWarning(msg) {
warnEl.textContent = msg;
warnEl.classList.add('buildertd-warning-show');
}
function hideWarning() {
warnEl.textContent = '';
warnEl.classList.remove('buildertd-warning-show');
}
// ---- Генерация ----
function buildRow(templateText, blocks, rowIndex) {
let result = templateText;
for (const block of blocks) {
const value = rowIndex < block.values.length ? block.values[rowIndex] : '';
const re = new RegExp('\\$' + escapeRegex(block.varName) + '\\$', 'g');
result = result.replace(re, value);
}
return result;
}
function generate() {
hideWarning();
outEl.value = '';
// 1. Собираем списки
const panels = Array.from(listsWrap.querySelectorAll('[data-list-id]'));
if (panels.length === 0) {
showWarning('Добавьте хотя бы один список');
return;
}
const blocks = [];
for (let i = 0; i < panels.length; i++) {
const ta = panels[i].querySelector('textarea');
const parsed = parseListBlock(ta.value);
if (parsed.error) {
showWarning(`Список ${i + 1}: ${parsed.error}`);
return;
}
blocks.push(parsed);
}
// 2. Проверяем количество
const counts = blocks.map(b => b.values.length);
const first = counts[0];
if (!counts.every(c => c === first)) {
showWarning(`Неравное количество элементов: ${counts.map((c, i) => `Список ${i + 1} — ${c}`).join(', ')}`);
return;
}
// 3. Шаблон
const templateText = templateEl.value;
if (!templateText.trim()) {
showWarning('Шаблон пуст');
return;
}
// 4. Размер группы (опционально)
const rawSize = groupSizeEl.value.trim();
let groupSize = null;
if (rawSize !== '') {
const n = Number(rawSize);
if (!Number.isFinite(n) || !Number.isInteger(n) || n < 1) {
showWarning('Размер группы должен быть целым числом ≥ 1');
return;
}
groupSize = n;
}
const padLast = padLastEl.checked && groupSize !== null;
// 5. Нет данных — нечего генерировать (но если группировка с padLast —
// пользователь просил "пустые строки до полной группы", но из пустого списка
// всё равно ничего не выйдет; требуем хотя бы одно значение)
if (first === 0) {
showWarning('Списки не содержат значений');
return;
}
// 6. Генерируем строки (с паддингом, если нужно)
let totalRows = first;
if (groupSize !== null) {
const remainder = first % groupSize;
if (remainder !== 0 && padLast) {
totalRows = first + (groupSize - remainder);
}
}
const lines = [];
for (let row = 0; row < totalRows; row++) {
lines.push(buildRow(templateText, blocks, row));
}
// 7. Группируем
if (groupSize === null) {
outEl.value = lines.join('\n');
} else {
const groups = [];
for (let i = 0; i < lines.length; i += groupSize) {
groups.push(lines.slice(i, i + groupSize).join('\n'));
}
outEl.value = groups.join('\n\n');
}
}
// ---- Копирование ----
async function copyOutput() {
if (!outEl.value) return;
try {
await navigator.clipboard.writeText(outEl.value);
const btn = document.getElementById('buildertdCopy');
const old = btn.textContent;
btn.textContent = 'Скопировано!';
setTimeout(() => btn.textContent = old, 1200);
} catch (e) {
outEl.select();
document.execCommand('copy');
}
}
// ---- События ----
document.getElementById('buildertdAddList').addEventListener('click', () => {
createListBlock();
renumberLists();
});
document.getElementById('buildertdGenerate').addEventListener('click', generate);
document.getElementById('buildertdCopy').addEventListener('click', copyOutput);
// Начальные списки
createListBlock();
createListBlock();
renumberLists();
updatePadState();
})();
</script>
|
Вы здесь » dust ultimate two » Организация » для сбора табличке 2