MediaWiki:Mobile.js

From the change wiki
Revision as of 18:43, 18 May 2022 by Elie (talk | contribs) (Created page with "→‎All JavaScript here will be loaded for users of the mobile site: var _calc3 = ''; // string to be sent to calc3.php ajax var _popupLastOpened = null; // element const _popupClasses = ['ppContent','dp','dpVar']; function closePopup() { for (var i=0; i<_popupClasses.length; i++) { e = document.getElementsByClassName(_popupClasses[i]); for (var j=0; j<e.length; j++) { e[j].style.display = 'none'; } } _popupLastOpened = null; } function togglePopup(element)...")
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)

Note: After publishing, 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 / Edge: Hold Ctrl while clicking Refresh, or press Ctrl-F5
  • Opera: Press Ctrl-F5.
/* All JavaScript here will be loaded for users of the mobile site */

var _calc3 = ''; // string to be sent to calc3.php ajax
var _popupLastOpened = null; // element
const _popupClasses = ['ppContent','dp','dpVar'];
function closePopup() {
 for (var i=0; i<_popupClasses.length; i++) {
  e = document.getElementsByClassName(_popupClasses[i]);
  for (var j=0; j<e.length; j++) {
   e[j].style.display = 'none';
  }
 } _popupLastOpened = null;
}
function togglePopup(element) {
 if (element == _popupLastOpened) {
  element.style.display = 'none';
  _popupLastOpened = element.parentNode;
 }
 else {
  closePopup();
  _popupLastOpened = element;
  // Show the element and any ancestors that are of a popup class
  var a = [];
  for (var p=element; p; p=p.parentNode) {
   if (_popupClasses.indexOf(p.className) >= 0) {
    p.style.display = 'initial';
    a.push(p);
   }
  }
  // Fix squished popups (needed for mobile)
  var fix = 0;
  for (var i=0; i<a.length; i++) {
   if (a[i].clientWidth < a[i].clientHeight && a[i].clientHeight > 50) fix += 2;
   if (fix) {
    a[i].style.left = a[i].style.right = '0.5em';
    if ((i==0 && a.length>1) || a[i].className == 'ppContent') a[i].style.marginTop = '1em';
    fix--;
   }
  }
 }
}
function showHide(show,hide) { // for expandables
 show.style.display = 'initial';
 hide.style.display = 'none';
}
function dpInfo(o) { // for calculator: display a datapoint's info
 var id = o.innerHTML;
 var calc = o.parentElement;
 var elem = document.getElementById('dp-'+id);
 if (elem) {
  calc.appendChild(elem);
  togglePopup(elem);
 }
}

// Functions for drag-and-drop of file graph nodes:
function allowDrop(ev) { ev.preventDefault(); }
function drag(ev) {
 var r = ev.target.getBoundingClientRect();
 ev.dataTransfer.setData("text", ev.target.id + ','
                              + (ev.clientX - 0.5*(r.left+r.right)) + ','
                              + (ev.clientY - r.top));
}
function drop(ev) {
 ev.preventDefault();
 var fg = ev.target;
 while (fg && fg.className != 'fileGraph') fg = fg.parentNode;
 if (fg) {
  var d = ev.dataTransfer.getData("text").split(',');
  var n = document.getElementById(d[0]);
  var r = fg.getBoundingClientRect();
  n.style.left = (ev.clientX - r.left - parseFloat(d[1]))+'px';
  n.style.top  = (ev.clientY - r.top  - parseFloat(d[2]))+'px';
  updateFgConnect();
  updateWikiText(n);
 }
}

