mirror of
https://github.com/BorysLevytskyi/BitwiseCmd.git
synced 2025-12-10 06:52:05 +01:00
Extracted mode views and controllers into separate features.
This commit is contained in:
31
app/app.js
31
app/app.js
@@ -1,7 +1,8 @@
|
||||
(function (should, commandr, bindr, Container) {
|
||||
|
||||
var app = {
|
||||
views: {}
|
||||
views: {},
|
||||
models: {}
|
||||
};
|
||||
|
||||
var commandHandlers = {};
|
||||
@@ -23,11 +24,8 @@
|
||||
|
||||
app.service = app.component;
|
||||
|
||||
app.controller = function(name, inst) {
|
||||
this.addControllerMixin(inst);
|
||||
this.di.register(name, inst);
|
||||
};
|
||||
|
||||
// TODO: introduce command feature
|
||||
app.command = function(name, handler) {
|
||||
var cmd = commandHandlers[name];
|
||||
|
||||
@@ -58,34 +56,15 @@
|
||||
};
|
||||
|
||||
app.bootstrap = function(rootViewElement) {
|
||||
this.rootViewElement = rootViewElement;
|
||||
invokeRunObservers();
|
||||
bindr.bindControllers(rootViewElement, app.di);
|
||||
};
|
||||
|
||||
function invokeRunObservers() {
|
||||
runObservers.forEach(function(o){ o(); });
|
||||
}
|
||||
|
||||
app.addControllerMixin = function (component) {
|
||||
component.attachView = function(viewElement) {
|
||||
|
||||
this.viewElement = viewElement;
|
||||
|
||||
if(typeof component.onViewAttached == 'function') {
|
||||
component.onViewAttached(viewElement);
|
||||
}
|
||||
};
|
||||
|
||||
component.detachView = function() {
|
||||
|
||||
this.viewElement = null;
|
||||
|
||||
if(typeof component.onViewDetached == 'function') {
|
||||
component.onViewDetached(viewElement);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
window.app = app;
|
||||
|
||||
|
||||
})(window.should, window.commandr, window.bindr, window.Container);
|
||||
@@ -24,15 +24,14 @@
|
||||
var o1 = parseInt(matches[1], 10);
|
||||
var o2 = parseInt(matches[3], 10);
|
||||
|
||||
return {
|
||||
string: matches.input,
|
||||
operand1: o1,
|
||||
sign: matches[2],
|
||||
operand2: o2,
|
||||
calculate: function() {
|
||||
return eval(this.string);
|
||||
}
|
||||
}
|
||||
var m = new app.models.BitwiseOperation();
|
||||
m.operand1 = o1;
|
||||
m.operand2 = o2;
|
||||
m.sing = matches[2];
|
||||
m.string = matches.input;
|
||||
m.result = eval(matches.input);
|
||||
|
||||
return m;
|
||||
}
|
||||
|
||||
function createListOfNumbersExpression(matches) {
|
||||
@@ -42,10 +41,7 @@
|
||||
numbers.push(parseInt(matches[i], 10));
|
||||
}
|
||||
|
||||
return {
|
||||
string:matches.input,
|
||||
operands: numbers
|
||||
}
|
||||
return app.models.BitwiseNumbers(numbers);
|
||||
}
|
||||
|
||||
})(window.app);
|
||||
@@ -20,7 +20,11 @@
|
||||
clear: function (){
|
||||
this.viewElement.innerHTML = '';
|
||||
},
|
||||
insert: function (htmlElement) {
|
||||
display: function (htmlElement) {
|
||||
if(typeof htmlElement.tagName == "undefined") {
|
||||
htmlElement = app.buildViewFor(htmlElement);
|
||||
}
|
||||
|
||||
var vw = this.viewElement;
|
||||
if(vw.childNodes.length == 0) {
|
||||
vw.appendChild(htmlElement);
|
||||
|
||||
27
app/models.js
Normal file
27
app/models.js
Normal file
@@ -0,0 +1,27 @@
|
||||
(function(app) {
|
||||
|
||||
function BitwiseOperation () {
|
||||
}
|
||||
|
||||
BitwiseOperation.prototype.calculate = function () {
|
||||
return eval(this.string);
|
||||
};
|
||||
|
||||
function BitwiseNumbers(numbers) {
|
||||
this.numbers = numbers;
|
||||
}
|
||||
|
||||
function ErrorResult(message) {
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
function HelpResult (commands) {
|
||||
this.commands = commands;
|
||||
}
|
||||
|
||||
app.models.BitwiseOperation = BitwiseOperation;
|
||||
app.models.BitwiseNumbers = BitwiseNumbers;
|
||||
app.models.ErrorResult = ErrorResult;
|
||||
app.models.HelpResult = HelpResult;
|
||||
|
||||
})(window.app);
|
||||
@@ -3,7 +3,45 @@
|
||||
app.service('html', {
|
||||
builder: function () {
|
||||
return new HtmlBuilder();
|
||||
},
|
||||
view: function (tml, model) {
|
||||
var func = template.compile(tml);
|
||||
return HtmlBuilder.createElement(func(model));
|
||||
}
|
||||
});
|
||||
|
||||
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.HtmlBuilder);
|
||||
70
app/views.js
70
app/views.js
@@ -4,42 +4,37 @@
|
||||
var formatter = app.service('formatter');
|
||||
var calc = app.service('calc');
|
||||
|
||||
function ExpressionView(expression) {
|
||||
this.expression = expression;
|
||||
}
|
||||
|
||||
ExpressionView.prototype.render = function () {
|
||||
var expr = this.expression,
|
||||
hb = app.service('html').builder();
|
||||
|
||||
console.log('Rendering expression: ', expr)
|
||||
|
||||
if(typeof expr.calculate == "function") {
|
||||
return renderCalculableExpression(expr, hb);
|
||||
} else {
|
||||
return renderListOfNumbers(expr, hb);
|
||||
app.modelView(app.models.BitwiseOperation, {
|
||||
$html:null,
|
||||
renderView: function(model) {
|
||||
return renderCalculableExpression(model, this.$html.builder());
|
||||
}
|
||||
});
|
||||
|
||||
};
|
||||
app.modelView(app.models.BitwiseNumbers, {
|
||||
$html:null,
|
||||
renderView: function(model) {
|
||||
return renderListOfNumbers(model.numbers, this.$html.builder());
|
||||
}
|
||||
});
|
||||
|
||||
function renderCalculableExpression(expr, hb) {
|
||||
var result = expr.calculate(),
|
||||
maxLen = calc.maxNumberOfBits([expr.operand1, expr.operand2, result]);
|
||||
var maxLen = calc.maxNumberOfBits([expr.operand1, expr.operand2, expr.result]);
|
||||
|
||||
hb.element('table', { class: "expression", cellspacing: "0"}, function () {
|
||||
buildRow(hb, expr.operand1, formatter.toBinaryString(expr.operand1, maxLen));
|
||||
buildRow(hb, expr.operand2, formatter.toBinaryString(expr.operand2, maxLen));
|
||||
buildRow(hb, expr.string, formatter.toBinaryString(result, maxLen), { class: 'result'});
|
||||
buildRow(hb, expr.result, formatter.toBinaryString(expr.result, maxLen), { class: 'result'});
|
||||
});
|
||||
|
||||
return hb.toHtmlElement();
|
||||
}
|
||||
|
||||
function renderListOfNumbers(expr, hb) {
|
||||
var maxLen = calc.maxNumberOfBits(expr.operands);
|
||||
function renderListOfNumbers(numbers, hb) {
|
||||
var maxLen = calc.maxNumberOfBits(numbers);
|
||||
|
||||
hb.element('table', { class: "expression", cellspacing: "0"}, function () {
|
||||
expr.operands.forEach(function(o){
|
||||
numbers.forEach(function(o){
|
||||
buildRow(hb, o, formatter.toBinaryString(o, maxLen));
|
||||
});
|
||||
});
|
||||
@@ -62,27 +57,18 @@
|
||||
}
|
||||
}
|
||||
|
||||
app.views.ExpressionView = ExpressionView;
|
||||
|
||||
})(window.app);
|
||||
|
||||
// Help View
|
||||
(function(app){
|
||||
function HelpView(commands) {
|
||||
this.commands = commands;
|
||||
}
|
||||
|
||||
HelpView.prototype.render = function() {
|
||||
var hb = app.service('html').builder();
|
||||
var commands = this.commands;
|
||||
hb.element('ul', { class: 'result' }, function() {
|
||||
commands.forEach(function(c) {
|
||||
hb.element('li', c.name + " — " + c.description);
|
||||
});});
|
||||
return hb.toHtmlElement();
|
||||
};
|
||||
|
||||
app.views.HelpView = HelpView;
|
||||
app.modelView(app.models.HelpResult, {
|
||||
$html: null,
|
||||
renderView: function(model) {
|
||||
var hb = this.$html.builder();
|
||||
var commands = model.commands;
|
||||
hb.element('ul', { class: 'result' }, function() {
|
||||
commands.forEach(function(c) {
|
||||
hb.element('li', c.name + " — " + c.description);
|
||||
});});
|
||||
return hb.toHtmlElement();
|
||||
}
|
||||
});
|
||||
|
||||
})(window.app);
|
||||
|
||||
|
||||
@@ -72,40 +72,6 @@
|
||||
|
||||
};
|
||||
|
||||
bindr.bindControllers = function (rootViewElement, container) {
|
||||
var elements = rootViewElement.querySelectorAll('[data-controller]'),
|
||||
i = 0, l = elements.length, ctrlName, ctrl, attached;
|
||||
|
||||
for(;i<l;i++){
|
||||
var element = elements[i];
|
||||
ctrlName = element.getAttribute('data-controller');
|
||||
ctrl = container.resolve(ctrlName);
|
||||
attached = [];
|
||||
|
||||
if(ctrl == null) {
|
||||
console.warn(ctrlName + ' controller wasn\'t found');
|
||||
continue;
|
||||
}
|
||||
|
||||
ctrl.attachView(element);
|
||||
attached.push(ctrl);
|
||||
|
||||
console.log(ctrlName + ' Controller: view attached');
|
||||
|
||||
if(typeof ctrl.detachView != "function") {
|
||||
continue;
|
||||
}
|
||||
|
||||
element.addEventListener('DOMNodeRemoved', function (evt) {
|
||||
if(element === evt.target) {
|
||||
ctrl.detachView();
|
||||
}
|
||||
|
||||
console.log(ctrlName + ' Controller: view detached');
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
function bindInput(model, intput, propertyName) {
|
||||
intput.addEventListener('keyup', function(e){
|
||||
model[propertyName] = e.srcElement.value;
|
||||
|
||||
67
components/controllersFeature.js
Normal file
67
components/controllersFeature.js
Normal file
@@ -0,0 +1,67 @@
|
||||
(function(app) {
|
||||
|
||||
app.controller = function(name, inst) {
|
||||
addControllerMixin(inst);
|
||||
this.di.register(name, inst);
|
||||
};
|
||||
|
||||
function addControllerMixin(component) {
|
||||
component.attachView = function(viewElement) {
|
||||
|
||||
this.viewElement = viewElement;
|
||||
|
||||
if(typeof component.onViewAttached == 'function') {
|
||||
component.onViewAttached(viewElement);
|
||||
}
|
||||
};
|
||||
|
||||
component.detachView = function() {
|
||||
|
||||
this.viewElement = null;
|
||||
|
||||
if(typeof component.onViewDetached == 'function') {
|
||||
component.onViewDetached(viewElement);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
app.run(function(){
|
||||
attachControllers(app.rootViewElement, app.di);
|
||||
});
|
||||
|
||||
function attachControllers(rootViewElement, di) {
|
||||
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 = di.resolve(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);
|
||||
@@ -35,8 +35,12 @@
|
||||
};
|
||||
|
||||
HtmlBuilder.prototype.toHtmlElement = function (){
|
||||
return HtmlBuilder.createElement(this.toString());
|
||||
};
|
||||
|
||||
HtmlBuilder.createElement = function(html) {
|
||||
var el = document.createElement('div');
|
||||
el.innerHTML = this.toString();
|
||||
el.innerHTML = html;
|
||||
return el.children[0];
|
||||
};
|
||||
|
||||
21
components/modelViewsFeature.js
Normal file
21
components/modelViewsFeature.js
Normal file
@@ -0,0 +1,21 @@
|
||||
(function(app){
|
||||
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);
|
||||
16
index.html
16
index.html
@@ -8,15 +8,19 @@
|
||||
<script type="text/javascript" src="components/bindr.js"></script>
|
||||
<script type="text/javascript" src="components/commandr.js"></script>
|
||||
<script type="text/javascript" src="components/should.js"></script>
|
||||
<script type="text/javascript" src="components/html.js"></script>
|
||||
<script type="text/javascript" src="components/htmlBuilder.js"></script>
|
||||
<script type="text/javascript" src="components/container.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/modelViewsFeature.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/views.js"></script>
|
||||
|
||||
<script type="text/javascript" src="app/dispatcher.js"></script>
|
||||
<script type="text/javascript" src="app/controllers.js"></script>
|
||||
<script type="text/javascript" src="app/services.js"></script>
|
||||
@@ -50,8 +54,7 @@
|
||||
return;
|
||||
}
|
||||
|
||||
var expressionView = new window.app.views.ExpressionView(expr);
|
||||
this.$resultView.insert(expressionView.render());
|
||||
this.$resultView.display(expr);
|
||||
|
||||
cmdArgs.commandHandled = true;
|
||||
}
|
||||
@@ -68,9 +71,9 @@
|
||||
{ name: 'clear', description: 'Clears console'}
|
||||
];
|
||||
|
||||
var helpView = new app.views.HelpView(commands);
|
||||
var model = new app.models.HelpResult(commands);
|
||||
|
||||
resultView.insert(helpView.render());
|
||||
resultView.display(model);
|
||||
cmdArgs.commandHandled = true;
|
||||
});
|
||||
|
||||
@@ -90,12 +93,13 @@
|
||||
var hb = html.builder();
|
||||
hb.element('div', { class: "result error", html: "Unknown expression: " + cmdArgs.input });
|
||||
|
||||
resultView.insert(hb.toHtmlElement());
|
||||
resultView.display(hb.toHtmlElement());
|
||||
|
||||
cmdArgs.commandHandled = true;
|
||||
});
|
||||
|
||||
app.bootstrap(document.getElementById('rootView'));
|
||||
|
||||
})();
|
||||
|
||||
</script>
|
||||
|
||||
Reference in New Issue
Block a user