<!--

  %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    "DECK THE HALLS"

   Music:    An Ancient Yule-Tide Carol!
   Lyrics:   Orville Chomer  ... why that's me! 😊 (*)

   Click [Start] to sing along... or just listen if you're shy!

   Click other special places to decorate your page!

   Click on ornaments you've added to remove them from the page.


   (*) Not your standard, regular words, but silly ones
       as you will soon find out!
  %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  










-->
<img width="50" class="jingleBell" id="bouncingBall"
     src="https://orvillechomer.github.io/myCDN/jingleBell.png">


<div class="bellSet1 shakeBells-1">
  <div class="bell1a">
    <img width="90" class="jingleBell bell1b shakeBell1"
     src="https://orvillechomer.github.io/myCDN/jingleBell.png">
  </div>
  <div class="bell2a">
    <img width="90" class="jingleBell bell2b shakeBell2"
     src="https://orvillechomer.github.io/myCDN/jingleBell.png">
  </div>
</div>

<div class="bellSet2 shakeBells-1">
  <div class="bell1a">
    <img width="90" class="jingleBell bell1b shakeBell1"
     src="https://orvillechomer.github.io/myCDN/jingleBell.png">
  </div>
  <div class="bell2a">
    <img width="90" class="jingleBell bell2b shakeBell2"
     src="https://orvillechomer.github.io/myCDN/jingleBell.png">
  </div>
</div>


<div class="bellSet3 shakeBells-1">
  <div class="bell1a">
    <img width="90" class="jingleBell bell1b shakeBell1"
     src="https://orvillechomer.github.io/myCDN/jingleBell.png">
  </div>
  <div class="bell2a">
    <img width="90" class="jingleBell bell2b shakeBell2"
     src="https://orvillechomer.github.io/myCDN/jingleBell.png">
  </div>
</div>


<div class="bellSet4 shakeBells-1">
  <div class="bell1a">
    <img width="90" class="jingleBell bell1b shakeBell1"
     src="https://orvillechomer.github.io/myCDN/jingleBell.png">
  </div>
  <div class="bell2a">
    <img width="90" class="jingleBell bell2b shakeBell2"
     src="https://orvillechomer.github.io/myCDN/jingleBell.png">
  </div>
</div>

<div id="dropZone" title="click or tap to place an ornament"></div>
<div id="dropZoneLeft" title="click or tap to place an ornament"></div>
<div id="dropZoneRight" title="click or tap to place an ornament"></div>

<div id="songVerse" class="songVerse1">DECK  THE  HALLS!</div>

<div id="bottomStuff">
  <center>
    <button id="startBtn">START</button>&nbsp;&nbsp;&nbsp;
    <button id="startOverBtn">START OVER</button>
  <center>
</div>
    
    
    <img class="hideMe" id="pbJar" src="https://orvillechomer.github.io/myCDN/peanutButterJar.png" width="100">
    <img class="hideMe" id="gjJar" src="https://orvillechomer.github.io/myCDN/grapeJellyJar.png" width="100">
    
    <img class="hideMe" id="garlicBulb" src="https://orvillechomer.github.io/myCDN/garlicBulb.png" width="100">
    
    <style id="jarFlyByStyling"></style>

    
    
    
    
    <div id="playPos">0000</div>

    
    
    
<script 
 src="https://orvillechomer.github.io/myCDN/simpleParallax.js">
</script>

<script 
 src="https://orvillechomer.github.io/myCDN/snowfall.js">
</script>
@import url('https://fonts.googleapis.com/css2?family=Flavors&display=swap');


