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
128 changes: 116 additions & 12 deletions MerlinAU.asp
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,12 @@ var isEMailConfigEnabledInAMTM = false;
var scriptAutoUpdateCronSchedHR = 'TBD';
var fwAutoUpdateCheckCronSchedHR = 'TBD';
var isScriptUpdateAvailable = 'TBD';
var fwUpdateEstimatedRunDate = 'TBD';
var MinimumScriptFWRequired = 'TBD';

let pendingScriptUpdateGateCheck = false;
let scriptUpdateGateTries = 0;
const scriptUpdateGateMaxTries = 12;

const validationErrorMsg = 'Validation failed. Please correct invalid value and try again.';

Expand Down Expand Up @@ -170,7 +176,7 @@ function FormatNumericSetting (formInput)
const numberRegExp = '^[0-9]+$';
const daysOfWeekNumbr = ['0', '1', '2', '3', '4', '5', '6'];
const daysOfWeekNames = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'];
const daysOfWeekRexpN = '([S|s]un|[M|m]on|[T|t]ue|[W|w]ed|[T|t]hu|[F|f]ri|[S|s]at)';
const daysOfWeekRexpN = '([Ss]un|[Mm]on|[Tt]ue|[Ww]ed|[Tt]hu|[Ff]ri|[Ss]at)';
const daysOfWeekRexp1 = `${daysOfWeekRexpN}|[0-6]`;
const daysOfWeekRexp2 = `${daysOfWeekRexpN}[-]${daysOfWeekRexpN}|[0-6][-][0-6]`;
const daysOfWeekRexp3 = `${daysOfWeekRexpN}([,]${daysOfWeekRexpN})+|[0-6]([,][0-6])+`;
Expand Down Expand Up @@ -248,6 +254,84 @@ const fwScheduleTime =
}
};

function ExtractFWVersion(verStr)
{
if (!verStr) { return ''; }
let match = String(verStr).trim().match(/\d+(?:\.\d+)+/);
return match ? match[0] : String(verStr).trim();
}

// Prefer hidden #firmver (same nvram source, no formatting risk), else fallback to #fwVersionInstalled
function GetInstalledFWVersionFromUI()
{
let firmverInput = document.getElementById('firmver');
if (firmverInput && firmverInput.value)
{ return ExtractFWVersion(firmverInput.value); }

let fwCell = document.getElementById('fwVersionInstalled');
if (fwCell)
{ return ExtractFWVersion(fwCell.textContent); }

return '';
}

function RunScriptUpdateFirmwareGateCheck()
{
if (!pendingScriptUpdateGateCheck) { return; }

scriptUpdateGateTries++;

$.ajax({
url: '/ext/MerlinAU/checkHelper.js?_=' + new Date().getTime(),
dataType: 'script',
timeout: 5000,

success: function()
{
let requiredStr = ExtractFWVersion(MinimumScriptFWRequired);
let installedStr = GetInstalledFWVersionFromUI();

// If required version isn't ready yet, retry a few times
if (!requiredStr || requiredStr === 'TBD')
{
if (scriptUpdateGateTries < scriptUpdateGateMaxTries)
{ setTimeout(RunScriptUpdateFirmwareGateCheck, 1000); }
else
{ pendingScriptUpdateGateCheck = false; isFormSubmitting = false; }
return;
}

let installedNum = FWVersionStrToNum(installedStr);
let requiredNum = FWVersionStrToNum(requiredStr);

pendingScriptUpdateGateCheck = false;
isFormSubmitting = false;

if (installedNum === 0 || requiredNum === 0) { return; }

if (installedNum < requiredNum)
{
alert(
"**SCRIPT UPDATE BLOCKED**\n\n" +
"MerlinAU cannot update because your installed firmware is " +
"below the minimum required firmware for this script update.\n\n" +
"Installed firmware: " + installedStr + "\n" +
"Minimum required: " + requiredStr + "\n\n" +
"Please update your router firmware, then try again."
);
}
},

error: function()
{
if (scriptUpdateGateTries < scriptUpdateGateMaxTries)
{ setTimeout(RunScriptUpdateFirmwareGateCheck, 1000); }
else
{ pendingScriptUpdateGateCheck = false; isFormSubmitting = false; }
}
});
}

