//  --------------------------------------------------------------------------->
var BrowserDetect = {
  init: function () {
    this.browser = this.searchString(this.dataBrowser) || "An unknown browser";
    this.version = this.searchVersion(navigator.userAgent) || this.searchVersion(navigator.appVersion) || "an unknown version";
    this.OS = this.searchString(this.dataOS) || "an unknown OS";
  },
  searchString: function (data) {
    for (var i = 0; i < data.length; i++) {
      var dataString = data[i].string;
      var dataProp = data[i].prop;
      this.versionSearchString = data[i].versionSearch || data[i].identity;
      if (dataString) {
        if (dataString.indexOf(data[i].subString) != -1) {return data[i].identity;}
      }
      else if (dataProp) {return data[i].identity;}
    }
  },
  searchVersion: function (dataString) {
    var index = dataString.indexOf(this.versionSearchString);
    if (index == -1) {return;}
    return parseFloat(dataString.substring(index + this.versionSearchString.length + 1));
  },
  dataBrowser: [{
    string: navigator.userAgent,
    subString: "Chrome",
    identity: "Chrome"
  },
  {
    string: navigator.userAgent,
    subString: "OmniWeb",
    versionSearch: "OmniWeb/",
    identity: "OmniWeb"
  },
  {
    string: navigator.vendor,
    subString: "Apple",
    identity: "Safari",
    versionSearch: "Version"
  },
  {
    prop: window.opera,
    identity: "Opera"
  },
  {
    string: navigator.vendor,
    subString: "iCab",
    identity: "iCab"
  },
  {
    string: navigator.vendor,
    subString: "KDE",
    identity: "Konqueror"
  },
  {
    string: navigator.userAgent,
    subString: "Firefox",
    identity: "Firefox"
  },
  {
    string: navigator.vendor,
    subString: "Camino",
    identity: "Camino"
  },
  { // for newer Netscapes (6+)
    string: navigator.userAgent,
    subString: "Netscape",
    identity: "Netscape"
  },
  {
    string: navigator.userAgent,
    subString: "MSIE",
    identity: "Explorer",
    versionSearch: "MSIE"
  },
  {
    string: navigator.userAgent,
    subString: "Gecko",
    identity: "Mozilla",
    versionSearch: "rv"
  },
  { // for older Netscapes (4-)
    string: navigator.userAgent,
    subString: "Mozilla",
    identity: "Netscape",
    versionSearch: "Mozilla"
  }],
  dataOS: [{
    string: navigator.platform,
    subString: "Win",
    identity: "Windows"
  },
  {
    string: navigator.platform,
    subString: "Mac",
    identity: "Mac"
  },
  {
    string: navigator.userAgent,
    subString: "iPhone",
    identity: "iPhone/iPod"
  },
  {
    string: navigator.platform,
    subString: "Linux",
    identity: "Linux"
  }]

};

//  --------------------------------------------------------------------------->

BrowserDetect.init();

//  ---------------------------------------------------------------------------
fixEvent = function (e) {
  if (typeof e        == 'undefined') {e        = window.event;}
  if (typeof e.layerX == 'undefined') {e.layerX = e.offsetX;}
  if (typeof e.layerY == 'undefined') {e.layerY = e.offsetY;}
  if (typeof e.target == 'undefined') {e.target = e.srcElement;}
  return e;
};

//  --------------------------------------------------------------------------->
function confine (x, lowerX, upperX) {
//  alert ('confine ('+x+', '+lowerX+', '+upperX+')');
  if (isNaN (parseFloat(x))) {return false;}
  if (x > upperX) {x = upperX;}
  if (x < lowerX) {x = lowerX;}
  return x;
}

//  --------------------------------------------------------------------------->
function toHex(N) {
  if (N === null) {return "00";}
  N = parseInt(N, 10);
  if (N === 0 || isNaN(N)) {return "00";}
  N = Math.max(0, N);
  N = Math.min(N, 255);
  N = Math.round(N);
  return "0123456789ABCDEF".charAt((N - N % 16) / 16) + "0123456789ABCDEF".charAt(N % 16);
}

//  --------------------------------------------------------------------------->
function RGBtoHex(R, G, B) {
  if ((G === undefined) && (B === undefined)) {return '#' + toHex(Math.round(R[0])) + toHex(Math.round(R[1])) + toHex(Math.round(R[2]));}
  else {return '#' + toHex(Math.round(R)) + toHex(Math.round(G)) + toHex(Math.round(B));}
}

