*/ protected array $inlineJsCode; /** * @var array */ protected array $inlineCssCode; public function __construct(Plugin $plugin, DataVendors $dataVendors) { $this->plugin = $plugin; $this->dataVendors = $dataVendors; $this->bufferLevel = 0; $this->assets = []; $this->inlineJsCode = []; $this->inlineCssCode = []; $this->loadAssets(); } /** * @return FrontAssetInterface[] */ protected function getAssets(): array { return [ new AcfViewsMapsFrontAsset($this->plugin, $this->dataVendors), ]; } protected function loadAssets(): void { foreach ($this->getAssets() as $asset) { $this->assets[$asset->getName()] = $asset; } } protected function getWebComponentJs(CptData $cptData, string $jsCode): string { $tagName = $cptData->getTagName(); $isWebComponent = 'div' !== $tagName; if (!$isWebComponent) { return $jsCode; } // dashes to camelCase $componentName = preg_replace_callback( '/-([a-z0-9])/', function ($matches) { return strtoupper($matches[1]); }, $tagName ); if (null === $componentName) { return $jsCode; } return sprintf( 'class %s extends HTMLElement{connectedCallback(){"loading"===document.readyState?document.addEventListener("DOMContentLoaded",this.setup.bind(this)):this.setup()}setup(){%s}}customElements.define("%s", %s);', esc_html($componentName), $jsCode, esc_html($tagName), esc_html($componentName), ); } public function startBuffering(): void { ob_start(); $this->bufferLevel = ob_get_level(); } public function printStylesStub(): void { echo ''; } public function addAssets(CptData $cptData): void { $cssCode = $cptData->getCssCode(); $jsCode = $cptData->getJsCode(); if ('' !== $cssCode) { $this->inlineCssCode[$cptData->getUniqueId()] = $cssCode; } if ('' !== $jsCode) { $this->inlineJsCode[$cptData->getUniqueId()] = $this->getWebComponentJs($cptData, $jsCode); } foreach ($this->assets as $asset) { $asset->maybeActivate($cptData); } } public function printCustomAssets(): void { $allJsCode = ''; $allCssCode = ''; foreach ($this->inlineCssCode as $name => $cssCode) { // no escaping, it's a CSS code, so e.g '.a > .b' shouldn't be escaped $allCssCode .= sprintf("\n/*%s*/\n%s", $name, $cssCode); } foreach ($this->inlineJsCode as $name => $jsCode) { $allJsCode .= sprintf("\n/*%s*/\n%s", $name, $jsCode); } if ('' === $allCssCode && '' === $allJsCode) { // do not close the buffer, if it's not ours // (then ours will be closed automatically with the end of script execution) if (ob_get_level() === $this->bufferLevel) { echo ob_get_clean(); } return; } // close previous buffers. Some plugins may not close, if detect that ob_get_level() is another than was // e.g. 'lightbox-photoswipe' while (ob_get_level() > $this->bufferLevel) { echo ob_get_clean(); } $pageContent = (string)ob_get_clean(); $cssTag = '' !== $allCssCode ? sprintf("", $allCssCode) : ''; $pageContent = str_replace('', $cssTag, $pageContent); echo $pageContent; if ('' !== $allJsCode) { printf("", $allJsCode); } } /** * @return array,js:array}> */ public function generateCode(CptData $cptData): array { $code = []; foreach ($this->assets as $asset) { $assetCode = $asset->generateCode($cptData); if (0 === count($assetCode['js']) && 0 === count($assetCode['css'])) { continue; } $code[$asset->getAutoDiscoverName()] = $assetCode; } return $code; } public function isWebComponentRequired(CptData $cptData): bool { foreach ($this->assets as $asset) { if (!$asset->isWebComponentRequired($cptData)) { continue; } return true; } return false; } /** * @param string[] $names * @return ViewFrontAssetInterface[] */ public function getViewAssetsByNames(array $names): array { $frontAssetsByNames = array_intersect_key($this->assets, array_flip($names)); return array_filter($frontAssetsByNames, function ($asset) { return $asset instanceof ViewFrontAssetInterface; }); } public function getCardItemsWrapperClass(CardData $cardData): string { $classes = []; foreach ($this->assets as $asset) { if (!($asset instanceof CommonFrontAsset) || !$asset->isTargetCard($cardData)) { continue; } $class = $asset->getCardItemsWrapperClass($cardData); if ('' === $class) { continue; } $classes[] = $class; } return implode(' ', $classes); } /** * @return HtmlWrapper[] */ public function getCardItemOuters(CardData $cardData): array { /** * @var HtmlWrapper[] $outers */ $outers = []; foreach ($this->assets as $asset) { if (!($asset instanceof CommonFrontAsset) || !$asset->isTargetCard($cardData)) { continue; } $assetOuters = $asset->getCardItemOuters($cardData); if ([] === $assetOuters) { continue; } $counter = 0; foreach ($assetOuters as $assetOuter) { $outers[$counter] = key_exists($counter, $outers) ? $outers[$counter] : new HtmlWrapper('', []); $outers[$counter]->merge($assetOuter); $counter++; } } return $outers; } /** * @return array */ public function getCardShortcodeAttrs(CardData $cardData): array { $attrs = []; foreach ($this->assets as $asset) { if (!($asset instanceof CommonFrontAsset) || !$asset->isTargetCard($cardData)) { continue; } $attrs = array_merge($attrs, $asset->getCardShortcodeAttrs($cardData)); } return $attrs; } public function enqueueAssets(): void { foreach ($this->assets as $asset) { $asset->enqueueActive(); } } public function setHooks(CurrentScreen $currentScreen): void { if (true === $currentScreen->isAdmin()) { return; } add_action('wp_footer', [$this, 'enqueueAssets']); // printCustomAssets() contains ob_get_clean, so must be executed after all other scripts add_action('wp_footer', [$this, 'printCustomAssets'], 9999); add_action('wp_head', [$this, 'printStylesStub']); // don't use 'get_header', as it doesn't work in blocks theme add_action('template_redirect', [$this, 'startBuffering']); } }