Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
64 changes: 51 additions & 13 deletions inc/class-scripts.php
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ public function init(): void {

add_action('admin_enqueue_scripts', [$this, 'enqueue_default_admin_scripts']);

add_action('admin_head', [$this, 'print_wubox_early_click_guard'], 1);

add_action('wp_ajax_wu_toggle_container', [$this, 'update_use_container']);

add_filter('admin_body_class', [$this, 'add_body_class_container_boxed']);
Expand Down Expand Up @@ -299,19 +301,7 @@ public function register_default_scripts(): void {
*/
wp_add_inline_script(
'wubox',
"(function(){
window.__wuboxEarlyClicks=[];
window.__wuboxEarlyClickHandler=function(e){
if(window.__wuboxReady)return;
var t=e.target.closest('.wubox');
if(!t)return;
e.preventDefault();
e.stopPropagation();
t.style.cursor='wait';
window.__wuboxEarlyClicks.push(t);
};
document.addEventListener('click',window.__wuboxEarlyClickHandler,true);
})();",
$this->get_wubox_early_click_guard_script(),
'before'
);

Expand Down Expand Up @@ -470,6 +460,54 @@ public function enqueue_default_admin_scripts(string $hook_suffix): void {
wp_enqueue_script('wu-password-toggle');
}

/**
* Prints the early wubox click guard in the document head.
*
* Wubox itself is printed in the footer, so links/buttons rendered earlier in
* the admin page can navigate to their AJAX URL before the footer inline script
* runs. Printing the capture listener in the head closes that race for all
* modal form triggers that use the shared `.wubox` class.
*
* @since 2.6.3
* @return void
*/
public function print_wubox_early_click_guard(): void {

if ( ! wu_is_wu_page() || ! wp_script_is('wubox', 'enqueued')) {
return;
}

printf(
"\n<script id=\"wu-wubox-early-click-guard\">\n%s\n</script>\n",
$this->get_wubox_early_click_guard_script() // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Static JavaScript, no user input.
);
}

/**
* Returns the idempotent early wubox click guard script.
*
* @since 2.6.3
* @return string
*/
protected function get_wubox_early_click_guard_script(): string {

return "(function(){
window.__wuboxEarlyClicks=window.__wuboxEarlyClicks||[];
if(window.__wuboxEarlyClickHandler)return;
window.__wuboxEarlyClickHandler=function(e){
if(window.__wuboxReady)return;
var t=e.target&&e.target.closest?e.target.closest('.wubox'):null;
if(!t)return;
e.preventDefault();
e.stopPropagation();
if(e.stopImmediatePropagation)e.stopImmediatePropagation();
t.style.cursor='wait';
window.__wuboxEarlyClicks.push(t);
};
document.addEventListener('click',window.__wuboxEarlyClickHandler,true);
})();";
}

/**
* Update the use container setting.
*
Expand Down
35 changes: 35 additions & 0 deletions tests/WP_Ultimo/Scripts_Test.php
Original file line number Diff line number Diff line change
Expand Up @@ -252,9 +252,44 @@ public function test_init_registers_hooks(): void {
$this->assertNotFalse(has_action('init', [$this->scripts, 'register_default_styles']));
$this->assertNotFalse(has_action('admin_enqueue_scripts', [$this->scripts, 'enqueue_default_admin_styles']));
$this->assertNotFalse(has_action('admin_enqueue_scripts', [$this->scripts, 'enqueue_default_admin_scripts']));
$this->assertNotFalse(has_action('admin_head', [$this->scripts, 'print_wubox_early_click_guard']));
$this->assertNotFalse(has_filter('admin_body_class', [$this->scripts, 'add_body_class_container_boxed']));
}

/**
* Test the wubox early click guard preserves queued clicks.
*/
public function test_wubox_early_click_guard_is_idempotent_and_preserves_queue(): void {

$reflection = new \ReflectionClass($this->scripts);
$method = $reflection->getMethod('get_wubox_early_click_guard_script');

if (PHP_VERSION_ID < 80100) {
$method->setAccessible(true);
}

$script = $method->invoke($this->scripts);

$this->assertStringContainsString('window.__wuboxEarlyClicks=window.__wuboxEarlyClicks||[];', $script);
$this->assertStringContainsString('if(window.__wuboxEarlyClickHandler)return;', $script);
$this->assertStringContainsString("e.target.closest('.wubox')", $script);
$this->assertStringContainsString("document.addEventListener('click',window.__wuboxEarlyClickHandler,true);", $script);
}

/**
* Test register_default_scripts attaches the same early click guard to wubox.
*/
public function test_register_default_scripts_attaches_wubox_early_click_guard(): void {

$this->scripts->register_default_scripts();

$before = wp_scripts()->get_data('wubox', 'before');

$this->assertIsArray($before);
$this->assertNotEmpty($before);
$this->assertStringContainsString('window.__wuboxEarlyClicks=window.__wuboxEarlyClicks||[];', implode("\n", $before));
}

/**
* Test register_default_scripts registers expected handles.
*/
Expand Down
Loading