diff --git a/documentUtils.js b/documentUtils.js
new file mode 100644
index 0000000..f2714d3
--- /dev/null
+++ b/documentUtils.js
@@ -0,0 +1,42 @@
+/**
+ * Checks the item is not missed or messed
+ * @param {object|string[]|Element[]|HTMLElement|string} elem - element
+ * @returns {boolean} true if element is correct
+ * @private
+ */
+function _isNotMissed(elem) {
+ return (!(elem === undefined || elem === null));
+}
+
+/**
+ * Create DOM element with set parameters
+ * @param {string} tagName - Html tag of the element to be created
+ * @param {string[]} cssClasses - Css classes that must be applied to an element
+ * @param {object} attrs - Attributes that must be applied to the element
+ * @param {Element[]} children - child elements of creating element
+ * @returns {HTMLElement} the new element
+ */
+export function create(tagName, cssClasses = null, attrs = null, children = null) {
+ const elem = document.createElement(tagName);
+
+ if (_isNotMissed(cssClasses)) {
+ for (let i = 0; i < cssClasses.length; i++) {
+ if (_isNotMissed(cssClasses[i])) {
+ elem.classList.add(cssClasses[i]);
+ }
+ }
+ }
+ if (_isNotMissed(attrs)) {
+ for (let key in attrs) {
+ elem.setAttribute(key, attrs[key]);
+ }
+ }
+ if (_isNotMissed(children)) {
+ for (let i = 0; i < children.length; i++) {
+ if (_isNotMissed(children[i])) {
+ elem.appendChild(children[i]);
+ }
+ }
+ }
+ return elem;
+}
diff --git a/img/deleteColIcon.svg b/img/deleteColIcon.svg
new file mode 100644
index 0000000..c0b4f84
--- /dev/null
+++ b/img/deleteColIcon.svg
@@ -0,0 +1 @@
+
diff --git a/img/deleteRowIcon.svg b/img/deleteRowIcon.svg
new file mode 100644
index 0000000..b4f331d
--- /dev/null
+++ b/img/deleteRowIcon.svg
@@ -0,0 +1 @@
+
diff --git a/img/indertColAfterIcon.svg b/img/indertColAfterIcon.svg
new file mode 100644
index 0000000..c06d8e1
--- /dev/null
+++ b/img/indertColAfterIcon.svg
@@ -0,0 +1 @@
+
diff --git a/img/insertColBeforeIcon.svg b/img/insertColBeforeIcon.svg
new file mode 100644
index 0000000..b1155b8
--- /dev/null
+++ b/img/insertColBeforeIcon.svg
@@ -0,0 +1 @@
+
diff --git a/img/insertRowAfter.svg b/img/insertRowAfter.svg
new file mode 100644
index 0000000..bcb6a4c
--- /dev/null
+++ b/img/insertRowAfter.svg
@@ -0,0 +1 @@
+
diff --git a/img/insertRowBeforeIcon.svg b/img/insertRowBeforeIcon.svg
new file mode 100644
index 0000000..885ae46
--- /dev/null
+++ b/img/insertRowBeforeIcon.svg
@@ -0,0 +1 @@
+
diff --git a/img/mergeCellIcon.svg b/img/mergeCellIcon.svg
new file mode 100644
index 0000000..808be67
--- /dev/null
+++ b/img/mergeCellIcon.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/img/toolboxIcon.svg b/img/toolboxIcon.svg
new file mode 100644
index 0000000..0db0ad5
--- /dev/null
+++ b/img/toolboxIcon.svg
@@ -0,0 +1 @@
+
diff --git a/img/unmergeCellIcon.svg b/img/unmergeCellIcon.svg
new file mode 100644
index 0000000..9b5eaf1
--- /dev/null
+++ b/img/unmergeCellIcon.svg
@@ -0,0 +1,3 @@
+
diff --git a/plugin.js b/plugin.js
new file mode 100644
index 0000000..a2163b9
--- /dev/null
+++ b/plugin.js
@@ -0,0 +1,323 @@
+const { TableConstructor } = require("./tableConstructor");
+const toolboxIcon = require("./img/toolboxIcon.svg");
+const insertColBefore = require("./img/insertColBeforeIcon.svg");
+const insertColAfter = require("./img/indertColAfterIcon.svg");
+const insertRowBefore = require("./img/insertRowBeforeIcon.svg");
+const insertRowAfter = require("./img/insertRowAfter.svg");
+const deleteRow = require("./img/deleteRowIcon.svg");
+const deleteCol = require("./img/deleteColIcon.svg");
+
+const Icons = {
+ Toolbox: toolboxIcon,
+ InsertColBefore: insertColBefore,
+ InsertColAfter: insertColAfter,
+ InsertRowBefore: insertRowBefore,
+ InsertRowAfter: insertRowAfter,
+ DeleteRow: deleteRow,
+ DeleteCol: deleteCol,
+};
+
+const CSS = {
+ input: "tc-table__inp",
+};
+
+/**
+ * Tool for table's creating
+ * @typedef {object} TableData - object with the data transferred to form a table
+ * @property {string[][]} content - two-dimensional array which contains table content
+ */
+class Table {
+ /**
+ * Allow to press Enter inside the CodeTool textarea
+ * @returns {boolean}
+ * @public
+ */
+ static get enableLineBreaks() {
+ return true;
+ }
+
+ /**
+ * Get Tool toolbox settings
+ * icon - Tool icon's SVG
+ * title - title to show in toolbox
+ *
+ * @return {{icon: string, title: string}}
+ */
+ static get toolbox() {
+ return {
+ icon: Icons.Toolbox,
+ title: "Table",
+ };
+ }
+
+ /**
+ * Render plugin`s main Element and fill it with saved data
+ * @param {TableData} data — previously saved data
+ * @param {object} config - user config for Tool
+ * @param {object} api - Editor.js API
+ */
+ constructor({ data, config, api }) {
+ this.api = api;
+ this.wrapper = undefined;
+ this.config = config;
+ this.data = data;
+ this._tableConstructor = new TableConstructor(data, config, api);
+
+ this.actions = [
+ {
+ actionName: "InsertColBefore",
+ icon: Icons.InsertColBefore,
+ label: this.api.i18n.t("Insert column before"),
+ },
+ {
+ actionName: "InsertColAfter",
+ icon: Icons.InsertColAfter,
+ label: this.api.i18n.t("Insert column after"),
+ },
+ {
+ actionName: "InsertRowBefore",
+ icon: Icons.InsertRowBefore,
+ label: this.api.i18n.t("Insert row before"),
+ },
+ {
+ actionName: "InsertRowAfter",
+ icon: Icons.InsertRowAfter,
+ label: this.api.i18n.t("Insert row after"),
+ },
+ {
+ actionName: "DeleteRow",
+ icon: Icons.DeleteRow,
+ label: this.api.i18n.t("Delete row"),
+ },
+ {
+ actionName: "DeleteCol",
+ icon: Icons.DeleteCol,
+ label: this.api.i18n.t("Delete column"),
+ },
+ ];
+ }
+
+ /**
+ * perform selected action
+ * @param actionName {string} - action name
+ * @return {undefined}
+ */
+ performAction(actionName) {
+ switch (actionName) {
+ case "InsertColBefore":
+ this._tableConstructor.table.insertColumnBefore();
+ break;
+ case "InsertColAfter":
+ this._tableConstructor.table.insertColumnAfter();
+ break;
+ case "InsertRowBefore":
+ this._tableConstructor.table.insertRowBefore();
+ break;
+ case "InsertRowAfter":
+ this._tableConstructor.table.insertRowAfter();
+ break;
+ case "DeleteRow":
+ this._tableConstructor.table.deleteRow();
+ break;
+ case "DeleteCol":
+ this._tableConstructor.table.deleteColumn();
+ break;
+ }
+ }
+
+ /**
+ * render actions toolbar
+ * @returns {HTMLDivElement}
+ */
+ renderSettings() {
+ const wrapper = document.createElement("div");
+
+ this.actions.forEach(({ actionName, label, icon }) => {
+ const title = this.api.i18n.t(label);
+ const button = document.createElement("div");
+
+ button.classList.add("cdx-settings-button");
+ button.innerHTML = icon;
+ button.title = actionName;
+
+ this.api.tooltip.onHover(button, title, {
+ placement: "top",
+ });
+ button.addEventListener(
+ "click",
+ this.performAction.bind(this, actionName)
+ );
+ wrapper.appendChild(button);
+ if(this._tableConstructor.table.selectedCell) {
+ this._tableConstructor.table.focusCellOnSelectedCell();
+ }
+ });
+ return wrapper;
+ }
+
+ /**
+ * Return Tool's view
+ * @returns {HTMLDivElement}
+ * @public
+ */
+ render() {
+ this.wrapper = document.createElement("div");
+
+ if ((this.data && this.data.content)) {
+ //Creates table if Data is Present
+ this._createTableConfiguration();
+ } else {
+ // Create table preview if New table is initialised
+ this.wrapper.classList.add("table-selector");
+ this.wrapper.setAttribute("data-hoveredClass", "m,n");
+ const rows = 6;
+ this.createCells(rows);
+ //Hover to select cells
+ if (this.wrapper.className === "table-selector") {
+ this.wrapper.addEventListener("mouseover", (event) => {
+ const selectedCell = event.target.id;
+ if (selectedCell.length) {
+ const selectedRow = event.target.attributes.row.value;
+ const selectedColumn = event.target.attributes.column.value;
+ this.wrapper.setAttribute(
+ "data-hoveredClass",
+ `${selectedRow},${selectedColumn}`
+ );
+ }
+ });
+ }
+ //set the select cell to load table config
+ this.wrapper.addEventListener("click", (event) => {
+ const selectedCell = event.target.id;
+ if (selectedCell.length) {
+ const selectedRow = event.target.attributes.row.value;
+ const selectedColumn = event.target.attributes.column.value;
+ this.wrapper.removeEventListener("mouseover", () => {});
+ this.config.rows = selectedRow;
+ this.config.cols = selectedColumn;
+ this._createTableConfiguration();
+ }
+ });
+ }
+ return this.wrapper;
+ }
+
+ createCells(rows) {
+ if (rows !== 0) {
+ for (let i = 0; i < rows; i++) {
+ let rowDiv = document.createElement("div");
+ rowDiv.setAttribute("class", "table-row");
+ for (let j = 0; j < rows; j++) {
+ let columnDivContainer = document.createElement("div");
+ let columnDiv = document.createElement("div");
+ columnDivContainer.setAttribute("class", "table-cell-container");
+ columnDiv.setAttribute("class", "table-cell");
+ columnDivContainer.setAttribute("id", `row_${i + 1}_cell_${j + 1}`);
+ columnDivContainer.setAttribute("column", j + 1);
+ columnDivContainer.setAttribute("row", i + 1);
+ columnDiv.setAttribute("id", `cell_${j + 1}`);
+ columnDiv.setAttribute("column", j + 1);
+ columnDiv.setAttribute("row", i + 1);
+ columnDivContainer.appendChild(columnDiv);
+ rowDiv.appendChild(columnDivContainer);
+ }
+ this.wrapper.appendChild(rowDiv);
+ }
+ }
+ const hiddenEl = document.createElement('input');
+ hiddenEl.classList.add('hidden-element');
+ hiddenEl.setAttribute('tabindex', 0);
+ this.wrapper.appendChild(hiddenEl);
+ }
+
+ _createTableConfiguration() {
+ this.wrapper.innerHTML = "";
+ this._tableConstructor = new TableConstructor(
+ this.data,
+ this.config,
+ this.api
+ );
+ this.wrapper.appendChild(this._tableConstructor.htmlElement);
+ }
+ /**
+ * Extract Tool's data from the view
+ * @returns {TableData} - saved data
+ * @public
+ */
+ save(toolsContent) {
+ const table = toolsContent.querySelector("table");
+ const data = [];
+ const rows = table ? table.rows : 0;
+ if(rows.length) {
+ for (let i = 0; i < rows.length; i++) {
+ const row = rows[i];
+ const cols = Array.from(row.cells);
+ const inputs = cols.map((cell) => cell.querySelector("." + CSS.input));
+ const isWorthless = inputs.every(this._isEmpty);
+
+ if (isWorthless) {
+ continue;
+ }
+ data.push(inputs.map((input) => input.innerHTML));
+ }
+ return {
+ content: data,
+ };
+ }
+}
+
+ /**
+ * @private
+ *
+ * Check input field is empty
+ * @param {HTMLElement} input - input field
+ * @return {boolean}
+ */
+ _isEmpty(input) {
+ return !input.textContent.trim();
+ }
+
+ static get pasteConfig() {
+ return {
+ tags: ['TABLE', 'TR', 'TD', 'TBODY', 'TH'],
+ };
+ }
+
+ async onPaste(event) {
+ const table = event.detail.data;
+ this.data = this.pasteHandler(table);
+ this._createTableConfiguration();
+ }
+
+ pasteHandler(element) {
+ const {tagName: tag} = element;
+ const data = {
+ content: [],
+ config: {
+ rows: 0,
+ cols: 0
+ }
+ }
+ if(tag ==='TABLE') {
+ let tableBody = Array.from(element.childNodes);
+ tableBody = tableBody.find(el => el.nodeName === 'TBODY');
+ let tableRows = Array.from(tableBody.childNodes);
+ tableRows = [tableRows].map(obj => {
+ return obj.filter((tr) => tr.nodeName === 'TR');
+ });
+ data.config.rows = tableRows[0].length;
+ data.content = tableRows[0].map((tr) => {
+ let tableData = tr.childNodes;
+ data.config.cols = tableData.length;
+ tableData = [...tableData].map((td) => {
+ return td.innerHTML;
+ });
+ return tableData;
+ })
+ }
+ return data;
+ }
+
+}
+
+module.exports = Table;
diff --git a/src/img/mergeCellIcon.svg b/src/img/mergeCellIcon.svg
new file mode 100644
index 0000000..808be67
--- /dev/null
+++ b/src/img/mergeCellIcon.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/img/unmergeCellIcon.svg b/src/img/unmergeCellIcon.svg
new file mode 100644
index 0000000..9b5eaf1
--- /dev/null
+++ b/src/img/unmergeCellIcon.svg
@@ -0,0 +1,3 @@
+
diff --git a/styles/table-constructor.pcss b/styles/table-constructor.pcss
new file mode 100644
index 0000000..e6c8ec5
--- /dev/null
+++ b/styles/table-constructor.pcss
@@ -0,0 +1,9 @@
+/* tcm - project's prefix*/
+
+.tc-editor {
+ padding: 10px;
+ position: relative;
+ box-sizing: content-box;
+ width: 100%;
+ left: -10px;
+}
\ No newline at end of file
diff --git a/styles/table.pcss b/styles/table.pcss
new file mode 100644
index 0000000..d4dd5bf
--- /dev/null
+++ b/styles/table.pcss
@@ -0,0 +1,522 @@
+/* tc- project's prefix*/
+
+.tc-table {
+ width: 100%;
+ height: 100%;
+ border-collapse: collapse;
+ table-layout: fixed;
+
+ &__wrap {
+ border: 1px solid #DBDBE2;
+ border-radius: 3px;
+ position: relative;
+ height: 100%;
+ width: 100%;
+ box-sizing: border-box;
+ }
+
+ &__cell {
+ border: 1px solid #DBDBE2;
+ padding: 0;
+ vertical-align: top;
+ }
+
+ &__area {
+ padding: 10px;
+ height: 100%;
+ }
+
+ &__inp {
+ outline: none;
+ flex-grow: 100;
+ min-height: 1.5em;
+ height: 100%;
+ overflow: hidden;
+ }
+
+ &__highlight:focus-within {
+ background-color: rgba(173, 164, 176, 0.1);
+ }
+
+ tbody tr:first-child td {
+ border-top: none;
+ }
+
+ tbody tr:last-child td {
+ border-bottom: none;
+ }
+
+ tbody tr td:last-child {
+ border-right: none;
+ }
+
+ tbody tr td:first-child {
+ border-left: none;
+ }
+}
+.table-selector {
+ display: flex;
+ flex-direction: column;
+ .hidden-element {
+ display: none;
+ }
+}
+.table-row {
+ display: flex;
+ flex-direction: row;
+}
+.table-cell-container {
+ width: 30px;
+ height: 30px;
+}
+.table-cell {
+ width: 25px;
+ height: 25px;
+ background: rgba(246, 246, 246);
+ border: 1px solid rgba(228, 228, 228);
+ cursor: pointer;
+}
+[data-hoveredClass="1,1"] #row_1_cell_1 #cell_1,
+[data-hoveredClass="1,2"] #row_1_cell_1 #cell_1,
+[data-hoveredClass="1,2"] #row_1_cell_2 #cell_2,
+[data-hoveredClass="1,3"] #row_1_cell_1 #cell_1,
+[data-hoveredClass="1,3"] #row_1_cell_2 #cell_2,
+[data-hoveredClass="1,3"] #row_1_cell_3 #cell_3,
+[data-hoveredClass="1,4"] #row_1_cell_1 #cell_1,
+[data-hoveredClass="1,4"] #row_1_cell_2 #cell_2,
+[data-hoveredClass="1,4"] #row_1_cell_3 #cell_3,
+[data-hoveredClass="1,4"] #row_1_cell_4 #cell_4,
+[data-hoveredClass="1,5"] #row_1_cell_1 #cell_1,
+[data-hoveredClass="1,5"] #row_1_cell_2 #cell_2,
+[data-hoveredClass="1,5"] #row_1_cell_3 #cell_3,
+[data-hoveredClass="1,5"] #row_1_cell_4 #cell_4,
+[data-hoveredClass="1,5"] #row_1_cell_5 #cell_5,
+[data-hoveredClass="1,6"] #row_1_cell_1 #cell_1,
+[data-hoveredClass="1,6"] #row_1_cell_2 #cell_2,
+[data-hoveredClass="1,6"] #row_1_cell_3 #cell_3,
+[data-hoveredClass="1,6"] #row_1_cell_4 #cell_4,
+[data-hoveredClass="1,6"] #row_1_cell_5 #cell_5,
+[data-hoveredClass="1,6"] #row_1_cell_6 #cell_6,
+[data-hoveredClass="2,1"] #row_1_cell_1 #cell_1,
+[data-hoveredClass="2,1"] #row_2_cell_1 #cell_1,
+[data-hoveredClass="2,2"] #row_1_cell_1 #cell_1,
+[data-hoveredClass="2,2"] #row_1_cell_2 #cell_2,
+[data-hoveredClass="2,2"] #row_2_cell_1 #cell_1,
+[data-hoveredClass="2,2"] #row_2_cell_2 #cell_2,
+[data-hoveredClass="2,3"] #row_1_cell_1 #cell_1,
+[data-hoveredClass="2,3"] #row_1_cell_2 #cell_2,
+[data-hoveredClass="2,3"] #row_1_cell_3 #cell_3,
+[data-hoveredClass="2,3"] #row_2_cell_1 #cell_1,
+[data-hoveredClass="2,3"] #row_2_cell_2 #cell_2,
+[data-hoveredClass="2,3"] #row_2_cell_3 #cell_3,
+[data-hoveredClass="2,4"] #row_1_cell_1 #cell_1,
+[data-hoveredClass="2,4"] #row_1_cell_2 #cell_2,
+[data-hoveredClass="2,4"] #row_1_cell_3 #cell_3,
+[data-hoveredClass="2,4"] #row_1_cell_4 #cell_4,
+[data-hoveredClass="2,4"] #row_2_cell_1 #cell_1,
+[data-hoveredClass="2,4"] #row_2_cell_2 #cell_2,
+[data-hoveredClass="2,4"] #row_2_cell_3 #cell_3,
+[data-hoveredClass="2,4"] #row_2_cell_4 #cell_4,
+[data-hoveredClass="2,5"] #row_1_cell_1 #cell_1,
+[data-hoveredClass="2,5"] #row_1_cell_2 #cell_2,
+[data-hoveredClass="2,5"] #row_1_cell_3 #cell_3,
+[data-hoveredClass="2,5"] #row_1_cell_4 #cell_4,
+[data-hoveredClass="2,5"] #row_1_cell_5 #cell_5,
+[data-hoveredClass="2,5"] #row_2_cell_1 #cell_1,
+[data-hoveredClass="2,5"] #row_2_cell_2 #cell_2,
+[data-hoveredClass="2,5"] #row_2_cell_3 #cell_3,
+[data-hoveredClass="2,5"] #row_2_cell_4 #cell_4,
+[data-hoveredClass="2,5"] #row_2_cell_5 #cell_5,
+[data-hoveredClass="2,6"] #row_1_cell_1 #cell_1,
+[data-hoveredClass="2,6"] #row_1_cell_2 #cell_2,
+[data-hoveredClass="2,6"] #row_1_cell_3 #cell_3,
+[data-hoveredClass="2,6"] #row_1_cell_4 #cell_4,
+[data-hoveredClass="2,6"] #row_1_cell_5 #cell_5,
+[data-hoveredClass="2,6"] #row_1_cell_6 #cell_6,
+[data-hoveredClass="2,6"] #row_2_cell_1 #cell_1,
+[data-hoveredClass="2,6"] #row_2_cell_2 #cell_2,
+[data-hoveredClass="2,6"] #row_2_cell_3 #cell_3,
+[data-hoveredClass="2,6"] #row_2_cell_4 #cell_4,
+[data-hoveredClass="2,6"] #row_2_cell_5 #cell_5,
+[data-hoveredClass="2,6"] #row_2_cell_6 #cell_6,
+[data-hoveredClass="3,1"] #row_1_cell_1 #cell_1,
+[data-hoveredClass="3,1"] #row_2_cell_1 #cell_1,
+[data-hoveredClass="3,1"] #row_3_cell_1 #cell_1,
+[data-hoveredClass="3,2"] #row_1_cell_1 #cell_1,
+[data-hoveredClass="3,2"] #row_1_cell_2 #cell_2,
+[data-hoveredClass="3,2"] #row_2_cell_1 #cell_1,
+[data-hoveredClass="3,2"] #row_2_cell_2 #cell_2,
+[data-hoveredClass="3,2"] #row_3_cell_1 #cell_1,
+[data-hoveredClass="3,2"] #row_3_cell_2 #cell_2,
+[data-hoveredClass="3,3"] #row_1_cell_1 #cell_1,
+[data-hoveredClass="3,3"] #row_1_cell_2 #cell_2,
+[data-hoveredClass="3,3"] #row_1_cell_3 #cell_3,
+[data-hoveredClass="3,3"] #row_2_cell_1 #cell_1,
+[data-hoveredClass="3,3"] #row_2_cell_2 #cell_2,
+[data-hoveredClass="3,3"] #row_2_cell_3 #cell_3,
+[data-hoveredClass="3,3"] #row_3_cell_1 #cell_1,
+[data-hoveredClass="3,3"] #row_3_cell_2 #cell_2,
+[data-hoveredClass="3,3"] #row_3_cell_3 #cell_3,
+[data-hoveredClass="3,4"] #row_1_cell_1 #cell_1,
+[data-hoveredClass="3,4"] #row_1_cell_2 #cell_2,
+[data-hoveredClass="3,4"] #row_1_cell_3 #cell_3,
+[data-hoveredClass="3,4"] #row_1_cell_4 #cell_4,
+[data-hoveredClass="3,4"] #row_2_cell_1 #cell_1,
+[data-hoveredClass="3,4"] #row_2_cell_2 #cell_2,
+[data-hoveredClass="3,4"] #row_2_cell_3 #cell_3,
+[data-hoveredClass="3,4"] #row_2_cell_4 #cell_4,
+[data-hoveredClass="3,4"] #row_3_cell_1 #cell_1,
+[data-hoveredClass="3,4"] #row_3_cell_2 #cell_2,
+[data-hoveredClass="3,4"] #row_3_cell_3 #cell_3,
+[data-hoveredClass="3,4"] #row_3_cell_4 #cell_4,
+[data-hoveredClass="3,5"] #row_1_cell_1 #cell_1,
+[data-hoveredClass="3,5"] #row_1_cell_2 #cell_2,
+[data-hoveredClass="3,5"] #row_1_cell_3 #cell_3,
+[data-hoveredClass="3,5"] #row_1_cell_4 #cell_4,
+[data-hoveredClass="3,5"] #row_1_cell_5 #cell_5,
+[data-hoveredClass="3,5"] #row_2_cell_1 #cell_1,
+[data-hoveredClass="3,5"] #row_2_cell_2 #cell_2,
+[data-hoveredClass="3,5"] #row_2_cell_3 #cell_3,
+[data-hoveredClass="3,5"] #row_2_cell_4 #cell_4,
+[data-hoveredClass="3,5"] #row_2_cell_5 #cell_5,
+[data-hoveredClass="3,5"] #row_3_cell_1 #cell_1,
+[data-hoveredClass="3,5"] #row_3_cell_2 #cell_2,
+[data-hoveredClass="3,5"] #row_3_cell_3 #cell_3,
+[data-hoveredClass="3,5"] #row_3_cell_4 #cell_4,
+[data-hoveredClass="3,5"] #row_3_cell_5 #cell_5,
+[data-hoveredClass="3,6"] #row_1_cell_1 #cell_1,
+[data-hoveredClass="3,6"] #row_1_cell_2 #cell_2,
+[data-hoveredClass="3,6"] #row_1_cell_3 #cell_3,
+[data-hoveredClass="3,6"] #row_1_cell_4 #cell_4,
+[data-hoveredClass="3,6"] #row_1_cell_5 #cell_5,
+[data-hoveredClass="3,6"] #row_1_cell_6 #cell_6,
+[data-hoveredClass="3,6"] #row_2_cell_1 #cell_1,
+[data-hoveredClass="3,6"] #row_2_cell_2 #cell_2,
+[data-hoveredClass="3,6"] #row_2_cell_3 #cell_3,
+[data-hoveredClass="3,6"] #row_2_cell_4 #cell_4,
+[data-hoveredClass="3,6"] #row_2_cell_5 #cell_5,
+[data-hoveredClass="3,6"] #row_2_cell_6 #cell_6,
+[data-hoveredClass="3,6"] #row_3_cell_1 #cell_1,
+[data-hoveredClass="3,6"] #row_3_cell_2 #cell_2,
+[data-hoveredClass="3,6"] #row_3_cell_3 #cell_3,
+[data-hoveredClass="3,6"] #row_3_cell_4 #cell_4,
+[data-hoveredClass="3,6"] #row_3_cell_5 #cell_5,
+[data-hoveredClass="3,6"] #row_3_cell_6 #cell_6,
+[data-hoveredClass="4,1"] #row_1_cell_1 #cell_1,
+[data-hoveredClass="4,1"] #row_2_cell_1 #cell_1,
+[data-hoveredClass="4,1"] #row_3_cell_1 #cell_1,
+[data-hoveredClass="4,1"] #row_4_cell_1 #cell_1,
+[data-hoveredClass="4,2"] #row_1_cell_1 #cell_1,
+[data-hoveredClass="4,2"] #row_1_cell_2 #cell_2,
+[data-hoveredClass="4,2"] #row_2_cell_1 #cell_1,
+[data-hoveredClass="4,2"] #row_2_cell_2 #cell_2,
+[data-hoveredClass="4,2"] #row_3_cell_1 #cell_1,
+[data-hoveredClass="4,2"] #row_3_cell_2 #cell_2,
+[data-hoveredClass="4,2"] #row_4_cell_1 #cell_1,
+[data-hoveredClass="4,2"] #row_4_cell_2 #cell_2,
+[data-hoveredClass="4,3"] #row_1_cell_1 #cell_1,
+[data-hoveredClass="4,3"] #row_1_cell_2 #cell_2,
+[data-hoveredClass="4,3"] #row_1_cell_3 #cell_3,
+[data-hoveredClass="4,3"] #row_2_cell_1 #cell_1,
+[data-hoveredClass="4,3"] #row_2_cell_2 #cell_2,
+[data-hoveredClass="4,3"] #row_2_cell_3 #cell_3,
+[data-hoveredClass="4,3"] #row_3_cell_1 #cell_1,
+[data-hoveredClass="4,3"] #row_3_cell_2 #cell_2,
+[data-hoveredClass="4,3"] #row_3_cell_3 #cell_3,
+[data-hoveredClass="4,3"] #row_4_cell_1 #cell_1,
+[data-hoveredClass="4,3"] #row_4_cell_2 #cell_2,
+[data-hoveredClass="4,3"] #row_4_cell_3 #cell_3,
+[data-hoveredClass="4,4"] #row_1_cell_1 #cell_1,
+[data-hoveredClass="4,4"] #row_1_cell_2 #cell_2,
+[data-hoveredClass="4,4"] #row_1_cell_3 #cell_3,
+[data-hoveredClass="4,4"] #row_1_cell_4 #cell_4,
+[data-hoveredClass="4,4"] #row_2_cell_1 #cell_1,
+[data-hoveredClass="4,4"] #row_2_cell_2 #cell_2,
+[data-hoveredClass="4,4"] #row_2_cell_3 #cell_3,
+[data-hoveredClass="4,4"] #row_2_cell_4 #cell_4,
+[data-hoveredClass="4,4"] #row_3_cell_1 #cell_1,
+[data-hoveredClass="4,4"] #row_3_cell_2 #cell_2,
+[data-hoveredClass="4,4"] #row_3_cell_3 #cell_3,
+[data-hoveredClass="4,4"] #row_3_cell_4 #cell_4,
+[data-hoveredClass="4,4"] #row_4_cell_1 #cell_1,
+[data-hoveredClass="4,4"] #row_4_cell_2 #cell_2,
+[data-hoveredClass="4,4"] #row_4_cell_3 #cell_3,
+[data-hoveredClass="4,4"] #row_4_cell_4 #cell_4,
+[data-hoveredClass="4,5"] #row_1_cell_1 #cell_1,
+[data-hoveredClass="4,5"] #row_1_cell_2 #cell_2,
+[data-hoveredClass="4,5"] #row_1_cell_3 #cell_3,
+[data-hoveredClass="4,5"] #row_1_cell_4 #cell_4,
+[data-hoveredClass="4,5"] #row_1_cell_5 #cell_5,
+[data-hoveredClass="4,5"] #row_2_cell_1 #cell_1,
+[data-hoveredClass="4,5"] #row_2_cell_2 #cell_2,
+[data-hoveredClass="4,5"] #row_2_cell_3 #cell_3,
+[data-hoveredClass="4,5"] #row_2_cell_4 #cell_4,
+[data-hoveredClass="4,5"] #row_2_cell_5 #cell_5,
+[data-hoveredClass="4,5"] #row_3_cell_1 #cell_1,
+[data-hoveredClass="4,5"] #row_3_cell_2 #cell_2,
+[data-hoveredClass="4,5"] #row_3_cell_3 #cell_3,
+[data-hoveredClass="4,5"] #row_3_cell_4 #cell_4,
+[data-hoveredClass="4,5"] #row_3_cell_5 #cell_5,
+[data-hoveredClass="4,5"] #row_4_cell_1 #cell_1,
+[data-hoveredClass="4,5"] #row_4_cell_2 #cell_2,
+[data-hoveredClass="4,5"] #row_4_cell_3 #cell_3,
+[data-hoveredClass="4,5"] #row_4_cell_4 #cell_4,
+[data-hoveredClass="4,5"] #row_4_cell_5 #cell_5,
+[data-hoveredClass="4,6"] #row_1_cell_1 #cell_1,
+[data-hoveredClass="4,6"] #row_1_cell_2 #cell_2,
+[data-hoveredClass="4,6"] #row_1_cell_3 #cell_3,
+[data-hoveredClass="4,6"] #row_1_cell_4 #cell_4,
+[data-hoveredClass="4,6"] #row_1_cell_5 #cell_5,
+[data-hoveredClass="4,6"] #row_1_cell_6 #cell_6,
+[data-hoveredClass="4,6"] #row_2_cell_1 #cell_1,
+[data-hoveredClass="4,6"] #row_2_cell_2 #cell_2,
+[data-hoveredClass="4,6"] #row_2_cell_3 #cell_3,
+[data-hoveredClass="4,6"] #row_2_cell_4 #cell_4,
+[data-hoveredClass="4,6"] #row_2_cell_5 #cell_5,
+[data-hoveredClass="4,6"] #row_2_cell_6 #cell_6,
+[data-hoveredClass="4,6"] #row_3_cell_1 #cell_1,
+[data-hoveredClass="4,6"] #row_3_cell_2 #cell_2,
+[data-hoveredClass="4,6"] #row_3_cell_3 #cell_3,
+[data-hoveredClass="4,6"] #row_3_cell_4 #cell_4,
+[data-hoveredClass="4,6"] #row_3_cell_5 #cell_5,
+[data-hoveredClass="4,6"] #row_3_cell_6 #cell_6,
+[data-hoveredClass="4,6"] #row_4_cell_1 #cell_1,
+[data-hoveredClass="4,6"] #row_4_cell_2 #cell_2,
+[data-hoveredClass="4,6"] #row_4_cell_3 #cell_3,
+[data-hoveredClass="4,6"] #row_4_cell_4 #cell_4,
+[data-hoveredClass="4,6"] #row_4_cell_5 #cell_5,
+[data-hoveredClass="4,6"] #row_4_cell_6 #cell_6,
+[data-hoveredClass="5,1"] #row_1_cell_1 #cell_1,
+[data-hoveredClass="5,1"] #row_2_cell_1 #cell_1,
+[data-hoveredClass="5,1"] #row_3_cell_1 #cell_1,
+[data-hoveredClass="5,1"] #row_4_cell_1 #cell_1,
+[data-hoveredClass="5,1"] #row_5_cell_1 #cell_1,
+[data-hoveredClass="5,2"] #row_1_cell_1 #cell_1,
+[data-hoveredClass="5,2"] #row_1_cell_2 #cell_2,
+[data-hoveredClass="5,2"] #row_2_cell_1 #cell_1,
+[data-hoveredClass="5,2"] #row_2_cell_2 #cell_2,
+[data-hoveredClass="5,2"] #row_3_cell_1 #cell_1,
+[data-hoveredClass="5,2"] #row_3_cell_2 #cell_2,
+[data-hoveredClass="5,2"] #row_4_cell_1 #cell_1,
+[data-hoveredClass="5,2"] #row_4_cell_2 #cell_2,
+[data-hoveredClass="5,2"] #row_5_cell_1 #cell_1,
+[data-hoveredClass="5,2"] #row_5_cell_2 #cell_2,
+[data-hoveredClass="5,3"] #row_1_cell_1 #cell_1,
+[data-hoveredClass="5,3"] #row_1_cell_2 #cell_2,
+[data-hoveredClass="5,3"] #row_1_cell_3 #cell_3,
+[data-hoveredClass="5,3"] #row_2_cell_1 #cell_1,
+[data-hoveredClass="5,3"] #row_2_cell_2 #cell_2,
+[data-hoveredClass="5,3"] #row_2_cell_3 #cell_3,
+[data-hoveredClass="5,3"] #row_3_cell_1 #cell_1,
+[data-hoveredClass="5,3"] #row_3_cell_2 #cell_2,
+[data-hoveredClass="5,3"] #row_3_cell_3 #cell_3,
+[data-hoveredClass="5,3"] #row_4_cell_1 #cell_1,
+[data-hoveredClass="5,3"] #row_4_cell_2 #cell_2,
+[data-hoveredClass="5,3"] #row_4_cell_3 #cell_3,
+[data-hoveredClass="5,3"] #row_5_cell_1 #cell_1,
+[data-hoveredClass="5,3"] #row_5_cell_2 #cell_2,
+[data-hoveredClass="5,3"] #row_5_cell_3 #cell_3,
+[data-hoveredClass="5,4"] #row_1_cell_1 #cell_1,
+[data-hoveredClass="5,4"] #row_1_cell_2 #cell_2,
+[data-hoveredClass="5,4"] #row_1_cell_3 #cell_3,
+[data-hoveredClass="5,4"] #row_1_cell_4 #cell_4,
+[data-hoveredClass="5,4"] #row_2_cell_1 #cell_1,
+[data-hoveredClass="5,4"] #row_2_cell_2 #cell_2,
+[data-hoveredClass="5,4"] #row_2_cell_3 #cell_3,
+[data-hoveredClass="5,4"] #row_2_cell_4 #cell_4,
+[data-hoveredClass="5,4"] #row_3_cell_1 #cell_1,
+[data-hoveredClass="5,4"] #row_3_cell_2 #cell_2,
+[data-hoveredClass="5,4"] #row_3_cell_3 #cell_3,
+[data-hoveredClass="5,4"] #row_3_cell_4 #cell_4,
+[data-hoveredClass="5,4"] #row_4_cell_1 #cell_1,
+[data-hoveredClass="5,4"] #row_4_cell_2 #cell_2,
+[data-hoveredClass="5,4"] #row_4_cell_3 #cell_3,
+[data-hoveredClass="5,4"] #row_4_cell_4 #cell_4,
+[data-hoveredClass="5,4"] #row_5_cell_1 #cell_1,
+[data-hoveredClass="5,4"] #row_5_cell_2 #cell_2,
+[data-hoveredClass="5,4"] #row_5_cell_3 #cell_3,
+[data-hoveredClass="5,4"] #row_5_cell_4 #cell_4,
+[data-hoveredClass="5,5"] #row_1_cell_1 #cell_1,
+[data-hoveredClass="5,5"] #row_1_cell_2 #cell_2,
+[data-hoveredClass="5,5"] #row_1_cell_3 #cell_3,
+[data-hoveredClass="5,5"] #row_1_cell_4 #cell_4,
+[data-hoveredClass="5,5"] #row_1_cell_5 #cell_5,
+[data-hoveredClass="5,5"] #row_2_cell_1 #cell_1,
+[data-hoveredClass="5,5"] #row_2_cell_2 #cell_2,
+[data-hoveredClass="5,5"] #row_2_cell_3 #cell_3,
+[data-hoveredClass="5,5"] #row_2_cell_4 #cell_4,
+[data-hoveredClass="5,5"] #row_2_cell_5 #cell_5,
+[data-hoveredClass="5,5"] #row_3_cell_1 #cell_1,
+[data-hoveredClass="5,5"] #row_3_cell_2 #cell_2,
+[data-hoveredClass="5,5"] #row_3_cell_3 #cell_3,
+[data-hoveredClass="5,5"] #row_3_cell_4 #cell_4,
+[data-hoveredClass="5,5"] #row_3_cell_5 #cell_5,
+[data-hoveredClass="5,5"] #row_4_cell_1 #cell_1,
+[data-hoveredClass="5,5"] #row_4_cell_2 #cell_2,
+[data-hoveredClass="5,5"] #row_4_cell_3 #cell_3,
+[data-hoveredClass="5,5"] #row_4_cell_4 #cell_4,
+[data-hoveredClass="5,5"] #row_4_cell_5 #cell_5,
+[data-hoveredClass="5,5"] #row_5_cell_1 #cell_1,
+[data-hoveredClass="5,5"] #row_5_cell_2 #cell_2,
+[data-hoveredClass="5,5"] #row_5_cell_3 #cell_3,
+[data-hoveredClass="5,5"] #row_5_cell_4 #cell_4,
+[data-hoveredClass="5,5"] #row_5_cell_5 #cell_5,
+[data-hoveredClass="5,6"] #row_1_cell_1 #cell_1,
+[data-hoveredClass="5,6"] #row_1_cell_2 #cell_2,
+[data-hoveredClass="5,6"] #row_1_cell_3 #cell_3,
+[data-hoveredClass="5,6"] #row_1_cell_4 #cell_4,
+[data-hoveredClass="5,6"] #row_1_cell_5 #cell_5,
+[data-hoveredClass="5,6"] #row_1_cell_6 #cell_6,
+[data-hoveredClass="5,6"] #row_2_cell_1 #cell_1,
+[data-hoveredClass="5,6"] #row_2_cell_2 #cell_2,
+[data-hoveredClass="5,6"] #row_2_cell_3 #cell_3,
+[data-hoveredClass="5,6"] #row_2_cell_4 #cell_4,
+[data-hoveredClass="5,6"] #row_2_cell_5 #cell_5,
+[data-hoveredClass="5,6"] #row_2_cell_6 #cell_6,
+[data-hoveredClass="5,6"] #row_3_cell_1 #cell_1,
+[data-hoveredClass="5,6"] #row_3_cell_2 #cell_2,
+[data-hoveredClass="5,6"] #row_3_cell_3 #cell_3,
+[data-hoveredClass="5,6"] #row_3_cell_4 #cell_4,
+[data-hoveredClass="5,6"] #row_3_cell_5 #cell_5,
+[data-hoveredClass="5,6"] #row_3_cell_6 #cell_6,
+[data-hoveredClass="5,6"] #row_4_cell_1 #cell_1,
+[data-hoveredClass="5,6"] #row_4_cell_2 #cell_2,
+[data-hoveredClass="5,6"] #row_4_cell_3 #cell_3,
+[data-hoveredClass="5,6"] #row_4_cell_4 #cell_4,
+[data-hoveredClass="5,6"] #row_4_cell_5 #cell_5,
+[data-hoveredClass="5,6"] #row_4_cell_6 #cell_6,
+[data-hoveredClass="5,6"] #row_5_cell_1 #cell_1,
+[data-hoveredClass="5,6"] #row_5_cell_2 #cell_2,
+[data-hoveredClass="5,6"] #row_5_cell_3 #cell_3,
+[data-hoveredClass="5,6"] #row_5_cell_4 #cell_4,
+[data-hoveredClass="5,6"] #row_5_cell_5 #cell_5,
+[data-hoveredClass="5,6"] #row_5_cell_6 #cell_6,
+[data-hoveredClass="6,1"] #row_1_cell_1 #cell_1,
+[data-hoveredClass="6,1"] #row_2_cell_1 #cell_1,
+[data-hoveredClass="6,1"] #row_3_cell_1 #cell_1,
+[data-hoveredClass="6,1"] #row_4_cell_1 #cell_1,
+[data-hoveredClass="6,1"] #row_5_cell_1 #cell_1,
+[data-hoveredClass="6,1"] #row_6_cell_1 #cell_1,
+[data-hoveredClass="6,2"] #row_1_cell_1 #cell_1,
+[data-hoveredClass="6,2"] #row_1_cell_2 #cell_2,
+[data-hoveredClass="6,2"] #row_2_cell_1 #cell_1,
+[data-hoveredClass="6,2"] #row_2_cell_2 #cell_2,
+[data-hoveredClass="6,2"] #row_3_cell_1 #cell_1,
+[data-hoveredClass="6,2"] #row_3_cell_2 #cell_2,
+[data-hoveredClass="6,2"] #row_4_cell_1 #cell_1,
+[data-hoveredClass="6,2"] #row_4_cell_2 #cell_2,
+[data-hoveredClass="6,2"] #row_5_cell_1 #cell_1,
+[data-hoveredClass="6,2"] #row_5_cell_2 #cell_2,
+[data-hoveredClass="6,2"] #row_6_cell_1 #cell_1,
+[data-hoveredClass="6,2"] #row_6_cell_2 #cell_2,
+[data-hoveredClass="6,3"] #row_1_cell_1 #cell_1,
+[data-hoveredClass="6,3"] #row_1_cell_2 #cell_2,
+[data-hoveredClass="6,3"] #row_1_cell_3 #cell_3,
+[data-hoveredClass="6,3"] #row_2_cell_1 #cell_1,
+[data-hoveredClass="6,3"] #row_2_cell_2 #cell_2,
+[data-hoveredClass="6,3"] #row_2_cell_3 #cell_3,
+[data-hoveredClass="6,3"] #row_3_cell_1 #cell_1,
+[data-hoveredClass="6,3"] #row_3_cell_2 #cell_2,
+[data-hoveredClass="6,3"] #row_3_cell_3 #cell_3,
+[data-hoveredClass="6,3"] #row_4_cell_1 #cell_1,
+[data-hoveredClass="6,3"] #row_4_cell_2 #cell_2,
+[data-hoveredClass="6,3"] #row_4_cell_3 #cell_3,
+[data-hoveredClass="6,3"] #row_5_cell_1 #cell_1,
+[data-hoveredClass="6,3"] #row_5_cell_2 #cell_2,
+[data-hoveredClass="6,3"] #row_5_cell_3 #cell_3,
+[data-hoveredClass="6,3"] #row_6_cell_1 #cell_1,
+[data-hoveredClass="6,3"] #row_6_cell_2 #cell_2,
+[data-hoveredClass="6,3"] #row_6_cell_3 #cell_3,
+[data-hoveredClass="6,4"] #row_1_cell_1 #cell_1,
+[data-hoveredClass="6,4"] #row_1_cell_2 #cell_2,
+[data-hoveredClass="6,4"] #row_1_cell_3 #cell_3,
+[data-hoveredClass="6,4"] #row_1_cell_4 #cell_4,
+[data-hoveredClass="6,4"] #row_2_cell_1 #cell_1,
+[data-hoveredClass="6,4"] #row_2_cell_2 #cell_2,
+[data-hoveredClass="6,4"] #row_2_cell_3 #cell_3,
+[data-hoveredClass="6,4"] #row_2_cell_4 #cell_4,
+[data-hoveredClass="6,4"] #row_3_cell_1 #cell_1,
+[data-hoveredClass="6,4"] #row_3_cell_2 #cell_2,
+[data-hoveredClass="6,4"] #row_3_cell_3 #cell_3,
+[data-hoveredClass="6,4"] #row_3_cell_4 #cell_4,
+[data-hoveredClass="6,4"] #row_4_cell_1 #cell_1,
+[data-hoveredClass="6,4"] #row_4_cell_2 #cell_2,
+[data-hoveredClass="6,4"] #row_4_cell_3 #cell_3,
+[data-hoveredClass="6,4"] #row_4_cell_4 #cell_4,
+[data-hoveredClass="6,4"] #row_5_cell_1 #cell_1,
+[data-hoveredClass="6,4"] #row_5_cell_2 #cell_2,
+[data-hoveredClass="6,4"] #row_5_cell_3 #cell_3,
+[data-hoveredClass="6,4"] #row_5_cell_4 #cell_4,
+[data-hoveredClass="6,4"] #row_6_cell_1 #cell_1,
+[data-hoveredClass="6,4"] #row_6_cell_2 #cell_2,
+[data-hoveredClass="6,4"] #row_6_cell_3 #cell_3,
+[data-hoveredClass="6,4"] #row_6_cell_4 #cell_4,
+[data-hoveredClass="6,5"] #row_1_cell_1 #cell_1,
+[data-hoveredClass="6,5"] #row_1_cell_2 #cell_2,
+[data-hoveredClass="6,5"] #row_1_cell_3 #cell_3,
+[data-hoveredClass="6,5"] #row_1_cell_4 #cell_4,
+[data-hoveredClass="6,5"] #row_1_cell_5 #cell_5,
+[data-hoveredClass="6,5"] #row_2_cell_1 #cell_1,
+[data-hoveredClass="6,5"] #row_2_cell_2 #cell_2,
+[data-hoveredClass="6,5"] #row_2_cell_3 #cell_3,
+[data-hoveredClass="6,5"] #row_2_cell_4 #cell_4,
+[data-hoveredClass="6,5"] #row_2_cell_5 #cell_5,
+[data-hoveredClass="6,5"] #row_3_cell_1 #cell_1,
+[data-hoveredClass="6,5"] #row_3_cell_2 #cell_2,
+[data-hoveredClass="6,5"] #row_3_cell_3 #cell_3,
+[data-hoveredClass="6,5"] #row_3_cell_4 #cell_4,
+[data-hoveredClass="6,5"] #row_3_cell_5 #cell_5,
+[data-hoveredClass="6,5"] #row_4_cell_1 #cell_1,
+[data-hoveredClass="6,5"] #row_4_cell_2 #cell_2,
+[data-hoveredClass="6,5"] #row_4_cell_3 #cell_3,
+[data-hoveredClass="6,5"] #row_4_cell_4 #cell_4,
+[data-hoveredClass="6,5"] #row_4_cell_5 #cell_5,
+[data-hoveredClass="6,5"] #row_5_cell_1 #cell_1,
+[data-hoveredClass="6,5"] #row_5_cell_2 #cell_2,
+[data-hoveredClass="6,5"] #row_5_cell_3 #cell_3,
+[data-hoveredClass="6,5"] #row_5_cell_4 #cell_4,
+[data-hoveredClass="6,5"] #row_5_cell_5 #cell_5,
+[data-hoveredClass="6,5"] #row_6_cell_1 #cell_1,
+[data-hoveredClass="6,5"] #row_6_cell_2 #cell_2,
+[data-hoveredClass="6,5"] #row_6_cell_3 #cell_3,
+[data-hoveredClass="6,5"] #row_6_cell_4 #cell_4,
+[data-hoveredClass="6,5"] #row_6_cell_5 #cell_5,
+[data-hoveredClass="6,6"] #row_1_cell_1 #cell_1,
+[data-hoveredClass="6,6"] #row_1_cell_2 #cell_2,
+[data-hoveredClass="6,6"] #row_1_cell_3 #cell_3,
+[data-hoveredClass="6,6"] #row_1_cell_4 #cell_4,
+[data-hoveredClass="6,6"] #row_1_cell_5 #cell_5,
+[data-hoveredClass="6,6"] #row_1_cell_6 #cell_6,
+[data-hoveredClass="6,6"] #row_2_cell_1 #cell_1,
+[data-hoveredClass="6,6"] #row_2_cell_2 #cell_2,
+[data-hoveredClass="6,6"] #row_2_cell_3 #cell_3,
+[data-hoveredClass="6,6"] #row_2_cell_4 #cell_4,
+[data-hoveredClass="6,6"] #row_2_cell_5 #cell_5,
+[data-hoveredClass="6,6"] #row_2_cell_6 #cell_6,
+[data-hoveredClass="6,6"] #row_3_cell_1 #cell_1,
+[data-hoveredClass="6,6"] #row_3_cell_2 #cell_2,
+[data-hoveredClass="6,6"] #row_3_cell_3 #cell_3,
+[data-hoveredClass="6,6"] #row_3_cell_4 #cell_4,
+[data-hoveredClass="6,6"] #row_3_cell_5 #cell_5,
+[data-hoveredClass="6,6"] #row_3_cell_6 #cell_6,
+[data-hoveredClass="6,6"] #row_4_cell_1 #cell_1,
+[data-hoveredClass="6,6"] #row_4_cell_2 #cell_2,
+[data-hoveredClass="6,6"] #row_4_cell_3 #cell_3,
+[data-hoveredClass="6,6"] #row_4_cell_4 #cell_4,
+[data-hoveredClass="6,6"] #row_4_cell_5 #cell_5,
+[data-hoveredClass="6,6"] #row_4_cell_6 #cell_6,
+[data-hoveredClass="6,6"] #row_5_cell_1 #cell_1,
+[data-hoveredClass="6,6"] #row_5_cell_2 #cell_2,
+[data-hoveredClass="6,6"] #row_5_cell_3 #cell_3,
+[data-hoveredClass="6,6"] #row_5_cell_4 #cell_4,
+[data-hoveredClass="6,6"] #row_5_cell_5 #cell_5,
+[data-hoveredClass="6,6"] #row_5_cell_6 #cell_6,
+[data-hoveredClass="6,6"] #row_6_cell_1 #cell_1,
+[data-hoveredClass="6,6"] #row_6_cell_2 #cell_2,
+[data-hoveredClass="6,6"] #row_6_cell_3 #cell_3,
+[data-hoveredClass="6,6"] #row_6_cell_4 #cell_4,
+[data-hoveredClass="6,6"] #row_6_cell_5 #cell_5,
+[data-hoveredClass="6,6"] #row_6_cell_6 #cell_6 {
+ background: #d5e4f9;
+ border: 1px solid #c0cffd;
+}
\ No newline at end of file
diff --git a/table.js b/table.js
new file mode 100644
index 0000000..5730465
--- /dev/null
+++ b/table.js
@@ -0,0 +1,324 @@
+import {create} from './documentUtils';
+import './styles/table.pcss';
+
+const CSS = {
+ table: 'tc-table',
+ inputField: 'tc-table__inp',
+ cell: 'tc-table__cell',
+ wrapper: 'tc-table__wrap',
+ area: 'tc-table__area',
+ highlight: 'tc-table__highlight'
+};
+
+/**
+ * Generates and manages _table contents.
+ */
+export class Table {
+ /**
+ * Creates
+ */
+ constructor() {
+ this._numberOfColumns = 0;
+ this._numberOfRows = 0;
+ this._element = this._createTableWrapper();
+ this._table = this._element.querySelector('table');
+ this._selectedCell = null;
+ this._attachEvents();
+ }
+
+ /**
+ * returns selected/editable cell or null if row is not selected
+ * @return {HTMLElement|null}
+ */
+ get selectedCell() {
+ return this._selectedCell;
+ }
+
+ /**
+ * sets a selected cell and highlights it
+ * @param cell - new current cell
+ */
+ set selectedCell(cell) {
+ if (this._selectedCell) {
+ this._selectedCell.classList.remove(CSS.highlight);
+ }
+
+ this._selectedCell = cell;
+
+ if (this._selectedCell) {
+ this._selectedCell.classList.add(CSS.highlight);
+ }
+ }
+
+ /**
+ * returns current a row that contains current cell
+ * or null if no cell selected
+ * @returns {HTMLElement|null}
+ */
+ get selectedRow() {
+ if (!this.selectedCell) return null;
+
+ return this.selectedCell.closest('tr');
+ }
+
+ /**
+ * Inserts column to the right from currently selected cell
+ */
+ insertColumnAfter() {
+ this.insertColumn(1);
+ this.focusCellOnSelectedCell();
+ }
+
+ /**
+ * Inserts column to the left from currently selected cell
+ */
+ insertColumnBefore() {
+ this.insertColumn();
+ this.focusCellOnSelectedCell();
+ }
+
+ /**
+ * Inserts new row below a current row
+ */
+ insertRowBefore() {
+ this.insertRow();
+ this.focusCellOnSelectedCell();
+ }
+
+ /**
+ * Inserts row above a current row
+ */
+ insertRowAfter() {
+ this.insertRow(1);
+ this.focusCellOnSelectedCell();
+ }
+
+ /**
+ * Insert a column into table relatively to a current cell
+ * @param {number} direction - direction of insertion. 0 is insertion before, 1 is insertion after
+ */
+ insertColumn(direction = 0) {
+ direction = Math.min(Math.max(direction, 0), 1);
+
+ const insertionIndex = this.selectedCell
+ ? this.selectedCell.cellIndex + direction
+ : 0;
+
+ this._numberOfColumns++;
+ /** Add cell in each row */
+ const rows = this._table.rows;
+
+ for (let i = 0; i < rows.length; i++) {
+ const cell = rows[i].insertCell(insertionIndex);
+
+ this._fillCell(cell);
+ }
+ };
+
+ /**
+ * Remove column that includes currently selected cell
+ * Do nothing if there's no current cell
+ */
+ deleteColumn() {
+ if (!this.selectedCell) return;
+
+ const removalIndex = this.selectedCell.cellIndex;
+
+ this._numberOfColumns--;
+ /** Delete cell in each row */
+ const rows = this._table.rows;
+
+ for (let i = 0; i < rows.length; i++) {
+ rows[i].deleteCell(removalIndex);
+ }
+ };
+
+ /**
+ * Insert a row into table relatively to a current cell
+ * @param {number} direction - direction of insertion. 0 is insertion before, 1 is insertion after
+ * @return {HTMLElement} row
+ */
+ insertRow(direction = 0) {
+ direction = Math.min(Math.max(direction, 0), 1);
+
+ const insertionIndex = this.selectedRow
+ ? this.selectedRow.rowIndex + direction
+ : 0;
+
+ const row = this._table.insertRow(insertionIndex);
+
+ this._numberOfRows++;
+
+ this._fillRow(row);
+ return row;
+ };
+
+ /**
+ * Remove row in table on index place
+ * @param {number} index - number in the array of columns, where new column to insert,-1 if insert at the end
+ */
+ deleteRow(index = -1) {
+ if (!this.selectedRow) return;
+
+ const removalIndex = this.selectedRow.rowIndex;
+
+ this._table.deleteRow(removalIndex);
+ this._numberOfRows--;
+ };
+
+ /**
+ * get html table wrapper
+ * @return {HTMLElement}
+ */
+ get htmlElement() {
+ return this._element;
+ }
+
+ /**
+ * get real table tag
+ * @return {HTMLElement}
+ */
+ get body() {
+ return this._table;
+ }
+
+ /**
+ * @private
+ *
+ * Creates table structure
+ * @return {HTMLElement} tbody - where rows will be
+ */
+ _createTableWrapper() {
+ return create('div', [ CSS.wrapper ], null, [
+ create('table', [ CSS.table ])
+ // This function can be updated so that it will render the table with the give config instead of 3x3
+ ]);
+ }
+
+ /**
+ * @private
+ *
+ * Create editable area of cell
+ * @return {HTMLElement} - the area
+ */
+ _createContenteditableArea() {
+ return create('div', [ CSS.inputField ], { contenteditable: 'true' });
+ }
+
+ /**
+ * @private
+ *
+ * Fills the empty cell of the editable area
+ * @param {HTMLElement} cell - empty cell
+ */
+ _fillCell(cell) {
+ cell.classList.add(CSS.cell);
+ const content = this._createContenteditableArea();
+ cell.appendChild(create('div', [ CSS.area ], null, [ content ]));
+ }
+
+ /**
+ * @private
+ *
+ * Fills the empty row with cells in the size of numberOfColumns
+ * @param row = the empty row
+ */
+ _fillRow(row) {
+ for (let i = 0; i < this._numberOfColumns; i++) {
+ const cell = row.insertCell();
+
+ this._fillCell(cell);
+ }
+ }
+
+ /**
+ * @private
+ *
+ * hang necessary events
+ */
+ _attachEvents() {
+ this._table.addEventListener('focus', (event) => {
+ this._focusEditField(event);
+ }, true);
+
+ this._table.addEventListener('keydown', (event) => {
+ this._pressedEnterInEditField(event);
+ });
+
+ this._table.addEventListener('click', (event) => {
+ this._clickedOnCell(event);
+ });
+
+ this.htmlElement.addEventListener('keydown', (event) => {
+ this._containerKeydown(event);
+ });
+ }
+
+ /**
+ * @private
+ *
+ * When you focus on an editable field, remembers the cell
+ * @param {FocusEvent} event
+ */
+ _focusEditField(event) {
+ this.selectedCell = event.target.tagName === 'TD'
+ ? event.target
+ : event.target.closest('td');
+ }
+ focusCellOnSelectedCell() {
+ this.selectedCell.childNodes[0].childNodes[0].focus();
+ }
+ /**
+ * @private
+ *
+ * When enter is pressed when editing a field
+ * @param {KeyboardEvent} event
+ */
+ _pressedEnterInEditField(event) {
+ if (!event.target.classList.contains(CSS.inputField)) {
+ return;
+ }
+ if (event.key === 'Enter' && !event.shiftKey) {
+ event.preventDefault();
+ }
+ }
+
+ /**
+ * @private
+ *
+ * When clicking on a cell
+ * @param {MouseEvent} event
+ */
+ _clickedOnCell(event) {
+ if (!event.target.classList.contains(CSS.cell)) {
+ return;
+ }
+ const content = event.target.querySelector('.' + CSS.inputField);
+
+ content.focus();
+ }
+
+ /**
+ * @private
+ *
+ * detects button presses when editing a table's content
+ * @param {KeyboardEvent} event
+ */
+ _containerKeydown(event) {
+ if (event.key === 'Enter' && event.ctrlKey) {
+ this._containerEnterPressed(event);
+ }
+ }
+
+ /**
+ * @private
+ *
+ * if "Ctrl + Enter" is pressed then create new line under current and focus it
+ * @param {KeyboardEvent} event
+ */
+ _containerEnterPressed(event) {
+ const newRow = this.insertRow(1);
+
+ newRow.cells[0].click();
+ }
+}
diff --git a/tableConstructor.js b/tableConstructor.js
new file mode 100644
index 0000000..57349e2
--- /dev/null
+++ b/tableConstructor.js
@@ -0,0 +1,108 @@
+import './styles/table-constructor.pcss';
+import {create} from './documentUtils';
+import {Table} from './table';
+
+const CSS = {
+ editor: 'tc-editor',
+ toolBarHor: 'tc-toolbar--hor',
+ toolBarVer: 'tc-toolbar--ver',
+ inputField: 'tc-table__inp'
+};
+
+/**
+ * Entry point. Controls table and give API to user
+ */
+export class TableConstructor {
+ /**
+ * Creates
+ * @param {TableData} data - previously saved data for insert in table
+ * @param {object} config - configuration of table
+ * @param {object} api - Editor.js API
+ */
+ constructor(data, config, api) {
+ /** creating table */
+ this._table = new Table();
+ const size = this._resizeTable(data, config);
+
+ this._fillTable(data, size);
+
+ /** creating container around table */
+ this._container = create('div', [CSS.editor, api.styles && api.styles.block], null, [
+ this._table.htmlElement
+ ]);
+ }
+
+ /**
+ * returns html element of TableConstructor;
+ * @return {HTMLElement}
+ */
+ get htmlElement() {
+ return this._container;
+ }
+
+ /**
+ * Returns instance of Table
+ * @returns {Table}
+ */
+ get table() {
+ return this._table;
+ }
+
+ /**
+ * @private
+ *
+ * Fill table data passed to the constructor
+ * @param {TableData} data - data for insert in table
+ * @param {{rows: number, cols: number}} size - contains number of rows and cols
+ */
+ _fillTable(data, size) {
+ if (data.content !== undefined) {
+ for (let i = 0; i < size.rows && i < data.content.length; i++) {
+ for (let j = 0; j < size.cols && j < data.content[i].length; j++) {
+ // get current cell and her editable part
+ const input = this._table.body.rows[i].cells[j].querySelector('.' + CSS.inputField);
+
+ input.innerHTML = data.content[i][j];
+ }
+ }
+ }
+ }
+
+ /**
+ * @private
+ *
+ * resize to match config or transmitted data
+ * @param {TableData} data - data for inserting to the table
+ * @param {object} config - configuration of table
+ * @param {number|string} config.rows - number of rows in configuration
+ * @param {number|string} config.cols - number of cols in configuration
+ * @return {{rows: number, cols: number}} - number of cols and rows
+ */
+ _resizeTable(data, config) {
+ const isValidArray = Array.isArray(data.content);
+ const isNotEmptyArray = isValidArray ? data.content.length : false;
+ const contentRows = isValidArray ? data.content.length : undefined;
+ const contentCols = isNotEmptyArray ? data.content[0].length : undefined;
+ const parsedRows = Number.parseInt(config.rows);
+ const parsedCols = Number.parseInt(config.cols);
+ // value of config have to be positive number
+ const configRows = !isNaN(parsedRows) && parsedRows > 0 ? parsedRows : undefined;
+ const configCols = !isNaN(parsedCols) && parsedCols > 0 ? parsedCols : undefined;
+ const defaultRows = 1;
+ const defaultCols = 1;
+ const rows = contentRows || configRows || defaultRows;
+ const cols = contentCols || configCols || defaultCols;
+
+ for (let i = 0; i < rows; i++) {
+ this._table.insertRow();
+ }
+ for (let i = 0; i < cols; i++) {
+ this._table.insertColumn();
+ }
+
+ return {
+ rows: rows,
+ cols: cols
+ };
+ }
+}