mirror of
https://github.com/BorysLevytskyi/BitwiseCmd.git
synced 2025-12-10 06:52:05 +01:00
Migrate to typescript (#14)
* Move BitwiseCmd to typescript * Add serve-build http command * Add Env type * Add CNAME and sitemap.xml files * Add files via upload
This commit is contained in:
30
.gitignore
vendored
30
.gitignore
vendored
@@ -1,9 +1,23 @@
|
||||
.idea/
|
||||
node_modules
|
||||
build
|
||||
BitwiseCmdPages/
|
||||
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
|
||||
|
||||
# dependencies
|
||||
/node_modules
|
||||
/.pnp
|
||||
.pnp.js
|
||||
|
||||
# testing
|
||||
/coverage
|
||||
|
||||
# production
|
||||
/build
|
||||
|
||||
# misc
|
||||
.DS_Store
|
||||
bundle.js
|
||||
bundle.js.map
|
||||
npm-debug.log
|
||||
react/
|
||||
.env.local
|
||||
.env.development.local
|
||||
.env.test.local
|
||||
.env.production.local
|
||||
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
|
||||
46
README.md
46
README.md
@@ -1,4 +1,44 @@
|
||||
# BitwiseCmd
|
||||
[Bitwise Calculator Online](http://bitwisecmd.com/)
|
||||
This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app).
|
||||
|
||||
Web App that helps better understand how bitwise operations are performed by displaying bytes in a way you can actually see what is going on there during AND, OR, XOR or shift operations.
|
||||
## Available Scripts
|
||||
|
||||
In the project directory, you can run:
|
||||
|
||||
### `npm start`
|
||||
|
||||
Runs the app in the development mode.<br>
|
||||
Open [http://localhost:3000](http://localhost:3000) to view it in the browser.
|
||||
|
||||
The page will reload if you make edits.<br>
|
||||
You will also see any lint errors in the console.
|
||||
|
||||
### `npm test`
|
||||
|
||||
Launches the test runner in the interactive watch mode.<br>
|
||||
See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information.
|
||||
|
||||
### `npm run build`
|
||||
|
||||
Builds the app for production to the `build` folder.<br>
|
||||
It correctly bundles React in production mode and optimizes the build for the best performance.
|
||||
|
||||
The build is minified and the filenames include the hashes.<br>
|
||||
Your app is ready to be deployed!
|
||||
|
||||
See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information.
|
||||
|
||||
### `npm run eject`
|
||||
|
||||
**Note: this is a one-way operation. Once you `eject`, you can’t go back!**
|
||||
|
||||
If you aren’t satisfied with the build tool and configuration choices, you can `eject` at any time. This command will remove the single build dependency from your project.
|
||||
|
||||
Instead, it will copy all the configuration files and the transitive dependencies (Webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except `eject` will still work, but they will point to the copied scripts so you can tweak them. At this point you’re on your own.
|
||||
|
||||
You don’t have to ever use `eject`. The curated feature set is suitable for small and middle deployments, and you shouldn’t feel obligated to use this feature. However we understand that this tool wouldn’t be useful if you couldn’t customize it when you are ready for it.
|
||||
|
||||
## Learn More
|
||||
|
||||
You can learn more in the [Create React App documentation](https://facebook.github.io/create-react-app/docs/getting-started).
|
||||
|
||||
To learn React, check out the [React documentation](https://reactjs.org/).
|
||||
|
||||
18
Setup.md
18
Setup.md
@@ -1,18 +0,0 @@
|
||||
|
||||
# Build Process
|
||||
* Install node.js (reboot required)
|
||||
* Install grunt `npm install -g grunt`
|
||||
* Install grunt cli `npm install -g grunt-cli`
|
||||
* In project dir - install all modules `npm install`
|
||||
|
||||
# End To End Tests
|
||||
* Install java (reboot required)
|
||||
* Install protractor `npm install -g protractor`
|
||||
* Update webdriver-manager `webdriver-manager update --standalone`
|
||||
|
||||
# Run End To End Tests
|
||||
* Start webdirver server `webdriver-manager update`
|
||||
* Run tests: `e2e.bat` or just `e2e`
|
||||
|
||||
|
||||
|
||||
@@ -1,40 +0,0 @@
|
||||
module.exports = function (config) {
|
||||
config.set({
|
||||
browsers: [ 'Chrome' ],
|
||||
// karma only needs to know about the test bundle
|
||||
files: [
|
||||
'./tests/unit/**/*.js'
|
||||
],
|
||||
frameworks: [ 'jasmine' ],
|
||||
plugins: [
|
||||
'karma-chrome-launcher',
|
||||
'karma-jasmine',
|
||||
'karma-webpack',
|
||||
],
|
||||
// run the bundle through the webpack and sourcemap plugins
|
||||
preprocessors: {
|
||||
'./tests/unit/**/*.js': [ 'webpack']
|
||||
},
|
||||
reporters: [ 'dots' ],
|
||||
singleRun: true,
|
||||
// webpack config object
|
||||
webpack: {
|
||||
module: {
|
||||
loaders: [
|
||||
{
|
||||
exclude: /node_modules/,
|
||||
loader: 'babel-loader',
|
||||
test: /\.jsx?$/,
|
||||
query: {
|
||||
presets: [require.resolve('babel-preset-es2015'), require.resolve('babel-preset-react')],
|
||||
plugins: [require.resolve('babel-plugin-transform-class-properties')]
|
||||
},
|
||||
}
|
||||
],
|
||||
}
|
||||
},
|
||||
webpackMiddleware: {
|
||||
noInfo: true,
|
||||
}
|
||||
});
|
||||
};
|
||||
13312
package-lock.json
generated
Normal file
13312
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
100
package.json
100
package.json
@@ -1,60 +1,48 @@
|
||||
{
|
||||
"name": "BitwiseCmd",
|
||||
"version": "1.0.0",
|
||||
"description": "Bitwise Operations Console",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"build": "webpack -p && rm -rf ./BitwiseCmdPages/* && cp ./src/index.html ./BitwiseCmdPages && cp ./src/*.js ./BitwiseCmdPages && cp ./src/bundle.js.map ./BitwiseCmdPages && cp -r ./src/css ./BitwiseCmdPages && cp -r ./src/img ./BitwiseCmdPages && cp -r ./src/css ./BitwiseCmdPages",
|
||||
"stage": "npm run build && cp -r ./BitwiseCmdPages/* ../BitwiseCmdPages",
|
||||
"stage:react": "npm run build && cp -r ./BitwiseCmdPages/* ../BitwiseCmdPages/react",
|
||||
"serve": "webpack-dev-server --content-base ./src",
|
||||
"e2e": "protractor ./tests/e2e.chrome.js --params.appUrl='http://localhost:8080/#clear'",
|
||||
"e2e:stage": "protractor ./tests/e2e.chrome.js --params.appUrl='http://localhost:3000/#clear'",
|
||||
"e2e:prod:old": "protractor ./tests/e2e.chrome.js --params.appUrl='http://bitwisecmd.com/old/#clear||-notrack'",
|
||||
"e2e:prod": "protractor ./tests/e2e.chrome.js --params.appUrl='http://bitwisecmd.com//#clear||-notrack'",
|
||||
"test": "karma start"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/BorysLevytskyi/BitwiseCmd.git"
|
||||
},
|
||||
"author": "Borys Levytskyi",
|
||||
"license": "MIT",
|
||||
"bugs": {
|
||||
"url": "https://github.com/BorysLevytskyi/BitwiseCmd/issues"
|
||||
},
|
||||
"homepage": "https://github.com/BorysLevytskyi/BitwiseCmd",
|
||||
"devDependencies": {
|
||||
"babel-cli": "^6.18.0",
|
||||
"babel-plugin-syntax-jsx": "^6.18.0",
|
||||
"babel-plugin-transform-class-properties": "^6.19.0",
|
||||
"babel-plugin-transform-react-jsx": "^6.8.0",
|
||||
"babel-preset-es2015": "^6.18.0",
|
||||
"babel-preset-react": "^6.16.0",
|
||||
"grunt-contrib-clean": "latest",
|
||||
"grunt-contrib-copy": "latest",
|
||||
"grunt-contrib-cssmin": "latest",
|
||||
"grunt-contrib-uglify": "latest",
|
||||
"grunt-processhtml": "latest",
|
||||
"jasmine": "latest",
|
||||
"karma": "latest",
|
||||
"karma-jasmine": "latest",
|
||||
"karma-webpack": "latest",
|
||||
"source-map-loader": "^0.1.5",
|
||||
"ts-loader": "^1.0.0"
|
||||
},
|
||||
"name": "bitwisecmd",
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@types/react": "^0.14.44",
|
||||
"@types/react-dom": "^0.14.18",
|
||||
"babel-core": "^6.18.2",
|
||||
"babel-loader": "^6.2.8",
|
||||
"babel-plugin-transform-class-properties": "^6.19.0",
|
||||
"babel-preset-es2015": "^6.18.0",
|
||||
"babel-preset-react": "^6.16.0",
|
||||
"body-parser": "^1.15.2",
|
||||
"loglevel": "^1.4.1",
|
||||
"react": "^15.4.0",
|
||||
"react-dom": "^15.4.0",
|
||||
"uuid": "^3.0.1"
|
||||
"@types/enzyme": "^3.9.4",
|
||||
"@types/jest": "^24.0.15",
|
||||
"@types/node": "^12.0.10",
|
||||
"@types/react": "^16.8.22",
|
||||
"@types/react-dom": "^16.8.4",
|
||||
"@types/uuid": "^3.4.4",
|
||||
"enzyme": "^3.10.0",
|
||||
"enzyme-adapter-react-16": "^1.14.0",
|
||||
"react": "^16.8.6",
|
||||
"react-dom": "^16.8.6",
|
||||
"react-scripts": "3.0.1",
|
||||
"react-test-renderer": "^16.8.6",
|
||||
"typescript": "^3.5.2",
|
||||
"uuid": "^3.3.2"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "react-scripts start",
|
||||
"build": "react-scripts build",
|
||||
"test": "react-scripts test",
|
||||
"eject": "react-scripts eject",
|
||||
"serve-build": "npx http-server ./build -p 3030"
|
||||
},
|
||||
"eslintConfig": {
|
||||
"extends": "react-app"
|
||||
},
|
||||
"browserslist": {
|
||||
"production": [
|
||||
">0.2%",
|
||||
"not dead",
|
||||
"not op_mini all"
|
||||
],
|
||||
"development": [
|
||||
"last 1 chrome version",
|
||||
"last 1 firefox version",
|
||||
"last 1 safari version"
|
||||
]
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/enzyme-adapter-react-16": "^1.0.5",
|
||||
"http-server": "^0.12.3",
|
||||
"wdio-jasmine-framework": "^0.3.8"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,14 +1,15 @@
|
||||
(function() {
|
||||
|
||||
var disableAnalytics = localStorage.getItem('trackAnalytics') === 'false' || window.location.hash.indexOf('-notrack') > -1
|
||||
var key = 'TrackAnalytics';
|
||||
var disableAnalytics = localStorage.getItem(key) === 'false' || window.location.hash.indexOf('-notrack') > -1
|
||||
|
||||
if(disableAnalytics) {
|
||||
localStorage.setItem("trackAnalytics", "false");
|
||||
localStorage.setItem(key, "false");
|
||||
console.log('Analytics tracking disabled.');
|
||||
return;
|
||||
}
|
||||
|
||||
if(window.location.host != 'bitwisecmd.com') {
|
||||
if(window.location.host !== 'bitwisecmd.com') {
|
||||
console.log("Analytics not tracked. Non-prod host")
|
||||
return;
|
||||
}
|
||||
BIN
public/favicon.ico
Normal file
BIN
public/favicon.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.1 KiB |
31
public/index.html
Normal file
31
public/index.html
Normal file
@@ -0,0 +1,31 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<meta name="theme-color" content="#000000" />
|
||||
|
||||
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
|
||||
<title>BitwiseCmd</title>
|
||||
</head>
|
||||
<body>
|
||||
<noscript>You need to enable JavaScript to run this app.</noscript>
|
||||
<div id="root" style="height: 100%"></div>
|
||||
|
||||
<script type="text/javascript" src="analytics.js"></script>
|
||||
|
||||
<div id="fb-root"></div>
|
||||
<script>(function(d, s, id) {
|
||||
var js, fjs = d.getElementsByTagName(s)[0];
|
||||
if (d.getElementById(id)) return;
|
||||
js = d.createElement(s); js.id = id;
|
||||
js.src = "//connect.facebook.net/en_US/sdk.js#xfbml=1&version=v2.5";
|
||||
fjs.parentNode.insertBefore(js, fjs);
|
||||
}(document, 'script', 'facebook-jssdk'));</script>
|
||||
|
||||
<div class="social-container">
|
||||
<div class="fb-like" data-href="http://bitwisecmd.com" data-layout="box_count" data-action="like" data-show-faces="false" data-share="true"></div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
15
public/manifest.json
Normal file
15
public/manifest.json
Normal file
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"short_name": "BitwiseCmd",
|
||||
"name": "BitwiseCmd",
|
||||
"icons": [
|
||||
{
|
||||
"src": "favicon.ico",
|
||||
"sizes": "64x64 32x32 24x24 16x16",
|
||||
"type": "image/x-icon"
|
||||
}
|
||||
],
|
||||
"start_url": ".",
|
||||
"display": "standalone",
|
||||
"theme_color": "#000000",
|
||||
"background_color": "#ffffff"
|
||||
}
|
||||
13
public/sitemap.xml
Normal file
13
public/sitemap.xml
Normal file
@@ -0,0 +1,13 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<urlset
|
||||
xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://www.sitemaps.org/schemas/sitemap/0.9
|
||||
http://www.sitemaps.org/schemas/sitemap/0.9/sitemap.xsd">
|
||||
|
||||
<url>
|
||||
<loc>http://bitwisecmd.com/</loc>
|
||||
<lastmod>2015-04-08T11:00:53+00:00</lastmod>
|
||||
<changefreq>weekly</changefreq>
|
||||
</url>
|
||||
</urlset>
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 196 B |
@@ -1,34 +1,53 @@
|
||||
import React from 'react';
|
||||
import InputBox from './InputBox';
|
||||
import DisplayResultView from './DisplayResultView';
|
||||
import InputBox from './components/InputBox';
|
||||
import DisplayResultView from './components/DisplayResultView';
|
||||
import AppState from './core/AppState';
|
||||
import cmd from './core/cmd';
|
||||
import CommandResult from './models/CommandResult';
|
||||
import log from 'loglevel';
|
||||
import Indicators from './components/Indicators';
|
||||
|
||||
type AppRootProps = {
|
||||
appState: AppState,
|
||||
};
|
||||
|
||||
type AppRootState = {
|
||||
uiTheme: string,
|
||||
emphasizeBytes: boolean,
|
||||
commandResults: CommandResult[]
|
||||
}
|
||||
|
||||
export default class AppRoot extends React.Component<AppRootProps, AppRootState> {
|
||||
|
||||
export default class AppRoot extends React.Component {
|
||||
componentWillMount() {
|
||||
this.refresh();
|
||||
this.props.appState.onChange(() => this.refresh());
|
||||
}
|
||||
|
||||
refresh() {
|
||||
this.setState(this.props.appState);
|
||||
}
|
||||
|
||||
getIndicator(value) {
|
||||
return value === true ? 'on' : 'off';
|
||||
getIndicator(value : boolean) {
|
||||
return value ? 'on' : 'off';
|
||||
}
|
||||
|
||||
getResultViews() {
|
||||
getResultViews() : JSX.Element[] {
|
||||
log.debug('getting result views')
|
||||
var results = this.state.commandResults.map((r, i) => <DisplayResultView key={i} content={r} input={r.input} inputHash={r.inputHash} appState={this.props.appState} />);
|
||||
return results;
|
||||
}
|
||||
|
||||
toggleEmphasizeBytes() {
|
||||
console.log(this.props.appState);
|
||||
this.props.appState.toggleEmphasizeBytes();
|
||||
}
|
||||
|
||||
render() {
|
||||
return <div className={`app-root ${this.state.uiTheme}`}>
|
||||
<Indicators appState={this.props.appState} />
|
||||
<div className="header">
|
||||
<h1>Bitwise<span className="header-cmd">Cmd</span></h1>
|
||||
<h1>Bitwise<span className="header-cmd">Cmd</span>
|
||||
</h1>
|
||||
<ul className="top-links">
|
||||
<li>
|
||||
<a href="https://github.com/BorisLevitskiy/BitwiseCmd"><i className="icon github"> </i><span className="link-text">Project on GitHub</span></a>
|
||||
@@ -43,7 +62,7 @@ export default class AppRoot extends React.Component {
|
||||
</div>
|
||||
|
||||
<div className="expressionInput-container">
|
||||
<InputBox />
|
||||
<InputBox onCommandEntered={(input) => cmd.execute(input)} />
|
||||
|
||||
<span className="configPnl">
|
||||
<span id="emphasizeBytes" data-cmd="em" className={"indicator " + this.getIndicator(this.state.emphasizeBytes)} title="Toggle Emphasize Bytes" onClick={e => this.toggleEmphasizeBytes()}>[em]</span>
|
||||
@@ -1,51 +0,0 @@
|
||||
export default class AppState {
|
||||
constructor(persistData) {
|
||||
this.emphasizeBytes = persistData.emphasizeBytes || true;
|
||||
this.commandResults = [];
|
||||
this.handlers = [];
|
||||
this.uiTheme = persistData.uiTheme || 'midnight';
|
||||
this.debugMode = false;
|
||||
|
||||
this.version = 4;
|
||||
this.persistedVersion = persistData.version || 0.1;
|
||||
this.wasOldVersion = persistData.version && this.version > this.persistedVersion;
|
||||
}
|
||||
|
||||
addCommandResult(result) {
|
||||
this.commandResults.unshift(result);
|
||||
this.triggerChanged();
|
||||
}
|
||||
|
||||
clearCommandResults() {
|
||||
this.commandResults = [];
|
||||
this.triggerChanged();
|
||||
}
|
||||
|
||||
toggleEmphasizeBytes() {
|
||||
this.emphasizeBytes = !this.emphasizeBytes;
|
||||
this.triggerChanged();
|
||||
}
|
||||
|
||||
onChange(handler) {
|
||||
this.handlers.push(handler);
|
||||
}
|
||||
|
||||
triggerChanged() {
|
||||
for(var h of this.handlers) {
|
||||
h();
|
||||
}
|
||||
}
|
||||
|
||||
setUiTheme(theme) {
|
||||
this.uiTheme = theme;
|
||||
this.triggerChanged();
|
||||
}
|
||||
|
||||
getPersistData() {
|
||||
return {
|
||||
emphasizeBytes: this.emphasizeBytes,
|
||||
uiTheme: this.uiTheme,
|
||||
version: this.version
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -1,26 +0,0 @@
|
||||
import should from './should';
|
||||
|
||||
export default calc = {
|
||||
numberOfBits: function (num) {
|
||||
if(num < 0) {
|
||||
return 32;
|
||||
}
|
||||
should.bePositiveInteger(num);
|
||||
return Math.floor(Math.log(num) / Math.log(2)) + 1;
|
||||
},
|
||||
|
||||
maxNumberOfBits: function (arr) {
|
||||
|
||||
var counts = [], num;
|
||||
for (var i = 0; i < arr.length; i++) {
|
||||
num = arr[i];
|
||||
counts.push(this.numberOfBits(num));
|
||||
}
|
||||
|
||||
return Math.max.apply(null, counts);
|
||||
},
|
||||
|
||||
calcExpression: function (expr) {
|
||||
return eval(expr.expressionString);
|
||||
}
|
||||
};
|
||||
@@ -1,97 +0,0 @@
|
||||
import is from './is';
|
||||
|
||||
var handlers = [];
|
||||
|
||||
var config = {
|
||||
errorHandler: (input, err) => logError(err)
|
||||
}
|
||||
|
||||
var cmd = {
|
||||
debugMode: false,
|
||||
execute: function(rawInput) {
|
||||
var input = rawInput.trim().toLowerCase();
|
||||
var handler = findHandler(input);
|
||||
|
||||
if(handler != null) {
|
||||
if(this.debugMode) {
|
||||
invokeHandler(input, handler);
|
||||
} else {
|
||||
try {
|
||||
invokeHandler(input, handler);
|
||||
} catch (e) {
|
||||
config.errorHandler(input, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
logError(input, new Error("Unsupported expression: " + input.trim()));
|
||||
}
|
||||
},
|
||||
commands: function(catalog) {
|
||||
for(var key in catalog) {
|
||||
if(catalog.hasOwnProperty(key)) {
|
||||
this.command(key, catalog[key]);
|
||||
}
|
||||
}
|
||||
},
|
||||
command: function(cmd, handler) {
|
||||
var h = createHandler(cmd, handler);
|
||||
if(h == null){
|
||||
console.warn('unexpected set of arguments: ', Array.prototype.splice.call(arguments));
|
||||
return;
|
||||
}
|
||||
|
||||
if(!is.aFunction(h.canHandle)) {
|
||||
console.warn('handler is missing "canHandle" function. registration denied.');
|
||||
return;
|
||||
}
|
||||
|
||||
if(!is.aFunction(h.handle)) {
|
||||
console.warn('handler is missing "handle" function. registration denied.');
|
||||
return;
|
||||
}
|
||||
|
||||
handlers.push(h);
|
||||
},
|
||||
clear: function() {
|
||||
console.log('clear');
|
||||
},
|
||||
onError: function(handler) {
|
||||
config.errorHandler = handler;
|
||||
}
|
||||
};
|
||||
|
||||
function logError(err) {
|
||||
console.error(err)
|
||||
}
|
||||
|
||||
function invokeHandler (input, handler) {
|
||||
|
||||
var cmdResult = handler.handle({ input: input});
|
||||
if(cmdResult != null) {
|
||||
console.log(cmdResult);
|
||||
}
|
||||
}
|
||||
|
||||
function createHandler (cmd, handler) {
|
||||
if(is.plainObject(cmd)) {
|
||||
return cmd;
|
||||
}
|
||||
|
||||
if(is.string(cmd)) {
|
||||
return { canHandle: function (input) { return input === cmd; }, handle: handler };
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
function findHandler (input) {
|
||||
var i= 0;
|
||||
for(i;i<handlers.length; i++) {
|
||||
if(handlers[i].canHandle(input)) {
|
||||
return handlers[i];
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export default cmd;
|
||||
@@ -1,67 +0,0 @@
|
||||
import HelpResult from './models/HelpResult';
|
||||
import AboutResult from './models/AboutResult';
|
||||
import UnknownCommandResult from './models/UnknownCommandResult';
|
||||
import ExpressionResult from './models/ExpressionResult';
|
||||
import ErrorResult from './models/ErrorResult';
|
||||
import WahtsnewResult from './models/WhatsnewResult';
|
||||
import StringResult from './models/StringResult';
|
||||
import * as expression from './expression';
|
||||
import uuid from 'uuid/v4';
|
||||
|
||||
var cmdConfig = {};
|
||||
|
||||
export default {
|
||||
initialize (cmd, appState) {
|
||||
|
||||
cmd.commands({
|
||||
'help': function(c) {
|
||||
appState.addCommandResult(new HelpResult(c.input));
|
||||
},
|
||||
'clear': function() {
|
||||
appState.clearCommandResults();
|
||||
},
|
||||
'em': function() {
|
||||
appState.toggleEmphasizeBytes();
|
||||
},
|
||||
'dark': function() {
|
||||
appState.setUiTheme('dark');
|
||||
},
|
||||
'light': function () {
|
||||
appState.setUiTheme('light');
|
||||
},
|
||||
'midnight': function() {
|
||||
appState.setUiTheme('midnight');
|
||||
},
|
||||
'about': function(c) {
|
||||
appState.addCommandResult(new AboutResult(c.input));
|
||||
},
|
||||
'whatsnew': function(c) {
|
||||
appState.addCommandResult(new WahtsnewResult(c.input));
|
||||
},
|
||||
'guid': function(c) {
|
||||
appState.addCommandResult(new StringResult(c.input, uuid()))
|
||||
},
|
||||
'-notrack': function () {},
|
||||
'-debug': function() {
|
||||
console.log('Debug mode on')
|
||||
cmd.debugMode = true;
|
||||
}
|
||||
});
|
||||
|
||||
cmd.command({
|
||||
canHandle: (input) => expression.parser.canParse(input),
|
||||
handle: function(c) {
|
||||
var expr = expression.parser.parse(c.input);
|
||||
appState.addCommandResult(new ExpressionResult(c.input, expr));
|
||||
}
|
||||
})
|
||||
|
||||
// Last command handler reports that input is unknown
|
||||
cmd.command({
|
||||
canHandle: () => true,
|
||||
handle: (c) => appState.addCommandResult(new UnknownCommandResult(c.input))
|
||||
});
|
||||
|
||||
cmd.onError((input, err) => appState.addCommandResult(new ErrorResult(input, err)));
|
||||
}
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
import React from 'react';
|
||||
import cmd from '../../cmd';
|
||||
|
||||
export default class CommandLink extends React.Component {
|
||||
render() {
|
||||
return <a href="javascript:void(0)" onClick={e => cmd.execute(this.props.command || this.props.text)}>{this.props.text}</a>
|
||||
}
|
||||
}
|
||||
@@ -1,112 +0,0 @@
|
||||
import React from 'react';
|
||||
import { Operand, ListOfNumbersExpression, ExpressionOperand, MultipleOperandsExpression } from '../../expression';
|
||||
import formatter from '../../formatter';
|
||||
import BinaryStringView from './BinaryStringView';
|
||||
import BitwiseExpressionViewModel from './models/BitwiseExpressionViewModel';
|
||||
import log from 'loglevel';
|
||||
|
||||
export default class BitwiseOperationEpxressionView extends React.Component {
|
||||
constructor() {
|
||||
super();
|
||||
this.state = {};
|
||||
}
|
||||
render() {
|
||||
var rows = this.getRows();
|
||||
if(!rows) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return <table className="expression">
|
||||
<tbody>
|
||||
{rows}
|
||||
</tbody>
|
||||
</table>
|
||||
}
|
||||
|
||||
getRows() {
|
||||
const expr = this.props.expression;
|
||||
var model = null;
|
||||
|
||||
if(expr instanceof ListOfNumbersExpression) {
|
||||
model = BitwiseExpressionViewModel.buildListOfNumbers(expr, {
|
||||
emphasizeBytes: this.props.emphasizeBytes,
|
||||
allowFlipBits: true
|
||||
});
|
||||
}
|
||||
|
||||
if(expr instanceof MultipleOperandsExpression) {
|
||||
model = BitwiseExpressionViewModel.buildMultiple(expr, {
|
||||
emphasizeBytes: this.props.emphasizeBytes,
|
||||
allowFlipBits: false
|
||||
});
|
||||
}
|
||||
|
||||
log.info('Render model', model);
|
||||
|
||||
return model.items.map((itm, i) =>
|
||||
<ExpressionRow
|
||||
key={i}
|
||||
{...itm}
|
||||
emphasizeBytes={this.props.emphasizeBytes}
|
||||
maxNumberOfBits={model.maxNumberOfBits}
|
||||
onBitFlipped={() => this.onBitFlipped()} />);
|
||||
}
|
||||
|
||||
onBitFlipped() {
|
||||
console.log('bit flipped');
|
||||
this.setState({d:new Date()});
|
||||
}
|
||||
}
|
||||
|
||||
class ExpressionRow extends React.Component {
|
||||
constructor() {
|
||||
super();
|
||||
this.state = { operand: null };
|
||||
}
|
||||
render() {
|
||||
const { sign, css, maxNumberOfBits, emphasizeBytes, allowFlipBits, operand } = this.props;
|
||||
|
||||
return <tr className={css}>
|
||||
<td className="sign">{sign}</td>
|
||||
<td className="label">{this.getLabel(operand)}</td>
|
||||
<td className="bin">
|
||||
<BinaryStringView
|
||||
emphasizeBytes={emphasizeBytes}
|
||||
binaryString={formatter.padLeft(operand.apply().toBinaryString(), maxNumberOfBits, '0')}
|
||||
allowFlipBits={allowFlipBits}
|
||||
onFlipBit={idx => this.flipBit(idx)}/>
|
||||
</td>
|
||||
<td className="other">{this.getOther(operand)}</td>
|
||||
</tr>;;
|
||||
}
|
||||
|
||||
getLabel(op) {
|
||||
if(op.isExpression) {
|
||||
return op.toString();
|
||||
}
|
||||
return op.toString(op.kind == 'bin' ? 'dec' : op.kind);
|
||||
}
|
||||
|
||||
getOther(op) {
|
||||
if(op.isExpression) {
|
||||
return op.apply().toString();
|
||||
}
|
||||
return op.toString(op.getOtherKind());
|
||||
}
|
||||
|
||||
flipBit(args) {
|
||||
|
||||
const op = this.props.operand;
|
||||
const { index, binaryString } = args;
|
||||
|
||||
var arr = binaryString.split('');
|
||||
arr[index] = arr[index] == '0' ? '1' : '0';
|
||||
var bin = arr.join('');
|
||||
|
||||
var newValue = parseInt(bin, 2);
|
||||
console.log('flipped \n%s to \n%s from \n%s to \n%s', binaryString, bin, op.value, newValue);
|
||||
op.setValue(newValue);
|
||||
|
||||
this.props.onBitFlipped();
|
||||
}
|
||||
}
|
||||
@@ -1,130 +0,0 @@
|
||||
import { Operand, ExpressionOperand } from '../../../expression';
|
||||
|
||||
export default class BitwiseExpressionViewModel {
|
||||
|
||||
constructor({ emphasizeBytes = false, allowFlipBits = false } = {}) {
|
||||
this.emphasizeBytes = emphasizeBytes;
|
||||
this.items = [];
|
||||
this.maxNumberOfBits = 0;
|
||||
this.allowFlipBits = allowFlipBits === true;
|
||||
}
|
||||
|
||||
static buildListOfNumbers(expr, config) {
|
||||
var model = new BitwiseExpressionViewModel(config);
|
||||
expr.numbers.forEach(op => model.addOperand(op));
|
||||
model.maxNumberOfBits = BitwiseExpressionViewModel.getNumberOfBits(model.maxNumberOfBits, model.emphasizeBytes);
|
||||
return model;
|
||||
}
|
||||
|
||||
static buildMultiple (expr, config) {
|
||||
// console.log('build: ', expr);
|
||||
var op = expr.expressions[0],
|
||||
i = 0, l = expr.expressions.length,
|
||||
ex, m = new BitwiseExpressionViewModel(config);
|
||||
|
||||
var cur = null;
|
||||
for (;i<l;i++) {
|
||||
var ex = expr.expressions[i];
|
||||
if(ex instanceof Operand) {
|
||||
m.addOperand(ex);
|
||||
cur = ex;
|
||||
// console.log('cur is ', cur)
|
||||
continue;
|
||||
}
|
||||
|
||||
// If it a single NOT expression
|
||||
if(ex.isNotExpression) {
|
||||
m.addExpression(ex);
|
||||
var notResult = ex.apply();
|
||||
m.addExpressionResult(notResult);
|
||||
cur = notResult;
|
||||
}
|
||||
else if(ex.isShiftExpression){
|
||||
// console.log('cur is ', cur)
|
||||
cur = ex.apply(cur);
|
||||
m.addShiftExpressionResult(ex, cur);
|
||||
} else {
|
||||
|
||||
cur = ex.apply(cur);
|
||||
m.addExpression(ex);
|
||||
m.addExpressionResult(cur);
|
||||
}
|
||||
}
|
||||
|
||||
m.maxNumberOfBits = BitwiseExpressionViewModel.getNumberOfBits(m.maxNumberOfBits, m.emphasizeBytes);
|
||||
return m;
|
||||
};
|
||||
|
||||
static buildNot (expression, config) {
|
||||
|
||||
var m = new BitwiseExpressionViewModel(config);
|
||||
m.addExpression(expression);
|
||||
m.addExpressionResult(expression.apply());
|
||||
m.maxNumberOfBits = BitwiseExpressionViewModel.getNumberOfBits(m.maxNumberOfBits, m.emphasizeBytes);
|
||||
return m;
|
||||
};
|
||||
|
||||
addOperand(operand) {
|
||||
this.maxNumberOfBits = Math.max(operand.getLengthInBits(), this.maxNumberOfBits);
|
||||
this.items.push({
|
||||
sign:'',
|
||||
css: '',
|
||||
operand: operand,
|
||||
allowFlipBits: this.allowFlipBits
|
||||
});
|
||||
};
|
||||
|
||||
addExpression(expression) {
|
||||
this.maxNumberOfBits = Math.max(expression.operand.apply().getLengthInBits(), this.maxNumberOfBits);
|
||||
|
||||
this.items.push({
|
||||
sign: expression.sign,
|
||||
label: this.getLabel(expression.operand),
|
||||
operand: expression.operand,
|
||||
allowFlipBits: this.allowFlipBits
|
||||
});
|
||||
};
|
||||
|
||||
addShiftExpressionResult(expression, resultOperand) {
|
||||
this.maxNumberOfBits = Math.max(resultOperand.getLengthInBits(), this.maxNumberOfBits);
|
||||
this.items.push({
|
||||
sign: expression.sign + expression.operand.toString(),
|
||||
css: 'expression-result',
|
||||
operand: resultOperand,
|
||||
allowFlipBits: false
|
||||
});
|
||||
};
|
||||
|
||||
addExpressionResult(operand) {
|
||||
this.maxNumberOfBits = Math.max(operand.getLengthInBits(), this.maxNumberOfBits);
|
||||
this.items.push({
|
||||
sign:'=',
|
||||
css: 'expression-result',
|
||||
operand: operand,
|
||||
allowFlipBits: false
|
||||
});
|
||||
};
|
||||
|
||||
getLabel (op) {
|
||||
|
||||
if(op.kind == 'bin') {
|
||||
return op.dec;
|
||||
}
|
||||
|
||||
return op.toString();
|
||||
}
|
||||
|
||||
// TODO: move this method elsewhere. It is also used in LisOfNumbersExpressionView.js
|
||||
static getNumberOfBits = function (bits, emphasizeBytes) {
|
||||
if(emphasizeBytes && bits % 8 != 0) {
|
||||
if(bits < 8) {
|
||||
return 8;
|
||||
}
|
||||
|
||||
var n = bits - (bits % 8);
|
||||
return n + 8;
|
||||
}
|
||||
|
||||
return bits;
|
||||
};
|
||||
}
|
||||
@@ -1,132 +0,0 @@
|
||||
import Operand from './expression/Operand';
|
||||
import ExpressionOperand from './expression/ExpressionOperand'
|
||||
import ListOfNumbersExpression from './expression/ListOfNumbersExpression';
|
||||
import MultipleOperandsExpression from './expression/MultipleOperandsExpression';
|
||||
|
||||
export { default as Operand } from './expression/Operand';
|
||||
export { default as ExpressionError } from './expression/ExpressionError';
|
||||
export { default as ExpressionOperand } from './expression/ExpressionOperand';
|
||||
export { default as ListOfNumbersExpression } from './expression/ListOfNumbersExpression';
|
||||
export { default as MultipleOperandsExpression } from './expression/MultipleOperandsExpression';
|
||||
|
||||
var expression = {
|
||||
factories:[],
|
||||
canParse: function(string) {
|
||||
var trimmed = string.replace(/^\s+|\s+$/, '');
|
||||
var i = this.factories.length-1;
|
||||
for(;i>=0;i--) {
|
||||
if(this.factories[i].canCreate(trimmed) === true){
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
},
|
||||
parse: function(string) {
|
||||
var trimmed = string.replace(/^\s+|\s+$/, '');
|
||||
var i = 0, l = this.factories.length, factory;
|
||||
|
||||
for(;i<l;i++) {
|
||||
factory = this.factories[i];
|
||||
|
||||
if(factory.canCreate(trimmed) == true){
|
||||
return factory.create(trimmed);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
},
|
||||
parseOperand: function(input) {
|
||||
return Operand.parse(input);
|
||||
},
|
||||
createOperand: function(number, kind) {
|
||||
return Operand.create(number, kind);
|
||||
},
|
||||
addFactory: function(factory) {
|
||||
this.factories.push(factory);
|
||||
}
|
||||
};
|
||||
|
||||
// List of numbers
|
||||
expression.addFactory({
|
||||
regex: /^(-?(?:\d+|0x[\d,a-f]+|0b[0-1])\s?)+$/,
|
||||
canCreate: function(string) {
|
||||
return this.regex.test(string);
|
||||
},
|
||||
create: function (string) {
|
||||
var matches = this.regex.exec(string),
|
||||
numbers = [],
|
||||
input = matches.input;
|
||||
|
||||
input.split(' ').forEach(function(n){
|
||||
if(n.trim().length > 0) {
|
||||
numbers.push(Operand.parse(n.trim()));
|
||||
}
|
||||
});
|
||||
|
||||
return new ListOfNumbersExpression(input, numbers);
|
||||
}
|
||||
});
|
||||
|
||||
// Multiple operands expression
|
||||
expression.addFactory({
|
||||
fullRegex: /^((<<|>>|>>>|\||\&|\^)?(~?-?([b,x,a-f,0-9]+)))+$/,
|
||||
regex: /(<<|>>|>>>|\||\&|\^)?(~?-?(?:[b,x,a-f,0-9]+))/g,
|
||||
canCreate: function(string) {
|
||||
this.fullRegex.lastIndex = 0;
|
||||
return this.fullRegex.test(this.normalizeString(string));
|
||||
},
|
||||
create: function (string) {
|
||||
var m, operands = [],
|
||||
normalizedString = this.normalizeString(string);
|
||||
|
||||
while ((m = this.regex.exec(normalizedString)) != null) {
|
||||
operands.push(this.parseMatch(m));
|
||||
}
|
||||
|
||||
return new MultipleOperandsExpression(normalizedString, operands)
|
||||
},
|
||||
parseMatch: function (m) {
|
||||
// console.log('match');
|
||||
// console.log(m);
|
||||
var input = m[0],
|
||||
sign = m[1],
|
||||
num = m[2];
|
||||
|
||||
var op = null;
|
||||
if(num.indexOf('~') == '0') {
|
||||
op = new ExpressionOperand(input, Operand.parse(num.substring(1)), '~');
|
||||
}
|
||||
else {
|
||||
op = Operand.parse(num);
|
||||
}
|
||||
|
||||
if(sign == null) {
|
||||
return op;
|
||||
} else {
|
||||
return new ExpressionOperand(input, op, sign);
|
||||
}
|
||||
},
|
||||
normalizeString: function (string) {
|
||||
return string.replace(/\s+/g,'');
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
export var parser = expression;
|
||||
|
||||
export class Parser {
|
||||
constructor(input, pos) {
|
||||
this.input = input;
|
||||
this.pos = pos || 0;
|
||||
this.buffer = [];
|
||||
}
|
||||
|
||||
parse() {
|
||||
console.log(this.input.length);
|
||||
while(this.pos<this.input.length) {
|
||||
this.buffer.push(this.input[this.pos]);
|
||||
this.pos++;
|
||||
}
|
||||
console.log('exit');
|
||||
}
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
export default class ExpressionError extends Error {
|
||||
constructor(message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
import Operand from './Operand';
|
||||
|
||||
export default class ExpressionOperand {
|
||||
constructor(expressionString, operand, sign) {
|
||||
this.expressionString = expressionString;
|
||||
this.operand = operand;
|
||||
this.sign = sign;
|
||||
this.isExpression = true;
|
||||
this.isShiftExpression = this.sign.indexOf('<') >= 0 || this.sign.indexOf('>')>= 0;
|
||||
this.isNotExpression = this.sign == '~';
|
||||
}
|
||||
|
||||
apply(operand) {
|
||||
if (operand instanceof ExpressionOperand) {
|
||||
console.error("value shouldnt be expression", value);
|
||||
throw new Error('value shouldnt be expression');
|
||||
}
|
||||
|
||||
// console.log('operand', operand);
|
||||
|
||||
var str = '';
|
||||
if(this.sign == '~'){
|
||||
str = '~' + this.operand.apply().value;
|
||||
} else {
|
||||
str = operand.value + this.sign + this.operand.apply().value;
|
||||
}
|
||||
|
||||
// console.log('eval:' + str, this);
|
||||
const resultValue = eval(str);
|
||||
var resultOp = Operand.create(eval(str), this.operand.kind || this.operand.operand.kind);
|
||||
// console.log(resultValue, resultOp);
|
||||
|
||||
return resultOp;
|
||||
};
|
||||
|
||||
toString() {
|
||||
return this.sign + this.operand.toString();
|
||||
}
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
export default class ListOfNumbersExpression {
|
||||
constructor(expressionString, numbers) {
|
||||
this.expressionString = expressionString;
|
||||
this.numbers = numbers;
|
||||
this.maxBitsLegnth = numbers.map(n => n.lengthInBits).reduce((n , c) => n >= c ? n : c, 0);
|
||||
}
|
||||
|
||||
toString() {
|
||||
return this.numbers.map(n => n.value.toString()).join(' ');
|
||||
}
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
export default class MultipleOperandsExpression {
|
||||
constructor(expressionString, expressions) {
|
||||
this.expressionString = expressionString;
|
||||
this.expressions = expressions;
|
||||
}
|
||||
}
|
||||
@@ -1,111 +0,0 @@
|
||||
import numberParser from './numberParser';
|
||||
import ExpressionError from './ExpressionError';
|
||||
|
||||
var id = 1;
|
||||
|
||||
// Represents numeric value
|
||||
export default class Operand {
|
||||
constructor(cfg) {
|
||||
this.id = id++;
|
||||
this.value = cfg.value;
|
||||
this.kind = cfg.kind;
|
||||
this.lengthInBits = Operand.getBitLength(this.value);
|
||||
this.isExpression = false;
|
||||
}
|
||||
|
||||
getLengthInBits() {
|
||||
if(this.value < 0) {
|
||||
return 32;
|
||||
}
|
||||
return Math.floor(Math.log(this.value) / Math.log(2)) + 1;
|
||||
};
|
||||
|
||||
getOtherKind(kind) {
|
||||
switch(kind || this.kind) {
|
||||
case 'dec':
|
||||
case 'bin':
|
||||
return 'hex';
|
||||
case 'hex': return 'dec';
|
||||
default : throw new Error(kind + " kind doesn't have opposite kind")
|
||||
}
|
||||
};
|
||||
|
||||
toString(kind) {
|
||||
return Operand.toKindString(this.value, kind || this.kind);
|
||||
}
|
||||
|
||||
toOtherKindString() {
|
||||
return this.toString(this.getOtherKind());
|
||||
}
|
||||
|
||||
toDecimalString() {
|
||||
return this.toString('dec');
|
||||
}
|
||||
|
||||
toHexString() {
|
||||
return this.toString('hex');
|
||||
}
|
||||
|
||||
toBinaryString() {
|
||||
return this.toString('bin');
|
||||
}
|
||||
|
||||
setValue(value) {
|
||||
console.log('Operand:%s.setValue: %s', this.id, this.value);
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
apply() {
|
||||
return this;
|
||||
}
|
||||
|
||||
static getBitLength(num) {
|
||||
return Math.floor(Math.log(num) / Math.log(2)) + 1;
|
||||
}
|
||||
|
||||
static getBase(kind){
|
||||
switch (kind){
|
||||
case 'bin': return 2;
|
||||
case 'hex': return 16;
|
||||
case 'dec': return 10;
|
||||
}
|
||||
};
|
||||
|
||||
static create(value, kind) {
|
||||
|
||||
return new Operand({
|
||||
value: value,
|
||||
kind: kind,
|
||||
input: Operand.toKindString(value, kind),
|
||||
});
|
||||
};
|
||||
|
||||
static parse(input) {
|
||||
|
||||
var parsed = numberParser.parse(input);
|
||||
|
||||
if(!parsed) {
|
||||
throw new ExpressionError(input + " is not a valid number");
|
||||
}
|
||||
|
||||
return new Operand(parsed);
|
||||
}
|
||||
|
||||
static toKindString(value, kind) {
|
||||
switch(kind) {
|
||||
case 'hex':
|
||||
var hexVal = Math.abs(value).toString(16);
|
||||
return value >= 0 ? '0x' + hexVal : '-0x' + hexVal;
|
||||
case 'bin':
|
||||
return (value>>>0).toString(2);
|
||||
case 'dec':
|
||||
return value.toString(10);
|
||||
default:
|
||||
throw new Error("Unexpected kind: " + kind)
|
||||
}
|
||||
};
|
||||
|
||||
static toHexString (hex) {
|
||||
return hex.indexOf('-') == 0 ? '-0x' + hex.substr(1) : '0x' + hex;
|
||||
};
|
||||
}
|
||||
@@ -1,41 +0,0 @@
|
||||
var decimalRegex = /^-?\d+$/;
|
||||
var hexRegex = /^-?0x[0-9,a-f]+$/i;
|
||||
var binRegex = /^-?0b[0-1]+$/i;
|
||||
var operatorRegex = /^<<|>>|<<<|\&|\|\^|~$/;
|
||||
|
||||
var parsers = [
|
||||
{ regex: decimalRegex, radix: 10, kind: 'dec', prefix: '^$' },
|
||||
{ regex: hexRegex, radix: 16, kind: 'hex', prefix:/0x/i },
|
||||
{ regex: binRegex, radix: 2, kind: 'bin', prefix:/0b/i }];
|
||||
|
||||
function applyParser(parser, rawInput) {
|
||||
|
||||
if(!parser.regex.test(rawInput)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
var value = parseInt(rawInput.replace(parser.prefix, ''), parser.radix);
|
||||
|
||||
return {
|
||||
value: value,
|
||||
kind: parser.kind,
|
||||
input: rawInput
|
||||
}
|
||||
}
|
||||
|
||||
var parser = {
|
||||
parse: function(input) {
|
||||
return parsers.map(p => applyParser(p, input)).reduce((c, n) => c || n);
|
||||
},
|
||||
|
||||
parseOperator: function(input) {
|
||||
var m = input.match(input);
|
||||
if(m.length == 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return m[0];
|
||||
}
|
||||
}
|
||||
|
||||
export default parser;
|
||||
@@ -1,26 +0,0 @@
|
||||
export default {
|
||||
formatString: function(num, kind) {
|
||||
return num.toString(getBase(kind || "bin"));
|
||||
},
|
||||
padLeft: function (str, length, symbol) {
|
||||
var sb = Array.prototype.slice.call(str), symbol = symbol || "0";
|
||||
|
||||
if(length == null) {
|
||||
return str;
|
||||
}
|
||||
|
||||
while(length > sb.length) {
|
||||
sb.unshift(symbol);
|
||||
}
|
||||
|
||||
return sb.join('');
|
||||
}
|
||||
};
|
||||
|
||||
function getBase(kind) {
|
||||
switch (kind){
|
||||
case 'bin': return 2;
|
||||
case 'hex': return 16;
|
||||
case 'dec': return 10;
|
||||
}
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
export default {
|
||||
encodeHash: function(string) {
|
||||
return encodeURI(string.trim().replace(/\s/g,','));
|
||||
},
|
||||
decodeHash: function(hashValue) {
|
||||
return decodeURI(hashValue).replace(/^\#/, '').replace(/,/g,' ');
|
||||
},
|
||||
getArgs: function (hashValue) {
|
||||
|
||||
var decodedHash = this.decodeHash(hashValue),
|
||||
args = { commands: [] };
|
||||
|
||||
splitHashList(decodedHash).forEach(function(value) {
|
||||
args.commands.push(value);
|
||||
});
|
||||
|
||||
return Object.freeze(args);
|
||||
}
|
||||
};
|
||||
|
||||
function splitHashList(str) {
|
||||
var values = [];
|
||||
|
||||
if(str.indexOf('||')) {
|
||||
str.split('||').forEach(function (v) {
|
||||
if (v.length > 0) {
|
||||
values.push(v);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
values.push(str);
|
||||
}
|
||||
|
||||
return values;
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
export default {
|
||||
plainObject: function(obj) {
|
||||
return typeof obj == "object" && obj instanceof Object;
|
||||
},
|
||||
|
||||
aFunction: function (obj) {
|
||||
return typeof obj == "function";
|
||||
},
|
||||
|
||||
string: function (obj) {
|
||||
return typeof obj == "string";
|
||||
},
|
||||
|
||||
regex: function (obj) {
|
||||
return typeof obj == "object" && this.constructedFrom(RegExp);
|
||||
},
|
||||
|
||||
constructedFrom: function (obj, ctor) {
|
||||
return obj instanceof ctor;
|
||||
},
|
||||
|
||||
htmlElement: function(obj) {
|
||||
return obj instanceof HtmlElement;
|
||||
},
|
||||
|
||||
array: function(obj) {
|
||||
return obj instanceof Array;
|
||||
},
|
||||
|
||||
number: function(num) {
|
||||
return typeof num == "number" && !isNaN(num)
|
||||
}
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
export default class CommandResult {
|
||||
constructor(input) {
|
||||
this.input = input;
|
||||
this.inputHash = this.encodeHash(input);
|
||||
}
|
||||
|
||||
encodeHash (string) {
|
||||
return encodeURI(string.trim().replace(/\s/g,','));
|
||||
}
|
||||
}
|
||||
52
src/commands.ts
Normal file
52
src/commands.ts
Normal file
@@ -0,0 +1,52 @@
|
||||
import HelpResult from './models/HelpResult';
|
||||
import AboutResult from './models/AboutResult';
|
||||
import UnknownCommandResult from './models/UnknownCommandResult';
|
||||
import ExpressionResult from './models/ExpressionResult';
|
||||
import ErrorResult from './models/ErrorResult';
|
||||
import WahtsnewResult from './models/WhatsnewResult';
|
||||
import StringResult from './models/StringResult';
|
||||
import * as expression from './expression/expression';
|
||||
import uuid from 'uuid/v4';
|
||||
import { CommandInput, CmdShell } from './core/cmd';
|
||||
import { ExpressionInput } from './expression/expression-interfaces';
|
||||
import AppState from './core/AppState';
|
||||
|
||||
export default {
|
||||
initialize (cmd: CmdShell, appState: AppState) {
|
||||
|
||||
cmd.debugMode = appState.debugMode;
|
||||
appState.onChange(() => cmd.debugMode = appState.debugMode);
|
||||
|
||||
cmd.command("help", (c: CommandInput) => appState.addCommandResult(new HelpResult(c.input)));
|
||||
cmd.command("clear", (c: CommandInput) => appState.clearCommandResults());
|
||||
cmd.command("em", (c: CommandInput) => appState.toggleEmphasizeBytes());
|
||||
cmd.command("dark", (c: CommandInput) => appState.setUiTheme('dark'));
|
||||
cmd.command("light", (c: CommandInput) => appState.setUiTheme('light'));
|
||||
cmd.command("midnight", (c: CommandInput) => appState.setUiTheme('midnight'));
|
||||
cmd.command("about", (c: CommandInput) => appState.addCommandResult(new AboutResult(c.input)));
|
||||
cmd.command("whatsnew", (c: CommandInput) => appState.addCommandResult(new WahtsnewResult(c.input)));
|
||||
cmd.command("guid", (c: CommandInput) => appState.addCommandResult(new StringResult(c.input, uuid())));
|
||||
cmd.command("-notrack", (c: CommandInput) => {});
|
||||
cmd.command("-debug", (c: CommandInput) => {
|
||||
appState.toggleDebugMode();
|
||||
appState.addCommandResult(new StringResult(c.input, `Debug Mode: ${appState.debugMode}`))
|
||||
});
|
||||
|
||||
cmd.command({
|
||||
canHandle: (input:string) => expression.parser.canParse(input),
|
||||
handle: function(c: CommandInput) {
|
||||
var expr = expression.parser.parse(c.input);
|
||||
appState.toggleDebugMode();
|
||||
appState.addCommandResult(new ExpressionResult(c.input, expr as ExpressionInput));
|
||||
}
|
||||
})
|
||||
|
||||
// Last command handler reports that input is unknown
|
||||
cmd.command({
|
||||
canHandle: () => true,
|
||||
handle: (c: CommandInput) => appState.addCommandResult(new UnknownCommandResult(c.input))
|
||||
});
|
||||
|
||||
cmd.onError((input: string, err: Error) => appState.addCommandResult(new ErrorResult(input, err)));
|
||||
}
|
||||
}
|
||||
13
src/components/CommandLink.tsx
Normal file
13
src/components/CommandLink.tsx
Normal file
@@ -0,0 +1,13 @@
|
||||
import React from 'react';
|
||||
import cmd from '../core/cmd';
|
||||
|
||||
type CommandLinkProps = {
|
||||
command?:string;
|
||||
text:string;
|
||||
}
|
||||
|
||||
function CommandLink(props: CommandLinkProps) {
|
||||
return <a href="javascript:void(0)" onClick={e => cmd.execute(props.command || props.text)}>{props.text}</a>
|
||||
}
|
||||
|
||||
export default CommandLink;
|
||||
@@ -1,19 +1,27 @@
|
||||
import React from 'react';
|
||||
import HelpResult from '../models/HelpResult';
|
||||
import AboutResult from '../models/AboutResult';
|
||||
import UnknownCommandResult from '../models/UnknownCommandResult';
|
||||
import HelpResultView from './results/HelpResultView';
|
||||
import AboutResultView from './results/AboutResultView';
|
||||
import ExpressionResult from '../models/ExpressionResult';
|
||||
import BitwiseOperationExpressionView from './results/BitwiseOperationExpressionView';
|
||||
import BitwiseOperationExpressionView from './results/expressions/BitwiseOperationExpressionView';
|
||||
import WhatsnewResult from '../models/WhatsnewResult';
|
||||
import WhatsnewResultView from './results/WhatsnewResultView';
|
||||
import WhatsnewResultView from './results/WhatsNewResultView';
|
||||
import ErrorResult from '../models/ErrorResult';
|
||||
import StringResult from '../models/StringResult';
|
||||
|
||||
import * as expression from '../expression';
|
||||
import CommandResult from '../models/CommandResult';
|
||||
import AppState from '../core/AppState';
|
||||
|
||||
export default class DisplayResult extends React.Component {
|
||||
type DisplayResultProps = {
|
||||
content : CommandResult,
|
||||
appState: AppState,
|
||||
inputHash: string,
|
||||
input: string,
|
||||
key: number
|
||||
}
|
||||
|
||||
export default class DisplayResult extends React.Component<DisplayResultProps> {
|
||||
render() {
|
||||
|
||||
return <div className="result">
|
||||
@@ -24,9 +32,10 @@ export default class DisplayResult extends React.Component {
|
||||
</div>;
|
||||
}
|
||||
|
||||
findResultComponent(result) {
|
||||
findResultComponent(result: CommandResult) : JSX.Element {
|
||||
|
||||
if(result instanceof HelpResult) {
|
||||
return <HelpResultView content={result} />
|
||||
return <HelpResultView />
|
||||
}
|
||||
|
||||
if(result instanceof AboutResult) {
|
||||
@@ -52,7 +61,7 @@ export default class DisplayResult extends React.Component {
|
||||
}
|
||||
|
||||
return <div className="result">
|
||||
<div className="error">¯\_(ツ)_/¯ Sorry, i don′t know what <strong>{this.props.input}</strong> is</div>
|
||||
<div className="error">¯\_(ツ)_/¯ Sorry, i don′t know what <strong>{this.props.content.input}</strong> is</div>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
33
src/components/Indicators.tsx
Normal file
33
src/components/Indicators.tsx
Normal file
@@ -0,0 +1,33 @@
|
||||
import AppState from "../core/AppState";
|
||||
import React from "react";
|
||||
|
||||
type IndicatorsProps = {
|
||||
appState: AppState
|
||||
};
|
||||
|
||||
function Indicators(props: IndicatorsProps) {
|
||||
|
||||
const list = [];
|
||||
const state = props.appState;
|
||||
|
||||
if(props.appState.env != 'prod') {
|
||||
list.push(state.env);
|
||||
}
|
||||
|
||||
if(props.appState.debugMode) {
|
||||
list.push("debug");
|
||||
}
|
||||
|
||||
if(localStorage.getItem('TrackAnalytics') === 'false') {
|
||||
list.push("notrack");
|
||||
}
|
||||
|
||||
if(list.length == 0)
|
||||
return null;
|
||||
|
||||
return <div>
|
||||
{list.map(i => <span>{i} </span>)}
|
||||
</div>
|
||||
}
|
||||
|
||||
export default Indicators;
|
||||
@@ -1,15 +1,26 @@
|
||||
import React from 'react';
|
||||
import cmd from '../cmd';
|
||||
import log from 'loglevel';
|
||||
|
||||
export default class InputBox extends React.Component {
|
||||
constructor() {
|
||||
super();
|
||||
export interface IInputBoxProps
|
||||
{
|
||||
onCommandEntered: (input :string) => void;
|
||||
}
|
||||
|
||||
export default class InputBox extends React.Component<IInputBoxProps> {
|
||||
history: string[];
|
||||
historyIndex: number;
|
||||
nameInput: HTMLInputElement | null;
|
||||
|
||||
constructor(props: IInputBoxProps) {
|
||||
super(props);
|
||||
this.nameInput = null;
|
||||
this.history = [];
|
||||
this.historyIndex = -1;
|
||||
}
|
||||
|
||||
componentDidMount(){
|
||||
this.nameInput.focus();
|
||||
if(this.nameInput != null)
|
||||
this.nameInput.focus();
|
||||
}
|
||||
|
||||
render() {
|
||||
@@ -21,21 +32,21 @@ export default class InputBox extends React.Component {
|
||||
placeholder="type expression like '1>>2' or 'help' "/>;
|
||||
}
|
||||
|
||||
onKeyUp(e) {
|
||||
onKeyUp(e: any) {
|
||||
var input = e.target;
|
||||
if (e.keyCode != 13 || input.value.trim().length == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
var value = input.value;
|
||||
this.history.unshift(value);
|
||||
var commandInput = input.value;
|
||||
this.history.unshift(commandInput);
|
||||
this.historyIndex = -1;
|
||||
|
||||
input.value = '';
|
||||
cmd.execute(value);
|
||||
this.props.onCommandEntered(commandInput);
|
||||
}
|
||||
|
||||
onKeyDown(args) {
|
||||
onKeyDown(args: any) {
|
||||
|
||||
if(args.keyCode == 38) {
|
||||
var newIndex = this.historyIndex+1;
|
||||
@@ -1,11 +1,12 @@
|
||||
import React from 'react'
|
||||
|
||||
export default class AboutResultView extends React.Component {
|
||||
render() {
|
||||
return <div className="aboutTpl">
|
||||
function AboutResultView() {
|
||||
|
||||
return <div className="aboutTpl" data-result-type="help">
|
||||
<p> Created by <a href="http://boryslevytskyi.github.io/">Borys Levytskyi</a>. Please give it a like if BitwiseCmd has helped you in your work.</p>
|
||||
<p>If you have an idea, suggestion or you've spotted a bug here, please send it to <a href="mailto:bitwisecmd@gmail.com?subject=Feedback">bitwisecmd@gmail.com</a> or tweet on <a href="http://twitter.com/BitwiseCmd">@BitwiseCmd</a>. Your feedback is greatly appreciated.</p>
|
||||
<p><a href="https://github.com/BorisLevitskiy/BitwiseCmd">Project on <strong>GitHub</strong></a></p>
|
||||
</div>;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export default AboutResultView;
|
||||
@@ -1,11 +1,24 @@
|
||||
import React from 'react';
|
||||
|
||||
export default class BinaryStringView extends React.Component {
|
||||
export type BinaryStringViewProps = {
|
||||
allowFlipBits: boolean;
|
||||
binaryString: string;
|
||||
onFlipBit: (input: FlipBitEventArg) => void;
|
||||
emphasizeBytes: boolean;
|
||||
};
|
||||
|
||||
export type FlipBitEventArg = {
|
||||
index: number;
|
||||
binaryString: string;
|
||||
$event: any;
|
||||
};
|
||||
|
||||
export default class BinaryStringView extends React.Component<BinaryStringViewProps> {
|
||||
render() {
|
||||
return <span>{this.getChildren()}</span>
|
||||
}
|
||||
|
||||
onBitClick(index, e) {
|
||||
onBitClick(index: number, e : any) {
|
||||
if(!this.props.allowFlipBits) {
|
||||
return;
|
||||
}
|
||||
@@ -25,14 +38,17 @@ export default class BinaryStringView extends React.Component {
|
||||
return bits;
|
||||
}
|
||||
|
||||
createBits(bitChars) {
|
||||
createBits(bitChars:string[]) : JSX.Element[] {
|
||||
const allowFlipBits = this.props.allowFlipBits || false;
|
||||
const css = allowFlipBits ? ' flipable' : ''
|
||||
const classNames = { '0': `zero${css}`, '1' : `one ${css}` };
|
||||
return bitChars.map((c, i) => <span className={classNames[c]} key={i} onClick={e => this.onBitClick(i, e)}>{c}</span>);
|
||||
|
||||
return bitChars.map((c, i) => {
|
||||
var className = c == '0' ? `zero${css}` : `one${css}`;
|
||||
return <span className={className} key={i} onClick={e => this.onBitClick(i, e)}>{c}</span>
|
||||
});
|
||||
}
|
||||
|
||||
splitIntoBytes(bits) {
|
||||
splitIntoBytes(bits: JSX.Element[]) {
|
||||
const bytes = [];
|
||||
|
||||
var key = 0;
|
||||
@@ -1,9 +1,8 @@
|
||||
import React from 'react';
|
||||
import cmd from '../../cmd';
|
||||
import CommandLink from '../misc/CommandLink';
|
||||
import CommandLink from '../CommandLink';
|
||||
|
||||
function HelpResultView() {
|
||||
|
||||
export default class HelpResultView extends React.Component {
|
||||
render() {
|
||||
return <div className="help helpResultTpl">
|
||||
<div style={{overflow: "hidden"}}>
|
||||
<div style={{float: "left", "marginRight": "20px"}}>
|
||||
@@ -45,5 +44,6 @@ export default class HelpResultView extends React.Component {
|
||||
</div>
|
||||
</div>
|
||||
</div>;
|
||||
}
|
||||
}
|
||||
|
||||
export default HelpResultView;
|
||||
@@ -1,8 +1,8 @@
|
||||
import React from 'react'
|
||||
import CommandLink from '../misc/CommandLink'
|
||||
import CommandLink from '../CommandLink'
|
||||
|
||||
function WhatsnewResultView() {
|
||||
|
||||
export default class WhatsnewResultView extends React.Component {
|
||||
render() {
|
||||
return <div className="changelog">
|
||||
<h3>Changelog</h3>
|
||||
<div className="item item-new">
|
||||
@@ -22,5 +22,6 @@ export default class WhatsnewResultView extends React.Component {
|
||||
Complete rewrite using React. Old implementation is available at <a href="http://bitwisecmd.com/old">http://bitwisecmd.com/old</a>. Please let me know if you have problems with this release by <a href="https://github.com/BorysLevytskyi/BitwiseCmd/issues">creating issue</a> in Github Repo.</p>
|
||||
</div>
|
||||
</div>;
|
||||
}
|
||||
}
|
||||
|
||||
export default WhatsnewResultView;
|
||||
173
src/components/results/expressions/BitwiseExpressionModel.ts
Normal file
173
src/components/results/expressions/BitwiseExpressionModel.ts
Normal file
@@ -0,0 +1,173 @@
|
||||
import { NumericOperand, ListOfNumbersExpression, BitwiseOperationExpression, ExpressionOperand } from '../../../expression/expression';
|
||||
import { ExpressionInputItem, ExpressionInput } from '../../../expression/expression-interfaces';
|
||||
|
||||
type Config = {
|
||||
emphasizeBytes: boolean;
|
||||
allowFlipBits: boolean;
|
||||
}
|
||||
|
||||
type ExpressionItemModel = {
|
||||
sign: string;
|
||||
css: string;
|
||||
expressionItem: ExpressionInputItem;
|
||||
allowFlipBits: boolean;
|
||||
label: string;
|
||||
}
|
||||
|
||||
export default class BitwiseExpressionViewModel {
|
||||
|
||||
emphasizeBytes: boolean;
|
||||
items: ExpressionItemModel[];
|
||||
maxNumberOfBits: number;
|
||||
allowFlipBits: boolean;
|
||||
|
||||
constructor({ emphasizeBytes = false, allowFlipBits = false} : Config) {
|
||||
this.emphasizeBytes = emphasizeBytes;
|
||||
this.items = [];
|
||||
this.maxNumberOfBits = 0;
|
||||
this.allowFlipBits = allowFlipBits === true;
|
||||
}
|
||||
|
||||
static buildListOfNumbers(expr : ListOfNumbersExpression, config : Config) {
|
||||
var model = new BitwiseExpressionViewModel(config);
|
||||
expr.numbers.forEach(op => model.addOperandRow(op));
|
||||
model.maxNumberOfBits = BitwiseExpressionViewModel.getNumberOfBits(model.maxNumberOfBits, model.emphasizeBytes);
|
||||
return model;
|
||||
}
|
||||
|
||||
static buildMultiple (expr : BitwiseOperationExpression, config : Config) {
|
||||
|
||||
var op = expr.expressionItems[0],
|
||||
i = 0, len = expr.expressionItems.length,
|
||||
ex, m = new BitwiseExpressionViewModel(config);
|
||||
|
||||
var prev : NumericOperand | null = null;
|
||||
|
||||
for (;i<len;i++) {
|
||||
ex = expr.expressionItems[i];
|
||||
if(ex instanceof NumericOperand) {
|
||||
m.addOperandRow(ex);
|
||||
prev = ex;
|
||||
continue;
|
||||
}
|
||||
|
||||
var eo = ex as ExpressionOperand;
|
||||
|
||||
// If it a single NOT expression
|
||||
if(eo.isNotExpression) {
|
||||
m.addExpressionOperandRow(eo);
|
||||
var notResult = eo.evaluate();
|
||||
m.addExpressionResultRow(notResult);
|
||||
prev = notResult;
|
||||
}
|
||||
else if(eo.isShiftExpression){
|
||||
prev = eo.evaluate(prev as NumericOperand);
|
||||
m.addShiftExpressionResultRow(eo, prev);
|
||||
} else {
|
||||
|
||||
prev = eo.evaluate(prev as NumericOperand);
|
||||
m.addExpressionOperandRow(eo);
|
||||
m.addExpressionResultRow(prev);
|
||||
}
|
||||
}
|
||||
|
||||
m.maxNumberOfBits = BitwiseExpressionViewModel.getNumberOfBits(m.maxNumberOfBits, m.emphasizeBytes);
|
||||
return m;
|
||||
};
|
||||
|
||||
static buildNot (expression: ExpressionOperand, config : Config) {
|
||||
|
||||
var m = new BitwiseExpressionViewModel(config);
|
||||
m.addExpressionOperandRow(expression);
|
||||
m.addExpressionResultRow(expression.evaluate());
|
||||
m.maxNumberOfBits = BitwiseExpressionViewModel.getNumberOfBits(m.maxNumberOfBits, m.emphasizeBytes);
|
||||
return m;
|
||||
};
|
||||
|
||||
addOperandRow(operand: NumericOperand) {
|
||||
this.maxNumberOfBits = Math.max(operand.getLengthInBits(), this.maxNumberOfBits);
|
||||
this.items.push({
|
||||
sign:'',
|
||||
css: '',
|
||||
expressionItem: operand,
|
||||
allowFlipBits: this.allowFlipBits,
|
||||
label: ''
|
||||
});
|
||||
};
|
||||
|
||||
addExpressionOperandRow(expression: ExpressionOperand) {
|
||||
const resultNumber = expression.isNotExpression ? expression.evaluate() : expression.getUnderlyingOperand();
|
||||
this.maxNumberOfBits = Math.max(resultNumber.getLengthInBits(), this.maxNumberOfBits);
|
||||
|
||||
this.items.push({
|
||||
sign: expression.sign,
|
||||
css: '',
|
||||
label: this.getLabel(resultNumber),
|
||||
expressionItem: expression.operand,
|
||||
allowFlipBits: this.allowFlipBits
|
||||
});
|
||||
};
|
||||
|
||||
addShiftExpressionResultRow(expression : ExpressionOperand, resultOperand : NumericOperand) {
|
||||
this.maxNumberOfBits = Math.max(resultOperand.getLengthInBits(), this.maxNumberOfBits);
|
||||
this.items.push({
|
||||
sign: expression.sign + expression.operand.toString(),
|
||||
css: 'expression-result',
|
||||
expressionItem: resultOperand,
|
||||
allowFlipBits: false,
|
||||
label: ''
|
||||
});
|
||||
};
|
||||
|
||||
addExpressionResultRow(operand : NumericOperand) {
|
||||
this.maxNumberOfBits = Math.max(operand.getLengthInBits(), this.maxNumberOfBits);
|
||||
this.items.push({
|
||||
sign:'=',
|
||||
css: 'expression-result',
|
||||
expressionItem: operand,
|
||||
allowFlipBits: false,
|
||||
label: '',
|
||||
});
|
||||
};
|
||||
|
||||
getLabel (op: NumericOperand) : string {
|
||||
|
||||
if(op.base == 'bin') {
|
||||
return op.toString("dec");
|
||||
}
|
||||
|
||||
return op.toString();
|
||||
}
|
||||
|
||||
// TODO: move this method elsewhere. It is also used in LisOfNumbersExpressionView.js
|
||||
static getNumberOfBits = function (bits : number, emphasizeBytes : boolean) : number {
|
||||
if(emphasizeBytes && bits % 8 != 0) {
|
||||
if(bits < 8) {
|
||||
return 8;
|
||||
}
|
||||
|
||||
var n = bits - (bits % 8);
|
||||
return n + 8;
|
||||
}
|
||||
|
||||
return bits;
|
||||
};
|
||||
|
||||
static createModel(expr : ExpressionInput, emphasizeBytes: boolean) : BitwiseExpressionViewModel {
|
||||
if(expr instanceof ListOfNumbersExpression) {
|
||||
return BitwiseExpressionViewModel.buildListOfNumbers(expr, {
|
||||
emphasizeBytes: emphasizeBytes,
|
||||
allowFlipBits: true
|
||||
});
|
||||
}
|
||||
|
||||
if(expr instanceof BitwiseOperationExpression) {
|
||||
return BitwiseExpressionViewModel.buildMultiple(expr, {
|
||||
emphasizeBytes: emphasizeBytes,
|
||||
allowFlipBits: false
|
||||
});
|
||||
}
|
||||
|
||||
throw new Error("Cannot build BitwiseExpressionViewModel out of expression " + expr);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,134 @@
|
||||
import React from 'react';
|
||||
import formatter from '../../../core/formatter';
|
||||
import BinaryStringView, { FlipBitEventArg } from '../BinaryString';
|
||||
import BitwiseExpressionViewModel from './BitwiseExpressionModel';
|
||||
import { ExpressionInput, ExpressionInputItem } from '../../../expression/expression-interfaces';
|
||||
import { ExpressionOperand, NumericOperand } from '../../../expression/expression';
|
||||
|
||||
type BitwiseOperationExpressionViewProps = {
|
||||
expression: ExpressionInput;
|
||||
emphasizeBytes: boolean;
|
||||
}
|
||||
|
||||
type BitwiseOperationExpressionViewState = {
|
||||
|
||||
}
|
||||
|
||||
export default class BitwiseOperationExpressionView extends React.Component<BitwiseOperationExpressionViewProps, BitwiseOperationExpressionViewState> {
|
||||
constructor(props: BitwiseOperationExpressionViewProps) {
|
||||
super(props);
|
||||
this.state = {};
|
||||
}
|
||||
render() {
|
||||
var rows = this.getRows();
|
||||
if(!rows) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return <table className="expression">
|
||||
<tbody>
|
||||
{rows}
|
||||
</tbody>
|
||||
</table>
|
||||
}
|
||||
|
||||
getRows() : JSX.Element[] | null {
|
||||
var model = BitwiseExpressionViewModel.createModel(this.props.expression, this.props.emphasizeBytes);
|
||||
|
||||
return model.items.map((itm, i) =>
|
||||
<ExpressionRow
|
||||
key={i}
|
||||
sign={itm.sign}
|
||||
css={itm.css}
|
||||
allowFlipBits={itm.allowFlipBits}
|
||||
expressionItem={itm.expressionItem}
|
||||
emphasizeBytes={this.props.emphasizeBytes}
|
||||
maxNumberOfBits={model.maxNumberOfBits}
|
||||
onBitFlipped={() => this.onBitFlipped()} />);
|
||||
}
|
||||
|
||||
onBitFlipped() {
|
||||
this.forceUpdate();
|
||||
//this.setState({d:new Date()});
|
||||
}
|
||||
}
|
||||
|
||||
type ExpressionRowProps = {
|
||||
sign: string,
|
||||
css: string,
|
||||
maxNumberOfBits: number,
|
||||
emphasizeBytes: boolean,
|
||||
allowFlipBits: boolean,
|
||||
expressionItem: ExpressionInputItem,
|
||||
onBitFlipped: any
|
||||
}
|
||||
|
||||
class ExpressionRow extends React.Component<ExpressionRowProps> {
|
||||
constructor(props: ExpressionRowProps) {
|
||||
super(props);
|
||||
this.state = { operand: null };
|
||||
}
|
||||
render() {
|
||||
const { sign, css, maxNumberOfBits, emphasizeBytes, allowFlipBits } = this.props;
|
||||
|
||||
return <tr className={css}>
|
||||
<td className="sign">{sign}</td>
|
||||
<td className="label">{this.getLabel()}</td>
|
||||
<td className="bin">
|
||||
<BinaryStringView
|
||||
emphasizeBytes={emphasizeBytes}
|
||||
binaryString={formatter.padLeft(this.getBinaryString(), maxNumberOfBits, '0')}
|
||||
allowFlipBits={allowFlipBits}
|
||||
onFlipBit={args => this.flipBit(args)}/>
|
||||
</td>
|
||||
<td className="other">{this.getOther()}</td>
|
||||
</tr>;;
|
||||
}
|
||||
|
||||
getBinaryString() : string {
|
||||
return this.props.expressionItem.evaluate().toBinaryString();
|
||||
}
|
||||
|
||||
getLabel(): string {
|
||||
|
||||
// For expressions like |~2
|
||||
// TODO: find a better way...
|
||||
if(this.props.expressionItem.isExpression) {
|
||||
const ex = this.props.expressionItem as ExpressionOperand;
|
||||
return ex.sign + this.getLabelString(ex.getUnderlyingOperand());
|
||||
}
|
||||
|
||||
return this.getLabelString(this.props.expressionItem.getUnderlyingOperand());
|
||||
}
|
||||
|
||||
getOther() {
|
||||
|
||||
if(this.props.expressionItem.isExpression) {
|
||||
const ex = this.props.expressionItem as ExpressionOperand;
|
||||
const op = ex.evaluate();
|
||||
|
||||
return op.toString();
|
||||
}
|
||||
|
||||
return this.props.expressionItem.evaluate().toOtherKindString();
|
||||
}
|
||||
|
||||
getLabelString (op: NumericOperand) : string {
|
||||
return op.toString(op.base == 'bin' ? 'dec' : op.base);
|
||||
}
|
||||
|
||||
flipBit(args: FlipBitEventArg) {
|
||||
|
||||
const op = this.props.expressionItem.getUnderlyingOperand();
|
||||
const { index, binaryString } = args;
|
||||
|
||||
var arr = binaryString.split('');
|
||||
arr[index] = arr[index] == '0' ? '1' : '0';
|
||||
var bin = arr.join('');
|
||||
|
||||
var newValue = parseInt(bin, 2);
|
||||
op.setValue(newValue);
|
||||
|
||||
this.props.onBitFlipped();
|
||||
}
|
||||
}
|
||||
78
src/core/AppState.ts
Normal file
78
src/core/AppState.ts
Normal file
@@ -0,0 +1,78 @@
|
||||
import log from 'loglevel';
|
||||
|
||||
export type PersistedAppData = {
|
||||
emphasizeBytes: boolean;
|
||||
uiTheme: string;
|
||||
version: number;
|
||||
debugMode: boolean | null;
|
||||
}
|
||||
|
||||
export type AppStateChangeHandler = (state: AppState) => void;
|
||||
|
||||
export default class AppState {
|
||||
|
||||
version: number = 4;
|
||||
emphasizeBytes: boolean;
|
||||
debugMode: boolean = false;
|
||||
uiTheme: string;
|
||||
handlers: AppStateChangeHandler[];
|
||||
commandResults: any[];
|
||||
persistedVersion: number;
|
||||
wasOldVersion: boolean;
|
||||
env: string;
|
||||
|
||||
constructor(persistData : PersistedAppData, env: string) {
|
||||
this.commandResults = [];
|
||||
this.handlers = [];
|
||||
this.uiTheme = persistData.uiTheme || 'midnight';
|
||||
this.env = env;
|
||||
|
||||
this.emphasizeBytes = persistData.emphasizeBytes || true;
|
||||
this.persistedVersion = persistData.version || 0.1;
|
||||
this.wasOldVersion = persistData.version != null && this.version > this.persistedVersion;
|
||||
this.debugMode = env !== 'prod' || persistData.debugMode === true;
|
||||
}
|
||||
|
||||
addCommandResult(result : any) {
|
||||
this.commandResults.unshift(result);
|
||||
log.debug("result added", result);
|
||||
this.triggerChanged();
|
||||
}
|
||||
|
||||
clearCommandResults() {
|
||||
this.commandResults = [];
|
||||
this.triggerChanged();
|
||||
}
|
||||
|
||||
toggleEmphasizeBytes() {
|
||||
this.emphasizeBytes = !this.emphasizeBytes;
|
||||
this.triggerChanged();
|
||||
}
|
||||
|
||||
onChange(handler : AppStateChangeHandler) {
|
||||
this.handlers.push(handler);
|
||||
}
|
||||
|
||||
triggerChanged() {
|
||||
this.handlers.forEach(h => h(this));
|
||||
}
|
||||
|
||||
setUiTheme(theme: string) {
|
||||
this.uiTheme = theme;
|
||||
this.triggerChanged();
|
||||
}
|
||||
|
||||
toggleDebugMode() {
|
||||
this.debugMode = !this.debugMode;
|
||||
this.triggerChanged();
|
||||
}
|
||||
|
||||
getPersistData() : PersistedAppData {
|
||||
return {
|
||||
emphasizeBytes: this.emphasizeBytes,
|
||||
uiTheme: this.uiTheme,
|
||||
version: this.version,
|
||||
debugMode: this.debugMode
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -1,26 +1,28 @@
|
||||
import AppState, { PersistedAppData } from "./AppState";
|
||||
|
||||
const storeKey = 'AppState';
|
||||
|
||||
export default {
|
||||
getPersistedData() {
|
||||
getPersistedData() : PersistedAppData {
|
||||
var json = window.localStorage.getItem(storeKey);
|
||||
if(!json) {
|
||||
return {};
|
||||
return {} as PersistedAppData;
|
||||
}
|
||||
|
||||
try {
|
||||
return JSON.parse(json);
|
||||
return JSON.parse(json) as PersistedAppData;
|
||||
}
|
||||
catch(ex) {
|
||||
console.error('Failed to parse AppState json. Json Value: \n' + json, ex);
|
||||
return {};
|
||||
return {} as PersistedAppData;;
|
||||
}
|
||||
},
|
||||
|
||||
watch (appState) {
|
||||
watch (appState: AppState) {
|
||||
appState.onChange(() => this.persistData(appState));
|
||||
},
|
||||
|
||||
persistData(appState) {
|
||||
persistData(appState: AppState) {
|
||||
localStorage.setItem(storeKey, JSON.stringify(appState.getPersistData()));
|
||||
}
|
||||
}
|
||||
27
src/core/calc.test.ts
Normal file
27
src/core/calc.test.ts
Normal file
@@ -0,0 +1,27 @@
|
||||
import calc from './calc';
|
||||
import { BitwiseOperationExpression, NumericOperand, ExpressionOperand } from '../expression/expression';
|
||||
|
||||
describe("calc", () => {
|
||||
it('calculates number of bits', () => {
|
||||
expect(calc.numberOfBits(1)).toBe(1);
|
||||
expect(calc.numberOfBits(2)).toBe(2);
|
||||
expect(calc.numberOfBits(3)).toBe(2);
|
||||
});
|
||||
|
||||
it('calculates max number of bits', () => {
|
||||
expect(calc.maxNumberOfBits([1, 2, 3, 10])).toBe(4);
|
||||
});
|
||||
|
||||
it('calculates expression', () => {
|
||||
var result = calc.calcExpression(new BitwiseOperationExpression(
|
||||
"1|2&3",
|
||||
[
|
||||
new NumericOperand(1),
|
||||
new ExpressionOperand("|2", new NumericOperand(2), "|"),
|
||||
new ExpressionOperand("&3", new NumericOperand(3), "&"),
|
||||
]
|
||||
));
|
||||
|
||||
expect(result).toBe(3);
|
||||
});
|
||||
});
|
||||
26
src/core/calc.ts
Normal file
26
src/core/calc.ts
Normal file
@@ -0,0 +1,26 @@
|
||||
import { ExpressionInput } from "../expression/expression-interfaces";
|
||||
|
||||
export default {
|
||||
numberOfBits: function (num: number) : number {
|
||||
if(num < 0) {
|
||||
return 32;
|
||||
}
|
||||
|
||||
return Math.floor(Math.log(num) / Math.log(2)) + 1;
|
||||
},
|
||||
|
||||
maxNumberOfBits: function (arr: number[]) {
|
||||
|
||||
var counts = [], num;
|
||||
for (var i = 0; i < arr.length; i++) {
|
||||
num = arr[i];
|
||||
counts.push(this.numberOfBits(num));
|
||||
}
|
||||
|
||||
return Math.max.apply(null, counts);
|
||||
},
|
||||
|
||||
calcExpression: function (expr: ExpressionInput) {
|
||||
return eval(expr.expressionString);
|
||||
}
|
||||
};
|
||||
49
src/core/cmd.test.ts
Normal file
49
src/core/cmd.test.ts
Normal file
@@ -0,0 +1,49 @@
|
||||
import { CmdShell, ICommandHandler } from "./cmd";
|
||||
import { func } from "prop-types";
|
||||
|
||||
describe("CmdShell", () => {
|
||||
it("simple command", () => {
|
||||
|
||||
var handler = {
|
||||
test1() { },
|
||||
test2() { }
|
||||
};
|
||||
|
||||
spyOn(handler, "test1");
|
||||
spyOn(handler, "test2");
|
||||
|
||||
var sut = new CmdShell();
|
||||
sut.command("test1", handler.test1);
|
||||
|
||||
sut.execute("test1");
|
||||
|
||||
expect(handler.test1).toHaveBeenCalled();
|
||||
expect(handler.test2).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("unknown command", () => {
|
||||
var sut = new CmdShell();
|
||||
sut.execute("test1");
|
||||
});
|
||||
|
||||
it("object handler", () => {
|
||||
|
||||
var handler = {
|
||||
canHandle: function(input: string) : boolean { return input === "test2"; },
|
||||
handle: function (input: string) { }
|
||||
};
|
||||
|
||||
spyOn(handler, "handle");
|
||||
|
||||
var sut = new CmdShell();
|
||||
sut.command(handler);
|
||||
|
||||
sut.execute("test1");
|
||||
|
||||
expect(handler.handle).not.toHaveBeenCalled();
|
||||
|
||||
sut.execute("test2");
|
||||
|
||||
expect(handler.handle).toHaveBeenCalled();
|
||||
});
|
||||
})
|
||||
107
src/core/cmd.ts
Normal file
107
src/core/cmd.ts
Normal file
@@ -0,0 +1,107 @@
|
||||
import is from './is';
|
||||
import log from 'loglevel';
|
||||
|
||||
export type CommandInput = {
|
||||
input: string;
|
||||
}
|
||||
|
||||
type HandleFunction = (input: CommandInput) => void;
|
||||
type InputErrorHandler = (input:string, error: Error) => void;
|
||||
|
||||
export interface ICommandHandler {
|
||||
canHandle (input:string) : boolean;
|
||||
handle: HandleFunction;
|
||||
}
|
||||
|
||||
export class CmdShell {
|
||||
debugMode: boolean;
|
||||
handlers: ICommandHandler[];
|
||||
errorHandler: InputErrorHandler | null;
|
||||
constructor() {
|
||||
this.handlers = [];
|
||||
this.debugMode = false;
|
||||
this.errorHandler = null;
|
||||
};
|
||||
|
||||
execute (rawInput: string) {
|
||||
|
||||
log.debug(`Executing command: ${rawInput}`)
|
||||
|
||||
var input = rawInput.trim().toLowerCase();
|
||||
var handler = this.findHandler(input);
|
||||
|
||||
if(handler != null) {
|
||||
if(this.debugMode) {
|
||||
this.invokeHandler(input, handler);
|
||||
} else {
|
||||
try {
|
||||
this.invokeHandler(input, handler);
|
||||
} catch (e) {
|
||||
this.handleError(input, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
log.debug(`Handled is not found for command: ${rawInput}`)
|
||||
this.handleError(input, new Error("Unsupported expression: " + input.trim()));
|
||||
}
|
||||
};
|
||||
|
||||
onError(h: InputErrorHandler) {
|
||||
this.errorHandler = h;
|
||||
}
|
||||
|
||||
command (cmd : string | object, handler? : any) {
|
||||
var h = this.createHandler(cmd, handler);
|
||||
if(h == null){
|
||||
console.warn('unexpected set of arguments: ', JSON.stringify(arguments));
|
||||
return;
|
||||
}
|
||||
|
||||
if(!is.aFunction(h.canHandle)) {
|
||||
console.warn('handler is missing "canHandle" function. registration denied.');
|
||||
return;
|
||||
}
|
||||
|
||||
if(!is.aFunction(h.handle)) {
|
||||
console.warn('handler is missing "handle" function. registration denied.');
|
||||
return;
|
||||
}
|
||||
|
||||
this.handlers.push(h);
|
||||
};
|
||||
|
||||
createHandler (cmd : string | object, handler : HandleFunction) : ICommandHandler | null {
|
||||
if(is.plainObject(cmd)) {
|
||||
return cmd as ICommandHandler;
|
||||
}
|
||||
|
||||
if(is.string(cmd)) {
|
||||
return { canHandle: function (input) { return input === cmd; }, handle: handler };
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
findHandler (input: string) : ICommandHandler | null {
|
||||
return this.handlers.filter(h => h.canHandle(input))[0];
|
||||
};
|
||||
|
||||
invokeHandler (input : string, handler : ICommandHandler) {
|
||||
|
||||
var cmdResult = handler.handle({ input: input});
|
||||
if(cmdResult != null) {
|
||||
log.debug(cmdResult);
|
||||
}
|
||||
};
|
||||
|
||||
handleError (input: string, err: Error) {
|
||||
if(this.debugMode)
|
||||
console.error(input, err);
|
||||
|
||||
if(this.errorHandler != null)
|
||||
this.errorHandler(input, err);
|
||||
}
|
||||
}
|
||||
|
||||
export default new CmdShell();
|
||||
13
src/core/formatter.test.ts
Normal file
13
src/core/formatter.test.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
import formatter from './formatter';
|
||||
|
||||
describe("formatter", () => {
|
||||
it('formats string', () => {
|
||||
expect(formatter.formatString(15, "dec")).toBe("15");
|
||||
expect(formatter.formatString(15, "hex")).toBe("f");
|
||||
expect(formatter.formatString(15, "bin")).toBe("1111");
|
||||
});
|
||||
|
||||
it('pads left', () => {
|
||||
expect(formatter.padLeft("1", 3, " ")).toBe(" 1");
|
||||
});
|
||||
});
|
||||
28
src/core/formatter.ts
Normal file
28
src/core/formatter.ts
Normal file
@@ -0,0 +1,28 @@
|
||||
export default {
|
||||
formatString: function(num: number, kind: string) : string {
|
||||
return num.toString(getBase(kind || "bin"));
|
||||
},
|
||||
padLeft: function (str: string, length: number, symbol: string) : string {
|
||||
var sb = Array.prototype.slice.call(str), symbol = symbol || "0";
|
||||
|
||||
if(length == null) {
|
||||
return str;
|
||||
}
|
||||
|
||||
while(length > sb.length) {
|
||||
sb.unshift(symbol);
|
||||
}
|
||||
|
||||
return sb.join('');
|
||||
}
|
||||
};
|
||||
|
||||
function getBase(kind:string) : number {
|
||||
switch (kind){
|
||||
case 'bin': return 2;
|
||||
case 'hex': return 16;
|
||||
case 'dec': return 10;
|
||||
}
|
||||
|
||||
throw new Error("Unsupported kind: " + kind);
|
||||
}
|
||||
35
src/core/hash.ts
Normal file
35
src/core/hash.ts
Normal file
@@ -0,0 +1,35 @@
|
||||
export default {
|
||||
encodeHash: function(input:string):string {
|
||||
return encodeURI(input.trim().replace(/\s/g,','));
|
||||
},
|
||||
decodeHash: function(hashValue:string):string {
|
||||
return decodeURI(hashValue).replace(/^\#/, '').replace(/,/g,' ');
|
||||
},
|
||||
getArgs: function (hashValue:string) : string[] {
|
||||
|
||||
var decodedHash = this.decodeHash(hashValue);
|
||||
var args : string[] = [];
|
||||
|
||||
splitHashList(decodedHash).forEach(function(value) {
|
||||
args.push(value);
|
||||
});
|
||||
|
||||
return args;
|
||||
}
|
||||
};
|
||||
|
||||
function splitHashList(str: string) : string[] {
|
||||
var values = [];
|
||||
|
||||
if(str.indexOf('||')) {
|
||||
str.split('||').forEach(function (v) {
|
||||
if (v.length > 0) {
|
||||
values.push(v);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
values.push(str);
|
||||
}
|
||||
|
||||
return values;
|
||||
}
|
||||
16
src/core/is.test.ts
Normal file
16
src/core/is.test.ts
Normal file
@@ -0,0 +1,16 @@
|
||||
import is from "./is";
|
||||
|
||||
describe("is", () => {
|
||||
|
||||
it("can detect array", () => {
|
||||
expect(is.array([1,2,3])).toBe(true);
|
||||
expect(is.array({})).toBe(false);
|
||||
expect(is.array("123")).toBe(false);
|
||||
});
|
||||
|
||||
it("can detect plain object", () => {
|
||||
expect(is.plainObject({})).toBe(true);
|
||||
expect(is.plainObject([1,2,3])).toBe(false);
|
||||
expect(is.plainObject("123")).toBe(false);
|
||||
});
|
||||
});
|
||||
21
src/core/is.ts
Normal file
21
src/core/is.ts
Normal file
@@ -0,0 +1,21 @@
|
||||
export default {
|
||||
plainObject: function(obj : any) : boolean {
|
||||
return typeof obj == "object" && !(obj instanceof Array) && obj instanceof Object;
|
||||
},
|
||||
|
||||
aFunction: function(obj : any) : boolean {
|
||||
return typeof obj == "function";
|
||||
},
|
||||
|
||||
string: function(obj : any) : boolean {
|
||||
return typeof obj == "string";
|
||||
},
|
||||
|
||||
array: function(obj : any) : boolean {
|
||||
return obj instanceof Array;
|
||||
},
|
||||
|
||||
number: function(obj : any) : boolean {
|
||||
return typeof obj == "number" && !isNaN(obj)
|
||||
}
|
||||
}
|
||||
12
src/expression/BitwiseOperationExpression.ts
Normal file
12
src/expression/BitwiseOperationExpression.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
import { ExpressionInput, ExpressionInputItem } from "./expression-interfaces";
|
||||
|
||||
export default class BitwiseOperationExpression implements ExpressionInput {
|
||||
|
||||
expressionString: string;
|
||||
expressionItems: ExpressionInputItem[];
|
||||
|
||||
constructor(expressionString: string, expressions: ExpressionInputItem[]) {
|
||||
this.expressionString = expressionString;
|
||||
this.expressionItems = expressions;
|
||||
}
|
||||
}
|
||||
19
src/expression/ExpressionOperand.test.ts
Normal file
19
src/expression/ExpressionOperand.test.ts
Normal file
@@ -0,0 +1,19 @@
|
||||
import NumericOperand from "./NumericOperand";
|
||||
import ExpressionOperand from './ExpressionOperand';
|
||||
|
||||
it('can apply ~ operand', () => {
|
||||
var op = new NumericOperand(10, 'dec');
|
||||
var expr = new ExpressionOperand("~10", op, "~");
|
||||
var result = expr.evaluate();
|
||||
expect(result.value).toBe(-11);
|
||||
expect(result.base).toBe('dec');
|
||||
});
|
||||
|
||||
it('can apply & operand', () => {
|
||||
var op1 = new NumericOperand(3, 'dec');
|
||||
var op2 = new NumericOperand(4, 'dec');
|
||||
var expr = new ExpressionOperand("|3", op1, "|");
|
||||
var result = expr.evaluate(op2);
|
||||
expect(result.value).toBe(7);
|
||||
expect(result.base).toBe('dec');
|
||||
});
|
||||
48
src/expression/ExpressionOperand.ts
Normal file
48
src/expression/ExpressionOperand.ts
Normal file
@@ -0,0 +1,48 @@
|
||||
import NumericOperand from './NumericOperand';
|
||||
import { ExpressionInputItem } from './expression-interfaces';
|
||||
|
||||
export default class ExpressionOperand implements ExpressionInputItem {
|
||||
expressionString: string;
|
||||
operand: ExpressionInputItem;
|
||||
sign: string;
|
||||
isExpression: boolean;
|
||||
isShiftExpression: boolean;
|
||||
isNotExpression: boolean;
|
||||
|
||||
constructor(expressionString : string, operand : ExpressionInputItem, sign : string) {
|
||||
this.expressionString = expressionString;
|
||||
this.operand = operand;
|
||||
this.sign = sign;
|
||||
this.isExpression = true;
|
||||
this.isShiftExpression = this.sign.indexOf('<') >= 0 || this.sign.indexOf('>')>= 0;
|
||||
this.isNotExpression = this.sign === '~';
|
||||
}
|
||||
|
||||
evaluate(operand?: NumericOperand) : NumericOperand {
|
||||
if (operand instanceof ExpressionOperand) {
|
||||
throw new Error('value shouldnt be expression');
|
||||
}
|
||||
|
||||
var evaluatedOperand = this.operand.evaluate();
|
||||
|
||||
var str = '';
|
||||
if(this.sign == '~'){
|
||||
str = '~' + evaluatedOperand.value;
|
||||
} else {
|
||||
if(operand == null)
|
||||
throw new Error("Other is required for expression: " + this.expressionString)
|
||||
|
||||
str = operand.value + this.sign + evaluatedOperand.value;
|
||||
}
|
||||
|
||||
return NumericOperand.create(eval(str), evaluatedOperand.base);
|
||||
}
|
||||
|
||||
getUnderlyingOperand() : NumericOperand {
|
||||
return this.operand.getUnderlyingOperand();
|
||||
}
|
||||
|
||||
toString(): string {
|
||||
return this.sign + this.operand.toString();
|
||||
}
|
||||
}
|
||||
7
src/expression/ListOfNumbersExpression.test.ts
Normal file
7
src/expression/ListOfNumbersExpression.test.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
import NumericOperand from "./NumericOperand";
|
||||
import ListOfNumbersExpression from "./ListOfNumbersExpression";
|
||||
|
||||
it('calculates max bits length', () => {
|
||||
var expr = new ListOfNumbersExpression("10 0xabef 0b01010", [NumericOperand.parse("10"), NumericOperand.parse("0xabef"), NumericOperand.parse("0b01010")])
|
||||
expect(expr.maxBitsLength).toBe(16);
|
||||
});
|
||||
18
src/expression/ListOfNumbersExpression.ts
Normal file
18
src/expression/ListOfNumbersExpression.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
import NumericOperand from "./NumericOperand";
|
||||
import { ExpressionInput, ExpressionInputItem } from "./expression-interfaces";
|
||||
|
||||
export default class ListOfNumbersExpression implements ExpressionInput {
|
||||
numbers: NumericOperand[];
|
||||
expressionString: string;
|
||||
maxBitsLength: number;
|
||||
|
||||
constructor(expressionString: string, numbers: NumericOperand[]) {
|
||||
this.expressionString = expressionString;
|
||||
this.numbers = numbers;
|
||||
this.maxBitsLength = numbers.map(n => n.lengthInBits).reduce((n , c) => n >= c ? n : c, 0);
|
||||
}
|
||||
|
||||
toString() {
|
||||
return this.numbers.map(n => n.value.toString()).join(' ');
|
||||
}
|
||||
}
|
||||
13
src/expression/NumericOperand.test.ts
Normal file
13
src/expression/NumericOperand.test.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
import NumericOperand from './NumericOperand';
|
||||
|
||||
it('parsed from string', () => {
|
||||
var op = NumericOperand.parse('123');
|
||||
expect(op.base).toBe('dec');
|
||||
expect(op.value).toBe(123);
|
||||
});
|
||||
|
||||
it('can get other kind', () => {
|
||||
var op = new NumericOperand(10, 'dec');
|
||||
expect(op.getOtherBase('hex')).toBe('dec');
|
||||
expect(op.getOtherBase('bin')).toBe('hex');
|
||||
});
|
||||
116
src/expression/NumericOperand.ts
Normal file
116
src/expression/NumericOperand.ts
Normal file
@@ -0,0 +1,116 @@
|
||||
import {numberParser} from './numberParser';
|
||||
import { ExpressionInputItem, NumberBase } from './expression-interfaces';
|
||||
|
||||
var globalId : number = 1;
|
||||
|
||||
// Represents numeric value
|
||||
export default class NumericOperand implements ExpressionInputItem {
|
||||
id: number;
|
||||
value: number;
|
||||
base: NumberBase;
|
||||
lengthInBits: number;
|
||||
isExpression: boolean;
|
||||
|
||||
constructor(value : number, base?: NumberBase) {
|
||||
this.id = globalId++;
|
||||
this.value = value;
|
||||
this.base = base || "dec";
|
||||
this.lengthInBits = NumericOperand.getBitLength(this.value);
|
||||
this.isExpression = false;
|
||||
}
|
||||
|
||||
getLengthInBits() {
|
||||
if(this.value < 0) {
|
||||
return 32;
|
||||
}
|
||||
return Math.floor(Math.log(this.value) / Math.log(2)) + 1;
|
||||
};
|
||||
|
||||
getOtherBase(kind?: NumberBase) : NumberBase {
|
||||
switch(kind || this.base) {
|
||||
case 'dec':
|
||||
case 'bin':
|
||||
return 'hex';
|
||||
case 'hex': return 'dec';
|
||||
default : throw new Error(kind + " kind doesn't have opposite kind")
|
||||
}
|
||||
};
|
||||
|
||||
toString(base?: NumberBase) : string {
|
||||
return NumericOperand.toBaseString(this.value, base || this.base);
|
||||
}
|
||||
|
||||
toOtherKindString() : string {
|
||||
return this.toString(this.getOtherBase());
|
||||
}
|
||||
|
||||
toDecimalString() {
|
||||
return this.toString('dec');
|
||||
}
|
||||
|
||||
toHexString() {
|
||||
return this.toString('hex');
|
||||
}
|
||||
|
||||
toBinaryString() : string {
|
||||
return this.toString('bin');
|
||||
}
|
||||
|
||||
setValue(value : number) {
|
||||
this.value = value;
|
||||
this.lengthInBits = NumericOperand.getBitLength(value);
|
||||
}
|
||||
|
||||
evaluate() : NumericOperand {
|
||||
return this;
|
||||
}
|
||||
|
||||
getUnderlyingOperand() : NumericOperand {
|
||||
return this
|
||||
}
|
||||
|
||||
static getBitLength(num : number) {
|
||||
return Math.floor(Math.log(num) / Math.log(2)) + 1;
|
||||
}
|
||||
|
||||
static getBase(kind : string){
|
||||
switch (kind){
|
||||
case 'bin': return 2;
|
||||
case 'hex': return 16;
|
||||
case 'dec': return 10;
|
||||
}
|
||||
};
|
||||
|
||||
static create(value : number, base? : NumberBase) {
|
||||
return new NumericOperand(value, base || "dec");
|
||||
};
|
||||
|
||||
static parse(input: string) : NumericOperand {
|
||||
|
||||
var parsed = numberParser.parse(input);
|
||||
|
||||
if(!parsed) {
|
||||
throw new Error(input + " is not a valid number");
|
||||
}
|
||||
|
||||
return new NumericOperand(parsed.value, parsed.base);
|
||||
}
|
||||
|
||||
static toBaseString(value : number, base : NumberBase) : string {
|
||||
switch(base) {
|
||||
case 'hex':
|
||||
var hexVal = Math.abs(value).toString(16);
|
||||
return value >= 0 ? '0x' + hexVal : '-0x' + hexVal;
|
||||
case 'bin':
|
||||
return (value>>>0).toString(2);
|
||||
case 'dec':
|
||||
return value.toString(10);
|
||||
default:
|
||||
throw new Error("Unexpected kind: " + base)
|
||||
}
|
||||
};
|
||||
|
||||
static toHexString (hex : string) {
|
||||
return hex.indexOf('-') === 0 ? '-0x' + hex.substr(1) : '0x' + hex;
|
||||
};
|
||||
}
|
||||
17
src/expression/expression-interfaces.ts
Normal file
17
src/expression/expression-interfaces.ts
Normal file
@@ -0,0 +1,17 @@
|
||||
import { NumericOperand } from "./expression";
|
||||
|
||||
export interface ExpressionInput
|
||||
{
|
||||
expressionString: string;
|
||||
}
|
||||
|
||||
export interface ExpressionInputItem
|
||||
{
|
||||
isExpression: boolean;
|
||||
getUnderlyingOperand: () => NumericOperand;
|
||||
evaluate(operand? : NumericOperand): NumericOperand;
|
||||
}
|
||||
|
||||
export type NumberBase = 'dec' | 'hex' | 'bin';
|
||||
|
||||
|
||||
35
src/expression/expression.test.ts
Normal file
35
src/expression/expression.test.ts
Normal file
@@ -0,0 +1,35 @@
|
||||
import { parser, ListOfNumbersExpression, BitwiseOperationExpression, NumericOperand, ExpressionOperand } from "./expression";
|
||||
|
||||
describe("expression parser", () => {
|
||||
|
||||
it("pares list of number expression", () => {
|
||||
var result = parser.parse("1 2 3");
|
||||
expect(result).toBeInstanceOf(ListOfNumbersExpression);
|
||||
});
|
||||
|
||||
it("pares different operations expressions", () => {
|
||||
expect(parser.parse("~1")).toBeInstanceOf(BitwiseOperationExpression);
|
||||
expect(parser.parse("1^2")).toBeInstanceOf(BitwiseOperationExpression);
|
||||
expect(parser.parse("1|2")).toBeInstanceOf(BitwiseOperationExpression);
|
||||
});
|
||||
|
||||
it("pares multiple operand expression", () => {
|
||||
const result = parser.parse("1^2") as BitwiseOperationExpression;
|
||||
expect(result.expressionItems.length).toBe(2);
|
||||
|
||||
const first = result.expressionItems[0];
|
||||
const second = result.expressionItems[1];
|
||||
|
||||
expect(first).toBeInstanceOf(NumericOperand);
|
||||
expect(first.value).toBe(1);
|
||||
|
||||
expect(second).toBeInstanceOf(ExpressionOperand);
|
||||
expect(second.sign).toBe("^");
|
||||
expect(second.operand.value).toBe(2);
|
||||
});
|
||||
|
||||
it("bug", () => {
|
||||
var result = parser.parse("1|~2") as BitwiseOperationExpression;
|
||||
expect(result.expressionItems.length).toBe(2);
|
||||
});
|
||||
})
|
||||
143
src/expression/expression.ts
Normal file
143
src/expression/expression.ts
Normal file
@@ -0,0 +1,143 @@
|
||||
import NumericOperand from './NumericOperand';
|
||||
import ExpressionOperand from './ExpressionOperand'
|
||||
import ListOfNumbersExpression from './ListOfNumbersExpression';
|
||||
import BitwiseOperationExpression from './BitwiseOperationExpression';
|
||||
import { ExpressionInput, ExpressionInputItem, NumberBase } from './expression-interfaces';
|
||||
|
||||
export { default as NumericOperand } from './NumericOperand';
|
||||
export { default as ExpressionOperand } from './ExpressionOperand';
|
||||
export { default as ListOfNumbersExpression } from './ListOfNumbersExpression';
|
||||
export { default as BitwiseOperationExpression } from './BitwiseOperationExpression';
|
||||
|
||||
interface IExpressionParserFactory {
|
||||
canCreate: (input: string) => boolean;
|
||||
create: (input: string) => ExpressionInput;
|
||||
};
|
||||
|
||||
class ExpressionParser {
|
||||
factories: IExpressionParserFactory[];
|
||||
constructor() {
|
||||
this.factories = [];
|
||||
};
|
||||
|
||||
canParse (input: string) : boolean {
|
||||
var trimmed = input.replace(/^\s+|\s+$/, '');
|
||||
var i = this.factories.length-1;
|
||||
for(;i>=0;i--) {
|
||||
if(this.factories[i].canCreate(trimmed) === true){
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
parse (input: string) : ExpressionInput | null {
|
||||
var trimmed = input.replace(/^\s+|\s+$/, '');
|
||||
var i = 0, l = this.factories.length, factory;
|
||||
|
||||
for(;i<l;i++) {
|
||||
factory = this.factories[i];
|
||||
|
||||
if(factory.canCreate(trimmed) == true){
|
||||
return factory.create(trimmed);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
parseOperand (input : string) : NumericOperand {
|
||||
return NumericOperand.parse(input);
|
||||
};
|
||||
|
||||
createOperand (number : number, base : NumberBase) : NumericOperand {
|
||||
return NumericOperand.create(number, base);
|
||||
};
|
||||
|
||||
addFactory (factory: IExpressionParserFactory) {
|
||||
this.factories.push(factory);
|
||||
}
|
||||
}
|
||||
|
||||
class ListOfNumbersExpressionFactory implements IExpressionParserFactory
|
||||
{
|
||||
regex: RegExp;
|
||||
|
||||
constructor() {
|
||||
this.regex = /^(-?(?:\d+|0x[\d,a-f]+|0b[0-1])\s?)+$/;
|
||||
}
|
||||
|
||||
canCreate (input: string): boolean {
|
||||
return this.regex.test(input);
|
||||
};
|
||||
|
||||
create (input : string) : ExpressionInput {
|
||||
var matches = this.regex.exec(input) as RegExpExecArray;
|
||||
var numbers = [] as NumericOperand[];
|
||||
var input = matches.input;
|
||||
|
||||
input.split(' ').forEach((n: string) => {
|
||||
if(n.trim().length > 0) {
|
||||
numbers.push(NumericOperand.parse(n.trim()));
|
||||
}
|
||||
});
|
||||
|
||||
return new ListOfNumbersExpression(input, numbers);
|
||||
}
|
||||
}
|
||||
|
||||
class BitwiseOperationExpressionFactory implements IExpressionParserFactory {
|
||||
fullRegex: RegExp;
|
||||
regex: RegExp;
|
||||
|
||||
constructor() {
|
||||
this.fullRegex = /^((<<|>>|>>>|\||\&|\^)?(~?-?([b,x,a-f,0-9]+)))+$/;
|
||||
this.regex = /(<<|>>|>>>|\||\&|\^)?(~?-?(?:[b,x,a-f,0-9]+))/g;
|
||||
}
|
||||
|
||||
canCreate (input: string) : boolean {
|
||||
this.fullRegex.lastIndex = 0;
|
||||
return this.fullRegex.test(this.normalizeString(input));
|
||||
};
|
||||
|
||||
create (input: string) : ExpressionInput {
|
||||
var m, operands : ExpressionInputItem[] = [],
|
||||
normalizedString = this.normalizeString(input);
|
||||
|
||||
while ((m = this.regex.exec(normalizedString)) != null) {
|
||||
operands.push(this.parseMatch(m));
|
||||
}
|
||||
|
||||
return new BitwiseOperationExpression(normalizedString, operands)
|
||||
};
|
||||
|
||||
parseMatch (m:any): ExpressionInputItem {
|
||||
var input = m[0],
|
||||
sign = m[1],
|
||||
num = m[2];
|
||||
|
||||
var parsed = null;
|
||||
if(num.indexOf('~') == '0') {
|
||||
parsed = new ExpressionOperand(num, NumericOperand.parse(num.substring(1)), '~');
|
||||
}
|
||||
else {
|
||||
parsed = NumericOperand.parse(num);
|
||||
}
|
||||
|
||||
if(sign == null) {
|
||||
return parsed as ExpressionOperand;
|
||||
} else {
|
||||
return new ExpressionOperand(input, parsed as NumericOperand, sign);
|
||||
}
|
||||
};
|
||||
|
||||
normalizeString (input : string): string {
|
||||
return input.replace(/\s+/g,'');
|
||||
};
|
||||
}
|
||||
|
||||
var parser = new ExpressionParser();
|
||||
parser.addFactory(new ListOfNumbersExpressionFactory());
|
||||
parser.addFactory(new BitwiseOperationExpressionFactory());
|
||||
|
||||
export {parser};
|
||||
38
src/expression/numberParser.tests.ts
Normal file
38
src/expression/numberParser.tests.ts
Normal file
@@ -0,0 +1,38 @@
|
||||
import {numberParser, ParsedNumber} from './numberParser';
|
||||
|
||||
describe("parser", () => {
|
||||
it('parses decimal number', () => {
|
||||
const result = numberParser.parse('10');
|
||||
expect(result).not.toBeNull();
|
||||
|
||||
var number = result as ParsedNumber;
|
||||
expect(number.value).toBe(10);
|
||||
expect(number.base).toBe('dec');
|
||||
expect(number.input).toBe('10');
|
||||
});
|
||||
|
||||
it('parses hex number', () => {
|
||||
const result = numberParser.parse('0xab');
|
||||
expect(result).not.toBeNull();
|
||||
|
||||
var number = result as ParsedNumber;
|
||||
expect(number.value).toBe(171);
|
||||
expect(number.base).toBe('hex');
|
||||
expect(number.input).toBe('0xab');
|
||||
});
|
||||
|
||||
it('parses bin number', () => {
|
||||
var result = numberParser.parse('0b0110');
|
||||
expect(result).not.toBeNull();
|
||||
|
||||
var number = result as ParsedNumber;
|
||||
expect(number.value).toBe(6);
|
||||
expect(number.base).toBe('bin');
|
||||
expect(number.input).toBe('0b0110');
|
||||
});
|
||||
|
||||
it('returns null on bad inputs', () => {
|
||||
expect(numberParser.parse('abc')).toBeNull();
|
||||
expect(numberParser.parse('')).toBeNull();
|
||||
});
|
||||
});
|
||||
68
src/expression/numberParser.ts
Normal file
68
src/expression/numberParser.ts
Normal file
@@ -0,0 +1,68 @@
|
||||
import { NumberBase } from "./expression-interfaces";
|
||||
|
||||
const decimalRegex = /^-?\d+$/;
|
||||
const hexRegex = /^-?0x[0-9,a-f]+$/i;
|
||||
const binRegex = /^-?0b[0-1]+$/i;
|
||||
const operatorRegex = /^<<|>>|<<<|\&|\|\^|~$/;
|
||||
|
||||
interface ParserConfig {
|
||||
regex: RegExp,
|
||||
radix: number,
|
||||
base: NumberBase,
|
||||
prefix: string|RegExp
|
||||
}
|
||||
|
||||
export interface ParsedNumber {
|
||||
value: number;
|
||||
base: NumberBase;
|
||||
input: string;
|
||||
}
|
||||
|
||||
var knownParsers : ParserConfig[] = [
|
||||
{ regex: decimalRegex, radix: 10, base: 'dec', prefix: '^$' },
|
||||
{ regex: hexRegex, radix: 16, base: 'hex', prefix:/0x/i },
|
||||
{ regex: binRegex, radix: 2, base: 'bin', prefix:/0b/i }];
|
||||
|
||||
|
||||
class NumberParser {
|
||||
|
||||
parsers: ParserConfig[];
|
||||
|
||||
constructor(parsers: ParserConfig[])
|
||||
{
|
||||
this.parsers = parsers;
|
||||
}
|
||||
|
||||
parse (input : string) : ParsedNumber | null {
|
||||
return this.parsers.map(p => this.applyParser(p, input)).reduce((c, n) => c || n);
|
||||
};
|
||||
|
||||
parseOperator (input: string) : string | null {
|
||||
var m = input.match(input);
|
||||
|
||||
if(m == null || m.length == 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return m[0];
|
||||
};
|
||||
|
||||
applyParser(parser : ParserConfig, rawInput: string) : ParsedNumber | null {
|
||||
|
||||
if(!parser.regex.test(rawInput)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
var value = parseInt(rawInput.replace(parser.prefix, ''), parser.radix);
|
||||
|
||||
return {
|
||||
value: value,
|
||||
base: parser.base,
|
||||
input: rawInput
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const numberParser = new NumberParser(knownParsers);
|
||||
|
||||
export {numberParser};
|
||||
@@ -54,17 +54,17 @@ code { font-size: 1.2em; font-weight: bold; }
|
||||
.cur { color: lightgray; }
|
||||
|
||||
.icon { width: 16px; height: 16px; display: inline-block; }
|
||||
.light .twitter { background: url('../img/twitter-light.png') }
|
||||
.dark .twitter { background: url('../img/twitter-dark.png') }
|
||||
.midnight .twitter { background: url('../img/twitter-dark.png') }
|
||||
.light .twitter { background: url('./img/twitter-light.png') }
|
||||
.dark .twitter { background: url('./img/twitter-dark.png') }
|
||||
.midnight .twitter { background: url('./img/twitter-dark.png') }
|
||||
|
||||
.light .feedback { background: url('../img/feedback-light.png') }
|
||||
.dark .feedback { background: url('../img/feedback-dark.png') }
|
||||
.midnight .feedback { background: url('../img/feedback-dark.png') }
|
||||
.light .feedback { background: url('./img/feedback-light.png') }
|
||||
.dark .feedback { background: url('./img/feedback-dark.png') }
|
||||
.midnight .feedback { background: url('./img/feedback-dark.png') }
|
||||
|
||||
.light .github { background: url('../img/github-light.png') }
|
||||
.dark .github { background: url('../img/github-dark.png') }
|
||||
.midnight .github { background: url('../img/github-dark.png') }
|
||||
.light .github { background: url('./img/github-light.png') }
|
||||
.dark .github { background: url('./img/github-dark.png') }
|
||||
.midnight .github { background: url('./img/github-dark.png') }
|
||||
|
||||
/* Light */
|
||||
.light { background: #fafafa; }
|
||||
@@ -1,39 +0,0 @@
|
||||
<!DOCTYPE>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="description" content="Free Text Online Bitwise Calculator">
|
||||
|
||||
<title>BitwiseCmd</title>
|
||||
<link rel="shortcut icon" href="http://bitwisecmd.com/favicon.ico">
|
||||
<link rel="stylesheet" type="text/css" href="css/styles.css" />
|
||||
|
||||
<meta property="og:url" content="http://bitwisecmd.com" />
|
||||
<meta property="og:type" content="website" />
|
||||
<meta property="og:title" content="BitwiseCmd" />
|
||||
<meta property="og:description" content="Free Text Online Bitwise Calculator" />
|
||||
<meta property="og:image" content="http://bitwisecmd.com/img/social-avatar.png" />
|
||||
</head>
|
||||
<body>
|
||||
<div id="root">
|
||||
<p>Ooops... Something went wrong.</p>
|
||||
<p>Either you have an old browser or I screwed something up ¯\_(ツ)_/¯</p>
|
||||
<p>No worries! There is a still old version avaliable at <a href="http://bitwisecmd.com/old">http://bitwisecmd.com/old</a>.</p>
|
||||
</div>
|
||||
<script src="bundle.js"></script>
|
||||
<script src="analytics.js"></script>
|
||||
|
||||
<div id="fb-root"></div>
|
||||
<script>(function(d, s, id) {
|
||||
var js, fjs = d.getElementsByTagName(s)[0];
|
||||
if (d.getElementById(id)) return;
|
||||
js = d.createElement(s); js.id = id;
|
||||
js.src = "//connect.facebook.net/en_US/sdk.js#xfbml=1&version=v2.5";
|
||||
fjs.parentNode.insertBefore(js, fjs);
|
||||
}(document, 'script', 'facebook-jssdk'));</script>
|
||||
|
||||
<div class="social-container">
|
||||
<div class="fb-like" data-href="http://bitwisecmd.com" data-layout="box_count" data-action="like" data-show-faces="false" data-share="true"></div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,17 +1,18 @@
|
||||
import AppState from './core/AppState';
|
||||
import appStateStore from './core/appStateStore';
|
||||
import cmd from './core/cmd';
|
||||
import commands from './commands';
|
||||
import AppRoot from './AppRoot';
|
||||
import hash from './core/hash';
|
||||
import log from 'loglevel';
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import InputBox from './components/InputBox.jsx';
|
||||
import AppState from './AppState';
|
||||
import appStateStore from './appStateStore';
|
||||
import cmd from './cmd';
|
||||
import commands from './commands';
|
||||
import AppRoot from './components/AppRoot';
|
||||
import hash from './hash';
|
||||
import log from 'loglevel';
|
||||
import './index.css';
|
||||
|
||||
setupLogger();
|
||||
const env = window.location.host === "bitwisecmd.com" ? 'prod' : 'stage';
|
||||
setupLogger(env);
|
||||
|
||||
const appState = createAppState();
|
||||
const appState = createAppState(env);
|
||||
|
||||
commands.initialize(cmd, appState);
|
||||
|
||||
@@ -22,17 +23,18 @@ ReactDOM.render(root, document.getElementById('root'));
|
||||
|
||||
log.debug("started");
|
||||
|
||||
function createAppState() {
|
||||
function createAppState(env:string) {
|
||||
var stateData = appStateStore.getPersistedData();
|
||||
const appState = new AppState(stateData);
|
||||
const appState = new AppState(stateData, env);
|
||||
appStateStore.watch(appState);
|
||||
log.debug("appState", appState);
|
||||
log.debug("appState initialized", appState);
|
||||
return appState;
|
||||
}
|
||||
|
||||
function setupLogger() {
|
||||
if(window.location.host != 'bitwisecmd.com' || window.location.hash.indexOf('-debug') > -1) {
|
||||
log.setLevel("trace");
|
||||
function setupLogger(env: Env) {
|
||||
if(env != 'prod'){
|
||||
log.setLevel("debug");
|
||||
log.debug(`Log level is set to debug. Env: ${env}`)
|
||||
} else {
|
||||
log.setLevel("warn");
|
||||
}
|
||||
@@ -47,9 +49,11 @@ function executeStartupCommands() {
|
||||
startupCommands = ["whatsnew"];
|
||||
}
|
||||
|
||||
if(hashArgs.commands.length > 0) {
|
||||
startupCommands = hashArgs.commands;
|
||||
if(hashArgs.length > 0) {
|
||||
startupCommands = hashArgs;
|
||||
}
|
||||
|
||||
startupCommands.forEach(cmd.execute.bind(cmd));
|
||||
}
|
||||
|
||||
type Env = 'prod' | 'stage';
|
||||
@@ -1,7 +1,7 @@
|
||||
import CommandResult from './CommandResult';
|
||||
|
||||
export default class AboutResult extends CommandResult {
|
||||
constructor(input) {
|
||||
constructor(input:string) {
|
||||
super(input);
|
||||
}
|
||||
}
|
||||
13
src/models/CommandResult.ts
Normal file
13
src/models/CommandResult.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
export default class CommandResult {
|
||||
input: string;
|
||||
inputHash: string;
|
||||
|
||||
constructor(input: string) {
|
||||
this.input = input;
|
||||
this.inputHash = this.encodeHash(input);
|
||||
}
|
||||
|
||||
encodeHash (input: string) {
|
||||
return encodeURI(input.trim().replace(/\s/g,','));
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,8 @@
|
||||
import CommandResult from './CommandResult';
|
||||
|
||||
export default class ErrorResult extends CommandResult {
|
||||
constructor(input, error) {
|
||||
error: Error;
|
||||
constructor(input: string, error : Error) {
|
||||
super(input);
|
||||
this.error = error;
|
||||
}
|
||||
@@ -1,7 +1,9 @@
|
||||
import CommandResult from './CommandResult';
|
||||
import { ExpressionInput } from '../expression/expression-interfaces';
|
||||
|
||||
export default class ExpressionResult extends CommandResult {
|
||||
constructor(input, expression) {
|
||||
expression: ExpressionInput;
|
||||
constructor(input: string, expression: ExpressionInput) {
|
||||
super(input);
|
||||
this.expression = expression;
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
import CommandResult from './CommandResult';
|
||||
|
||||
export default class HelpResult extends CommandResult {
|
||||
constructor(input) {
|
||||
constructor(input: string) {
|
||||
super(input);
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,9 @@
|
||||
import CommandResult from './CommandResult';
|
||||
|
||||
export default class StringResult extends CommandResult {
|
||||
constructor(input, text) {
|
||||
value:string;
|
||||
constructor(input: string, value : string) {
|
||||
super(input);
|
||||
this.value = text;
|
||||
this.value = value;
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,8 @@
|
||||
import CommandResult from './CommandResult';
|
||||
|
||||
export default class UnknownCommandResult extends CommandResult {
|
||||
constructor(input) {
|
||||
message:string;
|
||||
constructor(input : string) {
|
||||
super(input);
|
||||
this.message = `Sorry, i don''t know what ${input} is :(`;
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
import CommandResult from './CommandResult';
|
||||
|
||||
export default class WhatsnewResult extends CommandResult {
|
||||
constructor(input) {
|
||||
constructor(input: string) {
|
||||
super(input);
|
||||
}
|
||||
}
|
||||
1
src/react-app-env.d.ts
vendored
Normal file
1
src/react-app-env.d.ts
vendored
Normal file
@@ -0,0 +1 @@
|
||||
/// <reference types="react-scripts" />
|
||||
@@ -1,13 +0,0 @@
|
||||
exports.config = {
|
||||
seleniumAddress: 'http://127.0.0.1:4444/wd/hub',
|
||||
|
||||
specs: [
|
||||
'./e2e/spec.js'
|
||||
],
|
||||
|
||||
multiCapabilities: [{
|
||||
'browserName': 'chrome'
|
||||
},{
|
||||
'browserName': 'firefox'
|
||||
}]
|
||||
};
|
||||
@@ -1,11 +0,0 @@
|
||||
exports.config = {
|
||||
seleniumAddress: 'http://127.0.0.1:4444/wd/hub',
|
||||
|
||||
specs: [
|
||||
'./e2e/spec.js'
|
||||
],
|
||||
|
||||
capabilities: {
|
||||
'browserName': 'chrome'
|
||||
}
|
||||
};
|
||||
@@ -1,120 +0,0 @@
|
||||
var Key = protractor.Key;
|
||||
var By = protractor.By;
|
||||
var resultTableReader = require('./resultTableReader');
|
||||
|
||||
function BitwiseCmdPage(driver, appUrl) {
|
||||
this.driver = driver;
|
||||
this.appUrl = appUrl;
|
||||
}
|
||||
|
||||
BitwiseCmdPage.prototype.goToApp = function (hashValue) {
|
||||
var url = this.appUrl;
|
||||
var hash = hashValue || '-notrack';
|
||||
|
||||
if(hash.indexOf('-notrack') < 0) {
|
||||
hash += "||-notrack";
|
||||
}
|
||||
|
||||
if(url.indexOf("#") < 0) {
|
||||
url += "#" + hash;
|
||||
} else {
|
||||
url += "||" + hash;
|
||||
}
|
||||
|
||||
return this.driver.get(url);
|
||||
};
|
||||
|
||||
BitwiseCmdPage.prototype.sendCommand = function(cmd) {
|
||||
return this.driver.findElement(By.id('in')).then(function (el) {
|
||||
return el.sendKeys(cmd + Key.ENTER);
|
||||
});
|
||||
};
|
||||
|
||||
BitwiseCmdPage.prototype.clearResults = function () {
|
||||
return this.sendCommand("clear");
|
||||
};
|
||||
|
||||
BitwiseCmdPage.prototype.executeExpression = function(expr) {
|
||||
var self = this;
|
||||
return this.clearResults().then(function() {
|
||||
return self.sendCommand(expr);
|
||||
})
|
||||
};
|
||||
|
||||
BitwiseCmdPage.prototype.getLasExpressionResult = function() {
|
||||
return this.driver.findElement(By.css('.expression')).then(function(resultElement) {
|
||||
return new ExpressionResultObject(resultElement);
|
||||
});
|
||||
};
|
||||
|
||||
BitwiseCmdPage.prototype.getAllResults = function() {
|
||||
return this.driver.findElements(By.css('.result')).then(function(resultElements) {
|
||||
var results = [], i= 0, len = resultElements.length;
|
||||
|
||||
// TODO: Use _.map
|
||||
for(;i<len;i++) {
|
||||
results.push(new ExpressionResultObject(resultElements[i]));
|
||||
}
|
||||
return results;
|
||||
});
|
||||
};
|
||||
|
||||
BitwiseCmdPage.prototype.shouldHaveNoErrors = function () {
|
||||
return this.driver.findElements(By.css('.result .error')).then(function(els) {
|
||||
expect(els.length).toBe(0, "There should be no errors");
|
||||
});
|
||||
};
|
||||
|
||||
function ExpressionResultObject(resultElement) {
|
||||
this.resultElement = resultElement;
|
||||
}
|
||||
|
||||
ExpressionResultObject.prototype.readResult = function () {
|
||||
return resultTableReader.read(this.resultElement);
|
||||
};
|
||||
|
||||
ExpressionResultObject.prototype.shouldBe = function(expectedResult) {
|
||||
this.readResult().then(function(actualResult) {
|
||||
expect(actualResult.length).toEqual(expectedResult.length, "Unexpected result length");
|
||||
var expected, actual;
|
||||
for(var i=0;i<expectedResult.length; i++) {
|
||||
var actual = actualResult[i],
|
||||
expected = convertToExpected(expectedResult[i]);
|
||||
//console.log("actual.bin.length=" + actual.bin.length)
|
||||
expect(actual).toEqual(jasmine.objectContaining(expected));
|
||||
}
|
||||
});
|
||||
|
||||
};
|
||||
|
||||
function convertToExpected(arg) {
|
||||
if(arg.length) {
|
||||
return convertExpectedFromArray(arg);
|
||||
}
|
||||
|
||||
return arg;
|
||||
}
|
||||
|
||||
function convertExpectedFromArray(arg) {
|
||||
|
||||
var start = 0;
|
||||
if(arg.length == 4) {
|
||||
start = 1
|
||||
}
|
||||
|
||||
var obj = {
|
||||
sign: arg.length == 4 ? arg[0] : '',
|
||||
label: arg[start++],
|
||||
bin: arg[start++],
|
||||
other: arg[start++]
|
||||
}
|
||||
|
||||
// console.log('convert: ' + JSON.stringify(arg) + " to " + JSON.stringify(obj));
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
BitwiseCmdPage : BitwiseCmdPage,
|
||||
ExpressionResultObject: ExpressionResultObject
|
||||
};
|
||||
@@ -1,56 +0,0 @@
|
||||
var promise = protractor.promise;
|
||||
var resultTableReader = {
|
||||
read: function (table) {
|
||||
var self = this;
|
||||
return table.findElements(By.tagName('tr'))
|
||||
.then(function(rows) {
|
||||
var promises = [];
|
||||
for (var i = 0; i < rows.length; i++) {
|
||||
promises.push(self.readRow(rows[i]));
|
||||
}
|
||||
|
||||
return promise.all(promises);
|
||||
});
|
||||
},
|
||||
|
||||
readRow: function (row) {
|
||||
var def = promise.defer();
|
||||
var dataList = [];
|
||||
var promises = [];
|
||||
var self = this;
|
||||
|
||||
row.findElements(By.tagName('td')).then(function(cols) {
|
||||
for(var i=0; i<cols.length;i++){
|
||||
promises.push(self.readColumn(cols[i]).then(function(colData) { dataList.push(colData); }))
|
||||
}
|
||||
});
|
||||
|
||||
promise.all(promises).then(function() {
|
||||
var rowObj = {}, colObj;
|
||||
for(var i=0; i<dataList.length; i++ ){
|
||||
colObj = dataList[i];
|
||||
rowObj[colObj.className] = colObj.text;
|
||||
}
|
||||
|
||||
def.fulfill(rowObj);
|
||||
});
|
||||
|
||||
return def.promise;
|
||||
},
|
||||
|
||||
readColumn: function(col) {
|
||||
var def = promise.defer();
|
||||
var colData = {};
|
||||
var promises = [];
|
||||
|
||||
promises.push(col.getAttribute('class').then(function(className) { colData.className = className; }))
|
||||
promises.push(col.getText().then(function(text) { colData.text = text; }))
|
||||
|
||||
promise.all(promises).then(function () {
|
||||
def.fulfill(colData);
|
||||
});
|
||||
|
||||
return def.promise;
|
||||
}
|
||||
};
|
||||
module.exports = resultTableReader;
|
||||
@@ -1,242 +0,0 @@
|
||||
browser.ignoreSynchronization = true;
|
||||
var pageObject = require('./pageObject.js')
|
||||
var BitwiseCmdPage = pageObject.BitwiseCmdPage;
|
||||
|
||||
var By = protractor.By;
|
||||
var driver = browser.driver;
|
||||
var appUrl = browser.params.appUrl || 'http://localhost:63342/BitwiseCmd/src/#clear';
|
||||
var sutPage = new BitwiseCmdPage(driver, appUrl);
|
||||
|
||||
describe('when application starts', function() {
|
||||
it('should have title', function() {
|
||||
sutPage.goToApp().then(function() {
|
||||
expect(driver.getTitle()).toEqual('BitwiseCmd');
|
||||
});
|
||||
});
|
||||
|
||||
it('should have no errors upon loading', function() {
|
||||
return sutPage.shouldHaveNoErrors();
|
||||
});
|
||||
|
||||
it('should execute clear command', function() {
|
||||
sutPage.clearResults().then(function () {
|
||||
return driver.findElements(By.css('.result')).then(function(list) {
|
||||
expect(list.length).toBe(0, "There should be no results after clear");
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('should execute list of commands without errors', function() {
|
||||
sutPage.goToApp()
|
||||
.then(function() { return sutPage.executeExpression('clear')})
|
||||
.then(function() { return sutPage.executeExpression('1')})
|
||||
.then(function() { return sutPage.executeExpression('1|2')})
|
||||
.then(function() { return sutPage.executeExpression('1^2')})
|
||||
.then(function() { return sutPage.executeExpression('1^0b10')})
|
||||
.then(function() { return sutPage.executeExpression('0x1>>>0xf')})
|
||||
.then(function() { return sutPage.executeExpression('0x1 0xf')})
|
||||
.then(function() { return sutPage.executeExpression('0x1 | 0xf')})
|
||||
.then(function() { return sutPage.executeExpression('0x1 ^ 123')})
|
||||
.then(function() { return sutPage.executeExpression('1|2&3|5 |5')})
|
||||
.then(function() { return sutPage.executeExpression('dark')})
|
||||
.then(function() { return sutPage.executeExpression('light')})
|
||||
.then(function() { return sutPage.executeExpression('midnight')})
|
||||
.then(function() { return sutPage.executeExpression('guid')})
|
||||
.then(function() { return sutPage.shouldHaveNoErrors(); });
|
||||
});
|
||||
|
||||
it('should execute list of numbers', function() {
|
||||
assertOperation('3 0xf 0b101',
|
||||
[{ label: '3', bin:'00000011', other: '0x3'},
|
||||
{ label: '0xf', bin:'00001111', other: '15'},
|
||||
{ label: '5', bin: '00000101', other: '0x5' }]);
|
||||
});
|
||||
|
||||
it('should do a shift operation', function() {
|
||||
|
||||
return assertOperation('1<<1',
|
||||
[{ label: '1', bin:'00000001', other: '0x1'},
|
||||
{ sign:'<<1', label: '2', bin:'00000010', other: '0x2'}])
|
||||
});
|
||||
|
||||
it('should do a ignore sign RIGHT shift operation', function() {
|
||||
|
||||
return assertOperation('-1>>>1',
|
||||
[{ label: '-1', bin:'11111111111111111111111111111111', other: '-0x1'},
|
||||
{ sign: '>>>1', label: '2147483647', bin:'01111111111111111111111111111111', other: '0x7fffffff'}])
|
||||
});
|
||||
|
||||
it('should do NOT operation', function() {
|
||||
|
||||
return assertOperation('~1',
|
||||
[{ sign: '~', label: '1', bin:'00000000000000000000000000000001', other: '0x1'},
|
||||
{ sign: '=', label: '-2', bin:'11111111111111111111111111111110', other: '-0x2'}])
|
||||
});
|
||||
|
||||
it('should execute multiple expressions from hash arguments', function() {
|
||||
return sutPage.goToApp("16,15||16&15")
|
||||
.then(function() { return driver.navigate().refresh(); })
|
||||
.then(function() { return sutPage.shouldHaveNoErrors(); })
|
||||
.then(function() {
|
||||
return assertMultipleExpressionResults([
|
||||
//16&15
|
||||
[{ label: '16', bin:'00010000', other: '0x10'},
|
||||
{ sign:'&', label: '15', bin:'00001111', other: '0xf'},
|
||||
{ sign:'=', label: '0', bin:'00000000', other: '0x0'}],
|
||||
|
||||
//16 15
|
||||
[{ label: '16', bin:'00010000', other: '0x10'},
|
||||
{ label: '15', bin:'00001111', other: '0xf'}]
|
||||
])
|
||||
})
|
||||
});
|
||||
|
||||
it('should do OR operation', function() {
|
||||
|
||||
return assertOperation('1|2',
|
||||
[{ label: '1', bin:'00000001', other: '0x1'},
|
||||
{ sign: '|', label: '2', bin:'00000010', other: '0x2'},
|
||||
{ sign: '=', label: '3', bin:'00000011', other: '0x3'}])
|
||||
});
|
||||
|
||||
it('should do multiple operand expression', function() {
|
||||
// actual { sign: '', label: '1', bin: '00000000000000000000000000000001', other: '0x1' }
|
||||
// expected { sign: '', label: '1', bin: '0000000000000000000000000000001', other: '0x1' }
|
||||
return assertOperation(
|
||||
'1|2|4<<0x1|~2',
|
||||
[
|
||||
[ '1', '00000000000000000000000000000001', '0x1'],
|
||||
[ '|', '2', '00000000000000000000000000000010', '0x2'],
|
||||
[ '=', '3', '00000000000000000000000000000011', '0x3'],
|
||||
[ '|', '4', '00000000000000000000000000000100', '0x4'],
|
||||
[ '=', '7', '00000000000000000000000000000111', '0x7'],
|
||||
[ '<<0x1','0xe', '00000000000000000000000000001110', '14' ],
|
||||
[ '|', '~2', '11111111111111111111111111111101', '-3'],
|
||||
[ '=', '-1', '11111111111111111111111111111111', '-0x1']
|
||||
]);
|
||||
});
|
||||
|
||||
it('should support nested not operations', function() {
|
||||
return assertOperation(
|
||||
'~3|~213&~12^~223',
|
||||
[
|
||||
['~', '3', '00000000000000000000000000000011', '0x3'],
|
||||
['=', '-4', '11111111111111111111111111111100', '-0x4'],
|
||||
['|', '~213', '11111111111111111111111100101010', '-214'],
|
||||
['=', '-2', '11111111111111111111111111111110', '-0x2'],
|
||||
['&', '~12', '11111111111111111111111111110011', '-13'],
|
||||
['=', '-14', '11111111111111111111111111110010', '-0xe'],
|
||||
['^', '~223', '11111111111111111111111100100000', '-224'],
|
||||
['=', '210', '00000000000000000000000011010010', '0xd2']
|
||||
]
|
||||
)
|
||||
})
|
||||
|
||||
it('should do or for binary numbers', function() {
|
||||
return assertOperation('0b10|0b11',
|
||||
[[ "2", "00000010", "0x2"],
|
||||
["|", "3", "00000011", "0x3"],
|
||||
["=", "3", "00000011", "0x3"]]
|
||||
);
|
||||
})
|
||||
|
||||
it('should do prefer hex result', function() {
|
||||
|
||||
return assertOperation('1|0x2',
|
||||
[{ label: '1', bin:'00000001', other: '0x1'},
|
||||
{ sign: '|', label: '0x2', bin:'00000010', other: '2'},
|
||||
{ sign: '=', label: '0x3', bin:'00000011', other: '3'}])
|
||||
});
|
||||
|
||||
|
||||
// TODO: temporary disabled due to false positive on prod
|
||||
xit('should create hashlink', function() {
|
||||
var expression = '1|0x2';
|
||||
var expected = [{ label: '1', bin:'00000001', other: '0x1'},
|
||||
{ sign: '|', label: '0x2', bin:'00000010', other: '2'},
|
||||
{ sign: '=', label: '0x3', bin:'00000011', other: '3'}];
|
||||
|
||||
return assertOperation(expression, expected).then(function(){
|
||||
return driver.findElement(By.css('.hashLink'));
|
||||
}).then(function(el) {
|
||||
return el.getAttribute('href');
|
||||
}).then(function(hrefUrl) {
|
||||
console.log('haslink url: ' + hrefUrl);
|
||||
return driver.get(hrefUrl); // TODO: temp solution. Need to implement better tracking handling logic
|
||||
}).then(function() {
|
||||
return driver.findElements(By.css('.result'));
|
||||
}).then(function(list) {
|
||||
expect(list.length).toBeGreaterThan(0);
|
||||
return assertExpressionResult(expected);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
it('should emphasize bytes', function() {
|
||||
|
||||
sutPage.goToApp()
|
||||
.then(function() { return sutPage.executeExpression('1')})
|
||||
.then(function() {
|
||||
return assertExpressionResult([{ label: '1', bin:'00000001', other: '0x1'}])
|
||||
})
|
||||
.then(function() { return sutPage.executeExpression('clear')})
|
||||
.then(function() { return sutPage.executeExpression('em')})
|
||||
.then(function() { return sutPage.shouldHaveNoErrors(); })
|
||||
.then(function() { return sutPage.executeExpression('1 3')})
|
||||
.then(function() {
|
||||
return assertExpressionResult([{ label: '1', bin:'01', other: '0x1'}, { label: '3', bin:'11', other: '0x3'}])
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('interaction with results', function() {
|
||||
it('should flip bits', function () {
|
||||
|
||||
// Given: 0x2a 00101010 42
|
||||
// Expected: 0x6a 01101010 106
|
||||
|
||||
sutPage.goToApp()
|
||||
.then(function() { return sutPage.executeExpression('0x2a')})
|
||||
.then(function() { assertExpressionResult([{ label: "0x2a", bin: '00101010', other: '42' }]); })
|
||||
.then(function() { return flipBit(2); })
|
||||
.then(function() { return assertExpressionResult([{ label: "0x6a", bin: '01101010', other: '106' }]); })
|
||||
.then(function() { return flipBit(6); })
|
||||
.then(function() { return assertExpressionResult([{ label: "0x6e", bin: '01101110', other: '110' }]); });
|
||||
})
|
||||
});
|
||||
|
||||
function assertMultipleExpressionResults(array) {
|
||||
return sutPage.getAllResults().then(function(results){
|
||||
var all= null, cur;
|
||||
for(var i=0; i<results.length;i++) {
|
||||
var expected = array[i];
|
||||
cur = results[i].shouldBe(expected);
|
||||
all = all == null ? cur : all.then(cur);
|
||||
}
|
||||
|
||||
return all;
|
||||
});
|
||||
}
|
||||
|
||||
function assertExpressionResult(expected) {
|
||||
return sutPage.getLasExpressionResult().then(function(result){
|
||||
result.shouldBe(expected);
|
||||
});
|
||||
}
|
||||
|
||||
function assertOperation(op, expected) {
|
||||
console.log('\n' + op);
|
||||
return sutPage.executeExpression(op)
|
||||
.then(function() { return sutPage.shouldHaveNoErrors(); })
|
||||
.then(function() {
|
||||
return assertExpressionResult(expected)
|
||||
});
|
||||
}
|
||||
|
||||
function flipBit(bitNumber) {
|
||||
return driver.findElements(By.css('.flipable'))
|
||||
.then(function(rows) {
|
||||
var bitElemet = rows[bitNumber-1]; // Flip 2nd bit;
|
||||
return bitElemet.click();
|
||||
});
|
||||
}
|
||||
@@ -1,143 +0,0 @@
|
||||
var expression = require('../../src/app/expression');
|
||||
var parser = expression.parser;
|
||||
|
||||
describe("expression parser", function() {
|
||||
|
||||
var shouldParse = ['0x2>>1', '1 2 3', '0x1 1 2 3 5', '0x1>>0x2', '1|2', '-9', '-1|-2', '~3'];
|
||||
it("should be able to parse", function() {
|
||||
shouldParse.forEach(function(expr) {
|
||||
expect(parser.canParse(expr)).toBe(true, 'expr: ' + expr);
|
||||
})
|
||||
});
|
||||
|
||||
var expressionCases = {
|
||||
"0x2>>1": { operand: 2, operand2:1, "sign":">>", string:"0x2>>1" },
|
||||
"123|111": { operand: 123, operand2:111, "sign":"|", string: "123|111" },
|
||||
"23^0x1": { operand: 23, operand2:1, "sign":"^", string: "23^0x1" },
|
||||
"0xf>>0xa": { operand: 15, operand2:10, "sign":">>", string:"0xf>>0xa" },
|
||||
"0x10&0x11": { operand: 0x10, operand2:0x11, "sign":"&", string:"0x10&0x11" },
|
||||
"0x1a^11": { operand: 0x1a, operand2:11, "sign":"^", string:"0x1a^11" },
|
||||
"0x1a>>>11": { operand: 0x1a, operand2:11, "sign":">>>", string:"0x1a>>>11" },
|
||||
'~3': { operand: 3, operand2: null, "sign":"~", string:"~3" },
|
||||
'~0xa': { operand: 0xa, operand2: null, "sign":"~", string:"~0xa" },
|
||||
'~-0xa': { operand: -0xa, operand2: null, "sign":"~", string:"~-0xa" }
|
||||
};
|
||||
|
||||
// TODO: update to support multiple expressions
|
||||
xit("should parse expressions", function() {
|
||||
|
||||
for(input in expressionCases) {
|
||||
console.log('case: ' + input);
|
||||
var actual = parser.parse(input);
|
||||
var expected = expressionCases[input];
|
||||
|
||||
console.log('actual:' + actual.toString());
|
||||
|
||||
// expect(actual).toBeDefined();
|
||||
// expect(actual).not.toBe(null);
|
||||
// expect(actual.sign).toBe(expected.sign);
|
||||
// expect(actual.operand.value).toBe(expected.operand);
|
||||
|
||||
// if(expected.operand2 != null) {
|
||||
// expect(actual.operand2.value).toBe(expected.operand2);
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// expect(actual.operand2).not.toBeDefined();
|
||||
// }
|
||||
|
||||
// expect(actual.expressionString).toBe(expected.string);
|
||||
// console.log(actual.toString());
|
||||
}
|
||||
});
|
||||
|
||||
var listCases = {
|
||||
'-0xa -9' : [-10, -9],
|
||||
'1 2 3': [1, 2, 3],
|
||||
'0x1 2 0xa6b': [0x1, 2, 0xa6b],
|
||||
'0x11a': [0x11a]
|
||||
};
|
||||
|
||||
it("should parse hexadecimal expressions", function() {
|
||||
var input, i;
|
||||
for(input in listCases) {
|
||||
var actual = parser.parse(input);
|
||||
var expected = listCases[input];
|
||||
|
||||
for(i =0; i<expected.length;i++) {
|
||||
expect(actual.numbers[i].value).toBe(expected[i]);
|
||||
expect(actual.expressionString).toBe(input)
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
it ("should parse multiple operands expression", function () {
|
||||
var actual = parser.parse("1|2&3");
|
||||
})
|
||||
});
|
||||
|
||||
describe('parse operands', function() {
|
||||
|
||||
var hexOperand = parser.parseOperand('0x10');
|
||||
var decOperand = parser.parseOperand('10');
|
||||
rundOperandsTest(hexOperand, decOperand);
|
||||
});
|
||||
|
||||
describe('create operands', function() {
|
||||
|
||||
var hexOperand = parser.createOperand(0x10, 'hex');
|
||||
var decOperand = parser.createOperand(10, 'dec');
|
||||
rundOperandsTest(hexOperand, decOperand);
|
||||
});
|
||||
|
||||
describe('negative operands', function () {
|
||||
var op = parser.parseOperand('-0xa');
|
||||
it('shoold have correct values', function() {
|
||||
expect(op.value).toBe(-10);
|
||||
expect(op.toHexString()).toBe('-0xa');
|
||||
expect(op.toBinaryString()).toBe('11111111111111111111111111110110');
|
||||
expect(op.toDecimalString()).toBe('-10');
|
||||
expect(op.kind).toBe('hex');
|
||||
})
|
||||
});
|
||||
|
||||
describe('should format to kind strings', function() {
|
||||
var dec = expression.Operand.toKindString(15, 'dec'),
|
||||
hexNegative = expression.Operand.toKindString(-2, 'hex'),
|
||||
hex = expression.Operand.toKindString(11, 'hex'),
|
||||
bin = expression.Operand.toKindString(10, 'bin');
|
||||
|
||||
it('should be correctly formatted', function() {
|
||||
expect(dec).toBe('15');
|
||||
expect(hexNegative).toBe('-0x2');
|
||||
expect(hex).toBe('0xb');
|
||||
expect(bin).toBe('1010')
|
||||
});
|
||||
});
|
||||
|
||||
function rundOperandsTest(hexOperand, decOperand) {
|
||||
it('should remember input form', function() {
|
||||
expect(hexOperand.input).toBe('0x10');
|
||||
expect(decOperand.input).toBe('10');
|
||||
});
|
||||
|
||||
it('should return integer value', function () {
|
||||
expect(hexOperand.value).toBe(0x10);
|
||||
expect(decOperand.value).toBe(10);
|
||||
});
|
||||
|
||||
it('should have all kinds', function () {
|
||||
|
||||
expect(hexOperand.kind).toBe('hex');
|
||||
expect(hexOperand.toDecimalString()).toBe('16');
|
||||
expect(hexOperand.toBinaryString()).toBe('10000');
|
||||
expect(hexOperand.toHexString()).toBe('0x10');
|
||||
expect(hexOperand.toOtherKindString()).toBe('16');
|
||||
|
||||
expect(decOperand.kind).toBe('dec');
|
||||
expect(decOperand.toDecimalString()).toBe('10');
|
||||
expect(decOperand.toBinaryString()).toBe('1010');
|
||||
expect(decOperand.toHexString()).toBe('0xa');
|
||||
expect(decOperand.toOtherKindString()).toBe('0xa');
|
||||
});
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
var formatter = require('../../src/app/formatter');
|
||||
|
||||
describe('expression formatter', function () {
|
||||
|
||||
xit('should format number to binary by default', function() {
|
||||
expect(formatter.formatString(10)).toBe("1010");
|
||||
});
|
||||
|
||||
xit('should format number hexadecimal', function() {
|
||||
expect(formatter.formatString(15, 'hex')).toBe("f");
|
||||
});
|
||||
|
||||
xit('should format number decimal', function() {
|
||||
expect(formatter.formatString(16, 'dec')).toBe('16');
|
||||
});
|
||||
|
||||
xit('should respect padding', function() {
|
||||
expect(formatter.padLeft("a", 6)).toBe("00000a");
|
||||
});
|
||||
});
|
||||
@@ -1,39 +0,0 @@
|
||||
var hash = require('../../src/app/hash').default;
|
||||
|
||||
describe('hash arguments parser', function() {
|
||||
|
||||
it('should parse empty', function() {
|
||||
var args = hash.getArgs('');
|
||||
expect(args).not.toBe(null);
|
||||
expect(args).toBeDefined();
|
||||
expect(args.commands).toEqual([]);
|
||||
});
|
||||
|
||||
it('should parse single command', function() {
|
||||
var args = hash.getArgs('#cmd');
|
||||
expect(args).not.toBe(null);
|
||||
expect(args).toBeDefined();
|
||||
expect(args.commands).toEqual(['cmd']);
|
||||
});
|
||||
|
||||
it('should parse multiple commands', function() {
|
||||
var args = hash.getArgs('#1|2||1^2||~2');
|
||||
expect(args).not.toBe(null);
|
||||
expect(args).toBeDefined();
|
||||
expect(args.commands).toEqual(['1|2', '1^2', '~2']);
|
||||
});
|
||||
|
||||
it('should parse multiple commands with clear', function() {
|
||||
var args = hash.getArgs('#clear||16,15||16&15');
|
||||
expect(args).not.toBe(null);
|
||||
expect(args).toBeDefined();
|
||||
expect(args.commands).toEqual(['clear', '16 15', '16&15']);
|
||||
});
|
||||
|
||||
it('should parse multiple commands encoded', function() {
|
||||
var args = hash.getArgs('#' + encodeURI('1|2||1^2||~2||-notrack||-debug'));
|
||||
expect(args).not.toBe(null);
|
||||
expect(args).toBeDefined();
|
||||
expect(args.commands).toEqual(['1|2', '1^2', '~2','-notrack','-debug']);
|
||||
});
|
||||
});
|
||||
@@ -1,18 +0,0 @@
|
||||
var expression = require('../../src/app/expression');
|
||||
var parser = expression.parser;
|
||||
|
||||
describe("operand", function() {
|
||||
it("should be able to create opearand from positive binary string", function() {
|
||||
var input = "0b10";
|
||||
var op = new expression.Operand.parse(input);
|
||||
expect(op.value).toBe(2);
|
||||
expect(op.kind).toBe('bin');
|
||||
});
|
||||
|
||||
it("should be able to create opearand from negative binary string", function() {
|
||||
var input = "-0b10";
|
||||
var op = new expression.Operand.parse(input);
|
||||
expect(op.value).toBe(-2);
|
||||
expect(op.kind).toBe('bin');
|
||||
})
|
||||
});
|
||||
@@ -1,11 +0,0 @@
|
||||
// var expression = require('../../src/app/expression');
|
||||
// var parser = new expression.Parser();
|
||||
|
||||
// describe("Parser", function() {
|
||||
|
||||
// it("should be able to parse from start", function() {
|
||||
// var sut = new expression.Parser("test", 0);
|
||||
// sut.parse();
|
||||
// console.log(sut);
|
||||
// });
|
||||
// });
|
||||
@@ -1,14 +1,25 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"outDir": "./dist/",
|
||||
"sourceMap": true,
|
||||
"noImplicitAny": true,
|
||||
"module": "commonjs",
|
||||
"target": "es5",
|
||||
"jsx": "react"
|
||||
"lib": [
|
||||
"dom",
|
||||
"dom.iterable",
|
||||
"esnext"
|
||||
],
|
||||
"allowJs": true,
|
||||
"skipLibCheck": true,
|
||||
"esModuleInterop": true,
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"strict": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"module": "esnext",
|
||||
"moduleResolution": "node",
|
||||
"resolveJsonModule": true,
|
||||
"isolatedModules": true,
|
||||
"noEmit": true,
|
||||
"jsx": "preserve"
|
||||
},
|
||||
"files": [
|
||||
"./src.old/components/Hello.tsx",
|
||||
"./src.old/index.tsx"
|
||||
]
|
||||
"include": [
|
||||
"src"
|
||||
, "public/analytics.js" ]
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
module.exports = {
|
||||
entry: __dirname + "/src/app/index.jsx",
|
||||
output: {
|
||||
filename: 'bundle.js',
|
||||
path: __dirname + '/src',
|
||||
publicPath: 'http://localhost:8080/'
|
||||
},
|
||||
devtool: 'source-maps',
|
||||
module: {
|
||||
loaders: [{
|
||||
test: /\.jsx?$/,
|
||||
loader: 'babel-loader',
|
||||
exclude: /node-modules/,
|
||||
//output: { path: __dirname + '/src', filename: 'bundle.js' },
|
||||
query: {
|
||||
presets: [require.resolve('babel-preset-es2015'), require.resolve('babel-preset-react')],
|
||||
plugins: [require.resolve('babel-plugin-transform-class-properties')]
|
||||
},
|
||||
}]
|
||||
},
|
||||
// externals: {
|
||||
// //don't bundle the 'react' npm package with our bundle.js
|
||||
// //but get it from a global 'React' variable
|
||||
// 'react': 'React'
|
||||
// },
|
||||
resolve: {
|
||||
extensions: ['.js', '.jsx']
|
||||
}
|
||||
};
|
||||
Reference in New Issue
Block a user