MediaWiki:Common.js

From DiVersions
Revision as of 10:55, 3 December 2020 by Gijs (talk | contribs)
Jump to navigation Jump to search

Note: After saving, you may have to bypass your browser's cache to see the changes.

  • Firefox / Safari: Hold Shift while clicking Reload, or press either Ctrl-F5 or Ctrl-R (⌘-R on a Mac)
  • Google Chrome: Press Ctrl-Shift-R (⌘-Shift-R on a Mac)
  • Internet Explorer: Hold Ctrl while clicking Refresh, or press Ctrl-F5
  • Opera: Go to Menu → Settings (Opera → Preferences on a Mac) and then to Privacy & security → Clear browsing data → Cached images and files.
var API_URL = 'https://diversions.constantvzw.org/wiki/api.php';

/**
 * Make an api url with the given parameters
 * @param {object} parameters parameters for the url
 */
function makeApiUrl(parameters) {
  var params = new URLSearchParams();

  for (k in parameters) {
    params.append(k, parameters[k]);
  }

  return API_URL + '?' + params.toString();
}

/**
 * Fetches the given url, parses data as json
 * @param {string} url url to fetch
 */
function getJSON (url) {
  return new Promise(function (resolve, reject) {
    fetch(url, {
      method: "GET"
    }).then(function (response) {
      if (response.ok) {
        response.json().then(resolve); //.catch(reject);
      }
      else {
        reject();
      }
    });//.catch(reject);
  });
}



/**
 * Fetches a list of revisions from the wiki api
 * Returns a promise
 * Promise is revolved with a list of revions:
 * Array<Shape<
 *  comment: string
​ *  parentid: integer
​ *  revid: integer
​​ *  timestamp: string
​​ *  user: string
 * >>
 * @param {string} title Title of the lemma
 */
function getRevisions (title) {
  return new Promise(function (resolve, reject) {
    getJSON(makeApiUrl({
      action: 'query',
      prop: 'revisions',
      rvslots: '*',
      rvprop: ['timestamp', 'user', 'comment', 'ids'].join('|'),
      rvlimit: 500,
      format: 'json',
      'titles': title
    })).then(function (data) {
      try {
        var pages = data['query']['pages'],
            keys = Object.keys(pages),
            page = pages[keys[0]],
            revisions = page['revisions'];

        resolve(revisions);
      }
      catch(e) {
        reject(e);
      }
    });
  });
}

/**
 * Fetches a parsed revision from the api
 * Returns a promise
 * Promise is resolved with a dictionary containing the text
 * and display title of the lemma.
 * @param {int} revid Id of the revision 
 */
function getRevision (revid) {
  return new Promise(function (resolve, reject) {
    getJSON(makeApiUrl({
      action: 'parse',
      oldid: revid,
      format: 'json'
    })).then(function (data) {
      try {
        var title = data['parse']['displaytitle'],
            text = Object.values(data['parse']['text'])[0];
        resolve({ title: title, html: text });
      } catch (e) {
        reject(e);
      }
    });
  });
}

function makeTimelineEntry (revision) {

  var entry = document.createElement('section');
  entry.classList.add('timeline--entry');
  entry.dataset.revid = revision.revid;
  
  var entryData = document.createElement('section');
  entryData.classList.add('timeline--entry--data');

  var user = document.createElement('span');
  user.classList.add('timeline--user');
  user.append(document.createTextNode(revision.user));

  var timestamp = document.createElement('span');
  timestamp.classList.add('timeline--timestamp');
  timestamp.append(document.createTextNode(revision.timestamp));

  var comment = document.createElement('span');
  comment.classList.add('timeline--comment');
  comment.append(document.createTextNode(revision.comment));

  entryData.append(user, timestamp, comment);
  entry.append(entryData);

  entry.addEventListener('click', function () {
    showRevision(revision.revid);
  })

  return entry;
}

function makeRevision (revid) {
  var revision = document.createElement('section');
  revision.classList.add('revisions--revision', 'mw-body-content');
  revision.dataset.revid = revid;
  return revision;
}

// Maybe rename?
function makeRevisionStubs (revisions) {
  var container = document.getElementById('content');
  // container.classList.add('revisions');

  for (var i = 1; i < revisions.length; i++) {
    container.append(makeRevision(revisions[i].revid));
  }
}

function makeTimeline (revisions) {
  var timeline = document.createElement('section');
  timeline.classList.add('timeline');
  
  for (var i=0; i < revisions.length; i++) {
    if (i==0) {
      revisions[i].revid = 'current';
    }
    timeline.appendChild(makeTimelineEntry(revisions[i]));
  }

  return timeline;
}

