"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.Timeline = void 0;

var _infernoHyperscript = require("inferno-hyperscript");

var _VirtualDOMView = require("./VirtualDOMView");

var _jed = _interopRequireDefault(require("jed"));

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

/* 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/. */

/** Timeline pane of the presentation editor.
 *
 * The Timeline view shows a table where columns represent frames
 * and rows represent layers.
 * The user can choose to manipulate only a subset of the layers of the document.
 * The other layers are grouped into a "Default" row.
 *
 * The Timeline view allows the user to:
 *  - choose editable layers from the document
 *  - show/hide layers
 *  - create/delete/reorder frames
 *  - add/remove layers and frames to/from the selection
 *
 * @category view
 * @extends VirtualDOMView
 * @todo Add documentation
 */
class Timeline extends _VirtualDOMView.VirtualDOMView {
  /*
   * Initialize a Timeline view.
   *
   * The view is rendered and event handlers are set up.
   *
   * Parameters:
   *  - presentation: a presentation object
   *  - selection: a selection object
   *
   * Returns:
   *  - The current view
   */
  constructor(container, presentation, selection, controller) {
    super(container, controller);
    this.presentation = presentation;
    this.selection = selection;
  }

  toggleLayerVisibility(layerIndex, evt) {
    this.controller.updateLayerVisibility(this.controller.getLayersAtIndex(layerIndex));
    evt.stopPropagation();
  }

  updateFrameSelection(frameIndex, evt) {
    this.controller.updateFrameSelection(evt.ctrlKey, evt.shiftKey, frameIndex);
    evt.stopPropagation();
  }

  updateLayerSelection(layerIndex, evt) {
    this.controller.updateLayerSelection(evt.ctrlKey, evt.shiftKey, this.controller.getLayersAtIndex(layerIndex));
    evt.stopPropagation();
  }

  updateLayerAndFrameSelection(layerIndex, frameIndex, evt) {
    this.controller.updateLayerAndFrameSelection(evt.ctrlKey, evt.shiftKey, this.controller.getLayersAtIndex(layerIndex), frameIndex);
    evt.stopPropagation();
  }

  repaint() {
    super.repaint();
    const topLeft = this.container.querySelector(".timeline-top-left");
    const topRight = this.container.querySelector(".timeline-top-right");
    const bottomLeft = this.container.querySelector(".timeline-bottom-left");
    const bottomRight = this.container.querySelector(".timeline-bottom-right");
    const topLeftTable = topLeft.querySelector("table");
    const topRightTable = topRight.querySelector("table");
    const bottomLeftTable = bottomLeft.querySelector("table");
    const leftWidth = Math.max(topLeftTable.clientWidth, bottomLeftTable.clientWidth);
    const rightWidth = this.container.clientWidth - leftWidth;
    const topHeight = Math.max(topLeftTable.clientHeight, topRightTable.clientHeight);
    const bottomHeight = this.container.clientHeight - topHeight; // Fit the width of the left tables,
    // allocate remaining width to the right tables

    topLeft.style.width = bottomLeft.style.width = topLeftTable.style.width = bottomLeftTable.style.width = leftWidth + "px";
    topRight.style.width = bottomRight.style.width = rightWidth + "px"; // Fit the height of the top tables,
    // allocate remaining width to the bottom tables

    topLeft.style.height = topRight.style.height = topHeight + "px";
    bottomLeft.style.height = bottomRight.style.height = bottomHeight + "px"; // Corresponding rows in left and right tables must have the same height

    const leftRows = document.querySelectorAll(".timeline-top-left tr,  .timeline-bottom-left tr");
    const rightRows = document.querySelectorAll(".timeline-top-right tr, .timeline-bottom-right tr");
    leftRows.forEach((leftRow, rowIndex) => {
      const rightRow = rightRows[rowIndex];
      const maxHeight = Math.max(leftRow.clientHeight, rightRow.clientHeight);
      leftRow.style.height = rightRow.style.height = maxHeight + "px";
    });
  }

