mirror of
https://github.com/cds-astro/aladin-lite.git
synced 2026-01-15 22:42:45 -08:00
677 lines
22 KiB
JavaScript
677 lines
22 KiB
JavaScript
// 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 Catalog
|
|
*
|
|
* Author: Thomas Boch[CDS]
|
|
*
|
|
*****************************************************************************/
|
|
|
|
import { Source } from "./Source.js"
|
|
import { Color } from "./Color.js"
|
|
import { Utils } from "./Utils.js";
|
|
import { AladinUtils } from "./AladinUtils.js";
|
|
import { Coo } from "./libs/astro/coo.js";
|
|
import { VOTable } from "./vo/VOTable.js";
|
|
import { ALEvent } from "./events/ALEvent.js";
|
|
|
|
import $ from 'jquery';
|
|
|
|
// TODO : harmoniser parsing avec classe ProgressiveCat
|
|
export let Catalog = (function() {
|
|
|
|
function Catalog(options) {
|
|
let self = this;
|
|
options = options || {};
|
|
|
|
this.uuid = Utils.uuidv4();
|
|
this.type = 'catalog';
|
|
this.name = options.name || "catalog";
|
|
this.color = options.color || Color.getNextColor();
|
|
this.sourceSize = options.sourceSize || 8;
|
|
this.markerSize = options.sourceSize || 12;
|
|
this.selectSize = this.sourceSize;
|
|
this.shape = options.shape || "square";
|
|
this.maxNbSources = options.limit || undefined;
|
|
this.onClick = options.onClick || undefined;
|
|
|
|
this.raField = options.raField || undefined; // ID or name of the field holding RA
|
|
this.decField = options.decField || undefined; // ID or name of the field holding dec
|
|
|
|
this.fieldsClickedActions = {}; // callbacks when the user clicks on a cell in the measurement table associated
|
|
this.fields = undefined;
|
|
|
|
this.indexationNorder = 5; // à quel niveau indexe-t-on les sources
|
|
this.sources = [];
|
|
this.ra = [];
|
|
this.dec = [];
|
|
this.footprints = [];
|
|
|
|
this.displayLabel = options.displayLabel || false;
|
|
this.labelColor = options.labelColor || this.color;
|
|
this.labelFont = options.labelFont || '10px sans-serif';
|
|
if (this.displayLabel) {
|
|
this.labelColumn = options.labelColumn;
|
|
if (!this.labelColumn) {
|
|
this.displayLabel = false;
|
|
}
|
|
}
|
|
|
|
if (this.shape instanceof Image || this.shape instanceof HTMLCanvasElement) {
|
|
this.sourceSize = this.shape.width;
|
|
}
|
|
this._shapeIsFunction = false; // if true, the shape is a function drawing on the canvas
|
|
if (typeof this.shape === 'function') {
|
|
this._shapeIsFunction = true;
|
|
}
|
|
|
|
this.selectionColor = '#00ff00';
|
|
|
|
|
|
// create this.cacheCanvas
|
|
// cacheCanvas permet de ne créer le path de la source qu'une fois, et de le réutiliser (cf. http://simonsarris.com/blog/427-increasing-performance-by-caching-paths-on-canvas)
|
|
this.updateShape(options);
|
|
|
|
this.cacheMarkerCanvas = document.createElement('canvas');
|
|
this.cacheMarkerCanvas.width = this.markerSize;
|
|
this.cacheMarkerCanvas.height = this.markerSize;
|
|
var cacheMarkerCtx = this.cacheMarkerCanvas.getContext('2d');
|
|
cacheMarkerCtx.fillStyle = this.color;
|
|
cacheMarkerCtx.beginPath();
|
|
var half = (this.markerSize)/2.;
|
|
cacheMarkerCtx.arc(half, half, half-2, 0, 2 * Math.PI, false);
|
|
cacheMarkerCtx.fill();
|
|
cacheMarkerCtx.lineWidth = 2;
|
|
cacheMarkerCtx.strokeStyle = '#ccc';
|
|
cacheMarkerCtx.stroke();
|
|
|
|
this.isShowing = true;
|
|
};
|
|
|
|
Catalog.createShape = function(shapeName, color, sourceSize) {
|
|
if (shapeName instanceof Image || shapeName instanceof HTMLCanvasElement) { // in this case, the shape is already created
|
|
return shapeName;
|
|
}
|
|
var c = document.createElement('canvas');
|
|
c.width = c.height = sourceSize;
|
|
var ctx= c.getContext('2d');
|
|
ctx.beginPath();
|
|
ctx.strokeStyle = color;
|
|
ctx.lineWidth = 2.0;
|
|
if (shapeName=="plus") {
|
|
ctx.moveTo(sourceSize/2., 0);
|
|
ctx.lineTo(sourceSize/2., sourceSize);
|
|
ctx.stroke();
|
|
|
|
ctx.moveTo(0, sourceSize/2.);
|
|
ctx.lineTo(sourceSize, sourceSize/2.);
|
|
ctx.stroke();
|
|
}
|
|
else if (shapeName=="cross") {
|
|
ctx.moveTo(0, 0);
|
|
ctx.lineTo(sourceSize-1, sourceSize-1);
|
|
ctx.stroke();
|
|
|
|
ctx.moveTo(sourceSize-1, 0);
|
|
ctx.lineTo(0, sourceSize-1);
|
|
ctx.stroke();
|
|
}
|
|
else if (shapeName=="rhomb") {
|
|
ctx.moveTo(sourceSize/2, 0);
|
|
ctx.lineTo(0, sourceSize/2);
|
|
ctx.lineTo(sourceSize/2, sourceSize);
|
|
ctx.lineTo(sourceSize, sourceSize/2);
|
|
ctx.lineTo(sourceSize/2, 0);
|
|
ctx.stroke();
|
|
}
|
|
else if (shapeName=="triangle") {
|
|
ctx.moveTo(sourceSize/2, 0);
|
|
ctx.lineTo(0, sourceSize-1);
|
|
ctx.lineTo(sourceSize-1, sourceSize-1);
|
|
ctx.lineTo(sourceSize/2, 0);
|
|
ctx.stroke();
|
|
}
|
|
else if (shapeName=="circle") {
|
|
ctx.arc(sourceSize/2, sourceSize/2, sourceSize/2 - 1, 0, 2*Math.PI, true);
|
|
ctx.stroke();
|
|
}
|
|
else { // default shape: square
|
|
ctx.moveTo(1, 0);
|
|
ctx.lineTo(1, sourceSize-1);
|
|
ctx.lineTo( sourceSize-1, sourceSize-1);
|
|
ctx.lineTo( sourceSize-1, 1);
|
|
ctx.lineTo(1, 1);
|
|
ctx.stroke();
|
|
}
|
|
|
|
return c;
|
|
};
|
|
|
|
|
|
// find RA, Dec fields among the given fields
|
|
//
|
|
// @param fields: list of objects with ucd, unit, ID, name attributes
|
|
// @param raField: index or name of right ascension column (might be undefined)
|
|
// @param decField: index or name of declination column (might be undefined)
|
|
//
|
|
function findRADecFields(fields, raField, decField) {
|
|
var raFieldIdx, decFieldIdx;
|
|
raFieldIdx = decFieldIdx = null;
|
|
|
|
// first, look if RA/DEC fields have been already given
|
|
if (raField) { // ID or name of RA field given at catalogue creation
|
|
for (var l=0, len=fields.length; l<len; l++) {
|
|
var field = fields[l];
|
|
if (Utils.isInt(raField) && raField<fields.length) { // raField can be given as an index
|
|
raFieldIdx = raField;
|
|
break;
|
|
}
|
|
if ( (field.ID && field.ID===raField) || (field.name && field.name===raField)) {
|
|
raFieldIdx = l;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (decField) { // ID or name of dec field given at catalogue creation
|
|
for (var l=0, len=fields.length; l<len; l++) {
|
|
var field = fields[l];
|
|
if (Utils.isInt(decField) && decField<fields.length) { // decField can be given as an index
|
|
decFieldIdx = decField;
|
|
break;
|
|
}
|
|
if ( (field.ID && field.ID===decField) || (field.name && field.name===decField)) {
|
|
decFieldIdx = l;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
// if not already given, let's guess position columns on the basis of UCDs
|
|
for (var l=0, len=fields.length; l<len; l++) {
|
|
if (raFieldIdx!=null && decFieldIdx!=null) {
|
|
break;
|
|
}
|
|
|
|
var field = fields[l];
|
|
if ( ! raFieldIdx) {
|
|
if (field.ucd) {
|
|
var ucd = $.trim(field.ucd.toLowerCase());
|
|
if (ucd.indexOf('pos.eq.ra')==0 || ucd.indexOf('pos_eq_ra')==0) {
|
|
raFieldIdx = l;
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( ! decFieldIdx) {
|
|
if (field.ucd) {
|
|
var ucd = $.trim(field.ucd.toLowerCase());
|
|
if (ucd.indexOf('pos.eq.dec')==0 || ucd.indexOf('pos_eq_dec')==0) {
|
|
decFieldIdx = l;
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// still not found ? try some common names for RA and Dec columns
|
|
if (raFieldIdx==null && decFieldIdx==null) {
|
|
for (var l=0, len=fields.length; l<len; l++) {
|
|
var field = fields[l];
|
|
var name = field.name || field.ID || '';
|
|
name = name.toLowerCase();
|
|
|
|
if ( ! raFieldIdx) {
|
|
if (name.indexOf('ra')==0 || name.indexOf('_ra')==0 || name.indexOf('ra(icrs)')==0 || name.indexOf('_ra')==0 || name.indexOf('alpha')==0) {
|
|
raFieldIdx = l;
|
|
continue;
|
|
}
|
|
}
|
|
|
|
if ( ! decFieldIdx) {
|
|
if (name.indexOf('dej2000')==0 || name.indexOf('_dej2000')==0 || name.indexOf('de')==0 || name.indexOf('de(icrs)')==0 || name.indexOf('_de')==0 || name.indexOf('delta')==0) {
|
|
decFieldIdx = l;
|
|
continue;
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
// last resort: take two first fieds
|
|
if (raFieldIdx==null || decFieldIdx==null) {
|
|
raFieldIdx = 0;
|
|
decFieldIdx = 1
|
|
}
|
|
|
|
return [raFieldIdx, decFieldIdx];
|
|
};
|
|
|
|
|
|
Catalog.parseFields = function(fields, raField, decField) {
|
|
// This votable is not an obscore one
|
|
let [raFieldIdx, decFieldIdx] = findRADecFields(fields, raField, decField);
|
|
|
|
let parsedFields = {};
|
|
let fieldIdx = 0;
|
|
fields.forEach((field) => {
|
|
let key = field.name ? field.name : field.id;
|
|
|
|
let nameField;
|
|
if (fieldIdx == raFieldIdx) {
|
|
nameField = 'ra';
|
|
} else if (fieldIdx == decFieldIdx) {
|
|
nameField = 'dec';
|
|
} else {
|
|
nameField = key;
|
|
}
|
|
|
|
parsedFields[nameField] = {
|
|
name: key,
|
|
idx: fieldIdx,
|
|
};
|
|
|
|
fieldIdx++;
|
|
})
|
|
|
|
return parsedFields;
|
|
};
|
|
|
|
// return an array of Source(s) from a VOTable url
|
|
// callback function is called each time a TABLE element has been parsed
|
|
Catalog.parseVOTable = function(url, callback, maxNbSources, useProxy, raField, decField) {
|
|
VOTable.parse(
|
|
url,
|
|
(fields, rows, type) => {
|
|
let sources = [];
|
|
let footprints = [];
|
|
|
|
var coo = new Coo();
|
|
|
|
rows.every(row => {
|
|
let ra, dec, region;
|
|
var mesures = {};
|
|
|
|
for (const [fieldName, field] of Object.entries(fields)) {
|
|
if (fieldName === 's_region') {
|
|
// Obscore s_region param
|
|
region = row[field.idx];
|
|
} else if (fieldName === 'ra' || fieldName === 's_ra') {
|
|
ra = row[field.idx]
|
|
} else if (fieldName === 'dec' || fieldName === 's_dec') {
|
|
dec = row[field.idx]
|
|
}
|
|
|
|
var key = field.name;
|
|
mesures[key] = row[field.idx];
|
|
}
|
|
|
|
if (ra && dec) {
|
|
if (!Utils.isNumber(ra) || !Utils.isNumber(dec)) {
|
|
coo.parse(ra + " " + dec);
|
|
ra = coo.lon;
|
|
dec = coo.lat;
|
|
}
|
|
|
|
const source = new Source(ra, dec, mesures);
|
|
|
|
sources.push(source);
|
|
if (maxNbSources && sources.length == maxNbSources) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if (region) {
|
|
let footprint = A.footprintsFromSTCS(region, {lineWidth: 2});
|
|
footprints = footprints.concat(footprint);
|
|
}
|
|
|
|
return true;
|
|
})
|
|
|
|
if (callback) {
|
|
callback(sources, footprints, fields);
|
|
}
|
|
},
|
|
raField,
|
|
decField
|
|
)
|
|
};
|
|
|
|
// API
|
|
Catalog.prototype.updateShape = function(options) {
|
|
options = options || {};
|
|
this.color = options.color || this.color || Color.getNextColor();
|
|
this.sourceSize = options.sourceSize || this.sourceSize || 6;
|
|
this.shape = options.shape || this.shape || "square";
|
|
|
|
this.selectSize = this.sourceSize + 2;
|
|
|
|
this.cacheCanvas = Catalog.createShape(this.shape, this.color, this.sourceSize);
|
|
this.cacheSelectCanvas = Catalog.createShape('square', this.selectionColor, this.selectSize);
|
|
|
|
this.reportChange();
|
|
};
|
|
|
|
// API
|
|
Catalog.prototype.addSources = function(sourcesToAdd) {
|
|
sourcesToAdd = [].concat(sourcesToAdd); // make sure we have an array and not an individual source
|
|
this.sources = this.sources.concat(sourcesToAdd);
|
|
for (var k=0, len=sourcesToAdd.length; k<len; k++) {
|
|
sourcesToAdd[k].setCatalog(this);
|
|
|
|
// Create columns oriented ra and dec
|
|
this.ra.push(sourcesToAdd[k].ra);
|
|
this.dec.push(sourcesToAdd[k].dec);
|
|
}
|
|
|
|
ALEvent.AL_USE_WASM.dispatchedTo(document.body, {
|
|
callback: (wasm) => {
|
|
wasm.setCatalog(this);
|
|
this.reportChange();
|
|
|
|
}
|
|
});
|
|
|
|
};
|
|
|
|
Catalog.prototype.addFootprints = function(footprintsToAdd) {
|
|
footprintsToAdd = [].concat(footprintsToAdd); // make sure we have an array and not an individual footprints
|
|
this.footprints = this.footprints.concat(footprintsToAdd);
|
|
for (var k=0, len=footprintsToAdd.length; k<len; k++) {
|
|
footprintsToAdd[k].setOverlay(this);
|
|
}
|
|
this.reportChange();
|
|
};
|
|
|
|
Catalog.prototype.setFields = function(fields) {
|
|
this.fields = fields;
|
|
};
|
|
|
|
/// This add a callback when the user clicks on the field column in the measurementTable
|
|
Catalog.prototype.addFieldClickCallback = function(field, callback) {
|
|
this.fieldsClickedActions[field] = callback;
|
|
};
|
|
|
|
Catalog.prototype.isObsCore = function() {
|
|
return this.fields && this.fields.subtype === "ObsCore";
|
|
};
|
|
|
|
// API
|
|
//
|
|
// create sources from a 2d array and add them to the catalog
|
|
//
|
|
// @param columnNames: array with names of the columns
|
|
// @array: 2D-array, each item being a 1d-array with the same number of items as columnNames
|
|
Catalog.prototype.addSourcesAsArray = function(columnNames, array) {
|
|
var fields = [];
|
|
for (var colIdx=0 ; colIdx<columnNames.length; colIdx++) {
|
|
fields.push({name: columnNames[colIdx]});
|
|
}
|
|
var raDecFieldIdxes = findRADecFields(fields, this.raField, this.decField);
|
|
var raFieldIdx, decFieldIdx;
|
|
raFieldIdx = raDecFieldIdxes[0];
|
|
decFieldIdx = raDecFieldIdxes[1];
|
|
|
|
var newSources = [];
|
|
var coo = new Coo();
|
|
var ra, dec, row, dataDict;
|
|
for (var rowIdx=0 ; rowIdx<array.length ; rowIdx++) {
|
|
row = array[rowIdx];
|
|
if (Utils.isNumber(row[raFieldIdx]) && Utils.isNumber(row[decFieldIdx])) {
|
|
ra = parseFloat(row[raFieldIdx]);
|
|
dec = parseFloat(row[decFieldIdx]);
|
|
}
|
|
else {
|
|
coo.parse(row[raFieldIdx] + " " + row[decFieldIdx]);
|
|
ra = coo.lon;
|
|
dec = coo.lat;
|
|
}
|
|
|
|
dataDict = {};
|
|
for (var colIdx=0 ; colIdx<columnNames.length; colIdx++) {
|
|
dataDict[columnNames[colIdx]] = row[colIdx];
|
|
}
|
|
|
|
newSources.push(A.source(ra, dec, dataDict));
|
|
}
|
|
|
|
this.addSources(newSources);
|
|
};
|
|
|
|
// return the current list of Source objects
|
|
Catalog.prototype.getSources = function() {
|
|
return this.sources;
|
|
};
|
|
|
|
// TODO : fonction générique traversant la liste des sources
|
|
Catalog.prototype.selectAll = function() {
|
|
if (! this.sources) {
|
|
return;
|
|
}
|
|
|
|
for (var k=0; k<this.sources.length; k++) {
|
|
this.sources[k].select();
|
|
}
|
|
};
|
|
|
|
Catalog.prototype.deselectAll = function() {
|
|
if (! this.sources) {
|
|
return;
|
|
}
|
|
|
|
for (var k=0; k<this.sources.length; k++) {
|
|
this.sources[k].deselect();
|
|
}
|
|
};
|
|
|
|
// return a source by index
|
|
Catalog.prototype.getSource = function(idx) {
|
|
if (idx<this.sources.length) {
|
|
return this.sources[idx];
|
|
}
|
|
else {
|
|
return null;
|
|
}
|
|
};
|
|
|
|
Catalog.prototype.setView = function(view) {
|
|
this.view = view;
|
|
this.reportChange();
|
|
};
|
|
|
|
// remove a source
|
|
Catalog.prototype.remove = function(source) {
|
|
var idx = this.sources.indexOf(source);
|
|
if (idx<0) {
|
|
return;
|
|
}
|
|
|
|
this.sources[idx].deselect();
|
|
this.sources.splice(idx, 1);
|
|
|
|
this.ra.splice(idx, 1);
|
|
this.dec.splice(idx, 1);
|
|
|
|
this.reportChange();
|
|
};
|
|
|
|
Catalog.prototype.removeAll = Catalog.prototype.clear = function() {
|
|
// TODO : RAZ de l'index
|
|
this.sources = [];
|
|
this.ra = [];
|
|
this.dec = [];
|
|
};
|
|
|
|
Catalog.prototype.draw = function(ctx, frame, width, height, largestDim, zoomFactor) {
|
|
if (! this.isShowing) {
|
|
return;
|
|
}
|
|
// tracé simple
|
|
ctx.strokeStyle= this.color;
|
|
|
|
//ctx.lineWidth = 1;
|
|
//ctx.beginPath();
|
|
if (this._shapeIsFunction) {
|
|
ctx.save();
|
|
}
|
|
|
|
const sourcesInView = this.drawSources(ctx, width, height);
|
|
|
|
if (this._shapeIsFunction) {
|
|
ctx.restore();
|
|
}
|
|
|
|
// Draw labels
|
|
if (this.displayLabel) {
|
|
ctx.fillStyle = this.labelColor;
|
|
ctx.font = this.labelFont;
|
|
sourcesInView.forEach((s) => {
|
|
this.drawSourceLabel(s, ctx);
|
|
})
|
|
}
|
|
|
|
// Draw the footprints
|
|
this.drawFootprints(ctx);
|
|
};
|
|
|
|
Catalog.prototype.drawSources = function(ctx, width, height) {
|
|
if (!this.sources) {
|
|
return;
|
|
}
|
|
|
|
let sourcesInsideView = [];
|
|
let xy = this.view.wasm.worldToScreenVec(this.ra, this.dec);
|
|
|
|
let self = this;
|
|
this.sources.forEach(function(s, idx) {
|
|
if (xy[2*idx] && xy[2*idx + 1]) {
|
|
if (!self.filterFn || self.filterFn(s)) {
|
|
s.x = xy[2*idx];
|
|
s.y = xy[2*idx + 1];
|
|
|
|
self.drawSource(s, ctx, width, height)
|
|
sourcesInsideView.push(s);
|
|
}
|
|
}
|
|
|
|
//if (this.drawSource(s, ctx, width, height)) {
|
|
// sourcesInsideView.push(s);
|
|
//}
|
|
});
|
|
//this.view.wasm.drawSources(this.sources, ctx);
|
|
|
|
return sourcesInsideView;
|
|
};
|
|
|
|
Catalog.prototype.drawSource = function(s, ctx, width, height) {
|
|
if (!s.isShowing) {
|
|
return false;
|
|
}
|
|
|
|
let xy = AladinUtils.radecToViewXy(s.ra, s.dec, this.view);
|
|
|
|
//if (xy) {
|
|
// [s.x, s.y] = xy;
|
|
//var max = s.popup ? 100 : this.sourceSize;
|
|
// TODO : index sources by HEALPix cells at level 3, 4 ?
|
|
|
|
// check if source is visible in view
|
|
//if (xy[0]>(width+max) || xy[0]<(0-max) ||
|
|
// xy[1]>(height+max) || xy[1]<(0-max)) {
|
|
|
|
if (s.x <= width && s.x >= 0 && s.y <= height && s.y >= 0) {
|
|
if (this._shapeIsFunction) {
|
|
this.shape(s, ctx, this.view.getViewParams());
|
|
}
|
|
else if (s.marker && s.useMarkerDefaultIcon) {
|
|
ctx.drawImage(this.cacheMarkerCanvas, s.x-this.sourceSize/2, s.y-this.sourceSize/2);
|
|
}
|
|
else if (s.isSelected) {
|
|
ctx.drawImage(this.cacheSelectCanvas, s.x-this.selectSize/2, s.y-this.selectSize/2);
|
|
}
|
|
else {
|
|
ctx.drawImage(this.cacheCanvas, s.x-this.cacheCanvas.width/2, s.y-this.cacheCanvas.height/2);
|
|
}
|
|
|
|
// has associated popup ?
|
|
if (s.popup) {
|
|
s.popup.setPosition(s.x, s.y);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
//}
|
|
|
|
return false;
|
|
};
|
|
|
|
Catalog.prototype.drawSourceLabel = function(s, ctx) {
|
|
if (!s || !s.isShowing || !s.x || !s.y) {
|
|
return;
|
|
}
|
|
|
|
var label = s.data[this.labelColumn];
|
|
if (!label) {
|
|
return;
|
|
}
|
|
|
|
ctx.fillText(label, s.x, s.y);
|
|
};
|
|
|
|
Catalog.prototype.drawFootprints = function(ctx) {
|
|
this.footprints.forEach((f) => {
|
|
f.draw(ctx, this.view)
|
|
});
|
|
};
|
|
|
|
|
|
// callback function to be called when the status of one of the sources has changed
|
|
Catalog.prototype.reportChange = function() {
|
|
this.view && this.view.requestRedraw();
|
|
};
|
|
|
|
Catalog.prototype.show = function() {
|
|
if (this.isShowing) {
|
|
return;
|
|
}
|
|
this.isShowing = true;
|
|
this.reportChange();
|
|
};
|
|
|
|
Catalog.prototype.hide = function() {
|
|
if (! this.isShowing) {
|
|
return;
|
|
}
|
|
this.isShowing = false;
|
|
if (this.view && this.view.popup && this.view.popup.source && this.view.popup.source.catalog==this) {
|
|
this.view.popup.hide();
|
|
}
|
|
|
|
this.reportChange();
|
|
};
|
|
|
|
return Catalog;
|
|
})();
|