├── test ├── multi │ ├── 1.txt │ ├── 2.txt │ ├── 3.txt │ ├── 4.txt │ ├── 5.txt │ └── README.md ├── error.txt ├── ok-generator.py ├── ok.txt └── functions.js ├── netlify.toml ├── public ├── google2d9f556896e1d223.html ├── config.js ├── README.md └── privacidade.txt ├── .gitignore ├── util ├── lint.sh ├── set-commit-id.sh ├── gen-cloud-analytics.html ├── deploy.sh ├── gen-beta-config ├── gen-cloud-config ├── conversor │ ├── Quicken.qif │ ├── MSMoney97.qif │ ├── MSMoney98.qif │ ├── MSMoney99.qif │ ├── myMoneyLog.txt │ ├── moneylog4-ok.txt │ ├── moneylog4.txt │ ├── Caixa.ofc │ ├── Real.ofx │ ├── Caixa.ofx │ └── conversor.html ├── gen-browser ├── gen-cloud ├── portable-vs-browser.diff └── gen-portable ├── storage ├── drivers │ ├── html.js │ ├── browser.js │ ├── filesystem.js │ └── googledrive.js └── index.js ├── README.md ├── sample ├── widget-hello-world.js ├── config-dev.js ├── widget-hello-checkbox.js ├── widget-nerd-toy.js ├── widget-hello-translation.js ├── data-es.txt ├── data-en.txt ├── data-pt.txt └── config-pt.js ├── css ├── browser.css ├── cloud.css ├── portable.css ├── print.css └── mobile.css ├── LICENSE.txt ├── .eslintrc.yml ├── NEWS.t2t ├── moneylog.html ├── moneylog.css └── NEWS.html /test/multi/1.txt: -------------------------------------------------------------------------------- 1 | 2009-08-01 1 um|Um 2 | -------------------------------------------------------------------------------- /test/multi/2.txt: -------------------------------------------------------------------------------- 1 | 2009-08-02 2 dois|Dois 2 | -------------------------------------------------------------------------------- /test/multi/3.txt: -------------------------------------------------------------------------------- 1 | 2009-08-03 3 tres|Três 2 | -------------------------------------------------------------------------------- /test/multi/4.txt: -------------------------------------------------------------------------------- 1 | 2009-08-04 4 quatro|Quatro 2 | -------------------------------------------------------------------------------- /test/multi/5.txt: -------------------------------------------------------------------------------- 1 | 2009-08-05 5 cinco|Cinco 2 | -------------------------------------------------------------------------------- /netlify.toml: -------------------------------------------------------------------------------- 1 | [build] 2 | publish = "public" 3 | command = "util/deploy.sh" 4 | -------------------------------------------------------------------------------- /public/google2d9f556896e1d223.html: -------------------------------------------------------------------------------- 1 | google-site-verification: google2d9f556896e1d223.html -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # user personal settings for the Beta version 2 | /config.js 3 | 4 | # user data folder for the Beta version 5 | /txt/ 6 | -------------------------------------------------------------------------------- /util/lint.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Call ESLint to lint our JavaScript files. 3 | # Lint rules are on .eslintrc.yml at repo root. 4 | 5 | # Enter repo root 6 | cd $(dirname "$0") 7 | cd .. 8 | 9 | # Lint all JavaScript files 10 | eslint --fix \ 11 | *.js \ 12 | */*.js \ 13 | */*/*.js 14 | -------------------------------------------------------------------------------- /storage/drivers/html.js: -------------------------------------------------------------------------------- 1 | // HTML:
 2 | 
 3 | ml.storage.drivers.html = {
 4 | 	id: 'html',
 5 | 	name: '
 element',
 6 | 	config: {
 7 | 		isAsync: false,
 8 | 		isEditable: false,
 9 | 		isFileBased: false,
10 | 		isReloadable: false,
11 | 		loadDataAtSetup: true
12 | 	},
13 | 
14 | 	read: function () {
15 | 		return document.getElementById('data').innerText;
16 | 	},
17 | 
18 | 	init: function () {
19 | 		// none
20 | 	}
21 | };
22 | 


--------------------------------------------------------------------------------
/util/set-commit-id.sh:
--------------------------------------------------------------------------------
 1 | #!/bin/bash
 2 | # Aurelio Jargas
 3 | #
 4 | # Set the hash for the latest Git commit in $commit_id variable
 5 | #
 6 | # Usage:
 7 | #	source set-commit-id.sh
 8 | #
 9 | 
10 | commit_id=$(git log -1 --format="%H" | cut -c 1-8)  # 8 chars are enough
11 | 
12 | # Check
13 | if ! echo "$commit_id" | grep '^[0-9a-f]\{8\}$' > /dev/null
14 | then
15 | 	echo "ERROR: Invalid Git commit hash: '$commit_id'. Aborting." >&2
16 | 	exit 1
17 | fi
18 | 


--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
 1 | # MoneyLog Experience, by Aurelio Jargas
 2 | 
 3 | English website: https://aurelio.net/soft/moneylog/
 4 | 
 5 | Portuguese website: https://aurelio.net/moneylog/
 6 | 
 7 | 
 8 | ## Deploy
 9 | 
10 | Every commit to the `master` branch triggers an app deploy to https://moneylog.aurelio.net.
11 | 
12 | The deploy logs are in https://app.netlify.com/sites/moneylog/deploys.
13 | 
14 | More information in the [public/](https://github.com/aureliojargas/moneylog/blob/master/public/) folder.
15 | 


--------------------------------------------------------------------------------
/public/config.js:
--------------------------------------------------------------------------------
 1 | // See configuration help at:
 2 | // https://aurelio.net/moneylog/config/
 3 | 
 4 | // Set app lang
 5 | // Using portuguese because the userbase is mainly from Brazil
 6 | lang = 'pt';
 7 | 
 8 | // Disable drivers that do not make sense on this online version
 9 | ml.storage.availableDrivers = [
10 | 	// 'html',
11 | 	// 'filesystem',
12 | 	'browser',
13 | 	'googledrive'
14 | ];
15 | 
16 | // Set the default driver activated at startup
17 | ml.storage.defaultDriver = 'browser';
18 | 


--------------------------------------------------------------------------------
/sample/widget-hello-world.js:
--------------------------------------------------------------------------------
 1 | // --------------------------------------------------------------------
 2 | // Hello World Widget
 3 | // by Aurelio Jargas 2012-02-17
 4 | //
 5 | // A sample widget. Simple. Readable.
 6 | //
 7 | // Copy/paste all this code to the end of your config.js file.
 8 | 
 9 | 
10 | // Create a new widget instance (id, name, instance name)
11 | var HelloWorld = new Widget('hello-world', 'Hello World', 'HelloWorld');
12 | 
13 | // Set widget contents
14 | HelloWorld.populate = function () {
15 | 	this.content.innerHTML = 'Hellooo!';
16 | };
17 | 


--------------------------------------------------------------------------------
/util/gen-cloud-analytics.html:
--------------------------------------------------------------------------------
 1 | 
 2 | 
15 | 


--------------------------------------------------------------------------------
/util/deploy.sh:
--------------------------------------------------------------------------------
 1 | #!/bin/bash
 2 | # Build website https://moneylog.aurelio.net
 3 | # The actual deploy is handled automatically by Netlify.
 4 | 
 5 | # Enter repo root
 6 | cd $(dirname "$0")
 7 | cd ..
 8 | root="$PWD"
 9 | 
10 | deploy_dir="$root/public"
11 | 
12 | # Copy main file
13 | cp moneylog.html "$deploy_dir/index.html"
14 | 
15 | # Copy sample file (localStorage default text)
16 | mkdir -p "$deploy_dir/sample"
17 | cp sample/data-pt.txt "$deploy_dir/sample"
18 | 
19 | # Copy other files
20 | cp -r \
21 |   moneylog.css \
22 |   moneylog.js \
23 |   css/ \
24 |   storage/ \
25 |   "$deploy_dir"
26 | 
27 | 


--------------------------------------------------------------------------------
/public/README.md:
--------------------------------------------------------------------------------
 1 | # Deploy folder for the MoneyLog app
 2 | 
 3 | This is the root of the https://moneylog.aurelio.net website.
 4 | 
 5 | - Put static site-only files here.
 6 | 
 7 | - Repository files are added here at deploy time by the
 8 |   [util/deploy.sh](https://github.com/aureliojargas/moneylog/blob/master/util/deploy.sh)
 9 |   script.
10 | 
11 | The deploy is handled automatically by Netlify:
12 | every commit to the `master` branch triggers a deploy.
13 | 
14 | Deploy logs at: https://app.netlify.com/sites/moneylog/deploys/
15 | 
16 | See [netlify.toml](https://github.com/aureliojargas/moneylog/blob/master/netlify.toml)
17 | for the build commands.
18 | 


--------------------------------------------------------------------------------
/sample/config-dev.js:
--------------------------------------------------------------------------------
 1 | // MoneyLog config for devs
 2 | //
 3 | // Handy configurations to test MoneyLog features.
 4 | // Copy to repo root as config.js and fiddle.
 5 | 
 6 | // Set the default driver at app init
 7 | ml.storage.defaultDriver = 'html';
 8 | ml.storage.defaultDriver = 'browser';
 9 | ml.storage.defaultDriver = 'googledrive';
10 | ml.storage.defaultDriver = 'filesystem';
11 | 
12 | // Test files for the filesystem driver
13 | ml.storage.drivers.filesystem.defaultFile = 'sample/data-en.txt';
14 | ml.storage.drivers.filesystem.dataFiles = [
15 | 	'sample/data-pt.txt',
16 | 	'sample/data-en.txt',
17 | 	'sample/data-es.txt'
18 | ];
19 | 
20 | // LEGACY global configuration for the filesystem driver.
21 | // Won't be applied if the previous two configs are active.
22 | dataFilesDefault = 'sample/data-es.txt';
23 | dataFiles = [
24 | 	'sample/data-pt.txt',
25 | 	'sample/data-es.txt'
26 | ];
27 | 


--------------------------------------------------------------------------------
/util/gen-beta-config:
--------------------------------------------------------------------------------
 1 | #!/bin/bash
 2 | # Aurelio Jargas, https://aurelio.net/moneylog/
 3 | #
 4 | # Generates the commented config.js sample, with Beta specific sections.
 5 | #
 6 | # Usage:
 7 | #	gen-beta-config [--lang XX]
 8 | #
 9 | # Examples:
10 | #	gen-beta-config                  # Portuguese version
11 | #	gen-beta-config --lang en        # English version
12 | 
13 | 
14 | cd $(dirname "$0")
15 | 
16 | lang="pt"           # pt, en, es, ca. Use --lang to change it.
17 | 
18 | # Option --lang
19 | if test "$1" = '--lang'
20 | then
21 | 	lang=$2
22 | 	shift
23 | 	shift
24 | fi
25 | 
26 | file_path="../sample/config-$lang.js"
27 | 
28 | if test -f "$file_path"
29 | then
30 | 	sed '
31 | 		# Remove Dropbox-specific data
32 | 		/^\/\/ /, /^\/\/ <\/Dropbox>/ d
33 | 
34 | 		# Remove marker lines
35 | 		/^\/\/ / d
36 | 		/^\/\/ <\/Beta>/ d
37 | 	
38 | 	' "$file_path"
39 | else
40 | 	echo "Sorry, file not found: $file_path"
41 | fi
42 | 


--------------------------------------------------------------------------------
/util/gen-cloud-config:
--------------------------------------------------------------------------------
 1 | #!/bin/bash
 2 | # Aurelio Jargas, https://aurelio.net/moneylog/
 3 | #
 4 | # Generates the commented config.js sample, with Dropbox specific sections.
 5 | #
 6 | # Usage:
 7 | #	gen-cloud-config [--lang XX]
 8 | #
 9 | # Examples:
10 | #	gen-cloud-config                   # Portuguese version
11 | #	gen-cloud-config --lang en         # English version
12 | 
13 | 
14 | cd $(dirname "$0")
15 | 
16 | lang="pt"           # pt, en, es, ca. Use --lang to change it.
17 | 
18 | # Option --lang
19 | if test "$1" = '--lang'
20 | then
21 | 	lang=$2
22 | 	shift
23 | 	shift
24 | fi
25 | 
26 | file_path="../sample/config-$lang.js"
27 | 
28 | if test -f "$file_path"
29 | then
30 | 	sed '
31 | 		# Remove Beta-specific data
32 | 		/^\/\/ /, /^\/\/ <\/Beta>/ d
33 | 
34 | 		# Remove marker lines
35 | 		/^\/\/ / d
36 | 		/^\/\/ <\/Dropbox>/ d
37 | 	
38 | 	' "$file_path"
39 | else
40 | 	echo "Sorry, file not found: $file_path"
41 | fi
42 | 


--------------------------------------------------------------------------------
/util/conversor/Quicken.qif:
--------------------------------------------------------------------------------
 1 | 
 2 | !Account
 3 | N1234_1234567
 4 | TBank
 5 | ^
 6 | !Type:Bank
 7 | D07/01'09
 8 | T5.37
 9 | N0001813
10 | MJUROS POUPANCA
11 | ^
12 | D07/01'09
13 | T0.70
14 | N0002251
15 | MCORRECAO POUPANCA
16 | ^
17 | D07/02'09
18 | T-650.00
19 | N0007389
20 | MTRANSF.P/ FULANO DA SILVA
21 | ^
22 | D07/03'09
23 | T1234.56
24 | N0123482
25 | MDOC   REM  12345678000102
26 | ^
27 | D07/04'09
28 | T567.00
29 | N0002452
30 | MTRANSF.DE CICRANO DA SILVA
31 | ^
32 | D07/05'09
33 | T-234.56
34 | N0001555
35 | MTITULO COBRANCA-IB
36 | ^
37 | D07/06'09
38 | T-999.99
39 | N0000198
40 | MFATURA REALVISA
41 | ^
42 | D07/07'09
43 | T-34.67
44 | N0000077
45 | MCONTA DE LUZ
46 | ^
47 | D07/08'09
48 | T345.67
49 | N0002234
50 | MTED REM   12345678000102
51 | ^
52 | D07/13'09
53 | T-21.00
54 | N0001234
55 | MCOMPRA VISA ELECTRON
56 | ^
57 | D07/21'09
58 | T-100.00
59 | N0000123
60 | MSAQUE COM CARTAO
61 | ^
62 | 
63 | 


--------------------------------------------------------------------------------
/util/conversor/MSMoney97.qif:
--------------------------------------------------------------------------------
 1 | 
 2 | !Account
 3 | N1234_1234567
 4 | TBank
 5 | ^
 6 | !Type:Bank
 7 | D01/07'09
 8 | T5.37
 9 | N0001813
10 | MJUROS POUPANCA
11 | ^
12 | D01/07'09
13 | T0.70
14 | N0002251
15 | MCORRECAO POUPANCA
16 | ^
17 | D02/07'09
18 | T-650.00
19 | N0007389
20 | MTRANSF.P/ FULANO DA SILVA
21 | ^
22 | D03/07'09
23 | T1234.56
24 | N0123482
25 | MDOC   REM  12345678000102
26 | ^
27 | D04/07'09
28 | T567.00
29 | N0002452
30 | MTRANSF.DE CICRANO DA SILVA
31 | ^
32 | D05/07'09
33 | T-234.56
34 | N0001555
35 | MTITULO COBRANCA-IB
36 | ^
37 | D06/07'09
38 | T-999.99
39 | N0000198
40 | MFATURA REALVISA
41 | ^
42 | D07/07'09
43 | T-34.67
44 | N0000077
45 | MCONTA DE LUZ
46 | ^
47 | D08/07'09
48 | T345.67
49 | N0002234
50 | MTED REM   12345678000102
51 | ^
52 | D13/07'09
53 | T-21.00
54 | N0001234
55 | MCOMPRA VISA ELECTRON
56 | ^
57 | D21/07'09
58 | T-100.00
59 | N0000123
60 | MSAQUE COM CARTAO
61 | ^
62 | 
63 | 


--------------------------------------------------------------------------------
/util/conversor/MSMoney98.qif:
--------------------------------------------------------------------------------
 1 | 
 2 | !Account
 3 | N1234_1234567
 4 | TBank
 5 | ^
 6 | !Type:Bank
 7 | D01/07'09
 8 | T5.37
 9 | N0001813
10 | MJUROS POUPANCA
11 | ^
12 | D01/07'09
13 | T0.70
14 | N0002251
15 | MCORRECAO POUPANCA
16 | ^
17 | D02/07'09
18 | T-650.00
19 | N0007389
20 | MTRANSF.P/ FULANO DA SILVA
21 | ^
22 | D03/07'09
23 | T1234.56
24 | N0123482
25 | MDOC   REM  12345678000102
26 | ^
27 | D04/07'09
28 | T567.00
29 | N0002452
30 | MTRANSF.DE CICRANO DA SILVA
31 | ^
32 | D05/07'09
33 | T-234.56
34 | N0001555
35 | MTITULO COBRANCA-IB
36 | ^
37 | D06/07'09
38 | T-999.99
39 | N0000198
40 | MFATURA REALVISA
41 | ^
42 | D07/07'09
43 | T-34.67
44 | N0000077
45 | MCONTA DE LUZ
46 | ^
47 | D08/07'09
48 | T345.67
49 | N0002234
50 | MTED REM   12345678000102
51 | ^
52 | D13/07'09
53 | T-21.00
54 | N0001234
55 | MCOMPRA VISA ELECTRON
56 | ^
57 | D21/07'09
58 | T-100.00
59 | N0000123
60 | MSAQUE COM CARTAO
61 | ^
62 | 
63 | 


--------------------------------------------------------------------------------
/util/conversor/MSMoney99.qif:
--------------------------------------------------------------------------------
 1 | 
 2 | !Account
 3 | N1234_1234567
 4 | TBank
 5 | ^
 6 | !Type:Bank
 7 | D01/07/09
 8 | T5.37
 9 | N0001813
10 | MJUROS POUPANCA
11 | ^
12 | D01/07/09
13 | T0.70
14 | N0002251
15 | MCORRECAO POUPANCA
16 | ^
17 | D02/07/09
18 | T-650.00
19 | N0007389
20 | MTRANSF.P/ FULANO DA SILVA
21 | ^
22 | D03/07/09
23 | T1234.56
24 | N0123482
25 | MDOC   REM  12345678000102
26 | ^
27 | D04/07/09
28 | T567.00
29 | N0002452
30 | MTRANSF.DE CICRANO DA SILVA
31 | ^
32 | D05/07/09
33 | T-234.56
34 | N0001555
35 | MTITULO COBRANCA-IB
36 | ^
37 | D06/07/09
38 | T-999.99
39 | N0000198
40 | MFATURA REALVISA
41 | ^
42 | D07/07/09
43 | T-34.67
44 | N0000077
45 | MCONTA DE LUZ
46 | ^
47 | D08/07/09
48 | T345.67
49 | N0002234
50 | MTED REM   12345678000102
51 | ^
52 | D13/07/09
53 | T-21.00
54 | N0001234
55 | MCOMPRA VISA ELECTRON
56 | ^
57 | D21/07/09
58 | T-100.00
59 | N0000123
60 | MSAQUE COM CARTAO
61 | ^
62 | 
63 | 


--------------------------------------------------------------------------------
/test/error.txt:
--------------------------------------------------------------------------------
 1 | # https://aurelio.net/moneylog/
 2 | # MoneyLog ERROR test file.
 3 | # Uncomment one by one to see the error.
 4 | #
 5 | # Use these settings to test this file:
 6 | #   reportType = 'd';
 7 | #   checkDateFrom = false;
 8 | #   useLegacyDataFormat = false;
 9 | #   dataFiles = ['test/error.txt'];
10 | 
11 | 
12 | ### No value
13 | # 2000-01-01
14 | 
15 | ### Invalid value: spaces are not allowed between signal and number
16 | # 2000-01-01    - 10
17 | 
18 | ### Invalid value: unrecognized number format.
19 | # 2000-01-01    ++2
20 | # 2000-01-01    -+2
21 | # 2000-01-01    .50
22 | # 2000-01-01    1,2
23 | 
24 | ### Invalid value: cents are ok, but other punctuation is wrong
25 | # 2000-01-01    1.23,45
26 | # 2000-01-01    12.34.234,56
27 | 
28 | ### Invalid recurrent period: zero or negative
29 | # 2000-01-01    100/0
30 | # 2000-01-01    100/-2
31 | # 2000-01-01    100*-2
32 | 
33 | 
34 | ### useLegacyDataFormat: two or more Tabs in description
35 | # 2000-01-01	10	foo	bar
36 | # 2000-01-01	10	foo	bar	baz
37 | 


--------------------------------------------------------------------------------
/test/multi/README.md:
--------------------------------------------------------------------------------
 1 | # MoneyLog Multi Files Test
 2 | 
 3 | Test files for the multiple TXT files feature.
 4 | 
 5 | Put the following lines in `config.js`:
 6 | 
 7 | 	dataFiles = [
 8 | 		'test/multi/1.txt',
 9 | 		'test/multi/2.txt',
10 | 		'test/multi/3.txt',
11 | 		'test/multi/4.txt',
12 | 		'test/multi/5.txt',
13 | 		'*'
14 | 	];
15 | 
16 | And check the following behavior:
17 | 
18 | - The first file is the default and it's data must be loaded at MoneyLog startup.
19 | 
20 | - Selecting a different file in the file chooser will clear the current data and load the new file's data.
21 | 
22 | - Selecting the asterisk `*`, all TXT files are simultaneously loaded, as if they were just a single file.
23 | 
24 | - Edit `config.js` to place the asterisk as the first array item. All TXT files must be loaded at MoneyLog startup.
25 | 
26 | - Observe the behavior of the tags (selected or not), the other filtering options and the type of report.
27 | 
28 | - Use the Reload button to verify it's working.
29 | 
30 | 
31 | > Note: If you use an extra comma in the last array item, IE attempts to load the `undefined` item :/
32 | 


--------------------------------------------------------------------------------
/public/privacidade.txt:
--------------------------------------------------------------------------------
 1 | Política de Privacidade
 2 | -----------------------
 3 | 
 4 | Este site não identifica o usuário.
 5 | 
 6 | Este site não guarda nenhum dado do usuário.
 7 | 
 8 | Este site é apenas um programa simples, para uso anônimo, sem cadastro
 9 | nem login.
10 | 
11 | Este programa funciona exclusivamente no equipamento do usuário
12 | (JavaScript client side). Não há processamento no servidor. Não há envio
13 | de dados para o servidor.
14 | 
15 | É a API oficial do Google Drive quem autentica e obtém o arquivo do
16 | usuário. Esta comunicação acontece diretamente entre o equipamento do
17 | usuário e a API, sem intermédio do servidor deste programa.
18 | 
19 | Os dados do arquivo do Google Drive ficam apenas na memória do
20 | equipamento do usuário, e são apagados ao fechar a janela ou aba do
21 | navegador.
22 | 
23 | O responsável pelo site não tem acesso à identidade do usuário, aos
24 | dados carregados no programa nem à conta do usuário no Google Drive.
25 | 
26 | Este programa é um software livre e todo o seu código fonte está disponível
27 | para inspeção em https://github.com/aureliojargas/moneylog/
28 | 


--------------------------------------------------------------------------------
/css/browser.css:
--------------------------------------------------------------------------------
 1 | /*
 2 | 	MoneyLog Browser theme
 3 | 	by Aurelio Jargas
 4 | 	https://aurelio.net/moneylog/
 5 | 
 6 | 	Light orange: #FFEED1
 7 | 	 Dark orange: #E89E00
 8 | */
 9 | 
10 | 
11 | #app-flavor {
12 | 	color: #E89E00;
13 | }
14 | 
15 | /* Dark background for active/special buttons */
16 | #fullscreen,
17 | .report th:hover,
18 | #editor-file-name,
19 | .button.active,
20 | a.button:active {
21 | 	background-color: #E89E00;
22 | }
23 | 
24 | /* Dark background for selected rows */
25 | tr.selected,
26 | tr.selected:hover td,
27 | tr.selected .neg {
28 | 	background-color: #E89E00;
29 | }
30 | 
31 | /* Dark color for borders */
32 | #editor-data-wrapper,
33 | #rows-summary,
34 | .button.active,
35 | a.button:active {
36 | 	border-color: #E89E00;
37 | }
38 | 
39 | /* Light background for selections */
40 | .hl,
41 | #tag-cloud-tags a.selected {
42 | 	background-color: #FFEED1;
43 | }
44 | 
45 | /* Light background for totals */
46 | tr.totals,
47 | td.totals,
48 | #rows-summary {
49 | 	background-color: #FFEED1;
50 | }
51 | 
52 | /* Light color for the report borders */
53 | .report td {
54 | 	border-color: #FFEED1;
55 | }
56 | 
57 | 