body {
  height: 100%;
  background-image: linear-gradient(#2B3C46, #3363A1);
  padding:0px;
  margin:0px;
  overflow:hidden;
  background-repeat: no-repeat;
  background-attachment: fixed;
}

.jingleBell {
  position:absolute;
  left:650px;
  top:200px;
  width:50px;
}

.bellSet1 {
  position:absolute;
  left:50px;
  top:40px;
  box-sizing:border-box;
  pointer-events: none;
}

.bellSet2 {
  position:absolute;
  right:50px;
  top:40px;
  width:200px;
  box-sizing:border-box;
  pointer-events: none;
}

.bellSet3 {
  position:absolute;
  left:50px;
  bottom:40px;
  height:140px;
  z-index:20;
  box-sizing:border-box;
  pointer-events: none;
}

.bellSet4 {
  position:absolute;
  right:50px;
  bottom:40px;
  width:200px;
  height:140px;
  z-index:20;
  box-sizing:border-box;
  pointer-events: none;
}


.bell1a {
  position:absolute;
  width:130px;
  height:106px;
  left:-20px;
  top:10px;
  transform-origin: 91% 19%; 
  transform:rotate(240deg); /* 240 */
  box-sizing:border-box;
}

.bell1b {
  width:130px;
  left:0px;
  top:0px;
  box-sizing:border-box;
  padding:0px;
}

.bell2a {
  position:absolute;
  width:130px;
  height:106px;
  left:-5px;
  top:5px;
  transform-origin: 91% 19%; 
  transform:rotate(-10deg);
  z-index:100;
  box-sizing:border-box;
}

.bell2b {
  width:130px;
  left:0px;
  top:0px;
  box-sizing:border-box;
  padding:0px;
}

@keyframes shakeBell1_ani {
  0% {
    transform:rotate(15deg);
  }
  50% {
    transform:rotate(5deg);
  }
  100% {
    transform:rotate(15deg);
  }
}
.shakeBell1 {
  transform-origin: 91% 19%;
  animation: shakeBell1_ani 0.3s infinite both;
}


@keyframes shakeBell2_ani {
  0% {
    transform:rotate(5deg);
  }
  50% {
    transform:rotate(15deg);
  }
  100% {
    transform:rotate(5deg);
  }
}
.shakeBell2 {
  transform-origin: 91% 19%;
  animation: shakeBell2_ani 0.26s infinite both;
}


.bell1 {
  width:130px;
  left:80px;
  top:45px;
  /* transform-origin: 90% 5%; */
  transform:rotate(240deg); /* 240 */
  /* border:solid red 3px; */
  box-sizing:border-box;
}

.bell2 {
  width:130px;
  left:0px;
  top:20px;
  transform:rotate(-10deg);
  z-index:100;
}

.ornament {
  position:absolute;
  top:0px;
  margin:0px;
  padding:0px;
  z-index:30;
  overflow:hidden;
  box-sizing:border-box;
  transform-origin: 50% 0%;
}

.ornamentString {
  position:absolute;
  margin:0px;
  padding:0px;
  z-index:29;
  overflow:hidden;
  box-sizing:border-box;
  background:white;
  top:0px;
  width:2px;
}

.hook1 {
  position:absolute;
  margin:0px;
  padding:0px;
  overflow:hidden;
  box-sizing:border-box;
  background:silver;
  width:10px;
  height:5px;
  border-radius:5px;
  z-index:31;
}




@keyframes showProp_ani {
  0% {
    transform: translate(40vw,30vh) rotate(14deg) scale(.4);
  }
  25% {
    transform: translate(50vw,40vh) rotate(-20deg) scale(1);
  }
  90% {
    transform: translate(50vw,40vh) rotate(-20deg) scale(1);
  }
  100% {
    transform: translate(40vw,30vh) rotate(8deg) scale(.1);
  }
}

.showProp {
  position:absolute;
  left:0px;
  top:0px;
  display:block;
  z-index:200;
  opacity:1;
  animation-name: showProp_ani;
  animation-duration: 0.8s;
  animation-fill-mode:both;
}





#bottomStuff {
  position:absolute;
  left:0px;
  right:0px;
  bottom:6px;
  display:none;
  z-index:30;
}

