mirror of
https://github.com/BorysLevytskyi/BitwiseCmd.git
synced 2025-12-09 22:42:07 +01:00
upgrade dependencies and introduce ci workflow (#59)
This commit is contained in:
68
.github/workflows/pr-ci.yml
vendored
Normal file
68
.github/workflows/pr-ci.yml
vendored
Normal file
@@ -0,0 +1,68 @@
|
||||
name: PR CI
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
types: [opened, synchronize, reopened]
|
||||
|
||||
jobs:
|
||||
e2e-tests:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 5
|
||||
env:
|
||||
BITWISECMD_TESTS_TOKEN: ${{ secrets.BITWISECMD_TESTS_TOKEN }}
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 18
|
||||
|
||||
- name: Install dependencies
|
||||
run: npm install --no-audit --no-fund
|
||||
|
||||
- name: Build application
|
||||
run: npx react-scripts build
|
||||
env:
|
||||
CI: true
|
||||
|
||||
- name: Start static web server
|
||||
run: |
|
||||
npx --yes http-server ./build -p 3030 --silent </dev/null > server.log 2>&1 &
|
||||
echo $! > server.pid
|
||||
npx --yes wait-on --timeout=120000 http-get://127.0.0.1:3030
|
||||
|
||||
- name: Ensure BitwiseCmdTests access token is configured
|
||||
if: ${{ env.BITWISECMD_TESTS_TOKEN == '' }}
|
||||
run: |
|
||||
echo "::error::Add a personal access token with repo scope as the BITWISECMD_TESTS_TOKEN repository secret so the CI job can clone BitwiseCmdTests."
|
||||
exit 1
|
||||
|
||||
- name: Checkout BitwiseCmdTests repository
|
||||
if: ${{ env.BITWISECMD_TESTS_TOKEN != '' }}
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
repository: ${{ github.repository_owner }}/BitwiseCmdTests
|
||||
path: BitwiseCmdTests
|
||||
token: ${{ env.BITWISECMD_TESTS_TOKEN }}
|
||||
|
||||
- name: Install BitwiseCmdTests dependencies
|
||||
working-directory: BitwiseCmdTests
|
||||
run: npm install --no-audit --no-fund
|
||||
|
||||
- name: Run webdriver tests
|
||||
working-directory: BitwiseCmdTests
|
||||
env:
|
||||
BASE_URL: http://localhost:3030
|
||||
CHROME_ARGS: --headless=new --disable-gpu --no-sandbox --disable-dev-shm-usage --window-size=1280,720
|
||||
run: |
|
||||
xvfb-run --auto-servernum -- npm run test-build
|
||||
|
||||
- name: Stop static web server
|
||||
if: always()
|
||||
run: |
|
||||
if [ -f server.pid ]; then
|
||||
kill $(cat server.pid) || true
|
||||
fi
|
||||
9
docs/ci.md
Normal file
9
docs/ci.md
Normal file
@@ -0,0 +1,9 @@
|
||||
# Continuous Integration Setup
|
||||
|
||||
The pull request workflow checks out a private test suite from `BitwiseCmdTests`. To allow the workflow to clone that repository, add a repository secret named `BITWISECMD_TESTS_TOKEN` that contains a GitHub personal access token with at least `repo` scope.
|
||||
|
||||
1. Generate a classic personal access token from your GitHub account with `repo` scope.
|
||||
2. In the BitwiseCmd repository settings, open **Secrets and variables → Actions**.
|
||||
3. Create a new repository secret named `BITWISECMD_TESTS_TOKEN` and paste the token value.
|
||||
|
||||
Once the secret is configured, rerun the pull request workflow so it can authenticate and complete the checkout step.
|
||||
30037
package-lock.json
generated
30037
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
36
package.json
36
package.json
@@ -3,26 +3,26 @@
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@fortawesome/fontawesome-free": "^6.4.0",
|
||||
"@fortawesome/fontawesome-svg-core": "^6.4.0",
|
||||
"@fortawesome/free-brands-svg-icons": "^6.4.0",
|
||||
"@fortawesome/free-solid-svg-icons": "^6.4.0",
|
||||
"@fortawesome/fontawesome-free": "^6.7.2",
|
||||
"@fortawesome/fontawesome-svg-core": "^6.7.2",
|
||||
"@fortawesome/free-brands-svg-icons": "^6.7.2",
|
||||
"@fortawesome/free-solid-svg-icons": "^6.7.2",
|
||||
"@fortawesome/react-fontawesome": "^0.2.0",
|
||||
"@testing-library/jest-dom": "^5.16.5",
|
||||
"@testing-library/react": "^13.4.0",
|
||||
"@testing-library/user-event": "^13.5.0",
|
||||
"@types/jest": "^27.5.2",
|
||||
"@types/node": "^16.18.25",
|
||||
"@types/react": "^18.2.4",
|
||||
"@types/react-dom": "^18.2.3",
|
||||
"@types/uuid": "^9.0.1",
|
||||
"loglevel": "^1.8.1",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"@testing-library/jest-dom": "^6.4.5",
|
||||
"@testing-library/react": "^16.1.0",
|
||||
"@testing-library/user-event": "^14.5.2",
|
||||
"@types/jest": "^29.5.12",
|
||||
"@types/node": "^22.10.1",
|
||||
"@types/react": "^18.3.12",
|
||||
"@types/react-dom": "^18.3.7",
|
||||
"@types/uuid": "^9.0.7",
|
||||
"loglevel": "^1.9.1",
|
||||
"react": "^18.3.1",
|
||||
"react-dom": "^18.3.1",
|
||||
"react-scripts": "5.0.1",
|
||||
"typescript": "^4.9.5",
|
||||
"uuid": "^9.0.0",
|
||||
"web-vitals": "^2.1.4"
|
||||
"uuid": "^9.0.1",
|
||||
"web-vitals": "^4.2.4"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "react-scripts start",
|
||||
@@ -52,7 +52,7 @@
|
||||
]
|
||||
},
|
||||
"devDependencies": {
|
||||
"gh-pages": "^5.0.0",
|
||||
"gh-pages": "^6.1.1",
|
||||
"http-server": "^14.1.1"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { type } from "os";
|
||||
import { INT32_MAX_VALUE, INT32_MIN_VALUE, UINT32_MAX_VALUE } from "./const";
|
||||
import { asIntN, logLines as ln } from "./utils";
|
||||
import { asIntN } from "./utils";
|
||||
import formatter from "./formatter";
|
||||
import calc from "./calc";
|
||||
|
||||
@@ -15,9 +14,10 @@ export class Integer {
|
||||
|
||||
constructor(value: IntegerInput, maxBitSize?: number, signed? : boolean) {
|
||||
|
||||
this.value = typeof value == "bigint" ? value : BigInt(value);
|
||||
this.signed = signed == null ? true : signed == true;
|
||||
this.maxBitSize = maxBitSize != null ? maxBitSize : detectSize(this.value, this.signed);
|
||||
this.value = typeof value === "bigint" ? value : BigInt(value);
|
||||
const resolvedSigned = signed ?? true;
|
||||
this.signed = resolvedSigned;
|
||||
this.maxBitSize = maxBitSize ?? detectSize(this.value, this.signed);
|
||||
|
||||
if(!this.signed && this.value < 0)
|
||||
throw new Error("Value " + value + " cannot be negative if the type is unsigned");
|
||||
@@ -54,7 +54,7 @@ export class Integer {
|
||||
}
|
||||
|
||||
isTheSame (other : Integer) : boolean {
|
||||
return this.value == other.value && this.signed == other.signed && this.maxBitSize == other.maxBitSize;
|
||||
return this.value === other.value && this.signed === other.signed && this.maxBitSize === other.maxBitSize;
|
||||
}
|
||||
|
||||
toUnsigned() {
|
||||
@@ -71,9 +71,9 @@ export class Integer {
|
||||
|
||||
const orig = this.toString(2).padStart(this.maxBitSize, '0');
|
||||
|
||||
const inverted = orig[0] == '1' ? calc.engine.applyTwosComplement(orig) : orig;
|
||||
const inverted = orig[0] === '1' ? calc.engine.applyTwosComplement(orig) : orig;
|
||||
const n = BigInt("0b"+inverted);
|
||||
const negative = orig[0] == '1';
|
||||
const negative = orig[0] === '1';
|
||||
|
||||
return new Integer(negative ? -n : n, this.maxBitSize, true)
|
||||
}
|
||||
@@ -118,24 +118,24 @@ export class Integer {
|
||||
|
||||
export function asInteger(num: JsNumber | Integer | string): Integer {
|
||||
|
||||
if(typeof num == "string")
|
||||
if(typeof num === "string")
|
||||
return asInteger(BigInt(num));
|
||||
|
||||
if(isInteger(num))
|
||||
if(isInteger(num))
|
||||
return num;
|
||||
|
||||
if(typeof num == "number" && isNaN(num)) {
|
||||
if(typeof num === "number" && isNaN(num)) {
|
||||
throw new Error("Cannot create BoundedNumber from NaN");
|
||||
}
|
||||
|
||||
const size = num > INT32_MAX_VALUE || num < INT32_MIN_VALUE ? 64 : 32;
|
||||
|
||||
const n = typeof num == "bigint" ? num : BigInt(num);
|
||||
|
||||
const n = typeof num === "bigint" ? num : BigInt(num);
|
||||
return new Integer(n, size);
|
||||
}
|
||||
|
||||
export function isInteger(num: JsNumber | Integer): num is Integer {
|
||||
return (<Integer>num).maxBitSize !== undefined;
|
||||
return (num as Integer).maxBitSize !== undefined;
|
||||
}
|
||||
function detectSize(value: bigint, signed: boolean): number {
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
function flipBitsToZero(byte: number, numberOfBits : number) : number {
|
||||
if(numberOfBits == 0)
|
||||
if(numberOfBits === 0)
|
||||
return byte;
|
||||
|
||||
const zerouOutMask = Math.pow(2, 8-numberOfBits)-1<<numberOfBits; // E.g. 11111000 for flipping first three bits
|
||||
const result = byte & zerouOutMask;
|
||||
|
||||
const zerouOutMask = (Math.pow(2, 8 - numberOfBits) - 1) << numberOfBits; // E.g. 11111000 for flipping first three bits
|
||||
const result = byte & zerouOutMask;
|
||||
|
||||
return result;
|
||||
}
|
||||
@@ -11,16 +11,16 @@ function flipBitsToZero(byte: number, numberOfBits : number) : number {
|
||||
// TODO: continue here to implement getting broadcast address
|
||||
|
||||
function flipBitsToOne(byte : number, numberOfBits : number) : number {
|
||||
if(numberOfBits == 0) return byte;
|
||||
if(numberOfBits === 0) return byte;
|
||||
|
||||
const zerouOutMask = Math.pow(2, numberOfBits)-1; // E.g. 00000111 for flipping first three bits
|
||||
const result = byte | zerouOutMask;
|
||||
const zerouOutMask = Math.pow(2, numberOfBits) - 1; // E.g. 00000111 for flipping first three bits
|
||||
const result = byte | zerouOutMask;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
function createSubnetMaskByte(numberOfBits: number) {
|
||||
return 255<<(8-numberOfBits)&255;;
|
||||
return (255 << (8 - numberOfBits)) & 255;
|
||||
}
|
||||
|
||||
export {flipBitsToZero, createSubnetMaskByte, flipBitsToOne};
|
||||
@@ -1,12 +1,12 @@
|
||||
import { Integer, JsNumber, asInteger } from "./Integer";
|
||||
import { asIntN, logLines } from "./utils";
|
||||
import { asIntN } from "./utils";
|
||||
|
||||
export default {
|
||||
const calc = {
|
||||
|
||||
numberOfBitsDisplayed: function (num: Integer | JsNumber) : number {
|
||||
const n = asInteger(num);
|
||||
const len = this.toBinaryString(n).length;
|
||||
return (len+1) == n.maxBitSize ? n.maxBitSize : len; // Include sign bit if it is all that left
|
||||
return (len + 1) === n.maxBitSize ? n.maxBitSize : len; // Include sign bit if it is all that left
|
||||
},
|
||||
|
||||
flipBit: function(num: Integer | JsNumber, bitIndex: number): Integer {
|
||||
@@ -51,14 +51,14 @@ export default {
|
||||
? this.engine.applyTwosComplement(bin)
|
||||
: bin;
|
||||
|
||||
return bin.length != bitSize ? r.substring(r.length-bin.length) : r;
|
||||
return bin.length !== bitSize ? r.substring(r.length-bin.length) : r;
|
||||
},
|
||||
|
||||
lshift (num: Integer, numBytes : JsNumber) : Integer {
|
||||
|
||||
let bytes = asIntN(numBytes);
|
||||
|
||||
if(num.maxBitSize == numBytes)
|
||||
if(num.maxBitSize === bytes)
|
||||
return num; // Preserve C undefined behavior
|
||||
|
||||
while(bytes > num.maxBitSize) bytes -= num.maxBitSize;
|
||||
@@ -70,7 +70,7 @@ export default {
|
||||
|
||||
let bytes = asIntN(numBytes);
|
||||
|
||||
if(num.maxBitSize == numBytes)
|
||||
if(num.maxBitSize === bytes)
|
||||
return num; // Preserve C undefined behavior
|
||||
|
||||
while(bytes > num.maxBitSize) bytes -= num.maxBitSize;
|
||||
@@ -82,7 +82,7 @@ export default {
|
||||
|
||||
let bytes = asIntN(numBytes);
|
||||
|
||||
if(num.maxBitSize == numBytes)
|
||||
if(num.maxBitSize === bytes)
|
||||
return num; // Preserve C undefined behavior
|
||||
|
||||
while(bytes > num.maxBitSize) bytes -= num.maxBitSize;
|
||||
@@ -114,18 +114,18 @@ export default {
|
||||
|
||||
let negative = false;
|
||||
|
||||
if(num.signed && bin['0'] == '1') {
|
||||
if(num.signed && bin[0] === '1') {
|
||||
bin = this.engine.applyTwosComplement(bin);
|
||||
negative = true;
|
||||
}
|
||||
|
||||
const result = BigInt("0b" + bin) * BigInt(negative ? -1 : 1);
|
||||
return new Integer(typeof num.value == "bigint" ? result : asIntN(result), num.maxBitSize, num.signed);
|
||||
return new Integer(typeof num.value === "bigint" ? result : asIntN(result), num.maxBitSize, num.signed);
|
||||
},
|
||||
|
||||
_applyTwo(op1: Integer, op2: Integer, operation: (bin1:string, bin2:string) => string) : Integer {
|
||||
|
||||
if(op1.maxBitSize == op2.maxBitSize && op1.signed != op2.signed)
|
||||
if(op1.maxBitSize === op2.maxBitSize && op1.signed !== op2.signed)
|
||||
throw new Error("This operation cannot be applied to signed and unsigned operands of the same size");
|
||||
|
||||
const [num1, num2] = equalizeSize(op1, op2);
|
||||
@@ -137,7 +137,7 @@ export default {
|
||||
|
||||
let m = BigInt(1);
|
||||
|
||||
if(resultBin['0'] == '1') {
|
||||
if(resultBin[0] === '1') {
|
||||
resultBin = this.engine.applyTwosComplement(resultBin);
|
||||
m = BigInt(-1);
|
||||
}
|
||||
@@ -204,7 +204,7 @@ export default {
|
||||
const b1 = bin1[i] === "1";
|
||||
const b2 = bin2[i] === "1";
|
||||
|
||||
result.push(b1 != b2 ? "1" : "0");
|
||||
result.push(b1 !== b2 ? "1" : "0");
|
||||
}
|
||||
|
||||
return result.join('');
|
||||
@@ -217,7 +217,7 @@ export default {
|
||||
|
||||
// If there exists no '1' concat 1 at the
|
||||
// starting of string
|
||||
if (lastIndex == -1)
|
||||
if (lastIndex === -1)
|
||||
return "1" + bin;
|
||||
|
||||
// Continue traversal backward after the position of
|
||||
@@ -225,7 +225,7 @@ export default {
|
||||
var flipped =[];
|
||||
for (var i = lastIndex - 1; i >= 0; i--) {
|
||||
// Just flip the values
|
||||
flipped.unshift(bin.charAt(i) == "1" ? "0" : "1");
|
||||
flipped.unshift(bin.charAt(i) === "1" ? "0" : "1");
|
||||
}
|
||||
|
||||
const result = flipped.join('') + bin.substring(lastIndex);
|
||||
@@ -235,8 +235,10 @@ export default {
|
||||
}
|
||||
};
|
||||
|
||||
export default calc;
|
||||
|
||||
function checkSameLength(bin1: string, bin2: string) {
|
||||
if (bin1.length != bin2.length)
|
||||
if (bin1.length !== bin2.length)
|
||||
throw new Error("Binary strings must have the same length");
|
||||
}
|
||||
|
||||
@@ -251,7 +253,7 @@ function nextPowOfTwo(num: number) : number {
|
||||
}
|
||||
|
||||
function equalizeSize(n1: Integer, n2: Integer) : [Integer, Integer] {
|
||||
if(n1.maxBitSize == n2.maxBitSize)
|
||||
if(n1.maxBitSize === n2.maxBitSize)
|
||||
{
|
||||
if(n1.signed === n2.signed) return [n1,n2];
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import React from 'react';
|
||||
import './BinaryString.css';
|
||||
import loglevel from 'loglevel';
|
||||
|
||||
export type BinaryStringViewProps = {
|
||||
allowFlipBits?: boolean;
|
||||
@@ -32,7 +31,7 @@ export default class BinaryStringView extends React.Component<BinaryStringViewPr
|
||||
}
|
||||
|
||||
const arr = this.props.binaryString.split('');
|
||||
arr[index] = arr[index] == '0' ? '1' : '0';
|
||||
arr[index] = arr[index] === '0' ? '1' : '0';
|
||||
const newBinaryString = arr.join('');
|
||||
|
||||
this.props.onBitClicked({
|
||||
@@ -59,13 +58,13 @@ export default class BinaryStringView extends React.Component<BinaryStringViewPr
|
||||
const css = allowFlipBits ? ' flipable' : ''
|
||||
|
||||
const disableHighlight = this.props.disableHighlight || false;
|
||||
const firstBitIndex = this.props.valueBitSize != null
|
||||
const firstBitIndex = this.props.valueBitSize !== null && this.props.valueBitSize !== undefined
|
||||
? bitChars.length - this.props.valueBitSize
|
||||
: -1;
|
||||
|
||||
return bitChars.map((c, i) => {
|
||||
|
||||
var className = c == '1' ? `one${css}` : `zero${css}`;
|
||||
var className = c === '1' ? `one${css}` : `zero${css}`;
|
||||
var tooltip = '';
|
||||
|
||||
|
||||
|
||||
18
src/core/components/CommandLink.css
Normal file
18
src/core/components/CommandLink.css
Normal file
@@ -0,0 +1,18 @@
|
||||
.command-link {
|
||||
background: none;
|
||||
border: none;
|
||||
color: inherit;
|
||||
cursor: pointer;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 0.25rem;
|
||||
padding: 0;
|
||||
font: inherit;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.command-link:focus {
|
||||
outline: 2px solid currentColor;
|
||||
outline-offset: 2px;
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ import { IconDefinition } from '@fortawesome/fontawesome-svg-core';
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||
import React from 'react';
|
||||
import cmd from '../../shell/cmd';
|
||||
import './CommandLink.css';
|
||||
|
||||
type CommandLinkProps = {
|
||||
command?:string;
|
||||
@@ -11,13 +12,15 @@ type CommandLinkProps = {
|
||||
}
|
||||
|
||||
function CommandLink({icon, command, text, textClassName}: CommandLinkProps) {
|
||||
|
||||
const onClick = () => cmd.execute(command || text);
|
||||
|
||||
if(icon != null)
|
||||
return <a href="javascript:void(0)" onClick={onClick}><FontAwesomeIcon icon={icon} className="icon" /><span className={textClassName}>{text}</span></a>;
|
||||
const onClick = () => cmd.execute(command ?? text);
|
||||
|
||||
return <a href="javascript:void(0)" onClick={onClick}><span className={textClassName}>{text}</span></a>;
|
||||
return (
|
||||
<button type="button" className="command-link" onClick={onClick}>
|
||||
{icon ? <FontAwesomeIcon icon={icon} className="icon" /> : null}
|
||||
<span className={textClassName}>{text}</span>
|
||||
</button>
|
||||
);
|
||||
}
|
||||
|
||||
export default CommandLink;
|
||||
@@ -6,7 +6,7 @@ const formatter = {
|
||||
numberToString: function(num: Integer | JsNumber, base: NumberBase | number, padLength?: number) : string {
|
||||
|
||||
num = asInteger(num);
|
||||
base = typeof base == "string" ? getBase(base) : base;
|
||||
base = typeof base === "string" ? getBase(base) : base;
|
||||
|
||||
switch(base) {
|
||||
case 16:
|
||||
@@ -15,7 +15,7 @@ const formatter = {
|
||||
case 2:
|
||||
const bin = calc.toBinaryString(num);
|
||||
|
||||
if(padLength == null)
|
||||
if(padLength === null || padLength === undefined)
|
||||
return bin;
|
||||
|
||||
const padChar = num.value >= 0 ? '0' : '1';
|
||||
@@ -27,14 +27,15 @@ const formatter = {
|
||||
}
|
||||
},
|
||||
padLeft: function (str: string, length: number, symbol: string) : string {
|
||||
var sb = Array.prototype.slice.call(str), symbol = symbol || "0";
|
||||
const fillCharacter = symbol ?? "0";
|
||||
const sb: string[] = Array.prototype.slice.call(str);
|
||||
|
||||
if(length == null) {
|
||||
if(length === null || length === undefined) {
|
||||
return str;
|
||||
}
|
||||
|
||||
while(length > sb.length) {
|
||||
sb.unshift(symbol);
|
||||
sb.unshift(fillCharacter);
|
||||
}
|
||||
|
||||
return sb.join('');
|
||||
@@ -66,7 +67,7 @@ const formatter = {
|
||||
|
||||
mask++;
|
||||
|
||||
if(mask == b) {
|
||||
if(mask === b) {
|
||||
b = mask2;
|
||||
res.push(tmp.join(''));
|
||||
tmp = [];
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
export default {
|
||||
const hashUtils = {
|
||||
encodeHash: function(input:string):string {
|
||||
return encodeURIComponent(input.trim().replace(/\s/g,','));
|
||||
},
|
||||
decodeHash: function(hashValue:string):string {
|
||||
return decodeURIComponent(hashValue.replace(/^\#/, '')).replace(/,/g,' ');
|
||||
return decodeURIComponent(hashValue.replace(/^#/, '')).replace(/,/g,' ');
|
||||
},
|
||||
getArgs: function (hashValue:string) : string[] {
|
||||
|
||||
@@ -18,6 +18,8 @@ export default {
|
||||
}
|
||||
};
|
||||
|
||||
export default hashUtils;
|
||||
|
||||
function splitHashList(str: string) : string[] {
|
||||
|
||||
return str.split('||').filter(s => s.length > 0);
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
export default {
|
||||
const typeGuards = {
|
||||
plainObject: function(obj : any) : boolean {
|
||||
return typeof obj == "object" && !(obj instanceof Array) && obj instanceof Object;
|
||||
return typeof obj === "object" && !(obj instanceof Array) && obj instanceof Object;
|
||||
},
|
||||
|
||||
aFunction: function(obj : any) : boolean {
|
||||
return typeof obj == "function";
|
||||
return typeof obj === "function";
|
||||
},
|
||||
|
||||
string: function(obj : any) : boolean {
|
||||
return typeof obj == "string";
|
||||
return typeof obj === "string";
|
||||
},
|
||||
|
||||
array: function(obj : any) : boolean {
|
||||
@@ -16,6 +16,8 @@ export default {
|
||||
},
|
||||
|
||||
number: function(obj : any) : boolean {
|
||||
return typeof obj == "number" && !isNaN(obj)
|
||||
return typeof obj === "number" && !isNaN(obj);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export default typeGuards;
|
||||
@@ -15,7 +15,7 @@ function asIntN(num: JsNumber | Integer) : number {
|
||||
if(isInteger(num))
|
||||
return asIntN(num.value);
|
||||
|
||||
return typeof num == "bigint" ? parseInt(num.toString()): num as number;
|
||||
return typeof num === "bigint" ? parseInt(num.toString()): num as number;
|
||||
}
|
||||
|
||||
function random(from: number, to: number) {
|
||||
@@ -23,11 +23,11 @@ function random(from: number, to: number) {
|
||||
}
|
||||
|
||||
function randomBool() {
|
||||
return random(1, 10000) % 2 == 0;
|
||||
return random(1, 10000) % 2 === 0;
|
||||
}
|
||||
|
||||
function logLines(...params: any[]) {
|
||||
console.log(params.join('\n'))
|
||||
console.log(params.join('\n'));
|
||||
}
|
||||
|
||||
export {chunkifyString, asIntN, random, randomBool, logLines};
|
||||
@@ -63,7 +63,7 @@ function tokenize(input: string): Token[] {
|
||||
if (!firstToken.skip)
|
||||
found.push({value: value, type: firstToken.type});
|
||||
|
||||
if (cur.length == value.length)
|
||||
if (cur.length === value.length)
|
||||
break;
|
||||
|
||||
cur = cur.substring(value.length);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import calc from "../core/calc";
|
||||
import Operand from "./Operand";
|
||||
import { Expression, ExpressionElement } from "./expression-interfaces";
|
||||
import { Expression } from "./expression-interfaces";
|
||||
|
||||
export default class ListOfNumbers implements Expression {
|
||||
children: Operand[];
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { ExpressionElement as ExpressionElement } from './expression-interfaces';
|
||||
import { ExpressionElement } from './expression-interfaces';
|
||||
import { NumberBase } from '../core/formatter';
|
||||
import { INT64_MAX_VALUE, INT64_MIN_VALUE, UINT64_MAX_VALUE } from '../core/const';
|
||||
import { Integer, JsNumber, isInteger, asInteger } from '../core/Integer';
|
||||
|
||||
@@ -23,12 +23,12 @@ export default class Operator implements ExpressionElement {
|
||||
if (operand instanceof Operator)
|
||||
throw new Error('operand must be scalar value');
|
||||
|
||||
if( this.operator != "~" && operand == null)
|
||||
if( this.operator !== "~" && (operand === undefined || operand === null))
|
||||
throw new Error("operand is required");
|
||||
|
||||
var evaluatedOperand = this.operand.evaluate();
|
||||
|
||||
return this.operator == "~"
|
||||
return this.operator === "~"
|
||||
? applyNotOperator(this.operand.getUnderlyingOperand())
|
||||
: applyOperator(operand!, this.operator, evaluatedOperand);
|
||||
}
|
||||
@@ -52,7 +52,7 @@ function applyOperator(op1 : Operand, operator: string, op2 : Operand) : Operand
|
||||
|
||||
if(!isShift)
|
||||
{
|
||||
if(op1.value.maxBitSize == op2.value.maxBitSize && op1.value.signed != op2.value.signed)
|
||||
if(op1.value.maxBitSize === op2.value.maxBitSize && op1.value.signed !== op2.value.signed)
|
||||
throw new Error("Operator `" + operator + "` cannot be applied to signed and unsigned operands of the same " + op2.value.maxBitSize + " -bit size");
|
||||
|
||||
equalizeSize(op1, op2);
|
||||
|
||||
@@ -7,7 +7,7 @@ import { Operator, Operand, ListOfNumbers } from '../expression';
|
||||
import calc from '../../core/calc';
|
||||
import { Integer } from '../../core/Integer';
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||
import { faInfoCircle, faTriangleExclamation, faUndo } from '@fortawesome/free-solid-svg-icons';
|
||||
import { faInfoCircle, faUndo } from '@fortawesome/free-solid-svg-icons';
|
||||
import loglevel from 'loglevel';
|
||||
import IconWithToolTip from '../../shell/components/IconWithTooltip';
|
||||
|
||||
@@ -191,7 +191,7 @@ class ExpressionElementTableRow extends React.Component<ExpressionElementRowProp
|
||||
}
|
||||
|
||||
getLabelString(op: Operand): string {
|
||||
return formatter.numberToString(op.value, op.base == 'bin' ? 'dec' : op.base);
|
||||
return formatter.numberToString(op.value, op.base === 'bin' ? 'dec' : op.base);
|
||||
}
|
||||
|
||||
undo() {
|
||||
@@ -201,7 +201,7 @@ class ExpressionElementTableRow extends React.Component<ExpressionElementRowProp
|
||||
|
||||
onBitClicked(args: BitClickedEventArg) {
|
||||
|
||||
const { bitIndex, binaryStringLength: binaryStringLength } = args;
|
||||
const { bitIndex, binaryStringLength } = args;
|
||||
|
||||
const maxBitSize = this.scalar.value.maxBitSize;
|
||||
|
||||
|
||||
@@ -2,7 +2,6 @@ import { Operand, ListOfNumbers, BitwiseOperation, Operator } from '../expressio
|
||||
import { ExpressionElement, Expression } from '../expression-interfaces';
|
||||
import calc from '../../core/calc';
|
||||
import formatter from '../../core/formatter';
|
||||
import exp from 'constants';
|
||||
|
||||
type Config = {
|
||||
emphasizeBytes: boolean;
|
||||
@@ -43,27 +42,26 @@ export default class BitwiseResultViewModel {
|
||||
}
|
||||
|
||||
static buildBitwiseOperation (expr : BitwiseOperation, config : Config) {
|
||||
|
||||
var op = expr.children[0],
|
||||
i = 0, len = expr.children.length,
|
||||
ex, m = new BitwiseResultViewModel(config);
|
||||
|
||||
var prev : Operand | null = null;
|
||||
const len = expr.children.length;
|
||||
const m = new BitwiseResultViewModel(config);
|
||||
|
||||
for (;i<len;i++) {
|
||||
ex = expr.children[i];
|
||||
let prev : Operand | null = null;
|
||||
|
||||
for (let i = 0; i < len; i++) {
|
||||
const ex = expr.children[i];
|
||||
if(ex instanceof Operand) {
|
||||
m.addScalarRow(ex);
|
||||
prev = ex;
|
||||
continue;
|
||||
}
|
||||
|
||||
var eo = ex as Operator;
|
||||
const eo = ex as Operator;
|
||||
|
||||
// If it a single NOT expression
|
||||
if(eo.isNotExpression) {
|
||||
m.addOperatorRow(eo);
|
||||
var notResult = eo.evaluate();
|
||||
const notResult = eo.evaluate();
|
||||
m.addExpressionResultRow(notResult);
|
||||
prev = notResult;
|
||||
}
|
||||
@@ -152,7 +150,7 @@ export default class BitwiseResultViewModel {
|
||||
|
||||
static applyEmphasizeBytes (bits : number, emphasizeBytes : boolean) : number {
|
||||
|
||||
if(emphasizeBytes && bits % 8 != 0) {
|
||||
if(emphasizeBytes && bits % 8 !== 0) {
|
||||
if(bits < 8) {
|
||||
return 8;
|
||||
}
|
||||
|
||||
@@ -114,7 +114,7 @@ describe("comparison with nodejs engine", () => {
|
||||
const res = bo.evaluate();
|
||||
actual = res.value.toString();
|
||||
|
||||
if(actual != expected) {
|
||||
if(actual !== expected) {
|
||||
const uop = bo.getUnderlyingOperand();
|
||||
console.log(`Expected:${expectedInput}\nActual:${actualInput}\n${uop.value} ${uop.value.maxBitSize}\n${res.value} ${typeof res.value} ${res.value.maxBitSize}`)
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@ class ExpressionParser {
|
||||
var trimmed = input.replace(/^\s+|\s+$/, '');
|
||||
var i = this.factories.length-1;
|
||||
for(;i>=0;i--) {
|
||||
if(this.factories[i].canCreate(trimmed) === true){
|
||||
if(this.factories[i].canCreate(trimmed)){
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -41,7 +41,7 @@ class ExpressionParser {
|
||||
|
||||
factory = this.factories[i];
|
||||
|
||||
if(factory.canCreate(trimmed) == true){
|
||||
if(factory.canCreate(trimmed)){
|
||||
return factory.create(trimmed);
|
||||
}
|
||||
}
|
||||
@@ -56,17 +56,14 @@ class ExpressionParser {
|
||||
|
||||
class ListOfNumbersExpressionFactory implements IExpressionParserFactory
|
||||
{
|
||||
constructor() {
|
||||
}
|
||||
|
||||
canCreate (input: string): boolean {
|
||||
if(input.length == 0) return false;
|
||||
|
||||
if(input.length === 0) return false;
|
||||
|
||||
return input.split(' ')
|
||||
.filter(p => p.length > 0)
|
||||
.map(p => numberParser.caseParse(p))
|
||||
.filter(n => n == false)
|
||||
.length == 0;
|
||||
.filter(n => n === false)
|
||||
.length === 0;
|
||||
};
|
||||
|
||||
create (input : string) : Expression {
|
||||
@@ -84,8 +81,8 @@ class BitwiseOperationExpressionFactory implements IExpressionParserFactory {
|
||||
regex: RegExp;
|
||||
|
||||
constructor() {
|
||||
this.fullRegex = /^[-,~,<,>,&,\^\|,b,x,l,s,u,a-f,0-9,\s]+$/i;
|
||||
this.regex = /(<<|>>|>>>|\||\&|\^)?(~?-?(?:[b,x,l,s,u,,a-f,0-9]+))/gi;
|
||||
this.fullRegex = /^[-,~,<,>,&,^|,b,x,l,s,u,a-f,0-9,\s]+$/i;
|
||||
this.regex = /(<<|>>|>>>|\||&|\^)?(~?-?(?:[b,x,l,s,u,,a-f,0-9]+))/gi;
|
||||
}
|
||||
|
||||
canCreate (input: string) : boolean {
|
||||
@@ -100,7 +97,7 @@ class BitwiseOperationExpressionFactory implements IExpressionParserFactory {
|
||||
|
||||
this.regex.lastIndex = 0;
|
||||
|
||||
while ((m = this.regex.exec(normalizedString)) != null) {
|
||||
while ((m = this.regex.exec(normalizedString)) !== null) {
|
||||
operands.push(this.parseMatch(m));
|
||||
}
|
||||
|
||||
@@ -109,20 +106,19 @@ class BitwiseOperationExpressionFactory implements IExpressionParserFactory {
|
||||
|
||||
parseMatch (m:RegExpExecArray): ExpressionElement {
|
||||
|
||||
var input = m[0],
|
||||
operator = m[1],
|
||||
var operator = m[1],
|
||||
num = m[2];
|
||||
|
||||
var parsed = null;
|
||||
|
||||
if(num.indexOf('~') == 0) {
|
||||
if(num.indexOf('~') === 0) {
|
||||
parsed = new Operator(parseScalarValue(num.substring(1)), '~');
|
||||
}
|
||||
else {
|
||||
parsed = parseScalarValue(num);
|
||||
}
|
||||
|
||||
if(operator == null) {
|
||||
if(operator === undefined || operator === null) {
|
||||
return parsed as Operator;
|
||||
} else {
|
||||
return new Operator(parsed as Operand, operator);
|
||||
@@ -137,7 +133,7 @@ class BitwiseOperationExpressionFactory implements IExpressionParserFactory {
|
||||
function parseScalarValue(input : string) : Operand {
|
||||
const n = numberParser.parse(input);
|
||||
var sv = new Operand(n.value, n.base);
|
||||
if(sv.value.maxBitSize != n.value.maxBitSize) throw new Error("Gotcha!");
|
||||
if(sv.value.maxBitSize !== n.value.maxBitSize) throw new Error("Gotcha!");
|
||||
return sv;
|
||||
}
|
||||
|
||||
|
||||
@@ -27,13 +27,13 @@ class NumberParser {
|
||||
|
||||
parse (input : string) : ParsedNumber {
|
||||
|
||||
if(input.length == 0) throw new Error("input is null or empty");
|
||||
if(input.length === 0) throw new Error("input is null or empty");
|
||||
|
||||
const regex = new RegExp(numberRegexFullString, "i");
|
||||
|
||||
const m = regex.exec(input);
|
||||
|
||||
if(m == null || m.length == 0)
|
||||
if(m === null || m.length === 0)
|
||||
throw new Error(input + " is not a number");
|
||||
|
||||
const value = parseInteger(m[0], m[1], m[2] || '');
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
import React, { useState } from 'react';
|
||||
import BinaryStringView from '../../core/components/BinaryString';
|
||||
import './SubnetView.css';
|
||||
import { getNetworkAddress, getBroadCastAddress, createSubnetMaskIp } from '../subnet-utils';
|
||||
import { chunkifyString } from '../../core/utils';
|
||||
import IpAddressBinaryString from './IpAddressBinaryString';
|
||||
import { IpAddress, IpAddressWithSubnetMask, SubnetCommand } from '../models';
|
||||
|
||||
@@ -41,9 +39,9 @@ function SubnetView(props : {subnet : SubnetCommand}) {
|
||||
</td>
|
||||
<td data-test-name="decimal">
|
||||
|
||||
<button className="btn" onClick={decrementMask} disabled={subnet.cidr.maskBits === 0} title="Decrease mask size">-</button>
|
||||
<button type="button" className="btn" onClick={decrementMask} disabled={subnet.cidr.maskBits === 0} title="Decrease mask size">-</button>
|
||||
<span>{subnet.cidr.maskBits}</span>
|
||||
<button className="btn"onClick={incrementMask} disabled={subnet.cidr.maskBits === 32} title="Increase mask size">+</button>
|
||||
<button type="button" className="btn"onClick={incrementMask} disabled={subnet.cidr.maskBits === 32} title="Increase mask size">+</button>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
|
||||
@@ -2,8 +2,7 @@ import React, { useState } from 'react';
|
||||
import BinaryStringView from '../../core/components/BinaryString';
|
||||
import './VpcView.css';
|
||||
import { getNetworkAddress, getAddressSpaceSize } from '../subnet-utils';
|
||||
import IpAddressBinaryString from './IpAddressBinaryString';
|
||||
import { IpAddress, IpAddressWithSubnetMask, VpcCommand } from '../models';
|
||||
import { IpAddressWithSubnetMask, VpcCommand } from '../models';
|
||||
import formatter from '../../core/formatter';
|
||||
import { faInfoCircle } from '@fortawesome/free-solid-svg-icons';
|
||||
import IconWithToolTip from '../../shell/components/IconWithTooltip';
|
||||
@@ -57,9 +56,9 @@ function SubnetView(props: { vpc: VpcCommand }) {
|
||||
VPC CIDR Mask:
|
||||
</td>
|
||||
<td>
|
||||
<button className="btn" onClick={decrVpc} disabled={vpc.cidr.maskBits <= 1} title="Decrease vpc address bits">-</button>
|
||||
/{vpc.cidr.maskBits}
|
||||
<button className="btn" onClick={incrVpc} disabled={subnetMaskSize >= MAX_NON_HOSTS_BITS} title="Increse vpc address bits">+</button>
|
||||
<button type="button" className="btn" onClick={decrVpc} disabled={vpc.cidr.maskBits <= 1} title="Decrease vpc address bits">-</button>
|
||||
/{vpc.cidr.maskBits}
|
||||
<button type="button" className="btn" onClick={incrVpc} disabled={subnetMaskSize >= MAX_NON_HOSTS_BITS} title="Increase vpc address bits">+</button>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
@@ -67,9 +66,9 @@ function SubnetView(props: { vpc: VpcCommand }) {
|
||||
Subnet CIDR Mask:
|
||||
</td>
|
||||
<td>
|
||||
<button className="btn" onClick={decrSubnet} disabled={vpc.subnetBits <= 1} title="Increase subnet bits">-</button>
|
||||
/{subnetMaskSize}
|
||||
<button className="btn" onClick={incrSubnet} disabled={vpc.cidr.maskBits + vpc.subnetBits >= MAX_NON_HOSTS_BITS} title="Increase subnet bits">+</button>
|
||||
<button type="button" className="btn" onClick={decrSubnet} disabled={vpc.subnetBits <= 1} title="Increase subnet bits">-</button>
|
||||
/{subnetMaskSize}
|
||||
<button type="button" className="btn" onClick={incrSubnet} disabled={vpc.cidr.maskBits + vpc.subnetBits >= MAX_NON_HOSTS_BITS} title="Increase subnet bits">+</button>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
@@ -77,9 +76,9 @@ function SubnetView(props: { vpc: VpcCommand }) {
|
||||
Max Subnets in VPC:
|
||||
</td>
|
||||
<td>
|
||||
<button className="btn" onClick={decrSubnet} disabled={vpc.subnetBits <= 1} title="Decrease subnet bits">-</button>
|
||||
<button type="button" className="btn" onClick={decrSubnet} disabled={vpc.subnetBits <= 1} title="Decrease subnet bits">-</button>
|
||||
{maxSubnets}
|
||||
<button className="btn" onClick={incrSubnet} disabled={vpc.cidr.maskBits + vpc.subnetBits >= MAX_NON_HOSTS_BITS} title="Increase subnet bits">+</button>
|
||||
<button type="button" className="btn" onClick={incrSubnet} disabled={vpc.cidr.maskBits + vpc.subnetBits >= MAX_NON_HOSTS_BITS} title="Increase subnet bits">+</button>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
@@ -104,21 +103,6 @@ function SubnetView(props: { vpc: VpcCommand }) {
|
||||
</React.Fragment>;
|
||||
}
|
||||
|
||||
function Indicator2(props: { ip: IpAddress, descr: string }) {
|
||||
|
||||
const { ip, descr } = props;
|
||||
|
||||
return <tr>
|
||||
<td className="soft" data-test-name="label">{descr}</td>
|
||||
<td data-test-name="decimal" className="ip-address-col">
|
||||
{ip.toString()}
|
||||
</td>
|
||||
<td data-test-name="bin">
|
||||
<IpAddressBinaryString ip={ip} />
|
||||
</td>
|
||||
</tr>;
|
||||
}
|
||||
|
||||
export default SubnetView;
|
||||
|
||||
class VpcModel {
|
||||
|
||||
@@ -17,10 +17,10 @@ const ipAddressParser = {
|
||||
const result = this.parseCommand(input);
|
||||
|
||||
const matches = this.getMaches(result.nextInput);
|
||||
const correctInputs = matches.filter(m => m.matches != null);
|
||||
const incorrectInputs = matches.filter(m => m.matches == null);
|
||||
|
||||
if(correctInputs.length == 0)
|
||||
const correctInputs = matches.filter(m => m.matches !== null && m.matches !== undefined);
|
||||
const incorrectInputs = matches.filter(m => m.matches === null || m.matches === undefined);
|
||||
|
||||
if(correctInputs.length === 0)
|
||||
return null;
|
||||
|
||||
if(incorrectInputs.length > 0) {
|
||||
@@ -34,9 +34,9 @@ const ipAddressParser = {
|
||||
return parsingErrors[0] as ParsingError;
|
||||
}
|
||||
|
||||
if(result.command != null) {
|
||||
const cmd =
|
||||
result.command == CMD_SUBNET
|
||||
if(result.command !== null) {
|
||||
const cmd =
|
||||
result.command === CMD_SUBNET
|
||||
? this.createSubnetDefinition(parsedObjects as ParsedIpObject[])
|
||||
: this.createVpcDefinition(parsedObjects as ParsedIpObject[]);
|
||||
|
||||
@@ -62,15 +62,15 @@ const ipAddressParser = {
|
||||
|
||||
getMaches(input : string) : { matches: RegExpExecArray | null, input: string }[] {
|
||||
|
||||
return input.
|
||||
replace(/[\t\s]+/g, ' ')
|
||||
.split(' ')
|
||||
return input
|
||||
.replace(/[\t\s]+/g, ' ')
|
||||
.split(' ')
|
||||
.filter(s => s.length>0)
|
||||
.map(s => {
|
||||
const ipV4Regex = /^([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})(\/\d+)?$/;
|
||||
const matches = ipV4Regex.exec(s);
|
||||
|
||||
if(matches == null || matches.length === 0)
|
||||
if(matches === null || matches.length === 0)
|
||||
return {matches: null, input: s};
|
||||
|
||||
return {matches, input: s};
|
||||
@@ -105,7 +105,7 @@ const ipAddressParser = {
|
||||
},
|
||||
|
||||
createSubnetDefinition(items: ParsedIpObject[]) : SubnetCommand | ParsingError {
|
||||
if(items.length != 1)
|
||||
if(items.length !== 1)
|
||||
return new ParsingError("Incorrect network definition");
|
||||
|
||||
const first = items[0];
|
||||
@@ -118,7 +118,7 @@ const ipAddressParser = {
|
||||
|
||||
createVpcDefinition(items: ParsedIpObject[]) : VpcCommand | ParsingError {
|
||||
|
||||
if(items.length != 1)
|
||||
if(items.length !== 1)
|
||||
return new ParsingError("Incorrect VPC definition");
|
||||
|
||||
const first = items[0];
|
||||
|
||||
@@ -9,7 +9,6 @@ import log from 'loglevel';
|
||||
import SubnetView from './components/SubnetView';
|
||||
import { createSubnetMaskIp } from './subnet-utils';
|
||||
import {sendAnalyticsEvent} from '../shell/analytics';
|
||||
import TextResultView from '../shell/components/TextResultView';
|
||||
import VpcView from './components/VpcView';
|
||||
|
||||
const networkingAppModule = {
|
||||
@@ -17,11 +16,14 @@ const networkingAppModule = {
|
||||
|
||||
// Add Ip Address commands
|
||||
cmd.command({
|
||||
canHandle: (input:string) => ipAddressParser.parse(input) != null,
|
||||
canHandle: (input:string) => {
|
||||
const parsed = ipAddressParser.parse(input);
|
||||
return parsed !== null && parsed !== undefined;
|
||||
},
|
||||
handle: function(c: CommandInput) {
|
||||
var result = ipAddressParser.parse(c.input);
|
||||
|
||||
if(result == null)
|
||||
if(result === null || result === undefined)
|
||||
return;
|
||||
|
||||
if(result instanceof ParsingError) {
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { createSubnetMaskByte } from "../core/byte";
|
||||
import { flipBitsToOne, flipBitsToZero } from '../core/byte';
|
||||
import { createSubnetMaskByte, flipBitsToOne, flipBitsToZero } from '../core/byte';
|
||||
import { IpAddress, IpAddressWithSubnetMask, NetworkClass } from "./models";
|
||||
|
||||
function createSubnetMaskIp(ipm: IpAddressWithSubnetMask) : IpAddress {
|
||||
@@ -59,7 +58,6 @@ function getNetworkClass (ipAddress: IpAddress) : NetworkClass {
|
||||
const byte = ipAddress.firstByte;
|
||||
|
||||
const firstBitOne = (byte & 128) === 128;
|
||||
const firstBitZero = (byte & 128) === 0;
|
||||
const secondBitOne = (byte & 64) === 64;
|
||||
|
||||
const thirdBitOne = (byte & 32) === 32;
|
||||
|
||||
@@ -1,13 +1,41 @@
|
||||
import { ReportHandler } from 'web-vitals';
|
||||
import type { Metric } from 'web-vitals';
|
||||
|
||||
type ReportHandler = (metric: Metric) => void;
|
||||
|
||||
type WebVitalsModule = {
|
||||
onCLS?: (callback: ReportHandler) => void;
|
||||
onFID?: (callback: ReportHandler) => void;
|
||||
onFCP?: (callback: ReportHandler) => void;
|
||||
onLCP?: (callback: ReportHandler) => void;
|
||||
onTTFB?: (callback: ReportHandler) => void;
|
||||
getCLS?: (callback: ReportHandler) => void;
|
||||
getFID?: (callback: ReportHandler) => void;
|
||||
getFCP?: (callback: ReportHandler) => void;
|
||||
getLCP?: (callback: ReportHandler) => void;
|
||||
getTTFB?: (callback: ReportHandler) => void;
|
||||
};
|
||||
|
||||
const reportWebVitals = (onPerfEntry?: ReportHandler) => {
|
||||
if (onPerfEntry && onPerfEntry instanceof Function) {
|
||||
import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => {
|
||||
getCLS(onPerfEntry);
|
||||
getFID(onPerfEntry);
|
||||
getFCP(onPerfEntry);
|
||||
getLCP(onPerfEntry);
|
||||
getTTFB(onPerfEntry);
|
||||
import('web-vitals').then((module) => {
|
||||
const {
|
||||
onCLS,
|
||||
onFID,
|
||||
onFCP,
|
||||
onLCP,
|
||||
onTTFB,
|
||||
getCLS,
|
||||
getFID,
|
||||
getFCP,
|
||||
getLCP,
|
||||
getTTFB,
|
||||
} = module as WebVitalsModule;
|
||||
|
||||
(onCLS ?? getCLS)?.(onPerfEntry);
|
||||
(onFID ?? getFID)?.(onPerfEntry);
|
||||
(onFCP ?? getFCP)?.(onPerfEntry);
|
||||
(onLCP ?? getLCP)?.(onPerfEntry);
|
||||
(onTTFB ?? getTTFB)?.(onPerfEntry);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
@@ -49,7 +49,7 @@ export default class AppState {
|
||||
this.uiTheme = persistData.uiTheme;
|
||||
this.emphasizeBytes = !!persistData.emphasizeBytes;
|
||||
this.persistedVersion = persistData.version || 0.1;
|
||||
this.wasOldVersion = persistData.version != null && this.version > this.persistedVersion;
|
||||
this.wasOldVersion = persistData.version !== null && persistData.version !== undefined && this.version > this.persistedVersion;
|
||||
this.debugMode = persistData.debugMode === true;
|
||||
this.pageVisitsCount = persistData.pageVisistsCount || 0;
|
||||
this.donationClicked = persistData.donationClicked;
|
||||
@@ -79,7 +79,7 @@ export default class AppState {
|
||||
}
|
||||
|
||||
toggleEmphasizeBytes(value?: boolean) {
|
||||
this.emphasizeBytes = value != null ? value : !this.emphasizeBytes;
|
||||
this.emphasizeBytes = value !== null && value !== undefined ? value : !this.emphasizeBytes;
|
||||
this.triggerChanged();
|
||||
}
|
||||
|
||||
@@ -107,7 +107,7 @@ export default class AppState {
|
||||
}
|
||||
|
||||
toggleAnnotateTypes(value?: boolean) {
|
||||
this.annotateTypes = value != null ? value : !this.annotateTypes;
|
||||
this.annotateTypes = value !== null && value !== undefined ? value : !this.annotateTypes;
|
||||
this.triggerChanged();
|
||||
}
|
||||
|
||||
|
||||
@@ -10,8 +10,9 @@ export type AnalyticsHandler = (evt: AnalyticsEvent) => boolean;
|
||||
|
||||
function sendAnalyticsEvent(evt : AnalyticsEvent) {
|
||||
const handler = (window as any).bitwiseCmdAnalyticsHandler;
|
||||
if(handler == null) {
|
||||
if(handler === null || handler === undefined) {
|
||||
log.debug('ERROR!!!: Analytics event was not sent. Handler not found');
|
||||
return;
|
||||
}
|
||||
|
||||
const delivered = (handler as AnalyticsHandler)(evt);
|
||||
|
||||
@@ -14,7 +14,7 @@ const DEFAULT_DATA : PersistedAppData = {
|
||||
cookieDisclaimerHidden: false
|
||||
}
|
||||
|
||||
export default {
|
||||
const appStateStore = {
|
||||
getPersistedData() : PersistedAppData {
|
||||
var json = window.localStorage.getItem(storeKey);
|
||||
if(!json) {
|
||||
@@ -26,7 +26,7 @@ export default {
|
||||
}
|
||||
catch(ex) {
|
||||
console.error('Failed to parse AppState json. Json Value: \n' + json, ex);
|
||||
return DEFAULT_DATA;;
|
||||
return DEFAULT_DATA;
|
||||
}
|
||||
},
|
||||
|
||||
@@ -37,4 +37,6 @@ export default {
|
||||
persistData(appState: AppState) {
|
||||
localStorage.setItem(storeKey, JSON.stringify(appState.getPersistData()));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export default appStateStore;
|
||||
@@ -36,19 +36,19 @@ export class CmdShell {
|
||||
|
||||
log.debug(`Executing command: ${rawInput}`);
|
||||
|
||||
ops = ops || Object.assign({}, DEFUALT_COMMAND_OPTIONS);
|
||||
const resolvedOps = ops ?? Object.assign({}, DEFUALT_COMMAND_OPTIONS);
|
||||
|
||||
var input = rawInput.trim().toLowerCase();
|
||||
var handler = this.findHandler(input);
|
||||
const input = rawInput.trim().toLowerCase();
|
||||
const handler = this.findHandler(input);
|
||||
|
||||
if(handler != null) {
|
||||
if(handler !== null) {
|
||||
// if(this.debugMode) {
|
||||
// this.invokeHandler(input, handler, ops);
|
||||
// return
|
||||
// }
|
||||
|
||||
try {
|
||||
this.invokeHandler(input, handler, ops);
|
||||
this.invokeHandler(input, handler, resolvedOps);
|
||||
} catch (e) {
|
||||
this.handleError(input, e as Error);
|
||||
}
|
||||
@@ -64,8 +64,8 @@ export class CmdShell {
|
||||
}
|
||||
|
||||
command (cmd : string | object, handler? : any) {
|
||||
var h = this.createHandler(cmd, handler);
|
||||
if(h == null){
|
||||
const h = this.createHandler(cmd, handler);
|
||||
if(h === null){
|
||||
console.warn('unexpected set of arguments: ', JSON.stringify(arguments));
|
||||
return;
|
||||
}
|
||||
@@ -96,25 +96,27 @@ export class CmdShell {
|
||||
}
|
||||
|
||||
findHandler (input: string) : ICommandHandler | null {
|
||||
return this.handlers.filter(h => h.canHandle(input))[0];
|
||||
return this.handlers.find(h => h.canHandle(input)) ?? null;
|
||||
};
|
||||
|
||||
invokeHandler (input : string, handler : ICommandHandler, options: CommandOptions) {
|
||||
|
||||
var cmdResult = handler.handle({ input: input, options });
|
||||
if(cmdResult != null) {
|
||||
const cmdResult = handler.handle({ input: input, options });
|
||||
if(cmdResult !== null && cmdResult !== undefined) {
|
||||
log.debug(cmdResult);
|
||||
}
|
||||
};
|
||||
|
||||
handleError (input: string, err: Error) {
|
||||
|
||||
|
||||
if(this.debugMode)
|
||||
console.error(input, err);
|
||||
|
||||
if(this.errorHandler != null)
|
||||
if(this.errorHandler !== null)
|
||||
this.errorHandler(input, err);
|
||||
}
|
||||
}
|
||||
|
||||
export default new CmdShell();
|
||||
const cmdShell = new CmdShell();
|
||||
|
||||
export default cmdShell;
|
||||
@@ -3,7 +3,6 @@ import InputBox from './InputBox';
|
||||
import DisplayResultView from './DisplayResultView';
|
||||
import AppState, { CommandResultView } from '../AppState';
|
||||
import cmd from '../cmd';
|
||||
import log from 'loglevel';
|
||||
import DebugIndicators from './DebugIndicators';
|
||||
import hash from '../../core/hash';
|
||||
import TopLinks from './TopLinks';
|
||||
@@ -49,10 +48,10 @@ export default class AppRoot extends React.Component<AppRootProps, AppRootState>
|
||||
|
||||
render() {
|
||||
|
||||
const enableNewUi = this.props.appState.env != 'prod' || true;
|
||||
const enableNewUi = this.props.appState.env !== 'prod' || true;
|
||||
const newUi = enableNewUi ? 'new-ui' : '';
|
||||
const settingsCss = "settings-button" + (this.props.appState.showSettings ? '' : ' soft');
|
||||
|
||||
|
||||
return <div className={`app-root ${this.state.uiTheme} ${newUi}`}>
|
||||
<DebugIndicators appState={this.props.appState} />
|
||||
<div className="header">
|
||||
@@ -64,7 +63,9 @@ export default class AppRoot extends React.Component<AppRootProps, AppRootState>
|
||||
<div className="expressionInput-container">
|
||||
<InputBox onCommandEntered={(input) => cmd.execute(input)} />
|
||||
|
||||
<button className={settingsCss} title='Toggle Settings'><FontAwesomeIcon icon={faGear} onClick={() => this.props.appState.toggleShowSettings()} /></button>
|
||||
<button className={settingsCss} title='Toggle Settings' type="button" onClick={() => this.props.appState.toggleShowSettings()}>
|
||||
<FontAwesomeIcon icon={faGear} />
|
||||
</button>
|
||||
|
||||
</div>
|
||||
{this.props.appState.showSettings ? <SettingsPane appState={this.props.appState} /> : null}
|
||||
|
||||
@@ -7,7 +7,7 @@ function DebugIndicators(props: {appState: AppState}) {
|
||||
const list = [];
|
||||
const state = props.appState;
|
||||
|
||||
if(props.appState.env != 'prod') {
|
||||
if(props.appState.env !== 'prod') {
|
||||
list.push(state.env);
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@ function DebugIndicators(props: {appState: AppState}) {
|
||||
list.push("notrack");
|
||||
}
|
||||
|
||||
if(list.length == 0)
|
||||
if(list.length === 0)
|
||||
return null;
|
||||
|
||||
return <div className="debug-indicators">
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { faTrashAlt, faHashtag, faLink, faCheck } from '@fortawesome/free-solid-svg-icons';
|
||||
import { faTrashAlt, faLink, faCheck } from '@fortawesome/free-solid-svg-icons';
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||
import React, { useState } from 'react';
|
||||
import AppState from '../AppState';
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||
import React, { useState} from 'react';
|
||||
import { faClipboard, faCoffee} from "@fortawesome/free-solid-svg-icons";
|
||||
import { faCoffee} from "@fortawesome/free-solid-svg-icons";
|
||||
import "./DonateResultView.css";
|
||||
import { sendAnalyticsEvent } from '../analytics';
|
||||
import { faPaypal } from '@fortawesome/free-brands-svg-icons';
|
||||
|
||||
function DonateResultView() {
|
||||
|
||||
const copyCss = navigator.clipboard != null ? "" : "hidden";
|
||||
const copyCss = navigator.clipboard !== null && navigator.clipboard !== undefined ? "" : "hidden";
|
||||
const [state, setState] = useState('default');
|
||||
const copiedCss = state == "copied" ? "" : "hidden";
|
||||
const copiedCss = state === "copied" ? "" : "hidden";
|
||||
|
||||
const addr = "bc1qyv08z29776uwdwy2m0c77gpgpupzr78jpcnraq";
|
||||
|
||||
@@ -19,13 +19,13 @@ function DonateResultView() {
|
||||
<div className='section'>
|
||||
<h3>buymeacoffee.com</h3>
|
||||
<p>
|
||||
<a className='button button-large' href='https://bmc.link/boryslevytB' onClick={() => onBuyMeCoffe()} target='_blank'>
|
||||
<a className='button button-large' href='https://bmc.link/boryslevytB' onClick={() => onBuyMeCoffe()} target='_blank' rel="noreferrer">
|
||||
<FontAwesomeIcon icon={faCoffee} size='lg' /> Buy Me a Coffee
|
||||
</a>
|
||||
</p>
|
||||
<h3>PayPal</h3>
|
||||
<p>
|
||||
<a className='paypal-button button button-large' href='https://www.paypal.com/donate/?hosted_button_id=3GREJYC4T5AJ8' target='_blank'>
|
||||
<a className='paypal-button button button-large' href='https://www.paypal.com/donate/?hosted_button_id=3GREJYC4T5AJ8' target='_blank' rel="noreferrer">
|
||||
<FontAwesomeIcon icon={faPaypal} size='lg' />
|
||||
Donate via PayPal
|
||||
</a>
|
||||
@@ -35,14 +35,16 @@ function DonateResultView() {
|
||||
<div className='section'>
|
||||
<h3>Bitcoin</h3>
|
||||
<span>BTC Address:</span> <strong>{addr}</strong>
|
||||
<button onClick={() => copy()} title="Copy this address into the Cliboard" className={`button copy-button ${copyCss}`}>
|
||||
<button type="button" onClick={() => copy()} title="Copy this address into the Cliboard" className={`button copy-button ${copyCss}`}>
|
||||
Copy
|
||||
</button> <span className={`soft ${copiedCss}`}>copied</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
function copy() {
|
||||
navigator.clipboard.writeText(addr);
|
||||
if (navigator.clipboard) {
|
||||
navigator.clipboard.writeText(addr);
|
||||
}
|
||||
setState('copied');
|
||||
setTimeout(() => setState('default'), 3000);
|
||||
sendAnalyticsEvent({eventCategory: "Donation", eventAction: "CopyBTCAddressCopyClicked"})
|
||||
|
||||
@@ -3,7 +3,7 @@ import CommandLink from '../../core/components/CommandLink';
|
||||
import './HelpResultView.css';
|
||||
import { INT32_MAX_VALUE, INT32_MIN_VALUE } from '../../core/const';
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||
import { faCircleExclamation, faWarning } from '@fortawesome/free-solid-svg-icons';
|
||||
import { faCircleExclamation } from '@fortawesome/free-solid-svg-icons';
|
||||
|
||||
function HelpResultView() {
|
||||
|
||||
@@ -59,7 +59,7 @@ function HelpResultView() {
|
||||
<li><code>>>></code> — zero-fill right shift</li>
|
||||
</ul>
|
||||
<div className='important-note'>
|
||||
<FontAwesomeIcon icon={faCircleExclamation} size='lg'/> <a target='_blank' href='https://en.cppreference.com/w/c/language/operator_precedence'>Operator precedence</a> is IGNORED. Operators are executed <strong>left-to-right</strong>.
|
||||
<FontAwesomeIcon icon={faCircleExclamation} size='lg'/> <a target='_blank' href='https://en.cppreference.com/w/c/language/operator_precedence' rel="noreferrer">Operator precedence</a> is IGNORED. Operators are executed <strong>left-to-right</strong>.
|
||||
</div>
|
||||
</div>
|
||||
<div className="section soft-border">
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import React from 'react';
|
||||
import log from 'loglevel';
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||
import { faAngleRight } from '@fortawesome/free-solid-svg-icons';
|
||||
|
||||
@@ -21,7 +20,7 @@ export default class InputBox extends React.Component<IInputBoxProps> {
|
||||
}
|
||||
|
||||
componentDidMount(){
|
||||
if(this.nameInput != null)
|
||||
if(this.nameInput !== null)
|
||||
this.nameInput.focus();
|
||||
}
|
||||
|
||||
@@ -40,7 +39,7 @@ export default class InputBox extends React.Component<IInputBoxProps> {
|
||||
|
||||
onKeyUp(e: any) {
|
||||
var input = e.target;
|
||||
if (e.keyCode != 13 || input.value.trim().length == 0) {
|
||||
if (e.keyCode !== 13 || input.value.trim().length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -54,7 +53,7 @@ export default class InputBox extends React.Component<IInputBoxProps> {
|
||||
|
||||
onKeyDown(args: any) {
|
||||
|
||||
if(args.keyCode == 38) {
|
||||
if(args.keyCode === 38) {
|
||||
var newIndex = this.historyIndex+1;
|
||||
|
||||
if (this.history.length > newIndex) { // up
|
||||
@@ -66,7 +65,7 @@ export default class InputBox extends React.Component<IInputBoxProps> {
|
||||
return;
|
||||
}
|
||||
|
||||
if(args.keyCode == 40) {
|
||||
if(args.keyCode === 40) {
|
||||
if(this.historyIndex > 0) { // down
|
||||
args.target.value = this.history[--this.historyIndex];
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||
import './SettingsPane.css';
|
||||
import { faGear, faToggleOff, faToggleOn } from '@fortawesome/free-solid-svg-icons';
|
||||
import { faToggleOff, faToggleOn } from '@fortawesome/free-solid-svg-icons';
|
||||
import AppState from '../AppState';
|
||||
|
||||
type SettingsPaneProps = {
|
||||
@@ -15,7 +15,7 @@ function SettingsPane(props : SettingsPaneProps) {
|
||||
<div className="inner solid-border">
|
||||
<h3>Settings</h3>
|
||||
<div className='setting'>
|
||||
<button onClick={() => appState.toggleEmphasizeBytes()}>
|
||||
<button type="button" onClick={() => appState.toggleEmphasizeBytes()}>
|
||||
<FontAwesomeIcon size='xl' icon={appState.emphasizeBytes ? faToggleOn : faToggleOff} /> Emphasize Bytes
|
||||
</button>
|
||||
<p className='description'>
|
||||
@@ -25,7 +25,7 @@ function SettingsPane(props : SettingsPaneProps) {
|
||||
</p>
|
||||
</div>
|
||||
<div className='setting'>
|
||||
<button onClick={() => appState.toggleDimExtrBits()}>
|
||||
<button type="button" onClick={() => appState.toggleDimExtrBits()}>
|
||||
<FontAwesomeIcon size='xl' icon={appState.dimExtraBits ? faToggleOn : faToggleOff} /> Dim Extra Bits
|
||||
</button>
|
||||
<p className='description'>
|
||||
@@ -35,7 +35,7 @@ function SettingsPane(props : SettingsPaneProps) {
|
||||
</p>
|
||||
</div>
|
||||
<div className='setting'>
|
||||
<button onClick={() => appState.toggleAnnotateTypes()}>
|
||||
<button type="button" onClick={() => appState.toggleAnnotateTypes()}>
|
||||
<FontAwesomeIcon size='xl' icon={appState.annotateTypes ? faToggleOn : faToggleOff} /> Annotate Data Types
|
||||
</button>
|
||||
<p className='description'>
|
||||
|
||||
@@ -9,12 +9,12 @@ function TopLinks() {
|
||||
|
||||
return <ul className="top-links">
|
||||
<li>
|
||||
<button onClick={onDonate} className='link-button'>
|
||||
<button type="button" onClick={onDonate} className='link-button'>
|
||||
<FontAwesomeIcon className='icon' icon={faDonate} size="lg" /><span className='link-text'>donate</span>
|
||||
</button>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://github.com/BorisLevitskiy/BitwiseCmd" target='_blank'><FontAwesomeIcon className="icon" icon={faGithub} size="lg" /><span className="link-text">github</span></a>
|
||||
<a href="https://github.com/BorisLevitskiy/BitwiseCmd" target='_blank' rel="noreferrer"><FontAwesomeIcon className="icon" icon={faGithub} size="lg" /><span className="link-text">github</span></a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="mailto:bitwisecmd@gmail.com?subject=Feedback"><FontAwesomeIcon className="icon" icon={faEnvelope} size="lg" /><span className="link-text">idea or feedback</span></a>
|
||||
|
||||
@@ -40,7 +40,7 @@ function getStartupCommands(appState : AppState) : string[] {
|
||||
|
||||
var startupCommands = loadStoredCommands();
|
||||
|
||||
if(startupCommands.length == 0)
|
||||
if(startupCommands.length === 0)
|
||||
startupCommands = DEFAULT_COMMANDS;
|
||||
|
||||
if(appState.wasOldVersion) {
|
||||
@@ -49,7 +49,7 @@ function getStartupCommands(appState : AppState) : string[] {
|
||||
|
||||
if(hashArgs.length > 0) {
|
||||
|
||||
if(hashArgs.indexOf('empty')==-1)
|
||||
if(hashArgs.indexOf('empty') === -1)
|
||||
startupCommands = hashArgs;
|
||||
}
|
||||
|
||||
@@ -60,11 +60,11 @@ function getStartupCommands(appState : AppState) : string[] {
|
||||
|
||||
function loadStoredCommands() : string[] {
|
||||
const json = localStorage.getItem(STARTUP_COMMAND_KEY);
|
||||
return json != null ? [json] : [];
|
||||
return json !== null && json !== undefined ? [json] : [];
|
||||
}
|
||||
|
||||
function setupLogger(env: Env) {
|
||||
if(env != 'prod'){
|
||||
if(env !== 'prod'){
|
||||
log.setLevel("debug");
|
||||
log.debug(`Log level is set to debug. Env: ${env}`)
|
||||
} else {
|
||||
|
||||
Reference in New Issue
Block a user