File: /var/www/html/wp-content/plugins/allspice/allspice.php
<?php
/**
* Plugin Name: Allspice
* Description: Loads the Allspice widget and analytics packages.
* Version: 1.4.37
* Author: Allspice Labs, Inc.
* License: GPL v2 or later
* License URI: https://www.gnu.org/licenses/gpl-2.0.html
* Text Domain: allspice
*/
if (!defined('ABSPATH')) exit;
if (defined('ALLSPICE_WIDGET_LOADER_BOOTED')) return;
define('ALLSPICE_WIDGET_LOADER_BOOTED', true);
define('ALLSPICE_PLUGIN_FILE', __FILE__);
if (!defined('ALLSPICE_PLUGIN_VERSION')) {
define('ALLSPICE_PLUGIN_VERSION', allspice_get_plugin_version());
}
define('ALLSPICE_WIDGET_LOADER_URL', plugin_dir_url(__FILE__));
define('ALLSPICE_WIDGET_LOADER_PATH', plugin_dir_path(__FILE__));
define('ALLSPICE_DEBUG_PARAM', 'allspice_debug');
define('ALLSPICE_DEBUG_NONCE_ACTION', 'allspice_debug');
define('ALLSPICE_DEBUG_NONCE_PARAM', 'allspice_dbg_nonce');
require_once plugin_dir_path(__FILE__) . 'includes/env.php';
require_once plugin_dir_path(__FILE__) . 'includes/api.php';
require_once plugin_dir_path(__FILE__) . 'includes/settings.php';
require_once plugin_dir_path(__FILE__) . 'includes/cron.php';
require_once plugin_dir_path(__FILE__) . 'includes/webhook.php';
require_once plugin_dir_path(__FILE__) . 'includes/url_key.php';
require_once plugin_dir_path(__FILE__) . 'includes/db_recipes.php';
require_once plugin_dir_path(__FILE__) . 'includes/recipes_sync.php';
require_once plugin_dir_path(__FILE__) . 'includes/seo.php';
require_once ALLSPICE_WIDGET_LOADER_PATH . 'includes/blocks.php';
register_activation_hook(__FILE__, function () {
allspice_create_recipes_table();
allspice_activate_plugin();
});
register_deactivation_hook(__FILE__, function () {
allspice_deactivate_plugin();
});
add_filter('plugin_action_links_' . plugin_basename(__FILE__), function ($links) {
$url = admin_url('options-general.php?page=' . ALLSPICE_SETTINGS_PAGE);
array_unshift($links, '<a href="' . esc_url($url) . '">Settings</a>');
return $links;
});
function allspice_get_plugin_version(): string {
static $ver = null;
if ($ver !== null) return $ver;
if (!function_exists('get_file_data')) require_once ABSPATH . 'wp-includes/functions.php';
$data = get_file_data(ALLSPICE_PLUGIN_FILE, array('Version' => 'Version'), 'plugin');
$ver = (!empty($data['Version']) ? $data['Version'] : '0.0.0');
return $ver;
}
function allspice_get_widget_build(): string {
$files = [
ALLSPICE_WIDGET_LOADER_PATH . 'dist/allspicewidget.css',
ALLSPICE_WIDGET_LOADER_PATH . 'dist/allspicewidget.umd.cjs',
ALLSPICE_WIDGET_LOADER_PATH . 'dist/page.js',
];
$max = 0;
foreach ($files as $f) {
if (file_exists($f)) {
$max = max($max, filemtime($f));
}
}
return $max ? (string)$max : (string)time();
}
function allspice_assets_version(): string {
return allspice_get_plugin_version() . '.' . allspice_get_widget_build();
}
function allspice_is_debug(): bool {
if (!is_user_logged_in() || !current_user_can('manage_options')) return false;
$debug = isset($_GET[ALLSPICE_DEBUG_PARAM])
? sanitize_text_field(wp_unslash($_GET[ALLSPICE_DEBUG_PARAM]))
: '';
if ($debug !== '1') return false;
$nonce = isset($_GET[ALLSPICE_DEBUG_NONCE_PARAM])
? sanitize_text_field(wp_unslash($_GET[ALLSPICE_DEBUG_NONCE_PARAM]))
: '';
return (bool) wp_verify_nonce($nonce, ALLSPICE_DEBUG_NONCE_ACTION);
}
function allspice_console_log(string $label, $data = null): void {
if (!allspice_is_debug()) return;
if (!isset($GLOBALS['allspice_debug_logs']) || !is_array($GLOBALS['allspice_debug_logs'])) {
$GLOBALS['allspice_debug_logs'] = array();
}
$GLOBALS['allspice_debug_logs'][] = array('label' => $label, 'data' => $data);
}
add_filter('script_loader_tag', function ($tag, $handle, $src) {
$is_allspice =
($handle === 'allspice-widget-page') ||
($handle === 'allspice-widget-umd') ||
(is_string($src) && (
strpos($src, 'widget-dev.allspicelabs.com') !== false ||
strpos($src, 'widget.allspicelabs.com') !== false ||
strpos($src, 'allspicewidget.umd.cjs') !== false ||
strpos($src, '/prod/page/') !== false ||
strpos($src, '/dev/page/') !== false ||
strpos($src, '/main.js') !== false
)) ||
(strpos($tag, 'id="allspice-widget-page-js"') !== false) ||
(strpos($tag, 'id="allspice-widget-umd-js"') !== false);
if ($is_allspice && strpos($tag, ' nowprocket') === false) {
$tag = str_replace('<script ', '<script nowprocket ', $tag);
}
return $tag;
}, 10, 3);
add_filter('style_loader_tag', function ($html, $handle, $href, $media) {
$is_allspice =
($handle === 'allspice-widget-style') ||
(is_string($href) && (
strpos($href, 'widget-dev.allspicelabs.com') !== false ||
strpos($href, 'widget.allspicelabs.com') !== false ||
strpos($href, 'allspicewidget.css') !== false
));
if ($is_allspice && strpos($html, ' nowprocket') === false) {
$html = str_replace("<link ", "<link nowprocket ", $html);
}
return $html;
}, 10, 4);
add_action('wp_footer', function () {
if (!allspice_is_debug()) return;
$logs = $GLOBALS['allspice_debug_logs'] ?? array();
if (!$logs) return;
$json = wp_json_encode($logs, JSON_UNESCAPED_SLASHES);
$js = "try{(" . $json . ").forEach(function(x){console.log('[Allspice][PHP]', x);});}catch(e){}";
wp_print_inline_script_tag($js);
}, 999);
function allspice_mark_needed(): void {
$GLOBALS['allspice_widget_needed'] = true;
}
function allspice_shortcode_atts_to_string(array $atts): string {
$parts = array();
foreach ($atts as $k => $v) {
$parts[] = sanitize_key((string)$k) . '="' . esc_attr((string)$v) . '"';
}
return implode(' ', $parts);
}
function allspice_register_shortcodes(): void {
add_shortcode('allspice_button', function ($atts) {
allspice_mark_needed();
$atts = shortcode_atts(array(
'id' => '',
'reserve_w' => '',
'reserve_h' => '',
'class' => '',
'flow' => '0',
), (array)$atts, 'allspice_button');
$flow = ((string)$atts['flow'] === '1');
$id = trim((string)$atts['id']);
if ($id === '') return '';
$class = (string)$atts['class'];
$w = preg_replace('/[^0-9]/', '', trim((string)$atts['reserve_w']));
$h = preg_replace('/[^0-9]/', '', trim((string)$atts['reserve_h']));
if ($w === '' || $h === '') {
$d = allspice_premade_defaults($id);
if ($d && ($w === '' || $h === '')) {
if ($w === '') $w = (string)$d['reserve_w'];
if ($h === '') $h = (string)$d['reserve_h'];
if (!isset($atts['flow']) || (string)$atts['flow'] === '0') {
$flow = (bool)$d['flow'];
}
}
}
$w_px = ($w !== '' && ctype_digit($w)) ? $w . 'px' : '';
$h_px = ($h !== '' && ctype_digit($h)) ? $h . 'px' : '';
$style = '';
if ($flow) {
$style .= 'display:block;';
$style .= 'width:100%;';
if ($w_px) $style .= 'max-width:' . $w_px . ';';
$style .= 'margin-left:auto;margin-right:auto;';
if ($h_px) $style .= 'min-height:' . $h_px . ';';
} else {
$style .= 'display:inline-block;';
if ($w_px) $style .= 'width:' . $w_px . ';';
if ($h_px) $style .= 'height:' . $h_px . ';';
}
return '<span'
. ' class="allspice-slot ' . esc_attr($class) . '"'
. ' data-allspice-mode="premade"'
. ' data-allspice-button="' . esc_attr($id) . '"'
. ' data-allspice-layout="' . ($flow ? 'flow' : 'fixed') . '"'
. ' style="' . esc_attr($style) . '"'
. ' aria-busy="true"'
. '></span>';
});
add_shortcode('Allspice_Button', function ($atts) {
$atts = is_array($atts) ? $atts : array();
return do_shortcode('[allspice_button ' . allspice_shortcode_atts_to_string($atts) . ']');
});
}
add_action('init', 'allspice_register_shortcodes');
function allspice_mark_needed_on_shortcode_exec($output, $tag, $attr) {
if ($tag === 'allspice_button' || $tag === 'Allspice_Button') allspice_mark_needed();
return $output;
}
add_filter('do_shortcode_tag', 'allspice_mark_needed_on_shortcode_exec', 10, 3);
function allspice_detect_shortcode_in_content(): void {
if (is_admin() || !is_singular()) return;
global $post;
if (!$post || empty($post->post_content)) return;
if (has_shortcode($post->post_content, 'allspice_button') || has_shortcode($post->post_content, 'Allspice_Button')) allspice_mark_needed();
if (
strpos($post->post_content, 'data-allspice-id=') !== false
) {
allspice_mark_needed();
}
allspice_console_log('[Allspice] detect_shortcode_in_content fired; found=' . (
(has_shortcode($post->post_content, 'allspice_button') || has_shortcode($post->post_content, 'Allspice_Button') || strpos($post->post_content, 'data-allspice-id=') !== false)
? '1' : '0'
));
}
add_action('wp', 'allspice_detect_shortcode_in_content');
function allspice_allowed_html(): array {
return array(
'div' => array(
'id' => true,
'class' => true,
'data-asx-hide' => true,
'data-asx-side' => true,
'data-asx-initial-dock' => true,
'data-asx-page-kind' => true,
'data-asx-surface' => true,
'style' => true,
),
'span' => array(
'class' => true,
'data-allspice-mode' => true,
'data-allspice-button' => true,
'data-allspice-layout' => true,
'style' => true,
'aria-busy' => true,
),
);
}
function allspice_get_request_uri(): string {
// phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Sanitized below.
$v = isset($_SERVER['REQUEST_URI']) ? wp_unslash($_SERVER['REQUEST_URI']) : '';
$v = is_string($v) ? $v : '';
$v = preg_replace('/[^\x20-\x7E]/', '', $v);
$v = sanitize_text_field($v);
return $v !== '' ? $v : '/';
}
function allspice_current_url_key(): string {
$req_uri = allspice_get_request_uri();
if ($req_uri === '') $req_uri = '/';
$url = home_url($req_uri);
return allspice_url_key($url);
}
function allspice_is_recipe_page($postObj = null): bool {
if (is_admin()) return false;
if (!is_singular()) return false;
$k = allspice_current_url_key();
if ($k === '') return false;
if (function_exists('allspice_recipes_has_url_key')) {
$is_recipe = allspice_recipes_has_url_key($k);
if ($is_recipe) return true;
return false;
}
return false;
}
function allspice_widget_needed_now(): bool {
// if (allspice_is_debug()) return true;
if (is_admin()) return false;
return true;
// if (!is_singular()) return false;
// global $post;
// if (!$post) return false;
// if (allspice_is_recipe_page($post)) return true;
// if (!empty($GLOBALS['allspice_widget_needed'])) return true;
// if (allspice_has_manual_placements_in_post($post)) return true;
// return false;
}
function allspice_has_manual_placements_in_post($postObj = null): bool {
if (!$postObj) {
global $post;
$postObj = $post;
}
if (!$postObj || empty($postObj->post_content)) return false;
$c = (string)$postObj->post_content;
if (
has_shortcode($c, 'allspice_button') ||
has_shortcode($c, 'Allspice_Button') ||
strpos($c, 'data-allspice-id=') !== false
) return true;
if (function_exists('has_block')) {
if (
has_block('allspice/floating-button', $postObj) ||
has_block('allspice/rail', $postObj) ||
has_block('allspice/inline-chat', $postObj) ||
has_block('allspice/inline-search', $postObj) ||
has_block('allspice/inline-overview-style-1', $postObj) ||
has_block('allspice/inline-overview-style-2', $postObj) ||
has_block('allspice/inline-cooking-cta', $postObj)
) return true;
}
return false;
}
function allspice_cache_has_autoplaced_buttons(): bool {
$buttons = allspice_buttons_cache_get();
if (!$buttons) return false;
$dbg_buttons = array();
foreach (($buttons ?: array()) as $b) {
$dbg_buttons[] = array(
'id' => (string)($b['id'] ?? ''),
'active' => !empty($b['active']) ? 1 : 0,
'style' => (string)($b['style'] ?? ''),
'placement' => (string)($b['placement'] ?? ''),
);
}
allspice_console_log('[Allspice][PHP][floating] buttons_dump', $dbg_buttons);
foreach ($buttons as $b) {
if (empty($b['active'])) continue;
$style = (string)($b['style'] ?? '');
$placement = allspice_normalize_placement($style, (string)($b['placement'] ?? 'manual_only'));
if ($style === 'inline') {
if ($placement !== '' && $placement !== 'manual_only') return true;
} else if ($style === 'floating') {
if ($placement === 'automatic') return true;
}
}
return false;
}
add_filter('the_content', 'allspice_autoplace_inline_into_content', 20);
function allspice_autoplace_inline_into_content(string $content): string {
if (is_admin() || !is_singular()) return $content;
// if (!is_main_query() || !in_the_loop()) return $content; # genesis [KEEP]
global $post;
if (!$post) return $content;
if (!allspice_is_recipe_page($post)) return $content;
if (allspice_has_manual_inline_placements_in_post($post)) return $content;
$buttons = allspice_buttons_cache_get();
if (!$buttons) return $content;
$groups = array();
foreach ($buttons as $b) {
if (empty($b['active'])) continue;
if (($b['style'] ?? '') !== 'inline') continue;
$style = (string)($b['style'] ?? '');
$placement_raw = (string)($b['placement'] ?? 'manual_only');
$placement = allspice_normalize_placement($style, $placement_raw);
if ($placement === '' || $placement === 'manual_only') continue;
$id = trim((string)($b['id'] ?? ''));
if ($id === '') continue;
if (!isset($groups[$placement])) $groups[$placement] = array();
$groups[$placement][] = array(
'id' => $id,
'pad_top' => $b['pad_top'] ?? null,
'pad_bottom' => $b['pad_bottom'] ?? null,
'theme' => (isset($b['theme']) && is_array($b['theme'])) ? $b['theme'] : null,
);
}
if (!$groups) return $content;
foreach ($groups as $placement => $items) {
$indexed = array();
foreach ($items as $idx => $item) {
$indexed[] = array(
'__index' => $idx,
'__item' => $item,
);
}
usort($indexed, function ($a, $b) {
$aItem = $a['__item'];
$bItem = $b['__item'];
$aId = is_array($aItem) ? (string)($aItem['id'] ?? '') : (string)$aItem;
$bId = is_array($bItem) ? (string)($bItem['id'] ?? '') : (string)$bItem;
$aInlineCta = ($aId === 'WIDCON-f92u' || $aId === 'WIDCON-a75g' || $aId === 'WIDCON-b64k') ? 0 : 1;
$bInlineCta = ($bId === 'WIDCON-f92u' || $bId === 'WIDCON-a75g' || $bId === 'WIDCON-b64k') ? 0 : 1;
if ($aInlineCta !== $bInlineCta) {
return $aInlineCta <=> $bInlineCta;
}
return ((int)$a['__index']) <=> ((int)$b['__index']);
});
$sortedItems = array();
foreach ($indexed as $row) {
$sortedItems[] = $row['__item'];
}
$content = allspice_inject_inline_slots($content, $placement, $sortedItems);
}
return $content;
}
function allspice_render_slot_html(string $id, bool $flow = true, int $reserve_w = 720, int $reserve_h = 280): string {
$atts = array(
'id' => $id,
'reserve_w' => (string)$reserve_w,
'reserve_h' => (string)$reserve_h,
'flow' => $flow ? '1' : '0',
);
return do_shortcode('[allspice_button ' . allspice_shortcode_atts_to_string($atts) . ']');
}
function allspice_autoplace_inline_spacing(string $placement, array $buttonRow): array {
$top = 16;
$bottom = 16;
if (isset($buttonRow['theme']) && is_array($buttonRow['theme'])) {
$t = $buttonRow['theme'];
if (array_key_exists('padding_top', $t)) {
$top = allspice_autoplace_clamp_int($t['padding_top'], 0, 200, $top);
}
if (array_key_exists('padding_bottom', $t)) {
$bottom = allspice_autoplace_clamp_int($t['padding_bottom'], 0, 200, $bottom);
}
}
return array('top' => $top, 'bottom' => $bottom);
}
function allspice_autoplace_clamp_int($v, int $min, int $max, int $fallback): int {
if (function_exists('allspice_clamp_int')) {
return allspice_clamp_int($v, $min, $max, $fallback);
}
if ($v === null) return $fallback;
if (is_string($v)) $v = trim($v);
if ($v === '' || !is_numeric($v)) return $fallback;
$n = (int)$v;
if ($n < $min) return $min;
if ($n > $max) return $max;
return $n;
}
function allspice_autoplace_button_spacing(array $buttonRow, string $placement): array {
$sp = allspice_autoplace_inline_spacing($placement, $buttonRow);
$top = (int)($sp['top'] ?? 16);
$bot = (int)($sp['bottom'] ?? 16);
if (array_key_exists('pad_top', $buttonRow)) {
$top = allspice_autoplace_clamp_int($buttonRow['pad_top'], 0, 200, $top);
}
if (array_key_exists('pad_bottom', $buttonRow)) {
$bot = allspice_autoplace_clamp_int($buttonRow['pad_bottom'], 0, 200, $bot);
}
return array('top' => $top, 'bottom' => $bot);
}
function allspice_wrap_autoplaced_group_html(string $inner, string $placement): string {
$style = 'display:flow-root;';
return '<div class="asx-autoplace asx-autoplace--' . esc_attr($placement) . '" data-asx-autoplaced="' . esc_attr($placement) . '" style="' . esc_attr($style) . '">' . $inner . '</div>';
}
function allspice_wrap_autoplaced_slot_html(string $slotHtml, int $top, int $bottom): string {
$top = max(0, min(200, $top));
$bottom = max(0, min(200, $bottom));
$style = 'display:flow-root;padding-top:' . $top . 'px;padding-bottom:' . $bottom . 'px;';
return '<div class="asx-autoplace-slot" style="' . esc_attr($style) . '">' . $slotHtml . '</div>';
}
function allspice_inject_inline_slots(string $content, string $placement, array $items): string {
$inner = "\n";
foreach ($items as $it) {
$id = is_array($it) ? (string)($it['id'] ?? '') : (string)$it;
if ($id === '') continue;
$reserve_w = 920;
$reserve_h = ($id === 'WIDCON-f92u') ? 180 : (($id === 'WIDCON-a75g' || $id === 'WIDCON-b64k') ? 220 : 320);
$slot = allspice_render_slot_html($id, true, $reserve_w, $reserve_h);
$buttonRow = is_array($it) ? $it : array('id' => $id);
$sp = allspice_autoplace_button_spacing($buttonRow, $placement);
$inner .= allspice_wrap_autoplaced_slot_html($slot, (int)$sp['top'], (int)$sp['bottom']) . "\n";
}
$inner .= "\n";
$html = allspice_wrap_autoplaced_group_html($inner, $placement);
switch ($placement) {
case 'end_of_post':
return $content . $html;
case 'after_introduction':
return allspice_insert_after_nth_paragraph($content, $html, 2);
case 'after_first_section':
return allspice_insert_after_first_heading($content, $html);
case 'above_recipe_card':
return allspice_insert_above_recipe_card($content, $html);
case 'under_recipe_card':
return allspice_insert_under_recipe_card($content, $html);
case 'top_of_post':
return allspice_insert_at_top_of_post($content, $html);
default:
return $content . $html;
}
}
function allspice_insert_above_recipe_card(string $content, string $insertHtml): string {
$bounds = allspice_find_wprm_recipe_bounds($content);
if (!$bounds) {
return $content;
}
$start = (int)$bounds['start'];
return substr($content, 0, $start)
. "\n" . $insertHtml . "\n"
. substr($content, $start);
}
function allspice_insert_under_recipe_card(string $content, string $insertHtml): string {
$bounds = allspice_find_wprm_recipe_bounds($content);
if (!$bounds) {
return $content;
}
$end = (int)$bounds['end'];
return substr($content, 0, $end)
. "\n" . $insertHtml . "\n"
. substr($content, $end);
}
function allspice_insert_at_top_of_post(string $content, string $insertHtml): string {
$hPos = null;
if (preg_match('/<h[1-6]\b[^>]*>/i', $content, $m, PREG_OFFSET_CAPTURE)) {
$hPos = $m[0][1];
}
$pPos = null;
if (preg_match_all('/<p\b[^>]*>.*?<\/p>/is', $content, $pm, PREG_OFFSET_CAPTURE)) {
foreach ($pm[0] as $match) {
$pHtml = $match[0];
$pos = $match[1];
$text = html_entity_decode(wp_strip_all_tags($pHtml), ENT_QUOTES | ENT_HTML5, 'UTF-8');
$text = preg_replace('/\x{00A0}/u', ' ', $text); // nbsp
$text = trim(preg_replace('/\s+/u', ' ', $text));
$textNoPunct = trim(preg_replace('/[[:punct:]\s]+/u', '', $text));
if (mb_strlen($textNoPunct, 'UTF-8') >= 20) {
$pPos = $pos;
break;
}
}
}
$pos = null;
if ($hPos !== null && $pPos !== null) {
$pos = min($hPos, $pPos);
} elseif ($hPos !== null) {
$pos = $hPos;
} elseif ($pPos !== null) {
$pos = $pPos;
}
if ($pos !== null) {
return substr($content, 0, $pos)
. "\n" . $insertHtml . "\n"
. substr($content, $pos);
}
return $insertHtml . "\n" . $content;
}
function allspice_insert_after_nth_paragraph(string $content, string $insertHtml, int $n): string {
if ($n <= 0) return $insertHtml . $content;
$parts = preg_split('/(<\/p>)/i', $content, -1, PREG_SPLIT_DELIM_CAPTURE);
if (!$parts || count($parts) < 2) return $content . $insertHtml;
$out = '';
$pCount = 0;
for ($i = 0; $i < count($parts); $i++) {
$out .= $parts[$i];
if (preg_match('/<\/p>/i', $parts[$i])) {
$pCount++;
if ($pCount === $n) $out .= "\n" . $insertHtml . "\n";
}
}
if ($pCount < $n) $out .= "\n" . $insertHtml . "\n";
return $out;
}
function allspice_insert_after_first_heading(string $content, string $insertHtml): string {
foreach (array('/<\/h2>/i', '/<\/h3>/i') as $re) {
if (preg_match($re, $content, $m, PREG_OFFSET_CAPTURE)) {
$pos = $m[0][1] + strlen($m[0][0]);
return substr($content, 0, $pos) . "\n" . $insertHtml . "\n" . substr($content, $pos);
}
}
return allspice_insert_after_nth_paragraph($content, $insertHtml, 2);
}
if (!has_action('wp_footer', 'allspice_autoplace_floating')) {
add_action('wp_footer', 'allspice_autoplace_floating', 5);
}
function allspice_is_rail_button_id(string $id): bool {
return trim($id) === 'WIDCON-d29x';
}
function allspice_is_standard_floating_button(array $button): bool {
$id = trim((string)($button['id'] ?? ''));
$style = strtolower((string)($button['style'] ?? ''));
return $style === 'floating' && $id !== '' && !allspice_is_rail_button_id($id);
}
function allspice_floating_candidate_allowed_on_current_page(array $button, bool $is_recipe): bool {
if ($is_recipe) {
return true;
}
$opts = (!empty($button['options']) && is_array($button['options']))
? $button['options']
: array();
return (($opts['show_on_all_pages'] ?? false) === true);
}
//function allspice_autoplace_floating(): void {
// if (allspice_is_debug()) echo "\n<!-- ALLSPICE_FLOATING_FUNCTION_ENTERED -->\n";
// allspice_console_log('context in autoplace floating', array(
// 'is_admin' => is_admin() ? 1 : 0,
// 'is_singular' => is_singular() ? 1 : 0,
// 'post_id' => isset($GLOBALS['post']) && $GLOBALS['post'] ? (string)$GLOBALS['post']->ID : null,
// 'uri' => allspice_get_request_uri() ?: null,
// ));
// allspice_console_log('[Allspice][PHP][floating] ENTER uri=' . allspice_get_request_uri());
// if (is_admin()) {
// allspice_console_log('[Allspice][PHP][floating] EXIT admin');
// return;
// }
// global $post;
// $is_recipe = ($post ? allspice_is_recipe_page($post) : false);
// $page_kind = $is_recipe ? 'recipe' : 'non_recipe';
// allspice_console_log('[Allspice][PHP][floating] page_kind=' . $page_kind . ' post_id=' . (string)($post->ID ?? ''));
// $has_manual_float = ($post ? allspice_has_manual_floating_placements_in_post($post) : false);
// allspice_console_log('[Allspice][PHP][floating] has_manual_floating=' . ($has_manual_float ? '1' : '0'));
// if ($has_manual_float) {
// allspice_console_log('[Allspice][PHP][floating] EXIT manual floating present');
// return;
// }
// $buttons = allspice_buttons_cache_get();
// allspice_console_log('[Allspice][PHP][floating] buttons_cache_count=' . (is_array($buttons) ? count($buttons) : 0));
// if (!$buttons) {
// allspice_console_log('[Allspice][PHP][floating] EXIT no buttons cache');
// return;
// }
// $floatingId = '';
// $float_side = 'right';
// $show_on_all_pages = false;
// $opts = array();
// foreach ($buttons as $b) {
// if (empty($b['active'])) continue;
// if (($b['style'] ?? '') !== 'floating') continue;
// if ((string)($b['placement'] ?? 'manual_only') !== 'automatic') continue;
// $floatingId = trim((string)($b['id'] ?? ''));
// if ($floatingId !== '') {
// $opts = (!empty($b['options']) && is_array($b['options'])) ? $b['options'] : array();
// $candidate_side = strtolower(trim((string)($opts['float_side'] ?? '')));
// if ($candidate_side === 'left' || $candidate_side === 'right') {
// $float_side = $candidate_side;
// }
// $show_on_all_pages = (($opts['show_on_all_pages'] ?? false) === true);
// break;
// }
// }
// allspice_console_log('[Allspice][PHP][floating] selected_floating_id=' . ($floatingId ?: 'NONE'));
// if ($floatingId === '') {
// allspice_console_log('[Allspice][PHP][floating] EXIT no eligible floating button');
// return;
// }
// if (!$is_recipe && !$show_on_all_pages) {
// allspice_console_log('[Allspice][PHP][floating] EXIT non-recipe page and show_on_all_pages disabled');
// return;
// }
// $is_adaptive_rail = ($floatingId === 'WIDCON-d29x');
// $req_uri = allspice_get_request_uri();
// $wrap_id = 'asx-floatwrap-' . substr(md5($floatingId . '|' . $req_uri), 0, 10);
// if (!empty($GLOBALS['allspice_floating_injected']) && $GLOBALS['allspice_floating_injected'] === true) {
// allspice_console_log('[Allspice][PHP][floating] EXIT already injected');
// return;
// }
// if (!empty($GLOBALS['allspice_floating_wrap_id']) && $GLOBALS['allspice_floating_wrap_id'] === $wrap_id) {
// allspice_console_log('[Allspice][PHP][floating] EXIT same wrap_id already used');
// return;
// }
// $GLOBALS['allspice_floating_injected'] = true;
// $GLOBALS['allspice_floating_wrap_id'] = $wrap_id;
// $html = $is_adaptive_rail
// ? allspice_render_slot_html($floatingId, false, 1, 1)
// : allspice_render_slot_html($floatingId, false, 220, 196);
// // WIDCON-d29x (adaptive rail): default_position sets dock; WIDCON-ea5d etc. use float_side only.
// $initial_dock = ($float_side === 'left') ? 'ml' : 'mr';
// if ($is_adaptive_rail) {
// $valid_docks = array('tl', 'tr', 'bl', 'br', 'ml', 'mr');
// $dock_left = array('tl', 'bl', 'ml');
// $dp = isset($opts['default_position']) ? strtolower(trim((string)$opts['default_position'])) : '';
// if ($dp !== '' && in_array($dp, $valid_docks, true)) {
// $initial_dock = $dp;
// $float_side = in_array($dp, $dock_left, true) ? 'left' : 'right';
// }
// }
// $side_style = ($float_side === 'left')
// ? 'left:20px;'
// : 'right:20px;';
// if ($is_adaptive_rail) {
// $wrap_style = 'position:fixed;top:0;right:0;bottom:0;left:0;width:100vw;height:100vh;overflow:visible;pointer-events:none;z-index:9000;';
// } else {
// $wrap_style = 'position:fixed;' . $side_style . 'bottom:20px;transform:translateY(calc(-1 * var(--asx-bottom-offset, 0px)));will-change:transform;z-index:9000;';
// }
// $wrap_open = '<div id="' . esc_attr($wrap_id) . '" class="asx-floatwrap" data-asx-hide="1" data-asx-side="' . esc_attr($float_side) . '" data-asx-initial-dock="' . esc_attr($initial_dock) . '" data-asx-page-kind="' . esc_attr($page_kind) . '" style="' . esc_attr($wrap_style) . '">';
// $wrap_close = '</div>';
// echo wp_kses($wrap_open, allspice_allowed_html());
// echo wp_kses($html, allspice_allowed_html());
// echo wp_kses($wrap_close, allspice_allowed_html());
// $event_payload = array(
// 'kind' => 'floating',
// 'wrapId' => $wrap_id,
// 'buttonId'=> $floatingId,
// 'ts' => time(),
// );
// $js = '(() => { try {
// const detail = ' . wp_json_encode($event_payload, JSON_UNESCAPED_SLASHES) . ';
// window.__ALLSPICE_SLOT_QUEUE = window.__ALLSPICE_SLOT_QUEUE || [];
// window.__ALLSPICE_SLOT_QUEUE.push(detail);
// try { document.dispatchEvent(new CustomEvent("allspice:slotAdded", { detail })); }
// catch(e1){
// try { var ev=document.createEvent("CustomEvent"); ev.initCustomEvent("allspice:slotAdded", true, true, detail); document.dispatchEvent(ev); }
// catch(e2){}
// }
// } catch(e3){} })();';
// if (wp_script_is('allspice-widget-page', 'enqueued') || wp_script_is('allspice-widget-page', 'registered')) {
// wp_add_inline_script('allspice-widget-page', $js, 'after');
// } else {
// echo '<script>' . $js . '</script>';
// }
//}
function allspice_autoplace_floating(): void {
if (allspice_is_debug()) echo "\n<!-- ALLSPICE_FLOATING_FUNCTION_ENTERED -->\n";
allspice_console_log('context in autoplace floating', array(
'is_admin' => is_admin() ? 1 : 0,
'is_singular' => is_singular() ? 1 : 0,
'post_id' => isset($GLOBALS['post']) && $GLOBALS['post'] ? (string)$GLOBALS['post']->ID : null,
'uri' => allspice_get_request_uri() ?: null,
));
allspice_console_log('[Allspice][PHP][floating] ENTER uri=' . allspice_get_request_uri());
if (is_admin()) {
allspice_console_log('[Allspice][PHP][floating] EXIT admin');
return;
}
global $post;
$is_recipe = ($post ? allspice_is_recipe_page($post) : false);
$page_kind = $is_recipe ? 'recipe' : 'non_recipe';
allspice_console_log('[Allspice][PHP][floating] page_kind=' . $page_kind . ' post_id=' . (string)($post->ID ?? ''));
$has_manual_rail = ($post ? allspice_has_manual_rail_placements_in_post($post) : false);
$has_manual_standard_float = ($post ? allspice_has_manual_standard_floating_placements_in_post($post) : false);
allspice_console_log('[Allspice][PHP][floating] has_manual_rail=' . ($has_manual_rail ? '1' : '0'));
allspice_console_log('[Allspice][PHP][floating] has_manual_standard_float=' . ($has_manual_standard_float ? '1' : '0'));
$buttons = allspice_buttons_cache_get();
allspice_console_log('[Allspice][PHP][floating] buttons_cache_count=' . (is_array($buttons) ? count($buttons) : 0));
if (!$buttons || !is_array($buttons)) {
allspice_console_log('[Allspice][PHP][floating] EXIT no buttons cache');
return;
}
$rail_candidate = null;
$standard_float_candidate = null;
foreach ($buttons as $b) {
if (empty($b['active'])) {
continue;
}
$id = trim((string)($b['id'] ?? ''));
if ($id === '') {
continue;
}
if ((string)($b['placement'] ?? 'manual_only') !== 'automatic') {
continue;
}
$style = strtolower((string)($b['style'] ?? ''));
if (allspice_is_rail_button_id($id)) {
if (!$has_manual_rail && $rail_candidate === null) {
$rail_candidate = $b;
}
continue;
}
if ($style === 'floating') {
if (!$has_manual_standard_float) {
if ($id === 'WIDCON-ea5d') {
$standard_float_candidate = $b;
} elseif ($standard_float_candidate === null) {
$standard_float_candidate = $b;
}
}
continue;
}
}
if ($rail_candidate && !allspice_floating_candidate_allowed_on_current_page($rail_candidate, $is_recipe)) {
allspice_console_log('[Allspice][PHP][floating] rail skipped because non-recipe and show_on_all_pages disabled');
$rail_candidate = null;
}
if ($standard_float_candidate && !allspice_floating_candidate_allowed_on_current_page($standard_float_candidate, $is_recipe)) {
allspice_console_log('[Allspice][PHP][floating] standard float skipped because non-recipe and show_on_all_pages disabled');
$standard_float_candidate = null;
}
allspice_console_log('[Allspice][PHP][floating] selected_rail_id=' . ($rail_candidate ? (string)($rail_candidate['id'] ?? 'NONE') : 'NONE'));
allspice_console_log('[Allspice][PHP][floating] selected_standard_float_id=' . ($standard_float_candidate ? (string)($standard_float_candidate['id'] ?? 'NONE') : 'NONE'));
if (!$rail_candidate && !$standard_float_candidate) {
allspice_console_log('[Allspice][PHP][floating] EXIT no eligible floating surfaces');
return;
}
if ($standard_float_candidate) {
allspice_render_auto_floating_surface($standard_float_candidate, 'floating', $page_kind);
}
if ($rail_candidate) {
allspice_render_auto_floating_surface($rail_candidate, 'rail', $page_kind);
}
}
function allspice_render_auto_floating_surface(array $button, string $surface_kind, string $page_kind): void {
$button_id = trim((string)($button['id'] ?? ''));
if ($button_id === '') {
return;
}
$is_rail = allspice_is_rail_button_id($button_id);
$opts = (!empty($button['options']) && is_array($button['options']))
? $button['options']
: array();
$req_uri = allspice_get_request_uri();
if ($is_rail) {
$wrap_id = 'asx-railwrap-' . substr(md5($button_id . '|' . $req_uri), 0, 10);
if (!empty($GLOBALS['allspice_auto_rail_injected']) && $GLOBALS['allspice_auto_rail_injected'] === true) {
allspice_console_log('[Allspice][PHP][floating] EXIT rail already injected');
return;
}
if (!empty($GLOBALS['allspice_auto_rail_wrap_id']) && $GLOBALS['allspice_auto_rail_wrap_id'] === $wrap_id) {
allspice_console_log('[Allspice][PHP][floating] EXIT same rail wrap_id already used');
return;
}
$GLOBALS['allspice_auto_rail_injected'] = true;
$GLOBALS['allspice_auto_rail_wrap_id'] = $wrap_id;
} else {
$wrap_id = 'asx-floatwrap-' . substr(md5($button_id . '|' . $req_uri), 0, 10);
if (!empty($GLOBALS['allspice_auto_float_injected']) && $GLOBALS['allspice_auto_float_injected'] === true) {
allspice_console_log('[Allspice][PHP][floating] EXIT standard float already injected');
return;
}
if (!empty($GLOBALS['allspice_auto_float_wrap_id']) && $GLOBALS['allspice_auto_float_wrap_id'] === $wrap_id) {
allspice_console_log('[Allspice][PHP][floating] EXIT same standard float wrap_id already used');
return;
}
$GLOBALS['allspice_auto_float_injected'] = true;
$GLOBALS['allspice_auto_float_wrap_id'] = $wrap_id;
}
$float_side = 'right';
$candidate_side = strtolower(trim((string)($opts['float_side'] ?? '')));
if ($candidate_side === 'left' || $candidate_side === 'right') {
$float_side = $candidate_side;
}
$initial_dock = ($float_side === 'left') ? 'ml' : 'mr';
if ($is_rail) {
$valid_docks = array('tl', 'tr', 'bl', 'br', 'ml', 'mr');
$dock_left = array('tl', 'bl', 'ml');
$dp = isset($opts['default_position'])
? strtolower(trim((string)$opts['default_position']))
: '';
if ($dp !== '' && in_array($dp, $valid_docks, true)) {
$initial_dock = $dp;
$float_side = in_array($dp, $dock_left, true) ? 'left' : 'right';
}
}
$html = $is_rail
? allspice_render_slot_html($button_id, false, 1, 1)
: allspice_render_slot_html($button_id, false, 220, 196);
if ($is_rail) {
$wrap_style = 'position:fixed;inset:0;width:auto;height:auto;max-width:none;margin:0;padding:0;overflow:visible;pointer-events:none;z-index:9000;';
} else {
$side_style = ($float_side === 'left')
? 'left:20px;'
: 'right:20px;';
$wrap_style = 'position:fixed;' . $side_style . 'bottom:20px;transform:translateY(calc(-1 * var(--asx-bottom-offset, 0px)));will-change:transform;z-index:9000;';
}
$wrap_class = $is_rail
? 'asx-floatwrap asx-railwrap'
: 'asx-floatwrap asx-floatingwrap';
$wrap_open = '<div id="' . esc_attr($wrap_id) . '" class="' . esc_attr($wrap_class) . '" data-asx-hide="1" data-asx-side="' . esc_attr($float_side) . '" data-asx-initial-dock="' . esc_attr($initial_dock) . '" data-asx-page-kind="' . esc_attr($page_kind) . '" data-asx-surface="' . esc_attr($surface_kind) . '" style="' . esc_attr($wrap_style) . '">';
echo wp_kses($wrap_open, allspice_allowed_html());
echo wp_kses($html, allspice_allowed_html());
echo wp_kses('</div>', allspice_allowed_html());
$event_payload = array(
'kind' => $is_rail ? 'rail' : 'floating',
'surface' => $surface_kind,
'wrapId' => $wrap_id,
'buttonId' => $button_id,
'ts' => time(),
);
$js = '(() => { try {
const detail = ' . wp_json_encode($event_payload, JSON_UNESCAPED_SLASHES) . ';
window.__ALLSPICE_SLOT_QUEUE = window.__ALLSPICE_SLOT_QUEUE || [];
window.__ALLSPICE_SLOT_QUEUE.push(detail);
try { document.dispatchEvent(new CustomEvent("allspice:slotAdded", { detail })); }
catch(e1){
try { var ev=document.createEvent("CustomEvent"); ev.initCustomEvent("allspice:slotAdded", true, true, detail); document.dispatchEvent(ev); }
catch(e2){}
}
} catch(e3){} })();';
if (wp_script_is('allspice-widget-page', 'enqueued') || wp_script_is('allspice-widget-page', 'registered')) {
wp_add_inline_script('allspice-widget-page', $js, 'after');
} else {
echo '<script>' . $js . '</script>';
}
}
function allspice_normalize_placement(string $style, string $placement): string {
$style = strtolower($style);
$placement = strtolower($placement);
if ($style === 'inline') {
$ok = array(
'manual_only',
'top_of_post',
'above_recipe_card',
'under_recipe_card',
'end_of_post',
'after_introduction',
'after_first_section'
);
return in_array($placement, $ok, true) ? $placement : 'manual_only';
}
if ($style === 'floating') {
$ok = array('manual_only','automatic');
return in_array($placement, $ok, true) ? $placement : 'manual_only';
}
return 'manual_only';
}
function allspice_find_first_wprm_recipe_open_tag(string $content): ?array {
if (!preg_match_all('/<div\b[^>]*class=(["\'])(.*?)\1[^>]*>/is', $content, $matches, PREG_OFFSET_CAPTURE)) {
return null;
}
foreach ($matches[0] as $i => $fullMatch) {
$fullTag = $fullMatch[0];
$fullPos = $fullMatch[1];
$classValue = $matches[2][$i][0] ?? '';
$classes = preg_split('/\s+/', trim((string)$classValue));
if (!$classes || !is_array($classes)) continue;
$hasRecipe = in_array('wprm-recipe', $classes, true);
$hasContainer = in_array('wprm-recipe-container', $classes, true);
if ($hasRecipe && !$hasContainer) {
return array(
'tag' => $fullTag,
'start' => $fullPos,
'end' => $fullPos + strlen($fullTag),
);
}
}
return null;
}
function allspice_find_matching_div_end(string $content, int $openTagStart): ?int {
if (!preg_match('/<div\b[^>]*>/is', $content, $openMatch, PREG_OFFSET_CAPTURE, $openTagStart)) {
return null;
}
if ((int)$openMatch[0][1] !== $openTagStart) {
return null;
}
$cursor = $openTagStart;
$depth = 0;
$len = strlen($content);
while ($cursor < $len && preg_match('/<\/?div\b[^>]*>/is', $content, $m, PREG_OFFSET_CAPTURE, $cursor)) {
$tag = $m[0][0];
$pos = (int)$m[0][1];
if (preg_match('/^<div\b/is', $tag)) {
$depth++;
} else {
$depth--;
if ($depth === 0) {
return $pos + strlen($tag);
}
}
$cursor = $pos + strlen($tag);
}
return null;
}
function allspice_find_wprm_recipe_bounds(string $content): ?array {
$open = allspice_find_first_wprm_recipe_open_tag($content);
if (!$open) return null;
$closeEnd = allspice_find_matching_div_end($content, (int)$open['start']);
if ($closeEnd === null) return null;
return array(
'start' => (int)$open['start'],
'open_end' => (int)$open['end'],
'end' => (int)$closeEnd,
);
}
function allspice_post_manual_button_ids($postObj): array {
if (!$postObj || empty($postObj->post_content)) return array();
$c = (string)$postObj->post_content;
if (!preg_match_all('/allspice_button[^\]]*id\s*=\s*"([^"]+)"/i', $c, $m1)) $m1 = array(array(), array());
if (!preg_match_all('/data-allspice-button\s*=\s*"([^"]+)"/i', $c, $m2)) $m2 = array(array(), array());
if (!preg_match_all('/data-allspice-id\s*=\s*"([^"]+)"/i', $c, $m3)) $m3 = array(array(), array());
$ids = array_merge($m1[1] ?? array(), $m2[1] ?? array(), $m3[1] ?? array());
$ids = array_map('trim', $ids);
$ids = array_filter($ids);
return array_values(array_unique($ids));
}
function allspice_cached_button_map_by_id(): array {
$buttons = allspice_buttons_cache_get();
$map = array();
foreach ($buttons as $b) {
$id = trim((string)($b['id'] ?? ''));
if ($id !== '') $map[$id] = $b;
}
return $map;
}
function allspice_has_manual_inline_placements_in_post($postObj = null): bool {
if (!$postObj) { global $post; $postObj = $post; }
if (!$postObj) return false;
$ids = allspice_post_manual_button_ids($postObj);
if (!$ids) return false;
$map = allspice_cached_button_map_by_id();
foreach ($ids as $id) {
$b = $map[$id] ?? null;
if (!$b) continue;
$style = strtolower((string)($b['style'] ?? ''));
if ($style === 'inline') return true;
}
if (function_exists('has_block')) {
if (
has_block('allspice/inline-chat', $postObj) ||
has_block('allspice/inline-search', $postObj) ||
has_block('allspice/inline-overview-style-1', $postObj) ||
has_block('allspice/inline-overview-style-2', $postObj) ||
has_block('allspice/inline-cooking-cta', $postObj)
) return true;
}
return false;
}
function allspice_has_manual_rail_placements_in_post($postObj = null): bool {
if (!$postObj) {
global $post;
$postObj = $post;
}
if (!$postObj) {
return false;
}
$ids = allspice_post_manual_button_ids($postObj);
if ($ids) {
foreach ($ids as $id) {
if (allspice_is_rail_button_id((string)$id)) {
return true;
}
}
}
if (function_exists('has_block')) {
if (has_block('allspice/rail', $postObj)) {
return true;
}
}
return false;
}
function allspice_has_manual_standard_floating_placements_in_post($postObj = null): bool {
if (!$postObj) {
global $post;
$postObj = $post;
}
if (!$postObj) {
return false;
}
$ids = allspice_post_manual_button_ids($postObj);
if ($ids) {
$map = allspice_cached_button_map_by_id();
foreach ($ids as $id) {
$id = trim((string)$id);
if ($id === '') {
continue;
}
if (allspice_is_rail_button_id($id)) {
continue;
}
$b = $map[$id] ?? null;
if (!$b) {
continue;
}
if (allspice_is_standard_floating_button($b)) {
return true;
}
}
}
if (function_exists('has_block')) {
if (has_block('allspice/floating-button', $postObj)) {
return true;
}
}
return false;
}
function allspice_has_manual_floating_placements_in_post($postObj = null): bool {
return allspice_has_manual_rail_placements_in_post($postObj) || allspice_has_manual_standard_floating_placements_in_post($postObj);
}
function allspice_enqueue_widget_files(): void {
allspice_console_log('[Allspice] enqueue_widget_files needed=' . (!empty($GLOBALS['allspice_widget_needed']) ? '1' : '0'));
if (!defined('ALLSPICE_PAGE_BUNDLE_BASE')) {
allspice_console_log('[Allspice] missing page bundle env config');
return;
}
if (is_admin() || !allspice_widget_needed_now()) return;
allspice_console_log('[Allspice] enqueue_widget_files passed first guard.');
$ver = allspice_assets_version();
$base = trim((string) ALLSPICE_PAGE_BUNDLE_BASE);
$plugin_ver = defined('ALLSPICE_PLUGIN_VERSION')
? (string) ALLSPICE_PLUGIN_VERSION
: allspice_get_plugin_version();
$plugin_ver = preg_replace('/[^0-9.]/', '', $plugin_ver);
if ($base === '' || $plugin_ver === '') {
allspice_console_log('[Allspice] invalid page bundle config', [
'base' => $base,
'plugin_ver' => $plugin_ver,
]);
return;
}
$entry_file = defined('ALLSPICE_PAGE_ENTRY_FILE')
? trim((string) ALLSPICE_PAGE_ENTRY_FILE)
: 'main.js';
$chunks_dir = defined('ALLSPICE_PAGE_CHUNKS_DIR')
? trim((string) ALLSPICE_PAGE_CHUNKS_DIR)
: 'chunks/';
$page_version_base = trailingslashit(trailingslashit($base) . $plugin_ver);
$page_src = trailingslashit($page_version_base) . $entry_file;
$page_chunk_base = trailingslashit($page_version_base) . trailingslashit($chunks_dir);
wp_register_script('allspice-widget-page', $page_src, array(), $ver, true);
wp_enqueue_script('allspice-widget-page');
wp_register_style('allspice-compat', false, [], $ver);
wp_enqueue_style('allspice-compat');
wp_add_inline_style('allspice-compat',
'@media only screen and (max-width:450px){body:has(#allspicewidget) #universalPlayer_wrapper{align-self:end;left:-15px;bottom:60px}}'
);
$css_url = trailingslashit(ALLSPICE_WIDGET_BASE) . 'allspicewidget-' . $plugin_ver . '.css';
$umd_url = trailingslashit(ALLSPICE_WIDGET_BASE) . 'allspicewidget-' . $plugin_ver . '.umd.cjs';
wp_add_inline_script(
'allspice-widget-page',
'/* nowprocket */ ' . allspice_inline_lazy_asset_loader_js($css_url, $umd_url),
'before'
);
$s = allspice_opt_get();
$payload = array(
'partner_id' => (string)$s['partner_id'],
'domain_id' => (string)$s['domain_id'],
'fetched_at' => (int)$s['cache_fetched_at'],
'count' => (int)$s['buttons_count'],
'buttons' => allspice_buttons_cache_get(),
);
allspice_console_log('[Allspice] adding inline cache scripts for handle allspice-widget-page', [
'ver' => $ver,
'page_src' => $page_src,
'count' => $payload['count'] ?? null,
]);
wp_add_inline_script(
'allspice-widget-page',
'/* nowprocket */ window.__ALLSPICE_DEBUG=' . (allspice_is_debug() ? 'true' : 'false') . ';',
'before'
);
wp_add_inline_script(
'allspice-widget-page',
'/* nowprocket */ window.__ALLSPICE_VERSION=' . wp_json_encode($plugin_ver) . ';
window.__ALLSPICE_PAGE_BASE=' . wp_json_encode(trailingslashit($page_version_base)) . ';
window.__ALLSPICE_PAGE_CHUNK_BASE=' . wp_json_encode($page_chunk_base) . ';',
'before'
);
wp_add_inline_script(
'allspice-widget-page',
'/* nowprocket */ (() => {
try {
window.__ALLSPICE_BUTTONS_CACHE = ' . wp_json_encode($payload) . ';
window.__ALLSPICE_CACHE_READY = window.__ALLSPICE_CACHE_READY || {};
window.__ALLSPICE_CACHE_READY.buttons = true;
document.dispatchEvent(new CustomEvent("allspice:cacheReady", { detail: { kind: "buttons" } }));
} catch(e) {}
})();',
'before'
);
$tp_payload = allspice_theme_policy_payload();
wp_add_inline_script(
'allspice-widget-page',
'/* nowprocket */ (() => {
try {
window.__ALLSPICE_THEME_POLICY_CACHE = ' . wp_json_encode($tp_payload) . ';
window.__ALLSPICE_CACHE_READY.theme_policy = true;
document.dispatchEvent(new CustomEvent("allspice:cacheReady", { detail: { kind: "theme_policy" } }));
} catch(e) {}
})();',
'before'
);
$is_recipe = allspice_is_recipe_page();
$uk = null;
$rid = null;
$overview = null;
if ($is_recipe) {
$uk = allspice_current_url_key();
if (is_string($uk) && $uk !== '') {
$ctx_row = function_exists('allspice_recipes_get_overview_by_url_key')
? allspice_recipes_get_overview_by_url_key($uk)
: null;
if (is_array($ctx_row) || is_object($ctx_row)) {
if (is_object($ctx_row)) $ctx_row = (array) $ctx_row;
$rid = !empty($ctx_row['recipe_id']) ? (string)$ctx_row['recipe_id'] : null;
$overview = [
'url' => $ctx_row['url'] ?? null,
'wp_post_id' => $ctx_row['wp_post_id'] ?? null,
'time' => $ctx_row['time'] ?? null,
'meta_description' => $ctx_row['meta_description'] ?? null,
'meta_description_valid' => array_key_exists('meta_description_valid', $ctx_row) ? $ctx_row['meta_description_valid'] : null,
'takeaways' => is_array($ctx_row['takeaways'] ?? null) ? $ctx_row['takeaways'] : [],
'faqs' => is_array($ctx_row['faqs'] ?? null) ? $ctx_row['faqs'] : [],
'servings' => $ctx_row['servings'] ?? null,
'calories' => $ctx_row['calories'] ?? null,
'recipe_title' => $ctx_row['recipe_title'] ?? null,
'ingredients' => is_array($ctx_row['ingredients'] ?? null) ? $ctx_row['ingredients'] : [],
];
} else {
$rid = allspice_recipes_get_id_by_url_key($uk);
if (!$rid) $rid = null;
}
} else {
$uk = null;
}
}
wp_add_inline_script(
'allspice-widget-page',
'/* nowprocket */ window.__ALLSPICE_RECIPE_CTX=' . wp_json_encode([
'is_recipe' => (bool)$is_recipe,
'url_key' => $uk,
'recipe_id' => $rid,
'overview' => $overview,
]) . ';',
'before'
);
}
add_action('wp_enqueue_scripts', 'allspice_enqueue_widget_files');
function allspice_enqueue_bootstrap(): void {
if (is_admin()) return;
$ver = allspice_assets_version();
$src = ALLSPICE_WIDGET_LOADER_URL . 'dist/asx-bootstrap.js';
wp_register_script('allspice-bootstrap', $src, array(), $ver, true);
wp_enqueue_script('allspice-bootstrap');
if (!defined('ALLSPICE_PAGE_BUNDLE_BASE')) {
allspice_console_log('[Allspice] missing page bundle env config');
return;
}
$base = trim((string) ALLSPICE_PAGE_BUNDLE_BASE);
$plugin_ver = defined('ALLSPICE_PLUGIN_VERSION')
? (string) ALLSPICE_PLUGIN_VERSION
: allspice_get_plugin_version();
$plugin_ver = preg_replace('/[^0-9.]/', '', $plugin_ver);
if ($plugin_ver === '') return;
$entry_file = defined('ALLSPICE_PAGE_ENTRY_FILE')
? trim((string) ALLSPICE_PAGE_ENTRY_FILE)
: 'main.js';
$chunks_dir = defined('ALLSPICE_PAGE_CHUNKS_DIR')
? trim((string) ALLSPICE_PAGE_CHUNKS_DIR)
: 'chunks/';
$page_version_base = trailingslashit(trailingslashit($base) . $plugin_ver);
$page_src = trailingslashit($page_version_base) . $entry_file;
$page_chunk_base = trailingslashit($page_version_base) . trailingslashit($chunks_dir);
$css_url = trailingslashit(ALLSPICE_WIDGET_BASE) . 'allspicewidget-' . $plugin_ver . '.css';
$umd_url = trailingslashit(ALLSPICE_WIDGET_BASE) . 'allspicewidget-' . $plugin_ver . '.umd.cjs';
$s = allspice_opt_get();
$buttons_payload = array(
'partner_id' => (string)($s['partner_id'] ?? ''),
'domain_id' => (string)($s['domain_id'] ?? ''),
'fetched_at' => (int)($s['cache_fetched_at'] ?? 0),
'count' => (int)($s['buttons_count'] ?? 0),
'buttons' => allspice_buttons_cache_get(),
);
$tp_payload = allspice_theme_policy_payload();
$is_recipe = allspice_is_recipe_page();
$recipe_ctx = array('is_recipe' => (bool)$is_recipe, 'url_key' => null, 'recipe_id' => null, 'overview' => null);
if ($is_recipe) {
$uk = allspice_current_url_key();
$rid = null;
$overview = null;
if (is_string($uk) && $uk !== '') {
$ctx_row = function_exists('allspice_recipes_get_overview_by_url_key')
? allspice_recipes_get_overview_by_url_key($uk)
: null;
if (is_array($ctx_row) || is_object($ctx_row)) {
if (is_object($ctx_row)) $ctx_row = (array)$ctx_row;
$rid = !empty($ctx_row['recipe_id']) ? (string)$ctx_row['recipe_id'] : null;
$overview = array(
'url' => $ctx_row['url'] ?? null,
'wp_post_id' => $ctx_row['wp_post_id'] ?? null,
'time' => $ctx_row['time'] ?? null,
'meta_description' => $ctx_row['meta_description'] ?? null,
'meta_description_valid' => array_key_exists('meta_description_valid', $ctx_row) ? $ctx_row['meta_description_valid'] : null,
'takeaways' => is_array($ctx_row['takeaways'] ?? null) ? $ctx_row['takeaways'] : array(),
'faqs' => is_array($ctx_row['faqs'] ?? null) ? $ctx_row['faqs'] : array(),
'servings' => $ctx_row['servings'] ?? null,
'calories' => $ctx_row['calories'] ?? null,
'recipe_title' => $ctx_row['recipe_title'] ?? null,
'ingredients' => is_array($ctx_row['ingredients'] ?? null) ? $ctx_row['ingredients'] : array(),
);
} else if (function_exists('allspice_recipes_get_id_by_url_key')) {
$rid = allspice_recipes_get_id_by_url_key($uk);
if (!$rid) $rid = null;
}
$recipe_ctx = array('is_recipe' => true, 'url_key' => $uk, 'recipe_id' => $rid, 'overview' => $overview);
}
}
$cfg = array(
'debug' => allspice_is_debug(),
'version' => $plugin_ver,
'page_src' => $page_src,
'page_base' => trailingslashit($page_version_base),
'page_chunk_base' => $page_chunk_base,
'css_url' => $css_url,
'umd_url' => $umd_url,
'buttons_cache' => $buttons_payload,
'theme_policy' => $tp_payload,
'recipe_ctx' => $recipe_ctx,
);
wp_add_inline_script(
'allspice-bootstrap',
'window.__ALLSPICE_BOOTSTRAP_CFG=' . wp_json_encode($cfg, JSON_UNESCAPED_SLASHES) . ';',
'before'
);
}
add_action('wp_enqueue_scripts', 'allspice_enqueue_bootstrap', 1);
function allspice_inline_lazy_asset_loader_js(string $css_url, string $umd_url): string {
$css_json = wp_json_encode($css_url, JSON_UNESCAPED_SLASHES);
$umd_json = wp_json_encode($umd_url, JSON_UNESCAPED_SLASHES);
$js = '(function(){';
$js .= 'var CSS_URL=' . $css_json . ';';
$js .= 'var UMD_URL=' . $umd_json . ';';
$js .= 'var LOAD_TIMEOUT_MS=8000;';
$js .= 'if (window.__ALLSPICE_LOAD_ASSETS) { return; }';
$js .= 'var cssPromise=null;';
$js .= 'var umdPromise=null;';
$js .= 'function loadCss(){';
$js .= ' if (cssPromise) return cssPromise;';
$js .= ' cssPromise=new Promise(function(resolve){';
$js .= ' if (!CSS_URL) { resolve(false); return; }';
$js .= ' try{';
$js .= ' var existing=document.querySelector(\'link[data-allspice-css="1"]\');';
$js .= ' if(existing){ resolve(true); return; }';
$js .= ' var done=false;';
$js .= ' function finish(ok){ if(done) return; done=true; resolve(ok); }';
$js .= ' var l=document.createElement("link");';
$js .= ' var t=setTimeout(function(){ finish(false); }, LOAD_TIMEOUT_MS);';
$js .= ' l.rel="stylesheet";';
$js .= ' l.href=CSS_URL;';
$js .= ' l.setAttribute("data-allspice-css","1");';
$js .= ' l.onload=function(){';
$js .= ' try{ clearTimeout(t); }catch(e){}';
$js .= ' try{document.dispatchEvent(new CustomEvent("allspice:cssReady"));}catch(e2){}';
$js .= ' finish(true);';
$js .= ' };';
$js .= ' l.onerror=function(){';
$js .= ' try{ clearTimeout(t); }catch(e){}';
$js .= ' finish(false);';
$js .= ' };';
$js .= ' (document.head||document.documentElement).appendChild(l);';
$js .= ' }catch(e3){ resolve(false); }';
$js .= ' });';
$js .= ' return cssPromise;';
$js .= '}';
$js .= 'function loadUmd(){';
$js .= ' if (umdPromise) return umdPromise;';
$js .= ' umdPromise=new Promise(function(resolve){';
$js .= ' if (!UMD_URL) { resolve(false); return; }';
$js .= ' try{';
$js .= ' var existing=document.querySelector(\'script[data-allspice-umd="1"]\');';
$js .= ' if(existing){ resolve(true); return; }';
$js .= ' var done=false;';
$js .= ' function finish(ok){ if(done) return; done=true; resolve(ok); }';
$js .= ' var s=document.createElement("script");';
$js .= ' var t=setTimeout(function(){ finish(false); }, LOAD_TIMEOUT_MS);';
$js .= ' s.src=UMD_URL;';
$js .= ' s.async=true;';
$js .= ' s.setAttribute("data-allspice-umd","1");';
$js .= ' s.onload=function(){';
$js .= ' try{ clearTimeout(t); }catch(e){}';
$js .= ' try{document.dispatchEvent(new CustomEvent("allspice:umdReady"));}catch(e2){}';
$js .= ' finish(true);';
$js .= ' };';
$js .= ' s.onerror=function(){';
$js .= ' try{ clearTimeout(t); }catch(e){}';
$js .= ' finish(false);';
$js .= ' };';
$js .= ' (document.head||document.documentElement).appendChild(s);';
$js .= ' }catch(e3){ resolve(false); }';
$js .= ' });';
$js .= ' return umdPromise;';
$js .= '}';
$js .= 'function loadAssets(){';
$js .= ' return Promise.all([loadCss(), loadUmd()]).then(function(res){';
$js .= ' return { css: !!res[0], umd: !!res[1] };';
$js .= ' });';
$js .= '}';
$js .= 'window.__ALLSPICE_LOAD_CSS = loadCss;';
$js .= 'window.__ALLSPICE_LOAD_UMD = loadUmd;';
$js .= 'window.__ALLSPICE_LOAD_ASSETS = loadAssets;';
$js .= 'function armOnce(){';
$js .= ' var fired=false;';
$js .= ' function onFirst(){';
$js .= ' if(fired) return; fired=true;';
$js .= ' try{window.removeEventListener("pointerdown",onFirst,true);}catch(e){}';
$js .= ' try{window.removeEventListener("touchstart",onFirst,true);}catch(e){}';
$js .= ' try{window.removeEventListener("keydown",onFirst,true);}catch(e){}';
$js .= ' try{window.removeEventListener("scroll",onFirst,true);}catch(e){}';
$js .= ' loadAssets();';
$js .= ' }';
$js .= ' try{window.addEventListener("pointerdown",onFirst,true);}catch(e){}';
$js .= ' try{window.addEventListener("touchstart",onFirst,true);}catch(e){}';
$js .= ' try{window.addEventListener("keydown",onFirst,true);}catch(e){}';
$js .= ' try{window.addEventListener("scroll",onFirst,true);}catch(e){}';
$js .= '}';
$js .= 'function isHostedAuthReturn(){';
$js .= ' try {';
$js .= ' var hash = String(window.location.hash || "");';
$js .= ' if (hash.indexOf("asx_auth_code=") !== -1) return true;';
$js .= ' if (hash.indexOf("asx_auth_provider=") !== -1) return true;';
$js .= ' if (hash.indexOf("signInSource=hosted_oauth") !== -1) return true;';
$js .= ' if (window.localStorage && localStorage.getItem("allspiceHostedAuthState")) return true;';
$js .= ' if (window.sessionStorage) {';
$js .= ' var rawRestore = sessionStorage.getItem("allspiceHostedAuthUIRestore");';
$js .= ' if (rawRestore) {';
$js .= ' try {';
$js .= ' var restore = JSON.parse(rawRestore);';
$js .= ' if (restore && restore.savedAt && Date.now() - restore.savedAt < 10 * 60 * 1000) return true;';
$js .= ' } catch(e2) {}';
$js .= ' }';
$js .= ' }';
$js .= ' } catch(e) {}';
$js .= ' return false;';
$js .= '}';
$js .= 'function isAllspiceAutoOpenHash(){';
$js .= ' try {';
$js .= ' var hash = String(window.location.hash || "");';
$js .= ' return (';
$js .= ' hash.indexOf("#allspice-search") === 0 ||';
$js .= ' hash.indexOf("#allspice-guided-cooking") === 0 ||';
$js .= ' hash.indexOf("#allspice-recipe") === 0 ||';
$js .= ' hash.indexOf("#allspice-groceries") === 0 ||';
$js .= ' hash.indexOf("#allspice-save") === 0';
$js .= ' );';
$js .= ' } catch(e) {}';
$js .= ' return false;';
$js .= '}';
$js .= 'armOnce();';
$js .= 'if (isHostedAuthReturn() || isAllspiceAutoOpenHash()) {';
$js .= ' try { console.info("[Allspice Auth Bridge] Hosted auth return or auto-open detected; loading widget assets immediately."); } catch(e) {}';
$js .= ' loadAssets();';
$js .= '} else {';
$js .= ' try{ setTimeout(function(){ loadAssets(); }, 5000); }catch(e){}';
$js .= '}';
$js .= '})();';
return $js;
}