PluginProbe
Formidable Forms – WordPress Form Builder for Contact Forms, Calculators, Quizzes & More / 5.5
Formidable Forms – WordPress Form Builder for Contact Forms, Calculators, Quizzes & More v5.5
6.35 6.34 6.33.1 6.33 6.32.1 6.32 6.31 6.25 6.25.1 6.26 6.26.1 6.27 6.28 6.29 6.3 6.3.1 6.3.2 6.30 6.4 6.4.1 6.4.2 6.5 6.5.1 6.5.2 6.5.3 All 141 releases
← All changes | js/admin/dom.js +162 -399 6.32.1 → 5.5 View file →
@@ -2,9 +2,9 @@
2 2 /** globals frmGlobal */
3 3
4 4 let __;
5 5
6 - if ( 'undefined' === typeof wp || wp.i18n === undefined || 'function' !== typeof wp.i18n.__ ) {
6 + if ( 'undefined' === typeof wp || 'undefined' === typeof wp.i18n || 'function' !== typeof wp.i18n.__ ) {
7 7 __ = text => text;
8 8 } else {
9 9 __ = wp.i18n.__;
10 10 }
@@ -9,17 +9,17 @@
9 9 __ = wp.i18n.__;
10 10 }
11 11
12 12 const modal = {
13 - maybeCreateModal: ( id, { title, content, footer, width, dialogClass } = {} ) => {
13 + maybeCreateModal: ( id, { title, content, footer, width } = {}) => {
14 14 let modal = document.getElementById( id );
15 15
16 16 if ( ! modal ) {
17 17 modal = createEmptyModal( id );
18 18
19 - const titleElement = div( {
19 + const titleElement = div({
20 20 className: 'frm-modal-title'
21 - } );
21 + });
22 22
23 23 if ( 'string' === typeof title ) {
24 24 titleElement.textContent = title;
25 25 }
@@ -26,32 +26,29 @@
26 26
27 27 const a = tag(
28 28 'a',
29 29 {
30 - child: svg( { href: '#frm_close_icon' } ),
30 + child: svg({ href: '#frm_close_icon' }),
31 31 className: 'dismiss'
32 32 }
33 33 );
34 34 const postbox = modal.querySelector( '.postbox' );
35 35
36 - postbox.append(
37 - div( {
36 + postbox.appendChild(
37 + div({
38 38 className: 'frm_modal_top',
39 39 children: [
40 40 titleElement,
41 - div( { child: a } )
41 + div({ child: a })
42 42 ]
43 - } )
43 + })
44 44 );
45 - postbox.append(
46 - div( { className: 'frm_modal_content' } )
45 + postbox.appendChild(
46 + div({ className: 'frm_modal_content' })
47 47 );
48 -
49 - if ( footer ) {
50 - postbox.append(
51 - div( { className: 'frm_modal_footer' } )
52 - );
53 - }
48 + postbox.appendChild(
49 + div({ className: 'frm_modal_footer' })
50 + );
54 51 } else if ( 'string' === typeof title ) {
55 52 const titleElement = modal.querySelector( '.frm-modal-title' );
56 53 titleElement.textContent = title;
57 54 }
@@ -56,9 +53,9 @@
56 53 titleElement.textContent = title;
57 54 }
58 55
59 56 if ( ! content && ! footer ) {
60 - makeModalIntoADialogAndOpen( modal, { width, dialogClass } );
57 + makeModalIntoADialogAndOpen( modal, { width });
61 58 return modal;
62 59 }
63 60
64 61 const postbox = modal.querySelector( '.postbox' );
@@ -71,9 +68,9 @@
71 68 if ( footer ) {
72 69 modalHelper( footer, 'frm_modal_footer' );
73 70 }
74 71
75 - makeModalIntoADialogAndOpen( modal, { width, dialogClass } );
72 + makeModalIntoADialogAndOpen( modal );
76 73 return modal;
77 74 },
78 75 footerButton: args => {
79 76 const output = a( args );
@@ -80,21 +77,14 @@
80 77 output.setAttribute( 'role', 'button' );
81 78 output.setAttribute( 'tabindex', 0 );
82 79 if ( args.buttonType ) {
83 80 output.classList.add( 'button' );
84 -
85 - if ( ! args.noDismiss && [ 'red', 'primary' ].includes( args.buttonType ) ) {
86 - // Primary and red buttons close modals by default on click.
87 - // To disable this default behaviour you can use the noDismiss: 1 arg.
88 - output.classList.add( 'dismiss' );
89 - }
90 -
91 81 switch ( args.buttonType ) {
92 - case 'red':
93 - output.classList.add( 'frm-button-red', 'frm-button-primary' );
94 - break;
95 82 case 'primary':
96 83 output.classList.add( 'button-primary', 'frm-button-primary' );
84 + if ( ! args.noDismiss ) {
85 + output.classList.add( 'dismiss' );
86 + }
97 87 break;
98 88 case 'secondary':
99 89 output.classList.add( 'button-secondary', 'frm-button-secondary' );
100 90 output.style.marginRight = '10px';
@@ -108,14 +98,10 @@
108 98 }
109 99 };
110 100
111 101 const ajax = {
112 - async doJsonFetch( action ) {
113 - let targetUrl = `${ ajaxurl }?action=frm_${ action }`;
114 - if ( ! targetUrl.includes( 'nonce=' ) ) {
115 - targetUrl += `&nonce=${ frmGlobal.nonce }`;
116 - }
117 - const response = await fetch( targetUrl );
102 + doJsonFetch: async function( action ) {
103 + const response = await fetch( ajaxurl + '?action=frm_' + action );
118 104 const json = await response.json();
119 105 if ( ! json.success ) {
120 106 return Promise.reject( json.data || 'JSON result is not successful' );
121 107 }
@@ -120,50 +106,41 @@
120 106 return Promise.reject( json.data || 'JSON result is not successful' );
121 107 }
122 108 return Promise.resolve( json.data );
123 109 },
124 - async doJsonPost( action, formData, { signal } = {} ) {
110 + doJsonPost: async function( action, formData ) {
125 111 formData.append( 'nonce', frmGlobal.nonce );
126 112 const init = {
127 113 method: 'POST',
128 114 body: formData
129 115 };
130 - if ( signal ) {
131 - init.signal = signal;
132 - }
133 - const response = await fetch( `${ ajaxurl }?action=frm_${ action }`, init );
116 + const response = await fetch( ajaxurl + '?action=frm_' + action, init );
134 117 const json = await response.json();
135 118 if ( ! json.success ) {
136 119 return Promise.reject( json.data || 'JSON result is not successful' );
137 120 }
138 - return Promise.resolve( json.data !== undefined ? json.data : json );
121 + return Promise.resolve( 'undefined' !== typeof json.data ? json.data : json );
139 122 }
140 123 };
141 124
142 125 const multiselect = {
143 - init() {
144 - const $select = jQuery( this );
145 - const id = $select.is( '[id]' ) ? $select.attr( 'id' ).replace( '[]', '' ) : false;
126 + init: function() {
127 + let $select, id, labelledBy;
146 128
147 - let labelledBy = id ? jQuery( `#for_${ id }` ) : false;
148 - labelledBy = id && labelledBy.length ? `aria-labelledby="${ labelledBy.attr( 'id' ) }"` : '';
129 + $select = jQuery( this );
130 + id = $select.is( '[id]' ) ? $select.attr( 'id' ).replace( '[]', '' ) : false;
131 + labelledBy = id ? jQuery( '#for_' + id ) : false;
132 + labelledBy = id && labelledBy.length ? 'aria-labelledby="' + labelledBy.attr( 'id' ) + '"' : '';
149 133
150 - $select.multiselect( {
134 + $select.multiselect({
151 135 templates: {
152 - popupContainer: '<div class="multiselect-container frm-dropdown-menu dropdown-menu"></div>',
136 + popupContainer: '<div class="multiselect-container frm-dropdown-menu"></div>',
153 137 option: '<button type="button" class="multiselect-option dropdown-item frm_no_style_button"></button>',
154 - button: `<button type="button" class="multiselect dropdown-toggle btn" data-bs-toggle="dropdown" ${ labelledBy }><span class="multiselect-selected-text"></span> <b class="caret"></b></button>`
138 + button: '<button type="button" class="multiselect dropdown-toggle btn" data-toggle="dropdown" ' + labelledBy + '><span class="multiselect-selected-text"></span> <b class="caret"></b></button>'
155 139 },
156 140 buttonContainer: '<div class="btn-group frm-btn-group dropdown" />',
157 - nonSelectedText: __( '— Select —', 'formidable' ),
158 - // Prevent the dropdown from showing "All Selected" when every option is checked.
159 - allSelectedText: '',
160 - // This is 3 by default. We want to show more options before it starts showing a count.
161 - numberDisplayed: 8,
162 - onInitialized( _, $container ) {
163 - $container.find( '.multiselect.dropdown-toggle,.multiselect-option.dropdown-item' ).removeAttr( 'title' );
164 - },
165 - onDropdownShown( event ) {
141 + nonSelectedText: '',
142 + onDropdownShown: function( event ) {
166 143 const action = jQuery( event.currentTarget.closest( '.frm_form_action_settings, #frm-show-fields' ) );
167 144 if ( action.length ) {
168 145 jQuery( '#wpcontent' ).on( 'click', function() {
169 146 if ( jQuery( '.multiselect-container.frm-dropdown-menu' ).is( ':visible' ) ) {
@@ -168,55 +145,67 @@
168 145 jQuery( '#wpcontent' ).on( 'click', function() {
169 146 if ( jQuery( '.multiselect-container.frm-dropdown-menu' ).is( ':visible' ) ) {
170 147 jQuery( event.currentTarget ).removeClass( 'open' );
171 148 }
172 - } );
149 + });
173 150 }
174 -
175 - const $dropdown = $select.next( '.frm-btn-group.dropdown' );
176 - $dropdown.find( '.dropdown-item' ).each(
177 - function() {
178 - const option = this;
179 - const dropdownInput = option.querySelector( 'input[type="checkbox"], input[type="radio"]' );
180 - if ( dropdownInput ) {
181 - option.setAttribute( 'role', 'checkbox' );
182 - option.setAttribute( 'aria-checked', dropdownInput.checked ? 'true' : 'false' );
183 - }
184 - }
185 - );
186 151 },
187 - onChange( $option, checked ) {
188 - $select.trigger( 'frm-multiselect-changed', $option, checked );
189 -
190 - const $dropdown = $select.next( '.frm-btn-group.dropdown' );
191 - const optionValue = $option.val();
192 - const $dropdownItem = $dropdown.find( `input[value="${ optionValue }"]` ).closest( 'button.dropdown-item' );
193 - if ( $dropdownItem.length ) {
194 - $dropdownItem.attr( 'aria-checked', checked ? 'true' : 'false' );
195 -
196 - // Delay a focus event so the screen reader reads the option value again.
197 - // Without this, and without the setTimeout, it only reads "checked" or "unchecked".
198 - setTimeout( () => $dropdownItem.get( 0 ).focus(), 0 );
199 - }
152 + onChange: function( element, option ) {
153 + $select.trigger( 'frm-multiselect-changed', element, option );
200 154 }
201 - } );
155 + });
202 156 }
203 157 };
204 158
205 159 const bootstrap = {
206 - setupBootstrapDropdowns() {
207 - // This function is no longer necessary.
208 - // It's call in Pro though, so keep it to avoid any errors for now.
160 + setupBootstrapDropdowns( callback ) {
161 + if ( ! window.bootstrap || ! window.bootstrap.Dropdown ) {
162 + return;
163 + }
164 +
165 + window.bootstrap.Dropdown._getParentFromElement = getParentFromElement;
166 + window.bootstrap.Dropdown.prototype._getParentFromElement = getParentFromElement;
167 +
168 + function getParentFromElement( element ) {
169 + let parent;
170 + const selector = window.bootstrap.Util.getSelectorFromElement( element );
171 +
172 + if ( selector ) {
173 + parent = document.querySelector( selector );
174 + }
175 +
176 + const result = parent || element.parentNode;
177 + const frmDropdownMenu = result.querySelector( '.frm-dropdown-menu' );
178 +
179 + if ( ! frmDropdownMenu ) {
180 + // Not a formidable dropdown, treat like Bootstrap does normally.
181 + return result;
182 + }
183 +
184 + // Temporarily add dropdown-menu class so bootstrap can initialize.
185 + frmDropdownMenu.classList.add( 'dropdown-menu' );
186 + setTimeout(
187 + function() {
188 + frmDropdownMenu.classList.remove( 'dropdown-menu' );
189 + },
190 + 0
191 + );
192 +
193 + if ( 'function' === typeof callback ) {
194 + callback( frmDropdownMenu );
195 + }
196 +
197 + return result;
198 + }
209 199 },
210 200 multiselect
211 201 };
212 202
213 203 const autocomplete = {
214 - initSelectionAutocomplete( container ) {
204 + initSelectionAutocomplete: function() {
215 205 if ( jQuery.fn.autocomplete ) {
216 - autocomplete.initAutocomplete( 'page', container );
217 - autocomplete.initAutocomplete( 'user', container );
218 - autocomplete.initAutocomplete( 'custom', container );
206 + autocomplete.initAutocomplete( 'page' );
207 + autocomplete.initAutocomplete( 'user' );
219 208 }
220 209 },
221 210 /**
222 211 * Init autocomplete.
@@ -222,14 +211,14 @@
222 211 * Init autocomplete.
223 212 *
224 213 * @since 4.10.01 Add container param to init autocomplete elements inside an element.
225 214 *
226 - * @param {string} type Type of data. Accepts `page` or `user`.
227 - * @param {string|Object} container Container class or element. Default is null.
215 + * @param {String} type Type of data. Accepts `page` or `user`.
216 + * @param {String|Object} container Container class or element. Default is null.
228 217 */
229 - initAutocomplete( type, container ) {
230 - const basedUrlParams = `?action=frm_${ type }_search&nonce=${ frmGlobal.nonce }`;
231 - const elements = ! container ? jQuery( `.frm-${ type }-search` ) : jQuery( container ).find( `.frm-${ type }-search` );
218 + initAutocomplete: function( type, container ) {
219 + const basedUrlParams = '?action=frm_' + type + '_search&nonce=' + frmGlobal.nonce;
220 + const elements = ! container ? jQuery( '.frm-' + type + '-search' ) : jQuery( container ).find( '.frm-' + type + '-search' );
232 221
233 222 elements.each( initAutocompleteForElement );
234 223
235 224 function initAutocompleteForElement() {
@@ -237,24 +226,15 @@
237 226 const element = jQuery( this );
238 227
239 228 // Check if a custom post type is specific.
240 229 if ( element.attr( 'data-post-type' ) ) {
241 - urlParams += `&post_type=${ element.attr( 'data-post-type' ) }`;
230 + urlParams += '&post_type=' + element.attr( 'data-post-type' );
242 231 }
243 232
244 - let source = ajaxurl + urlParams;
245 -
246 - if ( this.dataset.source ) {
247 - const sourceData = JSON.parse( this.dataset.source );
248 - if ( sourceData ) {
249 - source = sourceData;
250 - }
251 - }
252 -
253 - element.autocomplete( {
233 + element.autocomplete({
254 234 delay: 100,
255 235 minLength: 0,
256 - source,
236 + source: ajaxurl + urlParams,
257 237 change: autocomplete.selectBlank,
258 238 select: autocomplete.completeSelectFromResults,
259 239 focus: () => false,
260 240 position: {
@@ -261,9 +241,9 @@
261 241 my: 'left top',
262 242 at: 'left bottom',
263 243 collision: 'flip'
264 244 },
265 - response( event, ui ) {
245 + response: function( event, ui ) {
266 246 if ( ! ui.content.length ) {
267 247 const noResult = {
268 248 value: '',
269 249 label: frm_admin_js.no_items_found
@@ -270,9 +250,9 @@
270 250 };
271 251 ui.content.push( noResult );
272 252 }
273 253 },
274 - create() {
254 + create: function() {
275 255 let $container = jQuery( this ).parent();
276 256
277 257 if ( $container.length === 0 ) {
278 258 $container = 'body';
@@ -279,48 +259,34 @@
279 259 }
280 260
281 261 jQuery( this ).autocomplete( 'option', 'appendTo', $container );
282 262 }
283 - } )
284 - .on( 'focus', function() {
263 + })
264 + .on( 'focus', function() {
285 265 // Show options on click to make it work more like a dropdown.
286 - if ( this.value === '' || this.nextElementSibling.value < 1 ) {
287 - jQuery( this ).autocomplete( 'search', this.value );
288 - }
289 - } )
290 - .data( 'ui-autocomplete' )._renderItem = function( ul, item ) {
291 - return jQuery( '<li>' )
292 - .attr( 'aria-label', item.label )
293 - .append( jQuery( '<div>' ).text( item.label ) )
294 - .appendTo( ul );
295 - };
266 + if ( this.value === '' || this.nextElementSibling.value < 1 ) {
267 + jQuery( this ).autocomplete( 'search', this.value );
268 + }
269 + })
270 + .data( 'ui-autocomplete' )._renderItem = function( ul, item ) {
271 + return jQuery( '<li>' )
272 + .attr( 'aria-label', item.label )
273 + .append( jQuery( '<div>' ).text( item.label ) )
274 + .appendTo( ul );
275 + };
296 276 }
297 277 },
298 278
299 - selectBlank( e, ui ) {
279 + selectBlank: function( e, ui ) {
300 280 if ( ui.item === null ) {
301 281 this.nextElementSibling.value = '';
302 -
303 - /**
304 - * Fires when an autocomplete value is cleared.
305 - *
306 - * @since 6.21
307 - */
308 - wp.hooks.doAction( 'frm_autocomplete_clear_value', e, ui, this );
309 282 }
310 283 },
311 284
312 - completeSelectFromResults( e, ui ) {
285 + completeSelectFromResults: function( e, ui ) {
313 286 e.preventDefault();
314 287 this.value = ui.item.value === '' ? '' : ui.item.label;
315 288 this.nextElementSibling.value = ui.item.value;
316 -
317 - /**
318 - * Fires when an autocomplete item is selected.
319 - *
320 - * @since 6.21
321 - */
322 - wp.hooks.doAction( 'frm_autocomplete_select', e, ui, this );
323 289 }
324 290 };
325 291
326 292 const search = {
@@ -338,21 +304,21 @@
338 304 {
339 305 className: 'frm-search',
340 306 children: [
341 307 label,
342 - span( { className: 'frmfont frm_search_icon' } ),
308 + span({ className: 'frmfont frm_search_icon' }),
343 309 searchInput
344 310 ]
345 311 }
346 312 );
347 313 },
348 - newSearchInput: ( id, placeholder, targetClassName, args = {} ) => {
314 + newSearchInput: ( id, placeholder, targetClassName, args = {}) => {
349 315 const input = getAutoSearchInput( id, placeholder );
350 316 const wrappedSearch = search.wrapInput( input, placeholder );
351 317 search.init( input, targetClassName, args );
352 318
353 319 function getAutoSearchInput( id, placeholder ) {
354 - const className = 'frm-search-input frm-auto-search frm-w-full';
320 + const className = 'frm-search-input frm-auto-search';
355 321 const inputArgs = { id, className };
356 322 const input = tag( 'input', inputArgs );
357 323 input.setAttribute( 'placeholder', placeholder );
358 324 return input;
@@ -359,9 +325,9 @@
359 325 }
360 326
361 327 return wrappedSearch;
362 328 },
363 - init: ( input, targetClassName, { handleSearchResult } = {} ) => {
329 + init: ( input, targetClassName, { handleSearchResult } = {}) => {
364 330 input.setAttribute( 'type', 'search' );
365 331 input.setAttribute( 'autocomplete', 'off' );
366 332
367 333 input.addEventListener( 'input', handleSearch );
@@ -367,9 +333,9 @@
367 333 input.addEventListener( 'input', handleSearch );
368 334 input.addEventListener( 'search', handleSearch );
369 335 input.addEventListener( 'change', handleSearch );
370 336
371 - function handleSearch( event ) {
337 + function handleSearch() {
372 338 const searchText = input.value.toLowerCase();
373 339 const notEmptySearchText = searchText !== '';
374 340 const items = Array.from( document.getElementsByClassName( targetClassName ) );
375 341
@@ -375,9 +341,9 @@
375 341
376 342 let foundSomething = false;
377 343 items.forEach( toggleSearchClassesForItem );
378 344 if ( 'function' === typeof handleSearchResult ) {
379 - handleSearchResult( { foundSomething, notEmptySearchText }, event );
345 + handleSearchResult({ foundSomething, notEmptySearchText });
380 346 }
381 347
382 348 function toggleSearchClassesForItem( item ) {
383 349 let itemText;
@@ -388,9 +354,9 @@
388 354 itemText = item.innerText.toLowerCase();
389 355 item.setAttribute( 'frm-search-text', itemText );
390 356 }
391 357
392 - const hide = notEmptySearchText && ! itemText.includes( searchText );
358 + const hide = notEmptySearchText && -1 === itemText.indexOf( searchText );
393 359 item.classList.toggle( 'frm_hidden', hide );
394 360
395 361 const isSearchResult = ! hide && notEmptySearchText;
396 362 if ( isSearchResult ) {
@@ -417,70 +383,14 @@
417 383 const listener = event => {
418 384 event.preventDefault();
419 385 callback( event );
420 386 };
421 - element?.addEventListener( 'click', listener );
422 - },
423 -
424 - /**
425 - * Does the same as jQuery( document ).on( 'event', 'selector', handler ).
426 - *
427 - * @since 6.0
428 - *
429 - * @param {string} event Event name.
430 - * @param {string} selector Selector.
431 - * @param {Function} handler Handler.
432 - * @param {boolean|Object} options Options to be added to `addEventListener()` method. Default is `false`.
433 - */
434 - documentOn: ( event, selector, handler, options ) => {
435 - if ( options === undefined ) {
436 - options = false;
437 - }
438 -
439 - document.addEventListener( event, function( e ) {
440 - let target;
441 -
442 - // loop parent nodes from the target to the delegation node.
443 - for ( target = e.target; target && target != this; target = target.parentNode ) {
444 - if ( target.matches && target.matches( selector ) ) {
445 - handler.call( target, e );
446 - break;
447 - }
448 - }
449 - }, options );
450 - },
451 -
452 - /**
453 - * Retrieves the value of a cookie by its name.
454 - *
455 - * @param {string} name - The name of the cookie.
456 - * @return {string|null} The value of the cookie, or undefined if the cookie does not exist.
457 - */
458 - getCookie: name => {
459 - const cookie = document.cookie.split( '; ' ).find( cookie => cookie.startsWith( `${ name }=` ) );
460 -
461 - if ( cookie ) {
462 - return cookie.split( '=' )[ 1 ];
463 - }
464 - return null;
465 - },
466 -
467 - /**
468 - * Sets a cookie with the specified name, value, and expiration time.
469 - *
470 - * @param {string} name - The name of the cookie.
471 - * @param {string} value - The value of the cookie.
472 - * @param {number} minutes - The number of minutes until the cookie expires.
473 - */
474 - setCookie: ( name, value, minutes ) => {
475 - const expires = new Date();
476 - expires.setTime( expires.getTime() + ( minutes * 60 * 1000 ) );
477 - document.cookie = `${ name }=${ value };expires=${ expires.toUTCString() };path=/`;
387 + element.addEventListener( 'click', listener );
478 388 }
479 389 };
480 390
481 391 const wysiwyg = {
482 - init( editor, { setupCallback, height, addFocusEvents } = {} ) {
392 + init( editor, { setupCallback } = {}) {
483 393 if ( isTinyMceActive() ) {
484 394 setTimeout( resetTinyMce, 0 );
485 395 } else {
486 396 initQuickTagsButtons();
@@ -489,25 +399,25 @@
489 399 setUpTinyMceVisualButtonListener();
490 400 setUpTinyMceHtmlButtonListener();
491 401
492 402 function initQuickTagsButtons() {
493 - if ( 'function' !== typeof window.quicktags || window.QTags.instances[ editor.id ] !== undefined ) {
403 + if ( 'function' !== typeof window.quicktags || typeof window.QTags.instances[ editor.id ] !== 'undefined' ) {
494 404 return;
495 405 }
496 406
497 - const { id } = editor;
498 - window.quicktags( {
499 - name: `qt_${ id }`,
500 - id,
407 + const id = editor.id;
408 + window.quicktags({
409 + name: 'qt_' + id,
410 + id: id,
501 411 canvas: editor,
502 412 settings: { id },
503 - toolbar: document.getElementById( `qt_${ id }_toolbar` ),
413 + toolbar: document.getElementById( 'qt_' + id + '_toolbar' ),
504 414 theButtons: {}
505 - } );
415 + });
506 416 }
507 417
508 418 function initRichText() {
509 - const key = Object.keys( tinyMCEPreInit.mceInit )[ 0 ];
419 + const key = Object.keys( tinyMCEPreInit.mceInit )[0];
510 420 const orgSettings = tinyMCEPreInit.mceInit[ key ];
511 421
512 422 const settings = Object.assign(
513 423 {},
@@ -512,33 +422,15 @@
512 422 const settings = Object.assign(
513 423 {},
514 424 orgSettings,
515 425 {
516 - selector: `#${ editor.id }`,
426 + selector: '#' + editor.id,
517 427 body_class: orgSettings.body_class.replace( key, editor.id )
518 428 }
519 429 );
520 430
521 - settings.setup = editor => {
522 - if ( addFocusEvents ) {
523 - function focusInCallback() {
524 - jQuery( editor.targetElm ).trigger( 'focusin' );
525 - editor.off( 'focusin', '**' );
526 - }
527 -
528 - editor.on( 'focusin', focusInCallback );
529 -
530 - editor.on( 'focusout', function() {
531 - editor.on( 'focusin', focusInCallback );
532 - } );
533 - }
534 - if ( setupCallback ) {
535 - setupCallback( editor );
536 - }
537 - };
538 -
539 - if ( height ) {
540 - settings.height = height;
431 + if ( setupCallback ) {
432 + settings.setup = setupCallback;
541 433 }
542 434
543 435 tinymce.init( settings );
544 436 }
@@ -552,25 +444,25 @@
552 444 initRichText();
553 445 }
554 446
555 447 function isTinyMceActive() {
556 - const { id } = editor;
557 - const wrapper = document.getElementById( `wp-${ id }-wrap` );
558 - return wrapper && wrapper.classList.contains( 'tmce-active' );
448 + const id = editor.id;
449 + const wrapper = document.getElementById( 'wp-' + id + '-wrap' );
450 + return null !== wrapper && wrapper.classList.contains( 'tmce-active' );
559 451 }
560 452
561 453 function setUpTinyMceVisualButtonListener() {
562 454 jQuery( document ).on(
563 - 'click', `#${ editor.id }-html`,
455 + 'click', '#' + editor.id + '-html',
564 456 function() {
565 457 editor.style.visibility = 'visible';
566 - initQuickTagsButtons();
458 + initQuickTagsButtons( editor );
567 459 }
568 460 );
569 461 }
570 462
571 463 function setUpTinyMceHtmlButtonListener() {
572 - jQuery( `#${ editor.id }-tmce` ).on( 'click', handleTinyMceHtmlButtonClick );
464 + jQuery( '#' + editor.id + '-tmce' ).on( 'click', handleTinyMceHtmlButtonClick );
573 465 }
574 466
575 467 function handleTinyMceHtmlButtonClick() {
576 468 if ( isTinyMceActive() ) {
@@ -578,9 +470,9 @@
578 470 } else {
579 471 initRichText();
580 472 }
581 473
582 - const wrap = document.getElementById( `wp-${ editor.id }-wrap` );
474 + const wrap = document.getElementById( 'wp-' + editor.id + '-wrap' );
583 475 wrap.classList.add( 'tmce-active' );
584 476 wrap.classList.remove( 'html-active' );
585 477 }
586 478 }
@@ -587,15 +479,15 @@
587 479 };
588 480
589 481 function getModalHelper( modal, appendTo ) {
590 482 return function( child, uniqueClassName ) {
591 - let element = modal.querySelector( `.${ uniqueClassName }` );
483 + let element = modal.querySelector( '.' + uniqueClassName );
592 484 if ( null === element ) {
593 - element = div( {
594 - child,
485 + element = div({
486 + child: child,
595 487 className: uniqueClassName
596 - } );
597 - appendTo.append( element );
488 + });
489 + appendTo.appendChild( element );
598 490 } else {
599 491 redraw( element, child );
600 492 }
601 493 };
@@ -601,23 +493,21 @@
601 493 };
602 494 }
603 495
604 496 function createEmptyModal( id ) {
605 - const modal = div( { id, className: 'frm-modal' } );
606 - const postbox = div( { className: 'postbox' } );
607 - const metaboxHolder = div( { className: 'metabox-holder', child: postbox } );
608 - modal.append( metaboxHolder );
609 - document.body.append( modal );
497 + const modal = div({ id, className: 'frm-modal' });
498 + const postbox = div({ className: 'postbox' });
499 + const metaboxHolder = div({ className: 'metabox-holder', child: postbox });
500 + modal.appendChild( metaboxHolder );
501 + document.body.appendChild( modal );
610 502 return modal;
611 503 }
612 504
613 - function makeModalIntoADialogAndOpen( modal, { width, dialogClass = '' } = {} ) {
614 - const bodyWithModalClassName = 'frm-body-with-open-modal';
615 -
505 + function makeModalIntoADialogAndOpen( modal, { width } = {}) {
616 506 const $modal = jQuery( modal );
617 507 if ( ! $modal.hasClass( 'frm-dialog' ) ) {
618 - $modal.dialog( {
619 - dialogClass: `frm-dialog ${ dialogClass }`,
508 + $modal.dialog({
509 + dialogClass: 'frm-dialog',
620 510 modal: true,
621 511 autoOpen: false,
622 512 closeOnEscape: true,
623 513 width: width || '550px',
@@ -622,9 +512,9 @@
622 512 closeOnEscape: true,
623 513 width: width || '550px',
624 514 resizable: false,
625 515 draggable: false,
626 - open() {
516 + open: function() {
627 517 jQuery( '.ui-dialog-titlebar' ).addClass( 'frm_hidden' ).removeClass( 'ui-helper-clearfix' );
628 518 jQuery( '#wpwrap' ).addClass( 'frm_overlay' );
629 519 jQuery( '.frm-dialog' ).removeClass( 'ui-widget ui-widget-content ui-corner-all' );
630 520
@@ -632,9 +522,9 @@
632 522
633 523 $modal.on( 'click', 'a.dismiss', function( event ) {
634 524 event.preventDefault();
635 525 $modal.dialog( 'close' );
636 - } );
526 + });
637 527
638 528 const overlay = document.querySelector( '.ui-widget-overlay' );
639 529 if ( overlay ) {
640 530 overlay.addEventListener(
@@ -645,18 +535,17 @@
645 535 }
646 536 );
647 537 }
648 538 },
649 - close() {
650 - document.body.classList.remove( bodyWithModalClassName );
539 + close: function() {
540 + document.body.style.overflowY = 'initial';
651 541 jQuery( '#wpwrap' ).removeClass( 'frm_overlay' );
652 542 jQuery( '.spinner' ).css( 'visibility', 'hidden' );
653 543 }
654 - } );
544 + });
655 545 }
656 546
657 - document.body.classList.add( bodyWithModalClassName );
658 -
547 + document.body.style.overflowY = 'hidden';
659 548 $modal.dialog( 'open' );
660 549 return $modal;
661 550 }
662 551
@@ -667,9 +556,9 @@
667 556 function span( args ) {
668 557 return tag( 'span', args );
669 558 }
670 559
671 - function a( args = {} ) {
560 + function a( args = {}) {
672 561 const anchor = tag( 'a', args );
673 562 anchor.setAttribute( 'href', 'string' === typeof args.href ? args.href : '#' );
674 563 if ( 'string' === typeof args.target ) {
675 564 anchor.target = args.target;
@@ -676,56 +565,17 @@
676 565 }
677 566 return anchor;
678 567 }
679 568
680 - function img( args = {} ) {
569 + function img( args = {}) {
681 570 const output = tag( 'img', args );
682 571 if ( 'string' === typeof args.src ) {
683 572 output.setAttribute( 'src', args.src );
684 573 }
685 - if ( 'string' === typeof args.alt ) {
686 - output.setAttribute( 'alt', args.alt );
687 - }
688 574 return output;
689 575 }
690 576
691 - /**
692 - * Get a labelled text input and a matching label.
693 - *
694 - * @since 6.0
695 - *
696 - * @param {string} inputId
697 - * @param {string} labelText
698 - * @param {string} inputName
699 - * @return {Element} The div element containing the label and input.
700 - */
701 - function labelledTextInput( inputId, labelText, inputName ) {
702 - const label = tag( 'label', labelText );
703 - label.setAttribute( 'for', inputId );
704 -
705 - const input = tag(
706 - 'input',
707 - {
708 - id: inputId,
709 - className: 'frm_long_input'
710 - }
711 - );
712 - input.type = 'text';
713 - input.setAttribute( 'name', inputName );
714 -
715 - return div( { children: [ label, input ] } );
716 - }
717 -
718 - /**
719 - * Build an element.
720 - *
721 - * @since 6.4.1 Accept a string as one of `children` to append a text node inside the element.
722 - *
723 - * @param {string} type Element tag name.
724 - * @param {Object} args The args.
725 - * @return {Object} The created DOM element.
726 - */
727 - function tag( type, args = {} ) {
577 + function tag( type, args = {}) {
728 578 const output = document.createElement( type );
729 579
730 580 if ( 'string' === typeof args ) {
731 581 // Support passing just a string to a tag for simple text elements.
@@ -732,9 +582,9 @@
732 582 output.textContent = args;
733 583 return output;
734 584 }
735 585
736 - const { id, className, children, child, text, data } = args;
586 + const { id, className, children, child, text } = args;
737 587
738 588 if ( id ) {
739 589 output.id = id;
740 590 }
@@ -741,29 +591,18 @@
741 591 if ( className ) {
742 592 output.className = className;
743 593 }
744 594 if ( children ) {
745 - children.forEach( child => {
746 - if ( 'string' === typeof child ) {
747 - output.append( document.createTextNode( child ) );
748 - } else {
749 - output.append( child );
750 - }
751 - } );
595 + children.forEach( child => output.appendChild( child ) );
752 596 } else if ( child ) {
753 - output.append( child );
597 + output.appendChild( child );
754 598 } else if ( text ) {
755 599 output.textContent = text;
756 600 }
757 - if ( data ) {
758 - Object.keys( data ).forEach( function( dataKey ) {
759 - output.setAttribute( `data-${ dataKey }`, data[ dataKey ] );
760 - } );
761 - }
762 601 return output;
763 602 }
764 603
765 - function svg( { href, classList } = {} ) {
604 + function svg({ href, classList } = {}) {
766 605 const namespace = 'http://www.w3.org/2000/svg';
767 606 const output = document.createElementNS( namespace, 'svg' );
768 607 if ( classList ) {
769 608 output.classList.add( ...classList );
@@ -771,99 +610,23 @@
771 610
772 611 if ( href ) {
773 612 const use = document.createElementNS( namespace, 'use' );
774 613 use.setAttribute( 'href', href );
775 - output.append( use );
614 + output.appendChild( use );
776 615 output.classList.add( 'frmsvg' );
777 616 }
778 617 return output;
779 618 }
780 619
781 - /**
782 - * Pop up a success message in the lower right corner.
783 - * It then fades out and gets deleted automatically.
784 - *
785 - * @param {HTMLElement|string} content
786 - * @return {void}
787 - */
788 - function success( content ) {
789 - const container = document.getElementById( 'wpbody' );
790 - const notice = div( {
791 - className: 'frm_updated_message frm-floating-success-message',
792 - child: div( {
793 - className: 'frm-satisfied',
794 - child: 'string' === typeof content ? document.createTextNode( content ) : content
795 - } )
796 - } );
797 - container.append( notice );
798 -
799 - setTimeout(
800 - () => jQuery( notice ).fadeOut( () => notice.remove() ),
801 - 2000
802 - );
803 - }
804 -
805 620 function setAttributes( element, attrs ) {
806 621 Object.entries( attrs ).forEach(
807 - ( [ key, value ] ) => element.setAttribute( key, value )
622 + ([ key, value ]) => element.setAttribute( key, value )
808 623 );
809 624 }
810 625
811 626 function redraw( element, child ) {
812 627 element.innerHTML = '';
813 - element.append( child );
628 + element.appendChild( child );
814 629 }
815 630
816 - const allowedHtml = {
817 - b: [],
818 - div: [ 'class' ],
819 - img: [ 'src', 'alt' ],
820 - p: [],
821 - span: [ 'class' ],
822 - strong: [],
823 - svg: [ 'class' ],
824 - use: [],
825 - a: [ 'href', 'class' ]
826 - };
827 -
828 - function cleanNode( node ) {
829 - if ( node.tagName === undefined ) {
830 - if ( '#text' === node.nodeName ) {
831 - return document.createTextNode( node.textContent );
832 - }
833 - return document.createTextNode( '' );
834 - }
835 -
836 - const tagType = node.tagName.toLowerCase();
837 -
838 - if ( 'svg' === tagType ) {
839 - const svgArgs = {
840 - classList: Array.from( node.classList )
841 - };
842 - const use = node.querySelector( 'use' );
843 - if ( use ) {
844 - svgArgs.href = use.getAttribute( 'href' ) || use.getAttribute( 'xlink:href' );
845 - }
846 - return svg( svgArgs );
847 - }
848 -
849 - if ( allowedHtml[ tagType ] === undefined ) {
850 - // Tag type is not allowed.
851 - return document.createTextNode( '' );
852 - }
853 -
854 - const newNode = document.createElement( tagType );
855 -
856 - allowedHtml[ tagType ].forEach(
857 - allowedTag => {
858 - if ( node.hasAttribute( allowedTag ) ) {
859 - newNode.setAttribute( allowedTag, node.getAttribute( allowedTag ) );
860 - }
861 - }
862 - );
863 -
864 - node.childNodes.forEach( child => newNode.append( cleanNode( child ) ) );
865 - return newNode;
866 - }
867 -
868 - window.frmDom = { tag, div, span, a, img, labelledTextInput, svg, setAttributes, success, modal, ajax, bootstrap, autocomplete, search, util, wysiwyg, cleanNode };
631 + window.frmDom = { tag, div, span, a, img, svg, setAttributes, modal, ajax, bootstrap, autocomplete, search, util, wysiwyg };
869 632 }() );