--------------------------------------------------------------------------------
/sample/widget-hello-checkbox.js:
--------------------------------------------------------------------------------
 1 | // --------------------------------------------------------------------
 2 | // Hello Checkbox Widget
 3 | // by Aurelio Jargas 2012-02-17
 4 | //
 5 | // A simple sample widget, using a checkbox.
 6 | //
 7 | // Copy/paste all this code to the end of your config.js file.
 8 | 
 9 | 
10 | // Create a new widget instance (id, name, instance name)
11 | var HelloCheckbox = new Widget('hello-checkbox', 'Hello Checkbox', 'HelloCheckbox');
12 | 
13 | // Widget config
14 | HelloCheckbox.config.active = true;  // Is this widget active?
15 | HelloCheckbox.config.opened = true;  // Start app with this widget opened?
16 | 
17 | // Set widget contents
18 | HelloCheckbox.populate = function () {
19 | 	this.addCheckbox('foo', 'Hello?');  // id suffix, label
20 | };
21 | 
22 | // Handle checkbox click
23 | HelloCheckbox.checkboxClicked = function (checkbox) {
24 | 	if (checkbox.checked) {
25 | 		this.header.innerHTML = 'I am checked';
26 | 	} else {
27 | 		this.header.innerHTML = 'I am not checked';
28 | 	}
29 | 
30 | 	// Debug
31 | 	console.log('Checkbox ID: ' + checkbox.id);
32 | 	console.log(this);  // Widget instance
33 | };
34 | 


--------------------------------------------------------------------------------
/LICENSE.txt:
--------------------------------------------------------------------------------
 1 | The MIT License (MIT)
 2 | 
 3 | Copyright (c) 2006-2014 Aurelio Jargas
 4 | 
 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of
 6 | this software and associated documentation files (the "Software"), to deal in
 7 | the Software without restriction, including without limitation the rights to
 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
 9 | of the Software, and to permit persons to whom the Software is furnished to do
10 | so, subject to the following conditions:
11 | 
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 | 
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 | 


--------------------------------------------------------------------------------
/css/cloud.css:
--------------------------------------------------------------------------------
 1 | /*
 2 | 	MoneyLog Cloud theme
 3 | 	by Aurelio Jargas
 4 | 	https://aurelio.net/moneylog/
 5 | 
 6 | 	Light blue: #D5EAFB
 7 | 	 Dark blue: #2B97E9
 8 | */
 9 | 
10 | 
11 | #app-flavor {
12 | 	color: #2B97E9;
13 | }
14 | 
15 | /* Dark background for active/special buttons */
16 | #fullscreen,
17 | .report th:hover,
18 | #editor-file-name,
19 | .button.active,
20 | a.button:active {
21 | 	background-color: #2B97E9;
22 | }
23 | 
24 | /* Dark background for selected rows */
25 | tr.selected,
26 | tr.selected:hover td,
27 | tr.selected .neg {
28 | 	background-color: #2B97E9;
29 | }
30 | 
31 | /* Dark color for borders */
32 | #editor-data-wrapper,
33 | #rows-summary,
34 | .button.active,
35 | a.button:active {
36 | 	border-color: #2B97E9;
37 | }
38 | 
39 | /* Light background for selections */
40 | .hl,
41 | #tag-cloud-tags a.selected {
42 | 	background-color: #D5EAFB;
43 | }
44 | 
45 | /* Light background for totals */
46 | tr.totals,
47 | td.totals,
48 | #rows-summary {
49 | 	background-color: #D5EAFB;
50 | }
51 | 
52 | /* Light color for the report borders */
53 | .report td {
54 | 	border-color: #D5EAFB;
55 | }
56 | 
57 | 
58 | /* Special: interface dark blue for the positive chart bar */
59 | .posbar {
60 | 	background-color: #2B97E9;
61 | }
62 | 


--------------------------------------------------------------------------------
/css/portable.css:
--------------------------------------------------------------------------------
 1 | /*
 2 | 	MoneyLog Portable theme
 3 | 	by Aurelio Jargas
 4 | 	https://aurelio.net/moneylog/
 5 | 
 6 | 	Light green: #E5EFDE
 7 | 	 Dark green: #4D8A68
 8 | */
 9 | 
10 | 
11 | #app-flavor {
12 | 	color: #4D8A68;
13 | }
14 | 
15 | /* Dark background for active/special buttons */
16 | #fullscreen,
17 | .report th:hover,
18 | #editor-file-name,
19 | .button.active,
20 | a.button:active {
21 | 	background-color: #4D8A68;
22 | }
23 | 
24 | /* Dark background for selected rows */
25 | tr.selected,
26 | tr.selected:hover td,
27 | tr.selected .neg {
28 | 	background-color: #4D8A68;
29 | }
30 | 
31 | /* Dark color for borders */
32 | #editor-data-wrapper,
33 | #rows-summary,
34 | .button.active,
35 | a.button:active {
36 | 	border-color: #4D8A68;
37 | }
38 | 
39 | /* Light background for selections */
40 | .hl,
41 | #tag-cloud-tags a.selected {
42 | 	background-color: #E5EFDE;
43 | }
44 | 
45 | /* Light background for totals */
46 | tr.totals,
47 | td.totals,
48 | #rows-summary {
49 | 	background-color: #E5EFDE;
50 | }
51 | 
52 | /* Light color for the report borders */
53 | .report td {
54 | 	border-color: #E5EFDE;
55 | }
56 | 
57 | 
58 | /* Special: interface dark green for the positive chart bar */
59 | .posbar {
60 | 	background-color: #4D8A68;
61 | }
62 | 


--------------------------------------------------------------------------------
/css/print.css:
--------------------------------------------------------------------------------
 1 | /*
 2 | 	MoneyLog print theme
 3 | 	by Aurelio Jargas
 4 | 	https://aurelio.net/moneylog/
 5 | */
 6 | 
 7 | /* No toolbar */
 8 | #toolbar {
 9 | 	display: none;
10 | }
11 | 
12 | /* No margin/padding */
13 | #content {
14 | 	margin: 0;
15 | 	padding: 0;
16 | }
17 | 
18 | /* Remove row backgrounds */
19 | tr.future,
20 | tr.totals,
21 | td.totals {
22 | 	background-color: white !important;
23 | }
24 | 
25 | /* Remove minibars from monthly/yearly reports */
26 | th.percent,
27 | td.minibar {
28 | 	display: none;
29 | }
30 | 
31 | /* Remove chart bars background and use outlines instead */
32 | .posbar {
33 | 	background-color: white;
34 | 	border: 1px solid silver;  /* solid */
35 | }
36 | .negbar {
37 | 	background-color: white;
38 | 	border: 1px dashed silver;  /* dashed */
39 | }
40 | 
41 | /* Make the SELECT button appear as plain text */
42 | #chart-selector {
43 | 	-webkit-appearance: none;
44 | 	background: white;
45 | 	border-width: 0;
46 | }
47 | 
48 | /* Remove (or lighten, or force B&W) colors */
49 | .report td {
50 | 	border-color: #DDDDDD;
51 | }
52 | tr.selected,
53 | tr.selected .neg {
54 | 	background-color: #EEEEEE;
55 | 	color: black ;
56 | }
57 | #rows-summary {
58 | 	border: 1px solid silver;
59 | 	background-color: white;
60 | }
61 | 