#songVerse {
  font-family: 'Flavors', cursive;
  position:absolute;
  left:0px;
  right:0px;
  text-align:center;
  top:40%;
  color:#ff8000; /* #ff8000  #d8ecf3 */
  text-shadow: 7px 5px black;
  z-index:22;
  opacity:0.8;
  pointer-events: none;
}

.songVerse1 {
  font-size:70pt;
}

.songVerse2 {
  font-size:50pt;
}

#dropZone {
  position:absolute;
  margin:0px;
  padding:0px;
  background:white;
  top:8px;
  height:35%;
  left:280px;
  right:280px;
  z-index:30;
  cursor:pointer;
  opacity:.001;
}

#dropZoneLeft {
  position:absolute;
  margin:0px;
  padding:0px;
  background:white;
  top:35%;
  height:50%;
  left:280px;
  width:21.5%;
  z-index:30;
  cursor:pointer;
  opacity:.001;
}

#dropZoneRight {
  position:absolute;
  margin:0px;
  padding:0px;
  background:white;
  top:35%;
  height:50%;
  right:280px;
  width:21.5%;
  z-index:30;
  cursor:pointer;
  opacity:.001;
}


button {
  background:white;
  opacity:0.8;
  border:none;
  width:120px;
  height:40px;
  border-radius:6px;
  font-size:13pt;
  font-weight:bold;
  cursor:pointer;
}

button:hover {
  background:pink;
}


#startOverBtn {
  display:none;
}


.hideMe {
  display:none;
}

.wrd1 {
  color:red;
}

.wrd2 {
  color:lightyellow;
}


/* ----------------------------------------------
 * Generated by Animista on 2020-12-16 13:58:13
 * Licensed under FreeBSD License.
 * See http://animista.net/license for more info. 
 * w: http://animista.net, t: @cssanimista
 * ---------------------------------------------- */

/**
 * ----------------------------------------
 * animation vibrate-1
 * ----------------------------------------
 */
@-webkit-keyframes shakeBells-1 {
  0% {
    -webkit-transform: translate(0);
            transform: translate(0);
  }
  20% {
    -webkit-transform: translate(-2px, 2px);
            transform: translate(-2px, 2px);
  }
  40% {
    -webkit-transform: translate(-2px, -2px);
            transform: translate(-2px, -2px);
  }
  60% {
    -webkit-transform: translate(2px, 2px);
            transform: translate(2px, 2px);
  }
  80% {
    -webkit-transform: translate(2px, -2px);
            transform: translate(2px, -2px);
  }
  100% {
    -webkit-transform: translate(0);
            transform: translate(0);
  }
}
@keyframes shakeBells-1 {
  0% {
    -webkit-transform: translate(0);
            transform: translate(0);
  }
  20% {
    -webkit-transform: translate(-2px, 2px);
            transform: translate(-2px, 2px);
  }
  40% {
    -webkit-transform: translate(-2px, -2px);
            transform: translate(-2px, -2px);
  }
  60% {
    -webkit-transform: translate(2px, 2px);
            transform: translate(2px, 2px);
  }
  80% {
    -webkit-transform: translate(2px, -2px);
            transform: translate(2px, -2px);
  }
  100% {
    -webkit-transform: translate(0);
            transform: translate(0);
  }
}

.shakeBells-1 {
	-webkit-animation: shakeBells-1 0.4s linear infinite both;
	        animation: shakeBells-1 0.4s linear infinite both;
}


@keyframes gentleSway_ani {
  0% {
    transform:rotate(0deg);
  }
  20% {
    transform:rotate(4deg);
  }
  25% {
    transform:rotate(3deg);
  }
  30% {
    transform:rotate(4deg);
  }
  50% {
    transform:rotate(4deg);
  }
  80% {
    transform:rotate(-1deg);
  }
  100% {
    transform:rotate(0deg);
  }
}

.gentleSway1 {
  animation:gentleSway_ani 4s infinite both;
}

.gentleSway2 {
  animation:gentleSway_ani 5s infinite both;
}


