--- a/labs/openlayers/lib/OpenLayers/BaseTypes.js +++ b/labs/openlayers/lib/OpenLayers/BaseTypes.js @@ -1,1 +1,682 @@ - +/* Copyright (c) 2006-2010 by OpenLayers Contributors (see authors.txt for + * full list of contributors). 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 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/Core_JavaScript_1.5_Reference/Global_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