]> dev.renevier.net Git - syp.git/blob - openlayers/lib/OpenLayers/BaseTypes.js
fixes notices
[syp.git] / openlayers / lib / OpenLayers / BaseTypes.js
1 /* Copyright (c) 2006-2008 MetaCarta, Inc., published under the Clear BSD
2  * license.  See http://svn.openlayers.org/trunk/openlayers/license.txt for the
3  * full text of the license. */
4
5 /**
6  * @requires OpenLayers/BaseTypes/Class.js
7  * @requires OpenLayers/BaseTypes/LonLat.js
8  * @requires OpenLayers/BaseTypes/Size.js
9  * @requires OpenLayers/BaseTypes/Pixel.js
10  * @requires OpenLayers/BaseTypes/Bounds.js
11  * @requires OpenLayers/BaseTypes/Element.js
12  * @requires OpenLayers/Lang/en.js
13  * @requires OpenLayers/Console.js
14  */
15  
16 /** 
17  * Header: OpenLayers Base Types
18  * OpenLayers custom string, number and function functions are described here.
19  */
20
21 /**
22  * Namespace: OpenLayers.String
23  * Contains convenience functions for string manipulation.
24  */
25 OpenLayers.String = {
26
27     /**
28      * APIFunction: startsWith
29      * Test whether a string starts with another string. 
30      * 
31      * Parameters:
32      * str - {String} The string to test.
33      * sub - {Sring} The substring to look for.
34      *  
35      * Returns:
36      * {Boolean} The first string starts with the second.
37      */
38     startsWith: function(str, sub) {
39         return (str.indexOf(sub) == 0);
40     },
41
42     /**
43      * APIFunction: contains
44      * Test whether a string contains another string.
45      * 
46      * Parameters:
47      * str - {String} The string to test.
48      * sub - {String} The substring to look for.
49      * 
50      * Returns:
51      * {Boolean} The first string contains the second.
52      */
53     contains: function(str, sub) {
54         return (str.indexOf(sub) != -1);
55     },
56     
57     /**
58      * APIFunction: trim
59      * Removes leading and trailing whitespace characters from a string.
60      * 
61      * Parameters:
62      * str - {String} The (potentially) space padded string.  This string is not
63      *     modified.
64      * 
65      * Returns:
66      * {String} A trimmed version of the string with all leading and 
67      *     trailing spaces removed.
68      */
69     trim: function(str) {
70         return str.replace(/^\s\s*/, '').replace(/\s\s*$/, '');
71     },
72     
73     /**
74      * APIFunction: camelize
75      * Camel-case a hyphenated string. 
76      *     Ex. "chicken-head" becomes "chickenHead", and
77      *     "-chicken-head" becomes "ChickenHead".
78      *
79      * Parameters:
80      * str - {String} The string to be camelized.  The original is not modified.
81      * 
82      * Returns:
83      * {String} The string, camelized
84      */
85     camelize: function(str) {
86         var oStringList = str.split('-');
87         var camelizedString = oStringList[0];
88         for (var i=1, len=oStringList.length; i<len; i++) {
89             var s = oStringList[i];
90             camelizedString += s.charAt(0).toUpperCase() + s.substring(1);
91         }
92         return camelizedString;
93     },
94     
95     /**
96      * APIFunction: format
97      * Given a string with tokens in the form ${token}, return a string
98      *     with tokens replaced with properties from the given context
99      *     object.  Represent a literal "${" by doubling it, e.g. "${${".
100      *
101      * Parameters:
102      * template - {String} A string with tokens to be replaced.  A template
103      *     has the form "literal ${token}" where the token will be replaced
104      *     by the value of context["token"].
105      * context - {Object} An optional object with properties corresponding
106      *     to the tokens in the format string.  If no context is sent, the
107      *     window object will be used.
108      * args - {Array} Optional arguments to pass to any functions found in
109      *     the context.  If a context property is a function, the token
110      *     will be replaced by the return from the function called with
111      *     these arguments.
112      *
113      * Returns:
114      * {String} A string with tokens replaced from the context object.
115      */
116     format: function(template, context, args) {
117         if(!context) {
118             context = window;
119         }
120
121         // Example matching: 
122         // str   = ${foo.bar}
123         // match = foo.bar
124         var replacer = function(str, match) {
125             var replacement;
126
127             // Loop through all subs. Example: ${a.b.c}
128             // 0 -> replacement = context[a];
129             // 1 -> replacement = context[a][b];
130             // 2 -> replacement = context[a][b][c];
131             var subs = match.split(/\.+/);
132             for (var i=0; i< subs.length; i++) {
133                 if (i == 0) {
134                     replacement = context;
135                 }
136
137                 replacement = replacement[subs[i]];
138             }
139
140             if(typeof replacement == "function") {
141                 replacement = args ?
142                     replacement.apply(null, args) :
143                     replacement();
144             }
145
146             // If replacement is undefined, return the string 'undefined'.
147             // This is a workaround for a bugs in browsers not properly 
148             // dealing with non-participating groups in regular expressions:
149             // http://blog.stevenlevithan.com/archives/npcg-javascript
150             if (typeof replacement == 'undefined') {
151                 return 'undefined';
152             } else {
153                 return replacement; 
154             }
155         };
156
157         return template.replace(OpenLayers.String.tokenRegEx, replacer);
158     },
159
160     /**
161      * Property: OpenLayers.String.tokenRegEx
162      * Used to find tokens in a string.
163      * Examples: ${a}, ${a.b.c}, ${a-b}, ${5}
164      */
165     tokenRegEx:  /\$\{([\w.]+?)\}/g,
166     
167     /**
168      * Property: OpenLayers.String.numberRegEx
169      * Used to test strings as numbers.
170      */
171     numberRegEx: /^([+-]?)(?=\d|\.\d)\d*(\.\d*)?([Ee]([+-]?\d+))?$/,
172     
173     /**
174      * APIFunction: OpenLayers.String.isNumeric
175      * Determine whether a string contains only a numeric value.
176      *
177      * Examples:
178      * (code)
179      * OpenLayers.String.isNumeric("6.02e23") // true
180      * OpenLayers.String.isNumeric("12 dozen") // false
181      * OpenLayers.String.isNumeric("4") // true
182      * OpenLayers.String.isNumeric(" 4 ") // false
183      * (end)
184      *
185      * Returns:
186      * {Boolean} String contains only a number.
187      */
188     isNumeric: function(value) {
189         return OpenLayers.String.numberRegEx.test(value);
190     },
191     
192     /**
193      * APIFunction: numericIf
194      * Converts a string that appears to be a numeric value into a number.
195      * 
196      * Returns
197      * {Number|String} a Number if the passed value is a number, a String
198      *     otherwise. 
199      */
200     numericIf: function(value) {
201         return OpenLayers.String.isNumeric(value) ? parseFloat(value) : value;
202     }
203
204 };
205
206 if (!String.prototype.startsWith) {
207     /**
208      * APIMethod: String.startsWith
209      * *Deprecated*. Whether or not a string starts with another string. 
210      * 
211      * Parameters:
212      * sStart - {Sring} The string we're testing for.
213      *  
214      * Returns:
215      * {Boolean} Whether or not this string starts with the string passed in.
216      */
217     String.prototype.startsWith = function(sStart) {
218         OpenLayers.Console.warn(OpenLayers.i18n("methodDeprecated",
219                                 {'newMethod':'OpenLayers.String.startsWith'}));
220         return OpenLayers.String.startsWith(this, sStart);
221     };
222 }
223
224 if (!String.prototype.contains) {
225     /**
226      * APIMethod: String.contains
227      * *Deprecated*. Whether or not a string contains another string.
228      * 
229      * Parameters:
230      * str - {String} The string that we're testing for.
231      * 
232      * Returns:
233      * {Boolean} Whether or not this string contains with the string passed in.
234      */
235     String.prototype.contains = function(str) {
236         OpenLayers.Console.warn(OpenLayers.i18n("methodDeprecated",
237                                   {'newMethod':'OpenLayers.String.contains'}));
238         return OpenLayers.String.contains(this, str);
239     };
240 }
241
242 if (!String.prototype.trim) {
243     /**
244      * APIMethod: String.trim
245      * *Deprecated*. Removes leading and trailing whitespace characters from a string.
246      * 
247      * Returns:
248      * {String} A trimmed version of the string - all leading and 
249      *          trailing spaces removed
250      */
251     String.prototype.trim = function() {
252         OpenLayers.Console.warn(OpenLayers.i18n("methodDeprecated",
253                                       {'newMethod':'OpenLayers.String.trim'}));
254         return OpenLayers.String.trim(this);
255     };
256 }
257
258 if (!String.prototype.camelize) {
259     /**
260      * APIMethod: String.camelize
261      * *Deprecated*. Camel-case a hyphenated string. 
262      *     Ex. "chicken-head" becomes "chickenHead", and
263      *     "-chicken-head" becomes "ChickenHead".
264      * 
265      * Returns:
266      * {String} The string, camelized
267      */
268     String.prototype.camelize = function() {
269         OpenLayers.Console.warn(OpenLayers.i18n("methodDeprecated",
270                                   {'newMethod':'OpenLayers.String.camelize'}));
271         return OpenLayers.String.camelize(this);
272     };
273 }
274
275 /**
276  * Namespace: OpenLayers.Number
277  * Contains convenience functions for manipulating numbers.
278  */
279 OpenLayers.Number = {
280
281     /**
282      * Property: decimalSeparator
283      * Decimal separator to use when formatting numbers.
284      */
285     decimalSeparator: ".",
286     
287     /**
288      * Property: thousandsSeparator
289      * Thousands separator to use when formatting numbers.
290      */
291     thousandsSeparator: ",",
292     
293     /**
294      * APIFunction: limitSigDigs
295      * Limit the number of significant digits on a float.
296      * 
297      * Parameters:
298      * num - {Float}
299      * sig - {Integer}
300      * 
301      * Returns:
302      * {Float} The number, rounded to the specified number of significant
303      *     digits.
304      */
305     limitSigDigs: function(num, sig) {
306         var fig = 0;
307         if (sig > 0) {
308             fig = parseFloat(num.toPrecision(sig));
309         }
310         return fig;
311     },
312     
313     /**
314      * APIFunction: format
315      * Formats a number for output.
316      * 
317      * Parameters:
318      * num  - {Float}
319      * dec  - {Integer} Number of decimal places to round to.
320      *        Defaults to 0. Set to null to leave decimal places unchanged.
321      * tsep - {String} Thousands separator.
322      *        Default is ",".
323      * dsep - {String} Decimal separator.
324      *        Default is ".".
325      *
326      * Returns:
327      * {String} A string representing the formatted number.
328      */
329     format: function(num, dec, tsep, dsep) {
330         dec = (typeof dec != "undefined") ? dec : 0; 
331         tsep = (typeof tsep != "undefined") ? tsep :
332             OpenLayers.Number.thousandsSeparator; 
333         dsep = (typeof dsep != "undefined") ? dsep :
334             OpenLayers.Number.decimalSeparator;
335
336         if (dec != null) {
337             num = parseFloat(num.toFixed(dec));
338         }
339
340         var parts = num.toString().split(".");
341         if (parts.length == 1 && dec == null) {
342             // integer where we do not want to touch the decimals
343             dec = 0;
344         }
345         
346         var integer = parts[0];
347         if (tsep) {
348             var thousands = /(-?[0-9]+)([0-9]{3})/; 
349             while(thousands.test(integer)) { 
350                 integer = integer.replace(thousands, "$1" + tsep + "$2"); 
351             }
352         }
353         
354         var str;
355         if (dec == 0) {
356             str = integer;
357         } else {
358             var rem = parts.length > 1 ? parts[1] : "0";
359             if (dec != null) {
360                 rem = rem + new Array(dec - rem.length + 1).join("0");
361             }
362             str = integer + dsep + rem;
363         }
364         return str;
365     }
366 };
367
368 if (!Number.prototype.limitSigDigs) {
369     /**
370      * APIMethod: Number.limitSigDigs
371      * *Deprecated*. Limit the number of significant digits on an integer. Does *not*
372      *     work with floats!
373      * 
374      * Parameters:
375      * sig - {Integer}
376      * 
377      * Returns:
378      * {Integer} The number, rounded to the specified number of significant digits.
379      *           If null, 0, or negative value passed in, returns 0
380      */
381     Number.prototype.limitSigDigs = function(sig) {
382         OpenLayers.Console.warn(OpenLayers.i18n("methodDeprecated",
383                               {'newMethod':'OpenLayers.Number.limitSigDigs'}));
384         return OpenLayers.Number.limitSigDigs(this, sig);
385     };
386 }
387
388 /**
389  * Namespace: OpenLayers.Function
390  * Contains convenience functions for function manipulation.
391  */
392 OpenLayers.Function = {
393     /**
394      * APIFunction: bind
395      * Bind a function to an object.  Method to easily create closures with
396      *     'this' altered.
397      * 
398      * Parameters:
399      * func - {Function} Input function.
400      * object - {Object} The object to bind to the input function (as this).
401      * 
402      * Returns:
403      * {Function} A closure with 'this' set to the passed in object.
404      */
405     bind: function(func, object) {
406         // create a reference to all arguments past the second one
407         var args = Array.prototype.slice.apply(arguments, [2]);
408         return function() {
409             // Push on any additional arguments from the actual function call.
410             // These will come after those sent to the bind call.
411             var newArgs = args.concat(
412                 Array.prototype.slice.apply(arguments, [0])
413             );
414             return func.apply(object, newArgs);
415         };
416     },
417     
418     /**
419      * APIFunction: bindAsEventListener
420      * Bind a function to an object, and configure it to receive the event
421      *     object as first parameter when called. 
422      * 
423      * Parameters:
424      * func - {Function} Input function to serve as an event listener.
425      * object - {Object} A reference to this.
426      * 
427      * Returns:
428      * {Function}
429      */
430     bindAsEventListener: function(func, object) {
431         return function(event) {
432             return func.call(object, event || window.event);
433         };
434     }
435 };
436
437 if (!Function.prototype.bind) {
438     /**
439      * APIMethod: Function.bind
440      * *Deprecated*. Bind a function to an object. 
441      * Method to easily create closures with 'this' altered.
442      * 
443      * Parameters:
444      * object - {Object} the this parameter
445      * 
446      * Returns:
447      * {Function} A closure with 'this' altered to the first
448      *            argument.
449      */
450     Function.prototype.bind = function() {
451         OpenLayers.Console.warn(OpenLayers.i18n("methodDeprecated",
452                                 {'newMethod':'OpenLayers.Function.bind'}));
453         // new function takes the same arguments with this function up front
454         Array.prototype.unshift.apply(arguments, [this]);
455         return OpenLayers.Function.bind.apply(null, arguments);
456     };
457 }
458
459 if (!Function.prototype.bindAsEventListener) {
460     /**
461      * APIMethod: Function.bindAsEventListener
462      * *Deprecated*. Bind a function to an object, and configure it to receive the
463      *     event object as first parameter when called. 
464      * 
465      * Parameters:
466      * object - {Object} A reference to this.
467      * 
468      * Returns:
469      * {Function}
470      */
471     Function.prototype.bindAsEventListener = function(object) {
472         OpenLayers.Console.warn(OpenLayers.i18n("methodDeprecated",
473                         {'newMethod':'OpenLayers.Function.bindAsEventListener'}));
474         return OpenLayers.Function.bindAsEventListener(this, object);
475     };
476 }
477
478 /**
479  * Namespace: OpenLayers.Array
480  * Contains convenience functions for array manipulation.
481  */
482 OpenLayers.Array = {
483
484     /**
485      * APIMethod: filter
486      * Filter an array.  Provides the functionality of the
487      *     Array.prototype.filter extension to the ECMA-262 standard.  Where
488      *     available, Array.prototype.filter will be used.
489      *
490      * Based on well known example from http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Objects:Array:filter
491      *
492      * Parameters:
493      * array - {Array} The array to be filtered.  This array is not mutated.
494      *     Elements added to this array by the callback will not be visited.
495      * callback - {Function} A function that is called for each element in
496      *     the array.  If this function returns true, the element will be
497      *     included in the return.  The function will be called with three
498      *     arguments: the element in the array, the index of that element, and
499      *     the array itself.  If the optional caller parameter is specified
500      *     the callback will be called with this set to caller.
501      * caller - {Object} Optional object to be set as this when the callback
502      *     is called.
503      *
504      * Returns:
505      * {Array} An array of elements from the passed in array for which the
506      *     callback returns true.
507      */
508     filter: function(array, callback, caller) {
509         var selected = [];
510         if (Array.prototype.filter) {
511             selected = array.filter(callback, caller);
512         } else {
513             var len = array.length;
514             if (typeof callback != "function") {
515                 throw new TypeError();
516             }
517             for(var i=0; i<len; i++) {
518                 if (i in array) {
519                     var val = array[i];
520                     if (callback.call(caller, val, i, array)) {
521                         selected.push(val);
522                     }
523                 }
524             }        
525         }
526         return selected;
527     }
528     
529 };