   var confirmActMessage = 'There\'s already a Jumble in progress, and\n' +
                           'processing this action will start a new one.\n' +
                           'Are you sure you want to do that?';

   function initialize(imageURL, force) {
      if (!imageURL) {
         var imgSelect = document.getElementById('verse');
         if (imgSelect) {
            imageURL = imgSelect.options[imgSelect.selectedIndex].value;
         }
      } 
      if ((typeof(inProgress) != 'undefined') && (inProgress) && (!force)) {
         if(!window.confirm(confirmActMessage)) return;
         startSeconds = new Date().valueOf();
         puzzleStop();
      } 
      else if ((typeof(inProgress) != 'undefined') && (inProgress)) {
         startSeconds = new Date().valueOf();
         puzzleStop();
      }
      document.getElementById('OuterBox').style.display = 'none';
      clearGrid();

      var theImage    = new Image();
      theImage.onload = imageLoaded;
      theImage.src    = imageURL;
      return true;
   }

   function imageLoaded() {
      var theWidth  = this.width;
      var theHeight = this.height;
      initialize.width = theWidth;
      initialize.height = theHeight;
      var theSrc = this.src;
      totalMoves = 0;
      updateMove();
      initPuzzle(theWidth, theHeight, theSrc);
   }

   function isFalse() {
      return false;
   }

   function clearGrid() {
      var theInnerBox = document.getElementById('InnerBox');
      var thePicBox = document.getElementById('PicBox');
      if (theInnerBox && thePicBox) {
         var children = thePicBox.childNodes;
         for (var i = (children.length - 1); i >= 0; i--) {
            var theBox = children[i];
            theBox.onmousedown = null;
            theBox.removeChild(theBox.firstChild);
            thePicBox.removeChild(theBox);
         }
         theInnerBox.removeChild(thePicBox);
      }
      numberOfBoxes = getDifficulty();
      gridSize = parseInt(Math.sqrt(numberOfBoxes));
      numberOfBoxes = parseInt(gridSize * gridSize);
      boxGrid = {};
      gridMap = [];
   }

   function initPuzzle(imgWidth, imgHeight, imgSrc) {
      var ratio = imgHeight / imgWidth;
      imgWidth = 5;
      imgHeight = parseInt(imgWidth * ratio);
      while ((imgHeight < 500) && (imgWidth < 500)) {
         imgWidth += 5;
         imgHeight = parseInt(imgWidth * ratio);
      } 

      while ((imgWidth  % gridSize) != (gridSize - 1)) imgWidth++;
      while ((imgHeight % gridSize) != (gridSize - 1)) imgHeight++;

      var theInnerBox = document.getElementById('InnerBox');
      var thePicBox   = document.createElement('div');

      thePicBox.style.width = (imgWidth + 2)  + 'px';
      thePicBox.style.height = (imgHeight + 2) + 'px';
      thePicBox.style.backgroundColor = getGridColor();
      thePicBox.style.margin  = '0px auto';
      thePicBox.style.border  = '0px';
      thePicBox.style.padding = '0px';
      thePicBox.style.position = 'relative';
      thePicBox.style.top = '0px';
      thePicBox.style.left = '0px';
      thePicBox.id = 'PicBox';

      for (var i = 0; i < numberOfBoxes; i++) {
         boxGrid['puzzleBox' + i] = {
                     id:'puzzleBox' + i,
            columnCount:((i < gridSize) ? i : (i % gridSize)) + 1,
               rowCount:parseInt(i / gridSize) + 1
                                    };
         var boxInfo = boxGrid['puzzleBox' + i];
         boxInfo.isLeftEdge   = (boxInfo.columnCount == 1) ? 1 : 0;
         boxInfo.isTopEdge    = (boxInfo.rowCount == 1) ? 1 : 0;
         boxInfo.isRightEdge  = ((boxInfo.columnCount % gridSize) == 0) ? 1 : 0;
         boxInfo.isBottomEdge = ((boxInfo.rowCount % gridSize) == 0) ? 1 : 0;
         boxInfo.width        = parseInt(imgWidth / gridSize);
         boxInfo.height       = parseInt(imgHeight / gridSize);

         if (!boxInfo.isLeftEdge) boxInfo.width++;
         if (!boxInfo.isTopEdge)  boxInfo.height++;

         if (!boxInfo.isTopEdge)  
            var aboveBox = boxGrid['puzzleBox' + (i - gridSize)];
         if (!boxInfo.isLeftEdge) 
            var leftBox  = boxGrid['puzzleBox' + (i - 1)];

         boxInfo.box = document.createElement('div');
         boxInfo.box.id = boxInfo.id + 'el';
         boxInfo.box.style.overflow = 'hidden';
         boxInfo.box.style.height = ((boxInfo.isTopEdge) 
                                      ? boxInfo.height : 
                                      boxInfo.height  - 1) + 'px';
         boxInfo.box.style.width  = ((boxInfo.isLeftEdge) 
                                      ? boxInfo.width
                                      : boxInfo.width - 1) + 'px';
         boxInfo.box.style.margin = '0px';
         boxInfo.box.style.padding = '0px';
         boxInfo.box.style.border = '0px';
         boxInfo.box.style.position = 'absolute';
         if (boxInfo.isTopEdge) boxInfo.box.style.top = '1px';
         else boxInfo.box.style.top = (parseInt(aboveBox.box.style.top) + 
                                       parseInt(aboveBox.box.style.height) +
                                       1) + 'px';
         if (boxInfo.isLeftEdge) boxInfo.box.style.left = '1px';
         else boxInfo.box.style.left = (parseInt(leftBox.box.style.left) + 
                                        parseInt(leftBox.box.style.width) +
                                        1) + 'px';

         boxInfo.img = document.createElement('img');
         boxInfo.img.title = 'Square ' + (i + 1) + '. Click to move';
         boxInfo.img.style.width = imgWidth + 'px';
         boxInfo.img.style.height = imgHeight + 'px';
         boxInfo.img.style.position = 'relative';
         boxInfo.img.style.margin = '0px';
         boxInfo.img.style.padding = '0px';
         boxInfo.img.style.visibility = 'inherit';
         boxInfo.img.src = imgSrc;
         if (boxInfo.isTopEdge) boxInfo.img.style.top = '0px';
         else boxInfo.img.style.top = '-' + 
                                      ((parseInt(aboveBox.box.style.top) - 1) + 
                                       (parseInt(aboveBox.box.style.height) + 1) + 'px');
         if (boxInfo.isLeftEdge) boxInfo.img.style.left = '0px';
         else boxInfo.img.style.left = '-' + 
                                       ((parseInt(leftBox.box.style.left) - 1) + 
                                        (parseInt(leftBox.box.style.width) + 1) + 'px');

         gridMap[i] = {currentBox:boxInfo.id,
                       originalBox:boxInfo.id,
                       top:boxInfo.box.style.top,
                       left:boxInfo.box.style.left,
                       isLeftEdge:boxInfo.isLeftEdge,
                       isTopEdge:boxInfo.isTopEdge,
                       isRightEdge:boxInfo.isRightEdge,
                       isBottomEdge:boxInfo.isBottomEdge,
                       rowCount:boxInfo.rowCount,
                       columnCount:boxInfo.columnCount};

         boxInfo.box.appendChild(boxInfo.img);
         thePicBox.appendChild(boxInfo.box);
      }

      theInnerBox.appendChild(thePicBox);
      document.getElementById('OuterBox').style.display = 'block';
      theInnerBox.onselectstart = isFalse;
      theInnerBox.style.MozUserSelect = 'none';
      initPuzzle.picLoaded = imgSrc;
   }

   function getPicSrc() {
      var thePicBox = document.getElementById('PicBox');
      if (!thePicBox) return '';
      return thePicBox.firstChild.firstChild.src;
   }

   function getPicWidth() {
      var thePicBox = document.getElementById('PicBox');
      if (!thePicBox) return '';
      return parseInt(thePicBox.firstChild.firstChild.style.width);
   }

   function getPicHeight() {
      var thePicBox = document.getElementById('PicBox');
      if (!thePicBox) return '';
      return parseInt(thePicBox.firstChild.firstChild.style.height);
   }

   function scramble(force) {
      if ((typeof(initPuzzle.picLoaded) == 'undefined') ||
          !initPuzzle.picLoaded) {
         initPuzzle.picLoaded = getPicSrc();
         var theWidth = getPicWidth();
         var theHeight = getPicHeight();
         if (theWidth && theHeight && initPuzzle.picLoaded) {
            clearGrid();
            initPuzzle(theWidth, theHeight, initPuzzle.picLoaded);
         }
      }
      if (!initPuzzle.picLoaded) {
         alert('Sorry, but you must first ' +
               'choose an image to scramble.');
         return;
      }
      if ((typeof(inProgress) != 'undefined') && (inProgress) && (!force)) {
         if(!window.confirm(confirmActMessage)) return;
         var theWidth  =
            parseInt(boxGrid[gridMap[0].currentBox].img.style.width);
         var theHeight =
            parseInt(boxGrid[gridMap[0].currentBox].img.style.height);
         clearGrid();
         initPuzzle(theWidth, theHeight, initPuzzle.picLoaded);
      }
      else if ((typeof(inProgress) != 'undefined') && (inProgress)) {
         var theWidth  = 
            parseInt(boxGrid[gridMap[0].currentBox].img.style.width);
         var theHeight = 
            parseInt(boxGrid[gridMap[0].currentBox].img.style.height);
         clearGrid();
         initPuzzle(theWidth, theHeight, initPuzzle.picLoaded);
      }
      var theBox = document.getElementById('PicBox');
      if (theBox)
         theBox.style.backgroundColor = 'white';
      for (var i = 0; i < numberOfBoxes; i++) {
         boxGrid[gridMap[i].currentBox].box.style.visibility = 'hidden';
      }
      setDark();
      setTimeout(function() { jumble(); 
                              puzzleStart(); 
                              toggleBG(); }, 100);
   }

   function updateMove() {
      var theMoveBox = document.getElementById('moveBox');
      if (theMoveBox) theMoveBox.firstChild.nodeValue = totalMoves;
   }

   function updateTime() {
      var totalMSSeconds = (new Date().valueOf()) - startSeconds; 
      var totalSeconds   = Math.floor(totalMSSeconds / 1000);

      var days     = Math.floor(totalSeconds / (60 * 60 * 24));
      totalSeconds = totalSeconds % (60 * 60 * 24);
      var hours    = Math.floor(totalSeconds / (60 * 60));
      totalSeconds = totalSeconds % (60 * 60);
      var minutes  = Math.floor(totalSeconds / 60);
      totalSeconds = totalSeconds % 60;

      if (totalSeconds < 10) totalSeconds = '0' + totalSeconds;
      var timeString = ':' + totalSeconds;

      if (days > 0) {
         if (hours < 10)   hours   = '0' + hours;
         if (minutes < 10) minutes = '0' + minutes;
         timeString = days + ':' + hours + ':' + minutes + timeString;
      }
      else if (hours > 0) {
         if (minutes < 10) minutes = '0' + minutes;
         timeString = hours + ':' + minutes + timeString;
      }
      else if (minutes > 0) {
         timeString = minutes + timeString;
      }

      var theTimerBox = document.getElementById('timeBox');
      if (theTimerBox) theTimerBox.firstChild.nodeValue = timeString;

      var resultString = timeString;
      var remainder = totalMSSeconds % 1000;
      if (remainder == 0) remainder = '000';
      else if (remainder < 10) remainder = '00' + remainder;
      else if (remainder < 100) remainder = '0' + remainder;
      resultString += '.' + remainder;
      return resultString;
   }

   function puzzleStart() {
      totalMoves = 0;
      inProgress = 1;
      startSeconds = new Date().valueOf();
      lastSeconds  = startSeconds;
      updateMove();
      updateTime();
      puzzleStop.timerTOID = setInterval(updateTime, 500);
      userLog = { startGrid:numberOfBoxes,
                    moveLog:'',
                  moveTimes:'' };
      for (var i = 0; i < numberOfBoxes; i++) {
         userLog.startGrid += 'z' + gridMap[i].currentBox.replace(/\D/g, '');
         var theBox = boxGrid[gridMap[i].currentBox];
         if (theBox.isDark) userLog.startGrid += 'D';
      }
   }

   function puzzleStop() {
      if (puzzleStop.timerTOID) {
         clearInterval(puzzleStop.timerTOID);
         puzzleStop.timerTOID = null;
      }
      inProgress = 0;
      return updateTime();
   }

   function doSlide() {
      var theBoxID = this.id.replace('el', '');
      slideBox(theBoxID, 0);
      return false;
   }
   
   function toggleBG() {
      var theBox = document.getElementById('PicBox');
      var theColor = getGridColor();
      if (theBox && theColor) 
         theBox.style.backgroundColor = theColor;
   }

   function slideBox(boxToMove, noToggle) {
      var boxInfo         = boxGrid[boxToMove];
      var darkPosition    = numberOfBoxes;
      var currentPosition = numberOfBoxes;
      for (var i = 0; i < numberOfBoxes; i++) {
         if (boxGrid[gridMap[i].currentBox].isDark) darkPosition = i;
         if (gridMap[i].currentBox == boxInfo.id)   currentPosition = i;
         if ((darkPosition < numberOfBoxes) &&
             (currentPosition < numberOfBoxes)) break;
      }
      if (currentPosition == darkPosition) return;
      if ((!gridMap[currentPosition].isTopEdge) &&
          ((currentPosition - gridSize) == darkPosition)) {
         swapBox(currentPosition, darkPosition, noToggle);
      }
      else if ((!gridMap[currentPosition].isBottomEdge) &&
               ((currentPosition + gridSize) == darkPosition)) {
         swapBox(currentPosition, darkPosition, noToggle);
      }
      else if ((!gridMap[currentPosition].isLeftEdge) &&
               ((currentPosition - 1) == darkPosition)) {
         swapBox(currentPosition, darkPosition, noToggle);
      }
      else if ((!gridMap[currentPosition].isRightEdge) &&
               ((currentPosition + 1) == darkPosition)) {
         swapBox(currentPosition, darkPosition, noToggle);
      }
      if (!noToggle) checkWin();
   }

   function swapBox(fromPosition, toPosition, noVisibilityToggle) {
      var tempID = gridMap[fromPosition].currentBox;
      gridMap[fromPosition].currentBox = gridMap[toPosition].currentBox;
      gridMap[toPosition].currentBox = tempID;
      if (noVisibilityToggle) return;

      var fromBox = boxGrid[gridMap[fromPosition].currentBox].box;
      var toBox   = boxGrid[gridMap[toPosition].currentBox].box;
      var tempX   = fromBox.style.left;
      var tempY   = fromBox.style.top;
      fromBox.style.left = toBox.style.left;
      fromBox.style.top  = toBox.style.top;
      toBox.style.left   = tempX;
      toBox.style.top    = tempY;
      if (typeof(totalMoves) != 'undefined') {
         totalMoves++;
         updateMove();
         if (userLog.moveLog) userLog.moveLog += 'z';
         userLog.moveLog += fromPosition;
         var currentSeconds = new Date().valueOf();
         var elapsed = currentSeconds - lastSeconds;
         lastSeconds = currentSeconds;
         if (userLog.moveTimes) userLog.moveTimes += 'z';
         userLog.moveTimes += elapsed;
      }
   }

   function checkWin() {
      var isWinner = 1;
      for (var i = 0; i < numberOfBoxes; i++) {
         if (gridMap[i].currentBox != gridMap[i].originalBox) {
            isWinner = 0;
            break;
         }
      }
      if (isWinner) {
         var totalTime = puzzleStop();
         for (var i = 0; i < numberOfBoxes; i++) {
            var theBox = boxGrid[gridMap[i].currentBox];
            theBox.box.onmousedown = null;
            if (!theBox.isDark) theBox.box.style.cursor = '';
            else { 
               theBox.box.style.visibility = 'visible';
               theBox.isDark = 0;
            }
         }
         reportResults(totalMoves, totalTime);
      }
   }

   function reportResults(totalMoves, totalTime) {
      alert('Congratulations!\n' +
            'You completed the puzzle in ' + totalMoves +
            ' moves,\nwith a time of ' + totalTime + '.');
   }

   function setDark() {
      var randSquare = Math.floor(numberOfBoxes*Math.random());
      var theBox = boxGrid[gridMap[randSquare].currentBox];
      theBox.isDark = 1;
      theBox.box.style.visibility = 'hidden';
   }

   function jumble() {
      var darkSquare = 0;
      for (var i = 0; i < numberOfBoxes; i++) {
         var theBox = boxGrid[gridMap[i].currentBox];
         if (theBox.isDark) {
            darkSquare = i;
            break;
         }
      }
      var maxMD = Math.max(calcMD(0,darkSquare),
                           calcMD((gridSize - 1), darkSquare),
                           calcMD((gridSize * 2), darkSquare),
                           calcMD((numberOfBoxes - 1), darkSquare));
      var validMDs = [];
      for (i = 1; i <= ((gridSize - 1) * 2); i++) {
         if (i > maxMD) break;
         if ((numberOfBoxes % 2) == 0) {
            if ((i % 2) != 0) validMDs[validMDs.length] = i;
         }
         else {
            if ((i % 2) == 0) validMDs[validMDs.length] = i;
         }
      }
      var randMD = validMDs[Math.floor(validMDs.length*Math.random())];
      var randStart = Math.floor(numberOfBoxes*Math.random());
      while (calcMD(randStart,darkSquare) != randMD) {
         randStart = Math.floor(numberOfBoxes*Math.random());
      }
      swapBox(darkSquare, randStart, 1);

      var remainingSquares = [];
      for (i = 0; i < numberOfBoxes; i++) {
         if ((i != randStart) &&
             (i != darkSquare)) 
            remainingSquares[remainingSquares.length] = i;
      }
      var lastSquare = darkSquare;
      while (remainingSquares.length > 0) {
         var arrayIndex = Math.floor(remainingSquares.length*Math.random());
         var nextSquare = remainingSquares[arrayIndex];
         swapBox(nextSquare, lastSquare, 1);
         lastSquare = nextSquare;
         remainingSquares.splice(arrayIndex,1);
      }

      for (var i = 0; i < numberOfBoxes; i++) {
         var theBox = boxGrid[gridMap[i].currentBox];
         theBox.box.style.top = gridMap[i].top;
         theBox.box.style.left = gridMap[i].left; 
         if (!theBox.isDark) {
            theBox.box.onmousedown      = doSlide;
            theBox.box.style.cursor     = 'pointer';
            theBox.box.style.visibility = 'visible';
         }
      }
   }

   function getDifficulty() {
      var theSelect = document.getElementById('difficulty');
      if (theSelect) {
         return theSelect.options[theSelect.selectedIndex].value;
      }
      else return 9;
   }

   function getDifficultyById(numBoxes) {
      if (numBoxes == 9) return 'Easy';
      if (numBoxes == 16) return 'Moderate';
      if (numBoxes == 25) return 'Difficult';
      if (numBoxes == 36) return 'Herculean';
   }

   function getGridColor() {
      var theSelect = document.getElementById('grid_color');
      if (theSelect) {
         return theSelect.options[theSelect.selectedIndex].value;
      }
      else return '#000000';
   }

   function getImageID() {
      return (initialize.uid) ? initialize.uid : '';
   }

   function getImageWidth() {
      return (initialize.width) ? initialize.width : '';
   }

   function getImageHeight() {
      return (initialize.height) ? initialize.height : '';
   }

   function calcMD(fromPos, toPos) {
      return (Math.abs(gridMap[fromPos].columnCount -
                       gridMap[toPos].columnCount) +
              Math.abs(gridMap[fromPos].rowCount - 
                       gridMap[toPos].rowCount));
   }

   function checkjs() {
      if (document.getElementById) {
         document.getElementById('jsOK').style.display = 'block';
         document.getElementById('noJS').style.display = 'none';
         if (window.location.search) {
            var ts = window.location.search.substring(1,window.location.search.indexOf("&"));
            var sc = window.location.search.substring(window.location.search.indexOf("&"),window.location.search.length);
                sc = sc.substring(1,sc.length);
                sc = sc.substring(0,sc.indexOf("&"));
            var ps = window.location.search.substring(window.location.search.indexOf("&"),window.location.search.length);
                ps = ps.substring(1,ps.length);
                ps = ps.substring((ps.indexOf("&")+1),ps.length);
            ts=ts.substring((ts.indexOf("=")+1),ts.length);
            sc=sc.substring((sc.indexOf("=")+1),sc.length);
            ps=ps.substring((ps.indexOf("=")+1),ps.length);
            if (((ts==9)||(ts==16)||(ts==25)||(ts==36)) &&
               ((sc==1)||(sc==0)) &&
               (ps)) {
               var theSelect = document.getElementById('difficulty');
               for (var i = 0; i < theSelect.options.length; i++) {
                  theSelect.options[i].selected = false;
                  if (theSelect.options[i].value == ts)
                     theSelect.options[i].selected = true;
               }
               theSelect = document.getElementById('grid_color');
               for (var i = 0; i < theSelect.options.length; i++) {
                  theSelect.options[i].selected = false;
                  if (i == sc)
                     theSelect.options[i].selected = true;
               }
               initialize(ps, 0);
            }
            else initialize('', 0);
         }
         else initialize('', 0);
      }
   }

   window.onload = checkjs; 