@keyframes dropOrnament_ani {
  0% {
    transform: translate(0px, 0px);
  }
  100% {
    transform: translate(0px, 120vh);
  }
}

.dropOrnament {
  animation:dropOrnament_ani .7s linear both;
}


#playPos {
  background:white;
  position:absolute;
  left:320px;
  top: 300px;
  z-index:1000;
  padding:4px;
  display:none;
}
const snowfall = new Snowfall()

let nNextStep = -1;
let nCurrentNextTimestamp = -1; // crazy variable name!
let currentNextStep; // and another crazy variable name!
let nNextWord = 0;

// @@@@@@@    STEPS...    @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
let steps = [];
steps.push({tm:.5,op:"nextWord"});// the
steps.push({tm:.6,op:"nextWord"});// halls
steps.push({tm:.7,op:"nextWord"});// with
steps.push({tm:1.19,op:"showProp",id:"pbJar"}); // show the peanutbutter jar prop.
steps.push({tm:1.2,op:"nextWord"});// pea-
steps.push({tm:1.8,op:"nextWord"});// nut
steps.push({tm:2.1,op:"nextWord"});// but-
steps.push({tm:2.5,op:"nextWord"});// ter

steps.push({tm:2.6,op:"nextLine"}); // Fa...
faLaLa()
steps.push({tm:6,op:"nextLine"});  // eat
steps.push({tm:6.5,op:"nextWord"});// it
steps.push({tm:6.6,op:"nextWord"});// til
steps.push({tm:6.7,op:"nextWord"});// you
steps.push({tm:6.8,op:"nextWord"});// cough
steps.push({tm:7,op:"nextWord"});//   and
steps.push({tm:7.4,op:"nextWord"});// sput-
steps.push({tm:8.1,op:"nextWord"});// ter

steps.push({tm:8.7,op:"nextLine"}); // Fa...
faLaLa()
steps.push({tm:11,op:"nextLine"})
steps.push({tm:11.7,op:"nextWord"});// your
steps.push({tm:11.9,op:"nextWord"});// bel-
steps.push({tm:12,op:"nextWord"});// ly
steps.push({tm:12.2,op:"nextWord"});// with
steps.push({tm:12.3,op:"showProp",id:"gjJar"}); // show the grape jelly jar prop.
steps.push({tm:12.32,op:"nextWord"});// grape
steps.push({tm:12.4,op:"nextWord"});// jel-
steps.push({tm:12.5,op:"nextWord"});// ly

steps.push({tm:14.5,op:"nextLine"}); // Fa...
faLaLa()

steps.push({tm:17,op:"nextLine"})
steps.push({tm:17.7,op:"nextWord"});// eat
steps.push({tm:17.8,op:"nextWord"});// gar-

// Final Fa La La line which is sung at a different rate:
steps.push({tm:20.5,op:"nextLine"})
steps.push({tm:20.6,op:"nextWord"});// la
steps.push({tm:20.7,op:"nextWord"});// la
// @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@


let bouncingBallNd,songVerseNd,bottomStuffNd,startBtnNd,playPosNd;
let startOverBtnNd,pbJarNd,gjJarNd,jarFlyByStylingNd,dropZoneNd,dropZoneLeftNd,dropZoneRightNd;
let currentWordObjs = [];
let activeAnimationIds = {};

const audio = new Audio('https://orvillechomer.github.io/myCDN/deckTheHallsWithPeanutbutter.mp3');

const NOT_PLAYING = 0;
const PLAYING = 1;
const PAUSED = 2;
let nPlayingStatus = NOT_PLAYING;
let bAbleToPlayAudio = false;

let nNextSongLine = -1;

let nTimeoutId = -1;

let nNextPlayPos;


