Moved app source files under src directory

This commit is contained in:
BorisLevitskiy
2015-04-09 15:48:15 +03:00
parent 912b957012
commit 2dc592ed2e
26 changed files with 0 additions and 0 deletions

33
src/app/analytics.js Normal file
View File

@@ -0,0 +1,33 @@
app.compose(function(){
var trackedDomains = { 'bitwisecmd.com': 'UA-61569164-1', 'borislevitskiy.github.io': 'UA-61569164-1' };
var host = window.location.host.toLowerCase();
if(trackedDomains.hasOwnProperty(host)) {
var trackingCode = trackedDomains[host];
setTimeout(doTrackAsync, 300);
}
function doTrackAsync() {
try
{
doTrack(trackingCode);
console.info('View tracked successfully');
}
catch(err) {
console.error('Failed to start tracking:', err);
}
}
function doTrack(trackingCode) {
(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');
var ga = window.ga;
ga('create', trackingCode, 'auto');
ga('send', 'pageview');
}
});

23
src/app/app.js Normal file
View File

@@ -0,0 +1,23 @@
(function (core) {
"use strict";
var di = new core.Container();
var app = new core.AppShell(di);
app.set('cmdConfig', core.ObservableObject.create({
emphasizeBytes: true
}));
app.debugMode = false;
app.bootstrap = function(rootViewElement) {
this.rootViewElement = rootViewElement;
this.set('rootView', rootViewElement)
this.initialize();
};
window.app = app;
})(window.core);

24
src/app/bitwise/calc.js Normal file
View File

@@ -0,0 +1,24 @@
app.compose(function() {
"use strict";
var should = app.get('should')
app.set('calc', {
numberOfBits: function (num) {
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);
}
});
});

View File

@@ -0,0 +1,52 @@
app.compose(function() {
"use strict";
var twoOperandsRegex = /^(\d+)\s*(<<|>>|\||\&|\^)\s*(\d+)$/;
var numbersList = /^((\d*)+\s?)+$/;
app.set('expression', {
canParse: function(string) {
return twoOperandsRegex.test(string) || numbersList.test(string);
},
parse: function(string) {
var trimmed = string.replace(/^\s+|\s+$/, '');
var matches = twoOperandsRegex.exec(trimmed);
if(matches != null) {
return createCalculableExpression(matches);
}
matches = numbersList.exec(string);
if(matches != null) {
return createListOfNumbersExpression(string)
}
}
});
function createCalculableExpression(matches) {
var o1 = parseInt(matches[1], 10);
var o2 = parseInt(matches[3], 10);
var m = new app.models.BitwiseOperation();
m.operand1 = o1;
m.operand2 = o2;
m.sign = matches[2];
m.string = matches.input;
m.result = eval(matches.input);
return m;
}
function createListOfNumbersExpression(input) {
var numbers = [];
input.split(' ').forEach(function(n){
if(n.trim().length > 0) {
numbers.push(parseInt(n));
}
});
return new app.models.BitwiseNumbers(numbers);
}
});

View File

@@ -0,0 +1,27 @@
app.compose(function() {
"use strict";
var should = app.get('should');
app.set("formatter", {
toBinaryString: function(num, totalLength) {
var binaryStr = num.toString(2),
formatted = [],
i;
if(totalLength != null) {
should.bePositiveInteger(totalLength);
}
for(i = 0; i<binaryStr.length; i++) {
formatted.push(binaryStr[i]);
}
while(totalLength > formatted.length) {
formatted.unshift('0');
}
return formatted.join('');
}
});
})

94
src/app/cmd/cmd.js Normal file
View File

@@ -0,0 +1,94 @@
app.compose(function() {
"use strict";
app.set('cmd', function() {
var handlers = [];
var is = app.get('is');
var cmdController = app.controller('cmdController');
return {
execute: function(rawInput) {
var input = rawInput.trim().toLowerCase();
var handler = findHandler(input);
if(handler != null) {
if(app.debugMode) {
invokeHandler(input, handler);
} else {
try {
invokeHandler(input, handler);
} catch (e) {
displayCommandError(input, "Error: " + e);
}
}
}
else {
displayCommandError(input, "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() {
cmdController.clear();
}
};
function displayCommandError(input, message) {
var error = new app.models.ErrorResult(message);
cmdController.display(new app.models.DisplayResult(input, error));
}
function invokeHandler (input, handler) {
var cmdResult = handler.handle(input);
if(cmdResult != null) {
var r = new app.models.DisplayResult(input, cmdResult);
cmdController.display(r);
}
}
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];
}
}
}
});
});

