-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathapp.js
243 lines (204 loc) · 10.2 KB
/
app.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
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
/********************************************************************************************
* What we'll learn
* ----------------
* 1. How to convert field inputs to numbers;
* 2. How to prevent false inputs (i.e., an empty input);
*/
// Budget Controller
var budgetController = (function() { // Code related to handling the budget (data) logic
// Function Constructor for Expense
var Expense = function(id, description, value) {
this.id = id;
this.description = description;
this.value = value;
};
// Function constructor for Incomes
var Income = function(id, description, value) {
this.id = id;
this.description = description;
this.value = value;
};
// Use a Data Structure to store each and every individual income & expense uniquely
var data = {
allItems: {
income: [], // each and every income and expense needs to be stored
expense: [] // separately => we use a list (array) data structure for this.
},
totals: {
income: 0, // the total income and expense would simply be a number.
expense: 0
}
};
function createID(type) {
// Every newly created item (either it is of type "Expense" or of type "Income")
// has an "id" property. If the data.allItems.<type> has no element in their arrays,
// then the first element's "id" would be 0. From there on, every element's id will
// 1 more from the previous element's id from the data.allItems.<type> array, where
// <type> can be either "expense" of "income", depending on what the user has entered
if (data.allItems[type].length === 0)
return 0;
else return data.allItems[type][data.allItems[type].length - 1].id + 1;
}
return {
addItem: function(type, des, val) {
// Get a new ID for the New Item to be created
var ID = createID(type);
// Check the type of the item we are adding, is it income or expense?
var newItem; // assign the new item to this variable
if (type === "income")
newItem = new Income(ID, des, val);
else if (type === "expense")
newItem = new Expense(ID, des, val);
// Push the newly created item into our data structure
data.allItems[type].push(newItem);
// new item should be returned to the global controller of this app
return newItem;
},
testing: function() {
// use this function only for testing purpose
return data;
}
};
})();
// UI Controller
var UIController = (function(){ // Code to manipulate the UI
/** Object desc:
* Each and every HTML DOM Element is added in this DOMStrings Object. This object
* is used in document.querySelector(DOMString.<property>) throughout our code.
*/
var DOMStrings = {
inputType: '.add__type',
inputDescription: '.add__description',
inputValue: '.add__value',
inputBtn: '.add__btn',
incomeContainer: '.income__list', // added newly
expenseContainer: '.expenses__list' // added newly
};
return {
getInput: function() {
/** function desc:
* returns the values present currently in the classes of the inputs
* which are .add__type, .add_description & .add__value, referred to as
* inputType, inputDescription & inputValue using the DOMStrings object
* which is defined above.
*
* type: income/expense option
* description: string
* value: floating point number
*/
return {
type: document.querySelector(DOMStrings.inputType).value,
description: document.querySelector(DOMStrings.inputDescription).value,
value: parseFloat(document.querySelector(DOMStrings.inputValue).value)
};
},
addListItem: function(obj, type) {
/** Desc: Creates a HTML string with placeholder string depending on the the
* type of the element being added. Then replaces the placeholder text (text
* inside %<placeholder>%) with the actual data. And finally, insert the HTML
* into the DOM.
*/
// 1. Create HTML string with placeholder text
var HTML; // string is copied from index.html into the variable
var newHTML;// used for replacing the %<placeholder>% in HTML
var element; // element to be added into the index.html at either the
// incomeContainer or at the expenseContainer. Both of which
// are properly defined in DOMStrings object above.
if (type == "income") {
element = DOMStrings.incomeContainer;
HTML = '<div class="item clearfix" id="income-%id%"><div class="item__description">%description%</div><div class="right clearfix"><div class="item__value">%value%</div><div class="item__delete"><button class="item__delete--btn"><i class="ion-ios-close-outline"></i></button></div></div></div>';
} else if (type == "expense") {
element = DOMStrings.expenseContainer;
HTML = '<div class="item clearfix" id="expense-%id%"><div class="item__description">%description%</div><div class="right clearfix"><div class="item__value">%value%</div><div class="item__percentage">21%</div><div class="item__delete"><button class="item__delete--btn"><i class="ion-ios-close-outline"></i></button></div></div></div>';
}
// 2. Replace the placeholder text with some actual data using regexp replace()
newHTML = HTML.replace("%id%", obj.id);
newHTML = newHTML.replace("%description%", obj.description);
newHTML = newHTML.replace("%value%", obj.value);
// 3. Insert the HTML into the DOM
// Find the documentation here: https://developer.mozilla.org/en-US/docs/Web/API/Element/insertAdjacentHTML
document.querySelector(element).insertAdjacentHTML("beforeend", newHTML);
},
clearFields: function() {
/** Desc: Clears all the relevant HTML fields (like inputDescription and
* inputValue from DOMStrings) after we added a new item using the addListItem
* into the page.
*/
var fields; // Used to store the NodeList type list (not an array) from the
// return of querySelectorAll() method.
var fieldsArr; // Used to store the converted fields NodeList variable as
// an array
// API Reference for querySelectorAll()
// https://developer.mozilla.org/en-US/docs/Web/API/Document/querySelectorAll
fields = document.querySelectorAll(DOMStrings.inputDescription +
", " + DOMStrings.inputValue);
//fieldsArr = Array.prototype.slice.call(fields); // instead of this, we can
fieldsArr = Array.from(fields); // simply do this.
// forEach is a fucnction that takes in a callback function.
// That callback function takes in 3 arguments: currentValue, index, array
// where, "currentValue" is the current element's value in the array pointed by
// the "index", and "array" is the entire array itself.
fieldsArr.forEach(function(curr, idx, arr) {
curr.value = "";
});
fieldsArr[0].focus(); // fieldsArr[0] is the DOMStrings.inputDescription element
},
getDOMStrings: function() {
/** function desc:
* simply returns the above defined DOMStrings object to the public scope, i.e.,
* wherever UIController.getDOMStrings() is called.
*/
return DOMStrings;
}
};
})();
// Global App Controller
var controller = (function(budgetCtrl, UICtrl){ // Code related to handling events
var setupEventListeners = function() {
// Get all the DOM classes, id's, etc
var DOM = UICtrl.getDOMStrings();
// when we press the .add__btn, we should add the expense/income
document.querySelector(DOM.inputBtn).addEventListener("click", ctrlAddItem);
// when we press the Enter/Return Key, we should add the expense/income
document.addEventListener("keypress", function(event) {
// If we press the Enter/Return Key, do the following
if (event.keyCode === 13 || event.which === 13)
ctrlAddItem();
});
};
var updateBudget = function() {
// 1. Calculate the budget
// 2. Return the budget
// 3. Display the budget on the UI
};
var ctrlAddItem = function() {
var input, newItem;
// 1. Get the field input data
input = UICtrl.getInput(); // console.log(input); // testing
/**
* We want steps 2 and above to happen, only when the input has some description and
* some income/expense value (not 0 & NaN). Therefore, we use an if statement to
* handle such a scenario, right here.
*/
if (input.description !== "" && !isNaN(input.value) && input.value > 0) {
// 2. Add the item to the budget controller
newItem = budgetCtrl.addItem(input.type, input.description, input.value);
// 3. Add the item to UI
UICtrl.addListItem(newItem, input.type);
// 4. Clear the input HTML fields
UICtrl.clearFields();
// 5. Calculate and update the budget
updateBudget();
}
};
// to be able to call the init() function from the global scope, we return an
// object that contains the init() function as follows:
return {
init: function() {
console.log("Application has started."); // test
setupEventListeners();
}
};
})(budgetController, UIController);
// This is the only piece of code that we write in the global scope
controller.init();