// Function to draw the connections in the file graph
function updateFgConnect() {
 const connectionLabels = ['- - - -','generates','is input for','contributes to'];
 const connectionColors = ['#333','#F06','#666','#960'];
 e = document.getElementsByClassName('fgConnect');
 for (var i=0; i<e.length; i++) {
  var a = e[i].title.split(',');
  var from = document.getElementById('fgNode-'+a[0].trim());
  var to   = document.getElementById('fgNode-'+a[1].trim());
  var type = (a[2] && a[2].trim()) || 0;
  var label;
  if (connectionLabels[type] && connectionColors[type]) {
   label = connectionLabels[type];
   e[i].style.color = e[i].style.borderColor = connectionColors[type];
  } else label = type;
  var fs = window.getComputedStyle(from);
  var ts = window.getComputedStyle(to);
  var x1 = parseFloat(fs.getPropertyValue('left'));
  var y1 = parseFloat(fs.getPropertyValue('top'));
  var x2 = parseFloat(ts.getPropertyValue('left'));
  var y2 = parseFloat(ts.getPropertyValue('top'));
  var angle = Math.atan((y2-y1)/(x2-x1))*180/Math.PI;
  var len = Math.sqrt((x1-x2)*(x1-x2)+(y1-y2)*(y1-y2));
  if (x2 >= x1) {
   e[i].innerHTML = '--&gt;&gt; '+label+' &gt;&gt;';
   e[i].style.left = x1+'px';
   e[i].style.top = y1+'px';
  }
  else {
   e[i].innerHTML = '&lt;&lt; '+label+' &lt;&lt;--';
   e[i].style.left = x2+'px';
   e[i].style.top = y2+'px';
  }
  e[i].style.paddingLeft=e[i].style.paddingRight = '0px';
  e[i].style.transform = 'none';
  e[i].style.display = 'initial';
  var padding = 0.5*(len - e[i].offsetWidth);
  e[i].style.paddingLeft=e[i].style.paddingRight = padding+'px';
  e[i].style.transform = 'rotate('+angle+'deg)';
 }
}

// Function for mediawiki 'preview' mode only:
//   Alters the wikitext in the editor: Edits the coordinates of a file-graph node after dragging it.
//   Warning: if you don't write your wikitext properly, you may get undefined results!
var _doUpdateWt=1;
function updateWikiText(fgNode) {
 if (_doUpdateWt) {
  var prefix = document.title.substr(0,8);
  var target = document.getElementById('wpTextbox1');
  if (target && (prefix=='Editing ' || prefix=='Creating')) {
   if (_doUpdateWt==1) {
    if (confirm('Update coordinates in wikitext?')) _doUpdateWt=2;
    else _doUpdateWt=0;
   }
   if (_doUpdateWt==2) {
    // get data to generate the new coordinates string
    var s = window.getComputedStyle(fgNode);
    var x = parseFloat(s.getPropertyValue('left'));
    var y = parseFloat(s.getPropertyValue('top'));
    var graph = fgNode.parentNode;
    s = window.getComputedStyle(graph);
    var em = parseFloat(s.getPropertyValue('font-size'));
    // generate strings
    var search = '{{fg-node|'+fgNode.id.substr(7)+'|';
    var replace = parseInt(100*x/graph.offsetWidth) + '%|' + (y/em+0.5).toFixed(1) + 'em'; // the new coordinates string
    // parse wikitext to find the place to insert
    var input = target.value;
    var i, j=0, len=input.length, sl=search.length;
    for (i=0; i<len; i++) {
     var c = input[i];
     if (c==' '||c=='\t'||c=='\n'||c=='\r'||c=='\v') continue;
     if (c == search[j]) {
      j++;
      if (j >= sl) break;
     }
     else j=0;
    }
    var start = ++i; while (i<len && input[i] != '|') i++;
    i++;             while (i<len && input[i] != '|') i++;
    // insert the new coordinates string
    if (start<len && i<len) target.value = input.substr(0,start) + replace + input.substr(i);
   }
  }else _doUpdateWt=0;
 }
}


