Commit 4bbc0e4ac9a371f657e3b5770a80fb7417d45bbd

Authored by Goutte
1 parent db69e5a7

Continue THE GREAT REFACTO

Showing 2 changed files with 1180 additions and 1163 deletions   Show diff stats
web/static/js/main.js
... ... @@ -17,7 +17,7 @@
17 17 * We're using a custom build of 4.9.1, one line changed, see d3-custom.js
18 18 * Event bubbling cannot trigger two rects unless we make an event dispatcher,
19 19 * and d3's brush is stopping propagation, as it should by default.
20   - *
  20 + *
21 21 *
22 22 * Code is weird, with ref$
23 23 * ------------------------
... ... @@ -27,1322 +27,1308 @@
27 27 */
28 28  
29 29 (function () {
30   - let Target;
31   - // let SpaceWeather;
32   - let TimeSeries;
33   - let Orbits;
  30 + // let TimeSeries;
  31 + // let Orbits;
34 32 const global = typeof exports !== 'undefined' && exports || this;
35 33 const GOLDEN_RATIO = 2 / (1 + Math.sqrt(5));
36   - Target = ((() => {
37   - // Target.displayName = 'Target';
38   - // const prototype = Target.prototype;
39   - // const constructor = Target;
40   - function Target(slug, name, config) {
  34 +
  35 + class Target {
  36 + constructor(slug, name, config) {
41 37 this.slug = slug;
42 38 this.name = name;
43 39 this.config = config;
44 40 this.active = this.config.active;
45 41 }
  42 + }
46 43  
47   - return Target;
48   - })());
49   -
50   -
51   - global.SpaceWeather = ((() => {
  44 + // global.SpaceWeather = ((() => {
52 45 // "The main app, instanciated from an inline script.\nIt defaults to an interval starting two months ago, and ending in a month.\n(both at midnight)";
53 46 // SpaceWeather.displayName = 'SpaceWeather';
54 47 // const prototype = SpaceWeather.prototype;
55 48 // const constructor = SpaceWeather;
56   - const API_TIME_FORMAT = "YYYY-MM-DDTHH:mm:ss";
57   - const INPUT_TIME_FORMAT = "YYYY-MM-DD";
58   -
59   - class SpaceWeather {
60   - constructor(configuration) {
61   - const that = this;
62   - this.configuration = configuration;
63   - this.parameters = {};
64   - this.targets = {};
65   - console.info("©2017\n _ _ _ _ ____\n | | | | ___| (_) ___ | _ \\ _ __ ___ _ __ __ _\n | |_| |/ _ \\ | |/ _ \\| |_) | '__/ _ \\| '_ \\ / _` |\n | _ | __/ | | (_) | __/| | | (_) | |_) | (_| |\n |_| |_|\\___|_|_|\\___/|_|_ |_|_ \\___/| .__/ \\__,_|\n | |__ _ _ / ___| _ \\| _ \\| _ \\_|\n | '_ \\| | | | | | | | | | |_) | |_) |\n | |_) | |_| | | |___| |_| | __/| __/\n |_.__/ \\__, | \\____|____/|_| |_|\n |___/\n\nThe full source of this website is available at :\nhttps://gitlab.irap.omp.eu/CDPP/SPACEWEATHERONLINE");
66   -
67   - let targets_configs = [];
68   - for (let k in this.configuration.targets) {
69   - targets_configs.push(this.configuration.targets[k]);
70   - }
71   - targets_configs.forEach(tc => that.addTarget(new Target(tc.slug, tc.name, tc)));
72   -
73   - this.configuration['parameters'].forEach(p => that.parameters[p['id']] = p);
74   - this.orbits = null;
75   - this.time_series = [];
76   - }
77 49  
78   - init(started_at, stopped_at) {
79   - // "This is called by the inline bootstrap javascript code.\nThis ain't in the constructor because it might return a Promise later on.\n(for the loader, for example)";
80   - const this$ = this;
81   - started_at = moment(started_at).hours(0).minutes(0).seconds(0);
82   - stopped_at = moment(stopped_at).hours(0).minutes(0).seconds(0);
83   - this.setStartAndStop(started_at, stopped_at);
84   - this.loadAndCreatePlots(started_at, stopped_at);
85   - window.addEventListener('resize', () => this$.resize());
86   - return this;
87   - }
  50 + const API_TIME_FORMAT = "YYYY-MM-DDTHH:mm:ss";
  51 + const INPUT_TIME_FORMAT = "YYYY-MM-DD";
88 52  
89   - buildDataUrlForTarget(target_slug, started_at, stopped_at) {
90   - let url;
91   - url = this.configuration['api']['data_for_interval'];
92   - url = url.replace('<target>', target_slug);
93   - url = url.replace('<started_at>', started_at);
94   - url = url.replace('<stopped_at>', stopped_at);
95   - return url;
96   - }
  53 + class SpaceWeather {
  54 + constructor(configuration) {
  55 + const that = this;
  56 + this.configuration = configuration;
  57 + this.parameters = {};
  58 + this.targets = {};
  59 + console.info("©2017\n _ _ _ _ ____\n | | | | ___| (_) ___ | _ \\ _ __ ___ _ __ __ _\n | |_| |/ _ \\ | |/ _ \\| |_) | '__/ _ \\| '_ \\ / _` |\n | _ | __/ | | (_) | __/| | | (_) | |_) | (_| |\n |_| |_|\\___|_|_|\\___/|_|_ |_|_ \\___/| .__/ \\__,_|\n | |__ _ _ / ___| _ \\| _ \\| _ \\_|\n | '_ \\| | | | | | | | | | |_) | |_) |\n | |_) | |_| | | |___| |_| | __/| __/\n |_.__/ \\__, | \\____|____/|_| |_|\n |___/\n\nThe full source of this website is available at :\nhttps://gitlab.irap.omp.eu/CDPP/SPACEWEATHERONLINE");
97 60  
98   - buildDownloadUrl() {
99   - let ref$;
100   - let started_at;
101   - let stopped_at;
102   - let targets;
103   - let t;
104   - let url;
105   - ref$ = this.getDomain(), started_at = ref$[0], stopped_at = ref$[1];
106   - targets = (function () {
107   - const results$ = [];
108   - for (t in this.targets) {
109   - if (this.targets[t].active) {
110   - results$.push(t);
111   - }
112   - }
113   - return results$;
114   - }.call(this)).sort().join('-');
115   - url = this.configuration['api']['download'];
116   - url = url.replace('<targets>', targets);
117   - url = url.replace('<started_at>', started_at.format(API_TIME_FORMAT));
118   - url = url.replace('<stopped_at>', stopped_at.format(API_TIME_FORMAT));
119   - return url;
  61 + let targets_configs = [];
  62 + for (let k in this.configuration.targets) {
  63 + targets_configs.push(this.configuration.targets[k]);
120 64 }
  65 + targets_configs.forEach(tc => that.addTarget(new Target(tc.slug, tc.name, tc)));
121 66  
122   - buildSampUrl() {
123   - let ref$;
124   - let started_at;
125   - let stopped_at;
126   - let targets;
127   - let t;
128   - let parameters;
129   - let p;
130   - let url;
131   - ref$ = this.getDomain(), started_at = ref$[0], stopped_at = ref$[1];
132   - targets = (function () {
133   - const results$ = [];
134   - for (t in this.targets) {
135   - if (this.targets[t].active) {
136   - results$.push(t);
137   - }
138   - }
139   - return results$;
140   - }.call(this)).sort().join('-');
141   - parameters = (function () {
142   - const results$ = [];
143   - for (p in this.parameters) {
144   - if (this.parameters[p].active) {
145   - results$.push(p);
146   - }
147   - }
148   - return results$;
149   - }.call(this)).sort().join('-');
150   - url = this.configuration['api']['samp'];
151   - url = url.replace('<targets>', targets);
152   - url = url.replace('<params>', parameters);
153   - url = url.replace('<started_at>', started_at.format(API_TIME_FORMAT));
154   - url = url.replace('<stopped_at>', stopped_at.format(API_TIME_FORMAT));
155   - return url;
156   - }
  67 + this.configuration['parameters'].forEach(p => that.parameters[p['id']] = p);
  68 + this.orbits = null;
  69 + this.time_series = [];
  70 + }
157 71  
158   - buildSampName() {
159   - let ref$;
160   - let started_at;
161   - let stopped_at;
162   - let targets;
163   - let t;
164   - ref$ = this.getDomain(), started_at = ref$[0], stopped_at = ref$[1];
165   - targets = (function () {
166   - let i$;
167   - let ref$;
168   - let len$;
169   - const results$ = [];
170   - for (i$ = 0, len$ = (ref$ = this.getEnabledTargets()).length; i$ < len$; ++i$) {
171   - t = ref$[i$];
172   - results$.push(t.name);
173   - }
174   - return results$;
175   - }.call(this)).sort().join(', ');
176   - return `Heliopropa for ${targets} from ${started_at.format(API_TIME_FORMAT)} to ${stopped_at.format(API_TIME_FORMAT)}.`;
177   - }
  72 + init(started_at, stopped_at) {
  73 + // "This is called by the inline bootstrap javascript code.\nThis ain't in the constructor because it might return a Promise later on.\n(for the loader, for example)";
  74 + const this$ = this;
  75 + started_at = moment(started_at).hours(0).minutes(0).seconds(0);
  76 + stopped_at = moment(stopped_at).hours(0).minutes(0).seconds(0);
  77 + this.setStartAndStop(started_at, stopped_at);
  78 + this.loadAndCreatePlots(started_at, stopped_at);
  79 + window.addEventListener('resize', () => this$.resize());
  80 + return this;
  81 + }
178 82  
179   - addTarget(target) {
180   - this.targets[target.slug] = target;
181   - return this;
182   - }
  83 + buildDataUrlForTarget(target_slug, started_at, stopped_at) {
  84 + let url;
  85 + url = this.configuration['api']['data_for_interval'];
  86 + url = url.replace('<target>', target_slug);
  87 + url = url.replace('<started_at>', started_at);
  88 + url = url.replace('<stopped_at>', stopped_at);
  89 + return url;
  90 + }
183 91  
184   - getEnabledTargets() {
185   - let slug;
186   - let ref$;
187   - let target;
  92 + buildDownloadUrl() {
  93 + let ref$;
  94 + let started_at;
  95 + let stopped_at;
  96 + let targets;
  97 + let t;
  98 + let url;
  99 + ref$ = this.getDomain(), started_at = ref$[0], stopped_at = ref$[1];
  100 + targets = (function () {
188 101 const results$ = [];
189   - for (slug in ref$ = this.targets) {
190   - target = ref$[slug];
191   - if (target.active) {
192   - results$.push(target);
  102 + for (t in this.targets) {
  103 + if (this.targets[t].active) {
  104 + results$.push(t);
193 105 }
194 106 }
195 107 return results$;
196   - }
  108 + }.call(this)).sort().join('-');
  109 + url = this.configuration['api']['download'];
  110 + url = url.replace('<targets>', targets);
  111 + url = url.replace('<started_at>', started_at.format(API_TIME_FORMAT));
  112 + url = url.replace('<stopped_at>', stopped_at.format(API_TIME_FORMAT));
  113 + return url;
  114 + }
197 115  
198   - enableTarget(target_slug) {
199   - let ref$;
200   - const this$ = this;
201   - this.time_series.forEach(ts => {
202   - if (ts.target.slug === target_slug && this$.parameters[ts.parameter].active) {
203   - return ts.show();
  116 + buildSampUrl() {
  117 + let ref$;
  118 + let started_at;
  119 + let stopped_at;
  120 + let targets;
  121 + let t;
  122 + let parameters;
  123 + let p;
  124 + let url;
  125 + ref$ = this.getDomain(), started_at = ref$[0], stopped_at = ref$[1];
  126 + targets = (function () {
  127 + const results$ = [];
  128 + for (t in this.targets) {
  129 + if (this.targets[t].active) {
  130 + results$.push(t);
204 131 }
205   - });
206   - this.targets[target_slug].active = true;
207   - if ((ref$ = this.orbits) != null) {
208   - ref$.enableTarget(target_slug);
209 132 }
210   - return this;
211   - }
  133 + return results$;
  134 + }.call(this)).sort().join('-');
  135 + parameters = (function () {
  136 + const results$ = [];
  137 + for (p in this.parameters) {
  138 + if (this.parameters[p].active) {
  139 + results$.push(p);
  140 + }
  141 + }
  142 + return results$;
  143 + }.call(this)).sort().join('-');
  144 + url = this.configuration['api']['samp'];
  145 + url = url.replace('<targets>', targets);
  146 + url = url.replace('<params>', parameters);
  147 + url = url.replace('<started_at>', started_at.format(API_TIME_FORMAT));
  148 + url = url.replace('<stopped_at>', stopped_at.format(API_TIME_FORMAT));
  149 + return url;
  150 + }
212 151  
213   - disableTarget(target_slug) {
  152 + buildSampName() {
  153 + let ref$;
  154 + let started_at;
  155 + let stopped_at;
  156 + let targets;
  157 + let t;
  158 + ref$ = this.getDomain(), started_at = ref$[0], stopped_at = ref$[1];
  159 + targets = (function () {
  160 + let i$;
214 161 let ref$;
215   - this.time_series.forEach(ts => {
216   - if (ts.target.slug === target_slug) {
217   - return ts.hide();
218   - }
219   - });
220   - this.targets[target_slug].active = false;
221   - if ((ref$ = this.orbits) != null) {
222   - ref$.disableTarget(target_slug);
  162 + let len$;
  163 + const results$ = [];
  164 + for (i$ = 0, len$ = (ref$ = this.getEnabledTargets()).length; i$ < len$; ++i$) {
  165 + t = ref$[i$];
  166 + results$.push(t.name);
  167 + }
  168 + return results$;
  169 + }.call(this)).sort().join(', ');
  170 + return `Heliopropa for ${targets} from ${started_at.format(API_TIME_FORMAT)} to ${stopped_at.format(API_TIME_FORMAT)}.`;
  171 + }
  172 +
  173 + addTarget(target) {
  174 + this.targets[target.slug] = target;
  175 + return this;
  176 + }
  177 +
  178 + getEnabledTargets() {
  179 + let slug;
  180 + let ref$;
  181 + let target;
  182 + const results$ = [];
  183 + for (slug in ref$ = this.targets) {
  184 + target = ref$[slug];
  185 + if (target.active) {
  186 + results$.push(target);
223 187 }
224   - return this;
225 188 }
  189 + return results$;
  190 + }
226 191  
227   - resize() {
228   - let ref$;
229   - if ((ref$ = this.orbits) != null) {
230   - ref$.resize();
  192 + enableTarget(target_slug) {
  193 + let ref$;
  194 + const this$ = this;
  195 + this.time_series.forEach(ts => {
  196 + if (ts.target.slug === target_slug && this$.parameters[ts.parameter].active) {
  197 + return ts.show();
231 198 }
232   - this.time_series.forEach(ts => ts.resize());
233   - return this;
  199 + });
  200 + this.targets[target_slug].active = true;
  201 + if ((ref$ = this.orbits) != null) {
  202 + ref$.enableTarget(target_slug);
234 203 }
  204 + return this;
  205 + }
235 206  
236   - showLoader() {
237   - return $('#plots_loader').show();
  207 + disableTarget(target_slug) {
  208 + let ref$;
  209 + this.time_series.forEach(ts => {
  210 + if (ts.target.slug === target_slug) {
  211 + return ts.hide();
  212 + }
  213 + });
  214 + this.targets[target_slug].active = false;
  215 + if ((ref$ = this.orbits) != null) {
  216 + ref$.disableTarget(target_slug);
238 217 }
  218 + return this;
  219 + }
239 220  
240   - hideLoader() {
241   - return $('#plots_loader').hide();
  221 + resize() {
  222 + let ref$;
  223 + if ((ref$ = this.orbits) != null) {
  224 + ref$.resize();
242 225 }
  226 + this.time_series.forEach(ts => ts.resize());
  227 + return this;
  228 + }
243 229  
244   - loadData(target_slug, started_at, stopped_at) {
245   - // "Load the data as CSV for the specified target and interval,\nand return it in a Promise.";
246   - let sw;
247   - sw = this;
248   - return new Promise((resolve, reject) => {
249   - let url;
250   - url = sw.buildDataUrlForTarget(target_slug, started_at, stopped_at);
251   - return d3.csv(url, csv => {
252   - let timeFormat;
253   - let data;
254   - console.debug(`Requested CSV for ${target_slug}…`, csv);
255   - timeFormat = d3.utcParse('%Y-%m-%dT%H:%M:%S%Z');
256   - data = {
257   - 'hee': []
258   - };
259   - configuration['parameters'].forEach(parameter => data[parameter['id']] = []);
260   - if (!csv) {
261   - reject('invalid');
262   - }
263   - if (!csv.length) {
264   - reject('empty');
265   - }
266   - csv.forEach(d => {
267   - let dtime;
268   - dtime = timeFormat(d['time']);
269   - configuration['parameters'].forEach(parameter => {
270   - let id;
271   - let val;
272   - id = parameter['id'];
273   - val = parseFloat(d[id]);
274   - if (!isNaN(val)) {
275   - return data[id].push({
276   - x: dtime,
277   - y: val
278   - });
279   - }
280   - });
281   - if (d['xhee'] && d['yhee']) {
282   - return data['hee'].push({
283   - t: dtime,
284   - x: parseFloat(d['xhee']),
285   - y: parseFloat(d['yhee'])
  230 + showLoader() {
  231 + return $('#plots_loader').show();
  232 + }
  233 +
  234 + hideLoader() {
  235 + return $('#plots_loader').hide();
  236 + }
  237 +
  238 + loadData(target_slug, started_at, stopped_at) {
  239 + // "Load the data as CSV for the specified target and interval,\nand return it in a Promise.";
  240 + let sw;
  241 + sw = this;
  242 + return new Promise((resolve, reject) => {
  243 + let url;
  244 + url = sw.buildDataUrlForTarget(target_slug, started_at, stopped_at);
  245 + return d3.csv(url, csv => {
  246 + let timeFormat;
  247 + let data;
  248 + console.debug(`Requested CSV for ${target_slug}…`, csv);
  249 + timeFormat = d3.utcParse('%Y-%m-%dT%H:%M:%S%Z');
  250 + data = {
  251 + 'hee': []
  252 + };
  253 + configuration['parameters'].forEach(parameter => data[parameter['id']] = []);
  254 + if (!csv) {
  255 + reject('invalid');
  256 + }
  257 + if (!csv.length) {
  258 + reject('empty');
  259 + }
  260 + csv.forEach(d => {
  261 + let dtime;
  262 + dtime = timeFormat(d['time']);
  263 + configuration['parameters'].forEach(parameter => {
  264 + let id;
  265 + let val;
  266 + id = parameter['id'];
  267 + val = parseFloat(d[id]);
  268 + if (!isNaN(val)) {
  269 + return data[id].push({
  270 + x: dtime,
  271 + y: val
286 272 });
287 273 }
288 274 });
289   - return resolve(data);
  275 + if (d['xhee'] && d['yhee']) {
  276 + return data['hee'].push({
  277 + t: dtime,
  278 + x: parseFloat(d['xhee']),
  279 + y: parseFloat(d['yhee'])
  280 + });
  281 + }
290 282 });
  283 + return resolve(data);
291 284 });
292   - }
  285 + });
  286 + }
293 287  
294   - loadAndCreatePlots(started_at, stopped_at) {
295   - // "started_at: moment(.js) datetime object\nstopped_at: moment(.js) datetime object";
296   - let targets;
297   - let res$;
298   - let k;
299   - let handleTarget;
300   - const this$ = this;
301   - this.showLoader();
302   - this.started_at = started_at;
303   - this.stopped_at = stopped_at;
304   - this.orbits = new Orbits(this.configuration.orbits_container, this.configuration);
305   - started_at = started_at.format(API_TIME_FORMAT);
306   - stopped_at = stopped_at.format(API_TIME_FORMAT);
307   - res$ = [];
308   - for (k in this.targets) {
309   - res$.push(this.targets[k]);
  288 + loadAndCreatePlots(started_at, stopped_at) {
  289 + // "started_at: moment(.js) datetime object\nstopped_at: moment(.js) datetime object";
  290 + let targets;
  291 + let res$;
  292 + let k;
  293 + let handleTarget;
  294 + const this$ = this;
  295 + this.showLoader();
  296 + this.started_at = started_at;
  297 + this.stopped_at = stopped_at;
  298 + this.orbits = new Orbits(this.configuration.orbits_container, this.configuration);
  299 + started_at = started_at.format(API_TIME_FORMAT);
  300 + stopped_at = stopped_at.format(API_TIME_FORMAT);
  301 + res$ = [];
  302 + for (k in this.targets) {
  303 + res$.push(this.targets[k]);
  304 + }
  305 + targets = res$;
  306 + targets.forEach(target => {
  307 + let targetButton;
  308 + targetButton = $(`.targets-filters .target.${target.slug}`);
  309 + targetButton.addClass('loading');
  310 + return targetButton.removeClass('failed error empty');
  311 + });
  312 + handleTarget = i => {
  313 + let target;
  314 + let targetButton;
  315 + if (i >= targets.length) {
  316 + return;
310 317 }
311   - targets = res$;
312   - targets.forEach(target => {
313   - let targetButton;
314   - targetButton = $(`.targets-filters .target.${target.slug}`);
315   - targetButton.addClass('loading');
316   - return targetButton.removeClass('failed error empty');
317   - });
318   - handleTarget = i => {
319   - let target;
320   - let targetButton;
321   - if (i >= targets.length) {
322   - return;
323   - }
324   - target = targets[i];
325   - console.info(`Loading CSV data of ${target.name}…`);
326   - targetButton = $(`.targets-filters .target.${target.slug}`);
327   - return this$.loadData(target.slug, started_at, stopped_at).then(data => {
328   - console.info(`Loaded CSV data of ${target.name}.`, data);
329   - this$.createTimeSeries(target, data);
330   - this$.orbits.initOrbiter(target.slug, target.config, data['hee']);
331   - targetButton.removeClass('loading');
332   - if (target.active) {
333   - this$.hideLoader();
334   - } else {
335   - this$.disableTarget(target.slug);
336   - }
337   - return handleTarget(i + 1);
338   - }, error => {
339   - let msg;
340   - switch (error) {
341   - case 'invalid':
342   - console.error(`Failed loading CSV data of ${target.name}.`);
343   - targetButton.addClass('error');
344   - break;
345   - case 'empty':
346   - msg = `No data for ${target.name}\n during interval from \n${started_at} to ${stopped_at}.`;
347   - console.warn(msg);
348   - targetButton.addClass('empty');
349   - break;
350   - }
351   - targetButton.addClass('failed');
352   - targetButton.removeClass('loading');
  318 + target = targets[i];
  319 + console.info(`Loading CSV data of ${target.name}…`);
  320 + targetButton = $(`.targets-filters .target.${target.slug}`);
  321 + return this$.loadData(target.slug, started_at, stopped_at).then(data => {
  322 + console.info(`Loaded CSV data of ${target.name}.`, data);
  323 + this$.createTimeSeries(target, data);
  324 + this$.orbits.initOrbiter(target.slug, target.config, data['hee']);
  325 + targetButton.removeClass('loading');
  326 + if (target.active) {
353 327 this$.hideLoader();
354   - return handleTarget(i + 1);
355   - });
356   - };
357   - handleTarget(0);
358   - return this;
359   - }
360   -
361   - clearPlots() {
362   - this.orbits.clear();
363   - this.time_series.forEach(ts => ts.clear());
364   - this.orbits = null;
365   - this.time_series = [];
366   - return this;
367   - }
368   -
369   - createTimeSeries(target, data) {
370   - const this$ = this;
371   - this.configuration['parameters'].forEach(parameter => {
372   - let container;
373   - let id;
374   - let title;
375   - container = this$.configuration['time_series_container'];
376   - id = parameter['id'];
377   - title = parameter['title'];
378   - if (!(id in data)) {
379   - console.error(`No data for id '${id}'.`, data);
  328 + } else {
  329 + this$.disableTarget(target.slug);
380 330 }
381   - console.log(target['name'], id, data[id]);
382   - if (data[id].length) {
383   - return this$.time_series.push(new TimeSeries(id, title, target, data[id], this$.parameters[id].active, container, {
384   - 'started_at': this$.started_at,
385   - 'stopped_at': this$.stopped_at
386   - }));
  331 + return handleTarget(i + 1);
  332 + }, error => {
  333 + let msg;
  334 + switch (error) {
  335 + case 'invalid':
  336 + console.error(`Failed loading CSV data of ${target.name}.`);
  337 + targetButton.addClass('error');
  338 + break;
  339 + case 'empty':
  340 + msg = `No data for ${target.name}\n during interval from \n${started_at} to ${stopped_at}.`;
  341 + console.warn(msg);
  342 + targetButton.addClass('empty');
  343 + break;
387 344 }
  345 + targetButton.addClass('failed');
  346 + targetButton.removeClass('loading');
  347 + this$.hideLoader();
  348 + return handleTarget(i + 1);
388 349 });
389   - this.time_series.forEach(ts => {
390   - ts.options['onMouseOver'] = () => true;
391   - ts.options['onMouseOut'] = () => {
392   - this$.time_series.forEach(ts2 => ts2.hideCursor());
393   - return true;
394   - };
395   - ts.options['onMouseMove'] = t => {
396   - let ref$;
397   - this$.time_series.forEach(ts2 => ts2.moveCursor(t));
398   - if ((ref$ = this$.orbits) != null) {
399   - ref$.moveToDate(t);
400   - }
401   - return true;
402   - };
403   - ts.options['onBrushEnd'] = (sta, sto) => {
404   - this$.resizeDomain(moment(sta), moment(sto));
405   - return true;
406   - };
407   - return ts.options['onDblClick'] = () => {
408   - let ref$;
409   - this$.resetZoom();
410   - if ((ref$ = $("#zoom_controls_help")) != null) {
411   - ref$.remove();
412   - }
413   - return true;
414   - };
415   - });
416   - return this.time_series;
417   - }
418   -
419   - getEnabledParameters() {
420   - let i$;
421   - let ref$;
422   - let len$;
423   - let p;
424   - let slug;
425   - const results$ = [];
426   - for (i$ = 0, len$ = (ref$ = this.parameters).length; i$ < len$; ++i$) {
427   - p = i$;
428   - slug = ref$[i$];
429   - if (p.active) {
430   - results$.push(p);
431   - }
432   - }
  350 + };
  351 + handleTarget(0);
  352 + return this;
  353 + }
433 354  
434   - return results$;
435   - }
  355 + clearPlots() {
  356 + this.orbits.clear();
  357 + this.time_series.forEach(ts => ts.clear());
  358 + this.orbits = null;
  359 + this.time_series = [];
  360 + return this;
  361 + }
436 362  
437   - enableParameter(parameter_slug) {
438   - const this$ = this;
439   - if (!(parameter_slug in this.parameters)) {
440   - console.error(`Unknown parameter ${parameter_slug}.`);
  363 + createTimeSeries(target, data) {
  364 + const this$ = this;
  365 + this.configuration['parameters'].forEach(parameter => {
  366 + let container;
  367 + let id;
  368 + let title;
  369 + container = this$.configuration['time_series_container'];
  370 + id = parameter['id'];
  371 + title = parameter['title'];
  372 + if (!(id in data)) {
  373 + console.error(`No data for id '${id}'.`, data);
  374 + }
  375 + console.log(target['name'], id, data[id]);
  376 + if (data[id].length) {
  377 + return this$.time_series.push(new TimeSeries(id, title, target, data[id], this$.parameters[id].active, container, {
  378 + 'started_at': this$.started_at,
  379 + 'stopped_at': this$.stopped_at
  380 + }));
441 381 }
442   - this.parameters[parameter_slug].active = true;
443   - this.time_series.forEach(ts => {
444   - if (ts.parameter === parameter_slug && this$.targets[ts.target.slug].active) {
445   - return ts.show();
  382 + });
  383 + this.time_series.forEach(ts => {
  384 + ts.options['onMouseOver'] = () => true;
  385 + ts.options['onMouseOut'] = () => {
  386 + this$.time_series.forEach(ts2 => ts2.hideCursor());
  387 + return true;
  388 + };
  389 + ts.options['onMouseMove'] = t => {
  390 + let ref$;
  391 + this$.time_series.forEach(ts2 => ts2.moveCursor(t));
  392 + if ((ref$ = this$.orbits) != null) {
  393 + ref$.moveToDate(t);
446 394 }
447   - });
  395 + return true;
  396 + };
  397 + ts.options['onBrushEnd'] = (sta, sto) => {
  398 + this$.resizeDomain(moment(sta), moment(sto));
  399 + return true;
  400 + };
  401 + return ts.options['onDblClick'] = () => {
  402 + let ref$;
  403 + this$.resetZoom();
  404 + if ((ref$ = $("#zoom_controls_help")) != null) {
  405 + ref$.remove();
  406 + }
  407 + return true;
  408 + };
  409 + });
  410 + return this.time_series;
  411 + }
448 412  
449   - return this;
  413 + getEnabledParameters() {
  414 + let i$;
  415 + let ref$;
  416 + let len$;
  417 + let p;
  418 + let slug;
  419 + const results$ = [];
  420 + for (i$ = 0, len$ = (ref$ = this.parameters).length; i$ < len$; ++i$) {
  421 + p = i$;
  422 + slug = ref$[i$];
  423 + if (p.active) {
  424 + results$.push(p);
  425 + }
450 426 }
451 427  
452   - disableParameter(parameter_slug) {
453   - if (!(parameter_slug in this.parameters)) {
454   - console.error(`Unknown parameter ${parameter_slug}.`);
455   - }
456   - this.parameters[parameter_slug].active = false;
457   - this.time_series.forEach(ts => {
458   - if (ts.parameter === parameter_slug) {
459   - return ts.hide();
460   - }
461   - });
  428 + return results$;
  429 + }
462 430  
463   - return this;
  431 + enableParameter(parameter_slug) {
  432 + const this$ = this;
  433 + if (!(parameter_slug in this.parameters)) {
  434 + console.error(`Unknown parameter ${parameter_slug}.`);
464 435 }
  436 + this.parameters[parameter_slug].active = true;
  437 + this.time_series.forEach(ts => {
  438 + if (ts.parameter === parameter_slug && this$.targets[ts.target.slug].active) {
  439 + return ts.show();
  440 + }
  441 + });
465 442  
466   - showCatalogLayer(catalog_slug) {
467   - this.time_series.forEach(ts => ts.showCatalogLayer(catalog_slug));
468   - return this;
469   - }
  443 + return this;
  444 + }
470 445  
471   - hideCatalogLayer(catalog_slug) {
472   - this.time_series.forEach(ts => ts.hideCatalogLayer(catalog_slug));
473   - return this;
  446 + disableParameter(parameter_slug) {
  447 + if (!(parameter_slug in this.parameters)) {
  448 + console.error(`Unknown parameter ${parameter_slug}.`);
474 449 }
475   -
476   - getDomain() {
477   - if (this.current_started_at != null && this.current_stopped_at != null) {
478   - return [this.current_started_at, this.current_stopped_at];
  450 + this.parameters[parameter_slug].active = false;
  451 + this.time_series.forEach(ts => {
  452 + if (ts.parameter === parameter_slug) {
  453 + return ts.hide();
479 454 }
480   - return [this.started_at, this.stopped_at];
  455 + });
  456 +
  457 + return this;
  458 + }
  459 +
  460 + showCatalogLayer(catalog_slug) {
  461 + this.time_series.forEach(ts => ts.showCatalogLayer(catalog_slug));
  462 + return this;
  463 + }
  464 +
  465 + hideCatalogLayer(catalog_slug) {
  466 + this.time_series.forEach(ts => ts.hideCatalogLayer(catalog_slug));
  467 + return this;
  468 + }
  469 +
  470 + getDomain() {
  471 + if (this.current_started_at != null && this.current_stopped_at != null) {
  472 + return [this.current_started_at, this.current_stopped_at];
481 473 }
  474 + return [this.started_at, this.stopped_at];
  475 + }
482 476  
483   - resizeDomain(started_at, stopped_at) {
484   - let ref$;
485   - let max_stopped_at;
486   - let formatted_started_at;
487   - let formatted_stopped_at;
488   - let tsv;
489   - let tsv_cursor;
490   - let tsv_length;
491   - let zoomedOnVisible;
492   - if (stopped_at < started_at) {
493   - ref$ = [stopped_at, started_at], started_at = ref$[0], stopped_at = ref$[1];
494   - }
495   - if (started_at === stopped_at) {
496   - console.warn("Please provide distinct start and stop dates.");
497   - return;
498   - }
499   - max_stopped_at = started_at.clone().add(2, 'years');
500   - if (stopped_at > max_stopped_at) {
501   - console.warn("The time interval was truncated beacuse it was bigger than two years.");
502   - stopped_at = max_stopped_at;
503   - }
504   - this.setStartAndStop(started_at, stopped_at);
505   - formatted_started_at = started_at.format();
506   - formatted_stopped_at = stopped_at.format();
507   - if ((this.started_at <= started_at && started_at <= this.stopped_at) && (this.started_at <= stopped_at && stopped_at <= this.stopped_at)) {
508   - console.info(`Resizing the temporal domain from ${formatted_started_at} to ${formatted_stopped_at} without fetching new data…`);
509   - tsv = this.time_series.filter(ts => ts.visible);
510   - tsv_cursor = 0;
511   - tsv_length = tsv.length;
512   - zoomedOnVisible = new Promise((resolve, reject) => {
513   - let tsv_zoom_on_next;
514   - tsv_zoom_on_next = i => {
515   - let ts;
516   - if (i >= tsv_length) {
517   - resolve();
518   - return;
519   - }
520   - ts = tsv[i];
521   - return ts.zoomIn(started_at, stopped_at).then(() => tsv_zoom_on_next(i + 1));
522   - };
523   - return tsv_zoom_on_next(0);
524   - });
525   - zoomedOnVisible.then(function () {
526   - return this.time_series.forEach(ts => {
527   - if (!ts.visible) {
528   - return ts.zoomIn(started_at, stopped_at);
529   - }
530   - });
  477 + resizeDomain(started_at, stopped_at) {
  478 + let ref$;
  479 + let max_stopped_at;
  480 + let formatted_started_at;
  481 + let formatted_stopped_at;
  482 + let tsv;
  483 + let tsv_cursor;
  484 + let tsv_length;
  485 + let zoomedOnVisible;
  486 + if (stopped_at < started_at) {
  487 + ref$ = [stopped_at, started_at], started_at = ref$[0], stopped_at = ref$[1];
  488 + }
  489 + if (started_at === stopped_at) {
  490 + console.warn("Please provide distinct start and stop dates.");
  491 + return;
  492 + }
  493 + max_stopped_at = started_at.clone().add(2, 'years');
  494 + if (stopped_at > max_stopped_at) {
  495 + console.warn("The time interval was truncated because it was bigger than two years.");
  496 + stopped_at = max_stopped_at;
  497 + }
  498 + this.setStartAndStop(started_at, stopped_at);
  499 + formatted_started_at = started_at.format();
  500 + formatted_stopped_at = stopped_at.format();
  501 + if ((this.started_at <= started_at && started_at <= this.stopped_at) &&
  502 + (this.started_at <= stopped_at && stopped_at <= this.stopped_at)) {
  503 + console.info(`Resizing the temporal domain from ${formatted_started_at} to ${formatted_stopped_at} without fetching new data…`);
  504 + tsv = this.time_series.filter(ts => ts.visible);
  505 + tsv_length = tsv.length;
  506 + zoomedOnVisible = new Promise((resolve, reject) => {
  507 + let tsv_zoom_on_next;
  508 + tsv_zoom_on_next = i => {
  509 + let ts;
  510 + if (i >= tsv_length) {
  511 + resolve();
  512 + return;
  513 + }
  514 + ts = tsv[i];
  515 + ts.zoomIn(started_at, stopped_at)
  516 + .then(() => tsv_zoom_on_next(i + 1));
  517 + };
  518 + return tsv_zoom_on_next(0);
  519 + });
  520 + zoomedOnVisible.then(() => {
  521 + this.time_series.forEach(ts => {
  522 + if (!ts.visible) {
  523 + ts.zoomIn(started_at, stopped_at);
  524 + }
531 525 });
532   - this.orbits.resizeDomain(started_at, stopped_at);
533   - } else {
534   - console.info(`Resizing the temporal domain from ${formatted_started_at} to ${formatted_stopped_at} and fetching new data…`);
535   - console.warn("This might take a good while… Why not see what else we're up to on http://cdpp.eu while you're waiting?");
536   - this.clearPlots();
537   - this.loadAndCreatePlots(started_at, stopped_at);
538   - }
539   - return this;
  526 + });
  527 + this.orbits.resizeDomain(started_at, stopped_at);
  528 + } else {
  529 + console.info(`Resizing the temporal domain from ${formatted_started_at} to ${formatted_stopped_at} and fetching new data…`);
  530 + console.warn("This might take a good while… Why not see what else we're up to on http://cdpp.eu while you're waiting?");
  531 + this.clearPlots();
  532 + this.loadAndCreatePlots(started_at, stopped_at);
540 533 }
  534 + return this;
  535 + }
541 536  
542   - resetZoom() {
543   - this.time_series.forEach(ts => ts.resetZoom());
544   - this.orbits.resetZoom();
545   - this.setStartAndStop(this.started_at, this.stopped_at);
  537 + resetZoom() {
  538 + this.time_series.forEach(ts => ts.resetZoom());
  539 + this.orbits.resetZoom();
  540 + this.setStartAndStop(this.started_at, this.stopped_at);
546 541  
547   - return this;
548   - }
  542 + return this;
  543 + }
549 544  
550   - setStartAndStop(started_at, stopped_at) {
551   - console.info(`Setting time interval from ${started_at} to ${stopped_at}…`);
552   - this.current_started_at = started_at;
553   - this.current_stopped_at = stopped_at;
554   - $("#started_at").val(started_at.format(INPUT_TIME_FORMAT));
555   - $("#stopped_at").val(stopped_at.format(INPUT_TIME_FORMAT));
  545 + setStartAndStop(started_at, stopped_at) {
  546 + console.info(`Setting time interval from ${started_at} to ${stopped_at}…`);
  547 + this.current_started_at = started_at;
  548 + this.current_stopped_at = stopped_at;
  549 + $("#started_at").val(started_at.format(INPUT_TIME_FORMAT));
  550 + $("#stopped_at").val(stopped_at.format(INPUT_TIME_FORMAT));
556 551  
557   - return this;
558   - }
  552 + return this;
559 553 }
  554 + }
560 555  
561   - return SpaceWeather;
562   - })());
  556 + // return SpaceWeather;
  557 + // })());
563 558  
564 559 /////////////////////////////////////////////////////////////////////////////
565 560 //// TIME SERIES ////////////////////////////////////////////////////////////
566 561  
567   - global.TimeSeries = TimeSeries = ((() => {
568   - const RATIO = GOLDEN_RATIO ** 4;
569   -
570   - class TimeSeries {
571   - constructor(parameter, title, target, data, visible, container, options) {
572   - this.onBrushEnd = this.onBrushEnd.bind(this);
573   - this.onDoubleClick = this.onDoubleClick.bind(this);
574   - this.onMouseOut = this.onMouseOut.bind(this);
575   - this.onMouseOver = this.onMouseOver.bind(this);
576   - this.onMouseMove = this.onMouseMove.bind(this);
577   - this.parameter = parameter;
578   - this.title = title;
579   - this.target = target;
580   - this.data = data;
581   - this.visible = visible;
582   - this.container = container;
583   - this.options = options || {};
584   - let now = moment();
585   - const dataLength = this.data.length;
586   - const predictiveData = [];
587   - let d;
588   - for (let i = 0; i < dataLength; ++i) {
589   - d = this.data[i];
590   - if (moment(d.x) >= now) {
591   - predictiveData.push(d);
592   - }
  562 + // global.TimeSeries = TimeSeries = ((() => {
  563 + const RATIO = GOLDEN_RATIO ** 4;
  564 +
  565 + class TimeSeries {
  566 + constructor(parameter, title, target, data, visible, container, options) {
  567 + this.onBrushEnd = this.onBrushEnd.bind(this);
  568 + this.onDoubleClick = this.onDoubleClick.bind(this);
  569 + this.onMouseOut = this.onMouseOut.bind(this);
  570 + this.onMouseOver = this.onMouseOver.bind(this);
  571 + this.onMouseMove = this.onMouseMove.bind(this);
  572 + this.parameter = parameter;
  573 + this.title = title;
  574 + this.target = target;
  575 + this.data = data;
  576 + this.visible = visible;
  577 + this.container = container;
  578 + this.options = options || {};
  579 + let now = moment();
  580 + const dataLength = this.data.length;
  581 + const predictiveData = [];
  582 + let d;
  583 + for (let i = 0; i < dataLength; ++i) {
  584 + d = this.data[i];
  585 + if (moment(d.x) >= now) {
  586 + predictiveData.push(d);
593 587 }
594   - this.predictiveData = predictiveData;
595   - this.init();
596 588 }
  589 + this.predictiveData = predictiveData;
  590 + this.init();
  591 + }
597 592  
598   - toString() {
599   - return `${this.title} of ${this.target.name}`;
600   - }
  593 + toString() {
  594 + return `${this.title} of ${this.target.name}`;
  595 + }
601 596  
602   - init() {
603   - let ref$;
604   - let width;
605   - let height;
606   - let formatMillisecond;
607   - let formatSecond;
608   - let formatMinute;
609   - let formatHour;
610   - let formatDay;
611   - let formatWeek;
612   - let formatMonth;
613   - let formatYear;
614   - let multiFormat;
615   - let clipId;
616   - let i$;
617   - let len$;
618   - let line;
619   - let lineElement;
620   - let dx;
621   - const this$ = this;
622   - console.info(`Initializing plot of ${this}…`);
623   - this.margin = {
624   - top: 30,
625   - right: 20,
626   - bottom: 30,
627   - left: 80
628   - };
629   - ref$ = this.recomputeDimensions(), width = ref$[0], height = ref$[1];
630   - this.xDataExtent = d3.extent(this.data, d => d.x);
631   - this.yDataExtent = d3.extent(this.data, d => d.y);
632   - if (this.options['started_at']) {
633   - this.xDataExtent[0] = this.options['started_at'];
  597 + init() {
  598 + let ref$;
  599 + let width;
  600 + let height;
  601 + let formatMillisecond;
  602 + let formatSecond;
  603 + let formatMinute;
  604 + let formatHour;
  605 + let formatDay;
  606 + let formatWeek;
  607 + let formatMonth;
  608 + let formatYear;
  609 + let multiFormat;
  610 + let clipId;
  611 + let i$;
  612 + let len$;
  613 + let line;
  614 + let lineElement;
  615 + let dx;
  616 + const this$ = this;
  617 + console.info(`Initializing plot of ${this}…`);
  618 + this.margin = {
  619 + top: 30,
  620 + right: 20,
  621 + bottom: 30,
  622 + left: 80
  623 + };
  624 + ref$ = this.recomputeDimensions(), width = ref$[0], height = ref$[1];
  625 + this.xDataExtent = d3.extent(this.data, d => d.x);
  626 + this.yDataExtent = d3.extent(this.data, d => d.y);
  627 + if (this.options['started_at']) {
  628 + this.xDataExtent[0] = this.options['started_at'];
  629 + }
  630 + if (this.options['stopped_at']) {
  631 + this.xDataExtent[1] = this.options['stopped_at'];
  632 + }
  633 + this.xScale = d3.scaleTime().domain(this.xDataExtent);
  634 + this.yScale = d3.scaleLinear().domain(this.yDataExtent);
  635 + formatMillisecond = d3.utcFormat(".%L");
  636 + formatSecond = d3.utcFormat(":%S");
  637 + formatMinute = d3.utcFormat("%H:%M");
  638 + formatHour = d3.utcFormat("%H:%M");
  639 + formatDay = d3.utcFormat("%a %d");
  640 + formatWeek = d3.utcFormat("%b %d");
  641 + formatMonth = d3.utcFormat("%B");
  642 + formatYear = d3.utcFormat("%Y");
  643 + multiFormat = date => {
  644 + if (date > d3.timeSecond(date)) {
  645 + return formatMillisecond(date);
634 646 }
635   - if (this.options['stopped_at']) {
636   - this.xDataExtent[1] = this.options['stopped_at'];
  647 + if (date > d3.timeMinute(date)) {
  648 + return formatSecond(date);
637 649 }
638   - this.xScale = d3.scaleTime().domain(this.xDataExtent);
639   - this.yScale = d3.scaleLinear().domain(this.yDataExtent);
640   - formatMillisecond = d3.utcFormat(".%L");
641   - formatSecond = d3.utcFormat(":%S");
642   - formatMinute = d3.utcFormat("%H:%M");
643   - formatHour = d3.utcFormat("%H:%M");
644   - formatDay = d3.utcFormat("%a %d");
645   - formatWeek = d3.utcFormat("%b %d");
646   - formatMonth = d3.utcFormat("%B");
647   - formatYear = d3.utcFormat("%Y");
648   - multiFormat = date => {
649   - if (date > d3.timeSecond(date)) {
650   - return formatMillisecond(date);
651   - }
652   - if (date > d3.timeMinute(date)) {
653   - return formatSecond(date);
654   - }
655   - if (date > d3.timeHour(date)) {
656   - return formatMinute(date);
657   - }
658   - if (date > d3.timeDay(date)) {
659   - return formatHour(date);
660   - }
661   - if (date > d3.timeMonth(date)) {
662   - if (date > d3.timeWeek(date)) {
663   - return formatDay(date);
664   - } else {
665   - return formatWeek(date);
666   - }
667   - }
668   - if (date > d3.timeYear(date)) {
669   - return formatMonth(date);
670   - }
671   - return formatYear(date);
672   - };
673   - this.xAxis = d3.axisBottom().tickFormat(multiFormat).ticks(7);
674   - this.yAxis = d3.axisLeft().ticks(10);
675   - this.svg = d3.select(this.container).append('svg');
676   - this.svg.attr("class", `${this.parameter} ${this.target.slug}`);
677   - this.line = d3.line().x(d => this$.xScale(d.x)).y(d => this$.yScale(d.y));
678   - this.plotWrapper = this.svg.append('g');
679   - this.plotWrapper.attr('transform', `translate(${this.margin.left},${this.margin.top})`);
680   - clipId = `ts-clip-${this.parameter}-${this.target.slug}`;
681   - this.clip = this.svg.append("defs").append("svg:clipPath").attr("id", clipId).append("svg:rect").attr("x", 0).attr("y", 0);
682   - this.pathWrapper = this.plotWrapper.append('g');
683   - this.pathWrapper.attr("clip-path", `url(#${clipId})`);
684   - this.path = this.pathWrapper.append('path').datum(this.data).classed('line', true);
685   - this.predictiveDataPath = this.pathWrapper.append('path').datum(this.predictiveData).classed('predictive-line', true);
686   - this.createCatalogLayers();
687   - this.horizontalLines = [];
688   - if (this.options['horizontalLines']) {
689   - for (i$ = 0, len$ = (ref$ = this.options['horizontalLines']).length; i$ < len$; ++i$) {
690   - line = ref$[i$];
691   - lineElement = this.svg.append("line").attr("class", "line horitonal-line").style("stroke", "orange").style("stroke-dasharray", "3, 2");
692   - this.horizontalLines.push({
693   - 'element': lineElement,
694   - 'config': line
695   - });
  650 + if (date > d3.timeHour(date)) {
  651 + return formatMinute(date);
  652 + }
  653 + if (date > d3.timeDay(date)) {
  654 + return formatHour(date);
  655 + }
  656 + if (date > d3.timeMonth(date)) {
  657 + if (date > d3.timeWeek(date)) {
  658 + return formatDay(date);
  659 + } else {
  660 + return formatWeek(date);
696 661 }
697 662 }
698   - this.brush = this.plotWrapper.append("g").attr("class", "brush");
699   - this.plotWrapper.append('g').classed('x axis', true);
700   - this.plotWrapper.append('g').classed('y axis', true);
701   - this.yAxisText = this.plotWrapper.append("text").attr("transform", "rotate(-90)").attr("dy", "1em").style("text-anchor", "middle").text(this.title);
702   - this.yAxisTextTarget = this.plotWrapper.append("text").attr("transform", "rotate(-90)").attr("dy", "1em").style("text-anchor", "middle").style("font-style", "oblique").text(this.target.name);
703   - this.focus = this.plotWrapper.append('g').style("display", "none");
704   - this.cursorCircle = this.focus.append("circle").attr("class", "cursor-circle").attr("r", 3);
705   - dx = 8;
706   - this.cursorValueShadow = this.focus.append("text").attr("class", "cursor-text cursor-text-shadow").attr("dx", dx).attr("dy", "-.3em");
707   - this.cursorValue = this.focus.append("text").attr("class", "cursor-text cursor-value").attr("dx", dx).attr("dy", "-.3em");
708   - this.cursorDateShadow = this.focus.append("text").attr("class", "cursor-text cursor-text-shadow").attr("dx", dx).attr("dy", "1em");
709   - this.cursorDate = this.focus.append("text").attr("class", "cursor-text cursor-date").attr("dx", dx).attr("dy", "1em");
710   - this.brushFunction = d3.brushX().extent([[0, 0], [width, height]]).handleSize(0).on("end", this.onBrushEnd);
711   - this.brush.call(this.brushFunction);
712   - this.brushOverlay = this.svg.select(".brush .overlay");
713   - this.brushOverlay.on("mouseover.swapp", this.onMouseOver).on("mouseout.swapp", this.onMouseOut).on("mousemove.swapp", this.onMouseMove).on("dblclick.swapp", this.onDoubleClick);
714   - return this.resize();
715   - }
716   -
717   - recomputeDimensions() {
718   - let width;
719   - let height;
720   - width = Math.ceil($(this.container).width() - this.margin.left - this.margin.right);
721   - height = Math.ceil(RATIO * width);
722   - this.plotWidth = width;
723   - this.plotHeight = height;
724   - return [width, height];
725   - }
726   -
727   - resize() {
728   - let ref$;
729   - let width;
730   - let height;
731   - let i$;
732   - let len$;
733   - let line;
734   - let lineValue;
735   - ref$ = this.recomputeDimensions(), width = ref$[0], height = ref$[1];
736   - console.debug(`Resizing ${this}: ${width} x ${height}…`);
737   - this.xScale.range([0, width]);
738   - this.yScale.range([height, 0]);
739   - this.svg.attr('width', width + this.margin.right + this.margin.left).attr('height', height + this.margin.top + this.margin.bottom);
740   - this.clip.attr("width", width).attr("height", height);
741   - this.path.attr('d', this.line);
742   - this.predictiveDataPath.attr('d', this.line);
743   - for (i$ = 0, len$ = (ref$ = this.horizontalLines).length; i$ < len$; ++i$) {
744   - line = ref$[i$];
745   - lineValue = this.yScale(line['config']['value']) + this.margin.top;
746   - line['element'].attr("x1", this.margin.left).attr("y1", lineValue).attr("x2", this.margin.left + width).attr("y2", lineValue);
  663 + if (date > d3.timeYear(date)) {
  664 + return formatMonth(date);
747 665 }
748   - this.xAxis.scale(this.xScale);
749   - this.yAxis.scale(this.yScale);
750   - this.xAxis.ticks(Math.floor(width / 80.0));
751   - this.yAxis.ticks(Math.floor(height / 18.0));
752   - this.svg.select('.x.axis').attr('transform', `translate(0,${height})`).call(this.xAxis);
753   - this.svg.select('.y.axis').call(this.yAxis);
754   - this.yAxisText.attr("y", 20 - this.margin.left).attr("x", 0 - height / 2);
755   - this.yAxisTextTarget.attr("y", 0 - this.margin.left).attr("x", 0 - height / 2.0);
756   - this.resizeCatalogLayers();
757   - if (!this.visible) {
758   - this.hide();
  666 + return formatYear(date);
  667 + };
  668 + this.xAxis = d3.axisBottom().tickFormat(multiFormat).ticks(7);
  669 + this.yAxis = d3.axisLeft().ticks(10);
  670 + this.svg = d3.select(this.container).append('svg');
  671 + this.svg.attr("class", `${this.parameter} ${this.target.slug}`);
  672 + this.line = d3.line().x(d => this$.xScale(d.x)).y(d => this$.yScale(d.y));
  673 + this.plotWrapper = this.svg.append('g');
  674 + this.plotWrapper.attr('transform', `translate(${this.margin.left},${this.margin.top})`);
  675 + clipId = `ts-clip-${this.parameter}-${this.target.slug}`;
  676 + this.clip = this.svg.append("defs").append("svg:clipPath").attr("id", clipId).append("svg:rect").attr("x", 0).attr("y", 0);
  677 + this.pathWrapper = this.plotWrapper.append('g');
  678 + this.pathWrapper.attr("clip-path", `url(#${clipId})`);
  679 + this.path = this.pathWrapper.append('path').datum(this.data).classed('line', true);
  680 + this.predictiveDataPath = this.pathWrapper.append('path').datum(this.predictiveData).classed('predictive-line', true);
  681 + this.createCatalogLayers();
  682 + this.horizontalLines = [];
  683 + if (this.options['horizontalLines']) {
  684 + for (i$ = 0, len$ = (ref$ = this.options['horizontalLines']).length; i$ < len$; ++i$) {
  685 + line = ref$[i$];
  686 + lineElement = this.svg.append("line").attr("class", "line horitonal-line").style("stroke", "orange").style("stroke-dasharray", "3, 2");
  687 + this.horizontalLines.push({
  688 + 'element': lineElement,
  689 + 'config': line
  690 + });
759 691 }
760   - return this;
761 692 }
  693 + this.brush = this.plotWrapper.append("g").attr("class", "brush");
  694 + this.plotWrapper.append('g').classed('x axis', true);
  695 + this.plotWrapper.append('g').classed('y axis', true);
  696 + this.yAxisText = this.plotWrapper.append("text").attr("transform", "rotate(-90)").attr("dy", "1em").style("text-anchor", "middle").text(this.title);
  697 + this.yAxisTextTarget = this.plotWrapper.append("text").attr("transform", "rotate(-90)").attr("dy", "1em").style("text-anchor", "middle").style("font-style", "oblique").text(this.target.name);
  698 + this.focus = this.plotWrapper.append('g').style("display", "none");
  699 + this.cursorCircle = this.focus.append("circle").attr("class", "cursor-circle").attr("r", 3);
  700 + dx = 8;
  701 + this.cursorValueShadow = this.focus.append("text").attr("class", "cursor-text cursor-text-shadow").attr("dx", dx).attr("dy", "-.3em");
  702 + this.cursorValue = this.focus.append("text").attr("class", "cursor-text cursor-value").attr("dx", dx).attr("dy", "-.3em");
  703 + this.cursorDateShadow = this.focus.append("text").attr("class", "cursor-text cursor-text-shadow").attr("dx", dx).attr("dy", "1em");
  704 + this.cursorDate = this.focus.append("text").attr("class", "cursor-text cursor-date").attr("dx", dx).attr("dy", "1em");
  705 + this.brushFunction = d3.brushX().extent([[0, 0], [width, height]]).handleSize(0).on("end", this.onBrushEnd);
  706 + this.brush.call(this.brushFunction);
  707 + this.brushOverlay = this.svg.select(".brush .overlay");
  708 + this.brushOverlay.on("mouseover.swapp", this.onMouseOver).on("mouseout.swapp", this.onMouseOut).on("mousemove.swapp", this.onMouseMove).on("dblclick.swapp", this.onDoubleClick);
  709 + return this.resize();
  710 + }
762 711  
763   - clear() {
764   - $(this.svg.node()).remove();
765   - return this.visible = false;
766   - }
  712 + recomputeDimensions() {
  713 + let width;
  714 + let height;
  715 + width = Math.ceil($(this.container).width() - this.margin.left - this.margin.right);
  716 + height = Math.ceil(RATIO * width);
  717 + this.plotWidth = width;
  718 + this.plotHeight = height;
  719 + return [width, height];
  720 + }
767 721  
768   - show() {
769   - $(this.svg.node()).show();
770   - return this.visible = true;
771   - }
  722 + resize() {
  723 + let ref$;
  724 + let width;
  725 + let height;
  726 + let i$;
  727 + let len$;
  728 + let line;
  729 + let lineValue;
  730 + ref$ = this.recomputeDimensions(), width = ref$[0], height = ref$[1];
  731 + console.debug(`Resizing ${this}: ${width} x ${height}…`);
  732 + this.xScale.range([0, width]);
  733 + this.yScale.range([height, 0]);
  734 + this.svg.attr('width', width + this.margin.right + this.margin.left).attr('height', height + this.margin.top + this.margin.bottom);
  735 + this.clip.attr("width", width).attr("height", height);
  736 + this.path.attr('d', this.line);
  737 + this.predictiveDataPath.attr('d', this.line);
  738 + for (i$ = 0, len$ = (ref$ = this.horizontalLines).length; i$ < len$; ++i$) {
  739 + line = ref$[i$];
  740 + lineValue = this.yScale(line['config']['value']) + this.margin.top;
  741 + line['element'].attr("x1", this.margin.left).attr("y1", lineValue).attr("x2", this.margin.left + width).attr("y2", lineValue);
  742 + }
  743 + this.xAxis.scale(this.xScale);
  744 + this.yAxis.scale(this.yScale);
  745 + this.xAxis.ticks(Math.floor(width / 80.0));
  746 + this.yAxis.ticks(Math.floor(height / 18.0));
  747 + this.svg.select('.x.axis').attr('transform', `translate(0,${height})`).call(this.xAxis);
  748 + this.svg.select('.y.axis').call(this.yAxis);
  749 + this.yAxisText.attr("y", 20 - this.margin.left).attr("x", 0 - height / 2);
  750 + this.yAxisTextTarget.attr("y", 0 - this.margin.left).attr("x", 0 - height / 2.0);
  751 + this.resizeCatalogLayers();
  752 + if (!this.visible) {
  753 + this.hide();
  754 + }
  755 + return this;
  756 + }
  757 +
  758 + clear() {
  759 + $(this.svg.node()).remove();
  760 + return this.visible = false;
  761 + }
772 762  
773   - hide() {
774   - $(this.svg.node()).hide();
775   - return this.visible = false;
  763 + show() {
  764 + $(this.svg.node()).show();
  765 + return this.visible = true;
  766 + }
  767 +
  768 + hide() {
  769 + $(this.svg.node()).hide();
  770 + return this.visible = false;
  771 + }
  772 +
  773 + onMouseMove() {
  774 + let x;
  775 + x = this.xScale.invert(d3.mouse(this.brushOverlay.node())[0]);
  776 + if (this.options.onMouseMove != null) {
  777 + return this.options.onMouseMove(x);
  778 + } else {
  779 + return this.moveCursor(x);
776 780 }
  781 + }
777 782  
778   - onMouseMove() {
779   - let x;
780   - x = this.xScale.invert(d3.mouse(this.brushOverlay.node())[0]);
781   - if (this.options.onMouseMove != null) {
782   - return this.options.onMouseMove(x);
783   - } else {
784   - return this.moveCursor(x);
785   - }
  783 + onMouseOver() {
  784 + if (this.options.onMouseOver != null) {
  785 + return this.options.onMouseOver();
  786 + } else {
  787 + return this.showCursor();
786 788 }
  789 + }
787 790  
788   - onMouseOver() {
789   - if (this.options.onMouseOver != null) {
790   - return this.options.onMouseOver();
791   - } else {
792   - return this.showCursor();
793   - }
  791 + onMouseOut() {
  792 + if (this.options.onMouseOut != null) {
  793 + return this.options.onMouseOut();
  794 + } else {
  795 + return this.hideCursor();
794 796 }
  797 + }
795 798  
796   - onMouseOut() {
797   - if (this.options.onMouseOut != null) {
798   - return this.options.onMouseOut();
799   - } else {
800   - return this.hideCursor();
801   - }
  799 + onDoubleClick() {
  800 + if (this.options.onDblClick != null) {
  801 + return this.options.onDblClick();
  802 + } else {
  803 + return this.resetZoom();
802 804 }
  805 + }
803 806  
804   - onDoubleClick() {
805   - if (this.options.onDblClick != null) {
806   - return this.options.onDblClick();
  807 + onBrushEnd() {
  808 + let s;
  809 + let minmax;
  810 + s = d3.event.selection;
  811 + if (s) {
  812 + minmax = [s[0], s[1]].map(this.xScale.invert, this.xScale);
  813 + this.brush.call(this.brushFunction.move, null);
  814 + if (this.options.onBrushEnd != null) {
  815 + return this.options.onBrushEnd(minmax[0], minmax[1]);
807 816 } else {
808   - return this.resetZoom();
  817 + return this.zoomIn(minmax[0], minmax[1]);
809 818 }
810 819 }
  820 + }
811 821  
812   - onBrushEnd() {
813   - let s;
814   - let minmax;
815   - s = d3.event.selection;
816   - if (s) {
817   - minmax = [s[0], s[1]].map(this.xScale.invert, this.xScale);
818   - this.brush.call(this.brushFunction.move, null);
819   - if (this.options.onBrushEnd != null) {
820   - return this.options.onBrushEnd(minmax[0], minmax[1]);
821   - } else {
822   - return this.zoomIn(minmax[0], minmax[1]);
823   - }
824   - }
  822 + zoomIn(startDate, stopDate) {
  823 + let ref$;
  824 + let minDate;
  825 + let maxDate;
  826 + console.debug(`Zooming in ${this} from ${startDate} to ${stopDate}.`);
  827 + ref$ = this.xDataExtent, minDate = ref$[0], maxDate = ref$[1];
  828 + if (startDate < minDate) {
  829 + startDate = minDate;
825 830 }
826   -
827   - zoomIn(startDate, stopDate) {
  831 + if (stopDate > maxDate) {
  832 + stopDate = maxDate;
  833 + }
  834 + this.xScale.domain([startDate, stopDate]);
  835 + this.yScale.domain(d3.extent(this.data, d => {
828 836 let ref$;
829   - let minDate;
830   - let maxDate;
831   - console.debug(`Zooming in ${this} from ${startDate} to ${stopDate}.`);
832   - ref$ = this.xDataExtent, minDate = ref$[0], maxDate = ref$[1];
833   - if (startDate < minDate) {
834   - startDate = minDate;
835   - }
836   - if (stopDate > maxDate) {
837   - stopDate = maxDate;
  837 + if (startDate <= (ref$ = d.x) && ref$ <= stopDate) {
  838 + return d.y;
  839 + } else {
  840 + return 0;
838 841 }
839   - this.xScale.domain([startDate, stopDate]);
840   - this.yScale.domain(d3.extent(this.data, d => {
841   - let ref$;
842   - if (startDate <= (ref$ = d.x) && ref$ <= stopDate) {
843   - return d.y;
844   - } else {
845   - return 0;
846   - }
847   - }));
848   - return this.applyZoom();
849   - }
  842 + }));
  843 + return this.applyZoom();
  844 + }
850 845  
851   - resetZoom() {
852   - this.xScale.domain(this.xDataExtent);
853   - this.yScale.domain(this.yDataExtent);
854   - return this.applyZoom();
855   - }
  846 + resetZoom() {
  847 + this.xScale.domain(this.xDataExtent);
  848 + this.yScale.domain(this.yDataExtent);
  849 + return this.applyZoom();
  850 + }
856 851  
857   - applyZoom() {
858   - let duration;
859   - let t;
860   - duration = 0;
861   - if (this.visible) {
862   - duration = 750;
863   - console.debug(`Applying zoom to visible ${this}…`);
864   - t = this.svg.transition().duration(duration);
865   - this.svg.select('.x.axis').transition(t).call(this.xAxis);
866   - this.svg.select('.y.axis').transition(t).call(this.yAxis);
867   - this.path.transition(t).attr('d', this.line);
868   - this.predictiveDataPath.transition(t).attr('d', this.line);
  852 + applyZoom() {
  853 + let duration;
  854 + let t;
  855 + duration = 0;
  856 + if (this.visible) {
  857 + duration = 750;
  858 + console.debug(`Applying zoom to visible ${this}…`);
  859 + t = this.svg.transition().duration(duration);
  860 + this.svg.select('.x.axis').transition(t).call(this.xAxis);
  861 + this.svg.select('.y.axis').transition(t).call(this.yAxis);
  862 + this.path.transition(t).attr('d', this.line);
  863 + this.predictiveDataPath.transition(t).attr('d', this.line);
  864 + } else {
  865 + console.debug(`Applying zoom to hidden ${this}…`);
  866 + this.svg.select('.x.axis').call(this.xAxis);
  867 + this.svg.select('.y.axis').call(this.yAxis);
  868 + this.path.attr('d', this.line);
  869 + this.predictiveDataPath.attr('d', this.line);
  870 + }
  871 + this.resizeCatalogLayers();
  872 + this.hideCursor();
  873 + return new Promise((resolve, reject) => {
  874 + if (0 === duration) {
  875 + return resolve();
869 876 } else {
870   - console.debug(`Applying zoom to hidden ${this}…`);
871   - this.svg.select('.x.axis').call(this.xAxis);
872   - this.svg.select('.y.axis').call(this.yAxis);
873   - this.path.attr('d', this.line);
874   - this.predictiveDataPath.attr('d', this.line);
  877 + return setTimeout(() => resolve(), duration + 50);
875 878 }
876   - this.resizeCatalogLayers();
877   - this.hideCursor();
878   - return new Promise((resolve, reject) => {
879   - if (0 === duration) {
880   - return resolve();
881   - } else {
882   - return setTimeout(() => resolve(), duration + 50);
883   - }
884   - });
885   - }
  879 + });
  880 + }
886 881  
887   - createCatalogLayers() {
888   - let catalog_slug;
889   - let ref$;
890   - let layers;
891   - let i$;
892   - let len$;
893   - let layer;
894   - let started_at;
895   - let stopped_at;
896   - this.layers_rects = {};
897   - for (catalog_slug in ref$ = this.target.config.layers) {
898   - layers = ref$[catalog_slug];
899   - this.layers_rects[catalog_slug] = [];
900   - for (i$ = 0, len$ = layers.length; i$ < len$; ++i$) {
901   - layer = layers[i$];
902   - started_at = moment(layer.start);
903   - stopped_at = moment(layer.stop);
904   - this.layers_rects[catalog_slug].push(this.createCatalogLayer(started_at, stopped_at));
905   - }
906   - this.hideCatalogLayer(catalog_slug);
  882 + createCatalogLayers() {
  883 + let catalog_slug;
  884 + let ref$;
  885 + let layers;
  886 + let i$;
  887 + let len$;
  888 + let layer;
  889 + let started_at;
  890 + let stopped_at;
  891 + this.layers_rects = {};
  892 + for (catalog_slug in ref$ = this.target.config.layers) {
  893 + layers = ref$[catalog_slug];
  894 + this.layers_rects[catalog_slug] = [];
  895 + for (i$ = 0, len$ = layers.length; i$ < len$; ++i$) {
  896 + layer = layers[i$];
  897 + started_at = moment(layer.start);
  898 + stopped_at = moment(layer.stop);
  899 + this.layers_rects[catalog_slug].push(this.createCatalogLayer(started_at, stopped_at));
907 900 }
908   - return this;
909   - }
910   -
911   - createCatalogLayer(started_at, stopped_at) {
912   - let layer_rect;
913   - layer_rect = this.pathWrapper.append("rect").attr('y', 0).attr('height', this.plotHeight).attr('fill', '#FFFD64C2');
914   - return layer_rect;
  901 + this.hideCatalogLayer(catalog_slug);
915 902 }
  903 + return this;
  904 + }
916 905  
917   - resizeCatalogLayers() {
918   - let catalog_slug;
919   - let ref$;
920   - let layers;
921   - let i$;
922   - let len$;
923   - let i;
924   - let layer;
925   - let started_at;
926   - let stopped_at;
927   - let width;
928   - for (catalog_slug in ref$ = this.target.config.layers) {
929   - layers = ref$[catalog_slug];
930   - for (i$ = 0, len$ = layers.length; i$ < len$; ++i$) {
931   - i = i$;
932   - layer = layers[i$];
933   - started_at = moment(layer.start);
934   - stopped_at = moment(layer.stop);
935   - width = Math.max(2, this.xScale(stopped_at) - this.xScale(started_at));
936   - this.layers_rects[catalog_slug][i].attr('x', this.xScale(started_at)).attr('width', width);
937   - }
938   - }
939   - return this;
940   - }
  906 + createCatalogLayer(started_at, stopped_at) {
  907 + let layer_rect;
  908 + layer_rect = this.pathWrapper.append("rect").attr('y', 0).attr('height', this.plotHeight).attr('fill', '#FFFD64C2');
  909 + return layer_rect;
  910 + }
941 911  
942   - showCatalogLayer(catalog_slug) {
943   - let i$;
944   - let ref$;
945   - let len$;
946   - let i;
947   - let layer;
948   - for (i$ = 0, len$ = (ref$ = this.target.config.layers[catalog_slug]).length; i$ < len$; ++i$) {
  912 + resizeCatalogLayers() {
  913 + let catalog_slug;
  914 + let ref$;
  915 + let layers;
  916 + let i$;
  917 + let len$;
  918 + let i;
  919 + let layer;
  920 + let started_at;
  921 + let stopped_at;
  922 + let width;
  923 + for (catalog_slug in ref$ = this.target.config.layers) {
  924 + layers = ref$[catalog_slug];
  925 + for (i$ = 0, len$ = layers.length; i$ < len$; ++i$) {
949 926 i = i$;
950   - layer = ref$[i$];
951   - this.layers_rects[catalog_slug][i].style("display", null);
  927 + layer = layers[i$];
  928 + started_at = moment(layer.start);
  929 + stopped_at = moment(layer.stop);
  930 + width = Math.max(2, this.xScale(stopped_at) - this.xScale(started_at));
  931 + this.layers_rects[catalog_slug][i].attr('x', this.xScale(started_at)).attr('width', width);
952 932 }
953   - return this;
954 933 }
  934 + return this;
  935 + }
955 936  
956   - hideCatalogLayer(catalog_slug) {
957   - let i$;
958   - let ref$;
959   - let len$;
960   - let i;
961   - let layer;
962   - for (i$ = 0, len$ = (ref$ = this.target.config.layers[catalog_slug]).length; i$ < len$; ++i$) {
963   - i = i$;
964   - layer = ref$[i$];
965   - this.layers_rects[catalog_slug][i].style("display", "none");
966   - }
967   - return this;
968   - }
  937 + showCatalogLayer(catalog_slug) {
  938 + let i$;
  939 + let ref$;
  940 + let len$;
  941 + let i;
  942 + let layer;
  943 + for (i$ = 0, len$ = (ref$ = this.target.config.layers[catalog_slug]).length; i$ < len$; ++i$) {
  944 + i = i$;
  945 + layer = ref$[i$];
  946 + this.layers_rects[catalog_slug][i].style("display", null);
  947 + }
  948 + return this;
  949 + }
969 950  
970   - showCursor() {
971   - return this.focus.style("display", null);
972   - }
  951 + hideCatalogLayer(catalog_slug) {
  952 + let i$;
  953 + let ref$;
  954 + let len$;
  955 + let i;
  956 + let layer;
  957 + for (i$ = 0, len$ = (ref$ = this.target.config.layers[catalog_slug]).length; i$ < len$; ++i$) {
  958 + i = i$;
  959 + layer = ref$[i$];
  960 + this.layers_rects[catalog_slug][i].style("display", "none");
  961 + }
  962 + return this;
  963 + }
973 964  
974   - hideCursor() {
975   - return this.focus.style("display", "none");
976   - }
  965 + showCursor() {
  966 + return this.focus.style("display", null);
  967 + }
977 968  
978   - moveCursor(x0) {
979   - let i;
980   - let d0;
981   - let d1;
982   - let d;
983   - let xx;
984   - let yy;
985   - let dx;
986   - let transform;
987   - i = this.bisectDate(this.data, x0, 1);
988   - d0 = this.data[i - 1];
989   - d1 = this.data[i];
990   - if (!d1 || !d0) {
991   - this.hideCursor();
992   - return;
993   - }
994   - d = x0 - d0.x > d1.x - x0 ? d1 : d0;
995   - xx = this.xScale(d.x);
996   - yy = this.yScale(d.y);
997   - const mirrored = this.plotWidth != null && xx > this.plotWidth / 2;
998   - dx = 8;
999   - if (mirrored) {
1000   - dx = -1 * dx;
1001   - }
1002   - transform = `translate(${xx}, ${yy})`;
1003   - this.cursorCircle.attr("transform", transform);
1004   - this.cursorValue.attr("transform", transform).text(d.y).attr('text-anchor', mirrored ? 'end' : 'start').attr("dx", dx);
1005   - this.cursorValueShadow.attr("transform", transform).text(d.y).attr('text-anchor', mirrored ? 'end' : 'start').attr("dx", dx);
1006   - this.cursorDate.attr("transform", transform).text(this.timeFormat(d.x)).attr('text-anchor', mirrored ? 'end' : 'start').attr("dx", dx);
1007   - this.cursorDateShadow.attr("transform", transform).text(this.timeFormat(d.x)).attr('text-anchor', mirrored ? 'end' : 'start').attr("dx", dx);
1008   - this.showCursor();
1009   - return this;
1010   - }
  969 + hideCursor() {
  970 + return this.focus.style("display", "none");
  971 + }
  972 +
  973 + moveCursor(x0) {
  974 + let i;
  975 + let d0;
  976 + let d1;
  977 + let d;
  978 + let xx;
  979 + let yy;
  980 + let dx;
  981 + let transform;
  982 + i = this.bisectDate(this.data, x0, 1);
  983 + d0 = this.data[i - 1];
  984 + d1 = this.data[i];
  985 + if (!d1 || !d0) {
  986 + this.hideCursor();
  987 + return;
  988 + }
  989 + d = x0 - d0.x > d1.x - x0 ? d1 : d0;
  990 + xx = this.xScale(d.x);
  991 + yy = this.yScale(d.y);
  992 + const mirrored = this.plotWidth != null && xx > this.plotWidth / 2;
  993 + dx = 8;
  994 + if (mirrored) {
  995 + dx = -1 * dx;
  996 + }
  997 + transform = `translate(${xx}, ${yy})`;
  998 + this.cursorCircle.attr("transform", transform);
  999 + this.cursorValue.attr("transform", transform).text(d.y).attr('text-anchor', mirrored ? 'end' : 'start').attr("dx", dx);
  1000 + this.cursorValueShadow.attr("transform", transform).text(d.y).attr('text-anchor', mirrored ? 'end' : 'start').attr("dx", dx);
  1001 + this.cursorDate.attr("transform", transform).text(this.timeFormat(d.x)).attr('text-anchor', mirrored ? 'end' : 'start').attr("dx", dx);
  1002 + this.cursorDateShadow.attr("transform", transform).text(this.timeFormat(d.x)).attr('text-anchor', mirrored ? 'end' : 'start').attr("dx", dx);
  1003 + this.showCursor();
  1004 + return this;
1011 1005 }
  1006 + }
1012 1007  
1013   - TimeSeries.prototype.bisectDate = d3.bisector(d => d.x).left;
1014   - TimeSeries.prototype.timeFormat = d3.utcFormat("%Y-%m-%d %H:%M");
1015   - return TimeSeries;
1016   - })());
  1008 + TimeSeries.prototype.bisectDate = d3.bisector(d => d.x).left;
  1009 + TimeSeries.prototype.timeFormat = d3.utcFormat("%Y-%m-%d %H:%M");
1017 1010  
1018 1011  
1019 1012 ///////////////////////////////////////////////////////////////////////////
1020 1013 //// ORBITS PLOT //////////////////////////////////////////////////////////
1021 1014  
1022   - global.Orbits = Orbits = ((() => {
1023   - // "View of the solar system from above, with orbits segments for selected time\ninterval, from real data.";
1024   - // Orbits.displayName = 'Orbits';
1025   - // const prototype = Orbits.prototype;
1026   - // const constructor = Orbits;
1027   -
1028   - class Orbits {
1029   - constructor(container, options) {
1030   - this.container = container;
1031   - this.options = options != null
1032   - ? options
1033   - : {};
1034   - console.log("Initializing plot of orbits…");
1035   - this.margin = {
1036   - top: 30,
1037   - right: 20,
1038   - bottom: 42,
1039   - left: 60
1040   - };
1041   - this.data = {};
1042   - this.orbiters = {};
1043   - this.orbitersElements = {};
1044   - this.orbitersExtrema = {};
1045   - this.lastOrbiterData = {};
1046   - this.xScale = d3.scaleLinear().domain([-1, 1]);
1047   - this.yScale = d3.scaleLinear().domain([1, -1]);
1048   - this.xAxis = d3.axisBottom().ticks(10);
1049   - this.yAxis = d3.axisLeft().ticks(10);
1050   - this.svg = d3.select(this.container).append('svg');
1051   - this.plotWrapper = this.svg.append('g');
1052   - this.plotWrapper.attr('transform', `translate(${this.margin.left},${this.margin.top})`);
1053   - this.xAxisLine = this.plotWrapper.append('g').classed('x axis', true);
1054   - this.yAxisLine = this.plotWrapper.append('g').classed('y axis', true);
1055   - this.xAxisTitle = this.xAxisLine.append('text').attr('fill', '#000');
1056   - this.xAxisTitle.style("text-anchor", "middle");
1057   - this.xAxisTitle.append('tspan').text('Y');
1058   - this.xAxisTitle.append('tspan').attr('dy', '3px').text('HEE').attr('font-size', '8px');
1059   - this.xAxisTitle.append('tspan').attr('dy', '-3px').text(' (AU)');
1060   - this.yAxisTitle = this.yAxisLine.append('text').attr('fill', '#000');
1061   - this.yAxisTitle.style("text-anchor", "middle");
1062   - this.yAxisTitle.append('tspan').text('X');
1063   - this.yAxisTitle.append('tspan').attr('dy', '3px').text('HEE').attr('font-size', '8px');
1064   - this.yAxisTitle.append('tspan').attr('dy', '-3px').text(' (AU)');
1065   - this.yAxisTitle.attr('transform', 'rotate(-90)');
1066   - this.sun = this.plotWrapper.append("svg:image").attr('xlink:href', this.options.sun.img).attr('width', '32px').attr('height', '32px');
1067   - this.sun.append('svg:title').text("Sun");
1068   - $(this.svg.node()).hide();
1069   - this.resize();
1070   - }
  1015 + class Orbits {
  1016 + constructor(container, options) {
  1017 + this.container = container;
  1018 + this.options = options || {};
  1019 + console.log("Initializing plot of orbits…");
  1020 + this.margin = {
  1021 + top: 30,
  1022 + right: 20,
  1023 + bottom: 42,
  1024 + left: 60
  1025 + };
  1026 + this.data = {};
  1027 + this.orbiters = {};
  1028 + this.orbitersElements = {};
  1029 + this.orbitersExtrema = {};
  1030 + this.lastOrbiterData = {};
  1031 + this.xScale = d3.scaleLinear().domain([-1, 1]);
  1032 + this.yScale = d3.scaleLinear().domain([1, -1]);
  1033 + this.xAxis = d3.axisBottom().ticks(10);
  1034 + this.yAxis = d3.axisLeft().ticks(10);
  1035 + this.svg = d3.select(this.container).append('svg');
  1036 + this.plotWrapper = this.svg.append('g');
  1037 + this.plotWrapper.attr('transform', `translate(${this.margin.left},${this.margin.top})`);
  1038 + this.xAxisLine = this.plotWrapper.append('g').classed('x axis', true);
  1039 + this.yAxisLine = this.plotWrapper.append('g').classed('y axis', true);
  1040 + this.xAxisTitle = this.xAxisLine.append('text').attr('fill', '#000');
  1041 + this.xAxisTitle.style("text-anchor", "middle");
  1042 + this.xAxisTitle.append('tspan').text('Y');
  1043 + this.xAxisTitle.append('tspan').attr('dy', '3px').text('HEE').attr('font-size', '8px');
  1044 + this.xAxisTitle.append('tspan').attr('dy', '-3px').text(' (AU)');
  1045 + this.yAxisTitle = this.yAxisLine.append('text').attr('fill', '#000');
  1046 + this.yAxisTitle.style("text-anchor", "middle");
  1047 + this.yAxisTitle.append('tspan').text('X');
  1048 + this.yAxisTitle.append('tspan').attr('dy', '3px').text('HEE').attr('font-size', '8px');
  1049 + this.yAxisTitle.append('tspan').attr('dy', '-3px').text(' (AU)');
  1050 + this.yAxisTitle.attr('transform', 'rotate(-90)');
  1051 + this.sun = this.plotWrapper.append("svg:image").attr('xlink:href', this.options.sun.img).attr('width', '32px').attr('height', '32px');
  1052 + this.sun.append('svg:title').text("Sun");
  1053 + $(this.svg.node()).hide();
  1054 + this.resize();
  1055 + }
1071 1056  
1072   - initOrbiter(slug, config, data) {
1073   - const thisOrbit = this;
1074   - let orbit_ellipse;
1075   - let orbiter;
1076   - let orbit_line;
1077   - let orbit_section;
1078   - this.data[slug] = data;
1079   - this.orbiters[slug] = config;
1080   - if (data.length) {
1081   - console.info(`Initializing orbit of ${config.name}…`);
1082   - } else {
1083   - console.warn(`No orbit data for ${config.name}…`);
1084   - return;
1085   - }
1086   - if (slug in this.orbitersElements) {
1087   - throw new Error(`Second init of ${slug}`);
1088   - }
1089   - orbit_ellipse = this.plotWrapper.append("svg:ellipse").classed('orbit orbit_ellipse', true);
1090   - orbiter = this.plotWrapper.append("svg:image").attr('xlink:href', config['img']).attr('width', '32px').attr('height', '32px');
1091   - orbiter.append('svg:title').text(config.name);
1092   - orbit_line = d3.line().x(d => thisOrbit.xScale(d.y)).y(d => thisOrbit.yScale(d.x));
1093   - orbit_section = this.plotWrapper.append('path').datum(data).classed('orbit orbit_section', true);
1094   - this.orbitersElements[slug] = {
1095   - orbiter,
1096   - orbit_ellipse,
1097   - orbit_section,
1098   - orbit_line
1099   - };
1100   - this.orbitersExtrema[slug] = d3.max(data, d => Math.max(Math.abs(d.x), Math.abs(d.y)));
1101   - $(this.svg.node()).show();
1102   - if (config.active) {
1103   - this.enableTarget(slug);
1104   - } else {
1105   - this.disableTarget(slug);
1106   - }
1107   - return this;
1108   - }
  1057 + initOrbiter(slug, config, data) {
  1058 + const thisOrbit = this;
  1059 + let orbit_ellipse;
  1060 + let orbiter;
  1061 + let orbit_line;
  1062 + let orbit_section;
  1063 + this.data[slug] = data;
  1064 + this.orbiters[slug] = config;
  1065 + if (data.length) {
  1066 + console.info(`Initializing orbit of ${config.name}…`);
  1067 + } else {
  1068 + console.warn(`No orbit data for ${config.name}…`);
  1069 + return;
  1070 + }
  1071 + if (slug in this.orbitersElements) {
  1072 + throw new Error(`Second init of ${slug}.`);
  1073 + }
  1074 + orbit_ellipse = this.plotWrapper.append("svg:ellipse").classed('orbit orbit_ellipse', true);
  1075 + orbiter = this.plotWrapper.append("svg:image").attr('xlink:href', config['img']).attr('width', '32px').attr('height', '32px');
  1076 + orbiter.append('svg:title').text(config.name);
  1077 + orbit_line = d3.line().x(d => thisOrbit.xScale(d.y)).y(d => thisOrbit.yScale(d.x));
  1078 + orbit_section = this.plotWrapper.append('path').datum(data).classed('orbit orbit_section', true);
  1079 + this.orbitersElements[slug] = {
  1080 + orbiter,
  1081 + orbit_ellipse,
  1082 + orbit_section,
  1083 + orbit_line
  1084 + };
  1085 + this.orbitersExtrema[slug] = d3.max(data, d => Math.max(Math.abs(d.x), Math.abs(d.y)));
  1086 + $(this.svg.node()).show();
  1087 + if (config.active) {
  1088 + this.enableTarget(slug);
  1089 + } else {
  1090 + this.disableTarget(slug);
  1091 + }
  1092 + return this;
  1093 + }
1109 1094  
1110   - enableTarget(slug) {
1111   - this.orbiters[slug].enabled = true;
1112   - return this.showOrbiter(slug);
1113   - }
  1095 + enableTarget(slug) {
  1096 + this.orbiters[slug].enabled = true;
  1097 + return this.showOrbiter(slug);
  1098 + }
1114 1099  
1115   - disableTarget(slug) {
1116   - this.orbiters[slug].enabled = false;
1117   - return this.hideOrbiter(slug);
1118   - }
  1100 + disableTarget(slug) {
  1101 + this.orbiters[slug].enabled = false;
  1102 + return this.hideOrbiter(slug);
  1103 + }
1119 1104  
1120   - showOrbiter(slug) {
1121   - if (!this.data[slug].length) {
1122   - return;
1123   - }
1124   - if (!this.orbiters[slug].enabled) {
1125   - return;
1126   - }
1127   - this.orbiters[slug].hidden = false;
1128   - this.orbitersElements[slug].orbiter.style("display", null);
1129   - this.orbitersElements[slug].orbit_ellipse.style("display", null);
1130   - this.orbitersElements[slug].orbit_section.style("display", null);
1131   - return this.resize(true);
  1105 + showOrbiter(slug) {
  1106 + if (!this.data[slug].length) {
  1107 + return;
1132 1108 }
1133   -
1134   - hideOrbiter(slug) {
1135   - if (!this.data[slug].length) {
1136   - return;
1137   - }
1138   - this.orbiters[slug].hidden = true;
1139   - this.orbitersElements[slug].orbiter.style("display", "none");
1140   - this.orbitersElements[slug].orbit_ellipse.style("display", "none");
1141   - this.orbitersElements[slug].orbit_section.style("display", "none");
1142   - return this.resize(true);
  1109 + if (!this.orbiters[slug].enabled) {
  1110 + return;
1143 1111 }
  1112 + this.orbiters[slug].hidden = false;
  1113 + this.orbitersElements[slug].orbiter.style("display", null);
  1114 + this.orbitersElements[slug].orbit_ellipse.style("display", null);
  1115 + this.orbitersElements[slug].orbit_section.style("display", null);
  1116 + return this.resize(true);
  1117 + }
1144 1118  
1145   - clear() {
1146   - return $(this.svg.node()).remove();
  1119 + hideOrbiter(slug) {
  1120 + if (!this.data[slug].length) {
  1121 + return;
1147 1122 }
  1123 + this.orbiters[slug].hidden = true;
  1124 + this.orbitersElements[slug].orbiter.style("display", "none");
  1125 + this.orbitersElements[slug].orbit_ellipse.style("display", "none");
  1126 + this.orbitersElements[slug].orbit_section.style("display", "none");
  1127 + return this.resize(true);
  1128 + }
1148 1129  
1149   - resize(animate, extremum) {
1150   - let width;
1151   - let height;
1152   - let s;
1153   - let o;
1154   - let slug;
1155   - let ref$;
1156   - let config;
1157   - let t;
1158   - let t1;
1159   - animate == null && (animate = false);
1160   - extremum == null && (extremum = null);
1161   - width = Math.ceil($(this.container).width() - this.margin.left - this.margin.right);
1162   - height = Math.ceil(1.0 * width);
1163   - console.debug(`Resizing orbits : ${width} × ${height}…`);
1164   - if (extremum === null) {
1165   - extremum = 1.1 * d3.max((function () {
1166   - let ref$;
1167   - const results$ = [];
1168   - for (s in ref$ = this.orbiters) {
1169   - o = ref$[s];
1170   - if (!o.hidden) {
1171   - results$.push(this.orbitersExtrema[s]);
1172   - }
  1130 + clear() {
  1131 + return $(this.svg.node()).remove();
  1132 + }
  1133 +
  1134 + resize(animate, extremum) {
  1135 + let s;
  1136 + let o;
  1137 + let slug;
  1138 + let ref$;
  1139 + let config;
  1140 + let t;
  1141 + let t1;
  1142 + animate == null && (animate = false);
  1143 + extremum == null && (extremum = null);
  1144 + const width = Math.ceil($(this.container).width() - this.margin.left - this.margin.right);
  1145 + const height = width; // it's a square
  1146 + console.debug(`Resizing orbits : ${width} × ${height}…`);
  1147 + if (extremum === null) {
  1148 + extremum = 1.1 * d3.max((function () {
  1149 + let ref$;
  1150 + const results$ = [];
  1151 + for (s in ref$ = this.orbiters) {
  1152 + o = ref$[s];
  1153 + if (!o.hidden) {
  1154 + results$.push(this.orbitersExtrema[s]);
1173 1155 }
1174   - return results$;
1175   - }.call(this)));
1176   - }
1177   - this.xScale = d3.scaleLinear().domain([-1 * extremum, extremum]);
1178   - this.yScale = d3.scaleLinear().domain([extremum, -1 * extremum]);
1179   - this.xScale.range([0, width]);
1180   - this.yScale.range([height, 0]);
1181   - this.svg.attr('width', width + this.margin.right + this.margin.left).attr('height', height + this.margin.top + this.margin.bottom);
1182   - this.sun.attr("x", width / 2.0 - 16).attr("y", height / 2.0 - 16);
1183   - for (slug in ref$ = this.orbiters) {
1184   - config = ref$[slug];
1185   - this.resizeOrbiter(slug, config, width, height, animate);
1186   - }
1187   - this.xAxis.scale(this.xScale);
1188   - this.yAxis.scale(this.yScale);
1189   - this.svg.select('.x.axis').attr('transform', `translate(0,${height})`);
1190   - if (animate) {
1191   - t = this.svg.transition().duration(750);
1192   - t1 = this.svg.transition().duration(4750);
1193   - this.svg.select('.x.axis').transition(t).call(this.xAxis);
1194   - this.svg.select('.y.axis').transition(t).call(this.yAxis);
1195   - } else {
1196   - this.svg.select('.x.axis').call(this.xAxis);
1197   - this.svg.select('.y.axis').call(this.yAxis);
1198   - }
1199   - this.xAxisTitle.attr("x", width / 2).attr("y", 37);
1200   - this.yAxisTitle.attr("x", -1 * height / 2).attr("y", -30);
1201   - return this;
  1156 + }
  1157 + return results$;
  1158 + }.call(this)));
  1159 + }
  1160 + this.xScale = d3.scaleLinear().domain([-1 * extremum, extremum]);
  1161 + this.yScale = d3.scaleLinear().domain([extremum, -1 * extremum]);
  1162 + this.xScale.range([0, width]);
  1163 + this.yScale.range([height, 0]);
  1164 + this.svg.attr('width', width + this.margin.right + this.margin.left).attr('height', height + this.margin.top + this.margin.bottom);
  1165 + this.sun.attr("x", width / 2.0 - 16).attr("y", height / 2.0 - 16);
  1166 + for (slug in ref$ = this.orbiters) {
  1167 + config = ref$[slug];
  1168 + this.resizeOrbiter(slug, config, width, height, animate);
  1169 + }
  1170 + this.xAxis.scale(this.xScale);
  1171 + this.yAxis.scale(this.yScale);
  1172 + this.svg.select('.x.axis').attr('transform', `translate(0,${height})`);
  1173 + if (animate) {
  1174 + t = this.svg.transition().duration(750);
  1175 + // t1 = this.svg.transition().duration(4750);
  1176 + this.svg.select('.x.axis').transition(t).call(this.xAxis);
  1177 + this.svg.select('.y.axis').transition(t).call(this.yAxis);
  1178 + } else {
  1179 + this.svg.select('.x.axis').call(this.xAxis);
  1180 + this.svg.select('.y.axis').call(this.yAxis);
1202 1181 }
  1182 + this.xAxisTitle.attr("x", width / 2).attr("y", 37);
  1183 + this.yAxisTitle.attr("x", -1 * height / 2).attr("y", -30);
  1184 + return this;
  1185 + }
1203 1186  
1204   - resizeOrbiter(slug, config, width, height, animate) {
1205   - let data;
1206   - let tt;
1207   - let el;
1208   - let orbit_section;
1209   - let t;
1210   - let a;
1211   - let b;
1212   - let c;
1213   - let cx;
1214   - let cy;
1215   - let orbit_ellipse;
1216   - animate == null && (animate = false);
1217   - data = this.data[slug];
1218   - if (!data.length) {
1219   - return;
1220   - }
1221   - console.debug(`Resizing orbit of ${slug}…`);
1222   - tt = this.svg.transition().duration(750);
1223   - el = this.orbitersElements[slug];
1224   - orbit_section = el['orbit_section'];
1225   - if (animate) {
1226   - t = this.svg.transition().duration(750);
1227   - orbit_section = orbit_section.transition(tt);
1228   - }
1229   - orbit_section.attr('d', el['orbit_line']);
1230   - a = config['orbit']['a'];
1231   - b = config['orbit']['b'];
1232   - c = Math.sqrt(a * a - b * b);
1233   - cx = width / 2 - c;
1234   - cy = height / 2;
1235   - orbit_ellipse = el['orbit_ellipse'];
1236   - if (animate) {
1237   - t = this.svg.transition().duration(750);
1238   - orbit_ellipse = orbit_ellipse.transition(t);
1239   - }
1240   - orbit_ellipse.attr('cx', cx).attr('cy', cy).attr('rx', this.xScale(a) - this.xScale(0)).attr('ry', this.yScale(b) - this.yScale(0));
1241   - this.repositionOrbiter(slug, null, true);
1242   - return this;
1243   - }
  1187 + resizeOrbiter(slug, config, width, height, animate) {
  1188 + let data;
  1189 + let tt;
  1190 + let el;
  1191 + let orbit_section;
  1192 + let t;
  1193 + let a;
  1194 + let b;
  1195 + let c;
  1196 + let cx;
  1197 + let cy;
  1198 + let orbit_ellipse;
  1199 + animate == null && (animate = false);
  1200 + data = this.data[slug];
  1201 + if (!data.length) {
  1202 + return;
  1203 + }
  1204 + console.debug(`Resizing orbit of ${slug}…`);
  1205 + tt = this.svg.transition().duration(750);
  1206 + el = this.orbitersElements[slug];
  1207 + orbit_section = el['orbit_section'];
  1208 + if (animate) {
  1209 + t = this.svg.transition().duration(750);
  1210 + orbit_section = orbit_section.transition(tt);
  1211 + }
  1212 + orbit_section.attr('d', el['orbit_line']);
  1213 + a = config['orbit']['a'];
  1214 + b = config['orbit']['b'];
  1215 + c = Math.sqrt(a * a - b * b);
  1216 + cx = width / 2 - c;
  1217 + cy = height / 2;
  1218 + orbit_ellipse = el['orbit_ellipse'];
  1219 + if (animate) {
  1220 + t = this.svg.transition().duration(750);
  1221 + orbit_ellipse = orbit_ellipse.transition(t);
  1222 + }
  1223 + orbit_ellipse.attr('cx', cx).attr('cy', cy).attr('rx', this.xScale(a) - this.xScale(0)).attr('ry', this.yScale(b) - this.yScale(0));
  1224 + this.repositionOrbiter(slug, null, true);
  1225 + return this;
  1226 + }
1244 1227  
1245   - zoomToTarget(slug) {
1246   - return this.resize(true, 1.1 * this.orbitersExtrema[slug]);
1247   - }
  1228 + zoomToTarget(slug) {
  1229 + return this.resize(true, 1.1 * this.orbitersExtrema[slug]);
  1230 + }
1248 1231  
1249   - repositionOrbiter(slug, datum, animate) {
1250   - let data;
1251   - let el;
1252   - let t;
1253   - animate == null && (animate = false);
  1232 + repositionOrbiter(slug, datum, animate) {
  1233 + let data;
  1234 + let el;
  1235 + let t;
  1236 + animate == null && (animate = false);
  1237 + data = this.data[slug];
  1238 + if (!data.length) {
  1239 + return;
  1240 + }
  1241 + datum == null && (datum = this.lastOrbiterData[slug]);
  1242 + datum == null && (datum = data[data.length - 1]);
  1243 + this.lastOrbiterData[slug] = datum;
  1244 + el = this.orbitersElements[slug]['orbiter'];
  1245 + if (animate) {
  1246 + t = this.svg.transition().duration(750);
  1247 + el = el.transition(t);
  1248 + }
  1249 + el.attr('x', this.xScale(datum.y) - 16);
  1250 + el.attr('y', this.yScale(datum.x) - 16);
  1251 + return this;
  1252 + }
  1253 +
  1254 + moveToDate(t) {
  1255 + let slug;
  1256 + let ref$;
  1257 + let el;
  1258 + let data;
  1259 + let i;
  1260 + let d0;
  1261 + let d1;
  1262 + let d;
  1263 + if (!t) {
  1264 + console.warn("Trying to move to an undefined date !");
  1265 + }
  1266 + for (slug in ref$ = this.orbitersElements) {
  1267 + el = ref$[slug];
1254 1268 data = this.data[slug];
1255   - if (!data.length) {
1256   - return;
1257   - }
1258   - datum == null && (datum = this.lastOrbiterData[slug]);
1259   - datum == null && (datum = data[data.length - 1]);
1260   - this.lastOrbiterData[slug] = datum;
1261   - el = this.orbitersElements[slug]['orbiter'];
1262   - if (animate) {
1263   - t = this.svg.transition().duration(750);
1264   - el = el.transition(t);
  1269 + i = this.bisectDate(data, t, 1);
  1270 + d0 = data[i - 1];
  1271 + d1 = data[i];
  1272 + if (!(d1 && d0)) {
  1273 + continue;
1265 1274 }
1266   - el.attr('x', this.xScale(datum.y) - 16);
1267   - el.attr('y', this.yScale(datum.x) - 16);
1268   - return this;
  1275 + d = t - d0.t > d1.t - t ? d1 : d0;
  1276 + this.repositionOrbiter(slug, d);
1269 1277 }
  1278 + return this;
  1279 + }
1270 1280  
1271   - moveToDate(t) {
1272   - let slug;
1273   - let ref$;
1274   - let el;
1275   - let data;
1276   - let i;
1277   - let d0;
1278   - let d1;
1279   - let d;
1280   - if (!t) {
1281   - console.warn("Trying to move to an undefined date !");
1282   - }
1283   - for (slug in ref$ = this.orbitersElements) {
1284   - el = ref$[slug];
1285   - data = this.data[slug];
1286   - i = this.bisectDate(data, t, 1);
1287   - d0 = data[i - 1];
1288   - d1 = data[i];
1289   - if (!(d1 && d0)) {
1290   - continue;
1291   - }
1292   - d = t - d0.t > d1.t - t ? d1 : d0;
1293   - this.repositionOrbiter(slug, d);
  1281 + resizeDomain(started_at, stopped_at) {
  1282 + let slug;
  1283 + let ref$;
  1284 + let config;
  1285 + let el;
  1286 + let data;
  1287 + const results$ = [];
  1288 + for (slug in ref$ = this.orbiters) {
  1289 + config = ref$[slug];
  1290 + el = this.orbitersElements[slug];
  1291 + data = this.data[slug].filter(onlyDataInRange);
  1292 + if (!data.length) {
  1293 + this.hideOrbiter(slug);
  1294 + continue;
1294 1295 }
1295   - return this;
  1296 + el['orbit_section'].datum(data);
  1297 + el['orbit_section'].attr('d', el['orbit_line']);
  1298 + results$.push(this.showOrbiter(slug));
1296 1299 }
  1300 + return results$;
1297 1301  
1298   - resizeDomain(started_at, stopped_at) {
1299   - let slug;
1300   - let ref$;
1301   - let config;
1302   - let el;
1303   - let data;
1304   - const results$ = [];
1305   - for (slug in ref$ = this.orbiters) {
1306   - config = ref$[slug];
1307   - el = this.orbitersElements[slug];
1308   - data = this.data[slug].filter(onlyDataInRange);
1309   - if (!data.length) {
1310   - this.hideOrbiter(slug);
1311   - continue;
1312   - }
1313   - el['orbit_section'].datum(data);
1314   - el['orbit_section'].attr('d', el['orbit_line']);
1315   - results$.push(this.showOrbiter(slug));
1316   - }
1317   - return results$;
1318   -
1319   - function onlyDataInRange(d) {
1320   - return started_at <= d.t && d.t <= stopped_at;
1321   - }
  1302 + function onlyDataInRange(d) {
  1303 + return started_at <= d.t && d.t <= stopped_at;
1322 1304 }
  1305 + }
1323 1306  
1324   - resetZoom() {
1325   - let slug;
1326   - let ref$;
1327   - let config;
1328   - let el;
1329   - const results$ = [];
1330   - for (slug in ref$ = this.orbiters) {
1331   - config = ref$[slug];
1332   - el = this.orbitersElements[slug];
1333   - if (!this.data[slug].length) {
1334   - this.hideOrbiter(slug);
1335   - continue;
1336   - }
1337   - el['orbit_section'].datum(this.data[slug]);
1338   - el['orbit_section'].attr('d', el['orbit_line']);
1339   - results$.push(this.showOrbiter(slug));
  1307 + resetZoom() {
  1308 + let slug;
  1309 + let ref$;
  1310 + let config;
  1311 + let el;
  1312 + const results$ = [];
  1313 + for (slug in ref$ = this.orbiters) {
  1314 + config = ref$[slug];
  1315 + el = this.orbitersElements[slug];
  1316 + if (!this.data[slug].length) {
  1317 + this.hideOrbiter(slug);
  1318 + continue;
1340 1319 }
1341   - return results$;
  1320 + el['orbit_section'].datum(this.data[slug]);
  1321 + el['orbit_section'].attr('d', el['orbit_line']);
  1322 + results$.push(this.showOrbiter(slug));
1342 1323 }
  1324 + return results$;
1343 1325 }
  1326 + }
  1327 +
  1328 + Orbits.prototype.bisectDate = d3.bisector(d => d.t).left;
  1329 +
  1330 + global.SpaceWeather = SpaceWeather;
  1331 + global.TimeSeries = TimeSeries;
  1332 + global.Orbits = Orbits;
1344 1333  
1345   - Orbits.prototype.bisectDate = d3.bisector(d => d.t).left;
1346   - return Orbits;
1347   - })());
1348 1334 }).call(this);
... ...
web/static/js/swapp.ls
  1 +
  2 +
  3 +
  4 +
  5 +
  6 +
  7 +
  8 +
  9 +
  10 +
  11 +
  12 +
  13 +
  14 +
  15 +# DEPRECATED
  16 +# Use web/static/js/main.js instead.
  17 +
  18 +
  19 +
  20 +
  21 +
  22 +
  23 +
  24 +
  25 +
  26 +
  27 +
  28 +
  29 +
  30 +
  31 +
1 32 # Livescript transpiles to javascript, and is easier on the eyes and brain.
2 33 # Get the `lsc` binary from here : http://livescript.net
3 34 # It is quite close to Python, syntax-wise, and full of sugar.
... ...