88
99// fix css paading (kusa
1010const fleth_style = document . createElement ( "style" ) ;
11- fleth_style . innerHTML = `.cbi-value-field { padding-top: 6px; }` ;
11+ fleth_style . innerHTML = `
12+ .cbi-value-field { padding-top: 6px; }
13+ .port-highlight {
14+ background-color: rgb(207, 226, 255);
15+ padding: 1px 4px;
16+ border-radius: 3px;
17+ font-weight: 500;
18+ }
19+ ` ;
1220document . head . appendChild ( fleth_style ) ;
1321
1422return view . extend ( {
@@ -26,52 +34,29 @@ return view.extend({
2634 let areaValue = area || "UNKNOWN" ;
2735 const mapeIsUnknown = mape_status . length <= 1 || mape_status [ 0 ] === "UNKNOWN" ;
2836
29- // Special handling for NURO
30- if ( mape_status [ 0 ] === "NURO" ) {
31- areaValue = "UNKNOWN(NURO)" ;
32- }
33- // If MAP-E is UNKNOWN, check pending status first
37+ // Base return object with common fields
38+ const baseData = {
39+ mape_status : mape_status ,
40+ prefix_length : prefix_length || "UNKNOWN" ,
41+ mapIsPatched : mapsh_status === "patched" ,
42+ } ;
43+
44+ if ( mape_status [ 0 ] === "NURO" ) areaValue = "UNKNOWN(NURO)" ;
45+
3446 if ( mapeIsUnknown ) {
3547 return L . resolveDefault ( fs . exec ( "/usr/sbin/fleth" , [ "pending_status" ] ) , { stdout : "" } )
3648 . then ( function ( pendingResult ) {
3749 const pendingStatus = ( pendingResult . stdout || "" ) . trim ( ) ;
38- // If pending status detected, return with pending flag
3950 if ( pendingStatus . endsWith ( "_pending" ) ) {
40- const detectedArea = pendingStatus . split ( '_' ) [ 0 ] ;
41- return {
42- area : detectedArea ,
43- dslite_provider : "UNKNOWN" ,
44- mape_status : mape_status ,
45- prefix_length : prefix_length || "UNKNOWN" ,
46- isPending : true ,
47- mapIsPatched : mapsh_status === "patched" ,
48- } ;
51+ return { ...baseData , area : pendingStatus . split ( '_' ) [ 0 ] , dslite_provider : "UNKNOWN" , isPending : true } ;
4952 }
50- // No pending status, check DS-Lite provider
5153 return L . resolveDefault ( fs . exec ( "/usr/sbin/fleth" , [ "get_dslite_provider" ] ) , { stdout : "" } )
5254 . then ( function ( dsliteResult ) {
53- const dslite_provider = ( dsliteResult . stdout || "" ) . trim ( ) ;
54- return {
55- area : areaValue ,
56- dslite_provider : dslite_provider || "UNKNOWN" ,
57- mape_status : mape_status ,
58- prefix_length : prefix_length || "UNKNOWN" ,
59- isPending : false ,
60- mapIsPatched : mapsh_status === "patched" ,
61- } ;
55+ return { ...baseData , area : areaValue , dslite_provider : ( dsliteResult . stdout || "" ) . trim ( ) || "UNKNOWN" , isPending : false } ;
6256 } ) ;
6357 } ) ;
64- } else {
65- // MAP-E detected, no need to check DS-Lite or pending
66- return {
67- area : areaValue ,
68- dslite_provider : "UNKNOWN" ,
69- mape_status : mape_status ,
70- prefix_length : prefix_length || "UNKNOWN" ,
71- isPending : false ,
72- mapIsPatched : mapsh_status === "patched" ,
73- } ;
7458 }
59+ return { ...baseData , area : areaValue , dslite_provider : "UNKNOWN" , isPending : false } ;
7560 } ) ;
7661 } ,
7762
@@ -151,9 +136,49 @@ return view.extend({
151136 mapeDetailFields . forEach ( ( field , i ) => {
152137 const [ fieldName , fieldLabel ] = field ;
153138 o = s . taboption ( "info" , form . DummyValue , fieldName , _ ( fieldLabel ) ) ;
154- o . cfgvalue = function ( ) {
155- return data . mape_status [ i + 1 ] || "" ;
156- } ;
139+
140+ // Special rendering for Available ports
141+ if ( fieldName === "mape_map_ports" ) {
142+ o . rawhtml = true ;
143+ o . cfgvalue = function ( ) {
144+ const portsString = data . mape_status [ i + 1 ] || "" ;
145+ if ( ! portsString ) return "" ;
146+
147+ // Split ports into individual numbers
148+ const ports = portsString . split ( / \s + / ) . filter ( p => p ) ;
149+ const highlightedPorts = ports . map ( port => {
150+ const len = port . length ;
151+ const half = len / 2 ;
152+ const reversed = port . split ( '' ) . reverse ( ) . join ( '' ) ;
153+ const counts = { } ;
154+ for ( let c of port ) counts [ c ] = ( counts [ c ] || 0 ) + 1 ;
155+
156+ const isSpecial = / ( \d ) \1{ 2 , } / . test ( port ) || // consecutive repeats
157+ port . endsWith ( '0' ) || // ends with 0
158+ ( len >= 2 && len % 2 === 0 && port . substring ( 0 , half ) === port . substring ( half ) ) || // ABAB
159+ ( len >= 3 && port === reversed ) || // palindrome
160+ Object . values ( counts ) . some ( n => n >= 3 ) ; // frequent digit
161+
162+ return isSpecial ? '<span class="port-highlight">' + port + '</span>' : port ;
163+ } ) ;
164+
165+ return highlightedPorts . join ( ' ' ) ;
166+ } ;
167+ // Override render to display as div instead of input
168+ o . render = function ( ) {
169+ const value = this . cfgvalue ( ) ;
170+ const contentDiv = E ( 'div' , { 'style' : 'line-height: 1.8; word-wrap: break-word;' } ) ;
171+ contentDiv . innerHTML = value ;
172+ return E ( 'div' , { 'class' : 'cbi-value' } , [
173+ E ( 'label' , { 'class' : 'cbi-value-title' } , _ ( fieldLabel ) ) ,
174+ E ( 'div' , { 'class' : 'cbi-value-field' } , contentDiv )
175+ ] ) ;
176+ } . bind ( o ) ;
177+ } else {
178+ o . cfgvalue = function ( ) {
179+ return data . mape_status [ i + 1 ] || "" ;
180+ } ;
181+ }
157182 } ) ;
158183 }
159184
@@ -252,7 +277,7 @@ return view.extend({
252277 o . title = _ ( "map.sh Management" ) ;
253278 o . cfgvalue = function ( ) {
254279 return _ ( "OpenWrt's map.sh has bugs: only the first port group works and ICMP is broken. Click below to replace with the fixed version." ) +
255- ' <a href="https://github.com/fakemanhk/openwrt-jp-ipoe/tree/main" target="_blank" style="color: #0088cc;">(' + _ ( "See more" ) + ')</a>' ;
280+ ' <a href="https://github.com/fakemanhk/openwrt-jp-ipoe/tree/main" target="_blank" style="color: #0088cc;">(' + _ ( "See more" ) + ')</a>' ;
256281 } ;
257282 o . rawhtml = true ;
258283
@@ -274,12 +299,12 @@ return view.extend({
274299 } ;
275300 o . rawhtml = true ;
276301
277- o = s . taboption ( "tools" , form . Button , "_replace_mapsh " ) ;
302+ o = s . taboption ( "tools" , form . Button , "_patch_mapsh " ) ;
278303 o . title = " " ;
279- o . inputtitle = _ ( "Replace " ) ;
304+ o . inputtitle = _ ( "Patch " ) ;
280305 o . inputstyle = data . mapIsPatched ? "cbi-button-action" : "cbi-button-apply" ;
281306 o . onclick = L . bind ( function ( m ) {
282- return this . replaceMapSh ( m ) ;
307+ return this . patchMapSh ( m ) ;
283308 } , this , m ) ;
284309
285310 o = s . taboption ( "tools" , form . Button , "_restore_mapsh" ) ;
@@ -289,15 +314,15 @@ return view.extend({
289314 o . onclick = L . bind ( function ( m ) {
290315 return this . restoreMapSh ( m ) ;
291316 } , this , m ) ;
292- o . depends ( "_replace_mapsh " , "" ) ;
317+ o . depends ( "_patch_mapsh " , "" ) ;
293318
294319 const renderedNode = await m . render ( ) ;
295320
296321 // Hide footer when tools tab is active
297- setTimeout ( function ( ) {
322+ setTimeout ( function ( ) {
298323 const footer = document . querySelector ( '.cbi-page-actions' ) ;
299324
300- const toggleFooter = function ( ) {
325+ const toggleFooter = function ( ) {
301326 // Check if tools tab is active
302327 const toolsActive = document . querySelector ( '.cbi-tab[data-tab="tools"]' ) ;
303328 if ( footer ) {
@@ -312,8 +337,8 @@ return view.extend({
312337 const tabMenu = document . querySelector ( '.cbi-tabmenu' ) ;
313338 if ( tabMenu ) {
314339 const tabItems = tabMenu . querySelectorAll ( 'li[data-tab]' ) ;
315- tabItems . forEach ( function ( tabItem ) {
316- tabItem . addEventListener ( 'click' , function ( ) {
340+ tabItems . forEach ( function ( tabItem ) {
341+ tabItem . addEventListener ( 'click' , function ( ) {
317342 setTimeout ( toggleFooter , 10 ) ;
318343 } ) ;
319344 } ) ;
@@ -374,7 +399,7 @@ return view.extend({
374399
375400 manageMapSh : function ( mapObj , action ) {
376401 const actionConfig = {
377- replace : { verb : _ ( 'Replacing ' ) , gerund : _ ( 'Downloading...' ) } ,
402+ patch : { verb : _ ( 'Patching ' ) , gerund : _ ( 'Downloading...' ) } ,
378403 restore : { verb : _ ( 'Restoring' ) , gerund : _ ( 'Restoring...' ) }
379404 } ;
380405
@@ -388,15 +413,15 @@ return view.extend({
388413 E ( 'p' , { 'class' : 'spinning' } , config . gerund )
389414 ] ) ;
390415
391- return fs . exec ( '/usr/sbin/fleth' , [ action + '_mapsh ' ] ) ;
416+ return fs . exec ( '/usr/sbin/fleth' , [ action + '_map.sh ' ] ) ;
392417 } )
393418 . then ( function ( result ) {
394419 ui . hideModal ( ) ;
395420
396421 if ( result . code === 0 && result . stdout . trim ( ) === 'SUCCESS' ) {
397422 ui . addNotification ( null , E ( 'p' , _ ( 'Operation completed successfully! Please restart the network interface manually.' ) ) , 'info' ) ;
398423
399- setTimeout ( function ( ) {
424+ setTimeout ( function ( ) {
400425 window . location . reload ( ) ;
401426 } , 5000 ) ;
402427 } else {
@@ -419,8 +444,8 @@ return view.extend({
419444 } ) ;
420445 } ,
421446
422- replaceMapSh : function ( mapObj ) {
423- return this . manageMapSh ( mapObj , 'replace ' ) ;
447+ patchMapSh : function ( mapObj ) {
448+ return this . manageMapSh ( mapObj , 'patch ' ) ;
424449 } ,
425450
426451 restoreMapSh : function ( mapObj ) {
0 commit comments