59
src/app/cmd/commands.js Normal file
View File

@@ -0,0 +1,59 @@
app.run(function() {
"use strict";
var cmd = app.get('cmd');
var cmdConfig = app.get('cmdConfig');
var rootView = app.get('rootView');
var shell = app.get('shell');
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() {
shell.setDarkTheme();
},
light: function () {
shell.setLightTheme();
},
about: function() {
var aboutResult = document.querySelector('.result .aboutTpl');
if(aboutResult != null) {
moveResultUp(aboutResult);
return;
}
return new app.models.ViewResult('aboutTpl');
}
});
// TODO: Make as function
cmd.command({
canHandle: function(input) { return app.get('expression').canParse(input); },
handle: function(input) {
return app.get('expression').parse(input);
}
});
function moveResultUp(helpResult) {
var container = helpResult.parentNode.parentNode;
if(container.parentNode.firstChild != container) {
var out = container.parentNode;
out.removeChild(container);
out.insertBefore(container, out.firstChild);
}
}
});

98
src/app/controllers.js Normal file
View File

@@ -0,0 +1,98 @@
app.compose(function() {
"use strict";
app.controller('expressionInputCtrl', function (){
var cmd = app.get('cmd');
return {
onViewAttached: function () {
var self = this;
self.history =[];
self.historyIndex = 0;
this.viewElement.focus();
this.viewElement.addEventListener('keyup', function (args) {
var inpt = args.target;
if (args.keyCode != 13 || inpt.value.trim().length == 0) {
return;
}
// Enter
cmd.execute(inpt.value);
self.history.unshift(inpt.value);
self.historyIndex = 0;
inpt.value = '';
});
this.viewElement.addEventListener('keydown', function(args){
if(args.keyCode == 38) {
if (self.history.length > self.historyIndex) { // up
args.target.value = self.history[self.historyIndex++];
}
args.preventDefault();
return;
}
if(args.keyCode == 40) {
if(self.historyIndex > 0) { // up
args.target.value = self.history[--self.historyIndex];
}
args.preventDefault();
}
})
}
}
});
app.controller('cmdController', function() {
var html = app.get('html');
return {
clear: function () {
this.viewElement.innerHTML = '';
},
display: function ( model) {
var view = app.buildViewFor(model);
var vw = this.viewElement;
if(vw.childNodes.length == 0) {
vw.appendChild(view);
}
else {
vw.insertBefore(view, vw.childNodes[0]);
}
}
};
});
app.controller('configPanelCtrl', {
onViewAttached: function (){
var self = this;
var cfg = app.get('cmdConfig');
self.update(cfg);
cfg.observe(function(){
self.update(cfg);
});
},
update: function (cfg) {
var emIndicator = this.viewElement.querySelector('#emphasizeBytes');
var reg = /\son/g;
if(cfg.emphasizeBytes) {
emIndicator.classList.add("on");
} else {
emIndicator.classList.remove("on");
}
}
});
});

98
src/app/modelViews.js Normal file
View File

