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;
 
 145             leftoffset = $elem.data('default-leftoffset');
 
 149             $elem.css({right: ''});
 
 151             // Do we need to move away from the left?
 
 158             // Do we need to move away from the right?
 
 159             if (nx + boxwidth > maxx) {
 
 160                 // ACTUALLY CALCULATE STUFF
 
 161                 // if maxx - minx < 470 px -- daj z lewej do prawej i już!
 
 164                 let d = nx + boxwidth - maxx;
 
 165                 //if (leftoffset + d > $elem.width() - 10) d = $elem.width() - leftoffset - 10;
 
 174         if (!$elem.data('attach-bottom')) {
 
 175             ny = y - $elem.height() - 10;
 
 182         $('.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();
 
 224         let x = $(this).width() / 2, y = 0;
 
 226         while (elem.attr('id') != 'book-text') {
 
 227             let p = $(elem).position();
 
 230             elem = elem.parent();
 
 232         href = $(this).attr('href').substr(1);
 
 233         content = $("[name='" + href + "']").next().next().html();
 
 234         if (!content) return;
 
 235         $("#annotation-content").html(content);
 
 236         $("#footnote-link").attr('href', '#' + href)
 
 239         putNoteAt($('#annotation-box'), this);
 
 240         event.stopPropagation();
 
 248         success: function(data) {
 
 250             $.each(zakladki, (i, e) => {
 
 252                     // TODO: not just paragraphs.
 
 253                     $('[href="#' + e.anchor + '"]').nextAll('.paragraph').first()
 
 259     // TODO: create bookmarks on init
 
 260     // We need to do that from anchors.
 
 262     function zakladkaUpdateFor($item) {
 
 264         let anchor = $item.prevAll('.target').first().attr('name');
 
 266         if (anchor in zakladki) {
 
 267             let $booktag = $item.data('booktag');
 
 270                 // TODO: only copy without the dialog.
 
 271                 $booktag = $("<div class='zakladka'>");
 
 272                 $booktag.append($('.icon', $zakladka).clone());
 
 274                 $item.data('booktag', $booktag);
 
 275                 $booktag.data('p', $item);
 
 276                 $booktag.data('anchor', anchor);
 
 277                 $zakladka.after($booktag);
 
 279                 zakladkaSetPosition($booktag);
 
 284             if (zakladki[anchor].note) {
 
 285                 $z.removeClass('zakladka-exists');
 
 286                 $z.addClass('zakladka-note');
 
 288                 $z.removeClass('zakladka-note');
 
 289                 $z.addClass('zakladka-exists');
 
 292             let $booktag = $item.data('booktag');
 
 294                 $item.data('booktag', null);
 
 295                 $zakladka.append($("#zakladka-box"));
 
 301     function zakladkaSetPosition($z) {
 
 302         $item = $z.data('p');
 
 303         pos = getPositionInBookText($item);
 
 307             right: ($('#main-text').width() - $('#book-text').width()) / 2,
 
 311     let $zakladka = $('#zakladka');
 
 312     $('#book-text .paragraph').on('mouseover', function() {showMarker($(this));});
 
 313     $('#book-text .verse').on('mouseover', function() {showMarker($(this));});
 
 314         //$.PMarker.showForP(this);
 
 317     function showMarker(p) {
 
 319         // Close the currently tag box when moving to another one.
 
 320         // TBD: Do we want to keep the box open and prevent moving?
 
 321         $("#zakladka-box").hide();
 
 323         let anchor = p.prevAll('.target').first().attr('name');
 
 324         // Don't bother if there's no anchor to use.
 
 327         // Only show tag if there is not already a tag for this p.
 
 328         if (p.data('booktag')) {
 
 331             $zakladka.data('p', p);
 
 332             $zakladka.data('anchor', anchor);
 
 334             // (not needed) zakladkaUpdateClass();
 
 335             // TODO: UPDATE THIS ON OPEN?
 
 336             //let note = anchor in zakladki ? zakladki[anchor].note : ''; 
 
 337             //$('textarea', $zakladka).val(note);
 
 339             zakladkaSetPosition($zakladka);
 
 344     $(".zakladka-tool_zakladka").on('click', function() {
 
 345         let $z = $("#zakladka-box").data('z');
 
 346         let anchor = $z.data('anchor');
 
 347         let $p = $z.data('p');
 
 351                 csrfmiddlewaretoken: csrf,
 
 354             success: function(data) {
 
 355                 zakladki[data.anchor] = data;
 
 356                 $("#zakladka-box").hide();
 
 358                 // Just hide, and create new .zakladka if not already exists?
 
 359                 // In general no hiding 'classed' .zakladka.
 
 360                 // So the 'cursor' .zakladka doesn't ever need class update.
 
 361                 //zakladkaUpdateClass();
 
 362                 zakladkaUpdateFor($p);
 
 368     $(".zakladka-tool_notka_text textarea").on('input', function() {
 
 369         // FIXME: no use const $zakladka here, check which .zakladka are we attached to.
 
 370         let $z = $(this).closest('.zakladka');
 
 371         let anchor = $z.data('anchor');
 
 373         $("#notka-saved").hide();
 
 374         //$("#notka-save").show();
 
 376             url: '/zakladki/' + zakladki[anchor].uuid + '/',
 
 378                 csrfmiddlewaretoken: csrf,
 
 381             success: function(data) {
 
 382                 zakladki[anchor] = data;
 
 383                 zakladkaUpdateFor($z.data('p'));
 
 384                 $("#notka-save").hide();
 
 385                 $("#notka-saved").fadeIn();
 
 390     $(".zakladka-tool_zakladka_delete").on('click', function() {
 
 391         let $z = $(this).closest('.zakladka');
 
 392         let anchor = $z.data('anchor');
 
 394             url: '/zakladki/' + zakladki[anchor].uuid + '/delete/',
 
 396                 csrfmiddlewaretoken: csrf,
 
 398             success: function(data) {
 
 399                 delete zakladki[anchor];
 
 400                 $("#zakladka-box").hide();
 
 401                 zakladkaUpdateFor($z.data('p'));
 
 406     $("#main-text").on("click", ".zakladka .icon", function() {
 
 407         let $z = $(this).closest('.zakladka');
 
 408         let $box = $("#zakladka-box");
 
 412         let $p = $z.data('p');
 
 413         let anchor = $z.data('anchor');
 
 414         let note = anchor in zakladki ? zakladki[anchor].note : ''; 
 
 416         $('.zakladka-tool_zakladka', $box).toggle(!(anchor in zakladki));
 
 417         $('.zakladka-tool_sluchaj', $box).toggle($p.hasClass('syncable')).data('sync', $p.attr('id'));
 
 418         $('textarea', $box).val(note);
 
 428         showForSelection(sel) {
 
 429             // TODO: only consider ranges inside text.?
 
 430             this.selection = sel;
 
 432             // TODO: multiple ranges.
 
 433             let range = sel.getRangeAt(0);
 
 434             let rect = range.getBoundingClientRect();
 
 436             putNoteAt(this.qbox, range)
 
 439             let rect = b.getBoundingClientRect();
 
 441             putNoteAt(this.qbox, b, 'left')
 
 444             this.qbox.data('anchoredTo', null);
 
 448             this.qbox.data('anchoredTo', null);
 
 449             this.qbox.addClass('copied').fadeOut(1500, () => {
 
 450                 this.qbox.removeClass('copied');
 
 455             // TODO: only consider ranges inside text.?
 
 456             let range = this.selection.getRangeAt(0);
 
 457             let e = range.startContainer;
 
 458             let anchor = getIdForElem(e);
 
 459             let text = window.location.protocol + '//' +
 
 460                 window.location.host +
 
 461                 window.location.pathname;
 
 463             navigator.clipboard.writeText(
 
 464                 this.selection.toString() +
 
 465                     '\n\nCałość czytaj na: ' + text
 
 470             // TODO: only consider ranges inside text.?
 
 471             let range = this.selection.getRangeAt(0);
 
 472             let e = range.startContainer;
 
 473             let anchor = getIdForElem(e);
 
 474             let text = window.location.protocol + '//' +
 
 475                 window.location.host +
 
 476                 window.location.pathname;
 
 477             if (anchor) text += '#' + anchor;
 
 478             navigator.clipboard.writeText(text);
 
 483             // What aboot non-contiguous selections?
 
 484             let sel = this.selection;
 
 485             let textContent = sel.toString();
 
 486             let anchor = getIdForElem(sel.getRangeAt(0).startContainer);
 
 487             let paths = getSelectionPaths(sel);
 
 491                     csrfmiddlewaretoken: csrf,
 
 499                 success: function (data) {
 
 500                     var win = window.open('/cytaty/' + data.uuid + '/', '_blank');
 
 507     let qbox = new QBox($("#qbox"));
 
 510     function getPathToNode(elem) {
 
 513         while (elem.id != 'book-text') {
 
 514             let p = elem.parentElement;
 
 515             path.unshift([...p.childNodes].indexOf(elem))
 
 520     function getSelectionPaths(selection) {
 
 522         let range1 = selection.getRangeAt(0);
 
 523         let range2 = selection.getRangeAt(selection.rangeCount - 1);
 
 525             getPathToNode(range1.startContainer) + [range1.startOffset],
 
 526             getPathToNode(range2.endContainer) + [range2.endOffset]
 
 532     function getIdForElem(elem) {
 
 535         // check if inside book-text?
 
 538             if ($elem.hasClass('target')) {
 
 539                 return $elem.attr('name');
 
 545                 // Gdy wychodzimy w górę -- to jest ten moment, w którym znajdujemy element od którego wychodzimy i zliczamy znaki.
 
 559     function getIdForElem(elem) {
 
 561         // check if inside book-text?
 
 564             if ($elem.hasClass('target')) {
 
 565                 return $elem.attr('name');
 
 583     function positionToIIDOffset(container, offset) {
 
 584         // Container and offset follow Range rules.
 
 585         // If container is a text node, offset is text offset.
 
 586         // If container is an element node, offset is number of child nodes from container start.
 
 587         // (containers of type Comment, CDATASection - ignore)z
 
 591     function updateQBox() {
 
 592         sel = document.getSelection();
 
 594         if (sel.isCollapsed || sel.rangeCount < 1) {
 
 601             qbox.showForSelection(sel);
 
 604     $(document).on('selectionchange', updateQBox);
 
 606     function updateBoxes() {
 
 607         updateNote(qbox.qbox);
 
 608         updateNote($('#annotation-box'));
 
 611     $(window).on('scroll', updateBoxes);
 
 612     $(window).on('resize', updateBoxes);
 
 615     $(window).on('resize', function() {
 
 616         $('.zakladka').each(function() {
 
 617             zakladkaSetPosition($(this));
 
 621     $('a.anchor').on('click', function(e) {
 
 624         let sel = window.getSelection();
 
 625         sel.removeAllRanges();
 
 626         let range = document.createRange();
 
 628         let $p = $(this).nextAll('.paragraph').first()
 
 629         range.selectNode($p[0]);
 
 632         qbox.showForSelection(sel);
 
 639     $('.qbox-t-copy').on('click', function(e) {
 
 643     $('.qbox-t-link').on('click', function(e) {
 
 647     $('.qbox-t-quote').on('click', function(e) {
 
 654     $(".paragraph").on('click', function(e) {
 
 655         qbox.showForBlock(this);
 
 660     function scrollToAnchor(anchor) {
 
 662             var anchor_name = anchor.slice(1);
 
 663             var element = $('a[name="' + anchor_name + '"]');
 
 664             if (element.length > 0) {
 
 666                     scrollTop: element.offset().top - 55
 
 670                         history.pushState({}, '', anchor);
 
 676     scrollToAnchor(window.location.hash)
 
 677     $('#toc, #themes, #book-text, #annotation').on('click', 'a', function(event) {
 
 678         event.preventDefault();
 
 679         scrollToAnchor($(this).attr('href'));