charting_ui 0.1.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +9 -0
- data/.travis.yml +4 -0
- data/CODE_OF_CONDUCT.md +49 -0
- data/Gemfile +25 -0
- data/LICENSE.txt +21 -0
- data/README.md +41 -0
- data/Rakefile +10 -0
- data/app/assets/javascripts/charting_ui/charting_core.coffee +525 -0
- data/app/assets/javascripts/charting_ui/charting_engine.coffee +39 -0
- data/app/assets/javascripts/charting_ui/charting_histogram.coffee +254 -0
- data/app/assets/javascripts/charting_ui/charting_point.coffee +8 -0
- data/app/assets/javascripts/charting_ui/charting_scatter.coffee +72 -0
- data/app/assets/javascripts/charting_ui/index.js +6 -0
- data/app/assets/stylesheets/charting_ui.scss +77 -0
- data/app/assets/stylesheets/index.scss +3 -0
- data/app/controllers/charting_ui/application_controller.rb +5 -0
- data/app/controllers/charting_ui/charters_controller.rb +124 -0
- data/app/views/charting_ui/_chart_content.html.erb +17 -0
- data/charting_ui.gemspec +37 -0
- data/config/locales/charting_ui.yml +16 -0
- data/config/routes.rb +3 -0
- data/lib/charting_ui.rb +19 -0
- data/lib/charting_ui/chartable.rb +19 -0
- data/lib/charting_ui/configuration.rb +15 -0
- data/lib/charting_ui/engine.rb +11 -0
- data/lib/charting_ui/version.rb +3 -0
- data/lib/charting_ui/view_helpers.rb +113 -0
- metadata +197 -0
@@ -0,0 +1,39 @@
|
|
1
|
+
#= require ./charting_scatter
|
2
|
+
#= require ./charting_histogram
|
3
|
+
|
4
|
+
@charts = []
|
5
|
+
|
6
|
+
@create_new_canvas = (block, is_mobile, type) ->
|
7
|
+
chart = switch type
|
8
|
+
when "scatter" then new ChartEngine_HardCoded_Scatter(block)
|
9
|
+
when "histogram_model" then new ChartEngine_Histogram(block)
|
10
|
+
else null
|
11
|
+
|
12
|
+
if chart == null
|
13
|
+
console.log "Unknown type: " + type
|
14
|
+
return
|
15
|
+
|
16
|
+
charts.push(chart)
|
17
|
+
register_canvas_resize(is_mobile)
|
18
|
+
return chart
|
19
|
+
|
20
|
+
@register_canvas_resize = (is_mobile) ->
|
21
|
+
if is_mobile
|
22
|
+
window.addEventListener("orientationchange",
|
23
|
+
() ->
|
24
|
+
setTimeout(() ->
|
25
|
+
$.each(charts, (i, chart) ->
|
26
|
+
chart.canvas_resize()
|
27
|
+
)
|
28
|
+
, 150)
|
29
|
+
, false);
|
30
|
+
else
|
31
|
+
$(window).resize(
|
32
|
+
() ->
|
33
|
+
$.each(charts, (i, chart) ->
|
34
|
+
chart.canvas_resize()
|
35
|
+
)
|
36
|
+
)
|
37
|
+
$.each(charts, (i, chart) ->
|
38
|
+
chart.canvas_resize()
|
39
|
+
)
|
@@ -0,0 +1,254 @@
|
|
1
|
+
#= require ./charting_core
|
2
|
+
|
3
|
+
class window.ChartEngine_Histogram extends ChartEngine_Core
|
4
|
+
|
5
|
+
offset_bar: 5
|
6
|
+
alone: false
|
7
|
+
min_height: 3
|
8
|
+
is_dates: false
|
9
|
+
|
10
|
+
#########################################
|
11
|
+
############### SETUP ###################
|
12
|
+
constructor: (block) ->
|
13
|
+
super(block)
|
14
|
+
|
15
|
+
$(@info_box).css('border-radius', '0px')
|
16
|
+
if @alone
|
17
|
+
$(@info_box).css('opacity', '1')
|
18
|
+
else
|
19
|
+
$(@info_box).css('opacity', '0.8')
|
20
|
+
|
21
|
+
if @is_dates
|
22
|
+
@create_extra_menu($(block).parent())
|
23
|
+
|
24
|
+
console.log "Chart histogram initialized!"
|
25
|
+
|
26
|
+
create_extra_menu: (container) ->
|
27
|
+
self = this
|
28
|
+
block = '<div class="charting_ui_date_range_pick">'
|
29
|
+
block += '<span>Date range: </span>'
|
30
|
+
block += '<select class="charting_ui_date_range_selector">'
|
31
|
+
block += '<option value="all" selected="selected">All</option>'
|
32
|
+
block += '<option value="day">per Days</option>'
|
33
|
+
block += '<option value="month">per Months</option>'
|
34
|
+
block += '<option value="year">per Years</option>'
|
35
|
+
block += '</select>'
|
36
|
+
block += '</div>'
|
37
|
+
|
38
|
+
container.append($(block))
|
39
|
+
|
40
|
+
$(container).on('change','.charting_ui_date_range_selector', (event) ->
|
41
|
+
self.load_source($(this).val())
|
42
|
+
self.redraw()
|
43
|
+
)
|
44
|
+
|
45
|
+
return block
|
46
|
+
|
47
|
+
get_info_box_content: () ->
|
48
|
+
return '<h3 class="charting_ui_info_main_title"></h3><ul class="charting_ui_info_container"></ul>'
|
49
|
+
|
50
|
+
create_info_info_box: (index, main_title, titles) ->
|
51
|
+
container = $(@info_box).find('ul.charting_ui_info_container')
|
52
|
+
|
53
|
+
block = '<li>'
|
54
|
+
if ! @alone
|
55
|
+
block += '<span class="charting_ui_info_title charting_ui_info_title_' + main_title + '" style="color:' + @colors[index] + '" >' + main_title + ':</span> '
|
56
|
+
block += '<ul style="display:inline-block;">'
|
57
|
+
$.each(titles, (i, t) ->
|
58
|
+
block += '<li style="display:inline-block;"><span class="charting_ui_info_sub_title charting_ui_info_sub_title_' + main_title + '_' + t.class_name + '" >' + t.name + ':</span> <span class="charting_ui_info_value charting_ui_info_box_value_' + main_title + '_' + t.class_name + '"></span> </li>'
|
59
|
+
)
|
60
|
+
block += '</ul></li>'
|
61
|
+
container.append($(block))
|
62
|
+
|
63
|
+
|
64
|
+
load_source: (date_scope = "all") ->
|
65
|
+
model = @data.model.name
|
66
|
+
key = @data.model.key
|
67
|
+
self = this
|
68
|
+
success = false
|
69
|
+
|
70
|
+
func_done = (data) ->
|
71
|
+
self.source = data.success
|
72
|
+
self.min_x = 0
|
73
|
+
self.max_x = self.source.length
|
74
|
+
self.min_y = 0
|
75
|
+
self.max_y = - Number.MAX_VALUE
|
76
|
+
total_value = {}
|
77
|
+
self.alone = self.source[0].values.length == 1
|
78
|
+
|
79
|
+
$.map(self.source, (v, k) ->
|
80
|
+
$.map(v.values, (v, k) ->
|
81
|
+
if k of total_value
|
82
|
+
total_value[k] += v.value
|
83
|
+
else
|
84
|
+
total_value[k] = v.value
|
85
|
+
)
|
86
|
+
)
|
87
|
+
|
88
|
+
$.map(self.source,(v, k) ->
|
89
|
+
type = v.key_type
|
90
|
+
if type == "date"
|
91
|
+
v.key = new Date(v.key)
|
92
|
+
self.is_dates = true
|
93
|
+
|
94
|
+
$.map(v.values, (v, k) ->
|
95
|
+
y = parseInt(v.value)
|
96
|
+
v.percent = self.round_up(v.value / total_value[k] * 1.0 * 100, 1)
|
97
|
+
if y > self.max_y
|
98
|
+
self.max_y = y
|
99
|
+
)
|
100
|
+
)
|
101
|
+
|
102
|
+
$(self.info_box).find('ul.charting_ui_info_container').empty()
|
103
|
+
$.map(self.source[0].values, (v, k) ->
|
104
|
+
self.create_info_info_box(k, v.serie, [{name: "Sum", class_name:"sum"}, {name: "%", class_name: "percent"}])
|
105
|
+
)
|
106
|
+
|
107
|
+
success = true
|
108
|
+
|
109
|
+
func_error = (data) ->
|
110
|
+
success = false
|
111
|
+
response = $.parseJSON data.responseText
|
112
|
+
console.log "ERROR: #" + data.status + ": " + response.error
|
113
|
+
|
114
|
+
func_always = (data) ->
|
115
|
+
console.log "Data fetched"
|
116
|
+
|
117
|
+
$.ajax({
|
118
|
+
url: '/charting_ui/fetch_data',
|
119
|
+
data: 'model_name=' + model + '&model_key=' + JSON.stringify(key) + "&date_scope=" + date_scope,
|
120
|
+
async: false,
|
121
|
+
method: 'post',
|
122
|
+
success: func_done,
|
123
|
+
done: func_done,
|
124
|
+
error: func_error,
|
125
|
+
fail: func_error,
|
126
|
+
complete: func_always,
|
127
|
+
always: func_always
|
128
|
+
})
|
129
|
+
|
130
|
+
console.log("MinX: " + @min_x + ", MaxX: " + @max_x)
|
131
|
+
console.log("MinY: " + @min_y + ", MaxY: " + @max_y)
|
132
|
+
return success
|
133
|
+
|
134
|
+
|
135
|
+
before_redraw: () ->
|
136
|
+
|
137
|
+
#########################################
|
138
|
+
############ DRAW GLOBAL ################
|
139
|
+
getIndentation: () ->
|
140
|
+
return @source.length
|
141
|
+
|
142
|
+
draw_chart: () ->
|
143
|
+
self = this
|
144
|
+
|
145
|
+
$.each(@source, (i, p) ->
|
146
|
+
series = p.values.length
|
147
|
+
$.map(p.values, (v, index) ->
|
148
|
+
x_start = i + (1.0 / series * index)
|
149
|
+
x_end = i + (1.0 / series * (index + 1))
|
150
|
+
top_left = new Chart_Point(x_start, v.value)
|
151
|
+
bottom_right = new Chart_Point(x_end, self.min_y)
|
152
|
+
top_left = self.convert_unit_to_pixel(top_left)
|
153
|
+
if v.value == 0
|
154
|
+
top_left.y -= self.min_height
|
155
|
+
|
156
|
+
bottom_right = self.convert_unit_to_pixel(bottom_right)
|
157
|
+
if index == 0
|
158
|
+
top_left.x += self.offset_bar
|
159
|
+
if index == series - 1
|
160
|
+
bottom_right.x -= self.offset_bar
|
161
|
+
|
162
|
+
|
163
|
+
self.draw_rectangle(top_left, bottom_right, self.colors[index], "white", 0)
|
164
|
+
)
|
165
|
+
)
|
166
|
+
|
167
|
+
get_axe_text_x_pixel_position: (n, indent) ->
|
168
|
+
return @convert_unit_to_pixel_x(n + @indent_x / 2)
|
169
|
+
|
170
|
+
get_axe_text_x_text: (n) ->
|
171
|
+
if n >= @source.length
|
172
|
+
return null
|
173
|
+
return @source[n].key
|
174
|
+
|
175
|
+
#########################################
|
176
|
+
######### UPDATE INTERACTIVE ############
|
177
|
+
update_user_interaction: (mouse_coordinate) ->
|
178
|
+
value_x = Math.ceil(@convert_pixel_to_unit_x(mouse_coordinate.x)) - 1
|
179
|
+
if value_x >= @source.length
|
180
|
+
return false
|
181
|
+
column = @source[value_x]
|
182
|
+
|
183
|
+
series = column.values.length
|
184
|
+
if series == 1
|
185
|
+
coordinate_x = value_x
|
186
|
+
coordinate_y = column.values[0].value
|
187
|
+
|
188
|
+
if mouse_coordinate.y < @convert_unit_to_pixel_y(coordinate_y) && coordinate_y > 0
|
189
|
+
return false
|
190
|
+
|
191
|
+
top_left = new Chart_Point(coordinate_x, coordinate_y)
|
192
|
+
bottom_right = new Chart_Point(coordinate_x + 1, @min_y)
|
193
|
+
|
194
|
+
top_left = @convert_unit_to_pixel(top_left)
|
195
|
+
|
196
|
+
if coordinate_y == 0
|
197
|
+
thickness = 1
|
198
|
+
top_left.y -= @min_height
|
199
|
+
else
|
200
|
+
thickness = 4
|
201
|
+
|
202
|
+
top_left.x += @offset_bar
|
203
|
+
|
204
|
+
bottom_right = @convert_unit_to_pixel(bottom_right)
|
205
|
+
bottom_right.x -= @offset_bar
|
206
|
+
bottom_right.y -= thickness / 2
|
207
|
+
|
208
|
+
|
209
|
+
|
210
|
+
@previous_drawn_region.push(@draw_rectangle_interactive(top_left, bottom_right, @default_cross_color, "black", thickness))
|
211
|
+
else
|
212
|
+
slice = 1.0 / series
|
213
|
+
|
214
|
+
return true
|
215
|
+
|
216
|
+
updateInfoBoxPosition: (mouse_coordinate) ->
|
217
|
+
value_x = Math.ceil(@convert_pixel_to_unit_x(mouse_coordinate.x)) - 1
|
218
|
+
column = @source[value_x]
|
219
|
+
|
220
|
+
coordinate_x = @convert_unit_to_pixel_x(value_x) + 1
|
221
|
+
|
222
|
+
if @alone
|
223
|
+
if column.values[0].value == 0
|
224
|
+
coordinate_y = @convert_unit_to_pixel_y(@max_y)
|
225
|
+
coordinate_x -= 2
|
226
|
+
else
|
227
|
+
coordinate_y = @convert_unit_to_pixel_y(column.values[0].value) + 1
|
228
|
+
else
|
229
|
+
coordinate_y = @convert_unit_to_pixel_y(@max_y)
|
230
|
+
|
231
|
+
if $(@info_box).outerWidth() > @canvas_width - coordinate_x - @offset_bar
|
232
|
+
coordinate_x = @convert_unit_to_pixel_x(value_x + 1) - 1
|
233
|
+
@info_box.css('left', (coordinate_x - $(@info_box).outerWidth() - @offset_bar) + 'px')
|
234
|
+
else
|
235
|
+
@info_box.css('left', (coordinate_x + @offset_bar) + 'px')
|
236
|
+
|
237
|
+
@info_box.css('top', (coordinate_y) + 'px')
|
238
|
+
|
239
|
+
|
240
|
+
update_info_box_content: (mouse_coordinate) ->
|
241
|
+
value_x = Math.ceil(@convert_pixel_to_unit_x(mouse_coordinate.x)) - 1
|
242
|
+
column = @source[value_x]
|
243
|
+
self = this
|
244
|
+
@info_box.find('.charting_ui_info_main_title').text(column.key)
|
245
|
+
|
246
|
+
$.map(column.values, (v, k) ->
|
247
|
+
serie = v.serie
|
248
|
+
self.info_box.find('.charting_ui_info_box_value_' + serie + '_sum').text(v.value)
|
249
|
+
self.info_box.find('.charting_ui_info_box_value_' + serie + '_percent').text(v.percent)
|
250
|
+
)
|
251
|
+
#@info_box.find('.charting_ui_info_box_y').text(column.values[0].value)
|
252
|
+
#@info_box.find('.charting_ui_info_box_extra').text(column.values[0].percent)
|
253
|
+
|
254
|
+
#########################################
|
@@ -0,0 +1,72 @@
|
|
1
|
+
#= require ./charting_core
|
2
|
+
|
3
|
+
class window.ChartEngine_HardCoded_Scatter extends ChartEngine_Core
|
4
|
+
|
5
|
+
#########################################
|
6
|
+
############### SETUP ###################
|
7
|
+
constructor: (block) ->
|
8
|
+
super(block)
|
9
|
+
|
10
|
+
console.log "Chart scatter initialized!"
|
11
|
+
|
12
|
+
get_info_box_content: () ->
|
13
|
+
return '<ul class="charting_ui_info_container">
|
14
|
+
<li><span class="charting_ui_info_title charting_ui_info_x_title" >x:</span> <span class="charting_ui_info_value charting_ui_info_box_x"></span></li>
|
15
|
+
<li><span class="charting_ui_info_title charting_ui_info_y_title" >y:</span> <span class="charting_ui_info_value charting_ui_info_box_y"></span></li>
|
16
|
+
</ul>'
|
17
|
+
|
18
|
+
load_source: () ->
|
19
|
+
@source = @data.collection
|
20
|
+
@min_x = 0
|
21
|
+
@max_x = @source.length
|
22
|
+
@min_y = Math.min.apply(null, @source)
|
23
|
+
@max_y = Math.max.apply(null, @source)
|
24
|
+
console.log("MinX: " + @min_x + ", MaxX: " + @max_x)
|
25
|
+
console.log("MinY: " + @min_y + ", MaxY: " + @max_y)
|
26
|
+
return true
|
27
|
+
|
28
|
+
#########################################
|
29
|
+
############ DRAW GLOBAL ################
|
30
|
+
draw_chart: () ->
|
31
|
+
points = []
|
32
|
+
self = this
|
33
|
+
$.each(@source, (i, p) ->
|
34
|
+
points.push(self.convert_unit_to_pixel(new Chart_Point(i, p)))
|
35
|
+
)
|
36
|
+
@draw_lines(points, @default_line_color)
|
37
|
+
|
38
|
+
#########################################
|
39
|
+
######### UPDATE INTERACTIVE ############
|
40
|
+
update_user_interaction: (mouse_coordinate) ->
|
41
|
+
value_x = Math.round(@convert_pixel_to_unit_x(mouse_coordinate.x))
|
42
|
+
if value_x >= @source.length
|
43
|
+
return false
|
44
|
+
value_y = @source[value_x]
|
45
|
+
|
46
|
+
|
47
|
+
mouse_coordinate.x = @convert_unit_to_pixel_x(value_x)
|
48
|
+
mouse_coordinate.y = @convert_unit_to_pixel_y(value_y)
|
49
|
+
|
50
|
+
@draw_line_interactive(new Chart_Point(@offset_left, mouse_coordinate.y), #new Chart_Point(mouse_coordinate.x - @default_font_size, mouse_coordinate.y),
|
51
|
+
new Chart_Point(@canvas_width - @offset_right, mouse_coordinate.y),#new Chart_Point(mouse_coordinate.x + @default_font_size, mouse_coordinate.y),
|
52
|
+
@default_cross_color, 1
|
53
|
+
)
|
54
|
+
@draw_line_interactive(new Chart_Point(mouse_coordinate.x, @offset_top), #new Chart_Point(mouse_coordinate.x, mouse_coordinate.y - @default_font_size),
|
55
|
+
new Chart_Point(mouse_coordinate.x, @canvas_height - @offset_bottom),#new Chart_Point(mouse_coordinate.x, mouse_coordinate.y + @default_font_size),
|
56
|
+
@default_cross_color, 1
|
57
|
+
)
|
58
|
+
|
59
|
+
#@previous_drawn_region.push(@draw_circle(@ctx_interactive, @convert_unit_to_pixel(new Chart_Point(value_x, value_y)), 5))
|
60
|
+
@draw_circle(@ctx_interactive, @convert_unit_to_pixel(new Chart_Point(value_x, value_y)), 5)
|
61
|
+
|
62
|
+
@previous_drawn_region.push([
|
63
|
+
0,0#mouse_coordinate.x - @default_font_size, mouse_coordinate.y - @default_font_size,
|
64
|
+
@canvas_width, @canvas_height #mouse_coordinate.x + @default_font_size, mouse_coordinate.y + @default_font_size
|
65
|
+
])
|
66
|
+
return true
|
67
|
+
|
68
|
+
update_info_box_content: (mouse_coordinate) ->
|
69
|
+
@info_box.find('.charting_ui_info_box_x').text(@round_up(@convert_pixel_to_unit_x(mouse_coordinate.x)))
|
70
|
+
@info_box.find('.charting_ui_info_box_y').text(@round_up(@convert_pixel_to_unit_y(mouse_coordinate.y)))
|
71
|
+
|
72
|
+
#########################################
|
@@ -0,0 +1,77 @@
|
|
1
|
+
.charting_ui_area{
|
2
|
+
width: 100%;
|
3
|
+
display: block;
|
4
|
+
position: relative;
|
5
|
+
|
6
|
+
.charting_ui_date_range_pick{
|
7
|
+
display: block;
|
8
|
+
position: absolute;
|
9
|
+
top: 0px;
|
10
|
+
left: 0px;
|
11
|
+
}
|
12
|
+
|
13
|
+
&.charting_ui_bordered_area{
|
14
|
+
border: 1px lightgray solid;
|
15
|
+
}
|
16
|
+
|
17
|
+
.charting_ui_main_title{
|
18
|
+
text-align: center;
|
19
|
+
}
|
20
|
+
|
21
|
+
.charting_ui_canvas_area{
|
22
|
+
position: relative;
|
23
|
+
display: block;
|
24
|
+
width: 100%;
|
25
|
+
|
26
|
+
.charting_ui_data{
|
27
|
+
display: none;
|
28
|
+
}
|
29
|
+
|
30
|
+
.charting_ui_info_block{
|
31
|
+
position: absolute;
|
32
|
+
display: none;
|
33
|
+
background: white;
|
34
|
+
border: 1px solid black;
|
35
|
+
border-radius: 5px;
|
36
|
+
z-index: 2;
|
37
|
+
pointer-events:none;
|
38
|
+
padding: 5px;
|
39
|
+
opacity: 0.7;
|
40
|
+
|
41
|
+
|
42
|
+
h3{
|
43
|
+
font-size: 1.0em;
|
44
|
+
text-align: center;
|
45
|
+
font-weight: bold;
|
46
|
+
margin: 0px;
|
47
|
+
border-bottom: 1px solid darkgray
|
48
|
+
}
|
49
|
+
|
50
|
+
ul{
|
51
|
+
font-size: 0.9em;
|
52
|
+
-webkit-padding-start: 0px;
|
53
|
+
list-style-type: none;
|
54
|
+
-webkit-margin-before: 0px;
|
55
|
+
-webkit-margin-after: 0px;
|
56
|
+
text-align: center;
|
57
|
+
|
58
|
+
.charting_ui_info_title, .charting_ui_info_sub_title{
|
59
|
+
font-weight: bold;
|
60
|
+
text-transform: capitalize;
|
61
|
+
}
|
62
|
+
}
|
63
|
+
}
|
64
|
+
|
65
|
+
.charting_ui_chart_interactive{
|
66
|
+
position: absolute;
|
67
|
+
z-index: 1;
|
68
|
+
display: block;
|
69
|
+
}
|
70
|
+
|
71
|
+
.charting_ui_chart_fix{
|
72
|
+
position: absolute;
|
73
|
+
z-index: 0;
|
74
|
+
display: block;
|
75
|
+
}
|
76
|
+
}
|
77
|
+
}
|