///
import * as dynamicgraph from "vistorian-core/src/dynamicgraph";
import * as messenger from "vistorian-core/src/messenger";
import { SmartSlider } from './smartslider'
import {
RadioButton
} from './ui'
import * as m from 'moment'
export class TimeSlider {
/** VISUALIZATION PARAMETERS */
MARGIN_SLIDER_RIGHT: number = 30;
MARGIN_SLIDER_LEFT: number = 10;
TICK_GAP: number = 2;
TICK_LABEL_GAP: number = 40;
SLIDER_TOP: number = 25;
HEIGHT = 200;
/** GLOBAL VARIABLES */
dgraph: dynamicgraph.DynamicGraph;
slider: SmartSlider;
times: dynamicgraph.Time[];
sliderWidth: number;
widgetWidth: number;
callBack: Function | undefined = undefined;
// function that is called when this time slider's time is changed
propagateButton: RadioButton = new RadioButton('#000000');
labelStart: any; // BEFORE d3.Selection;
labelEnd: any; // BEFORE d3.Selection;
tickScale: any;
tickHeightFunction: Function;
constructor(dgraph: dynamicgraph.DynamicGraph, width: number, callBack?: Function) {
this.dgraph = dgraph;
this.times = dgraph.times().toArray();
this.widgetWidth = width;
var timesDummy = new dynamicgraph.Time(0, this.dgraph);
this.sliderWidth = width - this.MARGIN_SLIDER_RIGHT + 5 - this.MARGIN_SLIDER_LEFT - 5;
var lastDummyYear: m.Moment = this.times.length != 0 ? this.times[this.times.length - 1].moment() : timesDummy.moment(); // WHAT HAPPEND??
var minGran: number = dgraph.gran_min;
var minGranName: m.unitOfTime.DurationConstructor = 'milliseconds';
switch (minGran) {
case 1: minGranName = 'milliseconds'; break;
case 2: minGranName = 'seconds'; break;
case 3: minGranName = 'minutes'; break;
case 4: minGranName = 'hours'; break;
case 5: minGranName = 'days'; break;
case 5: minGranName = 'weeks'; break;
case 6: minGranName = 'months'; break;
case 7: minGranName = 'years'; break;
// case 8: minGranName = 'decades'; break;
// case 9: minGranName = 'centuries'; break;
// case 10: minGranName = 'millenia'; break;
}
if (!lastDummyYear) {
lastDummyYear = m.unix(0);
}
lastDummyYear.add(1, minGranName);
let unixTimeSlider = this.times.length != 0 ? this.times[0].unixTime() : 0; // IS IT OK??
this.slider = new SmartSlider(this.MARGIN_SLIDER_LEFT, this.SLIDER_TOP, this.sliderWidth, unixTimeSlider, lastDummyYear.valueOf(), 1);
if (callBack)
this.callBack = callBack
this.tickScale = d3.time.scale.utc()
.range([this.MARGIN_SLIDER_LEFT, this.MARGIN_SLIDER_LEFT + this.sliderWidth])
.domain([unixTimeSlider, lastDummyYear.valueOf()]);
this.tickHeightFunction = d3.scale.linear()
.range([4, this.SLIDER_TOP - 10])
.domain([dgraph.gran_min, dgraph.gran_max]);
}
appendTo(svg: D3.Selection, x?: number, y?: number) {
if (!x) x = 0
if (!y) y = 0
var g: any = svg.append('g')
.attr('transform', 'translate(' + x + ',' + y + ')')
g.append("g")
.attr('transform', 'translate(0,' + this.SLIDER_TOP + ')')
.attr("class", "x axis")
.call(d3.svg.axis().scale(this.tickScale).orient("top"));
this.labelStart = g.append('text')
.attr('y', this.SLIDER_TOP + 20)
.style('opacity', 0)
.style('font-family', 'Helvetica')
.style('font-weigth', '100')
.style('font-size', '8pt')
.style('text-anchor', 'end')
.text('')
.style('user-select', 'none')
.style('-webkit-user-select', 'none')
.style('-khtml-user-select', 'none')
.style('-moz-user-select', 'none')
.style('-o-user-select', 'none')
.style('user-select', 'none')
this.labelEnd = g.append('text')
.style('opacity', 0)
.attr('y', this.SLIDER_TOP + 20)
.style('font-family', 'Helvetica')
.style('font-weigth', '100')
.style('font-size', '8pt')
.style('text-anchor', 'start')
.text('')
.style('user-select', 'none')
.style('-webkit-user-select', 'none')
.style('-khtml-user-select', 'none')
.style('-moz-user-select', 'none')
.style('-o-user-select', 'none')
.style('user-select', 'none')
this.slider.appendTo(g);
this.slider.setDragEndCallBack((min: any, max: any, single: any) => this.updateTime(min, max, single));
this.propagateButton = new RadioButton('#000000', 'Syncronize Time')
this.propagateButton.appendTo(this.sliderWidth + 15, this.SLIDER_TOP + 8, g)
}
drawTickmarks(granularity: number, tickTimes: dynamicgraph.Time[], svg: D3.Selection) {
var time: dynamicgraph.Time;
var displayLabelSpacing: number = 1; // display every label
while (Math.floor(this.sliderWidth / this.TICK_LABEL_GAP) < (tickTimes.length / displayLabelSpacing) && displayLabelSpacing < 100) {
displayLabelSpacing++;
}
for (var i = 0; i < tickTimes.length; i++) {
if ((i % displayLabelSpacing) == 0) {
svg.append('text')
.attr('x', this.tickScale(tickTimes[i].unixTime()))
.attr('y', this.SLIDER_TOP - this.tickHeightFunction(granularity))
.text(this.formatAtGranularity(tickTimes[i].time(), granularity))
.attr('id', 'timelabel_' + granularity + '_' + i)
.attr('class', 'timelabel')
.style('opacity', .5)
.style('font-family', 'Helvetica')
.style('font-weigth', '100')
.style('font-size', '7pt');
svg.append('line')
.attr('x1', this.tickScale(tickTimes[i].unixTime()))
.attr('x2', this.tickScale(tickTimes[i].unixTime()))
.attr('y1', this.SLIDER_TOP)
.attr('y2', this.SLIDER_TOP - this.tickHeightFunction(granularity))
.style('stroke', '#bbb');
}
}
}
formatAtGranularity(time: m.Moment, granualarity: number) {
switch (granualarity) {
case 0: return time.millisecond();
case 1: return time.second();
case 2: return time.minute();
case 3: return time.hour();
case 4: return time.day();
case 5: return time.week();
case 6: return time.month() + 1;
default: return time.year();
}
}
formatForGranularities(time: dynamicgraph.Time, gran_min: number, gran_max: number) {
var formatString: string = ''
var format: string;
while (gran_max >= gran_min) {
formatString += this.getGranularityFormattingString(gran_max, (gran_max > gran_min));
gran_max--;
}
return time.format(formatString.trim());
}
getGranularityFormattingString(granualarity: any, separator: boolean) {
switch (granualarity) {
case 0: return 'SSS';
case 1: return 'ss' + (separator ? '.' : '');
case 2: return 'mm' + (separator ? ':' : '');
case 3: return 'hh' + (separator ? '' : '');
case 4: return 'DD' + (separator ? ' ' : '');
case 6: return 'MM' + (separator ? '-' : '');
default: return 'YYYY' + (separator ? '-' : '');
}
}
updateTime(minUnix: number, maxUnix: number, single: number) {
// times are still correct here?
var format = function (d: any) { return d.toDateString(); };
single = Math.round(single);
this.labelStart
.attr('x', this.slider.valueRange.invert(minUnix) + 10)
.style('opacity', 1)
.text(format(new Date(minUnix)));
this.labelEnd
.attr('x', this.slider.valueRange.invert(maxUnix) + 10)
.style('opacity', 1)
.text(format(new Date(maxUnix)));
if (this.callBack != undefined)
this.callBack(minUnix, maxUnix, this.propagateButton.isChecked());
else
messenger.timeRange(minUnix, maxUnix, this.times[single], this.propagateButton.isChecked());
}
set(startUnix: number, endUnix: number) {
this.slider.set(startUnix, endUnix)
}
}