//  --------------------------------------------------------------------------->
function Color(red, green, blue) {
  if (red === undefined) {return '***  error  ***';}
  if ((green === undefined) && (blue === undefined)) {return 'rgb(' + Math.round(red[0]) + ',' + Math.round(red[1]) + ',' + Math.round(red[2]) + ')';}
  else {return 'rgb(' + Math.round(red) + ',' + Math.round(green) + ',' + Math.round(blue) + ')';}
}

//  ---------------------------------------------------------------------------
Object.prototype.showString = function () {
  alert(this.toString());
};

//  ---------------------------------------------------------------------------
function isFunction(object) {
  return typeof object == "function";
}

//  ---------------------------------------------------------------------------
function isString(object) {
  return typeof object == "string";
}

//  ---------------------------------------------------------------------------
function isNumber(object) {
  return typeof object == "number";
}

//  ---------------------------------------------------------------------------
function isUndefined(object) {
  return typeof object == "undefined";
}

//  ---------------------------------------------------------------------------
function isArray(obj) {
  if (obj.constructor.toString().indexOf("Array") == -1)
    {return false;}
  else
    {return true;}
}

//  ---------------------------------------------------------------------------
function include(element, pattern) {
  return element.indexOf(pattern) > -1;
}

//  ---------------------------------------------------------------------------
function startsWith(element, pattern) {
  return element.indexOf(pattern) === 0;
}

//  ---------------------------------------------------------------------------
function endsWith(element, pattern) {
  var d = element.length - pattern.length;
  return d >= 0 && element.lastIndexOf(pattern) === d;
}

//  ---------------------------------------------------------------------------
function empty() {
  return this == '';
}

//  ---------------------------------------------------------------------------
function camelize(element) {
  var parts = element.split('-'),
    len = parts.length;
  if (len == 1) {
    return parts[0];
  }
  var camelized = element.charAt(0) == '-' ? parts[0].charAt(0).toUpperCase() + parts[0].substring(1) : parts[0];
  for (var i = 1; i < len; i++) {
    camelized += parts[i].charAt(0).toUpperCase() + parts[i].substring(1);
  }
  return camelized;
}

//  ---------------------------------------------------------------------------
function isDate(x) {
  return (null !== x) && !isNaN(x) && ("undefined" !== typeof x.getDate);
}

//  ---------------------------------------------------------------------------
String.prototype.trim = function () {
//  return this.replace(/^\s+|\s+$/g, '');
  var temp = this;
  var obj = /^(\s*)([\W\w]*)(\b\s*$)/;
  if (obj.test(temp)) { temp = temp.replace(obj, '$2'); }
  obj = /  /g;
  while (temp.match(obj)) { temp = temp.replace(obj, " "); }
  return temp;
};

//  ---------------------------------------------------------------------------
function trim(inputString) {
  // Removes leading and trailing spaces from the passed string. Also removes
  // consecutive spaces and replaces it with one space. If something besides
  // a string is passed in (null, custom object, etc.) then return the input.
  if (typeof inputString != "string") {
    return inputString;
  }
  var retValue = inputString;
  var ch = retValue.substring(0, 1);
  while (ch === " ") { // Check for spaces at the beginning of the string
    retValue = retValue.substring(1, retValue.length);
    ch = retValue.substring(0, 1);
  }
  ch = retValue.substring(retValue.length - 1, retValue.length);
  while (ch === " ") { // Check for spaces at the end of the string
    retValue = retValue.substring(0, retValue.length - 1);
    ch = retValue.substring(retValue.length - 1, retValue.length);
  }
  while (retValue.indexOf("  ") != -1) { // Note that there are two spaces in the string - look for multiple spaces within the string
    retValue = retValue.substring(0, retValue.indexOf("  ")) + retValue.substring(retValue.indexOf("  ") + 1, retValue.length); // Again, there are two spaces in each of the strings
  }
  return retValue; // Return the trimmed string back to the user
} // Ends the "trim" function

//  ---------------------------------------------------------------------------
String.prototype.leftTrim = function () {
  while (this.charAt(0) === ' ') {this.replace (' ', '');}
  return this;
};

//  ---------------------------------------------------------------------------
String.prototype.rightTrim = function () {
  while (this.charAt(this.length-1) === ' ') {this.replace (' ', '');}
  return this;
};

//  ---------------------------------------------------------------------------
Array.prototype.sum = function () {
  for (var i = 0, sum = 0; i < this.length; sum += this[i++]) {}
  return sum;
};

//  ---------------------------------------------------------------------------
Array.prototype.mean = function () {
  return this.sum() / this.length;
};

