0
|
1 /*
|
|
2 * FuzzyTimelineWidget.js
|
|
3 *
|
|
4 * Copyright (c) 2013, Sebastian Kruse. All rights reserved.
|
|
5 *
|
|
6 * This library is free software; you can redistribute it and/or
|
|
7 * modify it under the terms of the GNU Lesser General Public
|
|
8 * License as published by the Free Software Foundation; either
|
|
9 * version 3 of the License, or (at your option) any later version.
|
|
10 *
|
|
11 * This library is distributed in the hope that it will be useful,
|
|
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
14 * Lesser General Public License for more details.
|
|
15 *
|
|
16 * You should have received a copy of the GNU Lesser General Public
|
|
17 * License along with this library; if not, write to the Free Software
|
|
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
|
19 * MA 02110-1301 USA
|
|
20 */
|
|
21
|
|
22 /**
|
|
23 * @class FuzzyTimelineWidget
|
|
24 * FuzzyTimelineWidget Implementation
|
|
25 * @author Sebastian Kruse (skruse@mpiwg-berlin.mpg.de)
|
|
26 *
|
|
27 * @param {WidgetWrapper} core wrapper for interaction to other widgets
|
|
28 * @param {HTML object} div parent div to append the FuzzyTimeline widget div
|
|
29 * @param {JSON} options user specified configuration that overwrites options in FuzzyTimelineConfig.js
|
|
30 */
|
|
31 FuzzyTimelineWidget = function(core, div, options) {
|
|
32
|
|
33 this.datasets;
|
|
34 this.selected = undefined;
|
|
35 this.overallMin;
|
|
36 this.overallMax;
|
|
37
|
|
38 this.core = core;
|
|
39 this.core.setWidget(this);
|
|
40
|
|
41 this.options = (new FuzzyTimelineConfig(options)).options;
|
|
42 this.gui = new FuzzyTimelineGui(this, div, this.options);
|
|
43
|
|
44 this.viewMode;
|
|
45 this.density;
|
|
46 this.rangeSlider;
|
|
47 this.rangeBars;
|
|
48 this.rangePiechart;
|
|
49 this.spanHash = [];
|
|
50
|
|
51 this.handles = [];
|
|
52 this.zoomFactor = 1;
|
|
53
|
|
54 this.scaleMode = "normal";
|
|
55 }
|
|
56
|
|
57 FuzzyTimelineWidget.prototype = {
|
|
58
|
|
59 initWidget : function(data) {
|
|
60 var fuzzyTimeline = this;
|
|
61
|
|
62 delete fuzzyTimeline.overallMin;
|
|
63 delete fuzzyTimeline.overallMax;
|
|
64
|
|
65 $(fuzzyTimeline.gui.plotDiv).empty();
|
|
66 $(fuzzyTimeline.gui.sliderTable).empty();
|
|
67 delete fuzzyTimeline.rangeSlider;
|
|
68 $(fuzzyTimeline.gui.rangePiechartDiv).empty();
|
|
69 delete fuzzyTimeline.rangePiechart;
|
|
70
|
|
71 fuzzyTimeline.switchViewMode("density");
|
|
72
|
|
73 if ( (data instanceof Array) && (data.length > 0) )
|
|
74 {
|
|
75 fuzzyTimeline.datasets = data;
|
|
76
|
|
77 $(fuzzyTimeline.datasets).each(function(){
|
|
78 $(this.objects).each(function(){
|
|
79 var datemin,datemax;
|
|
80 if (this.isTemporal){
|
|
81 //TODO: allow more than one date
|
|
82 datemin = moment(this.dates[0].date);
|
|
83 datemax = datemin;
|
|
84 } else if (this.isFuzzyTemporal){
|
|
85 //TODO: allow more than one date
|
|
86 datemin = this.TimeSpanBegin;
|
|
87 datemax = this.TimeSpanEnd;
|
|
88 }
|
|
89
|
|
90 if (typeof fuzzyTimeline.overallMin === "undefined")
|
|
91 fuzzyTimeline.overallMin = datemin;
|
|
92 if (typeof fuzzyTimeline.overallMax === "undefined")
|
|
93 fuzzyTimeline.overallMax = datemax;
|
|
94
|
|
95 if (fuzzyTimeline.overallMin > datemin)
|
|
96 fuzzyTimeline.overallMin = datemin;
|
|
97 if (fuzzyTimeline.overallMax < datemax)
|
|
98 fuzzyTimeline.overallMax = datemax;
|
|
99 });
|
|
100 });
|
|
101
|
|
102 fuzzyTimeline.rangeSlider = new FuzzyTimelineRangeSlider(fuzzyTimeline);
|
|
103 fuzzyTimeline.rangeSlider.initialize(fuzzyTimeline.datasets);
|
|
104
|
|
105 fuzzyTimeline.rangePiechart = new FuzzyTimelineRangePiechart(fuzzyTimeline, fuzzyTimeline.gui.rangePiechartDiv);
|
|
106 fuzzyTimeline.rangePiechart.initialize(fuzzyTimeline.datasets);
|
|
107 }
|
|
108 },
|
|
109
|
|
110 switchViewMode : function(viewMode){
|
|
111 var fuzzyTimeline = this;
|
|
112 if (viewMode !== fuzzyTimeline.viewMode){
|
|
113 $(fuzzyTimeline.gui.plotDiv).empty();
|
|
114 if (viewMode === "density"){
|
|
115 fuzzyTimeline.density = new FuzzyTimelineDensity(fuzzyTimeline,fuzzyTimeline.gui.plotDiv);
|
|
116 } else if (viewMode === "barchart"){
|
|
117 fuzzyTimeline.rangeBars = new FuzzyTimelineRangeBars(fuzzyTimeline);
|
|
118 }
|
|
119 fuzzyTimeline.viewMode = viewMode;
|
|
120 }
|
|
121 },
|
|
122
|
|
123 scaleData : function(data){
|
|
124 var fuzzyTimeline = this;
|
|
125 if (fuzzyTimeline.scaleMode == "normal"){
|
|
126 return data;
|
|
127 } else if (fuzzyTimeline.scaleMode == "logarithm"){
|
|
128 for(var index in data){
|
|
129 var val = data[index];
|
|
130 if (val!=0){
|
|
131 var sign = 1;
|
|
132 if (val<0){
|
|
133 sign = -1;
|
|
134 }
|
|
135 data[index] = sign*Math.log(Math.abs(data[index])+1);
|
|
136 }
|
|
137 }
|
|
138 return data;
|
|
139 } else if (fuzzyTimeline.scaleMode == "percentage"){
|
|
140 var overallCnt = 0;
|
|
141 for(var index in data){
|
|
142 var val = data[index];
|
|
143 if (val > 0){
|
|
144 overallCnt += val;
|
|
145 }
|
|
146 }
|
|
147 //make 1 = 100%
|
|
148 overallCnt = overallCnt/100;
|
|
149 if (overallCnt != 0){
|
|
150 for(var index in data){
|
|
151 data[index] = (data[index])/overallCnt;
|
|
152 }
|
|
153 }
|
|
154 return data;
|
|
155 }
|
|
156 },
|
|
157
|
|
158 changeScaleMode : function(scaleMode) {
|
|
159 var fuzzyTimeline = this;
|
|
160 fuzzyTimeline.scaleMode = scaleMode;
|
|
161 fuzzyTimeline.drawFuzzyTimeline();
|
|
162 },
|
|
163
|
|
164 slidePositionChanged : function(spanWidth) {
|
|
165 var fuzzyTimeline = this;
|
|
166 fuzzyTimeline.spanWidth = spanWidth;
|
|
167 fuzzyTimeline.drawFuzzyTimeline();
|
|
168 },
|
|
169
|
|
170 drawFuzzyTimeline : function(){
|
|
171 var fuzzyTimeline = this;
|
|
172 var datasets = fuzzyTimeline.datasets;
|
|
173 if (fuzzyTimeline.viewMode === "density"){
|
|
174 //redraw density plot
|
|
175 fuzzyTimeline.density.drawDensityPlot(datasets);
|
|
176 //select currently selected data (if there is any)
|
|
177 fuzzyTimeline.density.selectionChanged(fuzzyTimeline.selected);
|
|
178 } else if (fuzzyTimeline.viewMode === "barchart"){
|
|
179 //redraw range plot
|
|
180 fuzzyTimeline.rangeBars.drawRangeBarChart(datasets,fuzzyTimeline.spanWidth);
|
|
181 //select currently selected data (if there is any)
|
|
182 fuzzyTimeline.rangeBars.selectionChanged(fuzzyTimeline.selected);
|
|
183 }
|
|
184 },
|
|
185
|
|
186 highlightChanged : function(objects) {
|
|
187 var fuzzyTimeline = this;
|
|
188 if( !GeoTemConfig.highlightEvents ){
|
|
189 return;
|
|
190 }
|
|
191 if ( (typeof objects === "undefined") || (objects.length == 0) ){
|
|
192 return;
|
|
193 }
|
|
194 if (fuzzyTimeline.viewMode === "density")
|
|
195 this.density.highlightChanged(objects);
|
|
196 else if (fuzzyTimeline.viewMode === "barchart")
|
|
197 this.rangeBars.highlightChanged(objects);
|
|
198
|
|
199 fuzzyTimeline.rangePiechart.highlightChanged(objects);
|
|
200 },
|
|
201
|
|
202 selectionChanged : function(selection) {
|
|
203 var fuzzyTimeline = this;
|
|
204 if( !GeoTemConfig.selectionEvents ){
|
|
205 return;
|
|
206 }
|
|
207 if ((typeof selection.objects !== "undefined")&&
|
|
208 (selection.objects.length == GeoTemConfig.datasets.length))
|
|
209 fuzzyTimeline.selected = selection.objects;
|
|
210 else
|
|
211 delete fuzzyTimeline.selected;
|
|
212 if (fuzzyTimeline.viewMode === "density")
|
|
213 this.density.selectionChanged(fuzzyTimeline.selected);
|
|
214 else if (fuzzyTimeline.viewMode === "barchart")
|
|
215 this.rangeBars.selectionChanged(fuzzyTimeline.selected);
|
|
216
|
|
217 if (selection.valid())
|
|
218 fuzzyTimeline.rangePiechart.selectionChanged(fuzzyTimeline.selected);
|
|
219 else
|
|
220 fuzzyTimeline.rangePiechart.selectionChanged([]);
|
|
221
|
|
222 //selections "overwrite" each other
|
|
223 if (selection.widget != fuzzyTimeline)
|
|
224 fuzzyTimeline.clearHandles();
|
|
225 },
|
|
226
|
|
227 buildSpanArray : function(spanWidth) {
|
|
228 var spanArray = [];
|
|
229 var tickStart = moment(this.overallMin);
|
|
230 do{
|
|
231 spanArray.push(moment(tickStart));
|
|
232 tickStart.add(spanWidth);
|
|
233 } while (tickStart <= this.overallMax);
|
|
234 spanArray.push(moment(tickStart));
|
|
235
|
|
236 this.spanHash.push({spanWidth:spanWidth,overallMin:moment(this.overallMin),spanArray:spanArray});
|
|
237 return(spanArray);
|
|
238 },
|
|
239
|
|
240 getSpanArray : function(spanWidth){
|
|
241 for (var i = 0; i < this.spanHash.length; i++){
|
|
242 var element = this.spanHash[i];
|
|
243 if ( ((this.overallMin-element.overallMin)===0) &&
|
|
244 ((spanWidth-element.spanWidth)===0))
|
|
245 return element.spanArray;
|
|
246 }
|
|
247 return this.buildSpanArray(spanWidth);
|
|
248 },
|
|
249
|
|
250 clearSpanArray : function(){
|
|
251 this.spanHash = [];
|
|
252 },
|
|
253
|
|
254 getTicks : function(dataObject, spanWidth) {
|
|
255 var datemin,datemax;
|
|
256 if (dataObject.isTemporal){
|
|
257 datemin = moment(dataObject.dates[0].date);
|
|
258 datemax = datemin;
|
|
259 } else if (dataObject.isFuzzyTemporal){
|
|
260 datemin = dataObject.TimeSpanBegin;
|
|
261 datemax = dataObject.TimeSpanEnd;
|
|
262 } else{
|
|
263 return;
|
|
264 }
|
|
265
|
|
266 if (typeof spanWidth._data === "undefined"){
|
|
267 //This does only work with millisecond spans, as the length of years is (very) inaccurate.
|
|
268 //(e.g. 100-0 = 99, 2000-1000 = 1001, 5000-0 = 5003, and so on and even more: duration(5000a) = 4932a)
|
|
269 //So the time consuming loop below is needed for accurate dates, when years/months/days etc. are supplied
|
|
270 var firstTick = Math.floor((datemin-this.overallMin)/spanWidth);
|
|
271 var lastTick = Math.floor((datemax-this.overallMin)/spanWidth);
|
|
272 //calculate how much the first (and last) tick and the time-span overlap
|
|
273 var firstTickPercentage = 1;
|
|
274 var lastTickPercentage = 1;
|
|
275 if (firstTick != lastTick){
|
|
276 var secondTickStart = this.overallMin+(firstTick+1)*spanWidth;
|
|
277 var lastTickStart = this.overallMin+lastTick*spanWidth;
|
|
278 firstTickPercentage = (secondTickStart-datemin)/spanWidth;
|
|
279 lastTickPercentage = (datemax-lastTickStart)/spanWidth;
|
|
280 }
|
|
281 if (firstTickPercentage === 0){
|
|
282 firstTick++;
|
|
283 firstTickPercentage = 1;
|
|
284 }
|
|
285 if (lastTickPercentage === 0){
|
|
286 lastTick--;
|
|
287 lastTickPercentage = 1;
|
|
288 }
|
|
289 } else {
|
|
290 var spanArray = this.getSpanArray(spanWidth);
|
|
291 var firstTick, lastTick;
|
|
292 var tickCount = 0;
|
|
293 var tickStart = spanArray[0];
|
|
294 var lastTickStart;
|
|
295 do{
|
|
296 lastTickStart = spanArray[tickCount];
|
|
297 tickCount++;
|
|
298 tickStart = spanArray[tickCount];
|
|
299 if ( (typeof firstTick === "undefined") && (datemin < tickStart) ){
|
|
300 firstTick = tickCount-1;
|
|
301 firstTickPercentage = (tickStart - datemin)/spanWidth;
|
|
302 }
|
|
303 if ( (typeof lastTick === "undefined") && (datemax <= tickStart) ){
|
|
304 lastTick = tickCount-1;
|
|
305 lastTickPercentage = (datemax - lastTickStart)/spanWidth;
|
|
306 }
|
|
307 } while (tickStart < datemax);
|
|
308 if (firstTick == lastTick){
|
|
309 firstTickPercentage = 1;
|
|
310 lastTickPercentage = 1;
|
|
311 }
|
|
312 }
|
|
313
|
|
314 return({ firstTick:firstTick,
|
|
315 lastTick:lastTick,
|
|
316 firstTickPercentage:firstTickPercentage,
|
|
317 lastTickPercentage:lastTickPercentage});
|
|
318 },
|
|
319
|
|
320 getObjects : function(dateStart, dateEnd) {
|
|
321 var fuzzyTimeline = this;
|
|
322 var searchDateStart, searchDateEnd;
|
|
323 if (typeof dateStart !== "undefined")
|
|
324 searchDateStart = moment(dateStart);
|
|
325 if (typeof dateEnd !== "undefined")
|
|
326 searchDateEnd = moment(dateEnd);
|
|
327
|
|
328 var datasets = [];
|
|
329 $(fuzzyTimeline.datasets).each(function(){
|
|
330 var objects = [];
|
|
331 //check if we got "real" datasets, or just array of objects
|
|
332 var datasetObjects = this;
|
|
333 if (typeof this.objects !== "undefined")
|
|
334 datasetObjects = this.objects;
|
|
335 $(datasetObjects).each(function(){
|
|
336 var datemin,datemax;
|
|
337 var dataObject = this;
|
|
338 if (dataObject.isTemporal){
|
|
339 datemin = moment(dataObject.dates[0].date);
|
|
340 datemax = datemin;
|
|
341 } else if (dataObject.isFuzzyTemporal){
|
|
342 datemin = dataObject.TimeSpanBegin;
|
|
343 datemax = dataObject.TimeSpanEnd;
|
|
344 } else{
|
|
345 return;
|
|
346 }
|
|
347
|
|
348 if (typeof searchDateEnd === 'undefined'){
|
|
349 if ( (datemin <= searchDateStart) && (datemax >= searchDateStart) )
|
|
350 objects.push(this);
|
|
351 } else {
|
|
352 if ((datemin < searchDateEnd) && (datemax >= searchDateStart))
|
|
353 objects.push(this);
|
|
354 }
|
|
355 });
|
|
356 datasets.push(objects);
|
|
357 });
|
|
358
|
|
359 return(datasets);
|
|
360 },
|
|
361
|
|
362 triggerHighlight : function(highlightedObjects){
|
|
363 var fuzzyTimeline = this;
|
|
364 if (fuzzyTimeline.viewMode === "density")
|
|
365 fuzzyTimeline.density.highlightChanged(highlightedObjects);
|
|
366 else if (fuzzyTimeline.viewMode === "barchart")
|
|
367 fuzzyTimeline.rangeBars.highlightChanged(highlightedObjects);
|
|
368
|
|
369 fuzzyTimeline.core.triggerHighlight(highlightedObjects);
|
|
370 },
|
|
371
|
|
372 triggerSelection : function(selectedObjects){
|
|
373 var fuzzyTimeline = this;
|
|
374 fuzzyTimeline.selected = selectedObjects;
|
|
375 if (fuzzyTimeline.viewMode === "density")
|
|
376 fuzzyTimeline.density.selectionChanged(selectedObjects);
|
|
377 else if (fuzzyTimeline.viewMode === "barchart")
|
|
378 fuzzyTimeline.rangeBars.selectionChanged(selectedObjects);
|
|
379
|
|
380 selection = new Selection(selectedObjects);
|
|
381
|
|
382 fuzzyTimeline.core.triggerSelection(selection);
|
|
383 },
|
|
384
|
|
385 addHandle : function(x1,x2){
|
|
386 var fuzzyTimeline = this;
|
|
387 //make sure the interval is ordered correctly
|
|
388 if (x2<x1){
|
|
389 var temp = x1;
|
|
390 x1 = x2;
|
|
391 x2 = temp;
|
|
392 }
|
|
393 fuzzyTimeline.handles.push({x1:x1,x2:x2});
|
|
394 fuzzyTimeline.drawHandles();
|
|
395 //enabled "play" button
|
|
396 $(fuzzyTimeline.rangeSlider.startAnimation).removeClass("playDisabled").addClass("playEnabled");
|
|
397 },
|
|
398
|
|
399 selectByX : function(x1,x2){
|
|
400 var fuzzyTimeline = this;
|
|
401 if (fuzzyTimeline.viewMode === "density"){
|
|
402 fuzzyTimeline.density.selectByX(x1,x2);
|
|
403 } else if (fuzzyTimeline.viewMode === "barchart"){
|
|
404 fuzzyTimeline.rangeBars.selectByX(x1,x2);
|
|
405 }
|
|
406 },
|
|
407
|
|
408 drawHandles : function(){
|
|
409 var fuzzyTimeline = this;
|
|
410
|
|
411 $(fuzzyTimeline.gui.plotDiv).find(".plotHandle").remove();
|
|
412 $(fuzzyTimeline.gui.plotDiv).find(".dragTimeRangeAlt").remove();
|
|
413 $(fuzzyTimeline.gui.plotDiv).find(".plotHandleBox").remove();
|
|
414
|
|
415 var plotHeight = (fuzzyTimeline.density.plot?fuzzyTimeline.density.plot:fuzzyTimeline.rangeBars.plot).height();
|
|
416 var plotWidth = (fuzzyTimeline.density.plot?fuzzyTimeline.density.plot:fuzzyTimeline.rangeBars.plot).width();
|
|
417 //flot sends the wrong width if we extend the parent div, so scale it accordingly
|
|
418 plotWidth = plotWidth*fuzzyTimeline.zoomFactor;
|
|
419 var plotOffset = (fuzzyTimeline.density.plot?fuzzyTimeline.density.plot:fuzzyTimeline.rangeBars.plot).getPlotOffset().left;
|
|
420
|
|
421 $(fuzzyTimeline.handles).each(function(){
|
|
422 var handle = this;
|
|
423
|
|
424 var moveLeftHandle = function(){
|
|
425 leftHandle.style.left = handle.x1-$(leftHandle).width() + "px";
|
|
426 };
|
|
427
|
|
428 var moveRightHandle = function(){
|
|
429 rightHandle.style.left = handle.x2+ "px";
|
|
430 };
|
|
431
|
|
432 var resizeHandleBox = function(){
|
|
433 handleBox.style.left = handle.x1+"px";
|
|
434 $(handleBox).width(handle.x2-handle.x1);
|
|
435 };
|
|
436
|
|
437 var moveDragButton = function(){
|
|
438 dragButton.style.left = (handle.x1+handle.x2)/2 - $(dragButton).width()/2 + "px";
|
|
439 };
|
|
440
|
|
441 var leftHandle = document.createElement("div");
|
|
442 leftHandle.title = GeoTemConfig.getString('leftHandle');
|
|
443 leftHandle.style.backgroundImage = "url(" + GeoTemConfig.path + "leftHandle.png" + ")";
|
|
444 leftHandle.setAttribute('class', 'plotHandle plotHandleIcon');
|
|
445 leftHandle.style.visibility = "visible";
|
|
446 $(fuzzyTimeline.gui.plotDiv).append(leftHandle);
|
|
447 moveLeftHandle();
|
|
448 leftHandle.style.top = plotHeight/2-$(leftHandle).height()/2 + "px";
|
|
449
|
|
450 var rightHandle = document.createElement("div");
|
|
451 rightHandle.title = GeoTemConfig.getString('leftHandle');
|
|
452 rightHandle.style.backgroundImage = "url(" + GeoTemConfig.path + "rightHandle.png" + ")";
|
|
453 rightHandle.setAttribute('class', 'plotHandle plotHandleIcon');
|
|
454 rightHandle.style.visibility = "visible";
|
|
455 moveRightHandle();
|
|
456 $(fuzzyTimeline.gui.plotDiv).append(rightHandle);
|
|
457
|
|
458 rightHandle.style.top = plotHeight/2-$(rightHandle).height()/2 + "px";
|
|
459
|
|
460 var handleBox = document.createElement("div");
|
|
461 $(fuzzyTimeline.gui.plotDiv).append(handleBox);
|
|
462 $(handleBox).addClass("plotHandleBox");
|
|
463 resizeHandleBox();
|
|
464 $(handleBox).height(plotHeight);
|
|
465
|
|
466 var dragButton = document.createElement("div");
|
|
467 dragButton.title = GeoTemConfig.getString('dragTimeRange');
|
|
468 dragButton.style.backgroundImage = "url(" + GeoTemConfig.path + "drag.png" + ")";
|
|
469 dragButton.setAttribute('class', 'dragTimeRangeAlt plotHandleIcon');
|
|
470 $(fuzzyTimeline.gui.plotDiv).append(dragButton);
|
|
471 moveDragButton();
|
|
472 dragButton.style.top = plotHeight + "px";
|
|
473
|
|
474 $(leftHandle).mousedown(function(){
|
|
475 $(fuzzyTimeline.gui.plotDiv).mousemove(function(eventObj){
|
|
476 var x = eventObj.clientX;
|
|
477 x += $(fuzzyTimeline.gui.plotDiv).parent().scrollLeft();
|
|
478 if ((x < handle.x2) &&
|
|
479 (x >= plotOffset)){
|
|
480 x = x - leftHandle.offsetWidth;
|
|
481 handle.x1 = x + $(leftHandle).width();
|
|
482
|
|
483 moveLeftHandle();
|
|
484 resizeHandleBox();
|
|
485 moveDragButton();
|
|
486 }
|
|
487 });
|
|
488 $(fuzzyTimeline.gui.plotDiv).mouseup(function(eventObj){
|
|
489 fuzzyTimeline.selectByX(handle.x1,handle.x2);
|
|
490 $(fuzzyTimeline.gui.plotDiv).unbind("mouseup");
|
|
491 $(fuzzyTimeline.gui.plotDiv).unbind("mousemove");
|
|
492 });
|
|
493 });
|
|
494
|
|
495 $(rightHandle).mousedown(function(){
|
|
496 $(fuzzyTimeline.gui.plotDiv).mousemove(function(eventObj){
|
|
497 var x = eventObj.clientX;
|
|
498 x += $(fuzzyTimeline.gui.plotDiv).parent().scrollLeft();
|
|
499 x = x - rightHandle.offsetWidth;
|
|
500 if ((x > handle.x1) &&
|
|
501 (x <= plotOffset+plotWidth)){
|
|
502 handle.x2 = x;
|
|
503
|
|
504 moveRightHandle();
|
|
505 resizeHandleBox();
|
|
506 moveDragButton();
|
|
507 }
|
|
508 });
|
|
509 $(fuzzyTimeline.gui.plotDiv).mouseup(function(eventObj){
|
|
510 fuzzyTimeline.selectByX(handle.x1,handle.x2);
|
|
511 $(fuzzyTimeline.gui.plotDiv).unbind("mouseup");
|
|
512 $(fuzzyTimeline.gui.plotDiv).unbind("mousemove");
|
|
513 });
|
|
514 });
|
|
515
|
|
516 $(dragButton).mousedown(function(){
|
|
517 $(fuzzyTimeline.gui.plotDiv).mousemove(function(eventObj){
|
|
518 var x = eventObj.clientX;
|
|
519 //TODO: for some reason we don't need the scoll offset here
|
|
520 //this should be investigated?
|
|
521 //x += $(fuzzyTimeline.gui.plotDiv).parent().scrollLeft();
|
|
522 var xdiff = x - $(dragButton).offset().left - $(dragButton).width()/2;
|
|
523 handle.x1 = handle.x1+xdiff;
|
|
524 handle.x2 = handle.x2+xdiff;
|
|
525
|
|
526 moveLeftHandle();
|
|
527 moveRightHandle();
|
|
528 resizeHandleBox();
|
|
529 moveDragButton();
|
|
530 });
|
|
531 $(fuzzyTimeline.gui.plotDiv).mouseup(function(eventObj){
|
|
532 if (handle.x1 < plotOffset)
|
|
533 handle.x1 = plotOffset;
|
|
534 if (handle.x2 > plotOffset+plotWidth)
|
|
535 handle.x2 = plotOffset+plotWidth;
|
|
536
|
|
537 moveLeftHandle();
|
|
538 moveRightHandle();
|
|
539 resizeHandleBox();
|
|
540 moveDragButton();
|
|
541
|
|
542 fuzzyTimeline.selectByX(handle.x1,handle.x2);
|
|
543 $(fuzzyTimeline.gui.plotDiv).unbind("mouseup");
|
|
544 $(fuzzyTimeline.gui.plotDiv).unbind("mousemove");
|
|
545 });
|
|
546 });
|
|
547 });
|
|
548 },
|
|
549
|
|
550 clearHandles : function(){
|
|
551 var fuzzyTimeline = this;
|
|
552 $(fuzzyTimeline.gui.plotDiv).find(".plotHandle").remove();
|
|
553 $(fuzzyTimeline.gui.plotDiv).find(".dragTimeRangeAlt").remove();
|
|
554 $(fuzzyTimeline.gui.plotDiv).find(".plotHandleBox").remove();
|
|
555 fuzzyTimeline.handles = [];
|
|
556 //disable buttons
|
|
557 $(fuzzyTimeline.rangeSlider.startAnimation).removeClass("playEnabled").addClass("playDisabled");
|
|
558 $(fuzzyTimeline.rangeSlider.pauseAnimation).removeClass("pauseEnabled").addClass("pauseDisabled");
|
|
559 //stop the animation (if one was running)
|
|
560 fuzzyTimeline.pauseAnimation();
|
|
561 },
|
|
562
|
|
563 startAnimation : function(){
|
|
564 var fuzzyTimeline = this;
|
|
565 fuzzyTimeline.loopFunction = function(steps){
|
|
566 $(fuzzyTimeline.handles).each(function(){
|
|
567 if (typeof steps === "undefined")
|
|
568 steps = 1;
|
|
569
|
|
570 var handle = this;
|
|
571 var x1 = handle.x1;
|
|
572 var x2 = handle.x2;
|
|
573
|
|
574 if (typeof handle.width === "undefined")
|
|
575 handle.width = x2-x1;
|
|
576
|
|
577 var plotWidth = (fuzzyTimeline.density.plot?fuzzyTimeline.density.plot:fuzzyTimeline.rangeBars.plot).width();
|
|
578 var plotOffset = (fuzzyTimeline.density.plot?fuzzyTimeline.density.plot:fuzzyTimeline.rangeBars.plot).getPlotOffset().left;
|
|
579
|
|
580 var plotMax = plotWidth+plotOffset;
|
|
581
|
|
582 //TODO: has to be plotMin
|
|
583 if (!((x1 === plotOffset)&&(x2-x1 <= handle.width))){
|
|
584 x1 += steps;
|
|
585 }
|
|
586 if (x2 <= plotMax){
|
|
587 x2 += steps;
|
|
588 if (x2 > plotMax)
|
|
589 x2 = plotMax;
|
|
590 if (x2-x1 > handle.width){
|
|
591 x1 = x2-handle.width;
|
|
592 }
|
|
593 }
|
|
594 if (x1 >= plotMax){
|
|
595 //TODO: has to be plotMin
|
|
596 x1 = plotOffset;
|
|
597 x2 = plotOffset;
|
|
598 }
|
|
599
|
|
600 handle.x1 = x1;
|
|
601 handle.x2 = x2;
|
|
602
|
|
603 fuzzyTimeline.drawHandles();
|
|
604 fuzzyTimeline.selectByX(handle.x1, handle.x2);
|
|
605 });
|
|
606 };
|
|
607
|
|
608 fuzzyTimeline.loopId = setInterval(function(){
|
|
609 fuzzyTimeline.loopFunction(10);
|
|
610 }, 100);
|
|
611 },
|
|
612
|
|
613 pauseAnimation : function(){
|
|
614 var fuzzyTimeline = this;
|
|
615 clearInterval(fuzzyTimeline.loopId);
|
|
616 $(fuzzyTimeline.handles).each(function(){
|
|
617 var handle = this;
|
|
618 delete handle.width;
|
|
619 });
|
|
620 },
|
|
621
|
|
622 //This function enlargens the plot area
|
|
623 zoomPlot : function(zoomFactor){
|
|
624 var fuzzyTimeline = this;
|
|
625 var oldZoomFactor = fuzzyTimeline.zoomFactor;
|
|
626 fuzzyTimeline.zoomFactor = zoomFactor;
|
|
627 if (zoomFactor > 1){
|
|
628 $(fuzzyTimeline.gui.plotDiv).width(zoomFactor*100+"%");
|
|
629 //leave place for the scrollbar
|
|
630 $(fuzzyTimeline.gui.plotDiv).height(fuzzyTimeline.gui.plotDIVHeight-20);
|
|
631 } else{
|
|
632 $(fuzzyTimeline.gui.plotDiv).width("100%");
|
|
633 $(fuzzyTimeline.gui.plotDiv).height(fuzzyTimeline.gui.plotDIVHeight);
|
|
634 }
|
|
635
|
|
636 //fit handles
|
|
637 //this does not make much sense, as the selections are _completely_ different
|
|
638 //for each scale rate, as the objects may reside in different "ticks" of the graph
|
|
639 $(fuzzyTimeline.handles).each(function(){
|
|
640 var handle = this;
|
|
641 handle.x1 = handle.x1 * (zoomFactor/oldZoomFactor);
|
|
642 handle.x2 = handle.x2 * (zoomFactor/oldZoomFactor);
|
|
643 });
|
|
644 }
|
|
645 };
|