236 lines
7.4 KiB
HTML
236 lines
7.4 KiB
HTML
{% extends "base_list.html" %}
|
|
{% load static %}
|
|
{% load form_tags %}
|
|
|
|
{% block title %}{{object.title}}{% endblock %}
|
|
|
|
{% block head_extra %}
|
|
<style>
|
|
.webpage-metadata {border: 1px solid #aaa; border-top:none; border-right:none; line-height:0.65em;padding-top:10px;}
|
|
.webpage-body {text-align:justify; padding-top:20px; font-family:serif; font-size:1.25em; line-height:1.6em; border-right: 1px #ccc solid; padding-right: 2em;}
|
|
.webpage-body br { margin-bottom: 1em; }
|
|
.webpage-notes {padding-top:20px; min-width: 220px;}
|
|
.webpage-notes .sticky-notes-container {
|
|
flex-direction: column;
|
|
position: relative;
|
|
height: 100%;
|
|
min-height: 500px;
|
|
}
|
|
.webpage-notes .sticky-note {
|
|
max-width: none;
|
|
position: absolute;
|
|
}
|
|
</style>
|
|
<link href="{% static 'css/recogito.min.css' %}" rel="stylesheet">
|
|
{% endblock %}
|
|
|
|
{% block extra_js %}
|
|
<script>
|
|
(function() {
|
|
var article = document.getElementById('article');
|
|
var notesContainer = document.querySelector('.webpage-notes .sticky-notes-container');
|
|
if (!article || !notesContainer) return;
|
|
|
|
var notes = [
|
|
{% for note in latest_scrobble_notes %}
|
|
{ text: "{{ note|escapejs }}", start: null, end: null },
|
|
{% endfor %}
|
|
];
|
|
|
|
var positionRe = /\[start:(\d+)\|end:(\d+)\]$/;
|
|
|
|
notes.forEach(function(noteObj) {
|
|
var match = noteObj.text.match(positionRe);
|
|
if (match) {
|
|
noteObj.start = parseInt(match[1]);
|
|
noteObj.end = parseInt(match[2]);
|
|
}
|
|
});
|
|
|
|
function positionNotes() {
|
|
var articleText = article.innerText || article.textContent;
|
|
var articleHeight = article.offsetHeight;
|
|
|
|
notesContainer.style.height = articleHeight + 'px';
|
|
|
|
notes.forEach(function(note, index) {
|
|
if (note.start === null) return;
|
|
|
|
var noteEl = notesContainer.children[index];
|
|
if (!noteEl) return;
|
|
|
|
var percentage = (note.start / articleText.length) * 100;
|
|
var topPos = (percentage / 100) * articleHeight;
|
|
|
|
noteEl.style.top = topPos + 'px';
|
|
});
|
|
}
|
|
|
|
if (notes.length > 0) {
|
|
positionNotes();
|
|
window.addEventListener('resize', positionNotes);
|
|
}
|
|
})();
|
|
</script>
|
|
<script src="{% static 'js/recogito.min.js' %}"></script>
|
|
<script type="text/javascript">
|
|
(function() {
|
|
var latestScrobbleUuid = "{{ latest_scrobble_uuid|default:'' }}";
|
|
if (!latestScrobbleUuid) {
|
|
return;
|
|
}
|
|
|
|
var r = Recogito.init({
|
|
content: document.getElementById('article')
|
|
});
|
|
|
|
var notePositionRe = /\[start:(\d+)\|end:(\d+)\]$/;
|
|
|
|
function parseNote(noteStr) {
|
|
var match = noteStr.match(notePositionRe);
|
|
if (match) {
|
|
return {
|
|
text: noteStr.replace(notePositionRe, ''),
|
|
position: { start: parseInt(match[1]), end: parseInt(match[2]) }
|
|
};
|
|
}
|
|
return { text: noteStr, position: null };
|
|
}
|
|
|
|
var existingNotes = [
|
|
{% for note in latest_scrobble_notes %}
|
|
(function() {
|
|
var parsed = parseNote("{{ note|escapejs }}");
|
|
if (parsed.position) {
|
|
return {
|
|
"@context": "http://www.w3.org/ns/anno.jsonld",
|
|
"@type": "Annotation",
|
|
"id": "note-{{ forloop.counter }}",
|
|
"body": [{"@type": "TextualBody", "value": parsed.text}],
|
|
"target": {"selector": [{"type": "TextPositionSelector", "start": parsed.position.start, "end": parsed.position.end}]}
|
|
};
|
|
}
|
|
return null;
|
|
})(),
|
|
{% endfor %}
|
|
].filter(function(n) { return n !== null; });
|
|
|
|
console.log("existing:", existingNotes);
|
|
if (existingNotes.length > 0) {
|
|
r.setAnnotations(existingNotes);
|
|
}
|
|
|
|
function getPositionFromAnnotation(annotation) {
|
|
var position = null;
|
|
if (annotation.target && annotation.target.selector) {
|
|
var selector = annotation.target.selector;
|
|
for (var i = 0; i < selector.length; i++) {
|
|
if (selector[i].type === 'TextPositionSelector') {
|
|
position = { start: selector[i].start, end: selector[i].end };
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
return position;
|
|
}
|
|
|
|
var currentAnnotation = null;
|
|
|
|
r.on('createAnnotation', function(annotation) {
|
|
var note = annotation.body && annotation.body[0] ? annotation.body[0].value : null;
|
|
if (!note) {
|
|
return;
|
|
}
|
|
|
|
var position = getPositionFromAnnotation(annotation);
|
|
|
|
fetch('/api/v1/scrobbles/' + latestScrobbleUuid + '/', {
|
|
method: 'PATCH',
|
|
credentials: 'same-origin',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
'X-CSRFToken': '{{ csrf_token }}'
|
|
},
|
|
body: JSON.stringify({ note: note, note_position: position })
|
|
})
|
|
.then(response => {
|
|
if (!response.ok) {
|
|
console.error('Failed to save note:', response.status, response.statusText);
|
|
}
|
|
})
|
|
.catch(error => {
|
|
console.error('Error saving note:', error);
|
|
});
|
|
});
|
|
|
|
r.on('selectAnnotation', function(annotation, element) {
|
|
currentAnnotation = annotation;
|
|
});
|
|
|
|
r.on('deleteAnnotation', function(annotation) {
|
|
console.log('Deleted annotation:', annotation);
|
|
});
|
|
})();
|
|
</script>
|
|
{% endblock %}
|
|
|
|
{% block lists %}
|
|
<div class="row">
|
|
<div class="col-md">
|
|
<h3>Last scrobbles</h3>
|
|
<div class="table-responsive">
|
|
<table class="table table-striped table-sm">
|
|
<thead>
|
|
<tr>
|
|
<th scope="col">Date</th>
|
|
<th scope="col">Notes</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
{% for scrobble in scrobbles.all %}
|
|
<tr>
|
|
<td><a href={{scrobble.get_absolute_url}}>{{scrobble.local_timestamp}}</a></td>
|
|
<td>{% if scrobble.logdata.notes %}{{ scrobble.logdata.notes_as_str }}{% endif %}
|
|
</tr>
|
|
{% endfor %}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<hr/>
|
|
<div class="row webpage">
|
|
<div class="webpage-metadata">
|
|
<p>Source: <a href="{{object.url}}">{{object.domain}}</a></p>
|
|
{% if object.date %}<p>Published: <em>{{object.date}}</em></p>{% endif %}
|
|
<p>Time to read: {{object.estimated_time_to_read_in_minutes}} minutes</p>
|
|
{% if object.tags.all %}
|
|
<p>Tags:
|
|
{% for tag in object.tags.all %}
|
|
<a href="{% url 'webpages:webpage_list' %}?tag={{ tag.name|urlencode }}">{{ tag.name }}</a>{% if not forloop.last %}, {% endif %}
|
|
{% endfor %}
|
|
</p>
|
|
{% endif %}
|
|
</div>
|
|
{% if object.extract %}
|
|
<div class="col">
|
|
<div class="webpage-body" id="article">
|
|
{{object.extract|linebreaks}}
|
|
</div>
|
|
</div>
|
|
{% endif %}
|
|
<div class="col-auto">
|
|
<div class="webpage-notes">
|
|
{% if latest_scrobble_notes %}
|
|
<h5>Notes</h5>
|
|
<div class="sticky-notes-container" style="position: relative; height: 100%;">
|
|
{% for note in latest_scrobble_notes %}
|
|
<div class="sticky-note" {{ note|note_position:article_length }}>{{ note|note_text }}</div>
|
|
{% endfor %}
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{% endblock %}
|