@@ -0,0 +1,98 @@
// Expression View
app.compose(function () {
"use strict";
var formatter = app.get('formatter');
var calc = app.get('calc');
var html = app.get('html');
var cmdConfig = app.get('cmdConfig');
app.modelView(app.models.BitwiseOperation, {
renderView: function(expr) {
var maxLen = getBinaryLength([expr.operand1, expr.operand2, expr.result]);
expr.operand1Binary = formatter.toBinaryString(expr.operand1, maxLen);
expr.operand2Binary = formatter.toBinaryString(expr.operand2, maxLen);
expr.resultBinary = formatter.toBinaryString(expr.result, maxLen);
var templateId = /<<|>>/.test(expr.sign) ? 'shiftExpressionView' : 'binaryExpressionView';
var template = app.template(templateId)
var el = template.render(expr);
colorizeBits(el);
return el;
}
});
app.modelView(app.models.BitwiseNumbers, {
renderView: function(model) {
var maxLen = getBinaryLength(model.numbers);
var table = html.element('<table class="expression"></table>');
model.numbers.forEach(function(o){
var row = table.insertRow();
var decCell = row.insertCell();
decCell.className = 'label';
var binCell = row.insertCell();
binCell.className = 'bin';
decCell.textContent = o;
binCell.textContent = formatter.toBinaryString(o, maxLen);
});
colorizeBits(table);
return table;
}
});
app.modelView(app.models.ViewResult, {
renderView: function(model) {
var template = app.template(model.template);
return template.render();
}
});
app.modelView(app.models.ErrorResult, {
renderView: function(model) {
return html.element('<div class="error">{message}</div>', model);
}
});
app.modelView(app.models.DisplayResult, {
renderView: function(model) {
var resultView = app.template('resultView').render(model);
var contentView = app.buildViewFor(model.content);
resultView.querySelector('.content').appendChild(contentView);
return resultView;
}
});
function getBinaryLength(arr) {
var bits = calc.maxNumberOfBits(arr);
if(cmdConfig.emphasizeBytes && bits % 8 != 0) {
if(bits < 8) {
return 8;
}
var n = bits - (bits % 8);
return n + 8;
}
return bits;
}
function colorizeBits(container) {
var list = container.querySelectorAll('.bin');
Array.prototype.forEach.call(list, function(el){
var bin = el.textContent;
el.innerHTML = bin
.replace(/(\d{8})/g, '<span class="byte">$1</span>')
.replace(/0/g, '<span class="zero">0</span>')
.replace(/1/g, '<span class="one">1</span>');
});
}
});

34
src/app/models.js Normal file
View File

@@ -0,0 +1,34 @@
(function(app) {
"use strict";
function BitwiseOperation () {
}
BitwiseOperation.prototype.calculate = function () {
return eval(this.string);
};
function BitwiseNumbers(numbers) {
this.numbers = numbers;
}
function ErrorResult(message) {
this.message = message;
}
function ViewResult (template) {
this.template = template;
}
function DisplayResult (input, content) {
this.input = input;
this.content = content;
}
app.models.BitwiseOperation = BitwiseOperation;
app.models.BitwiseNumbers = BitwiseNumbers;
app.models.ErrorResult = ErrorResult;
app.models.ViewResult = ViewResult;
app.models.DisplayResult = DisplayResult;
})(window.app);

85
src/app/services.js Normal file
View File

@@ -0,0 +1,85 @@
(function(app, core){
"use strict";
app.set('html', core.HtmlBuilder);
app.set('is', core.is);
app.set('should', core.should);
app.set('bindr', core.bindr);
// Save config in local store
app.run(function() {
var cfg = app.get('cmdConfig');
var storeKey = 'cmdConfig';
load();
cfg.observe(function(property, value){
save();
});
function save() {
localStorage.setItem(storeKey, JSON.stringify(cfg.store()));
}
function load() {
var json = localStorage.getItem(storeKey), stored;
if(core.is.string(json)) {
stored = JSON.parse(json);
for(var key in stored) {
cfg[key] = stored[key];
}
}
}
});
app.set('shell', function(){
var rootView = app.get('rootView');
return {
setDarkTheme: function() {
rootView.classList.remove('light');
rootView.classList.add('dark');
},
setLightTheme: function() {
rootView.classList.remove('dark');
rootView.classList.add('light');
}
}
});
/*
var template = {
compile: function (template) {
var regex = /(?:{([^}]+)})/g;
var sb = [];
sb.push('(function() {')
sb.push('return function (model) { ')
sb.push('\tvar html = [];')
sb.push('\twith (model) { ')
var m, index = 0;
while ((m = regex.exec(template)) !== null) {
if(m.index > index) {
sb.push("\t\thtml.push('" + normalize(template.substr(index, m.index - index)) + "');");
}
sb.push('\t\thtml.push(' + m[1] + ');');
index = m.index + m[0].length;
}
if(index < template.length - 1) {
sb.push("\t\thtml.push('" + normalize(template.substr(index, template.length - index)) + "');");
}
sb.push('\t}');
sb.push("\treturn html.join('');");
sb.push('}');
sb.push('})()')
console.log(sb.join('\r\n'));
return eval(sb.join('\r\n'));
}
};
function normalize(str) {
return str.replace(/(\r|\n)+/g, '').replace("'", "\\\'");
}
*/
})(window.app, window.core);

View File

@@ -0,0 +1,57 @@
(function(app, core){
"use strict";
var should = core.should;
function Command(name) {
this.name = name;
this.executionHandlers = [];
}
Command.prototype.execute = function (cmdArgs) {
cmdArgs = cmdArgs || {};
cmdArgs.commandHandled = false;
for(var i=0; i<this.executionHandlers.length; i++) {
this.executionHandlers[i](cmdArgs);
if(cmdArgs.commandHandled === true) {
return;
}
}
};
Command.prototype.subscribe = function (handler) {
this.executionHandlers.push(handler);
// TODO: unsubcribe
};
app.commandHandlers = {};
app.command = function(name, handler) {
var cmd = this.commandHandlers[name];
if(cmd == null) {
cmd = this.commandHandlers[name] = new commandr.Command(name);
}
if(typeof handler == "function") {
cmd.subscribe(handler);
}
if (typeof handler == "object") {
if(typeof handler.execute != "function"){
console.warn('Given handler is an object, but doesn\'t have "execute" function');
return cmd;
}
this.di.resolveProperties(handler);
cmd.subscribe(handler.execute.bind(handler));
}
return cmd;
};
window.commandr = commandr;
})(window.app, window.core);

View File

@@ -0,0 +1,80 @@
(function(app, core) {
"use strict";
var should = core.should;
app.controller = function(name, instOrFactory) {
should.beString(name, "name");
if(instOrFactory == null) {
return this.get(name);
}
var reg = new core.Container.Registration(instOrFactory);
reg.onFirstTimeResolve = function (inst) {
addControllerMixin(inst);
};
this.set(name, reg);
};
app.run(function(){
attachControllers(app.get('rootView'), app.di);
});
function addControllerMixin(ctrl) {
ctrl.attachView = function(viewElement) {
this.viewElement = viewElement;
if(typeof ctrl.onViewAttached == 'function') {
ctrl.onViewAttached(viewElement);
}
};
ctrl.detachView = function() {
this.viewElement = null;
if(typeof ctrl.onViewDetached == 'function') {
ctrl.onViewDetached(viewElement);
}
};
}
function attachControllers(rootViewElement) {
var elements = rootViewElement.querySelectorAll('[data-controller]'),
i = 0, l = elements.length,
ctrlName,
ctrl, element;
for(;i<l;i++){
element = elements[i];
ctrlName = element.getAttribute('data-controller');
ctrl = app.controller(ctrlName);
if(ctrl == null) {
console.warn(ctrlName + ' controller wasn\'t found');
continue;
}
ctrl.attachView(element);
// console.log(ctrlName + ' Controller: view attached');
if(typeof ctrl.detachView != "function") {
continue;
}
// TODO: get rid from closure
element.addEventListener('DOMNodeRemoved', function (evt) {
if(element === evt.target) {
ctrl.detachView();
}
// console.log(ctrlName + ' Controller: view detached');
});
}
}
})(window.app, window.core);

View File

@@ -0,0 +1,42 @@
(function(app) {
"use strict";
function Template(html) {
this.html = html;
}
Template.prototype.render = function (model) {
return app.get('html').element(this.html, model);
};
app.templates = [];
app.template = function (key) {
var tpl = this.templates[key];
if(tpl == null) {
throw new Error(key + ' template is not found');
}
return tpl;
}
app.run(function() {
readTemplates(app.get('rootView'));
})
function readTemplates(containerEl) {
var els = containerEl.querySelectorAll('[data-template]');
var store = app.templates;
Array.prototype.forEach.call(els, function(element) {
var key = element.getAttribute('data-template');
if(store[key] instanceof Template) {
console.warn(key + ' templates already registered');
return;
}
store[key] = new Template(element.innerHTML);
});
}
})(window.app);

View File

@@ -0,0 +1,24 @@
(function(app, is){
"use strict";
app.modelView = function (modelCtor, builder) {
var name = getKey(modelCtor);
app.di.register(name, builder);
};
app.buildViewFor = function(model) {
var key = getKey(model.constructor);
var builder = this.di.resolve(key);
return builder.renderView(model);
};
function getKey(modelCtor) {
return getFunctionName(modelCtor) + "ViewBuilder";
}
function getFunctionName(func) {
var str = func.toString();
return str.substr(8, str.indexOf('(') - 8).trim();
}
})(window.app, window.is);

37
src/core/appShell.js Normal file
View File

