    function checkForConsole() {
        // the commented-out code doesn't work yet (crashes the script)...
      if (!window.console) {
        window.console = new Object();
        //      window.console.diagnosticsBlock = document.createElement("div");
        //      window.console.diagnosticsBlock.id = "diagnosticsBlock";
        //      body.insertBefore(diagnosticsBlock, body.firstChild);

        window.console.log = function (msg) {
          //window.console.diagnosticsBlock.textContent += '<p>' + msg + '</p>';
        };
        window.console.group = function (grpName) {
          // nothing yet
        };
        window.console.groupEnd = function () { };
        window.console.info = function () { };
        console.log("console initialized");
      }
      else {
        console.log("console already exists!");
      }
    }
    
    function addFloatbreaker(container) {
    floatbreaker = document.createElement("span");
    floatbreaker.className = "floatbreaker";
    container.appendChild(floatbreaker);
    }
    
    function inValidTestSection()
    {
      var valid = false;
    var testSections = "boating|drowning";
        var testSectionList = testSections.split("|");
      var body = document.getElementsByTagName("body")[0];
      var currentSection = body.attributes["class"].value;
      if(testSectionList.indexOf) {
        valid = testSections.length == 0 || testSectionList.indexOf(currentSection) != -1;
      } else {
        for(x in testSectionList) {
          if(testSectionList[x] == currentSection) {
            valid = true;
            break;
          }
        }
      }
    //console.log("section '%s' is one of the valid test sections? %s", body.attributes["class"].value, valid);
      
      return valid;
    }

    function create2ColumnLayout() {
      checkForConsole();
      try {
          //if (!inValidTestSection()) return; // we're restricting this to the "Actions" section for testing
          
//      console.log("section matches; will try to create 2-column layout..."); // **********

      // get the element containing the content to be split
          var contentBlock = document.getElementById("bodyText");
          //console.log("content block: " + contentBlock);
          var node;
//          if(console.group) console.group("child nodes"); // **********
          
          // create an array to hold the top-level "contentSections" (which will be the containers for the columns)
          var sections = new Array();
          var tablesBySection = new Array(); // the array of arrays of tables for each section; must be the same length as 'sections' array
          var tables = null; // the array of tables within each section; generally empty or null, since tables are a realtive rarity
          var tableIndex = 0;
          
          var newDiv; // <div> element to hold the split content; there will be one for each top-level 'content section'
          var newCol; // <div> element for left and right columns; there will generally be one of each of these in each of the content sections
          var floatbreaker;
          //node.parentNode.insertBefore(newDiv, node);
          
          // go through the child nodes of the original container element
          var elem;
          //console.log("contentBlock has %i child nodes", contentBlock.childNodes.length);
          while (contentBlock.childNodes[0]) {
              elem = contentBlock.removeChild(contentBlock.childNodes[0]);
              //console.log("moving " + elem);
              if(newDiv == null && elem.nodeName == "#text") continue; // not sure... something to do with skipping blank text?
              //if (elem.tagName && elem.tagName.toLowerCase() == "h2") { console.log("IT'S AN H2!! '" + elem.innerHTML + "'"); }
              
              // if this is the first pass through the loop, or when an <h2> tag is encountered,
              // create a new top-level "contentSection" <div> and add it to the "sections" array
              // (which is an in-memory storage for these divs, to be be added to the page later)
              if (sections.length == 0 || (elem.tagName && elem.tagName.toLowerCase() == "h2")) {
                  newDiv = document.createElement("div");
                  newDiv.className = "contentSection";
                  if (elem.className == "noSplit") newDiv.className += " noSplit";
                  sections.push(newDiv);
                tables = elem.className != "noSplit" ? new Array() : null;
                tablesBySection.push(tables);
                  newCol = elem.className == "noSplit" ? newDiv : addColumn(newDiv, "R");
              }
              if (elem.tagName && elem.tagName.toLowerCase() == "h2") {
                newDiv.insertBefore(elem, newDiv.firstChild);
              }
              else if (!window.doNotExtractTables && (elem.tagName == "TABLE" && elem.className != "inline") && tables != null) {
                elem.id = "extractedTable_" + tableIndex;
//                console.log("found a table & assigned id %s", elem.id);
                tables.push(elem);
                elem = document.createElement("p");
                newCol.appendChild(elem);
                elem.className = "tableReference";
                elem.innerHTML = "see Table " + (tableIndex+1) + ", above";
                tableIndex++;
              }
              // the assumption here is that 'bare' #text nodes will not be copied across; this should
              // be safe, given that Umbraco does not write bare nodes into the content
              else if (elem.nodeName != "#text") {
                newCol.appendChild(elem);
              }
          }
//          if(console.groupEnd) console.groupEnd(); // group "child nodes" // **********
          //console.group("sections");
          for (var i = 0; i < sections.length; i++) {
              contentBlock.appendChild(sections[i]);
              finishContentSection(sections[i], tablesBySection[i]);
             }
      //console.groupEnd();
      } catch (ex) {
          alert("Caught the following exception:\n\n" + ex);
      }
      // this script messes up the display of URLs contain 'hashes' to anchor tags; calling this method 
      // scrolls to the named anchor after the page geometry has been rewritten.
      navigateToAnchor();
      return;
  }
  
  function navigateToAnchor()
  {
    if(document.location.hash.length > 0) {
        location.hash = document.location.hash;
    }
  }

  function finishContentSection(container, tables) {
    if (container.className.indexOf("noSplit") < 0) {
      //if(console.group) console.group("finishing content section");
      var rightCol = container.getElementsByTagName("div")[0];
      var leftCol = addColumn(container, "L");
      //console.log("about to split elements for section: %s", container);
      splitElements(leftCol, rightCol);
      if (!window.doNotExtractTables && tables && tables.length > 0) {
//        console.log("will re-insert %i extracted tables", tables.length);
        var tableDiv = document.createElement("div");
        if (container.childNodes.length > 1) {
          container.insertBefore(tableDiv, container.childNodes[1]);
        } else {
          container.appendChild(tableDiv);
        }
        tableDiv.className = "extractedTables";
        var t = null;
        var c = null;
        for (var i = 0; i < tables.length; i++) {
            t = tables[i];
            tableDiv.appendChild(t);
            c = document.createElement("caption");
            t.appendChild(c);
            c.innerHTML = "Table " + (parseInt(t.id.substring(t.id.lastIndexOf("_")+1))+1);
          }
      }
    }
    addFloatbreaker(container);
    //if(console.groupEnd) console.groupEnd();
}
  
  function addColumn(container, colType) {
    var newCol = document.createElement("div");
    newCol.className = colType.toLowerCase().indexOf("r") == 0 ? "rightColumn" : "leftColumn";
    if(colType.toLowerCase().indexOf("r") == 0) {
      container.appendChild(newCol);
    } else {
      container.insertBefore(newCol, container.lastChild);
    }
    return newCol;
  }
  
  function splitElements(leftColumn, rightColumn)
  {
      var splitPattern = /<([^\s>]+)[^>]*?>\s*:::\s*<\/[a-z0-9]+>/im; // pattern that finds lines that contain nothing but 3 colons (":::"), the designated manual split indicator
      var result = splitPattern.exec(rightColumn.innerHTML);
      if (result && result.length > 1) {
          //console.log("result: %d (%i, %i)", result, result.length, result[0].length);
          var resultIndex = rightColumn.innerHTML.indexOf(result[0]);
          //console.log("resultIndex: %i", resultIndex);
          var remainderIndex = resultIndex + result[0].length;
          //console.log("remainderIndex: %i", remainderIndex);
          //console.log("remaining html: %d", rightColumn.innerHTML.substring(remainderIndex));
          leftColumn.innerHTML = rightColumn.innerHTML.substring(0, resultIndex);
          rightColumn.innerHTML = rightColumn.innerHTML.substring(remainderIndex);
          var listAttributes = null;
          if (result[1].toLowerCase() == "li") listAttributes = findLastListAttributes(leftColumn);
          if (listAttributes) {
            //console.log("list type: %s", listAttributes[0]);
            leftColumn.innerHTML += "</" + listAttributes[0] + ">";
            rightColumn.innerHTML = "<" + listAttributes[0] + " start=\"" + (Number(listAttributes[1]) + 1) + "\">" + rightColumn.innerHTML;
          }
      //else {
          //  console.log("LIST ATTRIBUTES NOT FOUND");
          //}
          // console.log("right col HTML = %s", rightColumn.innerHTML);
      } else {
          //if(console.group) console.group("splitElements");
        var childCount = rightColumn.childNodes.length;
        //console.log("there are %i top-level nodes", childCount);
        var elem;
        switch (childCount) {
            case 0:
                // do nothing
                break;
            case 1:
            case 2:
                elem = rightColumn.removeChild(rightColumn.childNodes[0]);
                leftColumn.appendChild(elem);
                break;
            default:
                //console.log("left col = %d; right col = %d", leftColumn, rightColumn);
                //console.log("right col height = %f px", rightColumn.clientHeight);
                //if(console.group) console.group("right col contains %i elements", rightColumn.childNodes.length);
                var totalHeight = rightColumn.clientHeight;
                var splitPoint = totalHeight / 2; // a little naive, maybe; gives us the chance to change the logic to do a left-preferred or right-preferred balancing
                //console.log("split point = %f", splitPoint);
                var cumulativeHeight = 0;
                while(rightColumn.childNodes[0] != null) {
                  cumulativeHeight += rightColumn.childNodes[0].clientHeight;
                  //console.log("%s; height = %f px -> %f px", rightColumn.childNodes[0], rightColumn.childNodes[0].clientHeight, cumulativeHeight);
                  elem = rightColumn.removeChild(rightColumn.childNodes[0]);
                  leftColumn.appendChild(elem);
                  var ratio = leftColumn.clientHeight / rightColumn.clientHeight;
                  //console.log("ratio = %f (%f)", ratio, Math.round(ratio));
                  if(Math.round(ratio * 100) > 75) break;
                }
                break;
        }
    }
    var leftColumnNodeCount = leftColumn.childNodes.length;
    if(leftColumnNodeCount > 0) {
        var lastLeftNode = leftColumn.childNodes[leftColumn.childNodes.length-1];
        //console.log("last elem in left column is %d", lastLeftNode.tagName[0]);
        if(lastLeftNode.tagName.length == 2 && lastLeftNode.tagName.toLowerCase()[0] == 'h')
        {
            lastLeftNode = leftColumn.removeChild(lastLeftNode);
            var tempNodes = new Array();
            tempNodes.push(lastLeftNode);
            while(rightColumn.childNodes[0] != null) {
                tempNodes.push(rightColumn.removeChild(rightColumn.childNodes[0]));
            }
            for(var i=0; i < tempNodes.length; i++ )
            {
                rightColumn.appendChild(tempNodes[i]);
            }
        }
    }
    
    // isolate the top element of each col with a CSS class
    if (leftColumn.childNodes[0] != null) {
        leftColumn.childNodes[0].className += " top";
    }
    if(rightColumn.childNodes[0] != null) {
      rightColumn.childNodes[0].className += " top";
    }
    //if(console.groupEnd) console.groupEnd();
    //if(console.groupEnd) console.groupEnd();
}
  

  if(!window.oneColumnLayout)  // if the page-level variable saying not to create a 2-column layout has been defined, then skip all this
  {    
    if (window.addEventListener) {
      window.addEventListener('load', create2ColumnLayout, false);
    }
    else if (window.attachEvent) {
      window.attachEvent('onload', create2ColumnLayout);
        }
  }

  function findLastListAttributes(col) {
    var result = null;
    // console.group("findLastListAttributes");
    // console.log("number of elements in column: %s", col.childNodes.length);
    var listTagPattern = /[uo]l/img;
    var itemTagPattern = /li/img;
    var match;
    var listTag = null;
    var listItemCount = 0;
    for (var i = col.childNodes.length - 1; 0 <= i; i--) {
      // console.log("elem %i: %s", i, col.childNodes[i].tagName);
      match = listTagPattern.exec(col.childNodes[i].tagName);
      if (match != null) {
        listTag = String(match);
        // console.log("match: %s", listTag);
        var listItems = col.childNodes[i].childNodes;
        // console.log("list items: %i", listItems.length);
        // console.group("child nodes");
        for (var j = 0; j < listItems.length; j++) {
          if(listItems[j].tagName == "LI") listItemCount++;
          // console.log("item: %s", listItems[j].tagName ? listItems[j].tagName : "undefined");
        }
        // console.groupEnd();
        // console.log("%i list items, really", listItemCount);
        break;
      }
    }
    // console.groupEnd();
    return [listTag, listItemCount];
  }
  