const verses =[
  "DECK THE HALLS WITH PEA- NUT BUT- TER!",
  "FA LA LA LA LA LA LA LA LA!",
  "EAT IT TIL YOU COUGH AND SPUT- TER!",
  "FA LA LA LA LA LA LA LA LA!",
  "STUFF YOUR BEL- LY WITH GRAPE JEL- LY!",
  "FA LA LA LA LA LA LA LA LA!",
  "DON'T EAT GAR- LIC IT'S TOO SMEL- LY!",
  "FA LA LA LA LA LA LA LA LA!"];

const sFontSize = "65pt"

function pageSetup() {
  console.clear()
  bouncingBallNd = document.getElementById("bouncingBall")
  bouncingBallNd.style.display = "none";
  songVerseNd = document.getElementById("songVerse")
  
  dropZoneNd = document.getElementById("dropZone")
  dropZoneLeftNd = document.getElementById("dropZoneLeft")
  dropZoneRightNd = document.getElementById("dropZoneRight")
  dropZoneNd.addEventListener("click",placeOrnament)
  dropZoneLeftNd.addEventListener("click",placeOrnament)
  dropZoneRightNd.addEventListener("click",placeOrnament)
  
  
  bottomStuffNd = document.getElementById("bottomStuff")
  startBtnNd = document.getElementById("startBtn")
  startBtnNd.addEventListener("click",buttonClicked)
  startOverBtnNd = document.getElementById("startOverBtn")
  startOverBtnNd.addEventListener("click",startOver)
  
  //animationEnded
  pbJarNd = document.getElementById("pbJar")
  pbJarNd.addEventListener('animationend', animationEnded);
  gjJarNd = document.getElementById("gjJar")
  gjJarNd.addEventListener('animationend', animationEnded);
  jarFlyByStylingNd = document.getElementById("jarFlyByStyling")
  
  
  audio.addEventListener('ended', donePlaying);
  audio.addEventListener('timeupdate', checkingPlayPos);
  
  window.addEventListener("mousedown", mouseDown)
  
  playPosNd = document.getElementById("playPos")
  
  pageResize()
  
  //pbJarNd.className = "peanutButterFlyBy";
  doJarFlyBy('pbJar')
  
  window.addEventListener("mousemove",mouseMoved)
  window.addEventListener("click",checkForOrnament)
  
  nTimeoutId = setTimeout(beginThings, 5000)
} // end of pageSetup()



let w,h;
function pageResize() {
  w = window.innerWidth;
  h = window.innerHeight;
  
} // end of pageResize



function faLaLa() {
  steps.push({tm:2.8,op:"nextWord"});// la
  steps.push({tm:3,op:"nextWord"});  // la
  steps.push({tm:3.2,op:"nextWord"});// la
  steps.push({tm:3.9,op:"nextWord"});// la
  steps.push({tm:4.4,op:"nextWord"});// la
  steps.push({tm:5,op:"nextWord"});  // la
  steps.push({tm:5.2,op:"nextWord"});  // la
} // end of faLaLa



function mouseDown(evt) {
  bAbleToPlayAudio = true;
} // end of mouseDown



let nNextId = 0;
function getNextId() {
  nNextId = nNextId + 1;
  return nNextId;
} // end getNextId()


function checkForOrnament(evt) {
  const el = evt.srcElement || evt.originalTarget;
  
  if (typeof el.dataset.ornamentid !== "undefined") {
    evt.preventDefault();
    let sId = el.dataset.ornamentid+""
    let ornStingNd = document.getElementById("ornString"+sId)
    ornStingNd.style.display = "none"
    let cntrNd = document.getElementById("orn"+sId)
    cntrNd.addEventListener("animationend",ornamentDropCompleted)
    cntrNd.classList.add("dropOrnament")
    //dropOrnament
  } // end if
  
} // end of checkForOrnament()




function animationEnded(evt) {
  const el = evt.srcElement || evt.originalTarget;
  if (typeof activeAnimationIds[el.id] === "string") {
    delete activeAnimationIds[el.id];
  } // end if
  
  el.className = "hideMe"
  jarFlyByStylingNd.innerHTML = ""; // free up a little bit of memory

} // end of animationEnded()