--------------------------------------------------------------------------------
/storage/drivers/browser.js:
--------------------------------------------------------------------------------
 1 | // browser: localStorage
 2 | 
 3 | ml.storage.drivers.browser = {
 4 | 	id: 'browser',
 5 | 	name: 'Browser (localStorage)',
 6 | 	config: {
 7 | 		isAsync: false,
 8 | 		isEditable: true,
 9 | 		isFileBased: false,
10 | 		isReloadable: false,
11 | 		loadDataAtSetup: true
12 | 	},
13 | 
14 | 	// localStorage database key
15 | 	database: 'moneylogData',
16 | 
17 | 	read: function () {
18 | 		return localStorage.getItem(this.database) || '';
19 | 	},
20 | 
21 | 	write: function (contents) {
22 | 		localStorage.setItem(this.database, contents);
23 | 	},
24 | 
25 | 	setDefaultData: function () {
26 | 		ml.storage.drivers.filesystem.readAsync(
27 | 			{name: 'sample/data-pt.txt'},
28 | 			function (contents) {
29 | 				ml.storage.drivers.browser.write(contents);
30 | 				loadData();
31 | 			}
32 | 		);
33 | 	},
34 | 
35 | 	init: function () {
36 | 
37 | 		// Browser support check
38 | 		if (!window.localStorage) {
39 | 			showError(
40 | 				i18n.errorNoLocalStorage.replace('%s', appName),
41 | 				'

' + i18n.errorRequirements + 42 | array2ul(['Internet Explorer 8', 'Firefox 3', 'Google Chrome 3', 'Safari 4', 'Opera 10.50']) 43 | ); 44 | } 45 | 46 | // Empty database? Initialize with sample content 47 | if (this.read().strip() === '') { 48 | this.setDefaultData(); 49 | } 50 | } 51 | }; 52 | -------------------------------------------------------------------------------- /sample/widget-nerd-toy.js: -------------------------------------------------------------------------------- 1 | // -------------------------------------------------------------------- 2 | // Nerd Toy Widget 3 | // by Aurelio Jargas 2012-02-16 4 | // 5 | // A sample widget that shows some MoneyLog options 6 | // for you to turn ON or OFF with handy checkboxes. 7 | // 8 | // Copy/paste all this code to the end of your config.js file. 9 | 10 | 11 | // Create a new widget instance 12 | var NerdToy = new Widget('nerd-toy', 'Nerd Toy', 'NerdToy'); 13 | 14 | // Widget config 15 | NerdToy.config.active = true; // Is this widget active? 16 | NerdToy.config.opened = true; // Start app with this widget opened? 17 | 18 | // This function fills the widget contents, creating all the checkboxes. 19 | // populate() is called automatically in the default this.init(). 20 | NerdToy.populate = function () { 21 | var i, opts, opt; 22 | opts = [ 23 | 'showBalance', 24 | 'showChartBarLabel', 25 | 'showCharts', 26 | 'showLocaleDate', 27 | 'showMiniBars', 28 | 'showMiniBarsLabels', 29 | 'showRowCount', 30 | 'showTagReport', 31 | 'monthlyRowCount' 32 | ]; 33 | for (i = 0; i < opts.length; i++) { 34 | opt = opts[i]; 35 | this.addCheckbox(opt, opt, (window[opt] == true)); 36 | } 37 | }; 38 | 39 | // This is called automatically whenever a checkbox is clicked. 40 | NerdToy.checkboxClicked = function (checkbox) { 41 | var optionName = checkbox.id.replace('nerd-toy-', ''); 42 | window[optionName] = checkbox.checked; // set config 43 | showReport(); // reload the report 44 | }; 45 | -------------------------------------------------------------------------------- /util/conversor/myMoneyLog.txt: -------------------------------------------------------------------------------- 1 | # http://nishimura.eti.br/mymoneylog/moneylog2my.html 2 | 3 | 2009-05-01 -100 Saldo inicial da conta :( CONTA 4 | 2009-05-15 -74.23 mercado CONTA 5 | 2009-05-15 -1.99 Prestação do mouse 10/12 nerd CONTA 6 | 2009-05-23 -39.90 Livro "Horóscopo dos Duendes" presente; livro CONTA 7 | 2009-06-03 -342.59 Um Shrubbery presente CONTA 8 | 2009-06-11 30 Ganhei a aposta com o Zé CONTA 9 | 2009-06-12 -35 Poster colorido 3D do Tux em Ascii Art presente; nerd CONTA 10 | 2009-06-15 -1.99 Prestação do mouse 11/12 nerd CONTA 11 | 2009-06-29 -95.67 mercado CONTA 12 | 2009-07-12 -199.90 Segway usado no Mercado Livre presente CONTA 13 | 2009-07-15 -1.99 Prestação do mouse 12/12 (acabou!) nerd CONTA 14 | 2009-08-20 -10 Carnê do Baú CONTA 15 | 2009-05-05 500 salario CONTA 16 | 2009-06-05 500 salario CONTA 17 | 2009-07-05 500 salario CONTA 18 | 2009-08-05 500 salario CONTA 19 | 2009-05-07 -25 luz CONTA 20 | 2009-06-07 -20 luz CONTA 21 | 2009-07-07 -29 luz CONTA 22 | 2009-08-07 -25 luz CONTA 23 | 2009-05-07 -20 agua CONTA 24 | 2009-06-07 -17.80 agua CONTA 25 | 2009-07-07 -32.37 agua CONTA 26 | 2009-08-07 -30 agua CONTA 27 | 2009-12-31 99.99 Bônus salario CONTA 28 | 2009-08-05 -500/5 Netbook usado comprado parcelado nerd CONTA 29 | 2009-11-25 100/2 Presente de Natal, metade Nov, metade Dez presente CONTA 30 | 2009-05-05 -200*6 Aluguel da casa (seis meses) aluguel CONTA 31 | 2009-09-10 100*4 O Zé vai me dar 100 pilas por mês até o fim do ano presente CONTA 32 | -------------------------------------------------------------------------------- /sample/widget-hello-translation.js: -------------------------------------------------------------------------------- 1 | // -------------------------------------------------------------------- 2 | // Hello Translation Widget 3 | // by Aurelio Jargas 2012-02-17 4 | // 5 | // A simple sample widget, using translated strings. 6 | // 7 | // Copy/paste all this code to the end of your config.js file. 8 | 9 | // Create a new widget instance (id, name, instance name) 10 | var HelloTranslation = new Widget('hello-translation', 'Hello Translation', 'HelloTranslation'); 11 | 12 | // Save widget translation to the main database 13 | // header 14 | i18nDatabase.en.HelloTranslationHeaderLabel = 'Hello Translation'; 15 | i18nDatabase.es.HelloTranslationHeaderLabel = 'Hola Traducción'; 16 | i18nDatabase.pt.HelloTranslationHeaderLabel = 'Oi Tradução'; 17 | // checkbox label 18 | i18nDatabase.en.HelloTranslationOptionLabel = 'Say hello!'; 19 | i18nDatabase.es.HelloTranslationOptionLabel = '¡Decir hola!'; 20 | i18nDatabase.pt.HelloTranslationOptionLabel = 'Diga oi!'; 21 | // Note: always use {InstanceName}HeaderLabel for automatic header translation 22 | 23 | // Debug: change the app language to test the translations 24 | // lang = 'es'; // en:English, es:Spanish, pt:Portuguese 25 | 26 | 27 | // Set widget contents 28 | HelloTranslation.populate = function () { 29 | // Here we use the translated label. 30 | // Note that the prefix is just i18n. 31 | this.addCheckbox('foo', i18n.HelloTranslationOptionLabel); 32 | }; 33 | 34 | // Handle checkbox click 35 | HelloTranslation.checkboxClicked = function (checkbox) { 36 | console.log('clicked ' + checkbox.id); 37 | }; 38 | -------------------------------------------------------------------------------- /util/conversor/moneylog4-ok.txt: -------------------------------------------------------------------------------- 1 | # comentário 2 | 3 | 2009-05-01 -100 normal, sem centavos 4 | 2009-05-01 -100,00 normal, com centavos 5 | 2009-05-01 +100.00 normal, com centavos ponto 6 | 7 | 2009-05-01 -100 4 espaços antes do sinal, sem centavos 8 | 2009-05-01 +100,00 4 espaços antes do sinal, com centavos 9 | 2009-05-01 -100.00 4 espaços antes do sinal, com centavos ponto 10 | 11 | 2009-05-01 +100 4 espaços após o sinal, sem centavos 12 | 2009-05-01 -100,00 4 espaços após o sinal, com centavos 13 | 2009-05-01 -100.00 4 espaços após o sinal, com centavos ponto 14 | 15 | 2009-05-01 -100/2 normal, sem centavos, recorrente sem espaços 16 | 2009-05-01 -100,00/2 normal, com centavos, recorrente sem espaços 17 | 2009-05-01 +100.00/2 normal, com centavos ponto, recorrente sem espaços 18 | 19 | 2009-05-01 -100*2 normal, sem centavos, recorrente sem espaços 20 | 2009-05-01 +100,00*2 normal, com centavos, recorrente sem espaços 21 | 2009-05-01 -100.00*2 normal, com centavos ponto, recorrente sem espaços 22 | 23 | 2009-05-01 +100*2 4 espaços após o sinal, sem centavos, recorrente com espaços 24 | 2009-05-01 -100,00*2 4 espaços após o sinal, com centavos, recorrente com espaços 25 | 2009-05-01 -100.00*2 4 espaços após o sinal, com centavos ponto, recorrente com espaços 26 | 27 | 2009-05-01 -100/2 4 espaços após o sinal, sem centavos, recorrente com espaços 28 | 2009-05-01 -100,00/2 4 espaços após o sinal, com centavos, recorrente com espaços 29 | 2009-05-01 +100.00/2 4 espaços após o sinal, com centavos ponto, recorrente com espaços 30 | -------------------------------------------------------------------------------- /util/conversor/moneylog4.txt: -------------------------------------------------------------------------------- 1 | # comentário 2 | 3 | 2009-05-01 -100 normal, sem centavos 4 | 2009-05-01 -100,00 normal, com centavos 5 | 2009-05-01 +100.00 normal, com centavos ponto 6 | 7 | 2009-05-01 -100 4 espaços antes do sinal, sem centavos 8 | 2009-05-01 +100,00 4 espaços antes do sinal, com centavos 9 | 2009-05-01 -100.00 4 espaços antes do sinal, com centavos ponto 10 | 11 | 2009-05-01 + 100 4 espaços após o sinal, sem centavos 12 | 2009-05-01 - 100,00 4 espaços após o sinal, com centavos 13 | 2009-05-01 - 100.00 4 espaços após o sinal, com centavos ponto 14 | 15 | 2009-05-01 -100/2 normal, sem centavos, recorrente sem espaços 16 | 2009-05-01 -100,00/2 normal, com centavos, recorrente sem espaços 17 | 2009-05-01 +100.00/2 normal, com centavos ponto, recorrente sem espaços 18 | 19 | 2009-05-01 -100*2 normal, sem centavos, recorrente sem espaços 20 | 2009-05-01 +100,00*2 normal, com centavos, recorrente sem espaços 21 | 2009-05-01 -100.00*2 normal, com centavos ponto, recorrente sem espaços 22 | 23 | 2009-05-01 + 100 *2 4 espaços após o sinal, sem centavos, recorrente com espaços 24 | 2009-05-01 - 100,00 *2 4 espaços após o sinal, com centavos, recorrente com espaços 25 | 2009-05-01 - 100.00 *2 4 espaços após o sinal, com centavos ponto, recorrente com espaços 26 | 27 | 2009-05-01 - 100 /2 4 espaços após o sinal, sem centavos, recorrente com espaços 28 | 2009-05-01 - 100,00 /2 4 espaços após o sinal, com centavos, recorrente com espaços 29 | 2009-05-01 + 100.00 /2 4 espaços após o sinal, com centavos ponto, recorrente com espaços 30 | -------------------------------------------------------------------------------- /sample/data-es.txt: -------------------------------------------------------------------------------- 1 | # Cambie estos datos por los suyos, separando las columnas usando espacios en blanco 2 | # La fecha debe ser ingresada en el formato AÑO-MES-DÍA 3 | 4 | 2016-11-01 -100,00 Saldo inicial :( 5 | 6 | 2016-12-15 -74,23 comida| 7 | 2016-12-23 -39,90 regalos, libros| Libro "Horóscopo de los Duendes" 8 | 9 | 2017-01-03 -342,59 regalos| Un Shrubbery 10 | 2017-01-11 +30,00 Le gané una apuesta a Marcos 11 | 2017-01-12 -35,00 regalos, nerd| Poster 3D en colores de Tux en Ascii Art 12 | 2017-01-29 -95,67 comida| 13 | 14 | 2017-02-12 -199,90 regalos| Segway usado en mercadolibre.com 15 | 16 | 2017-03-20 -10,00 Carnet del Club 17 | 18 | # Es posible dejar juntos todos los movimientos periodicos. El orden no importa en este caso 19 | 20 | 2016-12-05 500 salario| 21 | 2017-01-05 500 salario| 22 | 2017-02-05 500 salario| 23 | 2017-03-05 500 salario| 24 | 2017-04-05 500 salario| 25 | 26 | 2016-12-07 -25,00 luz| 27 | 2017-01-07 -20,00 luz| 28 | 2017-02-07 -29,00 luz| 29 | 2017-03-07 -25,00 luz| 30 | 31 | 2016-12-07 -20,00 agua| 32 | 2017-01-07 -17,80 agua| 33 | 2017-02-07 -32,37 agua| 34 | 2017-03-07 -30,00 agua| 35 | 36 | # Ingresos y Egresos en cuotas 37 | 38 | 2016-12-05 -500/5 nerd| Netbook usado comprado en cuotas 39 | 2016-11-25 100/2 regalos| Regalo de Navidad, mitad Nov., mitad Dic. 40 | 41 | # Ingresos y Egresos recurrentes 42 | 43 | 2017-01-05 -200*6 alquiler| Alquiler de la casa (seis meses) 44 | 2016-11-10 100*2 apuestas| Marcos me va a dar 100 pesos por mes hasta fin de año 45 | -------------------------------------------------------------------------------- /sample/data-en.txt: -------------------------------------------------------------------------------- 1 | # Change this data by yours, separated by spaces 2 | # The date is on the YEAR-MONTH-DAY format 3 | # The amount may be positive or negative, with or without cents 4 | # The tags are optional, separated from the description by a | 5 | # The description is also optional 6 | 7 | 2016-11-01 -100.00 My initial balance :( 8 | 9 | 2016-12-15 -74.23 supermarket| 10 | 2016-12-23 -39.90 gift, book| "Gremlins Horoscope" book 11 | 12 | 2017-01-03 -342.59 gift| One Shrubbery 13 | 2017-01-11 +30.00 I've won that bet with John 14 | 2017-01-12 -35.00 gift, geek| Ascii Art Tux 3D poster 15 | 2017-01-29 -95.67 supermarket| 16 | 17 | 2017-02-12 -199.90 gift| Used Segway on E-bay 18 | 19 | 2017-03-20 -10.00 Dinner at downtown 20 | 21 | # The order is not important. You can join together recurring transactions. 22 | 23 | 2016-12-05 500 salary| 24 | 2017-01-05 500 salary| 25 | 2017-02-05 500 salary| 26 | 2017-03-05 500 salary| 27 | 2017-04-05 500 salary| 28 | 29 | 2016-12-07 -25.00 energy| 30 | 2017-01-07 -20.00 energy| 31 | 2017-02-07 -29.00 energy| 32 | 2017-03-07 -25.00 energy| 33 | 34 | 2016-12-07 -20.00 water| 35 | 2017-01-07 -17.80 water| 36 | 2017-02-07 -32.37 water| 37 | 2017-03-07 -30.00 water| 38 | 39 | # Installment payments: use a / and the number of months 40 | 41 | 2016-12-05 -500/5 geek| Used Netbook bought in 5 payments 42 | 2016-11-25 100/2 gift| Christmas gift: half in November, half in December 43 | 44 | # Recurring payments: use a * and the number of months 45 | 46 | 2017-01-05 -200*6 rental| Home rental (seis months) 47 | 2016-11-10 100*2 gift| Uncle Arnold will give me 100 bucks/month until December 48 | -------------------------------------------------------------------------------- /sample/data-pt.txt: -------------------------------------------------------------------------------- 1 | # Troque estes dados pelos seus, separados por espaços 2 | # A data é no formato ANO-MES-DIA 3 | # O valor pode ser positivo ou negativo, com ou sem centavos 4 | # As tags são opcionais, separadas da descrição por uma | 5 | # A descrição também é opcional 6 | # Guia completo em https://aurelio.net/moneylog/input/ 7 | 8 | 2016-11-01 -100,00 Saldo inicial da conta :( 9 | 10 | 2016-12-15 -74,23 mercado| 11 | 2016-12-23 -39,90 presente, livro| Livro "Horóscopo dos Duendes" 12 | 13 | 2017-01-03 -342,59 presente| Um Shrubbery 14 | 2017-01-11 +30,00 Ganhei a aposta com o Zé 15 | 2017-01-12 -35,00 presente, nerd| Poster colorido 3D do Tux em Ascii Art 16 | 2017-01-29 -95,67 mercado| 17 | 18 | 2017-02-12 -199,90 presente| Segway usado no Mercado Livre 19 | 20 | 2017-03-20 -10,00 Carnê do Baú 21 | 22 | # A ordem não importa, é possível deixar juntas as transações periódicas 23 | 24 | 2016-12-05 500 salário| 25 | 2017-01-05 500 salário| 26 | 2017-02-05 500 salário| 27 | 2017-03-05 500 salário| 28 | 2017-04-05 500 salário| 29 | 30 | 2016-12-07 -25,00 luz| 31 | 2017-01-07 -20,00 luz| 32 | 2017-02-07 -29,00 luz| 33 | 2017-03-07 -25,00 luz| 34 | 35 | 2016-12-07 -20,00 água| 36 | 2017-01-07 -17,80 água| 37 | 2017-02-07 -32,37 água| 38 | 2017-03-07 -30,00 água| 39 | 40 | # Pagamentos e ganhos parcelados: use uma / e o número de meses 41 | 42 | 2016-12-05 -500/5 nerd| Netbook usado comprado parcelado 43 | 2016-11-25 100/2 presente| Presente de Natal, metade Nov, metade Dez 44 | 45 | # Pagamentos e ganhos recorrentes: use um * e o número de meses 46 | 47 | 2017-01-05 -200*6 aluguel| Aluguel da casa (seis meses) 48 | 2016-11-10 100*2 presente| O Zé vai me dar 100 pilas por mês até o fim do ano 49 | -------------------------------------------------------------------------------- /storage/drivers/filesystem.js: -------------------------------------------------------------------------------- 1 | // File system: local text files 2 | 3 | ml.storage.drivers.filesystem = { 4 | id: 'filesystem', 5 | name: 'Local text files', 6 | config: { 7 | isAsync: true, 8 | isEditable: false, 9 | isFileBased: true, 10 | isReloadable: true, 11 | showFolderLink: false, 12 | loadDataAtSetup: true, 13 | maxFilesForStar: 999 14 | }, 15 | 16 | dataFiles: [], // flat array, meant for easier user config 17 | userFiles: [], // [{id:'', name:''}, ...] 18 | defaultFile: '', 19 | 20 | setUserFilesFromFlatArray: function (arr) { 21 | var i; 22 | this.userFiles = []; 23 | for (i = 0; i < arr.length; i++) { 24 | this.userFiles.push({ 25 | id: i, 26 | name: arr[i] 27 | }); 28 | } 29 | }, 30 | 31 | // Use a temporary iframe to read a local text file contents. 32 | // Note: Firefox won't read text files in a parent folder. 33 | readAsync: function (fileData, callback) { 34 | var iframe = document.createElement('iframe'); 35 | iframe.style.display = 'none'; 36 | iframe.src = fileData.name; 37 | iframe.onload = function () { 38 | callback(iframe.contentWindow.document.getElementsByTagName('pre')[0].innerText); 39 | iframe.parentNode.removeChild(iframe); // del iframe 40 | }; 41 | document.body.appendChild(iframe); // add iframe 42 | }, 43 | 44 | init: function () { 45 | var filesCombo; 46 | 47 | // Honor legacy global config: dataFiles array 48 | if (this.dataFiles.length === 0 && dataFiles && dataFiles.length > 0) { 49 | this.dataFiles = dataFiles; 50 | } 51 | 52 | // Honor legacy global config: dataFilesDefault 53 | if (!this.defaultFile && dataFilesDefault) { 54 | this.defaultFile = dataFilesDefault; 55 | } 56 | 57 | // Set user files from config array 58 | if (this.dataFiles.length > 0) { 59 | this.setUserFilesFromFlatArray(this.dataFiles); 60 | } 61 | 62 | ml.storage.populateFilesCombo(); 63 | 64 | // Set the default file to load when using multiple files 65 | if (this.defaultFile) { 66 | filesCombo = document.getElementById('source-file'); 67 | selectOptionByText(filesCombo, this.defaultFile); 68 | } 69 | } 70 | }; 71 | -------------------------------------------------------------------------------- /util/gen-browser: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Aurelio Jargas, https://aurelio.net/moneylog/ 3 | # 4 | # Script to join all the MoneyLog pieces in one singe HTML file, generating 5 | # the MoneyLog Browser version, who uses localStorage instead of text files. 6 | # 7 | # Usage: 8 | # gen-browser [--lang XX] 9 | # 10 | # Examples: 11 | # gen-browser # Portuguese browser version 12 | # gen-browser --lang en # English browser version 13 | # 14 | # Note: 15 | # The results are Windows-style line break: CR+LF 16 | 17 | 18 | cd $(dirname "$0") 19 | 20 | lang="pt" # pt, en, es, ca. Use --lang to change it. 21 | 22 | # Option --lang 23 | if test "$1" = '--lang' 24 | then 25 | lang=$2 26 | shift 27 | shift 28 | fi 29 | 30 | # Files 31 | html_path="../moneylog.html" 32 | txt_path="../sample/data-$lang.txt" 33 | 34 | # Set $commit_id to the latest Git commit hash 35 | source set-commit-id.sh 36 | 37 | # Patterns: 38 | # 39 | # 40 | 41 | insert_css=' 42 | /^ 45 | r ../moneylog.css 46 | a \ 47 | \ 48 | 52 | d 53 | } 54 | 55 | /^ 58 | r ../css/mobile.css 59 | a \ 60 | 61 | d 62 | } 63 | 64 | /^ 67 | r ../css/print.css 68 | a \ 69 | 70 | d 71 | } 72 | ' 73 | insert_js=' 74 | /^ 80 | d 81 | } 82 | ' 83 | insert_txt=" 84 | /^

 sed_script
 88 | 
 89 | # Do it
 90 | control_m=$(printf '\r')
 91 | 
 92 | sed -f sed_script "$html_path" |
 93 | 	# Set language
 94 | 	sed "/^var lang = 'pt';/ s/pt/$lang/" |
 95 | 	# Set commit id
 96 | 	sed "/^var appCommit = '';/ s/'/'$commit_id/" |
 97 | 	# clean up
 98 | 	sed '
 99 | 		# Remove config.js call
100 | 		/^
 39 | 
 40 | insert_css='
 41 | /^
 44 | 	r ../moneylog.css
 45 | 	a \
 46 | \
 47 | 
 51 | 	d
 52 | }
 53 | 
 54 | /^
 57 | 	r ../css/mobile.css
 58 | 	a \
 59 | 
 60 | 	d
 61 | }
 62 | 
 63 | /^
 66 | 	r ../css/print.css
 67 | 	a \
 68 | 
 69 | 	d
 70 | }
 71 | '
 72 | insert_js='
 73 | /^\
 79 | \
 80 | \
 81 | \
 82 | \
 83 | {{ user_config }}
 84 | 	d
 85 | }
 86 | '
 87 | insert_meta='
 88 | /^
 90 | '
 91 | insert_analytics='
 92 | /^<\/head>/ {
 93 | 	r gen-cloud-analytics.html
 94 | 	a \
 95 | 
 96 | 	d
 97 | }
 98 | '
 99 | 
