PluginProbe
Polylang / 3.8.9
Polylang v3.8.9
3.8.9 3.8.8 3.8.7 3.8.6 3.8.5 3.8.4 3.8.3 2.7 2.7.0.1 2.7.1 2.7.2 2.7.3 2.7.4 2.8 2.8.1 2.8.2 2.8.3 2.8.4 2.9 2.9.1 2.9.2 3.0 3.0.1 3.0.2 3.0.3 All 233 releases
polylang / js / build / classic-editor.js

classic-editor.js in Polylang 3.8.9, at js/build/classic-editor.js

467 lines 15.2 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 /******/ "use strict";
2
3 ;// ./js/src/lib/confirmation-modal.js
4 /**
5 * @package Polylang
6 */
7
8 const languagesList = jQuery( '.post_lang_choice' );
9
10 // Dialog box for alerting the user about a risky changing.
11 const initializeConfirmationModal = () => {
12 // We can't use underscore or lodash in this common code because it depends of the context classic or block editor.
13 // Classic editor underscore is loaded, Block editor lodash is loaded.
14 const { __ } = wp.i18n;
15
16 // Create dialog container.
17 const dialogContainer = jQuery(
18 '<div/>',
19 {
20 id: 'pll-dialog',
21 style: 'display:none;'
22 }
23 ).text( __( 'Are you sure you want to change the language of the current content?', 'polylang' ) );
24
25 // Put it after languages list dropdown.
26 // PHPCS ignore dialogContainer is a new safe HTML code generated above.
27 languagesList.after( dialogContainer ); // phpcs:ignore WordPressVIPMinimum.JS.HTMLExecutingFunctions.after
28
29 const dialogResult = new Promise(
30 ( confirm, cancel ) => {
31 const confirmDialog = ( what ) => { // phpcs:ignore PEAR.Functions.FunctionCallSignature.Indent
32 switch ( what ) { // phpcs:ignore PEAR.Functions.FunctionCallSignature.Indent
33 case 'yes':
34 // Confirm the new language.
35 languagesList.data( 'old-value', languagesList.children( ':selected' ).first().val() );
36 confirm();
37 break;
38 case 'no':
39 // Revert to the old language.
40 languagesList.val( languagesList.data( 'old-value' ) );
41 cancel( 'Cancel' );
42 break;
43 }
44 dialogContainer.dialog( 'close' ); // phpcs:ignore PEAR.Functions.FunctionCallSignature.Indent
45 } // phpcs:ignore PEAR.Functions.FunctionCallSignature.Indent
46
47 // Initialize dialog box in the case a language is selected but not added in the list.
48 const dialogOptions = {
49 autoOpen: false,
50 modal: true,
51 draggable: false,
52 resizable: false,
53 title: __( 'Change language', 'polylang' ),
54 minWidth: 600,
55 maxWidth: '100%',
56 open: function ( event, ui ) {
57 // Change dialog box position for rtl language
58 if ( jQuery( 'body' ).hasClass( 'rtl' ) ) {
59 jQuery( this ).parent().css(
60 {
61 right: jQuery( this ).parent().css( 'left' ),
62 left: 'auto'
63 }
64 );
65 }
66 },
67 close: function ( event, ui ) {
68 // When we're closing the dialog box we need to cancel the language change as we click on Cancel button.
69 confirmDialog( 'no' );
70 },
71 buttons: [
72 {
73 text: __( 'OK', 'polylang' ),
74 click: function ( event ) {
75 confirmDialog( 'yes' );
76 }
77 },
78 {
79 text: __( 'Cancel', 'polylang' ),
80 click: function ( event ) {
81 confirmDialog( 'no' );
82 }
83 }
84 ]
85 };
86
87 if ( jQuery.ui.version >= '1.12.0' ) {
88 Object.assign( dialogOptions, { classes: { 'ui-dialog': 'pll-confirmation-modal' } } );
89 } else {
90 Object.assign( dialogOptions, { dialogClass: 'pll-confirmation-modal' } ); // jQuery UI 1.11.4 - WP < 5.6
91 }
92
93 dialogContainer.dialog( dialogOptions );
94 }
95 );
96 return { dialogContainer, dialogResult };
97 }
98
99 const initializeLanguageOldValue = () => {
100 // Keep the old language value to be able to compare to the new one and revert to it if necessary.
101 languagesList.attr( 'data-old-value', languagesList.children( ':selected' ).first().val() );
102 };
103
104 ;// ./js/src/lib/metabox-autocomplete.js
105 /**
106 * @package Polylang
107 */
108
109 // Translations autocomplete input box.
110 function initMetaboxAutoComplete() {
111 jQuery('.tr_lang').each(
112 function () {
113 var tr_lang = jQuery(this).attr('id').substring(8);
114 var td = jQuery(this).parent().parent().siblings('.pll-edit-column');
115
116 jQuery(this).autocomplete(
117 {
118 minLength: 0,
119 source: ajaxurl + '?action=pll_posts_not_translated' +
120 '&post_language=' + jQuery('.post_lang_choice').val() +
121 '&translation_language=' + tr_lang +
122 '&post_type=' + jQuery('#post_type').val() +
123 '&_pll_nonce=' + jQuery('#_pll_nonce').val(),
124 select: function (event, ui) {
125 jQuery('#htr_lang_' + tr_lang).val(ui.item.id);
126 // ui.item.link is built and come from server side and is well escaped when necessary
127 td.html(ui.item.link); // phpcs:ignore WordPressVIPMinimum.JS.HTMLExecutingFunctions.html
128 },
129 }
130 );
131
132 // when the input box is emptied
133 jQuery(this).on(
134 'blur',
135 function () {
136 if ( ! jQuery(this).val() ) {
137 jQuery('#htr_lang_' + tr_lang).val(0);
138 // Value is retrieved from HTML already generated server side
139 td.html(td.siblings('.hidden').children().clone()); // phpcs:ignore WordPressVIPMinimum.JS.HTMLExecutingFunctions.html
140 }
141 }
142 );
143 }
144 );
145 }
146
147 ;// ./js/src/classic-editor.js
148 /**
149 * @package Polylang
150 */
151
152
153
154
155
156 // tag suggest in metabox
157 jQuery(
158 function ( $ ) {
159 $.ajaxPrefilter(
160 function ( options, originalOptions, jqXHR ) {
161 var lang = $( '.post_lang_choice' ).val();
162 if ( 'string' === typeof options.data && -1 !== options.url.indexOf( 'action=ajax-tag-search' ) && lang ) {
163 options.data = 'lang=' + lang + '&' + options.data;
164 }
165 }
166 );
167 }
168 );
169
170 // overrides tagBox.get
171 jQuery(
172 function ( $ ) {
173 // overrides function to add the language
174 tagBox.get = function ( id ) {
175 var tax = id.substr( id.indexOf( '-' ) + 1 );
176
177 // add the language in the $_POST variable
178 var data = {
179 action: 'get-tagcloud',
180 lang: $( '.post_lang_choice' ).val(),
181 tax: tax
182 }
183
184 $.post(
185 ajaxurl,
186 data,
187 function ( r, stat ) {
188 if ( 0 == r || 'success' != stat ) {
189 r = wpAjax.broken;
190 }
191
192 // @see code from WordPress core https://github.com/WordPress/WordPress/blob/5.2.2/wp-admin/js/tags-box.js#L291
193 // @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
194 r = $( '<div />' ).addClass( 'the-tagcloud' ).attr( 'id', 'tagcloud-' + tax ).html( r ); // phpcs:ignore WordPressVIPMinimum.JS.HTMLExecutingFunctions.html
195 $( 'a', r ).on(
196 'click',
197 function () {
198 tagBox.flushTags( $( this ).closest( '.inside' ).children( '.tagsdiv' ), this );
199 return false;
200 }
201 );
202
203 var tagCloud = $( '#tagcloud-' + tax );
204 // add an if else condition to allow modifying the tags outputted when switching the language
205 var v = tagCloud.css( 'display' );
206 if ( v ) {
207 // See the comment above when r variable is created.
208 $( '#tagcloud-' + tax ).replaceWith( r ); // phpcs:ignore WordPressVIPMinimum.JS.HTMLExecutingFunctions.replaceWith
209 $( '#tagcloud-' + tax ).css( 'display', v );
210 }
211 else {
212 // See the comment above when r variable is created.
213 $( '#' + id ).after( r ); // phpcs:ignore WordPressVIPMinimum.JS.HTMLExecutingFunctions.after
214 }
215 }
216 );
217 }
218 }
219 );
220
221 jQuery(
222 function ( $ ) {
223 // collect taxonomies - code partly copied from WordPress
224 var taxonomies = new Array();
225 $( '.categorydiv' ).each(
226 function () {
227 var this_id = $( this ).attr( 'id' ), taxonomyParts, taxonomy;
228
229 taxonomyParts = this_id.split( '-' );
230 taxonomyParts.shift();
231 taxonomy = taxonomyParts.join( '-' );
232 taxonomies.push( taxonomy ); // store the taxonomy for future use
233
234 // add our hidden field in the new category form - for each hierarchical taxonomy
235 // to set the language when creating a new category
236 // html code inserted come from html code itself.
237 $( '#' + taxonomy + '-add-submit' ).before( // phpcs:ignore WordPressVIPMinimum.JS.HTMLExecutingFunctions.before
238 $( '<input />' ).attr( 'type', 'hidden' )
239 .attr( 'id', taxonomy + '-lang' )
240 .attr( 'name', 'term_lang_choice' )
241 .attr( 'value', $( '.post_lang_choice' ).val() )
242 );
243 }
244 );
245
246 // Initialize current language to be able to compare if it changes.
247 initializeLanguageOldValue();
248
249 // ajax for changing the post's language in the languages metabox
250 $( '.post_lang_choice' ).on(
251 'change',
252 function ( event ) {
253 // Initialize the confirmation dialog box.
254 const confirmationModal = initializeConfirmationModal();
255 const { dialogContainer: dialog } = confirmationModal;
256 let { dialogResult } = confirmationModal;
257 // The selected option in the dropdown list.
258 const selectedOption = event.target;
259
260 if ( $( this ).data( 'old-value' ) !== selectedOption.value && ! isEmptyPost() ) {
261 dialog.dialog( 'open' );
262 } else {
263 dialogResult = Promise.resolve();
264 }
265
266 dialogResult.then(
267 () => {
268 var data = {
269 action: 'post_lang_choice',
270 lang: selectedOption.value,
271 post_type: $( '#post_type' ).val(),
272 taxonomies: taxonomies,
273 post_id: $( '#post_ID' ).val(),
274 _pll_nonce: $( '#_pll_nonce' ).val()
275 }
276
277 $.post(
278 ajaxurl,
279 data,
280 function ( response ) {
281 // Target a non existing WP HTML id to avoid a conflict with WP ajax requests.
282 var res = wpAjax.parseAjaxResponse( response, 'pll-ajax-response' );
283 $.each(
284 res.responses,
285 function () {
286 switch ( this.what ) {
287 case 'translations': // translations fields
288 // Data is built and come from server side and is well escaped when necessary
289 $( '.translations' ).html( this.data ); // phpcs:ignore WordPressVIPMinimum.JS.HTMLExecutingFunctions.html
290 initMetaboxAutoComplete();
291 break;
292 case 'taxonomy': // categories metabox for posts
293 var tax = this.data;
294 // @see wp_terms_checklist https://github.com/WordPress/WordPress/blob/5.2.2/wp-admin/includes/template.php#L175
295 // @see https://github.com/WordPress/WordPress/blob/5.2.2/wp-admin/includes/class-walker-category-checklist.php#L89-L111
296 $( '#' + tax + 'checklist' ).html( this.supplemental.all ); // phpcs:ignore WordPressVIPMinimum.JS.HTMLExecutingFunctions.html
297 // @see wp_popular_terms_checklist https://github.com/WordPress/WordPress/blob/5.2.2/wp-admin/includes/template.php#L236
298 $( '#' + tax + 'checklist-pop' ).html( this.supplemental.populars ); // phpcs:ignore WordPressVIPMinimum.JS.HTMLExecutingFunctions.html
299 // @see wp_dropdown_categories https://github.com/WordPress/WordPress/blob/5.5.1/wp-includes/category-template.php#L336
300 // which is called by PLL_Admin_Classic_Editor::post_lang_choice to generate supplemental.dropdown
301 $( '#new' + tax + '_parent' ).replaceWith( this.supplemental.dropdown ); // phpcs:ignore WordPressVIPMinimum.JS.HTMLExecutingFunctions.replaceWith
302 $( '#' + tax + '-lang' ).val( $( '.post_lang_choice' ).val() ); // hidden field
303 break;
304 case 'pages': // parent dropdown list for pages
305 // @see wp_dropdown_pages https://github.com/WordPress/WordPress/blob/5.2.2/wp-includes/post-template.php#L1186-L1208
306 // @see https://github.com/WordPress/WordPress/blob/5.2.2/wp-includes/class-walker-page-dropdown.php#L88
307 $( '#parent_id' ).html( this.data ); // phpcs:ignore WordPressVIPMinimum.JS.HTMLExecutingFunctions.html
308 break;
309 case 'flag': // flag in front of the select dropdown
310 // Data is built and come from server side and is well escaped when necessary
311 $( '.pll-select-flag' ).html( this.data ); // phpcs:ignore WordPressVIPMinimum.JS.HTMLExecutingFunctions.html
312 break;
313 case 'permalink': // Sample permalink
314 var div = $( '#edit-slug-box' );
315 if ( '-1' != this.data && div.children().length ) {
316 // @see get_sample_permalink_html https://github.com/WordPress/WordPress/blob/5.2.2/wp-admin/includes/post.php#L1425-L1454
317 div.html( this.data ); // phpcs:ignore WordPressVIPMinimum.JS.HTMLExecutingFunctions.html
318 }
319 break;
320 }
321 }
322 );
323
324 // Creates an event once the language has been successfully changed.
325 const onPostLangChoice = new CustomEvent(
326 "onPostLangChoice",
327 {
328 detail: {
329 lang: JSON.parse( selectedOption.options[selectedOption.options.selectedIndex].getAttribute( 'data-lang' ) )
330 },
331 }
332 );
333 document.dispatchEvent( onPostLangChoice );
334 }
335 )
336 },
337 () => {} // Do nothing when promise is rejected by clicking the Cancel dialog button.
338 );
339
340 function isEmptyPost() {
341 const title = $( 'input#title' ).val();
342 const content = $( 'textarea#content' ).val();
343 const excerpt = $( 'textarea#excerpt' ).val();
344
345 return ! title && ! content && ! excerpt;
346 }
347 }
348 );
349
350 // Listen to `onPostLangChoice` to perform actions after the language has been changed.
351 document.addEventListener(
352 'onPostLangChoice',
353 ( e ) => {
354 // Update the old language with the new one to be able to compare it in the next changing.
355 initializeLanguageOldValue();
356
357 // Modifies the language in the tag cloud.
358 $( '.tagcloud-link' ).each(
359 function () {
360 var id = $( this ).attr( 'id' );
361 tagBox.get( id );
362 }
363 );
364
365 // Modifies the text direction.
366 let dir = e.detail.lang.is_rtl ? 'rtl' : 'ltr'
367 $( 'body' ).removeClass( 'pll-dir-rtl' ).removeClass( 'pll-dir-ltr' ).addClass( 'pll-dir-' + dir );
368 $( '#content_ifr' ).contents().find( 'html' ).attr( 'lang', e.detail.lang.locale ).attr( 'dir', dir );
369 $( '#content_ifr' ).contents().find( 'body' ).attr( 'dir', dir );
370
371 // Refresh media libraries.
372 pll.media.resetAllAttachmentsCollections();
373 }
374 );
375
376 initMetaboxAutoComplete();
377 }
378 );
379
380 /**
381 * @since 3.0
382 *
383 * @namespace pll
384 */
385 var pll = window.pll || {};
386
387 /**
388 * @since 3.0
389 *
390 * @namespace pll.media
391 */
392 _.extend( pll, { media: {} } );
393
394 /**
395 * @since 3.0
396 *
397 * @alias pll.media
398 * @memberOf pll
399 * @namespace
400 */
401 var media = _.extend(
402 pll.media, /** @lends pll.media.prototype */
403 {
404 /**
405 * TODO: Find a way to delete references to Attachments collections that are not used anywhere else.
406 *
407 * @type {wp.media.model.Attachments}
408 */
409 attachmentsCollections : [],
410
411 /**
412 * Imitates { @see wp.media.query } but log all Attachments collections created.
413 *
414 * @param {Object} [props]
415 * @return {wp.media.model.Attachments}
416 */
417 query: function ( props ) {
418 var attachments = pll.media.query.delegate( props );
419
420 pll.media.attachmentsCollections.push( attachments );
421
422 return attachments;
423 },
424
425 resetAllAttachmentsCollections: function () {
426 this.attachmentsCollections.forEach(
427 function ( attachmentsCollection ) {
428 /**
429 * First reset the { @see wp.media.model.Attachments } collection.
430 * Then, if it is mirroring a { @see wp.media.model.Query } collection,
431 * refresh this one too, so it will fetch new data from the server,
432 * and then the wp.media.model.Attachments collection will synchronize with the new data.
433 */
434 attachmentsCollection.reset();
435 if (attachmentsCollection.mirroring) {
436 attachmentsCollection.mirroring._hasMore = true;
437 attachmentsCollection.mirroring.reset();
438 }
439 }
440 );
441 }
442 }
443 );
444
445 if ( 'undefined' !== typeof wp && 'undefined' !== typeof wp.media ) {
446
447 /**
448 * @since 3.0
449 *
450 * @memberOf pll.media
451 */
452 media.query = _.extend(
453 media.query, /** @lends pll.media.query prototype */
454 {
455 /**
456 * @type Function References WordPress { @see wp.media.query } constructor
457 */
458 delegate: wp.media.query
459 }
460 )
461
462 // Substitute WordPress media query shortcut with our decorated function.
463 wp.media.query = media.query
464
465 }
466
467