function showRevision (revid) {
  var node = document.querySelector('.revisions--revision[data-revid="'+ revid + '"]'),
      current = document.querySelector('.timeline--entry[data-revid="'+ revid + '"]'),
      previous = document.querySelector('.timeline--entry.active');
  if (previous) {
    previous.classList.remove('active');
  }

  current.classList.add('active');

  node.scrollIntoView();
  if (! node.dataset.loaded) {
    node.dataset.loading = true;
    getRevision(revid).then(function (revision) {
      delete node.dataset.loading;
      node.dataset.loaded = true;
      node.innerHTML = revision.html;
      showRevision(revid);
    });
  }
}



/**
 * Transforms the content content div into the container for the revisions.
 */
function restructurePage () {
  var contentContainer = document.getElementById('content'),
      panel = makeRevision('current');

  panel.dataset.loaded = true;

  while(contentContainer.firstChild) {
    if (contentContainer.firstChild.classList) {
      contentContainer.firstChild.classList.remove('mw-body');
    }
    panel.appendChild(contentContainer.firstChild);
  }

  contentContainer.classList.add('revisions');
  contentContainer.appendChild(panel);
}

function wrap (element, maxWidth, end) {
  if (maxWidth === null) {
    maxWidth = 40;
  }

  if (end === null) {
    end = ' \\';
  }

  var lines = element.textContent.split("\n"),
      wrapped = [];

  for (var l=0; l < lines.length; l++) {
    if (lines[l].length > maxWidth) {
      var line = lines[l];
      wrapped.push(line.substring(0, maxWidth).replace(/\s/g, '&nbsp;'));
      line = line.substring(maxWidth);

      do {
        wrapped.push((end + line.substring(0, maxWidth - end.length).trim()).replace(/\s/g, '&nbsp;'));
        line = line.substring(maxWidth - end.length);
      } while (line.length > 0);
    }
    else {
      wrapped.push(lines[l].replace(/\s/g, '&nbsp;'))
    }
  }

  var codeElement = document.createElement('code');
  codeElement.innerHTML = '<span class="line">' + wrapped.join('</span><span class="line">') + '</span>';
  element.replaceWith(codeElement);
}

function unWikifyReferences () {
  var links = document.querySelectorAll('a');

  for (var i = 0; i < links.length; i++) {
    var a = links[i];

    if (a.href.includes('#cite_note')) {
      var content = a.textContent;

      if (content.startsWith('[') && content.endsWith(']')) {
        a.textContent = content.substring(1, content.length - 1);
      }
    }
  }
}

(function () {
  return;
  var urlParameters = new URLSearchParams(window.location.search),
      title = urlParameters.get('title'),
      paged = urlParameters.get('paged');


  if (!paged || paged != 'paged') {
    restructurePage();

    if (title) {
      getRevisions(title).then(function (revisions) {
        var container = document.getElementById('content');
      
        container.parentElement.appendChild(makeTimeline(revisions));
      
        for (var i = 0; i < revisions.length; i++) {
          container.append(makeRevision(revisions[i].revid));
        }

        showRevision('current');
      });
    }
  }
})();