@@ -0,0 +1,37 @@
(function() {
"use strict";
function AppShell(diContainer) {
this.models = {};
this.di = diContainer;
this.runList = [];
this.compositionList = [];
}
AppShell.prototype.get = function(name) {
return this.di.resolve(name);
};
AppShell.prototype.set = function(name, def) {
this.di.register(name, def);
};
AppShell.prototype.run = function(func) {
this.runList.push(func);
};
AppShell.prototype.compose = function (func) {
this.compositionList.push(func);
};
AppShell.prototype.initialize = function () {
callInvocationList(this.compositionList);
callInvocationList(this.runList);
};
function callInvocationList(functions) {
functions.forEach(function(o){ o(); });
}
window.core.AppShell = AppShell;
})();

72
src/core/bindr.js Normal file
View File

@@ -0,0 +1,72 @@
(function(){
"use strict";
var bindr = {};
bindr.bindChildren = function(container, model) {
var elements = container.querySelectorAll('[data-bind]');
Array.prototype.call(elements, function(el){
});
};
bindr.bind = function(element, model, propertyName) {
};
bindr.attachView = function(viewElement, model) {
var elements = viewElement.querySelectorAll('[data-bindr]'),
count = elements.length,
i =0, el;
for(;i<count; i++){
el = elements[i];
this.bindElement(el, model, el.getAttribute('data-bindr'))
}
};
function bindInput(model, intput, propertyName) {
bindTextInput(intput, model, propertyName);
}
function bindCheckBox(element, model, propertyName) {
element.checked = model[propertyName];
element.addEventListener('changed', function (e) {
model[propertyName] = e.target.checked == true;
});
model.observe(propertyName, function (property, value) {
if (window.event && window.event.target == element) {
return;
}
element.checked = value;
});
}
function bindTextInput(input, model, propertyName) {
input.value = model[propertyName];
input.addEventListener('keyup', function (e) {
model[propertyName] = e.target.value;
});
model.observe(propertyName, function (property, value) {
if (window.event && window.event.target == input) {
return;
}
input.value = value;
});
}
function bindHtmlElement(model, el, propertyName) {
model.observe(propertyName, function(propery, value){
el.innerHTML = value;
});
}
window.core.bindr = bindr;
})();

1
src/core/core.js Normal file
View File

@@ -0,0 +1 @@
window.core = {};

93
src/core/di.js Normal file
View File

@@ -0,0 +1,93 @@
(function(core){
"use strict";
var is = core.is;
function Container(store) {
this.store = {};
this.resolutionStack = [];
}
Container.prototype.register = function(name, def) {
var reg = this.store[name];
if(reg == null) {
if(def instanceof Registration) {
reg = def;
}
else {
reg = new Registration(def);
}
reg.name = name;
this.store[name] = reg;
}
// console.log('[' + name + '] component registered');
return reg;
};
Container.prototype.resolve = function(name) {
return resolveInternal.call(this, name);
};
function resolveInternal(name) {
if(contains(this.resolutionStack, name)) {
throw new Error("Failed to resolve service: " + name + ". Circular reference: " + this.resolutionStack.join(' < '));
}
this.resolutionStack.unshift(name);
// console.log('\tresolution path:' + this.resolutionStack.join(' < '));
var reg = this.store[name];
if(reg == null) {
throw new Error(name + ' component is not registered');
}
if(reg.resolved == null) {
reg.createInstance();
}
this.resolutionStack.shift();
// console.log('\tresolution path:' + this.resolutionStack.join(' < '));
return reg.resolved;
}
function Registration(definition) {
this.def = definition;
this.resolved = null;
}
Registration.prototype.createInstance = function() {
var def = this.def;
if(typeof def == "function") {
this.resolved = def();
}
else {
// this.resolveProperties(inst);
this.resolved = def;
}
if(is.aFunction(this.onFirstTimeResolve)){
this.onFirstTimeResolve(this.resolved);
}
};
Container.Registration = Registration;
function contains(arr, item) {
var i = arr.length;
while(i-- > 0) {
if(arr[i] === item) {
return true;
}
}
return false;
}
core.Container = Container;
})(window.core);

62
src/core/htmlBuilder.js Normal file
View File

