467 lines
15 KiB
JavaScript
467 lines
15 KiB
JavaScript
/**
|
|
* Maple Local Fonts - Admin JavaScript
|
|
*
|
|
* @package Maple_Local_Fonts
|
|
*/
|
|
|
|
(function($) {
|
|
'use strict';
|
|
|
|
var MLF = {
|
|
/**
|
|
* Search debounce timer.
|
|
*/
|
|
searchTimer: null,
|
|
|
|
/**
|
|
* Currently selected font.
|
|
*/
|
|
selectedFont: null,
|
|
|
|
/**
|
|
* Loaded font preview stylesheets.
|
|
*/
|
|
loadedFonts: {},
|
|
|
|
/**
|
|
* Initialize the admin functionality.
|
|
*/
|
|
init: function() {
|
|
this.bindEvents();
|
|
},
|
|
|
|
/**
|
|
* Bind event handlers.
|
|
*/
|
|
bindEvents: function() {
|
|
// Search input
|
|
$('#mlf-font-search').on('input', this.handleSearchInput.bind(this));
|
|
|
|
// Click outside to close search results
|
|
$(document).on('click', this.handleDocumentClick.bind(this));
|
|
|
|
// Prevent closing when clicking inside search area
|
|
$('.mlf-import-section').on('click', function(e) {
|
|
e.stopPropagation();
|
|
});
|
|
|
|
// Font selection from results
|
|
$(document).on('click', '.mlf-result-item', this.handleFontSelect.bind(this));
|
|
|
|
// Change font button
|
|
$('#mlf-change-font').on('click', this.handleChangeFont.bind(this));
|
|
|
|
// Form submission
|
|
$('#mlf-import-form').on('submit', this.handleDownload.bind(this));
|
|
|
|
// Delete button clicks
|
|
$(document).on('click', '.mlf-delete-btn', this.handleDelete.bind(this));
|
|
},
|
|
|
|
/**
|
|
* Handle search input (debounced).
|
|
*
|
|
* @param {Event} e Input event.
|
|
*/
|
|
handleSearchInput: function(e) {
|
|
var query = $(e.target).val().trim();
|
|
|
|
// Clear previous timer
|
|
if (this.searchTimer) {
|
|
clearTimeout(this.searchTimer);
|
|
}
|
|
|
|
// Hide results if query too short
|
|
if (query.length < 2) {
|
|
this.hideSearchResults();
|
|
return;
|
|
}
|
|
|
|
// Debounce: wait 300ms before searching
|
|
this.searchTimer = setTimeout(function() {
|
|
MLF.performSearch(query);
|
|
}, 300);
|
|
},
|
|
|
|
/**
|
|
* Perform the search.
|
|
*
|
|
* @param {string} query Search query.
|
|
*/
|
|
performSearch: function(query) {
|
|
var $spinner = $('#mlf-search-spinner');
|
|
var $results = $('#mlf-search-results');
|
|
var $list = $('#mlf-results-list');
|
|
|
|
// Show spinner
|
|
$spinner.addClass('is-active');
|
|
|
|
// Send AJAX request
|
|
$.ajax({
|
|
url: mapleLocalFontsData.ajaxUrl,
|
|
type: 'POST',
|
|
data: {
|
|
action: 'mlf_search_fonts',
|
|
nonce: mapleLocalFontsData.searchNonce,
|
|
query: query
|
|
},
|
|
success: function(response) {
|
|
if (response.success && response.data.fonts) {
|
|
MLF.displaySearchResults(response.data.fonts);
|
|
} else {
|
|
MLF.displayNoResults();
|
|
}
|
|
},
|
|
error: function() {
|
|
MLF.displayNoResults();
|
|
},
|
|
complete: function() {
|
|
$spinner.removeClass('is-active');
|
|
}
|
|
});
|
|
},
|
|
|
|
/**
|
|
* Display search results.
|
|
*
|
|
* @param {Array} fonts Array of font objects.
|
|
*/
|
|
displaySearchResults: function(fonts) {
|
|
var $results = $('#mlf-search-results');
|
|
var $list = $('#mlf-results-list');
|
|
|
|
if (fonts.length === 0) {
|
|
this.displayNoResults();
|
|
return;
|
|
}
|
|
|
|
var html = '';
|
|
var previewText = mapleLocalFontsData.strings.previewText || 'Maple Fonts Preview';
|
|
|
|
fonts.forEach(function(font) {
|
|
var fontFamily = font.family;
|
|
var category = font.category || 'sans-serif';
|
|
var categoryLabel = MLF.getCategoryLabel(category);
|
|
var badges = [];
|
|
|
|
if (font.has_variable) {
|
|
badges.push('<span class="mlf-badge mlf-badge-variable">Variable</span>');
|
|
}
|
|
|
|
html += '<div class="mlf-result-item" data-font-family="' + MLF.escapeHtml(fontFamily) + '">';
|
|
html += ' <div class="mlf-result-info">';
|
|
html += ' <span class="mlf-result-name">' + MLF.escapeHtml(fontFamily) + '</span>';
|
|
html += ' <span class="mlf-result-category">' + MLF.escapeHtml(categoryLabel) + '</span>';
|
|
if (badges.length > 0) {
|
|
html += ' <span class="mlf-result-badges">' + badges.join('') + '</span>';
|
|
}
|
|
html += ' </div>';
|
|
html += ' <div class="mlf-result-preview" style="font-family: \'' + MLF.escapeHtml(fontFamily) + '\', ' + category + ';">';
|
|
html += MLF.escapeHtml(previewText);
|
|
html += ' </div>';
|
|
html += '</div>';
|
|
|
|
// Load font for preview
|
|
MLF.loadFontPreview(fontFamily);
|
|
});
|
|
|
|
$list.html(html);
|
|
$results.show();
|
|
},
|
|
|
|
/**
|
|
* Display no results message.
|
|
*/
|
|
displayNoResults: function() {
|
|
var $results = $('#mlf-search-results');
|
|
var $list = $('#mlf-results-list');
|
|
var message = mapleLocalFontsData.strings.noResults || 'No fonts found.';
|
|
|
|
$list.html('<div class="mlf-no-results">' + MLF.escapeHtml(message) + '</div>');
|
|
$results.show();
|
|
},
|
|
|
|
/**
|
|
* Hide search results.
|
|
*/
|
|
hideSearchResults: function() {
|
|
$('#mlf-search-results').hide();
|
|
},
|
|
|
|
/**
|
|
* Maximum number of font previews to load.
|
|
*/
|
|
maxLoadedFonts: 50,
|
|
|
|
/**
|
|
* Load a font for preview from Google Fonts.
|
|
*
|
|
* @param {string} fontFamily Font family name.
|
|
*/
|
|
loadFontPreview: function(fontFamily) {
|
|
// Skip if already loaded
|
|
if (this.loadedFonts[fontFamily]) {
|
|
return;
|
|
}
|
|
|
|
// Limit number of loaded fonts to prevent memory accumulation
|
|
if (Object.keys(this.loadedFonts).length >= this.maxLoadedFonts) {
|
|
return;
|
|
}
|
|
|
|
// Mark as loading
|
|
this.loadedFonts[fontFamily] = true;
|
|
|
|
// Create Google Fonts link
|
|
var fontUrl = 'https://fonts.googleapis.com/css2?family=' +
|
|
encodeURIComponent(fontFamily.replace(/ /g, '+')) +
|
|
':wght@400&display=swap';
|
|
|
|
// Create and append link element
|
|
var $link = $('<link>', {
|
|
rel: 'stylesheet',
|
|
href: fontUrl
|
|
});
|
|
|
|
$('head').append($link);
|
|
},
|
|
|
|
/**
|
|
* Handle font selection.
|
|
*
|
|
* @param {Event} e Click event.
|
|
*/
|
|
handleFontSelect: function(e) {
|
|
var $item = $(e.currentTarget);
|
|
var fontFamily = $item.data('font-family');
|
|
|
|
// Store selected font
|
|
this.selectedFont = fontFamily;
|
|
|
|
// Update UI
|
|
$('#mlf-font-name').val(fontFamily);
|
|
$('#mlf-selected-name').text(fontFamily);
|
|
|
|
// Hide search, show selected font panel
|
|
this.hideSearchResults();
|
|
$('#mlf-font-search').closest('.mlf-form-row').hide();
|
|
$('#mlf-selected-font').show();
|
|
|
|
// Clear search input
|
|
$('#mlf-font-search').val('');
|
|
},
|
|
|
|
/**
|
|
* Handle change font button.
|
|
*
|
|
* @param {Event} e Click event.
|
|
*/
|
|
handleChangeFont: function(e) {
|
|
e.preventDefault();
|
|
|
|
// Clear selection
|
|
this.selectedFont = null;
|
|
$('#mlf-font-name').val('');
|
|
|
|
// Show search, hide selected font panel
|
|
$('#mlf-selected-font').hide();
|
|
$('#mlf-font-search').closest('.mlf-form-row').show();
|
|
$('#mlf-font-search').focus();
|
|
},
|
|
|
|
/**
|
|
* Handle document click (close search results).
|
|
*
|
|
* @param {Event} e Click event.
|
|
*/
|
|
handleDocumentClick: function(e) {
|
|
if (!$(e.target).closest('.mlf-import-section').length) {
|
|
this.hideSearchResults();
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Get human-readable category label.
|
|
*
|
|
* @param {string} category Category slug.
|
|
* @return {string} Category label.
|
|
*/
|
|
getCategoryLabel: function(category) {
|
|
var labels = {
|
|
'sans-serif': 'Sans Serif',
|
|
'serif': 'Serif',
|
|
'display': 'Display',
|
|
'handwriting': 'Handwriting',
|
|
'monospace': 'Monospace'
|
|
};
|
|
return labels[category] || category;
|
|
},
|
|
|
|
/**
|
|
* Escape HTML entities.
|
|
*
|
|
* @param {string} str String to escape.
|
|
* @return {string} Escaped string.
|
|
*/
|
|
escapeHtml: function(str) {
|
|
var div = document.createElement('div');
|
|
div.textContent = str;
|
|
return div.innerHTML;
|
|
},
|
|
|
|
/**
|
|
* Handle font download form submission.
|
|
*
|
|
* @param {Event} e Form submit event.
|
|
*/
|
|
handleDownload: function(e) {
|
|
e.preventDefault();
|
|
|
|
var $form = $('#mlf-import-form');
|
|
var $button = $('#mlf-download-btn');
|
|
var $spinner = $('#mlf-spinner');
|
|
var $message = $('#mlf-message');
|
|
|
|
// Get form values
|
|
var fontName = $('#mlf-font-name').val().trim();
|
|
var includeItalic = $('#mlf-include-italic').is(':checked') ? '1' : '0';
|
|
|
|
// Validate
|
|
if (!fontName) {
|
|
this.showMessage($message, mapleLocalFontsData.strings.selectFont || 'Please select a font.', 'error');
|
|
return;
|
|
}
|
|
|
|
// Store original button text
|
|
if (!$button.data('original-text')) {
|
|
$button.data('original-text', $button.text());
|
|
}
|
|
|
|
// Disable form
|
|
$form.addClass('mlf-loading');
|
|
$button.prop('disabled', true).text(mapleLocalFontsData.strings.downloading);
|
|
$spinner.addClass('is-active');
|
|
$message.hide();
|
|
|
|
// Send AJAX request
|
|
$.ajax({
|
|
url: mapleLocalFontsData.ajaxUrl,
|
|
type: 'POST',
|
|
data: {
|
|
action: 'mlf_download_font',
|
|
nonce: mapleLocalFontsData.downloadNonce,
|
|
font_name: fontName,
|
|
include_italic: includeItalic
|
|
},
|
|
success: function(response) {
|
|
if (response.success) {
|
|
MLF.showMessage($message, response.data.message, 'success');
|
|
// Reload page to show new font
|
|
setTimeout(function() {
|
|
window.location.reload();
|
|
}, 1500);
|
|
} else {
|
|
MLF.showMessage($message, response.data.message || mapleLocalFontsData.strings.error, 'error');
|
|
}
|
|
},
|
|
error: function(xhr) {
|
|
var message = mapleLocalFontsData.strings.error;
|
|
if (xhr.responseJSON && xhr.responseJSON.data && xhr.responseJSON.data.message) {
|
|
message = xhr.responseJSON.data.message;
|
|
}
|
|
MLF.showMessage($message, message, 'error');
|
|
},
|
|
complete: function() {
|
|
$form.removeClass('mlf-loading');
|
|
$button.prop('disabled', false).text($button.data('original-text'));
|
|
$spinner.removeClass('is-active');
|
|
}
|
|
});
|
|
},
|
|
|
|
/**
|
|
* Handle font deletion.
|
|
*
|
|
* @param {Event} e Click event.
|
|
*/
|
|
handleDelete: function(e) {
|
|
e.preventDefault();
|
|
|
|
var $button = $(e.currentTarget);
|
|
var fontId = $button.data('font-id');
|
|
var $fontItem = $button.closest('.mlf-font-item');
|
|
|
|
// Confirm deletion
|
|
if (!confirm(mapleLocalFontsData.strings.confirmDelete)) {
|
|
return;
|
|
}
|
|
|
|
// Store original button text
|
|
var originalText = $button.text();
|
|
|
|
// Disable button
|
|
$button.prop('disabled', true).text(mapleLocalFontsData.strings.deleting);
|
|
$fontItem.addClass('mlf-loading');
|
|
|
|
// Send AJAX request
|
|
$.ajax({
|
|
url: mapleLocalFontsData.ajaxUrl,
|
|
type: 'POST',
|
|
data: {
|
|
action: 'mlf_delete_font',
|
|
nonce: mapleLocalFontsData.deleteNonce,
|
|
font_id: fontId
|
|
},
|
|
success: function(response) {
|
|
if (response.success) {
|
|
// Remove font item with animation
|
|
$fontItem.slideUp(300, function() {
|
|
$(this).remove();
|
|
|
|
// Check if any fonts remain
|
|
if ($('.mlf-font-item').length === 0) {
|
|
$('#mlf-font-list').replaceWith(
|
|
'<p class="mlf-no-fonts">No fonts installed yet. Search and select a font above to get started.</p>'
|
|
);
|
|
}
|
|
});
|
|
} else {
|
|
alert(response.data.message || mapleLocalFontsData.strings.error);
|
|
$button.prop('disabled', false).text(originalText);
|
|
$fontItem.removeClass('mlf-loading');
|
|
}
|
|
},
|
|
error: function(xhr) {
|
|
var message = mapleLocalFontsData.strings.error;
|
|
if (xhr.responseJSON && xhr.responseJSON.data && xhr.responseJSON.data.message) {
|
|
message = xhr.responseJSON.data.message;
|
|
}
|
|
alert(message);
|
|
$button.prop('disabled', false).text(originalText);
|
|
$fontItem.removeClass('mlf-loading');
|
|
}
|
|
});
|
|
},
|
|
|
|
/**
|
|
* Show a message to the user.
|
|
*
|
|
* @param {jQuery} $element Message element.
|
|
* @param {string} message Message text.
|
|
* @param {string} type Message type (success or error).
|
|
*/
|
|
showMessage: function($element, message, type) {
|
|
$element
|
|
.removeClass('mlf-message-success mlf-message-error')
|
|
.addClass('mlf-message-' + type)
|
|
.text(message)
|
|
.show();
|
|
}
|
|
};
|
|
|
|
// Initialize on document ready
|
|
$(document).ready(function() {
|
|
MLF.init();
|
|
});
|
|
|
|
})(jQuery);
|