chartjs-zoomable 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: a1774f680deadbe485abb972e0cd29e92d68d3e9
4
+ data.tar.gz: 271068cca5ba6fc54127ae5885f2509a9e87641f
5
+ SHA512:
6
+ metadata.gz: b68ff04421cd69b5825cc5653d682c247547518e1b5b3ea200ae026fd616ade435e33aede74dd3220f453622ad3aa93a6a9c1888d10f3fa16e0abe640bc04a00
7
+ data.tar.gz: 1f1c2bb5da1f4f0e690c7305eb1e5eb90fbe09d36f0616500cb5d55822a32b7a2d824ddf616a06e42643b72c96f7e9a43c78e2903d2f9d73065b8b83b9b69698
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright 2018 Mateusz Michalski
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,90 @@
1
+ # chartjs-zoomable
2
+ Simplifies usage of Chart.js in Rails views with zoom and pan plugin!
3
+
4
+ ## Installation
5
+ Add this line to your application's Gemfile:
6
+
7
+ ```ruby
8
+ gem 'chartjs-zoomable'
9
+ ```
10
+
11
+ And then execute:
12
+ ```bash
13
+ $ bundle
14
+ ```
15
+ Add the following to your `application.js`:
16
+ ```javascript
17
+ $ //= require chartjs-zoomable
18
+ ```
19
+
20
+ ## Usage
21
+ Each chart type has a corresponding helper for your views. The helper methods take the same arguments as their Javascript counterparts. The `options` are optional. They allow you to make a graph zoomable:
22
+
23
+ ```erb
24
+ <%= line_chart data, options %>
25
+ <%= bar_chart data, options %>
26
+ <%= horizontal_bar_chart data, options %>
27
+ <%= radar_chart data, options %>
28
+ <%= polar_area_chart data, options %>
29
+ <%= pie_chart data, options %>
30
+ <%= doughnut_chart data, options %>
31
+ <%= bubble_chart data, options %>
32
+ <%= scatter_chart data, options %>
33
+ ```
34
+
35
+ If you don't want these helpers – perhaps they clash with other methods in your views – add this initializer to your app:
36
+
37
+ ```ruby
38
+ # config/initializers/chartjs.rb
39
+ Chartjs.no_conflict!
40
+ ```
41
+
42
+ Then you can use these helpers instead:
43
+
44
+ ```erb
45
+ <%= chartjs_line_chart data, options %>
46
+ <%= chartjs_bar_chart data, options %>
47
+ <%= chartjs_horizontal_bar_chart data, options %>
48
+ <%= chartjs_radar_chart data, options %>
49
+ <%= chartjs_polar_area_chart data, options %>
50
+ <%= chartjs_pie_chart data, options %>
51
+ <%= chartjs_doughnut_chart data, options %>
52
+ <%= chartjs_bubble_chart data, options %>
53
+ <%= chartjs_scatter_chart data, options %>
54
+ ```
55
+
56
+ For example, to render a [line chart][linechart] with zoom:
57
+
58
+ ```ruby
59
+ data = {
60
+ labels: ["March", "April", "May", "June", "July", "August", "September"],
61
+ datasets: [
62
+ {
63
+ label: "My First dataset",
64
+ backgroundColor: "rgba(220,220,220,0.2)",
65
+ borderColor: "rgba(220,220,220,1)",
66
+ data: [67, 58, 80, 81, 56, 55, 40]
67
+ },
68
+ {
69
+ label: "My Second dataset",
70
+ backgroundColor: "rgba(151,187,205,0.2)",
71
+ borderColor: "rgba(151,187,205,1)",
72
+ data: [28, 48, 40, 19, 86, 27, 91]
73
+ }
74
+ ]
75
+ }
76
+ options = {
77
+ id: "my_first_graph",
78
+ zoom: {
79
+ enabled: true,
80
+ mode: 'x',
81
+ },
82
+ }
83
+ <%= line_chart data, options %>
84
+ ```
85
+
86
+ ## Contributing
87
+ Contribution directions go here.
88
+
89
+ ## License
90
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
data/Rakefile ADDED
@@ -0,0 +1,32 @@
1
+ begin
2
+ require 'bundler/setup'
3
+ rescue LoadError
4
+ puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
5
+ end
6
+
7
+ require 'rdoc/task'
8
+
9
+ RDoc::Task.new(:rdoc) do |rdoc|
10
+ rdoc.rdoc_dir = 'rdoc'
11
+ rdoc.title = 'Chartjs::Zoomable'
12
+ rdoc.options << '--line-numbers'
13
+ rdoc.rdoc_files.include('README.md')
14
+ rdoc.rdoc_files.include('lib/**/*.rb')
15
+ end
16
+
17
+ APP_RAKEFILE = File.expand_path("test/dummy/Rakefile", __dir__)
18
+ load 'rails/tasks/engine.rake'
19
+
20
+ load 'rails/tasks/statistics.rake'
21
+
22
+ require 'bundler/gem_tasks'
23
+
24
+ require 'rake/testtask'
25
+
26
+ Rake::TestTask.new(:test) do |t|
27
+ t.libs << 'test'
28
+ t.pattern = 'test/**/*_test.rb'
29
+ t.verbose = false
30
+ end
31
+
32
+ task default: :test
@@ -0,0 +1,2 @@
1
+ //= link_directory ../javascripts/chartjs/zoomable .js
2
+ //= link_directory ../stylesheets/chartjs/zoomable .css
@@ -0,0 +1,14 @@
1
+ // This is a manifest file that'll be compiled into application.js, which will include all the files
2
+ // listed below.
3
+ //
4
+ // Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts,
5
+ // or any plugin's vendor/assets/javascripts directory can be referenced here using a relative path.
6
+ //
7
+ // It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
8
+ // compiled file. JavaScript code in this file should be added after the last require_* statement.
9
+ //
10
+ // Read Sprockets README (https://github.com/rails/sprockets#sprockets-directives) for details
11
+ // about supported directives.
12
+ //
13
+ //= require Chart.bundle.min
14
+ //= require_tree .
@@ -0,0 +1,580 @@
1
+ /*!
2
+ * chartjs-plugin-zoom
3
+ * http://chartjs.org/
4
+ * Version: 0.6.5
5
+ *
6
+ * Copyright 2016 Evert Timberg
7
+ * Released under the MIT license
8
+ * https://github.com/chartjs/chartjs-plugin-zoom/blob/master/LICENSE.md
9
+ */
10
+ (function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i<t.length;i++)o(t[i]);return o}return r})()({1:[function(require,module,exports){
11
+
12
+ },{}],2:[function(require,module,exports){
13
+ /*jslint browser:true, devel:true, white:true, vars:true */
14
+ /*global require*/
15
+
16
+ // hammer JS for touch support
17
+ var Hammer = require('hammerjs');
18
+ Hammer = typeof(Hammer) === 'function' ? Hammer : window.Hammer;
19
+
20
+ // Get the chart variable
21
+ var Chart = require('chart.js');
22
+ Chart = typeof(Chart) === 'function' ? Chart : window.Chart;
23
+ var helpers = Chart.helpers;
24
+
25
+ // Take the zoom namespace of Chart
26
+ var zoomNS = Chart.Zoom = Chart.Zoom || {};
27
+
28
+ // Where we store functions to handle different scale types
29
+ var zoomFunctions = zoomNS.zoomFunctions = zoomNS.zoomFunctions || {};
30
+ var panFunctions = zoomNS.panFunctions = zoomNS.panFunctions || {};
31
+
32
+ // Default options if none are provided
33
+ var defaultOptions = zoomNS.defaults = {
34
+ pan: {
35
+ enabled: true,
36
+ mode: 'xy',
37
+ speed: 20,
38
+ threshold: 10
39
+ },
40
+ zoom: {
41
+ enabled: true,
42
+ mode: 'xy',
43
+ sensitivity: 3
44
+ }
45
+ };
46
+
47
+ function directionEnabled(mode, dir) {
48
+ if (mode === undefined) {
49
+ return true;
50
+ } else if (typeof mode === 'string') {
51
+ return mode.indexOf(dir) !== -1;
52
+ }
53
+
54
+ return false;
55
+ }
56
+
57
+ function rangeMaxLimiter(zoomPanOptions, newMax) {
58
+ if (zoomPanOptions.scaleAxes && zoomPanOptions.rangeMax &&
59
+ !helpers.isNullOrUndef(zoomPanOptions.rangeMax[zoomPanOptions.scaleAxes])) {
60
+ var rangeMax = zoomPanOptions.rangeMax[zoomPanOptions.scaleAxes];
61
+ if (newMax > rangeMax) {
62
+ newMax = rangeMax;
63
+ }
64
+ }
65
+ return newMax;
66
+ }
67
+
68
+ function rangeMinLimiter(zoomPanOptions, newMin) {
69
+ if (zoomPanOptions.scaleAxes && zoomPanOptions.rangeMin &&
70
+ !helpers.isNullOrUndef(zoomPanOptions.rangeMin[zoomPanOptions.scaleAxes])) {
71
+ var rangeMin = zoomPanOptions.rangeMin[zoomPanOptions.scaleAxes];
72
+ if (newMin < rangeMin) {
73
+ newMin = rangeMin;
74
+ }
75
+ }
76
+ return newMin;
77
+ }
78
+
79
+ function zoomIndexScale(scale, zoom, center, zoomOptions) {
80
+ var labels = scale.chart.data.labels;
81
+ var minIndex = scale.minIndex;
82
+ var lastLabelIndex = labels.length - 1;
83
+ var maxIndex = scale.maxIndex;
84
+ var sensitivity = zoomOptions.sensitivity;
85
+ var chartCenter = scale.isHorizontal() ? scale.left + (scale.width/2) : scale.top + (scale.height/2);
86
+ var centerPointer = scale.isHorizontal() ? center.x : center.y;
87
+
88
+ zoomNS.zoomCumulativeDelta = zoom > 1 ? zoomNS.zoomCumulativeDelta + 1 : zoomNS.zoomCumulativeDelta - 1;
89
+
90
+ if (Math.abs(zoomNS.zoomCumulativeDelta) > sensitivity){
91
+ if(zoomNS.zoomCumulativeDelta < 0){
92
+ if(centerPointer >= chartCenter){
93
+ if (minIndex <= 0){
94
+ maxIndex = Math.min(lastLabelIndex, maxIndex + 1);
95
+ } else{
96
+ minIndex = Math.max(0, minIndex - 1);
97
+ }
98
+ } else if(centerPointer < chartCenter){
99
+ if (maxIndex >= lastLabelIndex){
100
+ minIndex = Math.max(0, minIndex - 1);
101
+ } else{
102
+ maxIndex = Math.min(lastLabelIndex, maxIndex + 1);
103
+ }
104
+ }
105
+ zoomNS.zoomCumulativeDelta = 0;
106
+ }
107
+ else if(zoomNS.zoomCumulativeDelta > 0){
108
+ if(centerPointer >= chartCenter){
109
+ minIndex = minIndex < maxIndex ? minIndex = Math.min(maxIndex, minIndex + 1) : minIndex;
110
+ } else if(centerPointer < chartCenter){
111
+ maxIndex = maxIndex > minIndex ? maxIndex = Math.max(minIndex, maxIndex - 1) : maxIndex;
112
+ }
113
+ zoomNS.zoomCumulativeDelta = 0;
114
+ }
115
+ scale.options.ticks.min = rangeMinLimiter(zoomOptions, labels[minIndex]);
116
+ scale.options.ticks.max = rangeMaxLimiter(zoomOptions, labels[maxIndex]);
117
+ }
118
+ }
119
+
120
+ function zoomTimeScale(scale, zoom, center, zoomOptions) {
121
+ var options = scale.options;
122
+
123
+ var range;
124
+ var min_percent;
125
+ if (scale.isHorizontal()) {
126
+ range = scale.right - scale.left;
127
+ min_percent = (center.x - scale.left) / range;
128
+ } else {
129
+ range = scale.bottom - scale.top;
130
+ min_percent = (center.y - scale.top) / range;
131
+ }
132
+
133
+ var max_percent = 1 - min_percent;
134
+ var newDiff = range * (zoom - 1);
135
+
136
+ var minDelta = newDiff * min_percent;
137
+ var maxDelta = newDiff * max_percent;
138
+
139
+ var newMin = scale.getValueForPixel(scale.getPixelForValue(scale.min) + minDelta);
140
+ var newMax = scale.getValueForPixel(scale.getPixelForValue(scale.max) - maxDelta);
141
+
142
+ var diffMinMax = newMax.diff(newMin);
143
+ var minLimitExceeded = rangeMinLimiter(zoomOptions, diffMinMax) != diffMinMax;
144
+ var maxLimitExceeded = rangeMaxLimiter(zoomOptions, diffMinMax) != diffMinMax;
145
+
146
+ if (!minLimitExceeded && !maxLimitExceeded) {
147
+ options.time.min = newMin;
148
+ options.time.max = newMax;
149
+ }
150
+ }
151
+
152
+ function zoomNumericalScale(scale, zoom, center, zoomOptions) {
153
+ var range = scale.max - scale.min;
154
+ var newDiff = range * (zoom - 1);
155
+
156
+ var cursorPixel = scale.isHorizontal() ? center.x : center.y;
157
+ var min_percent = (scale.getValueForPixel(cursorPixel) - scale.min) / range;
158
+ var max_percent = 1 - min_percent;
159
+
160
+ var minDelta = newDiff * min_percent;
161
+ var maxDelta = newDiff * max_percent;
162
+
163
+ scale.options.ticks.min = rangeMinLimiter(zoomOptions, scale.min + minDelta);
164
+ scale.options.ticks.max = rangeMaxLimiter(zoomOptions, scale.max - maxDelta);
165
+ }
166
+
167
+ function zoomScale(scale, zoom, center, zoomOptions) {
168
+ var fn = zoomFunctions[scale.options.type];
169
+ if (fn) {
170
+ fn(scale, zoom, center, zoomOptions);
171
+ }
172
+ }
173
+
174
+ function doZoom(chartInstance, zoom, center, whichAxes) {
175
+ var ca = chartInstance.chartArea;
176
+ if (!center) {
177
+ center = {
178
+ x: (ca.left + ca.right) / 2,
179
+ y: (ca.top + ca.bottom) / 2,
180
+ };
181
+ }
182
+
183
+ var zoomOptions = chartInstance.options.zoom;
184
+
185
+ if (zoomOptions && helpers.getValueOrDefault(zoomOptions.enabled, defaultOptions.zoom.enabled)) {
186
+ // Do the zoom here
187
+ var zoomMode = helpers.getValueOrDefault(chartInstance.options.zoom.mode, defaultOptions.zoom.mode);
188
+ zoomOptions.sensitivity = helpers.getValueOrDefault(chartInstance.options.zoom.sensitivity, defaultOptions.zoom.sensitivity);
189
+
190
+ // Which axe should be modified when figers were used.
191
+ var _whichAxes;
192
+ if (zoomMode == 'xy' && whichAxes !== undefined) {
193
+ // based on fingers positions
194
+ _whichAxes = whichAxes;
195
+ } else {
196
+ // no effect
197
+ _whichAxes = 'xy';
198
+ }
199
+
200
+ helpers.each(chartInstance.scales, function(scale, id) {
201
+ if (scale.isHorizontal() && directionEnabled(zoomMode, 'x') && directionEnabled(_whichAxes, 'x')) {
202
+ zoomOptions.scaleAxes = "x";
203
+ zoomScale(scale, zoom, center, zoomOptions);
204
+ } else if (!scale.isHorizontal() && directionEnabled(zoomMode, 'y') && directionEnabled(_whichAxes, 'y')) {
205
+ // Do Y zoom
206
+ zoomOptions.scaleAxes = "y";
207
+ zoomScale(scale, zoom, center, zoomOptions);
208
+ }
209
+ });
210
+
211
+ chartInstance.update(0);
212
+ }
213
+ }
214
+
215
+ function panIndexScale(scale, delta, panOptions) {
216
+ var labels = scale.chart.data.labels;
217
+ var lastLabelIndex = labels.length - 1;
218
+ var offsetAmt = Math.max((scale.ticks.length - ((scale.options.gridLines.offsetGridLines) ? 0 : 1)), 1);
219
+ var panSpeed = panOptions.speed;
220
+ var minIndex = scale.minIndex;
221
+ var step = Math.round(scale.width / (offsetAmt * panSpeed));
222
+ var maxIndex;
223
+
224
+ zoomNS.panCumulativeDelta += delta;
225
+
226
+ minIndex = zoomNS.panCumulativeDelta > step ? Math.max(0, minIndex -1) : zoomNS.panCumulativeDelta < -step ? Math.min(lastLabelIndex - offsetAmt + 1, minIndex + 1) : minIndex;
227
+ zoomNS.panCumulativeDelta = minIndex !== scale.minIndex ? 0 : zoomNS.panCumulativeDelta;
228
+
229
+ maxIndex = Math.min(lastLabelIndex, minIndex + offsetAmt - 1);
230
+
231
+ scale.options.ticks.min = rangeMinLimiter(panOptions, labels[minIndex]);
232
+ scale.options.ticks.max = rangeMaxLimiter(panOptions, labels[maxIndex]);
233
+ }
234
+
235
+ function panTimeScale(scale, delta, panOptions) {
236
+ var options = scale.options;
237
+ var limitedMax = rangeMaxLimiter(panOptions, scale.getValueForPixel(scale.getPixelForValue(scale.max) - delta));
238
+ var limitedMin = rangeMinLimiter(panOptions, scale.getValueForPixel(scale.getPixelForValue(scale.min) - delta));
239
+
240
+ var limitedTimeDelta = delta < 0 ? limitedMax - scale.max : limitedMin - scale.min;
241
+
242
+ options.time.max = scale.max + limitedTimeDelta;
243
+ options.time.min = scale.min + limitedTimeDelta;
244
+ }
245
+
246
+ function panNumericalScale(scale, delta, panOptions) {
247
+ var tickOpts = scale.options.ticks;
248
+ var start = scale.start,
249
+ end = scale.end;
250
+
251
+ if (tickOpts.reverse) {
252
+ tickOpts.max = scale.getValueForPixel(scale.getPixelForValue(start) - delta);
253
+ tickOpts.min = scale.getValueForPixel(scale.getPixelForValue(end) - delta);
254
+ } else {
255
+ tickOpts.min = scale.getValueForPixel(scale.getPixelForValue(start) - delta);
256
+ tickOpts.max = scale.getValueForPixel(scale.getPixelForValue(end) - delta);
257
+ }
258
+ tickOpts.min = rangeMinLimiter(panOptions, tickOpts.min);
259
+ tickOpts.max = rangeMaxLimiter(panOptions, tickOpts.max);
260
+ }
261
+
262
+ function panScale(scale, delta, panOptions) {
263
+ var fn = panFunctions[scale.options.type];
264
+ if (fn) {
265
+ fn(scale, delta, panOptions);
266
+ }
267
+ }
268
+
269
+ function doPan(chartInstance, deltaX, deltaY) {
270
+ var panOptions = chartInstance.options.pan;
271
+ if (panOptions && helpers.getValueOrDefault(panOptions.enabled, defaultOptions.pan.enabled)) {
272
+ var panMode = helpers.getValueOrDefault(chartInstance.options.pan.mode, defaultOptions.pan.mode);
273
+ panOptions.speed = helpers.getValueOrDefault(chartInstance.options.pan.speed, defaultOptions.pan.speed);
274
+
275
+ helpers.each(chartInstance.scales, function(scale, id) {
276
+ if (scale.isHorizontal() && directionEnabled(panMode, 'x') && deltaX !== 0) {
277
+ panOptions.scaleAxes = "x";
278
+ panScale(scale, deltaX, panOptions);
279
+ } else if (!scale.isHorizontal() && directionEnabled(panMode, 'y') && deltaY !== 0) {
280
+ panOptions.scaleAxes = "y";
281
+ panScale(scale, deltaY, panOptions);
282
+ }
283
+ });
284
+
285
+ chartInstance.update(0);
286
+ }
287
+ }
288
+
289
+ function positionInChartArea(chartInstance, position) {
290
+ return (position.x >= chartInstance.chartArea.left && position.x <= chartInstance.chartArea.right) &&
291
+ (position.y >= chartInstance.chartArea.top && position.y <= chartInstance.chartArea.bottom);
292
+ }
293
+
294
+ function getYAxis(chartInstance) {
295
+ var scales = chartInstance.scales;
296
+
297
+ for (var scaleId in scales) {
298
+ var scale = scales[scaleId];
299
+
300
+ if (!scale.isHorizontal()) {
301
+ return scale;
302
+ }
303
+ }
304
+ }
305
+
306
+ // Store these for later
307
+ zoomNS.zoomFunctions.category = zoomIndexScale;
308
+ zoomNS.zoomFunctions.time = zoomTimeScale;
309
+ zoomNS.zoomFunctions.linear = zoomNumericalScale;
310
+ zoomNS.zoomFunctions.logarithmic = zoomNumericalScale;
311
+ zoomNS.panFunctions.category = panIndexScale;
312
+ zoomNS.panFunctions.time = panTimeScale;
313
+ zoomNS.panFunctions.linear = panNumericalScale;
314
+ zoomNS.panFunctions.logarithmic = panNumericalScale;
315
+ // Globals for catergory pan and zoom
316
+ zoomNS.panCumulativeDelta = 0;
317
+ zoomNS.zoomCumulativeDelta = 0;
318
+
319
+ // Chartjs Zoom Plugin
320
+ var zoomPlugin = {
321
+ afterInit: function(chartInstance) {
322
+ helpers.each(chartInstance.scales, function(scale) {
323
+ scale.originalOptions = helpers.clone(scale.options);
324
+ });
325
+
326
+ chartInstance.resetZoom = function() {
327
+ helpers.each(chartInstance.scales, function(scale, id) {
328
+ var timeOptions = scale.options.time;
329
+ var tickOptions = scale.options.ticks;
330
+
331
+ if (timeOptions) {
332
+ timeOptions.min = scale.originalOptions.time.min;
333
+ timeOptions.max = scale.originalOptions.time.max;
334
+ }
335
+
336
+ if (tickOptions) {
337
+ tickOptions.min = scale.originalOptions.ticks.min;
338
+ tickOptions.max = scale.originalOptions.ticks.max;
339
+ }
340
+ });
341
+
342
+ helpers.each(chartInstance.data.datasets, function(dataset, id) {
343
+ dataset._meta = null;
344
+ });
345
+
346
+ chartInstance.update();
347
+ };
348
+
349
+ },
350
+ beforeInit: function(chartInstance) {
351
+ chartInstance.zoom = {};
352
+
353
+ var node = chartInstance.zoom.node = chartInstance.chart.ctx.canvas;
354
+
355
+ var options = chartInstance.options;
356
+ var panThreshold = helpers.getValueOrDefault(options.pan ? options.pan.threshold : undefined, zoomNS.defaults.pan.threshold);
357
+ if (!options.zoom || !options.zoom.enabled) {
358
+ return;
359
+ }
360
+ if (options.zoom.drag) {
361
+ // Only want to zoom horizontal axis
362
+ options.zoom.mode = 'x';
363
+
364
+ chartInstance.zoom._mouseDownHandler = function(event) {
365
+ chartInstance.zoom._dragZoomStart = event;
366
+ };
367
+ node.addEventListener('mousedown', chartInstance.zoom._mouseDownHandler);
368
+
369
+ chartInstance.zoom._mouseMoveHandler = function(event){
370
+ if (chartInstance.zoom._dragZoomStart) {
371
+ chartInstance.zoom._dragZoomEnd = event;
372
+ chartInstance.update(0);
373
+ }
374
+ };
375
+ node.addEventListener('mousemove', chartInstance.zoom._mouseMoveHandler);
376
+
377
+ chartInstance.zoom._mouseUpHandler = function(event){
378
+ if (chartInstance.zoom._dragZoomStart) {
379
+ var chartArea = chartInstance.chartArea;
380
+ var yAxis = getYAxis(chartInstance);
381
+ var beginPoint = chartInstance.zoom._dragZoomStart;
382
+ var offsetX = beginPoint.target.getBoundingClientRect().left;
383
+ var startX = Math.min(beginPoint.clientX, event.clientX) - offsetX;
384
+ var endX = Math.max(beginPoint.clientX, event.clientX) - offsetX;
385
+ var dragDistance = endX - startX;
386
+ var chartDistance = chartArea.right - chartArea.left;
387
+ var zoom = 1 + ((chartDistance - dragDistance) / chartDistance );
388
+
389
+ // Remove drag start and end before chart update to stop drawing selected area
390
+ chartInstance.zoom._dragZoomStart = null;
391
+ chartInstance.zoom._dragZoomEnd = null;
392
+
393
+ if (dragDistance > 0) {
394
+ doZoom(chartInstance, zoom, {
395
+ x: (dragDistance / 2) + startX,
396
+ y: (yAxis.bottom - yAxis.top) / 2,
397
+ });
398
+ }
399
+ }
400
+ };
401
+ node.addEventListener('mouseup', chartInstance.zoom._mouseUpHandler);
402
+ } else {
403
+ chartInstance.zoom._wheelHandler = function(event) {
404
+ var rect = event.target.getBoundingClientRect();
405
+ var offsetX = event.clientX - rect.left;
406
+ var offsetY = event.clientY - rect.top;
407
+
408
+ var center = {
409
+ x : offsetX,
410
+ y : offsetY
411
+ };
412
+
413
+ if (event.deltaY < 0) {
414
+ doZoom(chartInstance, 1.1, center);
415
+ } else {
416
+ doZoom(chartInstance, 0.909, center);
417
+ }
418
+ // Prevent the event from triggering the default behavior (eg. Content scrolling).
419
+ event.preventDefault();
420
+ };
421
+
422
+ node.addEventListener('wheel', chartInstance.zoom._wheelHandler);
423
+ }
424
+
425
+ if (Hammer) {
426
+ var mc = new Hammer.Manager(node);
427
+ mc.add(new Hammer.Pinch());
428
+ mc.add(new Hammer.Pan({
429
+ threshold: panThreshold
430
+ }));
431
+
432
+ // Hammer reports the total scaling. We need the incremental amount
433
+ var currentPinchScaling;
434
+ var handlePinch = function handlePinch(e) {
435
+ var diff = 1 / (currentPinchScaling) * e.scale;
436
+ var rect = e.target.getBoundingClientRect();
437
+ var offsetX = e.center.x - rect.left;
438
+ var offsetY = e.center.y - rect.top;
439
+ var center = {
440
+ x : offsetX,
441
+ y : offsetY
442
+ };
443
+
444
+ // fingers position difference
445
+ var x = Math.abs(e.pointers[0].clientX - e.pointers[1].clientX);
446
+ var y = Math.abs(e.pointers[0].clientY - e.pointers[1].clientY);
447
+
448
+ // diagonal fingers will change both (xy) axes
449
+ var p = x / y;
450
+ var xy;
451
+ if (p > 0.3 && p < 1.7) {
452
+ xy = 'xy';
453
+ }
454
+ // x axis
455
+ else if (x > y) {
456
+ xy = 'x';
457
+ }
458
+ // y axis
459
+ else {
460
+ xy = 'y';
461
+ }
462
+
463
+ doZoom(chartInstance, diff, center, xy);
464
+
465
+ // Keep track of overall scale
466
+ currentPinchScaling = e.scale;
467
+ };
468
+
469
+ mc.on('pinchstart', function(e) {
470
+ currentPinchScaling = 1; // reset tracker
471
+ });
472
+ mc.on('pinch', handlePinch);
473
+ mc.on('pinchend', function(e) {
474
+ handlePinch(e);
475
+ currentPinchScaling = null; // reset
476
+ zoomNS.zoomCumulativeDelta = 0;
477
+ });
478
+
479
+ var currentDeltaX = null, currentDeltaY = null, panning = false;
480
+ var handlePan = function handlePan(e) {
481
+ if (currentDeltaX !== null && currentDeltaY !== null) {
482
+ panning = true;
483
+ var deltaX = e.deltaX - currentDeltaX;
484
+ var deltaY = e.deltaY - currentDeltaY;
485
+ currentDeltaX = e.deltaX;
486
+ currentDeltaY = e.deltaY;
487
+ doPan(chartInstance, deltaX, deltaY);
488
+ }
489
+ };
490
+
491
+ mc.on('panstart', function(e) {
492
+ currentDeltaX = 0;
493
+ currentDeltaY = 0;
494
+ handlePan(e);
495
+ });
496
+ mc.on('panmove', handlePan);
497
+ mc.on('panend', function(e) {
498
+ currentDeltaX = null;
499
+ currentDeltaY = null;
500
+ zoomNS.panCumulativeDelta = 0;
501
+ setTimeout(function() { panning = false; }, 500);
502
+ });
503
+
504
+ chartInstance.zoom._ghostClickHandler = function(e) {
505
+ if (panning) {
506
+ e.stopImmediatePropagation();
507
+ e.preventDefault();
508
+ }
509
+ };
510
+ node.addEventListener('click', chartInstance.zoom._ghostClickHandler);
511
+
512
+ chartInstance._mc = mc;
513
+ }
514
+ },
515
+
516
+ beforeDatasetsDraw: function(chartInstance) {
517
+ var ctx = chartInstance.chart.ctx;
518
+ var chartArea = chartInstance.chartArea;
519
+ ctx.save();
520
+ ctx.beginPath();
521
+
522
+ if (chartInstance.zoom._dragZoomEnd) {
523
+ var yAxis = getYAxis(chartInstance);
524
+ var beginPoint = chartInstance.zoom._dragZoomStart;
525
+ var endPoint = chartInstance.zoom._dragZoomEnd;
526
+ var offsetX = beginPoint.target.getBoundingClientRect().left;
527
+ var startX = Math.min(beginPoint.clientX, endPoint.clientX) - offsetX;
528
+ var endX = Math.max(beginPoint.clientX, endPoint.clientX) - offsetX;
529
+ var rectWidth = endX - startX;
530
+
531
+
532
+ ctx.fillStyle = 'rgba(225,225,225,0.3)';
533
+ ctx.lineWidth = 5;
534
+ ctx.fillRect(startX, yAxis.top, rectWidth, yAxis.bottom - yAxis.top);
535
+ }
536
+
537
+ ctx.rect(chartArea.left, chartArea.top, chartArea.right - chartArea.left, chartArea.bottom - chartArea.top);
538
+ ctx.clip();
539
+ },
540
+
541
+ afterDatasetsDraw: function(chartInstance) {
542
+ chartInstance.chart.ctx.restore();
543
+ },
544
+
545
+ destroy: function(chartInstance) {
546
+ if (chartInstance.zoom) {
547
+ var options = chartInstance.options;
548
+ var node = chartInstance.zoom.node;
549
+
550
+ if (options.zoom && options.zoom.drag) {
551
+ node.removeEventListener('mousedown', chartInstance.zoom._mouseDownHandler);
552
+ node.removeEventListener('mousemove', chartInstance.zoom._mouseMoveHandler);
553
+ node.removeEventListener('mouseup', chartInstance.zoom._mouseUpHandler);
554
+ } else {
555
+ node.removeEventListener('wheel', chartInstance.zoom._wheelHandler);
556
+ }
557
+
558
+ if (Hammer) {
559
+ node.removeEventListener('click', chartInstance.zoom._ghostClickHandler);
560
+ }
561
+
562
+ delete chartInstance.zoom;
563
+
564
+ var mc = chartInstance._mc;
565
+ if (mc) {
566
+ mc.remove('pinchstart');
567
+ mc.remove('pinch');
568
+ mc.remove('pinchend');
569
+ mc.remove('panstart');
570
+ mc.remove('pan');
571
+ mc.remove('panend');
572
+ }
573
+ }
574
+ }
575
+ };
576
+
577
+ module.exports = zoomPlugin;
578
+ Chart.pluginService.register(zoomPlugin);
579
+
580
+ },{"chart.js":1,"hammerjs":1}]},{},[2]);
@@ -0,0 +1,15 @@
1
+ /*
2
+ * This is a manifest file that'll be compiled into application.css, which will include all the files
3
+ * listed below.
4
+ *
5
+ * Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets,
6
+ * or any plugin's vendor/assets/stylesheets directory can be referenced here using a relative path.
7
+ *
8
+ * You're free to add application-wide styles to this file and they'll appear at the bottom of the
9
+ * compiled file so the styles you add here take precedence over styles defined in any other CSS/SCSS
10
+ * files in this directory. Styles in this file should be added after the last require_* statement.
11
+ * It is generally better to create a new file per style scope.
12
+ *
13
+ *= require_tree .
14
+ *= require_self
15
+ */
@@ -0,0 +1,7 @@
1
+ module Chartjs
2
+ module Zoomable
3
+ class ApplicationController < ActionController::Base
4
+ protect_from_forgery with: :exception
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,6 @@
1
+ module Chartjs
2
+ module Zoomable
3
+ module ApplicationHelper
4
+ end
5
+ end
6
+ end
@@ -0,0 +1,6 @@
1
+ module Chartjs
2
+ module Zoomable
3
+ class ApplicationJob < ActiveJob::Base
4
+ end
5
+ end
6
+ end
@@ -0,0 +1,8 @@
1
+ module Chartjs
2
+ module Zoomable
3
+ class ApplicationMailer < ActionMailer::Base
4
+ default from: 'from@example.com'
5
+ layout 'mailer'
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,7 @@
1
+ module Chartjs
2
+ module Zoomable
3
+ class ApplicationRecord < ActiveRecord::Base
4
+ self.abstract_class = true
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,16 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>Chartjs zoomable</title>
5
+ <%= csrf_meta_tags %>
6
+ <%= csp_meta_tag %>
7
+
8
+ <%= stylesheet_link_tag "chartjs/zoomable/application", media: "all" %>
9
+ <%= javascript_include_tag "chartjs/zoomable/application" %>
10
+ </head>
11
+ <body>
12
+
13
+ <%= yield %>
14
+
15
+ </body>
16
+ </html>
data/config/routes.rb ADDED
@@ -0,0 +1,2 @@
1
+ Chartjs::Zoomable::Engine.routes.draw do
2
+ end
@@ -0,0 +1,7 @@
1
+ require "chartjs/zoomable/engine"
2
+
3
+ module Chartjs
4
+ module Zoomable
5
+ # Your code goes here...
6
+ end
7
+ end
@@ -0,0 +1,7 @@
1
+ module Chartjs
2
+ module Zoomable
3
+ class Engine < ::Rails::Engine
4
+ isolate_namespace Chartjs::Zoomable
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,5 @@
1
+ module Chartjs
2
+ module Zoomable
3
+ VERSION = '1.0.0'
4
+ end
5
+ end
@@ -0,0 +1,4 @@
1
+ # desc "Explaining what the task does"
2
+ # task :chartjs_zoomable do
3
+ # # Task goes here
4
+ # end
metadata ADDED
@@ -0,0 +1,105 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: chartjs-zoomable
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Mateusz Michalski
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2018-11-21 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rails
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: 5.2.1
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: 5.2.1
27
+ - !ruby/object:Gem::Dependency
28
+ name: chartjs-ror
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: 3.6.2
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: 3.6.2
41
+ - !ruby/object:Gem::Dependency
42
+ name: sqlite3
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ description: chartjs-zoomable lets you use Chartjs graphs using helper methods in
56
+ your views with zoom and pan functionality
57
+ email:
58
+ - nytorian@icloud.com
59
+ executables: []
60
+ extensions: []
61
+ extra_rdoc_files: []
62
+ files:
63
+ - MIT-LICENSE
64
+ - README.md
65
+ - Rakefile
66
+ - app/assets/config/chartjs_zoomable_manifest.js
67
+ - app/assets/javascripts/chartjs/zoomable/application.js
68
+ - app/assets/javascripts/chartjs/zoomable/chartjs-plugin-zoom.js
69
+ - app/assets/stylesheets/chartjs/zoomable/application.css
70
+ - app/controllers/chartjs/zoomable/application_controller.rb
71
+ - app/helpers/chartjs/zoomable/application_helper.rb
72
+ - app/jobs/chartjs/zoomable/application_job.rb
73
+ - app/mailers/chartjs/zoomable/application_mailer.rb
74
+ - app/models/chartjs/zoomable/application_record.rb
75
+ - app/views/layouts/chartjs/zoomable/application.html.erb
76
+ - config/routes.rb
77
+ - lib/chartjs/zoomable.rb
78
+ - lib/chartjs/zoomable/engine.rb
79
+ - lib/chartjs/zoomable/version.rb
80
+ - lib/tasks/chartjs/zoomable_tasks.rake
81
+ homepage: https://github.com/Nytorian/chartjs-zoomable
82
+ licenses:
83
+ - MIT
84
+ metadata: {}
85
+ post_install_message:
86
+ rdoc_options: []
87
+ require_paths:
88
+ - lib
89
+ required_ruby_version: !ruby/object:Gem::Requirement
90
+ requirements:
91
+ - - ">="
92
+ - !ruby/object:Gem::Version
93
+ version: '0'
94
+ required_rubygems_version: !ruby/object:Gem::Requirement
95
+ requirements:
96
+ - - ">="
97
+ - !ruby/object:Gem::Version
98
+ version: '0'
99
+ requirements: []
100
+ rubyforge_project:
101
+ rubygems_version: 2.5.1
102
+ signing_key:
103
+ specification_version: 4
104
+ summary: Simplifies usage of Chart.js in Rails views with zoom and pan plugin!
105
+ test_files: []