function ornamentDropCompleted(evt) {
  const el = evt.srcElement || evt.originalTarget;
  el.remove();
} // end of ornamentDropCompleted()




function placeOrnament(evt) {
  const s=[];
  const Q = '"'
  let nId = getNextId()
  const cntr = document.createElement("div")
  let n = intRnd(1)+1
  cntr.className = "ornament gentleSway"+n
  cntr.id = "orn"+nId;
  //cntr.dataset.ornamentid = nId+"";
  let nSize = intRnd(25)+35;
  let nHalfSize = Math.floor(nSize / 2)
  let sColor = "red"
  let sBorderColor = "brown"
  let nColor = intRnd(4)
  switch(nColor) {
    case 1:
      sColor = "lightyellow"
      sBorderColor = "gold"
      break;
    case 2:
      sColor = "lightgreen"
      sBorderColor = "gold"
      break;
    case 3:
      sColor = "lightblue"
      sBorderColor = "blue"
      break;
  } // end of switch
  let nTop = (evt.pageY+1)
  cntr.style.left = (evt.pageX-nHalfSize)+"px"
  cntr.style.height = (nTop+nSize+1)+"px"
  cntr.style.width = (nSize)+"px"
  //cntr.style.border = "solid red 1px"
  //s.push("")
  s.push("<div style="+Q)
  s.push("position:absolute;")
  s.push("cursor:pointer;")
  s.push("left:0px;")
  s.push("top:"+nTop+"px;")
  s.push("background:radial-gradient(#ffffff,"+sColor+", "+sBorderColor+");")
  s.push("width:"+(nSize)+"px;")
  s.push("height:"+(nSize)+"px;")
  s.push("border-radius:"+(nHalfSize)+"px;")
  s.push("border:solid "+sBorderColor+" .5px;")
  s.push("box-sizing:border-box;")
  s.push(Q)
  //s.push("")
  s.push(" data-ornamentid='"+nId+"'")
  s.push(" title='Remove Ornament'")
  s.push(">")
  // reflection div can go in here
  s.push("</div>")
  
  // hook
  s.push("<div class='hook1' style="+Q)
  s.push("left:"+(nHalfSize-5)+"px;")
  s.push("top:"+(nTop-5)+"px;")
  s.push(Q)
  s.push(" data-ornamentId='"+nId+"'")
  s.push("></div>")
  
  //s.push("")
  // string ornament is hanging from
  s.push("<div class='ornamentString' style="+Q)
  s.push("left:"+(nHalfSize-1)+"px;")
  s.push("height:"+(nTop)+"px;")
  s.push(Q)
  s.push(" id='ornString"+nId+"' ")
  s.push("></div>")
  
  //alert(s.join("\n"))
  cntr.innerHTML = s.join("")
  document.body.appendChild(cntr)
  
} // end of placeOrnament()



