/*******************************************************************************
 * Javascript utility functions to be used by multiple scripts.
 *******************************************************************************/


// Returns true if ch is a string consisting of a single digit, false
// otherwise.
function IsDigit(ch)
{
  return ch.match(/^\d$/) != null;
}

// Find toFind in ar.
function ArrayIndexOf(ar, toFind)
{
  len = ar.length;
  for (i = 0; i < len; i++) {
    if (ar[i] == toFind) return i;
  }
  return -1;
}

/////
// Remove el from array, if it exists. Returns true if it was there.
function ArrayRemove(array, el)
{
  var gotIt = false;

  while((index = ArrayIndexOf(array, el)) != -1) {
    gotIt = true;
    array.splice(index, 1);
  }
  return gotIt;
}

/////
// Adds el to array if it doesn't already exist.
function ArrayAddUnique(array, el)
{
  if (ArrayIndexOf(array, el) == -1) {
    array.push(el);
    return true;
  }
  return false;
}

// Returns a random element of an array.
function RandomChoice(choices)
{
  var i = Math.floor(choices.length * Math.random());
  return choices[i];
}

// Returns a random integer between lower and upper-1, inclusive.
function RandomInt(lower, upper)
{
  return Math.floor(Math.random() * (upper - lower)) + lower;
}

function sendHttpRequest(url, callback, params, isPost)
{
  var xmlhttp = false;
  /*@cc_on @*/
  /*@if (@_jscript_version >= 5)
  // JScript gives us Conditional compilation, we can cope with old IE versions.
  // and security blocked creation of the objects.
  try {
  xmlhttp = new ActiveXObject("Msxml2.XMLHTTP");
  } catch (e) {
  try {
  xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
  } catch (E) {
  xmlhttp = false;
  }
  }
  @end @*/
  if (!xmlhttp && typeof XMLHttpRequest!='undefined') {
    try {
      xmlhttp = new XMLHttpRequest();
    } catch (e) {
      xmlhttp=false;
    }
  }
  if (!xmlhttp && window.createRequest) {
    try {
      xmlhttp = window.createRequest();
    } catch (e) {
      xmlhttp=false;
    }
  }

  if (isPost) {
    xmlhttp.open("POST", url, true);
    xmlhttp.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
    xmlhttp.setRequestHeader("Content-length", params.length);
    xmlhttp.setRequestHeader("Connection", "close");
  } else {
    if (params) {
      url += "?" + params;
    }
    params = null;
    xmlhttp.open("GET", url, true);
  }
  xmlhttp.onreadystatechange = callback;
  xmlhttp.send(params);
  return xmlhttp;
}

function doHttpRequest(url, params, isPost,
		       success, failure)
{
  var request = null;
  function callback()
  {
    if (request && request.readyState == 4) {
      var resp = request.responseText;
      if (request.status == 200) {
	if (success) success(resp);
      } else {
	if (failure) failure(request.status, resp);
      }
      request = null;
    }
  }
  if (typeof(params) != 'string') params = makeParams(params);


  /*@cc_on @*/
  /*@if (@_jscript_version >= 5)
  // JScript gives us Conditional compilation, we can cope with old IE versions.
  // and security blocked creation of the objects.
  try {
  request = new ActiveXObject("Msxml2.XMLHTTP");
  } catch (e) {
  try {
  request = new ActiveXObject("Microsoft.XMLHTTP");
  } catch (E) {
  request = false;
  }
  }
  @end @*/
  if (!request && typeof XMLHttpRequest!='undefined') {
    try {
      request = new XMLHttpRequest();
    } catch (e) {
      request=false;
    }
  }
  if (!request && window.createRequest) {
    try {
      request = window.createRequest();
    } catch (e) {
      request=false;
    }
  }

  if (isPost) {
    request.open("POST", url, true);
    request.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
    request.setRequestHeader("Content-length", params.length);
    request.setRequestHeader("Connection", "close");
  } else {
    if (params) {
      url += "?" + params;
    }
    params = null;
    request.open("GET", url, true);
  }
  request.onreadystatechange = callback;
  request.send(params);
}

function makeParams(obj)
{
  var params = '';
  var sep = '';
  for (var el in obj) {
    params += sep + encodeURIComponent(el) + '=' + encodeURIComponent(obj[el]);
    sep = '&';
  }
//  console.log('makeParams: obj: ', obj, ', params: ', params);
  return params;
}

function setElementsDisplay(els, display)
{
  for (var i = 0; i < els.length; i++) {
    var el = document.getElementById(els[i]);
    el.style.display = display;
  }
}

function showElements(els)
{
  setElementsDisplay(els, '');
}

function hideElements(els)
{
  setElementsDisplay(els, 'none');
}

/******************************************************************************
 * Cookie code from:
 * http://www.quirksmode.org/js/cookies.html
 ******************************************************************************/

