/**
 * @fileOverview College Board UIL (User Interface Library) sandbox namespace.
 * These methods are general, but not yet clean enough to live inside the core library.  Therefore they are identified with the sandbox namespace to mark them as experimental production prototypes.
 * @author The iLab
 * @version 1.3.1
 * @todo get and set state with cookie.  need to hook the onload handler when each collapsible is created, is there a cleaner way?
 * - collapsible should toggle CSS classes, not physical styles
 * - ...should accept a callback function for special casing, so that I'm not special casing with things like modalHandler, but passing them in as prams
 * - CB.iterate should return a value?
 * - all interface functions should take a single JSON parameter?
 * @created This is Serious Cat version 0.1, created September 6, 2007.  Based upon previous libraries by Zand, Stevens, Porochnia &amp; Liu 2004-2006 and by Sussman 2005-2006.  Also depends upon open-source patterns by Matt Kruse and Robert Nyman.
 * @license MIT License
*/

/**
 * The sandbox method is a superclass for all of our UI modules under developmetnt.  As modules mature they should be migrated upward into {@link CB}.
 * @memberOf CB
 * @namespace sandbox
 */
CB.sandbox = CB.createObject( CB );

/**
 * Generic This Field Is Required HTML messages, for the form validator.  Includes CSS class name, tag type and an HTML template for.
 * @memberOf CB.sandbox
 * @namespace msg
 * @field
 */
CB.sandbox.msg = {};

/**
 * CSS class name for generic HTML error messages.
 * @memberOf CB.sandbox.msg
 * @name REQUIRED_FIELD_ERROR_CLASS
 * @constant
 */

CB.sandbox.msg.REQUIRED_FIELD_ERROR_CLASS = 'dhtmlErrorSpan';

/**
 * An HTML error message.
 * @memberOf CB.sandbox.msg
 * @name REQUIRED_FIELD
 * @constant
 * @requires CB.sandbox.msg#REQUIRED_FIELD_ERROR_CLASS
 */


CB.sandbox.msg.REQUIRED_FIELD = '<span class="'+ CB.sandbox.msg.REQUIRED_FIELD_ERROR_CLASS  +'" style="color: red;">This field is required!</span>';

/**
 * The kind of tag we will use to display error messages.
 * @memberOf CB.sandbox.msg
 * @name REQUIRED_FIELD_ERROR_NODENAME
 * @constant
 */


CB.sandbox.msg.REQUIRED_FIELD_ERROR_NODENAME = 'span';

/**
 * Checks that min page height is met If subnav or tools are taller than content, sets height of content to match that height.
 * @requires CB.get
 */
CB.sandbox.setPageHeight = function () {
	minPageHeight = 500;
	// reset content height to auto so we know how tall it really is without being modified
	if ( ! CB.get('content')) {
	  return false;
	}
	CB.get('content').style.height = "auto";

	var subNavHeight = ( CB.get('subNav') ? CB.get('subNav').offsetHeight : 0 ) ;
	var toolsHeight = ( CB.get('tools') ? CB.get('tools').offsetHeight : 0 ) ;
	var contentHeight = ( CB.get('content') ? CB.get('content').offsetHeight : 0 ) ;

	// check to see if the sub nav or tools are the tallest column
	if(subNavHeight > toolsHeight){
		tallestMod = subNavHeight;
	} else {
		tallestMod = toolsHeight;
	}

	if(tallestMod > contentHeight){
		CB.get('content').style.height = tallestMod + "px";
	} else {
		CB.get('content').style.height = "auto";
	}

	// see if the modified page height is less than the minimum page height
	if(CB.get('page')){
	  pageHeight = CB.get('page').offsetHeight;
	  if(pageHeight < minPageHeight){
	    heightDiff = minPageHeight - pageHeight;
	    newContentHeight = CB.get('content').offsetHeight + heightDiff;
	    CB.get('content').style.height = newContentHeight + "px";
	  }
	}
}

