|
1 |
| -// Define the Component's Actions: |
2 |
| -var Inc = 'inc'; // increment the counter |
3 |
| -var Dec = 'dec'; // decrement the counter |
4 |
| -var Res = 'reset'; // reset counter: git.io/v9KJk |
5 | 1 |
|
6 |
| -function update(model, action) { // Update function takes the current state |
7 |
| - var parts = action ? action.split('-') : []; // e.g: inc-0 where 0 is the counter "id" |
8 |
| - var act = parts[0]; |
9 |
| - var index = parts[1] || 0; |
10 |
| - var new_model = JSON.parse(JSON.stringify(model)) // "clone" the model |
11 |
| - switch(act) { // and an action (String) runs a switch |
12 |
| - case Inc: |
13 |
| - new_model.counters[index] = model.counters[index] + 1; |
14 |
| - break; |
15 |
| - case Dec: |
16 |
| - new_model.counters[index] = model.counters[index] - 1; |
17 |
| - break; |
18 |
| - case Res: // use ES6 Array.fill to create a new array with values set to 0: |
19 |
| - new_model.counters[index] = 0; |
20 |
| - break; |
21 |
| - default: return model; // if action not defined, return curent state. |
22 |
| - } |
23 |
| - return new_model; |
24 |
| -} |
25 | 2 |
|
26 |
| -function view(signal, model, root) { |
27 |
| - empty(root); // clear root element before re-rendering the App (DOM). |
28 |
| - model.counters.map(function(counter, index) { |
29 |
| - return container(index, [ // wrap DOM nodes in an "container" |
30 |
| - button('+', signal, Inc + '-' + index), // append index to action |
31 |
| - div('count', counter), // create div w/ count as text |
32 |
| - button('-', signal, Dec + '-' + index), // decrement counter |
33 |
| - button('Reset', signal, Res + '-' + index) // reset counter |
34 |
| - ]); |
35 |
| - }).forEach(function (el) { root.appendChild(el) }); // forEach is ES5 so IE9+ |
| 3 | + |
| 4 | + |
| 5 | +/** |
| 6 | + * `empty` the contents of a given DOM element "node" (before re-rendering). |
| 7 | + * This is the *fastest* way according to: stackoverflow.com/a/3955238/1148249 |
| 8 | + * @param {Object} node the exact DOM node you want to empty |
| 9 | + * @return {Boolean} returns true once the DOM node is empty. |
| 10 | + * @example |
| 11 | + * // returns true (once the 'app' node is emptied) |
| 12 | + * const node = document.getElementById('app'); |
| 13 | + * empty(node); |
| 14 | + */ |
| 15 | +function empty(node) { |
| 16 | + while (node.lastChild) { |
| 17 | + node.removeChild(node.lastChild); |
| 18 | + } |
| 19 | + return true; |
36 | 20 | }
|
37 | 21 |
|
38 | 22 | // Mount Function receives all MUV and mounts the app in the "root" DOM Element
|
39 | 23 | function mount(model, update, view, root_element_id) {
|
40 | 24 | var root = document.getElementById(root_element_id); // root DOM element
|
41 |
| - function signal(action) { // signal function takes action |
42 |
| - return function callback() { // and returns callback |
43 |
| - model = update(model, action); // update model according to action |
44 |
| - view(signal, model, root); // subsequent re-rendering |
| 25 | + function signal(action) { // signal function takes action |
| 26 | + return function callback() { // and returns callback |
| 27 | + var updatedModel = update(model, action); // update model for the action |
| 28 | + empty(root); // clear root el before rerender |
| 29 | + view(signal, updatedModel, root); // subsequent re-rendering |
45 | 30 | };
|
46 | 31 | };
|
47 |
| - view(signal, model, root); // render initial model (once) |
| 32 | + view(signal, model, root); // render initial model (once) |
48 | 33 | }
|
49 | 34 |
|
50 |
| -// The following are "Helper" Functions which each "Do ONLY One Thing" and are |
51 |
| -// used in the "View" function to render the Model (State) to the Browser DOM: |
52 |
| - |
53 |
| -// empty the contents of a given DOM element "node" (before re-rendering) |
54 |
| -function empty(node) { |
55 |
| - while (node.firstChild) { |
56 |
| - node.removeChild(node.firstChild); |
57 |
| - } |
58 |
| -} // Inspired by: stackoverflow.com/a/3955238/1148249 |
59 |
| - |
60 |
| -// https://developer.mozilla.org/en-US/docs/Web/HTML/Element/section |
61 |
| -function container(index, elements) { |
62 |
| - var con = document.createElement('section'); |
63 |
| - con.id = index; |
64 |
| - con.className = 'counter'; |
65 |
| - elements.forEach(function(el) { con.appendChild(el) }); |
66 |
| - return con; |
| 35 | +function init(doc){ |
| 36 | + document = doc; // this is used for instantiating JSDOM. ignore! |
67 | 37 | }
|
68 | 38 |
|
69 |
| -function button(text, signal, action) { |
70 |
| - var button = document.createElement('button'); |
71 |
| - var text = document.createTextNode(text); // human-readable button text |
72 |
| - button.appendChild(text); // text goes *inside* not attrib |
73 |
| - button.className = action.split('-')[0]; // use action as CSS class |
74 |
| - button.id = action; |
75 |
| - // console.log(signal, ' action:', action) |
76 |
| - button.onclick = signal(action); // onclick tells how to process |
77 |
| - return button; // return the DOM node(s) |
78 |
| -} // how to create a button in JavaScript: stackoverflow.com/a/8650996/1148249 |
79 |
| - |
80 |
| -function div(divid, text) { |
81 |
| - var div = document.createElement('div'); |
82 |
| - div.id = divid; |
83 |
| - div.className = divid; |
84 |
| - if(text !== undefined) { // if text is passed in render it in a "Text Node" |
85 |
| - var txt = document.createTextNode(text); |
86 |
| - div.appendChild(txt); |
| 39 | +/* The code block below ONLY Applies to tests run using Node.js */ |
| 40 | +/* istanbul ignore next */ |
| 41 | +if (typeof module !== 'undefined' && module.exports) { |
| 42 | + module.exports = { |
| 43 | + // view: view, |
| 44 | + mount: mount, |
| 45 | + // update: update, |
| 46 | + // div: div, |
| 47 | + // button: button, |
| 48 | + empty: empty, |
| 49 | + init: init |
87 | 50 | }
|
88 |
| - return div; |
89 |
| -} |
| 51 | +} else { init(document); } |
0 commit comments