]> git.openstreetmap.org Git - rails.git/blob - public/lib/OpenLayers/Util.js
api06: Fix normal way and relation uploading (i.e. not as part of a diff).
[rails.git] / public / lib / OpenLayers / Util.js
1 /* Copyright (c) 2006 MetaCarta, Inc., published under the BSD license.
2  * See http://svn.openlayers.org/trunk/openlayers/license.txt for the full
3  * text of the license. */
4 /**
5 * @class
6 */
7 OpenLayers.Util = new Object();
8
9
10
11
12 /**
13 * @class This class represents a screen coordinate, in x and y coordinates
14 */
15 OpenLayers.Pixel = Class.create();
16 OpenLayers.Pixel.prototype = {
17     
18     /** @type float */
19     x: 0.0,
20
21     /** @type float */
22     y: 0.0,
23     
24     /** 
25     * @constructor
26     *
27     * @param {float} x
28     * @param {float} y
29     */
30     initialize: function(x, y) {
31         this.x = x;
32         this.y = y;
33     },
34     
35     /**
36     * @return string representation of Pixel. ex: "x=200.4,y=242.2"
37     * @type str
38     */
39     toString:function() {
40         return ("x=" + this.x + ",y=" + this.y);
41     },
42
43     /**
44     * @type OpenLayers.Pixel
45     */
46     copyOf:function() {
47         return new OpenLayers.Pixel(this.x, this.y); 
48     },
49     
50     /** 
51     * @param {OpenLayers.Pixel} px
52     * 
53     * @return whether or not the point passed in as parameter is equal to this
54     *          note that if px passed in is null, returns false
55     * @type bool
56     */
57     equals:function(px) {
58         var equals = false;
59         if (px != null) {
60             equals = ((this.x == px.x) && (this.y == px.y));
61         }
62         return equals;
63     },
64
65     /**
66     * @param {int} x
67     * @param {int} y
68     * 
69     * @return a new Pixel with this pixel's x&y augmented by the 
70     *         values passed in.
71     * @type OpenLayers.Pixel
72     */
73     add:function(x, y) {
74         return new OpenLayers.Pixel(this.x + x, this.y + y);
75     },
76
77     /**
78     * @param {OpenLayers.Pixel} px
79     * 
80     * @return a new Pixel with this pixel's x&y augmented by the 
81     *         x&y values of the pixel passed in.
82     * @type OpenLayers.Pixel
83     */
84     offset:function(px) {
85         return this.add(px.x, px.y);                
86     },
87     
88     /** @final @type str */
89     CLASS_NAME: "OpenLayers.Pixel"
90 };
91
92
93 /**
94 * @class This class represents a width and height pair
95 */
96 OpenLayers.Size = Class.create();
97 OpenLayers.Size.prototype = {
98
99     /** @type float */
100     w: 0.0,
101     
102     /** @type float */
103     h: 0.0,
104
105
106     /** 
107     * @constructor
108     * 
109     * @param {float} w 
110     * @param {float} h 
111     */
112     initialize: function(w, h) {
113         this.w = w;
114         this.h = h;
115     },
116
117     /** 
118     * @return String representation of OpenLayers.Size object. 
119     *         (ex. <i>"w=55,h=66"</i>)
120     * @type String
121     */
122     toString:function() {
123         return ("w=" + this.w + ",h=" + this.h);
124     },
125
126     /** 
127     * @return New OpenLayers.Size object with the same w and h values
128     * @type OpenLayers.Size
129     */
130     copyOf:function() {
131         return new OpenLayers.Size(this.w, this.h);
132     },
133
134     /** 
135     * @param {OpenLayers.Size} sz
136     * @returns Boolean value indicating whether the passed-in OpenLayers.Size 
137     *          object has the same w and h components as this
138     *          note that if sz passed in is null, returns false
139     *
140     * @type bool
141     */
142     equals:function(sz) {
143         var equals = false;
144         if (sz != null) {
145             equals = ((this.w == sz.w) && (this.h == sz.h));
146         }
147         return equals;
148     },
149     
150     /** @final @type String */
151     CLASS_NAME: "OpenLayers.Size"
152 };
153
154 /**
155 * @class This class represents a longitude and latitude pair
156 */
157 OpenLayers.LonLat = Class.create();
158 OpenLayers.LonLat.prototype = {
159
160     /** @type float */
161     lon: 0.0,
162     
163     /** @type float */
164     lat: 0.0,
165
166     /**
167     * @constructor
168     * 
169     * @param {float} lon
170     * @param {float} lat
171     */
172     initialize: function(lon, lat) {
173         this.lon = lon;
174         this.lat = lat;
175     },
176     
177     /** 
178     * @return String representation of OpenLayers.LonLat object. 
179     *         (ex. <i>"lon=5,lat=42"</i>)
180     * @type String
181     */
182     toString:function() {
183         return ("lon=" + this.lon + ",lat=" + this.lat);
184     },
185
186     /** 
187     * @return Shortened String representation of OpenLayers.LonLat object. 
188     *         (ex. <i>"5, 42"</i>)
189     * @type String
190     */
191     toShortString:function() {
192         return (this.lon + ", " + this.lat);
193     },
194
195     /** 
196     * @return New OpenLayers.LonLat object with the same lon and lat values
197     * @type OpenLayers.LonLat
198     */
199     copyOf:function() {
200         return new OpenLayers.LonLat(this.lon, this.lat);
201     },
202
203     /** 
204     * @param {float} lon
205     * @param {float} lat
206     *
207     * @return A new OpenLayers.LonLat object with the lon and lat passed-in
208     *         added to this's. 
209     * @type OpenLayers.LonLat
210     */
211     add:function(lon, lat) {
212         return new OpenLayers.LonLat(this.lon + lon, this.lat + lat);
213     },
214
215     /** 
216     * @param {OpenLayers.LonLat} ll
217     * @returns Boolean value indicating whether the passed-in OpenLayers.LonLat
218     *          object has the same lon and lat components as this
219     *          note that if ll passed in is null, returns false
220     *
221     * @type bool
222     */
223     equals:function(ll) {
224         var equals = false;
225         if (ll != null) {
226             equals = ((this.lon == ll.lon) && (this.lat == ll.lat));
227         }
228         return equals;
229     },
230     
231     /** @final @type String */
232     CLASS_NAME: "OpenLayers.LonLat"
233 };
234
235 /** Alternative constructor that builds a new OpenLayers.LonLat from a 
236 *    parameter string
237
238 * @constructor
239
240 * @param {String} str Comma-separated Lon,Lat coordinate string. 
241 *                     (ex. <i>"5,40"</i>)
242 *
243 * @returns New OpenLayers.LonLat object built from the passed-in String.
244 * @type OpenLayers.LonLat
245 */
246 OpenLayers.LonLat.fromString = function(str) {
247     var pair = str.split(",");
248     return new OpenLayers.LonLat(parseFloat(pair[0]), 
249                                  parseFloat(pair[1]));
250 };
251
252
253
254
255 /**
256 * @class This class represents a bounding box. 
257 *        Data stored as left, bottom, right, top floats
258 */
259 OpenLayers.Bounds = Class.create();
260 OpenLayers.Bounds.prototype = {
261
262     /** @type float */
263     left: 0.0,
264
265     /** @type float */
266     bottom: 0.0,
267
268     /** @type float */
269     right: 0.0,
270
271     /** @type float */
272     top: 0.0,    
273
274     /**
275     * @constructor
276     *
277     * @param {float} left
278     * @param {float} bottom
279     * @param {float} right
280     * @param {float} top
281     *
282     */
283     initialize: function(left, bottom, right, top) {
284         this.left = left;
285         this.bottom = bottom;
286         this.right = right;
287         this.top = top;
288     },
289
290     /**
291     * @returns A fresh copy of the bounds
292     * @type OpenLayers.Bounds
293     */
294     copyOf:function() {
295         return new OpenLayers.Bounds(this.left, this.bottom, 
296                                      this.right, this.top);
297     },
298
299     /** 
300     * @param {OpenLayers.Bounds} bounds
301     * @returns Boolean value indicating whether the passed-in OpenLayers.Bounds
302     *          object has the same left, right, top, bottom components as this
303     *           note that if bounds passed in is null, returns false
304     *
305     * @type bool
306     */
307     equals:function(bounds) {
308         var equals = false;
309         if (bounds != null) {
310             equals = ((this.left == bounds.left) && 
311                       (this.right == bounds.right) &&
312                       (this.top == bounds.top) && 
313                       (this.bottom == bounds.bottom));
314         }
315         return equals;
316     },
317
318     /** 
319     * @return String representation of OpenLayers.Bounds object. 
320     *         (ex.<i>"left-bottom=(5,42) right-top=(10,45)"</i>)
321     * @type String
322     */
323     toString:function(){
324         return ( "left-bottom=(" + this.left + "," + this.bottom + ")"
325                  + " right-top=(" + this.right + "," + this.top + ")" );
326     },
327
328     /** 
329     * @return Simple String representation of OpenLayers.Bounds object.
330     *         (ex. <i>"5,42,10,45"</i>)
331     * @type String
332     */
333     toBBOX:function() {
334         return (this.left + "," + this.bottom + ","
335                 + this.right + "," + this.top);
336     },
337     
338     /**
339     * @returns The width of the bounds
340     * @type float
341     */
342     getWidth:function() {
343         return (this.right - this.left);
344     },
345
346     /**
347     * @returns The height of the bounds
348     * @type float
349     */
350     getHeight:function() {
351         return (this.top - this.bottom);
352     },
353
354     /**
355     * @returns An OpenLayers.Size which represents the size of the box
356     * @type OpenLayers.Size
357     */
358     getSize:function() {
359         return new OpenLayers.Size(this.getWidth(), this.getHeight());
360     },
361
362     /**
363     * @returns An OpenLayers.Pixel which represents the center of the bounds
364     * @type OpenLayers.Pixel
365     */
366     getCenterPixel:function() {
367         return new OpenLayers.Pixel( (this.left + this.right) / 2,
368                                      (this.bottom + this.top) / 2);
369     },
370
371     /**
372     * @returns An OpenLayers.LonLat which represents the center of the bounds
373     * @type OpenLayers.LonLat
374     */
375     getCenterLonLat:function() {
376         return new OpenLayers.LonLat( (this.left + this.right) / 2,
377                                       (this.bottom + this.top) / 2);
378     },
379
380     /**
381     * @param {float} x
382     * @param {float} y
383     *
384     * @returns A new OpenLayers.Bounds whose coordinates are the same as this, 
385     *          but shifted by the passed-in x and y values
386     * @type OpenLayers.Bounds
387     */
388     add:function(x, y){
389         return new OpenLayers.Box(this.left + x, this.bottom + y,
390                                   this.right + x, this.top + y);
391     },
392
393     /**
394     * @param {float} x
395     * @param {float} y
396     * @param {Boolean} inclusive Whether or not to include the border. 
397     *                            Default is true
398     *
399     * @return Whether or not the passed-in coordinates are within this bounds
400     * @type Boolean
401     */
402     contains:function(x, y, inclusive) {
403     
404         //set default
405         if (inclusive == null) {
406             inclusive = true;
407         }
408         
409         var contains = false;
410         if (inclusive) {
411             contains = ((x >= this.left) && (x <= this.right) && 
412                         (y >= this.bottom) && (y <= this.top));
413         } else {
414             contains = ((x > this.left) && (x < this.right) && 
415                         (y > this.bottom) && (y < this.top));
416         }              
417         return contains;
418     },
419  
420     /**
421     * @param {OpenLayers.Bounds} bounds
422     * @param {Boolean} partial If true, only part of passed-in 
423     *                          OpenLayers.Bounds needs be within this bounds. 
424     *                          If false, the entire passed-in bounds must be
425     *                          within. Default is false
426     * @param {Boolean} inclusive Whether or not to include the border. 
427     *                            Default is true
428     *
429     * @return Whether or not the passed-in OpenLayers.Bounds object is 
430     *         contained within this bounds. 
431     * @type Boolean
432     */
433     containsBounds:function(bounds, partial, inclusive) {
434
435         //set defaults
436         if (partial == null) {
437             partial = false;
438         }
439         if (inclusive == null) {
440             inclusive = true;
441         }
442
443         var inLeft;
444         var inTop;
445         var inRight;
446         var inBottom;
447         
448         if (inclusive) {
449             inLeft = (bounds.left >= this.left) && (bounds.left <= this.right);
450             inTop = (bounds.top >= this.bottom) && (bounds.top <= this.top);
451             inRight= (bounds.right >= this.left) && (bounds.right <= this.right);
452             inBottom = (bounds.bottom >= this.bottom) && (bounds.bottom <= this.top);
453         } else {
454             inLeft = (bounds.left > this.left) && (bounds.left < this.right);
455             inTop = (bounds.top > this.bottom) && (bounds.top < this.top);
456             inRight= (bounds.right > this.left) && (bounds.right < this.right);
457             inBottom = (bounds.bottom > this.bottom) && (bounds.bottom < this.top);
458         }
459         
460         return (partial) ? (inTop || inBottom) && (inLeft || inRight )
461                          : (inTop && inLeft && inBottom && inRight);
462     },
463
464     /** 
465      * @param {OpenLayers.LonLat} lonlat
466      *
467      * @returns The quadrant ("br" "tr" "tl" "bl") of the bounds in which 
468      *           the coordinate lies.
469      * @type String
470      */
471     determineQuadrant: function(lonlat) {
472     
473         var quadrant = "";
474         var center = this.getCenterLonLat();
475         
476         quadrant += (lonlat.lat < center.lat) ? "b" : "t";
477         quadrant += (lonlat.lon < center.lon) ? "l" : "r";
478     
479         return quadrant; 
480     },
481
482     /** @final @type String */
483     CLASS_NAME: "OpenLayers.Bounds"
484 };
485
486 /** Alternative constructor that builds a new OpenLayers.Bounds from a 
487 *    parameter string
488
489 * @constructor
490
491 * @param {String} str Comma-separated bounds string. (ex. <i>"5,42,10,45"</i>)
492 *
493 * @returns New OpenLayers.Bounds object built from the passed-in String.
494 * @type OpenLayers.Bounds
495 */
496 OpenLayers.Bounds.fromString = function(str) {
497     var bounds = str.split(",");
498     return OpenLayers.Bounds.fromArray(bounds);
499 };
500
501 /** Alternative constructor that builds a new OpenLayers.Bounds
502 *    from an array
503
504 * @constructor
505
506 * @param {Array} bbox Array of bounds values (ex. <i>[5,42,10,45]</i>)
507 *
508 * @returns New OpenLayers.Bounds object built from the passed-in Array.
509 * @type OpenLayers.Bounds
510 */
511 OpenLayers.Bounds.fromArray = function(bbox) {
512     return new OpenLayers.Bounds(parseFloat(bbox[0]),
513                                  parseFloat(bbox[1]),
514                                  parseFloat(bbox[2]),
515                                  parseFloat(bbox[3]));
516 };
517
518 /** Alternative constructor that builds a new OpenLayers.Bounds
519 *    from an OpenLayers.Size
520
521 * @constructor
522
523 * @param {OpenLayers.Size} size
524 *
525 * @returns New OpenLayers.Bounds object built with top and left set to 0 and
526 *           bottom right taken from the passed-in OpenLayers.Size.
527 * @type OpenLayers.Bounds
528 */
529 OpenLayers.Bounds.fromSize = function(size) {
530     return new OpenLayers.Bounds(0,
531                                  size.h,
532                                  size.w,
533                                  0);
534 };
535 /**
536  * @param {String} quadrant 
537  * 
538  * @returns The opposing quadrant ("br" "tr" "tl" "bl"). For Example, if 
539  *           you pass in "bl" it returns "tr", if you pass in "br" it 
540  *           returns "tl", etc.
541  * @type String
542  */
543 OpenLayers.Bounds.oppositeQuadrant = function(quadrant) {
544     var opp = "";
545     
546     opp += (quadrant.charAt(0) == 't') ? 'b' : 't';
547     opp += (quadrant.charAt(1) == 'l') ? 'r' : 'l';
548     
549     return opp;
550 };
551
552 // Some other helpful things
553
554 /**
555 * @param {String} sStart
556
557 * @returns Whether or not this string starts with the string passed in.
558 * @type Boolean
559 */
560 String.prototype.startsWith = function(sStart){
561     return (this.substr(0,sStart.length) == sStart);
562 };
563
564 /**
565 * @returns A trimmed version of the string - all leading and 
566 *          trailing spaces removed
567 * @type String
568 */
569 String.prototype.trim = function() {
570     
571     var b = 0;
572     while(this.substr(b,1) == " ") {
573         b++;
574     }
575     
576     var e = this.length - 1;
577     while(this.substr(e,1) == " ") {
578         e--;
579     }
580     
581     return this.substring(b, e+1);
582 };
583
584 /** Remove an object from an array. Iterates through the array
585 *    to find the item, then removes it.
586 *
587 * @param {Object} item
588
589 * @returns A reference to the array
590 * @type Array
591 */
592 Array.prototype.remove = function(item) {
593     for(var i=0; i < this.length; i++) {
594         if(this[i] == item) {
595             this.splice(i,1);
596             //break;more than once??
597         }
598     }
599     return this;
600 }
601
602 /**
603 * @returns A fresh copy of the array
604 * @type Array
605 */
606 Array.prototype.copyOf = function() {
607   var copy = new Array();
608   for (var i = 0; i < this.length; i++) {
609       copy[i] = this[i];
610   }
611   return copy;
612 };
613
614 /**
615 * @param  {Object} item
616 */
617 Array.prototype.prepend = function(item) {
618     this.splice(0, 0, item);
619 };
620
621 /**
622 * @param  {Object} item
623 */
624 Array.prototype.append = function(item){
625     this[this.length] = item;
626 };
627
628 /**
629 */
630 Array.prototype.clear = function() {
631     this.length = 0;
632 };
633
634 /**
635 * @param {Object} element
636 *
637 * @returns The first index of the element in the array if found. Else returns -1
638 * @type int
639 */
640 Array.prototype.indexOf = function(element) {
641     var index = -1;
642     for(var i=0; i < this.length; i++) {
643         if (this[i] == element) {
644             index = i;
645             break;
646         }
647     }
648     return index;    
649 }
650
651 /**
652  * @param {String} id
653  * @param {OpenLayers.Pixel} px
654  * @param {OpenLayers.Size} sz
655  * @param {String} position
656  * @param {String} border
657  * @param {String} overflow
658  */
659 OpenLayers.Util.modifyDOMElement = function(element, id, px, sz, position, 
660                                             border, overflow) {
661
662     if (id) {
663         element.id = id;
664     }
665     if (px) {
666         element.style.left = px.x + "px";
667         element.style.top = px.y + "px";
668     }
669     if (sz) {
670         element.style.width = sz.w + "px";
671         element.style.height = sz.h + "px";
672     }
673     if (position) {
674         element.style.position = position;
675     }
676     if (border) {
677         element.style.border = border;
678     }
679     if (overflow) {
680         element.style.overflow = overflow;
681     }
682 };
683
684 /** 
685 * zIndex is NOT set
686 *
687 * @param {String} id
688 * @param {OpenLayers.Pixel} px
689 * @param {OpenLayers.Size} sz
690 * @param {String} imgURL
691 * @param {String} position
692 * @param {String} border
693 * @param {String} overflow
694 *
695 * @returns A DOM Div created with the specified attributes.
696 * @type DOMElement
697 */
698 OpenLayers.Util.createDiv = function(id, px, sz, imgURL, position, 
699                                      border, overflow) {
700
701     var dom = document.createElement('div');
702
703     //set specific properties
704     dom.style.padding = "0";
705     dom.style.margin = "0";
706     if (imgURL) {
707         dom.style.backgroundImage = 'url(' + imgURL + ')';
708     }
709
710     //set generic properties
711     if (!id) {
712         id = "OpenLayersDiv" + (Math.random() * 10000 % 10000);
713     }
714     if (!position) {
715         position = "absolute";
716     }
717     OpenLayers.Util.modifyDOMElement(dom, id, px, sz, 
718                                      position, border, overflow);
719
720     return dom;
721 };
722
723 /** 
724 * @param {String} id
725 * @param {OpenLayers.Pixel} px
726 * @param {OpenLayers.Size} sz
727 * @param {String} imgURL
728 * @param {String} position
729 * @param {String} border
730 *
731 * @returns A DOM Image created with the specified attributes.
732 * @type DOMElement
733 */
734 OpenLayers.Util.createImage = function(id, px, sz, imgURL, position, border) {
735
736     image = document.createElement("img");
737
738     //set special properties
739     image.style.alt = id;
740     image.galleryImg = "no";
741     if (imgURL) {
742         image.src = imgURL;
743     }
744
745     //set generic properties
746     if (!id) {
747         id = "OpenLayersDiv" + (Math.random() * 10000 % 10000);
748     }
749     if (!position) {
750         position = "relative";
751     }
752     OpenLayers.Util.modifyDOMElement(image, id, px, sz, position, border);
753         
754     return image;
755 };
756
757 OpenLayers.Util.alphaHack = function() {
758     var arVersion = navigator.appVersion.split("MSIE");
759     var version = parseFloat(arVersion[1]);
760     
761     return ( (document.body.filters) &&
762                       (version >= 5.5) && (version < 7) );
763 }
764
765 /** 
766 * @param {DOMElement} div Div containing Alpha-adjusted Image
767 * @param {String} id
768 * @param {OpenLayers.Pixel} px
769 * @param {OpenLayers.Size} sz
770 * @param {String} imgURL
771 * @param {String} position
772 * @param {String} border
773 * @param {String} sizing 'crop', 'scale', or 'image'. Default is "scale"
774 */ 
775 OpenLayers.Util.modifyAlphaImageDiv = function(div, id, px, sz, imgURL, 
776                                                position, border, sizing) {
777
778     OpenLayers.Util.modifyDOMElement(div, id, px, sz);
779
780     var img = div.childNodes[0];
781
782     if (imgURL) {
783         img.src = imgURL;
784     }
785     OpenLayers.Util.modifyDOMElement(img, div.id + "_innerImage", null, sz, 
786                                      "relative", border);
787
788     if (OpenLayers.Util.alphaHack()) {
789         div.style.display = "inline-block";
790         if (sizing == null) {
791             sizing = "scale";
792         }
793         div.style.filter = "progid:DXImageTransform.Microsoft" +
794                            ".AlphaImageLoader(src='" + img.src + "', " +
795                            "sizingMethod='" + sizing + "')";
796         img.style.filter = "progid:DXImageTransform.Microsoft" +
797                                 ".Alpha(opacity=0)";
798     }
799 };
800
801 /** 
802 * @param {String} id
803 * @param {OpenLayers.Pixel} px
804 * @param {OpenLayers.Size} sz
805 * @param {String} imgURL
806 * @param {String} position
807 * @param {String} border
808 * @param {String} sizing 'crop', 'scale', or 'image'. Default is "scale"
809 *
810 * @returns A DOM Div created with a DOM Image inside it. If the hack is 
811 *           needed for transparency in IE, it is added.
812 * @type DOMElement
813 */ 
814 OpenLayers.Util.createAlphaImageDiv = function(id, px, sz, imgURL, 
815                                                position, border, sizing) {
816     
817     var div = OpenLayers.Util.createDiv();
818     var img = OpenLayers.Util.createImage();
819     div.appendChild(img);
820
821     OpenLayers.Util.modifyAlphaImageDiv(div, id, px, sz, imgURL, 
822                                         position, border, sizing);
823     
824     return div;
825 };
826
827
828 /** Creates a new hash and copies over all the keys from the 
829 *    passed-in object, but storing them under an uppercased
830 *    version of the key at which they were stored.
831
832 * @param {Object} object
833 *
834 * @returns A new Object with all the same keys but uppercased
835 * @type Object
836 */
837 OpenLayers.Util.upperCaseObject = function (object) {
838     var uObject = new Object();
839     for (var key in object) {
840         uObject[key.toUpperCase()] = object[key];
841     }
842     return uObject;
843 };
844
845 /** Takes a hash and copies any keys that don't exist from
846 *   another hash, by analogy with Object.extend() from
847 *   Prototype.js.
848 *
849 * @param {Object} to
850 * @param {Object} from
851 *
852 * @type Object
853 */
854 OpenLayers.Util.applyDefaults = function (to, from) {
855     for (var key in from) {
856         if (to[key] == null) {
857             to[key] = from[key];
858         }
859     }
860     return to;
861 };
862
863 /**
864 * @param {Object} params
865 *
866 * @returns a concatenation of the properties of an object in 
867 *    http parameter notation. 
868 *    (ex. <i>"key1=value1&key2=value2&key3=value3"</i>)
869 * @type String
870 */
871 OpenLayers.Util.getParameterString = function(params) {
872     paramsArray = new Array();
873     
874     for (var key in params) {
875         var value = params[key];
876         //skip functions
877         if (typeof value == 'function') continue;
878     
879         paramsArray.push(key + "=" + value);
880     }
881     
882     return paramsArray.join("&");
883 };
884
885 /** 
886 * @returns The fully formatted image location string
887 * @type String
888 */
889 OpenLayers.Util.getImagesLocation = function() {
890     return OpenLayers._getScriptLocation() + "img/";
891 };
892
893
894
895 /** These could/should be made namespace aware?
896 *
897 * @param {} p
898 * @param {str} tagName
899 *
900 * @return {Array}
901 */
902 OpenLayers.Util.getNodes=function(p, tagName) {
903     var nodes = Try.these(
904         function () {
905             return OpenLayers.Util._getNodes(p.documentElement.childNodes,
906                                             tagName);
907         },
908         function () {
909             return OpenLayers.Util._getNodes(p.childNodes, tagName);
910         }
911     );
912     return nodes;
913 };
914
915 /**
916 * @param {Array} nodes
917 * @param {str} tagName
918 *
919 * @return {Array}
920 */
921 OpenLayers.Util._getNodes=function(nodes, tagName) {
922     var retArray = new Array();
923     for (var i=0;i<nodes.length;i++) {
924         if (nodes[i].nodeName==tagName) {
925             retArray.push(nodes[i]);
926         }
927     }
928
929     return retArray;
930 };
931
932
933
934 /**
935 * @param {} parent
936 * @param {str} item
937 * @param {int} index
938 *
939 * @return {str}
940 */
941 OpenLayers.Util.getTagText = function (parent, item, index) {
942     var result = OpenLayers.Util.getNodes(parent, item);
943     if (result && (result.length > 0))
944     {
945         if (!index) {
946             index=0;
947         }
948         if (result[index].childNodes.length > 1) {
949             return result.childNodes[1].nodeValue; 
950         }
951         else if (result[index].childNodes.length == 1) {
952             return result[index].firstChild.nodeValue; 
953         }
954     } else { 
955         return ""; 
956     }
957 };
958
959 /** 
960 * @param {Event} evt
961 * @param {HTMLDivElement} div
962 *
963 * @return {boolean}
964 */
965 OpenLayers.Util.mouseLeft = function (evt, div) {
966     // start with the element to which the mouse has moved
967     var target = (evt.relatedTarget) ? evt.relatedTarget : evt.toElement;
968     // walk up the DOM tree.
969     while (target != div && target != null) {
970         target = target.parentNode;
971     }
972     // if the target we stop at isn't the div, then we've left the div.
973     return (target != div);
974 };
975
976 OpenLayers.Util.rad = function(x) {return x*Math.PI/180;};
977 OpenLayers.Util.distVincenty=function(p1, p2) {
978     var a = 6378137, b = 6356752.3142,  f = 1/298.257223563;
979     var L = OpenLayers.Util.rad(p2.lon - p1.lon);
980     var U1 = Math.atan((1-f) * Math.tan(OpenLayers.Util.rad(p1.lat)));
981     var U2 = Math.atan((1-f) * Math.tan(OpenLayers.Util.rad(p2.lat)));
982     var sinU1 = Math.sin(U1), cosU1 = Math.cos(U1);
983     var sinU2 = Math.sin(U2), cosU2 = Math.cos(U2);
984     var lambda = L, lambdaP = 2*Math.PI;
985     var iterLimit = 20;
986     while (Math.abs(lambda-lambdaP) > 1e-12 && --iterLimit>0) {
987         var sinLambda = Math.sin(lambda), cosLambda = Math.cos(lambda);
988         var sinSigma = Math.sqrt((cosU2*sinLambda) * (cosU2*sinLambda) +
989         (cosU1*sinU2-sinU1*cosU2*cosLambda) * (cosU1*sinU2-sinU1*cosU2*cosLambda));
990         if (sinSigma==0) return 0;  // co-incident points
991         var cosSigma = sinU1*sinU2 + cosU1*cosU2*cosLambda;
992         var sigma = Math.atan2(sinSigma, cosSigma);
993         var alpha = Math.asin(cosU1 * cosU2 * sinLambda / sinSigma);
994         var cosSqAlpha = Math.cos(alpha) * Math.cos(alpha);
995         var cos2SigmaM = cosSigma - 2*sinU1*sinU2/cosSqAlpha;
996         var C = f/16*cosSqAlpha*(4+f*(4-3*cosSqAlpha));
997         lambdaP = lambda;
998         lambda = L + (1-C) * f * Math.sin(alpha) *
999         (sigma + C*sinSigma*(cos2SigmaM+C*cosSigma*(-1+2*cos2SigmaM*cos2SigmaM)));
1000     }
1001     if (iterLimit==0) return NaN  // formula failed to converge
1002     var uSq = cosSqAlpha * (a*a - b*b) / (b*b);
1003     var A = 1 + uSq/16384*(4096+uSq*(-768+uSq*(320-175*uSq)));
1004     var B = uSq/1024 * (256+uSq*(-128+uSq*(74-47*uSq)));
1005     var deltaSigma = B*sinSigma*(cos2SigmaM+B/4*(cosSigma*(-1+2*cos2SigmaM*cos2SigmaM)-
1006         B/6*cos2SigmaM*(-3+4*sinSigma*sinSigma)*(-3+4*cos2SigmaM*cos2SigmaM)));
1007     var s = b*A*(sigma-deltaSigma);
1008     var d = s.toFixed(3)/1000; // round to 1mm precision
1009     return d;
1010 };