Source: model/Selection.js

/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

/** @module */

/** Selection in the timeline of the Sozi editor.
 *
 * A Selection instance holds the currently selected
 * frames and layers of the presentation.
 */
export class Selection {

    /** Create an empty selection for a given presentation.
     *
     * @param {module:model/Presentation.Presentation} presentation - A Sozi presentation object
     */
    constructor(presentation) {
        /** The presentation where selections happen.
         *
         * @type {module:model/Presentation.Presentation} */
        this.presentation = presentation;

        /** The list of selected frames.
         *
         * @default
         * @type {module:model/Presentation.Frame[]} */
        this.selectedFrames = [];

        /** The list of selected layers.
         *
         * @default
         * @type {module:model/Presentation.Layer[]} */
        this.selectedLayers = [];
    }

    /** Convert this instance to a plain object that can be stored as JSON.
     *
     * The result contains all the properties needed by the editor to restore
     * the state of this instance.
     *
     * @returns {object} - A plain object with the properties needed by the editor.
     */
    toStorable() {
        return {
            selectedFrames: this.selectedFrames.map(frame => frame.frameId),
            selectedLayers: this.selectedLayers.map(layer => layer.groupId)
        };
    }

    /** Copy the properties of the given object into this instance.
     *
     * @param {object} storable - A plain object with the properties to copy.
     */
    fromStorable(storable) {
        if ("selectedFrames" in storable) {
            this.selectedFrames = [];
            for (let frameId of storable.selectedFrames) {
                const frame = this.presentation.getFrameWithId(frameId);
                if (frame) {
                    this.selectedFrames.push(frame);
                }
            }
        }

        if ("selectedLayers" in storable) {
            this.selectedLayers = [];
            for (let groupId of storable.selectedLayers) {
                const layer = this.presentation.getLayerWithId(groupId);
                if (layer) {
                    this.selectedLayers.push(layer);
                }
            }
        }
    }

    /** The frame that was selected last, `null` if no frame is selected.
     *
     * @type {module:model/Presentation.Frame}
     */
    get currentFrame() {
        return this.selectedFrames.length ?
            this.selectedFrames[this.selectedFrames.length - 1] :
            null;
    }

    /** Check whether this selection contains the given frames.
     *
     * @param {module:model/Presentation.Frame[]} frames - The frames to check.
     * @returns {boolean} - `true` if all the given frames are selected.
     */
    hasFrames(frames) {
        return frames.every(frame => this.selectedFrames.indexOf(frame) >= 0);
    }

    /** Add a frame to this selection.
     *
     * @param {module:model/Presentation.Frame} frame - The frame to add.
     */
    addFrame(frame) {
        if (this.selectedFrames.indexOf(frame) < 0) {
            this.selectedFrames.push(frame);
        }
    }

    /** Remove a frame from this selection.
     *
     * @param {module:model/Presentation.Frame} frame - The frame to remove.
     */
    removeFrame(frame) {
        const index = this.selectedFrames.indexOf(frame);
        if (index >= 0) {
            this.selectedFrames.splice(index, 1);
        }
    }

    /** Add or remove the given frame to/from this selection.
     *
     * If the frame is not selected, add it to the selection,
     * otherwise, remove it.
     *
     * @param {module:model/Presentation.Frame} frame - The frame to add or remove.
     */
    toggleFrameSelection(frame) {
        const index = this.selectedFrames.indexOf(frame);
        if (index >= 0) {
            this.selectedFrames.splice(index, 1);
        }
        else {
            this.selectedFrames.push(frame);
        }
    }

    /** Check whether this selection contains the given layers.
     *
     * @param {module:model/Presentation.Layer[]} layers - The layers to check.
     * @returns {boolean} - `true` if all the given layers are selected.
     */
    hasLayers(layers) {
        return layers.every(layer => this.selectedLayers.indexOf(layer) >= 0);
    }

    /** Add a layer to this selection.
     *
     * @param {module:model/Presentation.Layer} layer - The layer to add.
     */
    addLayer(layer) {
        if (this.selectedLayers.indexOf(layer) < 0) {
            this.selectedLayers.push(layer);
        }
    }

    /** Remove a layer from this selection.
     *
     * @param {module:model/Presentation.Layer} layer - The layer to remove.
     */
    removeLayer(layer) {
        const index = this.selectedLayers.indexOf(layer);
        if (index >= 0) {
            this.selectedLayers.splice(index, 1);
        }
    }

    /** Add or remove the given layer to/from this selection.
     *
     * If the layer is not selected, add it to the selection,
     * otherwise, remove it.
     *
     * @param {module:model/Presentation.Layer} layer - The layer to add or remove.
     */
    toggleLayerSelection(layer) {
        const index = this.selectedLayers.indexOf(layer);
        if (index >= 0) {
            this.selectedLayers.splice(index, 1);
        }
        else {
            this.selectedLayers.push(layer);
        }
    }
}