# booking/11.9/includes/page-setup-wizard/step-publish-integration/_src/step-publish-integration.js

Booking Calendar, version 11.9. 397 lines.

- Page: https://pluginprobe.com/plugins/booking/11.9/code/includes/page-setup-wizard/step-publish-integration/_src/step-publish-integration.js
- Raw: https://pluginprobe.com/plugins/booking/11.9/raw/includes/page-setup-wizard/step-publish-integration/_src/step-publish-integration.js
- Modified: 2026-10-03T20:32:36+00:00

Line numbers below start at 1. Link to a line or a range by appending a fragment to the
page URL, for example `https://pluginprobe.com/plugins/booking/11.9/code/includes/page-setup-wizard/step-publish-integration/_src/step-publish-integration.js#L10-L20`.

```javascript
/**
 * Drive the progressive Publish & integrate Setup Wizard page.
 *
 * The adapter updates presentation fields and validates the current draft. It
 * never creates, publishes, or changes a WordPress page.
 *
 * @package Booking Calendar
 */
( function ( window, document ) {
	'use strict';

	var module_config = window.wpbc_setup_wizard_publish_integration || { i18n: {} };

	/**
	 * Create one isolated publishing-plan step adapter.
	 *
	 * @param {Object} config Localized messages.
	 * @return {Object} Adapter accepted by the shared wizard shell.
	 */
	function create_publish_integration_adapter( config ) {
		var shell_api = null;
		var editor = null;

		/**
		 * Return a localized message with a safe fallback.
		 *
		 * @param {string} key      Localized message key.
		 * @param {string} fallback English fallback.
		 * @return {string} Available message.
		 */
		function get_message( key, fallback ) {
			return config.i18n && config.i18n[ key ] ? String( config.i18n[ key ] ) : fallback;
		}

		/**
		 * Return the selected destination identifier.
		 *
		 * @return {string} Destination ID or an empty string.
		 */
		function get_destination() {
			var selected = editor ? editor.node.querySelector( '[data-wpbc-publish-destination]:checked' ) : null;

			return selected ? String( selected.value || '' ) : '';
		}

		/**
		 * Toggle an element's semantic hidden state.
		 *
		 * @param {HTMLElement|null} element   Element to update.
		 * @param {boolean}          is_hidden Whether the element is hidden.
		 * @return {void}
		 */
		function set_hidden( element, is_hidden ) {
			if ( ! element ) {
				return;
			}

			element.hidden = Boolean( is_hidden );
		}

		/**
		 * Build a safe root-relative URL preview from a page title.
		 *
		 * @param {string} page_title Proposed page title.
		 * @return {string} Root-relative preview URL.
		 */
		function build_page_url( page_title ) {
			var slug = '';
			var home_path = String( editor.node.dataset.homePath || '/' );

			if ( window.wp && window.wp.url && 'function' === typeof window.wp.url.cleanForSlug ) {
				slug = window.wp.url.cleanForSlug( page_title );
			} else {
				slug = String( page_title || '' )
					.toLowerCase()
					.trim()
					.replace( /[^a-z0-9\s-]/g, '' )
					.replace( /[\s-]+/g, '-' )
					.replace( /^-+|-+$/g, '' );
			}

			home_path = '/' + home_path.replace( /^\/+|\/+$/g, '' );
			if ( '/' !== home_path ) {
				home_path += '/';
			}

			return home_path + ( slug || 'booking' ) + '/';
		}

		/**
		 * Read the selected existing page's authorized display URL.
		 *
		 * @return {string} Root-relative page URL or an empty string.
		 */
		function get_existing_page_url() {
			var option = editor.existing_page && editor.existing_page.selectedIndex >= 0
				? editor.existing_page.options[ editor.existing_page.selectedIndex ]
				: null;

			return option ? String( option.dataset.pageUrl || '' ) : '';
		}

		/**
		 * Update the URL preview for the active page destination.
		 *
		 * @param {string} destination Active destination ID.
		 * @return {void}
		 */
		function update_page_url( destination ) {
			if ( ! editor.page_url ) {
				return;
			}

			if ( 'create_page' === destination ) {
				editor.page_url.value = build_page_url( editor.page_title ? editor.page_title.value : '' );
			} else if ( 'existing_page' === destination ) {
				editor.page_url.value = get_existing_page_url();
			} else {
				editor.page_url.value = '';
			}
		}

		/**
		 * Update journey-aware content details and shortcode presentation.
		 *
		 * @return {void}
		 */
		function update_content_details() {
			var option = editor.content && editor.content.selectedIndex >= 0
				? editor.content.options[ editor.content.selectedIndex ]
				: null;

			if ( ! option ) {
				return;
			}

			if ( editor.content_title ) {
				editor.content_title.textContent = option.textContent.trim();
			}
			if ( editor.content_description ) {
				editor.content_description.textContent = String( option.dataset.contentDescription || '' );
			}
			if ( editor.shortcode ) {
				editor.shortcode.value = String( option.dataset.contentShortcode || '' );
			}
			if ( editor.shortcode_help ) {
				editor.shortcode_help.href = String( option.dataset.contentHelpUrl || '' );
				editor.shortcode_help.hidden = ! option.dataset.contentHelpUrl;
			}
			if ( editor.shortcode_help_label ) {
				editor.shortcode_help_label.textContent = String( option.dataset.contentHelpLabel || '' );
			}
		}

		/**
		 * Return destination-specific approval notice copy.
		 *
		 * @param {string} destination Active destination ID.
		 * @return {string} Notice message.
		 */
		function get_notice( destination ) {
			if ( 'existing_page' === destination ) {
				return get_message( 'notice_existing', 'Save adds or updates one managed Booking Calendar block without replacing the page\'s other content.' );
			}
			if ( 'manual' === destination ) {
				return get_message( 'notice_manual', 'Save records this shortcode for the setup summary. No WordPress page is created or changed.' );
			}
			if ( 'later' === destination ) {
				return get_message( 'notice_later', 'No page will be created or changed. You can integrate the booking experience later.' );
			}

			return get_message( 'notice_create', 'Save this step to create the booking page now, or update the same page on a later save.' );
		}

		/**
		 * Return the server-localized primary action label for a destination.
		 *
		 * @param {string} destination Active destination ID.
		 * @return {string} Primary action label.
		 */
		function get_action_label( destination ) {
			if ( 'create_page' === destination ) {
				return '1' === editor.node.dataset.hasCreatedPage
					? get_message( 'action_update', 'Update page & continue' )
					: get_message( 'action_create', 'Create page & continue' );
			}
			if ( 'existing_page' === destination ) {
				return get_message( 'action_update', 'Update page & continue' );
			}

			return get_message( 'action_save', 'Save & continue' );
		}

		/**
		 * Synchronize all conditional fields with the selected destination.
		 *
		 * @return {void}
		 */
		function render_destination() {
			var destination = get_destination();
			var is_create = 'create_page' === destination;
			var is_existing = 'existing_page' === destination;
			var is_later = 'later' === destination;

			editor.cards.forEach( function ( card ) {
				var control = card.querySelector( '[data-wpbc-publish-destination]' );

				card.classList.toggle( 'is-selected', Boolean( control && control.checked ) );
			} );

			set_hidden( editor.create_field, ! is_create );
			set_hidden( editor.existing_field, ! is_existing );
			set_hidden( editor.url_field, ! is_create && ! is_existing );
			set_hidden( editor.settings_body, is_later );
			set_hidden( editor.later_panel, ! is_later );

			if ( editor.page_title ) {
				editor.page_title.required = is_create;
			}
			if ( editor.existing_page ) {
				editor.existing_page.required = is_existing;
			}
			if ( editor.content ) {
				editor.content.required = ! is_later;
			}
			if ( editor.notice_text ) {
				editor.notice_text.textContent = get_notice( destination );
			}
			if ( editor.continue_label ) {
				editor.continue_label.textContent = get_action_label( destination );
			}

			update_page_url( destination );
		}

		/**
		 * Copy text while preserving keyboard focus on the invoking button.
		 *
		 * @param {string}      text   Text to copy.
		 * @param {HTMLElement} button Invoking button.
		 * @return {Promise<void>} Completion promise.
		 */
		function copy_text( text, button ) {
			var clipboard_promise;

			if ( navigator.clipboard && 'function' === typeof navigator.clipboard.writeText ) {
				clipboard_promise = navigator.clipboard.writeText( text );
			} else {
				clipboard_promise = new Promise( function ( resolve, reject ) {
					var temporary = document.createElement( 'textarea' );

					temporary.value = text;
					temporary.setAttribute( 'readonly', 'readonly' );
					temporary.style.position = 'fixed';
					temporary.style.opacity = '0';
					document.body.appendChild( temporary );
					temporary.select();
					if ( document.execCommand( 'copy' ) ) {
						resolve();
					} else {
						reject( new Error( 'copy_failed' ) );
					}
					temporary.remove();
				} );
			}

			return clipboard_promise.then( function () {
				shell_api.set_status( get_message( 'copied', 'Copied to the clipboard.' ) );
				button.focus();
			} ).catch( function () {
				shell_api.set_status( get_message( 'copy_failed', 'Copy failed. Select the text and copy it manually.' ) );
				button.focus();
			} );
		}

		/**
		 * Handle destination and copy-button clicks.
		 *
		 * @param {MouseEvent} event Click event.
		 * @return {void}
		 */
		function handle_click( event ) {
			var copy_button = event.target.closest( '[data-wpbc-publish-copy]' );
			var copy_type;
			var copy_source;

			if ( ! copy_button || ! editor.node.contains( copy_button ) ) {
				return;
			}

			copy_type = String( copy_button.dataset.wpbcPublishCopy || '' );
			copy_source = 'page-url' === copy_type ? editor.page_url : editor.shortcode;
			if ( copy_source && copy_source.value ) {
				copy_text( String( copy_source.value ), copy_button );
			}
		}

		/**
		 * Handle select and radio changes.
		 *
		 * @param {Event} event Change event.
		 * @return {void}
		 */
		function handle_change( event ) {
			if ( event.target.matches( '[data-wpbc-publish-destination]' ) ) {
				render_destination();
			} else if ( event.target === editor.content ) {
				update_content_details();
			} else if ( event.target === editor.existing_page ) {
				update_page_url( get_destination() );
			}
		}

		/**
		 * Initialize the adapter against shell-owned services.
		 *
		 * @param {Object} registered_shell_api Shared wizard adapter services.
		 * @return {void}
		 */
		function initialize( registered_shell_api ) {
			var editor_node;

			shell_api = registered_shell_api;
			editor_node = shell_api.root.querySelector( '[data-wpbc-publish-integration-editor]' );
			if ( ! editor_node ) {
				return;
			}

			editor = {
				node: editor_node,
				cards: Array.prototype.slice.call( editor_node.querySelectorAll( '.wpbc_setup_wizard__publish-destination-card' ) ),
				create_field: editor_node.querySelector( '[data-wpbc-publish-create-field]' ),
				existing_field: editor_node.querySelector( '[data-wpbc-publish-existing-field]' ),
				url_field: editor_node.querySelector( '[data-wpbc-publish-page-url-field]' ),
				settings_body: editor_node.querySelector( '[data-wpbc-publish-settings-body]' ),
				later_panel: editor_node.querySelector( '[data-wpbc-publish-later-panel]' ),
				page_title: editor_node.querySelector( '[data-wpbc-publish-page-title]' ),
				existing_page: editor_node.querySelector( '[data-wpbc-publish-existing-page]' ),
				page_url: editor_node.querySelector( '[data-wpbc-publish-page-url]' ),
				content: editor_node.querySelector( '[data-wpbc-publish-page-content]' ),
				content_title: editor_node.querySelector( '[data-wpbc-publish-content-title]' ),
				content_description: editor_node.querySelector( '[data-wpbc-publish-content-description]' ),
				shortcode_help: editor_node.querySelector( '[data-wpbc-publish-shortcode-help]' ),
				shortcode_help_label: editor_node.querySelector( '[data-wpbc-publish-shortcode-help-label]' ),
				shortcode: editor_node.querySelector( '[data-wpbc-publish-shortcode]' ),
				notice_text: editor_node.querySelector( '[data-wpbc-publish-notice-text]' ),
				continue_label: shell_api.root.querySelector( '[data-wpbc-setup-wizard-direction="next"] > span' )
			};

			editor.node.addEventListener( 'click', handle_click );
			editor.node.addEventListener( 'change', handle_change );
			if ( editor.page_title ) {
				editor.page_title.addEventListener( 'input', function () {
					update_page_url( get_destination() );
				} );
			}

			render_destination();
			update_content_details();
		}

		/**
		 * Validate destination-specific fields before forward navigation.
		 *
		 * @return {HTMLElement|null} First invalid control or null.
		 */
		function validate() {
			var destination = get_destination();

			if ( 'create_page' === destination && ( ! editor.page_title || ! editor.page_title.value.trim() ) ) {
				return shell_api.set_field_error( 'publish_page_title', get_message( 'title_required', 'Enter a page title.' ) );
			}
			if ( 'existing_page' === destination && ( ! editor.existing_page || '0' === editor.existing_page.value ) ) {
				return shell_api.set_field_error( 'publish_existing_page_id', get_message( 'page_required', 'Choose a WordPress page.' ) );
			}
			if ( 'later' !== destination && ( ! editor.content || ! editor.content.value ) ) {
				return shell_api.set_field_error( 'publish_page_content', get_message( 'content_required', 'Choose the booking experience to display.' ) );
			}

			return null;
		}

		return {
			initialize: initialize,
			sync: render_destination,
			validate: validate
		};
	}

	window.wpbc_setup_editor_modules = window.wpbc_setup_editor_modules || {};
	window.wpbc_setup_editor_modules.publish_integration = { create: create_publish_integration_adapter };

	if ( window.wpbc_setup_wizard_api && 'function' === typeof window.wpbc_setup_wizard_api.register_step_adapter ) {
		window.wpbc_setup_wizard_api.register_step_adapter( create_publish_integration_adapter( module_config ) );
	}
}( window, document ) );

```
