. * * @var array */ private $enqueued = array(); /** * SRI hash per script handle, filled at register time and read back in script_loader_tag. * * @var array */ private $integrity = array(); public static function instance() { if ( null === self::$instance ) { self::$instance = new self(); } return self::$instance; } private function __construct() { add_shortcode( 'doxa_map', array( $this, 'render_shortcode' ) ); add_filter( 'script_loader_tag', array( $this, 'inject_sri' ), 10, 2 ); add_action( 'init', array( $this, 'register_block' ) ); } /** * The configured catalog base (Settings override → default), trailing slash stripped. * * @return string */ public function catalog_base() { $base = get_option( 'doxa_maps_embed_base', DOXA_MAPS_EMBED_DEFAULT_BASE ); $base = is_string( $base ) && '' !== trim( $base ) ? trim( $base ) : DOXA_MAPS_EMBED_DEFAULT_BASE; return rtrim( $base, '/' ); } /** * A bundle's script URL in the catalog: //.js (the built-tree layout). * * @param string $bundle Sanitized bundle slug. * @return string */ private function script_url( $bundle ) { return $this->catalog_base() . '/' . $bundle . '/' . $bundle . '.js'; } /** * Sanitize a bundle / element / profile slug to the safe charset the tree uses. * Bundles and elements are kebab-case ASCII; anything else is not a real catalog entry. * * @param string $slug Raw input. * @return string Sanitized slug (may be empty). */ private function slug( $slug ) { return preg_replace( '/[^a-z0-9-]/', '', strtolower( (string) $slug ) ); } /** * [doxa_map] shortcode. * * Attributes: * bundle (required) bundle slug, e.g. "doxa-simple-map" * element (optional) custom-element tag; defaults to bundle. Some bundles expose a * differently-named element (e.g. bundle doxa-simple-map → ), * so authors can override. See manifest.json → bundles[].elements. * profile (optional) profile key inside the bundle; defaults to bundle * token (optional) Mapbox token (data, not a secret) — better: leave empty and let the * page resolve it via tokenUrl / window.MAP_TOKEN_URL * utm (optional) utmSource * base (optional) per-embed catalog base override * integrity(optional) SRI hash (sha384-…) for this bundle's script. STRONGLY recommended: * copy it from manifest.json → embed.script. Without it, no SRI is set. * * @param array $atts Shortcode attributes. * @return string HTML. */ public function render_shortcode( $atts ) { $atts = shortcode_atts( array( 'bundle' => '', 'element' => '', 'profile' => '', 'token' => '', 'utm' => '', 'base' => '', 'integrity' => '', ), $atts, 'doxa_map' ); $bundle = $this->slug( $atts['bundle'] ); if ( '' === $bundle ) { return $this->author_notice( __( 'Doxa Maps: a "bundle" attribute is required.', 'doxa-maps-embed' ) ); } $element = '' !== $atts['element'] ? $this->slug( $atts['element'] ) : $bundle; $profile = '' !== $atts['profile'] ? $this->slug( $atts['profile'] ) : $bundle; if ( '' === $element ) { return $this->author_notice( __( 'Doxa Maps: invalid element name.', 'doxa-maps-embed' ) ); } return $this->render( array( 'bundle' => $bundle, 'element' => $element, 'profile' => $profile, 'token' => (string) $atts['token'], 'utm' => (string) $atts['utm'], 'base' => (string) $atts['base'], 'integrity' => (string) $atts['integrity'], ) ); } /** * The shared renderer for shortcode + block. * * Emits, in order: * 1. (once, if a per-embed base is given) a \n", wp_json_encode( $base ) ); $this->enqueued[ 'base-global-' . md5( $base ) ] = true; } // The custom element. Tag + attribute value are escaped. profile-config is single-quoted // (the JSON contains double quotes) with esc_attr on the JSON string. $html .= sprintf( "<%1\$s profile-config='%2\$s'>", esc_html( $args['element'] ), esc_attr( $config_json ) ); return $html; } /** * script_loader_tag filter — attach SRI + crossorigin to our registered bundle scripts. * * WordPress does not offer a portable API to set `integrity` on an enqueued script across all * supported versions, so the WP-idiomatic path is to register the script normally and rewrite * ITS tag here (only ours — matched by handle). This keeps the dependency graph intact instead * of echoing a raw