Files
kate522andClaude Opus 5 427968a721 chore(branding): remove leftover upstream theme identifiers
The theme was renamed Zibll/子比 → AKSRT → Jio, but upstream branding
survived the sweeps in CSS classes, the SVG sprite prefix, a persisted
widget field ID, JS events, admin Vue class names, test infrastructure
and docblocks.

Renames:
- zib-widget / zib-widget-wrap / zib-slider → jio-widget-box /
  jio-widget-box-wrap / jio-slider-box (jio-widget is taken by the modern
  widget shell, jio-box-* by the card primitives)
- zib-partners-scroll + zps-* → jio-partners-scroll + jps-*
- zib-combo-info-card + zci-* → jio-combo-info-card + jci-*
- zibsvg- → jiosvg-, including the exported picker helpers and the
  fa-zibsvg- family-prefix guard
- zibpay_type → pay_type; the dual-read fallback in PostQuery collapses
  to a single key
- [zibajax="submit"] dropped (no PHP emitter); zib_ajax.success →
  jio_ajax.success
- aks-* admin Vue classes → jio-*
- zib_palette() → palette()

User-facing vendor traces are gone: the floating "more" button default no
longer ships a zibll.com link or 子比主题 marketing copy, the notice
widget ships no preset promo entries, and the four 官网教程 links are
removed from the post-list admin schema.

Test infrastructure renamed to neutral names (docker-compose.reference.yml,
Caddyfile, article-baseline.json, modern-widgets-contract.test.cjs) with
their DB/volume/env/slug identifiers following.

Kept intentionally, documented in tests/fixtures/README.md: the golden
fixture reference fingerprint (a SHA-256 record of an external directory,
read by validate-widget-golden / generate-widget-golden-fixture /
audit-widget-system), the doesNotMatch guards that prove the old tokens are
gone, and the reference theme's own meta key in the visual fixtures.

No backward-compat or data migration: saved widget configs holding old
icon keys or the old field ID silently lose them, per the project's
rewrite-over-compatibility rule.

Verified: 132/132 node contract tests, php tests/widget-system/run.php
(146 assertions), audit-admin-contracts, audit-psr4-paths and
validate-widget-golden all green; tracked bundles regenerated and in sync.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-11-02 00:06:54 +08:00

326 lines
15 KiB
JavaScript