//  ---------------------------------------------------------------------------
Array.prototype.toString = function () {
  for (var i = 0, s = '['; i < this.length; s += (this[i++].toString()+', ')) {}
  return s+']';
};

//  ---------------------------------------------------------------------------
Array.prototype.max = function () {
  return Math.max.apply({}, this);
};

//  ---------------------------------------------------------------------------
Array.prototype.min = function () {
  return Math.min.apply({}, this);
};

//  ---------------------------------------------------------------------------
Array.prototype.minus = function (value) {
  if (!this.length) {return null;}
  if (!value) {return this;}
  for (var i=0; i < this.length; i++)
    {this[i] -= value;}
  return this;
};

//  ---------------------------------------------------------------------------
Array.prototype.plus = function (value) {
  if (!this.length) {return null;}
  if (!value) {return this;}
  for (var i=0; i < this.length; i++)
    {this[i] += value;}
  return this;
};

//  ---------------------------------------------------------------------------
Array.prototype.remove = function (s) {
  for (i = 0; i < this.length; i++) {
    if (s == this[i])
      {this.splice(i, 1);}}
};


//  ---------------------------------------------------------------------------
function numbersonly(e, decimal) {
//  <form>
//    <input name="number"  onKeyPress="return numbersonly(event, false)">
//  </form>
  var key;
  var keychar;

  if (window.event) {
    key = window.event.keyCode;
  } else if (e) {
    key = e.which;
  } else {
    return true;
  }
  keychar = String.fromCharCode(key);
  if ((key === null) || (key === 0) || (key == 8) || (key == 9) || (key == 13) || (key == 27)) {
    return true;
  } else if ((("0123456789").indexOf(keychar) > -1)) {
    return true;
  } else if (decimal && (keychar == ".")) {
    return true;
  } else {
    return false;
  }
}

//  ---------------------------------------------------------------------------
function includeCSS(p_file) {
  var v_css = document.createElement('link');
  v_css.rel = 'stylesheet';
  v_css.type = 'text/css';
  v_css.href = p_file;
  document.getElementsByTagName('head')[0].appendChild(v_css);
}

//  ---------------------------------------------------------------------------
function getElementsByClass(searchClass, node, tag) {
  var classElements = new Array(0);
  if (node === null) {
    node = document;
  }
  if (tag === null) {
    tag = '*';
  }
  var els = node.getElementsByTagName(tag);
  var elsLen = els.length;
  var pattern = new RegExp("(^|\\s)" + searchClass + "(\\s|$)");
  var j = 0;
  for (i = 0; i < elsLen; i++) {
    if (pattern.test(els[i].className)) {
      classElements[j] = els[i];
      j++;
    }
  }
  return classElements;
}

//  ---------------------------------------------------------------------------
function twoDigit(n) {
  return (n < 10) ? '0' + n : n;
}

//  ---------------------------------------------------------------------------
function threeDigit(n) {
  return (n < 100) ? ((n < 10) ? '00' + n : '0' + n) : n;
}

//  ---------------------------------------------------------------------------
function fourDigit(n) {
  return (n < 1000) ? ((n < 100) ? ((n < 10) ? '000' * n : '00' + n) : '0' + n) : n;
}

//  ---------------------------------------------------------------------------
Object.prototype.inspect = function (maxLevels, level) {
  var str = '',
    type, msg;
  // Start Input Validations
  // Don't touch, we start iterating at level zero
  if (level === null) {level = 0;}
  // At least you want to show the first level
  if (maxLevels === null) {maxLevels = 1;}
  if (maxLevels < 1) {return '<font color="red">Error: Levels number must be > 0</font>';}
  // We start with a non null object
  if (this === null) {return '<font color="red">Error: Object <b>NULL</b></font>';}
  // End Input Validations
  // Each Iteration must be indented
  str += '<ul>';
  // Start iterations for all objects in this
  for (property in this) {
    try {
      // Show "property" and "type property"
      type = typeof(this[property]);
      str += '<li>(' + type + ') ' + property + ((this[property] === null) ? (': <b>null</b>') : ('')) + '</li>';
      // We keep iterating if this property is an Object, non null and we are inside the required number of levels
      if ((type === 'object') && (this[property] !== null) && (level + 1 < maxLevels)) {str += this[property].inspect(maxLevels, level + 1);}
    }
    catch (err) {
      // Is there some properties in this we can't access? Print it red.
      if (typeof(err) === 'string') {msg = err;}
      else if (err.message) {msg = err.message;}
      else if (err.description) {msg = err.description;}
      else {msg = 'Unknown';}
      str += '<li><font color="red">(Error) ' + property + ': ' + msg + '</font></li>';
    }
  }
  // Close indent
  str += '</ul>';
  return str;
};

