+ /**
+ * Determine whether the user agent is iOS.
+ *
+ * @since 4.4.0
+ * @access public
+ *
+ * @return bool Whether the user agent is iOS.
+ */
+ public function is_ios() {
+ return wp_is_mobile() && preg_match( '/iPad|iPod|iPhone/', $_SERVER['HTTP_USER_AGENT'] );
+ }
+
+ /**
+ * Get the template string for the Customizer pane document title.
+ *
+ * @since 4.4.0
+ * @access public
+ *
+ * @return string The template string for the document title.
+ */
+ public function get_document_title_template() {
+ if ( $this->is_theme_active() ) {
+ /* translators: %s: document title from the preview */
+ $document_title_tmpl = __( 'Customize: %s' );
+ } else {
+ /* translators: %s: document title from the preview */
+ $document_title_tmpl = __( 'Live Preview: %s' );
+ }
+ $document_title_tmpl = html_entity_decode( $document_title_tmpl, ENT_QUOTES, 'UTF-8' ); // Because exported to JS and assigned to document.title.
+ return $document_title_tmpl;
+ }
+
+ /**
+ * Set the initial URL to be previewed.
+ *
+ * URL is validated.
+ *
+ * @since 4.4.0
+ * @access public
+ *
+ * @param string $preview_url URL to be previewed.
+ */
+ public function set_preview_url( $preview_url ) {
+ $preview_url = esc_url_raw( $preview_url );
+ $this->preview_url = wp_validate_redirect( $preview_url, home_url( '/' ) );
+ }
+
+ /**
+ * Get the initial URL to be previewed.
+ *
+ * @since 4.4.0
+ * @access public
+ *
+ * @return string URL being previewed.
+ */
+ public function get_preview_url() {
+ if ( empty( $this->preview_url ) ) {
+ $preview_url = home_url( '/' );
+ } else {
+ $preview_url = $this->preview_url;
+ }
+ return $preview_url;
+ }
+
+ /**
+ * Set URL to link the user to when closing the Customizer.
+ *
+ * URL is validated.
+ *
+ * @since 4.4.0
+ * @access public
+ *
+ * @param string $return_url URL for return link.
+ */
+ public function set_return_url( $return_url ) {
+ $return_url = esc_url_raw( $return_url );
+ $return_url = remove_query_arg( wp_removable_query_args(), $return_url );
+ $return_url = wp_validate_redirect( $return_url );
+ $this->return_url = $return_url;
+ }
+
+ /**
+ * Get URL to link the user to when closing the Customizer.
+ *
+ * @since 4.4.0
+ * @access public
+ *
+ * @return string URL for link to close Customizer.
+ */
+ public function get_return_url() {
+ $referer = wp_get_referer();
+ $excluded_referer_basenames = array( 'customize.php', 'wp-login.php' );
+
+ if ( $this->return_url ) {
+ $return_url = $this->return_url;
+ } else if ( $referer && ! in_array( basename( parse_url( $referer, PHP_URL_PATH ) ), $excluded_referer_basenames, true ) ) {
+ $return_url = $referer;
+ } else if ( $this->preview_url ) {
+ $return_url = $this->preview_url;
+ } else {
+ $return_url = home_url( '/' );
+ }
+ return $return_url;
+ }
+
+ /**
+ * Set the autofocused constructs.
+ *
+ * @since 4.4.0
+ * @access public
+ *
+ * @param array $autofocus {
+ * Mapping of 'panel', 'section', 'control' to the ID which should be autofocused.
+ *
+ * @type string [$control] ID for control to be autofocused.
+ * @type string [$section] ID for section to be autofocused.
+ * @type string [$panel] ID for panel to be autofocused.
+ * }
+ */
+ public function set_autofocus( $autofocus ) {
+ $this->autofocus = array_filter( wp_array_slice_assoc( $autofocus, array( 'panel', 'section', 'control' ) ), 'is_string' );
+ }
+
+ /**
+ * Get the autofocused constructs.
+ *
+ * @since 4.4.0
+ * @access public
+ *
+ * @return array {
+ * Mapping of 'panel', 'section', 'control' to the ID which should be autofocused.
+ *
+ * @type string [$control] ID for control to be autofocused.
+ * @type string [$section] ID for section to be autofocused.
+ * @type string [$panel] ID for panel to be autofocused.
+ * }
+ */
+ public function get_autofocus() {
+ return $this->autofocus;
+ }
+
+ /**
+ * Get nonces for the Customizer.
+ *
+ * @since 4.5.0
+ * @return array Nonces.
+ */
+ public function get_nonces() {
+ $nonces = array(
+ 'save' => wp_create_nonce( 'save-customize_' . $this->get_stylesheet() ),
+ 'preview' => wp_create_nonce( 'preview-customize_' . $this->get_stylesheet() ),
+ );
+
+ /**
+ * Filters nonces for Customizer.
+ *
+ * @since 4.2.0
+ *
+ * @param array $nonces Array of refreshed nonces for save and
+ * preview actions.
+ * @param WP_Customize_Manager $this WP_Customize_Manager instance.
+ */
+ $nonces = apply_filters( 'customize_refresh_nonces', $nonces, $this );
+
+ return $nonces;
+ }
+
+ /**
+ * Print JavaScript settings for parent window.
+ *
+ * @since 4.4.0
+ */
+ public function customize_pane_settings() {
+ /*
+ * If the front end and the admin are served from the same domain, load the
+ * preview over ssl if the Customizer is being loaded over ssl. This avoids
+ * insecure content warnings. This is not attempted if the admin and front end
+ * are on different domains to avoid the case where the front end doesn't have
+ * ssl certs. Domain mapping plugins can allow other urls in these conditions
+ * using the customize_allowed_urls filter.
+ */
+
+ $allowed_urls = array( home_url( '/' ) );
+ $admin_origin = parse_url( admin_url() );
+ $home_origin = parse_url( home_url() );
+ $cross_domain = ( strtolower( $admin_origin['host'] ) !== strtolower( $home_origin['host'] ) );
+
+ if ( is_ssl() && ! $cross_domain ) {
+ $allowed_urls[] = home_url( '/', 'https' );
+ }
+
+ /**
+ * Filters the list of URLs allowed to be clicked and followed in the Customizer preview.
+ *
+ * @since 3.4.0
+ *
+ * @param array $allowed_urls An array of allowed URLs.
+ */
+ $allowed_urls = array_unique( apply_filters( 'customize_allowed_urls', $allowed_urls ) );
+
+ $login_url = add_query_arg( array(
+ 'interim-login' => 1,
+ 'customize-login' => 1,
+ ), wp_login_url() );
+
+ // Prepare Customizer settings to pass to JavaScript.
+ $settings = array(
+ 'theme' => array(
+ 'stylesheet' => $this->get_stylesheet(),
+ 'active' => $this->is_theme_active(),
+ ),
+ 'url' => array(
+ 'preview' => esc_url_raw( $this->get_preview_url() ),
+ 'parent' => esc_url_raw( admin_url() ),
+ 'activated' => esc_url_raw( home_url( '/' ) ),
+ 'ajax' => esc_url_raw( admin_url( 'admin-ajax.php', 'relative' ) ),
+ 'allowed' => array_map( 'esc_url_raw', $allowed_urls ),
+ 'isCrossDomain' => $cross_domain,
+ 'home' => esc_url_raw( home_url( '/' ) ),
+ 'login' => esc_url_raw( $login_url ),
+ ),
+ 'browser' => array(
+ 'mobile' => wp_is_mobile(),
+ 'ios' => $this->is_ios(),
+ ),
+ 'panels' => array(),
+ 'sections' => array(),
+ 'nonce' => $this->get_nonces(),
+ 'autofocus' => $this->get_autofocus(),
+ 'documentTitleTmpl' => $this->get_document_title_template(),
+ 'previewableDevices' => $this->get_previewable_devices(),
+ );
+
+ // Prepare Customize Section objects to pass to JavaScript.
+ foreach ( $this->sections() as $id => $section ) {
+ if ( $section->check_capabilities() ) {
+ $settings['sections'][ $id ] = $section->json();
+ }
+ }
+
+ // Prepare Customize Panel objects to pass to JavaScript.
+ foreach ( $this->panels() as $panel_id => $panel ) {
+ if ( $panel->check_capabilities() ) {
+ $settings['panels'][ $panel_id ] = $panel->json();
+ foreach ( $panel->sections as $section_id => $section ) {
+ if ( $section->check_capabilities() ) {
+ $settings['sections'][ $section_id ] = $section->json();
+ }
+ }
+ }
+ }
+
+ ?>
+ <script type="text/javascript">
+ var _wpCustomizeSettings = <?php echo wp_json_encode( $settings ); ?>;
+ _wpCustomizeSettings.controls = {};
+ _wpCustomizeSettings.settings = {};
+ <?php
+
+ // Serialize settings one by one to improve memory usage.
+ echo "(function ( s ){\n";
+ foreach ( $this->settings() as $setting ) {
+ if ( $setting->check_capabilities() ) {
+ printf(
+ "s[%s] = %s;\n",
+ wp_json_encode( $setting->id ),
+ wp_json_encode( $setting->json() )
+ );
+ }
+ }
+ echo "})( _wpCustomizeSettings.settings );\n";
+
+ // Serialize controls one by one to improve memory usage.
+ echo "(function ( c ){\n";
+ foreach ( $this->controls() as $control ) {
+ if ( $control->check_capabilities() ) {
+ printf(
+ "c[%s] = %s;\n",
+ wp_json_encode( $control->id ),
+ wp_json_encode( $control->json() )
+ );
+ }
+ }
+ echo "})( _wpCustomizeSettings.controls );\n";
+ ?>
+ </script>
+ <?php
+ }
+
+ /**
+ * Returns a list of devices to allow previewing.
+ *
+ * @access public
+ * @since 4.5.0
+ *
+ * @return array List of devices with labels and default setting.
+ */
+ public function get_previewable_devices() {
+ $devices = array(
+ 'desktop' => array(
+ 'label' => __( 'Enter desktop preview mode' ),
+ 'default' => true,
+ ),
+ 'tablet' => array(
+ 'label' => __( 'Enter tablet preview mode' ),
+ ),
+ 'mobile' => array(
+ 'label' => __( 'Enter mobile preview mode' ),
+ ),
+ );
+
+ /**
+ * Filters the available devices to allow previewing in the Customizer.
+ *
+ * @since 4.5.0
+ *
+ * @see WP_Customize_Manager::get_previewable_devices()
+ *
+ * @param array $devices List of devices with labels and default setting.
+ */
+ $devices = apply_filters( 'customize_previewable_devices', $devices );
+
+ return $devices;
+ }
+