Track code size on PRs
[dygraphs.git] / gallery / interaction-api.js
1 /*global Dygraph */
2 // Code for a variety of interaction models. Used in interaction.html, but split out from
3 // that file so they can be tested in isolation.
4 //
5 function downV3(event, g, context) {
6 context.initializeMouseDown(event, g, context);
7 if (event.altKey || event.shiftKey) {
8 Dygraph.startZoom(event, g, context);
9 } else {
10 Dygraph.startPan(event, g, context);
11 }
12 }
13
14 function moveV3(event, g, context) {
15 if (context.isPanning) {
16 Dygraph.movePan(event, g, context);
17 } else if (context.isZooming) {
18 Dygraph.moveZoom(event, g, context);
19 }
20 }
21
22 function upV3(event, g, context) {
23 if (context.isPanning) {
24 Dygraph.endPan(event, g, context);
25 } else if (context.isZooming) {
26 Dygraph.endZoom(event, g, context);
27 }
28 }
29
30 // Take the offset of a mouse event on the dygraph canvas and
31 // convert it to a pair of percentages from the bottom left.
32 // (Not top left, bottom is where the lower value is.)
33 function offsetToPercentage(g, offsetX, offsetY) {
34 // This is calculating the pixel offset of the leftmost date.
35 var xOffset = g.toDomCoords(g.xAxisRange()[0], null)[0];
36 var yar0 = g.yAxisRange(0);
37
38 // This is calculating the pixel of the higest value. (Top pixel)
39 var yOffset = g.toDomCoords(null, yar0[1])[1];
40
41 // x y w and h are relative to the corner of the drawing area,
42 // so that the upper corner of the drawing area is (0, 0).
43 var x = offsetX - xOffset;
44 var y = offsetY - yOffset;
45
46 // This is computing the rightmost pixel, effectively defining the
47 // width.
48 var w = g.toDomCoords(g.xAxisRange()[1], null)[0] - xOffset;
49
50 // This is computing the lowest pixel, effectively defining the height.
51 var h = g.toDomCoords(null, yar0[0])[1] - yOffset;
52
53 // Percentage from the left.
54 var xPct = w === 0 ? 0 : (x / w);
55 // Percentage from the top.
56 var yPct = h === 0 ? 0 : (y / h);
57
58 // The (1-) part below changes it from "% distance down from the top"
59 // to "% distance up from the bottom".
60 return [xPct, (1-yPct)];
61 }
62
63 function dblClickV3(event, g, context) {
64 // Reducing by 20% makes it 80% the original size, which means
65 // to restore to original size it must grow by 25%
66
67 if (!(event.offsetX && event.offsetY)){
68 event.offsetX = event.layerX - event.target.offsetLeft;
69 event.offsetY = event.layerY - event.target.offsetTop;
70 }
71
72 var percentages = offsetToPercentage(g, event.offsetX, event.offsetY);
73 var xPct = percentages[0];
74 var yPct = percentages[1];
75
76 if (event.ctrlKey) {
77 zoom(g, -0.25, xPct, yPct);
78 } else {
79 zoom(g, +0.2, xPct, yPct);
80 }
81 }
82
83 var lastClickedGraph = null;
84
85 function clickV3(event, g, context) {
86 lastClickedGraph = g;
87 Dygraph.cancelEvent(event);
88 }
89
90 function scrollV3(event, g, context) {
91 if (lastClickedGraph != g) {
92 return;
93 }
94 var normal = event.detail ? event.detail * -1 : event.wheelDelta / 40;
95 // For me the normalized value shows 0.075 for one click. If I took
96 // that verbatim, it would be a 7.5%.
97 var percentage = normal / 50;
98
99 if (!(event.offsetX && event.offsetY)){
100 event.offsetX = event.layerX - event.target.offsetLeft;
101 event.offsetY = event.layerY - event.target.offsetTop;
102 }
103
104 var percentages = offsetToPercentage(g, event.offsetX, event.offsetY);
105 var xPct = percentages[0];
106 var yPct = percentages[1];
107
108 zoom(g, percentage, xPct, yPct);
109 Dygraph.cancelEvent(event);
110 }
111
112 // Adjusts [x, y] toward each other by zoomInPercentage%
113 // Split it so the left/bottom axis gets xBias/yBias of that change and
114 // tight/top gets (1-xBias)/(1-yBias) of that change.
115 //
116 // If a bias is missing it splits it down the middle.
117 function zoom(g, zoomInPercentage, xBias, yBias) {
118 xBias = xBias || 0.5;
119 yBias = yBias || 0.5;
120 function adjustAxis(axis, zoomInPercentage, bias) {
121 var delta = axis[1] - axis[0];
122 var increment = delta * zoomInPercentage;
123 var foo = [increment * bias, increment * (1-bias)];
124 return [ axis[0] + foo[0], axis[1] - foo[1] ];
125 }
126 var yAxes = g.yAxisRanges();
127 var newYAxes = [];
128 for (var i = 0; i < yAxes.length; i++) {
129 newYAxes[i] = adjustAxis(yAxes[i], zoomInPercentage, yBias);
130 }
131
132 g.updateOptions({
133 dateWindow: adjustAxis(g.xAxisRange(), zoomInPercentage, xBias),
134 valueRange: newYAxes[0]
135 });
136 }
137
138 var v4Active = false;
139 var v4Canvas = null;
140
141 function downV4(event, g, context) {
142 context.initializeMouseDown(event, g, context);
143 v4Active = true;
144 moveV4(event, g, context); // in case the mouse went down on a data point.
145 }
146
147 var processed = [];
148
149 function moveV4(event, g, context) {
150 var RANGE = 7;
151
152 if (v4Active) {
153 var graphPos = Dygraph.findPos(g.graphDiv);
154 var canvasx = Dygraph.pageX(event) - graphPos.x;
155 var canvasy = Dygraph.pageY(event) - graphPos.y;
156
157 var rows = g.numRows();
158 // Row layout:
159 // [date, [val1, stdev1], [val2, stdev2]]
160 for (var row = 0; row < rows; row++) {
161 var date = g.getValue(row, 0);
162 var x = g.toDomCoords(date, null)[0];
163 var diff = Math.abs(canvasx - x);
164 if (diff < RANGE) {
165 for (var col = 1; col < 3; col++) {
166 // TODO(konigsberg): these will throw exceptions as data is removed.
167 var vals = g.getValue(row, col);
168 if (vals === null || vals === undefined) { continue; }
169 var val = vals[0];
170 var y = g.toDomCoords(null, val)[1];
171 var diff2 = Math.abs(canvasy - y);
172 if (diff2 < RANGE) {
173 var found = false;
174 for (var i in processed) {
175 var stored = processed[i];
176 if(stored[0] == row && stored[1] == col) {
177 found = true;
178 break;
179 }
180 }
181 if (!found) {
182 processed.push([row, col]);
183 drawV4(x, y);
184 }
185 return;
186 }
187 }
188 }
189 }
190 }
191 }
192
193 function upV4(event, g, context) {
194 if (v4Active) {
195 v4Active = false;
196 }
197 }
198
199 function dblClickV4(event, g, context) {
200 restorePositioning(g);
201 }
202
203 function drawV4(x, y) {
204 var ctx = v4Canvas;
205
206 ctx.strokeStyle = "#000000";
207 ctx.fillStyle = "#FFFF00";
208 ctx.beginPath();
209 ctx.arc(x,y,5,0,Math.PI*2,true);
210 ctx.closePath();
211 ctx.stroke();
212 ctx.fill();
213 }
214
215 function captureCanvas(canvas, area, g) {
216 v4Canvas = canvas;
217 }
218
219 function restorePositioning(g) {
220 g.updateOptions({
221 dateWindow: null,
222 valueRange: null
223 });
224 }