Source: src/Element.js

/**
 * Class Representing Single Element of a slide
 */
class Element {
  /**
   * Create a Element
   * 
   * @param  {String} {[elemType="div"] type of element (eg. div)
   * @param  {number} slideIndex index of the slide 
   * @param  {Object} slideData all the data of the slide
   * @param  {String} parentElem  Parent Element in which new element is appened
   * @param  {String} innerHTML  content of the element
   * @param  {String} innerText  content of the element
   * @param  {String | Number} elemId  Id of the element 
   * @param  {Object} attr all the attributes of the element
   * @param  {Object} style default style of the element that will be applied inline on element
   * @param  {Boolean} [createNewElement]} create new element or import element
   */

  constructor({
    elemType,
    slideIndex,
    slideData,
    parentElem,
    innerHTML,
    innerText,
    elemId,
    attr,
    style,
    alt,
    src,
    createNewElement
  }) {

    this.parentElem = parentElem;
    this.slideIndex = slideIndex;
    this.elemId = elemId;
    this.slideData = slideData;

    // storing data;
    if (createNewElement) {

      this.slideData[slideIndex - 1][`elem${elemId}`]["elemId"] = elemId;
      this.slideData[slideIndex - 1][`elem${elemId}`]["slideIndex"] = slideIndex;

      if (attr) {
        this.slideData[slideIndex - 1][`elem${elemId}`]["attr"] = attr;
      } else if (elemType) {
        this.slideData[slideIndex - 1][`elem${elemId}`]["elemType"] = elemType;
      }

      if (src) {
        this.slideData[slideIndex - 1][`elem${elemId}`]["src"] = src;
        this.slideData[slideIndex - 1][`elem${elemId}`]["alt"] = alt;
      }
    }

    if (elemId === "Title") {
      this.element = createElementAndAppend({
        parentElem,
        innerHTML,
        innerText,
        attr,
        style,
      });
    } else if (elemId === "UserNote") {

      this.element = createElementAndAppend({
        parentElem,
        attr,
        style,
        innerHTML,
        innerText: "NOTE :"
      });

    } else {

      let upperElement = document.querySelector(`#slide${slideIndex}Element${elemId-1}Container`);

      let top = upperElement ? parseInt(upperElement.style.height) * (elemId - 1) + (GAP_BETWEEN_ELEMENT / 2) * (elemId) : GAP_BETWEEN_ELEMENT / 2;

      top = createNewElement ? `${top}px` : style.top
      // placing element in random place after place is occupie
      if ((parseInt(top) + DEFAULT_ELEMENT_HEIGHT) >= parseInt(this.parentElem.style.height)) {
        top = GAP_BETWEEN_ELEMENT * elemId + "px";
      }

      this.slideData[this.slideIndex - 1][`elem${this.elemId}`].style = {
        ...style,
        top
      };
      this.resizableContainer = createElementAndAppend({
        parentElem: this.parentElem,
        attr: {
          id: `slide${slideIndex}Element${elemId}Container`,
        },
        style: {
          ...style,
          top,
        }
      });

      attr = elemType === "img" ? {
        src,
        id: `slide${slideIndex}Element${elemId}`,
        contenteditable: true,
        alt: `${alt} - slide ${slideIndex} Element ${elemId} image `
      } : {
        contenteditable: true,
        id: `slide${slideIndex}Element${elemId}`,
        placeholder: "Please start writing..."
      };

      this.element = createElementAndAppend({
        innerHTML,
        parentElem: this.resizableContainer,
        elemType,
        style: elemType === "img" ? {
          width: "100%",
          height: "100%"
        } : {
          fontSize: "22px",
        },
        attr
      });
    }
  }


  /**
   * A function to initilize the slide Element 
   * @returns Element Object
   */
  init() {

    if (this.resizableContainer) {

      // element resizer

      this.resizer = createElementAndAppend({
        parentElem: this.resizableContainer,
        attr: {
          class: "resizer"
        }
      });

      // drag and drop grabber
      createElementAndAppend({
        parentElem: this.resizableContainer,
        attr: {
          class: "dragger"
        }
      });

      // Drag and drop only to the content not title and comment section

      updatePosition(this.resizableContainer, "drag");

      // make resizeable
      updatePosition(this.resizableContainer, "resize");

    }
    // Focus event on event
    this.element.addEventListener("focus", (e) => {
      if (!this.element.innerHTML) {
        this.element.innerHTML = " "
      }
      let activeElem = document.querySelector("[dataToolbarActive='true']");
      // make resizer visible
      let previousActiveResizer = activeElem && activeElem.parentElement.querySelector(".resizer");

      if (previousActiveResizer) {
        previousActiveResizer.style.display = "none";
      }

      if (this.resizer) {
        this.resizer.style.display = "initial";
      }

      activeElem && activeElem.removeAttribute("dataToolbarActive");

      toolbarActionsProperty.forEach(d => {
        let activeTool = document.querySelector(`[dataCmd=${d.attr.dataCmd}]`);
        if (e.target.style[d.attr.cssProperty] === d.attr.dataCmd) {
          activeTool && activeTool.classList.add("activeTool");
        } else {
          activeTool && activeTool.classList.remove("activeTool");
        }
      });

      let fontSize = e.target.style.fontSize;
      if (fontSize) {
        document.querySelector("#fontSize").value = parseInt(fontSize);
      } else {
        document.querySelector("#fontSize").value = 16;
      }

      let fontFamily = e.target.style.fontFamily;

      if (fontFamily) {
        document.querySelector("#fontFamily").value = fontFamily;
      } else {
        document.querySelector("#fontFamily").value = "sans-serif"; //default
      }

      e.target.setAttribute("dataToolbarActive", "true");
      this.updateStyleData();
    });

    this.element.addEventListener("focusout", () => {
      if (!this.element.innerHTML.trim()) {
        this.element.innerHTML = ""
      }
      this.updateStyleData();
    })

    //Watch the change on slide's element's content and collectd the data
    this.element.addEventListener("input", (e) => {

      let elemOnList = document.querySelector(`.slide-list #slide${this.slideIndex}Element${this.elemId}`);

      if (elemOnList) {
        elemOnList.innerHTML = this.element.innerHTML;
      }

      this.slideData[this.slideIndex - 1][`elem${this.elemId}`]["innerHTML"] = this.element.innerHTML;

    }, true);

    return this.resizableContainer;
  }

  // updating style of the element on this.slideData
  updateStyleData() {
    let currentStyle = formatStyleToStore(this.element.style);
    let prevStyle = this.slideData[this.slideIndex - 1][`elem${this.elemId}`].style;

    let parentElement = this.element.parentElement;
    let parentElemEId = parentElement.getAttribute("id");

    if (parentElemEId && parentElemEId.includes("Container")) {
      let style = formatStyleToStore(parentElement.style);
      if (this.element.tagName !== "IMG") {
        style = {
          ...currentStyle,
          ...style,
        };
      }

      if (JSON.stringify(style) !== JSON.stringify(prevStyle)) {
        this.slideData[this.slideIndex - 1][`elem${this.elemId}`].style = style;
      }
    } else if (JSON.stringify(currentStyle) !== JSON.stringify(prevStyle)) {
      this.slideData[this.slideIndex - 1][`elem${this.elemId}`].style = currentStyle;
    }
  }
}