Commit 8d4f8bc25f27b5fb8cf566e9ac7d473dc56c4cde

Authored by Goutte
1 parent 221bfc4d

More fixes towards THE GREAT REFACTORING

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