More trip planner testing with colors
[busui.git] / labs / openlayers / lib / OpenLayers / BaseTypes.js
blob:a/labs/openlayers/lib/OpenLayers/BaseTypes.js -> blob:b/labs/openlayers/lib/OpenLayers/BaseTypes.js
--- 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<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/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<len; i++) {
+                if (i in array) {
+                    var val = array[i];
+                    if (callback.call(caller, val, i, array)) {
+                        selected.push(val);
+                    }
+                }
+            }        
+        }
+        return selected;
+    }
+    
+};
+
+/**
+ * Namespace: OpenLayers.Date
+ * Contains implementations of Date.parse and date.toISOString that match the 
+ *     ECMAScript 5 specification for parsing RFC 3339 dates.
+ *     http://tools.ietf.org/html/rfc3339
+ */
+OpenLayers.Date = {
+    
+    /**
+     * APIMethod: toISOString
+     * Generates a string representing a date.  The format of the string follows 
+     *     the profile of ISO 8601 for date and time on the Internet (see 
+     *     http://tools.ietf.org/html/rfc3339).  If the toISOString method is 
+     *     available on the Date prototype, that is used.  The toISOString
+     *     method for Date instances is defined in ECMA-262.
+     *
+     * Parameters:
+     * date - {Date} A date object.
+     *
+     * Returns:
+     * {String} A string representing the date (e.g. 
+     *     "2010-08-07T16:58:23.123Z").  If the date does not have a valid time
+     *     (i.e. isNaN(date.getTime())) this method returns the string "Invalid
+     *     Date".  The ECMA standard says the toISOString method should throw
+     *     RangeError in this case, but Firefox returns a string instead.  For
+     *     best results, use isNaN(date.getTime()) to determine date validity
+     *     before generating date strings.  
+     */
+    toISOString: (function() {
+        if ("toISOString" in Date.prototype) {
+            return function(date) {
+                return date.toISOString();
+            }
+        } else {
+            function pad(num, len) {
+                var str = num + "";
+                while (str.length < len) {
+                    str = "0" + str;
+                }
+                return str;
+            }
+            return function(date) {
+                var str;
+                if (isNaN(date.getTime())) {
+                    // ECMA-262 says throw RangeError, Firefox returns 
+                    // "Invalid Date"
+                    str = "Invalid Date";
+                } else {
+                    str = 
+                        date.getUTCFullYear() + "-" +
+                        pad(date.getUTCMonth() + 1, 2) + "-" +
+                        pad(date.getUTCDate(), 2) + "T" +
+                        pad(date.getUTCHours(), 2) + ":" +
+                        pad(date.getUTCMinutes(), 2) + ":" +
+                        pad(date.getUTCSeconds(), 2) + "." +
+                        pad(date.getUTCMilliseconds(), 3) + "Z";
+                }
+                return str;
+            }
+        }
+
+    })(),
+    
+    /**
+     * APIMethod: parse
+     * Generate a date object from a string.  The format for the string follows
+     *     the profile of ISO 8601 for date and time on the Internet (see 
+     *     http://tools.ietf.org/html/rfc3339).  If the parse method on 
+     *     the Date constructor returns a valid date for the given string,
+     *     that method is used.
+     *
+     * Parameters:
+     * str - {String} A string representing the date (e.g. 
+     *     "2010", "2010-08", "2010-08-07", "2010-08-07T16:58:23.123Z",
+     *     "2010-08-07T11:58:23.123-06").
+     * 
+     * Returns:
+     * {Date} A date object.  If the string could not be parsed, an invalid
+     *     date is returned (i.e. isNaN(date.getTime())).
+     */
+    parse: function(str) {
+        var date;
+        // first check if the native parse method can parse it
+        var elapsed = Date.parse(str);
+        if (!isNaN(elapsed)) {
+            date = new Date(elapsed);
+        } else {
+            var match = str.match(/^(?:(\d{4})(?:-(\d{2})(?:-(\d{2}))?)?)?(?:T(\d{1,2}):(\d{2}):(\d{2}(?:\.\d+)?)(Z|(?:[+-]\d{1,2}(?::(\d{2}))?)))?$/);
+            var date;
+            if (match && (match[1] || match[7])) { // must have at least year or time
+                var year = parseInt(match[1], 10) || 0;
+                var month = (parseInt(match[2], 10) - 1) || 0;
+                var day = parseInt(match[3], 10) || 1;
+                date = new Date(Date.UTC(year, month, day));
+                // optional time
+                var type = match[7];
+                if (type) {
+                    var hours = parseInt(match[4], 10);
+                    var minutes = parseInt(match[5], 10);
+                    var secFrac = parseFloat(match[6]);
+                    var seconds = secFrac | 0;
+                    var milliseconds = Math.round(1000 * (secFrac - seconds));
+                    date.setUTCHours(hours, minutes, seconds, milliseconds);
+                    // check offset
+                    if (type !== "Z") {
+                        var hoursOffset = parseInt(type, 10);
+                        var minutesOffset = parseInt(match[8]) || 0;
+                        var offset = -1000 * (60 * (hoursOffset * 60) + minutesOffset * 60);
+                        date = new Date(date.getTime() + offset);
+                    }
+                }
+            } else {
+                date = new Date("invalid");
+            }
+        }
+        return date;
+    }
+
+};