mirror of
https://github.com/cds-astro/aladin-lite.git
synced 2025-12-25 20:34:50 -08:00
Compare commits
25 Commits
features/l
...
v3.2.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bc1096fce3 | ||
|
|
7d5696228d | ||
|
|
08699a9bd5 | ||
|
|
fc6a09e373 | ||
|
|
a8a86a2952 | ||
|
|
cc958bfa2d | ||
|
|
6c4ddce6b0 | ||
|
|
c1b2bd24b9 | ||
|
|
46573a23da | ||
|
|
121f4345bc | ||
|
|
0665f2b65f | ||
|
|
a58fb1dd8a | ||
|
|
466472a1a7 | ||
|
|
540f4e33be | ||
|
|
0b92b6d1db | ||
|
|
06dcc126f9 | ||
|
|
04e552b7c3 | ||
|
|
1bee9c8b77 | ||
|
|
c77f2aeda8 | ||
|
|
57c1b8423d | ||
|
|
ebf2d06f31 | ||
|
|
5d0ec40612 | ||
|
|
82b2eb0423 | ||
|
|
2dc6f17c7d | ||
|
|
402e270015 |
4
.github/workflows/test.yml
vendored
4
.github/workflows/test.yml
vendored
@@ -32,4 +32,6 @@ jobs:
|
||||
run: |
|
||||
npm run build
|
||||
- name: "Run some tests"
|
||||
run: npm test
|
||||
run: |
|
||||
npm run test:build
|
||||
npm run test:unit
|
||||
4
.gitignore
vendored
4
.gitignore
vendored
@@ -8,6 +8,6 @@ package-lock.json
|
||||
src/core/target/
|
||||
src/core/Cargo.lock
|
||||
|
||||
aladin-lite-3.1.0.tgz
|
||||
aladin-lite*.tgz
|
||||
|
||||
.vscode
|
||||
.vscode
|
||||
|
||||
@@ -6,7 +6,7 @@ Aladin Lite is a Web application which enables HiPS visualization from the brows
|
||||
|
||||
See [A&A 578, A114 (2015)](https://arxiv.org/abs/1505.02291) and [IVOA HiPS Recommendation](http://ivoa.net/documents/HiPS/index.html) for more details about the HiPS standard.
|
||||
|
||||
Aladin Lite is built to be easily embeddable in any web page. It powers astronomical portals like [ESASky](https://almascience.eso.org/asax/), [ESO Science Archive portal](http://archive.eso.org/scienceportal/) and [ALMA Portal](https://almascience.eso.org/asax/).
|
||||
Aladin Lite is built to be easily embeddable in any web page. It powers astronomical portals like [ESASky](https://sky.esa.int/), [ESO Science Archive portal](http://archive.eso.org/scienceportal/) and [ALMA Portal](https://almascience.eso.org/asax/).
|
||||
|
||||
More details on [Aladin Lite documentation page](http://aladin.u-strasbg.fr/AladinLite/doc/).
|
||||
|
||||
|
||||
@@ -227,6 +227,8 @@
|
||||
|
||||
<script type="module">
|
||||
import A from '../src/js/A.js';
|
||||
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, realFullscreen: true, fov: 100, allowFullZoomout: true, showReticle: false });
|
||||
@@ -495,7 +497,7 @@
|
||||
deleteOverlayTimeout = undefined;
|
||||
}
|
||||
isDrawing = true;
|
||||
points.push([drawOverlayCanvas.relMouseCoords(e)]);
|
||||
points.push([Utils.relMouseCoords(drawOverlayCanvas.imageCanvas, e)]);
|
||||
});
|
||||
|
||||
|
||||
@@ -504,7 +506,7 @@
|
||||
e.preventDefault();
|
||||
|
||||
drawOverlayCtx.clearRect(0, 0, drawOverlayCtx.canvas.width, drawOverlayCtx.canvas.height);
|
||||
points[points.length-1].push(drawOverlayCanvas.relMouseCoords(e));
|
||||
points[points.length-1].push(Utils.relMouseCoords(drawOverlayCanvas.imageCanvas, e));
|
||||
|
||||
drawOverlayCtx.beginPath();
|
||||
|
||||
|
||||
12
package.json
12
package.json
@@ -2,7 +2,7 @@
|
||||
"homepage": "https://aladin.u-strasbg.fr/",
|
||||
"name": "aladin-lite",
|
||||
"type": "module",
|
||||
"version": "3.1.0",
|
||||
"version": "3.2.0",
|
||||
"description": "An astronomical HiPS visualizer in the browser",
|
||||
"author": "Thomas Boch and Matthieu Baumann",
|
||||
"license": "GPL-3",
|
||||
@@ -39,16 +39,20 @@
|
||||
"dev": "npm run build && vite",
|
||||
"serve": "npm run dev",
|
||||
"preview": "vite preview",
|
||||
"test": "cd src/core && cargo test --release --features webgl2"
|
||||
"test:build": "cd src/core && cargo test --release --features webgl2",
|
||||
"test:unit": "vitest run"
|
||||
},
|
||||
"devDependencies": {
|
||||
"npm": "^8.19.2",
|
||||
"happy-dom": "^8.9.0",
|
||||
"npm": "^9.8.1",
|
||||
"typescript": "^5.0.4",
|
||||
"vite": "^4.3.8",
|
||||
"vite-plugin-css-injected-by-js": "^3.1.1",
|
||||
"vite-plugin-glsl": "^1.1.2",
|
||||
"vite-plugin-top-level-await": "^1.3.1",
|
||||
"vite-plugin-wasm": "^3.2.2",
|
||||
"vite-plugin-wasm-pack": "^0.1.12"
|
||||
"vite-plugin-wasm-pack": "^0.1.12",
|
||||
"vitest": "^0.32.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"autocompleter": "^6.1.3",
|
||||
|
||||
@@ -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.1.1"
|
||||
version = "3.2.0"
|
||||
authors = ["baumannmatthieu0@gmail.com", "matthieu.baumann@astro.unistra.fr"]
|
||||
edition = "2018"
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
*****************************************************************************/
|
||||
|
||||
import { View } from "./View.js";
|
||||
import { Utils } from "./Utils.js";
|
||||
import { Utils } from "./Utils";
|
||||
import { Overlay } from "./Overlay.js";
|
||||
import { Logger } from "./Logger.js";
|
||||
import { ProgressiveCat } from "./ProgressiveCat.js";
|
||||
@@ -594,34 +594,34 @@ export let Aladin = (function () {
|
||||
|
||||
Aladin.prototype.getOptionsFromQueryString = function () {
|
||||
var options = {};
|
||||
var requestedTarget = $.urlParam('target');
|
||||
var requestedTarget = Utils.urlParam('target');
|
||||
if (requestedTarget) {
|
||||
options.target = requestedTarget;
|
||||
}
|
||||
var requestedFrame = $.urlParam('frame');
|
||||
var requestedFrame = Utils.urlParam('frame');
|
||||
if (requestedFrame && CooFrameEnum[requestedFrame]) {
|
||||
options.frame = requestedFrame;
|
||||
}
|
||||
var requestedSurveyId = $.urlParam('survey');
|
||||
var requestedSurveyId = Utils.urlParam('survey');
|
||||
if (requestedSurveyId && ImageSurvey.getSurveyInfoFromId(requestedSurveyId)) {
|
||||
options.survey = requestedSurveyId;
|
||||
}
|
||||
var requestedZoom = $.urlParam('zoom');
|
||||
var requestedZoom = Utils.urlParam('zoom');
|
||||
if (requestedZoom && requestedZoom > 0 && requestedZoom < 180) {
|
||||
options.zoom = requestedZoom;
|
||||
}
|
||||
|
||||
var requestedShowreticle = $.urlParam('showReticle');
|
||||
var requestedShowreticle = Utils.urlParam('showReticle');
|
||||
if (requestedShowreticle) {
|
||||
options.showReticle = requestedShowreticle.toLowerCase() == 'true';
|
||||
}
|
||||
|
||||
var requestedCooFrame = $.urlParam('cooFrame');
|
||||
var requestedCooFrame = Utils.urlParam('cooFrame');
|
||||
if (requestedCooFrame) {
|
||||
options.cooFrame = requestedCooFrame;
|
||||
}
|
||||
|
||||
var requestedFullscreen = $.urlParam('fullScreen');
|
||||
var requestedFullscreen = Utils.urlParam('fullScreen');
|
||||
if (requestedFullscreen !== undefined) {
|
||||
options.fullScreen = requestedFullscreen;
|
||||
}
|
||||
@@ -1786,4 +1786,4 @@ Aladin.prototype.displayJPG = Aladin.prototype.displayPNG = function (url, optio
|
||||
Aladin.prototype.setReduceDeformations = function (reduce) {
|
||||
this.reduceDeformations = reduce;
|
||||
this.view.requestRedraw();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,7 +31,7 @@
|
||||
|
||||
import { Source } from "./Source.js"
|
||||
import { Color } from "./Color.js"
|
||||
import { Utils } from "./Utils.js";
|
||||
import { Utils } from "./Utils";
|
||||
import { Coo } from "./libs/astro/coo.js";
|
||||
import { VOTable } from "./vo/VOTable.js";
|
||||
import { ALEvent } from "./events/ALEvent.js";
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
import { Utils } from "./Utils.js";
|
||||
import { Utils } from "./Utils";
|
||||
import { AladinUtils } from "./AladinUtils.js";
|
||||
import { Overlay } from "./Overlay.js";
|
||||
|
||||
|
||||
1
src/js/Constants.ts
Normal file
1
src/js/Constants.ts
Normal file
@@ -0,0 +1 @@
|
||||
export const JSONP_PROXY = "https://alaskybis.cds.unistra.fr/cgi/JSONProxy";
|
||||
@@ -28,7 +28,7 @@
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
import { Utils } from "./Utils.js";
|
||||
import { Utils } from "./Utils";
|
||||
import { AladinUtils } from "./AladinUtils.js";
|
||||
import { Overlay } from "./Overlay.js";
|
||||
|
||||
|
||||
@@ -34,7 +34,7 @@
|
||||
*****************************************************************************/
|
||||
|
||||
import { AladinUtils } from './AladinUtils.js';
|
||||
import { Utils } from './Utils.js';
|
||||
import { Utils } from './Utils';
|
||||
|
||||
export let Footprint= (function() {
|
||||
// constructor
|
||||
|
||||
@@ -7,10 +7,11 @@
|
||||
|
||||
import { SimbadPointer } from "./SimbadPointer.js";
|
||||
import { PlanetaryFeaturesPointer } from "./PlanetaryFeaturesPointer.js";
|
||||
import { Utils } from './Utils';
|
||||
|
||||
// allow to call either Simbad or Planetary features Pointers
|
||||
export let GenericPointer = (function (view, e) {
|
||||
const xymouse = view.imageCanvas.relMouseCoords(e);
|
||||
const xymouse = Utils.relMouseCoords(view.imageCanvas, e);
|
||||
let radec = view.wasm.screenToWorld(xymouse.x, xymouse.y);
|
||||
if (radec) {
|
||||
// sky case
|
||||
@@ -29,4 +30,4 @@ export let GenericPointer = (function (view, e) {
|
||||
console.log("The location you clicked on is out of the view.");
|
||||
}
|
||||
}
|
||||
)
|
||||
)
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
import { Utils } from "./Utils.js";
|
||||
import { Utils } from "./Utils";
|
||||
import $ from 'jquery';
|
||||
|
||||
export let HiPSDefinition = (function() {
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
* Authors: Thomas Boch & Matthieu Baumann [CDS]
|
||||
*
|
||||
*****************************************************************************/
|
||||
import { Utils } from "./Utils.js";
|
||||
import { Utils } from "./Utils";
|
||||
import { HiPSDefinition } from "./HiPSDefinition.js";
|
||||
import { MocServer } from "./MocServer.js";
|
||||
|
||||
@@ -202,4 +202,4 @@ HiPSProperties.getFasterMirrorUrl = function (metadata) {
|
||||
}
|
||||
})
|
||||
.then((url) => Utils.fixURLForHTTPS(url));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
import { ALEvent } from "./events/ALEvent.js";
|
||||
import { ColorCfg } from "./ColorCfg.js";
|
||||
import { ImageLayer } from "./ImageLayer.js";
|
||||
import { Utils } from "./Utils.js";
|
||||
import { Utils } from "./Utils";
|
||||
|
||||
export let ImageFITS = (function () {
|
||||
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
* Authors: Thomas Boch & Matthieu Baumann [CDS]
|
||||
*
|
||||
*****************************************************************************/
|
||||
import { Utils } from "./Utils.js";
|
||||
import { Utils } from "./Utils";
|
||||
import { ALEvent } from "./events/ALEvent.js";
|
||||
import { CooFrameEnum } from "./CooFrameEnum.js"
|
||||
import { ColorCfg } from "./ColorCfg.js";
|
||||
|
||||
@@ -10,8 +10,9 @@
|
||||
*****************************************************************************/
|
||||
|
||||
import { Aladin } from "./Aladin.js";
|
||||
import { Utils } from "./Utils.js";
|
||||
import { Utils } from "./Utils";
|
||||
import { Color } from "./Color.js";
|
||||
|
||||
import { ALEvent } from "./events/ALEvent.js";
|
||||
|
||||
export let MOC = (function() {
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
// along with Aladin Lite.
|
||||
//
|
||||
|
||||
import { Utils } from "./Utils.js";
|
||||
import { Utils } from "./Utils";
|
||||
|
||||
|
||||
/******************************************************************************
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
import { Utils } from './Utils.js';
|
||||
import { Utils } from './Utils';
|
||||
import A from "./A.js";
|
||||
|
||||
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
import { Utils } from "./Utils.js";
|
||||
import { Utils } from "./Utils";
|
||||
|
||||
import $ from 'jquery';
|
||||
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
* Author: Thomas Boch [CDS]
|
||||
*
|
||||
*****************************************************************************/
|
||||
import { Utils } from "./Utils.js";
|
||||
import { Utils } from "./Utils";
|
||||
import { AladinUtils } from "./AladinUtils.js";
|
||||
|
||||
export let PlanetaryFeaturesPointer = (function() {
|
||||
|
||||
@@ -35,7 +35,7 @@
|
||||
|
||||
import { AladinUtils } from './AladinUtils.js';
|
||||
import { Line } from './Line.js';
|
||||
import { Utils } from './Utils.js';
|
||||
import { Utils } from './Utils';
|
||||
import { Overlay } from "./Overlay.js";
|
||||
import { ProjectionEnum, projectionNames } from "./ProjectionEnum.js";
|
||||
|
||||
|
||||
@@ -31,7 +31,7 @@ import { Catalog } from "./Catalog.js";
|
||||
import { Source } from "./Source.js";
|
||||
import { Color } from "./Color.js";
|
||||
import { Coo } from "./libs/astro/coo.js";
|
||||
import { Utils } from "./Utils.js";
|
||||
import { Utils } from "./Utils";
|
||||
import { CooFrameEnum } from "./CooFrameEnum.js";
|
||||
|
||||
import $ from 'jquery';
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
import { Utils } from "./Utils.js";
|
||||
import { Utils } from "./Utils";
|
||||
|
||||
import $ from 'jquery';
|
||||
|
||||
|
||||
@@ -31,7 +31,7 @@
|
||||
*
|
||||
*****************************************************************************/
|
||||
import { Coo } from "./libs/astro/coo.js";
|
||||
import { Utils } from "./Utils.js";
|
||||
import { Utils } from "./Utils";
|
||||
import { AladinUtils } from "./AladinUtils.js";
|
||||
|
||||
export let SimbadPointer = (function() {
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
*
|
||||
*****************************************************************************/
|
||||
import { Coo } from './libs/astro/coo.js';
|
||||
import { Utils } from './Utils.js';
|
||||
import { Utils } from './Utils';
|
||||
export let URLBuilder = (function() {
|
||||
|
||||
let URLBuilder = {
|
||||
|
||||
428
src/js/Utils.js
428
src/js/Utils.js
@@ -1,428 +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 Utils
|
||||
*
|
||||
* Author: Thomas Boch[CDS]
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
import { Aladin } from "./Aladin.js";
|
||||
import $ from 'jquery';
|
||||
|
||||
export let Utils = {};
|
||||
|
||||
// list of URL domains that can be safely switched from HTTP to HTTPS
|
||||
Utils.HTTPS_WHITELIST = ['alasky.u-strasbg.fr', 'alaskybis.u-strasbg.fr', 'alasky.unistra.fr', 'alaskybis.unistra.fr',
|
||||
'alasky.cds.unistra.fr', 'alaskybis.cds.unistra.fr', 'hips.astron.nl', 'jvo.nao.ac.jp',
|
||||
'archive.cefca.es', 'cade.irap.omp.eu', 'skies.esac.esa.int'];
|
||||
|
||||
Utils.cssScale = undefined;
|
||||
// adding relMouseCoords to HTMLCanvasElement prototype (see http://stackoverflow.com/questions/55677/how-do-i-get-the-coordinates-of-a-mouse-click-on-a-canvas-element )
|
||||
function relMouseCoords(event) {
|
||||
if (event.offsetX) {
|
||||
return {x: event.offsetX, y: event.offsetY};
|
||||
}
|
||||
else {
|
||||
if (!Utils.cssScale) {
|
||||
var st = window.getComputedStyle(document.body, null);
|
||||
var tr = st.getPropertyValue("-webkit-transform") ||
|
||||
st.getPropertyValue("-moz-transform") ||
|
||||
st.getPropertyValue("-ms-transform") ||
|
||||
st.getPropertyValue("-o-transform") ||
|
||||
st.getPropertyValue("transform");
|
||||
var matrixRegex = /matrix\((-?\d*\.?\d+),\s*0,\s*0,\s*(-?\d*\.?\d+),\s*0,\s*0\)/;
|
||||
var matches = tr.match(matrixRegex);
|
||||
if (matches) {
|
||||
Utils.cssScale = parseFloat(matches[1]);
|
||||
}
|
||||
else {
|
||||
Utils.cssScale = 1;
|
||||
}
|
||||
}
|
||||
var e = event;
|
||||
var canvas = e.target;
|
||||
// http://www.jacklmoore.com/notes/mouse-position/
|
||||
var target = e.target || e.srcElement;
|
||||
var style = target.currentStyle || window.getComputedStyle(target, null);
|
||||
var borderLeftWidth = parseInt(style['borderLeftWidth'], 10);
|
||||
var borderTopWidth = parseInt(style['borderTopWidth'], 10);
|
||||
var rect = target.getBoundingClientRect();
|
||||
|
||||
var clientX = e.clientX;
|
||||
var clientY = e.clientY;
|
||||
if (e.originalEvent.changedTouches) {
|
||||
clientX = e.originalEvent.changedTouches[0].clientX;
|
||||
clientY = e.originalEvent.changedTouches[0].clientY;
|
||||
}
|
||||
|
||||
var offsetX = clientX - borderLeftWidth - rect.left;
|
||||
var offsetY = clientY - borderTopWidth - rect.top
|
||||
|
||||
return {x: parseInt(offsetX/Utils.cssScale), y: parseInt(offsetY/Utils.cssScale)};
|
||||
}
|
||||
}
|
||||
HTMLCanvasElement.prototype.relMouseCoords = relMouseCoords;
|
||||
|
||||
|
||||
|
||||
//Function.prototype.bind polyfill from
|
||||
//https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Function/bind
|
||||
if (!Function.prototype.bind) {
|
||||
Function.prototype.bind = function (obj) {
|
||||
// closest thing possible to the ECMAScript 5 internal IsCallable function
|
||||
if (typeof this !== 'function') {
|
||||
throw new TypeError('Function.prototype.bind - what is trying to be bound is not callable');
|
||||
}
|
||||
|
||||
var slice = [].slice,
|
||||
args = slice.call(arguments, 1),
|
||||
self = this,
|
||||
nop = function () { },
|
||||
bound = function () {
|
||||
return self.apply(this instanceof nop ? this : (obj || {}),
|
||||
args.concat(slice.call(arguments)));
|
||||
};
|
||||
|
||||
bound.prototype = this.prototype;
|
||||
|
||||
return bound;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
//$ = $ || jQuery;
|
||||
|
||||
/* source : http://stackoverflow.com/a/8764051 */
|
||||
$.urlParam = function(name, queryString){
|
||||
if (queryString===undefined) {
|
||||
queryString = location.search;
|
||||
}
|
||||
return decodeURIComponent((new RegExp('[?|&]' + name + '=' + '([^&;]+?)(&|#|;|$)').exec(queryString)||[,""])[1].replace(/\+/g, '%20'))||null;
|
||||
};
|
||||
|
||||
/* source: http://stackoverflow.com/a/1830844 */
|
||||
Utils.isNumber = function(n) {
|
||||
return !isNaN(parseFloat(n)) && isFinite(n);
|
||||
};
|
||||
|
||||
Utils.isInt = function(n) {
|
||||
return Utils.isNumber(n) && Math.floor(n)==n;
|
||||
};
|
||||
|
||||
/* a debounce function, used to prevent multiple calls to the same function if less than delay milliseconds have passed */
|
||||
Utils.debounce = function(fn, delay) {
|
||||
var timer = null;
|
||||
return function () {
|
||||
var context = this, args = arguments;
|
||||
clearTimeout(timer);
|
||||
timer = setTimeout(function () {
|
||||
fn.apply(context, args);
|
||||
}, delay);
|
||||
};
|
||||
};
|
||||
|
||||
/* return a throttled function, to rate limit the number of calls (by default, one call every 250 milliseconds) */
|
||||
Utils.throttle = function(fn, threshhold, scope) {
|
||||
threshhold || (threshhold = 250);
|
||||
var last,
|
||||
deferTimer;
|
||||
return function () {
|
||||
var context = scope || this;
|
||||
|
||||
var now = +new Date,
|
||||
args = arguments;
|
||||
if (last && now < last + threshhold) {
|
||||
// hold on to it
|
||||
clearTimeout(deferTimer);
|
||||
deferTimer = setTimeout(function () {
|
||||
last = now;
|
||||
fn.apply(context, args);
|
||||
}, threshhold);
|
||||
} else {
|
||||
last = now;
|
||||
fn.apply(context, args);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
/* A LRU cache, inspired by https://gist.github.com/devinus/409353#file-gistfile1-js */
|
||||
// TODO : utiliser le LRU cache pour les tuiles images
|
||||
Utils.LRUCache = function (maxsize) {
|
||||
this._keys = [];
|
||||
this._items = {};
|
||||
this._expires = {};
|
||||
this._size = 0;
|
||||
this._maxsize = maxsize || 1024;
|
||||
};
|
||||
|
||||
Utils.LRUCache.prototype = {
|
||||
set: function (key, value) {
|
||||
var keys = this._keys,
|
||||
items = this._items,
|
||||
expires = this._expires,
|
||||
size = this._size,
|
||||
maxsize = this._maxsize;
|
||||
|
||||
if (size >= maxsize) { // remove oldest element when no more room
|
||||
keys.sort(function (a, b) {
|
||||
if (expires[a] > expires[b]) return -1;
|
||||
if (expires[a] < expires[b]) return 1;
|
||||
return 0;
|
||||
});
|
||||
|
||||
size--;
|
||||
}
|
||||
|
||||
keys[size] = key;
|
||||
items[key] = value;
|
||||
expires[key] = Date.now();
|
||||
size++;
|
||||
|
||||
this._keys = keys;
|
||||
this._items = items;
|
||||
this._expires = expires;
|
||||
this._size = size;
|
||||
},
|
||||
|
||||
get: function (key) {
|
||||
var item = this._items[key];
|
||||
if (item) { this._expires[key] = Date.now(); }
|
||||
return item;
|
||||
},
|
||||
|
||||
keys: function() {
|
||||
return this._keys;
|
||||
}
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////:
|
||||
|
||||
/**
|
||||
Fetch an url with the method GET, given a list of potential mirrors
|
||||
An optional object can be given with the following keywords accepted:
|
||||
* data: an object storing the params associated to the URL
|
||||
* contentType: specify the content type returned from the url (no verification is done, it is not mandatory to put it)
|
||||
* timeout: A maximum request time. If exceeded, the request is aborted and the next url will be fetched
|
||||
This method assumes the URL are CORS-compatible, no proxy will be used
|
||||
|
||||
A promise is returned. When all the urls fail, a rejected Promise is returned so that it can be catched afterwards
|
||||
*/
|
||||
Utils.loadFromMirrors = function(urls, options) {
|
||||
const contentType = options && options.contentType || "application/json";
|
||||
const data = options && options.data || undefined;
|
||||
const timeout = options && options.timeout || 5000;
|
||||
|
||||
// Base case, when all urls have been fetched and failed
|
||||
if (urls.length === 0) {
|
||||
return Promise.reject("None of the urls given can be fetched!");
|
||||
}
|
||||
|
||||
// A controller that can abort the query when a timeout is reached
|
||||
const controller = new AbortController();
|
||||
|
||||
// Launch a timemout that will interrupt the fetch if it has not yet succeded:
|
||||
const timeoutId = setTimeout(() => controller.abort(), timeout);
|
||||
const init = {
|
||||
// *GET, POST, PUT, DELETE, etc.
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Content-Type": contentType
|
||||
},
|
||||
// no-cors, *cors, same-origin
|
||||
mode: 'cors',
|
||||
// *default, no-cache, reload, force-cache, only-if-cached
|
||||
cache: 'default',
|
||||
// manual, *follow, error
|
||||
redirect: 'follow',
|
||||
// Abort the request when a timeout exceeded
|
||||
signal: controller.signal,
|
||||
};
|
||||
|
||||
const url = urls[0] + '?' + new URLSearchParams(data);
|
||||
return fetch(url, init)
|
||||
.then((response) => {
|
||||
// completed request before timeout fired
|
||||
clearTimeout(timeoutId)
|
||||
|
||||
if (!response.ok) {
|
||||
return Promise.reject("Url: ", urls[0], " cannot be reached in some way.");
|
||||
} else {
|
||||
return response;
|
||||
}
|
||||
})
|
||||
.catch((e) => {
|
||||
// The request aborted because it was to slow, fetch the next url given recursively
|
||||
return Utils.loadFromMirrors(urls.slice(1), options);
|
||||
});
|
||||
}
|
||||
|
||||
// return the jquery ajax object configured with the requested parameters
|
||||
// by default, we use the proxy (safer, as we don't know if the remote server supports CORS)
|
||||
Utils.getAjaxObject = function(url, method, dataType, useProxy) {
|
||||
if (useProxy!==false) {
|
||||
useProxy = true;
|
||||
}
|
||||
|
||||
if (useProxy===true) {
|
||||
var urlToRequest = Aladin.JSONP_PROXY + '?url=' + encodeURIComponent(url);
|
||||
}
|
||||
else {
|
||||
urlToRequest = url;
|
||||
}
|
||||
method = method || 'GET';
|
||||
dataType = dataType || null;
|
||||
|
||||
return $.ajax({
|
||||
url: urlToRequest,
|
||||
method: method,
|
||||
dataType: dataType
|
||||
});
|
||||
};
|
||||
|
||||
// return true if script is executed in a HTTPS context
|
||||
// return false otherwise
|
||||
Utils.isFileContext = function() {
|
||||
return ( window.location.protocol === 'file:' );
|
||||
};
|
||||
|
||||
Utils.isHttpsContext = function() {
|
||||
return ( window.location.protocol === 'https:' );
|
||||
};
|
||||
|
||||
Utils.isHttpContext = function() {
|
||||
return ( window.location.protocol === 'http:' );
|
||||
};
|
||||
|
||||
Utils.fixURLForHTTPS = function(url) {
|
||||
const switchToHttps = Utils.HTTPS_WHITELIST.some(element => {
|
||||
return url.includes(element);
|
||||
});
|
||||
|
||||
if (switchToHttps) {
|
||||
return url.replace('http://', 'https://');
|
||||
}
|
||||
|
||||
return url;
|
||||
};
|
||||
|
||||
// generate an absolute URL from a relative URL
|
||||
// example: getAbsoluteURL('foo/bar/toto') return http://cds.unistra.fr/AL/foo/bar/toto if executed from page http://cds.unistra.fr/AL/
|
||||
Utils.getAbsoluteURL = function(url) {
|
||||
var a = document.createElement('a');
|
||||
a.href = url;
|
||||
|
||||
return a.href;
|
||||
};
|
||||
|
||||
// generate a valid v4 UUID
|
||||
Utils.uuidv4 = function() {
|
||||
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
|
||||
var r = Math.random() * 16 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8);
|
||||
return v.toString(16);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @function
|
||||
* @description Deep clone a class instance.
|
||||
* @param {object} instance The class instance you want to clone.
|
||||
* @returns {object} A new cloned instance.
|
||||
*/
|
||||
Utils.clone = function(instance) {
|
||||
return Object.assign(
|
||||
Object.create(
|
||||
// Set the prototype of the new object to the prototype of the instance.
|
||||
// Used to allow new object behave like class instance.
|
||||
Object.getPrototypeOf(instance),
|
||||
),
|
||||
// Prevent shallow copies of nested structures like arrays, etc
|
||||
JSON.parse(JSON.stringify(instance)),
|
||||
);
|
||||
}
|
||||
|
||||
Utils.getDroppedFilesHandler = function(ev) {
|
||||
// Prevent default behavior (Prevent file from being opened)
|
||||
ev.preventDefault();
|
||||
|
||||
let items;
|
||||
if (ev.dataTransfer.items) {
|
||||
// Use DataTransferItemList interface to access the file(s)
|
||||
items = [...ev.dataTransfer.items];
|
||||
} else {
|
||||
// Use DataTransfer interface to access the file(s)
|
||||
items = [...ev.dataTransfer.files];
|
||||
}
|
||||
|
||||
const files = items.filter((item) => {
|
||||
// If dropped items aren't files, reject them
|
||||
return item.kind === 'file';
|
||||
})
|
||||
.map((item) => item.getAsFile());
|
||||
|
||||
return files;
|
||||
}
|
||||
|
||||
Utils.dragOverHandler = function(ev) {
|
||||
// Prevent default behavior (Prevent file from being opened)
|
||||
ev.preventDefault();
|
||||
}
|
||||
|
||||
Utils.requestCORSIfNotSameOrigin = function(url) {
|
||||
return (new URL(url, window.location.href)).origin !== window.location.origin;
|
||||
}
|
||||
|
||||
// Check the protocol, for http ones, use a CORS compatible proxy
|
||||
Utils.handleCORSNotSameOrigin = function(url) {
|
||||
if (Utils.requestCORSIfNotSameOrigin(url)) {
|
||||
// http(s) protocols and not in localhost
|
||||
let proxiedUrl = new URL(Aladin.JSONP_PROXY);
|
||||
proxiedUrl.searchParams.append("url", url);
|
||||
|
||||
url = proxiedUrl;
|
||||
}
|
||||
|
||||
return url;
|
||||
}
|
||||
|
||||
Utils.deepCopy = function(orig) {
|
||||
return Object.assign(Object.create(Object.getPrototypeOf(orig)), orig);
|
||||
}
|
||||
|
||||
Utils.download = function(url, name = undefined) {
|
||||
const a = document.createElement('a')
|
||||
a.href = url
|
||||
if (name) {
|
||||
a.download = name;
|
||||
}
|
||||
a.click()
|
||||
}
|
||||
413
src/js/Utils.ts
Normal file
413
src/js/Utils.ts
Normal file
@@ -0,0 +1,413 @@
|
||||
// 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 Utils
|
||||
*
|
||||
* Author: Thomas Boch[CDS]
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
import {JSONP_PROXY} from "@/js/Constants";
|
||||
|
||||
export let Utils: any = {}
|
||||
|
||||
// list of URL domains that can be safely switched from HTTP to HTTPS
|
||||
Utils.HTTPS_WHITELIST = ['alasky.u-strasbg.fr', 'alaskybis.u-strasbg.fr', 'alasky.unistra.fr', 'alaskybis.unistra.fr',
|
||||
'alasky.cds.unistra.fr', 'alaskybis.cds.unistra.fr', 'hips.astron.nl', 'jvo.nao.ac.jp',
|
||||
'archive.cefca.es', 'cade.irap.omp.eu', 'skies.esac.esa.int']
|
||||
|
||||
Utils.cssScale = undefined
|
||||
|
||||
Utils.relMouseCoords = function (canvas: HTMLCanvasElement, event: MouseEvent) {
|
||||
if (event.offsetX) {
|
||||
return {x: event.offsetX, y: event.offsetY}
|
||||
} else {
|
||||
if (!Utils.cssScale) {
|
||||
var st = window.getComputedStyle(document.body, null)
|
||||
var tr = st.getPropertyValue('-webkit-transform') ||
|
||||
st.getPropertyValue('-moz-transform') ||
|
||||
st.getPropertyValue('-ms-transform') ||
|
||||
st.getPropertyValue('-o-transform') ||
|
||||
st.getPropertyValue('transform')
|
||||
var matrixRegex = /matrix\((-?\d*\.?\d+),\s*0,\s*0,\s*(-?\d*\.?\d+),\s*0,\s*0\)/
|
||||
var matches = tr.match(matrixRegex)
|
||||
if (matches) {
|
||||
Utils.cssScale = parseFloat(matches[1])
|
||||
} else {
|
||||
Utils.cssScale = 1
|
||||
}
|
||||
}
|
||||
var e = event
|
||||
// http://www.jacklmoore.com/notes/mouse-position/
|
||||
var target = e.target || e.srcElement
|
||||
var style = target.currentStyle || window.getComputedStyle(target, null)
|
||||
var borderLeftWidth = parseInt(style['borderLeftWidth'], 10)
|
||||
var borderTopWidth = parseInt(style['borderTopWidth'], 10)
|
||||
var rect = target.getBoundingClientRect()
|
||||
|
||||
var clientX = e.clientX
|
||||
var clientY = e.clientY
|
||||
if (e.originalEvent.changedTouches) {
|
||||
clientX = e.originalEvent.changedTouches[0].clientX
|
||||
clientY = e.originalEvent.changedTouches[0].clientY
|
||||
}
|
||||
|
||||
var offsetX = clientX - borderLeftWidth - rect.left
|
||||
var offsetY = clientY - borderTopWidth - rect.top
|
||||
|
||||
return {x: Math.round(offsetX / Utils.cssScale), y: Math.round(offsetY / Utils.cssScale)}
|
||||
}
|
||||
}
|
||||
|
||||
//Function.prototype.bind polyfill from
|
||||
//https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Function/bind
|
||||
if (!Function.prototype.bind) {
|
||||
Function.prototype.bind = function (obj) {
|
||||
// closest thing possible to the ECMAScript 5 internal IsCallable function
|
||||
if (typeof this !== 'function') {
|
||||
throw new TypeError('Function.prototype.bind - what is trying to be bound is not callable')
|
||||
}
|
||||
|
||||
var slice = [].slice,
|
||||
args = slice.call(arguments, 1),
|
||||
self = this,
|
||||
nop = function () {
|
||||
},
|
||||
bound = function () {
|
||||
return self.apply(this instanceof nop ? this : (obj || {}),
|
||||
args.concat(slice.call(arguments)))
|
||||
}
|
||||
|
||||
bound.prototype = this.prototype
|
||||
|
||||
return bound
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* source : http://stackoverflow.com/a/8764051 */
|
||||
Utils.urlParam = function (name: string, queryString: string | undefined) {
|
||||
if (queryString === undefined) {
|
||||
queryString = location.search
|
||||
}
|
||||
return decodeURIComponent((new RegExp('[?|&]' + name + '=' + '([^&;]+?)(&|#|;|$)').exec(queryString) || [, ''])[1].replace(/\+/g, '%20')) || null
|
||||
}
|
||||
|
||||
/* source: http://stackoverflow.com/a/1830844 */
|
||||
Utils.isNumber = function (n: string | number) {
|
||||
return !isNaN(parseFloat(n as string)) && isFinite(n as number)
|
||||
}
|
||||
|
||||
Utils.isInt = function (n: string | number) {
|
||||
return Utils.isNumber(n) && Math.floor(n as number) === n
|
||||
}
|
||||
|
||||
/* a debounce function, used to prevent multiple calls to the same function if less than delay milliseconds have passed */
|
||||
Utils.debounce = function (fn, delay) {
|
||||
var timer = null
|
||||
return function () {
|
||||
var context = this, args = arguments
|
||||
clearTimeout(timer)
|
||||
timer = setTimeout(function () {
|
||||
fn.apply(context, args)
|
||||
}, delay)
|
||||
}
|
||||
}
|
||||
|
||||
/* return a throttled function, to rate limit the number of calls (by default, one call every 250 milliseconds) */
|
||||
Utils.throttle = function (fn, threshhold, scope) {
|
||||
threshhold || (threshhold = 250)
|
||||
var last,
|
||||
deferTimer
|
||||
return function () {
|
||||
var context = scope || this
|
||||
|
||||
var now = +new Date,
|
||||
args = arguments
|
||||
if (last && now < last + threshhold) {
|
||||
// hold on to it
|
||||
clearTimeout(deferTimer)
|
||||
deferTimer = setTimeout(function () {
|
||||
last = now
|
||||
fn.apply(context, args)
|
||||
}, threshhold)
|
||||
} else {
|
||||
last = now
|
||||
fn.apply(context, args)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* A LRU cache, inspired by https://gist.github.com/devinus/409353#file-gistfile1-js */
|
||||
// TODO : utiliser le LRU cache pour les tuiles images
|
||||
Utils.LRUCache = function (maxsize) {
|
||||
this._keys = []
|
||||
this._items = {}
|
||||
this._expires = {}
|
||||
this._size = 0
|
||||
this._maxsize = maxsize || 1024
|
||||
}
|
||||
|
||||
Utils.LRUCache.prototype = {
|
||||
set: function (key, value) {
|
||||
var keys = this._keys,
|
||||
items = this._items,
|
||||
expires = this._expires,
|
||||
size = this._size,
|
||||
maxsize = this._maxsize
|
||||
|
||||
if (size >= maxsize) { // remove oldest element when no more room
|
||||
keys.sort(function (a, b) {
|
||||
if (expires[a] > expires[b]) return -1
|
||||
if (expires[a] < expires[b]) return 1
|
||||
return 0
|
||||
})
|
||||
|
||||
size--
|
||||
}
|
||||
|
||||
keys[size] = key
|
||||
items[key] = value
|
||||
expires[key] = Date.now()
|
||||
size++
|
||||
|
||||
this._keys = keys
|
||||
this._items = items
|
||||
this._expires = expires
|
||||
this._size = size
|
||||
},
|
||||
|
||||
get: function (key) {
|
||||
var item = this._items[key]
|
||||
if (item) {
|
||||
this._expires[key] = Date.now()
|
||||
}
|
||||
return item
|
||||
},
|
||||
|
||||
keys: function () {
|
||||
return this._keys
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////:
|
||||
|
||||
/**
|
||||
Fetch an url with the method GET, given a list of potential mirrors
|
||||
An optional object can be given with the following keywords accepted:
|
||||
* data: an object storing the params associated to the URL
|
||||
* contentType: specify the content type returned from the url (no verification is done, it is not mandatory to put it)
|
||||
* timeout: A maximum request time. If exceeded, the request is aborted and the next url will be fetched
|
||||
This method assumes the URL are CORS-compatible, no proxy will be used
|
||||
|
||||
A promise is returned. When all the urls fail, a rejected Promise is returned so that it can be catched afterwards
|
||||
*/
|
||||
Utils.loadFromMirrors = function (urls, options) {
|
||||
const contentType = options && options.contentType || 'application/json'
|
||||
const data = options && options.data || undefined
|
||||
const timeout = options && options.timeout || 5000
|
||||
|
||||
// Base case, when all urls have been fetched and failed
|
||||
if (urls.length === 0) {
|
||||
return Promise.reject('None of the urls given can be fetched!')
|
||||
}
|
||||
|
||||
// A controller that can abort the query when a timeout is reached
|
||||
const controller = new AbortController()
|
||||
|
||||
// Launch a timemout that will interrupt the fetch if it has not yet succeded:
|
||||
const timeoutId = setTimeout(() => controller.abort(), timeout)
|
||||
const init = {
|
||||
// *GET, POST, PUT, DELETE, etc.
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'Content-Type': contentType
|
||||
},
|
||||
// no-cors, *cors, same-origin
|
||||
mode: 'cors',
|
||||
// *default, no-cache, reload, force-cache, only-if-cached
|
||||
cache: 'default',
|
||||
// manual, *follow, error
|
||||
redirect: 'follow',
|
||||
// Abort the request when a timeout exceeded
|
||||
signal: controller.signal,
|
||||
}
|
||||
|
||||
const url = urls[0] + '?' + new URLSearchParams(data)
|
||||
return fetch(url, init)
|
||||
.then((response) => {
|
||||
// completed request before timeout fired
|
||||
clearTimeout(timeoutId)
|
||||
|
||||
if (!response.ok) {
|
||||
return Promise.reject('Url: ', urls[0], ' cannot be reached in some way.')
|
||||
} else {
|
||||
return response
|
||||
}
|
||||
})
|
||||
.catch((e) => {
|
||||
// The request aborted because it was to slow, fetch the next url given recursively
|
||||
return Utils.loadFromMirrors(urls.slice(1), options)
|
||||
})
|
||||
}
|
||||
|
||||
// return the jquery ajax object configured with the requested parameters
|
||||
// by default, we use the proxy (safer, as we don't know if the remote server supports CORS)
|
||||
Utils.getAjaxObject = function (url, method, dataType, useProxy) {
|
||||
if (useProxy !== false) {
|
||||
useProxy = true
|
||||
}
|
||||
|
||||
if (useProxy === true) {
|
||||
var urlToRequest = JSONP_PROXY + '?url=' + encodeURIComponent(url)
|
||||
} else {
|
||||
urlToRequest = url
|
||||
}
|
||||
method = method || 'GET'
|
||||
dataType = dataType || null
|
||||
|
||||
return $.ajax({
|
||||
url: urlToRequest,
|
||||
method: method,
|
||||
dataType: dataType
|
||||
})
|
||||
}
|
||||
|
||||
// return true if script is executed in a HTTPS context
|
||||
// return false otherwise
|
||||
Utils.isFileContext = function () {
|
||||
return (window.location.protocol === 'file:')
|
||||
}
|
||||
|
||||
Utils.isHttpsContext = function () {
|
||||
return (window.location.protocol === 'https:')
|
||||
}
|
||||
|
||||
Utils.isHttpContext = function () {
|
||||
return (window.location.protocol === 'http:')
|
||||
}
|
||||
|
||||
Utils.fixURLForHTTPS = function (url) {
|
||||
const switchToHttps = Utils.HTTPS_WHITELIST.some(element => {
|
||||
return url.includes(element)
|
||||
})
|
||||
|
||||
if (switchToHttps) {
|
||||
return url.replace('http://', 'https://')
|
||||
}
|
||||
|
||||
return url
|
||||
}
|
||||
|
||||
// generate an absolute URL from a relative URL
|
||||
// example: getAbsoluteURL('foo/bar/toto') return http://cds.unistra.fr/AL/foo/bar/toto if executed from page http://cds.unistra.fr/AL/
|
||||
Utils.getAbsoluteURL = function (url) {
|
||||
var a = document.createElement('a')
|
||||
a.href = url
|
||||
|
||||
return a.href
|
||||
}
|
||||
|
||||
// generate a valid v4 UUID
|
||||
Utils.uuidv4 = function () {
|
||||
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
|
||||
var r = Math.random() * 16 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8)
|
||||
return v.toString(16)
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* @function
|
||||
* @description Deep clone a class instance.
|
||||
* @param {object} instance The class instance you want to clone.
|
||||
* @returns {object} A new cloned instance.
|
||||
*/
|
||||
Utils.clone = function (instance) {
|
||||
return Object.assign(
|
||||
Object.create(
|
||||
// Set the prototype of the new object to the prototype of the instance.
|
||||
// Used to allow new object behave like class instance.
|
||||
Object.getPrototypeOf(instance),
|
||||
),
|
||||
// Prevent shallow copies of nested structures like arrays, etc
|
||||
JSON.parse(JSON.stringify(instance)),
|
||||
)
|
||||
}
|
||||
|
||||
Utils.getDroppedFilesHandler = function (ev) {
|
||||
// Prevent default behavior (Prevent file from being opened)
|
||||
ev.preventDefault()
|
||||
|
||||
let items
|
||||
if (ev.dataTransfer.items) {
|
||||
// Use DataTransferItemList interface to access the file(s)
|
||||
items = [...ev.dataTransfer.items]
|
||||
} else {
|
||||
// Use DataTransfer interface to access the file(s)
|
||||
items = [...ev.dataTransfer.files]
|
||||
}
|
||||
|
||||
const files = items.filter((item) => {
|
||||
// If dropped items aren't files, reject them
|
||||
return item.kind === 'file'
|
||||
})
|
||||
.map((item) => item.getAsFile())
|
||||
|
||||
return files
|
||||
}
|
||||
|
||||
Utils.dragOverHandler = function (ev) {
|
||||
// Prevent default behavior (Prevent file from being opened)
|
||||
ev.preventDefault()
|
||||
}
|
||||
|
||||
Utils.requestCORSIfNotSameOrigin = function (url) {
|
||||
return (new URL(url, window.location.href)).origin !== window.location.origin
|
||||
}
|
||||
|
||||
// Check the protocol, for http ones, use a CORS compatible proxy
|
||||
Utils.handleCORSNotSameOrigin = function (url) {
|
||||
if (Utils.requestCORSIfNotSameOrigin(url)) {
|
||||
// http(s) protocols and not in localhost
|
||||
let proxiedUrl = new URL(JSONP_PROXY)
|
||||
proxiedUrl.searchParams.append('url', url)
|
||||
|
||||
url = proxiedUrl
|
||||
}
|
||||
|
||||
return url
|
||||
}
|
||||
|
||||
Utils.deepCopy = function (orig) {
|
||||
return Object.assign(Object.create(Object.getPrototypeOf(orig)), orig)
|
||||
}
|
||||
|
||||
Utils.download = function(url, name = undefined) {
|
||||
const a = document.createElement('a')
|
||||
a.href = url
|
||||
if (name) {
|
||||
a.download = name;
|
||||
}
|
||||
a.click()
|
||||
}
|
||||
@@ -34,7 +34,7 @@ import A from "./A.js";
|
||||
import { Popup } from "./Popup.js";
|
||||
import { HealpixGrid } from "./HealpixGrid.js";
|
||||
import { ProjectionEnum } from "./ProjectionEnum.js";
|
||||
import { Utils } from "./Utils.js";
|
||||
import { Utils } from "./Utils";
|
||||
import { GenericPointer } from "./GenericPointer.js";
|
||||
import { Stats } from "./libs/Stats.js";
|
||||
import { Circle } from "./Circle.js";
|
||||
@@ -461,7 +461,7 @@ export let View = (function () {
|
||||
|
||||
// various listeners
|
||||
let onDblClick = function (e) {
|
||||
var xymouse = view.imageCanvas.relMouseCoords(e);
|
||||
const xymouse = Utils.relMouseCoords(view.imageCanvas, e);
|
||||
|
||||
// deselect all the selected sources with Select panel
|
||||
view.deselectObjects()
|
||||
@@ -494,7 +494,7 @@ export let View = (function () {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
|
||||
var xymouse = view.imageCanvas.relMouseCoords(e);
|
||||
const xymouse = Utils.relMouseCoords(view.imageCanvas, e);
|
||||
|
||||
if (e.which === 3 || e.button === 2) {
|
||||
view.rightClick = true;
|
||||
@@ -660,7 +660,7 @@ export let View = (function () {
|
||||
|
||||
view.mustClearCatalog = true;
|
||||
view.dragx = view.dragy = null;
|
||||
const xymouse = view.imageCanvas.relMouseCoords(e);
|
||||
const xymouse = Utils.relMouseCoords(view.imageCanvas, e);
|
||||
|
||||
if (e.type === "mouseout" || e.type === "touchend" || e.type === "touchcancel") {
|
||||
view.updateLocation(xymouse.x, xymouse.y, true);
|
||||
@@ -766,7 +766,7 @@ export let View = (function () {
|
||||
var lastMouseMovePos = null;
|
||||
$(view.catalogCanvas).bind("mousemove touchmove", function (e) {
|
||||
e.preventDefault();
|
||||
var xymouse = view.imageCanvas.relMouseCoords(e);
|
||||
const xymouse = Utils.relMouseCoords(view.imageCanvas, e);
|
||||
|
||||
if (view.rightClick) {
|
||||
var onRightClickMoveFunction = view.aladin.callbacksByEventName['rightClickMove'];
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
//
|
||||
|
||||
import { MocServer } from "../MocServer.js";
|
||||
import { Utils } from "../Utils.js";
|
||||
import { Utils } from "../Utils";
|
||||
import autocomplete from 'autocompleter';
|
||||
|
||||
import $ from 'jquery';
|
||||
|
||||
@@ -30,8 +30,9 @@
|
||||
*
|
||||
*****************************************************************************/
|
||||
|
||||
import { Coo } from "../libs/astro/coo.js";
|
||||
import { CooFrameEnum } from "../CooFrameEnum.js";
|
||||
import { Coo } from '../libs/astro/coo.js';
|
||||
import { CooFrameEnum } from '../CooFrameEnum.js';
|
||||
import { Utils } from '../Utils';
|
||||
|
||||
export class ContextMenu {
|
||||
|
||||
@@ -42,11 +43,11 @@ export class ContextMenu {
|
||||
|
||||
_hideMenu(e) {
|
||||
//if (e === true || !this.contextMenuUl.contains(e.target)) {
|
||||
this.contextMenuUl.remove();
|
||||
document.removeEventListener('click', this._hideMenu);
|
||||
window.removeEventListener('resize', this._hideOnResize);
|
||||
this.contextMenuUl.remove();
|
||||
document.removeEventListener('click', this._hideMenu);
|
||||
window.removeEventListener('resize', this._hideOnResize);
|
||||
|
||||
this.isShowing = false;
|
||||
this.isShowing = false;
|
||||
//}
|
||||
}
|
||||
|
||||
@@ -57,31 +58,27 @@ export class ContextMenu {
|
||||
_attachOption(target, opt, xymouse) {
|
||||
const item = document.createElement('li');
|
||||
item.className = 'aladin-context-menu-item';
|
||||
if (opt.label=='Copy position') {
|
||||
if (opt.label == 'Copy position') {
|
||||
try {
|
||||
const pos = this.aladin.pix2world(xymouse.x, xymouse.y);
|
||||
const coo = new Coo(pos[0], pos[1], 6);
|
||||
let posStr;
|
||||
if (this.aladin.view.cooFrame == CooFrameEnum.J2000) {
|
||||
posStr = coo.format('s/');
|
||||
}
|
||||
else if (this.aladin.view.cooFrame == CooFrameEnum.J2000d) {
|
||||
} else if (this.aladin.view.cooFrame == CooFrameEnum.J2000d) {
|
||||
posStr = coo.format('d/');
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
posStr = coo.format('d/');
|
||||
}
|
||||
item.innerHTML = '<span>' + posStr + '</span>';
|
||||
}
|
||||
catch(e) {
|
||||
} catch (e) {
|
||||
item.innerHTML = '<span></span>';
|
||||
}
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
item.innerHTML = '<span>' + opt.label + '</span>';
|
||||
}
|
||||
|
||||
if (opt.subMenu && opt.subMenu.length>0) {
|
||||
if (opt.subMenu && opt.subMenu.length > 0) {
|
||||
item.innerHTML += '<span style="position: absolute; right: 4px;">▶</span>';
|
||||
}
|
||||
|
||||
@@ -89,10 +86,9 @@ export class ContextMenu {
|
||||
item.addEventListener('click', e => {
|
||||
e.stopPropagation();
|
||||
if (!opt.subMenu || opt.subMenu.length === 0) {
|
||||
if (opt.label=='Copy position') {
|
||||
if (opt.label == 'Copy position') {
|
||||
opt.action(e);
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
opt.action(this.event);
|
||||
}
|
||||
self._hideMenu(true);
|
||||
@@ -105,7 +101,7 @@ export class ContextMenu {
|
||||
const subMenu = document.createElement('ul');
|
||||
subMenu.className = 'aladin-context-sub-menu';
|
||||
item.appendChild(subMenu);
|
||||
opt.subMenu.forEach(subOpt => this._attachOption(subMenu, subOpt))
|
||||
opt.subMenu.forEach(subOpt => this._attachOption(subMenu, subOpt));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -115,9 +111,9 @@ export class ContextMenu {
|
||||
this.contextMenuUl.className = 'aladin-context-menu';
|
||||
this.contextMenuUl.innerHTML = '';
|
||||
|
||||
const xymouse = this.aladin.view.imageCanvas.relMouseCoords(e);
|
||||
const xymouse = Utils.relMouseCoords(view.imageCanvas, e);
|
||||
|
||||
this.menuOptions.forEach(opt => this._attachOption(this.contextMenuUl, opt, xymouse))
|
||||
this.menuOptions.forEach(opt => this._attachOption(this.contextMenuUl, opt, xymouse));
|
||||
document.body.appendChild(this.contextMenuUl);
|
||||
|
||||
const { innerWidth, innerHeight } = window;
|
||||
@@ -154,7 +150,6 @@ export class ContextMenu {
|
||||
}
|
||||
|
||||
|
||||
|
||||
attachTo(el, options) {
|
||||
this.contextMenuUl = document.createElement('ul');
|
||||
this.menuOptions = options;
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
|
||||
import A from "../A.js";
|
||||
import { MocServer } from "../MocServer.js";
|
||||
import { Utils } from "../Utils.js";
|
||||
import { Utils } from "../Utils";
|
||||
import autocomplete from 'autocompleter';
|
||||
|
||||
import $ from 'jquery';
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
*****************************************************************************/
|
||||
|
||||
import { VOTable } from "./VOTable.js";
|
||||
import { Utils } from './../Utils.js';
|
||||
import { Utils } from './../Utils';
|
||||
|
||||
export let Datalink = (function() {
|
||||
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
*****************************************************************************/
|
||||
import { VOTable } from "./VOTable.js";
|
||||
import { Datalink } from "./Datalink.js";
|
||||
import { Utils } from '../Utils.js';
|
||||
import { Utils } from '../Utils';
|
||||
|
||||
export let ObsCore = (function() {
|
||||
|
||||
@@ -241,4 +241,3 @@
|
||||
|
||||
return ObsCore;
|
||||
})();
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
import { ALEvent } from "../events/ALEvent.js";
|
||||
import { Catalog } from "../Catalog.js";
|
||||
import { ObsCore } from "./ObsCore.js";
|
||||
import { Utils } from "./../Utils.js";
|
||||
import { Utils } from "./../Utils";
|
||||
|
||||
export let VOTable = (function() {
|
||||
|
||||
|
||||
13
tests/unit/Utils.spec.js
Normal file
13
tests/unit/Utils.spec.js
Normal file
@@ -0,0 +1,13 @@
|
||||
import {Utils} from '@/js/Utils.ts';
|
||||
|
||||
describe('Utils.ts', () => {
|
||||
beforeEach(() => {
|
||||
delete window.location;
|
||||
window.location = {href: {}, search: ''};
|
||||
});
|
||||
|
||||
it('correctly parse a location parameter', () => {
|
||||
window.location.search = '?survey=DSS';
|
||||
expect(Utils.urlParam('survey')).toEqual('DSS');
|
||||
});
|
||||
});
|
||||
45
tsconfig.json
Normal file
45
tsconfig.json
Normal file
@@ -0,0 +1,45 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "esnext",
|
||||
"useDefineForClassFields": true,
|
||||
"module": "esnext",
|
||||
"moduleResolution": "node",
|
||||
"strict": true,
|
||||
"jsx": "preserve",
|
||||
"sourceMap": true,
|
||||
"resolveJsonModule": true,
|
||||
"esModuleInterop": true,
|
||||
"skipLibCheck": true,
|
||||
"isolatedModules": true,
|
||||
"noEmit": true,
|
||||
"lib": [
|
||||
"esnext",
|
||||
"dom"
|
||||
],
|
||||
"types": [
|
||||
"node",
|
||||
"vite/client",
|
||||
"vitest/globals",
|
||||
"vitest/importMeta",
|
||||
],
|
||||
"typeRoots": [
|
||||
"node_modules/@types",
|
||||
"src/types"
|
||||
],
|
||||
"baseUrl": "./",
|
||||
"paths": {
|
||||
"@/*": [
|
||||
"./src/*"
|
||||
],
|
||||
"#/*": [
|
||||
"./tests/unit/*"
|
||||
]
|
||||
}
|
||||
},
|
||||
"include": [
|
||||
"src/**/*.ts",
|
||||
"src/**/*.d.ts",
|
||||
"src/**/*.tsx",
|
||||
"tests/unit/**/*.ts"
|
||||
]
|
||||
}
|
||||
@@ -1,16 +1,14 @@
|
||||
import { resolve } from 'path'
|
||||
import { defineConfig } from 'vite';
|
||||
|
||||
// plugins
|
||||
/// <reference types="vitest" />
|
||||
import * as path from 'path'
|
||||
import {resolve} from 'path'
|
||||
import {defineConfig} from 'vite';
|
||||
// For wasm inclusion
|
||||
import wasm from "vite-plugin-wasm";
|
||||
import topLevelAwait from "vite-plugin-top-level-await";
|
||||
// For wasm genrated by wasm-pack
|
||||
// For wasm generated by wasm-pack
|
||||
import wasmPack from 'vite-plugin-wasm-pack';
|
||||
|
||||
// To include and minify glsl into the bundle
|
||||
import glsl from 'vite-plugin-glsl';
|
||||
|
||||
// To include css into the bundle
|
||||
import cssInjectedByJsPlugin from 'vite-plugin-css-injected-by-js'
|
||||
|
||||
@@ -41,7 +39,24 @@ export default defineConfig({
|
||||
}),
|
||||
cssInjectedByJsPlugin(),
|
||||
],
|
||||
resolve: {
|
||||
alias: [
|
||||
{find: '@', replacement: path.resolve(__dirname, '/src')},
|
||||
{find: '#', replacement: path.resolve(__dirname, '/tests/unit')},
|
||||
{find: '$', replacement: path.resolve(__dirname, '/tests/e2e')}
|
||||
],
|
||||
},
|
||||
test: {
|
||||
globals: true,
|
||||
environment: 'happy-dom',
|
||||
include: [
|
||||
'tests/unit/**/*.{test,spec}.{js,ts}'
|
||||
],
|
||||
deps: {
|
||||
inline: ['core/pkg'],
|
||||
},
|
||||
},
|
||||
server: {
|
||||
open: '/examples/index.html',
|
||||
},
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user