/**
 * Create a collapsible module from a container housing some content blocks, one of which (at least) is an H4 that the user can click to show/hide the other content blocks.
 * The first h4 found becomes the show/hide trigger.
 * @throws Fails Silently if there is no H4 within the container.
 *  @param {String} triggerId Should be the ID of a container.
 *  If the container has a child that is an h4, that will become the trigger,
 *  and the other child nodes will show/hide when the trigger is clicked
 *  if no h4 exists within the container, quit silently.
 *
 *  Note that this will NOT work unless all the CDATA within the container
 *  is wrapped in valid XHTML.  Bare text inside a DIV will NOT get hidden!
 *  @param {String} vShow CSS class name for content blocks when they are visible.
 *  @param {String} vHide CSS class name for content blocks when they are hidden.k
 * @requires CB.map
 * @requires CB.get
 * @requires CB.sandbox.createCollapsible
 * @requires CB.cookie.get
*/
CB.sandbox.makeCollapsible = function (triggerId, vShow, vHide){
  var that = this;
  var myCookieName = triggerId + this.toString();
  var container = this.get( triggerId  );
  if ( ! container ) return;
  var myTrigger = container.getElementsByTagName('h4')[0];  //take the first h4 and make it the trigger.
  if (! myTrigger) return;  // If no h4, do nothing.
  var myTargetList = [];

  this.map(getChildrenNotTrigger, container.childNodes);

  function getChildrenNotTrigger (el) {
    if ( that.hasChild(el, myTrigger) || el.style == null ) return;
    myTargetList.push(el);
  }
  this.createCollapsible(myTrigger, myTargetList, 'click', '', 'hidden', myCookieName, false, vShow, vHide, true);
  //this should be automatically added to the onload event queue
  if (this.cookie.get(myCookieName) == 'none') {
    myTrigger.onclick(); //this will fail if myTrigger is a string.  use CB.get() ?  also I've hardcoded the onclick method when its supposed to take any onmouse method, duh
    myTrigger.className = vHide;
  }
  else {
    myTrigger.className = vShow;
  }
}

  /**
   *  Lightweight or CSS or Web 2.0 Popup (Modal Dialog)
   *  Can only be closed by clicking on the close trigger.
   *  @param {Object} oConfig A JavaScript (JSON) hash of element IDs, like the one shown.
   *  @throws Fails silently if oConfig.trigger is not the ID of an existing DOM element.
   *  @example CB.sandbox.makeNonModalDialog({
   *                            trigger : 'clickingHereTriggersModal',
   *                            close : 'clickingHereClosesModal',
   *                            popup : 'elementToUseAsModal' ,
   *                            onaction : 'click'
   *                           });
   * @requires CB.cookie.get
   * @requires CB.get
   * @requires CB.sandbox.createCollapsible
   * @requires CB.toString
   */
  CB.sandbox.makeNonModalDialog = function ( oConfig ){
    var launchPopup = oConfig.trigger;
    var closePopup = oConfig.close;
    var thePopup = oConfig.popup;
    var onMouseAction = oConfig.onaction;
    var myTrigger = this.get(launchPopup);
    if ( ! myTrigger ) return;
    var myCookieName =  launchPopup + this.toString();
    var secondaryTrigger = document.getElementById(closePopup);
    var popup = this.get(thePopup);

    this.sandbox.createCollapsible(this.get(launchPopup), [ popup ], onMouseAction, '', '', myCookieName, true);
    this.sandbox.createCollapsible(this.get(closePopup), [ popup  ], onMouseAction, '', '', myCookieName);
    if (this.cookie.get(myCookieName) == 'block') {
      //this one's target starts out hidden
      myTrigger['on' + onMouseAction ]();
    }
 }

  /**
   *  LightBox (Modal Dialog)
   *  Can only be closed by clicking on the close trigger.
   *  Adds a transparent black layer that disables the interface.
   *  @param {Object} oConfig A JavaScript (JSON) hash of element IDs, like the one shown.
   *  @throws Fails silently if oConfig.trigger is not the ID of an existing DOM element.
   *  @example CB.sandbox.makeModalDialog({
   *                                       trigger : 'clickingHereTriggersModal',
   *                                       close : 'clickingHereClosesModal',
   *                                       popup : 'elementToUseAsPopup' ,
   *                                       transparentLayer : 'elementToUseAsTransparentLayer',
   *                                       onaction : 'click'
   *                                      });
   * @requires CB.cookie.get
   * @requires CB.get
   * @requires CB.sandbox.createCollapsible
   * @requires CB.toString
   */
    CB.sandbox.makeModalDialog = function ( oConfig ){
      var that = this;
      var launchPopup = oConfig.trigger;
      var closePopup = oConfig.close;
      var thePopup = oConfig.popup;
      var onMouseAction = oConfig.onaction;
      var theTransparentLayer = oConfig.transparentLayer;
      var myTrigger = document.getElementById(launchPopup);
      if (! myTrigger) return;
      var myCookieName =  launchPopup + this.toString();
      var secondaryTrigger = document.getElementById(closePopup);
      var popup = this.get(thePopup);
      this.createCollapsible(myTrigger, [ popup, this.get(theTransparentLayer)], onMouseAction, '', '', myCookieName);
      this.createCollapsible(secondaryTrigger, [ popup, this.get(theTransparentLayer) ], onMouseAction, '', '', myCookieName);

      /**
       * make sure the transparent layer always fills the viewport
       * or as close to that as is possible...
       */
      window.onresize = function () {
	var h = document.body.clientHeight;
	that.get(theTransparentLayer).style.height = h + 50 + 'px';
	var w = document.body.clientWidth;
	that.get(theTransparentLayer).style.width = w + 'px';
      }

      if (this.cookie.get(myCookieName) == 'block') {
	//this one's target starts out hidden
	myTrigger['on' + onMouseAction ]();
      }
    }



      /**
       * Factory method for producing instances of the Show/Hide DOM Element on Mouse Action pattern.
       * @param trigger An ID or reference to a DOM object that will become the trigger.
       * @param {Array} targetList List of IDs or references to DOM objects that will have a new CSS class applied to them when the user interacts with the trigger.
       * @param {String} onMouseAction Name of the mouse action the user should do to the trigger, in order to apply a new CSS class name to the target DOM elements.
       * @param {Deprecated} onClass This parameter is no longer used and should be removed.
       * @param {Deprecated} offClass This parameter is no longer used and should be removed.
       * @param {String} cookieName Name of the cookie that will be set in order to remember whether you prefer the target elements to be hidden or shown.
       * @param {Boolean} isNonModalDialog Set this to true when clicking anywhere on the page body should be the same as clicking the trigger.   This is useful, for instance, when clicking the popup won't close the window (trap events before they bubble up to the body) but the popup can be closed either by clicking on the close box within the popup OR on the page body.
       * @param {String} vShowClass CSS class name that makes DOM elements visible.
       * @param {String} vHideClass CSS class name that hides DOM elements.
       * @param {Boolean} checkPageHeight Set this to true if we should call {@link CB.sandbox.checkPageHeight} to correct the height of the content div.
       * @throws Fails silently if trigger is not the ID of an existing DOM element.
       * @requires CB.cookie.set
       * @requires CB.get
       * @requires CB.getEvent
       * @requires CB.iCanHasClass
       * @requires CB.iterate
       * @requires CB.map (Deprecated)
       * @requires CB.toggle
       * @requires CB.sandbox.setPageHeight
       */
      CB.sandbox.createCollapsible = function (trigger, targetList, onMouseAction, onClass, offClass, cookieName, isNonModalDialog, vShowClass, vHideClass, checkPageHeight) {
	var that = this;
	//given a list of triggers and a list of targets, execute a procedure on the targets when any of the triggers gets the specified mouse action
	var triggerEl = this.get(trigger);
	if (! triggerEl) return;
	var listOfTargets = [];
	var modalHandler = function () {};
	if (isNonModalDialog) {
	  modalHandler = function () {
	    //for non-modal dialogs, clicking anywhere on the page body will close the popup
	    document.body[ 'on' + onMouseAction ] = function (e) {
	      var event = that.getEvent(e);
	      if (event.target != triggerEl && event.target != document.getElementById(targetList[0]) && event.target != listOfTargets[0]) {  //hard coded style value:
		listOfTargets[0].style.display = 'none';
		that.cookie.set(cookieName, listOfTargets[0].style.display);
	      }
	    }
	  }
	}
	that.iterate(targetList, function (target, i) { listOfTargets.push(that.get(target[i])) }); //this is wonky, it should return the array, not work directly on listOfTargets
	triggerEl.style.cursor = 'pointer';
	triggerEl[ 'on' + onMouseAction ] = function () {
	  var count = 0;
	  //toggle the state of the targets, whenever the trigger gets the specified mouse action

	  if(triggerEl.className == vShowClass){
	    triggerEl.className = triggerEl.className.replace(vShowClass,vHideClass);
	  }
	  else{
	    triggerEl.className = triggerEl.className.replace(vHideClass,vShowClass);
	  }

	  that.map( toggleOnMouseAction, listOfTargets);

		if (checkPageHeight == true) {
			CB.sandbox.setPageHeight();
		}

	  //subroutine: toggle the state of the targets, whenever the trigger gets the specified mouse action
	  function toggleOnMouseAction (el) {
	    //			  console.debug('mouse action go', el, count+=1, count);
	    count+=1; //hack to hide select menus when the modal dialog is visible

	    el.style.display = ((el.style.display == '' || el.style.display != 'none') ? 'none' : 'block');  //wonky, should be done with class names, but I need to support multiple pre-existing names, especially this is needed so that the '+' icon can change to '-'
	    //so many stupid hacks to support a stupid hack :(
	    if ( that.iCanHasClass(el, 'stupidHide') ){  //hard-coded classname --fix!
	      el.className = el.className.replace(/stupidHide/,'');
	      el.style.display = 'block';
	    }
	    if (count == 1) {  //hack to fix the fact that this fires twice when the nonmodal window is open/closed, effectively doing nothing
	      that.map( toggleSelectMenu, document.getElementsByTagName('select'));
	    }
	  }
	  //should work via classname, not physical style?
	  that.cookie.set(cookieName, listOfTargets[0].style.display);
	}
	modalHandler();

	//subroutine: hide select menus in IE when popups are active
	function toggleSelectMenu (el) {
	  if (document.all && navigator.appVersion.match(/MSIE (\d)/)[1] < 7 && (el.id != 'viewAnother')) {
	    el.style.display = that.toggle(el.style.display, 'none', 'block');
	  }
	}
      }


      /**
       * Factory method for producing instances of the List Builder pattern.
       * @constructor
       * @class Representation of the HTML list builder pattern.
       * @param {String} lefthandListId ID or reference to the first 2 multiple-value select elements.
       * @param {String} righthandListId Second of 2 multiple-value select elements.
       * @param {String} leftBtnId ID or reference to the element to click in order to move selected option(s) from the second list to the first.
       * @param {String} rightBtnId Move selected option(s) from the first list to the second.
       * @throws Fails silently unless both lefthandListId and righthandListId are valid IDs or references to multiple-value select elements.
       * @returns {Object} A new ListBuilder object.
       */

	CB.sandbox.ListBuilder = function (lefthandListId, righthandListId, leftBtnId, rightBtnId) {
	  var that = this;
	  var lefthandList = this.get(lefthandListId);
	  var righthandList = this.get(righthandListId);
	  if (! lefthandList || ! righthandList) return;
	  /**
	   * Move selected option(s) from the first list to the second.
	   * @return {boolean} false
	   */
	  this.moveRight = function () {
	    //move option from left to right
	    return that.moveSelectedOptions(lefthandList, righthandList);
	  }
	  /**
	   * Move selected option(s) from the second multi-value select menu to the first.
	   * @return {boolean} false
	   */
	  this.moveLeft = function  () {
	    //move option from right to left
	    return that.moveSelectedOptions(righthandList, lefthandList);
	  }

	  /**
	   * Move selected options out of one multi-value select menu and into another.
	   * @param {Object} outOf Multiple-value select menu to move selected option(s) from.
	   * @param {Object} into Multiple-value select menu into which to move selected option(s).
	   * @returns {Boolean} false
	   * @see <a href="http://groups.google.com/group/comp.lang.javascript/browse_thread/thread/fd81d57e50f28b93/52f77fa69893d70c?lnk=gst&q=select+multiple&rnum=7#52f77fa69893d70c">cljs thread on hacking multiple SELECTs</a>
	   */

	  this.moveSelectedOptions = function (outOf, into) {
	    var fromList = outOf.options;
	    var toList = into.options;
	    for(var c = fromList.length; c--;){
	      if( fromList[c].selected  ){
		var accumulator = [];
		accumulator[0] = document.createElement('option');
		//insert the moved items in the same order, at the top of the target list
		into.insertBefore(fromList[c], into.firstChild);
	      }
	    }
	    return false;
	  }
	  //Set up event handlers for the new List Builder.
	  this.get(leftBtnId).onclick = this.moveLeft;
	  this.get(rightBtnId).onclick = this.moveRight;
	  return;
	}

	/**
	 * List builder wrapper so that we don't have to use the 'new' operator in configure.js
	 * When we create objects with 'new', then we have to explicitly set their prototype.
	 * @param {Object} oConf A JavaScript (JSON) hash that contains the parameters shown.
	 * @example CB.sandbox.createListBuilder({
         *                                        leftList : 'possibleCourses',
	 *                                        rightList : 'selectedCourses',
	 *                                        leftButton : 'removeFromPossibleCourses',
	 *                                        rightButton : 'addToPossibleCourses'
	 *                                       })
	 * @returns {Object} An instance of CB.sandbox.ListBuilder.
	 */

	  CB.sandbox.createListBuilder = function (oConf) {
	    var lefthandListId = oConf.leftList;
	    var righthandListId = oConf.rightList;
	    var leftBtnId = oConf.leftButton;
	    var rightBtnId = oConf.rightButton;
	    var lb = function(){ this.ListBuilder(lefthandListId, righthandListId, leftBtnId, rightBtnId) }
	    lb.prototype = this;  //hack
	    return new lb();
	  }

	  /**
	   *  Factory method for producing instances of the Hiearchical Tree Menu pattern.
	   * @constructor
	   * @class Representation of a hiearchical tree menu.
	   * @returns {Object} A new SeedTree object.
	   * @deprecated Never used in production and should be removed.
	   *
	   * note that in the comments 'item' always refers to an LI tag
	   * as in 'the clicked item' would be the LI tag that was last clicked
	   *
	   * applies the class .expanded_seedTree to the clicked item
	   * this class can be used to trigger the 'on' state of the expand/collapse icons for the nav
	   * @param {string} menuId The ID of the menu container.
	   * @param {string} expandedClassName  The class name given to menu items when they are expanded.
	   */
	    CB.sandbox.SeedTree = function ( menuId, expandedClassName ) {
	      var that = this;

	      /*
	      //this is neat-o, but does not work in Safari T.T
	      //set up the CSS
	      //For now just hard-code, since we'd be doing all this work to create just 1 style rule right now
	      //see Flanagan 5th ed. 16.6
	      var selector = '#'+ menuId + ' li ul';
	      //var expandedSelector = '#'+ menuId + ' .expanded_seedTree ';
	      //assume we have at least one stylesheet
	      if ( document.styleSheets[0].insertRule) {
	      //if not IE
	      var lastrule = document.styleSheets[0].cssRules.length;
	      document.styleSheets[0].insertRule(selector + '{display:none}', lastrule++);
	      //document.styleSheets[0].insertRule(expandedSelector + '{border-top: 1px solid red}', lastrule++);  //should be a pram
	      } else if ( document.styleSheets[0].addRule ) {
	      //if IE
	      var lastrule = document.styleSheets[0].rules.length;
	      document.styleSheets[0].addRule(selector, 'display:none', lastrule++);
	      //document.styleSheets[0].addRule(expandedSelector, 'border-top:1px solid red', lastrule++); //should be a pram
	      }
	      */

	      var container = this.get( menuId);  //this is fragile because we expect the ID of an el that /contains/ the top-level UL.  I think this may break if we pass instead the top-level UL itself...  but it works well enough with the IPE markup structure.

	      if (! container) {
		/**
		 * @ignore
		 * If we can't build a nav on this page,
		 * just define an empty version of the SeedTree's public methods
		 * so that even if the JSP is still calling those methods
		 * it won't do anything, not even cause an error.
		 */
		this.clickItem = function(){};  //this.clickItem() does nothing!
		return;
	      };

	      var menuList = container.getElementsByTagName('ul');
	      this.menu = menuList[0];

	      /**
		* call this to explicitly open or close a menu item
		* this could become a library method
	       */

	      this.clickItem = function ( itemToClick  ) {
		var itm = that.get(itemToClick);
		if (! itm) {
		  return;
		}
		var thatMenu = this.menu;
		that.findAncestor(itm, function(el){
		    if (el.nodeName == 'LI') {
		      simulateClick( el, thatMenu);
		    }
		  });
	      }
	      function simulateClick( itemToClick, menu) {
		if ( ! itemToClick.click ) {
		  //== Non-IE:
		  menu.onclick({target: itemToClick});
		} else {
		  //== IE:
		  itemToClick.click()
		}
	      }

	      /**
		* onclick handler for the top-level UL
		* only one event handler is required!
		* To simulate a click on the menu, use the clickItem method
	       */

	    this.menu.onclick = function(e) {
		var event = that.getEvent(e);
		var clickedItem = that.findAncestor( event.target, function (el) { return el.nodeName == 'LI'});
		var itemParent = that.findAncestor( clickedItem, function (el) { return el.nodeName == 'UL'});
		var childLists = clickedItem.getElementsByTagName('ul');

		that.toggleClassname(clickedItem, expandedClassName, '');

		if (childLists.length == 0) return;  //sanity check

		//open or close the child list of the clicked item
		//toggle all the children of the clicked item
		that.map(toggleMyChildren, childLists);

		//close any open siblings of the clicked item
		//this is a "find children" pattern analogous to findAncestor
		that.map(closeMySiblings, itemParent.childNodes);

		/* menu.onclick subroutines  */
		//subroutine: close any open siblings of the clicked item
		function closeMySiblings (el) {
		  if((el.nodeName != 'LI') || el == clickedItem) {
		    return;
		  }
		  that.removeClass( el, expandedClassName);
		  that.map(hideUL, el.childNodes);
		}
		/*
		 * subroutine: toggle all the children of the clicked element
		 * CSS: note that this works as written because
		 * the top-level node of the menu is hidden
		 * we override that on each child node of the clicked el
		 * so it's important that the 'show' class /overrides/ the 'hide' class
		 */
		function toggleMyChildren (el) {
		  if( el.parentNode == clickedItem ) {
		    el.style.display = that.toggle(el.style.display, '', 'block');
		  }
		}
		return false;
	      }

	      /**
	       *  Tree Navigation subroutine: if an el is a UL, hide it
	       */

	      function hideUL (siblingNode) {
		if (siblingNode.nodeName == 'UL') {
		  siblingNode.style.display='';
		  that.removeClass(siblingNode, 'expanded_seedTree');  //hard-coded classname -- fix!
		  //does the above line  even do anything???
		}
	      }
	    }

	    /**
	     * SeedTree tree nav builder wrapper
	     ** so that we don't have to use the 'new' operator in configure.js
	     ** when we create objects with 'new', then we have to explicitly set their prototype
	     * @deprecated Because we didn't need a dynamic tree menu.
	     */
	    //	CB.sandbox.createTree = function ( menuId, expandedClassName ) {
	      CB.sandbox.createTree = function ( oConf ) {
	        var menuId = oConf.menuId;
		var expandedClassName = oConf.expandedNodeClassName;

		var tn = function (){ this.SeedTree( menuId, expandedClassName )}
		tn.prototype = this;  //hack
		return new tn();
	      }

	      /**
	       *  Attach Unobtrusive Form Validation for required fields to the onSubmit handler of a form.
	       *  Please note that form fields that do not have name attributes will NOT be checked!
	       *  @memberOf CB.sandbox
	       *  @namespace formValidation
	       *  @see <a href="http://www.quirksmode.org/dom/error.html">PPK's unobtrusive validator script</a>
	       *  @throws Fails silently unless myForm is a valid ID or reference to a DOM element.
	       *  @param myForm ID or reference to a form element.
	       *  @requires CB.msg.REQUIRED_FIELD_ERROR_NODENAME
	       *  @requires CB.msg.REQUIRED_FIELD_ERROR_CLASS
	       */

		CB.sandbox.formValidation = function ( myForm ) {
		  var that = this;
		  var myForm = this.get( myForm );
		  if (! myForm) return;
		  /**
		   * Event handler attached to the form.
		   */
		  myForm.onsubmit = function () {

		    /**
		     * Anonymous factory for the Form validation dispatcher object.
		     * I wanted to document what the form validation module does.
		     * But I am not sure I chose the the proper way to document an anonymous object literal.
		     * @private
		     * @memberOf CB.sandbox.formValidation
		     * @name AnonymousOnSubmitHandler
		     * @function
		     */

		    return (function(){
			var isValid = true;
			var groupedFormElements = {};
			var oldErrorSpans = that.getElementsByClassName( myForm, that.msg.REQUIRED_FIELD_ERROR_NODENAME, that.msg.REQUIRED_FIELD_ERROR_CLASS );

			/*
			** if any form elements have warnings attached (from a previous attempt to submit)
			** then remove those warnings now
			*/
			that.map(function(el){ el.parentNode.removeChild(el) }, oldErrorSpans);

			/*
			** this loop does two separate tasks:
			** 1. write error messages next to invalid form elements, except checkboxes and radio buttons
			** 2. also populate the groupedFormElements object
			*/
			that.map(notifyOnError, myForm.elements);

			/*
			** write error messages next to invalid checkboxes and radio buttons
			*/
			for (var Id in groupedFormElements) {
			  if (groupedFormElements[Id] == false) {
			    handleValidationError(Id, false);
			  }
			}

			//cancel submit if any form fields are not valid
			if ( ! isValid) {
			  return false;
			}
			/* End of main logic.  Subroutines below here. */

			/**
			 * handle validation error
			 * @memberOf CB.sandbox.formValidation
			 * @name AnonymousOnSubmitHandler.handleValidationError
			 * @function
			 * @param elOrElId {ID or DOM element reference} A form element whose parentNode will have an error message injected into its innerHTML.  For IPE we wrapped all form/label pairs in containers so this approach was valid in that case.  Obviously it is not very portable.
			 * @param list {Boolean} This parameter should always be false.  It is a leftover from a previous, deprecated approach and should be removed.
			 * @param i {Deprecated} This parameter is no longer required or used.
			*/

			  function handleValidationError (elOrElId, list, i) {
			  var elId = (typeof elOrElId == 'string' ? elOrElId : elOrElId.id);
			  if (list == false) {  	// is list ever true???
			    if (CB.get(elId)) {
			      CB.get(elId).parentNode.innerHTML += that.msg.REQUIRED_FIELD;
			      //CB.get(elId).parentNode.innerHTML += '<span  style="color: red;">This field is required!</span>';
			    }
			  } else {
			    list[i].parentNode.innerHTML += that.msg.REQUIRED_FIELD;
			  }
			  isValid = false;
			}

			/**
			 * Write error messages next to invalid elements and populate the groupedFormElements object.
			 * @memberOf CB.sandbox.formValidation
			 * @name AnonymousOnSubmitHandler.notifyOnError
			 * @function
			 * @param el {DOM element reference} A form element to validate, either an input, radio button, checkbox or select menu.
			 * @param list A list of form elements to validate, usually MyForm.elements.
			 * @param i {Deprecated}  This parameter is no longer required or used.
			*/

			function notifyOnError(el, list, i) {
			  list[i].parentNode.style.border = 'none';  //why is this here?

			  if (that.iCanHasClass( el, 'notRequired' )) {
			    return;
			  }
			  if (el.nodeName == 'INPUT') {
			    if (el.type == 'text' || el.type == 'password') {
			      if ( ! el.value) {
				handleValidationError(el.id, false);
			      }
			    } else if (el.type == 'radio' || el.type == 'checkbox') {
			      if ( ! groupedFormElements[el.id]) {
				groupedFormElements[el.id] = el.checked;
			      }
			    }
			  } else if (el.nodeName == 'SELECT') {
			    if (el.selectedIndex < 1) {
			      handleValidationError(el.id, false);
			    }
			  }
			}
		      })();
		  }
		}



		/**
		 * Onclick Flyout Navigation
		 * This is currently used in conjunction with Son of Suckerfish CSS
		 * @see http://www.htmldog.com/articles/suckerfish/dropdowns/
		 * @constructor
		 * @deprecated Never used in production, although this was the pattern I preferrred (rather than onrollover flyouts).
		 */

		  CB.sandbox.flyoutNav = function ( elId ) {
		    var that = this;
		    var theNav = document.getElementById(elId);
		    var topLevelMenuNodes = theNav.getElementsByTagName('li');  //returns all the descendant LI's as well
		    topLevelMenuNodes = (function () {                          //so filter out the LI's that aren't children of theNav
			var accumulator = [];
			that.map(addChildrenToAccumulator, topLevelMenuNodes);
			return accumulator;

			//topLevelMenuNodes definition subroutine: push child nodes of el into the accumulator
			function addChildrenToAccumulator( el ) {
			  if (el.parentNode.id == elId) accumulator.push( el );
			}
		      })();

		    //hack to make the menu close when the background is clicked.
		    //doesn't close the child menus if they're open,
		    //but hey we're not planning on using this in production anyway
		    document.body.onclick = function() {
		      //var fakeEvent = {target:that.lastClickedMenuItem.getElementsByTagName('ul')[0]};
		      // that.lastClickedMenuItem.getElementsByTagName('ul')[0].onclick(fakeEvent);
		      if (that.lastClickedMenuItem != null) that.lastClickedMenuItem.getElementsByTagName('ul')[0].style.display='none';

		    }


		    that.map(assignEventHandlers, topLevelMenuNodes);
		    //subroutine:
		    function assignEventHandlers ( topLevelNode ) {
		      topLevelNode.onclick = clickHandler;
		      that.map(ULOnclick, topLevelNode.getElementsByTagName('ul'));

		      //sub-subroutine:
		      function ULOnclick (lowerLevelNode) {
			lowerLevelNode.onclick = function (e) {
			  var event = that.getEvent(e);
			  var subNavList = event.target.parentNode.getElementsByTagName('ul');
			  if (subNavList.length == 0) { //sanity check
			    alert('now imagine you are being redirected to the page named "'+ event.target.innerHTML +'"');
			    stopPropagation(event);
			  } else {
			    var subNav = subNavList[0];
			    if (subNav.style.display != 'block') { //need a  CB.isVisible method?
			      subNav.style.display = 'block'

				//sub-sub-subroutine:  hide el unless its the grandparent of the subNav
				function hideIfNotGrandparent ( el ){
				if (el != subNav.parentNode.parentNode && el != subNav) {
				  if(el.style.display == 'block') {
				    el.style.display = 'none';
				  }
				}
			      }
			      //close any other SUBnav items that may be open
			      that.map(hideIfNotGrandparent, event.target.parentNode.parentNode.parentNode.getElementsByTagName('ul'));
			    } else {
			      subNav.style.display = 'none';
			    }
			    stopPropagation(event);
			  }
			}
		      }
		    }

		    /**
		     * click handler subroutine
		    */

		    function clickHandler (e) {
		      that.lastClickedMenuItem = this;  //hack for making menu close when background clicked
		      var triggerEl = this;
		      var theSublist = this.getElementsByTagName('ul')[0];
		      if (theSublist.style.display != 'block') { //really need an CB.isVisible method and a hasClass method
			theSublist.style.display = 'block';
		      } else {
			theSublist.style.display = 'none';
		      }
		      that.map(hideChildren, topLevelMenuNodes);

		      stopPropagation(e);

		      /**
		       *  close any other nav items that may be open
		       * hide the children of any el that is not the current 'this'
		       */

		      function hideChildren ( el ){
			if ( el != triggerEl) {
			  that.map(hide, el.getElementsByTagName('ul'));
			}
		      }
		    }

		    /**
		     * another hide(), good candidate for a library method
		     */

		    function hide (el){
		      if(el.style.display == 'block') {
			el.style.display = 'none';
		      }
		    }

		    /**
		     * stop events from bubbling up subroutine
		     */

		    function stopPropagation(e) {
		      var event = that.getEvent(e);
		      if (event.stopPropagation) {
			event.stopPropagation();
		      } else {
			event.cancelBubble = true;
		      }
		    }
		  }

		  /**
		   * Onrollover Flyout Navigation for IE6-
		   * note that in all browsers but IE6- the onrollover flyout menus are pure css
		   * this is the Son of Suckerfish hack for IE6- init
		   * This only executes in IE 6 and lower.
		   * @see http://www.htmldog.com/articles/suckerfish/dropdowns/
		   * @param {string} navId The ID of the nested unordered list that is to become the nav.
		   */
		    CB.sandbox.suckerfishHoverIE = function(navId) {

		      /**
		       * Fix the Son of Suckerfish hover behavior in IE 6.
		       */

 	              function sfHover() {
			var sfNav = document.getElementById(navId);
			if (sfNav == null) {
			  //sanity check
			  return false;
			}
			var sfEls = sfNav.getElementsByTagName("LI");
			for (var i=0; i < sfEls.length; i++) {

			  /**
			   * @ignore
			   */

			  sfEls[i].onmouseover=function() {
			    this.className+=" sfhover";
			  }

			  /**
			   * @ignore
			   */

			  sfEls[i].onmouseout=function() {
			    this.className=this.className.replace(new RegExp(" sfhover\\b"), "");
			  }
			}
		      }
		      if (window.attachEvent) window.attachEvent("onload", sfHover);
}