@@ -0,0 +1,62 @@
(function(core){
"use strict";
var HtmlBuilder = {};
var should = core.should;
HtmlBuilder.element = function(template, model) {
var el = document.createElement('div');
el.innerHTML = HtmlBuilder.template(template, model);
return el.children[0];
};
HtmlBuilder.template = function(template, model) {
should.beString(template, "template");
var regex = /(?:{([^}]+)})/g, html;
if(model == null){
html = template;
} else {
html = template.replace(regex, function(m, g1) {
return HtmlBuilder.escapeHtml(model[g1]);
});
}
return html;
};
function getAttributesStr(attr) {
if(attr == null) {
return '';
}
var str = [];
for(var key in attr) {
if(key == 'html')
continue;
str.push(key + '="' + HtmlBuilder.escapeHtml(attr[key]) + '"');
}
return str.join(' ');
}
HtmlBuilder.escapeHtml = function(obj) {
if(obj == null) {
return obj;
}
if(typeof obj != 'string') {
obj = obj.toString();
}
return obj
.replace(/&/g, "&amp;")
.replace(/</g, "&lt;")
.replace(/>/g, "&gt;")
.replace(/"/g, "&quot;")
.replace(/'/g, "&#039;");
};
core.HtmlBuilder = HtmlBuilder;
})(window.core);

33
src/core/is.js Normal file
View File

@@ -0,0 +1,33 @@
(function(){
"use strict";
window.core.is = {
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;
}
};
})();

80
src/core/observable.js Normal file
View File

@@ -0,0 +1,80 @@
(function(core){
"use strict";
var is = core.is;
function ObservableObject () {
this.$store = {};
this.$executionHandlers = [];
}
ObservableObject.create = function(definition){
var obj = new ObservableObject();
for(var property in definition){
if(!definition.hasOwnProperty(property)){
continue;
}
Object.defineProperty(obj, property, {
get:ObservableObject.createGetter(property),
set:ObservableObject.createSetter(property)
});
obj[property] = definition[property];
}
return Object.seal(obj);
};
ObservableObject.createGetter = function (propertyName, store){
return function(){
return this.$store[propertyName];
}
};
ObservableObject.createSetter = function(propertyName, store){
return function(value){
this.$store[propertyName] = value;
this.notifyPropertyChanged(propertyName, value);
}
};
ObservableObject.prototype.observe = function (property, handler){
var func;
if(is.aFunction(property)) {
func = property;
}
else if(is.string(property) && is.aFunction(handler)) {
func = function (p, v) {
if(p === property) {
handler(p, v)
}
}
}
else {
console.warn('Unsupported set of arguments: ', arguments);
return;
}
var handlers = this.$executionHandlers;
var index = handlers.push(func);
return function () { handlers.splice(1, index); }
};
ObservableObject.prototype.notifyPropertyChanged = function(propertyName, value){
this.$executionHandlers.forEach(function(h){
h(propertyName, value);
});
};
ObservableObject.prototype.store = function() {
return this.$store;
};
ObservableObject.prototype.keys = function() {
return Object.keys(this.$store);
};
core.ObservableObject = ObservableObject;
})(window.core);

34
src/core/should.js Normal file
View File

@@ -0,0 +1,34 @@
(function(){
"use strict";
window.core.should = {
beNumber: function (num, name) {
this.check(typeof num == "number" && !isNaN(num), num + " is not a number");
this.check(isFinite(num), append(name, "is an infinite number"));
},
bePositiveInteger: function(num, name) {
this.beNumber(num);
this.check(num >= 0, append(name, "should be positive integer"));
},
notBeNull: function (obj, name) {
this.check(obj != null, append(name, "is null or undefined"));
},
beString: function(obj, name) {
this.check(typeof obj == "string", "should be a string");
},
check: function(assertion, message) {
if(assertion !== true) {
throw new Error (message);
}
}
};
function append(name, msg) {
return typeof name == "string" ? name + " " + msg : msg;
}
})();

36
src/css/styles.css Normal file
View File

