/*

Script for the main frame.
Originally an adaptation of (darkfish.js by Michael Granger)

*/

document.addEventListener(‘DOMContentLoaded’, function () {

const leftContainer = document.getElementById('left-container');
const leftFrame = document.getElementById('left-frame');
const resizer = document.getElementById('resizer');

setupTOC();
setupShowSource();
setupShowConstantValue();
setupShowAllFiles();
setupInnerLinksHighlight();
highlightUrlHash();

// --- resizing of the left frame

resizer.addEventListener('mousedown', startWidthResize);

let startX;       // where drag begins
let startWidth;   // width of left frame when drag begins
function startWidthResize(e) {
  startX = e.clientX;
  startWidth = leftContainer.getBoundingClientRect().width;
  document.documentElement.addEventListener('mousemove', doWidthResize);
  document.documentElement.addEventListener('mouseup', stopWidthResize);
  leftFrame.contentDocument.addEventListener('mousemove', doWidthResize);
  leftFrame.contentDocument.addEventListener('mouseup', stopWidthResize);
}

function stopWidthResize(e) {
  document.documentElement.removeEventListener('mousemove', doWidthResize);
  document.documentElement.removeEventListener('mouseup', stopWidthResize);
  leftFrame.contentDocument.removeEventListener('mousemove', doWidthResize);
  leftFrame.contentDocument.removeEventListener('mouseup', stopWidthResize);
}

function doWidthResize(e) {
  const dX = e.clientX - startX;
  const newWidth = startWidth + dX;
  if (newWidth > 100)
    leftContainer.style.width = `${newWidth}px`;
}

// --- TOC button setup

function setupTOC() {

  const nav = document.createElement('nav');
  nav.innerHTML = `
    <button id="menu-button">
      <img class="icon-hamburger">
      <img class="icon-cross">
    </button>
    <div id="menu-content" class="hidden">
      <p id="menu-top">
        <a href="#header">(top)</a>
      </p>
      <div id="toc-content">
      </div>
    </div>
  `;
  const main = document.getElementById('main-container');
  const doc = document.getElementById('documentation');
  main.insertBefore(nav, doc);

  const menu_button = document.getElementById('menu-button');
  const menu_content = document.getElementById('menu-content');
  const toc_content = document.getElementById('toc-content');

  function createTOC() {
    const headings = document.body.querySelectorAll('h1, h2, h3, h4, h5, h6');
    if (headings.length == 0)
      return false;
    let generated_id_index = 0;
    const first_summary_heading = firstSummaryHeading();
    // console.log(`${headings.length} headings`);
    for (const h of headings) {
      // console.log(`name = ${h.tagName} id=${h.id} text = ${h.innerText}`);
      if (!h.id) {
        generated_id_index++;
        h.setAttribute('id', `toc-auto-id-${generated_id_index}`);
      }
      if (h === first_summary_heading) {
        const sp = document.createElement('p');
        toc_content.appendChild(sp);
        sp.setAttribute('class', 'h2');
        sp.textContent = 'Summary';
      }
      // <p class="toc2"><a href="#label-News">News</a></p>
      const p = document.createElement('p');
      toc_content.appendChild(p);
      p.setAttribute('class', h.tagName.toLowerCase());
      const a = document.createElement('a');
      p.appendChild(a);
      a.setAttribute('href', `#${h.id}`);
      a.textContent = h.innerText; // h.textContent;
    }

    return true;
  }

  function firstSummaryHeading() {
    const ids = [
      'class-aliases',
      'method-list',
      'namespace-list',
      'include-list',
      'constant-list',
      'external-aliases',
      'class-attributes',
      'instance-attributes',
    ];
    for (const id of ids) {
      const h = document.querySelector(`#${id} h3`);
      if (h) return h;
    }
    return null;
  }

  if (!createTOC())
    nav.classList.add('hidden');

  // show/hide the menu/toc
  menu_button.addEventListener('click', function(e) {
    e.preventDefault();
    toggleMenu();
  });

  // hide the menu on Escape
  document.addEventListener('keydown', function(e) {
    if (!isMenuVisible()) return;
    if (e.code == 'Escape' && !e.ctrlKey) {
      e.preventDefault();
      hideMenu();
    }
  });

  // hide the menu when clicking outside of nav
  document.addEventListener('click', function (event) {
    if (!isMenuVisible()) return;
    if (!nav.contains(event.target))
      hideMenu();
  });

  function hideMenu() {
    menu_content.classList.add('hidden');
    menu_button.classList.remove('show-close');
  }

  function showMenu() {
    menu_content.classList.remove('hidden');
    menu_button.classList.add('show-close');
  }

  function isMenuVisible() {
    return !menu_content.classList.contains('hidden');
  }

  function toggleMenu() {
    if (isMenuVisible())
      hideMenu();
    else
      showMenu();
  }

}

// --- toggle method source display

function setupShowSource() {
  for (const node of document.querySelectorAll('.method-heading'))
    node.addEventListener('click', toggleSource);
  // <div id="method-c-_httpdate" class="method-detail">
  //   <div class="method-heading">
  //     ...
  //   </div>
  //   <div class="method-description">
  //     ...
  //   <pre class="method-source-code">
  //     (code)
  //   </pre>
}

function toggleSource(e) {
  //TODO not when clicking a link
  const source = e.currentTarget.parentNode.querySelector('.method-source-code');
  if (source)
    slideToggle(source);
}

// --- toggle constant value display

function setupShowConstantValue() {
  for (const node of document.querySelectorAll('#constant-list .const-display'))
    node.addEventListener('click', toggleValue);
  // <tr id="MONTHNAMES" class="const-display">
  //   <td class="const-name"><p>MONTHNAMES</p></td>
  //   <td class="const-desc">
  //     <p>An array of strings of full month names in <a href="English.html"><code>English</code></a>.  The first element is nil.</p>
  //   </td>
  // <td><p class="click-advice">click to toggle value</p></td>
  // </tr>
  // <tr class="const-value">
  //   <td></td>
  //   <td><pre>mk_ary_of_str(13, monthnames)</pre></td>
  //   <td></td>
  // </tr>
}

function toggleValue(e) {
  //TODO not when clicking a link
  toggleDisplay(e.currentTarget.nextElementSibling, 'table-row');
}

function toggleDisplay(element, visibleDisplayValue) {
  if (element.style.display !== visibleDisplayValue)
    element.style.display = visibleDisplayValue;
  else
    element.style.display = 'none';
}

// --- toggle the display of all files where the class/module if defined

function setupShowAllFiles() {
  const allFiles = document.getElementById('all-files');
  if (!allFiles) return;
  let skipBodyClick = false;
  document.getElementById('show-all-files').addEventListener('click', function(e) {
    e.preventDefault();
    skipBodyClick = true;
    toggleDisplay(allFiles, 'block');
  });
  document.body.addEventListener('click', function() {
    if (skipBodyClick)
      skipBodyClick = false;
    else if (allFiles.style.display !== 'none')
      allFiles.style.display = 'none';
  });
}

// --- highlight the target method when clicked from an inner method link

function setupInnerLinksHighlight() {
  for (const a of document.querySelectorAll('a[href*="#"]'))
    a.addEventListener('click', highlightTarget);
}

function highlightTarget(e) {
  const href = e.currentTarget.getAttribute('href');
  const match = /#(.*)/.exec(href);
  if (match && match[1].length > 1)
    highlightElement(match[1]);
}

function highlightElement(id) {
  for (const h of document.querySelectorAll('.highlighted'))
    h.classList.remove('highlighted');
  if (id === 'header')
    return;
  const e = document.getElementById(id);
  if (e)
    e.classList.add('highlighted');
}

// --- highlight the method if present in the location hash

function highlightUrlHash() {
  const h = window.location.hash;
  if (h && h.length > 1)
    highlightElement(h.substring(1));
}

// --- smooth toggling (for source code)
// from plain JS slideToggle https://github.com/ericbutler555/plain-js-slidetoggle

function slideToggle(element, duration) {
  if (typeof duration === 'undefined') duration = 400;
  if (element.clientHeight === 0)
    slide(element, duration, true);
  else
    slide(element, duration, false);
}

function slide(el, duration, isDown) {

  el.style.overflow = "hidden";
  if (isDown) el.style.display = "block";

  const elStyle        = window.getComputedStyle(el);

  const elHeight        = parseFloat(elStyle.getPropertyValue('height'));
  const elPaddingTop    = parseFloat(elStyle.getPropertyValue('padding-top'));
  const elPaddingBottom = parseFloat(elStyle.getPropertyValue('padding-bottom'));
  const elMarginTop     = parseFloat(elStyle.getPropertyValue('margin-top'));
  const elMarginBottom  = parseFloat(elStyle.getPropertyValue('margin-bottom'));

  const stepHeight        = elHeight        / duration;
  const stepPaddingTop    = elPaddingTop    / duration;
  const stepPaddingBottom = elPaddingBottom / duration;
  const stepMarginTop     = elMarginTop     / duration;
  const stepMarginBottom  = elMarginBottom  / duration;

  let start;

  function step(timestamp) {

    if (start === undefined) start = timestamp;

    const elapsed = timestamp - start;

    if (isDown) {
      el.style.height        = (stepHeight        * elapsed) + "px";
      el.style.paddingTop    = (stepPaddingTop    * elapsed) + "px";
      el.style.paddingBottom = (stepPaddingBottom * elapsed) + "px";
      el.style.marginTop     = (stepMarginTop     * elapsed) + "px";
      el.style.marginBottom  = (stepMarginBottom  * elapsed) + "px";
    } else {
      el.style.height        = elHeight        - (stepHeight        * elapsed) + "px";
      el.style.paddingTop    = elPaddingTop    - (stepPaddingTop    * elapsed) + "px";
      el.style.paddingBottom = elPaddingBottom - (stepPaddingBottom * elapsed) + "px";
      el.style.marginTop     = elMarginTop     - (stepMarginTop     * elapsed) + "px";
      el.style.marginBottom  = elMarginBottom  - (stepMarginBottom  * elapsed) + "px";
    }

    if (elapsed >= duration) {
      el.style.height        = "";
      el.style.paddingTop    = "";
      el.style.paddingBottom = "";
      el.style.marginTop     = "";
      el.style.marginBottom  = "";
      el.style.overflow      = "";
      if (!isDown) el.style.display = "none";
    } else {
      window.requestAnimationFrame(step);
    }
  }

  window.requestAnimationFrame(step);
}

});