1 (function($){$(function(){
2 let csrf = $("[name='csrfmiddlewaretoken']").val();
4 var interestingReferences = $("#interesting-references").text();
5 if (interestingReferences) {
6 interestingReferences = $.parseJSON(interestingReferences);
8 if (Object.keys(interestingReferences).length) {
9 $("#settings-references").css('display', 'block');
12 var map_enabled = false;
16 function enable_map() {
17 if (!$("#reference-map").length) return;
19 $("#reference-map").show('slow');
21 if (map_enabled) return;
23 map = L.map('reference-map').setView([0, 0], 11);
24 L.tileLayer('https://tile.thunderforest.com/cycle/{z}/{x}/{y}.png?apikey=a8a97f0ae5134403ac38c1a075b03e15', {
25 attribution: 'Maps © <a href="http://www.thunderforest.com">Thunderforest</a>, Data © <a href="http://www.openstreetmap.org/copyright">OpenStreetMap contributors</a>'
27 marker = L.circleMarker([0,0]);
31 function disable_map() {
32 $("#reference-map").hide('slow');
36 $("#reference-close").on("click", function(event) {
37 event.preventDefault();
38 $("#reference-box").hide();
41 $('a.reference').each(function() {
43 uri = $this.attr('data-uri');
48 if (interestingReferences.hasOwnProperty(uri)) {
49 $this.addClass('interesting');
50 ref = interestingReferences[uri];
52 $this.attr('href', ref.wikipedia_link);
53 $this.attr('target', '_blank');
58 $('a.reference.interesting').on('click', function(event) {
59 event.preventDefault();
61 $("#reference-box").show();
64 uri = $this.attr('data-uri');
65 ref = interestingReferences[uri];
72 ref.location[1] + Math.round(
73 (map.getCenter().lng - ref.location[1]) / 360
77 marker.setLatLng(newLoc);
78 marker.bindTooltip(ref.label).openTooltip();
88 map.removeLayer(marker);
92 $("#reference-images a").remove();
94 $.each(ref.images, function(i, e) {
95 $i = $("<a target='_blank'><img></a>");
96 $i.attr('href', e.page);
97 $('img', $i).attr('src', e.thumburl || e.url);
98 if (e.thumbresolution) {
99 $('img', $i).attr('width', e.thumbresolution[0]).attr('height', e.thumbresolution[1]);
102 $("#reference-images").append($i);
106 $("#reference-link").text(ref.label);
107 $("#reference-link").attr('href', ref.wikipedia_link);
109 _paq.push(['trackEvent', 'html', 'reference']);
113 function putNoteAt($elem, anchor, side) {
114 $elem.data('anchoredTo', anchor);
115 updateNote($elem, side);
118 function updateNote($elem, side) {
119 anchor = $elem.data('anchoredTo')
121 let anchorRect = anchor.getBoundingClientRect();
123 let x = anchorRect.x + anchorRect.width / 2;
124 let y = anchorRect.y;
125 if ($elem.data('attach-bottom')) {
126 y += anchorRect.height;
128 minx = $("#book-text").position().left;
129 maxx = minx + $("#book-text").width();
137 boxwidth = $elem.width();
139 if (maxx - minx <= boxwidth) {
142 leftoffset = x - margin;
146 leftoffset = $elem.data('default-leftoffset');
147 if (leftoffset === undefined) {
148 leftoffset = $elem.width() / 2;
153 $elem.css({right: ''});
155 // Do we need to move away from the left?
162 // Do we need to move away from the right?
163 if (nx + boxwidth > maxx) {
165 let d = nx + boxwidth - maxx;
177 if (!$elem.data('attach-bottom')) {
178 ny = y - $elem.height() - 10;
185 $('.pointer', $elem).css({
191 function closeNoteBox() {
192 $('#annotation-box').data('anchoredTo', null).fadeOut();
194 $(document).on('click', function(event) {
195 let t = $(event.target);
196 if (t.parents('#annotation-box').length && !t.is('#footnote-link')) {
201 $(window).on('resize', closeNoteBox);
203 function getPositionInBookText($e) {
206 // Ok dla Y, nie ok dla X
208 while ($e.attr('id') != 'book-text') {
209 let p = $e.position();
212 $e = $e.offsetParent();
215 return {"x": x, "y": y}
218 $('#book-text .annotation').on('click', function(event) {
219 if ($(this).parents('#footnotes').length) return;
220 event.preventDefault();
222 href = $(this).attr('href').substr(1);
223 content = $("[name='" + href + "']").next().next().html();
224 if (!content) return;
225 $("#annotation-content").html(content);
226 $("#footnote-link").attr('href', '#' + href)
228 putNoteAt($('#annotation-box'), this);
229 event.stopPropagation();
237 success: function(data) {
239 $.each(zakladki, (i, e) => {
241 // TODO: not just paragraphs.
242 $('[href="#' + e.anchor + '"]').nextAll('.paragraph').first()
248 // TODO: create bookmarks on init
249 // We need to do that from anchors.
251 function zakladkaUpdateFor($item) {
253 let anchor = $item.prevAll('.target').first().attr('name');
255 if (anchor in zakladki) {
256 let $booktag = $item.data('booktag');
259 // TODO: only copy without the dialog.
260 $booktag = $("<div class='zakladka'>");
261 $booktag.append($('.icon', $zakladka).clone());
263 $item.data('booktag', $booktag);
264 $booktag.data('p', $item);
265 $booktag.data('anchor', anchor);
266 $zakladka.after($booktag);
268 zakladkaSetPosition($booktag);
273 if (zakladki[anchor].note) {
274 $z.removeClass('zakladka-exists');
275 $z.addClass('zakladka-note');
277 $z.removeClass('zakladka-note');
278 $z.addClass('zakladka-exists');
281 let $booktag = $item.data('booktag');
283 $item.data('booktag', null);
284 $zakladka.append($("#zakladka-box"));
290 function zakladkaSetPosition($z) {
291 $item = $z.data('p');
292 pos = getPositionInBookText($item);
296 right: ($('#main-text').width() - $('#book-text').width()) / 2,
300 let $zakladka = $('#zakladka');
301 $('#book-text .paragraph').on('mouseover', function() {showMarker($(this));});
302 $('#book-text .verse').on('mouseover', function() {showMarker($(this));});
303 //$.PMarker.showForP(this);
306 function showMarker(p) {
308 // Close the currently tag box when moving to another one.
309 // TBD: Do we want to keep the box open and prevent moving?
310 $("#zakladka-box").hide();
312 let anchor = p.prevAll('.target').first().attr('name');
313 // Don't bother if there's no anchor to use.
316 // Only show tag if there is not already a tag for this p.
317 if (p.data('booktag')) {
320 $zakladka.data('p', p);
321 $zakladka.data('anchor', anchor);
323 // (not needed) zakladkaUpdateClass();
324 // TODO: UPDATE THIS ON OPEN?
325 //let note = anchor in zakladki ? zakladki[anchor].note : '';
326 //$('textarea', $zakladka).val(note);
328 zakladkaSetPosition($zakladka);
333 $(".zakladka-tool_zakladka").on('click', function() {
334 let $z = $("#zakladka-box").data('z');
335 let anchor = $z.data('anchor');
336 let $p = $z.data('p');
340 csrfmiddlewaretoken: csrf,
343 success: function(data) {
344 zakladki[data.anchor] = data;
345 $("#zakladka-box").hide();
347 // Just hide, and create new .zakladka if not already exists?
348 // In general no hiding 'classed' .zakladka.
349 // So the 'cursor' .zakladka doesn't ever need class update.
350 //zakladkaUpdateClass();
351 zakladkaUpdateFor($p);
357 $(".zakladka-tool_notka_text textarea").on('input', function() {
358 // FIXME: no use const $zakladka here, check which .zakladka are we attached to.
359 let $z = $(this).closest('.zakladka');
360 let anchor = $z.data('anchor');
362 $("#notka-saved").hide();
363 //$("#notka-save").show();
365 url: '/zakladki/' + zakladki[anchor].uuid + '/',
367 csrfmiddlewaretoken: csrf,
370 success: function(data) {
371 zakladki[anchor] = data;
372 zakladkaUpdateFor($z.data('p'));
373 $("#notka-save").hide();
374 $("#notka-saved").fadeIn();
379 $(".zakladka-tool_zakladka_delete").on('click', function() {
380 let $z = $(this).closest('.zakladka');
381 let anchor = $z.data('anchor');
383 url: '/zakladki/' + zakladki[anchor].uuid + '/delete/',
385 csrfmiddlewaretoken: csrf,
387 success: function(data) {
388 delete zakladki[anchor];
389 $("#zakladka-box").hide();
390 zakladkaUpdateFor($z.data('p'));
395 $("#main-text").on("click", ".zakladka .icon", function() {
396 let $z = $(this).closest('.zakladka');
397 let $box = $("#zakladka-box");
401 let $p = $z.data('p');
402 let anchor = $z.data('anchor');
403 let note = anchor in zakladki ? zakladki[anchor].note : '';
405 $('.zakladka-tool_zakladka', $box).toggle(!(anchor in zakladki));
406 $('.zakladka-tool_sluchaj', $box).toggle($p.hasClass('syncable')).data('sync', $p.attr('id'));
407 $('textarea', $box).val(note);
417 showForSelection(sel) {
418 // TODO: only consider ranges inside text.?
419 this.selection = sel;
421 // TODO: multiple ranges.
422 let range = sel.getRangeAt(0);
423 let rect = range.getBoundingClientRect();
425 putNoteAt(this.qbox, range)
428 let rect = b.getBoundingClientRect();
430 putNoteAt(this.qbox, b, 'left')
433 this.qbox.data('anchoredTo', null);
437 this.qbox.data('anchoredTo', null);
438 this.qbox.addClass('copied').fadeOut(1500, () => {
439 this.qbox.removeClass('copied');
444 // TODO: only consider ranges inside text.?
445 let range = this.selection.getRangeAt(0);
446 let e = range.startContainer;
447 let anchor = getIdForElem(e);
448 let text = window.location.protocol + '//' +
449 window.location.host +
450 window.location.pathname;
452 navigator.clipboard.writeText(
453 this.selection.toString() +
454 '\n\nCałość czytaj na: ' + text
459 // TODO: only consider ranges inside text.?
460 let range = this.selection.getRangeAt(0);
461 let e = range.startContainer;
462 let anchor = getIdForElem(e);
463 let text = window.location.protocol + '//' +
464 window.location.host +
465 window.location.pathname;
466 if (anchor) text += '#' + anchor;
467 navigator.clipboard.writeText(text);
472 // What aboot non-contiguous selections?
473 let sel = this.selection;
474 let textContent = sel.toString();
475 let anchor = getIdForElem(sel.getRangeAt(0).startContainer);
476 let paths = getSelectionPaths(sel);
480 csrfmiddlewaretoken: csrf,
488 success: function (data) {
489 var win = window.open('/cytaty/' + data.uuid + '/', '_blank');
496 let qbox = new QBox($("#qbox"));
499 function getPathToNode(elem) {
502 while (elem.id != 'book-text') {
503 let p = elem.parentElement;
504 path.unshift([...p.childNodes].indexOf(elem))
509 function getSelectionPaths(selection) {
511 let range1 = selection.getRangeAt(0);
512 let range2 = selection.getRangeAt(selection.rangeCount - 1);
514 getPathToNode(range1.startContainer) + [range1.startOffset],
515 getPathToNode(range2.endContainer) + [range2.endOffset]
521 function getIdForElem(elem) {
524 // check if inside book-text?
527 if ($elem.hasClass('target')) {
528 return $elem.attr('name');
534 // Gdy wychodzimy w górę -- to jest ten moment, w którym znajdujemy element od którego wychodzimy i zliczamy znaki.
548 function getIdForElem(elem) {
550 // check if inside book-text?
553 if ($elem.hasClass('target')) {
554 return $elem.attr('name');
572 function positionToIIDOffset(container, offset) {
573 // Container and offset follow Range rules.
574 // If container is a text node, offset is text offset.
575 // If container is an element node, offset is number of child nodes from container start.
576 // (containers of type Comment, CDATASection - ignore)z
580 function updateQBox() {
581 sel = document.getSelection();
583 if (sel.isCollapsed || sel.rangeCount < 1) {
590 qbox.showForSelection(sel);
593 $(document).on('selectionchange', updateQBox);
595 function updateBoxes() {
596 updateNote(qbox.qbox);
597 updateNote($('#annotation-box'));
600 $(window).on('scroll', updateBoxes);
601 $(window).on('resize', updateBoxes);
604 $(window).on('resize', function() {
605 $('.zakladka').each(function() {
606 zakladkaSetPosition($(this));
610 $('a.anchor').on('click', function(e) {
611 // Workaround for bad TOC markers.
612 if ($(this).closest('#toc').length) return;
613 if ($(this).closest('#wltoc').length) return;
616 let sel = window.getSelection();
617 sel.removeAllRanges();
618 let range = document.createRange();
620 let $p = $(this).nextAll('.paragraph').first()
621 range.selectNodeContents($p[0]);
624 qbox.showForSelection(sel);
631 $('.qbox-t-copy').on('click', function(e) {
635 $('.qbox-t-link').on('click', function(e) {
639 $('.qbox-t-quote').on('click', function(e) {
646 $(".paragraph").on('click', function(e) {
647 qbox.showForBlock(this);
652 function scrollToAnchor(anchor) {
654 var anchor_name = anchor.slice(1);
655 var element = $('a[name="' + anchor_name + '"]');
656 if (element.length > 0) {
658 scrollTop: element.offset().top - 55
662 history.pushState({}, '', anchor);
668 scrollToAnchor(window.location.hash)
669 $('#toc, #themes, #book-text, #annotation').on('click', 'a', function(event) {
670 event.preventDefault();
671 scrollToAnchor($(this).attr('href'));