Compare commits

...

17 Commits

Author SHA1 Message Date
Matthieu Baumann
4781ffdef2 versionning 3.8 2026-01-31 16:44:06 +01:00
Matthieu Baumann
0a48b016f9 fix cargo clippy 2026-01-31 16:41:32 +01:00
Matthieu Baumann
b699a4bc12 toolbar functional 2026-01-31 16:01:51 +01:00
Matthieu Baumann
95dfcc2cff fix some bgs before beta 2026-01-30 01:11:24 +01:00
Matthieu Baumann
c3eca21aee colorpicker valid tooltip 2026-01-29 23:44:45 +01:00
Matthieu Baumann
4524586145 add toolbar 2026-01-29 19:56:42 +01:00
Matthieu Baumann
13c5dc1aa1 toggle refac + toolbar 2026-01-29 19:21:24 +01:00
Matthieu Baumann
b84a444634 fix local hips bug 2026-01-27 18:58:51 +01:00
Matthieu Baumann
e16f34fdad fix displacement bug when the inertia is triggered after a zoom/unzoom 2026-01-22 16:55:03 +01:00
Matthieu Baumann
c7fcf3d451 HiPS browser adjustements 2026-01-22 14:23:21 +01:00
Matthieu Baumann
7960ef9bde wip hips composite ui 2026-01-22 11:30:34 +01:00
Matthieu Baumann
c2e7838930 fix polygonal selection #339 2026-01-22 11:29:50 +01:00
Matthieu Baumann
601928bc8c add settings for catalogs 2026-01-16 14:46:03 +01:00
Matthieu Baumann
feb892fa03 simplify UI Layout 2026-01-15 18:23:33 +01:00
Matthieu Baumann
60022b2659 enhance hips browsing window stability 2026-01-13 16:51:01 +01:00
Matthieu Baumann
c69aa990b6 add floating tooltip 2026-01-10 15:26:05 +01:00
Matthieu Baumann
42209b166e first commit 2026-01-08 17:56:19 +01:00
78 changed files with 9260 additions and 8567 deletions

View File

@@ -12,6 +12,14 @@
* [fix] layer opacity restored when switching from not visible to visible <https://github.com/cds-astro/aladin-lite/issues/332>
* [feat] dark/light mode for the interface
* [fix] polylines shapes size not consistent w.r.t to div size <https://github.com/cds-astro/aladin-lite/issues/331>
* [feat] 'stackChanged' new event informing when a layer has been added, removed or swapped.
* [ui] a new HiPS browser window to search and find HiPS among the HiPS worldwide network.
* [ui] new settings panel for Catalog overlays to change the size, color or shapes of sources
* [ui] possibility to swap 2 layers. Functional but not definitive, it would be better to allow drag and drop amond the layers.
* [fix] fix local HiPS loading.
* [ui] WIP. A toolbar object
* [fix] fix selection of footprints. In the future allow a skew selection mode and a additive selection shortkey.
* [fix] inertia bug when zooming in/out
### 3.7.0-beta

17
assets/icons/enlarge.svg Normal file
View File

@@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
<svg width="800px" height="800px" viewBox="-9 0 32 32" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:sketch="http://www.bohemiancoding.com/sketch/ns">
<title>arrow-up-down</title>
<desc>Created with Sketch Beta.</desc>
<defs>
</defs>
<g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" sketch:type="MSPage">
<g id="Icon-Set-Filled" sketch:type="MSLayerGroup" transform="translate(-213.000000, -1193.000000)" fill="#000000">
<path d="M225,1217 L222,1217 L222,1201 L225,1201 C225.643,1201 226.293,1201.02 226.687,1200.62 C227.08,1200.23 227.08,1199.6 226.687,1199.2 L220.747,1193.28 C220.537,1193.07 220.259,1192.98 219.984,1193 C219.71,1192.98 219.432,1193.07 219.222,1193.28 L213.283,1199.2 C212.89,1199.6 212.89,1200.23 213.283,1200.62 C213.676,1201.02 214.294,1201 215,1201 L218,1201 L218,1217 L215,1217 C214.357,1217 213.676,1216.98 213.283,1217.38 C212.89,1217.77 212.89,1218.4 213.283,1218.8 L219.222,1224.72 C219.432,1224.93 219.71,1225.02 219.984,1225 C220.259,1225.02 220.537,1224.93 220.747,1224.72 L226.687,1218.8 C227.08,1218.4 227.08,1217.77 226.687,1217.38 C226.293,1216.98 225.737,1217 225,1217" id="arrow-up-down" sketch:type="MSShapeGroup">
</path>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.4 KiB

6
assets/icons/tree.svg Normal file
View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?><!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
<svg fill="#000000" width="800px" height="800px" viewBox="0 0 36 36" version="1.1" preserveAspectRatio="xMidYMid meet" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<title>tree-view-line</title>
<path d="M15,32H11a1,1,0,0,1-1-1V27a1,1,0,0,1,1-1h4a1,1,0,0,1,1,1v4A1,1,0,0,1,15,32Zm-3-2h2V28H12Z" class="clr-i-outline clr-i-outline-path-1"></path><path d="M15,16H11a1,1,0,0,0-1,1v1.2H5.8V12H7a1,1,0,0,0,1-1V7A1,1,0,0,0,7,6H3A1,1,0,0,0,2,7v4a1,1,0,0,0,1,1H4.2V29.8h6.36a.8.8,0,0,0,0-1.6H5.8V19.8H10V21a1,1,0,0,0,1,1h4a1,1,0,0,0,1-1V17A1,1,0,0,0,15,16ZM4,8H6v2H4ZM14,20H12V18h2Z" class="clr-i-outline clr-i-outline-path-2"></path><path d="M34,9a1,1,0,0,0-1-1H10v2H33A1,1,0,0,0,34,9Z" class="clr-i-outline clr-i-outline-path-3"></path><path d="M33,18H18v2H33a1,1,0,0,0,0-2Z" class="clr-i-outline clr-i-outline-path-4"></path><path d="M33,28H18v2H33a1,1,0,0,0,0-2Z" class="clr-i-outline clr-i-outline-path-5"></path>
<rect x="0" y="0" width="36" height="36" fill-opacity="0"/>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@@ -4,13 +4,16 @@
</head>
<body>
<div id="aladin-lite-div" style="width: 512px; height: 512px"></div>
<div id="aladin-lite-div" style="width: 512px; height: 512px">
<div id="toolbar"></div>
</div>
<script type="module">
import showUrl from './../assets/icons/show.svg';
import A from '../src/js/A.js';
let aladin;
A.init.then(() => {
aladin = A.aladin('#aladin-lite-div', {samp: true, survey: "data/hips/PanSTARRS_DR1_color-z-zg-g", fov:2.0, target: "22 35 58.39 +33 57 57.8", showSettingsControl: true, log: false});
aladin = A.aladin('#aladin-lite-div', {toolbar: {divSelector: '#toolbar'}, samp: true, survey: "data/hips/PanSTARRS_DR1_color-z-zg-g", fov:2.0, target: "22 35 58.39 +33 57 57.8", showSettingsControl: true, log: false});
aladin.setProjection('AIT');
let cfht = aladin.createImageSurvey("CFHT", "CFHT MegaCam u+g+r", "./data/hips/CFHT", "equatorial", 10, {imgFormat: 'png'});
let jwst1 = aladin.createImageSurvey("CDS/P/JWST/Stephans-Quintet/NIRCam+MIRI", "JWST NIRCam+MIRI", "data/hips/JWST_NIRCam_MIRI", null, null, {imgFormat: 'png'});
@@ -20,6 +23,16 @@
aladin.setOverlayImageLayer('JWST2', 'overlay_JWST2');
aladin.getOverlayImageLayer('overlay_JWST1').setAlpha(0.0);
aladin.getOverlayImageLayer('overlay_JWST2').setAlpha(0.0);
aladin.toolbar.add('action_custom', {
icon: {
url: showUrl,
size: "medium"
},
action(_) {
console.log("do a thing")
}
});
});
</script>

View File

