Initial commit: Open sourcing all of the Maple Open Technologies code.

This commit is contained in:
Bartlomiej Mika 2025-12-02 14:33:08 -05:00
commit 755d54a99d
2010 changed files with 448675 additions and 0 deletions

View file

@ -0,0 +1,19 @@
/**
* MaplePress Admin Styles
*
* @package MaplePress
*/
.maplepress-admin {
max-width: 800px;
}
.maplepress-status-active {
color: #46b450;
font-weight: bold;
}
.maplepress-status-inactive {
color: #dc3232;
font-weight: bold;
}

View file

@ -0,0 +1,24 @@
/**
* MaplePress Public Styles
*
* @package MaplePress
*/
.maplepress-search-widget {
margin-bottom: 20px;
}
.maplepress-search-results {
margin-top: 20px;
}
.maplepress-search-result-item {
margin-bottom: 15px;
padding: 15px;
border: 1px solid #ddd;
border-radius: 4px;
}
.maplepress-search-result-item h3 {
margin-top: 0;
}

View file

@ -0,0 +1,440 @@
/**
* MaplePress Search Speed Test - Admin Styles
*/
.mpss-wrap {
max-width: 1200px;
margin: 20px auto 20px 0;
}
.mpss-card {
background: #fff;
border: 1px solid #ccd0d4;
box-shadow: 0 1px 1px rgba(0, 0, 0, 0.04);
padding: 20px;
margin-bottom: 20px;
}
.mpss-card h2 {
margin-top: 0;
display: flex;
align-items: center;
gap: 10px;
}
.mpss-card h3 {
border-bottom: 1px solid #eee;
padding-bottom: 10px;
margin-top: 25px;
margin-bottom: 15px;
}
/* Mode Selection */
.mpss-mode-selection {
margin: 20px 0;
}
.mpss-radio-label {
display: block;
padding: 15px;
margin: 10px 0;
border: 2px solid #ddd;
border-radius: 4px;
cursor: pointer;
transition: all 0.2s;
}
.mpss-radio-label:hover {
border-color: #2271b1;
background: #f9f9f9;
}
.mpss-radio-label input[type="radio"] {
margin-right: 10px;
}
.mpss-radio-label input[type="radio"]:checked + .mpss-radio-title {
color: #2271b1;
font-weight: 600;
}
.mpss-radio-label input[type="radio"]:checked ~ .mpss-radio-title {
color: #2271b1;
}
.mpss-radio-title {
font-weight: 600;
font-size: 14px;
display: block;
margin-bottom: 5px;
}
.mpss-radio-desc {
font-size: 13px;
color: #666;
display: block;
margin-left: 25px;
}
/* Test Info */
.mpss-test-info ul {
list-style: none;
margin: 10px 0;
padding: 0;
}
.mpss-test-info li {
padding: 8px 0;
border-bottom: 1px solid #f0f0f0;
}
.mpss-test-info li:last-child {
border-bottom: none;
}
/* Warning */
.mpss-warning {
background: #fff3cd;
border-left: 4px solid #ffc107;
padding: 12px 15px;
margin: 20px 0;
display: flex;
align-items: center;
gap: 10px;
}
.mpss-warning .dashicons {
color: #856404;
flex-shrink: 0;
}
/* Progress */
.mpss-progress {
margin: 20px 0;
}
.mpss-progress-bar {
width: 100%;
height: 30px;
background: #f0f0f0;
border-radius: 15px;
overflow: hidden;
margin-top: 15px;
}
.mpss-progress-fill {
height: 100%;
background: linear-gradient(90deg, #667eea, #764ba2);
transition: width 0.3s ease;
display: flex;
align-items: center;
justify-content: center;
color: #fff;
font-weight: 600;
position: relative;
overflow: hidden;
}
.mpss-progress-fill.pulsing::before {
content: '';
position: absolute;
top: 0;
left: -100%;
width: 100%;
height: 100%;
background: linear-gradient(90deg, transparent, rgba(255,255,255,0.3), transparent);
animation: shimmer 2s infinite;
}
@keyframes shimmer {
0% {
left: -100%;
}
100% {
left: 100%;
}
}
.mpss-progress-fill.pulsing {
animation: pulse 1.5s ease-in-out infinite;
}
@keyframes pulse {
0%, 100% {
opacity: 1;
}
50% {
opacity: 0.8;
}
}
.spin {
animation: spin 1s linear infinite;
}
@keyframes spin {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}
/* Test Meta */
.mpss-test-meta {
display: flex;
gap: 20px;
flex-wrap: wrap;
margin: 15px 0 25px 0;
padding: 15px;
background: #f9f9f9;
border-radius: 4px;
}
.mpss-test-meta span {
font-size: 14px;
}
.mpss-test-meta strong {
color: #2271b1;
}
/* Summary Cards */
.mpss-summary-cards {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
gap: 15px;
margin: 20px 0;
}
.mpss-profile-card {
border: 2px solid #ddd;
border-radius: 8px;
padding: 20px;
text-align: center;
transition: transform 0.2s;
}
.mpss-profile-card:hover {
transform: translateY(-2px);
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
}
.mpss-profile-card h4 {
margin: 0 0 15px 0;
font-size: 16px;
color: #333;
}
.mpss-profile-metrics {
display: flex;
justify-content: space-around;
margin: 15px 0;
}
.mpss-metric {
text-align: center;
}
.mpss-metric-label {
font-size: 11px;
color: #666;
text-transform: uppercase;
display: block;
margin-bottom: 5px;
}
.mpss-metric-value {
font-size: 18px;
font-weight: 600;
display: block;
}
.mpss-status-badge {
display: inline-block;
padding: 5px 12px;
border-radius: 12px;
font-size: 12px;
font-weight: 600;
margin-top: 10px;
}
.mpss-status-excellent {
background: #d4edda;
color: #155724;
border-color: #46b450;
}
.mpss-status-good {
background: #d1ecf1;
color: #0c5460;
border-color: #00a0d2;
}
.mpss-status-fair {
background: #fff3cd;
color: #856404;
border-color: #ffc107;
}
.mpss-status-poor {
background: #f8d7da;
color: #721c24;
border-color: #dc3545;
}
.mpss-status-critical {
background: #dc3232;
color: #fff;
border-color: #dc3232;
}
.mpss-profile-details {
font-size: 12px;
color: #666;
margin-top: 10px;
}
/* Chart Container */
.mpss-chart-container {
margin: 30px 0;
padding: 20px;
background: #fff;
border: 1px solid #ddd;
border-radius: 4px;
}
#mpss-chart {
max-height: 400px;
}
/* Results Table */
.mpss-table-container {
margin: 30px 0;
}
#mpss-results-table {
margin-top: 15px;
}
#mpss-results-table th {
font-weight: 600;
}
#mpss-results-table td {
vertical-align: middle;
}
/* Recommendations */
.mpss-recommendations {
margin: 30px 0;
}
.mpss-recommendation {
padding: 15px;
margin: 15px 0;
border-left: 4px solid;
border-radius: 4px;
}
.mpss-recommendation h4 {
margin: 0 0 10px 0;
font-size: 14px;
}
.mpss-recommendation p {
margin: 0;
font-size: 13px;
}
.mpss-recommendation-success {
background: #d4edda;
border-color: #46b450;
color: #155724;
}
.mpss-recommendation-info {
background: #d1ecf1;
border-color: #00a0d2;
color: #0c5460;
}
.mpss-recommendation-warning {
background: #fff3cd;
border-color: #ffc107;
color: #856404;
}
.mpss-recommendation-error {
background: #f8d7da;
border-color: #dc3545;
color: #721c24;
}
.mpss-recommendation-critical {
background: #dc3232;
border-color: #dc3232;
color: #fff;
}
/* Site Info */
.mpss-site-info {
margin: 30px 0;
padding: 20px;
background: #f9f9f9;
border-radius: 4px;
}
.mpss-site-info ul {
list-style: none;
margin: 10px 0;
padding: 0;
}
.mpss-site-info li {
padding: 8px 0;
font-size: 14px;
}
.mpss-site-info strong {
color: #2271b1;
min-width: 150px;
display: inline-block;
}
/* Actions */
.mpss-actions {
display: flex;
gap: 10px;
margin-top: 30px;
padding-top: 20px;
border-top: 1px solid #ddd;
}
.mpss-actions button {
display: flex;
align-items: center;
gap: 5px;
}
/* Responsive */
@media (max-width: 768px) {
.mpss-summary-cards {
grid-template-columns: 1fr;
}
.mpss-test-meta {
flex-direction: column;
gap: 10px;
}
.mpss-actions {
flex-direction: column;
}
.mpss-actions button {
width: 100%;
justify-content: center;
}
}