let sFlybyClassName="";
function doJarFlyBy(sId) {
  if (typeof activeAnimationIds[sId] === "string") {
    // animation still playing... so leave...
    if (activeAnimationIds[sId] === "active") return
  } // end if
  
  activeAnimationIds[sId] = "active"
  const s=[];
  const dt = new Date();
  sFlybyClassName = "jarFlyBy"+dt.getTime();
  //s.push("")
  let nLastAngle = 0;
  let nYOffset;
  
  let nSteps = 20;
  let incr = Math.floor(w / nSteps);
  let vp = Math.floor(h * .70);
  let ang = 0;
  let y = h - 130;
  let sc = .5;
  s.push("@keyframes "+sFlybyClassName+"_ani {")
  for (let n=0;n<nSteps;n++) {
    let nPct = Math.floor(n / nSteps * 100)
    let x = n * incr
    s.push(transForm({pct:nPct,x:x,y:y,a:ang,sc:sc}))
    nLastAngle = ang;
    
    ang = ang + 185
    
    if (x<w/2) {
      sc = sc + .022
    } else {
      sc = sc - .022
    } // end if/else
    
    if (x<w/4) {
      y = y - 6;
    }
    
    if (x>w *.75) {
      y = y + 6;
    }
            
  } // next n
  
  s.push(transForm({pct:100,x:w+60,y:h-130,a:ang,sc:sc}))
  
  /*
  s.push(transForm({pct:0,x:0,y:h-60,sc:.4}))
  s.push(transForm({pct:10,a:45,sc:.6}));
  s.push(transForm({pct:30,x:incr,y:h-200,a:45,sc:.6}));
  s.push(transForm({pct:50,x:incr*2,y:vp,a:90,sc:.7}));
  s.push(transForm({pct:70,x:incr*3,y:vp,sc:.6,a:90+45}));
  s.push(transForm({pct:100,x:w+55,y:h-30,sc:.5}));
  */
  s.push("}")
  s.push("")
  s.push("."+sFlybyClassName+"{")
  s.push("  position:absolute;")
  s.push("  left:0px;")
  s.push("  top:0px;")
  s.push("  display:block;")
  s.push("  animation:"+sFlybyClassName+"_ani 4s linear;")
  s.push("  animation-fill-mode:both;")
  s.push("}")
  let sStyle = s.join("\n")
  
  jarFlyByStylingNd.innerHTML = sStyle;
  
  const nd = document.getElementById(sId);
  nd.className = sFlybyClassName;
} // end of doJarFlyBy()



function transForm(params) {
  const s = [];
  const s2 = [];
  
  //pct, x, y, a, sc
  let pct = params.pct
  let x = params.x
  let y = params.y
  let a = params.a
  let sc = params.sc
  
  s2.push("  transform:")
  
  if (typeof x === "number" && typeof y=== "number") {
    s2.push("translate("+(x)+"px,"+(y)+"px)")
  } // end if
  
  if (typeof a == "number") {
    s2.push("rotate("+(a)+"deg)")
  } // end if
  
  if (typeof sc == "number") {
    s2.push("scale("+(sc)+")")
  } // end if
    
  let sTransform = s2.join(" ")+";"
  s.push("  "+(pct)+"% {")
  s.push(sTransform)
  s.push("  }")
  return s.join("\n")
} // end of trnsFm



function mouseMoved(evt) {
  if (nTimeoutId>-1) {
    clearTimeout(nTimeoutId)
    nTimeoutId = -1;
    beginThings()
  } // end if
} // end of mouseMoved()


function startOver(evt) {
  
} // end of startOver



function getNextStep() {
  if (nNextStep === steps.length-1) {
    // we already did the last step so do no more!
    nCurrentNextTimestamp = 10000; // time way off in future!
    return;
  } // end if
  
  nNextStep = nNextStep + 1;
  currentNextStep = steps[nNextStep];
  nCurrentNextTimestamp = currentNextStep.tm;
} // end of getNextStep()



function donePlaying(evt) {
  startOverBtnNd.style.display = "none"
  bottomStuffNd.style.display = "none"
  setTimeout(beginThings, 5000)
} // end of donePlaying()




let bPPcheckInProgress = false;
function checkingPlayPos(evt) {
  if (bPPcheckInProgress) return;
  
  bPPcheckInProgress = true;
  let nPlayPos = audio.currentTime;
  //nPlayPos = nPlayPos.toFixed(3)
  
  //playPosNd.innerText = nPlayPos+"";
  
  if (nPlayPos >= nCurrentNextTimestamp) {
    
    if (currentNextStep.op ==="nextLine") {
      getNextSongLine();
    } // end if
    
    if (currentNextStep.op ==="nextWord") {
      highlightNextWord();
    } // end if
    
    if (currentNextStep.op ==="showProp") {
      showProp(currentNextStep.id);  // think like a movie prop... not an object property! 
    } // end if
    
    getNextStep();
    
  } // end if
  
  bPPcheckInProgress = false;
  
} // end of checkingPlayPos()



