Source: CGObject.js

//////////////////////////////////////////////////////////////////////////////
// CGObject
//////////////////////////////////////////////////////////////////////////////

/**
 * CGView.js – Interactive Circular Genome Viewer
 * Copyright © 2016–2025 Jason R. Grant
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import utils from './Utils';

// Generate cgvID
let cgvID = 0;
const generateID = function() {
  return `cgv-id-${cgvID++}`;
};

/**
 * The CGObject is the base class of many CGView Classes. In particular, any class
 * that is drawn on the map will be a subclass of CGObject (e.g. [Track](Track.html),
 * [Slot](Slot.html), [Feature](Feature.html), [Plot](Plot.html), etc).
 *
 * Any subclass instances will be given a unique temporary cgvID.
 * This id is not saved to JSON and should not be used across CGView sessions
 * (i.e. don't expect a feature to have the same cgvID if it's loaded in the viewer again).
 * Any object can be easily returned using the cgvID and [Viewer.objects](Viewer.html#objects).
 *
 * Classes that extend CGObject will have access to several commonly accessed viewer objects:
 * - viewer
 * - sequence
 * - canvas
 * - layout
 *
 * <a name="attributes"></a>
 * ### Attributes
 *
 * Attribute           | Type      | Description
 * --------------------|-----------|------------
 * [visible](#visible) | Boolean   | Object is visible [Default: true]
 * [meta](#meta)       | Object    | [Meta data](../tutorials/details-meta-data.html) [Default: {}]
 *
 */
class CGObject {

  /**
   * Create a new CGObject.
   * @param {Viewer} viewer - The viewer
   * @param {Object} options - [Attributes](#attributes) used to create the bookmark
   * @param {Object} [meta] - User-defined [Meta data](../tutorials/details-meta-data.html) to add to the bookmark.
   */
  constructor(viewer, options = {}, meta = {}) {
    // super();
    this._viewer = viewer;
    this.meta = utils.merge(options.meta, meta);
    this.visible = utils.defaultFor(options.visible, true);
    this._cgvID = generateID();
    viewer._objects[this.cgvID] = this;
  }

  /**
   * Return the class name as a string.
   * @return {String} - 'CGObject'
   */
  toString() {
    return 'CGObject';
  }

  get cgvID() {
    return this._cgvID;
  }

  /**
   * @member {Viewer} - Get the viewer.
   */
  get viewer() {
    return this._viewer;
  }

  /**
   * @member {Canvas} - Get the canvas.
   */
  get canvas() {
    return this.viewer.canvas;
  }

  /**
   * @member {Layout} - Get the layout.
   */
  get layout() {
    return this.viewer.layout;
  }

  /**
   * @member {Sequence} - Get the sequence.
   */
  get sequence() {
    return this.viewer.sequence;
  }

  /**
   * @member {Boolean} - Get or Set the visibility of this object.
   */
  get visible() {
    return this._visible;
  }

  set visible(value) {
    this._visible = value;
  }

  /**
   * @member {Boolean} - Get or Set the meta data of this object. See the [meta data](../tutorials/details-meta-data.html) tutorial for details.
   */
  get meta() {
    return this._meta;
  }

  set meta(value) {
    this._meta = value;
  }

  /**
   * Remove the object from Viewer.objects
   */
  deleteFromObjects() {
    delete this.viewer._objects[this.cgvID];
  }

  //////////////////////////////////////////////////////////////////////////////
  // PLUGIN METHODS
  //////////////////////////////////////////////////////////////////////////////

  /**
   * Add a plugin to the object.
   * @param {String} id - The id of the plugin
   * @param {Object} options - The plugin options
   */
  addPluginOptions(id, options) {
    this.viewer.plugins?._addPluginOptions(this, id, options);
    // if (this.hasPlugin(id)) {
    //   throw new Error(`Plugin ${id} already exists.`);
    // }
    // if (!this.pluginOptions) {
    //   this.pluginOptions = {};
    // }
    // if (this.viewer.plugins.includes(id)) {
    //   this.pluginOptions[id] = options;
    // } else {
    //   throw new Error(`Plugin '${id}' not found in viewer.`);
    // }
  }

  /**
   * Update plugin options. Merge the new options with the old options.
   * @param {String} id - The id of the plugin
   * @param {Object} options - The plugin options
   */
  updatePluginOptions(id, options) {
    this.viewer.plugins?._updatePluginOptions(this, id, options);
    // if (this.hasPlugin(id)) {
    //   const updates = {...this.pluginOptions[id], ...options};
    //   const pluginOptions = {...this.pluginOptions, [id]: updates};
    //   if (typeof this.update === 'function') {
    //     this.update({pluginOptions});
    //   } else {
    //     console.log('No update function found.', this, pluginOptions);
    //     // this.pluginOptions = pluginOptions;
    //   }
    // } else {
    //   throw new Error(`Plugin '${id}' not found.`);
    // }
  }

  /**
   * Does this object have a particular plugin?
   * @param {String} pluginID - The ID of the plugin
   * @return {Boolean} - Whether the object has the plugin
   */
  hasPlugin(pluginID) {
    return this.viewer.plugins?._hasPlugin(this, pluginID);
    // return this.viewer.plugins.
    // if (this.pluginOptions) {
    //   const pluginIDs = Object.keys(this.pluginOptions).map(key => key.toLowerCase());
    //   return pluginIDs.includes(pluginID.toLowerCase());
    // }
  }

  /**
   * Get the options for a particular plugin.
   * @param {String} pluginName - The name of the plugin
   * @return {Object} - The options for the plugin or undefined if the plugin is not found
   */
  optionsForPlugin(pluginID) {
    return this.viewer.plugins?._optionsForPlugin(this, pluginID);
    // if (this.hasPlugin(pluginID)) {
    //   return this.pluginOptions[pluginID];
    // }
  }


}

export default CGObject;