  var TableBorder  = 0
  var TableSpacing = 0
  var TablePadding = 0
  var PlayerMovesWidth = 43;
  var StartLevel = 0
  var ShowDebug = false
  var DEBUG =false;
  var ShowLRowNumbers = true
  var ShowRRowNumbers = true
  var ShowTColNumbers = true
  var ShowBColNumbers = true
  var PlayMode = "play"
  var IdleTime = 5
  var ShouldShowPathCalculation = true
  var preloaded = new Array();

  var imageSets = new Array("simple","woop","flah")
  var imageSet = imageSets[1]

  var mapNames = new Array();
  var rowincs = new Array(0,1,0,-1)
  var colincs = new Array(1,0,-1,0)
  var directions = new Array(">","v","<","^");
  
  var PC_void   = "#"
  var PC_wall   = "O"
  var PC_empty  = " "
  var PC_target = "x"
  var PC_block  = "+"
  var PC_bot    = "*" // block on target
  var PC_player = "o"
  var PC_pot    = "8" // player on target

  var boardImageMapping = new Array(PC_void,PC_wall,PC_empty,PC_target,PC_block,PC_bot,PC_player,PC_pot,"undefined");
  var boardImages       = new Array()
  boardImages[PC_void]     = "void.gif"
  boardImages[PC_wall]     = "wall.gif"
  boardImages[PC_empty]    = "empty.gif"
  boardImages[PC_target]   = "target.gif"
  boardImages[PC_block]    = "block.gif"
  boardImages[PC_bot]      = "block_on_target.gif"
  boardImages[PC_player]   = "player.gif"
  boardImages[PC_pot]      = "player_on_target.gif"
  boardImages["undefined"] = "empty.gif"

  var boardInited       = false;
  var doneLastMove      = false;
  var gameWasWon        = false
  var movesToDo         = ""
  var started           = true;
  var theMaps;
  var theMap
  var theBoard
  var playerPos
  var playerMoves
  var playerPushes = new Array();
  var VIS_UNVISITED=-1
  var VIS_BLOCKED=-2
  var VIS_STARTPOINT=0
  var visb;
  var solutionMove


  function board()
  {
    checkTheMap();
    for(var r=0;r < theMap.rows;r++)
    {
      this[r] = theMap[r]
    }
  }
  function point(row,col)
  {
    this.r = row
    this.c = col
  }
  function object()
  {
  }

  function visited_board(rows,cols)
  {
    var columns;
    var piece;
    var vis;
    var r
    var c
    for(r=0;r< rows; r++)
    { 
      columns = new Array();
      this[r] = columns;
      for(c=0;c< cols; c++)
      {
        piece = getPiece(r,c); 
        if(isPlayer(piece))
        {
          vis = VIS_STARTPOINT;
        }
        else if(isEmpty(piece))
        {
          vis = VIS_UNVISITED;
        }
        else
        {
          vis = VIS_BLOCKED;
        }
        columns[c] = vis;
      }
    }
  }


  ///////////////////////////////////////////////////
  //stuff for defining maps
  ///////////////////////////////////////////////////

  var definingMap
  var definingLevel     = -1
  var definingRow       = -1
  var definingMapName   = ""
  var maxDefinedColumns = 0
  var maxColumns        = 0
  var maxRows           = 0
  var numberOfMaps      = 0


  var levelSets = new Array();
  var levelSetNames = new Array()
  var definingLevelSet

  function defineLevelSet(name)
  {
    levelSetNames[levelSetNames.length] = name
    definingLevelSet = new Array()
    levelSets[name] = definingLevelSet
    defineLevel0()
  }
  
  function defineLevel0()
  {
    numberOfMaps = 0;
    maxDefinedColumns = 0
    maxRows = 0
    defineLevel("Select a Level")
    addRow("OOOOOOOOOOOOOOOO")
    addRow("O#**########*##O")
    addRow("O*##########*#*O")
    addRow("O#*####**###**#O")
    addRow("O##*##*##*##*#*O")
    addRow("O**####**###*#*O")
    addRow("O##############O")
    addRow("O######**######O")
    addRow("Oxxx##*##*##xxxO")
    addRow("O######**######O")
    addRow("O*#############O")
    addRow("O*#############O")
    addRow("O**###***##***#O")
    addRow("O*o*#*##*##*##*O")
    addRow("O**###**#*#*##*O")
    addRow("OOOOOOOOOOOOOOOO")
    finishLevel()
  }

  function defineLevel(newLevel)
  {
    definingLevel=numberOfMaps;

    if(typeof newLevel == "number")
    {
      definingMapName = "Level " + newLevel
    }
    else
    {
      definingMapName = newLevel
    }
    definingMap = new object()
    definingLevelSet[definingLevel] = definingMap
    definingRow = 0;
    maxDefinedColumns = 0
    numberOfMaps++;
  }

  function setSolution(lev,sol)
  {
    definingLevelSet[lev].solution=sol
  }

  function addRow(rowdata)
  {
    definingMap[definingRow] = rowdata
    definingRow++
    if(rowdata.length > maxDefinedColumns)
    {
      maxDefinedColumns = rowdata.length
    }   
  }

  function finishLevel()
  {
    definingMap.rows=definingRow
    if(definingRow >  maxRows)
    {
      maxRows = definingRow
    }
    definingMap.cols=maxDefinedColumns
    if(maxDefinedColumns > maxColumns)
    {
      maxColumns = maxDefinedColumns
    }
    definingMap.name=definingMapName
    definingMap.solution=""
    definingMap.index = definingLevel
    mapNames[definingLevel] = definingMapName
  }

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

  function getImgURL(imgname)
  {
    if(typeof imgname == "number" || !isNaN(parseInt(imgname)))
    {
      return "images/"+ "n" + imgname + ".png"
    }
    else
    {
      return imageSet + "/" + imgname
    }
  }

  function findPath(from,to)
  {
    checkTheMap();
    visb = new visited_board(theMap.rows,theMap.cols)
    var new_marked_squares
    var last_marked_squares
    var len = 1
    var p
    last_marked_squares = markAround(playerPos,to,len,"undefined")
    if(typeof last_marked_squares.length == "undefined")
    {
      return false;
    }
    while (last_marked_squares.length > 0)
    {
      len++;
      new_marked_squares = "undefined"  
      for(var i = 0 ; i < last_marked_squares.length;i++)
      {
        p = last_marked_squares[i];
        new_marked_squares = markAround(p,to,len,new_marked_squares);
        if(typeof new_marked_squares.length == "undefined")
        {
          return followFoundPath(from,to,len);
        }
      }
      last_marked_squares = new_marked_squares
    }
    return false;
  }
  function checkTheMaps()
  {
    if(!started)
    {
      startSokoban();
    }
    if(!theMaps) 
    {
      theMaps=new Array();
      changeLevelSet(levelSetNames[0]);
    }
  }
  function checkTheMap()
  {
    if(!theMap)
    {
        loadBoard(document.forms[0].selectLevel.selectedIndex);
    }
  }
  function refreshBoard()
  {
    checkTheMap();
    for(var r=0;r< theMap.rows ; r++)
    {
      for(var c=0;c< theMap.cols ; c++)
      {
         setCellImage(r,c, getPiece(r,c))
      }
    }
  }

  function won()
  {
    checkTheMap();
    var piece;
    for(var r=0;r< theMap.rows ; r++)
    {
      for(var c=0;c< theMap.cols ; c++)
      {
        piece = getPiece(r,c);
        if(piece == PC_target || piece == PC_pot)
        {

          return false;
        }
      }
    }

    return true;
  }

  function markAround(from,to,n,marked)
  {

    var p
    var vis
    var ok = false
    var columns
    if(marked == "undefined")
    {
      marked = new Array();
    }

    for(var i=0;i< 4;i++)
    {
      p = getPointInDir(from,i);

      if(pointIsValid(p))
      {
        columns = visb[p.r]
        vis = columns[p.c]
        if(vis == VIS_UNVISITED)
        {
          columns[p.c] = n;
          if(ShouldShowPathCalculation)
          {
            setCellImage(p.r,p.c,n);
          }
          marked[marked.length] = p
          if(p.r == to.r && p.c == to.c)
          {
            return p;
          }
        }
      }
    }
    return marked
  }
   
  function followFoundPath(from,to,len)
  {
    var pa;
    var dirs = ""
    var pb = to
    for(var i=len-2;i>=0;i--)
    {
      for(var whichway=0;whichway<=4;whichway++)
      { 

       if(whichway == 4)
        {
          //something has gone wrong, just return without doing any moves
          return;
        }
        pa=getPointFromDir(pb,whichway);

        if(pointIsValid(pa) && visb[pa.r][pa.c] == i+1)
        {

          break;
        }
      }
      dirs = directions[whichway] + dirs;
      pb = pa;
    }
    dirs = directions[getDirFromTo(from,pb)] + dirs;
    movesToDo = dirs
  }

  function getDirFromTo(from,to)
  {
    for(var i=0;i<4;i++)
    {
      if(from.r + rowincs[i] == to.r && from.c + colincs[i] == to.c)
      {
        return i;
      }
    }
    return -1
  }
  function getPointInDir(from,dir)
  {
    return new point(from.r+rowincs[dir],from.c+colincs[dir]);
  }
  function getPointFromDir(to,dir)
  {
    return new point(to.r-rowincs[dir],to.c-colincs[dir]);
  }
  function getDirForMove(move)
  {
    for(var i=0;i< directions.length;i++)
    {
      if(directions[i] == move)
      {
        return i;
      }
    }
    return -1;
  }
  function undoLastMove()
  {
    var lastMoveNum = playerMoves.length-1;
    if(lastMoveNum < 0)
    {
      return;
    }
    var lastMove = playerMoves.substring(playerMoves.length-1,playerMoves.length)
    var wasPush = playerPushes[lastMoveNum];
    var blockedPoint;
    var oldPlayerPos = new point(playerPos.r,playerPos.c)
    var dir = getDirForMove(lastMove);
    if(dir >= 0)
    {
      var lastPoint = getPointFromDir(oldPlayerPos,dir);
      if(pointIsValid(lastPoint))
      {
        if(isEmpty(getPiece(lastPoint.r,lastPoint.c)))
        {
          playerMoves = playerMoves.substring(0,lastMoveNum)
          vacatePiece(oldPlayerPos);
          takePiece(lastPoint);
          if(typeof wasPush == "boolean" && wasPush)
          {
            blockedPoint = getPointInDir(oldPlayerPos,dir);
            unblockPiece(blockedPoint);
            blockPiece(oldPlayerPos);
          }
          playerPos = lastPoint;
          gameWasWon = false
          updateMoves();
        } 
      }
    } 
  }
  function movePlayer(to,dir, wasPush)
  {
    playerPos = new point(to.r,to.c)
    playerPushes[playerMoves.length] = wasPush
    playerMoves = playerMoves + directions[dir]
    updateMoves();
  }

  function doMove(dir)
  {
    var to = getPointInDir(playerPos,dir);
    doMoveToPosition(to.r,to.c,true)
  }

  function click(row,col,userclick)
  {
    if(playingDemo() || playingSolution())
    {
      setPlayMode("play");
      return;
    }
    if(gameWasWon)
    {
      restartCurrentLevel();
    } 
    if(movesToDo != "")
    { 
      return;
    }
    doMoveToPosition(row,col,false)
  }
  function doMoveToPosition(row,col,adjacentOnly)
  {
    var to = new point(row,col)
    var piece = getPiece(row,col)
    if(isPlayer(piece))
    {
      return false;
    }
    else if(isEmpty(piece))
    {
      var dir = getDirFromTo(playerPos,to)
  
      if(dir != -1)
      {
        if(vacatePiece(playerPos) && takePiece(to))
        {
          movePlayer(to,dir,false)
        }
        else
        {
          return false;
        }
      } 
      else if(!adjacentOnly)
      {
        DEBUG=true;
        var ok = findPath(playerPos,to)
        DEBUG=false;
        if(!ok)
        {
          doneLastMove = true;
        }
        return ok;
      }
    }
    else if(isBlock(piece))
    {
      var dir = getDirFromTo(playerPos,to)
  
      if(dir != -1)
      {
        var pointTwoOver = getPointInDir(to,dir);
        if(pointIsValid(pointTwoOver))
        {
          var pieceTwoOver = getPiece(pointTwoOver.r, pointTwoOver.c);
          if(isEmpty(pieceTwoOver))
          {
            if(blockPiece(pointTwoOver) 
               && unblockPiece(to) 
               && vacatePiece(playerPos) 
               && takePiece(to))
            {
              movePlayer(to,dir,true)
              gameWasOne = won();
              if(gameWasOne)
              {
                userWonGame();
              } 
              return true;
            }
          }
        }
      }

    }
    return false;
  }
  function userWonGame()
  {
    checkTheMap();
    var newSolutions = playerMoves;
    var oldLength = theMap.solution == "undefined" ? 0 : theMap.solution.length;
    if(playerMoves.length < oldLength || oldLength < 1)
    {
      if(oldLength < 1)
      {
        msg = "You've found a solution."
      } 
      else 
      {
        msg = "You've found a better solution";
      }     
      alert("Congratulations! " + msg);
      theMap.solution=playerMoves
      updateMoves()
    }
  }
  function pointIsValid(p)
  {
    checkTheMap();
    return p.r >= 0 && p.c >= 0 && p.r < theMap.rows && p.c < theMap.cols
  }
  function takePiece(p)
  {
    var oldPiece = getPiece(p.r,p.c)
    if(oldPiece == PC_empty)
    {
      setPiece(p.r,p.c,PC_player)
      return true;
    }
    else if(oldPiece == PC_target)
    {
      setPiece(p.r,p.c,PC_pot)
      return true;
    }
    else 
    {
      alert("tried to take piece:" + oldPiece)
      return false;
    }
  }
  function unblockPiece(p)
  {
    var oldPiece = getPiece(p.r,p.c)
    if(oldPiece == PC_block)
    {
      setPiece(p.r,p.c,PC_empty)
      return true;
    }
    else if(oldPiece == PC_bot)
    {
      setPiece(p.r,p.c,PC_target)
      return true;
    } 
    else 
    {
      alert("tried to unblock piece:" + oldPiece)
      return false;
    }
  }
  function vacatePiece(p)
  {
    var oldPiece = getPiece(p.r,p.c)
    if(oldPiece == PC_player)
    {
      setPiece(p.r,p.c,PC_empty)
      return true;
    }
    else if(oldPiece == PC_pot)
    {
      setPiece(p.r,p.c,PC_target)
      return true;
    }
    else 
    {
      alert("tried to vacate piece:" + oldPiece)
      return false;
    }
  }
  function blockPiece(p)
  {
    var oldPiece = getPiece(p.r,p.c)
    if(oldPiece == PC_empty)
    {
      setPiece(p.r,p.c,PC_block)
      return true;
    }
    else if(oldPiece == PC_target)
    {
      setPiece(p.r,p.c,PC_bot)
      return true;
    }
    else 
    {
      alert("tried to block piece:" + oldPiece)
      return false;
    }
  }
  function isEmpty(piece)
  {
    return piece == PC_empty || piece == PC_target;
  }
  function isBlock(piece)
  {
    return piece == PC_block || piece == PC_bot;
  }
  function isPlayer(piece)
  {
    return piece == PC_player || piece == PC_pot;
  }

  function nextLevel()
  {
    var theCurrentLevel = document.forms[0].selectLevel.selectedIndex
    var ind = theCurrentLevel+1
    if(ind >= numberOfMaps)
    {
     ind = 0;
    }

    loadBoard(ind);
  }
  function prevLevel()
  {
    var theCurrentLevel = document.forms[0].selectLevel.selectedIndex
    var ind = theCurrentLevel-1
    if(ind < 0)
    {
     ind = numberOfMaps-1;
    }
    loadBoard(ind);
  }
  function getInitialBoardSquare(r,c)
  {
    checkTheMap();
    theMap[r].charAt(c)
  }
  
  function loadBoard(theNewLevel)
  {
    checkTheMaps();
    document.forms[0].selectLevel.selectedIndex = theNewLevel
    gameWasWon = false;
    var piece;
    theMap = theMaps[theNewLevel]
    for(var r=0;r < maxRows ; r++)
    {
      for(var c=0;c < maxColumns ; c++)
      {
        if(r >= theMap.rows || c >= theMap.cols)
        {
          piece = PC_empty
        }
        else
        {
          piece = theMap[r].charAt(c);
        }
        if(piece == PC_player || piece == PC_pot)
        {
          playerPos = new point(r,c)
        }
        setCellImage(r,c,piece);
      }
    }
    theBoard = new board();
    playerMoves=""
    solutionMove=-1
    updateMoves()
  }
  function updateMoves()
  {
    checkTheMap();
    var solution=theMap.solution
    var solutionMoves
    if(solution== "undefined" || solution.length < 1 )
    {
      solutionMoves="none"
    }
    else
    {
      solutionMoves=solution.length
    }
    var textCols = PlayerMovesWidth -3;
    document.forms[0].BestSolution.value="Best Solution: " + solutionMoves
    document.forms[0].PlayerMoves.value="Current: " + playerMoves.length 
    document.forms[0].PlayerMovesText.rows = (playerMoves.length / textCols)+1
    document.forms[0].PlayerMovesText.value = splitIntoLines(textCols,playerMoves) //playerMoves;
 
  }
  function splitIntoLines(cols,str)
  { 
    var newstr = "";

    var nlines = (str.length / cols) + 1
    for(var i=0;i< nlines ; i++)
    {
      newstr += str.substring(i*cols,cols*(i+1))
      if(i < nlines)
      {
        newstr += "\n";
      }
    }
    return newstr;
  }
  function setPiece(row,col,piece)
  {
    var old = theBoard[row]
    var newRow 
    var firstPart = old.substring(0,col) ;
    var lastPart = old.substring(col+1,old.length)
    theBoard[row] = firstPart + piece + lastPart
    setCellImage(row,col,piece);
  }
  var loadingImages = false;
  function preloadImages()
  {
    var url;
    var name;
    for(var i=0;i<boardImages.length;i++)
    {
      var index=boardImageMapping[i];
      preloaded[i] = new Image();
      url=getImgURL(boardImages[index]);
      preloaded[i].src = url;
    }
    for(var i=0;i<39;i++)
    {
      var ind = i+boardImages.length
      preloaded[ind] = new Image();
      preloaded[ind].src = getImgURL(i);
    }
    loadingImages=true;
  }
  function debug(str)
  {
    if(ShowDebug && DEBUG)
    {
      document.forms[0].debugText.value = str + "\n" + document.forms[0].debugText.value
    }
  }
  function setCellImage(row,col,name)
  {
    if(typeof name != "number")
    {
      name = boardImages[name];
    }
    document.images["cell_" + row + "_" + col].src = getImgURL(name)
  }
  function getPiece(row,col)
  {
    return theBoard[row].charAt(col)
  }
  function restartCurrentLevel()
  {
    checkTheMap();
    loadBoard(theMap.index)
  }
  function showMoves()
  { 
    showMovesWindow("CurrentMoves",playerMoves);
  }
  function showMovesWindow(title, moves)
  { 
    document.forms[0].PlayerMovesText.value = moves
  }
  function togglePlayMode(mode)
  {
    if(PlayMode== mode)
    {
      setPlayMode("play"); 
    }
    else
    {
      setPlayMode(mode);
    }
  }
  var settingPlayMode =  false
  function setPlayMode(mode)
  {
    if(settingPlayMode)
    {
      return;
    }
    settingPlayMode = true;
    PlayMode = mode;
    document.forms[0].RunDemo.checked = mode == "demo"
    if(mode == "demo" || mode == "solution")
    {
      restartCurrentLevel()
    }
    solutionMove = -1;
    settingPlayMode = false
  }

  function doSolutionMove(keepPlaying)
  {
    checkTheMap();
    if(solutionMove >= theMap.solution.length)
    {
      if(keepPlaying)
      {
        nextLevel();
        return;
      }
      else
      { 
        restartCurrentLevel()
        setPlayMode("play");
        return;
      }
    } 
    else
    {
      solutionMove++
    }
    var sol = theMap.solution
    var move = sol.charAt(solutionMove);
    var dir = getDirForMove(move)

    if(dir < 0) 
    {
      return;
    }

    newpoint = getPointInDir(playerPos,dir);

    doMoveToPosition(newpoint.r,newpoint.c,true)
  }
  function doIdle()
  {
    var nextIdleTime = IdleTime
    if(loadingImages)
    {
      var numLoadingImages=0;
      for(var i=0;i<preloaded.length;i++)
      {
        if(preloaded[i] && !preloaded[i].complete)
        {
          numLoadingImages++;
        }
      }
      message = numLoadingImages + " images left to load";
      nextIdleTime=1000;
      if(numLoadingImages == 0)
      {
        message = "Ready!"
        loadingImages=false;
        if(!boardInited)
        {
          changeLevelSet("BoxWorld")
          loadBoard(StartLevel);
          boardInited=true;
        }
        else
        {
          refreshBoard();
        }
      }
      document.forms[0].PlayerMovesText.value= message;
    }
    else if(settingPlayMode)
    {
      //do nothing
    }
    else if(playingDemo())
    {
      doSolutionMove(true);
    } 
    else if(playingSolution())
    {
      doSolutionMove(false);
    } 
    else if(movesToDo.length > 0)
    {
      var theMove = movesToDo.charAt(0);
      movesToDo = movesToDo.substring(1,movesToDo.length);
      doMove(getDirForMove(theMove))
      if(movesToDo.length < 1)
      {
        doneLastMove = true;
        nextIdleTime = 1
      }
    }
    else if(doneLastMove)
    {
      doneLastMove = false;
      if(ShouldShowPathCalculation)
      {
        //redraw all numbered squares
        refreshBoard();
      }
    }
    setTimeout("doIdle()",nextIdleTime)
  }
  function playingDemo()
  {
    return PlayMode == "demo"
  }
  function playingSolution()
  {
    return PlayMode == "solution"
  }
  function changeImageSet(name)
  {
    if(name == "other...")
    {
      name = prompt("Enter a url:",""); 
    }
    if(name == "undefined" || name == "")
    {
      return
    }
    imageSet = name;
    preloadImages();
  }
  function changeSpeed()
  {
    var oldSpeed = (1000 / IdleTime) 
    var newSpeed = prompt(
      "Please enter the number of steps per second (current is " + oldSpeed + ") :", oldSpeed
    );
    if(newSpeed == "undefined")
    {
      return;
    }
    newSpeed = parseInt(newSpeed);
    if(!isNaN(newSpeed))
    {
      IdleTime = (1000 / newSpeed)
    }
  }
  function changeLevelSet(name)
  {
    theMaps = levelSets[name];
    loadBoard(0)
  }

  function startSokoban()
  {
    started = true;
    preloadImages();
    doIdle();
  }


  ///////////////////////////////////////////////////
  // stuff for generating html, controls etc
  ///////////////////////////////////////////////////

  function q(stringtoquote)
  {
    return "'" + stringtoquote + "'";
  }
  function dq(stringtoquote)
  {
    return '"' + stringtoquote + '"'
  }
  function getImgTag(imgURL)
  {
    return "<img src='"+imgURL+"'>";
  }
  function writeln(str)
  {
    document.writeln(str);
  }
  function columnNumbers(cols)
  {
    writeln("<tr>");
    if(ShowLRowNumbers)
    {
      writeln(getEmptyCellTag());
    }
    for(var c=0;c < cols ; c++)
    {
      writeln(getNumberedCellTag(c));
    }
    if(ShowRRowNumbers)
    {
      writeln(getEmptyCellTag());
    }
    writeln("</tr>");
  }
  function getEmptyCellTag()
  {
    return "<td>"+getImgTag(getImgURL("empty.gif"))+"</td>";
  }
  function getNumberedCellTag(num)
  {
    return "<td>"+getImgTag(getImgURL(num))+"</td>";
  }
  function makeBoardTable(rows,cols)
  {
    writeln("<table");
    writeln(" border=" + q(TableBorder));
    writeln(" cellpadding=" + q(TablePadding));
    writeln(" cellspacing=" + q(TableSpacing));
    writeln(">");
    if(ShowTColNumbers)
    {
      columnNumbers(cols);
    }
    for(var r=0;r < rows ; r++)
    {
      writeln("  <tr>");
      if(ShowLRowNumbers)
      {
        writeln(getNumberedCellTag(r));
      }
      for(var c=0;c < cols ; c++)
      {
        var imgname = getImgURL(boardImages[PC_empty])
        var command = "javascript:click("  + r + "," + c + ")"
        var img = "<img name='cell_"+r + "_" + c +"' src='" + imgname + "'border='0'></img>"
        var link = "<a href='"+command+"'>" + img + "</a>";
        writeln("    <td>" + link + "</td>");
      }
      if(ShowRRowNumbers)
      {
        writeln(getNumberedCellTag(r));
      }
      writeln("  </tr>");
    }
    if(ShowBColNumbers)
    {
      columnNumbers(cols);
    }
    writeln("</table>");
  }
  function checkBox(name,isChecked,description,handler,extraHTML)
  {
    writeln(
      '<input type="checkbox" name=' + dq(name)
      + (isChecked ? " checked " : "") 
      + (handler == "" ? "" : ' onClick='+ dq(handler))
      + '>'+description+extraHTML 
    );
  }
  function button(name,value,handler,extraHTML)
  {
    writeln(
      '<input type="button" name='+ dq(name)
      + (handler == "" ? "" : ' onClick='+dq(handler))
      + 'value =' +dq(value) + '>' +extraHTML
    );
  }
  
  function selectBox(name,value,values,handler,otherOption)
  {
    writeln("<select name ='" + name + "' " + (handler="" ? "" : "onChange='"+handler+"'")+">");
    for(var o=0;o<values.length;o++)
    {
      writeln("<option value='"+values[o]+"' " + (value == values[o] ? "selected" : "") + ">"+values[o]);
    }
    if(otherOption != "")
    {
      writeln("<option value='"+otherOption+"'>"+otherOption)
    }
    writeln("</select>");
  }
