// This file contains JavaScript functions to display Text Puzzles
// Version 3, Peter Jipsen (c) 2004-1-1

// This version is XML compliant, i.e. works with MathML and NS7

swap = true;       // default puzzle move behaviour
numbered = true;   // by default puzzle pieces are numbered
piecesdiv = false; // puzzle pieces listed in a <div id="pieces"/>
placeholder = "__________"

ppList = [];      //list of puzzle pieces
ppListClone = [];
ppClass = [];
innerHTMLppList = []; //innerHTML of original pieces
perm = [];        //permutation that scrambles the pieces
sel = [];         //selects the span nodes with class TP
selected = -1;    //indicated which puzzle piece is currently selected
nameStr = "";     //name of the student
graded = false;   //global flag (correct pieces are shown when true)
start = new Date(); //timer variable
now = new Date();   //timer variable

//alert(navigator.appVersion.lastIndexOf("IE"));
if (document.getElementById==null) 
  alert("This webpage requires a recent browser such as\
\nNetscape 7+ or Internet Explorer 6+")

function myCreateElementNS(s,t) {
  if (document.createElementNS==null)
    return document.createElement(t);
  else
    return document.createElementNS(s,t);
}

function tag(attrList,nodeList) {
  var t;
  t = myCreateElementNS("http://www.w3.org/1999/xhtml",attrList[0]);
  for (var i=1; i<attrList.length; i=i+2)
    t.setAttribute(attrList[i],attrList[i+1]);
  for (var i = 0; i<nodeList.length; i++) 
    if (typeof nodeList[i] == "string")
      t.appendChild(document.createTextNode(nodeList[i]));
    else t.appendChild(nodeList[i]);
  return t;
}

function ntag(attrList,nodeList) { //not used
  var t;
  if (document.createElementNS==null) {
    var st='';
    st = '<'+attrList[0]+' ';
    for (var i=1; i<attrList.length; i=i+2)
      st = st+' '+attrList[i]+'=\"'+attrList[i+1]+'\"';
    st = st+'>';
    for (var i = 0; i<nodeList.length; i++) 
      if (typeof nodeList[i] == 'string')
        st = st+nodeList[i];
      else
        st = st+nodeList[i].outerHTML;
    t = document.getElementById("bottom").cloneNode(true);
    st = st+'</'+attrList[0]+'>';
//alert(st);
    t.outerHTML = st;
//alert(t.outerHTML);
  } else {
    t = myCreateElementNS("http://www.w3.org/1999/xhtml",attrList[0]);
    for (var i=1; i<attrList.length; i=i+2)
      t.setAttribute(attrList[i],attrList[i+1]);
    for (var i = 0; i<nodeList.length; i++) 
      if (typeof nodeList[i] == "string")
        t.appendChild(document.createTextNode(nodeList[i]));
      else t.appendChild(nodeList[i]);
  }
  return t;
}

function replaceChildNodes(node,nodeList){
  var n = node.childNodes.length;
  for (var i=0; i<n; i++)
    node.removeChild(node.firstChild);
  for (var i=0; i<nodeList.length; i++)
    node.appendChild(nodeList[i]);
}

function getElementsWithClass(containingEl, tagName, className1){
  var returnedCollection = new Array(0);
  var collection = containingEl.getElementsByTagName(tagName);
  for(var i = 0; i < collection.length; i++)
    if(collection[i].className.slice(0,className1.length)==className1)
      returnedCollection[returnedCollection.length] = i;
  return returnedCollection;
}

function equalSubtrees(n1,n2){
  if (n1.childNodes.length==n2.childNodes.length){
    if (n1.childNodes.length==0) return n1.nodeValue==n2.nodeValue;
    for (var i=0; i<n1.childNodes.length; i++)
      if (!equalSubtrees(n1.childNodes[i],n2.childNodes[i])) return false;
    return true;
  }
  return false;
}

function movePieces(k){
  var i = 0;
  var t;
  if (selected>=0) {
    i = selected;
    selected = -1;
    if (document.createElementNS==null)
      ppList[sel[i]].outerHTML=
        '<span style="background-color:yellow" class='+ppClass[i]+'></span>';
    else ppList[sel[i]].setAttribute("style","background-color:yellow");
    if (i!=k) {
      if (swap || i==k-1) {
        t = perm[i];
        perm[i] = perm[k];
        perm[k] = t;
      } else if (i<k)
        perm = perm.slice(0,i).concat(perm.slice(i+1,k),[perm[i]],perm.slice(k));
      else
        perm = perm.slice(0,k).concat([perm[i]],perm.slice(k,i),perm.slice(i+1));
    }
    updatePieces(graded);
  }else{
    selected = k;
    if (document.createElementNS==null) {
      ppList[sel[k]].outerHTML=
        '<span style="border-style:groove; background-color:yellow" class='+ppClass[k]+'>'+
         ppListClone[perm[k]]+'</span>';
    } else {
      ppList[sel[k]].setAttribute("style","border-style:groove; background-color:yellow");
    }
  }
  return false;
}

