-
Notifications
You must be signed in to change notification settings - Fork 19
/
counter.js
85 lines (76 loc) · 3.55 KB
/
counter.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
// Define the Component's Actions:
var Inc = 'inc'; // increment the counter
var Dec = 'dec'; // decrement the counter
var Res = 'reset'; // reset counter: git.io/v9KJk
function update(model, action) { // Update function takes the current state
switch(action) { // and an action (String) runs a switch
case Inc: return model + 1; // add 1 to the model
case Dec: return model - 1; // subtract 1 from model
case Res: return 0; // reset state to 0 (Zero) git.io/v9KJk
default: return model; // if no action, return curent state.
} // (default action always returns current)
}
function view(signal, model, root) {
empty(root); // clear root element before
[ // Store DOM nodes in an array
button('+', signal, Inc), // then iterate to append them
div('count', model), // create div with stat as text
button('-', signal, Dec), // decrement counter
button('Reset', signal, Res) // reset counter
].forEach(function(el){ root.appendChild(el) }); // forEach is ES5 so IE9+
} // yes, for loop is "faster" than forEach, but readability trumps "perf" here!
// Mount Function receives all MUV and mounts the app in the "root" DOM Element
function mount(model, update, view, root_element_id) {
var root = document.getElementById(root_element_id); // root DOM element
function signal(action) { // signal function takes action
return function callback() { // and returns callback
model = update(model, action); // update model according to action
view(signal, model, root); // subsequent re-rendering
};
};
view(signal, model, root); // render initial model (once)
}
// The following are "Helper" Functions which each "Do ONLY One Thing" and are
// used in the "View" function to render the Model (State) to the Browser DOM:
// empty the contents of a given DOM element "node" (before re-rendering)
function empty(node) {
while (node.firstChild) {
node.removeChild(node.firstChild);
}
} // Inspired by: stackoverflow.com/a/3955238/1148249
function button(text, signal, action) {
var button = document.createElement('button');
var text = document.createTextNode(text); // human-readable button text
button.appendChild(text); // text goes *inside* not attrib
button.className = action; // use action as CSS class
button.id = action;
// console.log(signal, ' action:', action)
button.onclick = signal(action); // onclick tells how to process
return button; // return the DOM node(s)
} // how to create a button in JavaScript: stackoverflow.com/a/8650996/1148249
function div(divid, text) {
var div = document.createElement('div');
div.id = divid;
div.className = divid;
if(text !== undefined) { // if text is passed in render it in a "Text Node"
var txt = document.createTextNode(text);
div.appendChild(txt);
}
return div;
}
function init(doc){
document = doc; // this is used for instantiating JSDOM. ignore!
}
/* The code block below ONLY Applies to tests run using Node.js */
/* istanbul ignore next */
if (typeof module !== 'undefined' && module.exports) {
module.exports = {
view: view,
mount: mount,
update: update,
div: div,
button: button,
empty: empty,
init: init
}
} else { init(document); }