array(), 'style' => array(), 'custom-css' => array(), ); /** * Already added custom assets in head. * * @var boolean */ private static $already_added_custom_assets = false; /** * List with assets to skip from Autoptimize when CSS generated. * * @var array */ protected static $skip_from_autoptimize = array( 'ghostkit-blocks-content-custom-css', 'ghostkit-blocks-widget-custom-css', ); /** * Visual_Portfolio_Extend constructor. */ public function __construct() { add_action( 'init', array( $this, 'register_scripts' ) ); add_action( 'plugins_loaded', array( $this, 'enqueue_scripts_action' ), 11 ); add_action( 'ghostkit_parse_blocks', array( $this, 'maybe_enqueue_blocks_assets' ), 11, 2 ); add_action( 'wp_enqueue_scripts', array( $this, 'add_custom_assets' ), 100 ); add_action( 'autoptimize_filter_css_exclude', 'GhostKit_Assets::autoptimize_filter_css_exclude' ); } /** * Store used assets, so we can enqueue it later. * * @param string $name - asset name. * @param bool|string $value - just enqueue flag or url to asset. * @param string $type - assets type [script|style|custom-css]. * @param int $priority - asset enqueue priority. */ public static function store_used_assets( $name, $value = true, $type = 'script', $priority = 10 ) { if ( ! isset( self::$stored_assets[ $type ] ) ) { return; } if ( isset( self::$stored_assets[ $type ][ $name ] ) ) { if ( 'custom-css' === $type ) { if ( ! self::$stored_assets[ $type ][ $name ]['value'] ) { self::$stored_assets[ $type ][ $name ]['value'] = ''; } self::$stored_assets[ $type ][ $name ]['value'] .= $value; } do_action( 'gkt_assets_store', $name, $value, $type, $priority ); return; } self::$stored_assets[ $type ][ $name ] = array( 'value' => $value, 'priority' => $priority, ); do_action( 'gkt_assets_store', $name, $value, $type, $priority ); } /** * Enqueue stored assets. * * @param string $type - assets type [script|style|custom-css]. */ public static function enqueue_stored_assets( $type = 'script' ) { if ( ! isset( self::$stored_assets[ $type ] ) || empty( self::$stored_assets[ $type ] ) ) { return; } uasort( self::$stored_assets[ $type ], function ( $a, $b ) { if ( $a === $b ) { return 0; } if ( isset( $a['priority'] ) && isset( $b['priority'] ) ) { return $a['priority'] < $b['priority'] ? -1 : 1; } return 0; } ); foreach ( self::$stored_assets[ $type ] as $name => $data ) { if ( isset( $data['value'] ) && $data['value'] ) { if ( 'script' === $type ) { wp_enqueue_script( $name ); } elseif ( 'style' === $type ) { wp_enqueue_style( $name ); } elseif ( 'custom-css' === $type ) { self::add_custom_css( $name, $data['value'] ); } self::$stored_assets[ $type ]['value'] = false; } } } /** * Enqueue assets for blocks. * * @param array $blocks - blocks array. * @param string $location - blocks location [content,widget]. */ public static function enqueue( $blocks = array(), $location = 'content' ) { self::store_used_assets( 'ghostkit', true, 'style', 9 ); self::store_used_assets( 'ghostkit', true, 'script', 11 ); // Prepare blocks assets. foreach ( $blocks as $block ) { // GhostKit blocks. if ( isset( $block['blockName'] ) && strpos( $block['blockName'], 'ghostkit/' ) === 0 ) { $block_name = preg_replace( '/^ghostkit\//', '', $block['blockName'] ); self::store_used_assets( 'ghostkit-block-' . $block_name, true, 'style' ); self::store_used_assets( 'ghostkit-block-' . $block_name, true, 'script' ); } } // Blocks custom CSS. $blocks_css = self::parse_blocks_css( $blocks ); if ( ! empty( $blocks_css ) ) { self::store_used_assets( 'ghostkit-blocks-' . $location . '-custom-css', ghostkit()->replace_vars( $blocks_css ), 'custom-css' ); } } /** * Enqueue scripts and styles actions. * We need this to be used inside 'plugins_loaded' action to prevent conflicts with plugins and themes. * * 1. Enqueue plugins assets * 2. Enqueue Ghost Kit assets * 3. Enqueue theme assets * 4. Enqueue Ghost Kit custom css */ public function enqueue_scripts_action() { add_action( 'wp_enqueue_scripts', array( $this, 'wp_enqueue_head_assets' ) ); add_action( 'wp_footer', array( $this, 'wp_enqueue_foot_assets' ) ); // Custom css with higher priority to overwrite theme styles. add_action( 'wp_enqueue_scripts', array( $this, 'wp_enqueue_head_custom_assets' ), 11 ); add_action( 'wp_footer', array( $this, 'wp_enqueue_foot_custom_assets' ), 11 ); } /** * Register scripts that will be used in the future when portfolio will be printed. */ public function register_scripts() { $css_deps = array(); $js_deps = array( 'jquery', 'ghostkit-helper' ); do_action( 'gkt_before_assets_register' ); // ScrollReveal. if ( apply_filters( 'gkt_enqueue_plugin_scrollreveal', true ) ) { // We need to use previous version, as in 4.0.9 `cleanup` is not working at all. wp_register_script( 'scrollreveal', ghostkit()->plugin_url . 'assets/vendor/scrollreveal-4-0-7/scrollreveal.min.js', array(), '4.0.7', true ); $js_deps[] = 'scrollreveal'; } // Jarallax. if ( apply_filters( 'gkt_enqueue_plugin_jarallax', true ) ) { wp_register_script( 'jarallax', ghostkit()->plugin_url . 'assets/vendor/jarallax/dist/jarallax.min.js', array( 'jquery' ), '2.0.1', true ); wp_register_script( 'jarallax-video', ghostkit()->plugin_url . 'assets/vendor/jarallax/dist/jarallax-video.min.js', array( 'jarallax' ), '2.0.1', true ); } // Swiper. if ( apply_filters( 'gkt_enqueue_plugin_swiper', true ) ) { // Add legacy swiper version in order to support Elementor plugin. // https://wordpress.org/support/topic/visual-portfolio-elementor-issue/. if ( class_exists( '\Elementor\Plugin' ) ) { wp_register_style( 'swiper', ghostkit()->plugin_url . 'assets/vendor/swiper-5-4-5/swiper.min.css', array(), '5.4.5' ); wp_register_script( 'swiper', ghostkit()->plugin_url . 'assets/vendor/swiper-5-4-5/swiper.min.js', array(), '5.4.5', true ); } else { wp_register_style( 'swiper', ghostkit()->plugin_url . 'assets/vendor/swiper/swiper-bundle.min.css', array(), '8.4.0' ); wp_register_script( 'swiper', ghostkit()->plugin_url . 'assets/vendor/swiper/swiper-bundle.min.js', array(), '8.4.0', true ); } } // Luxon. if ( apply_filters( 'gkt_enqueue_plugin_luxon', true ) ) { wp_register_script( 'luxon', ghostkit()->plugin_url . 'assets/vendor/luxon/build/global/luxon.min.js', array(), '3.0.1', true ); } // GistEmbed. if ( apply_filters( 'gkt_enqueue_plugin_gist_simple', true ) ) { wp_register_style( 'gist-simple', ghostkit()->plugin_url . 'assets/vendor/gist-simple/dist/gist-simple.css', array(), '1.0.1' ); wp_register_script( 'gist-simple', ghostkit()->plugin_url . 'assets/vendor/gist-simple/dist/gist-simple.min.js', array( 'jquery' ), '1.0.1', true ); } // Google reCaptcha. if ( apply_filters( 'gkt_enqueue_google_recaptcha', true ) ) { $recaptcha_site_key = get_option( 'ghostkit_google_recaptcha_api_site_key' ); $recaptcha_secret_key = get_option( 'ghostkit_google_recaptcha_api_secret_key' ); if ( $recaptcha_site_key && $recaptcha_secret_key ) { wp_register_script( 'google-recaptcha', 'https://www.google.com/recaptcha/api.js?render=' . esc_attr( $recaptcha_site_key ), array(), '3.0.0', true ); } } // Parsley. if ( apply_filters( 'gkt_enqueue_plugin_parsley', true ) ) { wp_register_script( 'parsley', ghostkit()->plugin_url . 'assets/vendor/parsleyjs/dist/parsley.min.js', array( 'jquery' ), '2.9.2', true ); $locale = get_locale(); // phpcs:disable wp_add_inline_script( 'parsley', 'Parsley.addMessages("' . esc_attr( $locale ) . '", { defaultMessage: "' . esc_attr__( 'This value seems to be invalid.', 'ghostkit' ) . '", type: { email: "' . esc_attr__( 'This value should be a valid email.', 'ghostkit' ) . '", url: "' . esc_attr__( 'This value should be a valid url.', 'ghostkit' ) . '", number: "' . esc_attr__( 'This value should be a valid number.', 'ghostkit' ) . '", integer: "' . esc_attr__( 'This value should be a valid integer.', 'ghostkit' ) . '", digits: "' . esc_attr__( 'This value should be digits.', 'ghostkit' ) . '", alphanum: "' . esc_attr__( 'This value should be alphanumeric.', 'ghostkit' ) . '" }, notblank: "' . esc_attr__( 'This value should not be blank.', 'ghostkit' ) . '", required: "' . esc_attr__( 'This value is required.', 'ghostkit' ) . '", pattern: "' . esc_attr__( 'This value seems to be invalid.', 'ghostkit' ) . '", min: "' . esc_attr__( 'This value should be greater than or equal to %s.', 'ghostkit' ) . '", max: "' . esc_attr__( 'This value should be lower than or equal to %s.', 'ghostkit' ) . '", range: "' . esc_attr__( 'This value should be between %s and %s.', 'ghostkit' ) . '", minlength: "' . esc_attr__( 'This value is too short. It should have %s characters or more.', 'ghostkit' ) . '", maxlength: "' . esc_attr__( 'This value is too long. It should have %s characters or fewer.', 'ghostkit' ) . '", length: "' . esc_attr__( 'This value length is invalid. It should be between %s and %s characters long.', 'ghostkit' ) . '", mincheck: "' . esc_attr__( 'You must select at least %s choices.', 'ghostkit' ) . '", maxcheck: "' . esc_attr__( 'You must select %s choices or fewer.', 'ghostkit' ) . '", check: "' . esc_attr__( 'You must select between %s and %s choices.', 'ghostkit' ) . '", equalto: "' . esc_attr__( 'This value should be the same.', 'ghostkit' ) . '", euvatin: "' . esc_attr__( 'It\'s not a valid VAT Identification Number.', 'ghostkit' ) . '", confirmEmail: "' . esc_attr__( 'These emails should match.', 'ghostkit' ) . '", }); Parsley.setLocale("' . esc_attr( $locale ) . '");', 'after' ); // phpcs:enable } // Get all sidebars. $sidebars = array(); if ( ! empty( $GLOBALS['wp_registered_sidebars'] ) ) { foreach ( $GLOBALS['wp_registered_sidebars'] as $k => $sidebar ) { $sidebars[ $k ] = array( 'id' => $sidebar['id'], 'name' => $sidebar['name'], ); } } // helper script. wp_register_script( 'ghostkit-helper', ghostkit()->plugin_url . 'assets/js/helper.min.js', array( 'jquery' ), '2.25.0', true ); $default_variant = array( 'default' => array( 'title' => esc_html__( 'Default', 'ghostkit' ), ), ); // Google Maps prepare localization as in WordPress settings. $gmaps_locale = get_locale(); $gmaps_suffix = '.com'; switch ( $gmaps_locale ) { case 'he_IL': // Hebrew correction. $gmaps_locale = 'iw'; break; case 'zh_CN': // Chinese integration. $gmaps_suffix = '.cn'; break; } $gmaps_locale = substr( $gmaps_locale, 0, 2 ); $theme_data = wp_get_theme( get_template() ); $breakpoints = GhostKit_Breakpoints::get_breakpoints(); $timezone = wp_timezone_string(); if ( substr( $timezone, 0, 1 ) === '+' || substr( $timezone, 0, 1 ) === '-' ) { $timezone = 'UTC' . $timezone; } wp_localize_script( 'ghostkit-helper', 'ghostkitVariables', array( 'themeName' => $theme_data->get( 'Name' ), 'settings' => get_option( 'ghostkit_settings', array() ), 'disabledBlocks' => get_option( 'ghostkit_disabled_blocks', array() ), // TODO: Due to different formats in scss and assets there is an offset. 'media_sizes' => array( 'sm' => $breakpoints['xs'], 'md' => $breakpoints['sm'], 'lg' => $breakpoints['md'], 'xl' => $breakpoints['lg'], ), 'timezone' => $timezone, 'googleMapsAPIKey' => get_option( 'ghostkit_google_maps_api_key' ), 'googleMapsAPIUrl' => 'https://maps.googleapis' . $gmaps_suffix . '/maps/api/js?v=3.exp&language=' . esc_attr( $gmaps_locale ), 'googleMapsLibrary' => apply_filters( 'gkt_enqueue_plugin_gmaps', true ) ? array( 'url' => ghostkit()->plugin_url . 'assets/vendor/gmaps/gmaps.min.js?ver=0.4.25', ) : false, 'googleReCaptchaAPISiteKey' => get_option( 'ghostkit_google_recaptcha_api_site_key' ), 'googleReCaptchaAPISecretKey' => is_admin() ? get_option( 'ghostkit_google_recaptcha_api_secret_key' ) : '', 'sidebars' => $sidebars, 'icons' => is_admin() ? apply_filters( 'gkt_icons_list', array() ) : array(), 'shapes' => is_admin() ? apply_filters( 'gkt_shapes_list', array() ) : array(), 'fonts' => is_admin() ? apply_filters( 'gkt_fonts_list', array() ) : array(), 'customTypographyList' => is_admin() ? apply_filters( 'gkt_custom_typography', array() ) : array(), 'variants' => array( 'accordion' => array_merge( $default_variant, apply_filters( 'gkt_accordion_variants', array() ) ), 'accordion_item' => array_merge( $default_variant, apply_filters( 'gkt_accordion_item_variants', array() ) ), 'alert' => array_merge( $default_variant, apply_filters( 'gkt_alert_variants', array() ) ), 'button_wrapper' => array_merge( $default_variant, apply_filters( 'gkt_button_wrapper_variants', array() ) ), 'button' => array_merge( $default_variant, apply_filters( 'gkt_button_variants', array() ) ), 'carousel' => array_merge( $default_variant, apply_filters( 'gkt_carousel_variants', array() ) ), 'carousel_slide' => array_merge( $default_variant, apply_filters( 'gkt_carousel_slide_variants', array() ) ), 'changelog' => array_merge( $default_variant, apply_filters( 'gkt_changelog_variants', array() ) ), 'counter_box' => array_merge( $default_variant, apply_filters( 'gkt_counter_box_variants', array() ) ), 'divider' => array_merge( $default_variant, apply_filters( 'gkt_divider_variants', array() ) ), 'gist' => array_merge( $default_variant, apply_filters( 'gkt_gist_variants', array() ) ), 'google_maps' => array_merge( $default_variant, apply_filters( 'gkt_google_maps_variants', array() ) ), 'grid' => array_merge( $default_variant, apply_filters( 'gkt_grid_variants', array() ) ), 'grid_column' => array_merge( $default_variant, apply_filters( 'gkt_grid_column_variants', array() ) ), 'icon_box' => array_merge( $default_variant, apply_filters( 'gkt_icon_box_variants', array() ) ), 'instagram' => array_merge( $default_variant, apply_filters( 'gkt_instagram_variants', array() ) ), 'pricing_table' => array_merge( $default_variant, apply_filters( 'gkt_pricing_table_variants', array() ) ), 'pricing_table_item' => array_merge( $default_variant, apply_filters( 'gkt_pricing_table_item_variants', array() ) ), 'progress' => array_merge( $default_variant, apply_filters( 'gkt_progress_variants', array() ) ), 'tabs' => array_merge( $default_variant, apply_filters( 'gkt_tabs_variants', array() ) ), 'tabs_tab' => array_merge( $default_variant, apply_filters( 'gkt_tabs_tab_variants', array() ) ), 'testimonial' => array_merge( $default_variant, apply_filters( 'gkt_testimonial_variants', array() ) ), 'twitter' => array_merge( $default_variant, apply_filters( 'gkt_twitter_variants', array() ) ), 'video' => array_merge( $default_variant, apply_filters( 'gkt_video_variants', array() ) ), ), 'admin_url' => admin_url(), 'admin_templates_url' => admin_url( 'edit.php?post_type=ghostkit_template' ), ) ); // Ghost Kit. wp_register_style( 'ghostkit', ghostkit()->plugin_url . 'gutenberg/style.min.css', $css_deps, '2.25.0' ); wp_style_add_data( 'ghostkit', 'rtl', 'replace' ); wp_style_add_data( 'ghostkit', 'suffix', '.min' ); wp_register_script( 'ghostkit', ghostkit()->plugin_url . 'assets/js/main.min.js', $js_deps, '2.25.0', true ); // Blocks. foreach ( glob( ghostkit()->plugin_path . 'gutenberg/blocks/*/frontend.min.js' ) as $template ) { $block_name = basename( dirname( $template ) ); $block_script_url = ghostkit()->plugin_url . 'gutenberg/blocks/' . $block_name . '/frontend.min.js'; $block_js_deps = array( 'ghostkit', 'jquery' ); switch ( $block_name ) { case 'grid': case 'grid-column': if ( wp_script_is( 'jarallax-video' ) || wp_script_is( 'jarallax-video', 'registered' ) ) { $block_js_deps[] = 'jarallax-video'; } if ( wp_script_is( 'jarallax' ) || wp_script_is( 'jarallax', 'registered' ) ) { $block_js_deps[] = 'jarallax'; } break; case 'video': if ( wp_script_is( 'jarallax-video' ) || wp_script_is( 'jarallax-video', 'registered' ) ) { $block_js_deps[] = 'jarallax-video'; } break; case 'gist': if ( wp_script_is( 'gist-simple' ) || wp_script_is( 'gist-simple', 'registered' ) ) { $block_js_deps[] = 'gist-simple'; } break; case 'carousel': if ( wp_script_is( 'swiper' ) || wp_script_is( 'swiper', 'registered' ) ) { $block_js_deps[] = 'swiper'; } break; case 'countdown': $block_js_deps[] = 'luxon'; break; case 'form': if ( wp_script_is( 'parsley' ) || wp_script_is( 'parsley', 'registered' ) ) { $block_js_deps[] = 'parsley'; } if ( wp_script_is( 'google-recaptcha' ) || wp_script_is( 'google-recaptcha', 'registered' ) ) { $block_js_deps[] = 'google-recaptcha'; } break; case 'tabs': $block_name = 'tabs-v2'; break; } wp_register_script( 'ghostkit-block-' . $block_name, $block_script_url, array_unique( $block_js_deps ), '2.25.0', true ); } foreach ( glob( ghostkit()->plugin_path . 'gutenberg/blocks/*/styles/style.min.css' ) as $template ) { $block_name = basename( dirname( dirname( $template ) ) ); $block_style_url = ghostkit()->plugin_url . 'gutenberg/blocks/' . $block_name . '/styles/style.min.css'; $block_css_deps = array( 'ghostkit' ); switch ( $block_name ) { case 'gist': $block_css_deps[] = 'gist-simple'; break; case 'carousel': $block_css_deps[] = 'swiper'; break; case 'tabs': $block_name = 'tabs-v2'; break; } wp_register_style( 'ghostkit-block-' . $block_name, $block_style_url, array_unique( $block_css_deps ), '2.25.0' ); wp_style_add_data( 'ghostkit-block-' . $block_name, 'rtl', 'replace' ); wp_style_add_data( 'ghostkit-block-' . $block_name, 'suffix', '.min' ); } do_action( 'gkt_after_assets_register' ); } /** * Enqueue styles in head. */ public function wp_enqueue_head_assets() { self::enqueue_stored_assets( 'style' ); self::enqueue_stored_assets( 'script' ); } /** * Enqueue custom styles in head. */ public function wp_enqueue_head_custom_assets() { self::enqueue_stored_assets( 'custom-css' ); } /** * Enqueue scripts and styles in foot. */ public function wp_enqueue_foot_assets() { self::enqueue_stored_assets( 'style' ); self::enqueue_stored_assets( 'script' ); } /** * Enqueue custom styles in foot. */ public function wp_enqueue_foot_custom_assets() { self::enqueue_stored_assets( 'custom-css' ); } /** * Enqueue blocks assets. * * @param array $blocks - blocks array. * @param array $location - blocks location [content,widget]. */ public function maybe_enqueue_blocks_assets( $blocks, $location ) { if ( self::$already_added_custom_assets ) { $location = 'widget'; } self::enqueue( $blocks, $location ); } /** * Parse blocks and prepare styles * * @param array $blocks Blocks array with attributes. * @return string */ public static function parse_blocks_css( $blocks ) { $styles = ''; foreach ( $blocks as $block ) { if ( isset( $block['attrs'] ) ) { $styles .= GhostKit_Block_Custom_Styles::get( $block['attrs'] ); $styles .= GhostKit_Block_Custom_CSS::get( $block['attrs'] ); } } return $styles; } /** * Add styles from blocks to the head section. * * @param int $post_id - current post id. */ public function add_custom_assets( $post_id ) { $global_code = get_option( 'ghostkit_custom_code', array() ); if ( is_singular() && ! $post_id ) { $post_id = get_the_ID(); } $is_single = is_singular() && $post_id; // Global custom CSS. if ( $global_code && isset( $global_code['ghostkit_custom_css'] ) && $global_code['ghostkit_custom_css'] ) { self::add_custom_css( 'ghostkit-global-custom-css', $global_code['ghostkit_custom_css'] ); } // Local custom CSS. if ( $is_single ) { $meta_css = get_post_meta( $post_id, 'ghostkit_custom_css', true ); if ( ! empty( $meta_css ) ) { self::add_custom_css( 'ghostkit-custom-css', $meta_css ); } } // Global custom JS head. if ( $global_code && isset( $global_code['ghostkit_custom_js_head'] ) && $global_code['ghostkit_custom_js_head'] ) { self::add_custom_js( 'ghostkit-global-custom-js-head', $global_code['ghostkit_custom_js_head'] ); } // Local custom JS head. if ( $is_single ) { $meta_js_head = get_post_meta( $post_id, 'ghostkit_custom_js_head', true ); if ( ! empty( $meta_js_head ) ) { self::add_custom_js( 'ghostkit-custom-js-head', $meta_js_head ); } } // Global custom JS foot. if ( $global_code && isset( $global_code['ghostkit_custom_js_foot'] ) && $global_code['ghostkit_custom_js_foot'] ) { self::add_custom_js( 'ghostkit-global-custom-js-foot', $global_code['ghostkit_custom_js_foot'], true ); } // Local custom JS foot. if ( $is_single ) { $meta_js_foot = get_post_meta( $post_id, 'ghostkit_custom_js_foot', true ); if ( ! empty( $meta_js_foot ) ) { self::add_custom_js( 'ghostkit-custom-js-foot', $meta_js_foot, true ); } } self::$already_added_custom_assets = true; } /** * Skip custom styles from Autoptimize. * We need this since generated CSS with custom breakpoints are excluded * from Autoptimize by default and this cause conflicts. * * @param string $result - allowed list. * @return string */ public static function autoptimize_filter_css_exclude( $result ) { // By default in Autoptimize excluded folder `wp-content/uploads/`. // We need to check, if this folder is not excluded, then we // don't need to use our hack. if ( $result && strpos( $result, 'wp-content/uploads/' ) === false ) { return $result; } foreach ( self::$skip_from_autoptimize as $name ) { if ( $result ) { $result .= ','; } else { $result = ''; } $result .= $name; } return $result; } /** * Add custom CSS. * * @param String $name - handle name. * @param String $css - code. */ public static function add_custom_css( $name, $css ) { if ( ! wp_style_is( $name, 'enqueued' ) ) { $css = wp_kses( $css, array( '\'', '\"' ) ); $css = str_replace( '>', '>', $css ); // Enqueue custom CSS. if ( ! self::$already_added_custom_assets ) { wp_register_style( $name, false, array(), '2.25.0' ); wp_enqueue_style( $name ); wp_add_inline_style( $name, $css ); // Enqueue JS instead of CSS when rendering in to prevent W3C errors. } elseif ( ! wp_script_is( $name, 'enqueued' ) ) { wp_register_script( $name, false, array(), '2.25.0', true ); wp_enqueue_script( $name ); wp_add_inline_script( $name, '(function(){ var styleTag = document.createElement("style"); styleTag.id = "' . esc_attr( $name ) . '-inline-css"; styleTag.innerHTML = ' . wp_json_encode( $css ) . '; document.body.appendChild(styleTag); }());' ); } } self::$stored_assets[] = $name; } /** * Add custom JS. * * @param String $name - handle name. * @param String $js - code. * @param Boolean $footer - print in footer. */ public static function add_custom_js( $name, $js, $footer = false ) { wp_register_script( $name, '', array(), '2.25.0', $footer ); wp_enqueue_script( $name ); wp_add_inline_script( $name, $js ); } } new GhostKit_Assets();