Initial geladen: WP App Portal

This commit is contained in:
2026-04-10 11:32:42 +02:00
parent a772a0ad53
commit fdfd055748
5658 changed files with 1968631 additions and 0 deletions
@@ -0,0 +1,58 @@
/******/ "use strict";
;// ./js/src/lib/ajax-filter/index.js
/**
* @package Polylang
*/
/**
* Adds data to all ajax requests made with jQuery.
*
* @since 3.7
*
* @param {Object} data The data to add.
* @returns {void}
*/
function ajaxFilter( data ) {
if ( 'undefined' === typeof jQuery || ! data ) {
return;
}
const dataStr = jQuery.param( data );
jQuery.ajaxPrefilter( function ( options ) {
if ( -1 === options.url.indexOf( ajaxurl ) && -1 === ajaxurl.indexOf( options.url ) ) {
return;
}
if (
'undefined' === typeof options.data ||
null === options.data ||
'string' === typeof options.data && '' === options.data.trim()
) {
// An empty string or null/undefined.
options.data = dataStr;
} else if ( 'string' === typeof options.data ) {
// A non-empty string: can be a JSON string or a query string.
try {
options.data = JSON.stringify( Object.assign( JSON.parse( options.data ), data ) );
} catch ( exception ) {
// A non-empty non-JSON string is considered a query string.
options.data = `${ options.data }&${ dataStr }`;
}
} else if ( jQuery.isPlainObject( options.data ) ) {
// An object.
options.data = Object.assign( options.data, data );
}
} );
}
;// ./js/src/admin.js
/**
* @package Polylang
*/
ajaxFilter( pll_admin?.ajax_filter );
@@ -0,0 +1 @@
"use strict";function ajaxFilter(a){if("undefined"==typeof jQuery||!a)return;const t=jQuery.param(a);jQuery.ajaxPrefilter(function(e){if(-1!==e.url.indexOf(ajaxurl)||-1!==ajaxurl.indexOf(e.url))if(void 0===e.data||null===e.data||"string"==typeof e.data&&""===e.data.trim())e.data=t;else if("string"==typeof e.data)try{e.data=JSON.stringify(Object.assign(JSON.parse(e.data),a))}catch(a){e.data=`${e.data}&${t}`}else jQuery.isPlainObject(e.data)&&(e.data=Object.assign(e.data,a))})}ajaxFilter(pll_admin?.ajax_filter);
@@ -0,0 +1,460 @@
/******/ (() => { // webpackBootstrap
/******/ var __webpack_modules__ = ({
/***/ 631
(module) {
module.exports = (function() { return this["wp"]["apiFetch"]; }());
/***/ }
/******/ });
/************************************************************************/
/******/ // The module cache
/******/ var __webpack_module_cache__ = {};
/******/
/******/ // The require function
/******/ function __webpack_require__(moduleId) {
/******/ // Check if module is in cache
/******/ var cachedModule = __webpack_module_cache__[moduleId];
/******/ if (cachedModule !== undefined) {
/******/ return cachedModule.exports;
/******/ }
/******/ // Create a new module (and put it into the cache)
/******/ var module = __webpack_module_cache__[moduleId] = {
/******/ // no module.id needed
/******/ // no module.loaded needed
/******/ exports: {}
/******/ };
/******/
/******/ // Execute the module function
/******/ __webpack_modules__[moduleId](module, module.exports, __webpack_require__);
/******/
/******/ // Return the exports of the module
/******/ return module.exports;
/******/ }
/******/
/************************************************************************/
/******/ /* webpack/runtime/compat get default export */
/******/ (() => {
/******/ // getDefaultExport function for compatibility with non-harmony modules
/******/ __webpack_require__.n = (module) => {
/******/ var getter = module && module.__esModule ?
/******/ () => (module['default']) :
/******/ () => (module);
/******/ __webpack_require__.d(getter, { a: getter });
/******/ return getter;
/******/ };
/******/ })();
/******/
/******/ /* webpack/runtime/define property getters */
/******/ (() => {
/******/ // define getter functions for harmony exports
/******/ __webpack_require__.d = (exports, definition) => {
/******/ for(var key in definition) {
/******/ if(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {
/******/ Object.defineProperty(exports, key, { enumerable: true, get: definition[key] });
/******/ }
/******/ }
/******/ };
/******/ })();
/******/
/******/ /* webpack/runtime/hasOwnProperty shorthand */
/******/ (() => {
/******/ __webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop))
/******/ })();
/******/
/************************************************************************/
var __webpack_exports__ = {};
// This entry needs to be wrapped in an IIFE because it needs to be in strict mode.
(() => {
"use strict";
;// ./js/src/lib/confirmation-modal.js
/**
* @package Polylang
*/
const languagesList = jQuery('.post_lang_choice');
// Dialog box for alerting the user about a risky changing.
const initializeConfirmationModal = () => {
// We can't use underscore or lodash in this common code because it depends of the context classic or block editor.
// Classic editor underscore is loaded, Block editor lodash is loaded.
const {
__
} = wp.i18n;
// Create dialog container.
const dialogContainer = jQuery('<div/>', {
id: 'pll-dialog',
style: 'display:none;'
}).text(__('Are you sure you want to change the language of the current content?', 'polylang'));
// Put it after languages list dropdown.
// PHPCS ignore dialogContainer is a new safe HTML code generated above.
languagesList.after(dialogContainer); // phpcs:ignore WordPressVIPMinimum.JS.HTMLExecutingFunctions.after
const dialogResult = new Promise((confirm, cancel) => {
const confirmDialog = what => {
// phpcs:ignore PEAR.Functions.FunctionCallSignature.Indent
switch (what) {
// phpcs:ignore PEAR.Functions.FunctionCallSignature.Indent
case 'yes':
// Confirm the new language.
languagesList.data('old-value', languagesList.children(':selected').first().val());
confirm();
break;
case 'no':
// Revert to the old language.
languagesList.val(languagesList.data('old-value'));
cancel('Cancel');
break;
}
dialogContainer.dialog('close'); // phpcs:ignore PEAR.Functions.FunctionCallSignature.Indent
}; // phpcs:ignore PEAR.Functions.FunctionCallSignature.Indent
// Initialize dialog box in the case a language is selected but not added in the list.
const dialogOptions = {
autoOpen: false,
modal: true,
draggable: false,
resizable: false,
title: __('Change language', 'polylang'),
minWidth: 600,
maxWidth: '100%',
open: function (event, ui) {
// Change dialog box position for rtl language
if (jQuery('body').hasClass('rtl')) {
jQuery(this).parent().css({
right: jQuery(this).parent().css('left'),
left: 'auto'
});
}
},
close: function (event, ui) {
// When we're closing the dialog box we need to cancel the language change as we click on Cancel button.
confirmDialog('no');
},
buttons: [{
text: __('OK', 'polylang'),
click: function (event) {
confirmDialog('yes');
}
}, {
text: __('Cancel', 'polylang'),
click: function (event) {
confirmDialog('no');
}
}]
};
if (jQuery.ui.version >= '1.12.0') {
Object.assign(dialogOptions, {
classes: {
'ui-dialog': 'pll-confirmation-modal'
}
});
} else {
Object.assign(dialogOptions, {
dialogClass: 'pll-confirmation-modal'
}); // jQuery UI 1.11.4 - WP < 5.6
}
dialogContainer.dialog(dialogOptions);
});
return {
dialogContainer,
dialogResult
};
};
const initializeLanguageOldValue = () => {
// Keep the old language value to be able to compare to the new one and revert to it if necessary.
languagesList.attr('data-old-value', languagesList.children(':selected').first().val());
};
;// ./js/src/lib/metabox-autocomplete.js
/**
* @package Polylang
*/
// Translations autocomplete input box.
function initMetaboxAutoComplete() {
jQuery('.tr_lang').each(function () {
var tr_lang = jQuery(this).attr('id').substring(8);
var td = jQuery(this).parent().parent().siblings('.pll-edit-column');
jQuery(this).autocomplete({
minLength: 0,
source: ajaxurl + '?action=pll_posts_not_translated' + '&post_language=' + jQuery('.post_lang_choice').val() + '&translation_language=' + tr_lang + '&post_type=' + jQuery('#post_type').val() + '&_pll_nonce=' + jQuery('#_pll_nonce').val(),
select: function (event, ui) {
jQuery('#htr_lang_' + tr_lang).val(ui.item.id);
// ui.item.link is built and come from server side and is well escaped when necessary
td.html(ui.item.link); // phpcs:ignore WordPressVIPMinimum.JS.HTMLExecutingFunctions.html
}
});
// when the input box is emptied
jQuery(this).on('blur', function () {
if (!jQuery(this).val()) {
jQuery('#htr_lang_' + tr_lang).val(0);
// Value is retrieved from HTML already generated server side
td.html(td.siblings('.hidden').children().clone()); // phpcs:ignore WordPressVIPMinimum.JS.HTMLExecutingFunctions.html
}
});
});
}
// EXTERNAL MODULE: external {"this":["wp","apiFetch"]}
var external_this_wp_apiFetch_ = __webpack_require__(631);
var external_this_wp_apiFetch_default = /*#__PURE__*/__webpack_require__.n(external_this_wp_apiFetch_);
;// ./node_modules/@wpsyntex/polylang-react-library/build/middlewares/filter-path.js
/**
* Filters requests for translatable entities.
* This logic is shared across all Polylang plugins.
*
* @since 3.5
*
* @param {Object} options - API fetch options object.
* @param {Array} filteredRoutes - Array of route paths to filter.
* @param {Function} filter - Function to filter matching routes.
* @return {Object} Modified REST request options.
*/
const filterPathMiddleware = (options, filteredRoutes, filter) => {
const cleanPath = options.path.split('?')[0].replace(/^\/+|\/+$/g, ''); // Get path without query parameters and trim '/'.
return Object.values(filteredRoutes).find(path => cleanPath === path) ? filter(options) : options;
};
/* harmony default export */ const filter_path = (filterPathMiddleware);
;// ./node_modules/@wpsyntex/polylang-react-library/build/middlewares/editors-requests-filter.js
/**
* WordPress dependencies.
*/
/*
* Internal dependencies.
*/
/**
* Safely filters requests for translatable entities in block editor type screens.
* Ensures that `pllFilteredRoutes` has been well defined on server side and
* that the filtered request is a REST one.
*
* @param {Function} filterCallback - Function to filter API fetch options.
*/
const editorsRequestsFilter = filterCallback => {
external_this_wp_apiFetch_default().use((options, next) => {
/*
* If options.url is defined, this is not a REST request but a direct call to post.php for legacy metaboxes.
* If `filteredRoutes` is not defined, return early.
*/
if ('undefined' !== typeof options.url || 'undefined' === typeof window.pllFilteredRoutes) {
return next(options);
}
return next(filter_path(options, window.pllFilteredRoutes, filterCallback));
});
};
/* harmony default export */ const editors_requests_filter = (editorsRequestsFilter);
;// ./js/src/block-editor.js
/**
* @package Polylang
*/
/**
* Gets the language of the currently edited post, fallback to default language if none is found.
*
* @since 2.5
*
* @return {Element.value}
*/
function getCurrentLanguage() {
const lang = document.querySelector('[name=post_lang_choice]');
if (null === lang) {
return pllDefaultLanguage;
}
return lang.value;
}
/**
* Adds language parameter according to the current one (query string for GET, body for PUT and POST).
*
* @since 3.5
*
* @param {APIFetchOptions} options
* @returns {APIFetchOptions}
*/
function addLanguageParameter(options) {
if ('undefined' === typeof options.data || null === options.data) {
// GET
options.path += (options.path.indexOf('?') >= 0 ? '&lang=' : '?lang=') + getCurrentLanguage();
} else {
// PUT, POST
options.data.lang = getCurrentLanguage();
}
return options;
}
/**
* Filter REST API requests to add the language in the request
*
* @since 2.5
*/
editors_requests_filter(addLanguageParameter);
/**
* Handles internals of the metabox:
* Language select, autocomplete input field.
*
* @since 1.5
*
* Save post after lang choice is done and redirect to the same page for refreshing all the data.
*
* @since 2.5
*
* Link post saving after refreshing the metabox.
*
* @since 3.0
*/
jQuery(function ($) {
// Initialize current language to be able to compare if it changes.
initializeLanguageOldValue();
// Ajax for changing the post's language in the languages metabox
$('.post_lang_choice').on('change', function (event) {
const {
select,
dispatch,
subscribe
} = wp.data;
const emptyPost = isEmptyPost();
const {
addQueryArgs
} = wp.url;
// Initialize the confirmation dialog box.
const confirmationModal = initializeConfirmationModal();
const {
dialogContainer: dialog
} = confirmationModal;
let {
dialogResult
} = confirmationModal;
const selectedOption = event.target; // The selected option in the dropdown list.
// Specific case for empty posts.
// Place at the beginning because window.location change triggers automatically page reloading.
if (location.pathname.match(/post-new.php/gi) && emptyPost) {
reloadPageForEmptyPost(selectedOption.value);
}
// Otherwise send an ajax request to refresh the legacy metabox and set the post language with the new language.
// It needs a confirmation of the user before changing the language.
// Need to wait the ajax response before triggering the block editor post save action.
if ($(this).data('old-value') !== selectedOption.value && !emptyPost) {
dialog.dialog('open');
} else {
// Update the old language with the new one to be able to compare it in the next change.
// Because the page isn't reloaded in this case.
initializeLanguageOldValue();
dialogResult = Promise.resolve();
}
dialogResult.then(() => {
let data = {
// phpcs:ignore PEAR.Functions.FunctionCallSignature.Indent
action: 'post_lang_choice',
lang: selectedOption.value,
post_type: $('#post_type').val(),
post_id: $('#post_ID').val(),
_pll_nonce: $('#_pll_nonce').val()
};
// Update post language in database as soon as possible.
// Because, in addition of the block editor save process, the legacy metabox uses a post.php process to update the language and is too late compared to the page reload.
$.post(ajaxurl, data, function () {
blockEditorSavePostAndReloadPage();
});
}, () => {} // Do nothing when promise is rejected by clicking the Cancel dialog button.
);
function isEmptyPost() {
const editor = select('core/editor');
return !editor.getEditedPostAttribute('title')?.trim() && !editor.getEditedPostContent() && !editor.getEditedPostAttribute('excerpt')?.trim();
}
/**
* Reload the block editor page for empty posts.
*
* @param {string} lang The target language code.
*/
function reloadPageForEmptyPost(lang) {
// Change the new_lang parameter with the new language value for reloading the page
// WPCS location.search is never written in the page, just used to reload page with the right value of new_lang
// new_lang input is controlled server side in PHP. The value come from the dropdown list of language returned and escaped server side.
// Notice that window.location changing triggers automatically page reloading.
if (-1 != location.search.indexOf('new_lang')) {
// use regexp non capturing group to replace new_lang parameter no matter where it is and capture other parameters which can be behind it
window.location.search = window.location.search.replace(/(?:new_lang=[^&]*)(&)?(.*)/, 'new_lang=' + lang + '$1$2'); // phpcs:ignore WordPressVIPMinimum.JS.Window.location, WordPressVIPMinimum.JS.Window.VarAssignment
} else {
window.location.search = window.location.search + (-1 != window.location.search.indexOf('?') ? '&' : '?') + 'new_lang=' + lang; // phpcs:ignore WordPressVIPMinimum.JS.Window.location, WordPressVIPMinimum.JS.Window.VarAssignment
}
}
;
/**
* Triggers block editor post save and reload the block editor page when everything is ok.
*/
function blockEditorSavePostAndReloadPage() {
let unsubscribe = null;
const previousPost = select('core/editor').getCurrentPost();
// Listen if the savePost is completely done by subscribing to its events.
const savePostIsDone = new Promise(function (resolve, reject) {
unsubscribe = subscribe(function () {
const post = select('core/editor').getCurrentPost();
const {
id,
status,
type
} = post;
const error = select('core').getLastEntitySaveError('postType', type, id);
if (error) {
reject();
}
if (previousPost.modified !== post.modified) {
if (location.pathname.match(/post-new.php/gi) && status !== 'auto-draft' && id) {
window.history.replaceState({
id
}, 'Post ' + id, addQueryArgs('post.php', {
post: id,
action: 'edit'
}));
}
resolve();
}
});
});
// Triggers the post save.
dispatch('core/editor').savePost();
// Process
savePostIsDone.then(function () {
// If the post is well saved, we can reload the page
window.location.reload();
}, function () {
// If the post save failed
unsubscribe();
}).catch(function () {
// If an exception is thrown
unsubscribe();
});
}
;
});
initMetaboxAutoComplete();
});
})();
this.polylang = __webpack_exports__;
/******/ })()
;
@@ -0,0 +1 @@
(()=>{var t={631(t){t.exports=function(){return this.wp.apiFetch}()}},e={};function n(o){var a=e[o];if(void 0!==a)return a.exports;var l=e[o]={exports:{}};return t[o](l,l.exports,n),l.exports}n.n=t=>{var e=t&&t.__esModule?()=>t.default:()=>t;return n.d(e,{a:e}),e},n.d=(t,e)=>{for(var o in e)n.o(e,o)&&!n.o(t,o)&&Object.defineProperty(t,o,{enumerable:!0,get:e[o]})},n.o=(t,e)=>Object.prototype.hasOwnProperty.call(t,e);(()=>{"use strict";const t=jQuery(".post_lang_choice"),e=()=>{const{__:e}=wp.i18n,n=jQuery("<div/>",{id:"pll-dialog",style:"display:none;"}).text(e("Are you sure you want to change the language of the current content?","polylang"));t.after(n);const o=new Promise((o,a)=>{const l=e=>{switch(e){case"yes":t.data("old-value",t.children(":selected").first().val()),o();break;case"no":t.val(t.data("old-value")),a("Cancel")}n.dialog("close")},i={autoOpen:!1,modal:!0,draggable:!1,resizable:!1,title:e("Change language","polylang"),minWidth:600,maxWidth:"100%",open:function(t,e){jQuery("body").hasClass("rtl")&&jQuery(this).parent().css({right:jQuery(this).parent().css("left"),left:"auto"})},close:function(t,e){l("no")},buttons:[{text:e("OK","polylang"),click:function(t){l("yes")}},{text:e("Cancel","polylang"),click:function(t){l("no")}}]};jQuery.ui.version>="1.12.0"?Object.assign(i,{classes:{"ui-dialog":"pll-confirmation-modal"}}):Object.assign(i,{dialogClass:"pll-confirmation-modal"}),n.dialog(i)});return{dialogContainer:n,dialogResult:o}},o=()=>{t.attr("data-old-value",t.children(":selected").first().val())};var a=n(631),l=n.n(a);const i=(t,e,n)=>{const o=t.path.split("?")[0].replace(/^\/+|\/+$/g,"");return Object.values(e).find(t=>o===t)?n(t):t};function r(){const t=document.querySelector("[name=post_lang_choice]");return null===t?pllDefaultLanguage:t.value}(t=>{l().use((e,n)=>void 0!==e.url||void 0===window.pllFilteredRoutes?n(e):n(i(e,window.pllFilteredRoutes,t)))})(function(t){return void 0===t.data||null===t.data?t.path+=(t.path.indexOf("?")>=0?"&lang=":"?lang=")+r():t.data.lang=r(),t}),jQuery(function(t){o(),t(".post_lang_choice").on("change",function(n){const{select:a,dispatch:l,subscribe:i}=wp.data,r=function(){const t=a("core/editor");return!t.getEditedPostAttribute("title")?.trim()&&!t.getEditedPostContent()&&!t.getEditedPostAttribute("excerpt")?.trim()}(),{addQueryArgs:s}=wp.url,c=e(),{dialogContainer:u}=c;let{dialogResult:d}=c;const p=n.target;var g;location.pathname.match(/post-new.php/gi)&&r&&(g=p.value,-1!=location.search.indexOf("new_lang")?window.location.search=window.location.search.replace(/(?:new_lang=[^&]*)(&)?(.*)/,"new_lang="+g+"$1$2"):window.location.search=window.location.search+(-1!=window.location.search.indexOf("?")?"&":"?")+"new_lang="+g),t(this).data("old-value")===p.value||r?(o(),d=Promise.resolve()):u.dialog("open"),d.then(()=>{let e={action:"post_lang_choice",lang:p.value,post_type:t("#post_type").val(),post_id:t("#post_ID").val(),_pll_nonce:t("#_pll_nonce").val()};t.post(ajaxurl,e,function(){!function(){let t=null;const e=a("core/editor").getCurrentPost(),n=new Promise(function(n,o){t=i(function(){const t=a("core/editor").getCurrentPost(),{id:l,status:i,type:r}=t;a("core").getLastEntitySaveError("postType",r,l)&&o(),e.modified!==t.modified&&(location.pathname.match(/post-new.php/gi)&&"auto-draft"!==i&&l&&window.history.replaceState({id:l},"Post "+l,s("post.php",{post:l,action:"edit"})),n())})});l("core/editor").savePost(),n.then(function(){window.location.reload()},function(){t()}).catch(function(){t()})}()})},()=>{})}),jQuery(".tr_lang").each(function(){var t=jQuery(this).attr("id").substring(8),e=jQuery(this).parent().parent().siblings(".pll-edit-column");jQuery(this).autocomplete({minLength:0,source:ajaxurl+"?action=pll_posts_not_translated&post_language="+jQuery(".post_lang_choice").val()+"&translation_language="+t+"&post_type="+jQuery("#post_type").val()+"&_pll_nonce="+jQuery("#_pll_nonce").val(),select:function(n,o){jQuery("#htr_lang_"+t).val(o.item.id),e.html(o.item.link)}}),jQuery(this).on("blur",function(){jQuery(this).val()||(jQuery("#htr_lang_"+t).val(0),e.html(e.siblings(".hidden").children().clone()))})})})})(),this.polylang={}})();
File diff suppressed because it is too large Load Diff
File diff suppressed because one or more lines are too long
@@ -0,0 +1,466 @@
/******/ "use strict";
;// ./js/src/lib/confirmation-modal.js
/**
* @package Polylang
*/
const languagesList = jQuery( '.post_lang_choice' );
// Dialog box for alerting the user about a risky changing.
const initializeConfirmationModal = () => {
// We can't use underscore or lodash in this common code because it depends of the context classic or block editor.
// Classic editor underscore is loaded, Block editor lodash is loaded.
const { __ } = wp.i18n;
// Create dialog container.
const dialogContainer = jQuery(
'<div/>',
{
id: 'pll-dialog',
style: 'display:none;'
}
).text( __( 'Are you sure you want to change the language of the current content?', 'polylang' ) );
// Put it after languages list dropdown.
// PHPCS ignore dialogContainer is a new safe HTML code generated above.
languagesList.after( dialogContainer ); // phpcs:ignore WordPressVIPMinimum.JS.HTMLExecutingFunctions.after
const dialogResult = new Promise(
( confirm, cancel ) => {
const confirmDialog = ( what ) => { // phpcs:ignore PEAR.Functions.FunctionCallSignature.Indent
switch ( what ) { // phpcs:ignore PEAR.Functions.FunctionCallSignature.Indent
case 'yes':
// Confirm the new language.
languagesList.data( 'old-value', languagesList.children( ':selected' ).first().val() );
confirm();
break;
case 'no':
// Revert to the old language.
languagesList.val( languagesList.data( 'old-value' ) );
cancel( 'Cancel' );
break;
}
dialogContainer.dialog( 'close' ); // phpcs:ignore PEAR.Functions.FunctionCallSignature.Indent
} // phpcs:ignore PEAR.Functions.FunctionCallSignature.Indent
// Initialize dialog box in the case a language is selected but not added in the list.
const dialogOptions = {
autoOpen: false,
modal: true,
draggable: false,
resizable: false,
title: __( 'Change language', 'polylang' ),
minWidth: 600,
maxWidth: '100%',
open: function ( event, ui ) {
// Change dialog box position for rtl language
if ( jQuery( 'body' ).hasClass( 'rtl' ) ) {
jQuery( this ).parent().css(
{
right: jQuery( this ).parent().css( 'left' ),
left: 'auto'
}
);
}
},
close: function ( event, ui ) {
// When we're closing the dialog box we need to cancel the language change as we click on Cancel button.
confirmDialog( 'no' );
},
buttons: [
{
text: __( 'OK', 'polylang' ),
click: function ( event ) {
confirmDialog( 'yes' );
}
},
{
text: __( 'Cancel', 'polylang' ),
click: function ( event ) {
confirmDialog( 'no' );
}
}
]
};
if ( jQuery.ui.version >= '1.12.0' ) {
Object.assign( dialogOptions, { classes: { 'ui-dialog': 'pll-confirmation-modal' } } );
} else {
Object.assign( dialogOptions, { dialogClass: 'pll-confirmation-modal' } ); // jQuery UI 1.11.4 - WP < 5.6
}
dialogContainer.dialog( dialogOptions );
}
);
return { dialogContainer, dialogResult };
}
const initializeLanguageOldValue = () => {
// Keep the old language value to be able to compare to the new one and revert to it if necessary.
languagesList.attr( 'data-old-value', languagesList.children( ':selected' ).first().val() );
};
;// ./js/src/lib/metabox-autocomplete.js
/**
* @package Polylang
*/
// Translations autocomplete input box.
function initMetaboxAutoComplete() {
jQuery('.tr_lang').each(
function () {
var tr_lang = jQuery(this).attr('id').substring(8);
var td = jQuery(this).parent().parent().siblings('.pll-edit-column');
jQuery(this).autocomplete(
{
minLength: 0,
source: ajaxurl + '?action=pll_posts_not_translated' +
'&post_language=' + jQuery('.post_lang_choice').val() +
'&translation_language=' + tr_lang +
'&post_type=' + jQuery('#post_type').val() +
'&_pll_nonce=' + jQuery('#_pll_nonce').val(),
select: function (event, ui) {
jQuery('#htr_lang_' + tr_lang).val(ui.item.id);
// ui.item.link is built and come from server side and is well escaped when necessary
td.html(ui.item.link); // phpcs:ignore WordPressVIPMinimum.JS.HTMLExecutingFunctions.html
},
}
);
// when the input box is emptied
jQuery(this).on(
'blur',
function () {
if ( ! jQuery(this).val() ) {
jQuery('#htr_lang_' + tr_lang).val(0);
// Value is retrieved from HTML already generated server side
td.html(td.siblings('.hidden').children().clone()); // phpcs:ignore WordPressVIPMinimum.JS.HTMLExecutingFunctions.html
}
}
);
}
);
}
;// ./js/src/classic-editor.js
/**
* @package Polylang
*/
// tag suggest in metabox
jQuery(
function ( $ ) {
$.ajaxPrefilter(
function ( options, originalOptions, jqXHR ) {
var lang = $( '.post_lang_choice' ).val();
if ( 'string' === typeof options.data && -1 !== options.url.indexOf( 'action=ajax-tag-search' ) && lang ) {
options.data = 'lang=' + lang + '&' + options.data;
}
}
);
}
);
// overrides tagBox.get
jQuery(
function ( $ ) {
// overrides function to add the language
tagBox.get = function ( id ) {
var tax = id.substr( id.indexOf( '-' ) + 1 );
// add the language in the $_POST variable
var data = {
action: 'get-tagcloud',
lang: $( '.post_lang_choice' ).val(),
tax: tax
}
$.post(
ajaxurl,
data,
function ( r, stat ) {
if ( 0 == r || 'success' != stat ) {
r = wpAjax.broken;
}
// @see code from WordPress core https://github.com/WordPress/WordPress/blob/5.2.2/wp-admin/js/tags-box.js#L291
// @see wp_generate_tag_cloud function which generate the escaped HTML https://github.com/WordPress/WordPress/blob/a02b5cc2a8eecb8e076fbb7cf4de7bd2ec8a8eb1/wp-includes/category-template.php#L966-L975
r = $( '<div />' ).addClass( 'the-tagcloud' ).attr( 'id', 'tagcloud-' + tax ).html( r ); // phpcs:ignore WordPressVIPMinimum.JS.HTMLExecutingFunctions.html
$( 'a', r ).on(
'click',
function () {
tagBox.flushTags( $( this ).closest( '.inside' ).children( '.tagsdiv' ), this );
return false;
}
);
var tagCloud = $( '#tagcloud-' + tax );
// add an if else condition to allow modifying the tags outputted when switching the language
var v = tagCloud.css( 'display' );
if ( v ) {
// See the comment above when r variable is created.
$( '#tagcloud-' + tax ).replaceWith( r ); // phpcs:ignore WordPressVIPMinimum.JS.HTMLExecutingFunctions.replaceWith
$( '#tagcloud-' + tax ).css( 'display', v );
}
else {
// See the comment above when r variable is created.
$( '#' + id ).after( r ); // phpcs:ignore WordPressVIPMinimum.JS.HTMLExecutingFunctions.after
}
}
);
}
}
);
jQuery(
function ( $ ) {
// collect taxonomies - code partly copied from WordPress
var taxonomies = new Array();
$( '.categorydiv' ).each(
function () {
var this_id = $( this ).attr( 'id' ), taxonomyParts, taxonomy;
taxonomyParts = this_id.split( '-' );
taxonomyParts.shift();
taxonomy = taxonomyParts.join( '-' );
taxonomies.push( taxonomy ); // store the taxonomy for future use
// add our hidden field in the new category form - for each hierarchical taxonomy
// to set the language when creating a new category
// html code inserted come from html code itself.
$( '#' + taxonomy + '-add-submit' ).before( // phpcs:ignore WordPressVIPMinimum.JS.HTMLExecutingFunctions.before
$( '<input />' ).attr( 'type', 'hidden' )
.attr( 'id', taxonomy + '-lang' )
.attr( 'name', 'term_lang_choice' )
.attr( 'value', $( '.post_lang_choice' ).val() )
);
}
);
// Initialize current language to be able to compare if it changes.
initializeLanguageOldValue();
// ajax for changing the post's language in the languages metabox
$( '.post_lang_choice' ).on(
'change',
function ( event ) {
// Initialize the confirmation dialog box.
const confirmationModal = initializeConfirmationModal();
const { dialogContainer: dialog } = confirmationModal;
let { dialogResult } = confirmationModal;
// The selected option in the dropdown list.
const selectedOption = event.target;
if ( $( this ).data( 'old-value' ) !== selectedOption.value && ! isEmptyPost() ) {
dialog.dialog( 'open' );
} else {
dialogResult = Promise.resolve();
}
dialogResult.then(
() => {
var data = {
action: 'post_lang_choice',
lang: selectedOption.value,
post_type: $( '#post_type' ).val(),
taxonomies: taxonomies,
post_id: $( '#post_ID' ).val(),
_pll_nonce: $( '#_pll_nonce' ).val()
}
$.post(
ajaxurl,
data,
function ( response ) {
// Target a non existing WP HTML id to avoid a conflict with WP ajax requests.
var res = wpAjax.parseAjaxResponse( response, 'pll-ajax-response' );
$.each(
res.responses,
function () {
switch ( this.what ) {
case 'translations': // translations fields
// Data is built and come from server side and is well escaped when necessary
$( '.translations' ).html( this.data ); // phpcs:ignore WordPressVIPMinimum.JS.HTMLExecutingFunctions.html
initMetaboxAutoComplete();
break;
case 'taxonomy': // categories metabox for posts
var tax = this.data;
// @see wp_terms_checklist https://github.com/WordPress/WordPress/blob/5.2.2/wp-admin/includes/template.php#L175
// @see https://github.com/WordPress/WordPress/blob/5.2.2/wp-admin/includes/class-walker-category-checklist.php#L89-L111
$( '#' + tax + 'checklist' ).html( this.supplemental.all ); // phpcs:ignore WordPressVIPMinimum.JS.HTMLExecutingFunctions.html
// @see wp_popular_terms_checklist https://github.com/WordPress/WordPress/blob/5.2.2/wp-admin/includes/template.php#L236
$( '#' + tax + 'checklist-pop' ).html( this.supplemental.populars ); // phpcs:ignore WordPressVIPMinimum.JS.HTMLExecutingFunctions.html
// @see wp_dropdown_categories https://github.com/WordPress/WordPress/blob/5.5.1/wp-includes/category-template.php#L336
// which is called by PLL_Admin_Classic_Editor::post_lang_choice to generate supplemental.dropdown
$( '#new' + tax + '_parent' ).replaceWith( this.supplemental.dropdown ); // phpcs:ignore WordPressVIPMinimum.JS.HTMLExecutingFunctions.replaceWith
$( '#' + tax + '-lang' ).val( $( '.post_lang_choice' ).val() ); // hidden field
break;
case 'pages': // parent dropdown list for pages
// @see wp_dropdown_pages https://github.com/WordPress/WordPress/blob/5.2.2/wp-includes/post-template.php#L1186-L1208
// @see https://github.com/WordPress/WordPress/blob/5.2.2/wp-includes/class-walker-page-dropdown.php#L88
$( '#parent_id' ).html( this.data ); // phpcs:ignore WordPressVIPMinimum.JS.HTMLExecutingFunctions.html
break;
case 'flag': // flag in front of the select dropdown
// Data is built and come from server side and is well escaped when necessary
$( '.pll-select-flag' ).html( this.data ); // phpcs:ignore WordPressVIPMinimum.JS.HTMLExecutingFunctions.html
break;
case 'permalink': // Sample permalink
var div = $( '#edit-slug-box' );
if ( '-1' != this.data && div.children().length ) {
// @see get_sample_permalink_html https://github.com/WordPress/WordPress/blob/5.2.2/wp-admin/includes/post.php#L1425-L1454
div.html( this.data ); // phpcs:ignore WordPressVIPMinimum.JS.HTMLExecutingFunctions.html
}
break;
}
}
);
// Creates an event once the language has been successfully changed.
const onPostLangChoice = new CustomEvent(
"onPostLangChoice",
{
detail: {
lang: JSON.parse( selectedOption.options[selectedOption.options.selectedIndex].getAttribute( 'data-lang' ) )
},
}
);
document.dispatchEvent( onPostLangChoice );
}
)
},
() => {} // Do nothing when promise is rejected by clicking the Cancel dialog button.
);
function isEmptyPost() {
const title = $( 'input#title' ).val();
const content = $( 'textarea#content' ).val();
const excerpt = $( 'textarea#excerpt' ).val();
return ! title && ! content && ! excerpt;
}
}
);
// Listen to `onPostLangChoice` to perform actions after the language has been changed.
document.addEventListener(
'onPostLangChoice',
( e ) => {
// Update the old language with the new one to be able to compare it in the next changing.
initializeLanguageOldValue();
// Modifies the language in the tag cloud.
$( '.tagcloud-link' ).each(
function () {
var id = $( this ).attr( 'id' );
tagBox.get( id );
}
);
// Modifies the text direction.
let dir = e.detail.lang.is_rtl ? 'rtl' : 'ltr'
$( 'body' ).removeClass( 'pll-dir-rtl' ).removeClass( 'pll-dir-ltr' ).addClass( 'pll-dir-' + dir );
$( '#content_ifr' ).contents().find( 'html' ).attr( 'lang', e.detail.lang.locale ).attr( 'dir', dir );
$( '#content_ifr' ).contents().find( 'body' ).attr( 'dir', dir );
// Refresh media libraries.
pll.media.resetAllAttachmentsCollections();
}
);
initMetaboxAutoComplete();
}
);
/**
* @since 3.0
*
* @namespace pll
*/
var pll = window.pll || {};
/**
* @since 3.0
*
* @namespace pll.media
*/
_.extend( pll, { media: {} } );
/**
* @since 3.0
*
* @alias pll.media
* @memberOf pll
* @namespace
*/
var media = _.extend(
pll.media, /** @lends pll.media.prototype */
{
/**
* TODO: Find a way to delete references to Attachments collections that are not used anywhere else.
*
* @type {wp.media.model.Attachments}
*/
attachmentsCollections : [],
/**
* Imitates { @see wp.media.query } but log all Attachments collections created.
*
* @param {Object} [props]
* @return {wp.media.model.Attachments}
*/
query: function ( props ) {
var attachments = pll.media.query.delegate( props );
pll.media.attachmentsCollections.push( attachments );
return attachments;
},
resetAllAttachmentsCollections: function () {
this.attachmentsCollections.forEach(
function ( attachmentsCollection ) {
/**
* First reset the { @see wp.media.model.Attachments } collection.
* Then, if it is mirroring a { @see wp.media.model.Query } collection,
* refresh this one too, so it will fetch new data from the server,
* and then the wp.media.model.Attachments collection will synchronize with the new data.
*/
attachmentsCollection.reset();
if (attachmentsCollection.mirroring) {
attachmentsCollection.mirroring._hasMore = true;
attachmentsCollection.mirroring.reset();
}
}
);
}
}
);
if ( 'undefined' !== typeof wp && 'undefined' !== typeof wp.media ) {
/**
* @since 3.0
*
* @memberOf pll.media
*/
media.query = _.extend(
media.query, /** @lends pll.media.query prototype */
{
/**
* @type Function References WordPress { @see wp.media.query } constructor
*/
delegate: wp.media.query
}
)
// Substitute WordPress media query shortcut with our decorated function.
wp.media.query = media.query
}
File diff suppressed because one or more lines are too long
@@ -0,0 +1,303 @@
/**
* @package Polylang
*/
jQuery(
function ( $ ) {
var addLanguageForm = $( '.languages-step' ); // Form element.
var languageFields = $( '#language-fields' ); // Element where to append hidden fields for creating language.
var languagesTable = $( '#languages' ); // Table element contains languages list to create.
var languagesListTable = $( '#languages tbody' ); // Table rows with languages list to create.
var definedLanguagesListTable = $( '#defined-languages tbody' ); // Table rows with already defined languages list.
var languagesList = $( '#lang_list' ); // Select form element with predefined languages without already created languages.
var nextStepButton = $( '[name="save_step"]' ); // The button for continuing to the next step.
var messagesContainer = $( '#messages' ); // Element where to display error messages.
var languagesMap = new Map(); // Languages map object for managing the languages to create.
var dialog = $( '#dialog' ); // Dialog box for alerting the language selected has not been added to the list.
/**
* Add a language in the list to create it in Polylang settings
*
* @param {object} language The language object
*/
function addLanguage( language ) {
// language properties come from the select dropdown which is built server side and well escaped.
// see template view-wizard-step-languages.php.
var languageValueHtml = $( '<td />' ).text( language.text ).prepend( language.flagUrl ); // phpcs:ignore WordPressVIPMinimum.JS.HTMLExecutingFunctions.prepend
var languageTrashIconHtml = $( '<td />' )
.append( // phpcs:ignore WordPressVIPMinimum.JS.HTMLExecutingFunctions.append
$( '<span />' )
.addClass( 'dashicons dashicons-trash' )
.attr( 'data-language', language.locale )
.append( // phpcs:ignore WordPressVIPMinimum.JS.HTMLExecutingFunctions.append
$( '<span />' )
.addClass( 'screen-reader-text' )
.text( pll_wizard_params.i18n_remove_language_icon )
)
);
// see the comment and the hardcoded code above. languageTrashIconHtml and languageValueHtml are safe.
var languageLineHtml = $( '<tr />' ).prepend( languageTrashIconHtml ).prepend( languageValueHtml ); // phpcs:ignore WordPressVIPMinimum.JS.HTMLExecutingFunctions.prepend
var languageFieldHtml = $( '<input />' ).attr(
{
type: 'hidden',
name: 'languages[]'
}
).val( language.locale );
languagesList.val( '' );
languagesList.selectmenu( 'refresh' ); // Refresh jQuery selectmenu widget after changing the value.
languagesMap.set( language.locale, language );
// see above how languageLineHtml is built.
languagesListTable.append( languageLineHtml ); // phpcs:ignore WordPressVIPMinimum.JS.HTMLExecutingFunctions.append
// Bind click event on trash icon.
languagesListTable.on(
'click',
'span[data-language=' + language.locale + ']',
function ( event ) {
event.preventDefault();
// Remove line in languages table.
$( this ).parents( 'tr' ).remove();
// Remove input field.
var languageField = languageFields.children( 'input[value=' + $( this ).data( 'language' ) + ']' ).remove();
// If there is no more languages hide languages table.
if ( languagesListTable.children().length <= 0 ) {
languagesTable.hide();
}
// Remove language from the Map.
languagesMap.delete( $( this ).data( 'language' ) );
// Hide error message.
hideError();
}
);
// see above how languageFieldHtml is built.
// Add hidden input field for posting the form.
languageFields.append( languageFieldHtml ); // phpcs:ignore WordPressVIPMinimum.JS.HTMLExecutingFunctions.append
}
/**
* Display an error message
*
* @param {string} message The message to display
*/
function showError( message ) {
messagesContainer.empty();
// html is hardcoded and use of jQuery text method which is safe to add message value.
// In addition message is i18n value which is initialized server side in PLL_Wizard::add_step_languages and correctly escaped.
messagesContainer.prepend( $( '<p/>' ).addClass( 'error' ).text( message ) ); // phpcs:ignore WordPressVIPMinimum.JS.HTMLExecutingFunctions.prepend
}
/**
* Hide all error messages and fields in error
*/
function hideError() {
messagesContainer.empty();
addLanguageForm.find( '.error' ).removeClass( 'error field-in-error' );
}
/**
* Style the field to indicate where the error is
*
* @param {object} field The jQuery element which is in error
*/
function showFieldInError( field ) {
field.addClass( 'error field-in-error' );
}
/**
* Focus on a specific element
*
* @param {object} field The jQuery element which will be focused
*/
function focusOnField( field ) {
field.trigger( 'focus' );
}
/**
* Disable a specific button
*
* @param {object} button
*/
function disableButton( button ){
button.prop( 'disabled', true );
// Because the button is disabled we need to add the value of the button to ensure it will pass in the request.
addLanguageForm.append( // phpcs:ignore WordPressVIPMinimum.JS.HTMLExecutingFunctions.append
$( '<input />' ).prop(
{
type: 'hidden',
name: button.prop( 'name' ),
value: button.prop( 'value' )
}
)
);
}
/**
* Remove error when a new selection is done in languages list.
*/
languagesList.on(
'selectmenuchange',
function () {
hideError();;
}
);
/**
* Bind click event on "Add language" button
*/
$( '#add-language' ).on(
'click',
function ( event ) {
hideError();
var selectedOption = event.currentTarget.form.lang_list.options[event.currentTarget.form.lang_list.selectedIndex];
if ( '' !== selectedOption.value && ! languagesMap.has( selectedOption.value ) ) {
addLanguage(
{
locale: selectedOption.value,
text: selectedOption.innerText,
name: $( selectedOption ).data( 'language-name' ),
flagUrl: $( selectedOption ).data( 'flag-html' )
}
);
// Show table of languages.
languagesTable.show();
// Put back the focus on the select language field after clicking on "Add language button".
focusOnField( $( '#lang_list-button' ) );
} else {
var message = pll_wizard_params.i18n_no_language_selected;
if ( languagesMap.has( selectedOption.value ) ) {
message = pll_wizard_params.i18n_language_already_added;
}
showError( message );
showFieldInError( languagesList.next( 'span.ui-selectmenu-button' ) );
focusOnField( $( '#lang_list-button' ) );
}
}
);
/**
* Bind submit event on "add_lang" form
*/
addLanguageForm.on(
'submit',
function ( event ) {
// Verify if there is at least one language.
var isLanguagesAlreadyDefined = definedLanguagesListTable.children().length > 0;
var selectedLanguage = $( '#lang_list' ).val();
if ( languagesMap.size <= 0 && ! isLanguagesAlreadyDefined ) {
if ( '' === selectedLanguage ) {
showError( pll_wizard_params.i18n_no_language_added );
showFieldInError( languagesList.next( 'span.ui-selectmenu-button' ) );
focusOnField( $( '#lang_list-button' ) );
} else {
showError( pll_wizard_params.i18n_add_language_needed );
showFieldInError( languagesList.next( 'span.ui-selectmenu-button' ) );
focusOnField( $( '#add-language' ) ); // Put the focus on the "Add language" button.
}
return false;
}
// Verify if the language has been added in the list otherwise display a dialog box to confirm what to do.
if ( '' !== selectedLanguage ) {
// Verify we don't add a duplicate language before opening the dialog box otherwise display an error message.
if ( ! languagesMap.has( selectedLanguage ) ) {
dialog.dialog( 'open' );
} else {
showError( pll_wizard_params.i18n_language_already_added );
showFieldInError( languagesList.next( 'span.ui-selectmenu-button' ) );
focusOnField( $( '#lang_list-button' ) );
}
return false;
}
disableButton( nextStepButton );
}
);
// Is there an error return by PHP ?
var searchParams = new URLSearchParams( document.location.search );
if ( searchParams.has( 'activate_error' ) ) {
// If the error code exists, display it.
if ( undefined !== pll_wizard_params[ searchParams.get( 'activate_error' ) ] ) {
showError( pll_wizard_params[ searchParams.get( 'activate_error' ) ] );
}
}
function confirmDialog( what ) {
switch ( what ) {
case 'yes':
var selectedOption = $( '#lang_list' ).children( ':selected' );
addLanguage(
{
locale: selectedOption[0].value,
text: selectedOption[0].innerText,
name: $( selectedOption ).data( 'language-name' ),
flagUrl: $( selectedOption ).data( 'flag-html' )
}
);
break;
case 'no':
// Empty select form field and submit again the form.
languagesList.val( '' );
break;
case 'ignore':
}
dialog.dialog( 'close' );
if ( 'ignore' === what ) {
focusOnField( $( '#lang_list-button' ) );
} else {
addLanguageForm.submit();
}
}
// Initialize dialog box in the case a language is selected but not added in the list.
dialog.dialog(
{
autoOpen: false,
modal: true,
draggable: false,
resizable: false,
title: pll_wizard_params.i18n_dialog_title,
minWidth: 600,
maxWidth: '100%',
open: function ( event, ui ) {
// Change dialog box position for rtl language
if ( $( 'body' ).hasClass( 'rtl' ) ) {
$( this ).parent().css(
{
right: $( this ).parent().css( 'left' ),
left: 'auto'
}
);
}
// Display language name and flag information in dialog box.
$( this ).find( '#dialog-language' ).text( $( '#lang_list' ).children( ':selected' ).first().text() );
// language properties come from the select dropdown #lang_list which is built server side and well escaped.
// see template view-wizard-step-languages.php.
$( this ).find( '#dialog-language-flag' ).empty().prepend( $( '#lang_list' ).children( ':selected' ).data( 'flag-html' ) ); // phpcs:ignore WordPressVIPMinimum.JS.HTMLExecutingFunctions.prepend
},
buttons: [
{
text: pll_wizard_params.i18n_dialog_yes_button,
click: function ( event ) {
confirmDialog( 'yes' );
}
},
{
text: pll_wizard_params.i18n_dialog_no_button,
click: function ( event ) {
confirmDialog( 'no' );
}
},
{
text: pll_wizard_params.i18n_dialog_ignore_button,
click: function ( event ) {
confirmDialog( 'ignore' );
}
}
]
}
)
}
);
@@ -0,0 +1 @@
jQuery(function(a){var e=a(".languages-step"),n=a("#language-fields"),t=a("#languages"),l=a("#languages tbody"),i=a("#defined-languages tbody"),r=a("#lang_list"),d=a('[name="save_step"]'),s=a("#messages"),o=new Map,g=a("#dialog");function u(e){var i=a("<td />").text(e.text).prepend(e.flagUrl),d=a("<td />").append(a("<span />").addClass("dashicons dashicons-trash").attr("data-language",e.locale).append(a("<span />").addClass("screen-reader-text").text(pll_wizard_params.i18n_remove_language_icon))),s=a("<tr />").prepend(d).prepend(i),g=a("<input />").attr({type:"hidden",name:"languages[]"}).val(e.locale);r.val(""),r.selectmenu("refresh"),o.set(e.locale,e),l.append(s),l.on("click","span[data-language="+e.locale+"]",function(e){e.preventDefault(),a(this).parents("tr").remove();n.children("input[value="+a(this).data("language")+"]").remove();l.children().length<=0&&t.hide(),o.delete(a(this).data("language")),c()}),n.append(g)}function p(e){s.empty(),s.prepend(a("<p/>").addClass("error").text(e))}function c(){s.empty(),e.find(".error").removeClass("error field-in-error")}function _(a){a.addClass("error field-in-error")}function m(a){a.trigger("focus")}r.on("selectmenuchange",function(){c()}),a("#add-language").on("click",function(e){c();var n=e.currentTarget.form.lang_list.options[e.currentTarget.form.lang_list.selectedIndex];if(""===n.value||o.has(n.value)){var l=pll_wizard_params.i18n_no_language_selected;o.has(n.value)&&(l=pll_wizard_params.i18n_language_already_added),p(l),_(r.next("span.ui-selectmenu-button")),m(a("#lang_list-button"))}else u({locale:n.value,text:n.innerText,name:a(n).data("language-name"),flagUrl:a(n).data("flag-html")}),t.show(),m(a("#lang_list-button"))}),e.on("submit",function(n){var t,l=i.children().length>0,s=a("#lang_list").val();return o.size<=0&&!l?(""===s?(p(pll_wizard_params.i18n_no_language_added),_(r.next("span.ui-selectmenu-button")),m(a("#lang_list-button"))):(p(pll_wizard_params.i18n_add_language_needed),_(r.next("span.ui-selectmenu-button")),m(a("#add-language"))),!1):""!==s?(o.has(s)?(p(pll_wizard_params.i18n_language_already_added),_(r.next("span.ui-selectmenu-button")),m(a("#lang_list-button"))):g.dialog("open"),!1):((t=d).prop("disabled",!0),void e.append(a("<input />").prop({type:"hidden",name:t.prop("name"),value:t.prop("value")})))});var f=new URLSearchParams(document.location.search);function h(n){switch(n){case"yes":var t=a("#lang_list").children(":selected");u({locale:t[0].value,text:t[0].innerText,name:a(t).data("language-name"),flagUrl:a(t).data("flag-html")});break;case"no":r.val("")}g.dialog("close"),"ignore"===n?m(a("#lang_list-button")):e.submit()}f.has("activate_error")&&void 0!==pll_wizard_params[f.get("activate_error")]&&p(pll_wizard_params[f.get("activate_error")]),g.dialog({autoOpen:!1,modal:!0,draggable:!1,resizable:!1,title:pll_wizard_params.i18n_dialog_title,minWidth:600,maxWidth:"100%",open:function(e,n){a("body").hasClass("rtl")&&a(this).parent().css({right:a(this).parent().css("left"),left:"auto"}),a(this).find("#dialog-language").text(a("#lang_list").children(":selected").first().text()),a(this).find("#dialog-language-flag").empty().prepend(a("#lang_list").children(":selected").data("flag-html"))},buttons:[{text:pll_wizard_params.i18n_dialog_yes_button,click:function(a){h("yes")}},{text:pll_wizard_params.i18n_dialog_no_button,click:function(a){h("no")}},{text:pll_wizard_params.i18n_dialog_ignore_button,click:function(a){h("ignore")}}]})});
@@ -0,0 +1,20 @@
/**
* @package Polylang
*/
/**
* Media list table
* When clicking on attach link, filters find post list per media language
*/
jQuery(
function ( $ ) {
$.ajaxPrefilter(
function ( options, originalOptions, jqXHR ) {
if ( 'string' === typeof options.data && -1 !== options.data.indexOf( 'action=find_posts' ) ) {
options.data = 'pll_post_id=' + $( '#affected' ).val() + '&' + options.data;
}
}
);
}
);
@@ -0,0 +1 @@
jQuery(function(a){a.ajaxPrefilter(function(t,d,f){"string"==typeof t.data&&-1!==t.data.indexOf("action=find_posts")&&(t.data="pll_post_id="+a("#affected").val()+"&"+t.data)})});
@@ -0,0 +1,220 @@
/**
* Handles the options in the language switcher nav menu metabox.
*
* @package Polylang
*/
const pllNavMenu = {
/**
* The element wrapping the menu elements.
*
* @member {HTMLElement|null}
*/
wrapper: null,
/**
* Init.
*/
init: () => {
if ( document.readyState !== 'loading' ) {
pllNavMenu.ready();
} else {
document.addEventListener( 'DOMContentLoaded', pllNavMenu.ready );
}
},
/**
* Called when the DOM is ready. Attaches the events to the wrapper.
*/
ready: () => {
pllNavMenu.wrapper = document.getElementById( 'menu-to-edit' );
if ( ! pllNavMenu.wrapper ) {
return;
}
pllNavMenu.wrapper.addEventListener( 'click', pllNavMenu.printMetabox );
pllNavMenu.wrapper.addEventListener( 'change', pllNavMenu.ensureContent );
pllNavMenu.wrapper.addEventListener( 'change', pllNavMenu.showHideRows );
},
printMetabox: {
/**
* Event callback that prints our checkboxes in the language switcher.
*
* @param {Event} event The event.
*/
handleEvent: ( event ) => {
if ( ! event.target.classList.contains( 'item-edit' ) ) {
// Not clicking on a Edit arrow button.
return;
}
const metabox = event.target.closest( '.menu-item' ).querySelector( '.menu-item-settings' );
if ( ! metabox?.id ) {
// Should not happen.
return;
}
if ( ! metabox.querySelectorAll( 'input[value="#pll_switcher"][type=text]' ).length ) {
// Not our metabox, or already replaced.
return;
}
// Remove default fields we don't need.
[ ...metabox.children ].forEach( ( el ) => {
if ( 'P' === el.nodeName && ! el.classList.contains( 'field-move' ) ) {
el.remove();
}
} );
const t = pllNavMenu.printMetabox;
const itemId = Number( metabox.id.replace( 'menu-item-settings-', '' ) );
metabox.append( t.createHiddenInput( 'title', itemId, pll_data.title ) ); // phpcs:ignore WordPressVIPMinimum.JS.HTMLExecutingFunctions.append
metabox.append( t.createHiddenInput( 'url', itemId, '#pll_switcher' ) ); // phpcs:ignore WordPressVIPMinimum.JS.HTMLExecutingFunctions.append
metabox.append( t.createHiddenInput( 'pll-detect', itemId, 1 ) ); // phpcs:ignore WordPressVIPMinimum.JS.HTMLExecutingFunctions.append
const ids = Array( 'hide_if_no_translation', 'hide_current', 'force_home', 'show_flags', 'show_names', 'dropdown' ); // Reverse order.
const isValDefined = typeof( pll_data.val[ itemId ] ) !== 'undefined';
ids.forEach( ( optionName ) => {
// Create the checkbox's wrapper.
const inputWrapper = t.createElement( 'p', { class: 'description' } );
if ( 'hide_current' === optionName && isValDefined && 1 === pll_data.val[ itemId ].dropdown ) {
// Hide the `hide_current` checkbox if `dropdown` is checked.
inputWrapper.classList.add( 'hidden' );
}
metabox.prepend( inputWrapper ); // phpcs:ignore WordPressVIPMinimum.JS.HTMLExecutingFunctions.prepend
// Create the checkbox's label.
const inputId = `edit-menu-item-${ optionName }-${ itemId }`;
const label = t.createElement( 'label', { 'for': inputId } );
label.innerText = ` ${ pll_data.strings[ optionName ] }`;
inputWrapper.append( label ); // phpcs:ignore WordPressVIPMinimum.JS.HTMLExecutingFunctions.append
// Create the checkbox.
const cb = t.createElement( 'input', {
type: 'checkbox',
id: inputId,
name: `menu-item-${ optionName }[${ itemId }]`,
value: 1,
} );
if ( ( isValDefined && 1 === pll_data.val[ itemId ][ optionName ] ) || ( ! isValDefined && 'show_names' === optionName ) ) { // `show_names` as default value.
cb.checked = true;
}
label.prepend( cb ); // phpcs:ignore WordPressVIPMinimum.JS.HTMLExecutingFunctions.prepend
} );
},
/**
* Creates and returns a `<input type=hidden"/>` element.
*
* @param {string} id An identifier for this input. It will be part of the final `id` attribute.
* @param {number} itemId The ID of the menu element (post ID).
* @param {string|number} value The input's value.
* @return {HTMLElement} The input element.
*/
createHiddenInput: ( id, itemId, value ) => {
return pllNavMenu.printMetabox.createElement( 'input', {
type: 'hidden',
id: `edit-menu-item-${ id }-${ itemId }`,
name: `menu-item-${ id }[${ itemId }]`,
value: value,
} );
},
/**
* Creates and returns an element.
*
* @param {string} type Element's type.
* @param {Object} atts Element's attributes.
* @return {HTMLElement} The element.
*/
createElement: ( type, atts ) => {
const el = document.createElement( type );
for ( const [ key, value ] of Object.entries( atts ) ) {
el.setAttribute( key, value );
}
return el;
},
},
ensureContent: {
regExpr: new RegExp( /^edit-menu-item-show_(names|flags)-(\d+)$/ ),
/**
* Event callback that disallows unchecking both `show_names` and `show_flags`.
*
* @param {Event} event The event.
*/
handleEvent: ( event ) => {
if ( ! event.target.id || event.target.checked ) {
// Now checked, nothing to do.
return;
}
const matches = event.target.id.match( pllNavMenu.ensureContent.regExpr );
if ( ! matches ) {
// Not the checkbox we want.
return;
}
// Check the other checkbox.
const [ , type, id ] = matches;
const otherType = 'names' === type ? 'flags' : 'names';
document.getElementById( `edit-menu-item-show_${ otherType }-${ id }` ).checked = true;
},
},
showHideRows: {
regExpr: new RegExp( /^edit-menu-item-dropdown-(\d+)$/ ),
/**
* Event callback that shows or hides the `hide_current` checkbox when `dropdown` is checked.
*
* @param {Event} event The event.
*/
handleEvent: ( event ) => {
if ( ! event.target.id ) {
// Not the checkbox we want.
return;
}
const matches = event.target.id.match( pllNavMenu.showHideRows.regExpr );
if ( ! matches ) {
// Not the checkbox we want.
return;
}
const hideCb = document.getElementById( `edit-menu-item-hide_current-${ matches[1] }` );
if ( ! hideCb ) {
// Should not happen.
return;
}
const description = hideCb.closest( '.description' );
// Hide or show.
description.classList.toggle( 'hidden', event.target.checked );
if ( event.target.checked ) {
// Uncheck after hiding.
hideCb.checked = false;
hideCb.dispatchEvent( new Event( 'change' ) );
}
},
},
};
pllNavMenu.init();
@@ -0,0 +1 @@
const pllNavMenu={wrapper:null,init:()=>{"loading"!==document.readyState?pllNavMenu.ready():document.addEventListener("DOMContentLoaded",pllNavMenu.ready)},ready:()=>{pllNavMenu.wrapper=document.getElementById("menu-to-edit"),pllNavMenu.wrapper&&(pllNavMenu.wrapper.addEventListener("click",pllNavMenu.printMetabox),pllNavMenu.wrapper.addEventListener("change",pllNavMenu.ensureContent),pllNavMenu.wrapper.addEventListener("change",pllNavMenu.showHideRows))},printMetabox:{handleEvent:e=>{if(!e.target.classList.contains("item-edit"))return;const t=e.target.closest(".menu-item").querySelector(".menu-item-settings");if(!t?.id)return;if(!t.querySelectorAll('input[value="#pll_switcher"][type=text]').length)return;[...t.children].forEach(e=>{"P"!==e.nodeName||e.classList.contains("field-move")||e.remove()});const n=pllNavMenu.printMetabox,a=Number(t.id.replace("menu-item-settings-",""));t.append(n.createHiddenInput("title",a,pll_data.title)),t.append(n.createHiddenInput("url",a,"#pll_switcher")),t.append(n.createHiddenInput("pll-detect",a,1));const r=Array("hide_if_no_translation","hide_current","force_home","show_flags","show_names","dropdown"),d=void 0!==pll_data.val[a];r.forEach(e=>{const r=n.createElement("p",{class:"description"});"hide_current"===e&&d&&1===pll_data.val[a].dropdown&&r.classList.add("hidden"),t.prepend(r);const l=`edit-menu-item-${e}-${a}`,i=n.createElement("label",{for:l});i.innerText=` ${pll_data.strings[e]}`,r.append(i);const c=n.createElement("input",{type:"checkbox",id:l,name:`menu-item-${e}[${a}]`,value:1});(d&&1===pll_data.val[a][e]||!d&&"show_names"===e)&&(c.checked=!0),i.prepend(c)})},createHiddenInput:(e,t,n)=>pllNavMenu.printMetabox.createElement("input",{type:"hidden",id:`edit-menu-item-${e}-${t}`,name:`menu-item-${e}[${t}]`,value:n}),createElement:(e,t)=>{const n=document.createElement(e);for(const[e,a]of Object.entries(t))n.setAttribute(e,a);return n}},ensureContent:{regExpr:new RegExp(/^edit-menu-item-show_(names|flags)-(\d+)$/),handleEvent:e=>{if(!e.target.id||e.target.checked)return;const t=e.target.id.match(pllNavMenu.ensureContent.regExpr);if(!t)return;const[,n,a]=t,r="names"===n?"flags":"names";document.getElementById(`edit-menu-item-show_${r}-${a}`).checked=!0}},showHideRows:{regExpr:new RegExp(/^edit-menu-item-dropdown-(\d+)$/),handleEvent:e=>{if(!e.target.id)return;const t=e.target.id.match(pllNavMenu.showHideRows.regExpr);if(!t)return;const n=document.getElementById(`edit-menu-item-hide_current-${t[1]}`);if(!n)return;n.closest(".description").classList.toggle("hidden",e.target.checked),e.target.checked&&(n.checked=!1,n.dispatchEvent(new Event("change")))}}};pllNavMenu.init();
@@ -0,0 +1,178 @@
/**
* @package Polylang
*/
/**
* Tag suggest in quick edit
*/
jQuery(
function ( $ ) {
$.ajaxPrefilter(
function ( options, originalOptions, jqXHR ) {
if ( 'string' === typeof options.data && -1 !== options.data.indexOf( 'action=ajax-tag-search' ) && ( lang = $( ':input[name="inline_lang_choice"]' ).val() ) ) {
options.data = 'lang=' + lang + '&' + options.data;
}
}
);
}
);
/**
* Quick edit
*/
jQuery(
function ( $ ) {
const handleQuickEditInsertion = ( mutationsList ) => {
for ( const mutation of mutationsList ) {
const addedNodes = Array.from( mutation.addedNodes ).filter( el => el.nodeType === Node.ELEMENT_NODE )
const form = addedNodes[0];
if ( 0 < mutation.addedNodes.length && form.classList.contains( 'inline-editor' ) ) {
// WordPress has inserted the quick edit form.
const post_id = Number( form.id.substring( 5 ) );
if ( post_id > 0 ) {
// Get the language dropdown.
const select = form.querySelector( 'select[name="inline_lang_choice"]' );
const lang = document.querySelector( '#lang_' + String( post_id ) ).innerHTML;
select.value = lang; // Populates the dropdown with the post language.
filter_terms( lang ); // Initial filter for category checklist.
filter_pages( lang ); // Initial filter for parent dropdown.
// Modify category checklist and parent dropdown on language change.
select.addEventListener(
'change',
function ( event ) {
const newLang = event.target.value;
filter_terms( newLang );
filter_pages( newLang );
}
);
}
}
/**
* Filters the category checklist.
*/
function filter_terms( lang ) {
if ( "undefined" != typeof( pll_term_languages ) ) {
$.each(
pll_term_languages,
function ( lg, term_tax ) {
$.each(
term_tax,
function ( tax, terms ) {
$.each(
terms,
function ( i ) {
// Backward compatibility with WordPress < 6.7.
// Support both old (WP < 6.7) and new (WP >= 6.7) ID formats.
// Old format: category-123 (WordPress < 6.7).
// New format: in-category-123-1 (WordPress >= 6.7).
const termId = pll_term_languages[ lg ][ tax ][ i ];
const selector = `#${tax}-${termId}, [id^="in-${tax}-${termId}-"]`;
$( selector ).toggle( lang === lg );
}
);
}
);
}
);
}
}
/**
* Filters the parent page dropdown list.
*/
function filter_pages( lang ) {
if ( "undefined" != typeof( pll_page_languages ) ) {
$.each(
pll_page_languages,
function ( lg, pages ) {
$.each(
pages,
function ( i ) {
v = $( '#post_parent option[value="' + pll_page_languages[ lg ][ i ] + '"]' );
lang == lg ? v.show() : v.hide();
}
);
}
);
}
}
}
}
const table = document.getElementById( 'the-list' );
if ( ! table ) {
return;
}
const config = { childList: true, subtree: true };
const observer = new MutationObserver( handleQuickEditInsertion );
observer.observe( table, config);
}
);
/**
* Update rows of translated posts when the language is modified in quick edit
* Acts on ajaxSuccess event
*/
jQuery(
function ( $ ) {
$( document ).ajaxSuccess(
function ( event, xhr, settings ) {
function update_rows( post_id ) {
// collect old translations
var translations = new Array();
$( '.translation_' + post_id ).each(
function () {
translations.push( $( this ).parent().parent().attr( 'id' ).substring( 5 ) );
}
);
var data = {
action: 'pll_update_post_rows',
post_id: post_id,
translations: translations.join( ',' ),
post_type: $( "input[name='post_type']" ).val(),
screen: $( "input[name='screen']" ).val(),
_pll_nonce: $( "input[name='_inline_edit']" ).val() // reuse quick edit nonce
};
// get the modified rows in ajax and update them
$.post(
ajaxurl,
data,
function ( response ) {
if ( response ) {
// Since WP changeset #52710 parseAjaxResponse() return content to notice the user in a HTML tag with ajax-response id.
// Not to disturb this behaviour by executing another ajax request in the ajaxSuccess event, we need to target another unexisting id.
var res = wpAjax.parseAjaxResponse( response, 'pll-ajax-response' );
$.each(
res.responses,
function () {
if ( 'row' == this.what ) {
// data is built with a call to WP_Posts_List_Table::single_row method
// which uses internally other WordPress methods which escape correctly values.
// For Polylang language columns the HTML code is correctly escaped in PLL_Admin_Filters_Columns::post_column method.
$( "#post-" + this.supplemental.post_id ).replaceWith( this.data ); // phpcs:ignore WordPressVIPMinimum.JS.HTMLExecutingFunctions.replaceWith
}
}
);
}
}
);
}
if ( 'string' == typeof( settings.data ) ) { // Need to check the type due to block editor sometime sending FormData objects
var data = wpAjax.unserialize( settings.data ); // what were the data sent by the ajax request?
if ( 'undefined' != typeof( data['action'] ) && 'inline-save' == data['action'] ) {
update_rows( data['post_ID'] );
}
}
}
);
}
);
@@ -0,0 +1 @@
jQuery(function(n){n.ajaxPrefilter(function(e,t,a){"string"==typeof e.data&&-1!==e.data.indexOf("action=ajax-tag-search")&&(lang=n(':input[name="inline_lang_choice"]').val())&&(e.data="lang="+lang+"&"+e.data)})}),jQuery(function(n){const e=document.getElementById("the-list");if(!e)return;new MutationObserver(e=>{for(const i of e){const o=Array.from(i.addedNodes).filter(n=>n.nodeType===Node.ELEMENT_NODE)[0];if(0<i.addedNodes.length&&o.classList.contains("inline-editor")){const s=Number(o.id.substring(5));if(s>0){const l=o.querySelector('select[name="inline_lang_choice"]'),c=document.querySelector("#lang_"+String(s)).innerHTML;l.value=c,t(c),a(c),l.addEventListener("change",function(n){const e=n.target.value;t(e),a(e)})}}function t(e){"undefined"!=typeof pll_term_languages&&n.each(pll_term_languages,function(t,a){n.each(a,function(a,i){n.each(i,function(i){const o=pll_term_languages[t][a][i];n(`#${a}-${o}, [id^="in-${a}-${o}-"]`).toggle(e===t)})})})}function a(e){"undefined"!=typeof pll_page_languages&&n.each(pll_page_languages,function(t,a){n.each(a,function(a){v=n('#post_parent option[value="'+pll_page_languages[t][a]+'"]'),e==t?v.show():v.hide()})})}}}).observe(e,{childList:!0,subtree:!0})}),jQuery(function(n){n(document).ajaxSuccess(function(e,t,a){if("string"==typeof a.data){var i=wpAjax.unserialize(a.data);void 0!==i.action&&"inline-save"==i.action&&function(e){var t=new Array;n(".translation_"+e).each(function(){t.push(n(this).parent().parent().attr("id").substring(5))});var a={action:"pll_update_post_rows",post_id:e,translations:t.join(","),post_type:n("input[name='post_type']").val(),screen:n("input[name='screen']").val(),_pll_nonce:n("input[name='_inline_edit']").val()};n.post(ajaxurl,a,function(e){if(e){var t=wpAjax.parseAjaxResponse(e,"pll-ajax-response");n.each(t.responses,function(){"row"==this.what&&n("#post-"+this.supplemental.post_id).replaceWith(this.data)})}})}(i.post_ID)}})});
@@ -0,0 +1,395 @@
/**
* @package Polylang
*/
jQuery(
function ( $ ) {
// languages list table
// accessibility to row actions on focus
// mainly copy paste of WP code from common.js
var transitionTimeout;
$( 'table.languages' ).on(
{ // restricted to languages list table
focusin: function () {
clearTimeout( transitionTimeout );
var focusedRowActions = $( this ).find( '.row-actions' );
// transitionTimeout is necessary for Firefox, but Chrome won't remove the CSS class without a little help.
$( '.row-actions' ).not( this ).removeClass( 'visible' );
focusedRowActions.addClass( 'visible' );
},
focusout: function () {
// Tabbing between post title and .row-actions links needs a brief pause, otherwise
// the .row-actions div gets hidden in transit in some browsers ( ahem, Firefox ).
transitionTimeout = setTimeout(
function () {
focusedRowActions.removeClass( 'visible' );
},
30
);
}
},
'tr'
); // acts on the whole tr instead of single td as we have actions links in several columns
/**
* Common functions and variables for overriding languages and flags dropdown list by a jQuery UI selectmenu widget.
*/
// Allow to check if a flag list dropdown is present. Not present in the Wizard steps or other settings page.
var flagListExist = $( "#flag_list" ).length;
// Allow to check if a language list dropdown is present. Not present in other settings page.
var langListExist = $( "#lang_list" ).length;
// jQuery UI selectmenu widget width option
var defaultSelectmenuWidth = '95%';
var wizardSelectmenuWidth = '100%';
// Inject flag image when jQuery UI selectmenu is created or an item is selected.
// jQuery UI 1.12 introduces a wrapper inside de li tag which is necessary to selectmenu widget to work correctly.
// Mainly copy from the original jQuery UI 1.12 selectmenu widget _renderItem method.
var selectmenuRenderItem = function ( ul, item ) {
var li = $( '<li>' );
var wrapper = $( '<div>');
if ( item.disabled ) {
this._addClass( li, null, "ui-state-disabled" );
}
// `item.label` is the original `<option>`'s label.
this._setText( wrapper, item.label );
// Add the flag from the data attribute in the selected element.
// `item.element` is the original `<option>` element, the data to prepend comes from a `data-html-flag` HTML attribute, filled by a method from `PLL_Language`.
wrapper.prepend( $( item.element ).data( 'flag-html' ) ); // phpcs:ignore WordPressVIPMinimum.JS.HTMLExecutingFunctions.prepend
wrapper.children( 'img' ).addClass( 'ui-icon' );
// `wrapper` and `ul` are safe, see above.
return li.append( wrapper ).appendTo( ul ); // phpcs:ignore WordPressVIPMinimum.JS.HTMLExecutingFunctions.append, WordPressVIPMinimum.JS.HTMLExecutingFunctions.appendTo
};
// Override selected item since jQuery UI 1.12 which introduces extension point method _renderButtonItem.
// @see https://api.jqueryui.com/1.12/selectmenu/#method-_renderButtonItem _renderButtonItem documentation.
var selectmenuRenderButtonItem = function ( selectElement ) {
var buttonItem = $( '<span>' );
this._setText( buttonItem, selectElement.label );
this._addClass( buttonItem, "ui-selectmenu-text" );
// Add the flag from the data attribute in the selected element.
// The data to prepend comes from a `data-html-flag` HTML attribute, filled by a method from `PLL_Language`.
buttonItem.prepend( $( selectElement.element ).data( 'flag-html' ) ); // phpcs:ignore WordPressVIPMinimum.JS.HTMLExecutingFunctions.prepend
buttonItem.children( 'img' ).addClass( 'ui-icon' );
return buttonItem;
}
/**
* Initialize a jQuery UI selectmenu widget on a DOM element
*
* @param {*} element - The jQuery object representing the DOM element to attach the widget with.
* @param {*} config - All the parameters - options and callbacks - necessary to configure the jQuery UI selectmenu widget.
* @return {Object} - The jQuery UI selectmenu widget object instance.
*/
function initializeSelectmenuWidget( element, config ) {
// Create the jQuery UI selectmenu widget for flags list dropdown and return its instance.
var selectmenuWidgetInstance = element.selectmenu( config ).selectmenu( 'instance' );
// Overrides each item in the jQuery UI selectmenu list by injecting flag image.
selectmenuWidgetInstance._renderItem = selectmenuRenderItem;
// Override the selected item rendering.
selectmenuWidgetInstance._renderButtonItem = selectmenuRenderButtonItem;
// Need to refresh to take in account the new button item rendering method after the selectmenu widget instanciaion.
selectmenuWidgetInstance.refresh();
return selectmenuWidgetInstance
}
/**
* Selectmenu widget common parameters for its configuration: options and callbacks.
*/
// Selectmenu widget options
var selectmenuOptions = {
width: defaultSelectmenuWidth,
classes: {
'ui-selectmenu-menu': 'pll-selectmenu-menu',
'ui-selectmenu-button': 'pll-selectmenu-button',
}
};
// Selectmenu widget callbacks
var selectmenuFlagListCallbacks = {};
/**
* Overrides the flag dropdown list with our customized jquery ui selectmenu.
*/
// Callbacks when Selectmenu widget change or open event is triggered.
// Needed to correctly refresh the selected element in the list when editing an existing language or when the value change is triggered by the language choice.
var changeOpenCallback = function ( event, ui ) {
// Just a refresh of the menu is needed with jQuery UI 1.12 because _renderButtonItem is triggered and then inject correctly the flag.
$( event.target ).selectmenu( 'refresh' );
}
selectmenuFlagListCallbacks =
{
change: changeOpenCallback,
open: changeOpenCallback,
};
// Create the selectmenu widget only if the field is present.
if ( flagListExist ) {
// Create the jQuery UI selectmenu widget for flags list dropdown and return its instance.
var selectmenuFlagList = initializeSelectmenuWidget( $( '#flag_list' ), Object.assign( {}, selectmenuOptions, selectmenuFlagListCallbacks ) );
$( '#lang_list' ).on(
'languageChanged',
function ( event, flag ) {
// Refresh the flag field
selectmenuFlagList.element.val( flag );
selectmenuFlagList._trigger( 'change' );
}
);
}
/**
* Language choice in predefined languages in Polylang Languages settings page and wizard.
* Overrides the predefined language dropdown list with our customized jQuery ui selectmenu widget.
*/
/**
* Fill the other language form fields from the language element selected in the language list dropdown.
*
* @param {Object} language - language object of the selected element in the language list dropdown.
*/
function fillLanguageFields( language ) {
$( '#lang_slug' ).val( language.slug );
$( '#lang_locale' ).val( language.locale );
$( 'input[name="rtl"]' ).val( language.rtl );
$( '#lang_name' ).val( language.name );
}
/**
* Parse selected language element in the language list dropdown.
*
* @param {object} event - jQuery triggered event.
* @return {object} The language object with its named properties.
*/
function parseSelectedLanguage( event ) {
var selectedElement = $('option:selected', event.target);
var values = selectedElement.val().split(':')
return {
slug: values[0],
locale: values[1],
rtl: [values[2]],
flag: values[3],
name: selectedElement.text().split(' - ')[0] // At the moment there is no need of the 2nd part because it corresponds on the locale which is already known by splitting the selected element value
};
}
// Callback when selectmenu widget change event is triggered.
var changeCallback = function ( event, ui ) {
var language = parseSelectedLanguage( event );
fillLanguageFields( language );
$( event.target ).trigger( 'languageChanged', language.flag );
};
// Create the jQuery UI selectmenu widget languages list dropdown and return its instance.
var selectmenuLangListCallbacks = {};
// For the wizard we need a 100% width. So we override the previous defined value of selectmenuOptions.
if ( $( '#lang_list' ).closest( '.pll-wizard-content' ).length > 0 ) {
selectmenuOptions = Object.assign( selectmenuOptions, { width: wizardSelectmenuWidth } );
}
selectmenuLangListCallbacks = {
change: changeCallback,
};
if ( langListExist ) {
initializeSelectmenuWidget( $( '#lang_list' ), Object.assign( {}, selectmenuOptions, selectmenuLangListCallbacks ) );
}
// strings translations
// save translations when pressing enter
$( '.translation input' ).on(
'keydown',
function ( event ) {
if ( 'Enter' === event.key ) {
event.preventDefault();
$( '#submit' ).trigger( 'click' );
}
}
);
// settings page
// click on configure link
$( '#the-list' ).on(
'click',
'.configure>a',
function () {
$( '.pll-configure' ).hide().prev().show();
$( this ).closest( 'tr' ).hide().next().show();
return false;
}
);
// cancel
$( '#the-list' ).on(
'click',
'.cancel',
function () {
$( this ).closest( 'tr' ).hide().prev().show();
}
);
// save settings
$( '#the-list' ).on(
'click',
'.save',
function () {
var tr = $( this ).closest( 'tr' );
var parts = tr.attr( 'id' ).split( '-' );
var data = {
action: 'pll_save_options',
pll_ajax_settings: true,
module: parts[parts.length - 1],
_pll_nonce: $( '#_pll_nonce' ).val()
};
data = tr.find( ':input' ).serialize() + '&' + $.param( data );
$.post(
ajaxurl,
data,
function ( response ) {
// Target a non existing WP HTML id to avoid a conflict with WP ajax requests.
var res = wpAjax.parseAjaxResponse( response, 'pll-ajax-response' );
$.each(
res.responses,
function () {
/**
* Fires after saving the settings, before applying changes to the DOM.
*
* @since 3.6.0
*
* @param {Object} response The response from the AJAX call.
* @param {HTMLElement} tr The HTML element containing the fields.
*/
wp.hooks.doAction( 'pll_settings_saved', this, tr.get( 0 ) );
switch ( this.what ) {
case 'license-update':
// Data comes from `PLL_License::get_form_field()`, where everything is escaped.
$( '#pll-license-' + this.data ).replaceWith( this.supplemental.html ); // phpcs:ignore WordPressVIPMinimum.JS.HTMLExecutingFunctions.replaceWith
break;
case 'success':
tr.hide().prev().show(); // close only if there is no error
case 'error':
$( '.settings-error' ).remove(); // remove previous messages if any
// The data comes from `pll_add_notice()`, where message are passed through `wp_kses()`.
$( 'h1' ).after( this.data ); // phpcs:ignore WordPressVIPMinimum.JS.HTMLExecutingFunctions.after
// Make notices dismissible
// copy paste of common.js from WP 4.2.2
$( '.notice.is-dismissible' ).each(
function () {
var $this = $( this ),
$button = $( '<button type="button" class="notice-dismiss"><span class="screen-reader-text"></span></button>' ),
btnText = pll_settings.dismiss_notice || '';
// Ensure plain text
$button.find( '.screen-reader-text' ).text( btnText );
// Whitelist because of how the button is built. See above
$this.append( $button ); // phpcs:ignore WordPressVIPMinimum.JS.HTMLExecutingFunctions.append
$button.on(
'click.wp-dismiss-notice',
function ( event ) {
event.preventDefault();
$this.fadeTo(
100,
0,
function () {
$( this ).slideUp(
100,
function () {
$( this ).remove();
}
);
}
);
}
);
}
);
break;
}
}
);
}
);
}
);
// act when pressing enter or esc in configurations
$( '.pll-configure' ).on(
'keydown',
function ( event ) {
if ( 'Enter' === event.key ) {
event.preventDefault();
$( this ).find( '.save' ).trigger( 'click' );
}
if ( 'Escape' === event.key ) {
event.preventDefault();
$( this ).find( '.cancel' ).trigger( 'click' );
}
}
);
// settings URL modifications
// manages visibility of fields
$( "input[name='force_lang']" ).on(
'change',
function () {
function pll_toggle( a, test ) {
test ? a.show() : a.hide();
}
var value = $( this ).val();
pll_toggle( $( '#pll-domains-table' ), 3 == value );
pll_toggle( $( "#pll-hide-default" ), 3 > value );
pll_toggle( $( "#pll-rewrite" ), 2 > value );
pll_toggle( $( "#pll-redirect-lang" ), 2 > value );
}
);
// settings license
// deactivate button
$( '.pll-deactivate-license' ).on(
'click',
function () {
var data = {
action: 'pll_deactivate_license',
pll_ajax_settings: true,
id: $( this ).attr( 'id' ),
_pll_nonce: $( '#_pll_nonce' ).val()
};
$.post(
ajaxurl,
data,
function ( response ) {
// Data comes from `PLL_License::get_form_field()`, where everything is escaped.
$( '#pll-license-' + response.id ).replaceWith( response.html ); // phpcs:ignore WordPressVIPMinimum.JS.HTMLExecutingFunctions.replaceWith
}
);
}
);
// Manage closing the metabox.
// close postboxes that should be closed
$( '.if-js-closed' ).removeClass( 'if-js-closed' ).addClass( 'closed' );
// postboxes setup
if ( 'undefined' !== typeof postboxes ) {
postboxes.add_postbox_toggles( pagenow );
}
}
);
@@ -0,0 +1 @@
jQuery(function(e){var t;e("table.languages").on({focusin:function(){clearTimeout(t);var n=e(this).find(".row-actions");e(".row-actions").not(this).removeClass("visible"),n.addClass("visible")},focusout:function(){t=setTimeout(function(){focusedRowActions.removeClass("visible")},30)}},"tr");var n=e("#flag_list").length,l=e("#lang_list").length,s=function(t,n){var l=e("<li>"),s=e("<div>");return n.disabled&&this._addClass(l,null,"ui-state-disabled"),this._setText(s,n.label),s.prepend(e(n.element).data("flag-html")),s.children("img").addClass("ui-icon"),l.append(s).appendTo(t)},i=function(t){var n=e("<span>");return this._setText(n,t.label),this._addClass(n,"ui-selectmenu-text"),n.prepend(e(t.element).data("flag-html")),n.children("img").addClass("ui-icon"),n};function a(e,t){var n=e.selectmenu(t).selectmenu("instance");return n._renderItem=s,n._renderButtonItem=i,n.refresh(),n}var c,o={width:"95%",classes:{"ui-selectmenu-menu":"pll-selectmenu-menu","ui-selectmenu-button":"pll-selectmenu-button"}},r=function(t,n){e(t.target).selectmenu("refresh")};if(c={change:r,open:r},n){var u=a(e("#flag_list"),Object.assign({},o,c));e("#lang_list").on("languageChanged",function(e,t){u.element.val(t),u._trigger("change")})}var d;e("#lang_list").closest(".pll-wizard-content").length>0&&(o=Object.assign(o,{width:"100%"})),d={change:function(t,n){var l=function(t){var n=e("option:selected",t.target),l=n.val().split(":");return{slug:l[0],locale:l[1],rtl:[l[2]],flag:l[3],name:n.text().split(" - ")[0]}}(t);!function(t){e("#lang_slug").val(t.slug),e("#lang_locale").val(t.locale),e('input[name="rtl"]').val(t.rtl),e("#lang_name").val(t.name)}(l),e(t.target).trigger("languageChanged",l.flag)}},l&&a(e("#lang_list"),Object.assign({},o,d)),e(".translation input").on("keydown",function(t){"Enter"===t.key&&(t.preventDefault(),e("#submit").trigger("click"))}),e("#the-list").on("click",".configure>a",function(){return e(".pll-configure").hide().prev().show(),e(this).closest("tr").hide().next().show(),!1}),e("#the-list").on("click",".cancel",function(){e(this).closest("tr").hide().prev().show()}),e("#the-list").on("click",".save",function(){var t=e(this).closest("tr"),n=t.attr("id").split("-"),l={action:"pll_save_options",pll_ajax_settings:!0,module:n[n.length-1],_pll_nonce:e("#_pll_nonce").val()};l=t.find(":input").serialize()+"&"+e.param(l),e.post(ajaxurl,l,function(n){var l=wpAjax.parseAjaxResponse(n,"pll-ajax-response");e.each(l.responses,function(){switch(wp.hooks.doAction("pll_settings_saved",this,t.get(0)),this.what){case"license-update":e("#pll-license-"+this.data).replaceWith(this.supplemental.html);break;case"success":t.hide().prev().show();case"error":e(".settings-error").remove(),e("h1").after(this.data),e(".notice.is-dismissible").each(function(){var t=e(this),n=e('<button type="button" class="notice-dismiss"><span class="screen-reader-text"></span></button>'),l=pll_settings.dismiss_notice||"";n.find(".screen-reader-text").text(l),t.append(n),n.on("click.wp-dismiss-notice",function(n){n.preventDefault(),t.fadeTo(100,0,function(){e(this).slideUp(100,function(){e(this).remove()})})})})}})})}),e(".pll-configure").on("keydown",function(t){"Enter"===t.key&&(t.preventDefault(),e(this).find(".save").trigger("click")),"Escape"===t.key&&(t.preventDefault(),e(this).find(".cancel").trigger("click"))}),e("input[name='force_lang']").on("change",function(){function t(e,t){t?e.show():e.hide()}var n=e(this).val();t(e("#pll-domains-table"),3==n),t(e("#pll-hide-default"),3>n),t(e("#pll-rewrite"),2>n),t(e("#pll-redirect-lang"),2>n)}),e(".pll-deactivate-license").on("click",function(){var t={action:"pll_deactivate_license",pll_ajax_settings:!0,id:e(this).attr("id"),_pll_nonce:e("#_pll_nonce").val()};e.post(ajaxurl,t,function(t){e("#pll-license-"+t.id).replaceWith(t.html)})}),e(".if-js-closed").removeClass("if-js-closed").addClass("closed"),"undefined"!=typeof postboxes&&postboxes.add_postbox_toggles(pagenow)});
@@ -0,0 +1,250 @@
/**
* @package Polylang
*/
/**
* Quick edit
*/
jQuery(
function ( $ ) {
const handleQuickEditInsertion = ( mutationsList ) => {
for ( const mutation of mutationsList ) {
const addedNodes = Array.from( mutation.addedNodes ).filter( el => el.nodeType === Node.ELEMENT_NODE )
const form = addedNodes[0];
if ( 0 < mutation.addedNodes.length && form.classList.contains( 'inline-edit-row' ) ) {
// WordPress has inserted the quick edit form.
const term_id = Number( form.id.substring( 5 ) );
if ( term_id > 0 ) {
// Get the language dropdown.
const select = form.querySelector( 'select[name="inline_lang_choice"]' );
const lang = document.querySelector( '#lang_' + String( term_id ) ).innerHTML;
select.value = lang; // Populates the dropdown with the post language.
// Disable the language dropdown for default categories.
const default_cat = document.querySelector( `#default_cat_${term_id}` )?.innerHTML;
if ( term_id == default_cat ) {
select.disabled = true;
}
}
}
}
}
const table = document.getElementById( 'the-list' );
if ( null !== table ) {
// Ensure the table is displayed before listening to any change.
const config = { childList: true, subtree: true };
const observer = new MutationObserver( handleQuickEditInsertion );
observer.observe( table, config);
}
}
);
/**
* Update rows of translated terms when adding / deleting a translation or when the language is modified in quick edit.
* Acts on ajaxSuccess event.
*/
jQuery(
function ( $ ) {
$( document ).ajaxSuccess(
function ( event, xhr, settings ) {
function update_rows( term_id ) {
// collect old translations
var translations = new Array();
$( '.translation_' + term_id ).each(
function () {
translations.push( $( this ).parent().parent().attr( 'id' ).substring( 4 ) );
}
);
var data = {
action: 'pll_update_term_rows',
term_id: term_id,
translations: translations.join( ',' ),
taxonomy: $( "input[name='taxonomy']" ).val(),
post_type: $( "input[name='post_type']" ).val(),
screen: $( "input[name='screen']" ).val(),
_pll_nonce: $( '#_pll_nonce' ).val()
};
// get the modified rows in ajax and update them
$.post(
ajaxurl,
data,
function ( response ) {
if ( response ) {
// Target a non existing WP HTML id to avoid a conflict with WP ajax requests.
var res = wpAjax.parseAjaxResponse( response, 'pll-ajax-response' );
$.each(
res.responses,
function () {
if ( 'row' == this.what ) {
// data is built with a call to WP_Terms_List_Table::single_row method
// which uses internally other WordPress methods which escape correctly values.
// For Polylang language columns the HTML code is correctly escaped in PLL_Admin_Filters_Columns::term_column method.
$( "#tag-" + this.supplemental.term_id ).replaceWith( this.data ); // phpcs:ignore WordPressVIPMinimum.JS.HTMLExecutingFunctions.replaceWith
}
}
);
}
}
);
}
var data = wpAjax.unserialize( settings.data ); // what were the data sent by the ajax request?
if ( 'undefined' != typeof( data['action'] ) ) {
switch ( data['action'] ) {
// when adding a term, the new term_id is in the ajax response
case 'add-tag':
// Target a non existing WP HTML id to avoid a conflict with WP ajax requests.
res = wpAjax.parseAjaxResponse( xhr.responseXML, 'pll-ajax-response' );
$.each(
res.responses,
function () {
if ( 'term' == this.what ) {
update_rows( this.supplemental.term_id );
}
}
);
// and also reset translations hidden input fields
$( '.htr_lang' ).val( 0 );
break;
// when deleting a term
case 'delete-tag':
update_rows( data['tag_ID'] );
break;
// in case the language is modified in quick edit and breaks translations
case 'inline-save-tax':
update_rows( data['tax_ID'] );
break;
}
}
}
);
}
);
jQuery(
function ( $ ) {
// translations autocomplete input box
function init_translations() {
$( '.tr_lang' ).each(
function () {
var tr_lang = $( this ).attr( 'id' ).substring( 8 );
var td = $( this ).parent().parent().siblings( '.pll-edit-column' );
$( this ).autocomplete(
{
minLength: 0,
source: ajaxurl + '?action=pll_terms_not_translated' +
'&term_language=' + $( '#term_lang_choice' ).val() +
'&term_id=' + $( "input[name='tag_ID']" ).val() +
'&taxonomy=' + $( "input[name='taxonomy']" ).val() +
'&translation_language=' + tr_lang +
'&post_type=' + typenow +
'&_pll_nonce=' + $( '#_pll_nonce' ).val(),
select: function ( event, ui ) {
$( '#htr_lang_' + tr_lang ).val( ui.item.id );
// ui.item.link is built and come from server side and is well escaped when necessary
td.html( ui.item.link ); // phpcs:ignore WordPressVIPMinimum.JS.HTMLExecutingFunctions.html
},
}
);
// when the input box is emptied
$( this ).on(
'blur',
function () {
if ( ! $( this ).val() ) {
$( '#htr_lang_' + tr_lang ).val( 0 );
// Value is retrieved from HTML already generated server side
td.html( td.siblings( '.hidden' ).children().clone() ); // phpcs:ignore WordPressVIPMinimum.JS.HTMLExecutingFunctions.html
}
}
);
}
);
}
init_translations();
// ajax for changing the term's language
$( '#term_lang_choice' ).on(
'change',
function () {
var value = $( this ).val();
// The selected option in the dropdown list.
const selectedOption = event.target;
var data = {
action: 'term_lang_choice',
lang: value,
from_tag: $( "input[name='from_tag']" ).val(),
term_id: $( "input[name='tag_ID']" ).val(),
taxonomy: $( "input[name='taxonomy']" ).val(),
post_type: typenow,
_pll_nonce: $( '#_pll_nonce' ).val()
};
$.post(
ajaxurl,
data,
function ( response ) {
// Target a non existing WP HTML id to avoid a conflict with WP ajax requests.
var res = wpAjax.parseAjaxResponse( response, 'pll-ajax-response' );
$.each(
res.responses,
function () {
switch ( this.what ) {
case 'translations': // translations fields
// Data is built and come from server side and is well escaped when necessary
$( "#term-translations" ).html( this.data ); // phpcs:ignore WordPressVIPMinimum.JS.HTMLExecutingFunctions.html
init_translations();
break;
case 'parent': // parent dropdown list for hierarchical taxonomies
// data correctly escaped in PLL_Admin_Filters_Term::term_lang_choice method which uses wp_dropdown_categories function.
$( '#parent' ).replaceWith( this.data ); // phpcs:ignore WordPressVIPMinimum.JS.HTMLExecutingFunctions.replaceWith
break;
case 'tag_cloud': // popular items
// data correctly escaped in PLL_Admin_Filters_Term::term_lang_choice method which uses wp_tag_cloud and wp_generate_tag_cloud functions.
$( '.tagcloud' ).replaceWith( this.data ); // phpcs:ignore WordPressVIPMinimum.JS.HTMLExecutingFunctions.replaceWith
break;
case 'flag': // flag in front of the select dropdown
// Data is built and come from server side and is well escaped when necessary
$( '.pll-select-flag' ).html( this.data ); // phpcs:ignore WordPressVIPMinimum.JS.HTMLExecutingFunctions.html
break;
}
}
);
// Creates an event once the language has been successfully changed.
const onTermLangChoice = new CustomEvent(
"onTermLangChoice",
{
detail: {
lang: JSON.parse( selectedOption.options[selectedOption.options.selectedIndex].getAttribute( 'data-lang' ) )
},
}
);
document.dispatchEvent( onTermLangChoice );
}
);
}
);
// Listen to `onTermLangChoice` to perform actions after the language has been changed.
document.addEventListener(
'onTermLangChoice',
( e ) => {
// Modifies the text direction.
let dir = e.detail.lang.is_rtl ? 'rtl' : 'ltr'
$( 'body' ).removeClass( 'pll-dir-rtl' ).removeClass( 'pll-dir-ltr' ).addClass( 'pll-dir-' + dir );
}
);
}
);
@@ -0,0 +1 @@
jQuery(function(t){const e=t=>{for(const e of t){const t=Array.from(e.addedNodes).filter(t=>t.nodeType===Node.ELEMENT_NODE)[0];if(0<e.addedNodes.length&&t.classList.contains("inline-edit-row")){const e=Number(t.id.substring(5));if(e>0){const a=t.querySelector('select[name="inline_lang_choice"]'),n=document.querySelector("#lang_"+String(e)).innerHTML;a.value=n;const l=document.querySelector(`#default_cat_${e}`)?.innerHTML;e==l&&(a.disabled=!0)}}}},a=document.getElementById("the-list");if(null!==a){const t={childList:!0,subtree:!0};new MutationObserver(e).observe(a,t)}}),jQuery(function(t){t(document).ajaxSuccess(function(e,a,n){function l(e){var a=new Array;t(".translation_"+e).each(function(){a.push(t(this).parent().parent().attr("id").substring(4))});var n={action:"pll_update_term_rows",term_id:e,translations:a.join(","),taxonomy:t("input[name='taxonomy']").val(),post_type:t("input[name='post_type']").val(),screen:t("input[name='screen']").val(),_pll_nonce:t("#_pll_nonce").val()};t.post(ajaxurl,n,function(e){if(e){var a=wpAjax.parseAjaxResponse(e,"pll-ajax-response");t.each(a.responses,function(){"row"==this.what&&t("#tag-"+this.supplemental.term_id).replaceWith(this.data)})}})}var s=wpAjax.unserialize(n.data);if(void 0!==s.action)switch(s.action){case"add-tag":res=wpAjax.parseAjaxResponse(a.responseXML,"pll-ajax-response"),t.each(res.responses,function(){"term"==this.what&&l(this.supplemental.term_id)}),t(".htr_lang").val(0);break;case"delete-tag":l(s.tag_ID);break;case"inline-save-tax":l(s.tax_ID)}})}),jQuery(function(t){function e(){t(".tr_lang").each(function(){var e=t(this).attr("id").substring(8),a=t(this).parent().parent().siblings(".pll-edit-column");t(this).autocomplete({minLength:0,source:ajaxurl+"?action=pll_terms_not_translated&term_language="+t("#term_lang_choice").val()+"&term_id="+t("input[name='tag_ID']").val()+"&taxonomy="+t("input[name='taxonomy']").val()+"&translation_language="+e+"&post_type="+typenow+"&_pll_nonce="+t("#_pll_nonce").val(),select:function(n,l){t("#htr_lang_"+e).val(l.item.id),a.html(l.item.link)}}),t(this).on("blur",function(){t(this).val()||(t("#htr_lang_"+e).val(0),a.html(a.siblings(".hidden").children().clone()))})})}e(),t("#term_lang_choice").on("change",function(){var a=t(this).val();const n=event.target;var l={action:"term_lang_choice",lang:a,from_tag:t("input[name='from_tag']").val(),term_id:t("input[name='tag_ID']").val(),taxonomy:t("input[name='taxonomy']").val(),post_type:typenow,_pll_nonce:t("#_pll_nonce").val()};t.post(ajaxurl,l,function(a){var l=wpAjax.parseAjaxResponse(a,"pll-ajax-response");t.each(l.responses,function(){switch(this.what){case"translations":t("#term-translations").html(this.data),e();break;case"parent":t("#parent").replaceWith(this.data);break;case"tag_cloud":t(".tagcloud").replaceWith(this.data);break;case"flag":t(".pll-select-flag").html(this.data)}});const s=new CustomEvent("onTermLangChoice",{detail:{lang:JSON.parse(n.options[n.options.selectedIndex].getAttribute("data-lang"))}});document.dispatchEvent(s)})}),document.addEventListener("onTermLangChoice",e=>{let a=e.detail.lang.is_rtl?"rtl":"ltr";t("body").removeClass("pll-dir-rtl").removeClass("pll-dir-ltr").addClass("pll-dir-"+a)})});
@@ -0,0 +1,34 @@
/**
* Adds one biography input field per language in the user profile.
*
* @package Polylang
*/
jQuery(
function ( $ ) {
// biography
// FIXME there is probably a more efficient way to do this
var td = $( '#description' ).parent();
var d = $( '#description' ).clone();
var span = td.children( '.description' ).clone();
td.children().remove();
$( '.biography' ).each(
function () {
lang = $( this ).attr( 'name' ).split( '___' );
desc = d.clone();
desc.attr( 'name', 'description_' + lang[0] );
desc.attr( 'id', 'description_' + lang[0] );
// Whitelist because description and lang value is already escaped by the side of PHP
desc.html( $( this ).val() ); // phpcs:ignore WordPressVIPMinimum.JS.HTMLExecutingFunctions.html
td.append( $( '<div></div>' ).text( lang[1] ) ); // phpcs:ignore WordPressVIPMinimum.JS.HTMLExecutingFunctions.append
td.append( desc ); // phpcs:ignore WordPressVIPMinimum.JS.HTMLExecutingFunctions.append
}
);
td.append( '<br />' );
// Whitelist because description come from html code generated by WordPress
td.append( span ); // phpcs:ignore WordPressVIPMinimum.JS.HTMLExecutingFunctions.append
}
);
@@ -0,0 +1 @@
jQuery(function(e){var n=e("#description").parent(),i=e("#description").clone(),t=n.children(".description").clone();n.children().remove(),e(".biography").each(function(){lang=e(this).attr("name").split("___"),desc=i.clone(),desc.attr("name","description_"+lang[0]),desc.attr("id","description_"+lang[0]),desc.html(e(this).val()),n.append(e("<div></div>").text(lang[1])),n.append(desc)}),n.append("<br />"),n.append(t)});
@@ -0,0 +1,152 @@
/**
* Adds a flag to the widgets filtered by a language.
*
* @package Polylang
*/
jQuery(
function ( $ ) {
var widgets_container,
widgets_selector,
flags,
isBlockEditor = 'undefined' !== typeof wp.blockEditor;
if ( 'undefined' !== typeof pll_widgets && pll_widgets.hasOwnProperty( 'flags' ) ) {
flags = pll_widgets.flags;
}
/**
* Prepend widget titles with a flag once a language is selected.
*
* @param {object} widget The widget element.
* @return {void} Nothing.
*/
function add_flag( widget ) {
if ( ! flags ) {
return;
}
widget = $( widget );
var title = isBlockEditor ? widget.prev('h3') : $( '.widget-top .widget-title h3', widget ),
locale = $( '.pll-lang-choice option:selected', widget ).val(),
// Icon is HTML built and come from server side and is well escaped when necessary
icon = ( locale && flags.hasOwnProperty( locale ) ) ? flags[ locale ] : null;
if ( icon ) {
icon += ' &nbsp; ';
var current = $( '.pll-lang', title );
if ( current.length ) {
current.html( icon ); // phpcs:ignore WordPressVIPMinimum.JS.HTMLExecutingFunctions.html
} else {
flag = $( '<span />' ).addClass( 'pll-lang' ).html( icon ); // phpcs:ignore WordPressVIPMinimum.JS.HTMLExecutingFunctions.html
// See the comment above about the icon which is safe. So it is also safe to prepend flag which uses icon.
title.prepend( flag ); // phpcs:ignore WordPressVIPMinimum.JS.HTMLExecutingFunctions.prepend
}
} else {
$( '.pll-lang', title ).remove();
}
}
if ( isBlockEditor ) {
widgets_container = $( '.edit-widgets-main-block-list' );
widgets_selector = '.widget';
// Update flags when we click on the legacy widget to display its form.
widgets_container.on(
'click',
'.wp-block-legacy-widget',
function () {
add_flag( $( this ).find( '.widget' ) );
}
);
} else {
if ( 'undefined' !== typeof wp.customize ) {
widgets_container = $( '#customize-controls' );
widgets_selector = '.customize-control .widget';
/**
* WP Customizer add control listener.
*
* @link https://wordpress.stackexchange.com/questions/256536/callback-after-wordpress-customizer-complete-loading
*
* @param {object} control The control type.
* @return {void} Nothing.
*/
function customize_add_flag( control ) {
if ( ! control.extended( wp.customize.Widgets.WidgetControl ) ) {
return;
}
/*
* Make sure the widget's contents are embedded; normally this is done
* when the control is expanded, for DOM performance reasons.
*/
control.embedWidgetContent();
// Now we know for sure the widget is fully embedded.
add_flag( control.container.find( '.widget' ) );
}
wp.customize.control.each( customize_add_flag );
wp.customize.control.bind( 'add', customize_add_flag );
} else {
widgets_container = $( '#widgets-right' );
widgets_selector = '.widget';
}
// Add flags on load.
$( widgets_selector, widgets_container ).each(
function () {
add_flag( this );
}
);
}
// Update flags.
widgets_container.on(
'change',
'.pll-lang-choice',
function () {
add_flag( $( this ).parents( '.widget' ) );
}
);
function pll_toggle( a, test ) {
test ? a.show() : a.hide();
}
// Remove all options if dropdown is checked.
$( '.widgets-sortables,.control-section-sidebar,.edit-widgets-main-block-list' ).on(
'change',
'.pll-dropdown',
function () {
var this_id = $( this ).parent().parent().parent().children( '.widget-id' ).attr( 'value' );
pll_toggle( $( '.no-dropdown-' + this_id ), true != $( this ).prop( 'checked' ) );
}
);
// Disallow unchecking both show names and show flags.
var options = ['-show_flags', '-show_names'];
$.each(
options,
function ( i, v ) {
$( '.widgets-sortables,.control-section-sidebar,.edit-widgets-main-block-list' ).on(
'change',
'.pll' + v,
function () {
var this_id = $( this ).parent().parent().parent().children( '.widget-id' ).attr( 'value' );
if ( true != $( this ).prop( 'checked' ) ) {
$( '#widget-' + this_id + options[ 1 - i ] ).prop( 'checked', true );
}
}
);
}
);
}
);
@@ -0,0 +1 @@
jQuery(function(e){var t,i,n,o=void 0!==wp.blockEditor;function l(t){if(n){t=e(t);var i=o?t.prev("h3"):e(".widget-top .widget-title h3",t),l=e(".pll-lang-choice option:selected",t).val(),d=l&&n.hasOwnProperty(l)?n[l]:null;if(d){d+=" &nbsp; ";var s=e(".pll-lang",i);s.length?s.html(d):(flag=e("<span />").addClass("pll-lang").html(d),i.prepend(flag))}else e(".pll-lang",i).remove()}}if("undefined"!=typeof pll_widgets&&pll_widgets.hasOwnProperty("flags")&&(n=pll_widgets.flags),o)i=".widget",(t=e(".edit-widgets-main-block-list")).on("click",".wp-block-legacy-widget",function(){l(e(this).find(".widget"))});else{if(void 0!==wp.customize){function d(e){e.extended(wp.customize.Widgets.WidgetControl)&&(e.embedWidgetContent(),l(e.container.find(".widget")))}t=e("#customize-controls"),i=".customize-control .widget",wp.customize.control.each(d),wp.customize.control.bind("add",d)}else t=e("#widgets-right"),i=".widget";e(i,t).each(function(){l(this)})}t.on("change",".pll-lang-choice",function(){l(e(this).parents(".widget"))}),e(".widgets-sortables,.control-section-sidebar,.edit-widgets-main-block-list").on("change",".pll-dropdown",function(){var t,i=e(this).parent().parent().parent().children(".widget-id").attr("value");t=e(".no-dropdown-"+i),1!=e(this).prop("checked")?t.show():t.hide()});var s=["-show_flags","-show_names"];e.each(s,function(t,i){e(".widgets-sortables,.control-section-sidebar,.edit-widgets-main-block-list").on("change",".pll"+i,function(){var i=e(this).parent().parent().parent().children(".widget-id").attr("value");1!=e(this).prop("checked")&&e("#widget-"+i+s[1-t]).prop("checked",!0)})})});