function createCookie(name,value,days) {
  if (typeof(document.cookie) === 'undefined') return;
  var expires = '';
  if (days) {
    var date = new Date();
    date.setTime(date.getTime()+(days*24*60*60*1000));
    expires = "; expires="+date.toGMTString();
  }
  document.cookie = name+"="+value+expires+"; path=/";
};

function readCookie(name) {
  if (typeof(document.cookie) === 'undefined') return null;
    var nameEQ = name + "=";
    var ca = document.cookie.split(';');
    for(var i=0;i < ca.length;i++) {
	var c = ca[i];
	while (c.charAt(0)==' ') c = c.substring(1,c.length);
	if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length,c.length);
    }
    return null;
}

function eraseCookie(name) {
    createCookie(name,"",-1);
}

function makeEvent(el, eventType, fn)
{
  if (el.addEventListener){
    el.removeEventListener(eventType, fn, false);
    el.addEventListener(eventType, fn, false);
  } else if (el.attachEvent){
    clearEvent(el, eventType, fn);
    var eProp = eventType + fn;
    el["e"+eProp] = fn;
    el[eProp] = function() { el["e"+eProp]( window.event ); };
    el.attachEvent("on"+eventType, el[eProp] );
  } else {
    el['on'+eventType] = fn;
  }
  return fn;
}

function addEvent(el, eventName, fn)
{
  return makeEvent(el, eventName, fn);
}

function clearEvent(el, eventName, fn ) {
  if (el.removeEventListener) {
    el.removeEventListener(eventName, fn, false);
  }  else if (el.detachEvent) {
    var eProp = eventName + fn;
    if (el[eProp]) {
      el.detachEvent("on"+eventName, el[eProp]);
      el['e'+eProp] = null;
      el[eProp] = null;
    } else {
      el.detachEvent('on'+eventName, fn);
    }
  } else {
    el['on'+eventName] = null;
  }
}


// Scheme-like lazy evaluation (see Scheme function "delay")
function MakePromise(fn)
{
  var gotResult = false;
  var result = null;
  return function() {
    if (!gotResult) {
      result = fn();
      gotResult = true;
    }
    return result;
  };
}

function ForEach(ar, fn)
{
  for(var i = 0; i < ar.length; i++) {
    var ret = fn(ar[i], i);
    if (ret) return ret;
  }
  return null;
}

/////
// Calls fn n times (with the index) or until fn returns a true value.
function Repeat(n, fn)
{
  for (var i = 0; i < n; i++) {
    var ret = fn(i);
    if (ret) return ret;
  }
  return false;
};

function makeArray(param)
{
  var ret;
  if (typeof(param) == 'number') {
    ret = [];
    ret.length = param;
    return ret;
  } else {
    return [param];
  }
}

function Map(ar, fn)
{
  var len = ar.length;
  var ret = makeArray(len);
  for(var i = 0; i < len; i++) {
    ret[i] = fn(ar[i]);
  }
  return ret;
}

function Reduce(ar, init, fn)
{
  var total = init;
  ForEach(ar, function(item) {
    total = fn(total, item);
  });
  return total;
}

function Sum(ar)
{
  return Reduce(ar, 0, function(total, item) { return total + item; });
}

function ReduceOr(ar, fn)
{
  var arLen = ar.length;
  for(var i = 0; i < arLen; i++) {
    var val = fn(ar[i]);
    if (val) return val;
  }
  return false;
}

function Filter(ar, fn)
{
  var ret = [];
  ForEach(ar, function(item) {
    if (fn(item)) {
      ret.push(item);
    }
  });
  return ret;
}

function Complement(ar1, ar2)
{
  return Filter(ar1, function(item) {
    return (ArrayIndexOf(ar2, item) == -1);
  });
}

function ArrayFind(ar, fn)
{
  for (var i = 0; i < ar.length; i++) {
    if (fn(ar[i])) return ar[i];
  }
  return null;
}

function ArrayFindAndRemove(ar, fn)
{
  for (var i = 0; i < ar.length; i++) {
    if (fn(ar[i])) {
      var ret = ar[i];
      ar = ar.splice(i, 1);
      return ret;
    }
  }
  return null;
}


function ArrayJoinFn(ar, sep, fn)
{
  var retAr = [];
  for (var i = 0; i < ar.length; i++) {
    retAr.push(fn(ar[i], i));
  }
  return retAr.join(sep);
};

function Format(fmt, params)
{
  for (var p in params) {
    fmt = fmt.replace('{' + p + '}', params[p]);
  }
  return fmt;
}


function addClassName(el, className)
{
  var classNames = getClassNames(el);
  ArrayAddUnique(classNames, className);
  el.className = classNames.join(' ');
};

function getClassNames(el) {
  return el.className.split(' ');
};

function removeClassName(el, className)
{
  var classNames = getClassNames(el);
  ArrayRemove(classNames, className);
  el.className = classNames.join(' ');
};