function updatePieces(showCorrect){
  for (var i=0; i<sel.length; i++) {
    var newFrag = document.createDocumentFragment();
    if (document.createElementNS==null) 
      ppList[sel[i]].outerHTML='<span style="background-color:yellow" class='+
        ppClass[perm[i]]+'>'+ppListClone[perm[i]]+'</span>';
    else {
      newFrag.appendChild(ppListClone[perm[i]].cloneNode(true));
      ppList[sel[i]].parentNode.replaceChild(newFrag,ppList[sel[i]]);
    }
    if (showCorrect) {
      //check if correct and if so, replace anchor and img tag with span
      if ((ppList[sel[i]].parentNode.tagName=="a" ||
           ppList[sel[i]].parentNode.tagName=="A") && 
          (ppList[sel[i]].className.length>2 && 
           ppList[sel[i]].className==ppListClone[sel[i]].className || 
//           sel[perm[i]]==sel[i] || 
           equalSubtrees(ppList[sel[i]],ppListClone[sel[i]])))
         ppList[sel[i]].parentNode.parentNode.
           replaceChild(ppList[sel[i]],ppList[sel[i]].parentNode);
    }
  }
  if (!graded) now = new Date();
  sec = Math.floor((parent.now-parent.start)/1000);
  var timerNode = myCreateElementNS("http://www.w3.org/1999/xhtml","p");
  timerNode.setAttribute("align","right");
  timerNode.appendChild(document.createTextNode("Time: "+
    (sec>=60?Math.floor(sec/60)+" min ":"")+sec%60+" sec"));
  tnode = document.getElementById("timer")
  if (tnode.firstChild==null)
    tnode.appendChild(timerNode);
  else
    tnode.replaceChild(timerNode,tnode.firstChild);
}

function initPuzzlePieces(){
  var body = document.getElementsByTagName("body")[0];
  var pdiv = document.getElementById("pieces");
  piecesdiv = pdiv != undefined;
  myim = tag(["img","border","0","width","15","height","13","src",
            "http://www.chapman.edu/~jipsen/TP/puzzlePiece.gif"],[]);
  var bot = document.getElementById("bottom");
 if (bot != undefined) {
  bot.parentNode.insertBefore(tag(["div","id","timer"],[]),bot);
  newbot = tag(["div","id","bottom"],
    [tag(["input","type","button","value","Check","onclick","grade()"],[]),
     tag(["hr"],[]),
     tag(["b"],["Brief Instructions for solving this Text Puzzle: "]),
     "The ",
     myim.cloneNode(true),
     tag(["b","style","background-color:yellow"],[" puzzle pieces"]),
     piecesdiv?
     " above have been removed from the text, and your task is to move them back into their correct positions. Click a ":
     " above have been shuffled, and your task is to move them around so that they end up in the correct position. Click a ",
     myim.cloneNode(true),
     tag(["b","style","background-color:yellow"],[" puzzle piece"]),
     " to select it, then click another ",
     myim.cloneNode(true),
     tag(["b","style","background-color:yellow"],[" puzzle piece"]),
     " to move (or swap) the selected piece to that particular position. When you think you are done, click on the Check button."]);
  bot.parentNode.replaceChild(newbot,bot);
  if (document.createElementNS==null)
    document.getElementById("bottom").firstChild.outerHTML=
      '<input type="button" value="Check" onclick="grade()"/>';
  body.appendChild(tag(["hr"],[]));
  body.appendChild(tag(["center"],[
    myim.cloneNode(true)," Text Puzzles ",myim.cloneNode(true)," by ",
    tag(["a","href","http://www.chapman.edu/~jipsen","target","new"],
      ["Peter Jipsen"]),
    " ",myim.cloneNode(true)," Chapman University ",myim.cloneNode(true)]));

  ppList = document.getElementsByTagName("span");
  var n = ppList.length;
  for (var i=0; i<n; i++)
    if (ppList[i].className.slice(0,2)=="TP") sel[sel.length] = i;
  n = sel.length;
  for (var i=0; i<n; i++) perm[i] = i;
  var t;
  for (var i=0; i<n; i++) {//swap perm[i] with perm[random]
    j = Math.floor(n*Math.random());
    t = perm[i];
    perm[i] = perm[j];
    perm[j] = t;
  }
  if (piecesdiv) // create n new empty pieces and update perm
    if (document.createElementNS==null) {
      var st = [];
      var spn = '<span class="TP">'+placeholder+'</span>&nbsp; &nbsp; &nbsp;';
      for (var i=0; i<n; i++) {
        st = st + spn;
        perm[i] = perm[i]+n;
        perm[i+n] = i;
      }
      pdiv.innerHTML = st;//+'</div>'
    }else
      for (var i=0; i<n; i++) {
        pdiv.appendChild(tag(["span","class","TP"],[placeholder]));
        pdiv.appendChild(tag(["b"],["\u00A0 \u00A0 \u00A0"]));
        perm[i] = perm[i]+n;
        perm[i+n] = i;
      }
  ppList = document.getElementsByTagName("span");
  n = ppList.length;
  sel = [];
  for (var i=0; i<n; i++)
    if (ppList[i].className.slice(0,2)=="TP") sel[sel.length] = i;
  n = sel.length;
  for (var i=0; i<n; i++) {
    ppList[sel[i]].setAttribute("style","background-color:yellow");
    if (document.createElementNS==null) {
      ppListClone[i] = ppList[sel[i]].innerHTML;
      ppClass[i] = ppList[sel[i]].className;
    }
    else ppListClone[i] = ppList[sel[i]].cloneNode(true);
  }
  var newFrag = document.createDocumentFragment();
  for (var i=0; i<n; i++) { // shuffle nodes
    if (document.createElementNS==null) {
      ppList[sel[i]].outerHTML='<a onclick="movePieces('+i+')">'+(numbered?'<i style="color:blue">'+(i+1)+'</i>':'')+'<img border="0" width="15" height="13" src="http://www.chapman.edu/~jipsen/TP/puzzlePiece.gif" alt="Insert here"> <span style="background-color:yellow" class='+ppClass[i]+'></span></a>';
    } else {
      newFrag.appendChild(tag(["a","onclick","movePieces("+i+")"],[
        tag(["i","style","color:blue"],[(numbered?i+1+"":"")]),
        tag(["img","border","0","width","15","height","13",
             "src","http://www.chapman.edu/~jipsen/TP/puzzlePiece.gif",
             "alt","Insert here"],[]),
        ppListClone[perm[i]].cloneNode(true)]));
      ppList[sel[i]].parentNode.replaceChild(newFrag,ppList[sel[i]]);
    }
  }
  ppList = document.getElementsByTagName("span"); // read shuffled nodes
  start = new Date();
  updatePieces(false);
 }
}