/**
 * Onload event handler wrapper
 * should allow for attaching arbitrary events to onload, without overriding this library's onload event.
 * @author Rob Zand
 */

CB.sandbox.onloadQueue = [];

/**
 * add a function to the queue to be run onload
 * NOTE that this can be called anywhere in the HTML, or in any remote script
 * it does NOT need to be called before the line that calls CB.sandbox.init.
 */
CB.sandbox.runOnload = function ( fn ) {
  CB.sandbox.onloadQueue.push(fn);
}

/**
 * Initialize the sandbox object by running all the functions in the event queue in order.
 */
CB.sandbox.init = function () {
  window.onload = function () {
    for (var i=0; i< CB.sandbox.onloadQueue.length; i++) {
      CB.sandbox.onloadQueue[i]();
    }
  }
}

/**
 * Multiple-value-Cookie Manager
 * @memberof CB.sandbox
 * @namespace cookieManager
 * @deprecated The IPE persona module is no longer in use.
 */

CB.sandbox.cookieManager = CB.createObject(CB.sandbox);

/**
 * Constructor for Professional settings (multiple-value) cookie management tool
 * @constructor
 * @deprecated The IPE persona module is no longer in use.
 */

CB.sandbox.cookieManager.ProfsettingsCookie = function ()
{
	/* This whole business of construction the lookup table should be done in the XSLT.  Otherwise we get data mismatches like the one on 2007.11.01 */
    	var K12_TEACHER = "K_12_Teacher";
    	var K12_COUNSELOR = "K_12_Counselor";
    	var K12_ADMIN = "K_12_Admin";
    	var HIGHER_ED_FACULTY = "Higher_Ed_Faculty";
    	var HIGHER_ED_FIN_AID = "Financial_Aid_Officer";
    	var HIGHER_ED_ADMIN = "Higher_Ed_Administrator";
    	var HIGHER_ED_RECRUIT = "Recruitment_and_Admissions_Officer";
    	var HIGHER_ED_ADMISSIONS = "Recruitment_and_Admissions_Officer";
	var HIGHER_ED_ALL = "Higher_Ed_Faculty_or_Administrator";
    	var EDUCATOR = "Educator";

	var USERNAME = "username";
	var PERSONA = "persona";

	/* we only ever use the first two elements of the sub-arrays */
	var Personas = new Array(
		[K12_TEACHER, 'K-12 Teacher', '/teacher'],
		[K12_COUNSELOR, 'K-12 Counselor', '/counselor'],
		[K12_ADMIN, 'K-12 Administrator', '/k12-admin'],
		[HIGHER_ED_ADMIN, 'Higher Ed. Administrator', '/higher-ed-admin'],
		[HIGHER_ED_FIN_AID, 'Financial Aid Staff', '/financialaid'],
		[HIGHER_ED_FACULTY, 'Higher Ed. Faculty', '/faculty'],
		[HIGHER_ED_RECRUIT, 'Recruitment & Admissions Staff', '/recruit'],
		[HIGHER_ED_ALL, 'Higher Ed Faculty or Administrator', '/'],
		[EDUCATOR, 'Educator', '/educator']
	);
	var DefaultPersona = EDUCATOR;

	var PersistentCookieName = "prof_settings";
	var SessionCookieName = "prof_session_settings";


      var PersistentCookieValue = CB.cookie.get(PersistentCookieName, "");
	if(!CB.cookie.get(SessionCookieName))
		CB.cookie.set(SessionCookieName, PersistentCookieValue, -1);
	var PairSeparator = '&';
	var ProfCookie = new CB.cookie.Multitool(SessionCookieName, PairSeparator, -1);
	var ProfPersistentCookie = new CB.cookie.Multitool(PersistentCookieName, PairSeparator, -1);

	this.hasPersona = function ()
	{
		var persona = ProfPersistentCookie.get(PERSONA);
		return persona != null;
	}



	//getPersonaBool
	//like getPersona but returns false if no cookie, instead of returning a default persona value.
	this.getPersonaBool = function ()
	{
		var persona = ProfCookie.get(PERSONA);
		if(persona)
			return persona;
		else
			return false;
	}

	this.getPersona = this.getPersonaBool;

	this.getPersonaText = function()
      {
		return this.lookupPersonaText(this.getPersona());
      }

	this.getPersonaURL = function ()
	{
		return this.lookupPersonaURL(this.getPersona());
	}

	this.lookupPersonaText = function(persona)
      {
		for(var i = 0; i < Personas.length; i++)
		{
			if(Personas[i][0] == persona)
				return Personas[i][1];
		}
		return "";
      }

	this.lookupPersonaURL = function(persona)
      {
		for(var i = 0; i < Personas.length; i++)
		{
			if(Personas[i][0] == persona)
				return Personas[i][2];
		}
		return "";
      }



	//getCookiedPersona
	//like getSavedPersona but returns false instead of returning a default value.
	this.getSavedPersonaBool = function ()
	{
		var PersCookie = new CB.cookie.Multitool(PersistentCookieName, PairSeparator);
		var persona = PersCookie.get(PERSONA);
		if(persona)
			return persona;
		else
			return false;
	}

	this.getSavedPersona = this.getSavedPersonaBool;

	this.getSavedPersonaText = function ()
	{
		return this.lookupPersonaText(this.getSavedPersona());
	}

	this.getSavedPersonaURL = function ()
	{
		return this.lookupPersonaURL(this.getSavedPersona());
	}

	this.setPersona = function (persona)
	{
	  //	  debugger;
		for(var i = 0; i < Personas.length; i++)
		{
			if(Personas[i][0] == persona)
			{
				ProfCookie.set(PERSONA, persona);
				break;
			}
                  //else need some debug sanity check here.
		}
	}

	this.getPersonasList = function ()
	{
		return Personas;
	}

	this.setUsername = function(username)
	{
		if(username)
			ProfCookie.set(USERNAME, username);
		else
			ProfCookie.remove(USERNAME);
	}

	this.getUsername = function()
	{
		return ProfCookie.get(USERNAME);
	}

	this.isK12Teacher = function()
	{
		return K12_TEACHER == this.getPersona();
	}

	this.isK12Counselor = function()
	{
		return K12_COUNSELOR == this.getPersona();
	}

	this.isK12Admin = function()
	{
		return K12_ADMIN == this.getPersona();
	}

	this.isHigherEdAdmin = function()
	{
		return HIGHER_ED_ADMIN == this.getPersona();
	}

	this.isHigherEdFinance = function()
	{
		return HIGHER_ED_FIN_AID == this.getPersona();
	}

	this.isHigherEdFaculty = function()
	{
		return HIGHER_ED_FACULTY == this.getPersona();
	}

	this.isHigherEdRecruitment = function()
	{
		return HIGHER_ED_RECRUIT == this.getPersona();
	}

	this.isHigherEdAll = function()
	{
		return HIGHER_ED_ALL == this.getPersona();
	}

	this.isGeneralEducator = function()
	{
		var persona = this.getPersona();
		return !persona || (EDUCATOR == persona);
	}

	this.removeAll = function()
	{
	  //this won't work:
	  //ProfCookie.expire();
	  CB.cookie.expire(SessionCookieName);
	  CB.cookie.expire(PersistentCookieName);
	}

	this.toString = function()
	{
		return ProfCookie.toString();
	}

	this.isDirty = function()
      {
		var sessionCookie = CB.cookie.get(SessionCookieName, null);
		var persCookie = CB.cookie.get(PersistentCookieName, null);
		return sessionCookie != persCookie;
      }

	this.save = function()
	{
		if(this.isDirty())
		{
			var sessionCookie = CB.cookie.get(SessionCookieName, null);
			if(sessionCookie || sessionCookie == "")
				CB.cookie.set(PersistentCookieName, sessionCookie);
			else
				CB.cookie.expire(PersistentCookieName);
		}
	}

	this.undo = function()
	{
		if(this.isDirty())
		{
			var persistentCookie = CB.cookie.get(PersistentCookieName, null);
			if(persistentCookie || persistentCookie == "")
				CB.cookie.set(SessionCookieName, persistentCookie);
			else
				CB.cookie.set(SessionCookieName, "", -1);
		}
	}

      this.populateSelect = function(htmlSelect)
      {
		htmlSelect.options[0] = new Option("Choose another...", 0);
		var index = 1;
		for(var i = 0; i < Personas.length; i++)
		{
			if(Personas[i][0] != this.getPersona())
				htmlSelect.options[index++] = new Option(Personas[i][1], Personas[i][0]);
		}
		htmlSelect.selectedIndex = 0;
      }


	/**
	  * This method is a workaround for not being able to escape single quotes in XSLT.
	  * Call the property with the name of a persona, get back the name of the persona/property as a string :|
	 */

	this.personaString = {
	Educator : 'Educator',
	Financial_Aid_Officer : 'Financial_Aid_Officer',
	Higher_Ed_Faculty_or_Administrator : 'Higher_Ed_Faculty_or_Administrator',
	K_12_Admin : 'K_12_Admin',
	K_12_Counselor : 'K_12_Counselor',
	K_12_Teacher : 'K_12_Teacher',
	Recruitment_and_Admissions_Officer : 'Recruitment_and_Admissions_Officer'
	}

}

