| @@ -5,33 +5,40 @@ | ||
| 5 | 5 | /** |
| 6 | 6 | * Quick edit |
| 7 | 7 | */ |
| 8 | 8 | jQuery( |
| 9 | - function( $ ) { | |
| 10 | - $( document ).on( | |
| 11 | - 'DOMNodeInserted', | |
| 12 | - function( e ) { | |
| 13 | - var t = $( e.target ); | |
| 9 | + function ( $ ) { | |
| 10 | + const handleQuickEditInsertion = ( mutationsList ) => { | |
| 11 | + for ( const mutation of mutationsList ) { | |
| 12 | + const addedNodes = Array.from( mutation.addedNodes ).filter( el => el.nodeType === Node.ELEMENT_NODE ) | |
| 13 | + const form = addedNodes[0]; | |
| 14 | + if ( 0 < mutation.addedNodes.length && form.classList.contains( 'inline-edit-row' ) ) { | |
| 15 | + // WordPress has inserted the quick edit form. | |
| 16 | + const term_id = Number( form.id.substring( 5 ) ); | |
| 14 | 17 | |
| 15 | - // WP inserts the quick edit from | |
| 16 | - if ( 'inline-edit' == t.attr( 'id' ) ) { | |
| 17 | - var term_id = t.prev().attr( 'id' ).replace( "tag-", "" ); | |
| 18 | - | |
| 19 | 18 | if ( term_id > 0 ) { |
| 20 | - // language dropdown | |
| 21 | - var select = t.find( ':input[name="inline_lang_choice"]' ); | |
| 22 | - var lang = $( '#lang_' + term_id ).html(); | |
| 23 | - select.val( lang ); // populates the dropdown | |
| 19 | + // Get the language dropdown. | |
| 20 | + const select = form.querySelector( 'select[name="inline_lang_choice"]' ); | |
| 21 | + const lang = document.querySelector( '#lang_' + String( term_id ) ).innerHTML; | |
| 22 | + select.value = lang; // Populates the dropdown with the post language. | |
| 24 | 23 | |
| 25 | - // disable the language dropdown for default categories | |
| 26 | - var default_cat = $( '#default_cat_' + term_id ).html(); | |
| 24 | + // Disable the language dropdown for default categories. | |
| 25 | + const default_cat = document.querySelector( `#default_cat_${term_id}` )?.innerHTML; | |
| 27 | 26 | if ( term_id == default_cat ) { |
| 28 | - select.prop( 'disabled', true ); | |
| 27 | + select.disabled = true; | |
| 29 | 28 | } |
| 30 | 29 | } |
| 31 | 30 | } |
| 32 | 31 | } |
| 33 | - ); | |
| 32 | + } | |
| 33 | + const table = document.getElementById( 'the-list' ); | |
| 34 | + if ( null !== table ) { | |
| 35 | + // Ensure the table is displayed before listening to any change. | |
| 36 | + const config = { childList: true, subtree: true }; | |
| 37 | + const observer = new MutationObserver( handleQuickEditInsertion ); | |
| 38 | + | |
| 39 | + observer.observe( table, config); | |
| 40 | + } | |
| 34 | 41 | } |
| 35 | 42 | ); |
| 36 | 43 | |
| 37 | 44 | /** |
| @@ -38,16 +45,16 @@ | ||
| 38 | 45 | * Update rows of translated terms when adding / deleting a translation or when the language is modified in quick edit. |
| 39 | 46 | * Acts on ajaxSuccess event. |
| 40 | 47 | */ |
| 41 | 48 | jQuery( |
| 42 | - function( $ ) { | |
| 49 | + function ( $ ) { | |
| 43 | 50 | $( document ).ajaxSuccess( |
| 44 | - function( event, xhr, settings ) { | |
| 51 | + function ( event, xhr, settings ) { | |
| 45 | 52 | function update_rows( term_id ) { |
| 46 | 53 | // collect old translations |
| 47 | 54 | var translations = new Array(); |
| 48 | 55 | $( '.translation_' + term_id ).each( |
| 49 | - function() { | |
| 56 | + function () { | |
| 50 | 57 | translations.push( $( this ).parent().parent().attr( 'id' ).substring( 4 ) ); |
| 51 | 58 | } |
| 52 | 59 | ); |
| 53 | 60 | |
| @@ -64,19 +71,20 @@ | ||
| 64 | 71 | // get the modified rows in ajax and update them |
| 65 | 72 | $.post( |
| 66 | 73 | ajaxurl, |
| 67 | 74 | data, |
| 68 | - function( response ) { | |
| 75 | + function ( response ) { | |
| 69 | 76 | if ( response ) { |
| 70 | - var res = wpAjax.parseAjaxResponse( response, 'ajax-response' ); | |
| 77 | + // Target a non existing WP HTML id to avoid a conflict with WP ajax requests. | |
| 78 | + var res = wpAjax.parseAjaxResponse( response, 'pll-ajax-response' ); | |
| 71 | 79 | $.each( |
| 72 | 80 | res.responses, |
| 73 | - function() { | |
| 81 | + function () { | |
| 74 | 82 | if ( 'row' == this.what ) { |
| 75 | 83 | // data is built with a call to WP_Terms_List_Table::single_row method |
| 76 | 84 | // which uses internally other WordPress methods which escape correctly values. |
| 77 | 85 | // For Polylang language columns the HTML code is correctly escaped in PLL_Admin_Filters_Columns::term_column method. |
| 78 | - $( "#tag-" + this.supplemental.term_id ).replaceWith( this.data ); // phpcs:ignore WordPressVIPMinimum.JS.HTMLExecutingFunctions.replaceWith | |
| 86 | + $( "#tag-" + this.supplemental.term_id ).replaceWith( this.data ); | |
| 79 | 87 | } |
| 80 | 88 | } |
| 81 | 89 | ); |
| 82 | 90 | } |
| @@ -88,12 +96,13 @@ | ||
| 88 | 96 | if ( 'undefined' != typeof( data['action'] ) ) { |
| 89 | 97 | switch ( data['action'] ) { |
| 90 | 98 | // when adding a term, the new term_id is in the ajax response |
| 91 | 99 | case 'add-tag': |
| 92 | - res = wpAjax.parseAjaxResponse( xhr.responseXML, 'ajax-response' ); | |
| 100 | + // Target a non existing WP HTML id to avoid a conflict with WP ajax requests. | |
| 101 | + res = wpAjax.parseAjaxResponse( xhr.responseXML, 'pll-ajax-response' ); | |
| 93 | 102 | $.each( |
| 94 | 103 | res.responses, |
| 95 | - function() { | |
| 104 | + function () { | |
| 96 | 105 | if ( 'term' == this.what ) { |
| 97 | 106 | update_rows( this.supplemental.term_id ); |
| 98 | 107 | } |
| 99 | 108 | } |
| @@ -119,13 +128,13 @@ | ||
| 119 | 128 | } |
| 120 | 129 | ); |
| 121 | 130 | |
| 122 | 131 | jQuery( |
| 123 | - function( $ ) { | |
| 132 | + function ( $ ) { | |
| 124 | 133 | // translations autocomplete input box |
| 125 | 134 | function init_translations() { |
| 126 | 135 | $( '.tr_lang' ).each( |
| 127 | - function(){ | |
| 136 | + function () { | |
| 128 | 137 | var tr_lang = $( this ).attr( 'id' ).substring( 8 ); |
| 129 | 138 | var td = $( this ).parent().parent().siblings( '.pll-edit-column' ); |
| 130 | 139 | |
| 131 | 140 | $( this ).autocomplete( |
| @@ -137,12 +146,12 @@ | ||
| 137 | 146 | '&taxonomy=' + $( "input[name='taxonomy']" ).val() + |
| 138 | 147 | '&translation_language=' + tr_lang + |
| 139 | 148 | '&post_type=' + typenow + |
| 140 | 149 | '&_pll_nonce=' + $( '#_pll_nonce' ).val(), |
| 141 | - select: function( event, ui ) { | |
| 150 | + select: function ( event, ui ) { | |
| 142 | 151 | $( '#htr_lang_' + tr_lang ).val( ui.item.id ); |
| 143 | 152 | // ui.item.link is built and come from server side and is well escaped when necessary |
| 144 | - td.html( ui.item.link ); // phpcs:ignore WordPressVIPMinimum.JS.HTMLExecutingFunctions.html | |
| 153 | + td.html( ui.item.link ); | |
| 145 | 154 | }, |
| 146 | 155 | } |
| 147 | 156 | ); |
| 148 | 157 | |
| @@ -148,13 +157,13 @@ | ||
| 148 | 157 | |
| 149 | 158 | // when the input box is emptied |
| 150 | 159 | $( this ).on( |
| 151 | 160 | 'blur', |
| 152 | - function() { | |
| 161 | + function () { | |
| 153 | 162 | if ( ! $( this ).val() ) { |
| 154 | 163 | $( '#htr_lang_' + tr_lang ).val( 0 ); |
| 155 | 164 | // Value is retrieved from HTML already generated server side |
| 156 | - td.html( td.siblings( '.hidden' ).children().clone() ); // phpcs:ignore WordPressVIPMinimum.JS.HTMLExecutingFunctions.html | |
| 165 | + td.html( td.siblings( '.hidden' ).children().clone() ); | |
| 157 | 166 | } |
| 158 | 167 | } |
| 159 | 168 | ); |
| 160 | 169 | } |
| @@ -165,12 +174,12 @@ | ||
| 165 | 174 | |
| 166 | 175 | // ajax for changing the term's language |
| 167 | 176 | $( '#term_lang_choice' ).on( |
| 168 | 177 | 'change', |
| 169 | - function() { | |
| 178 | + function () { | |
| 170 | 179 | var value = $( this ).val(); |
| 171 | - var lang = $( this ).children( 'option[value="' + value + '"]' ).attr( 'lang' ); | |
| 172 | - var dir = $( '.pll-translation-column > span[lang="' + lang + '"]' ).attr( 'dir' ); | |
| 180 | + // The selected option in the dropdown list. | |
| 181 | + const selectedOption = event.target; | |
| 173 | 182 | |
| 174 | 183 | var data = { |
| 175 | 184 | action: 'term_lang_choice', |
| 176 | 185 | lang: value, |
| @@ -183,39 +192,58 @@ | ||
| 183 | 192 | |
| 184 | 193 | $.post( |
| 185 | 194 | ajaxurl, |
| 186 | 195 | data, |
| 187 | - function( response ) { | |
| 188 | - var res = wpAjax.parseAjaxResponse( response, 'ajax-response' ); | |
| 196 | + function ( response ) { | |
| 197 | + // Target a non existing WP HTML id to avoid a conflict with WP ajax requests. | |
| 198 | + var res = wpAjax.parseAjaxResponse( response, 'pll-ajax-response' ); | |
| 189 | 199 | $.each( |
| 190 | 200 | res.responses, |
| 191 | - function() { | |
| 201 | + function () { | |
| 192 | 202 | switch ( this.what ) { |
| 193 | 203 | case 'translations': // translations fields |
| 194 | 204 | // Data is built and come from server side and is well escaped when necessary |
| 195 | - $( "#term-translations" ).html( this.data ); // phpcs:ignore WordPressVIPMinimum.JS.HTMLExecutingFunctions.html | |
| 205 | + $( "#term-translations" ).html( this.data ); | |
| 196 | 206 | init_translations(); |
| 197 | 207 | break; |
| 198 | 208 | case 'parent': // parent dropdown list for hierarchical taxonomies |
| 199 | 209 | // data correctly escaped in PLL_Admin_Filters_Term::term_lang_choice method which uses wp_dropdown_categories function. |
| 200 | - $( '#parent' ).replaceWith( this.data ); // phpcs:ignore WordPressVIPMinimum.JS.HTMLExecutingFunctions.replaceWith | |
| 210 | + $( '#parent' ).replaceWith( this.data ); | |
| 201 | 211 | break; |
| 202 | 212 | case 'tag_cloud': // popular items |
| 203 | 213 | // data correctly escaped in PLL_Admin_Filters_Term::term_lang_choice method which uses wp_tag_cloud and wp_generate_tag_cloud functions. |
| 204 | - $( '.tagcloud' ).replaceWith( this.data ); // phpcs:ignore WordPressVIPMinimum.JS.HTMLExecutingFunctions.replaceWith | |
| 214 | + $( '.tagcloud' ).replaceWith( this.data ); | |
| 205 | 215 | break; |
| 206 | 216 | case 'flag': // flag in front of the select dropdown |
| 207 | 217 | // Data is built and come from server side and is well escaped when necessary |
| 208 | - $( '.pll-select-flag' ).html( this.data ); // phpcs:ignore WordPressVIPMinimum.JS.HTMLExecutingFunctions.html | |
| 218 | + $( '.pll-select-flag' ).html( this.data ); | |
| 209 | 219 | break; |
| 210 | 220 | } |
| 211 | 221 | } |
| 212 | 222 | ); |
| 213 | 223 | |
| 214 | - // Modifies the text direction | |
| 215 | - $( 'body' ).removeClass( 'pll-dir-rtl' ).removeClass( 'pll-dir-ltr' ).addClass( 'pll-dir-' + dir ); | |
| 224 | + // Creates an event once the language has been successfully changed. | |
| 225 | + const onTermLangChoice = new CustomEvent( | |
| 226 | + "onTermLangChoice", | |
| 227 | + { | |
| 228 | + detail: { | |
| 229 | + lang: JSON.parse( selectedOption.options[selectedOption.options.selectedIndex].getAttribute( 'data-lang' ) ) | |
| 230 | + }, | |
| 231 | + } | |
| 232 | + ); | |
| 233 | + document.dispatchEvent( onTermLangChoice ); | |
| 216 | 234 | } |
| 217 | 235 | ); |
| 236 | + } | |
| 237 | + ); | |
| 238 | + | |
| 239 | + // Listen to `onTermLangChoice` to perform actions after the language has been changed. | |
| 240 | + document.addEventListener( | |
| 241 | + 'onTermLangChoice', | |
| 242 | + ( e ) => { | |
| 243 | + // Modifies the text direction. | |
| 244 | + let dir = e.detail.lang.is_rtl ? 'rtl' : 'ltr' | |
| 245 | + $( 'body' ).removeClass( 'pll-dir-rtl' ).removeClass( 'pll-dir-ltr' ).addClass( 'pll-dir-' + dir ); | |
| 218 | 246 | } |
| 219 | 247 | ); |
| 220 | 248 | } |
| 221 | 249 | ); |