//  ---------------------------------------------------------------------------
  function insertInspect (object, id, maxLevels, level) {
    var div = document.createElement('div');
    div.id = id;
    div.innerHTML = object.inspect (maxLevels, level);
    document.body.appendChild (div);
  }

//  ---------------------------------------------------------------------------
Object.prototype.showAttributes = function() {
  var s = 'attributes:\n';
  for(var i = 0; i < this.attributes.length; i++ )
    {s += (this.attributes[i].nodeName.toLowerCase()+ ': ' + this.attributes[i].nodeValue+'\n' );}
  alert (s);
};

//  ---------------------------------------------------------------------------
function replaceSubstring(inputString, fromString, toString) {
  // Goes through the inputString and replaces every occurrence of fromString with toString
  var toTheLeft = '';
  var toTheRight = '';
  var temp = inputString;
  if (fromString == "") {
    return inputString;
  }
  if (toString.indexOf(fromString) == -1) { // If the string being replaced is not a part of the replacement string (normal situation)
    while (temp.indexOf(fromString) != -1) {
      toTheLeft = temp.substring(0, temp.indexOf(fromString));
      toTheRight = temp.substring(temp.indexOf(fromString) + fromString.length, temp.length);
      temp = toTheLeft + toString + toTheRight;
    }
  } else { // String being replaced is part of replacement string (like "+" being replaced with "++") - prevent an infinite loop
    var midStrings = ["~", "`", "_", "^", "#"];
    var midStringLen = 1;
    var midString = "";
    // Find a string that doesn't exist in the inputString to be used
    // as an "inbetween" string
    while (midString == "") {
      for (var i = 0; i < midStrings.length; i++) {
        var tempMidString = "";
        for (var j = 0; j < midStringLen; j++) {
          tempMidString += midStrings[i];
        }
        if (fromString.indexOf(tempMidString) == -1) {
          midString = tempMidString;
          i = midStrings.length + 1;
        }
      }
    } // Keep on going until we build an "inbetween" string that doesn't exist
    // Now go through and do two replaces - first, replace the "fromString" with the "inbetween" string
    while (temp.indexOf(fromString) != -1) {
      toTheLeft = temp.substring(0, temp.indexOf(fromString));
      toTheRight = temp.substring(temp.indexOf(fromString) + fromString.length, temp.length);
      temp = toTheLeft + midString + toTheRight;
    }
    // Next, replace the "inbetween" string with the "toString"
    while (temp.indexOf(midString) != -1) {
      toTheLeft = temp.substring(0, temp.indexOf(midString));
      toTheRight = temp.substring(temp.indexOf(midString) + midString.length, temp.length);
      temp = toTheLeft + toString + toTheRight;
    }
  } // Ends the check to see if the string being replaced is part of the replacement string or not
  return temp; // Send the updated string back to the user
} // Ends the "replaceSubstring" function

//  ---------------------------------------------------------------------------
function getMaxSize() {
  var maxSize = [0, 0];
  if (typeof(window.innerWidth) == 'number') {
      //Non-IE
      maxSize.width = window.innerWidth;
      maxSize.height = window.innerHeight;
    } else if (document.documentElement && (document.documentElement.clientWidth || document.documentElement.clientHeight)) {
      //IE 6+ in 'standards compliant mode'
      maxSize.width = document.documentElement.clientWidth;
      maxSize.height = document.documentElement.clientHeight;
    } else if (document.body && (document.body.clientWidth || document.body.clientHeight)) {
      //IE 4 compatible
      maxSize.width = document.body.clientWidth;
      maxSize.height = document.body.clientHeight;
    }
  return maxSize;
}

//  ---------------------------------------------------------------------------
function showSize() {
  var maxSize = getMaxSize ();
  window.alert('Width = ' + maxSize.width + ', Height = ' + maxSize.height);
  return maxSize;
}

//  ---------------------------------------------------------------------------
function drawMaxBox(origin) {
  var maxSize = getMaxSize ();
  var box = document.createElement('div');
//  box.style.backgroundColor = 'yellow';
  box.style.border = 'solid #6699ff 1px';
  box.style.position = 'absolute';
  box.style.left  = origin.x+'px';
  box.style.top   = origin.y+'px';
  box.style.width  = (maxSize.width  - origin.x - 250)+'px';
  box.style.height = (maxSize.height - origin.y -   1)+'px';
  document.body.appendChild (box);
//  alert (box.style.width+', '+box.style.height);
  return box;
}

