894 lines
30 KiB
JavaScript
894 lines
30 KiB
JavaScript
/**
|
|
* 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 = {
|
|
'&': '&',
|
|
'<': '<',
|
|
'>': '>',
|
|
'"': '"',
|
|
"'": '''
|
|
};
|
|
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);
|