6
.gitignore
vendored
@@ -1,3 +1,9 @@
|
|||||||
.idea/
|
.idea/
|
||||||
node_modules
|
node_modules
|
||||||
build
|
build
|
||||||
|
BitwiseCmdPages/
|
||||||
|
.DS_Store
|
||||||
|
bundle.js
|
||||||
|
bundle.js.map
|
||||||
|
npm-debug.log
|
||||||
|
react/
|
||||||
|
|||||||
3
.vscode/settings.json
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"typescript.check.workspaceVersion": false
|
||||||
|
}
|
||||||
85
Gruntfile.js
@@ -1,85 +0,0 @@
|
|||||||
module.exports = function(grunt) {
|
|
||||||
|
|
||||||
// Project configuration.
|
|
||||||
grunt.initConfig({
|
|
||||||
pkg: grunt.file.readJSON('package.json'),
|
|
||||||
uglify: {
|
|
||||||
options: {
|
|
||||||
banner: '/*! <%= pkg.name %> <%= grunt.template.today("yyyy-mm-dd") %> */\n'
|
|
||||||
},
|
|
||||||
build: {
|
|
||||||
files: {
|
|
||||||
'build/js/bitwisecmd.js': [
|
|
||||||
'src/js/core/core.js',
|
|
||||||
'src/js/core/is.js',
|
|
||||||
'src/js/core/should.js',
|
|
||||||
'src/js/core/di.js',
|
|
||||||
'src/js/core/appShell.js',
|
|
||||||
'src/js/core/htmlBuilder.js',
|
|
||||||
'src/js/core/observable.js',
|
|
||||||
|
|
||||||
'src/js/app.js',
|
|
||||||
|
|
||||||
// TODO: Make components to put their extensions to AppShell instead of app
|
|
||||||
'src/js/components/*.*',
|
|
||||||
'src/js/app/**/*.*'
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
cssmin: {
|
|
||||||
options: {
|
|
||||||
shorthandCompacting: false,
|
|
||||||
roundingPrecision: -1
|
|
||||||
},
|
|
||||||
target: {
|
|
||||||
files: {
|
|
||||||
'build/css/styles.css': ['src/css/styles.css']
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
copy: {
|
|
||||||
main: {
|
|
||||||
files: [{
|
|
||||||
src: 'src/*.*',
|
|
||||||
dest: 'build/',
|
|
||||||
flatten: true,
|
|
||||||
expand: true
|
|
||||||
}, {
|
|
||||||
src: 'src/img/*.*',
|
|
||||||
dest: 'build/img/',
|
|
||||||
flatten: true,
|
|
||||||
expand: true
|
|
||||||
}, {
|
|
||||||
src: 'src/js/analytics.js',
|
|
||||||
dest:'build/js/',
|
|
||||||
flatten: true,
|
|
||||||
expand: true
|
|
||||||
}]
|
|
||||||
}
|
|
||||||
|
|
||||||
},
|
|
||||||
processhtml: {
|
|
||||||
build: {
|
|
||||||
files: {
|
|
||||||
'build/index.html' : ['build/index.html']
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
clean: ['build/**']
|
|
||||||
});
|
|
||||||
|
|
||||||
// Load the plugin that provides the "uglify" task.
|
|
||||||
grunt.loadNpmTasks('grunt-contrib-uglify');
|
|
||||||
grunt.loadNpmTasks('grunt-contrib-cssmin');
|
|
||||||
grunt.loadNpmTasks('grunt-contrib-copy');
|
|
||||||
grunt.loadNpmTasks('grunt-processhtml');
|
|
||||||
grunt.loadNpmTasks('grunt-contrib-clean');
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Default task(s).
|
|
||||||
grunt.registerTask('default', ['uglify','cssmin','copy', 'processhtml']);
|
|
||||||
|
|
||||||
};
|
|
||||||
@@ -1,6 +1,4 @@
|
|||||||
# BitwiseCmd
|
# BitwiseCmd
|
||||||
[Bitwise Calculator Online](http://bitwisecmd.com/)
|
[Bitwise Calculator Online](http://bitwisecmd.com/)
|
||||||
|
|
||||||
Web App that rolls on wheels that I most certainly had to reinvent myself. Nuff said.
|
Web App that helps better understand how bitwise operations are pefromed by displaying bytes in a way you can actually see what is going on there during AND, OR, XOR or shift operations.
|
||||||
|
|
||||||
It helps better understand how bitwise operations are pefromed by displaying bytes in a way you can actually see what is going on there during AND, OR, XOR or shift operations.
|
|
||||||
|
|||||||
17
e2e.bat
@@ -1,17 +0,0 @@
|
|||||||
@echo off
|
|
||||||
|
|
||||||
SET appUrl="http://localhost:63342/BitwiseCmd/src/#clear"
|
|
||||||
SET confFile=e2e.chrome.js
|
|
||||||
|
|
||||||
FOR %%p IN (%*) DO (
|
|
||||||
IF [%%p] == [all] SET confFile=e2e.all.js
|
|
||||||
IF [%%p] == [build] SET appUrl="http://localhost:63342/BitwiseCmd/build/#clear"
|
|
||||||
IF [%%p] == [deploy] SET appUrl="http://bitwisecmd.com/#clear"
|
|
||||||
)
|
|
||||||
|
|
||||||
@echo on
|
|
||||||
|
|
||||||
echo "appUrl: %appUrl%"
|
|
||||||
echo "confFile: %confFile%"
|
|
||||||
|
|
||||||
protractor tests\%confFile% --params.appUrl=%appUrl%
|
|
||||||
@@ -1,20 +1,40 @@
|
|||||||
module.exports = function(config) {
|
module.exports = function (config) {
|
||||||
config.set({
|
config.set({
|
||||||
frameworks: ['jasmine'],
|
browsers: [ 'Chrome' ],
|
||||||
|
// karma only needs to know about the test bundle
|
||||||
files: [
|
files: [
|
||||||
'src/js/core/core.js',
|
'./tests/unit/**/*.js'
|
||||||
'src/js/core/is.js',
|
],
|
||||||
'src/js/core/di.js',
|
frameworks: [ 'jasmine' ],
|
||||||
'src/js/core/should.js',
|
plugins: [
|
||||||
'src/js/core/htmlBuilder.js',
|
'karma-chrome-launcher',
|
||||||
'src/js/core/should.js',
|
'karma-jasmine',
|
||||||
'src/js/core/appShell.js',
|
'karma-webpack',
|
||||||
'src/js/core/observable.js',
|
],
|
||||||
'src/js/app.js',
|
// run the bundle through the webpack and sourcemap plugins
|
||||||
'src/js/components/*.js',
|
preprocessors: {
|
||||||
'src/js/app/**/*.js',
|
'./tests/unit/**/*.js': [ 'webpack']
|
||||||
'tests/unit/**/*.js'
|
},
|
||||||
]
|
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,
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
35
package.json
@@ -4,7 +4,16 @@
|
|||||||
"description": "Bitwise Operations Console",
|
"description": "Bitwise Operations Console",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"test": "echo \"Error: no test specified\" && exit 1"
|
"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",
|
||||||
|
"serv": "webpack-dev-server --content-base ./src",
|
||||||
|
"e2e": "protractor ./tests/e2e.chrome.js --params.appUrl='http://localhost:8080/#clear'",
|
||||||
|
"e2e:build": "protractor ./tests/e2e.chrome.js --params.appUrl='http://localhost:3000/#clear'",
|
||||||
|
"e2e:remote:react": "protractor ./tests/e2e.chrome.js --params.appUrl='http://bitwisecmd.com/react/#clear||-notrack'",
|
||||||
|
"e2e:remote:old": "protractor ./tests/e2e.chrome.js --params.appUrl='http://bitwisecmd.com/old/#clear||-notrack'",
|
||||||
|
"e2e:remote": "protractor ./tests/e2e.chrome.js --params.appUrl='http://bitwisecmd.com//#clear||-notrack'",
|
||||||
|
"test": "karma start"
|
||||||
},
|
},
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
@@ -17,6 +26,12 @@
|
|||||||
},
|
},
|
||||||
"homepage": "https://github.com/BorysLevytskyi/BitwiseCmd",
|
"homepage": "https://github.com/BorysLevytskyi/BitwiseCmd",
|
||||||
"devDependencies": {
|
"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-clean": "latest",
|
||||||
"grunt-contrib-copy": "latest",
|
"grunt-contrib-copy": "latest",
|
||||||
"grunt-contrib-cssmin": "latest",
|
"grunt-contrib-cssmin": "latest",
|
||||||
@@ -24,6 +39,22 @@
|
|||||||
"grunt-processhtml": "latest",
|
"grunt-processhtml": "latest",
|
||||||
"jasmine": "latest",
|
"jasmine": "latest",
|
||||||
"karma": "latest",
|
"karma": "latest",
|
||||||
"karma-jasmine": "latest"
|
"karma-jasmine": "latest",
|
||||||
|
"karma-webpack": "latest",
|
||||||
|
"source-map-loader": "^0.1.5",
|
||||||
|
"ts-loader": "^1.0.0"
|
||||||
|
},
|
||||||
|
"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"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
103
src.old/css/styles.css
Normal file
@@ -0,0 +1,103 @@
|
|||||||
|
body { font-family: Verdana; font-size: 0.8em; margin: 0; padding: 20px 100px 0 100px; }
|
||||||
|
code { font-size: 1.2em; font-weight: bold; }
|
||||||
|
|
||||||
|
.top-links { position: absolute; right: 10px; top: 10px; list-style-type: none; margin: 0 }
|
||||||
|
.top-links li { float: left; }
|
||||||
|
.top-links a { display: inline-block; padding: 10px 15px}
|
||||||
|
.top-links .icon { margin-right: 5px; }
|
||||||
|
|
||||||
|
.mono { font-family: monospace; font-size: 1.3em }
|
||||||
|
.expressionInput { width: 500px; padding: 3px; border: solid 1px lightgray; }
|
||||||
|
|
||||||
|
.result { margin: 10px 10px 30px; }
|
||||||
|
.result .input { margin-bottom: 10px; }
|
||||||
|
.result .content { padding-left: 10px}
|
||||||
|
.result .cur { margin-right: 5px; }
|
||||||
|
|
||||||
|
.hashLink { text-decoration: none; margin-left: 5px; visibility: hidden }
|
||||||
|
.hashLink:hover { text-decoration: underline; margin-left: 5px; }
|
||||||
|
.input:hover .hashLink { visibility: visible }
|
||||||
|
|
||||||
|
.expression .label { font-weight: bold; padding-right: 5px; text-align: right; }
|
||||||
|
.expression .bin { letter-spacing: 3px; }
|
||||||
|
.expression .flipable { cursor: pointer; opacity: 1 }
|
||||||
|
.expression .flipable:hover { opacity: 0.8 }
|
||||||
|
.expression .byte { margin: 0 3px; }
|
||||||
|
.expression .flipable { cursor: pointer; opacity: 1 }
|
||||||
|
.expression .flipable:hover { opacity: 0.8 }
|
||||||
|
.expression-result td { border-top: dotted 1px gray; }
|
||||||
|
.expression { font-size: 1.5em; font-family: monospace }
|
||||||
|
.expression .prefix { font-weight: normal; display: none; font-size: 0.9em }
|
||||||
|
.expression .other { font-size: 0.9em}
|
||||||
|
.expression .sign { text-align: right}
|
||||||
|
|
||||||
|
.hex .prefix { display: inline; }
|
||||||
|
|
||||||
|
.help { padding: 10px; }
|
||||||
|
.help ul { list-style-type: none; margin: 0; padding: 0; }
|
||||||
|
.help p { margin-top: 0 }
|
||||||
|
|
||||||
|
.indicator { padding: 2px 5px; font-family: monospace; font-size: 1.3em; background: transparent; border: none; cursor: pointer }
|
||||||
|
.error { color: maroon; }
|
||||||
|
|
||||||
|
#view { padding: 10px}
|
||||||
|
|
||||||
|
.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') }
|
||||||
|
|
||||||
|
.light .feedback { background: url('../img/feedback-light.png') }
|
||||||
|
.dark .feedback { background: url('../img/feedback-dark.png') }
|
||||||
|
|
||||||
|
.dark .github { background: url('../img/github-dark.png') }
|
||||||
|
.light .github { background: url('../img/github-light.png') }
|
||||||
|
|
||||||
|
/* Light */
|
||||||
|
.light { background: #fafafa; }
|
||||||
|
.light a, .light a:visited { color: #222; }
|
||||||
|
.light .one { color: black; }
|
||||||
|
.light .zero { color: #888; }
|
||||||
|
.light .indicator { color: #ddd; }
|
||||||
|
.light .on { color: #121212; }
|
||||||
|
.light .prefix { color: #888}
|
||||||
|
.light .other { color: #bbb }
|
||||||
|
.light .hashLink, .light .hashLink:visited { color: #ddd }
|
||||||
|
.light .hashLink:hover { color: #888 }
|
||||||
|
.light ul.top-links li:hover { background: #ddd }
|
||||||
|
|
||||||
|
/* Dark */
|
||||||
|
.dark { background: #121212; color: white;}
|
||||||
|
.dark .expressionInput { background: #121212; color: white; }
|
||||||
|
.dark a, .dark a:visited { color: white; }
|
||||||
|
.dark .indicator { color: #555; }
|
||||||
|
.dark .on { color: white; }
|
||||||
|
.dark .zero { color: #999;}
|
||||||
|
.dark .prefix { color: #999}
|
||||||
|
.dark .other { color: #444;}
|
||||||
|
.dark .hashLink, .dark .hashLink:visited { color: #333 }
|
||||||
|
.dark .hashLink:hover { color: #999 }
|
||||||
|
.dark ul.top-links li:hover { background: #333 }
|
||||||
|
|
||||||
|
/* Top Links Shrink */
|
||||||
|
@media (max-width: 800px) {
|
||||||
|
|
||||||
|
.top-links .link-text { display: none }
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Remove margin space on body. Inline top links with header */
|
||||||
|
@media (max-width: 700px) {
|
||||||
|
body { padding: 10px; }
|
||||||
|
.expressionInput { width: 500px; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Further shrink */
|
||||||
|
@media (max-width: 500px) {
|
||||||
|
.expressionInput { width: 350px; }
|
||||||
|
.top-links a { display: inline-block; padding: 5px 10px}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 350px) {
|
||||||
|
.expressionInput { width: 200px; }
|
||||||
|
}
|
||||||
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB |
BIN
src.old/img/feedback-dark.png
Normal file
|
After Width: | Height: | Size: 1.1 KiB |
BIN
src.old/img/feedback-light.png
Normal file
|
After Width: | Height: | Size: 1.1 KiB |
BIN
src.old/img/github-dark.png
Normal file
|
After Width: | Height: | Size: 470 B |
BIN
src.old/img/github-light.png
Normal file
|
After Width: | Height: | Size: 605 B |
BIN
src.old/img/twitter-dark.png
Normal file
|
After Width: | Height: | Size: 1.1 KiB |
BIN
src.old/img/twitter-light.png
Normal file
|
After Width: | Height: | Size: 1.1 KiB |
182
src.old/index.html
Normal file
@@ -0,0 +1,182 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head lang="en">
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="description" content="Bitwise Calculator. Visualised Bitwise Operations.">
|
||||||
|
|
||||||
|
<title>BitwiseCmd</title>
|
||||||
|
<link rel="shortcut icon" href="http://bitwisecmd.com/favicon.ico">
|
||||||
|
|
||||||
|
<!-- build:js js/bitwisecmd.js -->
|
||||||
|
<script type="text/javascript" src="js/core/core.js"></script>
|
||||||
|
<script type="text/javascript" src="js/core/is.js"></script>
|
||||||
|
<script type="text/javascript" src="js/core/should.js"></script>
|
||||||
|
<script type="text/javascript" src="js/core/di.js"></script>
|
||||||
|
<script type="text/javascript" src="js/core/appShell.js"></script>
|
||||||
|
<script type="text/javascript" src="js/core/htmlBuilder.js"></script>
|
||||||
|
<script type="text/javascript" src="js/core/observable.js"></script>
|
||||||
|
|
||||||
|
<script type="text/javascript" src="js/app.js"></script>
|
||||||
|
|
||||||
|
<script type="text/javascript" src="js/components/controllersFeature.js"></script>
|
||||||
|
<script type="text/javascript" src="js/components/viewsFeature.js"></script>
|
||||||
|
<script type="text/javascript" src="js/components/templatesFeature.js"></script>
|
||||||
|
|
||||||
|
<script type="text/javascript" src="js/app/bitwise/calc.js"></script>
|
||||||
|
<script type="text/javascript" src="js/app/bitwise/expression.js"></script>
|
||||||
|
<script type="text/javascript" src="js/app/bitwise/formatter.js"></script>
|
||||||
|
|
||||||
|
<script type="text/javascript" src="js/app/models.js"></script>
|
||||||
|
<script type="text/javascript" src="js/app/modelViews.js"></script>
|
||||||
|
|
||||||
|
<script type="text/javascript" src="js/app/cmd/cmd.js"></script>
|
||||||
|
<script type="text/javascript" src="js/app/services.js"></script>
|
||||||
|
<script type="text/javascript" src="js/app/controllers.js"></script>
|
||||||
|
<script type="text/javascript" src="js/app/cmd/commands.js"></script>
|
||||||
|
|
||||||
|
<script type="text/javascript" src="js/app/run.js"></script>
|
||||||
|
<!-- /build -->
|
||||||
|
|
||||||
|
<link rel="stylesheet" type="text/css" href="css/styles.css" />
|
||||||
|
</head>
|
||||||
|
<body id="rootView" class="dark">
|
||||||
|
|
||||||
|
<header>
|
||||||
|
<div class="header">
|
||||||
|
<h1>Bitwise<span style="color: #c5c5c5;">Cmd</span></h1>
|
||||||
|
|
||||||
|
<ul class="top-links">
|
||||||
|
<li>
|
||||||
|
<a href="https://github.com/BorisLevitskiy/BitwiseCmd"><i class="icon github"> </i><span class="link-text">Project on GitHub</span></a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a href="https://twitter.com/BitwiseCmd"><i class="icon twitter"> </i><span class="link-text">Twitter</span></a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a href="mailto:bitwisecmd@gmail.com?subject=Feedback"><i class="icon feedback"> </i><span class="link-text">Send Feedback</span></a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="expressionInput-container">
|
||||||
|
<input id="in" type="text" class="expressionInput mono" data-controller="expressionInputCtrl" placeholder="type expression like '1>>2' or 'help' "/>
|
||||||
|
|
||||||
|
<span data-controller="configPanelCtrl" class="configPnl">
|
||||||
|
<span id="emphasizeBytes" data-cmd="em" class="indicator on" title="Emphasize Bytes">[em]</span>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<div id="output" data-controller="cmdController">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script data-template="helpResultTpl" type="text/template">
|
||||||
|
<div class="help helpResultTpl">
|
||||||
|
<div style="overflow: hidden">
|
||||||
|
<div style="float: left; margin-right: 20px">
|
||||||
|
<p class="section">
|
||||||
|
<strong>Supported Commands</strong>
|
||||||
|
<ul>
|
||||||
|
<li><code>23 ^ 34</code> — type bitwise expression to see result in binary (only positive integers are supported now)</li>
|
||||||
|
<li><code>23 34</code> — type one or more numbers to see their binary representations</li>
|
||||||
|
<li><code>clear</code> — clear output pane</li>
|
||||||
|
<li><code>help</code> — display this help</li>
|
||||||
|
<li><code>em</code> — turn On/Off Emphasize Bytes</li>
|
||||||
|
<li><code>dark</code> — set Dark theme</li>
|
||||||
|
<li><code>light</code> — set Light theme</li>
|
||||||
|
<li><code>about</code> — about the app</li>
|
||||||
|
</ul>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div class="float:left">
|
||||||
|
<p class="section">
|
||||||
|
<strong>Supported Bitwise Operations</strong><br/>
|
||||||
|
<small>
|
||||||
|
<a href="https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Operators/Bitwise_Operators">
|
||||||
|
as implemented in JavaScript engine
|
||||||
|
</a>
|
||||||
|
</small>
|
||||||
|
<ul>
|
||||||
|
<li><code>&</code> — bitwise AND</li>
|
||||||
|
<li><code>|</code> — bitwise inclusive OR</li>
|
||||||
|
<li><code>^</code> — bitwise exclusive XOR</li>
|
||||||
|
<li><code>~</code> — bitwise NOT</li>
|
||||||
|
<li><code><<</code> — left shift</li>
|
||||||
|
<li><code>>></code> — sign propagating right shift</li>
|
||||||
|
<li><code>>>></code> — zero-fill right shift</li>
|
||||||
|
|
||||||
|
</ul>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<p style="font-style: italic">
|
||||||
|
<strong>Tip:</strong> Use Up and Down keys to navigate trough executed commands.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script data-template="aboutTpl" type="text/template">
|
||||||
|
<div class="aboutTpl">
|
||||||
|
<p> Created by <a href="http://boryslevytskyi.github.io/">Borys Levytskyi</a></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>
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script data-template="resultView" data-compiled="" type="text/template">
|
||||||
|
<div class="result">
|
||||||
|
<div class="input mono"><span class="cur">></span>{m.input}<a class="hashLink" title="Link for this expression" href="{window.location.pathname}#{m.inputHash}">#</a></div>
|
||||||
|
<div class="content"></div>
|
||||||
|
</div>
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script data-template="bitwiseExpressionView" data-compiled="" type="text/template">
|
||||||
|
<table class="expression" cellspacing="0">
|
||||||
|
{each itm in m.items}
|
||||||
|
<tr class="{itm.css}">
|
||||||
|
<td class="sign">{itm.sign}</td>
|
||||||
|
<td class="label">{itm.label}</td>
|
||||||
|
<td class="bin">{itm.bin.padLeft(m.maxNumberOfBits, '0')}</td>
|
||||||
|
<td class="other">{itm.other}</td>
|
||||||
|
</tr>
|
||||||
|
{/}
|
||||||
|
</table>
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script data-template="numbersList" data-compiled="" type="text/template">
|
||||||
|
<table class="expression" cellspacing="0">
|
||||||
|
{each op in m.operands}
|
||||||
|
<tr data-kind="{op.kind}">
|
||||||
|
<td class="label">{op.input}</td>
|
||||||
|
<td class="bin">{op.bin.padLeft(m.bitsSize, '0')}</td>
|
||||||
|
<td class="other">{op.other}</td>
|
||||||
|
</tr>
|
||||||
|
{/}
|
||||||
|
</table>
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script type="text/javascript">
|
||||||
|
|
||||||
|
var app = window.app;
|
||||||
|
app.bootstrap(document.getElementById('rootView'));
|
||||||
|
|
||||||
|
var cmd = app.get('cmd');
|
||||||
|
var hashArgs = app.get('hashArgs');
|
||||||
|
|
||||||
|
if(hashArgs.commands.length > 0) {
|
||||||
|
hashArgs.commands.forEach(cmd.execute.bind(cmd));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
cmd.execute('help');
|
||||||
|
cmd.execute('1|2&6');
|
||||||
|
cmd.execute('1<<0x2a');
|
||||||
|
cmd.execute('2 4 8 16 32');
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<!-- build:js js/analytics.js -->
|
||||||
|
<!-- /build -->
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -6,12 +6,13 @@ app.set('cmd', function() {
|
|||||||
var cmdController = app.controller('cmdController');
|
var cmdController = app.controller('cmdController');
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
debugMode: true,
|
||||||
execute: function(rawInput) {
|
execute: function(rawInput) {
|
||||||
var input = rawInput.trim().toLowerCase();
|
var input = rawInput.trim().toLowerCase();
|
||||||
var handler = findHandler(input);
|
var handler = findHandler(input);
|
||||||
|
|
||||||
if(handler != null) {
|
if(handler != null) {
|
||||||
if(app.debugMode) {
|
if(this.debugMode) {
|
||||||
invokeHandler(input, handler);
|
invokeHandler(input, handler);
|
||||||
} else {
|
} else {
|
||||||
try {
|
try {
|
||||||
@@ -52,13 +53,12 @@ app.set('cmd', function() {
|
|||||||
handlers.push(h);
|
handlers.push(h);
|
||||||
},
|
},
|
||||||
clear: function() {
|
clear: function() {
|
||||||
cmdController.clear();
|
console.error('[displayCommandError] not implemented');
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
function displayCommandError(input, message) {
|
function displayCommandError(input, message) {
|
||||||
var error = new app.models.ErrorResult(message);
|
console.error('[displayCommandError] not implemented');
|
||||||
cmdController.display(new app.models.DisplayResult(input, error));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function invokeHandler (input, handler) {
|
function invokeHandler (input, handler) {
|
||||||
@@ -6,42 +6,6 @@ app.run(function() {
|
|||||||
var rootView = app.get('rootView');
|
var rootView = app.get('rootView');
|
||||||
var expression = app.get('expression');
|
var expression = app.get('expression');
|
||||||
|
|
||||||
cmd.commands({
|
|
||||||
'help': function() {
|
|
||||||
var helpResult = document.querySelector('.result .helpResultTpl');
|
|
||||||
if(helpResult != null) {
|
|
||||||
moveResultUp(helpResult);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
return new app.models.ViewResult('helpResultTpl');
|
|
||||||
},
|
|
||||||
'clear': function() {
|
|
||||||
cmd.clear();
|
|
||||||
},
|
|
||||||
'em': function() {
|
|
||||||
cmdConfig.emphasizeBytes = !cmdConfig.emphasizeBytes;
|
|
||||||
},
|
|
||||||
'dark': function() {
|
|
||||||
cmdConfig.theme = 'dark';
|
|
||||||
},
|
|
||||||
light: function () {
|
|
||||||
cmdConfig.theme = 'light';
|
|
||||||
},
|
|
||||||
about: function() {
|
|
||||||
var aboutResult = document.querySelector('.result .aboutTpl');
|
|
||||||
if(aboutResult != null) {
|
|
||||||
moveResultUp(aboutResult);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
return new app.models.ViewResult('aboutTpl');
|
|
||||||
},
|
|
||||||
'-debug': function() {
|
|
||||||
app.debugMode = true;
|
|
||||||
console.log('debug is on');
|
|
||||||
},
|
|
||||||
'-notrack': function () {}
|
|
||||||
});
|
|
||||||
|
|
||||||
// TODO: Make as function
|
// TODO: Make as function
|
||||||
cmd.command({
|
cmd.command({
|
||||||
canHandle: function(input) { return app.get('expression').canParse(input); },
|
canHandle: function(input) { return app.get('expression').canParse(input); },
|
||||||
15
src/analytics.js
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
(function() {
|
||||||
|
|
||||||
|
if(window.location.host != 'bitwisecmd.com' || window.location.hash.indexOf('-notrack') > -1) {
|
||||||
|
console.log("Analytics not tracked")
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
|
||||||
|
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
|
||||||
|
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
|
||||||
|
})(window,document,'script','//www.google-analytics.com/analytics.js','ga');
|
||||||
|
|
||||||
|
ga('create', 'UA-61569164-1', 'auto');
|
||||||
|
ga('send', 'pageview');
|
||||||
|
})();
|
||||||
51
src/app/appState.js
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
export default class AppState {
|
||||||
|
constructor(persistData) {
|
||||||
|
this.emphasizeBytes = persistData.emphasizeBytes || true;
|
||||||
|
this.commandResults = [];
|
||||||
|
this.handlers = [];
|
||||||
|
this.uiTheme = persistData.uiTheme || 'dark';
|
||||||
|
this.debugMode = false;
|
||||||
|
|
||||||
|
this.version = 1;
|
||||||
|
this.persistedVersion = persistData.version || 0.9;
|
||||||
|
this.wasOldVersion = this.version > this.persistedVersion;
|
||||||
|
}
|
||||||
|
|
||||||
|
addCommandResult(result) {
|
||||||
|
this.commandResults.unshift(result);
|
||||||
|
this.triggerChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
clearCommmandResults() {
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
26
src/app/appStateStore.js
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
const storeKey = 'AppState';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
getPersistedData() {
|
||||||
|
var json = window.localStorage.getItem(storeKey);
|
||||||
|
if(!json) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
return JSON.parse(json);
|
||||||
|
}
|
||||||
|
catch(ex) {
|
||||||
|
console.error('Failed to parse AppState json. Json Value: \n' + json, ex);
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
watch (appState) {
|
||||||
|
appState.onChange(() => this.persistData(appState));
|
||||||
|
},
|
||||||
|
|
||||||
|
persistData(appState) {
|
||||||
|
localStorage.setItem(storeKey, JSON.stringify(appState.getPersistData()));
|
||||||
|
}
|
||||||
|
}
|
||||||
26
src/app/calc.js
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
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);
|
||||||
|
}
|
||||||
|
};
|
||||||
96
src/app/cmd.js
Normal file
@@ -0,0 +1,96 @@
|
|||||||
|
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;
|
||||||
55
src/app/commands.js
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
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 * as expression from './expression';
|
||||||
|
|
||||||
|
var cmdConfig = {};
|
||||||
|
|
||||||
|
export default {
|
||||||
|
initialize (cmd, appState) {
|
||||||
|
|
||||||
|
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));
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
cmd.commands({
|
||||||
|
'help': function(c) {
|
||||||
|
appState.addCommandResult(new HelpResult(c.input));
|
||||||
|
},
|
||||||
|
'clear': function() {
|
||||||
|
appState.clearCommmandResults();
|
||||||
|
},
|
||||||
|
'em': function() {
|
||||||
|
appState.toggleEmphasizeBytes();
|
||||||
|
},
|
||||||
|
'dark': function() {
|
||||||
|
appState.setUiTheme('dark');
|
||||||
|
},
|
||||||
|
'light': function () {
|
||||||
|
appState.setUiTheme('light');
|
||||||
|
},
|
||||||
|
'about': function(c) {
|
||||||
|
appState.addCommandResult(new AboutResult(c.input));
|
||||||
|
},
|
||||||
|
'whatsnew': function(c) {
|
||||||
|
appState.addCommandResult(new WahtsnewResult(c.input));
|
||||||
|
},
|
||||||
|
'-notrack': function () {}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 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)));
|
||||||
|
}
|
||||||
|
}
|
||||||
58
src/app/components/AppRoot.jsx
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import InputBox from './InputBox';
|
||||||
|
import DisplayResultView from './DisplayResultView';
|
||||||
|
|
||||||
|
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';
|
||||||
|
}
|
||||||
|
|
||||||
|
getResultViews() {
|
||||||
|
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}`}>
|
||||||
|
<div className="header">
|
||||||
|
<h1>Bitwise<span style={{color: "#c5c5c5"}}>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>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a href="https://twitter.com/BitwiseCmd"><i className="icon twitter"> </i><span className="link-text">Twitter</span></a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<a href="mailto:bitwisecmd@gmail.com?subject=Feedback"><i className="icon feedback"> </i><span className="link-text">Send Feedback</span></a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="expressionInput-container">
|
||||||
|
<InputBox />
|
||||||
|
|
||||||
|
<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>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="output">
|
||||||
|
{this.getResultViews()}
|
||||||
|
</div>
|
||||||
|
</div>;
|
||||||
|
}
|
||||||
|
}
|
||||||
64
src/app/components/DisplayResultView.jsx
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
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 ExpressionResultView from './results/ExpressionResultView';
|
||||||
|
import WhatsnewResult from '../models/WhatsnewResult';
|
||||||
|
import WhatsnewResultView from './results/WhatsnewResultView';
|
||||||
|
import ErrorResult from '../models/ErrorResult';
|
||||||
|
|
||||||
|
export default class DisplayResult extends React.Component {
|
||||||
|
render() {
|
||||||
|
|
||||||
|
if(this.props.content instanceof UnknownCommandResult) {
|
||||||
|
return this.renderUnknown();
|
||||||
|
}
|
||||||
|
|
||||||
|
if(this.props.content instanceof ErrorResult) {
|
||||||
|
return this.renderError(this.props.content.error.message);
|
||||||
|
}
|
||||||
|
|
||||||
|
return <div className="result">
|
||||||
|
<div className="input mono"><span className="cur">></span>{this.props.content.input}<a className="hashLink" title="Link for this expression" href={window.location.pathname + '#' + this.props.inputHash}>#</a></div>
|
||||||
|
<div className="content">
|
||||||
|
{this.findResultComponent(this.props.content)}
|
||||||
|
</div>
|
||||||
|
</div>;
|
||||||
|
}
|
||||||
|
|
||||||
|
renderUnknown() {
|
||||||
|
return <div className="result">
|
||||||
|
<div className="error">¯\_(ツ)_/¯ Sorry, i don′t know what <strong>{this.props.input}</strong> is</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
renderError(message) {
|
||||||
|
return <div className="result">
|
||||||
|
<div className="error">(X_X) Error occurred: <strong>{message}</strong></div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
findResultComponent(result) {
|
||||||
|
if(result instanceof HelpResult) {
|
||||||
|
return <HelpResultView content={result} />
|
||||||
|
}
|
||||||
|
|
||||||
|
if(result instanceof AboutResult) {
|
||||||
|
return <AboutResultView />
|
||||||
|
}
|
||||||
|
|
||||||
|
if(result instanceof ExpressionResult) {
|
||||||
|
return <ExpressionResultView result={result} emphasizeBytes={this.props.appState.emphasizeBytes} />
|
||||||
|
}
|
||||||
|
|
||||||
|
if(result instanceof WhatsnewResult) {
|
||||||
|
return <WhatsnewResultView />
|
||||||
|
}
|
||||||
|
|
||||||
|
console.warn('Unknown result:', result);
|
||||||
|
return <span>Unknown result: {typeof result}</span>
|
||||||
|
}
|
||||||
|
}
|
||||||
61
src/app/components/InputBox.jsx
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import cmd from '../cmd';
|
||||||
|
|
||||||
|
export default class InputBox extends React.Component {
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
this.history = [];
|
||||||
|
this.historyIndex = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidMount(){
|
||||||
|
this.nameInput.focus();
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return <input id="in" type="text"
|
||||||
|
ref={(input) => { this.nameInput = input; }}
|
||||||
|
onKeyUp={e => this.onKeyUp(e)}
|
||||||
|
onKeyDown={e => this.onKeyDown(e)}
|
||||||
|
className="expressionInput mono"
|
||||||
|
placeholder="type expression like '1>>2' or 'help' "/>;
|
||||||
|
}
|
||||||
|
|
||||||
|
onKeyUp(e) {
|
||||||
|
var input = e.target;
|
||||||
|
if (e.keyCode != 13 || input.value.trim().length == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var value = input.value;
|
||||||
|
this.history.unshift(value);
|
||||||
|
this.historyIndex = -1;
|
||||||
|
|
||||||
|
input.value = '';
|
||||||
|
cmd.execute(value);
|
||||||
|
console.log(this.history);
|
||||||
|
}
|
||||||
|
|
||||||
|
onKeyDown(args) {
|
||||||
|
|
||||||
|
if(args.keyCode == 38) {
|
||||||
|
var newIndex = this.historyIndex+1;
|
||||||
|
|
||||||
|
if (this.history.length > newIndex) { // up
|
||||||
|
args.target.value = this.history[newIndex];
|
||||||
|
this.historyIndex = newIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
args.preventDefault();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(args.keyCode == 40) {
|
||||||
|
if(this.historyIndex > 0) { // down
|
||||||
|
args.target.value = this.history[--this.historyIndex];
|
||||||
|
}
|
||||||
|
|
||||||
|
args.preventDefault();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
11
src/app/components/results/AboutResultView.jsx
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
import React from 'react'
|
||||||
|
|
||||||
|
export default class AboutResultView extends React.Component {
|
||||||
|
render() {
|
||||||
|
return <div className="aboutTpl">
|
||||||
|
<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>;
|
||||||
|
}
|
||||||
|
}
|
||||||
46
src/app/components/results/BinaryStringView.jsx
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
export default class BinaryStringView extends React.Component {
|
||||||
|
render() {
|
||||||
|
return <span>{this.getChildren()}</span>
|
||||||
|
}
|
||||||
|
|
||||||
|
onBitClick(index, e) {
|
||||||
|
if(!this.props.allowFlipBits) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(this.props.onFlipBit) {
|
||||||
|
this.props.onFlipBit(index);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getChildren() {
|
||||||
|
var bits = this.createBits(this.props.binaryString.split(''));
|
||||||
|
|
||||||
|
if(this.props.emphasizeBytes) {
|
||||||
|
return this.splitIntoBytes(bits);
|
||||||
|
}
|
||||||
|
|
||||||
|
return bits;
|
||||||
|
}
|
||||||
|
|
||||||
|
createBits(bitChars) {
|
||||||
|
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>);
|
||||||
|
}
|
||||||
|
|
||||||
|
splitIntoBytes(bits) {
|
||||||
|
const bytes = [];
|
||||||
|
|
||||||
|
var key = 0;
|
||||||
|
while(bits.length > 0) {
|
||||||
|
bytes.push(<span key={key++} className="byte">{bits.splice(0, 8)}</span>);
|
||||||
|
}
|
||||||
|
|
||||||
|
return bytes;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import * as expression 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 {
|
||||||
|
render() {
|
||||||
|
var rows = this.getRows();
|
||||||
|
if(!rows) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return <table className="expression">
|
||||||
|
<tbody>
|
||||||
|
{rows}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
}
|
||||||
|
|
||||||
|
getRows() {
|
||||||
|
const expr = this.props.expression;
|
||||||
|
|
||||||
|
if(expr instanceof expression.SingleOperandExpression) {
|
||||||
|
const m = BitwiseExpressionViewModel.buildNot(expr, { emphasizeBytes: this.props.emphasizeBytes });
|
||||||
|
log.info('Render model', m);
|
||||||
|
return m.items.map((itm, i) => <ExpressionRow key={i} {...itm} emphasizeBytes={this.props.emphasizeBytes} maxNumberOfBits={m.maxNumberOfBits} />);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(expr instanceof expression.MultipleOperandsExpression) {
|
||||||
|
const m = BitwiseExpressionViewModel.buildMultiple(expr, { emphasizeBytes: this.props.emphasizeBytes });
|
||||||
|
log.info('Render model', m);
|
||||||
|
return m.items.map((itm, i) => <ExpressionRow key={i} {...itm} emphasizeBytes={this.props.emphasizeBytes} maxNumberOfBits={m.maxNumberOfBits} />);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class ExpressionRow extends React.Component {
|
||||||
|
render() {
|
||||||
|
const { sign, label, bin, other, css, maxNumberOfBits, emphasizeBytes } = this.props;
|
||||||
|
|
||||||
|
return <tr className={css}>
|
||||||
|
<td className="sign">{sign}</td>
|
||||||
|
<td className="label">{label}</td>
|
||||||
|
<td className="bin">
|
||||||
|
<BinaryStringView emphasizeBytes={emphasizeBytes} binaryString={formatter.padLeft(bin, maxNumberOfBits, '0')} allowFlipBits={false}/>
|
||||||
|
</td>
|
||||||
|
<td className="other">{other}</td>
|
||||||
|
</tr>;
|
||||||
|
}
|
||||||
|
}
|
||||||
26
src/app/components/results/ExpressionResultView.jsx
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
import React from 'react'
|
||||||
|
import ListOfNumbersExpressionView from './ListOfNumbersExpressionView';
|
||||||
|
import BitwiseOperationExpressionView from './BitwiseOperationExpressionView';
|
||||||
|
|
||||||
|
import * as expression from '../../expression';
|
||||||
|
|
||||||
|
export default class ExpressionResultView extends React.Component {
|
||||||
|
render() {
|
||||||
|
var expr = this.props.result.expression;
|
||||||
|
|
||||||
|
if(expr instanceof expression.ListOfNumbersExpression) {
|
||||||
|
return <div>
|
||||||
|
<ListOfNumbersExpressionView expression={expr} emphasizeBytes={this.props.emphasizeBytes} />
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
if(expr instanceof expression.SingleOperandExpression || expr instanceof expression.MultipleOperandsExpression) {
|
||||||
|
return <div>
|
||||||
|
<BitwiseOperationExpressionView expression={expr} emphasizeBytes={this.props.emphasizeBytes} />
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('[BitwiseOperationExpressionView] render()', expr);
|
||||||
|
|
||||||
|
return <b>Expression: {expr.expressionString}</b>;
|
||||||
|
}
|
||||||
|
}
|
||||||
45
src/app/components/results/HelpResultView.jsx
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
import React from 'react'
|
||||||
|
|
||||||
|
export default class HelpResultView extends React.Component {
|
||||||
|
render() {
|
||||||
|
return <div className="help helpResultTpl">
|
||||||
|
<div style={{overflow: "hidden"}}>
|
||||||
|
<div style={{float: "left", "marginRight": "20px"}}>
|
||||||
|
<div className="section">
|
||||||
|
<strong>Supported Commands</strong>
|
||||||
|
<ul>
|
||||||
|
<li><code>23 ^ 34</code> — type bitwise expression to see result in binary (only positive integers are supported now)</li>
|
||||||
|
<li><code>23 34</code> — type one or more numbers to see their binary representations</li>
|
||||||
|
<li><code>clear</code> — clear output pane</li>
|
||||||
|
<li><code>help</code> — display this help</li>
|
||||||
|
<li><code>whatsnew</code> — display changelog</li>
|
||||||
|
<li><code>em</code> — turn On/Off Emphasize Bytes</li>
|
||||||
|
<li><code>dark</code> — set Dark theme</li>
|
||||||
|
<li><code>light</code> — set Light theme</li>
|
||||||
|
<li><code>about</code> — about the app</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div style={{"float":"left"}}>
|
||||||
|
<div className="section">
|
||||||
|
<strong>Supported Bitwise Operations</strong><br/>
|
||||||
|
<small>
|
||||||
|
<a href="https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Operators/Bitwise_Operators">
|
||||||
|
as implemented in JavaScript engine
|
||||||
|
</a>
|
||||||
|
</small>
|
||||||
|
<ul>
|
||||||
|
<li><code>&</code> — bitwise AND</li>
|
||||||
|
<li><code>|</code> — bitwise inclusive OR</li>
|
||||||
|
<li><code>^</code> — bitwise exclusive XOR</li>
|
||||||
|
<li><code>~</code> — bitwise NOT</li>
|
||||||
|
<li><code><<</code> — left shift</li>
|
||||||
|
<li><code>>></code> — sign propagating right shift</li>
|
||||||
|
<li><code>>>></code> — zero-fill right shift</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>;
|
||||||
|
}
|
||||||
|
}
|
||||||
47
src/app/components/results/ListOfNumbersExpressionView.jsx
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import formatter from '../../formatter';
|
||||||
|
import BinaryStringView from './BinaryStringView';
|
||||||
|
import BitwiseExpressionViewModel from './models/BitwiseExpressionViewModel'
|
||||||
|
|
||||||
|
|
||||||
|
export default class ListOfNumersExpressionView extends React.Component {
|
||||||
|
render() {
|
||||||
|
const expr = this.props.expression;
|
||||||
|
const maxBitsLegnth = BitwiseExpressionViewModel.getNumberOfBits(expr.maxBitsLegnth, this.props.emphasizeBytes);
|
||||||
|
const numberRows = expr.numbers.map((n, i) => <OperandView key={i} operand={n} maxBitsLegnth={maxBitsLegnth} emphasizeBytes={this.props.emphasizeBytes} />);
|
||||||
|
return <table className="expression">
|
||||||
|
<tbody>
|
||||||
|
{numberRows}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class OperandView extends React.Component {
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
this.state = { operand: null };
|
||||||
|
}
|
||||||
|
render() {
|
||||||
|
const op = this.props.operand;
|
||||||
|
const binaryString = formatter.padLeft(op.bin, this.props.maxBitsLegnth, '0');
|
||||||
|
|
||||||
|
return <tr data-kind={op.kind}>
|
||||||
|
<td className="label">{op.input}</td>
|
||||||
|
<td className="bin"><BinaryStringView emphasizeBytes={this.props.emphasizeBytes} binaryString={binaryString} allowFlipBits={true} onFlipBit={e => this.flipBit(e)} /></td>
|
||||||
|
<td className="other">{op.other}</td>
|
||||||
|
</tr>;
|
||||||
|
};
|
||||||
|
|
||||||
|
flipBit(index) {
|
||||||
|
var op = this.props.operand;
|
||||||
|
const binaryString = formatter.padLeft(op.bin, this.props.maxBitsLegnth, '0');
|
||||||
|
var arr = binaryString.split('');
|
||||||
|
// TODO: this code looks ugly
|
||||||
|
arr[index] = arr[index] == '0' ? '1' : '0';
|
||||||
|
var bin = arr.join('');
|
||||||
|
op.setValue(parseInt(bin, 2));
|
||||||
|
|
||||||
|
this.setState({ operand: op });
|
||||||
|
}
|
||||||
|
}
|
||||||
13
src/app/components/results/WhatsnewResultView.jsx
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
import React from 'react'
|
||||||
|
|
||||||
|
export default class WhatsnewResultView extends React.Component {
|
||||||
|
render() {
|
||||||
|
return <div class="changelog">
|
||||||
|
<h3>BitwiseCmd Changelog</h3>
|
||||||
|
<div class="item">
|
||||||
|
<p><strong>May 16th, 2017</strong> Complete rewrite using React. Old implementation is available at <a href="http://bitwisecmd.com/old">http://bitwisecmd.com/old</a>.</p>
|
||||||
|
<p>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>;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,80 @@
|
|||||||
|
export default class BitwiseExpressionViewModel {
|
||||||
|
|
||||||
|
constructor({ emphasizeBytes = false } = {}) {
|
||||||
|
this.emphasizeBytes = emphasizeBytes;
|
||||||
|
this.items = [];
|
||||||
|
this.maxNumberOfBits = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static buildMultiple (expr, config) {
|
||||||
|
|
||||||
|
var op = expr.expressions[0],
|
||||||
|
i = 1, l = expr.expressions.length,
|
||||||
|
ex, m = new BitwiseExpressionViewModel(config);
|
||||||
|
|
||||||
|
m.addOperand(op);
|
||||||
|
|
||||||
|
for (;i<l;i++) {
|
||||||
|
ex = expr.expressions[i];
|
||||||
|
op = ex.apply(op.value);
|
||||||
|
|
||||||
|
if(ex.isShiftExpression()){
|
||||||
|
m.addShiftExpressionResult(ex, op);
|
||||||
|
} else {
|
||||||
|
m.addExpression(ex);
|
||||||
|
m.addExpressionResult(op);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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:'', label: operand.toString(), bin: operand.bin, other: operand.other, css: ''});
|
||||||
|
};
|
||||||
|
|
||||||
|
addExpression(expression) {
|
||||||
|
this.maxNumberOfBits = Math.max(expression.operand1.getLengthInBits(), this.maxNumberOfBits);
|
||||||
|
this.items.push({ sign: expression.sign, label: expression.operand1.toString(), bin: expression.operand1.bin, other: expression.operand1.other, css: ''});
|
||||||
|
};
|
||||||
|
|
||||||
|
addShiftExpressionResult(expression, resultOperand) {
|
||||||
|
this.maxNumberOfBits = Math.max(resultOperand.getLengthInBits(), this.maxNumberOfBits);
|
||||||
|
this.items.push({
|
||||||
|
sign: expression.sign + expression.operand1.input,
|
||||||
|
label: resultOperand.toString(),
|
||||||
|
bin: resultOperand.bin,
|
||||||
|
other: resultOperand.other,
|
||||||
|
css: 'expression-result'});
|
||||||
|
};
|
||||||
|
|
||||||
|
addExpressionResult(operand) {
|
||||||
|
this.maxNumberOfBits = Math.max(operand.getLengthInBits(), this.maxNumberOfBits);
|
||||||
|
this.items.push({ sign:'=', label: operand.toString(), bin: operand.bin, other: operand.other, css: 'expression-result'});
|
||||||
|
};
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
};
|
||||||
|
}
|
||||||
273
src/app/expression.js
Normal file
@@ -0,0 +1,273 @@
|
|||||||
|
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 new Operand(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]+)\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(new Operand(n.trim()));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return new ListOfNumbersExpression(input, numbers);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Not Expression
|
||||||
|
expression.addFactory({
|
||||||
|
regex: /^(~)(-?(?:\d+|0x[\d,a-f]+))$/,
|
||||||
|
canCreate: function(string) {
|
||||||
|
return this.regex.test(string);
|
||||||
|
},
|
||||||
|
create: function (string) {
|
||||||
|
var matches = this.regex.exec(string),
|
||||||
|
operand = new Operand(matches[2]);
|
||||||
|
|
||||||
|
return new SingleOperandExpression(matches.input, operand, matches[1]);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Multiple operands expression
|
||||||
|
expression.addFactory({
|
||||||
|
fullRegex: /^((<<|>>|>>>|\||\&|\^)?(-?((?:\d+(?!x))|(?:0x[\d,a-f]+))))+$/,
|
||||||
|
regex: /(<<|>>|>>>|\||\&|\^)?(-?((?:\d+(?!x))|(?:0x[\d,a-f]+)))/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) {
|
||||||
|
var input = m[0],
|
||||||
|
sign = m[1],
|
||||||
|
num = m[2];
|
||||||
|
|
||||||
|
if(sign == null) {
|
||||||
|
return new Operand(num);
|
||||||
|
} else {
|
||||||
|
return new SingleOperandExpression(input, new Operand(num), sign);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
normalizeString: function (string) {
|
||||||
|
return string.replace(/\s+/g,'');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
// Represents numeric value
|
||||||
|
export class Operand {
|
||||||
|
constructor(input) {
|
||||||
|
this.input = input;
|
||||||
|
this.value = parseInt(input);
|
||||||
|
this.hex = Operand.toHexString(this.value.toString(16));
|
||||||
|
this.dec = this.value.toString(10);
|
||||||
|
// >>> 0 makes negative numbers like -1 to be displayed as '11111111111111111111111111111111' in binary instead of -1
|
||||||
|
this.bin = this.value < 0 ? (this.value >>> 0).toString(2) : this.value.toString(2);
|
||||||
|
this.kind = this.input.indexOf('0x') > -1 ? 'hex' : 'dec';
|
||||||
|
this.other = this.kind == 'dec' ? this.hex : this.dec;
|
||||||
|
this.lengthInBits = Operand.getBitLength(this.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
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': return 'hex';
|
||||||
|
case 'hex': return 'dec';
|
||||||
|
default : throw new Error(kind + " kind doesn't have opposite kind")
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
toString() {
|
||||||
|
return this.input;
|
||||||
|
}
|
||||||
|
|
||||||
|
setValue(value) {
|
||||||
|
console.log('Before ' + value, this);
|
||||||
|
this.value = value;
|
||||||
|
this.bin = Operand.toKindString(this.value, 'bin');
|
||||||
|
this.dec = Operand.toKindString(this.value, 'dec');
|
||||||
|
this.hex = Operand.toKindString(this.value, 'hex');
|
||||||
|
this.other = Operand.toKindString(this.value, this.getOtherKind());
|
||||||
|
this.input = Operand.toKindString(this.value, this.kind);
|
||||||
|
console.log('After ' + value, 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(number, kind) {
|
||||||
|
var str = number.toString(Operand.getBase(kind));
|
||||||
|
if(kind == 'hex') {
|
||||||
|
str = Operand.toHexString(str);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Operand(str);
|
||||||
|
};
|
||||||
|
|
||||||
|
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;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Expressions like ~1
|
||||||
|
export class SingleOperandExpression {
|
||||||
|
constructor(expressionString, operand, sign) {
|
||||||
|
this.expressionString = expressionString;
|
||||||
|
this.operand1 = operand;
|
||||||
|
this.sign = sign;
|
||||||
|
}
|
||||||
|
|
||||||
|
apply(value) {
|
||||||
|
var str = '';
|
||||||
|
if(this.sign == '~'){
|
||||||
|
str = '~' + this.operand1.value;
|
||||||
|
} else {
|
||||||
|
str = value + this.sign + this.operand1.value
|
||||||
|
}
|
||||||
|
|
||||||
|
return Operand.create(eval(str), this.operand1.kind);
|
||||||
|
};
|
||||||
|
|
||||||
|
isShiftExpression() {
|
||||||
|
return this.sign.indexOf('<') >= 0 || this.sign.indexOf('>')>= 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
toString() {
|
||||||
|
return this.sign + this.operand1.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Expression like 1|2 or 4^5
|
||||||
|
export class TwoOperandExpression {
|
||||||
|
constructor(expressionString, operand1, operand2, sign) {
|
||||||
|
this.expressionString = expressionString;
|
||||||
|
this.operand1 = operand1;
|
||||||
|
this.operand2 = operand2;
|
||||||
|
this.sign = sign;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class MultipleOperandsExpression {
|
||||||
|
constructor(expressionString, expressions) {
|
||||||
|
this.expressionString = expressionString;
|
||||||
|
this.expressions = expressions;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export 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(' ');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class Expression {
|
||||||
|
toString() {
|
||||||
|
return this.expressionString ? "Expression: " + this.expressionString : this.toString();
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
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');
|
||||||
|
}
|
||||||
|
}
|
||||||
26
src/app/formatter.js
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
41
src/app/hash.js
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
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) {
|
||||||
|
// Support for -debur or -notrack properties
|
||||||
|
if(/^\-[a-zA-Z]+$/.test(value)) {
|
||||||
|
args[value.substr(1)] = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
55
src/app/index.jsx
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
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';
|
||||||
|
|
||||||
|
setupLogger();
|
||||||
|
|
||||||
|
const appState = createAppState();
|
||||||
|
|
||||||
|
commands.initialize(cmd, appState);
|
||||||
|
|
||||||
|
executeStartupCommands();
|
||||||
|
|
||||||
|
var root = <AppRoot appState={appState} />;
|
||||||
|
ReactDOM.render(root, document.getElementById('root'));
|
||||||
|
|
||||||
|
log.debug("started");
|
||||||
|
|
||||||
|
function createAppState() {
|
||||||
|
var stateData = appStateStore.getPersistedData();
|
||||||
|
const appState = new AppState(stateData);
|
||||||
|
appStateStore.watch(appState);
|
||||||
|
log.debug("appState", appState);
|
||||||
|
return appState;
|
||||||
|
}
|
||||||
|
|
||||||
|
function setupLogger() {
|
||||||
|
if(window.location.host != 'bitwisecmd.com' || window.location.hash.indexOf('-debug') > -1) {
|
||||||
|
log.setLevel("trace");
|
||||||
|
} else {
|
||||||
|
log.setLevel("warn");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function executeStartupCommands() {
|
||||||
|
var hashArgs = hash.getArgs(window.location.hash);
|
||||||
|
|
||||||
|
var startupCommands = ['help', '1|2&6','1<<0x2a','2 4 8 16 32'];
|
||||||
|
|
||||||
|
if(appState.wasOldVersion) {
|
||||||
|
startupCommands = ["whatsnew"];
|
||||||
|
}
|
||||||
|
|
||||||
|
if(hashArgs.commands.length > 0) {
|
||||||
|
startupCommands = hashArgs.commands;
|
||||||
|
}
|
||||||
|
|
||||||
|
startupCommands.forEach(cmd.execute.bind(cmd));
|
||||||
|
}
|
||||||
33
src/app/is.js
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
7
src/app/models/AboutResult.js
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
import CommandResult from './CommandResult';
|
||||||
|
|
||||||
|
export default class AboutResult extends CommandResult {
|
||||||
|
constructor(input) {
|
||||||
|
super(input);
|
||||||
|
}
|
||||||
|
}
|
||||||
10
src/app/models/CommandResult.js
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
export default class CommandResult {
|
||||||
|
constructor(input) {
|
||||||
|
this.input = input;
|
||||||
|
this.inputHash = this.encodeHash(input);
|
||||||
|
}
|
||||||
|
|
||||||
|
encodeHash (string) {
|
||||||
|
return encodeURI(string.trim().replace(/\s/g,','));
|
||||||
|
}
|
||||||
|
}
|
||||||
8
src/app/models/ErrorResult.js
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
import CommandResult from './CommandResult';
|
||||||
|
|
||||||
|
export default class ErrorResult extends CommandResult {
|
||||||
|
constructor(input, error) {
|
||||||
|
super(input);
|
||||||
|
this.error = error;
|
||||||
|
}
|
||||||
|
}
|
||||||
8
src/app/models/ExpressionResult.js
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
import CommandResult from './CommandResult';
|
||||||
|
|
||||||
|
export default class ExpressionResult extends CommandResult {
|
||||||
|
constructor(input, expression) {
|
||||||
|
super(input);
|
||||||
|
this.expression = expression;
|
||||||
|
}
|
||||||
|
}
|
||||||
7
src/app/models/HelpResult.js
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
import CommandResult from './CommandResult';
|
||||||
|
|
||||||
|
export default class HelpResult extends CommandResult {
|
||||||
|
constructor(input) {
|
||||||
|
super(input);
|
||||||
|
}
|
||||||
|
}
|
||||||
8
src/app/models/UnknownCommandResult.js
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
import CommandResult from './CommandResult';
|
||||||
|
|
||||||
|
export default class UnknownCommandResult extends CommandResult {
|
||||||
|
constructor(input) {
|
||||||
|
super(input);
|
||||||
|
this.message = `Sorry, i don''t know what ${input} is :(`;
|
||||||
|
}
|
||||||
|
}
|
||||||
7
src/app/models/WhatsnewResult.js
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
import CommandResult from './CommandResult';
|
||||||
|
|
||||||
|
export default class WhatsnewResult extends CommandResult {
|
||||||
|
constructor(input) {
|
||||||
|
super(input);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,4 +1,7 @@
|
|||||||
body { font-family: Verdana; font-size: 0.8em; margin: 0; padding: 20px 100px 0 100px; }
|
body { padding:0; margin:0; height: 100%; overflow: hidden; }
|
||||||
|
html { height: 100% }
|
||||||
|
|
||||||
|
.app-root { font-family: Verdana; font-size: 0.8em; margin: 0; padding: 20px 100px 0 100px; height: 100%; overflow: auto; }
|
||||||
code { font-size: 1.2em; font-weight: bold; }
|
code { font-size: 1.2em; font-weight: bold; }
|
||||||
|
|
||||||
.top-links { position: absolute; right: 10px; top: 10px; list-style-type: none; margin: 0 }
|
.top-links { position: absolute; right: 10px; top: 10px; list-style-type: none; margin: 0 }
|
||||||
@@ -69,6 +72,7 @@ code { font-size: 1.2em; font-weight: bold; }
|
|||||||
|
|
||||||
/* Dark */
|
/* Dark */
|
||||||
.dark { background: #121212; color: white;}
|
.dark { background: #121212; color: white;}
|
||||||
|
.dark .expression { color: white;}
|
||||||
.dark .expressionInput { background: #121212; color: white; }
|
.dark .expressionInput { background: #121212; color: white; }
|
||||||
.dark a, .dark a:visited { color: white; }
|
.dark a, .dark a:visited { color: white; }
|
||||||
.dark .indicator { color: #555; }
|
.dark .indicator { color: #555; }
|
||||||
@@ -79,6 +83,7 @@ code { font-size: 1.2em; font-weight: bold; }
|
|||||||
.dark .hashLink, .dark .hashLink:visited { color: #333 }
|
.dark .hashLink, .dark .hashLink:visited { color: #333 }
|
||||||
.dark .hashLink:hover { color: #999 }
|
.dark .hashLink:hover { color: #999 }
|
||||||
.dark ul.top-links li:hover { background: #333 }
|
.dark ul.top-links li:hover { background: #333 }
|
||||||
|
.dark .error { color: mediumvioletred}
|
||||||
|
|
||||||
/* Top Links Shrink */
|
/* Top Links Shrink */
|
||||||
@media (max-width: 800px) {
|
@media (max-width: 800px) {
|
||||||
@@ -86,6 +91,8 @@ code { font-size: 1.2em; font-weight: bold; }
|
|||||||
.top-links .link-text { display: none }
|
.top-links .link-text { display: none }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.social-container{ position:fixed; bottom:10px; right:10px }
|
||||||
|
|
||||||
/* Remove margin space on body. Inline top links with header */
|
/* Remove margin space on body. Inline top links with header */
|
||||||
@media (max-width: 700px) {
|
@media (max-width: 700px) {
|
||||||
body { padding: 10px; }
|
body { padding: 10px; }
|
||||||
|
|||||||
BIN
src/img/social-avatar.png
Normal file
|
After Width: | Height: | Size: 6.4 KiB |
219
src/index.html
@@ -1,182 +1,39 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE>
|
||||||
<html>
|
<html>
|
||||||
<head lang="en">
|
<head>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<meta name="description" content="Bitwise Calculator. Visualised Bitwise Operations.">
|
<meta name="description" content="Free Text Online Bitwise Calculator">
|
||||||
|
|
||||||
<title>BitwiseCmd</title>
|
<title>BitwiseCmd</title>
|
||||||
<link rel="shortcut icon" href="http://bitwisecmd.com/favicon.ico">
|
<link rel="shortcut icon" href="http://bitwisecmd.com/favicon.ico">
|
||||||
|
<link rel="stylesheet" type="text/css" href="css/styles.css" />
|
||||||
<!-- build:js js/bitwisecmd.js -->
|
|
||||||
<script type="text/javascript" src="js/core/core.js"></script>
|
<meta property="og:url" content="http://bitwisecmd.com" />
|
||||||
<script type="text/javascript" src="js/core/is.js"></script>
|
<meta property="og:type" content="website" />
|
||||||
<script type="text/javascript" src="js/core/should.js"></script>
|
<meta property="og:title" content="BitwiseCmd" />
|
||||||
<script type="text/javascript" src="js/core/di.js"></script>
|
<meta property="og:description" content="Free Text Online Bitwise Calculator" />
|
||||||
<script type="text/javascript" src="js/core/appShell.js"></script>
|
<meta property="og:image" content="http://bitwisecmd.com/img/social-avatar.png" />
|
||||||
<script type="text/javascript" src="js/core/htmlBuilder.js"></script>
|
</head>
|
||||||
<script type="text/javascript" src="js/core/observable.js"></script>
|
<body>
|
||||||
|
<div id="root">
|
||||||
<script type="text/javascript" src="js/app.js"></script>
|
<p>Ooops... Something went wrong.</p>
|
||||||
|
<p>Either you have an old browser or I screwed something up ¯\_(ツ)_/¯</p>
|
||||||
<script type="text/javascript" src="js/components/controllersFeature.js"></script>
|
<p>No worries! There is a still old version avaliable at <a href="http://bitwisecmd.com/old">http://bitwisecmd.com/old</a>.</p>
|
||||||
<script type="text/javascript" src="js/components/viewsFeature.js"></script>
|
</div>
|
||||||
<script type="text/javascript" src="js/components/templatesFeature.js"></script>
|
<script src="bundle.js"></script>
|
||||||
|
<script src="analytics.js"></script>
|
||||||
<script type="text/javascript" src="js/app/bitwise/calc.js"></script>
|
|
||||||
<script type="text/javascript" src="js/app/bitwise/expression.js"></script>
|
<div id="fb-root"></div>
|
||||||
<script type="text/javascript" src="js/app/bitwise/formatter.js"></script>
|
<script>(function(d, s, id) {
|
||||||
|
var js, fjs = d.getElementsByTagName(s)[0];
|
||||||
<script type="text/javascript" src="js/app/models.js"></script>
|
if (d.getElementById(id)) return;
|
||||||
<script type="text/javascript" src="js/app/modelViews.js"></script>
|
js = d.createElement(s); js.id = id;
|
||||||
|
js.src = "//connect.facebook.net/en_US/sdk.js#xfbml=1&version=v2.5";
|
||||||
<script type="text/javascript" src="js/app/cmd/cmd.js"></script>
|
fjs.parentNode.insertBefore(js, fjs);
|
||||||
<script type="text/javascript" src="js/app/services.js"></script>
|
}(document, 'script', 'facebook-jssdk'));</script>
|
||||||
<script type="text/javascript" src="js/app/controllers.js"></script>
|
|
||||||
<script type="text/javascript" src="js/app/cmd/commands.js"></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>
|
||||||
<script type="text/javascript" src="js/app/run.js"></script>
|
</div>
|
||||||
<!-- /build -->
|
</body>
|
||||||
|
|
||||||
<link rel="stylesheet" type="text/css" href="css/styles.css" />
|
|
||||||
</head>
|
|
||||||
<body id="rootView" class="dark">
|
|
||||||
|
|
||||||
<header>
|
|
||||||
<div class="header">
|
|
||||||
<h1>Bitwise<span style="color: #c5c5c5;">Cmd</span></h1>
|
|
||||||
|
|
||||||
<ul class="top-links">
|
|
||||||
<li>
|
|
||||||
<a href="https://github.com/BorisLevitskiy/BitwiseCmd"><i class="icon github"> </i><span class="link-text">Project on GitHub</span></a>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<a href="https://twitter.com/BitwiseCmd"><i class="icon twitter"> </i><span class="link-text">Twitter</span></a>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<a href="mailto:bitwisecmd@gmail.com?subject=Feedback"><i class="icon feedback"> </i><span class="link-text">Send Feedback</span></a>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="expressionInput-container">
|
|
||||||
<input id="in" type="text" class="expressionInput mono" data-controller="expressionInputCtrl" placeholder="type expression like '1>>2' or 'help' "/>
|
|
||||||
|
|
||||||
<span data-controller="configPanelCtrl" class="configPnl">
|
|
||||||
<span id="emphasizeBytes" data-cmd="em" class="indicator on" title="Emphasize Bytes">[em]</span>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</header>
|
|
||||||
|
|
||||||
<div id="output" data-controller="cmdController">
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<script data-template="helpResultTpl" type="text/template">
|
|
||||||
<div class="help helpResultTpl">
|
|
||||||
<div style="overflow: hidden">
|
|
||||||
<div style="float: left; margin-right: 20px">
|
|
||||||
<p class="section">
|
|
||||||
<strong>Supported Commands</strong>
|
|
||||||
<ul>
|
|
||||||
<li><code>23 ^ 34</code> — type bitwise expression to see result in binary (only positive integers are supported now)</li>
|
|
||||||
<li><code>23 34</code> — type one or more numbers to see their binary representations</li>
|
|
||||||
<li><code>clear</code> — clear output pane</li>
|
|
||||||
<li><code>help</code> — display this help</li>
|
|
||||||
<li><code>em</code> — turn On/Off Emphasize Bytes</li>
|
|
||||||
<li><code>dark</code> — set Dark theme</li>
|
|
||||||
<li><code>light</code> — set Light theme</li>
|
|
||||||
<li><code>about</code> — about the app</li>
|
|
||||||
</ul>
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<div class="float:left">
|
|
||||||
<p class="section">
|
|
||||||
<strong>Supported Bitwise Operations</strong><br/>
|
|
||||||
<small>
|
|
||||||
<a href="https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Operators/Bitwise_Operators">
|
|
||||||
as implemented in JavaScript engine
|
|
||||||
</a>
|
|
||||||
</small>
|
|
||||||
<ul>
|
|
||||||
<li><code>&</code> — bitwise AND</li>
|
|
||||||
<li><code>|</code> — bitwise inclusive OR</li>
|
|
||||||
<li><code>^</code> — bitwise exclusive XOR</li>
|
|
||||||
<li><code>~</code> — bitwise NOT</li>
|
|
||||||
<li><code><<</code> — left shift</li>
|
|
||||||
<li><code>>></code> — sign propagating right shift</li>
|
|
||||||
<li><code>>>></code> — zero-fill right shift</li>
|
|
||||||
|
|
||||||
</ul>
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<p style="font-style: italic">
|
|
||||||
<strong>Tip:</strong> Use Up and Down keys to navigate trough executed commands.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<script data-template="aboutTpl" type="text/template">
|
|
||||||
<div class="aboutTpl">
|
|
||||||
<p> Created by <a href="http://boryslevytskyi.github.io/">Borys Levytskyi</a></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>
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<script data-template="resultView" data-compiled="" type="text/template">
|
|
||||||
<div class="result">
|
|
||||||
<div class="input mono"><span class="cur">></span>{m.input}<a class="hashLink" title="Link for this expression" href="{window.location.pathname}#{m.inputHash}">#</a></div>
|
|
||||||
<div class="content"></div>
|
|
||||||
</div>
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<script data-template="bitwiseExpressionView" data-compiled="" type="text/template">
|
|
||||||
<table class="expression" cellspacing="0">
|
|
||||||
{each itm in m.items}
|
|
||||||
<tr class="{itm.css}">
|
|
||||||
<td class="sign">{itm.sign}</td>
|
|
||||||
<td class="label">{itm.label}</td>
|
|
||||||
<td class="bin">{itm.bin.padLeft(m.maxNumberOfBits, '0')}</td>
|
|
||||||
<td class="other">{itm.other}</td>
|
|
||||||
</tr>
|
|
||||||
{/}
|
|
||||||
</table>
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<script data-template="numbersList" data-compiled="" type="text/template">
|
|
||||||
<table class="expression" cellspacing="0">
|
|
||||||
{each op in m.operands}
|
|
||||||
<tr data-kind="{op.kind}">
|
|
||||||
<td class="label">{op.input}</td>
|
|
||||||
<td class="bin">{op.bin.padLeft(m.bitsSize, '0')}</td>
|
|
||||||
<td class="other">{op.other}</td>
|
|
||||||
</tr>
|
|
||||||
{/}
|
|
||||||
</table>
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<script type="text/javascript">
|
|
||||||
|
|
||||||
var app = window.app;
|
|
||||||
app.bootstrap(document.getElementById('rootView'));
|
|
||||||
|
|
||||||
var cmd = app.get('cmd');
|
|
||||||
var hashArgs = app.get('hashArgs');
|
|
||||||
|
|
||||||
if(hashArgs.commands.length > 0) {
|
|
||||||
hashArgs.commands.forEach(cmd.execute.bind(cmd));
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
cmd.execute('help');
|
|
||||||
cmd.execute('1|2&6');
|
|
||||||
cmd.execute('1<<0x2a');
|
|
||||||
cmd.execute('2 4 8 16 32');
|
|
||||||
}
|
|
||||||
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<!-- build:js js/analytics.js -->
|
|
||||||
<!-- /build -->
|
|
||||||
</body>
|
|
||||||
</html>
|
</html>
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
rem /S Copy folders and subfolders
|
|
||||||
rem /Y Suppress prompt to confirm overwriting a file.
|
|
||||||
|
|
||||||
xcopy .\build\*.* ..\BitwiseCmdPages\ /S /Y
|
|
||||||
@@ -7,7 +7,7 @@ var driver = browser.driver;
|
|||||||
var appUrl = browser.params.appUrl || 'http://localhost:63342/BitwiseCmd/src/#clear';
|
var appUrl = browser.params.appUrl || 'http://localhost:63342/BitwiseCmd/src/#clear';
|
||||||
var sutPage = new BitwiseCmdPage(driver, appUrl);
|
var sutPage = new BitwiseCmdPage(driver, appUrl);
|
||||||
|
|
||||||
describe('launch of application', function() {
|
describe('when application starts', function() {
|
||||||
it('should have title', function() {
|
it('should have title', function() {
|
||||||
sutPage.goToApp().then(function() {
|
sutPage.goToApp().then(function() {
|
||||||
expect(driver.getTitle()).toEqual('BitwiseCmd');
|
expect(driver.getTitle()).toEqual('BitwiseCmd');
|
||||||
@@ -149,15 +149,15 @@ describe('launch of application', function() {
|
|||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
xit('should emphasize bytes', function() {
|
it('should emphasize bytes', function() {
|
||||||
|
|
||||||
goToApp()
|
sutPage.goToApp()
|
||||||
.then(function() { return sutPage.executeExpression('1')})
|
.then(function() { return sutPage.executeExpression('1')})
|
||||||
.then(function() {
|
.then(function() {
|
||||||
return assertExpressionResult([{ label: '1', bin:'00000001', other: '0x1'}])
|
return assertExpressionResult([{ label: '1', bin:'00000001', other: '0x1'}])
|
||||||
})
|
})
|
||||||
.then(function() { return sutPage.executeExpression('clear')})
|
.then(function() { return sutPage.executeExpression('clear')})
|
||||||
// .then(function() { return sendCommand('em')})
|
.then(function() { return sutPage.executeExpression('em')})
|
||||||
.then(function() { return sutPage.shouldHaveNoErrors(); })
|
.then(function() { return sutPage.shouldHaveNoErrors(); })
|
||||||
.then(function() { return sutPage.executeExpression('1 3')})
|
.then(function() { return sutPage.executeExpression('1 3')})
|
||||||
.then(function() {
|
.then(function() {
|
||||||
|
|||||||
@@ -1,20 +0,0 @@
|
|||||||
describe("cloned container", function() {
|
|
||||||
var objA = { id: "a"};
|
|
||||||
var objB = { id: "b" };
|
|
||||||
var objC = { id: 'c'};
|
|
||||||
|
|
||||||
var parent = new core.Container();
|
|
||||||
|
|
||||||
parent.register('a', objA);
|
|
||||||
parent.register('b', objB);
|
|
||||||
|
|
||||||
var cloned = parent.clone();
|
|
||||||
cloned.register('a', objC);
|
|
||||||
|
|
||||||
it("should be independent from source container", function() {
|
|
||||||
expect(parent.resolve('a')).toBe(objA);
|
|
||||||
expect(cloned.resolve('a')).toBe(objC);
|
|
||||||
expect(parent.resolve('b')).toBe(objB);
|
|
||||||
expect(cloned.resolve('b')).toBe(objB);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
describe('html templates', function () {
|
|
||||||
var html = core.html;
|
|
||||||
|
|
||||||
it('should compile template', function() {
|
|
||||||
var t = "<div>{m.name}</div>";
|
|
||||||
var compiled = html.compileTemplate(t);
|
|
||||||
expect(typeof compiled).toBe("function");
|
|
||||||
expect(compiled({name: 'test'})).toBe('<div>test</div>');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should support each', function () {
|
|
||||||
var t = '{each n in m.lst}{each c in m.lst2}{n}{c}{/}{/}';
|
|
||||||
var compiled = html.compileTemplate(t);
|
|
||||||
var result = compiled({lst:[1,2,3], lst2:['a','b']});
|
|
||||||
console.log(result);
|
|
||||||
expect(result).toBe('1a1b2a2b3a3b');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@@ -1,13 +1,12 @@
|
|||||||
var app = window.app;
|
var expression = require('../../src/app/expression');
|
||||||
var expression = app.get('expression');
|
var parser = expression.parser;
|
||||||
|
|
||||||
describe("expression parse", function() {
|
describe("expression parser", function() {
|
||||||
|
|
||||||
var shouldParse = ['0x2>>1', '1 2 3', '0x1 1 2 3 5', '0x1>>0x2', '1|2', '-9', '-1|-2', '~3'];
|
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() {
|
it("should be able to parse", function() {
|
||||||
shouldParse.forEach(function(expr) {
|
shouldParse.forEach(function(expr) {
|
||||||
expect(expression.canParse(expr)).toBe(true, 'expr: ' + expr);
|
expect(parser.canParse(expr)).toBe(true, 'expr: ' + expr);
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -29,7 +28,7 @@ describe("expression parse", function() {
|
|||||||
|
|
||||||
for(input in expressionCases) {
|
for(input in expressionCases) {
|
||||||
console.log('case: ' + input);
|
console.log('case: ' + input);
|
||||||
var actual = expression.parse(input);
|
var actual = parser.parse(input);
|
||||||
var expected = expressionCases[input];
|
var expected = expressionCases[input];
|
||||||
expect(actual).toBeDefined();
|
expect(actual).toBeDefined();
|
||||||
expect(actual).not.toBe(null);
|
expect(actual).not.toBe(null);
|
||||||
@@ -58,7 +57,7 @@ describe("expression parse", function() {
|
|||||||
it("should parse hexadecimal expressions", function() {
|
it("should parse hexadecimal expressions", function() {
|
||||||
var input, i;
|
var input, i;
|
||||||
for(input in listCases) {
|
for(input in listCases) {
|
||||||
var actual = expression.parse(input);
|
var actual = parser.parse(input);
|
||||||
var expected = listCases[input];
|
var expected = listCases[input];
|
||||||
|
|
||||||
for(i =0; i<expected.length;i++) {
|
for(i =0; i<expected.length;i++) {
|
||||||
@@ -69,26 +68,26 @@ describe("expression parse", function() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it ("should parse multiple operands expression", function () {
|
it ("should parse multiple operands expression", function () {
|
||||||
var actual = expression.parse("1|2&3");
|
var actual = parser.parse("1|2&3");
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('parse operands', function() {
|
describe('parse operands', function() {
|
||||||
|
|
||||||
var hexOperand = expression.parseOperand('0x10');
|
var hexOperand = parser.parseOperand('0x10');
|
||||||
var decOperand = expression.parseOperand('10');
|
var decOperand = parser.parseOperand('10');
|
||||||
rundOperandsTest(hexOperand, decOperand);
|
rundOperandsTest(hexOperand, decOperand);
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('create operands', function() {
|
describe('create operands', function() {
|
||||||
|
|
||||||
var hexOperand = expression.createOperand(0x10, 'hex');
|
var hexOperand = parser.createOperand(0x10, 'hex');
|
||||||
var decOperand = expression.createOperand(10, 'dec');
|
var decOperand = parser.createOperand(10, 'dec');
|
||||||
rundOperandsTest(hexOperand, decOperand);
|
rundOperandsTest(hexOperand, decOperand);
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('negative operands', function () {
|
describe('negative operands', function () {
|
||||||
var op = expression.parseOperand('-0xa');
|
var op = parser.parseOperand('-0xa');
|
||||||
it('shoold have correct values', function() {
|
it('shoold have correct values', function() {
|
||||||
expect(op.value).toBe(-10);
|
expect(op.value).toBe(-10);
|
||||||
expect(op.hex).toBe('-0xa');
|
expect(op.hex).toBe('-0xa');
|
||||||
@@ -112,8 +111,6 @@ describe('should format to kind strings', function() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
function rundOperandsTest(hexOperand, decOperand) {
|
function rundOperandsTest(hexOperand, decOperand) {
|
||||||
it('should remember input form', function() {
|
it('should remember input form', function() {
|
||||||
expect(hexOperand.input).toBe('0x10');
|
expect(hexOperand.input).toBe('0x10');
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
|
var formatter = require('../../src/app/formatter');
|
||||||
|
|
||||||
describe('expression formatter', function () {
|
describe('expression formatter', function () {
|
||||||
var di = app.di.clone();
|
|
||||||
var formatter = di.resolve('formatter');
|
|
||||||
|
|
||||||
xit('should format number to binary by default', function() {
|
xit('should format number to binary by default', function() {
|
||||||
expect(formatter.formatString(10)).toBe("1010");
|
expect(formatter.formatString(10)).toBe("1010");
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
|
var hash = require('../../src/app/hash').default;
|
||||||
|
|
||||||
describe('hash arguments parser', function() {
|
describe('hash arguments parser', function() {
|
||||||
var hash = app.get('hash');
|
|
||||||
|
|
||||||
it('should parse empty', function() {
|
it('should parse empty', function() {
|
||||||
var args = hash.getArgs('');
|
var args = hash.getArgs('');
|
||||||
@@ -53,7 +54,4 @@ describe('hash arguments parser', function() {
|
|||||||
expect(args.notrack).toBe(true);
|
expect(args.notrack).toBe(true);
|
||||||
expect(args.debug).toBe(true);
|
expect(args.debug).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
});
|
});
|
||||||
11
tests/unit/parserSpec.js
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
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);
|
||||||
|
});
|
||||||
|
});
|
||||||
14
tsconfig.json
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"outDir": "./dist/",
|
||||||
|
"sourceMap": true,
|
||||||
|
"noImplicitAny": true,
|
||||||
|
"module": "commonjs",
|
||||||
|
"target": "es5",
|
||||||
|
"jsx": "react"
|
||||||
|
},
|
||||||
|
"files": [
|
||||||
|
"./src.old/components/Hello.tsx",
|
||||||
|
"./src.old/index.tsx"
|
||||||
|
]
|
||||||
|
}
|
||||||
29
webpack.config.js
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
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']
|
||||||
|
}
|
||||||
|
};
|
||||||