(function() {

/** 
* Heavily of tosser (convert calc expresiion to mm): https://github.com/shyndman/tosser/
*/

/**
 * The MIT License (MIT)
 * 
 * Copyright (c) 2015 Scott Hyndman
 * 
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 * 
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
*/

'use strict';
var referenceElement;
var units = {
      'mm': (72/25.4)*(96/72), // 25.4mm = 72pt
      'cm': (72/2.54)*(96/72),
      'pt': (96/72),
      'in': 96,
      'px': 1 
    },
    calcPattern = /calc\s*\(/g,
    unitReplacementPattern = /(\d+(?:\.\d+)?)(mm|cm|in|pt|px)/gi,
    varReplacementPattern = /var\((.+?)\)/g;

/**
 * Evaluates a calc expression.
 *
 * @param {string} expression A calc expression.
 * @return {string} The result of the expression, in px units.
 */
function evaluateCalc (expression) {
  expression = expression
      .trim()
      .replace(/^calc/, '')
      .replace(unitReplacementPattern, function(m, v, unit) {
        return parseFloat(v) * units[unit];
      });

  return eval(expression);
};

function evaluateCalcs (input) {
  if (/calc\s*\(/g.test(input)) { // Test whether there is a calc( in the string
    var rawChunks = input.split(calcPattern),
        chunks = [rawChunks.shift()]; // Add anything untill first calc expression

    for (var i=0; i < rawChunks.length; i++) {
      var chunk = rawChunks[i],
          pos = chunk.lastIndexOf(')'),
          // find last parenthesis in the string. To balance the one removed with
          // the split. Anything before will be parsed / evaluated to mm's every-
          // thing after will be added as a tail.
          body = evaluateCalc(chunk.substr(0, pos)),
          tail = (pos + 1 < chunk.length) ? chunk.substr(pos+1) : "";

      chunks.push(body+tail);
    }

    return chunks.join('');
  } else {
    return input;
  }
}

function fixUnits (input) {
  return input.replace(unitReplacementPattern, function(m, v, unit) {
    return parseFloat(v) * units[unit];
  });
}

/**
 * Lookup declaration of given css lookupCssVariable
 * 
 * https://www.broken-links.com/2014/08/28/css-variables-updating-custom-properties-javascript/
 */
function lookupCSSVariable (name) {
    return window.getComputedStyle(referenceElement).getPropertyValue(name);
}

function evaluateCSSVariables (input) {
  while (varReplacementPattern.test(input)) {
    input = input.replace(varReplacementPattern, function (m, name) { return evaluateCalcs(lookupCSSVariable(name).trim()); });
  }

  return input;
}

function cssValueToPx (cssValue, element) {
  referenceElement = (element) ? element : document.body;
  return parseFloat(fixUnits(evaluateCalcs(evaluateCSSVariables(cssValue.trim()))));
}

window.cssValueToPx = cssValueToPx;
})();



/**
- Get revisions
- Generate the timeline
  <section>
    <section class="timeline--entry" data-revision="{data}" data-time="{time}">
      <span class="timeline--user">{ user }</span>
      <span class="timeline--timestamp">{ timestamp }</span>
      <span class="timeline--comment">{ comment }</span>
    </section>
  </section>
- Generate the panels, maybe do not render them all
- <section data-revision="{revision}"></section>

*/

(function () {
  var urlParameters = new URLSearchParams(window.location.search),
      paged = urlParameters.get('paged');
      prevent = urlParameters.get('prevent');
      etherstyles = urlParameters.get('etherstyles');
  if (paged == 'paged' && prevent !== 'prevent') {
    /*
    var pre = document.querySelectorAll('pre');
    while (pre.length > 0) {
      wrap(pre[0], 50, '                        ');
      pre = document.querySelectorAll('pre');
    }
    */

    unWikifyReferences();
    /* mw.loader.load('https://unpkg.com/pagedjs@0.1.40/dist/paged.polyfill.js'); */
    /* mw.loader.load('https://unpkg.com/pagedjs@0.1.33/dist/paged.polyfill.js'); */
    /* mw.loader.load('https://unpkg.com/pagedjs@0.1.28/dist/paged.polyfill.js'); */
    
    const pagedPreviewCSS = ":root{--color-background:whitesmoke;--color-pageBox:#666;--color-paper:white;--color-marginBox:transparent}@media screen{body{background-color:var(--color-background)}.pagedjs_pages{display:flex;width:calc(var(--pagedjs-width) * 2);flex:0;flex-wrap:wrap;margin:0 auto}.pagedjs_page{background-color:var(--color-paper);box-shadow:0 0 0 1px var(--color-pageBox);margin:0;flex-shrink:0;flex-grow:0;margin-top:10mm}.pagedjs_first_page{margin-left:var(--pagedjs-width)}.pagedjs_page:last-of-type{margin-bottom:10mm}.pagedjs_margin-bottom,.pagedjs_margin-bottom-center,.pagedjs_margin-bottom-left,.pagedjs_margin-bottom-left-corner-holder,.pagedjs_margin-bottom-right,.pagedjs_margin-bottom-right-corner-holder,.pagedjs_margin-left,.pagedjs_margin-left-bottom,.pagedjs_margin-left-middle,.pagedjs_margin-left-top,.pagedjs_margin-right,.pagedjs_margin-right-bottom,.pagedjs_margin-right-middle,.pagedjs_margin-right-top,.pagedjs_margin-top,.pagedjs_margin-top-center,.pagedjs_margin-top-left,.pagedjs_margin-top-left-corner-holder,.pagedjs_margin-top-right,.pagedjs_margin-top-right-corner-holder{box-shadow:0 0 0 1px inset var(--color-marginBox)}}";
    previewStyles = document.createElement('style');
    previewStyles.textContent = pagedPreviewCSS;
    document.head.appendChild(previewStyles);
 
    if (etherstyles == 'etherstyles') {
      window.setTimeout(function () {
        window.PagedConfig={auto:false};
        jQuery.getScript('https://unpkg.com/pagedjs/dist/paged.polyfill.js').then(function () {


function snapToGrid (node) {
  var lineHeight = cssValueToPx('var(--line-height)', node),
      height = node.getBoundingClientRect().height,
      newDesiredHeight = Math.ceil(height/lineHeight) * lineHeight,
      style = window.getComputedStyle(node),
      borderTop = cssValueToPx(style.borderTopWidth, node),
      borderBottom = cssValueToPx(style.borderBottomWidth, node),
      paddingTop = cssValueToPx(style.paddingTop, node),
      paddingBottom = cssValueToPx(style.paddingBottom, node);

  node.style.height = (newDesiredHeight - (borderTop + borderBottom + paddingTop + paddingBottom)) + 'px'
}


var classElemFloat = 'elem-float-top';

////////////////////////////////////////////////////////////////

function _instanceof(left, right) { if (right != null && typeof Symbol !== "undefined" && right[Symbol.hasInstance]) { return !!right[Symbol.hasInstance](left); } else { return left instanceof right; } }

function _typeof(obj) { "@babel/helpers - typeof"; if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); }

function _classCallCheck(instance, Constructor) { if (!_instanceof(instance, Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } }

function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; }

function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function"); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, writable: true, configurable: true } }); if (superClass) _setPrototypeOf(subClass, superClass); }

function _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); }

function _createSuper(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct(); return function _createSuperInternal() { var Super = _getPrototypeOf(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = _getPrototypeOf(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return _possibleConstructorReturn(this, result); }; }

function _possibleConstructorReturn(self, call) { if (call && (_typeof(call) === "object" || typeof call === "function")) { return call; } return _assertThisInitialized(self); }

function _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return self; }

function _isNativeReflectConstruct() { if (typeof Reflect === "undefined" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === "function") return true; try { Date.prototype.toString.call(Reflect.construct(Date, [], function () {})); return true; } catch (e) { return false; } }

function _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return _getPrototypeOf(o); }

var elemFloatTop = /*#__PURE__*/function (_Paged$Handler) {
  _inherits(elemFloatTop, _Paged$Handler);

  var _super = _createSuper(elemFloatTop);

  function elemFloatTop(chunker, polisher, caller) {
    var _this;

    _classCallCheck(this, elemFloatTop);

    _this = _super.call(this, chunker, polisher, caller);
    _this.floatPageEls = [];
    _this.token;
    return _this;
  }

  _createClass(elemFloatTop, [{
    key: "layoutNode",
    value: function layoutNode(node) {
      // If you find a float page element, move it in the array,
      if (node.nodeType == 1 && node.classList.contains(classElemFloat) && node.style.display != "none") {
        var clone = node.cloneNode(true);
        this.floatPageEls.push(clone); // Remove the element from the flow by hiding it.

        node.style.display = "none";
      }
    }
  }, {
    key: "beforePageLayout",
    value: function beforePageLayout(page, content, breakToken) {
      // If there is an element in the floatPageEls array,
      if (this.floatPageEls.length >= 1) {
        // Put the first element on the page.
        var inserted = page.element.querySelector(".pagedjs_page_content").insertAdjacentElement('afterbegin', this.floatPageEls[0]);
        if (inserted.contains('snap')) {
          snapToGrid(inserted);
        }
        this.floatPageEls.shift();
      }
    }
  }]);

  return elemFloatTop;
}(Paged.Handler);


///////////////////////////////////////////////////////////////

Paged.registerHandlers(elemFloatTop);

var links = document.querySelectorAll('a');
for (var i=0;i<links.length;i++){
  try {
    var url = new URL(links[i].href);
    if (links[i].parentNode.parentNode.classList.contains('nav')) {
      continue;
    }

    if (url.hash && url.hash != '#' && !url.hash.startsWith('#cite')) {
      links[i].href = url.hash;
      links[i].dataset.wordLink = true;
    }
  } catch (e) {
    console.log(e);
    console.log(links[i]);
  }
}

window.PagedPolyfill.preview();
        });
      }, 4000);
    } else {
      mw.loader.load('https://unpkg.com/pagedjs/dist/paged.polyfill.js');
    }
  }
})();

function loadEtherStyle(url, interval) {
  function load(url) {
    fetch(url)
      .then(function(r) { return r.text()})
      .then(function (styles) {
        var el = document.createElement('style');
        el.appendChild(document.createTextNode(styles));
        el.setAttribute('data-type', 'etherstyle');
        document.head.appendChild(el);
        window.requestAnimationFrame(function () {
          var styles = document.querySelectorAll('[data-type="etherstyle"]');
          for (var i=0;i<(styles.length-1);i++) {
            styles[i].remove();
          }
        });
      });
  }

  load(url);
  var urlParameters = new URLSearchParams(window.location.search),
      reload = urlParameters.get('reload');
  if (reload == 'reload') {
    window.setInterval(function () { load(url); }, interval);
  }
}

(function () {
  var urlParameters = new URLSearchParams(window.location.search),
      etherstyles = urlParameters.get('etherstyles');
  if (etherstyles == 'etherstyles') {
    loadEtherStyle('https://pad.constantvzw.org/p/diversions.css/export/txt', 7500);
  }
})();