├── LICENSE
├── README.md
├── budget.gif
├── css
└── main.css
├── favicon.png
├── index.htm
├── js
├── en.json
├── es.json
├── main.js
├── pt-br.json
└── zh-cn.json
├── manifest.appcache
└── manifest.json
/LICENSE:
--------------------------------------------------------------------------------
1 | CACHE MANIFEST
2 | # 2018-10-14:v1.0
3 |
4 | CACHE:
5 | favicon.png
6 | index.htm
7 | css/main.css
8 | js/main.js
9 | js/pt-br.json
10 | js/en.json
11 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Budget
2 |
3 | #### A smart budgeting app that that predicts the location of the expense being made.
4 |
5 | Budget is a JavaScript toy app that implements a [KNN](https://en.wikipedia.org/wiki/K-nearest_neighbors_algorithm) to predict where the expense is being made.
6 |
7 | 
8 |
9 | An online version is hosted [here](https://victorribeiro.com/budget/). You can add it to your phone as an app, if you like.
10 |
11 | It uses the day of the week, the hour of the day and the value of the expense as features to predict the location of the purchase.
12 | Its a toy app because it stores data in the localStorage of the browser. Its not reliable or robust and the code is a bit messy; it was made as an exercise (or proof of concept) after a machine learning class.
13 |
14 | The app was written in portuguese, but a quick hack was made to accept other languages. Right now you can choose between english and Brazilian portuguese.
15 |
16 | ## How to Translate
17 |
18 | Make a copy of the en.json file and replace the sentences, translating to the desired language. After that, change the variable `langSetting` in js/main.js to the name of the json file you made:
19 |
20 | ```javascript
21 | let langSetting = 'js/en.json';
22 | ```
23 |
24 | [](https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=victorqribeiro%40gmail%2ecom&lc=BR&item_name=Victor%20Ribeiro&item_number=donation¤cy_code=USD&bn=PP%2dDonationsBF%3abtn_donateCC_LG%2egif%3aNonHosted)
25 |
--------------------------------------------------------------------------------
/budget.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/victorqribeiro/budget/73d9e359ffda909e2617583f5ad6b797d37263f5/budget.gif
--------------------------------------------------------------------------------
/css/main.css:
--------------------------------------------------------------------------------
1 | html,body{
2 | padding:0;
3 | margin:0;
4 | text-align:center;
5 | font-family: Arial;
6 | height:100%;
7 | -webkit-touch-callout: none;
8 | -webkit-user-select: none;
9 | -khtml-user-select: none;
10 | -moz-user-select: none;
11 | -ms-user-select: none;
12 | user-select: none;
13 | }
14 | input, select{
15 | height:3em;
16 | font-size:1em;
17 | }
18 | input[type=text],input[type=number],input[type=password]{
19 | text-align:center;
20 | }
21 | input[type=button]{
22 | min-width:90px;
23 | }
24 | input[type=checkbox]{
25 | display:inline;
26 | height:1.1em;
27 | font-size:1.1em;
28 | }
29 | #abalancamento{
30 | display:none;
31 | }
32 | #menu{
33 | padding:10px;
34 | }
35 | #apagar{
36 | display:none;
37 | }
38 | .divapagarbotao{
39 | padding-top:10px;
40 | }
41 | .data{
42 | display:table;
43 | text-align:left;
44 | width:90%;
45 | margin: 0 auto;
46 | background-color:rgb(200,200,200);
47 | padding:5px;
48 | }
49 | .despesa{
50 | display:table;
51 | text-align:right;
52 | width:90%;
53 | margin: 0 auto;
54 | }
55 | #despesacell1{
56 | display:table-cell;
57 | width:10%;
58 | }
59 | #despesacell2{
60 | display:table-cell;
61 | width:20%;
62 | }
63 | #despesacell3{
64 | display:table-cell;
65 | width:30%;
66 | }
67 | #despesacell4{
68 | display:table-cell;
69 | width:50%;
70 | }
71 | .lockinput{
72 | min-width:200px!important;
73 | }
74 | .row{
75 | padding-bottom:10px;
76 | }
77 | .valordia{
78 | float:right;
79 | }
80 | .valormes{
81 | display:inline;
82 | }
83 | .aba{
84 | display:none;
85 | }
86 | .conteudo{
87 | max-height:65vH;
88 | height:60vH;
89 | overflow:auto;
90 | border:solid 1px rgb(200,200,200);
91 | border-left:0;
92 | border-right:0;
93 | padding-top:2px;
94 | padding-bottom:2px;
95 | }
96 | .categoriarow{
97 | width:90%;
98 | max-width: 300px;
99 | text-align:left;
100 | margin: auto auto;
101 | }
102 | #alerta{
103 | position: absolute;
104 | width: 300px;
105 | height: 200px;
106 | z-index: 9999;
107 | top: 50%;
108 | left: 50%;
109 | margin: -100px 0 0 -150px;
110 | background: rgb(200,200,200);
111 | border:solid 1px rgb(128,128,128);
112 | display:none;
113 | }
114 | #alertamsg{
115 | margin:20px;
116 | }
117 | #confirma{
118 | position: absolute;
119 | width: 300px;
120 | height: 200px;
121 | z-index: 9999;
122 | top: 50%;
123 | left: 50%;
124 | margin: -100px 0 0 -150px;
125 | background: rgb(200,200,200);
126 | border:solid 1px rgb(128,128,128);
127 | display:none;
128 | }
129 | #confirmamsg{
130 | margin:20px;
131 | }
132 | #back{
133 | position:absolute;
134 | top:0;
135 | left:0;
136 | width:100vW;
137 | height:100vH;
138 | display:none;
139 | z-index:9997;
140 | }
141 | #configPanel{
142 | width: 50%;
143 | min-width: 100px;
144 | max-width: 250px;
145 | margin: auto auto;
146 | }
147 | .btn{
148 | display: block;
149 | width:100%;
150 | }
151 |
--------------------------------------------------------------------------------
/favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/victorqribeiro/budget/73d9e359ffda909e2617583f5ad6b797d37263f5/favicon.png
--------------------------------------------------------------------------------
/index.htm:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Budget
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
20 |
32 |
36 |
42 |
43 |
44 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
59 |
68 |
69 |
70 |
--------------------------------------------------------------------------------
/js/en.json:
--------------------------------------------------------------------------------
1 | {
2 | "id" : "en",
3 | "menu" : {
4 | "expense": "Expenses",
5 | "newExpense" : "New",
6 | "settings" : "Settings",
7 | "categories" : "Categories",
8 | "category" : "Category",
9 | "reset" : "Reset",
10 | "erase" : "Delete"
11 | },
12 | "day" : [
13 | "Sunday",
14 | "Monday",
15 | "Tuesday",
16 | "Wednesday",
17 | "Thursday",
18 | "Friday",
19 | "Saturday" ],
20 | "month" : [
21 | "January",
22 | "February",
23 | "March",
24 | "April",
25 | "may",
26 | "June",
27 | "July",
28 | "August",
29 | "September",
30 | "October",
31 | "November",
32 | "December"
33 | ],
34 | "money" : "Us$",
35 | "catExists" : "Category already exists!",
36 | "invalidData" : "Invalid input!",
37 | "defaultCat" : "Market;Gas Station;Snacks;Store",
38 | "moneyInvalid" : "Invalid value! e.g: valid: 105.50",
39 | "noneExpanse" : "No expenses!",
40 | "reset" : "Reseted!",
41 | "confirm" : "Do you want to procced?",
42 | "accept" : "Continue",
43 | "cancel" : "Cancel"
44 | }
45 |
--------------------------------------------------------------------------------
/js/es.json:
--------------------------------------------------------------------------------
1 | {
2 | "id" : "es",
3 | "menu" : {
4 | "expense": "Gastos",
5 | "newExpense" : "Nuevo",
6 | "settings" : "Configuración",
7 | "categories" : "Categorías",
8 | "category" : "Categoría",
9 | "reset" : "Borrar Data",
10 | "erase" : "Eliminar"
11 | },
12 | "day" : [
13 | "Domingo",
14 | "Lunes",
15 | "Martes",
16 | "Miércoles",
17 | "Jueves",
18 | "Viernes",
19 | "Sábado"
20 | ],
21 | "month" : [
22 | "Enero",
23 | "Febrero",
24 | "Marzo",
25 | "Abril",
26 | "Mayo",
27 | "Junio",
28 | "Julio",
29 | "Agosto",
30 | "Septiembre",
31 | "Octubre",
32 | "Noviembre",
33 | "Diciembre"
34 | ],
35 | "money" : "$",
36 | "catExists" : "La categoría ya existe!",
37 | "invalidData" : "!El valor introducido es inválido!",
38 | "defaultCat" : "Supermercado;Gasolinería;Bocadillos;Tienda",
39 | "moneyInvalid" : "!Valor inválido! ejemplo: válido: 105.50",
40 | "noneExpanse" : "!No hay gastos!",
41 | "reset" : "!Reiniciar!",
42 | "confirm" : "Desea continuar?",
43 | "accept" : "Continuar",
44 | "cancel" : "Cancelar"
45 | }
46 |
--------------------------------------------------------------------------------
/js/main.js:
--------------------------------------------------------------------------------
1 | let langSetting = 'js/en.json', lang;
2 |
3 | function init(){
4 | dias = lang.day
5 | meses = lang.month;
6 | money = lang.money;
7 | data = new Date();
8 | diasemana = data.getDay();
9 | dia = data.getDate();
10 | mes = parseInt(data.getMonth())+1;
11 | ano = data.getFullYear();
12 | hora = data.getHours();
13 | minuto = data.getMinutes();
14 | if(localStorage.ultimoid){
15 | id = localStorage.getItem("ultimoid");
16 | }else{
17 | id = 1;
18 | }
19 | if(lang.id !== "pt-br")
20 | traduzGUI();
21 | recuperaDespesa();
22 | recuperaCategorias();
23 | navegacao("abadespesas");
24 | overwriteForms();
25 | }
26 | function traduzGUI(){
27 | document.getElementById("despesa").value = lang.menu.expense;
28 | document.getElementById("lancamento").value = lang.menu.newExpense;
29 | document.getElementById("config").value = lang.menu.settings;
30 | document.getElementById("categoriasbtn").value = lang.menu.categories;
31 | document.getElementById("categoriainput").placeholder = lang.menu.category;
32 | document.getElementById("resetarbtn").value = lang.menu.reset;
33 | document.getElementById("apagarbotao").value = lang.menu.erase;
34 | document.getElementById("apagarcategoria").value = lang.menu.erase;
35 | document.getElementById("cancelbotao").value = lang.cancel;
36 | document.getElementById("confirmabotao").value = lang.accept;
37 | }
38 | function overwriteForms(){
39 | document.getElementById("despesaform").addEventListener("submit", function(event){
40 | event.preventDefault();
41 | lancaDespesa();
42 | });
43 | document.getElementById("confirmaform").addEventListener("submit", function(event){
44 | event.preventDefault();
45 | document.getElementById("confirmabotao").click();
46 | });
47 | document.getElementById("categoriaform").addEventListener("submit", function(event){
48 | event.preventDefault();
49 | document.getElementById("adicionacategoria").click();
50 | });
51 | }
52 | function gravaCategorias(click=true){
53 | if(click){
54 | if(document.getElementById("categoriainput").value.match(/^[0-9a-zA-Z]+$/)){
55 | cat = localStorage.getItem("categorias").toLowerCase().split(';');
56 | v = document.getElementById("categoriainput").value;
57 | if( cat.indexOf(v.toLowerCase()) != -1 ){
58 | alerta("show",lang.catExists);
59 | document.getElementById("categoriainput").value = "";
60 | return;
61 | }
62 | newCat = localStorage.getItem("categorias")+";"+document.getElementById("categoriainput").value;
63 | localStorage.setItem("categorias",newCat);
64 | }else{
65 | alerta("show",lang.invalidData);
66 | document.getElementById("categoriainput").value = "";
67 | return;
68 | }
69 | }else{
70 | cat = document.getElementById("categorias").innerText.replace(/\n/g,';').replace(/;$/,'');
71 | localStorage.setItem("categorias",cat);
72 | }
73 | recuperaCategorias();
74 | document.getElementById("categoriainput").value = "";
75 | }
76 | function removeCategorias(confirmado=false){
77 | abreConfirma("show", removeCategorias);
78 | if(confirmado){
79 | categorias = document.getElementsByName("categoria");
80 | for(let i = categorias.length-1; i >= 0 ; i--){
81 | if(categorias[i].checked){
82 | categorias[i].parentNode.parentNode.remove();
83 | }
84 | }
85 | categorias = document.getElementsByName("categoria");
86 | gravaCategorias(false);
87 | }
88 | }
89 | function recuperaCategorias(){
90 | if(!localStorage.categorias){
91 | localStorage.setItem("categorias",lang.defaultCat);
92 | }
93 | cats = localStorage.getItem("categorias").split(";");
94 | document.getElementById("categorias").innerHTML = "";
95 | document.getElementById("local").options.length = 0;
96 | for(let i=0; i < cats.length; i++){
97 | if(cats[i].length > 0){
98 | let option = document.createElement("option");
99 | option.text = cats[i];
100 | option.value = cats[i];
101 | document.getElementById("local").appendChild(option);
102 | document.getElementById("categorias").innerHTML += "";
106 | }
107 | }
108 | habilitaApagar("categoria","apagarcategoria");
109 | }
110 | function n(n){
111 | return n > 9 ? "" + n: "0" + n;
112 | }
113 | function lancaDespesa(){
114 | data = diasemana+";"+n(dia)+"/"+n(mes)+"/"+ano+";"+n(hora)+":"+n(minuto);
115 | valor = parseFloat(document.getElementById("valor").value).toFixed(2);
116 | if(valor && !isNaN(valor)){
117 | local = document.getElementById("local").value;
118 | localStorage.setItem(id, data+";"+valor+";"+local);
119 | localStorage.setItem("ultimoid", ++id);
120 | }else{
121 | alerta("show",lang.moneyInvalid);
122 | return;
123 | }
124 | document.getElementById("valor").value = "";
125 | recuperaDespesa();
126 | }
127 | function recuperaDespesa(treina=true){
128 | let temalguem = false;
129 | _x = new Array();
130 | _y = new Array();
131 | if(id > 1){
132 | for(i = id-1; i > 0; i--){
133 | if(localStorage.getItem(i)){
134 | temalguem = true;
135 | break;
136 | }
137 | }
138 | }
139 | if(temalguem){
140 | document.getElementById("apagardespesa").style.display = "block";
141 | document.getElementById("conteudodespesas").innerHTML = "";
142 | valormes = 0;
143 | valordia = 0;
144 | ultimomes = "00";
145 | ultimoano = "0000";
146 | ultimadata = "00/00/0000";
147 | for(i = id-1; i > 0; i--){
148 | if(localStorage.getItem(i)){
149 | dados = localStorage.getItem(i).split(";");
150 | splitdata = dados[1].split('/');
151 | a = splitdata[2];
152 | m = splitdata[1];
153 | if(a == ano && m >= mes-3){
154 | if(localStorage.categorias.split(';').indexOf(dados[4]) != -1){
155 | _x.push([dados[0],dados[2].split(':')[0],parseFloat(dados[3]).toFixed(2)]);
156 | _y.push(localStorage.categorias.split(';').indexOf(dados[4]));
157 | }
158 | }
159 | if(a != ultimoano){
160 | ultimoano = a;
161 | }
162 | if(m != ultimomes){
163 | ultimomes = m;
164 | if( i != id-1){
165 | document.getElementById("conteudodespesas").innerHTML += "
";
166 | }
167 | document.getElementById("conteudodespesas").innerHTML += meses[parseInt(ultimomes)-1]
168 | +" ";
169 | valormes = 0;
170 | }
171 |
172 | if(dados[1] != ultimadata){
173 | ultimadata = dados[1];
174 | document.getElementById("conteudodespesas").innerHTML += ""+dias[parseInt(dados[0])]+", "+dados[1]
175 | +"
";
176 | valordia = 0;
177 | }
178 |
179 | valordia += parseFloat(dados[3]);
180 | valormes += parseFloat(dados[3]);
181 | document.getElementById("conteudodespesas").innerHTML += ""
182 | + "
"
183 | + "
"
185 | + "
"+dados[2]+"
"
186 | + "
"+money+" "+parseFloat(dados[3]).toFixed(2)+"
"
187 | + "
"+dados[4]+"
";
188 | document.getElementById(""+ultimomes+ultimoano).innerHTML = money+" "+valormes.toFixed(2);
189 | document.getElementById(""+dados[1]).innerHTML = money+" "+valordia.toFixed(2);
190 | }
191 | }
192 | habilitaApagar("despesa","apagarbotao");
193 | }else{
194 | document.getElementById("conteudodespesas").innerHTML = lang.noneExpanse;
195 | document.getElementById("apagardespesa").style.display = "none";
196 | }
197 | }
198 | function alerta(what, msg=""){
199 | switch(what){
200 | case "show" :
201 | document.getElementById("alertamsg").innerHTML = msg;
202 | document.getElementById("back").style.display = "block";
203 | document.getElementById("alerta").style.display = "block";
204 | break;
205 | case "hide" :
206 | document.getElementById("back").style.display = "none";
207 | document.getElementById("alerta").style.display = "none";
208 | break;
209 | }
210 | }
211 | function abreConfirma(what, callback){
212 | switch(what){
213 | case "show" :
214 | document.getElementById("back").style.display = "block";
215 | document.getElementById("confirma").style.display = "block";
216 | document.getElementById("confirmamsg").innerHTML = lang.confirm;
217 | document.getElementById("confirmabotao").onclick = function(){
218 | callback( true );
219 | abreConfirma('hide');
220 | };
221 | break;
222 | case "hide" :
223 | document.getElementById("back").style.display = "none";
224 | document.getElementById("confirma").style.display = "none";
225 | break;
226 | }
227 | }
228 | function habilitaApagar(checkbox,botao){
229 | algumselecionado = false;
230 | for(i = 0; i < document.getElementsByName(checkbox).length; i++){
231 | if(document.getElementsByName(checkbox)[i].checked){
232 | algumselecionado = true;
233 | break;
234 | }
235 | }
236 | if(!algumselecionado){
237 | document.getElementById(botao).disabled = true;
238 | }else{
239 | document.getElementById(botao).disabled = false;
240 | }
241 | }
242 | function removeDespesa(confirmado=false){
243 | abreConfirma("show",removeDespesa);
244 | if(confirmado){
245 | let valores = document.getElementsByName("despesa");
246 | for(i = valores.length-1; i >= 0 ; i--){
247 | if(valores[i].checked){
248 | localStorage.removeItem(valores[i].value);
249 | }
250 | }
251 | recuperaDespesa();
252 | }
253 | }
254 | function navegacao(aba){
255 | abas = document.getElementsByClassName("aba");
256 | for(i = 0; i < abas.length; i++){
257 | abas[i].style.display = "none";
258 | }
259 | document.getElementById(aba).style.display = "block";
260 | }
261 |
262 | function resetar(confirmado=false){
263 | abreConfirma("show", resetar);
264 | if(confirmado){
265 | localStorage.clear();
266 | init();
267 | }
268 | }
269 | function predizCategoria(){
270 | if(_x && _x.length > 0){
271 | let textInput = document.getElementById('valor');
272 | let hora = new Date().getHours();
273 |
274 | let timeout = null;
275 |
276 | clearTimeout(timeout);
277 | timeout = setTimeout(function () {
278 | document.getElementById("local").selectedIndex = previsao([diasemana,hora,textInput.value]);
279 | }, 500);
280 | }
281 | }
282 | function previsao(_p,k=3){
283 | let distancia = [],
284 | resposta,
285 | compare,
286 | r = {},
287 | resultado,
288 | _d;
289 | for(i=0; i < _x.length; i++){
290 | _d = 0;
291 | for(j=0; j < _x[i].length; j++){
292 | _d += Math.abs( parseFloat(_p[j]) - parseFloat(_x[i][j]) );
293 | }
294 | distancia.push( _d );
295 | }
296 | resultado = [];
297 | if(distancia.length >= k*k){
298 | for(i=0; i < k; i++){
299 | let iDist = distancia.indexOf(Math.min.apply( null, distancia ));
300 | resultado.push( _y[iDist] );
301 | distancia.splice( iDist,1 );
302 | }
303 | r = {};
304 | compare = -1;
305 | for(i=0; i < k; i++){
306 | if( !r[resultado[i]] ){
307 | r[resultado[i]] = 1;
308 | }else{
309 | r[resultado[i]] += 1
310 | }
311 | if( r[resultado[i]] > compare ){
312 | compare = r[resultado[i]];
313 | resposta = resultado[i];
314 | }
315 | }
316 | }else{
317 | resposta = _y[distancia.indexOf(Math.min.apply( null, distancia ))];
318 | }
319 | return resposta;
320 | }
321 | window.onload = () => {
322 | window.applicationCache.addEventListener('updateready', function(e) {
323 | if (window.applicationCache.status == window.applicationCache.UPDATEREADY) {
324 | window.applicationCache.swapCache();
325 | window.location.reload();
326 | }
327 | }, false);
328 | fetch(langSetting)
329 | .then( response => {
330 | response.json().then( result => {
331 | lang = result;
332 | init();
333 | });
334 | }).catch( e => console.error( e ) );
335 | };
336 |
--------------------------------------------------------------------------------
/js/pt-br.json:
--------------------------------------------------------------------------------
1 | {
2 | "id": "pt-br",
3 | "menu" : {
4 | "expense": "Despesas",
5 | "new": "Novo",
6 | "settings" : "Configurações",
7 | "categories" : "Categorias",
8 | "category" : "Category",
9 | "reset" : "Resetar",
10 | "erase" : "Apagar"
11 | },
12 | "day" : [
13 | "Domingo",
14 | "Segunda",
15 | "Terça",
16 | "Quarta",
17 | "Quinta",
18 | "Sexta",
19 | "Sábado"
20 | ],
21 | "month" : [
22 | "Janeiro",
23 | "Fevereiro",
24 | "Março",
25 | "Abril",
26 | "Maio",
27 | "Junho",
28 | "Julho",
29 | "Agosto",
30 | "Setembro",
31 | "Outurbo",
32 | "Novembro",
33 | "Dezembro"
34 | ],
35 | "money" : "R$",
36 | "catExists" : "Categoria já existe!",
37 | "invalidData" : "Dados inválidos!",
38 | "defaultCat" : "Mercado;Posto;Lanche;Gasolina",
39 | "moneyInvalid" : "Valor inválido! ex. válido: 105.50",
40 | "noneExpanse" : "Nenhuma despesa lançada!",
41 | "reset" : "Resetado!",
42 | "confirm" : "Confirma?",
43 | "accept" : "Aceitar",
44 | "cancel" : "Cancel"
45 | }
46 |
--------------------------------------------------------------------------------
/js/zh-cn.json:
--------------------------------------------------------------------------------
1 | {
2 | "id" : "zh-cn",
3 | "menu" : {
4 | "expense": "消费",
5 | "newExpense" : "添加",
6 | "settings" : "设置",
7 | "categories" : "分类",
8 | "category" : "分类",
9 | "reset" : "重置",
10 | "erase" : "删除"
11 | },
12 | "day" : [ "星期天",
13 | "星期一",
14 | "星期二",
15 | "星期三",
16 | "星期四",
17 | "星期五",
18 | "星期六" ],
19 | "month" : [ "一月",
20 | "二月",
21 | "三月",
22 | "四月",
23 | "五月",
24 | "六月",
25 | "七月",
26 | "八月",
27 | "九月",
28 | "十月",
29 | "十一月",
30 | "十二月" ],
31 | "money" : "RMB",
32 | "catExists" : "分类已存在!",
33 | "invalidData" : "输入错误",
34 | "defaultCat" : "市场;汽油站;零食;商店",
35 | "moneyInvalid" : "输入错误!正确例子:105.50",
36 | "noneExpanse" : "无消费!",
37 | "reset" : "重置了!",
38 | "confirm" : "确定?",
39 | "accept" : "接受",
40 | "cancel" : "取消"
41 | }
42 |
--------------------------------------------------------------------------------
/manifest.appcache:
--------------------------------------------------------------------------------
1 | CACHE MANIFEST
2 | # 2018-10-15:v1.1
3 |
4 | CACHE:
5 | manifest.json
6 | favicon.png
7 | index.htm
8 | css/main.css
9 | js/main.js
10 | js/pt-br.json
11 | js/en.json
12 |
--------------------------------------------------------------------------------
/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "Budget",
3 | "short_name": "Budget",
4 | "description": "A simply budgeting app that makes predictions",
5 | "start_url": "index.htm",
6 | "display": "standalone",
7 | "orientation": "portrait",
8 | "icons": [
9 | {
10 | "src": "favicon.png",
11 | "sizes": "128x128",
12 | "type": "image/png",
13 | "density": 1
14 | }
15 | ]
16 | }
17 |
--------------------------------------------------------------------------------