Files
gustavoo-me/wp-content/plugins/automatic-ai-posts/admin/admin.js
T
Gustavo 0ea3651fb5 Automatic AI Posts, setup automatizado e seeder de permalinks/avatar
Plugin automatic-ai-posts (v2.0.0)
- Pipeline completo: coleta de feeds RSS/Atom, filtro por IA, reescrita,
  categoria, imagem de capa e agendamento
- Página de configuração com teste real do provedor de IA
- Editor: botão para refazer imagem de capa e metadados, textos em PT-BR
- Ajuda com botão Pix (copia-e-cola + QR) e sem nota de avaliação
- Cache-busting por mtime nos assets do admin

Tema gustavoo-portfolio
- Marca do cabeçalho reduzida a "Gustavo"
- Suporte a título próprio da marca

Setup (docker/wp-init.sh, compose.yaml)
- Plugins de terceiros (Post Views Counter, Rank Math, Site Kit)
  instalados do WordPress.org em vez de versionados
- Falha explícita ao final se algum plugin obrigatório não instalar
- WPCLI_UID/WPCLI_GID configuráveis

Seeder do tema
- Links permanentes "Dia e nome" e avatar "Iniciais" aplicados
  apenas em instalação nova, com regravação do .htaccess
2026-11-02 13:33:30 -03:00

803 lines
31 KiB
JavaScript

