@@ -68,6 +68,12 @@ var isEMailConfigEnabledInAMTM = false;
6868var scriptAutoUpdateCronSchedHR = ' TBD' ;
6969var fwAutoUpdateCheckCronSchedHR = ' TBD' ;
7070var isScriptUpdateAvailable = ' TBD' ;
71+ var fwUpdateEstimatedRunDate = ' TBD' ;
72+ var MinimumScriptFWRequired = ' TBD' ;
73+
74+ let pendingScriptUpdateGateCheck = false ;
75+ let scriptUpdateGateTries = 0 ;
76+ const scriptUpdateGateMaxTries = 12 ;
7177
7278const validationErrorMsg = ' Validation failed. Please correct invalid value and try again.' ;
7379
@@ -170,7 +176,7 @@ function FormatNumericSetting (formInput)
170176const numberRegExp = ' ^[0-9]+$' ;
171177const daysOfWeekNumbr = [' 0' , ' 1' , ' 2' , ' 3' , ' 4' , ' 5' , ' 6' ];
172178const daysOfWeekNames = [' Sun' , ' Mon' , ' Tue' , ' Wed' , ' Thu' , ' Fri' , ' Sat' ];
173- const daysOfWeekRexpN = ' ([S|s ]un|[M|m ]on|[T|t ]ue|[W|w ]ed|[T|t ]hu|[F|f ]ri|[S|s ]at)' ;
179+ const daysOfWeekRexpN = ' ([Ss ]un|[Mm ]on|[Tt ]ue|[Ww ]ed|[Tt ]hu|[Ff ]ri|[Ss ]at)' ;
174180const daysOfWeekRexp1 = ` ${ daysOfWeekRexpN} |[0-6]` ;
175181const daysOfWeekRexp2 = ` ${ daysOfWeekRexpN} [-]${ daysOfWeekRexpN} |[0-6][-][0-6]` ;
176182const daysOfWeekRexp3 = ` ${ daysOfWeekRexpN} ([,]${ daysOfWeekRexpN} )+|[0-6]([,][0-6])+` ;
@@ -248,6 +254,84 @@ const fwScheduleTime =
248254 }
249255};
250256
257+ function ExtractFWVersion (verStr )
258+ {
259+ if (! verStr) { return ' ' ; }
260+ let match = String (verStr).trim ().match (/ \d + (?:\. \d + )+ / );
261+ return match ? match[0 ] : String (verStr).trim ();
262+ }
263+
264+ // Prefer hidden #firmver (same nvram source, no formatting risk), else fallback to #fwVersionInstalled
265+ function GetInstalledFWVersionFromUI ()
266+ {
267+ let firmverInput = document .getElementById (' firmver' );
268+ if (firmverInput && firmverInput .value )
269+ { return ExtractFWVersion (firmverInput .value ); }
270+
271+ let fwCell = document .getElementById (' fwVersionInstalled' );
272+ if (fwCell)
273+ { return ExtractFWVersion (fwCell .textContent ); }
274+
275+ return ' ' ;
276+ }
277+
278+ function RunScriptUpdateFirmwareGateCheck ()
279+ {
280+ if (! pendingScriptUpdateGateCheck) { return ; }
281+
282+ scriptUpdateGateTries++ ;
283+
284+ $ .ajax ({
285+ url: ' /ext/MerlinAU/checkHelper.js?_=' + new Date ().getTime (),
286+ dataType: ' script' ,
287+ timeout: 5000 ,
288+
289+ success : function ()
290+ {
291+ let requiredStr = ExtractFWVersion (MinimumScriptFWRequired);
292+ let installedStr = GetInstalledFWVersionFromUI ();
293+
294+ // If required version isn't ready yet, retry a few times
295+ if (! requiredStr || requiredStr === ' TBD' )
296+ {
297+ if (scriptUpdateGateTries < scriptUpdateGateMaxTries)
298+ { setTimeout (RunScriptUpdateFirmwareGateCheck, 1000 ); }
299+ else
300+ { pendingScriptUpdateGateCheck = false ; isFormSubmitting = false ; }
301+ return ;
302+ }
303+
304+ let installedNum = FWVersionStrToNum (installedStr);
305+ let requiredNum = FWVersionStrToNum (requiredStr);
306+
307+ pendingScriptUpdateGateCheck = false ;
308+ isFormSubmitting = false ;
309+
310+ if (installedNum === 0 || requiredNum === 0 ) { return ; }
311+
312+ if (installedNum < requiredNum)
313+ {
314+ alert (
315+ " **SCRIPT UPDATE BLOCKED**\n\n " +
316+ " MerlinAU cannot update because your installed firmware is " +
317+ " below the minimum required firmware for this script update.\n\n " +
318+ " Installed firmware: " + installedStr + " \n " +
319+ " Minimum required: " + requiredStr + " \n\n " +
320+ " Please update your router firmware, then try again."
321+ );
322+ }
323+ },
324+
325+ error : function ()
326+ {
327+ if (scriptUpdateGateTries < scriptUpdateGateMaxTries)
328+ { setTimeout (RunScriptUpdateFirmwareGateCheck, 1000 ); }
329+ else
330+ { pendingScriptUpdateGateCheck = false ; isFormSubmitting = false ; }
331+ }
332+ });
333+ }
334+
251335/* *-------------------------------------**/
252336/* * Added by Martinski W. [2025-Jan-24] **/
253337/* *-------------------------------------**/
@@ -910,6 +994,10 @@ function ShowLatestChangelog(e)
910994 box .scrollTop -= 40 ;
911995 ev .preventDefault ();
912996 break ;
997+ case ' Escape' :
998+ $ (' #changelogModal' ).hide ();
999+ ev .preventDefault ();
1000+ break ;
9131001 default :
9141002 break ;
9151003 }
@@ -997,7 +1085,7 @@ function ValidateDirectoryPath (formField, dirType)
9971085function GetExternalCheckResults ()
9981086{
9991087 $ .ajax ({
1000- url: ' /ext/MerlinAU/checkHelper.js' ,
1088+ url: ' /ext/MerlinAU/checkHelper.js?_= ' + new Date (). getTime () ,
10011089 dataType: ' script' ,
10021090 timeout: 5000 ,
10031091 error : function (xhr ){
@@ -1537,7 +1625,10 @@ function GetLoginPswdCheckStatus()
15371625
15381626 document .getElementById (' LoginPswdStatusText' ).textContent = pswdStatusText;
15391627 showhide (' LoginPswdStatusText' ,true );
1540- loginPswdHint = loginPswdHint .replace (/ PswdSTATUS/ , pswdStatusHint1);
1628+ // Rebuild the base hint fresh EACH time (so PswdSTATUS always exists) //
1629+ let loginUserStr = document .getElementById (' http_username' )? .value .trim () || ' admin' ;
1630+ let baseHint = loginPswdStatHintMsg .replace (/ LoginUSER/ , loginUserStr);
1631+ loginPswdHint = baseHint .replace (/ PswdSTATUS/ , pswdStatusHint1);
15411632
15421633 pswdField = document .getElementById (' routerPassword' );
15431634 if (passwordFailed || pswdVerified || pswdUnverified)
@@ -1688,7 +1779,7 @@ function InitializeFields()
16881779 if (script_AutoUpdate_Check)
16891780 {
16901781 script_AutoUpdate_Check .checked = (custom_settings .Allow_Script_Auto_Update === ' ENABLED' );
1691- UpdateForceScriptCheckboxState (script_AutoUpdate_Check? .checked );
1782+ UpdateForceScriptCheckboxState (script_AutoUpdate_Check && script_AutoUpdate_Check .checked );
16921783 }
16931784
16941785 if (betaToReleaseUpdatesEnabled)
@@ -2132,7 +2223,14 @@ function initial()
21322223 hiddenFrame .onload = function ()
21332224 {
21342225 console .log (" Hidden frame loaded with server response." );
2226+
2227+ if (pendingScriptUpdateGateCheck)
2228+ {
2229+ // Wait a moment to allow the backend logic to finish writing checkHelper.js
2230+ setTimeout (RunScriptUpdateFirmwareGateCheck, 1000 );
2231+ }
21352232 };
2233+
21362234 initializeCollapsibleSections ();
21372235 }
21382236}
@@ -2142,6 +2240,9 @@ function initial()
21422240/** ----------------------------------------**/
21432241function SaveCombinedConfig ()
21442242{
2243+ // Reset containers per-save so stale values never leak forward //
2244+ advanced_settings = {};
2245+
21452246 // Clear the hidden field before saving //
21462247 document .getElementById (' amng_custom' ).value = ' ' ;
21472248
@@ -2386,6 +2487,10 @@ function UpdateMerlinAUScript()
23862487 ? ' start_MerlinAUscrptupdate_force'
23872488 : ' start_MerlinAUscrptupdate' ;
23882489
2490+ pendingScriptUpdateGateCheck = true ;
2491+ scriptUpdateGateTries = 0 ;
2492+ isFormSubmitting = true ;
2493+
23892494 document .form .action_script .value = actionScriptValue;
23902495 document .form .action_wait .value = 10 ;
23912496 showLoading ();
@@ -2612,7 +2717,6 @@ function initializeCollapsibleSections()
26122717< input type= " hidden" id= " nvram_model" value= " <% nvram_get(" model" ); %>" / >
26132718< input type= " hidden" id= " nvram_build_name" value= " <% nvram_get(" build_name" ); %>" / >
26142719< input type= " hidden" id= " nvram_productid" value= " <% nvram_get(" productid" ); %>" / >
2615- < input type= " hidden" name= " installedfirm" value= " <% nvram_get(" innerver" ); %>" / >
26162720< input type= " hidden" name= " amng_custom" id= " amng_custom" value= " " / >
26172721
26182722< table class = " content" cellpadding= " 0" cellspacing= " 0" style= " margin:0 auto;" >
@@ -2636,7 +2740,7 @@ function initializeCollapsibleSections()
26362740< div class = " formfonttitle" id= " headerTitle" style= " text-align:center;" > MerlinAU< / div>
26372741< div style= " margin:10px 0 10px 5px;" class = " splitLine" >< / div>
26382742< div class = " formfontdesc" > This is the MerlinAU add- on integrated into the router WebUI
2639- < span style= " margin-left:8px;" id= " WikiURL" " >[
2743+ < span style= " margin-left:8px;" id= " WikiURL" > [
26402744 < a style= " font-weight:bolder; text-decoration:underline; cursor:pointer;"
26412745 href= " https://github.com/ExtremeFiretop/MerlinAutoUpdate-Router/wiki"
26422746 title= " Go to MerlinAU Wiki page" target= " _blank" > Wiki< / a> ]
@@ -2761,7 +2865,7 @@ function initializeCollapsibleSections()
27612865 < input type= " submit" id= " FWUpdateCheckButton" onclick= " CheckFirmwareUpdate();
27622866 return false;" value= " F/W Update Check" class = " button_gen savebutton" name= " button" >
27632867 < br>
2764- < label style= " color:#FFCC00; margin-top: 5px; margin-bottom:8x " >
2868+ < label style= " color:#FFCC00; margin-top: 5px; margin-bottom:8px " >
27652869 < input type= " checkbox" checked= " " id= " BypassPostponedDays" name= " BypassPostponedDays"
27662870 style= " padding:0; vertical-align:middle; position:relative; margin-left:-5px; margin-top:5px; margin-bottom:8px" / > Bypass postponed days< / label>
27672871 < / br>
@@ -2770,7 +2874,7 @@ function initializeCollapsibleSections()
27702874 < input type= " submit" id= " LatestChangelogButton" onclick= " ShowLatestChangelog();
27712875 return false;" value= " Latest Changelog" class = " button_gen savebutton" title= " View the latest changelog" name= " button" >
27722876 < br>
2773- < label style= " color:#FFCC00; margin-top: 5px; margin-bottom:8x " >
2877+ < label style= " color:#FFCC00; margin-top: 5px; margin-bottom:8px " >
27742878 < input type= " checkbox" id= " approveChangelogCheck" name= " approveChangelogCheck" onclick= " ToggleChangelogApproval(this);"
27752879 style= " padding:0; vertical-align:middle; position:relative; margin-left:-5px; margin-top:5px; margin-bottom:8px" / > Approve changelog< / label>
27762880 < / br>
@@ -2794,7 +2898,7 @@ function initializeCollapsibleSections()
27942898 < input type= " submit" id= " UninstallButton" onclick= " Uninstall(); return false;"
27952899 value= " Uninstall" class = " button_gen savebutton" name= " button" >
27962900 < br>
2797- < label style= " color:#FFCC00; margin-top: 5px; margin-bottom:8x " >
2901+ < label style= " color:#FFCC00; margin-top: 5px; margin-bottom:8px " >
27982902 < input type= " checkbox" checked= " " id= " KeepConfigFile" name= " KeepConfigFile"
27992903 style= " padding:0; vertical-align:middle; position:relative; margin-left:-3px; margin-top:5px; margin-bottom:8px" / > Keep configuration file< / label>
28002904 < / br>
@@ -2812,7 +2916,7 @@ function initializeCollapsibleSections()
28122916< tbody>
28132917 < tr>
28142918 < td colspan= " 2" >
2815- < form id= " advancedOptionsForm" >
2919+ < div id= " advancedOptionsForm" >
28162920 < table class = " FormTable SettingsTable" width= " 100%" border= " 0" cellpadding= " 5" cellspacing= " 5" style= " table-layout: fixed;" >
28172921< colgroup>
28182922 < col style= " width: 37%;" / >
@@ -2838,7 +2942,7 @@ function initializeCollapsibleSections()
28382942 onblur= " ValidatePasswordString(this,'onBLUR')"
28392943 onkeyup= " ValidatePasswordString(this,'onKEYUP')" / >
28402944 < div id= " eyeToggle" onclick= " togglePassword();"
2841- 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;" >
2945+ 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;" >
28422946 < / div>
28432947 < / div>
28442948 < br>
@@ -3086,7 +3190,7 @@ function initializeCollapsibleSections()
30863190 < input type= " submit" onclick= " SaveCombinedConfig(); return false;"
30873191 value= " Save Configuration" class = " button_gen savebutton" name= " button" >
30883192< / div>
3089- < / form >< / td>< / tr>< / tbody>< / table>
3193+ < / div >< / td>< / tr>< / tbody>< / table>
30903194< div id= " footerTitle" style= " margin-top:10px;text-align:center;" > MerlinAU< / div>
30913195< / td>< / tr>< / tbody>< / table>< / td>< / tr>< / table>< / td>
30923196< td width= " 10" >< / td>
0 commit comments