From 04c01c442b065e674e091bdecdecca3ac9392a16 Mon Sep 17 00:00:00 2001 From: Nikhil Pawar Date: Wed, 13 Jul 2022 16:40:00 -0700 Subject: [PATCH 01/32] Initial commit --- css/viewer/annotationContainer.css | 10 +++++ js/toolbar/annotation-tool.js | 14 +++++++ js/viewer/annotation/annotation-group.js | 24 +++++++++++ js/viewer/annotation/annotation-svg.js | 2 + js/viewer/annotation/base.js | 23 ++++++++++ js/viewer/annotation/text.js | 53 ++++++++++++++++++++++++ mview.html | 1 + 7 files changed, 127 insertions(+) create mode 100644 js/viewer/annotation/text.js diff --git a/css/viewer/annotationContainer.css b/css/viewer/annotationContainer.css index 459585be..708fea75 100644 --- a/css/viewer/annotationContainer.css +++ b/css/viewer/annotationContainer.css @@ -47,3 +47,13 @@ #channel-names-overlay-container .channel-label-overlay.visible { display: inline-block; } + + +/* CSS for text annotation tool */ +.foreign { + text-align: left; + alignment-baseline: baseline; +} +.insideforeign { + display: inline-block; +} \ No newline at end of file diff --git a/js/toolbar/annotation-tool.js b/js/toolbar/annotation-tool.js index 1e84eaa0..df3da611 100644 --- a/js/toolbar/annotation-tool.js +++ b/js/toolbar/annotation-tool.js @@ -95,6 +95,9 @@ function AnnotationTool(parent){ "", "", "", + "", + "", + "", "", "", "", @@ -206,6 +209,17 @@ function AnnotationTool(parent){ } }); break; + case "TEXT": + _self.dispatchEvent("drawingStart", { + svgID : _self.curSVGID, + groupID : _self.curGroupID, + type : _self.curType, + attrs : { + "stroke" : _self.curStroke, + "fill" : _self.curStroke, + } + }); + break; case "ERASER": _self.dispatchEvent("setMode", { mode : "ERASE_ANNOTATIONS" diff --git a/js/viewer/annotation/annotation-group.js b/js/viewer/annotation/annotation-group.js index ccf737b6..6ceb3ed2 100644 --- a/js/viewer/annotation/annotation-group.js +++ b/js/viewer/annotation/annotation-group.js @@ -53,6 +53,30 @@ function AnnotationGroup(id, anatomy, description, parent){ case "LINE": annotation = new Line(attrs); break; + case "TEXT": + annotation = new Text(attrs); + + var myforeign = document.createElementNS('http://www.w3.org/2000/svg', 'foreignObject') + var textdiv = document.createElement("div"); + var textnode = document.createTextNode("This is the text"); + // var input = document.createElement("textarea"); + // input.name = "post"; + // input.maxLength = "5000"; + // input.cols = "80"; + // input.rows = "40"; + // textdiv.appendChild(input); + textdiv.appendChild(textnode); + textdiv.setAttribute("contentEditable", "true"); + textdiv.setAttribute("width", "auto"); + myforeign.setAttribute("width", "100%"); + myforeign.setAttribute("height", "100%"); + myforeign.classList.add("foreign"); //to make div fit text + textdiv.classList.add("insideforeign"); //to make div fit text + myforeign.setAttributeNS(null, "transform", "translate(18000 18000)"); + myforeign.appendChild(textdiv); + group = document.getElementById(this.id); + group.appendChild(myforeign); + break; case "ARROWLINE": // Create the marker definition and append it to the annotation group group = document.getElementById(this.id); diff --git a/js/viewer/annotation/annotation-svg.js b/js/viewer/annotation/annotation-svg.js index c40870ce..1333cfbc 100644 --- a/js/viewer/annotation/annotation-svg.js +++ b/js/viewer/annotation/annotation-svg.js @@ -55,6 +55,8 @@ function AnnotationSVG(parent, id, imgWidth, imgHeight, scale, ignoreReferencePo annotation = group.addAnnotation(type, subtype, attrs); annotation.renderSVG(group); + texts = document.getElementsByTagName("text"); + texts[0].textContent = "This is a new text"; annotation.setupDrawingAttrs(attrs); this.dispatchEvent("onDrawingBegin", { diff --git a/js/viewer/annotation/base.js b/js/viewer/annotation/base.js index 9b6459a3..ec94290b 100644 --- a/js/viewer/annotation/base.js +++ b/js/viewer/annotation/base.js @@ -267,3 +267,26 @@ Base.prototype.unbind = function(){ .on('click', null); } } + + + +Base.prototype.editText = function (localpoint) { + + var myforeign = document.createElementNS('http://www.w3.org/2000/svg', 'foreignObject') + var textdiv = document.createElement("div"); + var input = document.createElement("textarea"); + input.name = "post"; + input.maxLength = "5000"; + input.cols = "80"; + input.rows = "40"; + textdiv.appendChild(input); + textdiv.setAttribute("contentEditable", "true"); + textdiv.setAttribute("width", "auto"); + myforeign.setAttribute("width", "100%"); + myforeign.setAttribute("height", "100%"); + myforeign.classList.add("foreign"); //to make div fit text + textdiv.classList.add("insideforeign"); //to make div fit text + myforeign.setAttributeNS(null, "transform", "translate(" + localpoint.x + " " + localpoint.y + ")"); + myforeign.appendChild(textdiv); + this.svg.appendChild(myforeign); +} \ No newline at end of file diff --git a/js/viewer/annotation/text.js b/js/viewer/annotation/text.js new file mode 100644 index 00000000..d79926eb --- /dev/null +++ b/js/viewer/annotation/text.js @@ -0,0 +1,53 @@ +function Text(attrs) { + + attrs = attrs ? attrs : {}; + Base.call(this, attrs); + + this._tag = "text"; + this._attrs["x"] = attrs["x"] || null; + this._attrs["y"] = attrs["y"] || null; + this._attrs["fill"] = attrs["fill"] || "red"; + this._attrs["font-size"] = attrs["font-size"] || 2000; + this._attrs["font-weight"] = attrs["font-weight"] || 700; + this.svg = null; +} + +Text.prototype = Object.create(Base.prototype); +Text.prototype.constructor = Text; + +Text.prototype.insertPoint = function (point) { + var _self = this; + var updateAttrs = {}; + + // Only update the x and y attributes once when they are empty at the start + if (_self._attrs.x == null || _self._attrs.y == null) { + updateAttrs = { + x: point.x, + y: point.y, + }; + } + // _self.editText(point); + _self.setAttributesByJSON(updateAttrs); + _self.renderSVG(); +}; + +// Text.prototype.editText = function (point) { + +// var myforeign = document.createElementNS('http://www.w3.org/2000/svg', 'foreignObject') +// var textdiv = document.createElement("div"); +// var input = document.createElement("textarea"); +// input.name = "post"; +// input.maxLength = "5000"; +// input.cols = "80"; +// input.rows = "40"; +// div.appendChild(input); +// textdiv.setAttribute("contentEditable", "true"); +// textdiv.setAttribute("width", "auto"); +// myforeign.setAttribute("width", "100%"); +// myforeign.setAttribute("height", "100%"); +// myforeign.classList.add("foreign"); //to make div fit text +// textdiv.classList.add("insideforeign"); //to make div fit text +// myforeign.setAttributeNS(null, "transform", "translate(" + localpoint.x + " " + localpoint.y + ")"); +// myforeign.appendChild(textdiv); +// this.parent.svg.appendChild(myforeign); +// } \ No newline at end of file diff --git a/mview.html b/mview.html index 137b25d2..df2efbce 100644 --- a/mview.html +++ b/mview.html @@ -81,6 +81,7 @@ + From df2c127a1bc66c639898e4d71ba0802fddfb22c1 Mon Sep 17 00:00:00 2001 From: Nikhil Pawar Date: Thu, 28 Jul 2022 14:50:25 -0700 Subject: [PATCH 02/32] Implemented foreign object based textarea: --- css/viewer/annotationContainer.css | 5 ++ js/viewer/annotation/annotation-group.js | 81 ++++++++++++++++++------ js/viewer/annotation/annotation-svg.js | 2 - js/viewer/viewer.js | 3 +- 4 files changed, 67 insertions(+), 24 deletions(-) diff --git a/css/viewer/annotationContainer.css b/css/viewer/annotationContainer.css index 708fea75..bec63061 100644 --- a/css/viewer/annotationContainer.css +++ b/css/viewer/annotationContainer.css @@ -56,4 +56,9 @@ } .insideforeign { display: inline-block; + border: 100px solid red; +} +#textInput { + width: 100%; + height: 100%; } \ No newline at end of file diff --git a/js/viewer/annotation/annotation-group.js b/js/viewer/annotation/annotation-group.js index 6ceb3ed2..2349aa9e 100644 --- a/js/viewer/annotation/annotation-group.js +++ b/js/viewer/annotation/annotation-group.js @@ -55,27 +55,7 @@ function AnnotationGroup(id, anatomy, description, parent){ break; case "TEXT": annotation = new Text(attrs); - - var myforeign = document.createElementNS('http://www.w3.org/2000/svg', 'foreignObject') - var textdiv = document.createElement("div"); - var textnode = document.createTextNode("This is the text"); - // var input = document.createElement("textarea"); - // input.name = "post"; - // input.maxLength = "5000"; - // input.cols = "80"; - // input.rows = "40"; - // textdiv.appendChild(input); - textdiv.appendChild(textnode); - textdiv.setAttribute("contentEditable", "true"); - textdiv.setAttribute("width", "auto"); - myforeign.setAttribute("width", "100%"); - myforeign.setAttribute("height", "100%"); - myforeign.classList.add("foreign"); //to make div fit text - textdiv.classList.add("insideforeign"); //to make div fit text - myforeign.setAttributeNS(null, "transform", "translate(18000 18000)"); - myforeign.appendChild(textdiv); - group = document.getElementById(this.id); - group.appendChild(myforeign); + this.addTextBox(); break; case "ARROWLINE": // Create the marker definition and append it to the annotation group @@ -107,6 +87,65 @@ function AnnotationGroup(id, anatomy, description, parent){ return annotation; } + + this.addTextBox = function () { + + var svgForeignObj = document.createElementNS('http://www.w3.org/2000/svg', 'foreignObject') + var textOuterDiv = document.createElementNS("http://www.w3.org/1999/xhtml", "div"); + var textInput = document.createElementNS("http://www.w3.org/1999/xhtml", "textarea"); + + // textInput.setAttribute("cols", 50); + // textInput.setAttribute("rows", 5); + // textInput.setAttribute("font-size", "1000px"); + textInput.setAttribute("id", "textInput"); + textInput.setAttribute("tabindex", "-1"); + textInput.style.fontSize = "1000px"; + + // textInput.oninput = function(e) { + // e.stopImmediatePropagation(); + // // console.log(textInput.value); + // }; + // textInput.onpointerdown = function(evt) { + // // console.log(evt); + // evt.stopImmediatePropagation(); + // }; + // textInput.keydown = function(evt) { + // console.log(evt); + // evt.stopImmediatePropagation(); + // }; + // textInput.keyup = function(evt) { + // console.log(evt); + // evt.stopImmediatePropagation(); + // }; + + textOuterDiv.setAttribute("contentEditable", "true"); + textInput.setAttribute("contentEditable", "true"); + svgForeignObj.setAttribute("contentEditable", "true"); + textOuterDiv.setAttribute("width", "auto"); + svgForeignObj.setAttribute("width", "100%"); + svgForeignObj.setAttribute("height", "100%"); + textOuterDiv.appendChild(textInput); + svgForeignObj.classList.add("foreign"); //to make div fit text + textOuterDiv.classList.add("insideforeign"); //to make div fit text + svgForeignObj.setAttributeNS(null, "transform", "translate(18000 18000)"); + svgForeignObj.appendChild(textOuterDiv); + group = document.getElementById(this.id); + group.appendChild(svgForeignObj); + // group.setAttribute("contentEditable", "true"); + textInput.addEventListener("click", this.clickedInput); + document.getElementById("textInput").addEventListener("keydown", function (e) { + e.stopImmediatePropagation(); + console.log(e); + console.log(e.key); + }); + } + + this.clickedInput = function (e) { + e.stopImmediatePropagation(); + // console.log(e); + // console.log("clicked textInput"); + } + /** * This function creates the marker definition needed for the arrowline annotation tool. * The returned definition gets added to the annotation SVG. diff --git a/js/viewer/annotation/annotation-svg.js b/js/viewer/annotation/annotation-svg.js index 1333cfbc..c40870ce 100644 --- a/js/viewer/annotation/annotation-svg.js +++ b/js/viewer/annotation/annotation-svg.js @@ -55,8 +55,6 @@ function AnnotationSVG(parent, id, imgWidth, imgHeight, scale, ignoreReferencePo annotation = group.addAnnotation(type, subtype, attrs); annotation.renderSVG(group); - texts = document.getElementsByTagName("text"); - texts[0].textContent = "This is a new text"; annotation.setupDrawingAttrs(attrs); this.dispatchEvent("onDrawingBegin", { diff --git a/js/viewer/viewer.js b/js/viewer/viewer.js index cc2b59db..9f9b9051 100644 --- a/js/viewer/viewer.js +++ b/js/viewer/viewer.js @@ -54,7 +54,8 @@ function Viewer(parent, config) { // configure osd this.osd = OpenSeadragon(this.config.osd); - + this.osd.innerTracker.keyHandler = null; + // show spinner while initializing the page this.resetSpinner(true); From b996112590b6542b75208297ddc2585686c0ee5f Mon Sep 17 00:00:00 2001 From: Nikhil Pawar Date: Fri, 26 Aug 2022 15:47:50 -0700 Subject: [PATCH 03/32] Implemented resizing and moving of input div MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Implemented the four corner resizing of the text input div and movement of the input div. To do in future commits: • Switching the text input to SVG text and vice versa. • Styling of the input div. • Checking the disable mouse trackers and enabling them. --- css/viewer/annotationContainer.css | 85 ++++++++ js/viewer/annotation/annotation-group.js | 266 ++++++++++++++++++++--- js/viewer/annotation/annotation-svg.js | 3 +- js/viewer/viewer.js | 19 ++ vendor/openseadragon.js | 2 +- 5 files changed, 343 insertions(+), 32 deletions(-) diff --git a/css/viewer/annotationContainer.css b/css/viewer/annotationContainer.css index bec63061..77781894 100644 --- a/css/viewer/annotationContainer.css +++ b/css/viewer/annotationContainer.css @@ -53,12 +53,97 @@ .foreign { text-align: left; alignment-baseline: baseline; + overflow: visible; } .insideforeign { display: inline-block; border: 100px solid red; + position: absolute; + padding: 500px; +} + +.insideforeign:hover { + cursor: move; } + #textInput { width: 100%; height: 100%; +} + +.insideforeign .resizer-topleft { + border: 100px solid red; + width: 600px; + height: 600px; + background: red; + z-index: 10; + position: absolute; + top: -300px; + left: -300px ; + cursor: nw-resize; +} + +.insideforeign .resizer-topright { + border: 100px solid red; + + width: 600px; + height: 600px; + background: red; + z-index: 10; + position: absolute; + top: -300px; + right: -300px; + cursor: ne-resize; +} + +.insideforeign .resizer-bottomright { + border: 100px solid red; + + width: 600px; + height: 600px; + background: red; + z-index: 10; + position: absolute; + bottom: -300px; + right: -300px; + cursor: se-resize; +} + +.insideforeign .resizer-bottomleft { + border: 100px solid red; + + width: 600px; + height: 600px; + background: red; + z-index: 10; + position: absolute; + bottom: -300px; + left: -300px; + cursor: sw-resize; +} + + + +.insideforeign .move-header { + cursor: move; + position: absolute; + top: 0; + left: 0; + height: 500px; + width: 100%; + z-index: 10; + background-color: #2196f3; + color: #fff; +} + +/*NOSELECT*/ + +#divid * { + -webkit-touch-callout: none; /* iOS Safari */ + -webkit-user-select: none; /* Safari */ + -khtml-user-select: none; /* Konqueror HTML */ + -moz-user-select: none; /* Firefox */ + -ms-user-select: none; /* Internet Explorer/Edge */ + user-select: none; /* Non-prefixed version, currently + supported by Chrome and Opera */ } \ No newline at end of file diff --git a/js/viewer/annotation/annotation-group.js b/js/viewer/annotation/annotation-group.js index 2349aa9e..93784d6a 100644 --- a/js/viewer/annotation/annotation-group.js +++ b/js/viewer/annotation/annotation-group.js @@ -17,6 +17,9 @@ function AnnotationGroup(id, anatomy, description, parent){ this.isDisplay = true; this.isSelected = false; + // Text height + this.textHeight = 1000; + // the stroke that is used by annotations in this group this.stroke = []; @@ -87,63 +90,266 @@ function AnnotationGroup(id, anatomy, description, parent){ return annotation; } - + /** + * This function will add a textbox div to the SVG. This will be used to edit the text and would be later moved to the text.js + * once the feature to switch to and fro from text is implemented. + */ this.addTextBox = function () { var svgForeignObj = document.createElementNS('http://www.w3.org/2000/svg', 'foreignObject') var textOuterDiv = document.createElementNS("http://www.w3.org/1999/xhtml", "div"); var textInput = document.createElementNS("http://www.w3.org/1999/xhtml", "textarea"); - // textInput.setAttribute("cols", 50); - // textInput.setAttribute("rows", 5); - // textInput.setAttribute("font-size", "1000px"); + // This would later need a unique ID for each text textInput.setAttribute("id", "textInput"); - textInput.setAttribute("tabindex", "-1"); - textInput.style.fontSize = "1000px"; - - // textInput.oninput = function(e) { - // e.stopImmediatePropagation(); - // // console.log(textInput.value); - // }; - // textInput.onpointerdown = function(evt) { - // // console.log(evt); - // evt.stopImmediatePropagation(); - // }; - // textInput.keydown = function(evt) { - // console.log(evt); - // evt.stopImmediatePropagation(); - // }; - // textInput.keyup = function(evt) { - // console.log(evt); - // evt.stopImmediatePropagation(); - // }; - + + textInput.oninput = function(e) { + e.stopImmediatePropagation(); + }; + textInput.onpointerdown = function(evt) { + evt.stopImmediatePropagation(); + }; + textInput.keydown = function(evt) { + evt.stopImmediatePropagation(); + }; + textInput.keyup = function(evt) { + evt.stopImmediatePropagation(); + }; + textOuterDiv.setAttribute("contentEditable", "true"); textInput.setAttribute("contentEditable", "true"); svgForeignObj.setAttribute("contentEditable", "true"); textOuterDiv.setAttribute("width", "auto"); + textOuterDiv.setAttribute("tabindex", "-1"); svgForeignObj.setAttribute("width", "100%"); svgForeignObj.setAttribute("height", "100%"); textOuterDiv.appendChild(textInput); svgForeignObj.classList.add("foreign"); //to make div fit text textOuterDiv.classList.add("insideforeign"); //to make div fit text + + // This would need to take the value from the click trackers of OSD to decide the position of the text. svgForeignObj.setAttributeNS(null, "transform", "translate(18000 18000)"); svgForeignObj.appendChild(textOuterDiv); + group = document.getElementById(this.id); group.appendChild(svgForeignObj); - // group.setAttribute("contentEditable", "true"); - textInput.addEventListener("click", this.clickedInput); + group.setAttribute("contentEditable", "true"); + textOuterDiv.addEventListener("mousedown", this.clickedInput); + document.getElementById("textInput").addEventListener("keydown", function (e) { e.stopImmediatePropagation(); - console.log(e); - console.log(e.key); + this.style.height = (this.scrollHeight) + "px"; + this.textHeight = this.scrollHeight; }); + + this.initResizeElement(); + this.initDragElement(); + + textInput.setAttribute("style", "height:" + this.textHeight + "px;overflow-y:hidden;"); + textInput.style.fontSize = "1000px"; + textInput.addEventListener("input", OnInput, false); } this.clickedInput = function (e) { e.stopImmediatePropagation(); - // console.log(e); - // console.log("clicked textInput"); + console.log(e); + console.log("clicked textInput"); + } + + function OnInput() { + this.style.height = "auto"; + this.style.height = (this.scrollHeight) + "px"; + } + + this.initDragElement = function () { + + pos1 = 0, pos2 = 0, pos3 = 0, pos4 = 0; + + var divCont = document.getElementsByClassName("insideforeign")[0]; + var elmnt = null; + var currentZIndex = 100; + divCont.onmousedown = function() { + this.style.zIndex = "" + ++currentZIndex; + }; + + divCont.onmousedown = dragMouseDown; + divCont.onpointerdown = dragMouseDown; + + function dragMouseDown(e) { + e.stopImmediatePropagation(); + console.log(e); + elmnt = e.target; + elmnt.style.zIndex = "" + ++currentZIndex; + + e = e || window.event; + // get the mouse cursor position at startup: + pos3 = e.clientX; + pos4 = e.clientY; + divCont.onmouseup = closeDragElement; + divCont.onpointerup = closeDragElement; + divCont.onmouseleave = closeDragElement; + // call a function whenever the cursor moves: + document.onmousemove = elementDrag; + } + + function elementDrag(e) { + if (!elmnt) { + return; + } + + e = e || window.event; + // calculate the new cursor position: + pos1 = pos3 - e.clientX; + pos2 = pos4 - e.clientY; + pos3 = e.clientX; + pos4 = e.clientY; + // set the element's new position: + elmnt.style.top = elmnt.offsetTop - 40 * pos2 + "px"; + elmnt.style.left = elmnt.offsetLeft - 40 * pos1 + "px"; + } + + function closeDragElement(e) { + console.log(e); + /* stop moving when mouse button is released:*/ + document.onmouseup = null; + document.onmousemove = null; + document.onpointerup = null; + } + + function getHeader(element) { + var headerItems = element.getElementsByClassName("move-header"); + + if (headerItems.length === 1) { + return headerItems[0]; + } + + return null; + } + } + + // This function would be used to add the resizing functionality to the text box. This would need to be executed for every text + this.initResizeElement = function () { + + var divCont = document.getElementsByClassName("insideforeign")[0]; + var element = null; + var startX, startY, startWidth, startHeight; + + // Creating the four corners for resizing + + topleft = document.createElement("div"); + topleft.className = "resizer-topleft"; + divCont.appendChild(topleft); + topleft.addEventListener("pointerdown", initDrag, false); + topleft.parentPopup = divCont; + + bottomleft = document.createElement("div"); + bottomleft.className = "resizer-bottomleft"; + divCont.appendChild(bottomleft); + bottomleft.addEventListener("pointerdown", initDrag, false); + bottomleft.parentPopup = divCont; + + topright = document.createElement("div"); + topright.className = "resizer-topright"; + divCont.appendChild(topright); + topright.addEventListener("pointerdown", initDrag, false); + topright.parentPopup = divCont; + + bottomright = document.createElement("div"); + bottomright.className = "resizer-bottomright"; + divCont.appendChild(bottomright); + bottomright.addEventListener("pointerdown", initDrag, false); + bottomright.parentPopup = divCont; + + // var right = document.createElement("div"); + // right.className = "resizer-right"; + // divCont.appendChild(right); + // right.addEventListener("mousedown", function (e) { + // console.log("Clicked on div"); + // }); + // right.parentPopup = divCont; + + // var bottom = document.createElement("div", true); + // bottom.className = "resizer-bottom"; + // divCont.appendChild(bottom); + // bottom.addEventListener("pointerdown", function (e) { + // e.stopImmediatePropagation(); + // }); + + // bottom.parentPopup = divCont; + // bottom.setAttribute("tabindex", "-1"); + // both = document.createElement("div"); + // both.className = "resizer-both"; + // divCont.appendChild(both); + // bottom.addEventListener("mousedown", initDrag); + // both.addEventListener("click", function (e) { + // console.log(e); + // console.log("Clicked on div"); + // }); + // both.parentPopup = divCont; + + function initDrag(e) { + e.stopImmediatePropagation(); + console.log("Inside"); + element = this.parentPopup; + resizer = e.target.className; + + startX = e.clientX; + startY = e.clientY; + startWidth = parseInt( + document.defaultView.getComputedStyle(element).width, + 10 + ); + startHeight = parseInt( + document.defaultView.getComputedStyle(element).height, + 10 + ); + + document.documentElement.addEventListener("mousemove", doDrag, false); + document.documentElement.addEventListener("pointerup", stopDrag, false); + document.documentElement.addEventListener("mouseup", stopDrag, false); + } + + function doDrag(e) { + console.log("doing drag"); + element.style.position = "absolute"; + switch(resizer) { + case "resizer-bottomleft": + element.style.width = startWidth + 12 * (startX - e.clientX)+ "px"; + element.style.left = startX + 12 * (e.clientX - startX) + "px"; + // element.style.top = startY + (e.clientY - startY) + "px"; + element.style.height = startHeight + 12 * (e.clientY - startY) + "px"; + break; + case "resizer-topleft": + element.style.width = startWidth + 12 * (startX - e.clientX)+ "px"; + element.style.left = startX + 12 * (e.clientX - startX) + "px"; + element.style.top = startY + 12 * (e.clientY - startY) + "px"; + element.style.height = startHeight + 12 * (startY - e.clientY) + "px"; + break; + case "resizer-topright": + element.style.width = startWidth + 12 * (e.clientX - startX) + "px"; + // element.style.left = startX + (e.clientX - startX) + "px"; + element.style.top = startY + 12 * (e.clientY - startY) + "px"; + element.style.height = startHeight + 12 * (startY - e.clientY) + "px"; + break; + case "resizer-bottomright": + element.style.width = startWidth + 12 * (e.clientX - startX) + "px"; + // element.style.left = startX + (e.clientX - startX) + "px"; + // element.style.top = startY + (e.clientY - startY) + "px"; + element.style.height = startHeight + 12 * (e.clientY - startY) + "px"; + break; + default: + break; + } + // element.style.width = startWidth + 12 * (e.clientX - startX) + "px"; + // element.style.height = startHeight + 12 * (e.clientY - startY) + "px"; + } + + function stopDrag() { + console.log("Stopping drag"); + document.documentElement.removeEventListener("mousemove", doDrag, false); + document.documentElement.removeEventListener("pointerup", stopDrag, false); + document.documentElement.removeEventListener("mouseup", stopDrag, false); + } } /** diff --git a/js/viewer/annotation/annotation-svg.js b/js/viewer/annotation/annotation-svg.js index c40870ce..d713fa95 100644 --- a/js/viewer/annotation/annotation-svg.js +++ b/js/viewer/annotation/annotation-svg.js @@ -56,7 +56,6 @@ function AnnotationSVG(parent, id, imgWidth, imgHeight, scale, ignoreReferencePo annotation = group.addAnnotation(type, subtype, attrs); annotation.renderSVG(group); annotation.setupDrawingAttrs(attrs); - this.dispatchEvent("onDrawingBegin", { svgID : this.id, groupID : groupID, @@ -69,6 +68,8 @@ function AnnotationSVG(parent, id, imgWidth, imgHeight, scale, ignoreReferencePo subtype: subtype, attrs : attrs }) + this.dispatchEvent("removeHandlers", null); + } } diff --git a/js/viewer/viewer.js b/js/viewer/viewer.js index 9f9b9051..997b012f 100644 --- a/js/viewer/viewer.js +++ b/js/viewer/viewer.js @@ -54,6 +54,8 @@ function Viewer(parent, config) { // configure osd this.osd = OpenSeadragon(this.config.osd); + + // Added to disable the key tracking temporarily this.osd.innerTracker.keyHandler = null; // show spinner while initializing the page @@ -472,6 +474,9 @@ function Viewer(parent, config) { }); } break; + case "removeHandlers": + this.removeHandler(); + break; default: this.parent.dispatchEvent(type, data); break; @@ -1090,6 +1095,20 @@ function Viewer(parent, config) { // _self.destoryMouseTracker(); } + this.removeHandler = function (event) { + + if (_self.mouseTrackers.length > 0) { + + _self.mouseTrackers[0].destroy(); + _self.mouseTrackers.shift(); + } + this.osd.innerTracker.clickHanlder = null; + this.osd.innerTracker.pressHandler = null; + this.osd.innerTracker.releaseHandler = null; + this.osd.panVertical = false; + this.osd.panHorizontal = false; + } + // Pan to specific location this.panTo = function (x, y, z) { var centerPoint = new OpenSeadragon.Point(x, y); diff --git a/vendor/openseadragon.js b/vendor/openseadragon.js index 87f4276d..bc866e62 100644 --- a/vendor/openseadragon.js +++ b/vendor/openseadragon.js @@ -5058,7 +5058,7 @@ $.EventSource.prototype = { */ function onMouseDown( tracker, event ) { var gPoint; - + console.log("laskdjfasdlk"); event = $.getEvent( event ); gPoint = { From e864c71fabc37e2f2f930d538a6f18c9e48587c8 Mon Sep 17 00:00:00 2001 From: Nikhil Pawar Date: Tue, 18 Oct 2022 11:29:06 -0700 Subject: [PATCH 04/32] Style changes to textarea --- css/viewer/annotationContainer.css | 2 + js/viewer/annotation/annotation-group.js | 491 +++++++++++------------ js/viewer/annotation/annotation-svg.js | 10 +- js/viewer/annotation/base.js | 27 +- js/viewer/annotation/text.js | 289 +++++++++++-- js/viewer/viewer.js | 14 +- vendor/openseadragon.js | 1 - 7 files changed, 522 insertions(+), 312 deletions(-) diff --git a/css/viewer/annotationContainer.css b/css/viewer/annotationContainer.css index 77781894..90427495 100644 --- a/css/viewer/annotationContainer.css +++ b/css/viewer/annotationContainer.css @@ -69,6 +69,8 @@ #textInput { width: 100%; height: 100%; + background: transparent; + /* color: white; */ } .insideforeign .resizer-topleft { diff --git a/js/viewer/annotation/annotation-group.js b/js/viewer/annotation/annotation-group.js index 93784d6a..e5beaf33 100644 --- a/js/viewer/annotation/annotation-group.js +++ b/js/viewer/annotation/annotation-group.js @@ -17,9 +17,6 @@ function AnnotationGroup(id, anatomy, description, parent){ this.isDisplay = true; this.isSelected = false; - // Text height - this.textHeight = 1000; - // the stroke that is used by annotations in this group this.stroke = []; @@ -58,7 +55,7 @@ function AnnotationGroup(id, anatomy, description, parent){ break; case "TEXT": annotation = new Text(attrs); - this.addTextBox(); + annotation.addTextBox(this.id); break; case "ARROWLINE": // Create the marker definition and append it to the annotation group @@ -90,267 +87,249 @@ function AnnotationGroup(id, anatomy, description, parent){ return annotation; } - /** - * This function will add a textbox div to the SVG. This will be used to edit the text and would be later moved to the text.js - * once the feature to switch to and fro from text is implemented. - */ - this.addTextBox = function () { + // /** + // * This function will add a textbox div to the SVG. This will be used to edit the text and would be later moved to the text.js + // * once the feature to switch to and fro from text is implemented. + // */ + // this.addTextBox = function () { - var svgForeignObj = document.createElementNS('http://www.w3.org/2000/svg', 'foreignObject') - var textOuterDiv = document.createElementNS("http://www.w3.org/1999/xhtml", "div"); - var textInput = document.createElementNS("http://www.w3.org/1999/xhtml", "textarea"); + // var svgForeignObj = document.createElementNS('http://www.w3.org/2000/svg', 'foreignObject') + // var textOuterDiv = document.createElementNS("http://www.w3.org/1999/xhtml", "div"); + // var textInput = document.createElementNS("http://www.w3.org/1999/xhtml", "textarea"); - // This would later need a unique ID for each text - textInput.setAttribute("id", "textInput"); + // // This would later need a unique ID for each text + // textInput.setAttribute("id", "textInput"); - textInput.oninput = function(e) { - e.stopImmediatePropagation(); - }; - textInput.onpointerdown = function(evt) { - evt.stopImmediatePropagation(); - }; - textInput.keydown = function(evt) { - evt.stopImmediatePropagation(); - }; - textInput.keyup = function(evt) { - evt.stopImmediatePropagation(); - }; + // textInput.oninput = function(e) { + // e.stopImmediatePropagation(); + // }; + // textInput.onpointerdown = function(evt) { + // evt.stopImmediatePropagation(); + // }; + // textInput.keydown = function(evt) { + // evt.stopImmediatePropagation(); + // }; + // textInput.keyup = function(evt) { + // evt.stopImmediatePropagation(); + // }; - textOuterDiv.setAttribute("contentEditable", "true"); - textInput.setAttribute("contentEditable", "true"); - svgForeignObj.setAttribute("contentEditable", "true"); - textOuterDiv.setAttribute("width", "auto"); - textOuterDiv.setAttribute("tabindex", "-1"); - svgForeignObj.setAttribute("width", "100%"); - svgForeignObj.setAttribute("height", "100%"); - textOuterDiv.appendChild(textInput); - svgForeignObj.classList.add("foreign"); //to make div fit text - textOuterDiv.classList.add("insideforeign"); //to make div fit text - - // This would need to take the value from the click trackers of OSD to decide the position of the text. - svgForeignObj.setAttributeNS(null, "transform", "translate(18000 18000)"); - svgForeignObj.appendChild(textOuterDiv); - - group = document.getElementById(this.id); - group.appendChild(svgForeignObj); - group.setAttribute("contentEditable", "true"); - textOuterDiv.addEventListener("mousedown", this.clickedInput); - - document.getElementById("textInput").addEventListener("keydown", function (e) { - e.stopImmediatePropagation(); - this.style.height = (this.scrollHeight) + "px"; - this.textHeight = this.scrollHeight; - }); - - this.initResizeElement(); - this.initDragElement(); - - textInput.setAttribute("style", "height:" + this.textHeight + "px;overflow-y:hidden;"); - textInput.style.fontSize = "1000px"; - textInput.addEventListener("input", OnInput, false); - } - - this.clickedInput = function (e) { - e.stopImmediatePropagation(); - console.log(e); - console.log("clicked textInput"); - } - - function OnInput() { - this.style.height = "auto"; - this.style.height = (this.scrollHeight) + "px"; - } - - this.initDragElement = function () { + // textOuterDiv.setAttribute("contentEditable", "true"); + // textInput.setAttribute("contentEditable", "true"); + // svgForeignObj.setAttribute("contentEditable", "true"); + // textOuterDiv.setAttribute("width", "auto"); + // textOuterDiv.setAttribute("tabindex", "-1"); + // svgForeignObj.setAttribute("width", "100%"); + // svgForeignObj.setAttribute("height", "100%"); + // textOuterDiv.appendChild(textInput); + // svgForeignObj.classList.add("foreign"); //to make div fit text + // textOuterDiv.classList.add("insideforeign"); //to make div fit text + + // // This would need to take the value from the click trackers of OSD to decide the position of the text. + // svgForeignObj.setAttributeNS(null, "transform", "translate(18000 18000)"); + // svgForeignObj.appendChild(textOuterDiv); + + // group = document.getElementById(this.id); + // group.appendChild(svgForeignObj); + // group.setAttribute("contentEditable", "true"); + // textOuterDiv.addEventListener("mousedown", this.clickedInput); + + // document.getElementById("textInput").addEventListener("keydown", function (e) { + // e.stopImmediatePropagation(); + // console.log("laskdjfl"); + // this.style.height = (this.scrollHeight) + "px"; + // this.textHeight = this.scrollHeight; + // }); + + // this.initResizeElement(); + // this.initDragElement(); + + // textInput.setAttribute("style", "height:" + this.textHeight + "px;overflow-y:hidden;"); + // textInput.style.fontSize = "1000px"; + // textInput.addEventListener("input", OnInput, false); + // } + + // this.clickedInput = function (e) { + // e.stopImmediatePropagation(); + // console.log(e); + // console.log("clicked textInput"); + // } + + // function OnInput() { + // console.log("Input"); + // this.style.height = "auto"; + // this.style.height = (this.scrollHeight) + "px"; + // } + + // this.initDragElement = function () { - pos1 = 0, pos2 = 0, pos3 = 0, pos4 = 0; + // pos1 = 0, pos2 = 0, pos3 = 0, pos4 = 0; - var divCont = document.getElementsByClassName("insideforeign")[0]; - var elmnt = null; - var currentZIndex = 100; - divCont.onmousedown = function() { - this.style.zIndex = "" + ++currentZIndex; - }; - - divCont.onmousedown = dragMouseDown; - divCont.onpointerdown = dragMouseDown; - - function dragMouseDown(e) { - e.stopImmediatePropagation(); - console.log(e); - elmnt = e.target; - elmnt.style.zIndex = "" + ++currentZIndex; - - e = e || window.event; - // get the mouse cursor position at startup: - pos3 = e.clientX; - pos4 = e.clientY; - divCont.onmouseup = closeDragElement; - divCont.onpointerup = closeDragElement; - divCont.onmouseleave = closeDragElement; - // call a function whenever the cursor moves: - document.onmousemove = elementDrag; - } - - function elementDrag(e) { - if (!elmnt) { - return; - } - - e = e || window.event; - // calculate the new cursor position: - pos1 = pos3 - e.clientX; - pos2 = pos4 - e.clientY; - pos3 = e.clientX; - pos4 = e.clientY; - // set the element's new position: - elmnt.style.top = elmnt.offsetTop - 40 * pos2 + "px"; - elmnt.style.left = elmnt.offsetLeft - 40 * pos1 + "px"; - } - - function closeDragElement(e) { - console.log(e); - /* stop moving when mouse button is released:*/ - document.onmouseup = null; - document.onmousemove = null; - document.onpointerup = null; - } - - function getHeader(element) { - var headerItems = element.getElementsByClassName("move-header"); - - if (headerItems.length === 1) { - return headerItems[0]; - } - - return null; - } - } - - // This function would be used to add the resizing functionality to the text box. This would need to be executed for every text - this.initResizeElement = function () { - - var divCont = document.getElementsByClassName("insideforeign")[0]; - var element = null; - var startX, startY, startWidth, startHeight; - - // Creating the four corners for resizing - - topleft = document.createElement("div"); - topleft.className = "resizer-topleft"; - divCont.appendChild(topleft); - topleft.addEventListener("pointerdown", initDrag, false); - topleft.parentPopup = divCont; + // var divCont = document.getElementsByClassName("insideforeign")[0]; + // var elmnt = null; + // var currentZIndex = 100; + // divCont.onmousedown = function() { + // this.style.zIndex = "" + ++currentZIndex; + // }; + + // divCont.onmousedown = dragMouseDown; + // divCont.onpointerdown = dragMouseDown; + + // function dragMouseDown(e) { + // e.stopImmediatePropagation(); + // console.log(e); + // elmnt = e.target; + // elmnt.style.zIndex = "" + ++currentZIndex; + + // e = e || window.event; + // // get the mouse cursor position at startup: + // pos3 = e.clientX; + // pos4 = e.clientY; + // divCont.onmouseup = closeDragElement; + // divCont.onpointerup = closeDragElement; + // divCont.onmouseleave = closeDragElement; + // // call a function whenever the cursor moves: + // document.onmousemove = elementDrag; + // } + + // function elementDrag(e) { + // if (!elmnt) { + // return; + // } + + // e = e || window.event; + // // calculate the new cursor position: + // pos1 = pos3 - e.clientX; + // pos2 = pos4 - e.clientY; + // pos3 = e.clientX; + // pos4 = e.clientY; + // // set the element's new position: + // elmnt.style.top = elmnt.offsetTop - 60 * pos2 + "px"; + // elmnt.style.left = elmnt.offsetLeft - 60 * pos1 + "px"; + // } + + // function closeDragElement(e) { + // /* stop moving when mouse button is released:*/ + // document.onmouseup = null; + // document.onmousemove = null; + // document.onpointerup = null; + // } + + // function getHeader(element) { + // var headerItems = element.getElementsByClassName("move-header"); + + // if (headerItems.length === 1) { + // return headerItems[0]; + // } + + // return null; + // } + // } + + // // This function would be used to add the resizing functionality to the text box. This would need to be executed for every text + // this.initResizeElement = function () { + + // var divCont = document.getElementsByClassName("insideforeign")[0]; + // // console.log(divCont.getElementsByTagName("textarea").style.height); + // // var height = textheight.style.split(";")[0]; + // // height = height.slice(8, height.length - 2); + // var element = null; + // var startX, startY, startWidth, startHeight; + + // // Creating the four corners for resizing + + // // topleft = document.createElement("div"); + // // topleft.className = "resizer-topleft"; + // // divCont.appendChild(topleft); + // // topleft.addEventListener("pointerdown", initDrag, false); + // // topleft.parentPopup = divCont; - bottomleft = document.createElement("div"); - bottomleft.className = "resizer-bottomleft"; - divCont.appendChild(bottomleft); - bottomleft.addEventListener("pointerdown", initDrag, false); - bottomleft.parentPopup = divCont; + // // bottomleft = document.createElement("div"); + // // bottomleft.className = "resizer-bottomleft"; + // // divCont.appendChild(bottomleft); + // // bottomleft.addEventListener("pointerdown", initDrag, false); + // // bottomleft.parentPopup = divCont; - topright = document.createElement("div"); - topright.className = "resizer-topright"; - divCont.appendChild(topright); - topright.addEventListener("pointerdown", initDrag, false); - topright.parentPopup = divCont; + // // topright = document.createElement("div"); + // // topright.className = "resizer-topright"; + // // divCont.appendChild(topright); + // // topright.addEventListener("pointerdown", initDrag, false); + // // topright.parentPopup = divCont; - bottomright = document.createElement("div"); - bottomright.className = "resizer-bottomright"; - divCont.appendChild(bottomright); - bottomright.addEventListener("pointerdown", initDrag, false); - bottomright.parentPopup = divCont; - - // var right = document.createElement("div"); - // right.className = "resizer-right"; - // divCont.appendChild(right); - // right.addEventListener("mousedown", function (e) { - // console.log("Clicked on div"); - // }); - // right.parentPopup = divCont; - - // var bottom = document.createElement("div", true); - // bottom.className = "resizer-bottom"; - // divCont.appendChild(bottom); - // bottom.addEventListener("pointerdown", function (e) { - // e.stopImmediatePropagation(); - // }); - - // bottom.parentPopup = divCont; - // bottom.setAttribute("tabindex", "-1"); - // both = document.createElement("div"); - // both.className = "resizer-both"; - // divCont.appendChild(both); - // bottom.addEventListener("mousedown", initDrag); - // both.addEventListener("click", function (e) { - // console.log(e); - // console.log("Clicked on div"); - // }); - // both.parentPopup = divCont; - - function initDrag(e) { - e.stopImmediatePropagation(); - console.log("Inside"); - element = this.parentPopup; - resizer = e.target.className; - - startX = e.clientX; - startY = e.clientY; - startWidth = parseInt( - document.defaultView.getComputedStyle(element).width, - 10 - ); - startHeight = parseInt( - document.defaultView.getComputedStyle(element).height, - 10 - ); - - document.documentElement.addEventListener("mousemove", doDrag, false); - document.documentElement.addEventListener("pointerup", stopDrag, false); - document.documentElement.addEventListener("mouseup", stopDrag, false); - } - - function doDrag(e) { - console.log("doing drag"); - element.style.position = "absolute"; - switch(resizer) { - case "resizer-bottomleft": - element.style.width = startWidth + 12 * (startX - e.clientX)+ "px"; - element.style.left = startX + 12 * (e.clientX - startX) + "px"; - // element.style.top = startY + (e.clientY - startY) + "px"; - element.style.height = startHeight + 12 * (e.clientY - startY) + "px"; - break; - case "resizer-topleft": - element.style.width = startWidth + 12 * (startX - e.clientX)+ "px"; - element.style.left = startX + 12 * (e.clientX - startX) + "px"; - element.style.top = startY + 12 * (e.clientY - startY) + "px"; - element.style.height = startHeight + 12 * (startY - e.clientY) + "px"; - break; - case "resizer-topright": - element.style.width = startWidth + 12 * (e.clientX - startX) + "px"; - // element.style.left = startX + (e.clientX - startX) + "px"; - element.style.top = startY + 12 * (e.clientY - startY) + "px"; - element.style.height = startHeight + 12 * (startY - e.clientY) + "px"; - break; - case "resizer-bottomright": - element.style.width = startWidth + 12 * (e.clientX - startX) + "px"; - // element.style.left = startX + (e.clientX - startX) + "px"; - // element.style.top = startY + (e.clientY - startY) + "px"; - element.style.height = startHeight + 12 * (e.clientY - startY) + "px"; - break; - default: - break; - } - // element.style.width = startWidth + 12 * (e.clientX - startX) + "px"; - // element.style.height = startHeight + 12 * (e.clientY - startY) + "px"; - } - - function stopDrag() { - console.log("Stopping drag"); - document.documentElement.removeEventListener("mousemove", doDrag, false); - document.documentElement.removeEventListener("pointerup", stopDrag, false); - document.documentElement.removeEventListener("mouseup", stopDrag, false); - } - } + // bottomright = document.createElement("div"); + // bottomright.className = "resizer-bottomright"; + // divCont.appendChild(bottomright); + // bottomright.addEventListener("pointerdown", initDrag, false); + // bottomright.parentPopup = divCont; + + // function initDrag(e) { + // e.stopImmediatePropagation(); + // element = this.parentPopup; + // resizer = e.target.className; + + // startX = e.clientX; + // startY = e.clientY; + // startWidth = parseInt( + // document.defaultView.getComputedStyle(element).width, + // 10 + // ); + // startHeight = parseInt( + // document.defaultView.getComputedStyle(element).height, + // 10 + // ); + + // document.documentElement.addEventListener("mousemove", doDrag, false); + // document.documentElement.addEventListener("pointerup", stopDrag, false); + // document.documentElement.addEventListener("mouseup", stopDrag, false); + // } + + // function doDrag(e) { + // element.style.position = "absolute"; + // switch(resizer) { + // case "resizer-bottomleft": + // element.style.width = startWidth + 60 * (e.clientX - startX)+ "px"; + // element.style.left = startX + 60 * (startX - e.clientX) + "px"; + // element.style.height = startHeight + 60 * (e.clientY - startY) + "px"; + // console.log(element.style.left); + // break; + // case "resizer-topleft": + // element.style.width = startWidth + 60 * (startX - e.clientX)+ "px"; + // element.style.left = startX + 60 * (e.clientX - startX) + "px"; + // element.style.top = startY + 60 * (e.clientY - startY) + "px"; + // element.style.height = startHeight + 60 * (startY - e.clientY) + "px"; + // break; + // case "resizer-topright": + // element.style.width = startWidth + 60 * (e.clientX - startX) + "px"; + // element.style.top = startY + 60 * (e.clientY - startY) + "px"; + // element.style.height = startHeight + 60 * (startY - e.clientY) + "px"; + // break; + // case "resizer-bottomright": + // element.style.width = startWidth + 60 * (e.clientX - startX) + "px"; + // // element.style.height = (element.scrollHeight) + "px"; + // // element.textHeight = element.scrollHeight; + // var textheight = divCont.getElementsByTagName("textarea")[0].offsetHeight; + // console.log(textheight); + // h = startHeight + 60 * (e.clientY - startY) + "px"; + // if(h > textheight){ + // element.style.height = h; + // } + // else{ + // element.style.height = textheight; + // } + // break; + // default: + // break; + // } + // // element.style.width = startWidth + 60 * (e.clientX - startX) + "px"; + // // element.style.height = startHeight + 60 * (e.clientY - startY) + "px"; + // } + + // function stopDrag() { + // console.log("Stopping drag"); + // document.documentElement.removeEventListener("mousemove", doDrag, false); + // document.documentElement.removeEventListener("pointerup", stopDrag, false); + // document.documentElement.removeEventListener("mouseup", stopDrag, false); + // } + // } /** * This function creates the marker definition needed for the arrowline annotation tool. diff --git a/js/viewer/annotation/annotation-svg.js b/js/viewer/annotation/annotation-svg.js index d713fa95..40d6da50 100644 --- a/js/viewer/annotation/annotation-svg.js +++ b/js/viewer/annotation/annotation-svg.js @@ -68,7 +68,7 @@ function AnnotationSVG(parent, id, imgWidth, imgHeight, scale, ignoreReferencePo subtype: subtype, attrs : attrs }) - this.dispatchEvent("removeHandlers", null); + // this.dispatchEvent("removeHandlers", null); } } @@ -589,10 +589,14 @@ function AnnotationSVG(parent, id, imgWidth, imgHeight, scale, ignoreReferencePo } // Remove annotation from a group - this.removeAnnotationByGraphID = function(groupID, graphID){ + this.removeAnnotationByGraphID = function(groupID, graphID, userData){ + + if(userData.type == "TEXT"){ + userData.annotation.transform(); + return; + } if(this.groups.hasOwnProperty(groupID)){ var group = this.groups[groupID]; - group.removeAnnotationByID(graphID); } } diff --git a/js/viewer/annotation/base.js b/js/viewer/annotation/base.js index ec94290b..0b0e994c 100644 --- a/js/viewer/annotation/base.js +++ b/js/viewer/annotation/base.js @@ -215,6 +215,10 @@ Base.prototype.renderSVG = function(annotationType){ // add all the attributes for(attr in this._attrs){ + if(attr === "text"){ + this.svg.text(this._attrs[attr]); + continue + } value = this._attrs[attr]; if (attr === "stroke-width") { // TODO what if the value is not in pixel? (it can be pixel) @@ -266,27 +270,4 @@ Base.prototype.unbind = function(){ .on("mouseout", null) .on('click', null); } -} - - - -Base.prototype.editText = function (localpoint) { - - var myforeign = document.createElementNS('http://www.w3.org/2000/svg', 'foreignObject') - var textdiv = document.createElement("div"); - var input = document.createElement("textarea"); - input.name = "post"; - input.maxLength = "5000"; - input.cols = "80"; - input.rows = "40"; - textdiv.appendChild(input); - textdiv.setAttribute("contentEditable", "true"); - textdiv.setAttribute("width", "auto"); - myforeign.setAttribute("width", "100%"); - myforeign.setAttribute("height", "100%"); - myforeign.classList.add("foreign"); //to make div fit text - textdiv.classList.add("insideforeign"); //to make div fit text - myforeign.setAttributeNS(null, "transform", "translate(" + localpoint.x + " " + localpoint.y + ")"); - myforeign.appendChild(textdiv); - this.svg.appendChild(myforeign); } \ No newline at end of file diff --git a/js/viewer/annotation/text.js b/js/viewer/annotation/text.js index d79926eb..e9dc9edd 100644 --- a/js/viewer/annotation/text.js +++ b/js/viewer/annotation/text.js @@ -4,12 +4,21 @@ function Text(attrs) { Base.call(this, attrs); this._tag = "text"; - this._attrs["x"] = attrs["x"] || null; - this._attrs["y"] = attrs["y"] || null; + this._attrs["x"] = attrs["x"] || 18600; + this._attrs["y"] = attrs["y"] || 19650; this._attrs["fill"] = attrs["fill"] || "red"; - this._attrs["font-size"] = attrs["font-size"] || 2000; - this._attrs["font-weight"] = attrs["font-weight"] || 700; + this._attrs["font-size"] = attrs["font-size"] || 1000; + this._attrs["font-weight"] = attrs["font-weight"] || 400; this.svg = null; + this.foreignObj = ""; + + this.setForeignObj = function(obj) { + this.foreignObj = obj; + } + + this.getForeignObj = function() { + return this.foreignObj; + } } Text.prototype = Object.create(Base.prototype); @@ -31,23 +40,255 @@ Text.prototype.insertPoint = function (point) { _self.renderSVG(); }; -// Text.prototype.editText = function (point) { - -// var myforeign = document.createElementNS('http://www.w3.org/2000/svg', 'foreignObject') -// var textdiv = document.createElement("div"); -// var input = document.createElement("textarea"); -// input.name = "post"; -// input.maxLength = "5000"; -// input.cols = "80"; -// input.rows = "40"; -// div.appendChild(input); -// textdiv.setAttribute("contentEditable", "true"); -// textdiv.setAttribute("width", "auto"); -// myforeign.setAttribute("width", "100%"); -// myforeign.setAttribute("height", "100%"); -// myforeign.classList.add("foreign"); //to make div fit text -// textdiv.classList.add("insideforeign"); //to make div fit text -// myforeign.setAttributeNS(null, "transform", "translate(" + localpoint.x + " " + localpoint.y + ")"); -// myforeign.appendChild(textdiv); -// this.parent.svg.appendChild(myforeign); -// } \ No newline at end of file +// +Text.prototype.transform = function () { + + this._attrs["id"] = "asdkfjaslkdfj"; + this._attrs["text"] = textInput.value; + console.log(textInput.value); + obj = this.getForeignObj(); + var rect = obj.getBoundingClientRect(); + console.log(rect); + // this._attrs["x"] = window.scrollX + rect.left; + // this._attrs["y"] = window.scrollY + rect.top; + obj.parentNode.removeChild(obj); + // console.log(textOuterDiv); + // svgForeignObj.parentNode.removeChild(svgForeignObj); + this.renderSVG(); + // document.getElementById("asdkfjaslkdfj").textContent = "AKSDFASKDJF"; + // document.getElementById("asdkfjaslkdfj").style["font-size"] = 2000; +} + +/** + * This function will add a textbox div to the SVG. This will be used to edit the text and would be later moved to the text.js + * once the feature to switch to and fro from text is implemented. + */ + Text.prototype.addTextBox = function (groupId) { + + var svgForeignObj = document.createElementNS('http://www.w3.org/2000/svg', 'foreignObject') + var textOuterDiv = document.createElementNS("http://www.w3.org/1999/xhtml", "div"); + var textInput = document.createElementNS("http://www.w3.org/1999/xhtml", "textarea"); + var prev = null; + // textInput.style.color = "#0000ff"; + this.setForeignObj(svgForeignObj); + + // This would later need a unique ID for each text + textInput.setAttribute("id", "textInput"); + + textInput.oninput = function(e) { + e.stopImmediatePropagation(); + }; + textInput.onpointerdown = function(evt) { + evt.stopImmediatePropagation(); + }; + textInput.keydown = function(evt) { + evt.stopImmediatePropagation(); + }; + textInput.keyup = function(evt) { + evt.stopImmediatePropagation(); + }; + + textOuterDiv.setAttribute("contentEditable", "true"); + textInput.setAttribute("contentEditable", "true"); + svgForeignObj.setAttribute("contentEditable", "true"); + textOuterDiv.setAttribute("width", "auto"); + textOuterDiv.setAttribute("tabindex", "-1"); + svgForeignObj.setAttribute("width", "100%"); + svgForeignObj.setAttribute("height", "1000px"); + textOuterDiv.appendChild(textInput); + svgForeignObj.classList.add("foreign"); //to make div fit text + textOuterDiv.classList.add("insideforeign"); //to make div fit text + + // This would need to take the value from the click trackers of OSD to decide the position of the text. + svgForeignObj.setAttributeNS(null, "transform", "translate(18000 18000)"); + svgForeignObj.appendChild(textOuterDiv); + + group = document.getElementById(groupId); + group.appendChild(svgForeignObj); + group.setAttribute("contentEditable", "true"); + + textInput.addEventListener("keydown", function (e) { + e.stopImmediatePropagation(); + prev = this.textHeight; + this.style.height = (this.scrollHeight) + "px"; + this.textHeight = this.scrollHeight; + }); + + this.initResizeElement(); + this.initDragElement(); + + textInput.setAttribute("style", "height:" + this.textHeight + "px;overflow-y:hidden;color:blue;"); + textInput.style.fontSize = "1000px"; + textInput.setAttribute("wrap", "hard"); + } + + Text.prototype.initDragElement = function () { + + pos1 = 0, pos2 = 0, pos3 = 0, pos4 = 0; + + var divCont = document.getElementsByClassName("insideforeign")[0]; + var elmnt = null; + var currentZIndex = 100; + divCont.onmousedown = function() { + this.style.zIndex = "" + ++currentZIndex; + }; + + divCont.onmousedown = dragMouseDown; + divCont.onpointerdown = dragMouseDown; + + function dragMouseDown(e) { + e.stopImmediatePropagation(); + console.log(e); + elmnt = e.target; + elmnt.style.zIndex = "" + ++currentZIndex; + + e = e || window.event; + // get the mouse cursor position at startup: + pos3 = e.clientX; + pos4 = e.clientY; + divCont.onmouseup = closeDragElement; + divCont.onpointerup = closeDragElement; + divCont.onmouseleave = closeDragElement; + // call a function whenever the cursor moves: + document.onmousemove = elementDrag; + } + + function elementDrag(e) { + if (!elmnt) { + return; + } + + e = e || window.event; + // calculate the new cursor position: + pos1 = pos3 - e.clientX; + pos2 = pos4 - e.clientY; + pos3 = e.clientX; + pos4 = e.clientY; + // set the element's new position: + elmnt.style.top = elmnt.offsetTop - 60 * pos2 + "px"; + elmnt.style.left = elmnt.offsetLeft - 60 * pos1 + "px"; + } + + function closeDragElement(e) { + /* stop moving when mouse button is released:*/ + document.onmouseup = null; + document.onmousemove = null; + document.onpointerup = null; + } + + function getHeader(element) { + var headerItems = element.getElementsByClassName("move-header"); + + if (headerItems.length === 1) { + return headerItems[0]; + } + + return null; + } + } + + // This function would be used to add the resizing functionality to the text box. This would need to be executed for every text + Text.prototype.initResizeElement = function () { + + var divCont = document.getElementsByClassName("insideforeign")[0]; + // console.log(divCont.getElementsByTagName("textarea").style.height); + // var height = textheight.style.split(";")[0]; + // height = height.slice(8, height.length - 2); + var element = null; + var startX, startY, startWidth, startHeight; + + // Creating the four corners for resizing + + // topleft = document.createElement("div"); + // topleft.className = "resizer-topleft"; + // divCont.appendChild(topleft); + // topleft.addEventListener("pointerdown", initDrag, false); + // topleft.parentPopup = divCont; + + // bottomleft = document.createElement("div"); + // bottomleft.className = "resizer-bottomleft"; + // divCont.appendChild(bottomleft); + // bottomleft.addEventListener("pointerdown", initDrag, false); + // bottomleft.parentPopup = divCont; + + // topright = document.createElement("div"); + // topright.className = "resizer-topright"; + // divCont.appendChild(topright); + // topright.addEventListener("pointerdown", initDrag, false); + // topright.parentPopup = divCont; + + bottomright = document.createElement("div"); + bottomright.className = "resizer-bottomright"; + divCont.appendChild(bottomright); + bottomright.addEventListener("pointerdown", initDrag, false); + bottomright.parentPopup = divCont; + + function initDrag(e) { + e.stopImmediatePropagation(); + element = this.parentPopup; + resizer = e.target.className; + + startX = e.clientX; + startY = e.clientY; + startWidth = parseInt( + document.defaultView.getComputedStyle(element).width, + 10 + ); + startHeight = parseInt( + document.defaultView.getComputedStyle(element).height, + 10 + ); + + document.documentElement.addEventListener("mousemove", doDrag, false); + document.documentElement.addEventListener("pointerup", stopDrag, false); + document.documentElement.addEventListener("mouseup", stopDrag, false); + } + + function doDrag(e) { + element.style.position = "absolute"; + switch(resizer) { + case "resizer-bottomleft": + element.style.width = startWidth + 60 * (e.clientX - startX)+ "px"; + element.style.left = startX + 60 * (startX - e.clientX) + "px"; + element.style.height = startHeight + 60 * (e.clientY - startY) + "px"; + console.log(element.style.left); + break; + case "resizer-topleft": + element.style.width = startWidth + 60 * (startX - e.clientX)+ "px"; + element.style.left = startX + 60 * (e.clientX - startX) + "px"; + element.style.top = startY + 60 * (e.clientY - startY) + "px"; + element.style.height = startHeight + 60 * (startY - e.clientY) + "px"; + break; + case "resizer-topright": + element.style.width = startWidth + 60 * (e.clientX - startX) + "px"; + element.style.top = startY + 60 * (e.clientY - startY) + "px"; + element.style.height = startHeight + 60 * (startY - e.clientY) + "px"; + break; + case "resizer-bottomright": + element.style.width = startWidth + 60 * (e.clientX - startX) + "px"; + // element.style.height = (element.scrollHeight) + "px"; + // element.textHeight = element.scrollHeight; + var textheight = divCont.getElementsByTagName("textarea")[0].offsetHeight; + console.log(textheight); + h = startHeight + 60 * (e.clientY - startY) + "px"; + if(h > textheight){ + element.style.height = h; + } + else{ + element.style.height = textheight; + } + break; + default: + break; + } + // element.style.width = startWidth + 60 * (e.clientX - startX) + "px"; + // element.style.height = startHeight + 60 * (e.clientY - startY) + "px"; + } + + function stopDrag() { + console.log("Stopping drag"); + document.documentElement.removeEventListener("mousemove", doDrag, false); + document.documentElement.removeEventListener("pointerup", stopDrag, false); + document.documentElement.removeEventListener("mouseup", stopDrag, false); + } + } \ No newline at end of file diff --git a/js/viewer/viewer.js b/js/viewer/viewer.js index 997b012f..b8060dda 100644 --- a/js/viewer/viewer.js +++ b/js/viewer/viewer.js @@ -428,10 +428,12 @@ function Viewer(parent, config) { // alert("view start drawing") var tracker = new OpenSeadragon.MouseTracker({ element: _self.svg, + // clickHandler: this.onMouseDragToDraw, dragHandler: this.onMouseDragToDraw, dragEndHandler: this.onMouseDragToDrawEnd, userData: data }); + this.osd.addHandler('canvas-click', this.onMouseDragToDraw); _self.mouseTrackers.push(tracker); break; case "updateSVGId": @@ -474,9 +476,9 @@ function Viewer(parent, config) { }); } break; - case "removeHandlers": - this.removeHandler(); - break; + // case "removeHandlers": + // this.removeHandler(); + // break; default: this.parent.dispatchEvent(type, data); break; @@ -531,7 +533,7 @@ function Viewer(parent, config) { break; // Remove annotation object from a group case "removeAnnotationByGraphID": - svg.removeAnnotationByGraphID(data.groupID, data.graphID); + svg.removeAnnotationByGraphID(data.groupID, data.graphID, data.userData); this.dispatchEvent("onMouseoutHideTooltip"); break; // Set annotation groups attributes @@ -1040,6 +1042,7 @@ function Viewer(parent, config) { // Drag to draw event handler start this.onMouseDragToDraw = function(event){ + console.log(event.position); var annotation = event.userData.annotation; var viewBox = event.userData.viewBox; var scaleX = event.userData.imgScaleX || 1; @@ -1182,7 +1185,8 @@ function Viewer(parent, config) { this.dispatchSVGEvent("removeAnnotationByGraphID", { svgID : userData.svgID, groupID : userData.groupID, - graphID : userData.graphID + graphID : userData.graphID, + userData: userData }) } diff --git a/vendor/openseadragon.js b/vendor/openseadragon.js index bc866e62..8cd9cf63 100644 --- a/vendor/openseadragon.js +++ b/vendor/openseadragon.js @@ -5058,7 +5058,6 @@ $.EventSource.prototype = { */ function onMouseDown( tracker, event ) { var gPoint; - console.log("laskdjfasdlk"); event = $.getEvent( event ); gPoint = { From 26e29045153882958e3a09852b9e6b0da84eca92 Mon Sep 17 00:00:00 2001 From: Nikhil Pawar Date: Mon, 24 Oct 2022 12:34:39 -0700 Subject: [PATCH 05/32] Export and Import for text annotation Added the export functionality for the foreign object. Also, removed the transition from textarea to SVG text. --- js/viewer/annotation/annotation-group.js | 246 +---------------------- js/viewer/annotation/annotation-svg.js | 11 +- js/viewer/annotation/base.js | 5 + js/viewer/annotation/text.js | 37 ++-- 4 files changed, 35 insertions(+), 264 deletions(-) diff --git a/js/viewer/annotation/annotation-group.js b/js/viewer/annotation/annotation-group.js index e5beaf33..5d093ca0 100644 --- a/js/viewer/annotation/annotation-group.js +++ b/js/viewer/annotation/annotation-group.js @@ -34,6 +34,7 @@ function AnnotationGroup(id, anatomy, description, parent){ "graph-id" : graphID, "parent" : this } + console.log(type); switch (type.toUpperCase()) { case "PATH": annotation = new Path(attrs); @@ -87,249 +88,6 @@ function AnnotationGroup(id, anatomy, description, parent){ return annotation; } - // /** - // * This function will add a textbox div to the SVG. This will be used to edit the text and would be later moved to the text.js - // * once the feature to switch to and fro from text is implemented. - // */ - // this.addTextBox = function () { - - // var svgForeignObj = document.createElementNS('http://www.w3.org/2000/svg', 'foreignObject') - // var textOuterDiv = document.createElementNS("http://www.w3.org/1999/xhtml", "div"); - // var textInput = document.createElementNS("http://www.w3.org/1999/xhtml", "textarea"); - - // // This would later need a unique ID for each text - // textInput.setAttribute("id", "textInput"); - - // textInput.oninput = function(e) { - // e.stopImmediatePropagation(); - // }; - // textInput.onpointerdown = function(evt) { - // evt.stopImmediatePropagation(); - // }; - // textInput.keydown = function(evt) { - // evt.stopImmediatePropagation(); - // }; - // textInput.keyup = function(evt) { - // evt.stopImmediatePropagation(); - // }; - - // textOuterDiv.setAttribute("contentEditable", "true"); - // textInput.setAttribute("contentEditable", "true"); - // svgForeignObj.setAttribute("contentEditable", "true"); - // textOuterDiv.setAttribute("width", "auto"); - // textOuterDiv.setAttribute("tabindex", "-1"); - // svgForeignObj.setAttribute("width", "100%"); - // svgForeignObj.setAttribute("height", "100%"); - // textOuterDiv.appendChild(textInput); - // svgForeignObj.classList.add("foreign"); //to make div fit text - // textOuterDiv.classList.add("insideforeign"); //to make div fit text - - // // This would need to take the value from the click trackers of OSD to decide the position of the text. - // svgForeignObj.setAttributeNS(null, "transform", "translate(18000 18000)"); - // svgForeignObj.appendChild(textOuterDiv); - - // group = document.getElementById(this.id); - // group.appendChild(svgForeignObj); - // group.setAttribute("contentEditable", "true"); - // textOuterDiv.addEventListener("mousedown", this.clickedInput); - - // document.getElementById("textInput").addEventListener("keydown", function (e) { - // e.stopImmediatePropagation(); - // console.log("laskdjfl"); - // this.style.height = (this.scrollHeight) + "px"; - // this.textHeight = this.scrollHeight; - // }); - - // this.initResizeElement(); - // this.initDragElement(); - - // textInput.setAttribute("style", "height:" + this.textHeight + "px;overflow-y:hidden;"); - // textInput.style.fontSize = "1000px"; - // textInput.addEventListener("input", OnInput, false); - // } - - // this.clickedInput = function (e) { - // e.stopImmediatePropagation(); - // console.log(e); - // console.log("clicked textInput"); - // } - - // function OnInput() { - // console.log("Input"); - // this.style.height = "auto"; - // this.style.height = (this.scrollHeight) + "px"; - // } - - // this.initDragElement = function () { - - // pos1 = 0, pos2 = 0, pos3 = 0, pos4 = 0; - - // var divCont = document.getElementsByClassName("insideforeign")[0]; - // var elmnt = null; - // var currentZIndex = 100; - // divCont.onmousedown = function() { - // this.style.zIndex = "" + ++currentZIndex; - // }; - - // divCont.onmousedown = dragMouseDown; - // divCont.onpointerdown = dragMouseDown; - - // function dragMouseDown(e) { - // e.stopImmediatePropagation(); - // console.log(e); - // elmnt = e.target; - // elmnt.style.zIndex = "" + ++currentZIndex; - - // e = e || window.event; - // // get the mouse cursor position at startup: - // pos3 = e.clientX; - // pos4 = e.clientY; - // divCont.onmouseup = closeDragElement; - // divCont.onpointerup = closeDragElement; - // divCont.onmouseleave = closeDragElement; - // // call a function whenever the cursor moves: - // document.onmousemove = elementDrag; - // } - - // function elementDrag(e) { - // if (!elmnt) { - // return; - // } - - // e = e || window.event; - // // calculate the new cursor position: - // pos1 = pos3 - e.clientX; - // pos2 = pos4 - e.clientY; - // pos3 = e.clientX; - // pos4 = e.clientY; - // // set the element's new position: - // elmnt.style.top = elmnt.offsetTop - 60 * pos2 + "px"; - // elmnt.style.left = elmnt.offsetLeft - 60 * pos1 + "px"; - // } - - // function closeDragElement(e) { - // /* stop moving when mouse button is released:*/ - // document.onmouseup = null; - // document.onmousemove = null; - // document.onpointerup = null; - // } - - // function getHeader(element) { - // var headerItems = element.getElementsByClassName("move-header"); - - // if (headerItems.length === 1) { - // return headerItems[0]; - // } - - // return null; - // } - // } - - // // This function would be used to add the resizing functionality to the text box. This would need to be executed for every text - // this.initResizeElement = function () { - - // var divCont = document.getElementsByClassName("insideforeign")[0]; - // // console.log(divCont.getElementsByTagName("textarea").style.height); - // // var height = textheight.style.split(";")[0]; - // // height = height.slice(8, height.length - 2); - // var element = null; - // var startX, startY, startWidth, startHeight; - - // // Creating the four corners for resizing - - // // topleft = document.createElement("div"); - // // topleft.className = "resizer-topleft"; - // // divCont.appendChild(topleft); - // // topleft.addEventListener("pointerdown", initDrag, false); - // // topleft.parentPopup = divCont; - - // // bottomleft = document.createElement("div"); - // // bottomleft.className = "resizer-bottomleft"; - // // divCont.appendChild(bottomleft); - // // bottomleft.addEventListener("pointerdown", initDrag, false); - // // bottomleft.parentPopup = divCont; - - // // topright = document.createElement("div"); - // // topright.className = "resizer-topright"; - // // divCont.appendChild(topright); - // // topright.addEventListener("pointerdown", initDrag, false); - // // topright.parentPopup = divCont; - - // bottomright = document.createElement("div"); - // bottomright.className = "resizer-bottomright"; - // divCont.appendChild(bottomright); - // bottomright.addEventListener("pointerdown", initDrag, false); - // bottomright.parentPopup = divCont; - - // function initDrag(e) { - // e.stopImmediatePropagation(); - // element = this.parentPopup; - // resizer = e.target.className; - - // startX = e.clientX; - // startY = e.clientY; - // startWidth = parseInt( - // document.defaultView.getComputedStyle(element).width, - // 10 - // ); - // startHeight = parseInt( - // document.defaultView.getComputedStyle(element).height, - // 10 - // ); - - // document.documentElement.addEventListener("mousemove", doDrag, false); - // document.documentElement.addEventListener("pointerup", stopDrag, false); - // document.documentElement.addEventListener("mouseup", stopDrag, false); - // } - - // function doDrag(e) { - // element.style.position = "absolute"; - // switch(resizer) { - // case "resizer-bottomleft": - // element.style.width = startWidth + 60 * (e.clientX - startX)+ "px"; - // element.style.left = startX + 60 * (startX - e.clientX) + "px"; - // element.style.height = startHeight + 60 * (e.clientY - startY) + "px"; - // console.log(element.style.left); - // break; - // case "resizer-topleft": - // element.style.width = startWidth + 60 * (startX - e.clientX)+ "px"; - // element.style.left = startX + 60 * (e.clientX - startX) + "px"; - // element.style.top = startY + 60 * (e.clientY - startY) + "px"; - // element.style.height = startHeight + 60 * (startY - e.clientY) + "px"; - // break; - // case "resizer-topright": - // element.style.width = startWidth + 60 * (e.clientX - startX) + "px"; - // element.style.top = startY + 60 * (e.clientY - startY) + "px"; - // element.style.height = startHeight + 60 * (startY - e.clientY) + "px"; - // break; - // case "resizer-bottomright": - // element.style.width = startWidth + 60 * (e.clientX - startX) + "px"; - // // element.style.height = (element.scrollHeight) + "px"; - // // element.textHeight = element.scrollHeight; - // var textheight = divCont.getElementsByTagName("textarea")[0].offsetHeight; - // console.log(textheight); - // h = startHeight + 60 * (e.clientY - startY) + "px"; - // if(h > textheight){ - // element.style.height = h; - // } - // else{ - // element.style.height = textheight; - // } - // break; - // default: - // break; - // } - // // element.style.width = startWidth + 60 * (e.clientX - startX) + "px"; - // // element.style.height = startHeight + 60 * (e.clientY - startY) + "px"; - // } - - // function stopDrag() { - // console.log("Stopping drag"); - // document.documentElement.removeEventListener("mousemove", doDrag, false); - // document.documentElement.removeEventListener("pointerup", stopDrag, false); - // document.documentElement.removeEventListener("mouseup", stopDrag, false); - // } - // } /** * This function creates the marker definition needed for the arrowline annotation tool. @@ -485,6 +243,8 @@ function AnnotationGroup(id, anatomy, description, parent){ if (rst.length === 0) { return ""; } + // console.log("" + rst.join("") + ""); + return "" + rst.join("") + ""; } diff --git a/js/viewer/annotation/annotation-svg.js b/js/viewer/annotation/annotation-svg.js index 40d6da50..d07a4e34 100644 --- a/js/viewer/annotation/annotation-svg.js +++ b/js/viewer/annotation/annotation-svg.js @@ -348,6 +348,7 @@ function AnnotationSVG(parent, id, imgWidth, imgHeight, scale, ignoreReferencePo // if (svgElems[i].getAttribute("id") == null) { continue; } var node = svgElems[i]; + console.log(node); // var groupID = svgElems[i].getAttribute("id") || Date.parse(new Date()) + parseInt(Math.random() * 1000); var className = node.getAttribute("class") || ""; var attrs = styleSheet[className] ? JSON.parse(JSON.stringify(styleSheet[className])) : {}; @@ -364,6 +365,7 @@ function AnnotationSVG(parent, id, imgWidth, imgHeight, scale, ignoreReferencePo case "rect": case "line": case "arrowline": + case "foreignObject": this.parseSVGNodes([node], styleSheet, node); break; // Added the defs case to handle the definition of markers. We just skip the definition as @@ -512,6 +514,7 @@ function AnnotationSVG(parent, id, imgWidth, imgHeight, scale, ignoreReferencePo case "polyline": case "polygon": case "rect": + case "foreignObject": if(id !== "undefined"){ group = this.groups.hasOwnProperty(id) ? this.groups[id] : this.createAnnotationGroup(id, anatomy); annotation = group.addAnnotation(node.nodeName); @@ -591,10 +594,10 @@ function AnnotationSVG(parent, id, imgWidth, imgHeight, scale, ignoreReferencePo // Remove annotation from a group this.removeAnnotationByGraphID = function(groupID, graphID, userData){ - if(userData.type == "TEXT"){ - userData.annotation.transform(); - return; - } + // if(userData.type == "TEXT"){ + // userData.annotation.transform(); + // return; + // } if(this.groups.hasOwnProperty(groupID)){ var group = this.groups[groupID]; group.removeAnnotationByID(graphID); diff --git a/js/viewer/annotation/base.js b/js/viewer/annotation/base.js index 0b0e994c..f706184a 100644 --- a/js/viewer/annotation/base.js +++ b/js/viewer/annotation/base.js @@ -62,10 +62,15 @@ function Base(attrs){ this.exportToSVG = function(){ + if(this._tag == "text"){ + return (this.getForeignObj().outerHTML); + } + // Check to see if there are necessary dimensions needed to construct the component. This makes sure that no empty components are added to the final SVG output file. if (!this.hasDimensions(this._attrs, this._tag)) { return ""; } + var tag = this._tag; var rst = "<" + tag + " "; var attr; diff --git a/js/viewer/annotation/text.js b/js/viewer/annotation/text.js index e9dc9edd..74ecd177 100644 --- a/js/viewer/annotation/text.js +++ b/js/viewer/annotation/text.js @@ -45,10 +45,7 @@ Text.prototype.transform = function () { this._attrs["id"] = "asdkfjaslkdfj"; this._attrs["text"] = textInput.value; - console.log(textInput.value); obj = this.getForeignObj(); - var rect = obj.getBoundingClientRect(); - console.log(rect); // this._attrs["x"] = window.scrollX + rect.left; // this._attrs["y"] = window.scrollY + rect.top; obj.parentNode.removeChild(obj); @@ -63,7 +60,7 @@ Text.prototype.transform = function () { * This function will add a textbox div to the SVG. This will be used to edit the text and would be later moved to the text.js * once the feature to switch to and fro from text is implemented. */ - Text.prototype.addTextBox = function (groupId) { + Text.prototype.addTextBox = function (groupId, textValue) { var svgForeignObj = document.createElementNS('http://www.w3.org/2000/svg', 'foreignObject') var textOuterDiv = document.createElementNS("http://www.w3.org/1999/xhtml", "div"); @@ -75,18 +72,18 @@ Text.prototype.transform = function () { // This would later need a unique ID for each text textInput.setAttribute("id", "textInput"); - textInput.oninput = function(e) { - e.stopImmediatePropagation(); - }; - textInput.onpointerdown = function(evt) { - evt.stopImmediatePropagation(); - }; - textInput.keydown = function(evt) { - evt.stopImmediatePropagation(); - }; - textInput.keyup = function(evt) { - evt.stopImmediatePropagation(); - }; + // textInput.oninput = function(e) { + // e.stopImmediatePropagation(); + // }; + // textInput.onpointerdown = function(evt) { + // evt.stopImmediatePropagation(); + // }; + // textInput.keydown = function(evt) { + // evt.stopImmediatePropagation(); + // }; + // textInput.keyup = function(evt) { + // evt.stopImmediatePropagation(); + // }; textOuterDiv.setAttribute("contentEditable", "true"); textInput.setAttribute("contentEditable", "true"); @@ -107,11 +104,17 @@ Text.prototype.transform = function () { group.appendChild(svgForeignObj); group.setAttribute("contentEditable", "true"); + if(textValue != null){ + textInput.value = textValue; + textInput.innerHTML = textValue; + } + textInput.addEventListener("keydown", function (e) { - e.stopImmediatePropagation(); + // e.stopImmediatePropagation(); prev = this.textHeight; this.style.height = (this.scrollHeight) + "px"; this.textHeight = this.scrollHeight; + textInput.innerHTML = textInput.value; }); this.initResizeElement(); From 58d078352651e0bb06e8f4e39fb1cb65f836de75 Mon Sep 17 00:00:00 2001 From: Nikhil Pawar Date: Thu, 10 Nov 2022 14:08:27 -0800 Subject: [PATCH 06/32] Added transformation, export and import functionality --- css/viewer/annotationContainer.css | 8 +- js/viewer/annotation/annotation-group.js | 6 + js/viewer/annotation/annotation-svg.js | 12 +- js/viewer/annotation/text.js | 484 ++++++++++++----------- mview.html | 1 + 5 files changed, 268 insertions(+), 243 deletions(-) diff --git a/css/viewer/annotationContainer.css b/css/viewer/annotationContainer.css index 90427495..f30e4635 100644 --- a/css/viewer/annotationContainer.css +++ b/css/viewer/annotationContainer.css @@ -50,12 +50,13 @@ /* CSS for text annotation tool */ -.foreign { +.foreign-object { text-align: left; - alignment-baseline: baseline; + /* alignment-baseline: baseline; */ overflow: visible; } -.insideforeign { + +.foreign-object-div { display: inline-block; border: 100px solid red; position: absolute; @@ -70,7 +71,6 @@ width: 100%; height: 100%; background: transparent; - /* color: white; */ } .insideforeign .resizer-topleft { diff --git a/js/viewer/annotation/annotation-group.js b/js/viewer/annotation/annotation-group.js index 5d093ca0..7949a90e 100644 --- a/js/viewer/annotation/annotation-group.js +++ b/js/viewer/annotation/annotation-group.js @@ -55,9 +55,15 @@ function AnnotationGroup(id, anatomy, description, parent){ annotation = new Line(attrs); break; case "TEXT": + attrs["x"] = this.parent.imgWidth; + attrs["y"] = this.parent.imgHeight; annotation = new Text(attrs); annotation.addTextBox(this.id); break; + case "FOREIGNOBJECT": + annotation = new Text(attrs); + annotation.addTextBox(this.id, subtype); + break; case "ARROWLINE": // Create the marker definition and append it to the annotation group group = document.getElementById(this.id); diff --git a/js/viewer/annotation/annotation-svg.js b/js/viewer/annotation/annotation-svg.js index d07a4e34..7bb99cab 100644 --- a/js/viewer/annotation/annotation-svg.js +++ b/js/viewer/annotation/annotation-svg.js @@ -348,7 +348,6 @@ function AnnotationSVG(parent, id, imgWidth, imgHeight, scale, ignoreReferencePo // if (svgElems[i].getAttribute("id") == null) { continue; } var node = svgElems[i]; - console.log(node); // var groupID = svgElems[i].getAttribute("id") || Date.parse(new Date()) + parseInt(Math.random() * 1000); var className = node.getAttribute("class") || ""; var attrs = styleSheet[className] ? JSON.parse(JSON.stringify(styleSheet[className])) : {}; @@ -517,7 +516,8 @@ function AnnotationSVG(parent, id, imgWidth, imgHeight, scale, ignoreReferencePo case "foreignObject": if(id !== "undefined"){ group = this.groups.hasOwnProperty(id) ? this.groups[id] : this.createAnnotationGroup(id, anatomy); - annotation = group.addAnnotation(node.nodeName); + annotation = group.addAnnotation(node.nodeName, node); + console.log(annotation); annotation.setAttributesByJSON(this.getNodeAttributes(node)); annotation.renderSVG(this); } @@ -594,10 +594,10 @@ function AnnotationSVG(parent, id, imgWidth, imgHeight, scale, ignoreReferencePo // Remove annotation from a group this.removeAnnotationByGraphID = function(groupID, graphID, userData){ - // if(userData.type == "TEXT"){ - // userData.annotation.transform(); - // return; - // } + if(userData.type == "TEXT"){ + userData.annotation.transform(); + return; + } if(this.groups.hasOwnProperty(groupID)){ var group = this.groups[groupID]; group.removeAnnotationByID(graphID); diff --git a/js/viewer/annotation/text.js b/js/viewer/annotation/text.js index 74ecd177..a85cfe5d 100644 --- a/js/viewer/annotation/text.js +++ b/js/viewer/annotation/text.js @@ -4,13 +4,13 @@ function Text(attrs) { Base.call(this, attrs); this._tag = "text"; - this._attrs["x"] = attrs["x"] || 18600; - this._attrs["y"] = attrs["y"] || 19650; + this._attrs["x"] = (attrs["x"] / 2) || 18600; + this._attrs["y"] = (attrs["y"] / 2) || 19650; this._attrs["fill"] = attrs["fill"] || "red"; this._attrs["font-size"] = attrs["font-size"] || 1000; this._attrs["font-weight"] = attrs["font-weight"] || 400; this.svg = null; - this.foreignObj = ""; + this.foreignObj = null; this.setForeignObj = function(obj) { this.foreignObj = obj; @@ -40,258 +40,276 @@ Text.prototype.insertPoint = function (point) { _self.renderSVG(); }; -// +/* +Function to transform the