100 | echo "$insert_css $insert_js $insert_meta $insert_analytics" > sed_script
101 | 
102 | # Do it
103 | control_m=$(printf '\r')
104 | 
105 | sed -f sed_script "$html_path" |
106 | 	# Set language
107 | 	sed "/^var lang = 'pt';/ s/pt/$lang/" |
108 | 	# Set commit id
109 | 	sed "/^var appCommit = '';/ s/'/'$commit_id/" |
110 | 	# clean up
111 | 	sed '
112 | 		# Remove config.js call
113 | 		/^
 69 | 
 70 | insert_css='
 71 | /^
 74 | 	r ../moneylog.css
 75 | 	a \
 76 | 
 77 | 	d
 78 | }
 79 | 
 80 | /^
 83 | 	r ../css/portable.css
 84 | 	a \
 85 | 
 86 | 	d
 87 | }
 88 | 
 89 | /^
 92 | 	r ../css/mobile.css
 93 | 	a \
 94 | 
 95 | 	d
 96 | }
 97 | 
 98 | /^
101 | 	r ../css/print.css
102 | 	a \
103 | 
104 | 	d
105 | }
106 | '
107 | insert_js='
108 | /^
114 | 	d
115 | }
116 | /^
122 | 	d
123 | }
124 | /^
130 | 	d
131 | }
132 | /^
  8 | // 2. In your browser, open the JavaScript console to see the messages.
  9 | // 3. Run MoneyLog, every tests should report "ok".
 10 | 
 11 | /* eslint no-plusplus: "off" */
 12 | 
 13 | var zz, suffix;
 14 | 
 15 | suffix = ' (expected, results):';
 16 | 
 17 | 
 18 | // Disable MoneyLog init() process
 19 | window.onload = function () {
 20 | 	document.getElementById('report').innerHTML =
 21 | 		'

MoneyLog Test Mode

' + 22 | 'Open the JavaScript console to see the test messages.'; 23 | }; 24 | 25 | 26 | function check(results, ok) { 27 | // Tests strings and numbers 28 | 29 | var prefix = 'Test ' + zz + ': '; 30 | 31 | if (results === ok) { 32 | console.log(prefix + 'ok'); 33 | } else { 34 | console.log(prefix + 'FAILED' + suffix); 35 | console.log(ok); 36 | console.log(results); 37 | } 38 | } 39 | 40 | function checkArray(results, ok, quiet) { 41 | // Special function to test arrays because [] == [] returns false. 42 | 43 | // console.log('checkArray called with (results, ok, quiet); 44 | // console.log(results); 45 | // console.log(ok); 46 | // console.log(quiet); 47 | 48 | var i, failed; 49 | var prefix = 'Test ' + zz + ': '; 50 | 51 | for (i = 0; i < ok.length; i++) { 52 | 53 | // Array inside array? 54 | if (typeof results[i] === 'object') { 55 | if (checkArray(results[i], ok[i], 'quiet')) { 56 | continue; 57 | } else { 58 | failed = i; 59 | break; 60 | } 61 | } 62 | 63 | // string|number contents, just test 64 | if (results[i] !== ok[i]) { 65 | failed = i; 66 | break; 67 | } 68 | } 69 | if (failed !== undefined) { 70 | if (!quiet) { 71 | console.log(prefix + 'FAILED at array item ' + failed + suffix); 72 | console.log(ok[failed]); 73 | console.log(results[failed]); 74 | } 75 | return false; 76 | } else { 77 | if (!quiet) { 78 | console.log(prefix + 'ok'); 79 | } 80 | return true; 81 | } 82 | } 83 | 84 | function checkObject(results, ok) { 85 | // Loop and check all object properties 86 | 87 | var x, failed; 88 | var prefix = 'Test ' + zz + ': '; 89 | 90 | for (x in ok) { 91 | 92 | // Array inside object? 93 | // Note: nested objects are not supported 94 | if (typeof results[x] === 'object') { 95 | if (checkArray(results[x], ok[x], 'quiet')) { 96 | continue; 97 | } else { 98 | failed = x; 99 | break; 100 | } 101 | } 102 | 103 | // string|number contents, just test 104 | if (results[x] !== ok[x]) { 105 | failed = x; 106 | break; 107 | } 108 | } 109 | if (failed !== undefined) { 110 | console.log(prefix + 'FAILED at property "' + failed + '"' + suffix); 111 | console.log(ok[failed]); 112 | console.log(results[failed]); 113 | 114 | console.log('*** Full objects (expected, results):'); 115 | console.log(ok); 116 | console.log(results); 117 | return false; 118 | } else { 119 | console.log(prefix + 'ok'); 120 | return true; 121 | } 122 | } 123 | 124 | console.log('-------------- MoneyLog Tests BEGIN'); 125 | 126 | zz = 0; 127 | zz++; checkArray([], []); 128 | zz++; checkArray([1, 2, 3], [1, 2, 3]); 129 | zz++; checkArray([[1, 2, 3]], [[1, 2, 3]]); 130 | zz++; checkArray([1, [2, [3, [4]]]], [1, [2, [3, [4]]]]); 131 | zz++; checkObject({}, {}); 132 | zz++; checkObject({n: 1}, {n: 1}); 133 | zz++; checkObject({n: 1, s: 'x', a: [1]}, {n: 1, s: 'x', a: [1]}); 134 | zz++; checkObject({n: 1, s: 'x', a: [1, [2, [3]]]}, {n: 1, s: 'x', a: [1, [2, [3]]]}); 135 | zz++; checkObject({a1: [1, [2, [3]]], a2: [3, [2, [1]]]}, {a2: [3, [2, [1]]], a1: [1, [2, [3]]]}); 136 | // -------------------------------------------------------------- ^ Test 9 137 | 138 | // function Array.getColumn(n) 139 | zz++; checkArray([].getColumn(1), []); 140 | zz++; checkArray([[], [], []].getColumn(1), [undefined, undefined, undefined]); 141 | zz++; checkArray([[0, 1, 2], [3, 4, 5], [6, 7, 8]].getColumn(0), [0, 3, 6]); 142 | zz++; checkArray([[0, 1, 2], [3, 4, 5], [6, 7, 8]].getColumn(1), [1, 4, 7]); 143 | zz++; checkArray([[0, 1, 2], [3, 4, 5], [6, 7, 8]].getColumn(2), [2, 5, 8]); 144 | zz++; checkArray([[0, 1, 2], [3, 4], [6, 7, 8]].getColumn(2), [2, undefined, 8]); 145 | zz++; checkArray([[0, 1, 2], [3, 4, 5], [6, 7, 8]].getColumn(3), [undefined, undefined, undefined]); 146 | // -------------------------------------------------------------- ^ Test 16 147 | 148 | // function computeTotals(arr) 149 | zz++; check(computeTotals([]), undefined); 150 | zz++; checkObject( 151 | computeTotals([0]), 152 | {min: 0, max: 0, sum: 0, average: 0, sumPositive: 0, sumNegative: 0, balance: [0]} 153 | ); 154 | zz++; checkObject( 155 | computeTotals([1]), 156 | {min: 1, max: 1, sum: 1, average: 1, sumPositive: 1, sumNegative: 0, balance: [1]} 157 | ); 158 | zz++; checkObject( 159 | computeTotals([-1]), 160 | {min: -1, max: -1, sum: -1, average: -1, sumPositive: 0, sumNegative: -1, balance: [-1]} 161 | ); 162 | zz++; checkObject( 163 | computeTotals([0, 0, 0, 0]), 164 | {min: 0, max: 0, sum: 0, average: 0, sumPositive: 0, sumNegative: 0, balance: [0, 0, 0, 0]} 165 | ); 166 | zz++; checkObject( 167 | computeTotals([-2, -1, 0, 1, 2]), 168 | {min: -2, max: 2, sum: 0, average: 0, sumPositive: 3, sumNegative: -3, balance: [-2, -3, -3, -2, 0]} 169 | ); 170 | zz++; checkObject( 171 | computeTotals([10.00, 25.50, -5.50, 33.33]), 172 | {min: -5.50, max: 33.33, sum: 63.33, average: 15.8325, sumPositive: 68.83, sumNegative: -5.50, balance: [10.00, 35.50, 30.00, 63.33]} 173 | ); 174 | // -------------------------------------------------------------- ^ Test 23 175 | 176 | // function groupByPeriod(arr, periodType) { // m, y 177 | zz++; checkObject( 178 | groupByPeriod([ 179 | ['2012-02-15', '1', 'foo1'], 180 | ['2012-02-18', '2', 'foo2'], 181 | ['2012-02-28', '3', 'foo3'], 182 | ['2012-03-04', '-4', 'bar1'], 183 | ['2012-03-18', '-5', 'bar2'], 184 | ['2012-04-01', '6', 'baz'] 185 | ], 'm'), 186 | { 187 | '2012-02': [ 188 | ['2012-02-15', '1', 'foo1'], 189 | ['2012-02-18', '2', 'foo2'], 190 | ['2012-02-28', '3', 'foo3'] 191 | ], 192 | '2012-03': [ 193 | ['2012-03-04', '-4', 'bar1'], 194 | ['2012-03-18', '-5', 'bar2'] 195 | ], 196 | '2012-04': [['2012-04-01', '6', 'baz']], 197 | 'keys': ['2012-02', '2012-03', '2012-04'] 198 | } 199 | ); 200 | zz++; checkObject( 201 | groupByPeriod([ // input sorted 202 | ['2012-02-15', '1', 'foo1'], 203 | ['2012-02-18', '2', 'foo2'], 204 | ['2012-02-28', '3', 'foo3'], 205 | ['2013-03-04', '-4', 'bar1'], 206 | ['2013-03-18', '-5', 'bar2'], 207 | ['2014-04-01', '6', 'baz'] 208 | ], 'y'), 209 | { 210 | '2012': [ 211 | ['2012-02-15', '1', 'foo1'], 212 | ['2012-02-18', '2', 'foo2'], 213 | ['2012-02-28', '3', 'foo3'] 214 | ], 215 | '2013': [ 216 | ['2013-03-04', '-4', 'bar1'], 217 | ['2013-03-18', '-5', 'bar2'] 218 | ], 219 | '2014': [['2014-04-01', '6', 'baz']], 220 | 'keys': ['2012', '2013', '2014'] 221 | } 222 | ); 223 | zz++; checkObject( 224 | groupByPeriod([ // input unsorted 225 | ['2012-02-15', '1', 'foo1'], 226 | ['2014-04-01', '6', 'baz'], 227 | ['2013-03-04', '-4', 'bar1'], 228 | ['2012-02-18', '2', 'foo2'], 229 | ['2013-03-18', '-5', 'bar2'], 230 | ['2012-02-28', '3', 'foo3'] 231 | ], 'y'), 232 | { 233 | '2012': [ 234 | ['2012-02-15', '1', 'foo1'], 235 | ['2012-02-18', '2', 'foo2'], 236 | ['2012-02-28', '3', 'foo3'] 237 | ], 238 | '2013': [ 239 | ['2013-03-04', '-4', 'bar1'], 240 | ['2013-03-18', '-5', 'bar2'] 241 | ], 242 | '2014': [['2014-04-01', '6', 'baz']], 243 | 'keys': ['2012', '2013', '2014'] 244 | } 245 | ); 246 | // -------------------------------------------------------------- ^ Test 26 247 | 248 | 249 | console.log('-------------- MoneyLog Tests END'); 250 | -------------------------------------------------------------------------------- /storage/drivers/googledrive.js: -------------------------------------------------------------------------------- 1 | // Google Drive integration for MoneyLog 2 | // 3 | // By default, it searches for a MoneyLog folder in the root of your Drive, 4 | // and load all its files automatically, no user action required. 5 | // 6 | // If that folder is not found, then the Google Picker is loaded so the 7 | // user can point where the MoneyLog folder is located. 8 | 9 | ml.storage.drivers.googledrive = { 10 | id: 'googledrive', 11 | name: 'Google Drive', 12 | config: { 13 | isAsync: true, 14 | isEditable: false, 15 | isFileBased: true, 16 | isReloadable: true, 17 | showFolderLink: true, 18 | loadDataAtSetup: false, 19 | 20 | // Google Drive API has a User Rate Limit of 10 requests per second 21 | // See https://github.com/aureliojargas/moneylog/issues/22 22 | maxFilesForStar: 9 23 | }, 24 | 25 | userFolder: {}, // {id:'', name:''} 26 | configFile: {}, // {id:'', name:''} 27 | userFiles: [], // [{id:'', name:''}, ...] 28 | defaultFile: '', 29 | 30 | readAsync: function (fileData, callback) { 31 | this.readFile(fileData.id, callback); 32 | }, 33 | 34 | init: function () { 35 | ml.storage.resetFilesCombo(); 36 | ml.storage.resetWidgetFolder(); 37 | 38 | if (!window.gapi) { 39 | // Load the Google API 40 | addScript('https://apis.google.com/js/api.js', this.onApiLoad.bind(this)); 41 | } else { 42 | // Already loaded, call API entrypoint 43 | this.onApiLoad(); 44 | } 45 | }, 46 | 47 | // ----------------------------------------------------------------------- 48 | 49 | // The base of this code is a copy/paste from the official documentation: 50 | // https://developers.google.com/picker/docs/ 51 | 52 | // The Browser API and Client ID keys obtained from the Google API Console 53 | developerKey: 'AIzaSyAgPNmODKpMNzP30VdvvgQFSw-H8mtIegc', 54 | clientId: '372105999892-po48pkb5kjlhlf1t3j2bj96se9v986cp.apps.googleusercontent.com', 55 | oauthToken: '', 56 | 57 | // Scope to use to access user's files (the more restrictive, the better) 58 | // https://developers.google.com/drive/v3/web/about-auth 59 | scope: ['https://www.googleapis.com/auth/drive.readonly'], 60 | 61 | // https://developers.google.com/picker/docs/#i18n 62 | pickerLanguages: { 63 | pt: 'pt-BR', 64 | en: 'en', 65 | es: 'es', 66 | ca: 'ca' 67 | }, 68 | 69 | // Use the API Loader script to load google.picker and gapi.auth. 70 | onApiLoad: function () { 71 | gapi.load('auth', this.onAuthApiLoad.bind(this)); 72 | }, 73 | 74 | onAuthApiLoad: function () { 75 | gapi.auth.authorize( 76 | { 77 | 'client_id': this.clientId, 78 | 'scope': this.scope, 79 | 'immediate': false 80 | }, 81 | function handleAuthResult(authResult) { 82 | if (authResult && !authResult.error) { 83 | this.oauthToken = authResult.access_token; 84 | this.onAuthOk(); 85 | } else { 86 | console.log('Google auth failed:', authResult); 87 | } 88 | }.bind(this) 89 | ); 90 | }, 91 | 92 | onAuthOk: function () { 93 | this.findUserFolder(function () { 94 | this.setWidgetFolder(); 95 | this.listAllUserFiles(this.processFiles.bind(this)); 96 | }.bind(this)); 97 | }, 98 | 99 | runPicker: function (callback) { 100 | 101 | // Load Picker API 102 | gapi.load( 103 | 'picker', 104 | function onPickerApiLoad() { 105 | 106 | // Picker setup: will show folders only, in hierarquical view 107 | var view = new google.picker.DocsView(google.picker.ViewId.FOLDERS) 108 | .setParent('root') 109 | .setIncludeFolders(true) 110 | .setSelectFolderEnabled(true) 111 | .setMode(google.picker.DocsViewMode.LIST); 112 | 113 | // Load Picker 114 | var picker = new google.picker.PickerBuilder() 115 | .addView(view) 116 | .setOAuthToken(this.oauthToken) 117 | .setDeveloperKey(this.developerKey) 118 | .enableFeature(google.picker.Feature.NAV_HIDDEN) 119 | .setLocale(this.pickerLanguages[lang]) 120 | .setTitle(i18n.labelLocateAppFolder) 121 | .setCallback(function onPickerDone(data) { 122 | if (data.action == google.picker.Action.PICKED) { 123 | this.userFolder = data.docs[0]; // Save picked folder metadata 124 | callback(); 125 | } 126 | }.bind(this)) 127 | .build(); 128 | picker.setVisible(true); 129 | 130 | }.bind(this) 131 | ); 132 | }, 133 | 134 | processFiles: function (files) { 135 | 136 | // Filter relevant files 137 | this.userFiles = files.filter(function (file) { return file.name.endsWith('.txt'); }); 138 | this.configFile = files.filter(function (file) { return file.name === 'config.js'; })[0] || {}; 139 | 140 | ml.storage.populateFilesCombo(); 141 | 142 | // Apply user config.js file (if any) 143 | if (this.configFile.id) { 144 | this.readFile(this.configFile.id, function (contents) { 145 | ml.storage.applyUserConfig(contents); 146 | ml.storage.setFilesCombo(this.defaultFile); 147 | loadData(); 148 | }.bind(this)); 149 | } else { 150 | ml.storage.setFilesCombo(this.defaultFile); 151 | loadData(); 152 | } 153 | }, 154 | 155 | // Set this.userFolder and callback 156 | findUserFolder: function (callback) { 157 | this.findDefaultUserFolder(function () { 158 | 159 | // Found default /MoneyLog folder 160 | if (this.userFolder.id) { 161 | callback(); 162 | 163 | // Not found, will prompt user with the Picker 164 | } else { 165 | this.runPicker(callback); 166 | } 167 | }.bind(this)); 168 | }, 169 | 170 | findDefaultUserFolder: function (callback) { 171 | // Folder named MoneyLog at Google Drive root 172 | var query = '"root" in parents and name = "MoneyLog" and mimeType = "application/vnd.google-apps.folder"'; 173 | this.listFiles(query, function (files) { 174 | if (files && files.length > 0) { 175 | this.userFolder = files[0]; 176 | } 177 | callback(); 178 | }.bind(this)); 179 | }, 180 | 181 | setWidgetFolder: function () { 182 | var a = document.getElementById('storage-folder'); 183 | a.href = 'https://drive.google.com/drive/folders/' + this.userFolder.id; 184 | a.innerText = this.userFolder.name; 185 | }, 186 | 187 | listAllUserFiles: function (callback) { 188 | var query; 189 | if (this.userFolder.id) { 190 | query = '"' + this.userFolder.id + '" in parents and (mimeType = "text/plain" or mimeType = "application/x-javascript")'; 191 | this.listFiles(query, callback); 192 | } 193 | }, 194 | 195 | // https://developers.google.com/drive/v3/web/folder 196 | // https://developers.google.com/drive/v3/reference/files/list 197 | // https://developers.google.com/drive/v3/web/search-parameters 198 | listFiles: function (query, callback) { 199 | var url, queryString, accessToken, xhr, data; 200 | 201 | accessToken = gapi.auth.getToken().access_token; 202 | url = 'https://www.googleapis.com/drive/v3/files'; 203 | queryString = encodeQueryData({ 204 | q: 'trashed = false and ' + query, // never list trashed 205 | spaces: 'drive', 206 | orderBy: 'name', 207 | fields: 'files(id, name)' 208 | }); 209 | 210 | xhr = new XMLHttpRequest(); 211 | xhr.open('GET', url + '?' + queryString); 212 | xhr.setRequestHeader('Authorization', 'Bearer ' + accessToken); 213 | xhr.onreadystatechange = function () { 214 | if (xhr.readyState === XMLHttpRequest.DONE) { 215 | if (xhr.status === 200) { 216 | data = JSON.parse(xhr.responseText); 217 | callback(data.files); 218 | } else { 219 | console.log(xhr.responseText); 220 | } 221 | } 222 | }; 223 | xhr.send(); 224 | }, 225 | 226 | // https://developers.google.com/drive/v3/web/manage-downloads 227 | readFile: function (id, callback) { 228 | var downloadUrl, accessToken, xhr; 229 | 230 | if (id) { 231 | downloadUrl = 'https://www.googleapis.com/drive/v3/files/' + id + '?alt=media'; 232 | accessToken = gapi.auth.getToken().access_token; 233 | 234 | xhr = new XMLHttpRequest(); 235 | xhr.open('GET', downloadUrl); 236 | xhr.setRequestHeader('Authorization', 'Bearer ' + accessToken); 237 | xhr.onreadystatechange = function () { 238 | if (xhr.readyState === XMLHttpRequest.DONE) { 239 | if (xhr.status === 200) { 240 | callback(xhr.responseText); 241 | } else { 242 | console.log(xhr.responseText); 243 | } 244 | } 245 | }; 246 | xhr.send(); 247 | } else { 248 | console.log('ERROR: No file id informed'); 249 | } 250 | } 251 | }; 252 | -------------------------------------------------------------------------------- /.eslintrc.yml: -------------------------------------------------------------------------------- 1 | env: 2 | browser: true 3 | 4 | # Unfortunately, MoneyLog is a globals festival :( 5 | # These globals are used by other files than moneylog.js 6 | globals: 7 | addScript: false 8 | appName: false 9 | array2ul: false 10 | computeTotals: false 11 | encodeQueryData: false 12 | gapi: false 13 | google: false 14 | groupByPeriod: false 15 | i18nDatabase: false 16 | initUI: false 17 | loadData: false 18 | ml: false 19 | resetData: false 20 | sanitizeConfig: false 21 | selectOptionByText: false 22 | showError: false 23 | showReport: false 24 | Widget: false 25 | # modified 26 | dataFiles: true 27 | dataFilesDefault: true 28 | i18n: true 29 | lang: true 30 | 31 | extends: 'eslint:recommended' 32 | rules: 33 | accessor-pairs: error 34 | array-bracket-newline: 35 | - error 36 | - multiline: true 37 | array-bracket-spacing: 38 | - error 39 | - never 40 | array-callback-return: error 41 | array-element-newline: 'off' #ok 42 | block-scoped-var: error 43 | block-spacing: 44 | - error 45 | - always 46 | brace-style: 47 | - error 48 | - 1tbs 49 | - allowSingleLine: true 50 | callback-return: 'off' #ok 51 | camelcase: error 52 | capitalized-comments: 'off' #ok 53 | class-methods-use-this: error 54 | comma-dangle: error 55 | comma-spacing: error 56 | comma-style: 57 | - error 58 | - last 59 | complexity: 'off' #ok 60 | computed-property-spacing: 61 | - error 62 | - never 63 | consistent-return: error 64 | consistent-this: error 65 | curly: error 66 | default-case: error 67 | dot-location: 68 | - error 69 | - property 70 | dot-notation: error 71 | eol-last: error 72 | eqeqeq: 'off' 73 | for-direction: error 74 | func-call-spacing: error 75 | func-name-matching: error 76 | func-names: 'off' #ok 77 | func-style: 78 | - error 79 | - declaration 80 | function-paren-newline: error 81 | getter-return: error 82 | global-require: error 83 | guard-for-in: 'off' 84 | handle-callback-err: error 85 | id-blacklist: error 86 | id-length: 'off' #ok 87 | id-match: error 88 | implicit-arrow-linebreak: error 89 | indent: 90 | - error 91 | - tab 92 | init-declarations: 'off' #ok 93 | jsx-quotes: error 94 | key-spacing: error 95 | keyword-spacing: error 96 | line-comment-position: 'off' #ok 97 | linebreak-style: 98 | - error 99 | - unix 100 | lines-around-comment: error 101 | lines-between-class-members: error 102 | max-depth: 'off' 103 | max-len: 'off' #ok 104 | max-lines: 'off' #ok 105 | max-nested-callbacks: error 106 | max-params: 'off' #ok 107 | max-statements: 'off' #ok 108 | max-statements-per-line: 109 | - error 110 | - max: 2 111 | multiline-comment-style: 112 | - error 113 | - separate-lines 114 | multiline-ternary: 115 | - error 116 | - always-multiline 117 | new-cap: error 118 | new-parens: error 119 | newline-per-chained-call: 'off' #ok 120 | no-alert: error 121 | no-array-constructor: error 122 | no-await-in-loop: error 123 | no-bitwise: error 124 | no-buffer-constructor: error 125 | no-caller: error 126 | no-catch-shadow: error 127 | no-console: 'off' #ok 128 | no-continue: 'off' #ok 129 | no-div-regex: error 130 | no-else-return: 'off' #ok 131 | no-empty: 132 | - error 133 | - allowEmptyCatch: true 134 | no-empty-function: error 135 | no-eq-null: error 136 | no-eval: error 137 | no-extend-native: 'off' #ok 138 | no-extra-bind: error 139 | no-extra-label: error 140 | no-extra-parens: 'off' #ok 141 | no-floating-decimal: error 142 | no-implicit-coercion: error 143 | no-implicit-globals: 'off' #ok 144 | no-implied-eval: error 145 | no-inline-comments: 'off' #ok 146 | no-inner-declarations: 147 | - error 148 | - functions 149 | no-invalid-this: error 150 | no-iterator: error 151 | no-label-var: error 152 | no-labels: error 153 | no-lone-blocks: error 154 | no-lonely-if: 'off' #ok 155 | no-loop-func: error 156 | no-magic-numbers: 'off' #ok 157 | no-mixed-operators: 158 | - error 159 | - allowSamePrecedence: true 160 | no-mixed-requires: error 161 | no-multi-assign: error 162 | no-multi-spaces: 'off' #ok 163 | no-multi-str: error 164 | no-multiple-empty-lines: 165 | - error 166 | - max: 2 167 | maxBOF: 0 168 | maxEOF: 0 169 | no-negated-condition: 'off' #ok 170 | no-nested-ternary: 'off' 171 | no-new: error 172 | no-new-func: error 173 | no-new-object: error 174 | no-new-require: error 175 | no-new-wrappers: error 176 | no-octal-escape: error 177 | no-param-reassign: 'off' #ok 178 | no-path-concat: error 179 | no-plusplus: 180 | - error 181 | - allowForLoopAfterthoughts: true 182 | no-process-env: error 183 | no-process-exit: error 184 | no-proto: error 185 | no-prototype-builtins: error 186 | no-restricted-globals: error 187 | no-restricted-modules: error 188 | no-restricted-properties: error 189 | no-restricted-syntax: error 190 | no-return-assign: error 191 | no-return-await: error 192 | no-script-url: error 193 | no-self-compare: error 194 | no-sequences: error 195 | no-shadow: error 196 | no-shadow-restricted-names: error 197 | no-sync: error 198 | no-tabs: 'off' #ok 199 | no-template-curly-in-string: error 200 | no-ternary: 'off' #ok 201 | no-throw-literal: error 202 | no-trailing-spaces: error 203 | no-undef-init: error 204 | no-undefined: 'off' 205 | no-underscore-dangle: error 206 | no-unmodified-loop-condition: error 207 | no-unneeded-ternary: error 208 | no-unused-expressions: error 209 | no-use-before-define: 'off' #ok 210 | no-useless-call: error 211 | no-useless-concat: error 212 | no-useless-return: error 213 | no-void: error 214 | no-warning-comments: error 215 | no-whitespace-before-property: error 216 | no-with: error 217 | nonblock-statement-body-position: error 218 | object-curly-newline: error 219 | object-curly-spacing: error 220 | one-var: 221 | - error 222 | - uninitialized: always 223 | initialized: never 224 | one-var-declaration-per-line: error 225 | operator-assignment: 'off' #ok 226 | operator-linebreak: 227 | - error 228 | - after 229 | padded-blocks: 'off' #ok 230 | padding-line-between-statements: 231 | - error 232 | - blankLine: always 233 | prev: "*" 234 | next: function 235 | prefer-promise-reject-errors: error 236 | quote-props: 'off' #ok 237 | quotes: 238 | - error 239 | - single 240 | radix: 241 | - error 242 | - always 243 | require-await: error 244 | require-jsdoc: 'off' #ok 245 | semi: error 246 | semi-spacing: 247 | - error 248 | - after: true 249 | before: false 250 | semi-style: 251 | - error 252 | - last 253 | sort-keys: 'off' #ok 254 | sort-vars: 'off' #ok 255 | space-before-blocks: error 256 | space-before-function-paren: 257 | - error 258 | - anonymous: always 259 | named: never 260 | asyncArrow: always 261 | space-in-parens: 'off' #ok 262 | space-infix-ops: error 263 | space-unary-ops: error 264 | spaced-comment: error 265 | strict: 'off' #ok 266 | switch-colon-spacing: error 267 | template-tag-spacing: error 268 | unicode-bom: 269 | - error 270 | - never 271 | valid-jsdoc: error 272 | vars-on-top: error 273 | wrap-iife: 274 | - error 275 | - inside 276 | wrap-regex: error 277 | yoda: 278 | - error 279 | - never 280 | 281 | ### ES6 rules - not for me 282 | # arrow-body-style: error 283 | # arrow-parens: error 284 | # arrow-spacing: error 285 | # generator-star-spacing: error 286 | # no-confusing-arrow: error 287 | # no-duplicate-imports: error 288 | # no-restricted-imports: error 289 | # no-useless-computed-key: error 290 | # no-useless-constructor: error 291 | # no-useless-rename: error 292 | # no-var: 'off' #ok 293 | # object-shorthand: 'off' #ok 294 | # prefer-arrow-callback: 'off' #ok 295 | # prefer-const: error 296 | # prefer-destructuring: 'off' #ok 297 | # prefer-numeric-literals: error 298 | # prefer-rest-params: 'off' #ok 299 | # prefer-spread: error 300 | # prefer-template: 'off' #ok 301 | # rest-spread-spacing: error 302 | # sort-imports: error 303 | # symbol-description: error 304 | # template-curly-spacing: error 305 | # yield-star-spacing: error 306 | -------------------------------------------------------------------------------- /NEWS.t2t: -------------------------------------------------------------------------------- 1 | NEWS — MoneyLog v5 2 | March, 2012 3 | 4 | %%% txt2tags config - http://txt2tags.org 5 | % 6 | %!target: html 7 | %!encoding: UTF-8 8 | %!options: --toc 9 | % 10 | % Twitter autolink 11 | %!preproc(html): @([A-Za-z0-9_]+) [@\1 http://twitter.com/\1] 12 | % 13 | % Add links to SVN revisions [r123] 14 | %!postproc(html): '\[r(\d+)\]' 'r\1' 15 | % 16 | % Add links to configuration keys ``fooBar`` 17 | %!preproc(html): ``(.*?)`` [\1 https://aurelio.net/moneylog/config/#\1] 18 | % 19 | % Fix some config links: #i18nDatabase.pt.dateFormat -> #i18nDatabase-pt-dateFormat 20 | %!postproc(html): (/config/#[A-Za-z0-9-]+?)\. \1- 21 | %!postproc(html): (/config/#[A-Za-z0-9-]+?)\. \1- 22 | 23 | 24 | == New flavors ==[flavors] 25 | - Now the app come in four flavors: 26 | - **MoneyLog Cloud** — runs online, get TXT and config files from [Dropbox http://dropbox.com]. (by @xupisco) 27 | - **MoneyLog Browser** — runs online and offline, saves data to browser [localStorage http://en.wikipedia.org/wiki/Web_storage]. 28 | - **MoneyLog Portable** — runs offline, all-in-one HTML file with app and user data. 29 | - **MoneyLog Beta** — runs offline, with local TXT files. This is the default SVN version. 30 | - These flavors are generated by the scripts named gen-* in [SVN/trunk/util/ http://code.google.com/p/moneylog-dev/source/browse/#svn%2Ftrunk%2Futil]. 31 | 32 | 33 | == New interface (UI) ==[ui] 34 | - All new user interface. (by @xupisco) [r285] 35 | - Toolbar at left side, with collapsable boxes. 36 | - Reports at right. 37 | - Help screen is gone. 38 | - No colors, black & white UI. Each MoneyLog flavor will have its own color theme. 39 | 40 | 41 | == Translations ==[i18n] 42 | - Added es **Spanish** (Argentina) translation. (by @g_nemmi and Isadora Pinardi) [r137] 43 | - Added ca **Catalan** translation. (by @pacoriviere) [r126] 44 | 45 | 46 | == Mobile ==[mobile] 47 | - New mobile UI, for screens with 480px width or less. [r459] 48 | - New iOS icon. (by @xupisco) [r466] 49 | 50 | 51 | == Printer ready ==[print] 52 | - New print style, removing colors and undesired elements from interface. (thanks @xupisco) [r465] 53 | 54 | 55 | == User data ==[user-data] 56 | - **New data separator:** spaces are now allowed, the TAB is no longer mandatory. DO NOT use spaces inside values. [r268] [r392] 57 | - Out-of-range days (2000-01-99) will turn to last month day (2000-01-31). [r496] 58 | - New config ``dataFilesDefault`` to set the default file when using multiple TXT files (local or Dropbox). [r260] 59 | - New config ``ignoreDataOlderThan``, to ignore entries older than the specified date. [r415] 60 | - New config ``ignoreDataNewerThan``, to ignore entries newer than the specified date. [r417] 61 | 62 | 63 | == Password ==[password] 64 | - **New feature: access password.** Use the ``myPassword`` config. (thanks @bebetasso) [r434] 65 | 66 | 67 | == Full Screen mode ==[full-screen] 68 | - New button for Full Screen mode. [r119] 69 | - New config ``initFullScreen`` to start app in Full Screen mode. (thanks @xupisco) [r324] 70 | 71 | 72 | == Filters for all reports ==[filters-all] 73 | - Now all filters are also available in Monthly and Yearly reports. (thanks @denilsonsa, @eeev2011) [r514] 74 | 75 | 76 | == View widget ==[widget-view] 77 | - New config ``showViewWidget`` to enable/disable the View widget. [r387] 78 | - New config ``initViewWidgetOpen`` to open/close the View widget. [r386] 79 | 80 | 81 | == Tag Cloud widget ==[widget-tag-cloud] 82 | - New option in Tag Cloud: [X] Reset. (thanks @wcomnisky) [r383] 83 | - **New tri-state Tag Cloud:** first click select, second exclude (negate), third unselect. The option "[X] Group selected tags" does not affect excluded tags. [r451] 84 | - New config ``showTagCloud`` to enable/disable the Tag Cloud. [r387] 85 | - New config ``initTagCloudOpen`` to open/close the Tag Cloud. [r386] 86 | - New config ``initSelectedTags``, to select some tags at start up. [r444] 87 | - New config ``initExcludedTags``, to exclude some tags at start up. [r452] 88 | - New config ``ignoreTags`` to ignore all entries with one of the specified tags. (thanks @erickmor) [r435] 89 | - The config ``highlightTags`` now can also be an array. Useful for tags with spaces. [r431] 90 | 91 | 92 | == Tag Summary widget ==[widget-tag-summary] 93 | - New feature: Tag Summary. [r194] [r511] 94 | - New option in Tag Summary: [X] Sort by value. [r429] 95 | - New config ``TagSummary.config.active`` to enable/disable the Tag Summary. [r387] [r511] 96 | - New config ``TagSummary.config.opened`` to open/close the Tag Summary. [r386] [r511] 97 | - New config ``TagSummary.config.showTagless`` to show/hide the EMPTY tag. (thanks @denilsonsa) [r211] [r511] 98 | - New config ``TagSummary.config.checkSort`` to check the option [X] Order by value. [r430] [r511] 99 | 100 | 101 | == About widget ==[widget-about] 102 | 103 | - New About widget. [r591] 104 | 105 | 106 | == Tag report ==[tag-report] 107 | - **New feature: Tag Report** in monthly and yearly reports. Table headings are clickable for sorting. (thanks @denilsonsa) [r524] 108 | - New option in Tag Report: [X] Hide related tags. [r527] 109 | - New config ``checkHideRelatedTags``, to check/uncheck the option [X] Hide related tags. [r527] 110 | - New config ``showTagReport``, to enable/disable the Tag Report. [r533] 111 | 112 | 113 | == Rows Summary ==[rows-summary] 114 | - New feature: click a report row to highlight it. Click again to un-highlight it. [r180] 115 | - **New feature: Rows Summary.** When selecting rows, a popup appears with: sum, max, min, average, count. (thanks @denilsonsa) [r207] 116 | - Only show the rows summary when two or more rows are selected. [r281] 117 | - Rows Summary now works for the monthly and yearly reports, with a new combo to choose the column: Incoming, Expense, Partial. [r342] 118 | - New button to reset the Rows Summary, undoing all user selections in the report. (thanks @denilsonsa) [r478] 119 | 120 | 121 | == Date format ==[date-format] 122 | - **New configs to set locale date**: ``showLocaleDate``, ``i18nDatabase.pt.dateFormat``, ``i18nDatabase.pt.dateFormatMonth``, ``i18nDatabase.pt.dateFormatYear``. Change "pt" to "en", "es"…. (thanks @g_nemmi) [r113] [r352] 123 | - New tokens for locale dates: B = full month name, b = abbreviated month name. [r356] 124 | - **Now showing locale dates by default.** [r412] 125 | - Now using month names instead numbers in default locale format. [r420] 126 | 127 | 128 | == Date range ==[date-range] 129 | - **New date filter:** combos to choose an initial and final month for the reports. It disables the Recent Only and Future Data options. (thanks @denilsonsa, @adolfont, @magasine) [r358] 130 | - Now the new date filter is the default. New config ``useLegacyDateFilter`` to bring the old options back. (thanks @denilsonsa) [r369] 131 | - New configs ``checkDateFrom`` and ``checkDateUntil``, to check/uncheck date filters at startup. [r378] 132 | - New config ``initMonthsOffsetFrom`` to set the initial selected item for From:. [r405] 133 | - New config ``initMonthsOffsetUntil`` to set the initial selected item for Until:. [r407] 134 | 135 | 136 | == Charts ==[charts] 137 | - Now showing chart bar original value as a tooltip. (by @_Felipe) [r144] 138 | - **New feature: charts at daily reports.** [r190] 139 | - New configs to set the initial selected item for each chart type: ``initChartDaily``, ``initChartMonthly``, ``initChartYearly``. [r192] 140 | - Fixed two bugs in the chart bar label. (thanks @xupisco) [r350] 141 | - When the value was exactly 1000, it was showing 1 instead 1k. 142 | - When the value was 1 million (or greater), it was showing 1000m instead 1m. 143 | 144 | 145 | == Misc ==[misc] 146 | - Faster TXT file loading. (thanks @ricobl) [r125] 147 | - Added tooltips (help) for all controls. [r165] 148 | - Added favicon. (by @xupisco) [r340] 149 | - Set the page TITLE to full app name. (thanks @xupisco) [r341] 150 | - Hide the totals rows if there's only one row in the report. [r460] 151 | 152 | 153 | == Config ==[config] 154 | - New sample config file, in Portuguese. [r274] 155 | - Renamed some config names from default* to check*: ``checkMonthPartials``, ``checkRegex``, ``checkNegate``. [r380] 156 | - New config ``showBalance``, to show/hide the Balance column in all reports. [r529] 157 | - New configs for the default report sorting: ``sortData.d.index``, ``sortData.d.rev``, ``sortData.m.index``, ``sortData.m.rev``, ``sortData.y.index``, ``sortData.y.rev``. [r523] 158 | - The config ``oneFile`` was removed. [r288] 159 | - Check the new config guide https://aurelio.net/moneylog/config/ 160 | 161 | 162 | == Widgets ==[widgets] 163 | - Added Widget support. See sample in sample/widget-nerd-toy.js. [r505] 164 | - Added three new "hello world" sample Widgets. [r513] 165 | 166 | 167 | == Test Suite ==[test-suite] 168 | - New test-suite test/functions.js. Instructions at file header. [r549] 169 | 170 | -------------------------------------------------------------------------------- /moneylog.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 28 | 29 | MoneyLog 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 | 71 | 72 |
||
73 |
74 | 75 |
76 |
77 | 78 | 79 |
80 | Storage 81 |
82 | 84 | 86 | 88 | 91 |
92 |
93 | 94 |
95 | 96 | 97 |
98 | 102 |
103 | 104 |
105 | 106 | 107 | 124 | 125 | 126 | 127 |
128 | 129 |
130 | 131 | 132 |
133 | 135 | 137 | 138 |
139 | 140 | 141 |
142 | 144 | 146 | 147 |
148 | 149 | 150 |
151 | 153 | 155 |
156 | 157 | 158 |
159 | 161 | 163 | 164 |
165 | 169 |
170 |
171 | 172 |
173 |
174 | 175 | 176 |
177 | 178 |
179 | 180 |
181 |
182 | 183 |
184 |
185 |
186 | 188 | 190 |
191 |
192 | 194 | 196 |
197 |
198 |
199 |
200 | 201 | 202 |
203 |
204 | 205 |
206 |
207 |
208 | 209 | 210 |
211 | 212 | 216 | 217 |
218 |
219 | 220 | 221 |
222 |
223 | 224 | 225 |
226 | 227 |
228 |
229 | 230 | 231 |
232 |
233 | 234 |
235 | 237 | 239 |
240 |
241 | 242 | 243 |
244 | 245 |
246 | 247 |
248 | 249 | 251 | 252 |
253 | 254 | 255 | 256 |
257 |
258 |
259 | 260 |
261 |
262 | 263 | 264 |
265 |
266 | 267 | 268 |
269 | 
270 | 271 |
272 | 273 | 274 | -------------------------------------------------------------------------------- /sample/config-pt.js: -------------------------------------------------------------------------------- 1 | // 2 | // Este é o arquivo de configuração do MoneyLog. 3 | // 4 | // Aqui você pode alterar o comportamento padrão do programa, modificando as 5 | // configurações disponíveis. Você pode, por exemplo, mudar o idioma para 6 | // inglês ou sempre iniciar no relatório mensal. 7 | // 8 | // Para saber mais informações sobre cada uma das configurações, visite: 9 | // 10 | // https://aurelio.net/moneylog/config/ 11 | // 12 | // -------------------------------------------------------------------------- 13 | // 14 | // Este é um arquivo em JavaScript, você deve seguir as regras de sintaxe da 15 | // linguagem. Se você não sabe nada de JavaScript, não se preocupe. Vou lhe 16 | // explicar o básico necessário. As configurações estão logo após este texto 17 | // explicativo. 18 | // 19 | // COMO ATIVAR/DESATIVAR UMA CONFIGURAÇÃO 20 | // -------------------------------------- 21 | // 22 | // Todas as configurações deste arquivo estão desativadas. Isso quer dizer 23 | // que nenhuma delas vai afetar o funcionamento do MoneyLog. Você precisa 24 | // ativar aquelas que desejar utilizar. 25 | // 26 | // É muito simples, para ativar uma configuração, basta apagar os // que 27 | // estão no começo da linha. 28 | // 29 | // De: 30 | // // opcaoBacana = 12 ;// comentário 31 | // 32 | // Para: 33 | // opcaoBacana = 12 ;// comentário 34 | // 35 | // Se você mudar de ideia e quiser desativar a configuração, fazendo o 36 | // MoneyLog voltar ao seu comportamento padrão, basta recolocar os // na 37 | // frente. 38 | // 39 | // 40 | // COMO ALTERAR UMA CONFIGURAÇÃO ATIVA 41 | // ----------------------------------- 42 | // 43 | // Basta apenas alterar o conteúdo de cada configuração e não mexer no resto. 44 | // Por exemplo, estes são os três formatos que você vai encontrar: 45 | // 46 | // opcaoBacana = 12 ;// comentário 47 | // opcaoBacana = S ;// comentário 48 | // opcaoBacana = 'texto' ;// comentário 49 | // 50 | // No primeiro exemplo o conteúdo é um número, 12. Basta você trocar este 51 | // número por outro e pronto. Deixe todo o resto intocado. 52 | // 53 | // No segundo exemplo é uma configuração do tipo LIGA/DESLIGA. Há somente 54 | // dois valores possíveis para ela: S e N, que significam sim e não. Não use 55 | // números ou qualquer outra letra, nem aspas. Não use minúsculas. 56 | // 57 | // No terceiro exemplo é uma configuração que recebe um texto entre aspas. 58 | // As aspas são importantes, não as apague. Apenas troque a palavra que está 59 | // dentro delas. 60 | // 61 | // -------------------------------------------------------------------------- 62 | 63 | 64 | // SENHA DE ACESSO 65 | // 66 | // Você pode definir uma senha de acesso, para impedir que outras pessoas 67 | // vejam seus dados. Porém, saiba que esta é uma proteção bem simples, que 68 | // pode ser facilmente quebrada por quem entende de tecnologias web. Use 69 | // apenas para impedir o acesso casual de familiares ou colegas não-nerds. 70 | // 71 | // myPassword = 'abc123' ;// Pedir esta senha ao iniciar o app 72 | 73 | 74 | // IDIOMA 75 | // 76 | // lang = 'pt' ;// pt, en, es (Português, Inglês, Espanhol) 77 | 78 | 79 | // EXTRATO PADRÃO AO INICIAR 80 | // 81 | // reportType = 'd' ;// d, m, y (diário, mensal, anual) 82 | 83 | 84 | // TELA CHEIA 85 | // 86 | // initFullScreen = N ;// Iniciar o app já no modo Tela Cheia? 87 | 88 | 89 | // BUSCA 90 | // 91 | // defaultSearch = '' ;// Iniciar já pesquisando por este texto 92 | // checkRegex = N ;// Marcar a opção [X] regex? 93 | // checkNegate = N ;// Marcar a opção [X] excluir? 94 | 95 | 96 | // PERÍODO - DATA INICIAL E FINAL 97 | // 98 | // checkDateFrom = S ;// Marcar a opção [X] De:? 99 | // checkDateUntil = S ;// Marcar a opção [X] Até:? 100 | // 101 | // As duas configurações seguintes servem para escolher qual será o valor 102 | // padrão que virá escolhido nos seletores de data De: e Até:. Coloque um 103 | // número, positivo ou negativo, que indicará o número de meses à partir da 104 | // data atual. Use números positivos para meses futuros e negativos para os 105 | // passados. Por exemplo, para dizer "três meses atrás", use -3. Para dizer 106 | // mês seguinte, use 1. Zero significa o mês corrente. 107 | // 108 | // initMonthOffsetFrom = -2 ;// Valor inicial da opção [X] De: 109 | // initMonthOffsetUntil = 0 ;// Valor inicial da opção [X] Até: 110 | 111 | 112 | // PARCIAIS MENSAIS 113 | // 114 | // checkMonthPartials = S ;// Marcar a opção [X] Parciais Mensais? 115 | 116 | 117 | // SOMENTE VALORES 118 | // 119 | // Nada ainda. 120 | 121 | 122 | // WIDGETS 123 | // 124 | // initViewWidgetOpen = S ;// Iniciar com a caixa Visualizar aberta? 125 | // initTagCloudOpen = S ;// Iniciar com a Nuvem de Tags aberta? 126 | // showTagCloud = S ;// Usar o widget Nuvem de Tags? 127 | 128 | 129 | // WIDGET: SOMATÓRIO DE TAGS 130 | // TagSummary.config.active = S ;// Usar o widget Somatório de tags? 131 | // TagSummary.config.opened = S ;// Iniciar com este widget já aberto? 132 | // TagSummary.config.showTagless = S ;// Mostrar o item (sem tag)? 133 | // TagSummary.config.checkSort = N ;// Marcar a opção [X] Ordenar por valor? 134 | 135 | 136 | // TABELA DO EXTRATO 137 | // 138 | // showBalance = S ;// Mostrar a coluna Acumulado? 139 | // showRowCount = S ;// Mostrar o número da linha à esquerda? 140 | // monthlyRowCount = S ;// O número da linha recomeça a cada mês? 141 | // 142 | // highlightWords = 'XXX TODO' ;// Destacar estas palavras na Descrição 143 | // highlightTags = 'luz água' ;// Destacar estas tags no extrato 144 | // 145 | // sortData.d.index = 1 ;// Diário: iniciar ordenando por esta coluna (1-4) 146 | // sortData.m.index = 1 ;// Mensal: iniciar ordenando por esta coluna (1-5) 147 | // sortData.y.index = 1 ;// Anual : iniciar ordenando por esta coluna (1-5) 148 | // sortData.d.rev = N ;// Diário: iniciar com a ordem inversa? 149 | // sortData.m.rev = N ;// Mensal: iniciar com a ordem inversa? 150 | // sortData.y.rev = N ;// Anual : iniciar com a ordem inversa? 151 | 152 | 153 | // GRÁFICO DE BARRAS 154 | // 155 | // showCharts = S ;// Mostrar gráfico de barras depois do extrato? 156 | // showChartBarLabel = S ;// Mostrar os números no topo de cada barra? 157 | // 158 | // initChartDaily = 3 ;// Iniciar mostrando este item no gráfico diário [1-4] 159 | // initChartMonthly = 1 ;// Iniciar mostrando este item no gráfico mensal [1-4] 160 | // initChartYearly = 1 ;// Iniciar mostrando este item no gráfico anual [1-4] 161 | 162 | 163 | // BARRA DE PORCENTAGEM 164 | // 165 | // showMiniBars = S ;// Mostrar barra de porcentagem no mensal/anual? 166 | // showMiniBarsLabels = S ;// Mostrar os números dentro destas barras? 167 | // miniBarWidth = 70 ;// Largura da barra de porcentagem, em pixels 168 | 169 | 170 | // TAGS 171 | // 172 | // showTagReport = S ;// Mostrar o relatório de tags? 173 | // ignoreTags = 'poupança' ;// Ignorar lançamentos com estas tags 174 | // initSelectedTags = 'água' ;// Iniciar já com estas tags marcadas 175 | // initExcludedTags = 'luz' ;// Iniciar já com estas tags riscadas 176 | // checkHideRelatedTags = N ;// Marcar a opção [X] Esconder relacionadas? 177 | 178 | 179 | // FORMATO DA DATA 180 | // 181 | // showLocaleDate = N ;// Mostrar datas no formato regional d/m/a? 182 | // 183 | // Você também pode personalizar o formato regional: usar outros separadores, 184 | // mudar a ordem ou até escolher exatamente quais componentes mostrar. Além 185 | // de símbolos, você pode usar as seguintes letras: 186 | // Y = ano com 4 dígitos b = nome do mês com 3 letras 187 | // y = ano com 2 dígitos B = nome completo do mês 188 | // m = mês 189 | // d = dia 190 | // 191 | // i18nDatabase.pt.dateFormat = 'd.m.Y' ;// Personalizar formato dia-mês-ano 192 | // i18nDatabase.pt.dateFormatMonth = 'B Y' ;// Personalizar formato mês-ano 193 | // i18nDatabase.pt.dateFormatYear = 'Y' ;// Personalizar formato ano 194 | 195 | 196 | // IGNORAR LANÇAMENTOS ANTIGOS E FUTUROS 197 | // 198 | // Se você já usa o MoneyLog há bastante tempo, pode querer simplesmente 199 | // ignorar os lançamentos antigos, dos anos anteriores. Ou ainda, limitar a 200 | // visão de anos futuros para poucos anos, sumindo de sua vista com aquelas 201 | // dezenas de parcelas do financiamento que vai demorar para acabar. Basta 202 | // colocar nas configurações seguintes as datas limite, no passado e/ou no 203 | // futuro, e o MoneyLog vai fingir que não viu nada :) 204 | // 205 | // ignoreDataOlderThan = '2010-01-01' ;// Ignorar lançamentos de 2009, 2008... 206 | // ignoreDataNewerThan = '2020-12-31' ;// Ignorar lançamentos após 2020 207 | 208 | 209 | // ARQUIVOS TXT 210 | // 211 | // 212 | // Se você usa mais de um arquivo TXT, o MoneyLog automaticamente carrega 213 | // todos os arquivos que encontrar em sua pasta. Com essa configuração você 214 | // pode mudar isso e carregar somente um arquivo específico no início. 215 | // 216 | // dataFilesDefault = 'meu-arquivo.txt'; 217 | // 218 | // 219 | // Se você quer usar mais de um arquivo TXT, deverá cadastrar todos eles no 220 | // array dataFiles. Podem ser inúmeros, tantos quantos você queira. Veja 221 | // alguns exemplos, separado por ano, por tipo de conta e por tipo de gasto: 222 | // 223 | // dataFiles = ['2012.txt', '2011.txt', '2010.txt']; 224 | // 225 | // dataFiles = ['bb.txt', 'caixa.txt', 'itau.txt', 'dinheiro.txt']; 226 | // 227 | // dataFiles = ['salario.txt', 'carro.txt', 'escola.txt', 'geral.txt']; 228 | // 229 | // Ao iniciar, o MoneyLog sempre carregará o primeiro item da lista. Para 230 | // mudar isso e carregar outro, basta usar esta configuração: 231 | // 232 | // dataFilesDefault = 'carro.txt'; 233 | // 234 | -------------------------------------------------------------------------------- /util/conversor/conversor.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 15 | 16 | Importar dados para o MoneyLog 17 | 18 | 19 | 20 | 23 | 24 | 325 | 326 | 327 | 328 | 329 |

Converta lançamentos para o formato do MoneyLog

330 | 331 |

Dúvidas? Aprenda como salvar o extrato de seu banco e usar este conversor.

332 | 333 | 338 | 339 |


340 | 341 |

De: 342 | 352 |

353 |
354 | 355 |
356 | 357 |



358 | 359 |

Para: MoneyLog

360 | 361 |
362 | 363 | 368 | 369 |

CONFIRA O RESULTADO. Este é um processo automático, pode haver falhas.

370 |
371 | 372 | 373 | 374 | 375 | -------------------------------------------------------------------------------- /moneylog.css: -------------------------------------------------------------------------------- 1 | /* 2 | MoneyLog default CSS 3 | by Aurelio Jargas 4 | https://aurelio.net/moneylog/ 5 | */ 6 | 7 | /* ---------------------------------------------- Reset CSS (from YUI) */ 8 | 9 | body,div,dl,dt,dd,ul,ol,li,h1,h2,h3,h4,h5,h6,pre,form,fieldset,input,textarea,p,blockquote,th,td { margin:0;padding:0; } 10 | table { border-collapse:collapse;border-spacing:0; } 11 | body { font:12px/1.231 arial,helvetica,clean,sans-serif;*font-size:small;*font:x-small; } 12 | select,input,button,textarea { font:99% arial,helvetica,clean,sans-serif; } 13 | table { font-size:inherit;font:100%; } 14 | th,td { border-style:none;padding:0; } 15 | p,blockquote,ul,ol,dl { margin:1em; } 16 | ol,ul,dl { margin-left:2em; } 17 | dl dd { margin-left:1em; } 18 | 19 | 20 | /* ---------------------------------------------- Structure */ 21 | 22 | body { 23 | background-color: white; /* content_bg */ 24 | color: #333; /* text_color */ 25 | } 26 | #container { 27 | width: 100%; 28 | margin: 0 auto; 29 | overflow: hidden; 30 | } 31 | #toolbar { 32 | position: fixed; 33 | top: 57px; /* This is the height of #logo-wrapper */ 34 | bottom: 0; 35 | left: 0; 36 | width: 217px; 37 | overflow: auto; 38 | } 39 | #content { 40 | margin-left: 217px; /* See toggleFullScreen, #toolbar */ 41 | padding: 30px; 42 | overflow: auto; 43 | } 44 | 45 | /* ---------------------------------------------- Misc */ 46 | 47 | label, 48 | .trigger { 49 | cursor: pointer; 50 | } 51 | .number { 52 | text-align: right !important; 53 | white-space: nowrap; 54 | } 55 | .neg { /* Negative numbers */ 56 | color: #e33; 57 | } 58 | .hl { /* Highlighted words */ 59 | color: black; 60 | background-color: #ddd; /* _light */ 61 | } 62 | #tag-report tr:hover td.totals, 63 | tr:hover td, 64 | tr.future:hover td { /* explicit TD to exclude TH */ 65 | background-color: #eee; /* hover_bg */ 66 | color: black; 67 | } 68 | #toolbar input, 69 | #toolbar label, 70 | #toolbar select { 71 | vertical-align:middle; 72 | } 73 | #toolbar input { 74 | border: 1px solid #bbb; /* element_border */ 75 | } 76 | 77 | /* ---------------------------------------------- Error */ 78 | 79 | #error { 80 | display: none; 81 | margin: 5em 10%; 82 | padding: 10px; 83 | border: 8px solid red; 84 | background-color: yellow; 85 | color: black; 86 | line-height: 180%; 87 | } 88 | 89 | /* ---------------------------------------------- Logo */ 90 | 91 | #logo-wrapper { 92 | position: fixed; 93 | top: 0; 94 | left: 0; 95 | } 96 | #logo { 97 | float: left; 98 | background-color: #EBEBEB; 99 | width: 201px; 100 | height: 57px; 101 | margin: 0; 102 | } 103 | #logo a { 104 | color: #333; /* text_color */ 105 | } 106 | #app-name { 107 | font-size: 30px; 108 | line-height: 30px; 109 | padding: 5px 0 0 20px; 110 | } 111 | #website:hover { 112 | text-decoration: underline; 113 | } 114 | #app-flavor { 115 | font-size: 15px; 116 | line-height: 15px; 117 | padding: 2px 0 0 130px; 118 | text-transform: lowercase; 119 | color: black; /* _dark */ 120 | } 121 | 122 | /* ---------------------------------------------- Full screen */ 123 | 124 | #fullscreen { 125 | font-size: 11px; 126 | background-color: black; /* _dark */ 127 | color: white; 128 | width: 15px; /* same as scrollbar width */ 129 | float: left; 130 | text-align: center; 131 | height: 57px; 132 | line-height: 55px; 133 | } 134 | 135 | /* ---------------------------------------------- Toolbar */ 136 | 137 | #toolbar a, 138 | a.button { 139 | text-decoration: none; 140 | } 141 | 142 | #toolbar hr { 143 | clear: both; 144 | border: 1px solid #ddd; /* toolbar_sep */ 145 | border-width: 1px 0 0 0; 146 | margin: 1em 0; 147 | } 148 | 149 | #toolbar-controls-wrapper { 150 | background-color: #FAFAFA; 151 | clear: both; 152 | width: 200px; 153 | } 154 | 155 | #toolbar-controls { 156 | padding: 15px 17px; 157 | } 158 | 159 | /* ---------------------------------------------- Button */ 160 | 161 | .button { 162 | display: block; 163 | margin: 0; 164 | text-align: center; 165 | font-weight: normal; 166 | font-size: 100%; 167 | line-height: 200%; 168 | text-transform: lowercase; 169 | border: 1px solid #bbb; /* element_border */ 170 | background-color: white; 171 | background-color: #F4F4F4; /* button_bg */ 172 | position: relative; 173 | } 174 | .button.active, 175 | a.button:active { 176 | border-color: black; /* _dark */ 177 | background-color: black; /* _dark */ 178 | color: white; 179 | } 180 | a.button { 181 | color: #333; /* text_color */ 182 | } 183 | a.button.active { 184 | color: white; 185 | } 186 | .button.wide { 187 | display: block !important; 188 | float: none !important; 189 | width: 100% !important; 190 | } 191 | .button.naked { 192 | background-color: inherit; 193 | border-style: none; 194 | } 195 | 196 | .widget-box > .button:before { 197 | content: "▶"; 198 | position: absolute; 199 | top: 0; 200 | left: 8px; /* The same as .widget-content padding-left */ 201 | } 202 | .widget-box > .button.active:before { 203 | content: "▼"; 204 | position: absolute; 205 | top: 0; 206 | left: 8px; /* The same as .widget-content padding-left */ 207 | } 208 | 209 | /* ---------------------------------------------- [ widgets ] */ 210 | 211 | .widget-box { 212 | margin: 1em 0; 213 | } 214 | .widget-box:first-child { 215 | margin-top: 0; 216 | } 217 | .widget-content { 218 | background-color: white; 219 | border: 1px solid #bbb; /* element_border */ 220 | border-top-width: 0; 221 | padding: 8px; 222 | line-height: 185%; 223 | } 224 | .widget-content a { 225 | color: #2B97E9; /* cloud_blue */ 226 | } 227 | .widget-content a.button { 228 | color: inherit; 229 | } 230 | #about-content a:hover { 231 | text-decoration: underline; 232 | /* Restricted to #about, this breaks Tag Cloud */ 233 | } 234 | .widget-content select, 235 | .widget-content table { 236 | width: 100%; 237 | } 238 | .widget-options input { 239 | display: inline; 240 | margin-right: 0.5em; 241 | } 242 | 243 | /* ---------------------------------------------- [file.txt ▼] */ 244 | 245 | #storage-content { 246 | display: none; /* JS will show */ 247 | } 248 | 249 | #storage-driver, 250 | #source-file { 251 | width: 100%; 252 | margin-bottom: 1em; 253 | } 254 | 255 | #storage-folder { 256 | display: block; 257 | margin: -0.5em 0 0.5em 0; 258 | } 259 | #storage-folder:before { 260 | content: "📁 "; /* U+1F4C1 File Folder */ 261 | } 262 | 263 | #source-file-box.mini { 264 | line-height: 150%; 265 | } 266 | #source-file.mini { 267 | width: 140px; 268 | } 269 | 270 | /* ---------------------------------------------- [edit] [reload] */ 271 | 272 | #source-reload, 273 | #editor-open { 274 | display: inline-block; /* see showHideEditButton() */ 275 | width: 69px; 276 | margin: 0; 277 | } 278 | #editor-open { 279 | margin-right: 5px; 280 | } 281 | 282 | #source-reload.mini { /* ↻ */ 283 | float: right; 284 | margin: 0; 285 | padding: 0; 286 | line-height: 150%; 287 | width: 1.5em; 288 | } 289 | #source-reload.mini:hover { 290 | background-color: black; 291 | color: white; 292 | } 293 | 294 | /* ---------------------------------------------- [daily] [monthly] [yearly] */ 295 | 296 | #toolbar #report-nav { 297 | text-align: center; 298 | } 299 | #toolbar #report-nav a { 300 | display: inline-block; 301 | width: 50px; 302 | margin: 0 5px 0 0; 303 | } 304 | #toolbar #report-nav #y { 305 | margin-right: 0; 306 | } 307 | 308 | /* ---------------------------------------------- [search ] */ 309 | 310 | #search-content { 311 | border-top-width: 1px; 312 | } 313 | #filter { 314 | width: 100%; 315 | border: 1px solid #C9C9C9; 316 | background-color: #F4F4F4; /* button_bg */ 317 | padding: 3px 0; 318 | margin-bottom: 5px; 319 | } 320 | #opt-negate-check { 321 | margin-left: 1em; 322 | } 323 | 324 | /* ---------------------------------------------- [X] Options */ 325 | 326 | #toolbar label:hover, 327 | #toolbar input:hover + label { 328 | /*font-weight: bold;*/ 329 | } 330 | .checkbox-option { 331 | text-align: left; 332 | clear: both; 333 | } 334 | .checkbox-option-extra { 335 | margin-left: 1em; 336 | } 337 | .auto-hide { 338 | display: none; /* appears when clicked, see toggleCheckboxOptionExtra() */ 339 | } 340 | .option-disabled { 341 | color: #AAAAAA; 342 | } 343 | 344 | /* ---------------------------------------------- [ view ] */ 345 | 346 | #view-options-content { 347 | display: none; /* JS will show */ 348 | } 349 | #opt-date-1-month-combo, 350 | #opt-date-2-month-combo, 351 | #opt-date-1-year-combo, 352 | #opt-date-2-year-combo { 353 | float: right; 354 | width: auto; 355 | } 356 | #opt-date-1-year-combo, 357 | #opt-date-2-year-combo { 358 | display: none; /* appears in Yearly report */ 359 | } 360 | #opt-value-filter-number { 361 | display: none; /* appears when SELECT item is chosen */ 362 | } 363 | 364 | /* ---------------------------------------------- [ tag cloud ] */ 365 | 366 | #tag-cloud-content { 367 | display: none; /* JS will show if there are tags */ 368 | text-align: center; 369 | } 370 | #tag-cloud-tags a { 371 | padding: 2px 2px; 372 | color: #333; /* text_color */ 373 | white-space: nowrap; 374 | } 375 | #tag-cloud-tags a.selected { 376 | background-color: #ddd; /* _light */ 377 | color: black; 378 | } 379 | #tag-cloud-tags a.excluded { 380 | text-decoration: line-through; 381 | } 382 | #tag-cloud-options { 383 | display: none; /* JS will show */ 384 | } 385 | 386 | /* ---------------------------------------------- [ tag summary ] */ 387 | 388 | #tag-summary-content { 389 | display: none; /* JS will show */ 390 | } 391 | 392 | /* ---------------------------------------------- [ about ] */ 393 | 394 | #about-content { 395 | display: none; /* JS will show */ 396 | text-align: center; 397 | } 398 | #about-donate { 399 | font-size: 150%; 400 | } 401 | #about-credits { 402 | display: inline-block; 403 | text-align: left; 404 | } 405 | 406 | /* ---------------------------------------------- Report */ 407 | 408 | /* labelNoData */ 409 | #report p { 410 | text-align: center; 411 | } 412 | 413 | /* Report table */ 414 | .report { 415 | margin: 0 auto; /* do not use width:100% */ 416 | } 417 | 418 | /* Table headings are also buttons */ 419 | .report th { 420 | text-align: center; 421 | cursor: pointer; 422 | padding: 0.5em; 423 | } 424 | .report th:hover { 425 | background-color: black; /* _dark */ 426 | color: white; 427 | } 428 | 429 | /* Column sorting is not working for some columns, so undo formatting */ 430 | th.row-count, 431 | table.daily th.balance, 432 | table.overview th.percent { 433 | cursor: auto !important; 434 | } 435 | th.row-count, 436 | table.daily th.balance:hover, 437 | table.overview th.percent:hover { 438 | color: #333 !important; /* text_color */ 439 | background-color: white !important; /* content_bg */ 440 | } 441 | 442 | /* Generic cell config */ 443 | .report td { 444 | border: 1px solid #ddd; /* _light */ 445 | border-width: 1px 0; 446 | vertical-align: middle; 447 | } 448 | 449 | /* Column config */ 450 | td.row-count { 451 | text-align: center; 452 | font-size: 75%; 453 | border-style: none !important; 454 | background-color: white !important; /* content_bg */ 455 | color: silver !important; /* _light_text */ 456 | } 457 | td.date { 458 | white-space: nowrap; 459 | } 460 | 461 | /* Future */ 462 | tr.future { 463 | background-color: #f8f8f8; /* future_bg */ 464 | font-style: italic; 465 | } 466 | tr.future td.row-count { 467 | font-style: normal; 468 | } 469 | 470 | /* Selected row */ 471 | tr.selected, 472 | tr.selected:hover td, 473 | tr.selected .neg { 474 | background-color: black; /* _dark */ 475 | color: white; 476 | } 477 | 478 | /* Totals rows & cells */ 479 | tr.totals, 480 | td.totals { 481 | background-color: #ddd; /* _light */ 482 | } 483 | tr.total, 484 | td.total { 485 | font-weight: bold; 486 | } 487 | 488 | /* Rows padding */ 489 | .report td { 490 | padding: 6px 10px; 491 | } 492 | .report tr.totals td { 493 | padding: 1px 10px; /* top/bottom: td - 6px */ 494 | } 495 | 496 | /* ---------------------------------------------- Report - Daily */ 497 | 498 | /* Total row tweaks */ 499 | #report table.daily tr.totals { 500 | font-weight: bold; 501 | } 502 | #report table.daily tr.totals table.posneg { 503 | float: right; 504 | font-size: 90%; 505 | font-weight: normal; 506 | } 507 | #report table.daily tr.totals table.posneg td { 508 | padding: 0; 509 | white-space: nowrap; 510 | border-style: none; 511 | } 512 | #report table.daily tr.totals td.monthtotal { 513 | text-align: left; 514 | padding-left: 0; 515 | } 516 | #report table.daily tr.totals td.monthtotal .arrow { 517 | margin-right: 8px; 518 | } 519 | 520 | /* ---------------------------------------------- Report - Monthly/Yearly */ 521 | 522 | .report td.rowlabel { 523 | font-weight: bold; 524 | } 525 | 526 | /* ---------------------------------------------- Minibars inside report table */ 527 | 528 | td.minibar { 529 | border-style: none !important; 530 | } 531 | div.minibar { 532 | float: left; 533 | } 534 | td.minibar .label { 535 | float: left; 536 | font-size: 75%; 537 | color: white !important; 538 | line-height: 14px; 539 | } 540 | 541 | /* ---------------------------------------------- Charts */ 542 | 543 | #charts { 544 | display: none; /* JS will show */ 545 | text-align: center; 546 | margin-top: 4em; 547 | } 548 | #chart-content { 549 | overflow: auto; 550 | } 551 | #chart-selector { 552 | margin-bottom: 5px; 553 | } 554 | .posbar { 555 | background-color: #2B97E9; /* bar1_bg cloud_blue */ 556 | } 557 | .negbar { 558 | background-color: #f33; /* bar2_bg */ 559 | } 560 | table.chart { 561 | margin: 0 auto; 562 | border: 1px solid #bbb; /* element_border */ 563 | border-collapse: separate; 564 | border-spacing: 7px; /* space between bars and also border */ 565 | } 566 | table.chart td.bar { 567 | text-align: center; 568 | vertical-align: bottom; 569 | } 570 | table.chart td.bar div.bar { 571 | width: 35px; /* must fit 4 digits for the year, i.e. 2012 */ 572 | margin: 0 auto; 573 | } 574 | table.chart td.bar .label { 575 | font-size: 11px; 576 | font-style: italic; 577 | } 578 | table.chart tr.label { 579 | font-size: 10px; 580 | line-height: 100%; 581 | text-align: center; 582 | } 583 | table.chart tr:hover td { 584 | background-color: white; /* content_bg */ 585 | } 586 | 587 | /* ---------------------------------------------- Tag report */ 588 | 589 | #tag-report { 590 | margin-top: 4em; 591 | } 592 | #tag-report th { 593 | text-align: right; 594 | vertical-align: bottom; 595 | padding: 6px 10px; /* same as .report td */ 596 | } 597 | #tag-report th.tagname { 598 | text-align: left; 599 | } 600 | #tag-report th i { /* obfuscate year */ 601 | font-weight: normal; 602 | font-style: normal; 603 | color: silver; /* _light_text */ 604 | } 605 | #tag-report th:hover i { 606 | color: white; 607 | } 608 | #tag-report td.totals { 609 | background-color: inherit; 610 | font-style: italic; 611 | } 612 | #tag-report td.total { 613 | padding-left: 20px; /* separate */ 614 | } 615 | #tag-report-options { 616 | display: none; /* JS will show */ 617 | text-align: center; 618 | margin-top: 1.5em; 619 | } 620 | 621 | /* ---------------------------------------------- Rows summary */ 622 | 623 | #rows-summary { 624 | display: none; /* JS will show */ 625 | position: fixed; 626 | right: 0; 627 | bottom: 0; 628 | padding: 5px; 629 | border: 8px solid black; /* _dark */ 630 | background-color: #ddd; /* _light */ 631 | } 632 | #rows-summary-content td { 633 | padding: 4px 10px; 634 | } 635 | #rows-summary-index { 636 | width: 100%; 637 | margin-bottom: 5px; 638 | } 639 | #rows-summary-reset { 640 | margin-top: 0.5em; 641 | } 642 | 643 | /* ---------------------------------------------- Footer */ 644 | 645 | #footer-message { 646 | color: silver; /* _light_text */ 647 | text-align: center; 648 | margin-top: 4em; 649 | } 650 | 651 | /* ---------------------------------------------- Editor */ 652 | 653 | /* The editor */ 654 | #editor { /* fill full window, hiding app interface */ 655 | display: none; 656 | position: fixed; 657 | top: 0; 658 | right: 0; 659 | bottom: 0; 660 | left: 0; 661 | background-color: white; /* content_bg */ 662 | } 663 | #editor-file-name { 664 | position: fixed; 665 | left: 41px; 666 | top: 21px; 667 | line-height: 29px; /* 50px (wrapper-top) - 21px (top) */ 668 | margin: 0; 669 | padding: 0 11px; 670 | letter-spacing: 1px; 671 | color: white; /* content_bg */ 672 | background-color: black; /* _dark */ 673 | } 674 | #editor-data-wrapper { /* position:fixed so we can set textarea height:~100% */ 675 | position: fixed; 676 | top: 50px; /* space for file name */ 677 | right: 30px; 678 | bottom: 70px; /* space for the buttons */ 679 | left: 30px; 680 | border: 8px solid black; /* _dark */ 681 | padding: 15px 0 15px 15px; 682 | } 683 | #editor-data { 684 | width: 100%; 685 | height: 100%; 686 | outline-width: 0; /* disable focus ring */ 687 | border-style: none; 688 | margin: 0; 689 | font-family: monospace; 690 | } 691 | #editor-buttons { 692 | position: fixed; 693 | right: 30px; 694 | bottom: 30px; 695 | } 696 | #editor-close, 697 | #editor-save { 698 | display: inline-block; 699 | width: 100px; 700 | } 701 | #editor-close { 702 | margin-right: 3px; 703 | } 704 | 705 | /* ---------------------------------------------- Data */ 706 | 707 | /* Always hidden */ 708 | #data, 709 | #data-frame { 710 | display: none !important; 711 | } 712 | 713 | -------------------------------------------------------------------------------- /NEWS.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | NEWS — MoneyLog v5 7 | 8 |
9 |

NEWS — MoneyLog v5

10 | March, 2012
11 |
12 | 13 |

14 |
15 |

16 | 17 |
41 | 42 |

43 |
44 |

45 | 46 |

New flavors

47 | 48 |
    49 |
  • Now the app come in four flavors: 50 |
      51 |
    • MoneyLog Cloud — runs online, get TXT and config files from Dropbox. (by @xupisco) 52 |
    • MoneyLog Browser — runs online and offline, saves data to browser localStorage. 53 |
    • MoneyLog Portable — runs offline, all-in-one HTML file with app and user data. 54 |
    • MoneyLog Beta — runs offline, with local TXT files. This is the default SVN version. 55 |
    56 |
  • These flavors are generated by the scripts named gen-* in SVN/trunk/util/. 57 |
58 | 59 |

New interface (UI)

60 | 61 |
    62 |
  • All new user interface. (by @xupisco) r285 63 |
      64 |
    • Toolbar at left side, with collapsable boxes. 65 |
    • Reports at right. 66 |
    • Help screen is gone. 67 |
    • No colors, black & white UI. Each MoneyLog flavor will have its own color theme. 68 |
    69 |
70 | 71 |

Translations

72 | 73 |
    74 |
  • Added es Spanish (Argentina) translation. (by @g_nemmi and Isadora Pinardi) r137 75 |
  • Added ca Catalan translation. (by @pacoriviere) r126 76 |
77 | 78 |

Mobile

79 | 80 |
    81 |
  • New mobile UI, for screens with 480px width or less. r459 82 |
  • New iOS icon. (by @xupisco) r466 83 |
84 | 85 |

Printer ready

86 | 87 |
    88 |
  • New print style, removing colors and undesired elements from interface. (thanks @xupisco) r465 89 |
90 | 91 |

User data

92 | 93 |
    94 |
  • New data separator: spaces are now allowed, the TAB is no longer mandatory. DO NOT use spaces inside values. r268 r392 95 |
  • Out-of-range days (2000-01-99) will turn to last month day (2000-01-31). r496 96 |
  • New config dataFilesDefault to set the default file when using multiple TXT files (local or Dropbox). r260 97 |
  • New config ignoreDataOlderThan, to ignore entries older than the specified date. r415 98 |
  • New config ignoreDataNewerThan, to ignore entries newer than the specified date. r417 99 |
100 | 101 |

Password

102 | 103 | 106 | 107 |

Full Screen mode

108 | 109 | 113 | 114 |

Filters for all reports

115 | 116 | 119 | 120 |

View widget

121 | 122 | 126 | 127 |

Tag Cloud widget

128 | 129 |
    130 |
  • New option in Tag Cloud: [X] Reset. (thanks @wcomnisky) r383 131 |
  • New tri-state Tag Cloud: first click select, second exclude (negate), third unselect. The option "[X] Group selected tags" does not affect excluded tags. r451 132 |
  • New config showTagCloud to enable/disable the Tag Cloud. r387 133 |
  • New config initTagCloudOpen to open/close the Tag Cloud. r386 134 |
  • New config initSelectedTags, to select some tags at start up. r444 135 |
  • New config initExcludedTags, to exclude some tags at start up. r452 136 |
  • New config ignoreTags to ignore all entries with one of the specified tags. (thanks @erickmor) r435 137 |
  • The config highlightTags now can also be an array. Useful for tags with spaces. r431 138 |
139 | 140 |

Tag Summary widget

141 | 142 | 150 | 151 |

About widget

152 | 153 |
    154 |
  • New About widget. r591 155 |
156 | 157 |

Tag report

158 | 159 |
    160 |
  • New feature: Tag Report in monthly and yearly reports. Table headings are clickable for sorting. (thanks @denilsonsa) r524 161 |
  • New option in Tag Report: [X] Hide related tags. r527 162 |
  • New config checkHideRelatedTags, to check/uncheck the option [X] Hide related tags. r527 163 |
  • New config showTagReport, to enable/disable the Tag Report. r533 164 |
165 | 166 |

Rows Summary

167 | 168 |
    169 |
  • New feature: click a report row to highlight it. Click again to un-highlight it. r180 170 |
  • New feature: Rows Summary. When selecting rows, a popup appears with: sum, max, min, average, count. (thanks @denilsonsa) r207 171 |
  • Only show the rows summary when two or more rows are selected. r281 172 |
  • Rows Summary now works for the monthly and yearly reports, with a new combo to choose the column: Incoming, Expense, Partial. r342 173 |
  • New button to reset the Rows Summary, undoing all user selections in the report. (thanks @denilsonsa) r478 174 |
175 | 176 |

Date format

177 | 178 | 184 | 185 |

Date range

186 | 187 | 194 | 195 |

Charts

196 | 197 |
    198 |
  • Now showing chart bar original value as a tooltip. (by @_Felipe) r144 199 |
  • New feature: charts at daily reports. r190 200 |
  • New configs to set the initial selected item for each chart type: initChartDaily, initChartMonthly, initChartYearly. r192 201 |
  • Fixed two bugs in the chart bar label. (thanks @xupisco) r350 202 |
      203 |
    • When the value was exactly 1000, it was showing 1 instead 1k. 204 |
    • When the value was 1 million (or greater), it was showing 1000m instead 1m. 205 |
    206 |
207 | 208 |

Misc

209 | 210 |
    211 |
  • Faster TXT file loading. (thanks @ricobl) r125 212 |
  • Added tooltips (help) for all controls. r165 213 |
  • Added favicon. (by @xupisco) r340 214 |
  • Set the page TITLE to full app name. (thanks @xupisco) r341 215 |
  • Hide the totals rows if there's only one row in the report. r460 216 |
217 | 218 |

Config

219 | 220 | 228 | 229 |

Widgets

230 | 231 |
    232 |
  • Added Widget support. See sample in sample/widget-nerd-toy.js. r505 233 |
  • Added three new "hello world" sample Widgets. r513 234 |
235 | 236 |

Test Suite

237 | 238 |
    239 |
  • New test-suite test/functions.js. Instructions at file header. r549 240 |

    241 |
242 | 243 | 244 | 245 | 246 | --------------------------------------------------------------------------------