Jump to content

MediaWiki:Common.js: Difference between revisions

From Grantha
No edit summary
No edit summary
Line 1: Line 1:
/* =========================
/* Grantha.io — chapter nav scroll-spy */
  VERSE ACTIONS
(function () {
========================= */
    'use strict';
( function () {
  'use strict';


  /* ── Safe delegated listener ─────────────────────────────────────────── */
    function initScrollSpy() {
  function onDocClick( e ) {
        var nav = document.querySelector('.gr-chapter-nav');
    var target = e.target;
        if (!nav) return;


    // Walk up to find a matching action button
         var links = Array.from(nav.querySelectorAll('a')).filter(function (a) {
    function closest( el, cls ) {
            return a.href && a.href.indexOf('#') !== -1;
      while ( el && el !== document ) {
         });
        if ( el.classList && el.classList.contains( cls ) ) return el;
         if (!links.length) return;
         el = el.parentNode;
      }
      return null;
    }
 
    /* ── Commentary toggle ── */
var commentBtn = closest( target, 'verse-action-commentary' );
if ( commentBtn ) {
  e.preventDefault();
  var verseId = commentBtn.getAttribute( 'data-verse' );
  if ( !verseId ) return;
 
  // Match all commentary bodies for this verse by id prefix
  var allBodies = document.querySelectorAll( '[id^="commentary-body-' + verseId + '"]' );
  var allBtns  = document.querySelectorAll( '.verse-action-commentary[data-verse="' + verseId + '"]' );
 
  var isOpen = allBodies.length && allBodies[0].classList.contains( 'open' );
 
  // Close all open commentaries on the page first
  document.querySelectorAll( '.commentary-body.open' ).forEach( function ( el ) {
    el.classList.remove( 'open' );
  } );
  document.querySelectorAll( '.verse-action-commentary.active' ).forEach( function ( el ) {
    el.classList.remove( 'active' );
  } );
 
  // If it was closed, open it
  if ( !isOpen ) {
    allBodies.forEach( function ( el ) { el.classList.add( 'open' ); } );
    allBtns.forEach(  function ( el ) { el.classList.add( 'active' ); } );
  }
  return;
}
 
    /* ── Copy verse — copies verse ID for crosslinking ── */
var copyBtn = closest( target, 'verse-action-copy' );
if ( copyBtn ) {
  e.preventDefault();
  var verseId = copyBtn.getAttribute( 'data-verse' );
  if ( !verseId ) return;
  copyText( verseId, copyBtn );
  return;
}
    /* ── Copy ID ── */
    var idBtn = closest( target, 'copy-id-btn' );
    if ( idBtn ) {
      e.preventDefault();
      var id = idBtn.getAttribute( 'data-copyid' );
      if ( !id ) return;
      copyText( id, idBtn );
      return;
    }
  }
 
  document.addEventListener( 'click', onDocClick );
 
  /* ── Copy helper + tooltip ───────────────────────────────────────────── */
  function copyText( text, btn ) {
    function showTooltip() {
      // Remove stale tooltip if double-clicked
      var old = btn.querySelector( '.copy-tooltip' );
      if ( old ) old.remove();
 
      btn.style.position = 'relative';
      var tip = document.createElement( 'span' );
      tip.className  = 'copy-tooltip';
      tip.textContent = 'Copied ✓';
      btn.appendChild( tip );
 
      requestAnimationFrame( function () {
        requestAnimationFrame( function () {
          tip.classList.add( 'copy-tooltip-visible' );
         } );
      } );
 
      setTimeout( function () {
         tip.classList.remove( 'copy-tooltip-visible' );
        setTimeout( function () { tip.remove(); }, 400 );
      }, 1400 );
    }
 
    if ( navigator.clipboard && window.isSecureContext ) {
      navigator.clipboard.writeText( text ).then( showTooltip );
    } else {
      var ta = document.createElement( 'textarea' );
      ta.value = text;
      ta.style.cssText = 'position:fixed;opacity:0;pointer-events:none;';
      document.body.appendChild( ta );
      ta.select();
      document.execCommand( 'copy' );
      document.body.removeChild( ta );
      showTooltip();
    }
  }
 
}() );
function openCreateDialog() {
 
  var overlay = document.createElement('div');
  overlay.className = 'grantha-modal';
 
  overlay.innerHTML = `
    <div class="grantha-modal-box">
     
      <div class="gm-header">
        <div class="gm-title">New document</div>
        <div class="gm-sub">Create a new text</div>
      </div>
 
      <input
        type="text"
        id="gm-input"
        placeholder="Untitled document"
        autofocus
      />
 
      <div class="gm-actions">
        <button class="gm-btn gm-cancel">Cancel</button>
        <button class="gm-btn gm-create">Create</button>
      </div>
 
    </div>
  `;
 
  document.body.appendChild(overlay);
 
  var input = overlay.querySelector('#gm-input');
  input.focus();
 
  // Cancel
  overlay.querySelector('.gm-cancel').onclick = function () {
    overlay.remove();
  };


  // Create
        var targets = links.map(function (a) {
  overlay.querySelector('.gm-create').onclick = function () {
            var id = a.href.split('#')[1];
    var name = input.value.trim();
            return id ? document.getElementById(id) : null;
    if (!name) return;
        });


    name = name.replace(/\s+/g, '_');
        function update() {
            /* Try all possible scroll containers — window, html, body,
              and MW-specific wrappers */
            var scrollY = window.scrollY
                || document.documentElement.scrollTop
                || document.body.scrollTop
                || 0;
            var threshold = scrollY + 130;
            var active = 0;
            for (var i = 0; i < targets.length; i++) {
                if (targets[i]) {
                    /* offsetTop walks up to get absolute position */
                    var el = targets[i];
                    var top = 0;
                    while (el) { top += el.offsetTop; el = el.offsetParent; }
                    if (top <= threshold) active = i;
                }
            }
            links.forEach(function (a, i) {
                a.style.color = (i === active) ? '#f57c00' : '';
                a.style.fontWeight = (i === active) ? '600' : '';
            });
        }


    window.location.href = mw.util.getUrl(name, { action: 'edit' });
        /* Attach to every possible scroll source */
  };
        window.addEventListener('scroll', update, { passive: true });
        document.addEventListener('scroll', update, { passive: true });
        var mwContent = document.getElementById('mw-content-text')
            || document.getElementById('content')
            || document.getElementById('bodyContent');
        if (mwContent) {
            mwContent.addEventListener('scroll', update, { passive: true });
        }


  // Enter key support
        update();
  input.addEventListener('keydown', function (e) {
    if (e.key === 'Enter') {
      overlay.querySelector('.gm-create').click();
     }
     }
  });


  // Click outside to close
    mw.hook('wikipage.content').add(function () {
  overlay.onclick = function (e) {
        /* Small delay to let MW finish rendering large pages */
    if (e.target === overlay) overlay.remove();
        setTimeout(initScrollSpy, 300);
  };
    });
}
}());