/**
 * Pointer to an instance of a Professional Cookie Manager object that will be globally available.
 * @deprecated The IPE persona module is no longer in use.
 *
 */
CB.sandbox.cookieManager.profsettings = new CB.sandbox.cookieManager.ProfsettingsCookie();

/**
 * Persona module manager to update the HTML based on the values of the saved and session persona cookies, if any.
 * @deprecated The IPE persona module is no longer in use.
 */
CB.sandbox.personaModule = {

  /**
    * URLs should get populated in the XSLT
    * @example CB.sandbox.personaModule.lookupUrl = {
    *    Educator : 'EducatorURL',
    *    Financial_Aid_Officer : 'Financial_Aid_OfficerURL',
    *    Higher_Ed_Faculty : 'Higher_Ed_Faculty_or_AdministratorURL',
    *    Higher_Ed_Administrator : 'Higher_Ed_Faculty_or_AdministratorURL',
    *    K_12_Admin : 'K_12_AdminURL',
    *    K_12_Counselor : 'K_12_CounselorURL',
    *    K_12_Teacher : 'K_12_TeacherURL',
    *    Recruitment_and_Admissions_Officer : 'Recruitment_and_Admissions_OfficerURL'
    * }
    */

 lookupUrl : {
 },
 setUpPersonaModule : function(){
    //debugger;
   // function notify ( id ) {
      //debugger;
      /*var notifierIDs = ['noSavedPersona', 'savedPersonaExists', 'personaMismatch'];
      if (id === false) {
	msgEl = '';
      } else {
	var msgEl = CB.get(id);
	if ( ! msgEl || ! msgEl.style) return false;
	msgEl.style.display = 'block';
      }
      for (var i=0; i< notifierIDs.length; i++){
	var current = 	CB.get(notifierIDs[i]);
	if (current == msgEl || ! current || ! current.style) continue;
	current.style.display = 'none';
      }
      return;
    }
	*/
    if ( ! CB.get('personaBox')) {
      return false;
    }
    //if viewing a Persona Page and there is no saved or session persona set
    //OR if we are not viewing a Persona Landing Page and there is no saved cookie
    /*if ( ! CB.sandbox.cookieManager.profsettings.getSavedPersonaBool()  &&
	   (! CB.sandbox.cookieManager.profsettings.getPersonaBool() || CB.sandbox.personaModule.notPersonaPage)
	 ) {
      notify(false);
    }*/
    //if  viewing a Persona Page and the saved persona is not the default, and is the same as the session persona write the name of the session persona
    /*else if ( ! CB.sandbox.cookieManager.profsettings.getSavedPersonaBool()  &&
	      CB.sandbox.cookieManager.profsettings.getPersonaBool() ) {
      notify('noSavedPersona');
      var setPersonaLink = CB.get('noPersistent');
      setPersonaLink.innerHTML = CB.sandbox.cookieManager.profsettings.getPersonaText() + " &gt;&gt;";
      setPersonaLink.onclick = function () {
	CB.sandbox.cookieManager.profsettings.save();
	CB.sandbox.personaModule.setUpPersonaModule();
      }
    }*/
    //if viewing a Persona Page and the saved persona is the same as the session persona, or is the default persona.
    //OR if there is no saved persona and we are not viewing a Persona Landing Page
    /*else if (CB.sandbox.cookieManager.profsettings.getSavedPersona() == CB.sandbox.cookieManager.profsettings.getPersona()  ||
	     (CB.sandbox.cookieManager.profsettings.getSavedPersonaBool() && CB.sandbox.personaModule.notPersonaPage)
	     ) {
      notify('savedPersonaExists');
      var link = CB.get('onlyPersistent');
      link.href = this.lookupUrl[ CB.sandbox.cookieManager.profsettings.getSavedPersona()  ]
      link.innerHTML = CB.sandbox.cookieManager.profsettings.getSavedPersonaText() + " &gt;&gt;";
    }*/
    //if the saved persona is not the default, but is different from the session persona
    /*else if (CB.sandbox.cookieManager.profsettings.getSavedPersona() != CB.sandbox.cookieManager.profsettings.getPersona()) {
      notify('personaMismatch');
      var setPersonaLink = CB.get('hasSession');
      var link = CB.get('hasPersistent');
      setPersonaLink.innerHTML = CB.sandbox.cookieManager.profsettings.getPersonaText() + " &gt;&gt;";
      setPersonaLink.onclick = function () {
	CB.sandbox.cookieManager.profsettings.save();
	CB.sandbox.personaModule.setUpPersonaModule();
      }
      link.href = this.lookupUrl[ CB.sandbox.cookieManager.profsettings.getSavedPersona()  ];
      link.innerHTML = CB.sandbox.cookieManager.profsettings.getSavedPersonaText() + " &gt;&gt;";
    }*/
    /*
     * now set up the onclick handler for the "change persona" form button
     */
    CB.get('goButtonMakeHomepage').onclick = function () {
      var dropdown = CB.get('viewAnother');
      var chosenPersona = dropdown.options[dropdown.selectedIndex].value;
      if (chosenPersona == 0) {
	return;
      }
	  var newURL = chosenPersona;
      //var newURL = CB.sandbox.personaModule.lookupUrl[ dropdown.options[dropdown.selectedIndex].value ]  //using a global name to refer to a local method :( total hack, fix later
      //console.debug( CB.sandbox.personaModule.lookupUrl[ dropdown.options[dropdown.selectedIndex].value ] ,  dropdown.options[dropdown.selectedIndex].value  , newURL);
      document.location = newURL;
    }
  }
}

