Commit 5a7474a9044036e2811e67f3efc8535e04ef528d

Authored by Goutte
1 parent fb461dbf

Lebab the livescript.

Showing 1 changed file with 1324 additions and 0 deletions   Show diff stats
web/static/js/main.js 0 → 100644
@@ -0,0 +1,1324 @@ @@ -0,0 +1,1324 @@
  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);
  199 + }
  200 + return this;
  201 + }
  202 +
  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 + });
  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 + }
  530 + }
  531 +
  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 + }
  765 +
  766 + onMouseOver() {
  767 + if (this.options.onMouseOver != null) {
  768 + return this.options.onMouseOver();
  769 + } else {
  770 + return this.showCursor();
  771 + }
  772 + }
  773 +
  774 + onMouseOut() {
  775 + if (this.options.onMouseOut != null) {
  776 + return this.options.onMouseOut();
  777 + } else {
  778 + return this.hideCursor();
  779 + }
  780 + }
  781 +
  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;
  980 + }
  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 +
  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;
  1059 + }
  1060 + if (slug in this.orbitersElements) {
  1061 + throw new Error(`Second init of ${slug}`);
  1062 + }
  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
  1073 + };
  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 + }
  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);