/* Sidebar toolbar: per-area export / import / template import. */
(function ($) {
'use strict';
const config = window.jioWidgetAdmin || {};
const t = (key) => (config.i18n && config.i18n[key]) || key;
function post(action, data) {
return $.ajax({
url: config.ajaxUrl,
type: 'POST',
dataType: 'json',
data: $.extend({ action, nonce: config.transferNonce }, data || {}),
});
}
function failMessage(res, xhr, fallbackKey) {
return res?.data?.message || xhr?.responseJSON?.data?.message || t(fallbackKey);
}
function clearResult($result) {
return $result.removeClass('is-ok is-warn is-err').empty();
}
function showError($result, message) {
clearResult($result).addClass('is-err').text(message);
}
function reloadButton() {
return $('<p class="jio-wie-modal__reload-wrap"></p>').append(
$('<button type="button" class="button button-primary jio-wie-modal__reload"></button>').text(t('reloadPage'))
);
}
function renderImportResult($result, res, autoReload) {
clearResult($result);
if (!res?.success || !res.data) {
showError($result, failMessage(res, null, 'importFailed'));
return;
}
const warnings = Array.isArray(res.data.warnings) ? res.data.warnings : [];
$result.addClass(warnings.length ? 'is-warn' : 'is-ok');
[res.data.message || t('importSuccess')].concat(warnings).forEach((line, index) => {
if (index) $result.append('<br>');
$result.append(document.createTextNode(String(line)));
});
$result.append(reloadButton());
if (autoReload) window.setTimeout(() => window.location.reload(), 200);
}
function withButtonLoading($button, loadingText, idleText, request) {
$button.prop('disabled', true).text(loadingText);
return request().always(() => $button.prop('disabled', false).text(idleText));
}
function importModeHtml(name) {
return `<div class="jio-wie-modal__import-mode">
<div class="jio-wie-modal__import-mode-title description"></div>
<label class="jio-wie-modal__import-mode-row"><input type="radio" name="${name}" value="replace"> <span data-mode-label="replace"></span></label>
<label class="jio-wie-modal__import-mode-row"><input type="radio" name="${name}" value="append" checked> <span data-mode-label="append"></span></label>
</div>`;
}
function createModal(opts) {
let $modal = null;
function ensure() {
if ($modal) return $modal;
$modal = $(`<div id="${opts.id}" class="jio-wie-modal${opts.extraClass ? ' ' + opts.extraClass : ''}" aria-hidden="true">
<div class="jio-wie-modal__backdrop" data-jio-wie-close></div>
<div class="jio-wie-modal__dialog" role="dialog" aria-modal="true">
<button type="button" class="jio-wie-modal__close" data-jio-wie-close aria-label="${t('close')}">&times;</button>
${opts.bodyHtml}
${opts.actionsHtml}
<p class="jio-wie-modal__result" aria-live="polite"></p>
</div>
</div>`).appendTo('body');
$modal.find('.jio-wie-modal__import-mode-title').text(t('importModeLabel'));
$modal.find('[data-mode-label="replace"]').text(t('importModeReplace'));
$modal.find('[data-mode-label="append"]').text(t('importModeAppend'));
$modal.find('.jio-wie-modal__cancel').text(t('close'));
$modal.on('click', '[data-jio-wie-close], .jio-wie-modal__cancel', close);
if (opts.onInit) opts.onInit($modal);
return $modal;
}
function open() {
ensure().find('input[name$="-import-mode"][value="append"]').prop('checked', true);
clearResult($modal.find('.jio-wie-modal__result'));
$modal.addClass('is-open').attr('aria-hidden', 'false');
}
function close() {
if ($modal) $modal.removeClass('is-open').attr('aria-hidden', 'true');
}
return { ensure, open, close, $result: () => ensure().find('.jio-wie-modal__result') };
}
const state = { mode: 'export', sidebarId: '', templateSidebarId: '' };
const ioModal = createModal({
id: 'jio-wie-modal',
bodyHtml: `<h3 class="jio-wie-modal__title"></h3>
<p class="jio-wie-modal__hint description"></p>
<textarea class="jio-wie-modal__textarea" spellcheck="false"></textarea>
${importModeHtml('jio-wie-import-mode')}`,
actionsHtml: `<p class="jio-wie-modal__actions">
<button type="button" class="button jio-wie-modal__copy"></button>
<button type="button" class="button button-primary jio-wie-modal__submit"></button>
<button type="button" class="button jio-wie-modal__cancel"></button>
</p>`,
onInit($modal) {
$modal.find('.jio-wie-modal__copy').on('click', function () {
const textarea = $modal.find('.jio-wie-modal__textarea')[0];
textarea.select();
textarea.setSelectionRange(0, textarea.value.length);
try {
document.execCommand('copy');
$(this).text(t('copyDone'));
} catch (_error) {
window.alert(t('copyFail'));
}
});
$modal.find('.jio-wie-modal__submit').on('click', submitImport);
},
});
const templateModal = createModal({
id: 'jio-wie-template-modal',
extraClass: 'jio-wie-template-layer',
bodyHtml: `<h3 class="jio-wie-modal__title"></h3>
<div class="jio-wie-modal__hint description"></div>
<div class="jio-wie-template-list-wrap"><p class="jio-wie-template-list-status description"></p><div class="jio-wie-template-list"></div></div>
${importModeHtml('jio-wie-template-import-mode')}`,
actionsHtml: `<p class="jio-wie-modal__actions">
<button type="button" class="button button-primary jio-wie-template-submit"></button>
<button type="button" class="button jio-wie-modal__cancel"></button>
</p>`,
onInit($modal) {
$modal.find('.jio-wie-modal__title').text(t('templateModalTitle'));
// templateHint is server-built, escaped translation markup.
$modal.find('.jio-wie-modal__hint').html(t('templateHint'));
$modal.find('.jio-wie-template-submit').text(t('templateSubmit')).on('click', submitTemplate);
},
});
function openIoModal(mode, sidebarId) {
state.mode = mode;
state.sidebarId = sidebarId;
const $modal = ioModal.ensure();
const isExport = mode === 'export';
$modal.find('.jio-wie-modal__title').text(t(isExport ? 'exportTitle' : 'importTitle'));
$modal.find('.jio-wie-modal__hint').text(t(isExport ? 'exportHint' : 'importHint'));
$modal.find('.jio-wie-modal__textarea').prop('readonly', isExport).val('');
$modal.find('.jio-wie-modal__copy').toggle(isExport).text(t('copy'));
$modal.find('.jio-wie-modal__submit').toggle(!isExport).text(t('submitImport')).prop('disabled', false);
$modal.find('.jio-wie-modal__import-mode').toggle(!isExport);
ioModal.open();
if (isExport) fetchExport();
}
function fetchExport() {
const $textarea = ioModal.ensure().find('.jio-wie-modal__textarea').val(t('loadingExport'));
post('jio_widgets_export_sidebar', { sidebar_id: state.sidebarId })
.done((res) => {
if (res?.success && res.data?.json_text) {
$textarea.val(res.data.json_text);
return;
}
$textarea.val('');
showError(ioModal.$result(), failMessage(res, null, 'exportFailed'));
})
.fail((xhr) => {
$textarea.val('');
showError(ioModal.$result(), failMessage(null, xhr, 'networkError'));
});
}
function submitImport() {
const $modal = ioModal.ensure();
const mode = $modal.find('input[name="jio-wie-import-mode"]:checked').val() || 'append';
if (!window.confirm(t(mode === 'append' ? 'confirmImportAppend' : 'confirmImportReplace'))) return;
const raw = String($modal.find('.jio-wie-modal__textarea').val() || '').trim();
if (!raw) {
showError(ioModal.$result(), t('pasteJsonFirst'));
return;
}
withButtonLoading($modal.find('.jio-wie-modal__submit'), t('loadingImport'), t('submitImport'), () =>
post('jio_widgets_import_sidebar', { sidebar_id: state.sidebarId, json: raw, import_mode: mode })
.done((res) => {
if (res?.success) $modal.find('.jio-wie-modal__textarea').val('');
renderImportResult(ioModal.$result(), res, false);
})
.fail((xhr) => showError(ioModal.$result(), failMessage(null, xhr, 'networkError')))
);
}
function templateItem(template) {
const $meta = $('<span class="jio-wie-template-item-meta"></span>')
.append($('<span class="jio-wie-template-item-title"></span>').text(template.title || template.id));
if (template.desc) $meta.append($('<span class="jio-wie-template-item-desc"></span>').text(template.desc));
if (template.remind) {
$meta.append($('<span class="jio-wie-template-item-remind"></span>')
.append('<span class="dashicons dashicons-info-outline" aria-hidden="true"></span>')
.append(document.createTextNode(template.remind)));
}
if (Array.isArray(template.modules) && template.modules.length) {
$meta.append($('<span class="jio-wie-template-item-modules"></span>').text(template.modules.map((module) => module.title).join(' / ')));
}
const $label = $('<label class="jio-wie-template-item-label"></label>')
.append($('<input type="radio" name="jio-wie-template-id">').val(template.id))
.append($meta);
return $('<div class="jio-wie-template-item"></div>').append($('<div class="jio-wie-template-item-row"></div>').append($label));
}
function openTemplateModal(sidebarId) {
state.templateSidebarId = sidebarId;
const $modal = templateModal.ensure();
const $list = $modal.find('.jio-wie-template-list').empty();
const $status = $modal.find('.jio-wie-template-list-status').text(t('templateLoadingList'));
templateModal.open();
post('jio_widgets_list_templates')
.done((res) => {
const templates = res?.success ? res.data?.templates : null;
$status.text(Array.isArray(templates) && templates.length ? '' : t('templateEmpty'));
(templates || []).forEach((template) => $list.append(templateItem(template)));
})
.fail((xhr) => $status.text(failMessage(null, xhr, 'networkError')));
}
function submitTemplate() {
const $modal = templateModal.ensure();
const $picked = $modal.find('input[name="jio-wie-template-id"]:checked');
if (!$picked.length) {
window.alert(t('templatePick'));
return;
}
const templateId = String($picked.val());
const mode = $modal.find('input[name="jio-wie-template-import-mode"]:checked').val() || 'append';
const title = $picked.closest('.jio-wie-template-item').find('.jio-wie-template-item-title').text().trim() || templateId;
const message = t('confirmTemplate').replace('{title}', title).replace('{mode}', t(mode === 'append' ? 'importModeAppend' : 'importModeReplace'));
if (!window.confirm(message)) return;
withButtonLoading($modal.find('.jio-wie-template-submit'), t('loadingImport'), t('templateSubmit'), () =>
post('jio_widgets_import_template', { sidebar_id: state.templateSidebarId, template_id: templateId, import_mode: mode })
.done((res) => renderImportResult(templateModal.$result(), res, true))
.fail((xhr) => showError(templateModal.$result(), failMessage(null, xhr, 'networkError')))
);
}
function injectToolbar($anchor, sidebarId) {
if (!$anchor?.length || !sidebarId || $anchor.next('.jio-wie-toolbar').length) return;
const $toolbar = $('<div class="jio-wie-toolbar"></div>');
[['export', 'exportModules'], ['import', 'importModules'], ['template', 'importTemplate']].forEach(([action, label]) => {
$('<button type="button" class="button button-small"></button>')
.text(t(label))
.attr('data-jio-wie-action', action)
.attr('data-sidebar-id', sidebarId)
.appendTo($toolbar);
});
$anchor.after($toolbar);
}
function scanWidgetsScreen() {
$('.widgets-holder-wrap:not(#available-widgets):not(.inactive-sidebar):not(.is-jio-wie)').each(function () {
const $wrap = $(this);
const $sortables = $wrap.find('.widgets-sortables').first();
const sidebarId = String($sortables.attr('id') || '');
if (!sidebarId) return;
const $description = $sortables.find('.sidebar-description .description').first();
$wrap.addClass('is-jio-wie');
injectToolbar($description.length ? $description : $sortables.find('.description').first(), sidebarId);
});
}
function scanCustomizer() {
const prefix = 'sub-accordion-section-sidebar-widgets-';
$('.control-section-sidebar:not(.is-jio-wie)').each(function () {
const $section = $(this);
const sectionId = String($section.attr('id') || '');
if (!sectionId.startsWith(prefix) || sectionId.length === prefix.length) return;
$section.addClass('is-jio-wie');
const $description = $section.find('.section-meta .description').first();
injectToolbar($description.length ? $description : $section.find('.accordion-section-title, .customize-section-title').first(), sectionId.slice(prefix.length));
});
$('.customize-control-widget_form:not(.is-jio-wie)').each(function () {
const $nav = $(this).find('.widget-reorder-nav').first();
if (!$nav.length) return;
$('<span class="jio-wie-btn-delete dashicons-trash" tabindex="0" role="button"></span>').text(t('removeModule')).appendTo($nav);
$(this).addClass('is-jio-wie');
});
}
$(document)
.on('click', '[data-jio-wie-action]', function () {
const action = String($(this).data('jio-wie-action'));
const sidebarId = String($(this).data('sidebar-id') || '');
if (!sidebarId) return;
if (action === 'template') openTemplateModal(sidebarId);
else openIoModal(action, sidebarId);
})
.on('click keydown', '.jio-wie-btn-delete', function (event) {
if (event.type === 'keydown' && event.key !== 'Enter' && event.key !== ' ') return;
event.preventDefault();
$(this).closest('.customize-control-widget_form').find('.widget-control-remove').first().trigger('click');
})
.on('click', '.jio-wie-modal__reload', () => window.location.reload())
.on('keydown', (event) => {
if (event.key !== 'Escape') return;
ioModal.close();
templateModal.close();
})
.on('widget-added', () => {
if (document.body.classList.contains('wp-customizer')) scanCustomizer();
});
$(function () {
if (!config.ajaxUrl) return;
if (document.body.classList.contains('widgets-php')) scanWidgetsScreen();
if (document.body.classList.contains('wp-customizer')) {
window.wp?.customize?.bind?.('ready', () => window.setTimeout(scanCustomizer, 300));
$(document).on('click', '.control-panel-widgets .control-section-sidebar', () => window.setTimeout(scanCustomizer, 100));
}
});
})(jQuery);