/**-------------------------------------**/
/** Added by Martinski W. [2025-Jan-24] **/
/**-------------------------------------**/
Expand Down Expand Up @@ -910,6 +994,10 @@ function ShowLatestChangelog(e)
box.scrollTop -= 40;
ev.preventDefault();
break;
case 'Escape':
$('#changelogModal').hide();
ev.preventDefault();
break;
default:
break;
}
Expand Down Expand Up @@ -997,7 +1085,7 @@ function ValidateDirectoryPath (formField, dirType)
function GetExternalCheckResults()
{
$.ajax({
url: '/ext/MerlinAU/checkHelper.js',
url: '/ext/MerlinAU/checkHelper.js?_=' + new Date().getTime(),
dataType: 'script',
timeout: 5000,
error: function(xhr){
Expand Down Expand Up @@ -1537,7 +1625,10 @@ function GetLoginPswdCheckStatus()

document.getElementById('LoginPswdStatusText').textContent = pswdStatusText;
showhide('LoginPswdStatusText',true);
loginPswdHint = loginPswdHint.replace (/PswdSTATUS/, pswdStatusHint1);
// Rebuild the base hint fresh EACH time (so PswdSTATUS always exists) //
let loginUserStr = document.getElementById('http_username')?.value.trim() || 'admin';
let baseHint = loginPswdStatHintMsg.replace(/LoginUSER/, loginUserStr);
loginPswdHint = baseHint.replace(/PswdSTATUS/, pswdStatusHint1);

pswdField = document.getElementById('routerPassword');
if (passwordFailed || pswdVerified || pswdUnverified)
Expand Down Expand Up @@ -1688,7 +1779,7 @@ function InitializeFields()
if (script_AutoUpdate_Check)
{
script_AutoUpdate_Check.checked = (custom_settings.Allow_Script_Auto_Update === 'ENABLED');
UpdateForceScriptCheckboxState(script_AutoUpdate_Check?.checked);
UpdateForceScriptCheckboxState(script_AutoUpdate_Check && script_AutoUpdate_Check.checked);
}

if (betaToReleaseUpdatesEnabled)
Expand Down Expand Up @@ -2132,7 +2223,14 @@ function initial()
hiddenFrame.onload = function()
{
console.log("Hidden frame loaded with server response.");

if (pendingScriptUpdateGateCheck)
{
// Wait a moment to allow the backend logic to finish writing checkHelper.js
setTimeout(RunScriptUpdateFirmwareGateCheck, 1000);
}
};

initializeCollapsibleSections();
}
}
Expand All @@ -2142,6 +2240,9 @@ function initial()
/**----------------------------------------**/
function SaveCombinedConfig()
{
// Reset containers per-save so stale values never leak forward //
advanced_settings = {};

// Clear the hidden field before saving //
document.getElementById('amng_custom').value = '';

Expand Down Expand Up @@ -2386,6 +2487,10 @@ function UpdateMerlinAUScript()
? 'start_MerlinAUscrptupdate_force'
: 'start_MerlinAUscrptupdate';

pendingScriptUpdateGateCheck = true;
scriptUpdateGateTries = 0;
isFormSubmitting = true;

document.form.action_script.value = actionScriptValue;
document.form.action_wait.value = 10;
showLoading();
Expand Down Expand Up @@ -2612,7 +2717,6 @@ function initializeCollapsibleSections()
<input type="hidden" id="nvram_model" value="<% nvram_get("model"); %>" />
<input type="hidden" id="nvram_build_name" value="<% nvram_get("build_name"); %>" />
<input type="hidden" id="nvram_productid" value="<% nvram_get("productid"); %>" />
<input type="hidden" name="installedfirm" value="<% nvram_get("innerver"); %>" />
<input type="hidden" name="amng_custom" id="amng_custom" value="" />

<table class="content" cellpadding="0" cellspacing="0" style="margin:0 auto;">
Expand All @@ -2636,7 +2740,7 @@ function initializeCollapsibleSections()
<div class="formfonttitle" id="headerTitle" style="text-align:center;">MerlinAU</div>
<div style="margin:10px 0 10px 5px;" class="splitLine"></div>
<div class="formfontdesc">This is the MerlinAU add-on integrated into the router WebUI
<span style="margin-left:8px;" id="WikiURL"">[
<span style="margin-left:8px;" id="WikiURL">[
<a style="font-weight:bolder; text-decoration:underline; cursor:pointer;"
href="https://github.com/ExtremeFiretop/MerlinAutoUpdate-Router/wiki"
title="Go to MerlinAU Wiki page" target="_blank">Wiki</a> ]
Expand Down Expand Up @@ -2761,7 +2865,7 @@ function initializeCollapsibleSections()
<input type="submit" id="FWUpdateCheckButton" onclick="CheckFirmwareUpdate();
return false;" value="F/W Update Check" class="button_gen savebutton" name="button">
<br>
<label style="color:#FFCC00; margin-top: 5px; margin-bottom:8x">
<label style="color:#FFCC00; margin-top: 5px; margin-bottom:8px">
<input type="checkbox" checked="" id="BypassPostponedDays" name="BypassPostponedDays"
style="padding:0; vertical-align:middle; position:relative; margin-left:-5px; margin-top:5px; margin-bottom:8px"/>Bypass postponed days</label>
</br>
Expand All @@ -2770,7 +2874,7 @@ function initializeCollapsibleSections()
<input type="submit" id="LatestChangelogButton" onclick="ShowLatestChangelog();
return false;" value="Latest Changelog" class="button_gen savebutton" title="View the latest changelog" name="button">
<br>
<label style="color:#FFCC00; margin-top: 5px; margin-bottom:8x">
<label style="color:#FFCC00; margin-top: 5px; margin-bottom:8px">
<input type="checkbox" id="approveChangelogCheck" name="approveChangelogCheck" onclick="ToggleChangelogApproval(this);"
style="padding:0; vertical-align:middle; position:relative; margin-left:-5px; margin-top:5px; margin-bottom:8px"/>Approve changelog</label>
</br>
Expand All @@ -2794,7 +2898,7 @@ function initializeCollapsibleSections()
<input type="submit" id="UninstallButton" onclick="Uninstall(); return false;"
value="Uninstall" class="button_gen savebutton" name="button">
<br>
<label style="color:#FFCC00; margin-top: 5px; margin-bottom:8x">
<label style="color:#FFCC00; margin-top: 5px; margin-bottom:8px">
<input type="checkbox" checked="" id="KeepConfigFile" name="KeepConfigFile"
style="padding:0; vertical-align:middle; position:relative; margin-left:-3px; margin-top:5px; margin-bottom:8px"/>Keep configuration file</label>
</br>
Expand All @@ -2812,7 +2916,7 @@ function initializeCollapsibleSections()
<tbody>
<tr>
<td colspan="2">
<form id="advancedOptionsForm">
<div id="advancedOptionsForm">
<table class="FormTable SettingsTable" width="100%" border="0" cellpadding="5" cellspacing="5" style="table-layout: fixed;">
<colgroup>
<col style="width: 37%;" />
Expand All @@ -2838,7 +2942,7 @@ function initializeCollapsibleSections()
onblur="ValidatePasswordString(this,'onBLUR')"
onkeyup="ValidatePasswordString(this,'onKEYUP')"/>
<div id="eyeToggle" onclick="togglePassword();"
style="position: absolute; display: inline-block; margin-left: 5px; vertical-align: middle; width:24px; height:24px; background:url('/images/icon-visible@2x.png') no-repeat center; background-size: contain; cursor: pointer;">
style="position: relative; display: inline-block; margin-left: 5px; vertical-align: middle; width:24px; height:24px; background:url('/images/icon-visible@2x.png') no-repeat center; background-size: contain; cursor: pointer;">
</div>
</div>
<br>
Expand Down Expand Up @@ -3086,7 +3190,7 @@ function initializeCollapsibleSections()
<input type="submit" onclick="SaveCombinedConfig(); return false;"
value="Save Configuration" class="button_gen savebutton" name="button">
</div>
</form></td></tr></tbody></table>
</div></td></tr></tbody></table>
<div id="footerTitle" style="margin-top:10px;text-align:center;">MerlinAU</div>
</td></tr></tbody></table></td></tr></table></td>
<td width="10"></td>
Expand Down
Loading