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(callback) {
17 if (!$("#reference-map").length) return;
20 map = L.map('reference-map').setView([0, 0], 11);
21 L.tileLayer('https://tile.thunderforest.com/neighbourhood/{z}/{x}/{y}.png?apikey=a8a97f0ae5134403ac38c1a075b03e15', {
22 attribution: 'Maps © <a href="http://www.thunderforest.com">Thunderforest</a>, Data © <a href="http://www.openstreetmap.org/copyright">OpenStreetMap contributors</a>'
24 marker = L.circleMarker([0,0]);
27 $("#reference-map").show('slow', () => {
32 function disable_map() {
33 $("#reference-map").hide('slow');
37 $("#reference-close").on("click", function(event) {
38 event.preventDefault();
39 $("#reference-box").hide();
42 $('a.reference').each(function() {
44 uri = $this.attr('data-uri');
49 if (interestingReferences.hasOwnProperty(uri)) {
50 $this.addClass('interesting');
51 ref = interestingReferences[uri];
53 if (ref.wikipedia_link) {
54 $this.attr('href', ref.wikipedia_link);
55 $this.attr('target', '_blank');
57 $this.removeAttr('href')
63 $('a.reference.interesting').on('click', function(event) {
64 event.preventDefault();
66 $("#reference-box").show();
69 uri = $this.attr('data-uri');
70 ref = interestingReferences[uri];
77 ref.location[1] + Math.round(
78 (map.getCenter().lng - ref.location[1]) / 360
82 marker.setLatLng(newLoc);
83 marker.bindTooltip(ref.label).openTooltip();
94 map.removeLayer(marker);
98 $("#reference-images a").remove();
100 $.each(ref.images, function(i, e) {
101 $i = $("<a target='_blank'><img></a>");
102 $i.attr('href', e.page);
103 $('img', $i).attr('src', e.thumburl || e.url);
104 if (e.thumbresolution) {
105 $('img', $i).attr('width', e.thumbresolution[0]).attr('height', e.thumbresolution[1]);
108 $("#reference-images").append($i);
112 $("#reference-link").text(ref.label);
113 if (ref.wikipedia_link) {
114 $("#reference-link").attr('href', ref.wikipedia_link);
116 $('#reference-link').removeAttr('href');
119 $("#reference-print").attr('href', '/mapa/ulotka/' + ref.id);
121 _paq.push(['trackEvent', 'html', 'reference']);
125 function putNoteAt($elem, anchor, side) {
126 $elem.data('anchoredTo', anchor);
127 updateNote($elem, side);
130 function updateNote($elem, side) {
131 anchor = $elem.data('anchoredTo')
133 let anchorRect = anchor.getBoundingClientRect();
135 let x = anchorRect.x + anchorRect.width / 2;
136 let y = anchorRect.y;
137 if ($elem.data('attach-bottom')) {
138 y += anchorRect.height;
140 minx = $("#book-text").position().left;
141 maxx = minx + $("#book-text").width();
149 boxwidth = $elem.width();
151 if (maxx - minx <= boxwidth) {
154 leftoffset = x - margin;
158 leftoffset = $elem.data('default-leftoffset');
159 if (leftoffset === undefined) {
160 leftoffset = $elem.width() / 2;
165 $elem.css({right: ''});
167 // Do we need to move away from the left?
174 // Do we need to move away from the right?
175 if (nx + boxwidth > maxx) {
177 let d = nx + boxwidth - maxx;
189 if (!$elem.data('attach-bottom')) {
190 ny = y - $elem.height() - 10;
197 $('.pointer', $elem).css({
203 function closeNoteBox() {
204 $('#annotation-box').data('anchoredTo', null).fadeOut();
206 $(document).on('click', function(event) {
207 let t = $(event.target);
208 if (t.parents('#annotation-box').length && !t.is('#footnote-link')) {
213 $(window).on('resize', closeNoteBox);
215 function getPositionInBookText($e) {
218 // Ok dla Y, nie ok dla X
220 while ($e.attr('id') != 'book-text') {
221 let p = $e.position();
224 $e = $e.offsetParent();
227 return {"x": x, "y": y}
230 $('#book-text .annotation').on('click', function(event) {
231 if ($(this).parents('#footnotes').length) return;
232 event.preventDefault();
234 href = $(this).attr('href').substr(1);
235 content = $("[name='" + href + "']").next().next().html();
236 if (!content) return;
237 $("#annotation-content").html(content);
238 $("#footnote-link").attr('href', '#' + href)
240 putNoteAt($('#annotation-box'), this);
241 event.stopPropagation();
249 success: function(data) {
251 $.each(zakladki, (i, e) => {
253 // TODO: not just paragraphs.
254 $('[href="#' + e.anchor + '"]').nextAll('.paragraph').first()
260 // TODO: create bookmarks on init
261 // We need to do that from anchors.
263 function zakladkaUpdateFor($item) {
265 let anchor = $item.prevAll('.target').first().attr('name');
267 if (anchor in zakladki) {
268 let $booktag = $item.data('booktag');
271 // TODO: only copy without the dialog.
272 $booktag = $("<div class='zakladka'>");
273 $booktag.append($('.icon', $zakladka).clone());
275 $item.data('booktag', $booktag);
276 $booktag.data('p', $item);
277 $booktag.data('anchor', anchor);
278 $zakladka.after($booktag);
280 zakladkaSetPosition($booktag);
285 if (zakladki[anchor].note) {
286 $z.removeClass('zakladka-exists');
287 $z.addClass('zakladka-note');
289 $z.removeClass('zakladka-note');
290 $z.addClass('zakladka-exists');
293 let $booktag = $item.data('booktag');
295 $item.data('booktag', null);
296 $zakladka.append($("#zakladka-box"));
302 function zakladkaSetPosition($z) {
303 $item = $z.data('p');
304 pos = getPositionInBookText($item);
308 right: ($('#main-text').width() - $('#book-text').width()) / 2,
312 let $zakladka = $('#zakladka');
313 $('#book-text .paragraph').on('mouseover', function() {showMarker($(this));});
314 $('#book-text .verse').on('mouseover', function() {showMarker($(this));});
315 //$.PMarker.showForP(this);
318 function showMarker(p) {
320 // Close the currently tag box when moving to another one.
321 // TBD: Do we want to keep the box open and prevent moving?
322 $("#zakladka-box").hide();
324 let anchor = p.prevAll('.target').first().attr('name');
325 // Don't bother if there's no anchor to use.
328 // Only show tag if there is not already a tag for this p.
329 if (p.data('booktag')) {
332 $zakladka.data('p', p);
333 $zakladka.data('anchor', anchor);
335 // (not needed) zakladkaUpdateClass();
336 // TODO: UPDATE THIS ON OPEN?
337 //let note = anchor in zakladki ? zakladki[anchor].note : '';
338 //$('textarea', $zakladka).val(note);
340 zakladkaSetPosition($zakladka);
345 $(".zakladka-tool_zakladka").on('click', function() {
346 let $z = $("#zakladka-box").data('z');
347 let anchor = $z.data('anchor');
348 let $p = $z.data('p');
352 csrfmiddlewaretoken: csrf,
355 success: function(data) {
356 zakladki[data.anchor] = data;
357 $("#zakladka-box").hide();
359 // Just hide, and create new .zakladka if not already exists?
360 // In general no hiding 'classed' .zakladka.
361 // So the 'cursor' .zakladka doesn't ever need class update.
362 //zakladkaUpdateClass();
363 zakladkaUpdateFor($p);
369 $(".zakladka-tool_notka_text textarea").on('input', function() {
370 // FIXME: no use const $zakladka here, check which .zakladka are we attached to.
371 let $z = $(this).closest('.zakladka');
372 let anchor = $z.data('anchor');
374 $("#notka-saved").hide();
375 //$("#notka-save").show();
377 url: '/zakladki/' + zakladki[anchor].uuid + '/',
379 csrfmiddlewaretoken: csrf,
382 success: function(data) {
383 zakladki[anchor] = data;
384 zakladkaUpdateFor($z.data('p'));
385 $("#notka-save").hide();
386 $("#notka-saved").fadeIn();
391 $(".zakladka-tool_zakladka_delete").on('click', function() {
392 let $z = $(this).closest('.zakladka');
393 let anchor = $z.data('anchor');
395 url: '/zakladki/' + zakladki[anchor].uuid + '/delete/',
397 csrfmiddlewaretoken: csrf,
399 success: function(data) {
400 delete zakladki[anchor];
401 $("#zakladka-box").hide();
402 zakladkaUpdateFor($z.data('p'));
407 $("#main-text").on("click", ".zakladka .icon", function() {
408 let $z = $(this).closest('.zakladka');
409 let $box = $("#zakladka-box");
413 let $p = $z.data('p');
414 let anchor = $z.data('anchor');
415 let note = anchor in zakladki ? zakladki[anchor].note : '';
417 $('.zakladka-tool_zakladka', $box).toggle(!(anchor in zakladki));
418 $('.zakladka-tool_sluchaj', $box).toggle($p.hasClass('syncable')).data('sync', $p.attr('id'));
419 $('textarea', $box).val(note);
429 showForSelection(sel) {
430 // TODO: only consider ranges inside text.?
431 this.selection = sel;
433 // TODO: multiple ranges.
434 let range = sel.getRangeAt(0);
435 let rect = range.getBoundingClientRect();
437 putNoteAt(this.qbox, range)
440 let rect = b.getBoundingClientRect();
442 putNoteAt(this.qbox, b, 'left')
445 this.qbox.data('anchoredTo', null);
449 this.qbox.data('anchoredTo', null);
450 this.qbox.addClass('copied').fadeOut(1500, () => {
451 this.qbox.removeClass('copied');
456 // TODO: only consider ranges inside text.?
457 let range = this.selection.getRangeAt(0);
458 let e = range.startContainer;
459 let anchor = getIdForElem(e);
460 let text = window.location.protocol + '//' +
461 window.location.host +
462 window.location.pathname;
464 navigator.clipboard.writeText(
465 this.selection.toString() +
466 '\n\nCałość czytaj na: ' + text
471 // TODO: only consider ranges inside text.?
472 let range = this.selection.getRangeAt(0);
473 let e = range.startContainer;
474 let anchor = getIdForElem(e);
475 let text = window.location.protocol + '//' +
476 window.location.host +
477 window.location.pathname;
478 if (anchor) text += '#' + anchor;
479 navigator.clipboard.writeText(text);
484 // What aboot non-contiguous selections?
485 let sel = this.selection;
486 let textContent = sel.toString();
487 let anchor = getIdForElem(sel.getRangeAt(0).startContainer);
488 let paths = getSelectionPaths(sel);
492 csrfmiddlewaretoken: csrf,
500 success: function (data) {
501 var win = window.open('/cytaty/' + data.uuid + '/', '_blank');
508 let qbox = new QBox($("#qbox"));
511 function getPathToNode(elem) {
514 while (elem.id != 'book-text') {
515 let p = elem.parentElement;
516 path.unshift([...p.childNodes].indexOf(elem))
521 function getSelectionPaths(selection) {
523 let range1 = selection.getRangeAt(0);
524 let range2 = selection.getRangeAt(selection.rangeCount - 1);
526 getPathToNode(range1.startContainer) + [range1.startOffset],
527 getPathToNode(range2.endContainer) + [range2.endOffset]
533 function getIdForElem(elem) {
536 // check if inside book-text?
539 if ($elem.hasClass('target')) {
540 return $elem.attr('name');
546 // Gdy wychodzimy w górę -- to jest ten moment, w którym znajdujemy element od którego wychodzimy i zliczamy znaki.
560 function getIdForElem(elem) {
562 // check if inside book-text?
565 if ($elem.hasClass('target')) {
566 return $elem.attr('name');
584 function positionToIIDOffset(container, offset) {
585 // Container and offset follow Range rules.
586 // If container is a text node, offset is text offset.
587 // If container is an element node, offset is number of child nodes from container start.
588 // (containers of type Comment, CDATASection - ignore)z
592 function updateQBox() {
593 sel = document.getSelection();
595 if (sel.isCollapsed || sel.rangeCount < 1) {
602 qbox.showForSelection(sel);
605 $(document).on('selectionchange', updateQBox);
607 function updateBoxes() {
608 updateNote(qbox.qbox);
609 updateNote($('#annotation-box'));
612 $(window).on('scroll', updateBoxes);
613 $(window).on('resize', updateBoxes);
616 $(window).on('resize', function() {
617 $('.zakladka').each(function() {
618 zakladkaSetPosition($(this));
622 $('a.anchor').on('click', function(e) {
623 // Workaround for bad TOC markers.
624 if ($(this).closest('#toc').length) return;
625 if ($(this).closest('.toc').length) return;
626 if ($(this).closest('#wltoc').length) return;
629 let sel = window.getSelection();
630 sel.removeAllRanges();
631 let range = document.createRange();
633 let $p = $(this).nextAll('.paragraph').first()
634 range.selectNodeContents($p[0]);
637 qbox.showForSelection(sel);
644 $('.qbox-t-copy').on('click', function(e) {
648 $('.qbox-t-link').on('click', function(e) {
652 $('.qbox-t-quote').on('click', function(e) {
659 $(".paragraph").on('click', function(e) {
660 qbox.showForBlock(this);
665 function scrollToAnchor(anchor) {
667 var anchor_name = anchor.slice(1);
668 var element = $('a[name="' + anchor_name + '"]');
669 if (element.length > 0) {
671 scrollTop: element.offset().top - 55
675 history.pushState({}, '', anchor);
682 scrollToAnchor(window.location.hash)
683 $('#toc, #themes, #book-text, #annotation, .scroll').on('click', 'a', function(event) {
684 if (scrollToAnchor($(this).attr('href'))) {
685 event.preventDefault();