  render() {
    const controller = this.controller;
    const _ = controller.gettext;
    let even = true;

    function updateEven(frame, layer) {
      if (frame.index === 0) {
        even = true;
      } else if (!frame.layerProperties[layer.index].link) {
        even = !even;
      }

      return even;
    }

    function isLinked(frame, layer) {
      return frame.index > 0 && frame.layerProperties[layer.index].link;
    }

    function hasNoReferenceElement(frame, layer) {
      return !isLinked(frame, layer) && !frame.layerProperties[layer.index].referenceElement;
    }

    const defaultLayersAreVisible = controller.defaultLayers.some(layer => layer.isVisible);
    return (0, _infernoHyperscript.h)("div", [(0, _infernoHyperscript.h)("div.timeline-top-left", [(0, _infernoHyperscript.h)("table.timeline", [(0, _infernoHyperscript.h)("tr", [(0, _infernoHyperscript.h)("th", [(0, _infernoHyperscript.h)("button", {
      title: _("Delete the selected frames"),
      disabled: this.selection.selectedFrames.length ? undefined : "disabled",

      onclick() {
        controller.deleteFrames();
      }

    }, (0, _infernoHyperscript.h)("i.fas.fa-trash"))]), (0, _infernoHyperscript.h)("th", [(0, _infernoHyperscript.h)("button", {
      title: _("Create a new frame"),

      onclick() {
        controller.addFrame();
      }

    }, (0, _infernoHyperscript.h)("i.fas.fa-plus"))])]), (0, _infernoHyperscript.h)("tr", [(0, _infernoHyperscript.h)("th", {
      colspan: 2
    }, (0, _infernoHyperscript.h)("select", {
      onchange: evt => {
        const value = evt.target.value;
        evt.target.value = "__sozi_add__";

        if (value === "__sozi_add_all__") {
          controller.addAllLayers();
        } else {
          controller.addLayer(value);
        }
      }
    }, [(0, _infernoHyperscript.h)("option", {
      value: "__sozi_add__",
      selected: "selected"
    }, _("Add layer")), (0, _infernoHyperscript.h)("option", {
      value: "__sozi_add_all__"
    }, _("Add all layers")), this.presentation.layers.slice().reverse().filter(layer => !layer.auto && controller.defaultLayers.indexOf(layer) >= 0).map(layer => (0, _infernoHyperscript.h)("option", {
      value: layer.index
    }, layer.label))]))])])]), (0, _infernoHyperscript.h)("div.timeline-bottom-left", [(0, _infernoHyperscript.h)("table.timeline", [controller.hasDefaultLayer ? (0, _infernoHyperscript.h)("tr", [(0, _infernoHyperscript.h)("th.layer-icons", [defaultLayersAreVisible ? (0, _infernoHyperscript.h)("i.visibility.far.fa-eye", {
      title: _("This layer is visible. Click to hide it."),
      onclick: evt => this.toggleLayerVisibility(-1, evt)
    }) : (0, _infernoHyperscript.h)("i.visibility.far.fa-eye-slash", {
      title: _("This layer is hidden. Click to show it."),
      onclick: evt => this.toggleLayerVisibility(-1, evt)
    }), (0, _infernoHyperscript.h)("i.remove.fas.fa-times", {
      style: {
        visibility: "hidden"
      }
    })]), (0, _infernoHyperscript.h)("th", {
      className: "layer-label" + (controller.defaultLayersAreSelected ? " selected" : ""),
      onclick: evt => this.updateLayerSelection(-1, evt)
    }, _("Default"))]) : null, this.presentation.layers.slice().reverse().filter(layer => controller.editableLayers.indexOf(layer) >= 0).map(layer => (0, _infernoHyperscript.h)("tr", [(0, _infernoHyperscript.h)("th.layer-icons", [layer.isVisible ? (0, _infernoHyperscript.h)("i.visibility.far.fa-eye", {
      title: _("This layer is visible. Click to hide it."),
      onclick: evt => this.toggleLayerVisibility(layer.index, evt)
    }) : (0, _infernoHyperscript.h)("i.visibility.far.fa-eye-slash", {
      title: _("This layer is hidden. Click to show it."),
      onclick: evt => this.toggleLayerVisibility(layer.index, evt)
    }), (0, _infernoHyperscript.h)("i.remove.fas.fa-times", {
      title: _("Remove this layer"),
      onclick: () => controller.removeLayer(layer.index)
    })]), (0, _infernoHyperscript.h)("th", {
      className: "layer-label" + (this.selection.selectedLayers.indexOf(layer) >= 0 ? " selected" : ""),
      onclick: evt => this.updateLayerSelection(layer.index, evt)
    }, layer.label)])), (0, _infernoHyperscript.h)("tr", {
      style: {
        visibility: "collapse"
      }
    }, [(0, _infernoHyperscript.h)("th.layer-icons"), (0, _infernoHyperscript.h)("th.layer-label", _("Default"))])])]), (0, _infernoHyperscript.h)("div.timeline-top-right", [(0, _infernoHyperscript.h)("table.timeline", [(0, _infernoHyperscript.h)("tr", this.presentation.frames.map((frame, frameIndex) => (0, _infernoHyperscript.h)("th", {
      className: "frame-index" + (this.selection.selectedFrames.indexOf(frame) >= 0 ? " selected" : "") + (frame === this.selection.currentFrame ? " current" : ""),
      onclick: evt => this.updateFrameSelection(frameIndex, evt)
    }, [(0, _infernoHyperscript.h)("i.insert-before.fas.fa-arrow-circle-down", {
      title: _jed.default.sprintf(_("Insert selection before frame %d"), frameIndex + 1),

      onclick(evt) {
        controller.moveFrames(frameIndex);
        evt.stopPropagation();
      }

    }), (0, _infernoHyperscript.h)("i.insert-after.fas.fa-arrow-circle-down", {
      title: _jed.default.sprintf(_("Insert selection after frame %d"), frameIndex + 1),

      onclick(evt) {
        controller.moveFrames(frameIndex + 1);
        evt.stopPropagation();
      }

    }), (frameIndex + 1).toString()]))), (0, _infernoHyperscript.h)("tr", this.presentation.frames.map((frame, frameIndex) => (0, _infernoHyperscript.h)("th", {
      title: frame.title,
      className: "frame-title" + (this.selection.selectedFrames.indexOf(frame) >= 0 ? " selected" : "") + (frame === this.selection.currentFrame ? " current" : ""),
      onclick: evt => this.updateFrameSelection(frameIndex, evt)
    }, frame.title)))])]), (0, _infernoHyperscript.h)("div.timeline-bottom-right", {
      onscroll: evt => {
        this.container.querySelector(".timeline-top-right").scrollLeft = evt.target.scrollLeft;
        this.container.querySelector(".timeline-bottom-left").scrollTop = evt.target.scrollTop;
      }
    }, (0, _infernoHyperscript.h)("table.timeline", [controller.hasDefaultLayer ? (0, _infernoHyperscript.h)("tr", this.presentation.frames.map((frame, frameIndex) => (0, _infernoHyperscript.h)("td", {
      className: (controller.defaultLayersAreSelected && this.selection.selectedFrames.indexOf(frame) >= 0 ? "selected" : "") + (frame === this.selection.currentFrame ? " current" : "") + (isLinked(frame, controller.defaultLayers[0]) ? " link" : "") + (updateEven(frame, controller.defaultLayers[0]) ? " even" : " odd"),
      onclick: evt => this.updateLayerAndFrameSelection(-1, frameIndex, evt)
    }, hasNoReferenceElement(frame, controller.defaultLayers[0]) ? (0, _infernoHyperscript.h)("i.fas.fa-exclamation-triangle", {
      title: _("You should add graphic elements in the current area to help Sozi keep track of this layer's position.")
    }) : null))) : null, this.presentation.layers.slice().reverse().filter(layer => controller.editableLayers.indexOf(layer) >= 0).map(layer => (0, _infernoHyperscript.h)("tr", this.presentation.frames.map((frame, frameIndex) => (0, _infernoHyperscript.h)("td", {
      className: (this.selection.selectedLayers.indexOf(layer) >= 0 && this.selection.selectedFrames.indexOf(frame) >= 0 ? "selected" : "") + (frame === this.selection.currentFrame ? " current" : "") + (isLinked(frame, layer) ? " link" : "") + (updateEven(frame, layer) ? " even" : " odd"),
      onclick: evt => this.updateLayerAndFrameSelection(layer.index, frameIndex, evt)
    }, hasNoReferenceElement(frame, layer) ? (0, _infernoHyperscript.h)("i.fas.fa-exclamation-triangle", {
      title: _("You should add graphic elements in the current area to help Sozi keep track of this layer's position.")
    }) : null)))), (0, _infernoHyperscript.h)("tr.collapse", this.presentation.frames.map(frame => (0, _infernoHyperscript.h)("td", frame.title)))]))]);
  }

}

exports.Timeline = Timeline;