@@ -0,0 +1,36 @@
body { font-family: Verdana; font-size: 0.8em; padding: 20px 100px 0px 100px; margin: 0 }
code { font-size: 1.2em; font-weight: bold; }
.links { float: right; position: absolute; right: 10px; top: 10px; }
.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 { color: lightgray; margin-right: 5px; }
.expression .label { font-weight: bold; padding-right: 5px; text-align: right; }
.expression .bin { letter-spacing: 3px; }
.expression .byte { margin: 0 3px; }
.expression .zero { color: #848586 }
.expression .result td { border-top: dotted 1px gray; }
.expression { font-size: 1.5em; font-family: monospace }
.help { padding: 10px; }
.help ul { list-style-type: none; margin: 0; padding: 0; }
.help p { margin-top: 0 }
.configPnl .indicator { font-size: 0.7em; background: gray; color: white; padding: 2px 5px; }
.configPnl .on { background: darkblue; }
.error { color: maroon; }
#view { padding: 10px}
/* Light */
.light .one { color: black; }
/* Dark */
.dark { background: black; color: white;}
.dark .expressionInput { background: black; color: white; }
.dark a, .dark a:visited { color: white; }

BIN
src/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

166
src/index.html Normal file
View File

@@ -0,0 +1,166 @@
<!DOCTYPE html>
<html>
<head lang="en">
<meta charset="UTF-8">
<meta name="description" content="Visualised Bitwise Operations">
<title>BitwiseCmd</title>
<link rel="shortcut icon" href="http://bitwisecmd.com/favicon.ico">
<script type="text/javascript" src="core/core.js"></script>
<script type="text/javascript" src="core/is.js"></script>
<script type="text/javascript" src="core/should.js"></script>
<script type="text/javascript" src="core/di.js"></script>
<script type="text/javascript" src="core/appShell.js"></script>
<script type="text/javascript" src="core/htmlBuilder.js"></script>
<script type="text/javascript" src="core/observable.js"></script>
<script type="text/javascript" src="app/app.js"></script>
<script type="text/javascript" src="components/controllersFeature.js"></script>
<script type="text/javascript" src="components/viewsFeature.js"></script>
<script type="text/javascript" src="components/templatesFeature.js"></script>
<script type="text/javascript" src="app/bitwise/calc.js"></script>
<script type="text/javascript" src="app/bitwise/expression.js"></script>
<script type="text/javascript" src="app/bitwise/formatter.js"></script>
<script type="text/javascript" src="app/models.js"></script>
<script type="text/javascript" src="app/modelViews.js"></script>
<script type="text/javascript" src="app/cmd/cmd.js"></script>
<script type="text/javascript" src="app/services.js"></script>
<script type="text/javascript" src="app/controllers.js"></script>
<script type="text/javascript" src="app/cmd/commands.js"></script>
<link rel="stylesheet" type="text/css" href="css/styles.css" />
</head>
<body id="rootView" class="dark">
<header>
<h1>Bitwise<span style="color: #c5c5c5;">Cmd</span></h1>
<div class="links">
<a href="https://github.com/BorisLevitskiy/BitwiseCmd">Project on GitHub</a>
</div>
<div>
<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" class="indicator on"><strong>em:</strong> Emphasize Bytes</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>
<ul>
<li><code>&</code> bitwise AND</li>
<li><code>|</code> bitwise inclusive OR</li>
<li><code>^</code> bitwise exclusive XOR</li>
<li><code><<</code> left shift</li>
<li><code>>></code> 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><a href="https://github.com/BorisLevitskiy/BitwiseCmd">Project on <strong>GitHub</strong></a></p>
</div>
</script>
<script data-template="resultView" type="text/template">
<div class="result">
<div class="input mono"><span class="cur">&gt;</span>{input}</div>
<div class="content"></div>
</div>
</script>
<script data-template="shiftExpressionView" type="text/template">
<table class="expression">
<tr>
<td class="label">{operand1}</td>
<td class="bin">{operand1Binary}</td>
</tr>
<tr class="result">
<td class="label">{operand1}{sign}{operand2}={result}</td>
<td class="bin">{resultBinary}</td>
</tr>
</table>
</script>
<script data-template="binaryExpressionView" type="text/template">
<table class="expression">
<tr>
<td></td>
<td class="label">{operand1}</td>
<td class="bin">{operand1Binary}</td>
</tr>
<tr>
<td>{sign}</td>
<td class="label">{operand2}</td>
<td class="bin">{operand2Binary}</td>
</tr>
<tr class="result">
<td>=</td>
<td class="label">{result}</td>
<td class="bin">{resultBinary}</td>
</tr>
</table>
</script>
<script type="text/javascript">
var app = window.app;
app.bootstrap(document.getElementById('rootView'));
var cmd = app.get('cmd');
cmd.execute('help');
if(window.location.hash.length > 1) {
cmd.execute(window.location.hash.substr(1).replace(/,/g,' '));
}
else {
cmd.execute('1|2');
cmd.execute('2 4 8 16 32');
}
</script>
</body>
</html>