@@ -19,7 +19,6 @@
<meta name="viewport" content="viewport-fit=cover, width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no">
</head>
<body>
<script src="https://code.jquery.com/jquery-1.10.1.min.js"></script>
<script type="text/javascript">
let aladin;
@@ -38,8 +37,8 @@
<!-- fin temporaire gestion cercle -->
<b>Orientation</b><br>
<button id="hips-coronelli" class="pure-button" name="ref-hips" onclick="longitudeReversed = false; aladin.reverseLongitude(longitudeReversed)">Normal</button><br>
<button id="hips-illenoroc" class="pure-button" name="ref-hips" onclick="longitudeReversed = true; aladin.reverseLongitude(longitudeReversed)">Inversé</button>
<button id="hips-coronelli" class="pure-button" name="ref-hips" onclick="aladin.reverseLongitude(false)">Normal</button><br>
<button id="hips-illenoroc" class="pure-button" name="ref-hips" onclick="aladin.reverseLongitude(true);">Inversé</button>
<br><br>
<b>Constellations</b>
@@ -75,6 +74,13 @@
<a href="#"><img id="coronelli-stars-red" class="catcoro coro-star" src="star_red.png"></a>
<a href="#"><img id="coronelli-stars-blue" class="catcoro coro-star" src="star_blue.png"></a>
</div>
<div>Coronelli2<br>
<!--a id="coronelli-stars" class="pure-button catcoro" href="#">Coronelli</a-->
<a href="#"><img id="coronelli-stars-white" class="catcoro coro-star" src="star_white.png"></a>
<a href="#"><img id="coronelli-stars-yellow" class="catcoro coro-star" src="star_yellow.png"></a><br>
<a href="#"><img id="coronelli-stars-red" class="catcoro coro-star" src="star_red.png"></a>
<a href="#"><img id="coronelli-stars-blue" class="catcoro coro-star" src="star_blue.png"></a>
</div>
<br><br>
<b>Navigation</b>&nbsp;&nbsp;<br><button id="stop">Stop</button>
@@ -99,6 +105,14 @@
<a class="pure-button nav-button nav-goto" href="#">Halley</a><br>
&nbsp;&nbsp;<a class="pure-button nav-button nav-flyto" href="#">Move</a>
</div>
<div id="coo_halley">
<a class="pure-button nav-button nav-goto" href="#">Halley</a><br>
&nbsp;&nbsp;<a class="pure-button nav-button nav-flyto" href="#">Move</a>
</div>
<div id="coo_halley">
<a class="pure-button nav-button nav-goto" href="#">Halley</a><br>
&nbsp;&nbsp;<a class="pure-button nav-button nav-flyto" href="#">Move</a>
</div>
</div>
<style type="text/css"> .aladin-reticleColor { color: rgb(178, 50, 178); font-weight:bold;} </style>
@@ -231,11 +245,10 @@
import {Utils} from '../src/js/Utils';
A.init.then(() => {
var hipsDir="http://alasky.u-strasbg.fr/CDS_P_Coronelli";
aladin = A.aladin("#aladin-lite-div", {showSimbadPointerControl: true, expandLayersControl: true, realFullscreen: true, fov: 100, allowFullZoomout: true, showReticle: false });
aladin.createImageSurvey('Coronelli', 'Coronelli', hipsDir, 'equatorial', 4, {imgFormat: 'jpg'});
aladin.setImageSurvey('Coronelli');
var hipsDir="http://alasky.cds.unistra.fr/CDS_P_Coronelli";
aladin = A.aladin("#aladin-lite-div", {showSimbadPointerControl: true, lockNorthUp: true, reverseLongitude: true, realFullscreen: true, fov: 100, allowFullZoomout: true, showReticle: false });
aladin.createImageSurvey('Coronelli', 'Coronelli', hipsDir, 'equatorial', 4, {imgFormat: 'jpg', minOrder: 3});
aladin.setImageSurvey('Coronelli')
$('#layersControlLeft').show();
$('#layersCL2').show();
$('#layersControlRight').show();
@@ -344,14 +357,14 @@
// listen click on navigation buttons
$('.nav-button').click(function() {
var cooTarget = $(this).parent().attr('id');
var cooTarget = $(this).parent().attr('id');
if ($(this).hasClass("nav-goto")) {
aladin.gotoRaDec(cooNav[cooTarget].ra, cooNav[cooTarget].dec);
}
else if ($(this).hasClass("nav-flyto")) {
aladin.animateToRaDec(cooNav[cooTarget].ra, cooNav[cooTarget].dec, cooNav[cooTarget].time);
}
if ($(this).hasClass("nav-goto")) {
aladin.gotoRaDec(cooNav[cooTarget].ra, cooNav[cooTarget].dec);
}
else if ($(this).hasClass("nav-flyto")) {
aladin.animateToRaDec(cooNav[cooTarget].ra, cooNav[cooTarget].dec, cooNav[cooTarget].time);
}
});
// stop animations

View File

@@ -68,8 +68,8 @@
console.log(pos)
});
aladin.on('layerChanged', function(imageLayer, layer, state){
console.log(imageLayer, layer, state)
aladin.on('stackChanged', function(state) {
console.log(state)
});
cat.sources[0].actionClicked();

View File

@@ -13,6 +13,9 @@
aladin = A.aladin(
'#aladin-lite-div',
{
toolbar: {
vertical: false,
},
showSimbadPointerControl: true,
survey: 'https://skies.esac.esa.int/AKARI/color/', // set initial image survey
projection: 'AIT', // set a projection
@@ -23,6 +26,8 @@
reticleSize: 64, // change reticle size
showContextMenu: true,
showShareControl: true,
showCooGridControl: true,
showColorPickerControl: true,
showFrame: true,
showZoomControl:true,
showSettingsControl:true,

View File

@@ -2,7 +2,7 @@
"homepage": "https://aladin.cds.unistra.fr/",
"name": "aladin-lite",
"type": "module",
"version": "3.7.3-beta",
"version": "3.8.0-beta",
"description": "An astronomical HiPS visualizer in the browser",
"author": "Thomas Boch and Matthieu Baumann",
"license": "GPL-3",

View File

@@ -3,7 +3,7 @@ name = "aladin-lite"
description = "Aladin Lite v3 introduces a new graphical engine written in Rust with the use of WebGL"
license = "BSD-3-Clause"
repository = "https://github.com/cds-astro/aladin-lite"
version = "3.7.3-beta"
version = "3.8.0-beta"
authors = [ "baumannmatthieu0@gmail.com", "matthieu.baumann@astro.unistra.fr",]
edition = "2018"

View File

@@ -1,6 +1,6 @@
[package]
name = "al-api"
version = "3.7.0"
version = "3.8.0"
authors = ["baumannmatthieu0@gmail.com", "matthieu.baumann@astro.unistra.fr"]
edition = "2018"

View File

@@ -180,11 +180,11 @@ impl HiPSProperties {
#[serde(rename_all = "camelCase")]
pub enum ImageExt {
Fits,
#[serde(alias = "fits.fz")]
FitsFz,
Jpeg,
Png,
Webp,
#[serde(alias = "fits.fz")]
FitsFz,
}
#[derive(Deserialize, Debug, Clone, Copy, PartialEq, Eq, Hash)]

View File

@@ -1,6 +1,6 @@
[package]
name = "al-core"
version = "3.7.0"
version = "3.8.0"
authors = ["baumannmatthieu0@gmail.com", "matthieu.baumann@astro.unistra.fr"]
edition = "2018"

View File

@@ -1605,6 +1605,10 @@ impl App {
return;
}
if self.vel_history.len() < 5 {
return;
}
let now = Time::now();
let avg_vel = self.vel_history.iter().copied().sum::<f32>() / self.vel_history.len() as f32;
@@ -1650,6 +1654,11 @@ impl App {
// For the moment, no animation is triggered.
// The fov is directly set
self.camera.set_aperture(fov, &self.projection);
// reset the parameters that determine if an inertia is needed
self.vel_history.clear();
self.dist_dragging = 0.0;
self.request_for_new_tiles = true;
self.request_redraw = true;
}
@@ -1807,6 +1816,10 @@ impl App {
pub(crate) fn set_zoom_factor(&mut self, zoom_factor: f64) {
self.camera.set_zoom_factor(zoom_factor, &self.projection);
// reset the parameters that determine if an inertia is needed
self.vel_history.clear();
self.dist_dragging = 0.0;
self._update_hips_location();
self.request_for_new_tiles = true;

View File

@@ -224,6 +224,8 @@ impl CameraViewPort {
self.view_hpx_cells.get_cells(depth, frame)
}
// This method has the role to determine the render mode based on the fov
// For large FoV, raytracing drawing mode, rasterizer otherwise
pub fn is_raytracing(&self, proj: &ProjectionType) -> bool {
// Check whether the tile depth is 0 for square projection
// definition domains i.e. Mercator
@@ -240,6 +242,7 @@ impl CameraViewPort {
ProjectionType::Ait(_) => self.aperture >= 100.0_f64.to_radians(),
ProjectionType::Mol(_) => self.aperture >= 100.0_f64.to_radians(),
ProjectionType::Zea(_) => self.aperture >= 140.0_f64.to_radians(),
_ => self.aperture >= 140.0_f64.to_radians(),
}
}

View File

@@ -231,7 +231,7 @@ impl WebClient {
"ZEA" => self
.app
.set_projection(ProjectionType::Zea(mapproj::zenithal::zea::Zea::new())), /* Equal-area */
/*"FEYE" => self
"FEYE" => self
.app
.set_projection(ProjectionType::Feye(mapproj::zenithal::feye::Feye::new())),
"AIR" => {
@@ -239,19 +239,19 @@ impl WebClient {
//air_proj.set_n_iter(10);
//air_proj.set_eps(1e-12);
self.app.set_projection(ProjectionType::Air(air_proj))
}*/
}
//"AZP",
/*"ARC" => self
"ARC" => self
.app
.set_projection(ProjectionType::Arc(mapproj::zenithal::arc::Arc::new())),
"NCP" => self
.app
.set_projection(ProjectionType::Ncp(mapproj::zenithal::ncp::Ncp::new())),*/
.set_projection(ProjectionType::Ncp(mapproj::zenithal::ncp::Ncp::new())),
// Cylindrical
"MER" => self
.app
.set_projection(ProjectionType::Mer(mapproj::cylindrical::mer::Mer::new())),
/*"CAR" => self
"CAR" => self
.app
.set_projection(ProjectionType::Car(mapproj::cylindrical::car::Car::new())),
"CEA" => self
@@ -259,17 +259,17 @@ impl WebClient {
.set_projection(ProjectionType::Cea(mapproj::cylindrical::cea::Cea::new())),
"CYP" => self
.app
.set_projection(ProjectionType::Cyp(mapproj::cylindrical::cyp::Cyp::new())),*/
.set_projection(ProjectionType::Cyp(mapproj::cylindrical::cyp::Cyp::new())),
// Pseudo-cylindrical
"AIT" => self
.app
.set_projection(ProjectionType::Ait(mapproj::pseudocyl::ait::Ait::new())),
/*"PAR" => self
"PAR" => self
.app
.set_projection(ProjectionType::Par(mapproj::pseudocyl::par::Par::new())),
"SFL" => self
.app
.set_projection(ProjectionType::Sfl(mapproj::pseudocyl::sfl::Sfl::new())),*/
.set_projection(ProjectionType::Sfl(mapproj::pseudocyl::sfl::Sfl::new())),
"MOL" => {
let mut mol_proj = mapproj::pseudocyl::mol::Mol::new();
mol_proj.set_n_iter(10);
@@ -277,13 +277,13 @@ impl WebClient {
self.app.set_projection(ProjectionType::Mol(mol_proj))
} // Conic
/*"COD" => self
"COD" => self
.app
.set_projection(ProjectionType::Cod(mapproj::conic::cod::Cod::new())),
// Hybrid
"HPX" => self
.app
.set_projection(ProjectionType::Hpx(mapproj::hybrid::hpx::Hpx::new())),*/
.set_projection(ProjectionType::Hpx(mapproj::hybrid::hpx::Hpx::new())),
_ => Err(JsValue::from_str(
"Not a valid projection name. AIT, ZEA, SIN, STG, TAN, MOL and MER are accepted",
)),

View File

@@ -9,6 +9,7 @@
// World space
use crate::camera::CameraViewPort;
use crate::domain::sdf::ProjDefType;
use crate::math::rotation::Rotation;
use coo_space::XYZModel;
//use crate::num_traits::FloatConst;
@@ -22,7 +23,7 @@ pub mod domain;
use crate::math::angle::ToAngle;
use crate::math::angle::Angle;
use domain::{basic, full::FullScreen};
use domain::{basic, cod::Cod, full::FullScreen, hpx::Hpx, par::Par};
/* S <-> NDC space conversion methods */
pub fn screen_to_ndc_space(
pos_screen_space: &XYScreen<f64>,
@@ -114,15 +115,15 @@ pub enum ProjectionType {
/* ZEA, Equal-area */
Zea(mapproj::zenithal::zea::Zea),
/* FEYE, Fish-eyes */
//Feye(mapproj::zenithal::feye::Feye),
Feye(mapproj::zenithal::feye::Feye),
/* AIR, */
//Air(mapproj::zenithal::air::Air),
Air(mapproj::zenithal::air::Air),
//AZP: {fov: 180},
//Azp(mapproj::zenithal::azp::Azp),
/* ARC, */
//Arc(mapproj::zenithal::arc::Arc),
Arc(mapproj::zenithal::arc::Arc),
/* NCP, */
//Ncp(mapproj::zenithal::ncp::Ncp),
Ncp(mapproj::zenithal::ncp::Ncp),
// Pseudo-cylindrical projections
/* AIT, Aitoff */
@@ -130,26 +131,26 @@ pub enum ProjectionType {
// MOL, Mollweide */
Mol(mapproj::pseudocyl::mol::Mol),
// PAR, */
//Par(mapproj::pseudocyl::par::Par),
Par(mapproj::pseudocyl::par::Par),
// SFL, */
//Sfl(mapproj::pseudocyl::sfl::Sfl),
Sfl(mapproj::pseudocyl::sfl::Sfl),
// Cylindrical projections
// MER, Mercator */
Mer(mapproj::cylindrical::mer::Mer),
// CAR, */
//Car(mapproj::cylindrical::car::Car),
Car(mapproj::cylindrical::car::Car),
// CEA, */
//Cea(mapproj::cylindrical::cea::Cea),
Cea(mapproj::cylindrical::cea::Cea),
// CYP, */
//Cyp(mapproj::cylindrical::cyp::Cyp),
Cyp(mapproj::cylindrical::cyp::Cyp),
// Conic projections
// COD, */
//Cod(mapproj::conic::cod::Cod),
Cod(mapproj::conic::cod::Cod),
// HEALPix hybrid projection
//Hpx(mapproj::hybrid::hpx::Hpx),
Hpx(mapproj::hybrid::hpx::Hpx),
}
use crate::math::lonlat::LonLat;
@@ -305,38 +306,38 @@ impl ProjectionType {
/* ZEA, Equal-area */
ProjectionType::Zea(_) => 1.0,
/* FEYE, Fish-eyes */
//ProjectionType::Feye(_) => 1.0,
ProjectionType::Feye(_) => 1.0,
/* AIR, */
//ProjectionType::Air(_) => 1.0,
ProjectionType::Air(_) => 1.0,
//AZP: {fov: 180},
//Azp(mapproj::zenithal::azp::Azp),
/* ARC, */
//ProjectionType::Arc(_) => 1.0,
ProjectionType::Arc(_) => 1.0,
/* NCP, */
//ProjectionType::Ncp(_) => 1.0,
ProjectionType::Ncp(_) => 1.0,
// Pseudo-cylindrical projections
/* AIT, Aitoff */
ProjectionType::Ait(_) => 2.0,
// MOL, Mollweide */
ProjectionType::Mol(_) => 2.0,
// PAR, */
//ProjectionType::Par(_) => 2.0,
ProjectionType::Par(_) => 2.0,
// SFL, */
//ProjectionType::Sfl(_) => 2.0,
ProjectionType::Sfl(_) => 2.0,
// Cylindrical projections
// MER, Mercator */
ProjectionType::Mer(_) => 1.0,
// CAR, */
//ProjectionType::Car(_) => 1.0,
ProjectionType::Car(_) => 1.0,
// CEA, */
//ProjectionType::Cea(_) => 1.0,
ProjectionType::Cea(_) => 1.0,
// CYP, */
//ProjectionType::Cyp(_) => 1.0,
ProjectionType::Cyp(_) => 1.0,
// Conic projections
// COD, */
//ProjectionType::Cod(_) => 1.0,
ProjectionType::Cod(_) => 1.0,
// HEALPix hybrid projection
//ProjectionType::Hpx(_) => 2.0,
ProjectionType::Hpx(_) => 2.0,
}
}
@@ -352,15 +353,15 @@ impl ProjectionType {
/* ZEA, Equal-area */
ProjectionType::Zea(_) => 359.9_f64.to_radians().to_angle(),
/* FEYE, Fish-eyes */
//ProjectionType::Feye(_) => 190.0,
ProjectionType::Feye(_) => 190.0_f64.to_radians().to_angle(),
/* AIR, */
//ProjectionType::Air(_) => 360.0,
ProjectionType::Air(_) => 360.0_f64.to_radians().to_angle(),
//AZP: {fov: 180},
//Azp(mapproj::zenithal::azp::Azp),
//ProjectionType::Azp(mapproj::zenithal::azp::Azp),
/* ARC, */
//ProjectionType::Arc(_) => 360.0,
ProjectionType::Arc(_) => 360.0_f64.to_radians().to_angle(),
/* NCP, */
//ProjectionType::Ncp(_) => 180.0,
ProjectionType::Ncp(_) => 180.0_f64.to_radians().to_angle(),
// Pseudo-cylindrical projections
/* AIT, Aitoff */
@@ -368,26 +369,26 @@ impl ProjectionType {
// MOL, Mollweide */
ProjectionType::Mol(_) => 360.0_f64.to_radians().to_angle(),
// PAR, */
//ProjectionType::Par(_) => 360.0,
ProjectionType::Par(_) => 360.0_f64.to_radians().to_angle(),
// SFL, */
//ProjectionType::Sfl(_) => 360.0,
ProjectionType::Sfl(_) => 360.0_f64.to_radians().to_angle(),
// Cylindrical projections
// MER, Mercator */
ProjectionType::Mer(_) => 360.0_f64.to_radians().to_angle(),
// CAR, */
//ProjectionType::Car(_) => 360.0,
ProjectionType::Car(_) => 360.0_f64.to_radians().to_angle(),
// CEA, */
//ProjectionType::Cea(_) => 360.0,
ProjectionType::Cea(_) => 360.0_f64.to_radians().to_angle(),
// CYP, */
//ProjectionType::Cyp(_) => 360.0,
ProjectionType::Cyp(_) => 360.0_f64.to_radians().to_angle(),
// Conic projections
// COD, */
//ProjectionType::Cod(_) => 330.0,
ProjectionType::Cod(_) => 330.0_f64.to_radians().to_angle(),
// HEALPix hybrid projection
//ProjectionType::Hpx(_) => 360.0,
ProjectionType::Hpx(_) => 360.0_f64.to_radians().to_angle(),
}
}
@@ -415,27 +416,27 @@ impl ProjectionType {
&DISK
}
/* FEYE, Fish-eyes */
/*ProjectionType::Feye(_) => {
ProjectionType::Feye(_) => {
const DISK: ProjDefType = ProjDefType::Disk(basic::disk::Disk { radius: 1.0 });
&DISK
}*/
}
/* AIR, */
/*ProjectionType::Air(_) => {
ProjectionType::Air(_) => {
const DISK: ProjDefType = ProjDefType::FullScreen(FullScreen);
&DISK
}*/
}
//AZP: {fov: 180},
//Azp(mapproj::zenithal::azp::Azp),
/* ARC, */
/*ProjectionType::Arc(_) => {
ProjectionType::Arc(_) => {
const DISK: ProjDefType = ProjDefType::Disk(basic::disk::Disk { radius: 1.0 });
&DISK
}*/
}
/* NCP, */
/*ProjectionType::Ncp(_) => {
ProjectionType::Ncp(_) => {
const DISK: ProjDefType = ProjDefType::Disk(basic::disk::Disk { radius: 1.0 });
&DISK
}*/
}
// Pseudo-cylindrical projections
/* AIT, Aitoff */
@@ -449,15 +450,15 @@ impl ProjectionType {
&ELLIPSE
}
// PAR, */
/*ProjectionType::Par(_) => {
ProjectionType::Par(_) => {
const PAR: ProjDefType = ProjDefType::Par(Par);
&PAR
}*/
}
// SFL, */
/*ProjectionType::Sfl(_) => {
ProjectionType::Sfl(_) => {
const PAR: ProjDefType = ProjDefType::Par(Par);
&PAR
}*/
}
// Cylindrical projections
// MER, Mercator */
@@ -465,32 +466,32 @@ impl ProjectionType {
const FULL_SCREEN: ProjDefType = ProjDefType::FullScreen(FullScreen);
&FULL_SCREEN
} // CAR, */
/*ProjectionType::Car(_) => {
const FULL_SCREEN: ProjDefType = ProjDefType::FullScreen(FullScreen);
&FULL_SCREEN
}*/
// CEA, */
/*ProjectionType::Cea(_) => {
const FULL_SCREEN: ProjDefType = ProjDefType::FullScreen(FullScreen);
&FULL_SCREEN
}*/
// CYP, */
/*ProjectionType::Cyp(_) => {
const FULL_SCREEN: ProjDefType = ProjDefType::FullScreen(FullScreen);
&FULL_SCREEN
}*/
ProjectionType::Car(_) => {
const FULL_SCREEN: ProjDefType = ProjDefType::FullScreen(FullScreen);
&FULL_SCREEN
}
// CEA, */
ProjectionType::Cea(_) => {
const FULL_SCREEN: ProjDefType = ProjDefType::FullScreen(FullScreen);
&FULL_SCREEN
}
// CYP, */
ProjectionType::Cyp(_) => {
const FULL_SCREEN: ProjDefType = ProjDefType::FullScreen(FullScreen);
&FULL_SCREEN
}
// Conic projections
// COD, */
/*ProjectionType::Cod(_) => {
const CONIC: ProjDefType = ProjDefType::Cod(Cod::new());
&CONIC
}*/
// HEALPix hybrid projection
/*ProjectionType::Hpx(_) => {
const HPX_DEF_REG: ProjDefType = ProjDefType::Hpx(Hpx);
&HPX_DEF_REG
}*/
// Conic projections
// COD, */
ProjectionType::Cod(_) => {
const CONIC: ProjDefType = ProjDefType::Cod(Cod::new());
&CONIC
}
// HEALPix hybrid projection
ProjectionType::Hpx(_) => {
const HPX_DEF_REG: ProjDefType = ProjDefType::Hpx(Hpx);
&HPX_DEF_REG
}
}
}
}
@@ -509,15 +510,15 @@ impl Projection for ProjectionType {
/* ZEA, Equal-area */
ProjectionType::Zea(zea) => zea.clip_to_world_space(xy),
/* FEYE, Fish-eyes */
//ProjectionType::Feye(feye) => feye.clip_to_world_space(xy),
ProjectionType::Feye(feye) => feye.clip_to_world_space(xy),
/* AIR, */
//ProjectionType::Air(air) => air.clip_to_world_space(xy),
ProjectionType::Air(air) => air.clip_to_world_space(xy),
//AZP: {fov: 180},
//Azp(mapproj::zenithal::azp::Azp),
/* ARC, */
//ProjectionType::Arc(arc) => arc.clip_to_world_space(xy),
ProjectionType::Arc(arc) => arc.clip_to_world_space(xy),
/* NCP, */
//ProjectionType::Ncp(ncp) => ncp.clip_to_world_space(xy),
ProjectionType::Ncp(ncp) => ncp.clip_to_world_space(xy),
// Pseudo-cylindrical projections
/* AIT, Aitoff */
@@ -525,30 +526,34 @@ impl Projection for ProjectionType {
// MOL, Mollweide */
ProjectionType::Mol(mol) => mol.clip_to_world_space(xy),
// PAR, */
//ProjectionType::Par(par) => par.clip_to_world_space(xy),
ProjectionType::Par(par) => par.clip_to_world_space(xy),
// SFL, */
//ProjectionType::Sfl(sfl) => sfl.clip_to_world_space(xy),
ProjectionType::Sfl(sfl) => sfl.clip_to_world_space(xy),
// Cylindrical projections
// MER, Mercator */
ProjectionType::Mer(mer) => mer.clip_to_world_space(xy),
// CAR, */
//ProjectionType::Car(car) => car.clip_to_world_space(xy),
ProjectionType::Car(car) => car.clip_to_world_space(xy),
// CEA, */
//ProjectionType::Cea(cea) => cea.clip_to_world_space(xy),
ProjectionType::Cea(cea) => cea.clip_to_world_space(xy),
// CYP, */
//ProjectionType::Cyp(cyp) => cyp.clip_to_world_space(xy),
ProjectionType::Cyp(cyp) => cyp.clip_to_world_space(xy),
// Conic projections
// COD, */
/*ProjectionType::Cod(cod) => cod.clip_to_world_space(xy).map(|xyz| {
ProjectionType::Cod(cod) => cod.clip_to_world_space(xy).map(|xyz| {
let rot = Rotation::from_sky_position(
&LonLatT::new(0.0_f64.to_angle(), (HALF_PI * 0.5).to_angle()).vector(),
&LonLatT::new(
0.0_f64.to_angle(),
(mapproj::math::HALF_PI * 0.5).to_angle(),
)
.vector(),
);
rot.inv_rotate(&xyz)
}),*/
}),
// HEALPix hybrid projection
//ProjectionType::Hpx(hpx) => hpx.clip_to_world_space(xy),
ProjectionType::Hpx(hpx) => hpx.clip_to_world_space(xy),
}
}
@@ -565,15 +570,15 @@ impl Projection for ProjectionType {
/* ZEA, Equal-area */
ProjectionType::Zea(zea) => zea.world_to_clip_space(xyz),
/* FEYE, Fish-eyes */
//ProjectionType::Feye(feye) => feye.world_to_clip_space(xyz),
ProjectionType::Feye(feye) => feye.world_to_clip_space(xyz),
/* AIR, */
//ProjectionType::Air(air) => air.world_to_clip_space(xyz),
ProjectionType::Air(air) => air.world_to_clip_space(xyz),
//AZP: {fov: 180},
//Azp(mapproj::zenithal::azp::Azp),
/* ARC, */
//ProjectionType::Arc(arc) => arc.world_to_clip_space(xyz),
ProjectionType::Arc(arc) => arc.world_to_clip_space(xyz),
/* NCP, */
//ProjectionType::Ncp(ncp) => ncp.world_to_clip_space(xyz),
ProjectionType::Ncp(ncp) => ncp.world_to_clip_space(xyz),
// Pseudo-cylindrical projections
/* AIT, Aitoff */
@@ -581,30 +586,34 @@ impl Projection for ProjectionType {
// MOL, Mollweide */
ProjectionType::Mol(mol) => mol.world_to_clip_space(xyz),
// PAR, */
//ProjectionType::Par(par) => par.world_to_clip_space(xyz),
ProjectionType::Par(par) => par.world_to_clip_space(xyz),
// SFL, */
//ProjectionType::Sfl(sfl) => sfl.world_to_clip_space(xyz),
ProjectionType::Sfl(sfl) => sfl.world_to_clip_space(xyz),
// Cylindrical projections
// MER, Mercator */
ProjectionType::Mer(mer) => mer.world_to_clip_space(xyz),
// CAR, */
//ProjectionType::Car(car) => car.world_to_clip_space(xyz),
ProjectionType::Car(car) => car.world_to_clip_space(xyz),
// CEA, */
//ProjectionType::Cea(cea) => cea.world_to_clip_space(xyz),
ProjectionType::Cea(cea) => cea.world_to_clip_space(xyz),
// CYP, */
//ProjectionType::Cyp(cyp) => cyp.world_to_clip_space(xyz),
ProjectionType::Cyp(cyp) => cyp.world_to_clip_space(xyz),
// Conic projections
// COD, */
/*ProjectionType::Cod(cod) => {
ProjectionType::Cod(cod) => {
// The Cod projection is centered on (0, 45 deg)
let rot = Rotation::from_sky_position(
&LonLatT::new(0.0_f64.to_angle(), (HALF_PI * 0.5).to_angle()).vector(),
&LonLatT::new(
0.0_f64.to_angle(),
(mapproj::math::HALF_PI * 0.5).to_angle(),
)
.vector(),
);
cod.world_to_clip_space(&rot.rotate(&xyz))
}*/
cod.world_to_clip_space(&rot.rotate(xyz))
}
// HEALPix hybrid projection
//ProjectionType::Hpx(hpx) => hpx.world_to_clip_space(xyz),
ProjectionType::Hpx(hpx) => hpx.world_to_clip_space(xyz),
}
}
}
@@ -634,6 +643,7 @@ impl UniformType for ProjectionType {
// Cylindrical projections
// MER, Mercator */
ProjectionType::Mer(_) => gl.uniform1i(location, 6),
_ => gl.uniform1i(location, 6),
}
}
}
@@ -808,7 +818,7 @@ mod tests {
"./../img/zea.jpg",
ProjectionType::Zea(mapproj::zenithal::zea::Zea),
);
/*generate_projection_map(
generate_projection_map(
"./../img/feye.png",
ProjectionType::Feye(mapproj::zenithal::feye::Feye),
);
@@ -823,14 +833,14 @@ mod tests {
generate_projection_map(
"./../img/air.png",
ProjectionType::Air(mapproj::zenithal::air::Air::new()),
);*/
);
// Cylindrical
generate_projection_map(
"./../img/mer.jpg",
ProjectionType::Mer(mapproj::cylindrical::mer::Mer),
);
/*generate_projection_map(
generate_projection_map(
"./../img/car.png",
ProjectionType::Car(mapproj::cylindrical::car::Car),
);
@@ -841,26 +851,26 @@ mod tests {
generate_projection_map(
"./../img/cyp.png",
ProjectionType::Cyp(mapproj::cylindrical::cyp::Cyp::new()),
);*/
);
// Pseudo-cylindrical
generate_projection_map(
"./../img/ait.jpg",
ProjectionType::Ait(mapproj::pseudocyl::ait::Ait),
);
/*generate_projection_map(
generate_projection_map(
"./../img/car.png",
ProjectionType::Par(mapproj::pseudocyl::par::Par),
);
generate_projection_map(
"./../img/cea.png",
ProjectionType::Sfl(mapproj::pseudocyl::sfl::Sfl),
);*/
);
generate_projection_map(
"./../img/mol.jpg",
ProjectionType::Mol(mapproj::pseudocyl::mol::Mol::new()),
);
// Conic
/*generate_projection_map(
generate_projection_map(
"./../img/cod.png",
ProjectionType::Cod(mapproj::conic::cod::Cod::new()),
);
@@ -868,6 +878,6 @@ mod tests {
generate_projection_map(
"./../img/hpx.png",
ProjectionType::Hpx(mapproj::hybrid::hpx::Hpx),
);*/
);
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -16,7 +16,6 @@
/* Aladin lite default color */
--aladin-color: #b232b2;
--aladin-color-border: #fff;
}
[data-theme="dark"] {
@@ -37,14 +36,14 @@
--hover-color: green;
--toggle-color: dodgerblue;
--border-size: 2px;
--valid-color: greenyellow;
--valid-color: green;
--error-color: red;
}
.aladin-tree {
width: 100%;
border-top: 1px solid var(--border-color);
border-bottom: 2px solid var(--border-color);
height: calc(100% - 130px);
border-bottom: var(--border-size) solid var(--border-color);
}
.aladin-tree .aladin-directory-path {
@@ -57,26 +56,62 @@
.aladin-tree ul {
padding:0;
margin:0;
overflow-y: scroll;
height: 300px;
overflow-y: auto;
height: calc(100% - 37px);
}
.aladin-link {
list-style-type: none;
padding: 0.5em 0px;
cursor:pointer;
margin: 0;
}
.aladin-tree li:hover {
color: var(--hover-color);
}
.aladin-tree li:last-child {
border-bottom: none;
}
.aladin-tree li {
border-bottom: 1px solid var(--border-color);
.aladin-tree li > * {
border-bottom: var(--border-size) solid var(--border-color);
padding: 0.5em 0px;
}
.aladin-tree li:last-of-type > * {
border-bottom: 0px;
}
#aladin-tooltip-mouse {
display: none;
position: fixed;
pointer-events: none; /* tooltip won't block mouse */
z-index: 1000;
padding: 0;
max-width: 100%;
width: fit-content;
}
#aladin-tooltip-mouse * {
margin: 0;
}
.aladin-fig {
display: inline-block; /* shrink-wraps content */
position: relative;
}
.aladin-fig img {
display: block;
max-width: 128px;
max-height: 128px;
}
.aladin-fig figcaption {
position: absolute;
bottom: 0;
left: 50%;
transform: translateX(-50%);
color: white;
font-size: 11px;
text-align: center;
background: black;
}
.aladin-lite-spectra-displayer .aladin-spectra-unit-selector {
@@ -195,7 +230,7 @@
color: green;
}
.aladin-measurement-div table tr td a:hover {
color: var(--hover-color);
color: var(--aladin-color);
}
.aladin-measurement-div table tr td {
@@ -249,11 +284,22 @@
.aladin-anchor-top{top:0}
.aladin-anchor-middle{top:50%; transform: translate(0, -50%);/*half of the .box height*/}
.aladin-anchor-bottom{bottom:0}
.aladin-anchor-bottom{
bottom:0;
.aladin-anchor-left{left:0;}
.aladin-anchor-center{left:50%; transform: translate(-50%, 0%);/*half of the .box width*/}
.aladin-anchor-right{right:0;}
}
.aladin-anchor-left {
left: 0;
}
.aladin-anchor-center{
left:50%;
transform: translate(-50%, 0%);/*half of the .box width*/
}
.aladin-anchor-right{
right:0;
}
.aladin-anchor-middle.aladin-anchor-center {
transform: translate(-50%, -50%);
@@ -286,7 +332,7 @@
}
.aladin-box {
padding: 0.2rem;
padding: 0.3rem;
background: whitesmoke;
position: absolute;
font-size: inherit;
@@ -301,6 +347,12 @@
width: min-content;
border: var(--border-size) solid var(--border-color);
box-sizing: border-box;
}
.aladin-box .aladin-box-content {
height: 100%;
}
.aladin-container canvas {
@@ -318,12 +370,8 @@
line-height: 1.5em;
font-weight: bold;
white-space: nowrap;
width: 100%;
}
.aladin-box-content {
padding: 10px;
}
.aladin-shareInput {
width: 300px;
margin-top: 10px;
@@ -353,7 +401,7 @@
margin-top: 0 0 2px 0;
cursor:pointer;
color: #605F61;
border: var(--border-size) solid #AEAEAE;
border: var(--border-size) solid var(--border-color);
border-radius: 3px;
background: #fff;
font-size: 1.0rem;
@@ -368,8 +416,10 @@
.aladin-box-separator {
height: 0;
border-top: 1px solid #d2d2d2;
padding-bottom: 5px;
border-top: var(--border-size) solid var(--border-color);
margin-top: 5px;
margin-bottom: 5px;
}
.aladin-restore {
@@ -438,12 +488,6 @@
height: 100%;
}
/*
.aladin-icon.aladin-dark-theme {
background-color: transparent;
}
*/
[data-theme="dark"] .aladin-icon.aladin-icon-monochrome img {
filter: invert(100%) sepia(91%) saturate(0%) hue-rotate(169deg) brightness(115%) contrast(100%);
}
@@ -473,26 +517,27 @@
color: var(--text-color);
}
.aladin-container .small-sized-icon {
.aladin-small-sized-icon {
width: 1.2rem;
height: 1.2rem;
}
.aladin-container .medium-sized-icon {
.aladin-medium-sized-icon {
width: 1.6rem;
height: 1.6rem;
}
.aladin-container .medium-sized {
.aladin-medium-sized {
height: 1.6rem;
}
.aladin-container .small-sized {
.aladin-small-sized {
height: 1.2rem;
}
.aladin-input-text:focus, .aladin-input-number:focus {
border-color: var(--toggle-color);
background-color: var(--bg-color);
}
[data-theme="light"] .aladin-input-text.search:focus {
@@ -529,12 +574,6 @@
line-height: 1.2rem;
}
.aladin-input-text.search.aladin-not-valid {
-webkit-box-shadow:inset 0px 0px 0px 1px #f00;
-moz-box-shadow:inset 0px 0px 0px 1px #f00;
box-shadow:inset 0px 0px 0px 1px #f00;
}
.aladin-input-text.aladin-not-valid {
border: var(--border-size) solid var(--error-color);
}
@@ -543,8 +582,16 @@
border: var(--border-size) solid var(--valid-color);
}
.aladin-box-content > div {
margin: 10px 0px 0px 0px;
.aladin-valid {
color: var(--valid-color);
}
.aladin-not-valid {
color: var(--error-color);
}
.aladin-not-found {
color: darkorange;
}
.aladin-button:hover {
@@ -554,18 +601,7 @@
}
.aladin-vertical-list {
display: flex;
align-items: flex-start;
list-style: none;
flex-direction: column;
}
.aladin-vertical-list.left {
align-items: flex-start;
}
.aladin-vertical-list.right {
align-items: flex-end;
}
.aladin-vertical-list > *:first-child {
@@ -601,10 +637,6 @@
justify-content: space-between;
}
.aladin-form-group > * {
margin-bottom: 0.4rem;
}
.aladin-form-group > *:last-of-type {
margin-bottom: 0;
}
@@ -811,7 +843,13 @@
margin-left: 2px;
}
.aladin-context-menu-item:hover > .aladin-context-sub-menu {
.aladin-stack-box .aladin-overlay-label {
text-align: center;
width: 100%;
}
.aladin-context-menu-item:hover > .aladin-context-sub-menu,
.aladin-context-menu-item:focus-within > .aladin-context-sub-menu {
display: block;
}
@@ -834,6 +872,22 @@
text-overflow: ellipsis;
}
.aladin-context-menu.left {
left: 0;
transform: translateX(-100%);
}
.aladin-context-menu.top{
top: 0;
left: 0;
transform: translateY(-100%);
}
.aladin-context-menu.bottom {
top: 100%;
left: 0;
}
.aladin-context-menu .aladin-context-sub-menu.left {
left: 0;
transform: translateX(-100%);
@@ -858,6 +912,29 @@
transform: translate(-100%, -100%);
}
.aladin-box.left {
left: 0;
transform: translateX(-100%);
}
.aladin-box.right {
right: 0;
}
.aladin-box.bottom {
top: 100%;
left: 0;
}
.aladin-box.top {
top: 0;
left: 0;
transform: translateY(-100%);
}
.aladin-box.left.top {
transform: translate(-100%, -100%);
}
.aladin-reticle {
position: absolute;
@@ -882,7 +959,7 @@
-moz-appearance: none;
-webkit-appearance: none;
outline-style: none;
padding: 0.2rem;
padding: 0.5rem;
border-radius: 0.2rem;
font-family: monospace;
box-sizing: border-box;
@@ -912,9 +989,6 @@
cursor: pointer;
font-family: monospace;
box-sizing: content-box;
/* <option> colors */
/* Remove focus outline */
/* Remove IE arrow */
}
/* This is done so that the select shrink to the size of its parent (coupled with flex)
@@ -943,7 +1017,7 @@ otherwise it fits its content options. If those are too big the select can go ou
-webkit-appearance: none;
outline-style: none;
padding: 0;
border: none;
border: var(--border-size) solid var(--border-color);
border-radius: 0.4rem;
width: 2rem;
height: 1.5rem;
@@ -952,20 +1026,17 @@ otherwise it fits its content options. If those are too big the select can go ou
.aladin-input-color::-webkit-color-swatch {
border: none;
border-radius: 0.4rem;
padding: 0;
}
.aladin-input-color::-webkit-color-swatch-wrapper {
border: none;
border-radius: 0.4rem;
padding: 0;
}
/********** Range Input Styles **********/
/*Range Reset*/
.aladin-input-range {
background: transparent;
cursor: pointer;
margin: 0.8rem 0;
box-sizing: border-box;
@@ -981,11 +1052,20 @@ otherwise it fits its content options. If those are too big the select can go ou
.aladin-input-range:focus {
outline: none;
}
.aladin-input-text:-internal-autofill-selected,
.aladin-input-text:-internal-autofill-selected:hover,
.aladin-input-text:-internal-autofill-selected:focus,
.aladin-input-text:-internal-autofill-selected:active {
-webkit-box-shadow: 0 0 0 1000px var(--bg-color) inset !important;
box-shadow: 0 0 0 1000px var(--bg-color) inset !important;
-webkit-text-fill-color: var(--text-color) !important;
padding-left: 0.5rem !important;
}
/***** Chrome, Safari, Opera and Edge Chromium styles *****/
.aladin-input-range::-webkit-slider-container {
background: var(--text-color);
height: 0.1rem;
min-height: 0.1rem;
}
@@ -999,8 +1079,7 @@ otherwise it fits its content options. If those are too big the select can go ou
font-family: monospace;
line-height: 1rem;
float: left;
}
.aladin-tooltip-container .aladin-tooltip {
@@ -1076,22 +1155,22 @@ otherwise it fits its content options. If those are too big the select can go ou
transform: translateY(-100%);
}
.aladin-container .aladin-color-picker {
.aladin-color-picker {
transform: translate(10px, 10px);
position: fixed;
pointer-events: none;
}
.aladin-container .aladin-view-label {
.aladin-view-label {
font-weight: bold;
font-family: monospace;
background-color: #00000000;
font-size: 1rem;
color: var(--aladin-color);
text-shadow: 1px 0 var(--aladin-color-border), -1px 0 var(--aladin-color-border), 0 1px var(--aladin-color-border), 0 -1px var(--aladin-color-border),
1px 1px var(--aladin-color-border), -1px -1px var(--aladin-color-border), 1px -1px var(--aladin-color-border), -1px 1px var(--aladin-color-border);
text-shadow: 1px 0 var(--border-color), -1px 0 var(--border-color), 0 1px var(--border-color), 0 -1px var(--border-color),
1px 1px var(--border-color), -1px -1px var(--border-color), 1px -1px var(--border-color), -1px 1px var(--border-color);
}
/* *********************************************** */
@@ -1112,46 +1191,28 @@ otherwise it fits its content options. If those are too big the select can go ou
*
*/
.aladin-stack-control {
position: absolute;
top: 3rem;
left: 0.2rem;
.aladin-indicator {
display: inline-block;
width: 20px;
height: 20px;
border-radius: 50%;
margin: 0 5px;
vertical-align: middle;
border: var(--border-size) solid var(--border-color);
}
.aladin-settings-control {
position: absolute;
top: 5.4rem;
left: 0.2rem;
.aladin-indicator.aladin-not-found {
background-color: darkorange;
}
.aladin-link {
color: var(--text-color);
.aladin-indicator.aladin-valid {
background-color: var(--valid-color);
}
.aladin-link:hover {
color: var(--hover-color);
text-decoration: underline;
}
.aladin-simbadPointer-control {
position: absolute;
top: 7.8rem;
left: 0.2rem;
}
.aladin-grid-control {
position: absolute;
top: 10.2rem;
left: 0.2rem;
}
.aladin-colorPicker-control {
position: absolute;
top: 15rem;
left: 0.2rem;
}
.aladin-cooFrame {
position: absolute;
top: 0.2rem;
@@ -1166,32 +1227,29 @@ otherwise it fits its content options. If those are too big the select can go ou
.aladin-stack-box {
min-width: 17rem;
max-width: 300px;
}
.aladin-item-selected {
border: var(--border-size) solid orange;
}
.aladin-HiPS-filter-box {
margin-top: 0.4rem;
/*width: 250px;*/
.aladin-HiPS-browser-box {
height: 500px;
min-width: 400px;
}
.aladin-HiPS-filter-box .aladin-horizontal-list {
justify-content: space-evenly;
width:100%;
.aladin-cat-browser-box {
width: 400px;
}
.aladin-HiPS-browser-box .aladin-input-text {
width: 100%;
min-width: 300px;
padding: 0.5rem;
}
.aladin-cat-browser-box .aladin-input-text.search {
width: 100%;
min-width: 300px;
padding-top: 0.5rem;
padding-bottom: 0.5rem;
}
.aladin-location {
@@ -1213,6 +1271,7 @@ otherwise it fits its content options. If those are too big the select can go ou
border-radius: 0 0.2rem 0.2rem 0;
box-sizing: content-box;
width: 12.5rem;
padding: 0.2rem;
}
.aladin-location .aladin-location-copy {
@@ -1262,6 +1321,12 @@ otherwise it fits its content options. If those are too big the select can go ou
bottom: 0;
left: 50%;
transform: translate(-50%, 0);
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
max-width: 50vw;
}
.aladin-status-bar{
@@ -1280,10 +1345,15 @@ otherwise it fits its content options. If those are too big the select can go ou
color: var(--text-color);
}
.aladin-share-control {
.aladin-widgets-toolbar {
position: absolute;
top: 12.6rem;
left: 0.2rem;
margin-top: 3rem;
margin-bottom: 5rem;
}
.aladin-widgets-toolbar > * {
padding-top: 0rem;
margin-right: 0rem;
}
.aladin-fullScreen-control {

View File

@@ -117,8 +117,6 @@ A.aladin = function (divSelector, options) {
} else {
theme = retrieveDefaultMode()
}
// Retrieve the data-theme from localStorage or system preferences
divElement.setAttribute("data-theme", theme);
// Associate the CSS inside the div
var cssStyleSheet = document.createElement('style')
@@ -126,7 +124,10 @@ A.aladin = function (divSelector, options) {
cssStyleSheet.innerHTML = aladinCSS;
divElement.appendChild(cssStyleSheet)
return new Aladin(divElement, options);
let aladin = new Aladin(divElement, options);
aladin._applyTheme(theme)
return aladin;
};
/**
@@ -904,7 +905,7 @@ A.catalogFromSkyBot = function (ra, dec, radius, epoch, queryOptions, options, s
* @param {function} [options.action] - The callback function to execute when the button is clicked.
* @param {string} [options.title] - The title attribute for the button.
* @param {Object} [options.icon] - An icon object for the button.
* @param {boolean} [options.disable=false] - Whether the button is initially disabled.
* @param {boolean} [options.disabled=false] - Whether the button is initially disabled.
* @param {HTMLElement|string|Widget} [options.content] - The content to be added to the button.
* @param {CSSStyleSheet} [options.cssStyle] - The CSS styles to apply to the button.
* @param {Object} [options.tooltip] - A tooltip.

View File

@@ -42,7 +42,7 @@ import { Coo } from "./libs/astro/coo.js";
import { CooConversion } from "./CooConversion.js";
import { HiPSCache } from "./HiPSCache.js";
import { HiPSList } from "./DefaultHiPSList.js";
import { Toolbar } from "./gui/Toolbar.js";
import { ProjectionEnum } from "./ProjectionEnum.js";
import { ALEvent } from "./events/ALEvent.js";
@@ -64,12 +64,11 @@ import A from "./A.js";
import { StatusBarBox } from "./gui/Box/StatusBarBox.js";
import { FullScreenActionButton } from "./gui/Button/FullScreen.js";
import { ProjectionActionButton } from "./gui/Button/Projection.js";
import { Stack } from "./gui/Button/Stack.js";
// features
import { SettingsButton } from "./gui/Button/Settings";
import { SimbadPointer } from "./gui/Button/SimbadPointer";
import { ColorPicker } from "./gui/Button/ColorPicker";
import { OverlayStackButton } from "./gui/Button/OverlayStack";
import { GridEnabler } from "./gui/Button/GridEnabler";
import { CooFrame } from "./gui/Input/CooFrame";
import { Circle } from "./shapes/Circle";
@@ -156,6 +155,10 @@ import { Polyline } from "./shapes/Polyline";
* @property {Object} [selector] - More options for the the selector.
* @property {string} [selector.color] - Color of the selector, defaults to the color of the reticle. Can be a hex color or a function returning a hex color.
* @property {number} [selector.lineWidth=2] - Width of the selector line.
* @property {Object} [toolbar] - Toolbar object
* @property {string} [toolbar.divSelector="null"] - A selector to put the toolbar in. By default the toolbar will be inserted in the Aladin Lite view.
* @property {string} [toolbar.position="topleft"] - Can be 'topleft', 'topright', 'bottomleft', 'bottomright'. Default to 'topleft'
* @property {boolean} [toolbar.vertical=true] - Is the toolbar horizontal or not. Default to vertical
*
* @example
* let aladin = A.aladin({
@@ -254,7 +257,7 @@ import { Polyline } from "./shapes/Polyline";
*/
/**
* @typedef {('select'|'objectsSelected'|'objectClicked'|'objectHovered'|'objectHoveredStop'|'footprintClicked'|'footprintHovered'|'positionChanged'|'zoomChanged'|'rotationChanged'|'click'|'rightClickMove'|'mouseMove'|'wheelTriggered'|'fullScreenToggled'|'cooFrameChanged'|'resizeChanged'|'projectionChanged'|'layerChanged')} EventListener
* @typedef {('select'|'objectsSelected'|'objectClicked'|'objectHovered'|'objectHoveredStop'|'footprintClicked'|'footprintHovered'|'positionChanged'|'zoomChanged'|'rotationChanged'|'click'|'rightClickMove'|'mouseMove'|'wheelTriggered'|'fullScreenToggled'|'cooFrameChanged'|'resizeChanged'|'projectionChanged'|'stackChanged')} EventListener
*
* <ul>
* <li>'positionChanged' is triggered when the view position has been changed. It gives the user the new center position of the view in ICRS frame. See {@link positionChangedParam}</li>
@@ -262,6 +265,7 @@ import { Polyline } from "./shapes/Polyline";
* <li>'mouseMove' is triggered when the mouse move over the view. It gives the the user the new position of the cursor in the current frame. See {@link mouseMoveParam}</li>
* <li>'wheelTriggered' allows to redefine the zooming. Listening for it will disable the default zooming heuristic.</li>
* <li>'objectsSelected', 'objectClicked', 'objectHovered', 'objectHoveredStop', 'footprintClicked', 'footprintHovered' are triggered when a catalog source/footprint has been clicked, hovered, ...
* <li>'stackChanged' is triggered when a layer has been added, removed or swapped. The callback passed is an object having fields. The layer object that has been added/removed (or the swapped layers) and a flag that tells you if it has been 'added', 'removed' or 'swapped'.
* </ul>
*/
@@ -301,14 +305,32 @@ export let Aladin = (function () {
const self = this;
ALEvent.HIPS_LAYER_ADDED.listenedBy(aladinDiv, (imageLayer) => {
this.callbacksByEventName["layerChanged"] &&
this.callbacksByEventName["layerChanged"](imageLayer.detail.layer, imageLayer.detail.layer.layer, "ADDED");
ALEvent.LAYER_ADDED.listenedBy(aladinDiv, (e) => {
const {layer} = e.detail;
let callback = this.callbacksByEventName["stackChanged"];
callback && callback({
change: 'added',
layer,
});
});
ALEvent.HIPS_LAYER_REMOVED.listenedBy(aladinDiv, (imageLayer) => {
this.callbacksByEventName["layerChanged"] &&
this.callbacksByEventName["layerChanged"](imageLayer.detail.layer, imageLayer.detail.layer.layer, "REMOVED");
ALEvent.LAYER_REMOVED.listenedBy(aladinDiv, (e) => {
const {layer} = e.detail;
let callback = this.callbacksByEventName["stackChanged"];
callback && callback({
change: 'removed',
layer
});
});
ALEvent.LAYER_SWAPPED.listenedBy(aladinDiv, (e) => {
const {layer1, layer2} = e.detail;
let callback = this.callbacksByEventName["stackChanged"];
callback && callback({
change: 'swapped',
layer1,
layer2
});
});
// if not options was set, try to retrieve them from the query string
@@ -335,6 +357,10 @@ export let Aladin = (function () {
...Aladin.DEFAULT_OPTIONS.gridOptions,
...requestedOptions.gridOptions
},
toolbar: {
...Aladin.DEFAULT_OPTIONS.toolbar,
...requestedOptions.toolbar
},
// and use the slice method to create a new array for the hipsList property:
// https://stackoverflow.com/questions/7486085/copy-array-by-value
hipsList: requestedOptions.hipsList || Aladin.DEFAULT_OPTIONS.hipsList.slice()
@@ -358,6 +384,11 @@ export let Aladin = (function () {
this.reticle = new Reticle(this.options, this);
this.popup = new Popup(this.aladinDiv, this.view);
this.tooltip = document.createElement('div')
this.tooltip.id = 'aladin-tooltip-mouse';
this.tooltip.classList.add("aladin-box")
this.aladinDiv.appendChild(this.tooltip)
this.ui = [];
@@ -395,7 +426,7 @@ export let Aladin = (function () {
}
}
// Format the hipslist given by the user before storing it in the aladin objec
// Format the hipslist given by the user before storing it in the aladin instance
this.hipsFavorites = [];
let hipsList = [].concat(options.hipsList);
@@ -421,7 +452,7 @@ export let Aladin = (function () {
name = hips.name || hips.id || hips.url;
hipsObj = { ...hipsObj, ...hips };
hipsObj = { ...hips };
} else {
console.warn(
"unable to parse the survey list item: ",
@@ -440,9 +471,6 @@ export let Aladin = (function () {
hipsObj["name"] = name;
}
// at least id or url is defined
//let key = name || id || url;
// Merge what is already in the cache for that HiPS with new properties
// coming from the MOCServer
this.hipsFavorites.push(hipsObj);
@@ -532,9 +560,9 @@ export let Aladin = (function () {
// set right click context menu
if (options.showContextMenu) {
this.contextMenu = new ContextMenu(this);
this.contextMenu.attach(
DefaultActionsForContextMenu.getDefaultActions(this)
DefaultActionsForContextMenu.getDefaultActions(this),
null
);
}
@@ -557,9 +585,25 @@ export let Aladin = (function () {
}
};
Aladin.prototype._applyTheme = function(newTheme) {
this.aladinDiv.setAttribute("data-theme", newTheme);
this.toolbar.el.setAttribute("data-theme", newTheme);
}
Aladin.prototype._setupUI = function (options) {
let self = this;
this.contextMenu = new ContextMenu(this);
let toolbarDivSelector = options && options.toolbar.divSelector || this.aladinDiv;
if (!(toolbarDivSelector instanceof HTMLElement)) {
toolbarDivSelector = document.querySelector(toolbarDivSelector);
}
this.toolbar = new Toolbar({
classList: ["aladin-widgets-toolbar"],
...options.toolbar
}, toolbarDivSelector)
// Status bar
if (options.showStatusBar) {
this.statusBar = new StatusBarBox(this);
@@ -582,48 +626,48 @@ export let Aladin = (function () {
}
////////////////////////////////////////////////////
let stack = new OverlayStackButton(this);
let simbad = new SimbadPointer(this);
let colorPicker = new ColorPicker(this);
let grid = new GridEnabler(this);
this.addUI(stack);
this.addUI(simbad);
this.addUI(grid);
this.addUI(colorPicker)
let widgets = {};
// Add the layers control
if (!options.showLayersControl) {
stack._hide();
}
// Add the simbad pointer control
if (!options.showSimbadPointerControl) {
simbad._hide();
}
// Add the projection control
// Add the coo grid control
if (!options.showCooGridControl) {
grid._hide();
}
// Add the projection control
// Add the coo grid control
if (!options.showColorPickerControl) {
colorPicker._hide();
if (options.showLayersControl) {
let stack = new Stack(this);
widgets["stack"] = stack
}
// Settings control
if (options.showSettingsControl) {
let settings = new SettingsButton(this, {
features: { stack, simbad, grid },
});
this.addUI(settings);
let settings = new SettingsButton(this);
widgets["settings"] = settings
}
// Add the simbad pointer control
if (options.showSimbadPointerControl) {
let simbad = new SimbadPointer(this);
widgets["simbad"] = simbad
}
// Add the projection control
// Add the coo grid control
if (options.showCooGridControl) {
let grid = new GridEnabler(this);
widgets["grid"] = grid;
}
// Add the projection control
// Add the coo grid control
if (options.showColorPickerControl) {
let picker = new ColorPicker(this);
widgets["picker"] = picker;
}
// share control panel
if (options.showShareControl) {
this.addUI(new ShareActionButton(self));
let share = new ShareActionButton(this);
widgets["share"] = share;
}
for (let [name, widget] of Object.entries(widgets)) {
this.toolbar.add(name, widget);
}
if (options.showProjectionControl) {
@@ -751,6 +795,11 @@ export let Aladin = (function () {
realFullscreen: false,
pixelateCanvas: true,
manualSelection: false,
toolbar: {
divSelector: null,
vertical: true,
position: 'topleft'
}
};
/**
@@ -764,7 +813,7 @@ export let Aladin = (function () {
self.isInFullscreen = !self.isInFullscreen;
ContextMenu.hideAll();
this.contextMenu && this.contextMenu._hide();
this.ui.forEach(ui => {
if (ui.toggle) {
@@ -1097,7 +1146,6 @@ export let Aladin = (function () {
return projName;
};
``;
/**
* Returns the current coordinate system: possible values are 'ICRS', 'ICRSd', and 'Galactic' .
@@ -1114,6 +1162,16 @@ export let Aladin = (function () {
return this.view.cooFrame.label;
};
/**
* Get a reference to the Aladin Lite toolbar object. User can append, remove DOMElement/widgets to it
*
* @memberof Aladin
* @returns {Toolbar}
*/
Aladin.prototype.getToolbar = function () {
return this.toolbar;
};
/**
* Moves the Aladin instance to the specified astronomical object.
*
@@ -1772,14 +1830,6 @@ export let Aladin = (function () {
name: hips.name,
})
// and resort the favorites
this.hipsFavorites.sort((h1, h2) => {
let k1 = h1.name || h1.id || h1.url;
let k2 = h2.name || h2.id || h2.url;
return k1 < k2;
})
// send the final event
ALEvent.FAVORITE_HIPS_LIST_UPDATED.dispatchedTo(document.body, this.hipsFavorites);
}
@@ -2231,7 +2281,7 @@ export let Aladin = (function () {
"cooFrameChanged",
"resizeChanged",
"projectionChanged",
"layerChanged"
"stackChanged"
];
/**
@@ -2289,9 +2339,9 @@ export let Aladin = (function () {
console.log("positionChanged", ra, dec)
})
aladin.on("layerChanged", (layer, layerName, state) => {
console.log("layerChanged", layer, layerName, state)
})
aladin.on('stackChanged', function(state) {
console.log(state)
});
*/
Aladin.prototype.on = function (what, myFunction) {
if (Aladin.AVAILABLE_CALLBACKS.indexOf(what) < 0) {

View File

@@ -112,11 +112,6 @@ export let Footprint= (function() {
this.overlay.reportChange();
return;
}
/*let catalog = this.getCatalog();
if (catalog) {
catalog.view && catalog.view.requestRedraw();
}*/
};
Footprint.prototype.unhover = function() {
@@ -209,35 +204,6 @@ export let Footprint= (function() {
};
Footprint.prototype.intersectsBBox = function(x, y, w, h, view) {
/*if(this.source) {
let s = this.source;
if (!s.isShowing) {
return false;
}
let c = null;
if (s.x && s.y) {
c = {
x: s.x,
y: s.y,
};
} else {
var xy = view.aladin.world2pix(s.ra, s.dec);
if (!xy) {
return false;
}
c = {
x: xy[0],
y: xy[1],
};
}
if (c.x >= x && c.x <= x + w && c.y >= y && c.y <= y + h) {
return true;
}
}*/
return this.shapes.some((shape) => shape.intersectsBBox(x, y, w, h, view));
};

View File

@@ -339,7 +339,6 @@ export let HiPS = (function () {
await HiPSProperties.fetchFromFile(self.localFiles["properties"])
.then((p) => {
self._parseProperties(p);
self.url = "local";
delete self.localFiles["properties"]
@@ -870,7 +869,7 @@ export let HiPS = (function () {
imgFormat: this.imgFormat,
});
// once the meta have been well parsed, we can set the meta
ALEvent.HIPS_LAYER_CHANGED.dispatchedTo(this.view.aladinDiv, {
ALEvent.LAYER_CHANGED.dispatchedTo(this.view.aladinDiv, {
layer: this,
});
}
@@ -1075,7 +1074,19 @@ export let HiPS = (function () {
}
};
HiPS.prototype._add2View = function (layer) {
HiPS.prototype._removeFromView = function() {
if (!this.view)
return;
if (this.added) {
this.view.wasm.removeLayer(this.layer);
}
};
HiPS.prototype._addToView = function (layer) {
if (!this.view)
return this;
this.layer = layer;
let self = this;

86
src/js/HiPSComposite.js Normal file
View File

@@ -0,0 +1,86 @@
// Copyright 2013 - UDS/CNRS
// The Aladin Lite program is distributed under the terms
// of the GNU General Public License version 3.
//
// This file is part of Aladin Lite.
//
// Aladin Lite is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, version 3 of the License.
//
// Aladin Lite is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// The GNU General Public License is available in COPYING file
// along with Aladin Lite.
//
/******************************************************************************
* Aladin Lite project
*
* File HiPSComposite.js
*
* Authors: Matthieu Baumann [CDS]
*
*****************************************************************************/
import { HiPS } from "./HiPS.js";
import A from "./A.js";
export let HiPSComposite = (function () {
/**
* The object describing the color composition of image surveys
*
* @class
* @constructs HiPSComposite
*
* @param {HiPSOptions[]} [options] - The option for the survey
*/
function HiPSComposite(options) {
this.name = options && options.name || 'Composite HiPS';
this.hipses = []
};
HiPSComposite.prototype.setOptions = function(options) {
this.hipses = [];
for (var hipsOptions of options) {
let hips = A.HiPS(hipsOptions.id, hipsOptions)
this.hipses.push(hips)
}
};
HiPSComposite.prototype._setView = HiPS.prototype._setView;
HiPSComposite.prototype._addToView = function (layer) {
this._removeFromView();
this.layer = layer;
let i = 0;
for (var hips of this.hipses) {
hips._addToView(layer + '_' + i)
i++;
}
return this
};
HiPSComposite.prototype._removeFromView = function() {
for (var hips of this.hipses) {
hips._removeFromView()
}
}
HiPSComposite.prototype.getOpacity = function() {
return this.hipses[0].getOpacity()
}
HiPSComposite.prototype.setOpacity = function(opacity) {
for (var hips of this.hipses) {
hips.setOpacity(opacity)
}
}
return HiPSComposite;
})();

View File

@@ -362,7 +362,9 @@ export let Image = (function () {
// Private method for updating the view with the new meta
Image.prototype._updateMetadata = HiPS.prototype._updateMetadata;
Image.prototype._add2View = function (layer) {
Image.prototype._removeFromView = HiPS.prototype._removeFromView;
Image.prototype._addToView = function (layer) {
this.layer = layer;
let self = this;

View File

@@ -30,7 +30,6 @@
*
*****************************************************************************/
import { Color } from "./Color.js"
import { Icon } from "./gui/Widgets/Icon.js";
import { Tabs } from "./gui/Widgets/Tab.js";
import { Table } from "./gui/Widgets/Table.js";
@@ -77,7 +76,7 @@ export let MeasurementTable = (function() {
tooltip: {
global: true,
aladin: this.aladin,
content: 'Press shift + mouse wheel for scrolling'
content: 'Scroll to see more...'
},
aladin: this.aladin,
layout,

View File

@@ -51,8 +51,7 @@ export class MocServer {
//expr: "dataproduct_type=image",
get: "record",
fmt: "json",
fields: "ID,hips_creator,hips_copyright,hips_order,hips_tile_width,hips_frame,hips_tile_format,obs_title,obs_description,obs_copyright,obs_regime,client_category",
//fields: "ID,hips_initial_fov,hips_initial_ra,hips_initial_dec,hips_pixel_bitpix,hips_creator,hips_copyright,hips_frame,hips_order,hips_order_min,hips_tile_width,hips_tile_format,hips_pixel_cut,obs_title,obs_description,obs_copyright,obs_regime,hips_data_range,hips_service_url",
fields: "ID,hips_creator,hips_copyright,hips_order,hips_tile_width,hips_frame,hips_tile_format,obs_title,obs_description,obs_copyright,obs_regime,client_category,dataproduct_subtype,hips_service_url,hips_initial_ra,hips_initial_dec,hips_initial_fov,em_min,em_max",
};
this._allHiPSes = Utils.loadFromUrls(MocServer.MIRRORS_HTTPS, {
@@ -97,8 +96,7 @@ export class MocServer {
expr: "dataproduct_type=image&&ID=" + ids.join(','),
get: "record",
fmt: "json",
fields: "ID,hips_creator,hips_copyright,hips_frame,hips_tile_format,obs_title,obs_description,obs_copyright,obs_regime",
//fields: "ID,hips_initial_fov,hips_initial_ra,hips_initial_dec,hips_pixel_bitpix,hips_creator,hips_copyright,hips_frame,hips_order,hips_order_min,hips_tile_width,hips_tile_format,hips_pixel_cut,obs_title,obs_description,obs_copyright,obs_regime,hips_data_range,hips_service_url",
fields: "ID,hips_creator,hips_copyright,hips_frame,hips_tile_format,obs_title,obs_description,obs_copyright,obs_regime,dataproduct_subtype,hips_service_url,hips_initial_ra,hips_initial_dec,hips_initial_fov,em_min,em_max",
};
return Utils.loadFromUrls(MocServer.MIRRORS_HTTPS, {
@@ -114,7 +112,6 @@ export class MocServer {
get: "record",
fmt: "json",
fields: "ID,hips_copyright,obs_title,obs_description,obs_copyright,cs_service_url,hips_service_url",
//fields: "ID,hips_copyright,hips_order,hips_order_min,obs_title,obs_description,obs_copyright,obs_regime,cs_service_url,hips_service_url",
};
this._allCatalogHiPSes = Utils.loadFromUrls(MocServer.MIRRORS_HTTPS, {data: params, dataType: 'json'})

View File

@@ -624,12 +624,13 @@ export let ProgressiveCat = (function() {
},
computeFootprints: Catalog.prototype.computeFootprints,
setSourceSize: Catalog.prototype.setSourceSize,
setShape: Catalog.prototype.setShape,
setColor: Catalog.prototype.setColor,
reportChange: function() { // TODO: to be shared with Catalog
this.view && this.view.requestRedraw();
}
}; // END OF .prototype functions
ProgressiveCat.parser = new DOMParser();

View File

@@ -34,20 +34,20 @@ export let ProjectionEnum = {
SIN: {id: 3, label: "Spheric"}, /* Orthographic */
// TODO: fix why the projection disappears at fov = 360.0
ZEA: {id: 4, label: "Zenital equal-area"}, /* Equal-area */
//FEYE: {id: 5, fov: 190, label: "fish eye"},
//AIR: {id: 6, fov: 360, label: "airy"},
/*FEYE: {id: 5, fov: 190, label: "fish eye"},
AIR: {id: 6, fov: 360, label: "airy"},
//AZP: {fov: 180},
//ARC: {id: 7, fov: 360, label: "zenital equidistant"},
//NCP: {id: 8, fov: 180, label: "north celestial pole"},
ARC: {id: 7, fov: 360, label: "zenital equidistant"},
NCP: {id: 8, fov: 180, label: "north celestial pole"},*/
// Cylindrical
MER: {id: 9, label: "Mercator"},
//CAR: {id: 10, fov: 360, label: "plate carrée"},
//CEA: {id: 11, fov: 360, label: "cylindrical equal area"},
//CYP: {id: 12, fov: 360, label: "cylindrical perspective"},
/*CAR: {id: 10, fov: 360, label: "plate carrée"},
CEA: {id: 11, fov: 360, label: "cylindrical equal area"},
CYP: {id: 12, fov: 360, label: "cylindrical perspective"},*/
// Pseudo-cylindrical
AIT: {id: 13, label: "Hammer-Aïtoff"},
//PAR: {id: 14, fov: 360, label: "parabolic"},
//SFL: {id: 15, fov: 360, label: "sanson-flamsteed"},
/*PAR: {id: 14, fov: 360, label: "parabolic"},
SFL: {id: 15, fov: 360, label: "sanson-flamsteed"},*/
MOL: {id: 16, label: "Mollweide"},
// Conic
//COD: {id: 17, fov: 360, label: "conic equidistant"},

View File

@@ -23,7 +23,7 @@ import HomeIconUrl from '../../assets/icons/maximize.svg';
import SpectraIconUrl from '../../assets/icons/freq.svg';
import { Utils } from "./Utils";
import { Aladin } from "./Aladin";
import { DOMElement } from "./gui/Widgets/Widget";
/******************************************************************************
* Aladin Lite project
*
@@ -34,7 +34,7 @@ import { Aladin } from "./Aladin";
*
*****************************************************************************/
export class SpectraDisplayer {
export class SpectraDisplayer extends DOMElement {
static UNIT = {
FREQUENCY: {
label: "f",
@@ -118,6 +118,8 @@ export class SpectraDisplayer {
};
constructor(view, options) {
super()
let createPlotCanvas = (name) => {
const canvas = document.createElement("canvas");
canvas.classList.add(name);
@@ -555,67 +557,9 @@ export class SpectraDisplayer {
this.view.catalogCanvas.dispatchEvent(wheelEvent);
});
/*
const updateSelectorList = () => {
let options = [];
for (const hipsName of this.hips3DList.keys()) {
options.push(hipsName)
}
this.selector.update({options})
};
ALEvent.HIPS_LAYER_ADDED.listenedBy(
this.view.aladin.aladinDiv,
function (e) {
let hips = e.detail.layer;
if (hips.dataproductType === "spectral-cube") {
self.hips3DList.set(hips.name, hips);
updateSelectorList()
}
}
);
ALEvent.HIPS_LAYER_SWAP.listenedBy(
this.view.aladin.aladinDiv,
function (e) {
let firstHiPS = e.detail.firstLayer;
let secondHiPS = e.detail.secondLayer;
self.hips3DList.delete(firstHiPS.name);
if (secondHiPS.dataproductType === "spectral-cube") {
self.hips3DList.set(secondHiPS.name, secondHiPS);
}
updateSelectorList()
}
);
ALEvent.HIPS_LAYER_REMOVED.listenedBy(
this.view.aladin.aladinDiv,
function (e) {
let hips = e.detail.layer;
self.hips3DList.delete(hips.name);
if (hips === this.hips) {
// the hips pointed by the tool has been removed
self.attachHiPS3D(null);
}
if (self.hips3DList.size === 0) {
self.hide()
}
updateSelectorList()
}
);*/
}
hide() {
_hide() {
if (this.isHidden) {
return;
}
@@ -624,7 +568,7 @@ export class SpectraDisplayer {
this.isHidden = true;
}
show() {
_show() {
if (!this.isHidden) {
return;
}
@@ -654,7 +598,7 @@ export class SpectraDisplayer {
window.addEventListener("spectra", this.spectraUpdateCallback);
this.resetScale();
this.show()
this._show()
}
}

View File

@@ -48,6 +48,7 @@ import { HiPS } from "./HiPS.js";
import { Image } from "./Image.js";
import { Color } from "./Color.js";
import { SpectraDisplayer } from "./SpectraDisplayer.js";
import { DefaultActionsForContextMenu } from "./DefaultActionsForContextMenu.js";
export let View = (function () {
@@ -93,7 +94,7 @@ export let View = (function () {
// 1. Print the original exception message in the console
//console.error(e)
// 2. Add a more explicite message to the end user
console.error("Problem initializing Aladin Lite. Please contact the support by contacting Matthieu Baumann (baumannmatthieu0@gmail.com) or Thomas Boch (thomas.boch@astro.unistra.fr). You can also open an issue on the Aladin Lite github repository here: https://github.com/cds-astro/aladin-lite. Message error:" + e)
console.error("Problem initializing Aladin Lite. Please contact the support by contacting Matthieu Baumann (matthieu.baumann@astro.unistra.fr) or Thomas Boch (thomas.boch@astro.unistra.fr). You can also open an issue on the Aladin Lite github repository here: https://github.com/cds-astro/aladin-lite. Message error:" + e)
}
this._defineProperties();
@@ -276,7 +277,6 @@ export let View = (function () {
colorPickerElement = document.createElement('span');
colorPickerElement.classList.add('aladin-color-picker')
colorPickerElement.classList.add('aladin-view-label')
colorPickerElement.classList.add('aladin-dark-theme')
this.aladin.aladinDiv.appendChild(colorPickerElement);
}
@@ -596,6 +596,9 @@ export let View = (function () {
return;
}
if (this.spectraDisplayer)
this.spectraDisplayer._hide();
if (imageLayer.dataproductType === "spectral-cube") {
if (!this.spectraDisplayer) {
this.spectraDisplayer = new SpectraDisplayer(this, {width: 800, height: 300});
@@ -833,7 +836,7 @@ export let View = (function () {
.then(() => {
if (view.aladin.statusBar) {
view.aladin.statusBar.appendMessage({
message: `${view.colorPickerTool.probedValue} copied into your clipboard`,
message: `<span class="aladin-indicator" style="background-color: ${view.colorPickerTool.probedValue}"></span> [${view.colorPickerTool.probedValue}] copied into your clipboard`,
duration: 1500,
type: 'info'
})
@@ -904,25 +907,7 @@ export let View = (function () {
return;
}
var wasDragging = view.realDragging === true;
/*if (view.dragging) { // if we were dragging, reset to default cursor
if(view.mode === View.PAN) {
view.setCursor('default');
}
view.dragging = false;
if (wasDragging) {
view.realDragging = false;
// call the positionChanged once more with a dragging = false
view.throttledPositionChanged(false);
}
if (view.spectraDisplayer) {
view.spectraDisplayer.enableInteraction();
}
} // end of "if (view.dragging) ... "*/
var wasDragging = view.realDragging === true;
view.mustClearCatalog = true;
view.dragCoo = null;
@@ -953,8 +938,13 @@ export let View = (function () {
}
if (view.rightClick) {
if (showContextMenu) {
view.aladin.contextMenu && view.aladin.contextMenu.show({e});
let ctxMenu = view.aladin.contextMenu;
if (showContextMenu && ctxMenu) {
ctxMenu.attach(
DefaultActionsForContextMenu.getDefaultActions(view.aladin),
null
);
ctxMenu._show({e});
}
view.rightClick = false;
@@ -1796,7 +1786,7 @@ export let View = (function () {
if (alreadyPresentImageLayer) {
if (alreadyPresentImageLayer.added === true) {
ALEvent.HIPS_LAYER_REMOVED.dispatchedTo(this.aladinDiv, { layer: alreadyPresentImageLayer });
ALEvent.LAYER_REMOVED.dispatchedTo(this.aladinDiv, { layer: alreadyPresentImageLayer });
}
alreadyPresentImageLayer.added = false;
@@ -1813,16 +1803,15 @@ export let View = (function () {
// Insert a layer object (Image/HiPS) at a specific index in the stack
View.prototype._addLayer = function(imageLayer) {
// Keep the JS frontend in-line with the wasm state
const layerName = imageLayer.layer;
const layer = imageLayer.layer;
imageLayer.added = true;
this.imageLayers.set(layerName, imageLayer);
this.imageLayers.set(layer, imageLayer);
// select the layer if he is on top
this.selectLayer(layerName);
this.selectLayer(layer);
ALEvent.HIPS_LAYER_ADDED.dispatchedTo(this.aladinDiv, { layer: imageLayer });
ALEvent.LAYER_ADDED.dispatchedTo(this.aladinDiv, { layer: imageLayer });
}
View.prototype.addImageLayer = function (imageLayer, layer) {
@@ -1835,7 +1824,7 @@ export let View = (function () {
// All image layer promises must be completed (fullfilled or rejected)
const task = {
message: 'Load layer: ' + imageLayer.name,
message: imageLayer.name + ' loading...',
id: Utils.uuidv4(),
}
// Ensure all the properties for HiPSes have been seeked
@@ -1844,7 +1833,7 @@ export let View = (function () {
// so that we can add it to the view (call of _add2View)
Promise.all([Promise.allSettled(this.promises), imageLayerPromise])
// Then we add the layer to the view
.then((_) => imageLayer._add2View(layer))
.then((_) => imageLayer._addToView(layer))
// Then we keep a track of the layer in the JS front
.then((imageLayer) => {
this._addLayer(imageLayer);
@@ -1877,37 +1866,29 @@ export let View = (function () {
// Remove the settled promise
this.promises.splice(idx, 1);
/*const noMoreLayersToWaitFor = this.promises.length === 0;
if (noMoreLayersToWaitFor) {
if (self.empty) {
// no promises to launch and the view has no HiPS.
// This situation can occurs if the MOCServer is out
// If so we can directly put the url of the DSS hosted in alasky,
// it the best I can do if the MOCServer is out
self.aladin.setBaseImageLayer("https://alaskybis.cds.unistra.fr/DSS/DSSColor/");
} else {
//self.renameLayer(this.overlayLayers[0], "base");
}
}*/
})
}
View.prototype.swapLayers = function(firstLayer, secondLayer) {
View.prototype.swapLayers = function(layer1, layer2) {
// Throw an exception if either the first or the second layers are not in the stack
this.wasm.swapLayers(firstLayer, secondLayer);
this.wasm.swapLayers(layer1, layer2);
// Swap in overlaylayers
const idxFirstLayer = this.overlayLayers.indexOf(firstLayer);
const idxSecondLayer = this.overlayLayers.indexOf(secondLayer);
const i = this.overlayLayers.indexOf(layer1);
const j = this.overlayLayers.indexOf(layer2);
const tmp = this.overlayLayers[idxFirstLayer];
this.overlayLayers[idxFirstLayer] = this.overlayLayers[idxSecondLayer];
this.overlayLayers[idxSecondLayer] = tmp;
const tmp = this.overlayLayers[i];
this.overlayLayers[i] = this.overlayLayers[j];
this.overlayLayers[j] = tmp;
// Tell the layer hierarchy has changed
ALEvent.HIPS_LAYER_SWAP.dispatchedTo(this.aladinDiv, { firstLayer: firstLayer, secondLayer: secondLayer });
ALEvent.LAYER_SWAPPED.dispatchedTo(
this.aladinDiv,
{
layer1: this.imageLayers.get(layer1),
layer2: this.imageLayers.get(layer2)
}
);
}
View.prototype.removeImageLayer = function (layer) {
@@ -1920,9 +1901,7 @@ export let View = (function () {
}
// Update the backend
if (imageLayer.added) {
this.wasm.removeLayer(layer);
}
imageLayer._removeFromView();
// Get the survey to remove to dissociate it from the view
imageLayer.added = false;
@@ -1945,15 +1924,7 @@ export let View = (function () {
this.selectLayer(this.overlayLayers[this.overlayLayers.length - 1]);
}
ALEvent.HIPS_LAYER_REMOVED.dispatchedTo(this.aladinDiv, { layer: imageLayer });
// check if there are no more surveys
/*const noMoreLayersToWaitFor = this.promises.length === 0;
if (noMoreLayersToWaitFor && this.empty) {
// no promises to launch!
const dssId = Aladin.DEFAULT_OPTIONS.survey;
this.aladin.setBaseImageLayer(dssId);
}*/
ALEvent.LAYER_REMOVED.dispatchedTo(this.aladinDiv, { layer: imageLayer });
};
View.prototype.contains = function(survey) {

View File

@@ -51,17 +51,14 @@ export class ALEvent {
static POSITION_CHANGED = new ALEvent("AL:position.changed");
static ZOOM_CHANGED = new ALEvent("AL:zoom.changed");
static HIPS_LAYER_ADDED = new ALEvent("AL:HiPSLayer.added");
static HIPS_LAYER_REMOVED = new ALEvent("AL:HiPSLayer.removed");
static HIPS_LAYER_RENAMED = new ALEvent("AL:HiPSLayer.renamed");
static HIPS_LAYER_SWAP = new ALEvent("AL:HiPSLayer.swap");
static HIPS_LAYER_CHANGED = new ALEvent("AL:HiPSLayer.changed");
static LAYER_ADDED = new ALEvent("AL:Layer.added");
static LAYER_REMOVED = new ALEvent("AL:Layer.removed");
static LAYER_SWAPPED = new ALEvent("AL:Layer.swapped");
static LAYER_CHANGED = new ALEvent("AL:Layer.changed");
static HIPS_CACHE_UPDATED = new ALEvent("AL:HiPSCache.updated");
static FAVORITE_HIPS_LIST_UPDATED = new ALEvent("AL:HiPSFavorites.updated");
static GRAPHIC_OVERLAY_LAYER_ADDED = new ALEvent("AL:GraphicOverlayLayer.added");
static GRAPHIC_OVERLAY_LAYER_REMOVED = new ALEvent("AL:GraphicOverlayLayer.removed");

View File

@@ -51,7 +51,7 @@ import { ActionButton } from "../Widgets/ActionButton.js";
CatalogQueryBox.catalogs[cat.obs_title] = cat;
});
inputText.update({autocomplete: {options: Object.keys(CatalogQueryBox.catalogs)}})
searchDropdown.update({options: Object.keys(CatalogQueryBox.catalogs)})
})
const fnIdSelected = function(type, params) {
@@ -120,12 +120,12 @@ import { ActionButton } from "../Widgets/ActionButton.js";
self.fnIdSelected('votable', {
url: votableUrl,
success: () => {
inputText.addClass('aladin-valid');
searchDropdown.addClass('aladin-valid');
},
error: () => {
inputText.addClass('aladin-not-valid')
self.csForm.submit.update({disable: true})
self.hipsCatLoad.update({disable: true});
searchDropdown.addClass('aladin-not-valid')
self.csForm.submit.update({disabled: true})
self.hipsCatLoad.update({disabled: true});
}
})
} catch (e) {
@@ -134,24 +134,24 @@ import { ActionButton } from "../Widgets/ActionButton.js";
if (catalog) {
self._selectItem(catalog, aladin);
inputText.addClass('aladin-valid');
searchDropdown.addClass('aladin-valid');
} else {
// consider it as a cat ID and search in catalogs for it
const foundCat = Object.values(CatalogQueryBox.catalogs)
.find((c) => c.ID === value);
if (foundCat) {
self._selectItem(foundCat, aladin);
inputText.addClass('aladin-valid')
searchDropdown.addClass('aladin-valid')
} else {
inputText.addClass('aladin-not-valid')
self.csForm.submit.update({disable: true})
self.hipsCatLoad.update({disable: true});
searchDropdown.addClass('aladin-not-valid')
self.csForm.submit.update({disabled: true})
self.hipsCatLoad.update({disabled: true});
}
}
}
}
let inputText = new Dropdown(aladin, {
let searchDropdown = new Dropdown(aladin, {
name: 'catalogs',
placeholder: "Type ID, title, keyword or URL",
tooltip: {
@@ -159,22 +159,9 @@ import { ActionButton } from "../Widgets/ActionButton.js";
aladin,
content: 'HiPS url, ID or keyword accepted',
},
actions: {
input(e) {
inputText.removeClass('aladin-valid')
inputText.removeClass('aladin-not-valid')
},
focus(e) {
inputText.removeClass('aladin-valid')
inputText.removeClass('aladin-not-valid')
},
change(e) {
e.stopPropagation();
e.preventDefault()
_parseEntry(e)
},
},
action: (e) => {
_parseEntry(e)
}
});
let self;
@@ -190,7 +177,7 @@ import { ActionButton } from "../Widgets/ActionButton.js";
position: {direction: "bottom"}
},
content: 'HiPS',
disable: true,
disabled: true,
action() {
self.fnIdSelected('hips', {
hipsURL: self.selectedItem.hips_service_url,
@@ -220,7 +207,6 @@ import { ActionButton } from "../Widgets/ActionButton.js";
let [lon, lat] = coo.format('s2');
let fov = new Angle(radius, 1).format();
//selectorBtn.update({tooltip: {content: 'center: ' + ra.toFixed(2) + ', ' + dec.toFixed(2) + '<br\>radius: ' + radius.toFixed(2), position: {direction: 'left'}}})
form.set('ra', lon)
form.set('dec', lat)
form.set('rad', fov)
@@ -241,7 +227,7 @@ import { ActionButton } from "../Widgets/ActionButton.js";
let form = new Form({
submit: {
disable: true,
disabled: true,
icon: {
monochrome: true,
url: targetIconUrl,
@@ -344,20 +330,25 @@ import { ActionButton } from "../Widgets/ActionButton.js";
classList: ['aladin-cat-browser-box'],
content: Layout.vertical(
[
Layout.horizontal({
layout: ["Search:", inputText], cssStyle: {width: '100%'}
}),
Layout.horizontal({
layout: ["Progressive catalog:", hipsCatLoad],
cssStyle: {
textAlign: "center",
display: "flex",
alignItems: "center",
listStyle: "none",
justifyContent: "space-between",
width: "100%",
},
}),
Layout.horizontal(
["Search:", searchDropdown],
{cssStyle:
{width: '100%'}
}
),
Layout.horizontal(
["Progressive catalog:", hipsCatLoad],
{
cssStyle: {
textAlign: "center",
display: "flex",
alignItems: "center",
listStyle: "none",
justifyContent: "space-between",
width: "100%",
},
}
),
form
]
),
@@ -367,7 +358,7 @@ import { ActionButton } from "../Widgets/ActionButton.js";
self = this;
this.hipsCatLoad = hipsCatLoad;
this.csForm = form;
this.inputText = inputText;
this.searchDropdown = searchDropdown;
this.fnIdSelected = fnIdSelected;
}
@@ -375,15 +366,15 @@ import { ActionButton } from "../Widgets/ActionButton.js";
this.selectedItem = item;
if (!item) {
this.csForm.submit.update({disable: true})
this.hipsCatLoad.update({disable: true});
this.csForm.submit.update({disabled: true})
this.hipsCatLoad.update({disabled: true});
} else {
if (item && item.cs_service_url) {
this.csForm.submit.update({disable: false});
this.csForm.submit.update({disabled: false});
}
if (item && item.hips_service_url) {
this.hipsCatLoad.update({disable: false});
this.hipsCatLoad.update({disabled: false});
}
}
}

View File

@@ -36,20 +36,6 @@ import { SearchTextInput } from "../Input/InputTextSearch.js";
export class GotoBox extends Box {
// Constructor
constructor(aladin) {
/*let content = Layout.horizontal([
'Go to:',
Input.text({
//tooltip: {content: 'Search for a VizieR catalogue', position: {direction :'bottom'}},
label: "Go to:",
name: "goto",
type: "text",
placeholder: 'Object name/position',
autocomplete: 'off',
change(e, self) {
self.addEventListener('blur', (event) => {});
}
})
]);*/
let textField = new SearchTextInput(aladin, {
cssStyle: {
width: '15rem'

View File

@@ -41,6 +41,7 @@ import labelSizeIcon from './../../../../assets/icons/font-size.svg';
export class GridBox extends Box {
// Constructor
constructor(aladin) {
let self;
let colorInput = new Input({
layout: {
name: 'gridColor',
@@ -98,7 +99,7 @@ export class GridBox extends Box {
})
}
ctxMenu.attach(ctxMenuLayout);
ctxMenu.attach(ctxMenuLayout, self);
ctxMenu.show({
e: e,
position: {
@@ -134,7 +135,7 @@ export class GridBox extends Box {
})
}
ctxMenu.attach(ctxMenuLayout);
ctxMenu.attach(ctxMenuLayout, self);
ctxMenu.show({
e: e,
position: {
@@ -154,15 +155,13 @@ export class GridBox extends Box {
}
});
sliderOpacity.addClass("aladin-input-range")
const layout = Layout.horizontal({
layout: [
enableCheckbox,
labelSizeBtn,
thicknessLineBtn,
colorInput,
sliderOpacity
]
})
const layout = Layout.horizontal([
enableCheckbox,
labelSizeBtn,
thicknessLineBtn,
colorInput,
sliderOpacity
])
layout.addClass('aladin-grid-frame');
@@ -182,6 +181,7 @@ export class GridBox extends Box {
this.addClass("aladin-box-night")
this.aladin = aladin;
self = this;
this._hide();
}

View File

@@ -22,16 +22,17 @@ import { MocServer } from "../../MocServer.js";
import { Box } from "../Widgets/Box.js";
import { Dropdown } from "../Input/Dropdown.js";
import filterOnUrl from "../../../../assets/icons/filter-on.svg";
import hipsIconUrl from "../../../../assets/icons/hips.svg";
import treeIconUrl from "../../../../assets/icons/tree.svg";
import filterOffUrl from "../../../../assets/icons/filter-off.svg";
import helpIconUrl from "../../../../assets/icons/help.svg";
import { Input } from "../Widgets/Input.js";
import { TogglerActionButton } from "../Button/Toggler.js";
import { WidgetTogglerButton } from "../Button/Toggler.js";
import { Layout } from "../Layout.js";
import { HiPSFilterBox } from "./HiPSFilterBox.js";
import A from "../../A.js";
import { Utils } from "../../Utils.ts";
import { ActionButton } from "../Widgets/ActionButton.js";
import infoIconUrl from "../../../../assets/icons/info.svg"
import { Icon } from "../Widgets/Icon.js";
import { Tree } from "../Widgets/Tree.js";
import { ALEvent } from "../../events/ALEvent.js";
@@ -47,6 +48,10 @@ import { ALEvent } from "../../events/ALEvent.js";
*****************************************************************************/
function fillHiPSHierarchy(name, hips, path, hierarchy) {
if (path[path.length - 1] === '/') {
path = path.substring(0, path.length - 1);
}
let folders = path.split('/')
let curFolder = folders.shift()
@@ -105,10 +110,13 @@ export class HiPSBrowserBox extends Box {
}
if (params.title) {
if (!item.obs_title)
if (!item.obs_title && !item.ID)
return false;
if (!item.obs_title.toLowerCase().includes(params.title.toLowerCase())) {
let obsTitleDoesNotMatch = !item.obs_title.toLowerCase().includes(params.title.toLowerCase());
let creatorDidDoesNotMatch = !item.ID.toLowerCase().includes(params.title.toLowerCase());
if (obsTitleDoesNotMatch && creatorDidDoesNotMatch) {
return false;
}
}
@@ -129,8 +137,16 @@ export class HiPSBrowserBox extends Box {
let name = item.obs_title || item.ID;
self._addHiPS(image, name)
},
dblclick: (item) => {
let image = item.ID || item.hips_service_url;
let name = item.obs_title || item.ID;
self._addHiPS(image, name)
self.close();
},
// a callback called for filtering
filter,
aladin,
});
MocServer.getAllHiPSes().then((HiPSes) => {
@@ -140,19 +156,28 @@ export class HiPSBrowserBox extends Box {
// Fill the HiPSList from the MOCServer
// Build a hierarchy w.r.t sorted by regime
let HiPSIDs = []
HiPSes.forEach((h) => {
let name = h.obs_title;
name = name.replace(/:|\'/g, '');
HiPSIDs.push(name)
HiPSBrowserBox.HiPSList[name] = h;
if (h.client_category) {
let path = h.client_category
fillHiPSHierarchy(name, h, path, hipsHierarchy)
} else {
let hipsID = h.ID;
hipsID = hipsID.replace('/P', '');
let path = "Others/" + hipsID;
fillHiPSHierarchy(name, h, path, hipsHierarchy)
}
});
self.searchDropdown.update({ options: HiPSIDs });
self.searchTree.setHierarchy(hipsHierarchy)
// Initialize the autocompletion without any filtering
@@ -191,37 +216,24 @@ export class HiPSBrowserBox extends Box {
tooltip: {
global: true,
aladin,
content: 'HiPS url, ID or keyword accepted',
content: 'HiPS url, ID or keyword accepted.',
},
actions: {
focus(e) {
searchDropdown.removeClass('aladin-valid')
searchDropdown.removeClass('aladin-not-valid')
},
keydown(e) {
e.stopPropagation();
action: (e) => {
_parseHiPS(e)
},
input: (e) => {
let value = e.target.value;
self.searchTree.triggerFilter({title: value});
if (e.key === 'Enter') {
e.preventDefault()
_parseHiPS(e)
}
},
input(e) {
self.infoCurrentHiPSBtn.update({
disable: true,
})
self.searchTree.triggerFilter({title: e.target.value});
searchDropdown.removeClass('aladin-valid')
searchDropdown.removeClass('aladin-not-valid')
},
self.infoCurrentHiPSBtn.update({
disabled: true,
})
},
});
let filterEnabler = Input.checkbox({
name: "filter-enabler",
tooltip: { content: "enable/disable" },
tooltip: { content: "Enable the filter" },
checked: false,
click(e) {
let on = e.target.checked;
@@ -247,21 +259,15 @@ export class HiPSBrowserBox extends Box {
},
});
let infoCurrentHiPSBtn = new ActionButton({
disable: true,
icon: {
size: 'medium',
monochrome: true,
url: infoIconUrl,
},
tooltip: {
global: true,
aladin,
content: "More about that survey?"
}
});
let infoCurrentHiPSBtn = ActionButton.BUTTONS(aladin)
.infoHiPS({disabled: true})
let filterBtn = new TogglerActionButton({
let filterBox = new HiPSFilterBox(aladin, {
callback: (params) => {
self._filterHiPSList(params);
},
})
let filterBtn = new WidgetTogglerButton({
icon: {
url: filterOffUrl,
monochrome: true,
@@ -272,40 +278,50 @@ export class HiPSBrowserBox extends Box {
position: { direction: "top" },
},
toggled: false,
actionOn: (e) => {
self.filterBox._show({position: {
anchor: 'right center'
}});
},
actionOff: (e) => {
self.filterBox._hide();
},
openPosition: 'right center',
widget: filterBox,
});
let filterNumberElt = document.createElement("div");
super(
{
super({
close: true,
tooltip: {
global: true,
aladin,
content: 'orange: out of the view, green: in view'
},
header: {
title: Layout.horizontal([new Icon({
size: 'medium',
url: hipsIconUrl,
monochrome: true,
}), "HiPS browser"]),
title: [
new Icon({
size: 'medium',
url: treeIconUrl,
monochrome: true,
}),
"HiPS browser",
new Icon({
size: 'medium',
url: helpIconUrl,
monochrome: true,
tooltip: {
content: 'HiPS:<br/><span class="aladin-indicator aladin-not-found"></span> out of the view<br /><span class="aladin-indicator aladin-valid"></span> in view',
mouse: true,
aladin
},
style: {
cursor: 'help'
}
}),
],
draggable: true,
},
//onDragged: () => {
//if (self.filterBtn.toggled) {
//self.filterBtn.toggle();
//}
//},
sizeable: true,
classList: ['aladin-HiPS-browser-box'],
content: Layout.vertical([
content: [
searchTree,
Layout.horizontal(["Search:", searchDropdown, infoCurrentHiPSBtn]),
Layout.horizontal(["Filter:", Layout.horizontal([filterEnabler, filterBtn, filterNumberElt])]),
]),
["Search:", searchDropdown, infoCurrentHiPSBtn],
[filterEnabler, filterBtn, filterNumberElt],
],
...options,
},
aladin.aladinDiv
@@ -314,11 +330,7 @@ export class HiPSBrowserBox extends Box {
self = this;
this.searchTree = searchTree;
this.filterBox = new HiPSFilterBox(aladin, {
callback: (params) => {
self._filterHiPSList(params);
},
})
this.filterBox = filterBox;
this.filterNumberElt = filterNumberElt;
this.filterBox._hide();
@@ -371,7 +383,7 @@ export class HiPSBrowserBox extends Box {
self.searchDropdown.addClass('aladin-valid');
self.infoCurrentHiPSBtn.update({
disable: false,
disabled: false,
action(e) {
window.open(hips.url);
}
@@ -477,14 +489,15 @@ export class HiPSBrowserBox extends Box {
self.searchDropdown.addClass('aladin-not-valid');
}
});
this.aladin.setOverlayImageLayer(hips, self.layer);
self.selected(hips)
}
// This method is executed only if the filter is enabled
_filterHiPSList(params) {
let self = this;
let HiPSIDs = [];
let numHiPSMatching = 0;
for (var key in HiPSBrowserBox.HiPSList) {
let HiPS = HiPSBrowserBox.HiPSList[key];
// apply filtering
@@ -496,7 +509,7 @@ export class HiPSBrowserBox extends Box {
let name = HiPS.obs_title;
name = name.replace(/:|\'/g, "");
HiPSIDs.push(name);
numHiPSMatching += 1;
}
}
@@ -504,8 +517,7 @@ export class HiPSBrowserBox extends Box {
self.searchTree.triggerFilter(params);
}
self.searchDropdown.update({ options: HiPSIDs });
self.filterNumberElt.innerHTML = HiPSIDs.length + "/" + Object.keys(HiPSBrowserBox.HiPSList).length;
self.filterNumberElt.innerHTML = numHiPSMatching + "/" + Object.keys(HiPSBrowserBox.HiPSList).length;
}
_hide() {
@@ -517,10 +529,11 @@ export class HiPSBrowserBox extends Box {
}
_show(options) {
this._requestMOCServer();
// Regenerate a new layer name
this.layer = (options && options.layer) || Utils.uuidv4();
this.selected = options && options.selected;
super._show(options)
this._requestMOCServer();
}
}

View File

@@ -0,0 +1,267 @@
// Copyright 2013 - UDS/CNRS
// The Aladin Lite program is distributed under the terms
// of the GNU General Public License version 3.
//
// This file is part of Aladin Lite.
//
// Aladin Lite is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, version 3 of the License.
//
// Aladin Lite is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// The GNU General Public License is available in COPYING file
// along with Aladin Lite.
//
import { Box } from "../Widgets/Box.js";
import hipsIconUrl from "../../../../assets/icons/hips.svg";
import addIconUrl from "../../../../assets/icons/plus.svg";
import settingsIconUrl from "../../../../assets/icons/settings.svg";
import { Icon } from "../Widgets/Icon.js";
import { HiPSSelector } from "../Input/HiPSSelector.js";
import { HiPSBrowserBox } from "./HiPSBrowserBox.js";
import { Input } from "../Widgets/Input.js";
import { ActionButton } from "../Widgets/ActionButton.js";
import { Form } from "../Widgets/Form.js";
import { TogglerActionButton } from "../Button/Toggler.js";
import { Utils } from "../../Utils";
import { HiPSComposite } from "../../HiPSComposite.js";
/******************************************************************************
* Aladin Lite project
*
* File gui/Box/HiPSCompositeBox.js
*
* The code source of the interface for creating a new composite HiPS survey from multiple surveys
*
* Author: Matthieu Baumann[CDS]
*
*****************************************************************************/
export class HiPSCompositeBox extends Box {
static HiPSList = {};
constructor(aladin, options) {
let self;
let nameInput = Input.text({
tooltip: {
global: true,
aladin,
content: 'What name for your composite survey?'
},
placeholder: "What name?...",
autocomplete: 'off',
autofocus: true,
actions: {
dblclick: (_) => {
nameInput.set('')
},
keydown: (e) => {
e.stopPropagation();
//
}
},
});
let content = [[
new ActionButton({
icon: {
url: addIconUrl,
size: "small",
monochrome: true,
},
tooltip: {
content: "Add a new layer",
position: { direction: "top" },
},
toggled: false,
action(_) {
self.content.push(self._addNewHiPS());
self.update({content: self.content})
}
}),
nameInput
]];
super(
{
close: true,
header: {
title: [
new Icon({
size: 'medium',
url: hipsIconUrl,
monochrome: true,
}),
"HiPS Compositor"
],
draggable: true,
},
content,
...options,
},
aladin.aladinDiv
);
this.aladin = aladin;
this.hipsOptions = [];
self = this;
this.layer = Utils.uuidv4();
this.hipsComposite = new HiPSComposite(this.hipsOptions)
this.numHiPSLayers = 0;
this.content = content.concat([this._addNewHiPS()])
this.update({content: this.content})
this.openSettings = null;
}
_addNewHiPS() {
const getIdHiPS = (node) => {
let parent = node.parentElement;
return [...parent.parentElement.children].indexOf(parent) - 1;
};
this.hipsOptions.push({});
let self = this;
let newLayerLayout = [
new HiPSSelector({
change(e) {
let name = e.target.value;
let idLayer = getIdHiPS(e.target);
if (name === "More...") {
if (!aladin.hipsBrowser) {
aladin.hipsBrowser = new HiPSBrowserBox(aladin);
}
aladin.hipsBrowser._show({
selected: (hips) => {
self.hipsOptions[idLayer].id = hips.id || hips.url
},
position: { anchor: "center center" }
});
} else {
// it is an hips
let HiPSOptions = HiPSSelector.cachedHiPS[name];
self.hipsOptions[idLayer].id = HiPSOptions.id || HiPSOptions.url
}
self.hipsComposite.setOptions(self.hipsOptions);
self.aladin.setOverlayImageLayer(self.hipsComposite, self.layer);
}
}),
this._createLayerSettingsBox(),
ActionButton.BUTTONS(aladin)
.remove((e) => {
let node = e.target.parentElement.parentElement.parentElement;
let idLayer = [...node.parentElement.children].indexOf(node);
this.content.splice(idLayer, 1)
this.hipsOptions.splice(idLayer, 1);
this.update({content: this.content})
this.numHiPSLayers = this.content.length - 1;
})
];
this.numHiPSLayers += 1;
return newLayerLayout;
}
_createLayerSettingsBox() {
let self = this;
let layerSettingsBox = new Box({
close: false,
content: new Form({
subInputs: [
{
type: 'color',
label: "Color",
value: 'red',
name: 'color',
change(e) {
let idLayer = getIdHiPS(e.target);
let hex = e.target.value;
}
},
{
label: 'Stretch',
type: "select",
name: 'stretch',
value: 'linear',
options: ['sqrt', 'linear', 'asinh', 'pow2', 'log'],
change(e) {},
},
{
type: 'number',
label: "Min cut",
name: 'mincut',
value: 0.0,
change: (e) => {
let minCut = +e.target.value
}
},
{
label: 'Max cut',
type: "number",
name: 'maxcut',
value: 0.0,
change: (e) => {
let maxCut = +e.target.value
}
},
]
}),
}, this.aladin.aladinDiv);
layerSettingsBox._hide()
/*let layerSettingsBtn = new TogglerActionButton({
icon: { url: settingsIconUrl, monochrome: true },
size: "small",
tooltip: {
content: "Settings",
position: { direction: "top" },
},
toggled: false,
on: (_) => {
layerSettingsBox._show({
position: {
nextTo: layerSettingsBtn,
direction: "right",
aladin: self.aladin,
},
});
if (self.openSettings) {
self.openSettings.close();
}
self.openSettings = layerSettingsBtn;
},
off: (_) => {
layerSettingsBox._hide();
if (self.openSettings === layerSettingsBtn) {
self.openSettings = null;
}
},
});*/
return layerSettingsBtn
}
_hide() {
if (this.openSettings)
this.openSettings.close();
super._hide()
}
}

View File

@@ -17,13 +17,12 @@
// along with Aladin Lite.
//
import filterOnUrl from "../../../../assets/icons/filter-on.svg";
import { Box } from "../Widgets/Box.js";
import { Form } from "../Widgets/Form.js";
import { Layout } from "../Layout.js";
import { Angle } from "../../libs/astro/angle.js";
import { AladinUtils } from "../../AladinUtils.js";
import { Input } from "../Widgets/Input.js";
import { Icon } from "../Widgets/Icon.js";
/******************************************************************************
* Aladin Lite project
@@ -41,7 +40,7 @@ export class HiPSFilterBox extends Box {
let regimeBtn = Input.checkbox({
name: 'Freq',
tooltip: {content: 'Observation bandwidth', position: {direction: 'left'}},
tooltip: {content: 'enable/disable', position: {direction: 'left'}},
type: 'checkbox',
checked: false,
click(e) {
@@ -50,7 +49,7 @@ export class HiPSFilterBox extends Box {
});
let resolutionBtn = Input.checkbox({
name: 'Resolution',
tooltip: {content: 'Check for HiPS with a specific pixel resolution.', position: {direction: 'left'}},
tooltip: {content: 'enable/disable', position: {direction: 'left'}},
type: 'checkbox',
checked: false,
click(e) {
@@ -58,82 +57,80 @@ export class HiPSFilterBox extends Box {
}
});
let regimeOption = Layout.horizontal({
tooltip: {
content: "Observation regime",
position: { direction: "right" },
},
label: 'Freq: ',
layout: [Input.select({
value: "Optical",
options: [
"Radio",
"Infrared",
"Millimeter",
"Optical",
"UV",
"EUV",
"X-ray",
"Gamma-ray",
],
change: (e) => {
let regime = e.target.value;
self.params["regime"] = regime;
self._triggerFilteringCallback();
},
}), regimeBtn]
});
let resolutionOption = Layout.horizontal({
label: "Max resolution [°/px]:",
layout: [
new Input({
name: "res",
value: 0.1,
type: 'range',
cssStyle: {
width: '200px'
},
tooltip: {content: AladinUtils.degreesToString(0.1), position: {direction: 'bottom'}},
ticks: [0.1 / 3600, 1 / 3600, 1 / 60, 0.1],
stretch: "log",
min: 0.1 / 3600,
max: 0.1,
reversed: true,
change: (e, slider, deg) => {
slider.update({value: e.target.value, tooltip: {content: AladinUtils.degreesToString(deg), position:{direction:'bottom'}}});
let resolution = new Angle(deg);
self.params["resolution"] = resolution.degrees();
self._triggerFilteringCallback();
},
}),
resolutionBtn,
]
});
super(
{
header: {
title: 'Filter tags',
title: [
new Icon({
size: 'medium',
url: filterOnUrl,
monochrome: true,
}),
'Filter'
],
draggable: false,
},
close: false,
classList: ['aladin-HiPS-filter-box'],
content: Layout.vertical([
new Form({
subInputs: [
{
type: "group",
subInputs: [
regimeOption,
resolutionOption
content: [
{
start: [
"Freq:",
Input.select({
tooltip: {
content: "Observation regime",
position: { direction: "left" },
},
value: "Optical",
options: [
"Radio",
"Infrared",
"Millimeter",
"Optical",
"UV",
"EUV",
"X-ray",
"Gamma-ray",
],
},
change: (e) => {
let regime = e.target.value;
self.params["regime"] = regime;
self._triggerFilteringCallback();
},
}),
],
}),
])
end: [regimeBtn]
},
{
start: [
"Max resolution [°/px]:",
new Input({
name: "res",
value: 0.1,
type: 'range',
cssStyle: {
width: '200px'
},
tooltip: {content: AladinUtils.degreesToString(0.1), position: {direction: 'bottom'}},
ticks: [0.001 / 3600, 0.01 / 3600, 0.1 / 3600, 1 / 3600, 1 / 60, 0.1],
stretch: "log",
min: 0.001 / 3600,
max: 0.1,
reversed: true,
change: (e, slider, deg) => {
slider.update({value: e.target.value, tooltip: {content: AladinUtils.degreesToString(deg), position:{direction:'bottom'}}});
let resolution = new Angle(deg);
self.params["resolution"] = resolution.degrees();
self._triggerFilteringCallback();
},
})
],
end: [resolutionBtn]
}
]
},
aladin.aladinDiv
);
@@ -170,15 +167,6 @@ export class HiPSFilterBox extends Box {
}
}
/*signalBrowserStatus(closed) {
this.browserClosed = closed;
// open
if (!closed) {
this._requestMOCServer()
}
}*/
enable(enable) {
this.on = enable;

View File

@@ -1,172 +0,0 @@
// Copyright 2013 - UDS/CNRS
// The Aladin Lite program is distributed under the terms
// of the GNU General Public License version 3.
//
// This file is part of Aladin Lite.
//
// Aladin Lite is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, version 3 of the License.
//
// Aladin Lite is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// The GNU General Public License is available in COPYING file
// along with Aladin Lite.
//
import { MocServer } from "../../MocServer.js";
import { Box } from "../Widgets/Box.js";
import { Layout } from "../Layout.js";
import { ActionButton } from "../Widgets/ActionButton.js";
import { Input } from "../Widgets/Input.js";
/******************************************************************************
* Aladin Lite project
*
* File gui/HiPSSelector.js
*
*
* Author: Thomas Boch, Matthieu Baumann[CDS]
*
*****************************************************************************/
export class HiPSSelectorBox extends Box {
static HiPSList = {};
constructor(aladin, options) {
MocServer.getAllHiPSes()
.then((HiPSes) => {
HiPSes.forEach((h) => {
HiPSSelectorBox.HiPSList[h.obs_title] = h
HiPSSelectorBox.HiPSList[h.ID] = h
});
inputText.update({autocomplete: {options: Object.keys(HiPSSelectorBox.HiPSList)}})
});
let self;
let loadBtn = new ActionButton({
content: 'Add',
disable: true,
action(e) {
self.callback && self.callback(inputText.get());
// reset the field
inputText.set('');
self._hide();
}
})
let inputText = Input.text({
classList: ['search'],
name: 'survey',
placeholder: "Type survey keywords",
actions: {
change() {
const HiPS = HiPSSelectorBox.HiPSList[this.value];
inputText.set(HiPS.ID);
loadBtn.update({disable: false});
},
keydown() {
loadBtn.update({disable: true});
}
}
});
super(
{
close: false,
content: Layout.horizontal({
layout: [
inputText,
loadBtn
]
}),
...options
},
aladin.aladinDiv
)
self = this;
// Query the mocserver
/*MocServer.getAllHiPSes();
autocomplete({
input: inputText.element(),
fetch: function(text, update) {
text = text.toLowerCase();
// filter suggestions
const suggestions = MocServer.getAllHiPSes().filter(n => n.ID.toLowerCase().includes(text) || n.obs_title.toLowerCase().includes(text))
// sort suggestions
suggestions.sort( function(a , b) {
let scoreForA = 0;
let scoreForB = 0;
if (a.ID.toLowerCase().includes(text)) {
scoreForA += 100;
}
if (b.ID.toLowerCase().includes(text)) {
scoreForB += 100;
}
if (a.obs_title.toLowerCase().includes(text)) {
scoreForA += 50;
}
if (b.obs_title.toLowerCase().includes(text)) {
scoreForB += 50;
}
if (a.obs_description && a.obs_description.toLowerCase().includes(text)) {
scoreForA += 10;
}
if (b.obs_description && b.obs_description.toLowerCase().includes(text)) {
scoreForB += 10;
}
if (scoreForA > scoreForB) {
return -1;
}
if (scoreForB > scoreForA) {
return 1;
}
return 0;
});
// limit to 50 first suggestions
const returnedSuggestions = suggestions.slice(0, 50);
update(returnedSuggestions);
},
onSelect: function(item) {
inputText.set(item.ID);
loadBtn.update({disable: false});
inputText.element().blur();
},
// attach container to AL div if needed (to prevent it from being hidden in full screen mode)
customize: function(input, inputRect, container, maxHeight) {
// this tests if we are in full screen mode
if (aladin.isInFullscreen) {
aladin.aladinDiv.appendChild(container);
}
},
render: function(item, currentValue) {
const itemElement = document.createElement("div");
itemElement.innerHTML = item.obs_title + ' - ' + '<span style="color: #ae8de1">' + item.ID + '</span>';
return itemElement;
}
});*/
}
attach(callback) {
this.callback = callback;
}
}

View File

@@ -36,7 +36,7 @@ import { Form } from "../Widgets/Form.js";
import pixelHistIconUrl from '../../../../assets/icons/pixel_histogram.svg';
import { RadioButton } from "../Widgets/Radio.js";
import waveOnIconUrl from '../../../../assets/icons/wave-on.svg';
import { TogglerActionButton } from "../Button/Toggler.js";
import { WidgetTogglerButton } from "../Button/Toggler.js";
import { Layout } from "../Layout.js";
export class HiPSSettingsBox extends Box {
@@ -53,9 +53,7 @@ import { TogglerActionButton } from "../Button/Toggler.js";
},
tooltip: {content: 'Contrast', position: {direction: 'bottom'}},
action: (e) => {
const content = Layout.vertical({
layout: [Layout.horizontal([self.selector, self.spectraBtn]), self.luminositySettingsContent]
});
const content = Layout.vertical([[self.selector, self.spectraBtn], self.luminositySettingsContent]);
self.update({content})
}
},
@@ -67,7 +65,7 @@ import { TogglerActionButton } from "../Button/Toggler.js";
},
tooltip: {content: 'Opacity', position: {direction: 'bottom'}},
action: (e) => {
const content = Layout.vertical({layout: [Layout.horizontal([self.selector, self.spectraBtn]), self.opacitySettingsContent]});
const content = Layout.vertical([[self.selector, self.spectraBtn], self.opacitySettingsContent]);
self.update({content})
}
},
@@ -78,7 +76,7 @@ import { TogglerActionButton } from "../Button/Toggler.js";
},
tooltip: {content: 'Colormap', position: {direction: 'bottom'}},
action: (e) => {
const content = Layout.vertical({layout: [Layout.horizontal([self.selector, self.spectraBtn]), self.colorSettingsContent]});
const content = Layout.vertical([[self.selector, self.spectraBtn], self.colorSettingsContent]);
self.update({content})
}
},
@@ -90,7 +88,7 @@ import { TogglerActionButton } from "../Button/Toggler.js";
},
tooltip: {content: 'Cutouts', position: {direction: 'bottom'}},
action: (e) => {
const content = Layout.vertical({layout: [Layout.horizontal([self.selector, self.spectraBtn]), self.pixelSettingsContent]});
const content = Layout.vertical([[self.selector, self.spectraBtn], self.pixelSettingsContent]);
self.update({content})
}
},
@@ -211,30 +209,26 @@ import { TogglerActionButton } from "../Button/Toggler.js";
{
label: 'min cut:',
type: 'number',
cssStyle: {
width: '6rem',
},
tooltip: {content: 'Min cut', position: {direction: 'bottom'}},
name: 'mincut',
value: 0.0,
change: (e) => {
let minCut = +e.target.value
self.options.layer.setCuts(minCut, self.options.layer.getColorCfg().getCuts()[1])
}
},
cssStyle: { width: '7rem' }
},
{
type: 'number',
label: 'max cut:',
cssStyle: {
width: '6rem',
},
tooltip: {content: 'Max cut', position: {direction: 'bottom'}},
name: 'maxcut',
value: 1.0,
change: (e) => {
let maxCut = +e.target.value
self.options.layer.setCuts(self.options.layer.getColorCfg().getCuts()[0], maxCut)
}
},
cssStyle: { width: '7rem' }
}]
});
@@ -325,7 +319,7 @@ import { TogglerActionButton } from "../Button/Toggler.js";
if (options.layer.isSpectralCube && options.layer.isSpectralCube()) {
let spectraDisplayer = self.aladin.view.spectraDisplayer;
self.spectraBtn = new TogglerActionButton({
self.spectraBtn = new WidgetTogglerButton({
content: 'Spectra',
icon: {
size: 'small',
@@ -334,16 +328,13 @@ import { TogglerActionButton } from "../Button/Toggler.js";
},
tooltip: {content: 'Show/hide spectra', position: {direction: 'bottom'}},
toggled: true,
actionOn: () => {
enable: (o) => {
spectraDisplayer.attachHiPS3D(options.layer)
spectraDisplayer.show()
},
actionOff: () => {
spectraDisplayer.hide()
}
widget: spectraDisplayer
});
self.update({content: Layout.vertical([Layout.horizontal([self.selector, self.spectraBtn]), self.opacitySettingsContent])})
self.update({content: Layout.vertical([[self.selector, self.spectraBtn], self.opacitySettingsContent])})
}
this._update(options.layer)
@@ -353,9 +344,7 @@ import { TogglerActionButton } from "../Button/Toggler.js";
}
_addListeners() {
let self = this;
ALEvent.HIPS_LAYER_CHANGED.listenedBy(this.aladin.aladinDiv, (e) => {
ALEvent.LAYER_CHANGED.listenedBy(this.aladin.aladinDiv, (e) => {
const hips = e.detail.layer;
let selectedLayer = this.options.layer;

View File

@@ -36,6 +36,7 @@ import { Utils } from "../../Utils";
import { View } from "../../View.js";
import { HiPSSettingsBox } from "./HiPSSettingsBox.js";
import hipsIconUrl from "../../../../assets/icons/hips.svg";
import treeIconUrl from "../../../../assets/icons/tree.svg";
import showIconUrl from "../../../../assets/icons/show.svg";
import addIconUrl from "../../../../assets/icons/plus.svg";
import hideIconUrl from "../../../../assets/icons/hide.svg";
@@ -44,13 +45,18 @@ import settingsIconUrl from "../../../../assets/icons/settings.svg";
import searchIconImg from "../../../../assets/icons/search.svg";
import downloadIconUrl from '../../../../assets/icons/download.svg';
import swapIcon from '../../../../assets/icons/swap.svg'
import { TogglerActionButton } from "../Button/Toggler.js";
import { WidgetTogglerButton } from "../Button/Toggler.js";
import { Icon } from "../Widgets/Icon.js";
import { Box } from "../Widgets/Box.js";
import { CtxMenuActionButtonOpener } from "../Button/CtxMenuOpener.js";
import { Input } from "../Widgets/Input.js";
import { Image } from "../../Image.js";
import { HiPSBrowserBox } from "./HiPSBrowserBox.js";
import { HiPSCompositeBox } from "./HiPSCompositeBox.js"
import { Catalog } from "../../Catalog.js";
import { ProgressiveCat } from "../../ProgressiveCat.js";
import { Form } from "../Widgets/Form.js";
import { HiPSSelector } from "./../Input/HiPSSelector.js";
import { HiPS } from "../../HiPS.js";
export class OverlayStackBox extends Box {
/*static previewImagesUrl = {
@@ -120,7 +126,7 @@ export class OverlayStackBox extends Box {
},
};
// Constructor
constructor(aladin, stackBtn) {
constructor(aladin) {
super(
{
close: true,
@@ -132,18 +138,14 @@ export class OverlayStackBox extends Box {
},
aladin.aladinDiv
);
this.stackBtn = stackBtn;
this.cachedHiPS = {};
this.aladin = aladin;
this.mode = "stack";
this._addListeners();
this.mocHiPSUrls = {};
this.ui = {};
this.HiPSui = {};
let self = this;
// Add overlay button
this.addOverlayBtn = new CtxMenuActionButtonOpener(
@@ -153,6 +155,7 @@ export class OverlayStackBox extends Box {
size: "small",
monochrome: true,
},
openDirection: 'right',
tooltip: {
content: "A catalog, MOC or footprint",
position: { direction: "top" },
@@ -187,8 +190,6 @@ export class OverlayStackBox extends Box {
o.stopPropagation();
o.preventDefault();
//self._hide();
const simbadHiPS = A.catalogHiPS(
OverlayStackBox.predefinedCats.simbad
.url,
@@ -204,8 +205,6 @@ export class OverlayStackBox extends Box {
o.stopPropagation();
o.preventDefault();
//self._hide();
const simbadHiPS = A.catalogHiPS(
OverlayStackBox.predefinedCats.gaia.url,
OverlayStackBox.predefinedCats.gaia
@@ -487,6 +486,7 @@ export class OverlayStackBox extends Box {
size: "small",
monochrome: true,
},
openDirection: 'right',
ctxMenu: [
{
label: {
@@ -501,28 +501,12 @@ export class OverlayStackBox extends Box {
cursor: "help",
},
},
content: "Add new survey",
content: "Add a new HiPS",
},
action: (e) => {
e.stopPropagation();
e.preventDefault();
/*self._hide();
self.hipsSelectorBox = new HiPSSelectorBox(self.aladin);
// attach a callback
self.hipsSelectorBox.attach(
(HiPSId) => {
let name = Utils.uuidv4()
self.aladin.setOverlayImageLayer(HiPSId, name)
self.show();
}
);
self.hipsSelectorBox._show({
position: self.position,
});*/
self.aladin.addNewImageLayer(
'P/DSS2/color'
);
@@ -531,7 +515,7 @@ export class OverlayStackBox extends Box {
{
label: {
icon: {
url: hipsIconUrl,
url: treeIconUrl,
monochrome: true,
tooltip: {
content: "From our database...",
@@ -547,10 +531,50 @@ export class OverlayStackBox extends Box {
e.stopPropagation();
e.preventDefault();
if (!self.hipsBrowser)
self.hipsBrowser = new HiPSBrowserBox(aladin);
if (!aladin.hipsBrowser)
aladin.hipsBrowser = new HiPSBrowserBox(aladin);
self.hipsBrowser._show({position: {
let newLayer = Utils.uuidv4();
aladin.hipsBrowser._show({
selected: (hips) => {
let oldHiPS = aladin.getOverlayImageLayer(newLayer);
if (oldHiPS && hips.id === oldHiPS.id) {
return;
}
aladin.setOverlayImageLayer(hips, newLayer);
},
position: {
anchor: 'center center'
}
});
},
},
{
label: {
icon: {
url: hipsIconUrl,
monochrome: true,
tooltip: {
content: "Combine different surveys into a color one!",
position: { direction: "right" },
},
cssStyle: {
cursor: "help",
},
},
content: "Add a composite HiPS",
},
disabled: true,
action: (e) => {
e.stopPropagation();
e.preventDefault();
if (!self.hipsCompositeBox)
self.hipsCompositeBox = new HiPSCompositeBox(aladin);
self.hipsCompositeBox._show({position: {
anchor: 'center center'
}});
},
@@ -582,8 +606,9 @@ export class OverlayStackBox extends Box {
ContextMenu.webkitDir({
label: "Load local HiPS",
action(files) {
let id = files[0].webkitRelativePath.split("/")[0];
let name = id;
// Give a different id at each loading.
let id = Utils.uuidv4();
let name = files[0].webkitRelativePath.split("/")[0];
let hips = self.aladin.createImageSurvey(
id,
@@ -646,36 +671,29 @@ export class OverlayStackBox extends Box {
}
);
ALEvent.GRAPHIC_OVERLAY_LAYER_CHANGED.listenedBy(
ALEvent.LAYER_ADDED.listenedBy(
this.aladin.aladinDiv,
function (e) {
updateOverlayList();
}
);
ALEvent.HIPS_LAYER_ADDED.listenedBy(
this.aladin.aladinDiv,
function (e) {
updateOverlayList();
}
);
ALEvent.HIPS_LAYER_SWAP.listenedBy(this.aladin.aladinDiv, function (e) {
ALEvent.LAYER_SWAPPED.listenedBy(this.aladin.aladinDiv, function (e) {
updateOverlayList();
});
ALEvent.HIPS_LAYER_REMOVED.listenedBy(
ALEvent.LAYER_REMOVED.listenedBy(
this.aladin.aladinDiv,
function (e) {
updateOverlayList();
}
);
ALEvent.HIPS_LAYER_CHANGED.listenedBy(
ALEvent.LAYER_CHANGED.listenedBy(
this.aladin.aladinDiv,
function (e) {
const hips = e.detail.layer;
let ui = self.HiPSui[hips.layer];
let ui = self.ui[hips.layer];
if (!ui) {
return;
@@ -702,83 +720,65 @@ export class OverlayStackBox extends Box {
);
updateOverlayList();
// Add a listener for HiPS list changes
ALEvent.FAVORITE_HIPS_LIST_UPDATED.listenedBy(document.body, (event) => {
let favoritesHips = event.detail;
self.cachedHiPS = {};
for (var hips of favoritesHips) {
let key = hips.name || hips.id || hips.url;
self.cachedHiPS[key] = hips;
}
// Update the options of the selector
const favorites = Object.keys(self.cachedHiPS);
for (var key in self.HiPSui) {
let hips = self.HiPSui[key];
let currentHiPS = hips.HiPSSelector.options.value
let favoritesCopy = [...favorites];
// add the current hips to the selector as well, even if it has been manually
// removed from the HiPSList
if (favoritesCopy.indexOf(currentHiPS) < 0) {
favoritesCopy.push(currentHiPS)
}
// one must add the current HiPS too!
favoritesCopy.sort();
favoritesCopy.push("More...")
hips.HiPSSelector.update({value: currentHiPS, options: favoritesCopy});
}
});
}
_hide() {
for (var key in this.HiPSui) {
let hips = this.HiPSui[key];
if (hips.settingsBtn.toggled) {
for (var key in this.ui) {
let ui = this.ui[key];
if (ui.settingsBtn && ui.settingsBtn.toggled) {
// toggle off
hips.settingsBtn.toggle();
ui.settingsBtn.toggle();
}
}
if (this.addOverlayBtn) this.addOverlayBtn.hideMenu();
if (this.addOverlayBtn) this.addOverlayBtn.close();
if (this.addHiPSBtn) this.addHiPSBtn.hideMenu();
// toggle the button because the window is closed
this.stackBtn.update({toggled: false});
if (this.addHiPSBtn) this.addHiPSBtn.close();
super._hide();
}
createLayout() {
this.HiPSui = {};
delete() {
if (!this.ui) {
return
}
let layout = [Layout.horizontal([this.addOverlayBtn, "Overlays"])];
for (let component of Object.values(this.ui)) {
for (let elt of Object.values(component)) {
elt.remove && elt.remove()
}
}
}
createLayout() {
this.delete()
this.ui = {};
let layout = [[this.addOverlayBtn, "&nbsp;Overlays"]];
layout = layout.concat(this._createOverlaysList());
layout.push(
Layout.horizontal({
layout: [
this.addHiPSBtn,
"Surveys",
this.filterEnabler,
this.filterBtn,
],
})
[
this.addHiPSBtn,
"&nbsp;Surveys",
this.filterEnabler,
this.filterBtn,
],
);
layout = layout.concat(this._createSurveysList());
return Layout.vertical({ layout });
return Layout.vertical(layout,
{
cssStyle: {
overflowWrap: "anywhere",
wordBreak: "break-word",
}
}
);
}
_createOverlaysList() {
let self = this;
let aladin = self.aladin;
let layout = [];
const overlays = Array.from(this.aladin.getOverlays())
@@ -789,8 +789,7 @@ export class OverlayStackBox extends Box {
// list of overlays
for (const overlay of overlays) {
const name = overlay.name;
let optBtn = [];
optBtn.push(new ActionButton({
let showBtn = new ActionButton({
size: "small",
icon: {
url: overlay.isShowing ? showIconUrl : hideIconUrl,
@@ -815,25 +814,10 @@ export class OverlayStackBox extends Box {
});
}
},
}));
optBtn.push(new ActionButton({
icon: {
url: removeIconUrl,
monochrome: true,
},
size: "small",
/*cssStyle: {
visibility: Utils.hasTouchScreen() ? 'visible' : 'hidden',
},*/
tooltip: {
content: "Remove",
position: { direction: "top" },
},
action(e) {
self.aladin.removeLayer(overlay);
},
}));
});
let optBtn = [
showBtn,
];
if (overlay.serialize) {
optBtn.push(new ActionButton({
@@ -853,44 +837,108 @@ export class OverlayStackBox extends Box {
},
}));
}
let item = Layout.horizontal({
layout: [
this._addOverlayIcon(overlay),
name,
Layout.horizontal({ layout: optBtn }),
],
cssStyle: {
textAlign: "center",
display: "flex",
alignItems: "center",
listStyle: "none",
justifyContent: "space-between",
width: "100%",
},
});
if (overlay instanceof Catalog || overlay instanceof ProgressiveCat) {
let catSettingsBox = new Box({
close: false,
content: new Form({
subInputs: [
{
label: 'Size',
tooltip: {content: 'Size of the sources', position: {direction: 'right'}},
name: 'size',
type: 'range',
min: 2.0,
max: 30.0,
value: overlay.sourceSize,
change: (e) => {
const size = +e.target.value;
overlay.setSourceSize(size)
}
},
{
label: 'Shape',
name: 'shape',
type: 'select',
options: [
{ value: "plus", label: "+" },
{ value: "rhomb", label: "◇" },
{ value: "triangle", label: "△" },
{ value: "cross", label: "✕" },
{ value: "square", label: "□" },
{ value: "circle", label: "○" },
],
value: (overlay.shapeFn && "square") || overlay.shape,
change: (e) => {
const shape = e.target.value
overlay.setShape(shape)
}
},
{
label: 'Color',
name: 'color',
type: 'color',
value: overlay.color,
change: (e) => {
let hex = e.target.value;
overlay.setColor(hex)
}
},
]
}),
}, this.aladin.aladinDiv);
catSettingsBox._hide()
/*if(!Utils.hasTouchScreen()) {
layout.push({
label: item,
cssStyle,
hover(e) {
showBtn.el.style.visibility = 'visible'
deleteBtn.el.style.visibility = 'visible'
// catalog settings
let catSettingsBtn = new WidgetTogglerButton({
icon: { url: settingsIconUrl, monochrome: true },
size: "small",
tooltip: {
content: "Settings",
position: { direction: "top" },
},
unhover(e) {
showBtn.el.style.visibility = 'hidden'
deleteBtn.el.style.visibility = 'hidden'
toggled: false,
enable: (_) => {
// toggle off the other settings if opened
for (var l in self.ui) {
let ui = self.ui[l]
if (l != name) {
if (ui.settingsBtn)
ui.settingsBtn.close();
}
}
let spectraDisplayer = aladin.view.spectraDisplayer;
if (spectraDisplayer)
spectraDisplayer.attachHiPS3D(options.layer)
},
})
} else {
layout.push({
label: item,
cssStyle
})
}*/
layout.push(item);
widget: catSettingsBox,
openDirection: "right"
});
optBtn.push(catSettingsBtn);
if (!(name in self.ui)) {
self.ui[name] = {
settingsBox: catSettingsBox,
settingsBtn: catSettingsBtn,
showBtn,
};
}
}
optBtn.push(ActionButton.BUTTONS(self.aladin).remove(
(e) => {
self.aladin.removeLayer(overlay);
}
));
layout.push([
this._addOverlayIcon(overlay),
'<div class="aladin-overlay-label">' + name + "</div>",
optBtn
]);
}
return layout;
@@ -899,98 +947,96 @@ export class OverlayStackBox extends Box {
_createSurveysList() {
let self = this;
const layers = Array.from(self.aladin.getStackLayers())
let aladin = self.aladin;
const layers = Array.from(aladin.getStackLayers())
.reverse()
.map((name) => {
let overlay = self.aladin.getOverlayImageLayer(name);
let overlay = aladin.getOverlayImageLayer(name);
return overlay;
});
// survey list
let layout = [];
let hipsOptions = Object.keys(self.cachedHiPS);
hipsOptions.sort()
for (const layer of layers) {
if (!layer) {
for (const hips of layers) {
if (!hips) {
continue;
}
let options = Array.from([...hipsOptions])
let value = layer.name || layer.id
if (options.indexOf(value) < 0) {
options.push(value)
}
options.push("More...")
let HiPSSelector = Input.select({
value,
options,
title: layer.name,
change: (e) => {
let HiPSselect = new HiPSSelector({
layer: hips,
change(e) {
let name = e.target.value;
if (name === "More...") {
if (!self.hipsBrowser)
self.hipsBrowser = new HiPSBrowserBox(self.aladin);
if (!aladin.hipsBrowser) {
aladin.hipsBrowser = new HiPSBrowserBox(aladin);
}
self.hipsBrowser._show({ layer: layer.layer, position: { anchor: "center center" } });
aladin.hipsBrowser._show({
selected: (hips) => {
self.aladin.setOverlayImageLayer(hips, hips.layer);
},
position: { anchor: "center center" }
});
return;
}
// search for the
let overlayLayer;
if (name in self.cachedHiPS) {
// it is an hips
let HiPSOptions = self.cachedHiPS[name];
let overlayLayer;
if (name in HiPSSelector.cachedHiPS) {
// it is an hips
let HiPSOptions = HiPSSelector.cachedHiPS[name];
overlayLayer = A.HiPS(HiPSOptions.id || HiPSOptions.url, HiPSOptions);
} else {
overlayLayer = layer
overlayLayer = hips
}
self.aladin.setOverlayImageLayer(overlayLayer, layer.layer);
aladin.setOverlayImageLayer(overlayLayer, hips.layer);
}
});
let deleteBtn = ActionButton.createSmallSizedIconBtn({
icon: { url: removeIconUrl, monochrome: true },
//disable: layer.layer === "base",
tooltip: { content: "Remove", position: { direction: "top" } },
action(e) {
self.aladin.removeImageLayer(layer.layer);
action: (e) => {
aladin.removeImageLayer(hips.layer);
// remove HiPS cube player if any
self.aladin.removeUIByName("cube_displayer" + layer.layer)
aladin.removeUIByName("cube_displayer" + hips.layer)
let spectraDisplayer = aladin.view.spectraDisplayer;
if (hips instanceof HiPS && spectraDisplayer && hips === spectraDisplayer.hips) {
spectraDisplayer._hide()
}
},
});
let prevOpacity = null;
let showBtn = ActionButton.createSmallSizedIconBtn({
icon: {
url: layer.getOpacity() === 0.0 ? hideIconUrl : showIconUrl,
url: hips.getOpacity() === 0.0 ? hideIconUrl : showIconUrl,
monochrome: true,
},
tooltip: {
content: layer.getOpacity() === 0.0 ? "Show" : "Hide",
content: hips.getOpacity() === 0.0 ? "Show" : "Hide",
position: { direction: "top" },
},
action(e, btn) {
e.preventDefault();
e.stopPropagation();
let opacity = layer.getOpacity();
let opacity = hips.getOpacity();
if (opacity === 0.0) {
let newOpacity = prevOpacity || 1.0;
prevOpacity = null;
layer.setOpacity(newOpacity);
hips.setOpacity(newOpacity);
btn.update({
icon: { monochrome: true, url: showIconUrl },
tooltip: { content: "Hide" },
});
} else {
prevOpacity = opacity;
layer.setOpacity(0.0);
hips.setOpacity(0.0);
btn.update({
icon: { monochrome: true, url: hideIconUrl },
tooltip: { content: "Show" },
@@ -1000,10 +1046,9 @@ export class OverlayStackBox extends Box {
});
let settingsBox = new HiPSSettingsBox(self.aladin);
settingsBox.update({ layer });
settingsBox._hide();
let settingsBtn = new TogglerActionButton({
let settingsBtn = new WidgetTogglerButton({
icon: { url: settingsIconUrl, monochrome: true },
size: "small",
tooltip: {
@@ -1011,104 +1056,27 @@ export class OverlayStackBox extends Box {
position: { direction: "top" },
},
toggled: false,
actionOn: (e) => {
enable: (_) => {
// toggle off the other settings if opened
for (var l in self.HiPSui) {
let ui = self.HiPSui[l]
for (var l in self.ui) {
let ui = self.ui[l]
if (l != layer.layer) {
if (l != hips.layer) {
ui.settingsBtn.close();
}
}
settingsBox._show({
position: {
nextTo: settingsBtn,
direction: "right",
aladin: self.aladin,
},
});
},
actionOff: (e) => {
settingsBox._hide();
settingsBox.update({ layer: hips });
},
widget: settingsBox,
openDirection: "right",
});
let loadMOCBtn = new ActionButton({
size: "small",
icon: {
url: Icon.dataURLFromSVG({ svg: Icon.SVG_ICONS.MOC }),
size: "small",
monochrome: true,
},
tooltip: {
content: "Add coverage",
position: { direction: "top" },
},
toggled: (() => {
let overlays = self.aladin.getOverlays();
let found = overlays.find(
(o) => o.type === "moc" && o.name === layer.name
);
return found !== undefined;
})(),
action: (e) => {
if (!loadMOCBtn.options.toggled) {
// load the moc
let moc = A.MOCFromURL(
layer.url + "/Moc.fits",
{ name: layer.name },
() => {
self.mocHiPSUrls[layer.url] = moc;
if (self.aladin.statusBar) {
self.aladin.statusBar.appendMessage({
message:
"Coverage of " +
layer.name +
" loaded",
duration: 2000,
type: "info",
});
}
loadMOCBtn.update({
toggled: true,
tooltip: {
content: "Remove coverage",
position: { direction: "top" },
},
});
}
);
self.aladin.addMOC(moc);
} else {
// unload the moc
let moc = self.mocHiPSUrls[layer.url];
self.aladin.removeLayer(moc);
delete self.mocHiPSUrls[layer.url];
if (self.aladin.statusBar) {
self.aladin.statusBar.appendMessage({
message:
"Coverage of " + layer.name + " removed",
duration: 2000,
type: "info",
});
}
loadMOCBtn.update({
toggled: false,
tooltip: {
content: "Add coverage",
position: { direction: "top" },
},
});
}
},
});
let loadMOCBtn = ActionButton.BUTTONS(self.aladin)
.addMOC({
name: hips.name,
url: hips.url + '/Moc.fits'
});
self.layer2swap = null;
let swapBtn = new ActionButton({
@@ -1127,9 +1095,9 @@ export class OverlayStackBox extends Box {
let toggled = swapBtn.options.toggled;
if (!toggled) {
if (!self.layer2swap) {
self.layer2swap = layer;
self.layer2swap = hips;
} else {
self.aladin.view.swapLayers(self.layer2swap.layer, layer.layer);
self.aladin.view.swapLayers(self.layer2swap.layer, hips.layer);
}
} else {
if (self.layer2swap) {
@@ -1145,20 +1113,17 @@ export class OverlayStackBox extends Box {
let btns = [showBtn, settingsBtn];
if (!(layer instanceof Image)) {
if (!(hips instanceof Image)) {
btns.push(loadMOCBtn);
}
btns = btns.concat([swapBtn, deleteBtn]);
let item = Layout.horizontal({
layout: [HiPSSelector, Layout.horizontal(btns)],
});
let item = Layout.horizontal([HiPSselect, Layout.horizontal(btns)]);
layout.push(item);
if (!(layer.layer in self.HiPSui)) {
self.HiPSui[layer.layer] = {
HiPSSelector,
if (!(hips.layer in self.ui)) {
self.ui[hips.layer] = {
HiPSSelector: HiPSselect,
settingsBox,
settingsBtn,
showBtn,
@@ -1224,7 +1189,5 @@ export class OverlayStackBox extends Box {
...options,
...{ position: this.position },
});
this.stackBtn.update({toggled: true});
}
}

View File

@@ -117,27 +117,12 @@ export class StatusBarBox extends Box {
this.el.title = task.message;
// create message div
let message = Layout.horizontal({
layout: task.message,
tooltip: {
content: task.message,
position: {
direction: "top",
},
hoverable: true,
delayShowUpTime: '500ms',
cssStyle: {
fontSize: 'x-small',
maxWidth: "200px",
"overflow-wrap": "break-word",
}
},
});
let message = Layout.horizontal(task.message);
message.addClass("aladin-status-bar-message")
this._show({
content: new Layout({layout: [StatusBarBox.icons[task.type], message], orientation: 'horizontal'}),
content: Layout.horizontal([StatusBarBox.icons[task.type], message]),
})
}

View File

@@ -49,7 +49,7 @@ export class ConeSearchActionButton extends ActionButton {
url: targetIconUrl
},
tooltip: options.tooltip,
disable: options.disable,
disabled: options.disabled,
cssStyle: {
backgroundPosition: 'center center',
cursor: 'pointer',

View File

@@ -28,10 +28,10 @@
*
*****************************************************************************/
import { ActionButton } from "../Widgets/ActionButton.js";
import { ContextMenu } from "../Widgets/ContextMenu.js";
import { WidgetTogglerButton } from "./Toggler.js";
/*
export class CtxMenuActionButtonOpener extends ActionButton {
// Constructor
constructor(options, aladin) {
let self;
@@ -48,35 +48,86 @@ export class CtxMenuActionButtonOpener extends ActionButton {
};
super({
...options,
cssStyle: {
backgroundPosition: 'center center',
cursor: 'pointer',
...options.cssStyle
},
action(e) {
enableTooltips()
let isHidden = self.ctxMenu.isHidden
let wasClosed = self.ctxMenu.isHidden;
self.close()
ContextMenu.hideAll();
if (self.ctxMenu.toggler === self && !wasClosed) {
return;
}
// If it was hidden then reopen it
if (isHidden) {
if (options.action) {
options.action(e)
}
self.open(e);
if (self.layout) {
self.ctxMenu.attach(self.layout)
}
// the panel is now open and we know the button has a tooltip
// => we close it!
if (self.tooltip && !self.ctxMenu.isHidden) {
self.tooltip.element().style.visibility = 'hidden'
self.tooltip.element().style.transitionDelay = '0ms';
self.ctxMenu.show({
position: {
nextTo: self,
direction: options.openDirection,
},
});
aladin.aladinDiv.addEventListener("click", enableTooltips)
}
},
...options,
})
self = this;
this.ctxMenu = aladin.contextMenu;
this.layout = options.ctxMenu;
}
close() {
this.closed = true;
this.ctxMenu._hide();
}
open(e) {
if (this.layout) {
this.ctxMenu.attach(this.layout, this)
}
this.ctxMenu.show({
position: {
nextTo: this,
direction: this.options.openDirection,
},
});
this.closed = false;
}
_hide() {
this.close();
super._hide();
}
}*/
export class CtxMenuActionButtonOpener extends WidgetTogglerButton {
// Constructor
constructor(options, aladin) {
let self;
const enableTooltips = () => {
aladin.aladinDiv.removeEventListener('click', enableTooltips);
aladin.aladinDiv.querySelectorAll('.aladin-tooltip')
// for each tooltips reset its visibility and transition delay
.forEach((t) => {
t.style.visibility = ''
t.style.transitionDelay = ''
})
};
super({
widget: aladin.contextMenu,
enable(e) {
enableTooltips()
// If it was hidden then reopen it
if (self.layout) {
self.ctxMenu.attach(self.layout, self)
}
// the panel is now open and we know the button has a tooltip
@@ -87,58 +138,22 @@ export class CtxMenuActionButtonOpener extends ActionButton {
aladin.aladinDiv.addEventListener("click", enableTooltips)
}
}
},
...options,
})
self = this;
let ctxMenu;
if (options.ctxMenu instanceof ContextMenu) {
ctxMenu = options.ctxMenu;
} else {
this.layout = options.ctxMenu;
ctxMenu = new ContextMenu(aladin, {hideOnClick: true, hideOnResize: true})
}
self.ctxMenu = ctxMenu;
}
hideMenu() {
this.ctxMenu._hide();
}
_hide() {
this.hideMenu();
super._hide();
this.ctxMenu = aladin.contextMenu;
this.layout = options.ctxMenu;
}
update(options) {
if(options.ctxMenu) {
if (options.ctxMenu instanceof ContextMenu) {
this.ctxMenu = options.ctxMenu
} else {
this.layout = options.ctxMenu;
}
}
if (!this.ctxMenu) {
this.ctxMenu = new ContextMenu(aladin, {hideOnClick: true, hideOnResize: true})
if (options && options.ctxMenu) {
this.layout = options.ctxMenu;
//this.ctxMenu.attach(this.layout, this)
}
super.update(options)
if (!this.ctxMenu.isHidden) {
if (this.layout) {
this.ctxMenu.attach(this.layout)
}
this.ctxMenu.show({
position: {
nextTo: this,
// it case it is not given then it will be computed by default
direction: options.openDirection,
},
});
}
}
}

View File

@@ -44,25 +44,28 @@ import { ALEvent } from "../../events/ALEvent";
constructor(aladin, options) {
options = options || {};
options.verbosity = (options && options.verbosity) || 'full';
let projectionName = aladin.getProjectionName();
let self;
let ctxMenu = _buildLayout(aladin);
super({
icon: {
monochrome: true,
size: 'medium',
url: projectionIconUrl,
},
openDirection: 'left',
classList: ['aladin-projection-control'],
//content: [options.verbosity === 'full' ? ProjectionEnum[projectionName].label : projectionName],
content: projectionName,
tooltip: {content: 'Change the view projection', position: {direction: 'bottom left'}},
ctxMenu,
...options
}, aladin);
self = this;
this.aladin = aladin;
let ctxMenu = this._buildLayout();
this.update({ctxMenu})
this._addEventListeners()
}
@@ -77,41 +80,21 @@ import { ALEvent } from "../../events/ALEvent";
self.update({content})
});
}
_buildLayout() {
let aladin = this.aladin;
let layout = [];
let self = this;
let aladinProj = aladin.getProjectionName();
for (const key in ProjectionEnum) {
let proj = ProjectionEnum[key];
layout.push({
label: proj.label,
selected: aladinProj === key,
action(o) {
aladin.setProjection(key)
let ctxMenu = self._buildLayout(aladin);
//self.update({ctxMenu, content: self.options.verbosity === 'full' ? proj.label : key});
self.update({ctxMenu});
}
})
}
return layout;
}
update(options) {
super.update(options);
/*if (options.verbosity) {
let ctxMenu = this._buildLayout();
let projName = this.aladin.getProjectionName();
let label = options.verbosity === 'full' ? ProjectionEnum[projName].label : projName;
super.update({ctxMenu, content: label});
}*/
}
}
function _buildLayout(aladin) {
let layout = [];
for (const key in ProjectionEnum) {
let proj = ProjectionEnum[key];
layout.push({
label: proj.label,
action(o) {
aladin.setProjection(key)
}
})
}
return layout;
}

View File

@@ -44,7 +44,7 @@ import waveOffIconUrl from '../../../../assets/icons/wave-off.svg';
url: waveOffIconUrl
},
tooltip: {content: 'SAMP disabled in Aladin Lite options', position: {direction: 'top'}},
disable: true,
disabled: true,
}
} else {
//let isHubRunning = aladin.samp.isHubCurrentlyRunning();
@@ -92,23 +92,6 @@ import waveOffIconUrl from '../../../../assets/icons/wave-off.svg';
}
self.update({icon})
});
/*ALEvent.SAMP_HUB_RUNNING.listenedBy(aladin.aladinDiv, function (e) {
const isHubRunning = e.detail.isHubRunning;
if (hubRunning !== isHubRunning) {
let newOptions = {
disable: !isHubRunning,
tooltip: isHubRunning ? {content: 'Connect to SAMP hub'} : {content: 'No hub running found'}
};
self.update(newOptions)
if (isHubRunning === false) {
self.update({iconURL: waveOffIconUrl})
}
hubRunning = isHubRunning;
}
});*/
}
static sendSources(aladin) {

View File

@@ -17,9 +17,22 @@
// along with Aladin Lite.
//
import { Layout } from "../Layout.js";
import { Input } from "../Widgets/Input.js";
import { Color } from "../../Color.js";
import { ALEvent } from "../../events/ALEvent.js";
import { SAMPActionButton } from "../Button/SAMP.js";
import helpIconBtn from '../../../../assets/icons/help.svg';
import { Utils } from "../../Utils";
import { GridSettingsCtxMenu } from "./../CtxMenu/GridSettings.js";
import { CtxMenuActionButtonOpener } from "./CtxMenuOpener";
import settingsIconUrl from './../../../../assets/icons/settings.svg';
import { SettingsCtxMenu } from "../CtxMenu/Settings";
import { SimbadPointer } from "./SimbadPointer.js";
import { GridEnabler } from "./GridEnabler.js";
import { Stack } from "./Stack.js";
import { ColorPicker } from "./ColorPicker.js";
import { ShareActionButton } from "./ShareView.js";
/******************************************************************************
* Aladin Lite project
*
@@ -54,8 +67,307 @@ import { SettingsCtxMenu } from "../CtxMenu/Settings";
direction: 'right'
}
},
ctxMenu: new SettingsCtxMenu(aladin, options),
ctxMenu: undefined,
...options
}, aladin);
this.aladin = aladin;
let ctxMenu = this._buildLayout()
this.update({ctxMenu})
}
_buildLayout() {
let self = this;
let aladin = this.aladin;
let backgroundColorInput = Input.color({
name: 'color',
value: (() => {
let {r, g, b} = aladin.getBackgroundColor();
return Color.rgbToHex(r, g, b);
})(),
change(e) {
let hex = e.target.value;
aladin.setBackgroundColor(hex)
}
});
let reticleColorInput = Input.color({
value: new Color(aladin.getReticle().getColor()).toHex(),
name: 'reticleColor',
change(e) {
let hex = e.target.value;
aladin.setDefaultColor(hex)
}
});
// Event received from aladin
ALEvent.BACKGROUND_COLOR_CHANGED.listenedBy(aladin.aladinDiv, function (e) {
const {r, g, b} = e.detail.color;
let hex = Color.rgbToHex(r, g, b);
backgroundColorInput.set(hex)
});
ALEvent.RETICLE_CHANGED.listenedBy(aladin.aladinDiv, function (e) {
const color = e.detail.color;
let hex = new Color(color).toHex();
reticleColorInput.set(hex)
});
const toggleCheckbox = (checkbox) => {
const pastVal = checkbox.get();
const curVal = !pastVal;
checkbox.set(curVal)
return curVal;
};
let hpxGridCheckbox = Input.checkbox({
name: 'hpxgrid', checked: aladin.healpixGrid(),
click(e) {
let newVal = toggleCheckbox(hpxGridCheckbox);
aladin.showHealpixGrid(newVal)
}
})
let reticleCheckbox = Input.checkbox({
name: 'reticle',
checked: aladin.isReticleDisplayed(),
click(e) {
let newVal = toggleCheckbox(reticleCheckbox);
aladin.showReticle(newVal)
}
})
let reticle = aladin.getReticle();
let sliderReticleSize = Input.slider({
name: 'reticleSize',
type: 'range',
min: 0.0,
max: 50,
value: reticle.getSize(),
change(e) {
reticle.update({size: e.target.value})
}
});
let sampBtn = new SAMPActionButton({
size: 'small',
action(conn) {
if (conn.isConnected()) {
conn.unregister();
} else {
conn.register();
}
//self._hide()
}
}, aladin);
return [
GridSettingsCtxMenu.getLayout(aladin),
{
label: {
content: ['Reticle']
},
subMenu: [
{
label: {
content: [reticleCheckbox, 'Show/Hide']
},
mustHide: false,
action(o) {
let newVal = toggleCheckbox(reticleCheckbox);
aladin.showReticle(newVal)
}
},
{
label: {
content: [reticleColorInput, 'Color']
},
},
{
label: Layout.horizontal(['Size', sliderReticleSize]),
}
]
},
{
label: {
content: [backgroundColorInput, 'Back color']
},
mustHide: false,
},
{
label: {
content: 'Light/Dark mode'
},
mustHide: false,
action(o) {
const currentTheme = aladin.aladinDiv.getAttribute("data-theme");
const newTheme = currentTheme === "dark" ? "light" : "dark";
aladin._applyTheme(newTheme);
localStorage.setItem("theme", newTheme);
}
},
{
label: {
content: [hpxGridCheckbox, 'HEALPix grid']
},
mustHide: false,
action(o) {
let newVal = toggleCheckbox(hpxGridCheckbox);
aladin.showHealpixGrid(newVal)
}
},
{
label: {
content: [sampBtn, 'SAMP']
},
},
{
label: 'Tools',
subMenu: [
{
label: 'Stack',
mustHide: false,
action: (o) => {
let toolbar = aladin.toolbar;
if (!toolbar.has('stack')) {
toolbar.add('stack', new Stack(aladin));
} else {
if (toolbar.enabled('stack')) {
toolbar.disable('stack')
} else {
toolbar.enable('stack')
}
}
}
},
{
label: 'Simbad',
mustHide: false,
action: (o) => {
let toolbar = aladin.toolbar;
if (!toolbar.has('simbad')) {
toolbar.add('simbad', new SimbadPointer(aladin));
} else {
if (toolbar.enabled('simbad')) {
toolbar.disable('simbad')
} else {
toolbar.enable('simbad')
}
}
}
},
{
label: 'Grid',
mustHide: false,
action: (o) => {
let toolbar = aladin.toolbar;
if (!toolbar.has('grid')) {
toolbar.add('grid', new GridEnabler(aladin));
} else {
if (toolbar.enabled('grid')) {
toolbar.disable('grid')
} else {
toolbar.enable('grid')
}
}
}
},
{
label: 'Color picker',
mustHide: false,
action: (o) => {
let toolbar = aladin.toolbar;
if (!toolbar.has('picker')) {
toolbar.add('picker', new ColorPicker(aladin));
} else {
if (toolbar.enabled('picker')) {
toolbar.disable('picker')
} else {
toolbar.enable('picker')
}
}
}
},
{
label: 'Share view',
mustHide: false,
action: (o) => {
let toolbar = aladin.toolbar;
if (!toolbar.has('share')) {
toolbar.add('share', new ShareActionButton(aladin));
} else {
if (toolbar.enabled('share')) {
toolbar.disable('share')
} else {
toolbar.enable('share')
}
}
}
},
]
},
{
label: {
icon: {
monochrome: true,
tooltip: {content: 'Documentation about Aladin Lite', position: {direction: 'top'}},
url: helpIconBtn,
size: 'small',
cssStyle: {
cursor: 'help',
}
},
content: 'Help'
},
subMenu: [
{
label: 'Aladin Lite API',
action(o) {
Utils.openNewTab('https://aladin.cds.unistra.fr/AladinLite/doc/API/')
}
},
{
label: {
content: 'Contact us',
tooltip: { content: 'For bug reports, discussions, feature ideas...', position: {direction: 'bottom'} }
},
subMenu: [
{
label: 'GitHub',
action(o) {
Utils.openNewTab('https://github.com/cds-astro/aladin-lite/issues')
}
},
{
label: 'by email',
action(o) {
Utils.openNewTab('mailto:matthieu.baumann@astro.unistra.fr,thomas.boch@astro.unistra.fr?subject=Aladin Lite issue&body=message%20goes%20here')
}
}
],
},
{
label: 'General documentation',
action(o) {
Utils.openNewTab('https://aladin.cds.unistra.fr/AladinLite/doc/')
}
},
{
label: Layout.horizontal('Examples', { tooltip: { content: 'How to embed Aladin Lite <br \>into your own webpages!', position: {direction: 'bottom'}}}),
action(o) {
Utils.openNewTab('https://aladin.cds.unistra.fr/AladinLite/doc/API/examples/')
}
}
]
}
]
}
}

View File

@@ -42,6 +42,6 @@ export class SnapshotActionButton extends ActionButton {
}
})
this.addClass('medium-sized-icon')
this.addClass('aladin-medium-sized-icon')
}
}

View File

@@ -17,11 +17,9 @@
// along with Aladin Lite.
//
import { CtxMenuActionButtonOpener } from "./CtxMenuOpener";
import stackOverlayIconUrl from './../../../../assets/icons/stack.svg';
import { OverlayStackBox } from "../Box/StackBox";
import { ActionButton } from "./../Widgets/ActionButton";
import { WidgetTogglerButton } from "./Toggler";
import stackOverlayIconUrl from "./../../../../assets/icons/stack.svg";
/******************************************************************************
* Aladin Lite project
*
@@ -34,18 +32,18 @@ import { ActionButton } from "./../Widgets/ActionButton";
*
*****************************************************************************/
/**
* Class representing a Tabs layout
* @extends CtxMenuActionButtonOpener
* Class representing the stack
* @extends WidgetTogglerButton
*/
export class OverlayStackButton extends ActionButton {
export class Stack extends WidgetTogglerButton {
/**
* UI responsible for displaying the viewport infos
* @param {Aladin} aladin - The aladin instance.
*/
constructor(aladin, options) {
let self;
let stack;
super({
openDirection: (options && options.openDirection) || 'right',
widget: new OverlayStackBox(aladin),
icon: {
size: 'medium',
monochrome: true,
@@ -55,25 +53,9 @@ import { ActionButton } from "./../Widgets/ActionButton";
tooltip: {
content: 'Open the overlays menu',
position: {
direction: 'top right'
direction: (options && options.openDirection) || 'left'
}
},
toggled: false,
action(e) {
if (stack.isHidden) {
stack._show({
position: {
nextTo: self,
direction: 'right'
}
})
} else {
stack._hide()
}
},
...options
});
self = this;
stack = new OverlayStackBox(aladin, self);
}
}

View File

@@ -42,9 +42,8 @@ export class TogglerActionButton extends ActionButton {
super({
...options,
toggled,
action(o) {
options.action && options.action(o);
self.toggle(o);
action: (o) => {
self.toggle(o)
}
})
this.toggled = toggled;
@@ -67,15 +66,89 @@ export class TogglerActionButton extends ActionButton {
toggle(o) {
this.toggled = !this.toggled;
if (this.toggled && this.options.actionOn) {
this.options.actionOn(o)
if (this.toggled && this.options.on) {
this.options.on(o)
}
if (!this.toggled && this.options.actionOff) {
this.options.actionOff(o)
if (!this.toggled && this.options.off) {
this.options.off(o)
}
// once the actions has been executed, modify the styling
this.update({toggled: this.toggled, tooltip: this.toggled ? this.options.tooltipOn : this.options.tooltipOff})
this.update({toggled: this.toggled})
}
}
// It may happen that the widget closes and so the toggler
// has to be notified. For example when the user clicks on a Box that
// is attached to a toggler.
notify(state) {
if (this.toggled === state)
return;
this.toggled = state;
this.update({toggled: this.toggled})
}
}
/**
* Class representing a Tabs layout
* @extends TogglerActionButton
*/
export class WidgetTogglerButton extends TogglerActionButton {
/**
* UI responsible for displaying the viewport infos
* @param {Aladin} aladin - The aladin instance.
*/
constructor(options) {
let self;
let widget = options && options.widget;
let enable = options && options.enable;
super({
toggled: false,
on: (o) => {
if (enable)
enable(o)
widget._show({
position: self.position
})
},
off: (_) => {
self.close();
},
...options
});
self = this;
this.update(options)
widget.setToggler(this);
this.widget = widget;
}
close() {
this.widget._hide();
super.close()
}
update(options) {
this.openDirection = (options && options.openDirection) || this.openDirection;
this.openPosition = (options && options.openPosition) || this.openPosition;
if (this.openPosition) {
this.position = {
anchor: this.openPosition,
}
} else {
this.position = {
direction: this.openDirection,
nextTo: this
}
}
super.update(options)
}
}

View File

@@ -1,58 +0,0 @@
// Copyright 2013 - UDS/CNRS
// The Aladin Lite program is distributed under the terms
// of the GNU General Public License version 3.
//
// This file is part of Aladin Lite.
//
// Aladin Lite is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, version 3 of the License.
//
// Aladin Lite is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// The GNU General Public License is available in COPYING file
// along with Aladin Lite.
//
/******************************************************************************
* Aladin Lite project
*
* File gui/Stack/Menu.js
*
*
* Author: Matthieu Baumann [CDS, matthieu.baumann@astro.unistra.fr]
*
*****************************************************************************/
import { ActionButton } from "../Widgets/ActionButton.js";
/*
options = {
action: (connector) => {
}
tooltip
}
*/
export class WidgetOpenerActionButton extends ActionButton {
// Constructor
constructor(options) {
let widget = options.widget;
super({
...options,
action(e) {
if (widget.isHidden) {
widget._show();
} else {
widget._hide();
}
}
})
this.addClass('medium-sized-icon')
}
}

View File

@@ -30,9 +30,7 @@
import { ALEvent } from "../../events/ALEvent.js";
import { Input } from "../Widgets/Input.js";
import { ActionButton } from "../Widgets/ActionButton.js";
import { Color } from "../../Color.js";
import thicknessLineIcon from './../../../../assets/icons/thickness.svg';
export let GridSettingsCtxMenu = (function () {
@@ -77,55 +75,6 @@ export let GridSettingsCtxMenu = (function () {
}
});
const thicknessLineBtn = ActionButton.createSmallSizedIconBtn({
icon: {
url: thicknessLineIcon,
monochrome: true,
},
tooltip: {content: 'Grid line thickness', position: {direction: 'left'}},
cssStyle: {
backgroundColor: '#bababa',
borderColor: '#484848',
cursor: 'pointer',
width: '20px',
height: '20px',
padding: '0',
},
action(e) {
let ctxMenu = ContextMenu.getInstance(aladin);
ctxMenu._hide();
let ctxMenuLayout = [];
for (let thickness = 1; thickness <= 5; thickness++) {
ctxMenuLayout.push({
label: thickness + 'px',
action(o) {
aladin.setCooGrid({thickness: thickness})
}
})
}
ctxMenu.attach(ctxMenuLayout);
ctxMenu.show({
e: e,
position: {
nextTo: thicknessLineBtn,
direction: 'bottom',
}
})
}
});
let enableCheckbox = Input.checkbox({
name: 'enableGrid',
tooltip: {content: 'Enable/disable the grid', position: {direction: 'left'}},
type: 'checkbox',
checked: aladin.getGridOptions().enabled,
click(e) {
aladin.setCooGrid({enabled: enableCheckbox.get()})
}
});
ALEvent.COO_GRID_UPDATED.listenedBy(aladin.aladinDiv, function (e) {
let color = e.detail.color;

View File

@@ -1,309 +0,0 @@
// Copyright 2013 - UDS/CNRS
// The Aladin Lite program is distributed under the terms
// of the GNU General Public License version 3.
//
// This file is part of Aladin Lite.
//
// Aladin Lite is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, version 3 of the License.
//
// Aladin Lite is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// The GNU General Public License is available in COPYING file
// along with Aladin Lite.
//
/******************************************************************************
* Aladin Lite project
*
* File gui/Stack/Menu.js
*
*
* Author: Matthieu Baumann [CDS, matthieu.baumann@astro.unistra.fr]
*
*****************************************************************************/
import { Layout } from "../Layout.js";
import { ContextMenu } from "../Widgets/ContextMenu.js";
import { Input } from "../Widgets/Input.js";
import { Color } from "../../Color.js";
import { ALEvent } from "../../events/ALEvent.js";
import { SAMPActionButton } from "../Button/SAMP.js";
import helpIconBtn from '../../../../assets/icons/help.svg';
import { Utils } from "../../Utils";
import { GridSettingsCtxMenu } from "./GridSettings.js";
export class SettingsCtxMenu extends ContextMenu {
// Constructor
constructor(aladin, options) {
super(aladin, {hideOnClick: true});
let self = this;
self.backgroundColorInput = Input.color({
name: 'color',
value: (() => {
let {r, g, b} = aladin.getBackgroundColor();
return Color.rgbToHex(r, g, b);
})(),
change(e) {
let hex = e.target.value;
aladin.setBackgroundColor(hex)
}
});
let reticleColor = new Color(aladin.getReticle().getColor())
self.reticleColorInput = Input.color({
value: reticleColor.toHex(),
name: 'reticleColor',
change(e) {
let hex = e.target.value;
aladin.setDefaultColor(hex)
}
});
// Event received from aladin
ALEvent.BACKGROUND_COLOR_CHANGED.listenedBy(aladin.aladinDiv, function (e) {
const {r, g, b} = e.detail.color;
let hex = Color.rgbToHex(r, g, b);
self.backgroundColorInput.set(hex)
});
ALEvent.RETICLE_CHANGED.listenedBy(aladin.aladinDiv, function (e) {
const color = e.detail.color;
let hex = new Color(color).toHex();
self.reticleColorInput.set(hex)
});
this.toggleCheckbox = (checkbox) => {
const pastVal = checkbox.get();
const curVal = !pastVal;
checkbox.set(curVal)
return curVal;
};
self.hpxGridCheckbox = Input.checkbox({
name: 'hpxgrid', checked: this.aladin.healpixGrid(),
click(e) {
let newVal = self.toggleCheckbox(self.hpxGridCheckbox);
self.aladin.showHealpixGrid(newVal)
}
})
self.reticleCheckbox = Input.checkbox({
name: 'reticle',
checked: this.aladin.isReticleDisplayed(),
click(e) {
let newVal = self.toggleCheckbox(self.reticleCheckbox);
self.aladin.showReticle(newVal)
}
})
this.features = options && options.features;
let sampBtn = new SAMPActionButton({
size: 'small',
action(conn) {
if (conn.isConnected()) {
conn.unregister();
} else {
conn.register();
}
self._hide()
}
}, aladin);
this.sampBtn = sampBtn;
this.attach();
}
attach() {
let self = this;
const toggleFeature = (name) => {
let feature = self.features[name];
if(feature.isHidden) {
feature._show();
} else {
feature._hide();
}
}
let reticle = self.aladin.getReticle();
let sliderReticleSize = Input.slider({
name: 'reticleSize',
type: 'range',
min: 0.0,
max: 50,
value: reticle.getSize(),
change(e) {
reticle.update({size: e.target.value})
}
});
let options = [
//ProjectionCtxMenu.getLayout(self.aladin),
GridSettingsCtxMenu.getLayout(self.aladin),
{
label: {
content: ['Reticle']
},
subMenu: [
{
label: {
content: [self.reticleCheckbox, 'Show/Hide']
},
mustHide: false,
action(o) {
let newVal = self.toggleCheckbox(self.reticleCheckbox);
self.aladin.showReticle(newVal)
self.attach();
}
},
{
label: {
content: [self.reticleColorInput, 'Color']
},
},
{
label: Layout.horizontal(['Size', sliderReticleSize]),
}
]
},
{
label: {
content: [self.backgroundColorInput, 'Back color']
},
},
{
label: {
content: 'Light/Dark mode'
},
action(o) {
const currentTheme = self.aladin.aladinDiv.getAttribute("data-theme");
const newTheme = currentTheme === "dark" ? "light" : "dark";
self.aladin.aladinDiv.setAttribute("data-theme", newTheme);
localStorage.setItem("theme", newTheme);
}
},
{
label: {
content: [self.hpxGridCheckbox, 'HEALPix grid']
},
mustHide: false,
action(o) {
let newVal = self.toggleCheckbox(self.hpxGridCheckbox);
self.aladin.showHealpixGrid(newVal)
self.attach();
}
},
{
label: {
content: [self.sampBtn, 'SAMP']
},
},
{
label: 'Tools',
subMenu: [
{
label: 'Stack',
selected: !self.features['stack'].isHidden,
action(o) {
toggleFeature('stack')
}
},
{
label: 'Simbad',
selected: !self.features['simbad'].isHidden,
action(o) {
toggleFeature('simbad');
}
},
{
label: 'Grid',
selected: !self.features['grid'].isHidden,
action(o) {
toggleFeature('grid');
}
}
]
},
{
label: {
icon: {
monochrome: true,
tooltip: {content: 'Documentation about Aladin Lite', position: {direction: 'top'}},
url: helpIconBtn,
size: 'small',
cssStyle: {
cursor: 'help',
}
},
content: 'Help'
},
subMenu: [
{
label: 'Aladin Lite API',
action(o) {
Utils.openNewTab('https://aladin.cds.unistra.fr/AladinLite/doc/API/')
}
},
{
label: {
content: 'Contact us',
tooltip: { content: 'For bug reports, discussions, feature ideas...', position: {direction: 'bottom'} }
},
subMenu: [
{
label: 'GitHub',
action(o) {
Utils.openNewTab('https://github.com/cds-astro/aladin-lite/issues')
}
},
{
label: 'by email',
action(o) {
Utils.openNewTab('mailto:matthieu.baumann@astro.unistra.fr,thomas.boch@astro.unistra.fr?subject=Aladin Lite issue&body=message%20goes%20here')
}
}
],
},
{
label: 'General documentation',
action(o) {
Utils.openNewTab('https://aladin.cds.unistra.fr/AladinLite/doc/')
}
},
{
label: Layout.horizontal({layout: ['Examples'], tooltip: { content: 'How to embed Aladin Lite <br \>into your own webpages!', position: {direction: 'bottom'}}}),
action(o) {
Utils.openNewTab('https://aladin.cds.unistra.fr/AladinLite/doc/API/examples/')
}
}
]
}
]
super.attach(options);
}
show(options) {
this.attach();
this.position = (options && options.position) || this.position || { anchor: 'center center'};
super.show({
position: this.position,
})
}
}

View File

@@ -83,10 +83,9 @@ export class FoV extends DOMElement {
'<div class="aladin-monospace-text"></div>'])
}
let el = Layout.horizontal({layout});
let el = Layout.horizontal(layout);
if (el) {
el.addClass('aladin-fov');
el.addClass('aladin-dark-theme')
}
super(el)

View File

@@ -58,7 +58,7 @@
...options
})
this.addClass('medium-sized');
this.addClass('aladin-medium-sized');
self = this;

View File

@@ -67,7 +67,6 @@ import infoIconUrl from '../../../../assets/icons/info.svg';
import { Input } from "../Widgets/Input.js";
export class Dropdown extends Input {
// constructor
constructor(aladin, options) {
let self;
@@ -75,33 +74,70 @@ export class Dropdown extends Input {
self.el.blur();
});
options.options = options.options || [];
options.autocomplete = {options: options.options || []};
delete options.options;
super({
type: 'text',
//autocomplete: {options: options.options},
actions: {
focus(_) {
self.removeClass('aladin-valid')
self.removeClass('aladin-not-valid')
},
dblclick(e) {
self.set('')
if (self.options.input) {
self.options.input(e)
}
},
input(e) {
self.removeClass('aladin-valid')
self.removeClass('aladin-not-valid')
if (e.data === undefined) {
// select
self.options.action(e)
let value = e.target.value;
self.set('');
if (self.options.input) {
self.options.input(e)
}
self.set(value)
} else {
if (self.options.input) {
self.options.input(e)
}
}
},
keydown(e) {
if (!e.key) {
return;
}
e.stopPropagation();
// ignore navigation keys
if (e.key === 'Enter') {
self.options.action(e)
}
},
},
...options
})
this.el.classList.add('search')
self = this;
this._addListeners(aladin);
}
update(options) {
let newOptions = {};
/*if (options && options.options) {
newOptions['autocomplete'] = {options: options.options};
if (options.options) {
options.autocomplete = {options: options.options || []};
delete options.options;
}*/
}
// add the other input text options
newOptions = {...newOptions, ...options};
super.update(newOptions)
}
_addListeners(aladin) {
super.update(options)
}
};

View File

@@ -0,0 +1,148 @@
// Copyright 2013 - UDS/CNRS
// The Aladin Lite program is distributed under the terms
// of the GNU General Public License version 3.
//
// This file is part of Aladin Lite.
//
// Aladin Lite is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, version 3 of the License.
//
// Aladin Lite is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// The GNU General Public License is available in COPYING file
// along with Aladin Lite.
//
/******************************************************************************
* Aladin Lite project
*
* File gui/Input/HiPSSelector.js
*
*
* Author: Matthieu Baumann[CDS]
*
*****************************************************************************/
import { ALEvent } from "../../events/ALEvent.js";
import { Input } from "../Widgets/Input.js";
export class HiPSSelector extends Input {
static cachedHiPS = {}
static objects = [];
// constructor
constructor(options) {
let surveys = [];
for (var survey of Object.values(HiPSSelector.cachedHiPS)) {
surveys.push({value: survey.id, label: survey.name});
}
surveys.sort((s1, s2) => {
const s1l = s1.label.toLowerCase()
const s2l = s2.label.toLowerCase()
if (s1l < s2l) {
return -1;
}
if (s1l > s2l) {
return 1;
}
return 0;
});
let current = {};
if (options.layer) {
let id = options.layer.id;
let name = options.layer.name;
current["value"] = id;
current["label"] = name;
if (!surveys.some(item => item.value === id)) {
surveys.push(current)
}
} else {
current = surveys[0];
}
current["title"] = current["label"];
surveys.push("More...")
super({
type: "select",
options: surveys,
...current,
...options
})
self = this;
HiPSSelector.objects.push(self);
}
};
(function () {
ALEvent.FAVORITE_HIPS_LIST_UPDATED.listenedBy(document.body, (event) => {
let favoritesHips = event.detail;
HiPSSelector.cachedHiPS = {};
for (var hips of favoritesHips) {
let key = hips.id || hips.url || hips.name;
HiPSSelector.cachedHiPS[key] = hips;
}
// Update the options of the selector
let favoritesHiPS = []
for(var hips of Object.values(HiPSSelector.cachedHiPS)) {
favoritesHiPS.push({
value: hips.id,
label: hips.name
})
}
favoritesHiPS.sort((s1, s2) => {
const s1l = s1.label.toLowerCase()
const s2l = s2.label.toLowerCase()
if (s1l < s2l) {
return -1;
}
if (s1l > s2l) {
return 1;
}
return 0;
});
for (var selector of HiPSSelector.objects) {
// refers to an HiPS image survey
let currentFavoriteHiPS = {
value: selector.options.value,
label: selector.options.label,
};
let favoritesHiPSCopy = [...favoritesHiPS];
// Add the current hips to the selector as well, even if it has been manually
// removed from the HiPSList
if (!favoritesHiPSCopy.some(item => item.value === currentFavoriteHiPS.value)) {
favoritesHiPSCopy.push(currentFavoriteHiPS)
}
favoritesHiPSCopy.push("More...")
currentFavoriteHiPS["title"] = currentFavoriteHiPS["label"];
selector.update({
...currentFavoriteHiPS,
options: favoritesHiPSCopy
});
}
});
})();

View File

@@ -18,6 +18,7 @@
//
import { DOMElement } from "./Widgets/Widget";
import { Tooltip } from "./Widgets/Tooltip";
import { isJSObject } from "./Utils";
/******************************************************************************
* Aladin Lite project
@@ -34,23 +35,21 @@ import { Tooltip } from "./Widgets/Tooltip";
export class Layout extends DOMElement {
/**
* Create a layout
* @param {layout: Array.<DOMElement | String>, cssStyle: Object} options - Represents the structure of the Tabs
* @param {layout: Array.<DOMElement | String>} layout - Represents the structure of the Tabs
* @param {Object} options - Options object
* @param {DOMElement} target - The parent element.
* @param {String} position - The position of the tabs layout relative to the target.
* For the list of possibilities, see https://developer.mozilla.org/en-US/docs/Web/API/Element/insertAdjacentHTML
*/
constructor(options = {layout: []}, target, position = "beforeend") {
constructor(layout, options, target, position = "beforeend") {
let el = document.createElement('div');
// The user should also be able to give just a list of DOMElement
if (options instanceof Array) {
options['layout'] = options;
}
options.layout = options.layout || [];
layout = layout || [];
super(el, options);
if (options.cssStyle) {
this.layout = layout;
if (options && options.cssStyle) {
this.setCss(options.cssStyle);
}
@@ -58,17 +57,35 @@ export class Layout extends DOMElement {
this.attachTo(target, position);
// 2. Once self is attached, attach the children
if (options.layout) {
if (typeof options.layout === 'string' || options.layout instanceof String) {
this.el.innerHTML = options.layout;
} else {
if (typeof layout === 'string' || layout instanceof String) {
this.el.innerHTML = layout;
// otherwise it is an object
} else if (
isJSObject(layout)
) {
if (layout.start) {
this.appendContent(new Layout(layout.start));
}
if (layout.end) {
this.appendContent(new Layout(layout.end));
}
this.el.style.justifyContent = "space-between";
} else if (Array.isArray(layout)) {
// treat it as an array
for (const item of options.layout) {
for (let item of layout) {
if (Array.isArray(item) || isJSObject(item)) {
item = new Layout(item)
}
this.appendContent(item)
}
} else {
const item = layout;
this.appendContent(item)
}
if (options.draggable) {
if (options && options.draggable) {
// retrieve the children and add the drag listeners
let draggableFn = options.draggable;
let firstSelected = null;
@@ -108,43 +125,44 @@ export class Layout extends DOMElement {
});
});
}
}
// The tooltip has to be set once the element
// lies in the DOM
if (options.tooltip) {
if (options && options.tooltip) {
Tooltip.add(options.tooltip, this)
}
if (options.position) {
if (options && options.position) {
this.setPosition(options.position)
}
if (options.orientation) {
if (options.orientation === 'horizontal') {
this.addClass('aladin-horizontal-list')
} else {
this.addClass('aladin-vertical-list')
}
if (options && options.vertical && options.vertical === true) {
this.addClass('aladin-vertical-list')
} else {
this.addClass('aladin-horizontal-list')
}
if (options.classList) {
if (options && options.classList) {
this.addClass(options.classList)
}
}
static horizontal(options, target, position = "beforeend") {
let layout = new Layout(options, target, position);
layout.addClass('aladin-horizontal-list');
return layout;
static horizontal(layout, options, target, position = "beforeend") {
return new Layout(layout, options, target, position);
}
static vertical(options, target, position = "beforeend") {
let layout = new Layout(options, target, position);
layout.addClass('aladin-vertical-list');
static nested(layout, options, target, position = "beforeend") {
let horizontalLayout = new Layout(layout, options, target, position);
horizontalLayout.removeClass('aladin-horizontal-list');
return layout;
return horizontalLayout;
}
static vertical(layout, options, target, position = "beforeend") {
let verticalLayout = new Layout(layout, {...options, vertical: true}, target, position);
verticalLayout.addClass('aladin-vertical-list');
return verticalLayout;
}
/**
@@ -160,7 +178,7 @@ export class Layout extends DOMElement {
* @param {DOMElement} item - Represents the structure of the Tabs
*/
removeItem(item) {
let arr = this.options.layout;
let arr = this.layout;
var index = arr.indexOf(item);
if (index > -1) {
@@ -175,7 +193,7 @@ export class Layout extends DOMElement {
* @param {DOMElement} item - Represents the structure of the Tabs
*/
appendLast(item) {
this.insertItemAtIndex(item, this.options.layout.length);
this.insertItemAtIndex(item, this.layout.length);
}
/**
@@ -185,7 +203,7 @@ export class Layout extends DOMElement {
* For the list of possibilities, see https://developer.mozilla.org/en-US/docs/Web/API/Element/insertAdjacentHTML
*/
insertItemAtIndex(item, index) {
this.options.layout.splice(index, 0, item);
this.layout.splice(index, 0, item);
this._show();
}
@@ -195,11 +213,7 @@ export class Layout extends DOMElement {
*/
empty() {
// remove all the sub elements
/*for (let elmt of this.options.layout) {
elmt.remove();
}*/
this.options.layout = [];
this.layout = [];
this._show();
}
@@ -208,19 +222,19 @@ export class Layout extends DOMElement {
this.el.innerHTML = "";
// apply css
if (this.options.cssStyle) {
if (this.options && this.options.cssStyle) {
this.setCss(this.options.cssStyle);
}
if (this.options.layout) {
for (const item of this.options.layout) {
if (this.layout) {
for (const item of this.layout) {
if (item) {
this.appendContent(item)
}
}
}
if (this.options.position) {
if (this.options && this.options.position) {
this.setPosition(this.options.position)
}
}

View File

@@ -78,6 +78,9 @@ export class Location extends DOMElement {
autocomplete: 'off',
autofocus: true,
actions: {
dblclick: (_) => {
field.set('')
},
focus: (e) => {
focused = true;
},
@@ -91,8 +94,6 @@ export class Location extends DOMElement {
field.removeClass('aladin-valid'); // remove red border
if (e.key === 'Enter') {
//field.el.blur();
let object = field.get();
field.update({placeholder: 'Resolving ' + object + '...'})
@@ -120,7 +121,7 @@ export class Location extends DOMElement {
value: parseCoo(),
});
field.addClass("medium-sized")
field.addClass("aladin-medium-sized")
let copyBtn = new ActionButton({
icon: {
@@ -135,12 +136,10 @@ export class Location extends DOMElement {
})
copyBtn.el.classList.add("aladin-location-copy");
let el = Layout.horizontal({
layout: [
copyBtn,
field
]
})
let el = Layout.horizontal([
copyBtn,
field
])
el.addClass('aladin-location');
super(el)

166
src/js/gui/Toolbar.js Normal file
View File

@@ -0,0 +1,166 @@
// Copyright 2023 - UDS/CNRS
// The Aladin Lite program is distributed under the terms
// of the GNU General Public License version 3.
//
// This file is part of Aladin Lite.
//
// Aladin Lite is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, version 3 of the License.
//
// Aladin Lite is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// The GNU General Public License is available in COPYING file
// along with Aladin Lite.
//
import { Layout } from "./Layout";
import { ActionButton } from "./Widgets/ActionButton";
import { DOMElement } from "./Widgets/Widget";
/******************************************************************************
* Aladin Lite project
*
* File gui/Widgets/layout/Horizontal.js
*
* A layout grouping widgets horizontaly
*
*
* Author: Matthieu Baumann[CDS]
*
*****************************************************************************/
export class Toolbar extends Layout {
/**
* Create a layout
* @param {Object[]} widgets - A list of predefined widgets
* @param {Object} options - Options object
* @param {DOMElement} target - The parent element.
* @param {String} position - The position of the tabs layout relative to the target.
* For the list of possibilities, see https://developer.mozilla.org/en-US/docs/Web/API/Element/insertAdjacentHTML
*/
constructor(options, target) {
let position = (options && options.position) || 'topleft';
delete options.position;
super(
[],
options,
target,
)
console.log("options toolbar", options)
this.position = position;
this.vertical = options && options.vertical === true;
this.toggled = null;
this.widgets = {};
}
// Close the toggled widget if the user clicks on another one
_toggleOffWidget(widget) {
if (this.toggled && this.toggled !== widget) {
let canBeClosed = this.toggled && this.toggled.close;
if (canBeClosed) {
this.toggled.close();
}
this.toggled = null;
}
}
has(name) {
return name in this.widgets;
}
enabled(name) {
if (!this.has(name)) {
return false;
}
let widget = this.widgets[name];
return widget.el.disabled === false;
}
enable(name) {
if (!this.has(name)) {
return;
}
let widget = this.widgets[name];
widget.update({disabled: false})
}
disable(name) {
if (!this.has(name)) {
return;
}
let widget = this.widgets[name];
widget.update({disabled: true})
}
add(name, widget) {
if (!(widget instanceof DOMElement)) {
widget = new ActionButton(widget)
}
switch (this.position) {
case 'topleft':
widget.update({openDirection: 'right'})
this.update({position: {
anchor: 'left top'
}})
break;
case 'topright':
widget.update({openDirection: 'left'})
this.update({position: {
anchor: 'right top'
}})
break;
case 'bottomleft':
widget.update({openDirection: 'top'})
this.update({position: {
anchor: 'left bottom'
}})
break;
case 'bottomright':
widget.update({openDirection: 'top right'})
this.update({position: {
anchor: 'right bottom'
}})
break;
default:
break;
}
const action = widget.options.action;
widget.update({
action: (o) => {
// toggle off the current toggled widget
this._toggleOffWidget(widget)
this.toggled = widget;
action(o)
}
})
this.widgets[name] = widget;
this.appendLast(widget);
}
remove(name) {
let widget = this.widgets[name];
if (this.toggled === widget)
this.toggled = null;
this.removeItem(widget);
delete this.widgets[name];
widget.remove()
}
}

View File

@@ -15,4 +15,10 @@ Element.prototype.swap = function (node) {
// Move `node` to before the sibling of `this`
parent.insertBefore(node, sibling);
};
};
export function isJSObject(obj) {
return obj !== null &&
typeof obj === "object" &&
obj.constructor === Object
}

View File

@@ -21,6 +21,12 @@ import { DOMElement } from "./Widget";
import { Tooltip } from "./Tooltip";
import { Icon } from "./Icon";
import { Layout } from "../Layout";
import infoIconUrl from "../../../../assets/icons/info.svg"
import targetIconUrl from "../../../../assets/icons/target.svg";
import removeIconUrl from "../../../../assets/icons/remove.svg";
import A from "../../A";
/******************************************************************************
* Aladin Lite project
*
@@ -48,7 +54,7 @@ import { Layout } from "../Layout";
* @property {function} [opt.action] - The callback function to execute when the button is clicked.
* @property {string} [opt.title] - The title attribute for the button.
* @property {Object} [opt.icon] - An icon object for the button.
* @property {boolean} [opt.disable=false] - Whether the button is initially disabled.
* @property {boolean} [opt.disabled=false] - Whether the button is initially disabled.
* @property {HTMLElement|string|Widget} [opt.content] - The content to be added to the button.
* @property {CSSStyleSheet} [opt.cssStyle] - The CSS styles to apply to the button.
* @property {Object} [opt.tooltip] - A tooltip.
@@ -63,24 +69,25 @@ import { Layout } from "../Layout";
*
* @example
* const actionButton = new ActionButton({
* toggled: false,
* action: (e) => { /* callback function * },
* title: "Click me",
* iconURL: "path/to/icon.png",
* cssStyle: "color: red;",
* tooltip: {
* position: {
* direction: 'left,
* },
* content: 'A tooltip'
* },
* position: { nextTo: someDOMElement, direction: 'right' }
* }, document.getElementById('container'));
size: 'small',
content: '❌',
//tooltip: {content: 'Close the window', position: {direction: 'bottom'}},
action(e) {
self._hide();
},
cssStyle: {
position: 'absolute',
},
position: {
top: 0,
right: 0,
}
});
*/
export class ActionButton extends DOMElement {
constructor(options, target, position = "beforeend") {
let el = document.createElement('button');
el.classList.add('aladin-btn', 'aladin-dark-theme');
el.classList.add('aladin-btn');
// add it to the dom
super(el, options);
@@ -100,9 +107,9 @@ export class ActionButton extends DOMElement {
}
if (this.options.size === 'small') {
this.addClass('small-sized-icon')
this.addClass('aladin-small-sized-icon')
} else if (this.options.size === 'medium') {
this.addClass('medium-sized-icon')
this.addClass('aladin-medium-sized-icon')
}
if (this.options.action) {
@@ -125,7 +132,7 @@ export class ActionButton extends DOMElement {
layout.push(new Icon(this.options.icon));
}
if (this.options.disable) {
if (this.options.disabled) {
this.el.disabled = true;
this.addClass('disabled')
} else {
@@ -143,7 +150,7 @@ export class ActionButton extends DOMElement {
if (layout.length === 1) {
this.appendContent(layout[0])
} else {
this.appendContent(new Layout({layout, orientation: 'horizontal'}))
this.appendContent(new Layout(layout))
}
}
@@ -188,4 +195,118 @@ export class ActionButton extends DOMElement {
return new ActionButton(opt, target, position);
}
static BUTTONS(aladin) {
return {
infoHiPS: (options) => {
return new ActionButton({
icon: {
size: 'small',
monochrome: true,
url: infoIconUrl,
},
tooltip: {
position: {direction: "top"},
content: "More about that survey?"
},
action(e) {
window.open(options && options.url);
},
...options
})
},
targetHiPSLocation: (options) => {
let ra = options && options.ra;
let dec = options && options.dec;
let fov = options && options.fov;
return new ActionButton({
icon: {
size: 'small',
monochrome: true,
url: targetIconUrl,
},
disabled: ra === undefined || dec === undefined || fov === undefined,
tooltip: {
content: "Target interesting sky location",
},
action(e) {
if (fov !== undefined && ra !== undefined && dec !== undefined) {
aladin.setFoV(+fov)
aladin.gotoObject(ra + ' ' + dec);
}
},
...options
})
},
addMOC: (options) => {
let name = options && options.name;
let url = options && options.url;
let button = new ActionButton({
size: "small",
icon: {
url: Icon.dataURLFromSVG({ svg: Icon.SVG_ICONS.MOC }),
size: "small",
monochrome: true,
},
tooltip: {
content: "Add coverage",
position: { direction: "top" },
},
action: (e) => {
// load the moc
let moc = A.MOCFromURL(
url,
{ name },
() => {
if (aladin.statusBar) {
aladin.statusBar.appendMessage({
message:
"Coverage of " +
name +
" loaded",
duration: 2000,
type: "info",
});
}
}
);
aladin.addMOC(moc);
},
...options
})
return button;
},
remove: (action) => {
return new ActionButton({
icon: {
url: removeIconUrl,
monochrome: true,
},
size: "small",
tooltip: {
content: "Remove",
},
action
})
},
close: (widget) => {
return new ActionButton({
size: 'small',
content: '❌',
action(_) {
widget.close();
},
cssStyle: {
position: 'absolute',
top: 0,
right: 0,
},
});
},
}
}
}

View File

@@ -19,13 +19,14 @@
import { DOMElement } from "./Widget";
import { ActionButton } from "./ActionButton";
import enlargeIconImg from '../../../../assets/icons/enlarge.svg';
import moveIconImg from '../../../../assets/icons/move.svg';
import { Layout } from "../Layout";
/******************************************************************************
* Aladin Lite project
*
* File gui/Tab.js
* File gui/Widgets/Box.js
*
* A context menu that shows when the user right clicks, or long touch on touch device
*
@@ -33,16 +34,6 @@ import { Layout } from "../Layout";
* Author: Matthieu Baumann[CDS]
*
*****************************************************************************/
/* Example of layout
[{
content: ''
title: '',
color: <label color>,
backgroundColor: <background tab color>,
action: () => {}
},]
*/
export class Box extends DOMElement {
constructor(options, target, position = "beforeend") {
let el = document.createElement("div");
@@ -52,7 +43,10 @@ export class Box extends DOMElement {
this.attachTo(target, position);
this._show();
this.addClass('aladin-dark-theme')
}
close() {
this._hide()
}
_show(options) {
@@ -63,26 +57,12 @@ export class Box extends DOMElement {
this.el.innerHTML = "";
let self = this;
let close = this.options.close === false ? false : true;
let draggable = false;
if (close) {
new ActionButton({
size: 'small',
content: '❌',
//tooltip: {content: 'Close the window', position: {direction: 'bottom'}},
action(e) {
self._hide();
},
cssStyle: {
position: 'absolute',
},
position: {
top: 0,
right: 0,
}
}, this.el);
this.el.appendChild(
ActionButton.BUTTONS(null).close(this).element()
);
}
if (this.options.onDragged) {
@@ -97,7 +77,7 @@ export class Box extends DOMElement {
titleEl = document.createElement('div')
titleEl.classList.add("aladin-box-title");
DOMElement.appendTo(header.title, titleEl);
DOMElement.appendTo(new Layout(header.title), titleEl);
}
let draggableEl;
@@ -120,7 +100,7 @@ export class Box extends DOMElement {
});
}
let headerEl = Layout.horizontal([draggableEl, titleEl], this.el);
let headerEl = Layout.horizontal([draggableEl, titleEl], {}, this.el);
if (draggable) {
dragElement(headerEl.element(), this.el, this.options.onDragged);
headerEl.element().style.cursor = 'move';
@@ -137,7 +117,33 @@ export class Box extends DOMElement {
if (this.options.content) {
let content = this.options.content
this.appendContent(content);
if (content instanceof Layout) {
this.appendContent(content);
} else {
this.appendContent(Layout.vertical(content));
}
this.el.lastChild.classList.add("aladin-box-content");
}
if (this.options.sizeable) {
let sizeableBtn = new ActionButton({
icon: {
url: enlargeIconImg,
size: "small",
monochrome: true,
},
tooltip: {content: 'Enlarge the window', global: true, aladin: this.aladin},
cssStyle: {
cursor: 'move',
position: 'absolute',
bottom: 0,
right: 0
},
});
this.appendContent(sizeableBtn);
enlargeElement(sizeableBtn.element(), this.el);
}
if (this.options.position) {
@@ -216,3 +222,38 @@ function dragElement(triggerElt, elmnt, onDragged) {
}
}
}
function enlargeElement(triggerElt, elmnt) {
let pos3 = 0, pos4 = 0;
triggerElt.onmousedown = dragMouseDown;
function dragMouseDown(e) {
e.preventDefault();
pos3 = e.clientX;
pos4 = e.clientY;
document.onmouseup = closeDragElement;
document.onmousemove = elementDrag;
}
function elementDrag(e) {
e.preventDefault();
const dx = e.clientX - pos3;
const dy = e.clientY - pos4;
pos3 = e.clientX;
pos4 = e.clientY;
const newWidth = elmnt.offsetWidth + 2*dx;
const newHeight = elmnt.offsetHeight + 2*dy;
elmnt.style.width = Math.max(20, newWidth) + "px";
elmnt.style.height = Math.max(20, newHeight) + "px";
}
function closeDragElement() {
document.onmouseup = null;
document.onmousemove = null;
}
}

View File

@@ -51,13 +51,11 @@ export class ContextMenu extends DOMElement {
super(el, options);
this.addClass('aladin-dark-theme')
this.aladin = aladin;
this.cssStyleDefault = el.style;
if (!options || options.hideOnClick === undefined || options.hideOnClick === true || typeof options.hideOnClick === 'function') {
/*if (!options || options.hideOnClick === undefined || options.hideOnClick === true || typeof options.hideOnClick === 'function') {
this.aladin.aladinDiv.addEventListener('click', (e) => {
if (!el.contains(e.target)) {
if (options && options.hideOnClick && typeof options.hideOnClick === 'function') {
@@ -67,7 +65,7 @@ export class ContextMenu extends DOMElement {
}
}
});
}
}*/
if (!options || options.hideOnResize === undefined || options.hideOnResize === true) {
if (Utils.hasTouchScreen()) {
@@ -176,7 +174,7 @@ export class ContextMenu extends DOMElement {
tooltip = opt.label.tooltip
}
let labelEl = Layout.horizontal({layout, tooltip});
let labelEl = Layout.horizontal(layout, {tooltip});
labelEl.attachTo(item)
} else if (opt.disabled && opt.disabled.reason) {
let tooltip = {
@@ -184,7 +182,7 @@ export class ContextMenu extends DOMElement {
position: {direction: 'top'}
}
let labelEl = Layout.horizontal({layout: opt.label, tooltip});
let labelEl = Layout.horizontal(opt.label, {tooltip});
labelEl.attachTo(item)
} else {
let wrapEl = document.createElement('div');
@@ -335,7 +333,7 @@ export class ContextMenu extends DOMElement {
parent.style.display = "";
}
show(options) {
_show(options) {
this.el.innerHTML = '';
this.el.style = this.cssStyleDefault
@@ -365,13 +363,11 @@ export class ContextMenu extends DOMElement {
super._show()
}
attach(options) {
this.menuOptions = options;
}
attach(options, toggler) {
this._hide()
this.setToggler(toggler)
/* Hide all the defined menus */
static hideAll() {
ContextMenu._menus.forEach((menu) => menu._hide())
this.menuOptions = options;
}
/// Context menu predefined items

View File

@@ -142,7 +142,7 @@ export class Form extends DOMElement {
groupLayout.push(input)
});
let item = new Layout({layout: groupLayout});
let item = new Layout(groupLayout, {vertical: true});
item.addClass('aladin-form-group')
return item;

View File

@@ -46,7 +46,7 @@ import { Tooltip } from "./Tooltip";
* @property {function} [opt.action] - The callback function to execute when the button is clicked.
* @property {string} [opt.title] - The title attribute for the button.
* @property {string} [opt.iconURL] - The URL of the icon image for the button.
* @property {boolean} [opt.disable=false] - Whether the button is initially disabled.
* @property {boolean} [opt.disabled=false] - Whether the button is initially disabled.
* @property {HTMLElement|string|Widget} [opt.content] - The content to be added to the button.
* @property {CSSStyleSheet} [opt.cssStyle] - The CSS styles to apply to the button.
* @property {string} [opt.tooltip] - The tooltip text for the button.
@@ -82,7 +82,6 @@ export class Icon extends DOMElement {
this._show();
this.addClass('aladin-icon')
this.addClass('aladin-dark-theme')
this.attachTo(target, position)
}
@@ -91,9 +90,9 @@ export class Icon extends DOMElement {
this.el.innerHTML = '';
if (this.options.size === 'small') {
this.addClass('small-sized-icon')
this.addClass('aladin-small-sized-icon')
} else if (this.options.size === 'medium') {
this.addClass('medium-sized-icon')
this.addClass('aladin-medium-sized-icon')
}
if (this.options.title) {
@@ -107,7 +106,7 @@ export class Icon extends DOMElement {
this.el.appendChild(img);
}
if (this.options.disable) {
if (this.options.disabled) {
this.el.disabled = true;
this.addClass('disabled')
} else {
@@ -119,7 +118,7 @@ export class Icon extends DOMElement {
this.setCss(this.options.cssStyle);
}
if (this.options.monochrome && this.options.monochrome === true) {
if (this.options.monochrome === undefined || this.options.monochrome === true) {
this.addClass('aladin-icon-monochrome');
}
@@ -141,7 +140,8 @@ export class Icon extends DOMElement {
static SVG_ICONS = {
CATALOG: '<svg xmlns="http://www.w3.org/2000/svg"><polygon points="1,0,5,0,5,3,1,3" fill="FILLCOLOR" /><polygon points="7,0,9,0,9,3,7,3" fill="FILLCOLOR" /><polygon points="10,0,12,0,12,3,10,3" fill="FILLCOLOR" /><polygon points="13,0,15,0,15,3,13,3" fill="FILLCOLOR" /><polyline points="1,5,5,9" stroke="FILLCOLOR" /><polyline points="1,9,5,5" stroke="FILLCOLOR" /><line x1="7" y1="7" x2="15" y2="7" stroke="FILLCOLOR" stroke-width="2" /><polyline points="1,11,5,15" stroke="FILLCOLOR" /><polyline points="1,15,5,11" stroke="FILLCOLOR" /><line x1="7" y1="13" x2="15" y2="13" stroke="FILLCOLOR" stroke-width="2" /></svg>',
MOC: '<svg xmlns="http://www.w3.org/2000/svg"><polyline points="0.5,7,2.5,7,2.5,5,7,5,7,3,10,3,10,5,13,5,13,7,15,7,15,9,13,9,13,12,10,12,10,14,7,14,7,12,2.5,12,2.5,10,0.5,10,0.5,7" stroke-width="1" stroke="FILLCOLOR" fill="transparent" /><line x1="1" y1="10" x2="6" y2="5" stroke="FILLCOLOR" stroke-width="0.5" /><line x1="2" y1="12" x2="10" y2="4" stroke="FILLCOLOR" stroke-width="0.5" /><line x1="5" y1="12" x2="12" y2="5" stroke="FILLCOLOR" stroke-width="0.5" /><line x1="7" y1="13" x2="13" y2="7" stroke="FILLCOLOR" stroke-width="0.5" /><line x1="10" y1="13" x2="13" y2="10" stroke="FILLCOLOR" stroke-width="0.5" /></svg>',
OVERLAY: '<svg xmlns="http://www.w3.org/2000/svg"><polygon points="10,5,10,1,14,1,14,14,2,14,2,9,6,9,6,5" fill="transparent" stroke="FILLCOLOR" stroke-width="2"/></svg>'
OVERLAY: '<svg xmlns="http://www.w3.org/2000/svg"><polygon points="10,5,10,1,14,1,14,14,2,14,2,9,6,9,6,5" fill="transparent" stroke="FILLCOLOR" stroke-width="2"/></svg>',
COLOR: '<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 122.88 112.75" style="enable-background:new 0 0 122.88 112.75" xml:space="preserve"><style type="text/css">.st0{fill-rule:evenodd;clip-rule:evenodd;fill:#6BBE66;} .st1{fill-rule:evenodd;clip-rule:evenodd;fill:#FF4141;} .st2{fill-rule:evenodd;clip-rule:evenodd;fill:#00A1F1;}</style><g><path class="st0" d="M56.66,105.35c-5.94,4.6-13.4,7.34-21.5,7.34C15.74,112.69,0,96.95,0,77.53c0-13.47,7.58-25.17,18.7-31.07 c1.96,6.96,5.68,13.19,10.65,18.16c4.64,4.64,10.38,8.2,16.79,10.24c-0.06,0.9-0.09,1.81-0.09,2.73 C46.06,88.25,50.07,97.98,56.66,105.35L56.66,105.35z"/><path class="st1" d="M122.88,77.59c0,19.42-15.74,35.16-35.16,35.16S52.56,97,52.56,77.59c0-0.41,0.01-0.82,0.02-1.23 c2.03,0.3,4.11,0.46,6.23,0.46c11.5,0,21.92-4.66,29.46-12.2c5.45-5.45,9.4-12.41,11.17-20.19 C113.1,49.26,122.88,62.28,122.88,77.59L122.88,77.59z"/><path class="st2" d="M93.97,35.16c0,19.42-15.74,35.16-35.16,35.16S23.65,54.58,23.65,35.16S39.39,0,58.81,0 S93.97,15.74,93.97,35.16L93.97,35.16z"/></g></svg>'
}
static dataURLFromSVG(icon) {
@@ -150,9 +150,6 @@ export class Icon extends DOMElement {
let elt = document.createElement('div');
elt.innerHTML = str;
//elt.querySelector('svg').setAttribute('width', size);
//elt.querySelector('svg').setAttribute('height', size);
elt.style.width = size;
elt.style.height = size;

View File

@@ -92,7 +92,19 @@ export class Input extends DOMElement {
let innerHTML = "";
for (const option of this.options.options) {
innerHTML += "<option>" + option + "</option>";
let value;
let label;
if (option.value) {
value = option.value
} else {
value = option
}
if (option.label) {
label = option.label
} else {
label = option
}
innerHTML += "<option value=\"" + value + "\">" + label + "</option>";
}
this.el.innerHTML = innerHTML;
}
@@ -330,7 +342,6 @@ export class Input extends DOMElement {
}
this.el.classList.add('aladin-input');
this.el.classList.add('aladin-dark-theme');
if (this.options.cssStyle) {
this.setCss(this.options.cssStyle);

View File

@@ -87,7 +87,7 @@ export class RadioButton extends DOMElement {
}
}
let el = Layout.horizontal({layout});
let el = Layout.horizontal(layout);
super(el, options)

View File

@@ -1,155 +0,0 @@
// Copyright 2023 - UDS/CNRS
// The Aladin Lite program is distributed under the terms
// of the GNU General Public License version 3.
//
// This file is part of Aladin Lite.
//
// Aladin Lite is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, version 3 of the License.
//
// Aladin Lite is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// The GNU General Public License is available in COPYING file
// along with Aladin Lite.
//
import { DOMElement } from "./Widget";
import { FSM } from "../../FiniteStateMachine";
import { ActionButton } from "./ActionButton";
import { ContextMenu } from "./ContextMenu";
/******************************************************************************
* Aladin Lite project
*
* File gui/Form.js
*
* A context menu that shows when the user right clicks, or long touch on touch device
*
*
* Author: Matthieu Baumann[CDS]
*
*****************************************************************************/
/*
Exemple of layout object
{
{
label: "ID",
type: "text",
value: "the placeholder value...",
},
*/
/*
options = {id: (btn option), id2: btn option, selected: id}
*/
export class SelectorButton extends DOMElement {
/**
* Create a layout
* @param {{layout: {type: String, name: String, value: Number | String, placeholder: Number | String, change: Function } | {type: String, name: String, checked: Boolean, change: Function } | { type: String, name: String, value: String, options: Array.<String>, change: Function }, cssStyle: Object}} options - Represents the structure of the Tabs
* @param {DOMElement} target - The parent element.
* @param {String} position - The position of the tabs layout relative to the target.
* For the list of possibilities, see https://developer.mozilla.org/en-US/docs/Web/API/Element/insertAdjacentHTML
*/
constructor(options, aladin, target, position = 'beforeend') {
let el;
super(el, options)
this.aladin = aladin;
let self = this;
let ctxMenu = new ContextMenu(this.aladin, {hideOnClick: false});
let openCtxMenuOnClick = (e) => {
this.fsm.dispatch('closeCtxMenu')
};
const openCtxMenu = () => {
this.aladin.aladinDiv.addEventListener('click', openCtxMenuOnClick)
let menuOptions = [];
for (const id in this.options) {
if (id === 'selected' || this.options.selected === id || id === 'tooltip') {
continue;
}
let optSelect = this.options[id];
menuOptions.push({
label: new ActionButton(optSelect),
action(e) {
if(optSelect.change) {
optSelect.change(e)
}
self.update({selected: id});
self._show();
self.fsm.dispatch('closeCtxMenu')
},
cssStyle: {
padding: "0",
}
})
}
ctxMenu.attach(menuOptions);
ctxMenu.show({
position: {
nextTo: this.el,
direction: 'bottom',
}
})
};
const closeCtxMenu = () => {
this.aladin.aladinDiv.removeEventListener('click', openCtxMenuOnClick)
ctxMenu._hide();
};
this.fsm = new FSM({
state: 'init',
transitions: {
init: {
openCtxMenu
},
openCtxMenu: {
closeCtxMenu
},
closeCtxMenu: {
openCtxMenu
},
}
})
this._show();
}
_hide() {
this.fsm.dispatch('closeCtxMenu');
super._hide()
}
_show() {
let self = this;
// remove from the DOM tree
const selectedId = this.options.selected;
let {target, position} = this.remove();
this.el = new ActionButton({
...this.options[selectedId],
action: (e) => {
if (self.fsm.state === 'openCtxMenu') {
self.fsm.dispatch('closeCtxMenu');
} else {
self.fsm.dispatch('openCtxMenu');
}
}
}).element();
// Reattach it at the same position
this.attachTo(target, position)
}
}

View File

@@ -57,13 +57,13 @@ export class Tabs extends DOMElement {
this.tabSelectedIdx = 0;
let tabs = this.buildTabs(options);
this.el = new Layout({
layout: [
new Layout({layout: tabs.layout, orientation: 'horizontal'}),
this.el = Layout.nested(
[
tabs.layout,
tabs.content
],
classList: "aladin-table"
}).element();
{ classList: "aladin-table" }
).element();
this._show();

View File

@@ -52,8 +52,6 @@ export class Table extends DOMElement {
super(el, options);
this.attachTo(target, position);
this.addClass("aladin-dark-theme")
}
static _createTableBody = function(options) {

View File

@@ -50,8 +50,7 @@ export class Tooltip extends DOMElement {
}
options.position.anchor = target;
if (!options.delayShowUpTime) {
if (options.delayShowUpTime === undefined) {
options.delayShowUpTime = 500;
}
@@ -76,8 +75,6 @@ export class Tooltip extends DOMElement {
super(wrapperEl, options)
this.element().classList.add('aladin-dark-theme')
this._show();
}
@@ -176,6 +173,8 @@ export class Tooltip extends DOMElement {
return this.el.querySelector('.aladin-tooltip');
}
static hoveredEl = null;
static add(options, target) {
if (target) {
if (target.tooltip) {
@@ -188,13 +187,42 @@ export class Tooltip extends DOMElement {
let targetEl = target.element()
if (options.mouse) {
let tooltip = options.aladin && options.aladin.tooltip;
Utils.on(targetEl, 'mousemove', (e) => {
tooltip.style.left = e.clientX + 12 + 'px';
tooltip.style.top = e.clientY + 12 + 'px';
});
Utils.on(targetEl, 'mouseover', (e) => {
if (Tooltip.hoveredEl && Tooltip.hoveredEl.contains(targetEl))
return;
Tooltip.hoveredEl = targetEl;
// Change the content to match
tooltip.innerHTML = options.content;
tooltip.style.display = 'block';
});
Utils.on(targetEl, 'mouseleave', (e) => {
if (Tooltip.hoveredEl && Tooltip.hoveredEl.contains(targetEl) && Tooltip.hoveredEl !== targetEl) {
return;
}
tooltip.style.display = 'none';
Tooltip.hoveredEl = null;
});
return;
}
if (options.global) {
let statusBar = options.aladin && options.aladin.statusBar;
if (!statusBar) {
return;
}
// handle global tooltip div display
Utils.on(targetEl, 'mouseover', (e) => {
statusBar.removeMessage('tooltip')
statusBar.appendMessage({

View File

@@ -21,6 +21,9 @@ import { DOMElement } from "./Widget";
import { Icon } from "./Icon";
import folderIconUrl from "../../../../assets/icons/folder.svg";
import { Layout } from "../Layout";
import { ActionButton } from "./ActionButton";
import { Input } from "./Input";
/******************************************************************************
* Aladin Lite project
*
@@ -40,6 +43,10 @@ export class Tree extends DOMElement {
super(el, options);
this.click = options && options.click;
this.dblclick = options && options.dblclick;
this.aladin = options && options.aladin;
this.onlyInView = false;
let rootNode = options && options.root || {};
this.params = null;
@@ -48,9 +55,9 @@ export class Tree extends DOMElement {
this._setRoot(rootNode);
this.attachTo(target, position);
this._show();
this.addClass('aladin-dark-theme')
}
_setRoot(root) {
@@ -100,8 +107,6 @@ export class Tree extends DOMElement {
directoryLinks.reverse()
let directoryListEl = document.createElement('div');
directoryListEl.classList.add('aladin-directory-path');
directoryListEl.style.display = "inline-block"
for (var link of directoryLinks) {
directoryListEl.appendChild(link);
@@ -112,11 +117,79 @@ export class Tree extends DOMElement {
directoryListEl.appendChild(spanSplitEl)
}
this.el.appendChild(directoryListEl)
let self = this;
this.el.appendChild(
new Layout({
start: [directoryListEl],
end: [
Input.checkbox({
name: "filter-out-not-in-view",
tooltip: { content: "Mask data not in the view", position: {direction: "bottom"} },
checked: this.onlyInView,
click(e) {
self.onlyInView = e.target.checked;
self._createDOM(self.curNode)
},
})
]
}, {classList: 'aladin-directory-path'}).element()
)
let listElt = document.createElement('ul');
for (const label of Object.keys(node).sort()) {
let seekMinEmInNode = (n) => {
const isLeaf = typeof n === 'object' && 'ID' in n;
if (isLeaf) {
return (+n.em_min)
} else if (typeof n === 'object') {
let min = Number.POSITIVE_INFINITY;
for (var [key, child] of Object.entries(n)) {
if (key === "parent" || key === "label") {
continue;
}
let val = seekMinEmInNode(child);
if (Number.isFinite(val)) {
min = Math.min(val, min);
}
}
return min;
} else {
return Number.POSITIVE_INFINITY;
}
};
let labels = Object.keys(node).sort((la, lb) => {
let na = node[la];
let nb = node[lb];
let aIsLeaf = typeof na === "object" && 'ID' in na;
let bIsLeaf = typeof nb === "object" && 'ID' in nb;
if (aIsLeaf !== bIsLeaf) {
return aIsLeaf - bIsLeaf;
} else if (typeof na === "object" && typeof nb === "object") {
let emNa = seekMinEmInNode(na)
let emNb = seekMinEmInNode(nb)
if (emNa > emNb) {
return -1;
} else {
return 1;
}
} else if (la < lb) {
return -1
} else {
return 1
}
});
let noEltsListed = true;
for (const label of labels) {
if (label !== 'parent' && label !== "label") {
let elt = document.createElement('li');
// points towards the parent node
@@ -126,10 +199,88 @@ export class Tree extends DOMElement {
if(this.params && this.filter && !this.filter(child, this.params)) {
elt.style.display = "none";
} else {
elt.style.display = "block";
elt.style.display = "";
noEltsListed = false;
}
elt.innerHTML = this.label(child);
let label = this.label(child);
let layout = {start: [label], end: []};
if (child.dataproduct_subtype === "color") {
layout.end.push(new Icon({
size: "small",
url: Icon.dataURLFromSVG({ svg: Icon.SVG_ICONS.COLOR }),
}))
}
layout.end.push(ActionButton.BUTTONS(this.aladin)
.infoHiPS({
url: child.hips_service_url,
tooltip: {
aladin: this.aladin,
global: true,
content: "More info on the survey ?",
},
}).element()
)
layout.end = layout.end.concat([
ActionButton.BUTTONS(this.aladin)
.targetHiPSLocation({
ra: child.hips_initial_ra,
dec: child.hips_initial_dec,
fov: child.hips_initial_fov,
tooltip: {
aladin: this.aladin,
global: true,
content: "Move to an interesting location",
},
})
.element(),
ActionButton.BUTTONS(this.aladin)
.addMOC({
name: label,
url: child.hips_service_url + '/Moc.fits',
tooltip: {
aladin: this.aladin,
global: true,
content: "Click to add its coverage",
},
})
.element(),
])
let childElt = new Layout(
layout,
{
vertical: false,
tooltip: {
content: '<figure class="aladin-fig"><img ' +
`src="${child.hips_service_url + "/preview.jpg"}"` +
`alt="${label}" />` +
`<figcaption>${label}</figcaption>` +
'</figure>',
mouse: true,
aladin: this.aladin,
}
}).element();
if (this.highlight) {
if(this.highlight.includes(child.ID)) {
childElt.classList.add("aladin-valid");
childElt.classList.remove("aladin-not-found");
} else {
childElt.classList.remove("aladin-valid");
childElt.classList.add("aladin-not-found");
if (this.onlyInView) {
childElt.style.display = "none";
} else {
childElt.style.display = "";
}
}
}
elt.appendChild(childElt);
} else {
// we see a parent, we must determine:
// * its color: he has at least 1 child inside the FoV => green
@@ -138,16 +289,34 @@ export class Tree extends DOMElement {
let numTotal = this.numChildMatchingFilter(child, false);
let name = label;
elt.innerHTML = name + ` (${numFilteringMatching}/${numTotal})`
elt.appendChild(Layout.horizontal([
new Icon({
size: "small",
monochrome: true,
url: folderIconUrl,
}),
name + ` (${numFilteringMatching}/${numTotal})`
]).element())
if (numFilteringMatching == 0) {
elt.style.display = "none";
} else {
elt.style.display = "block";
elt.style.display = "";
noEltsListed = false;
}
}
elt.style.color = this.hasChildLocatedInFov(child) ? 'yellowgreen' : 'orange';
if(this.hasChildLocatedInFov(child)) {
elt.classList.add("aladin-valid");
elt.classList.remove("aladin-not-found");
} else {
elt.classList.remove("aladin-valid");
elt.classList.add("aladin-not-found");
if (this.onlyInView) {
elt.style.display = "none";
}
}
child.label = label;
child.parent = node;
@@ -163,12 +332,21 @@ export class Tree extends DOMElement {
this._createDOM(child);
}
})
elt.addEventListener('dblclick', (e) => {
if (isLeaf) {
this.dblclick(child)
}
})
listElt.appendChild(elt)
}
}
this.el.appendChild(listElt);
if (noEltsListed && this.curNode !== this.root) {
this.navigate(1)
} else {
this.el.appendChild(listElt);
}
}
setHierarchy(root) {
@@ -188,44 +366,7 @@ export class Tree extends DOMElement {
highlightNodes(highlight) {
this.highlight = highlight
let elts = this.el.querySelectorAll("li");
let i = 0;
for (const label of Object.keys(this.curNode).sort()) {
if (label !== 'parent' && label !== "label") {
let elt = elts[i];
i += 1;
// points towards the parent node
let child = this.curNode[label];
let isLeaf = typeof child === "object" && 'ID' in child;
if (isLeaf) {
// Check if its ID is found in the view
if (this.highlight) {
elt.style.color = this.highlight.includes(child.ID) ? 'yellowgreen' : 'orange';
}
} else {
// we see a parent, we must determine:
// * its color: he has at least 1 child inside the FoV => green
// * the number of children matching the filter params
elt.style.color = this.hasChildLocatedInFov(child) ? 'yellowgreen' : 'orange';
// we see a parent, we must determine:
// * its color: he has at least 1 child inside the FoV => green
// * the number of children matching the filter params
let numFilteringMatching = this.numChildMatchingFilter(child, true);
let numTotal = this.numChildMatchingFilter(child, false);
let name = elt.innerText.split('(');
elt.innerHTML = name[0] + ` (${numFilteringMatching}/${numTotal})`
if (numFilteringMatching == 0) {
elt.style.display = "none";
} else {
elt.style.display = "block";
}
}
}
}
this._createDOM(this.curNode);
}
// Set params to null, undefined or {} to disable the filtering
@@ -236,39 +377,7 @@ export class Tree extends DOMElement {
this.params = params;
let elts = this.el.querySelectorAll("li");
let i = 0;
for (const label of Object.keys(this.curNode).sort()) {
if (label !== 'parent' && label !== "label") {
let elt = elts[i];
i += 1;
// points towards the parent node
let child = this.curNode[label];
let isLeaf = typeof child === "object" && 'ID' in child;
if (isLeaf) {
if(this.params && this.filter && !this.filter(child, this.params)) {
elt.style.display = "none"
} else {
elt.style.display = "block"
}
} else {
// we see a parent, we must determine:
// * its color: he has at least 1 child inside the FoV => green
// * the number of children matching the filter params
let numFilteringMatching = this.numChildMatchingFilter(child, true);
let numTotal = this.numChildMatchingFilter(child, false);
let name = elt.innerText.split('(');
elt.innerHTML = name[0] + ` (${numFilteringMatching}/${numTotal})`
if (numFilteringMatching == 0) {
elt.style.display = "none";
} else {
elt.style.display = "block";
}
}
}
}
this._createDOM(this.curNode);
}
hasChildLocatedInFov(node) {
@@ -282,12 +391,12 @@ export class Tree extends DOMElement {
let isLeaf = typeof node === "object" && 'ID' in node;
if (isLeaf) {
if (this.highlight.includes(node.ID)) {
if (this.highlight.includes(node.ID) && this.params && this.filter && this.filter(node, this.params)) {
return true;
}
} else {
for (const label of Object.keys(node).sort()) {
if (label === "parent")
for (const label of Object.keys(node)) {
if (label === "parent" || label === "label")
continue;
let child = node[label];
@@ -314,7 +423,7 @@ export class Tree extends DOMElement {
}
} else {
let num = 0;
for (const label of Object.keys(node).sort()) {
for (const label of Object.keys(node)) {
if (label === "parent")
continue;

View File

@@ -18,7 +18,6 @@
//
import { Utils } from "../../Utils";
/******************************************************************************
* Aladin Lite project
*
@@ -47,28 +46,6 @@ export class DOMElement {
this.options = options;
this.name = options && options.name || Utils.uuidv4()
this.isHidden = true;
/*this.el.addEventListener("mouseup", (e) => {
var wasDragging = view.realDragging === true;
if (view.dragging) { // if we were dragging, reset to default cursor
if(view.mode === View.PAN) {
view.setCursor('default');
}
view.dragging = false;
if (wasDragging) {
view.realDragging = false;
// call the positionChanged once more with a dragging = false
view.throttledPositionChanged(false);
}
if (view.spectraDisplayer) {
view.spectraDisplayer.enableInteraction();
}
}
});*/
}
element() {
@@ -119,14 +96,19 @@ export class DOMElement {
static appendTo(elmt, parent) {
if(elmt) {
// Append the updated content
if (elmt instanceof DOMElement) {
if (Array.isArray(elmt)) {
for (var elt of elmt) {
DOMElement.appendTo(elt, parent)
}
} else if (elmt instanceof DOMElement) {
elmt.attachTo(parent)
} else if (elmt instanceof Element) {
parent.insertAdjacentElement('beforeend', elmt);
} else {
let wrapEl = document.createElement('div');
wrapEl.innerHTML = elmt;
parent.insertAdjacentElement('beforeend', wrapEl);
const template = document.createElement('template');
template.innerHTML = elmt;
parent.append(template.content.cloneNode(true));
}
}
}
@@ -231,9 +213,11 @@ export class DOMElement {
let aDivRect = aladinDiv.getBoundingClientRect();
const offViewX = aDivRect.x;
const offViewY = aDivRect.y;
if (!dir) {
// determine the direction with respect to the element given
let elX = options.nextTo.el.getBoundingClientRect().left + options.nextTo.el.getBoundingClientRect().width * 0.5 - offViewX;
const nextElementRect = nextTo.el.getBoundingClientRect();
let elX = nextElementRect.left + nextElementRect.width * 0.5 - offViewX;
dir = (elX < innerWidth / 2) ? 'right' : 'left';
}
@@ -242,23 +226,28 @@ export class DOMElement {
}
let rect = nextTo.getBoundingClientRect();
this.el.classList.remove('left', 'right', 'top', 'bottom');
switch (dir) {
case 'left':
left = rect.x - offsetWidth - offViewX;
left = rect.x - offViewX;
top = rect.y - offViewY;
this.el.classList.add('left');
break;
case 'right':
left = rect.x + rect.width - offViewX;
top = rect.y - offViewY;
this.el.classList.add('right');
break;
case 'top':
left = rect.x - offViewX;
top = rect.y - offsetHeight - offViewY;
top = rect.y - offViewY;
this.el.classList.add('top');
break;
case 'bottom':
left = rect.x - offViewX;
top = rect.y + rect.height - offViewY;
this.el.classList.add('bottom');
break;
default:
left = 0;
@@ -268,11 +257,11 @@ export class DOMElement {
// Translate if the div in
if (typeof top === 'number') {
if (top + offsetHeight >= innerHeight) {
/*if (top + offsetHeight >= innerHeight) {
y = '-' + (top + offsetHeight - innerHeight) + 'px';
} else if (top < 0) {
y = Math.abs(top) + 'px';
}
}*/
top = top + 'px';
}
@@ -280,11 +269,11 @@ export class DOMElement {
bottom = bottom + 'px';
}
if (typeof left === 'number') {
if (left + offsetWidth > innerWidth) {
/*if (left + offsetWidth > innerWidth) {
x = '-' + (left + offsetWidth - innerWidth) + 'px';
} else if (left < 0) {
x = Math.abs(left) + 'px';
}
}*/
left = left + 'px';
}
@@ -316,12 +305,24 @@ export class DOMElement {
}
}
setToggler(toggler) {
this.toggler = toggler;
}
_show() {
if (this.toggler) {
this.toggler.notify(true)
}
this.el.style.display = ""
this.isHidden = false;
}
_hide() {
if (this.toggler) {
this.toggler.notify(false)
}
this.isHidden = true;
this.el.style.display = 'none';
}

View File

@@ -159,6 +159,7 @@ export let Circle = (function() {
return;
}
this.isHovered = true;
this.setLineWidth(this.getLineWidth() + 2)
if (this.overlay) {
this.overlay.reportChange();
}
@@ -169,6 +170,8 @@ export let Circle = (function() {
return;
}
this.isHovered = false;
this.setLineWidth(this.getLineWidth() - 2)
if (this.overlay) {
this.overlay.reportChange();
}

View File

@@ -164,6 +164,7 @@ export let Ellipse = (function() {
return;
}
this.isHovered = true;
this.setLineWidth(this.getLineWidth() + 2)
if (this.overlay) {
this.overlay.reportChange();
}
@@ -174,6 +175,7 @@ export let Ellipse = (function() {
return;
}
this.isHovered = false;
this.setLineWidth(this.getLineWidth() - 2)
if (this.overlay) {
this.overlay.reportChange();
}

View File

@@ -149,6 +149,7 @@ export let Polyline = (function() {
return;
}
this.isHovered = true;
this.setLineWidth(this.getLineWidth() + 2)
if (this.overlay) {
this.overlay.reportChange();
}
@@ -159,6 +160,7 @@ export let Polyline = (function() {
return;
}
this.isHovered = false;
this.setLineWidth(this.getLineWidth() - 2)
if (this.overlay) {
this.overlay.reportChange();
}
@@ -456,51 +458,111 @@ export let Polyline = (function() {
return false;
};
Polyline.prototype.intersectsBBox = function(x, y, w, h, view) {
for (let i = 0; i < this.raDecArray.length - 1; i++) {
let p1 = this.raDecArray[i];
let p2 = this.raDecArray[i + 1];
Polyline.prototype.intersectsBBox = function (x, y, w, h, view) {
let n = this.raDecArray.length;
if (n < 2) return false;
let xy1 = view.aladin.world2pix(p1[0], p1[1]);
let xy2 = view.aladin.world2pix(p2[0], p2[1]);
// 2⃣ Edge intersection test
let i = this.closed ? n - 1 : 0;
let j = this.closed ? 0 : 1;
let poly = [];
while (j < n) {
const p1 = this.raDecArray[i];
const p2 = this.raDecArray[j];
const xy1 = view.aladin.world2pix(p1[0], p1[1]);
const xy2 = view.aladin.world2pix(p2[0], p2[1]);
// Skip invalid segments, do NOT abort
if (!xy1 || !xy2) {
return false;
i = j++;
continue;
}
xy1 = {x: xy1[0], y: xy1[1]};
xy2 = {x: xy2[0], y: xy2[1]};
// Check if line segment intersects with the bounding box
if (this.lineIntersectsBox(xy1, xy2, x, y, w, h)) {
const a = { x: xy1[0], y: xy1[1] };
const b = { x: xy2[0], y: xy2[1] };
poly.push(a);
if (Polyline.segmentIntersectsBox(a, b, x, y, w, h)) {
return true;
}
i = j++;
}
if (this.closed && poly.length === this.raDecArray.length) {
console.log("closed poly")
const corners = [
{ x, y },
{ x: x + w, y },
{ x: x + w, y: y + h },
{ x, y: y + h }
];
for (const c of corners) {
if (Polyline.pointInPolygon(c, poly)) {
return true;
}
}
}
return false;
};
Polyline.prototype.lineIntersectsBox = function(p1, p2, x, y, w, h) {
// Check if line segment is completely outside the box
if ((p1.x < x && p2.x < x) ||
(p1.y < y && p2.y < y) ||
(p1.x > x + w && p2.x > x + w) ||
(p1.y > y + h && p2.y > y + h)) {
return false;
}
let m = (p2.y - p1.y) / (p2.x - p1.x); // Slope of the line
let c = p1.y - m * p1.x; // y-intercept of the line
Polyline.segmentIntersectsBox = function (p1, p2, x, y, w, h) {
const x2 = x + w;
const y2 = y + h;
// Check if line intersects with the sides of the box
if ((p1.y >= y && p1.y <= y + h) ||
(p2.y >= y && p2.y <= y + h) ||
(m * x + c >= y && m * x + c <= y + h) ||
(m * (x + w) + c >= y && m * (x + w) + c <= y + h)) {
// 1⃣ Endpoint inside box
if (Polyline.pointInBox(p1, x, y, x2, y2) ||
Polyline.pointInBox(p2, x, y, x2, y2)) {
return true;
}
return false;
// 2⃣ Check intersection with 4 box edges
return (
Polyline.segmentsIntersect(p1, p2, { x, y }, { x: x2, y }) || // top
Polyline.segmentsIntersect(p1, p2, { x: x2, y }, { x: x2, y: y2 }) || // right
Polyline.segmentsIntersect(p1, p2, { x: x2, y: y2 }, { x, y: y2 }) || // bottom
Polyline.segmentsIntersect(p1, p2, { x, y: y2 }, { x, y }) // left
);
};
Polyline.pointInBox = function (p, x1, y1, x2, y2) {
return p.x >= x1 && p.x <= x2 && p.y >= y1 && p.y <= y2;
};
Polyline.pointInPolygon = function (xy, poly) {
let inside = false;
for (let i = 0, j = poly.length - 1; i < poly.length; j = i++) {
const xi = poly[i].x, yi = poly[i].y;
const xj = poly[j].x, yj = poly[j].y;
const intersect =
((yi > xy.y) !== (yj > xy.y)) &&
(xy.x < (xj - xi) * (xy.y - yi) / (yj - yi) + xi);
if (intersect) inside = !inside;
}
return inside;
};
Polyline.segmentsIntersect = function (p1, p2, p3, p4) {
function orient(a, b, c) {
return (b.x - a.x) * (c.y - a.y) - (b.y - a.y) * (c.x - a.x);
}
const o1 = orient(p1, p2, p3);
const o2 = orient(p1, p2, p4);
const o3 = orient(p3, p4, p1);
const o4 = orient(p3, p4, p2);
return o1 * o2 < 0 && o3 * o4 < 0;
};
// static methods
@@ -516,10 +578,10 @@ export let Polyline = (function() {
// check if the line has hit any of the rectangle's sides
// uses the Line/Line function below
let left = Polyline._intersectLine(x1, y1, x2, y2, 0, 0, 0, rh);
let right = Polyline._intersectLine(x1, y1, x2, y2, rw, 0, rw, rh);
let top = Polyline._intersectLine(x1, y1, x2, y2, 0, 0, rw, 0);
let bottom = Polyline._intersectLine(x1, y1, x2, y2, 0, rh, rw, rh);
let left = Polyline.segmentsIntersect({x: x1, y: y1}, {x: x2, y: y2}, {x: 0, y: 0}, {x: 0, y: rh});
let right = Polyline.segmentsIntersect({x: x1, y: y1}, {x: x2, y: y2}, {x: rw, y: 0}, {x: rw, y: rh});
let top = Polyline.segmentsIntersect({x: x1, y: y1}, {x: x2, y: y2}, {x: 0, y: 0}, {x: rw, y: 0});
let bottom = Polyline.segmentsIntersect({x: x1, y: y1}, {x: x2, y: y2}, {x: 0, y: rh}, {x: rw, y: rh});
// if ANY of the above are true, the line
// has hit the rectangle
@@ -530,17 +592,5 @@ export let Polyline = (function() {
return false;
};
Polyline._intersectLine = function(x1, y1, x2, y2, x3, y3, x4, y4) {
// Calculate the direction of the lines
let uA = ((x4-x3)*(y1-y3) - (y4-y3)*(x1-x3)) / ((y4-y3)*(x2-x1) - (x4-x3)*(y2-y1));
let uB = ((x2-x1)*(y1-y3) - (y2-y1)*(x1-x3)) / ((y4-y3)*(x2-x1) - (x4-x3)*(y2-y1));
// If uA and uB are between 0-1, lines are colliding
if (uA >= 0 && uA <= 1 && uB >= 0 && uB <= 1) {
return true;
}
return false;
};
return Polyline;
})();

View File

@@ -193,7 +193,7 @@ export let Vector = (function() {
xy2 = {x: xy2[0], y: xy2[1]};
// Check if line segment intersects with the bounding box
if (this.lineIntersectsBox(xy1, xy2, x, y, w, h)) {
if (Polyline.segmentIntersectsBox(xy1, xy2, x, y, w, h)) {
return true;
}

View File

@@ -234,7 +234,7 @@ export let Datalink = (function() {
title: 'HiPS cube player',
draggable: true,
},
content: Layout.horizontal([prevBtn, nextBtn, slicer, (idxSlice + 1) + '/' + numSlices]),
content: [prevBtn, nextBtn, slicer, (idxSlice + 1) + '/' + numSlices],
position: {anchor: 'center top'},
});
aladinInstance.addUI(cubeDisplayer)