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;
13 var marker = L.circleMarker([0,0]);
16 function enable_map() {
17 $("#reference-map").show('slow');
19 if (map_enabled) return;
21 map = L.map('reference-map').setView([0, 0], 11);
22 L.tileLayer('https://tile.thunderforest.com/cycle/{z}/{x}/{y}.png?apikey=a8a97f0ae5134403ac38c1a075b03e15', {
23 attribution: 'Maps © <a href="http://www.thunderforest.com">Thunderforest</a>, Data © <a href="http://www.openstreetmap.org/copyright">OpenStreetMap contributors</a>'
28 function disable_map() {
29 $("#reference-map").hide('slow');
33 $("#reference-close").on("click", function(event) {
34 event.preventDefault();
35 $("#reference-box").hide();
38 $('a.reference').each(function() {
40 uri = $this.attr('data-uri');
45 if (interestingReferences.hasOwnProperty(uri)) {
46 $this.addClass('interesting');
47 ref = interestingReferences[uri];
49 $this.attr('href', ref.wikipedia_link);
50 $this.attr('target', '_blank');
55 $('a.reference.interesting').on('click', function(event) {
56 event.preventDefault();
58 $("#reference-box").show();
61 uri = $this.attr('data-uri');
62 ref = interestingReferences[uri];
69 ref.location[1] + Math.round(
70 (map.getCenter().lng - ref.location[1]) / 360
74 marker.setLatLng(newLoc);
75 marker.bindTooltip(ref.label).openTooltip();
85 map.removeLayer(marker);
89 $("#reference-images a").remove();
91 $.each(ref.images, function(i, e) {
92 $i = $("<a target='_blank'><img></a>");
93 $i.attr('href', e.page);
94 $('img', $i).attr('src', e.thumburl || e.url);
95 if (e.thumbresolution) {
96 $('img', $i).attr('width', e.thumbresolution[0]).attr('height', e.thumbresolution[1]);
99 $("#reference-images").append($i);
103 $("#reference-link").text(ref.label);
104 $("#reference-link").attr('href', ref.wikipedia_link);
106 _paq.push(['trackEvent', 'html', 'reference']);
110 function putNoteAt($elem, anchor, side) {
111 $elem.data('anchoredTo', anchor);
112 updateNote($elem, side);
115 function updateNote($elem, side) {
116 anchor = $elem.data('anchoredTo')
118 let anchorRect = anchor.getBoundingClientRect();
120 let x = anchorRect.x + anchorRect.width / 2;
121 let y = anchorRect.y;
122 if ($elem.data('attach-bottom')) {
123 y += anchorRect.height;
125 minx = $("#book-text").position().left;
126 maxx = minx + $("#book-text").width();
134 boxwidth = $elem.width();
136 if (maxx - minx <= boxwidth) {
139 leftoffset = x - margin;
143 leftoffset = $elem.data('default-leftoffset');
144 if (leftoffset === undefined) {
145 leftoffset = $elem.width() / 2;
150 $elem.css({right: ''});
152 // Do we need to move away from the left?
159 // Do we need to move away from the right?
160 if (nx + boxwidth > maxx) {
162 let d = nx + boxwidth - maxx;
171 if (!$elem.data('attach-bottom')) {
172 ny = y - $elem.height() - 10;
179 $('.pointer', $elem).css({
188 function closeNoteBox() {
189 $('#annotation-box').data('anchoredTo', null).fadeOut();
191 $(document).on('click', function(event) {
192 let t = $(event.target);
193 if (t.parents('#annotation-box').length && !t.is('#footnote-link')) {
198 $(window).on('resize', closeNoteBox);
200 function getPositionInBookText($e) {
203 // Ok dla Y, nie ok dla X
205 while ($e.attr('id') != 'book-text') {
206 let p = $e.position();
209 $e = $e.offsetParent();
212 return {"x": x, "y": y}
215 $('#book-text .annotation').on('click', function(event) {
216 if ($(this).parents('#footnotes').length) return;
217 event.preventDefault();
221 let x = $(this).width() / 2, y = 0;
223 while (elem.attr('id') != 'book-text') {
224 let p = $(elem).position();
227 elem = elem.parent();
229 href = $(this).attr('href').substr(1);
230 content = $("[name='" + href + "']").next().next().html();
231 if (!content) return;
232 $("#annotation-content").html(content);
233 $("#footnote-link").attr('href', '#' + href)
236 putNoteAt($('#annotation-box'), this);
237 event.stopPropagation();
245 success: function(data) {
247 $.each(zakladki, (i, e) => {
249 // TODO: not just paragraphs.
250 $('[href="#' + e.anchor + '"]').nextAll('.paragraph').first()
256 // TODO: create bookmarks on init
257 // We need to do that from anchors.
259 function zakladkaUpdateFor($item) {
261 let anchor = $item.prevAll('.target').first().attr('name');
263 if (anchor in zakladki) {
264 let $booktag = $item.data('booktag');
267 // TODO: only copy without the dialog.
268 $booktag = $("<div class='zakladka'>");
269 $booktag.append($('.icon', $zakladka).clone());
271 $item.data('booktag', $booktag);
272 $booktag.data('p', $item);
273 $booktag.data('anchor', anchor);
274 $zakladka.after($booktag);
276 zakladkaSetPosition($booktag);
281 if (zakladki[anchor].note) {
282 $z.removeClass('zakladka-exists');
283 $z.addClass('zakladka-note');
285 $z.removeClass('zakladka-note');
286 $z.addClass('zakladka-exists');
289 let $booktag = $item.data('booktag');
291 $item.data('booktag', null);
292 $zakladka.append($("#zakladka-box"));
298 function zakladkaSetPosition($z) {
299 $item = $z.data('p');
300 pos = getPositionInBookText($item);
304 right: ($('#main-text').width() - $('#book-text').width()) / 2,
308 let $zakladka = $('#zakladka');
309 $('#book-text .paragraph').on('mouseover', function() {showMarker($(this));});
310 $('#book-text .verse').on('mouseover', function() {showMarker($(this));});
311 //$.PMarker.showForP(this);
314 function showMarker(p) {
316 // Close the currently tag box when moving to another one.
317 // TBD: Do we want to keep the box open and prevent moving?
318 $("#zakladka-box").hide();
320 let anchor = p.prevAll('.target').first().attr('name');
321 // Don't bother if there's no anchor to use.
324 // Only show tag if there is not already a tag for this p.
325 if (p.data('booktag')) {
328 $zakladka.data('p', p);
329 $zakladka.data('anchor', anchor);
331 // (not needed) zakladkaUpdateClass();
332 // TODO: UPDATE THIS ON OPEN?
333 //let note = anchor in zakladki ? zakladki[anchor].note : '';
334 //$('textarea', $zakladka).val(note);
336 zakladkaSetPosition($zakladka);
341 $(".zakladka-tool_zakladka").on('click', function() {
342 let $z = $("#zakladka-box").data('z');
343 let anchor = $z.data('anchor');
344 let $p = $z.data('p');
348 csrfmiddlewaretoken: csrf,
351 success: function(data) {
352 zakladki[data.anchor] = data;
353 $("#zakladka-box").hide();
355 // Just hide, and create new .zakladka if not already exists?
356 // In general no hiding 'classed' .zakladka.
357 // So the 'cursor' .zakladka doesn't ever need class update.
358 //zakladkaUpdateClass();
359 zakladkaUpdateFor($p);
365 $(".zakladka-tool_notka_text textarea").on('input', function() {
366 // FIXME: no use const $zakladka here, check which .zakladka are we attached to.
367 let $z = $(this).closest('.zakladka');
368 let anchor = $z.data('anchor');
370 $("#notka-saved").hide();
371 //$("#notka-save").show();
373 url: '/zakladki/' + zakladki[anchor].uuid + '/',
375 csrfmiddlewaretoken: csrf,
378 success: function(data) {
379 zakladki[anchor] = data;
380 zakladkaUpdateFor($z.data('p'));
381 $("#notka-save").hide();
382 $("#notka-saved").fadeIn();
387 $(".zakladka-tool_zakladka_delete").on('click', function() {
388 let $z = $(this).closest('.zakladka');
389 let anchor = $z.data('anchor');
391 url: '/zakladki/' + zakladki[anchor].uuid + '/delete/',
393 csrfmiddlewaretoken: csrf,
395 success: function(data) {
396 delete zakladki[anchor];
397 $("#zakladka-box").hide();
398 zakladkaUpdateFor($z.data('p'));
403 $("#main-text").on("click", ".zakladka .icon", function() {
404 let $z = $(this).closest('.zakladka');
405 let $box = $("#zakladka-box");
409 let $p = $z.data('p');
410 let anchor = $z.data('anchor');
411 let note = anchor in zakladki ? zakladki[anchor].note : '';
413 $('.zakladka-tool_zakladka', $box).toggle(!(anchor in zakladki));
414 $('.zakladka-tool_sluchaj', $box).toggle($p.hasClass('syncable')).data('sync', $p.attr('id'));
415 $('textarea', $box).val(note);
425 showForSelection(sel) {
426 // TODO: only consider ranges inside text.?
427 this.selection = sel;
429 // TODO: multiple ranges.
430 let range = sel.getRangeAt(0);
431 let rect = range.getBoundingClientRect();
433 putNoteAt(this.qbox, range)
436 let rect = b.getBoundingClientRect();
438 putNoteAt(this.qbox, b, 'left')
441 this.qbox.data('anchoredTo', null);
445 this.qbox.data('anchoredTo', null);
446 this.qbox.addClass('copied').fadeOut(1500, () => {
447 this.qbox.removeClass('copied');
452 // TODO: only consider ranges inside text.?
453 let range = this.selection.getRangeAt(0);
454 let e = range.startContainer;
455 let anchor = getIdForElem(e);
456 let text = window.location.protocol + '//' +
457 window.location.host +
458 window.location.pathname;
460 navigator.clipboard.writeText(
461 this.selection.toString() +
462 '\n\nCałość czytaj na: ' + text
467 // TODO: only consider ranges inside text.?
468 let range = this.selection.getRangeAt(0);
469 let e = range.startContainer;
470 let anchor = getIdForElem(e);
471 let text = window.location.protocol + '//' +
472 window.location.host +
473 window.location.pathname;
474 if (anchor) text += '#' + anchor;
475 navigator.clipboard.writeText(text);
480 // What aboot non-contiguous selections?
481 let sel = this.selection;
482 let textContent = sel.toString();
483 let anchor = getIdForElem(sel.getRangeAt(0).startContainer);
484 let paths = getSelectionPaths(sel);
488 csrfmiddlewaretoken: csrf,
496 success: function (data) {
497 var win = window.open('/cytaty/' + data.uuid + '/', '_blank');
504 let qbox = new QBox($("#qbox"));
507 function getPathToNode(elem) {
510 while (elem.id != 'book-text') {
511 let p = elem.parentElement;
512 path.unshift([...p.childNodes].indexOf(elem))
517 function getSelectionPaths(selection) {
519 let range1 = selection.getRangeAt(0);
520 let range2 = selection.getRangeAt(selection.rangeCount - 1);
522 getPathToNode(range1.startContainer) + [range1.startOffset],
523 getPathToNode(range2.endContainer) + [range2.endOffset]
529 function getIdForElem(elem) {
532 // check if inside book-text?
535 if ($elem.hasClass('target')) {
536 return $elem.attr('name');
542 // Gdy wychodzimy w górę -- to jest ten moment, w którym znajdujemy element od którego wychodzimy i zliczamy znaki.
556 function getIdForElem(elem) {
558 // check if inside book-text?
561 if ($elem.hasClass('target')) {
562 return $elem.attr('name');
580 function positionToIIDOffset(container, offset) {
581 // Container and offset follow Range rules.
582 // If container is a text node, offset is text offset.
583 // If container is an element node, offset is number of child nodes from container start.
584 // (containers of type Comment, CDATASection - ignore)z
588 function updateQBox() {
589 sel = document.getSelection();
591 if (sel.isCollapsed || sel.rangeCount < 1) {
598 qbox.showForSelection(sel);
601 $(document).on('selectionchange', updateQBox);
603 function updateBoxes() {
604 updateNote(qbox.qbox);
605 updateNote($('#annotation-box'));
608 $(window).on('scroll', updateBoxes);
609 $(window).on('resize', updateBoxes);
612 $(window).on('resize', function() {
613 $('.zakladka').each(function() {
614 zakladkaSetPosition($(this));
618 $('a.anchor').on('click', function(e) {
621 let sel = window.getSelection();
622 sel.removeAllRanges();
623 let range = document.createRange();
625 let $p = $(this).nextAll('.paragraph').first()
626 range.selectNode($p[0]);
629 qbox.showForSelection(sel);
636 $('.qbox-t-copy').on('click', function(e) {
640 $('.qbox-t-link').on('click', function(e) {
644 $('.qbox-t-quote').on('click', function(e) {
651 $(".paragraph").on('click', function(e) {
652 qbox.showForBlock(this);
657 function scrollToAnchor(anchor) {
659 var anchor_name = anchor.slice(1);
660 var element = $('a[name="' + anchor_name + '"]');
661 if (element.length > 0) {
663 scrollTop: element.offset().top - 55
667 history.pushState({}, '', anchor);
673 scrollToAnchor(window.location.hash)
674 $('#toc, #themes, #book-text, #annotation').on('click', 'a', function(event) {
675 event.preventDefault();
676 scrollToAnchor($(this).attr('href'));