/**
* Assign class name to element whos REL partially matches the current persona.
* Assign an arbitrary class name to all tags whose REL contains a space-delimited list, one item of which matches the name of the current persona.    Allow for filtering by tag type and parent container (via Nyman's getElementsByClassname).
* We assume that all modules are ULs that are all inside the same parent container.
 * @param  container A container element or its ID.
 * @param {regex}  matchInRel Regular expression to match in the REL attribute of elements in the container.
 * @param  newClass The new class name to append to elements whose RELs match the regex.
 */
 CB.sandbox.assignClassToModuleByRel = function (container, matchInRel, newClass) {
   var re = new RegExp("(^|\\s)" + matchInRel + "(\\s|$)");
   var moduleContainer = CB.get(container);
   if (! moduleContainer) {
     return;
   }
   var modules = moduleContainer.getElementsByTagName('ul');
   CB.map(addClassToModule, modules);

   function addClassToModule(el) {
     var links = el.getElementsByTagName('a');
     CB.map(addClassToLinkByRel, links);
   }

   function addClassToLinkByRel (link) {
   	if (link.getAttribute('rel')){
      if (link.getAttribute('rel').match(re) != null){
       addClass(link);
      }
	}
   }

   function addClass (link) {
     link.className += ' ' + newClass;
   }
 }

 /**
  * create a print button and a close button based on the class name and parent container of a SPAN.  For popup windows.
  */
   CB.sandbox.makePrintButton = function (container, className) {
     if(CB.get(container)){
       CB.assignEventByClassName(function(){window.print()}, 'click', CB.get(container), 'span', className);
     }
   }
 /**
  * create a "close" button and a close button based on the class name and parent container of a SPAN. For popup windows.
  */
   CB.sandbox.makeCloseButton = function (container, className) {
     if(CB.get(container)){
   CB.assignEventByClassName(function(){window.close()}, 'click', CB.get(container), 'span', className);
     }
   }

 /**
  * Hiding and Showing Form and Submission message
  * if the query_string value &submitted appears, then hide form and show submission message
  */
  CB.sandbox.hideShowForm = function (container, formClass, messageBlock){
  var url = document.location.search;
  hasMessage = url.match(/\&submitted/);
  	if(hasMessage){
	  var allForms = CB.getElementsByClassName(CB.get(container), 'div', formClass);
	  CB.map(function (el){el.style.display = 'none';}, allForms);
	  if(CB.get(messageBlock)){
 	    CB.get(messageBlock).className = 'showMessage';
	  }
	}
  }

  /**
   * Hiding dropdown list when hover over nav
   * onmouseover top nav, select box in persona module dissapears
   *   flyouts appear behind form select boxes in ie
   * @author Rob Zand
   */

  CB.sandbox.hideDropDownList = function (triggeredId, containerToHide){
  	var agt      = navigator.userAgent.toLowerCase();
	var appVer   = navigator.appVersion.toLowerCase();
	var is_minor = parseFloat(appVer);
	var is_major = parseInt(is_minor);
	var is_opera = (agt.indexOf("opera") != -1);
	var is_safari = ((agt.indexOf('safari')!=-1)&&(agt.indexOf('mac')!=-1))?true:false;
	var iePos  = appVer.indexOf('msie');
	if (iePos !=-1) {
	   is_minor = parseFloat(appVer.substring(iePos+5,appVer.indexOf(';',iePos)));
	   is_major = parseInt(is_minor);
	}
	var is_ie   = (iePos!=-1);
	var is_ie5   = (is_ie && is_major == 5);
	var is_win   = ( (agt.indexOf("win")!=-1) || (agt.indexOf("16bit")!=-1) );
	var is_mac    = (agt.indexOf("mac")!=-1);
	var is_ie5up = (is_ie && is_minor >= 5);
	var is_ie6down = (is_ie && is_major<7);

	if(is_ie && is_major<7){
	  /**
	   * Pointer to the element that will trigger an action when the user interacts with it.
	   */
	  var navigashun = CB.get(triggeredId);
	  var dropDownList = CB.get(containerToHide);
	   	if(navigashun && dropDownList){
		  navigashun.onmouseover=function() {
		    dropDownList.parentNode.style.paddingBottom = "19px";
		    dropDownList.style.display = 'none';
		  }
		   navigashun.onmouseout=function() {
		     dropDownList.parentNode.style.paddingBottom = "0px";
		     dropDownList.style.display = 'block';
		  }
		}
     }
  }

  /**
  * Remove the default text from the search box onfocus
  * @author Noah Sussman
   */
    CB.sandbox.clearOnInitialFocus = function (fieldName) {
      var clearedOnce = false;
      if ( ! CB.get(fieldName)) {
	return false;
      }
      CB.get(fieldName).onfocus = (function () {
	  if (clearedOnce == false) {
	    this.value = '';
	    clearedOnce = true;
	  }
	})
      return;
    }

  /**
  * Add ID to top level top nav LIs.
   * solution to new top nav styles. temporary fix until we can
   * get the xsl modified to include the IDs
   * @author Stephen Liu

	CB.sandbox.addNavIds = function () {
		navObj = CB.get('nav');
		if(navObj){
			ids = new Array("policy","membeship","basics","guidance","k12","higherEd","development","research");
			j=0;
			for(i=0; navObj.childNodes.length > i; i++){
				if(navObj.childNodes[i].tagName == "LI"){
					navObj.childNodes[i].id = ids[j];
					j++;
				}
			}
		}
	}
  */

 /**
 * Check viewable width of browser window
 * returns the width of the viewable window in pixels
 * @returns null
 */
	CB.sandbox.getWinWidth = function () {
		if (window.innerWidth) {
			return window.innerWidth;
		} else if (document.body.clientWidth) {
			return document.body.clientWidth;
		} else {
			return null;
		}
	}

 /**
 * Check that flyouts will fit in window
 * assigns class to the #nav el that will have the last flyout. open on the left if the viewable screen isn't wide enough. the minimum width is hard-coded at 1425 for the moment.
 */
	CB.sandbox.checkNavFit = function () {
		if(CB.get("nav")){
			if (CB.sandbox.getWinWidth()<1425){
				if(!CB.iCanHasClass(CB.get("nav"),"narrow")){
					CB.addClass(CB.get("nav"),"narrow");
				}
			}
			else {
				if(CB.iCanHasClass(CB.get("nav"),"narrow")){
					CB.removeClass(CB.get("nav"), "narrow");
				}
			}
		}
	}

/**
 * ToggleView
 * Show and Hide view, passed id and link
 */
 CB.sandbox.toggleView = function (id,lnk){
   toggleObj = CB.get(id);
   if (toggleObj.style.display == "none"){
     toggleObj.style.display = "block";
     lnk.className = "hidesummary";
   }
   else {
     toggleObj.style.display = "none";
     lnk.className = "showsummary";
   }
   return false;
 }