function storeName() {
  nameStr = document.getElementById("nametext").value;
  var d = new Date();
  botobj = document.getElementById("bottom");
  replaceChildNodes(botobj,[
    tag(["p"],["You got "+correct+" out of "+
         (piecesdiv?sel.length/2:sel.length)+" correct, or "+percent+"%. "]),
    tag(["p"],["Text Puzzle completed by ",
      tag(["b"],[nameStr])," on "+d.toLocaleString()+"."]),
      tag(["p"],["\"Yes, I did it on my own without any help\": _________________________________"])]);
}

function grade(){
  correct = 0;
  for (var i=(piecesdiv?sel.length/2:0); i<sel.length; i++) {
    correct = correct+(ppList[sel[i]].className.length>2 && 
      ppList[sel[i]].className==ppListClone[sel[i]].className || 
      equalSubtrees(ppList[sel[i]],ppListClone[sel[i]])?1:0);
  }
  graded=true;
  var n = (piecesdiv?sel.length/2:sel.length);
  percent = Math.round(100*correct/n);
  swap = true;
  updatePieces(true);
  botobj = document.getElementById("bottom");
  replaceChildNodes(botobj,[
    tag(["p"],["You got "+correct+" out of "+n+
            " correct, or "+percent+"%. "]),
    tag(["p"],[percent==100?
"Well done. You seem to know what's going on.":
(percent>=80?
"Pretty good. Finish the last few steps and try another puzzle.":
(percent>=65?
"Fairly decent. Finish the puzzle, then reload the page and \
try it again.":
(percent>=50?
"Not so good. Look carefully at what lines are not yet in the \
right place. Finish the puzzle without guessing, then reload \
the page and try it again.":
"Oh-oh. Read the relevant chapter(s) in your textbook, then \
reload the page and retry this puzzle several times.")))]),
    tag(["p"],["Finished? Type your name: ",
      tag(["input", "id", "nametext", "type", "text", "size", "25", 
           "name", "yourName"],[]),
      "Click ",
      tag(["input", "id", "enterButton", "type", "button", "value", "Enter", 
           "onClick", "storeName()"],[]),
      "and then print this page.",
      tag(["br"],[]),
      "(Don't worry, your score and name are not recorded anywhere.)"])]);
  if (document.createElementNS==null)
    document.getElementById("enterButton").outerHTML=
      '<input type="button" value="Enter" onClick="storeName()"/>';
}