Revision as of 09:46, 13 April 2026

/* Grantha.io — chapter nav scroll-spy */
(function () {
    'use strict';

    function initScrollSpy() {
        var nav = document.querySelector('.gr-chapter-nav');
        if (!nav) return;

        var links = Array.from(nav.querySelectorAll('a')).filter(function (a) {
            return a.href && a.href.indexOf('#') !== -1;
        });
        if (!links.length) return;

        var targets = links.map(function (a) {
            var id = a.href.split('#')[1];
            return id ? document.getElementById(id) : null;
        });

        function update() {
            /* Try all possible scroll containers — window, html, body,
               and MW-specific wrappers */
            var scrollY = window.scrollY
                || document.documentElement.scrollTop
                || document.body.scrollTop
                || 0;
            var threshold = scrollY + 130;
            var active = 0;
            for (var i = 0; i < targets.length; i++) {
                if (targets[i]) {
                    /* offsetTop walks up to get absolute position */
                    var el = targets[i];
                    var top = 0;
                    while (el) { top += el.offsetTop; el = el.offsetParent; }
                    if (top <= threshold) active = i;
                }
            }
            links.forEach(function (a, i) {
                a.style.color = (i === active) ? '#f57c00' : '';
                a.style.fontWeight = (i === active) ? '600' : '';
            });
        }

        /* Attach to every possible scroll source */
        window.addEventListener('scroll', update, { passive: true });
        document.addEventListener('scroll', update, { passive: true });
        var mwContent = document.getElementById('mw-content-text')
            || document.getElementById('content')
            || document.getElementById('bodyContent');
        if (mwContent) {
            mwContent.addEventListener('scroll', update, { passive: true });
        }

        update();
    }

    mw.hook('wikipage.content').add(function () {
        /* Small delay to let MW finish rendering large pages */
        setTimeout(initScrollSpy, 300);
    });
}());