/* rsn_misc.js
 * a place for miscellaneous functions, variables, constants and data 
 * structures that have no better home
 *
 * Dependencies
 * this module requires rsn_global.js
 * 
 * Version 1.14 - modified hideNext - back button now works
 * Version 1.13 - showInlineErrorMessage() now takes in an optional color
 *                parameter (added by TH on 09/07/2008)
 * Version 1.12 - modified hideThenShowNext(): added check for numSecs for 
 *                indefinite hide
 * Version 1.11 - added isErrorPage()
 * Version 1.10 - added getCalculatedStyle()
 * Version 1.9  - added sleep()
 * Version 1.8  - modified openWindow(): added a 4th param so window options 
 *                are customizable by the caller
 * Version 1.7  - form submission prevented in hideNextBeforeOnload(), 
 *                hideNext(), and explicitly enabled in showNext()
 * Version 1.6  - added hideNextBeforeOnload() and autoNextBeforeOnload()
 * Version 1.5  - showInlineErrorMessage() now uses scrollTo()
 * Version 1.4  - added autoNext()
 * Version 1.3  - added openWindow()
 * Version 1.2  - added trimOpens() and trim()
 * Version 1.1  - added showInlineErrorMessage(), insertHiddenInput(), 
 *                getWindowSize(), maximizeWindow(). These latter 2 for Baby 
 *                Milk survey.
 * Version 1.0  - original version
 *
 * Copyright (2006) Research Now Plc.
 */


/*** functions for hiding and showing HTML elements ***/

/* hides HTML element with id == "forwardbutton" as soon as it loads and 
 * prevents form submission. It's safe to call this function before it or 
 * the DOM has loaded because it keeps polling for its availability.
 */
function hideNextBeforeOnload() {
  var elem = document.getElementById("forwardbutton");
  if (elem == null)
    
    // poll for forwardbutton's presence every 50 milliseconds
    window.setTimeout("hideNextBeforeOnload()", 50);
  else {
    elem.style.visibility = "hidden";
    doOnSubmit( "hideNext", function() { return false; } );
  }
}

/* hides HTML element with id == "forwardbutton" and mechanically prevents 
 * form submission
 */
function hideNext() {
  var elem = document.getElementById("forwardbutton");
  if (elem != null) {
    elem.style.visibility = "hidden";
    doOnSubmit( "hideNext", function() { return false; } );
  }
  var backButton = document.getElementById("backButton");
  if (backButton != null) {
    backButton.onclick = function() {
      revokeOnSubmit("hideNext");
    };
  }
}

/* ensures HTML element with id == "forwardbutton" is visible and that form 
 * submission will succeed
 */
function showNext() {
  var elem = document.getElementById("forwardbutton");
  if (elem != null) {
    elem.style.visibility = "visible";
    revokeOnSubmit("hideNext");
  }
}

/* hides HTML element with id == "forwardbutton" for a duration of numSecs 
 * seconds
 */
function hideThenShowNext(numSecs) {
  hideNext();
  if (numSecs)
	  window.setTimeout( "showNext()", numSecs * 1000 );
}

/* automatically submits the Confirmit ctlform after numSecs has elapsed
 */
function autoNext(numSecs) {
  window.setTimeout( "document.ctlform.submit()", numSecs * 1000 );
}

/* submits the Confirmit ctlform ASAP. It's safe to call this function before 
 * the ctlform or DOM has fully loaded because their presence is continually 
 * polled for.
 */
function autoNextBeforeOnload() {
  try {
    document.ctlform.submit()
  }
  catch (e) { window.setTimeout("autoNextBeforeOnload()", 50); }
}

/* hides arbitrary HTML elements given the value of their id attr
 *
 * idList - a space and/or comma separated string of ids of elements to hide 
 *         (e.g. "q1_2, q5, q22_3")
 */
function hideElementsById(idList) {
  var elems = idList.split( /, */ );
  for (var i = 0; i < elems.length; i++) {
    var elem = document.getElementById( elems[i] );
    if (elem != null)
      elem.style.display = "none";
  }
}


/*** functions for client detection (e.g. JavaScript, Java, Flash) ***/

/* allows Confirmit or some server side app to detect if the client has 
 * JavaScript capability: we use JavaScript to generate and send a name=value 
 * form pair via GET or POST. If this pair doesn't reach the server side, we 
 * assume the client lacked JavaScript.
 */
function detectJavaScript() {
  insertHiddenInput("hasJavaScript", "yes");
}

/* writes the client's color depth, screen size and window size to a bunch of 
 * hidden form elements, allowing a server side app to pick them up
 */
function detectDisplayDetails() {
  insertHiddenInput( "colorDepth", screen.colorDepth );
  insertHiddenInput( "screenSize", screen.width + "x" + screen.height );
  insertHiddenInput( "windowSize", getWindowSize() );
}

/* returns the dimensions of the usable space of the browser, the space a web 
 * site can occupy (i.e. the whole window minus the browser toolbar and 
 * status bar etc.)
 *
 * Returns a string of the form "WxH" (e.g. "1003x491")
 */
function getWindowSize() {

  // Gecko browsers
  if (self.innerWidth)
    return self.innerWidth + "x" + self.innerHeight;

  // IE6 with a DOCTYPE
  else if (document.documentElement && document.documentElement.clientWidth)
    return document.documentElement.clientWidth + "x" 
  		+ document.documentElement.clientHeight;

  // IE5 or IE6 without a DOCTYPE
  else if (document.body)
    return document.body.clientWidth + "x" + document.body.clientHeight;
}


/*** truly miscellaneous functions ***/