View file

@ -0,0 +1,15 @@
/**
* MaplePress Admin JavaScript
*
* @package MaplePress
*/
(function($) {
'use strict';
$(document).ready(function() {
// Admin-specific JavaScript can be added here
console.log('MaplePress Admin JS loaded');
});
})(jQuery);

View file

@ -0,0 +1,15 @@
/**
* MaplePress Public JavaScript
*
* @package MaplePress
*/
(function($) {
'use strict';
$(document).ready(function() {
// Public-facing JavaScript can be added here
console.log('MaplePress Public JS loaded');
});
})(jQuery);

View file

@ -0,0 +1,894 @@
/**
* MaplePress Search Speed Test - Simple Admin JavaScript
*/
(function($) {
'use strict';
var testResults = null;
var performanceChart = null;
$(document).ready(function() {
// Note: Notification permission will be requested when user runs first test
// (requestNotificationPermission() can only be called from user gesture)
// Run test button
$('#mpss-run-test').on('click', function() {
runSpeedTest();
});
// Cancel test button
$(document).on('click', '#mpss-cancel-test', function() {
if (confirm('Are you sure you want to cancel the test?')) {
cancelTest();
}
});
// Run again button
$(document).on('click', '#mpss-run-again', function() {
resetTest();
});
// Export results button
$(document).on('click', '#mpss-export-results', function() {
exportResults();
});
// Share results button
$(document).on('click', '#mpss-share-results', function() {
showShareModal();
});
// Close share modal
$(document).on('click', '#mpss-close-share-modal', function() {
closeShareModal();
});
// Share buttons
$(document).on('click', '.mpss-share-twitter', function() {
shareToTwitter();
});
$(document).on('click', '.mpss-share-facebook', function() {
shareToFacebook();
});
$(document).on('click', '.mpss-share-linkedin', function() {
shareToLinkedIn();
});
$(document).on('click', '.mpss-share-copy', function() {
copyShareLink();
});
// Handle mode card selection
$('.mpss-mode-card').on('click', function() {
var $radio = $(this).find('input[type="radio"]');
$radio.prop('checked', true);
// Update visual state of cards
$('.mpss-mode-card-inner').css({
'border-color': '#e0e0e0',
'box-shadow': '0 1px 3px rgba(0, 0, 0, 0.05)',
'transform': 'scale(1)'
});
$(this).find('.mpss-mode-card-inner').css({
'border-color': '#667eea',
'box-shadow': '0 4px 15px rgba(102, 126, 234, 0.3)',
'transform': 'scale(1.02)'
});
});
// Add hover effects for mode cards
$('.mpss-mode-card').hover(
function() {
if (!$(this).find('input[type="radio"]').is(':checked')) {
$(this).find('.mpss-mode-card-inner').css({
'transform': 'translateY(-2px)',
'box-shadow': '0 4px 10px rgba(0, 0, 0, 0.1)'
});
}
},
function() {
if (!$(this).find('input[type="radio"]').is(':checked')) {
$(this).find('.mpss-mode-card-inner').css({
'transform': 'scale(1)',
'box-shadow': '0 1px 3px rgba(0, 0, 0, 0.05)'
});
}
}
);
// Add hover effect for run button
$('#mpss-run-test').hover(
function() {
$(this).css({
'transform': 'translateY(-2px)',
'box-shadow': '0 6px 20px rgba(102, 126, 234, 0.5)'
});
},
function() {
$(this).css({
'transform': 'translateY(0)',
'box-shadow': '0 4px 15px rgba(102, 126, 234, 0.4)'
});
}
);
// Add focus effect for select dropdown
$('#mpss-query-count').on('focus', function() {
$(this).css({
'border-color': '#667eea',
'box-shadow': '0 0 0 3px rgba(102, 126, 234, 0.1)'
});
}).on('blur', function() {
$(this).css({
'border-color': '#e0e0e0',
'box-shadow': 'none'
});
});
// Initialize the first card as selected
$('.mpss-mode-card').first().find('.mpss-mode-card-inner').css({
'border-color': '#667eea',
'box-shadow': '0 4px 15px rgba(102, 126, 234, 0.3)',
'transform': 'scale(1.02)'
});
});
var progressInterval = null;
var currentAjaxRequest = null;
function runSpeedTest() {
// Request notification permission on first test run
requestNotificationPermission();
var queryCount = parseInt($('#mpss-query-count').val()) || 10;
var executionMode = $('input[name="execution_mode"]:checked').val() || 'serial';
console.log('Starting speed test with ' + queryCount + ' queries in ' + executionMode + ' mode');
console.log('Execution mode value:', executionMode);
console.log('Radio button checked:', $('input[name="execution_mode"]:checked').length);
// Hide config, show progress
$('.mpss-test-config').fadeOut();
$('#mpss-results').hide();
$('#mpss-progress').fadeIn();
var modeText = executionMode === 'parallel' ? 'many at once' : 'one at a time';
$('#mpss-progress-text').text('Testing ' + queryCount + ' searches (' + modeText + ')...');
$('#mpss-progress-fill').css('width', '10%');
// Initialize counter
$('#mpss-total-count').text(queryCount);
$('#mpss-completed-count').text(0);
// Calculate and display estimated time
var estimatedSeconds = Math.ceil((queryCount * 200) / 1000); // 200ms per query
if (executionMode === 'parallel') {
estimatedSeconds = Math.ceil(estimatedSeconds * 0.1); // Parallel is much faster
}
updateTimeEstimate(estimatedSeconds);
// Start animated progress counter
startProgressAnimation(queryCount, estimatedSeconds);
// Make AJAX request
currentAjaxRequest = $.ajax({
url: mpssAjax.ajaxurl,
method: 'POST',
data: {
action: 'mpss_run_test',
nonce: mpssAjax.nonce,
query_count: queryCount,
execution_mode: executionMode
},
success: function(response) {
currentAjaxRequest = null;
console.log('Response received:', response);
// Stop the progress animation
stopProgressAnimation();
if (response.success) {
testResults = response.data;
console.log('Test results:', testResults);
// Complete the progress
$('#mpss-completed-count').text(queryCount);
$('#mpss-progress-fill').css('width', '100%');
$('#mpss-progress-text').text('Test complete!');
setTimeout(function() {
displayResults(testResults);
}, 500);
} else {
showError('Test failed: ' + (response.data ? response.data.message : 'Unknown error'));
}
},
error: function(xhr, status, error) {
currentAjaxRequest = null;
// Don't show error if request was aborted (cancelled by user)
if (status === 'abort') {
return;
}
console.error('AJAX error:', error);
stopProgressAnimation();
showError('Network error: ' + error);
}
});
}
function cancelTest() {
// Abort the AJAX request
if (currentAjaxRequest) {
currentAjaxRequest.abort();
currentAjaxRequest = null;
}
// Stop the animation
stopProgressAnimation();
// Hide progress and show config
$('#mpss-progress').fadeOut(function() {
$('.mpss-test-config').fadeIn();
});
// Show cancellation message
alert('Test cancelled');
}
function updateTimeEstimate(secondsRemaining) {
if (secondsRemaining <= 0) {
$('#mpss-time-estimate').text('Almost done...');
return;
}
var minutes = Math.floor(secondsRemaining / 60);
var seconds = secondsRemaining % 60;
var timeText = 'Estimated time: ';
if (minutes > 0) {
timeText += minutes + ' min ' + seconds + ' sec';
} else {
timeText += seconds + ' sec';
}
$('#mpss-time-estimate').text(timeText);
}
function startProgressAnimation(totalCount, estimatedSeconds) {
// Clear any existing interval
if (progressInterval) {
clearInterval(progressInterval);
}
var currentCount = 0;
var estimatedTime = totalCount * 200; // Rough estimate: 200ms per search
var updateInterval = 100; // Update every 100ms
var incrementPerUpdate = totalCount / (estimatedTime / updateInterval);
var reachedNinetyFive = false;
var timeRemaining = estimatedSeconds;
var lastTimeUpdate = Date.now();
progressInterval = setInterval(function() {
// Update time estimate every second
var now = Date.now();
if (now - lastTimeUpdate >= 1000) {
timeRemaining--;
updateTimeEstimate(timeRemaining);
lastTimeUpdate = now;
}
// After reaching 95%, slow down significantly
if (currentCount >= totalCount * 0.95 && !reachedNinetyFive) {
reachedNinetyFive = true;
incrementPerUpdate = incrementPerUpdate * 0.1; // Slow down to 10% speed
// Update the message and add pulsing animation
$('#mpss-progress-text').html('Almost done, finalizing results...');
$('#mpss-progress-fill').addClass('pulsing');
}
currentCount += incrementPerUpdate;
// Cap at 99% until we get the real result
var displayCount = Math.min(Math.floor(currentCount), Math.floor(totalCount * 0.99));
var percentage = Math.min((displayCount / totalCount) * 100, 99);
$('#mpss-completed-count').text(displayCount);
$('#mpss-progress-fill').css('width', percentage + '%');
// Stop at 99%
if (displayCount >= totalCount * 0.99) {
clearInterval(progressInterval);
progressInterval = null;
}
}, updateInterval);
}
function stopProgressAnimation() {
if (progressInterval) {
clearInterval(progressInterval);
progressInterval = null;
}
}
function displayResults(data) {
console.log('Displaying results...');
// Calculate average time for notification
var avgTime = data.total_time / data.total_queries;
// Play completion sound and show notification
playCompletionSound();
showTestCompleteNotification(avgTime, data.total_queries);
// Hide progress, show results
$('#mpss-progress').fadeOut();
$('#mpss-results').fadeIn();
// Display recommendations
displayRecommendations(data.total_queries, data.mode);
// Display summary stats (only total queries and total time)
$('#mpss-total-queries').text(data.total_queries.toLocaleString());
$('#mpss-total-time').text(data.total_time + ' sec');
// Add mode-specific explanation
var $chart = $('#mpss-chart');
var existingExplanation = $chart.parent().find('.mpss-mode-explanation');
if (existingExplanation.length) {
existingExplanation.remove();
}
var explanation = '';
if (data.mode === 'parallel') {
explanation = '<div class="mpss-mode-explanation" style="padding: 15px; margin-bottom: 20px; background: linear-gradient(to right, rgba(102, 126, 234, 0.1), rgba(118, 75, 162, 0.1)); border-left: 4px solid #667eea; border-radius: 8px;">' +
'<strong style="color: #667eea; font-size: 14px;">👥 Stress Test Mode:</strong><br>' +
'<span style="font-size: 13px; color: #666; line-height: 1.6;">' +
'We simulated <strong>' + data.total_queries + ' people</strong> searching your site at the same time. It took <strong>' + data.total_time + ' seconds</strong> to handle everyone.' +
'</span>' +
'</div>';
} else {
explanation = '<div class="mpss-mode-explanation" style="padding: 15px; margin-bottom: 20px; background: linear-gradient(to right, rgba(240, 147, 251, 0.1), rgba(245, 87, 108, 0.1)); border-left: 4px solid #f093fb; border-radius: 8px;">' +
'<strong style="color: #f093fb; font-size: 14px;">👤 Normal Mode:</strong><br>' +
'<span style="font-size: 13px; color: #666; line-height: 1.6;">' +
'We tested <strong>' + data.total_queries + ' searches</strong> one at a time. The total test took <strong>' + data.total_time + ' seconds</strong> to complete.' +
'</span>' +
'</div>';
}
$chart.parent().prepend(explanation);
// Display chart
displayChart(data);
}
function displayChart(data) {
var chartContainer = $('#mpss-chart').parent();
// Destroy existing chart
if (performanceChart) {
performanceChart.destroy();
performanceChart = null;
}
// Restore the canvas if needed
if (!chartContainer.find('canvas').length) {
chartContainer.html('<canvas id="mpss-chart"></canvas>');
}
var ctx = document.getElementById('mpss-chart').getContext('2d');
// Prepare data
var labels = data.results.map(function(r, i) { return 'Query ' + (i + 1); });
var times = data.results.map(function(r) { return r.duration_ms; });
// Determine title based on mode
var chartTitle = 'How Long Each Search Took';
var chartSubtitle = '';
if (data.mode === 'parallel') {
chartSubtitle = 'Each dot shows how long one search took (all happening at the same time)';
} else {
chartSubtitle = 'Each dot shows how long one search took (one after another)';
}
// Create chart
performanceChart = new Chart(ctx, {
type: 'line',
data: {
labels: labels,
datasets: [{
label: 'Response Time (ms)',
data: times,
backgroundColor: 'rgba(102, 126, 234, 0.3)',
borderColor: 'rgba(102, 126, 234, 1)',
borderWidth: 2,
pointRadius: 3,
tension: 0.1
}]
},
options: {
responsive: true,
maintainAspectRatio: true,
plugins: {
legend: {
position: 'top',
},
title: {
display: true,
text: chartTitle
},
subtitle: {
display: true,
text: chartSubtitle,
font: {
size: 11,
style: 'italic'
},
color: '#666'
}
},
scales: {
x: {
display: false
},
y: {
beginAtZero: true,
title: {
display: true,
text: 'Response Time (ms)'
}
}
}
}
});
}
function exportResults() {
if (!testResults) {
alert('No results to export');
return;
}
// Calculate metrics
var avgTime = testResults.total_time / testResults.total_queries;
var avgMs = (avgTime * 1000).toFixed(0);
var modeLabel = testResults.mode === 'parallel' ? 'Stress Test - Many People at Once' : 'Normal - One Person at a Time';
var date = new Date();
var dateStr = date.toLocaleDateString() + ' ' + date.toLocaleTimeString();
// Calculate statistics
var times = testResults.results.map(function(r) { return r.duration_ms; });
var minTime = Math.min.apply(null, times);
var maxTime = Math.max.apply(null, times);
var medianTime = calculateMedian(times);
// Build HTML report
var html = '<!DOCTYPE html>\n';
html += '<html lang="en">\n';
html += '<head>\n';
html += ' <meta charset="UTF-8">\n';
html += ' <meta name="viewport" content="width=device-width, initial-scale=1.0">\n';
html += ' <title>Search Speed Test Report - ' + dateStr + '</title>\n';
html += ' <style>\n';
html += ' * { margin: 0; padding: 0; box-sizing: border-box; }\n';
html += ' body { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif; background: #f5f5f5; padding: 40px 20px; color: #333; line-height: 1.6; }\n';
html += ' .container { max-width: 1000px; margin: 0 auto; background: white; padding: 50px; border-radius: 20px; box-shadow: 0 10px 40px rgba(0, 0, 0, 0.1); }\n';
html += ' .header { text-align: center; margin-bottom: 50px; padding-bottom: 30px; border-bottom: 3px solid #667eea; }\n';
html += ' .header h1 { font-size: 42px; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); -webkit-background-clip: text; -webkit-text-fill-color: transparent; background-clip: text; margin-bottom: 15px; }\n';
html += ' .header .date { font-size: 16px; color: #666; }\n';
html += ' .summary-section { text-align: center; padding: 40px; background: linear-gradient(135deg, rgba(102, 126, 234, 0.1), rgba(118, 75, 162, 0.1)); border-radius: 16px; margin-bottom: 40px; border: 3px solid #667eea; }\n';
html += ' .summary-icon { font-size: 80px; margin-bottom: 20px; }\n';
html += ' .summary-title { font-size: 32px; font-weight: 700; color: #667eea; margin-bottom: 15px; }\n';
html += ' .summary-message { font-size: 18px; color: #666; }\n';
html += ' .metrics { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 20px; margin-bottom: 40px; }\n';
html += ' .metric { background: linear-gradient(135deg, rgba(102, 126, 234, 0.1), rgba(118, 75, 162, 0.1)); padding: 25px; border-radius: 12px; text-align: center; }\n';
html += ' .metric-label { font-size: 13px; color: #666; text-transform: uppercase; font-weight: 600; margin-bottom: 10px; }\n';
html += ' .metric-value { font-size: 32px; font-weight: 800; color: #667eea; }\n';
html += ' .section { margin-bottom: 40px; }\n';
html += ' .section h2 { font-size: 24px; color: #333; margin-bottom: 20px; padding-bottom: 10px; border-bottom: 2px solid #e0e0e0; }\n';
html += ' .info-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 15px; }\n';
html += ' .info-item { padding: 15px; background: #f9f9f9; border-radius: 8px; }\n';
html += ' .info-label { font-size: 12px; color: #666; text-transform: uppercase; margin-bottom: 5px; }\n';
html += ' .info-value { font-size: 18px; font-weight: 600; color: #333; }\n';
html += ' .footer { text-align: center; padding-top: 30px; margin-top: 50px; border-top: 2px solid #e0e0e0; color: #999; font-size: 14px; }\n';
html += ' @media print { body { background: white; padding: 0; } .container { box-shadow: none; padding: 30px; } }\n';
html += ' </style>\n';
html += '</head>\n';
html += '<body>\n';
html += ' <div class="container">\n';
html += ' <div class="header">\n';
html += ' <h1>Search Speed Test Report</h1>\n';
html += ' <div class="date">Generated on ' + escapeHtml(dateStr) + '</div>\n';
html += ' </div>\n';
html += ' \n';
html += ' <div class="summary-section">\n';
html += ' <div class="summary-icon">🚀</div>\n';
html += ' <div class="summary-title">Search Speed Test Results</div>\n';
html += ' <div class="summary-message">Average response time: ' + avgMs + ' ms</div>\n';
html += ' </div>\n';
html += ' \n';
html += ' <div class="metrics">\n';
html += ' <div class="metric">\n';
html += ' <div class="metric-label">Average Time</div>\n';
html += ' <div class="metric-value">' + avgMs + ' ms</div>\n';
html += ' </div>\n';
html += ' <div class="metric">\n';
html += ' <div class="metric-label">Total Tests</div>\n';
html += ' <div class="metric-value">' + testResults.total_queries + '</div>\n';
html += ' </div>\n';
html += ' <div class="metric">\n';
html += ' <div class="metric-label">Total Time</div>\n';
html += ' <div class="metric-value">' + testResults.total_time + ' s</div>\n';
html += ' </div>\n';
html += ' <div class="metric">\n';
html += ' <div class="metric-label">Test Mode</div>\n';
html += ' <div class="metric-value" style="font-size: 18px;">' + escapeHtml(modeLabel.split(' - ')[0]) + '</div>\n';
html += ' </div>\n';
html += ' </div>\n';
html += ' \n';
html += ' <div class="section">\n';
html += ' <h2>Detailed Statistics</h2>\n';
html += ' <div class="info-grid">\n';
html += ' <div class="info-item">\n';
html += ' <div class="info-label">Fastest Response</div>\n';
html += ' <div class="info-value">' + minTime.toFixed(0) + ' ms</div>\n';
html += ' </div>\n';
html += ' <div class="info-item">\n';
html += ' <div class="info-label">Slowest Response</div>\n';
html += ' <div class="info-value">' + maxTime.toFixed(0) + ' ms</div>\n';
html += ' </div>\n';
html += ' <div class="info-item">\n';
html += ' <div class="info-label">Median Response</div>\n';
html += ' <div class="info-value">' + medianTime.toFixed(0) + ' ms</div>\n';
html += ' </div>\n';
html += ' <div class="info-item">\n';
html += ' <div class="info-label">Test Mode</div>\n';
html += ' <div class="info-value">' + escapeHtml(modeLabel) + '</div>\n';
html += ' </div>\n';
html += ' </div>\n';
html += ' </div>\n';
html += ' \n';
html += ' <div class="section">\n';
html += ' <h2>General Recommendations</h2>\n';
html += ' <div style="background: rgba(102, 126, 234, 0.1); padding: 25px; border-radius: 12px; border-left: 4px solid #667eea;">\n';
html += ' <p><strong>💡 Optimization Tips</strong></p>\n';
html += ' <p>Consider these strategies to improve search performance:</p>\n';
html += ' <ul style="margin-top: 15px; margin-left: 20px;">\n';
html += ' <li>Install and configure a caching plugin (e.g., WP Super Cache, W3 Total Cache)</li>\n';
html += ' <li>Optimize your database with plugins like WP-Optimize</li>\n';
html += ' <li>Review and optimize database indexes</li>\n';
html += ' <li>Consider upgrading your hosting plan for better resources</li>\n';
html += ' <li>Monitor performance regularly as your site grows</li>\n';
html += ' </ul>\n';
html += ' </div>\n';
html += ' </div>\n';
html += ' \n';
html += ' <div class="footer">\n';
html += ' <p>Generated by MaplePress Search Speed Test Plugin</p>\n';
html += ' <p>Website: ' + escapeHtml(window.location.hostname) + '</p>\n';
html += ' </div>\n';
html += ' </div>\n';
html += '</body>\n';
html += '</html>';
// Create and download the HTML file
var htmlBlob = new Blob([html], {type: 'text/html'});
var url = URL.createObjectURL(htmlBlob);
var link = document.createElement('a');
link.href = url;
link.download = 'search-speed-test-report-' + Date.now() + '.html';
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
URL.revokeObjectURL(url);
// Show success message
alert('✅ Report exported! Open the HTML file in your browser and use "Print to PDF" to create a PDF version.');
}
function calculateMedian(arr) {
var sorted = arr.slice().sort(function(a, b) { return a - b; });
var mid = Math.floor(sorted.length / 2);
if (sorted.length % 2 === 0) {
return (sorted[mid - 1] + sorted[mid]) / 2;
}
return sorted[mid];
}
function resetTest() {
$('#mpss-results').fadeOut();
$('.mpss-test-config').fadeIn();
testResults = null;
}
function showError(message) {
$('#mpss-progress').fadeOut();
$('.mpss-test-config').fadeIn();
alert('Error: ' + message);
}
function displayRecommendations(totalQueries, mode) {
var recommendations = [];
// Query count recommendations
if (totalQueries < 100) {
recommendations.push({
type: 'info',
icon: '📊',
title: 'Try more searches',
message: 'Testing with ' + totalQueries + ' searches gives you a basic idea. Try 500-1000 searches for more accurate results.'
});
}
// Mode-based recommendations
if (mode === 'serial') {
recommendations.push({
type: 'info',
icon: '🔄',
title: 'Try Stress Test mode',
message: 'You tested in Normal mode. Try "Stress Test - Many People at Once" to see how your site handles high traffic.'
});
} else {
recommendations.push({
type: 'info',
icon: '👥',
title: 'Consider traffic patterns',
message: 'Stress Test mode shows worst-case performance. If your site rarely gets simultaneous searches, Normal mode results matter more.'
});
}
// Build HTML
var html = '<div style="margin: 30px 0;">';
html += '<h3 style="margin-bottom: 20px; font-size: 18px; color: #333;">💡 Quick Tips</h3>';
for (var i = 0; i < recommendations.length; i++) {
var rec = recommendations[i];
var bgColor, borderColor;
if (rec.type === 'success') {
bgColor = '#d1fae5';
borderColor = '#10b981';
} else if (rec.type === 'info') {
bgColor = '#dbeafe';
borderColor = '#3b82f6';
} else if (rec.type === 'warning') {
bgColor = '#fef3c7';
borderColor = '#f59e0b';
} else {
bgColor = '#fee2e2';
borderColor = '#ef4444';
}
html += '<div style="padding: 15px; margin: 15px 0; background: ' + bgColor + '; border-left: 4px solid ' + borderColor + '; border-radius: 8px;">';
html += '<div style="font-size: 16px; font-weight: 600; color: #333; margin-bottom: 8px;">' + rec.icon + ' ' + rec.title + '</div>';
html += '<div style="font-size: 14px; color: #666; line-height: 1.6;">' + rec.message + '</div>';
html += '</div>';
}
html += '</div>';
$('#mpss-recommendations').html(html);
}
function escapeHtml(text) {
var map = {
'&': '&amp;',
'<': '&lt;',
'>': '&gt;',
'"': '&quot;',
"'": '&#039;'
};
return String(text).replace(/[&<>"']/g, function(m) { return map[m]; });
}
function showShareModal() {
if (!testResults) {
alert('No results to share');
return;
}
// Calculate metrics
var avgTime = testResults.total_time / testResults.total_queries;
var avgMs = (avgTime * 1000).toFixed(0);
var modeLabel = testResults.mode === 'parallel' ? 'Stress Test' : 'Normal';
// Build share graphic
var graphicHtml = '<div style="text-align: center;">';
graphicHtml += '<div style="font-size: 64px; margin-bottom: 15px;">🚀</div>';
graphicHtml += '<div style="font-size: 28px; font-weight: 700; margin-bottom: 10px;">My Search Speed Test Results</div>';
graphicHtml += '<div style="font-size: 48px; font-weight: 800; margin: 20px 0; text-shadow: 0 2px 10px rgba(0, 0, 0, 0.2);">' + avgMs + ' ms</div>';
graphicHtml += '<div style="font-size: 18px; opacity: 0.9; margin-bottom: 10px;">Average Response Time</div>';
graphicHtml += '<div style="font-size: 14px; opacity: 0.8; margin-top: 20px;">Tested ' + testResults.total_queries + ' searches in ' + modeLabel + ' mode</div>';
graphicHtml += '</div>';
$('#mpss-share-graphic').html(graphicHtml);
// Show modal with flex display
$('#mpss-share-modal').css('display', 'flex').hide().fadeIn(300);
}
function closeShareModal() {
$('#mpss-share-modal').fadeOut(300);
}
function shareToTwitter() {
if (!testResults) return;
var avgTime = testResults.total_time / testResults.total_queries;
var avgMs = (avgTime * 1000).toFixed(0);
var text = 'I just tested my website\'s search speed! 🚀 Average response time: ' + avgMs + 'ms #WebPerformance #SiteSpeed';
var url = window.location.href;
var twitterUrl = 'https://twitter.com/intent/tweet?text=' + encodeURIComponent(text) + '&url=' + encodeURIComponent(url);
window.open(twitterUrl, '_blank', 'width=550,height=420');
}
function shareToFacebook() {
if (!testResults) return;
var url = window.location.href;
var facebookUrl = 'https://www.facebook.com/sharer/sharer.php?u=' + encodeURIComponent(url);
window.open(facebookUrl, '_blank', 'width=550,height=420');
}
function shareToLinkedIn() {
if (!testResults) return;
var avgTime = testResults.total_time / testResults.total_queries;
var avgMs = (avgTime * 1000).toFixed(0);
var url = window.location.href;
var title = 'Website Search Speed Test Results';
var summary = 'I tested my website\'s search performance. Average response time: ' + avgMs + 'ms';
var linkedinUrl = 'https://www.linkedin.com/sharing/share-offsite/?url=' + encodeURIComponent(url);
window.open(linkedinUrl, '_blank', 'width=550,height=420');
}
function copyShareLink() {
if (!testResults) return;
var avgTime = testResults.total_time / testResults.total_queries;
var avgMs = (avgTime * 1000).toFixed(0);
var text = 'I just tested my website\'s search speed! 🚀 Average response time: ' + avgMs + 'ms\n' + window.location.href;
// Try to use modern clipboard API
if (navigator.clipboard && navigator.clipboard.writeText) {
navigator.clipboard.writeText(text).then(function() {
alert('✅ Link copied to clipboard!');
}).catch(function() {
fallbackCopyText(text);
});
} else {
fallbackCopyText(text);
}
}
function fallbackCopyText(text) {
// Fallback for older browsers
var textArea = document.createElement('textarea');
textArea.value = text;
textArea.style.position = 'fixed';
textArea.style.top = '0';
textArea.style.left = '0';
textArea.style.width = '2em';
textArea.style.height = '2em';
textArea.style.padding = '0';
textArea.style.border = 'none';
textArea.style.outline = 'none';
textArea.style.boxShadow = 'none';
textArea.style.background = 'transparent';
document.body.appendChild(textArea);
textArea.select();
try {
document.execCommand('copy');
alert('✅ Link copied to clipboard!');
} catch (err) {
alert('❌ Could not copy to clipboard. Please copy manually:\n\n' + text);
}
document.body.removeChild(textArea);
}
function requestNotificationPermission() {
// Check if browser supports notifications
if (!('Notification' in window)) {
console.log('This browser does not support desktop notifications');
return;
}
// Request permission if not already granted or denied
if (Notification.permission === 'default') {
Notification.requestPermission();
}
}
function showTestCompleteNotification(avgTime, totalQueries) {
// Check if notifications are supported and permitted
if (!('Notification' in window) || Notification.permission !== 'granted') {
return;
}
var avgMs = (avgTime * 1000).toFixed(0);
var title = 'Search Speed Test Complete!';
var body = 'Tested ' + totalQueries + ' searches. Average response time: ' + avgMs + 'ms';
var icon = ''; // WordPress doesn't have a default icon, but we could add one
try {
var notification = new Notification(title, {
body: body,
icon: icon,
badge: icon,
tag: 'mpss-test-complete',
requireInteraction: false
});
// Auto-close notification after 5 seconds
setTimeout(function() {
notification.close();
}, 5000);
// Focus window when notification is clicked
notification.onclick = function() {
window.focus();
notification.close();
};
} catch (e) {
console.log('Could not show notification:', e);
}
}
function playCompletionSound() {
// Create a simple success sound using Web Audio API
try {
var audioContext = new (window.AudioContext || window.webkitAudioContext)();
var oscillator = audioContext.createOscillator();
var gainNode = audioContext.createGain();
oscillator.connect(gainNode);
gainNode.connect(audioContext.destination);
// Set sound parameters for a pleasant "ding" sound
oscillator.frequency.value = 800; // Start frequency
oscillator.type = 'sine';
gainNode.gain.setValueAtTime(0.3, audioContext.currentTime);
gainNode.gain.exponentialRampToValueAtTime(0.01, audioContext.currentTime + 0.5);
oscillator.start(audioContext.currentTime);
oscillator.stop(audioContext.currentTime + 0.5);
// Add a second tone for a pleasant chord
setTimeout(function() {
var oscillator2 = audioContext.createOscillator();
var gainNode2 = audioContext.createGain();
oscillator2.connect(gainNode2);
gainNode2.connect(audioContext.destination);
oscillator2.frequency.value = 1000;
oscillator2.type = 'sine';
gainNode2.gain.setValueAtTime(0.2, audioContext.currentTime);
gainNode2.gain.exponentialRampToValueAtTime(0.01, audioContext.currentTime + 0.4);
oscillator2.start(audioContext.currentTime);
oscillator2.stop(audioContext.currentTime + 0.4);
}, 100);
} catch (e) {
console.log('Could not play sound:', e);
}
}
})(jQuery);