jQuery(function ($) {
if (typeof aapData === 'undefined') return;
var ajaxurl = aapData.ajaxurl;
var s = aapData.s; // translated strings
var $doc = $(document);
var $form = $('#aap-settings-form');
var $exec = $('#rap-auto-interval');
// O <form> é fechado logo após o grid de configurações (para que os botões de
// ação dos outros painéis não o enviem). Por isso os campos do formulário são
// mantidos em sincronia com os valores únicos exibidos na aba Execuções:
// - intervalo do automático: preenchido a partir do valor salvo
// - categorias GNews: os checkboxes só existem no formulário principal
function mirrorFromSaved() {
if (!$exec.length) return;
$exec.val(aapData.interval_value);
$('#rap-auto-unit').val(aapData.interval_unit);
}
function mirrorToForm() {
if (!$exec.length) return;
$('input[name="aap_auto_interval_value"]').val($exec.val());
$('select[name="aap_auto_interval_unit"]').val($('#rap-auto-unit').val());
}
// O destaque dos botões de categoria é controlado pela rolagem (scroll spy)
// ── Doação Pix: copiar a chave ──────────────────────────────────────────
$doc.on('click', '#aap-pix-btn', function () {
var key = String($(this).data('pix') || '');
if (!key) return;
var $feedback = $('#aap-pix-feedback');
function done() {
$feedback.removeClass('is-error').addClass('is-success').text(s.pix_copied || 'OK');
setTimeout(function () {
$feedback.text('').removeClass('is-success');
}, 4000);
}
function fallback() {
var $tmp = $('<textarea>').val(key).css({ position: 'fixed', opacity: 0 }).appendTo('body');
$tmp[0].select();
try { document.execCommand('copy'); done(); } catch (e) {}
$tmp.remove();
}
if (navigator.clipboard && navigator.clipboard.writeText) {
navigator.clipboard.writeText(key).then(done).catch(fallback);
} else {
fallback();
}
});
// ── Teste da conexão com a IA ───────────────────────────────────────────
$doc.on('click', '#aap-test-ai-btn', function () {
var $btn = $(this);
var $feedback = $('#aap-test-ai-feedback');
var i18n = aapData.ai_test || {};
if ($btn.data('busy')) return;
$btn.data('busy', true).prop('disabled', true);
$feedback.removeClass('is-error is-success').addClass('is-pending')
.text(i18n.testing || '...');
$.post(aapData.ajaxurl, {
action: 'aap_test_ai',
nonce: $btn.data('nonce')
})
.done(function (res) {
var data = (res && res.data) || {};
if (res && res.success) {
$feedback.removeClass('is-pending').addClass('is-success').text(
(i18n.ok || 'OK') + ' — ' + data.provider
+ (data.model ? ' · ' + data.model : '')
+ (data.ms ? ' (' + data.ms + ' ms)' : '')
);
} else {
$feedback.removeClass('is-pending').addClass('is-error')
.text(data.error || i18n.error || 'Error.');
}
})
.fail(function () {
$feedback.removeClass('is-pending').addClass('is-error')
.text(i18n.error || 'Error.');
})
.always(function () {
$btn.data('busy', false).prop('disabled', false);
});
});
// ── Provedor de IA: mostra apenas os campos do provedor ativo ───────────
(function providerFields() {
var $select = $('#aap_ai_provider');
if (!$select.length) return;
var defaults = aapData.provider_defaults || {};
function refresh() {
var id = $select.val();
$('.aap-provider-block').each(function () {
var $block = $(this);
$block.prop('hidden', String($block.data('provider')) !== String(id));
});
var $model = $('#aap_model');
if ($model.length && defaults[id]) {
$model.attr('placeholder', defaults[id]);
var known = $.map(defaults, function (value) { return value; });
var value = $model.val();
if (value === '' || $.inArray(value, known) !== -1) {
$model.val(defaults[id]);
}
}
}
$select.on('change', refresh);
refresh();
})();
// ── Alterações não salvas: barra flutuante + descartar ──────────────────
(function unsavedChanges() {
if (!$form.length) return;
var $float = $('#aap-save-float');
var $discard = $('#aap-discard-all');
if (!$float.length) return;
var fields = function () {
return $form.find('input, select, textarea').filter(':enabled');
};
// Fotografia do estado inicial de cada campo
function snapshot() {
var snap = {};
fields().each(function () {
var el = this;
var key = el.name || el.id;
if (!key) return;
if (el.type === 'checkbox' || el.type === 'radio') {
snap[key + '::' + (el.value || '')] = el.checked;
} else {
snap[key] = el.value;
}
});
return snap;
}
var initial = snapshot();
function isDirty() {
var now = snapshot();
var k;
for (k in now) {
if (now[k] !== initial[k]) return true;
}
for (k in initial) {
if (!(k in now)) return true;
}
return false;
}
function refresh() {
$float.prop('hidden', !isDirty());
}
// Desfaz tudo o que foi digitado desde o carregamento
function restore() {
fields().each(function () {
var el = this;
var key = el.name || el.id;
if (!key) return;
if (el.type === 'checkbox' || el.type === 'radio') {
var k = key + '::' + (el.value || '');
if (k in initial) el.checked = initial[k];
} else if (key in initial) {
el.value = initial[key];
}
});
}
$form.on('input change', 'input, select, textarea', refresh);
$discard.on('click', function () {
restore();
refresh();
// Recoloca o foco no início do conteúdo para o usuário ver o estado original
var body = document.querySelector('#aap-app .aap-panel:not([hidden]) .aap-panel-body');
if (body) body.scrollTo({ top: 0, behavior: 'smooth' });
});
refresh();
})();
// ── Salva configurações antes de executar ────────────────────────────────
function saveSettings() {
mirrorToForm();
if (!$form.length) return $.Deferred().resolve().promise();
return $.ajax({
url: aapData.options_url,
type: 'POST',
data: $form.serialize(),
});
}
function feedback($el, text, type) {
$el.removeClass('is-error is-success is-warn is-pending').addClass(type ? 'is-' + type : '').html(text);
}
// ── Links de categoria (âncoras) ────────────────────────────────────────
$doc.on('click', '#aap-app .aap-cat-links a', function (e) {
var $card = $($(this).attr('href'));
if (!$card.length) return;
e.preventDefault();
// Rola até o card
// No layout de aplicação o conteúdo rola dentro do painel; fora dele,
// a página inteira é que rola.
var body = document.querySelector('#aap-app .aap-panel:not([hidden]) .aap-panel-body');
if (body && body.scrollHeight > body.clientHeight + 2) {
var delta = $card[0].getBoundingClientRect().top - body.getBoundingClientRect().top;
body.scrollTo({ top: Math.max(0, body.scrollTop + delta - 16), behavior: 'smooth' });
} else {
var top = $card[0].getBoundingClientRect().top + window.pageYOffset - aapHeadHeight() - 16;
window.scrollTo({ top: Math.max(0, top), behavior: 'smooth' });
}
$('#aap-app .aap-cat-links a').removeClass('is-active');
$(this).addClass('is-active');
$card.addClass('aap-flash');
setTimeout(function () { $card.removeClass('aap-flash'); }, 1400);
});
// ── Destaque automático do botão da seção visível (scroll spy) ──────────
(function catScrollSpy() {
var links = Array.prototype.slice.call(
document.querySelectorAll('#aap-app .aap-cat-links a')
);
if (!links.length) return;
var body = document.querySelector('#aap-app .aap-panel:not([hidden]) .aap-panel-body');
var head = document.querySelector('#aap-app .aap-sticky-head');
function update() {
// Linha de referência: logo abaixo do cabeçalho visível
var threshold = (head ? head.getBoundingClientRect().bottom : 0) + 24;
var current = links[0];
links.forEach(function (link) {
var card = document.getElementById(link.getAttribute('data-cat'));
if (!card) return;
if (card.getBoundingClientRect().top <= threshold) current = link;
});
// A última seção costuma ser curta e nunca chega ao topo:
// no fim da rolagem ela é a ativa.
var atBottom;
if (body && body.scrollHeight > body.clientHeight + 2) {
atBottom = body.scrollTop + body.clientHeight >= body.scrollHeight - 2;
} else {
atBottom = window.innerHeight + window.pageYOffset >=
document.documentElement.scrollHeight - 2;
}
if (atBottom) current = links[links.length - 1];
links.forEach(function (link) {
link.classList.toggle('is-active', link === current);
});
}
// Evita recalcular a cada pixel: no máximo um cálculo por quadro
var ticking = false;
function onScroll() {
if (ticking) return;
ticking = true;
requestAnimationFrame(function () {
ticking = false;
update();
});
}
// Rola dentro do painel (desktop) e na janela (mobile)
if (body) body.addEventListener('scroll', onScroll, { passive: true });
window.addEventListener('scroll', onScroll, { passive: true });
window.addEventListener('resize', onScroll, { passive: true });
update();
})();
// ── Cabeçalho fixo de cada aba ─────────────────────────────────────────
function adminBarHeight() {
var bar = document.getElementById('wpadminbar');
if (!bar) return 0;
var r = bar.getBoundingClientRect();
return r.bottom > 0 ? Math.round(r.height) : 0; // 32px (desktop) / 46px (mobile)
}
// Altura total ocupada no topo = barra do WordPress (quando visível) + header
function aapHeadHeight() {
var head = document.querySelector('#aap-app .aap-sticky-head');
return head ? Math.round(head.getBoundingClientRect().height) + adminBarHeight() : 0;
}
(function stickyHead() {
var head = document.querySelector('#aap-app .aap-sticky-head');
var side = document.querySelector('#aap-app .aap-sidebar-inner');
if (!head && !side) return;
function update() {
var barH = adminBarHeight();
var foot = document.getElementById('wpfooter');
var footH = (foot && getComputedStyle(foot).display !== 'none')
? Math.round(foot.getBoundingClientRect().height) : 0;
// Altura do rodapé usada no layout do painel (desktop)
document.documentElement.style.setProperty('--aap-footer-h', footH + 'px');
// Cola exatamente abaixo da barra do WordPress; se ela sair da tela, cola no topo
if (head) {
head.style.top = barH + 'px';
head.classList.toggle('is-stuck', window.pageYOffset > 0);
document.documentElement.style.setProperty('--aap-head-h', aapHeadHeight() + 'px');
}
// A sidebar fica fixa: sai do fluxo da página e não é afetada pelo fim
// da coluna (era isso que fazia os itens "subirem" no fim da rolagem).
if (side) {
if (window.matchMedia('(max-width: 782px)').matches) {
// No mobile ela é empilhada no topo, sem posicionamento fixo
clearSide();
} else {
var colRect = side.parentElement.getBoundingClientRect();
// No topo da página o app começa abaixo da barra (e de avisos);
// ao rolar ele encosta na barra e ali permanece.
var top = Math.max(barH, Math.round(colRect.top));
var h = Math.max(200, Math.round(window.innerHeight - top - footH));
setSide('position', 'fixed');
setSide('left', Math.round(colRect.left) + 'px');
setSide('width', Math.round(colRect.width) + 'px');
setSide('top', top + 'px');
setSide('height', h + 'px');
}
}
}
// Escreve no estilo só quando o valor muda, evitando recálculos por quadro
var last = {};
function setSide(prop, value) {
if (last[prop] === value) return;
last[prop] = value;
side.style[prop] = value;
}
function clearSide() {
['position', 'left', 'width', 'top', 'height'].forEach(function (p) {
if (last[p] !== '') { last[p] = ''; side.style[p] = ''; }
});
}
update();
window.addEventListener('scroll', update, { passive: true });
window.addEventListener('resize', update, { passive: true });
})();
// ── Preferências de log (compartilhadas entre configurações e aba log) ──
function pref(key, value) {
try {
if (value === undefined) return localStorage.getItem('aap_' + key) !== '0';
localStorage.setItem('aap_' + key, value ? '1' : '0');
} catch (err) {}
return value;
}
$doc.on('change', '.aap-pref-autoscroll', function () {
var on = this.checked;
$('.aap-pref-autoscroll').prop('checked', on);
pref('autoscroll', on);
});
$doc.on('change', '.aap-pref-live', function () {
var on = this.checked;
$('.aap-pref-live').prop('checked', on);
pref('live', on);
if (on) startLogPolling(); else stopLogPolling();
});
// ── Mensagem de "configurações salvas" ──────────────────────────────────
if (/[?&](aap-saved=1|settings-updated=true)/.test(window.location.search)) {
var $toast = $('#aap-save-toast');
var $saveFb = $('#aap-save-feedback');
if ($toast.length && $saveFb.length) {
$saveFb.text(s.settings_saved);
$toast.prop('hidden', false);
setTimeout(function () { $toast.prop('hidden', true); }, 5000);
// Limpa a marca da URL para não repetir o aviso ao recarregar
if (window.history && window.history.replaceState) {
var clean = window.location.href
.replace(/([?&])aap-saved=1&?/, '$1')
.replace(/([?&])settings-updated=true&?/, '$1')
.replace(/[?&]$/, '');
window.history.replaceState({}, '', clean);
}
}
}
// ── Estado de execução ──────────────────────────────────────────────────
var $execSection = $('#rap-exec-section');
var $execText = $('#rap-exec-text');
var $execSub = $('#rap-exec-sub');
var $progress = $('#rap-progress');
var $runStatus = $('#rap-run-status');
var $abortBtn = $('#rap-abort-btn');
var $abortStatus = $('#rap-abort-status');
var $lastRun = $('#rap-last-run');
function setBadge(processing, abortRequested) {
if ($execSection.length) {
$execSection.removeClass('is-idle is-aborting');
if (processing) {
$execSection.toggleClass('is-aborting', !!abortRequested);
$progress.toggleClass('is-indeterminate', true);
$execText.text(abortRequested ? s.aborting : s.running);
if (aapData.started_label) $execSub.text(aapData.started_label);
} else {
$execSection.addClass('is-idle');
$progress.removeClass('is-indeterminate');
$execText.text(s.no_execution);
$execSub.text(idleDesc);
}
if (processing) {
$abortBtn.prop('disabled', false).show();
} else {
$abortBtn.hide();
$abortStatus.text('');
if (($runStatus.text() || '') === s.processing_feeds) $runStatus.text('');
}
}
}
// Subtítulo do estado ocioso (vem pronto do servidor)
var idleDesc = aapData.idle_desc || $execSub.text();
// ── Botão "Executar agora" ──────────────────────────────────────────────
var $runBtn = $('#rap-run-now');
function setRunBtn(running) {
$runBtn.prop('disabled', running);
var label = running ? s.running : s.run_now;
var $lbl = $runBtn.find('span');
if ($lbl.length) $lbl.text(label); else $runBtn.text(label);
$runBtn.toggleClass('is-busy', running);
}
function showRunError(msg) {
feedback($runStatus, s.error_prefix + ' ' + msg, 'error');
}
// ── Polling permanente — o servidor é a fonte da verdade ────────────────
var serverProcessing = aapData.processing;
var consecutiveErrors = 0;
var wasProcessing = serverProcessing;
setBadge(serverProcessing, false);
setRunBtn(serverProcessing);
function handleAjaxError(xhr) {
if (xhr.status === 403 || xhr.status === 400) {
window.location.reload();
}
consecutiveErrors++;
}
function pollStatus() {
$.post(ajaxurl, { action: 'aap_check_status', nonce: aapData.nonce_status })
.done(function (res) {
consecutiveErrors = 0;
if (!res.success) return;
var was = serverProcessing;
serverProcessing = res.data.processing;
setBadge(serverProcessing, res.data.abort_requested);
setRunBtn(serverProcessing);
if (res.data.last_run && $lastRun.length) {
$lastRun.html(s.last_run_prefix + ' <strong>' + $('<span>').text(res.data.last_run).html() + '</strong>');
}
// Execução terminou → recarrega a lista de últimos posts uma única vez
if (was && !serverProcessing && aapData.tab === 'execucoes') {
setTimeout(function () {
if (window.location.hash !== '#no-reload') window.location.reload();
}, 1500);
}
})
.fail(handleAjaxError);
}
setInterval(pollStatus, 3000);
// ── Iniciar pipeline (retorna imediatamente — roda em processo separado) ─
$runBtn.on('click', function () {
setRunBtn(true);
setBadge(true, false);
feedback($runStatus, s.preparing, 'pending');
$abortStatus.text('');
saveSettings().always(function () {
$.post(ajaxurl, { action: 'aap_start_pipeline', nonce: aapData.nonce_start })
.done(function (res) {
if (!res.success) {
showRunError(res.data || s.error_start);
setRunBtn(false);
setBadge(false, false);
} else {
feedback($runStatus, s.processing_feeds, 'pending');
}
})
.fail(function (xhr) {
var detail = xhr.responseText ? xhr.responseText.substring(0, 300) : s.comm_error;
showRunError('HTTP ' + xhr.status + ' — ' + detail);
setRunBtn(false);
setBadge(false, false);
});
});
});
// ── Abortar — encerra o processo imediatamente via PID ──────────────────
function doAbort() {
$abortBtn.prop('disabled', true);
feedback($abortStatus, s.aborting, 'pending');
setBadge(true, true);
$.post(ajaxurl, { action: 'aap_abort_execution', nonce: aapData.nonce_abort })
.done(function (res) {
if (res.success) {
setBadge(false, false);
setRunBtn(false);
$abortStatus.text('');
feedback($runStatus, s.aborted, 'warn');
} else {
feedback($abortStatus, res.data || s.error_abort, 'error');
$abortBtn.prop('disabled', false);
setBadge(serverProcessing, false);
}
})
.fail(function () {
feedback($abortStatus, s.comm_error, 'error');
$abortBtn.prop('disabled', false);
});
}
$abortBtn.on('click', doAbort);
// ── Geração automática ──────────────────────────────────────────────────
var $toggleBtn = $('#rap-toggle-auto');
var $autoBadge = $('#rap-auto-badge');
var $autoBadgeDot = $('#rap-auto-badge-dot');
var $autoBadgeText = $('#rap-auto-badge-text');
var $nextRunEl = $('#rap-next-run');
var $autoModeWrap = $('#rap-auto-mode-wrap');
var $modeSchedule = $('#rap-mode-schedule');
var $scheduleWrap = $('#rap-schedule-wrap');
var $scheduledAt = $('#rap-scheduled-at');
var $autoStatus = $('#rap-auto-status');
var $interval = $('#rap-auto-interval');
var $unit = $('#rap-auto-unit');
var autoEnabled = aapData.auto_enabled;
// Mostra/esconde o campo de agendamento conforme o rádio selecionado
$('input[name="aap_auto_mode"]').on('change', function () {
if ($modeSchedule.is(':checked')) {
$scheduleWrap.show();
if (!$scheduledAt.val()) {
var d = new Date();
d.setHours(d.getHours() + 1, 0, 0, 0);
var pad = function (n) { return String(n).padStart(2, '0'); };
$scheduledAt.val(d.getFullYear() + '-' + pad(d.getMonth() + 1) + '-' + pad(d.getDate()) + 'T' + pad(d.getHours()) + ':00');
}
} else {
$scheduleWrap.hide();
}
});
// Intervalo do automático também é uma opção salva — o valor editável fica na aba
// Execuções, por isso o formulário de configurações é atualizado antes de qualquer save.
$interval.add($unit).on('change', mirrorToForm);
mirrorToForm();
function updateAutoUI(enabled, nextRun) {
autoEnabled = enabled;
$autoBadgeDot.toggleClass('is-on', !!enabled);
$autoBadgeText.text(enabled ? s.auto_active : s.auto_inactive);
$toggleBtn.text(enabled ? s.stop_auto : s.start_auto).toggleClass('aap-btn-outline', !!enabled).toggleClass('aap-btn-primary', !enabled);
if (enabled) {
$autoModeWrap.hide();
if (nextRun) $nextRunEl.html(s.next_run + ' <strong>' + nextRun + '</strong>').show();
} else {
$autoModeWrap.show();
$nextRunEl.hide();
}
}
if (aapData.auto_enabled) updateAutoUI(true, aapData.next_run);
$toggleBtn.on('click', function () {
var $btn = $(this).prop('disabled', true);
var enable = !autoEnabled;
if (!enable) {
// Parando o automático — salva configs antes
feedback($autoStatus, '<em>' + s.wait + '</em>', 'pending');
saveSettings().always(function () {
$.post(ajaxurl, { action: 'aap_toggle_auto', nonce: aapData.nonce_toggle_auto, enable: 0 })
.done(function (res) {
if (res.success) {
updateAutoUI(false, '');
feedback($autoStatus, $('<span>').text(s.auto_paused).html(), 'pending');
} else {
feedback($autoStatus, s.error_prefix + ' ' + (res.data || s.error_start), 'error');
}
})
.fail(function () { feedback($autoStatus, s.comm_error, 'error'); })
.always(function () { $btn.prop('disabled', false); });
});
return;
}
// Iniciando o automático — valida o agendamento se necessário
var mode = $modeSchedule.is(':checked') ? 'schedule' : 'now';
var scheduledAt = 0;
if (mode === 'schedule') {
var val = $scheduledAt.val();
if (!val) {
feedback($autoStatus, s.schedule_required, 'error');
$btn.prop('disabled', false);
return;
}
scheduledAt = Math.floor(new Date(val).getTime() / 1000);
if (scheduledAt <= Math.floor(Date.now() / 1000)) {
feedback($autoStatus, s.schedule_past, 'error');
$btn.prop('disabled', false);
return;
}
}
feedback($autoStatus, '<em>' + s.wait + '</em>', 'pending');
// Salva configs (incluindo o intervalo exibido aqui) antes de iniciar
saveSettings().always(function () {
$.post(ajaxurl, {
action: 'aap_toggle_auto',
nonce: aapData.nonce_toggle_auto,
enable: 1,
mode: mode,
scheduled_at: scheduledAt,
interval_value: $interval.val(),
interval_unit: $unit.val(),
})
.done(function (res) {
if (res.success) {
updateAutoUI(res.data.enabled, res.data.next_run);
feedback($autoStatus, res.data.enabled ? s.auto_started : s.auto_paused, res.data.enabled ? 'success' : 'pending');
} else {
feedback($autoStatus, s.error_prefix + ' ' + (res.data || s.error_start), 'error');
}
})
.fail(function () { feedback($autoStatus, s.comm_error, 'error'); })
.always(function () { $btn.prop('disabled', false); });
});
});
// ── Log (banco de dados, paginado) ─────────────────────────────────────
var $logBox = $('#rap-log-box');
var $logStatus = $('#rap-log-status');
var $logMore = $('#rap-log-more');
var $logLevel = $('#rap-log-level');
var logPage = 1;
var logFetching = false;
var logTimer = null;
var logIds = {};
if (pref('live', undefined) === false) {
$('.aap-pref-live').prop('checked', false);
}
function logRow(row) {
var cls = row.level === 'error' ? 'erro' : row.level === 'warn' ? 'aviso' : 'info';
return '<div class="aap-log-line" data-id="' + row.id + '">'
+ '<span class="aap-log-time">' + $('<span>').text(row.time).html() + '</span>'
+ '<span class="aap-log-level aap-log-level-' + cls + '">' + String(row.level).toUpperCase() + '</span>'
+ '<span class="aap-log-msg">' + $('<span>').text(row.message).html() + '</span>'
+ '</div>';
}
function appendRows(rows, prepend) {
var html = '';
$.each(rows, function (_, row) {
if (logIds[row.id]) return;
logIds[row.id] = true;
html += logRow(row);
});
if (!html) return;
if (prepend) {
$logBox.prepend(html);
} else {
$logBox.append(html);
}
}
function fetchLogs(page, mode) {
if (logFetching || !$logBox.length) return;
logFetching = true;
$.post(ajaxurl, {
action: 'aap_fetch_logs',
nonce: aapData.nonce_logs,
page: page,
per_page: aapData.log_per_page || 50,
level: $logLevel.length ? $logLevel.val() : ''
})
.done(function (res) {
if (!res.success) return;
var rows = res.data.rows || [];
if (mode === 'reset') {
$logBox.empty();
logIds = {};
}
appendRows(rows, mode === 'new');
if (!$logBox.children().length) {
$logBox.html('<span class="aap-log-empty">' + $('<span>').text(s.log_empty).html() + '</span>');
} else {
$logBox.find('.aap-log-empty').remove();
}
$logMore.prop('hidden', !res.data.has_more);
$logStatus.text(s.log_updated + ' ' + new Date().toLocaleTimeString());
})
.fail(function () {
$logStatus.text(s.comm_error);
})
.always(function () {
logFetching = false;
});
}
function reloadLogs() {
logPage = 1;
fetchLogs(1, 'reset');
}
function startLogPolling() {
if (!$logBox.length) return;
stopLogPolling();
reloadLogs();
if (pref('live', undefined)) {
logTimer = setInterval(function () { fetchLogs(1, 'new'); }, 4000);
}
}
function stopLogPolling() {
clearInterval(logTimer);
logTimer = null;
}
if ($logMore.length) {
$logMore.on('click', function () {
logPage += 1;
fetchLogs(logPage, 'more');
});
}
if ($logLevel.length) {
$logLevel.on('change', reloadLogs);
}
document.addEventListener('visibilitychange', function () {
if (!$logBox.length) return;
if (document.hidden) stopLogPolling(); else startLogPolling();
});
if ($logBox.length) startLogPolling();
});