Skip to content
Snippets Groups Projects
Commit 6055c901 authored by Sven Kästle's avatar Sven Kästle
Browse files

feat: Implement first design concept

parent cf9d3b72
No related branches found
No related tags found
No related merge requests found
ol {
padding: 0px;
margin: 0px;
list-style-type: none;
}
.mainpage {
display: flex;
box-sizing: border-box;
font-family: Arial;
height: 100%;
}
.rightcolumn {
float: right;
width: 25%;
display: inline-block;
/* background-color: blue; */
}
.leftcolumn {
float: left;
width: 75%;
display: inline-block;
/* background-color: yellow; */
}
.rightcontent {
margin: 10px;
}
.leftcontent {
margin: 10px;
/* background-color: white; */
}
.spacing {
height: 10px;
/* background-color: orange; */
}
.annotation-card {
width: 100%;
box-shadow: 0 4px 8px 0 rgba(0,0,0,0.2);
transition: 0.3s;
border-radius: 5px;
display: inline-block;
background-color: white;
}
.annotation-card:hover {
box-shadow: 0 8px 16px 0 rgba(0,0,0,0.2);
}
.annotation-header {
padding: 5px;
display: flex;
flex-wrap: wrap;
align-items: center;
border-top-right-radius: 5px;
border-top-left-radius: 5px;
color: white; }
.annotation-header i {
font-size: 11px;
}
.annotation-header span {
font-size: 11px;
margin-left: 5px;
margin-right: 5px;
}
.annotation-header a:link {
color: white;
text-decoration: none;
}
.annotation-header a:visited {
color: white;
text-decoration: none;
}
.annotation-header a:active {
color: white;
text-decoration: none;
}
.annotation-header a:hover {
color: #e6e6e6;
text-decoration: none;
}
.annotation-header-title {
display: flex;
flex-flow: column;
width: calc(100% - 40px);
}
.annotation-header-toggle {
height: 40px;
width: 40px;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
}
.annotation-body {
padding: 8px;
border-bottom-right-radius: 5px;
border-bottom-left-radius: 5px;
}
.annotation-body p {
margin: 0px;
font-size: 13px;
}
.overflow-hidden {
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
.annotation-footer {
padding: 5px;
padding-top: 0px;
text-align: right;
font-size: 9px;
color: lightgrey;
display: flex;
}
.annotation-footer span {
margin-left: 5px;
}
.annotation-footer-delete {
margin-right: 5px;
cursor: pointer;
}
.annotation-footer-date {
flex: 1
}
.unstructured-textarea {
resize: none;
}
.document-text-buttons {
margin-top: 10px;
display: inline;
}
.document-text-buttons-lock {
float: left;
}
.document-text-buttons-save {
float: right;
}
// initialize userId, userColors and targetId
var userId = randomUserId();
var userColors = new Map();
var userColorsDark = new Map();
var targetId = 200;
// declare document text
var documentText;
/**
* This function will fire when the DOM is ready
*/
$(document).ready(function() {
/**
* Context menu handler
*/
$.contextMenu({
selector: '.context-menu-one',
callback: function(key, options) {
// close context menu
window.close;
// initialize selected body
var body = getSelectedTextFromTextArea();
// if user selected something
if (body.length > 0) {
// annotationPostRequest
var request = {
userId: userId,
targetId: targetId,
body: body,
startCharacter: window.getSelection().getRangeAt(0).startOffset,
endCharacter: window.getSelection().getRangeAt(0).endOffset
};
console.log(request);
createAnnotation(request, function(response) {
// display the new annotation
displayAnnotation(response);
});
}
},
items: {
"annotation": {name: "Annotation", icon: "edit"}
}
});
/*
* PAGE LOADED
*/
documentText = $('#documentText').html();
// fetch annotations from server on page start
getAnnotations(targetId, function (response) {
// iterate over annotations and display each
$.each(response, function (i, annotation) {
displayAnnotation(annotation);
})
});
});
/**
* POST: Save an annotation in the database
*
* @param annotationPostRequest The post request
* @param responseHandler The response handler
*/
function createAnnotation(annotationPostRequest, responseHandler) {
var url = "http://localhost:8080/rest/annotations/";
var json = JSON.stringify(annotationPostRequest);
$.ajax({
url: url,
type: "POST",
data: json,
contentType: "application/json",
dataType: "json",
success: function (response) {
responseHandler(response);
}
});
}
/**
* PATCH: Alter an annotation in database
*
* @param id The annotation id
* @param annotationPatchRequest The patch request
* @param responseHandler The response handler
*/
function alterAnnotation(id, annotationPatchRequest, responseHandler) {
var url = "http://localhost:8080/rest/annotations/" + id;
var json = JSON.stringify(annotationPatchRequest);
$.ajax({
url: url,
type: "PATCH",
data: json,
contentType: "application/json",
dataType: "json",
success: function (response) {
responseHandler(response);
}
});
}
/**
* DELETE: Delete an annotation from database
*
* @param id The annotation id
*/
function deleteAnnotation(id) {
var url = "http://localhost:8080/rest/annotations/" + id;
$.ajax({
url: url,
type: "DELETE",
dataType: "json",
success: function (response) {
// Nothing to do
}
});
}
/**
* GET: Get all annotations from database for a specific target
*
*
* @param targetId The target id
* @param responseHandler The response handler
*/
function getAnnotations(targetId, responseHandler) {
var url = "http://localhost:8080/rest/annotations/target/" + targetId;
$.ajax({
url: url,
type: "GET",
dataType: "json",
success: function (response) {
// sort the responding annotations by timestamp (DESC)
response.sort(function (a, b) {
return a.timestamp - b.timestamp;
});
// handle the response
responseHandler(response);
}
});
}
/**
* Delete annotation from list
*
* @param elem The parent li element
* @param id The id of the annotation
*/
function deleteUnstructuredAnnotationHandler(elem, id) {
// remove annotation from list
elem.remove()
// remove annotation from database
deleteAnnotation(id)
}
/**
* Display annotation in the list
*
* @param annotation The annotation to be displayed
*/
function displayAnnotation(annotation) {
// fetch list of annotations
var list = $('#annotations')
var deleteIcon = "fas fa-trash";
var dateIcon = "fas fa-calendar";
if (isTimestampToday(annotation.timestamp)) {
dateIcon = "fas fa-clock";
}
// insert annotation card
list.prepend(
$('<li>')
.attr('class', 'listelement')
.append(
$('<div>').attr('class', 'annotation-card')
.mouseenter(function () {
$(this).children('.annotation-header').css('background-color', getDarkUserColor(annotation.userId));
})
.mouseleave(function () {
$(this).children('.annotation-header').css('background-color', getUserColor(annotation.userId));
})
.append(
$('<div>').attr('class', 'annotation-header')
.css('background-color', getUserColor(annotation.userId))
.append(
$('<div>').attr('class', 'annotation-header-title')
.append(
$('<div>').attr('class', 'overflow-hidden')
.append(
$('<i>').attr('class', 'fas fa-user')
)
.append(
$('<span>').append(annotation.userId)
)
)
.append(
$('<div>').attr('class', 'overflow-hidden')
.append(
$('<i>').attr('class', 'fas fa-bookmark')
)
.append(
$('<span>').append('title' + annotation.userId)
)
)
)
.append(
$('<div>').attr('class', 'annotation-header-toggle')
.click(function () {
toggleButtonHandler($(this));
})
.append(
$('<i>').attr('class', 'fas fa-chevron-down')
)
)
)
.append(
$('<div>').attr('class', 'annotation-body')
.append(
$('<p>').attr('class', 'overflow-hidden').append(annotation.body)
)
)
.append(
$('<div>').attr('class', 'annotation-footer')
.append(
function () {
if (userId == annotation.userId) {
return $('<div>').attr('class', 'annotation-footer-delete')
.append(
$('<i>').attr('class', deleteIcon)
)
.click(function () {
deleteUnstructuredAnnotationHandler($(this).closest('li'), annotation.id)
})
}
}
)
.append(
$('<div>').attr('class', 'annotation-footer-date overflow-hidden')
.append(
$('<i>').attr('class', dateIcon)
)
.append(
$('<span>').append(timestampToReadableTime(annotation.timestamp))
)
)
)
)
.data('annotation', annotation)
.append(function () {
if ($('#annotations li').filter( ".listelement" ).length > 0) {
return $('<div>').attr('class', 'spacing')
}
})
);
}
/**
* Add a highlighted text at specific position
*
* @param startCharacter The offset of the start character
* @param endCharacter The offset of the end character
* @param userId The user id
*/
function addHighlightedText(startCharacter, endCharacter, userId) {
// create <span> tag with the annotated text
var replacement = $('<span></span>').css('background-color', getUserColor(userId)).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);
// set new document text
$('#documentText').html(newDocument);
}
/**
* Restore the base text
*/
function deleteHighlightedText() {
$('#documentText').html(documentText);
}
/**
* Get the text value of the selected text
*
* @returns {string} The text
*/
function getSelectedText() {
if(window.getSelection){
return window.getSelection().toString();
}
else if(document.getSelection){
return document.getSelection();
}
else if(document.selection){
return document.selection.createRange().text;
}
}
/**
* Get the text value of the selected text from a textarea
*
* @returns {string} The text
*/
function getSelectedTextFromTextArea() {
// if firefox
if( navigator.userAgent.toLowerCase().indexOf('firefox') > -1 ){
// reference to the textarea
var txtarea = document.getElementById("upload-area");
// get index of first character
var start = txtarea.selectionStart;
// get index of last character
var end = txtarea.selectionEnd;
// return substring from start to end
return txtarea.value.substring(start, end);
}
else if(window.getSelection){
return window.getSelection().toString();
}
else if(document.getSelection){
return document.getSelection();
}
else if(document.selection){
return document.selection.createRange().text;
}
}
/**
* Get color based on user id
*
* @param userId The id of the user
* @returns {string} The user color
*/
function getUserColor(userId) {
// insert new color if there is no userId key
if (userColors.get(userId) == null) {
generateRandomColor(userId);
}
// return the color
return userColors.get(userId);
}
/**
* Get dark color based on user id
*
* @param userId The id of the user
* @returns {string} The dark user color
*/
function getDarkUserColor(userId) {
// insert new color if there is no userId key
if (userColorsDark.get(userId) == null) {
generateRandomColor(userId);
}
// return the color
return userColorsDark.get(userId);
}
/**
* Generate a random color of the format 'rgb(r, g, b)'
*
* @param userId The given user id
*/
function generateRandomColor(userId) {
var r = Math.floor(Math.random()*56)+170;
var g = Math.floor(Math.random()*56)+170;
var b = Math.floor(Math.random()*56)+170;
var r_d = r - 50;
var g_d = g - 50;
var b_d = b - 50;
var color = 'rgb(' + r + ',' + g + ',' + b + ')';
var colorDark = 'rgb(' + r_d + ',' + g_d + ',' + b_d + ')';
userColors.set(userId, color);
userColorsDark.set(userId, colorDark);
}
/**
* Calculate and build a readable timestamp from an unix timestamp
*
* @param timestamp A unix timestamp
* @returns {string} A readable timestamp
*/
function timestampToReadableTime(timestamp) {
// build Date object from timestamp
var annotationDate = new Date(timestamp);
// declare response
var responseTimestamp;
// if annotation is from today
if (isTimestampToday(timestamp)) {
// get hours from date
var hours = annotationDate.getHours();
// get minutes from date
var minutes = "0" + annotationDate.getMinutes();
// get seconds from date
// var seconds = "0" + annotationDate.getSeconds();
// build readable timestamp
responseTimestamp = hours + ":" + minutes.substr(-2);
}
// else annotation is not from today
else {
// get date
var date = annotationDate.getDate();
// get month
var month = annotationDate.getMonth();
// get year
var year = annotationDate.getFullYear();
// build readable timestamp
responseTimestamp = date + "." + month + "." + year;
}
return responseTimestamp;
}
/**
* Check if given timestamp is from today
*
* @param timestamp The given timestamp in milliseconds
* @returns {boolean} Returns true if the timestamp is from today
*/
function isTimestampToday(timestamp) {
// now
var now = new Date();
// build Date object from timestamp
var date = new Date(timestamp);
// return true if timestamp is today
if (now.getDate() == date.getDate() && now.getMonth() == date.getMonth() && now.getFullYear() == date.getFullYear()) {
return true;
}
else {
return false;
}
}
/**
* Toggle between the toggle button status
*
* @param element The given toggle button
*/
function toggleButtonHandler(element) {
// open and close annotation text
element.parent().siblings(".annotation-body").children("p").toggleClass("overflow-hidden");
// toggle between up and down button
element.children("i").toggleClass("fa-chevron-down fa-chevron-up")
}
function lockButtonHandler() {
var lock = $('#btnLock').children('i')
lock.toggleClass("fa-lock-open fa-lock")
var area = $('#upload-area')
if (area.attr('readonly')) {
area.attr('readonly', false);
}
else {
area.attr('readonly', true);
}
}
/*
MOCKUP FUNCTIONS
*/
function randomUserId() {
return Math.floor((Math.random() * 12) + 1);;
}
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>muster-gemeinsam-forschen</title>
<!-- css - annotationStyle -->
<link rel="stylesheet" type="text/css" href="../assets/css/upload-unstructured.css">
<!-- css - contextMenu -->
<link href="https://swisnl.github.io/jQuery-contextMenu/dist/jquery.contextMenu.css" rel="stylesheet" type="text/css" />
<!-- css - bootstrap -->
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css">
<!-- css - styles -->
<link rel="stylesheet" href="../assets/css/styles.css">
<!-- css - font awesome -->
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.1.0/css/all.css" integrity="sha384-lKuwvrZot6UHsBSfcMvOkWwlCMgc0TaWr+30HWe3a4ltaBwTZhyTEggF5tJv8tbt" crossorigin="anonymous">
<!-- css - sidebar -->
<link rel="stylesheet" href="../assets/css/Sidebar-Menu.css">
<!-- js - jQuery -->
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<!-- js - bootstrap -->
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>
<!-- js - jQuery ui position -->
<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 - annotationScript -->
<script src="../assets/js/uploadUnstructured.js"></script>
</head>
<body>
<div id="wrapper">
<div id="sidebar-wrapper">
<ul class="sidebar-nav">
<li class="sidebar-brand"><a href="#">overview</a></li>
<li><a href="#">Quizfrage</a></li>
<li><a href="#">ePortfolio</a></li>
<li><a href="#">Beitrag</a></li>
<li><a href="#">Bewertung</a></li>
<li><a href="#">Logout</a></li>
</ul>
</div>
<div class="page-content-wrapper">
<div class="container-fluid">
<h1>Unstrukturierte Abgabe
<a href="#">
<span class="glyphicon glyphicon-envelope"
style="font-size:27px;margin-top:-17px;margin-left:600px;"></span>
</a>
<a href="#">
<span class="glyphicon glyphicon-cog" style="font-size:29px;margin-left:5px;margin-top:-25px;"></span>
</a>
</h1>
<div class="mainpage">
<div class="leftcolumn">
<div class="leftcontent ">
<div class="context-menu-one form-group" id="documentText">
<label for="upload-area">Texteingabe:</label>
<textarea class="unstructured-textarea form-control" rows="20" id="upload-area">
Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.
Duis autem vel eum iriure dolor in hendrerit in vulputate velit esse molestie consequat, vel illum dolore eu feugiat nulla facilisis at vero eros et accumsan et iusto odio dignissim qui blandit praesent luptatum zzril delenit augue duis dolore te feugait nulla facilisi. Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut laoreet dolore magna aliquam erat volutpat.
Ut wisi enim ad minim veniam, quis nostrud exerci tation ullamcorper suscipit lobortis nisl ut aliquip ex ea commodo consequat. Duis autem vel eum iriure dolor in hendrerit in vulputate velit esse molestie consequat, vel illum dolore eu feugiat nulla facilisis at vero eros et accumsan et iusto odio dignissim qui blandit praesent luptatum zzril delenit augue duis dolore te feugait nulla facilisi.
Nam liber tempor cum soluta nobis eleifend option congue nihil imperdiet doming id quod mazim placerat facer possim assum. Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut laoreet dolore magna aliquam erat volutpat. Ut wisi enim ad minim veniam, quis nostrud exerci tation ullamcorper suscipit lobortis nisl ut aliquip ex ea commodo consequat.
At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, At accusam aliquyam diam diam dolore dolores duo eirmod eos erat, et nonumy sed tempor et et invidunt justo labore Stet clita ea et gubergren, kasd magna no rebum. sanctus sea sed takimata ut vero voluptua. est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat.
</textarea>
</div>
<div class="document-text-buttons">
<button onclick="lockButtonHandler()" type="button" class="btn btn-secondary document-text-buttons-lock" id="btnLock"><i class="fas fa-lock-open" id="lock"></i></button>
<button type="button" class="btn btn-secondary document-text-buttons-save" id="btnSave">Speichern</i></button>
</div>
</div>
</div>
<div class="rightcolumn">
<div class="rightcontent">
<ol id="annotations">
</ol>
</div>
</div>
</div>
</div>
</div>
</div>
</body>
</html>
\ No newline at end of file
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment