class Jesse { constructor(selector = null) { this.element = document.querySelector(selector); this.parent = null; this.mode = selector === "svg" ? "svg" : selector === "canvas" ? "canvas" : "html"; this.childrenArray = []; this.ctx = null; this.dataArray = null; this.dataParent = null; this.dataChildren = []; this.dataStyle = () => {}; } make(elementType = "div", parentElement = this.parent.element, classString = "") { if (!elementType) return; if (this.mode === "svg") { this.element = document.createElementNS("http://www.w3.org/2000/svg", elementType); } else if (elementType === "canvas") { this.element = document.createElement(elementType); this.ctx = this.element.getContext("2d"); } else if (this.ctx) { this.element = elementType; return this; } else { this.element = document.createElement(elementType); } this.classAdd(classString); parentElement.appendChild(this.element); return this; } append(elementType, classString = "") { if (elementType.charAt(0) === ".") { classString = elementType; elementType = "div"; } if ((this.isDataParent || this.dataParent) && this.isInitialized()) { let chosenChild = this.childrenArray.find((child) => child.classString === classString); chosenChild.datum = this.datum; return chosenChild; } let newObj = new Jesse(); newObj.classString = classString; if (classString) classString = classString.replace(".", " ").trimStart(); if (this.mode === "svg" || elementType === "svg") { newObj.mode = "svg"; } else if (elementType === "canvas") { newObj.mode = "canvas"; } else if (this.mode === "canvas") { newObj.mode = "canvas"; newObj.ctx = this.ctx; } if (this.isDataParent) { newObj.dataParent = this; } else { newObj.dataParent = this.dataParent; } newObj.make(elementType, this.element, classString); newObj.parent = this; this.childrenArray.push(newObj); return newObj; } draw(...args) { let c = this.ctx; args = args.map((arg) => { if (typeof arg === "function") { return arg(this.dataParent.datum, this.parent.index, this); } else { return arg; } }); if (this.element === "fillRect" || this.element === "rect") { let [x, y, w, h, fill] = args; c.fillStyle = fill; c[this.element](x, y, w, h); } else if (this.element === "text") { let [text, x, y, size, fill, font] = args; if (fill) c.fillStyle = fill; if (size && font) c.font = `${size}px ${font}`; if (size && !font) c.font = `${size}px ${c.font.split(" ")[1]}`; c.fillText(text, x, y); } else if (this.element === "line") { let [x0, y0, x1, y1, width, color, dashArray] = args; if (color) c.strokeStyle = color; if (width) c.lineWidth = width; c.setLineDash(dashArray ? dashArray : []); c.beginPath(); c.moveTo(x0, y0); c.lineTo(x1, y1); c.stroke(); } return this; } remove() { this.element.remove(); return this; } clear() { if (this.mode === "canvas") { this.ctx.clearRect(0, 0, this.ctx.canvas.width, this.ctx.canvas.height); } //else { this.element.innerHTML = ""; //} return this; } size(w, h) { if (this.mode !== "canvas") return this; this.element.width = w; this.element.height = h; this.element.style.width = w; this.element.style.height = h; return this; } text(text) { if (this.dataParent?.datum) { if (typeof text === "function") { this.element.innerText = text(this.dataParent.datum); } else { this.element.innerText = text; } } else { this.element.innerText = text; } return this; } html(html) { this.element.innerHTML = html; return this; } style(attribute, value) { if (this.dataParent?.datum) { if (typeof value === "function") { // Will need to find index in parent this.element.style[attribute] = value(this.dataParent.datum, this.parent.index, this); } else { this.element.style[attribute] = value; } } else { this.element.style[attribute] = value; } return this; } attr(attribute, value) { if (this.dataParent?.datum) { if (typeof value === "function") { // Will need to find index in parent this.element.setAttribute(attribute, value(this.dataParent.datum, this.parent.index)); } else { this.element.setAttribute(attribute, value); } } else { this.element.setAttribute(attribute, value); } return this; } classAdd(classString) { if (!classString) return; classString.split(" ").forEach((c) => this.element.classList.add(c)); return this; } classRemove(classString) { if (!classString) return; classString.split(" ").forEach((c) => this.element.classList.remove(c)); return this; } // Data data(data) { if (typeof data === "function") { this.dataArray = data(this.dataParent.datum); } else { this.dataArray = data; } this.dataArray.forEach((datum, i) => { if (this.dataChildren[i]) { this.dataChildren[i].dataArray = this.dataArray; this.dataChildren[i].datum = datum; } else { let newChild = new Jesse(); newChild.parent = this; newChild.isDataParent = true; newChild.mode = this.mode; newChild.ctx = this.ctx; newChild.datum = datum; newChild.dataArray = this.dataArray; newChild.element = this.element; newChild.index = i; this.dataChildren.push(newChild); } }); if (this.ctx && this.initialized) this.clear(); this.dataChildren.forEach((child, i) => { this.dataStyle(child); }); return this; } isDataChild() { if (this.dataChildren.length > 0) { return true; } else if (this.parent) { return this.parent.isDataChild(); } return false; } isInitialized() { if (this.initialized) { return true; } else if (this.parent) { return this.parent.isInitialized(); } return false; } max(key, returnObj = false) { let array = this.isDataParent ? this.dataArray : this.dataParent.dataArray; if (key) { let maxObj = array.reduce((acc, cur) => (cur[key] > acc[key] ? cur : acc), array[0]); return returnObj ? maxObj : maxObj[key]; } else { return array.reduce((acc, cur) => (cur > acc ? cur : acc), 0); } } vis(fn = () => {}) { this.dataChildren.forEach((child, i) => { this.dataStyle = fn; child.dataIndex = i; this.dataStyle(child, i); }); this.initialized = true; return this; } vis2(fn = () => {}) { this.dataChildren.forEach((child, i) => { this.dataStyle = fn; this.dataStyle(child.datum, i, child.dataArray, this); }); this.initialized = true; return this; } // Mouse interaction click(fn) { this.element.onclick = fn; return this; } mouseover(fn) { this.element.onmouseover = fn; return this; } mouseout(fn) { this.element.onmouseout = fn; return this; } }