From 54b7c90fcd4457609ce1922785dca56f9addfe6c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sven=20K=C3=A4stle?= <sven.kaestle@gmx.de> Date: Tue, 28 Aug 2018 14:29:21 +0200 Subject: [PATCH] feat: Save user category selection in database and implement selection logic --- .../assets/css/unstructured-annotation.css | 68 +++++- .../assets/js/unstructuredAnnotation.js | 211 +++++++++++++++--- .../main/webapp/assets/js/unstructuredRest.js | 5 +- .../webapp/pages/unstructured-annotation.jsp | 4 + 4 files changed, 254 insertions(+), 34 deletions(-) diff --git a/gemeinsamforschen/src/main/webapp/assets/css/unstructured-annotation.css b/gemeinsamforschen/src/main/webapp/assets/css/unstructured-annotation.css index 7a2ec45e..dc0b1394 100644 --- a/gemeinsamforschen/src/main/webapp/assets/css/unstructured-annotation.css +++ b/gemeinsamforschen/src/main/webapp/assets/css/unstructured-annotation.css @@ -91,9 +91,75 @@ body, html { border-color: lightgray; color: lightgray; } -.added { + +.titel { + background-color: lightgreen; +} +.added-titel { border-style: solid; border-color: lightgreen; color: lightgreen; } +.recherche { + background-color: lightblue; +} +.added-recherche { + border-style: solid; + border-color: lightblue; + color: lightblue; +} + +.literaturverzeichnis { + background-color: lightcoral; +} +.added-literaturverzeichnis { + border-style: solid; + border-color: lightcoral; + color: lightcoral; +} + +.forschungsfrage { + background-color: cyan; +} +.added-forschungsfrage { + border-style: solid; + border-color: cyan; + color: cyan; +} + +.untersuchungskonzept { + background-color: goldenrod; +} +.added-untersuchungskonzept { + border-style: solid; + border-color: goldenrod; + color: goldenrod; +} + +.methodik { + background-color: lightpink; +} +.added-methodik { + border-style: solid; + border-color: lightpink; + color: lightpink; +} + +.durchfuehrung { + background-color: lightsalmon; +} +.added-durchfuehrung { + border-style: solid; + border-color: lightsalmon; + color: lightsalmon; +} + +.auswertung { + background-color: lightseagreen; +} +.added-auswertung { + border-style: solid; + border-color: lightseagreen; + color: lightseagreen; +} \ No newline at end of file diff --git a/gemeinsamforschen/src/main/webapp/assets/js/unstructuredAnnotation.js b/gemeinsamforschen/src/main/webapp/assets/js/unstructuredAnnotation.js index ea01fb02..d3008597 100644 --- a/gemeinsamforschen/src/main/webapp/assets/js/unstructuredAnnotation.js +++ b/gemeinsamforschen/src/main/webapp/assets/js/unstructuredAnnotation.js @@ -1,5 +1,3 @@ -var documentText; - /** * This function will fire when the DOM is ready */ @@ -15,6 +13,11 @@ $(document).ready(function() { location.href="unstructured-upload.jsp?token="+getUserTokenFromUrl(); }); + // set click listener to save button + $('#btnSave').click(function () { + saveButtonHandler(); + }); + /** @@ -24,24 +27,7 @@ $(document).ready(function() { selector: '.context-menu-one', callback: function(key, options) { - // TODO - show and handle more options - if (key === "titel" || - key === "recherche" || - key === "literaturverzeichnis" || - key === "forschungsfrage" || - key === "untersuchungskonzept" || - key === "methodik" || - key === "durchfuehrung" || - key === "auswertung") { - if (getSelectedText().length > 0) { - let startCharacter = window.getSelection().getRangeAt(0).startOffset; - let endCharacter = window.getSelection().getRangeAt(0).endOffset; - let selectedText = getSelectedText(); - - handleCategorySelection(selectedText, key, startCharacter, endCharacter); - - } - } + handleContextStuff(key); }, items: { @@ -81,14 +67,34 @@ function getSubmissionIdFromUrl() { } +/** + * Handel the category selection + * + * @param category The chosen category + * @param startCharacter The start character of the selected range + * @param endCharacter The end character of the selected range + */ +function handleCategorySelection(category, startCharacter, endCharacter) { -function handleCategorySelection(text, category, startCharacter, endCharacter) { - var elem = $('#' + category); - elem.toggleClass("not-added added"); + // if highlighting is possible + if (!isAlreadyHighlighted(startCharacter, endCharacter)) { - addHighlightedText(startCharacter, endCharacter); + // check if element has 'not-added' class + var elem = $('#' + category); + if (elem.hasClass("not-added")) { + elem.toggleClass("not-added added-" + category); + } - isAlreadyHighlighted(startCharacter, endCharacter); + // add highlighted text based on selected text + addHighlightedText(startCharacter, endCharacter, category, calculateExtraOffset(startCharacter)); + + // update data from category list + addSelectionDataToList(startCharacter, endCharacter, category); + } + else { + // show error message to user + window.alert("Dieser Bereich wurde bereits zugeordnet.") + } } @@ -114,30 +120,171 @@ function getSelectedText() { * * @param startCharacter The offset of the start character * @param endCharacter The offset of the end character + * @param category The category selected by user + * @param offset The calculated extra offset depending on already highlighted text */ -function addHighlightedText(startCharacter, endCharacter) { +function addHighlightedText(startCharacter, endCharacter, category, offset) { var documentText = $('#documentText').text(); var documentHtml = $('#documentText').html(); // create <span> tag with the annotated text - var replacement = $('<span></span>').css('background-color', 'lightgreen').html(documentText.slice(startCharacter, endCharacter)); + var replacement = $('<span></span>').attr('class', category).html(documentText.slice(startCharacter, endCharacter)); // wrap an <p> tag around the replacement, get its parent (the <p>) and ask for the html var replacementHtml = replacement.wrap('<p/>').parent().html(); // insert the replacementHtml - var newDocument = documentText.slice(0, startCharacter) + replacementHtml + documentText.slice(endCharacter); + var newDocument = documentHtml.slice(0, startCharacter + offset) + replacementHtml + documentHtml.slice(endCharacter + offset); // set new document text $('#documentText').html(newDocument); } +/** + * Check if the selected range is already highlighted + * + * @param startCharacter The start character of the range + * @param endCharacter The end character of the range + * @returns {boolean} Returns true if the selected range ist already highlighted + */ function isAlreadyHighlighted(startCharacter, endCharacter) { - $('#annotations').each(function () { - if ($(this).find(".category-card").attr("added")) { - console.log("hi") + let isHighlighted = false; + $('#annotations').find('.category-card').each(function () { + let array = $(this).data('array'); + if (array != null) { + for (let i = 0; i < array.length; i++) { + if ((array[i].start <= startCharacter && startCharacter <= array[i].end - 1) || // startCharacter inside highlighted range + (array[i].start <= endCharacter - 1 && endCharacter - 1 <= array[i].end - 1) || // endCharacter inside highlighted range + (startCharacter <= array[i].start && array[i].end - 1 <= endCharacter - 1)) { // new range overlaps hightlighted range + isHighlighted = true; + } + } + } + }); + return isHighlighted; +} + +/** + * Iterate over all data arrays and calculate the offset for a given start character + * + * @param startCharacter The given start character + * @returns {number} The offset + */ +function calculateExtraOffset(startCharacter) { + let extraOffset = 0; + $('#annotations').find('.category-card').each(function () { + let array = $(this).data('array'); + if (array != null) { + for (let i = 0; i < array.length; i++) { + if (array[i].end <= startCharacter) { + extraOffset += 22 + $(this).attr('id').length; + } + } } }); - return false; + return extraOffset; +} + +/** + * Save start and end character to the data array of a category + * + * @param startCharacter The start character of the body element + * @param endCharacter The end character of the body element + * @param category The chosen category + */ +function addSelectionDataToList(startCharacter, endCharacter, category) { + let elem = $('#' + category); + let array = elem.data('array'); + + if (array != null) { + // define new object + let newElement = { + start: startCharacter, + end: endCharacter + }; + // update array + array.push(newElement); + } + else { + // store first element in array + array = [ + { + "start": startCharacter, + "end": endCharacter + } + ] + } + + // sort array + array.sort(function (a, b) { + return a.start - b.start; + }); + // store updated array + elem.data('array', array); +} + +/** + * Handel the save button click + * Iterate over the category cards and send each post request to the back-end + */ +function saveButtonHandler() { + // show alert message + if (window.confirm("Möchten Sie wirklich ihre Annotationen speichern?")) { + $('#annotations').find('.category-card').each(function () { + let array = $(this).data('array'); + if (array != null) { + + // initialize the post request + let category = $(this).attr('id').toUpperCase(); + let submissionPartPostRequest = { + userId: getUserTokenFromUrl(), + fullSubmissionId: getSubmissionIdFromUrl(), + category: category, + body: [] + }; + + // iterate over the array + for (let i = 0; i < array.length; i++) { + + // initialize a body element + let submissionPartBodyElement = { + text: $('#documentText').text().slice(array[i].start, array[i].end), + startCharacter: array[i].start, + endCharacter: array[i].end + }; + + // store the body element in the post request + submissionPartPostRequest.body.push(submissionPartBodyElement); + } + + // send the post request to the back-end + createSubmissionPart(submissionPartPostRequest, function (response) { + console.log("send " + response.category + "'s post request to back-end") + }) + + } + }); + } +} + +function handleContextStuff(key) { + + // if saved selection's range count is > 0 + let sel = rangy.getSelection(); + if (sel.rangeCount > 0) { + // calculate character range offset from range + let range = sel.getRangeAt(0); + let offsets = range.toCharacterRange($('#documentText')[0]); + + // if selected text's length is > 0 + let selectedText = getSelectedText(); + if (selectedText.length > 0) { + // save start and end character and handle the selection + let startCharacter = offsets.start; + let endCharacter = offsets.end; + handleCategorySelection(key, startCharacter, endCharacter); + } + } + } diff --git a/gemeinsamforschen/src/main/webapp/assets/js/unstructuredRest.js b/gemeinsamforschen/src/main/webapp/assets/js/unstructuredRest.js index 56bc502d..80d3b51f 100644 --- a/gemeinsamforschen/src/main/webapp/assets/js/unstructuredRest.js +++ b/gemeinsamforschen/src/main/webapp/assets/js/unstructuredRest.js @@ -51,7 +51,7 @@ function getFullSubmission(id, responseHandler, errorHandler) { */ function createSubmissionPart(submissionPartPostRequest, responseHandler) { var url = "../rest/submissions/part/"; - var json = JSON.stringify(fullSubmissionPostRequest); + var json = JSON.stringify(submissionPartPostRequest); $.ajax({ url: url, type: "POST", @@ -60,6 +60,9 @@ function createSubmissionPart(submissionPartPostRequest, responseHandler) { dataType: "json", success: function (response) { responseHandler(response); + }, + error: function (e) { + console.log(e); } }); } diff --git a/gemeinsamforschen/src/main/webapp/pages/unstructured-annotation.jsp b/gemeinsamforschen/src/main/webapp/pages/unstructured-annotation.jsp index bdce1c37..94b29c07 100644 --- a/gemeinsamforschen/src/main/webapp/pages/unstructured-annotation.jsp +++ b/gemeinsamforschen/src/main/webapp/pages/unstructured-annotation.jsp @@ -20,6 +20,10 @@ <script src="https://cdnjs.cloudflare.com/ajax/libs/jqueryui/1.12.1/jquery-ui.min.js" type="text/javascript"></script> <!-- js - contextMenu script --> <script src="https://swisnl.github.io/jQuery-contextMenu/dist/jquery.contextMenu.js" type="text/javascript"></script> + <!-- js - rangy Core --> + <script src="https://cdnjs.cloudflare.com/ajax/libs/rangy/1.3.0/rangy-core.js" type="text/javascript"></script> + <!-- js - rangy TextRange Module --> + <script src="https://cdnjs.cloudflare.com/ajax/libs/rangy/1.3.0/rangy-textrange.js" type="text/javascript"></script> <!-- js - unstructuredRest --> <script src="../assets/js/unstructuredRest.js"></script> <!-- js - unstructuredUpload --> -- GitLab