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 // Generated by LiveScript 1.5.0 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 }).call(this); 1325 }).call(this);