| @@ -2,9 +2,8 @@ | ||
| 2 | 2 | * This script file handles style settings in the Lite plugin. |
| 3 | 3 | * Pro-specific features are in the style-settings.js file in Pro. |
| 4 | 4 | */ |
| 5 | 5 | ( function() { |
| 6 | - /* globals frmDom, frmAdminBuild */ | |
| 7 | 6 | 'use strict'; |
| 8 | 7 | |
| 9 | 8 | if ( ! document.getElementById( 'frm_active_style_form' ) ) { |
| 10 | 9 | return; |
| @@ -61,11 +60,100 @@ | ||
| 61 | 60 | document.body.classList.remove( 'wp-core-ui' ); |
| 62 | 61 | document.getElementById( 'frm_style_sidebar' ).classList.add( 'wp-core-ui' ); |
| 63 | 62 | |
| 64 | 63 | jQuery( document ).on( 'input change', 'input[data-frmrange]', initSliderPreview ); |
| 64 | + | |
| 65 | + initOptionLayoutPreview(); | |
| 65 | 66 | } |
| 66 | 67 | |
| 67 | 68 | /** |
| 69 | + * Sync the radio/checkbox option layout (alignment) style settings to the preview form in real time. | |
| 70 | + * | |
| 71 | + * The layout is applied to the front end through a container class, so changing the style setting | |
| 72 | + * does not re-render the preview form. This swaps the container class on preview fields that use the | |
| 73 | + * style setting. The styler preview marks those fields with .frm-default-option-align, so fields with | |
| 74 | + * their own alignment override are left untouched. | |
| 75 | + * | |
| 76 | + * @return {void} | |
| 77 | + */ | |
| 78 | + function initOptionLayoutPreview() { | |
| 79 | + bindOptionLayoutSelect( document.getElementById( 'frm_radio_align' ), '.frm_radio' ); | |
| 80 | + bindOptionLayoutSelect( document.getElementById( 'frm_check_align' ), '.frm_checkbox' ); | |
| 81 | + } | |
| 82 | + | |
| 83 | + /** | |
| 84 | + * Listen for changes on an option layout style setting and update the preview to match. | |
| 85 | + * | |
| 86 | + * @param {HTMLSelectElement|null} select The style setting dropdown. | |
| 87 | + * @param {string} optionSelector The single option selector ('.frm_radio' or '.frm_checkbox'). | |
| 88 | + * @return {void} | |
| 89 | + */ | |
| 90 | + function bindOptionLayoutSelect( select, optionSelector ) { | |
| 91 | + if ( ! select ) { | |
| 92 | + return; | |
| 93 | + } | |
| 94 | + | |
| 95 | + select.addEventListener( 'change', () => { | |
| 96 | + updatePreviewOptionLayout( optionSelector, select.value ); | |
| 97 | + } ); | |
| 98 | + } | |
| 99 | + | |
| 100 | + /** | |
| 101 | + * Replace the alignment container class on preview fields that are using the style setting. | |
| 102 | + * | |
| 103 | + * @param {string} optionSelector The single option selector ('.frm_radio' or '.frm_checkbox'). | |
| 104 | + * @param {string} newAlign The newly selected style alignment value. | |
| 105 | + * @return {void} | |
| 106 | + */ | |
| 107 | + function updatePreviewOptionLayout( optionSelector, newAlign ) { | |
| 108 | + const newClass = optionLayoutAlignToClass( newAlign ); | |
| 109 | + | |
| 110 | + if ( ! newClass ) { | |
| 111 | + return; | |
| 112 | + } | |
| 113 | + | |
| 114 | + const activeForm = document.getElementById( 'frm_active_style_form' ); | |
| 115 | + if ( ! activeForm ) { | |
| 116 | + return; | |
| 117 | + } | |
| 118 | + | |
| 119 | + const alignClasses = [ 'vertical_radio', 'horizontal_radio', 'frm_two_col', 'frm_three_col', 'frm_four_col' ]; | |
| 120 | + const containers = new Set(); | |
| 121 | + | |
| 122 | + activeForm.querySelectorAll( optionSelector ).forEach( option => { | |
| 123 | + const container = option.closest( '.frm_form_field' ); | |
| 124 | + | |
| 125 | + // Only fields using the style setting (no override) are marked in the styler preview. | |
| 126 | + if ( container?.classList.contains( 'frm-default-option-align' ) ) { | |
| 127 | + containers.add( container ); | |
| 128 | + } | |
| 129 | + } ); | |
| 130 | + | |
| 131 | + containers.forEach( container => { | |
| 132 | + container.classList.remove( ...alignClasses ); | |
| 133 | + container.classList.add( newClass ); | |
| 134 | + } ); | |
| 135 | + } | |
| 136 | + | |
| 137 | + /** | |
| 138 | + * Map an option layout style value to its front-end container class. | |
| 139 | + * | |
| 140 | + * @param {string} align The style alignment value. | |
| 141 | + * @return {string} The matching container class. | |
| 142 | + */ | |
| 143 | + function optionLayoutAlignToClass( align ) { | |
| 144 | + if ( 'inline' === align ) { | |
| 145 | + return 'horizontal_radio'; | |
| 146 | + } | |
| 147 | + | |
| 148 | + if ( 'block' === align ) { | |
| 149 | + return 'vertical_radio'; | |
| 150 | + } | |
| 151 | + | |
| 152 | + return align; | |
| 153 | + } | |
| 154 | + | |
| 155 | + /** | |
| 68 | 156 | * Initialize the slider functionality in the style preview. |
| 69 | 157 | * |
| 70 | 158 | * @param {HTMLElement} event |
| 71 | 159 | * @return {void} |
| @@ -95,11 +183,10 @@ | ||
| 95 | 183 | * @return {void} |
| 96 | 184 | */ |
| 97 | 185 | function initListPage() { |
| 98 | 186 | document.addEventListener( 'click', handleClickEventsForListPage ); |
| 99 | - // Add a timeout so Pro has a chance to add a filter first. | |
| 100 | - // 0 does not always work in Google Chrome, so use 1. | |
| 101 | - setTimeout( addHamburgerMenusToCards, 1 ); | |
| 187 | + // The dropdown options are added on the first open, so there is no longer a filter from Pro to wait for here. | |
| 188 | + addHamburgerMenusToCards(); | |
| 102 | 189 | initDatepickerSample(); |
| 103 | 190 | |
| 104 | 191 | const enableToggle = document.getElementById( 'frm_enable_styling' ); |
| 105 | 192 | const styleIdInput = getStyleIdInput(); |
| @@ -255,9 +342,9 @@ | ||
| 255 | 342 | * @param {Event} event |
| 256 | 343 | * @return {void} |
| 257 | 344 | */ |
| 258 | 345 | function handleCommonClickEvents( event ) { |
| 259 | - const target = event.target; | |
| 346 | + const { target } = event; | |
| 260 | 347 | |
| 261 | 348 | if ( 'frm_toggle_sample_form' === target.id || target.closest( '#frm_toggle_sample_form' ) ) { |
| 262 | 349 | toggleSampleForm(); |
| 263 | 350 | return; |
| @@ -281,9 +368,9 @@ | ||
| 281 | 368 | * @return {void} |
| 282 | 369 | */ |
| 283 | 370 | function switchAdvancedSettingsFormAction( target ) { |
| 284 | 371 | const form = document.querySelector( '#frm_styling_form' ); |
| 285 | - if ( null === form ) { | |
| 372 | + if ( ! form ) { | |
| 286 | 373 | return; |
| 287 | 374 | } |
| 288 | 375 | if ( target.closest( 'a#frm_style_back_to_quick_settings' ) ) { |
| 289 | 376 | form.action = form.action.replace( '§ion=advanced-settings', '' ); |
| @@ -321,9 +408,9 @@ | ||
| 321 | 408 | * @param {Event} event |
| 322 | 409 | * @return {void} |
| 323 | 410 | */ |
| 324 | 411 | function handleClickEventsForListPage( event ) { |
| 325 | - const target = event.target; | |
| 412 | + const { target } = event; | |
| 326 | 413 | |
| 327 | 414 | if ( target.classList.contains( 'frm-style-card' ) || target.closest( '.frm-style-card' ) ) { |
| 328 | 415 | handleStyleCardClick( event ); |
| 329 | 416 | } |
| @@ -336,9 +423,9 @@ | ||
| 336 | 423 | * @param {Event} event |
| 337 | 424 | * @return {void} |
| 338 | 425 | */ |
| 339 | 426 | function handleStyleCardClick( event ) { |
| 340 | - const target = event.target; | |
| 427 | + const { target } = event; | |
| 341 | 428 | |
| 342 | 429 | if ( target.closest( '.dropdown' ) ) { |
| 343 | 430 | // Ignore the hamburger menu inside of the card. |
| 344 | 431 | return; |
| @@ -371,13 +458,11 @@ | ||
| 371 | 458 | |
| 372 | 459 | sampleForm.classList.remove( activeCard.dataset.classname ); |
| 373 | 460 | sampleForm.classList.add( card.dataset.classname ); |
| 374 | 461 | |
| 375 | - if ( ! cardIsLocked ) { | |
| 376 | - // Don't update the form when a locked card is clicked. | |
| 377 | - styleIdInput.value = card.dataset.styleId; | |
| 378 | - trackListPageChange(); | |
| 379 | - } | |
| 462 | + // cardIsLocked is always false here due to early return above. | |
| 463 | + styleIdInput.value = card.dataset.styleId; | |
| 464 | + trackListPageChange(); | |
| 380 | 465 | |
| 381 | 466 | setTimeout( enableLabelTransitions, 1 ); |
| 382 | 467 | |
| 383 | 468 | // We want to toggle the edit button so you can only leave the page to edit the style if it's active (to avoid unsaved changes). |
| @@ -563,9 +648,9 @@ | ||
| 563 | 648 | return; |
| 564 | 649 | } |
| 565 | 650 | |
| 566 | 651 | const anchor = clickTarget.hasAttribute( 'href' ) ? clickTarget : clickTarget.querySelector( 'a[href]' ); |
| 567 | - anchor.setAttribute( 'href', anchor.getAttribute( 'href' ) + '&sample=1' ); | |
| 652 | + anchor.setAttribute( 'href', `${ anchor.getAttribute( 'href' ) }&sample=1` ); | |
| 568 | 653 | } |
| 569 | 654 | |
| 570 | 655 | /** |
| 571 | 656 | * Add menu dropdowns to style cards dynamically on load. |
| @@ -635,9 +720,73 @@ | ||
| 635 | 720 | hamburgerMenu.setAttribute( 'data-bs-container', 'body' ); |
| 636 | 721 | hamburgerMenu.setAttribute( 'role', 'button' ); |
| 637 | 722 | hamburgerMenu.setAttribute( 'tabindex', 0 ); |
| 638 | 723 | |
| 639 | - const isTemplate = 'undefined' !== typeof data.templateKey; | |
| 724 | + const isTemplate = data.templateKey !== undefined; | |
| 725 | + | |
| 726 | + const dropdownMenu = div( { | |
| 727 | + // Use dropdown-menu-right to avoid an overlapping issue with the card to the right (where the # of forms would appear above the menu). | |
| 728 | + className: 'frm-dropdown-menu frm-style-options-menu frm-p-1' | |
| 729 | + } ); | |
| 730 | + | |
| 731 | + const isRtl = document.body.classList.contains( 'rtl' ); | |
| 732 | + dropdownMenu.classList.add( `dropdown-menu-${ isRtl ? 'left' : 'right' }` ); | |
| 733 | + | |
| 734 | + dropdownMenu.setAttribute( 'role', 'menu' ); | |
| 735 | + | |
| 736 | + fillDropdownMenuOnFirstOpen( hamburgerMenu, dropdownMenu, data, isTemplate ); | |
| 737 | + | |
| 738 | + return div( { | |
| 739 | + className: 'dropdown frm_wrap', // The .frm_wrap class prevents a blue outline on the active dropdown trigger. | |
| 740 | + children: [ hamburgerMenu, dropdownMenu ] | |
| 741 | + } ); | |
| 742 | + } | |
| 743 | + | |
| 744 | + /** | |
| 745 | + * Add the options to a style card dropdown the first time that dropdown is opened. | |
| 746 | + * | |
| 747 | + * The options are built through the frm_style_card_dropdown_options filter, and Pro registers its | |
| 748 | + * callback while its own script file runs. Adding the options while the page loads raced that file, | |
| 749 | + * so a request for it that was slow to come back left every card holding the Lite only options. | |
| 750 | + * Waiting for the first open takes the script order out of it, since Pro has always registered the | |
| 751 | + * filter by the time a card can be clicked. | |
| 752 | + * | |
| 753 | + * @since 6.35 | |
| 754 | + * | |
| 755 | + * @param {HTMLElement} hamburgerMenu The dropdown trigger. | |
| 756 | + * @param {HTMLElement} dropdownMenu The dropdown to add the options to. | |
| 757 | + * @param {DOMStringMap} data The dataset of the style card, or an object with the style ID on the edit page. | |
| 758 | + * @param {boolean} isTemplate Whether the card is for a style template. | |
| 759 | + * @return {void} | |
| 760 | + */ | |
| 761 | + function fillDropdownMenuOnFirstOpen( hamburgerMenu, dropdownMenu, data, isTemplate ) { | |
| 762 | + const fillDropdownMenu = () => { | |
| 763 | + if ( dropdownMenu.children.length ) { | |
| 764 | + return; | |
| 765 | + } | |
| 766 | + | |
| 767 | + const options = getDropdownMenuOptions( data, isTemplate ); | |
| 768 | + dropdownMenu.append( ...options.map( wrapDropdownItem ) ); | |
| 769 | + }; | |
| 770 | + | |
| 771 | + // Bootstrap fires this on the trigger before it measures and positions the dropdown. | |
| 772 | + hamburgerMenu.addEventListener( 'show.bs.dropdown', fillDropdownMenu ); | |
| 773 | + | |
| 774 | + // Fall back to the click in case the dropdown is ever opened without Bootstrap. Listen in the | |
| 775 | + // capture phase so the options are in place before Bootstrap's own delegated handler runs. | |
| 776 | + hamburgerMenu.addEventListener( 'click', fillDropdownMenu, true ); | |
| 777 | + } | |
| 778 | + | |
| 779 | + /** | |
| 780 | + * Get the options for a single style card dropdown. | |
| 781 | + * | |
| 782 | + * @since 6.35 | |
| 783 | + * | |
| 784 | + * @param {DOMStringMap} data The dataset of the style card, or an object with the style ID on the edit page. | |
| 785 | + * @param {boolean} isTemplate Whether the card is for a style template. | |
| 786 | + * @return {Array} The dropdown options, each an object with an anchor and a type. | |
| 787 | + */ | |
| 788 | + function getDropdownMenuOptions( data, isTemplate ) { | |
| 640 | 789 | let dropdownMenuOptions = []; |
| 641 | 790 | |
| 642 | 791 | if ( isListPage ) { |
| 643 | 792 | const applyOption = a( { |
| @@ -678,23 +827,9 @@ | ||
| 678 | 827 | if ( isListPage && ! isTemplate ) { |
| 679 | 828 | maybeAddDuplicateUpsell( dropdownMenuOptions ); |
| 680 | 829 | } |
| 681 | 830 | |
| 682 | - const dropdownMenu = div( { | |
| 683 | - // Use dropdown-menu-right to avoid an overlapping issue with the card to the right (where the # of forms would appear above the menu). | |
| 684 | - className: 'frm-dropdown-menu frm-style-options-menu frm-p-1', | |
| 685 | - children: dropdownMenuOptions.map( wrapDropdownItem ) | |
| 686 | - } ); | |
| 687 | - | |
| 688 | - const isRtl = document.body.classList.contains( 'rtl' ); | |
| 689 | - dropdownMenu.classList.add( 'dropdown-menu-' + ( isRtl ? 'left' : 'right' ) ); | |
| 690 | - | |
| 691 | - dropdownMenu.setAttribute( 'role', 'menu' ); | |
| 692 | - | |
| 693 | - return div( { | |
| 694 | - className: 'dropdown frm_wrap', // The .frm_wrap class prevents a blue outline on the active dropdown trigger. | |
| 695 | - children: [ hamburgerMenu, dropdownMenu ] | |
| 696 | - } ); | |
| 831 | + return dropdownMenuOptions; | |
| 697 | 832 | } |
| 698 | 833 | |
| 699 | 834 | /** |
| 700 | 835 | * @param {Array} dropdownMenuOptions |
| @@ -799,9 +934,9 @@ | ||
| 799 | 934 | // Create a form so we can listen to Enter key presses that trigger a form submit event. |
| 800 | 935 | const form = tag( |
| 801 | 936 | 'form', |
| 802 | 937 | { |
| 803 | - child: labelledTextInput( 'frm_' + context + '_style_name_input', __( 'Style name', 'formidable' ), 'style_name' ) | |
| 938 | + child: labelledTextInput( `frm_${ context }_style_name_input`, __( 'Style name', 'formidable' ), 'style_name' ) | |
| 804 | 939 | } |
| 805 | 940 | ); |
| 806 | 941 | form.addEventListener( |
| 807 | 942 | 'submit', |
| @@ -876,8 +1011,26 @@ | ||
| 876 | 1011 | // The button gets disabled on an input event when the name is empty. |
| 877 | 1012 | return; |
| 878 | 1013 | } |
| 879 | 1014 | |
| 1015 | + if ( ! styleId || '0' === String( styleId ) ) { | |
| 1016 | + // A new or duplicated style has no ID yet, so there is nothing to rename on the server. | |
| 1017 | + // Update the post_title input (which overrides $_GET['style_name'] on save) and the | |
| 1018 | + // visible style name instead of calling the rename_style endpoint. | |
| 1019 | + const postTitleInput = document.querySelector( 'input[name="frm_style_setting[post_title]"]' ); | |
| 1020 | + if ( postTitleInput ) { | |
| 1021 | + postTitleInput.value = newStyleName; | |
| 1022 | + } | |
| 1023 | + | |
| 1024 | + const styleNameElement = document.getElementById( 'frm_style_name' ); | |
| 1025 | + if ( styleNameElement ) { | |
| 1026 | + styleNameElement.textContent = newStyleName; | |
| 1027 | + } | |
| 1028 | + | |
| 1029 | + success( __( 'Style has been renamed successfully', 'formidable' ) ); | |
| 1030 | + return; | |
| 1031 | + } | |
| 1032 | + | |
| 880 | 1033 | const formData = new FormData(); |
| 881 | 1034 | formData.append( 'style_id', styleId ); |
| 882 | 1035 | formData.append( 'style_name', newStyleName ); |
| 883 | 1036 | doJsonPost( 'rename_style', formData ).then( |
| @@ -914,21 +1067,13 @@ | ||
| 914 | 1067 | titleElement.textContent = newStyleName; |
| 915 | 1068 | } |
| 916 | 1069 | |
| 917 | 1070 | /** |
| 918 | - * @param {string} templateKey | |
| 919 | - * @return {HTMLElement} The template card element. | |
| 920 | - */ | |
| 921 | - function getTemplateCard( templateKey ) { | |
| 922 | - return document.getElementById( 'frm_template_style_cards_wrapper' ).querySelector( '.frm-style-card[data-template-key="' + templateKey + '"]' ); | |
| 923 | - } | |
| 924 | - | |
| 925 | - /** | |
| 926 | 1071 | * @param {string} styleId |
| 927 | 1072 | * @return {HTMLElement} The card element. |
| 928 | 1073 | */ |
| 929 | 1074 | function getCardByStyleId( styleId ) { |
| 930 | - const defaultCard = document.querySelector( '#frm_default_style_cards_wrapper > div[data-style-id="' + styleId + '"]' ); | |
| 1075 | + const defaultCard = document.querySelector( `#frm_default_style_cards_wrapper > div[data-style-id="${ styleId }"]` ); | |
| 931 | 1076 | if ( defaultCard ) { |
| 932 | 1077 | return defaultCard; |
| 933 | 1078 | } |
| 934 | 1079 | return Array.from( document.getElementById( 'frm_custom_style_cards_wrapper' ).children ).find( card => card.dataset.styleId === styleId ); |
| @@ -939,9 +1084,9 @@ | ||
| 939 | 1084 | * @param {string} iconId |
| 940 | 1085 | * @return {void} |
| 941 | 1086 | */ |
| 942 | 1087 | function addIconToOption( option, iconId ) { |
| 943 | - const icon = frmDom.svg( { href: '#' + iconId } ); | |
| 1088 | + const icon = frmDom.svg( { href: `#${ iconId }` } ); | |
| 944 | 1089 | option.insertBefore( icon, option.firstChild ); |
| 945 | 1090 | } |
| 946 | 1091 | |
| 947 | 1092 | /** |
| @@ -1059,9 +1204,9 @@ | ||
| 1059 | 1204 | defaultValues = JSON.parse( defaultValues ); |
| 1060 | 1205 | } |
| 1061 | 1206 | |
| 1062 | 1207 | for ( const key in defaultValues ) { |
| 1063 | - let targetInput = document.querySelector( 'input[name$="[' + key + ']"], select[name$="[' + key + ']"]' ); | |
| 1208 | + let targetInput = document.querySelector( `input[name$="[${ key }]"], select[name$="[${ key }]"]` ); | |
| 1064 | 1209 | if ( ! targetInput ) { |
| 1065 | 1210 | continue; |
| 1066 | 1211 | } |
| 1067 | 1212 | |
| @@ -1066,9 +1211,9 @@ | ||
| 1066 | 1211 | } |
| 1067 | 1212 | |
| 1068 | 1213 | if ( 'radio' === targetInput.getAttribute( 'type' ) ) { |
| 1069 | 1214 | // Reset the repeater icon dropdown. |
| 1070 | - targetInput = document.querySelector( 'input[name$="[' + key + ']"][value="' + defaultValues[ key ] + '"]' ); | |
| 1215 | + targetInput = document.querySelector( `input[name$="[${ key }]"][value="${ defaultValues[ key ] }"]` ); | |
| 1071 | 1216 | if ( targetInput ) { |
| 1072 | 1217 | targetInput.checked = true; |
| 1073 | 1218 | jQuery( targetInput ).trigger( 'change' ); |
| 1074 | 1219 | } |
| @@ -1117,9 +1262,9 @@ | ||
| 1117 | 1262 | |
| 1118 | 1263 | const newStyle = document.createElement( 'link' ); |
| 1119 | 1264 | newStyle.rel = 'stylesheet'; |
| 1120 | 1265 | newStyle.type = 'text/css'; |
| 1121 | - newStyle.href = style.href + '&key=' + getAutoId(); // Make the URL unique so the old stylesheet doesn't get picked up by cache. | |
| 1266 | + newStyle.href = `${ style.href }&key=${ getAutoId() }`; // Make the URL unique so the old stylesheet doesn't get picked up by cache. | |
| 1122 | 1267 | |
| 1123 | 1268 | // Listen for the new style to load before removing the old style to avoid having no styles while the new style is loading. |
| 1124 | 1269 | newStyle.addEventListener( |
| 1125 | 1270 | 'load', |
| @@ -1147,9 +1292,9 @@ | ||
| 1147 | 1292 | * @return {HTMLElement} The dropdown item element. |
| 1148 | 1293 | */ |
| 1149 | 1294 | function wrapDropdownItem( { anchor, type } ) { |
| 1150 | 1295 | return div( { |
| 1151 | - className: 'dropdown-item frm-' + type + '-style', | |
| 1296 | + className: `dropdown-item frm-${ type }-style`, | |
| 1152 | 1297 | child: anchor |
| 1153 | 1298 | } ); |
| 1154 | 1299 | } |
| 1155 | 1300 | |
| @@ -1177,29 +1322,86 @@ | ||
| 1177 | 1322 | [ 'frm_field_height', 'frm_field_font_size', 'frm_field_pad' ].forEach( selector => { |
| 1178 | 1323 | document.getElementById( selector ).addEventListener( 'change', debouncedTextSquishCheck ); |
| 1179 | 1324 | } ); |
| 1180 | 1325 | |
| 1181 | - jQuery( 'input.hex' ).wpColorPicker( { | |
| 1182 | - change: function( event, ui ) { | |
| 1183 | - let color = jQuery( this ).wpColorPicker( 'color' ); | |
| 1326 | + function detectColorFormat( value ) { | |
| 1327 | + if ( value.startsWith( 'rgba' ) ) { | |
| 1328 | + return 'rgba'; | |
| 1329 | + } | |
| 1330 | + if ( value.startsWith( 'rgb' ) ) { | |
| 1331 | + return 'rgb'; | |
| 1332 | + } | |
| 1333 | + if ( value.startsWith( 'hsla' ) ) { | |
| 1334 | + return 'hsla'; | |
| 1335 | + } | |
| 1336 | + if ( value.startsWith( 'hsl' ) ) { | |
| 1337 | + return 'hsl'; | |
| 1338 | + } | |
| 1339 | + return 'hex'; | |
| 1340 | + } | |
| 1341 | + | |
| 1342 | + const nativeInputValueDescriptor = Object.getOwnPropertyDescriptor( HTMLInputElement.prototype, 'value' ); | |
| 1343 | + | |
| 1344 | + jQuery( 'input.hex' ).each( function() { | |
| 1345 | + this.dataset.colorFormat = detectColorFormat( this.value ); | |
| 1346 | + | |
| 1347 | + // Prevent iris from overwriting non-hex formats with hex during color picking. | |
| 1348 | + const input = this; | |
| 1349 | + Object.defineProperty( input, 'value', { | |
| 1350 | + get() { | |
| 1351 | + return nativeInputValueDescriptor.get.call( this ); | |
| 1352 | + }, | |
| 1353 | + set( val ) { | |
| 1354 | + const format = input.dataset.colorFormat; | |
| 1355 | + if ( format && 'hex' !== format && /^#[0-9a-f]{3,8}$/i.test( val ) ) { | |
| 1356 | + return; | |
| 1357 | + } | |
| 1358 | + nativeInputValueDescriptor.set.call( this, val ); | |
| 1359 | + }, | |
| 1360 | + configurable: true | |
| 1361 | + } ); | |
| 1362 | + } ).on( 'keyup', function() { | |
| 1363 | + const newFormat = detectColorFormat( this.value ); | |
| 1364 | + if ( this.dataset.colorFormat !== newFormat ) { | |
| 1365 | + this.dataset.colorFormat = newFormat; | |
| 1366 | + const container = this.closest( '.wp-picker-container' ); | |
| 1367 | + if ( container ) { | |
| 1368 | + container.querySelector( '.wp-color-result-text' ).textContent = this.value; | |
| 1369 | + } | |
| 1370 | + debouncedColorChange( { target: this }, this.value ); | |
| 1371 | + } | |
| 1372 | + } ).wpColorPicker( { | |
| 1373 | + change( event, ui ) { | |
| 1374 | + const input = event.target; | |
| 1375 | + const format = input.dataset.colorFormat || 'hex'; | |
| 1376 | + let color; | |
| 1377 | + | |
| 1184 | 1378 | trackUnsavedChange(); |
| 1379 | + | |
| 1185 | 1380 | if ( ui.color._alpha < 1 ) { |
| 1186 | - // If there's transparency, use RGBA | |
| 1381 | + input.dataset.colorFormat = 'rgba'; | |
| 1187 | 1382 | color = ui.color.toCSS( 'rgba' ); |
| 1383 | + } else if ( 'hex' === format ) { | |
| 1384 | + color = ui.color.toString(); | |
| 1385 | + } else { | |
| 1386 | + color = ui.color.toCSS( format ); | |
| 1188 | 1387 | } |
| 1388 | + | |
| 1189 | 1389 | debouncedColorChange( event, color ); |
| 1190 | 1390 | |
| 1191 | - if ( null !== event.target.getAttribute( 'data-alpha-color-type' ) ) { | |
| 1391 | + input.value = color; | |
| 1392 | + | |
| 1393 | + if ( null !== input.getAttribute( 'data-alpha-color-type' ) ) { | |
| 1192 | 1394 | debouncedPreviewUpdate(); |
| 1193 | 1395 | return; |
| 1194 | 1396 | } |
| 1195 | 1397 | |
| 1196 | - jQuery( event.target ).val( color ).trigger( 'change' ); | |
| 1398 | + debouncedPreviewUpdate(); | |
| 1197 | 1399 | } |
| 1198 | 1400 | } ); |
| 1199 | 1401 | jQuery( '.wp-color-result-text' ).text( function( _, oldText ) { |
| 1200 | 1402 | const container = jQuery( this ).closest( '.wp-picker-container' ); |
| 1201 | - if ( 'undefined' !== typeof container && container[ 0 ].parentElement.classList.contains( 'frm-colorpicker' ) ) { | |
| 1403 | + if ( container !== undefined && container[ 0 ].parentElement.classList.contains( 'frm-colorpicker' ) ) { | |
| 1202 | 1404 | return container[ 0 ].querySelector( '.wp-color-picker' ).value; |
| 1203 | 1405 | } |
| 1204 | 1406 | return oldText === 'Select Color' ? 'Select' : oldText; |
| 1205 | 1407 | } ); |
| @@ -1248,9 +1450,9 @@ | ||
| 1248 | 1450 | * @return {void} |
| 1249 | 1451 | */ |
| 1250 | 1452 | function handleChangeStylingSuccess( css ) { |
| 1251 | 1453 | // Validate the string response. A valid output will include rules with .with_frm_style |
| 1252 | - if ( -1 === css.indexOf( '.with_frm_style' ) ) { | |
| 1454 | + if ( ! css.includes( '.with_frm_style' ) ) { | |
| 1253 | 1455 | // Handle error (possibly a permission error, or an outdated nonce). |
| 1254 | 1456 | alert( css ); |
| 1255 | 1457 | return; |
| 1256 | 1458 | } |
| @@ -1310,9 +1512,9 @@ | ||
| 1310 | 1512 | if ( select ) { |
| 1311 | 1513 | select.value = radio.value; |
| 1312 | 1514 | } |
| 1313 | 1515 | |
| 1314 | - jQuery( btnGrp ).children( 'button' ).html( radio.nextElementSibling.innerHTML + ' <b class="caret"></b>' ); | |
| 1516 | + jQuery( btnGrp ).children( 'button' ).html( `${ radio.nextElementSibling.innerHTML } <b class="caret"></b>` ); | |
| 1315 | 1517 | |
| 1316 | 1518 | const activeItem = btnGrp.querySelector( '.dropdown-item.active' ); |
| 1317 | 1519 | if ( activeItem ) { |
| 1318 | 1520 | activeItem.classList.remove( 'active' ); |
| @@ -1332,24 +1534,8 @@ | ||
| 1332 | 1534 | } |
| 1333 | 1535 | } |
| 1334 | 1536 | |
| 1335 | 1537 | /** |
| 1336 | - * @param {Event} event | |
| 1337 | - */ | |
| 1338 | - function maybeCollapseSettings( event ) { | |
| 1339 | - let expanded; | |
| 1340 | - const sectionParent = event.target.parentElement; | |
| 1341 | - if ( event.type === 'keydown' ) { | |
| 1342 | - expanded = sectionParent.classList.toggle( 'open' ); | |
| 1343 | - jQuery( sectionParent.querySelector( '.accordion-section-content' ) ).toggle( ! expanded ).slideToggle( 150 ); // Animate toggle as in click/enter. | |
| 1344 | - } else { | |
| 1345 | - expanded = sectionParent.classList.contains( 'open' ); | |
| 1346 | - } | |
| 1347 | - | |
| 1348 | - event.target.setAttribute( 'aria-expanded', expanded ); | |
| 1349 | - } | |
| 1350 | - | |
| 1351 | - /** | |
| 1352 | 1538 | * @param {HTMLElement} input |
| 1353 | 1539 | * @param {HTMLElement} container |
| 1354 | 1540 | * @return {void} |
| 1355 | 1541 | */ |
| @@ -1399,9 +1585,9 @@ | ||
| 1399 | 1585 | * @return {void} |
| 1400 | 1586 | */ |
| 1401 | 1587 | function setPosClass() { |
| 1402 | 1588 | /*jshint validthis:true */ |
| 1403 | - let value = this.value; | |
| 1589 | + let { value } = this; | |
| 1404 | 1590 | if ( value === 'none' ) { |
| 1405 | 1591 | value = 'top'; |
| 1406 | 1592 | } else if ( value === 'no_label' ) { |
| 1407 | 1593 | value = 'none'; |
| @@ -1412,9 +1598,9 @@ | ||
| 1412 | 1598 | const shouldForceTopStyling = 'inside' === value && ( ! input || 'hidden' === input.type ); // We do not want file upload to use floating labels, or inline datepickers, which both use hidden inputs. |
| 1413 | 1599 | const currentValue = shouldForceTopStyling ? 'top' : value; |
| 1414 | 1600 | |
| 1415 | 1601 | container.classList.remove( 'frm_top_container', 'frm_left_container', 'frm_right_container', 'frm_none_container', 'frm_inside_container' ); |
| 1416 | - container.classList.add( 'frm_' + currentValue + '_container' ); | |
| 1602 | + container.classList.add( `frm_${ currentValue }_container` ); | |
| 1417 | 1603 | |
| 1418 | 1604 | if ( 'inside' === currentValue ) { |
| 1419 | 1605 | checkFloatingLabelsForStyles( input, container ); |
| 1420 | 1606 | } |
| @@ -1442,9 +1628,9 @@ | ||
| 1442 | 1628 | * |
| 1443 | 1629 | * @return {void} |
| 1444 | 1630 | */ |
| 1445 | 1631 | function fillMissingSignatureValidationFunction() { |
| 1446 | - if ( 'undefined' === typeof window.__FRMSIG || 'undefined' !== typeof window.frmFrontForm ) { | |
| 1632 | + if ( window.__FRMSIG === undefined || window.frmFrontForm !== undefined ) { | |
| 1447 | 1633 | return; |
| 1448 | 1634 | } |
| 1449 | 1635 | |
| 1450 | 1636 | window.frmFrontForm = { validateFormSubmit: () => {} }; |
| @@ -1483,9 +1669,9 @@ | ||
| 1483 | 1669 | } |
| 1484 | 1670 | |
| 1485 | 1671 | // Function to change the color of a select element |
| 1486 | 1672 | const changeSelectColor = select => { |
| 1487 | - if ( select.options[ select.selectedIndex ] && select.options[ select.selectedIndex ].classList.contains( 'frm-select-placeholder' ) ) { | |
| 1673 | + if ( select.options[ select.selectedIndex ]?.classList.contains( 'frm-select-placeholder' ) ) { | |
| 1488 | 1674 | select.style.setProperty( 'color', textColorDisabled, 'important' ); |
| 1489 | 1675 | } else { |
| 1490 | 1676 | select.style.color = ''; |
| 1491 | 1677 | } |