statosio 0.3.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +8 -0
- data/.ipynb_checkpoints/[Ruby] statosio to svg-checkpoint.ipynb +1396 -0
- data/.rubocop.yml +13 -0
- data/CHANGELOG.md +5 -0
- data/CODE_OF_CONDUCT.md +84 -0
- data/Gemfile +10 -0
- data/LICENSE.txt +21 -0
- data/README.md +182 -0
- data/Rakefile +8 -0
- data/[Ruby] statosio to svg.ipynb +1459 -0
- data/assets/statosio.rb.png +0 -0
- data/bin/console +15 -0
- data/bin/setup +8 -0
- data/jupyter/0-templates/d3.js +19542 -0
- data/jupyter/0-templates/dataset.json +3 -0
- data/jupyter/0-templates/html.txt +30 -0
- data/jupyter/0-templates/ruby.txt +140 -0
- data/jupyter/0-templates/statosio.js +854 -0
- data/jupyter/1-test/boilerplate-test.html +20547 -0
- data/jupyter/1-test/module-test-generate-svg.pdf +1179 -1
- data/jupyter/1-test/module-test-generate.pdf +1179 -1
- data/jupyter/1-test/module-test-html.html +20547 -0
- data/jupyter/2-examples/0-example1.svg +1 -0
- data/jupyter/2-examples/1-example2.pdf +1126 -0
- data/lib/boilerplate.rb +20563 -0
- data/lib/statosio.rb +206 -0
- data/lib/statosio/version.rb +5 -0
- data/statosio.gemspec +40 -0
- metadata +104 -0
@@ -0,0 +1,3 @@
|
|
1
|
+
{
|
2
|
+
"data": [ { "name": "Neelix", "mobile": "25", "desktop": "50" }, { "name": "Data", "mobile": "51", "desktop": "87" }, { "name": "Jake Sisko", "mobile": "49", "desktop": "78" }, { "name": "Spock", "mobile": "7", "desktop": "60" }, { "name": "Montgomery Scott", "mobile": "11", "desktop": "35" }, { "name": "Nyota Uhuru", "mobile": "n/a", "desktop": "n/a" }, { "name": "Chakotay", "mobile": "24", "desktop": "77" }, { "name": "Beverly Crusher", "mobile": "13", "desktop": "45" }, { "name": "Hikaru Sulu", "mobile": "41", "desktop": "82" }, { "name": "William T. Riker", "mobile": "48", "desktop": "97" }, { "name": "Natasha Yar", "mobile": "11", "desktop": "60" }, { "name": "Kathryn Janeway", "mobile": "48", "desktop": "89" }, { "name": "Deanna Troi", "mobile": "33", "desktop": "39" }, { "name": "Quark", "mobile": "47", "desktop": "64" }, { "name": "Benjamin Sisko", "mobile": "n/a", "desktop": "n/a" }, { "name": "T'Pol", "mobile": "30", "desktop": "45" }, { "name": "Tuvok", "mobile": "17", "desktop": "50" }, { "name": "Worf", "mobile": "26", "desktop": "41" }, { "name": "B'Elanna Torres", "mobile": "10", "desktop": "63" }, { "name": "Malcolm Reed", "mobile": "14", "desktop": "55" }, { "name": "Kira Nerys", "mobile": "51", "desktop": "70" }, { "name": "Tom Paris", "mobile": "42", "desktop": "69" }, { "name": "Charles Tucker III", "mobile": "46", "desktop": "74" }, { "name": "Jonathan Archer", "mobile": "22", "desktop": "49" } ]
|
3
|
+
}
|
@@ -0,0 +1,30 @@
|
|
1
|
+
<!DOCTYPE html>
|
2
|
+
<head>
|
3
|
+
<meta content="text/html;charset=utf-8" http-equiv="Content-Type">
|
4
|
+
<meta content="utf-8" http-equiv="encoding">
|
5
|
+
<style>
|
6
|
+
body {background-color: green}
|
7
|
+
* {
|
8
|
+
margin: 0;
|
9
|
+
padding: 0;
|
10
|
+
}
|
11
|
+
</style>
|
12
|
+
<script>
|
13
|
+
<--d3-->
|
14
|
+
</script>
|
15
|
+
<script>
|
16
|
+
<--statosio-->
|
17
|
+
</script>
|
18
|
+
<script>var dataset=<--dataset-->
|
19
|
+
</script>
|
20
|
+
</head>
|
21
|
+
<body>
|
22
|
+
<script>
|
23
|
+
d3.statosio(
|
24
|
+
dataset['data'],
|
25
|
+
'<--x-->',
|
26
|
+
<--y-->,
|
27
|
+
<--options-->
|
28
|
+
)
|
29
|
+
</script>
|
30
|
+
</body>
|
@@ -0,0 +1,140 @@
|
|
1
|
+
class Boilerplate
|
2
|
+
def initialize
|
3
|
+
@values = nil
|
4
|
+
@markers = {
|
5
|
+
dataset: {
|
6
|
+
sub: "<--dataset-->",
|
7
|
+
value: nil
|
8
|
+
},
|
9
|
+
x: {
|
10
|
+
sub: "<--x-->",
|
11
|
+
value: nil
|
12
|
+
},
|
13
|
+
y: {
|
14
|
+
sub: "<--y-->",
|
15
|
+
value: nil
|
16
|
+
},
|
17
|
+
options: {
|
18
|
+
sub: '<--options-->',
|
19
|
+
value: {}
|
20
|
+
}
|
21
|
+
}
|
22
|
+
@boilerplate = nil
|
23
|
+
@boilerplate_raw = <<'STATOSIOOOO'
|
24
|
+
<<--boilerplate-->>
|
25
|
+
STATOSIOOOO
|
26
|
+
@options_allow_list = set_options_allow_list()
|
27
|
+
end
|
28
|
+
|
29
|
+
|
30
|
+
def get_boilerplate_raw
|
31
|
+
@boilerplate_raw
|
32
|
+
end
|
33
|
+
|
34
|
+
|
35
|
+
def set_boilerplate
|
36
|
+
@boilerplate = @boilerplate_raw.clone
|
37
|
+
@markers.keys.each do | key |
|
38
|
+
case key
|
39
|
+
when :dataset
|
40
|
+
tmp = { data: @markers[ key ][:value] }
|
41
|
+
value = JSON.pretty_generate( tmp )
|
42
|
+
when :x
|
43
|
+
value = @markers[ key ][:value].to_s
|
44
|
+
when :y
|
45
|
+
value = @markers[ key ][:value].to_s
|
46
|
+
when :options
|
47
|
+
value = @markers[ key ][:value].to_json
|
48
|
+
end
|
49
|
+
|
50
|
+
@boilerplate.gsub!( @markers[ key ][:sub], value )
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
|
55
|
+
def get_boilerplate
|
56
|
+
@boilerplate
|
57
|
+
end
|
58
|
+
|
59
|
+
|
60
|
+
def get_markers
|
61
|
+
@markers
|
62
|
+
end
|
63
|
+
|
64
|
+
|
65
|
+
def set_markers_value( _values )
|
66
|
+
_values.keys.each do | key |
|
67
|
+
@markers[ key ][:value] = _values[ key ]
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
|
72
|
+
def set_options_allow_list
|
73
|
+
g = {
|
74
|
+
start: 'let default_values = ',
|
75
|
+
end: 'const params_create'
|
76
|
+
}
|
77
|
+
|
78
|
+
html = @boilerplate_raw
|
79
|
+
tmp = html[ html.index( g[:start] ) + g[:start].length, html.length ]
|
80
|
+
tmp = tmp[ 0, tmp.index( g[:end] ) ]
|
81
|
+
tmp = tmp
|
82
|
+
.strip!
|
83
|
+
.gsub( "'", '"' )
|
84
|
+
.split( "\n" )
|
85
|
+
.map { | ss | ss.include?( '//' ) ? ss[ 0, ss.index( '//' )] : ss }
|
86
|
+
.join()
|
87
|
+
options = JSON.parse( tmp )
|
88
|
+
|
89
|
+
keys = {
|
90
|
+
alias: [],
|
91
|
+
camel_case: [],
|
92
|
+
allow_list: []
|
93
|
+
}
|
94
|
+
|
95
|
+
options.keys.each do | lvl1 |
|
96
|
+
if options[ lvl1 ].class.to_s == 'Hash'
|
97
|
+
options[ lvl1 ].keys.each do | lvl2 |
|
98
|
+
if options[ lvl1 ][ lvl2 ].class.to_s == 'Hash'
|
99
|
+
options[ lvl1 ][ lvl2 ].keys.each do | lvl3 |
|
100
|
+
if options[ lvl1 ][ lvl2 ][ lvl3 ].class.to_s == 'Hash'
|
101
|
+
options[ lvl1 ][ lvl2 ][ lvl3 ].keys.each do | lvl4 |
|
102
|
+
if !options[ lvl1 ][ lvl2 ][ lvl3 ][ lvl4 ].nil?
|
103
|
+
keys[:alias].push( lvl1.to_s + '__' + lvl2.to_s + '__' + lvl3.to_s + '__' + lvl4.to_s )
|
104
|
+
end
|
105
|
+
end
|
106
|
+
else
|
107
|
+
if !options[ lvl1 ][ lvl2 ][ lvl3 ].nil?
|
108
|
+
keys[:alias].push( lvl1.to_s + '__' + lvl2.to_s + '__' + lvl3.to_s )
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
else
|
113
|
+
if !options[ lvl1 ][ lvl2 ].nil?
|
114
|
+
keys[:alias].push( lvl1.to_s + '__' + lvl2.to_s )
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
118
|
+
else
|
119
|
+
if !options[ lvl1 ].nil?
|
120
|
+
keys[:alias].push( lvl1.to_s )
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
keys[:alias].each do | key |
|
126
|
+
tmp = key
|
127
|
+
.gsub('__', '_')
|
128
|
+
.split('_')
|
129
|
+
one = tmp[ 0 ]
|
130
|
+
two = tmp
|
131
|
+
.drop(1)
|
132
|
+
.map { | d | d[ 0, 1 ].upcase + d[ 1, d.length] }
|
133
|
+
.join()
|
134
|
+
keys[:camel_case].push( one + two )
|
135
|
+
end
|
136
|
+
|
137
|
+
keys[:allow_list] = keys[:alias].concat( keys[:camel_case] )
|
138
|
+
return keys[:allow_list]
|
139
|
+
end
|
140
|
+
end
|
@@ -0,0 +1,854 @@
|
|
1
|
+
// https://statosio.com v0.1 Copyright 2020 Andreas Banholzer
|
2
|
+
|
3
|
+
d3.statosio = ( file, x_key, y_keys, optional={} ) => {
|
4
|
+
let default_values = {
|
5
|
+
'show': {
|
6
|
+
'title': false,
|
7
|
+
'legend': false,
|
8
|
+
'average': true,
|
9
|
+
'range_y_log': false,
|
10
|
+
'data_as_circle': false
|
11
|
+
},
|
12
|
+
'view': {
|
13
|
+
'title': 'Statosio Demo',
|
14
|
+
'dom_id': 'd3_statosio',
|
15
|
+
'margin': {
|
16
|
+
'top': 20,
|
17
|
+
'right': 40,
|
18
|
+
'bottom': 100,
|
19
|
+
'left': 60
|
20
|
+
},
|
21
|
+
'width': {
|
22
|
+
'outer': 600,
|
23
|
+
'inner': null
|
24
|
+
},
|
25
|
+
'height': {
|
26
|
+
'outer': 300,
|
27
|
+
'inner': null
|
28
|
+
},
|
29
|
+
'translate': {
|
30
|
+
'multiplicator': 1.5,
|
31
|
+
'title': null,
|
32
|
+
'legend': null,
|
33
|
+
'title_and_legend': null,
|
34
|
+
'top_global': null,
|
35
|
+
'legend_text': null
|
36
|
+
}
|
37
|
+
},
|
38
|
+
'data': {
|
39
|
+
'x': {
|
40
|
+
'key': '',
|
41
|
+
'selectors': [],
|
42
|
+
'text': {
|
43
|
+
'length': 25,
|
44
|
+
'placeholder': '...'
|
45
|
+
}
|
46
|
+
},
|
47
|
+
'y': {
|
48
|
+
'keys': [],
|
49
|
+
'ticks': 5
|
50
|
+
},
|
51
|
+
'sort': {
|
52
|
+
'current': 'none', // none, values, names
|
53
|
+
'by': {
|
54
|
+
'values': 'decending', // none , ascending, decending
|
55
|
+
'names': 'ascending' // none , ascending, decending
|
56
|
+
},
|
57
|
+
'selection': 'none' // none, start, end
|
58
|
+
},
|
59
|
+
'legend': {
|
60
|
+
'names': {
|
61
|
+
'keys': null,
|
62
|
+
'titleized': null
|
63
|
+
},
|
64
|
+
'text': {
|
65
|
+
'separater': '_'
|
66
|
+
}
|
67
|
+
}
|
68
|
+
},
|
69
|
+
'style': {
|
70
|
+
'color': {
|
71
|
+
'average': '#000000',
|
72
|
+
'canvas_background': 'white',
|
73
|
+
'font': '#000000',
|
74
|
+
'legends': [ '#5186EC','#D95040', '#F2BD42' ],
|
75
|
+
'gridline': '#E5E5E5',
|
76
|
+
'selectors': {
|
77
|
+
'chart': [ '#EE752F', '#5186EC' ],
|
78
|
+
'text': [ '#000000', '#000000' ]
|
79
|
+
}
|
80
|
+
},
|
81
|
+
'font': {
|
82
|
+
'size': {
|
83
|
+
'text': 10,
|
84
|
+
'title': 18
|
85
|
+
},
|
86
|
+
'family': 'arial',
|
87
|
+
'format': {
|
88
|
+
'selectors': [ 'bold', 'normal' ]
|
89
|
+
}
|
90
|
+
},
|
91
|
+
'stroke': {
|
92
|
+
'average': 2,
|
93
|
+
'gridline': 2
|
94
|
+
},
|
95
|
+
'legend' : {
|
96
|
+
'rect_size': {
|
97
|
+
'full': 16,
|
98
|
+
'half': null
|
99
|
+
},
|
100
|
+
'padding': {
|
101
|
+
'before': null,
|
102
|
+
'after': null
|
103
|
+
}
|
104
|
+
},
|
105
|
+
'other': {
|
106
|
+
'legend_padding_text_after': null,
|
107
|
+
'legend_padding_text_before': null,
|
108
|
+
'circle_chart_radius': 4,
|
109
|
+
'range_x_text_rotation': -45
|
110
|
+
}
|
111
|
+
}
|
112
|
+
}
|
113
|
+
|
114
|
+
|
115
|
+
const params_create = ( _default_values, _user_x_key, _user_y_keys, _user_optional ) => {
|
116
|
+
const prepare_params_access_list = ( obj, _user_x_key, _user_y_keys ) => {
|
117
|
+
const key_finding = ( obj, keys ) => {
|
118
|
+
let val = ''
|
119
|
+
let str = keys.join( '__' )
|
120
|
+
allow = false
|
121
|
+
switch( keys.length) {
|
122
|
+
case 1:
|
123
|
+
val = obj[ keys[ 0 ] ]
|
124
|
+
break;
|
125
|
+
case 2:
|
126
|
+
val = obj[ keys[ 0 ] ][ keys[ 1 ] ]
|
127
|
+
break;
|
128
|
+
case 3:
|
129
|
+
val = obj[ keys[ 0 ] ][ keys[ 1 ] ][ keys[ 2 ] ]
|
130
|
+
break;
|
131
|
+
case 4:
|
132
|
+
val = obj[ keys[ 0 ] ][ keys[ 1 ] ][ keys[ 2 ] ][ keys[ 3 ] ]
|
133
|
+
break;
|
134
|
+
}
|
135
|
+
|
136
|
+
val === null ? '' : allow = true
|
137
|
+
allow ? access['allow'].push( str ) : access['block'].push( str )
|
138
|
+
return allow
|
139
|
+
}
|
140
|
+
|
141
|
+
|
142
|
+
const detect_finding = ( obj, keys ) => {
|
143
|
+
const is_array = ( _val ) => {
|
144
|
+
return is_object( _val ) && ( _val instanceof Array )
|
145
|
+
}
|
146
|
+
|
147
|
+
const is_object = ( _val ) => {
|
148
|
+
return _val && (typeof _val === 'object' )
|
149
|
+
}
|
150
|
+
|
151
|
+
let val = null
|
152
|
+
switch( keys.length ) {
|
153
|
+
case 1:
|
154
|
+
val = obj[ keys[ 0 ] ]
|
155
|
+
break;
|
156
|
+
case 2:
|
157
|
+
val = obj[ keys[ 0 ] ][ keys[ 1 ] ]
|
158
|
+
break;
|
159
|
+
case 3:
|
160
|
+
val = obj[ keys[ 0 ] ][ keys[ 1 ] ][ keys[ 2 ] ]
|
161
|
+
break;
|
162
|
+
case 4:
|
163
|
+
val = obj[ keys[ 0 ] ][ keys[ 1 ] ][ keys[ 2 ] ][ keys[ 3 ] ]
|
164
|
+
break;
|
165
|
+
}
|
166
|
+
|
167
|
+
let result = {
|
168
|
+
'null': null,
|
169
|
+
'string': null,
|
170
|
+
'array': null,
|
171
|
+
'object': null,
|
172
|
+
}
|
173
|
+
|
174
|
+
val !== null ? result['null'] = true : result['null'] = false
|
175
|
+
typeof( val ) === 'string' ? result['string'] = true : result['string'] = false
|
176
|
+
|
177
|
+
if ( is_array( val ) ) {
|
178
|
+
result['object'] = false
|
179
|
+
}
|
180
|
+
else if ( is_object( val ) ) {
|
181
|
+
result['object'] = true
|
182
|
+
}
|
183
|
+
|
184
|
+
is_array( val ) ? result['array'] = true : result['array'] = false
|
185
|
+
|
186
|
+
return result
|
187
|
+
}
|
188
|
+
|
189
|
+
|
190
|
+
let access = { 'allow': [], 'block': [] }
|
191
|
+
let required = { 'data__x__key': _user_x_key, 'data__y__keys': _user_y_keys }
|
192
|
+
let alias = {}
|
193
|
+
|
194
|
+
Object.keys( obj ).forEach( ( key1 ) => {
|
195
|
+
let ks1 = [ key1 ]
|
196
|
+
let r = detect_finding( obj, ks1 )
|
197
|
+
|
198
|
+
if( r['object'] ) {
|
199
|
+
Object.keys( obj[ key1 ] ).forEach( ( key2 ) => {
|
200
|
+
let ks2 = [ key1, key2 ]
|
201
|
+
let r = detect_finding( obj, ks2 )
|
202
|
+
r['array'] ? key_finding( obj, ks2 ) : ''
|
203
|
+
|
204
|
+
if( r['object'] ) {
|
205
|
+
Object.keys( obj[ key1 ][ key2 ] ).forEach( ( key3 ) => {
|
206
|
+
let ks3 = [ key1, key2, key3 ]
|
207
|
+
let r = detect_finding( obj, ks3 )
|
208
|
+
r['array'] ? key_finding( obj, ks3 ) : ''
|
209
|
+
|
210
|
+
if( r['object'] ) {
|
211
|
+
Object.keys( obj[ key1 ][ key2 ][ key3 ] ).forEach( ( key4 ) => {
|
212
|
+
let ks4 = [ key1, key2, key3, key4 ]
|
213
|
+
let r = detect_finding( obj, ks4 )
|
214
|
+
r['array'] ? key_finding( obj, ks4 ) : ''
|
215
|
+
|
216
|
+
if( r['object'] ) {
|
217
|
+
} else {
|
218
|
+
key_finding( obj, ks4 )
|
219
|
+
}
|
220
|
+
} )
|
221
|
+
} else {
|
222
|
+
key_finding( obj, ks3 )
|
223
|
+
}
|
224
|
+
} )
|
225
|
+
} else {
|
226
|
+
key_finding( obj, ks2 )
|
227
|
+
}
|
228
|
+
} )
|
229
|
+
} else {
|
230
|
+
key_finding( obj, ks1 )
|
231
|
+
}
|
232
|
+
} )
|
233
|
+
|
234
|
+
|
235
|
+
Object.keys( access ).forEach( ( key1 ) => {
|
236
|
+
access[ key1 ].forEach( ( key2 ) => {
|
237
|
+
alias[ key2 ] = {}
|
238
|
+
alias[ key2 ]['type'] = key1 === 'allow' ? true : false
|
239
|
+
alias[ key2 ]['names'] = []
|
240
|
+
|
241
|
+
let tmp = key2
|
242
|
+
.split( '__' ).join( '---' )
|
243
|
+
.split( '_' ).join( '---' )
|
244
|
+
.split( '---' )
|
245
|
+
|
246
|
+
alias[ key2 ]['names'].push( key2 )
|
247
|
+
|
248
|
+
let type2 = []
|
249
|
+
tmp.forEach( ( key2, i ) => {
|
250
|
+
let word = ''
|
251
|
+
if( i != 0 ) {
|
252
|
+
word += key2.charAt( 0 ).toUpperCase()
|
253
|
+
word += key2.slice( 1 ).toLowerCase()
|
254
|
+
} else {
|
255
|
+
word = key2
|
256
|
+
}
|
257
|
+
type2.push( word )
|
258
|
+
} )
|
259
|
+
alias[ key2 ]['names'].push( type2.join( '' ) )
|
260
|
+
} )
|
261
|
+
} )
|
262
|
+
|
263
|
+
result = {
|
264
|
+
'required': required,
|
265
|
+
'access': access,
|
266
|
+
'alias': alias
|
267
|
+
}
|
268
|
+
return result
|
269
|
+
}
|
270
|
+
|
271
|
+
|
272
|
+
const prepare_params_insert_user_input = ( obj, _access_list, _user_optional ) => {
|
273
|
+
const obj_insert_value = ( obj, _key_joined, insert ) => {
|
274
|
+
let keys = _key_joined.split( '__' )
|
275
|
+
switch( keys.length ) {
|
276
|
+
case 1:
|
277
|
+
obj[ keys[ 0 ] ] = insert
|
278
|
+
break;
|
279
|
+
case 2:
|
280
|
+
obj[ keys[ 0 ] ][ keys[ 1 ] ] = insert
|
281
|
+
break;
|
282
|
+
case 3:
|
283
|
+
obj[ keys[ 0 ] ][ keys[ 1 ] ][ keys[ 2 ] ] = insert
|
284
|
+
break;
|
285
|
+
case 4:
|
286
|
+
obj[ keys[ 0 ] ][ keys[ 1 ] ][ keys[ 2 ] ][ keys[ 3 ] ] = insert
|
287
|
+
break;
|
288
|
+
}
|
289
|
+
return obj
|
290
|
+
}
|
291
|
+
|
292
|
+
const required_clean_up = ( _required, key ) => {
|
293
|
+
if( key === 'data__y__keys' ) {
|
294
|
+
if( typeof _required[ key ] === 'string' ) {
|
295
|
+
if( _required[ key ].indexOf( ',' ) == -1 ) {
|
296
|
+
_required[ key ] = [ _required[ key ] ]
|
297
|
+
} else {
|
298
|
+
_required[ key ] = _required[ key ].split( ',' ).map( ( value ) => { return value.trim() } )
|
299
|
+
}
|
300
|
+
}
|
301
|
+
}
|
302
|
+
return _required
|
303
|
+
}
|
304
|
+
|
305
|
+
|
306
|
+
Object.keys( _access_list['required'] )
|
307
|
+
.forEach( ( key ) => {
|
308
|
+
_access_list['required'] = required_clean_up( _access_list['required'], key )
|
309
|
+
key == null ? console.log( 'Key "' + key + '" is empty!' ) : ''
|
310
|
+
obj = obj_insert_value( obj, key, _access_list['required'][ key ] )
|
311
|
+
} )
|
312
|
+
|
313
|
+
Object.keys( _user_optional )
|
314
|
+
.forEach( ( key1 ) => {
|
315
|
+
let search = key1
|
316
|
+
let found = {
|
317
|
+
'key' : null,
|
318
|
+
'add' : false,
|
319
|
+
}
|
320
|
+
|
321
|
+
Object.keys( _access_list['alias'] ).forEach( ( key2 ) => {
|
322
|
+
if( _access_list['alias'][ key2 ]['names'].includes( search ) ) {
|
323
|
+
if( _access_list['alias'][ key2 ]['type'] ) {
|
324
|
+
found['key'] = _access_list['alias'][ key2 ]['names'][ 0 ] //key2
|
325
|
+
found['add'] = true
|
326
|
+
}
|
327
|
+
}
|
328
|
+
} )
|
329
|
+
|
330
|
+
if( found['add'] ) {
|
331
|
+
insert = _user_optional[ search ]
|
332
|
+
obj = obj_insert_value( obj, found['key'], insert )
|
333
|
+
}
|
334
|
+
} )
|
335
|
+
return obj
|
336
|
+
}
|
337
|
+
|
338
|
+
|
339
|
+
const prepare_params_adjust_margin_top = ( obj, multiplicator ) => {
|
340
|
+
let struct = {
|
341
|
+
'margin_top': null,
|
342
|
+
'title': {
|
343
|
+
'translate': null,
|
344
|
+
'offset': null,
|
345
|
+
'show': null
|
346
|
+
},
|
347
|
+
'legend': {
|
348
|
+
'translate': null,
|
349
|
+
'offset': null,
|
350
|
+
'show': null
|
351
|
+
}
|
352
|
+
}
|
353
|
+
|
354
|
+
let iteration = Object.keys( struct )
|
355
|
+
.filter( ( key ) => { return !key.includes( 'margin_top' ) } )
|
356
|
+
|
357
|
+
struct['title']['offset'] = obj['style']['font']['size']['title'] * multiplicator
|
358
|
+
struct['title']['show'] = obj['show']['title']
|
359
|
+
|
360
|
+
struct['legend']['offset'] = obj['style']['font']['size']['text'] * multiplicator
|
361
|
+
struct['legend']['show'] = obj['show']['legend']
|
362
|
+
|
363
|
+
struct['margin_top'] = obj['view']['margin']['top'] * 1
|
364
|
+
struct['margin_top'] += iteration
|
365
|
+
.map( ( key ) => { return struct[ key ]['show'] ? struct[ key ]['offset'] : 0 } )
|
366
|
+
.reduce( ( a, b ) => { return a + b } )
|
367
|
+
|
368
|
+
iteration
|
369
|
+
.forEach( ( key ) => { return struct[ key ]['translate'] = -( struct['margin_top'] - struct[ key ]['offset'] ) } )
|
370
|
+
|
371
|
+
result = {
|
372
|
+
'canvas__margin__top': struct['margin_top'],
|
373
|
+
'canvas__translate__title': struct['title']['translate'],
|
374
|
+
'canvas__translate__legend': struct['legend']['translate']
|
375
|
+
}
|
376
|
+
return result
|
377
|
+
}
|
378
|
+
|
379
|
+
|
380
|
+
const helper_string_titleize = ( phrase, separater ) => {
|
381
|
+
result = phrase.split( separater )
|
382
|
+
.map( ( string ) => {
|
383
|
+
let word = ''
|
384
|
+
word += string.charAt( 0 ).toUpperCase()
|
385
|
+
word += string.slice( 1 ).toLowerCase()
|
386
|
+
return word
|
387
|
+
} )
|
388
|
+
.join( ' ' )
|
389
|
+
return result
|
390
|
+
}
|
391
|
+
|
392
|
+
|
393
|
+
const prepare_params_offset_sum = ( obj ) => {
|
394
|
+
const font_width = ( font_size_text, font_family ) => {
|
395
|
+
let dom = document.createElement( 'div' )
|
396
|
+
dom.style.position = 'absolute'
|
397
|
+
dom.style.float = 'left'
|
398
|
+
dom.style.whiteSpace = 'nowrap'
|
399
|
+
dom.style.visibility = 'hidden'
|
400
|
+
dom.style.font = font_size_text + 'px ' + font_family
|
401
|
+
dom.innerHTML = this
|
402
|
+
dom = document.body.appendChild( dom )
|
403
|
+
let _font_width = dom.offsetWidth
|
404
|
+
dom.parentNode.removeChild( dom )
|
405
|
+
return _font_width
|
406
|
+
}
|
407
|
+
|
408
|
+
|
409
|
+
let legend_offset_tmp = obj['data']['legend']['names']['titleized']
|
410
|
+
.map( ( d ) => { return font_width( obj['style']['font']['size']['text'], obj['style']['font']['family'] ) } )
|
411
|
+
.map( ( d ) => { return obj['style']['legend']['rect_size']['full'] + obj['style']['legend']['rect_size']['half'] + d + obj['style']['legend']['padding']['after'] } )
|
412
|
+
legend_offset_tmp.unshift( 0 )
|
413
|
+
|
414
|
+
let result = []
|
415
|
+
result.push( 0 )
|
416
|
+
legend_offset_tmp.forEach( ( v ) => { result.push( result[ result.length - 1 ] + v ) } )
|
417
|
+
result.shift( 0 )
|
418
|
+
|
419
|
+
return result
|
420
|
+
}
|
421
|
+
|
422
|
+
|
423
|
+
let hash = null
|
424
|
+
hash = _default_values
|
425
|
+
let access = prepare_params_access_list( hash, _user_x_key, _user_y_keys )
|
426
|
+
hash = prepare_params_insert_user_input( hash, access, _user_optional )
|
427
|
+
let tmp = prepare_params_adjust_margin_top( hash, hash['view']['translate']['multiplicator'] )
|
428
|
+
|
429
|
+
hash['view']['margin']['top'] = tmp['canvas__margin__top']
|
430
|
+
hash['view']['translate']['title'] = tmp['canvas__translate__title']
|
431
|
+
hash['view']['translate']['legend'] = tmp['canvas__translate__legend']
|
432
|
+
|
433
|
+
hash['view']['width']['inner'] = hash['view']['width']['outer'] - hash['view']['margin']['left'] - hash['view']['margin']['right']
|
434
|
+
hash['view']['height']['inner'] = hash['view']['height']['outer'] - hash['view']['margin']['top'] - hash['view']['margin']['bottom']
|
435
|
+
|
436
|
+
hash['style']['legend']['rect_size']['half'] = Math.floor( hash['style']['legend']['rect_size']['full'] / 2 )
|
437
|
+
hash['view']['translate']['legend_text'] = Math.floor( ( hash['style']['legend']['rect_size']['full'] / 100 ) * 80 )
|
438
|
+
|
439
|
+
hash['style']['legend']['padding']['before'] = hash['style']['legend']['rect_size']['half']
|
440
|
+
hash['style']['legend']['padding']['after'] = hash['style']['legend']['rect_size']['full']
|
441
|
+
|
442
|
+
hash['data']['legend']['names']['keys'] = hash['data']['y']['keys']
|
443
|
+
hash['data']['legend']['names']['titleized'] = hash['data']['legend']['names']['keys'].map( ( key ) => {
|
444
|
+
return helper_string_titleize( key, hash['data']['legend']['text']['separater'] )
|
445
|
+
} )
|
446
|
+
|
447
|
+
hash['view']['translate']['title_and_legend'] = prepare_params_offset_sum( hash )
|
448
|
+
hash['view']['translate']['top_global'] = Math.floor( (
|
449
|
+
hash['view']['width']['inner'] -
|
450
|
+
hash['view']['translate']['title_and_legend'][ hash['view']['translate']['title_and_legend'].length - 1 ]
|
451
|
+
) / 2 )
|
452
|
+
|
453
|
+
return hash
|
454
|
+
}
|
455
|
+
|
456
|
+
|
457
|
+
const data_create = ( file, obj ) => {
|
458
|
+
const prepare_data_points = ( file, obj, _data ) => {
|
459
|
+
const helper_cast_to_number = ( value ) => {
|
460
|
+
if( value === 'true' ) {
|
461
|
+
value = 1
|
462
|
+
} else if( value === true ) {
|
463
|
+
value = 1
|
464
|
+
} else if( value === 'false' ) {
|
465
|
+
value = 0
|
466
|
+
} else if( value === false ) {
|
467
|
+
value = 0
|
468
|
+
}
|
469
|
+
|
470
|
+
let r = parseInt( value )
|
471
|
+
r = Number.isNaN( r ) ? 0 : r
|
472
|
+
return r
|
473
|
+
}
|
474
|
+
|
475
|
+
let results = file
|
476
|
+
.map( ( d, i ) => {
|
477
|
+
let result = {
|
478
|
+
'name_full': null,
|
479
|
+
'name_trimmed': null,
|
480
|
+
'color_chart': null,
|
481
|
+
'color_text': null,
|
482
|
+
'font_format': null,
|
483
|
+
'value': null,
|
484
|
+
'average': null
|
485
|
+
}
|
486
|
+
|
487
|
+
if( d[ obj['data']['x']['key'] ].length > obj['data']['x']['text']['length'] ) {
|
488
|
+
result['name_full'] = d[ obj['data']['x']['key'] ]
|
489
|
+
result['name_trimmed'] = result['name_full']
|
490
|
+
.substring( 0, obj['data']['x']['text']['length'] ) + obj['data']['x']['text']['placeholder']
|
491
|
+
} else {
|
492
|
+
result['name_full'] = d[ obj['data']['x']['key'] ]
|
493
|
+
result['name_trimmed'] = result['name_full']
|
494
|
+
}
|
495
|
+
|
496
|
+
switch( _data['type'] ) {
|
497
|
+
case 'stacked':
|
498
|
+
let values = []
|
499
|
+
obj['data']['y']['keys'].forEach( ( key ) => {
|
500
|
+
result[ key ] = helper_cast_to_number( d[ key ] )
|
501
|
+
values.push( result[ key ] )
|
502
|
+
} )
|
503
|
+
result['value'] = values.reduce( ( a, b ) => { return a + b } )
|
504
|
+
|
505
|
+
break;
|
506
|
+
case 'single':
|
507
|
+
result['value'] = helper_cast_to_number( d[ obj['data']['y']['keys'][ 0 ] ] )
|
508
|
+
break;
|
509
|
+
}
|
510
|
+
|
511
|
+
result['color_chart'] = obj['data']['x']['selectors']
|
512
|
+
.includes( d[ obj['data']['x']['key'] ] ) ? obj['style']['color']['selectors']['chart'][ 0 ] : obj['style']['color']['selectors']['chart'][ 1 ]
|
513
|
+
result['color_text'] = obj['data']['x']['selectors']
|
514
|
+
.includes( d[ obj['data']['x']['key'] ] ) ? obj['style']['color']['selectors']['text'][ 0 ] : obj['style']['color']['selectors']['text'][ 1 ]
|
515
|
+
result['font_format'] = obj['data']['x']['selectors']
|
516
|
+
.includes( d[ obj['data']['x']['key'] ] ) ? obj['style']['font']['format']['selectors'][ 0 ] : obj['style']['font']['format']['selectors'][ 1 ]
|
517
|
+
return result
|
518
|
+
} )
|
519
|
+
return results
|
520
|
+
}
|
521
|
+
|
522
|
+
|
523
|
+
const prepare_data_points_sort = ( obj, _data ) => {
|
524
|
+
if( obj['data']['sort']['current'] !== 'none' && [ 'values', 'names' ].includes( obj['data']['sort']['current'] ) ) {
|
525
|
+
switch( obj['data']['sort']['current'] ) {
|
526
|
+
case 'values':
|
527
|
+
let a = obj['data']['sort']['by']['values'] !== 'none'
|
528
|
+
let b = [ 'ascending', 'decending' ].includes( obj['data']['sort']['by']['values'] )
|
529
|
+
if( a && b ) {
|
530
|
+
_data.sort( ( d, e ) => { return parseFloat( d['value'] ) - parseFloat( e['value'] ) } )
|
531
|
+
switch( obj['data']['sort']['by']['values'] ) {
|
532
|
+
case 'ascending':
|
533
|
+
break;
|
534
|
+
case 'decending':
|
535
|
+
_data.reverse()
|
536
|
+
break;
|
537
|
+
}
|
538
|
+
}
|
539
|
+
break;
|
540
|
+
case 'names':
|
541
|
+
let g = obj['data']['sort']['by']['names'] !== 'none'
|
542
|
+
let h = [ 'ascending', 'decending' ].includes( obj['data']['sort']['by']['names'] )
|
543
|
+
if( g && h ) {
|
544
|
+
_data.sort( ( a, b ) => { return a['name_full'].localeCompare( b['name_full'] ) } )
|
545
|
+
switch( obj['data']['sort']['by']['names'] ) {
|
546
|
+
case 'ascending':
|
547
|
+
break;
|
548
|
+
case 'decending':
|
549
|
+
_data.reverse()
|
550
|
+
break;
|
551
|
+
}
|
552
|
+
}
|
553
|
+
break;
|
554
|
+
}
|
555
|
+
}
|
556
|
+
|
557
|
+
if( obj['data']['sort']['selection'] !== 'none' && [ 'start', 'end' ].includes( obj['data']['sort']['selection'] ) ) {
|
558
|
+
let selection = []
|
559
|
+
let rest = []
|
560
|
+
let searchs = obj['data']['x']['selectors']
|
561
|
+
searchs.forEach( ( search ) => {
|
562
|
+
let index = _data.findIndex( ( d ) => { return d['name_full'] === search } )
|
563
|
+
let item = _data[ index ]
|
564
|
+
selection.push( item )
|
565
|
+
_data.forEach( ( d ) => {
|
566
|
+
!searchs.includes( d['name_full'] ) ? rest.push( d ) : ''
|
567
|
+
} )
|
568
|
+
} )
|
569
|
+
|
570
|
+
switch( obj['data']['sort']['selection'] ) {
|
571
|
+
case 'start':
|
572
|
+
_data = selection.concat( rest )
|
573
|
+
break;
|
574
|
+
case 'end':
|
575
|
+
_data = rest.concat( selection )
|
576
|
+
break
|
577
|
+
}
|
578
|
+
}
|
579
|
+
return _data
|
580
|
+
}
|
581
|
+
|
582
|
+
|
583
|
+
const prepare_data_average = ( _data, obj ) => {
|
584
|
+
let result = null
|
585
|
+
if( obj['show']['average'] ) {
|
586
|
+
let average_tmp = _data['points'].map( ( d ) => {
|
587
|
+
let result = null
|
588
|
+
switch( _data['type'] ) {
|
589
|
+
case 'stacked':
|
590
|
+
result = obj['data']['y']['keys']
|
591
|
+
.map( ( key ) => { return d[ key ] } )
|
592
|
+
.reduce( ( a, b ) => { return a + b } )
|
593
|
+
break;
|
594
|
+
case 'single' :
|
595
|
+
result = d['value']
|
596
|
+
break;
|
597
|
+
}
|
598
|
+
return result
|
599
|
+
} )
|
600
|
+
|
601
|
+
result = average_tmp.reduce( ( a, b ) => { return a + b } ) / average_tmp.length
|
602
|
+
result = Math.round( ( result + Number.EPSILON ) * 100 ) / 100
|
603
|
+
}
|
604
|
+
return result
|
605
|
+
}
|
606
|
+
|
607
|
+
|
608
|
+
let _dataset = {}
|
609
|
+
_dataset['type'] = obj['data']['y']['keys'].length != 1 ? 'stacked' : 'single'
|
610
|
+
_dataset['view'] = obj['show']['data_as_circle'] ? 'circle' : 'bar'
|
611
|
+
_dataset['points'] = prepare_data_points( file, obj, _dataset )
|
612
|
+
_dataset['points'] = prepare_data_points_sort( obj, _dataset['points'] )
|
613
|
+
_dataset['average'] = prepare_data_average( _dataset, obj )
|
614
|
+
_dataset['points'].forEach( ( d, i ) => { _dataset['points'][ i ]['average'] = _dataset['average'] } )
|
615
|
+
return _dataset
|
616
|
+
}
|
617
|
+
|
618
|
+
|
619
|
+
const view_create = ( obj, _dataset ) => {
|
620
|
+
const prepare_view_axes = ( _data, obj ) => {
|
621
|
+
let vals = null
|
622
|
+
switch( _data['type'] ) {
|
623
|
+
case 'single':
|
624
|
+
vals = _data['points'].map( ( d ) => { return d['value'] } )
|
625
|
+
break;
|
626
|
+
case 'stacked':
|
627
|
+
vals = _data['points'].map( ( d ) => {
|
628
|
+
a = obj['data']['y']['keys'].map( ( key ) => { return d[ key ] } )
|
629
|
+
return a.reduce( ( ( accumulator, currentValue ) => { return accumulator + currentValue } ) )
|
630
|
+
} )
|
631
|
+
break;
|
632
|
+
}
|
633
|
+
|
634
|
+
const x = d3.scaleBand()
|
635
|
+
.domain( _data['points'].map( ( d ) => { return d['name_trimmed'] } ) )
|
636
|
+
.range( [ 0, obj['view']['width']['inner'] ] )
|
637
|
+
|
638
|
+
let y = null
|
639
|
+
let range_y = [ 0, Math.max( ...vals ) * 1.05 ]
|
640
|
+
if( obj['show']['range_y_log'] ) {
|
641
|
+
y = d3.scaleSqrt().domain( range_y ).range( [ obj['view']['height']['inner'], 0 ] ).nice()
|
642
|
+
} else {
|
643
|
+
y = d3.scaleLinear().domain( range_y ).range( [ obj['view']['height']['inner'], 0 ] ).nice()
|
644
|
+
}
|
645
|
+
|
646
|
+
result = {
|
647
|
+
'x' : x,
|
648
|
+
'y' : y
|
649
|
+
}
|
650
|
+
return result
|
651
|
+
}
|
652
|
+
|
653
|
+
|
654
|
+
let _d3_view = prepare_view_axes( _dataset, obj )
|
655
|
+
_d3_view['x_axis_range'] = d3.axisBottom( _d3_view['x'] )
|
656
|
+
.ticks( obj['data']['y']['ticks'] )
|
657
|
+
_d3_view['x_axis_range'] = d3.axisBottom( _d3_view['x'] )
|
658
|
+
.ticks( obj['data']['y']['ticks'] )
|
659
|
+
_d3_view['y_axis_range'] = d3.axisLeft( _d3_view['y'] )
|
660
|
+
.ticks( obj['data']['y']['ticks'] )
|
661
|
+
_d3_view['y_axis_grid'] = d3.axisLeft( _d3_view['y'] )
|
662
|
+
.tickSize( -obj['view']['width']['inner'] )
|
663
|
+
.tickFormat( '' )
|
664
|
+
.ticks( obj['data']['y']['ticks'] )
|
665
|
+
return _d3_view
|
666
|
+
}
|
667
|
+
|
668
|
+
|
669
|
+
const draw_create = ( _params, _dataset, _d3_view ) => {
|
670
|
+
const draw_grid = ( obj, _dataset, _d3_view ) => {
|
671
|
+
_d3_view['grid'] = document.createElement( 'div' )
|
672
|
+
_d3_view['grid'].setAttribute( 'id', obj['view']['dom_id'] )
|
673
|
+
document.body.appendChild( _d3_view['grid'] )
|
674
|
+
|
675
|
+
_d3_view['svg'] = d3.select( '#' + obj['view']['dom_id'] ).append( 'svg' )
|
676
|
+
.attr( 'width', obj['view']['width']['outer'] )
|
677
|
+
.attr( 'height', obj['view']['height']['outer'] )
|
678
|
+
.style( 'background-color', obj['style']['color']['canvas_background'] )
|
679
|
+
.append( 'g' )
|
680
|
+
.attr( 'transform', 'translate(' + obj['view']['margin']['left'] + ',' + obj['view']['margin']['top'] + ')' )
|
681
|
+
|
682
|
+
_d3_view['svg'].append( 'g' )
|
683
|
+
.attr( 'class', 'x axis-grid' )
|
684
|
+
.attr( 'transform', 'translate(0,' + obj['view']['height']['inner'] + ')' )
|
685
|
+
|
686
|
+
_d3_view['svg'].append( 'g')
|
687
|
+
.call( _d3_view['y_axis_grid'] )
|
688
|
+
.selectAll( 'g.tick' )
|
689
|
+
.select( 'line' )
|
690
|
+
.attr( 'class', 'quadrantBorder' )
|
691
|
+
.style( 'stroke-width', obj['style']['stroke']['gridline'] )
|
692
|
+
.style( 'color', obj['style']['color']['gridline'] )
|
693
|
+
|
694
|
+
_d3_view['svg'].append( 'g' )
|
695
|
+
.attr( 'class', 'x axis' )
|
696
|
+
.attr( 'transform', 'translate(0,' + obj['view']['height']['inner'] + ')' )
|
697
|
+
.call( _d3_view['x_axis_range'] )
|
698
|
+
.selectAll( 'text' )
|
699
|
+
.data( _dataset['points'] )
|
700
|
+
.attr( 'font-family', obj['style']['font']['family'] )
|
701
|
+
.style( 'fill', ( d ) => { return d['color_text'] } )
|
702
|
+
.attr( 'font-size', obj['style']['font']['size']['text'] )
|
703
|
+
.style( 'font-weight', ( d ) => { return d['font_format'] } )
|
704
|
+
.style( 'text-anchor', 'end' )
|
705
|
+
.attr( 'dx', '-.8em' )
|
706
|
+
.attr( 'dy', '-.55em' )
|
707
|
+
.attr( 'transform', 'translate(8,0) rotate(' + obj['style']['other']['range_x_text_rotation'] + ')' )
|
708
|
+
|
709
|
+
_d3_view['svg'].append( 'g' )
|
710
|
+
.attr( 'class', 'y axis' )
|
711
|
+
.call( _d3_view['y_axis_range'] )
|
712
|
+
.selectAll( 'text' )
|
713
|
+
.attr( 'font-family', obj['style']['font']['family'] )
|
714
|
+
.style( 'fill', obj['style']['color']['font'] )
|
715
|
+
.attr( 'font-size', obj['style']['font']['size']['text'] )
|
716
|
+
|
717
|
+
document.querySelectorAll( 'g.y.axis g.tick line' ).forEach( ( node ) => { node.remove() } )
|
718
|
+
document.querySelectorAll( 'g.x.axis g.tick line' ).forEach( ( node ) => { node.remove() } )
|
719
|
+
document.querySelectorAll( '.domain' ).forEach( ( node ) => { node.remove() } )
|
720
|
+
}
|
721
|
+
|
722
|
+
|
723
|
+
const draw_data = ( obj, _dataset, _d3_view ) => {
|
724
|
+
switch( _dataset['type'] ) {
|
725
|
+
case 'single':
|
726
|
+
switch( _dataset['view'] ) {
|
727
|
+
case 'bar' :
|
728
|
+
_d3_view['svg'].selectAll( 'bar' )
|
729
|
+
.data( _dataset['points'] )
|
730
|
+
.enter().append( 'rect' )
|
731
|
+
.attr( 'class', 'bar' )
|
732
|
+
.style( 'fill', ( d ) => { return d['color_chart'] } )
|
733
|
+
.attr( 'x', ( d ) => { return _d3_view['x']( d['name_trimmed'] ) } )
|
734
|
+
.attr( 'transform', 'translate(3,0)' )
|
735
|
+
.attr( 'width', _d3_view['x'].bandwidth() - 6 )
|
736
|
+
.attr( 'y', ( d ) => { return _d3_view['y']( d['value'] ) } )
|
737
|
+
.attr( 'height', ( d ) => { return obj['view']['height']['inner'] - _d3_view['y']( d['value'] ) } )
|
738
|
+
break;
|
739
|
+
case 'circle':
|
740
|
+
_d3_view['svg'].selectAll('circle')
|
741
|
+
.data( _dataset['points'] )
|
742
|
+
.enter().append( 'circle' )
|
743
|
+
.style( 'stroke-width', 0 )
|
744
|
+
.style( 'fill', ( d ) => { return d['color_chart'] } )
|
745
|
+
.attr( 'transform', 'translate(' + ( _d3_view['x'].bandwidth() / 2 ) +',0)' )
|
746
|
+
.attr( 'r', obj['style']['other']['circle_chart_radius'] )
|
747
|
+
.attr( 'cx', ( d ) => { return _d3_view['x']( d['name_trimmed'] ) } )
|
748
|
+
.attr( 'cy', ( d ) => { return _d3_view['y']( d['value'] ) } )
|
749
|
+
break;
|
750
|
+
}
|
751
|
+
break;
|
752
|
+
case 'stacked':
|
753
|
+
let color = d3.scaleOrdinal()
|
754
|
+
.domain( obj['data']['legend']['names']['keys'] )
|
755
|
+
.range( obj['style']['color']['legends'] )
|
756
|
+
|
757
|
+
let stacked_data = d3.stack().keys( obj['data']['legend']['names']['keys'] )( _dataset['points'] )
|
758
|
+
|
759
|
+
_d3_view['svg'].append( 'g' )
|
760
|
+
.selectAll( 'g' )
|
761
|
+
.data( stacked_data )
|
762
|
+
.enter().append( 'g' )
|
763
|
+
.attr( 'fill', ( d ) => { return color( d.key ) } )
|
764
|
+
.selectAll( 'rect' )
|
765
|
+
.data( ( d ) => { return d } )
|
766
|
+
.enter().append( 'rect' )
|
767
|
+
.attr( 'x', ( d ) => { return _d3_view['x']( d.data['name_trimmed'] ) } )
|
768
|
+
.attr( 'y', ( d ) => { return _d3_view['y']( d[ 1 ] ) } )
|
769
|
+
.attr( 'height', ( d ) => { return _d3_view['y']( d[ 0 ] ) - _d3_view['y']( d[ 1 ] ) } )
|
770
|
+
.attr( 'transform', 'translate(3,0)' )
|
771
|
+
.attr( 'width', _d3_view['x'].bandwidth() - 6 )
|
772
|
+
break;
|
773
|
+
}
|
774
|
+
}
|
775
|
+
|
776
|
+
|
777
|
+
const draw_average = ( obj, _dataset, _d3_view ) => {
|
778
|
+
if( obj['show']['average'] ) {
|
779
|
+
_d3_view['svg'].append( 'path' )
|
780
|
+
.datum( _dataset['points'] )
|
781
|
+
.attr( 'stroke', obj['style']['color']['average'] )
|
782
|
+
.attr( 'stroke-width', obj['style']['stroke']['average'] )
|
783
|
+
.attr( 'transform', 'translate(' + ( _d3_view['x'].bandwidth() / 2 ) + ',0)' )
|
784
|
+
.attr( 'd', d3.line()
|
785
|
+
.x( ( d ) => { return _d3_view['x']( d['name_trimmed'] ) } )
|
786
|
+
.y( ( d ) => { return _d3_view['y']( d['average']) } )
|
787
|
+
)
|
788
|
+
|
789
|
+
_d3_view['svg'].append( 'text' )
|
790
|
+
.attr( 'font-family', obj['style']['font']['family'] )
|
791
|
+
.style( 'fill', obj['style']['color']['font'] )
|
792
|
+
.attr( 'font-size', obj['style']['font']['size']['text'] )
|
793
|
+
.attr( 'transform', 'translate(' + ( obj['view']['width']['inner'] + obj['style']['legend']['padding']['before'] ) + ',' + _d3_view['y']( _dataset['average'] ) + ')' )
|
794
|
+
.attr( 'dy', '.35em' )
|
795
|
+
.attr( 'text-anchor', 'start' )
|
796
|
+
.text( _dataset['average'] )
|
797
|
+
}
|
798
|
+
}
|
799
|
+
|
800
|
+
|
801
|
+
const draw_title = ( obj, _d3_view ) => {
|
802
|
+
if( obj['show']['title'] ) {
|
803
|
+
_d3_view['svg'].append('text')
|
804
|
+
.attr( 'x', 0 )
|
805
|
+
.attr( 'y', obj['view']['translate']['title'] )
|
806
|
+
.style( 'fill', obj['style']['color']['font'] )
|
807
|
+
.text( obj['view']['title'] )
|
808
|
+
.attr( 'font-family', obj['style']['font']['family'] )
|
809
|
+
.attr( 'font-size', obj['style']['font']['size']['title'] )
|
810
|
+
}
|
811
|
+
}
|
812
|
+
|
813
|
+
|
814
|
+
const draw_legend = ( obj, _d3_view ) => {
|
815
|
+
if( obj['show']['legend'] ) {
|
816
|
+
let legend = _d3_view['svg'].selectAll( '.legend' )
|
817
|
+
.data( obj['data']['legend']['names']['titleized'] )
|
818
|
+
.enter()
|
819
|
+
.append( 'g' )
|
820
|
+
.attr( 'transform', ( d, i ) => { return 'translate(' + obj['view']['translate']['title_and_legend'][ i ] + ',' + '0)' } )
|
821
|
+
.attr( 'class','legend' )
|
822
|
+
|
823
|
+
legend.append( 'rect' )
|
824
|
+
.attr( 'width',obj['style']['legend']['rect_size']['full'] )
|
825
|
+
.attr( 'height',obj['style']['legend']['rect_size']['full'] )
|
826
|
+
.attr( 'fill', ( d, i ) => { return obj['style']['color']['legends'][ i ] } )
|
827
|
+
.attr( 'transform','translate(' + obj['view']['translate']['top_global'] + ', -20 )' )
|
828
|
+
|
829
|
+
legend.append( 'text')
|
830
|
+
.attr( 'font-size', obj['style']['font']['size']['text'] )
|
831
|
+
.attr( 'font-family', obj['style']['font']['family'] )
|
832
|
+
.style( 'fill', obj['style']['color']['font'] )
|
833
|
+
.attr( 'x',obj['style']['legend']['rect_size']['full'] )
|
834
|
+
.attr( 'y', obj['view']['translate']['legend_text'] )
|
835
|
+
.text( ( d ) => { return d } )
|
836
|
+
.attr( 'transform','translate(' + ( obj['view']['translate']['top_global'] + obj['style']['legend']['rect_size']['half'] ) + ', -20 )' )
|
837
|
+
}
|
838
|
+
}
|
839
|
+
|
840
|
+
|
841
|
+
draw_grid( _params, _dataset, _d3_view )
|
842
|
+
draw_data( _params, _dataset, _d3_view )
|
843
|
+
draw_average( _params, _dataset, _d3_view )
|
844
|
+
draw_title( _params, _d3_view )
|
845
|
+
draw_legend( _params, _d3_view )
|
846
|
+
return true
|
847
|
+
}
|
848
|
+
|
849
|
+
|
850
|
+
let params = params_create( default_values, x_key, y_keys, optional )
|
851
|
+
let data = data_create( file, params )
|
852
|
+
let d3_view = view_create( params, data )
|
853
|
+
draw_create( params, data, d3_view )
|
854
|
+
}
|