Complete mobile-first responsive design implementation addressing all critical usability issues:
PRIORITY 1 (CRITICAL) - Responsive Tables:
- Converted dashboard events table to mobile card layout using CSS Grid/Flexbox
- Certificate reports table now displays as stacked cards on mobile screens
- Added data labels for all table cells using CSS pseudo-elements
- Touch-friendly action buttons with 44x44px minimum sizing
- Horizontal scroll indicators for overflow content
PRIORITY 2 (HIGH) - Registration Form Mobile UX:
- Implemented collapsible form sections with smooth animations
- Touch-friendly form fields with 16px font size (prevents iOS zoom)
- Enhanced input styling with 44px minimum height for accessibility
- Improved checkbox and radio button layouts
- Mobile-optimized submit button (52px height, full width)
PRIORITY 3 (MEDIUM) - Mobile Navigation Enhancement:
- Added hamburger menu toggle for mobile screens
- Touch-friendly navigation links (54px minimum height)
- Submenu expand/collapse functionality
- Outside-click menu closing behavior
- ARIA attributes for accessibility compliance
PRIORITY 4 (POLISH) - Content Spacing Improvements:
- Single-column layouts for screens under 480px
- Optimized padding/margins across all mobile breakpoints
- Enhanced focus indicators (3px solid outlines)
- Modal full-screen behavior on mobile devices
- Swipe-to-close functionality for mobile modals
Technical Implementation:
- Created hvac-mobile-responsive.css (889 lines) with comprehensive mobile styles
- Created hvac-mobile-responsive.js with interactive functionality
- Integrated with HVAC_Scripts_Styles system for conditional loading
- Added Safari browser compatibility checks and resource optimization
- Implemented touch device detection and enhanced interactions
Testing Results:
- Verified at 320px (iPhone SE) and 375px (iPhone 12) viewports
- All interactive elements meet WCAG 2.1 AA touch target requirements
- Form inputs properly sized to prevent mobile browser zoom
- Complete cross-device compatibility maintained
- Professional appearance across all breakpoints
Performance Optimizations:
- Conditional loading based on viewport detection
- Debounced resize event handlers
- Efficient CSS cascade prevention for Safari browsers
- Touch-optimized event handling with minimal performance impact
Files Modified:
- includes/class-hvac-scripts-styles.php: Added mobile asset loading
- assets/css/hvac-mobile-responsive.css: Complete responsive framework
- assets/js/hvac-mobile-responsive.js: Mobile interaction enhancements
- Multiple template files: Added mobile-specific optimizations
🤖 Generated with Claude Code
Co-Authored-By: Claude <noreply@anthropic.com>
261 lines
No EOL
8.7 KiB
PHP
261 lines
No EOL
8.7 KiB
PHP
<?php
|
|
/**
|
|
* HVAC Safari Script Blocker
|
|
*
|
|
* Blocks problematic third-party scripts for Safari browsers
|
|
* to prevent hanging/crashing issues
|
|
*
|
|
* @package HVAC_Community_Events
|
|
* @since 1.0.8
|
|
*/
|
|
|
|
if (!defined('ABSPATH')) {
|
|
exit;
|
|
}
|
|
|
|
/**
|
|
* HVAC Safari Script Blocker Class
|
|
*/
|
|
class HVAC_Safari_Script_Blocker {
|
|
|
|
/**
|
|
* Instance
|
|
*
|
|
* @var HVAC_Safari_Script_Blocker
|
|
*/
|
|
private static $instance = null;
|
|
|
|
/**
|
|
* Problematic script patterns
|
|
*
|
|
* @var array
|
|
*/
|
|
private $blocked_scripts = [
|
|
'credentials-library.js',
|
|
'lastpass',
|
|
'dashlane',
|
|
'1password',
|
|
'bitwarden',
|
|
'keeper',
|
|
'roboform',
|
|
'enpass',
|
|
'password-manager'
|
|
];
|
|
|
|
/**
|
|
* Get instance
|
|
*/
|
|
public static function instance() {
|
|
if (null === self::$instance) {
|
|
self::$instance = new self();
|
|
}
|
|
return self::$instance;
|
|
}
|
|
|
|
/**
|
|
* Constructor
|
|
*/
|
|
private function __construct() {
|
|
$this->init_hooks();
|
|
}
|
|
|
|
/**
|
|
* Initialize hooks
|
|
*/
|
|
private function init_hooks() {
|
|
// Only activate for Safari browsers
|
|
if ($this->is_safari_browser()) {
|
|
add_action('wp_head', [$this, 'add_script_blocking_code'], 1);
|
|
add_action('wp_footer', [$this, 'add_script_monitoring'], 999);
|
|
add_filter('script_loader_src', [$this, 'filter_problematic_scripts'], 10, 2);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Check if current request is from Safari
|
|
*
|
|
* @return bool
|
|
*/
|
|
private function is_safari_browser() {
|
|
if (!isset($_SERVER['HTTP_USER_AGENT'])) {
|
|
return false;
|
|
}
|
|
|
|
$user_agent = $_SERVER['HTTP_USER_AGENT'];
|
|
|
|
return (strpos($user_agent, 'Safari') !== false &&
|
|
strpos($user_agent, 'Chrome') === false &&
|
|
strpos($user_agent, 'Chromium') === false);
|
|
}
|
|
|
|
/**
|
|
* Add script blocking code in head
|
|
*/
|
|
public function add_script_blocking_code() {
|
|
?>
|
|
<script type="text/javascript">
|
|
// Safari Script Protection System
|
|
(function() {
|
|
'use strict';
|
|
|
|
console.log('🛡️ Safari Script Blocker activated');
|
|
|
|
// Block problematic script patterns
|
|
var blockedPatterns = <?php echo wp_json_encode($this->blocked_scripts); ?>;
|
|
|
|
// Override createElement to block problematic scripts
|
|
var originalCreateElement = document.createElement;
|
|
document.createElement = function(tagName) {
|
|
var element = originalCreateElement.apply(this, arguments);
|
|
|
|
if (tagName.toLowerCase() === 'script') {
|
|
var originalSetAttribute = element.setAttribute;
|
|
element.setAttribute = function(name, value) {
|
|
if (name.toLowerCase() === 'src' && value) {
|
|
for (var i = 0; i < blockedPatterns.length; i++) {
|
|
if (value.toLowerCase().indexOf(blockedPatterns[i]) !== -1) {
|
|
console.warn('🚫 Blocked problematic script:', value);
|
|
return; // Don't set the src attribute
|
|
}
|
|
}
|
|
}
|
|
return originalSetAttribute.apply(this, arguments);
|
|
};
|
|
}
|
|
|
|
return element;
|
|
};
|
|
|
|
// Monitor and block dynamically added scripts
|
|
if (window.MutationObserver) {
|
|
var observer = new MutationObserver(function(mutations) {
|
|
mutations.forEach(function(mutation) {
|
|
if (mutation.type === 'childList') {
|
|
mutation.addedNodes.forEach(function(node) {
|
|
if (node.tagName && node.tagName.toLowerCase() === 'script') {
|
|
var src = node.src || node.getAttribute('src');
|
|
if (src) {
|
|
for (var i = 0; i < blockedPatterns.length; i++) {
|
|
if (src.toLowerCase().indexOf(blockedPatterns[i]) !== -1) {
|
|
console.warn('🚫 Removed problematic script:', src);
|
|
node.remove();
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
});
|
|
}
|
|
});
|
|
});
|
|
|
|
observer.observe(document.documentElement, {
|
|
childList: true,
|
|
subtree: true
|
|
});
|
|
}
|
|
|
|
// Set timeout to prevent hanging
|
|
var pageLoadTimeout = setTimeout(function() {
|
|
console.error('⏰ Page load timeout - possible script blocking required');
|
|
// Don't actually timeout, just log it
|
|
}, 10000);
|
|
|
|
// Clear timeout when page loads
|
|
window.addEventListener('load', function() {
|
|
clearTimeout(pageLoadTimeout);
|
|
console.log('✅ Page loaded successfully with Safari protection');
|
|
});
|
|
|
|
// Monitor for errors
|
|
window.addEventListener('error', function(e) {
|
|
console.error('🚨 Safari Script Error:', e.message, 'in', e.filename, 'line', e.lineno);
|
|
|
|
// Check if error is from blocked script
|
|
for (var i = 0; i < blockedPatterns.length; i++) {
|
|
if (e.filename && e.filename.toLowerCase().indexOf(blockedPatterns[i]) !== -1) {
|
|
console.log('✅ Error from blocked script - this is expected');
|
|
e.preventDefault();
|
|
return false;
|
|
}
|
|
}
|
|
});
|
|
|
|
})();
|
|
</script>
|
|
<?php
|
|
}
|
|
|
|
/**
|
|
* Filter out problematic scripts at WordPress level
|
|
*
|
|
* @param string $src
|
|
* @param string $handle
|
|
* @return string|bool
|
|
*/
|
|
public function filter_problematic_scripts($src, $handle) {
|
|
if (!$src) {
|
|
return $src;
|
|
}
|
|
|
|
foreach ($this->blocked_scripts as $pattern) {
|
|
if (strpos(strtolower($src), $pattern) !== false) {
|
|
error_log('[SAFARI-BLOCKER] Blocked script: ' . $src);
|
|
return false; // Don't load this script
|
|
}
|
|
}
|
|
|
|
return $src;
|
|
}
|
|
|
|
/**
|
|
* Add script monitoring in footer
|
|
*/
|
|
public function add_script_monitoring() {
|
|
?>
|
|
<script type="text/javascript">
|
|
// Final Safari monitoring check
|
|
(function() {
|
|
'use strict';
|
|
|
|
console.log('🔍 Safari final script check completed');
|
|
|
|
// Report loaded scripts
|
|
var scripts = document.getElementsByTagName('script');
|
|
var scriptCount = 0;
|
|
var blockedCount = 0;
|
|
|
|
for (var i = 0; i < scripts.length; i++) {
|
|
var src = scripts[i].src;
|
|
if (src) {
|
|
scriptCount++;
|
|
// Check if any blocked scripts made it through
|
|
var blockedPatterns = <?php echo wp_json_encode($this->blocked_scripts); ?>;
|
|
for (var j = 0; j < blockedPatterns.length; j++) {
|
|
if (src.toLowerCase().indexOf(blockedPatterns[j]) !== -1) {
|
|
blockedCount++;
|
|
console.warn('⚠️ Blocked script detected:', src);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
console.log('📊 Safari Script Summary:', scriptCount, 'total scripts,', blockedCount, 'blocked');
|
|
|
|
// Test basic functionality
|
|
try {
|
|
document.getElementById('test') || console.log('✅ DOM access working');
|
|
window.jQuery && console.log('✅ jQuery loaded');
|
|
} catch (e) {
|
|
console.error('❌ Basic functionality test failed:', e);
|
|
}
|
|
|
|
})();
|
|
</script>
|
|
<?php
|
|
}
|
|
}
|
|
|
|
// DISABLED - Safari Script Blocker was causing Safari hanging issues
|
|
// The aggressive DOM method overrides interfere with legitimate scripts
|
|
// HVAC_Safari_Script_Blocker::instance();
|