Files
2025-02-24 22:33:42 +01:00

220 lines
7.2 KiB
JavaScript

/**
* @package Polylang-Pro
*/
const { addAction } = wp.hooks;
const pllMachineTranslation = {
/**
* Init.
*/
init: () => {
if ( document.readyState !== 'loading' ) {
pllMachineTranslation.ajaxButton.attachEvent();
pllMachineTranslation.dataUsage.fetchData();
} else {
document.addEventListener( 'DOMContentLoaded', pllMachineTranslation.ajaxButton.attachEvent );
document.addEventListener( 'DOMContentLoaded', pllMachineTranslation.dataUsage.fetchData );
}
addAction( 'pll_settings_saved', 'polylang-pro', pllMachineTranslation.saveSettings.highlightRow );
},
/**
* Resets a field's row.
*
* @param {HTMLElement} fieldRow Field's row.
*/
resetFieldRow: ( fieldRow ) => {
fieldRow.querySelectorAll( '.pll-message-shown' ).forEach( ( el ) => {
el.classList.remove( 'pll-message-shown' );
} );
fieldRow.classList.remove( 'notice-success', 'notice-warning', 'notice-error', 'notice-alt' );
fieldRow.querySelectorAll( '.pll-error-message-text' ).forEach( ( el ) => {
el.textContent = '';
} );
},
/**
* Displays an error message under the field by adding HTML classes.
*
* @param {HTMLElement} fieldRow Field's row.
* @param {String} messageClass HTML class of the error message to display.
* @param {String} type Type of the error: `'error'` or `'warning'`.
*/
displayErrorMessage: ( fieldRow, messageClass, type = 'error' ) => {
if ( messageClass ) {
fieldRow.querySelectorAll( '.' + messageClass ).forEach( ( el ) => {
el.classList.add( 'pll-message-shown' );
} );
}
fieldRow.classList.add( 'notice-' + type, 'notice-alt' );
},
ajaxButton: {
/**
* Attaches an event to `.pll-ajax-button` buttons to trigger AJAX requests.
*/
attachEvent: () => {
document.querySelectorAll( '.pll-ajax-button' ).forEach( ( el ) => {
el.addEventListener( 'click', ( event ) => {
const button = event.target;
const action = button.getAttribute( 'data-action' );
const nonce = button.getAttribute( 'data-nonce' );
const fieldRow = button.closest( 'tr' );
const errorElms = fieldRow.querySelectorAll( '.pll-error-message-text' );
if ( ! action || ! nonce || ! fieldRow || ! errorElms.length || button.getAttribute( 'disabled' ) ) {
return;
}
const urlParams = { 'action': action, '_pll_nonce': nonce, 'pll_ajax_settings': 1 };
fieldRow.querySelectorAll( '[data-name]' ).forEach( ( el ) => {
urlParams[ el.getAttribute( 'data-name' ) ] = el.value;
} );
const url = wp.url.addQueryArgs( ajaxurl, urlParams );
button.setAttribute( 'disabled', 'disabled' );
pllMachineTranslation.resetFieldRow( fieldRow );
fetch( url ).then( ( response ) => {
return response.json();
} ).then( ( json ) => {
button.removeAttribute( 'disabled' );
if ( json.success ) {
fieldRow.classList.add( 'notice-success', 'notice-alt' );
} else {
errorElms[0].textContent = json.data && json.data.message ? json.data.message : '';
pllMachineTranslation.displayErrorMessage( fieldRow, json.data ? json.data.message_class : '' );
}
} ).catch( () => {
button.removeAttribute( 'disabled' );
fieldRow.classList.add( 'notice-error', 'notice-alt' );
} );
} );
} );
}
},
saveSettings: {
/**
* Highlights a settings row in case of error when the settings are saved.
* Hooked to `'pll_settings_saved'`.
*
* @param {Object} response The response from the AJAX call.
* @param {HTMLElement} tr The HTML element containing the module's fields.
*/
highlightRow: ( response, tr ) => {
switch ( response.what ) {
case 'success':
tr.querySelectorAll( '.notice-alt, .pll-message-shown' ).forEach( ( el ) => {
el.classList.remove( 'notice-success', 'notice-warning', 'notice-error', 'notice-alt', 'pll-message-shown' );
} );
break;
case 'error':
const noticeData = pllMachineTranslation.saveSettings.getNoticeData( response.data );
if ( ! noticeData.fieldId ) {
break;
}
const field = document.getElementById( noticeData.fieldId );
if ( ! field ) {
break;
}
const fieldRow = field.closest( 'tr' );
if ( ! fieldRow ) {
break;
}
pllMachineTranslation.resetFieldRow( fieldRow );
pllMachineTranslation.displayErrorMessage( fieldRow, noticeData.messageClass, noticeData.type );
break;
}
},
/**
* Returns the data contained in the HTML classes of the given element.
*
* @param {String} htmlString HTML string.
* @returns {Object}
*/
getNoticeData: ( htmlString ) => {
const div = document.createElement( 'div' );
div.innerHTML = htmlString.trim(); // phpcs:ignore WordPressVIPMinimum.JS.InnerHTML.Found
return {
type: pllMachineTranslation.saveSettings.find( div.firstChild.className, 'notice-(success|warning|error)', 'error' ),
fieldId: pllMachineTranslation.saveSettings.find( div.firstChild.className, 'pll-field-id-([^\\s]+)', '' ), // See `Settings\Deepl::is_api_key_valid()` and `Module_Settings::update()`.
messageClass: pllMachineTranslation.saveSettings.find( div.firstChild.className, 'pll-message-class-([^\\s]+)', '' ) // See `Settings\Deepl::is_api_key_valid()` and `Module_Settings::update()`.
};
},
/**
* Returns the part of the given string matching the given pattern.
*
* @param {String} string A string.
* @param {String} pattern A regex pattern.
* @param {String} def String to return if nothing is found.
* @returns {String}
*/
find: ( string, pattern, def ) => {
const matches = ( ' ' + string + ' ' ).match( new RegExp( '\\s' + pattern + '\\s' ) );
return matches && matches[1] ? matches[1] : def;
}
},
dataUsage: {
fetchData: () => {
document.querySelectorAll( '.pll-progress-bar-wrapper' ).forEach( ( el ) => {
const action = el.getAttribute( 'data-action' );
const nonce = el.getAttribute( 'data-nonce' );
const spinner = el.querySelectorAll( '.spinner' ).item( 0 );
const progress = el.querySelectorAll( 'div' ).item( 0 );
if ( ! action || ! nonce || ! spinner || ! progress || ! el.parentElement ) {
return;
}
const description = el.parentElement.querySelectorAll( '.description' ).item( 0 );
if ( ! description ) {
return;
}
const urlParams = { 'action': action, '_pll_nonce': nonce, 'pll_ajax_settings': 1 };
const url = wp.url.addQueryArgs( ajaxurl, urlParams );
fetch( url ).then( ( response ) => {
return response.json();
} ).then( ( json ) => {
if ( ! json.success || ! json.data.percent ) {
/*
* 2 cases:
* - Error while retrieving the data: display the error message.
* - The character limit is 0: display only the character count.
*/
el.remove();
description.textContent = json.data.message;
return;
}
// Display a graphic.
el.replaceChild( document.createTextNode( json.data.percent_formatted ), spinner );
progress.textContent = json.data.percent_formatted;
progress.style.width = json.data.percent;
description.textContent = json.data.message;
} ).catch( () => {
el.closest( 'tr' ).remove();
} );
} );
}
}
};
pllMachineTranslation.init();