//  ---------------------------------------------------------------------------
function show (id) {
  var node = document.getElementById (id);
  node.style.display="block";
  return node;
}

//  ---------------------------------------------------------------------------
function hide (id) {
  var node = document.getElementById (id);
  node.style.display="none";
  return node;
}

//  ---------------------------------------------------------------------------
function toggle (id) {
  var node = document.getElementById (id);
  node.style.display=node.style.display=="none" ? "block" : "none";
  return node;
}

//  ---------------------------------------------------------------------------
Array.prototype.isLowerEqual = function (a, b) {
  if (isDate(a) && isDate(b))
    {return a.isLowerEqual (b);}
  else
    {return a <= b;}
};

//  --------------------------------------------------------------------------->
Array.prototype.partition = function (begin, end, pivot) {
  var piv = this[pivot];
  this.swap(pivot, end - 1);
  var store = begin;
  var ix;
  for (ix = begin; ix < end - 1; ++ix) {
    if (this.isLowerEqual (this[ix], piv)) {
      this.swap(store, ix);
      ++store;
    }
  }
  this.swap(end - 1, store);
  return store;
};

//  In partition(), we used the swap() method of Array.This is not included in standard Javascript, so we have to define it.
//  --------------------------------------------------------------------------->
Array.prototype.swap = function (a, b) {
  var tmp = this[a];
  this[a] = this[b];
  this[b] = tmp;
  return this;
};

//  ---------------------------------------------------------------------------
//  Quicksort
//  The qsort() function takes an array, a start index and an end index (pointing 1 element past the last element to be sorted). This implementation uses a random pivot value.

Array.prototype.qsort = function (begin, end) {
  if (end - 1 > begin) {
    var pivot = begin + Math.floor(Math.random() * (end - begin));
    pivot = this.partition(begin, end, pivot);
    this.qsort(begin, pivot);
    this.qsort(pivot + 1, end);
  }
  return this;
};

//  ---------------------------------------------------------------------------
//  Normally, a user wants to sort a complete array, so we provide a convenience function where it is not necessary to specify the indexes.

Array.prototype.sort = function () {
  return this.qsort(0, this.length);
};

//  ---------------------------------------------------------------------------
//  The complete quicksort.js Javascript source file includes the functions that make up the quicksort implementation, as well as some support functions for testing:
//  ---------------------------------------------------------------------------
//  dosort() is a helper function, translating the unsorted text into an array, calling quick_sort(), translating the sorted array back to a string and write it to the sorted text box.

textSort = function (text) {
  return text.split(/ +/).sort().join(' ');
};

//  ---------------------------------------------------------------------------
commandNoOf = function (command, allCommands) {
  var found = 0;
  var stillMatches = new Array(0);
  for (var i=0; i < allCommands.length; i++)
    {stillMatches[i] = true;}
  for (i = 0; i < command.length; i++) {
    found = 0;
    for (var j = 0; j < allCommands.length; j++) {
      if (stillMatches[j]) {
        stillMatches[j] = allCommands[j].indexOf(command[i]) > -1;
        if (stillMatches[j]) {
          found++;
          lastJ = j;
        }
      }
    }
    if (found==1) {return lastJ;}
  }
  return -1;
};

//  ---------------------------------------------------------------------------
Object.prototype.getStyleFromCSS = function (property) {
  var sheets = document.styleSheets;
//  alert (this.nodeName.toLowerCase()+'.'+this.id+': '+this.className);
  if (!sheets.length) {return '';}
  for (var i = 0; i < sheets.length; i++) {
    var rules = sheets[i].cssRules;
//    alert ('rules: '+rules.length);
//    var s = 'rules: '+rules.length+'\n';
    if (rules.length > 0) {
      for (var j = 0; j < rules.length; j++) {
        var style = rules[j].style;
//        s += rules[j].selectorText+':\n'+style.length+' properties\n'+style[property]+'\n';
        if ((style[property] !== "") && (style[property] !== null)){ 
          if ((rules[j].selectorText == ("." + this.className)) || (rules[j].selectorText == (this.nodeName.toLowerCase() + "." + this.className)))
          {return style[property];}
        }
      }
    }
  }
//  alert (s);
  return '';
};

//  --------------------------------------------------------------------------->
nodeOf = function (element) {
  return isString(element) ? document.getElementById(element) : element;
};