// Main function - should be called on page load.
function parseFeatures() {
 var e;
 // Expandable text
 e = document.getElementsByClassName('xp');
 for (var i=0; i<e.length; i++) {
  var content = e[i].innerHTML;
  e[i].innerHTML = "<span class='forMore' onclick='showHide(this.nextSibling, this)'>(...)</span>"
                 + "<span class='xpContent' onclick='showHide(this.previousSibling, this)'>( "+content+" )</span>";
 }
 // Popups
 e = document.getElementsByClassName('pp');
 for (var i=0; i<e.length; i++) {
  var content = e[i].innerHTML;
  e[i].innerHTML = "<span class='forMore' onclick='togglePopup(this.nextSibling)'>[''']</span>"
                 + "<div class='ppContent' onclick='closePopup()'>"+content+"</div>";
 }
 e = document.getElementsByClassName('ppv2');
 for (var i=0; i<e.length; i++) {
  var content = e[i].innerHTML.split('|');
  e[i].innerHTML = "<span class='forMore' onclick='togglePopup(this.nextSibling)'>"+content[0]+"</span>"
                 + "<div class='ppContent' onclick='closePopup()'>"+content[1]+"</div>";
 }
 // Local datapoints and calculations
 e = document.getElementsByClassName('dp');
 for (var i=0; i<e.length; i++) {
  var id = e[i].children[0].innerHTML.trim().split('\n')[0];
  var val= e[i].children[1].innerHTML.split('\n').join(' ');
  var redundant = document.getElementById('dp-'+id);
  if (redundant) redundant.id = ''; // XXX: This can only handle the case 'same identifier, same value'. It can't handle 'same identifier, different value' - at least 1 calculation would show up wrong. But to fix that, we would need a huge refactor - I don't think it's worthwhile. Instead, let's just warn users not to let their datapoints conflict with each other.
  e[i].id = 'dp-'+id;
  e[i].onclick = closePopup;
  _calc3 += id+' = '+val+'\n';
 }
 e = document.getElementsByClassName('calc');
 for (var i=0; i<e.length; i++) {
  var v = e[i].children[2] && e[i].children[2].innerHTML.trim().split('\n')[0]; // variable
  if (v) {
   e[i].id = 'calc-'+v; // XXX: If the user sets the same variable more than once (iterative math), it'll break the 'From previous calculation' link. To fix this, we would have to edit both this script and calc3.php.
   var n = document.createElement('div');
   n.className = 'dpVar'; n.id = 'dp-'+v;
   n.innerHTML = "<div class='dp0'>"+v+"</div><div class='dp2'>From <a href='#calc-"+v+"'>previous calculation</a></div>";
   n.onclick = closePopup;
   e[i].parentElement.insertBefore(n, e[i]);
   _calc3 += v+' = ';
  }
  _calc3 += e[i].children[0].innerHTML.split('\n').join(' '); // expression
  if (e[i].children[1]) _calc3 += ' <> '+e[i].children[1].innerHTML.split('\n').join(' '); // units wanted
  _calc3 += '\n';
  e[i].innerHTML = 'Loading...';
 }
 var xhttp = new XMLHttpRequest();
 xhttp.onreadystatechange = function() {
  if (this.readyState==4 && this.status==200) {
   var l=0;
   var lines = this.responseText.split('\n');
   var e = document.getElementsByClassName('dp');
   for (var i=0; i<e.length; i++, l++) e[i].children[1].innerHTML = lines[l].split('<br')[0]; // update 'value' to contain hyperlinks
   e = document.getElementsByClassName('calc');
   for (var i=0; i<e.length; i++, l++) e[i].innerHTML = lines[l]; // equation
  }
 };
 xhttp.open("POST", "/calc/calc3.php?input="+encodeURIComponent(_calc3), true)
 try { xhttp.send(); } catch(error) { /* AJAX URL not found */ }

 // External calculation links
 e = document.getElementsByClassName('ecalc');
 for (var i=0; i<e.length; i++) {
  var str = "<a href='/calc/calc2.php";
  str += "?a="    +encodeURIComponent(e[i].children[0].innerHTML);
  str += "&b="    +encodeURIComponent(e[i].children[1].innerHTML);
  str += "&title="+encodeURIComponent(e[i].children[2].innerHTML);
  str += "' target='_blank' class='forMore'>[=] &#128279;</a>";
  e[i].innerHTML = str;
  e[i].style.display = 'initial';
 }

 // File graph
 updateFgConnect();
 window.addEventListener('resize', updateFgConnect);
 // make file graph nodes draggable
 e = document.getElementsByClassName('fileGraph');
 for (var i=0; i<e.length; i++) {
  e[i].id = 'fileGraph'+i;
  e[i].ondrop = drop;
  e[i].ondragover = allowDrop;
 }
 e = document.getElementsByClassName('fgNode');
 for (var i=0; i<e.length; i++) {
  e[i].draggable = true;
  e[i].ondragstart = drag;
  var c = e[i].children;
  for (var j=0; j<c.length; j++) {
   if (c[j].tagName=='A') c[j].draggable = false; // override hyperlink drag
  }
 }
}
ParseFeatures();