function showProp(sId) {
  const prop = document.getElementById(sId)
  prop.className = "showProp"
  
} // end of showProp()



function getNextSongLine() {

  nNextSongLine = nNextSongLine + 1;
  let sLine1 = verses[nNextSongLine];
  let sLine2 = [];
  let words = sLine1.split(" ")
  let sLastWord = "";
  
  currentWordObjs = [];
  
  //sLine2.push("")
  for (let n=0;n<words.length;n++) {
    const wordObj = {};
    wordObj.word = words[n];
    wordObj.top = -1; // not set yet
    wordObj.left = -1; // not set yet
    currentWordObjs.push(wordObj);
    
    if (n>0 && sLastWord.indexOf("-")===-1) {
      sLine2.push("&nbsp;&nbsp;");
    } // end if
    
    sLine2.push("<span id='w"+n+"'>")
    let sWord = words[n];
    
    if (sWord.substr(sWord.length-1,1)==="-") {
      sWord = sWord.substr(0,sWord.length-1);
    } // end if
    
    sLine2.push(sWord)
    sLine2.push("</span>")
    sLastWord = words[n]
  } // next n
  
  songVerseNd.innerHTML = sLine2.join("");
  
  // figure out where the position of the bouncing ball is supposed to be above each word!
  // ****************************************************************************************
  for (let n=0;n<words.length;n++) {
    const wordNd = document.getElementById("w"+n)
    const wordObj = currentWordObjs[n];
    const rect = wordNd.getBoundingClientRect()
    wordObj.top = rect.top - 55; 
    wordObj.left = rect.left + Math.floor(wordNd.clientWidth - 55 / 2); 
  } // next n
  
  
  nNextWord = -1;
  highlightNextWord(); // do for the first word in the line!
  
} // end of getNextSongLine




function highlightNextWord() {
  nNextWord = nNextWord + 1;
  const wordNd = document.getElementById("w"+nNextWord)
  wordNd.className = "wrd1"
  
  if (nNextWord > 0) {
    const wordNd2= document.getElementById("w"+(nNextWord-1))
    wordNd2.className = "wrd2"
  } // end if
  
} // end of highlightNextWord




function beginThings() {
  nPlayingStatus = NOT_PLAYING;
  songVerseNd.className = "songVerse1"
  songVerseNd.innerHTML = "DECK THE HALLS!"
  startBtnNd.innerText = "START"
  bottomStuffNd.style.display = "block"
  nNextSongLine = -1;
  nNextStep = -1;
  
  opts = ['pbJar','gjJar']
  const idx = intRnd(1)
  doJarFlyBy(opts[idx])  
 
  startOverBtnNd.style.display = "none";
} // end of beginThings()



function buttonClicked() {
  switch(nPlayingStatus) {
    case NOT_PLAYING:
      startPlaying();
      break;
    case PLAYING:
      pausePlaying();
      break;
    case PAUSED:
      resumePlaying();
      break;
  } // end switch
  
} // end of buttonClicked



function startPlaying() {  
  pbJarNd.className = "hideMe";
  gjJarNd.className = "hideMe";
  //startOverBtnNd.style.display = "inline";
  songVerseNd.className = "songVerse2"
  getNextSongLine()
  startBtnNd.innerText = "PAUSE"
  getNextStep();
  audio.play();
  nPlayingStatus=PLAYING
} // end of startPlaying



function pausePlaying() {
  startBtnNd.innerText = "RESUME"
  audio.pause();
  nPlayingStatus=PAUSED
} // end of pausePlaying()


function resumePlaying() {
  startBtnNd.innerText = "PAUSE"
  audio.play();
  nPlayingStatus=PLAYING
} // end of resumePlaying()


function intRnd(nMax) {
  return Math.floor(Math.random() * nMax);
} // end of function intRnd()

window.addEventListener("load", pageSetup)
window.addEventListener("resize", pageResize)

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

This Pen doesn't use any external JavaScript resources.