PluginProbe
Polylang / trunk
Polylang vtrunk
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 trunk, at js/build/classic-editor.js

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