@@ -1195,136 +1195,169 @@ function getCalculatorHTML() {
11951195 </style>
11961196 ` ;
11971197}
1198- let calculatorKeyboardAdded = false ;
1198+
11991199function initCalculator ( ) {
1200- const display = document . getElementById ( 'calcDisplay' ) ;
1201- let currentValue = '0' ;
1202- let previousValue = '' ;
1203- let operation = '' ;
1204-
1205- document . querySelectorAll ( '.calc-btn' ) . forEach ( btn => {
1206- btn . addEventListener ( 'click' , ( ) => {
1207- const value = btn . getAttribute ( 'data-value' ) ;
1208- const action = btn . getAttribute ( 'data-action' ) ;
1200+ const display = document . getElementById ( "calcDisplay" ) ;
1201+ if ( ! display ) return ;
1202+ let expression = "" ;
1203+
1204+ function update ( ) {
1205+ display . textContent = expression || "0" ;
1206+ }
1207+
1208+ function format ( expr ) {
1209+ return expr
1210+ . replace ( / ÷ / g, "/" )
1211+ . replace ( / × / g, "*" )
1212+ . replace ( / \^ / g, "**" ) ;
1213+ }
1214+
1215+ function safeEval ( expr ) {
1216+ try {
1217+ if ( ! expr ) return "" ;
1218+ let result = eval ( format ( expr ) ) ;
1219+ if ( result === undefined ) return "" ;
1220+ if ( isNaN ( result ) ) return "Error" ;
1221+ return String ( result ) ;
1222+ } catch {
1223+ return "Error" ;
1224+ }
1225+ }
1226+
1227+ function applyFunction ( type ) {
1228+ try {
1229+ let value = eval ( format ( expression || "0" ) ) ;
1230+ let result ;
1231+ switch ( type ) {
1232+ case "sin" : result = Math . sin ( value ) ; break ;
1233+ case "cos" : result = Math . cos ( value ) ; break ;
1234+ case "tan" : result = Math . tan ( value ) ; break ;
1235+ case "sqrt" : result = Math . sqrt ( value ) ; break ;
1236+ case "square" : result = value * value ; break ;
1237+ case "inv" : result = 1 / value ; break ;
1238+ }
1239+ if ( isNaN ( result ) ) return "Error" ;
1240+ return String ( result ) ;
1241+ } catch {
1242+ return "Error" ;
1243+ }
1244+ }
1245+
1246+
1247+ function clearIfFinished ( ) {
1248+ if ( expression === "Error" || expression === "NaN" ) {
1249+ expression = "" ;
1250+ }
1251+ }
1252+
1253+ document . querySelectorAll ( ".calc-btn" ) . forEach ( ( btn ) => {
1254+ btn . addEventListener ( "click" , ( ) => {
1255+ clearIfFinished ( ) ;
12091256
1210- if ( value ) {
1211- handleNumber ( value ) ;
1212- } else if ( action ) {
1213- handleAction ( action ) ;
1257+ const value = btn . dataset . value ;
1258+ const action = btn . dataset . action ;
1259+
1260+ if ( value !== undefined ) {
1261+ if ( value === "." ) {
1262+
1263+ const lastOperand = expression . split ( / [ \+ \- \* \/ \^ \( \) ] / ) . pop ( ) ;
1264+ if ( lastOperand . includes ( "." ) ) return ;
1265+ }
1266+ expression += value ;
1267+ update ( ) ;
1268+ return ;
12141269 }
1215-
1216- updateDisplay ( ) ;
1270+
1271+ if ( ! action ) return ;
1272+
1273+
1274+ switch ( action ) {
1275+ case "clear" :
1276+ expression = "" ;
1277+ break ;
1278+ case "delete" :
1279+ if ( expression === "Infinity" || expression === "-Infinity" ) {
1280+ expression = "" ;
1281+ } else {
1282+ expression = expression . slice ( 0 , - 1 ) ;
1283+ }
1284+ break ;
1285+ case "=" :
1286+ expression = safeEval ( expression ) ;
1287+ break ;
1288+ case "sin" :
1289+ case "cos" :
1290+ case "tan" :
1291+ case "sqrt" :
1292+ case "square" :
1293+ case "inv" :
1294+ expression = applyFunction ( action ) ;
1295+ break ;
1296+ case "^" :
1297+ case "+" :
1298+ case "-" :
1299+ case "*" :
1300+ case "/" :
1301+
1302+ const lastChar = expression . slice ( - 1 ) ;
1303+ if ( [ "+" , "-" , "*" , "/" , "^" ] . includes ( lastChar ) ) {
1304+ expression = expression . slice ( 0 , - 1 ) + action ;
1305+ } else {
1306+ expression += action ;
1307+ }
1308+ break ;
1309+ default :
1310+ expression += action ;
1311+ }
1312+ update ( ) ;
12171313 } ) ;
12181314 } ) ;
1219- if ( ! calculatorKeyboardAdded ) {
1220- document . addEventListener ( "keydown" , ( e ) => {
1221- const display = document . getElementById ( "calcDisplay" ) ;
1222- if ( ! display ) return ;
12231315
1316+ document . addEventListener ( "keydown" , ( e ) => {
12241317 const key = e . key ;
1318+ if ( ! document . getElementById ( "calcDisplay" ) ) return ;
12251319
1226- if (
1227- key === "Enter" ||
1228- key === " " ||
1229- key === "Backspace" ||
1230- key === "Escape" ||
1231- key === "=" ||
1232- key === "+" ||
1233- key === "-" ||
1234- key === "*" ||
1235- key === "/" ||
1236- key === "^" ||
1237- key === "." ||
1238- / ^ [ 0 - 9 ] $ / . test ( key ) ||
1239- key . startsWith ( "Arrow" )
1240- ) {
1320+ // Whitelist allowed keys to prevent typing letters
1321+ const allowedKeys = [ "Enter" , "Backspace" , "Escape" , "=" , "+" , "-" , "*" , "/" , "^" , "." , "(" , ")" ] ;
1322+ if ( allowedKeys . includes ( key ) || / ^ [ 0 - 9 ] $ / . test ( key ) ) {
12411323 e . preventDefault ( ) ;
1324+ } else {
1325+ return ;
12421326 }
12431327
1328+ clearIfFinished ( ) ;
1329+
12441330 if ( / ^ [ 0 - 9 ] $ / . test ( key ) ) {
1245- handleNumber ( key ) ;
1246- }
1247- else if ( key === "." ) {
1248- handleNumber ( "." ) ;
1249- }
1250- else if ( [ "+" , "-" , "*" , "/" ] . includes ( key ) ) {
1251- handleAction ( key ) ;
1252- }
1253- else if ( key === "^" ) {
1254- handleAction ( "**" ) ;
1255- }
1256- else if ( key === "Enter" || key === "=" ) {
1257- handleAction ( "=" ) ;
1258- }
1259- else if ( key === "Backspace" ) {
1260- handleAction ( "delete" ) ;
1261- }
1262- else if ( key === "Escape" || key . toLowerCase ( ) === "c" ) {
1263- handleAction ( "clear" ) ;
1331+ expression += key ;
1332+ } else if ( key === "." ) {
1333+ const lastOperand = expression . split ( / [ \+ \- \* \/ \^ \( \) ] / ) . pop ( ) ;
1334+ if ( ! lastOperand . includes ( "." ) ) {
1335+ expression += "." ;
1336+ }
1337+ } else if ( [ "+" , "-" , "*" , "/" , "^" ] . includes ( key ) ) {
1338+ const lastChar = expression . slice ( - 1 ) ;
1339+ if ( [ "+" , "-" , "*" , "/" , "^" ] . includes ( lastChar ) ) {
1340+ expression = expression . slice ( 0 , - 1 ) + key ;
1341+ } else {
1342+ expression += key ;
1343+ }
1344+ } else if ( key === ")" || key === "(" ) {
1345+ expression += key ;
1346+ } else if ( key === "Enter" || key === "=" ) {
1347+ expression = safeEval ( expression ) ;
1348+ } else if ( key === "Backspace" ) {
1349+ if ( expression === "Infinity" || expression === "-Infinity" ) {
1350+ expression = "" ;
1351+ } else {
1352+ expression = expression . slice ( 0 , - 1 ) ;
1353+ }
1354+ } else if ( key === "Escape" || key . toLowerCase ( ) === "c" ) {
1355+ expression = "" ;
12641356 }
1265-
1266- updateDisplay ( ) ;
1357+ update ( ) ;
12671358 } ) ;
12681359
1269- calculatorKeyboardAdded = true ;
1270- }
1271-
1272- function handleNumber ( num ) {
1273- if ( currentValue === '0' || currentValue === 'Error' ) {
1274- currentValue = num ;
1275- } else {
1276- currentValue += num ;
1277- }
1278- }
1279-
1280- function handleAction ( action ) {
1281- if ( action === 'clear' ) {
1282- currentValue = '0' ;
1283- previousValue = '' ;
1284- operation = '' ;
1285- } else if ( action === 'delete' ) {
1286- currentValue = currentValue . slice ( 0 , - 1 ) || '0' ;
1287- } else if ( action === '=' ) {
1288- calculate ( ) ;
1289- } else {
1290- if ( previousValue && operation ) {
1291- calculate ( ) ;
1292- }
1293- previousValue = currentValue ;
1294- currentValue = '0' ;
1295- operation = action ;
1296- }
1297- }
1298-
1299- function calculate ( ) {
1300- try {
1301- const prev = parseFloat ( previousValue ) ;
1302- const curr = parseFloat ( currentValue ) ;
1303- let result ;
1304-
1305- switch ( operation ) {
1306- case '+' : result = prev + curr ; break ;
1307- case '-' : result = prev - curr ; break ;
1308- case '*' : result = prev * curr ; break ;
1309- case '/' : result = prev / curr ; break ;
1310- case '**' : result = Math . pow ( prev , curr ) ; break ;
1311- default : return ;
1312- }
1313-
1314- currentValue = result . toString ( ) ;
1315- previousValue = '' ;
1316- operation = '' ;
1317- } catch ( e ) {
1318- currentValue = 'Error' ;
1319- }
1320- }
1321-
1322- function updateDisplay ( ) {
1323- const display = document . getElementById ( 'calcDisplay' ) ;
1324- if ( display ) {
1325- display . textContent = currentValue ;
1326- }
1327- }
1360+ update ( ) ;
13281361}
13291362
13301363// ============================================
0 commit comments