text: /* Copyright (c) 2006-2008 MetaCarta, Inc., published under the Clear BSD
   * license.  See http://svn.openlayers.org/trunk/openlayers/license.txt for the
   * full text of the license. */
  
  /**
   * @requires OpenLayers/BaseTypes/Class.js
   * @requires OpenLayers/BaseTypes/LonLat.js
   * @requires OpenLayers/BaseTypes/Size.js
   * @requires OpenLayers/BaseTypes/Pixel.js
   * @requires OpenLayers/BaseTypes/Bounds.js
   * @requires OpenLayers/BaseTypes/Element.js
   * @requires OpenLayers/Lang/en.js
   * @requires OpenLayers/Console.js
   */
   
  /** 
   * Header: OpenLayers Base Types
   * OpenLayers custom string, number and function functions are described here.
   */
  
  /**
   * Namespace: OpenLayers.String
   * Contains convenience functions for string manipulation.
   */
  OpenLayers.String = {
  
      /**
       * APIFunction: startsWith
       * Test whether a string starts with another string. 
       * 
       * Parameters:
       * str - {String} The string to test.
       * sub - {Sring} The substring to look for.
       *  
       * Returns:
       * {Boolean} The first string starts with the second.
       */
      startsWith: function(str, sub) {
          return (str.indexOf(sub) == 0);
      },
  
      /**
       * APIFunction: contains
       * Test whether a string contains another string.
       * 
       * Parameters:
       * str - {String} The string to test.
       * sub - {String} The substring to look for.
       * 
       * Returns:
       * {Boolean} The first string contains the second.
       */
      contains: function(str, sub) {
          return (str.indexOf(sub) != -1);
      },
      
      /**
       * APIFunction: trim
       * Removes leading and trailing whitespace characters from a string.
       * 
       * Parameters:
       * str - {String} The (potentially) space padded string.  This string is not
       *     modified.
       * 
       * Returns:
       * {String} A trimmed version of the string with all leading and 
       *     trailing spaces removed.
       */
      trim: function(str) {
          return str.replace(/^\s\s*/, '').replace(/\s\s*$/, '');
      },
      
      /**
       * APIFunction: camelize
       * Camel-case a hyphenated string. 
       *     Ex. "chicken-head" becomes "chickenHead", and
       *     "-chicken-head" becomes "ChickenHead".
       *
       * Parameters:
       * str - {String} The string to be camelized.  The original is not modified.
       * 
       * Returns:
       * {String} The string, camelized
       */
      camelize: function(str) {
          var oStringList = str.split('-');
          var camelizedString = oStringList[0];
          for (var i=1, len=oStringList.length; i<len; i++) {
              var s = oStringList[i];
              camelizedString += s.charAt(0).toUpperCase() + s.substring(1);
          }
          return camelizedString;
      },
      
      /**
       * APIFunction: format
       * Given a string with tokens in the form ${token}, return a string
       *     with tokens replaced with properties from the given context
       *     object.  Represent a literal "${" by doubling it, e.g. "${${".
       *
       * Parameters:
       * template - {String} A string with tokens to be replaced.  A template
       *     has the form "literal ${token}" where the token will be replaced
       *     by the value of context["token"].
       * context - {Object} An optional object with properties corresponding
       *     to the tokens in the format string.  If no context is sent, the
       *     window object will be used.
       * args - {Array} Optional arguments to pass to any functions found in
       *     the context.  If a context property is a function, the token
       *     will be replaced by the return from the function called with
       *     these arguments.
       *
       * Returns:
       * {String} A string with tokens replaced from the context object.
       */
      format: function(template, context, args) {
          if(!context) {
              context = window;
          }
  
          // Example matching: 
          // str   = ${foo.bar}
          // match = foo.bar
          var replacer = function(str, match) {
              var replacement;
  
              // Loop through all subs. Example: ${a.b.c}
              // 0 -> replacement = context[a];
              // 1 -> replacement = context[a][b];
              // 2 -> replacement = context[a][b][c];
              var subs = match.split(/\.+/);
              for (var i=0; i< subs.length; i++) {
                  if (i == 0) {
                      replacement = context;
                  }
  
                  replacement = replacement[subs[i]];
              }
  
              if(typeof replacement == "function") {
                  replacement = args ?
                      replacement.apply(null, args) :
                      replacement();
              }
  
              // If replacement is undefined, return the string 'undefined'.
              // This is a workaround for a bugs in browsers not properly 
              // dealing with non-participating groups in regular expressions:
              // http://blog.stevenlevithan.com/archives/npcg-javascript
              if (typeof replacement == 'undefined') {
                  return 'undefined';
              } else {
                  return replacement; 
              }
          };
  
          return template.replace(OpenLayers.String.tokenRegEx, replacer);
      },
  
      /**
       * Property: OpenLayers.String.tokenRegEx
       * Used to find tokens in a string.
       * Examples: ${a}, ${a.b.c}, ${a-b}, ${5}
       */
      tokenRegEx:  /\$\{([\w.]+?)\}/g,
      
      /**
       * Property: OpenLayers.String.numberRegEx
       * Used to test strings as numbers.
       */
      numberRegEx: /^([+-]?)(?=\d|\.\d)\d*(\.\d*)?([Ee]([+-]?\d+))?$/,
      
      /**
       * APIFunction: OpenLayers.String.isNumeric
       * Determine whether a string contains only a numeric value.
       *
       * Examples:
       * (code)
       * OpenLayers.String.isNumeric("6.02e23") // true
       * OpenLayers.String.isNumeric("12 dozen") // false
       * OpenLayers.String.isNumeric("4") // true
       * OpenLayers.String.isNumeric(" 4 ") // false
       * (end)
       *
       * Returns:
       * {Boolean} String contains only a number.
       */
      isNumeric: function(value) {
          return OpenLayers.String.numberRegEx.test(value);
      },
      
      /**
       * APIFunction: numericIf
       * Converts a string that appears to be a numeric value into a number.
       * 
       * Returns
       * {Number|String} a Number if the passed value is a number, a String
       *     otherwise. 
       */
      numericIf: function(value) {
          return OpenLayers.String.isNumeric(value) ? parseFloat(value) : value;
      }
  
  };
  
  if (!String.prototype.startsWith) {
      /**
       * APIMethod: String.startsWith
       * *Deprecated*. Whether or not a string starts with another string. 
       * 
       * Parameters:
       * sStart - {Sring} The string we're testing for.
       *  
       * Returns:
       * {Boolean} Whether or not this string starts with the string passed in.
       */
      String.prototype.startsWith = function(sStart) {
          OpenLayers.Console.warn(OpenLayers.i18n("methodDeprecated",
                                  {'newMethod':'OpenLayers.String.startsWith'}));
          return OpenLayers.String.startsWith(this, sStart);
      };
  }
  
  if (!String.prototype.contains) {
      /**
       * APIMethod: String.contains
       * *Deprecated*. Whether or not a string contains another string.
       * 
       * Parameters:
       * str - {String} The string that we're testing for.
       * 
       * Returns:
       * {Boolean} Whether or not this string contains with the string passed in.
       */
      String.prototype.contains = function(str) {
          OpenLayers.Console.warn(OpenLayers.i18n("methodDeprecated",
                                    {'newMethod':'OpenLayers.String.contains'}));
          return OpenLayers.String.contains(this, str);
      };
  }
  
  if (!String.prototype.trim) {
      /**
       * APIMethod: String.trim
       * *Deprecated*. Removes leading and trailing whitespace characters from a string.
       * 
       * Returns:
       * {String} A trimmed version of the string - all leading and 
       *          trailing spaces removed
       */
      String.prototype.trim = function() {
          OpenLayers.Console.warn(OpenLayers.i18n("methodDeprecated",
                                        {'newMethod':'OpenLayers.String.trim'}));
          return OpenLayers.String.trim(this);
      };
  }
  
  if (!String.prototype.camelize) {
      /**
       * APIMethod: String.camelize
       * *Deprecated*. Camel-case a hyphenated string. 
       *     Ex. "chicken-head" becomes "chickenHead", and
       *     "-chicken-head" becomes "ChickenHead".
       * 
       * Returns:
       * {String} The string, camelized
       */
      String.prototype.camelize = function() {
          OpenLayers.Console.warn(OpenLayers.i18n("methodDeprecated",
                                    {'newMethod':'OpenLayers.String.camelize'}));
          return OpenLayers.String.camelize(this);
      };
  }
  
  /**
   * Namespace: OpenLayers.Number
   * Contains convenience functions for manipulating numbers.
   */
  OpenLayers.Number = {
  
      /**
       * Property: decimalSeparator
       * Decimal separator to use when formatting numbers.
       */
      decimalSeparator: ".",
      
      /**
       * Property: thousandsSeparator
       * Thousands separator to use when formatting numbers.
       */
      thousandsSeparator: ",",
      
      /**
       * APIFunction: limitSigDigs
       * Limit the number of significant digits on a float.
       * 
       * Parameters:
       * num - {Float}
       * sig - {Integer}
       * 
       * Returns:
       * {Float} The number, rounded to the specified number of significant
       *     digits.
       */
      limitSigDigs: function(num, sig) {
          var fig = 0;
          if (sig > 0) {
              fig = parseFloat(num.toPrecision(sig));
          }
          return fig;
      },
      
      /**
       * APIFunction: format
       * Formats a number for output.
       * 
       * Parameters:
       * num  - {Float}
       * dec  - {Integer} Number of decimal places to round to.
       *        Defaults to 0. Set to null to leave decimal places unchanged.
       * tsep - {String} Thousands separator.
       *        Default is ",".
       * dsep - {String} Decimal separator.
       *        Default is ".".
       *
       * Returns:
       * {String} A string representing the formatted number.
       */
      format: function(num, dec, tsep, dsep) {
          dec = (typeof dec != "undefined") ? dec : 0; 
          tsep = (typeof tsep != "undefined") ? tsep :
              OpenLayers.Number.thousandsSeparator; 
          dsep = (typeof dsep != "undefined") ? dsep :
              OpenLayers.Number.decimalSeparator;
  
          if (dec != null) {
              num = parseFloat(num.toFixed(dec));
          }
  
          var parts = num.toString().split(".");
          if (parts.length == 1 && dec == null) {
              // integer where we do not want to touch the decimals
              dec = 0;
          }
          
          var integer = parts[0];
          if (tsep) {
              var thousands = /(-?[0-9]+)([0-9]{3})/; 
              while(thousands.test(integer)) { 
                  integer = integer.replace(thousands, "$1" + tsep + "$2"); 
              }
          }
          
          var str;
          if (dec == 0) {
              str = integer;
          } else {
              var rem = parts.length > 1 ? parts[1] : "0";
              if (dec != null) {
                  rem = rem + new Array(dec - rem.length + 1).join("0");
              }
              str = integer + dsep + rem;
          }
          return str;
      }
  };
  
  if (!Number.prototype.limitSigDigs) {
      /**
       * APIMethod: Number.limitSigDigs
       * *Deprecated*. Limit the number of significant digits on an integer. Does *not*
       *     work with floats!
       * 
       * Parameters:
       * sig - {Integer}
       * 
       * Returns:
       * {Integer} The number, rounded to the specified number of significant digits.
       *           If null, 0, or negative value passed in, returns 0
       */
      Number.prototype.limitSigDigs = function(sig) {
          OpenLayers.Console.warn(OpenLayers.i18n("methodDeprecated",
                                {'newMethod':'OpenLayers.Number.limitSigDigs'}));
          return OpenLayers.Number.limitSigDigs(this, sig);
      };
  }
  
  /**
   * Namespace: OpenLayers.Function
   * Contains convenience functions for function manipulation.
   */
  OpenLayers.Function = {
      /**
       * APIFunction: bind
       * Bind a function to an object.  Method to easily create closures with
       *     'this' altered.
       * 
       * Parameters:
       * func - {Function} Input function.
       * object - {Object} The object to bind to the input function (as this).
       * 
       * Returns:
       * {Function} A closure with 'this' set to the passed in object.
       */
      bind: function(func, object) {
          // create a reference to all arguments past the second one
          var args = Array.prototype.slice.apply(arguments, [2]);
          return function() {
              // Push on any additional arguments from the actual function call.
              // These will come after those sent to the bind call.
              var newArgs = args.concat(
                  Array.prototype.slice.apply(arguments, [0])
              );
              return func.apply(object, newArgs);
          };
      },
      
      /**
       * APIFunction: bindAsEventListener
       * Bind a function to an object, and configure it to receive the event
       *     object as first parameter when called. 
       * 
       * Parameters:
       * func - {Function} Input function to serve as an event listener.
       * object - {Object} A reference to this.
       * 
       * Returns:
       * {Function}
       */
      bindAsEventListener: function(func, object) {
          return function(event) {
              return func.call(object, event || window.event);
          };
      },
      
      /**
       * APIFunction: False
       * A simple function to that just does "return false". We use this to 
       * avoid attaching anonymous functions to DOM event handlers, which 
       * causes "issues" on IE<8.
       * 
       * Usage:
       * document.onclick = OpenLayers.Function.False;
       * 
       * Returns:
       * {Boolean}
       */
      False : function() {
          return false;
      },
  
      /**
       * APIFunction: True
       * A simple function to that just does "return true". We use this to 
       * avoid attaching anonymous functions to DOM event handlers, which 
       * causes "issues" on IE<8.
       * 
       * Usage:
       * document.onclick = OpenLayers.Function.True;
       * 
       * Returns:
       * {Boolean}
       */
      True : function() {
          return true;
      }
  };
  
  if (!Function.prototype.bind) {
      /**
       * APIMethod: Function.bind
       * *Deprecated*. Bind a function to an object. 
       * Method to easily create closures with 'this' altered.
       * 
       * Parameters:
       * object - {Object} the this parameter
       * 
       * Returns:
       * {Function} A closure with 'this' altered to the first
       *            argument.
       */
      Function.prototype.bind = function() {
          OpenLayers.Console.warn(OpenLayers.i18n("methodDeprecated",
                                  {'newMethod':'OpenLayers.Function.bind'}));
          // new function takes the same arguments with this function up front
          Array.prototype.unshift.apply(arguments, [this]);
          return OpenLayers.Function.bind.apply(null, arguments);
      };
  }
  
  if (!Function.prototype.bindAsEventListener) {
      /**
       * APIMethod: Function.bindAsEventListener
       * *Deprecated*. Bind a function to an object, and configure it to receive the
       *     event object as first parameter when called. 
       * 
       * Parameters:
       * object - {Object} A reference to this.
       * 
       * Returns:
       * {Function}
       */
      Function.prototype.bindAsEventListener = function(object) {
          OpenLayers.Console.warn(OpenLayers.i18n("methodDeprecated",
                          {'newMethod':'OpenLayers.Function.bindAsEventListener'}));
          return OpenLayers.Function.bindAsEventListener(this, object);
      };
  }
  
  /**
   * Namespace: OpenLayers.Array
   * Contains convenience functions for array manipulation.
   */
  OpenLayers.Array = {
  
      /**
       * APIMethod: filter
       * Filter an array.  Provides the functionality of the
       *     Array.prototype.filter extension to the ECMA-262 standard.  Where
       *     available, Array.prototype.filter will be used.
       *
       * Based on well known example from http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Objects:Array:filter
       *
       * Parameters:
       * array - {Array} The array to be filtered.  This array is not mutated.
       *     Elements added to this array by the callback will not be visited.
       * callback - {Function} A function that is called for each element in
       *     the array.  If this function returns true, the element will be
       *     included in the return.  The function will be called with three
       *     arguments: the element in the array, the index of that element, and
       *     the array itself.  If the optional caller parameter is specified
       *     the callback will be called with this set to caller.
       * caller - {Object} Optional object to be set as this when the callback
       *     is called.
       *
       * Returns:
       * {Array} An array of elements from the passed in array for which the
       *     callback returns true.
       */
      filter: function(array, callback, caller) {
          var selected = [];
          if (Array.prototype.filter) {
              selected = array.filter(callback, caller);
          } else {
              var len = array.length;
              if (typeof callback != "function") {
                  throw new TypeError();
              }
              for(var i=0; i<len; i++) {
                  if (i in array) {
                      var val = array[i];
                      if (callback.call(caller, val, i, array)) {
                          selected.push(val);
                      }
                  }
              }        
          }
          return selected;
      }
      
  };
  