// This is ddis11.js, convenience functions to put before all other
// Javascript written by Stephen Compall.

//// Portability stuff

if (!window.encodeURIComponent)
  window.encodeURIComponent = escape;

// I only need one-arg indexOf.
if (!Array.prototype.indexOf)
  {
    Array.prototype.indexOf = function (elt)
      {
	var len = this.length;
	for (var from = 0; from < len; ++from)
	  {
	    if (from in this && this[from] === elt)
	      return from;
	  }
	return -1;
      };
  }

if (!Array.prototype.filter)
  {
    Array.prototype.filter = function (pred)
      {
	var len = this.length, res = [], elt;
	for (var from = 0; from < len; ++from)
	  if (from in this && pred (elt = this[from], from, this))
	    res.push (elt);
	return res;
      };
  }

if (!Array.prototype.forEach)
  {
    Array.prototype.forEach = function (proc)
      {
	var len = this.length;
	for (var from = 0; from < len; ++from)
	  if (from in this)
	    proc (this[from], from, this);
      };
  }

if (!Array.prototype.map)
  {
    Array.prototype.map = function (proc)
      {
	var len = this.length;
	var res = new Array (len);
	for (var from = 0; from < len; ++from)
	  if (from in this)
	    res[from] = proc (res[from], from, this);
	return res;
      }
  }

Array.prototype.includes = function (elt)
{
  return 0 <= this.indexOf (elt);
};

// Answer a function that will call FUNC1, then FUNC2, passing through
// arguments.
function apply_two_functions_proc (func1, func2)
{
  return function ()
    {
      func1.apply (this, arguments);
      func2.apply (this, arguments);
    };
}

function add_normal_handler (dom_obj, event_name, func)
{
  if (window.addEventListener)
    return dom_obj.addEventListener (event_name, func, false);
  else if (document.addEventListener)
    {
      return (dom_obj === window ? document : dom_obj)
	.addEventListener (event_name, func, false);
    }
  event_name = 'on' + event_name;
  // XXX this doesn't work in IE7
  //if (window.attachEvent)
  //  return dom_obj.attachEvent ('on' + event_name, func);
  if (!dom_obj[event_name])
    dom_obj[event_name] = func;
  else
    {
      dom_obj[event_name] =
	apply_two_functions_proc (dom_obj[event_name], func);
    }
}

// Thanks to "Learning JavaScript".
function get_xml_http_request ()
{
  var xmlhttp;
  if (window.XMLHttpRequest) {
    xmlhttp = new XMLHttpRequest ();
  } else {
    try
      {
	xmlhttp = new ActiveXObject("Msxml2.XMLHTTP");
      }
    catch (e)
      {
	try
	  {
	    xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
	  }
	catch (e)
	  {
	    xmlhttp = null;
	  }
      }
  }
  return xmlhttp;
}

//// Convenience functions

// XXX this only does neat stuff for CONTENT when METHOD is 'GET'
function request_http_xml (method, url, content, func)
{
  var xmlhttp = get_xml_http_request ();
  if (content && method == 'GET')
    {
      var divider = '&';
      if (-1 == url.indexOf ('?')) divider = '?';
      keys_and_values_do (content, function (k, obj) {
	  url = url + divider + encodeURIComponent (k)
	    + '=' + encodeURIComponent (obj);
	  divider = '&';
	});
    }
  xmlhttp.open (method, url);
  xmlhttp.onreadystatechange = function ()
    {
      if (xmlhttp.readyState == 4 && xmlhttp.status == 200)
	{
	  if (-1 != xmlhttp.getResponseHeader ('Content-Type')
	      .search (/^\s*(?:text\/|application\/|.*\+)xml\s*(?:;.*)$/))
	    func (xmlhttp.responseXML);
	  else func (xmlhttp.responseText);
	}
    };
  xmlhttp.send (method == 'GET' ? null : content);
  return xmlhttp;
}

// Call FUNC with each key and value from OBJ.
function keys_and_values_do (obj, func)
{
  if (obj instanceof Array)
    {
      var len = obj.length;
      for (var k = 0; k < len; ++k)
	if (k in obj) func (k, obj[k]);
    }
  else for (var k in obj) func (k, obj[k]);
}

function values_do (obj, func)
{
  keys_and_values_do (obj, function (k, v) {return func (v);});
}

// Answer a real array given a NodeList or something like it.
function fake_array_as_array (fa)
{
  if (fa instanceof Array) return fa;
  var len = fa.length;
  var answer = [];
  for (var idx = 0; idx < len; ++idx)
    answer.push (fa[idx]);
  return answer;
}

// Answer the first element of LST such that PRED answers true.
// Answer null if none.
function find_if (lst, pred)
{
  var posn = position_if (lst, pred);
  return posn == null ? null : lst[posn];
}

// Answer the first key of LST such that PRED answers true.  Answer
// null if none.
function position_if (lst, pred)
{
  var res = null;
  var dummy = {};
  try
    {
      keys_and_values_do (lst, function (k, elt) {
	  if (pred (elt))
	    {
	      res = k;
	      throw dummy;
	    }
	});
    }
  catch (ex) {if (dummy !== ex) throw ex;}
  return res;
}

// Answer all elements of LST where PRED answers true.
function filter_if (lst, pred)
{
  var res = [];
  values_do (lst, function (elt) {if (pred (elt)) res.push (elt);});
  return res;
}

function html_escape (str)
{
  return (str.replace (/</g, '&lt;').replace (/\"/g, '&#34;')
	  .replace (/\'/g, '&#39;'));
}

// ddis11.js ends here