/* emulates a Confirmit error message (orange text, top of page) without 
 * refreshing the page
 *
 * msg - the text of the error message to show
 * color (optional) - specify a color for custom surveys (default is orange)
 *                    precede hex values with a #.
 */
function showInlineErrorMessage(msg, color) {
  var bodyDiv                   = document.getElementById("body");
  var errDiv                    = document.createElement("div");
  errDiv.className              = "errorMessage";
  errDiv.style.marginTop        = "8px";
	if (color) errDiv.style.color = color;
  else       errDiv.style.color = "#ff6600";
  errDiv.style.fontWeight       = "bold";
	
	var msgs = msg.split("\n");
	
	for (var i=0;i<msgs.length;i++) {
		errDiv.appendChild( document.createTextNode(msgs[i]) );
		errDiv.appendChild( document.createElement("br") );
	}
	
  // erase previous error message if there was one
  if (bodyDiv.firstChild.className == "errorMessage")
    //bodyDiv.replaceChild(errDiv, bodyDiv.firstChild);
    bodyDiv.firstChild.style.display = "none";
  
  bodyDiv.insertBefore(errDiv, bodyDiv.firstChild);
  
  /* jump to error message so it's visible on user's screen. We could also 
   * use errDiv.scrollIntoView() so the window will scroll up just enough to 
   * reveal errDiv
   */
  //location.href = "#top";
  self.scrollTo(0, 0);
}

/* inserts a hidden input element (<input type="hidden">) at the beginning of 
 * Confirmit's form (document.ctlform)
 *
 * name  - name and id of the input element (value for the name and id 
 *         attributes
 * value - value of the input element (value for the value attr)
 *
 * returns a reference to the newly created input element after it's been 
 * attached to the DOM
 */
function insertHiddenInput(name, value) {
  var form     = document.ctlform;
  var newInput = document.createElement("input");
  newInput.setAttribute("type", "hidden");
  newInput.setAttribute("name", name);
  newInput.setAttribute("id", name);		// or newInput.id = name
  if (value != null)
    newInput.setAttribute("value", value);
  return form.insertBefore(newInput, form.firstChild);	// returns newInput
}

/* maximizes the browser window 
 */
function maximizeWindow() {
  top.window.moveTo(0, 0);
  if (isIE)
    top.window.resizeTo(screen.availWidth, screen.availHeight);
  else {
    top.window.outerHeight = top.screen.availHeight;
    top.window.outerWidth  = top.screen.availWidth;  
  }
}

/* opens url in a new popup window, named "popup", with dimensions w x h. 
 * Returns a reference to the new window.
 *
 * url     - URL of a resource to load in the new popup window
 * w       - width in pixels to make the new popup
 * h       - height in pixels to make the new popup
 * options - a string of options specifying characteristics for the new 
 *           popup. E.g. pass "menubar=no,scrollbars=no,status=yes". See 
 *           JavaScript documentation for the 3rd param of window.open()
 */
function openWindow(url, w, h, options) {

  // if optionsStr isn't empty, prefix it with a comma
  if (options != null && options.length > 0)
    options = "," + options;
  else
    options = "";
  
  return window.open(url, "popup", "width=" + w + ",height=" + h + options);
}

/* halts/blocks the current JavaScript thread for a duration of millis 
 * milliseconds
 */
function sleep(millis) {
  var entryDate = new Date();
  var curDate   = null;
  do {
    curDate = new Date();
  } while (curDate.getTime() - entryDate.getTime() <= millis);
}

/* gets the computed CSS value for a given selector on an element. Some HTML 
 * elements don't have their own style attr. The CSS for these is therefore 
 * inherited from a parent or somehow "computed".
 *
 * elem     - the element whose CSS you want to query
 * selector - the CSS selector whose value you want (e.g. "fontSize")
 */
function getCalculatedStyle(elem, selector) {
  if (elem.currentStyle)			// IE
    return elem.currentStyle[selector];
  else if (window.getComputedStyle)		// Firefox et al
    return window.getComputedStyle(elem, null)[selector];
  return null;
}

/* returns true if the current Confirmit page contains an error message (a 
 * GERROR primitive) and, as such, appeared because it wasn't filled in 
 * correctly when the respondent tried to submit it last time
 */
function isErrorPage() {
  return document.getElementById("errorMessage") != null;
}


/*** functions for trimming whitespace ***/

/* discovers every open-end element in Confirmit's ctlform (i.e. input 
 * elements with type == "text" and textarea elements), and trims leading and 
 * trailing whitespace from the values in each. See trim().
 *
 * Returns void.
 */
function trimOpens() {
  var formControls = document.ctlform.elements;
  for (var i = 0; i < formControls.length; i++) {
    var formControl = formControls[i];

    // faster than /^text/.test(formControl.type) and case sensitive :-/
    if (formControl.type.indexOf("text") == 0)
      formControl.value = trim(formControl.value);
  }
}

/* trims leading and trailing whitespace from str and returns it. E.g. given 
 * "  chewing gum   weekend ", returns "chewing gum   weekend". Interior 
 * spacing is obviously preserved.
 *
 * str - a string to be trimmed
 *
 * returns a copy of str with whitespace trimmed
 *
 * Refactoring note - regular expressions are created on each invocation: 
 * slightly inefficient, performance wise. They should persist, entailing a 
 * more global lexical scope, but that kinda breaks the function's 
 * encapsulation.
 */
function trim(str) {
  var re1 = /^\s+/;	// leading whitespace
  var re2 = /\s+$/;	// trailing whitespace
  return str.replace(re1, "").replace(re2, "");
}